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