burn-down round 2: R-593 papra field copy, R-760 vikunja healthcheck reason, R-594 English allow-list, R-605 refusal exit 3, R-781 onboarding decoy clone, R-806 scheme; stale runner test fixed
gates / gates (push) Successful in 4s
gates / gates (push) Successful in 4s
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:
@@ -381,8 +381,20 @@ class TestCheckEntryPoint(unittest.TestCase):
|
||||
root = self._catalog(td, ["idle"])
|
||||
with redirect_stdout(io.StringIO()) as buf:
|
||||
rc = cvp.check(root, prober=self._prober({"idle": IDLE}))
|
||||
self.assertEqual(rc, 2)
|
||||
self.assertEqual(rc, 2, "a per-app UNDETERMINED is 2 — the harness ran; it must not read as refused (R-605)")
|
||||
self.assertIn("not a clean bill of health", buf.getvalue())
|
||||
self.assertNotIn("HARNESS REFUSED", buf.getvalue())
|
||||
|
||||
def test_canary_failure_prints_the_harness_refused_marker(self):
|
||||
"""R-605 decoy, the other way: a canary failure must NOT read as a per-app undetermined."""
|
||||
blind = lambda app, app_dir, **kw: VAULTWARDEN # noqa: E731
|
||||
with tempfile.TemporaryDirectory() as td:
|
||||
root = self._catalog(td, ["papra"])
|
||||
with redirect_stdout(io.StringIO()) as buf:
|
||||
rc = cvp.check(root, prober=blind)
|
||||
self.assertEqual(rc, 3)
|
||||
self.assertIn("HARNESS REFUSED", buf.getvalue())
|
||||
self.assertNotIn("UNDETERMINED", buf.getvalue())
|
||||
|
||||
def test_broken_wins_over_undetermined(self):
|
||||
with tempfile.TemporaryDirectory() as td:
|
||||
@@ -400,7 +412,9 @@ class TestCheckEntryPoint(unittest.TestCase):
|
||||
err = io.StringIO()
|
||||
with redirect_stdout(io.StringIO()), redirect_stdout(io.StringIO()):
|
||||
rc = cvp.check(root, prober=blind)
|
||||
self.assertEqual(rc, 2, "a blind prober must yield rc=2, never rc=0")
|
||||
self.assertEqual(rc, 3,
|
||||
"a blind prober must yield rc=3 (the harness refused), never 0 — and never 2, "
|
||||
"which reads as 'it ran and some apps were undetermined' (R-605)")
|
||||
|
||||
def test_a_prober_that_flags_everything_is_refused(self):
|
||||
"""The other direction — a prober that cannot clear a correct template is equally useless."""
|
||||
@@ -409,7 +423,7 @@ class TestCheckEntryPoint(unittest.TestCase):
|
||||
root = self._catalog(td, ["vaultwarden"])
|
||||
with redirect_stdout(io.StringIO()):
|
||||
rc = cvp.check(root, prober=crying_wolf)
|
||||
self.assertEqual(rc, 2)
|
||||
self.assertEqual(rc, 3)
|
||||
|
||||
def test_out_of_circulation_apps_are_skipped_and_named(self):
|
||||
with tempfile.TemporaryDirectory() as td:
|
||||
@@ -488,6 +502,51 @@ class TestRoutedPorts(unittest.TestCase):
|
||||
self.assertEqual(cvp.routed_ports({"services": {"db": {"labels": {"x": "y"}}}}), [])
|
||||
|
||||
|
||||
class TestRoutedSchemes(unittest.TestCase):
|
||||
"""R-806: the GET exercise speaks the backend's own scheme. Plain http to crafty-controller's HTTPS :8443 got no
|
||||
application answer, so the app reached data only through its fixture seed."""
|
||||
|
||||
@staticmethod
|
||||
def _labels_of(app):
|
||||
# the REAL template's traefik labels, read as text (no PyYAML on the catalog CI runner)
|
||||
p = Path(__file__).resolve().parent.parent / "templates" / app / "docker-compose.yml"
|
||||
return {k: v for k, v in (l.strip().strip("-").strip().strip("\"'").split("=", 1)
|
||||
for l in p.read_text(encoding="utf-8").splitlines()
|
||||
if l.strip().startswith(("- \"traefik.", "- traefik.", "- 'traefik.")) and "=" in l)}
|
||||
|
||||
def test_https_backend_gets_an_https_url_with_k(self):
|
||||
labels = self._labels_of("crafty-controller")
|
||||
resolved = {"services": {"crafty-controller": {"labels": labels}}}
|
||||
schemes = cvp.routed_schemes(resolved)
|
||||
port = cvp.routed_ports(resolved)[0]
|
||||
self.assertEqual(schemes.get(port), "https", "crafty-controller's scheme=https label was not read: %r" % schemes)
|
||||
argv = cvp.exercise_argv("10.0.0.5", port, "/", schemes[port])
|
||||
self.assertEqual(argv[-1], "https://10.0.0.5:%d/" % port)
|
||||
self.assertIn("-k", argv, "a self-signed backend refuses curl without -k: the exercise would read 000")
|
||||
|
||||
def test_no_scheme_label_stays_http_without_k(self):
|
||||
resolved = {"services": {"vikunja": {"labels": self._labels_of("vikunja")}}}
|
||||
schemes = cvp.routed_schemes(resolved)
|
||||
self.assertEqual(schemes, {3456: "http"})
|
||||
argv = cvp.exercise_argv("10.0.0.5", 3456, "/login", schemes[3456], deep=True)
|
||||
self.assertEqual(argv[-1], "http://10.0.0.5:3456/login")
|
||||
self.assertNotIn("-k", argv)
|
||||
self.assertIn("-L", argv)
|
||||
|
||||
def test_decoy_scheme_on_ANOTHER_traefik_service_does_not_upgrade_this_port(self):
|
||||
"""The label without the fact: `scheme=https` present, but on a different traefik service than the port."""
|
||||
resolved = {"services": {"a": {"labels": {
|
||||
"traefik.http.services.web.loadbalancer.server.port": "8080",
|
||||
"traefik.http.services.admin.loadbalancer.server.scheme": "https"}}}}
|
||||
self.assertEqual(cvp.routed_schemes(resolved), {8080: "http"})
|
||||
|
||||
def test_list_form_labels_read_too(self):
|
||||
resolved = {"services": {"c": {"labels": [
|
||||
"traefik.http.services.c.loadbalancer.server.port=8443",
|
||||
"traefik.http.services.c.loadbalancer.server.scheme=https"]}}}
|
||||
self.assertEqual(cvp.routed_schemes(resolved), {8443: "https"})
|
||||
|
||||
|
||||
class TestEmptyDeclaredVolume(unittest.TestCase):
|
||||
def test_empty_declared_volume_is_undetermined_even_when_another_mount_has_data(self):
|
||||
"""R-788: one mount holds data, the DECLARED volume is empty — the old rule called this CLEAN."""
|
||||
|
||||
Reference in New Issue
Block a user