feat(repo): WelcomeTemplateRepository singleton CRUD
This commit is contained in:
parent
33e7107637
commit
19f1795350
2 changed files with 93 additions and 0 deletions
53
src/repositories/welcome_template.py
Normal file
53
src/repositories/welcome_template.py
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
"""Repository for the per-instance welcome-prompt override (singleton row)."""
|
||||
|
||||
from datetime import datetime, timezone
|
||||
from typing import Any, Optional
|
||||
|
||||
import duckdb
|
||||
|
||||
|
||||
class WelcomeTemplateRepository:
|
||||
def __init__(self, conn: duckdb.DuckDBPyConnection):
|
||||
self.conn = conn
|
||||
|
||||
def get(self) -> dict[str, Any]:
|
||||
"""Return the singleton row. Always exists post-migration; content
|
||||
is None when no override is set (= use shipped default)."""
|
||||
row = self.conn.execute(
|
||||
"SELECT id, content, updated_at, updated_by FROM welcome_template WHERE id = 1"
|
||||
).fetchone()
|
||||
if row is None:
|
||||
# Defensive: re-seed if a previous admin manually deleted it.
|
||||
self.conn.execute(
|
||||
"INSERT INTO welcome_template (id, content) VALUES (1, NULL) "
|
||||
"ON CONFLICT (id) DO NOTHING"
|
||||
)
|
||||
return {"id": 1, "content": None, "updated_at": None, "updated_by": None}
|
||||
return {
|
||||
"id": row[0],
|
||||
"content": row[1],
|
||||
"updated_at": row[2],
|
||||
"updated_by": row[3],
|
||||
}
|
||||
|
||||
def set(self, content: str, *, updated_by: str) -> None:
|
||||
now = datetime.now(timezone.utc)
|
||||
self.conn.execute(
|
||||
"""INSERT INTO welcome_template (id, content, updated_at, updated_by)
|
||||
VALUES (1, ?, ?, ?)
|
||||
ON CONFLICT (id) DO UPDATE SET
|
||||
content = excluded.content,
|
||||
updated_at = excluded.updated_at,
|
||||
updated_by = excluded.updated_by""",
|
||||
[content, now, updated_by],
|
||||
)
|
||||
|
||||
def reset(self, *, updated_by: str) -> None:
|
||||
"""Clear override; renderer falls back to shipped default."""
|
||||
now = datetime.now(timezone.utc)
|
||||
self.conn.execute(
|
||||
"""UPDATE welcome_template
|
||||
SET content = NULL, updated_at = ?, updated_by = ?
|
||||
WHERE id = 1""",
|
||||
[now, updated_by],
|
||||
)
|
||||
40
tests/test_welcome_template_repo.py
Normal file
40
tests/test_welcome_template_repo.py
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
"""Unit tests for WelcomeTemplateRepository."""
|
||||
|
||||
import duckdb
|
||||
import pytest
|
||||
|
||||
from src.db import _ensure_schema
|
||||
from src.repositories.welcome_template import WelcomeTemplateRepository
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def conn(tmp_path):
|
||||
db_path = tmp_path / "system.duckdb"
|
||||
c = duckdb.connect(str(db_path))
|
||||
_ensure_schema(c)
|
||||
yield c
|
||||
c.close()
|
||||
|
||||
|
||||
def test_get_returns_none_on_fresh_install(conn):
|
||||
repo = WelcomeTemplateRepository(conn)
|
||||
row = repo.get()
|
||||
assert row is not None
|
||||
assert row["content"] is None # default sentinel
|
||||
|
||||
|
||||
def test_set_stores_content(conn):
|
||||
repo = WelcomeTemplateRepository(conn)
|
||||
repo.set("Hello {{ instance.name }}", updated_by="admin@example.com")
|
||||
row = repo.get()
|
||||
assert row["content"] == "Hello {{ instance.name }}"
|
||||
assert row["updated_by"] == "admin@example.com"
|
||||
assert row["updated_at"] is not None
|
||||
|
||||
|
||||
def test_reset_clears_content(conn):
|
||||
repo = WelcomeTemplateRepository(conn)
|
||||
repo.set("custom", updated_by="admin@example.com")
|
||||
repo.reset(updated_by="admin@example.com")
|
||||
row = repo.get()
|
||||
assert row["content"] is None
|
||||
Loading…
Reference in a new issue