1- """Smoke tests for CLI entry-points (main() functions via sys.argv monkeypatching) ."""
1+ """Smoke tests for CLI entry-points via Typer CliRunner ."""
22
33import shutil
4- import sys
5- import warnings
64
75import pandas as pd
86import pytest
7+ from typer .testing import CliRunner
98
10- from krakenparser .counts .convert2csv import main as convert2csv_main
11- from krakenparser .counts .processing_script import main as processing_main
12- from krakenparser .counts .split_mpa import main as split_mpa_main
13- from krakenparser .mpa .mpa_table import main as mpa_table_main
14- from krakenparser .mpa .transform2mpa import main as transform2mpa_main
15- from krakenparser .pipeline import main as pipeline_main
16- from krakenparser .stats .diversity import main as diversity_main
17- from krakenparser .stats .relabund import main as relabund_main
9+ from krakenparser .counts .convert2csv import app as convert2csv_app
10+ from krakenparser .counts .processing_script import app as processing_app
11+ from krakenparser .counts .split_mpa import app as split_mpa_app
12+ from krakenparser .mpa .mpa_table import app as mpa_table_app
13+ from krakenparser .mpa .transform2mpa import app as transform2mpa_app
14+ from krakenparser .pipeline import app as pipeline_app
15+ from krakenparser .stats .diversity import app as diversity_app
16+ from krakenparser .stats .relabund import app as relabund_app
1817
1918_MPA_A = "#Classification\t sample1\n d__Bacteria|s__Pseudomonas_aeruginosa\t 300\n "
2019_MPA_B = "#Classification\t sample2\n d__Bacteria|s__Pseudomonas_aeruginosa\t 100\n "
2625)
2726
2827
28+ @pytest .fixture
29+ def runner ():
30+ return CliRunner ()
31+
32+
2933# ---------------------------------------------------------------------------
3034# convert2csv
3135# ---------------------------------------------------------------------------
3236
3337
34- def test_convert2csv_main (counts_txt_file , tmp_path , monkeypatch ):
35- out = tmp_path / "out.csv"
36- monkeypatch .setattr (
37- sys , "argv" , ["c2c" , "-i" , str (counts_txt_file ), "-o" , str (out )]
38+ def test_convert2csv_no_args_shows_help (runner ):
39+ result = runner .invoke (convert2csv_app , [])
40+ assert result .exit_code == 0
41+ assert "Usage" in result .output
42+
43+
44+ def test_convert2csv_missing_one_option (runner , tmp_path ):
45+ result = runner .invoke (convert2csv_app , ["-i" , str (tmp_path / "x.txt" )])
46+ assert result .exit_code == 1
47+ assert "Missing required options" in result .output
48+
49+
50+ def test_convert2csv_file_not_found (runner , tmp_path ):
51+ result = runner .invoke (
52+ convert2csv_app ,
53+ ["-i" , str (tmp_path / "ghost.txt" ), "-o" , str (tmp_path / "out.csv" )],
3854 )
39- convert2csv_main ()
40- assert out . exists ()
55+ assert result . exit_code == 1
56+ assert "Error" in result . output
4157
4258
4359# ---------------------------------------------------------------------------
4460# processing_script
4561# ---------------------------------------------------------------------------
4662
4763
48- def test_processing_main (tmp_path , monkeypatch ):
49- source = tmp_path / "COMBINED.txt"
50- source .write_text ("#Classification\t sample1.kreport\n " )
51- dest = tmp_path / "counts.txt"
52- dest .write_text ("s__Pseudomonas_aeruginosa\t 100\n " )
53- monkeypatch .setattr (sys , "argv" , ["ps" , "-i" , str (source ), "-o" , str (dest )])
54- processing_main ()
64+ def test_processing_no_args_shows_help (runner ):
65+ result = runner .invoke (processing_app , [])
66+ assert result .exit_code == 0
67+ assert "Usage" in result .output
68+
69+
70+ def test_processing_missing_one_option (runner , tmp_path ):
71+ result = runner .invoke (processing_app , ["-i" , str (tmp_path / "x.txt" )])
72+ assert result .exit_code == 1
73+ assert "Missing required options" in result .output
74+
75+
76+ def test_processing_file_not_found (runner , tmp_path ):
77+ result = runner .invoke (
78+ processing_app ,
79+ ["-i" , str (tmp_path / "ghost.txt" ), "-o" , str (tmp_path / "dest.txt" )],
80+ )
81+ assert result .exit_code == 1
82+ assert "Error" in result .output
5583
5684
5785# ---------------------------------------------------------------------------
5886# split_mpa
5987# ---------------------------------------------------------------------------
6088
6189
62- def test_split_mpa_main (tmp_path , monkeypatch ):
63- combined = tmp_path / "COMBINED.txt"
64- combined .write_text (_COMBINED_MPA )
65- out = tmp_path / "out"
66- monkeypatch .setattr (sys , "argv" , ["sm" , "-i" , str (combined ), "-o" , str (out )])
67- split_mpa_main ()
68- assert (out / "txt" / "counts_species.txt" ).exists ()
90+ def test_split_mpa_no_args_shows_help (runner ):
91+ result = runner .invoke (split_mpa_app , [])
92+ assert result .exit_code == 0
93+ assert "Usage" in result .output
6994
7095
71- def test_split_mpa_main_viruses_only (tmp_path , monkeypatch ):
72- combined = tmp_path / "COMBINED.txt"
73- combined .write_text (_COMBINED_MPA + "d__Viruses|s__Virus_X\t 5\t 3\n " )
74- out = tmp_path / "out"
75- monkeypatch .setattr (
76- sys , "argv" , ["sm" , "-i" , str (combined ), "-o" , str (out ), "--viruses-only" ]
77- )
78- split_mpa_main ()
96+ def test_split_mpa_missing_one_option (runner , tmp_path ):
97+ result = runner .invoke (split_mpa_app , ["-i" , str (tmp_path / "x.txt" )])
98+ assert result .exit_code == 1
99+ assert "Missing required options" in result .output
79100
80101
81- def test_split_mpa_main_keep_human (tmp_path , monkeypatch ):
82- combined = tmp_path / "COMBINED.txt"
83- combined .write_text (_COMBINED_MPA )
84- out = tmp_path / "out"
85- monkeypatch .setattr (
86- sys , "argv" , ["sm" , "-i" , str (combined ), "-o" , str (out ), "--keep-human" ]
102+ def test_split_mpa_file_not_found (runner , tmp_path ):
103+ result = runner .invoke (
104+ split_mpa_app ,
105+ ["-i" , str (tmp_path / "ghost.txt" ), "-o" , str (tmp_path / "out" )],
87106 )
88- split_mpa_main ()
107+ assert result .exit_code == 1
108+ assert "Error" in result .output
89109
90110
91111# ---------------------------------------------------------------------------
92112# mpa_table
93113# ---------------------------------------------------------------------------
94114
95115
96- def test_mpa_table_main (tmp_path , monkeypatch ):
116+ def test_mpa_table_main (tmp_path , runner ):
97117 a , b = tmp_path / "a.MPA.TXT" , tmp_path / "b.MPA.TXT"
98118 a .write_text (_MPA_A )
99119 b .write_text (_MPA_B )
100120 out = tmp_path / "COMBINED.txt"
101- monkeypatch .setattr (sys , "argv" , ["mt" , "-i" , str (a ), str (b ), "-o" , str (out )])
102- mpa_table_main ()
121+
122+ result = runner .invoke (mpa_table_app , ["-i" , str (a ), "-i" , str (b ), "-o" , str (out )])
123+ assert result .exit_code == 0
103124 assert out .exists ()
104125
105126
@@ -108,22 +129,23 @@ def test_mpa_table_main(tmp_path, monkeypatch):
108129# ---------------------------------------------------------------------------
109130
110131
111- def test_transform2mpa_main_single (kreport_file , tmp_path , monkeypatch ):
132+ def test_transform2mpa_main_single (kreport_file , tmp_path , runner ):
112133 out = tmp_path / "out.MPA.TXT"
113- monkeypatch . setattr ( sys , "argv" , [ "t2m" , "-r" , str (kreport_file ), "-o" , str (out )])
114- transform2mpa_main ()
134+ result = runner . invoke ( transform2mpa_app , [ "-r" , str (kreport_file ), "-o" , str (out )])
135+ assert result . exit_code == 0
115136 assert out .exists ()
116137
117138
118- def test_transform2mpa_main_batch (kreport_file , tmp_path , monkeypatch ):
139+ def test_transform2mpa_main_batch (kreport_file , tmp_path , runner ):
119140 kreports_dir = tmp_path / "kreports"
120141 kreports_dir .mkdir ()
121142 shutil .copy (kreport_file , kreports_dir / kreport_file .name )
122143 out_dir = tmp_path / "mpa_out"
123- monkeypatch .setattr (
124- sys , "argv" , ["t2m" , "-i" , str (kreports_dir ), "-o" , str (out_dir )]
144+
145+ result = runner .invoke (
146+ transform2mpa_app , ["-i" , str (kreports_dir ), "-o" , str (out_dir )]
125147 )
126- transform2mpa_main ()
148+ assert result . exit_code == 0
127149 assert out_dir .is_dir ()
128150
129151
@@ -132,78 +154,91 @@ def test_transform2mpa_main_batch(kreport_file, tmp_path, monkeypatch):
132154# ---------------------------------------------------------------------------
133155
134156
135- def test_diversity_main_with_seed ( counts_csv_file , tmp_path , monkeypatch ):
136- out_dir = tmp_path / "div"
137- monkeypatch . setattr (
138- sys ,
139- "argv" ,
140- [
141- "div" ,
142- "-i" ,
143- str ( counts_csv_file ),
144- "-o" ,
145- str ( out_dir ),
146- "-d" ,
147- "1000" ,
148- "-s" ,
149- "42" ,
150- ],
157+ def test_diversity_no_args_shows_help ( runner ):
158+ result = runner . invoke ( diversity_app , [])
159+ assert result . exit_code == 0
160+ assert "Usage" in result . output
161+
162+
163+ def test_diversity_missing_one_option ( runner , tmp_path ):
164+ result = runner . invoke ( diversity_app , [ "-o" , str ( tmp_path / "out" )])
165+ assert result . exit_code == 1
166+ assert "Missing required options" in result . output
167+
168+
169+ def test_diversity_file_not_found ( runner , tmp_path ):
170+ result = runner . invoke (
171+ diversity_app ,
172+ [ "-i" , str ( tmp_path / "ghost.csv" ), "-o" , str ( tmp_path / "out" ) ],
151173 )
152- diversity_main ()
153- assert ( out_dir / "alpha_div.csv" ). exists ()
174+ assert result . exit_code == 1
175+ assert "Error" in result . output
154176
155177
156- def test_diversity_main_no_seed (counts_csv_file , tmp_path , monkeypatch ):
178+ def test_diversity_not_enough_samples_for_beta (runner , tmp_path ):
179+ csv_in = tmp_path / "single.csv"
180+ pd .DataFrame ({"Taxon_A" : [100 ], "Taxon_B" : [200 ]}, index = ["S1" ]).to_csv (csv_in )
157181 out_dir = tmp_path / "div"
158- monkeypatch .setattr (
159- sys ,
160- "argv" ,
161- ["div" , "-i" , str (counts_csv_file ), "-o" , str (out_dir ), "-d" , "1000" ],
182+ result = runner .invoke (
183+ diversity_app ,
184+ ["-i" , str (csv_in ), "-o" , str (out_dir ), "-d" , "50" ],
162185 )
163- diversity_main ()
186+ assert result .exit_code == 1
187+ assert "Error" in result .output
164188
165189
166190# ---------------------------------------------------------------------------
167191# relabund
168192# ---------------------------------------------------------------------------
169193
170194
171- def test_relabund_main (counts_csv_file , tmp_path , monkeypatch ):
172- out = tmp_path / "ra.csv"
173- monkeypatch .setattr (sys , "argv" , ["ra" , "-i" , str (counts_csv_file ), "-o" , str (out )])
174- relabund_main ()
175- assert out .exists ()
195+ def test_relabund_no_args_shows_help (runner ):
196+ result = runner .invoke (relabund_app , [])
197+ assert result .exit_code == 0
198+ assert "Usage" in result .output
176199
177200
178- def test_relabund_main_with_other_threshold (counts_csv_file , tmp_path , monkeypatch ):
179- out = tmp_path / "ra.csv"
180- monkeypatch .setattr (
181- sys , "argv" , ["ra" , "-i" , str (counts_csv_file ), "-o" , str (out ), "-O" , "50" ]
182- )
183- relabund_main ()
201+ def test_relabund_missing_one_option (runner , tmp_path ):
202+ result = runner .invoke (relabund_app , ["-i" , str (tmp_path / "x.csv" )])
203+ assert result .exit_code == 1
204+ assert "Missing required options" in result .output
184205
185206
186- def test_relabund_warns_zero_abundance_sample (tmp_path ):
187- df = pd .DataFrame (
188- {"Sample_id" : ["S1" , "S2" ], "Taxon_A" : [0 , 100 ], "Taxon_B" : [0 , 200 ]}
207+ def test_relabund_file_not_found (runner , tmp_path ):
208+ result = runner .invoke (
209+ relabund_app ,
210+ ["-i" , str (tmp_path / "ghost.csv" ), "-o" , str (tmp_path / "out.csv" )],
189211 )
190- csv_in = tmp_path / "counts.csv"
191- df .to_csv (csv_in , index = False )
192- out = tmp_path / "ra.csv"
193- with warnings .catch_warnings (record = True ) as caught :
194- warnings .simplefilter ("always" )
195- from krakenparser .stats .relabund import calculate_rel_abund
196-
197- calculate_rel_abund (str (csv_in ), str (out ))
198- assert any ("zero total abundance" in str (w .message ) for w in caught )
212+ assert result .exit_code == 1
213+ assert "Error" in result .output
199214
200215
201216# ---------------------------------------------------------------------------
202217# pipeline (error paths only — success path covered by test_full_pipeline.py)
203218# ---------------------------------------------------------------------------
204219
205220
206- def test_pipeline_main_missing_input_exits (tmp_path , monkeypatch ):
207- monkeypatch .setattr (sys , "argv" , ["pipeline" , "-i" , str (tmp_path / "ghost" )])
208- with pytest .raises (SystemExit ):
209- pipeline_main ()
221+ def test_pipeline_no_mpa_files (runner , tmp_path ):
222+ empty_dir = tmp_path / "kreports"
223+ empty_dir .mkdir ()
224+ result = runner .invoke (pipeline_app , ["-i" , str (empty_dir )])
225+ assert result .exit_code == 1
226+ assert "Error" in result .output
227+
228+
229+ def test_pipeline_file_exists_error (runner , tmp_path , kreport_file ):
230+ kreports_dir = tmp_path / "kreports"
231+ kreports_dir .mkdir ()
232+ shutil .copy (kreport_file , kreports_dir / kreport_file .name )
233+
234+ runner .invoke (pipeline_app , ["-i" , str (kreports_dir ), "--overwrite" ])
235+
236+ result = runner .invoke (pipeline_app , ["-i" , str (kreports_dir )])
237+ assert result .exit_code == 1
238+ assert "Error" in result .output
239+
240+
241+ def test_pipeline_missing_input_dir (runner , tmp_path ):
242+ result = runner .invoke (pipeline_app , ["-i" , str (tmp_path / "ghost" )])
243+ assert result .exit_code == 1
244+ assert "Error" in result .output
0 commit comments