fix(tests): the backup stand-ins predate kind (#3849 step 1)
CI & Build / Python lint (push) Successful in 3s
CI & Build / Plugin hooks (push) Successful in 9s
CI & Build / integration (push) Successful in 45s
CI & Build / TypeScript typecheck (push) Successful in 56s
CI & Build / Python tests (push) Successful in 1m38s
CI & Build / Build & push image (push) Successful in 48s

Two unit tests build a rule with SimpleNamespace rather than the model, so
adding a column broke them — the fixture has no `kind` for `_rule_rows` to
read. Fixture-only; the export itself was already right, which the existing
column-coverage guard confirmed by passing.

Adds the guard that coverage check cannot make. `_stand_in` walks
`__table__.columns` and proves the KEY is emitted; it cannot prove the VALUE
survives. A preference exported as a rule is a silent failure — the restored
rule reads fine and simply binds when it was only ever a preference — and a
fixture carrying the default would pass against a `_rule_rows` that dropped
the field and let the importer's `or "rule"` refill it. So the new test
asserts on `preference`, the one value that cannot be reconstructed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011cPyzNnegXHr5iRMzzy5KJ
This commit is contained in:
2026-09-10 21:31:16 -04:00
co-authored by Claude Opus 5
parent c63172272d
commit 4aae4973f7
+26 -2
View File
@@ -388,7 +388,7 @@ def test_rule_rows_carry_the_verification_fields():
row = SimpleNamespace(
id=1, topic_id=2, project_id=None, title="t", statement="s",
why="w", how_to_apply="h", order_index=0,
when_to_apply="when", tier="conditional",
when_to_apply="when", tier="conditional", kind="rule",
verify_with="cat some/file", expires_when="the file grows a shell",
verified_at=checked, arose_from_id=99,
created_at=checked, updated_at=checked,
@@ -415,7 +415,7 @@ def test_rule_rows_keep_an_unverified_rule_unverified():
row = SimpleNamespace(
id=1, topic_id=2, project_id=None, title="t", statement="s",
why=None, how_to_apply=None, order_index=0,
when_to_apply=None, tier="always_on",
when_to_apply=None, tier="always_on", kind="rule",
verify_with=None, expires_when=None, verified_at=None,
arose_from_id=None,
created_at=datetime(2026, 8, 27, tzinfo=timezone.utc),
@@ -426,3 +426,27 @@ def test_rule_rows_keep_an_unverified_rule_unverified():
assert backup._dt_or_none("2026-08-27T12:00:00+00:00") == datetime(
2026, 8, 27, 12, 0, tzinfo=timezone.utc
)
def test_rule_rows_carry_the_kind_so_a_preference_does_not_restore_as_a_rule():
"""FORCE has to survive a backup, and the failure would be silent.
A preference that comes back as a rule is not a missing field anyone would
notice — the rule reads fine, it simply binds when it was only ever meant
to be how the operator prefers things done. Nothing in the restored
rulebook says it used to be softer.
Asserted with `preference` rather than `rule` on purpose: a fixture
carrying the DEFAULT would pass just as happily against a `_rule_rows`
that dropped the field entirely and let the importer's `or "rule"` fill
the hole back in, which is precisely the bug this guards.
"""
stamp = datetime(2026, 9, 10, tzinfo=timezone.utc)
row = SimpleNamespace(
id=1, topic_id=2, project_id=None, title="t", statement="s",
why=None, how_to_apply=None, order_index=0,
when_to_apply="when", tier="conditional", kind="preference",
verify_with=None, expires_when=None, verified_at=None,
arose_from_id=None, created_at=stamp, updated_at=stamp,
)
assert backup._rule_rows([row])[0]["kind"] == "preference"