Files
personal-progress-monitor/tests/test_schedules.py
MobKBK 8340d1dbbd feat: 新增日程面板、复盘页面与 Agent 读写接口
- 日程:单次/每日/每周/每月重复,JSON 存储,月历按优先级着色圆点 + 悬停提示
- 复盘页:可自定义折线图卡片(SVG) + 每日心得总览(按日期/任务展开)
- Agent 接口:GET /api/agent/overview 读日程+进度,POST /api/agent/schedules 建日程(PPM_API_TOKEN Bearer 鉴权)
- 导出/导入/统计纳入日程与图表;47 项测试全绿
2026-09-04 14:46:41 +08:00

168 lines
7.7 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
from datetime import date
def schedule_payload(title="日程", **overrides):
payload = {"title": title, "date": "2026-08-01", "time": "22:00", "recurrence": "none", "priority": 3, "state": "active"}
payload.update(overrides)
return payload
def test_schedule_crud_lifecycle(client):
created = client.post("/api/schedules", json=schedule_payload("第一个日程"))
assert created.status_code == 201, created.text
schedule = created.json()
schedule_id = schedule["scheduleId"]
assert schedule["version"] == 1 and schedule["completions"] == {}
listed = client.get("/api/schedules").json()
assert len(listed["items"]) == 1
fetched = client.get(f"/api/schedules/{schedule_id}")
assert fetched.status_code == 200 and fetched.json()["title"] == "第一个日程"
updated = client.put(
f"/api/schedules/{schedule_id}",
json=schedule_payload("改名日程", expectedVersion=1),
)
assert updated.status_code == 200 and updated.json()["version"] == 2
deleted = client.delete(f"/api/schedules/{schedule_id}?expectedVersion=2")
assert deleted.status_code == 200
assert client.get("/api/schedules").json()["items"] == []
restored = client.post(f"/api/schedules/{schedule_id}/restore?expectedVersion=3")
assert restored.status_code == 200
assert len(client.get("/api/schedules").json()["items"]) == 1
def test_schedule_update_conflict_and_purge(client):
created = client.post("/api/schedules", json=schedule_payload("冲突日程")).json()
schedule_id = created["scheduleId"]
conflict = client.put(f"/api/schedules/{schedule_id}", json=schedule_payload("冲突", expectedVersion=99))
assert conflict.status_code == 409
client.delete(f"/api/schedules/{schedule_id}?expectedVersion=1")
assert client.get(f"/api/schedules/{schedule_id}").status_code == 404
purged = client.post(f"/api/schedules/{schedule_id}/purge", json={"confirmation": schedule_id})
assert purged.status_code == 200
assert client.get(f"/api/schedules/{schedule_id}?includeDeleted=true").status_code == 404
def test_schedule_complete_toggle(client):
schedule = client.post("/api/schedules", json=schedule_payload("打卡日程")).json()
schedule_id = schedule["scheduleId"]
done = client.post(
f"/api/schedules/{schedule_id}/complete",
json={"date": "2026-08-01", "completed": True, "expectedVersion": 1},
)
assert done.status_code == 200
today = client.get("/api/schedules/today?date=2026-08-01").json()
assert today["items"][0]["completed"] is True
undone = client.post(
f"/api/schedules/{schedule_id}/complete",
json={"date": "2026-08-01", "completed": False, "expectedVersion": 2},
)
assert undone.status_code == 200
today = client.get("/api/schedules/today?date=2026-08-01").json()
assert today["items"][0]["completed"] is False
def test_schedule_recurrence_resolution(client):
client.post("/api/schedules", json=schedule_payload("单次", date="2026-08-01"))
client.post("/api/schedules", json=schedule_payload("每天", date="2026-08-01", recurrence="daily"))
client.post("/api/schedules", json=schedule_payload("每周", date="2026-08-03", recurrence="weekly")) # 2026-08-03 是周一
client.post("/api/schedules", json=schedule_payload("每月", date="2026-08-15", recurrence="monthly"))
def titles(day):
return {item["title"] for item in client.get(f"/api/schedules/today?date={day}").json()["items"]}
# 单次:只有当天
assert titles("2026-08-01") == {"单次", "每天"}
assert "单次" not in titles("2026-08-02")
# 每天:起始日之后每天都出现
assert "每天" in titles("2026-08-20")
assert "每天" not in titles("2026-07-31")
# 每周:起始日之后的每个周一出现
assert "每周" in titles("2026-08-10")
assert "每周" not in titles("2026-08-04") # 周二
# 每月:起始日之后的每月 15 号出现
assert "每月" in titles("2026-09-15")
assert "每月" not in titles("2026-09-16")
def test_schedule_archived_not_shown(client):
client.post("/api/schedules", json=schedule_payload("归档日程", state="archived"))
today = client.get("/api/schedules/today?date=2026-08-01").json()
assert today["items"] == []
def test_schedule_calendar_endpoint(client):
client.post("/api/schedules", json=schedule_payload("八月日程", date="2026-08-15"))
calendar = client.get("/api/schedules/calendar?month=2026-08").json()
assert calendar["year"] == 2026 and calendar["month"] == 8
assert len(calendar["days"]) == 31
day_15 = next(item for item in calendar["days"] if item["date"] == "2026-08-15")
assert [item["title"] for item in day_15["schedules"]] == ["八月日程"]
assert client.get("/api/schedules/calendar?month=bad").status_code == 422
def test_schedule_validation(client):
bad_time = client.post("/api/schedules", json=schedule_payload("坏时间", time="25:99"))
assert bad_time.status_code == 422
missing_date = client.post("/api/schedules", json={"title": "无日期", "recurrence": "none"})
assert missing_date.status_code == 422
bad_recurrence = client.post("/api/schedules", json=schedule_payload("坏重复", recurrence="yearly"))
assert bad_recurrence.status_code == 422
def test_agent_overview_token_auth(client, monkeypatch):
client.post("/api/schedules", json=schedule_payload("今日日程", date="2026-08-01"))
client.post("/api/tasks", json={"name": "任务", "startDate": "2026-08-01", "metrics": [{"name": "完成", "kind": "checkin", "period": "day"}]})
# 未配置 token → 503
assert client.get("/api/agent/overview").status_code == 503
monkeypatch.setenv("PPM_API_TOKEN", "tok")
# 无 / 错误 token → 401
assert client.get("/api/agent/overview").status_code == 401
assert client.get("/api/agent/overview", headers={"Authorization": "Bearer wrong"}).status_code == 401
# 正确 token → 200,含日程与任务进度
response = client.get("/api/agent/overview?date=2026-08-01", headers={"Authorization": "Bearer tok"})
assert response.status_code == 200, response.text
body = response.json()
assert body["date"] == "2026-08-01"
assert [item["title"] for item in body["schedules"]] == ["今日日程"]
assert "tasks" in body["taskProgress"]
task_info = next(iter(body["taskProgress"]["tasks"].values()))
assert task_info["name"] == "任务"
def test_agent_create_schedule_write(client, monkeypatch):
monkeypatch.setenv("PPM_API_TOKEN", "tok")
# 无 token → 401
assert client.post("/api/agent/schedules", json=schedule_payload("通过API添加")).status_code == 401
# 带 token → 201,且出现在列表里
created = client.post(
"/api/agent/schedules",
json=schedule_payload("通过API添加"),
headers={"Authorization": "Bearer tok"},
)
assert created.status_code == 201, created.text
schedule_id = created.json()["scheduleId"]
assert any(item["scheduleId"] == schedule_id for item in client.get("/api/schedules").json()["items"])
# 非法 payload → 422
assert client.post("/api/agent/schedules", json={"title": "x"}, headers={"Authorization": "Bearer tok"}).status_code == 422
def test_schedule_survives_export_import(client):
client.post("/api/schedules", json=schedule_payload("导出日程"))
exported = client.get("/api/export").json()
assert any(s["title"] == "导出日程" for s in exported.get("schedules", []))
imported = client.post("/api/admin/import", json=exported)
assert imported.status_code == 200, imported.text
listed = client.get("/api/schedules").json()["items"]
assert any(s["title"] == "导出日程" for s in listed)
assert client.get("/api/admin/stats").json()["scheduleCount"] == 1