github.com/google/osv-scalibr@v0.4.1/enricher/transitivedependency/requirements/requirements_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 requirements_test 16 17 import ( 18 "sort" 19 "testing" 20 21 "github.com/google/go-cmp/cmp" 22 cpb "github.com/google/osv-scalibr/binary/proto/config_go_proto" 23 "github.com/google/osv-scalibr/clients/clienttest" 24 "github.com/google/osv-scalibr/enricher" 25 "github.com/google/osv-scalibr/enricher/transitivedependency/requirements" 26 "github.com/google/osv-scalibr/extractor" 27 requirementsextractor "github.com/google/osv-scalibr/extractor/filesystem/language/python/requirements" 28 scalibrfs "github.com/google/osv-scalibr/fs" 29 "github.com/google/osv-scalibr/inventory" 30 "github.com/google/osv-scalibr/purl" 31 ) 32 33 func TestEnricher_Enrich(t *testing.T) { 34 input := enricher.ScanInput{ 35 ScanRoot: &scalibrfs.ScanRoot{ 36 Path: "testdata", 37 FS: scalibrfs.DirFS("."), 38 }, 39 } 40 inv := inventory.Inventory{ 41 Packages: []*extractor.Package{ 42 { 43 // Not a Python package. 44 Name: "abc:xyz", 45 Version: "1.0.0", 46 PURLType: purl.TypeMaven, 47 Locations: []string{"testdata/maven/pom.xml"}, 48 Plugins: []string{"java/pomxml"}, 49 }, 50 { 51 // Not extracted in requirements.txt. 52 Name: "abc", 53 Version: "1.0.0", 54 PURLType: purl.TypePyPi, 55 Locations: []string{"testdata/poetry/poetry.lock"}, 56 Plugins: []string{"python/poetrylock"}, 57 }, 58 { 59 Name: "alice", 60 PURLType: purl.TypePyPi, 61 Locations: []string{"testdata/requirements.txt"}, 62 Metadata: &requirementsextractor.Metadata{Requirement: "alice"}, 63 Plugins: []string{"python/requirements"}, 64 }, 65 { 66 Name: "bob", 67 Version: "2.0.0", 68 PURLType: purl.TypePyPi, 69 Locations: []string{"testdata/requirements.txt"}, 70 Metadata: &requirementsextractor.Metadata{VersionComparator: "==", Requirement: "bob==2.0.0"}, 71 Plugins: []string{"python/requirements"}, 72 }, 73 { 74 // Hash checking mode. 75 Name: "hash1", 76 Version: "1.0.0", 77 PURLType: purl.TypePyPi, 78 Locations: []string{"testdata/hash/requirements.txt"}, 79 Metadata: &requirementsextractor.Metadata{VersionComparator: "==", HashCheckingModeValues: []string{"sha256:123"}, Requirement: "hash1==1.0.0"}, 80 Plugins: []string{"python/requirements"}, 81 }, 82 { 83 // Hash checking mode. 84 Name: "hash2", 85 Version: "2.0.0", 86 PURLType: purl.TypePyPi, 87 Locations: []string{"testdata/hash/requirements.txt"}, 88 Metadata: &requirementsextractor.Metadata{VersionComparator: "==", HashCheckingModeValues: []string{"sha256:456"}, Requirement: "hash2==2.0.0"}, 89 Plugins: []string{"python/requirements"}, 90 }, 91 }, 92 } 93 94 resolutionClient := clienttest.NewMockResolutionClient(t, "testdata/universe.yaml") 95 enricher := requirements.New(&cpb.PluginConfig{}) 96 enricher.(*requirements.Enricher).Client = resolutionClient 97 err := enricher.Enrich(t.Context(), &input, &inv) 98 if err != nil { 99 t.Fatalf("failed to enrich: %v", err) 100 } 101 102 wantInventory := inventory.Inventory{ 103 Packages: []*extractor.Package{ 104 { 105 // Not extracted in requirements.txt. 106 Name: "abc", 107 Version: "1.0.0", 108 PURLType: purl.TypePyPi, 109 Locations: []string{"testdata/poetry/poetry.lock"}, 110 Plugins: []string{"python/poetrylock"}, 111 }, 112 { 113 // Not a Python package. 114 Name: "abc:xyz", 115 Version: "1.0.0", 116 PURLType: purl.TypeMaven, 117 Locations: []string{"testdata/maven/pom.xml"}, 118 Plugins: []string{"java/pomxml"}, 119 }, 120 { 121 Name: "alice", 122 Version: "1.0.0", 123 PURLType: purl.TypePyPi, 124 Locations: []string{"testdata/requirements.txt"}, 125 Metadata: &requirementsextractor.Metadata{Requirement: "alice"}, 126 Plugins: []string{"python/requirements", "transitivedependency/requirements"}, 127 }, 128 { 129 Name: "bob", 130 Version: "2.0.0", 131 PURLType: purl.TypePyPi, 132 Locations: []string{"testdata/requirements.txt"}, 133 Metadata: &requirementsextractor.Metadata{VersionComparator: "==", Requirement: "bob==2.0.0"}, 134 Plugins: []string{"python/requirements", "transitivedependency/requirements"}, 135 }, 136 { 137 Name: "chuck", 138 Version: "2.0.0", 139 PURLType: purl.TypePyPi, 140 Locations: []string{"testdata/requirements.txt"}, 141 Plugins: []string{"transitivedependency/requirements"}, 142 }, 143 { 144 Name: "dave", 145 Version: "2.0.0", 146 PURLType: purl.TypePyPi, 147 Locations: []string{"testdata/requirements.txt"}, 148 Plugins: []string{"transitivedependency/requirements"}, 149 }, 150 { 151 Name: "eve", 152 Version: "1.5.0", 153 PURLType: purl.TypePyPi, 154 Locations: []string{"testdata/requirements.txt"}, 155 Plugins: []string{"transitivedependency/requirements"}, 156 }, 157 { 158 Name: "frank", 159 Version: "2.0.0", 160 PURLType: purl.TypePyPi, 161 Locations: []string{"testdata/requirements.txt"}, 162 Plugins: []string{"transitivedependency/requirements"}, 163 }, 164 { 165 // Hash checking mode. 166 Name: "hash1", 167 Version: "1.0.0", 168 PURLType: purl.TypePyPi, 169 Locations: []string{"testdata/hash/requirements.txt"}, 170 Metadata: &requirementsextractor.Metadata{VersionComparator: "==", HashCheckingModeValues: []string{"sha256:123"}, Requirement: "hash1==1.0.0"}, 171 Plugins: []string{"python/requirements"}, 172 }, 173 { 174 // Hash checking mode. 175 Name: "hash2", 176 Version: "2.0.0", 177 PURLType: purl.TypePyPi, 178 Locations: []string{"testdata/hash/requirements.txt"}, 179 Metadata: &requirementsextractor.Metadata{VersionComparator: "==", HashCheckingModeValues: []string{"sha256:456"}, Requirement: "hash2==2.0.0"}, 180 Plugins: []string{"python/requirements"}, 181 }, 182 }, 183 } 184 sort.Slice(inv.Packages, func(i, j int) bool { 185 return inv.Packages[i].Name < inv.Packages[j].Name 186 }) 187 if diff := cmp.Diff(wantInventory, inv); diff != "" { 188 t.Errorf("%s.Enrich() diff (-want +got):\n%s", enricher.Name(), diff) 189 } 190 }