fix: audit findings — list details, batch integrity, migrations, restore fidelity
ci / docker (push) Successful in 3m17s

This commit is contained in:
2026-09-06 11:16:52 +08:00
parent d21c6629ba
commit 0cc486069e
8 changed files with 245 additions and 42 deletions
+58 -1
View File
@@ -172,7 +172,10 @@ def test_task_search_tags_subtasks_and_recycle_bin(client):
assert detail.status_code == 200
assert detail.json()["tags"][0]["name"] == "重要"
assert len(detail.json()["subtasks"]) == 1
assert len(client.get("/api/v1/tasks", params={"q": "咖啡"}).json()["items"]) == 1
listed = client.get("/api/v1/tasks", params={"q": "咖啡"}).json()["items"]
assert len(listed) == 1
assert listed[0]["tags"][0]["name"] == "重要"
assert listed[0]["subtasks"][0]["title"] == "比较价格"
assert client.delete(f"/api/v1/tasks/{parent['id']}").status_code == 204
trash = client.get("/api/v1/trash").json()["items"]
@@ -197,6 +200,29 @@ def test_batch_complete_and_move(client):
assert all(row["completed"] and row["list_id"] == other["id"] for row in rows)
def test_batch_move_rejects_standalone_subtask_and_delete_cascades(client):
client = initialized_client(client)
inbox = client.get("/api/v1/lists").json()[0]
other = client.post("/api/v1/lists", json={"name": "稍后"}).json()
parent = client.post("/api/v1/tasks", json={"title": "", "list_id": inbox["id"]}).json()
child = client.post(
"/api/v1/tasks",
json={"title": "", "list_id": inbox["id"], "parent_id": parent["id"]},
).json()
moved_child = client.post(
"/api/v1/tasks/batch", json={"task_ids": [child["id"]], "list_id": other["id"]}
)
assert moved_child.status_code == 400
deleted_parent = client.post(
"/api/v1/tasks/batch", json={"task_ids": [parent["id"]], "soft_delete": True}
)
assert deleted_parent.status_code == 200
assert client.get(f"/api/v1/tasks/{parent['id']}").status_code == 404
assert client.get(f"/api/v1/tasks/{child['id']}").status_code == 404
def test_inbox_is_protected_and_deleted_collections_are_hidden(client):
client = initialized_client(client)
inbox = client.get("/api/v1/lists").json()[0]
@@ -258,6 +284,37 @@ def test_tags_are_global_searchable_and_validated(client):
assert invalid.status_code == 404
def test_restore_replace_recovers_tags_habits_and_task_links(client):
client = initialized_client(client)
inbox = client.get("/api/v1/lists").json()[0]
tag = client.post("/api/v1/tags", json={"name": "备份标签", "color": "#f15a29"}).json()
client.post(
"/api/v1/tasks",
json={"title": "备份任务", "list_id": inbox["id"], "tag_ids": [tag["id"]]},
).json()
client.post(
"/api/v1/habits",
json={"name": "俯卧撑", "kind": "boolean", "schedule_type": "daily"},
).json()
exported = client.get("/api/v1/export")
assert exported.status_code == 200
client.post("/api/v1/tags", json={"name": "现有标签", "color": "#123abc"})
client.post(
"/api/v1/habits",
json={"name": "深蹲", "kind": "boolean", "schedule_type": "daily"},
)
restored = client.post("/api/v1/restore?mode=replace", json=exported.json())
assert restored.status_code == 200
tags = client.get("/api/v1/tags").json()
assert [row["name"] for row in tags] == ["备份标签"]
habits = client.get("/api/v1/habits").json()
assert [row["name"] for row in habits] == ["俯卧撑"]
listed = client.get("/api/v1/tasks", params={"q": "备份任务"}).json()["items"]
assert listed[0]["tags"][0]["name"] == "备份标签"
def test_recycle_bin_restore_and_permanent_delete_include_subtasks(client):
client = initialized_client(client)
inbox = client.get("/api/v1/lists").json()[0]