github.com/google/osv-scalibr@v0.4.1/extractor/filesystem/language/javascript/pnpmlock/pnpmlock_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 pnpmlock_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/pnpmlock"
    24  	"github.com/google/osv-scalibr/extractor/filesystem/osv"
    25  	"github.com/google/osv-scalibr/extractor/filesystem/simplefileapi"
    26  	"github.com/google/osv-scalibr/inventory"
    27  	"github.com/google/osv-scalibr/purl"
    28  	"github.com/google/osv-scalibr/testing/extracttest"
    29  )
    30  
    31  func TestExtractor_FileRequired(t *testing.T) {
    32  	tests := []struct {
    33  		name      string
    34  		inputPath string
    35  		want      bool
    36  	}{
    37  		{
    38  			name:      "",
    39  			inputPath: "",
    40  			want:      false,
    41  		},
    42  		{
    43  			name:      "",
    44  			inputPath: "pnpm-lock.yaml",
    45  			want:      true,
    46  		},
    47  		{
    48  			name:      "",
    49  			inputPath: "path/to/my/pnpm-lock.yaml",
    50  			want:      true,
    51  		},
    52  		{
    53  			name:      "",
    54  			inputPath: "path/to/my/pnpm-lock.yaml/file",
    55  			want:      false,
    56  		},
    57  		{
    58  			name:      "",
    59  			inputPath: "path/to/my/pnpm-lock.yaml.file",
    60  			want:      false,
    61  		},
    62  		{
    63  			name:      "",
    64  			inputPath: "path.to.my.pnpm-lock.yaml",
    65  			want:      false,
    66  		},
    67  		{
    68  			name:      "",
    69  			inputPath: "foo/node_modules/bar/pnpn-lock.yaml",
    70  			want:      false,
    71  		},
    72  	}
    73  	for _, tt := range tests {
    74  		t.Run(tt.name, func(t *testing.T) {
    75  			e := pnpmlock.Extractor{}
    76  			got := e.FileRequired(simplefileapi.New(tt.inputPath, nil))
    77  			if got != tt.want {
    78  				t.Errorf("FileRequired(%s, FileInfo) got = %v, want %v", tt.inputPath, got, tt.want)
    79  			}
    80  		})
    81  	}
    82  }
    83  
    84  func TestExtractor_Extract(t *testing.T) {
    85  	tests := []extracttest.TestTableEntry{
    86  		{
    87  			Name: "invalid yaml",
    88  			InputConfig: extracttest.ScanInputMockConfig{
    89  				Path: "testdata/not-yaml.txt",
    90  			},
    91  			WantErr:      extracttest.ContainsErrStr{Str: "could not extract"},
    92  			WantPackages: nil,
    93  		},
    94  		{
    95  			Name: "invalid dep path",
    96  			InputConfig: extracttest.ScanInputMockConfig{
    97  				Path: "testdata/invalid-path.yaml",
    98  			},
    99  			WantErr: extracttest.ContainsErrStr{Str: "invalid dependency path"},
   100  			WantPackages: []*extractor.Package{
   101  				{
   102  					Name:       "acorn",
   103  					Version:    "8.7.0",
   104  					PURLType:   purl.TypeNPM,
   105  					Locations:  []string{"testdata/invalid-path.yaml"},
   106  					SourceCode: &extractor.SourceCodeIdentifier{},
   107  					Metadata: osv.DepGroupMetadata{
   108  						DepGroupVals: []string{},
   109  					},
   110  				},
   111  			},
   112  		},
   113  		{
   114  			Name: "invalid dep paths (first error)",
   115  			InputConfig: extracttest.ScanInputMockConfig{
   116  				Path: "testdata/invalid-paths.yaml",
   117  			},
   118  			WantErr: extracttest.ContainsErrStr{Str: "invalid dependency path: invalidpath1"},
   119  			WantPackages: []*extractor.Package{
   120  				{
   121  					Name:       "acorn",
   122  					Version:    "8.7.0",
   123  					PURLType:   purl.TypeNPM,
   124  					Locations:  []string{"testdata/invalid-paths.yaml"},
   125  					SourceCode: &extractor.SourceCodeIdentifier{},
   126  					Metadata: osv.DepGroupMetadata{
   127  						DepGroupVals: []string{},
   128  					},
   129  				},
   130  			},
   131  		},
   132  		{
   133  			Name: "invalid dep paths (second error)",
   134  			InputConfig: extracttest.ScanInputMockConfig{
   135  				Path: "testdata/invalid-paths.yaml",
   136  			},
   137  			WantErr: extracttest.ContainsErrStr{Str: "invalid dependency path: invalidpath2"},
   138  			WantPackages: []*extractor.Package{
   139  				{
   140  					Name:       "acorn",
   141  					Version:    "8.7.0",
   142  					PURLType:   purl.TypeNPM,
   143  					Locations:  []string{"testdata/invalid-paths.yaml"},
   144  					SourceCode: &extractor.SourceCodeIdentifier{},
   145  					Metadata: osv.DepGroupMetadata{
   146  						DepGroupVals: []string{},
   147  					},
   148  				},
   149  			},
   150  		},
   151  		{
   152  			Name: "empty",
   153  			InputConfig: extracttest.ScanInputMockConfig{
   154  				Path: "testdata/empty.yaml",
   155  			},
   156  			WantPackages: []*extractor.Package{},
   157  		},
   158  		{
   159  			Name: "no packages",
   160  			InputConfig: extracttest.ScanInputMockConfig{
   161  				Path: "testdata/no-packages.yaml",
   162  			},
   163  			WantPackages: []*extractor.Package{},
   164  		},
   165  		{
   166  			Name: "one package",
   167  			InputConfig: extracttest.ScanInputMockConfig{
   168  				Path: "testdata/one-package.yaml",
   169  			},
   170  			WantPackages: []*extractor.Package{
   171  				{
   172  					Name:       "acorn",
   173  					Version:    "8.7.0",
   174  					PURLType:   purl.TypeNPM,
   175  					Locations:  []string{"testdata/one-package.yaml"},
   176  					SourceCode: &extractor.SourceCodeIdentifier{},
   177  					Metadata: osv.DepGroupMetadata{
   178  						DepGroupVals: []string{},
   179  					},
   180  				},
   181  			},
   182  		},
   183  		{
   184  			Name: "one package v6 lockfile",
   185  			InputConfig: extracttest.ScanInputMockConfig{
   186  				Path: "testdata/one-package-v6-lockfile.yaml",
   187  			},
   188  			WantPackages: []*extractor.Package{
   189  				{
   190  					Name:       "acorn",
   191  					Version:    "8.7.0",
   192  					PURLType:   purl.TypeNPM,
   193  					Locations:  []string{"testdata/one-package-v6-lockfile.yaml"},
   194  					SourceCode: &extractor.SourceCodeIdentifier{},
   195  					Metadata: osv.DepGroupMetadata{
   196  						DepGroupVals: []string{},
   197  					},
   198  				},
   199  			},
   200  		},
   201  		{
   202  			Name: "one package dev",
   203  			InputConfig: extracttest.ScanInputMockConfig{
   204  				Path: "testdata/one-package-dev.yaml",
   205  			},
   206  			WantPackages: []*extractor.Package{
   207  				{
   208  					Name:       "acorn",
   209  					Version:    "8.7.0",
   210  					PURLType:   purl.TypeNPM,
   211  					Locations:  []string{"testdata/one-package-dev.yaml"},
   212  					SourceCode: &extractor.SourceCodeIdentifier{},
   213  					Metadata: osv.DepGroupMetadata{
   214  						DepGroupVals: []string{},
   215  					},
   216  				},
   217  			},
   218  		},
   219  		{
   220  			Name: "scoped packages",
   221  			InputConfig: extracttest.ScanInputMockConfig{
   222  				Path: "testdata/scoped-packages.yaml",
   223  			},
   224  			WantPackages: []*extractor.Package{
   225  				{
   226  					Name:       "@typescript-eslint/types",
   227  					Version:    "5.13.0",
   228  					PURLType:   purl.TypeNPM,
   229  					Locations:  []string{"testdata/scoped-packages.yaml"},
   230  					SourceCode: &extractor.SourceCodeIdentifier{},
   231  					Metadata: osv.DepGroupMetadata{
   232  						DepGroupVals: []string{},
   233  					},
   234  				},
   235  			},
   236  		},
   237  		{
   238  			Name: "scoped packages v6 lockfile",
   239  			InputConfig: extracttest.ScanInputMockConfig{
   240  				Path: "testdata/scoped-packages-v6-lockfile.yaml",
   241  			},
   242  			WantPackages: []*extractor.Package{
   243  				{
   244  					Name:       "@typescript-eslint/types",
   245  					Version:    "5.57.1",
   246  					PURLType:   purl.TypeNPM,
   247  					Locations:  []string{"testdata/scoped-packages-v6-lockfile.yaml"},
   248  					SourceCode: &extractor.SourceCodeIdentifier{},
   249  					Metadata: osv.DepGroupMetadata{
   250  						DepGroupVals: []string{},
   251  					},
   252  				},
   253  			},
   254  		},
   255  		{
   256  			Name: "peer dependencies",
   257  			InputConfig: extracttest.ScanInputMockConfig{
   258  				Path: "testdata/peer-dependencies.yaml",
   259  			},
   260  			WantPackages: []*extractor.Package{
   261  				{
   262  					Name:       "acorn-jsx",
   263  					Version:    "5.3.2",
   264  					PURLType:   purl.TypeNPM,
   265  					Locations:  []string{"testdata/peer-dependencies.yaml"},
   266  					SourceCode: &extractor.SourceCodeIdentifier{},
   267  					Metadata: osv.DepGroupMetadata{
   268  						DepGroupVals: []string{},
   269  					},
   270  				},
   271  				{
   272  					Name:       "acorn",
   273  					Version:    "8.7.0",
   274  					PURLType:   purl.TypeNPM,
   275  					Locations:  []string{"testdata/peer-dependencies.yaml"},
   276  					SourceCode: &extractor.SourceCodeIdentifier{},
   277  					Metadata: osv.DepGroupMetadata{
   278  						DepGroupVals: []string{},
   279  					},
   280  				},
   281  			},
   282  		},
   283  		{
   284  			Name: "peer_dependencies_v6",
   285  			InputConfig: extracttest.ScanInputMockConfig{
   286  				Path: "testdata/peer-dependencies-v6.yaml",
   287  			},
   288  			WantPackages: []*extractor.Package{
   289  				{
   290  					Name:       "js-tokens",
   291  					Version:    "4.0.0",
   292  					PURLType:   purl.TypeNPM,
   293  					Locations:  []string{"testdata/peer-dependencies-v6.yaml"},
   294  					SourceCode: &extractor.SourceCodeIdentifier{},
   295  					Metadata: osv.DepGroupMetadata{
   296  						DepGroupVals: []string{},
   297  					},
   298  				},
   299  				{
   300  					Name:       "loose-envify",
   301  					Version:    "1.4.0",
   302  					PURLType:   purl.TypeNPM,
   303  					Locations:  []string{"testdata/peer-dependencies-v6.yaml"},
   304  					SourceCode: &extractor.SourceCodeIdentifier{},
   305  					Metadata: osv.DepGroupMetadata{
   306  						DepGroupVals: []string{},
   307  					},
   308  				},
   309  				{
   310  					Name:       "react-dom",
   311  					Version:    "18.2.0",
   312  					PURLType:   purl.TypeNPM,
   313  					Locations:  []string{"testdata/peer-dependencies-v6.yaml"},
   314  					SourceCode: &extractor.SourceCodeIdentifier{},
   315  					Metadata: osv.DepGroupMetadata{
   316  						DepGroupVals: []string{},
   317  					},
   318  				},
   319  				{
   320  					Name:       "react",
   321  					Version:    "18.2.0",
   322  					PURLType:   purl.TypeNPM,
   323  					Locations:  []string{"testdata/peer-dependencies-v6.yaml"},
   324  					SourceCode: &extractor.SourceCodeIdentifier{},
   325  					Metadata: osv.DepGroupMetadata{
   326  						DepGroupVals: []string{},
   327  					},
   328  				},
   329  				{
   330  					Name:       "scheduler",
   331  					Version:    "0.23.0",
   332  					PURLType:   purl.TypeNPM,
   333  					Locations:  []string{"testdata/peer-dependencies-v6.yaml"},
   334  					SourceCode: &extractor.SourceCodeIdentifier{},
   335  					Metadata: osv.DepGroupMetadata{
   336  						DepGroupVals: []string{},
   337  					},
   338  				},
   339  			},
   340  		},
   341  		{
   342  			Name: "peer dependencies advanced",
   343  			InputConfig: extracttest.ScanInputMockConfig{
   344  				Path: "testdata/peer-dependencies-advanced.yaml",
   345  			},
   346  			WantPackages: []*extractor.Package{
   347  				{
   348  					Name:       "@typescript-eslint/eslint-plugin",
   349  					Version:    "5.13.0",
   350  					PURLType:   purl.TypeNPM,
   351  					Locations:  []string{"testdata/peer-dependencies-advanced.yaml"},
   352  					SourceCode: &extractor.SourceCodeIdentifier{},
   353  					Metadata: osv.DepGroupMetadata{
   354  						DepGroupVals: []string{},
   355  					},
   356  				},
   357  				{
   358  					Name:       "@typescript-eslint/parser",
   359  					Version:    "5.13.0",
   360  					PURLType:   purl.TypeNPM,
   361  					Locations:  []string{"testdata/peer-dependencies-advanced.yaml"},
   362  					SourceCode: &extractor.SourceCodeIdentifier{},
   363  					Metadata: osv.DepGroupMetadata{
   364  						DepGroupVals: []string{},
   365  					},
   366  				},
   367  				{
   368  					Name:       "@typescript-eslint/type-utils",
   369  					Version:    "5.13.0",
   370  					PURLType:   purl.TypeNPM,
   371  					Locations:  []string{"testdata/peer-dependencies-advanced.yaml"},
   372  					SourceCode: &extractor.SourceCodeIdentifier{},
   373  					Metadata: osv.DepGroupMetadata{
   374  						DepGroupVals: []string{},
   375  					},
   376  				},
   377  				{
   378  					Name:       "@typescript-eslint/types",
   379  					Version:    "5.13.0",
   380  					PURLType:   purl.TypeNPM,
   381  					Locations:  []string{"testdata/peer-dependencies-advanced.yaml"},
   382  					SourceCode: &extractor.SourceCodeIdentifier{},
   383  					Metadata: osv.DepGroupMetadata{
   384  						DepGroupVals: []string{},
   385  					},
   386  				},
   387  				{
   388  					Name:       "@typescript-eslint/typescript-estree",
   389  					Version:    "5.13.0",
   390  					PURLType:   purl.TypeNPM,
   391  					Locations:  []string{"testdata/peer-dependencies-advanced.yaml"},
   392  					SourceCode: &extractor.SourceCodeIdentifier{},
   393  					Metadata: osv.DepGroupMetadata{
   394  						DepGroupVals: []string{},
   395  					},
   396  				},
   397  				{
   398  					Name:       "@typescript-eslint/utils",
   399  					Version:    "5.13.0",
   400  					PURLType:   purl.TypeNPM,
   401  					Locations:  []string{"testdata/peer-dependencies-advanced.yaml"},
   402  					SourceCode: &extractor.SourceCodeIdentifier{},
   403  					Metadata: osv.DepGroupMetadata{
   404  						DepGroupVals: []string{},
   405  					},
   406  				},
   407  				{
   408  					Name:       "eslint-utils",
   409  					Version:    "3.0.0",
   410  					PURLType:   purl.TypeNPM,
   411  					Locations:  []string{"testdata/peer-dependencies-advanced.yaml"},
   412  					SourceCode: &extractor.SourceCodeIdentifier{},
   413  					Metadata: osv.DepGroupMetadata{
   414  						DepGroupVals: []string{},
   415  					},
   416  				},
   417  				{
   418  					Name:       "eslint",
   419  					Version:    "8.10.0",
   420  					PURLType:   purl.TypeNPM,
   421  					Locations:  []string{"testdata/peer-dependencies-advanced.yaml"},
   422  					SourceCode: &extractor.SourceCodeIdentifier{},
   423  					Metadata: osv.DepGroupMetadata{
   424  						DepGroupVals: []string{},
   425  					},
   426  				},
   427  				{
   428  					Name:       "tsutils",
   429  					Version:    "3.21.0",
   430  					PURLType:   purl.TypeNPM,
   431  					Locations:  []string{"testdata/peer-dependencies-advanced.yaml"},
   432  					SourceCode: &extractor.SourceCodeIdentifier{},
   433  					Metadata: osv.DepGroupMetadata{
   434  						DepGroupVals: []string{},
   435  					},
   436  				},
   437  			},
   438  		},
   439  		{
   440  			Name: "peer_dependencies_advanced_v6",
   441  			InputConfig: extracttest.ScanInputMockConfig{
   442  				Path: "testdata/peer-dependencies-advanced-v6.yaml",
   443  			},
   444  			WantPackages: []*extractor.Package{
   445  				{
   446  					Name:       "js-tokens",
   447  					Version:    "4.0.0",
   448  					PURLType:   purl.TypeNPM,
   449  					Locations:  []string{"testdata/peer-dependencies-advanced-v6.yaml"},
   450  					SourceCode: &extractor.SourceCodeIdentifier{},
   451  					Metadata: osv.DepGroupMetadata{
   452  						DepGroupVals: []string{},
   453  					},
   454  				},
   455  				{
   456  					Name:       "loose-envify",
   457  					Version:    "1.4.0",
   458  					PURLType:   purl.TypeNPM,
   459  					Locations:  []string{"testdata/peer-dependencies-advanced-v6.yaml"},
   460  					SourceCode: &extractor.SourceCodeIdentifier{},
   461  					Metadata: osv.DepGroupMetadata{
   462  						DepGroupVals: []string{},
   463  					},
   464  				},
   465  				{
   466  					Name:       "react-dom",
   467  					Version:    "18.3.0-canary-ab31a9ed2-20230824",
   468  					PURLType:   purl.TypeNPM,
   469  					Locations:  []string{"testdata/peer-dependencies-advanced-v6.yaml"},
   470  					SourceCode: &extractor.SourceCodeIdentifier{},
   471  					Metadata: osv.DepGroupMetadata{
   472  						DepGroupVals: []string{},
   473  					},
   474  				},
   475  				{
   476  					Name:       "react",
   477  					Version:    "18.3.0-canary-ab31a9ed2-20230824",
   478  					PURLType:   purl.TypeNPM,
   479  					Locations:  []string{"testdata/peer-dependencies-advanced-v6.yaml"},
   480  					SourceCode: &extractor.SourceCodeIdentifier{},
   481  					Metadata: osv.DepGroupMetadata{
   482  						DepGroupVals: []string{},
   483  					},
   484  				},
   485  				{
   486  					Name:       "scheduler",
   487  					Version:    "0.24.0-canary-ab31a9ed2-20230824",
   488  					PURLType:   purl.TypeNPM,
   489  					Locations:  []string{"testdata/peer-dependencies-advanced-v6.yaml"},
   490  					SourceCode: &extractor.SourceCodeIdentifier{},
   491  					Metadata: osv.DepGroupMetadata{
   492  						DepGroupVals: []string{},
   493  					},
   494  				},
   495  			},
   496  		},
   497  		{
   498  			Name: "peer_dependencies_advanced_rc_v6",
   499  			InputConfig: extracttest.ScanInputMockConfig{
   500  				Path: "testdata/peer-dependencies-advanced-rc-v6.yaml",
   501  			},
   502  			WantPackages: []*extractor.Package{
   503  				{
   504  					Name:       "js-tokens",
   505  					Version:    "4.0.0",
   506  					PURLType:   purl.TypeNPM,
   507  					Locations:  []string{"testdata/peer-dependencies-advanced-rc-v6.yaml"},
   508  					SourceCode: &extractor.SourceCodeIdentifier{},
   509  					Metadata: osv.DepGroupMetadata{
   510  						DepGroupVals: []string{},
   511  					},
   512  				},
   513  				{
   514  					Name:       "loose-envify",
   515  					Version:    "1.4.0",
   516  					PURLType:   purl.TypeNPM,
   517  					Locations:  []string{"testdata/peer-dependencies-advanced-rc-v6.yaml"},
   518  					SourceCode: &extractor.SourceCodeIdentifier{},
   519  					Metadata: osv.DepGroupMetadata{
   520  						DepGroupVals: []string{},
   521  					},
   522  				},
   523  				{
   524  					Name:       "react-dom",
   525  					Version:    "18.0.0-rc.3",
   526  					PURLType:   purl.TypeNPM,
   527  					Locations:  []string{"testdata/peer-dependencies-advanced-rc-v6.yaml"},
   528  					SourceCode: &extractor.SourceCodeIdentifier{},
   529  					Metadata: osv.DepGroupMetadata{
   530  						DepGroupVals: []string{},
   531  					},
   532  				},
   533  				{
   534  					Name:       "react",
   535  					Version:    "18.2.0",
   536  					PURLType:   purl.TypeNPM,
   537  					Locations:  []string{"testdata/peer-dependencies-advanced-rc-v6.yaml"},
   538  					SourceCode: &extractor.SourceCodeIdentifier{},
   539  					Metadata: osv.DepGroupMetadata{
   540  						DepGroupVals: []string{},
   541  					},
   542  				},
   543  				{
   544  					Name:       "scheduler",
   545  					Version:    "0.21.0",
   546  					PURLType:   purl.TypeNPM,
   547  					Locations:  []string{"testdata/peer-dependencies-advanced-rc-v6.yaml"},
   548  					SourceCode: &extractor.SourceCodeIdentifier{},
   549  					Metadata: osv.DepGroupMetadata{
   550  						DepGroupVals: []string{},
   551  					},
   552  				},
   553  			},
   554  		},
   555  		{
   556  			Name: "multiple packages",
   557  			InputConfig: extracttest.ScanInputMockConfig{
   558  				Path: "testdata/multiple-packages.yaml",
   559  			},
   560  			WantPackages: []*extractor.Package{
   561  				{
   562  					Name:       "aws-sdk",
   563  					Version:    "2.1087.0",
   564  					PURLType:   purl.TypeNPM,
   565  					Locations:  []string{"testdata/multiple-packages.yaml"},
   566  					SourceCode: &extractor.SourceCodeIdentifier{},
   567  					Metadata: osv.DepGroupMetadata{
   568  						DepGroupVals: []string{},
   569  					},
   570  				},
   571  				{
   572  					Name:       "base64-js",
   573  					Version:    "1.5.1",
   574  					PURLType:   purl.TypeNPM,
   575  					Locations:  []string{"testdata/multiple-packages.yaml"},
   576  					SourceCode: &extractor.SourceCodeIdentifier{},
   577  					Metadata: osv.DepGroupMetadata{
   578  						DepGroupVals: []string{},
   579  					},
   580  				},
   581  				{
   582  					Name:       "buffer",
   583  					Version:    "4.9.2",
   584  					PURLType:   purl.TypeNPM,
   585  					Locations:  []string{"testdata/multiple-packages.yaml"},
   586  					SourceCode: &extractor.SourceCodeIdentifier{},
   587  					Metadata: osv.DepGroupMetadata{
   588  						DepGroupVals: []string{},
   589  					},
   590  				},
   591  				{
   592  					Name:       "events",
   593  					Version:    "1.1.1",
   594  					PURLType:   purl.TypeNPM,
   595  					Locations:  []string{"testdata/multiple-packages.yaml"},
   596  					SourceCode: &extractor.SourceCodeIdentifier{},
   597  					Metadata: osv.DepGroupMetadata{
   598  						DepGroupVals: []string{},
   599  					},
   600  				},
   601  				{
   602  					Name:       "ieee754",
   603  					Version:    "1.1.13",
   604  					PURLType:   purl.TypeNPM,
   605  					Locations:  []string{"testdata/multiple-packages.yaml"},
   606  					SourceCode: &extractor.SourceCodeIdentifier{},
   607  					Metadata: osv.DepGroupMetadata{
   608  						DepGroupVals: []string{},
   609  					},
   610  				},
   611  				{
   612  					Name:       "isarray",
   613  					Version:    "1.0.0",
   614  					PURLType:   purl.TypeNPM,
   615  					Locations:  []string{"testdata/multiple-packages.yaml"},
   616  					SourceCode: &extractor.SourceCodeIdentifier{},
   617  					Metadata: osv.DepGroupMetadata{
   618  						DepGroupVals: []string{},
   619  					},
   620  				},
   621  				{
   622  					Name:       "jmespath",
   623  					Version:    "0.16.0",
   624  					PURLType:   purl.TypeNPM,
   625  					Locations:  []string{"testdata/multiple-packages.yaml"},
   626  					SourceCode: &extractor.SourceCodeIdentifier{},
   627  					Metadata: osv.DepGroupMetadata{
   628  						DepGroupVals: []string{},
   629  					},
   630  				},
   631  				{
   632  					Name:       "punycode",
   633  					Version:    "1.3.2",
   634  					PURLType:   purl.TypeNPM,
   635  					Locations:  []string{"testdata/multiple-packages.yaml"},
   636  					SourceCode: &extractor.SourceCodeIdentifier{},
   637  					Metadata: osv.DepGroupMetadata{
   638  						DepGroupVals: []string{},
   639  					},
   640  				},
   641  				{
   642  					Name:       "querystring",
   643  					Version:    "0.2.0",
   644  					PURLType:   purl.TypeNPM,
   645  					Locations:  []string{"testdata/multiple-packages.yaml"},
   646  					SourceCode: &extractor.SourceCodeIdentifier{},
   647  					Metadata: osv.DepGroupMetadata{
   648  						DepGroupVals: []string{},
   649  					},
   650  				},
   651  				{
   652  					Name:       "sax",
   653  					Version:    "1.2.1",
   654  					PURLType:   purl.TypeNPM,
   655  					Locations:  []string{"testdata/multiple-packages.yaml"},
   656  					SourceCode: &extractor.SourceCodeIdentifier{},
   657  					Metadata: osv.DepGroupMetadata{
   658  						DepGroupVals: []string{},
   659  					},
   660  				},
   661  				{
   662  					Name:       "url",
   663  					Version:    "0.10.3",
   664  					PURLType:   purl.TypeNPM,
   665  					Locations:  []string{"testdata/multiple-packages.yaml"},
   666  					SourceCode: &extractor.SourceCodeIdentifier{},
   667  					Metadata: osv.DepGroupMetadata{
   668  						DepGroupVals: []string{},
   669  					},
   670  				},
   671  				{
   672  					Name:       "uuid",
   673  					Version:    "3.3.2",
   674  					PURLType:   purl.TypeNPM,
   675  					Locations:  []string{"testdata/multiple-packages.yaml"},
   676  					SourceCode: &extractor.SourceCodeIdentifier{},
   677  					Metadata: osv.DepGroupMetadata{
   678  						DepGroupVals: []string{},
   679  					},
   680  				},
   681  				{
   682  					Name:       "xml2js",
   683  					Version:    "0.4.19",
   684  					PURLType:   purl.TypeNPM,
   685  					Locations:  []string{"testdata/multiple-packages.yaml"},
   686  					SourceCode: &extractor.SourceCodeIdentifier{},
   687  					Metadata: osv.DepGroupMetadata{
   688  						DepGroupVals: []string{},
   689  					},
   690  				},
   691  				{
   692  					Name:       "xmlbuilder",
   693  					Version:    "9.0.7",
   694  					PURLType:   purl.TypeNPM,
   695  					Locations:  []string{"testdata/multiple-packages.yaml"},
   696  					SourceCode: &extractor.SourceCodeIdentifier{},
   697  					Metadata: osv.DepGroupMetadata{
   698  						DepGroupVals: []string{},
   699  					},
   700  				},
   701  			},
   702  		},
   703  		{
   704  			Name: "multiple versions",
   705  			InputConfig: extracttest.ScanInputMockConfig{
   706  				Path: "testdata/multiple-versions.yaml",
   707  			},
   708  			WantPackages: []*extractor.Package{
   709  				{
   710  					Name:       "uuid",
   711  					Version:    "3.3.2",
   712  					PURLType:   purl.TypeNPM,
   713  					Locations:  []string{"testdata/multiple-versions.yaml"},
   714  					SourceCode: &extractor.SourceCodeIdentifier{},
   715  					Metadata: osv.DepGroupMetadata{
   716  						DepGroupVals: []string{},
   717  					},
   718  				},
   719  				{
   720  					Name:       "uuid",
   721  					Version:    "8.3.2",
   722  					PURLType:   purl.TypeNPM,
   723  					Locations:  []string{"testdata/multiple-versions.yaml"},
   724  					SourceCode: &extractor.SourceCodeIdentifier{},
   725  					Metadata: osv.DepGroupMetadata{
   726  						DepGroupVals: []string{},
   727  					},
   728  				},
   729  				{
   730  					Name:       "xmlbuilder",
   731  					Version:    "9.0.7",
   732  					PURLType:   purl.TypeNPM,
   733  					Locations:  []string{"testdata/multiple-versions.yaml"},
   734  					SourceCode: &extractor.SourceCodeIdentifier{},
   735  					Metadata: osv.DepGroupMetadata{
   736  						DepGroupVals: []string{},
   737  					},
   738  				},
   739  			},
   740  		},
   741  		{
   742  			Name: "tarball",
   743  			InputConfig: extracttest.ScanInputMockConfig{
   744  				Path: "testdata/tarball.yaml",
   745  			},
   746  			WantPackages: []*extractor.Package{
   747  				{
   748  					Name:       "@my-org/my-package",
   749  					Version:    "3.2.3",
   750  					PURLType:   purl.TypeNPM,
   751  					Locations:  []string{"testdata/tarball.yaml"},
   752  					SourceCode: &extractor.SourceCodeIdentifier{},
   753  					Metadata: osv.DepGroupMetadata{
   754  						DepGroupVals: []string{"dev"},
   755  					},
   756  				},
   757  			},
   758  		},
   759  		{
   760  			Name: "exotic",
   761  			InputConfig: extracttest.ScanInputMockConfig{
   762  				Path: "testdata/exotic.yaml",
   763  			},
   764  			WantPackages: []*extractor.Package{
   765  				{
   766  					Name:       "foo",
   767  					Version:    "1.0.0",
   768  					PURLType:   purl.TypeNPM,
   769  					Locations:  []string{"testdata/exotic.yaml"},
   770  					SourceCode: &extractor.SourceCodeIdentifier{},
   771  					Metadata: osv.DepGroupMetadata{
   772  						DepGroupVals: []string{},
   773  					},
   774  				},
   775  				{
   776  					Name:       "@foo/bar",
   777  					Version:    "1.0.0",
   778  					PURLType:   purl.TypeNPM,
   779  					Locations:  []string{"testdata/exotic.yaml"},
   780  					SourceCode: &extractor.SourceCodeIdentifier{},
   781  					Metadata: osv.DepGroupMetadata{
   782  						DepGroupVals: []string{},
   783  					},
   784  				},
   785  				{
   786  					Name:       "foo",
   787  					Version:    "1.1.0",
   788  					PURLType:   purl.TypeNPM,
   789  					Locations:  []string{"testdata/exotic.yaml"},
   790  					SourceCode: &extractor.SourceCodeIdentifier{},
   791  					Metadata: osv.DepGroupMetadata{
   792  						DepGroupVals: []string{},
   793  					},
   794  				},
   795  				{
   796  					Name:       "@foo/bar",
   797  					Version:    "1.1.0",
   798  					PURLType:   purl.TypeNPM,
   799  					Locations:  []string{"testdata/exotic.yaml"},
   800  					SourceCode: &extractor.SourceCodeIdentifier{},
   801  					Metadata: osv.DepGroupMetadata{
   802  						DepGroupVals: []string{},
   803  					},
   804  				},
   805  				{
   806  					Name:       "foo",
   807  					Version:    "1.2.0",
   808  					PURLType:   purl.TypeNPM,
   809  					Locations:  []string{"testdata/exotic.yaml"},
   810  					SourceCode: &extractor.SourceCodeIdentifier{},
   811  					Metadata: osv.DepGroupMetadata{
   812  						DepGroupVals: []string{},
   813  					},
   814  				},
   815  				{
   816  					Name:       "foo",
   817  					Version:    "1.3.0",
   818  					PURLType:   purl.TypeNPM,
   819  					Locations:  []string{"testdata/exotic.yaml"},
   820  					SourceCode: &extractor.SourceCodeIdentifier{},
   821  					Metadata: osv.DepGroupMetadata{
   822  						DepGroupVals: []string{},
   823  					},
   824  				},
   825  				{
   826  					Name:       "foo",
   827  					Version:    "1.4.0",
   828  					PURLType:   purl.TypeNPM,
   829  					Locations:  []string{"testdata/exotic.yaml"},
   830  					SourceCode: &extractor.SourceCodeIdentifier{},
   831  					Metadata: osv.DepGroupMetadata{
   832  						DepGroupVals: []string{},
   833  					},
   834  				},
   835  			},
   836  		},
   837  		{
   838  			Name: "commits",
   839  			InputConfig: extracttest.ScanInputMockConfig{
   840  				Path: "testdata/commits.yaml",
   841  			},
   842  			WantPackages: []*extractor.Package{
   843  				{
   844  					Name:      "my-bitbucket-package",
   845  					Version:   "1.0.0",
   846  					PURLType:  purl.TypeNPM,
   847  					Locations: []string{"testdata/commits.yaml"},
   848  					SourceCode: &extractor.SourceCodeIdentifier{
   849  						Commit: "6104ae42cd32c3d724036d3964678f197b2c9cdb",
   850  					},
   851  					Metadata: osv.DepGroupMetadata{
   852  						DepGroupVals: []string{},
   853  					},
   854  				},
   855  				{
   856  					Name:      "@my-scope/my-package",
   857  					Version:   "1.0.0",
   858  					PURLType:  purl.TypeNPM,
   859  					Locations: []string{"testdata/commits.yaml"},
   860  					SourceCode: &extractor.SourceCodeIdentifier{
   861  						Commit: "267087851ad5fac92a184749c27cd539e2fc862e",
   862  					},
   863  					Metadata: osv.DepGroupMetadata{
   864  						DepGroupVals: []string{},
   865  					},
   866  				},
   867  				{
   868  					Name:      "@my-scope/my-other-package",
   869  					Version:   "1.0.0",
   870  					PURLType:  purl.TypeNPM,
   871  					Locations: []string{"testdata/commits.yaml"},
   872  					SourceCode: &extractor.SourceCodeIdentifier{
   873  						Commit: "fbfc962ab51eb1d754749b68c064460221fbd689",
   874  					},
   875  					Metadata: osv.DepGroupMetadata{
   876  						DepGroupVals: []string{},
   877  					},
   878  				},
   879  				{
   880  					Name:      "faker-parser",
   881  					Version:   "0.0.1",
   882  					PURLType:  purl.TypeNPM,
   883  					Locations: []string{"testdata/commits.yaml"},
   884  					SourceCode: &extractor.SourceCodeIdentifier{
   885  						Commit: "d2dc42a9351d4d89ec48c525e34f612b6d77993f",
   886  					},
   887  					Metadata: osv.DepGroupMetadata{
   888  						DepGroupVals: []string{},
   889  					},
   890  				},
   891  				{
   892  					Name:      "mocks",
   893  					Version:   "20.0.1",
   894  					PURLType:  purl.TypeNPM,
   895  					Locations: []string{"testdata/commits.yaml"},
   896  					SourceCode: &extractor.SourceCodeIdentifier{
   897  						Commit: "590f321b4eb3f692bb211bd74e22947639a6f79d",
   898  					},
   899  					Metadata: osv.DepGroupMetadata{
   900  						DepGroupVals: []string{},
   901  					},
   902  				},
   903  			},
   904  		},
   905  		{
   906  			Name: "files",
   907  			InputConfig: extracttest.ScanInputMockConfig{
   908  				Path: "testdata/files.yaml",
   909  			},
   910  			WantPackages: []*extractor.Package{
   911  				{
   912  					Name:       "my-file-package",
   913  					Version:    "0.0.0",
   914  					PURLType:   purl.TypeNPM,
   915  					Locations:  []string{"testdata/files.yaml"},
   916  					SourceCode: &extractor.SourceCodeIdentifier{},
   917  					Metadata: osv.DepGroupMetadata{
   918  						DepGroupVals: []string{},
   919  					},
   920  				},
   921  				{
   922  					Name:       "a-local-package",
   923  					Version:    "1.0.0",
   924  					PURLType:   purl.TypeNPM,
   925  					Locations:  []string{"testdata/files.yaml"},
   926  					SourceCode: &extractor.SourceCodeIdentifier{},
   927  					Metadata: osv.DepGroupMetadata{
   928  						DepGroupVals: []string{},
   929  					},
   930  				},
   931  				{
   932  					Name:       "a-nested-local-package",
   933  					Version:    "1.0.0",
   934  					PURLType:   purl.TypeNPM,
   935  					Locations:  []string{"testdata/files.yaml"},
   936  					SourceCode: &extractor.SourceCodeIdentifier{},
   937  					Metadata: osv.DepGroupMetadata{
   938  						DepGroupVals: []string{},
   939  					},
   940  				},
   941  				{
   942  					Name:       "one-up",
   943  					Version:    "1.0.0",
   944  					PURLType:   purl.TypeNPM,
   945  					Locations:  []string{"testdata/files.yaml"},
   946  					SourceCode: &extractor.SourceCodeIdentifier{},
   947  					Metadata: osv.DepGroupMetadata{
   948  						DepGroupVals: []string{},
   949  					},
   950  				},
   951  				{
   952  					Name:       "one-up-with-peer",
   953  					Version:    "1.0.0",
   954  					PURLType:   purl.TypeNPM,
   955  					Locations:  []string{"testdata/files.yaml"},
   956  					SourceCode: &extractor.SourceCodeIdentifier{},
   957  					Metadata: osv.DepGroupMetadata{
   958  						DepGroupVals: []string{},
   959  					},
   960  				},
   961  			},
   962  		},
   963  	}
   964  
   965  	for _, tt := range tests {
   966  		t.Run(tt.Name, func(t *testing.T) {
   967  			extr := pnpmlock.Extractor{}
   968  
   969  			scanInput := extracttest.GenerateScanInputMock(t, tt.InputConfig)
   970  			defer extracttest.CloseTestScanInput(t, scanInput)
   971  
   972  			got, err := extr.Extract(t.Context(), &scanInput)
   973  
   974  			if diff := cmp.Diff(tt.WantErr, err, cmpopts.EquateErrors()); diff != "" {
   975  				t.Errorf("%s.Extract(%q) error diff (-want +got):\n%s", extr.Name(), tt.InputConfig.Path, diff)
   976  				return
   977  			}
   978  
   979  			wantInv := inventory.Inventory{Packages: tt.WantPackages}
   980  			if diff := cmp.Diff(wantInv, got, cmpopts.SortSlices(extracttest.PackageCmpLess)); diff != "" {
   981  				t.Errorf("%s.Extract(%q) diff (-want +got):\n%s", extr.Name(), tt.InputConfig.Path, diff)
   982  			}
   983  		})
   984  	}
   985  }