Files
Aislo/resources/tester/test_b01_knowledge_chat.py
T

123 lines
4.5 KiB
Python

import asyncio
import pytest
from fastapi import HTTPException
from B01_Dashboard import B01_Dashboard_Knowledge_Chat as chat
def test_greeting_answers_without_search_or_api_key(monkeypatch):
monkeypatch.delenv("GEMINI_API_KEY", raising=False)
monkeypatch.setattr(
chat, "search", lambda _question: (_ for _ in ()).throw(AssertionError("searched"))
)
result = asyncio.run(chat.ask_knowledge_chat(chat.ChatRequest(question="안녕!"), {}))
assert result == {
"answer": "안녕하세요! 법령이나 지침에 대해 궁금한 점을 물어보세요.",
"sources": [],
}
def test_natural_question_finds_term_without_particle(monkeypatch):
monkeypatch.setattr(
chat,
"_sections",
lambda: [("original/원가계산/품셈.md", "# 일위대가\n공사 한 단위에 필요한 비용을 계산")],
)
assert chat.search("일위대가가 뭐야")[0]["path"] == "original/원가계산/품셈.md"
def test_missing_api_key_is_reported_before_search(monkeypatch):
monkeypatch.delenv("GEMINI_API_KEY", raising=False)
monkeypatch.setattr(chat, "search", lambda _question: [])
with pytest.raises(HTTPException) as error:
asyncio.run(chat.ask_knowledge_chat(chat.ChatRequest(question="일위대가가 뭐야"), {}))
assert error.value.status_code == 503
def test_busy_gemini_is_retried_and_reported(monkeypatch):
monkeypatch.setenv("GEMINI_API_KEY", "test-key")
monkeypatch.setattr(chat, "search", lambda _question: [])
calls = []
class Client:
def __init__(self, timeout):
pass
async def __aenter__(self):
return self
async def __aexit__(self, *_args):
pass
async def post(self, *_args, **_kwargs):
calls.append(1)
return type("Response", (), {"status_code": 503})()
async def no_wait(_seconds):
pass
monkeypatch.setattr(chat.httpx, "AsyncClient", Client)
monkeypatch.setattr(chat.asyncio, "sleep", no_wait)
with pytest.raises(HTTPException) as error:
asyncio.run(chat.ask_knowledge_chat(chat.ChatRequest(question="일위대가가 뭐야"), {}))
assert len(calls) == 3
assert "요청이 몰려" in error.value.detail
def test_chat_searches_all_knowledge_markdown_except_excluded_folders(monkeypatch, tmp_path):
public = tmp_path / "original" / "법률"
guide = tmp_path / "technical_info"
private = tmp_path / "original" / "실무문서"
stmate = tmp_path / "original" / "원가계산" / "STmate"
dasan = tmp_path / "original" / "원가계산" / "다산소프트"
public.mkdir(parents=True)
guide.mkdir()
private.mkdir()
stmate.mkdir(parents=True)
dasan.mkdir()
(public / "현행.md").write_text("# 임도 기준\n" + "임도 설치 기준 내용 " * 10, encoding="utf-8")
(guide / "_검토.md").write_text("# 임도 기준\n기술정보 안내 " * 10, encoding="utf-8")
(private / "사례.md").write_text("# 임도 기준\n비공개 사례 " * 10, encoding="utf-8")
(stmate / "자료.md").write_text("# 임도 기준\nSTmate 자료 " * 10, encoding="utf-8")
(dasan / "자료.md").write_text("# 임도 기준\n다산소프트 자료 " * 10, encoding="utf-8")
monkeypatch.setattr(chat, "KNOWLEDGE", tmp_path)
monkeypatch.setenv("GEMINI_API_KEY", "test-key")
chat._sections.cache_clear()
sent = {}
class Response:
status_code = 200
def raise_for_status(self):
pass
def json(self):
return {"candidates": [{"content": {"parts": [{"text": "설명 [1]"}]}}]}
class Client:
def __init__(self, timeout):
pass
async def __aenter__(self):
return self
async def __aexit__(self, *_args):
pass
async def post(self, url, *, headers, json):
sent["prompt"] = json["contents"][0]["parts"][0]["text"]
return Response()
monkeypatch.setattr(chat.httpx, "AsyncClient", Client)
result = asyncio.run(chat.ask_knowledge_chat(chat.ChatRequest(question="임도 기준"), {}))
assert result["answer"] == "설명 [1]"
paths = {source["path"] for source in result["sources"]}
assert paths == {"original/법률/현행.md"}
assert "실무문서" not in sent["prompt"]
assert "비공개 사례" not in sent["prompt"]
assert "STmate" not in sent["prompt"]
assert "다산소프트" not in sent["prompt"]
assert "기술정보 안내" not in sent["prompt"]
chat._sections.cache_clear()