Files
ArcaSuite/services/mcp-docugen/tests/unit/test_template_store.py
T
Adriano 9e80a20063 feat(mcp-docugen): Task 4-6 template_store, llm_client, generation_store
- TemplateStore: CRUD filesystem + asset dir, frontmatter YAML roundtrip,
  path traversal rejection
- OpenRouterClient: async httpx con retry backoff esponenziale (5xx, 429,
  timeout), no-retry su 4xx, parse usage/cost
- GenerationStore: SQLite aiosqlite con schema generations + ephemeral_assets,
  cleanup TTL, stats aggregate

Root pyproject aggiornato con respx + pytest-cov dev deps.
19 + 11 + 9 + 6 = 45 test totali, tutti passed.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-21 12:21:43 +02:00

110 lines
3.3 KiB
Python

import base64
import pytest
from mcp_docugen.models import TemplateFrontmatter
from mcp_docugen.template_store import (
InvalidFrontmatter,
TemplateAlreadyExists,
TemplateNotFound,
TemplateStore,
)
@pytest.fixture
def store(tmp_path):
return TemplateStore(base_dir=tmp_path)
def _fm(name="fattura"):
return TemplateFrontmatter(
name=name,
description="Fattura commerciale",
required_variables=[{"name": "cliente", "type": "string"}],
)
async def test_create_and_get(store):
fm = _fm()
await store.create(name="fattura", frontmatter=fm, body="# Body {{cliente}}")
got = await store.get("fattura")
assert got.frontmatter.name == "fattura"
assert "# Body {{cliente}}" in got.body
async def test_create_duplicate_raises(store):
fm = _fm()
await store.create(name="fattura", frontmatter=fm, body="body")
with pytest.raises(TemplateAlreadyExists):
await store.create(name="fattura", frontmatter=fm, body="body")
async def test_get_missing_raises(store):
with pytest.raises(TemplateNotFound):
await store.get("nope")
async def test_list_returns_summaries(store):
await store.create(name="a", frontmatter=_fm("a"), body="b")
await store.create(name="b", frontmatter=_fm("b"), body="b")
result = await store.list()
names = sorted(t.name for t in result)
assert names == ["a", "b"]
async def test_update_overwrites(store):
await store.create(name="f", frontmatter=_fm("f"), body="old")
new_fm = TemplateFrontmatter(name="f", description="new description")
await store.update(name="f", frontmatter=new_fm, body="new body")
got = await store.get("f")
assert got.body == "new body"
assert got.frontmatter.description == "new description"
async def test_update_missing_raises(store):
with pytest.raises(TemplateNotFound):
await store.update(name="nope", frontmatter=_fm("nope"), body="x")
async def test_delete_removes(store):
await store.create(name="f", frontmatter=_fm("f"), body="b")
await store.delete("f")
with pytest.raises(TemplateNotFound):
await store.get("f")
async def test_delete_missing_raises(store):
with pytest.raises(TemplateNotFound):
await store.delete("nope")
async def test_assets_are_saved_and_listed(store):
png_bytes = b"\x89PNG\r\n\x1a\n"
assets = [
{
"filename": "logo.png",
"data_b64": base64.b64encode(png_bytes).decode(),
"mime": "image/png",
}
]
await store.create(name="f", frontmatter=_fm("f"), body="b", assets=assets)
got = await store.get("f")
asset_names = [a.filename for a in got.assets]
assert "logo.png" in asset_names
content = await store.read_asset("f", "logo.png")
assert content == png_bytes
async def test_asset_filename_rejects_path_traversal(store):
assets = [{"filename": "../evil.png", "data_b64": "aGk=", "mime": "image/png"}]
with pytest.raises(ValueError):
await store.create(name="f", frontmatter=_fm("f"), body="b", assets=assets)
async def test_frontmatter_parsing_rejects_malformed_yaml(store, tmp_path):
template_dir = tmp_path / "broken"
template_dir.mkdir()
(template_dir / "template.md").write_text("---\nname: :::broken\n---\nbody")
with pytest.raises(InvalidFrontmatter):
await store.get("broken")