Files
silo-server/internal/api/handlers/server_collections_test.go
88aa769fe6 feat(collections): surface server collections on the user Collections tab (#156)
* 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>
2026-06-15 10:51:12 -04:00

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)
}
})
}