- 纵轴自动刻度改为精细步进(1/1.5/2/2.5/3/4/5/6/8/10),数据最大值落在约80%处 - ChartInput 新增 yMax 字段(JSON 存储、可拓展),图表弹窗新增「纵轴上限」输入 - 新增 yMax 往返测试
79 lines
3.7 KiB
Python
79 lines
3.7 KiB
Python
from datetime import date
|
|
|
|
from app.models import ChartInput, DailyRecordInput, MetricValue, TaskInput
|
|
from app.repository import ConflictError, JsonRepository
|
|
|
|
|
|
def _task(repo):
|
|
return repo.create_task(TaskInput.model_validate({
|
|
"name": "复盘任务",
|
|
"metrics": [
|
|
{"name": "完成", "kind": "checkin", "period": "day"},
|
|
{"name": "数量", "kind": "quantity", "period": "day"},
|
|
],
|
|
}))
|
|
|
|
|
|
def test_chart_crud_and_conflict(tmp_path):
|
|
repo = JsonRepository(tmp_path)
|
|
chart = repo.create_chart(ChartInput(title="趋势"))
|
|
assert repo.list_charts()["items"][0]["chartId"] == chart["chartId"]
|
|
updated = repo.update_chart(chart["chartId"], ChartInput(title="新趋势"), chart["version"])
|
|
assert updated["version"] == 2
|
|
try:
|
|
repo.update_chart(chart["chartId"], ChartInput(title="冲突"), chart["version"])
|
|
except ConflictError:
|
|
pass
|
|
else:
|
|
raise AssertionError("expected chart version conflict")
|
|
assert repo.delete_chart(chart["chartId"])["deleted"] is True
|
|
|
|
|
|
def test_chart_yMax_roundtrip(tmp_path):
|
|
repo = JsonRepository(tmp_path)
|
|
bounded = repo.create_chart(ChartInput(title="带上限", yMax=10))
|
|
assert repo.get_chart(bounded["chartId"])["yMax"] == 10
|
|
auto = repo.create_chart(ChartInput(title="自动"))
|
|
assert repo.get_chart(auto["chartId"])["yMax"] is None
|
|
|
|
|
|
def test_chart_data_modes_and_continuous_points(tmp_path):
|
|
repo = JsonRepository(tmp_path)
|
|
task = _task(repo)
|
|
checkin_id, quantity_id = [metric["metricId"] for metric in task["metrics"]]
|
|
repo.upsert_daily(task["taskId"], date(2026, 8, 2), DailyRecordInput(values=[MetricValue(metricId=checkin_id, value=1), MetricValue(metricId=quantity_id, value=3)], note="完成"))
|
|
repo.upsert_daily(task["taskId"], date(2026, 8, 4), DailyRecordInput(values=[MetricValue(metricId=quantity_id, value=2)]))
|
|
done = repo.create_chart(ChartInput(title="完成数", days=7))
|
|
metric = repo.create_chart(ChartInput(title="数量", taskId=task["taskId"], metricId=quantity_id, days=7))
|
|
for result in (repo.chart_data(done["chartId"], date(2026, 8, 4)), repo.chart_data(metric["chartId"], date(2026, 8, 4))):
|
|
assert len(result["points"]) == 7
|
|
assert [point["date"] for point in result["points"]] == sorted(point["date"] for point in result["points"])
|
|
done_points = {point["date"]: point["value"] for point in repo.chart_data(done["chartId"], date(2026, 8, 4))["points"]}
|
|
metric_points = {point["date"]: point["value"] for point in repo.chart_data(metric["chartId"], date(2026, 8, 4))["points"]}
|
|
assert done_points["2026-08-03"] == 0 and done_points["2026-08-04"] == 1
|
|
assert metric_points["2026-08-02"] == 3 and metric_points["2026-08-03"] == 0
|
|
|
|
|
|
def test_review_notes_skips_empty_and_resolves_deleted_task(tmp_path):
|
|
repo = JsonRepository(tmp_path)
|
|
task = _task(repo)
|
|
repo.upsert_daily(task["taskId"], date(2026, 8, 1), DailyRecordInput(note="心得"))
|
|
repo.upsert_daily(task["taskId"], date(2026, 8, 2), DailyRecordInput())
|
|
repo.soft_delete_task(task["taskId"], task["version"])
|
|
result = repo.review_notes(date(2026, 8, 1), date(2026, 8, 2))
|
|
assert [day["date"] for day in result["days"]] == ["2026-08-01"]
|
|
assert result["days"][0]["records"][0]["taskName"] == "已删除任务"
|
|
|
|
|
|
def test_export_import_charts(tmp_path):
|
|
repo = JsonRepository(tmp_path)
|
|
repo.create_chart(ChartInput(title="备份图表"))
|
|
exported = repo.export_all()
|
|
assert len(exported["charts"]) == 1
|
|
target = JsonRepository(tmp_path / "target")
|
|
target.import_all(exported)
|
|
assert target.stats()["chartCount"] == 1
|
|
legacy = {key: value for key, value in exported.items() if key != "charts"}
|
|
target.import_all(legacy)
|
|
assert target.stats()["chartCount"] == 0
|