|
17 | 17 | InvalidJSONError, |
18 | 18 | ServerError, |
19 | 19 | _run, |
| 20 | + dockerhub_ecosystem, |
20 | 21 | download, |
21 | 22 | npm_ecosystem, |
22 | | - parse_npm, |
| 23 | + parse_packages_ecosystems_source, |
23 | 24 | parse_pypi, |
24 | 25 | ) |
25 | 26 |
|
@@ -70,6 +71,18 @@ def patch_npm_ecosystem(data: dict[str, Any]) -> Iterator[None]: |
70 | 71 | yield |
71 | 72 |
|
72 | 73 |
|
| 74 | +@contextmanager |
| 75 | +def patch_dockerhub_ecosystem(data: dict[str, Any]) -> Iterator[None]: |
| 76 | + """Context manager that temporarily modifies the npm ecosystem configuration for testing.""" |
| 77 | + with ( |
| 78 | + patch.dict( |
| 79 | + ECOSYSTEMS, |
| 80 | + {"dockerhub": Ecosystem(**dockerhub_ecosystem.__dict__ | data)}, |
| 81 | + ), |
| 82 | + ): |
| 83 | + yield |
| 84 | + |
| 85 | + |
73 | 86 | @freeze_time("2025-01-01") |
74 | 87 | class TestDownload: |
75 | 88 | def test_pypi_download(self) -> None: |
@@ -141,7 +154,7 @@ def test_invalid_pypi_json_format(self) -> None: |
141 | 154 | def test_invalid_npm_json_format(self) -> None: |
142 | 155 | """Test that InvalidJSONError is raised when npm JSON data has invalid format.""" |
143 | 156 | with pytest.raises(InvalidJSONError): |
144 | | - parse_npm([{"key": "val"}]) |
| 157 | + parse_packages_ecosystems_source([{"key": "val"}]) |
145 | 158 |
|
146 | 159 | def test_invalid_downloaded_json(self) -> None: |
147 | 160 | """Test that InvalidJSONError is raised when downloaded JSON cannot be parsed.""" |
@@ -219,6 +232,56 @@ def test_npm_download_with_multiple_pages(self) -> None: |
219 | 232 | assert set(m_save.call_args[0][0]["packages"]) == {"lodash", "@aws/sdk", "react", "express"} |
220 | 233 | assert m_save.call_args[0][1] == m_open().__enter__() |
221 | 234 |
|
| 235 | + def test_dockerhub_download_with_multiple_pages(self) -> None: |
| 236 | + """Test that the script will iterate through pages if provided.""" |
| 237 | + page1_data = [ |
| 238 | + {"name": "sundeepm1/weatherapi", "downloads": 12345}, |
| 239 | + {"name": "hitesh25/jenkins_argo", "downloads": 98765}, |
| 240 | + ] |
| 241 | + page2_data = [ |
| 242 | + {"name": "jchensg/sg-support-integration", "downloads": 87654}, |
| 243 | + ] |
| 244 | + |
| 245 | + with ( |
| 246 | + patch_client(None) as m_client, # We'll configure the side_effect below |
| 247 | + patch_save_to_file() as m_save, |
| 248 | + patch_open_file() as m_open, |
| 249 | + patch_dockerhub_ecosystem({"pages": 2}), |
| 250 | + ): |
| 251 | + # Configure the mock to return different data for each call |
| 252 | + mock_responses = [] |
| 253 | + for data in [page1_data, page2_data]: |
| 254 | + mock_response = Mock() |
| 255 | + mock_response.json.return_value = data |
| 256 | + mock_responses.append(mock_response) |
| 257 | + |
| 258 | + m_client.side_effect = mock_responses |
| 259 | + |
| 260 | + _run("dockerhub") |
| 261 | + |
| 262 | + assert m_client.call_count == 2 |
| 263 | + |
| 264 | + assert m_client.call_args_list == [ |
| 265 | + call( |
| 266 | + "https://packages.ecosyste.ms/api/v1/registries/hub.docker.com/packages", |
| 267 | + params={"per_page": 100, "sort": "downloads", "page": 1}, |
| 268 | + ), |
| 269 | + call( |
| 270 | + "https://packages.ecosyste.ms/api/v1/registries/hub.docker.com/packages", |
| 271 | + params={"per_page": 100, "sort": "downloads", "page": 2}, |
| 272 | + ), |
| 273 | + ] |
| 274 | + |
| 275 | + # Verify that all packages from all pages were collected |
| 276 | + assert m_save.call_count == 1 |
| 277 | + assert m_save.call_args[0][0]["date"] == "2025-01-01T00:00:00+00:00" |
| 278 | + assert set(m_save.call_args[0][0]["packages"]) == { |
| 279 | + "sundeepm1/weatherapi", |
| 280 | + "hitesh25/jenkins_argo", |
| 281 | + "jchensg/sg-support-integration", |
| 282 | + } |
| 283 | + assert m_save.call_args[0][1] == m_open().__enter__() |
| 284 | + |
222 | 285 |
|
223 | 286 | class TestCli: |
224 | 287 | def test_non_existing_ecosystem_error(self) -> None: |
|
0 commit comments