* feat(collections): surface server collections on the user Collections tab The user-facing Collections tab only showed personal collections, which are usually empty — leaving most users with a confusingly blank page. Server (admin-curated) collections were reachable only inside each individual library's tab. Add a new GET /collections/server endpoint that aggregates visible library collections across every accessible library (honoring access scope, capped per library with a total_count for a See all link), and restructure Collections.tsx into two titled sections: Your collections (personal) and Server collections (horizontal teaser rows per library, linking into each library's Collections tab). Extract the shared CollectionPosterCard so the per-library grid and the new rows share one implementation. * fix(collections): match server-collections loading skeleton to row layout The Server collections section renders as one horizontal teaser row per library, but the loading skeleton showed a poster grid — so data arriving visibly reflowed the page from a grid into rows. Mirror the final layout (section header + per-library rows of poster cards) in the skeleton, and drop the now-unused COLLECTION_POSTER_GRID_CLASSES import. Addresses CodeRabbit review comment on PR #156. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * Align server collections with shared carousel behavior - Add opt-out edge padding to reusable media carousels - Render server collection rows with shared carousel controls and spacing --------- Co-authored-by: Quick <31828688+Quick104@users.noreply.github.com> Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
48 lines
1.5 KiB
Go
48 lines
1.5 KiB
Go
package handlers
|
|
|
|
import (
|
|
"testing"
|
|
|
|
"github.com/Silo-Server/silo-server/internal/models"
|
|
)
|
|
|
|
func TestCapServerCollections(t *testing.T) {
|
|
makeN := func(n int) []*models.LibraryCollection {
|
|
out := make([]*models.LibraryCollection, n)
|
|
for i := range out {
|
|
out[i] = &models.LibraryCollection{ID: string(rune('a' + i%26))}
|
|
}
|
|
return out
|
|
}
|
|
|
|
t.Run("under cap returns all, total equals length", func(t *testing.T) {
|
|
in := makeN(serverCollectionsPerLibraryCap - 3)
|
|
got, total := capServerCollections(in)
|
|
if total != serverCollectionsPerLibraryCap-3 {
|
|
t.Fatalf("total = %d, want %d", total, serverCollectionsPerLibraryCap-3)
|
|
}
|
|
if len(got) != len(in) {
|
|
t.Fatalf("len(got) = %d, want %d", len(got), len(in))
|
|
}
|
|
})
|
|
|
|
t.Run("at cap returns all", func(t *testing.T) {
|
|
in := makeN(serverCollectionsPerLibraryCap)
|
|
got, total := capServerCollections(in)
|
|
if total != serverCollectionsPerLibraryCap || len(got) != serverCollectionsPerLibraryCap {
|
|
t.Fatalf("got len=%d total=%d, want both %d", len(got), total, serverCollectionsPerLibraryCap)
|
|
}
|
|
})
|
|
|
|
t.Run("over cap trims slice but reports full total", func(t *testing.T) {
|
|
in := makeN(serverCollectionsPerLibraryCap + 17)
|
|
got, total := capServerCollections(in)
|
|
if len(got) != serverCollectionsPerLibraryCap {
|
|
t.Fatalf("len(got) = %d, want %d", len(got), serverCollectionsPerLibraryCap)
|
|
}
|
|
if total != serverCollectionsPerLibraryCap+17 {
|
|
t.Fatalf("total = %d, want %d", total, serverCollectionsPerLibraryCap+17)
|
|
}
|
|
})
|
|
}
|