|
15 | 15 | get_result_report, |
16 | 16 | get_result_setting_params, |
17 | 17 | ) |
| 18 | +from app.services.s3 import S3ConfigurationError, S3ServiceError |
| 19 | +from app.services.seqera_errors import SeqeraAPIError, SeqeraConfigurationError |
18 | 20 |
|
19 | 21 |
|
20 | 22 | @pytest.mark.asyncio |
@@ -85,6 +87,24 @@ async def test_get_result_setting_params_falls_back_to_local_fields(test_db): |
85 | 87 | } |
86 | 88 |
|
87 | 89 |
|
| 90 | +@pytest.mark.asyncio |
| 91 | +async def test_get_result_setting_params_returns_404_for_missing_owned_run(test_db): |
| 92 | + user = AppUser( |
| 93 | + id=uuid4(), |
| 94 | + auth0_user_id="auth0|results-user-setting-missing", |
| 95 | + name="Results User Missing", |
| 96 | + email="results-missing@example.com", |
| 97 | + ) |
| 98 | + test_db.add(user) |
| 99 | + test_db.commit() |
| 100 | + |
| 101 | + with pytest.raises(HTTPException) as exc_info: |
| 102 | + await get_result_setting_params("wf-setting-missing", user.id, test_db) |
| 103 | + |
| 104 | + assert exc_info.value.status_code == 404 |
| 105 | + assert exc_info.value.detail == "Job not found" |
| 106 | + |
| 107 | + |
88 | 108 | @pytest.mark.asyncio |
89 | 109 | async def test_get_result_logs_returns_formatted_entries(test_db): |
90 | 110 | user = AppUser( |
@@ -154,6 +174,105 @@ async def test_get_result_logs_returns_404_for_missing_owned_run(test_db): |
154 | 174 | assert exc_info.value.detail == "Job not found" |
155 | 175 |
|
156 | 176 |
|
| 177 | +@pytest.mark.asyncio |
| 178 | +async def test_get_result_logs_handles_top_level_payload_and_seqera_defaults(test_db): |
| 179 | + user = AppUser( |
| 180 | + id=uuid4(), |
| 181 | + auth0_user_id="auth0|results-user-logs-top-level", |
| 182 | + name="Results User Logs", |
| 183 | + email="results-logs@example.com", |
| 184 | + ) |
| 185 | + run = WorkflowRun( |
| 186 | + id=uuid4(), |
| 187 | + owner_user_id=user.id, |
| 188 | + seqera_run_id="wf-logs-top-level", |
| 189 | + work_dir="/tmp/wf-logs-top-level", |
| 190 | + ) |
| 191 | + test_db.add_all([user, run]) |
| 192 | + test_db.commit() |
| 193 | + |
| 194 | + payload = { |
| 195 | + "truncated": 1, |
| 196 | + "pending": 0, |
| 197 | + "message": None, |
| 198 | + "rewindToken": None, |
| 199 | + "forwardToken": None, |
| 200 | + "downloads": "not-a-list", |
| 201 | + "entries": None, |
| 202 | + } |
| 203 | + |
| 204 | + with patch( |
| 205 | + "app.routes.workflow.results.get_workflow_logs_raw", |
| 206 | + new=AsyncMock(return_value=payload), |
| 207 | + ): |
| 208 | + result = await get_result_logs("wf-logs-top-level", user.id, test_db) |
| 209 | + |
| 210 | + assert result.truncated is True |
| 211 | + assert result.pending is False |
| 212 | + assert result.message == "" |
| 213 | + assert result.rewindToken == "" |
| 214 | + assert result.forwardToken == "" |
| 215 | + assert result.downloads == [] |
| 216 | + assert result.entries == [] |
| 217 | + assert result.formattedEntries == [] |
| 218 | + |
| 219 | + |
| 220 | +@pytest.mark.asyncio |
| 221 | +async def test_get_result_logs_maps_seqera_configuration_error_to_500(test_db): |
| 222 | + user = AppUser( |
| 223 | + id=uuid4(), |
| 224 | + auth0_user_id="auth0|results-user-logs-config-error", |
| 225 | + name="Results User Logs Config", |
| 226 | + email="results-logs-config@example.com", |
| 227 | + ) |
| 228 | + run = WorkflowRun( |
| 229 | + id=uuid4(), |
| 230 | + owner_user_id=user.id, |
| 231 | + seqera_run_id="wf-logs-config-error", |
| 232 | + work_dir="/tmp/wf-logs-config-error", |
| 233 | + ) |
| 234 | + test_db.add_all([user, run]) |
| 235 | + test_db.commit() |
| 236 | + |
| 237 | + with patch( |
| 238 | + "app.routes.workflow.results.get_workflow_logs_raw", |
| 239 | + new=AsyncMock(side_effect=SeqeraConfigurationError("missing seqera config")), |
| 240 | + ): |
| 241 | + with pytest.raises(HTTPException) as exc_info: |
| 242 | + await get_result_logs("wf-logs-config-error", user.id, test_db) |
| 243 | + |
| 244 | + assert exc_info.value.status_code == 500 |
| 245 | + assert exc_info.value.detail == "missing seqera config" |
| 246 | + |
| 247 | + |
| 248 | +@pytest.mark.asyncio |
| 249 | +async def test_get_result_logs_maps_seqera_api_error_to_502(test_db): |
| 250 | + user = AppUser( |
| 251 | + id=uuid4(), |
| 252 | + auth0_user_id="auth0|results-user-logs-api-error", |
| 253 | + name="Results User Logs API", |
| 254 | + email="results-logs-api@example.com", |
| 255 | + ) |
| 256 | + run = WorkflowRun( |
| 257 | + id=uuid4(), |
| 258 | + owner_user_id=user.id, |
| 259 | + seqera_run_id="wf-logs-api-error", |
| 260 | + work_dir="/tmp/wf-logs-api-error", |
| 261 | + ) |
| 262 | + test_db.add_all([user, run]) |
| 263 | + test_db.commit() |
| 264 | + |
| 265 | + with patch( |
| 266 | + "app.routes.workflow.results.get_workflow_logs_raw", |
| 267 | + new=AsyncMock(side_effect=SeqeraAPIError("seqera upstream failed")), |
| 268 | + ): |
| 269 | + with pytest.raises(HTTPException) as exc_info: |
| 270 | + await get_result_logs("wf-logs-api-error", user.id, test_db) |
| 271 | + |
| 272 | + assert exc_info.value.status_code == 502 |
| 273 | + assert exc_info.value.detail == "seqera upstream failed" |
| 274 | + |
| 275 | + |
157 | 276 | @pytest.mark.asyncio |
158 | 277 | async def test_get_result_downloads_returns_presigned_links_for_tracked_outputs(test_db): |
159 | 278 | user = AppUser( |
@@ -265,6 +384,80 @@ def _list_side_effect(prefix: str, file_extension=None): |
265 | 384 | assert mock_list.await_count >= 1 |
266 | 385 |
|
267 | 386 |
|
| 387 | +@pytest.mark.asyncio |
| 388 | +async def test_get_result_downloads_returns_404_for_missing_owned_run(test_db): |
| 389 | + user = AppUser( |
| 390 | + id=uuid4(), |
| 391 | + auth0_user_id="auth0|results-user-downloads-missing", |
| 392 | + name="Results User Downloads Missing", |
| 393 | + email="results-downloads-missing@example.com", |
| 394 | + ) |
| 395 | + test_db.add(user) |
| 396 | + test_db.commit() |
| 397 | + |
| 398 | + with pytest.raises(HTTPException) as exc_info: |
| 399 | + await get_result_downloads("wf-downloads-missing", user.id, test_db) |
| 400 | + |
| 401 | + assert exc_info.value.status_code == 404 |
| 402 | + assert exc_info.value.detail == "Job not found" |
| 403 | + |
| 404 | + |
| 405 | +@pytest.mark.asyncio |
| 406 | +async def test_get_result_downloads_maps_s3_configuration_error_to_500(test_db): |
| 407 | + user = AppUser( |
| 408 | + id=uuid4(), |
| 409 | + auth0_user_id="auth0|results-user-downloads-config-error", |
| 410 | + name="Results User Downloads Config", |
| 411 | + email="results-downloads-config@example.com", |
| 412 | + ) |
| 413 | + run = WorkflowRun( |
| 414 | + id=uuid4(), |
| 415 | + owner_user_id=user.id, |
| 416 | + seqera_run_id="wf-downloads-config-error", |
| 417 | + work_dir="/tmp/wf-downloads-config-error", |
| 418 | + ) |
| 419 | + test_db.add_all([user, run]) |
| 420 | + test_db.commit() |
| 421 | + |
| 422 | + with patch( |
| 423 | + "app.routes.workflow.results.get_result_output_downloads", |
| 424 | + new=AsyncMock(side_effect=S3ConfigurationError("missing s3 config")), |
| 425 | + ): |
| 426 | + with pytest.raises(HTTPException) as exc_info: |
| 427 | + await get_result_downloads("wf-downloads-config-error", user.id, test_db) |
| 428 | + |
| 429 | + assert exc_info.value.status_code == 500 |
| 430 | + assert exc_info.value.detail == "missing s3 config" |
| 431 | + |
| 432 | + |
| 433 | +@pytest.mark.asyncio |
| 434 | +async def test_get_result_downloads_maps_s3_service_error_to_502(test_db): |
| 435 | + user = AppUser( |
| 436 | + id=uuid4(), |
| 437 | + auth0_user_id="auth0|results-user-downloads-service-error", |
| 438 | + name="Results User Downloads Service", |
| 439 | + email="results-downloads-service@example.com", |
| 440 | + ) |
| 441 | + run = WorkflowRun( |
| 442 | + id=uuid4(), |
| 443 | + owner_user_id=user.id, |
| 444 | + seqera_run_id="wf-downloads-service-error", |
| 445 | + work_dir="/tmp/wf-downloads-service-error", |
| 446 | + ) |
| 447 | + test_db.add_all([user, run]) |
| 448 | + test_db.commit() |
| 449 | + |
| 450 | + with patch( |
| 451 | + "app.routes.workflow.results.get_result_output_downloads", |
| 452 | + new=AsyncMock(side_effect=S3ServiceError("s3 upstream failed")), |
| 453 | + ): |
| 454 | + with pytest.raises(HTTPException) as exc_info: |
| 455 | + await get_result_downloads("wf-downloads-service-error", user.id, test_db) |
| 456 | + |
| 457 | + assert exc_info.value.status_code == 502 |
| 458 | + assert exc_info.value.detail == "s3 upstream failed" |
| 459 | + |
| 460 | + |
268 | 461 | @pytest.mark.asyncio |
269 | 462 | async def test_get_result_report_returns_single_presigned_html_for_tracked_output(test_db): |
270 | 463 | user = AppUser( |
@@ -419,3 +612,104 @@ def _list_side_effect(prefix: str, file_extension=None): |
419 | 612 | test_db.query(RunOutput).filter_by(run_id=run.id, s3_object_id=real_key).one_or_none() |
420 | 613 | ) |
421 | 614 | assert synced_link is not None |
| 615 | + |
| 616 | + |
| 617 | +@pytest.mark.asyncio |
| 618 | +async def test_get_result_report_returns_404_for_missing_owned_run(test_db): |
| 619 | + user = AppUser( |
| 620 | + id=uuid4(), |
| 621 | + auth0_user_id="auth0|results-user-report-missing", |
| 622 | + name="Results User Report Missing", |
| 623 | + email="results-report-missing@example.com", |
| 624 | + ) |
| 625 | + test_db.add(user) |
| 626 | + test_db.commit() |
| 627 | + |
| 628 | + with pytest.raises(HTTPException) as exc_info: |
| 629 | + await get_result_report("wf-report-missing", user.id, test_db) |
| 630 | + |
| 631 | + assert exc_info.value.status_code == 404 |
| 632 | + assert exc_info.value.detail == "Job not found" |
| 633 | + |
| 634 | + |
| 635 | +@pytest.mark.asyncio |
| 636 | +async def test_get_result_report_maps_s3_configuration_error_to_500(test_db): |
| 637 | + user = AppUser( |
| 638 | + id=uuid4(), |
| 639 | + auth0_user_id="auth0|results-user-report-config-error", |
| 640 | + name="Results User Report Config", |
| 641 | + email="results-report-config@example.com", |
| 642 | + ) |
| 643 | + run = WorkflowRun( |
| 644 | + id=uuid4(), |
| 645 | + owner_user_id=user.id, |
| 646 | + seqera_run_id="wf-report-config-error", |
| 647 | + work_dir="/tmp/wf-report-config-error", |
| 648 | + ) |
| 649 | + test_db.add_all([user, run]) |
| 650 | + test_db.commit() |
| 651 | + |
| 652 | + with patch( |
| 653 | + "app.routes.workflow.results.get_result_report_download", |
| 654 | + new=AsyncMock(side_effect=S3ConfigurationError("missing s3 config")), |
| 655 | + ): |
| 656 | + with pytest.raises(HTTPException) as exc_info: |
| 657 | + await get_result_report("wf-report-config-error", user.id, test_db) |
| 658 | + |
| 659 | + assert exc_info.value.status_code == 500 |
| 660 | + assert exc_info.value.detail == "missing s3 config" |
| 661 | + |
| 662 | + |
| 663 | +@pytest.mark.asyncio |
| 664 | +async def test_get_result_report_maps_s3_service_error_to_502(test_db): |
| 665 | + user = AppUser( |
| 666 | + id=uuid4(), |
| 667 | + auth0_user_id="auth0|results-user-report-service-error", |
| 668 | + name="Results User Report Service", |
| 669 | + email="results-report-service@example.com", |
| 670 | + ) |
| 671 | + run = WorkflowRun( |
| 672 | + id=uuid4(), |
| 673 | + owner_user_id=user.id, |
| 674 | + seqera_run_id="wf-report-service-error", |
| 675 | + work_dir="/tmp/wf-report-service-error", |
| 676 | + ) |
| 677 | + test_db.add_all([user, run]) |
| 678 | + test_db.commit() |
| 679 | + |
| 680 | + with patch( |
| 681 | + "app.routes.workflow.results.get_result_report_download", |
| 682 | + new=AsyncMock(side_effect=S3ServiceError("s3 upstream failed")), |
| 683 | + ): |
| 684 | + with pytest.raises(HTTPException) as exc_info: |
| 685 | + await get_result_report("wf-report-service-error", user.id, test_db) |
| 686 | + |
| 687 | + assert exc_info.value.status_code == 502 |
| 688 | + assert exc_info.value.detail == "s3 upstream failed" |
| 689 | + |
| 690 | + |
| 691 | +@pytest.mark.asyncio |
| 692 | +async def test_get_result_report_allows_missing_report_payload(test_db): |
| 693 | + user = AppUser( |
| 694 | + id=uuid4(), |
| 695 | + auth0_user_id="auth0|results-user-report-none", |
| 696 | + name="Results User Report None", |
| 697 | + email="results-report-none@example.com", |
| 698 | + ) |
| 699 | + run = WorkflowRun( |
| 700 | + id=uuid4(), |
| 701 | + owner_user_id=user.id, |
| 702 | + seqera_run_id="wf-report-none", |
| 703 | + work_dir="/tmp/wf-report-none", |
| 704 | + ) |
| 705 | + test_db.add_all([user, run]) |
| 706 | + test_db.commit() |
| 707 | + |
| 708 | + with patch( |
| 709 | + "app.routes.workflow.results.get_result_report_download", |
| 710 | + new=AsyncMock(return_value=None), |
| 711 | + ): |
| 712 | + result = await get_result_report("wf-report-none", user.id, test_db) |
| 713 | + |
| 714 | + assert result.runId == "wf-report-none" |
| 715 | + assert result.report is None |
0 commit comments