95 lines
5.0 KiB
Gherkin
95 lines
5.0 KiB
Gherkin
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
|