209 lines
12 KiB
Gherkin
209 lines
12 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 repository's not-found error (gorm.ErrRecordNotFound) |
|
|
| GetOnlineArticle | 1 | the article with id 1 |
|
|
| GetOnlineArticle | 2 | the error article.ErrArticleNotFound |
|
|
| GetOnlineArticle | 9 | the repository's not-found error (gorm.ErrRecordNotFound), not 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 repository's not-found error (gorm.ErrRecordNotFound) |
|
|
|
|
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 |
|
|
| ListOnlineArticles | 1,3 |
|
|
| ListAllArticles | 1,2,3 |
|
|
|
|
Scenario: Listing articles on an empty repository returns an empty slice, not an error
|
|
Given an empty article repository fake
|
|
When ListOnlineArticles 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 repository's not-found error (gorm.ErrRecordNotFound) |
|
|
| GetUserByUsername | alice | the user with id 1 |
|
|
| GetUserByUsername | bob | the repository's not-found error (gorm.ErrRecordNotFound) |
|
|
| GetUserByEmail | alice@example.com | the user with id 1 |
|
|
| GetUserByEmail | bob@example.com | the repository's not-found error (gorm.ErrRecordNotFound) |
|
|
|
|
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: UpdateUser delegates each operation to the repository and surfaces not-found for an unknown id
|
|
Given a user repository fake containing a user with id 1
|
|
When <method> is called with id <id> and <input>
|
|
Then the result is <outcome>
|
|
|
|
Examples:
|
|
| method | id | input | outcome |
|
|
| UpdateVerificationStatus | 1 | isVerified true | the user with id 1 and is_verified true |
|
|
| UpdateVerificationStatus | 9 | isVerified true | the repository's not-found error (gorm.ErrRecordNotFound) |
|
|
| UpdatePassword | 1 | password "newpassword123" | the user with id 1 |
|
|
| UpdatePassword | 9 | password "newpassword123" | the repository's not-found error (gorm.ErrRecordNotFound) |
|
|
| UpdatePasswordResetCode | 1 | code "reset-code-123" | the user with id 1 |
|
|
| UpdatePasswordResetCode | 9 | code "reset-code-123" | the repository's not-found error (gorm.ErrRecordNotFound) |
|
|
|
|
Scenario Outline: DeleteUser succeeds silently whether or not the id exists
|
|
Given a user repository fake containing a user with id 1
|
|
When DeleteUser is called with id <id>
|
|
Then no error is returned
|
|
|
|
Examples:
|
|
| id |
|
|
| 1 |
|
|
| 9 |
|
|
|
|
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
|
|
Given the test/ suite passes via `go test -tags nofrontend ./test/... -count=1`
|
|
When `gremlins unleash --integration --tags nofrontend --coverpkg=./internal/infrastructure/useCases/... --timeout-coefficient 10 --output tmp/gremlins.json ./internal/infrastructure/useCases` is run
|
|
Then the report contains zero mutants with status LIVED
|
|
And the report contains zero mutants with status NOT COVERED
|