f2205b93f4
Tree and person soft-delete + restore (owner-only for trees, editor for people) with recovery listings (?deleted=true); the worker already purges past the 30-day window. Adds tree-wide GET /relationships and /events so the family/pedigree view loads the whole graph in a few calls. 27 tests pass. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Signed-off-by: Justin Paul <justin@jpaul.me>
55 lines
2.5 KiB
Python
55 lines
2.5 KiB
Python
"""Soft-delete + recovery for trees and people."""
|
|
|
|
from tests.conftest import auth, register
|
|
|
|
|
|
async def test_tree_delete_and_restore(client):
|
|
h = auth(await register(client, "rec1@example.com"))
|
|
tree_id = (await client.post("/api/v1/trees", json={"name": "T"}, headers=h)).json()["id"]
|
|
|
|
# Delete -> gone from active lists, present in the recovery list.
|
|
assert (await client.delete(f"/api/v1/trees/{tree_id}", headers=h)).status_code == 204
|
|
assert len((await client.get("/api/v1/trees", headers=h)).json()) == 0
|
|
# A soft-deleted tree is no longer visible (404 to the would-be viewer).
|
|
gone = await client.get(f"/api/v1/trees/{tree_id}", headers=h)
|
|
assert gone.status_code == 404
|
|
deleted = (await client.get("/api/v1/trees?deleted=true", headers=h)).json()
|
|
assert len(deleted) == 1 and deleted[0]["id"] == tree_id
|
|
|
|
# Restore -> back in active lists.
|
|
assert (await client.post(f"/api/v1/trees/{tree_id}/restore", headers=h)).status_code == 200
|
|
assert len((await client.get("/api/v1/trees", headers=h)).json()) == 1
|
|
assert (await client.get(f"/api/v1/trees/{tree_id}", headers=h)).status_code == 200
|
|
|
|
|
|
async def test_only_owner_can_delete_tree(client):
|
|
owner = auth(await register(client, "rec-owner@example.com"))
|
|
tree_id = (await client.post("/api/v1/trees", json={"name": "T"}, headers=owner)).json()["id"]
|
|
other = auth(await register(client, "rec-other@example.com"))
|
|
blocked = await client.delete(f"/api/v1/trees/{tree_id}", headers=other)
|
|
assert blocked.status_code in (403, 404)
|
|
|
|
|
|
async def test_person_delete_and_restore(client):
|
|
h = auth(await register(client, "rec2@example.com"))
|
|
tree_id = (await client.post("/api/v1/trees", json={"name": "T"}, headers=h)).json()["id"]
|
|
person_id = (
|
|
await client.post(
|
|
f"/api/v1/trees/{tree_id}/persons", json={"given": "Ada"}, headers=h
|
|
)
|
|
).json()["id"]
|
|
|
|
assert (
|
|
await client.delete(f"/api/v1/trees/{tree_id}/persons/{person_id}", headers=h)
|
|
).status_code == 204
|
|
assert len((await client.get(f"/api/v1/trees/{tree_id}/persons", headers=h)).json()) == 0
|
|
deleted = (
|
|
await client.get(f"/api/v1/trees/{tree_id}/persons?deleted=true", headers=h)
|
|
).json()
|
|
assert len(deleted) == 1 and deleted[0]["primary_name"] == "Ada"
|
|
|
|
assert (
|
|
await client.post(f"/api/v1/trees/{tree_id}/persons/{person_id}/restore", headers=h)
|
|
).status_code == 200
|
|
assert len((await client.get(f"/api/v1/trees/{tree_id}/persons", headers=h)).json()) == 1
|