Skip to content

Commit a0f685a

Browse files
committed
Improve main.py test coverage from 42% to 100%
- Add test_main_default_behavior for basic main() execution - Add test_main_verbose_flag to verify INFO log level - Add test_main_silent_flag to verify WARNING log level - Add test_main_offline_flag to verify fetch is skipped - Add test_main_threads_flag to verify threads override - Add test_main_no_fetch_flag to verify --no-fetch behavior - Add test_main_fetch_only_flag to verify write is skipped All CLI argument paths are now tested, bringing main.py to 100% coverage
1 parent 3f6c7ba commit a0f685a

File tree

2 files changed

+255
-0
lines changed

2 files changed

+255
-0
lines changed

CHANGES.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
[jensens]
99
- Fix: Three tests that were accidentally marked as skipped during PR #66 merge are now fixed and passing: `test_resolve_dependencies_simple_file` (fixed assertion to check line contents), `test_write_output_with_ignores` (fixed to use read() for proper ignore processing), and `test_write_relative_constraints_path_different_dirs` (fixed to include constraints content).
1010
[jensens]
11+
- Chore: Improved test coverage for main.py from 42% to 100%. Added comprehensive tests for the main() function covering all CLI argument combinations (--verbose, --silent, --offline, --threads, --no-fetch, --fetch-only), ensuring robust testing of the entry point and all code paths.
12+
[jensens]
1113
- Fix: Add 'synchronize' event to pull_request workflow triggers. This ensures CI runs when PRs are updated with new commits (e.g., after rebasing or pushing new changes), not just when opened or reopened.
1214
[jensens]
1315
- Chore: Optimize GitHub Actions to prevent duplicate workflow runs on pull requests. Restrict `push` trigger to only run on `main` branch, so PRs only trigger via `pull_request` event. This reduces CI resource usage by 50% for PR workflows.

tests/test_main.py

Lines changed: 253 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,3 +151,256 @@ def test_supports_unicode_with_latin1():
151151

152152
with patch("sys.stdout", mock_stdout):
153153
assert supports_unicode() is False
154+
155+
156+
def test_main_default_behavior(tmp_path, monkeypatch):
157+
"""Test main() function with default arguments."""
158+
import logging
159+
160+
# Create a minimal mx.ini file
161+
config_file = tmp_path / "mx.ini"
162+
config_file.write_text(
163+
"""[settings]
164+
requirements-in = requirements.txt
165+
requirements-out = requirements-out.txt
166+
constraints-out = constraints-out.txt
167+
"""
168+
)
169+
170+
# Create empty requirements file
171+
req_file = tmp_path / "requirements.txt"
172+
req_file.write_text("")
173+
174+
# Change to temp directory
175+
monkeypatch.chdir(tmp_path)
176+
177+
# Mock command line arguments
178+
test_args = ["-c", str(config_file)]
179+
180+
with (
181+
patch("sys.argv", ["mxdev"] + test_args),
182+
patch("mxdev.main.load_hooks", return_value=[]),
183+
patch("mxdev.main.read") as mock_read,
184+
patch("mxdev.main.read_hooks") as mock_read_hooks,
185+
patch("mxdev.main.fetch") as mock_fetch,
186+
patch("mxdev.main.write") as mock_write,
187+
patch("mxdev.main.write_hooks") as mock_write_hooks,
188+
patch("mxdev.main.setup_logger") as mock_setup_logger,
189+
):
190+
from mxdev.main import main
191+
192+
main()
193+
194+
# Verify logger was set up with INFO level (default)
195+
mock_setup_logger.assert_called_once_with(logging.INFO)
196+
197+
# Verify all processing functions were called
198+
assert mock_read.called
199+
assert mock_read_hooks.called
200+
assert mock_fetch.called
201+
assert mock_write.called
202+
assert mock_write_hooks.called
203+
204+
205+
def test_main_verbose_flag(tmp_path, monkeypatch):
206+
"""Test main() with --verbose flag sets INFO log level."""
207+
import logging
208+
209+
config_file = tmp_path / "mx.ini"
210+
config_file.write_text(
211+
"""[settings]
212+
requirements-in = requirements.txt
213+
requirements-out = requirements-out.txt
214+
"""
215+
)
216+
(tmp_path / "requirements.txt").write_text("")
217+
monkeypatch.chdir(tmp_path)
218+
219+
test_args = ["-c", str(config_file), "--verbose"]
220+
221+
with (
222+
patch("sys.argv", ["mxdev"] + test_args),
223+
patch("mxdev.main.load_hooks", return_value=[]),
224+
patch("mxdev.main.read"),
225+
patch("mxdev.main.read_hooks"),
226+
patch("mxdev.main.fetch"),
227+
patch("mxdev.main.write"),
228+
patch("mxdev.main.write_hooks"),
229+
patch("mxdev.main.setup_logger") as mock_setup_logger,
230+
):
231+
from mxdev.main import main
232+
233+
main()
234+
235+
# Verify logger was set up with INFO level when verbose flag is used
236+
mock_setup_logger.assert_called_once_with(logging.INFO)
237+
238+
239+
def test_main_silent_flag(tmp_path, monkeypatch):
240+
"""Test main() with --silent flag sets WARNING log level."""
241+
import logging
242+
243+
config_file = tmp_path / "mx.ini"
244+
config_file.write_text(
245+
"""[settings]
246+
requirements-in = requirements.txt
247+
requirements-out = requirements-out.txt
248+
"""
249+
)
250+
(tmp_path / "requirements.txt").write_text("")
251+
monkeypatch.chdir(tmp_path)
252+
253+
test_args = ["-c", str(config_file), "--silent"]
254+
255+
with (
256+
patch("sys.argv", ["mxdev"] + test_args),
257+
patch("mxdev.main.load_hooks", return_value=[]),
258+
patch("mxdev.main.read"),
259+
patch("mxdev.main.read_hooks"),
260+
patch("mxdev.main.fetch"),
261+
patch("mxdev.main.write"),
262+
patch("mxdev.main.write_hooks"),
263+
patch("mxdev.main.setup_logger") as mock_setup_logger,
264+
):
265+
from mxdev.main import main
266+
267+
main()
268+
269+
# Verify logger was set up with WARNING level when silent flag is used
270+
mock_setup_logger.assert_called_once_with(logging.WARNING)
271+
272+
273+
def test_main_offline_flag(tmp_path, monkeypatch):
274+
"""Test main() with --offline flag skips fetch."""
275+
276+
config_file = tmp_path / "mx.ini"
277+
config_file.write_text(
278+
"""[settings]
279+
requirements-in = requirements.txt
280+
requirements-out = requirements-out.txt
281+
"""
282+
)
283+
(tmp_path / "requirements.txt").write_text("")
284+
monkeypatch.chdir(tmp_path)
285+
286+
test_args = ["-c", str(config_file), "--offline"]
287+
288+
with (
289+
patch("sys.argv", ["mxdev"] + test_args),
290+
patch("mxdev.main.load_hooks", return_value=[]),
291+
patch("mxdev.main.read"),
292+
patch("mxdev.main.read_hooks"),
293+
patch("mxdev.main.fetch") as mock_fetch,
294+
patch("mxdev.main.write"),
295+
patch("mxdev.main.write_hooks"),
296+
patch("mxdev.main.setup_logger"),
297+
):
298+
from mxdev.main import main
299+
300+
main()
301+
302+
# Verify fetch was NOT called when offline flag is used
303+
assert not mock_fetch.called
304+
305+
306+
def test_main_threads_flag(tmp_path, monkeypatch):
307+
"""Test main() with --threads flag passes value to Configuration."""
308+
config_file = tmp_path / "mx.ini"
309+
config_file.write_text(
310+
"""[settings]
311+
requirements-in = requirements.txt
312+
requirements-out = requirements-out.txt
313+
"""
314+
)
315+
(tmp_path / "requirements.txt").write_text("")
316+
monkeypatch.chdir(tmp_path)
317+
318+
test_args = ["-c", str(config_file), "--threads", "8"]
319+
320+
with (
321+
patch("sys.argv", ["mxdev"] + test_args),
322+
patch("mxdev.main.load_hooks", return_value=[]),
323+
patch("mxdev.main.Configuration") as mock_config,
324+
patch("mxdev.main.read"),
325+
patch("mxdev.main.read_hooks"),
326+
patch("mxdev.main.fetch"),
327+
patch("mxdev.main.write"),
328+
patch("mxdev.main.write_hooks"),
329+
patch("mxdev.main.setup_logger"),
330+
):
331+
from mxdev.main import main
332+
333+
main()
334+
335+
# Verify Configuration was called with threads override
336+
mock_config.assert_called_once()
337+
call_kwargs = mock_config.call_args[1]
338+
assert "override_args" in call_kwargs
339+
assert call_kwargs["override_args"]["threads"] == 8
340+
341+
342+
def test_main_no_fetch_flag(tmp_path, monkeypatch):
343+
"""Test main() with --no-fetch flag skips fetch."""
344+
config_file = tmp_path / "mx.ini"
345+
config_file.write_text(
346+
"""[settings]
347+
requirements-in = requirements.txt
348+
requirements-out = requirements-out.txt
349+
"""
350+
)
351+
(tmp_path / "requirements.txt").write_text("")
352+
monkeypatch.chdir(tmp_path)
353+
354+
test_args = ["-c", str(config_file), "--no-fetch"]
355+
356+
with (
357+
patch("sys.argv", ["mxdev"] + test_args),
358+
patch("mxdev.main.load_hooks", return_value=[]),
359+
patch("mxdev.main.read"),
360+
patch("mxdev.main.read_hooks"),
361+
patch("mxdev.main.fetch") as mock_fetch,
362+
patch("mxdev.main.write"),
363+
patch("mxdev.main.write_hooks"),
364+
patch("mxdev.main.setup_logger"),
365+
):
366+
from mxdev.main import main
367+
368+
main()
369+
370+
# Verify fetch was NOT called when no-fetch flag is used
371+
assert not mock_fetch.called
372+
373+
374+
def test_main_fetch_only_flag(tmp_path, monkeypatch):
375+
"""Test main() with --fetch-only flag skips write and hooks."""
376+
config_file = tmp_path / "mx.ini"
377+
config_file.write_text(
378+
"""[settings]
379+
requirements-in = requirements.txt
380+
requirements-out = requirements-out.txt
381+
"""
382+
)
383+
(tmp_path / "requirements.txt").write_text("")
384+
monkeypatch.chdir(tmp_path)
385+
386+
test_args = ["-c", str(config_file), "--fetch-only"]
387+
388+
with (
389+
patch("sys.argv", ["mxdev"] + test_args),
390+
patch("mxdev.main.load_hooks", return_value=[]),
391+
patch("mxdev.main.read"),
392+
patch("mxdev.main.read_hooks") as mock_read_hooks,
393+
patch("mxdev.main.fetch"),
394+
patch("mxdev.main.write") as mock_write,
395+
patch("mxdev.main.write_hooks") as mock_write_hooks,
396+
patch("mxdev.main.setup_logger"),
397+
):
398+
from mxdev.main import main
399+
400+
main()
401+
402+
# Verify read_hooks was NOT called
403+
assert not mock_read_hooks.called
404+
# Verify write and write_hooks were NOT called
405+
assert not mock_write.called
406+
assert not mock_write_hooks.called

0 commit comments

Comments
 (0)