Spaces:
Running
Running
Download tests/test_audit_msa_randomized.py from Samad14/bio-nexus-api: direct link, hf CLI and curl.
- Browser
- Download file 6.31 kB
-
https://huggingface.co/spaces/Samad14/bio-nexus-api/resolve/main/tests/test_audit_msa_randomized.py
- Command line
-
hf download hf://spaces/Samad14/bio-nexus-api/tests/test_audit_msa_randomized.py
-
curl -L -o test_audit_msa_randomized.py https://huggingface.co/spaces/Samad14/bio-nexus-api/resolve/main/tests/test_audit_msa_randomized.py
6.31 kB
| """Independent deterministic audit checks for BioNexus MSA resilience paths.""" | |
| from __future__ import annotations | |
| import random | |
| from io import StringIO | |
| from types import SimpleNamespace | |
| import pytest | |
| from Bio import Phylo | |
| from app.tools import mafft_local | |
| from app.tools.msa_fallback import ( | |
| _aligner, | |
| _pairwise, | |
| _upgma_newick, | |
| progressive_msa, | |
| ) | |
| SEED = 20260916 | |
| def _parse_alignment(fasta: str) -> dict[str, str]: | |
| records: dict[str, str] = {} | |
| current = None | |
| for raw in fasta.splitlines(): | |
| line = raw.strip() | |
| if not line: | |
| continue | |
| if line.startswith(">"): | |
| current = line[1:].split()[0] | |
| assert current not in records | |
| records[current] = "" | |
| else: | |
| assert current is not None | |
| records[current] += line.upper() | |
| return records | |
| def test_pairwise_fallback_preserves_residues_and_alignment_length(): | |
| g1, g2 = _pairwise(_aligner("dna"), "ACGT", "ACGTT") | |
| assert len(g1) == len(g2) | |
| assert g1.replace("-", "") == "ACGT" | |
| assert g2.replace("-", "") == "ACGTT" | |
| assert "-" in g1 | |
| def test_upgma_branch_lengths_are_ultrametric_for_known_matrix(): | |
| labels = ["A", "B", "C"] | |
| dist = [ | |
| [0.0, 0.2, 0.8], | |
| [0.2, 0.0, 0.8], | |
| [0.8, 0.8, 0.0], | |
| ] | |
| newick = _upgma_newick(labels, dist) | |
| tree = Phylo.read(StringIO(newick), "newick") | |
| leaves = {leaf.name: leaf for leaf in tree.get_terminals()} | |
| root_distances = {name: tree.distance(tree.root, leaf) for name, leaf in leaves.items()} | |
| assert set(leaves) == set(labels) | |
| assert root_distances["A"] == pytest.approx(0.4, abs=1e-6) | |
| assert root_distances["B"] == pytest.approx(0.4, abs=1e-6) | |
| assert root_distances["C"] == pytest.approx(0.4, abs=1e-6) | |
| def test_upgma_rejects_invalid_distance_contracts(): | |
| with pytest.raises(ValueError, match="dimensions"): | |
| _upgma_newick(["A", "B"], [[0.0]]) | |
| with pytest.raises(ValueError, match="symmetric"): | |
| _upgma_newick(["A", "B"], [[0.0, 0.1], [0.2, 0.0]]) | |
| with pytest.raises(ValueError, match="non-negative"): | |
| _upgma_newick(["A", "B"], [[0.0, -0.1], [-0.1, 0.0]]) | |
| def test_progressive_fallback_rejects_duplicate_and_unsafe_ids(): | |
| with pytest.raises(ValueError, match="Duplicate"): | |
| progressive_msa([("a", "ACGT"), ("a", "ACGA")], "dna") | |
| with pytest.raises(ValueError, match="identifier"): | |
| progressive_msa([("a,1", "ACGT"), ("b", "ACGA")], "dna") | |
| def test_seeded_progressive_fallback_preserves_every_sequence_and_taxon(): | |
| rng = random.Random(SEED) | |
| for replicate in range(20): | |
| source: list[tuple[str, str]] = [] | |
| for index in range(rng.randint(2, 6)): | |
| length = rng.randint(8, 24) | |
| sequence = "".join(rng.choice("ACGT") for _ in range(length)) | |
| source.append((f"r{replicate}_s{index}", sequence)) | |
| aligned_fasta, newick = progressive_msa(source, "dna") | |
| aligned = _parse_alignment(aligned_fasta) | |
| assert set(aligned) == {sid for sid, _ in source} | |
| assert len({len(seq) for seq in aligned.values()}) == 1 | |
| for sid, original in source: | |
| assert aligned[sid].replace("-", "") == original | |
| tree = Phylo.read(StringIO(newick), "newick") | |
| leaves = [leaf.name for leaf in tree.get_terminals()] | |
| assert sorted(leaves) == sorted(sid for sid, _ in source) | |
| assert len(leaves) == len(set(leaves)) | |
| def test_mafft_strategy_flags_match_canonical_v7_definitions(): | |
| assert mafft_local.STRATEGY_FLAGS["fft-ns-2"] == ["--retree", "2", "--maxiterate", "0"] | |
| assert mafft_local.STRATEGY_FLAGS["fft-ns-i"] == ["--maxiterate", "1000"] | |
| assert mafft_local.STRATEGY_FLAGS["l-ins-i"] == ["--localpair", "--maxiterate", "1000"] | |
| assert mafft_local.STRATEGY_FLAGS["g-ins-i"] == ["--globalpair", "--maxiterate", "1000"] | |
| assert mafft_local.STRATEGY_FLAGS["e-ins-i"] == ["--genafpair", "--maxiterate", "1000"] | |
| def test_local_mafft_command_and_output_integrity(monkeypatch): | |
| fasta = ">a\nACGT\n>b\nACGTT\n" | |
| alignment = ">a\nACG-T\n>b\nACGTT\n" | |
| calls: list[list[str]] = [] | |
| monkeypatch.setattr(mafft_local, "_ensure_mafft", lambda: "/fake/mafft") | |
| monkeypatch.setattr(mafft_local, "_MAFFT_VERSION", None) | |
| def fake_run(cmd, **kwargs): | |
| calls.append(list(cmd)) | |
| if "--version" in cmd: | |
| return SimpleNamespace(returncode=0, stdout="v7.526 (2024/Apr/26)\n", stderr="") | |
| return SimpleNamespace(returncode=0, stdout=alignment, stderr="") | |
| monkeypatch.setattr(mafft_local.subprocess, "run", fake_run) | |
| result = mafft_local.run_local_mafft(fasta, strategy="e-ins-i", threads=2, timeout=30) | |
| assert result is not None | |
| assert result["aln_fasta"] == alignment | |
| assert result["strategy"] == "e-ins-i" | |
| assert result["threads"] == 2 | |
| assert result["tool_version"] == "7.526" | |
| execution = next(call for call in calls if "--version" not in call) | |
| assert execution[0] == "/fake/mafft" | |
| assert "--genafpair" in execution | |
| assert "--maxiterate" in execution and "1000" in execution | |
| assert "--thread" in execution and "2" in execution | |
| def test_local_mafft_rejects_malformed_or_residue_changing_output(monkeypatch): | |
| fasta = ">a\nACGT\n>b\nACGTT\n" | |
| monkeypatch.setattr(mafft_local, "_ensure_mafft", lambda: "/fake/mafft") | |
| outputs = iter([ | |
| ">a\nACGT\n", # missing b | |
| ">a\nACG-T\n>b\nACGTA\n", # b residue changed | |
| ]) | |
| def fake_run(cmd, **kwargs): | |
| if "--version" in cmd: | |
| return SimpleNamespace(returncode=0, stdout="v7.526\n", stderr="") | |
| return SimpleNamespace(returncode=0, stdout=next(outputs), stderr="") | |
| monkeypatch.setattr(mafft_local.subprocess, "run", fake_run) | |
| assert mafft_local.run_local_mafft(fasta) is None | |
| assert mafft_local.run_local_mafft(fasta) is None | |
| def test_local_mafft_rejects_unknown_strategy_before_execution(monkeypatch): | |
| monkeypatch.setattr(mafft_local, "_ensure_mafft", lambda: "/fake/mafft") | |
| monkeypatch.setattr( | |
| mafft_local.subprocess, | |
| "run", | |
| lambda *args, **kwargs: (_ for _ in ()).throw(AssertionError("MAFFT should not execute")), | |
| ) | |
| assert mafft_local.run_local_mafft(">a\nACGT\n>b\nACGA\n", strategy="invented") is None | |