github.com/google/osv-scalibr@v0.4.1/extractor/filesystem/language/dotnet/depsjson/depsjson_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 depsjson_test 16 17 import ( 18 "io/fs" 19 "os" 20 "path/filepath" 21 "testing" 22 23 "github.com/google/go-cmp/cmp" 24 "github.com/google/go-cmp/cmp/cmpopts" 25 "github.com/google/osv-scalibr/extractor" 26 "github.com/google/osv-scalibr/extractor/filesystem" 27 "github.com/google/osv-scalibr/extractor/filesystem/internal/units" 28 "github.com/google/osv-scalibr/extractor/filesystem/language/dotnet/depsjson" 29 "github.com/google/osv-scalibr/extractor/filesystem/simplefileapi" 30 scalibrfs "github.com/google/osv-scalibr/fs" 31 "github.com/google/osv-scalibr/inventory" 32 "github.com/google/osv-scalibr/purl" 33 "github.com/google/osv-scalibr/stats" 34 "github.com/google/osv-scalibr/testing/fakefs" 35 "github.com/google/osv-scalibr/testing/testcollector" 36 ) 37 38 func TestNew(t *testing.T) { 39 tests := []struct { 40 name string 41 cfg depsjson.Config 42 wantCfg depsjson.Config 43 }{ 44 { 45 name: "default", 46 cfg: depsjson.DefaultConfig(), 47 wantCfg: depsjson.Config{ 48 MaxFileSizeBytes: 10 * units.MiB, 49 }, 50 }, 51 { 52 name: "custom", 53 cfg: depsjson.Config{ 54 MaxFileSizeBytes: 10, 55 }, 56 wantCfg: depsjson.Config{ 57 MaxFileSizeBytes: 10, 58 }, 59 }, 60 } 61 62 for _, tt := range tests { 63 t.Run(tt.name, func(t *testing.T) { 64 got := depsjson.New(tt.cfg) 65 if diff := cmp.Diff(tt.wantCfg, got.Config()); diff != "" { 66 t.Errorf("New(%+v).Config(): (-want +got):\n%s", tt.cfg, diff) 67 } 68 }) 69 } 70 } 71 72 func TestFileRequired(t *testing.T) { 73 tests := []struct { 74 name string 75 path string 76 fileSizeBytes int64 77 maxFileSizeBytes int64 78 wantRequired bool 79 wantResultMetric stats.FileRequiredResult 80 }{ 81 { 82 name: "application1.deps.json file", 83 path: "application1.deps.json", 84 wantRequired: true, 85 wantResultMetric: stats.FileRequiredResultOK, 86 }, 87 { 88 name: "path application1.deps.json file", 89 path: "path/to/my/application1.deps.json", 90 wantRequired: true, 91 wantResultMetric: stats.FileRequiredResultOK, 92 }, 93 { 94 name: "file not required", 95 path: "/test.deps", 96 wantRequired: false, 97 }, 98 { 99 name: "application1.deps.json file required if file size < max file size", 100 path: "application1.deps.json", 101 fileSizeBytes: 100 * units.KiB, 102 maxFileSizeBytes: 1000 * units.KiB, 103 wantRequired: true, 104 wantResultMetric: stats.FileRequiredResultOK, 105 }, 106 { 107 name: "application1.deps.json file required if file size == max file size", 108 path: "application1.deps.json", 109 fileSizeBytes: 1000 * units.KiB, 110 maxFileSizeBytes: 1000 * units.KiB, 111 wantRequired: true, 112 wantResultMetric: stats.FileRequiredResultOK, 113 }, 114 { 115 name: "application1.deps.json file not required if file size > max file size", 116 path: "application1.deps.json", 117 fileSizeBytes: 1000 * units.KiB, 118 maxFileSizeBytes: 100 * units.KiB, 119 wantRequired: false, 120 wantResultMetric: stats.FileRequiredResultSizeLimitExceeded, 121 }, 122 { 123 name: "application1.deps.json file required if max file size set to 0", 124 path: "application1.deps.json", 125 fileSizeBytes: 100 * units.KiB, 126 maxFileSizeBytes: 0, 127 wantRequired: true, 128 wantResultMetric: stats.FileRequiredResultOK, 129 }, 130 } 131 132 for _, tt := range tests { 133 t.Run(tt.name, func(t *testing.T) { 134 collector := testcollector.New() 135 var e filesystem.Extractor = depsjson.New(depsjson.Config{ 136 Stats: collector, 137 MaxFileSizeBytes: tt.maxFileSizeBytes, 138 }) 139 140 fileSizeBytes := tt.fileSizeBytes 141 if fileSizeBytes == 0 { 142 fileSizeBytes = 1000 143 } 144 145 isRequired := e.FileRequired(simplefileapi.New(tt.path, fakefs.FakeFileInfo{ 146 FileName: filepath.Base(tt.path), 147 FileMode: fs.ModePerm, 148 FileSize: fileSizeBytes, 149 })) 150 if isRequired != tt.wantRequired { 151 t.Fatalf("FileRequired(%s): got %v, want %v", tt.path, isRequired, tt.wantRequired) 152 } 153 154 gotResultMetric := collector.FileRequiredResult(tt.path) 155 if tt.wantResultMetric != "" && gotResultMetric != tt.wantResultMetric { 156 t.Errorf("FileRequired(%s) recorded result metric %v, want result metric %v", tt.path, gotResultMetric, tt.wantResultMetric) 157 } 158 }) 159 } 160 } 161 162 func TestExtract(t *testing.T) { 163 tests := []struct { 164 name string 165 path string 166 osrelease string 167 cfg depsjson.Config 168 wantPackages []*extractor.Package 169 wantErr error 170 wantResultMetric stats.FileExtractedResult 171 }{ 172 { 173 name: "valid_application1.deps.json_file", 174 path: "testdata/valid", 175 wantPackages: []*extractor.Package{ 176 { 177 Name: "TestLibrary", 178 Version: "1.0.0", 179 PURLType: purl.TypeNuget, 180 Metadata: &depsjson.Metadata{ 181 PackageName: "TestLibrary", 182 PackageVersion: "1.0.0", 183 Type: "project", 184 }, 185 Locations: []string{"testdata/valid"}, 186 }, 187 { 188 Name: "AWSSDK.Core", 189 Version: "3.7.10.6", 190 PURLType: purl.TypeNuget, 191 Metadata: &depsjson.Metadata{ 192 PackageName: "AWSSDK.Core", 193 PackageVersion: "3.7.10.6", 194 Type: "package", 195 }, 196 Locations: []string{"testdata/valid"}, 197 }, 198 { 199 Name: "Microsoft.Extensions.DependencyInjection", 200 Version: "6.0.0", 201 PURLType: purl.TypeNuget, 202 Metadata: &depsjson.Metadata{ 203 PackageName: "Microsoft.Extensions.DependencyInjection", 204 PackageVersion: "6.0.0", 205 Type: "package", 206 }, 207 Locations: []string{"testdata/valid"}, 208 }, 209 }, 210 wantResultMetric: stats.FileExtractedResultSuccess, 211 }, 212 { 213 name: "application1.deps.json file not json", 214 path: "testdata/invalid", 215 wantErr: cmpopts.AnyError, 216 wantResultMetric: stats.FileExtractedResultErrorUnknown, 217 }, 218 { 219 name: "application1.deps.json file empty", 220 path: "testdata/empty", 221 wantErr: cmpopts.AnyError, 222 wantResultMetric: stats.FileExtractedResultErrorUnknown, 223 }, 224 { 225 name: "valid_application1.deps.json_file_with_an_invalid_package", 226 path: "testdata/nopackagename", 227 wantPackages: []*extractor.Package{ 228 { 229 Name: "TestLibrary", 230 Version: "1.0.0", 231 PURLType: purl.TypeNuget, 232 Metadata: &depsjson.Metadata{ 233 PackageName: "TestLibrary", 234 PackageVersion: "1.0.0", 235 Type: "project", 236 }, 237 Locations: []string{"testdata/nopackagename"}, 238 }, 239 { 240 Name: "AWSSDK.Core", 241 Version: "3.7.10.6", 242 PURLType: purl.TypeNuget, 243 Metadata: &depsjson.Metadata{ 244 PackageName: "AWSSDK.Core", 245 PackageVersion: "3.7.10.6", 246 Type: "package", 247 }, 248 Locations: []string{"testdata/nopackagename"}, 249 }, 250 }, 251 wantResultMetric: stats.FileExtractedResultSuccess, 252 }, 253 } 254 255 for _, tt := range tests { 256 t.Run(tt.name, func(t *testing.T) { 257 collector := testcollector.New() 258 var e filesystem.Extractor = depsjson.New(depsjson.Config{ 259 Stats: collector, 260 MaxFileSizeBytes: 100, 261 }) 262 263 d := t.TempDir() 264 265 // Opening and Reading the Test File 266 r, err := os.Open(tt.path) 267 defer func() { 268 if err = r.Close(); err != nil { 269 t.Errorf("Close(): %v", err) 270 } 271 }() 272 if err != nil { 273 t.Fatal(err) 274 } 275 276 info, err := os.Stat(tt.path) 277 if err != nil { 278 t.Fatalf("Failed to stat test file: %v", err) 279 } 280 281 input := &filesystem.ScanInput{ 282 FS: scalibrfs.DirFS(d), Path: tt.path, Reader: r, Root: d, Info: info, 283 } 284 285 got, err := e.Extract(t.Context(), input) 286 287 wantInv := inventory.Inventory{Packages: tt.wantPackages} 288 if diff := cmp.Diff(wantInv, got, cmpopts.SortSlices(pkgLess)); diff != "" { 289 t.Errorf("Package mismatch (-want +got):\n%s", diff) 290 } 291 }) 292 } 293 } 294 295 func pkgLess(i1, i2 *extractor.Package) bool { 296 return i1.Name < i2.Name 297 }