fix(series): Date Added respects the direction toggle; bump bundle stamp

Two follow-ups from live testing:

- "recent" is BOTH a base toolbar key and a full registry key (where it
  means desc). ResolveSeriesSort's full-key passthrough ran first, so
  sort=recent&dir=asc silently resolved back to desc — the direction
  chevron flipped but Date Added could never sort ascending. "recent"
  now composes with the direction before the passthrough, unknown bases
  fall to the default entirely (a hostile dir can't ride an unknown
  sort), and the composition table is unit-pinned plus an HTTP-level
  row in the series filter test.

- The header search "still broken" report was a stale-cache artifact:
  the fixed code shipped but main.js kept its ?v=7 stamp, so browsers
  with a cached copy never refetched (fresh-profile e2e passed while
  the user's browser didn't — heuristically cached module). Stamp bumped
  to ?v=8 in header + docs; the e2e suite re-verified against the
  rebuilt server.
This commit is contained in:
John O'Keefe committed 2026-10-27 22:55:11 -04:00
1 parent ffbcc53083
commit fd9292e6f2
7 files changed
+56 -5

No files matched your search

+37
View File
@@ -320,3 +320,40 @@ func TestSeriesNumberFloatOrderingValues(t *testing.T) {
t.Fatal("1 must sort before 1.5")
}
}
// ResolveSeriesSort composes the toolbar's base key + direction into
// the registry key the SQL cascade implements. "recent" is both a base
// key AND a full registry key (meaning desc) — the composition must
// run BEFORE the full-key passthrough, or Date Added can never sort
// ascending (2026-10-27 regression the direction toggle exposed).
func TestResolveSeriesSortComposition(t *testing.T) {
cases := []struct {
sort, dir string
wantKey, wantD string
}{
// THE regression: Date Added + toggle up.
{"recent", "asc", "recent_asc", "asc"},
{"recent", "", "recent", "desc"},
{"recent", "desc", "recent", "desc"},
{"recent", "bogus", "recent", "desc"},
// Full keys (back-compat URLs / JSON API) win as-is.
{"name_desc", "", "name_desc", "desc"},
{"name_asc", "desc", "name_asc", "asc"},
// Base keys compose; default direction applies when dir absent.
{"name", "", "name_asc", "asc"},
{"name", "desc", "name_desc", "desc"},
{"books", "", "books_desc", "desc"},
{"books", "asc", "books_asc", "asc"},
{"last_read", "asc", "last_read_asc", "asc"},
// Unknown/empty → default.
{"", "", "recent", "desc"},
{"bogus", "asc", "recent", "desc"},
}
for _, tc := range cases {
key, dir := ResolveSeriesSort(tc.sort, tc.dir)
if key != tc.wantKey || dir != tc.wantD {
t.Errorf("ResolveSeriesSort(%q, %q) = (%q, %q), want (%q, %q)",
tc.sort, tc.dir, key, dir, tc.wantKey, tc.wantD)
}
}
}
+12 -1
View File
@@ -91,6 +91,15 @@ func ResolveSeriesSort(sort, dir string) (string, string) {
if dir != "asc" && dir != "desc" {
dir = ""
}
// "recent" is BOTH a base toolbar key and a registry key (it means
// desc). Handle it before the full-key check or dir=asc is silently
// swallowed and the direction toggle can never flip Date Added.
if sort == "recent" {
if dir == "asc" {
return "recent_asc", "asc"
}
return "recent", "desc"
}
// Full registry key straight from the URL wins as-is.
if seriesSortKeys[sort] {
return sort, seriesDirOf(sort)
@@ -101,7 +110,9 @@ func ResolveSeriesSort(sort, dir string) (string, string) {
}
defaultDir, known := seriesBaseSorts[base]
if !known {
base, defaultDir = "recent", "desc"
// Unknown base: the whole thing falls to the default — an
// unhandled sort must not sneak a direction past the registry.
return "recent", "desc"
}
if dir == "" {
dir = defaultDir