github.com/google/osv-scalibr@v0.4.1/extractor/filesystem/os/purl/purl_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 purl_test
    16  
    17  import (
    18  	"testing"
    19  
    20  	"github.com/google/go-cmp/cmp"
    21  	apkmeta "github.com/google/osv-scalibr/extractor/filesystem/os/apk/metadata"
    22  	cosmeta "github.com/google/osv-scalibr/extractor/filesystem/os/cos/metadata"
    23  	dpkgmeta "github.com/google/osv-scalibr/extractor/filesystem/os/dpkg/metadata"
    24  	flatpakmeta "github.com/google/osv-scalibr/extractor/filesystem/os/flatpak/metadata"
    25  	nixmeta "github.com/google/osv-scalibr/extractor/filesystem/os/nix/metadata"
    26  	pacmanmeta "github.com/google/osv-scalibr/extractor/filesystem/os/pacman/metadata"
    27  	portagemeta "github.com/google/osv-scalibr/extractor/filesystem/os/portage/metadata"
    28  	ospurl "github.com/google/osv-scalibr/extractor/filesystem/os/purl"
    29  	rpmmeta "github.com/google/osv-scalibr/extractor/filesystem/os/rpm/metadata"
    30  	snapmeta "github.com/google/osv-scalibr/extractor/filesystem/os/snap/metadata"
    31  	"github.com/google/osv-scalibr/purl"
    32  )
    33  
    34  func TestMakePackageURLDebian(t *testing.T) {
    35  	pkgname := "pkgname"
    36  	sourcename := "sourcename"
    37  	name := "name"
    38  	version := "1.2.3"
    39  	sourceversion := "1.2.4"
    40  	source := "sourcename"
    41  	tests := []struct {
    42  		desc     string
    43  		purlType string
    44  		metadata *dpkgmeta.Metadata
    45  		want     *purl.PackageURL
    46  	}{
    47  		{
    48  			desc:     "both OS versions present",
    49  			purlType: purl.TypeDebian,
    50  			metadata: &dpkgmeta.Metadata{
    51  				PackageName:       pkgname,
    52  				SourceName:        sourcename,
    53  				SourceVersion:     sourceversion,
    54  				OSID:              "debian",
    55  				OSVersionCodename: "jammy",
    56  				OSVersionID:       "22.04",
    57  			},
    58  			want: &purl.PackageURL{
    59  				Type:      purl.TypeDebian,
    60  				Name:      pkgname,
    61  				Namespace: "debian",
    62  				Version:   version,
    63  				Qualifiers: purl.QualifiersFromMap(map[string]string{
    64  					purl.Source:        source,
    65  					purl.SourceVersion: sourceversion,
    66  					purl.Distro:        "jammy",
    67  				}),
    68  			},
    69  		},
    70  		{
    71  			desc:     "only VERSION_ID set",
    72  			purlType: purl.TypeDebian,
    73  			metadata: &dpkgmeta.Metadata{
    74  				PackageName:   pkgname,
    75  				SourceName:    sourcename,
    76  				SourceVersion: sourceversion,
    77  				OSID:          "debian",
    78  				OSVersionID:   "22.04",
    79  			},
    80  			want: &purl.PackageURL{
    81  				Type:      purl.TypeDebian,
    82  				Name:      pkgname,
    83  				Namespace: "debian",
    84  				Version:   version,
    85  				Qualifiers: purl.QualifiersFromMap(map[string]string{
    86  					purl.Source:        source,
    87  					purl.SourceVersion: sourceversion,
    88  					purl.Distro:        "22.04",
    89  				}),
    90  			},
    91  		},
    92  		{
    93  			desc:     "ID not set, fallback to linux",
    94  			purlType: purl.TypeDebian,
    95  			metadata: &dpkgmeta.Metadata{
    96  				PackageName:       pkgname,
    97  				SourceName:        sourcename,
    98  				SourceVersion:     sourceversion,
    99  				OSVersionCodename: "jammy",
   100  				OSVersionID:       "22.04",
   101  			},
   102  			want: &purl.PackageURL{
   103  				Type:      purl.TypeDebian,
   104  				Name:      pkgname,
   105  				Namespace: "linux",
   106  				Version:   version,
   107  				Qualifiers: purl.QualifiersFromMap(map[string]string{
   108  					purl.Source:        source,
   109  					purl.SourceVersion: sourceversion,
   110  					purl.Distro:        "jammy",
   111  				}),
   112  			},
   113  		},
   114  		{
   115  			desc:     "OS ID and OS Version set (OpenWrt)",
   116  			purlType: purl.TypeOpkg,
   117  			metadata: &dpkgmeta.Metadata{
   118  				PackageName: pkgname,
   119  				OSID:        "openwrt",
   120  				OSVersionID: "22.03.5",
   121  			},
   122  			want: &purl.PackageURL{
   123  				Type:      purl.TypeOpkg,
   124  				Name:      pkgname,
   125  				Namespace: "openwrt",
   126  				Version:   version,
   127  				Qualifiers: purl.QualifiersFromMap(map[string]string{
   128  					purl.Distro: "22.03.5",
   129  				}),
   130  			},
   131  		},
   132  		{
   133  			desc:     "OS ID not set, fallback to linux",
   134  			purlType: purl.TypeOpkg,
   135  			metadata: &dpkgmeta.Metadata{
   136  				PackageName:       pkgname,
   137  				OSVersionCodename: "jammy",
   138  				OSVersionID:       "5.10",
   139  			},
   140  			want: &purl.PackageURL{
   141  				Type:      purl.TypeOpkg,
   142  				Name:      pkgname,
   143  				Namespace: "linux",
   144  				Version:   version,
   145  				Qualifiers: purl.QualifiersFromMap(map[string]string{
   146  					purl.Distro: "jammy",
   147  				}),
   148  			},
   149  		},
   150  	}
   151  	for _, tt := range tests {
   152  		t.Run(tt.desc, func(t *testing.T) {
   153  			got := ospurl.MakePackageURL(name, version, tt.purlType, tt.metadata)
   154  			if diff := cmp.Diff(tt.want, got); diff != "" {
   155  				t.Errorf("ospurl.MakePackageURL(%v, %v, %v, %v): unexpected PURL (-want +got):\n%s", name, version, tt.purlType, tt.metadata, diff)
   156  			}
   157  		})
   158  	}
   159  }
   160  
   161  func TestMakePackageURLFlatpak(t *testing.T) {
   162  	pkgname := "pkgname"
   163  	pkgid := "pkgid"
   164  	pkgversion := "1.2.3"
   165  	releasedate := "2024-05-02"
   166  	osname := "Debian GNU/Linux"
   167  	osid := "debian"
   168  	osversionid := "12"
   169  	osbuildid := "bookworm"
   170  	developer := "developer"
   171  	tests := []struct {
   172  		desc     string
   173  		metadata *flatpakmeta.Metadata
   174  		want     *purl.PackageURL
   175  	}{
   176  		{
   177  			desc: "Both_VERSION_ID_and_BUILD_ID_is_set",
   178  			metadata: &flatpakmeta.Metadata{
   179  				PackageName:    pkgname,
   180  				PackageID:      pkgid,
   181  				PackageVersion: pkgversion,
   182  				ReleaseDate:    releasedate,
   183  				OSName:         osname,
   184  				OSID:           osid,
   185  				OSVersionID:    osversionid,
   186  				OSBuildID:      osbuildid,
   187  				Developer:      developer,
   188  			},
   189  			want: &purl.PackageURL{
   190  				Type:      purl.TypeFlatpak,
   191  				Name:      pkgname,
   192  				Namespace: "debian",
   193  				Version:   pkgversion,
   194  				Qualifiers: purl.QualifiersFromMap(map[string]string{
   195  					purl.Distro: "debian-12",
   196  				}),
   197  			},
   198  		},
   199  		{
   200  			desc: "only_BUILD_ID_set",
   201  			metadata: &flatpakmeta.Metadata{
   202  				PackageName:    pkgname,
   203  				PackageID:      pkgid,
   204  				PackageVersion: pkgversion,
   205  				ReleaseDate:    releasedate,
   206  				OSName:         osname,
   207  				OSID:           osid,
   208  				OSBuildID:      osbuildid,
   209  				Developer:      developer,
   210  			},
   211  			want: &purl.PackageURL{
   212  				Type:      purl.TypeFlatpak,
   213  				Name:      pkgname,
   214  				Namespace: "debian",
   215  				Version:   pkgversion,
   216  				Qualifiers: purl.QualifiersFromMap(map[string]string{
   217  					purl.Distro: "debian-bookworm",
   218  				}),
   219  			},
   220  		},
   221  		{
   222  			desc: "OS_ID_not_set",
   223  			metadata: &flatpakmeta.Metadata{
   224  				PackageName:    pkgname,
   225  				PackageID:      pkgid,
   226  				PackageVersion: pkgversion,
   227  				ReleaseDate:    releasedate,
   228  				OSName:         osname,
   229  				OSVersionID:    osversionid,
   230  				OSBuildID:      osbuildid,
   231  				Developer:      developer,
   232  			},
   233  			want: &purl.PackageURL{
   234  				Type:      purl.TypeFlatpak,
   235  				Name:      pkgname,
   236  				Namespace: "",
   237  				Version:   pkgversion,
   238  				Qualifiers: purl.QualifiersFromMap(map[string]string{
   239  					purl.Distro: "12",
   240  				}),
   241  			},
   242  		},
   243  	}
   244  	for _, tt := range tests {
   245  		t.Run(tt.desc, func(t *testing.T) {
   246  			got := ospurl.MakePackageURL(pkgname, pkgversion, purl.TypeFlatpak, tt.metadata)
   247  			if diff := cmp.Diff(tt.want, got); diff != "" {
   248  				t.Errorf("ospurl.MakePackageURL(%v, %v, %v, %v): unexpected PURL (-want +got):\n%s", pkgname, pkgversion, purl.TypeFlatpak, tt.metadata, diff)
   249  			}
   250  		})
   251  	}
   252  }
   253  
   254  func TestMakePackageURLAPK(t *testing.T) {
   255  	tests := []struct {
   256  		desc     string
   257  		metadata *apkmeta.Metadata
   258  		want     *purl.PackageURL
   259  	}{
   260  		{
   261  			desc: "all_fields_present",
   262  			metadata: &apkmeta.Metadata{
   263  				PackageName: "Name",
   264  				OriginName:  "originName",
   265  				OSID:        "id",
   266  				OSVersionID: "4.5.6",
   267  			},
   268  			want: &purl.PackageURL{
   269  				Type:       purl.TypeApk,
   270  				Name:       "name",
   271  				Namespace:  "id",
   272  				Version:    "1.2.3",
   273  				Qualifiers: purl.QualifiersFromMap(map[string]string{purl.Distro: "4.5.6", purl.Origin: "originName"}),
   274  			},
   275  		},
   276  		{
   277  			desc: "OS_ID_missing",
   278  			metadata: &apkmeta.Metadata{
   279  				PackageName: "Name",
   280  				OriginName:  "originName",
   281  				OSVersionID: "4.5.6",
   282  			},
   283  			want: &purl.PackageURL{
   284  				Type:       purl.TypeApk,
   285  				Name:       "name",
   286  				Namespace:  "alpine",
   287  				Version:    "1.2.3",
   288  				Qualifiers: purl.QualifiersFromMap(map[string]string{purl.Distro: "4.5.6", purl.Origin: "originName"}),
   289  			},
   290  		},
   291  		{
   292  			desc: "OS_version_ID_missing",
   293  			metadata: &apkmeta.Metadata{
   294  				PackageName: "Name",
   295  				OriginName:  "originName",
   296  				OSID:        "id",
   297  			},
   298  			want: &purl.PackageURL{
   299  				Type:       purl.TypeApk,
   300  				Name:       "name",
   301  				Namespace:  "id",
   302  				Version:    "1.2.3",
   303  				Qualifiers: purl.QualifiersFromMap(map[string]string{purl.Origin: "originName"}),
   304  			},
   305  		},
   306  	}
   307  	for _, tt := range tests {
   308  		t.Run(tt.desc, func(t *testing.T) {
   309  			got := ospurl.MakePackageURL("name", "1.2.3", purl.TypeApk, tt.metadata)
   310  			if diff := cmp.Diff(tt.want, got); diff != "" {
   311  				t.Errorf("ospurl.MakePackageURL(%v): unexpected PURL (-want +got):\n%s", tt.metadata, diff)
   312  			}
   313  		})
   314  	}
   315  }
   316  
   317  func TestMakePackageURLCOS(t *testing.T) {
   318  	tests := []struct {
   319  		desc     string
   320  		metadata *cosmeta.Metadata
   321  		want     *purl.PackageURL
   322  	}{
   323  		{
   324  			desc: "both_versions_present",
   325  			metadata: &cosmeta.Metadata{
   326  				OSVersionID: "101",
   327  				OSVersion:   "97",
   328  			},
   329  			want: &purl.PackageURL{
   330  				Type:       purl.TypeCOS,
   331  				Name:       "name",
   332  				Version:    "1.2.3",
   333  				Qualifiers: purl.QualifiersFromMap(map[string]string{purl.Distro: "cos-101"}),
   334  			},
   335  		},
   336  		{
   337  			desc: "only_VERSION_set",
   338  			metadata: &cosmeta.Metadata{
   339  				OSVersion: "97",
   340  			},
   341  			want: &purl.PackageURL{
   342  				Type:       purl.TypeCOS,
   343  				Name:       "name",
   344  				Version:    "1.2.3",
   345  				Qualifiers: purl.QualifiersFromMap(map[string]string{purl.Distro: "cos-97"}),
   346  			},
   347  		},
   348  		{
   349  			desc: "only_VERSION_ID_set",
   350  			metadata: &cosmeta.Metadata{
   351  				OSVersionID: "101",
   352  			},
   353  			want: &purl.PackageURL{
   354  				Type:       purl.TypeCOS,
   355  				Name:       "name",
   356  				Version:    "1.2.3",
   357  				Qualifiers: purl.QualifiersFromMap(map[string]string{purl.Distro: "cos-101"}),
   358  			},
   359  		},
   360  		{
   361  			desc:     "no versions set",
   362  			metadata: &cosmeta.Metadata{},
   363  			want: &purl.PackageURL{
   364  				Type:       purl.TypeCOS,
   365  				Name:       "name",
   366  				Version:    "1.2.3",
   367  				Qualifiers: purl.Qualifiers{},
   368  			},
   369  		},
   370  	}
   371  	for _, tt := range tests {
   372  		t.Run(tt.desc, func(t *testing.T) {
   373  			got := ospurl.MakePackageURL("name", "1.2.3", purl.TypeCOS, tt.metadata)
   374  			if diff := cmp.Diff(tt.want, got); diff != "" {
   375  				t.Errorf("ospurl.MakePackageURL(%v): unexpected PURL (-want +got):\n%s", tt.metadata, diff)
   376  			}
   377  		})
   378  	}
   379  }
   380  
   381  func TestMakePackageURLRPM(t *testing.T) {
   382  	pkgname := "pkgname"
   383  	source := "source.rpm"
   384  	version := "1.2.3"
   385  	epoch := 1
   386  	tests := []struct {
   387  		desc     string
   388  		metadata *rpmmeta.Metadata
   389  		want     *purl.PackageURL
   390  	}{
   391  		{
   392  			desc: "version_ID_and_build_ID_present",
   393  			metadata: &rpmmeta.Metadata{
   394  				PackageName: pkgname,
   395  				SourceRPM:   source,
   396  				Epoch:       epoch,
   397  				OSID:        "fedora",
   398  				OSVersionID: "32",
   399  				OSBuildID:   "asdf",
   400  			},
   401  			want: &purl.PackageURL{
   402  				Type:      purl.TypeRPM,
   403  				Name:      pkgname,
   404  				Namespace: "fedora",
   405  				Version:   version,
   406  				Qualifiers: purl.QualifiersFromMap(map[string]string{
   407  					purl.Epoch:     "1",
   408  					purl.Distro:    "fedora-32",
   409  					purl.SourceRPM: source,
   410  				}),
   411  			},
   412  		},
   413  		{
   414  			desc: "only_build_ID_present",
   415  			metadata: &rpmmeta.Metadata{
   416  				PackageName: pkgname,
   417  				SourceRPM:   source,
   418  				Epoch:       epoch,
   419  				OSID:        "fedora",
   420  				OSBuildID:   "asdf",
   421  			},
   422  			want: &purl.PackageURL{
   423  				Type:      purl.TypeRPM,
   424  				Name:      pkgname,
   425  				Namespace: "fedora",
   426  				Version:   version,
   427  				Qualifiers: purl.QualifiersFromMap(map[string]string{
   428  					purl.Epoch:     "1",
   429  					purl.Distro:    "fedora-asdf",
   430  					purl.SourceRPM: source,
   431  				}),
   432  			},
   433  		},
   434  		{
   435  			desc: "ID_missing",
   436  			metadata: &rpmmeta.Metadata{
   437  				PackageName: pkgname,
   438  				SourceRPM:   source,
   439  				Epoch:       epoch,
   440  				OSVersionID: "32",
   441  				OSBuildID:   "asdf",
   442  			},
   443  			want: &purl.PackageURL{
   444  				Type:      purl.TypeRPM,
   445  				Name:      pkgname,
   446  				Namespace: "",
   447  				Version:   version,
   448  				Qualifiers: purl.QualifiersFromMap(map[string]string{
   449  					purl.Epoch:     "1",
   450  					purl.Distro:    "32",
   451  					purl.SourceRPM: source,
   452  				}),
   453  			},
   454  		},
   455  	}
   456  	for _, tt := range tests {
   457  		t.Run(tt.desc, func(t *testing.T) {
   458  			got := ospurl.MakePackageURL(pkgname, version, purl.TypeRPM, tt.metadata)
   459  			if diff := cmp.Diff(tt.want, got); diff != "" {
   460  				t.Errorf("ospurl.MakePackageURL(%v): unexpected PURL (-want +got):\n%s", tt.metadata, diff)
   461  			}
   462  		})
   463  	}
   464  }
   465  
   466  func TestMakePackageURLSnap(t *testing.T) {
   467  	snapName := "testSnap"
   468  	snapVersion := "1.2.3"
   469  	snapGrade := "stable"
   470  	snapType := "os"
   471  	architectures := []string{"amd64", "arm64"}
   472  	osID := "debian"
   473  	osVersionCodename := "bookworm"
   474  	osVersionID := "12"
   475  
   476  	tests := []struct {
   477  		desc     string
   478  		metadata *snapmeta.Metadata
   479  		want     *purl.PackageURL
   480  	}{
   481  		{
   482  			desc: "Both_VERSION_CODENAME_and_VERSION_ID_are_set",
   483  			metadata: &snapmeta.Metadata{
   484  				Name:              snapName,
   485  				Version:           snapVersion,
   486  				Grade:             snapGrade,
   487  				Type:              snapType,
   488  				Architectures:     architectures,
   489  				OSID:              osID,
   490  				OSVersionCodename: osVersionCodename,
   491  				OSVersionID:       osVersionID,
   492  			},
   493  			want: &purl.PackageURL{
   494  				Type:      purl.TypeSnap,
   495  				Name:      snapName,
   496  				Namespace: osID,
   497  				Version:   snapVersion,
   498  				Qualifiers: purl.QualifiersFromMap(map[string]string{
   499  					purl.Distro: osVersionCodename,
   500  				}),
   501  			},
   502  		},
   503  		{
   504  			desc: "Only_VERSION_ID_is_set",
   505  			metadata: &snapmeta.Metadata{
   506  				Name:          snapName,
   507  				Version:       snapVersion,
   508  				Grade:         snapGrade,
   509  				Type:          snapType,
   510  				Architectures: architectures,
   511  				OSID:          osID,
   512  				OSVersionID:   osVersionID,
   513  			},
   514  			want: &purl.PackageURL{
   515  				Type:      purl.TypeSnap,
   516  				Name:      snapName,
   517  				Namespace: osID,
   518  				Version:   snapVersion,
   519  				Qualifiers: purl.QualifiersFromMap(map[string]string{
   520  					purl.Distro: osVersionID,
   521  				}),
   522  			},
   523  		},
   524  		{
   525  			desc: "OSID,_VERSION_CODENAME_and_VERSION_ID_all_are_not_set",
   526  			metadata: &snapmeta.Metadata{
   527  				Name:          snapName,
   528  				Version:       snapVersion,
   529  				Grade:         snapGrade,
   530  				Type:          snapType,
   531  				Architectures: architectures,
   532  			},
   533  			want: &purl.PackageURL{
   534  				Type:       purl.TypeSnap,
   535  				Name:       snapName,
   536  				Namespace:  "",
   537  				Version:    snapVersion,
   538  				Qualifiers: purl.QualifiersFromMap(map[string]string{}),
   539  			},
   540  		},
   541  	}
   542  	for _, tt := range tests {
   543  		t.Run(tt.desc, func(t *testing.T) {
   544  			got := ospurl.MakePackageURL(snapName, snapVersion, purl.TypeSnap, tt.metadata)
   545  			if diff := cmp.Diff(tt.want, got); diff != "" {
   546  				t.Errorf("ospurl.MakePackageURL(%v): unexpected PURL (-want +got):\n%s", tt.metadata, diff)
   547  			}
   548  		})
   549  	}
   550  }
   551  
   552  func TestMakePackageURLPacman(t *testing.T) {
   553  	pkgName := "pkgName"
   554  	pkgVersion := "pkgVersion"
   555  	PackageDependencies := "pkgDependencies1, pkgDependencies2"
   556  
   557  	tests := []struct {
   558  		desc     string
   559  		metadata *pacmanmeta.Metadata
   560  		want     *purl.PackageURL
   561  	}{
   562  		{
   563  			desc: "all_fields_present",
   564  			metadata: &pacmanmeta.Metadata{
   565  				PackageName:         pkgName,
   566  				PackageVersion:      pkgVersion,
   567  				OSID:                "arch",
   568  				OSVersionID:         "20241201.0.284684",
   569  				PackageDependencies: PackageDependencies,
   570  			},
   571  			want: &purl.PackageURL{
   572  				Type:      purl.TypePacman,
   573  				Name:      pkgName,
   574  				Namespace: "arch",
   575  				Version:   pkgVersion,
   576  				Qualifiers: purl.QualifiersFromMap(map[string]string{
   577  					purl.Distro:              "20241201.0.284684",
   578  					purl.PackageDependencies: PackageDependencies,
   579  				}),
   580  			},
   581  		},
   582  		{
   583  			desc: "only_VERSION_ID_set",
   584  			metadata: &pacmanmeta.Metadata{
   585  				PackageName:         pkgName,
   586  				PackageVersion:      pkgVersion,
   587  				OSID:                "arch",
   588  				OSVersionID:         "20241201.0.284684",
   589  				PackageDependencies: PackageDependencies,
   590  			},
   591  			want: &purl.PackageURL{
   592  				Type:      purl.TypePacman,
   593  				Name:      pkgName,
   594  				Namespace: "arch",
   595  				Version:   pkgVersion,
   596  				Qualifiers: purl.QualifiersFromMap(map[string]string{
   597  					purl.Distro:              "20241201.0.284684",
   598  					purl.PackageDependencies: PackageDependencies,
   599  				}),
   600  			},
   601  		},
   602  		{
   603  			desc: "OS_ID_not_set,_fallback_to_Linux",
   604  			metadata: &pacmanmeta.Metadata{
   605  				PackageName:         pkgName,
   606  				PackageVersion:      pkgVersion,
   607  				OSVersionID:         "20241201.0.284684",
   608  				PackageDependencies: PackageDependencies,
   609  			},
   610  			want: &purl.PackageURL{
   611  				Type:      purl.TypePacman,
   612  				Name:      pkgName,
   613  				Namespace: "linux",
   614  				Version:   pkgVersion,
   615  				Qualifiers: purl.QualifiersFromMap(map[string]string{
   616  					purl.Distro:              "20241201.0.284684",
   617  					purl.PackageDependencies: PackageDependencies,
   618  				}),
   619  			},
   620  		},
   621  	}
   622  	for _, tt := range tests {
   623  		t.Run(tt.desc, func(t *testing.T) {
   624  			got := ospurl.MakePackageURL(pkgName, pkgVersion, purl.TypePacman, tt.metadata)
   625  			if diff := cmp.Diff(tt.want, got); diff != "" {
   626  				t.Errorf("ospurl.MakePackageURL(%v): unexpected PURL (-want +got):\n%s", tt.metadata, diff)
   627  			}
   628  		})
   629  	}
   630  }
   631  
   632  func TestMakePackageURLPortage(t *testing.T) {
   633  	pkgName := "pkgName"
   634  	pkgVersion := "pkgVersion"
   635  
   636  	tests := []struct {
   637  		desc     string
   638  		metadata *portagemeta.Metadata
   639  		want     *purl.PackageURL
   640  	}{
   641  		{
   642  			desc: "all_fields_present",
   643  			metadata: &portagemeta.Metadata{
   644  				PackageName:    pkgName,
   645  				PackageVersion: pkgVersion,
   646  				OSID:           "Gentoo",
   647  				OSVersionID:    "20241201.0.284684",
   648  			},
   649  			want: &purl.PackageURL{
   650  				Type:      purl.TypePortage,
   651  				Name:      pkgName,
   652  				Namespace: "Gentoo",
   653  				Version:   pkgVersion,
   654  				Qualifiers: purl.QualifiersFromMap(map[string]string{
   655  					purl.Distro: "20241201.0.284684",
   656  				}),
   657  			},
   658  		},
   659  		{
   660  			desc: "only_VERSION_ID_set",
   661  			metadata: &portagemeta.Metadata{
   662  				PackageName:    pkgName,
   663  				PackageVersion: pkgVersion,
   664  				OSID:           "linux",
   665  				OSVersionID:    "2.17",
   666  			},
   667  			want: &purl.PackageURL{
   668  				Type:      purl.TypePortage,
   669  				Name:      pkgName,
   670  				Namespace: "linux",
   671  				Version:   pkgVersion,
   672  				Qualifiers: purl.QualifiersFromMap(map[string]string{
   673  					purl.Distro: "2.17",
   674  				}),
   675  			},
   676  		},
   677  		{
   678  			desc: "ID_not_set,_fallback_to_linux",
   679  			metadata: &portagemeta.Metadata{
   680  				PackageName:    pkgName,
   681  				PackageVersion: pkgVersion,
   682  				OSVersionID:    "jammy",
   683  			},
   684  			want: &purl.PackageURL{
   685  				Type:      purl.TypePortage,
   686  				Name:      pkgName,
   687  				Namespace: "linux",
   688  				Version:   pkgVersion,
   689  				Qualifiers: purl.QualifiersFromMap(map[string]string{
   690  					purl.Distro: "jammy",
   691  				}),
   692  			},
   693  		},
   694  	}
   695  	for _, tt := range tests {
   696  		t.Run(tt.desc, func(t *testing.T) {
   697  			got := ospurl.MakePackageURL(pkgName, pkgVersion, purl.TypePortage, tt.metadata)
   698  			if diff := cmp.Diff(tt.want, got); diff != "" {
   699  				t.Errorf("ospurl.MakePackageURL(%v): unexpected PURL (-want +got):\n%s", tt.metadata, diff)
   700  			}
   701  		})
   702  	}
   703  }
   704  
   705  func TestMakePackageURLNix(t *testing.T) {
   706  	pkgName := "pkgName"
   707  	pkgVersion := "pkgVersion"
   708  	pkgHash := "pkgHash"
   709  	pkgOutput := "pkgOutput"
   710  
   711  	tests := []struct {
   712  		desc     string
   713  		metadata *nixmeta.Metadata
   714  		want     *purl.PackageURL
   715  	}{
   716  		{
   717  			desc: "all_fields_present",
   718  			metadata: &nixmeta.Metadata{
   719  				PackageName:       pkgName,
   720  				PackageVersion:    pkgVersion,
   721  				PackageHash:       pkgHash,
   722  				PackageOutput:     pkgOutput,
   723  				OSID:              "nixos",
   724  				OSVersionCodename: "vicuna",
   725  				OSVersionID:       "24.11",
   726  			},
   727  			want: &purl.PackageURL{
   728  				Type:    purl.TypeNix,
   729  				Name:    pkgName,
   730  				Version: pkgVersion,
   731  				Qualifiers: purl.QualifiersFromMap(map[string]string{
   732  					"distro": "vicuna",
   733  				}),
   734  			},
   735  		},
   736  		{
   737  			desc: "only_VERSION_ID_set",
   738  			metadata: &nixmeta.Metadata{
   739  				PackageName:    pkgName,
   740  				PackageVersion: pkgVersion,
   741  				PackageHash:    pkgHash,
   742  				PackageOutput:  pkgOutput,
   743  				OSID:           "nixos",
   744  				OSVersionID:    "24.11",
   745  			},
   746  			want: &purl.PackageURL{
   747  				Type:    purl.TypeNix,
   748  				Name:    pkgName,
   749  				Version: pkgVersion,
   750  				Qualifiers: purl.QualifiersFromMap(map[string]string{
   751  					"distro": "24.11",
   752  				}),
   753  			},
   754  		},
   755  		{
   756  			desc: "OS_ID_not_set,_fallback_to_Nixos",
   757  			metadata: &nixmeta.Metadata{
   758  				PackageName:       pkgName,
   759  				PackageVersion:    pkgVersion,
   760  				PackageHash:       pkgHash,
   761  				PackageOutput:     pkgOutput,
   762  				OSVersionCodename: "vicuna",
   763  				OSVersionID:       "24.11",
   764  			},
   765  			want: &purl.PackageURL{
   766  				Type:    purl.TypeNix,
   767  				Name:    pkgName,
   768  				Version: pkgVersion,
   769  				Qualifiers: purl.QualifiersFromMap(map[string]string{
   770  					"distro": "vicuna",
   771  				}),
   772  			},
   773  		},
   774  	}
   775  	for _, tt := range tests {
   776  		t.Run(tt.desc, func(t *testing.T) {
   777  			got := ospurl.MakePackageURL(pkgName, pkgVersion, purl.TypeNix, tt.metadata)
   778  			if diff := cmp.Diff(tt.want, got); diff != "" {
   779  				t.Errorf("ospurl.MakePackageURL(%v): unexpected PURL (-want +got):\n%s", tt.metadata, diff)
   780  			}
   781  		})
   782  	}
   783  }