Browse Source

Add more tests for ActivityPub controllers (#13585)

closed-social-v3
Eugen Rochko 4 years ago
committed by GitHub
parent
commit
988b0493fe
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 1315 additions and 142 deletions
  1. +7
    -7
      app/controllers/accounts_controller.rb
  2. +10
    -7
      app/controllers/activitypub/collections_controller.rb
  3. +3
    -3
      app/controllers/activitypub/outboxes_controller.rb
  4. +15
    -6
      app/controllers/activitypub/replies_controller.rb
  5. +1
    -1
      app/controllers/api/v1/polls/votes_controller.rb
  6. +1
    -1
      app/controllers/api/v1/polls_controller.rb
  7. +6
    -5
      app/controllers/api/v1/push/subscriptions_controller.rb
  8. +1
    -2
      app/controllers/api/v1/statuses/mutes_controller.rb
  9. +1
    -1
      app/controllers/api/v1/statuses_controller.rb
  10. +1
    -1
      app/controllers/media_controller.rb
  11. +1
    -1
      app/controllers/remote_interaction_controller.rb
  12. +1
    -1
      app/controllers/statuses_controller.rb
  13. +1
    -1
      app/models/status.rb
  14. +122
    -10
      spec/controllers/activitypub/collections_controller_spec.rb
  15. +17
    -11
      spec/controllers/activitypub/inboxes_controller_spec.rb
  16. +162
    -8
      spec/controllers/activitypub/outboxes_controller_spec.rb
  17. +196
    -0
      spec/controllers/activitypub/replies_controller_spec.rb
  18. +768
    -75
      spec/controllers/statuses_controller_spec.rb
  19. +1
    -1
      spec/fabricators/status_pin_fabricator.rb

+ 7
- 7
app/controllers/accounts_controller.rb View File

@ -27,7 +27,7 @@ class AccountsController < ApplicationController
end
@pinned_statuses = cache_collection(@account.pinned_statuses, Status) if show_pinned_statuses?
@statuses = filtered_status_page(params)
@statuses = filtered_status_page
@statuses = cache_collection(@statuses, Status)
@rss_url = rss_url
@ -140,12 +140,12 @@ class AccountsController < ApplicationController
request.path.split('.').first.ends_with?(Addressable::URI.parse("/tagged/#{params[:tag]}").normalize)
end
def filtered_status_page(params)
if params[:min_id].present?
filtered_statuses.paginate_by_min_id(PAGE_SIZE, params[:min_id]).reverse
else
filtered_statuses.paginate_by_max_id(PAGE_SIZE, params[:max_id], params[:since_id]).to_a
end
def filtered_status_page
filtered_statuses.paginate_by_id(PAGE_SIZE, params_slice(:max_id, :min_id, :since_id))
end
def params_slice(*keys)
params.slice(*keys).permit(*keys)
end
def restrict_fields_to

+ 10
- 7
app/controllers/activitypub/collections_controller.rb View File

@ -24,20 +24,23 @@ class ActivityPub::CollectionsController < ActivityPub::BaseController
def set_size
case params[:id]
when 'featured'
@account.pinned_statuses.count
@size = @account.pinned_statuses.count
else
raise ActiveRecord::RecordNotFound
not_found
end
end
def scope_for_collection
case params[:id]
when 'featured'
return Status.none if @account.blocking?(signed_request_account)
@account.pinned_statuses
else
raise ActiveRecord::RecordNotFound
# Because in public fetch mode we cache the response, there would be no
# benefit from performing the check below, since a blocked account or domain
# would likely be served the cache from the reverse proxy anyway
if authorized_fetch_mode? && !signed_request_account.nil? && (@account.blocking?(signed_request_account) || (!signed_request_account.domain.nil? && @account.domain_blocking?(signed_request_account.domain)))
Status.none
else
@account.pinned_statuses
end
end
end

+ 3
- 3
app/controllers/activitypub/outboxes_controller.rb View File

@ -11,7 +11,7 @@ class ActivityPub::OutboxesController < ActivityPub::BaseController
before_action :set_cache_headers
def show
expires_in(page_requested? ? 0 : 3.minutes, public: public_fetch_mode?)
expires_in(page_requested? ? 0 : 3.minutes, public: public_fetch_mode? && !(signed_request_account.present? && page_requested?))
render json: outbox_presenter, serializer: ActivityPub::OutboxSerializer, adapter: ActivityPub::Adapter, content_type: 'application/activity+json'
end
@ -50,12 +50,12 @@ class ActivityPub::OutboxesController < ActivityPub::BaseController
return unless page_requested?
@statuses = @account.statuses.permitted_for(@account, signed_request_account)
@statuses = params[:min_id].present? ? @statuses.paginate_by_min_id(LIMIT, params[:min_id]).reverse : @statuses.paginate_by_max_id(LIMIT, params[:max_id])
@statuses = @statuses.paginate_by_id(LIMIT, params_slice(:max_id, :min_id, :since_id))
@statuses = cache_collection(@statuses, Status)
end
def page_requested?
params[:page] == 'true'
truthy_param?(:page)
end
def page_params

+ 15
- 6
app/controllers/activitypub/replies_controller.rb View File

@ -1,7 +1,7 @@
# frozen_string_literal: true
class ActivityPub::RepliesController < ActivityPub::BaseController
include SignatureAuthentication
include SignatureVerification
include Authorization
include AccountOwnedConcern
@ -19,15 +19,19 @@ class ActivityPub::RepliesController < ActivityPub::BaseController
private
def pundit_user
signed_request_account
end
def set_status
@status = @account.statuses.find(params[:status_id])
authorize @status, :show?
rescue Mastodon::NotPermittedError
raise ActiveRecord::RecordNotFound
not_found
end
def set_replies
@replies = page_params[:only_other_accounts] ? Status.where.not(account_id: @account.id) : @account.statuses
@replies = only_other_accounts? ? Status.where.not(account_id: @account.id) : @account.statuses
@replies = @replies.where(in_reply_to_id: @status.id, visibility: [:public, :unlisted])
@replies = @replies.paginate_by_min_id(DESCENDANTS_LIMIT, params[:min_id])
end
@ -38,7 +42,7 @@ class ActivityPub::RepliesController < ActivityPub::BaseController
type: :unordered,
part_of: account_status_replies_url(@account, @status),
next: next_page,
items: @replies.map { |status| status.local ? status : status.uri }
items: @replies.map { |status| status.local? ? status : status.uri }
)
return page if page_requested?
@ -51,16 +55,21 @@ class ActivityPub::RepliesController < ActivityPub::BaseController
end
def page_requested?
params[:page] == 'true'
truthy_param?(:page)
end
def only_other_accounts?
truthy_param?(:only_other_accounts)
end
def next_page
only_other_accounts = !(@replies&.last&.account_id == @account.id && @replies.size == DESCENDANTS_LIMIT)
account_status_replies_url(
@account,
@status,
page: true,
min_id: only_other_accounts && !page_params[:only_other_accounts] ? nil : @replies&.last&.id,
min_id: only_other_accounts && !only_other_accounts? ? nil : @replies&.last&.id,
only_other_accounts: only_other_accounts
)
end

+ 1
- 1
app/controllers/api/v1/polls/votes_controller.rb View File

@ -18,7 +18,7 @@ class Api::V1::Polls::VotesController < Api::BaseController
@poll = Poll.attached.find(params[:poll_id])
authorize @poll.status, :show?
rescue Mastodon::NotPermittedError
raise ActiveRecord::RecordNotFound
not_found
end
def vote_params

+ 1
- 1
app/controllers/api/v1/polls_controller.rb View File

@ -17,7 +17,7 @@ class Api::V1::PollsController < Api::BaseController
@poll = Poll.attached.find(params[:id])
authorize @poll.status, :show?
rescue Mastodon::NotPermittedError
raise ActiveRecord::RecordNotFound
not_found
end
def refresh_poll

+ 6
- 5
app/controllers/api/v1/push/subscriptions_controller.rb View File

@ -4,6 +4,7 @@ class Api::V1::Push::SubscriptionsController < Api::BaseController
before_action -> { doorkeeper_authorize! :push }
before_action :require_user!
before_action :set_web_push_subscription
before_action :check_web_push_subscription, only: [:show, :update]
def create
@web_subscription&.destroy!
@ -21,16 +22,11 @@ class Api::V1::Push::SubscriptionsController < Api::BaseController
end
def show
raise ActiveRecord::RecordNotFound if @web_subscription.nil?
render json: @web_subscription, serializer: REST::WebPushSubscriptionSerializer
end
def update
raise ActiveRecord::RecordNotFound if @web_subscription.nil?
@web_subscription.update!(data: data_params)
render json: @web_subscription, serializer: REST::WebPushSubscriptionSerializer
end
@ -45,12 +41,17 @@ class Api::V1::Push::SubscriptionsController < Api::BaseController
@web_subscription = ::Web::PushSubscription.find_by(access_token_id: doorkeeper_token.id)
end
def check_web_push_subscription
not_found if @web_subscription.nil?
end
def subscription_params
params.require(:subscription).permit(:endpoint, keys: [:auth, :p256dh])
end
def data_params
return {} if params[:data].blank?
params.require(:data).permit(alerts: [:follow, :follow_request, :favourite, :reblog, :mention, :poll])
end
end

+ 1
- 2
app/controllers/api/v1/statuses/mutes_controller.rb View File

@ -28,8 +28,7 @@ class Api::V1::Statuses::MutesController < Api::BaseController
@status = Status.find(params[:status_id])
authorize @status, :show?
rescue Mastodon::NotPermittedError
# Reraise in order to get a 404 instead of a 403 error code
raise ActiveRecord::RecordNotFound
not_found
end
def set_conversation

+ 1
- 1
app/controllers/api/v1/statuses_controller.rb View File

@ -67,7 +67,7 @@ class Api::V1::StatusesController < Api::BaseController
@status = Status.find(params[:id])
authorize @status, :show?
rescue Mastodon::NotPermittedError
raise ActiveRecord::RecordNotFound
not_found
end
def set_thread

+ 1
- 1
app/controllers/media_controller.rb View File

@ -33,7 +33,7 @@ class MediaController < ApplicationController
def verify_permitted_status!
authorize @media_attachment.status, :show?
rescue Mastodon::NotPermittedError
raise ActiveRecord::RecordNotFound
not_found
end
def check_playable

+ 1
- 1
app/controllers/remote_interaction_controller.rb View File

@ -41,7 +41,7 @@ class RemoteInteractionController < ApplicationController
@status = Status.find(params[:id])
authorize @status, :show?
rescue Mastodon::NotPermittedError
raise ActiveRecord::RecordNotFound
not_found
end
def set_body_classes

+ 1
- 1
app/controllers/statuses_controller.rb View File

@ -46,7 +46,7 @@ class StatusesController < ApplicationController
end
def embed
return not_found if @status.hidden?
return not_found if @status.hidden? || @status.reblog?
expires_in 180, public: true
response.headers['X-Frame-Options'] = 'ALLOWALL'

+ 1
- 1
app/models/status.rb View File

@ -354,7 +354,7 @@ class Status < ApplicationRecord
if account.nil?
where(visibility: visibility)
elsif target_account.blocking?(account) # get rid of blocked peeps
elsif target_account.blocking?(account) || (account.domain.present? && target_account.domain_blocking?(account.domain)) # get rid of blocked peeps
none
elsif account.id == target_account.id # author can see own stuff
all

+ 122
- 10
spec/controllers/activitypub/collections_controller_spec.rb View File

@ -3,21 +3,133 @@
require 'rails_helper'
RSpec.describe ActivityPub::CollectionsController, type: :controller do
describe 'POST #show' do
let(:account) { Fabricate(:account) }
let!(:account) { Fabricate(:account) }
let(:remote_account) { nil }
context 'id is "featured"' do
it 'returns 200 with "application/activity+json"' do
post :show, params: { id: 'featured', account_username: account.username }
before do
allow(controller).to receive(:signed_request_account).and_return(remote_account)
expect(response).to have_http_status(200)
expect(response.content_type).to eq 'application/activity+json'
Fabricate(:status_pin, account: account)
Fabricate(:status_pin, account: account)
Fabricate(:status, account: account, visibility: :private)
end
describe 'GET #show' do
context 'when id is "featured"' do
context 'without signature' do
let(:remote_account) { nil }
before do
get :show, params: { id: 'featured', account_username: account.username }
end
it 'returns http success' do
expect(response).to have_http_status(200)
end
it 'returns application/activity+json' do
expect(response.content_type).to eq 'application/activity+json'
end
it 'returns public Cache-Control header' do
expect(response.headers['Cache-Control']).to include 'public'
end
it 'returns orderedItems with pinned statuses' do
json = body_as_json
expect(json[:orderedItems]).to be_an Array
expect(json[:orderedItems].size).to eq 2
end
end
context 'with signature' do
let(:remote_account) { Fabricate(:account, domain: 'example.com') }
context do
before do
get :show, params: { id: 'featured', account_username: account.username }
end
it 'returns http success' do
expect(response).to have_http_status(200)
end
it 'returns application/activity+json' do
expect(response.content_type).to eq 'application/activity+json'
end
it 'returns public Cache-Control header' do
expect(response.headers['Cache-Control']).to include 'public'
end
it 'returns orderedItems with pinned statuses' do
json = body_as_json
expect(json[:orderedItems]).to be_an Array
expect(json[:orderedItems].size).to eq 2
end
end
context 'in authorized fetch mode' do
before do
allow(controller).to receive(:authorized_fetch_mode?).and_return(true)
end
context 'when signed request account is blocked' do
before do
account.block!(remote_account)
get :show, params: { id: 'featured', account_username: account.username }
end
it 'returns http success' do
expect(response).to have_http_status(200)
end
it 'returns application/activity+json' do
expect(response.content_type).to eq 'application/activity+json'
end
it 'returns private Cache-Control header' do
expect(response.headers['Cache-Control']).to include 'private'
end
it 'returns empty orderedItems' do
json = body_as_json
expect(json[:orderedItems]).to be_an Array
expect(json[:orderedItems].size).to eq 0
end
end
context 'when signed request account is domain blocked' do
before do
account.block_domain!(remote_account.domain)
get :show, params: { id: 'featured', account_username: account.username }
end
it 'returns http success' do
expect(response).to have_http_status(200)
end
it 'returns application/activity+json' do
expect(response.content_type).to eq 'application/activity+json'
end
it 'returns private Cache-Control header' do
expect(response.headers['Cache-Control']).to include 'private'
end
it 'returns empty orderedItems' do
json = body_as_json
expect(json[:orderedItems]).to be_an Array
expect(json[:orderedItems].size).to eq 0
end
end
end
end
end
context 'id is not "featured"' do
it 'returns 404' do
post :show, params: { id: 'hoge', account_username: account.username }
context 'when id is not "featured"' do
it 'returns http not found' do
get :show, params: { id: 'hoge', account_username: account.username }
expect(response).to have_http_status(404)
end
end

+ 17
- 11
spec/controllers/activitypub/inboxes_controller_spec.rb View File

@ -3,25 +3,31 @@
require 'rails_helper'
RSpec.describe ActivityPub::InboxesController, type: :controller do
let(:remote_account) { nil }
before do
allow(controller).to receive(:signed_request_account).and_return(remote_account)
end
describe 'POST #create' do
context 'with signed_request_account' do
it 'returns 202' do
allow(controller).to receive(:signed_request_account) do
Fabricate(:account)
end
context 'with signature' do
let(:remote_account) { Fabricate(:account, domain: 'example.com', protocol: :activitypub) }
before do
post :create, body: '{}'
end
it 'returns http accepted' do
expect(response).to have_http_status(202)
end
end
context 'without signed_request_account' do
it 'returns 401' do
allow(controller).to receive(:signed_request_account) do
false
end
context 'without signature' do
before do
post :create, body: '{}'
end
it 'returns http not authorized' do
expect(response).to have_http_status(401)
end
end

+ 162
- 8
spec/controllers/activitypub/outboxes_controller_spec.rb View File

@ -4,20 +4,174 @@ RSpec.describe ActivityPub::OutboxesController, type: :controller do
let!(:account) { Fabricate(:account) }
before do
Fabricate(:status, account: account)
Fabricate(:status, account: account, visibility: :public)
Fabricate(:status, account: account, visibility: :unlisted)
Fabricate(:status, account: account, visibility: :private)
Fabricate(:status, account: account, visibility: :direct)
Fabricate(:status, account: account, visibility: :limited)
end
before do
allow(controller).to receive(:signed_request_account).and_return(remote_account)
end
describe 'GET #show' do
before do
get :show, params: { account_username: account.username }
end
context 'without signature' do
let(:remote_account) { nil }
before do
get :show, params: { account_username: account.username, page: page }
end
context 'with page not requested' do
let(:page) { nil }
it 'returns http success' do
expect(response).to have_http_status(200)
end
it 'returns application/activity+json' do
expect(response.content_type).to eq 'application/activity+json'
end
it 'returns totalItems' do
json = body_as_json
expect(json[:totalItems]).to eq 4
end
it 'returns http success' do
expect(response).to have_http_status(200)
it 'returns public Cache-Control header' do
expect(response.headers['Cache-Control']).to include 'public'
end
end
context 'with page requested' do
let(:page) { 'true' }
it 'returns http success' do
expect(response).to have_http_status(200)
end
it 'returns application/activity+json' do
expect(response.content_type).to eq 'application/activity+json'
end
it 'returns orderedItems with public or unlisted statuses' do
json = body_as_json
expect(json[:orderedItems]).to be_an Array
expect(json[:orderedItems].size).to eq 2
expect(json[:orderedItems].all? { |item| item[:to].include?(ActivityPub::TagManager::COLLECTIONS[:public]) || item[:cc].include?(ActivityPub::TagManager::COLLECTIONS[:public]) }).to be true
end
it 'returns public Cache-Control header' do
expect(response.headers['Cache-Control']).to include 'public'
end
end
end
it 'returns application/activity+json' do
expect(response.content_type).to eq 'application/activity+json'
context 'with signature' do
let(:remote_account) { Fabricate(:account, domain: 'example.com') }
let(:page) { 'true' }
context 'when signed request account does not follow account' do
before do
get :show, params: { account_username: account.username, page: page }
end
it 'returns http success' do
expect(response).to have_http_status(200)
end
it 'returns application/activity+json' do
expect(response.content_type).to eq 'application/activity+json'
end
it 'returns orderedItems with public or unlisted statuses' do
json = body_as_json
expect(json[:orderedItems]).to be_an Array
expect(json[:orderedItems].size).to eq 2
expect(json[:orderedItems].all? { |item| item[:to].include?(ActivityPub::TagManager::COLLECTIONS[:public]) || item[:cc].include?(ActivityPub::TagManager::COLLECTIONS[:public]) }).to be true
end
it 'returns private Cache-Control header' do
expect(response.headers['Cache-Control']).to eq 'max-age=0, private'
end
end
context 'when signed request account follows account' do
before do
remote_account.follow!(account)
get :show, params: { account_username: account.username, page: page }
end
it 'returns http success' do
expect(response).to have_http_status(200)
end
it 'returns application/activity+json' do
expect(response.content_type).to eq 'application/activity+json'
end
it 'returns orderedItems with private statuses' do
json = body_as_json
expect(json[:orderedItems]).to be_an Array
expect(json[:orderedItems].size).to eq 3
expect(json[:orderedItems].all? { |item| item[:to].include?(ActivityPub::TagManager::COLLECTIONS[:public]) || item[:cc].include?(ActivityPub::TagManager::COLLECTIONS[:public]) || item[:to].include?(account_followers_url(account, ActionMailer::Base.default_url_options)) }).to be true
end
it 'returns private Cache-Control header' do
expect(response.headers['Cache-Control']).to eq 'max-age=0, private'
end
end
context 'when signed request account is blocked' do
before do
account.block!(remote_account)
get :show, params: { account_username: account.username, page: page }
end
it 'returns http success' do
expect(response).to have_http_status(200)
end
it 'returns application/activity+json' do
expect(response.content_type).to eq 'application/activity+json'
end
it 'returns empty orderedItems' do
json = body_as_json
expect(json[:orderedItems]).to be_an Array
expect(json[:orderedItems].size).to eq 0
end
it 'returns private Cache-Control header' do
expect(response.headers['Cache-Control']).to eq 'max-age=0, private'
end
end
context 'when signed request account is domain blocked' do
before do
account.block_domain!(remote_account.domain)
get :show, params: { account_username: account.username, page: page }
end
it 'returns http success' do
expect(response).to have_http_status(200)
end
it 'returns application/activity+json' do
expect(response.content_type).to eq 'application/activity+json'
end
it 'returns empty orderedItems' do
json = body_as_json
expect(json[:orderedItems]).to be_an Array
expect(json[:orderedItems].size).to eq 0
end
it 'returns private Cache-Control header' do
expect(response.headers['Cache-Control']).to eq 'max-age=0, private'
end
end
end
end
end

+ 196
- 0
spec/controllers/activitypub/replies_controller_spec.rb View File

@ -0,0 +1,196 @@
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe ActivityPub::RepliesController, type: :controller do
let(:status) { Fabricate(:status, visibility: parent_visibility) }
let(:remote_account) { nil }
before do
allow(controller).to receive(:signed_request_account).and_return(remote_account)
Fabricate(:status, thread: status, visibility: :public)
Fabricate(:status, thread: status, visibility: :public)
Fabricate(:status, thread: status, visibility: :private)
Fabricate(:status, account: status.account, thread: status, visibility: :public)
Fabricate(:status, account: status.account, thread: status, visibility: :private)
end
describe 'GET #index' do
context 'with no signature' do
before do
get :index, params: { account_username: status.account.username, status_id: status.id }
end
context 'when status is public' do
let(:parent_visibility) { :public }
it 'returns http success' do
expect(response).to have_http_status(200)
end
it 'returns application/activity+json' do
expect(response.content_type).to eq 'application/activity+json'
end
it 'returns public Cache-Control header' do
expect(response.headers['Cache-Control']).to include 'public'
end
it 'returns items with account\'s own replies' do
json = body_as_json
expect(json[:first]).to be_a Hash
expect(json[:first][:items]).to be_an Array
expect(json[:first][:items].size).to eq 1
expect(json[:first][:items].all? { |item| item[:to].include?(ActivityPub::TagManager::COLLECTIONS[:public]) || item[:cc].include?(ActivityPub::TagManager::COLLECTIONS[:public]) }).to be true
end
end
context 'when status is private' do
let(:parent_visibility) { :private }
it 'returns http not found' do
expect(response).to have_http_status(404)
end
end
context 'when status is direct' do
let(:parent_visibility) { :direct }
it 'returns http not found' do
expect(response).to have_http_status(404)
end
end
end
context 'with signature' do
let(:remote_account) { Fabricate(:account, domain: 'example.com') }
let(:only_other_accounts) { nil }
context do
before do
get :index, params: { account_username: status.account.username, status_id: status.id, only_other_accounts: only_other_accounts }
end
context 'when status is public' do
let(:parent_visibility) { :public }
it 'returns http success' do
expect(response).to have_http_status(200)
end
it 'returns application/activity+json' do
expect(response.content_type).to eq 'application/activity+json'
end
it 'returns public Cache-Control header' do
expect(response.headers['Cache-Control']).to include 'public'
end
context 'without only_other_accounts' do
it 'returns items with account\'s own replies' do
json = body_as_json
expect(json[:first]).to be_a Hash
expect(json[:first][:items]).to be_an Array
expect(json[:first][:items].size).to eq 1
expect(json[:first][:items].all? { |item| item[:to].include?(ActivityPub::TagManager::COLLECTIONS[:public]) || item[:cc].include?(ActivityPub::TagManager::COLLECTIONS[:public]) }).to be true
end
end
context 'with only_other_accounts' do
let(:only_other_accounts) { 'true' }
it 'returns items with other public or unlisted replies' do
json = body_as_json
expect(json[:first]).to be_a Hash
expect(json[:first][:items]).to be_an Array
expect(json[:first][:items].size).to eq 2
expect(json[:first][:items].all? { |item| item[:to].include?(ActivityPub::TagManager::COLLECTIONS[:public]) || item[:cc].include?(ActivityPub::TagManager::COLLECTIONS[:public]) }).to be true
end
end
end
context 'when status is private' do
let(:parent_visibility) { :private }
it 'returns http not found' do
expect(response).to have_http_status(404)
end
end
context 'when status is direct' do
let(:parent_visibility) { :direct }
it 'returns http not found' do
expect(response).to have_http_status(404)
end
end
end
context 'when signed request account is blocked' do
before do
status.account.block!(remote_account)
get :index, params: { account_username: status.account.username, status_id: status.id }
end
context 'when status is public' do
let(:parent_visibility) { :public }
it 'returns http not found' do
expect(response).to have_http_status(404)
end
end
context 'when status is private' do
let(:parent_visibility) { :private }
it 'returns http not found' do
expect(response).to have_http_status(404)
end
end
context 'when status is direct' do
let(:parent_visibility) { :direct }
it 'returns http not found' do
expect(response).to have_http_status(404)
end
end
end
context 'when signed request account is domain blocked' do
before do
status.account.block_domain!(remote_account.domain)
get :index, params: { account_username: status.account.username, status_id: status.id }
end
context 'when status is public' do
let(:parent_visibility) { :public }
it 'returns http not found' do
expect(response).to have_http_status(404)
end
end
context 'when status is private' do
let(:parent_visibility) { :private }
it 'returns http not found' do
expect(response).to have_http_status(404)
end
end
context 'when status is direct' do
let(:parent_visibility) { :direct }
it 'returns http not found' do
expect(response).to have_http_status(404)
end
end
end
end
end
end

+ 768
- 75
spec/controllers/statuses_controller_spec.rb View File

@ -5,128 +5,821 @@ require 'rails_helper'
describe StatusesController do
render_views
describe '#show' do
context 'account is suspended' do
it 'returns gone' do
account = Fabricate(:account, suspended: true)
status = Fabricate(:status, account: account)
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 'status is not permitted' do
it 'raises ActiveRecord::RecordNotFound' do
user = Fabricate(:user)
status = Fabricate(:status)
status.account.block!(user.account)
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) }
sign_in(user)
before do
get :show, params: { account_username: status.account.username, id: status.id }
end
expect(response).to have_http_status(404)
it 'redirects to the original status' do
expect(response).to redirect_to(original_status.url)
end
end
context 'status is a reblog' do
it 'redirects to the original status' do
original_account = Fabricate(:account, domain: 'example.com')
original_status = Fabricate(:status, account: original_account, uri: 'tag:example.com,2017:foo', url: 'https://example.com/123')
status = Fabricate(:status, reblog: original_status)
context 'when status is public' do
before do
get :show, params: { account_username: status.account.username, id: status.id, format: format }
end
get :show, params: { account_username: status.account.username, id: status.id }
context 'as HTML' do
let(:format) { 'html' }
expect(response).to redirect_to(original_status.url)
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 '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 'account is not suspended and status is permitted' do
it 'assigns @account' do
status = Fabricate(:status)
get :show, params: { account_username: status.account.username, id: status.id }
expect(assigns(:account)).to eq status.account
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
it 'assigns @status' do
status = Fabricate(:status)
get :show, params: { account_username: status.account.username, id: status.id }
expect(assigns(:status)).to eq status
context 'as JSON' do
let(:format) { 'json' }
it 'returns http not found' do
expect(response).to have_http_status(404)
end
end
it 'assigns @ancestors for ancestors of the status if it is a reply' do
ancestor = Fabricate(:status)
status = Fabricate(:status, in_reply_to_id: ancestor.id)
context 'as HTML' do
let(:format) { 'html' }
get :show, params: { account_username: status.account.username, id: status.id }
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) }
expect(assigns(:ancestors)).to eq [ancestor]
before do
get :show, params: { account_username: status.account.username, id: status.id, format: format }
end
it 'assigns @ancestors for [] if it is not a reply' do
status = Fabricate(:status)
get :show, params: { account_username: status.account.username, id: status.id }
expect(assigns(:ancestors)).to eq []
context 'as JSON' do
let(:format) { 'json' }
it 'returns http not found' do
expect(response).to have_http_status(404)
end
end
it 'assigns @descendant_threads for a thread with several statuses' do
status = Fabricate(:status)
child = Fabricate(:status, in_reply_to_id: status.id)
grandchild = Fabricate(:status, in_reply_to_id: child.id)
context 'as HTML' do
let(:format) { 'html' }
get :show, params: { account_username: status.account.username, id: status.id }
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) }
expect(assigns(:descendant_threads)[0][:statuses].pluck(:id)).to eq [child.id, grandchild.id]
before do
sign_in(user)
end
it 'assigns @descendant_threads for several threads sharing the same descendant' do
status = Fabricate(:status)
child = Fabricate(:status, in_reply_to_id: status.id)
grandchildren = 2.times.map { Fabricate(:status, in_reply_to_id: child.id) }
context 'when account blocks user' do
before do
account.block!(user.account)
get :show, params: { account_username: status.account.username, id: status.id }
end
get :show, params: { account_username: status.account.username, id: status.id }
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
expect(assigns(:descendant_threads)[0][:statuses].pluck(:id)).to eq [child.id, grandchildren[0].id]
expect(assigns(:descendant_threads)[1][:statuses].pluck(:id)).to eq [grandchildren[1].id]
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
it 'assigns @max_descendant_thread_id for the last thread if it is hitting the status limit' do
stub_const 'StatusControllerConcern::DESCENDANTS_LIMIT', 1
status = Fabricate(:status)
child = Fabricate(:status, in_reply_to_id: status.id)
context 'when status is private' do
let(:status) { Fabricate(:status, account: account, visibility: :private) }
get :show, params: { account_username: status.account.username, id: status.id }
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
expect(assigns(:descendant_threads)).to eq []
expect(assigns(:max_descendant_thread_id)).to eq child.id
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
it 'assigns @descendant_threads for threads with :next_status key if they are hitting the depth limit' do
stub_const 'StatusControllerConcern::DESCENDANTS_DEPTH_LIMIT', 2
status = Fabricate(:status)
child0 = Fabricate(:status, in_reply_to_id: status.id)
child1 = Fabricate(:status, in_reply_to_id: child0.id)
child2 = Fabricate(:status, in_reply_to_id: child0.id)
context 'when status is direct' do
let(:status) { Fabricate(:status, account: account, visibility: :direct) }
get :show, params: { account_username: status.account.username, id: status.id }
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' }
expect(assigns(:descendant_threads)[0][:statuses].pluck(:id)).not_to include child1.id
expect(assigns(:descendant_threads)[1][:statuses].pluck(:id)).not_to include child2.id
expect(assigns(:descendant_threads)[0][:next_status].id).to eq child1.id
expect(assigns(:descendant_threads)[1][:next_status].id).to eq child2.id
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
it 'returns a success' do
status = Fabricate(:status)
get :show, params: { account_username: status.account.username, id: status.id }
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 '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
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 'renders statuses/show' do
status = Fabricate(:status)
get :show, params: { account_username: status.account.username, id: status.id }
expect(response).to render_template 'statuses/show'
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

+ 1
- 1
spec/fabricators/status_pin_fabricator.rb View File

@ -1,4 +1,4 @@
Fabricator(:status_pin) do
account
status
status { |attrs| Fabricate(:status, account: attrs[:account], visibility: :public) }
end

Loading…
Cancel
Save