- Fix first daily record 422 (expectedVersion now accepts 0)
- Add period_window + summarize; /api/summary and /api/tasks/{id}/summary
aggregate each metric inside its own day/week/month/lifetime window,
reading raw facts only (never the rebuildable month archives)
- Drive frontend rendering from per-page layout JSON with a component
registry; scope/cardinality enforced server-side, invalid layouts fall back
- Add quick checkin and duration input in H:M:S that round-trips integer seconds
- Fix heatmap/completedAt timezone drift to Asia/Shanghai
- Add server-side session TTL (30d) matching the login cookie
- Handle date.min/date.max boundaries without OverflowError
- Half-up rounding to match browser Math.round
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
43 lines
1.6 KiB
Python
43 lines
1.6 KiB
Python
from app.layouts import load_layout, validate_layout
|
|
|
|
|
|
def _layout(page, main_components, sidebar=None):
|
|
return {
|
|
"schemaVersion": 1,
|
|
"page": page,
|
|
"regions": {
|
|
"sidebar": sidebar or ([{"component": "task-list"}] if page == "task-detail" else []),
|
|
"main": main_components,
|
|
},
|
|
}
|
|
|
|
|
|
def test_valid_home_layout():
|
|
assert validate_layout(_layout("home", [{"component": "overview-summary"}, {"component": "task-progress-grid"}]), "home")
|
|
|
|
|
|
def test_valid_task_detail_layout():
|
|
assert validate_layout(_layout("task-detail", [{"component": "task-header"}, {"component": "today-record"}, {"component": "metric-progress"}, {"component": "activity-heatmap"}, {"component": "task-settings"}]), "task-detail")
|
|
|
|
|
|
def test_rejects_component_on_wrong_page():
|
|
assert not validate_layout(_layout("home", [{"component": "today-record"}]), "home")
|
|
|
|
|
|
def test_rejects_component_in_wrong_region():
|
|
layout = _layout("task-detail", [{"component": "task-header"}], sidebar=[{"component": "task-header"}])
|
|
assert not validate_layout(layout, "task-detail")
|
|
|
|
|
|
def test_rejects_duplicate_component():
|
|
layout = _layout("task-detail", [{"component": "today-record"}, {"component": "today-record"}])
|
|
assert not validate_layout(layout, "task-detail")
|
|
|
|
|
|
def test_load_layout_falls_back_on_invalid(tmp_path):
|
|
import json
|
|
bad = tmp_path / "home.json"
|
|
bad.write_text(json.dumps(_layout("home", [{"component": "today-record"}])), encoding="utf-8")
|
|
loaded = load_layout(tmp_path, "home")
|
|
assert loaded["regions"]["main"] != [{"component": "today-record"}]
|