Spaces:
Running
Running
File size: 8,216 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 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 | """Tests for sequence-visible expression risk (audit #24).
Two things are actually being defended here: that no yield is ever predicted,
and that the rare-codon threshold is derived from the tables rather than
chosen β the second is what makes the first honest rather than merely cautious.
"""
import pytest
from dee.core import expression as E
ADAPTED = "ATG" + "AAACTGGCGCGTGAAGATCTGAAAGCGGTTCTGGAAAAC" * 3 + "TAA"
RARE_HEAVY = "ATG" + "AAACTGGCGAGAAGGATAGATCTGAAAGCGGTTCTGGAAAAC" * 3 + "TAA"
# ββ the honesty contract βββββββββββββββββββββββββββββββββββββββββββββββββ
def test_no_expression_level_is_ever_predicted():
r = E.assess(ADAPTED, "e_coli")
assert r["ok"]
assert "does NOT predict an expression level" in r["no_yield"]
for key in ("yield", "expression_level", "mg_per_l", "predicted_yield",
"expression_score"):
assert key not in r
def test_the_result_names_what_it_does_not_cover():
"""An assessment that looks complete is worse than one that lists its own
blind spots β the user assumes the unchecked thing was checked."""
n = E.assess(ADAPTED, "e_coli")["not_covered"]
assert "Shine-Dalgarno" in n
assert "#38" in n and "#39" in n
assert "assess_solubility" in n
# ββ the threshold is derived, and the derivation is checkable ββββββββββββ
def test_the_rare_cutoff_selects_the_codons_rosetta_strains_supply():
"""w < 0.15 against the E. coli table selects AGA, AGG, ATA, CTA β exactly
the set argU/ileY/leuW exist to supply. That is the evidence the cutoff is
measuring something real rather than being a round number."""
selected = set(E.assess(ADAPTED, "e_coli")["rare_codons_in_this_host"])
assert {"AGA", "AGG", "ATA", "CTA"} <= selected
def test_the_same_cutoff_finds_nothing_rare_in_human_and_says_so():
"""Mammalian codon usage is much flatter. Manufacturing a warning to look
thorough would be worse than the empty result."""
r = E.assess(ADAPTED, "human")
assert r["rare_codons_in_this_host"] == []
assert r["rare_codon_count"] == 0
assert "nothing" in r["method"]
def test_the_method_line_lists_the_selected_codons():
m = E.assess(ADAPTED, "e_coli")["method"]
assert "Sharp & Li" in m
assert str(E.RARE_BELOW) in m
assert "AGA" in m
# ββ rare codons and the runs that matter βββββββββββββββββββββββββββββββββ
def test_an_adapted_sequence_is_clean():
r = E.assess(ADAPTED, "e_coli")
assert r["rare_codon_count"] == 0
assert r["flags"] == []
assert r["codon_adaptation_index"] > 0.9
def test_rare_codons_lower_the_adaptation_index():
assert (E.assess(RARE_HEAVY, "e_coli")["codon_adaptation_index"]
< E.assess(ADAPTED, "e_coli")["codon_adaptation_index"])
def test_consecutive_rare_codons_are_reported_as_a_run():
"""The finding that changes what someone does. A scattered rare codon is
absorbed; three in a row depletes the local charged-tRNA pool."""
r = E.assess(RARE_HEAVY, "e_coli")
assert r["rare_codon_runs"]
run = r["rare_codon_runs"][0]
assert run["length"] >= E.RUN_LENGTH
assert run["codons"] == ["AGA", "AGG", "ATA"]
flag = [f for f in r["flags"] if "consecutive rare" in f["factor"]][0]
assert "recoding just this run" in flag["why"]
def test_scattered_rare_codons_do_not_form_a_run():
dna = "ATG" + ("AGA" + "CTGGCGAAAGATCTGGAA") * 6 + "TAA"
r = E.assess(dna, "e_coli")
assert r["rare_codon_count"] >= 6
assert r["rare_codon_runs"] == []
def test_the_five_prime_ramp_is_reported_separately():
"""A rare codon at codon 5 costs more than the same codon at codon 400."""
r = E.assess(RARE_HEAVY, "e_coli")
assert r["rare_in_ramp"]
assert all(x["codon_number"] <= E.RAMP_CODONS for x in r["rare_in_ramp"])
def test_cai_excludes_single_codon_families():
"""Met and Trp have no synonym to choose between. Including them only
drags every score toward 1 and makes a bad sequence look adapted."""
only_mw = "ATG" + "ATGTGG" * 20 + "TAA"
r = E.assess(only_mw, "e_coli")
assert r["codon_adaptation_index"] is None or r["codon_adaptation_index"] > 0
def test_cai_does_not_underflow_on_a_long_cds():
"""The direct product of hundreds of fractions underflows to 0.0 and
silently reports a perfectly adapted gene as unadapted."""
long_cds = "ATG" + "AAACTGGCGCGTGAAGATCTGAAAGCGGTTCTGGAAAAC" * 60 + "TAA"
cai = E.assess(long_cds, "e_coli")["codon_adaptation_index"]
assert cai is not None and cai > 0.5
# ββ reading frame ββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
def test_an_internal_stop_is_flagged_as_a_truncated_product():
dna = "ATG" + "AAACTGGCG" * 4 + "TAA" + "AAACTGGCG" * 6 + "TAA"
r = E.assess(dna, "e_coli")
assert r["internal_stops"]
flag = [f for f in r["flags"] if "internal stop" in f["factor"]][0]
assert "truncated" in flag["why"]
def test_a_missing_terminal_stop_is_flagged():
r = E.assess(ADAPTED[:-3], "e_coli")
assert r["has_terminal_stop"] is False
assert any("no terminal stop" in f["factor"] for f in r["flags"])
def test_an_out_of_frame_length_is_flagged_not_silently_truncated():
r = E.assess(ADAPTED + "AT", "e_coli")
assert r["in_frame"] is False
assert any("whole number of codons" in f["factor"] for f in r["flags"])
# ββ hosts ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
def test_an_unknown_host_declines_instead_of_using_another_table():
"""Scoring a plant construct against the E. coli table would produce
confident nonsense. The refusal must read as missing data, not as the
organism being unsupported."""
r = E.assess(ADAPTED, "plant")
assert r["ok"] is False and r["kind"] == "no_codon_table"
assert "missing data file, not an unsupported organism" in r["next"]
def test_a_missing_host_is_refused_because_expression_is_host_specific():
assert E.assess(ADAPTED, "")["ok"] is False
def test_every_known_host_alias_works():
for host in ("e_coli", "ecoli", "yeast", "s_cerevisiae", "human", "h_sapiens"):
assert E.assess(ADAPTED, host)["ok"], host
def test_the_same_sequence_scores_differently_per_host():
"""The point of asking for a host. E. coli-optimised DNA is not
human-optimised."""
a = E.assess(ADAPTED, "e_coli")["codon_adaptation_index"]
b = E.assess(ADAPTED, "human")["codon_adaptation_index"]
assert a != b
def test_too_short_is_refused():
assert E.assess("ATGAAACTG", "e_coli")["kind"] == "too_short"
# ββ the agent tool βββββββββββββββββββββββββββββββββββββββββββββββββββββββ
def test_the_tool_runs_and_needs_a_host():
from dee.core import agent_tools as t
fn = t._TOOLS["assess_expression"]["fn"]
assert fn({"dna": ADAPTED, "host": "e_coli"})["ok"]
assert fn({"host": "e_coli"})["ok"] is False
def test_the_tool_explains_that_protein_input_cannot_answer_this():
from dee.core import agent_tools as t
err = t._TOOLS["assess_expression"]["fn"]({"host": "e_coli"})["error"]
assert "reverse-translate" in err
def test_the_spec_forbids_substituting_another_hosts_table():
from dee.core import agent_tools as t
desc = next(s for s in t.TOOL_SPECS
if s["function"]["name"] == "assess_expression")["function"]["description"]
assert "never substitute another host's table" in desc
assert "no_yield" in desc
# No enum on host β a hardcoded organism list is the hardest gate to see.
props = next(s for s in t.TOOL_SPECS
if s["function"]["name"] == "assess_expression")["function"]["parameters"]["properties"]
assert "enum" not in props["host"]
|