test_research_task_view_service.py 7.91 KB
from __future__ import annotations

from types import SimpleNamespace

from services.application.research import ResearchTaskNotFoundError, ResearchTaskViewService


class _FakeResponsePayload:
    def __init__(self, payload: dict[str, object]) -> None:
        self._payload = payload

    def to_response_payload(self) -> dict[str, object]:
        return dict(self._payload)


class _FakeResponseItem:
    def __init__(self, payload: dict[str, object]) -> None:
        self._payload = payload

    def to_response_item(self) -> dict[str, object]:
        return dict(self._payload)


class _FakeTask:
    def __init__(
        self,
        *,
        task_id: str,
        analysis_run_id: str | None = None,
        crawler_job_id: str | None = None,
        report_job_id: str | None = None,
    ) -> None:
        self.id = task_id
        self.analysis_run_id = analysis_run_id
        self.crawler_job_id = crawler_job_id
        self.report_job_id = report_job_id

    def to_response_item(self) -> dict[str, object]:
        return {
            "id": self.id,
            "analysis_run_id": self.analysis_run_id,
            "crawler_job_id": self.crawler_job_id,
            "report_job_id": self.report_job_id,
        }


class _FakeReportQueryService:
    def __init__(self, payload: dict[str, object]) -> None:
        self._payload = payload
        self.typed_calls: list[dict[str, object]] = []
        self.payload_calls: list[dict[str, object]] = []

    def get_task_report_resource_dto(self, **kwargs):
        self.typed_calls.append(kwargs)
        return _FakeResponsePayload(self._payload)

    def build_task_report_resource_payload(self, **kwargs):
        self.payload_calls.append(kwargs)
        return {"report": {"status": "legacy"}}


def test_build_task_view_collection_payload_delegates_to_task_scoped_resource_builders():
    snapshot = SimpleNamespace(
        active_task=SimpleNamespace(id="task-1"),
        tasks=[
            _FakeTask(
                task_id="task-1",
                analysis_run_id="analysis-1",
                crawler_job_id="crawler-1",
                report_job_id="report-1",
            )
        ],
    )

    analysis_calls: list[dict[str, object]] = []
    crawler_calls: list[dict[str, object]] = []
    report_query_service = _FakeReportQueryService({"task_id": "task-1", "report": {"status": "completed"}})

    service = ResearchTaskViewService(
        task_service=SimpleNamespace(get_task_snapshot=lambda: snapshot),
        analysis_run_builder=lambda **kwargs: _FakeResponseItem({"id": kwargs["analysis_run_id"]}),
        analysis_resource_builder=lambda **kwargs: analysis_calls.append(kwargs)
        or _FakeResponsePayload({"task_id": "task-1", "analysis": {"status": "queued"}}),
        analysis_runs_builder=lambda **kwargs: [_FakeResponseItem({"id": kwargs["analysis_run_id"]})],
        crawler_resource_builder=lambda **kwargs: crawler_calls.append(kwargs) or {"crawler": {"status": "completed"}},
        report_resource_builder=report_query_service,
    )

    payload = service.build_task_view_collection_payload()

    assert payload == {
        "active_task_id": "task-1",
        "task_views": [
            {
                "task_id": "task-1",
                "task": {
                    "id": "task-1",
                    "analysis_run_id": "analysis-1",
                    "crawler_job_id": "crawler-1",
                    "report_job_id": "report-1",
                },
                "analysis": {"status": "queued"},
                "crawler": {"status": "completed"},
                "report": {"status": "completed"},
            }
        ],
    }
    assert analysis_calls == [
        {
            "research_task_id": "task-1",
            "analysis_run_id": "analysis-1",
            "task": snapshot.tasks[0],
        }
    ]
    assert crawler_calls == [
        {
            "research_task_id": "task-1",
            "crawler_job_id": "crawler-1",
            "task": snapshot.tasks[0],
        }
    ]
    assert report_query_service.typed_calls == [
        {
            "research_task_id": "task-1",
            "report_job_id": "report-1",
            "task": snapshot.tasks[0],
        }
    ]
    assert report_query_service.payload_calls == []


def test_build_task_analysis_payloads_delegate_after_resolving_task():
    task = _FakeTask(
        task_id="task-2",
        analysis_run_id="analysis-2",
        crawler_job_id="crawler-2",
        report_job_id="report-2",
    )
    report_query_service = _FakeReportQueryService(
        {
            "task_id": "task-2",
            "report_job_id": "report-2",
            "report": {"id": "report-2"},
        }
    )
    service = ResearchTaskViewService(
        task_service=SimpleNamespace(
            get_task_snapshot=lambda: SimpleNamespace(active_task=None, tasks=[]),
            get_task_dto=lambda task_id: task if task_id == "task-2" else None,
        ),
        analysis_run_builder=lambda **kwargs: _FakeResponseItem({"id": kwargs["analysis_run_id"]}),
        analysis_resource_builder=lambda **kwargs: {
            "task_id": kwargs["research_task_id"],
            "analysis": {"status": "completed"},
        },
        analysis_runs_builder=lambda **kwargs: [_FakeResponseItem({"id": kwargs["analysis_run_id"]})],
        crawler_resource_builder=lambda **kwargs: {
            "task_id": kwargs["research_task_id"],
            "crawler_job_id": kwargs["crawler_job_id"],
            "crawler": {"id": "crawler-2"},
        },
        report_resource_builder=report_query_service,
    )

    assert service.build_task_analysis_run_payload("task-2") == {
        "task_id": "task-2",
        "analysis_run_id": "analysis-2",
        "analysis_run": {"id": "analysis-2"},
    }
    assert service.build_task_analysis_resource_payload("task-2") == {
        "task_id": "task-2",
        "analysis": {"status": "completed"},
    }
    assert service.build_task_analysis_runs_payload("task-2") == {
        "task_id": "task-2",
        "analysis_run_id": "analysis-2",
        "analysis_runs": [{"id": "analysis-2"}],
    }
    assert service.build_task_crawler_resource_payload("task-2") == {
        "task_id": "task-2",
        "crawler_job_id": "crawler-2",
        "crawler": {"id": "crawler-2"},
    }
    assert service.build_task_report_resource_payload("task-2") == {
        "task_id": "task-2",
        "report_job_id": "report-2",
        "report": {"id": "report-2"},
    }
    assert report_query_service.typed_calls == [
        {
            "research_task_id": "task-2",
            "report_job_id": "report-2",
            "task": task,
        }
    ]
    assert report_query_service.payload_calls == []


def test_build_task_analysis_payloads_raise_when_task_is_missing():
    service = ResearchTaskViewService(
        task_service=SimpleNamespace(
            get_task_snapshot=lambda: SimpleNamespace(active_task=None, tasks=[]),
            get_task_dto=lambda _task_id: (_ for _ in ()).throw(ResearchTaskNotFoundError("missing")),
        ),
        analysis_run_builder=lambda **kwargs: _FakeResponseItem({"id": kwargs["analysis_run_id"]}),
        analysis_resource_builder=lambda **kwargs: {"analysis": kwargs},
        analysis_runs_builder=lambda **kwargs: [_FakeResponseItem({"id": kwargs["analysis_run_id"]})],
        crawler_resource_builder=lambda **kwargs: {"crawler": kwargs},
        report_resource_builder=lambda **kwargs: {"report": kwargs},
    )

    try:
        service.build_task_analysis_run_payload("missing")
    except ResearchTaskNotFoundError:
        pass
    else:
        raise AssertionError("Expected ResearchTaskNotFoundError")

    try:
        service.build_task_crawler_resource_payload("missing")
    except ResearchTaskNotFoundError:
        pass
    else:
        raise AssertionError("Expected ResearchTaskNotFoundError")

    try:
        service.build_task_report_resource_payload("missing")
    except ResearchTaskNotFoundError:
        pass
    else:
        raise AssertionError("Expected ResearchTaskNotFoundError")