refac: authenticated user can upload an image to an existing article and receives its public location; an unknown article returns 404 and a disallowed type returns 400
This commit is contained in:
1 parent
5992c8598b
commit
e474f03d91
5 files changed
+563
-14
No files matched your search
@@ -0,0 +1,94 @@
|
||||
Feature: Uploading an image to an article
|
||||
|
||||
As an authenticated RenewCMS user managing the CMS
|
||||
I want to attach an image file to an existing article and get back the
|
||||
image's public location
|
||||
So that an unknown article answers 404 the same way every other article
|
||||
write does, without ever creating an orphaned image, and a disallowed
|
||||
content type is rejected with 400 before it becomes part of the
|
||||
article, without a maintainer needing to trust the client to send a
|
||||
sane file
|
||||
|
||||
Background:
|
||||
Given internal/infrastructure/useCases.CreateImageUseCase.CreateImage
|
||||
already checks the uploaded content type against the allowed list
|
||||
(image/png, image/jpeg, image/webp, image/svg+xml) and the size
|
||||
against the 10485760-byte maximum before calling image.Repository.Create,
|
||||
returning image.ErrImageTypeNotAllowed or image.ErrImageTooLarge and
|
||||
calling the repository not at all when either check fails
|
||||
And internal/infrastructure/persistence's ArticleRepository.AddImage
|
||||
already returns article.ErrArticleNotFound when the article id doesn't
|
||||
match any row, and image.ErrImageNotFound when the article exists but
|
||||
the image id doesn't, both asserted with errors.Is
|
||||
And before this slice, api/handlers/image.Handler.PostImage existed and
|
||||
was wired to CreateImageUseCase and UpdateArticleUseCase.AddImage, but
|
||||
no route called it, and every error it got back from either use case
|
||||
fell through to a blanket 500
|
||||
And this slice mounts POST /v1/article/{id}/image in api/router.go, in
|
||||
the same jwtauth Verifier + Authenticator group as the other article
|
||||
write routes, and changes the upload flow so that the article's
|
||||
existence is confirmed before the image is created, so that an
|
||||
unknown article id never leaves an orphaned image behind
|
||||
And this slice changes PostImage's error handling so that errors.Is(err,
|
||||
image.ErrImageTypeNotAllowed) or errors.Is(err, image.ErrImageTooLarge)
|
||||
answers 400 with the sentinel's own message, errors.Is(err,
|
||||
article.ErrArticleNotFound) answers 404 with body
|
||||
apperr.ArticleNotFoundErrorMessage, and any other error still falls
|
||||
back to a generic 500
|
||||
And PostImage reads the uploaded file from the multipart form field
|
||||
"file"
|
||||
And on success PostImage answers 201 with a JSON body {"location": ...}
|
||||
holding the created image's public path, e.g.
|
||||
"/static/uploadedImages/<stored-filename>"
|
||||
|
||||
Scenario: Uploading an allowed image type to an existing article succeeds
|
||||
Given a valid jwt cookie for a user with id 1
|
||||
And the article repository contains an article with id 1 titled
|
||||
"Backyard Renovation Update"
|
||||
When the client sends POST /v1/article/1/image with that cookie and a
|
||||
multipart form field "file" holding a file named "before.png" with
|
||||
content type "image/png"
|
||||
Then the response status is 201
|
||||
And the response body is JSON with a "location" field whose value is
|
||||
"/static/uploadedImages/before.png"
|
||||
And the image repository contains a new image whose Path is
|
||||
"/static/uploadedImages/before.png"
|
||||
And the article repository's article with id 1 has that new image's id
|
||||
in its Images slice
|
||||
|
||||
Scenario: Uploading to an unknown article answers 404 and creates no image
|
||||
Given a valid jwt cookie for a user with id 1
|
||||
And the article repository has no article with id 9999
|
||||
When the client sends POST /v1/article/9999/image with that cookie and a
|
||||
multipart form field "file" holding a file named "before.png" with
|
||||
content type "image/png"
|
||||
Then the response status is 404
|
||||
And the response body is the text "Article not found"
|
||||
And the image repository's Create is never called
|
||||
And no image exists in the image repository
|
||||
|
||||
Scenario: Uploading a disallowed content type answers 400 before the article is touched
|
||||
Given a valid jwt cookie for a user with id 1
|
||||
And the article repository contains an article with id 1 titled
|
||||
"Backyard Renovation Update"
|
||||
When the client sends POST /v1/article/1/image with that cookie and a
|
||||
multipart form field "file" holding a file named "notes.pdf" with
|
||||
content type "application/pdf"
|
||||
Then the response status is 400
|
||||
And the response body is the text
|
||||
"the file must be a PNG, JPEG, WEBP, or SVG image"
|
||||
And the image repository's Create is never called
|
||||
And the article repository's article with id 1 still has an empty
|
||||
Images slice
|
||||
|
||||
Scenario: Uploading an image still requires authentication
|
||||
Given no jwt cookie is sent
|
||||
And the article repository contains an article with id 1 titled
|
||||
"Backyard Renovation Update"
|
||||
When the client sends POST /v1/article/1/image without a cookie and a
|
||||
multipart form field "file" holding a file named "before.png" with
|
||||
content type "image/png"
|
||||
Then the response status is 401
|
||||
And the image repository's Create is never called
|
||||
And the article repository's article with id 1 still has an empty
|
||||
Images slice
|
||||
Reference in new issue
Block a user