Files
felhom.eu/hub/internal/store/escrow_superseded_test.go
T
admin e247dbc1be hub v0.60.0: offsite continuity Part B — superseded-escrow retention (data-first)
- host_escrow_superseded table + SaveHostEscrow retains a different-sha old blob before overwrite (tx); same-sha idempotent (no supersede row); returns superseded bool. ACK/restore read the current row unchanged. CountSuperseded/ListSuperseded; DeleteHost drops retained rows.
- escrow_superseded audit event + operator retained-count on host detail; register offbox_repo_orphaned/reset. Red-proof TestSaveHostEscrow_RetainsSuperseded.
2026-07-17 10:47:50 +02:00

59 lines
2.1 KiB
Go

package store
import "testing"
// Part B (v0.60.0) — the escrow retention red-proof (the 2026-07-17 incident class). A new escrow blob
// with a DIFFERENT sealed-passphrase sha must RETAIN the old blob (recoverable) instead of destroying
// it; a same-sha re-upload is idempotent (no supersede row). Pre-fix (destructive ON CONFLICT
// overwrite) the old blob is gone → the retrieval assertion FAILS.
func TestSaveHostEscrow_RetainsSuperseded(t *testing.T) {
st := newTestStore(t)
const h = "h1"
// 1st upload (P_old) — nothing to supersede.
sup, err := st.SaveHostEscrow(h, []byte("blob-old"), "fp-old", "zk", "2026-07-09T00:00:00Z", "SHA_OLD")
if err != nil {
t.Fatal(err)
}
if sup {
t.Fatal("first upload must not supersede")
}
// 2nd upload (P_new, DIFFERENT sha) — must supersede + retain the old.
sup, err = st.SaveHostEscrow(h, []byte("blob-new"), "fp-new", "zk", "2026-07-16T00:00:00Z", "SHA_NEW")
if err != nil {
t.Fatal(err)
}
if !sup {
t.Fatal("a different-passphrase upload must supersede (retain the old blob)")
}
// Current row is the NEW blob (ACK/restore-serving read this — unchanged behavior).
cur, _ := st.GetHostEscrow(h)
if cur == nil || string(cur.Blob) != "blob-new" || cur.ResticPwSHA256 != "SHA_NEW" {
t.Fatalf("current row not the new blob: %+v", cur)
}
// RED-PROOF: the OLD blob is RETAINED and retrievable.
old, err := st.ListSupersededEscrow(h)
if err != nil {
t.Fatal(err)
}
if len(old) != 1 || string(old[0].Blob) != "blob-old" || old[0].ResticPwSHA256 != "SHA_OLD" {
t.Fatalf("old blob NOT retained after overwrite (incident): %+v", old)
}
if n, _ := st.CountSupersededEscrow(h); n != 1 {
t.Fatalf("superseded count = %d, want 1", n)
}
// 3rd upload, SAME sha as current — idempotent (re-ceremony of the same password): NO supersede row.
sup, err = st.SaveHostEscrow(h, []byte("blob-new-2"), "fp-new", "zk", "2026-07-16T01:00:00Z", "SHA_NEW")
if err != nil {
t.Fatal(err)
}
if sup {
t.Fatal("a same-sha re-upload must NOT supersede (idempotent)")
}
if n, _ := st.CountSupersededEscrow(h); n != 1 {
t.Fatalf("idempotent re-upload created a superseded row: count=%d", n)
}
}