github.com/google/osv-scalibr@v0.4.1/extractor/filesystem/language/javascript/yarnlock/yarnlock-v2_test.go (about)

     1  // Copyright 2025 Google LLC
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //      http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package yarnlock_test
    16  
    17  import (
    18  	"testing"
    19  
    20  	"github.com/google/go-cmp/cmp"
    21  	"github.com/google/go-cmp/cmp/cmpopts"
    22  	"github.com/google/osv-scalibr/extractor"
    23  	"github.com/google/osv-scalibr/extractor/filesystem/language/javascript/yarnlock"
    24  	"github.com/google/osv-scalibr/inventory"
    25  	"github.com/google/osv-scalibr/purl"
    26  	"github.com/google/osv-scalibr/testing/extracttest"
    27  )
    28  
    29  func TestExtractor_Extract_v2(t *testing.T) {
    30  	tests := []extracttest.TestTableEntry{
    31  		{
    32  			Name: "no packages",
    33  			InputConfig: extracttest.ScanInputMockConfig{
    34  				Path: "testdata/empty.v2.lock",
    35  			},
    36  			WantPackages: []*extractor.Package{},
    37  		},
    38  		{
    39  			Name: "one package",
    40  			InputConfig: extracttest.ScanInputMockConfig{
    41  				Path: "testdata/one-package.v2.lock",
    42  			},
    43  			WantPackages: []*extractor.Package{
    44  				{
    45  					Name:      "balanced-match",
    46  					Version:   "1.0.2",
    47  					PURLType:  purl.TypeNPM,
    48  					Locations: []string{"testdata/one-package.v2.lock"},
    49  					SourceCode: &extractor.SourceCodeIdentifier{
    50  						Commit: "",
    51  					},
    52  				},
    53  			},
    54  		},
    55  		{
    56  			Name: "two packages",
    57  			InputConfig: extracttest.ScanInputMockConfig{
    58  				Path: "testdata/two-packages.v2.lock",
    59  			},
    60  			WantPackages: []*extractor.Package{
    61  				{
    62  					Name:      "compare-func",
    63  					Version:   "2.0.0",
    64  					PURLType:  purl.TypeNPM,
    65  					Locations: []string{"testdata/two-packages.v2.lock"},
    66  					SourceCode: &extractor.SourceCodeIdentifier{
    67  						Commit: "",
    68  					},
    69  				},
    70  				{
    71  					Name:      "concat-map",
    72  					Version:   "0.0.1",
    73  					PURLType:  purl.TypeNPM,
    74  					Locations: []string{"testdata/two-packages.v2.lock"},
    75  					SourceCode: &extractor.SourceCodeIdentifier{
    76  						Commit: "",
    77  					},
    78  				},
    79  			},
    80  		},
    81  		{
    82  			Name: "with quotes",
    83  			InputConfig: extracttest.ScanInputMockConfig{
    84  				Path: "testdata/with-quotes.v2.lock",
    85  			},
    86  			WantPackages: []*extractor.Package{
    87  				{
    88  					Name:      "compare-func",
    89  					Version:   "2.0.0",
    90  					PURLType:  purl.TypeNPM,
    91  					Locations: []string{"testdata/with-quotes.v2.lock"},
    92  					SourceCode: &extractor.SourceCodeIdentifier{
    93  						Commit: "",
    94  					},
    95  				},
    96  				{
    97  					Name:      "concat-map",
    98  					Version:   "0.0.1",
    99  					PURLType:  purl.TypeNPM,
   100  					Locations: []string{"testdata/with-quotes.v2.lock"},
   101  					SourceCode: &extractor.SourceCodeIdentifier{
   102  						Commit: "",
   103  					},
   104  				},
   105  			},
   106  		},
   107  		{
   108  			Name: "multiple versions",
   109  			InputConfig: extracttest.ScanInputMockConfig{
   110  				Path: "testdata/multiple-versions.v2.lock",
   111  			},
   112  			WantPackages: []*extractor.Package{
   113  				{
   114  					Name:      "debug",
   115  					Version:   "4.3.3",
   116  					PURLType:  purl.TypeNPM,
   117  					Locations: []string{"testdata/multiple-versions.v2.lock"},
   118  					SourceCode: &extractor.SourceCodeIdentifier{
   119  						Commit: "",
   120  					},
   121  				},
   122  				{
   123  					Name:      "debug",
   124  					Version:   "2.6.9",
   125  					PURLType:  purl.TypeNPM,
   126  					Locations: []string{"testdata/multiple-versions.v2.lock"},
   127  					SourceCode: &extractor.SourceCodeIdentifier{
   128  						Commit: "",
   129  					},
   130  				},
   131  				{
   132  					Name:      "debug",
   133  					Version:   "3.2.7",
   134  					PURLType:  purl.TypeNPM,
   135  					Locations: []string{"testdata/multiple-versions.v2.lock"},
   136  					SourceCode: &extractor.SourceCodeIdentifier{
   137  						Commit: "",
   138  					},
   139  				},
   140  			},
   141  		},
   142  		{
   143  			Name: "scoped packages",
   144  			InputConfig: extracttest.ScanInputMockConfig{
   145  				Path: "testdata/scoped-packages.v2.lock",
   146  			},
   147  			WantPackages: []*extractor.Package{
   148  				{
   149  					Name:      "@babel/cli",
   150  					Version:   "7.16.8",
   151  					PURLType:  purl.TypeNPM,
   152  					Locations: []string{"testdata/scoped-packages.v2.lock"},
   153  					SourceCode: &extractor.SourceCodeIdentifier{
   154  						Commit: "",
   155  					},
   156  				},
   157  				{
   158  					Name:      "@babel/code-frame",
   159  					Version:   "7.16.7",
   160  					PURLType:  purl.TypeNPM,
   161  					Locations: []string{"testdata/scoped-packages.v2.lock"},
   162  					SourceCode: &extractor.SourceCodeIdentifier{
   163  						Commit: "",
   164  					},
   165  				},
   166  				{
   167  					Name:      "@babel/compat-data",
   168  					Version:   "7.16.8",
   169  					PURLType:  purl.TypeNPM,
   170  					Locations: []string{"testdata/scoped-packages.v2.lock"},
   171  					SourceCode: &extractor.SourceCodeIdentifier{
   172  						Commit: "",
   173  					},
   174  				},
   175  			},
   176  		},
   177  		{
   178  			Name: "with prerelease",
   179  			InputConfig: extracttest.ScanInputMockConfig{
   180  				Path: "testdata/with-prerelease.v2.lock",
   181  			},
   182  			WantPackages: []*extractor.Package{
   183  				{
   184  					Name:      "@nicolo-ribaudo/chokidar-2",
   185  					Version:   "2.1.8-no-fsevents.3",
   186  					PURLType:  purl.TypeNPM,
   187  					Locations: []string{"testdata/with-prerelease.v2.lock"},
   188  					SourceCode: &extractor.SourceCodeIdentifier{
   189  						Commit: "",
   190  					},
   191  				},
   192  				{
   193  					Name:      "gensync",
   194  					Version:   "1.0.0-beta.2",
   195  					PURLType:  purl.TypeNPM,
   196  					Locations: []string{"testdata/with-prerelease.v2.lock"},
   197  					SourceCode: &extractor.SourceCodeIdentifier{
   198  						Commit: "",
   199  					},
   200  				},
   201  			},
   202  		},
   203  		{
   204  			Name: "with build string",
   205  			InputConfig: extracttest.ScanInputMockConfig{
   206  				Path: "testdata/with-build-string.v2.lock",
   207  			},
   208  			WantPackages: []*extractor.Package{
   209  				{
   210  					Name:      "domino",
   211  					Version:   "2.1.6+git",
   212  					PURLType:  purl.TypeNPM,
   213  					Locations: []string{"testdata/with-build-string.v2.lock"},
   214  					SourceCode: &extractor.SourceCodeIdentifier{
   215  						Commit: "f2435fe1f9f7c91ade0bd472c4723e5eacd7d19a",
   216  					},
   217  				},
   218  				{
   219  					Name:      "tslib",
   220  					Version:   "2.6.2",
   221  					PURLType:  purl.TypeNPM,
   222  					Locations: []string{"testdata/with-build-string.v2.lock"},
   223  					SourceCode: &extractor.SourceCodeIdentifier{
   224  						Commit: "",
   225  					},
   226  				},
   227  			},
   228  		},
   229  		{
   230  			Name: "commits",
   231  			InputConfig: extracttest.ScanInputMockConfig{
   232  				Path: "testdata/commits.v2.lock",
   233  			},
   234  			WantPackages: []*extractor.Package{
   235  				{
   236  					Name:      "@my-scope/my-first-package",
   237  					Version:   "0.0.6",
   238  					PURLType:  purl.TypeNPM,
   239  					Locations: []string{"testdata/commits.v2.lock"},
   240  					SourceCode: &extractor.SourceCodeIdentifier{
   241  						Commit: "0b824c650d3a03444dbcf2b27a5f3566f6e41358",
   242  					},
   243  				},
   244  				{
   245  					Name:      "my-second-package",
   246  					Version:   "0.2.2",
   247  					PURLType:  purl.TypeNPM,
   248  					Locations: []string{"testdata/commits.v2.lock"},
   249  					SourceCode: &extractor.SourceCodeIdentifier{
   250  						Commit: "59e2127b9f9d4fda5f928c4204213b3502cd5bb0",
   251  					},
   252  				},
   253  				{
   254  					Name:      "@typegoose/typegoose",
   255  					Version:   "7.2.0",
   256  					PURLType:  purl.TypeNPM,
   257  					Locations: []string{"testdata/commits.v2.lock"},
   258  					SourceCode: &extractor.SourceCodeIdentifier{
   259  						Commit: "3ed06e5097ab929f69755676fee419318aaec73a",
   260  					},
   261  				},
   262  				{
   263  					Name:      "vuejs",
   264  					Version:   "2.5.0",
   265  					PURLType:  purl.TypeNPM,
   266  					Locations: []string{"testdata/commits.v2.lock"},
   267  					SourceCode: &extractor.SourceCodeIdentifier{
   268  						Commit: "0948d999f2fddf9f90991956493f976273c5da1f",
   269  					},
   270  				},
   271  				{
   272  					Name:      "my-third-package",
   273  					Version:   "0.16.1-dev",
   274  					PURLType:  purl.TypeNPM,
   275  					Locations: []string{"testdata/commits.v2.lock"},
   276  					SourceCode: &extractor.SourceCodeIdentifier{
   277  						Commit: "5675a0aed98e067ff6ecccc5ac674fe8995960e0",
   278  					},
   279  				},
   280  				{
   281  					Name:      "my-node-sdk",
   282  					Version:   "1.1.0",
   283  					PURLType:  purl.TypeNPM,
   284  					Locations: []string{"testdata/commits.v2.lock"},
   285  					SourceCode: &extractor.SourceCodeIdentifier{
   286  						Commit: "053dea9e0b8af442d8f867c8e690d2fb0ceb1bf5",
   287  					},
   288  				},
   289  				{
   290  					Name:      "is-really-great",
   291  					Version:   "1.0.0",
   292  					PURLType:  purl.TypeNPM,
   293  					Locations: []string{"testdata/commits.v2.lock"},
   294  					SourceCode: &extractor.SourceCodeIdentifier{
   295  						Commit: "191eeef50c584714e1fb8927d17ee72b3b8c97c4",
   296  					},
   297  				},
   298  			},
   299  		},
   300  		{
   301  			Name: "files",
   302  			InputConfig: extracttest.ScanInputMockConfig{
   303  				Path: "testdata/files.v2.lock",
   304  			},
   305  			WantPackages: []*extractor.Package{
   306  				{
   307  					Name:      "my-package",
   308  					Version:   "0.0.2",
   309  					PURLType:  purl.TypeNPM,
   310  					Locations: []string{"testdata/files.v2.lock"},
   311  					SourceCode: &extractor.SourceCodeIdentifier{
   312  						Commit: "",
   313  					},
   314  				},
   315  			},
   316  		},
   317  		{
   318  			Name: "with aliases",
   319  			InputConfig: extracttest.ScanInputMockConfig{
   320  				Path: "testdata/with-aliases.v2.lock",
   321  			},
   322  			WantPackages: []*extractor.Package{
   323  				{
   324  					Name:      "@babel/helper-validator-identifier",
   325  					Version:   "7.22.20",
   326  					PURLType:  purl.TypeNPM,
   327  					Locations: []string{"testdata/with-aliases.v2.lock"},
   328  					SourceCode: &extractor.SourceCodeIdentifier{
   329  						Commit: "",
   330  					},
   331  				},
   332  				{
   333  					Name:      "ansi-regex",
   334  					Version:   "6.0.1",
   335  					PURLType:  purl.TypeNPM,
   336  					Locations: []string{"testdata/with-aliases.v2.lock"},
   337  					SourceCode: &extractor.SourceCodeIdentifier{
   338  						Commit: "",
   339  					},
   340  				},
   341  				{
   342  					Name:      "ansi-regex",
   343  					Version:   "5.0.1",
   344  					PURLType:  purl.TypeNPM,
   345  					Locations: []string{"testdata/with-aliases.v2.lock"},
   346  					SourceCode: &extractor.SourceCodeIdentifier{
   347  						Commit: "",
   348  					},
   349  				},
   350  			},
   351  		},
   352  		{
   353  			Name: "exclude root",
   354  			InputConfig: extracttest.ScanInputMockConfig{
   355  				Path: "testdata/exclude-root.v2.lock",
   356  			},
   357  			WantPackages: []*extractor.Package{
   358  				{
   359  					Name:      "@ws/ansi-regex",
   360  					Version:   "0.0.0-use.local",
   361  					PURLType:  purl.TypeNPM,
   362  					Locations: []string{"testdata/exclude-root.v2.lock"},
   363  					SourceCode: &extractor.SourceCodeIdentifier{
   364  						Commit: "",
   365  					},
   366  				},
   367  				{
   368  					Name:      "ansi-regex",
   369  					Version:   "6.1.0",
   370  					PURLType:  purl.TypeNPM,
   371  					Locations: []string{"testdata/exclude-root.v2.lock"},
   372  					SourceCode: &extractor.SourceCodeIdentifier{
   373  						Commit: "",
   374  					},
   375  				},
   376  			},
   377  		},
   378  	}
   379  
   380  	for _, tt := range tests {
   381  		t.Run(tt.Name, func(t *testing.T) {
   382  			extr := yarnlock.Extractor{}
   383  
   384  			scanInput := extracttest.GenerateScanInputMock(t, tt.InputConfig)
   385  			defer extracttest.CloseTestScanInput(t, scanInput)
   386  
   387  			got, err := extr.Extract(t.Context(), &scanInput)
   388  
   389  			if diff := cmp.Diff(tt.WantErr, err, cmpopts.EquateErrors()); diff != "" {
   390  				t.Errorf("%s.Extract(%q) error diff (-want +got):\n%s", extr.Name(), tt.InputConfig.Path, diff)
   391  				return
   392  			}
   393  
   394  			wantInv := inventory.Inventory{Packages: tt.WantPackages}
   395  			if diff := cmp.Diff(wantInv, got, cmpopts.SortSlices(extracttest.PackageCmpLess)); diff != "" {
   396  				t.Errorf("%s.Extract(%q) diff (-want +got):\n%s", extr.Name(), tt.InputConfig.Path, diff)
   397  			}
   398  		})
   399  	}
   400  }