github.com/google/osv-scalibr@v0.4.1/extractor/filesystem/os/ecosystem/ecosystem_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 ecosystem_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  	"github.com/google/osv-scalibr/extractor/filesystem/os/ecosystem"
    25  	flatpakmeta "github.com/google/osv-scalibr/extractor/filesystem/os/flatpak/metadata"
    26  	nixmeta "github.com/google/osv-scalibr/extractor/filesystem/os/nix/metadata"
    27  	pacmanmeta "github.com/google/osv-scalibr/extractor/filesystem/os/pacman/metadata"
    28  	portagemeta "github.com/google/osv-scalibr/extractor/filesystem/os/portage/metadata"
    29  	ospurl "github.com/google/osv-scalibr/extractor/filesystem/os/purl"
    30  	rpmmeta "github.com/google/osv-scalibr/extractor/filesystem/os/rpm/metadata"
    31  	snapmeta "github.com/google/osv-scalibr/extractor/filesystem/os/snap/metadata"
    32  	"github.com/google/osv-scalibr/purl"
    33  )
    34  
    35  func TestMakeEcosystemAPK(t *testing.T) {
    36  	tests := []struct {
    37  		desc     string
    38  		metadata *apkmeta.Metadata
    39  		want     string
    40  	}{
    41  		{
    42  			desc: "OS_version_trimmed",
    43  			metadata: &apkmeta.Metadata{
    44  				OSVersionID: "4.5.6",
    45  			},
    46  			want: "Alpine:v4.5",
    47  		},
    48  		{
    49  			desc: "short_OS_version_not_trimmed",
    50  			metadata: &apkmeta.Metadata{
    51  				OSVersionID: "4",
    52  			},
    53  			want: "Alpine:v4",
    54  		},
    55  		{
    56  			desc:     "no OS version",
    57  			metadata: &apkmeta.Metadata{},
    58  			want:     "Alpine",
    59  		},
    60  		{
    61  			desc: "BellSoft 1st case",
    62  			metadata: &apkmeta.Metadata{
    63  				OSID:        "alpaquita",
    64  				OSVersionID: "23",
    65  			},
    66  			want: "Alpaquita:23",
    67  		},
    68  		{
    69  			desc: "BellSoft 2nd case",
    70  			metadata: &apkmeta.Metadata{
    71  				OSID:        "alpaquita",
    72  				OSVersionID: "stream",
    73  			},
    74  			want: "Alpaquita:stream",
    75  		},
    76  		{
    77  			desc: "BellSoft 3rd case",
    78  			metadata: &apkmeta.Metadata{
    79  				OSID:        "bellsoft-hardened-containers",
    80  				OSVersionID: "23",
    81  			},
    82  			want: "BellSoft Hardened Containers:23",
    83  		},
    84  		{
    85  			desc: "BellSoft 4th case",
    86  			metadata: &apkmeta.Metadata{
    87  				OSID:        "bellsoft-hardened-containers",
    88  				OSVersionID: "stream",
    89  			},
    90  			want: "BellSoft Hardened Containers:stream",
    91  		},
    92  	}
    93  	for _, tt := range tests {
    94  		t.Run(tt.desc, func(t *testing.T) {
    95  			got := ecosystem.MakeEcosystem(tt.metadata)
    96  			if diff := cmp.Diff(tt.want, got.String()); diff != "" {
    97  				t.Errorf("ecosystem.MakeEcosystem(%v) (-want +got):\n%s", tt.metadata, diff)
    98  			}
    99  		})
   100  	}
   101  }
   102  
   103  func TestMakeEcosystemDebian(t *testing.T) {
   104  	tests := []struct {
   105  		desc     string
   106  		metadata *dpkgmeta.Metadata
   107  		want     string
   108  	}{
   109  		{
   110  			desc: "OS_ID_present",
   111  			metadata: &dpkgmeta.Metadata{
   112  				OSID: "debian",
   113  			},
   114  			want: "Debian",
   115  		},
   116  		{
   117  			desc:     "OS ID not present",
   118  			metadata: &dpkgmeta.Metadata{},
   119  			want:     "Linux",
   120  		},
   121  		{
   122  			desc: "OS_version_present",
   123  			metadata: &dpkgmeta.Metadata{
   124  				OSID:        "debian",
   125  				OSVersionID: "12",
   126  			},
   127  			want: "Debian:12",
   128  		},
   129  		{
   130  			desc: "OS_ID_present_(OpenWrt),_not_valid_ecosystem,_so_ecosystem_should_be_empty",
   131  			metadata: &dpkgmeta.Metadata{
   132  				OSID: "openwrt",
   133  			},
   134  			want: "",
   135  		},
   136  		{
   137  			desc: "OS_version_present_(OpenWrt),_not_valid_ecosystem,_ecosystem_should_be_empty",
   138  			metadata: &dpkgmeta.Metadata{
   139  				OSID:        "openwrt",
   140  				OSVersionID: "22.03.5",
   141  			},
   142  			want: "",
   143  		},
   144  		{
   145  			desc: "OS_version_present_(Generic_Linux),_not_valid_ecosystem,_ecosystem_should_be_empty",
   146  			metadata: &dpkgmeta.Metadata{
   147  				OSID:        "linux",
   148  				OSVersionID: "5",
   149  			},
   150  			want: "Linux:5",
   151  		},
   152  	}
   153  	for _, tt := range tests {
   154  		t.Run(tt.desc, func(t *testing.T) {
   155  			got := ecosystem.MakeEcosystem(tt.metadata)
   156  			if diff := cmp.Diff(tt.want, got.String()); diff != "" {
   157  				t.Errorf("ecosystem.MakeEcosystem(%v) (-want +got):\n%s", tt.metadata, diff)
   158  			}
   159  		})
   160  	}
   161  }
   162  
   163  func TestEcosystemPacman(t *testing.T) {
   164  	tests := []struct {
   165  		desc     string
   166  		metadata *pacmanmeta.Metadata
   167  		want     string
   168  	}{
   169  		{
   170  			desc: "OS_ID_present",
   171  			metadata: &pacmanmeta.Metadata{
   172  				OSID: "arch",
   173  			},
   174  			want: "",
   175  		},
   176  		{
   177  			desc:     "OS ID not present",
   178  			metadata: &pacmanmeta.Metadata{},
   179  			want:     "Linux",
   180  		},
   181  		{
   182  			desc: "OS_version_present",
   183  			metadata: &pacmanmeta.Metadata{
   184  				OSID:        "arch",
   185  				OSVersionID: "20241201.0.284684",
   186  			},
   187  			want: "",
   188  		},
   189  	}
   190  	for _, tt := range tests {
   191  		t.Run(tt.desc, func(t *testing.T) {
   192  			got := ecosystem.MakeEcosystem(tt.metadata)
   193  			if diff := cmp.Diff(tt.want, got.String()); diff != "" {
   194  				t.Errorf("ecosystem.MakeEcosystem(%v) (-want +got):\n%s", tt.metadata, diff)
   195  			}
   196  		})
   197  	}
   198  }
   199  
   200  func TestEcosystemPortage(t *testing.T) {
   201  	tests := []struct {
   202  		desc     string
   203  		metadata *portagemeta.Metadata
   204  		want     string
   205  	}{
   206  		{
   207  			desc: "OS_ID_present",
   208  			metadata: &portagemeta.Metadata{
   209  				OSID: "gentoo",
   210  			},
   211  			want: "",
   212  		},
   213  		{
   214  			desc:     "OS ID not present",
   215  			metadata: &portagemeta.Metadata{},
   216  			want:     "Linux",
   217  		},
   218  		{
   219  			desc: "OS_version_present",
   220  			metadata: &portagemeta.Metadata{
   221  				OSID:        "gentoo",
   222  				OSVersionID: "2.17",
   223  			},
   224  			want: "",
   225  		},
   226  	}
   227  	for _, tt := range tests {
   228  		t.Run(tt.desc, func(t *testing.T) {
   229  			got := ecosystem.MakeEcosystem(tt.metadata)
   230  			if diff := cmp.Diff(tt.want, got.String()); diff != "" {
   231  				t.Errorf("ecosystem.MakeEcosystem(%v) (-want +got):\n%s", tt.metadata, diff)
   232  			}
   233  		})
   234  	}
   235  }
   236  
   237  func TestEcosystemRPM(t *testing.T) {
   238  	tests := []struct {
   239  		desc     string
   240  		metadata *rpmmeta.Metadata
   241  		want     string
   242  	}{
   243  		{
   244  			desc: "RHEL",
   245  			metadata: &rpmmeta.Metadata{
   246  				OSID: "rhel",
   247  			},
   248  			want: "Red Hat",
   249  		},
   250  		{
   251  			desc: "RHEL_with_CPE_name",
   252  			metadata: &rpmmeta.Metadata{
   253  				OSID:      "rhel",
   254  				OSCPEName: "cpe:/o:redhat:enterprise_linux:9::baseos",
   255  			},
   256  			want: "Red Hat:enterprise_linux:9::baseos",
   257  		},
   258  		{
   259  			desc: "rocky",
   260  			metadata: &rpmmeta.Metadata{
   261  				OSID: "rocky",
   262  			},
   263  			want: "Rocky Linux",
   264  		},
   265  		{
   266  			desc: "openEuler_base_version_from_pretty_name",
   267  			metadata: &rpmmeta.Metadata{
   268  				OSID:         "openEuler",
   269  				OSPrettyName: "openEuler 24.03",
   270  			},
   271  			want: "openEuler:24.03",
   272  		},
   273  		{
   274  			desc: "openEuler_LTS_qualifier",
   275  			metadata: &rpmmeta.Metadata{
   276  				OSID:         "openEuler",
   277  				OSPrettyName: "openEuler 24.03 (LTS)",
   278  			},
   279  			want: "openEuler:24.03-LTS",
   280  		},
   281  		{
   282  			desc: "openEuler_LTS_service_pack_qualifier",
   283  			metadata: &rpmmeta.Metadata{
   284  				OSID:         "openEuler",
   285  				OSPrettyName: "openEuler 24.03 (LTS-SP1)",
   286  			},
   287  			want: "openEuler:24.03-LTS-SP1",
   288  		},
   289  		{
   290  			desc: "openEuler_fallback_to_version_ID",
   291  			metadata: &rpmmeta.Metadata{
   292  				OSID:        "openEuler",
   293  				OSVersionID: "24.03",
   294  			},
   295  			want: "openEuler:24.03",
   296  		},
   297  		{
   298  			desc: "openEuler_no_version_details",
   299  			metadata: &rpmmeta.Metadata{
   300  				OSID: "openEuler",
   301  			},
   302  			want: "openEuler",
   303  		},
   304  		{
   305  			desc:     "OS ID not present",
   306  			metadata: &rpmmeta.Metadata{},
   307  			want:     "",
   308  		},
   309  	}
   310  	for _, tt := range tests {
   311  		t.Run(tt.desc, func(t *testing.T) {
   312  			got := ecosystem.MakeEcosystem(tt.metadata)
   313  			if diff := cmp.Diff(tt.want, got.String()); diff != "" {
   314  				t.Errorf("ecosystem.MakeEcosystem(%v) (-want +got):\n%s", tt.metadata, diff)
   315  			}
   316  		})
   317  	}
   318  }
   319  
   320  func TestEcosystemSnap(t *testing.T) {
   321  	tests := []struct {
   322  		desc     string
   323  		metadata *snapmeta.Metadata
   324  		want     string
   325  	}{
   326  		{
   327  			desc: "OS_ID_present",
   328  			metadata: &snapmeta.Metadata{
   329  				OSID: "ubuntu",
   330  			},
   331  			want: "Ubuntu",
   332  		},
   333  		{
   334  			desc:     "OS ID not present",
   335  			metadata: &snapmeta.Metadata{},
   336  			want:     "",
   337  		},
   338  	}
   339  	for _, tt := range tests {
   340  		t.Run(tt.desc, func(t *testing.T) {
   341  			got := ecosystem.MakeEcosystem(tt.metadata)
   342  			if diff := cmp.Diff(tt.want, got.String()); diff != "" {
   343  				t.Errorf("ecosystem.MakeEcosystem(%v) (-want +got):\n%s", tt.metadata, diff)
   344  			}
   345  		})
   346  	}
   347  }
   348  
   349  func TestMakePackageURLFlatpak(t *testing.T) {
   350  	pkgname := "pkgname"
   351  	pkgid := "pkgid"
   352  	pkgversion := "1.2.3"
   353  	releasedate := "2024-05-02"
   354  	osname := "Debian GNU/Linux"
   355  	osid := "debian"
   356  	osversionid := "12"
   357  	osbuildid := "bookworm"
   358  	developer := "developer"
   359  	tests := []struct {
   360  		desc     string
   361  		metadata *flatpakmeta.Metadata
   362  		want     *purl.PackageURL
   363  	}{
   364  		{
   365  			desc: "Both_VERSION_ID_and_BUILD_ID_is_set",
   366  			metadata: &flatpakmeta.Metadata{
   367  				PackageName:    pkgname,
   368  				PackageID:      pkgid,
   369  				PackageVersion: pkgversion,
   370  				ReleaseDate:    releasedate,
   371  				OSName:         osname,
   372  				OSID:           osid,
   373  				OSVersionID:    osversionid,
   374  				OSBuildID:      osbuildid,
   375  				Developer:      developer,
   376  			},
   377  			want: &purl.PackageURL{
   378  				Type:      purl.TypeFlatpak,
   379  				Name:      pkgname,
   380  				Namespace: "debian",
   381  				Version:   pkgversion,
   382  				Qualifiers: purl.QualifiersFromMap(map[string]string{
   383  					purl.Distro: "debian-12",
   384  				}),
   385  			},
   386  		},
   387  		{
   388  			desc: "only_BUILD_ID_set",
   389  			metadata: &flatpakmeta.Metadata{
   390  				PackageName:    pkgname,
   391  				PackageID:      pkgid,
   392  				PackageVersion: pkgversion,
   393  				ReleaseDate:    releasedate,
   394  				OSName:         osname,
   395  				OSID:           osid,
   396  				OSBuildID:      osbuildid,
   397  				Developer:      developer,
   398  			},
   399  			want: &purl.PackageURL{
   400  				Type:      purl.TypeFlatpak,
   401  				Name:      pkgname,
   402  				Namespace: "debian",
   403  				Version:   pkgversion,
   404  				Qualifiers: purl.QualifiersFromMap(map[string]string{
   405  					purl.Distro: "debian-bookworm",
   406  				}),
   407  			},
   408  		},
   409  		{
   410  			desc: "OS_ID_not_set",
   411  			metadata: &flatpakmeta.Metadata{
   412  				PackageName:    pkgname,
   413  				PackageID:      pkgid,
   414  				PackageVersion: pkgversion,
   415  				ReleaseDate:    releasedate,
   416  				OSName:         osname,
   417  				OSVersionID:    osversionid,
   418  				OSBuildID:      osbuildid,
   419  				Developer:      developer,
   420  			},
   421  			want: &purl.PackageURL{
   422  				Type:      purl.TypeFlatpak,
   423  				Name:      pkgname,
   424  				Namespace: "",
   425  				Version:   pkgversion,
   426  				Qualifiers: purl.QualifiersFromMap(map[string]string{
   427  					purl.Distro: "12",
   428  				}),
   429  			},
   430  		},
   431  	}
   432  	for _, tt := range tests {
   433  		t.Run(tt.desc, func(t *testing.T) {
   434  			got := ospurl.MakePackageURL(pkgname, pkgversion, purl.TypeFlatpak, tt.metadata)
   435  			if diff := cmp.Diff(tt.want, got); diff != "" {
   436  				t.Errorf("ospurl.MakePackageURL(%v, %v, %v, %v): unexpected PURL (-want +got):\n%s", pkgname, pkgversion, purl.TypeFlatpak, tt.metadata, diff)
   437  			}
   438  		})
   439  	}
   440  }
   441  
   442  func TestMakePackageURLAPK(t *testing.T) {
   443  	tests := []struct {
   444  		desc     string
   445  		metadata *apkmeta.Metadata
   446  		want     *purl.PackageURL
   447  	}{
   448  		{
   449  			desc: "all_fields_present",
   450  			metadata: &apkmeta.Metadata{
   451  				PackageName: "Name",
   452  				OriginName:  "originName",
   453  				OSID:        "id",
   454  				OSVersionID: "4.5.6",
   455  			},
   456  			want: &purl.PackageURL{
   457  				Type:       purl.TypeApk,
   458  				Name:       "name",
   459  				Namespace:  "id",
   460  				Version:    "1.2.3",
   461  				Qualifiers: purl.QualifiersFromMap(map[string]string{purl.Distro: "4.5.6", purl.Origin: "originName"}),
   462  			},
   463  		},
   464  		{
   465  			desc: "OS_ID_missing",
   466  			metadata: &apkmeta.Metadata{
   467  				PackageName: "Name",
   468  				OriginName:  "originName",
   469  				OSVersionID: "4.5.6",
   470  			},
   471  			want: &purl.PackageURL{
   472  				Type:       purl.TypeApk,
   473  				Name:       "name",
   474  				Namespace:  "alpine",
   475  				Version:    "1.2.3",
   476  				Qualifiers: purl.QualifiersFromMap(map[string]string{purl.Distro: "4.5.6", purl.Origin: "originName"}),
   477  			},
   478  		},
   479  		{
   480  			desc: "OS_version_ID_missing",
   481  			metadata: &apkmeta.Metadata{
   482  				PackageName: "Name",
   483  				OriginName:  "originName",
   484  				OSID:        "id",
   485  			},
   486  			want: &purl.PackageURL{
   487  				Type:       purl.TypeApk,
   488  				Name:       "name",
   489  				Namespace:  "id",
   490  				Version:    "1.2.3",
   491  				Qualifiers: purl.QualifiersFromMap(map[string]string{purl.Origin: "originName"}),
   492  			},
   493  		},
   494  	}
   495  	for _, tt := range tests {
   496  		t.Run(tt.desc, func(t *testing.T) {
   497  			got := ospurl.MakePackageURL("name", "1.2.3", purl.TypeApk, tt.metadata)
   498  			if diff := cmp.Diff(tt.want, got); diff != "" {
   499  				t.Errorf("ospurl.MakePackageURL(%v): unexpected PURL (-want +got):\n%s", tt.metadata, diff)
   500  			}
   501  		})
   502  	}
   503  }
   504  
   505  func TestMakePackageURLCOS(t *testing.T) {
   506  	tests := []struct {
   507  		desc     string
   508  		metadata *cosmeta.Metadata
   509  		want     *purl.PackageURL
   510  	}{
   511  		{
   512  			desc: "both_versions_present",
   513  			metadata: &cosmeta.Metadata{
   514  				OSVersionID: "101",
   515  				OSVersion:   "97",
   516  			},
   517  			want: &purl.PackageURL{
   518  				Type:       purl.TypeCOS,
   519  				Name:       "name",
   520  				Version:    "1.2.3",
   521  				Qualifiers: purl.QualifiersFromMap(map[string]string{purl.Distro: "cos-101"}),
   522  			},
   523  		},
   524  		{
   525  			desc: "only_VERSION_set",
   526  			metadata: &cosmeta.Metadata{
   527  				OSVersion: "97",
   528  			},
   529  			want: &purl.PackageURL{
   530  				Type:       purl.TypeCOS,
   531  				Name:       "name",
   532  				Version:    "1.2.3",
   533  				Qualifiers: purl.QualifiersFromMap(map[string]string{purl.Distro: "cos-97"}),
   534  			},
   535  		},
   536  		{
   537  			desc: "only_VERSION_ID_set",
   538  			metadata: &cosmeta.Metadata{
   539  				OSVersionID: "101",
   540  			},
   541  			want: &purl.PackageURL{
   542  				Type:       purl.TypeCOS,
   543  				Name:       "name",
   544  				Version:    "1.2.3",
   545  				Qualifiers: purl.QualifiersFromMap(map[string]string{purl.Distro: "cos-101"}),
   546  			},
   547  		},
   548  		{
   549  			desc:     "no versions set",
   550  			metadata: &cosmeta.Metadata{},
   551  			want: &purl.PackageURL{
   552  				Type:       purl.TypeCOS,
   553  				Name:       "name",
   554  				Version:    "1.2.3",
   555  				Qualifiers: purl.Qualifiers{},
   556  			},
   557  		},
   558  	}
   559  	for _, tt := range tests {
   560  		t.Run(tt.desc, func(t *testing.T) {
   561  			got := ospurl.MakePackageURL("name", "1.2.3", purl.TypeCOS, tt.metadata)
   562  			if diff := cmp.Diff(tt.want, got); diff != "" {
   563  				t.Errorf("ospurl.MakePackageURL(%v): unexpected PURL (-want +got):\n%s", tt.metadata, diff)
   564  			}
   565  		})
   566  	}
   567  }
   568  
   569  func TestMakePackageURLRPM(t *testing.T) {
   570  	pkgname := "pkgname"
   571  	source := "source.rpm"
   572  	version := "1.2.3"
   573  	epoch := 1
   574  	tests := []struct {
   575  		desc     string
   576  		metadata *rpmmeta.Metadata
   577  		want     *purl.PackageURL
   578  	}{
   579  		{
   580  			desc: "version_ID_and_build_ID_present",
   581  			metadata: &rpmmeta.Metadata{
   582  				PackageName: pkgname,
   583  				SourceRPM:   source,
   584  				Epoch:       epoch,
   585  				OSID:        "fedora",
   586  				OSVersionID: "32",
   587  				OSBuildID:   "asdf",
   588  			},
   589  			want: &purl.PackageURL{
   590  				Type:      purl.TypeRPM,
   591  				Name:      pkgname,
   592  				Namespace: "fedora",
   593  				Version:   version,
   594  				Qualifiers: purl.QualifiersFromMap(map[string]string{
   595  					purl.Epoch:     "1",
   596  					purl.Distro:    "fedora-32",
   597  					purl.SourceRPM: source,
   598  				}),
   599  			},
   600  		},
   601  		{
   602  			desc: "only_build_ID_present",
   603  			metadata: &rpmmeta.Metadata{
   604  				PackageName: pkgname,
   605  				SourceRPM:   source,
   606  				Epoch:       epoch,
   607  				OSID:        "fedora",
   608  				OSBuildID:   "asdf",
   609  			},
   610  			want: &purl.PackageURL{
   611  				Type:      purl.TypeRPM,
   612  				Name:      pkgname,
   613  				Namespace: "fedora",
   614  				Version:   version,
   615  				Qualifiers: purl.QualifiersFromMap(map[string]string{
   616  					purl.Epoch:     "1",
   617  					purl.Distro:    "fedora-asdf",
   618  					purl.SourceRPM: source,
   619  				}),
   620  			},
   621  		},
   622  		{
   623  			desc: "ID_missing",
   624  			metadata: &rpmmeta.Metadata{
   625  				PackageName: pkgname,
   626  				SourceRPM:   source,
   627  				Epoch:       epoch,
   628  				OSVersionID: "32",
   629  				OSBuildID:   "asdf",
   630  			},
   631  			want: &purl.PackageURL{
   632  				Type:      purl.TypeRPM,
   633  				Name:      pkgname,
   634  				Namespace: "",
   635  				Version:   version,
   636  				Qualifiers: purl.QualifiersFromMap(map[string]string{
   637  					purl.Epoch:     "1",
   638  					purl.Distro:    "32",
   639  					purl.SourceRPM: source,
   640  				}),
   641  			},
   642  		},
   643  	}
   644  	for _, tt := range tests {
   645  		t.Run(tt.desc, func(t *testing.T) {
   646  			got := ospurl.MakePackageURL(pkgname, version, purl.TypeRPM, tt.metadata)
   647  			if diff := cmp.Diff(tt.want, got); diff != "" {
   648  				t.Errorf("ospurl.MakePackageURL(%v): unexpected PURL (-want +got):\n%s", tt.metadata, diff)
   649  			}
   650  		})
   651  	}
   652  }
   653  
   654  func TestMakePackageURLSnap(t *testing.T) {
   655  	snapName := "testSnap"
   656  	snapVersion := "1.2.3"
   657  	snapGrade := "stable"
   658  	snapType := "os"
   659  	architectures := []string{"amd64", "arm64"}
   660  	osID := "debian"
   661  	osVersionCodename := "bookworm"
   662  	osVersionID := "12"
   663  
   664  	tests := []struct {
   665  		desc     string
   666  		metadata *snapmeta.Metadata
   667  		want     *purl.PackageURL
   668  	}{
   669  		{
   670  			desc: "Both_VERSION_CODENAME_and_VERSION_ID_are_set",
   671  			metadata: &snapmeta.Metadata{
   672  				Name:              snapName,
   673  				Version:           snapVersion,
   674  				Grade:             snapGrade,
   675  				Type:              snapType,
   676  				Architectures:     architectures,
   677  				OSID:              osID,
   678  				OSVersionCodename: osVersionCodename,
   679  				OSVersionID:       osVersionID,
   680  			},
   681  			want: &purl.PackageURL{
   682  				Type:      purl.TypeSnap,
   683  				Name:      snapName,
   684  				Namespace: osID,
   685  				Version:   snapVersion,
   686  				Qualifiers: purl.QualifiersFromMap(map[string]string{
   687  					purl.Distro: osVersionCodename,
   688  				}),
   689  			},
   690  		},
   691  		{
   692  			desc: "Only_VERSION_ID_is_set",
   693  			metadata: &snapmeta.Metadata{
   694  				Name:          snapName,
   695  				Version:       snapVersion,
   696  				Grade:         snapGrade,
   697  				Type:          snapType,
   698  				Architectures: architectures,
   699  				OSID:          osID,
   700  				OSVersionID:   osVersionID,
   701  			},
   702  			want: &purl.PackageURL{
   703  				Type:      purl.TypeSnap,
   704  				Name:      snapName,
   705  				Namespace: osID,
   706  				Version:   snapVersion,
   707  				Qualifiers: purl.QualifiersFromMap(map[string]string{
   708  					purl.Distro: osVersionID,
   709  				}),
   710  			},
   711  		},
   712  		{
   713  			desc: "OSID,_VERSION_CODENAME_and_VERSION_ID_all_are_not_set",
   714  			metadata: &snapmeta.Metadata{
   715  				Name:          snapName,
   716  				Version:       snapVersion,
   717  				Grade:         snapGrade,
   718  				Type:          snapType,
   719  				Architectures: architectures,
   720  			},
   721  			want: &purl.PackageURL{
   722  				Type:       purl.TypeSnap,
   723  				Name:       snapName,
   724  				Namespace:  "",
   725  				Version:    snapVersion,
   726  				Qualifiers: purl.QualifiersFromMap(map[string]string{}),
   727  			},
   728  		},
   729  	}
   730  	for _, tt := range tests {
   731  		t.Run(tt.desc, func(t *testing.T) {
   732  			got := ospurl.MakePackageURL(snapName, snapVersion, purl.TypeSnap, tt.metadata)
   733  			if diff := cmp.Diff(tt.want, got); diff != "" {
   734  				t.Errorf("ospurl.MakePackageURL(%v): unexpected PURL (-want +got):\n%s", tt.metadata, diff)
   735  			}
   736  		})
   737  	}
   738  }
   739  
   740  func TestMakePackageURLPacman(t *testing.T) {
   741  	pkgName := "pkgName"
   742  	pkgVersion := "pkgVersion"
   743  	PackageDependencies := "pkgDependencies1, pkgDependencies2"
   744  
   745  	tests := []struct {
   746  		desc     string
   747  		metadata *pacmanmeta.Metadata
   748  		want     *purl.PackageURL
   749  	}{
   750  		{
   751  			desc: "all_fields_present",
   752  			metadata: &pacmanmeta.Metadata{
   753  				PackageName:         pkgName,
   754  				PackageVersion:      pkgVersion,
   755  				OSID:                "arch",
   756  				OSVersionID:         "20241201.0.284684",
   757  				PackageDependencies: PackageDependencies,
   758  			},
   759  			want: &purl.PackageURL{
   760  				Type:      purl.TypePacman,
   761  				Name:      pkgName,
   762  				Namespace: "arch",
   763  				Version:   pkgVersion,
   764  				Qualifiers: purl.QualifiersFromMap(map[string]string{
   765  					purl.Distro:              "20241201.0.284684",
   766  					purl.PackageDependencies: PackageDependencies,
   767  				}),
   768  			},
   769  		},
   770  		{
   771  			desc: "only_VERSION_ID_set",
   772  			metadata: &pacmanmeta.Metadata{
   773  				PackageName:         pkgName,
   774  				PackageVersion:      pkgVersion,
   775  				OSID:                "arch",
   776  				OSVersionID:         "20241201.0.284684",
   777  				PackageDependencies: PackageDependencies,
   778  			},
   779  			want: &purl.PackageURL{
   780  				Type:      purl.TypePacman,
   781  				Name:      pkgName,
   782  				Namespace: "arch",
   783  				Version:   pkgVersion,
   784  				Qualifiers: purl.QualifiersFromMap(map[string]string{
   785  					purl.Distro:              "20241201.0.284684",
   786  					purl.PackageDependencies: PackageDependencies,
   787  				}),
   788  			},
   789  		},
   790  		{
   791  			desc: "OS_ID_not_set,_fallback_to_Linux",
   792  			metadata: &pacmanmeta.Metadata{
   793  				PackageName:         pkgName,
   794  				PackageVersion:      pkgVersion,
   795  				OSVersionID:         "20241201.0.284684",
   796  				PackageDependencies: PackageDependencies,
   797  			},
   798  			want: &purl.PackageURL{
   799  				Type:      purl.TypePacman,
   800  				Name:      pkgName,
   801  				Namespace: "linux",
   802  				Version:   pkgVersion,
   803  				Qualifiers: purl.QualifiersFromMap(map[string]string{
   804  					purl.Distro:              "20241201.0.284684",
   805  					purl.PackageDependencies: PackageDependencies,
   806  				}),
   807  			},
   808  		},
   809  	}
   810  	for _, tt := range tests {
   811  		t.Run(tt.desc, func(t *testing.T) {
   812  			got := ospurl.MakePackageURL(pkgName, pkgVersion, purl.TypePacman, tt.metadata)
   813  			if diff := cmp.Diff(tt.want, got); diff != "" {
   814  				t.Errorf("ospurl.MakePackageURL(%v): unexpected PURL (-want +got):\n%s", tt.metadata, diff)
   815  			}
   816  		})
   817  	}
   818  }
   819  
   820  func TestMakePackageURLPortage(t *testing.T) {
   821  	pkgName := "pkgName"
   822  	pkgVersion := "pkgVersion"
   823  
   824  	tests := []struct {
   825  		desc     string
   826  		metadata *portagemeta.Metadata
   827  		want     *purl.PackageURL
   828  	}{
   829  		{
   830  			desc: "all_fields_present",
   831  			metadata: &portagemeta.Metadata{
   832  				PackageName:    pkgName,
   833  				PackageVersion: pkgVersion,
   834  				OSID:           "Gentoo",
   835  				OSVersionID:    "20241201.0.284684",
   836  			},
   837  			want: &purl.PackageURL{
   838  				Type:      purl.TypePortage,
   839  				Name:      pkgName,
   840  				Namespace: "Gentoo",
   841  				Version:   pkgVersion,
   842  				Qualifiers: purl.QualifiersFromMap(map[string]string{
   843  					purl.Distro: "20241201.0.284684",
   844  				}),
   845  			},
   846  		},
   847  		{
   848  			desc: "only_VERSION_ID_set",
   849  			metadata: &portagemeta.Metadata{
   850  				PackageName:    pkgName,
   851  				PackageVersion: pkgVersion,
   852  				OSID:           "linux",
   853  				OSVersionID:    "2.17",
   854  			},
   855  			want: &purl.PackageURL{
   856  				Type:      purl.TypePortage,
   857  				Name:      pkgName,
   858  				Namespace: "linux",
   859  				Version:   pkgVersion,
   860  				Qualifiers: purl.QualifiersFromMap(map[string]string{
   861  					purl.Distro: "2.17",
   862  				}),
   863  			},
   864  		},
   865  		{
   866  			desc: "ID_not_set,_fallback_to_linux",
   867  			metadata: &portagemeta.Metadata{
   868  				PackageName:    pkgName,
   869  				PackageVersion: pkgVersion,
   870  				OSVersionID:    "jammy",
   871  			},
   872  			want: &purl.PackageURL{
   873  				Type:      purl.TypePortage,
   874  				Name:      pkgName,
   875  				Namespace: "linux",
   876  				Version:   pkgVersion,
   877  				Qualifiers: purl.QualifiersFromMap(map[string]string{
   878  					purl.Distro: "jammy",
   879  				}),
   880  			},
   881  		},
   882  	}
   883  	for _, tt := range tests {
   884  		t.Run(tt.desc, func(t *testing.T) {
   885  			got := ospurl.MakePackageURL(pkgName, pkgVersion, purl.TypePortage, tt.metadata)
   886  			if diff := cmp.Diff(tt.want, got); diff != "" {
   887  				t.Errorf("ospurl.MakePackageURL(%v): unexpected PURL (-want +got):\n%s", tt.metadata, diff)
   888  			}
   889  		})
   890  	}
   891  }
   892  
   893  func TestMakePackageURLNix(t *testing.T) {
   894  	pkgName := "pkgName"
   895  	pkgVersion := "pkgVersion"
   896  	pkgHash := "pkgHash"
   897  	pkgOutput := "pkgOutput"
   898  
   899  	tests := []struct {
   900  		desc     string
   901  		metadata *nixmeta.Metadata
   902  		want     *purl.PackageURL
   903  	}{
   904  		{
   905  			desc: "all_fields_present",
   906  			metadata: &nixmeta.Metadata{
   907  				PackageName:       pkgName,
   908  				PackageVersion:    pkgVersion,
   909  				PackageHash:       pkgHash,
   910  				PackageOutput:     pkgOutput,
   911  				OSID:              "nixos",
   912  				OSVersionCodename: "vicuna",
   913  				OSVersionID:       "24.11",
   914  			},
   915  			want: &purl.PackageURL{
   916  				Type:    purl.TypeNix,
   917  				Name:    pkgName,
   918  				Version: pkgVersion,
   919  				Qualifiers: purl.QualifiersFromMap(map[string]string{
   920  					"distro": "vicuna",
   921  				}),
   922  			},
   923  		},
   924  		{
   925  			desc: "only_VERSION_ID_set",
   926  			metadata: &nixmeta.Metadata{
   927  				PackageName:    pkgName,
   928  				PackageVersion: pkgVersion,
   929  				PackageHash:    pkgHash,
   930  				PackageOutput:  pkgOutput,
   931  				OSID:           "nixos",
   932  				OSVersionID:    "24.11",
   933  			},
   934  			want: &purl.PackageURL{
   935  				Type:    purl.TypeNix,
   936  				Name:    pkgName,
   937  				Version: pkgVersion,
   938  				Qualifiers: purl.QualifiersFromMap(map[string]string{
   939  					"distro": "24.11",
   940  				}),
   941  			},
   942  		},
   943  		{
   944  			desc: "OS_ID_not_set,_fallback_to_Nixos",
   945  			metadata: &nixmeta.Metadata{
   946  				PackageName:       pkgName,
   947  				PackageVersion:    pkgVersion,
   948  				PackageHash:       pkgHash,
   949  				PackageOutput:     pkgOutput,
   950  				OSVersionCodename: "vicuna",
   951  				OSVersionID:       "24.11",
   952  			},
   953  			want: &purl.PackageURL{
   954  				Type:    purl.TypeNix,
   955  				Name:    pkgName,
   956  				Version: pkgVersion,
   957  				Qualifiers: purl.QualifiersFromMap(map[string]string{
   958  					"distro": "vicuna",
   959  				}),
   960  			},
   961  		},
   962  	}
   963  	for _, tt := range tests {
   964  		t.Run(tt.desc, func(t *testing.T) {
   965  			got := ospurl.MakePackageURL(pkgName, pkgVersion, purl.TypeNix, tt.metadata)
   966  			if diff := cmp.Diff(tt.want, got); diff != "" {
   967  				t.Errorf("ospurl.MakePackageURL(%v): unexpected PURL (-want +got):\n%s", tt.metadata, diff)
   968  			}
   969  		})
   970  	}
   971  }