"""Paste-anything target resolution (dee/core/resolve.py). NO NETWORK — Ensembl/NCBI fetchers are monkeypatched. Locks the input classifier (sequence vs symbol vs accession) and the resolve dispatch, including the privacy-preserving rule that raw sequences make no calls. """ import pytest from dee.core import resolve as R from dee.core import exon as E from dee.core.exon import Exon, GeneStructure # ─────────────────────────── classifier ─────────────────────────── @pytest.mark.parametrize("text,kind", [ ("", "empty"), ("ACGT" * 10, "sequence"), # 40 nt one-liner (">hdr\nACGTACGTACGTACGTACGTACGT", "sequence"), # FASTA ("TP53", "symbol"), ("BRCA1", "symbol"), ("ENST00000269305", "ensembl_tx"), ("ENSMUST00000000001", "ensembl_tx"), ("ENSG00000141510", "ensembl_gene"), ("NM_000546", "refseq"), ("NM_000546.6", "refseq"), ("XM_011535573", "refseq"), ("!!!", "unknown"), ]) def test_classify(text, kind): assert R.classify(text)[0] == kind def test_classify_sequence_strips_to_clean_dna(): kind, val = R.classify("acgt ACGT\nnnNN gtca gtca gtca") assert kind == "sequence" assert val == "ACGTACGTNNNNGTCAGTCAGTCA" # ─────────────────────────── raw sequence (offline) ─────────────────────────── def test_resolve_raw_sequence_makes_no_network_call(monkeypatch): # Any network helper firing would be a privacy bug for raw input. def _boom(*a, **k): raise AssertionError("network call on raw-sequence path!") monkeypatch.setattr(E, "_http_get_text", _boom) monkeypatch.setattr(E, "_http_get_json", _boom) monkeypatch.setattr(E, "fetch_gene_structure", _boom) out = R.resolve_target("ACGT" * 20) assert out["ok"] and out["kind"] == "sequence" assert out["sequence"] == "ACGT" * 20 assert "pasted sequence" in out["label"] def test_resolve_short_sequence_errors(): out = R.resolve_target("ACGTACGT") # < 23 nt → symbol attempt → fails assert out["ok"] is False # ─────────────────────────── gene symbol ─────────────────────────── def _fake_gene(): return GeneStructure( organism="human", gene_symbol="TP53", transcript_id="ENST00000269305", strand=1, cds_sequence="ATG" + "GCT" * 50 + "TAA", exons=[Exon(1, 1, 99, 0, 156)], last_junction_cds_pos=0, ) def test_resolve_symbol_requires_organism(): out = R.resolve_target("TP53", organism="") assert out["ok"] is False and "Human or" in out["error"] def test_resolve_symbol_with_organism(monkeypatch): monkeypatch.setattr(E, "fetch_gene_structure", lambda org, sym: _fake_gene()) out = R.resolve_target("TP53", organism="human") assert out["ok"] and out["kind"] == "gene" assert out["gene_symbol"] == "TP53" assert out["sequence"].startswith("ATG") assert "ENST00000269305" in out["label"] def test_resolve_symbol_not_found(monkeypatch): monkeypatch.setattr(E, "fetch_gene_structure", lambda org, sym: None) out = R.resolve_target("ZZZ9", organism="mouse") assert out["ok"] is False and "Couldn't find" in out["error"] # ─────────────────────────── accessions ─────────────────────────── def test_resolve_ensembl_transcript(monkeypatch): monkeypatch.setattr(E, "_http_get_text", lambda url, **k: ">ENST\nATGAAACCCGGGTTTACGTACGTACGT") out = R.resolve_target("ENST00000269305") assert out["ok"] and out["kind"] == "ensembl" assert out["sequence"] == "ATGAAACCCGGGTTTACGTACGTACGT" def test_resolve_ensembl_gene_resolves_canonical(monkeypatch): monkeypatch.setattr(E, "_http_get_json", lambda url, **k: { "Transcript": [ {"id": "ENST_other", "is_canonical": 0}, {"id": "ENST_canon", "is_canonical": 1}, ]}) captured = {} def _seq(url, **k): captured["url"] = url return ">x\nATGCGTACGTACGTACGTACGTACG" monkeypatch.setattr(E, "_http_get_text", _seq) out = R.resolve_target("ENSG00000141510") assert out["ok"] and out["kind"] == "ensembl" assert "ENST_canon" in captured["url"] # used the canonical transcript def test_resolve_refseq(monkeypatch): monkeypatch.setattr(E, "_http_get_text", lambda url, **k: ">NM_000546\nATGGAGGAGCCGCAGTCAGAT") out = R.resolve_target("NM_000546") assert out["ok"] and out["kind"] == "refseq" assert out["sequence"] == "ATGGAGGAGCCGCAGTCAGAT" def test_resolve_ensembl_fetch_failure(monkeypatch): monkeypatch.setattr(E, "_http_get_text", lambda url, **k: None) out = R.resolve_target("ENST00000269305") assert out["ok"] is False and "Ensembl" in out["error"] # ─────────────────────────── UniProt / structure (M5) ─────────────────────────── def test_resolve_uniprot_ok(monkeypatch): captured = {} def _json(url, **k): captured["url"] = url return {"results": [{"primaryAccession": "P04637"}]} monkeypatch.setattr(E, "_http_get_json", _json) out = R.resolve_uniprot("human", "TP53") assert out["ok"] and out["uniprot"] == "P04637" assert out["alphafold_url"] == "https://alphafold.ebi.ac.uk/files/AF-P04637-F1-model_v6.pdb" assert out["alphafold_page"] == "https://alphafold.ebi.ac.uk/entry/P04637" assert "organism_id%3A9606" in captured["url"] and "reviewed%3Atrue" in captured["url"] def test_resolve_uniprot_no_hit(monkeypatch): monkeypatch.setattr(E, "_http_get_json", lambda url, **k: {"results": []}) out = R.resolve_uniprot("human", "ZZZ9") assert out["ok"] is False def test_resolve_uniprot_bad_organism(): assert R.resolve_uniprot("ecoli", "lacZ")["ok"] is False