Files
RenewCMS/features/not_found_errors.feature

81 lines
4.3 KiB
Gherkin

Feature: Domain not-found errors instead of raw GORM errors
As the maintainer of RenewCMS
I want a missing article or user reported as a domain not-found error
So that the API answers 404 for it instead of leaking a raw GORM error as a 500, and so that
a caller can rely on errors.Is(err, article.ErrArticleNotFound) or errors.Is(err, user.ErrUserNotFound)
Background:
Given internal/domain/article defines the sentinel article.ErrArticleNotFound
And internal/domain/user defines the sentinel user.ErrUserNotFound
And ArticleRepository.Get and ArticleRepository.GetByName return article.ErrArticleNotFound, not gorm.ErrRecordNotFound, when no row matches
And UserRepository.Get, UserRepository.GetByUsername and UserRepository.GetByEmail return user.ErrUserNotFound, not gorm.ErrRecordNotFound, when no row matches
And GET /v1/article/{id} sits behind the jwtauth Verifier and Authenticator group, and requires a valid jwt cookie
And a later slice, features/public_article_read.feature, moves GET /v1/article/{id} into the
Verifier-only group and makes it return an offline article to a verified caller instead of
404, mirroring the listing endpoint's visibility rule; the two scenarios below that depended
on the older, stricter rule are updated accordingly
Scenario Outline: The article repository reports a missing article as a domain error
Given a repository backed by an in-memory SQLite database containing an article titled "Backyard Renovation Update" with id 1
When <method> is called with <input>
Then the result is the error article.ErrArticleNotFound
And errors.Is(err, gorm.ErrRecordNotFound) is false
Examples:
| method | input |
| Get | id 9 |
| GetByName | "Unknown Title" |
Scenario Outline: The user repository reports a missing user as a domain error
Given a repository backed by an in-memory SQLite database containing a user with id 1, username "alice" and email "alice@example.com"
When <method> is called with <input>
Then the result is the error user.ErrUserNotFound
And errors.Is(err, gorm.ErrRecordNotFound) is false
Examples:
| method | input |
| Get | id 9 |
| GetByUsername | "bob" |
| GetByEmail | "bob@example.com" |
Scenario: Fetching a nonexistent article answers 404, not 500
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 GET /v1/article/9999 with that cookie
Then the response status is 404
And the response body is the text "Article not found"
Scenario: Fetching an offline article as an anonymous or unverified caller still answers 404 with the same message
Given no jwt cookie is sent
And the article repository contains an article with id 2, is_online false
When the client sends GET /v1/article/2 without a cookie
Then the response status is 404
And the response body is the text "Article not found"
Scenario: Fetching an online article still answers 200
Given a valid jwt cookie for a user with id 1
And the article repository contains an article with id 1, is_online true
When the client sends GET /v1/article/1 with that cookie
Then the response status is 200
And the response body is the article's JSON representation
Scenario: An unknown article id answers 404 even without authentication
Given no jwt cookie is sent
And the article repository has no article with id 9999
When the client sends GET /v1/article/9999 without a cookie
Then the response status is 404
And the response body is the text "Article not found"
Scenario Outline: Login never reveals whether the username exists
Given a user repository containing a user "alice" with password "hunter2example"
When the client sends POST /v1/auth/login with username "<username>" and password "<password>"
Then the response status is 403
And the response body is the text apperr.LogsErrorMessage
And no jwt cookie is set
Examples:
| username | password | case |
| alice | wrongpassword12 | existing user, bad pass |
| bob | hunter2example | unknown username |