Files
RenewCMS/features/media_pagination_api.qa.md
T

4.2 KiB

QA: Paginated media listing (API)

HTTP-level (Go, test/, task test)

Build media.NewHandler(...) over the real ListMediaUseCase and in-memory fakes of image.Repository, article.Repository, user.Repository and image.VariantFileRepository (extend the existing media fakes, keeping type names unique across files). Mount on a fresh chi.NewRouter() mirroring the media router in api/router.go (GET / inside jwtauth.Verifier + jwtauth.Authenticator, test jwtauth.New("HS256", ...), valid jwt cookie with a user_id claim of a verified user). Call validator.InitValidator(). No live server, no SQLite file.

Helper: decode into {items []map, total, page, size int, counts {all, unused, duplicates}}; also assert the raw JSON contains "items":[ (never null) and does not contain next_cursor.

  1. Bare request: 30 images -> object, page 1, size 25, total 30, 25 items, no next_cursor.
  2. Defaults table: page=2, page=3, size=5, size=50, size=100 on 60 images.
  3. Partition: 12 images, pages 1-3 of size 5 -> 5, 5, 2; 12 distinct ids.
  4. Order: equal-timestamp case -> ids 3,2,1. Sort table (newest, oldest, name, largest, total_size): 12 images, three pages, concatenation equals the full ordered list, no repeat. Ties break on id.
  5. Filter/variants/q: unused -> total 6, counts unchanged; variants=with -> total 3; q=wall&size=5&page=2 -> total 7, 2 items, counts.all 27.
  6. Hash: hash=H&page=1&size=100 -> 3 copies; hash=H alone -> page 1, size 25, 3 items.
  7. Out of range: page=99&size=5 -> 200, raw "items":[], total 12.
  8. Empty library -> items [], total 0, counts 0/0/0.
  9. counts library-wide: q=zzzz&page=3&size=5 -> items [], total 0, counts.all 30.
  10. cursor/limit ignored: rows cursor=abc, limit=5, limit=0, limit=abc, a valid-looking old cursor -> 200, page 1, size 25, 25 items.
  11. Invalid table (all rows): each -> 400, body exactly "The media query is invalid.", repository call counters stay 0.
  12. Repository failure -> 500 "Internal server error".
  13. Missing or invalid jwt cookie -> the auth group's 401, use case counter 0.
  14. Use-case tests: ListMediaUseCase.List returns items, total, page, size and counts; slices after sorting; propagates a repository error (errors.Is on a sentinel); Describe is unchanged; ListQuery.Validate rejects page < 1 and sizes outside 5/25/50/100 with ErrInvalidQuery. image.ListQuery.Cursor and .Limit, MediaPage.NextCursor, encodeCursor and decodeCursor no longer exist.
  15. Existing tests that read next_cursor, send limit or cursor, or call NewListQuery (media tests in test/, features/media_library.qa.md, features/media_browse.qa.md) are amended to the envelope; list them in the diff.

Docs (verified by reading the diff)

  • docs/api.md lines 99-103: replace cursor and limit with page (default 1) and size (5, 25, 50, 100; default 25); state the envelope (items, total, page, size, counts), that counts stays library-wide, that invalid values answer 400 "The media query is invalid.", and a "Breaking change" note: next_cursor is gone, cursor and limit are ignored.
  • docs/api/*.md: grep for /v1/media/ and cursor; update the media listing section if one exists.
  • web/src/data/apiCatalogue.ts med-list: add page, size, q, filter, sort, variants, hash query entries, the 400 error, and a paged example (total, page, size, no next_cursor). Remove the next_cursor rule (line ~129, with its French entry in web/src/i18n/messages.ts line ~1606) if it is only used by media. Integration specs asserting the catalogue are amended.
  • Flag for the user (read-only to agents): docs/design/screens/integration.md and docs/design/source/IntegrationPage.dc.html (med-list with no params), docs/design/screens/media.md, routes.md, change-list.md item 13b, README.md line 92 and screens/media-picker.md: all still say "array body" and "X-Total-Count".
  • A release note under docs/releases/ is the maintainer's call: flag it.

Manual verification

  • Real SQLite file via task dev: seed 60 images, check page=2&size=25, ordering by each sort and counts; the same on MySQL/PostgreSQL if available (no live DB in tests).
  • An authenticated curl returns the envelope with no next_cursor.