github.com/google/osv-scalibr@v0.4.1/guidedremediation/internal/resolution/resolve_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 resolution_test 16 17 import ( 18 "testing" 19 20 "deps.dev/util/maven" 21 "deps.dev/util/resolve" 22 "deps.dev/util/resolve/dep" 23 "deps.dev/util/resolve/schema" 24 "github.com/google/go-cmp/cmp" 25 "github.com/google/osv-scalibr/clients/clienttest" 26 "github.com/google/osv-scalibr/guidedremediation/internal/manifest" 27 mavenmanifest "github.com/google/osv-scalibr/guidedremediation/internal/manifest/maven" 28 "github.com/google/osv-scalibr/guidedremediation/internal/resolution" 29 "github.com/google/osv-scalibr/guidedremediation/options" 30 ) 31 32 // mockManifest represents a manifest file for testing purposes. 33 // It implements the manifest.Manifest interface. 34 type mockManifest struct { 35 name string 36 version string 37 system resolve.System 38 requirements []mockManifestRequirements 39 groups map[manifest.RequirementKey][]string 40 localManifests []mockManifest 41 ecosystemSpecific any 42 } 43 44 type mockManifestRequirements struct { 45 name string 46 version string 47 typ dep.Type 48 } 49 50 func (m mockManifest) FilePath() string { 51 return "" 52 } 53 54 func (m mockManifest) Root() resolve.Version { 55 return resolve.Version{ 56 VersionKey: resolve.VersionKey{ 57 PackageKey: resolve.PackageKey{ 58 Name: m.name, 59 System: m.system, 60 }, 61 Version: m.version, 62 VersionType: resolve.Concrete, 63 }, 64 } 65 } 66 67 func (m mockManifest) System() resolve.System { 68 return m.system 69 } 70 71 func (m mockManifest) Requirements() []resolve.RequirementVersion { 72 reqs := make([]resolve.RequirementVersion, len(m.requirements)) 73 for i, r := range m.requirements { 74 reqs[i] = resolve.RequirementVersion{ 75 VersionKey: resolve.VersionKey{ 76 PackageKey: resolve.PackageKey{ 77 Name: r.name, 78 System: m.system, 79 }, 80 Version: r.version, 81 VersionType: resolve.Requirement, 82 }, 83 Type: r.typ.Clone(), 84 } 85 } 86 return reqs 87 } 88 89 func (m mockManifest) Groups() map[manifest.RequirementKey][]string { 90 return m.groups 91 } 92 93 func (m mockManifest) LocalManifests() []manifest.Manifest { 94 ret := make([]manifest.Manifest, len(m.localManifests)) 95 for i, lm := range m.localManifests { 96 ret[i] = lm 97 } 98 return ret 99 } 100 101 func (m mockManifest) EcosystemSpecific() any { 102 return m.ecosystemSpecific 103 } 104 105 func (m mockManifest) Clone() manifest.Manifest { 106 return m 107 } 108 109 func (m mockManifest) PatchRequirement(resolve.RequirementVersion) error { 110 return nil 111 } 112 113 func TestResolveNPM(t *testing.T) { 114 aliasType := func(knownAs string) dep.Type { 115 var typ dep.Type 116 typ.AddAttr(dep.KnownAs, knownAs) 117 return typ 118 } 119 // Create a mock manifest with dependencies, including aliases and workspaces. 120 m := mockManifest{ 121 name: "test", 122 version: "1.0.0", 123 system: resolve.NPM, 124 requirements: []mockManifestRequirements{ 125 { 126 name: "pkg", 127 version: "^1.0.0", 128 }, 129 { 130 // Alias for "pkg" 131 name: "pkg", 132 version: "^2.0.0", 133 typ: aliasType("pkg-aliased"), 134 }, 135 { 136 // Workspace dependency 137 name: "one:workspace", 138 version: "*", 139 }, 140 { 141 // Workspace dependency 142 name: "two:workspace", 143 version: "*", 144 }, 145 }, 146 localManifests: []mockManifest{ 147 { 148 name: "one:workspace", 149 version: "1.1.1", 150 system: resolve.NPM, 151 requirements: []mockManifestRequirements{ 152 { 153 name: "two:workspace", 154 version: "*", 155 }, 156 { 157 name: "pkg", 158 version: "^2.0.0", 159 }, 160 }, 161 }, 162 { 163 name: "two:workspace", 164 version: "2.2.2", 165 system: resolve.NPM, 166 requirements: []mockManifestRequirements{ 167 { 168 name: "pkg", 169 version: "^1.0.0", 170 }, 171 }, 172 }, 173 }, 174 } 175 cl := clienttest.NewMockResolutionClient(t, "testdata/universe/npm.yaml") 176 177 got, err := resolution.Resolve(t.Context(), cl, m, options.ResolutionOptions{}) 178 if err != nil { 179 t.Fatal(err) 180 } 181 _ = got.Canon() 182 got.Duration = 0 // Ignore duration for comparison 183 184 want, err := schema.ParseResolve(` 185 test 1.0.0 186 p1: Selector | pkg@^1.0.0 1.0.0 187 p2: Selector | pkg2@^1.0.0 1.1.1 188 KnownAs pkg-aliased Selector | pkg@^2.0.0 2.0.0 189 $p2@^1.0.0 190 Selector | one:workspace@* 1.1.1 191 $ws2@* 192 Selector | pkg@^2.0.0 2.0.0 193 $p2@^1.0.0 194 ws2: Selector | two:workspace@* 2.2.2 195 $p1@^1.0.0 196 `, resolve.NPM) 197 if err != nil { 198 t.Fatal(err) 199 } 200 _ = want.Canon() 201 want.Duration = 0 202 203 if diff := cmp.Diff(want, got); diff != "" { 204 t.Errorf("Resolve() mismatch (-want +got):\n%s", diff) 205 } 206 } 207 208 func TestResolveMaven(t *testing.T) { 209 var managementType dep.Type 210 managementType.AddAttr(dep.MavenDependencyOrigin, "management") 211 m := mockManifest{ 212 name: "test:test", 213 version: "1.0.0", 214 system: resolve.Maven, 215 requirements: []mockManifestRequirements{ 216 { 217 name: "group:pkg1", 218 version: "1.0", 219 }, 220 { 221 // Dependency from dependencyManagement (used) 222 name: "group:pkg2", 223 version: "2.0", 224 typ: managementType.Clone(), 225 }, 226 { 227 // Dependency from dependencyManagement (unused) 228 name: "group:pkg3", 229 version: "3.0", 230 typ: managementType.Clone(), 231 }, 232 }, 233 ecosystemSpecific: mavenmanifest.ManifestSpecific{ 234 // Construct the OriginalRequirements that resolvePostProcess checks. 235 OriginalRequirements: []mavenmanifest.DependencyWithOrigin{ 236 { 237 Dependency: maven.Dependency{ 238 GroupID: "group", 239 ArtifactID: "pkg1", 240 Version: "1.0", 241 }, 242 Origin: "", 243 }, 244 { 245 Dependency: maven.Dependency{ 246 GroupID: "group", 247 ArtifactID: "pkg2", 248 Version: "2.0", 249 }, 250 Origin: "management", 251 }, 252 { 253 Dependency: maven.Dependency{ 254 GroupID: "group", 255 ArtifactID: "pkg3", 256 Version: "3.0", 257 }, 258 Origin: "management", 259 }, 260 }, 261 }, 262 } 263 cl := clienttest.NewMockResolutionClient(t, "testdata/universe/maven.yaml") 264 265 got, err := resolution.Resolve(t.Context(), cl, m, options.ResolutionOptions{MavenManagement: true}) 266 if err != nil { 267 t.Fatal(err) 268 } 269 _ = got.Canon() 270 got.Duration = 0 271 272 want, err := schema.ParseResolve(` 273 test:test 1.0.0 274 Selector | group:pkg1@1.0 1.0 275 Selector | group:pkg2@2.0 2.0 276 MavenDependencyOrigin management | group:pkg3@3.0 3.0 277 `, resolve.Maven) 278 if err != nil { 279 t.Fatal(err) 280 } 281 _ = want.Canon() 282 want.Duration = 0 283 284 if diff := cmp.Diff(want, got); diff != "" { 285 t.Errorf("Resolve() mismatch (-want +got):\n%s", diff) 286 } 287 }