R-896: the volume-persistence gate refuses a host that is not a scratch host (no containers on DooPlex)
gates / gates (push) Successful in 7s

check() refuses (HARNESS REFUSED, exit 3) before any docker call unless /opt/upg exists (the bench)
or FELHOM_SCRATCH_HOST=1. TestVenue red before the guard; new end-to-end decoy.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
This commit is contained in:
2026-10-10 15:01:48 +02:00
parent 8ea72aa397
commit 1a642b43e7
6 changed files with 113 additions and 88 deletions
+60
View File
@@ -9,6 +9,7 @@ Run: python3 scripts/test_check_volume_persistence.py
"""
import importlib.util
import io
import os
import sys
import tempfile
import unittest
@@ -597,5 +598,64 @@ class TestEmptyBind(unittest.TestCase):
self.assertFalse(any("R-805" in w for w in why), why)
class TestVenue(unittest.TestCase):
"""R-896: the REAL prober runs containers on the local Docker. On a host that is not a scratch host (DooPlex is
the production host) the gate refuses BEFORE any docker call — the consequence, not the helper: the prober is
never reached."""
def setUp(self):
self._env = os.environ.pop(cvp.SCRATCH_ENV, None)
self._markers = cvp.SCRATCH_MARKERS
self._real = cvp.docker_prober
self.calls = []
def tearDown(self):
if self._env is not None:
os.environ[cvp.SCRATCH_ENV] = self._env
else:
os.environ.pop(cvp.SCRATCH_ENV, None)
cvp.SCRATCH_MARKERS = self._markers
def _run(self):
def spy(*a, **kw):
self.calls.append(a)
raise AssertionError("the real prober was reached")
with tempfile.TemporaryDirectory() as td:
d = Path(td) / "templates" / "demo"
d.mkdir(parents=True)
(d / "docker-compose.yml").write_text("services:\n s:\n image: alpine:3.22\n")
(d / ".felhom.yml").write_text("slug: demo\n")
orig_self_test = cvp.self_test
cvp.self_test = lambda prober: spy()
try:
buf = io.StringIO()
with redirect_stdout(buf):
rc = cvp.check(Path(td)) # the default prober = the real one
finally:
cvp.self_test = orig_self_test
return rc, buf.getvalue()
def test_no_marker_refuses_before_any_docker_call(self):
with tempfile.TemporaryDirectory() as nowhere:
cvp.SCRATCH_MARKERS = (Path(nowhere) / "absent",)
rc, out = self._run()
self.assertEqual(rc, cvp.HARNESS_REFUSED, out)
self.assertIn("not a scratch host", out)
self.assertEqual(self.calls, [])
def test_marker_directory_lets_it_run(self):
with tempfile.TemporaryDirectory() as here:
cvp.SCRATCH_MARKERS = (Path(here),)
with self.assertRaises(AssertionError): # it went on to the self-test (the spy)
self._run()
def test_explicit_env_lets_it_run(self):
with tempfile.TemporaryDirectory() as nowhere:
cvp.SCRATCH_MARKERS = (Path(nowhere) / "absent",)
os.environ[cvp.SCRATCH_ENV] = "1"
with self.assertRaises(AssertionError):
self._run()
if __name__ == "__main__":
unittest.main(verbosity=2)