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  }