Merge tag 'v3.2.0' into hometown-dev
This commit is contained in:
@ -5,6 +5,21 @@ RSpec.describe AccountsController, type: :controller do
|
||||
|
||||
let(:account) { Fabricate(:user).account }
|
||||
|
||||
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(:format) { 'html' }
|
||||
|
||||
@ -323,9 +338,7 @@ RSpec.describe AccountsController, type: :controller 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_behaves_like 'cachable response'
|
||||
|
||||
it 'renders account' do
|
||||
json = body_as_json
|
||||
@ -343,9 +356,7 @@ RSpec.describe AccountsController, type: :controller 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_behaves_like 'cachable response'
|
||||
|
||||
it 'returns Vary header with Signature' do
|
||||
expect(response.headers['Vary']).to include 'Signature'
|
||||
@ -401,9 +412,7 @@ RSpec.describe AccountsController, type: :controller 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_behaves_like 'cachable response'
|
||||
|
||||
it 'renders account' do
|
||||
json = body_as_json
|
||||
@ -447,9 +456,7 @@ RSpec.describe AccountsController, type: :controller do
|
||||
expect(response).to have_http_status(200)
|
||||
end
|
||||
|
||||
it 'returns public Cache-Control header' do
|
||||
expect(response.headers['Cache-Control']).to include 'public'
|
||||
end
|
||||
it_behaves_like 'cachable response'
|
||||
end
|
||||
|
||||
context do
|
||||
|
||||
@ -6,6 +6,21 @@ RSpec.describe ActivityPub::CollectionsController, type: :controller do
|
||||
let!(:account) { Fabricate(:account) }
|
||||
let(:remote_account) { nil }
|
||||
|
||||
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
|
||||
|
||||
before do
|
||||
allow(controller).to receive(:signed_request_account).and_return(remote_account)
|
||||
|
||||
@ -31,9 +46,7 @@ RSpec.describe ActivityPub::CollectionsController, type: :controller 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_behaves_like 'cachable response'
|
||||
|
||||
it 'returns orderedItems with pinned statuses' do
|
||||
json = body_as_json
|
||||
@ -58,9 +71,7 @@ RSpec.describe ActivityPub::CollectionsController, type: :controller 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_behaves_like 'cachable response'
|
||||
|
||||
it 'returns orderedItems with pinned statuses' do
|
||||
json = body_as_json
|
||||
|
||||
@ -3,6 +3,21 @@ require 'rails_helper'
|
||||
RSpec.describe ActivityPub::OutboxesController, type: :controller do
|
||||
let!(:account) { Fabricate(:account) }
|
||||
|
||||
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
|
||||
|
||||
before do
|
||||
Fabricate(:status, account: account, visibility: :public)
|
||||
Fabricate(:status, account: account, visibility: :unlisted)
|
||||
@ -39,9 +54,7 @@ RSpec.describe ActivityPub::OutboxesController, type: :controller do
|
||||
expect(json[:totalItems]).to eq 4
|
||||
end
|
||||
|
||||
it 'returns public Cache-Control header' do
|
||||
expect(response.headers['Cache-Control']).to include 'public'
|
||||
end
|
||||
it_behaves_like 'cachable response'
|
||||
end
|
||||
|
||||
context 'with page requested' do
|
||||
@ -62,9 +75,7 @@ RSpec.describe ActivityPub::OutboxesController, type: :controller do
|
||||
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
|
||||
it_behaves_like 'cachable response'
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@ -4,8 +4,24 @@ require 'rails_helper'
|
||||
|
||||
RSpec.describe ActivityPub::RepliesController, type: :controller do
|
||||
let(:status) { Fabricate(:status, visibility: parent_visibility) }
|
||||
let(:remote_reply_id) { nil }
|
||||
let(:remote_account) { nil }
|
||||
|
||||
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
|
||||
|
||||
before do
|
||||
allow(controller).to receive(:signed_request_account).and_return(remote_account)
|
||||
|
||||
@ -14,6 +30,8 @@ RSpec.describe ActivityPub::RepliesController, type: :controller do
|
||||
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) if remote_reply_id
|
||||
end
|
||||
|
||||
describe 'GET #index' do
|
||||
@ -33,9 +51,7 @@ RSpec.describe ActivityPub::RepliesController, type: :controller 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_behaves_like 'cachable response'
|
||||
|
||||
it 'returns items with account\'s own replies' do
|
||||
json = body_as_json
|
||||
@ -84,9 +100,7 @@ RSpec.describe ActivityPub::RepliesController, type: :controller 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_behaves_like 'cachable response'
|
||||
|
||||
context 'without only_other_accounts' do
|
||||
it 'returns items with account\'s own replies' do
|
||||
@ -110,6 +124,20 @@ RSpec.describe ActivityPub::RepliesController, type: :controller do
|
||||
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
|
||||
|
||||
context 'with remote responses' do
|
||||
let(:remote_reply_id) { 'foo' }
|
||||
|
||||
it 'returned items are all inlined local toots or are ids' 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 3
|
||||
expect(json[:first][:items].all? { |item| item.is_a?(Hash) ? ActivityPub::TagManager.instance.local_uri?(item[:id]) : item.is_a?(String) }).to be true
|
||||
expect(json[:first][:items]).to include remote_reply_id
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@ -82,6 +82,36 @@ describe Api::V1::Statuses::ReblogsController do
|
||||
end
|
||||
end
|
||||
|
||||
context 'with public status when blocked by its author' do
|
||||
let(:status) { Fabricate(:status, account: user.account) }
|
||||
|
||||
before do
|
||||
ReblogService.new.call(user.account, status)
|
||||
status.account.block!(user.account)
|
||||
post :destroy, params: { status_id: status.id }
|
||||
end
|
||||
|
||||
it 'returns http success' do
|
||||
expect(response).to have_http_status(200)
|
||||
end
|
||||
|
||||
it 'updates the reblogs count' do
|
||||
expect(status.reblogs.count).to eq 0
|
||||
end
|
||||
|
||||
it 'updates the reblogged attribute' do
|
||||
expect(user.account.reblogged?(status)).to be false
|
||||
end
|
||||
|
||||
it 'returns json with updated attributes' do
|
||||
hash_body = body_as_json
|
||||
|
||||
expect(hash_body[:id]).to eq status.id.to_s
|
||||
expect(hash_body[:reblogs_count]).to eq 0
|
||||
expect(hash_body[:reblogged]).to be false
|
||||
end
|
||||
end
|
||||
|
||||
context 'with private status that was not reblogged' do
|
||||
let(:status) { Fabricate(:status, visibility: :private) }
|
||||
|
||||
|
||||
@ -215,7 +215,7 @@ RSpec.describe Auth::SessionsController, type: :controller do
|
||||
|
||||
context 'using a valid OTP' do
|
||||
before do
|
||||
post :create, params: { user: { otp_attempt: user.current_otp } }, session: { otp_user_id: user.id }
|
||||
post :create, params: { user: { otp_attempt: user.current_otp } }, session: { attempt_user_id: user.id }
|
||||
end
|
||||
|
||||
it 'redirects to home' do
|
||||
@ -230,7 +230,7 @@ RSpec.describe Auth::SessionsController, type: :controller do
|
||||
context 'when the server has an decryption error' do
|
||||
before do
|
||||
allow_any_instance_of(User).to receive(:validate_and_consume_otp!).and_raise(OpenSSL::Cipher::CipherError)
|
||||
post :create, params: { user: { otp_attempt: user.current_otp } }, session: { otp_user_id: user.id }
|
||||
post :create, params: { user: { otp_attempt: user.current_otp } }, session: { attempt_user_id: user.id }
|
||||
end
|
||||
|
||||
it 'shows a login error' do
|
||||
@ -244,7 +244,7 @@ RSpec.describe Auth::SessionsController, type: :controller do
|
||||
|
||||
context 'using a valid recovery code' do
|
||||
before do
|
||||
post :create, params: { user: { otp_attempt: recovery_codes.first } }, session: { otp_user_id: user.id }
|
||||
post :create, params: { user: { otp_attempt: recovery_codes.first } }, session: { attempt_user_id: user.id }
|
||||
end
|
||||
|
||||
it 'redirects to home' do
|
||||
@ -258,7 +258,7 @@ RSpec.describe Auth::SessionsController, type: :controller do
|
||||
|
||||
context 'using an invalid OTP' do
|
||||
before do
|
||||
post :create, params: { user: { otp_attempt: 'wrongotp' } }, session: { otp_user_id: user.id }
|
||||
post :create, params: { user: { otp_attempt: 'wrongotp' } }, session: { attempt_user_id: user.id }
|
||||
end
|
||||
|
||||
it 'shows a login error' do
|
||||
@ -270,5 +270,63 @@ RSpec.describe Auth::SessionsController, type: :controller do
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when 2FA is disabled and IP is unfamiliar' do
|
||||
let!(:user) { Fabricate(:user, email: 'x@y.com', password: 'abcdefgh', current_sign_in_at: 3.weeks.ago, current_sign_in_ip: '0.0.0.0') }
|
||||
|
||||
before do
|
||||
request.remote_ip = '10.10.10.10'
|
||||
request.user_agent = 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:75.0) Gecko/20100101 Firefox/75.0'
|
||||
|
||||
allow(UserMailer).to receive(:sign_in_token).and_return(double('email', deliver_later!: nil))
|
||||
end
|
||||
|
||||
context 'using email and password' do
|
||||
before do
|
||||
post :create, params: { user: { email: user.email, password: user.password } }
|
||||
end
|
||||
|
||||
it 'renders sign in token authentication page' do
|
||||
expect(controller).to render_template("sign_in_token")
|
||||
end
|
||||
|
||||
it 'generates sign in token' do
|
||||
expect(user.reload.sign_in_token).to_not be_nil
|
||||
end
|
||||
|
||||
it 'sends sign in token e-mail' do
|
||||
expect(UserMailer).to have_received(:sign_in_token)
|
||||
end
|
||||
end
|
||||
|
||||
context 'using a valid sign in token' do
|
||||
before do
|
||||
user.generate_sign_in_token && user.save
|
||||
post :create, params: { user: { sign_in_token_attempt: user.sign_in_token } }, session: { attempt_user_id: user.id }
|
||||
end
|
||||
|
||||
it 'redirects to home' do
|
||||
expect(response).to redirect_to(root_path)
|
||||
end
|
||||
|
||||
it 'logs the user in' do
|
||||
expect(controller.current_user).to eq user
|
||||
end
|
||||
end
|
||||
|
||||
context 'using an invalid sign in token' do
|
||||
before do
|
||||
post :create, params: { user: { sign_in_token_attempt: 'wrongotp' } }, session: { attempt_user_id: user.id }
|
||||
end
|
||||
|
||||
it 'shows a login error' do
|
||||
expect(flash[:alert]).to match I18n.t('users.invalid_sign_in_token')
|
||||
end
|
||||
|
||||
it "doesn't log the user in" do
|
||||
expect(controller.current_user).to be_nil
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@ -5,6 +5,21 @@ 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) }
|
||||
@ -80,9 +95,7 @@ describe StatusesController 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_behaves_like 'cachable response'
|
||||
|
||||
it 'returns Content-Type header' do
|
||||
expect(response.headers['Content-Type']).to include 'application/activity+json'
|
||||
@ -470,9 +483,7 @@ describe StatusesController 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_behaves_like 'cachable response'
|
||||
|
||||
it 'returns Content-Type header' do
|
||||
expect(response.headers['Content-Type']).to include 'application/activity+json'
|
||||
|
||||
@ -84,5 +84,15 @@ PEM
|
||||
|
||||
expect(response).to have_http_status(:not_found)
|
||||
end
|
||||
|
||||
it 'returns http bad request when not given a resource parameter' do
|
||||
get :show, params: { }, format: :json
|
||||
expect(response).to have_http_status(:bad_request)
|
||||
end
|
||||
|
||||
it 'returns http bad request when given a nonsense parameter' do
|
||||
get :show, params: { resource: 'df/:dfkj' }
|
||||
expect(response).to have_http_status(:bad_request)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
Fabricator(:account_migration) do
|
||||
account
|
||||
target_account
|
||||
target_account { |attrs| Fabricate(:account, also_known_as: [ActivityPub::TagManager.instance.uri_for(attrs[:account])]) }
|
||||
acct { |attrs| attrs[:target_account].acct }
|
||||
followers_count 1234
|
||||
acct 'test@example.com'
|
||||
end
|
||||
|
||||
5
spec/fabricators/account_note_fabricator.rb
Normal file
5
spec/fabricators/account_note_fabricator.rb
Normal file
@ -0,0 +1,5 @@
|
||||
Fabricator(:account_note) do
|
||||
account
|
||||
target_account { Fabricate(:account) }
|
||||
comment "User note text"
|
||||
end
|
||||
8
spec/fabricators/device_fabricator.rb
Normal file
8
spec/fabricators/device_fabricator.rb
Normal file
@ -0,0 +1,8 @@
|
||||
Fabricator(:device) do
|
||||
access_token
|
||||
account
|
||||
device_id { Faker::Number.number(digits: 5) }
|
||||
name { Faker::App.name }
|
||||
fingerprint_key { Base64.strict_encode64(Ed25519::SigningKey.generate.verify_key.to_bytes) }
|
||||
identity_key { Base64.strict_encode64(Ed25519::SigningKey.generate.verify_key.to_bytes) }
|
||||
end
|
||||
8
spec/fabricators/encrypted_message_fabricator.rb
Normal file
8
spec/fabricators/encrypted_message_fabricator.rb
Normal file
@ -0,0 +1,8 @@
|
||||
Fabricator(:encrypted_message) do
|
||||
device
|
||||
from_account
|
||||
from_device_id { Faker::Number.number(digits: 5) }
|
||||
type 0
|
||||
body ""
|
||||
message_franking ""
|
||||
end
|
||||
11
spec/fabricators/one_time_key_fabricator.rb
Normal file
11
spec/fabricators/one_time_key_fabricator.rb
Normal file
@ -0,0 +1,11 @@
|
||||
Fabricator(:one_time_key) do
|
||||
device
|
||||
key_id { Faker::Alphanumeric.alphanumeric(number: 10) }
|
||||
key { Base64.strict_encode64(Ed25519::SigningKey.generate.verify_key.to_bytes) }
|
||||
|
||||
signature do |attrs|
|
||||
signing_key = Ed25519::SigningKey.generate
|
||||
attrs[:device].update(fingerprint_key: Base64.strict_encode64(signing_key.verify_key.to_bytes))
|
||||
Base64.strict_encode64(signing_key.sign(attrs[:key]))
|
||||
end
|
||||
end
|
||||
3
spec/fabricators/system_key_fabricator.rb
Normal file
3
spec/fabricators/system_key_fabricator.rb
Normal file
@ -0,0 +1,3 @@
|
||||
Fabricator(:system_key) do
|
||||
|
||||
end
|
||||
@ -28,6 +28,28 @@ RSpec.describe ActivityPub::Activity::Block do
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the recipient is already blocked' do
|
||||
before do
|
||||
sender.block!(recipient, uri: 'old')
|
||||
end
|
||||
|
||||
describe '#perform' do
|
||||
subject { described_class.new(json, sender) }
|
||||
|
||||
before do
|
||||
subject.perform
|
||||
end
|
||||
|
||||
it 'creates a block from sender to recipient' do
|
||||
expect(sender.blocking?(recipient)).to be true
|
||||
end
|
||||
|
||||
it 'sets the uri to that of last received block activity' do
|
||||
expect(sender.block_relationships.find_by(target_account: recipient).uri).to eq 'foo'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the recipient follows the sender' do
|
||||
before do
|
||||
recipient.follow!(sender)
|
||||
|
||||
@ -287,6 +287,31 @@ RSpec.describe ActivityPub::Activity::Create do
|
||||
end
|
||||
end
|
||||
|
||||
context 'with media attachments with long description as summary' do
|
||||
let(:object_json) do
|
||||
{
|
||||
id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
|
||||
type: 'Note',
|
||||
content: 'Lorem ipsum',
|
||||
attachment: [
|
||||
{
|
||||
type: 'Document',
|
||||
mediaType: 'image/png',
|
||||
url: 'http://example.com/attachment.png',
|
||||
summary: '*' * 1500,
|
||||
},
|
||||
],
|
||||
}
|
||||
end
|
||||
|
||||
it 'creates status' do
|
||||
status = sender.statuses.first
|
||||
|
||||
expect(status).to_not be_nil
|
||||
expect(status.media_attachments.map(&:description)).to include('*' * 1500)
|
||||
end
|
||||
end
|
||||
|
||||
context 'with media attachments with focal points' do
|
||||
let(:object_json) do
|
||||
{
|
||||
@ -554,6 +579,62 @@ RSpec.describe ActivityPub::Activity::Create do
|
||||
end
|
||||
end
|
||||
|
||||
context 'with an encrypted message' do
|
||||
let(:recipient) { Fabricate(:account) }
|
||||
let(:target_device) { Fabricate(:device, account: recipient) }
|
||||
|
||||
subject { described_class.new(json, sender, delivery: true, delivered_to_account_id: recipient.id) }
|
||||
|
||||
let(:object_json) do
|
||||
{
|
||||
id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
|
||||
type: 'EncryptedMessage',
|
||||
attributedTo: {
|
||||
type: 'Device',
|
||||
deviceId: '1234',
|
||||
},
|
||||
to: {
|
||||
type: 'Device',
|
||||
deviceId: target_device.device_id,
|
||||
},
|
||||
messageType: 1,
|
||||
cipherText: 'Foo',
|
||||
messageFranking: 'Baz678',
|
||||
digest: {
|
||||
digestAlgorithm: 'Bar456',
|
||||
digestValue: 'Foo123',
|
||||
},
|
||||
}
|
||||
end
|
||||
|
||||
before do
|
||||
subject.perform
|
||||
end
|
||||
|
||||
it 'creates an encrypted message' do
|
||||
encrypted_message = target_device.encrypted_messages.reload.first
|
||||
|
||||
expect(encrypted_message).to_not be_nil
|
||||
expect(encrypted_message.from_device_id).to eq '1234'
|
||||
expect(encrypted_message.from_account).to eq sender
|
||||
expect(encrypted_message.type).to eq 1
|
||||
expect(encrypted_message.body).to eq 'Foo'
|
||||
expect(encrypted_message.digest).to eq 'Foo123'
|
||||
end
|
||||
|
||||
it 'creates a message franking' do
|
||||
encrypted_message = target_device.encrypted_messages.reload.first
|
||||
message_franking = encrypted_message.message_franking
|
||||
|
||||
crypt = ActiveSupport::MessageEncryptor.new(SystemKey.current_key, serializer: Oj)
|
||||
json = crypt.decrypt_and_verify(message_franking)
|
||||
|
||||
expect(json['source_account_id']).to eq sender.id
|
||||
expect(json['target_account_id']).to eq recipient.id
|
||||
expect(json['original_franking']).to eq 'Baz678'
|
||||
end
|
||||
end
|
||||
|
||||
context 'when sender is followed by local users' do
|
||||
subject { described_class.new(json, sender, delivery: true) }
|
||||
|
||||
|
||||
@ -50,6 +50,19 @@ RSpec.describe ActivityPub::Activity::Undo do
|
||||
expect(sender.reblogged?(status)).to be false
|
||||
end
|
||||
end
|
||||
|
||||
context 'with only object uri' do
|
||||
let(:object_json) { 'bar' }
|
||||
|
||||
before do
|
||||
Fabricate(:status, reblog: status, account: sender, uri: 'bar')
|
||||
end
|
||||
|
||||
it 'deletes the reblog by uri' do
|
||||
subject.perform
|
||||
expect(sender.reblogged?(status)).to be false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'with Accept' do
|
||||
@ -91,13 +104,22 @@ RSpec.describe ActivityPub::Activity::Undo do
|
||||
end
|
||||
|
||||
before do
|
||||
sender.block!(recipient)
|
||||
sender.block!(recipient, uri: 'bar')
|
||||
end
|
||||
|
||||
it 'deletes block from sender to recipient' do
|
||||
subject.perform
|
||||
expect(sender.blocking?(recipient)).to be false
|
||||
end
|
||||
|
||||
context 'with only object uri' do
|
||||
let(:object_json) { 'bar' }
|
||||
|
||||
it 'deletes block from sender to recipient' do
|
||||
subject.perform
|
||||
expect(sender.blocking?(recipient)).to be false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'with Follow' do
|
||||
@ -113,13 +135,22 @@ RSpec.describe ActivityPub::Activity::Undo do
|
||||
end
|
||||
|
||||
before do
|
||||
sender.follow!(recipient)
|
||||
sender.follow!(recipient, uri: 'bar')
|
||||
end
|
||||
|
||||
it 'deletes follow from sender to recipient' do
|
||||
subject.perform
|
||||
expect(sender.following?(recipient)).to be false
|
||||
end
|
||||
|
||||
context 'with only object uri' do
|
||||
let(:object_json) { 'bar' }
|
||||
|
||||
it 'deletes follow from sender to recipient' do
|
||||
subject.perform
|
||||
expect(sender.following?(recipient)).to be false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'with Like' do
|
||||
|
||||
@ -429,4 +429,29 @@ RSpec.describe FeedManager do
|
||||
expect(Redis.current).to have_received(:publish).with("timeline:#{receiver.id}", deletion)
|
||||
end
|
||||
end
|
||||
|
||||
describe '#clear_from_timeline' do
|
||||
let(:account) { Fabricate(:account) }
|
||||
let(:followed_account) { Fabricate(:account) }
|
||||
let(:target_account) { Fabricate(:account) }
|
||||
let(:status_1) { Fabricate(:status, account: followed_account) }
|
||||
let(:status_2) { Fabricate(:status, account: target_account) }
|
||||
let(:status_3) { Fabricate(:status, account: followed_account, mentions: [Fabricate(:mention, account: target_account)]) }
|
||||
let(:status_4) { Fabricate(:status, mentions: [Fabricate(:mention, account: target_account)]) }
|
||||
let(:status_5) { Fabricate(:status, account: followed_account, reblog: status_4) }
|
||||
let(:status_6) { Fabricate(:status, account: followed_account, reblog: status_2) }
|
||||
let(:status_7) { Fabricate(:status, account: followed_account) }
|
||||
|
||||
before do
|
||||
[status_1, status_3, status_5, status_6, status_7].each do |status|
|
||||
Redis.current.zadd("feed:home:#{account.id}", status.id, status.id)
|
||||
end
|
||||
end
|
||||
|
||||
it 'correctly cleans the timeline' do
|
||||
FeedManager.instance.clear_from_timeline(account, target_account)
|
||||
|
||||
expect(Redis.current.zrange("feed:home:#{account.id}", 0, -1)).to eq [status_1.id.to_s, status_7.id.to_s]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@ -39,7 +39,7 @@ describe WebfingerResource do
|
||||
|
||||
expect {
|
||||
WebfingerResource.new(resource).username
|
||||
}.to raise_error(ActiveRecord::RecordNotFound)
|
||||
}.to raise_error(WebfingerResource::InvalidRequest)
|
||||
end
|
||||
|
||||
it 'finds the username in a valid https route' do
|
||||
@ -123,5 +123,15 @@ describe WebfingerResource do
|
||||
expect(result).to eq 'alice'
|
||||
end
|
||||
end
|
||||
|
||||
describe 'with a nonsense resource' do
|
||||
it 'raises InvalidRequest' do
|
||||
resource = 'df/:dfkj'
|
||||
|
||||
expect {
|
||||
WebfingerResource.new(resource).username
|
||||
}.to raise_error(WebfingerResource::InvalidRequest)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@ -59,4 +59,9 @@ class UserMailerPreview < ActionMailer::Preview
|
||||
def warning
|
||||
UserMailer.warning(User.first, AccountWarning.new(text: '', action: :silence), [Status.first.id])
|
||||
end
|
||||
|
||||
# Preview this email at http://localhost:3000/rails/mailers/user_mailer/sign_in_token
|
||||
def sign_in_token
|
||||
UserMailer.sign_in_token(User.first.tap { |user| user.generate_sign_in_token }, '127.0.0.1', 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:75.0) Gecko/20100101 Firefox/75.0', Time.now.utc)
|
||||
end
|
||||
end
|
||||
|
||||
@ -29,201 +29,175 @@ RSpec.describe Remotable do
|
||||
end
|
||||
end
|
||||
|
||||
context 'Remotable module is included' do
|
||||
before do
|
||||
class Foo
|
||||
include Remotable
|
||||
|
||||
remotable_attachment :hoge, 1.kilobyte
|
||||
end
|
||||
end
|
||||
|
||||
let(:attribute_name) { "#{hoge}_remote_url".to_sym }
|
||||
let(:code) { 200 }
|
||||
let(:file) { 'filename="foo.txt"' }
|
||||
let(:foo) { Foo.new }
|
||||
let(:headers) { { 'content-disposition' => file } }
|
||||
let(:hoge) { :hoge }
|
||||
let(:url) { 'https://google.com' }
|
||||
|
||||
it 'defines a method #hoge_remote_url=' do
|
||||
expect(foo).to respond_to(:hoge_remote_url=)
|
||||
end
|
||||
|
||||
it 'defines a method #reset_hoge!' do
|
||||
expect(foo).to respond_to(:reset_hoge!)
|
||||
end
|
||||
|
||||
it 'defines a method #download_hoge!' do
|
||||
expect(foo).to respond_to(:download_hoge!)
|
||||
end
|
||||
|
||||
describe '#hoge_remote_url=' do
|
||||
before do
|
||||
class Foo
|
||||
include Remotable
|
||||
remotable_attachment :hoge, 1.kilobyte
|
||||
stub_request(:get, url).to_return(status: code, headers: headers)
|
||||
end
|
||||
|
||||
it 'always returns its argument' do
|
||||
[nil, '', [], {}].each do |arg|
|
||||
expect(foo.hoge_remote_url = arg).to be arg
|
||||
end
|
||||
end
|
||||
|
||||
let(:attribute_name) { "#{hoge}_remote_url".to_sym }
|
||||
let(:code) { 200 }
|
||||
let(:file) { 'filename="foo.txt"' }
|
||||
let(:foo) { Foo.new }
|
||||
let(:headers) { { 'content-disposition' => file } }
|
||||
let(:hoge) { :hoge }
|
||||
let(:url) { 'https://google.com' }
|
||||
|
||||
let(:request) do
|
||||
stub_request(:get, url)
|
||||
.to_return(status: code, headers: headers)
|
||||
end
|
||||
|
||||
it 'defines a method #hoge_remote_url=' do
|
||||
expect(foo).to respond_to(:hoge_remote_url=)
|
||||
end
|
||||
|
||||
it 'defines a method #reset_hoge!' do
|
||||
expect(foo).to respond_to(:reset_hoge!)
|
||||
end
|
||||
|
||||
describe '#hoge_remote_url' do
|
||||
context 'with an invalid URL' do
|
||||
before do
|
||||
request
|
||||
allow(Addressable::URI).to receive_message_chain(:parse, :normalize).with(url).with(no_args).and_raise(Addressable::URI::InvalidURIError)
|
||||
end
|
||||
|
||||
it 'always returns arg' do
|
||||
[nil, '', [], {}].each do |arg|
|
||||
expect(foo.hoge_remote_url = arg).to be arg
|
||||
it 'makes no request' do
|
||||
foo.hoge_remote_url = url
|
||||
expect(a_request(:get, url)).to_not have_been_made
|
||||
end
|
||||
end
|
||||
|
||||
context 'with scheme that is neither http nor https' do
|
||||
let(:url) { 'ftp://google.com' }
|
||||
|
||||
it 'makes no request' do
|
||||
foo.hoge_remote_url = url
|
||||
expect(a_request(:get, url)).to_not have_been_made
|
||||
end
|
||||
end
|
||||
|
||||
context 'with relative URL' do
|
||||
let(:url) { 'https:///path' }
|
||||
|
||||
it 'makes no request' do
|
||||
foo.hoge_remote_url = url
|
||||
expect(a_request(:get, url)).to_not have_been_made
|
||||
end
|
||||
end
|
||||
|
||||
context 'when URL has not changed' do
|
||||
it 'makes no request if file is already saved' do
|
||||
allow(foo).to receive(:[]).with(attribute_name).and_return(url)
|
||||
allow(foo).to receive(:hoge_file_name).and_return('foo.jpg')
|
||||
|
||||
foo.hoge_remote_url = url
|
||||
expect(a_request(:get, url)).to_not have_been_made
|
||||
end
|
||||
|
||||
it 'makes request if file is not already saved' do
|
||||
allow(foo).to receive(:[]).with(attribute_name).and_return(url)
|
||||
allow(foo).to receive(:hoge_file_name).and_return(nil)
|
||||
|
||||
foo.hoge_remote_url = url
|
||||
expect(a_request(:get, url)).to have_been_made
|
||||
end
|
||||
end
|
||||
|
||||
context 'when instance has no attribute for URL' do
|
||||
before do
|
||||
allow(foo).to receive(:has_attribute?).with(attribute_name).and_return(false)
|
||||
end
|
||||
|
||||
it 'does not try to write attribute' do
|
||||
expect(foo).to_not receive('[]=').with(attribute_name, url)
|
||||
foo.hoge_remote_url = url
|
||||
end
|
||||
end
|
||||
|
||||
context 'when instance has an attribute for URL' do
|
||||
before do
|
||||
allow(foo).to receive(:has_attribute?).with(attribute_name).and_return(true)
|
||||
end
|
||||
|
||||
it 'does not try to write attribute' do
|
||||
expect(foo).to receive('[]=').with(attribute_name, url)
|
||||
foo.hoge_remote_url = url
|
||||
end
|
||||
end
|
||||
|
||||
context 'with a valid URL' do
|
||||
it 'makes a request' do
|
||||
foo.hoge_remote_url = url
|
||||
expect(a_request(:get, url)).to have_been_made
|
||||
end
|
||||
|
||||
context 'when the response is not successful' do
|
||||
let(:code) { 500 }
|
||||
|
||||
it 'does not assign file' do
|
||||
expect(foo).not_to receive(:public_send).with("#{hoge}=", any_args)
|
||||
expect(foo).not_to receive(:public_send).with("#{hoge}_file_name=", any_args)
|
||||
|
||||
foo.hoge_remote_url = url
|
||||
end
|
||||
end
|
||||
|
||||
context 'Addressable::URI::InvalidURIError raised' do
|
||||
it 'makes no request' do
|
||||
allow(Addressable::URI).to receive_message_chain(:parse, :normalize)
|
||||
.with(url).with(no_args).and_raise(Addressable::URI::InvalidURIError)
|
||||
context 'when the response is successful' do
|
||||
let(:code) { 200 }
|
||||
|
||||
foo.hoge_remote_url = url
|
||||
expect(request).not_to have_been_requested
|
||||
context 'and contains Content-Disposition header' do
|
||||
let(:file) { 'filename="foo.txt"' }
|
||||
let(:headers) { { 'content-disposition' => file } }
|
||||
|
||||
it 'assigns file' do
|
||||
response_with_limit = ResponseWithLimit.new(nil, 0)
|
||||
|
||||
allow(ResponseWithLimit).to receive(:new).with(anything, anything).and_return(response_with_limit)
|
||||
|
||||
expect(foo).to receive(:public_send).with("download_#{hoge}!", url)
|
||||
|
||||
foo.hoge_remote_url = url
|
||||
|
||||
expect(foo).to receive(:public_send).with("#{hoge}=", response_with_limit)
|
||||
|
||||
foo.download_hoge!(url)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'scheme is neither http nor https' do
|
||||
let(:url) { 'ftp://google.com' }
|
||||
|
||||
it 'makes no request' do
|
||||
foo.hoge_remote_url = url
|
||||
expect(request).not_to have_been_requested
|
||||
end
|
||||
end
|
||||
|
||||
context 'parsed_url.host is empty' do
|
||||
it 'makes no request' do
|
||||
parsed_url = double(scheme: 'https', host: double(blank?: true))
|
||||
allow(Addressable::URI).to receive_message_chain(:parse, :normalize)
|
||||
.with(url).with(no_args).and_return(parsed_url)
|
||||
|
||||
foo.hoge_remote_url = url
|
||||
expect(request).not_to have_been_requested
|
||||
end
|
||||
end
|
||||
|
||||
context 'parsed_url.host is nil' do
|
||||
it 'makes no request' do
|
||||
parsed_url = Addressable::URI.parse('https:https://example.com/path/file.png')
|
||||
allow(Addressable::URI).to receive_message_chain(:parse, :normalize)
|
||||
.with(url).with(no_args).and_return(parsed_url)
|
||||
|
||||
foo.hoge_remote_url = url
|
||||
expect(request).not_to have_been_requested
|
||||
end
|
||||
end
|
||||
|
||||
context 'foo[attribute_name] == url' do
|
||||
it 'makes no request if file is saved' do
|
||||
allow(foo).to receive(:[]).with(attribute_name).and_return(url)
|
||||
allow(foo).to receive(:hoge_file_name).and_return('foo.jpg')
|
||||
|
||||
foo.hoge_remote_url = url
|
||||
expect(request).not_to have_been_requested
|
||||
context 'when an error is raised during the request' do
|
||||
before do
|
||||
stub_request(:get, url).to_raise(error_class)
|
||||
end
|
||||
|
||||
it 'makes request if file is not saved' do
|
||||
allow(foo).to receive(:[]).with(attribute_name).and_return(url)
|
||||
allow(foo).to receive(:hoge_file_name).and_return(nil)
|
||||
error_classes = [
|
||||
HTTP::TimeoutError,
|
||||
HTTP::ConnectionError,
|
||||
OpenSSL::SSL::SSLError,
|
||||
Paperclip::Errors::NotIdentifiedByImageMagickError,
|
||||
Addressable::URI::InvalidURIError,
|
||||
]
|
||||
|
||||
foo.hoge_remote_url = url
|
||||
expect(request).to have_been_requested
|
||||
end
|
||||
end
|
||||
error_classes.each do |error_class|
|
||||
let(:error_class) { error_class }
|
||||
|
||||
context "scheme is https, parsed_url.host isn't empty, and foo[attribute_name] != url" do
|
||||
it 'makes a request' do
|
||||
foo.hoge_remote_url = url
|
||||
expect(request).to have_been_requested
|
||||
end
|
||||
|
||||
context 'response.code != 200' do
|
||||
let(:code) { 500 }
|
||||
|
||||
it 'calls not send' do
|
||||
expect(foo).not_to receive(:send).with("#{hoge}=", any_args)
|
||||
expect(foo).not_to receive(:send).with("#{hoge}_file_name=", any_args)
|
||||
it 'calls Rails.logger.debug' do
|
||||
expect(Rails.logger).to receive(:debug).with(/^Error fetching remote #{hoge}: /)
|
||||
foo.hoge_remote_url = url
|
||||
end
|
||||
end
|
||||
|
||||
context 'response.code == 200' do
|
||||
let(:code) { 200 }
|
||||
|
||||
context 'response contains headers["content-disposition"]' do
|
||||
let(:file) { 'filename="foo.txt"' }
|
||||
let(:headers) { { 'content-disposition' => file } }
|
||||
|
||||
it 'calls send' do
|
||||
string_io = StringIO.new('')
|
||||
extname = '.txt'
|
||||
basename = '0123456789abcdef'
|
||||
|
||||
allow(SecureRandom).to receive(:hex).and_return(basename)
|
||||
allow(StringIO).to receive(:new).with(anything).and_return(string_io)
|
||||
|
||||
expect(foo).to receive(:send).with("#{hoge}=", string_io)
|
||||
expect(foo).to receive(:send).with("#{hoge}_file_name=", basename + extname)
|
||||
foo.hoge_remote_url = url
|
||||
end
|
||||
end
|
||||
|
||||
context 'if has_attribute?' do
|
||||
it 'calls foo[attribute_name] = url' do
|
||||
allow(foo).to receive(:has_attribute?).with(attribute_name).and_return(true)
|
||||
expect(foo).to receive('[]=').with(attribute_name, url)
|
||||
foo.hoge_remote_url = url
|
||||
end
|
||||
end
|
||||
|
||||
context 'unless has_attribute?' do
|
||||
it 'calls not foo[attribute_name] = url' do
|
||||
allow(foo).to receive(:has_attribute?)
|
||||
.with(attribute_name).and_return(false)
|
||||
expect(foo).not_to receive('[]=').with(attribute_name, url)
|
||||
foo.hoge_remote_url = url
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'an error raised during the request' do
|
||||
let(:request) { stub_request(:get, url).to_raise(error_class) }
|
||||
|
||||
error_classes = [
|
||||
HTTP::TimeoutError,
|
||||
HTTP::ConnectionError,
|
||||
OpenSSL::SSL::SSLError,
|
||||
Paperclip::Errors::NotIdentifiedByImageMagickError,
|
||||
Addressable::URI::InvalidURIError,
|
||||
]
|
||||
|
||||
error_classes.each do |error_class|
|
||||
let(:error_class) { error_class }
|
||||
|
||||
it 'calls Rails.logger.debug' do
|
||||
expect(Rails.logger).to receive(:debug).with(/^Error fetching remote #{hoge}: /)
|
||||
foo.hoge_remote_url = url
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#reset_hoge!' do
|
||||
context 'if url.blank?' do
|
||||
it 'returns nil, without clearing foo[attribute_name] and calling #hoge_remote_url=' do
|
||||
url = nil
|
||||
expect(foo).not_to receive(:send).with(:hoge_remote_url=, url)
|
||||
foo[attribute_name] = url
|
||||
expect(foo.reset_hoge!).to be_nil
|
||||
expect(foo[attribute_name]).to be_nil
|
||||
end
|
||||
end
|
||||
|
||||
context 'unless url.blank?' do
|
||||
it 'clears foo[attribute_name] and calls #hoge_remote_url=' do
|
||||
foo[attribute_name] = url
|
||||
expect(foo).to receive(:send).with(:hoge_remote_url=, url)
|
||||
foo.reset_hoge!
|
||||
expect(foo[attribute_name]).to be ''
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
5
spec/models/device_spec.rb
Normal file
5
spec/models/device_spec.rb
Normal file
@ -0,0 +1,5 @@
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe Device, type: :model do
|
||||
|
||||
end
|
||||
5
spec/models/encrypted_message_spec.rb
Normal file
5
spec/models/encrypted_message_spec.rb
Normal file
@ -0,0 +1,5 @@
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe EncryptedMessage, type: :model do
|
||||
|
||||
end
|
||||
5
spec/models/one_time_key_spec.rb
Normal file
5
spec/models/one_time_key_spec.rb
Normal file
@ -0,0 +1,5 @@
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe OneTimeKey, type: :model do
|
||||
|
||||
end
|
||||
5
spec/models/system_key_spec.rb
Normal file
5
spec/models/system_key_spec.rb
Normal file
@ -0,0 +1,5 @@
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe SystemKey, type: :model do
|
||||
|
||||
end
|
||||
23
spec/services/clear_domain_media_service_spec.rb
Normal file
23
spec/services/clear_domain_media_service_spec.rb
Normal file
@ -0,0 +1,23 @@
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe ClearDomainMediaService, type: :service do
|
||||
let!(:bad_account) { Fabricate(:account, username: 'badguy666', domain: 'evil.org') }
|
||||
let!(:bad_status1) { Fabricate(:status, account: bad_account, text: 'You suck') }
|
||||
let!(:bad_status2) { Fabricate(:status, account: bad_account, text: 'Hahaha') }
|
||||
let!(:bad_attachment) { Fabricate(:media_attachment, account: bad_account, status: bad_status2, file: attachment_fixture('attachment.jpg')) }
|
||||
|
||||
subject { ClearDomainMediaService.new }
|
||||
|
||||
describe 'for a silence with reject media' do
|
||||
before do
|
||||
subject.call(DomainBlock.create!(domain: 'evil.org', severity: :silence, reject_media: true))
|
||||
end
|
||||
|
||||
it 'leaves the domains status and attachements, but clears media' do
|
||||
expect { bad_status1.reload }.not_to raise_error
|
||||
expect { bad_status2.reload }.not_to raise_error
|
||||
expect { bad_attachment.reload }.not_to raise_error
|
||||
expect(bad_attachment.file.exists?).to be false
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -91,6 +91,14 @@ describe SearchService, type: :service do
|
||||
expect(Tag).not_to have_received(:search_for)
|
||||
expect(results).to eq empty_results
|
||||
end
|
||||
it 'does not include account when starts with # character' do
|
||||
query = '#tag'
|
||||
allow(AccountSearchService).to receive(:new)
|
||||
|
||||
results = subject.call(query, nil, 10)
|
||||
expect(AccountSearchService).to_not have_received(:new)
|
||||
expect(results).to eq empty_results
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@ -20,6 +20,7 @@ RSpec.describe SuspendAccountService, type: :service do
|
||||
let!(:passive_relationship) { Fabricate(:follow, target_account: account) }
|
||||
let!(:remote_alice) { Fabricate(:account, inbox_url: 'https://alice.com/inbox', protocol: :activitypub) }
|
||||
let!(:remote_bob) { Fabricate(:account, inbox_url: 'https://bob.com/inbox', protocol: :activitypub) }
|
||||
let!(:endorsment) { Fabricate(:account_pin, account: passive_relationship.account, target_account: account) }
|
||||
|
||||
it 'deletes associated records' do
|
||||
is_expected.to change {
|
||||
@ -30,8 +31,9 @@ RSpec.describe SuspendAccountService, type: :service do
|
||||
account.favourites,
|
||||
account.active_relationships,
|
||||
account.passive_relationships,
|
||||
AccountPin.where(target_account: account),
|
||||
].map(&:count)
|
||||
}.from([1, 1, 1, 1, 1, 1]).to([0, 0, 0, 0, 0, 0])
|
||||
}.from([1, 1, 1, 1, 1, 1, 1]).to([0, 0, 0, 0, 0, 0, 0])
|
||||
end
|
||||
|
||||
it 'sends a delete actor activity to all known inboxes' do
|
||||
|
||||
64
spec/services/unallow_domain_service_spec.rb
Normal file
64
spec/services/unallow_domain_service_spec.rb
Normal file
@ -0,0 +1,64 @@
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe UnallowDomainService, type: :service do
|
||||
let!(:bad_account) { Fabricate(:account, username: 'badguy666', domain: 'evil.org') }
|
||||
let!(:bad_status1) { Fabricate(:status, account: bad_account, text: 'You suck') }
|
||||
let!(:bad_status2) { Fabricate(:status, account: bad_account, text: 'Hahaha') }
|
||||
let!(:bad_attachment) { Fabricate(:media_attachment, account: bad_account, status: bad_status2, file: attachment_fixture('attachment.jpg')) }
|
||||
let!(:already_banned_account) { Fabricate(:account, username: 'badguy', domain: 'evil.org', suspended: true, silenced: true) }
|
||||
let!(:domain_allow) { Fabricate(:domain_allow, domain: 'evil.org') }
|
||||
|
||||
subject { UnallowDomainService.new }
|
||||
|
||||
context 'in limited federation mode' do
|
||||
before do
|
||||
allow(subject).to receive(:whitelist_mode?).and_return(true)
|
||||
end
|
||||
|
||||
describe '#call' do
|
||||
before do
|
||||
subject.call(domain_allow)
|
||||
end
|
||||
|
||||
it 'removes the allowed domain' do
|
||||
expect(DomainAllow.allowed?('evil.org')).to be false
|
||||
end
|
||||
|
||||
it 'removes remote accounts from that domain' do
|
||||
expect(Account.where(domain: 'evil.org').exists?).to be false
|
||||
end
|
||||
|
||||
it 'removes the remote accounts\'s statuses and media attachments' do
|
||||
expect { bad_status1.reload }.to raise_exception ActiveRecord::RecordNotFound
|
||||
expect { bad_status2.reload }.to raise_exception ActiveRecord::RecordNotFound
|
||||
expect { bad_attachment.reload }.to raise_exception ActiveRecord::RecordNotFound
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'without limited federation mode' do
|
||||
before do
|
||||
allow(subject).to receive(:whitelist_mode?).and_return(false)
|
||||
end
|
||||
|
||||
describe '#call' do
|
||||
before do
|
||||
subject.call(domain_allow)
|
||||
end
|
||||
|
||||
it 'removes the allowed domain' do
|
||||
expect(DomainAllow.allowed?('evil.org')).to be false
|
||||
end
|
||||
|
||||
it 'does not remove accounts from that domain' do
|
||||
expect(Account.where(domain: 'evil.org').exists?).to be true
|
||||
end
|
||||
|
||||
it 'removes the remote accounts\'s statuses and media attachments' do
|
||||
expect { bad_status1.reload }.to_not raise_exception ActiveRecord::RecordNotFound
|
||||
expect { bad_status2.reload }.to_not raise_exception ActiveRecord::RecordNotFound
|
||||
expect { bad_attachment.reload }.to_not raise_exception ActiveRecord::RecordNotFound
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
22
spec/workers/activitypub/move_distribution_worker_spec.rb
Normal file
22
spec/workers/activitypub/move_distribution_worker_spec.rb
Normal file
@ -0,0 +1,22 @@
|
||||
require 'rails_helper'
|
||||
|
||||
describe ActivityPub::MoveDistributionWorker do
|
||||
subject { described_class.new }
|
||||
|
||||
let(:migration) { Fabricate(:account_migration) }
|
||||
let(:follower) { Fabricate(:account, protocol: :activitypub, inbox_url: 'http://example.com') }
|
||||
let(:blocker) { Fabricate(:account, protocol: :activitypub, inbox_url: 'http://example2.com') }
|
||||
|
||||
describe '#perform' do
|
||||
before do
|
||||
allow(ActivityPub::DeliveryWorker).to receive(:push_bulk)
|
||||
follower.follow!(migration.account)
|
||||
blocker.block!(migration.account)
|
||||
end
|
||||
|
||||
it 'delivers to followers and known blockers' do
|
||||
subject.perform(migration.id)
|
||||
expect(ActivityPub::DeliveryWorker).to have_received(:push_bulk).with(['http://example.com', 'http://example2.com'])
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -8,7 +8,7 @@ describe DomainBlockWorker do
|
||||
describe 'perform' do
|
||||
let(:domain_block) { Fabricate(:domain_block) }
|
||||
|
||||
it 'returns true for non-existent domain block' do
|
||||
it 'calls domain block service for relevant domain block' do
|
||||
service = double(call: nil)
|
||||
allow(BlockDomainService).to receive(:new).and_return(service)
|
||||
result = subject.perform(domain_block.id)
|
||||
@ -17,7 +17,7 @@ describe DomainBlockWorker do
|
||||
expect(service).to have_received(:call).with(domain_block, false)
|
||||
end
|
||||
|
||||
it 'calls domain block service for relevant domain block' do
|
||||
it 'returns true for non-existent domain block' do
|
||||
result = subject.perform('aaa')
|
||||
|
||||
expect(result).to eq(true)
|
||||
|
||||
26
spec/workers/domain_clear_media_worker_spec.rb
Normal file
26
spec/workers/domain_clear_media_worker_spec.rb
Normal file
@ -0,0 +1,26 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
|
||||
describe DomainClearMediaWorker do
|
||||
subject { described_class.new }
|
||||
|
||||
describe 'perform' do
|
||||
let(:domain_block) { Fabricate(:domain_block, severity: :silence, reject_media: true) }
|
||||
|
||||
it 'calls domain clear media service for relevant domain block' do
|
||||
service = double(call: nil)
|
||||
allow(ClearDomainMediaService).to receive(:new).and_return(service)
|
||||
result = subject.perform(domain_block.id)
|
||||
|
||||
expect(result).to be_nil
|
||||
expect(service).to have_received(:call).with(domain_block)
|
||||
end
|
||||
|
||||
it 'returns true for non-existent domain block' do
|
||||
result = subject.perform('aaa')
|
||||
|
||||
expect(result).to eq(true)
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -4,22 +4,67 @@ require 'rails_helper'
|
||||
|
||||
describe MoveWorker do
|
||||
let(:local_follower) { Fabricate(:user, email: 'bob@example.com', account: Fabricate(:account, username: 'bob')).account }
|
||||
let(:blocking_account) { Fabricate(:user, email: 'bar@example.com', account: Fabricate(:account, username: 'bar')).account }
|
||||
let(:muting_account) { Fabricate(:user, email: 'foo@example.com', account: Fabricate(:account, username: 'foo')).account }
|
||||
let(:source_account) { Fabricate(:account, protocol: :activitypub, domain: 'example.com') }
|
||||
let(:target_account) { Fabricate(:account, protocol: :activitypub, domain: 'example.com') }
|
||||
let(:local_user) { Fabricate(:user) }
|
||||
let!(:account_note) { Fabricate(:account_note, account: local_user.account, target_account: source_account) }
|
||||
|
||||
let(:block_service) { double }
|
||||
|
||||
subject { described_class.new }
|
||||
|
||||
before do
|
||||
local_follower.follow!(source_account)
|
||||
blocking_account.block!(source_account)
|
||||
muting_account.mute!(source_account)
|
||||
|
||||
allow(UnfollowFollowWorker).to receive(:push_bulk)
|
||||
allow(BlockService).to receive(:new).and_return(block_service)
|
||||
allow(block_service).to receive(:call)
|
||||
end
|
||||
|
||||
shared_examples 'user note handling' do
|
||||
it 'copies user note' do
|
||||
subject.perform(source_account.id, target_account.id)
|
||||
expect(AccountNote.find_by(account: account_note.account, target_account: target_account).comment).to include(source_account.acct)
|
||||
expect(AccountNote.find_by(account: account_note.account, target_account: target_account).comment).to include(account_note.comment)
|
||||
end
|
||||
|
||||
it 'merges user notes when needed' do
|
||||
new_account_note = AccountNote.create!(account: account_note.account, target_account: target_account, comment: 'new note prior to move')
|
||||
|
||||
subject.perform(source_account.id, target_account.id)
|
||||
expect(AccountNote.find_by(account: account_note.account, target_account: target_account).comment).to include(source_account.acct)
|
||||
expect(AccountNote.find_by(account: account_note.account, target_account: target_account).comment).to include(account_note.comment)
|
||||
expect(AccountNote.find_by(account: account_note.account, target_account: target_account).comment).to include(new_account_note.comment)
|
||||
end
|
||||
end
|
||||
|
||||
shared_examples 'block and mute handling' do
|
||||
it 'makes blocks carry over and add a note' do
|
||||
subject.perform(source_account.id, target_account.id)
|
||||
expect(block_service).to have_received(:call).with(blocking_account, target_account)
|
||||
expect(AccountNote.find_by(account: blocking_account, target_account: target_account).comment).to include(source_account.acct)
|
||||
end
|
||||
|
||||
it 'makes mutes carry over and add a note' do
|
||||
subject.perform(source_account.id, target_account.id)
|
||||
expect(muting_account.muting?(target_account)).to be true
|
||||
expect(AccountNote.find_by(account: muting_account, target_account: target_account).comment).to include(source_account.acct)
|
||||
end
|
||||
end
|
||||
|
||||
context 'both accounts are distant' do
|
||||
describe 'perform' do
|
||||
it 'calls UnfollowFollowWorker' do
|
||||
allow(UnfollowFollowWorker).to receive(:push_bulk)
|
||||
subject.perform(source_account.id, target_account.id)
|
||||
expect(UnfollowFollowWorker).to have_received(:push_bulk).with([local_follower.id])
|
||||
end
|
||||
|
||||
include_examples 'user note handling'
|
||||
include_examples 'block and mute handling'
|
||||
end
|
||||
end
|
||||
|
||||
@ -28,10 +73,12 @@ describe MoveWorker do
|
||||
|
||||
describe 'perform' do
|
||||
it 'calls UnfollowFollowWorker' do
|
||||
allow(UnfollowFollowWorker).to receive(:push_bulk)
|
||||
subject.perform(source_account.id, target_account.id)
|
||||
expect(UnfollowFollowWorker).to have_received(:push_bulk).with([local_follower.id])
|
||||
end
|
||||
|
||||
include_examples 'user note handling'
|
||||
include_examples 'block and mute handling'
|
||||
end
|
||||
end
|
||||
|
||||
@ -45,6 +92,9 @@ describe MoveWorker do
|
||||
expect(local_follower.following?(target_account)).to be true
|
||||
end
|
||||
|
||||
include_examples 'user note handling'
|
||||
include_examples 'block and mute handling'
|
||||
|
||||
it 'does not fail when a local user is already following both accounts' do
|
||||
double_follower = Fabricate(:user, email: 'eve@example.com', account: Fabricate(:account, username: 'eve')).account
|
||||
double_follower.follow!(source_account)
|
||||
|
||||
Reference in New Issue
Block a user