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