File size: 5,620 Bytes
1bf37a2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
"""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"