github.com/google/osv-scalibr@v0.4.1/extractor/filesystem/language/java/pomxml/pomxml_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 pomxml_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/java/javalockfile" 24 "github.com/google/osv-scalibr/extractor/filesystem/language/java/pomxml" 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 inputPath string 34 want bool 35 }{ 36 { 37 inputPath: "", 38 want: false, 39 }, 40 { 41 inputPath: "pom.xml", 42 want: true, 43 }, 44 { 45 inputPath: "my-app-1.0.pom", 46 want: true, 47 }, 48 { 49 inputPath: "path/to/my/pom.xml", 50 want: true, 51 }, 52 { 53 inputPath: "path/to/my/pom/my-app-1.0.pom", 54 want: true, 55 }, 56 { 57 inputPath: "path/to/my/pom.xml/file", 58 want: false, 59 }, 60 { 61 inputPath: "path/to/my/pom.xml.file", 62 want: false, 63 }, 64 { 65 inputPath: "path.to.my.pom.xml", 66 want: false, 67 }, 68 } 69 for _, tt := range tests { 70 t.Run(tt.inputPath, func(t *testing.T) { 71 e := pomxml.Extractor{} 72 got := e.FileRequired(simplefileapi.New(tt.inputPath, nil)) 73 if got != tt.want { 74 t.Errorf("FileRequired(%s, FileInfo) got = %v, want %v", tt.inputPath, got, tt.want) 75 } 76 }) 77 } 78 } 79 80 func TestExtractor_Extract(t *testing.T) { 81 tests := []extracttest.TestTableEntry{ 82 { 83 Name: "invalid", 84 InputConfig: extracttest.ScanInputMockConfig{ 85 Path: "testdata/not-pom.txt", 86 }, 87 WantPackages: nil, 88 WantErr: extracttest.ContainsErrStr{Str: "could not extract"}, 89 }, 90 { 91 Name: "invalid syntax", 92 InputConfig: extracttest.ScanInputMockConfig{ 93 Path: "testdata/invalid-syntax.xml", 94 }, 95 WantPackages: nil, 96 WantErr: extracttest.ContainsErrStr{Str: "could not extract"}, 97 }, 98 { 99 Name: "no packages", 100 InputConfig: extracttest.ScanInputMockConfig{ 101 Path: "testdata/empty.xml", 102 }, 103 WantPackages: nil, 104 }, 105 { 106 Name: "one package", 107 InputConfig: extracttest.ScanInputMockConfig{ 108 Path: "testdata/one-package.xml", 109 }, 110 WantPackages: []*extractor.Package{ 111 { 112 Name: "org.apache.maven:maven-artifact", 113 Version: "1.0.0", 114 PURLType: purl.TypeMaven, 115 Locations: []string{"testdata/one-package.xml"}, 116 Metadata: &javalockfile.Metadata{ 117 ArtifactID: "maven-artifact", 118 GroupID: "org.apache.maven", 119 DepGroupVals: []string{}, 120 }, 121 }, 122 }, 123 }, 124 { 125 Name: "two packages", 126 InputConfig: extracttest.ScanInputMockConfig{ 127 Path: "testdata/two-packages.xml", 128 }, 129 WantPackages: []*extractor.Package{ 130 { 131 Name: "io.netty:netty-all", 132 Version: "4.1.42.Final", 133 PURLType: purl.TypeMaven, 134 Locations: []string{"testdata/two-packages.xml"}, 135 Metadata: &javalockfile.Metadata{ 136 ArtifactID: "netty-all", 137 GroupID: "io.netty", 138 DepGroupVals: []string{}, 139 }, 140 }, 141 { 142 Name: "org.slf4j:slf4j-log4j12", 143 Version: "1.7.25", 144 PURLType: purl.TypeMaven, 145 Locations: []string{"testdata/two-packages.xml"}, 146 Metadata: &javalockfile.Metadata{ 147 ArtifactID: "slf4j-log4j12", 148 GroupID: "org.slf4j", 149 DepGroupVals: []string{}, 150 }, 151 }, 152 }, 153 }, 154 { 155 Name: "with dependency management", 156 InputConfig: extracttest.ScanInputMockConfig{ 157 Path: "testdata/with-dependency-management.xml", 158 }, 159 WantPackages: []*extractor.Package{ 160 { 161 Name: "io.netty:netty-all", 162 Version: "4.1.9", 163 PURLType: purl.TypeMaven, 164 Locations: []string{"testdata/with-dependency-management.xml"}, 165 Metadata: &javalockfile.Metadata{ 166 ArtifactID: "netty-all", 167 GroupID: "io.netty", 168 DepGroupVals: []string{}, 169 }, 170 }, 171 { 172 Name: "org.slf4j:slf4j-log4j12", 173 Version: "1.7.25", 174 PURLType: purl.TypeMaven, 175 Locations: []string{"testdata/with-dependency-management.xml"}, 176 Metadata: &javalockfile.Metadata{ 177 ArtifactID: "slf4j-log4j12", 178 GroupID: "org.slf4j", 179 DepGroupVals: []string{}, 180 }, 181 }, 182 }, 183 }, 184 { 185 Name: "interpolation", 186 InputConfig: extracttest.ScanInputMockConfig{ 187 Path: "testdata/interpolation.xml", 188 }, 189 WantPackages: []*extractor.Package{ 190 { 191 Name: "org.mine:mypackage", 192 Version: "1.0.0", 193 PURLType: purl.TypeMaven, 194 Locations: []string{"testdata/interpolation.xml"}, 195 Metadata: &javalockfile.Metadata{ 196 ArtifactID: "mypackage", 197 GroupID: "org.mine", 198 DepGroupVals: []string{}, 199 }, 200 }, 201 { 202 Name: "org.mine:my.package", 203 Version: "2.3.4", 204 PURLType: purl.TypeMaven, 205 Locations: []string{"testdata/interpolation.xml"}, 206 Metadata: &javalockfile.Metadata{ 207 ArtifactID: "my.package", 208 GroupID: "org.mine", 209 DepGroupVals: []string{}, 210 }, 211 }, 212 { 213 Name: "org.mine:ranged-package", 214 Version: "9.4.35.v20201120", 215 PURLType: purl.TypeMaven, 216 Locations: []string{"testdata/interpolation.xml"}, 217 Metadata: &javalockfile.Metadata{ 218 ArtifactID: "ranged-package", 219 GroupID: "org.mine", 220 DepGroupVals: []string{}, 221 }, 222 }, 223 }, 224 }, 225 { 226 Name: "with scope", 227 InputConfig: extracttest.ScanInputMockConfig{ 228 Path: "testdata/with-scope.xml", 229 }, 230 WantPackages: []*extractor.Package{ 231 { 232 Name: "abc:xyz", 233 Version: "1.2.3", 234 PURLType: purl.TypeMaven, 235 Locations: []string{"testdata/with-scope.xml"}, 236 Metadata: &javalockfile.Metadata{ 237 ArtifactID: "xyz", 238 GroupID: "abc", 239 DepGroupVals: []string{}, 240 }, 241 }, 242 { 243 Name: "junit:junit", 244 Version: "4.12", 245 PURLType: purl.TypeMaven, 246 Locations: []string{"testdata/with-scope.xml"}, 247 Metadata: &javalockfile.Metadata{ 248 ArtifactID: "junit", 249 GroupID: "junit", 250 DepGroupVals: []string{"test"}, 251 }, 252 }, 253 }, 254 }, 255 { 256 Name: "with type and classifier", 257 InputConfig: extracttest.ScanInputMockConfig{ 258 Path: "testdata/with-type-classifier.xml", 259 }, 260 WantPackages: []*extractor.Package{ 261 { 262 Name: "abc:xyz", 263 Version: "1.0.0", 264 PURLType: purl.TypeMaven, 265 Locations: []string{"testdata/with-type-classifier.xml"}, 266 Metadata: &javalockfile.Metadata{ 267 ArtifactID: "xyz", 268 GroupID: "abc", 269 Type: "pom", 270 Classifier: "sources", 271 DepGroupVals: []string{}, 272 }, 273 }, 274 }, 275 }, 276 { 277 Name: "with parent", 278 InputConfig: extracttest.ScanInputMockConfig{ 279 Path: "testdata/with-parent.xml", 280 }, 281 WantPackages: []*extractor.Package{ 282 { 283 Name: "org.alice:alice", 284 Version: "1.0.0", 285 PURLType: purl.TypeMaven, 286 Locations: []string{"testdata/with-parent.xml"}, 287 Metadata: &javalockfile.Metadata{ 288 ArtifactID: "alice", 289 GroupID: "org.alice", 290 DepGroupVals: []string{}, 291 }, 292 }, 293 { 294 Name: "org.bob:bob", 295 Version: "2.0.0", 296 PURLType: purl.TypeMaven, 297 Locations: []string{"testdata/with-parent.xml"}, 298 Metadata: &javalockfile.Metadata{ 299 ArtifactID: "bob", 300 GroupID: "org.bob", 301 DepGroupVals: []string{}, 302 }, 303 }, 304 { 305 Name: "org.chuck:chuck", 306 Version: "3.0.0", 307 PURLType: purl.TypeMaven, 308 Locations: []string{"testdata/with-parent.xml"}, 309 Metadata: &javalockfile.Metadata{ 310 ArtifactID: "chuck", 311 GroupID: "org.chuck", 312 DepGroupVals: []string{}, 313 }, 314 }, 315 { 316 Name: "org.dave:dave", 317 Version: "4.0.0", 318 PURLType: purl.TypeMaven, 319 Locations: []string{"testdata/with-parent.xml"}, 320 Metadata: &javalockfile.Metadata{ 321 ArtifactID: "dave", 322 GroupID: "org.dave", 323 DepGroupVals: []string{}, 324 }, 325 }, 326 { 327 Name: "org.frank:frank", 328 // Version is not available in the local pom.xml. 329 PURLType: purl.TypeMaven, 330 Locations: []string{"testdata/with-parent.xml"}, 331 Metadata: &javalockfile.Metadata{ 332 ArtifactID: "frank", 333 GroupID: "org.frank", 334 DepGroupVals: []string{}, 335 }, 336 }, 337 }, 338 }, 339 } 340 341 for _, tt := range tests { 342 t.Run(tt.Name, func(t *testing.T) { 343 extr := pomxml.Extractor{} 344 345 scanInput := extracttest.GenerateScanInputMock(t, tt.InputConfig) 346 defer extracttest.CloseTestScanInput(t, scanInput) 347 348 got, err := extr.Extract(t.Context(), &scanInput) 349 350 if diff := cmp.Diff(tt.WantErr, err, cmpopts.EquateErrors()); diff != "" { 351 t.Errorf("%s.Extract(%q) error diff (-want +got):\n%s", extr.Name(), tt.InputConfig.Path, diff) 352 return 353 } 354 355 wantInv := inventory.Inventory{Packages: tt.WantPackages} 356 if diff := cmp.Diff(wantInv, got, cmpopts.SortSlices(extracttest.PackageCmpLess)); diff != "" { 357 t.Errorf("%s.Extract(%q) diff (-want +got):\n%s", extr.Name(), tt.InputConfig.Path, diff) 358 } 359 }) 360 } 361 }