From 874f9f97113d8e8e5166527264866300d4c84be8 Mon Sep 17 00:00:00 2001 From: Jeroen Schweitzer Date: Tue, 18 Aug 2026 15:49:20 +0200 Subject: [PATCH] fix(tests): repair three mock-wiring bugs in doc_sync_executor tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit test_executor_successful_sync_entire_repo: _get_git_commit is patched separately and never calls the real _run_command, so it does not consume a slot in mock_run.side_effect. The list reserved one anyway (labelled "git rev-parse HEAD"), which shifted "M README.md\n" one call late — git status --porcelain saw "" (no changes) instead, so execute() took the no-changes branch and the test asserted "Successfully synced" against "already up to date". Removed the phantom slot. Also gave the iterdir() mock items real string .name attributes: MagicMock(name="X") sets the mock's repr, not the .name attribute read by execute()'s `item.name != '.git'` check, so every item was being treated as non-.git regardless of the intended value. test_executor_sync_specific_paths, test_executor_handles_no_changes: mock_upstream_dir/mock_gitea_dir were built but never wired to mock_path.return_value, so `work_dir = Path(...)` and `upstream_dir = work_dir / "upstream"` resolved to a different, unconfigured auto-generated MagicMock. `source.name` on that mock is itself a MagicMock, not a string, so `', '.join(copied_paths)` raised TypeError. Wired mock_path.return_value to a work_dir mock whose __truediv__ yields the intended upstream/gitea mocks, matching the pattern the first test already used correctly. All three are test-side: doc_sync_executor.py is unchanged. Confirmed via git log -p that this file and its test have exactly one commit in this repo's history (the initial extraction), so there is no prior passing version to regress from — these tests appear to have never passed. --- tests/test_doc_sync_executor.py | 63 ++++++++++++++++++++++++++------- 1 file changed, 50 insertions(+), 13 deletions(-) diff --git a/tests/test_doc_sync_executor.py b/tests/test_doc_sync_executor.py index cb8ac70..b9a4064 100644 --- a/tests/test_doc_sync_executor.py +++ b/tests/test_doc_sync_executor.py @@ -60,23 +60,36 @@ class TestDocSyncExecutor: mock_upstream_dir = MagicMock() mock_gitea_dir = MagicMock() - # Setup directory mocking - mock_upstream_dir.iterdir.return_value = [ - MagicMock(name=".git", is_dir=lambda: True), - MagicMock(name="README.md", is_dir=lambda: False), - MagicMock(name="docs", is_dir=lambda: True), - ] - mock_gitea_dir.iterdir.return_value = [ - MagicMock(name=".git", is_dir=lambda: True) - ] + # Setup directory mocking. MagicMock(name=...) sets the mock's + # repr, not its .name attribute (classic gotcha — see + # test_executor_sync_specific_paths) — set .name explicitly so + # execute()'s `item.name != '.git'` check actually excludes it. + git_item = MagicMock(is_dir=lambda: True) + git_item.name = ".git" + readme_item = MagicMock(is_dir=lambda: False) + readme_item.name = "README.md" + docs_item = MagicMock(is_dir=lambda: True) + docs_item.name = "docs" + mock_upstream_dir.iterdir.return_value = [git_item, readme_item, docs_item] + + gitea_git_item = MagicMock(is_dir=lambda: True) + gitea_git_item.name = ".git" + mock_gitea_dir.iterdir.return_value = [gitea_git_item] mock_path.return_value = mock_work_dir mock_work_dir.__truediv__.side_effect = [mock_upstream_dir, mock_gitea_dir] - # Mock git status to show changes + # Mock git status to show changes. _get_git_commit is patched + # separately above and never calls the real _run_command, so it + # does not consume a slot in this side_effect list — the actual + # call order for this (clone-succeeds, entire-repo) path is: + # clone upstream, clone gitea, add, status, commit, tag, push + # branch, push tag. The list previously reserved a slot for + # "git rev-parse HEAD" that _run_command is never asked for, + # which shifted "M README.md\n" one call late and made + # `git status --porcelain` see "" (no changes) instead. mock_run.side_effect = [ "", # git clone upstream - "", # git rev-parse HEAD "", # git clone gitea "", # git add "M README.md\n", # git status --porcelain (has changes) @@ -108,7 +121,15 @@ class TestDocSyncExecutor: mock_get_commit.return_value = "abc123" - # Mock path operations + # Mock path operations. work_dir = Path(...) resolves to + # mock_path.return_value — mock_upstream_dir/mock_gitea_dir have + # to be reachable from there via __truediv__, the same way + # test_executor_successful_sync_entire_repo wires it, or + # `upstream_dir / doc_path` never reaches these mocks at all and + # falls through to an unconfigured auto-generated MagicMock + # instead (observed failure: TypeError joining a MagicMock into + # ', '.join(copied_paths)). + mock_work_dir = MagicMock() mock_upstream_dir = MagicMock() mock_gitea_dir = MagicMock() mock_docs = MagicMock(name="docs") @@ -121,6 +142,8 @@ class TestDocSyncExecutor: mock_examples.is_dir.return_value = True mock_examples.name = "examples" + mock_path.return_value = mock_work_dir + mock_work_dir.__truediv__.side_effect = [mock_upstream_dir, mock_gitea_dir] mock_upstream_dir.__truediv__.side_effect = [mock_docs, mock_examples] mock_gitea_dir.iterdir.return_value = [] @@ -159,13 +182,27 @@ class TestDocSyncExecutor: mock_run.side_effect = run_command_side_effect - # Setup minimal mocking + # Setup minimal mocking. sample_doc_sync_config's docs_paths is + # ["/docs"] (tests/conftest.py), so execute() takes the + # specific-paths branch and needs `upstream_dir / "docs"` wired + # to something with a real string .name — see + # test_executor_sync_specific_paths for the same wiring gap and + # the TypeError it produces unwired. mock_work_dir = MagicMock() mock_upstream_dir = MagicMock() mock_gitea_dir = MagicMock() mock_gitea_dir.iterdir.return_value = [] mock_upstream_dir.iterdir.return_value = [] + mock_docs = MagicMock() + mock_docs.exists.return_value = True + mock_docs.is_dir.return_value = True + mock_docs.name = "docs" + + mock_path.return_value = mock_work_dir + mock_work_dir.__truediv__.side_effect = [mock_upstream_dir, mock_gitea_dir] + mock_upstream_dir.__truediv__.side_effect = [mock_docs] + result = await doc_sync_executor.execute(sample_doc_sync_config, test_settings) assert "already up to date" in result.lower() or "no changes" in result.lower()