github.com/nextlinux/gosbom@v0.81.1-0.20230627115839-1ff50c281391/gosbom/pkg/cataloger/java/archive_parser_test.go (about) 1 package java 2 3 import ( 4 "bufio" 5 "fmt" 6 "io" 7 "os" 8 "os/exec" 9 "path" 10 "path/filepath" 11 "strings" 12 "syscall" 13 "testing" 14 15 "github.com/gookit/color" 16 "github.com/nextlinux/gosbom/gosbom/file" 17 "github.com/nextlinux/gosbom/gosbom/pkg" 18 "github.com/nextlinux/gosbom/gosbom/pkg/cataloger/internal/pkgtest" 19 "github.com/nextlinux/gosbom/internal" 20 "github.com/stretchr/testify/require" 21 ) 22 23 func generateJavaBuildFixture(t *testing.T, fixturePath string) { 24 if _, err := os.Stat(fixturePath); !os.IsNotExist(err) { 25 // fixture already exists... 26 return 27 } 28 29 makeTask := strings.TrimPrefix(fixturePath, "test-fixtures/java-builds/") 30 t.Logf(color.Bold.Sprintf("Generating Fixture from 'make %s'", makeTask)) 31 32 cwd, err := os.Getwd() 33 if err != nil { 34 t.Errorf("unable to get cwd: %+v", err) 35 } 36 37 cmd := exec.Command("make", makeTask) 38 cmd.Dir = filepath.Join(cwd, "test-fixtures/java-builds/") 39 40 stderr, err := cmd.StderrPipe() 41 if err != nil { 42 t.Fatalf("could not get stderr: %+v", err) 43 } 44 stdout, err := cmd.StdoutPipe() 45 if err != nil { 46 t.Fatalf("could not get stdout: %+v", err) 47 } 48 49 err = cmd.Start() 50 if err != nil { 51 t.Fatalf("failed to start cmd: %+v", err) 52 } 53 54 show := func(label string, reader io.ReadCloser) { 55 scanner := bufio.NewScanner(reader) 56 scanner.Split(bufio.ScanLines) 57 for scanner.Scan() { 58 t.Logf("%s: %s", label, scanner.Text()) 59 } 60 } 61 go show("out", stdout) 62 go show("err", stderr) 63 64 if err := cmd.Wait(); err != nil { 65 if exiterr, ok := err.(*exec.ExitError); ok { 66 // The program has exited with an exit code != 0 67 68 // This works on both Unix and Windows. Although package 69 // syscall is generally platform dependent, WaitStatus is 70 // defined for both Unix and Windows and in both cases has 71 // an ExitStatus() method with the same signature. 72 if status, ok := exiterr.Sys().(syscall.WaitStatus); ok { 73 if status.ExitStatus() != 0 { 74 t.Fatalf("failed to generate fixture: rc=%d", status.ExitStatus()) 75 } 76 } 77 } else { 78 t.Fatalf("unable to get generate fixture result: %+v", err) 79 } 80 } 81 } 82 83 func TestParseJar(t *testing.T) { 84 tests := []struct { 85 fixture string 86 expected map[string]pkg.Package 87 ignoreExtras []string 88 }{ 89 { 90 fixture: "test-fixtures/java-builds/packages/example-jenkins-plugin.hpi", 91 ignoreExtras: []string{ 92 "Plugin-Version", // has dynamic date 93 "Built-By", // podman returns the real UID 94 "Build-Jdk", // can't guarantee the JDK used at build time 95 }, 96 expected: map[string]pkg.Package{ 97 "example-jenkins-plugin": { 98 Name: "example-jenkins-plugin", 99 Version: "1.0-SNAPSHOT", 100 PURL: "pkg:maven/io.jenkins.plugins/example-jenkins-plugin@1.0-SNAPSHOT", 101 Licenses: pkg.NewLicenseSet( 102 pkg.NewLicenseFromLocations("MIT License", file.NewLocation("test-fixtures/java-builds/packages/example-jenkins-plugin.hpi")), 103 ), 104 Language: pkg.Java, 105 Type: pkg.JenkinsPluginPkg, 106 MetadataType: pkg.JavaMetadataType, 107 Metadata: pkg.JavaMetadata{ 108 VirtualPath: "test-fixtures/java-builds/packages/example-jenkins-plugin.hpi", 109 Manifest: &pkg.JavaManifest{ 110 Main: map[string]string{ 111 "Manifest-Version": "1.0", 112 "Specification-Title": "Example Jenkins Plugin", 113 "Specification-Version": "1.0", 114 "Implementation-Title": "Example Jenkins Plugin", 115 "Implementation-Version": "1.0-SNAPSHOT", 116 // extra fields... 117 //"Archiver-Version": "Plexus Archiver", 118 "Plugin-License-Url": "https://opensource.org/licenses/MIT", 119 "Plugin-License-Name": "MIT License", 120 "Created-By": "Maven Archiver 3.6.0", 121 //"Built-By": "?", 122 //"Build-Jdk": "14.0.1", 123 "Build-Jdk-Spec": "18", 124 "Jenkins-Version": "2.204", 125 //"Minimum-Java-Version": "1.8", 126 "Plugin-Developers": "", 127 "Plugin-ScmUrl": "https://github.com/jenkinsci/plugin-pom/example-jenkins-plugin", 128 //"Extension-Name": "example-jenkins-plugin", 129 "Short-Name": "example-jenkins-plugin", 130 "Group-Id": "io.jenkins.plugins", 131 "Plugin-Dependencies": "structs:1.20", 132 //"Plugin-Version": "1.0-SNAPSHOT (private-07/09/2020 13:30-?)", 133 "Hudson-Version": "2.204", 134 "Long-Name": "Example Jenkins Plugin", 135 }, 136 }, 137 PomProperties: &pkg.PomProperties{ 138 Path: "META-INF/maven/io.jenkins.plugins/example-jenkins-plugin/pom.properties", 139 GroupID: "io.jenkins.plugins", 140 ArtifactID: "example-jenkins-plugin", 141 Version: "1.0-SNAPSHOT", 142 }, 143 }, 144 }, 145 }, 146 }, 147 { 148 fixture: "test-fixtures/java-builds/packages/example-java-app-gradle-0.1.0.jar", 149 expected: map[string]pkg.Package{ 150 "example-java-app-gradle": { 151 Name: "example-java-app-gradle", 152 Version: "0.1.0", 153 PURL: "pkg:maven/example-java-app-gradle/example-java-app-gradle@0.1.0", 154 Language: pkg.Java, 155 Type: pkg.JavaPkg, 156 MetadataType: pkg.JavaMetadataType, 157 Metadata: pkg.JavaMetadata{ 158 VirtualPath: "test-fixtures/java-builds/packages/example-java-app-gradle-0.1.0.jar", 159 Manifest: &pkg.JavaManifest{ 160 Main: map[string]string{ 161 "Manifest-Version": "1.0", 162 "Main-Class": "hello.HelloWorld", 163 }, 164 }, 165 }, 166 }, 167 "joda-time": { 168 Name: "joda-time", 169 Version: "2.2", 170 PURL: "pkg:maven/joda-time/joda-time@2.2", 171 Language: pkg.Java, 172 Type: pkg.JavaPkg, 173 MetadataType: pkg.JavaMetadataType, 174 Metadata: pkg.JavaMetadata{ 175 // ensure that nested packages with different names than that of the parent are appended as 176 // a suffix on the virtual path 177 VirtualPath: "test-fixtures/java-builds/packages/example-java-app-gradle-0.1.0.jar:joda-time", 178 PomProperties: &pkg.PomProperties{ 179 Path: "META-INF/maven/joda-time/joda-time/pom.properties", 180 GroupID: "joda-time", 181 ArtifactID: "joda-time", 182 Version: "2.2", 183 }, 184 PomProject: &pkg.PomProject{ 185 Path: "META-INF/maven/joda-time/joda-time/pom.xml", 186 GroupID: "joda-time", 187 ArtifactID: "joda-time", 188 Version: "2.2", 189 Name: "Joda time", 190 Description: "Date and time library to replace JDK date handling", 191 URL: "http://joda-time.sourceforge.net", 192 }, 193 }, 194 }, 195 }, 196 }, 197 { 198 fixture: "test-fixtures/java-builds/packages/example-java-app-maven-0.1.0.jar", 199 ignoreExtras: []string{ 200 "Build-Jdk", // can't guarantee the JDK used at build time 201 "Built-By", // podman returns the real UID 202 }, 203 expected: map[string]pkg.Package{ 204 "example-java-app-maven": { 205 Name: "example-java-app-maven", 206 Version: "0.1.0", 207 PURL: "pkg:maven/org.anchore/example-java-app-maven@0.1.0", 208 Language: pkg.Java, 209 Type: pkg.JavaPkg, 210 MetadataType: pkg.JavaMetadataType, 211 Metadata: pkg.JavaMetadata{ 212 VirtualPath: "test-fixtures/java-builds/packages/example-java-app-maven-0.1.0.jar", 213 Manifest: &pkg.JavaManifest{ 214 Main: map[string]string{ 215 "Manifest-Version": "1.0", 216 // extra fields... 217 "Archiver-Version": "Plexus Archiver", 218 "Created-By": "Apache Maven 3.8.6", 219 //"Built-By": "?", 220 //"Build-Jdk": "14.0.1", 221 "Main-Class": "hello.HelloWorld", 222 }, 223 }, 224 PomProperties: &pkg.PomProperties{ 225 Path: "META-INF/maven/org.anchore/example-java-app-maven/pom.properties", 226 GroupID: "org.anchore", 227 ArtifactID: "example-java-app-maven", 228 Version: "0.1.0", 229 }, 230 }, 231 }, 232 "joda-time": { 233 Name: "joda-time", 234 Version: "2.9.2", 235 PURL: "pkg:maven/joda-time/joda-time@2.9.2", 236 Language: pkg.Java, 237 Type: pkg.JavaPkg, 238 MetadataType: pkg.JavaMetadataType, 239 Metadata: pkg.JavaMetadata{ 240 // ensure that nested packages with different names than that of the parent are appended as 241 // a suffix on the virtual path 242 VirtualPath: "test-fixtures/java-builds/packages/example-java-app-maven-0.1.0.jar:joda-time", 243 PomProperties: &pkg.PomProperties{ 244 Path: "META-INF/maven/joda-time/joda-time/pom.properties", 245 GroupID: "joda-time", 246 ArtifactID: "joda-time", 247 Version: "2.9.2", 248 }, 249 PomProject: &pkg.PomProject{ 250 Path: "META-INF/maven/joda-time/joda-time/pom.xml", 251 GroupID: "joda-time", 252 ArtifactID: "joda-time", 253 Version: "2.9.2", 254 Name: "Joda-Time", 255 Description: "Date and time library to replace JDK date handling", 256 URL: "http://www.joda.org/joda-time/", 257 }, 258 }, 259 }, 260 }, 261 }, 262 } 263 264 for _, test := range tests { 265 t.Run(path.Base(test.fixture), func(t *testing.T) { 266 267 generateJavaBuildFixture(t, test.fixture) 268 269 fixture, err := os.Open(test.fixture) 270 require.NoError(t, err) 271 272 for k := range test.expected { 273 p := test.expected[k] 274 p.Locations.Add(file.NewLocation(test.fixture)) 275 test.expected[k] = p 276 } 277 278 parser, cleanupFn, err := newJavaArchiveParser(file.LocationReadCloser{ 279 Location: file.NewLocation(fixture.Name()), 280 ReadCloser: fixture, 281 }, false) 282 defer cleanupFn() 283 require.NoError(t, err) 284 285 actual, _, err := parser.parse() 286 require.NoError(t, err) 287 288 if len(actual) != len(test.expected) { 289 for _, a := range actual { 290 t.Log(" ", a) 291 } 292 t.Fatalf("unexpected package count: %d!=%d", len(actual), len(test.expected)) 293 } 294 295 var parent *pkg.Package 296 for _, a := range actual { 297 a := a 298 if strings.Contains(a.Name, "example-") { 299 parent = &a 300 } 301 } 302 303 if parent == nil { 304 t.Fatal("could not find the parent pkg") 305 } 306 307 for _, a := range actual { 308 if a.ID() == "" { 309 t.Fatalf("empty package ID: %+v", a) 310 } 311 312 e, ok := test.expected[a.Name] 313 if !ok { 314 t.Errorf("entry not found: %s", a.Name) 315 continue 316 } 317 318 if a.Name != parent.Name && a.Metadata.(pkg.JavaMetadata).Parent != nil && a.Metadata.(pkg.JavaMetadata).Parent.Name != parent.Name { 319 t.Errorf("mismatched parent: %+v", a.Metadata.(pkg.JavaMetadata).Parent) 320 } 321 322 // we need to compare the other fields without parent attached 323 metadata := a.Metadata.(pkg.JavaMetadata) 324 metadata.Parent = nil 325 326 // redact Digest which is computed differently between CI and local 327 if len(metadata.ArchiveDigests) > 0 { 328 metadata.ArchiveDigests = nil 329 } 330 331 // ignore select fields (only works for the main section) 332 for _, field := range test.ignoreExtras { 333 if metadata.Manifest != nil && metadata.Manifest.Main != nil { 334 delete(metadata.Manifest.Main, field) 335 } 336 } 337 338 // write censored data back 339 a.Metadata = metadata 340 341 pkgtest.AssertPackagesEqual(t, e, a) 342 } 343 }) 344 } 345 } 346 347 func TestParseNestedJar(t *testing.T) { 348 tests := []struct { 349 fixture string 350 expected []pkg.Package 351 ignoreExtras []string 352 }{ 353 { 354 fixture: "test-fixtures/java-builds/packages/spring-boot-0.0.1-SNAPSHOT.jar", 355 expected: []pkg.Package{ 356 { 357 Name: "spring-boot", 358 Version: "0.0.1-SNAPSHOT", 359 }, 360 { 361 Name: "spring-boot-starter", 362 Version: "2.2.2.RELEASE", 363 }, 364 { 365 Name: "jul-to-slf4j", 366 Version: "1.7.29", 367 }, 368 { 369 Name: "tomcat-embed-websocket", 370 Version: "9.0.29", 371 }, 372 { 373 Name: "spring-boot-starter-validation", 374 Version: "2.2.2.RELEASE", 375 }, 376 { 377 Name: "hibernate-validator", 378 Version: "6.0.18.Final", 379 }, 380 { 381 Name: "jboss-logging", 382 Version: "3.4.1.Final", 383 }, 384 { 385 Name: "spring-expression", 386 Version: "5.2.2.RELEASE", 387 }, 388 { 389 Name: "jakarta.validation-api", 390 Version: "2.0.1", 391 }, 392 { 393 Name: "spring-web", 394 Version: "5.2.2.RELEASE", 395 }, 396 { 397 Name: "spring-boot-starter-actuator", 398 Version: "2.2.2.RELEASE", 399 }, 400 { 401 Name: "log4j-api", 402 Version: "2.12.1", 403 }, 404 { 405 Name: "snakeyaml", 406 Version: "1.25", 407 }, 408 { 409 Name: "jackson-core", 410 Version: "2.10.1", 411 }, 412 { 413 Name: "jackson-datatype-jsr310", 414 Version: "2.10.1", 415 }, 416 { 417 Name: "spring-aop", 418 Version: "5.2.2.RELEASE", 419 }, 420 { 421 Name: "spring-boot-actuator-autoconfigure", 422 Version: "2.2.2.RELEASE", 423 }, 424 { 425 Name: "spring-jcl", 426 Version: "5.2.2.RELEASE", 427 }, 428 { 429 Name: "spring-boot", 430 Version: "2.2.2.RELEASE", 431 }, 432 { 433 Name: "spring-boot-starter-logging", 434 Version: "2.2.2.RELEASE", 435 }, 436 { 437 Name: "jakarta.annotation-api", 438 Version: "1.3.5", 439 }, 440 { 441 Name: "spring-webmvc", 442 Version: "5.2.2.RELEASE", 443 }, 444 { 445 Name: "HdrHistogram", 446 Version: "2.1.11", 447 }, 448 { 449 Name: "spring-boot-starter-web", 450 Version: "2.2.2.RELEASE", 451 }, 452 { 453 Name: "logback-classic", 454 Version: "1.2.3", 455 }, 456 { 457 Name: "log4j-to-slf4j", 458 Version: "2.12.1", 459 }, 460 { 461 Name: "spring-boot-starter-json", 462 Version: "2.2.2.RELEASE", 463 }, 464 { 465 Name: "jackson-databind", 466 Version: "2.10.1", 467 }, 468 { 469 Name: "jackson-module-parameter-names", 470 Version: "2.10.1", 471 }, 472 { 473 Name: "LatencyUtils", 474 Version: "2.0.3", 475 }, 476 { 477 Name: "spring-boot-autoconfigure", 478 Version: "2.2.2.RELEASE", 479 }, 480 { 481 Name: "jackson-datatype-jdk8", 482 Version: "2.10.1", 483 }, 484 { 485 Name: "tomcat-embed-core", 486 Version: "9.0.29", 487 }, 488 { 489 Name: "tomcat-embed-el", 490 Version: "9.0.29", 491 }, 492 { 493 Name: "spring-beans", 494 Version: "5.2.2.RELEASE", 495 }, 496 { 497 Name: "spring-boot-actuator", 498 Version: "2.2.2.RELEASE", 499 }, 500 { 501 Name: "slf4j-api", 502 Version: "1.7.29", 503 }, 504 { 505 Name: "spring-core", 506 Version: "5.2.2.RELEASE", 507 }, 508 { 509 Name: "logback-core", 510 Version: "1.2.3", 511 }, 512 { 513 Name: "micrometer-core", 514 Version: "1.3.1", 515 }, 516 { 517 Name: "pcollections", 518 Version: "3.1.0", 519 }, 520 { 521 Name: "jackson-annotations", 522 Version: "2.10.1", 523 }, 524 { 525 Name: "spring-boot-starter-tomcat", 526 Version: "2.2.2.RELEASE", 527 }, 528 { 529 Name: "classmate", 530 Version: "1.5.1", 531 }, 532 { 533 Name: "spring-context", 534 Version: "5.2.2.RELEASE", 535 }, 536 }, 537 }, 538 } 539 540 for _, test := range tests { 541 t.Run(test.fixture, func(t *testing.T) { 542 543 generateJavaBuildFixture(t, test.fixture) 544 545 fixture, err := os.Open(test.fixture) 546 require.NoError(t, err) 547 548 actual, _, err := parseJavaArchive(nil, nil, file.LocationReadCloser{ 549 Location: file.NewLocation(fixture.Name()), 550 ReadCloser: fixture, 551 }) 552 require.NoError(t, err) 553 554 expectedNameVersionPairSet := internal.NewStringSet() 555 556 makeKey := func(p *pkg.Package) string { 557 if p == nil { 558 t.Fatal("cannot make key for nil pkg") 559 } 560 return fmt.Sprintf("%s|%s", p.Name, p.Version) 561 } 562 563 for _, e := range test.expected { 564 expectedNameVersionPairSet.Add(makeKey(&e)) 565 } 566 567 actualNameVersionPairSet := internal.NewStringSet() 568 for _, a := range actual { 569 a := a 570 key := makeKey(&a) 571 actualNameVersionPairSet.Add(key) 572 if !expectedNameVersionPairSet.Contains(key) { 573 t.Errorf("extra package: %s", a) 574 } 575 } 576 577 for _, key := range expectedNameVersionPairSet.ToSlice() { 578 if !actualNameVersionPairSet.Contains(key) { 579 t.Errorf("missing package: %s", key) 580 } 581 } 582 583 if len(actual) != len(expectedNameVersionPairSet) { 584 t.Fatalf("unexpected package count: %d!=%d", len(actual), len(expectedNameVersionPairSet)) 585 } 586 587 for _, a := range actual { 588 a := a 589 actualKey := makeKey(&a) 590 591 metadata := a.Metadata.(pkg.JavaMetadata) 592 if actualKey == "spring-boot|0.0.1-SNAPSHOT" { 593 if metadata.Parent != nil { 594 t.Errorf("expected no parent for root pkg, got %q", makeKey(metadata.Parent)) 595 } 596 } else { 597 if metadata.Parent == nil { 598 t.Errorf("unassigned error for pkg=%q", actualKey) 599 } else if makeKey(metadata.Parent) != "spring-boot|0.0.1-SNAPSHOT" { 600 // NB: this is a hard-coded condition to simplify the test harness to account for https://github.com/micrometer-metrics/micrometer/issues/1785 601 if a.Name == "pcollections" { 602 if metadata.Parent.Name != "micrometer-core" { 603 t.Errorf("nested 'pcollections' pkg has wrong parent: %q", metadata.Parent.Name) 604 } 605 } else { 606 t.Errorf("bad parent for pkg=%q parent=%q", actualKey, makeKey(metadata.Parent)) 607 } 608 } 609 } 610 } 611 }) 612 } 613 } 614 615 func Test_newPackageFromMavenData(t *testing.T) { 616 virtualPath := "given/virtual/path" 617 tests := []struct { 618 name string 619 props pkg.PomProperties 620 project *pkg.PomProject 621 parent *pkg.Package 622 expectedParent pkg.Package 623 expectedPackage *pkg.Package 624 }{ 625 { 626 name: "go case: get a single package from pom properties", 627 props: pkg.PomProperties{ 628 Name: "some-name", 629 GroupID: "some-group-id", 630 ArtifactID: "some-artifact-id", 631 Version: "1.0", 632 }, 633 parent: &pkg.Package{ 634 Name: "some-parent-name", 635 Version: "2.0", 636 Metadata: pkg.JavaMetadata{ 637 VirtualPath: "some-parent-virtual-path", 638 Manifest: nil, 639 PomProperties: nil, 640 Parent: nil, 641 }, 642 }, 643 // note: the SAME as the original parent values 644 expectedParent: pkg.Package{ 645 Name: "some-parent-name", 646 Version: "2.0", 647 Metadata: pkg.JavaMetadata{ 648 VirtualPath: "some-parent-virtual-path", 649 Manifest: nil, 650 PomProperties: nil, 651 Parent: nil, 652 }, 653 }, 654 expectedPackage: &pkg.Package{ 655 Name: "some-artifact-id", 656 Version: "1.0", 657 Language: pkg.Java, 658 Type: pkg.JavaPkg, 659 MetadataType: pkg.JavaMetadataType, 660 Metadata: pkg.JavaMetadata{ 661 VirtualPath: virtualPath + ":" + "some-artifact-id", 662 PomProperties: &pkg.PomProperties{ 663 Name: "some-name", 664 GroupID: "some-group-id", 665 ArtifactID: "some-artifact-id", 666 Version: "1.0", 667 }, 668 Parent: &pkg.Package{ 669 Name: "some-parent-name", 670 Version: "2.0", 671 Metadata: pkg.JavaMetadata{ 672 VirtualPath: "some-parent-virtual-path", 673 Manifest: nil, 674 PomProperties: nil, 675 Parent: nil, 676 }, 677 }, 678 }, 679 }, 680 }, 681 { 682 name: "get a single package from pom properties + project", 683 props: pkg.PomProperties{ 684 Name: "some-name", 685 GroupID: "some-group-id", 686 ArtifactID: "some-artifact-id", 687 Version: "1.0", 688 }, 689 project: &pkg.PomProject{ 690 Parent: &pkg.PomParent{ 691 GroupID: "some-parent-group-id", 692 ArtifactID: "some-parent-artifact-id", 693 Version: "1.0-parent", 694 }, 695 Name: "some-name", 696 GroupID: "some-group-id", 697 ArtifactID: "some-artifact-id", 698 Version: "1.0", 699 Description: "desc", 700 URL: "aweso.me", 701 }, 702 parent: &pkg.Package{ 703 Name: "some-parent-name", 704 Version: "2.0", 705 Metadata: pkg.JavaMetadata{ 706 VirtualPath: "some-parent-virtual-path", 707 Manifest: nil, 708 PomProperties: nil, 709 Parent: nil, 710 }, 711 }, 712 // note: the SAME as the original parent values 713 expectedParent: pkg.Package{ 714 Name: "some-parent-name", 715 Version: "2.0", 716 Metadata: pkg.JavaMetadata{ 717 VirtualPath: "some-parent-virtual-path", 718 Manifest: nil, 719 PomProperties: nil, 720 Parent: nil, 721 }, 722 }, 723 expectedPackage: &pkg.Package{ 724 Name: "some-artifact-id", 725 Version: "1.0", 726 Language: pkg.Java, 727 Type: pkg.JavaPkg, 728 MetadataType: pkg.JavaMetadataType, 729 Metadata: pkg.JavaMetadata{ 730 VirtualPath: virtualPath + ":" + "some-artifact-id", 731 PomProperties: &pkg.PomProperties{ 732 Name: "some-name", 733 GroupID: "some-group-id", 734 ArtifactID: "some-artifact-id", 735 Version: "1.0", 736 }, 737 PomProject: &pkg.PomProject{ 738 Parent: &pkg.PomParent{ 739 GroupID: "some-parent-group-id", 740 ArtifactID: "some-parent-artifact-id", 741 Version: "1.0-parent", 742 }, 743 Name: "some-name", 744 GroupID: "some-group-id", 745 ArtifactID: "some-artifact-id", 746 Version: "1.0", 747 Description: "desc", 748 URL: "aweso.me", 749 }, 750 Parent: &pkg.Package{ 751 Name: "some-parent-name", 752 Version: "2.0", 753 Metadata: pkg.JavaMetadata{ 754 VirtualPath: "some-parent-virtual-path", 755 Manifest: nil, 756 PomProperties: nil, 757 Parent: nil, 758 }, 759 }, 760 }, 761 }, 762 }, 763 { 764 name: "single package from pom properties that's a Jenkins plugin", 765 props: pkg.PomProperties{ 766 Name: "some-name", 767 GroupID: "com.cloudbees.jenkins.plugins", 768 ArtifactID: "some-artifact-id", 769 Version: "1.0", 770 }, 771 parent: &pkg.Package{ 772 Name: "some-parent-name", 773 Version: "2.0", 774 Metadata: pkg.JavaMetadata{ 775 VirtualPath: "some-parent-virtual-path", 776 Manifest: nil, 777 PomProperties: nil, 778 Parent: nil, 779 }, 780 }, 781 // note: the SAME as the original parent values 782 expectedParent: pkg.Package{ 783 Name: "some-parent-name", 784 Version: "2.0", 785 Metadata: pkg.JavaMetadata{ 786 VirtualPath: "some-parent-virtual-path", 787 Manifest: nil, 788 PomProperties: nil, 789 Parent: nil, 790 }, 791 }, 792 expectedPackage: &pkg.Package{ 793 Name: "some-artifact-id", 794 Version: "1.0", 795 Language: pkg.Java, 796 Type: pkg.JenkinsPluginPkg, 797 MetadataType: pkg.JavaMetadataType, 798 Metadata: pkg.JavaMetadata{ 799 VirtualPath: virtualPath + ":" + "some-artifact-id", 800 PomProperties: &pkg.PomProperties{ 801 Name: "some-name", 802 GroupID: "com.cloudbees.jenkins.plugins", 803 ArtifactID: "some-artifact-id", 804 Version: "1.0", 805 }, 806 Parent: &pkg.Package{ 807 Name: "some-parent-name", 808 Version: "2.0", 809 Metadata: pkg.JavaMetadata{ 810 VirtualPath: "some-parent-virtual-path", 811 Manifest: nil, 812 PomProperties: nil, 813 Parent: nil, 814 }, 815 }, 816 }, 817 }, 818 }, 819 { 820 name: "child matches parent by key", 821 props: pkg.PomProperties{ 822 Name: "some-name", 823 GroupID: "some-group-id", 824 ArtifactID: "some-parent-name", // note: matches parent package 825 Version: "2.0", // note: matches parent package 826 }, 827 parent: &pkg.Package{ 828 Name: "some-parent-name", 829 Version: "2.0", 830 Type: pkg.JavaPkg, 831 Metadata: pkg.JavaMetadata{ 832 VirtualPath: "some-parent-virtual-path", 833 Manifest: nil, 834 PomProperties: nil, 835 Parent: nil, 836 }, 837 }, 838 // note: the SAME as the original parent values 839 expectedParent: pkg.Package{ 840 Name: "some-parent-name", 841 Version: "2.0", 842 Type: pkg.JavaPkg, 843 Metadata: pkg.JavaMetadata{ 844 VirtualPath: "some-parent-virtual-path", 845 Manifest: nil, 846 // note: we attach the discovered pom properties data 847 PomProperties: &pkg.PomProperties{ 848 Name: "some-name", 849 GroupID: "some-group-id", 850 ArtifactID: "some-parent-name", // note: matches parent package 851 Version: "2.0", // note: matches parent package 852 }, 853 Parent: nil, 854 }, 855 }, 856 expectedPackage: nil, 857 }, 858 { 859 name: "child matches parent by key and is Jenkins plugin", 860 props: pkg.PomProperties{ 861 Name: "some-name", 862 GroupID: "com.cloudbees.jenkins.plugins", 863 ArtifactID: "some-parent-name", // note: matches parent package 864 Version: "2.0", // note: matches parent package 865 }, 866 parent: &pkg.Package{ 867 Name: "some-parent-name", 868 Version: "2.0", 869 Type: pkg.JavaPkg, 870 Metadata: pkg.JavaMetadata{ 871 VirtualPath: "some-parent-virtual-path", 872 Manifest: nil, 873 PomProperties: nil, 874 Parent: nil, 875 }, 876 }, 877 expectedParent: pkg.Package{ 878 Name: "some-parent-name", 879 Version: "2.0", 880 Type: pkg.JenkinsPluginPkg, 881 Metadata: pkg.JavaMetadata{ 882 VirtualPath: "some-parent-virtual-path", 883 Manifest: nil, 884 // note: we attach the discovered pom properties data 885 PomProperties: &pkg.PomProperties{ 886 Name: "some-name", 887 GroupID: "com.cloudbees.jenkins.plugins", 888 ArtifactID: "some-parent-name", // note: matches parent package 889 Version: "2.0", // note: matches parent package 890 }, 891 Parent: nil, 892 }, 893 }, 894 expectedPackage: nil, 895 }, 896 { 897 name: "child matches parent by virtual path -- override name and version", 898 props: pkg.PomProperties{ 899 Name: "some-name", 900 GroupID: "some-group-id", 901 ArtifactID: "some-parent-name", // note: DOES NOT match parent package 902 Version: "3.0", // note: DOES NOT match parent package 903 }, 904 parent: &pkg.Package{ 905 Name: "", // note: empty, so should not be matched on 906 Version: "", // note: empty, so should not be matched on 907 Type: pkg.JavaPkg, 908 Metadata: pkg.JavaMetadata{ 909 VirtualPath: virtualPath, // note: matching virtual path 910 Manifest: nil, 911 PomProperties: nil, 912 Parent: nil, 913 }, 914 }, 915 expectedParent: pkg.Package{ 916 Name: "some-parent-name", 917 Version: "3.0", 918 Type: pkg.JavaPkg, 919 Metadata: pkg.JavaMetadata{ 920 VirtualPath: virtualPath, 921 Manifest: nil, 922 // note: we attach the discovered pom properties data 923 PomProperties: &pkg.PomProperties{ 924 Name: "some-name", 925 GroupID: "some-group-id", 926 ArtifactID: "some-parent-name", 927 Version: "3.0", 928 }, 929 Parent: nil, 930 }, 931 }, 932 expectedPackage: nil, 933 }, 934 { 935 name: "child matches parent by artifact id", 936 props: pkg.PomProperties{ 937 Name: "some-name", 938 GroupID: "some-group-id", 939 ArtifactID: "some-parent-name", // note: matches parent package 940 Version: "NOT_THE_PARENT_VERSION", // note: DOES NOT match parent package 941 }, 942 parent: &pkg.Package{ 943 Name: "some-parent-name", 944 Version: "2.0", 945 Type: pkg.JavaPkg, 946 Metadata: pkg.JavaMetadata{ 947 VirtualPath: virtualPath + ":NEW_VIRTUAL_PATH", // note: DOES NOT match the existing virtual path 948 Manifest: nil, 949 PomProperties: nil, 950 Parent: nil, 951 }, 952 }, 953 // note: the SAME as the original parent values 954 expectedParent: pkg.Package{ 955 Name: "some-parent-name", 956 Version: "NOT_THE_PARENT_VERSION", // note: the version is updated from pom properties 957 Type: pkg.JavaPkg, 958 Metadata: pkg.JavaMetadata{ 959 VirtualPath: virtualPath + ":NEW_VIRTUAL_PATH", 960 Manifest: nil, 961 // note: we attach the discovered pom properties data 962 PomProperties: &pkg.PomProperties{ 963 Name: "some-name", 964 GroupID: "some-group-id", 965 ArtifactID: "some-parent-name", 966 Version: "NOT_THE_PARENT_VERSION", 967 }, 968 Parent: nil, 969 }, 970 }, 971 expectedPackage: nil, 972 }, 973 } 974 975 for _, test := range tests { 976 t.Run(test.name, func(t *testing.T) { 977 locations := file.NewLocationSet(file.NewLocation(virtualPath)) 978 if test.expectedPackage != nil { 979 test.expectedPackage.Locations = locations 980 if test.expectedPackage.Metadata.(pkg.JavaMetadata).Parent != nil { 981 test.expectedPackage.Metadata.(pkg.JavaMetadata).Parent.Locations = locations 982 } 983 } 984 if test.parent != nil { 985 test.parent.Locations = locations 986 } 987 test.expectedParent.Locations = locations 988 989 actualPackage := newPackageFromMavenData(test.props, test.project, test.parent, file.NewLocation(virtualPath)) 990 if test.expectedPackage == nil { 991 require.Nil(t, actualPackage) 992 } else { 993 pkgtest.AssertPackagesEqual(t, *test.expectedPackage, *actualPackage) 994 } 995 996 pkgtest.AssertPackagesEqual(t, test.expectedParent, *test.parent) 997 }) 998 } 999 }