Spaces:
Running
Running
| """ | |
| Tests for 3b multi-method MSA: | |
| - EBI_TOOLS catalog covers all five methods with correct base URLs | |
| - AlignRequest defaults to clustalo (backwards compatible) | |
| - POST /api/alignment/run validates method before any network call | |
| - run_alignment delegates to the shared run_ebi_msa and preserves response shape | |
| All network calls are mocked; these run fully offline. | |
| """ | |
| import pytest | |
| from fastapi import HTTPException | |
| from app.tools.ebi_msa import EBI_TOOLS, run_ebi_msa | |
| from app.routers import alignment as alignment_router | |
| from app.routers.alignment import AlignRequest, run_alignment | |
| class TestEbiToolsCatalog: | |
| def test_five_methods_present(self): | |
| assert set(EBI_TOOLS) == {"clustalo", "muscle", "kalign", "mafft", "tcoffee"} | |
| def test_base_urls_match_tool_name(self): | |
| for name, url in EBI_TOOLS.items(): | |
| assert url.endswith(f"/rest/{name}") | |
| assert url.startswith("https://www.ebi.ac.uk/Tools/services/rest/") | |
| class TestAlignRequestModel: | |
| def test_default_method_clustalo(self): | |
| req = AlignRequest(sequence=">a\nMEEPQSDPSV") | |
| assert req.method == "clustalo" | |
| def test_explicit_method_accepted(self): | |
| req = AlignRequest(sequence=">a\nMEEPQSDPSV", method="mafft") | |
| assert req.method == "mafft" | |
| class TestRunAlignment: | |
| async def test_invalid_method_rejected_before_network(self): | |
| req = AlignRequest(sequence=">a\nMEEPQSDPSV", method="clustalw") | |
| with pytest.raises(HTTPException) as exc: | |
| await run_alignment(req) | |
| assert exc.value.status_code == 400 | |
| assert "method must be one of" in exc.value.detail | |
| async def test_success_delegates_to_shared_runner(self, monkeypatch): | |
| canned = { | |
| "job_id": "job-1", | |
| "aln_fasta": ">a\nMEEPQSDPSV\n", | |
| "phylotree": "(a:0.0,b:0.1);", | |
| "method": "muscle", | |
| } | |
| async def fake_run_ebi_msa(**kwargs): | |
| assert kwargs["base_url"] == EBI_TOOLS["muscle"] | |
| return canned | |
| monkeypatch.setattr(alignment_router, "run_ebi_msa", fake_run_ebi_msa) | |
| req = AlignRequest(sequence=">a\nMEEPQSDPSV\n>b\nMEEPQSDPSA", method="muscle") | |
| result = await run_alignment(req) | |
| assert result["job_id"] == "job-1" | |
| assert result["aln_fasta"] == canned["aln_fasta"] | |
| assert result["phylotree"] == canned["phylotree"] | |
| assert result["stype"] == "protein" | |
| assert result["method"] == "muscle" | |
| async def test_runner_failure_maps_to_502(self, monkeypatch): | |
| async def fake_run_ebi_msa(**kwargs): | |
| raise ValueError("EBI submission failed (HTTP 500): boom") | |
| monkeypatch.setattr(alignment_router, "run_ebi_msa", fake_run_ebi_msa) | |
| req = AlignRequest(sequence=">a\nMEEPQSDPSV") | |
| with pytest.raises(HTTPException) as exc: | |
| await run_alignment(req) | |
| assert exc.value.status_code == 502 | |
| assert "boom" in exc.value.detail | |