github.com/google/osv-scalibr@v0.4.1/extractor/filesystem/language/rust/cargotoml/cargotoml_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 cargotoml_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/rust/cargotoml"
    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:      "Empty path",
    38  			inputPath: "",
    39  			want:      false,
    40  		},
    41  		{
    42  			name:      "",
    43  			inputPath: "Cargo.toml",
    44  			want:      true,
    45  		},
    46  		{
    47  			name:      "",
    48  			inputPath: "path/to/my/Cargo.toml",
    49  			want:      true,
    50  		},
    51  		{
    52  			name:      "",
    53  			inputPath: "path/to/my/Cargo.toml/file",
    54  			want:      false,
    55  		},
    56  		{
    57  			name:      "",
    58  			inputPath: "path/to/my/Cargo.toml.file",
    59  			want:      false,
    60  		},
    61  		{
    62  			name:      "",
    63  			inputPath: "path.to.my.Cargo.toml",
    64  			want:      false,
    65  		},
    66  	}
    67  
    68  	for _, tt := range tests {
    69  		t.Run(tt.name, func(t *testing.T) {
    70  			e := cargotoml.Extractor{}
    71  			got := e.FileRequired(simplefileapi.New(tt.inputPath, nil))
    72  			if got != tt.want {
    73  				t.Errorf("FileRequired(%s, 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 toml",
    83  			InputConfig: extracttest.ScanInputMockConfig{
    84  				Path: "testdata/not-toml.txt",
    85  			},
    86  			WantPackages: nil,
    87  			WantErr:      extracttest.ContainsErrStr{Str: "could not extract"},
    88  		},
    89  		{
    90  			Name: "Invalid dependency toml",
    91  			InputConfig: extracttest.ScanInputMockConfig{
    92  				Path: "testdata/invalid-dependency.toml",
    93  			},
    94  			WantPackages: nil,
    95  			WantErr:      extracttest.ContainsErrStr{Str: "could not extract"},
    96  		},
    97  		{
    98  			Name: "no dependencies",
    99  			InputConfig: extracttest.ScanInputMockConfig{
   100  				Path: "testdata/no-dependency.toml",
   101  			},
   102  			WantPackages: []*extractor.Package{
   103  				{
   104  					Name:      "hello_world",
   105  					Version:   "0.1.0",
   106  					PURLType:  purl.TypeCargo,
   107  					Locations: []string{"testdata/no-dependency.toml"},
   108  				},
   109  			},
   110  		},
   111  		{
   112  			Name: "dependency with only version specified",
   113  			InputConfig: extracttest.ScanInputMockConfig{
   114  				Path: "testdata/only-version-dependency.toml",
   115  			},
   116  			WantPackages: []*extractor.Package{
   117  				{
   118  					Name:      "hello_world",
   119  					Version:   "0.1.0",
   120  					PURLType:  purl.TypeCargo,
   121  					Locations: []string{"testdata/only-version-dependency.toml"},
   122  				},
   123  				{
   124  					Name:      "regex",
   125  					Version:   "0.0.1",
   126  					PURLType:  purl.TypeCargo,
   127  					Locations: []string{"testdata/only-version-dependency.toml"},
   128  				},
   129  			},
   130  		},
   131  		{
   132  			// dependencies with only tag specified should be skipped
   133  			Name: "git dependency with tag specified",
   134  			InputConfig: extracttest.ScanInputMockConfig{
   135  				Path: "testdata/git-dependency-tagged.toml",
   136  			},
   137  			WantPackages: []*extractor.Package{
   138  				{
   139  					Name:      "hello_world",
   140  					Version:   "0.1.0",
   141  					PURLType:  purl.TypeCargo,
   142  					Locations: []string{"testdata/git-dependency-tagged.toml"},
   143  				},
   144  			},
   145  		},
   146  		{
   147  			Name: "git dependency with commit specified",
   148  			InputConfig: extracttest.ScanInputMockConfig{
   149  				Path: "testdata/git-dependency-with-commit.toml",
   150  			},
   151  			WantPackages: []*extractor.Package{
   152  				{
   153  					Name:      "hello_world",
   154  					Version:   "0.1.0",
   155  					PURLType:  purl.TypeCargo,
   156  					Locations: []string{"testdata/git-dependency-with-commit.toml"},
   157  				},
   158  				{
   159  					Name:     "regex",
   160  					PURLType: purl.TypeCargo,
   161  					SourceCode: &extractor.SourceCodeIdentifier{
   162  						Repo:   "https://github.com/rust-lang/regex.git",
   163  						Commit: "0c0990399270277832fbb5b91a1fa118e6f63dba",
   164  					},
   165  					Locations: []string{"testdata/git-dependency-with-commit.toml"},
   166  				},
   167  			},
   168  		},
   169  		{
   170  			Name: "git dependency with pr rev specified",
   171  			InputConfig: extracttest.ScanInputMockConfig{
   172  				Path: "testdata/git-dependency-with-pr.toml",
   173  			},
   174  			WantPackages: []*extractor.Package{
   175  				{
   176  					Name:      "hello_world",
   177  					Version:   "0.1.0",
   178  					PURLType:  purl.TypeCargo,
   179  					Locations: []string{"testdata/git-dependency-with-pr.toml"},
   180  				},
   181  			},
   182  		},
   183  		{
   184  			Name: "two dependencies",
   185  			InputConfig: extracttest.ScanInputMockConfig{
   186  				Path: "testdata/two-dependencies.toml",
   187  			},
   188  			WantPackages: []*extractor.Package{
   189  				{
   190  					Name:      "hello_world",
   191  					Version:   "0.1.0",
   192  					PURLType:  purl.TypeCargo,
   193  					Locations: []string{"testdata/two-dependencies.toml"},
   194  				},
   195  				{
   196  					Name:      "futures",
   197  					Version:   "0.3",
   198  					PURLType:  purl.TypeCargo,
   199  					Locations: []string{"testdata/two-dependencies.toml"},
   200  				},
   201  			},
   202  		},
   203  	}
   204  
   205  	for _, tt := range tests {
   206  		t.Run(tt.Name, func(t *testing.T) {
   207  			extr := cargotoml.Extractor{}
   208  
   209  			scanInput := extracttest.GenerateScanInputMock(t, tt.InputConfig)
   210  			defer extracttest.CloseTestScanInput(t, scanInput)
   211  
   212  			got, err := extr.Extract(t.Context(), &scanInput)
   213  
   214  			if diff := cmp.Diff(tt.WantErr, err, cmpopts.EquateErrors()); diff != "" {
   215  				t.Errorf("%s.Extract(%q) error diff (-want +got):\n%s", extr.Name(), tt.InputConfig.Path, diff)
   216  				return
   217  			}
   218  
   219  			wantInv := inventory.Inventory{Packages: tt.WantPackages}
   220  			if diff := cmp.Diff(wantInv, got, cmpopts.SortSlices(extracttest.PackageCmpLess)); diff != "" {
   221  				t.Errorf("%s.Extract(%q) diff (-want +got):\n%s", extr.Name(), tt.InputConfig.Path, diff)
   222  			}
   223  		})
   224  	}
   225  }