67 lines
4.0 KiB
Gherkin
67 lines
4.0 KiB
Gherkin
Feature: Public and authenticated article listing
|
|
|
|
As a visitor of the public site and as an authenticated RenewCMS user
|
|
I want GET /v1/article/ to return every online article to anyone,
|
|
and every article, including offline ones, to a caller with a valid jwt cookie
|
|
So that the public site never displays a draft, while an authenticated user managing
|
|
the CMS can see and act on articles that are not published yet
|
|
|
|
Background:
|
|
Given internal/domain/article.Article has a json "is_online" field
|
|
And ListArticlesUseCase.ListOnlineArticles filters article.Filters{IsOnline: true} and
|
|
ListArticlesUseCase.ListAllArticles applies no filter, both already implemented
|
|
And before this slice, GET /v1/article/ sits inside the jwtauth Verifier + Authenticator group
|
|
in api/router.go along with GET /v1/article/{id}, POST /v1/article/ and DELETE /v1/article/{id},
|
|
so an anonymous request to any of them answers 401, not an online-only list
|
|
And this slice moves GET /v1/article/ under jwtauth.Verifier only, so a missing or invalid jwt
|
|
cookie no longer blocks the request; POST /v1/article/ and DELETE /v1/article/{id} keep
|
|
requiring jwtauth.Verifier + Authenticator, unchanged
|
|
And a later slice, features/public_article_read.feature, moves GET /v1/article/{id} into the
|
|
same Verifier-only group as GET /v1/article/, so the 401 Examples table below is updated
|
|
accordingly
|
|
And Handler.ListArticles decides between ListAllArticles and ListOnlineArticles by checking the
|
|
request for a valid jwt, read with jwtauth.FromContext from the token the router-level
|
|
jwtauth.Verifier (header and cookie sources) placed in the request context, rather than
|
|
requiring the router-level Authenticator middleware; article.NewHandler takes no token
|
|
And the response body is the JSON array article.Article marshals to, each element carrying its
|
|
own "is_online" value
|
|
|
|
Scenario: An anonymous visitor only sees online articles
|
|
Given the article repository contains an online article titled "Backyard Renovation Update" with id 1
|
|
And the same repository contains an offline article titled "Draft: Kitchen Remodel" with id 2
|
|
When the client sends GET /v1/article/ with no jwt cookie
|
|
Then the response status is 200
|
|
And the response body is a JSON array containing exactly the article with id 1
|
|
And the article with id 2 is absent from the response body
|
|
|
|
Scenario: An authenticated user sees every article, online and offline
|
|
Given a valid jwt cookie for a user with id 1
|
|
And the article repository contains an online article titled "Backyard Renovation Update" with id 1
|
|
And the same repository contains an offline article titled "Draft: Kitchen Remodel" with id 2
|
|
When the client sends GET /v1/article/ with that cookie
|
|
Then the response status is 200
|
|
And the response body is a JSON array containing both the article with id 1 and the article with id 2
|
|
|
|
Scenario: A malformed or expired jwt cookie falls back to the public online-only list
|
|
Given a jwt cookie whose value is not a token issued by JWT_SECRET
|
|
And the article repository contains an online article with id 1 and an offline article with id 2
|
|
When the client sends GET /v1/article/ with that cookie
|
|
Then the response status is 200
|
|
And the response body is a JSON array containing exactly the article with id 1
|
|
|
|
Scenario Outline: The rest of the article routes still require authentication
|
|
Given no jwt cookie is sent
|
|
When the client sends <method> <path>
|
|
Then the response status is 401
|
|
|
|
Examples:
|
|
| method | path |
|
|
| POST | /v1/article/ |
|
|
| DELETE | /v1/article/1 |
|
|
|
|
Scenario: A repository failure still answers 500 regardless of auth state
|
|
Given the article repository fails on its next call to FindByFilters
|
|
When the client sends GET /v1/article/ with no jwt cookie
|
|
Then the response status is 500
|
|
And the response body is the text "Failed to retrieve articles"
|