feat(collections): the rules-Apply path seeds the reversed default too

The creation seed (d56031f3) covered born-smart collections; a STATIC
collection that becomes smart through the rules editor's Apply landed
without any curated order — its dropdown stayed "Feed order" while
every other podcast smart collection opened reversed. Same decision,
same guardrails (2026-11-01 user approved closing this gap): a PUT
carrying an episodes-scope tree seeds chain_order=reverse only when
neither the request NOR the stored view_settings names one — a
deliberate native (client-sent or saved) is never clobbered, and
books-scope trees seed nothing.

Pins: apply-seeds-when-unordered, respects-stored-native,
respects-client-native (TestSmartCollectionBornReversed now covers
both seed paths); changelogged in contract_pins.
This commit is contained in:
John O'Keefe committed 2026-11-01 13:44:23 -05:00
1 parent 96a40f7d88
commit 57bc0e9dd4
3 files changed
+69

No files matched your search

+37
View File
@@ -2414,6 +2414,43 @@ func TestSmartCollectionBornReversed(t *testing.T) {
assert.NotContains(t, string(body), `"chain_order"`,
"static collections get no chain_order seed")
})
// The creation seed's twin (2026-11-01): APPLYING an episodes tree
// to a collection with no curated order seeds reverse — a static
// collection becoming smart lands with the same default a
// born-smart one gets. Explicit values (client-sent or stored) are
// never clobbered.
t.Run("apply_path_seeds_when_unordered", func(t *testing.T) {
id := create(t, `{"name":"test BornRev Apply `+t.Name()+`"}`)
code, body := podcastAuthed(t, setup, regToken, "PUT", "/api/collections/"+id,
`{"name":"test BornRev Apply `+t.Name()+`","filter_tree":{"scope":"episodes","mode":"all","children":[]}}`)
require.Equal(t, http.StatusCreated, code, "%s", string(body))
_, body = podcastAuthed(t, setup, regToken, "GET", "/api/collections/"+id, "")
assert.Contains(t, string(body), `"chain_order":"reverse"`,
"applying an episodes tree to an unordered collection seeds reverse")
})
t.Run("apply_path_respects_stored_native", func(t *testing.T) {
id := create(t, `{"name":"test BornRev KeepNative `+t.Name()+`","view_settings":{"chain_order":"native"}}`)
code, body := podcastAuthed(t, setup, regToken, "PUT", "/api/collections/"+id,
`{"name":"test BornRev KeepNative `+t.Name()+`","filter_tree":{"scope":"episodes","mode":"all","children":[]}}`)
require.Equal(t, http.StatusCreated, code, "%s", string(body))
_, body = podcastAuthed(t, setup, regToken, "GET", "/api/collections/"+id, "")
assert.Contains(t, string(body), `"chain_order":"native"`,
"a stored explicit chain_order is never clobbered by the apply seed")
assert.NotContains(t, string(body), `"chain_order":"reverse"`)
})
t.Run("apply_path_respects_client_native", func(t *testing.T) {
id := create(t, `{"name":"test BornRev ClientNative `+t.Name()+`"}`)
code, body := podcastAuthed(t, setup, regToken, "PUT", "/api/collections/"+id,
`{"name":"test BornRev ClientNative `+t.Name()+`","filter_tree":{"scope":"episodes","mode":"all","children":[]},"view_settings":{"chain_order":"native"}}`)
require.Equal(t, http.StatusCreated, code, "%s", string(body))
_, body = podcastAuthed(t, setup, regToken, "GET", "/api/collections/"+id, "")
assert.Contains(t, string(body), `"chain_order":"native"`,
"the client's explicit chain_order wins over the apply seed")
assert.NotContains(t, string(body), `"chain_order":"reverse"`)
})
}
// ---- label endpoint: names id-kind values for client-held trees ----