github.com/google/osv-scalibr@v0.4.1/converter/spdx/spdx_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 spdx_test
    16  
    17  import (
    18  	"math/rand"
    19  	"testing"
    20  
    21  	"github.com/google/go-cmp/cmp"
    22  	scalibr "github.com/google/osv-scalibr"
    23  	"github.com/google/osv-scalibr/converter"
    24  	"github.com/google/osv-scalibr/converter/spdx"
    25  	"github.com/google/osv-scalibr/extractor"
    26  	"github.com/google/osv-scalibr/extractor/filesystem/language/python/wheelegg"
    27  	"github.com/google/osv-scalibr/inventory"
    28  	"github.com/google/osv-scalibr/purl"
    29  	"github.com/google/uuid"
    30  	"github.com/spdx/tools-golang/spdx/v2/common"
    31  	"github.com/spdx/tools-golang/spdx/v2/v2_3"
    32  )
    33  
    34  func TestToSPDX23(t *testing.T) {
    35  	// Make UUIDs deterministic
    36  	uuid.SetRand(rand.New(rand.NewSource(1)))
    37  
    38  	testCases := []struct {
    39  		desc       string
    40  		scanResult *scalibr.ScanResult
    41  		config     spdx.Config
    42  		want       *v2_3.Document
    43  	}{
    44  		{
    45  			desc: "Package_with_no_custom_config",
    46  			scanResult: &scalibr.ScanResult{
    47  				Inventory: inventory.Inventory{
    48  					Packages: []*extractor.Package{{
    49  						Name:     "software",
    50  						Version:  "1.2.3",
    51  						PURLType: purl.TypePyPi,
    52  						Plugins:  []string{wheelegg.Name},
    53  					}},
    54  				},
    55  			},
    56  			want: &v2_3.Document{
    57  				SPDXVersion:       "SPDX-2.3",
    58  				DataLicense:       "CC0-1.0",
    59  				SPDXIdentifier:    "DOCUMENT",
    60  				DocumentName:      "SCALIBR-generated SPDX",
    61  				DocumentNamespace: "https://spdx.google/81855ad8-681d-4d86-91e9-1e00167939cb",
    62  				CreationInfo: &v2_3.CreationInfo{
    63  					Creators: []common.Creator{
    64  						{
    65  							CreatorType: "Tool",
    66  							Creator:     "SCALIBR",
    67  						},
    68  					},
    69  				},
    70  				Packages: []*v2_3.Package{
    71  					{
    72  						PackageName:           "main",
    73  						PackageSPDXIdentifier: "SPDXRef-Package-main-52fdfc07-2182-454f-963f-5f0f9a621d72",
    74  						PackageVersion:        "0",
    75  						PackageSupplier: &common.Supplier{
    76  							Supplier:     spdx.NoAssertion,
    77  							SupplierType: spdx.NoAssertion,
    78  						},
    79  						PackageDownloadLocation:   spdx.NoAssertion,
    80  						IsFilesAnalyzedTagPresent: false,
    81  					},
    82  					{
    83  						PackageName:           "software",
    84  						PackageSPDXIdentifier: "SPDXRef-Package-software-9566c74d-1003-4c4d-bbbb-0407d1e2c649",
    85  						PackageVersion:        "1.2.3",
    86  						PackageSupplier: &common.Supplier{
    87  							Supplier:     spdx.NoAssertion,
    88  							SupplierType: spdx.NoAssertion,
    89  						},
    90  						PackageDownloadLocation:   spdx.NoAssertion,
    91  						PackageLicenseConcluded:   spdx.NoAssertion,
    92  						PackageLicenseDeclared:    spdx.NoAssertion,
    93  						IsFilesAnalyzedTagPresent: false,
    94  						PackageSourceInfo:         "Identified by the python/wheelegg extractor",
    95  						PackageExternalReferences: []*v2_3.PackageExternalReference{
    96  							{
    97  								Category: "PACKAGE-MANAGER",
    98  								RefType:  "purl",
    99  								Locator:  "pkg:pypi/software@1.2.3",
   100  							},
   101  						},
   102  					},
   103  				},
   104  				Relationships: []*v2_3.Relationship{
   105  					{
   106  						RefA: common.DocElementID{
   107  							ElementRefID: "SPDXRef-DOCUMENT",
   108  						},
   109  						RefB: common.DocElementID{
   110  							ElementRefID: "SPDXRef-Package-main-52fdfc07-2182-454f-963f-5f0f9a621d72",
   111  						},
   112  						Relationship: "DESCRIBES",
   113  					},
   114  					{
   115  						RefA: common.DocElementID{
   116  							ElementRefID: "SPDXRef-Package-main-52fdfc07-2182-454f-963f-5f0f9a621d72",
   117  						},
   118  						RefB: common.DocElementID{
   119  							ElementRefID: "SPDXRef-Package-software-9566c74d-1003-4c4d-bbbb-0407d1e2c649",
   120  						},
   121  						Relationship: "CONTAINS",
   122  					},
   123  					{
   124  						RefA: common.DocElementID{
   125  							ElementRefID: "SPDXRef-Package-software-9566c74d-1003-4c4d-bbbb-0407d1e2c649",
   126  						},
   127  						RefB: common.DocElementID{
   128  							SpecialID: spdx.NoAssertion,
   129  						},
   130  						Relationship: "CONTAINS",
   131  					},
   132  				},
   133  			},
   134  		},
   135  		{
   136  			desc: "Package_with_custom_config",
   137  			scanResult: &scalibr.ScanResult{
   138  				Inventory: inventory.Inventory{
   139  					Packages: []*extractor.Package{{
   140  						Name:     "software",
   141  						Version:  "1.2.3",
   142  						PURLType: purl.TypePyPi,
   143  						Plugins:  []string{wheelegg.Name},
   144  					}},
   145  				},
   146  			},
   147  			config: spdx.Config{
   148  				DocumentName:      "Custom name",
   149  				DocumentNamespace: "Custom namespace",
   150  				Creators: []common.Creator{
   151  					{
   152  						CreatorType: "Person",
   153  						Creator:     "Custom creator",
   154  					},
   155  				},
   156  			},
   157  			want: &v2_3.Document{
   158  				SPDXVersion:       "SPDX-2.3",
   159  				DataLicense:       "CC0-1.0",
   160  				SPDXIdentifier:    "DOCUMENT",
   161  				DocumentName:      "Custom name",
   162  				DocumentNamespace: "Custom namespace",
   163  				CreationInfo: &v2_3.CreationInfo{
   164  					Creators: []common.Creator{
   165  						{
   166  							CreatorType: "Tool",
   167  							Creator:     "SCALIBR",
   168  						},
   169  						{
   170  							CreatorType: "Person",
   171  							Creator:     "Custom creator",
   172  						},
   173  					},
   174  				},
   175  				Packages: []*v2_3.Package{
   176  					{
   177  						PackageName:           "main",
   178  						PackageSPDXIdentifier: "SPDXRef-Package-main-6694d2c4-22ac-4208-a007-2939487f6999",
   179  						PackageVersion:        "0",
   180  						PackageSupplier: &common.Supplier{
   181  							Supplier:     spdx.NoAssertion,
   182  							SupplierType: spdx.NoAssertion,
   183  						},
   184  						PackageDownloadLocation:   spdx.NoAssertion,
   185  						IsFilesAnalyzedTagPresent: false,
   186  					},
   187  					{
   188  						PackageName:           "software",
   189  						PackageSPDXIdentifier: "SPDXRef-Package-software-eb9d18a4-4784-445d-87f3-c67cf22746e9",
   190  						PackageVersion:        "1.2.3",
   191  						PackageSupplier: &common.Supplier{
   192  							Supplier:     spdx.NoAssertion,
   193  							SupplierType: spdx.NoAssertion,
   194  						},
   195  						PackageDownloadLocation:   spdx.NoAssertion,
   196  						PackageLicenseConcluded:   spdx.NoAssertion,
   197  						PackageLicenseDeclared:    spdx.NoAssertion,
   198  						IsFilesAnalyzedTagPresent: false,
   199  						PackageSourceInfo:         "Identified by the python/wheelegg extractor",
   200  						PackageExternalReferences: []*v2_3.PackageExternalReference{
   201  							{
   202  								Category: "PACKAGE-MANAGER",
   203  								RefType:  "purl",
   204  								Locator:  "pkg:pypi/software@1.2.3",
   205  							},
   206  						},
   207  					},
   208  				},
   209  				Relationships: []*v2_3.Relationship{
   210  					{
   211  						RefA: common.DocElementID{
   212  							ElementRefID: "SPDXRef-DOCUMENT",
   213  						},
   214  						RefB: common.DocElementID{
   215  							ElementRefID: "SPDXRef-Package-main-6694d2c4-22ac-4208-a007-2939487f6999",
   216  						},
   217  						Relationship: "DESCRIBES",
   218  					},
   219  					{
   220  						RefA: common.DocElementID{
   221  							ElementRefID: "SPDXRef-Package-main-6694d2c4-22ac-4208-a007-2939487f6999",
   222  						},
   223  						RefB: common.DocElementID{
   224  							ElementRefID: "SPDXRef-Package-software-eb9d18a4-4784-445d-87f3-c67cf22746e9",
   225  						},
   226  						Relationship: "CONTAINS",
   227  					},
   228  					{
   229  						RefA: common.DocElementID{
   230  							ElementRefID: "SPDXRef-Package-software-eb9d18a4-4784-445d-87f3-c67cf22746e9",
   231  						},
   232  						RefB: common.DocElementID{
   233  							SpecialID: spdx.NoAssertion,
   234  						},
   235  						Relationship: "CONTAINS",
   236  					},
   237  				},
   238  			},
   239  		},
   240  		{
   241  			desc: "Packages_with_licenses",
   242  			scanResult: &scalibr.ScanResult{
   243  				Inventory: inventory.Inventory{
   244  					Packages: []*extractor.Package{{
   245  						Name:     "software-1",
   246  						Version:  "1.2.3",
   247  						PURLType: purl.TypePyPi,
   248  						Licenses: []string{"MIT"},
   249  						Plugins:  []string{wheelegg.Name},
   250  					}, {
   251  						Name:     "software-2",
   252  						Version:  "4.5.6",
   253  						PURLType: purl.TypePyPi,
   254  						Licenses: []string{"Apache-2.0", "MIT", "MADE UP"},
   255  						Plugins:  []string{wheelegg.Name},
   256  					}},
   257  				},
   258  			},
   259  			want: &v2_3.Document{
   260  				SPDXVersion:       "SPDX-2.3",
   261  				DataLicense:       "CC0-1.0",
   262  				SPDXIdentifier:    "DOCUMENT",
   263  				DocumentName:      "SCALIBR-generated SPDX",
   264  				DocumentNamespace: "https://spdx.google/6325253f-ec73-4dd7-a9e2-8bf921119c16",
   265  				CreationInfo: &v2_3.CreationInfo{
   266  					Creators: []common.Creator{
   267  						{
   268  							CreatorType: "Tool",
   269  							Creator:     "SCALIBR",
   270  						},
   271  					},
   272  				},
   273  				Packages: []*v2_3.Package{
   274  					{
   275  						PackageName:           "main",
   276  						PackageSPDXIdentifier: "SPDXRef-Package-main-95af5a25-3679-41ba-a2ff-6cd471c483f1",
   277  						PackageVersion:        "0",
   278  						PackageSupplier: &common.Supplier{
   279  							Supplier:     spdx.NoAssertion,
   280  							SupplierType: spdx.NoAssertion,
   281  						},
   282  						PackageDownloadLocation:   spdx.NoAssertion,
   283  						IsFilesAnalyzedTagPresent: false,
   284  					},
   285  					{
   286  						PackageName:           "software-1",
   287  						PackageSPDXIdentifier: "SPDXRef-Package-software-1-5fb90bad-b37c-4821-b6d9-5526a41a9504",
   288  						PackageVersion:        "1.2.3",
   289  						PackageSupplier: &common.Supplier{
   290  							Supplier:     spdx.NoAssertion,
   291  							SupplierType: spdx.NoAssertion,
   292  						},
   293  						PackageDownloadLocation:   spdx.NoAssertion,
   294  						PackageLicenseConcluded:   "MIT",
   295  						PackageLicenseDeclared:    spdx.NoAssertion,
   296  						IsFilesAnalyzedTagPresent: false,
   297  						PackageSourceInfo:         "Identified by the python/wheelegg extractor",
   298  						PackageExternalReferences: []*v2_3.PackageExternalReference{
   299  							{
   300  								Category: "PACKAGE-MANAGER",
   301  								RefType:  "purl",
   302  								Locator:  "pkg:pypi/software-1@1.2.3",
   303  							},
   304  						},
   305  					},
   306  					{
   307  						PackageName:           "software-2",
   308  						PackageSPDXIdentifier: "SPDXRef-Package-software-2-680b4e7c-8b76-4a1b-9d49-d4955c848621",
   309  						PackageVersion:        "4.5.6",
   310  						PackageSupplier: &common.Supplier{
   311  							Supplier:     spdx.NoAssertion,
   312  							SupplierType: spdx.NoAssertion,
   313  						},
   314  						PackageDownloadLocation:   spdx.NoAssertion,
   315  						PackageLicenseConcluded:   "Apache-2.0 AND LicenseRef-MADE-UP AND MIT",
   316  						PackageLicenseDeclared:    spdx.NoAssertion,
   317  						IsFilesAnalyzedTagPresent: false,
   318  						PackageSourceInfo:         "Identified by the python/wheelegg extractor",
   319  						PackageExternalReferences: []*v2_3.PackageExternalReference{
   320  							{
   321  								Category: "PACKAGE-MANAGER",
   322  								RefType:  "purl",
   323  								Locator:  "pkg:pypi/software-2@4.5.6",
   324  							},
   325  						},
   326  					},
   327  				},
   328  				Relationships: []*v2_3.Relationship{
   329  					{
   330  						RefA: common.DocElementID{
   331  							ElementRefID: "SPDXRef-DOCUMENT",
   332  						},
   333  						RefB: common.DocElementID{
   334  							ElementRefID: "SPDXRef-Package-main-95af5a25-3679-41ba-a2ff-6cd471c483f1",
   335  						},
   336  						Relationship: "DESCRIBES",
   337  					},
   338  					{
   339  						RefA: common.DocElementID{
   340  							ElementRefID: "SPDXRef-Package-main-95af5a25-3679-41ba-a2ff-6cd471c483f1",
   341  						},
   342  						RefB: common.DocElementID{
   343  							ElementRefID: "SPDXRef-Package-software-1-5fb90bad-b37c-4821-b6d9-5526a41a9504",
   344  						},
   345  						Relationship: "CONTAINS",
   346  					},
   347  					{
   348  						RefA: common.DocElementID{
   349  							ElementRefID: "SPDXRef-Package-software-1-5fb90bad-b37c-4821-b6d9-5526a41a9504",
   350  						},
   351  						RefB: common.DocElementID{
   352  							SpecialID: spdx.NoAssertion,
   353  						},
   354  						Relationship: "CONTAINS",
   355  					},
   356  					{
   357  						RefA: common.DocElementID{
   358  							ElementRefID: "SPDXRef-Package-main-95af5a25-3679-41ba-a2ff-6cd471c483f1",
   359  						},
   360  						RefB: common.DocElementID{
   361  							ElementRefID: "SPDXRef-Package-software-2-680b4e7c-8b76-4a1b-9d49-d4955c848621",
   362  						},
   363  						Relationship: "CONTAINS",
   364  					},
   365  					{
   366  						RefA: common.DocElementID{
   367  							ElementRefID: "SPDXRef-Package-software-2-680b4e7c-8b76-4a1b-9d49-d4955c848621",
   368  						},
   369  						RefB: common.DocElementID{
   370  							SpecialID: spdx.NoAssertion,
   371  						},
   372  						Relationship: "CONTAINS",
   373  					},
   374  				},
   375  				OtherLicenses: []*v2_3.OtherLicense{{LicenseIdentifier: "LicenseRef-MADE-UP", ExtractedText: "MADE UP"}},
   376  			},
   377  		},
   378  		{
   379  			desc: "Package_with_invalid_PURLs_skipped",
   380  			scanResult: &scalibr.ScanResult{
   381  				Inventory: inventory.Inventory{
   382  					Packages: []*extractor.Package{
   383  						// PURL field missing
   384  						{Plugins: []string{wheelegg.Name}},
   385  						// No name
   386  						{
   387  							Version: "1.2.3", PURLType: purl.TypePyPi, Plugins: []string{wheelegg.Name},
   388  						},
   389  						// No version
   390  						{
   391  							Name: "software", PURLType: purl.TypePyPi, Plugins: []string{wheelegg.Name},
   392  						},
   393  					},
   394  				},
   395  			},
   396  			want: &v2_3.Document{
   397  				SPDXVersion:       "SPDX-2.3",
   398  				DataLicense:       "CC0-1.0",
   399  				SPDXIdentifier:    "DOCUMENT",
   400  				DocumentName:      "SCALIBR-generated SPDX",
   401  				DocumentNamespace: "https://spdx.google/0bf50598-7592-4e66-8a5b-df2c7fc48445",
   402  				CreationInfo: &v2_3.CreationInfo{
   403  					Creators: []common.Creator{
   404  						{
   405  							CreatorType: "Tool",
   406  							Creator:     "SCALIBR",
   407  						},
   408  					},
   409  				},
   410  				Packages: []*v2_3.Package{{
   411  					PackageName:           "main",
   412  					PackageSPDXIdentifier: "SPDXRef-Package-main-0f070244-8615-4bda-8831-3f6a8eb668d2",
   413  					PackageVersion:        "0",
   414  					PackageSupplier: &common.Supplier{
   415  						Supplier:     spdx.NoAssertion,
   416  						SupplierType: spdx.NoAssertion,
   417  					},
   418  					PackageDownloadLocation:   spdx.NoAssertion,
   419  					IsFilesAnalyzedTagPresent: false,
   420  				}},
   421  				Relationships: []*v2_3.Relationship{
   422  					{
   423  						RefA: common.DocElementID{
   424  							ElementRefID: "SPDXRef-DOCUMENT",
   425  						},
   426  						RefB: common.DocElementID{
   427  							ElementRefID: "SPDXRef-Package-main-0f070244-8615-4bda-8831-3f6a8eb668d2",
   428  						},
   429  						Relationship: "DESCRIBES",
   430  					},
   431  				},
   432  			},
   433  		},
   434  		{
   435  			desc: "Invalid_chars_in_package_name_replaced",
   436  			scanResult: &scalibr.ScanResult{
   437  				Inventory: inventory.Inventory{
   438  					Packages: []*extractor.Package{{
   439  						Name:     "softw@re&",
   440  						Version:  "1.2.3",
   441  						PURLType: purl.TypePyPi,
   442  						Plugins:  []string{wheelegg.Name},
   443  					}},
   444  				},
   445  			},
   446  			want: &v2_3.Document{
   447  				SPDXVersion:       "SPDX-2.3",
   448  				DataLicense:       "CC0-1.0",
   449  				SPDXIdentifier:    "DOCUMENT",
   450  				DocumentName:      "SCALIBR-generated SPDX",
   451  				DocumentNamespace: "https://spdx.google/172ed857-94bb-458b-8c3b-525da1786f9f",
   452  				CreationInfo: &v2_3.CreationInfo{
   453  					Creators: []common.Creator{
   454  						{
   455  							CreatorType: "Tool",
   456  							Creator:     "SCALIBR",
   457  						},
   458  					},
   459  				},
   460  				Packages: []*v2_3.Package{
   461  					{
   462  						PackageName:           "main",
   463  						PackageSPDXIdentifier: "SPDXRef-Package-main-92d2572b-cd06-48d2-96c5-2f5054e2d083",
   464  						PackageVersion:        "0",
   465  						PackageSupplier: &common.Supplier{
   466  							Supplier:     spdx.NoAssertion,
   467  							SupplierType: spdx.NoAssertion,
   468  						},
   469  						PackageDownloadLocation:   spdx.NoAssertion,
   470  						IsFilesAnalyzedTagPresent: false,
   471  					},
   472  					{
   473  						PackageName:           "softw@re&",
   474  						PackageSPDXIdentifier: "SPDXRef-Package-softw-re--6bf84c71-74cb-4476-b64c-c3dbd968b0f7",
   475  						PackageVersion:        "1.2.3",
   476  						PackageSupplier: &common.Supplier{
   477  							Supplier:     spdx.NoAssertion,
   478  							SupplierType: spdx.NoAssertion,
   479  						},
   480  						PackageDownloadLocation:   spdx.NoAssertion,
   481  						PackageLicenseConcluded:   spdx.NoAssertion,
   482  						PackageLicenseDeclared:    spdx.NoAssertion,
   483  						IsFilesAnalyzedTagPresent: false,
   484  						PackageSourceInfo:         "Identified by the python/wheelegg extractor",
   485  						PackageExternalReferences: []*v2_3.PackageExternalReference{
   486  							{
   487  								Category: "PACKAGE-MANAGER",
   488  								RefType:  "purl",
   489  								Locator:  "pkg:pypi/softw%40re%26@1.2.3",
   490  							},
   491  						},
   492  					},
   493  				},
   494  				Relationships: []*v2_3.Relationship{
   495  					{
   496  						RefA: common.DocElementID{
   497  							ElementRefID: "SPDXRef-DOCUMENT",
   498  						},
   499  						RefB: common.DocElementID{
   500  							ElementRefID: "SPDXRef-Package-main-92d2572b-cd06-48d2-96c5-2f5054e2d083",
   501  						},
   502  						Relationship: "DESCRIBES",
   503  					},
   504  					{
   505  						RefA: common.DocElementID{
   506  							ElementRefID: "SPDXRef-Package-main-92d2572b-cd06-48d2-96c5-2f5054e2d083",
   507  						},
   508  						RefB: common.DocElementID{
   509  							ElementRefID: "SPDXRef-Package-softw-re--6bf84c71-74cb-4476-b64c-c3dbd968b0f7",
   510  						},
   511  						Relationship: "CONTAINS",
   512  					},
   513  					{
   514  						RefA: common.DocElementID{
   515  							ElementRefID: "SPDXRef-Package-softw-re--6bf84c71-74cb-4476-b64c-c3dbd968b0f7",
   516  						},
   517  						RefB: common.DocElementID{
   518  							SpecialID: spdx.NoAssertion,
   519  						},
   520  						Relationship: "CONTAINS",
   521  					},
   522  				},
   523  			},
   524  		},
   525  		{
   526  			desc: "One_location_reported",
   527  			scanResult: &scalibr.ScanResult{
   528  				Inventory: inventory.Inventory{
   529  					Packages: []*extractor.Package{{
   530  						Name:      "software",
   531  						Version:   "1.2.3",
   532  						PURLType:  purl.TypePyPi,
   533  						Plugins:   []string{wheelegg.Name},
   534  						Locations: []string{"/file1"},
   535  					}},
   536  				},
   537  			},
   538  			want: &v2_3.Document{
   539  				SPDXVersion:       "SPDX-2.3",
   540  				DataLicense:       "CC0-1.0",
   541  				SPDXIdentifier:    "DOCUMENT",
   542  				DocumentName:      "SCALIBR-generated SPDX",
   543  				DocumentNamespace: "https://spdx.google/29b0223b-eea5-44f7-8391-f445d15afd42",
   544  				CreationInfo: &v2_3.CreationInfo{
   545  					Creators: []common.Creator{
   546  						{
   547  							CreatorType: "Tool",
   548  							Creator:     "SCALIBR",
   549  						},
   550  					},
   551  				},
   552  				Packages: []*v2_3.Package{
   553  					{
   554  						PackageName:           "main",
   555  						PackageSPDXIdentifier: "SPDXRef-Package-main-ff094279-db19-44eb-97a1-9d0f7bbacbe0",
   556  						PackageVersion:        "0",
   557  						PackageSupplier: &common.Supplier{
   558  							Supplier:     spdx.NoAssertion,
   559  							SupplierType: spdx.NoAssertion,
   560  						},
   561  						PackageDownloadLocation:   spdx.NoAssertion,
   562  						IsFilesAnalyzedTagPresent: false,
   563  					},
   564  					{
   565  						PackageName:           "software",
   566  						PackageSPDXIdentifier: "SPDXRef-Package-software-255aa5b7-d44b-4c40-b84c-892b9bffd436",
   567  						PackageVersion:        "1.2.3",
   568  						PackageSupplier: &common.Supplier{
   569  							Supplier:     spdx.NoAssertion,
   570  							SupplierType: spdx.NoAssertion,
   571  						},
   572  						PackageDownloadLocation:   spdx.NoAssertion,
   573  						PackageLicenseConcluded:   spdx.NoAssertion,
   574  						PackageLicenseDeclared:    spdx.NoAssertion,
   575  						IsFilesAnalyzedTagPresent: false,
   576  						PackageSourceInfo:         "Identified by the python/wheelegg extractor from /file1",
   577  						PackageExternalReferences: []*v2_3.PackageExternalReference{
   578  							{
   579  								Category: "PACKAGE-MANAGER",
   580  								RefType:  "purl",
   581  								Locator:  "pkg:pypi/software@1.2.3",
   582  							},
   583  						},
   584  					},
   585  				},
   586  				Relationships: []*v2_3.Relationship{
   587  					{
   588  						RefA: common.DocElementID{
   589  							ElementRefID: "SPDXRef-DOCUMENT",
   590  						},
   591  						RefB: common.DocElementID{
   592  							ElementRefID: "SPDXRef-Package-main-ff094279-db19-44eb-97a1-9d0f7bbacbe0",
   593  						},
   594  						Relationship: "DESCRIBES",
   595  					},
   596  					{
   597  						RefA: common.DocElementID{
   598  							ElementRefID: "SPDXRef-Package-main-ff094279-db19-44eb-97a1-9d0f7bbacbe0",
   599  						},
   600  						RefB: common.DocElementID{
   601  							ElementRefID: "SPDXRef-Package-software-255aa5b7-d44b-4c40-b84c-892b9bffd436",
   602  						},
   603  						Relationship: "CONTAINS",
   604  					},
   605  					{
   606  						RefA: common.DocElementID{
   607  							ElementRefID: "SPDXRef-Package-software-255aa5b7-d44b-4c40-b84c-892b9bffd436",
   608  						},
   609  						RefB: common.DocElementID{
   610  							SpecialID: spdx.NoAssertion,
   611  						},
   612  						Relationship: "CONTAINS",
   613  					},
   614  				},
   615  			},
   616  		},
   617  		{
   618  			desc: "Multiple_locations_reported",
   619  			scanResult: &scalibr.ScanResult{
   620  				Inventory: inventory.Inventory{
   621  					Packages: []*extractor.Package{{
   622  						Name:      "software",
   623  						Version:   "1.2.3",
   624  						Plugins:   []string{wheelegg.Name},
   625  						PURLType:  purl.TypePyPi,
   626  						Locations: []string{"/file1", "/file2", "/file3"},
   627  					}},
   628  				},
   629  			},
   630  			want: &v2_3.Document{
   631  				SPDXVersion:       "SPDX-2.3",
   632  				DataLicense:       "CC0-1.0",
   633  				SPDXIdentifier:    "DOCUMENT",
   634  				DocumentName:      "SCALIBR-generated SPDX",
   635  				DocumentNamespace: "https://spdx.google/b14323a6-bc8f-4e7d-b1d9-29333ff99393",
   636  				CreationInfo: &v2_3.CreationInfo{
   637  					Creators: []common.Creator{
   638  						{
   639  							CreatorType: "Tool",
   640  							Creator:     "SCALIBR",
   641  						},
   642  					},
   643  				},
   644  				Packages: []*v2_3.Package{
   645  					{
   646  						PackageName:           "main",
   647  						PackageSPDXIdentifier: "SPDXRef-Package-main-94040374-f692-4b98-8bf8-713f8d962d7c",
   648  						PackageVersion:        "0",
   649  						PackageSupplier: &common.Supplier{
   650  							Supplier:     spdx.NoAssertion,
   651  							SupplierType: spdx.NoAssertion,
   652  						},
   653  						PackageDownloadLocation:   spdx.NoAssertion,
   654  						IsFilesAnalyzedTagPresent: false,
   655  					},
   656  					{
   657  						PackageName:           "software",
   658  						PackageSPDXIdentifier: "SPDXRef-Package-software-8d019192-c242-44e2-8afc-cae3a61fb586",
   659  						PackageVersion:        "1.2.3",
   660  						PackageSupplier: &common.Supplier{
   661  							Supplier:     spdx.NoAssertion,
   662  							SupplierType: spdx.NoAssertion,
   663  						},
   664  						PackageDownloadLocation:   spdx.NoAssertion,
   665  						PackageLicenseConcluded:   spdx.NoAssertion,
   666  						PackageLicenseDeclared:    spdx.NoAssertion,
   667  						IsFilesAnalyzedTagPresent: false,
   668  						PackageSourceInfo:         "Identified by the python/wheelegg extractor from 3 locations, including /file1 and /file2",
   669  						PackageExternalReferences: []*v2_3.PackageExternalReference{
   670  							{
   671  								Category: "PACKAGE-MANAGER",
   672  								RefType:  "purl",
   673  								Locator:  "pkg:pypi/software@1.2.3",
   674  							},
   675  						},
   676  					},
   677  				},
   678  				Relationships: []*v2_3.Relationship{
   679  					{
   680  						RefA: common.DocElementID{
   681  							ElementRefID: "SPDXRef-DOCUMENT",
   682  						},
   683  						RefB: common.DocElementID{
   684  							ElementRefID: "SPDXRef-Package-main-94040374-f692-4b98-8bf8-713f8d962d7c",
   685  						},
   686  						Relationship: "DESCRIBES",
   687  					},
   688  					{
   689  						RefA: common.DocElementID{
   690  							ElementRefID: "SPDXRef-Package-main-94040374-f692-4b98-8bf8-713f8d962d7c",
   691  						},
   692  						RefB: common.DocElementID{
   693  							ElementRefID: "SPDXRef-Package-software-8d019192-c242-44e2-8afc-cae3a61fb586",
   694  						},
   695  						Relationship: "CONTAINS",
   696  					},
   697  					{
   698  						RefA: common.DocElementID{
   699  							ElementRefID: "SPDXRef-Package-software-8d019192-c242-44e2-8afc-cae3a61fb586",
   700  						},
   701  						RefB: common.DocElementID{
   702  							SpecialID: spdx.NoAssertion,
   703  						},
   704  						Relationship: "CONTAINS",
   705  					},
   706  				},
   707  			},
   708  		},
   709  	}
   710  
   711  	for _, tc := range testCases {
   712  		t.Run(tc.desc, func(t *testing.T) {
   713  			got := converter.ToSPDX23(tc.scanResult, tc.config)
   714  			// Can't mock time.Now() so skip verifying the timestamp.
   715  			tc.want.CreationInfo.Created = got.CreationInfo.Created
   716  
   717  			if diff := cmp.Diff(tc.want, got, cmp.AllowUnexported(v2_3.Package{})); diff != "" {
   718  				t.Errorf("converter.ToSPDX23(%v): unexpected diff (-want +got):\n%s", tc.scanResult, diff)
   719  			}
   720  		})
   721  	}
   722  }