stackchain-dashboard/tests/test_week_plan.py
timmy e01681af09
Some checks failed
CI / lint (pull_request) Successful in 3m43s
CI / build-release (pull_request) Successful in 8s
CI / browser-journey (pull_request) Failing after 4m21s
CI / release-candidate (pull_request) Has been skipped
feat: plan the next seven days on mobile (Closes #1177)
2026-08-20 11:28:58 +00:00

113 lines
4.1 KiB
Python

import sqlite3
from datetime import datetime, timezone
import pytest
from src import main
from src.today_store import TodayStore, WeekPlanConflict
def sample_days():
return [
{
"plan_date": "2026-08-21",
"ids": ["issue:secret/repo:3:", "issue:secret/repo:2:"],
"capacity_minutes": 150,
"estimates": {"issue:secret/repo:3:": 60, "issue:secret/repo:2:": 45},
},
{
"plan_date": "2026-08-23",
"ids": ["issue:secret/repo:5:"],
"capacity_minutes": 90,
"estimates": {"issue:secret/repo:5:": 30},
},
]
def test_week_plan_is_encrypted_account_scoped_revisioned_and_preserves_other_dates(tmp_path):
path = tmp_path / "today.sqlite3"
store = TodayStore(path, encryption_key=b"w" * 32)
saved = store.replace_week(
"Timmy", base_revision=0, days=sample_days(), timezone="America/Los_Angeles"
)
assert saved == {
"revision": 1,
"timezone": "America/Los_Angeles",
"days": sample_days(),
}
assert store.get_week("timmy") == saved
assert store.get_week("alexander") == {"revision": 0, "timezone": None, "days": []}
retained = path.read_bytes()
assert b"issue:secret/repo" not in retained
assert b"America/Los_Angeles" not in retained
changed = [dict(sample_days()[0], capacity_minutes=180), sample_days()[1]]
updated = store.replace_week("timmy", base_revision=1, days=changed, timezone="America/Los_Angeles")
assert updated["days"][1] == saved["days"][1]
with pytest.raises(WeekPlanConflict) as conflict:
store.replace_week("timmy", base_revision=1, days=[], timezone="UTC")
assert conflict.value.snapshot == updated
def test_week_promotion_moves_only_due_date_to_today_exactly_once(tmp_path):
store = TodayStore(
tmp_path / "today.sqlite3",
encryption_key=b"p" * 32,
clock=lambda: datetime(2026, 8, 22, 8, tzinfo=timezone.utc).timestamp(),
)
today = store.apply("timmy", "today", "add", "issue:r:1:")
week = store.replace_week("timmy", base_revision=0, days=sample_days(), timezone="UTC")
promoted = store.promote_week(
"timmy", promotion_id="week-2026-08-21-r1", week_revision=week["revision"],
plan_date="2026-08-21", today_revision=today["revision"],
)
replay = store.promote_week(
"timmy", promotion_id="week-2026-08-21-r1", week_revision=week["revision"],
plan_date="2026-08-21", today_revision=today["revision"],
)
assert replay == promoted
assert promoted["ids"] == sample_days()[0]["ids"]
remaining = store.get_week("timmy")
assert remaining["revision"] == 2
assert remaining["days"] == [sample_days()[1]]
def test_existing_tomorrow_plan_migrates_into_week_without_losing_planning_data(tmp_path):
store = TodayStore(tmp_path / "today.sqlite3", encryption_key=b"m" * 32)
tomorrow = store.replace_tomorrow(
"timmy", base_revision=0, ids=["issue:r:9:"], capacity_minutes=75,
estimates={"issue:r:9:": 45}, plan_date="2026-08-21", timezone="UTC",
)
week = store.get_week("timmy")
assert week == {
"revision": 1, "timezone": "UTC",
"days": [{key: tomorrow[key] for key in ("plan_date", "ids", "capacity_minutes", "estimates")}],
}
assert store.get_tomorrow("timmy") == {
"revision": 2, "ids": [], "capacity_minutes": None, "estimates": {}
}
@pytest.mark.anyio
async def test_week_api_round_trip_and_conflict_contract(monkeypatch, tmp_path):
async def user():
return {"login": "timmy"}
store = TodayStore(tmp_path / "today.sqlite3", encryption_key=b"a" * 32)
monkeypatch.setattr(main, "current_user", user)
monkeypatch.setattr(main, "_today_store", lambda: store)
payload = main.WeekPlanUpdate(base_revision=0, days=sample_days(), timezone="UTC")
saved = await main.replace_week_plan(payload)
assert await main.get_week_plan() == saved
with pytest.raises(main.HTTPException) as raised:
await main.replace_week_plan(payload)
assert raised.value.status_code == 409
assert raised.value.detail == {"code": "week_changed", "snapshot": saved}