Spec #136: reader-facing finished label on the flat Bookmark (#160)

This commit is contained in:
2026-08-22 17:05:09 +07:00
parent 77e3f710ec
commit ea7bb97112
9 changed files with 165 additions and 6 deletions
+31
View File
@@ -934,6 +934,37 @@ func TestUpsertCreatesSeriesFromClient(t *testing.T) {
}
}
// The wire's finished flag is derived, never stored: the Upsert's explicit
// column list does not name finished_at — the same omission that protects
// cover — so a client echoing a cached flag, true or false, cannot change the
// Series' retired state (issues #157, #160).
func TestUpsertCannotWriteSeriesFinished(t *testing.T) {
store := newTestStore(t)
seedFinished(t, store, "asura:done", "https://asurascans.com/comics/asura:done")
for _, sent := range []bool{true, false} {
stored, err := store.Upsert(store.OwnerID(), Bookmark{
Key: "asura:done", Site: "asura", SeriesID: "done",
Finished: sent, UpdatedAt: 2000,
})
if err != nil {
t.Fatalf("Upsert(Finished: %v): %v", sent, err)
}
if !stored.Finished {
t.Fatalf("stored.Finished = false after echoing %v, want true", sent)
}
}
var finishedAt int64
if err := store.db.QueryRow(
`SELECT finished_at FROM series WHERE site = $1 AND series_id = $2`,
"asura", "done").Scan(&finishedAt); err != nil {
t.Fatalf("read finished_at: %v", err)
}
if finishedAt != 1000 {
t.Fatalf("series.finished_at = %v, want the seeded 1000 untouched", finishedAt)
}
}
// The hook is what starts creation-time acquisition, so it must fire exactly
// once per Series — on the PUT that created it, and on no later one, whichever
// Reader sends it.