Files
caveman/tests/test_validate_inline.py
Julius BrusseeandClaude Fable 5 dcd51f16fe fix(compress): stop Windows data loss — UTF-8 + atomic writes (#652, #655, #686)
Every read/write resolved to the locale codec (cp1252/cp949 on Windows):
non-ASCII files were silently mojibake'd, and because Path.write_text
truncates before encoding, a UnicodeEncodeError left the target at 0 bytes.
The backup readback check couldn't catch it — it read back with the same
wrong codec.

- encoding=utf-8 pinned on every I/O call site (compress, validate,
  detect, benchmark); validate now decodes strict — it's the fidelity gate
- write_text_atomic: encode first, temp file in same dir, fsync, preserve
  permissions, os.replace; temp unlinked on any failure
- fix-retry pass gains the same empty-output guard as the first pass
- fix-retry preamble leak (#588): output must start at the original's
  structural anchor (frontmatter/heading) or the attempt is rejected
- primary-write failure now prints the backup path — users hitting the
  crash had no idea a backup existed
- extract_inline_codes: strip fences via the CommonMark-aware extractor;
  the old column-0 regex leaked indented fences into inline-code pairing,
  causing false validation failures (extracted from PR #619's diagnosis)
- SKILL.md/README/SECURITY corrected: backups live in the out-of-tree data
  dir (#420), not beside the source file

Supersedes PRs #683 #678 #626 #534 and the fence fix from #619 with a
local implementation. 58 python tests.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016ySX6TBWZuvFze4ajf7Hpf
2026-07-21 02:00:59 +02:00

98 lines
3.3 KiB
Python

import sys
import tempfile
import unittest
from pathlib import Path
REPO_ROOT = Path(__file__).resolve().parent.parent
sys.path.insert(0, str(REPO_ROOT / "skills" / "caveman-compress"))
from scripts.validate import ( # noqa: E402
ValidationResult,
extract_inline_codes,
validate,
validate_inline_codes,
)
class TestExtractInlineCodes(unittest.TestCase):
def test_fenced_blocks_excluded(self):
text = "```\ncode here\n```\n`inline code`"
result = extract_inline_codes(text)
self.assertEqual(result, ["inline code"])
def test_inline_only(self):
text = "Use `rm -rf /` to delete everything"
result = extract_inline_codes(text)
self.assertEqual(result, ["rm -rf /"])
def test_mixed_content(self):
text = """
Some text with `inline1` and `inline2`.
```
code block
```
More text with `inline3`.
"""
result = extract_inline_codes(text)
self.assertEqual(set(result), {"inline1", "inline2", "inline3"})
def test_empty(self):
self.assertEqual(extract_inline_codes("no backticks here"), [])
def test_indented_fence_backtick_not_leaked_as_inline(self):
# A fence indented 1-3 spaces is valid CommonMark and already handled
# by extract_code_blocks/FENCE_OPEN_REGEX. The old column-0-anchored
# strip regex missed it, so a backtick inside the indented fence body
# leaked out and got paired with the next real inline span (issue
# from PR #619 review). Only the real trailing inline span should
# come back.
text = " ```\n `weird`\n ```\nReal `inline` span here."
result = extract_inline_codes(text)
self.assertEqual(result, ["inline"])
class TestValidateInlineCodes(unittest.TestCase):
def test_match(self):
result = ValidationResult()
validate_inline_codes("use `cmd` here", "use `cmd` here", result)
self.assertTrue(result.is_valid)
def test_lost(self):
result = ValidationResult()
validate_inline_codes("use `cmd` here", "use here", result)
self.assertFalse(result.is_valid)
self.assertIn("Inline code lost", result.errors[0])
def test_added(self):
result = ValidationResult()
validate_inline_codes("use here", "use `new` here", result)
self.assertTrue(result.is_valid)
self.assertIn("Inline code added", result.warnings[0])
def test_empty_orig(self):
result = ValidationResult()
validate_inline_codes("no codes", "use `new` here", result)
self.assertTrue(result.is_valid)
def test_both_empty(self):
result = ValidationResult()
validate_inline_codes("plain text", "also plain", result)
self.assertTrue(result.is_valid)
class TestValidateIntegration(unittest.TestCase):
def test_validate_inline_codes_wired(self):
with tempfile.TemporaryDirectory() as tmp:
orig = Path(tmp) / "original.md"
comp = Path(tmp) / "compressed.md"
orig.write_text("Run `rm -rf /` to delete")
comp.write_text("Run to delete")
result = validate(orig, comp)
self.assertFalse(result.is_valid)
self.assertTrue(any("Inline code lost" in e for e in result.errors))
if __name__ == "__main__":
unittest.main()