Three harness corrections found while executing the invariant test:
- library type is 'ebooks' (plural) in library_types, not 'ebook'
- cleanup ordering: t.Cleanup is LIFO — register the pool Close BEFORE
the fixture's library deletes, or the deletes run against a closed
pool and fail silently into ignored errors (leaving RLSMARKER rows
behind; found because a second run saw the first run's 'deleted'
books still leaking through Recently Added)
- collection detail is addressed by the section's collection_id (the
UUID in view_all_url), not the section name
Adds the permanent auditor for per-user library visibility: a restricted
user with a hidden library sees none of its items on any media-returning
surface (media list, search, dashboard sections, series, facets, OPDS
device catalog, collection detail, scoped/detail access).
RED now, on exactly the surfaces the audit predicted: the dashboard
section queries (GetNotStartedItems / GetRecentlyAddedItems /
GetCollectionItemsForDashboard et al.) never carried the
visible_library_ids clause, and the collection-detail endpoint (mobile
View All) exposes the full section query results. Clause-protected
surfaces (media list, search, series) pass, proving the test itself
isn't a false positive.
Implementation-agnostic on purpose: it asserts the user-level invariant
through the public API, so it stays valid when RLS replaces the
per-query clauses and catches any future regression regardless of how
the code is written. Also pins: hidden library_id=403 (never silently
passed), item detail denial, and that the hide is USER-scoped (admin
still sees the hidden library).
Test-lesson encoded: cleanup ordering is LIFO — the pool Close must be
registered BEFORE fixture cleanups, or library deletes run against a
closed pool and fail silently into ignored errors.