github.com/anchore/syft@v1.38.2/syft/pkg/cataloger/javascript/parse_pnpm_lock_test.go (about)

     1  package javascript
     2  
     3  import (
     4  	"context"
     5  	"io"
     6  	"net/http"
     7  	"net/http/httptest"
     8  	"os"
     9  	"testing"
    10  
    11  	"github.com/anchore/syft/syft/artifact"
    12  	"github.com/anchore/syft/syft/file"
    13  	"github.com/anchore/syft/syft/pkg"
    14  	"github.com/anchore/syft/syft/pkg/cataloger/internal/pkgtest"
    15  )
    16  
    17  func TestParsePnpmLock(t *testing.T) {
    18  	var expectedRelationships []artifact.Relationship
    19  	fixture := "test-fixtures/pnpm/pnpm-lock.yaml"
    20  
    21  	locationSet := file.NewLocationSet(file.NewLocation(fixture))
    22  
    23  	expectedPkgs := []pkg.Package{
    24  		{
    25  			Name:      "nanoid",
    26  			Version:   "3.3.4",
    27  			PURL:      "pkg:npm/nanoid@3.3.4",
    28  			Locations: locationSet,
    29  			Language:  pkg.JavaScript,
    30  			Type:      pkg.NpmPkg,
    31  			Metadata:  pkg.PnpmLockEntry{Resolution: pkg.PnpmLockResolution{}},
    32  		},
    33  		{
    34  			Name:      "picocolors",
    35  			Version:   "1.0.0",
    36  			PURL:      "pkg:npm/picocolors@1.0.0",
    37  			Locations: locationSet,
    38  			Language:  pkg.JavaScript,
    39  			Type:      pkg.NpmPkg,
    40  			Metadata:  pkg.PnpmLockEntry{Resolution: pkg.PnpmLockResolution{}},
    41  		},
    42  		{
    43  			Name:      "source-map-js",
    44  			Version:   "1.0.2",
    45  			PURL:      "pkg:npm/source-map-js@1.0.2",
    46  			Locations: locationSet,
    47  			Language:  pkg.JavaScript,
    48  			Type:      pkg.NpmPkg,
    49  			Metadata:  pkg.PnpmLockEntry{Resolution: pkg.PnpmLockResolution{}},
    50  		},
    51  		{
    52  			Name:      "@bcoe/v8-coverage",
    53  			Version:   "0.2.3",
    54  			PURL:      "pkg:npm/%40bcoe/v8-coverage@0.2.3",
    55  			Locations: locationSet,
    56  			Language:  pkg.JavaScript,
    57  			Type:      pkg.NpmPkg,
    58  			Metadata: pkg.PnpmLockEntry{
    59  				Resolution:   pkg.PnpmLockResolution{Integrity: "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw=="},
    60  				Dependencies: map[string]string{},
    61  			},
    62  		},
    63  	}
    64  
    65  	adapter := newGenericPnpmLockAdapter(CatalogerConfig{})
    66  	pkgtest.TestFileParser(t, fixture, adapter.parsePnpmLock, expectedPkgs, expectedRelationships)
    67  }
    68  
    69  func TestParsePnpmV6Lock(t *testing.T) {
    70  	var expectedRelationships []artifact.Relationship
    71  	fixture := "test-fixtures/pnpm-v6/pnpm-lock.yaml"
    72  
    73  	locationSet := file.NewLocationSet(file.NewLocation(fixture))
    74  
    75  	expectedPkgs := []pkg.Package{
    76  		{
    77  			Name:      "@testing-library/jest-dom",
    78  			Version:   "5.16.5",
    79  			PURL:      "pkg:npm/%40testing-library/jest-dom@5.16.5",
    80  			Locations: locationSet,
    81  			Language:  pkg.JavaScript,
    82  			Type:      pkg.NpmPkg,
    83  			Metadata: pkg.PnpmLockEntry{
    84  				Resolution: pkg.PnpmLockResolution{Integrity: "sha512-N5ixQ2qKpi5OLYfwQmUb/5mSV9LneAcaUfp32pn4yCnpb8r/Yz0pXFPck21dIicKmi+ta5WRAknkZCfA8refMA=="},
    85  				Dependencies: map[string]string{
    86  					"@adobe/css-tools":                 "4.2.0",
    87  					"@babel/runtime":                   "7.21.0",
    88  					"@types/testing-library__jest-dom": "5.14.5",
    89  					"aria-query":                       "5.1.3",
    90  					"chalk":                            "3.0.0",
    91  					"css.escape":                       "1.5.1",
    92  					"dom-accessibility-api":            "0.5.16",
    93  					"lodash":                           "4.17.21",
    94  					"redent":                           "3.0.0",
    95  				}},
    96  		},
    97  		{
    98  			Name:      "@testing-library/react",
    99  			Version:   "13.4.0",
   100  			PURL:      "pkg:npm/%40testing-library/react@13.4.0",
   101  			Locations: locationSet,
   102  			Language:  pkg.JavaScript,
   103  			Type:      pkg.NpmPkg,
   104  			Metadata: pkg.PnpmLockEntry{
   105  				Resolution: pkg.PnpmLockResolution{Integrity: "sha512-sXOGON+WNTh3MLE9rve97ftaZukN3oNf2KjDy7YTx6hcTO2uuLHuCGynMDhFwGw/jYf4OJ2Qk0i4i79qMNNkyw=="},
   106  				Dependencies: map[string]string{
   107  					"@babel/runtime":       "7.21.0",
   108  					"@testing-library/dom": "8.20.0",
   109  					"@types/react-dom":     "18.2.1",
   110  					"react":                "18.2.0",
   111  					"react-dom":            "18.2.0",
   112  				},
   113  			},
   114  		},
   115  		{
   116  			Name:      "@testing-library/user-event",
   117  			Version:   "13.5.0",
   118  			PURL:      "pkg:npm/%40testing-library/user-event@13.5.0",
   119  			Locations: locationSet,
   120  			Language:  pkg.JavaScript,
   121  			Type:      pkg.NpmPkg,
   122  			Metadata: pkg.PnpmLockEntry{
   123  				Resolution: pkg.PnpmLockResolution{Integrity: "sha512-5Kwtbo3Y/NowpkbRuSepbyMFkZmHgD+vPzYB/RJ4oxt5Gj/avFFBYjhw27cqSVPVw/3a67NK1PbiIr9k4Gwmdg=="},
   124  				Dependencies: map[string]string{
   125  					"@babel/runtime":       "7.21.0",
   126  					"@testing-library/dom": "9.2.0",
   127  				},
   128  			},
   129  		},
   130  		{
   131  			Name:      "react",
   132  			Version:   "18.2.0",
   133  			PURL:      "pkg:npm/react@18.2.0",
   134  			Locations: locationSet,
   135  			Language:  pkg.JavaScript,
   136  			Type:      pkg.NpmPkg,
   137  			Metadata: pkg.PnpmLockEntry{
   138  				Resolution: pkg.PnpmLockResolution{Integrity: "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ=="},
   139  				Dependencies: map[string]string{
   140  					"loose-envify": "1.4.0",
   141  				},
   142  			},
   143  		},
   144  		{
   145  			Name:      "react-dom",
   146  			Version:   "18.2.0",
   147  			PURL:      "pkg:npm/react-dom@18.2.0",
   148  			Locations: locationSet,
   149  			Language:  pkg.JavaScript,
   150  			Type:      pkg.NpmPkg,
   151  			Metadata: pkg.PnpmLockEntry{
   152  				Resolution: pkg.PnpmLockResolution{Integrity: "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g=="},
   153  				Dependencies: map[string]string{
   154  					"loose-envify": "1.4.0",
   155  					"react":        "18.2.0",
   156  					"scheduler":    "0.23.0",
   157  				},
   158  			},
   159  		},
   160  		{
   161  			Name:      "web-vitals",
   162  			Version:   "2.1.4",
   163  			PURL:      "pkg:npm/web-vitals@2.1.4",
   164  			Locations: locationSet,
   165  			Language:  pkg.JavaScript,
   166  			Type:      pkg.NpmPkg,
   167  			Metadata: pkg.PnpmLockEntry{
   168  				Resolution:   pkg.PnpmLockResolution{Integrity: "sha512-sVWcwhU5mX6crfI5Vd2dC4qchyTqxV8URinzt25XqVh+bHEPGH4C3NPrNionCP7Obx59wrYEbNlw4Z8sjALzZg=="},
   169  				Dependencies: map[string]string{},
   170  			},
   171  		},
   172  		{
   173  			Name:      "@babel/core",
   174  			Version:   "7.21.4",
   175  			PURL:      "pkg:npm/%40babel/core@7.21.4",
   176  			Locations: locationSet,
   177  			Language:  pkg.JavaScript,
   178  			Type:      pkg.NpmPkg,
   179  			Metadata: pkg.PnpmLockEntry{
   180  				Resolution: pkg.PnpmLockResolution{Integrity: "sha512-qt/YV149Jman/6AfmlxJ04LMIu8bMoyl3RB91yTFrxQmgbrSvQMy7cI8Q62FHx1t8wJ8B5fu0UDoLwHAhUo1QA=="},
   181  				Dependencies: map[string]string{
   182  					"@ampproject/remapping":             "2.2.1",
   183  					"@babel/code-frame":                 "7.21.4",
   184  					"@babel/generator":                  "7.21.4",
   185  					"@babel/helper-compilation-targets": "7.21.4",
   186  					"@babel/helper-module-transforms":   "7.21.2",
   187  					"@babel/helpers":                    "7.21.0",
   188  					"@babel/parser":                     "7.21.4",
   189  					"@babel/template":                   "7.20.7",
   190  					"@babel/traverse":                   "7.21.4",
   191  					"@babel/types":                      "7.21.4",
   192  					"convert-source-map":                "1.9.0",
   193  					"debug":                             "4.3.4",
   194  					"gensync":                           "1.0.0-beta.2",
   195  					"json5":                             "2.2.3",
   196  					"semver":                            "6.3.0",
   197  				},
   198  			},
   199  		},
   200  		{
   201  			Name:      "@types/eslint",
   202  			Version:   "8.37.0",
   203  			PURL:      "pkg:npm/%40types/eslint@8.37.0",
   204  			Locations: locationSet,
   205  			Language:  pkg.JavaScript,
   206  			Type:      pkg.NpmPkg,
   207  			Metadata: pkg.PnpmLockEntry{
   208  				Resolution: pkg.PnpmLockResolution{Integrity: "sha512-Piet7dG2JBuDIfohBngQ3rCt7MgO9xCO4xIMKxBThCq5PNRB91IjlJ10eJVwfoNtvTErmxLzwBZ7rHZtbOMmFQ=="},
   209  				Dependencies: map[string]string{
   210  					"@types/estree":      "1.0.1",
   211  					"@types/json-schema": "7.0.11",
   212  				},
   213  			},
   214  		},
   215  		{
   216  			Name:      "read-cache",
   217  			Version:   "1.0.0",
   218  			PURL:      "pkg:npm/read-cache@1.0.0",
   219  			Locations: locationSet,
   220  			Language:  pkg.JavaScript,
   221  			Type:      pkg.NpmPkg,
   222  			Metadata: pkg.PnpmLockEntry{
   223  				Resolution: pkg.PnpmLockResolution{Integrity: "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA=="},
   224  				Dependencies: map[string]string{
   225  					"pify": "2.3.0",
   226  				},
   227  			},
   228  		},
   229  		{
   230  			Name:      "schema-utils",
   231  			Version:   "3.1.2",
   232  			PURL:      "pkg:npm/schema-utils@3.1.2",
   233  			Locations: locationSet,
   234  			Language:  pkg.JavaScript,
   235  			Type:      pkg.NpmPkg,
   236  			Metadata: pkg.PnpmLockEntry{
   237  				Resolution: pkg.PnpmLockResolution{Integrity: "sha512-pvjEHOgWc9OWA/f/DE3ohBWTD6EleVLf7iFUkoSwAxttdBhB9QUebQgxER2kWueOvRJXPHNnyrvvh9eZINB8Eg=="},
   238  				Dependencies: map[string]string{
   239  					"@types/json-schema": "7.0.11",
   240  					"ajv":                "6.12.6",
   241  					"ajv-keywords":       "3.5.2",
   242  				},
   243  			},
   244  		},
   245  	}
   246  	expectedRelationships = []artifact.Relationship{
   247  		{
   248  			From: expectedPkgs[3],
   249  			To:   expectedPkgs[1],
   250  			Type: artifact.DependencyOfRelationship,
   251  		},
   252  		{
   253  			From: expectedPkgs[3],
   254  			To:   expectedPkgs[4],
   255  			Type: artifact.DependencyOfRelationship,
   256  		},
   257  		{
   258  			From: expectedPkgs[4],
   259  			To:   expectedPkgs[1],
   260  			Type: artifact.DependencyOfRelationship,
   261  		},
   262  	}
   263  	adapter := newGenericPnpmLockAdapter(CatalogerConfig{})
   264  	pkgtest.TestFileParser(t, fixture, adapter.parsePnpmLock, expectedPkgs, expectedRelationships)
   265  }
   266  
   267  func TestParsePnpmLockV9(t *testing.T) {
   268  	var expectedRelationships []artifact.Relationship
   269  	fixture := "test-fixtures/pnpm-v9/pnpm-lock.yaml"
   270  	locationSet := file.NewLocationSet(file.NewLocation(fixture))
   271  
   272  	expected := []pkg.Package{
   273  		{
   274  			Name:      "@babel/core",
   275  			Version:   "7.24.7",
   276  			PURL:      "pkg:npm/%40babel/core@7.24.7",
   277  			Locations: locationSet,
   278  			Language:  pkg.JavaScript,
   279  			Type:      pkg.NpmPkg,
   280  			Metadata:  pkg.PnpmLockEntry{Resolution: pkg.PnpmLockResolution{Integrity: "sha512-4RjkiFFI42+268iBv2nC+iMLTJGQW3u9P7YvA3x/6MDrJ9IYZ8I/xx5a2GIhY5gBTOcI4iC5S5in2fGjE+P4Yw=="}},
   281  		},
   282  		{
   283  			Name:      "@babel/helper-plugin-utils",
   284  			Version:   "7.24.7",
   285  			PURL:      "pkg:npm/%40babel/helper-plugin-utils@7.24.7",
   286  			Locations: locationSet,
   287  			Language:  pkg.JavaScript,
   288  			Type:      pkg.NpmPkg,
   289  			Metadata:  pkg.PnpmLockEntry{Resolution: pkg.PnpmLockResolution{Integrity: "sha512-8A2+zKm53/3w4rwbX11FMW/yFS6c5Vam02P/dw01aK6KbwkKqBaIt3eEATiKtn9I2uS1itk8/aZ2yZ/kURee4Q=="}},
   290  		},
   291  		{
   292  			Name:      "is-positive",
   293  			Version:   "3.1.0",
   294  			PURL:      "pkg:npm/is-positive@3.1.0",
   295  			Locations: locationSet,
   296  			Language:  pkg.JavaScript,
   297  			Type:      pkg.NpmPkg,
   298  			Metadata:  pkg.PnpmLockEntry{Resolution: pkg.PnpmLockResolution{Integrity: "sha512-9ffLCf_f5sopimAhg2g91a7b9Rw5A1aA9eI6S391S3VEzYw99I3iKjcZGxLp25s0cRxNBV5aL2mhn7421SSlA=="}},
   299  		},
   300  		{
   301  			Name:      "rollup",
   302  			Version:   "4.18.0",
   303  			PURL:      "pkg:npm/rollup@4.18.0",
   304  			Locations: locationSet,
   305  			Language:  pkg.JavaScript,
   306  			Type:      pkg.NpmPkg,
   307  			Metadata:  pkg.PnpmLockEntry{Resolution: pkg.PnpmLockResolution{Integrity: "sha512-QpQY2Q5i0y0Q3RoAvoChE/R5iN2k05N//bNvQbC2XvRjHFT1qWJ2r3n1bNqE+gGRJaeuQf0BxE42D7CyuLh3ZQ=="}},
   308  		},
   309  	}
   310  	adapter := newGenericPnpmLockAdapter(CatalogerConfig{})
   311  	// TODO: no relationships are under test
   312  	pkgtest.TestFileParser(t, fixture, adapter.parsePnpmLock, expected, expectedRelationships)
   313  }
   314  
   315  func TestParsePnpmLockV9WithDependencies(t *testing.T) {
   316  	adapter := newGenericPnpmLockAdapter(CatalogerConfig{})
   317  	fixture := "test-fixtures/pnpm-v9-snapshots/pnpm-lock.yaml"
   318  	locationSet := file.NewLocationSet(file.NewLocation(fixture))
   319  	expectedPkgs := []pkg.Package{
   320  		{
   321  			Name:      "cross-spawn",
   322  			Version:   "7.0.6",
   323  			PURL:      "pkg:npm/cross-spawn@7.0.6",
   324  			Locations: locationSet,
   325  			Language:  pkg.JavaScript,
   326  			Type:      pkg.NpmPkg,
   327  			Metadata: pkg.PnpmLockEntry{
   328  				Resolution: pkg.PnpmLockResolution{Integrity: "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="},
   329  				Dependencies: map[string]string{
   330  					"path-key":        "3.1.1",
   331  					"shebang-command": "2.0.0",
   332  					"which":           "2.0.2",
   333  				},
   334  			},
   335  		},
   336  		{
   337  			Name:      "isexe",
   338  			Version:   "2.0.0",
   339  			PURL:      "pkg:npm/isexe@2.0.0",
   340  			Locations: locationSet,
   341  			Language:  pkg.JavaScript,
   342  			Type:      pkg.NpmPkg,
   343  			Metadata: pkg.PnpmLockEntry{Resolution: pkg.PnpmLockResolution{Integrity: "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="},
   344  				Dependencies: map[string]string{},
   345  			},
   346  		},
   347  		{
   348  			Name:      "path-key",
   349  			Version:   "3.1.1",
   350  			PURL:      "pkg:npm/path-key@3.1.1",
   351  			Locations: locationSet,
   352  			Language:  pkg.JavaScript,
   353  			Type:      pkg.NpmPkg,
   354  			Metadata: pkg.PnpmLockEntry{Resolution: pkg.PnpmLockResolution{Integrity: "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="},
   355  				Dependencies: map[string]string{},
   356  			},
   357  		},
   358  		{
   359  			Name:      "shebang-command",
   360  			Version:   "2.0.0",
   361  			PURL:      "pkg:npm/shebang-command@2.0.0",
   362  			Locations: locationSet,
   363  			Language:  pkg.JavaScript,
   364  			Type:      pkg.NpmPkg,
   365  			Metadata: pkg.PnpmLockEntry{Resolution: pkg.PnpmLockResolution{Integrity: "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA=="},
   366  				Dependencies: map[string]string{
   367  					"shebang-regex": "3.0.0",
   368  				},
   369  			},
   370  		},
   371  		{
   372  			Name:      "shebang-regex",
   373  			Version:   "3.0.0",
   374  			PURL:      "pkg:npm/shebang-regex@3.0.0",
   375  			Locations: locationSet,
   376  			Language:  pkg.JavaScript,
   377  			Type:      pkg.NpmPkg,
   378  			Metadata: pkg.PnpmLockEntry{Resolution: pkg.PnpmLockResolution{Integrity: "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="},
   379  				Dependencies: map[string]string{},
   380  			},
   381  		},
   382  		{
   383  			Name:      "which",
   384  			Version:   "2.0.2",
   385  			PURL:      "pkg:npm/which@2.0.2",
   386  			Locations: locationSet,
   387  			Language:  pkg.JavaScript,
   388  			Type:      pkg.NpmPkg,
   389  			Metadata: pkg.PnpmLockEntry{Resolution: pkg.PnpmLockResolution{Integrity: "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="},
   390  				Dependencies: map[string]string{
   391  					"isexe": "2.0.0",
   392  				},
   393  			},
   394  		},
   395  	}
   396  	expectedRelationships := []artifact.Relationship{
   397  		{
   398  			From: expectedPkgs[1],
   399  			To:   expectedPkgs[5],
   400  			Type: artifact.DependencyOfRelationship,
   401  		},
   402  		{
   403  			From: expectedPkgs[2],
   404  			To:   expectedPkgs[0],
   405  			Type: artifact.DependencyOfRelationship,
   406  		},
   407  		{
   408  			From: expectedPkgs[3],
   409  			To:   expectedPkgs[0],
   410  			Type: artifact.DependencyOfRelationship,
   411  		},
   412  		{
   413  			From: expectedPkgs[4],
   414  			To:   expectedPkgs[3],
   415  			Type: artifact.DependencyOfRelationship,
   416  		},
   417  		{
   418  			From: expectedPkgs[5],
   419  			To:   expectedPkgs[0],
   420  			Type: artifact.DependencyOfRelationship,
   421  		},
   422  	}
   423  	pkgtest.TestFileParser(t, fixture, adapter.parsePnpmLock, expectedPkgs, expectedRelationships)
   424  }
   425  
   426  func TestSearchPnpmForLicenses(t *testing.T) {
   427  	ctx := context.TODO()
   428  	fixture := "test-fixtures/pnpm-remote/pnpm-lock.yaml"
   429  	locations := file.NewLocationSet(file.NewLocation(fixture))
   430  	mux, url, teardown := setupNpmRegistry()
   431  	defer teardown()
   432  	tests := []struct {
   433  		name             string
   434  		fixture          string
   435  		config           CatalogerConfig
   436  		requestHandlers  []handlerPath
   437  		expectedPackages []pkg.Package
   438  	}{
   439  		{
   440  			name:   "search remote licenses returns the expected licenses when search is set to true",
   441  			config: CatalogerConfig{SearchRemoteLicenses: true},
   442  			requestHandlers: []handlerPath{
   443  				{
   444  					// https://registry.npmjs.org/nanoid/3.3.4
   445  					path:    "/nanoid/3.3.4",
   446  					handler: generateMockNpmRegistryHandler("test-fixtures/pnpm-remote/registry_response.json"),
   447  				},
   448  			},
   449  			expectedPackages: []pkg.Package{
   450  				{
   451  					Name:      "nanoid",
   452  					Version:   "3.3.4",
   453  					Locations: locations,
   454  					PURL:      "pkg:npm/nanoid@3.3.4",
   455  					Licenses:  pkg.NewLicenseSet(pkg.NewLicenseWithContext(ctx, "MIT")),
   456  					Language:  pkg.JavaScript,
   457  					Type:      pkg.NpmPkg,
   458  					Metadata: pkg.PnpmLockEntry{
   459  						Resolution:   pkg.PnpmLockResolution{Integrity: "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw=="},
   460  						Dependencies: map[string]string{},
   461  					},
   462  				},
   463  			},
   464  		},
   465  	}
   466  
   467  	for _, tc := range tests {
   468  		t.Run(tc.name, func(t *testing.T) {
   469  			// set up the mock server
   470  			for _, handler := range tc.requestHandlers {
   471  				mux.HandleFunc(handler.path, handler.handler)
   472  			}
   473  			tc.config.NPMBaseURL = url
   474  			adapter := newGenericPnpmLockAdapter(tc.config)
   475  			pkgtest.TestFileParser(t, fixture, adapter.parsePnpmLock, tc.expectedPackages, nil)
   476  		})
   477  	}
   478  }
   479  func Test_corruptPnpmLock(t *testing.T) {
   480  	adapter := newGenericPnpmLockAdapter(CatalogerConfig{})
   481  	pkgtest.NewCatalogTester().
   482  		FromFile(t, "test-fixtures/corrupt/pnpm-lock.yaml").
   483  		WithError().
   484  		TestParser(t, adapter.parsePnpmLock)
   485  }
   486  
   487  func generateMockNpmRegistryHandler(responseFixture string) func(w http.ResponseWriter, r *http.Request) {
   488  	return func(w http.ResponseWriter, r *http.Request) {
   489  		w.WriteHeader(http.StatusOK)
   490  		// Copy the file's content to the response writer
   491  		file, err := os.Open(responseFixture)
   492  		if err != nil {
   493  			http.Error(w, err.Error(), http.StatusInternalServerError)
   494  			return
   495  		}
   496  		defer file.Close()
   497  
   498  		_, err = io.Copy(w, file)
   499  		if err != nil {
   500  			http.Error(w, err.Error(), http.StatusInternalServerError)
   501  			return
   502  		}
   503  	}
   504  }
   505  
   506  // setup sets up a test HTTP server for mocking requests to a particular registry.
   507  // The returned url is injected into the Config so the client uses the test server.
   508  // Tests should register handlers on mux to simulate the expected request/response structure
   509  func setupNpmRegistry() (mux *http.ServeMux, serverURL string, teardown func()) {
   510  	// mux is the HTTP request multiplexer used with the test server.
   511  	mux = http.NewServeMux()
   512  
   513  	// We want to ensure that tests catch mistakes where the endpoint URL is
   514  	// specified as absolute rather than relative. It only makes a difference
   515  	// when there's a non-empty base URL path. So, use that. See issue #752.
   516  	apiHandler := http.NewServeMux()
   517  	apiHandler.Handle("/", mux)
   518  	// server is a test HTTP server used to provide mock API responses.
   519  	server := httptest.NewServer(apiHandler)
   520  
   521  	return mux, server.URL, server.Close
   522  }