github.com/google/osv-scalibr@v0.4.1/clients/datasource/pypi_registry_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 datasource_test 16 17 import ( 18 "net/url" 19 "os" 20 "path/filepath" 21 "testing" 22 23 "github.com/google/go-cmp/cmp" 24 "github.com/google/osv-scalibr/clients/clienttest" 25 "github.com/google/osv-scalibr/clients/datasource" 26 "github.com/google/osv-scalibr/clients/internal/pypi" 27 ) 28 29 const jsonResp = `{ 30 "files": [ 31 { 32 "core-metadata": false, 33 "data-dist-info-metadata": false, 34 "filename": "beautifulsoup4-4.0.1.tar.gz", 35 "hashes": { 36 "sha256": "dc6bc8e8851a1c590c8cc8f25915180fdcce116e268d1f37fa991d2686ea38de" 37 }, 38 "requires-python": null, 39 "size": 51024, 40 "upload-time": "2014-01-21T05:35:05.558877Z", 41 "url": "https://files.pythonhosted.org/packages/6f/be/99dcf74d947cc1e7abef5d0c4572abcb479c33ef791d94453a8fd7987d8f/beautifulsoup4-4.0.1.tar.gz", 42 "yanked": false 43 }, 44 { 45 "core-metadata": false, 46 "data-dist-info-metadata": false, 47 "filename": "beautifulsoup4-4.0.2.tar.gz", 48 "hashes": { 49 "sha256": "353792f8246a9551b232949fb14dce21d9b6ced9207bf9f4a69a4c4eb46c8127" 50 }, 51 "requires-python": null, 52 "size": 51240, 53 "upload-time": "2014-01-21T05:35:09.581933Z", 54 "url": "https://files.pythonhosted.org/packages/a0/75/db36172ea767dd2f0c9817a99e24f7e9b79c2ce63eb2f8b867284cc60daf/beautifulsoup4-4.0.2.tar.gz", 55 "yanked": false 56 }, 57 { 58 "core-metadata": { 59 "sha256": "524392d64a088e56a4232f50d6edb208dc03105394652acb72c6d5fa64c89f3e" 60 }, 61 "data-dist-info-metadata": { 62 "sha256": "524392d64a088e56a4232f50d6edb208dc03105394652acb72c6d5fa64c89f3e" 63 }, 64 "filename": "beautifulsoup4-4.12.3-py3-none-any.whl", 65 "hashes": { 66 "sha256": "b80878c9f40111313e55da8ba20bdba06d8fa3969fc68304167741bbf9e082ed" 67 }, 68 "requires-python": ">=3.6.0", 69 "size": 147925, 70 "upload-time": "2024-01-17T16:53:12.779164Z", 71 "url": "https://files.pythonhosted.org/packages/b1/fe/e8c672695b37eecc5cbf43e1d0638d88d66ba3a44c4d321c796f4e59167f/beautifulsoup4-4.12.3-py3-none-any.whl", 72 "yanked": false 73 }, 74 { 75 "core-metadata": false, 76 "data-dist-info-metadata": false, 77 "filename": "beautifulsoup4-4.12.3.tar.gz", 78 "hashes": { 79 "sha256": "74e3d1928edc070d21748185c46e3fb33490f22f52a3addee9aee0f4f7781051" 80 }, 81 "requires-python": ">=3.6.0", 82 "size": 581181, 83 "upload-time": "2024-01-17T16:53:17.902970Z", 84 "url": "https://files.pythonhosted.org/packages/b3/ca/824b1195773ce6166d388573fc106ce56d4a805bd7427b624e063596ec58/beautifulsoup4-4.12.3.tar.gz", 85 "yanked": false 86 }, 87 { 88 "core-metadata": { 89 "sha256": "d0aa787c2b55e5b0b3aff66f137cf33341c5e781cb87b4dc184cbb25c7ac0ab5" 90 }, 91 "data-dist-info-metadata": { 92 "sha256": "d0aa787c2b55e5b0b3aff66f137cf33341c5e781cb87b4dc184cbb25c7ac0ab5" 93 }, 94 "filename": "beautifulsoup4-4.13.0b2-py3-none-any.whl", 95 "hashes": { 96 "sha256": "7e05ad0b6c26108d9990e2235e8a9b4e2c03ead6f391ceb60347f8ebea6b80ba" 97 }, 98 "requires-python": ">=3.6.0", 99 "size": 179607, 100 "upload-time": "2024-03-20T13:00:33.355932Z", 101 "url": "https://files.pythonhosted.org/packages/14/7e/e4313dad823c3a0751c99b9bc0182b1dd19aea164ce7445e9a70429b9e92/beautifulsoup4-4.13.0b2-py3-none-any.whl", 102 "yanked": false 103 }, 104 { 105 "core-metadata": false, 106 "data-dist-info-metadata": false, 107 "filename": "beautifulsoup4-4.13.0b2.tar.gz", 108 "hashes": { 109 "sha256": "c684ddec071aa120819889aa9e8940f85c3f3cdaa08e23b9fa26510387897bd5" 110 }, 111 "requires-python": ">=3.6.0", 112 "size": 550258, 113 "upload-time": "2024-03-20T13:00:31.245327Z", 114 "url": "https://files.pythonhosted.org/packages/81/bd/c97d94e2b96f03d1c50bc9de04130e014eda89322ba604923e0c251eb02e/beautifulsoup4-4.13.0b2.tar.gz", 115 "yanked": false 116 }, 117 { 118 "filename": "beautifulsoup4-4.14.tar.gz", 119 "yanked": "yanked" 120 } 121 ], 122 "meta": { 123 "_last-serial": 22406780, 124 "api-version": "1.1" 125 }, 126 "name": "beautifulsoup4", 127 "versions": [ 128 "4.0.1", 129 "4.0.2", 130 "4.12.3", 131 "4.13.0b2", 132 "4.14" 133 ] 134 }` 135 136 func TestGetVersions(t *testing.T) { 137 srv := clienttest.NewMockHTTPServer(t) 138 client := datasource.NewPyPIRegistryAPIClient(srv.URL, "") 139 srv.SetResponse(t, "/beautifulsoup4/", []byte(jsonResp)) 140 141 got, err := client.GetIndex(t.Context(), "beautifulsoup4") 142 if err != nil { 143 t.Fatalf("failed to get versions of PyPI project %s: %v", "beautifulsoup4", err) 144 } 145 want := pypi.IndexResponse{ 146 Name: "beautifulsoup4", 147 Files: []pypi.File{ 148 { 149 Name: "beautifulsoup4-4.0.1.tar.gz", 150 URL: "https://files.pythonhosted.org/packages/6f/be/99dcf74d947cc1e7abef5d0c4572abcb479c33ef791d94453a8fd7987d8f/beautifulsoup4-4.0.1.tar.gz", 151 }, 152 { 153 Name: "beautifulsoup4-4.0.2.tar.gz", 154 URL: "https://files.pythonhosted.org/packages/a0/75/db36172ea767dd2f0c9817a99e24f7e9b79c2ce63eb2f8b867284cc60daf/beautifulsoup4-4.0.2.tar.gz", 155 }, 156 { 157 Name: "beautifulsoup4-4.12.3-py3-none-any.whl", 158 URL: "https://files.pythonhosted.org/packages/b1/fe/e8c672695b37eecc5cbf43e1d0638d88d66ba3a44c4d321c796f4e59167f/beautifulsoup4-4.12.3-py3-none-any.whl", 159 }, 160 { 161 Name: "beautifulsoup4-4.12.3.tar.gz", 162 URL: "https://files.pythonhosted.org/packages/b3/ca/824b1195773ce6166d388573fc106ce56d4a805bd7427b624e063596ec58/beautifulsoup4-4.12.3.tar.gz", 163 }, 164 { 165 Name: "beautifulsoup4-4.13.0b2-py3-none-any.whl", 166 URL: "https://files.pythonhosted.org/packages/14/7e/e4313dad823c3a0751c99b9bc0182b1dd19aea164ce7445e9a70429b9e92/beautifulsoup4-4.13.0b2-py3-none-any.whl", 167 }, 168 { 169 Name: "beautifulsoup4-4.13.0b2.tar.gz", 170 URL: "https://files.pythonhosted.org/packages/81/bd/c97d94e2b96f03d1c50bc9de04130e014eda89322ba604923e0c251eb02e/beautifulsoup4-4.13.0b2.tar.gz", 171 }, 172 { 173 Name: "beautifulsoup4-4.14.tar.gz", 174 Yanked: pypi.Yanked{Value: true}, 175 }, 176 }, 177 Versions: []string{ 178 "4.0.1", 179 "4.0.2", 180 "4.12.3", 181 "4.13.0b2", 182 "4.14", 183 }, 184 } 185 if diff := cmp.Diff(want, got); diff != "" { 186 t.Errorf("GetIndex(%s) mismatch (-want +got):\n%s", "beautifulsoup4", diff) 187 } 188 } 189 190 func TestPyPILocalRegistry(t *testing.T) { 191 tempDir := t.TempDir() 192 srv := clienttest.NewMockHTTPServer(t) 193 client := datasource.NewPyPIRegistryAPIClient(srv.URL, tempDir) 194 srv.SetResponse(t, "/beautifulsoup4/", []byte(jsonResp)) 195 196 _, err := client.GetIndex(t.Context(), "beautifulsoup4") 197 if err != nil { 198 t.Fatalf("failed to get versions of PyPI project %s: %v", "beautifulsoup4", err) 199 } 200 201 // Check that the JSON response is stored locally. 202 parsed, err := url.Parse(srv.URL) 203 if err != nil { 204 t.Fatalf("failed to parse URL %s: %v", srv.URL, err) 205 } 206 filePath := filepath.Join(tempDir, "pypi", parsed.Hostname(), "beautifulsoup4") 207 content, err := os.ReadFile(filePath) 208 if err != nil { 209 t.Fatalf("failed to read file: %v", err) 210 } 211 if string(content) != jsonResp { 212 t.Errorf("unexpected file content: got %s, want %s", string(content), jsonResp) 213 } 214 }