"""윤문 전후에 보호 구간이 그대로인지 보는 검사기. 정상 윤문은 통과하고, 수치·코드·인용·URL 을 건드린 편집은 걸린다. 유보 표현이 줄어든 것은 걸러 내되 판정하지 않는다 — 판단은 근거를 읽는 검토가 한다. """ import importlib.util import os import unittest ROOT = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) F = os.path.join(ROOT, "scripts", "tests", "fixtures", "preservation") _spec = importlib.util.spec_from_file_location( "check_preservation", os.path.join(ROOT, "scripts", "check-preservation.py")) cp = importlib.util.module_from_spec(_spec) _spec.loader.exec_module(cp) def _read(name): return open(os.path.join(F, name), encoding="utf-8").read() class PreservationTest(unittest.TestCase): def setUp(self): self.before = _read("before.md") def test_an_ordinary_rewrite_passes(self): res = cp.compare(self.before, _read("after-ok.md")) self.assertEqual([], res["findings"]) self.assertEqual([], res["hedgesDropped"]) def test_a_number_changed_in_prose_is_caught(self): res = cp.compare(self.before, _read("after-tampered.md")) values = {f["value"] for f in res["findings"] if f["kind"] == "수치"} self.assertIn("14ms", values) self.assertIn("4ms", values) def test_a_number_changed_inside_a_code_block_is_caught(self): res = cp.compare(self.before, _read("after-tampered.md")) blocks = {f["value"] for f in res["findings"] if f["kind"] == "코드블록"} self.assertTrue(any("exit=1" in b for b in blocks)) self.assertTrue(any("exit=0" in b for b in blocks)) def test_a_changed_direct_quotation_is_caught(self): res = cp.compare(self.before, _read("after-tampered.md")) kinds = {f["kind"] for f in res["findings"]} self.assertIn("직접인용", kinds) def test_a_changed_url_is_caught(self): res = cp.compare(self.before, _read("after-tampered.md")) kinds = {f["kind"] for f in res["findings"]} self.assertIn("URL", kinds) def test_dropped_hedges_are_surfaced_without_a_verdict(self): """유보가 줄면 낸다. 옳은지 그른지는 말하지 않는다.""" res = cp.compare(self.before, _read("after-tampered.md")) dropped = {h["word"] for h in res["hedgesDropped"]} self.assertIn("확인하지 못했다", dropped) self.assertIn("로컬", dropped) self.assertEqual(0, res["hedgeTotalAfter"]) def test_adding_a_hedge_is_not_reported(self): """유보를 더하는 것은 이 규범에서 안전한 쪽이다.""" after = self.before.replace("14ms 였다", "14ms 였다. 다만 한 번만 쟀다") res = cp.compare(self.before, after) self.assertEqual([], res["hedgesDropped"]) def test_a_missing_file_is_not_reported_as_clean(self): import subprocess p = subprocess.run( ["python3", os.path.join(ROOT, "scripts", "check-preservation.py"), os.path.join(F, "before.md"), os.path.join(F, "nope.md")], cwd=ROOT, capture_output=True, text=True) self.assertEqual(2, p.returncode) if __name__ == "__main__": unittest.main()