Skip to content

Commit 6535eba

Browse files
test: add gff3_merge CLI coverage
Cover gff3_merge argument validation, conflict handling, user-defined parsing, and default output/report routing. Assisted-by: GitHub Copilot (GPT-5.4)
1 parent 0c5e59c commit 6535eba

1 file changed

Lines changed: 147 additions & 0 deletions

File tree

tests/unit/test_gff3_merge_cli.py

Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
import io
2+
import unittest
3+
from argparse import Namespace
4+
from unittest import mock
5+
6+
from gff3tool.bin import gff3_merge
7+
8+
9+
class TestGff3MergeCli(unittest.TestCase):
10+
def test_script_main_exits_when_gff_file1_missing(self):
11+
args = Namespace(
12+
gff_file1=None,
13+
gff_file2="ref.gff3",
14+
fasta="ref.fa",
15+
user_defined_file1=None,
16+
user_defined_file2=None,
17+
output_gff="merged.gff",
18+
report_file="report.txt",
19+
all=False,
20+
auto_assignment=True,
21+
)
22+
stdin = mock.Mock()
23+
stdin.isatty.return_value = True
24+
25+
with mock.patch("argparse.ArgumentParser.parse_args", return_value=args), \
26+
mock.patch("sys.stdin", stdin), \
27+
mock.patch("argparse.ArgumentParser.print_help") as print_help, \
28+
self.assertRaises(SystemExit) as exc:
29+
gff3_merge.script_main()
30+
31+
print_help.assert_called_once()
32+
self.assertEqual(exc.exception.code, 1)
33+
34+
def test_script_main_exits_when_gff_file2_missing(self):
35+
args = Namespace(
36+
gff_file1="new.gff3",
37+
gff_file2=None,
38+
fasta="ref.fa",
39+
user_defined_file1=None,
40+
user_defined_file2=None,
41+
output_gff="merged.gff",
42+
report_file="report.txt",
43+
all=False,
44+
auto_assignment=True,
45+
)
46+
stdin = mock.Mock()
47+
stdin.isatty.return_value = True
48+
49+
with mock.patch("argparse.ArgumentParser.parse_args", return_value=args), \
50+
mock.patch("sys.stdin", stdin), \
51+
mock.patch("argparse.ArgumentParser.print_help") as print_help, \
52+
self.assertRaises(SystemExit) as exc:
53+
gff3_merge.script_main()
54+
55+
print_help.assert_called_once()
56+
self.assertEqual(exc.exception.code, 2)
57+
58+
def test_script_main_exits_on_conflicting_all_and_no_auto(self):
59+
args = Namespace(
60+
gff_file1="new.gff3",
61+
gff_file2="ref.gff3",
62+
fasta="ref.fa",
63+
user_defined_file1=None,
64+
user_defined_file2=None,
65+
output_gff="merged.gff",
66+
report_file="report.txt",
67+
all=True,
68+
auto_assignment=False,
69+
)
70+
71+
with mock.patch("argparse.ArgumentParser.parse_args", return_value=args), \
72+
self.assertRaises(SystemExit) as exc:
73+
gff3_merge.script_main()
74+
75+
self.assertEqual(exc.exception.code, 0)
76+
77+
def test_script_main_parses_user_defined_files_and_calls_main(self):
78+
args = Namespace(
79+
gff_file1="new.gff3",
80+
gff_file2="ref.gff3",
81+
fasta="ref.fa",
82+
user_defined_file1="u1.txt",
83+
user_defined_file2="u2.txt",
84+
output_gff="out.gff3",
85+
report_file="report.txt",
86+
all=False,
87+
auto_assignment=True,
88+
)
89+
90+
def open_side_effect(path, mode="r", *args, **kwargs):
91+
if path == "u1.txt":
92+
return io.StringIO("gene mRNA\ngene mRNA\n")
93+
if path == "u2.txt":
94+
return io.StringIO("gene mRNA\n")
95+
if path == "report.txt":
96+
return io.StringIO()
97+
raise AssertionError(path)
98+
99+
with mock.patch("argparse.ArgumentParser.parse_args", return_value=args), \
100+
mock.patch("builtins.open", side_effect=open_side_effect), \
101+
mock.patch.object(gff3_merge, "main", autospec=True) as main_mock:
102+
gff3_merge.script_main()
103+
104+
main_mock.assert_called_once_with(
105+
"new.gff3",
106+
"ref.gff3",
107+
"ref.fa",
108+
mock.ANY,
109+
"out.gff3",
110+
False,
111+
True,
112+
[["gene", "mRNA"]],
113+
[["gene", "mRNA"]],
114+
logger=mock.ANY,
115+
)
116+
117+
def test_script_main_uses_default_report_and_output_names(self):
118+
args = Namespace(
119+
gff_file1="new.gff3",
120+
gff_file2="ref.gff3",
121+
fasta="ref.fa",
122+
user_defined_file1=None,
123+
user_defined_file2=None,
124+
output_gff=None,
125+
report_file=None,
126+
all=False,
127+
auto_assignment=True,
128+
)
129+
130+
def open_side_effect(path, mode="r", *args, **kwargs):
131+
if path == "merge_report.txt":
132+
return io.StringIO()
133+
raise AssertionError(path)
134+
135+
with mock.patch("argparse.ArgumentParser.parse_args", return_value=args), \
136+
mock.patch("builtins.open", side_effect=open_side_effect), \
137+
mock.patch.object(gff3_merge, "main", autospec=True) as main_mock:
138+
gff3_merge.script_main()
139+
140+
called = main_mock.call_args
141+
self.assertEqual(called.args[4], "merged.gff")
142+
self.assertEqual(called.args[5], False)
143+
self.assertEqual(called.args[6], True)
144+
145+
146+
if __name__ == "__main__":
147+
unittest.main()

0 commit comments

Comments
 (0)