182 lines
6.1 KiB
Python
182 lines
6.1 KiB
Python
from __future__ import annotations
|
|
|
|
import json
|
|
from pathlib import Path
|
|
|
|
|
|
def _request_and_stages():
|
|
from apps.web.vue3.main import ConfirmedPlanExecutionRequest, ConfirmedPlanStage
|
|
|
|
stages = [
|
|
ConfirmedPlanStage(
|
|
id="identify",
|
|
params={
|
|
"scopeId": "scope-a",
|
|
"analysisRoute": "post_subfamily_confirm",
|
|
"memberSetId": "members-a",
|
|
},
|
|
),
|
|
ConfirmedPlanStage(id="motif", dependencies=["identify"]),
|
|
ConfirmedPlanStage(id="pep", dependencies=["motif"]),
|
|
]
|
|
request = ConfirmedPlanExecutionRequest(
|
|
session_id="session-a",
|
|
job_id="job-a",
|
|
prefix="Ft.SPL",
|
|
plan_id="plan-a",
|
|
stages=stages,
|
|
)
|
|
return request, stages
|
|
|
|
|
|
def test_confirmed_plan_terminal_success_requests_final_report() -> None:
|
|
from apps.web.vue3.main import _confirmed_plan_report_request
|
|
|
|
request, stages = _request_and_stages()
|
|
report = _confirmed_plan_report_request(
|
|
request,
|
|
stages,
|
|
{"status": "success", "completed_stages": ["identify", "motif", "pep"]},
|
|
)
|
|
|
|
assert report is not None
|
|
assert report.stage == "gene_family_report"
|
|
assert report.expectedStages == ["identify", "motif", "pep"]
|
|
assert report.stageStatuses == {"identify": "success", "motif": "success", "pep": "success"}
|
|
assert report.reportAction == "generate"
|
|
|
|
|
|
def test_confirmed_plan_failure_requests_partial_report() -> None:
|
|
from apps.web.vue3.main import _confirmed_plan_report_request
|
|
|
|
request, stages = _request_and_stages()
|
|
report = _confirmed_plan_report_request(
|
|
request,
|
|
stages,
|
|
{
|
|
"status": "failed",
|
|
"completed_stages": ["identify"],
|
|
"failure": {"stage": "motif"},
|
|
},
|
|
)
|
|
|
|
assert report is not None
|
|
assert report.stageStatuses == {"identify": "success", "motif": "failed", "pep": "not_run"}
|
|
assert report.scopeId == "scope-a"
|
|
assert report.analysisRoute == "post_subfamily_confirm"
|
|
assert report.memberSetId == "members-a"
|
|
|
|
|
|
def test_paused_plan_does_not_auto_finalize_report() -> None:
|
|
from apps.web.vue3.main import _confirmed_plan_report_request
|
|
|
|
request, stages = _request_and_stages()
|
|
report = _confirmed_plan_report_request(
|
|
request,
|
|
stages,
|
|
{"status": "paused", "completed_stages": ["identify"]},
|
|
)
|
|
assert report is None
|
|
|
|
|
|
def test_frontend_batch_terminal_path_calls_report_stage_with_planned_statuses() -> None:
|
|
source = (Path(__file__).resolve().parents[2] / "apps/web/vue3/index.html").read_text(encoding="utf-8")
|
|
|
|
assert "submitGeneFamilyReportForBatch" in source
|
|
assert "executeSingleStageWithParams('gene_family_report'" in source
|
|
assert "expectedStages: plannedIds" in source
|
|
assert "stageStatuses" in source
|
|
assert "if (pausedCount > 0) return" in source
|
|
assert "continuationOpened" in source
|
|
assert "name: 'gene_family_report'" in source
|
|
assert "openGeneratedGeneFamilyReport" in source
|
|
|
|
|
|
def test_report_context_prefers_terminal_confirmed_plan(monkeypatch, tmp_path: Path) -> None:
|
|
from apps.web.vue3 import main as web_main
|
|
|
|
final_dir = tmp_path / "job-a" / "final"
|
|
confirmed_dir = final_dir / ".confirmed_plans"
|
|
confirmed_dir.mkdir(parents=True)
|
|
(final_dir / "Ft.SPL.gene_family_report.manifest.json").write_text(
|
|
json.dumps(
|
|
{
|
|
"stage": "gene_family_report",
|
|
"status": "success",
|
|
"prefix": "Ft.SPL",
|
|
"parameters": {
|
|
"expected_stages": ["identify"],
|
|
"stage_statuses": {"identify": "success"},
|
|
},
|
|
}
|
|
),
|
|
encoding="utf-8",
|
|
)
|
|
(confirmed_dir / "0123456789abcdefabcd.json").write_text(
|
|
json.dumps(
|
|
{
|
|
"status": "failed",
|
|
"job_id": "job-a",
|
|
"prefix": "Ft.SPL",
|
|
"stages": ["identify", "motif", "pep", "gene_family_report"],
|
|
"completed_stages": ["identify", "motif"],
|
|
"failure": {"stage": "pep"},
|
|
"scopeId": "scope-a",
|
|
"memberSetId": "members-a",
|
|
}
|
|
),
|
|
encoding="utf-8",
|
|
)
|
|
monkeypatch.setattr(web_main, "WORKSPACE_ROOT", tmp_path)
|
|
|
|
context = web_main._gene_family_report_plan_context("job-a")
|
|
|
|
assert context == {
|
|
"prefix": "Ft.SPL",
|
|
"expectedStages": ["identify", "motif", "pep"],
|
|
"stageStatuses": {"identify": "success", "motif": "success", "pep": "failed"},
|
|
"scopeId": "scope-a",
|
|
"analysisRoute": "",
|
|
"memberSetId": "members-a",
|
|
"rnaseqScopeId": "",
|
|
}
|
|
|
|
|
|
def test_report_context_reuses_formal_report_plan_only(monkeypatch, tmp_path: Path) -> None:
|
|
from apps.web.vue3 import main as web_main
|
|
|
|
final_dir = tmp_path / "job-a" / "final"
|
|
final_dir.mkdir(parents=True)
|
|
(final_dir / "Ft.SPL.motif.manifest.json").write_text(
|
|
json.dumps({"stage": "motif", "status": "success", "prefix": "Ft.SPL"}),
|
|
encoding="utf-8",
|
|
)
|
|
monkeypatch.setattr(web_main, "WORKSPACE_ROOT", tmp_path)
|
|
assert web_main._gene_family_report_plan_context("job-a") is None
|
|
|
|
(final_dir / "Ft.SPL.gene_family_report.manifest.json").write_text(
|
|
json.dumps(
|
|
{
|
|
"stage": "gene_family_report",
|
|
"status": "success",
|
|
"prefix": "Ft.SPL",
|
|
"scope_id": "scope-old",
|
|
"parameters": {
|
|
"expected_stages": ["identify", "motif"],
|
|
"stage_statuses": {"identify": "success", "motif": "success"},
|
|
},
|
|
}
|
|
),
|
|
encoding="utf-8",
|
|
)
|
|
|
|
context = web_main._gene_family_report_plan_context("job-a", "Ft.SPL")
|
|
|
|
assert context is not None
|
|
assert context["expectedStages"] == ["identify", "motif"]
|
|
assert context["stageStatuses"] == {"identify": "success", "motif": "success"}
|
|
assert context["scopeId"] == ""
|
|
assert context["analysisRoute"] == ""
|
|
assert context["memberSetId"] == ""
|
|
assert context["rnaseqScopeId"] == ""
|