Files
RenewCMS/features/delete_article_cascades_images.qa.md
T

4.4 KiB

QA procedure: delete_article_cascades_images

Package: test (black-box, extending article_test.go).

Setup shared by every case

  1. Call validator.InitValidator() once.
  2. Reuse mockArticleRepo (test/article_test.go) and mockImageRepo (test/image_test.go) as-is. Extend mockArticleRepo seeding so a seeded article's Images []*image.Image can hold pointers into the same mockImageRepo.images map used by the test, so deleting through the article side and asserting through the image side observe the same state.
  3. Add a call counter (or reuse the existing failSwitch/call-tracking pattern already in test/) on mockImageRepo.Delete so a scenario can assert it was never called, and reuse mockImageRepo.fail (the existing failSwitch) to make Delete fail for a specific image id.
  4. Wire the real use cases over the fakes: useCases.NewCreateArticleUseCase, useCases.NewGetArticleUseCase, useCases.NewListArticlesUseCase, useCases.NewUpdateArticleUseCase, and the new two-argument useCases.NewDeleteArticleUseCase(articleRepo, imageRepo). Build article.NewHandler(...) with the same constructor shape it already has (the use case's internal dependency count changes, not the handler's).
  5. Build a test jwtauth.New("HS256", []byte("test-secret"), nil).
  6. Mount the routes on a fresh chi.NewRouter() mirroring api/router.go's current shape: GET / under jwtauth.Verifier only; GET /{id}, POST /, DELETE /{id}, PATCH /{id}, PATCH /{id}/publish, PATCH /{id}/unpublish under jwtauth.Verifier + jwtauth.Authenticator.
  7. Mint a valid jwt cookie for user id 1: token.Encode(map[string]any{"user_id": uint32(1)}), set as the jwt cookie on the relevant httptest.NewRequest.

HTTP checks

  1. Seed imageRepo.images[10] and imageRepo.images[11], then seed articleRepo.articles[1] = article.Article{ID: 1, Title: "Backyard Renovation Update", Images: []*image.Image{imageRepo.images[10], imageRepo.images[11]}} (pointers into the same map). Send DELETE /v1/article/1 with the valid cookie -> status 204; assert articleRepo.articles no longer has key 1; assert imageRepo.images no longer has keys 10 or 11.
  2. Seed articleRepo.articles[2] = article.Article{ID: 2, Title: "Draft: Kitchen Remodel"} with a nil/empty Images slice. Send DELETE /v1/article/2 with the valid cookie -> status 204; assert articleRepo.articles no longer has key 2; assert the image-delete call counter is 0.
  3. Empty articleRepo, empty imageRepo. Send DELETE /v1/article/9999 with the valid cookie -> status 404, body "Article not found\n"; assert the image-delete call counter is 0.
  4. Seed imageRepo.images[10] and articleRepo.articles[1] with Images holding image id 10, per step 8's pattern. Send DELETE /v1/article/1 with no cookie -> status 401; assert articleRepo.articles[1] is still present and imageRepo.images[10] is still present.
  5. Seed imageRepo.images[20] and imageRepo.images[21], then seed articleRepo.articles[3] = article.Article{ID: 3, Title: "Kitchen Progress Photos", Images: []*image.Image{imageRepo.images[20], imageRepo.images[21]}} (image 20 before image 21 in the slice). Arm imageRepo.fail so Delete returns an error only when called with id 20 (leave id 21 unarmed). Send DELETE /v1/article/3 with the valid cookie -> status 500, body "Failed to delete article\n"; assert articleRepo.articles[3] is still present; assert imageRepo.images[21] is still present (never reached because the delete aborted on image 20 first).

Manual verification (not exercised by the Go test)

  • Real cascading delete against a real SQLite file and real UPLOAD_DIR: confirm that after DELETE /v1/article/{id} on a real running instance, the uploaded image files under UPLOAD_DIR for that article are gone from disk and the images table no longer holds those rows, and that no orphaned rows are left behind in the article_images join table. Not exercised by task test (fakes only); requires task dev or a built task build binary with a scratch DB/upload dir.
  • SPA-visible behaviour: confirm the back-office article deletion flow (if/when one exists) reflects that any images shown for that article are gone too. No such UI exists yet as of this slice.
  • No SMTP involvement.