github.com/google/osv-scalibr@v0.4.1/extractor/filesystem/language/dart/pubspec/pubspec_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 pubspec_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/dart/pubspec"
    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  			inputPath: "",
    39  			want:      false,
    40  		},
    41  		{
    42  			inputPath: "pubspec.lock",
    43  			want:      true,
    44  		},
    45  		{
    46  			inputPath: "path/to/my/pubspec.lock",
    47  			want:      true,
    48  		},
    49  		{
    50  			inputPath: "path/to/my/pubspec.lock/file",
    51  			want:      false,
    52  		},
    53  		{
    54  			inputPath: "path/to/my/pubspec.lock.file",
    55  			want:      false,
    56  		},
    57  		{
    58  			inputPath: "path.to.my.pubspec.lock",
    59  			want:      false,
    60  		},
    61  	}
    62  	for _, tt := range tests {
    63  		t.Run(tt.inputPath, func(t *testing.T) {
    64  			e := pubspec.Extractor{}
    65  			got := e.FileRequired(simplefileapi.New(tt.inputPath, nil))
    66  			if got != tt.want {
    67  				t.Errorf("FileRequired(%s, FileInfo) got = %v, want %v", tt.inputPath, got, tt.want)
    68  			}
    69  		})
    70  	}
    71  }
    72  
    73  func TestExtractor_Extract(t *testing.T) {
    74  	tests := []extracttest.TestTableEntry{
    75  		{
    76  			Name: "invalid yaml",
    77  			InputConfig: extracttest.ScanInputMockConfig{
    78  				Path: "testdata/not-yaml.txt",
    79  			},
    80  			WantErr: extracttest.ContainsErrStr{Str: "could not extract"},
    81  		},
    82  		{
    83  			Name: "empty",
    84  			InputConfig: extracttest.ScanInputMockConfig{
    85  				Path: "testdata/empty.lock",
    86  			},
    87  			WantErr: extracttest.ContainsErrStr{Str: "could not extract"},
    88  		},
    89  		{
    90  			Name: "no packages",
    91  			InputConfig: extracttest.ScanInputMockConfig{
    92  				Path: "testdata/no-packages.lock",
    93  			},
    94  			WantPackages: []*extractor.Package{},
    95  		},
    96  		{
    97  			Name: "one package",
    98  			InputConfig: extracttest.ScanInputMockConfig{
    99  				Path: "testdata/one-package.lock",
   100  			},
   101  			WantPackages: []*extractor.Package{
   102  				{
   103  					Name:      "back_button_interceptor",
   104  					Version:   "6.0.1",
   105  					PURLType:  purl.TypePub,
   106  					Locations: []string{"testdata/one-package.lock"},
   107  					SourceCode: &extractor.SourceCodeIdentifier{
   108  						Commit: "",
   109  					},
   110  					Metadata: osv.DepGroupMetadata{},
   111  				},
   112  			},
   113  		},
   114  		{
   115  			Name: "one package dev",
   116  			InputConfig: extracttest.ScanInputMockConfig{
   117  				Path: "testdata/one-package-dev.lock",
   118  			},
   119  			WantPackages: []*extractor.Package{
   120  				{
   121  					Name:      "build_runner",
   122  					Version:   "2.2.1",
   123  					PURLType:  purl.TypePub,
   124  					Locations: []string{"testdata/one-package-dev.lock"},
   125  					SourceCode: &extractor.SourceCodeIdentifier{
   126  						Commit: "",
   127  					},
   128  					Metadata: osv.DepGroupMetadata{
   129  						DepGroupVals: []string{"dev"},
   130  					},
   131  				},
   132  			},
   133  		},
   134  		{
   135  			Name: "two packages",
   136  			InputConfig: extracttest.ScanInputMockConfig{
   137  				Path: "testdata/two-packages.lock",
   138  			},
   139  			WantPackages: []*extractor.Package{
   140  				{
   141  					Name:      "shelf",
   142  					Version:   "1.3.2",
   143  					PURLType:  purl.TypePub,
   144  					Locations: []string{"testdata/two-packages.lock"},
   145  					SourceCode: &extractor.SourceCodeIdentifier{
   146  						Commit: "",
   147  					},
   148  					Metadata: osv.DepGroupMetadata{},
   149  				},
   150  				{
   151  					Name:      "shelf_web_socket",
   152  					Version:   "1.0.2",
   153  					PURLType:  purl.TypePub,
   154  					Locations: []string{"testdata/two-packages.lock"},
   155  					SourceCode: &extractor.SourceCodeIdentifier{
   156  						Commit: "",
   157  					},
   158  					Metadata: osv.DepGroupMetadata{},
   159  				},
   160  			},
   161  		},
   162  		{
   163  			Name: "mixed packages",
   164  			InputConfig: extracttest.ScanInputMockConfig{
   165  				Path: "testdata/mixed-packages.lock",
   166  			},
   167  			WantPackages: []*extractor.Package{
   168  				{
   169  					Name:      "back_button_interceptor",
   170  					Version:   "6.0.1",
   171  					PURLType:  purl.TypePub,
   172  					Locations: []string{"testdata/mixed-packages.lock"},
   173  					SourceCode: &extractor.SourceCodeIdentifier{
   174  						Commit: "",
   175  					},
   176  					Metadata: osv.DepGroupMetadata{},
   177  				},
   178  				{
   179  					Name:      "build_runner",
   180  					Version:   "2.2.1",
   181  					PURLType:  purl.TypePub,
   182  					Locations: []string{"testdata/mixed-packages.lock"},
   183  					SourceCode: &extractor.SourceCodeIdentifier{
   184  						Commit: "",
   185  					},
   186  					Metadata: osv.DepGroupMetadata{
   187  						DepGroupVals: []string{"dev"},
   188  					},
   189  				},
   190  				{
   191  					Name:      "shelf",
   192  					Version:   "1.3.2",
   193  					PURLType:  purl.TypePub,
   194  					Locations: []string{"testdata/mixed-packages.lock"},
   195  					SourceCode: &extractor.SourceCodeIdentifier{
   196  						Commit: "",
   197  					},
   198  					Metadata: osv.DepGroupMetadata{},
   199  				},
   200  				{
   201  					Name:      "shelf_web_socket",
   202  					Version:   "1.0.2",
   203  					PURLType:  purl.TypePub,
   204  					Locations: []string{"testdata/mixed-packages.lock"},
   205  					SourceCode: &extractor.SourceCodeIdentifier{
   206  						Commit: "",
   207  					},
   208  					Metadata: osv.DepGroupMetadata{},
   209  				},
   210  			},
   211  		},
   212  		{
   213  			Name: "package with git source",
   214  			InputConfig: extracttest.ScanInputMockConfig{
   215  				Path: "testdata/source-git.lock",
   216  			},
   217  			WantPackages: []*extractor.Package{
   218  				{
   219  					Name:      "flutter_rust_bridge",
   220  					Version:   "1.32.0",
   221  					PURLType:  purl.TypePub,
   222  					Locations: []string{"testdata/source-git.lock"},
   223  					SourceCode: &extractor.SourceCodeIdentifier{
   224  						Commit: "e5adce55eea0b74d3680e66a2c5252edf17b07e1",
   225  					},
   226  					Metadata: osv.DepGroupMetadata{},
   227  				},
   228  				{
   229  					Name:      "screen_retriever",
   230  					Version:   "0.1.2",
   231  					PURLType:  purl.TypePub,
   232  					Locations: []string{"testdata/source-git.lock"},
   233  					SourceCode: &extractor.SourceCodeIdentifier{
   234  						Commit: "406b9b038b2c1d779f1e7bf609c8c248be247372",
   235  					},
   236  					Metadata: osv.DepGroupMetadata{},
   237  				},
   238  				{
   239  					Name:      "tray_manager",
   240  					Version:   "0.1.8",
   241  					PURLType:  purl.TypePub,
   242  					Locations: []string{"testdata/source-git.lock"},
   243  					SourceCode: &extractor.SourceCodeIdentifier{
   244  						Commit: "3aa37c86e47ea748e7b5507cbe59f2c54ebdb23a",
   245  					},
   246  					Metadata: osv.DepGroupMetadata{},
   247  				},
   248  				{
   249  					Name:      "window_manager",
   250  					Version:   "0.2.7",
   251  					PURLType:  purl.TypePub,
   252  					Locations: []string{"testdata/source-git.lock"},
   253  					SourceCode: &extractor.SourceCodeIdentifier{
   254  						Commit: "88487257cbafc501599ab4f82ec343b46acec020",
   255  					},
   256  					Metadata: osv.DepGroupMetadata{},
   257  				},
   258  				{
   259  					Name:      "toggle_switch",
   260  					Version:   "1.4.0",
   261  					PURLType:  purl.TypePub,
   262  					Locations: []string{"testdata/source-git.lock"},
   263  					SourceCode: &extractor.SourceCodeIdentifier{
   264  						Commit: "",
   265  					},
   266  					Metadata: osv.DepGroupMetadata{},
   267  				},
   268  			},
   269  		},
   270  		{
   271  			Name: "package with sdk source",
   272  			InputConfig: extracttest.ScanInputMockConfig{
   273  				Path: "testdata/source-sdk.lock",
   274  			},
   275  			WantPackages: []*extractor.Package{
   276  				{
   277  					Name:      "flutter_web_plugins",
   278  					Version:   "0.0.0",
   279  					PURLType:  purl.TypePub,
   280  					Locations: []string{"testdata/source-sdk.lock"},
   281  					SourceCode: &extractor.SourceCodeIdentifier{
   282  						Commit: "",
   283  					},
   284  					Metadata: osv.DepGroupMetadata{},
   285  				},
   286  			},
   287  		},
   288  		{
   289  			Name: "package with path source",
   290  			InputConfig: extracttest.ScanInputMockConfig{
   291  				Path: "testdata/source-path.lock",
   292  			},
   293  			WantPackages: []*extractor.Package{
   294  				{
   295  					Name:      "maa_core",
   296  					Version:   "0.0.1",
   297  					PURLType:  purl.TypePub,
   298  					Locations: []string{"testdata/source-path.lock"},
   299  					SourceCode: &extractor.SourceCodeIdentifier{
   300  						Commit: "",
   301  					},
   302  					Metadata: osv.DepGroupMetadata{},
   303  				},
   304  			},
   305  		},
   306  	}
   307  
   308  	for _, tt := range tests {
   309  		t.Run(tt.Name, func(t *testing.T) {
   310  			extr := pubspec.Extractor{}
   311  
   312  			scanInput := extracttest.GenerateScanInputMock(t, tt.InputConfig)
   313  			defer extracttest.CloseTestScanInput(t, scanInput)
   314  
   315  			got, err := extr.Extract(t.Context(), &scanInput)
   316  
   317  			if diff := cmp.Diff(tt.WantErr, err, cmpopts.EquateErrors()); diff != "" {
   318  				t.Errorf("%s.Extract(%q) error diff (-want +got):\n%s", extr.Name(), tt.InputConfig.Path, diff)
   319  				return
   320  			}
   321  
   322  			wantInv := inventory.Inventory{Packages: tt.WantPackages}
   323  			if diff := cmp.Diff(wantInv, got, cmpopts.SortSlices(extracttest.PackageCmpLess)); diff != "" {
   324  				t.Errorf("%s.Extract(%q) diff (-want +got):\n%s", extr.Name(), tt.InputConfig.Path, diff)
   325  			}
   326  		})
   327  	}
   328  }