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

112 lines
4.3 KiB
Python

from __future__ import annotations
import asyncio
import hashlib
import json
from pathlib import Path
from gfa_core.mas.tools.stage_identity import StageIdentity, get_stage_status_for_identity
def _trait_manifest(final: Path, *, phenotype_scope: str, digest12: str) -> Path:
trait = final / f"cohort.gwas_trait.{digest12}.trait.txt"
report = final / f"cohort.gwas_trait.{digest12}.report.md"
environment = final / f"cohort.gwas_trait.{digest12}.environment.txt"
for path in (trait, report, environment):
path.write_text(path.name + "\n", encoding="utf-8")
def artifact(path: Path, role: str) -> dict:
return {
"role": role, "path": f"final/{path.name}", "full_path": str(path.resolve()),
"type": "text/plain", "size": path.stat().st_size,
"sha256": hashlib.sha256(path.read_bytes()).hexdigest(),
}
manifest = {
"manifest_version": "1.0", "stage": "gwas_trait", "prefix": "cohort",
"job_id": "job", "run_id": f"gwas_trait_{digest12}", "status": "success",
"phenotype_scope_id": phenotype_scope,
"analysis_scope_id": "gwas-analysis:" + digest12.ljust(64, "0"),
"elapsed_sec": 1.2,
"artifacts": {
"trait_table": artifact(trait, "trait_table"),
"report": artifact(report, "report"),
"environment": artifact(environment, "environment"),
},
}
path = final / f"cohort.gwas_trait.{digest12}.manifest.json"
path.write_text(json.dumps(manifest), encoding="utf-8")
return path
def test_status_and_results_select_exact_phenotype_scope(monkeypatch, tmp_path: Path) -> None:
from apps.web.vue3 import main as web
workspace_root = tmp_path / "workspace"
final = workspace_root / "job" / "final"
final.mkdir(parents=True)
wanted = "phenotype:" + "a" * 64
_trait_manifest(final, phenotype_scope=wanted, digest12="aaaaaaaaaaaa")
_trait_manifest(final, phenotype_scope="phenotype:" + "b" * 64, digest12="bbbbbbbbbbbb")
monkeypatch.setattr(web, "WORKSPACE_ROOT", workspace_root)
report = get_stage_status_for_identity(
job_id="job",
identity=StageIdentity(stage="gwas_trait", prefix="cohort", phenotype_scope_id=wanted),
workspace_root=workspace_root,
)
assert report.completed is True
assert report.manifest["phenotype_scope_id"] == wanted
payload = asyncio.run(web.get_results(
job_id="job", stage="gwas_trait", rnaseq_scope_id="", scope_id="",
genotype_scope_id="", phenotype_scope_id=wanted,
covariate_scope_id="", analysis_scope_id="",
))
assert payload["gwas_trait"]["status"] == "success"
assert {item["role"] for item in payload["gwas_trait"]["files"]} == {
"manifest", "trait_table", "report", "environment",
}
assert all("aaaaaaaaaaaa" in item["name"] for item in payload["gwas_trait"]["files"])
without_scope = asyncio.run(web.get_results(
job_id="job", stage="gwas_trait", rnaseq_scope_id="", scope_id="",
genotype_scope_id="", phenotype_scope_id="",
covariate_scope_id="", analysis_scope_id="",
))
assert without_scope["gwas_trait"]["files"] == []
def test_results_rehydration_does_not_replay_success_after_failed_attempt(
monkeypatch, tmp_path: Path,
) -> None:
from apps.web.vue3 import main as web
workspace_root = tmp_path / "workspace"
final = workspace_root / "job" / "final"
final.mkdir(parents=True)
success_path = _trait_manifest(
final, phenotype_scope="phenotype:" + "a" * 64, digest12="aaaaaaaaaaaa"
)
failed = json.loads(success_path.read_text(encoding="utf-8"))
failed.update({
"run_id": "gwas_trait_failed",
"status": "failed",
"start_time": "2026-07-24T00:00:00+00:00",
"end_time": "2026-07-24T00:00:01+00:00",
"artifacts": {},
"error": "forced failure",
})
(final / "cohort.gwas_trait.failed.manifest.json").write_text(
json.dumps(failed), encoding="utf-8"
)
monkeypatch.setattr(web, "WORKSPACE_ROOT", workspace_root)
payload = asyncio.run(web.get_results(
job_id="job", stage="gwas_trait", rnaseq_scope_id="", scope_id="",
genotype_scope_id="", phenotype_scope_id="",
covariate_scope_id="", analysis_scope_id="",
))
assert payload["gwas_trait"]["files"] == []