stubborn-otter/otter-boards
Otter Boards, a team task board you can run from the web or from Claude and ChatGPT. FastAPI + DataK3 + appid + Models + MCP.
A remix template · 1 remixes · Remix stubborn-otter/otter-boards
tests/test_repository.py
"""The parts of the real repository the rules can't reach, and the promises schema.sql and the
Dodil files make. The rules themselves run on SqlBoards via tests/e2e/run.sh."""
import re
import time
from datetime import datetime, timedelta, timezone
from pathlib import Path
import psycopg
import pytest
from boards.infrastructure import repository
from boards.infrastructure.repository import SqlBoards
ROOT = Path(__file__).parent.parent
SCHEMA = (ROOT / "schema.sql").read_text()
TABLES = re.findall(r"CREATE TABLE IF NOT EXISTS (\w+) \((.*?)\n\);", SCHEMA, re.S)
class FakeConnection:
closed, description = False, None
def __init__(self, fails=False):
self.fails, self.statements = fails, []
def execute(self, statement, params=()):
if self.fails:
raise psycopg.OperationalError("server closed the connection")
self.statements.append(statement)
return self
def close(self):
self.closed = True
@pytest.fixture(autouse=True)
def no_retry_pauses(monkeypatch):
monkeypatch.setattr(repository, "RETRY_PAUSES", (0, 0))
def repo_with(*connections):
opened = list(connections)
repo = SqlBoards(lambda: opened.pop(0))
return repo, opened
def test_one_connection_serves_a_thread_across_queries():
good = FakeConnection()
repo, _ = repo_with(good)
repo.boards_for("a@x.io")
repo.boards_for("a@x.io")
assert len(good.statements) == 4
def test_a_stale_connection_is_replaced_and_the_query_still_runs():
stale, fresh = FakeConnection(fails=True), FakeConnection()
repo, _ = repo_with(stale, fresh)
repo.boards_for("a@x.io")
assert stale.closed and len(fresh.statements) == 2
def test_a_connection_drop_that_lasts_two_tries_is_survived():
"""Seen on DataK3: "tcp connect error" twice in a row, then fine."""
first, second = FakeConnection(fails=True), FakeConnection(fails=True)
fresh = FakeConnection()
repo, _ = repo_with(first, second, fresh)
repo.boards_for("a@x.io")
assert first.closed and second.closed and len(fresh.statements) == 2
def test_a_database_that_stays_down_is_reported_not_hidden():
repo, _ = repo_with(*(FakeConnection(fails=True) for _ in range(3)))
with pytest.raises(psycopg.OperationalError):
repo.boards_for("a@x.io")
def test_datak3_timestamps_become_plain_utc():
"""DataK3 returns TIMESTAMP with a zone, Postgres without. The rest of the app must not care:
a zone broke the dates in history and crashed the board summary."""
chicago = timezone(timedelta(hours=-5))
row = ("e1", "b1", "c1", "a@x.io", "moved it", datetime(2026, 11, 1, 16, 53, tzinfo=chicago),
"list", "To do", "Doing")
assert repository._event(row).at == datetime(2026, 11, 1, 21, 53)
plain = ("e1", "b1", "c1", "a@x.io", "moved it", datetime(2026, 11, 1, 21, 53), "", "", "")
assert repository._event(plain).at == datetime(2026, 11, 1, 21, 53)
def test_an_old_connection_is_retired(monkeypatch):
old, fresh = FakeConnection(), FakeConnection()
repo, _ = repo_with(old, fresh)
repo.boards_for("a@x.io")
later = time.time() + repository.MAX_CONNECTION_AGE + 1
monkeypatch.setattr(repository.time, "time", lambda: later)
repo.boards_for("a@x.io")
assert old.closed and fresh.statements
def test_every_table_has_a_chosen_primary_key():
assert len(TABLES) == 6
for name, body in TABLES:
assert "PRIMARY KEY" in body, name
def test_schema_stays_plain_sql():
code = "\n".join(line.partition("--")[0] for line in SCHEMA.splitlines()).upper()
for feature in ("SERIAL", "SEQUENCE", "TRIGGER", "FUNCTION", "GENERATED", "JSONB", "RETURNING",
"REFERENCES", "EXTENSION"):
assert feature not in code, feature
def test_schema_comments_can_not_split_a_statement():
"""A remix tool may split on semicolons, so comments mustn't contain one."""
assert not any(";" in line.partition("--")[2] for line in SCHEMA.splitlines())
def test_repository_uses_only_selects_and_inserts():
source = Path(repository.__file__).read_text()
statements = re.findall(r'"((?:SELECT|INSERT|UPDATE|DELETE|WITH|CREATE|ALTER|DROP)\b)', source)
assert set(statements) <= {"SELECT", "INSERT"}, set(statements)
for feature in ("RETURNING", "UNION", "JOIN", "BEGIN", "COMMIT", ".transaction("):
assert feature not in source, feature
def test_contest_files_still_fit_the_rules():
remix = (ROOT / ".dodil/remix.yaml").read_text()
deploy = (ROOT / ".dodil/deploy.yaml").read_text()
assert '"${{ dodil.repo }}-data"' in remix and '"${{ dodil.repo }}-data"' in deploy
assert '"${{ dodil.repo }}-users"' in remix and '"${{ dodil.repo }}-users"' in deploy
assert "on_conflict" not in remix # the default, fail, is the rule
assert "schema: schema.sql" in remix
assert "OPENAI" not in deploy.upper() and "ANTHROPIC" not in deploy.upper()
assert len("otter-boards") <= 18
def test_no_outside_ai_or_database_keys_in_the_app():
for path in (ROOT / "app" / "boards").rglob("*.py"):
text = path.read_text().upper()
assert "OPENAI_API_KEY" not in text and "ANTHROPIC_API_KEY" not in text, path