github.com/google/osv-scalibr@v0.4.1/extractor/filesystem/language/python/pdmlock/pdmlock_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 pdmlock_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/pdmlock"
    24  	"github.com/google/osv-scalibr/extractor/filesystem/osv"
    25  	"github.com/google/osv-scalibr/extractor/filesystem/simplefileapi"
    26  	"github.com/google/osv-scalibr/inventory"
    27  	"github.com/google/osv-scalibr/purl"
    28  	"github.com/google/osv-scalibr/testing/extracttest"
    29  )
    30  
    31  func TestPdmExtractor_FileRequired(t *testing.T) {
    32  	tests := []struct {
    33  		name      string
    34  		inputPath string
    35  		want      bool
    36  	}{
    37  		{
    38  			name:      "empty",
    39  			inputPath: "",
    40  			want:      false,
    41  		},
    42  		{
    43  			name:      "plain",
    44  			inputPath: "pdm.lock",
    45  			want:      true,
    46  		},
    47  		{
    48  			name:      "absolute",
    49  			inputPath: "/path/to/pdm.lock",
    50  			want:      true,
    51  		},
    52  		{
    53  			name:      "relative",
    54  			inputPath: "../../pdm.lock",
    55  			want:      true,
    56  		},
    57  		{
    58  			name:      "in-path",
    59  			inputPath: "/path/with/pdm.lock/in/middle",
    60  			want:      false,
    61  		},
    62  		{
    63  			name:      "invalid-suffix",
    64  			inputPath: "pdm.lock.file",
    65  			want:      false,
    66  		},
    67  		{
    68  			name:      "invalid-prefix",
    69  			inputPath: "project.name.pdm.lock",
    70  			want:      false,
    71  		},
    72  	}
    73  
    74  	for _, tt := range tests {
    75  		t.Run(tt.name, func(t *testing.T) {
    76  			e := pdmlock.Extractor{}
    77  			got := e.FileRequired(simplefileapi.New(tt.inputPath, nil))
    78  			if got != tt.want {
    79  				t.Errorf("FileRequired(%q, FileInfo) got = %v, want %v", tt.inputPath, got, tt.want)
    80  			}
    81  		})
    82  	}
    83  }
    84  
    85  func TestExtractor_Extract(t *testing.T) {
    86  	tests := []extracttest.TestTableEntry{
    87  		{
    88  			Name: "invalid toml",
    89  			InputConfig: extracttest.ScanInputMockConfig{
    90  				Path: "testdata/not-toml.txt",
    91  			},
    92  			WantErr:      extracttest.ContainsErrStr{Str: "could not extract"},
    93  			WantPackages: nil,
    94  		},
    95  		{
    96  			Name: "no packages",
    97  			InputConfig: extracttest.ScanInputMockConfig{
    98  				Path: "testdata/empty.toml",
    99  			},
   100  			WantPackages: []*extractor.Package{},
   101  		},
   102  		{
   103  			Name: "single package",
   104  			InputConfig: extracttest.ScanInputMockConfig{
   105  				Path: "testdata/single-package.toml",
   106  			},
   107  			WantPackages: []*extractor.Package{
   108  				{
   109  					Name:      "toml",
   110  					Version:   "0.10.2",
   111  					PURLType:  purl.TypePyPi,
   112  					Locations: []string{"testdata/single-package.toml"},
   113  					Metadata: osv.DepGroupMetadata{
   114  						DepGroupVals: []string{},
   115  					},
   116  				},
   117  			},
   118  		},
   119  		{
   120  			Name: "two packages",
   121  			InputConfig: extracttest.ScanInputMockConfig{
   122  				Path: "testdata/two-packages.toml",
   123  			},
   124  			WantPackages: []*extractor.Package{
   125  				{
   126  					Name:      "toml",
   127  					Version:   "0.10.2",
   128  					PURLType:  purl.TypePyPi,
   129  					Locations: []string{"testdata/two-packages.toml"},
   130  					Metadata: osv.DepGroupMetadata{
   131  						DepGroupVals: []string{},
   132  					},
   133  				},
   134  				{
   135  					Name:      "six",
   136  					Version:   "1.16.0",
   137  					PURLType:  purl.TypePyPi,
   138  					Locations: []string{"testdata/two-packages.toml"},
   139  					Metadata: osv.DepGroupMetadata{
   140  						DepGroupVals: []string{},
   141  					},
   142  				},
   143  			},
   144  		},
   145  		{
   146  			Name: "package with dev dependencies",
   147  			InputConfig: extracttest.ScanInputMockConfig{
   148  				Path: "testdata/dev-dependency.toml",
   149  			},
   150  			WantPackages: []*extractor.Package{
   151  				{
   152  					Name:      "toml",
   153  					Version:   "0.10.2",
   154  					PURLType:  purl.TypePyPi,
   155  					Locations: []string{"testdata/dev-dependency.toml"},
   156  					Metadata: osv.DepGroupMetadata{
   157  						DepGroupVals: []string{},
   158  					},
   159  				},
   160  				{
   161  					Name:      "pyroute2",
   162  					Version:   "0.7.11",
   163  					PURLType:  purl.TypePyPi,
   164  					Locations: []string{"testdata/dev-dependency.toml"},
   165  					Metadata: osv.DepGroupMetadata{
   166  						DepGroupVals: []string{"dev"},
   167  					},
   168  				},
   169  				{
   170  					Name:      "win-inet-pton",
   171  					Version:   "1.1.0",
   172  					PURLType:  purl.TypePyPi,
   173  					Locations: []string{"testdata/dev-dependency.toml"},
   174  					Metadata: osv.DepGroupMetadata{
   175  						DepGroupVals: []string{"dev"},
   176  					},
   177  				},
   178  			},
   179  		},
   180  		{
   181  			Name: "package with optional dependency",
   182  			InputConfig: extracttest.ScanInputMockConfig{
   183  				Path: "testdata/optional-dependency.toml",
   184  			},
   185  			WantPackages: []*extractor.Package{
   186  				{
   187  					Name:      "toml",
   188  					Version:   "0.10.2",
   189  					PURLType:  purl.TypePyPi,
   190  					Locations: []string{"testdata/optional-dependency.toml"},
   191  					Metadata: osv.DepGroupMetadata{
   192  						DepGroupVals: []string{},
   193  					},
   194  				},
   195  				{
   196  					Name:      "pyroute2",
   197  					Version:   "0.7.11",
   198  					PURLType:  purl.TypePyPi,
   199  					Locations: []string{"testdata/optional-dependency.toml"},
   200  					Metadata: osv.DepGroupMetadata{
   201  						DepGroupVals: []string{"optional"},
   202  					},
   203  				},
   204  				{
   205  					Name:      "win-inet-pton",
   206  					Version:   "1.1.0",
   207  					PURLType:  purl.TypePyPi,
   208  					Locations: []string{"testdata/optional-dependency.toml"},
   209  					Metadata: osv.DepGroupMetadata{
   210  						DepGroupVals: []string{"optional"},
   211  					},
   212  				},
   213  			},
   214  		},
   215  		{
   216  			Name: "package with git dependency",
   217  			InputConfig: extracttest.ScanInputMockConfig{
   218  				Path: "testdata/git-dependency.toml",
   219  			},
   220  			WantPackages: []*extractor.Package{
   221  				{
   222  					Name:      "toml",
   223  					Version:   "0.10.2",
   224  					PURLType:  purl.TypePyPi,
   225  					Locations: []string{"testdata/git-dependency.toml"},
   226  					Metadata: osv.DepGroupMetadata{
   227  						DepGroupVals: []string{},
   228  					},
   229  					SourceCode: &extractor.SourceCodeIdentifier{
   230  						Commit: "65bab7582ce14c55cdeec2244c65ea23039c9e6f",
   231  					},
   232  				},
   233  			},
   234  		},
   235  	}
   236  
   237  	for _, tt := range tests {
   238  		t.Run(tt.Name, func(t *testing.T) {
   239  			extr := pdmlock.Extractor{}
   240  
   241  			scanInput := extracttest.GenerateScanInputMock(t, tt.InputConfig)
   242  			defer extracttest.CloseTestScanInput(t, scanInput)
   243  
   244  			got, err := extr.Extract(t.Context(), &scanInput)
   245  
   246  			if diff := cmp.Diff(tt.WantErr, err, cmpopts.EquateErrors()); diff != "" {
   247  				t.Errorf("%s.Extract(%q) error diff (-want +got):\n%s", extr.Name(), tt.InputConfig.Path, diff)
   248  				return
   249  			}
   250  
   251  			wantInv := inventory.Inventory{Packages: tt.WantPackages}
   252  			if diff := cmp.Diff(wantInv, got, cmpopts.SortSlices(extracttest.PackageCmpLess)); diff != "" {
   253  				t.Errorf("%s.Extract(%q) diff (-want +got):\n%s", extr.Name(), tt.InputConfig.Path, diff)
   254  			}
   255  		})
   256  	}
   257  }