syntheogenesis / tests /test_expression.py
github-actions[bot]
Deploy 90a38dc
1bf37a2
Raw
History Blame Contribute Delete
8.22 kB
"""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"]