# QA procedure: public_article_read Package: `test` (black-box, extending `article_test.go`, `verified_account_required_http_test.go` and `not_found_errors_test.go`). ## Setup shared by every case 1. Call `validator.InitValidator()` once. 2. Reuse `mockArticleRepo` (`test/article_test.go`) and `mockUserRepo` (`test/verified_account_required_http_test.go`) as-is; no fake changes expected. 3. Wire the real use cases over the fakes: `useCases.NewGetArticleUseCase`, `useCases.NewGetUserUseCase`, plus `NewCreateArticleUseCase`/`NewListArticlesUseCase`/`NewDeleteArticleUseCase`/ `NewUpdateArticleUseCase` for the regression routes. Build `article.NewHandler(create, get, list, delete, update, getUser)`. 4. Build a test `jwtauth.New("HS256", []byte("test-secret"), nil)`. 5. Mount routes on a fresh `chi.NewRouter()` mirroring the amended `api/router.go` shape: - `r.Route("/v1", func(r chi.Router) { r.Route("/article", func(r chi.Router) { ... }) })` - `r.Use(jwtauth.Verifier(token))` on the whole `/article` group - `r.Get("/", articleHandler.ListArticles)` and `r.Get("/{id}", articleHandler.GetArticle)` directly under that Verifier-only group - a nested `r.Group` adding `jwtauth.Authenticator(token)` then `middleware.RequireVerifiedUser(getUserUseCase)`, containing `r.Post("/", ...)`, `r.Delete("/{id}", ...)`, `r.Patch("/{id}", ...)`, `r.Patch("/{id}/publish", ...)`, `r.Patch("/{id}/unpublish", ...)` No live server. 6. Mint a valid jwt cookie for user id 1 via `token.Encode(map[string]any{"user_id": uint32(1)})`. 7. Seed the article repo directly, e.g. `repo.articles[1] = article.Article{ID: 1, Title: "Backyard Renovation Update", IsOnline: true}` and `repo.articles[2] = article.Article{ID: 2, Title: "Draft: Kitchen Remodel", IsOnline: false}`. 8. Seed the user repo with `repo.users[1] = user.User{ID: 1, IsVerified: }` as each case needs. ## HTTP checks 9. `GET /v1/article/1` (online), no cookie -> 200, decode body, assert `id == 1`. 10. `GET /v1/article/2` (offline), no cookie -> 404, body `"Article not found\n"`. 11. `GET /v1/article/9999` (no article with that id), no cookie -> 404, body `"Article not found\n"`. 12. `GET /v1/article/2`, cookie for user 1 with `IsVerified: true` -> 200, decode body, assert `id == 2`. 13. `GET /v1/article/1`, cookie for user 1 with `IsVerified: true` -> 200, `id == 1`. 14. `GET /v1/article/9999`, cookie for user 1 with `IsVerified: true` -> 404, body `"Article not found\n"`. 15. `GET /v1/article/2`, cookie for user 1 with `IsVerified: false` -> 404, body `"Article not found\n"` (never 403). 16. `GET /v1/article/1` and `GET /v1/article/2`, cookie named `jwt` whose value is an arbitrary non-token string (e.g. `"not-a-real-token"`) -> 200 for id 1, 404 for id 2 (same as step 9/10, proving the invalid cookie falls back to public visibility). 17. `GET /v1/article/not-a-number`, no cookie -> 400, body `apperr.IdUint32ErrorMessage + "\n"`. 18. Regression, no cookie: `POST /v1/article/` -> 401; `DELETE /v1/article/1` -> 401; `PATCH /v1/article/1` -> 401; `PATCH /v1/article/1/publish` -> 401; `PATCH /v1/article/1/unpublish` -> 401. 19. Configure the fake repo's `failSwitch` so the next `Get` call returns `errRepositoryFailure` (`failNext("Get")`). `GET /v1/article/1` with no cookie -> 500, body `"Internal server error\n"`. ## Required amendments to existing tests/specs (part of this slice, not optional cleanup) 20. `test/verified_account_required_http_test.go`: move `r.Get("/{id}", articleHandler.GetArticle)` in `newVerifiedAccountRequiredRouter` out of the `Authenticator` + `RequireVerifiedUser` group into the outer Verifier-only group (mirroring step 5 above); remove the `{http.MethodGet, "/v1/article/1", ""}` row from `protectedRouteCases` and the `{http.MethodGet, "/v1/article/1"}` row from `noCookieCases` in `TestVerifiedAccountRequiredHttp`, since that route no longer produces 403 or 401. 21. `test/not_found_errors_test.go`: move `r.Get("/{id}", articleHandler.GetArticle)` in `newNotFoundErrorsRouter` the same way; change the `"Fetching an offline article still answers 404 with the same message"` subtest to use an unverified or anonymous caller (it currently uses `newVerifiedUserRepo()`, which will now 200 instead of 404); replace `"The article route still requires authentication before existence is even checked"` with an assertion that an anonymous request for an unknown id now answers 404 (covered by step 11 above, so this subtest can instead be deleted in favor of the new test file, or repurposed to check `GET /v1/article/1` with no cookie still answers 200 as a cross-file regression note). `features/verified_account_required.feature` and `features/not_found_errors.feature` get the corresponding Examples-table/scenario edits described in this feature's Background. ## Manual verification (not exercised by the Go test) - SPA-visible behaviour: confirm the public front-office single-article view (unauthenticated) can open a published article and 404s on a draft URL, and the authenticated back-office view can open its own unpublished draft for editing, against the real embedded frontend and a real SQLite file. Not exercised by `task test`; requires `task dev` or a built `task build` binary. - No SMTP involvement.