github.com/anchore/syft@v1.38.2/syft/pkg/cataloger/python/parse_poetry_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 TestParsePoetryLock(t *testing.T) { 14 fixture := "test-fixtures/poetry/dev-deps/poetry.lock" 15 locations := file.NewLocationSet(file.NewLocation(fixture)) 16 expectedPkgs := []pkg.Package{ 17 { 18 Name: "added-value", 19 Version: "0.14.2", 20 PURL: "pkg:pypi/added-value@0.14.2", 21 Locations: locations, 22 Language: pkg.Python, 23 Type: pkg.PythonPkg, 24 Metadata: pkg.PythonPoetryLockEntry{ 25 Index: "https://test.pypi.org/simple", 26 Dependencies: []pkg.PythonPoetryLockDependencyEntry{ 27 {Name: "docutils", Version: "*"}, 28 {Name: "msal", Version: ">=0.4.1,<2.0.0"}, 29 {Name: "natsort", Version: "*"}, 30 {Name: "packaging", Version: "*"}, 31 {Name: "portalocker", Version: ">=1.0,<3", Markers: `platform_system != "Windows"`}, 32 {Name: "portalocker", Version: ">=1.6,<3", Markers: `platform_system == "Windows"`}, 33 {Name: "six", Version: "*"}, 34 {Name: "sphinx", Version: "*"}, 35 }, 36 Extras: []pkg.PythonPoetryLockExtraEntry{ 37 { 38 Name: "deploy", 39 Dependencies: []string{"bumpversion", "twine", "wheel"}, 40 }, 41 { 42 Name: "docs", 43 Dependencies: []string{"sphinx", "sphinx-rtd-theme"}, 44 }, 45 { 46 Name: "test", 47 Dependencies: []string{"pytest", "pytest-cov", "coveralls", "beautifulsoup4", "hypothesis"}, 48 }, 49 }, 50 }, 51 }, 52 { 53 Name: "alabaster", 54 Version: "0.7.12", 55 PURL: "pkg:pypi/alabaster@0.7.12", 56 Locations: locations, 57 Language: pkg.Python, 58 Type: pkg.PythonPkg, 59 Metadata: pkg.PythonPoetryLockEntry{Index: "https://pypi.org/simple"}, 60 }, 61 { 62 Name: "appnope", 63 Version: "0.1.0", 64 PURL: "pkg:pypi/appnope@0.1.0", 65 Locations: locations, 66 Language: pkg.Python, 67 Type: pkg.PythonPkg, 68 Metadata: pkg.PythonPoetryLockEntry{Index: "https://pypi.org/simple"}, 69 }, 70 { 71 Name: "asciitree", 72 Version: "0.3.3", 73 PURL: "pkg:pypi/asciitree@0.3.3", 74 Locations: locations, 75 Language: pkg.Python, 76 Type: pkg.PythonPkg, 77 Metadata: pkg.PythonPoetryLockEntry{Index: "https://pypi.org/simple"}, 78 }, 79 } 80 81 var expectedRelationships []artifact.Relationship 82 83 poetryLockParser := newPoetryLockParser(DefaultCatalogerConfig()) 84 pkgtest.TestFileParser(t, fixture, poetryLockParser.parsePoetryLock, expectedPkgs, expectedRelationships) 85 } 86 87 func TestParsePoetryLockWithLicenseEnrichment(t *testing.T) { 88 ctx := context.TODO() 89 fixture := "test-fixtures/pypi-remote/poetry.lock" 90 locations := file.NewLocationSet(file.NewLocation(fixture)) 91 mux, url, teardown := setupPypiRegistry() 92 defer teardown() 93 tests := []struct { 94 name string 95 fixture string 96 config CatalogerConfig 97 requestHandlers []handlerPath 98 expectedPackages []pkg.Package 99 }{ 100 { 101 name: "search remote licenses returns the expected licenses when search is set to true", 102 config: CatalogerConfig{SearchRemoteLicenses: true}, 103 requestHandlers: []handlerPath{ 104 { 105 path: "/certifi/2025.10.5/json", 106 handler: generateMockPypiRegistryHandler("test-fixtures/pypi-remote/registry_response.json"), 107 }, 108 }, 109 expectedPackages: []pkg.Package{ 110 { 111 Name: "certifi", 112 Version: "2025.10.5", 113 Locations: locations, 114 PURL: "pkg:pypi/certifi@2025.10.5", 115 Licenses: pkg.NewLicenseSet(pkg.NewLicenseWithContext(ctx, "MPL-2.0")), 116 Language: pkg.Python, 117 Type: pkg.PythonPkg, 118 Metadata: pkg.PythonPoetryLockEntry{ 119 Index: "https://pypi.org/simple", 120 }, 121 }, 122 }, 123 }, 124 } 125 for _, tc := range tests { 126 t.Run(tc.name, func(t *testing.T) { 127 // set up the mock server 128 for _, handler := range tc.requestHandlers { 129 mux.HandleFunc(handler.path, handler.handler) 130 } 131 tc.config.PypiBaseURL = url 132 poetryLockParser := newPoetryLockParser(tc.config) 133 pkgtest.TestFileParser(t, fixture, poetryLockParser.parsePoetryLock, tc.expectedPackages, nil) 134 }) 135 } 136 } 137 func Test_corruptPoetryLock(t *testing.T) { 138 poetryLockParser := newPoetryLockParser(DefaultCatalogerConfig()) 139 pkgtest.NewCatalogTester(). 140 FromFile(t, "test-fixtures/glob-paths/src/poetry.lock"). 141 WithError(). 142 TestParser(t, poetryLockParser.parsePoetryLock) 143 }