github.com/google/osv-scalibr@v0.4.1/extractor/filesystem/language/dotnet/packagesconfig/packagesconfig_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 // Copyright 2024 Google LLC 16 // 17 // Licensed under the Apache License, Version 2.0 (the "License"); 18 // you may not use this file except in compliance with the License. 19 // You may obtain a copy of the License at 20 // 21 // http://www.apache.org/licenses/LICENSE-2.0 22 // 23 // Unless required by applicable law or agreed to in writing, software 24 // distributed under the License is distributed on an "AS IS" BASIS, 25 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 26 // See the License for the specific language governing permissions and 27 // limitations under the License. 28 29 package packagesconfig_test 30 31 import ( 32 "io/fs" 33 "path/filepath" 34 "testing" 35 36 "github.com/google/go-cmp/cmp" 37 "github.com/google/go-cmp/cmp/cmpopts" 38 "github.com/google/osv-scalibr/extractor" 39 "github.com/google/osv-scalibr/extractor/filesystem" 40 "github.com/google/osv-scalibr/extractor/filesystem/internal/units" 41 "github.com/google/osv-scalibr/extractor/filesystem/language/dotnet/packagesconfig" 42 "github.com/google/osv-scalibr/extractor/filesystem/simplefileapi" 43 "github.com/google/osv-scalibr/inventory" 44 "github.com/google/osv-scalibr/purl" 45 "github.com/google/osv-scalibr/stats" 46 "github.com/google/osv-scalibr/testing/extracttest" 47 "github.com/google/osv-scalibr/testing/fakefs" 48 "github.com/google/osv-scalibr/testing/testcollector" 49 ) 50 51 func TestNew(t *testing.T) { 52 tests := []struct { 53 name string 54 cfg packagesconfig.Config 55 wantCfg packagesconfig.Config 56 }{ 57 { 58 name: "default", 59 cfg: packagesconfig.DefaultConfig(), 60 wantCfg: packagesconfig.Config{ 61 MaxFileSizeBytes: 20 * units.MiB, 62 }, 63 }, 64 { 65 name: "custom", 66 cfg: packagesconfig.Config{ 67 MaxFileSizeBytes: 20, 68 }, 69 wantCfg: packagesconfig.Config{ 70 MaxFileSizeBytes: 20, 71 }, 72 }, 73 } 74 75 for _, tt := range tests { 76 t.Run(tt.name, func(t *testing.T) { 77 got := packagesconfig.New(tt.cfg) 78 if diff := cmp.Diff(tt.wantCfg, got.Config()); diff != "" { 79 t.Errorf("New(%+v).Config(): (-want +got):\n%s", tt.cfg, diff) 80 } 81 }) 82 } 83 } 84 85 func TestFileRequired(t *testing.T) { 86 tests := []struct { 87 name string 88 path string 89 fileSizeBytes int64 90 maxFileSizeBytes int64 91 wantRequired bool 92 wantResultMetric stats.FileRequiredResult 93 }{ 94 { 95 name: "packages.config file", 96 path: "packages.config", 97 wantRequired: true, 98 wantResultMetric: stats.FileRequiredResultOK, 99 }, 100 { 101 name: "path packages.config file", 102 path: "path/to/my/packages.config", 103 wantRequired: true, 104 wantResultMetric: stats.FileRequiredResultOK, 105 }, 106 { 107 name: "file not required", 108 path: "test.config", 109 wantRequired: false, 110 }, 111 { 112 name: "packages.config file required if file size < max file size", 113 path: "packages.config", 114 fileSizeBytes: 100 * units.KiB, 115 maxFileSizeBytes: 1000 * units.KiB, 116 wantRequired: true, 117 wantResultMetric: stats.FileRequiredResultOK, 118 }, 119 { 120 name: "packages.config file required if file size == max file size", 121 path: "packages.config", 122 fileSizeBytes: 1000 * units.KiB, 123 maxFileSizeBytes: 1000 * units.KiB, 124 wantRequired: true, 125 wantResultMetric: stats.FileRequiredResultOK, 126 }, 127 { 128 name: "packages.config file not required if file size > max file size", 129 path: "packages.config", 130 fileSizeBytes: 1000 * units.KiB, 131 maxFileSizeBytes: 100 * units.KiB, 132 wantRequired: false, 133 wantResultMetric: stats.FileRequiredResultSizeLimitExceeded, 134 }, 135 { 136 name: "packages.config file required if max file size set to 0", 137 path: "packages.config", 138 fileSizeBytes: 100 * units.KiB, 139 maxFileSizeBytes: 0, 140 wantRequired: true, 141 wantResultMetric: stats.FileRequiredResultOK, 142 }, 143 } 144 145 for _, tt := range tests { 146 t.Run(tt.name, func(t *testing.T) { 147 collector := testcollector.New() 148 var e filesystem.Extractor = packagesconfig.New(packagesconfig.Config{ 149 Stats: collector, 150 MaxFileSizeBytes: tt.maxFileSizeBytes, 151 }) 152 153 fileSizeBytes := tt.fileSizeBytes 154 if fileSizeBytes == 0 { 155 fileSizeBytes = 1000 156 } 157 158 isRequired := e.FileRequired(simplefileapi.New(tt.path, fakefs.FakeFileInfo{ 159 FileName: filepath.Base(tt.path), 160 FileMode: fs.ModePerm, 161 FileSize: fileSizeBytes, 162 })) 163 if isRequired != tt.wantRequired { 164 t.Fatalf("FileRequired(%s): got %v, want %v", tt.path, isRequired, tt.wantRequired) 165 } 166 167 gotResultMetric := collector.FileRequiredResult(tt.path) 168 if tt.wantResultMetric != "" && gotResultMetric != tt.wantResultMetric { 169 t.Errorf("FileRequired(%s) recorded result metric %v, want result metric %v", tt.path, gotResultMetric, tt.wantResultMetric) 170 } 171 }) 172 } 173 } 174 175 func TestExtract(t *testing.T) { 176 tests := []extracttest.TestTableEntry{ 177 { 178 Name: "valid packages.config file", 179 InputConfig: extracttest.ScanInputMockConfig{ 180 Path: "testdata/valid", 181 }, 182 WantPackages: []*extractor.Package{ 183 { 184 Name: "Microsoft.CodeDom.Providers.DotNetCompilerPlatform", 185 Version: "1.0.0", 186 PURLType: purl.TypeNuget, 187 Locations: []string{"testdata/valid"}, 188 }, 189 { 190 Name: "Microsoft.Net.Compilers", 191 Version: "1.0.0", 192 PURLType: purl.TypeNuget, 193 Locations: []string{"testdata/valid"}, 194 }, 195 }, 196 }, 197 { 198 Name: "packages.config file not xml", 199 InputConfig: extracttest.ScanInputMockConfig{ 200 Path: "testdata/invalid", 201 }, 202 WantErr: cmpopts.AnyError, 203 }, 204 { 205 Name: "packages without version", 206 InputConfig: extracttest.ScanInputMockConfig{ 207 Path: "testdata/noversion", 208 }, 209 WantPackages: []*extractor.Package{ 210 { 211 Name: "Microsoft.CodeDom.Providers.DotNetCompilerPlatform", 212 Version: "1.0.0", 213 PURLType: purl.TypeNuget, 214 Locations: []string{"testdata/noversion"}, 215 }, 216 }, 217 }, 218 { 219 Name: "packages without name", 220 InputConfig: extracttest.ScanInputMockConfig{ 221 Path: "testdata/nopackage", 222 }, 223 WantPackages: []*extractor.Package{ 224 { 225 Name: "Microsoft.CodeDom.Providers.DotNetCompilerPlatform", 226 Version: "1.0.0", 227 PURLType: purl.TypeNuget, 228 Locations: []string{"testdata/nopackage"}, 229 }, 230 }, 231 }, 232 } 233 234 for _, tt := range tests { 235 t.Run(tt.Name, func(t *testing.T) { 236 collector := testcollector.New() 237 var e filesystem.Extractor = packagesconfig.New(packagesconfig.Config{ 238 Stats: collector, 239 MaxFileSizeBytes: 100, 240 }) 241 242 // Use the generated scan input for each test case 243 scanInput := extracttest.GenerateScanInputMock(t, tt.InputConfig) 244 defer extracttest.CloseTestScanInput(t, scanInput) 245 246 got, err := e.Extract(t.Context(), &scanInput) 247 248 // Compare errors if any 249 if diff := cmp.Diff(tt.WantErr, err, cmpopts.EquateErrors()); diff != "" { 250 t.Errorf("%s.Extract(%q) error diff (-want +got):\n%s", e.Name(), tt.InputConfig.Path, diff) 251 return 252 } 253 254 // Compare the expected package with the actual package 255 wantInv := inventory.Inventory{Packages: tt.WantPackages} 256 if diff := cmp.Diff(wantInv, got, cmpopts.SortSlices(extracttest.PackageCmpLess)); diff != "" { 257 t.Errorf("%s.Extract(%q) diff (-want +got):\n%s", e.Name(), tt.InputConfig.Path, diff) 258 } 259 260 wantInv = inventory.Inventory{Packages: tt.WantPackages} 261 if diff := cmp.Diff(wantInv, got, cmpopts.SortSlices(extracttest.PackageCmpLess)); diff != "" { 262 t.Errorf("%s.Extract(%q) diff (-want +got):\n%s", e.Name(), tt.InputConfig.Path, diff) 263 } 264 }) 265 } 266 }