feat: articles pagination
This commit is contained in:
1 parent
a2d224240c
commit
73f4b539fe
45 files changed
+3528
-317
No files matched your search
@@ -0,0 +1,44 @@
|
||||
# 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.
|
||||
Reference in new issue
Block a user