"""Phase 4-A 후속 — explanation_md 길이 cap 단위 테스트. worker 의 _cap_explanation_md 헬퍼가 plan 의도대로 동작하는지 검증. """ from __future__ import annotations import sys from pathlib import Path ROOT = Path(__file__).resolve().parent.parent sys.path.insert(0, str(ROOT / "app")) from workers.study_explanation_worker import ( # noqa: E402 EXPLANATION_MAX_CHARS, _cap_explanation_md, ) def test_cap_short_text_passthrough(): """1200자 이하는 변경 없이 통과.""" text = "짧은 풀이." * 50 # ~300자 assert _cap_explanation_md(text) == text def test_cap_exactly_at_limit(): """딱 1200자는 그대로.""" text = "ㄱ" * EXPLANATION_MAX_CHARS assert len(_cap_explanation_md(text)) == EXPLANATION_MAX_CHARS def test_cap_long_text_with_paragraph_boundary(): """긴 텍스트 + 마지막 200자 안에 \\n\\n 있으면 거기서 자르기 + …""" head = "정답 풀이 본문 그리고 추가 설명. " * 100 # 충분히 긴 head boundary_pos = 1100 # cap (1200) 의 마지막 200 안 text = head[:boundary_pos] + "\n\n" + ("추가 단락 본문이 더 들어갑니다. " * 50) assert len(text) > EXPLANATION_MAX_CHARS capped = _cap_explanation_md(text) assert len(capped) <= EXPLANATION_MAX_CHARS + 1 # + "…" assert capped.endswith("…") def test_cap_long_text_with_sentence_boundary(): """\\n\\n 이 없으면 마침표 / "다." / "요." 에서 자르기.""" parts = ["이 문장은 약간 길게 만든 것입니다. " for _ in range(200)] text = "".join(parts) assert len(text) > EXPLANATION_MAX_CHARS capped = _cap_explanation_md(text) assert len(capped) <= EXPLANATION_MAX_CHARS + 1 assert capped.endswith("…") # 자른 위치가 마침표/공백 직후여야 — 단어 중간 X body_no_ellipsis = capped[:-1] assert body_no_ellipsis[-1] in (".", " ", "다", "요"), \ f"last char before ellipsis: {body_no_ellipsis[-1]!r}" def test_cap_no_boundary_falls_back_to_plain_cut(): """경계 마커 (\\n, ., 다., 요.) 가 전혀 없으면 단순 자르기.""" text = "ㅁ" * 2000 # 한글 한 글자만 반복, 경계 0 capped = _cap_explanation_md(text) assert capped == "ㅁ" * EXPLANATION_MAX_CHARS + "…" assert len(capped) == EXPLANATION_MAX_CHARS + 1 def test_cap_empty_input(): assert _cap_explanation_md("") == "" assert _cap_explanation_md(None) is None if __name__ == "__main__": test_cap_short_text_passthrough() test_cap_exactly_at_limit() test_cap_long_text_with_paragraph_boundary() test_cap_long_text_with_sentence_boundary() test_cap_no_boundary_falls_back_to_plain_cut() test_cap_empty_input() print("OK")