diff --git a/apps/api/app/api/routes/ingest_jobs.py b/apps/api/app/api/routes/ingest_jobs.py index b7148ae93..791be8964 100644 --- a/apps/api/app/api/routes/ingest_jobs.py +++ b/apps/api/app/api/routes/ingest_jobs.py @@ -3,7 +3,7 @@ from typing import Any from app.core.celery_app import celery_app from app.dependencies import get_ingest_job_repository from app.schemas.ingest_job import IngestJobResponse, SubmitIngestJobRequest -from fastapi import APIRouter, Depends +from fastapi import APIRouter, Depends, HTTPException from packages.application import SubmitIngestJobCommand, SubmitIngestJobUseCase @@ -17,7 +17,7 @@ def get_ingest_job( ) -> IngestJobResponse: job = ingest_job_repository.get(job_id) if job is None: - raise ValueError(f"IngestJob {job_id} not found") + raise HTTPException(status_code=404, detail=f"IngestJob {job_id} not found") return IngestJobResponse( id=job.id, project_id=job.project_id, diff --git a/tests/unit/test_ingest_jobs_route_404.py b/tests/unit/test_ingest_jobs_route_404.py new file mode 100644 index 000000000..321b4d9e5 --- /dev/null +++ b/tests/unit/test_ingest_jobs_route_404.py @@ -0,0 +1,83 @@ +"""Ingest jobs GET route — 查不到 job_id 应返回 404,不是 500。 + +覆盖: +- job 不存在:GET /ingest-jobs/{job_id} → 404 +- job 存在:200 + 序列化字段完整 +""" + +from __future__ import annotations + +import os +import sys +from pathlib import Path +from unittest.mock import MagicMock + +os.environ.setdefault("JWT_SECRET_KEY", "unit-test-secret-key-for-testing") +os.environ.setdefault("DATABASE_URL", "sqlite:///test.db") + +import pytest # noqa: E402 +from fastapi import FastAPI # noqa: E402 +from fastapi.testclient import TestClient # noqa: E402 + +sys.path.insert(0, str(Path(__file__).resolve().parents[2] / "apps" / "api")) + +from app.api.routes.ingest_jobs import get_ingest_job, router # noqa: E402 +from app.dependencies import get_ingest_job_repository # noqa: E402 + +from packages.domain import IngestJob, IngestJobStatus # noqa: E402 + + +@pytest.fixture +def fake_job(): + return IngestJob( + id="job-abc", + project_id="proj-1", + library_id="lib-1", + storage_key="key/test.mp4", + status=IngestJobStatus.COMPLETED, + error_message="", + result_asset_id="asset-xyz", + ) + + +@pytest.fixture +def client_not_found(): + app = FastAPI() + app.include_router(router, prefix="/ingest-jobs") + + mock_repo = MagicMock() + mock_repo.get.return_value = None + + app.dependency_overrides[get_ingest_job_repository] = lambda: mock_repo + return TestClient(app) + + +@pytest.fixture +def client_found(fake_job): + app = FastAPI() + app.include_router(router, prefix="/ingest-jobs") + + mock_repo = MagicMock() + mock_repo.get.return_value = fake_job + + app.dependency_overrides[get_ingest_job_repository] = lambda: mock_repo + return TestClient(app) + + +def test_get_ingest_job_not_found_returns_404(client_not_found): + """job 不存在时应返回 404,而不是 500 ValueError。""" + resp = client_not_found.get("/ingest-jobs/no-such-job") + assert resp.status_code == 404, resp.text + assert "no-such-job" in resp.json()["detail"] + + +def test_get_ingest_job_found_returns_200(client_found, fake_job): + """job 存在时正常 200 + 字段完整。""" + resp = client_found.get(f"/ingest-jobs/{fake_job.id}") + assert resp.status_code == 200, resp.text + body = resp.json() + assert body["id"] == fake_job.id + assert body["project_id"] == fake_job.project_id + assert body["storage_key"] == fake_job.storage_key + assert body["status"] == IngestJobStatus.COMPLETED.value + assert body["result_asset_id"] == fake_job.result_asset_id