Plant-DNA-Designer / cool /tests /test_api.py
Dinesh4311's picture
Add application code
aa752f5 verified
Raw
History Blame Contribute Delete
4.57 kB
"""API-level smoke tests using FastAPI's TestClient."""
import os
import sys
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from fastapi.testclient import TestClient # noqa: E402
from api.main import app # noqa: E402
client = TestClient(app)
def test_health():
r = client.get("/api/health")
assert r.status_code == 200
assert r.json()["status"] == "ok"
def test_index_page_renders():
r = client.get("/")
assert r.status_code == 200
assert "Plant DNA Designer" in r.text
def test_traits_partial():
r = client.get("/traits/rice")
assert r.status_code == 200
assert "Bacterial blight resistance" in r.text
def test_traits_unknown_species_404():
assert client.get("/traits/nope").status_code == 404
def test_design_endpoint():
r = client.post("/api/design", json={
"species": "rice", "gc_target": 45, "traits": ["Drought tolerance"],
"codon_table": "rice", "protein": "MKAILV", "cas_type": "Cas9 (NGG)",
"grna_num": 3, "population_size": 20, "generations": 10, "num_sequences": 1,
})
assert r.status_code == 200, r.text
body = r.json()
assert body["sequence"].startswith("ACGTGG") # required motif prefix
assert {"gc_content", "melting_temp", "molecular_weight", "cai", "mrna_stability"}.issubset(set(body["metrics"]))
def test_design_rejects_bad_protein():
r = client.post("/api/design", json={
"species": "rice", "traits": ["Drought tolerance"], "protein": "MKZZZ",
"population_size": 20, "generations": 5,
})
assert r.status_code == 422 # pydantic validation
def test_design_requires_trait():
r = client.post("/api/design", json={
"species": "rice", "traits": [], "protein": "MKAILV",
"population_size": 20, "generations": 10,
})
assert r.status_code == 400
def test_analyze_endpoint():
r = client.post("/api/analyze", json={"dna": "TATAATCAATGATAGGCC", "codon_table": "rice"})
assert r.status_code == 200
assert "metrics" in r.json() and "motifs" in r.json()
def test_export_fasta():
r = client.get("/api/export/fasta", params={"dna": "ATGAAAGCT", "name": "rice"})
assert r.status_code == 200
assert r.text.startswith(">")
def test_mechanism_endpoint():
r = client.get("/api/mechanism/Drought tolerance")
assert r.status_code == 200
body = r.json()
assert body["primary_effector"]["gene"] == "DREB2A"
assert "effectors" in body
def test_mechanism_endpoint_unknown_404():
assert client.get("/api/mechanism/not a trait").status_code == 404
def test_design_without_protein_uses_trait_effector():
# No protein supplied → resolved from the trait's validated effector.
r = client.post("/api/design", json={
"species": "rice", "gc_target": 45, "traits": ["Drought tolerance"],
"codon_table": "rice", "cas_type": "Cas9 (NGG)",
"grna_num": 3, "population_size": 20, "generations": 10, "num_sequences": 1,
})
assert r.status_code == 200, r.text
bt = r.json()["biological_target"]
assert bt["primary"]["gene"] == "DREB2A"
assert bt["encoded_protein_source"] == "trait-derived effector"
def test_design_pareto_mode_returns_front():
r = client.post("/api/design", json={
"species": "rice", "gc_target": 50, "traits": ["Drought tolerance"],
"codon_table": "rice", "cas_type": "Cas9 (NGG)", "grna_num": 3,
"population_size": 30, "generations": 15, "pareto": True,
})
assert r.status_code == 200, r.text
body = r.json()
assert body["pareto_front"] and len(body["pareto_front"]) >= 1
assert "axes" in body["pareto_front"][0]
assert "pareto" in body["ga_report"]["mode"]
def test_design_includes_report_card():
r = client.post("/api/design", json={
"species": "rice", "gc_target": 45, "traits": ["Drought tolerance"],
"codon_table": "rice", "cas_type": "Cas9 (NGG)", "grna_num": 3,
"population_size": 20, "generations": 10, "num_sequences": 1,
})
assert r.status_code == 200, r.text
card = r.json()["report_card"]
assert card["grade"] in ("Strong", "Good", "Needs work")
assert 0 <= card["score"] <= 100
labels = {it["label"] for it in card["items"]}
assert {"Expression strength", "Plant safety", "Silencing risk", "Synthesis-ready"}.issubset(labels)
for it in card["items"]:
assert it["status"] in ("good", "watch", "fix")