personal memory agent
1# SPDX-License-Identifier: AGPL-3.0-only
2# Copyright (c) 2026 sol pbc
3"""Canary for the fixture-leak detector in the project-root conftest.py."""
4
5from __future__ import annotations
6
7import subprocess
8import sys
9from pathlib import Path
10
11import pytest
12
13pytest_plugins = ["pytester"]
14pytestmark = pytest.mark.xdist_group("fixture_leak_detector")
15
16_ROOT_CONFTEST = Path(__file__).resolve().parent.parent / "conftest.py"
17
18
19def _prime_git_repo(project_dir: Path) -> None:
20 """Initialise a miniature git repo with one tracked file under tests/fixtures/."""
21 subprocess.run(["git", "init", "-q"], cwd=project_dir, check=True)
22 subprocess.run(
23 [
24 "git",
25 "-c",
26 "commit.gpgsign=false",
27 "-c",
28 "user.email=canary@example.invalid",
29 "-c",
30 "user.name=canary",
31 "commit",
32 "--allow-empty",
33 "-q",
34 "-m",
35 "init",
36 ],
37 cwd=project_dir,
38 check=True,
39 )
40 fixtures_dir = project_dir / "tests" / "fixtures"
41 fixtures_dir.mkdir(parents=True, exist_ok=True)
42 (fixtures_dir / "keep").write_text("kept\n", encoding="utf-8")
43 subprocess.run(["git", "add", "tests/fixtures/keep"], cwd=project_dir, check=True)
44 subprocess.run(
45 [
46 "git",
47 "-c",
48 "commit.gpgsign=false",
49 "-c",
50 "user.email=canary@example.invalid",
51 "-c",
52 "user.name=canary",
53 "commit",
54 "-q",
55 "-m",
56 "fixtures",
57 ],
58 cwd=project_dir,
59 check=True,
60 )
61
62
63def _install_detector(pytester: pytest.Pytester) -> None:
64 """Copy the real root conftest.py into the pytester project root."""
65 pytester.makepyfile(conftest=_ROOT_CONFTEST.read_text(encoding="utf-8"))
66
67
68def _run_nested(pytester: pytest.Pytester) -> pytest.RunResult:
69 basetemp = pytester.path / "basetemp"
70 return pytester.run(
71 sys.executable,
72 "-mpytest",
73 "-q",
74 "-p",
75 "no:cacheprovider",
76 "--basetemp",
77 str(basetemp),
78 )
79
80
81@pytest.mark.timeout(30)
82def test_detector_fires_on_leaked_file(pytester: pytest.Pytester) -> None:
83 _install_detector(pytester)
84 _prime_git_repo(pytester.path)
85 pytester.makepyfile(
86 test_leak="""
87 from pathlib import Path
88
89 def test_writes_into_fixtures(tmp_path):
90 Path("tests/fixtures/leak_probe.tmp").write_text("x")
91 """
92 )
93 result = _run_nested(pytester)
94 assert result.ret != 0, result.stderr.str() + result.stdout.str()
95 combined = result.stderr.str() + result.stdout.str()
96 assert "solstone fixture-leak detector" in combined
97 assert "tests/fixtures/leak_probe.tmp" in combined
98 assert "journal_copy fixture" in combined
99
100
101@pytest.mark.timeout(30)
102def test_detector_silent_on_clean(pytester: pytest.Pytester) -> None:
103 _install_detector(pytester)
104 _prime_git_repo(pytester.path)
105 pytester.makepyfile(
106 test_clean="""
107 def test_noop():
108 assert True
109 """
110 )
111 result = _run_nested(pytester)
112 assert result.ret == 0, result.stderr.str() + result.stdout.str()
113 combined = result.stderr.str() + result.stdout.str()
114 assert "fixture-leak detector" not in combined
115
116
117@pytest.mark.timeout(30)
118def test_detector_skips_without_git_repo(pytester: pytest.Pytester) -> None:
119 _install_detector(pytester)
120 pytester.makepyfile(
121 test_clean="""
122 def test_noop():
123 assert True
124 """
125 )
126 result = _run_nested(pytester)
127 assert result.ret == 0, result.stderr.str() + result.stdout.str()
128 combined = result.stderr.str() + result.stdout.str()
129 assert "git unavailable" in combined