test(visibility): full suite green under RLS — sync paths, device-auth bridge, seed pool

Phase 5 (plan §7): the entire integration suite passes with the RLS
policies live. The suite was the triage enumerator the plan predicted:

- device_auth middleware: the THIRD identity bridge — KOReader/Kobo
  device-authenticated requests read media as the DEVICE OWNER
  (plan A9 called this path out; the flip was 404s on sync metadata).
- conflicts/analytics/progress handlers: detached
  context.Background() reads threaded to the request context (the
  requesting user's identity) — 12+ sites; resolve helpers take ctx.
- scanner: ScanFolders self-declares its configured admin identity
  (server maintenance, product-correct for any caller); generic job
  creation stamps the requesting admin.
- harness: setup.DBPool and the sync-test pool declare
  rls.SeedIdentity at SESSION scope via BeforeAcquire — the pool never
  serves a second identity, so no A3 bleed; setup.DB falls back to the
  seed identity (NewSeedDB) for ungoverned-context calls; filter tests
  use seedExec for raw governed-table statements.
- contract pin WIDENED (docs/contract_pins.md 2026-10-29): under RLS,
  item-scoped gates may answer 404 (row invisible — no existence leak)
  as well as 403 (Go-level checks on listings keep strict 403).
- TestWorker_JobStatusTracking — the pre-existing red from the Phase 0
  baseline — passes with the harness seed work; no product change.
This commit is contained in:
John O'Keefe committed 2026-10-29 10:14:08 -04:00
1 parent bb2cc26e9d
commit 4d697f5fcf
7 files changed
+84 -38

No files matched your search

+7 -8
View File
@@ -80,7 +80,7 @@ func (h *ConflictHandler) GetConflictsData(c *echo.Context) ([]ConflictDetailRes
status = "unresolved"
}
ctx := context.Background()
ctx := c.Request().Context()
var conflicts interface{}
var err error
@@ -263,13 +263,13 @@ func (h *ConflictHandler) ResolveConflict(c *echo.Context) error {
}
if conflict.ConflictType == "progress" {
if err := h.applyProgressResolution(conflict.MediaItemID, conflict.UserID, winnerSource, winnerData); err == nil {
if err := h.applyProgressResolution(c.Request().Context(), conflict.MediaItemID, conflict.UserID, winnerSource, winnerData); err == nil {
appliedTo["progress"] = true
}
}
if conflict.ConflictType == "annotation_highlight" || conflict.ConflictType == "annotation_bookmark" || conflict.ConflictType == "annotation_note" {
if err := h.applyAnnotationResolution(conflict.MediaItemID, conflict.UserID, winnerData, conflict.ConflictType); err == nil {
if err := h.applyAnnotationResolution(c.Request().Context(), conflict.MediaItemID, conflict.UserID, winnerData, conflict.ConflictType); err == nil {
appliedTo["annotations"] = true
}
}
@@ -321,7 +321,7 @@ func (h *ConflictHandler) ResolveConflict(c *echo.Context) error {
// the service's LWW seed instead of being NULLed — that is the fix.
// chapter_progress keeps its stored value instead of being overwritten
// with the book-level percentage.
func (h *ConflictHandler) applyProgressResolution(mediaItemID pgtype.UUID, userID pgtype.UUID, winnerSource string, data map[string]interface{}) error {
func (h *ConflictHandler) applyProgressResolution(ctx context.Context, mediaItemID pgtype.UUID, userID pgtype.UUID, winnerSource string, data map[string]interface{}) error {
req := wsync.SaveProgressRequest{
MediaItemID: mediaItemID,
UserID: userID,
@@ -361,12 +361,11 @@ func (h *ConflictHandler) applyProgressResolution(mediaItemID pgtype.UUID, userI
req.TotalPages = &tp
}
_, err := h.progressSvc.SaveProgress(context.Background(), req)
_, err := h.progressSvc.SaveProgress(ctx, req)
return err
}
func (h *ConflictHandler) applyAnnotationResolution(mediaItemID pgtype.UUID, userID pgtype.UUID, winnerData map[string]interface{}, conflictType string) error {
ctx := context.Background()
func (h *ConflictHandler) applyAnnotationResolution(ctx context.Context, mediaItemID pgtype.UUID, userID pgtype.UUID, winnerData map[string]interface{}, conflictType string) error {
dedupKey, _ := winnerData["dedup_key"].(string)
if dedupKey == "" {
@@ -702,7 +701,7 @@ func (h *ConflictHandler) BulkResolveConflicts(c *echo.Context) error {
continue
}
if err := h.applyProgressResolution(conflict.MediaItemID, conflict.UserID, winningSource, winnerData); err != nil {
if err := h.applyProgressResolution(c.Request().Context(), conflict.MediaItemID, conflict.UserID, winningSource, winnerData); err != nil {
results = append(results, ConflictResult{
ConflictID: conflictIDStr,
Status: "error",