105 lines
4.6 KiB
Python
105 lines
4.6 KiB
Python
"""Maintenance tools in scripts/jev: everything except the API calls."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import gzip
|
|
import importlib.util
|
|
import json
|
|
import sys
|
|
from pathlib import Path
|
|
from types import ModuleType
|
|
|
|
ROOT = Path(__file__).resolve().parents[2]
|
|
JEV = ROOT / "scripts" / "jev"
|
|
|
|
|
|
def _load(name: str) -> ModuleType:
|
|
if str(JEV) not in sys.path:
|
|
sys.path.insert(0, str(JEV))
|
|
spec = importlib.util.spec_from_file_location(name, JEV / f"{name}.py")
|
|
assert spec is not None and spec.loader is not None
|
|
mod = importlib.util.module_from_spec(spec)
|
|
sys.modules[name] = mod
|
|
spec.loader.exec_module(mod)
|
|
return mod
|
|
|
|
|
|
client = _load("client")
|
|
cov = _load("spec_coverage")
|
|
voice = _load("voice_check")
|
|
tri = _load("nightly_triage")
|
|
|
|
|
|
def test_client_builders() -> None:
|
|
assert client.noul("x?", true="a", false="b") == {"type": "noul", "instructions": "x?",
|
|
"criteria": {"true": "a", "false": "b"}} # fmt: skip
|
|
assert client.choice("pick", {"a": "A"})["type"] == "choice"
|
|
assert client.score("how", ["low", "high"])["criteria"] == ["low", "high"]
|
|
|
|
|
|
def test_spec_parsing_skips_open_questions() -> None:
|
|
text = (
|
|
"## 14 CLI\n- **CLI-005** `§14 · P1` **Option precedence.**\nCLI flags beat config files.\n"
|
|
"- **OQ-001** `§20` Is this needed?\n## 15 Outputs\n- **OUT-031** `§9 · P6` VCF records are sorted.\n"
|
|
)
|
|
items = cov.parse_spec(text)
|
|
assert [i.id for i in items] == ["CLI-005", "OUT-031"]
|
|
assert items[0].tag == "§14 · P1" and "config files" in items[0].text
|
|
|
|
|
|
def test_bm25_prefers_matching_test() -> None:
|
|
bm = cov.Bm25(["def test_vcf_sorted(): records sorted by position", "def test_idat_header(): magic bytes"])
|
|
assert bm.top("VCF records are sorted by position", 1) == [0]
|
|
|
|
|
|
def test_voice_rules() -> None:
|
|
paras = voice.paragraphs(Path("x.md"), "# H\n\nA seamless — revolutionary tool.\n\n```\nnot prose\n```\n")
|
|
assert len(paras) == 1 and paras[0].heading == "H"
|
|
issues = voice.mechanical(paras[0])
|
|
assert "em or en dash in prose" in issues and any("seamless" in i for i in issues)
|
|
quoted = voice.Para("x.md", 1, "", 'Never write "revolutionary" or "seamless".')
|
|
assert voice.mechanical(quoted) == []
|
|
|
|
|
|
def test_known_findings_and_evidence(tmp_path: Path) -> None:
|
|
kf = tri.known_findings("| K-014 | 2026-09-28 | NaN sign | NaNs take the platform's sign. | resolved |\n")
|
|
assert kf == {"K-014": "NaN sign: NaNs take the platform's sign."}
|
|
(tmp_path / "s" / "compare").mkdir(parents=True)
|
|
(tmp_path / "s" / "compare" / "summary.json").write_text(
|
|
json.dumps({"fields": {"GT": {"tolerance": "exact", "discrepancies": 2}, "X": {"discrepancies": 0}}})
|
|
)
|
|
with gzip.open(tmp_path / "s" / "compare" / "discrepancies.tsv.gz", "wt") as fh:
|
|
fh.write("pair\tfield\n1\tGT\n")
|
|
(tmp_path / "run.log").write_text("pi_run: MemAvailable 500 MiB < 800 MiB -- stopping the job\n")
|
|
ev = tri.evidence(tmp_path, "s", {"verdict": "FAIL", "report": "s/compare/dashboard.html"})
|
|
assert ev["differing_fields"] == {"GT": {"tolerance": "exact", "discrepancies": 2}}
|
|
assert ev["first_rows"][1] == "1\tGT" and "log" not in ev # another step's log line must not leak in
|
|
ev2 = tri.evidence(tmp_path, "t", {"verdict": "ERROR", "error": "exit 137"})
|
|
assert ev2["log"] == ["pi_run: MemAvailable 500 MiB < 800 MiB -- stopping the job"]
|
|
|
|
|
|
issue = _load("issue_triage")
|
|
lit = _load("literature_watch")
|
|
|
|
|
|
def test_issue_missing_info_and_comment() -> None:
|
|
body = "### SimurgArray version\n\n_No response_\n\n### Command\n\n```shell\nsimurg genotype\n```\n"
|
|
assert issue.missing_info(body, "bug") == ["the SimurgArray version"]
|
|
assert issue.missing_info(body.replace("_No response_", "2.0.0"), "bug") == []
|
|
assert issue.missing_info("no template at all", "feature") == []
|
|
d = {"kind": "security", "labels": ["kind/security"], "privacy": ["credentials"], "clinical": True,
|
|
"missing": []} # fmt: skip
|
|
text = issue.comment_text(d)
|
|
assert "[email protected]" in text and "credentials" in text and "research tool" in text
|
|
|
|
|
|
def test_literature_prefilter() -> None:
|
|
p = lit.Paper
|
|
papers = [
|
|
p("bioRxiv", "Fast genotype calling for Infinium arrays", "We call genotypes from IDAT files.", "u1", "d"),
|
|
p("PubMed", "Fast genotype calling for Infinium arrays!", "Duplicate title.", "u2", "d"),
|
|
p("bioRxiv", "Protein folding at scale", "Nothing about arrays here.", "u3", "d"),
|
|
p("bioRxiv", "No abstract", "", "u4", "d"),
|
|
]
|
|
assert [x.url for x in lit.prefilter(papers)] == ["u1"]
|