fix(spec-revocation-check): --tag scopes marker parsing; free-prose ignored by default (brief)
This commit is contained in:
@@ -229,21 +229,36 @@ assert_exit "untagged marker no --tag exits 0" 0 "$rc" "$out" ""
|
||||
assert_contains "untagged marker no --tag says clean" "clean" "$out"
|
||||
|
||||
# ---- Test 11: --tag filters other tags ---------------------------------
|
||||
# With --tag X, lines containing markers for tag Y (or untagged or free-
|
||||
# prose) are entirely ignored: not parsed, not reported, not used as
|
||||
# quotes. The base line that the foreign marker quoted is still in the
|
||||
# current file (inside the ignored marker text), so Check B still passes.
|
||||
echo "=== --tag filters other tags ==="
|
||||
T="$SCRATCH/t11"; mk_repo "$T"
|
||||
commit_file "$T" "spec/foo.md" \
|
||||
$'# Spec\n\nA line that stays.\n\nA line that gets revoked.\n\n' >/dev/null
|
||||
BASE=$(git -C "$T" rev-parse HEAD)
|
||||
# Quote uses M33, but we run with --tag M99, so the marker is malformed
|
||||
# (no closing quote because the regex requires "M99").
|
||||
# Quote uses M33, but we run with --tag M99 — the M33 marker is ignored
|
||||
# entirely (not parsed as malformed). The base line text is still in the
|
||||
# current file (inside the ignored marker), so Check B still passes.
|
||||
write_file_via_py "$T" "spec/foo.md" \
|
||||
$'# Spec\n\nA line that stays.\n\nRevokes prior text (M33): "A line that gets revoked."\n\n'
|
||||
out=$(run_check "$T" "$BASE" --tag M99); rc=$?
|
||||
assert_exit "--tag filters other tag exits 1" 1 "$rc" "$out" ""
|
||||
# Without --tag, the marker WOULD have parsed and the line would survive.
|
||||
assert_exit "--tag M99 ignores M33 marker exits 0" 0 "$rc" "$out" ""
|
||||
assert_contains "--tag M99 ignores M33 says clean" "clean" "$out"
|
||||
# Without --tag, the marker parses and the line is covered.
|
||||
out_notag=$(run_check "$T" "$BASE"); rc_notag=$?
|
||||
assert_exit "no --tag accepts M33 marker exits 0" 0 "$rc_notag" "$out_notag" ""
|
||||
|
||||
# Now run --tag M99 with an M33 marker that quotes something OTHER than
|
||||
# a base line — the base line is then truly unquoted, and with --tag M99
|
||||
# the M33 marker doesn't cover it → unquoted deletion → violation.
|
||||
write_file_via_py "$T" "spec/foo.md" \
|
||||
$'# Spec\n\nA line that stays.\n\nRevokes prior text (M33): "Some other thing entirely."\n\n'
|
||||
out=$(run_check "$T" "$BASE" --tag M99); rc=$?
|
||||
assert_exit "--tag M99 + bogus M33 quote exits 1" 1 "$rc" "$out" ""
|
||||
assert_contains "--tag M99 + bogus M33 quote names unquoted" "unquoted deletions" "$out"
|
||||
|
||||
# ---- Test 12: bad --base (exit 2) ---------------------------------------
|
||||
echo "=== bad --base ==="
|
||||
T="$SCRATCH/t12"; mk_repo "$T"
|
||||
@@ -286,6 +301,92 @@ out=$(python3 "$SCRIPT" --help); rc=$?
|
||||
assert_exit "--help exits 0" 0 "$rc" "$out" ""
|
||||
assert_contains "--help shows usage" "spec-revocation-check" "$out"
|
||||
assert_contains "--help mentions --base" "--base" "$out"
|
||||
assert_contains "--help mentions --strict" "--strict" "$out"
|
||||
|
||||
# ---- Tests 16-19: real-case fixture (round 2) -------------------------
|
||||
# One base file, one current file with four marker styles:
|
||||
# - (M33) tagged quote (valid)
|
||||
# - Older untagged mid-line marker (M32-era, M23 amendment note)
|
||||
# - (M23) tagged quote
|
||||
# - Free-prose `**Revokes prior text:**` line
|
||||
# Each marker covers a real base line. With --tag M33 the foreign markers
|
||||
# must be ignored; the free-prose line must be ignored unless --strict.
|
||||
echo "=== --tag scopes marker parsing to the literal tag ==="
|
||||
T="$SCRATCH/t16"; mk_repo "$T"
|
||||
# Base: a stable line + one line per marker style + a short line that
|
||||
# Check B skips (under min-line-chars=12). The short line is what the
|
||||
# free-prose marker refers to (so Check B won't flag its deletion).
|
||||
commit_file "$T" "spec/foo.md" \
|
||||
$'# Spec\n\nA line that stays.\n\nLine revoked by M33.\n\nLine revoked by untagged mid-line.\n\nLine revoked by M23.\n\nshort text\n' >/dev/null
|
||||
BASE=$(git -C "$T" rev-parse HEAD)
|
||||
write_file_via_py "$T" "spec/foo.md" \
|
||||
$'# Spec\n\nA line that stays.\n\nRevokes prior text (M33): "Line revoked by M33."\n\n*(Amended M23: ... — Revokes prior text: "Line revoked by untagged mid-line.".)*\n\nRevokes prior text (M23): "Line revoked by M23."\n\n> **Revokes prior text:** the short text\n'
|
||||
|
||||
# Sub-case 1: --tag M33 — only the (M33) line is parsed; foreign markers
|
||||
# are ignored entirely. The base lines they refer to are still verbatim
|
||||
# in the current file (inside the ignored marker text), so Check B
|
||||
# passes. Clean.
|
||||
out=$(run_check "$T" "$BASE" --tag M33); rc=$?
|
||||
assert_exit "--tag M33 ignores foreign markers exits 0" 0 "$rc" "$out" ""
|
||||
assert_contains "--tag M33 ignores foreign says clean" "clean" "$out"
|
||||
|
||||
# Sub-case 2: malformed (M33) line with --tag M33 — flagged. The other
|
||||
# foreign markers must STILL be ignored (not malformed).
|
||||
write_file_via_py "$T" "spec/foo.md" \
|
||||
$'# Spec\n\nA line that stays.\n\nRevokes prior text (M33) no quote\n\n*(Amended M23: ... — Revokes prior text: "Line revoked by untagged mid-line.".)*\n\nRevokes prior text (M23): "Line revoked by M23."\n\n> **Revokes prior text:** the short text\n'
|
||||
out=$(run_check "$T" "$BASE" --tag M33); rc=$?
|
||||
assert_exit "malformed (M33) line exits 1" 1 "$rc" "$out" ""
|
||||
assert_contains "malformed (M33) under header" "malformed markers" "$out"
|
||||
assert_contains "malformed (M33) names the raw line" "no quote" "$out"
|
||||
|
||||
# Restore the valid fixture for sub-cases 3 and 4.
|
||||
write_file_via_py "$T" "spec/foo.md" \
|
||||
$'# Spec\n\nA line that stays.\n\nRevokes prior text (M33): "Line revoked by M33."\n\n*(Amended M23: ... — Revokes prior text: "Line revoked by untagged mid-line.".)*\n\nRevokes prior text (M23): "Line revoked by M23."\n\n> **Revokes prior text:** the short text\n'
|
||||
|
||||
# Sub-case 3: no --tag — foreign markers parse (untagged branch + M23
|
||||
# tagged branch); the free-prose line is silently ignored by default.
|
||||
# Output: zero violations but a summary line that counts the ignored
|
||||
# marker (NOT the early "clean — every line survives" message, since
|
||||
# ignored > 0).
|
||||
out=$(run_check "$T" "$BASE"); rc=$?
|
||||
assert_exit "no --tag, free-prose ignored, exits 0" 0 "$rc" "$out" ""
|
||||
assert_contains "no --tag, free-prose ignored, shows summary line" "Summary:" "$out"
|
||||
assert_contains "no --tag, free-prose ignored, summary shows ignored count" "1 ignored" "$out"
|
||||
|
||||
# Sub-case 4: no --tag --strict — free-prose becomes malformed.
|
||||
out=$(run_check "$T" "$BASE" --strict); rc=$?
|
||||
assert_exit "no --tag --strict, free-prose malformed exits 1" 1 "$rc" "$out" ""
|
||||
assert_contains "no --tag --strict, free-prose under header" "malformed markers" "$out"
|
||||
|
||||
# ---- Round-2 dedicated negative fixtures --------------------------------
|
||||
# The negative-test proof loop below needs fixtures that exit 1
|
||||
# consistently (not the ones above, whose state mutates between sub-
|
||||
# cases). t17 is a malformed-(M33) fixture, t19 is a free-prose line with
|
||||
# --strict; both should fail when run against the synthetic no-op.
|
||||
|
||||
# ---- Test 17 (negative): malformed (M33) line with --tag M33 ------------
|
||||
echo "=== negative: malformed (M33) line with --tag M33 ==="
|
||||
T="$SCRATCH/t17"; mk_repo "$T"
|
||||
commit_file "$T" "spec/foo.md" \
|
||||
$'# Spec\n\nA line that stays.\n\nA line that gets revoked.\n\n' >/dev/null
|
||||
BASE=$(git -C "$T" rev-parse HEAD)
|
||||
write_file_via_py "$T" "spec/foo.md" \
|
||||
$'# Spec\n\nA line that stays.\n\nRevokes prior text (M33) no quote\n\n'
|
||||
out=$(run_check "$T" "$BASE" --tag M33); rc=$?
|
||||
assert_exit "negative t17 malformed (M33) exits 1" 1 "$rc" "$out" ""
|
||||
assert_contains "negative t17 malformed (M33) under header" "malformed markers" "$out"
|
||||
|
||||
# ---- Test 19 (negative): free-prose line with no --tag --strict ---------
|
||||
echo "=== negative: free-prose with no --tag --strict ==="
|
||||
T="$SCRATCH/t19"; mk_repo "$T"
|
||||
commit_file "$T" "spec/foo.md" \
|
||||
$'# Spec\n\nA line that stays.\n\nshort text\n' >/dev/null
|
||||
BASE=$(git -C "$T" rev-parse HEAD)
|
||||
write_file_via_py "$T" "spec/foo.md" \
|
||||
$'# Spec\n\nA line that stays.\n\n> **Revokes prior text:** the short text\n'
|
||||
out=$(run_check "$T" "$BASE" --strict); rc=$?
|
||||
assert_exit "negative t19 free-prose strict exits 1" 1 "$rc" "$out" ""
|
||||
assert_contains "negative t19 free-prose strict under header" "malformed markers" "$out"
|
||||
|
||||
# ---- Negative-test verification (would each negative fail if disabled?) -
|
||||
# A "negative" test would PASS against a no-op script that always exits 0.
|
||||
@@ -317,7 +418,7 @@ sys.exit(0)
|
||||
PYEOF
|
||||
chmod +x "$SCRATCH/nop.py"
|
||||
|
||||
for case in t2 t3 t4 t6 t7; do
|
||||
for case in t2 t3 t4 t6 t7 t17 t19; do
|
||||
T="$SCRATCH/$case"
|
||||
BASE=$(git -C "$T" rev-parse HEAD 2>/dev/null || echo "HEAD")
|
||||
out_nop=$(python3 "$SCRATCH/nop.py" --base HEAD --repo-root "$T" --glob "spec/**/*.md" 2>&1); rc_nop=$?
|
||||
|
||||
Reference in New Issue
Block a user