mirror of
https://github.com/mastodon/mastodon.git
synced 2024-11-24 23:01:20 +00:00
Add have_cacheable_headers
matcher for responses (#31727)
This commit is contained in:
parent
490bdb7944
commit
e1fa456c7c
|
@ -25,10 +25,10 @@ RSpec.describe ActivityPub::CollectionsController do
|
||||||
context 'without signature' do
|
context 'without signature' do
|
||||||
let(:remote_account) { nil }
|
let(:remote_account) { nil }
|
||||||
|
|
||||||
it_behaves_like 'cacheable response'
|
|
||||||
|
|
||||||
it 'returns http success and correct media type and correct items' do
|
it 'returns http success and correct media type and correct items' do
|
||||||
expect(response).to have_http_status(200)
|
expect(response)
|
||||||
|
.to have_http_status(200)
|
||||||
|
.and have_cacheable_headers
|
||||||
expect(response.media_type).to eq 'application/activity+json'
|
expect(response.media_type).to eq 'application/activity+json'
|
||||||
|
|
||||||
expect(body_as_json[:orderedItems])
|
expect(body_as_json[:orderedItems])
|
||||||
|
@ -64,10 +64,11 @@ RSpec.describe ActivityPub::CollectionsController do
|
||||||
let(:remote_account) { Fabricate(:account, domain: 'example.com') }
|
let(:remote_account) { Fabricate(:account, domain: 'example.com') }
|
||||||
|
|
||||||
context 'when getting a featured resource' do
|
context 'when getting a featured resource' do
|
||||||
it_behaves_like 'cacheable response'
|
|
||||||
|
|
||||||
it 'returns http success and correct media type and expected items' do
|
it 'returns http success and correct media type and expected items' do
|
||||||
expect(response).to have_http_status(200)
|
expect(response)
|
||||||
|
.to have_http_status(200)
|
||||||
|
.and have_cacheable_headers
|
||||||
|
|
||||||
expect(response.media_type).to eq 'application/activity+json'
|
expect(response.media_type).to eq 'application/activity+json'
|
||||||
|
|
||||||
expect(body_as_json[:orderedItems])
|
expect(body_as_json[:orderedItems])
|
||||||
|
|
|
@ -25,10 +25,11 @@ RSpec.describe ActivityPub::OutboxesController do
|
||||||
context 'with page not requested' do
|
context 'with page not requested' do
|
||||||
let(:page) { nil }
|
let(:page) { nil }
|
||||||
|
|
||||||
it_behaves_like 'cacheable response'
|
|
||||||
|
|
||||||
it 'returns http success and correct media type and headers and items count' do
|
it 'returns http success and correct media type and headers and items count' do
|
||||||
expect(response).to have_http_status(200)
|
expect(response)
|
||||||
|
.to have_http_status(200)
|
||||||
|
.and have_cacheable_headers
|
||||||
|
|
||||||
expect(response.media_type).to eq 'application/activity+json'
|
expect(response.media_type).to eq 'application/activity+json'
|
||||||
expect(response.headers['Vary']).to be_nil
|
expect(response.headers['Vary']).to be_nil
|
||||||
expect(body[:totalItems]).to eq 4
|
expect(body[:totalItems]).to eq 4
|
||||||
|
@ -59,10 +60,11 @@ RSpec.describe ActivityPub::OutboxesController do
|
||||||
context 'with page requested' do
|
context 'with page requested' do
|
||||||
let(:page) { 'true' }
|
let(:page) { 'true' }
|
||||||
|
|
||||||
it_behaves_like 'cacheable response'
|
|
||||||
|
|
||||||
it 'returns http success and correct media type and vary header and items' do
|
it 'returns http success and correct media type and vary header and items' do
|
||||||
expect(response).to have_http_status(200)
|
expect(response)
|
||||||
|
.to have_http_status(200)
|
||||||
|
.and have_cacheable_headers
|
||||||
|
|
||||||
expect(response.media_type).to eq 'application/activity+json'
|
expect(response.media_type).to eq 'application/activity+json'
|
||||||
expect(response.headers['Vary']).to include 'Signature'
|
expect(response.headers['Vary']).to include 'Signature'
|
||||||
|
|
||||||
|
|
|
@ -68,10 +68,11 @@ RSpec.describe ActivityPub::RepliesController do
|
||||||
let(:parent_visibility) { :public }
|
let(:parent_visibility) { :public }
|
||||||
let(:page_json) { body_as_json[:first] }
|
let(:page_json) { body_as_json[:first] }
|
||||||
|
|
||||||
it_behaves_like 'cacheable response'
|
|
||||||
|
|
||||||
it 'returns http success and correct media type' do
|
it 'returns http success and correct media type' do
|
||||||
expect(response).to have_http_status(200)
|
expect(response)
|
||||||
|
.to have_http_status(200)
|
||||||
|
.and have_cacheable_headers
|
||||||
|
|
||||||
expect(response.media_type).to eq 'application/activity+json'
|
expect(response.media_type).to eq 'application/activity+json'
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -72,13 +72,12 @@ describe StatusesController do
|
||||||
context 'with JSON' do
|
context 'with JSON' do
|
||||||
let(:format) { 'json' }
|
let(:format) { 'json' }
|
||||||
|
|
||||||
it_behaves_like 'cacheable response', expects_vary: 'Accept, Accept-Language, Cookie'
|
|
||||||
|
|
||||||
it 'renders ActivityPub Note object successfully', :aggregate_failures do
|
it 'renders ActivityPub Note object successfully', :aggregate_failures do
|
||||||
expect(response)
|
expect(response)
|
||||||
.to have_http_status(200)
|
.to have_http_status(200)
|
||||||
|
.and have_cacheable_headers.with_vary('Accept, Accept-Language, Cookie')
|
||||||
|
|
||||||
expect(response.headers).to include(
|
expect(response.headers).to include(
|
||||||
'Vary' => 'Accept, Accept-Language, Cookie',
|
|
||||||
'Content-Type' => include('application/activity+json'),
|
'Content-Type' => include('application/activity+json'),
|
||||||
'Link' => satisfy { |header| header.to_s.include?('activity+json') }
|
'Link' => satisfy { |header| header.to_s.include?('activity+json') }
|
||||||
)
|
)
|
||||||
|
@ -380,13 +379,11 @@ describe StatusesController do
|
||||||
context 'with JSON' do
|
context 'with JSON' do
|
||||||
let(:format) { 'json' }
|
let(:format) { 'json' }
|
||||||
|
|
||||||
it_behaves_like 'cacheable response', expects_vary: 'Accept, Accept-Language, Cookie'
|
|
||||||
|
|
||||||
it 'renders ActivityPub Note object successfully', :aggregate_failures do
|
it 'renders ActivityPub Note object successfully', :aggregate_failures do
|
||||||
expect(response)
|
expect(response)
|
||||||
.to have_http_status(200)
|
.to have_http_status(200)
|
||||||
|
.and have_cacheable_headers.with_vary('Accept, Accept-Language, Cookie')
|
||||||
expect(response.headers).to include(
|
expect(response.headers).to include(
|
||||||
'Vary' => 'Accept, Accept-Language, Cookie',
|
|
||||||
'Content-Type' => include('application/activity+json'),
|
'Content-Type' => include('application/activity+json'),
|
||||||
'Link' => satisfy { |header| header.to_s.include?('activity+json') }
|
'Link' => satisfy { |header| header.to_s.include?('activity+json') }
|
||||||
)
|
)
|
||||||
|
|
|
@ -130,6 +130,7 @@ describe 'Accounts show response' do
|
||||||
it 'returns a JSON version of the account', :aggregate_failures do
|
it 'returns a JSON version of the account', :aggregate_failures do
|
||||||
expect(response)
|
expect(response)
|
||||||
.to have_http_status(200)
|
.to have_http_status(200)
|
||||||
|
.and have_cacheable_headers.with_vary('Accept, Accept-Language, Cookie')
|
||||||
.and have_attributes(
|
.and have_attributes(
|
||||||
media_type: eq('application/activity+json')
|
media_type: eq('application/activity+json')
|
||||||
)
|
)
|
||||||
|
@ -137,8 +138,6 @@ describe 'Accounts show response' do
|
||||||
expect(body_as_json).to include(:id, :type, :preferredUsername, :inbox, :publicKey, :name, :summary)
|
expect(body_as_json).to include(:id, :type, :preferredUsername, :inbox, :publicKey, :name, :summary)
|
||||||
end
|
end
|
||||||
|
|
||||||
it_behaves_like 'cacheable response', expects_vary: 'Accept, Accept-Language, Cookie'
|
|
||||||
|
|
||||||
context 'with authorized fetch mode' do
|
context 'with authorized fetch mode' do
|
||||||
let(:authorized_fetch_mode) { true }
|
let(:authorized_fetch_mode) { true }
|
||||||
|
|
||||||
|
@ -179,6 +178,7 @@ describe 'Accounts show response' do
|
||||||
it 'returns a JSON version of the account', :aggregate_failures do
|
it 'returns a JSON version of the account', :aggregate_failures do
|
||||||
expect(response)
|
expect(response)
|
||||||
.to have_http_status(200)
|
.to have_http_status(200)
|
||||||
|
.and have_cacheable_headers.with_vary('Accept, Accept-Language, Cookie')
|
||||||
.and have_attributes(
|
.and have_attributes(
|
||||||
media_type: eq('application/activity+json')
|
media_type: eq('application/activity+json')
|
||||||
)
|
)
|
||||||
|
@ -186,8 +186,6 @@ describe 'Accounts show response' do
|
||||||
expect(body_as_json).to include(:id, :type, :preferredUsername, :inbox, :publicKey, :name, :summary)
|
expect(body_as_json).to include(:id, :type, :preferredUsername, :inbox, :publicKey, :name, :summary)
|
||||||
end
|
end
|
||||||
|
|
||||||
it_behaves_like 'cacheable response', expects_vary: 'Accept, Accept-Language, Cookie'
|
|
||||||
|
|
||||||
context 'with authorized fetch mode' do
|
context 'with authorized fetch mode' do
|
||||||
let(:authorized_fetch_mode) { true }
|
let(:authorized_fetch_mode) { true }
|
||||||
|
|
||||||
|
@ -215,10 +213,10 @@ describe 'Accounts show response' do
|
||||||
get short_account_path(username: account.username, format: format)
|
get short_account_path(username: account.username, format: format)
|
||||||
end
|
end
|
||||||
|
|
||||||
it_behaves_like 'cacheable response', expects_vary: 'Accept, Accept-Language, Cookie'
|
|
||||||
|
|
||||||
it 'responds with correct statuses', :aggregate_failures do
|
it 'responds with correct statuses', :aggregate_failures do
|
||||||
expect(response).to have_http_status(200)
|
expect(response)
|
||||||
|
.to have_http_status(200)
|
||||||
|
.and have_cacheable_headers.with_vary('Accept, Accept-Language, Cookie')
|
||||||
expect(response.body).to include(status_tag_for(status_media))
|
expect(response.body).to include(status_tag_for(status_media))
|
||||||
expect(response.body).to include(status_tag_for(status_self_reply))
|
expect(response.body).to include(status_tag_for(status_self_reply))
|
||||||
expect(response.body).to include(status_tag_for(status))
|
expect(response.body).to include(status_tag_for(status))
|
||||||
|
@ -234,10 +232,11 @@ describe 'Accounts show response' do
|
||||||
get short_account_with_replies_path(username: account.username, format: format)
|
get short_account_with_replies_path(username: account.username, format: format)
|
||||||
end
|
end
|
||||||
|
|
||||||
it_behaves_like 'cacheable response', expects_vary: 'Accept, Accept-Language, Cookie'
|
|
||||||
|
|
||||||
it 'responds with correct statuses with replies', :aggregate_failures do
|
it 'responds with correct statuses with replies', :aggregate_failures do
|
||||||
expect(response).to have_http_status(200)
|
expect(response)
|
||||||
|
.to have_http_status(200)
|
||||||
|
.and have_cacheable_headers.with_vary('Accept, Accept-Language, Cookie')
|
||||||
|
|
||||||
expect(response.body).to include(status_tag_for(status_media))
|
expect(response.body).to include(status_tag_for(status_media))
|
||||||
expect(response.body).to include(status_tag_for(status_reply))
|
expect(response.body).to include(status_tag_for(status_reply))
|
||||||
expect(response.body).to include(status_tag_for(status_self_reply))
|
expect(response.body).to include(status_tag_for(status_self_reply))
|
||||||
|
@ -253,10 +252,10 @@ describe 'Accounts show response' do
|
||||||
get short_account_media_path(username: account.username, format: format)
|
get short_account_media_path(username: account.username, format: format)
|
||||||
end
|
end
|
||||||
|
|
||||||
it_behaves_like 'cacheable response', expects_vary: 'Accept, Accept-Language, Cookie'
|
|
||||||
|
|
||||||
it 'responds with correct statuses with media', :aggregate_failures do
|
it 'responds with correct statuses with media', :aggregate_failures do
|
||||||
expect(response).to have_http_status(200)
|
expect(response)
|
||||||
|
.to have_http_status(200)
|
||||||
|
.and have_cacheable_headers.with_vary('Accept, Accept-Language, Cookie')
|
||||||
expect(response.body).to include(status_tag_for(status_media))
|
expect(response.body).to include(status_tag_for(status_media))
|
||||||
expect(response.body).to_not include(status_tag_for(status_direct))
|
expect(response.body).to_not include(status_tag_for(status_direct))
|
||||||
expect(response.body).to_not include(status_tag_for(status_private))
|
expect(response.body).to_not include(status_tag_for(status_private))
|
||||||
|
@ -277,10 +276,11 @@ describe 'Accounts show response' do
|
||||||
get short_account_tag_path(username: account.username, tag: tag, format: format)
|
get short_account_tag_path(username: account.username, tag: tag, format: format)
|
||||||
end
|
end
|
||||||
|
|
||||||
it_behaves_like 'cacheable response', expects_vary: 'Accept, Accept-Language, Cookie'
|
|
||||||
|
|
||||||
it 'responds with correct statuses with a tag', :aggregate_failures do
|
it 'responds with correct statuses with a tag', :aggregate_failures do
|
||||||
expect(response).to have_http_status(200)
|
expect(response)
|
||||||
|
.to have_http_status(200)
|
||||||
|
.and have_cacheable_headers.with_vary('Accept, Accept-Language, Cookie')
|
||||||
|
|
||||||
expect(response.body).to include(status_tag_for(status_tag))
|
expect(response.body).to include(status_tag_for(status_tag))
|
||||||
expect(response.body).to_not include(status_tag_for(status_direct))
|
expect(response.body).to_not include(status_tag_for(status_direct))
|
||||||
expect(response.body).to_not include(status_tag_for(status_media))
|
expect(response.body).to_not include(status_tag_for(status_media))
|
||||||
|
|
|
@ -9,11 +9,10 @@ describe 'Custom stylesheets' do
|
||||||
it 'returns http success' do
|
it 'returns http success' do
|
||||||
expect(response)
|
expect(response)
|
||||||
.to have_http_status(200)
|
.to have_http_status(200)
|
||||||
|
.and have_cacheable_headers
|
||||||
.and have_attributes(
|
.and have_attributes(
|
||||||
content_type: match('text/css')
|
content_type: match('text/css')
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
it_behaves_like 'cacheable response'
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -17,6 +17,7 @@ RSpec.describe 'Instance actor endpoint' do
|
||||||
it 'returns http success with correct media type and body' do
|
it 'returns http success with correct media type and body' do
|
||||||
expect(response)
|
expect(response)
|
||||||
.to have_http_status(200)
|
.to have_http_status(200)
|
||||||
|
.and have_cacheable_headers
|
||||||
expect(response.content_type)
|
expect(response.content_type)
|
||||||
.to start_with('application/activity+json')
|
.to start_with('application/activity+json')
|
||||||
expect(body_as_json)
|
expect(body_as_json)
|
||||||
|
@ -32,8 +33,6 @@ RSpec.describe 'Instance actor endpoint' do
|
||||||
url: about_more_url(instance_actor: true)
|
url: about_more_url(instance_actor: true)
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
it_behaves_like 'cacheable response'
|
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'with limited federation mode disabled' do
|
context 'with limited federation mode disabled' do
|
||||||
|
|
|
@ -9,6 +9,7 @@ describe 'Manifest' do
|
||||||
it 'returns http success' do
|
it 'returns http success' do
|
||||||
expect(response)
|
expect(response)
|
||||||
.to have_http_status(200)
|
.to have_http_status(200)
|
||||||
|
.and have_cacheable_headers
|
||||||
.and have_attributes(
|
.and have_attributes(
|
||||||
content_type: match('application/json')
|
content_type: match('application/json')
|
||||||
)
|
)
|
||||||
|
@ -18,7 +19,5 @@ describe 'Manifest' do
|
||||||
name: 'Mastodon'
|
name: 'Mastodon'
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
it_behaves_like 'cacheable response'
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -8,16 +8,15 @@ RSpec.describe 'Tags' do
|
||||||
let(:tag) { Fabricate :tag }
|
let(:tag) { Fabricate :tag }
|
||||||
|
|
||||||
context 'with HTML format' do
|
context 'with HTML format' do
|
||||||
# TODO: Convert the cacheable response shared example into a matcher,
|
# TODO: Update the have_cacheable_headers matcher to operate on capybara sessions
|
||||||
# remove this example, rely on system spec (which should use matcher)
|
# Remove this example, rely on system spec (which should use matcher)
|
||||||
before { get tag_path(tag) }
|
before { get tag_path(tag) }
|
||||||
|
|
||||||
it 'returns http success' do
|
it 'returns http success' do
|
||||||
expect(response)
|
expect(response)
|
||||||
.to have_http_status(200)
|
.to have_http_status(200)
|
||||||
|
.and have_cacheable_headers.with_vary('Accept, Accept-Language, Cookie')
|
||||||
end
|
end
|
||||||
|
|
||||||
it_behaves_like 'cacheable response', expects_vary: 'Accept, Accept-Language, Cookie'
|
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'with JSON format' do
|
context 'with JSON format' do
|
||||||
|
@ -26,11 +25,10 @@ RSpec.describe 'Tags' do
|
||||||
it 'returns http success' do
|
it 'returns http success' do
|
||||||
expect(response)
|
expect(response)
|
||||||
.to have_http_status(200)
|
.to have_http_status(200)
|
||||||
|
.and have_cacheable_headers.with_vary('Accept, Accept-Language, Cookie')
|
||||||
expect(response.content_type)
|
expect(response.content_type)
|
||||||
.to start_with('application/activity+json')
|
.to start_with('application/activity+json')
|
||||||
end
|
end
|
||||||
|
|
||||||
it_behaves_like 'cacheable response', expects_vary: 'Accept, Accept-Language, Cookie'
|
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'with RSS format' do
|
context 'with RSS format' do
|
||||||
|
@ -39,11 +37,10 @@ RSpec.describe 'Tags' do
|
||||||
it 'returns http success' do
|
it 'returns http success' do
|
||||||
expect(response)
|
expect(response)
|
||||||
.to have_http_status(200)
|
.to have_http_status(200)
|
||||||
|
.and have_cacheable_headers.with_vary('Accept, Accept-Language, Cookie')
|
||||||
expect(response.content_type)
|
expect(response.content_type)
|
||||||
.to start_with('application/rss+xml')
|
.to start_with('application/rss+xml')
|
||||||
end
|
end
|
||||||
|
|
||||||
it_behaves_like 'cacheable response', expects_vary: 'Accept, Accept-Language, Cookie'
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -1,14 +0,0 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
shared_examples 'cacheable response' do |expects_vary: false|
|
|
||||||
it 'sets correct cache and vary headers and does not set cookies or session', :aggregate_failures do
|
|
||||||
expect(response.cookies).to be_empty
|
|
||||||
expect(response.headers['Set-Cookies']).to be_nil
|
|
||||||
|
|
||||||
expect(session).to be_empty
|
|
||||||
|
|
||||||
expect(response.headers['Vary']).to include(expects_vary) if expects_vary
|
|
||||||
|
|
||||||
expect(response.headers['Cache-Control']).to include('public')
|
|
||||||
end
|
|
||||||
end
|
|
50
spec/support/matchers/cacheable_response.rb
Normal file
50
spec/support/matchers/cacheable_response.rb
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
RSpec::Matchers.define :have_cacheable_headers do
|
||||||
|
match do |response|
|
||||||
|
@response = response
|
||||||
|
|
||||||
|
@errors = [].tap do |errors|
|
||||||
|
errors << check_cookies
|
||||||
|
errors << check_cookie_headers
|
||||||
|
errors << check_session
|
||||||
|
errors << check_cache_control
|
||||||
|
errors << check_vary if @expected_vary.present?
|
||||||
|
end
|
||||||
|
|
||||||
|
@errors.compact.empty?
|
||||||
|
end
|
||||||
|
|
||||||
|
chain :with_vary do |string|
|
||||||
|
@expected_vary = string
|
||||||
|
end
|
||||||
|
|
||||||
|
failure_message do
|
||||||
|
<<~ERROR
|
||||||
|
Expected that the response would be cacheable but it was not:
|
||||||
|
- #{@errors.compact.join("\n - ")}
|
||||||
|
ERROR
|
||||||
|
end
|
||||||
|
|
||||||
|
def check_vary
|
||||||
|
puts @expected_vary
|
||||||
|
pp @response.headers
|
||||||
|
"Response `Vary` header does not contain `#{@expected_vary}`" unless @response.headers['Vary'].include?(@expected_vary)
|
||||||
|
end
|
||||||
|
|
||||||
|
def check_cookies
|
||||||
|
'Reponse cookies are present' unless @response.cookies.empty?
|
||||||
|
end
|
||||||
|
|
||||||
|
def check_cookie_headers
|
||||||
|
'Response `Set-Cookies` headers are present' if @response.headers['Set-Cookies'].present?
|
||||||
|
end
|
||||||
|
|
||||||
|
def check_session
|
||||||
|
'The session is not empty' unless session.empty?
|
||||||
|
end
|
||||||
|
|
||||||
|
def check_cache_control
|
||||||
|
'The `Cache-Control` header does not contain `public`' unless @response.headers['Cache-Control'].include?('public')
|
||||||
|
end
|
||||||
|
end
|
Loading…
Reference in a new issue