chore: 문서 수정
This commit is contained in:
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -13,6 +13,50 @@ from tests.helpers import brief_dict, make_brief, make_sources
|
||||
|
||||
|
||||
class LintTests(unittest.TestCase):
|
||||
@staticmethod
|
||||
def _korean_experience_brief(document_type: str = "technical_blog") -> Brief:
|
||||
data = brief_dict(document_type)
|
||||
data["title"] = "기술적 선택을 설명하는 글"
|
||||
data["language"] = "ko-KR"
|
||||
data["reader_goal"] = "안전한 구현 방식을 선택합니다"
|
||||
data["core_message"] = "기술 선택은 문제와 비용을 함께 설명해야 합니다."
|
||||
data["constraints"]["style_profile"] = "auto"
|
||||
return Brief.from_dict(data)
|
||||
|
||||
@staticmethod
|
||||
def _experience_document(
|
||||
brief: Brief,
|
||||
*,
|
||||
marked_sections: set[int] | None = None,
|
||||
body_by_section: dict[int, str] | None = None,
|
||||
extra_by_section: dict[int, str] | None = None,
|
||||
) -> tuple[str, object]:
|
||||
sources = make_sources()
|
||||
outline = create_outline(brief, sources)
|
||||
marked_sections = (
|
||||
set(range(len(outline.sections)))
|
||||
if marked_sections is None
|
||||
else marked_sections
|
||||
)
|
||||
body_by_section = body_by_section or {}
|
||||
extra_by_section = extra_by_section or {}
|
||||
lines = [f"# {brief.title}", ""]
|
||||
for index, section in enumerate(outline.sections):
|
||||
lines.extend([f"## {section.title}", ""])
|
||||
if index in body_by_section:
|
||||
lines.append(body_by_section[index])
|
||||
else:
|
||||
subject = "저는 " if index in marked_sections else ""
|
||||
lines.append(
|
||||
f"{subject}이 절의 입력과 실제 동작을 확인했습니다. "
|
||||
"현재 구현은 명시된 경계를 사용합니다. "
|
||||
"대안과 비용, 검증 범위도 함께 설명합니다."
|
||||
)
|
||||
if index in extra_by_section:
|
||||
lines.extend(["", extra_by_section[index]])
|
||||
lines.append("")
|
||||
return "\n".join(lines), outline
|
||||
|
||||
def test_mock_document_meets_structural_gate(self) -> None:
|
||||
brief = make_brief()
|
||||
sources = make_sources()
|
||||
@@ -142,6 +186,121 @@ class LintTests(unittest.TestCase):
|
||||
self.assertIn("STYLE001", {issue.code for issue in report.issues})
|
||||
self.assertEqual(report.metrics["formulaic_ordinal_opening_count"], 3)
|
||||
|
||||
def test_korean_experience_contract_blocks_plain_form_endings(self) -> None:
|
||||
brief = self._korean_experience_brief()
|
||||
sources = make_sources()
|
||||
text, outline = self._experience_document(
|
||||
brief,
|
||||
extra_by_section={0: "현재 구현은 이 값을 사용한다."},
|
||||
)
|
||||
|
||||
report = lint_document(text, brief, outline, sources)
|
||||
|
||||
issues = [issue for issue in report.issues if issue.code == "STYLE002"]
|
||||
self.assertEqual(len(issues), 1)
|
||||
self.assertEqual(issues[0].severity, Severity.BLOCKER)
|
||||
self.assertEqual(report.metrics["plain_form_ending_count"], 1)
|
||||
|
||||
def test_korean_style_lint_exempts_non_reader_prose(self) -> None:
|
||||
brief = self._korean_experience_brief()
|
||||
sources = make_sources()
|
||||
text, outline = self._experience_document(
|
||||
brief,
|
||||
extra_by_section={
|
||||
0: (
|
||||
"### 현재 구현은 사용한다.\n\n"
|
||||
"> 원문 인용은 현재 구현을 사용한다.\n\n"
|
||||
"항목 | 설명\n"
|
||||
"--- | ---\n"
|
||||
"현재 값 | 현재 구현은 사용한다.\n\n"
|
||||
"\n\n"
|
||||
" 명령 출력은 현재 구현을 사용한다.\n\n"
|
||||
"`현재 구현은 사용한다.`\n\n"
|
||||
"직접 기록에는 “현재 구현은 사용한다.”라고 적혀 있습니다.\n\n"
|
||||
"```text\n"
|
||||
"현재 구현은 사용한다.\n"
|
||||
"```"
|
||||
)
|
||||
},
|
||||
)
|
||||
|
||||
report = lint_document(text, brief, outline, sources)
|
||||
|
||||
self.assertNotIn("STYLE002", {issue.code for issue in report.issues})
|
||||
self.assertEqual(report.metrics["plain_form_ending_count"], 0)
|
||||
|
||||
def test_korean_style_lint_requires_first_person_opening(self) -> None:
|
||||
brief = self._korean_experience_brief()
|
||||
sources = make_sources()
|
||||
text, outline = self._experience_document(
|
||||
brief,
|
||||
marked_sections=set(range(1, 8)),
|
||||
)
|
||||
|
||||
report = lint_document(text, brief, outline, sources)
|
||||
|
||||
issues = [issue for issue in report.issues if issue.code == "STYLE003"]
|
||||
self.assertEqual(len(issues), 1)
|
||||
self.assertEqual(issues[0].severity, Severity.BLOCKER)
|
||||
self.assertFalse(report.metrics["opening_has_first_person"])
|
||||
|
||||
def test_korean_style_lint_requires_major_section_coverage(self) -> None:
|
||||
brief = self._korean_experience_brief()
|
||||
sources = make_sources()
|
||||
text, outline = self._experience_document(
|
||||
brief,
|
||||
marked_sections={0},
|
||||
)
|
||||
|
||||
report = lint_document(text, brief, outline, sources)
|
||||
|
||||
self.assertIn("STYLE003", {issue.code for issue in report.issues})
|
||||
self.assertEqual(report.metrics["experience_section_count"], 8)
|
||||
self.assertEqual(report.metrics["marked_experience_section_count"], 1)
|
||||
self.assertEqual(report.metrics["experience_section_coverage"], 0.125)
|
||||
|
||||
def test_korean_style_lint_ignores_non_prose_sections(self) -> None:
|
||||
brief = self._korean_experience_brief()
|
||||
sources = make_sources()
|
||||
text, outline = self._experience_document(
|
||||
brief,
|
||||
marked_sections={0, 1},
|
||||
body_by_section={
|
||||
4: "```text\n현재 구현은 사용한다.\n```",
|
||||
5: "항목 | 값\n--- | ---\n구현 | 현재 값",
|
||||
6: "",
|
||||
7: "> 원문 인용만 남아 있습니다.",
|
||||
},
|
||||
)
|
||||
|
||||
report = lint_document(text, brief, outline, sources)
|
||||
|
||||
self.assertNotIn("STYLE003", {issue.code for issue in report.issues})
|
||||
self.assertEqual(report.metrics["experience_section_count"], 4)
|
||||
self.assertEqual(report.metrics["marked_experience_section_count"], 2)
|
||||
self.assertEqual(report.metrics["experience_section_coverage"], 0.5)
|
||||
|
||||
def test_korean_style_lint_accepts_compliant_experience_prose(self) -> None:
|
||||
brief = self._korean_experience_brief("readme")
|
||||
sources = make_sources()
|
||||
text, outline = self._experience_document(
|
||||
brief,
|
||||
marked_sections={0, 2, 4, 6},
|
||||
)
|
||||
|
||||
report = lint_document(text, brief, outline, sources)
|
||||
|
||||
codes = {issue.code for issue in report.issues}
|
||||
self.assertNotIn("STYLE002", codes)
|
||||
self.assertNotIn("STYLE003", codes)
|
||||
self.assertEqual(
|
||||
report.metrics["style_contract"],
|
||||
"korean_first_person_experience_v1",
|
||||
)
|
||||
self.assertTrue(report.metrics["opening_has_first_person"])
|
||||
self.assertEqual(report.metrics["first_person_marker_count"], 4)
|
||||
self.assertEqual(report.metrics["experience_section_coverage"], 0.5)
|
||||
|
||||
def test_numbered_procedure_is_not_formulaic_ordinal_prose(self) -> None:
|
||||
data = brief_dict()
|
||||
data["title"] = "기술적 선택을 설명하는 글"
|
||||
|
||||
Reference in New Issue
Block a user