From 687497bab5bc01010798e453556c14fdee496440 Mon Sep 17 00:00:00 2001 From: Wolfvin Date: Sun, 12 Jul 2026 12:34:45 +0700 Subject: [PATCH] fix(tests): test_doctor.py MagicMock auto-vivifies args.check, breaks 11 tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Issue #195's --check dispatch (doctor.execute() -> _dispatch_subcommands() when getattr(args, 'check', None) is truthy) introduced an implicit requirement: callers must explicitly set args.check = None to exercise the normal full-checks path. mock.MagicMock() auto-vivifies any attribute access — args.check silently returns a fresh MagicMock object (truthy), not None, so getattr's default never kicks in. Every test using _run_doctor() (or constructing its own MagicMock args inline) unknowingly went through _dispatch_subcommands() instead, getting the umbrella {s, st, r} shape instead of doctor's own {status, exit_code, checks, fixes, summary, platform, workspace} shape -- hence 'assert status in result' / 'KeyError: fixes' failures. Found via baseline comparison: these 11 test_doctor.py failures did NOT exist before today's session (commit c722f4a) -- they were introduced as an unintended side effect of issue #195's consolidation work, not caught at the time because doctor.py's own test suite wasn't run against the new dispatch path with an explicit check=None default. Fix: set args.check = None in _run_doctor() and the 2 inline MagicMock constructions in TestFixMode. Before: 12 failed. After: 1 failed (os.geteuid() -- pre-existing, Windows doesn't have geteuid, confirmed present in baseline too), 37 passed. --- tests/test_doctor.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/tests/test_doctor.py b/tests/test_doctor.py index 6a5b46a7..0cccd370 100644 --- a/tests/test_doctor.py +++ b/tests/test_doctor.py @@ -77,6 +77,15 @@ def _run_doctor(workspace=None, fix=False, verbose=False, fmt="json"): args.verbose = verbose args.format = fmt args.workspace = workspace + # Issue #195 added a --check dispatch to doctor.execute(): when + # getattr(args, "check", None) is truthy, execution goes through + # _dispatch_subcommands() instead of the normal full-checks path. + # MagicMock auto-vivifies any attribute access (args.check returns a + # fresh MagicMock, not None), so without this explicit assignment every + # test using this helper silently exercised the wrong code path and got + # the umbrella {s, st, r} shape instead of doctor's own + # {status, exit_code, checks, ...} shape. + args.check = None return doctor_module.execute(args, workspace or "") @@ -346,6 +355,7 @@ def test_fix_calls_pip_when_deps_missing(self, tmp_path): args.verbose = False args.format = "json" args.workspace = str(tmp_path) + args.check = None # see _run_doctor() docstring result = doctor_module.execute(args, str(tmp_path)) assert mock_run.called @@ -372,6 +382,7 @@ def test_fix_noop_when_nothing_fixable(self, tmp_path): args.verbose = False args.format = "json" args.workspace = str(tmp_path) + args.check = None # see _run_doctor() docstring result = doctor_module.execute(args, str(tmp_path)) # pip should NOT have been called.