151 lines
6.4 KiB
Python
151 lines
6.4 KiB
Python
#!/usr/bin/env python3
|
|
"""wiki_rules.py 단위 테스트 (stdlib unittest)."""
|
|
import importlib.util
|
|
import sys
|
|
import tempfile
|
|
import unittest
|
|
from pathlib import Path
|
|
|
|
sys.path.insert(0, str(Path(__file__).resolve().parent))
|
|
_SPEC = importlib.util.spec_from_file_location(
|
|
"wiki_rules", str(Path(__file__).with_name("wiki_rules.py")))
|
|
wr = importlib.util.module_from_spec(_SPEC)
|
|
_SPEC.loader.exec_module(wr)
|
|
|
|
|
|
class TestProjectedContent(unittest.TestCase):
|
|
def test_write_full_content(self):
|
|
# Write 스타일: content 키가 있으면 그대로 반환
|
|
inp = {"content": "FULL BODY"}
|
|
self.assertEqual(wr.projected_content(None, inp), "FULL BODY")
|
|
|
|
def test_edit_applies_old_new(self):
|
|
with tempfile.TemporaryDirectory() as d:
|
|
p = Path(d) / "f.md"
|
|
p.write_text("alpha BETA gamma")
|
|
inp = {"old_string": "BETA", "new_string": "DELTA"}
|
|
self.assertEqual(wr.projected_content(p, inp), "alpha DELTA gamma")
|
|
|
|
|
|
class TestSeverityData(unittest.TestCase):
|
|
def test_critical_codes_are_links(self):
|
|
self.assertIn("BROKEN_LINK", wr.CRITICAL_CODES)
|
|
self.assertIn("BROKEN_MD_LINK", wr.CRITICAL_CODES)
|
|
self.assertNotIn("MISSING_SECTION", wr.CRITICAL_CODES)
|
|
|
|
def test_fixup_codes_are_completeness(self):
|
|
for c in ("MISSING_SECTION", "MISSING_FRONTMATTER",
|
|
"EMPTY_SELECTION_CRITERION", "DANGLING_ANCHOR",
|
|
"PROJECT_NO_DIAGRAM", "PROJECT_NO_BRANCH_TABLE",
|
|
"UNMAPPED_SOURCE_TYPE"):
|
|
self.assertIn(c, wr.FIXUP_CODES)
|
|
self.assertNotIn("BROKEN_LINK", wr.FIXUP_CODES)
|
|
|
|
def test_claim_requirements_cover_five_prefixes(self):
|
|
prefixes = {p for req in wr.CLAIM_REQUIREMENTS for p in req["prefix"]}
|
|
for p in ("raw/official-docs/", "raw/company-tech-blogs/",
|
|
"raw/branch-notes/", "wiki/concepts/"):
|
|
self.assertIn(p, prefixes)
|
|
|
|
|
|
STD_OK = "리포트...\n```wiki-verdict\nagent: branch-depth-auditor\nverdict: not-ready\nblocking: 2\nshould_fix: 1\nadvisory: 0\n```\n끝"
|
|
STD_CONTRADICT = "```wiki-verdict\nagent: branch-depth-auditor\nverdict: ready\nblocking: 2\nshould_fix: 0\nadvisory: 0\n```"
|
|
STD_BADVERDICT = "```wiki-verdict\nagent: x\nverdict: foo\nblocking: 0\nshould_fix: 0\nadvisory: 0\n```"
|
|
ADV_OK = "```wiki-verdict\nagent: wiki-adversarial-reviewer\nfinding: 4.1.1 action: KEEP\nfinding: 4.2.1 action: REJECT\n```"
|
|
ADV_BADACTION = "```wiki-verdict\nagent: wiki-adversarial-reviewer\nfinding: 4.1.1 action: NOPE\n```"
|
|
ADV_EMPTY = "```wiki-verdict\nagent: wiki-adversarial-reviewer\n```"
|
|
|
|
|
|
class TestVerdictBlock(unittest.TestCase):
|
|
def test_no_marker_returns_none(self):
|
|
parsed, errors = wr.validate_verdict_block("그냥 산문, 마커 없음")
|
|
self.assertIsNone(parsed)
|
|
self.assertEqual(errors, [])
|
|
|
|
def test_standard_valid(self):
|
|
parsed, errors = wr.validate_verdict_block(STD_OK)
|
|
self.assertEqual(errors, [])
|
|
self.assertEqual(parsed["agent"], "branch-depth-auditor")
|
|
self.assertEqual(parsed["kv"]["verdict"], "not-ready")
|
|
|
|
def test_standard_contradiction_flagged(self):
|
|
_, errors = wr.validate_verdict_block(STD_CONTRADICT)
|
|
self.assertTrue(any("blocking" in e for e in errors))
|
|
|
|
def test_standard_bad_verdict_flagged(self):
|
|
_, errors = wr.validate_verdict_block(STD_BADVERDICT)
|
|
self.assertTrue(any("verdict" in e for e in errors))
|
|
|
|
def test_adversarial_valid(self):
|
|
parsed, errors = wr.validate_verdict_block(ADV_OK)
|
|
self.assertEqual(errors, [])
|
|
self.assertEqual(len(parsed["findings"]), 2)
|
|
|
|
def test_adversarial_bad_action_flagged(self):
|
|
_, errors = wr.validate_verdict_block(ADV_BADACTION)
|
|
self.assertTrue(any("action" in e for e in errors))
|
|
|
|
def test_adversarial_empty_findings_flagged(self):
|
|
_, errors = wr.validate_verdict_block(ADV_EMPTY)
|
|
self.assertTrue(any("finding" in e for e in errors))
|
|
|
|
|
|
def _adv(*pairs):
|
|
lines = "\n".join(f"finding: {fid} action: {act}" for fid, act in pairs)
|
|
return f"```wiki-verdict\nagent: wiki-adversarial-reviewer\n{lines}\n```"
|
|
|
|
|
|
class TestTallyQuorum(unittest.TestCase):
|
|
def test_two_rejects_kill(self):
|
|
blocks = [_adv(("A", "REJECT")), _adv(("A", "REJECT")), _adv(("A", "KEEP"))]
|
|
self.assertEqual(wr.tally_quorum(blocks)["A"]["decision"], "KILL")
|
|
|
|
def test_unanimous_keep(self):
|
|
blocks = [_adv(("A", "KEEP")), _adv(("A", "KEEP")), _adv(("A", "KEEP"))]
|
|
self.assertEqual(wr.tally_quorum(blocks)["A"]["decision"], "KEEP")
|
|
|
|
def test_reject_plus_downgrade_is_downgrade(self):
|
|
blocks = [_adv(("A", "REJECT")), _adv(("A", "DOWNGRADE")), _adv(("A", "KEEP"))]
|
|
self.assertEqual(wr.tally_quorum(blocks)["A"]["decision"], "DOWNGRADE")
|
|
|
|
def test_abstain_not_pass(self):
|
|
# 한 블록만 A=KEEP, 나머지 둘은 A 누락(abstain) → 정족수 미달 → UNVERIFIED
|
|
blocks = [_adv(("A", "KEEP")), _adv(("B", "KEEP")), _adv(("C", "KEEP"))]
|
|
self.assertEqual(wr.tally_quorum(blocks)["A"]["decision"], "UNVERIFIED")
|
|
|
|
|
|
def _stats(found, processed, dropped, reason=None):
|
|
body = f"agent: coverage-auditor\nfound: {found}\nprocessed: {processed}\ndropped: {dropped}"
|
|
if reason is not None:
|
|
body += f"\ndropped_reason: {reason}"
|
|
return f"```wiki-stats\n{body}\n```"
|
|
|
|
|
|
class TestStatsBlock(unittest.TestCase):
|
|
def test_no_marker_returns_none(self):
|
|
parsed, errors = wr.validate_stats_block("산문, 마커 없음")
|
|
self.assertIsNone(parsed)
|
|
self.assertEqual(errors, [])
|
|
|
|
def test_balanced_ok(self):
|
|
parsed, errors = wr.validate_stats_block(_stats(12, 10, 2, "2 out-of-scope"))
|
|
self.assertEqual(errors, [])
|
|
self.assertEqual(parsed["agent"], "coverage-auditor")
|
|
|
|
def test_imbalance_flagged(self):
|
|
_, errors = wr.validate_stats_block(_stats(12, 10, 0))
|
|
self.assertTrue(any("불균형" in e for e in errors))
|
|
|
|
def test_dropped_without_reason_flagged(self):
|
|
_, errors = wr.validate_stats_block(_stats(12, 10, 2))
|
|
self.assertTrue(any("dropped_reason" in e for e in errors))
|
|
|
|
def test_non_integer_flagged(self):
|
|
block = "```wiki-stats\nagent: x\nfound: many\nprocessed: 1\ndropped: 0\n```"
|
|
_, errors = wr.validate_stats_block(block)
|
|
self.assertTrue(any("정수" in e for e in errors))
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main(verbosity=2)
|