syntheogenesis / tests /test_features.py
github-actions[bot]
Deploy 90a38dc
1bf37a2
Raw
History Blame Contribute Delete
5.62 kB
"""Audit #7: the residues you should not mutate, from the people who curated them.
ESM-2 scores how UNUSUAL a residue is, not how load-bearing it is. A catalytic
serine is often unremarkable in sequence terms — it is the geometry that
matters, and a sequence model does not see geometry. So the engine will rank a
substitution at the active site as promising, and the cheapest fix is not a
better model but asking UniProt.
Network is stubbed throughout: a test that depends on a live public API is a
test that fails on Tuesdays and teaches everyone to ignore it.
"""
import json
import pytest
from dee.core import agent_tools as t
from dee.core import features as F
from dee.core import orchestrator as orch
ENTRY = {
"primaryAccession": "P00760",
"proteinDescription": {"recommendedName": {"fullName": {"value": "Serine protease 1"}}},
"sequence": {"length": 246},
"features": [
{"type": "Active site", "location": {"start": {"value": 63}, "end": {"value": 63}},
"description": "Charge relay system",
"evidences": [{"evidenceCode": "ECO:0000255"}]},
{"type": "Disulfide bond", "location": {"start": {"value": 30}, "end": {"value": 46}}},
{"type": "Domain", "location": {"start": {"value": 20}, "end": {"value": 240}},
"description": "Peptidase S1"},
# Not a reason to avoid a position — must be filtered out.
{"type": "Sequence conflict", "location": {"start": {"value": 100}, "end": {"value": 100}}},
# Malformed span: must be skipped, not crash.
{"type": "Active site", "location": {"start": {"value": None}, "end": {"value": None}}},
],
}
@pytest.fixture(autouse=True)
def _no_network(monkeypatch):
monkeypatch.setattr(F, "_get", lambda *a, **k: ENTRY)
def test_functional_features_are_kept_and_record_noise_is_dropped():
"""Sequence conflicts and variants describe the RECORD, not a reason to
avoid a position. Including them would bury the signal."""
r = F.fetch("P00760")
kinds = {f["type"] for f in r["features"]}
assert "Active site" in kinds and "Disulfide bond" in kinds
assert "Sequence conflict" not in kinds
def test_a_malformed_span_is_skipped_not_crashed():
r = F.fetch("P00760")
assert r["ok"] and all(f["start"] is not None for f in r["features"])
def test_a_position_on_an_active_site_says_avoid():
a = F.annotate("P00760", ["S63A"])
h = a["positions"][0]
assert h["critical"] and h["verdict"].startswith("AVOID")
assert "catalytic" in h["critical"][0]["means"]
def test_an_unannotated_position_is_never_called_safe():
"""The failure that would make this tool harmful. Most proteins are
annotated sparsely; 'no annotation' is absence of knowledge."""
a = F.annotate("P00760", [200])
assert a["positions"][0]["critical"] == []
assert "safe" not in a["positions"][0]["verdict"].lower()
assert "absence of KNOWLEDGE" in a["caveat"]
def test_the_precursor_numbering_trap_is_stated():
"""UniProt numbers the FULL precursor. Bovine trypsin's triad is at
63/107/200 there, not the classic His57/Asp102/Ser195, because the signal
peptide and propeptide are counted. A user in mature numbering gets a
confident flag on the wrong residue — worse than no flag."""
a = F.annotate("P00760", [63])
assert "precursor" in a["numbering"]
assert "246" in a["numbering"] # the real length, not a guess
assert "offset" in a["numbering"]
def test_substitution_labels_are_accepted_because_that_is_what_the_engine_speaks():
a = F.annotate("P00760", ["R63H", 63, "63"])
assert [h["position"] for h in a["positions"]] == [63, 63, 63]
def test_uniprot_evidence_codes_travel_with_the_claim():
"""ECO:0000269 is experimental; ECO:0000250 is inferred by similarity. A
designer should weight those differently, so the code is not discarded."""
r = F.fetch("P00760")
act = next(f for f in r["features"] if f["type"] == "Active site")
assert act["evidence"] == ["ECO:0000255"]
def test_domains_are_context_not_a_veto():
"""Being inside a domain is not a reason to avoid a residue — almost every
residue is. Only the critical tier vetoes."""
a = F.annotate("P00760", [200])
assert any(c["type"] == "Domain" for c in a["positions"][0]["context"])
assert a["positions"][0]["critical"] == []
def test_an_unreachable_uniprot_is_not_reported_as_no_features(monkeypatch):
""""Couldn't ask" and "nothing known" must not look the same — one means
retry, the other means proceed with care."""
monkeypatch.setattr(F, "_get", lambda *a, **k: None)
r = F.fetch("P00760")
assert r["ok"] is False and r["kind"] == "unreachable"
def test_it_is_reachable_ungated_and_specced():
assert "check_residues" in t._TOOLS
assert any(s["function"]["name"] == "check_residues" for s in orch.TOOL_SPECS)
assert orch._requires_confirm("check_residues") is False
def test_the_spec_makes_the_agent_relay_both_warnings():
d = next(s["function"]["description"] for s in orch.TOOL_SPECS
if s["function"]["name"] == "check_residues")
assert "PRECURSOR" in d
assert "does NOT mean safe" in d
assert "BEFORE recommending" in d
def test_no_sequence_is_ever_sent(monkeypatch):
"""Standing rule: only (accession) leaves the Space."""
seen = []
monkeypatch.setattr(F, "_get", lambda url, **k: seen.append(url) or ENTRY)
F.annotate("P00760", ["R63H"])
assert seen and all("P00760" in u for u in seen)
assert not any(len(u) > 200 for u in seen), "a sequence would blow the URL up"