github.com/anchore/syft@v1.38.2/syft/pkg/cataloger/java/parse_pom_xml_test.go (about)

     1  package java
     2  
     3  import (
     4  	"context"
     5  	"os"
     6  	"testing"
     7  
     8  	"github.com/stretchr/testify/assert"
     9  	"github.com/stretchr/testify/require"
    10  
    11  	"github.com/anchore/syft/syft/artifact"
    12  	"github.com/anchore/syft/syft/cataloging"
    13  	"github.com/anchore/syft/syft/file"
    14  	"github.com/anchore/syft/syft/license"
    15  	"github.com/anchore/syft/syft/pkg"
    16  	"github.com/anchore/syft/syft/pkg/cataloger/internal/pkgtest"
    17  	"github.com/anchore/syft/syft/pkg/cataloger/java/internal/maven"
    18  	maventest "github.com/anchore/syft/syft/pkg/cataloger/java/internal/maven/test"
    19  	"github.com/anchore/syft/syft/source"
    20  	"github.com/anchore/syft/syft/source/directorysource"
    21  )
    22  
    23  func Test_parsePomXML(t *testing.T) {
    24  	pomLocation := file.NewLocationSet(file.NewLocation("pom.xml"))
    25  
    26  	exampleJavaAppMaven := pkg.Package{
    27  		Name:      "example-java-app-maven",
    28  		Version:   "0.1.0",
    29  		PURL:      "pkg:maven/org.anchore/example-java-app-maven@0.1.0",
    30  		Language:  pkg.Java,
    31  		Type:      pkg.JavaPkg,
    32  		FoundBy:   pomCatalogerName,
    33  		Locations: pomLocation,
    34  		Metadata: pkg.JavaArchive{
    35  			PomProject: &pkg.JavaPomProject{
    36  				GroupID:    "org.anchore",
    37  				ArtifactID: "example-java-app-maven",
    38  				Version:    "0.1.0",
    39  			},
    40  		},
    41  	}
    42  	finalizePackage(&exampleJavaAppMaven)
    43  
    44  	jodaTime := pkg.Package{
    45  		Name:      "joda-time",
    46  		Version:   "2.9.2",
    47  		PURL:      "pkg:maven/com.joda/joda-time@2.9.2",
    48  		Language:  pkg.Java,
    49  		Type:      pkg.JavaPkg,
    50  		FoundBy:   pomCatalogerName,
    51  		Locations: pomLocation,
    52  		Metadata: pkg.JavaArchive{
    53  			PomProperties: &pkg.JavaPomProperties{
    54  				GroupID:    "com.joda",
    55  				ArtifactID: "joda-time",
    56  			},
    57  		},
    58  	}
    59  	finalizePackage(&jodaTime)
    60  
    61  	junit := pkg.Package{
    62  		Name:      "junit",
    63  		Version:   "4.12",
    64  		PURL:      "pkg:maven/junit/junit@4.12",
    65  		Language:  pkg.Java,
    66  		Type:      pkg.JavaPkg,
    67  		FoundBy:   pomCatalogerName,
    68  		Locations: pomLocation,
    69  		Metadata: pkg.JavaArchive{
    70  			PomProperties: &pkg.JavaPomProperties{
    71  				GroupID:    "junit",
    72  				ArtifactID: "junit",
    73  				Scope:      "test",
    74  			},
    75  		},
    76  	}
    77  	finalizePackage(&junit)
    78  
    79  	tests := []struct {
    80  		dir                   string
    81  		expected              []pkg.Package
    82  		expectedRelationships []artifact.Relationship
    83  	}{
    84  		{
    85  			dir: "test-fixtures/pom/example-java-app-maven",
    86  			expected: []pkg.Package{
    87  				exampleJavaAppMaven,
    88  				jodaTime,
    89  				junit,
    90  			},
    91  			expectedRelationships: []artifact.Relationship{
    92  				{
    93  					From: jodaTime,
    94  					To:   exampleJavaAppMaven,
    95  					Type: artifact.DependencyOfRelationship,
    96  				},
    97  				{
    98  					From: junit,
    99  					To:   exampleJavaAppMaven,
   100  					Type: artifact.DependencyOfRelationship,
   101  				},
   102  			},
   103  		},
   104  	}
   105  
   106  	for _, test := range tests {
   107  		t.Run(test.dir, func(t *testing.T) {
   108  			for i := range test.expected {
   109  				test.expected[i].Locations.Add(file.NewLocation("pom.xml"))
   110  			}
   111  
   112  			cat := NewPomCataloger(ArchiveCatalogerConfig{
   113  				ArchiveSearchConfig: cataloging.ArchiveSearchConfig{
   114  					IncludeIndexedArchives:   true,
   115  					IncludeUnindexedArchives: true,
   116  				},
   117  			})
   118  
   119  			pkgtest.TestCataloger(t, test.dir, cat, test.expected, test.expectedRelationships)
   120  		})
   121  	}
   122  }
   123  
   124  func Test_parseCommonsTextPomXMLProject(t *testing.T) {
   125  	mavenLocalRepoDir := "internal/maven/test-fixtures/maven-repo"
   126  	mavenBaseURL := maventest.MockRepo(t, "internal/maven/test-fixtures/maven-repo")
   127  
   128  	tests := []struct {
   129  		name     string
   130  		dir      string
   131  		config   ArchiveCatalogerConfig
   132  		expected expected
   133  	}{
   134  		{
   135  			name: "no resolution",
   136  			dir:  "test-fixtures/pom/commons-text-1.10.0",
   137  			config: ArchiveCatalogerConfig{
   138  				UseNetwork:              false,
   139  				UseMavenLocalRepository: false,
   140  			},
   141  			expected: getCommonsTextExpectedPackages(false),
   142  		},
   143  		{
   144  			name: "use network",
   145  			dir:  "test-fixtures/pom/commons-text-1.10.0",
   146  			config: ArchiveCatalogerConfig{
   147  				UseNetwork:              true,
   148  				MavenBaseURL:            mavenBaseURL,
   149  				UseMavenLocalRepository: false,
   150  			},
   151  			expected: getCommonsTextExpectedPackages(true),
   152  		},
   153  		{
   154  			name: "use local repository",
   155  			dir:  "test-fixtures/pom/commons-text-1.10.0",
   156  			config: ArchiveCatalogerConfig{
   157  				UseNetwork:              false,
   158  				UseMavenLocalRepository: true,
   159  				MavenLocalRepositoryDir: mavenLocalRepoDir,
   160  			},
   161  			expected: getCommonsTextExpectedPackages(true),
   162  		},
   163  		{
   164  			name: "transitive dependencies",
   165  			dir:  "test-fixtures/pom/transitive-top-level",
   166  			config: ArchiveCatalogerConfig{
   167  				UseNetwork:                    false,
   168  				UseMavenLocalRepository:       true,
   169  				MavenLocalRepositoryDir:       mavenLocalRepoDir,
   170  				ResolveTransitiveDependencies: true,
   171  			},
   172  			expected: expectedTransientPackageData(),
   173  		},
   174  	}
   175  
   176  	for _, test := range tests {
   177  		t.Run(test.name, func(t *testing.T) {
   178  			cat := NewPomCataloger(ArchiveCatalogerConfig{
   179  				ArchiveSearchConfig: cataloging.ArchiveSearchConfig{
   180  					IncludeIndexedArchives:   true,
   181  					IncludeUnindexedArchives: true,
   182  				},
   183  				UseNetwork:                    test.config.UseNetwork,
   184  				MavenBaseURL:                  test.config.MavenBaseURL,
   185  				UseMavenLocalRepository:       test.config.UseMavenLocalRepository,
   186  				MavenLocalRepositoryDir:       test.config.MavenLocalRepositoryDir,
   187  				ResolveTransitiveDependencies: test.config.ResolveTransitiveDependencies,
   188  			})
   189  			pkgtest.TestCataloger(t, test.dir, cat, test.expected.packages, test.expected.relationships)
   190  		})
   191  	}
   192  }
   193  
   194  func Test_parsePomXMLProject(t *testing.T) {
   195  	// TODO: ideally we would have the path to the contained pom.xml, not the jar
   196  	jarLocation := file.NewLocation("path/to/archive.jar")
   197  	ctx := context.TODO()
   198  	tests := []struct {
   199  		name     string
   200  		project  *pkg.JavaPomProject
   201  		licenses []pkg.License
   202  	}{
   203  		{
   204  			name: "no license info",
   205  			project: &pkg.JavaPomProject{
   206  				Path: "test-fixtures/pom/commons-codec.pom.xml",
   207  				Parent: &pkg.JavaPomParent{
   208  					GroupID:    "org.apache.commons",
   209  					ArtifactID: "commons-parent",
   210  					Version:    "42",
   211  				},
   212  				GroupID:     "commons-codec",
   213  				ArtifactID:  "commons-codec",
   214  				Version:     "1.11",
   215  				Name:        "Apache Commons Codec",
   216  				Description: "The Apache Commons Codec package contains simple encoder and decoders for various formats such as Base64 and Hexadecimal.  In addition to these widely used encoders and decoders, the codec package also maintains a collection of phonetic encoding utilities.",
   217  				URL:         "http://commons.apache.org/proper/commons-codec/",
   218  			},
   219  		},
   220  		{
   221  			name: "with license data",
   222  			project: &pkg.JavaPomProject{
   223  				Path: "test-fixtures/pom/neo4j-license-maven-plugin.pom.xml",
   224  				Parent: &pkg.JavaPomParent{
   225  					GroupID:    "org.sonatype.oss",
   226  					ArtifactID: "oss-parent",
   227  					Version:    "7",
   228  				},
   229  				GroupID:     "org.neo4j.build.plugins",
   230  				ArtifactID:  "license-maven-plugin",
   231  				Version:     "4-SNAPSHOT",
   232  				Name:        "license-maven-plugin",
   233  				Description: "Maven 2 plugin to check and update license headers in source files",
   234  				URL:         "http://components.neo4j.org/license-maven-plugin/4-SNAPSHOT",
   235  			},
   236  			licenses: []pkg.License{
   237  				{
   238  					Value:          "The Apache Software License, Version 2.0",
   239  					SPDXExpression: "", // TODO: ideally we would parse this title to get Apache-2.0 (created issue #2210 https://github.com/anchore/syft/issues/2210)
   240  					Type:           license.Declared,
   241  					URLs:           []string{"http://www.apache.org/licenses/LICENSE-2.0.txt"},
   242  					Locations:      file.NewLocationSet(jarLocation),
   243  				},
   244  				{
   245  					Value:          "MIT",
   246  					SPDXExpression: "MIT",
   247  					Type:           license.Declared,
   248  					Locations:      file.NewLocationSet(jarLocation),
   249  				},
   250  				{
   251  					Type:      license.Declared,
   252  					URLs:      []string{"https://opensource.org/license/unlicense/"},
   253  					Locations: file.NewLocationSet(jarLocation),
   254  				},
   255  			},
   256  		},
   257  	}
   258  
   259  	for _, test := range tests {
   260  		t.Run(test.name, func(t *testing.T) {
   261  			fixture, err := os.Open(test.project.Path)
   262  			assert.NoError(t, err)
   263  			r := maven.NewResolver(nil, maven.Config{})
   264  
   265  			pom, err := maven.ParsePomXML(fixture)
   266  			require.NoError(t, err)
   267  
   268  			actual := newPomProject(context.Background(), r, fixture.Name(), pom)
   269  			assert.NoError(t, err)
   270  			assert.Equal(t, test.project, actual)
   271  
   272  			licenses, err := r.ResolveLicenses(context.Background(), pom)
   273  			//assert.NoError(t, err)
   274  			assert.Equal(t, test.licenses, toPkgLicenses(ctx, &jarLocation, licenses))
   275  		})
   276  	}
   277  }
   278  
   279  func Test_pomParent(t *testing.T) {
   280  	tests := []struct {
   281  		name     string
   282  		input    *maven.Parent
   283  		expected *pkg.JavaPomParent
   284  	}{
   285  		{
   286  			name: "only group ID",
   287  			input: &maven.Parent{
   288  				GroupID: ptr("org.something"),
   289  			},
   290  			expected: &pkg.JavaPomParent{
   291  				GroupID: "org.something",
   292  			},
   293  		},
   294  		{
   295  			name: "only artifact ID",
   296  			input: &maven.Parent{
   297  				ArtifactID: ptr("something"),
   298  			},
   299  			expected: &pkg.JavaPomParent{
   300  				ArtifactID: "something",
   301  			},
   302  		},
   303  		{
   304  			name: "only Version",
   305  			input: &maven.Parent{
   306  				Version: ptr("something"),
   307  			},
   308  			expected: &pkg.JavaPomParent{
   309  				Version: "something",
   310  			},
   311  		},
   312  		{
   313  			name:     "nil",
   314  			input:    nil,
   315  			expected: nil,
   316  		},
   317  		{
   318  			name:     "empty",
   319  			input:    &maven.Parent{},
   320  			expected: nil,
   321  		},
   322  		{
   323  			name: "unused field",
   324  			input: &maven.Parent{
   325  				RelativePath: ptr("something"),
   326  			},
   327  			expected: nil,
   328  		},
   329  	}
   330  
   331  	for _, test := range tests {
   332  		t.Run(test.name, func(t *testing.T) {
   333  			r := maven.NewResolver(nil, maven.DefaultConfig())
   334  			assert.Equal(t, test.expected, pomParent(context.Background(), r, &maven.Project{Parent: test.input}))
   335  		})
   336  	}
   337  }
   338  
   339  func Test_cleanDescription(t *testing.T) {
   340  	tests := []struct {
   341  		name     string
   342  		input    string
   343  		expected string
   344  	}{
   345  		{
   346  			name: "indent + multiline",
   347  			input: `        The Apache Commons Codec package contains simple encoder and decoders for
   348          various formats such as Base64 and Hexadecimal.  In addition to these
   349          widely used encoders and decoders, the codec package also maintains a
   350          collection of phonetic encoding utilities.`,
   351  			expected: "The Apache Commons Codec package contains simple encoder and decoders for various formats such as Base64 and Hexadecimal.  In addition to these widely used encoders and decoders, the codec package also maintains a collection of phonetic encoding utilities.",
   352  		},
   353  	}
   354  
   355  	for _, test := range tests {
   356  		t.Run(test.name, func(t *testing.T) {
   357  			assert.Equal(t, test.expected, cleanDescription(test.input))
   358  		})
   359  	}
   360  }
   361  
   362  func Test_resolveLicenses(t *testing.T) {
   363  	mavenURL := maventest.MockRepo(t, "internal/maven/test-fixtures/maven-repo")
   364  	localM2 := "internal/maven/test-fixtures/maven-repo"
   365  	localDir := "internal/maven/test-fixtures/local"
   366  	containingDir := "internal/maven/test-fixtures/local/contains-child-1"
   367  
   368  	expectedLicenses := []pkg.License{
   369  		{
   370  			Value:          "Eclipse Public License v2.0",
   371  			SPDXExpression: "",
   372  			Type:           license.Declared,
   373  			URLs:           []string{"https://www.eclipse.org/legal/epl-v20.html"},
   374  		},
   375  	}
   376  
   377  	tests := []struct {
   378  		name     string
   379  		scanDir  string
   380  		cfg      ArchiveCatalogerConfig
   381  		expected []pkg.License
   382  	}{
   383  		{
   384  			name:    "local no resolution",
   385  			scanDir: containingDir,
   386  			cfg: ArchiveCatalogerConfig{
   387  				UseMavenLocalRepository: false,
   388  				UseNetwork:              false,
   389  				MavenLocalRepositoryDir: "",
   390  				MavenBaseURL:            "",
   391  			},
   392  			expected: nil,
   393  		},
   394  		{
   395  			name:    "local all poms",
   396  			scanDir: localDir,
   397  			cfg: ArchiveCatalogerConfig{
   398  				UseMavenLocalRepository: false,
   399  				UseNetwork:              false,
   400  			},
   401  			expected: expectedLicenses,
   402  		},
   403  		{
   404  			name:    "local m2 cache",
   405  			scanDir: containingDir,
   406  			cfg: ArchiveCatalogerConfig{
   407  				UseMavenLocalRepository: true,
   408  				MavenLocalRepositoryDir: localM2,
   409  				UseNetwork:              false,
   410  				MavenBaseURL:            "",
   411  			},
   412  			expected: expectedLicenses,
   413  		},
   414  		{
   415  			name:    "local with network",
   416  			scanDir: containingDir,
   417  			cfg: ArchiveCatalogerConfig{
   418  				UseMavenLocalRepository: false,
   419  				UseNetwork:              true,
   420  				MavenBaseURL:            mavenURL,
   421  			},
   422  			expected: expectedLicenses,
   423  		},
   424  	}
   425  
   426  	for _, test := range tests {
   427  		t.Run(test.name, func(t *testing.T) {
   428  			cat := NewPomCataloger(test.cfg)
   429  
   430  			ds, err := directorysource.NewFromPath(test.scanDir)
   431  			require.NoError(t, err)
   432  
   433  			fr, err := ds.FileResolver(source.AllLayersScope)
   434  			require.NoError(t, err)
   435  
   436  			ctx := context.TODO()
   437  			pkgs, _, err := cat.Catalog(ctx, fr)
   438  			require.NoError(t, err)
   439  
   440  			var child1 pkg.Package
   441  			for _, p := range pkgs {
   442  				if p.Name == "child-one" {
   443  					child1 = p
   444  					break
   445  				}
   446  			}
   447  			require.Equal(t, "child-one", child1.Name)
   448  
   449  			got := child1.Licenses.ToSlice()
   450  			for i := 0; i < len(got); i++ {
   451  				// ignore locations, just check license text
   452  				(&got[i]).Locations = file.LocationSet{}
   453  			}
   454  			require.ElementsMatch(t, test.expected, got)
   455  		})
   456  	}
   457  }
   458  
   459  func Test_corruptPomXml(t *testing.T) {
   460  	c := NewPomCataloger(DefaultArchiveCatalogerConfig())
   461  	pkgtest.NewCatalogTester().
   462  		FromDirectory(t, "test-fixtures/corrupt").
   463  		WithError().
   464  		TestCataloger(t, c)
   465  }
   466  
   467  type expected struct {
   468  	packages      []pkg.Package
   469  	relationships []artifact.Relationship
   470  }
   471  
   472  func getCommonsTextExpectedPackages(resolved bool) expected {
   473  	pomXmlLocation := file.NewLocationSet(file.NewLocation("pom.xml"))
   474  
   475  	commonsText := pkg.Package{
   476  		Name:     "commons-text",
   477  		Version:  "1.10.0",
   478  		PURL:     "pkg:maven/org.apache.commons/commons-text@1.10.0",
   479  		Language: pkg.Java,
   480  		Type:     pkg.JavaPkg,
   481  		FoundBy:  pomCatalogerName,
   482  		Metadata: pkg.JavaArchive{
   483  			PomProject: &pkg.JavaPomProject{
   484  				Parent: &pkg.JavaPomParent{
   485  					GroupID:    "org.apache.commons",
   486  					ArtifactID: "commons-parent",
   487  					Version:    "54",
   488  				},
   489  				GroupID:     "org.apache.commons",
   490  				ArtifactID:  "commons-text",
   491  				Version:     "1.10.0",
   492  				Name:        "Apache Commons Text",
   493  				Description: "Apache Commons Text is a library focused on algorithms working on strings.",
   494  				URL:         "https://commons.apache.org/proper/commons-text",
   495  			},
   496  		},
   497  	}
   498  
   499  	commonsLang3 := pkg.Package{
   500  		Name:     "commons-lang3",
   501  		Version:  "3.12.0",
   502  		PURL:     "pkg:maven/org.apache.commons/commons-lang3@3.12.0",
   503  		Language: pkg.Java,
   504  		Type:     pkg.JavaPkg,
   505  		FoundBy:  pomCatalogerName,
   506  		Metadata: pkg.JavaArchive{
   507  			PomProperties: &pkg.JavaPomProperties{
   508  				GroupID:    "org.apache.commons",
   509  				ArtifactID: "commons-lang3",
   510  			},
   511  		},
   512  	}
   513  
   514  	junitJupiter := pkg.Package{
   515  		Name:     "junit-jupiter",
   516  		Version:  "",
   517  		PURL:     "pkg:maven/org.junit.jupiter/junit-jupiter",
   518  		Language: pkg.Java,
   519  		Type:     pkg.JavaPkg,
   520  		FoundBy:  pomCatalogerName,
   521  		Metadata: pkg.JavaArchive{
   522  			PomProperties: &pkg.JavaPomProperties{
   523  				GroupID:    "org.junit.jupiter",
   524  				ArtifactID: "junit-jupiter",
   525  				Scope:      "test",
   526  			},
   527  		},
   528  	}
   529  
   530  	assertjCore := pkg.Package{
   531  		Name:     "assertj-core",
   532  		Version:  "3.23.1",
   533  		PURL:     "pkg:maven/org.assertj/assertj-core@3.23.1",
   534  		Language: pkg.Java,
   535  		Type:     pkg.JavaPkg,
   536  		FoundBy:  pomCatalogerName,
   537  		Metadata: pkg.JavaArchive{
   538  			PomProperties: &pkg.JavaPomProperties{
   539  				GroupID:    "org.assertj",
   540  				ArtifactID: "assertj-core",
   541  				Scope:      "test",
   542  			},
   543  		},
   544  	}
   545  
   546  	commonsIO := pkg.Package{
   547  		Name:     "commons-io",
   548  		Version:  "2.11.0",
   549  		PURL:     "pkg:maven/commons-io/commons-io@2.11.0",
   550  		Language: pkg.Java,
   551  		Type:     pkg.JavaPkg,
   552  		FoundBy:  pomCatalogerName,
   553  		Metadata: pkg.JavaArchive{
   554  			PomProperties: &pkg.JavaPomProperties{
   555  				GroupID:    "commons-io",
   556  				ArtifactID: "commons-io",
   557  				Scope:      "test",
   558  			},
   559  		},
   560  	}
   561  
   562  	mockitoInline := pkg.Package{
   563  		Name:     "mockito-inline",
   564  		Version:  "4.8.0",
   565  		PURL:     "pkg:maven/org.mockito/mockito-inline@4.8.0",
   566  		Language: pkg.Java,
   567  		Type:     pkg.JavaPkg,
   568  		FoundBy:  pomCatalogerName,
   569  		Metadata: pkg.JavaArchive{
   570  			PomProperties: &pkg.JavaPomProperties{
   571  				GroupID:    "org.mockito",
   572  				ArtifactID: "mockito-inline",
   573  				Scope:      "test",
   574  			},
   575  		},
   576  	}
   577  
   578  	js := pkg.Package{
   579  		Name:     "js",
   580  		Version:  "22.0.0.2",
   581  		PURL:     "pkg:maven/org.graalvm.js/js@22.0.0.2",
   582  		Language: pkg.Java,
   583  		Type:     pkg.JavaPkg,
   584  		FoundBy:  pomCatalogerName,
   585  		Metadata: pkg.JavaArchive{
   586  			PomProperties: &pkg.JavaPomProperties{
   587  				GroupID:    "org.graalvm.js",
   588  				ArtifactID: "js",
   589  				Scope:      "test",
   590  			},
   591  		},
   592  	}
   593  
   594  	jsScriptengine := pkg.Package{
   595  		Name:     "js-scriptengine",
   596  		Version:  "22.0.0.2",
   597  		PURL:     "pkg:maven/org.graalvm.js/js-scriptengine@22.0.0.2",
   598  		Language: pkg.Java,
   599  		Type:     pkg.JavaPkg,
   600  		FoundBy:  pomCatalogerName,
   601  		Metadata: pkg.JavaArchive{
   602  			PomProperties: &pkg.JavaPomProperties{
   603  				GroupID:    "org.graalvm.js",
   604  				ArtifactID: "js-scriptengine",
   605  				Scope:      "test",
   606  			},
   607  		},
   608  	}
   609  
   610  	commonsRngSimple := pkg.Package{
   611  		Name:     "commons-rng-simple",
   612  		Version:  "1.4",
   613  		PURL:     "pkg:maven/org.apache.commons/commons-rng-simple@1.4",
   614  		Language: pkg.Java,
   615  		Type:     pkg.JavaPkg,
   616  		FoundBy:  pomCatalogerName,
   617  		Metadata: pkg.JavaArchive{
   618  			PomProperties: &pkg.JavaPomProperties{
   619  				GroupID:    "org.apache.commons",
   620  				ArtifactID: "commons-rng-simple",
   621  				Scope:      "test",
   622  			},
   623  		},
   624  	}
   625  
   626  	jmhCore := pkg.Package{
   627  		Name:     "jmh-core",
   628  		Version:  "1.35",
   629  		PURL:     "pkg:maven/org.openjdk.jmh/jmh-core@1.35",
   630  		Language: pkg.Java,
   631  		Type:     pkg.JavaPkg,
   632  		FoundBy:  pomCatalogerName,
   633  		Metadata: pkg.JavaArchive{
   634  			PomProperties: &pkg.JavaPomProperties{
   635  				GroupID:    "org.openjdk.jmh",
   636  				ArtifactID: "jmh-core",
   637  				Scope:      "test",
   638  			},
   639  		},
   640  	}
   641  
   642  	jmhGeneratorAnnprocess := pkg.Package{
   643  		Name:     "jmh-generator-annprocess",
   644  		Version:  "1.35",
   645  		PURL:     "pkg:maven/org.openjdk.jmh/jmh-generator-annprocess@1.35",
   646  		Language: pkg.Java,
   647  		Type:     pkg.JavaPkg,
   648  		FoundBy:  pomCatalogerName,
   649  		Metadata: pkg.JavaArchive{
   650  			PomProperties: &pkg.JavaPomProperties{
   651  				GroupID:    "org.openjdk.jmh",
   652  				ArtifactID: "jmh-generator-annprocess",
   653  				Scope:      "test",
   654  			},
   655  		},
   656  	}
   657  
   658  	if resolved {
   659  		junitJupiter.Version = "5.9.1"
   660  	}
   661  
   662  	pkgs := []pkg.Package{
   663  		commonsText,
   664  		commonsLang3,
   665  		junitJupiter,
   666  		assertjCore,
   667  		commonsIO,
   668  		mockitoInline,
   669  		js,
   670  		jsScriptengine,
   671  		commonsRngSimple,
   672  		jmhCore,
   673  		jmhGeneratorAnnprocess,
   674  	}
   675  
   676  	var relationships []artifact.Relationship
   677  	for i := range pkgs {
   678  		p := &pkgs[i]
   679  		p.Locations = pomXmlLocation
   680  		finalizePackage(p)
   681  		if i == 0 {
   682  			continue
   683  		}
   684  		relationships = append(relationships, artifact.Relationship{
   685  			From: *p,
   686  			To:   pkgs[0],
   687  			Type: artifact.DependencyOfRelationship,
   688  		})
   689  	}
   690  
   691  	return expected{pkgs, relationships}
   692  }
   693  
   694  func expectedTransientPackageData() expected {
   695  	epl2 := pkg.NewLicenseSet(pkg.License{
   696  		Value: "Eclipse Public License v2.0",
   697  		Type:  license.Declared,
   698  		URLs:  []string{"https://www.eclipse.org/legal/epl-v20.html"},
   699  	})
   700  	transitiveTopLevel := pkg.Package{
   701  		Name:    "transitive-top-level",
   702  		Version: "99",
   703  		Metadata: pkg.JavaArchive{
   704  			PomProject: &pkg.JavaPomProject{
   705  				GroupID:    "my.other.group",
   706  				ArtifactID: "transitive-top-level",
   707  				Version:    "99",
   708  			},
   709  		},
   710  	}
   711  	childOne := pkg.Package{
   712  		Name:     "child-one",
   713  		Version:  "1.3.6",
   714  		Licenses: epl2,
   715  		Metadata: pkg.JavaArchive{
   716  			PomProject: &pkg.JavaPomProject{
   717  				GroupID:    "my.org",
   718  				ArtifactID: "child-one",
   719  				Version:    "1.3.6",
   720  				Parent: &pkg.JavaPomParent{
   721  					GroupID:    "my.org",
   722  					ArtifactID: "parent-one",
   723  					Version:    "3.11.0",
   724  				},
   725  			},
   726  			PomProperties: &pkg.JavaPomProperties{
   727  				GroupID:    "my.org",
   728  				ArtifactID: "child-one",
   729  			},
   730  		},
   731  	}
   732  	childTwo := pkg.Package{
   733  		Name:     "child-two",
   734  		Version:  "2.1.90",
   735  		Licenses: epl2,
   736  		Metadata: pkg.JavaArchive{
   737  			PomProject: &pkg.JavaPomProject{
   738  				GroupID:    "my.org",
   739  				ArtifactID: "child-two",
   740  				Version:    "2.1.90",
   741  				Parent: &pkg.JavaPomParent{
   742  					GroupID:    "my.org",
   743  					ArtifactID: "parent-one",
   744  					Version:    "3.11.0",
   745  				},
   746  			},
   747  			PomProperties: &pkg.JavaPomProperties{
   748  				GroupID:    "my.org",
   749  				ArtifactID: "child-two",
   750  				Scope:      "test",
   751  			},
   752  		},
   753  	}
   754  	commonsLang3_113_7_8_0 := pkg.Package{
   755  		Name:    "commons-lang3",
   756  		Version: "3.113.7.8.0",
   757  		Metadata: pkg.JavaArchive{
   758  			PomProperties: &pkg.JavaPomProperties{
   759  				GroupID:    "org.apache.commons",
   760  				ArtifactID: "commons-lang3",
   761  			},
   762  		},
   763  	}
   764  	commonsLang3_12_0 := pkg.Package{
   765  		Name:    "commons-lang3",
   766  		Version: "3.12.0",
   767  		Metadata: pkg.JavaArchive{
   768  			PomProperties: &pkg.JavaPomProperties{
   769  				GroupID:    "org.apache.commons",
   770  				ArtifactID: "commons-lang3",
   771  			},
   772  		},
   773  	}
   774  	commonsMath3 := pkg.Package{
   775  		Name:    "commons-math3.11.0",
   776  		Version: "3.5",
   777  		Metadata: pkg.JavaArchive{
   778  			PomProperties: &pkg.JavaPomProperties{
   779  				GroupID:    "org.apache.commons",
   780  				ArtifactID: "commons-math3.11.0",
   781  			},
   782  		},
   783  	}
   784  	commonsExec := pkg.Package{
   785  		Name:    "commons-exec",
   786  		Version: "1.3",
   787  		Metadata: pkg.JavaArchive{
   788  			PomProperties: &pkg.JavaPomProperties{
   789  				GroupID:    "org.apache.commons",
   790  				ArtifactID: "commons-exec",
   791  			},
   792  		},
   793  	}
   794  
   795  	allPackages := []*pkg.Package{
   796  		&transitiveTopLevel,
   797  		&childOne,
   798  		&childTwo,
   799  		&commonsLang3_12_0,
   800  		&commonsLang3_113_7_8_0,
   801  		&commonsMath3,
   802  		&commonsExec,
   803  	}
   804  
   805  	for _, p := range allPackages {
   806  		p.Language = pkg.Java
   807  		p.Type = pkg.JavaPkg
   808  		p.FoundBy = pomCatalogerName
   809  		p.Locations = file.NewLocationSet(file.NewLocation("pom.xml"))
   810  		finalizePackage(p)
   811  	}
   812  
   813  	pkgs := make([]pkg.Package, len(allPackages))
   814  	for i := 0; i < len(allPackages); i++ {
   815  		pkgs[i] = *allPackages[i]
   816  	}
   817  
   818  	depOf := func(a, b pkg.Package) artifact.Relationship {
   819  		return artifact.Relationship{
   820  			From: a,
   821  			To:   b,
   822  			Type: artifact.DependencyOfRelationship,
   823  		}
   824  	}
   825  
   826  	return expected{
   827  		packages: pkgs,
   828  		relationships: []artifact.Relationship{
   829  			depOf(childTwo, transitiveTopLevel),
   830  			depOf(childOne, transitiveTopLevel),
   831  			depOf(commonsLang3_12_0, childOne),
   832  			depOf(commonsLang3_113_7_8_0, childTwo),
   833  			depOf(commonsMath3, childTwo),
   834  			depOf(commonsExec, childTwo),
   835  		},
   836  	}
   837  }