Skip to content

Commit 8595cf8

Browse files
test: make Windows phase fence fixtures portable
1 parent 04acabe commit 8595cf8

3 files changed

Lines changed: 48 additions & 11 deletions

File tree

‎.github/workflows/test.yml‎

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,3 +18,22 @@ jobs:
1818
- run: python tools/run_validation.py
1919
- run: python -m pip install --no-deps .
2020
- run: trace-gc --version
21+
windows-fence:
22+
runs-on: windows-latest
23+
strategy:
24+
matrix:
25+
python-version: ['3.12', '3.13']
26+
steps:
27+
- uses: actions/checkout@v4
28+
- uses: actions/setup-python@v5
29+
with:
30+
python-version: ${{ matrix.python-version }}
31+
- run: python -m pip install -r requirements.txt
32+
- name: Run the nine native Windows admission-fence cases
33+
shell: bash
34+
run: |
35+
set -o pipefail
36+
python -m unittest discover -s tests -p "test_paper_pilot_phase_cutover_guarded_v2.py" -v 2>&1 | tee "$RUNNER_TEMP/windows-fence.log"
37+
# A bare "OK" means every case executed; skips would print "OK (skipped=N)".
38+
grep -Eq '^Ran 9 tests in ' "$RUNNER_TEMP/windows-fence.log"
39+
grep -Eq '^OK[[:space:]]*$' "$RUNNER_TEMP/windows-fence.log"

‎MANIFEST.json‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@
77
"files": [
88
{
99
"path": ".github/workflows/test.yml",
10-
"bytes": 615,
11-
"sha256": "ba3c6064c1fb430eda6f7fedf60561637be3daedfff4d1d533a61e5ef851ed2b"
10+
"bytes": 1434,
11+
"sha256": "a38dd6c889f7d214a3a3590c76fa07ab73586b737d78f4b7e2fad798575de650"
1212
},
1313
{
1414
"path": ".gitignore",
@@ -2632,8 +2632,8 @@
26322632
},
26332633
{
26342634
"path": "tests/test_paper_pilot_phase_cutover_guarded_v2.py",
2635-
"bytes": 11173,
2636-
"sha256": "c1fab4e9adbe973c53d01fa2d0340559ffef0dc4d482e2b5b8bb68cfa9f43c33"
2635+
"bytes": 11981,
2636+
"sha256": "8ab205a79fd925b3ed645a6ea3bd8e6f872c98d731b0467358f7f5de74ca6513"
26372637
},
26382638
{
26392639
"path": "tests/test_paper_pilot_phase_cutover_guarded_v4.py",

‎tests/test_paper_pilot_phase_cutover_guarded_v2.py‎

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
1-
"""Windows-only owned disposable tests for the historical admission barrier."""
1+
"""Windows-only owned disposable tests for the historical admission barrier.
2+
3+
Every case builds its own fixture inside a unique temporary directory created
4+
by this test, so it never depends on (or creates) any historical task state.
5+
"""
26
from __future__ import annotations
37

48
import hashlib
@@ -17,7 +21,7 @@
1721
from trace_gc.budget import RunBudget
1822

1923

20-
OWNED = Path(r"D:\codex-task-runs\issue90-real-historical-transition-disabled-plan-20260930-01\fence-owned-tests")
24+
TEMP_PREFIX = "jraphyte-fence-"
2125
LIMITS = {"retrieval_requests": 1, "model_calls": 1, "retries": 0,
2226
"solver_expansions": 1, "review_actions": 1, "request_bytes": 1000}
2327

@@ -38,11 +42,14 @@ def test_module_load_does_not_require_msvcrt(self):
3842
def setUp(self):
3943
if os.name != "nt":
4044
self.skipTest("Windows ACL contract")
41-
OWNED.mkdir(exist_ok=True)
42-
self.temp = tempfile.TemporaryDirectory(dir=OWNED)
45+
self.sid = _current_sid()
46+
self.temp = tempfile.TemporaryDirectory(prefix=TEMP_PREFIX)
4347
self.addCleanup(self._cleanup)
4448
self.base = Path(self.temp.name)
45-
self.assertEqual(self.base.resolve().parent, OWNED.resolve())
49+
# The fixture owns this fresh directory; only paths beneath it are ever
50+
# given a barrier ACL or have one removed again.
51+
self.assertTrue(self.base.is_dir())
52+
self.assertTrue(self.base.name.startswith(TEMP_PREFIX))
4653
self.root = self.base / "old-app"
4754
self.root.mkdir()
4855
self.old = self.root / "state"
@@ -73,14 +80,25 @@ def setUp(self):
7380
guarded.directory_dacl_sddl(self.old / name).encode()).hexdigest()
7481
for name in guarded.NAMES}
7582

83+
def _owned(self, path: Path) -> Path:
84+
resolved = Path(path).resolve()
85+
if not resolved.is_relative_to(self.base.resolve()):
86+
raise AssertionError(f"refusing ACL change outside fixture: {path}")
87+
return resolved
88+
7689
def _cleanup(self):
90+
# Lift only this test's own deny ACEs so the owned tree can be removed;
91+
# no ancestor of the temporary directory has its ACL touched.
7792
if hasattr(self, "root") and self.root.exists():
78-
_call("icacls.exe", str(self.root), "/remove:d", "*" + self.sid)
93+
_call("icacls.exe", str(self._owned(self.root)), "/remove:d", "*" + self.sid)
7994
for root in (getattr(self, "old", None), getattr(self, "archive", None)):
8095
if root is not None and root.exists():
8196
for name in guarded.NAMES:
82-
_call("icacls.exe", str(root / name), "/remove:d", "*" + self.sid)
97+
if (root / name).exists():
98+
_call("icacls.exe", str(self._owned(root / name)),
99+
"/remove:d", "*" + self.sid)
83100
self.temp.cleanup()
101+
self.assertFalse(self.base.exists())
84102

85103
def invoke(self):
86104
return guarded.fence_closed_old_phase_guarded(

0 commit comments

Comments
 (0)