Files
RenewCMS/features/use_case_baseline.feature
T

185 lines
11 KiB
Gherkin

Feature: Use case baseline characterization
As the maintainer of RenewCMS
I want every use case in internal/infrastructure/useCases pinned down by tests
So that a refactor cannot silently change behaviour that callers already depend on
Background:
Given the article, image, user and mail repository fakes in test/ each implement their domain Repository interface in full, with no stubbed method returning a hardcoded zero value
And each fake mirrors the observable behaviour of the matching real repository in internal/infrastructure/persistence/repositories or internal/infrastructure/mailer, including which operations return a not-found style error for an unknown id and which succeed silently instead
And each fake can be configured to fail on its next call to a chosen method, independently of the state it already holds, returning a generic repository error
Scenario Outline: CreateArticle enforces the 3-200 character title length limit
Given an empty article repository fake
When CreateArticle is called with a title that is <length> characters long and a body "Some body text"
Then the result is <outcome>
Examples:
| length | outcome |
| 0 | the error article.ErrInvalidTitle, and no article is stored |
| 2 | the error article.ErrInvalidTitle, and no article is stored |
| 3 | a created article whose id is repository-assigned and is_online is false |
| 200 | a created article whose id is repository-assigned and is_online is false |
| 201 | the error article.ErrInvalidTitle, and no article is stored |
Scenario: CreateArticle propagates a configured repository failure
Given an article repository fake configured to fail on Create
When CreateArticle is called with a valid title "Backyard Renovation Update" and a body "Some body text"
Then the use case returns the repository's error unchanged
And no article is stored in the fake
Scenario Outline: GetArticle and GetOnlineArticle expose different behaviour for a missing article than for an offline one
Given an article repository fake containing:
| id | title | is_online |
| 1 | Online Article | true |
| 2 | Offline Article | false |
When <method> is called with id <id>
Then the result is <outcome>
Examples:
| method | id | outcome |
| GetArticle | 1 | the article with id 1 |
| GetArticle | 2 | the article with id 2 (GetArticle does not hide offline articles) |
| GetArticle | 9 | the error article.ErrArticleNotFound |
| GetOnlineArticle | 1 | the article with id 1 |
| GetOnlineArticle | 2 | the error article.ErrArticleNotFound |
| GetOnlineArticle | 9 | the error article.ErrArticleNotFound |
Scenario Outline: GetArticleByName delegates verbatim to the repository
Given an article repository fake containing an article titled "Backyard Renovation Update" with id 1
When GetArticleByName is called with "<name>"
Then the result is <outcome>
Examples:
| name | outcome |
| Backyard Renovation Update | the article with id 1 |
| Unknown Title | the error article.ErrArticleNotFound |
Scenario Outline: ListArticles filters by is_online
Given an article repository fake containing:
| id | title | is_online |
| 1 | A | true |
| 2 | B | false |
| 3 | C | true |
When <method> is called
Then the result is the articles with ids <ids>, and no error
Examples:
| method | ids |
| ListArticlesPage unverified | 1,3 |
| ListArticlesPage verified | 1,2,3 |
Scenario: Listing articles on an empty repository returns an empty slice, not an error
Given an empty article repository fake
When ListArticlesPage is called
Then the result is an empty slice and no error
Scenario Outline: UpdateArticle delegates each operation to the repository and surfaces not-found for an unknown id
Given an article repository fake containing an article with id 1 and an image with id 1
When <method> is called with <input>
Then the result is <outcome>
Examples:
| method | input | outcome |
| UpdateBody | id 1, body "New body" | the article with id 1 and body "New body" |
| UpdateBody | id 9, body "New body" | the repository's not-found error (gorm.ErrRecordNotFound) |
| UpdateIsOnline | id 1, isOnline true | the article with id 1 and is_online true |
| UpdateIsOnline | id 9, isOnline true | the repository's not-found error (gorm.ErrRecordNotFound) |
| AddImage | articleId 1, imageId 1 | no error, and the image is attached to the article |
| AddImage | articleId 9, imageId 1 | the repository's not-found error (gorm.ErrRecordNotFound) |
| AddImage | articleId 1, imageId 9 | the repository's not-found error (gorm.ErrRecordNotFound) |
Scenario Outline: DeleteArticle succeeds silently whether or not the id exists
Given an article repository fake containing an article with id 1
When DeleteArticle is called with id <id>
Then no error is returned
Examples:
| id |
| 1 |
| 9 |
Scenario: DeleteArticle propagates a configured repository failure
Given an article repository fake configured to fail on Delete
When DeleteArticle is called with id 1
Then the use case returns the repository's error unchanged
Scenario Outline: CreateImage delegates verbatim to the repository, including its content-type check
Given an empty image repository fake that mirrors the real repository's accepted content types image/png, image/jpeg, image/webp and image/svg+xml
When CreateImage is called with content-type "<content_type>"
Then the result is <outcome>
Examples:
| content_type | outcome |
| image/png | a created image whose id is repository-assigned |
| image/jpeg | a created image whose id is repository-assigned |
| image/webp | a created image whose id is repository-assigned |
| image/svg+xml | a created image whose id is repository-assigned |
| image/gif | an unsupported-content-type error, and no image is stored |
Scenario: CreateImage propagates a configured repository failure
Given an image repository fake configured to fail on Create
When CreateImage is called with a valid image/png input
Then the use case returns the repository's error unchanged
Scenario Outline: DeleteImage requires the image to exist, unlike article and user deletion
Given an image repository fake containing an image with id 1
When DeleteImage is called with id <id>
Then the result is <outcome>
Examples:
| id | outcome |
| 1 | no error, and the image is removed from the fake |
| 9 | the repository's not-found error (gorm.ErrRecordNotFound) |
Scenario: CreateUser assigns a fresh verification code and starts unverified
Given an empty user repository fake
When CreateUser is called with username "alice", password "hunter2example" and email "alice@example.com"
Then the result is a created user whose id is repository-assigned, is_verified is false, verification_code is a newly generated value distinct from any previous call, verification_expiration is about 2 hours from now, and password_reset_code is empty
Scenario: CreateUser propagates a configured repository failure
Given a user repository fake configured to fail on Create
When CreateUser is called with a valid username, password and email
Then the use case returns the repository's error unchanged
Scenario Outline: GetUser reads by id, username or email and surfaces not-found
Given a user repository fake containing a user with id 1, username "alice" and email "alice@example.com"
When <method> is called with "<input>"
Then the result is <outcome>
Examples:
| method | input | outcome |
| GetUser | 1 | the user with id 1 |
| GetUser | 9 | the error user.ErrUserNotFound |
| GetUserByUsername | alice | the user with id 1 |
| GetUserByUsername | bob | the error user.ErrUserNotFound |
| GetUserByEmail | alice@example.com | the user with id 1 |
| GetUserByEmail | bob@example.com | the error user.ErrUserNotFound |
Scenario: ListUsers returns every user in the fake
Given a user repository fake containing users with ids 1 and 2
When ListUsers is called
Then the result is the users with ids 1 and 2, and no error
Scenario Outline: SendMail forwards its arguments verbatim to the mail repository
Given a mail repository fake
When SendMail is called with receiver "<receiver>", template "<template>" and some data
Then the fake recorded exactly one send with that receiver, template and data, and no error is returned
Examples:
| receiver | template |
| alice@example.com | verify-email |
| bob@example.com | password-reset |
Scenario: SendMail propagates a configured repository failure without recording a send
Given a mail repository fake configured to fail
When SendMail is called with receiver "carol@example.com", template "welcome" and nil data
Then the use case returns the repository's error unchanged
And the fake recorded no send
Scenario: Mutation testing runs clean on the use cases package, api/middleware and api/pkg/claims
Given the test/ suite passes via `go test -tags nofrontend ./test/... -count=1`
When `gremlins unleash --integration --tags nofrontend --coverpkg=./internal/infrastructure/useCases/...,./api/middleware/...,./api/pkg/claims/... --timeout-coefficient 10 --output tmp/gremlins.json ./internal/infrastructure/useCases ./api/middleware ./api/pkg/claims` is run
Then the report contains zero mutants with status LIVED
And the report contains zero mutants with status NOT COVERED