Files
RenewCMS/features/public_article_read.qa.md

5.3 KiB

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: <true|false>} as each case needs.

HTTP checks

  1. GET /v1/article/1 (online), no cookie -> 200, decode body, assert id == 1.
  2. GET /v1/article/2 (offline), no cookie -> 404, body "Article not found\n".
  3. GET /v1/article/9999 (no article with that id), no cookie -> 404, body "Article not found\n".
  4. GET /v1/article/2, cookie for user 1 with IsVerified: true -> 200, decode body, assert id == 2.
  5. GET /v1/article/1, cookie for user 1 with IsVerified: true -> 200, id == 1.
  6. GET /v1/article/9999, cookie for user 1 with IsVerified: true -> 404, body "Article not found\n".
  7. GET /v1/article/2, cookie for user 1 with IsVerified: false -> 404, body "Article not found\n" (never 403).
  8. 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).
  9. GET /v1/article/not-a-number, no cookie -> 400, body apperr.IdUint32ErrorMessage + "\n".
  10. 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.
  11. 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)

  1. 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.
  2. 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.