test(pins): widen whitelist for the episode-collections JSON shapes (item 2 series)
TestContractPin_CollectionsJSON tripped on the additive keys the episode-collections work introduced — the tripwire worked exactly as designed (additive keys must be whitelisted here AND in docs/contract_pins.md first). - Collections envelope gains rule_match_mode (item 2.4): the smart-collection match semantics on GET /api/collections/:id. - BookInfo gains four STABLE keys (item 2.3): is_episode, feed_title, download_state, hidden — dropped omitempty so every card surface emits the same key set (the viewer_percentage precedent; adapters that predate episode awareness emit zero values). docs/contract_pins.md changelog entry written. Nothing removed, nothing renamed.
This commit is contained in:
1 parent
962f9089e0
commit
bc91ce68d4
3 files changed
+24
-5
No files matched your search
@@ -459,6 +459,8 @@ var bookInfoKeys = []string{
|
|||||||
"media_item_id", "title", "author", "cover_image_path",
|
"media_item_id", "title", "author", "cover_image_path",
|
||||||
"has_conflict", "format_group", "badge",
|
"has_conflict", "format_group", "badge",
|
||||||
"viewer_percentage", "library_name",
|
"viewer_percentage", "library_name",
|
||||||
|
// Episode-collections extras (2.3 widening, every surface emits):
|
||||||
|
"is_episode", "feed_title", "download_state", "hidden",
|
||||||
}
|
}
|
||||||
|
|
||||||
func collectionURL(setup *TestServerSetup, id string) string {
|
func collectionURL(setup *TestServerSetup, id string) string {
|
||||||
@@ -500,6 +502,7 @@ func TestContractPin_CollectionsJSON(t *testing.T) {
|
|||||||
"id", "user_id", "name", "description", "color", "icon",
|
"id", "user_id", "name", "description", "color", "icon",
|
||||||
"auto_assign_rules", "view_settings", "books", "book_count",
|
"auto_assign_rules", "view_settings", "books", "book_count",
|
||||||
"is_smart", "created_at", "up_next",
|
"is_smart", "created_at", "up_next",
|
||||||
|
"rule_match_mode", // item-2.4 widening (docs/contract_pins.md)
|
||||||
}
|
}
|
||||||
|
|
||||||
t.Run("user collection with library_id (badged branch)", func(t *testing.T) {
|
t.Run("user collection with library_id (badged branch)", func(t *testing.T) {
|
||||||
|
|||||||
@@ -468,3 +468,16 @@ never-queued episodes) is stamped from the `podcast_queue_seq` SEQUENCE
|
|||||||
at enqueue time so claim order = enqueue order (the effective listening
|
at enqueue time so claim order = enqueue order (the effective listening
|
||||||
order). Raw-row pins (TestContractPin_MediaListBothBranches →
|
order). Raw-row pins (TestContractPin_MediaListBothBranches →
|
||||||
rawRowKeys) whitelist the key. Nothing removed, nothing renamed.
|
rawRowKeys) whitelist the key. Nothing removed, nothing renamed.
|
||||||
|
|
||||||
|
## 2026-10-30 — episode collections (additive widening, plan item 2/2.3/2.4)
|
||||||
|
|
||||||
|
Two shapes widen, both ADDITIVE — nothing removed, nothing renamed:
|
||||||
|
|
||||||
|
- **Collections envelope** (`GET /api/collections/:id` → envelopeKeys)
|
||||||
|
gains `rule_match_mode` (smart-collection match semantics, 'any'
|
||||||
|
legacy default | 'all'). Static collections emit the default value.
|
||||||
|
- **BookInfo** (every card surface → bookInfoKeys) gains four STABLE
|
||||||
|
keys — `is_episode`, `feed_title`, `download_state`, `hidden` — the
|
||||||
|
episode-collections extras. Adapters that predate episode awareness
|
||||||
|
emit their zero values (`false`/`""`), matching the
|
||||||
|
viewer_percentage precedent (nil-representable, key always present).
|
||||||
@@ -50,11 +50,14 @@ type BookInfo struct {
|
|||||||
// LEFT JOIN missed under RLS (unsubscribed episode, or a
|
// LEFT JOIN missed under RLS (unsubscribed episode, or a
|
||||||
// family-lens hidden book) — the row renders as an "unavailable"
|
// family-lens hidden book) — the row renders as an "unavailable"
|
||||||
// hint instead of silently vanishing. Adapters that don't load
|
// hint instead of silently vanishing. Adapters that don't load
|
||||||
// visibility leave Hidden false (cards stay fully rendered).
|
// visibility leave Hidden false (cards stay fully rendered). The
|
||||||
IsEpisode bool `json:"is_episode,omitempty"`
|
// four keys are NOT omitempty: every BookInfo surface emits the
|
||||||
FeedTitle string `json:"feed_title,omitempty"`
|
// same stable key set (the viewer_percentage precedent —
|
||||||
DownloadState string `json:"download_state,omitempty"`
|
// contract-pin widening, docs/contract_pins.md 2026-10-30).
|
||||||
Hidden bool `json:"hidden,omitempty"`
|
IsEpisode bool `json:"is_episode"`
|
||||||
|
FeedTitle string `json:"feed_title"`
|
||||||
|
DownloadState string `json:"download_state"`
|
||||||
|
Hidden bool `json:"hidden"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// LibraryTypeMap builds libraryID→library-type-name for badge resolution
|
// LibraryTypeMap builds libraryID→library-type-name for badge resolution
|
||||||
|
|||||||
Reference in new issue
Block a user