github.com/nextlinux/gosbom@v0.81.1-0.20230627115839-1ff50c281391/gosbom/pkg/cataloger/python/cataloger_test.go (about)

     1  package python
     2  
     3  import (
     4  	"testing"
     5  
     6  	"github.com/nextlinux/gosbom/gosbom/file"
     7  	"github.com/nextlinux/gosbom/gosbom/pkg"
     8  	"github.com/nextlinux/gosbom/gosbom/pkg/cataloger/internal/pkgtest"
     9  	"github.com/stretchr/testify/require"
    10  )
    11  
    12  func Test_PackageCataloger(t *testing.T) {
    13  	tests := []struct {
    14  		name            string
    15  		fixtures        []string
    16  		expectedPackage pkg.Package
    17  	}{
    18  		{
    19  			name:     "egg-file-no-version",
    20  			fixtures: []string{"test-fixtures/no-version-py3.8.egg-info"},
    21  			expectedPackage: pkg.Package{
    22  				Name:         "no-version",
    23  				PURL:         "pkg:pypi/no-version",
    24  				Type:         pkg.PythonPkg,
    25  				Language:     pkg.Python,
    26  				FoundBy:      "python-package-cataloger",
    27  				MetadataType: pkg.PythonPackageMetadataType,
    28  				Metadata: pkg.PythonPackageMetadata{
    29  					Name:                 "no-version",
    30  					SitePackagesRootPath: "test-fixtures",
    31  				},
    32  			},
    33  		},
    34  		{
    35  			name: "egg-info directory",
    36  			fixtures: []string{
    37  				"test-fixtures/egg-info/PKG-INFO",
    38  				"test-fixtures/egg-info/RECORD",
    39  				"test-fixtures/egg-info/top_level.txt",
    40  			},
    41  			expectedPackage: pkg.Package{
    42  				Name:     "requests",
    43  				Version:  "2.22.0",
    44  				PURL:     "pkg:pypi/requests@2.22.0",
    45  				Type:     pkg.PythonPkg,
    46  				Language: pkg.Python,
    47  				Licenses: pkg.NewLicenseSet(
    48  					pkg.NewLicenseFromLocations("Apache 2.0", file.NewLocation("test-fixtures/egg-info/PKG-INFO")),
    49  				),
    50  				FoundBy:      "python-package-cataloger",
    51  				MetadataType: pkg.PythonPackageMetadataType,
    52  				Metadata: pkg.PythonPackageMetadata{
    53  					Name:                 "requests",
    54  					Version:              "2.22.0",
    55  					Platform:             "UNKNOWN",
    56  					Author:               "Kenneth Reitz",
    57  					AuthorEmail:          "me@kennethreitz.org",
    58  					SitePackagesRootPath: "test-fixtures",
    59  					Files: []pkg.PythonFileRecord{
    60  						{Path: "requests-2.22.0.dist-info/INSTALLER", Digest: &pkg.PythonFileDigest{"sha256", "zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg"}, Size: "4"},
    61  						{Path: "requests/__init__.py", Digest: &pkg.PythonFileDigest{"sha256", "PnKCgjcTq44LaAMzB-7--B2FdewRrE8F_vjZeaG9NhA"}, Size: "3921"},
    62  						{Path: "requests/__pycache__/__version__.cpython-38.pyc"},
    63  						{Path: "requests/__pycache__/utils.cpython-38.pyc"},
    64  						{Path: "requests/__version__.py", Digest: &pkg.PythonFileDigest{"sha256", "Bm-GFstQaFezsFlnmEMrJDe8JNROz9n2XXYtODdvjjc"}, Size: "436"},
    65  						{Path: "requests/utils.py", Digest: &pkg.PythonFileDigest{"sha256", "LtPJ1db6mJff2TJSJWKi7rBpzjPS3mSOrjC9zRhoD3A"}, Size: "30049"},
    66  					},
    67  					TopLevelPackages: []string{"requests"},
    68  				},
    69  			},
    70  		},
    71  		{
    72  			name: "dist-info directory",
    73  			fixtures: []string{
    74  				"test-fixtures/dist-info/METADATA",
    75  				"test-fixtures/dist-info/RECORD",
    76  				"test-fixtures/dist-info/top_level.txt",
    77  				"test-fixtures/dist-info/direct_url.json",
    78  			},
    79  			expectedPackage: pkg.Package{
    80  				Name:     "Pygments",
    81  				Version:  "2.6.1",
    82  				PURL:     "pkg:pypi/Pygments@2.6.1?vcs_url=git+https://github.com/python-test/test.git%40aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
    83  				Type:     pkg.PythonPkg,
    84  				Language: pkg.Python,
    85  				Licenses: pkg.NewLicenseSet(
    86  					pkg.NewLicenseFromLocations("BSD License", file.NewLocation("test-fixtures/dist-info/METADATA")),
    87  				),
    88  				FoundBy:      "python-package-cataloger",
    89  				MetadataType: pkg.PythonPackageMetadataType,
    90  				Metadata: pkg.PythonPackageMetadata{
    91  					Name:                 "Pygments",
    92  					Version:              "2.6.1",
    93  					Platform:             "any",
    94  					Author:               "Georg Brandl",
    95  					AuthorEmail:          "georg@python.org",
    96  					SitePackagesRootPath: "test-fixtures",
    97  					Files: []pkg.PythonFileRecord{
    98  						{Path: "../../../bin/pygmentize", Digest: &pkg.PythonFileDigest{"sha256", "dDhv_U2jiCpmFQwIRHpFRLAHUO4R1jIJPEvT_QYTFp8"}, Size: "220"},
    99  						{Path: "Pygments-2.6.1.dist-info/AUTHORS", Digest: &pkg.PythonFileDigest{"sha256", "PVpa2_Oku6BGuiUvutvuPnWGpzxqFy2I8-NIrqCvqUY"}, Size: "8449"},
   100  						{Path: "Pygments-2.6.1.dist-info/RECORD"},
   101  						{Path: "pygments/__pycache__/__init__.cpython-38.pyc"},
   102  						{Path: "pygments/util.py", Digest: &pkg.PythonFileDigest{"sha256", "586xXHiJGGZxqk5PMBu3vBhE68DLuAe5MBARWrSPGxA"}, Size: "10778"},
   103  
   104  						{Path: "pygments/x_util.py", Digest: &pkg.PythonFileDigest{"sha256", "qpzzsOW31KT955agi-7NS--90I0iNiJCyLJQnRCHgKI="}, Size: "10778"},
   105  					},
   106  					TopLevelPackages: []string{"pygments", "something_else"},
   107  					DirectURLOrigin:  &pkg.PythonDirectURLOriginInfo{URL: "https://github.com/python-test/test.git", VCS: "git", CommitID: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"},
   108  				},
   109  			},
   110  		},
   111  		{
   112  			name: "malformed-record",
   113  			fixtures: []string{
   114  				"test-fixtures/malformed-record/dist-info/METADATA",
   115  				"test-fixtures/malformed-record/dist-info/RECORD",
   116  			},
   117  			expectedPackage: pkg.Package{
   118  				Name:     "Pygments",
   119  				Version:  "2.6.1",
   120  				PURL:     "pkg:pypi/Pygments@2.6.1",
   121  				Type:     pkg.PythonPkg,
   122  				Language: pkg.Python,
   123  				Licenses: pkg.NewLicenseSet(
   124  					pkg.NewLicenseFromLocations("BSD License", file.NewLocation("test-fixtures/malformed-record/dist-info/METADATA")),
   125  				),
   126  				FoundBy:      "python-package-cataloger",
   127  				MetadataType: pkg.PythonPackageMetadataType,
   128  				Metadata: pkg.PythonPackageMetadata{
   129  					Name:                 "Pygments",
   130  					Version:              "2.6.1",
   131  					Platform:             "any",
   132  					Author:               "Georg Brandl",
   133  					AuthorEmail:          "georg@python.org",
   134  					SitePackagesRootPath: "test-fixtures/malformed-record",
   135  					Files: []pkg.PythonFileRecord{
   136  						{Path: "flask/json/tag.py", Digest: &pkg.PythonFileDigest{"sha256", "9ehzrmt5k7hxf7ZEK0NOs3swvQyU9fWNe-pnYe69N60"}, Size: "8223"},
   137  						{Path: "../../Scripts/flask.exe", Digest: &pkg.PythonFileDigest{"sha256", "mPrbVeZCDX20himZ_bRai1nCs_tgr7jHIOGZlcgn-T4"}, Size: "93063"},
   138  						{Path: "../../Scripts/flask.exe", Size: "89470", Digest: &pkg.PythonFileDigest{"sha256", "jvqh4N3qOqXLlq40i6ZOLCY9tAOwfwdzIpLDYhRjoqQ"}},
   139  						{Path: "Flask-1.0.2.dist-info/INSTALLER", Size: "4", Digest: &pkg.PythonFileDigest{"sha256", "zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg"}},
   140  					},
   141  				},
   142  			},
   143  		},
   144  		{
   145  			// in cases where the metadata file is available and the record is not we should still record there is a package
   146  			// additionally empty top_level.txt files should not result in an error
   147  			name:     "partial dist-info directory",
   148  			fixtures: []string{"test-fixtures/partial.dist-info/METADATA"},
   149  			expectedPackage: pkg.Package{
   150  				Name:     "Pygments",
   151  				Version:  "2.6.1",
   152  				PURL:     "pkg:pypi/Pygments@2.6.1",
   153  				Type:     pkg.PythonPkg,
   154  				Language: pkg.Python,
   155  				Licenses: pkg.NewLicenseSet(
   156  					pkg.NewLicenseFromLocations("BSD License", file.NewLocation("test-fixtures/partial.dist-info/METADATA")),
   157  				),
   158  				FoundBy:      "python-package-cataloger",
   159  				MetadataType: pkg.PythonPackageMetadataType,
   160  				Metadata: pkg.PythonPackageMetadata{
   161  					Name:                 "Pygments",
   162  					Version:              "2.6.1",
   163  					Platform:             "any",
   164  					Author:               "Georg Brandl",
   165  					AuthorEmail:          "georg@python.org",
   166  					SitePackagesRootPath: "test-fixtures",
   167  				},
   168  			},
   169  		},
   170  		{
   171  			name:     "egg-info regular file",
   172  			fixtures: []string{"test-fixtures/test.egg-info"},
   173  			expectedPackage: pkg.Package{
   174  				Name:     "requests",
   175  				Version:  "2.22.0",
   176  				PURL:     "pkg:pypi/requests@2.22.0",
   177  				Type:     pkg.PythonPkg,
   178  				Language: pkg.Python,
   179  				Licenses: pkg.NewLicenseSet(
   180  					pkg.NewLicenseFromLocations("Apache 2.0", file.NewLocation("test-fixtures/test.egg-info")),
   181  				),
   182  				FoundBy:      "python-package-cataloger",
   183  				MetadataType: pkg.PythonPackageMetadataType,
   184  				Metadata: pkg.PythonPackageMetadata{
   185  					Name:                 "requests",
   186  					Version:              "2.22.0",
   187  					Platform:             "UNKNOWN",
   188  					Author:               "Kenneth Reitz",
   189  					AuthorEmail:          "me@kennethreitz.org",
   190  					SitePackagesRootPath: "test-fixtures",
   191  				},
   192  			},
   193  		},
   194  	}
   195  
   196  	for _, test := range tests {
   197  		t.Run(test.name, func(t *testing.T) {
   198  			resolver := file.NewMockResolverForPaths(test.fixtures...)
   199  
   200  			locations, err := resolver.FilesByPath(test.fixtures...)
   201  			require.NoError(t, err)
   202  
   203  			test.expectedPackage.Locations = file.NewLocationSet(locations...)
   204  
   205  			pkgtest.NewCatalogTester().
   206  				WithResolver(resolver).
   207  				Expects([]pkg.Package{test.expectedPackage}, nil).
   208  				TestCataloger(t, NewPythonPackageCataloger())
   209  		})
   210  	}
   211  }
   212  
   213  func Test_PackageCataloger_IgnorePackage(t *testing.T) {
   214  	tests := []struct {
   215  		MetadataFixture string
   216  	}{
   217  		{
   218  			MetadataFixture: "test-fixtures/Python-2.7.egg-info",
   219  		},
   220  		{
   221  			MetadataFixture: "test-fixtures/empty-1.0.0-py3.8.egg-info",
   222  		},
   223  	}
   224  
   225  	for _, test := range tests {
   226  		t.Run(test.MetadataFixture, func(t *testing.T) {
   227  			resolver := file.NewMockResolverForPaths(test.MetadataFixture)
   228  
   229  			actual, _, err := NewPythonPackageCataloger().Catalog(resolver)
   230  			require.NoError(t, err)
   231  
   232  			if len(actual) != 0 {
   233  				t.Fatalf("Expected 0 packages but found: %d", len(actual))
   234  			}
   235  		})
   236  	}
   237  }
   238  
   239  func Test_IndexCataloger_Globs(t *testing.T) {
   240  	tests := []struct {
   241  		name     string
   242  		fixture  string
   243  		expected []string
   244  	}{
   245  		{
   246  			name:    "obtain index files",
   247  			fixture: "test-fixtures/glob-paths",
   248  			expected: []string{
   249  				"src/requirements.txt",
   250  				"src/extra-requirements.txt",
   251  				"src/requirements-dev.txt",
   252  				"src/1-requirements-dev.txt",
   253  				"src/setup.py",
   254  				"src/poetry.lock",
   255  				"src/Pipfile.lock",
   256  			},
   257  		},
   258  	}
   259  
   260  	for _, test := range tests {
   261  		t.Run(test.name, func(t *testing.T) {
   262  			pkgtest.NewCatalogTester().
   263  				FromDirectory(t, test.fixture).
   264  				ExpectsResolverContentQueries(test.expected).
   265  				TestCataloger(t, NewPythonIndexCataloger())
   266  		})
   267  	}
   268  }
   269  
   270  func Test_PackageCataloger_Globs(t *testing.T) {
   271  	tests := []struct {
   272  		name     string
   273  		fixture  string
   274  		expected []string
   275  	}{
   276  		{
   277  			name:    "obtain index files",
   278  			fixture: "test-fixtures/glob-paths",
   279  			expected: []string{
   280  				"site-packages/x.dist-info/METADATA",
   281  				"site-packages/y.egg-info/PKG-INFO",
   282  				"site-packages/z.egg-info",
   283  			},
   284  		},
   285  	}
   286  
   287  	for _, test := range tests {
   288  		t.Run(test.name, func(t *testing.T) {
   289  			pkgtest.NewCatalogTester().
   290  				FromDirectory(t, test.fixture).
   291  				ExpectsResolverContentQueries(test.expected).
   292  				TestCataloger(t, NewPythonPackageCataloger())
   293  		})
   294  	}
   295  }