spark: Q83 -- five cells pin the config.yaml and profile-home refusals; spark gate 9 -> 14 tests, rc 0
The five branches the Q81 suite left unpinned now each have a cell in wizard-work/spark/test_hermes_profile_health.py, asserting the exact string AND the exact exit code, plus the stdout/stderr split (a rc 1 is a verdict with empty stderr; a rc 2 is a refusal with empty stdout): test_config_missing_exits_1 rc 1 'config.yaml missing' test_config_as_directory_exits_1 rc 1 'config.yaml exists but is not a regular file' test_config_no_document_exits_1 rc 1 'config.yaml parses to no document' test_config_empty_mapping_exits_1 rc 1 'config.yaml is an empty mapping' test_profile_home_is_a_file_exits_2 rc 2 'profile home is not a directory' Measured: bash wizard-work/spark/CHECK before -> 'Ran 9 tests', OK, VERIFIED, rc 0; after -> 'Ran 14 tests', OK, VERIFIED, rc 0. Mutant battery (q83_mutants.py, q83-mutants.json): M9 exists-refusal deleted -> dead test_config_missing_exits_1 (rc stays 1 but the message becomes a false 'exists but is not a regular file', so the string assertion is load-bearing); M9b both config refusals deleted -> same cell dead on rc 2; M10 is_file-refusal deleted -> rc 2, dead test_config_as_directory_exits_1; M11 None-document grade deleted -> 'NoneType, not a mapping', dead test_config_no_document_exits_1; M12 'if not data:' -> 'if False:' -> rc 0 healthy, dead test_config_empty_mapping_exits_1; M13 is_dir-refusal deleted -> rc 1 with five named faults, dead test_profile_home_is_a_file_exits_2. Baseline copy: rc 0, Ran 14, 0 dead. hermes_profile_health.py is not touched. Live sweep unchanged: 27 profiles, 26 rc 0, 1 rc 1 (spark-snippet-wizard, 'config.yaml missing').
This commit is contained in:
1 parent
ef7c2bda41
commit
fe0991738a
2 files changed
+54
-2
No files matched your search
@@ -27,7 +27,8 @@ python3 --version 2>&1 | grep -q "Python" || exit 10
|
|||||||
# Check git commit pushed
|
# Check git commit pushed
|
||||||
git -C /root/troupe-standup rev-parse ed959f0 >/dev/null 2>&1 || exit 11
|
git -C /root/troupe-standup rev-parse ed959f0 >/dev/null 2>&1 || exit 11
|
||||||
|
|
||||||
# Q81: the profile-health suite. One cell per graded behaviour (9 cells at 2026-10-26).
|
# Q81: the profile-health suite. One cell per graded behaviour (9 cells at 2026-10-26,
|
||||||
|
# 14 after Q83 pins the five config.yaml/profile-home refusals).
|
||||||
# Resolves its own directory from $0, so this leg grades THIS checkout, not a host path.
|
# Resolves its own directory from $0, so this leg grades THIS checkout, not a host path.
|
||||||
SPARK_DIR="$(cd "$(dirname "$0")" && pwd)"
|
SPARK_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||||
suite_out="$(python3 -m unittest discover -s "$SPARK_DIR" -p 'test_hermes_profile_health.py' 2>&1)"
|
suite_out="$(python3 -m unittest discover -s "$SPARK_DIR" -p 'test_hermes_profile_health.py' 2>&1)"
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
"""Cells for wizard-work/spark/hermes_profile_health.py (Q81).
|
"""Cells for wizard-work/spark/hermes_profile_health.py (Q81 cells; Q83 adds five).
|
||||||
|
|
||||||
Every cell drives the tool as a subprocess and grades its exit code, so the thing
|
Every cell drives the tool as a subprocess and grades its exit code, so the thing
|
||||||
under test is the CLI contract (0 clean / 1 named faults / 2 ungradable), not an
|
under test is the CLI contract (0 clean / 1 named faults / 2 ungradable), not an
|
||||||
@@ -7,6 +7,10 @@ internal function. Fixtures are built in a fresh temp dir per cell; nothing here
|
|||||||
reads or writes a real profile home, and no host path is referenced: the tool is
|
reads or writes a real profile home, and no host path is referenced: the tool is
|
||||||
located from this file's own directory.
|
located from this file's own directory.
|
||||||
|
|
||||||
|
Q83: the five cells from test_config_missing_exits_1 to test_profile_home_is_a_file_exits_2
|
||||||
|
pin the config.yaml refusals and the profile-home refusal that the Q81 nine left unpinned;
|
||||||
|
each names the mutant that kills it in its docstring.
|
||||||
|
|
||||||
Run: python3 -m unittest discover -s wizard-work/spark -p test_hermes_profile_health.py
|
Run: python3 -m unittest discover -s wizard-work/spark -p test_hermes_profile_health.py
|
||||||
"""
|
"""
|
||||||
import os
|
import os
|
||||||
@@ -121,6 +125,53 @@ class ProfileHealthTest(unittest.TestCase):
|
|||||||
self.assertIn("no profile named", r.stderr)
|
self.assertIn("no profile named", r.stderr)
|
||||||
self.assertEqual(r.stdout.strip(), "", "rc 2 must not print a verdict")
|
self.assertEqual(r.stdout.strip(), "", "rc 2 must not print a verdict")
|
||||||
|
|
||||||
|
# ---- Q83: the five branches Q81's nine cells left unpinned -----------------
|
||||||
|
def test_config_missing_exits_1(self):
|
||||||
|
"""Killed by M9 (refusal deleted: the is_file() branch relabels a MISSING config as
|
||||||
|
'exists but is not a regular file' at the same rc 1 — the sweep's only live red
|
||||||
|
becomes a lie) and by M9b (both refusals deleted -> rc 2, ungradable).
|
||||||
|
"""
|
||||||
|
home = self.make_home(config=None)
|
||||||
|
r = self.run_tool("--profile", str(home))
|
||||||
|
self.assertEqual(r.returncode, 1, r.stdout + r.stderr)
|
||||||
|
self.assertIn("config.yaml missing", r.stdout)
|
||||||
|
self.assertNotIn("Profile is healthy", r.stdout)
|
||||||
|
self.assertEqual(r.stderr.strip(), "", "rc 1 is a verdict, not a refusal")
|
||||||
|
|
||||||
|
def test_config_as_directory_exits_1(self):
|
||||||
|
"""Killed by M10 (the is_file() refusal deleted -> rc 2, a graded fault lost)."""
|
||||||
|
home = self.make_home(config=None)
|
||||||
|
(home / "config.yaml").mkdir()
|
||||||
|
r = self.run_tool("--profile", str(home))
|
||||||
|
self.assertEqual(r.returncode, 1, r.stdout + r.stderr)
|
||||||
|
self.assertIn("config.yaml exists but is not a regular file", r.stdout)
|
||||||
|
self.assertEqual(r.stderr.strip(), "", "rc 1 is a verdict, not a refusal")
|
||||||
|
|
||||||
|
def test_config_no_document_exits_1(self):
|
||||||
|
"""Killed by M11 (the 'parses to no document' grade deleted -> NoneType wording)."""
|
||||||
|
home = self.make_home(config="# only comments\n# and nothing else\n")
|
||||||
|
r = self.run_tool("--profile", str(home))
|
||||||
|
self.assertEqual(r.returncode, 1, r.stdout + r.stderr)
|
||||||
|
self.assertIn("config.yaml parses to no document", r.stdout)
|
||||||
|
self.assertNotIn("not a mapping", r.stdout, "a null document is not a non-mapping")
|
||||||
|
|
||||||
|
def test_config_empty_mapping_exits_1(self):
|
||||||
|
"""Killed by M12 ('if not data:' -> 'if False:' -> rc 0, healthy)."""
|
||||||
|
home = self.make_home(config="{}\n")
|
||||||
|
r = self.run_tool("--profile", str(home))
|
||||||
|
self.assertEqual(r.returncode, 1, r.stdout + r.stderr)
|
||||||
|
self.assertIn("config.yaml is an empty mapping", r.stdout)
|
||||||
|
self.assertNotIn("Profile is healthy", r.stdout)
|
||||||
|
|
||||||
|
def test_profile_home_is_a_file_exits_2(self):
|
||||||
|
"""Killed by M13 (the is_dir() refusal deleted -> rc 1, five named faults)."""
|
||||||
|
home = self.base / "not-a-home"
|
||||||
|
home.write_text("", encoding="utf-8")
|
||||||
|
r = self.run_tool("--profile", str(home))
|
||||||
|
self.assertEqual(r.returncode, 2, r.stdout + r.stderr)
|
||||||
|
self.assertIn("profile home is not a directory", r.stderr)
|
||||||
|
self.assertEqual(r.stdout.strip(), "", "rc 2 must not print a verdict")
|
||||||
|
|
||||||
def test_profile_name_resolves_under_root(self):
|
def test_profile_name_resolves_under_root(self):
|
||||||
"""Killed by M8 (a bare NAME taken as a relative path)."""
|
"""Killed by M8 (a bare NAME taken as a relative path)."""
|
||||||
root = self.base / "profiles"
|
root = self.base / "profiles"
|
||||||
|
|||||||
Reference in new issue
Block a user