@@ -433,6 +433,143 @@ def test__given_submitted_job_with_telemetry__then_polling_echoes_run_id(
433433 assert response .status_code == 200
434434 assert response .json ()["run_id" ] == "run-123"
435435
436+ def test__given_unknown_job_id__then_polling_returns_404 (
437+ self , mock_modal , client : TestClient
438+ ):
439+ """
440+ Given a job id that the gateway never issued
441+ When polling job status
442+ Then the gateway returns 404 before asking Modal for a call result.
443+ """
444+ response = client .get ("/jobs/unknown-job-id" )
445+
446+ assert response .status_code == 404
447+ assert response .json ()["detail" ] == "Job not found: unknown-job-id"
448+
449+ def test__given_lazy_modal_call_without_metadata__then_polling_returns_404 (
450+ self , mock_modal , client : TestClient
451+ ):
452+ """
453+ Given Modal can construct a FunctionCall handle for an arbitrary id
454+ When the gateway has no metadata for that id
455+ Then the gateway still treats it as not found.
456+ """
457+ mock_modal ["func" ].call_for ("auth-smoke-probe-does-not-exist" )
458+
459+ response = client .get ("/jobs/auth-smoke-probe-does-not-exist" )
460+
461+ assert response .status_code == 404
462+ assert (
463+ response .json ()["detail" ]
464+ == "Job not found: auth-smoke-probe-does-not-exist"
465+ )
466+
467+ def test__given_running_job__then_polling_returns_202 (
468+ self , mock_modal , client : TestClient
469+ ):
470+ mock_modal ["dicts" ]["simulation-api-us-versions" ] = {
471+ "latest" : "1.500.0" ,
472+ "1.500.0" : "policyengine-simulation-us1-500-0-uk2-66-0" ,
473+ }
474+
475+ submit_response = client .post (
476+ "/simulate/economy/comparison" ,
477+ json = {
478+ "country" : "us" ,
479+ "scope" : "macro" ,
480+ "reform" : {},
481+ },
482+ )
483+ job_id = submit_response .json ()["job_id" ]
484+ mock_modal ["func" ].last_call .running = True
485+
486+ response = client .get (f"/jobs/{ job_id } " )
487+
488+ assert response .status_code == 202
489+ assert response .json ()["status" ] == "running"
490+
491+ def test__given_expired_modal_output__then_polling_returns_404 (
492+ self , mock_modal , client : TestClient
493+ ):
494+ mock_modal ["dicts" ]["simulation-api-us-versions" ] = {
495+ "latest" : "1.500.0" ,
496+ "1.500.0" : "policyengine-simulation-us1-500-0-uk2-66-0" ,
497+ }
498+
499+ submit_response = client .post (
500+ "/simulate/economy/comparison" ,
501+ json = {
502+ "country" : "us" ,
503+ "scope" : "macro" ,
504+ "reform" : {},
505+ },
506+ )
507+ job_id = submit_response .json ()["job_id" ]
508+ mock_modal ["func" ].last_call .error = mock_modal [
509+ "exception"
510+ ].OutputExpiredError ()
511+
512+ # Modal's FastAPI job queue example maps OutputExpiredError to 404:
513+ # https://modal.com/docs/guide/job-queue#integration-with-web-frameworks
514+ response = client .get (f"/jobs/{ job_id } " )
515+
516+ assert response .status_code == 404
517+ assert response .json ()["detail" ] == f"Job not found: { job_id } "
518+
519+ def test__given_modal_call_not_found__then_polling_returns_404 (
520+ self , mock_modal , client : TestClient
521+ ):
522+ mock_modal ["dicts" ]["simulation-api-us-versions" ] = {
523+ "latest" : "1.500.0" ,
524+ "1.500.0" : "policyengine-simulation-us1-500-0-uk2-66-0" ,
525+ }
526+
527+ submit_response = client .post (
528+ "/simulate/economy/comparison" ,
529+ json = {
530+ "country" : "us" ,
531+ "scope" : "macro" ,
532+ "reform" : {},
533+ },
534+ )
535+ job_id = submit_response .json ()["job_id" ]
536+ mock_modal ["function_call" ].from_id_errors [job_id ] = mock_modal [
537+ "exception"
538+ ].NotFoundError ()
539+
540+ response = client .get (f"/jobs/{ job_id } " )
541+
542+ assert response .status_code == 404
543+ assert response .json ()["detail" ] == f"Job not found: { job_id } "
544+
545+ def test__given_worker_error__then_polling_returns_redacted_500 (
546+ self , mock_modal , client : TestClient
547+ ):
548+ mock_modal ["dicts" ]["simulation-api-us-versions" ] = {
549+ "latest" : "1.500.0" ,
550+ "1.500.0" : "policyengine-simulation-us1-500-0-uk2-66-0" ,
551+ }
552+
553+ submit_response = client .post (
554+ "/simulate/economy/comparison" ,
555+ json = {
556+ "country" : "us" ,
557+ "scope" : "macro" ,
558+ "reform" : {},
559+ },
560+ )
561+ job_id = submit_response .json ()["job_id" ]
562+ mock_modal ["func" ].last_call .error = RuntimeError ("worker crashed" )
563+
564+ response = client .get (f"/jobs/{ job_id } " )
565+
566+ assert response .status_code == 500
567+ body = response .json ()
568+ assert body ["status" ] == "failed"
569+ assert body ["error" ].startswith ("Simulation failed" )
570+ assert "correlation_id=" in body ["error" ]
571+ assert "worker crashed" not in body ["error" ]
572+
436573
437574class TestBudgetWindowBatchEndpoints :
438575 """Tests for budget-window batch gateway endpoints."""
0 commit comments