legawa / tests /test_cli.py
pebaryan
Harden citation pipeline + add comprehensive test suite
b4a776c
Raw
History Blame Contribute Delete
5.97 kB
from __future__ import annotations
import sys
import unittest
from pathlib import Path
from unittest.mock import patch
from typer.testing import CliRunner
SRC = Path(__file__).resolve().parents[1] / "src"
if str(SRC) not in sys.path:
sys.path.insert(0, str(SRC))
from legawa.cli import app
from legawa.config import LLMConfig, Settings
class FakeLLM:
def __init__(self, response: str):
self.response = response
self.calls: list[tuple[list[dict], dict]] = []
def chat(self, messages, **kwargs):
self.calls.append((messages, kwargs))
return self.response
class FakePool:
def __init__(self, settings: Settings):
self.settings = settings
self.big = FakeLLM("OK")
self.small = FakeLLM("OK")
class FakePasalClient:
def __init__(self, settings: Settings):
self.settings = settings
self.closed = False
self.calls: list[dict] = []
def close(self):
self.closed = True
def __enter__(self):
return self
def __exit__(self, *_):
self.close()
def search(self, q=None, limit=1, **kwargs):
if q is None:
q = kwargs.get("q")
self.calls.append({"q": q, "limit": limit, **kwargs})
if q in {"UU 31/1999", "UU 20/2001", "Perpres 16/2018", "Perpres 12/2021"}:
kind_map = {
"UU 31/1999": ("uu", "1999", "31", "Undang-Undang Nomor 31 Tahun 1999"),
"UU 20/2001": ("uu", "2001", "20", "Undang-Undang Nomor 20 Tahun 2001"),
"Perpres 16/2018": ("perpres", "2018", "16", "Peraturan Presiden Nomor 16 Tahun 2018"),
"Perpres 12/2021": ("perpres", "2021", "12", "Peraturan Presiden Nomor 12 Tahun 2021"),
}
kind, year, number, title = kind_map[q]
return {
"results": [
{
"title": title,
"frbr_uri": f"akn/id/act/{kind}/{year}/{number}",
"status": "berlaku",
}
]
}
return {"results": [{"title": "UU test", "frbr_uri": "akn/id/act/uu/2003/13", "status": "berlaku"}]}
class FakeCachingPasalClient:
def __init__(self, raw):
self.raw = raw
self.closed = False
def stats(self):
return {"entries": 2, "bytes": 128, "session_hits": 1, "session_misses": 0}
def purge_expired(self):
return 3
def close(self):
self.closed = True
def make_settings() -> Settings:
cfg = LLMConfig(base_url="http://example.invalid", api_key="x", model="qwen3", temperature=0.3, max_tokens=4096)
return Settings(
pasal_token="token",
pasal_base_url="http://pasal.invalid",
big=cfg,
small=cfg,
run_date="2026-04-30",
corpus_watermark="2026-04-30",
strict_citations=True,
)
class CliSmokeTests(unittest.TestCase):
def setUp(self) -> None:
self.runner = CliRunner()
self.settings = make_settings()
def test_health_smoke(self) -> None:
with (
patch("legawa.cli.load_settings", return_value=self.settings),
patch("legawa.cli.LLMPool", return_value=FakePool(self.settings)),
patch("legawa.cli.PasalClient", side_effect=lambda settings: FakePasalClient(settings)),
):
result = self.runner.invoke(app, ["health"])
self.assertEqual(result.exit_code, 0, result.output)
self.assertIn("OK big", result.output)
self.assertIn("OK small", result.output)
self.assertIn("OK pasal.id", result.output)
def test_cache_commands_smoke(self) -> None:
with (
patch("legawa.cli.load_settings", return_value=self.settings),
patch("legawa.cli.LLMPool", return_value=FakePool(self.settings)),
patch("legawa.cli.PasalClient", side_effect=lambda settings: FakePasalClient(settings)),
patch("legawa.cli.CachingPasalClient", side_effect=lambda raw: FakeCachingPasalClient(raw)),
):
stats = self.runner.invoke(app, ["cache", "stats"])
purge = self.runner.invoke(app, ["cache", "purge"])
self.assertEqual(stats.exit_code, 0, stats.output)
self.assertIn("'entries': 2", stats.output)
self.assertEqual(purge.exit_code, 0, purge.output)
self.assertIn("purged 3 expired entries", purge.output)
def test_draft_cli_it_audit_perspective(self) -> None:
source = (Path(__file__).resolve().parents[1] / "tests" / "fixtures" / "ibam-it-audit-perspective.txt").read_text(
encoding="utf-8"
)
fake_pool = FakePool(self.settings)
fake_pool.big.response = (
"# Memo Teknis\n"
"Kami menilai serial number, audit trail, CDM license history, firmware, dan MDM policy.\n"
"Rujukan: UU 31/1999, UU 20/2001, Perpres 16/2018."
)
fake_pasal = FakePasalClient(self.settings)
def bootstrap():
return fake_pool, fake_pasal
with patch("legawa.cli._bootstrap", side_effect=bootstrap):
result = self.runner.invoke(
app,
[
"draft",
"memo_kebijakan",
"audit teknis dan tata kelola perangkat digital pada Kasus Ibam",
"--instruksi",
source,
"--no-research",
],
)
self.assertEqual(result.exit_code, 0, result.output)
self.assertIn("Memo Teknis", result.output)
self.assertIn("UU 31/1999", result.output)
self.assertNotIn("Perguruan Tinggi", result.output)
system_prompt = fake_pool.big.calls[0][0][0]["content"]
self.assertIn("forensik digital", system_prompt)
self.assertIn("Tanggal penyusunan: 2026-04-30", system_prompt)
if __name__ == "__main__":
unittest.main()