mirror of
https://git.kescher.at/CatCatNya/catstodon.git
synced 2024-11-27 09:21:38 +01:00
f55dd193f9
* Add tests for some cachable responses This only covers responses that we should have managed to make cachable so far. It's not the case of all responses that should be cachable in the end. * Fix RSS feeds not being cachable
837 lines
24 KiB
Ruby
837 lines
24 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
require 'rails_helper'
|
|
|
|
describe StatusesController do
|
|
render_views
|
|
|
|
shared_examples 'cachable response' do
|
|
it 'does not set cookies' do
|
|
expect(response.cookies).to be_empty
|
|
expect(response.headers['Set-Cookies']).to be nil
|
|
end
|
|
|
|
it 'does not set sessions' do
|
|
expect(session).to be_empty
|
|
end
|
|
|
|
it 'returns public Cache-Control header' do
|
|
expect(response.headers['Cache-Control']).to include 'public'
|
|
end
|
|
end
|
|
|
|
describe 'GET #show' do
|
|
let(:account) { Fabricate(:account) }
|
|
let(:status) { Fabricate(:status, account: account) }
|
|
|
|
context 'when account is suspended' do
|
|
let(:account) { Fabricate(:account, suspended: true) }
|
|
|
|
before do
|
|
get :show, params: { account_username: account.username, id: status.id }
|
|
end
|
|
|
|
it 'returns http gone' do
|
|
expect(response).to have_http_status(410)
|
|
end
|
|
end
|
|
|
|
context 'when status is a reblog' do
|
|
let(:original_account) { Fabricate(:account, domain: 'example.com') }
|
|
let(:original_status) { Fabricate(:status, account: original_account, url: 'https://example.com/123') }
|
|
let(:status) { Fabricate(:status, account: account, reblog: original_status) }
|
|
|
|
before do
|
|
get :show, params: { account_username: status.account.username, id: status.id }
|
|
end
|
|
|
|
it 'redirects to the original status' do
|
|
expect(response).to redirect_to(original_status.url)
|
|
end
|
|
end
|
|
|
|
context 'when status is public' do
|
|
before do
|
|
get :show, params: { account_username: status.account.username, id: status.id, format: format }
|
|
end
|
|
|
|
context 'as HTML' do
|
|
let(:format) { 'html' }
|
|
|
|
it 'returns http success' do
|
|
expect(response).to have_http_status(200)
|
|
end
|
|
|
|
it 'returns Link header' do
|
|
expect(response.headers['Link'].to_s).to include 'activity+json'
|
|
end
|
|
|
|
it 'returns Vary header' do
|
|
expect(response.headers['Vary']).to eq 'Accept'
|
|
end
|
|
|
|
it 'returns public Cache-Control header' do
|
|
expect(response.headers['Cache-Control']).to include 'public'
|
|
end
|
|
|
|
it 'renders status' do
|
|
expect(response).to render_template(:show)
|
|
expect(response.body).to include status.text
|
|
end
|
|
end
|
|
|
|
context 'as JSON' do
|
|
let(:format) { 'json' }
|
|
|
|
it 'returns http success' do
|
|
expect(response).to have_http_status(200)
|
|
end
|
|
|
|
it 'returns Link header' do
|
|
expect(response.headers['Link'].to_s).to include 'activity+json'
|
|
end
|
|
|
|
it 'returns Vary header' do
|
|
expect(response.headers['Vary']).to eq 'Accept'
|
|
end
|
|
|
|
it_behaves_like 'cachable response'
|
|
|
|
it 'returns Content-Type header' do
|
|
expect(response.headers['Content-Type']).to include 'application/activity+json'
|
|
end
|
|
|
|
it 'renders ActivityPub Note object' do
|
|
json = body_as_json
|
|
expect(json[:content]).to include status.text
|
|
end
|
|
end
|
|
end
|
|
|
|
context 'when status is private' do
|
|
let(:status) { Fabricate(:status, account: account, visibility: :private) }
|
|
|
|
before do
|
|
get :show, params: { account_username: status.account.username, id: status.id, format: format }
|
|
end
|
|
|
|
context 'as JSON' do
|
|
let(:format) { 'json' }
|
|
|
|
it 'returns http not found' do
|
|
expect(response).to have_http_status(404)
|
|
end
|
|
end
|
|
|
|
context 'as HTML' do
|
|
let(:format) { 'html' }
|
|
|
|
it 'returns http not found' do
|
|
expect(response).to have_http_status(404)
|
|
end
|
|
end
|
|
end
|
|
|
|
context 'when status is direct' do
|
|
let(:status) { Fabricate(:status, account: account, visibility: :direct) }
|
|
|
|
before do
|
|
get :show, params: { account_username: status.account.username, id: status.id, format: format }
|
|
end
|
|
|
|
context 'as JSON' do
|
|
let(:format) { 'json' }
|
|
|
|
it 'returns http not found' do
|
|
expect(response).to have_http_status(404)
|
|
end
|
|
end
|
|
|
|
context 'as HTML' do
|
|
let(:format) { 'html' }
|
|
|
|
it 'returns http not found' do
|
|
expect(response).to have_http_status(404)
|
|
end
|
|
end
|
|
end
|
|
|
|
context 'when signed-in' do
|
|
let(:user) { Fabricate(:user) }
|
|
|
|
before do
|
|
sign_in(user)
|
|
end
|
|
|
|
context 'when account blocks user' do
|
|
before do
|
|
account.block!(user.account)
|
|
get :show, params: { account_username: status.account.username, id: status.id }
|
|
end
|
|
|
|
it 'returns http not found' do
|
|
expect(response).to have_http_status(404)
|
|
end
|
|
end
|
|
|
|
context 'when status is public' do
|
|
before do
|
|
get :show, params: { account_username: status.account.username, id: status.id, format: format }
|
|
end
|
|
|
|
context 'as HTML' do
|
|
let(:format) { 'html' }
|
|
|
|
it 'returns http success' do
|
|
expect(response).to have_http_status(200)
|
|
end
|
|
|
|
it 'returns Link header' do
|
|
expect(response.headers['Link'].to_s).to include 'activity+json'
|
|
end
|
|
|
|
it 'returns Vary header' do
|
|
expect(response.headers['Vary']).to eq 'Accept'
|
|
end
|
|
|
|
it 'returns no Cache-Control header' do
|
|
expect(response.headers).to_not include 'Cache-Control'
|
|
end
|
|
|
|
it 'renders status' do
|
|
expect(response).to render_template(:show)
|
|
expect(response.body).to include status.text
|
|
end
|
|
end
|
|
|
|
context 'as JSON' do
|
|
let(:format) { 'json' }
|
|
|
|
it 'returns http success' do
|
|
expect(response).to have_http_status(200)
|
|
end
|
|
|
|
it 'returns Link header' do
|
|
expect(response.headers['Link'].to_s).to include 'activity+json'
|
|
end
|
|
|
|
it 'returns Vary header' do
|
|
expect(response.headers['Vary']).to eq 'Accept'
|
|
end
|
|
|
|
it 'returns public Cache-Control header' do
|
|
expect(response.headers['Cache-Control']).to include 'public'
|
|
end
|
|
|
|
it 'returns Content-Type header' do
|
|
expect(response.headers['Content-Type']).to include 'application/activity+json'
|
|
end
|
|
|
|
it 'renders ActivityPub Note object' do
|
|
json = body_as_json
|
|
expect(json[:content]).to include status.text
|
|
end
|
|
end
|
|
end
|
|
|
|
context 'when status is private' do
|
|
let(:status) { Fabricate(:status, account: account, visibility: :private) }
|
|
|
|
context 'when user is authorized to see it' do
|
|
before do
|
|
user.account.follow!(account)
|
|
get :show, params: { account_username: status.account.username, id: status.id, format: format }
|
|
end
|
|
|
|
context 'as HTML' do
|
|
let(:format) { 'html' }
|
|
|
|
it 'returns http success' do
|
|
expect(response).to have_http_status(200)
|
|
end
|
|
|
|
it 'returns Link header' do
|
|
expect(response.headers['Link'].to_s).to include 'activity+json'
|
|
end
|
|
|
|
it 'returns Vary header' do
|
|
expect(response.headers['Vary']).to eq 'Accept'
|
|
end
|
|
|
|
it 'returns no Cache-Control header' do
|
|
expect(response.headers).to_not include 'Cache-Control'
|
|
end
|
|
|
|
it 'renders status' do
|
|
expect(response).to render_template(:show)
|
|
expect(response.body).to include status.text
|
|
end
|
|
end
|
|
|
|
context 'as JSON' do
|
|
let(:format) { 'json' }
|
|
|
|
it 'returns http success' do
|
|
expect(response).to have_http_status(200)
|
|
end
|
|
|
|
it 'returns Link header' do
|
|
expect(response.headers['Link'].to_s).to include 'activity+json'
|
|
end
|
|
|
|
it 'returns Vary header' do
|
|
expect(response.headers['Vary']).to eq 'Accept'
|
|
end
|
|
|
|
it 'returns private Cache-Control header' do
|
|
expect(response.headers['Cache-Control']).to include 'private'
|
|
end
|
|
|
|
it 'returns Content-Type header' do
|
|
expect(response.headers['Content-Type']).to include 'application/activity+json'
|
|
end
|
|
|
|
it 'renders ActivityPub Note object' do
|
|
json = body_as_json
|
|
expect(json[:content]).to include status.text
|
|
end
|
|
end
|
|
end
|
|
|
|
context 'when user is not authorized to see it' do
|
|
before do
|
|
get :show, params: { account_username: status.account.username, id: status.id, format: format }
|
|
end
|
|
|
|
context 'as JSON' do
|
|
let(:format) { 'json' }
|
|
|
|
it 'returns http not found' do
|
|
expect(response).to have_http_status(404)
|
|
end
|
|
end
|
|
|
|
context 'as HTML' do
|
|
let(:format) { 'html' }
|
|
|
|
it 'returns http not found' do
|
|
expect(response).to have_http_status(404)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
context 'when status is direct' do
|
|
let(:status) { Fabricate(:status, account: account, visibility: :direct) }
|
|
|
|
context 'when user is authorized to see it' do
|
|
before do
|
|
Fabricate(:mention, account: user.account, status: status)
|
|
get :show, params: { account_username: status.account.username, id: status.id, format: format }
|
|
end
|
|
|
|
context 'as HTML' do
|
|
let(:format) { 'html' }
|
|
|
|
it 'returns http success' do
|
|
expect(response).to have_http_status(200)
|
|
end
|
|
|
|
it 'returns Link header' do
|
|
expect(response.headers['Link'].to_s).to include 'activity+json'
|
|
end
|
|
|
|
it 'returns Vary header' do
|
|
expect(response.headers['Vary']).to eq 'Accept'
|
|
end
|
|
|
|
it 'returns no Cache-Control header' do
|
|
expect(response.headers).to_not include 'Cache-Control'
|
|
end
|
|
|
|
it 'renders status' do
|
|
expect(response).to render_template(:show)
|
|
expect(response.body).to include status.text
|
|
end
|
|
end
|
|
|
|
context 'as JSON' do
|
|
let(:format) { 'json' }
|
|
|
|
it 'returns http success' do
|
|
expect(response).to have_http_status(200)
|
|
end
|
|
|
|
it 'returns Link header' do
|
|
expect(response.headers['Link'].to_s).to include 'activity+json'
|
|
end
|
|
|
|
it 'returns Vary header' do
|
|
expect(response.headers['Vary']).to eq 'Accept'
|
|
end
|
|
|
|
it 'returns private Cache-Control header' do
|
|
expect(response.headers['Cache-Control']).to include 'private'
|
|
end
|
|
|
|
it 'returns Content-Type header' do
|
|
expect(response.headers['Content-Type']).to include 'application/activity+json'
|
|
end
|
|
|
|
it 'renders ActivityPub Note object' do
|
|
json = body_as_json
|
|
expect(json[:content]).to include status.text
|
|
end
|
|
end
|
|
end
|
|
|
|
context 'when user is not authorized to see it' do
|
|
before do
|
|
get :show, params: { account_username: status.account.username, id: status.id, format: format }
|
|
end
|
|
|
|
context 'as JSON' do
|
|
let(:format) { 'json' }
|
|
|
|
it 'returns http not found' do
|
|
expect(response).to have_http_status(404)
|
|
end
|
|
end
|
|
|
|
context 'as HTML' do
|
|
let(:format) { 'html' }
|
|
|
|
it 'returns http not found' do
|
|
expect(response).to have_http_status(404)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
context 'with signature' do
|
|
let(:remote_account) { Fabricate(:account, domain: 'example.com') }
|
|
|
|
before do
|
|
allow(controller).to receive(:signed_request_account).and_return(remote_account)
|
|
end
|
|
|
|
context 'when account blocks account' do
|
|
before do
|
|
account.block!(remote_account)
|
|
get :show, params: { account_username: status.account.username, id: status.id }
|
|
end
|
|
|
|
it 'returns http not found' do
|
|
expect(response).to have_http_status(404)
|
|
end
|
|
end
|
|
|
|
context 'when account domain blocks account' do
|
|
before do
|
|
account.block_domain!(remote_account.domain)
|
|
get :show, params: { account_username: status.account.username, id: status.id }
|
|
end
|
|
|
|
it 'returns http not found' do
|
|
expect(response).to have_http_status(404)
|
|
end
|
|
end
|
|
|
|
context 'when status is public' do
|
|
before do
|
|
get :show, params: { account_username: status.account.username, id: status.id, format: format }
|
|
end
|
|
|
|
context 'as HTML' do
|
|
let(:format) { 'html' }
|
|
|
|
it 'returns http success' do
|
|
expect(response).to have_http_status(200)
|
|
end
|
|
|
|
it 'returns Link header' do
|
|
expect(response.headers['Link'].to_s).to include 'activity+json'
|
|
end
|
|
|
|
it 'returns Vary header' do
|
|
expect(response.headers['Vary']).to eq 'Accept'
|
|
end
|
|
|
|
it 'returns no Cache-Control header' do
|
|
expect(response.headers).to_not include 'Cache-Control'
|
|
end
|
|
|
|
it 'renders status' do
|
|
expect(response).to render_template(:show)
|
|
expect(response.body).to include status.text
|
|
end
|
|
end
|
|
|
|
context 'as JSON' do
|
|
let(:format) { 'json' }
|
|
|
|
it 'returns http success' do
|
|
expect(response).to have_http_status(200)
|
|
end
|
|
|
|
it 'returns Link header' do
|
|
expect(response.headers['Link'].to_s).to include 'activity+json'
|
|
end
|
|
|
|
it 'returns Vary header' do
|
|
expect(response.headers['Vary']).to eq 'Accept'
|
|
end
|
|
|
|
it_behaves_like 'cachable response'
|
|
|
|
it 'returns Content-Type header' do
|
|
expect(response.headers['Content-Type']).to include 'application/activity+json'
|
|
end
|
|
|
|
it 'renders ActivityPub Note object' do
|
|
json = body_as_json
|
|
expect(json[:content]).to include status.text
|
|
end
|
|
end
|
|
end
|
|
|
|
context 'when status is private' do
|
|
let(:status) { Fabricate(:status, account: account, visibility: :private) }
|
|
|
|
context 'when user is authorized to see it' do
|
|
before do
|
|
remote_account.follow!(account)
|
|
get :show, params: { account_username: status.account.username, id: status.id, format: format }
|
|
end
|
|
|
|
context 'as HTML' do
|
|
let(:format) { 'html' }
|
|
|
|
it 'returns http success' do
|
|
expect(response).to have_http_status(200)
|
|
end
|
|
|
|
it 'returns Link header' do
|
|
expect(response.headers['Link'].to_s).to include 'activity+json'
|
|
end
|
|
|
|
it 'returns Vary header' do
|
|
expect(response.headers['Vary']).to eq 'Accept'
|
|
end
|
|
|
|
it 'returns no Cache-Control header' do
|
|
expect(response.headers).to_not include 'Cache-Control'
|
|
end
|
|
|
|
it 'renders status' do
|
|
expect(response).to render_template(:show)
|
|
expect(response.body).to include status.text
|
|
end
|
|
end
|
|
|
|
context 'as JSON' do
|
|
let(:format) { 'json' }
|
|
|
|
it 'returns http success' do
|
|
expect(response).to have_http_status(200)
|
|
end
|
|
|
|
it 'returns Link header' do
|
|
expect(response.headers['Link'].to_s).to include 'activity+json'
|
|
end
|
|
|
|
it 'returns Vary header' do
|
|
expect(response.headers['Vary']).to eq 'Accept'
|
|
end
|
|
|
|
it 'returns private Cache-Control header' do
|
|
expect(response.headers['Cache-Control']).to include 'private'
|
|
end
|
|
|
|
it 'returns Content-Type header' do
|
|
expect(response.headers['Content-Type']).to include 'application/activity+json'
|
|
end
|
|
|
|
it 'renders ActivityPub Note object' do
|
|
json = body_as_json
|
|
expect(json[:content]).to include status.text
|
|
end
|
|
end
|
|
end
|
|
|
|
context 'when user is not authorized to see it' do
|
|
before do
|
|
get :show, params: { account_username: status.account.username, id: status.id, format: format }
|
|
end
|
|
|
|
context 'as JSON' do
|
|
let(:format) { 'json' }
|
|
|
|
it 'returns http not found' do
|
|
expect(response).to have_http_status(404)
|
|
end
|
|
end
|
|
|
|
context 'as HTML' do
|
|
let(:format) { 'html' }
|
|
|
|
it 'returns http not found' do
|
|
expect(response).to have_http_status(404)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
context 'when status is direct' do
|
|
let(:status) { Fabricate(:status, account: account, visibility: :direct) }
|
|
|
|
context 'when user is authorized to see it' do
|
|
before do
|
|
Fabricate(:mention, account: remote_account, status: status)
|
|
get :show, params: { account_username: status.account.username, id: status.id, format: format }
|
|
end
|
|
|
|
context 'as HTML' do
|
|
let(:format) { 'html' }
|
|
|
|
it 'returns http success' do
|
|
expect(response).to have_http_status(200)
|
|
end
|
|
|
|
it 'returns Link header' do
|
|
expect(response.headers['Link'].to_s).to include 'activity+json'
|
|
end
|
|
|
|
it 'returns Vary header' do
|
|
expect(response.headers['Vary']).to eq 'Accept'
|
|
end
|
|
|
|
it 'returns no Cache-Control header' do
|
|
expect(response.headers).to_not include 'Cache-Control'
|
|
end
|
|
|
|
it 'renders status' do
|
|
expect(response).to render_template(:show)
|
|
expect(response.body).to include status.text
|
|
end
|
|
end
|
|
|
|
context 'as JSON' do
|
|
let(:format) { 'json' }
|
|
|
|
it 'returns http success' do
|
|
expect(response).to have_http_status(200)
|
|
end
|
|
|
|
it 'returns Link header' do
|
|
expect(response.headers['Link'].to_s).to include 'activity+json'
|
|
end
|
|
|
|
it 'returns Vary header' do
|
|
expect(response.headers['Vary']).to eq 'Accept'
|
|
end
|
|
|
|
it 'returns private Cache-Control header' do
|
|
expect(response.headers['Cache-Control']).to include 'private'
|
|
end
|
|
|
|
it 'returns Content-Type header' do
|
|
expect(response.headers['Content-Type']).to include 'application/activity+json'
|
|
end
|
|
|
|
it 'renders ActivityPub Note object' do
|
|
json = body_as_json
|
|
expect(json[:content]).to include status.text
|
|
end
|
|
end
|
|
end
|
|
|
|
context 'when user is not authorized to see it' do
|
|
before do
|
|
get :show, params: { account_username: status.account.username, id: status.id, format: format }
|
|
end
|
|
|
|
context 'as JSON' do
|
|
let(:format) { 'json' }
|
|
|
|
it 'returns http not found' do
|
|
expect(response).to have_http_status(404)
|
|
end
|
|
end
|
|
|
|
context 'as HTML' do
|
|
let(:format) { 'html' }
|
|
|
|
it 'returns http not found' do
|
|
expect(response).to have_http_status(404)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
describe 'GET #activity' do
|
|
let(:account) { Fabricate(:account) }
|
|
let(:status) { Fabricate(:status, account: account) }
|
|
|
|
context 'when account is suspended' do
|
|
let(:account) { Fabricate(:account, suspended: true) }
|
|
|
|
before do
|
|
get :activity, params: { account_username: account.username, id: status.id }
|
|
end
|
|
|
|
it 'returns http gone' do
|
|
expect(response).to have_http_status(410)
|
|
end
|
|
end
|
|
|
|
context 'when status is public' do
|
|
pending
|
|
end
|
|
|
|
context 'when status is private' do
|
|
pending
|
|
end
|
|
|
|
context 'when status is direct' do
|
|
pending
|
|
end
|
|
|
|
context 'when signed-in' do
|
|
context 'when status is public' do
|
|
pending
|
|
end
|
|
|
|
context 'when status is private' do
|
|
context 'when user is authorized to see it' do
|
|
pending
|
|
end
|
|
|
|
context 'when user is not authorized to see it' do
|
|
pending
|
|
end
|
|
end
|
|
|
|
context 'when status is direct' do
|
|
context 'when user is authorized to see it' do
|
|
pending
|
|
end
|
|
|
|
context 'when user is not authorized to see it' do
|
|
pending
|
|
end
|
|
end
|
|
end
|
|
|
|
context 'with signature' do
|
|
context 'when status is public' do
|
|
pending
|
|
end
|
|
|
|
context 'when status is private' do
|
|
context 'when user is authorized to see it' do
|
|
pending
|
|
end
|
|
|
|
context 'when user is not authorized to see it' do
|
|
pending
|
|
end
|
|
end
|
|
|
|
context 'when status is direct' do
|
|
context 'when user is authorized to see it' do
|
|
pending
|
|
end
|
|
|
|
context 'when user is not authorized to see it' do
|
|
pending
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
describe 'GET #embed' do
|
|
let(:account) { Fabricate(:account) }
|
|
let(:status) { Fabricate(:status, account: account) }
|
|
|
|
context 'when account is suspended' do
|
|
let(:account) { Fabricate(:account, suspended: true) }
|
|
|
|
before do
|
|
get :embed, params: { account_username: account.username, id: status.id }
|
|
end
|
|
|
|
it 'returns http gone' do
|
|
expect(response).to have_http_status(410)
|
|
end
|
|
end
|
|
|
|
context 'when status is a reblog' do
|
|
let(:original_account) { Fabricate(:account, domain: 'example.com') }
|
|
let(:original_status) { Fabricate(:status, account: original_account, url: 'https://example.com/123') }
|
|
let(:status) { Fabricate(:status, account: account, reblog: original_status) }
|
|
|
|
before do
|
|
get :embed, params: { account_username: status.account.username, id: status.id }
|
|
end
|
|
|
|
it 'returns http not found' do
|
|
expect(response).to have_http_status(404)
|
|
end
|
|
end
|
|
|
|
context 'when status is public' do
|
|
before do
|
|
get :embed, params: { account_username: status.account.username, id: status.id }
|
|
end
|
|
|
|
it 'returns http success' do
|
|
expect(response).to have_http_status(200)
|
|
end
|
|
|
|
it 'returns Link header' do
|
|
expect(response.headers['Link'].to_s).to include 'activity+json'
|
|
end
|
|
|
|
it 'returns Vary header' do
|
|
expect(response.headers['Vary']).to eq 'Accept'
|
|
end
|
|
|
|
it 'returns public Cache-Control header' do
|
|
expect(response.headers['Cache-Control']).to include 'public'
|
|
end
|
|
|
|
it 'renders status' do
|
|
expect(response).to render_template(:embed)
|
|
expect(response.body).to include status.text
|
|
end
|
|
end
|
|
|
|
context 'when status is private' do
|
|
let(:status) { Fabricate(:status, account: account, visibility: :private) }
|
|
|
|
before do
|
|
get :embed, params: { account_username: status.account.username, id: status.id }
|
|
end
|
|
|
|
it 'returns http not found' do
|
|
expect(response).to have_http_status(404)
|
|
end
|
|
end
|
|
|
|
context 'when status is direct' do
|
|
let(:status) { Fabricate(:status, account: account, visibility: :direct) }
|
|
|
|
before do
|
|
get :embed, params: { account_username: status.account.username, id: status.id }
|
|
end
|
|
|
|
it 'returns http not found' do
|
|
expect(response).to have_http_status(404)
|
|
end
|
|
end
|
|
end
|
|
end
|