Merge remote-tracking branch 'parent/main' into upstream-20250328
This commit is contained in:
commit
12ed20b6d5
257 changed files with 3505 additions and 2010 deletions
|
@ -1,130 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe ActivityPub::CollectionsController do
|
||||
let!(:account) { Fabricate(:account) }
|
||||
let!(:private_pinned) { Fabricate(:status, account: account, text: 'secret private stuff', visibility: :private) }
|
||||
let(:remote_account) { nil }
|
||||
|
||||
before do
|
||||
allow(controller).to receive(:signed_request_actor).and_return(remote_account)
|
||||
|
||||
Fabricate(:status_pin, account: account)
|
||||
Fabricate(:status_pin, account: account)
|
||||
Fabricate(:status_pin, account: account, status: private_pinned)
|
||||
Fabricate(:status, account: account, visibility: :private)
|
||||
end
|
||||
|
||||
describe 'GET #show' do
|
||||
subject(:response) { get :show, params: { id: id, account_username: account.username } }
|
||||
|
||||
context 'when id is "featured"' do
|
||||
let(:id) { 'featured' }
|
||||
|
||||
context 'without signature' do
|
||||
let(:remote_account) { nil }
|
||||
|
||||
it 'returns http success and correct media type and correct items' do
|
||||
expect(response)
|
||||
.to have_http_status(200)
|
||||
.and have_cacheable_headers
|
||||
expect(response.media_type).to eq 'application/activity+json'
|
||||
|
||||
expect(response.parsed_body[:orderedItems])
|
||||
.to be_an(Array)
|
||||
.and have_attributes(size: 3)
|
||||
.and include(ActivityPub::TagManager.instance.uri_for(private_pinned))
|
||||
.and not_include(private_pinned.text)
|
||||
end
|
||||
|
||||
context 'when account is permanently suspended' do
|
||||
before do
|
||||
account.suspend!
|
||||
account.deletion_request.destroy
|
||||
end
|
||||
|
||||
it 'returns http gone' do
|
||||
expect(response).to have_http_status(410)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when account is temporarily suspended' do
|
||||
before do
|
||||
account.suspend!
|
||||
end
|
||||
|
||||
it 'returns http forbidden' do
|
||||
expect(response).to have_http_status(403)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'with signature' do
|
||||
let(:remote_account) { Fabricate(:account, domain: 'example.com') }
|
||||
|
||||
context 'when getting a featured resource' do
|
||||
it 'returns http success and correct media type and expected items' do
|
||||
expect(response)
|
||||
.to have_http_status(200)
|
||||
.and have_cacheable_headers
|
||||
|
||||
expect(response.media_type).to eq 'application/activity+json'
|
||||
|
||||
expect(response.parsed_body[:orderedItems])
|
||||
.to be_an(Array)
|
||||
.and have_attributes(size: 3)
|
||||
.and include(ActivityPub::TagManager.instance.uri_for(private_pinned))
|
||||
.and not_include(private_pinned.text)
|
||||
end
|
||||
end
|
||||
|
||||
context 'with 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)
|
||||
end
|
||||
|
||||
it 'returns http success and correct media type and cache headers and empty items' do
|
||||
expect(response).to have_http_status(200)
|
||||
expect(response.media_type).to eq 'application/activity+json'
|
||||
expect(response.headers['Cache-Control']).to include 'private'
|
||||
|
||||
expect(response.parsed_body[:orderedItems])
|
||||
.to be_an(Array)
|
||||
.and be_empty
|
||||
end
|
||||
end
|
||||
|
||||
context 'when signed request account is domain blocked' do
|
||||
before do
|
||||
account.block_domain!(remote_account.domain)
|
||||
end
|
||||
|
||||
it 'returns http success and correct media type and cache headers and empty items' do
|
||||
expect(response).to have_http_status(200)
|
||||
expect(response.media_type).to eq 'application/activity+json'
|
||||
expect(response.headers['Cache-Control']).to include 'private'
|
||||
|
||||
expect(response.parsed_body[:orderedItems])
|
||||
.to be_an(Array)
|
||||
.and be_empty
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when id is not "featured"' do
|
||||
let(:id) { 'hoge' }
|
||||
|
||||
it 'returns http not found' do
|
||||
expect(response).to have_http_status(404)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,75 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe ActivityPub::FollowersSynchronizationsController do
|
||||
let!(:account) { Fabricate(:account) }
|
||||
let!(:follower_example_com_user_a) { Fabricate(:account, domain: 'example.com', uri: 'https://example.com/users/a') }
|
||||
let!(:follower_example_com_user_b) { Fabricate(:account, domain: 'example.com', uri: 'https://example.com/users/b') }
|
||||
let!(:follower_foo_com_user_a) { Fabricate(:account, domain: 'foo.com', uri: 'https://foo.com/users/a') }
|
||||
let!(:follower_example_com_instance_actor) { Fabricate(:account, username: 'instance-actor', domain: 'example.com', uri: 'https://example.com') }
|
||||
|
||||
before do
|
||||
follower_example_com_user_a.follow!(account)
|
||||
follower_example_com_user_b.follow!(account)
|
||||
follower_foo_com_user_a.follow!(account)
|
||||
follower_example_com_instance_actor.follow!(account)
|
||||
|
||||
allow(controller).to receive(:signed_request_actor).and_return(remote_account)
|
||||
end
|
||||
|
||||
describe 'GET #show' do
|
||||
context 'without signature' do
|
||||
let(:remote_account) { nil }
|
||||
|
||||
before do
|
||||
get :show, params: { account_username: account.username }
|
||||
end
|
||||
|
||||
it 'returns http not authorized' do
|
||||
expect(response).to have_http_status(401)
|
||||
end
|
||||
end
|
||||
|
||||
context 'with signature from example.com' do
|
||||
subject(:response) { get :show, params: { account_username: account.username } }
|
||||
|
||||
let(:remote_account) { Fabricate(:account, domain: 'example.com', uri: 'https://example.com/instance') }
|
||||
|
||||
it 'returns http success and cache control and activity json types and correct items' do
|
||||
expect(response).to have_http_status(200)
|
||||
expect(response.headers['Cache-Control']).to eq 'max-age=0, private'
|
||||
expect(response.media_type).to eq 'application/activity+json'
|
||||
|
||||
expect(response.parsed_body[:orderedItems])
|
||||
.to be_an(Array)
|
||||
.and contain_exactly(
|
||||
follower_example_com_instance_actor.uri,
|
||||
follower_example_com_user_a.uri,
|
||||
follower_example_com_user_b.uri
|
||||
)
|
||||
end
|
||||
|
||||
context 'when account is permanently suspended' do
|
||||
before do
|
||||
account.suspend!
|
||||
account.deletion_request.destroy
|
||||
end
|
||||
|
||||
it 'returns http gone' do
|
||||
expect(response).to have_http_status(410)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when account is temporarily suspended' do
|
||||
before do
|
||||
account.suspend!
|
||||
end
|
||||
|
||||
it 'returns http forbidden' do
|
||||
expect(response).to have_http_status(403)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,112 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe ActivityPub::InboxesController do
|
||||
let(:remote_account) { nil }
|
||||
|
||||
before do
|
||||
allow(controller).to receive(:signed_request_actor).and_return(remote_account)
|
||||
end
|
||||
|
||||
describe 'POST #create' do
|
||||
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
|
||||
|
||||
context 'with a specific account' do
|
||||
subject(:response) { post :create, params: { account_username: account.username }, body: '{}' }
|
||||
|
||||
let(:account) { Fabricate(:account) }
|
||||
|
||||
context 'when account is permanently suspended' do
|
||||
before do
|
||||
account.suspend!
|
||||
account.deletion_request.destroy
|
||||
end
|
||||
|
||||
it 'returns http gone' do
|
||||
expect(response).to have_http_status(410)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when account is temporarily suspended' do
|
||||
before do
|
||||
account.suspend!
|
||||
end
|
||||
|
||||
it 'returns http accepted' do
|
||||
expect(response).to have_http_status(202)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'with Collection-Synchronization header' do
|
||||
let(:remote_account) { Fabricate(:account, followers_url: 'https://example.com/followers', domain: 'example.com', uri: 'https://example.com/actor', protocol: :activitypub) }
|
||||
let(:synchronization_collection) { remote_account.followers_url }
|
||||
let(:synchronization_url) { 'https://example.com/followers-for-domain' }
|
||||
let(:synchronization_hash) { 'somehash' }
|
||||
let(:synchronization_header) { "collectionId=\"#{synchronization_collection}\", digest=\"#{synchronization_hash}\", url=\"#{synchronization_url}\"" }
|
||||
|
||||
before do
|
||||
allow(ActivityPub::FollowersSynchronizationWorker).to receive(:perform_async).and_return(nil)
|
||||
allow(remote_account).to receive(:local_followers_hash).and_return('somehash')
|
||||
|
||||
request.headers['Collection-Synchronization'] = synchronization_header
|
||||
post :create, body: '{}'
|
||||
end
|
||||
|
||||
context 'with mismatching target collection' do
|
||||
let(:synchronization_collection) { 'https://example.com/followers2' }
|
||||
|
||||
it 'does not start a synchronization job' do
|
||||
expect(ActivityPub::FollowersSynchronizationWorker).to_not have_received(:perform_async)
|
||||
end
|
||||
end
|
||||
|
||||
context 'with mismatching domain in partial collection attribute' do
|
||||
let(:synchronization_url) { 'https://example.org/followers' }
|
||||
|
||||
it 'does not start a synchronization job' do
|
||||
expect(ActivityPub::FollowersSynchronizationWorker).to_not have_received(:perform_async)
|
||||
end
|
||||
end
|
||||
|
||||
context 'with matching digest' do
|
||||
it 'does not start a synchronization job' do
|
||||
expect(ActivityPub::FollowersSynchronizationWorker).to_not have_received(:perform_async)
|
||||
end
|
||||
end
|
||||
|
||||
context 'with mismatching digest' do
|
||||
let(:synchronization_hash) { 'wronghash' }
|
||||
|
||||
it 'starts a synchronization job' do
|
||||
expect(ActivityPub::FollowersSynchronizationWorker).to have_received(:perform_async)
|
||||
end
|
||||
end
|
||||
|
||||
it 'returns http accepted' do
|
||||
expect(response).to have_http_status(202)
|
||||
end
|
||||
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
|
||||
end
|
||||
end
|
|
@ -1,197 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe ActivityPub::OutboxesController do
|
||||
let!(:account) { Fabricate(:account) }
|
||||
|
||||
before do
|
||||
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)
|
||||
|
||||
allow(controller).to receive(:signed_request_actor).and_return(remote_account)
|
||||
end
|
||||
|
||||
describe 'GET #show' do
|
||||
context 'without signature' do
|
||||
subject(:response) { get :show, params: { account_username: account.username, page: page } }
|
||||
|
||||
let(:remote_account) { nil }
|
||||
|
||||
context 'with page not requested' do
|
||||
let(:page) { nil }
|
||||
|
||||
it 'returns http success and correct media type and headers and items count' do
|
||||
expect(response)
|
||||
.to have_http_status(200)
|
||||
.and have_cacheable_headers
|
||||
|
||||
expect(response.media_type).to eq 'application/activity+json'
|
||||
expect(response.headers['Vary']).to be_nil
|
||||
expect(response.parsed_body[:totalItems]).to eq 4
|
||||
end
|
||||
|
||||
context 'when account is permanently suspended' do
|
||||
before do
|
||||
account.suspend!
|
||||
account.deletion_request.destroy
|
||||
end
|
||||
|
||||
it 'returns http gone' do
|
||||
expect(response).to have_http_status(410)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when account is temporarily suspended' do
|
||||
before do
|
||||
account.suspend!
|
||||
end
|
||||
|
||||
it 'returns http forbidden' do
|
||||
expect(response).to have_http_status(403)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'with page requested' do
|
||||
let(:page) { 'true' }
|
||||
|
||||
it 'returns http success and correct media type and vary header and items' do
|
||||
expect(response)
|
||||
.to have_http_status(200)
|
||||
.and have_cacheable_headers
|
||||
|
||||
expect(response.media_type).to eq 'application/activity+json'
|
||||
expect(response.headers['Vary']).to include 'Signature'
|
||||
|
||||
expect(response.parsed_body)
|
||||
.to include(
|
||||
orderedItems: be_an(Array)
|
||||
.and(have_attributes(size: 2))
|
||||
.and(all(satisfy { |item| targets_public_collection?(item) }))
|
||||
)
|
||||
end
|
||||
|
||||
context 'when account is permanently suspended' do
|
||||
before do
|
||||
account.suspend!
|
||||
account.deletion_request.destroy
|
||||
end
|
||||
|
||||
it 'returns http gone' do
|
||||
expect(response).to have_http_status(410)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when account is temporarily suspended' do
|
||||
before do
|
||||
account.suspend!
|
||||
end
|
||||
|
||||
it 'returns http forbidden' do
|
||||
expect(response).to have_http_status(403)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
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 and correct media type and headers and items' do
|
||||
expect(response).to have_http_status(200)
|
||||
expect(response.media_type).to eq 'application/activity+json'
|
||||
expect(response.headers['Cache-Control']).to eq 'max-age=60, private'
|
||||
|
||||
expect(response.parsed_body)
|
||||
.to include(
|
||||
orderedItems: be_an(Array)
|
||||
.and(have_attributes(size: 2))
|
||||
.and(all(satisfy { |item| targets_public_collection?(item) }))
|
||||
)
|
||||
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 and correct media type and headers and items' do
|
||||
expect(response).to have_http_status(200)
|
||||
expect(response.media_type).to eq 'application/activity+json'
|
||||
expect(response.headers['Cache-Control']).to eq 'max-age=60, private'
|
||||
|
||||
expect(response.parsed_body)
|
||||
.to include(
|
||||
orderedItems: be_an(Array)
|
||||
.and(have_attributes(size: 3))
|
||||
.and(all(satisfy { |item| targets_public_collection?(item) || targets_followers_collection?(item, account) }))
|
||||
)
|
||||
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 and correct media type and headers and items' do
|
||||
expect(response).to have_http_status(200)
|
||||
expect(response.media_type).to eq 'application/activity+json'
|
||||
expect(response.headers['Cache-Control']).to eq 'max-age=60, private'
|
||||
|
||||
expect(response.parsed_body)
|
||||
.to include(
|
||||
orderedItems: be_an(Array).and(be_empty)
|
||||
)
|
||||
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 and correct media type and headers and items' do
|
||||
expect(response).to have_http_status(200)
|
||||
expect(response.media_type).to eq 'application/activity+json'
|
||||
expect(response.headers['Cache-Control']).to eq 'max-age=60, private'
|
||||
|
||||
expect(response.parsed_body)
|
||||
.to include(
|
||||
orderedItems: be_an(Array).and(be_empty)
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def ap_public_collection
|
||||
ActivityPub::TagManager::COLLECTIONS[:public]
|
||||
end
|
||||
|
||||
def targets_public_collection?(item)
|
||||
item[:to].include?(ap_public_collection) || item[:cc].include?(ap_public_collection)
|
||||
end
|
||||
|
||||
def targets_followers_collection?(item, account)
|
||||
item[:to].include?(
|
||||
account_followers_url(account, ActionMailer::Base.default_url_options)
|
||||
)
|
||||
end
|
||||
end
|
|
@ -1,250 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe ActivityPub::RepliesController do
|
||||
let(:status) { Fabricate(:status, visibility: parent_visibility) }
|
||||
let(:remote_account) { Fabricate(:account, domain: 'foobar.com') }
|
||||
let(:remote_reply_id) { 'https://foobar.com/statuses/1234' }
|
||||
let(:remote_querier) { nil }
|
||||
|
||||
shared_examples 'common behavior' do
|
||||
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
|
||||
|
||||
shared_examples 'disallowed access' do
|
||||
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
|
||||
|
||||
it_behaves_like 'common behavior'
|
||||
end
|
||||
|
||||
shared_examples 'allowed access' do
|
||||
context 'when account is permanently suspended' do
|
||||
let(:parent_visibility) { :public }
|
||||
|
||||
before do
|
||||
status.account.suspend!
|
||||
status.account.deletion_request.destroy
|
||||
end
|
||||
|
||||
it 'returns http gone' do
|
||||
expect(response).to have_http_status(410)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when account is temporarily suspended' do
|
||||
let(:parent_visibility) { :public }
|
||||
|
||||
before do
|
||||
status.account.suspend!
|
||||
end
|
||||
|
||||
it 'returns http forbidden' do
|
||||
expect(response).to have_http_status(403)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when status is public' do
|
||||
let(:parent_visibility) { :public }
|
||||
|
||||
it 'returns http success and correct media type' do
|
||||
expect(response)
|
||||
.to have_http_status(200)
|
||||
.and have_cacheable_headers
|
||||
|
||||
expect(response.media_type).to eq 'application/activity+json'
|
||||
end
|
||||
|
||||
context 'without only_other_accounts' do
|
||||
it "returns items with thread author's replies" do
|
||||
expect(response.parsed_body)
|
||||
.to include(
|
||||
first: be_a(Hash).and(
|
||||
include(
|
||||
items: be_an(Array)
|
||||
.and(have_attributes(size: 1))
|
||||
.and(all(satisfy { |item| targets_public_collection?(item) }))
|
||||
)
|
||||
)
|
||||
)
|
||||
end
|
||||
|
||||
context 'when there are few self-replies' do
|
||||
it 'points next to replies from other people' do
|
||||
expect(response.parsed_body)
|
||||
.to include(
|
||||
first: be_a(Hash).and(
|
||||
include(
|
||||
next: satisfy { |value| (parsed_uri_query_values(value) & %w(only_other_accounts=true page=true)).any? }
|
||||
)
|
||||
)
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when there are many self-replies' do
|
||||
before do
|
||||
10.times { Fabricate(:status, account: status.account, thread: status, visibility: :public) }
|
||||
end
|
||||
|
||||
it 'points next to other self-replies' do
|
||||
expect(response.parsed_body)
|
||||
.to include(
|
||||
first: be_a(Hash).and(
|
||||
include(
|
||||
next: satisfy { |value| (parsed_uri_query_values(value) & %w(only_other_accounts=false page=true)).any? }
|
||||
)
|
||||
)
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'with only_other_accounts' do
|
||||
let(:only_other_accounts) { 'true' }
|
||||
|
||||
it 'returns items with other public or unlisted replies' do
|
||||
expect(response.parsed_body)
|
||||
.to include(
|
||||
first: be_a(Hash).and(
|
||||
include(items: be_an(Array).and(have_attributes(size: 3)))
|
||||
)
|
||||
)
|
||||
end
|
||||
|
||||
it 'only inlines items that are local and public or unlisted replies' do
|
||||
expect(inlined_replies)
|
||||
.to all(satisfy { |item| targets_public_collection?(item) })
|
||||
.and all(satisfy { |item| ActivityPub::TagManager.instance.local_uri?(item[:id]) })
|
||||
end
|
||||
|
||||
it 'uses ids for remote toots' do
|
||||
expect(remote_replies)
|
||||
.to all(satisfy { |item| item.is_a?(String) && !ActivityPub::TagManager.instance.local_uri?(item) })
|
||||
end
|
||||
|
||||
context 'when there are few replies' do
|
||||
it 'does not have a next page' do
|
||||
expect(response.parsed_body)
|
||||
.to include(
|
||||
first: be_a(Hash).and(not_include(next: be_present))
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when there are many replies' do
|
||||
before do
|
||||
10.times { Fabricate(:status, thread: status, visibility: :public) }
|
||||
end
|
||||
|
||||
it 'points next to other replies' do
|
||||
expect(response.parsed_body)
|
||||
.to include(
|
||||
first: be_a(Hash).and(
|
||||
include(
|
||||
next: satisfy { |value| (parsed_uri_query_values(value) & %w(only_other_accounts=true page=true)).any? }
|
||||
)
|
||||
)
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
it_behaves_like 'common behavior'
|
||||
end
|
||||
|
||||
before do
|
||||
stub_const 'ActivityPub::RepliesController::DESCENDANTS_LIMIT', 5
|
||||
allow(controller).to receive(:signed_request_actor).and_return(remote_querier)
|
||||
|
||||
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)
|
||||
|
||||
Fabricate(:status, account: remote_account, thread: status, visibility: :public, uri: remote_reply_id)
|
||||
end
|
||||
|
||||
describe 'GET #index' do
|
||||
subject(:response) { get :index, params: { account_username: status.account.username, status_id: status.id, only_other_accounts: only_other_accounts } }
|
||||
|
||||
let(:only_other_accounts) { nil }
|
||||
|
||||
context 'with no signature' do
|
||||
it_behaves_like 'allowed access'
|
||||
end
|
||||
|
||||
context 'with signature' do
|
||||
let(:remote_querier) { Fabricate(:account, domain: 'example.com') }
|
||||
|
||||
it_behaves_like 'allowed access'
|
||||
|
||||
context 'when signed request account is blocked' do
|
||||
before do
|
||||
status.account.block!(remote_querier)
|
||||
end
|
||||
|
||||
it_behaves_like 'disallowed access'
|
||||
end
|
||||
|
||||
context 'when signed request account is domain blocked' do
|
||||
before do
|
||||
status.account.block_domain!(remote_querier.domain)
|
||||
end
|
||||
|
||||
it_behaves_like 'disallowed access'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def inlined_replies
|
||||
response
|
||||
.parsed_body[:first][:items]
|
||||
.select { |x| x.is_a?(Hash) }
|
||||
end
|
||||
|
||||
def remote_replies
|
||||
response
|
||||
.parsed_body[:first][:items]
|
||||
.reject { |x| x.is_a?(Hash) }
|
||||
end
|
||||
|
||||
def parsed_uri_query_values(uri)
|
||||
Addressable::URI
|
||||
.parse(uri)
|
||||
.query
|
||||
.split('&')
|
||||
end
|
||||
|
||||
def ap_public_collection
|
||||
ActivityPub::TagManager::COLLECTIONS[:public]
|
||||
end
|
||||
|
||||
def targets_public_collection?(item)
|
||||
item[:to].include?(ap_public_collection) || item[:cc].include?(ap_public_collection)
|
||||
end
|
||||
end
|
|
@ -1,35 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe Admin::AccountActionsController do
|
||||
render_views
|
||||
|
||||
let(:user) { Fabricate(:admin_user) }
|
||||
|
||||
before do
|
||||
sign_in user, scope: :user
|
||||
end
|
||||
|
||||
describe 'GET #new' do
|
||||
let(:account) { Fabricate(:account) }
|
||||
|
||||
it 'returns http success' do
|
||||
get :new, params: { account_id: account.id }
|
||||
|
||||
expect(response).to have_http_status(:success)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'POST #create' do
|
||||
let(:account) { Fabricate(:account) }
|
||||
|
||||
it 'records the account action' do
|
||||
expect do
|
||||
post :create, params: { account_id: account.id, admin_account_action: { type: 'silence' } }
|
||||
end.to change { account.strikes.count }.by(1)
|
||||
|
||||
expect(response).to redirect_to(admin_account_path(account.id))
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,48 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe Admin::ActionLogsController do
|
||||
render_views
|
||||
|
||||
# Action logs typically cause issues when their targets are not in the database
|
||||
let!(:account) { Fabricate(:account) }
|
||||
|
||||
before do
|
||||
orphaned_log_types.map do |type|
|
||||
Fabricate(:action_log, account: account, action: 'destroy', target_type: type, target_id: 1312)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'GET #index' do
|
||||
it 'returns 200' do
|
||||
sign_in Fabricate(:admin_user)
|
||||
get :index, params: { page: 1 }
|
||||
|
||||
expect(response).to have_http_status(200)
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def orphaned_log_types
|
||||
%w(
|
||||
Account
|
||||
AccountWarning
|
||||
Announcement
|
||||
Appeal
|
||||
CanonicalEmailBlock
|
||||
CustomEmoji
|
||||
DomainAllow
|
||||
DomainBlock
|
||||
EmailDomainBlock
|
||||
Instance
|
||||
IpBlock
|
||||
Report
|
||||
Status
|
||||
UnavailableDomain
|
||||
User
|
||||
UserRole
|
||||
)
|
||||
end
|
||||
end
|
|
@ -1,48 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe Admin::ChangeEmailsController do
|
||||
render_views
|
||||
|
||||
let(:admin) { Fabricate(:admin_user) }
|
||||
|
||||
before do
|
||||
sign_in admin
|
||||
end
|
||||
|
||||
describe 'GET #show' do
|
||||
it 'returns http success' do
|
||||
user = Fabricate(:user)
|
||||
|
||||
get :show, params: { account_id: user.account.id }
|
||||
|
||||
expect(response).to have_http_status(200)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'GET #update' do
|
||||
before do
|
||||
allow(UserMailer).to receive(:confirmation_instructions)
|
||||
.and_return(instance_double(ActionMailer::MessageDelivery, deliver_later: nil))
|
||||
end
|
||||
|
||||
it 'returns http success' do
|
||||
user = Fabricate(:user)
|
||||
|
||||
previous_email = user.email
|
||||
|
||||
post :update, params: { account_id: user.account.id, user: { unconfirmed_email: 'test@example.com' } }
|
||||
|
||||
user.reload
|
||||
|
||||
expect(user.email).to eq previous_email
|
||||
expect(user.unconfirmed_email).to eq 'test@example.com'
|
||||
expect(user.confirmation_token).to_not be_nil
|
||||
|
||||
expect(UserMailer).to have_received(:confirmation_instructions).with(user, user.confirmation_token, { to: 'test@example.com' })
|
||||
|
||||
expect(response).to redirect_to(admin_account_path(user.account.id))
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,35 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe Admin::DashboardController do
|
||||
render_views
|
||||
|
||||
describe 'GET #index' do
|
||||
let(:user) { Fabricate(:owner_user) }
|
||||
|
||||
before do
|
||||
stub_system_checks
|
||||
Fabricate :software_update
|
||||
sign_in(user)
|
||||
end
|
||||
|
||||
it 'returns http success and body with system check messages' do
|
||||
get :index
|
||||
|
||||
expect(response)
|
||||
.to have_http_status(200)
|
||||
.and have_attributes(
|
||||
body: include(I18n.t('admin.system_checks.software_version_patch_check.message_html'))
|
||||
)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def stub_system_checks
|
||||
stub_const 'Admin::SystemCheck::ACTIVE_CHECKS', [
|
||||
Admin::SystemCheck::SoftwareVersionCheck,
|
||||
]
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,54 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
require 'webauthn/fake_client'
|
||||
|
||||
RSpec.describe Admin::Users::TwoFactorAuthenticationsController do
|
||||
render_views
|
||||
|
||||
let(:user) { Fabricate(:user) }
|
||||
|
||||
before do
|
||||
sign_in Fabricate(:admin_user), scope: :user
|
||||
end
|
||||
|
||||
describe 'DELETE #destroy' do
|
||||
context 'when user has OTP enabled' do
|
||||
before do
|
||||
user.update(otp_required_for_login: true)
|
||||
end
|
||||
|
||||
it 'redirects to admin account page' do
|
||||
delete :destroy, params: { user_id: user.id }
|
||||
|
||||
user.reload
|
||||
expect(user.otp_enabled?).to be false
|
||||
expect(response).to redirect_to(admin_account_path(user.account_id))
|
||||
end
|
||||
end
|
||||
|
||||
context 'when user has OTP and WebAuthn enabled' do
|
||||
let(:fake_client) { WebAuthn::FakeClient.new('http://test.host') }
|
||||
|
||||
before do
|
||||
user.update(otp_required_for_login: true, webauthn_id: WebAuthn.generate_user_id)
|
||||
|
||||
public_key_credential = WebAuthn::Credential.from_create(fake_client.create)
|
||||
Fabricate(:webauthn_credential,
|
||||
user_id: user.id,
|
||||
external_id: public_key_credential.id,
|
||||
public_key: public_key_credential.public_key,
|
||||
nickname: 'Security Key')
|
||||
end
|
||||
|
||||
it 'redirects to admin account page' do
|
||||
delete :destroy, params: { user_id: user.id }
|
||||
|
||||
user.reload
|
||||
expect(user.otp_enabled?).to be false
|
||||
expect(user.webauthn_enabled?).to be false
|
||||
expect(response).to redirect_to(admin_account_path(user.account_id))
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -342,6 +342,42 @@ RSpec.describe Auth::RegistrationsController do
|
|||
end
|
||||
end
|
||||
|
||||
context 'when age verification is enabled' do
|
||||
subject { post :create, params: { user: { account_attributes: { username: 'test' }, email: 'test@example.com', password: '12345678', password_confirmation: '12345678', agreement: 'true' }.merge(date_of_birth) } }
|
||||
|
||||
before do
|
||||
Setting.min_age = 16
|
||||
end
|
||||
|
||||
let(:date_of_birth) { {} }
|
||||
|
||||
context 'when date of birth is below age limit' do
|
||||
let(:date_of_birth) { 13.years.ago.then { |date| { 'date_of_birth(1i)': date.day.to_s, 'date_of_birth(2i)': date.month.to_s, 'date_of_birth(3i)': date.year.to_s } } }
|
||||
|
||||
it 'does not create user' do
|
||||
subject
|
||||
user = User.find_by(email: 'test@example.com')
|
||||
expect(user).to be_nil
|
||||
end
|
||||
end
|
||||
|
||||
context 'when date of birth is above age limit' do
|
||||
let(:date_of_birth) { 17.years.ago.then { |date| { 'date_of_birth(1i)': date.day.to_s, 'date_of_birth(2i)': date.month.to_s, 'date_of_birth(3i)': date.year.to_s } } }
|
||||
|
||||
it 'redirects to setup and creates user' do
|
||||
subject
|
||||
|
||||
expect(response).to redirect_to auth_setup_path
|
||||
|
||||
expect(User.find_by(email: 'test@example.com'))
|
||||
.to be_present
|
||||
.and have_attributes(
|
||||
age_verified_at: not_eq(nil)
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when max user count is set' do
|
||||
subject do
|
||||
post :create, params: { user: { account_attributes: { username: 'test' }, email: 'test@example.com', password: '12345678', password_confirmation: '12345678', agreement: 'true' } }
|
||||
|
|
|
@ -1,32 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe Disputes::StrikesController do
|
||||
render_views
|
||||
|
||||
before { sign_in current_user, scope: :user }
|
||||
|
||||
describe '#show' do
|
||||
let(:current_user) { Fabricate(:user) }
|
||||
let(:strike) { Fabricate(:account_warning, target_account: current_user.account) }
|
||||
|
||||
before do
|
||||
get :show, params: { id: strike.id }
|
||||
end
|
||||
|
||||
context 'when meant for the user' do
|
||||
it 'returns http success' do
|
||||
expect(response).to have_http_status(:success)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when meant for a different user' do
|
||||
let(:strike) { Fabricate(:account_warning) }
|
||||
|
||||
it 'returns http forbidden' do
|
||||
expect(response).to have_http_status(403)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,45 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe Filters::StatusesController do
|
||||
render_views
|
||||
|
||||
describe 'GET #index' do
|
||||
let(:filter) { Fabricate(:custom_filter) }
|
||||
|
||||
context 'with signed out user' do
|
||||
it 'redirects' do
|
||||
get :index, params: { filter_id: filter }
|
||||
|
||||
expect(response).to be_redirect
|
||||
end
|
||||
end
|
||||
|
||||
context 'with a signed in user' do
|
||||
context 'with the filter user signed in' do
|
||||
before do
|
||||
sign_in(filter.account.user)
|
||||
get :index, params: { filter_id: filter }
|
||||
end
|
||||
|
||||
it 'returns http success and private cache control headers' do
|
||||
expect(response).to have_http_status(200)
|
||||
|
||||
expect(response.headers['Cache-Control']).to include('private, no-store')
|
||||
end
|
||||
end
|
||||
|
||||
context 'with another user signed in' do
|
||||
before do
|
||||
sign_in(Fabricate(:user))
|
||||
get :index, params: { filter_id: filter }
|
||||
end
|
||||
|
||||
it 'returns http not found' do
|
||||
expect(response).to have_http_status(404)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,53 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe IntentsController do
|
||||
render_views
|
||||
|
||||
let(:user) { Fabricate(:user) }
|
||||
|
||||
before { sign_in user, scope: :user }
|
||||
|
||||
describe 'GET #show' do
|
||||
subject { get :show, params: { uri: uri } }
|
||||
|
||||
context 'when schema is web+mastodon' do
|
||||
context 'when host is follow' do
|
||||
let(:uri) { 'web+mastodon://follow?uri=test' }
|
||||
|
||||
it { is_expected.to redirect_to authorize_interaction_path(uri: 'test') }
|
||||
end
|
||||
|
||||
context 'when host is share' do
|
||||
let(:uri) { 'web+mastodon://share?text=test' }
|
||||
|
||||
it { is_expected.to redirect_to share_path(text: 'test') }
|
||||
end
|
||||
|
||||
context 'when host is none of the above' do
|
||||
let(:uri) { 'web+mastodon://test' }
|
||||
|
||||
it { is_expected.to have_http_status 404 }
|
||||
end
|
||||
end
|
||||
|
||||
context 'when schema is not web+mastodon' do
|
||||
let(:uri) { 'api+mastodon://test.com' }
|
||||
|
||||
it { is_expected.to have_http_status 404 }
|
||||
end
|
||||
|
||||
context 'when uri param is blank' do
|
||||
let(:uri) { '' }
|
||||
|
||||
it { is_expected.to have_http_status 404 }
|
||||
end
|
||||
|
||||
context 'when uri is invalid' do
|
||||
let(:uri) { 'invalid uri://test.com' }
|
||||
|
||||
it { is_expected.to have_http_status 404 }
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,23 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe Oauth::TokensController do
|
||||
describe 'POST #revoke' do
|
||||
let!(:user) { Fabricate(:user) }
|
||||
let!(:application) { Fabricate(:application, confidential: false) }
|
||||
let!(:access_token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, application: application) }
|
||||
let!(:web_push_subscription) { Fabricate(:web_push_subscription, user: user, access_token: access_token) }
|
||||
|
||||
it 'revokes the token and removes subscriptions' do
|
||||
post :revoke, params: { client_id: application.uid, token: access_token.token }
|
||||
|
||||
expect(access_token.reload.revoked_at)
|
||||
.to_not be_nil
|
||||
expect(Web::PushSubscription.where(access_token: access_token).count)
|
||||
.to eq(0)
|
||||
expect { web_push_subscription.reload }
|
||||
.to raise_error(ActiveRecord::RecordNotFound)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,87 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe Settings::DeletesController do
|
||||
render_views
|
||||
|
||||
describe 'GET #show' do
|
||||
context 'when signed in' do
|
||||
let(:user) { Fabricate(:user) }
|
||||
|
||||
before do
|
||||
sign_in user, scope: :user
|
||||
get :show
|
||||
end
|
||||
|
||||
it 'renders confirmation page with private cache control headers', :aggregate_failures do
|
||||
expect(response).to have_http_status(200)
|
||||
expect(response.headers['Cache-Control']).to include('private, no-store')
|
||||
end
|
||||
|
||||
context 'when suspended' do
|
||||
let(:user) { Fabricate(:user, account_attributes: { suspended_at: Time.now.utc }) }
|
||||
|
||||
it 'returns http forbidden with private cache control headers', :aggregate_failures do
|
||||
expect(response).to have_http_status(403)
|
||||
expect(response.headers['Cache-Control']).to include('private, no-store')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when not signed in' do
|
||||
it 'redirects' do
|
||||
get :show
|
||||
expect(response).to redirect_to '/auth/sign_in'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'DELETE #destroy' do
|
||||
context 'when signed in' do
|
||||
let(:user) { Fabricate(:user, password: 'petsmoldoggos') }
|
||||
|
||||
before do
|
||||
sign_in user, scope: :user
|
||||
end
|
||||
|
||||
context 'with correct password' do
|
||||
before do
|
||||
delete :destroy, params: { form_delete_confirmation: { password: 'petsmoldoggos' } }
|
||||
end
|
||||
|
||||
it 'removes user record and redirects', :aggregate_failures, :inline_jobs do
|
||||
expect(response).to redirect_to '/auth/sign_in'
|
||||
expect(User.find_by(id: user.id)).to be_nil
|
||||
expect(user.account.reload).to be_suspended
|
||||
expect(CanonicalEmailBlock.block?(user.email)).to be false
|
||||
end
|
||||
|
||||
context 'when suspended' do
|
||||
let(:user) { Fabricate(:user, account_attributes: { suspended_at: Time.now.utc }) }
|
||||
|
||||
it 'returns http forbidden' do
|
||||
expect(response).to have_http_status(403)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'with incorrect password' do
|
||||
before do
|
||||
delete :destroy, params: { form_delete_confirmation: { password: 'blaze420' } }
|
||||
end
|
||||
|
||||
it 'redirects back to confirmation page' do
|
||||
expect(response).to redirect_to settings_delete_path
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when not signed in' do
|
||||
it 'redirects' do
|
||||
delete :destroy
|
||||
expect(response).to redirect_to '/auth/sign_in'
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
Loading…
Add table
Add a link
Reference in a new issue