stackchain-dashboard/tests/test_today_store.py
timmy 020106f001
All checks were successful
CI / lint (pull_request) Successful in 38s
CI / build-release (pull_request) Successful in 5s
CI / release-candidate (pull_request) Has been skipped
feat: sync Today plan across devices (#357)
2026-08-09 00:39:08 +00:00

106 lines
3.7 KiB
Python

import pytest
import httpx
from src import main
from src.today_store import TodayPlanFull, TodayStore
def test_operations_are_durable_ordered_idempotent_and_account_scoped(tmp_path):
path = tmp_path / "today.sqlite3"
store = TodayStore(path, limit=3)
assert store.apply("timmy", "op-1", "add", "issue:stackchain/dashboard:1:") == {
"revision": 1,
"ids": ["issue:stackchain/dashboard:1:"],
}
store.apply("timmy", "op-2", "add", "issue:stackchain/dashboard:2:")
store.apply("timmy", "op-3", "add", "issue:stackchain/dashboard:3:")
moved = store.apply(
"timmy", "op-4", "move", "issue:stackchain/dashboard:3:", direction="up"
)
duplicate = store.apply(
"timmy", "op-4", "move", "issue:stackchain/dashboard:3:", direction="up"
)
assert moved == duplicate == {
"revision": 4,
"ids": [
"issue:stackchain/dashboard:1:",
"issue:stackchain/dashboard:3:",
"issue:stackchain/dashboard:2:",
],
}
assert TodayStore(path, limit=3).get("timmy") == moved
assert store.get("alexander") == {"revision": 0, "ids": []}
def test_limit_is_atomic_and_remove_frees_capacity(tmp_path):
store = TodayStore(tmp_path / "today.sqlite3", limit=2)
store.apply("timmy", "one", "add", "issue:r:1:")
store.apply("timmy", "two", "add", "issue:r:2:")
with pytest.raises(TodayPlanFull):
store.apply("timmy", "three", "add", "issue:r:3:")
assert store.get("timmy") == {
"revision": 2,
"ids": ["issue:r:1:", "issue:r:2:"],
}
store.apply("timmy", "remove", "remove", "issue:r:1:")
assert store.apply("timmy", "retry-three", "add", "issue:r:3:")["ids"] == [
"issue:r:2:",
"issue:r:3:",
]
@pytest.mark.anyio
async def test_authenticated_today_api_uses_confirmed_account_and_csrf(monkeypatch, tmp_path):
monkeypatch.setenv("STACKCHAIN_DASHBOARD_AUTH_MODE", "operator")
monkeypatch.setenv("STACKCHAIN_DASHBOARD_ACCESS_TOKEN", "correct horse battery staple")
monkeypatch.setenv(
"STACKCHAIN_DASHBOARD_SESSION_SECRET",
"a-separate-session-signing-secret-with-enough-entropy",
)
monkeypatch.setenv("STACKCHAIN_SESSION_DB", str(tmp_path / "sessions.sqlite3"))
monkeypatch.setenv("STACKCHAIN_LOGIN_ATTEMPT_DB", str(tmp_path / "login.sqlite3"))
monkeypatch.setenv("STACKCHAIN_TODAY_DB", str(tmp_path / "today.sqlite3"))
async def user():
return {"id": 1, "login": "Timmy"}
monkeypatch.setattr(main, "current_user", user)
transport = httpx.ASGITransport(app=main.app)
async with httpx.AsyncClient(transport=transport, base_url="https://test") as client:
await client.post(
"/api/v1/session", json={"access_token": "correct horse battery staple"}
)
forbidden = await client.patch(
"/api/v1/today",
json={
"operation_id": "mobile-1",
"action": "add",
"item_id": "issue:stackchain/dashboard:357:",
},
)
changed = await client.patch(
"/api/v1/today",
json={
"operation_id": "mobile-1",
"action": "add",
"item_id": "issue:stackchain/dashboard:357:",
},
headers={
"Origin": "https://test",
"X-CSRF-Token": client.cookies["stackchain_csrf"],
},
)
fetched = await client.get("/api/v1/today")
assert forbidden.status_code == 403
assert changed.status_code == 200
assert changed.json() == fetched.json() == {
"revision": 1,
"ids": ["issue:stackchain/dashboard:357:"],
}
assert fetched.headers["cache-control"] == "no-store"