"""Ranking is the scientific claim this app makes, so it is tested as pure logic.""" import pytest from app.models import Prediction, Variant from app.services import triage def variant(**kw: object) -> Variant: fields: dict = { "id": 1, "chrom": "22", "pos": 100, "ref": "A", "alt": "G", "gene": "NF2", "impact": "HIGH", "consequence": "frameshift_variant", "gnomad_af": None, "clinvar_sig": None, "annotations": {}, } fields.update(kw) score = fields.pop("score", None) v = Variant(**fields) if score is not None: v.prediction = Prediction(model_name="m", model_version="1", score=float(score)) return v def test_weights_sum_to_one() -> None: assert sum(triage.WEIGHTS.values()) == pytest.approx(1.0) @pytest.mark.parametrize( ("af", "expected"), [(None, 1.0), (0.0, 1.0), (0.00005, 0.8), (0.0005, 0.5), (0.005, 0.2), (0.05, 0.0)], ) def test_rarity_rewards_absence_from_gnomad(af: float | None, expected: float) -> None: assert triage.rarity_score(af) == expected @pytest.mark.parametrize( ("impact", "expected"), [("HIGH", 1.0), ("MODERATE", 0.6), ("LOW", 0.2), ("MODIFIER", 0.0), (None, 0.0), ("?", 0.0)], ) def test_consequence_severity(impact: str | None, expected: float) -> None: assert triage.consequence_score(impact) == expected def test_phenotype_match_is_the_fraction_of_the_patients_terms() -> None: gene_terms = {"NF2": {"HP:0000365", "HP:0009592"}} case_terms = ["HP:0000365", "HP:0009592", "HP:0002321", "HP:0000598"] score, matched = triage.phenotype_score("NF2", case_terms, gene_terms) assert score == 0.5 assert matched == ["HP:0000365", "HP:0009592"] def test_phenotype_match_is_zero_for_genes_hpo_has_never_annotated() -> None: assert triage.phenotype_score("NOVEL1", ["HP:0000365"], {}) == (0.0, []) def test_phenotype_match_is_zero_when_no_phenotype_was_entered() -> None: assert triage.phenotype_score("NF2", [], {"NF2": {"HP:0000365"}}) == (0.0, []) def test_the_funnel_counts_each_narrowing_step() -> None: variants = [ variant(id=1, gnomad_af=None, impact="HIGH", gene="NF2"), # rare, coding, matched variant(id=2, gnomad_af=0.0002, impact="MODERATE", gene="CHEK2"), # rare, coding variant(id=3, gnomad_af=0.3, impact="HIGH", gene="NF2"), # common variant(id=4, gnomad_af=None, impact="MODIFIER", gene="NF2"), # rare, non-coding ] funnel = triage.funnel(variants, case_terms=["HP:0000365"], gene_terms={"NF2": {"HP:0000365"}}) assert (funnel.total, funnel.rare, funnel.candidates, funnel.phenotype_matched) == (4, 3, 2, 1) def test_the_diagnosis_outranks_the_noise() -> None: gene_terms = {"NF2": {"HP:0000365", "HP:0009592"}} case_terms = ["HP:0000365", "HP:0009592"] diagnosis = variant(id=1, gene="NF2", impact="HIGH", gnomad_af=None, score=0.94) plausible = variant(id=2, gene="CHEK2", impact="MODERATE", gnomad_af=0.0004, score=0.55) noise = variant(id=3, gene="TTN", impact="MODERATE", gnomad_af=0.0009, score=0.10) ranked = triage.rank([noise, plausible, diagnosis], case_terms, gene_terms) assert [c.variant.id for c in ranked] == [1, 2, 3] top = ranked[0] assert top.matched_terms == case_terms assert top.components["phenotype"] == 1.0 assert top.score == pytest.approx(0.35 + 0.25 + 0.20 + 0.20 * 0.94) def test_an_unscored_variant_still_ranks_and_says_so() -> None: [candidate] = triage.rank([variant(id=1, gnomad_af=None)], [], {}) assert candidate.components["model"] == 0.0 assert candidate.scored is False def test_common_and_non_coding_variants_are_not_candidates() -> None: variants = [ variant(id=1, gnomad_af=0.2, impact="HIGH"), variant(id=2, gnomad_af=None, impact="MODIFIER"), ] assert triage.rank(variants, [], {}) == [] def test_ranking_is_deterministic_for_equal_scores() -> None: a = variant(id=7, gene="AAA", chrom="1", pos=10, gnomad_af=None) b = variant(id=3, gene="BBB", chrom="1", pos=10, gnomad_af=None) assert [c.variant.id for c in triage.rank([a, b], [], {})] == [3, 7]