github.com/google/osv-scalibr@v0.4.1/extractor/filesystem/language/python/pylock/pylock_test.go (about)

     1  // Copyright 2025 Google LLC
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //      http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package pylock_test
    16  
    17  import (
    18  	"testing"
    19  
    20  	"github.com/google/go-cmp/cmp"
    21  	"github.com/google/go-cmp/cmp/cmpopts"
    22  	"github.com/google/osv-scalibr/extractor"
    23  	"github.com/google/osv-scalibr/extractor/filesystem/language/python/pylock"
    24  	"github.com/google/osv-scalibr/extractor/filesystem/simplefileapi"
    25  	"github.com/google/osv-scalibr/inventory"
    26  	"github.com/google/osv-scalibr/purl"
    27  	"github.com/google/osv-scalibr/testing/extracttest"
    28  )
    29  
    30  func TestExtractor_FileRequired(t *testing.T) {
    31  	tests := []struct {
    32  		name      string
    33  		inputPath string
    34  		want      bool
    35  	}{
    36  		{
    37  			name:      "",
    38  			inputPath: "",
    39  			want:      false,
    40  		},
    41  		{
    42  			name:      "",
    43  			inputPath: "mypylock.toml",
    44  			want:      false,
    45  		},
    46  		{
    47  			name:      "",
    48  			inputPath: "pylock.toml",
    49  			want:      true,
    50  		},
    51  		{
    52  			name:      "",
    53  			inputPath: "pylock.spam.toml",
    54  			want:      true,
    55  		},
    56  		{
    57  			name:      "",
    58  			inputPath: "pylock.beans.toml",
    59  			want:      true,
    60  		},
    61  		{
    62  			name:      "",
    63  			inputPath: "PYLOCK.spam.toml",
    64  			want:      false,
    65  		},
    66  		{
    67  			name:      "",
    68  			inputPath: "path/to/my/pylock.toml",
    69  			want:      true,
    70  		},
    71  		{
    72  			name:      "",
    73  			inputPath: "path/to/my/pylock.spam.toml",
    74  			want:      true,
    75  		},
    76  		{
    77  			name:      "",
    78  			inputPath: "path/to/my/pylock.toml/file",
    79  			want:      false,
    80  		},
    81  		{
    82  			name:      "",
    83  			inputPath: "path/to/my/pylock.toml.file",
    84  			want:      false,
    85  		},
    86  		{
    87  			name:      "",
    88  			inputPath: "path.to.my.pylock.toml",
    89  			want:      false,
    90  		},
    91  	}
    92  	for _, tt := range tests {
    93  		t.Run(tt.name, func(t *testing.T) {
    94  			e := pylock.Extractor{}
    95  			got := e.FileRequired(simplefileapi.New(tt.inputPath, nil))
    96  			if got != tt.want {
    97  				t.Errorf("FileRequired(%q, FileInfo) got = %v, want %v", tt.inputPath, got, tt.want)
    98  			}
    99  		})
   100  	}
   101  }
   102  
   103  func TestExtractor_Extract(t *testing.T) {
   104  	tests := []extracttest.TestTableEntry{
   105  		{
   106  			Name: "invalid toml",
   107  			InputConfig: extracttest.ScanInputMockConfig{
   108  				Path: "testdata/not-toml.txt",
   109  			},
   110  			WantErr:      extracttest.ContainsErrStr{Str: "could not extract"},
   111  			WantPackages: nil,
   112  		},
   113  		{
   114  			Name: "example",
   115  			InputConfig: extracttest.ScanInputMockConfig{
   116  				Path: "testdata/example.toml",
   117  			},
   118  			WantPackages: []*extractor.Package{
   119  				{
   120  					Name:      "attrs",
   121  					Version:   "25.1.0",
   122  					PURLType:  purl.TypePyPi,
   123  					Locations: []string{"testdata/example.toml"},
   124  				},
   125  				{
   126  					Name:      "cattrs",
   127  					Version:   "24.1.2",
   128  					PURLType:  purl.TypePyPi,
   129  					Locations: []string{"testdata/example.toml"},
   130  				},
   131  				{
   132  					Name:      "numpy",
   133  					Version:   "2.2.3",
   134  					PURLType:  purl.TypePyPi,
   135  					Locations: []string{"testdata/example.toml"},
   136  				},
   137  			},
   138  		},
   139  		{
   140  			Name: "package_with_commits",
   141  			InputConfig: extracttest.ScanInputMockConfig{
   142  				Path: "testdata/commits.toml",
   143  			},
   144  			WantPackages: []*extractor.Package{
   145  				{
   146  					Name:      "click",
   147  					Version:   "8.2.1",
   148  					PURLType:  purl.TypePyPi,
   149  					Locations: []string{"testdata/commits.toml"},
   150  				},
   151  				{
   152  					Name:      "mleroc",
   153  					Version:   "0.1.0",
   154  					PURLType:  purl.TypePyPi,
   155  					Locations: []string{"testdata/commits.toml"},
   156  					SourceCode: &extractor.SourceCodeIdentifier{
   157  						Commit: "735093f03c4d8be70bfaaae44074ac92d7419b6d",
   158  					},
   159  				},
   160  				{
   161  					Name:      "packaging",
   162  					Version:   "24.2",
   163  					PURLType:  purl.TypePyPi,
   164  					Locations: []string{"testdata/commits.toml"},
   165  				},
   166  				{
   167  					Name:      "pathspec",
   168  					Version:   "0.12.1",
   169  					PURLType:  purl.TypePyPi,
   170  					Locations: []string{"testdata/commits.toml"},
   171  				},
   172  				{
   173  					Name:      "python-dateutil",
   174  					Version:   "2.9.0.post0",
   175  					PURLType:  purl.TypePyPi,
   176  					Locations: []string{"testdata/commits.toml"},
   177  				},
   178  				{
   179  					Name:      "scikit-learn",
   180  					Version:   "1.6.1",
   181  					PURLType:  purl.TypePyPi,
   182  					Locations: []string{"testdata/commits.toml"},
   183  				},
   184  				{
   185  					Name:      "tqdm",
   186  					Version:   "4.67.1",
   187  					PURLType:  purl.TypePyPi,
   188  					Locations: []string{"testdata/commits.toml"},
   189  				},
   190  			},
   191  		},
   192  		{
   193  			Name: "created_by_pip_with_just_self",
   194  			InputConfig: extracttest.ScanInputMockConfig{
   195  				Path: "testdata/pip-just-self.toml",
   196  			},
   197  			WantPackages: []*extractor.Package{},
   198  		},
   199  		{
   200  			Name: "created_by_pip",
   201  			InputConfig: extracttest.ScanInputMockConfig{
   202  				Path: "testdata/pip-full.toml",
   203  			},
   204  			WantPackages: []*extractor.Package{
   205  				{
   206  					Name:      "annotated-types",
   207  					Version:   "0.7.0",
   208  					PURLType:  purl.TypePyPi,
   209  					Locations: []string{"testdata/pip-full.toml"},
   210  				},
   211  				{
   212  					Name:      "packaging",
   213  					Version:   "25.0",
   214  					PURLType:  purl.TypePyPi,
   215  					Locations: []string{"testdata/pip-full.toml"},
   216  				},
   217  				{
   218  					Name:      "pyproject-toml",
   219  					Version:   "0.1.0",
   220  					PURLType:  purl.TypePyPi,
   221  					Locations: []string{"testdata/pip-full.toml"},
   222  				},
   223  				{
   224  					Name:      "setuptools",
   225  					Version:   "80.9.0",
   226  					PURLType:  purl.TypePyPi,
   227  					Locations: []string{"testdata/pip-full.toml"},
   228  				},
   229  				{
   230  					Name:      "wheel",
   231  					Version:   "0.45.1",
   232  					PURLType:  purl.TypePyPi,
   233  					Locations: []string{"testdata/pip-full.toml"},
   234  				},
   235  			},
   236  		},
   237  		{
   238  			Name: "created_by_pdm",
   239  			InputConfig: extracttest.ScanInputMockConfig{
   240  				Path: "testdata/pdm-full.toml",
   241  			},
   242  			WantPackages: []*extractor.Package{
   243  				{
   244  					Name:      "certifi",
   245  					Version:   "2025.1.31",
   246  					PURLType:  purl.TypePyPi,
   247  					Locations: []string{"testdata/pdm-full.toml"},
   248  				},
   249  				{
   250  					Name:      "chardet",
   251  					Version:   "3.0.4",
   252  					PURLType:  purl.TypePyPi,
   253  					Locations: []string{"testdata/pdm-full.toml"},
   254  				},
   255  				{
   256  					Name:      "charset-normalizer",
   257  					Version:   "2.0.12",
   258  					PURLType:  purl.TypePyPi,
   259  					Locations: []string{"testdata/pdm-full.toml"},
   260  				},
   261  				{
   262  					Name:      "colorama",
   263  					Version:   "0.3.9",
   264  					PURLType:  purl.TypePyPi,
   265  					Locations: []string{"testdata/pdm-full.toml"},
   266  				},
   267  				{
   268  					Name:      "idna",
   269  					Version:   "2.7",
   270  					PURLType:  purl.TypePyPi,
   271  					Locations: []string{"testdata/pdm-full.toml"},
   272  				},
   273  				{
   274  					Name:      "py",
   275  					Version:   "1.4.34",
   276  					PURLType:  purl.TypePyPi,
   277  					Locations: []string{"testdata/pdm-full.toml"},
   278  				},
   279  				{
   280  					Name:      "pytest",
   281  					Version:   "3.2.5",
   282  					PURLType:  purl.TypePyPi,
   283  					Locations: []string{"testdata/pdm-full.toml"},
   284  				},
   285  				{
   286  					Name:      "requests",
   287  					Version:   "2.27.1",
   288  					PURLType:  purl.TypePyPi,
   289  					Locations: []string{"testdata/pdm-full.toml"},
   290  				},
   291  				{
   292  					Name:      "setuptools",
   293  					Version:   "39.2.0",
   294  					PURLType:  purl.TypePyPi,
   295  					Locations: []string{"testdata/pdm-full.toml"},
   296  				},
   297  				{
   298  					Name:      "urllib3",
   299  					Version:   "1.26.20",
   300  					PURLType:  purl.TypePyPi,
   301  					Locations: []string{"testdata/pdm-full.toml"},
   302  				},
   303  			},
   304  		},
   305  	}
   306  
   307  	for _, tt := range tests {
   308  		t.Run(tt.Name, func(t *testing.T) {
   309  			extr := pylock.Extractor{}
   310  
   311  			scanInput := extracttest.GenerateScanInputMock(t, tt.InputConfig)
   312  			defer extracttest.CloseTestScanInput(t, scanInput)
   313  
   314  			got, err := extr.Extract(t.Context(), &scanInput)
   315  
   316  			if diff := cmp.Diff(tt.WantErr, err, cmpopts.EquateErrors()); diff != "" {
   317  				t.Errorf("%s.Extract(%q) error diff (-want +got):\n%s", extr.Name(), tt.InputConfig.Path, diff)
   318  				return
   319  			}
   320  
   321  			wantInv := inventory.Inventory{Packages: tt.WantPackages}
   322  			if diff := cmp.Diff(wantInv, got, cmpopts.SortSlices(extracttest.PackageCmpLess)); diff != "" {
   323  				t.Errorf("%s.Extract(%q) diff (-want +got):\n%s", extr.Name(), tt.InputConfig.Path, diff)
   324  			}
   325  		})
   326  	}
   327  }