test(pins): annotation lifecycles pin the restore GET, not just writes
Release / build-and-push (push) Successful in 5m11s

Answering the owner's round-trip question (highlight/note/progress
survives to the DB and restores on reopen) surfaced a gap: the
annotation lifecycle pins covered POST/PUT/DELETE but never the GET
list — the exact call web/src/reader/reader.ts makes when a book is
reopened. Every lifecycle now pins 'GET list restores the annotation':
the created row must come back by id with the update's fields applied
(highlights assert color, notes content, bookmarks title/cfi), proving
the DB write and the restore read through the same endpoint the UI
loads. Ran green in the rebuilt tests image.

Workflow note surfaced by this change: the tests service builds an
image (no source mount), so test edits need 'docker compose ... build
tests' (or run --build) before the filtered runner sees them — the
first run of the new subtest silently executed the stale image.
This commit is contained in:
John O'Keefe committed 2026-11-02 18:29:54 -05:00
1 parent dd963993eb
commit 2bec4e60bd
1 file changed
+24
+24
View File
@@ -1214,6 +1214,30 @@ func pinAnnotationLifecycle(t *testing.T, setup *TestServerSetup, fx contractFix
require.NoError(t, json.Unmarshal(raw, &body2)) require.NoError(t, json.Unmarshal(raw, &body2))
assertResponseShape(t, "annotation "+kind+" update", body2, rowKeys, rowKeys) assertResponseShape(t, "annotation "+kind+" update", body2, rowKeys, rowKeys)
// The restore half of the round trip: what the reader fetches
// when the book is reopened must return the persisted annotation
// with the update applied — written to the DB, read back through
// the same list endpoint the UI loads on open.
t.Run(kind+" GET list restores the annotation", func(t *testing.T) {
resp3 := doReq(t, "GET", annotationURL(setup.Server.URL, fx.itemID, kind, ""), nil, setup.RegularToken)
defer resp3.Body.Close()
require.Equal(t, http.StatusOK, resp3.StatusCode, "list must stay 200")
var list []map[string]interface{}
require.NoError(t, json.NewDecoder(resp3.Body).Decode(&list))
var found map[string]interface{}
for _, row := range list {
if row["id"] == id {
found = row
break
}
}
require.NotNil(t, found, "created %s must come back in the list that restores a reopened book", kind)
updates, _ := updateBody.(map[string]interface{})
for key, want := range updates {
assert.Equal(t, want, found[key], "%s field must round-trip from the DB on restore", key)
}
})
t.Run(kind+" DELETE response", func(t *testing.T) { t.Run(kind+" DELETE response", func(t *testing.T) {
resp3 := doReq(t, "DELETE", annotationURL(setup.Server.URL, fx.itemID, kind, id), nil, setup.RegularToken) resp3 := doReq(t, "DELETE", annotationURL(setup.Server.URL, fx.itemID, kind, id), nil, setup.RegularToken)
defer resp3.Body.Close() defer resp3.Body.Close()