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).
- Bare request: 30 online articles, GET /v1/article/ -> object (not array), page 1, size 25, total 30, 25 items, counts 30/30/0.
- Defaults table (page=2, page=3, size=5, size=50) on 60 articles.
- 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.
- Partition: 12 articles, pages 1-3 of size 5 -> 5,5,2 and 12 distinct ids.
- Public: 5 online + 3 offline -> total 5, counts 5/5/0, no offline item.
- Verified: same data -> total 8, counts 8/5/3, three offline items.
- Malformed cookie and unverified user -> same as public.
- Out of range: ?page=99&size=5 -> 200, raw
"items":[], total 12, counts.all 12. - Empty repository -> items [], total 0, counts 0/0/0.
- Status table (verified): online -> total 4, draft -> total 2, counts always 6/4/2.
- Public status=draft -> items [], total 0, counts 4/4/0.
- q=PLÂSTER (accent and case insensitive) -> total 1, counts.all 2. q=zzzz -> empty, counts unchanged.
- q+status+page combination: total 7, 2 items on page 2, counts.draft 7.
- 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. - ?page=0&format=pdf -> 400 with the format message.
- ?size=5&format=html -> every item body_format "html".
- Fake repo failure (existing
failNext("FindByFilters")) -> 500 "Failed to retrieve articles". - 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 bypurgePlan.goandListMedia.gostill returns every article (existing tests keep passing). - 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 includingsortvalues, 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; readitems.web/src/data/apiCatalogue.tsart-list: addpage,size,q,status,sortquery entries, the 400 error, and a paged JSON example; the Integration specs that assert this catalogue are amended.docs/design/screens/integration.mdis 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
titleon 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.