feat(podcasts): widen reader_settings key ceiling — item 2.1 foundation

reader_settings.setting_key was VARCHAR(50), and the composite
podcast_order:<uuid> key already sits at exactly 50 chars — zero
headroom. The deferred queue-editor idea (plan §0 item 2b) and any
future per-feed/per-collection preference key would have been forced
to payload-stuff inside the order row by a column width. Composite
prefix:<uuid> keys are the norm now (§10 R5), so the ceiling widens
to VARCHAR(120): idempotent in-place ALTER (varchar widening is
metadata-only, no table rewrite), re-runs harmlessly on every boot
per the schema convention.

No new episode-membership tables: collection_items already carries
ordered playlist membership (position, A2c) against media_items,
which podcast episodes ARE — episodes join collections through the
existing table.

Pinned by TestReaderSettingsCompositeKeyHeadroom: a 57-char composite
key (the podcast_custom_order:<uuid> shape) upserts and reads back.
Under the old width this fails; sqlc codegen is byte-identical.
This commit is contained in:
John O'Keefe committed 2026-10-30 15:29:34 -04:00
1 parent f7ba4d24d6
commit 7bf0b1257d
2 files changed
+36 -1

No files matched your search

+28
View File
@@ -471,6 +471,34 @@ func TestDeleteMediaItemPodcastGuard(t *testing.T) {
})
}
// ---- reader_settings key width (§10 R5, item 2.1): composite
// prefix:<uuid> keys have headroom past the old 50-char ceiling ----
func TestReaderSettingsCompositeKeyHeadroom(t *testing.T) {
setup := setupTestServer(t)
userID := getRegularUserID(t, setup.DB)
// 57 chars — the deferred podcast_custom_order:<uuid> shape. Under
// the old VARCHAR(50) this upsert fails; the widening (item 2.1)
// makes it ordinary preference state.
key := "podcast_custom_order:01921a2b-3c4d-5e6f-7a8b-9c0d1e2f3a4b"
require.Len(t, key, 57, "sentinel: the pinned key must exceed the old VARCHAR(50)")
err := setup.DB.UpsertUserSettingByKey(seedCtx(), database.UpsertUserSettingByKeyParams{
UserID: pgtype.UUID{Bytes: userID, Valid: true},
SettingKey: key,
SettingValue: []byte(`{"order":"custom"}`),
})
require.NoError(t, err, "composite keys longer than 50 chars must store (VARCHAR(120) headroom)")
val, err := setup.DB.GetUserSettingByKey(seedCtx(), database.GetUserSettingByKeyParams{
UserID: pgtype.UUID{Bytes: userID, Valid: true},
SettingKey: key,
})
require.NoError(t, err)
require.JSONEq(t, `{"order":"custom"}`, string(val))
}
// ---- download worker: state machine, idempotency, file layout ----
func TestPodcastDownloadWorker(t *testing.T) {