|
| 1 | +""" |
| 2 | +Tests for finaletoolkit.cli._dispatch |
| 3 | +
|
| 4 | +Covers the pure, CRAM-independent logic: strand-flag translation, the |
| 5 | +input-file-absent/BAM-without-reference fast paths through |
| 6 | +``_validate_inputs``, and ``run``'s param-filtering/dispatch mechanics. The |
| 7 | +CRAM+reference contig-validation branch (which needs real alignment/reference |
| 8 | +files) is already exercised by the filter-file tests in test_cli.py. |
| 9 | +""" |
| 10 | + |
| 11 | +import pytest |
| 12 | + |
| 13 | +from finaletoolkit.cli._dispatch import _translate_strand, _validate_inputs, run |
| 14 | + |
| 15 | + |
| 16 | +class TestTranslateStrand: |
| 17 | + def test_both(self): |
| 18 | + params = {"strand": "both"} |
| 19 | + _translate_strand(params) |
| 20 | + assert params == {"both_strands": True, "negative_strand": False} |
| 21 | + |
| 22 | + def test_forward(self): |
| 23 | + params = {"strand": "forward"} |
| 24 | + _translate_strand(params) |
| 25 | + assert params == {"both_strands": False, "negative_strand": False} |
| 26 | + |
| 27 | + def test_reverse(self): |
| 28 | + params = {"strand": "reverse"} |
| 29 | + _translate_strand(params) |
| 30 | + assert params == {"both_strands": False, "negative_strand": True} |
| 31 | + |
| 32 | + def test_no_strand_key_is_a_no_op(self): |
| 33 | + params = {"other": 1} |
| 34 | + _translate_strand(params) |
| 35 | + assert params == {"other": 1} |
| 36 | + |
| 37 | + |
| 38 | +class TestValidateInputs: |
| 39 | + def test_no_input_file_is_a_no_op(self): |
| 40 | + _validate_inputs({}) |
| 41 | + _validate_inputs({"input_file": None}) |
| 42 | + _validate_inputs({"input_file": ""}) |
| 43 | + |
| 44 | + def test_cram_without_reference_exits(self): |
| 45 | + with pytest.raises(SystemExit) as exc_info: |
| 46 | + _validate_inputs({"input_file": "sample.cram"}) |
| 47 | + assert exc_info.value.code == 1 |
| 48 | + |
| 49 | + def test_bam_without_reference_is_a_no_op(self): |
| 50 | + # A reference is optional for BAM; only CRAM requires one. |
| 51 | + _validate_inputs({"input_file": "sample.bam"}) |
| 52 | + |
| 53 | + def test_non_alignment_input_is_a_no_op(self): |
| 54 | + # Neither .bam nor .cram: the reference/contig-compatibility branch |
| 55 | + # doesn't apply regardless of whether a reference was given. |
| 56 | + _validate_inputs({"input_file": "sample.frag.gz", "reference_file": "ref.fa"}) |
| 57 | + |
| 58 | + |
| 59 | +def _stub_add(a, b=10): |
| 60 | + return a + b |
| 61 | + |
| 62 | + |
| 63 | +def _stub_kwargs(**kwargs): |
| 64 | + return kwargs |
| 65 | + |
| 66 | + |
| 67 | +class TestRun: |
| 68 | + def test_filters_params_to_function_signature(self): |
| 69 | + result = run( |
| 70 | + __name__, "_stub_add", {"a": 1, "b": 2, "unrelated_cli_only_key": "x"} |
| 71 | + ) |
| 72 | + assert result == 3 |
| 73 | + |
| 74 | + def test_uses_function_defaults_for_missing_params(self): |
| 75 | + result = run(__name__, "_stub_add", {"a": 5}) |
| 76 | + assert result == 15 |
| 77 | + |
| 78 | + def test_varkw_function_receives_all_params_unfiltered(self): |
| 79 | + result = run(__name__, "_stub_kwargs", {"a": 1, "anything": "goes"}) |
| 80 | + assert result == {"a": 1, "anything": "goes"} |
| 81 | + |
| 82 | + def test_strand_translated_before_dispatch(self): |
| 83 | + result = run( |
| 84 | + __name__, "_stub_kwargs", {"strand": "reverse", "a": 1} |
| 85 | + ) |
| 86 | + assert result == {"a": 1, "both_strands": False, "negative_strand": True} |
0 commit comments