diff --git a/tests/test_changes.py b/tests/test_changes.py index 34dfdbb3..02f67c7b 100644 --- a/tests/test_changes.py +++ b/tests/test_changes.py @@ -19,6 +19,7 @@ class TestChanges: def setup_method(self): self.tmp = tempfile.NamedTemporaryFile(suffix=".db", delete=False) + self.tmp.close() # close the OS handle so Windows can unlink it in teardown self.store = GraphStore(self.tmp.name) def teardown_method(self): @@ -438,11 +439,11 @@ def test_detect_changes_tool_no_changes(self): patch("code_review_graph.tools.review._get_store") as mock_get_store, patch("code_review_graph.tools.review.get_changed_files", return_value=[]), patch("code_review_graph.tools.review.get_staged_and_unstaged", return_value=[]), + # Prevent the tool from closing our shared store; patch.object + # restores close() afterwards so teardown can really close it. + patch.object(self.store, "close"), ): mock_get_store.return_value = (self.store, Path("/fake/repo")) - # Prevent the store from being closed by the tool - # (our teardown handles it). - self.store.close = lambda: None result = detect_changes_func(base="HEAD~1", repo_root="/fake/repo") assert result["status"] == "ok" @@ -463,9 +464,9 @@ def test_detect_changes_tool_with_changes(self): "code_review_graph.tools.review.parse_git_diff_ranges", return_value={"app.py": [(1, 10)]}, ), + patch.object(self.store, "close"), ): mock_get_store.return_value = (self.store, Path("/fake/repo")) - self.store.close = lambda: None result = detect_changes_func(base="HEAD~1", repo_root="/fake/repo") assert result["status"] == "ok" @@ -480,6 +481,7 @@ class TestAnalyzeChangesFunctionCap: def setup_method(self): self.tmp = tempfile.NamedTemporaryFile(suffix=".db", delete=False) + self.tmp.close() # close the OS handle so Windows can unlink it in teardown self.store = GraphStore(self.tmp.name) def teardown_method(self): @@ -530,6 +532,7 @@ class TestAnalyzeChangesInternalParseRemap: def setup_method(self): self.tmp = tempfile.NamedTemporaryFile(suffix=".db", delete=False) + self.tmp.close() # close the OS handle so Windows can unlink it in teardown self.store = GraphStore(self.tmp.name) def teardown_method(self): diff --git a/tests/test_communities.py b/tests/test_communities.py index 7d7b2dca..91b8c24a 100644 --- a/tests/test_communities.py +++ b/tests/test_communities.py @@ -24,6 +24,7 @@ class TestCommunities: def setup_method(self): self.tmp = tempfile.NamedTemporaryFile(suffix=".db", delete=False) + self.tmp.close() # close the OS handle so Windows can unlink it in teardown self.store = GraphStore(self.tmp.name) def teardown_method(self): diff --git a/tests/test_daemon.py b/tests/test_daemon.py index 61f5139b..836640ba 100644 --- a/tests/test_daemon.py +++ b/tests/test_daemon.py @@ -268,6 +268,7 @@ def test_clear_pid(self, pid_path): clear_pid(pid_path) assert not pid_path.exists() + @pytest.mark.skipif(sys.platform == "win32", reason="POSIX os.kill branch") @patch("os.kill") def test_is_daemon_running_alive(self, mock_kill, pid_path): """os.kill(pid, 0) succeeds — daemon is running.""" diff --git a/tests/test_flows.py b/tests/test_flows.py index 8c0536fa..15cdcd6e 100644 --- a/tests/test_flows.py +++ b/tests/test_flows.py @@ -19,6 +19,7 @@ class TestFlows: def setup_method(self): self.tmp = tempfile.NamedTemporaryFile(suffix=".db", delete=False) + self.tmp.close() # close the OS handle so Windows can unlink it in teardown self.store = GraphStore(self.tmp.name) def teardown_method(self): diff --git a/tests/test_graph.py b/tests/test_graph.py index fb407810..d0757e6e 100644 --- a/tests/test_graph.py +++ b/tests/test_graph.py @@ -12,6 +12,7 @@ class TestGraphStore: def setup_method(self): self.tmp = tempfile.NamedTemporaryFile(suffix=".db", delete=False) + self.tmp.close() # close the OS handle so Windows can unlink it in teardown self.store = GraphStore(self.tmp.name) def teardown_method(self): @@ -283,6 +284,7 @@ class TestImpactRadiusSql: def setup_method(self): self.tmp = tempfile.NamedTemporaryFile(suffix=".db", delete=False) + self.tmp.close() # close the OS handle so Windows can unlink it in teardown self.store = GraphStore(self.tmp.name) self._build_chain() @@ -347,6 +349,7 @@ class TestGetTransitiveTestsFrontierCap: def setup_method(self): self.tmp = tempfile.NamedTemporaryFile(suffix=".db", delete=False) + self.tmp.close() # close the OS handle so Windows can unlink it in teardown self.store = GraphStore(self.tmp.name) def teardown_method(self): diff --git a/tests/test_integration_v2.py b/tests/test_integration_v2.py index 48225bb9..a9b4eeeb 100644 --- a/tests/test_integration_v2.py +++ b/tests/test_integration_v2.py @@ -37,6 +37,7 @@ class TestV2Integration: def setup_method(self): self.tmp = tempfile.NamedTemporaryFile(suffix=".db", delete=False) + self.tmp.close() # close the OS handle so Windows can unlink it in teardown self.store = GraphStore(self.tmp.name) self._seed_realistic_graph() diff --git a/tests/test_migrations.py b/tests/test_migrations.py index d208e604..5de9c702 100644 --- a/tests/test_migrations.py +++ b/tests/test_migrations.py @@ -16,6 +16,7 @@ class TestMigrations: def setup_method(self): self.tmp = tempfile.NamedTemporaryFile(suffix=".db", delete=False) + self.tmp.close() # close the OS handle so Windows can unlink it in teardown self.store = GraphStore(self.tmp.name) def teardown_method(self): diff --git a/tests/test_postprocessing.py b/tests/test_postprocessing.py index f9b0f946..ed9804cc 100644 --- a/tests/test_postprocessing.py +++ b/tests/test_postprocessing.py @@ -21,6 +21,7 @@ def _get_signature(store, qualified_name): class TestRunPostProcessing: def setup_method(self): self.tmp = tempfile.NamedTemporaryFile(suffix=".db", delete=False) + self.tmp.close() # close the OS handle so Windows can unlink it in teardown self.store = GraphStore(self.tmp.name) self._seed_data() @@ -174,6 +175,7 @@ def test_no_warnings_on_healthy_store(self): def test_empty_store_no_crash(self): empty_tmp = tempfile.NamedTemporaryFile(suffix=".db", delete=False) + empty_tmp.close() # close the OS handle so Windows can unlink it below empty_store = GraphStore(empty_tmp.name) try: result = run_post_processing(empty_store) @@ -213,6 +215,7 @@ def test_signature_truncated_at_512(self): class TestPostProcessingStepIsolation: def setup_method(self): self.tmp = tempfile.NamedTemporaryFile(suffix=".db", delete=False) + self.tmp.close() # close the OS handle so Windows can unlink it in teardown self.store = GraphStore(self.tmp.name) self.store.upsert_node( NodeInfo( diff --git a/tests/test_refactor.py b/tests/test_refactor.py index bc83bc7f..28368c75 100644 --- a/tests/test_refactor.py +++ b/tests/test_refactor.py @@ -23,6 +23,7 @@ class TestRenamePreview: def setup_method(self): self.tmp = tempfile.NamedTemporaryFile(suffix=".db", delete=False) + self.tmp.close() # close the OS handle so Windows can unlink it in teardown self.store = GraphStore(self.tmp.name) self._seed() @@ -110,6 +111,7 @@ class TestFindDeadCode: def setup_method(self): self.tmp = tempfile.NamedTemporaryFile(suffix=".db", delete=False) + self.tmp.close() # close the OS handle so Windows can unlink it in teardown self.store = GraphStore(self.tmp.name) self._seed() @@ -439,6 +441,7 @@ class TestSuggestRefactorings: def setup_method(self): self.tmp = tempfile.NamedTemporaryFile(suffix=".db", delete=False) + self.tmp.close() # close the OS handle so Windows can unlink it in teardown self.store = GraphStore(self.tmp.name) self._seed() @@ -723,6 +726,7 @@ class TestFindDeadCodeWithReferences: def setup_method(self): self.tmp = tempfile.NamedTemporaryFile(suffix=".db", delete=False) + self.tmp.close() # close the OS handle so Windows can unlink it in teardown self.store = GraphStore(self.tmp.name) self._seed() @@ -850,6 +854,7 @@ class TestFindDeadCodeModuleScope: def setup_method(self): self.tmp = tempfile.NamedTemporaryFile(suffix=".db", delete=False) + self.tmp.close() # close the OS handle so Windows can unlink it in teardown self.store = GraphStore(self.tmp.name) self.parser = CodeParser() diff --git a/tests/test_search.py b/tests/test_search.py index e7d90675..e10f99a2 100644 --- a/tests/test_search.py +++ b/tests/test_search.py @@ -16,6 +16,7 @@ class TestHybridSearch: def setup_method(self): self.tmp = tempfile.NamedTemporaryFile(suffix=".db", delete=False) + self.tmp.close() # close the OS handle so Windows can unlink it in teardown self.store = GraphStore(self.tmp.name) self._seed_data() diff --git a/tests/test_skills.py b/tests/test_skills.py index 2914ca79..490fab24 100644 --- a/tests/test_skills.py +++ b/tests/test_skills.py @@ -430,7 +430,8 @@ def test_commands_do_not_pin_a_specific_repo_path(self, tmp_path): class TestInstallCodexHooks: def test_creates_hooks_file(self, tmp_path, monkeypatch): - monkeypatch.setenv("HOME", str(tmp_path)) + # Path.home() ignores HOME on Windows; patch it like the cursor tests do. + monkeypatch.setattr("code_review_graph.skills.Path.home", lambda: tmp_path) hooks_path = install_codex_hooks(tmp_path / "repo") assert hooks_path == tmp_path / ".codex" / "hooks.json" assert hooks_path.exists() @@ -440,7 +441,8 @@ def test_creates_hooks_file(self, tmp_path, monkeypatch): assert "SessionStart" in data["hooks"] def test_merges_with_existing(self, tmp_path, monkeypatch): - monkeypatch.setenv("HOME", str(tmp_path)) + # Path.home() ignores HOME on Windows; patch it like the cursor tests do. + monkeypatch.setattr("code_review_graph.skills.Path.home", lambda: tmp_path) codex_dir = tmp_path / ".codex" codex_dir.mkdir(parents=True) existing = { @@ -460,7 +462,8 @@ def test_merges_with_existing(self, tmp_path, monkeypatch): assert "SessionStart" in data["hooks"] def test_creates_hooks_backup(self, tmp_path, monkeypatch): - monkeypatch.setenv("HOME", str(tmp_path)) + # Path.home() ignores HOME on Windows; patch it like the cursor tests do. + monkeypatch.setattr("code_review_graph.skills.Path.home", lambda: tmp_path) codex_dir = tmp_path / ".codex" codex_dir.mkdir(parents=True) existing = {"hooks": {"Stop": []}} @@ -475,7 +478,8 @@ def test_creates_hooks_backup(self, tmp_path, monkeypatch): assert backup == existing def test_idempotent_by_command(self, tmp_path, monkeypatch): - monkeypatch.setenv("HOME", str(tmp_path)) + # Path.home() ignores HOME on Windows; patch it like the cursor tests do. + monkeypatch.setattr("code_review_graph.skills.Path.home", lambda: tmp_path) repo_root = tmp_path / "repo" install_codex_hooks(repo_root) install_codex_hooks(repo_root) @@ -1068,6 +1072,7 @@ def test_creates_hook_scripts(self, tmp_path): assert (hooks_dir / "crg-session-start.sh").exists() assert (hooks_dir / "crg-pre-commit.sh").exists() + @pytest.mark.skipif(sys.platform == "win32", reason="POSIX exec bits") def test_scripts_are_executable(self, tmp_path): with patch("code_review_graph.skills.Path.home", return_value=tmp_path): install_cursor_hooks() diff --git a/tests/test_tools.py b/tests/test_tools.py index 578536d4..b81046b8 100644 --- a/tests/test_tools.py +++ b/tests/test_tools.py @@ -29,6 +29,7 @@ class TestTools: def setup_method(self): self.tmp = tempfile.NamedTemporaryFile(suffix=".db", delete=False) + self.tmp.close() # close the OS handle so Windows can unlink it in teardown self.store = GraphStore(self.tmp.name) self._seed_data() @@ -557,6 +558,7 @@ class TestFindLargeFunctions: def setup_method(self): self.tmp = tempfile.NamedTemporaryFile(suffix=".db", delete=False) + self.tmp.close() # close the OS handle so Windows can unlink it in teardown self.store = GraphStore(self.tmp.name) # Create functions of various sizes self.store.upsert_node(NodeInfo( @@ -1202,6 +1204,7 @@ class TestComputeSummaries: def setup_method(self): self.tmp = tempfile.NamedTemporaryFile(suffix=".db", delete=False) + self.tmp.close() # close the OS handle so Windows can unlink it in teardown self.store = GraphStore(self.tmp.name) self._seed_graph() diff --git a/tests/test_wiki.py b/tests/test_wiki.py index 966ec732..1fba4fdc 100644 --- a/tests/test_wiki.py +++ b/tests/test_wiki.py @@ -17,6 +17,7 @@ class TestWiki: def setup_method(self): self.tmp = tempfile.NamedTemporaryFile(suffix=".db", delete=False) + self.tmp.close() # close the OS handle so Windows can unlink it in teardown self.store = GraphStore(self.tmp.name) self.wiki_dir = tempfile.mkdtemp()