Files
PlantAI-dev/tests/unit/test_wgs_impute_backend.py
T
2026-08-03 18:42:47 +08:00

215 lines
6.7 KiB
Python

from __future__ import annotations
import asyncio
import inspect
import json
from pathlib import Path
import runpy
from apps.web.vue3.main import ExecuteRequest
from gfa_core.mas.agents.planning_v2 import PlanningAgentV2
from gfa_core.mas.stage_capabilities import CAPABILITIES
from gfa_core.mas.stage_catalog import EXECUTABLE_STAGES, STAGE_DEPENDENCIES
from gfa_core.mas.tools.stage_tools import StageToolRegistry
FIXTURES = runpy.run_path(
str(Path(__file__).with_name("test_wgs_impute_cli.py"))
)
SAMPLES = FIXTURES["SAMPLES"]
REFERENCE = FIXTURES["REFERENCE"]
FILTER_ANALYSIS = FIXTURES["FILTER_ANALYSIS"]
_filter_authority = FIXTURES["_filter_authority"]
_java = FIXTURES["_java"]
def test_wgs_impute_vertical_registration_is_exact() -> None:
assert STAGE_DEPENDENCIES["wgs_impute"] == ["wgs_variant_filter"]
assert "wgs_impute" in EXECUTABLE_STAGES
capability = CAPABILITIES["wgs_impute"]
assert {item.role for item in capability.consumes} == {
"pass_snp_vcf",
"pass_snp_vcf_index",
}
assert {item.role for item in capability.provides} == {
"imputed_snp_vcf",
"imputed_snp_vcf_index",
"impute_report",
"impute_log",
}
definition = next(
item
for item in StageToolRegistry.STAGE_DEFINITIONS
if item["id"] == "wgs_impute"
)
assert definition["script"] == "run_wgs_impute.py"
assert definition["required_inputs"] == ["wgs_filter_manifest"]
assert PlanningAgentV2.REQUIRED_INPUTS["wgs_impute"] == []
def test_api_and_worker_boundary_carry_exact_parameters() -> None:
request = ExecuteRequest(
stage="wgs_impute",
wgsFilterManifest="final/Cohort.wgs_variant_filter.gatk.manifest.json",
wgsJava="/opt/java",
wgsBeagleJar="/opt/beagle.jar",
wgsMemory="16g",
wgsWindow=30,
wgsOverlap=3,
wgsNe=90000,
wgsIterations=10,
wgsPhaseStates=200,
wgsImpStates=1200,
wgsCluster=0.01,
)
assert request.wgsFilterManifest.endswith(".manifest.json")
assert request.wgsJava == "/opt/java"
assert request.wgsBeagleJar == "/opt/beagle.jar"
parameters = inspect.signature(
__import__("apps.web.pipeline_backend", fromlist=["run_stage"]).run_stage
).parameters
for name in (
"wgs_filter_manifest",
"wgs_java",
"wgs_beagle_jar",
"wgs_memory",
"wgs_window",
"wgs_overlap",
"wgs_ne",
"wgs_iterations",
"wgs_phase_states",
"wgs_imp_states",
"wgs_cluster",
):
assert name in parameters
def test_backend_builds_exact_command_and_accepts_fresh_manifest(
tmp_path: Path, monkeypatch
) -> None:
from apps.cli.run_wgs_impute import main
from apps.web import pipeline_backend
workspace = tmp_path / "job-1"
upstream = _filter_authority(workspace, method="bcftools")
java = _java(tmp_path)
calls: list[list[str]] = []
def fake_run(command, *_args, **_kwargs):
calls.append(command)
return main(command[3:])
monkeypatch.setattr(pipeline_backend, "run_cmd_logged", fake_run)
message, output = pipeline_backend.run_stage(
stage="wgs_impute",
job=pipeline_backend.JobInfo(
"job-1", "", str(workspace), str(workspace / "logs"), "now"
),
prefix="Cohort",
wgs_filter_manifest=str(upstream),
wgs_sample_set_id=SAMPLES,
wgs_reference_id=REFERENCE,
wgs_filter_analysis_id=FILTER_ANALYSIS,
wgs_java=str(java),
wgs_beagle_jar="/tmp/beagle.29Oct24.c8e.jar",
)
assert "completed" in message.lower()
assert output == workspace / "final" / "Cohort.wgs_impute.manifest.json"
manifest = json.loads(output.read_text())
from gfa_core.mas.tools.stage_status import get_stage_status
status = get_stage_status(
"job-1",
"Cohort",
"wgs_impute",
workspace_root=tmp_path,
wgs_sample_set_id=SAMPLES,
wgs_reference_id=REFERENCE,
wgs_analysis_id=manifest["wgs_analysis_id"],
)
assert status.completed, status.errors
from apps.web.vue3 import main as web_main
monkeypatch.setattr(web_main, "WORKSPACE_ROOT", tmp_path)
web_status = asyncio.run(
web_main.get_stage_status(
job_id="job-1",
stage="wgs_impute",
prefix="Cohort",
wgs_sample_set_id=SAMPLES,
wgs_reference_id=REFERENCE,
wgs_analysis_id=manifest["wgs_analysis_id"],
)
)
assert web_status["completed"], web_status["errors"]
result = asyncio.run(
web_main.get_results(
job_id="job-1",
stage="wgs_impute",
wgs_sample_set_id=SAMPLES,
wgs_reference_id=REFERENCE,
wgs_analysis_id=manifest["wgs_analysis_id"],
)
)
output_name = Path(
manifest["artifacts"]["imputed_snp_vcf"]["full_path"]
).name
assert output_name in json.dumps(result)
Path(
manifest["artifacts"]["imputed_snp_vcf"]["full_path"]
).write_bytes(b"tampered")
rejected = asyncio.run(
web_main.get_results(
job_id="job-1",
stage="wgs_impute",
wgs_sample_set_id=SAMPLES,
wgs_reference_id=REFERENCE,
wgs_analysis_id=manifest["wgs_analysis_id"],
)
)
assert output_name not in json.dumps(rejected)
command = calls[-1]
assert command[:3] == [
pipeline_backend.sys.executable,
"-m",
"apps.cli.run_wgs_impute",
]
for option, value in (
("--filter-manifest", str(upstream)),
("--java", str(java)),
("--beagle-jar", "/tmp/beagle.29Oct24.c8e.jar"),
("--memory", "8g"),
("--window", "40.0"),
("--overlap", "2.0"),
("--ne", "100000"),
("--iterations", "12"),
("--phase-states", "280"),
("--imp-states", "1600"),
("--cluster", "0.005"),
):
assert command[command.index(option) + 1] == value
def test_status_results_frontend_and_skill_are_manifest_role_driven() -> None:
status = Path("gfa_core/mas/tools/stage_status.py").read_text()
results = Path("apps/web/vue3/main.py").read_text()
frontend = Path("apps/web/vue3/index.html").read_text()
skill = Path("skills/wgs-impute/SKILL.md").read_text()
for token in (
"imputed_snp_vcf",
"imputed_snp_vcf_index",
"impute_report",
"impute_log",
):
assert token in status
assert token in results
for token in (
"wgs_impute",
"wgsFilterManifest",
"wgsBeagleJar",
"handoffWgsImputeProducerAuthority",
):
assert token in frontend
assert "python -m apps.cli.run_wgs_impute" in skill
assert "Stage10" in skill