refac: article title rules are the same in the API request and the domain; an invalid title returns 400 instead of 500
This commit is contained in:
1 parent
ff8cd5f0ec
commit
4278d96882
9 files changed
+456
-7
No files matched your search
@@ -0,0 +1,85 @@
|
||||
Feature: The article title rule is the same in the request and the domain
|
||||
|
||||
As the maintainer of RenewCMS
|
||||
I want PostArticle's title validation and article.CreateArticle's title validation to
|
||||
enforce the identical rule, counted the identical way
|
||||
So that a title the request layer accepts can never be rejected by the domain as a
|
||||
surprise 500, and a domain title rejection that does happen is always reported as a
|
||||
400 with a client-facing apperr message instead of a raw 500
|
||||
|
||||
Background:
|
||||
Given internal/domain/article defines the sentinel article.ErrInvalidTitle
|
||||
And CreateArticleUseCase.CreateArticle rejects a title whose length, counted with
|
||||
utf8.RuneCountInString (runes, not bytes), is below 3 or above 200, returning
|
||||
article.ErrInvalidTitle and storing nothing
|
||||
And api/handlers/article/articleHandler.go's PostArticle.Title carries
|
||||
validate:"required,min=3,max=200", and go-playground/validator's min/max on a
|
||||
string already count runes with utf8.RuneCountInString, so the request tag and the
|
||||
domain check now agree on both the bound (3-200) and the unit (runes)
|
||||
And api/pkg/apperr defines ArticleTitleErrorMessage as "Article title must be
|
||||
between 3 and 200 characters.", a client-facing string distinct from both the raw
|
||||
validator error text and "Failed to create article"
|
||||
And Handler.PostArticle maps errors.Is(err, article.ErrInvalidTitle) from
|
||||
CreateArticleUseCase.CreateArticle to a 400 response with body
|
||||
apperr.ArticleTitleErrorMessage; any other error from CreateArticle still answers
|
||||
500 with body "Failed to create article"
|
||||
And POST /v1/article/ sits behind the jwtauth Verifier and Authenticator group and
|
||||
requires a valid jwt cookie
|
||||
And UpdateArticleUseCase exposes only UpdateBody, AddImage and UpdateIsOnline; no
|
||||
route or use case updates an article's title after creation, so this feature only
|
||||
concerns article creation
|
||||
|
||||
Scenario Outline: CreateArticle now measures title length in runes, not bytes
|
||||
Given an empty article repository fake
|
||||
When CreateArticle is called with a title made of <rune_count> repetitions of the
|
||||
4-byte rune "😀" and a body "Some body text"
|
||||
Then the result is <outcome>
|
||||
|
||||
Examples:
|
||||
| rune_count | outcome |
|
||||
| 2 | ErrInvalidTitle, nothing stored |
|
||||
| 3 | created |
|
||||
| 200 | created |
|
||||
| 201 | ErrInvalidTitle, nothing stored |
|
||||
|
||||
Scenario Outline: POST /v1/article/ enforces the same 3-200 rune boundary over HTTP
|
||||
Given a valid jwt cookie for a user with id 1
|
||||
When the client sends POST /v1/article/ with that cookie, a title of <length> ASCII
|
||||
characters and a body "Some body text"
|
||||
Then the response status is <status>
|
||||
And <storage_effect>
|
||||
|
||||
Examples:
|
||||
| length | status | storage_effect |
|
||||
| 2 | 400 | not created |
|
||||
| 3 | 201 | created, is_online false |
|
||||
| 200 | 201 | created, is_online false |
|
||||
| 201 | 400 | not created |
|
||||
|
||||
Scenario: A 200-rune multibyte title is accepted end to end over HTTP
|
||||
Given a valid jwt cookie for a user with id 1
|
||||
When the client sends POST /v1/article/ with that cookie, a title made of 200
|
||||
repetitions of the 4-byte rune "😀" (200 runes, 800 bytes) and a body
|
||||
"Some body text"
|
||||
Then the response status is 201
|
||||
And the response body is the created article's JSON with that title
|
||||
|
||||
Scenario: The handler maps a domain title rejection to 400, not 500
|
||||
Given a valid jwt cookie for a user with id 1
|
||||
And the article repository fake is configured so its next Create call returns
|
||||
article.ErrInvalidTitle directly, standing in for any layer beneath the use
|
||||
case's own check rejecting the title with that same sentinel
|
||||
When the client sends POST /v1/article/ with that cookie, title "Valid Title" and
|
||||
body "Some body text" (both already satisfying PostArticle's own validate tags)
|
||||
Then the response status is 400
|
||||
And the response body is the text apperr.ArticleTitleErrorMessage
|
||||
And the response body is not "Failed to create article"
|
||||
|
||||
Scenario: A non-title repository failure during creation still answers 500
|
||||
Given a valid jwt cookie for a user with id 1
|
||||
And the article repository fake is configured to fail on its next Create call with
|
||||
a generic repository error, not article.ErrInvalidTitle
|
||||
When the client sends POST /v1/article/ with that cookie, title "Valid Title" and
|
||||
body "Some body text"
|
||||
Then the response status is 500
|
||||
And the response body is the text "Failed to create article"
|
||||
Reference in new issue
Block a user