import json
from pathlib import Path
import pytest
import ferro_hgvs
CORPUS = [
"NM_000088.3:c.459A>G",
"NC_000001.11:g.1000A>T",
"NM_000088.3:c.100_102del",
"NM_000088.3:c.100dup",
"NP_000079.2:p.Gly100Ala",
"NM_000088.3:c.100+5G>A",
"NR_003051.3:n.100A>G",
]
class TestParseIdempotency:
@pytest.mark.parametrize("variant", CORPUS)
def test_roundtrip_is_a_fixed_point(self, variant: str) -> None:
once = ferro_hgvs.parse(variant)
twice = ferro_hgvs.parse(str(once))
assert once == twice
assert hash(once) == hash(twice)
def test_hgvs_variant_is_set_usable(self) -> None:
variants = [ferro_hgvs.parse(v) for v in CORPUS]
again = [ferro_hgvs.parse(str(v)) for v in variants]
assert len(set(variants + again)) == len(CORPUS)
class TestCrossTypeEqualityDoesNotRaise:
def test_hgvs_variant_cross_type(self) -> None:
v = ferro_hgvs.parse("NM_000088.3:c.459A>G")
assert (v == None) is False assert v != "NM_000088.3:c.459A>G"
assert (v == 5) is False
def test_protein_effect_cross_type(self) -> None:
effect = ferro_hgvs.EffectPredictor().classify_indel(3, 0)
assert (effect == None) is False assert effect != "frameshift"
assert (effect == 5) is False
def test_projection_cross_type(self, projector: ferro_hgvs.VariantProjector) -> None:
proj = projector.project("NC_000001.11:g.1003C>A", transcript="NM_TEST.1")
assert (proj == None) is False assert proj != "NM_TEST.1"
assert (proj == 5) is False
effect = ferro_hgvs.EffectPredictor().classify_indel(3, 0)
assert proj != effect
class TestProteinEffectValueSemantics:
def test_equal_effects_are_equal_and_hash_equal(self) -> None:
predictor = ferro_hgvs.EffectPredictor()
a = predictor.classify_indel(3, 0)
b = predictor.classify_indel(3, 0)
assert a == b
assert hash(a) == hash(b)
def test_distinct_effects_differ(self) -> None:
predictor = ferro_hgvs.EffectPredictor()
inframe = predictor.classify_indel(3, 0)
frameshift = predictor.classify_indel(1, 0)
assert inframe != frameshift
def test_effect_is_set_usable(self) -> None:
predictor = ferro_hgvs.EffectPredictor()
effects = [
predictor.classify_indel(3, 0),
predictor.classify_indel(3, 0),
predictor.classify_indel(1, 0),
]
assert len(set(effects)) == 2
@pytest.fixture
def projector(tmp_path: Path) -> ferro_hgvs.VariantProjector:
fixture = {
"transcripts": [
{
"id": "NM_TEST.1",
"gene_symbol": "TESTGENE",
"strand": "+",
"sequence": "ATGCGCTAA",
"cds_start": 1,
"cds_end": 9,
"exons": [
{
"number": 1,
"start": 1,
"end": 9,
"genomic_start": 1000,
"genomic_end": 1008,
}
],
"chromosome": "chr1",
"genomic_start": 1000,
"genomic_end": 1008,
}
],
"genomic_sequences": {"chr1": "N" * 999 + "ATGCGCTAA" + "N" * 100},
}
path = tmp_path / "transcripts.json"
path.write_text(json.dumps(fixture))
return ferro_hgvs.VariantProjector(reference_json=str(path))
class TestVariantProjectionValueSemantics:
def test_equal_projections_are_equal_and_hash_equal(
self, projector: ferro_hgvs.VariantProjector
) -> None:
a = projector.project("NC_000001.11:g.1003C>A", transcript="NM_TEST.1")
b = projector.project("NC_000001.11:g.1003C>A", transcript="NM_TEST.1")
assert a == b
assert hash(a) == hash(b)
def test_distinct_projections_differ(self, projector: ferro_hgvs.VariantProjector) -> None:
a = projector.project("NC_000001.11:g.1003C>A", transcript="NM_TEST.1")
c = projector.project("NC_000001.11:g.1004G>T", transcript="NM_TEST.1")
assert a != c
def test_projection_is_set_usable(self, projector: ferro_hgvs.VariantProjector) -> None:
a = projector.project("NC_000001.11:g.1003C>A", transcript="NM_TEST.1")
b = projector.project("NC_000001.11:g.1003C>A", transcript="NM_TEST.1")
c = projector.project("NC_000001.11:g.1004G>T", transcript="NM_TEST.1")
assert len({a, b, c}) == 2
def test_warning_carrying_projection_is_hashable(
self, projector: ferro_hgvs.VariantProjector
) -> None:
results = projector.project_all("NC_000001.11:g.[1003C>A;1003C>G]")
warned = [p for p in results if p.has_warnings()]
assert warned, "expected a warning-carrying projection from the cis-allele"
proj = warned[0]
assert isinstance(hash(proj), int)
assert len({proj}) == 1
assert proj == proj