github.com/anchore/syft@v1.38.2/syft/pkg/cataloger/python/parse_pipfile_lock_test.go (about)

     1  package python
     2  
     3  import (
     4  	"context"
     5  	"testing"
     6  
     7  	"github.com/anchore/syft/syft/artifact"
     8  	"github.com/anchore/syft/syft/file"
     9  	"github.com/anchore/syft/syft/pkg"
    10  	"github.com/anchore/syft/syft/pkg/cataloger/internal/pkgtest"
    11  )
    12  
    13  func TestParsePipFileLock(t *testing.T) {
    14  
    15  	fixture := "test-fixtures/pipfile-lock/Pipfile.lock"
    16  	locations := file.NewLocationSet(file.NewLocation(fixture))
    17  	expectedPkgs := []pkg.Package{
    18  		{
    19  			Name:      "aio-pika",
    20  			Version:   "6.8.0",
    21  			PURL:      "pkg:pypi/aio-pika@6.8.0",
    22  			Locations: locations,
    23  			Language:  pkg.Python,
    24  			Type:      pkg.PythonPkg,
    25  			Metadata: pkg.PythonPipfileLockEntry{
    26  				Index: "https://pypi.org/simple",
    27  				Hashes: []string{
    28  					"sha256:1d4305a5f78af3857310b4fe48348cdcf6c097e0e275ea88c2cd08570531a369",
    29  					"sha256:e69afef8695f47c5d107bbdba21bdb845d5c249acb3be53ef5c2d497b02657c0",
    30  				}},
    31  		},
    32  		{
    33  			Name:      "aiodns",
    34  			Version:   "2.0.0",
    35  			PURL:      "pkg:pypi/aiodns@2.0.0",
    36  			Locations: locations,
    37  			Language:  pkg.Python,
    38  			Type:      pkg.PythonPkg,
    39  			Metadata: pkg.PythonPipfileLockEntry{
    40  				Index: "https://test.pypi.org/simple",
    41  				Hashes: []string{
    42  					"sha256:815fdef4607474295d68da46978a54481dd1e7be153c7d60f9e72773cd38d77d",
    43  					"sha256:aaa5ac584f40fe778013df0aa6544bf157799bd3f608364b451840ed2c8688de",
    44  				},
    45  			},
    46  		},
    47  		{
    48  			Name:      "aiohttp",
    49  			Version:   "3.7.4.post0",
    50  			PURL:      "pkg:pypi/aiohttp@3.7.4.post0",
    51  			Locations: locations,
    52  			Language:  pkg.Python,
    53  			Type:      pkg.PythonPkg,
    54  			Metadata: pkg.PythonPipfileLockEntry{
    55  				Index: "https://pypi.org/simple",
    56  				Hashes: []string{
    57  					"sha256:02f46fc0e3c5ac58b80d4d56eb0a7c7d97fcef69ace9326289fb9f1955e65cfe",
    58  					"sha256:0563c1b3826945eecd62186f3f5c7d31abb7391fedc893b7e2b26303b5a9f3fe",
    59  				},
    60  			},
    61  		},
    62  		{
    63  			Name:      "aiohttp-jinja2",
    64  			Version:   "1.4.2",
    65  			PURL:      "pkg:pypi/aiohttp-jinja2@1.4.2",
    66  			Locations: locations,
    67  			Language:  pkg.Python,
    68  			Type:      pkg.PythonPkg,
    69  			Metadata: pkg.PythonPipfileLockEntry{
    70  				Index: "https://pypi.org/simple",
    71  				Hashes: []string{
    72  					"sha256:860da7582efa866744bad5883947557d0f82e457d69903ea65d666b66f8a69ca",
    73  					"sha256:9c22a0e48e3b277fc145c67dd8c3b8f609dab36bce9eb337f70dfe716663c9a0",
    74  				},
    75  			},
    76  		},
    77  	}
    78  
    79  	// TODO: relationships are not under test
    80  	var expectedRelationships []artifact.Relationship
    81  
    82  	pipfileLockParser := newPipfileLockParser(DefaultCatalogerConfig())
    83  	pkgtest.TestFileParser(t, fixture, pipfileLockParser.parsePipfileLock, expectedPkgs, expectedRelationships)
    84  }
    85  
    86  func TestParsePipfileLockWithLicenseEnrichment(t *testing.T) {
    87  	ctx := context.TODO()
    88  	fixture := "test-fixtures/pypi-remote/Pipfile.lock"
    89  	locations := file.NewLocationSet(file.NewLocation(fixture))
    90  	mux, url, teardown := setupPypiRegistry()
    91  	defer teardown()
    92  	tests := []struct {
    93  		name             string
    94  		fixture          string
    95  		config           CatalogerConfig
    96  		requestHandlers  []handlerPath
    97  		expectedPackages []pkg.Package
    98  	}{
    99  		{
   100  			name:   "search remote licenses returns the expected licenses when search is set to true",
   101  			config: CatalogerConfig{SearchRemoteLicenses: true},
   102  			requestHandlers: []handlerPath{
   103  				{
   104  					path:    "/certifi/2025.10.5/json",
   105  					handler: generateMockPypiRegistryHandler("test-fixtures/pypi-remote/registry_response.json"),
   106  				},
   107  			},
   108  			expectedPackages: []pkg.Package{
   109  				{
   110  					Name:      "certifi",
   111  					Version:   "2025.10.5",
   112  					Locations: locations,
   113  					PURL:      "pkg:pypi/certifi@2025.10.5",
   114  					Licenses:  pkg.NewLicenseSet(pkg.NewLicenseWithContext(ctx, "MPL-2.0")),
   115  					Language:  pkg.Python,
   116  					Type:      pkg.PythonPkg,
   117  					Metadata: pkg.PythonPipfileLockEntry{
   118  						Index: "https://pypi.org/simple",
   119  						Hashes: []string{
   120  							"sha256:47c09d31ccf2acf0be3f701ea53595ee7e0b8fa08801c6624be771df09ae7b43",
   121  							"sha256:0f212c2744a9bb6de0c56639a6f68afe01ecd92d91f14ae897c4fe7bbeeef0de",
   122  						},
   123  					},
   124  				},
   125  			},
   126  		},
   127  	}
   128  	for _, tc := range tests {
   129  		t.Run(tc.name, func(t *testing.T) {
   130  			// set up the mock server
   131  			for _, handler := range tc.requestHandlers {
   132  				mux.HandleFunc(handler.path, handler.handler)
   133  			}
   134  			tc.config.PypiBaseURL = url
   135  			pipfileLockParser := newPipfileLockParser(tc.config)
   136  			pkgtest.TestFileParser(t, fixture, pipfileLockParser.parsePipfileLock, tc.expectedPackages, nil)
   137  		})
   138  	}
   139  }
   140  
   141  func Test_corruptPipfileLock(t *testing.T) {
   142  	pipfileLockParser := newPipfileLockParser(DefaultCatalogerConfig())
   143  	pkgtest.NewCatalogTester().
   144  		FromFile(t, "test-fixtures/glob-paths/src/Pipfile.lock").
   145  		WithError().
   146  		TestParser(t, pipfileLockParser.parsePipfileLock)
   147  }