diff --git a/AGENTS.md b/AGENTS.md index 74d2c0c..76289d3 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -6,7 +6,7 @@ This repo uses automation agents (local or CI) to keep code healthy and consiste - **Package management**: use `pipenv` for environment and dependency management, but maintain `pyproject.toml` (preferred) and keep `setup.py` / `setup.cfg` in sync if present. - **Python**: runtime support starts at **Python 3.11**; develop and run CI on **Python 3.13**. - **Static checks**: enforce `mypy` and `flake8` on all tracked Python files. -- **Tests**: run `pytest` with `pytest-cov`; fail if coverage drops below the configured threshold. +- **Tests**: run `pytest tests/unit --cov=postalign` and `behave tests/component` (which downloads minimap2 2.17); fail if coverage drops below the configured threshold. - **Mocks**: use `unittest.mock`; avoid `monkeypatch` or plain stubs. - **Coverage pragmas**: annotate unavoidable no-op statements with `# pragma: no cover` and a brief justification. File-wide pragmas are not @@ -41,7 +41,8 @@ pipenv run flake8 . pipenv run mypy . # Tests + coverage -pipenv run pytest --cov=postalign --cov-report=term-missing +pipenv run pytest tests/unit --cov=postalign --cov-report=term-missing +pipenv run behave tests/component # Update Pipfile.lock pipenv lock --dev --clear @@ -53,10 +54,10 @@ make requirements.txt ## CI expectations (example) - Use Python 3.13 runner (project supports Python 3.11+). - Steps: - 1. `pip install -e .[dev]` - 2. `flake8 .` - 3. `mypy .` - 4. `pytest --cov=postalign --cov-report=xml` (record artifact, enforce threshold) + 1. `pipenv run pip install -e .[dev]` + 2. `pipenv run flake8 .` + 3. `pipenv run mypy .` + 4. `pipenv run pytest --cov=postalign --cov-report=xml` (record artifact, enforce threshold) ## Sphinx docstring style (minimal rules) - Use Sphinx fields: `:param name:`, `:type name:`, `:returns:`, `:rtype:`, `:raises:`. @@ -68,24 +69,10 @@ make requirements.txt # With pipenv pipenv update # safe minor/patch upgrades per constraints pipenv update - -# If using pip/requirements: -pip list --outdated -pip install -U -# If using pip-tools: -pip-compile --upgrade -pip-sync ``` - Pin in requirements/lockfile as appropriate. - Run tests and type checks after any upgrade. -## Pre-commit (recommended) -```bash -pip install pre-commit -pre-commit install -# Example hooks: flake8, mypy (via local hook), trailing-whitespace, end-of-file-fixer -``` - ## Pull request checklist - [ ] Code runs on Python 3.11+ (tests executed on Python 3.13). - [ ] Added/updated tests for all touched code. diff --git a/Makefile b/Makefile index 4864921..95377e9 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,13 @@ requirements.txt: Pipfile.lock - @pipenv requirements --from-pipfile > requirements.txt - @sed -i '' '/^-e \.$$/d' requirements.txt + @pipenv requirements --from-pipfile | grep -v '^-i' > requirements.txt + +test-unit: + @pytest tests/unit + +test-component: + @behave tests/component + +test: test-unit test-component build-docker-builder: requirements.txt @docker pull ubuntu:18.04 @@ -33,4 +40,4 @@ dist/postalign_linux-amd64.tar.gz: dist/linux-amd64 dist: dist/postalign_linux-amd64.tar.gz build: dist -.PHONY: build-docker-builder push-docker-builder +.PHONY: test-unit test-component test build-docker-builder push-docker-builder diff --git a/Pipfile b/Pipfile index 7ca26e0..05737f0 100644 --- a/Pipfile +++ b/Pipfile @@ -10,6 +10,7 @@ mypy = "*" flake8 = "*" pytest = "*" pytest-cov = "*" +behave = "*" [packages] more-itertools = "*" diff --git a/Pipfile.lock b/Pipfile.lock index c157eb4..c64b358 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "b7e026aaf72b897c53851a168145143e46c073196457d50f9bdb6f815797e6f7" + "sha256": "95f5c4dee270345d9c1f7d7fb54885ef140ba1534916865e111ea5636e84b7a3" }, "pipfile-spec": 6, "requires": { @@ -95,11 +95,11 @@ }, "markdown-it-py": { "hashes": [ - "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1", - "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb" + "sha256:87327c59b172c5011896038353a81343b6754500a08cd7a4973bb48c6d578147", + "sha256:cb0a2b4aa34f932c007117b194e945bd74e0ec24133ceb5bac59009cda1cb9f3" ], - "markers": "python_version >= '3.8'", - "version": "==3.0.0" + "markers": "python_version >= '3.10'", + "version": "==4.0.0" }, "mdurl": { "hashes": [ @@ -285,6 +285,23 @@ "markers": "python_version >= '3.8'", "version": "==3.0.0" }, + "behave": { + "hashes": [ + "sha256:657ee8c167af716e6ab7b817100bb427a2674440d431751af47af9ffd8a90b57", + "sha256:b32ec1a1ed67f23adc007c1cb7ee31ac1c939638d30c8e3e27a00d9ddb063c09" + ], + "index": "pypi", + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2'", + "version": "==1.3.0" + }, + "colorama": { + "hashes": [ + "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", + "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6" + ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5, 3.6'", + "version": "==0.4.6" + }, "coverage": { "extras": [ "toml" @@ -382,6 +399,22 @@ "markers": "python_version >= '3.9'", "version": "==7.10.3" }, + "cucumber-expressions": { + "hashes": [ + "sha256:86230d503cdda7ef35a1f2072a882d7d57c740aa4c163c82b07f039b6bc60c42", + "sha256:86ce41bf28ee520408416f38022e5a083d815edf04a0bd1dae46d474ca597c60" + ], + "markers": "python_version >= '3.8' and python_version < '4.0'", + "version": "==18.0.1" + }, + "cucumber-tag-expressions": { + "hashes": [ + "sha256:b60aa2cdbf9ac43e28d9b0e4fd49edf9f09d5d941257d2912f5228f9d166c023", + "sha256:f94404b656831c56a3815da5305ac097003884d2ae64fa51f5f4fad82d97e583" + ], + "markers": "python_version >= '2.7'", + "version": "==6.2.0" + }, "decorator": { "hashes": [ "sha256:65f266143752f734b0a7cc83c46f4618af75b8c5911b00ccb61d0ac9b6da0360", @@ -517,6 +550,21 @@ "markers": "python_version >= '3.8'", "version": "==25.0" }, + "parse": { + "hashes": [ + "sha256:967095588cb802add9177d0c0b6133b5ba33b1ea9007ca800e526f42a85af558", + "sha256:b41d604d16503c79d81af5165155c0b20f6c8d6c559efa66b4b695c3e5a0a0ce" + ], + "version": "==1.20.2" + }, + "parse-type": { + "hashes": [ + "sha256:5e1ec10440b000c3f818006033372939e693a9ec0176f446d9303e4db88489a6", + "sha256:83d41144a82d6b8541127bf212dd76c7f01baff680b498ce8a4d052a7a5bce4c" + ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1'", + "version": "==0.6.4" + }, "parso": { "hashes": [ "sha256:a418670a20291dacd2dddc80c377c5c3791378ee1e8d12bffc35420643d43f18", @@ -648,6 +696,14 @@ "markers": "python_version >= '3.9'", "version": "==80.9.0" }, + "six": { + "hashes": [ + "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", + "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81" + ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2'", + "version": "==1.17.0" + }, "stack-data": { "hashes": [ "sha256:836a778de4fec4dcd1dcd89ed8abff8a221f58308462e1c4aa2a3cf30148f0b9", diff --git a/README.md b/README.md index 7b70398..2b334a8 100644 --- a/README.md +++ b/README.md @@ -33,7 +33,7 @@ The typical workflow runs minimap2 to generate alignments and writes the post‑processed result as JSON: ```bash -pipenv run post-align -i reads.fasta -r ref.fasta -o result.json -f MINIMAP2 \ +pipenv run post-align -i reads.fas -r ref.fas -o result.json -f MINIMAP2 \ save-json ``` @@ -44,7 +44,8 @@ Linting, type checking and tests: ```bash pipenv run flake8 . pipenv run mypy . -pipenv run pytest --cov=postalign --cov-report=term-missing +pipenv run pytest tests/unit --cov=postalign --cov-report=term-missing +pipenv run behave tests/component # downloads minimap2 2.17 automatically ``` The project currently relies on a minimal `setup.py` for building Cython diff --git a/postalign/processors/codon_alignment.py b/postalign/processors/codon_alignment.py index 44ff6e9..8bd651d 100644 --- a/postalign/processors/codon_alignment.py +++ b/postalign/processors/codon_alignment.py @@ -609,32 +609,18 @@ def parse_gap_placement_score(value: str) -> dict[ return scores -@cython.ccall -@cython.returns(dict) -def gap_placement_score_callback( - ctx: typer.Context, - param: typer.CallbackParam, - value: tuple[str, ...], -) -> dict[int, dict[tuple[int, int], int]]: - """Parse ``--gap-placement-score`` arguments. - - :param ctx: Typer context. - :param param: Callback parameter definition. - :param value: Tuple of score specifications. - :returns: Nested mapping of gap placement scores. - :raises typer.BadParameter: On invalid input value. - """ - if not param.name: - raise typer.BadParameter( - 'Internal error (gap_placement_score_callback:1)' - ) - try: - result: dict[ - int, dict[tuple[int, int], int] - ] = parse_gap_placement_score(','.join(value)) - return result - except ValueError as exp: - raise typer.BadParameter(str(exp)) +_GAP_PLACEMENT_HELP = ( + 'Bonus (positive number) or penalty (negative number) for gaps ' + 'appear at certain NA position (relative to the WHOLE ref seq) ' + 'in the ref seq (ins) or target seq (del). For example, ' + '204ins:-5 is a -5 penalty designate to a gap with any size ' + 'gap in ref seq after NA position 204 (AA position 68). ' + '2041/12del:10 is a +10 score for a 12 NAs size ' + '(4 codons) gap in target seq at NA position 2041, ' + 'equivalent to deletion at 681, 682, 683 and 684 AA position. ' + 'Multiple scores can be delimited by commas, such as ' + '204ins:-5,2041/12del:10.' +) @cli.command('codon-alignment') @@ -662,29 +648,12 @@ def codon_alignment( ), ] = 10, gap_placement_score: Annotated[ - # For NASize, 0 means any size - # Indel NAPos NASize Score - # v v v v - dict[int, dict[tuple[int, int], int]], + list[str] | None, typer.Option( - (), '--gap-placement-score', - callback=gap_placement_score_callback, - multiple=True, - help=( - 'Bonus (positive number) or penalty (negative number) for gaps' - ' appear at certain NA position ' - '(relative to the WHOLE ref seq) ' - 'in the ref seq (ins) or target seq (del). For example, ' - '204ins:-5 is a -5 penalty designate to a gap with any size ' - 'gap in ref seq after NA position 204 (AA position 68). ' - '2041/12del:10 is a +10 score for a 12 NAs size' - ' (4 codons) gap in target seq at NA position 2041,' - ' equivalent to deletion at 681, 682, 683 and 684 AA position.' - ' Multiple scores can be delimited by commas, such as ' - '204ins:-5,2041/12del:10.' - ), - ), # type: ignore[call-overload] - ] = {}, + None, '--gap-placement-score', + help=_GAP_PLACEMENT_HELP, + ), + ] = None, ref_start: Annotated[int, typer.Argument()] = 1, ref_end: Annotated[int, typer.Argument()] = -1, # XXX: see https://github.com/cython/cython/issues/2753 @@ -699,11 +668,17 @@ def codon_alignment( :param min_gap_distance: Minimal nucleotide gap distance of the output. :param window_size: Amino acid window size for finding optimal placement. :param gap_placement_score: Bonus or penalty scores for gaps at specific - positions. + positions, expressed as repeated ``--gap-placement-score`` options in + the form ``[/](ins|del):``. :param ref_start: Start position relative to reference sequence. :param ref_end: End position relative to reference sequence. :raises typer.BadParameter: If provided positions are invalid. """ + if gap_placement_score: + gap_scores = parse_gap_placement_score(','.join(gap_placement_score)) + else: + gap_scores = {REFGAP: {}, SEQGAP: {}} + if ref_start < 1: raise typer.BadParameter( f'argument :{ref_start} must be not less than 1' @@ -740,7 +715,7 @@ def processor( seq, min_gap_distance, window_size, - gap_placement_score, + gap_scores, ref_start, my_ref_end ) diff --git a/requirements.txt b/requirements.txt index b5262de..8c1d0ec 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,8 +1,7 @@ --i https://pypi.org/simple cython==3.1.2; python_version >= '3.8' more-itertools==10.7.0; python_version >= '3.9' orjson==3.11.1; python_version >= '3.9' pafpy==0.2.0; python_version >= '3.6' and python_version < '4.0' +rich==14.1.0; python_full_version >= '3.8.0' +typer==0.16.0; python_version >= '3.7' types-setuptools==80.9.0.20250809; python_version >= '3.9' -rich==14.1.0; python_version >= '3.9' -typer==0.16.0; python_version >= '3.9' diff --git a/tests/component/data/hiv1_reference.fas b/tests/component/data/hiv1_reference.fas new file mode 100644 index 0000000..3dcdd2b --- /dev/null +++ b/tests/component/data/hiv1_reference.fas @@ -0,0 +1,163 @@ +>HXB2_x_ConsensusB +tggaagggctaattcactcccaacgaagacaagatatccttgatctgtggatctaccaca +cacaaggctacttccctgattGgcagaactacacaccagggccagggatcagatatccac +tgacctttggatggtgctacaagctagtaccagttgagccagagaagttagaagaagcca +acaaaggagagaacaccagcttgttacaccctgtgagcctgcatggaatggatgacccgg +agagagaagtgttagagtggaggtttgacagccgcctagcatttcatcacatggcccgag +agctgcatccggagtacttcaagaactgctgacatcgagcttgctacaagggactttccg +ctggggactttccagggaggcgtggcctgggcgggactggggagtggcgagccctcagat +cctgcatataagcagctgctttttgcctgtactgggtctctctggttagaccagatctga +gcctgggagctctctggctaactagggaacccactgcttaagcctcaataaagcttgcct +tgagtgcttcaagtagtgtgtgcccgtctgttgtgtgactctggtaactagagatccctc +agacccttttagtcagtgtggaaaatctctagcagtggcgcccgaacagggacctgaaag +cgaaagggaaaccagaggagctctctcgacgcaggactcggcttgctgaagcgcgcacgg +caagaggcgaggggcggcgactggtgagtacgccaaaaattttgactagcggaggctaga +aggagagagATGGGTGCGAGAGCGTCAGTATTAAGCGGGGGAGAATTAGATAGATGGGAA +AAAATTCGGTTAAGGCCAGGGGGAAAGAAAAAATATAAATTAAAACATATAGTATGGGCA +AGCAGGGAGCTAGAACGATTCGCAGTTAATCCTGGCCTGTTAGAAACATCAGAAGGCTGT +AGACAAATACTGGGACAGCTACAACCATCCCTTCAGACAGGATCAGAAGAACTTAGATCA +TTATATAATACAGTAGCAACCCTCTATTGTGTGCATCAAAGGATAGAGGTAAAAGACACC +AAGGAAGCTTTAGAGAAGATAGAGGAAGAGCAAAACAAAAGTAAGAAAAAAGCACAGCAA +GCAGCAGCTGACACAGGAAACAGCAGCCAGGTCAGCCAAAATTACCCTATAGTGCAGAAC +CTCCAGGGGCAAATGGTACATCAGGCCATATCACCTAGAACTTTAAATGCATGGGTAAAA +GTAGTAGAAGAGAAGGCTTTCAGCCCAGAAGTAATACCCATGTTTTCAGCATTATCAGAA +GGAGCCACCCCACAAGATTTAAACACCATGCTAAACACAGTGGGGGGACATCAAGCAGCC +ATGCAAATGTTAAAAGAGACCATCAATGAGGAAGCTGCAGAATGGGATAGATTGCATCCA +GTGCATGCAGGGCCTATTGCACCAGGCCAGATGAGAGAACCAAGGGGAAGTGACATAGCA +GGAACTACTAGTACCCTTCAGGAACAAATAGGATGGATGACAAATAATCCACCTATCCCA +GTAGGAGAAATCTATAAAAGATGGATAATCCTGGGATTAAATAAAATAGTAAGAATGTAT +AGCCCTACCAGCATTCTGGACATAAGACAAGGACCAAAGGAACCCTTTAGAGACTATGTA +GACCGGTTCTATAAAACTCTAAGAGCCGAGCAAGCTTCACAGGAGGTAAAAAATTGGATG +ACAGAAACCTTGTTGGTCCAAAATGCGAACCCAGATTGTAAGACTATTTTAAAAGCATTG +GGACCAGCAGCTACACTAGAAGAAATGATGACAGCATGTCAGGGAGTGGGAGGACCCGGC +CATAAAGCAAGAGTTTTGGCTGAAGCAATGAGCCAAGTAACAAATTCAGCTACCATAATG +ATGCAGAGAGGCAATTTTAGGAACCAAAGAAAGACTGTTAAGTGTTTCAATTGTGGCAAA +GAAGGGCACATAGCCAAAAATTGCAGGGCCCCTAGGAAAAAGGGCTGTTGGAAATGTGGA +AAGGAAGGACACCAAATGAAAGATTGTACTGAGAGACAGGCTAATTTTTTAGGGAAGATC +TGGCCTTCCCACAAGGGAAGGCCAGGGAATTTTCTTCAGAGCAGACCAGAGCCAACAGCC +CCACCAGAAGAGAGCTTCAGGTTTGGGGAAGAGACAACAACTCCCTCTCAGAAGCAGGAG +CCGATAGACAAGGAACTGTATCCTTTAGCTTCCCTCAGATCACTCTTTGGCAACGACCCC +TCGTCACAATAAAGATAGGGGGGCAACTAAAGGAAGCTCTATTAGATACAGGAGCAGATG +ATACAGTATTAGAAGAAATGAATTTGCCAGGAAGATGGAAACCAAAAATGATAGGGGGAA +TTGGAGGTTTTATCAAAGTAAGACAGTATGATCAGATACTCATAGAAATCTGTGGACATA +AAGCTATAGGTACAGTATTAGTAGGACCTACACCTGTCAACATAATTGGAAGAAATCTGT +TGACTCAGATTGGTTGCACTTTAAATTTTCCCATTAGTCCTATTGAAACTGTACCAGTAA +AATTAAAGCCAGGAATGGATGGCCCAAAAGTTAAACAATGGCCATTGACAGAAGAAAAAA +TAAAAGCATTAGTAGAAATTTGTACAGAAATGGAAAAGGAAGGGAAAATTTCAAAAATTG +GGCCTGAAAATCCATACAATACTCCAGTATTTGCCATAAAGAAAAAAGACAGTACTAAAT +GGAGAAAATTAGTAGATTTCAGAGAACTTAATAAGAGAACTCAAGACTTCTGGGAAGTTC +AATTAGGAATACCACATCCCGCAGGGTTAAAAAAGAAAAAATCAGTAACAGTACTGGATG +TGGGTGATGCATATTTTTCAGTTCCCTTAGATAAAGACTTCAGGAAGTATACTGCATTTA +CCATACCTAGTATAAACAATGAGACACCAGGGATTAGATATCAGTACAATGTGCTTCCAC +AGGGATGGAAAGGATCACCAGCAATATTCCAAAGTAGCATGACAAAAATCTTAGAGCCTT +TTAGAAAACAAAATCCAGACATAGTTATCTATCAATACATGGATGATTTGTATGTAGGAT +CTGACTTAGAAATAGGGCAGCATAGAACAAAAATAGAGGAACTGAGACAACATCTGTTGA +GGTGGGGATTTACCACACCAGACAAAAAACATCAGAAAGAACCTCCATTCCTTTGGATGG +GTTATGAACTCCATCCTGATAAATGGACAGTACAGCCTATAGTGCTGCCAGAAAAAGACA +GCTGGACTGTCAATGACATACAGAAGTTAGTGGGAAAATTGAATTGGGCAAGTCAGATTT +ATGCAGGGATTAAAGTAAAGCAATTATGTAAACTCCTTAGGGGAACCAAAGCACTAACAG +AAGTAATACCACTAACAGAAGAAGCAGAGCTAGAACTGGCAGAAAACAGGGAGATTCTAA +AAGAACCAGTACATGGAGTGTATTATGACCCATCAAAAGACTTAATAGCAGAAATACAGA +AGCAGGGGCAAGGCCAATGGACATATCAAATTTATCAAGAGCCATTTAAAAATCTGAAAA +CAGGAAAGTATGCAAGAATGAGGGGTGCCCACACTAATGATGTAAAACAATTAACAGAGG +CAGTGCAAAAAATAGCCACAGAAAGCATAGTAATATGGGGAAAGACTCCTAAATTTAAAC +TACCCATACAAAAAGAAACATGGGAAGCATGGTGGACAGAGTATTGGCAAGCCACCTGGA +TTCCTGAGTGGGAGTTTGTCAATACCCCTCCCTTAGTGAAATTATGGTACCAGTTAGAGA +AAGAACCCATAGTAGGAGCAGAAACTTTCTATGTAGATGGGGCAGCTAATAGGGAGACTA +AATTAGGAAAAGCAGGATATGTTACTGACAGAGGAAGACAAAAAGTTGTCTCCCTAACTG +ACACAACAAATCAGAAGACTGAGTTACAAGCAATTCATCTAGCTTTGCAGGATTCGGGAT +TAGAAGTAAACATAGTAACAGACTCACAATATGCATTAGGAATCATTCAAGCACAACCAG +ATAAAAGTGAATCAGAGTTAGTCAGTCAAATAATAGAGCAGTTAATAAAAAAGGAAAAGG +TCTACCTGGCATGGGTACCAGCACACAAAGGAATTGGAGGAAATGAACAAGTAGATAAAT +TAGTCAGTGCTGGAATCAGGAAAGTACTATTTTTAGATGGAATAGATAAGGCCCAAGAAG +AACATGAGAAATATCACAGTAATTGGAGAGCAATGGCTAGTGATTTTAACCTGCCACCTG +TAGTAGCAAAAGAAATAGTAGCCAGCTGTGATAAATGTCAGCTAAAAGGAGAAGCCATGC +ATGGACAAGTAGACTGTAGTCCAGGAATATGGCAACTAGATTGTACACATTTAGAAGGAA +AAATTATCCTGGTAGCAGTTCATGTAGCCAGTGGATATATAGAAGCAGAAGTTATTCCAG +CAGAGACAGGGCAGGAAACAGCATACTTTCTCTTAAAATTAGCAGGAAGATGGCCAGTAA +AAACAATACATACAGACAATGGCAGCAATTTCACCAGTACTACGGTTAAGGCCGCCTGTT +GGTGGGCAGGGATCAAGCAGGAATTTGGCATTCCCTACAATCCCCAAAGTCAAGGAGTAG +TAGAATCTATGAATAAAGAATTAAAGAAAATTATAGGACAGGTAAGAGATCAGGCTGAAC +ATCTTAAGACAGCAGTACAAATGGCAGTATTCATCCACAATTTTAAAAGAAAAGGGGGGA +TTGGGGGGTACAGTGCAGGGGAAAGAATAGTAGACATAATAGCAACAGACATACAAACTA +AAGAATTACAAAAACAAATTACAAAAATTCAAAATTTTCGGGTTTATTACAGGGACAGCA +GAGATCCACTTTGGAAAGGACCAGCAAAGCTTCTCTGGAAAGGTGAAGGGGCAGTAGTAA +TACAAGATAATAGTGACATAAAAGTAGTGCCAAGAAGAAAAGCAAAGATCATTAGGGATT +ATGGAAAACAGATGGCAGGTGATGATTGTGTGGCAAGTAGACAGGATGAGGATTAGaaca +tggaaaagtttagtaaaacaccatatgtatgtttcagggaaagctaggggatggttttat +agacatcactatgaaagccctcatccaagaataagttcagaagtacacatcccactaggg +gatgctagattggtaataacaacatattggggtctgcatacaggagaaagagactggcat +ttgggtcagggagtctccatagaatggaggaaaaagagatatagcacacaagtagaccct +gaactagcagaccaactaattcatctgtattactttgactgtttttcagactctgctata +agaaaggccttattaggacacatagttagccctaggtgtgaatatcaagcaggacataac +aaggtaggatctctacaatacttggcactagcagcattaataacaccaaaaaagataaag +ccacctttgcctagtgttacgaaactgacagaggatagatggaacaagccccagaagacc +aagggccacagagggagccacacaatgaatggacactagagcttttagaggagcttaaga +atgaagctgttagacattttcctaggatttggctccatggcttagggcaacatatctatg +aaacttatggggatacttgggcaggagtggaagccataataagaattctgcaacaactgc +tgtttatccattttcagaattgggtgtcgacatagcagaataggcgttactcgacagagg +agagcaagaaatggagccagtagatcctagactagagccctggaagcatccaggaagtca +gcctaaaactgcttgtaccaattgctattgtaaaaagtgttgctttcattgccaagtttg +tttcataacaaaagccttaggcatctcctatggcaggaagaagcggagacagcgacgaag +agctcatcagaacagtcagactcatcaagcttctctatcaaagcagtaagtagtacatgt +aacgcaacctataccaatagtagcaatagtagcattagtagtagcaataataatagcaat +agttgtgtggtccatagtaatcatagaatataggaaaatattaagacaaagaaaaataga +caggttaattgatagactaatagaaagagcagaagacagtggcaatgagagtgaaggaga +aatatcagcacttgtggagatgggggtggagatggggcaccatgctccttgggatgttga +tgatctgtagtgctacagaaaaattgtgggtcacagtctattatggggtacctgtgtgga +aggaagcaaccaccactctattttgtgcatcagatgctaaagcatatgatacagaggtac +ataatgtttgggccacacatgcctgtgtacccacagaccccaacccacaagaagtagtat +tggtaaatgtgacagaaaattttaacatgtggaaaaatgacatggtagaacagatgcatg +aggatataatcagtttatgggatcaaagcctaaagccatgtgtaaaattaaccccactct +gtgttagtttaaagtgcactgatttgaagaatgatactaataccaatagtagtagcggga +gaatgataatggagaaaggagagataaaaaactgctctttcaatatcagcacaagcataa +gaggtaaggtgcagaaagaatatgcatttttttataaacttgatataataccaatagata +atgatactaccagctataagttgacaagttgtaacacctcagtcattacacaggcctgtc +caaaggtatcctttgagccaattcccatacattattgtgccccggctggttttgcgattc +taaaatgtaataataagacgttcaatggaacaggaccatgtacaaatgtcagcacagtac +aatgtacacatggaattaggccagtagtatcaactcaactgctgttaaatggcagtctag +cagaagaagaggtagtaattagatctgtcaatttcacggacaatgctaaaaccataatag +tacagctgaacacatctgtagaaattaattgtacaagacccaacaacaatacaagaaaaa +gaatccgtatccagagaggaccagggagagcatttgttacaataggaaaaataggaaata +tgagacaagcacattgtaacattagtagagcaaaatggaataacactttaaaacagatag +ctagcaaattaagagaacaatttggaaataataaaacaataatctttaagcaatcctcag +gaggggacccagaaattgtaacgcacagttttaattgtggaggggaatttttctactgta +attcaacacaactgtttaatagtacttggtttaatagtacttggagtactgaagggtcaa +ataacactgaaggaagtgacacaatcaccctcccatgcagaataaaacaaattataaaca +tgtggcagaaagtaggaaaagcaatgtatgcccctcccatcagtggacaaattagatgtt +catcaaatattacagggctgctattaacaagagatggtggtaatagcaacaatgagtccg +agatcttcagacctggaggaggagatatgagggacaattggagaagtgaattatataaat +ataaagtagtaaaaattgaaccattaggagtagcacccaccaaggcaaagagaagagtgg +tgcagagagaaaaaagagcagtgggaataggagctttgttccttgggttcttgggagcag +caggaagcactatgggcgcagcctcaatgacgctgacggtacaggccagacaattattgt +ctggtatagtgcagcagcagaacaatttgctgagggctattgaggcgcaacagcatctgt +tgcaactcacagtctggggcatcaagcagctccaggcaagaatcctggctgtggaaagat +acctaaaggatcaacagctcctggggatttggggttgctctggaaaactcatttgcacca +ctgctgtgccttggaatgctagttggagtaataaatctctggaacagatttggaatcaca +cgacctggatggagtgggacagagaaattaacaattacacaagcttaatacactccttaa +ttgaagaatcgcaaaaccagcaagaaaagaatgaacaagaattattggaattagataaat +gggcaagtttgtggaattggtttaacataacaaattggctgtggtatataaaattattca +taatgatagtaggaggcttggtaggtttaagaatagtttttgctgtactttctatagtga +atagagttaggcagggatattcaccattatcgtttcagacccacctcccaaccccgaggg +gacccgacaggcccgaaggaatCgaagaagaaggtggagagagagacagagacagatcca +ttcgattagtgaacggatccttggcacttatctgggacgatctgcggagcctgtgcctct +tcagctaccaccgcttgagagacttactcttgattgtaacgaggattgtggaacttctgg +gacgcagggggtgggaagccctcaaatattggtggaatctcctacagtattggagtcagg +aactaaagaatagtgctgttagcttgctcaatgccacagccatagcagtagctgagggga +cagatagggttatagaagtagtacaaggagcttgtagagctattcgccacatacctagaa +gaataagacagggcttggaaaggattttgctataagatgggtggcaagtggtcaaaaagt +agtgtgattggatggcctactgtaagggaaagaatgagacgagctgagccagcagcagat +agggtgggagcagcatctcgagacctggaaaaacatggagcaatcacaagtagcaataca +gcagctaccaatgctgcttgtgcctggctagaagcacaagaggaggaggaggtgggtttt +ccagtcacacctcaggtacctttaagaccaatgacttacaaggcagctgtagatcttagc +cactttttaaaagaaaaggggggactggaagggctaattcactcccaaagaagacaagat +atccttgatctgtggatctaccacacacaaggctacttccctgattGgcagaactacaca +ccagggccaggggtcagatatccactgacctttggatggtgctacaagctagtaccagtt +gagccagataagatagaagaggccaataaaggagagaacaccagcttgttacaccctgtg +agcctgcatgggatggatgacccggagagagaagtgttagagtggaggtttgacagccgc +ctagcatttcatcacgtggcccgagagctgcatccggagtacttcaagaactgctgacat +cgagcttgctacaagggactttccgctggggactttccagggaggcgtggcctgggcggg +actggggagtggcgagccctcagatcctgcatataagcagctgctttttgcctgtactgg +gtctctctggttagaccagatctgagcctgggagctctctggctaactagggaacccact +gcttaagcctcaataaagcttgccttgagtgcttcaagtagtgtgtgcccgtctgttgtg +tgactctggtaactagagatccctcagacccttttagtcagtgtggaaaatctctagca diff --git a/tests/component/data/samplesmall.fas b/tests/component/data/samplesmall.fas new file mode 100644 index 0000000..2180ec1 --- /dev/null +++ b/tests/component/data/samplesmall.fas @@ -0,0 +1,81 @@ +>DQ995848 +SAGGATTGGGGACCCTGCGCTGAACATGGAGAACATCACATCAGGATTCCTAGGACCCCTGCTCGTGTTA +CAGGCGGGGTTTTTCTTGTTGACAAGAATCCTCACAATACCGCAGAGTCTAGACTCGTGGTGGACTTCTC +TCAATTTTCTAGGGGGAACCACCGTGTGTCTTGGCCAAAATTCGCAGTCCCCAACCTCCAATCACTCACC +AACCTCCTGTCCTCCAACTTGTCCTGGTTATCGCTGGATGTGTCTGCGGCGTTTTATCATCTTCCTCTTC +ATCCTGCTGCTATGCCTCATCTTCTTGTTGGTTCTTCTGGACTATCAGGGTATGTTGCCCGTCTGTCCTC +TGATTCCAGGATCTTCAACCACCAGCGCGGGACCATGCAGAACCTGCACGACTACTGCTCAAGGAACCTC +TATGTATCCCTCCTGTTGCTGTACCAAACCTTCGGACGGAAATTGCACCTGTATTCCCATCCCATCATCC +TGGGCTTTCGGAAAATTCCTATGGGAGTGGGCCTCAGCCCGTTTCTCATGGCTCAGTTTTCTAGTGCCAT +TTGTTCAGTGGTTCGTAGGGCTTTCCCCCACTGTTTGGCTTTCAGTTATGTGGATGATGTGGTATTGGGG +GCCAAGTCTGTACAGCACCTTGAGTCCCTTTTTACCGCTGTTACCAATTTTCTTTTGTCTTTGGGTATAC +ATTTAAACCCTAACAAAACTAAAAGATGGGGTTACTCTTTAAATTTCATGGGCTATGTCATTGGATGTTA +TGGGTCATTGCCACAAGATCACATCATACAGAAAATCAAAGAATGTTTTAGRAAACTTCCTGTTAACT + +>FJ518811 +GAAGACTGGGGACCCTGTACCGAACATGGAGAACATCGCATCAGGACTCCTAAGACCCCTGCTCGTGTTAC +AGGCGGGGTTTTTCTTGTTGACAAAAATCCTCACAATACCACAGAGTCTAGACTCGTGGTGGACTTCTCTC +AATTTTCTAGGGGGAACACCCGTGTGTCTTGGCCAAAATTCGCAGTCCCAAATCTCCAGTCACTCACCAAC +CTGCTGTCCTCCAATTTGTCCTGGTTATCGCTGGATGTGTCTGCGGCGTTTTATCATCTTCCTCTGCATCC +TGCTGCTATGCCTCATCTTCTTGTTGGTTCTTCTGGACTATCAAGGTATGTTGCCCGTTTGTCCTCTAATT +CCAGGATCATCAACAACCAGCACCGGACCATGCAAGGCCTGCACGACTCCTGCTCAAGGAACCTCTATGTT +TCCCTCATGTTGCTGTACAAAACCTACGGACGGAAACTGCACCTGTATTCCCATCCCATCATCTTCGGCTT +TCGCAAAATACCTATGGGAGTGGGCCTCAGTCCGTTTCTCTTGACTCAGTTTACTAGTGCCATTTGTTCAG +TGGTTCGTAGGGCTTTCCCCCACTGTCTGGCTTTCAGTTATATGGATGATGTGGTATTGGGGGCCAAGTCT +GTACAACATCTTGAGTCCCTTTATGCCGCTGTTACCAATTTTCTTTTGTCTTTGGGTATACATTTAACCCC +TCACAAAACAAAAAGATGGGGATATTCCCTTAACTTTATGGGATATGTAATTGGGAGTTGGGGCACATTGC +CACAGGAACATATTGTACAAAAAATCAAAATGTGTTTTAGGAAACTTCCTGTAAACAGGCCTATTGATTGG +AAAGTATGTCAACGAATTGTGGGTCTTTTGGGGTTTGCCGCCCCTTTCACACAATGTGGATATCCTGCTTT +AATGCCTTTATATGCATGTATACAAGCAAAACAGGCTTTTACTTTCTCGCCAACTTACAAGGCCTTTCTCA +GTAAACAGTATCTGAACCTTTACCCCGTTGCTCGGCAA + +>AY373430 +GAGGATTGGGGACCCTGCGCTGAACATGGAGAACATCACATCAGGATTCCTAGGACCCCTTCTAGTGTTAC +AGGCGGGGTTTTTCTTGTTGACAAGAATCCTCACAATACCGCAGAGTCTAGACTCGTGGTGGACTTCTCTC +AATTTTCTAGGGGGAACTACCGTGTGTCTTGGCCAAAATTCGCAGTCCCCAACCTCCAATCACTCACCAAC +CTCCTGTCCTCCAACTTGTCCTGGTTATCGCTGGATGTGTCTGCGGCGTTTTATCATCTTCCTCTTCATCC +TGCTGCTATGCCTCATCTTCTTGTTGGTTCTTCTGGACTATCAAGGTATGTTGCCCGTTTGTCCTCTAATT +CCAGGATCCTCAACCACCAGCACGGGACCATGCAGAACCTGCACGACTCCTGCTCAAGGAACCTCTATGTA +TCCCTCCTGTTGCTGTACCAAACCTTCGGACGGAAATTGCACCTGTATTCCCATCCCATCATCCTGGGCTT +TCGGAAAATTCCTATGGGAGTGGGCCTCAGCCCGTTTCTCCTGGCTCAGTTTACTAGTGCCATTTGTTCAG +TGGTTCGTAGGGCTTTCCCCCACTGTTTGGCTTTCAGTTATATGGATGATGTGGAATTGGGGGCCAAGTCT +GTACAGCAACTTGAGTCCCTTTTTACCGCTGTTACCAATTTTCTTTTGTCTTTGGGTATACATTTAAACCC +TAACAAAACAAAAAGATGGGGTTACTCTCTACATTTCATGGGCTATGTCATTGGATGTTATGGGTCCTTGC +CACAAGAACACATCATACAAAAAATCAAAGAATGTTTTAGAAAACTTCCTATTAACAGGCCTATTGATTGG +AAAGTATGTCAAAGAATTGTGGGTCTTTTGGGTTTTGCTGCCCCTTTTACACAATGTGGTTATCCTGCTTT +AATGCCCTTGTATGCATGTATTCAATCTAAGCAGGCTTTCACTTTCTCGCCAACTTACAAGGCCTTTCTGT +GTAAACAATACCTGAACCTTTACCCCGTTGCCCGGCAA + +>GU456684 +GAGGATTGGGGACCCTGCGCTGAACATGGAGAACATCACATCAGGACTCCTAGGACCCCTGCTCGTATTAC +AGGCGGGGTTTTTCTTGTTGACAAGAATCCTCACAATACCGCAGAGTCTAGACTCGTGGTGGACTTCTCTC +AATTTTCTAGGGGGAACTACCGTGTGTCTTGGCCAAAATTCGCAGTCCCCAACCTCCAATCACTCACCAAC +CTCCTGTCCTCCAACTTGTCCTGGTTATCGCTGGATGTGTCTGCGGCGTTTTATCATCTTCCTCTTCATCC +TGCTGCTATGCCTCATCTTCTTGTTGGTTCTTCTGGACTATCAAGGTATGTTGCCCGTTTGTCCTCTAATT +CCAGGATCGTCAACCACAAGCACGGGACCATGCAGAACCTGCACGACTCCTGCTCAAGGAACCTCTATGTA +TCCCTCCTGTTGCTGTACCAAACCTTCGGACGGAAATTGCACCTGTATTCCCATCCCATCATCCTGGGCTT +TCGGAAAATTCCTATGGGAGTGGGCCTCAGCCCGTTTCTCCTGGCTCAGTTTACTAGTGCCATTTGTTCAG +TGGTTCGTAGGGCTTTCCCCCACTGTTTGGCTTTCAGTTATATGGATGATGTGGTATTGGGGGCCAACTCT +GTACAACATCTTGAAGCCCTTTTTACCGCTGTTACCAATTTTCTTTTGTCTCTGGGCATACATTTAAACCC +TAACAAAACGAAAAGATGGGGTTACTCTTTACATTTTATGGGCTATGTCATTGGATGTCATGGGTCATTGC +CACAAGATCACATCATACAGAAAATCAAAGAATGTTTTAGAAAACTTCCTGTTAACAGGCCTATAGATTGG +AAAGTCTGTCAACGTATTGTGGGTCTTTTGGGTTTTGCTGCCCCTTTTACACAATGTGGGTATCCTGCTTT +AAAGCCCTTATATGCATGTATTCAATCTAAGCAGGCTTTCACTTTCTCGCCAACTTACAAGACCTTTCTGT +GTAAACAATACCTGAACCTTTACCCCGTTGCCCGGCAA + +>FJ622881 +GAGGACTGGGGACCCTGCACCGAACATGGAGAACACAACATCAGGATTCCTAGGACCCCTGCTCGTGTTA +CAGGCGGGGTTTTTCTTGTTGACAAGAATCCTCACAATACCACAGAGTCTAGACTCGTGGTGGACTTCTC +TCAATTTTCTAGGGGGAGCACCCACGTGTCCTGGCCAAAATTCGCAGTCCCCAACCTCCAATCACTCACC +AACCTCTTGTCCTCCAATTTGTCCTGGCTATCGCTGGATGTGTCTGCGGCGTTTTATCATATTCCTCTTC +ATCCTGCTGCTATGCCTCATCTTCTTGTTGGTTCTTCTGGACTACCAAGGTATGTTGCCCGTTTGTCCTC +TACTTCCAGGAACATCAACTACCAGCACGGGACCATGCAAGACCTGCACAATTCCTGCTCAAGGAACCTC +TATGTTTCCCTCTTGTTGCTGTACAAAACCTTCGGACGGAAACTGCACTTGTATTCCCATCCCATCATCC +TGGGCTTTCGCAAGATTCCTATGGGAGTGGGCCTCAGTCCGTTTCTCCTGGCTCAGTTTACTAGTGCCAT +TTGTTCAGTGGTTCGCAGGGCTTTCCCCCACTGTTTGGCTTTCAGTTATATGGATGATGTGGTATTGGGG +GCCAAGTCTGTACAACATCTTGAGTCCCTTTTTACCTCTATTACCAATTTTCTTTTGTCTTTGGGTATAC +ATTTGAACCCTAGTAAAACCAAACGTTGGGGCTACTCCCTTAACTTCATGGGATATGTAATTGGAAGTTG +GGGTACTTTACCGCAGGAACATATTGTACTAAAAATCAAGCAATGTTTTCGAAAACTGCCTGTAAATAGA +CCTATTGATTGGAAAGTATGTCAAAGAATTGTGGGTCTTTTAGGCTTTGCTGCCCCTTTTACCCAATGTG +GCTATCCTGCCTTAATGCCTTTATATGCATGTATACACTCTAAGCAGGCTTTCACTTTCTCGCCAACTTA +CAAGGCCTTTCTGTGTAAACAATATATGAACCTTTACCCCGTTGCCCGGCAA diff --git a/tests/component/features/codon_alignment.feature b/tests/component/features/codon_alignment.feature new file mode 100644 index 0000000..5228d18 --- /dev/null +++ b/tests/component/features/codon_alignment.feature @@ -0,0 +1,6 @@ +Feature: Codon alignment on real sequences + Scenario: Align sample sequences to reference + Given sample sequences "tests/component/data/samplesmall.fas" and reference "tests/component/data/hiv1_reference.fas" + And minimap2 is available + When they are codon-aligned with options "-k 6 -w 3 --score-N 0 --secondary no" from 790 to 2085 + Then the number of aligned pairs is 5 diff --git a/tests/component/steps/codon_steps.py b/tests/component/steps/codon_steps.py new file mode 100644 index 0000000..7170bab --- /dev/null +++ b/tests/component/steps/codon_steps.py @@ -0,0 +1,124 @@ +"""Behave steps for codon alignment component tests.""" + +from __future__ import annotations + +import os +import ssl +import tarfile +import tempfile +import urllib.request +from pathlib import Path +from typing import Any, Callable + +import typer +from behave import ( # type: ignore[import-not-found,import-untyped] + given, + then, + when, +) + +if not hasattr(typer.Typer, "result_callback"): + def result_callback( + self: Any, *args: Any, **kwargs: Any + ) -> Callable[[Callable[..., Any]], Callable[..., Any]]: + def decorator(func: Callable[..., Any]) -> Callable[..., Any]: + return func + return decorator + typer.Typer.result_callback = result_callback # type: ignore[attr-defined] + +from postalign.cli import AlignmentFormat, process_pipeline +from postalign.processors.codon_alignment import codon_alignment +from postalign.processors.save_fasta import save_fasta + + +MINIMAP2_URL = ( + "https://github.com/lh3/minimap2/releases/download/" + "v2.17/minimap2-2.17_x64-linux.tar.bz2" +) + + +@given('sample sequences "{seqs}" and reference "{ref}"') +def given_sequences(context: Any, seqs: str, ref: str) -> None: + """Store input sequence and reference paths. + + :param context: Behave scenario context. + :param seqs: Path to sample sequences file. + :param ref: Path to reference sequence file. + """ + context.seqs_path = Path(seqs) + context.ref_path = Path(ref) + + +@given('minimap2 is available') +def given_minimap2(context: Any) -> None: + """Download and prepare the minimap2 binary. + + :param context: Behave scenario context. + """ + tmpdir = Path(tempfile.mkdtemp()) + archive = tmpdir / 'minimap2.tar.bz2' + ssl_ctx = ssl.create_default_context() + ssl_ctx.check_hostname = False + ssl_ctx.verify_mode = ssl.CERT_NONE + with urllib.request.urlopen(MINIMAP2_URL, context=ssl_ctx) as resp: + with open(archive, 'wb') as out: + out.write(resp.read()) + with tarfile.open(archive, 'r:bz2') as tar: + tar.extractall(tmpdir) + binary = next(tmpdir.glob('*/minimap2')) + binary.chmod(0o755) + context.minimap2 = str(binary) + context.tmpdir = tmpdir + + +@when('they are codon-aligned with options "{opts}" from {start:d} to {end:d}') +def when_codon_aligned(context: Any, opts: str, start: int, end: int) -> None: + """Run the codon-alignment pipeline via the CLI. + + :param context: Behave scenario context. + :param opts: Options to pass to minimap2. + :param start: Reference start position. + :param end: Reference end position. + """ + fd, path = tempfile.mkstemp(suffix='.fas') + os.close(fd) + output = Path(path) + context.output_path = output + processors = [ + codon_alignment(ref_start=start, ref_end=end), + save_fasta(pairwise=True, modifiers=False), + ] + old_path = os.environ['PATH'] + os.environ['PATH'] = f"{Path(context.minimap2).parent}:{old_path}" + try: + with ( + open(context.seqs_path) as seqs, + open(context.ref_path) as ref, + open(output, 'w') as out, + ): + process_pipeline( + processors, + seqs, + None, + out, + AlignmentFormat.MINIMAP2, + ref, + True, + True, + False, + opts, + ) + finally: + os.environ['PATH'] = old_path + + +@then('the number of aligned pairs is {count:d}') +def then_check_count(context: Any, count: int) -> None: + """Verify the expected number of sequence pairs were produced. + + :param context: Behave scenario context. + :param count: Expected number of alignment pairs. + """ + with open(context.output_path) as handle: + seqs = sum(1 for line in handle if line.startswith('>')) // 2 + assert seqs == count, f"Expected {count} pairs, got {seqs}" diff --git a/tests/conftest.py b/tests/unit/conftest.py similarity index 99% rename from tests/conftest.py rename to tests/unit/conftest.py index c3eb58c..e90107c 100644 --- a/tests/conftest.py +++ b/tests/unit/conftest.py @@ -13,7 +13,7 @@ import pytest -PROJECT_ROOT = Path(__file__).resolve().parents[1] +PROJECT_ROOT = Path(__file__).resolve().parents[2] if str(PROJECT_ROOT) not in sys.path: sys.path.insert(0, str(PROJECT_ROOT)) diff --git a/tests/test_aa_position.py b/tests/unit/test_aa_position.py similarity index 100% rename from tests/test_aa_position.py rename to tests/unit/test_aa_position.py diff --git a/tests/test_blosum62.py b/tests/unit/test_blosum62.py similarity index 100% rename from tests/test_blosum62.py rename to tests/unit/test_blosum62.py diff --git a/tests/test_cigar.py b/tests/unit/test_cigar.py similarity index 100% rename from tests/test_cigar.py rename to tests/unit/test_cigar.py diff --git a/tests/test_cli.py b/tests/unit/test_cli.py similarity index 100% rename from tests/test_cli.py rename to tests/unit/test_cli.py diff --git a/tests/test_codon_alignment.py b/tests/unit/test_codon_alignment.py similarity index 95% rename from tests/test_codon_alignment.py rename to tests/unit/test_codon_alignment.py index 6d662b2..46eee72 100644 --- a/tests/test_codon_alignment.py +++ b/tests/unit/test_codon_alignment.py @@ -2,7 +2,7 @@ from __future__ import annotations -from unittest.mock import MagicMock, patch +from unittest.mock import patch import pytest import typer @@ -19,7 +19,6 @@ find_first_gap, move_gaps_to_center, parse_gap_placement_score, - gap_placement_score_callback, calc_match_score, separate_gaps_from_nas, paired_find_best_matches, @@ -53,16 +52,6 @@ def test_parse_gap_placement_score_skips_empty() -> None: assert scores[SEQGAP][(205, 0)] == 3 -def test_gap_placement_score_callback_missing_name() -> None: - """Missing parameter name should raise :class:`BadParameter`.""" - - ctx = MagicMock() - param = MagicMock() - param.name = None - with pytest.raises(typer.BadParameter): - gap_placement_score_callback(ctx, param, ()) - - def test_codon_alignment_invalid_ref_start() -> None: """Invalid reference start should raise :class:`BadParameter`.""" @@ -426,13 +415,3 @@ def test_codon_alignment_processor_dispatches() -> None: ca.assert_called_once() assert result[0] == (empty_ref, empty_seq) assert result[1] == (full_ref, full_seq) - - -def test_gap_placement_score_callback_invalid_value() -> None: - """Invalid callback values should raise :class:`BadParameter`.""" - - ctx = MagicMock() - param = MagicMock() - param.name = "gps" - with pytest.raises(typer.BadParameter): - gap_placement_score_callback(ctx, param, ("204foo",)) diff --git a/tests/test_codonutils.py b/tests/unit/test_codonutils.py similarity index 100% rename from tests/test_codonutils.py rename to tests/unit/test_codonutils.py diff --git a/tests/test_entry.py b/tests/unit/test_entry.py similarity index 100% rename from tests/test_entry.py rename to tests/unit/test_entry.py diff --git a/tests/test_group_by_codons.py b/tests/unit/test_group_by_codons.py similarity index 100% rename from tests/test_group_by_codons.py rename to tests/unit/test_group_by_codons.py diff --git a/tests/test_iupac.py b/tests/unit/test_iupac.py similarity index 100% rename from tests/test_iupac.py rename to tests/unit/test_iupac.py diff --git a/tests/test_message.py b/tests/unit/test_message.py similarity index 100% rename from tests/test_message.py rename to tests/unit/test_message.py diff --git a/tests/test_modifier.py b/tests/unit/test_modifier.py similarity index 100% rename from tests/test_modifier.py rename to tests/unit/test_modifier.py diff --git a/tests/test_na_position.py b/tests/unit/test_na_position.py similarity index 100% rename from tests/test_na_position.py rename to tests/unit/test_na_position.py diff --git a/tests/test_paf.py b/tests/unit/test_paf.py similarity index 100% rename from tests/test_paf.py rename to tests/unit/test_paf.py diff --git a/tests/test_parsers.py b/tests/unit/test_parsers.py similarity index 100% rename from tests/test_parsers.py rename to tests/unit/test_parsers.py diff --git a/tests/test_processor.py b/tests/unit/test_processor.py similarity index 100% rename from tests/test_processor.py rename to tests/unit/test_processor.py diff --git a/tests/test_sanitize_sequence.py b/tests/unit/test_sanitize_sequence.py similarity index 100% rename from tests/test_sanitize_sequence.py rename to tests/unit/test_sanitize_sequence.py diff --git a/tests/test_save_fasta.py b/tests/unit/test_save_fasta.py similarity index 100% rename from tests/test_save_fasta.py rename to tests/unit/test_save_fasta.py diff --git a/tests/test_save_json.py b/tests/unit/test_save_json.py similarity index 100% rename from tests/test_save_json.py rename to tests/unit/test_save_json.py diff --git a/tests/test_sequence.py b/tests/unit/test_sequence.py similarity index 100% rename from tests/test_sequence.py rename to tests/unit/test_sequence.py diff --git a/tests/test_trim_by_ref.py b/tests/unit/test_trim_by_ref.py similarity index 100% rename from tests/test_trim_by_ref.py rename to tests/unit/test_trim_by_ref.py diff --git a/tests/test_version.py b/tests/unit/test_version.py similarity index 100% rename from tests/test_version.py rename to tests/unit/test_version.py