86 lines
4.6 KiB
Gherkin
86 lines
4.6 KiB
Gherkin
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"
|