"""스토리 요약 모듈 단위테스트 (계획서 과제2 ②). 추출적 요약은 외부 의존(LLM/정답셋) 없이 동작해야 한다. """ from __future__ import annotations from app.core.config import Settings from app.engine.summarizer import ( Summarizer, extractive_summary, split_sentences, ) _DOC = ( "홍길동은 조선시대 의적이었다. 그는 활빈당을 만들어 탐관오리의 재물을 빼앗았다. " "빼앗은 재물은 가난한 백성들에게 나누어 주었다. 홍길동은 둔갑술과 도술에 능했다. " "조정에서는 그를 잡으려 했으나 번번이 실패했다. 결국 홍길동은 율도국으로 떠나 새 나라를 세웠다. " "오늘 날씨는 맑고 화창하다. 점심으로 김밥을 먹었다." ) def test_split_sentences(): sents = split_sentences("첫 문장이다. 둘째 문장이다! 셋째 문장인가?") assert len(sents) == 3 def test_extractive_summary_shortens(): result = extractive_summary(_DOC, ratio=0.4) assert result.num_sentences_in == 8 assert 0 < result.num_sentences_out < result.num_sentences_in assert result.mode == "extractive" assert result.final == result.extractive def test_extractive_preserves_source_order(): result = extractive_summary(_DOC, ratio=0.5) # 선택된 인덱스는 원문 등장 순서여야 함 (가독성) assert result.selected_indices == sorted(result.selected_indices) def test_extractive_picks_central_over_offtopic(): """본문 핵심(홍길동 서사)이 무관한 문장(날씨/점심)보다 우선 선택.""" result = extractive_summary(_DOC, ratio=0.4) text = result.extractive assert "홍길동" in text # 마지막 두 무관 문장은 덜 선택되어야 함 assert not ("김밥" in text and "날씨" in text) def test_max_sentences_cap(): result = extractive_summary(_DOC, ratio=1.0, max_sentences=2) assert result.num_sentences_out <= 2 def test_empty_and_short(): assert extractive_summary("").final == "" one = extractive_summary("한 문장만 있다.") assert one.final == "한 문장만 있다." def test_summarizer_without_llm_is_extractive(): """LLM 비활성 시 통합 요청도 추출적 결과를 반환 (외부 의존 0 폴백).""" settings = Settings(use_llm_extractor=False, openai_api_key="", use_kosimcse=False) result = Summarizer(settings).summarize(_DOC, ratio=0.4, use_abstractive=True) assert result.mode == "extractive" assert result.abstractive is None assert result.final