Full-CRUD sweep: update endpoints for tree, source, citation, relationship, media
Closes the rule #8 gap at the API layer: PATCH endpoints + service updates for Tree (name/description/visibility), Source, Citation (page/detail/confidence), Relationship (qualifier/notes), and Media (title/attachment) — editor-gated and audited. Every core entity now has create/read/update/delete. Edit UIs for these land in the frontend batch. 37 tests pass. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Signed-off-by: Justin Paul <justin@jpaul.me>
This commit is contained in:
@@ -0,0 +1,79 @@
|
||||
"""Update (the U in CRUD) for the remaining entities — rule #8."""
|
||||
|
||||
from tests.conftest import auth, register
|
||||
|
||||
|
||||
async def _setup(client, email):
|
||||
h = auth(await register(client, email))
|
||||
tid = (await client.post("/api/v1/trees", json={"name": "T"}, headers=h)).json()["id"]
|
||||
return h, tid
|
||||
|
||||
|
||||
async def test_tree_update(client):
|
||||
h, tid = await _setup(client, "u-tree@example.com")
|
||||
r = await client.patch(
|
||||
f"/api/v1/trees/{tid}", json={"name": "Renamed", "visibility": "unlisted"}, headers=h
|
||||
)
|
||||
assert r.status_code == 200
|
||||
assert r.json()["name"] == "Renamed" and r.json()["visibility"] == "unlisted"
|
||||
|
||||
|
||||
async def test_source_update(client):
|
||||
h, tid = await _setup(client, "u-src@example.com")
|
||||
sid = (
|
||||
await client.post(f"/api/v1/trees/{tid}/sources", json={"title": "Old"}, headers=h)
|
||||
).json()["id"]
|
||||
r = await client.patch(
|
||||
f"/api/v1/trees/{tid}/sources/{sid}",
|
||||
json={"title": "New", "repository": "NARA"},
|
||||
headers=h,
|
||||
)
|
||||
assert r.status_code == 200
|
||||
assert r.json()["title"] == "New" and r.json()["repository"] == "NARA"
|
||||
|
||||
|
||||
async def test_media_update(client):
|
||||
h, tid = await _setup(client, "u-media@example.com")
|
||||
mid = (
|
||||
await client.post(
|
||||
f"/api/v1/trees/{tid}/media",
|
||||
files={"file": ("a.txt", b"x", "text/plain")},
|
||||
data={"title": "old"},
|
||||
headers=h,
|
||||
)
|
||||
).json()["id"]
|
||||
r = await client.patch(f"/api/v1/trees/{tid}/media/{mid}", json={"title": "new"}, headers=h)
|
||||
assert r.status_code == 200 and r.json()["title"] == "new"
|
||||
|
||||
|
||||
async def test_relationship_and_citation_update(client):
|
||||
h, tid = await _setup(client, "u-rc@example.com")
|
||||
|
||||
async def mk(path, body):
|
||||
return (await client.post(f"/api/v1/trees/{tid}/{path}", json=body, headers=h)).json()["id"]
|
||||
|
||||
p1 = await mk("persons", {"given": "A"})
|
||||
p2 = await mk("persons", {"given": "B"})
|
||||
rid = await mk(
|
||||
"relationships",
|
||||
{
|
||||
"type": "parent_child",
|
||||
"person_from_id": p1,
|
||||
"person_to_id": p2,
|
||||
"qualifier": "biological",
|
||||
},
|
||||
)
|
||||
r = await client.patch(
|
||||
f"/api/v1/trees/{tid}/relationships/{rid}", json={"qualifier": "adoptive"}, headers=h
|
||||
)
|
||||
assert r.status_code == 200 and r.json()["qualifier"] == "adoptive"
|
||||
|
||||
src = await mk("sources", {"title": "S"})
|
||||
cid = await mk("citations", {"source_id": src, "person_id": p1})
|
||||
r2 = await client.patch(
|
||||
f"/api/v1/trees/{tid}/citations/{cid}",
|
||||
json={"page": "p.7", "confidence": "high"},
|
||||
headers=h,
|
||||
)
|
||||
assert r2.status_code == 200
|
||||
assert r2.json()["page"] == "p.7" and r2.json()["confidence"] == "high"
|
||||
Reference in New Issue
Block a user