feat(mcp-docugen): templates seed versionati + auto-seed all'avvio
- templates_seed/{offerta,report-analisi}/template.md: template Tielogic
ufficiali versionati come sorgente di verità nel repo
- template_seed.py: copia idempotente seed→volume al boot, mai
sovrascrive template esistenti (preserva edit fatti via MCP runtime)
- config.py: nuova Settings.templates_seed_dir
(default /app/services/mcp-docugen/templates_seed)
- main.py: chiamata seed_templates() in build_app dopo TemplateStore init
- 4 nuovi test unit (idempotenza, skip se seed_dir mancante,
no-op su entry non valide). 72 test verde totali
Workflow: edit del template nel repo → rebuild image → al primo boot
il volume vuoto riceve i template; se il template esiste già nel
volume (es. modificato dall'utente via tool MCP) viene preservato.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,78 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
from mcp_docugen.template_seed import seed_templates
|
||||
|
||||
VALID_FRONTMATTER = (
|
||||
"---\n"
|
||||
"name: {name}\n"
|
||||
"description: test seed\n"
|
||||
"required_variables: []\n"
|
||||
"---\n"
|
||||
"body of {name}\n"
|
||||
)
|
||||
|
||||
|
||||
def _make_seed_template(seed_dir: Path, name: str, body: str | None = None) -> None:
|
||||
tdir = seed_dir / name
|
||||
(tdir / "assets").mkdir(parents=True, exist_ok=True)
|
||||
(tdir / "template.md").write_text(
|
||||
body if body is not None else VALID_FRONTMATTER.format(name=name)
|
||||
)
|
||||
|
||||
|
||||
def test_seed_copies_missing_templates(tmp_path: Path) -> None:
|
||||
seed_dir = tmp_path / "seed"
|
||||
target_dir = tmp_path / "target"
|
||||
_make_seed_template(seed_dir, "alpha")
|
||||
_make_seed_template(seed_dir, "beta")
|
||||
|
||||
seeded = seed_templates(seed_dir, target_dir)
|
||||
|
||||
assert sorted(seeded) == ["alpha", "beta"]
|
||||
assert (target_dir / "alpha" / "template.md").is_file()
|
||||
assert (target_dir / "beta" / "template.md").is_file()
|
||||
assert (target_dir / "alpha" / "assets").is_dir()
|
||||
|
||||
|
||||
def test_seed_is_idempotent_and_does_not_overwrite(tmp_path: Path) -> None:
|
||||
seed_dir = tmp_path / "seed"
|
||||
target_dir = tmp_path / "target"
|
||||
_make_seed_template(seed_dir, "alpha")
|
||||
|
||||
seed_templates(seed_dir, target_dir)
|
||||
|
||||
user_edit = (
|
||||
"---\nname: alpha\ndescription: edited by user\nrequired_variables: []\n---\n"
|
||||
"user content\n"
|
||||
)
|
||||
(target_dir / "alpha" / "template.md").write_text(user_edit)
|
||||
|
||||
seeded = seed_templates(seed_dir, target_dir)
|
||||
|
||||
assert seeded == []
|
||||
assert (target_dir / "alpha" / "template.md").read_text() == user_edit
|
||||
|
||||
|
||||
def test_seed_skips_when_seed_dir_missing(tmp_path: Path) -> None:
|
||||
target_dir = tmp_path / "target"
|
||||
|
||||
seeded = seed_templates(tmp_path / "missing", target_dir)
|
||||
|
||||
assert seeded == []
|
||||
assert not target_dir.exists() or list(target_dir.iterdir()) == []
|
||||
|
||||
|
||||
def test_seed_ignores_non_directory_entries_and_dirs_without_template(
|
||||
tmp_path: Path,
|
||||
) -> None:
|
||||
seed_dir = tmp_path / "seed"
|
||||
target_dir = tmp_path / "target"
|
||||
seed_dir.mkdir()
|
||||
(seed_dir / "stray-file.md").write_text("not a template dir")
|
||||
(seed_dir / "no-template-md").mkdir()
|
||||
|
||||
seeded = seed_templates(seed_dir, target_dir)
|
||||
|
||||
assert seeded == []
|
||||
Reference in New Issue
Block a user