215 lines
6.7 KiB
Python
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
|