sirenamc/stackline
Stackline, a team task board you run from the web or from Claude/ChatGPT. FastAPI + DataK3 + appid + Models + MCP.
A remix template · 0 remixes · Remix sirenamc/stackline
tests/live_check.py
"""The main rules against a REAL bucket and Dodil Models, through the same use cases the web page
and the MCP tools call. Not part of the unit tests: it needs a Dodil service account.
cd app
BUCKET=<repo>-data DODIL_SERVICE_ACCOUNT_ID=... DODIL_SERVICE_ACCOUNT_SECRET=... \\
python ../tests/live_check.py
It writes one board owned by made-up @example.test people, so nobody real ever sees it.
"""
import time
import uuid
from boards.application.use_cases import Boards, NotFound
from boards.domain.model import Forbidden, Person
from boards.infrastructure.dodil import ModelsAssistant
from boards.infrastructure.repository import SqlBoards
run = uuid.uuid4().hex[:6]
OWNER, MEMBER, VIEWER, STRANGER = (Person(f"{who}-{run}@example.test")
for who in ("owner", "member", "viewer", "stranger"))
results = []
def check(name, fn):
t = time.time()
try:
fn()
results.append((True, name, time.time() - t))
except Exception as e: # report every failure, keep going
results.append((False, f"{name}: {type(e).__name__}: {e}", time.time() - t))
def refused(exc, fn):
try:
fn()
except exc:
return
raise AssertionError(f"expected {exc.__name__}")
s = Boards(SqlBoards(), ModelsAssistant())
state = {}
def setup():
b = s.create_board(OWNER, f"Live check {run}")
state["b"] = b["id"]
s.invite_member(OWNER, b["id"], MEMBER.email, "member")
s.invite_member(OWNER, b["id"], VIEWER.email, "viewer")
def cards():
a = s.add_card(MEMBER, state["b"], "Send invoice to ACME", list_name="doing",
assignee="me", due_date="2026-01-05", labels=["finance"])
b = s.add_card(MEMBER, state["b"], "Chase unpaid invoice from ACME")
c = s.add_card(OWNER, state["b"], "Design the launch page")
assert a["list"] == "Doing" and a["overdue"], a
assert b["number"] > a["number"]
state.update(a=a["id"], b2=b["id"], c=c["id"])
def roles():
refused(Forbidden, lambda: s.add_card(VIEWER, state["b"], "nope"))
refused(Forbidden, lambda: s.move_card(VIEWER, state["a"], "Done"))
refused(Forbidden, lambda: s.invite_member(MEMBER, state["b"], "x@example.test", "member"))
refused(NotFound, lambda: s.show_board(STRANGER, state["b"]))
refused(NotFound, lambda: s.card_details(STRANGER, state["a"]))
assert s.my_boards(STRANGER) == []
def moves_and_log():
s.move_card(MEMBER, state["a"], "Done")
s.update_card(MEMBER, state["a"], title="Send invoice to ACME Ltd")
hist = s.card_details(OWNER, state["a"])["history"]
seen = {(h["action"], h["field"], h["before"], h["after"]) for h in hist}
assert ("moved", "list", "Doing", "Done") in seen, seen
assert ("edited", "title", "Send invoice to ACME", "Send invoice to ACME Ltd") in seen, seen
def attention():
s.update_card(OWNER, state["c"], due_date="2026-01-01")
late = [c["number"] for c in s.overdue(VIEWER, state["b"])]
assert int(state["c"]) in late and int(state["a"]) not in late, late # a is Done now
s.assign_card(OWNER, state["b2"], MEMBER.email)
plate = s.my_plate(MEMBER)
assert any(c["number"] == int(state["b2"]) for c in plate["no_due_date"]), plate
def blockers():
s.block_card(OWNER, state["c"], state["b2"])
chain = s.what_is_blocking(VIEWER, state["c"])
assert [x["number"] for x in chain["blocking"]] == [int(state["b2"])], chain
def ai_checklist():
steps = s.suggest_checklist(MEMBER, state["c"])["suggested_checklist"]
assert 2 <= len(steps) <= 8, steps
print(" checklist:", steps)
def ai_similar():
hits = s.similar_cards(MEMBER, state["a"], limit=2)
assert hits and hits[0]["number"] == int(state["b2"]), [(h["title"], h["similarity"]) for h in hits]
print(" similar:", [(h["title"], h["similarity"]) for h in hits])
def ai_summary():
out = s.board_summary(VIEWER, state["b"])
assert out["overdue"] >= 1 and out["summary"].strip(), out
print(" summary:", out["summary"].replace("\n", " | ")[:300])
def ai_plan():
t = time.time()
board = s.set_up_board(OWNER, f"Plan {run}", goal="Launch the new company website",
deadline="2026-12-01", people=[MEMBER.email])
titles = [c["title"] for lst in board["lists"] for c in lst["cards"]]
blocked = sum(1 for lst in board["lists"] for c in lst["cards"] if c["blocked_by"])
assert 4 <= len(titles) <= 15 and blocked, (titles, blocked)
assert time.time() - t < 50, "too close to Ignite's 60 s"
print(f" plan ({len(titles)} cards, {blocked} blocked):", titles)
state["plan"] = board["id"]
def example_and_demo_reset():
ex = s.start_example(STRANGER)
assert len(ex["created_cards"]) == 9 and len(s.overdue(STRANGER, ex["id"])) == 2
demo = Boards(SqlBoards(), ModelsAssistant(), demo_accounts=(
OWNER.email, MEMBER.email, VIEWER.email))
demo.reset_demo(VIEWER)
demo.reset_demo(VIEWER) # twice: no leftovers from the first run
titles = sorted(b["title"] for b in demo.my_boards(OWNER))
assert titles.count("Launch") == 1 and titles.count("Ops") == 1, titles
assert "Ops" not in [b["title"] for b in demo.my_boards(VIEWER)]
# tidy up: the reset owns these boards, so remove them the same way
for b in s.repo.boards_for(OWNER.email) + s.repo.boards_for(STRANGER.email):
s.repo.delete_board(b.id)
assert s.my_boards(OWNER) == [] and s.my_boards(STRANGER) == []
for name, fn in [("create board and invite", setup), ("add cards by list name", cards),
("roles hold", roles), ("move, edit and the log", moves_and_log),
("overdue and my plate", attention), ("blocked by chain", blockers),
("AI checklist", ai_checklist),
("similar by meaning (jina-embeddings-v4)", ai_similar),
("daily summary", ai_summary),
("a board from one sentence (Dodil Models)", ai_plan),
("example board, demo reset, delete board", example_and_demo_reset)]:
check(name, fn)
for ok, name, secs in results:
print(f"{'PASS' if ok else 'FAIL'} {name} ({secs:.1f}s)")
print(f"{sum(ok for ok, _, _ in results)}/{len(results)} passed")