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