"""Endpoint tests for Plasmid Studio (Flask test client; auth stubbed, no DB). Covers the sign-in gate + import / restriction / digest / export wiring.""" import types import pytest from dee import server SEQ = ("TAATACGACTCACTATAG" + "GAATTC" + "AAAAGGGGAAAAGGGGAAAA" + "GAATTC" + "CCCCTTTTCCCCTTTTCCCC") @pytest.fixture def client(): app = server.create_app() app.config.update(TESTING=True) return app.test_client() def _signed_in(monkeypatch): monkeypatch.setattr(server._auth, "get_auth", lambda: types.SimpleNamespace(anonymous=False, user_id="u1", email="x@y.z", plan="free")) def _anon(monkeypatch): monkeypatch.setattr(server._auth, "get_auth", lambda: types.SimpleNamespace(anonymous=True, user_id=None, email=None, plan="anon")) def test_import_requires_signin(client, monkeypatch): _anon(monkeypatch) r = client.post("/api/plasmid/import", json={"text": SEQ}) assert r.status_code == 403 assert r.get_json()["kind"] == "signin_required" def test_import_raw_dna_annotates(client, monkeypatch): _signed_in(monkeypatch) r = client.post("/api/plasmid/import", json={"text": SEQ}) assert r.status_code == 200 p = r.get_json()["plasmid"] assert p["length"] == len(SEQ) assert p["topology"] == "circular" assert any(f["name"] == "T7 promoter" for f in p["features"]) def test_import_bad_input_400(client, monkeypatch): _signed_in(monkeypatch) r = client.post("/api/plasmid/import", json={"text": "this is not dna @@@"}) assert r.status_code == 400 assert "error" in r.get_json() def test_restriction(client, monkeypatch): _signed_in(monkeypatch) r = client.post("/api/plasmid/restriction", json={"sequence": SEQ, "topology": "circular", "enzymes": ["EcoRI", "BamHI"]}) assert r.status_code == 200 a = r.get_json()["analysis"] assert "EcoRI" in a["double_cutters"] assert "BamHI" in a["non_cutters"] def test_digest_fragments(client, monkeypatch): _signed_in(monkeypatch) r = client.post("/api/plasmid/digest", json={"sequence": SEQ, "topology": "circular", "enzymes": ["EcoRI"]}) assert r.status_code == 200 d = r.get_json()["digest"] assert d["n_fragments"] == 2 assert sum(d["fragments"]) == len(SEQ) def test_digest_requires_enzyme(client, monkeypatch): _signed_in(monkeypatch) r = client.post("/api/plasmid/digest", json={"sequence": SEQ, "topology": "circular", "enzymes": []}) assert r.status_code == 400 def test_import_rejects_oversize_text(client, monkeypatch): _signed_in(monkeypatch) from dee.core import plasmid as PL monkeypatch.setattr(PL, "MAX_INPUT_TEXT", 2000) r = client.post("/api/plasmid/import", json={"text": "A" * 5000}) assert r.status_code == 400 def test_export_sanitizes_malicious_features(client, monkeypatch): _signed_in(monkeypatch) # injection-laden colour + out-of-range coords + a flood of features: # must not crash, must produce a valid GenBank (sanitized server-side). feats = [{"name": "evil", "type": "cds", "start": 0, "end": 18, "color": '#fff" onload="alert(1)'}] feats += [{"name": "f", "type": "misc", "start": 0, "end": 3} for _ in range(20000)] r = client.post("/api/plasmid/export", json={ "sequence": SEQ, "topology": "circular", "name": "pX", "features": feats, "format": "genbank"}) assert r.status_code == 200 body = r.data.decode("utf-8", "replace") assert "LOCUS" in body assert "onload=" not in body # injection scrubbed out of the export def test_export_genbank_and_fasta(client, monkeypatch): _signed_in(monkeypatch) gb = client.post("/api/plasmid/export", json={ "sequence": SEQ, "topology": "circular", "name": "pTEST", "features": [{"name": "T7 promoter", "type": "promoter", "start": 0, "end": 18, "strand": 1}], "format": "genbank"}) assert gb.status_code == 200 assert b"LOCUS" in gb.data and b"promoter" in gb.data fa = client.post("/api/plasmid/export", json={"sequence": SEQ, "topology": "circular", "name": "pTEST", "format": "fasta"}) assert fa.status_code == 200 assert fa.data.startswith(b">")