You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

86 lines
2.6 KiB
Python

# backend/tests/test_nl_parser.py — 자연어 파서 골든 + 휴리스틱 폴백
import pytest
from app.automation.nl_parser import parse_rule
from tests._fake_llm import FakeLLMProvider
GOLDEN = [
(
"출장 전날엔 저녁 일정 비워줘",
"cal",
"출장 전날이 되면",
"18시 이후 일정이 있으면",
"다른 날로 옮기자고 제안",
),
(
"뉴스레터는 모아서 저녁에 보여줘",
"mail",
"뉴스레터 메일 도착",
None,
"받은편지함 건너뛰고 18:30 다이제스트로",
),
(
"운동을 3일 거르면 산책 잡아줘",
"life",
"운동 기록 3일 연속 없음",
"저녁에 빈 시간이 있으면",
"30분 산책 블록 제안",
),
]
@pytest.mark.parametrize("text,cat,trig,cond,action", GOLDEN)
def test_golden_examples(text, cat, trig, cond, action):
r = parse_rule(text)
assert r.matched and r.cat == cat
assert r.trigger == trig and r.cond == cond and r.action == action
assert r.model == "example"
def test_heuristic_fallback_classifies_cat():
# examples 미일치 → 휴리스틱(명시 provider 로 환경(Ollama 가동 여부) 무관 결정적)
r = parse_rule("영수증 들어오면 자동으로 정리해줘", provider=FakeLLMProvider())
assert r.matched and r.cat == "life" and r.model == "heuristic"
def test_empty_input_not_matched():
r = parse_rule("") # 빈 입력은 LLM 호출 없이 미매칭
assert not r.matched
class _FakeProvider:
name = "fake"
model = "fake-llm"
def health(self):
return {"reachable": True}
def generate_json(self, prompt, schema=None):
return {
"name": "회의록 자동화",
"cat": "cal",
"trigger": "회의 종료",
"cond": None,
"action": "요약 작성",
"confidence": 0.9,
}
def classify_capture(self, raw, context): # pragma: no cover
raise NotImplementedError
def test_llm_path_uses_provider_json():
r = parse_rule("회의 끝나면 요약해줘", provider=_FakeProvider())
assert r.matched and r.cat == "cal" and r.action == "요약 작성"
assert r.model == "llm:fake-llm"
class _BrokenProvider(_FakeProvider):
def generate_json(self, prompt, schema=None):
raise RuntimeError("llm down")
def test_llm_exception_falls_back_to_heuristic():
r = parse_rule("영수증 들어오면 정리해줘", provider=_BrokenProvider())
assert r.matched and r.model == "heuristic"