Files
RenewCMS/features/articles_pagination_api.qa.md
T

4.9 KiB

QA: Paginated article listing (API)

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

Build article.NewHandler(...) over the real ListArticlesUseCase and a fake article.Repository (extend mockArticleRepo in test/article_test.go, keeping type names unique across files). The fake implements the interface contract: paging (Page/Size 0 = unpaged), q/status filtering, ordering updated_at DESC then id DESC, total, and the visibility counts. Mount on a fresh chi.NewRouter() mirroring NewArticleRouter in api/router.go (GET / under jwtauth.Verifier only, test jwtauth.New("HS256", ...)). Call validator.InitValidator(). Use a user fake for the verified, unverified and missing cases, as in test/public_article_listing_test.go. No live server, no SQLite file.

Helper: decode the body into {items []map, total, page, size int, counts {all, online, draft}} and also assert the raw JSON contains "items":[ (never null).

  1. Bare request: 30 online articles, GET /v1/article/ -> object (not array), page 1, size 25, total 30, 25 items, counts 30/30/0.
  2. Defaults table (page=2, page=3, size=5, size=50) on 60 articles.
  3. Order: ids 3,2,1 for the equal-timestamp case with no sort parameter (default updated_at:desc). Sort table: the 8 whitelisted values on the mixed-case fixture (apple/Banana/Cherry), asserting the item ids. The fake repo implements the same contract: key, direction, then id desc. 3a. Equal titles with sort=title:asc -> ids 3,2,1. 3b. Sort before paging: 12 articles, ?sort=title:desc&size=5, three pages, no overlap. 3c. Sort + q + status combination: ordered titles, total 3, counts unchanged. 3d. Use-case test: ListArticlesUseCase passes the typed sort through to Filters and defaults to updated_at desc when absent. Only whitelisted values reaching Order in the repository is a code-review item; the handler test covers the 400 side.
  4. Partition: 12 articles, pages 1-3 of size 5 -> 5,5,2 and 12 distinct ids.
  5. Public: 5 online + 3 offline -> total 5, counts 5/5/0, no offline item.
  6. Verified: same data -> total 8, counts 8/5/3, three offline items.
  7. Malformed cookie and unverified user -> same as public.
  8. Out of range: ?page=99&size=5 -> 200, raw "items":[], total 12, counts.all 12.
  9. Empty repository -> items [], total 0, counts 0/0/0.
  10. Status table (verified): online -> total 4, draft -> total 2, counts always 6/4/2.
  11. Public status=draft -> items [], total 0, counts 4/4/0.
  12. q=PLÂSTER (accent and case insensitive) -> total 1, counts.all 2. q=zzzz -> empty, counts unchanged.
  13. q+status+page combination: total 7, 2 items on page 2, counts.draft 7.
  14. Invalid table (all rows incl. page=, page=1.5, status=archived, status=, 201-char q, and the 7 sort rows: sort=title, sort=title:up, sort=body:asc, sort=, sort=updated_at, sort=title:asc;DROP TABLE, sort=TITLE:ASC): each -> 400 with the exact BodyErrorMessage text, and the fake's FindByFilters/count counters stay 0.
  15. ?page=0&format=pdf -> 400 with the format message.
  16. ?size=5&format=html -> every item body_format "html".
  17. Fake repo failure (existing failNext("FindByFilters")) -> 500 "Failed to retrieve articles".
  18. Use-case tests: the paged list method returns items, total and counts, propagates a repo error (errors.Is on a sentinel), and the unpaged Filters{} call used by purgePlan.go and ListMedia.go still returns every article (existing tests keep passing).
  19. Existing tests that decode GET /v1/article/ as an array (test/public_article_listing_test.go, test/article_test.go, test/use_case_baseline_test.go, features/articles_list.qa.md item 1) are amended to the envelope. List them in the diff.

Docs (verified by reading the diff)

  • docs/api/articles.md: new "Pagination, search, sort and counts" section (parameters including sort values, default and id tie-break, envelope, ordering, errors) and a "Breaking change" note, like the existing format note: GET /v1/article/ no longer returns a bare array; read items.
  • web/src/data/apiCatalogue.ts art-list: add page, size, q, status, sort query entries, the 400 error, and a paged JSON example; the Integration specs that assert this catalogue are amended.
  • docs/design/screens/integration.md is read-only for agents: flag that its text "GET /v1/article/" example needs the user's update.
  • A release note under docs/releases/ is the maintainer's call: flag it.

Manual verification

  • Real SQLite file via task dev: seed 60 articles, check ?page=2&size=25, ordering, q accent-insensitivity, total and counts. MySQL and PostgreSQL parity of the COUNT / LIMIT-OFFSET / LIKE queries is manual (no live DB in tests).
  • Sort by title on a live SQLite file, and on MySQL/PostgreSQL if available: title collation differs (binary, case-insensitive, locale), so mixed-case and accented ordering may differ between databases. Accepted; note any difference.
  • An anonymous curl against a running server returns the envelope.