github.com/google/osv-scalibr@v0.4.1/clients/resolution/pypi_registry_client_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 "os" 19 "testing" 20 21 "deps.dev/util/resolve" 22 "deps.dev/util/resolve/dep" 23 "deps.dev/util/resolve/version" 24 "github.com/google/go-cmp/cmp" 25 "github.com/google/osv-scalibr/clients/clienttest" 26 "github.com/google/osv-scalibr/clients/resolution" 27 ) 28 29 func TestVersions(t *testing.T) { 30 srv := clienttest.NewMockHTTPServer(t) 31 srv.SetResponse(t, "/beautifulsoup4/", []byte(` 32 { 33 "files": [ 34 { 35 "filename": "beautifulsoup4-0.0.1-py2.4-linux-x86_64.egg", 36 "yanked": "yanked" 37 }, 38 { 39 "filename": "beautifulsoup4-0.0.2.zip", 40 "yanked": "yanked" 41 }, 42 { 43 "core-metadata": false, 44 "data-dist-info-metadata": false, 45 "filename": "beautifulsoup4-4.0.1.tar.gz", 46 "hashes": { 47 "sha256": "dc6bc8e8851a1c590c8cc8f25915180fdcce116e268d1f37fa991d2686ea38de" 48 }, 49 "requires-python": null, 50 "size": 51024, 51 "upload-time": "2014-01-21T05:35:05.558877Z", 52 "url": "https://files.pythonhosted.org/packages/6f/be/99dcf74d947cc1e7abef5d0c4572abcb479c33ef791d94453a8fd7987d8f/beautifulsoup4-4.0.1.tar.gz", 53 "yanked": false 54 }, 55 { 56 "core-metadata": false, 57 "data-dist-info-metadata": false, 58 "filename": "beautifulsoup4-4.0.2.tar.gz", 59 "hashes": { 60 "sha256": "353792f8246a9551b232949fb14dce21d9b6ced9207bf9f4a69a4c4eb46c8127" 61 }, 62 "requires-python": null, 63 "size": 51240, 64 "upload-time": "2014-01-21T05:35:09.581933Z", 65 "url": "https://files.pythonhosted.org/packages/a0/75/db36172ea767dd2f0c9817a99e24f7e9b79c2ce63eb2f8b867284cc60daf/beautifulsoup4-4.0.2.tar.gz", 66 "yanked": false 67 }, 68 { 69 "core-metadata": { 70 "sha256": "524392d64a088e56a4232f50d6edb208dc03105394652acb72c6d5fa64c89f3e" 71 }, 72 "data-dist-info-metadata": { 73 "sha256": "524392d64a088e56a4232f50d6edb208dc03105394652acb72c6d5fa64c89f3e" 74 }, 75 "filename": "beautifulsoup4-4.12.3-py3-none-any.whl", 76 "hashes": { 77 "sha256": "b80878c9f40111313e55da8ba20bdba06d8fa3969fc68304167741bbf9e082ed" 78 }, 79 "requires-python": ">=3.6.0", 80 "size": 147925, 81 "upload-time": "2024-01-17T16:53:12.779164Z", 82 "url": "https://files.pythonhosted.org/packages/b1/fe/e8c672695b37eecc5cbf43e1d0638d88d66ba3a44c4d321c796f4e59167f/beautifulsoup4-4.12.3-py3-none-any.whl", 83 "yanked": false 84 }, 85 { 86 "core-metadata": false, 87 "data-dist-info-metadata": false, 88 "filename": "beautifulsoup4-4.12.3.tar.gz", 89 "hashes": { 90 "sha256": "74e3d1928edc070d21748185c46e3fb33490f22f52a3addee9aee0f4f7781051" 91 }, 92 "requires-python": ">=3.6.0", 93 "size": 581181, 94 "upload-time": "2024-01-17T16:53:17.902970Z", 95 "url": "https://files.pythonhosted.org/packages/b3/ca/824b1195773ce6166d388573fc106ce56d4a805bd7427b624e063596ec58/beautifulsoup4-4.12.3.tar.gz", 96 "yanked": false 97 }, 98 { 99 "core-metadata": { 100 "sha256": "d0aa787c2b55e5b0b3aff66f137cf33341c5e781cb87b4dc184cbb25c7ac0ab5" 101 }, 102 "data-dist-info-metadata": { 103 "sha256": "d0aa787c2b55e5b0b3aff66f137cf33341c5e781cb87b4dc184cbb25c7ac0ab5" 104 }, 105 "filename": "beautifulsoup4-4.13.0b2-py3-none-any.whl", 106 "hashes": { 107 "sha256": "7e05ad0b6c26108d9990e2235e8a9b4e2c03ead6f391ceb60347f8ebea6b80ba" 108 }, 109 "requires-python": ">=3.6.0", 110 "size": 179607, 111 "upload-time": "2024-03-20T13:00:33.355932Z", 112 "url": "https://files.pythonhosted.org/packages/14/7e/e4313dad823c3a0751c99b9bc0182b1dd19aea164ce7445e9a70429b9e92/beautifulsoup4-4.13.0b2-py3-none-any.whl", 113 "yanked": false 114 }, 115 { 116 "core-metadata": false, 117 "data-dist-info-metadata": false, 118 "filename": "beautifulsoup4-4.13.0b2.tar.gz", 119 "hashes": { 120 "sha256": "c684ddec071aa120819889aa9e8940f85c3f3cdaa08e23b9fa26510387897bd5" 121 }, 122 "requires-python": ">=3.6.0", 123 "size": 550258, 124 "upload-time": "2024-03-20T13:00:31.245327Z", 125 "url": "https://files.pythonhosted.org/packages/81/bd/c97d94e2b96f03d1c50bc9de04130e014eda89322ba604923e0c251eb02e/beautifulsoup4-4.13.0b2.tar.gz", 126 "yanked": false 127 }, 128 { 129 "filename": "beautifulsoup4-4.14.tar.gz", 130 "yanked": "yanked" 131 } 132 ], 133 "meta": { 134 "_last-serial": 22406780, 135 "api-version": "1.1" 136 }, 137 "name": "beautifulsoup4", 138 "versions": [ 139 "0.0.1", 140 "0.0.2", 141 "4.0.1", 142 "4.0.2", 143 "4.12.3", 144 "4.13.0b2", 145 "4.14" 146 ] 147 } 148 `)) 149 150 pk := resolve.PackageKey{ 151 System: resolve.PyPI, 152 Name: "beautifulsoup4", 153 } 154 client := resolution.NewPyPIRegistryClient(srv.URL, "") 155 got, err := client.Versions(t.Context(), pk) 156 if err != nil { 157 t.Fatalf("failed to get versions %v: %v", pk, err) 158 } 159 160 var yanked version.AttrSet 161 yanked.SetAttr(version.Blocked, "") 162 want := []resolve.Version{ 163 { 164 VersionKey: resolve.VersionKey{ 165 PackageKey: pk, 166 Version: "0.0.1", 167 VersionType: resolve.Concrete, 168 }, 169 AttrSet: yanked, 170 }, 171 { 172 VersionKey: resolve.VersionKey{ 173 PackageKey: pk, 174 Version: "0.0.2", 175 VersionType: resolve.Concrete, 176 }, 177 AttrSet: yanked, 178 }, 179 { 180 VersionKey: resolve.VersionKey{ 181 PackageKey: pk, 182 Version: "4.0.1", 183 VersionType: resolve.Concrete, 184 }, 185 }, 186 { 187 VersionKey: resolve.VersionKey{ 188 PackageKey: pk, 189 Version: "4.0.2", 190 VersionType: resolve.Concrete, 191 }, 192 }, 193 { 194 VersionKey: resolve.VersionKey{ 195 PackageKey: pk, 196 Version: "4.12.3", 197 VersionType: resolve.Concrete, 198 }, 199 }, 200 { 201 VersionKey: resolve.VersionKey{ 202 PackageKey: pk, 203 Version: "4.13.0b2", 204 VersionType: resolve.Concrete, 205 }, 206 }, 207 { 208 VersionKey: resolve.VersionKey{ 209 PackageKey: pk, 210 Version: "4.14", 211 VersionType: resolve.Concrete, 212 }, 213 AttrSet: yanked, 214 }, 215 } 216 if diff := cmp.Diff(want, got); diff != "" { 217 t.Fatalf("Versions(%v) mismatch (-want +got):\n%s", pk, diff) 218 } 219 for i, v := range got { 220 if !v.AttrSet.Equal(want[i].AttrSet) { 221 t.Errorf("AttrSet for package %s version %s mismatch", v.Name, v.Version) 222 } 223 } 224 } 225 226 func TestRequirements(t *testing.T) { 227 srv := clienttest.NewMockHTTPServer(t) 228 srv.SetResponse(t, "/beautifulsoup4/", []byte(` 229 { 230 "files": [ 231 { 232 "core-metadata": false, 233 "data-dist-info-metadata": false, 234 "filename": "beautifulsoup4-4.0.1.tar.gz", 235 "hashes": { 236 "sha256": "dc6bc8e8851a1c590c8cc8f25915180fdcce116e268d1f37fa991d2686ea38de" 237 }, 238 "requires-python": null, 239 "size": 51024, 240 "upload-time": "2014-01-21T05:35:05.558877Z", 241 "url": "https://files.pythonhosted.org/packages/6f/be/99dcf74d947cc1e7abef5d0c4572abcb479c33ef791d94453a8fd7987d8f/beautifulsoup4-4.0.1.tar.gz", 242 "yanked": false 243 }, 244 { 245 "core-metadata": false, 246 "data-dist-info-metadata": false, 247 "filename": "beautifulsoup4-4.0.2.tar.gz", 248 "hashes": { 249 "sha256": "353792f8246a9551b232949fb14dce21d9b6ced9207bf9f4a69a4c4eb46c8127" 250 }, 251 "requires-python": null, 252 "size": 51240, 253 "upload-time": "2014-01-21T05:35:09.581933Z", 254 "url": "https://files.pythonhosted.org/packages/a0/75/db36172ea767dd2f0c9817a99e24f7e9b79c2ce63eb2f8b867284cc60daf/beautifulsoup4-4.0.2.tar.gz", 255 "yanked": false 256 }, 257 { 258 "core-metadata": false, 259 "data-dist-info-metadata": false, 260 "filename": "beautifulsoup4-4.12.3.tar.gz", 261 "hashes": { 262 "sha256": "74e3d1928edc070d21748185c46e3fb33490f22f52a3addee9aee0f4f7781051" 263 }, 264 "requires-python": ">=3.6.0", 265 "size": 581181, 266 "upload-time": "2024-01-17T16:53:17.902970Z", 267 "url": "`+srv.URL+`/beautifulsoup4-4.12.3.tar.gz", 268 "yanked": false 269 }, 270 { 271 "core-metadata": { 272 "sha256": "524392d64a088e56a4232f50d6edb208dc03105394652acb72c6d5fa64c89f3e" 273 }, 274 "data-dist-info-metadata": { 275 "sha256": "524392d64a088e56a4232f50d6edb208dc03105394652acb72c6d5fa64c89f3e" 276 }, 277 "filename": "beautifulsoup4-4.12.3-py3-none-any.whl", 278 "hashes": { 279 "sha256": "b80878c9f40111313e55da8ba20bdba06d8fa3969fc68304167741bbf9e082ed" 280 }, 281 "requires-python": ">=3.6.0", 282 "size": 147925, 283 "upload-time": "2024-01-17T16:53:12.779164Z", 284 "url": "`+srv.URL+`/beautifulsoup4-4.12.3-py3-none-any.whl", 285 "yanked": false 286 }, 287 { 288 "core-metadata": { 289 "sha256": "d0aa787c2b55e5b0b3aff66f137cf33341c5e781cb87b4dc184cbb25c7ac0ab5" 290 }, 291 "data-dist-info-metadata": { 292 "sha256": "d0aa787c2b55e5b0b3aff66f137cf33341c5e781cb87b4dc184cbb25c7ac0ab5" 293 }, 294 "filename": "beautifulsoup4-4.13.0b2-py3-none-any.whl", 295 "hashes": { 296 "sha256": "7e05ad0b6c26108d9990e2235e8a9b4e2c03ead6f391ceb60347f8ebea6b80ba" 297 }, 298 "requires-python": ">=3.6.0", 299 "size": 179607, 300 "upload-time": "2024-03-20T13:00:33.355932Z", 301 "url": "https://files.pythonhosted.org/packages/14/7e/e4313dad823c3a0751c99b9bc0182b1dd19aea164ce7445e9a70429b9e92/beautifulsoup4-4.13.0b2-py3-none-any.whl", 302 "yanked": false 303 }, 304 { 305 "core-metadata": false, 306 "data-dist-info-metadata": false, 307 "filename": "beautifulsoup4-4.13.0b2.tar.gz", 308 "hashes": { 309 "sha256": "c684ddec071aa120819889aa9e8940f85c3f3cdaa08e23b9fa26510387897bd5" 310 }, 311 "requires-python": ">=3.6.0", 312 "size": 550258, 313 "upload-time": "2024-03-20T13:00:31.245327Z", 314 "url": "https://files.pythonhosted.org/packages/81/bd/c97d94e2b96f03d1c50bc9de04130e014eda89322ba604923e0c251eb02e/beautifulsoup4-4.13.0b2.tar.gz", 315 "yanked": false 316 }, 317 { 318 "filename": "beautifulsoup4-4.14.tar.gz", 319 "yanked": "yanked" 320 } 321 ], 322 "meta": { 323 "_last-serial": 22406780, 324 "api-version": "1.1" 325 }, 326 "name": "beautifulsoup4", 327 "versions": [ 328 "4.0.1", 329 "4.0.2", 330 "4.12.3", 331 "4.13.0b2", 332 "4.14" 333 ] 334 } 335 `)) 336 337 content, err := os.ReadFile("testdata/beautifulsoup4-4.12.3-py3-none-any.whl") 338 if err != nil { 339 t.Fatalf("Error reading content from test file: %v", err) 340 } 341 srv.SetResponse(t, "/beautifulsoup4-4.12.3-py3-none-any.whl", content) 342 343 vk := resolve.VersionKey{ 344 PackageKey: resolve.PackageKey{ 345 System: resolve.PyPI, 346 Name: "beautifulsoup4", 347 }, 348 Version: "4.12.3", 349 VersionType: resolve.Concrete, 350 } 351 client := resolution.NewPyPIRegistryClient(srv.URL, "") 352 got, err := client.Requirements(t.Context(), vk) 353 if err != nil { 354 t.Fatalf("failed to get requirements %v: %v", vk, err) 355 } 356 357 depType := func(extra string) dep.Type { 358 typ := dep.NewType() 359 typ.AddAttr(dep.Environment, "extra == '"+extra+"'") 360 return typ 361 } 362 363 want := []resolve.RequirementVersion{ 364 { 365 VersionKey: resolve.VersionKey{ 366 PackageKey: resolve.PackageKey{ 367 System: resolve.PyPI, 368 Name: "soupsieve", 369 }, 370 Version: ">1.2", 371 VersionType: resolve.Requirement, 372 }, 373 }, 374 { 375 VersionKey: resolve.VersionKey{ 376 PackageKey: resolve.PackageKey{ 377 System: resolve.PyPI, 378 Name: "cchardet", 379 }, 380 Version: "", 381 VersionType: resolve.Requirement, 382 }, 383 Type: depType("cchardet"), 384 }, 385 { 386 VersionKey: resolve.VersionKey{ 387 PackageKey: resolve.PackageKey{ 388 System: resolve.PyPI, 389 Name: "chardet", 390 }, 391 Version: "", 392 VersionType: resolve.Requirement, 393 }, 394 Type: depType("chardet"), 395 }, 396 { 397 VersionKey: resolve.VersionKey{ 398 PackageKey: resolve.PackageKey{ 399 System: resolve.PyPI, 400 Name: "charset-normalizer", 401 }, 402 Version: "", 403 VersionType: resolve.Requirement, 404 }, 405 Type: depType("charset-normalizer"), 406 }, 407 { 408 VersionKey: resolve.VersionKey{ 409 PackageKey: resolve.PackageKey{ 410 System: resolve.PyPI, 411 Name: "html5lib", 412 }, 413 Version: "", 414 VersionType: resolve.Requirement, 415 }, 416 Type: depType("html5lib"), 417 }, 418 { 419 VersionKey: resolve.VersionKey{ 420 PackageKey: resolve.PackageKey{ 421 System: resolve.PyPI, 422 Name: "lxml", 423 }, 424 Version: "", 425 VersionType: resolve.Requirement, 426 }, 427 Type: depType("lxml"), 428 }, 429 } 430 if diff := cmp.Diff(want, got); diff != "" { 431 t.Errorf("Requirements(%v) mismatch (-want +got):\n%s", vk, diff) 432 } 433 }