Reduce factory usage across spec/services area (#32098)

This commit is contained in:
Matt Jankowski 2024-10-04 10:11:15 -04:00 committed by Claire
parent 77ff94d3d2
commit fc5b558b32
21 changed files with 567 additions and 641 deletions

View file

@ -27,39 +27,35 @@ RSpec.describe AccountStatusesCleanupService do
end end
context 'when given a normal budget of 10' do context 'when given a normal budget of 10' do
it 'reports 3 deleted statuses' do it 'reports 3 deleted statuses and records last deleted id, deletes statuses, preserves recent unrelated statuses' do
expect(subject.call(account_policy, 10)).to eq 3 expect(subject.call(account_policy, 10))
end .to eq(3)
it 'records the last deleted id' do expect(account_policy.last_inspected)
subject.call(account_policy, 10) .to eq [old_status.id, another_old_status.id].max
expect(account_policy.last_inspected).to eq [old_status.id, another_old_status.id].max
end
it 'actually deletes the statuses' do expect(Status.find_by(id: [very_old_status.id, old_status.id, another_old_status.id]))
subject.call(account_policy, 10) .to be_nil
expect(Status.find_by(id: [very_old_status.id, old_status.id, another_old_status.id])).to be_nil expect { recent_status.reload }
expect { recent_status.reload }.to_not raise_error .to_not raise_error
end expect { unrelated_status.reload }
.to_not raise_error
it 'preserves recent and unrelated statuses' do expect { recent_status.reload }
subject.call(account_policy, 10) .to_not raise_error
expect { unrelated_status.reload }.to_not raise_error
expect { recent_status.reload }.to_not raise_error
end end
end end
context 'when called repeatedly with a budget of 2' do context 'when called repeatedly with a budget of 2' do
it 'reports 2 then 1 deleted statuses' do it 'reports 2 then 1 deleted statuses and deletes in expected order' do
expect(subject.call(account_policy, 2)).to eq 2 expect(subject.call(account_policy, 2))
expect(subject.call(account_policy, 2)).to eq 1 .to eq(2)
end expect(Status.find_by(id: very_old_status.id))
.to be_nil
it 'actually deletes the statuses in the expected order' do expect(subject.call(account_policy, 2))
subject.call(account_policy, 2) .to eq(1)
expect(Status.find_by(id: very_old_status.id)).to be_nil expect(Status.find_by(id: [very_old_status.id, old_status.id, another_old_status.id]))
subject.call(account_policy, 2) .to be_nil
expect(Status.find_by(id: [very_old_status.id, old_status.id, another_old_status.id])).to be_nil
end end
end end
@ -90,19 +86,24 @@ RSpec.describe AccountStatusesCleanupService do
end end
end end
it 'reports 0 deleted statuses then 0 then 3 then 0 again' do it 'reports 0 deleted statuses then 0 then 3 then 0 again, and keeps id under oldest deletable record' do
expect(subject.call(account_policy, 10)).to eq 0 expect(subject.call(account_policy, 10))
expect(subject.call(account_policy, 10)).to eq 0 .to eq(0)
expect(subject.call(account_policy, 10)).to eq 3 expect(subject.call(account_policy, 10))
expect(subject.call(account_policy, 10)).to eq 0 .to eq(0)
expect(subject.call(account_policy, 10))
.to eq(3)
expect(subject.call(account_policy, 10))
.to eq(0)
expect(account_policy.last_inspected)
.to be < oldest_deletable_record_id
end end
it 'never causes the recorded id to get higher than oldest deletable toot' do def oldest_deletable_record_id
subject.call(account_policy, 10) Mastodon::Snowflake.id_at(
subject.call(account_policy, 10) account_policy.min_status_age.seconds.ago,
subject.call(account_policy, 10) with_random: false
subject.call(account_policy, 10) )
expect(account_policy.last_inspected).to be < Mastodon::Snowflake.id_at(account_policy.min_status_age.seconds.ago, with_random: false)
end end
end end
end end

View file

@ -2,10 +2,6 @@
require 'rails_helper' require 'rails_helper'
def poll_option_json(name, votes)
{ type: 'Note', name: name, replies: { type: 'Collection', totalItems: votes } }
end
RSpec.describe ActivityPub::ProcessStatusUpdateService do RSpec.describe ActivityPub::ProcessStatusUpdateService do
subject { described_class.new } subject { described_class.new }
@ -294,7 +290,6 @@ RSpec.describe ActivityPub::ProcessStatusUpdateService do
context 'when originally without media attachments' do context 'when originally without media attachments' do
before do before do
stub_request(:get, 'https://example.com/foo.png').to_return(body: attachment_fixture('emojo.png')) stub_request(:get, 'https://example.com/foo.png').to_return(body: attachment_fixture('emojo.png'))
subject.call(status, json, json)
end end
let(:payload) do let(:payload) do
@ -310,19 +305,18 @@ RSpec.describe ActivityPub::ProcessStatusUpdateService do
} }
end end
it 'updates media attachments' do it 'updates media attachments, fetches attachment, records media change in edit' do
media_attachment = status.reload.ordered_media_attachments.first subject.call(status, json, json)
expect(media_attachment).to_not be_nil expect(status.reload.ordered_media_attachments.first)
expect(media_attachment.remote_url).to eq 'https://example.com/foo.png' .to be_present
end .and(have_attributes(remote_url: 'https://example.com/foo.png'))
it 'fetches the attachment' do expect(a_request(:get, 'https://example.com/foo.png'))
expect(a_request(:get, 'https://example.com/foo.png')).to have_been_made .to have_been_made
end
it 'records media change in edit' do expect(status.edits.reload.last.ordered_media_attachment_ids)
expect(status.edits.reload.last.ordered_media_attachment_ids).to_not be_empty .to_not be_empty
end end
end end
@ -344,27 +338,26 @@ RSpec.describe ActivityPub::ProcessStatusUpdateService do
before do before do
allow(RedownloadMediaWorker).to receive(:perform_async) allow(RedownloadMediaWorker).to receive(:perform_async)
end
it 'updates the existing media attachment in-place, does not queue redownload, updates media, records media change' do
subject.call(status, json, json) subject.call(status, json, json)
end
it 'updates the existing media attachment in-place' do expect(status.media_attachments.ordered.reload.first)
media_attachment = status.media_attachments.ordered.reload.first .to be_present
.and have_attributes(
remote_url: 'https://example.com/foo.png',
description: 'A picture'
)
expect(media_attachment).to_not be_nil expect(RedownloadMediaWorker)
expect(media_attachment.remote_url).to eq 'https://example.com/foo.png' .to_not have_received(:perform_async)
expect(media_attachment.description).to eq 'A picture'
end
it 'does not queue redownload for the existing media attachment' do expect(status.ordered_media_attachments.map(&:remote_url))
expect(RedownloadMediaWorker).to_not have_received(:perform_async) .to eq %w(https://example.com/foo.png)
end
it 'updates media attachments' do expect(status.edits.reload.last.ordered_media_attachment_ids)
expect(status.ordered_media_attachments.map(&:remote_url)).to eq %w(https://example.com/foo.png) .to_not be_empty
end
it 'records media change in edit' do
expect(status.edits.reload.last.ordered_media_attachment_ids).to_not be_empty
end end
end end
@ -372,10 +365,11 @@ RSpec.describe ActivityPub::ProcessStatusUpdateService do
before do before do
poll = Fabricate(:poll, status: status) poll = Fabricate(:poll, status: status)
status.update(preloadable_poll: poll) status.update(preloadable_poll: poll)
subject.call(status, json, json)
end end
it 'removes poll and records media change in edit' do it 'removes poll and records media change in edit' do
subject.call(status, json, json)
expect(status.reload.poll).to be_nil expect(status.reload.poll).to be_nil
expect(status.edits.reload.last.poll_options).to be_nil expect(status.edits.reload.last.poll_options).to be_nil
end end
@ -398,15 +392,13 @@ RSpec.describe ActivityPub::ProcessStatusUpdateService do
} }
end end
before do
subject.call(status, json, json)
end
it 'creates a poll and records media change in edit' do it 'creates a poll and records media change in edit' do
poll = status.reload.poll subject.call(status, json, json)
expect(status.reload.poll)
.to be_present
.and have_attributes(options: %w(Foo Bar Baz))
expect(poll).to_not be_nil
expect(poll.options).to eq %w(Foo Bar Baz)
expect(status.edits.reload.last.poll_options).to eq %w(Foo Bar Baz) expect(status.edits.reload.last.poll_options).to eq %w(Foo Bar Baz)
end end
end end
@ -419,4 +411,8 @@ RSpec.describe ActivityPub::ProcessStatusUpdateService do
.to eq '2021-09-08 22:39:25 UTC' .to eq '2021-09-08 22:39:25 UTC'
end end
end end
def poll_option_json(name, votes)
{ type: 'Note', name: name, replies: { type: 'Collection', totalItems: votes } }
end
end end

View file

@ -12,15 +12,15 @@ RSpec.describe AuthorizeFollowService do
before do before do
FollowRequest.create(account: bob, target_account: sender) FollowRequest.create(account: bob, target_account: sender)
end
it 'removes follow request and creates follow relation' do
subject.call(bob, sender) subject.call(bob, sender)
end
it 'removes follow request' do expect(bob)
expect(bob.requested?(sender)).to be false .to_not be_requested(sender)
end expect(bob)
.to be_following(sender)
it 'creates follow relation' do
expect(bob.following?(sender)).to be true
end end
end end
@ -30,19 +30,17 @@ RSpec.describe AuthorizeFollowService do
before do before do
FollowRequest.create(account: bob, target_account: sender) FollowRequest.create(account: bob, target_account: sender)
stub_request(:post, bob.inbox_url).to_return(status: 200) stub_request(:post, bob.inbox_url).to_return(status: 200)
end
it 'removes follow request, creates follow relation, send accept activity', :inline_jobs do
subject.call(bob, sender) subject.call(bob, sender)
end
it 'removes follow request' do expect(bob)
expect(bob.requested?(sender)).to be false .to_not be_requested(sender)
end expect(bob)
.to be_following(sender)
it 'creates follow relation' do expect(a_request(:post, bob.inbox_url))
expect(bob.following?(sender)).to be true .to have_been_made.once
end
it 'sends an accept activity', :inline_jobs do
expect(a_request(:post, bob.inbox_url)).to have_been_made.once
end end
end end
end end

View file

@ -24,32 +24,38 @@ RSpec.describe BatchedRemoveStatusService, :inline_jobs do
status_alice_hello status_alice_hello
status_alice_other status_alice_other
end
it 'removes status records, removes from author and local follower feeds, notifies stream, sends delete' do
subject.call([status_alice_hello, status_alice_other]) subject.call([status_alice_hello, status_alice_other])
expect { Status.find(status_alice_hello.id) }
.to raise_error ActiveRecord::RecordNotFound
expect { Status.find(status_alice_other.id) }
.to raise_error ActiveRecord::RecordNotFound
expect(feed_ids_for(alice))
.to_not include(status_alice_hello.id, status_alice_other.id)
expect(feed_ids_for(jeff))
.to_not include(status_alice_hello.id, status_alice_other.id)
expect(redis)
.to have_received(:publish)
.with("timeline:#{jeff.id}", any_args).at_least(:once)
expect(redis)
.to have_received(:publish)
.with('timeline:public', any_args).at_least(:once)
expect(a_request(:post, 'http://example.com/inbox'))
.to have_been_made.at_least_once
end end
it 'removes statuses' do def feed_ids_for(account)
expect { Status.find(status_alice_hello.id) }.to raise_error ActiveRecord::RecordNotFound HomeFeed
expect { Status.find(status_alice_other.id) }.to raise_error ActiveRecord::RecordNotFound .new(account)
end .get(10)
.pluck(:id)
it 'removes statuses from author\'s home feed' do
expect(HomeFeed.new(alice).get(10).pluck(:id)).to_not include(status_alice_hello.id, status_alice_other.id)
end
it 'removes statuses from local follower\'s home feed' do
expect(HomeFeed.new(jeff).get(10).pluck(:id)).to_not include(status_alice_hello.id, status_alice_other.id)
end
it 'notifies streaming API of followers' do
expect(redis).to have_received(:publish).with("timeline:#{jeff.id}", any_args).at_least(:once)
end
it 'notifies streaming API of public timeline' do
expect(redis).to have_received(:publish).with('timeline:public', any_args).at_least(:once)
end
it 'sends delete activity to followers' do
expect(a_request(:post, 'http://example.com/inbox')).to have_been_made.at_least_once
end end
end end

View file

@ -26,15 +26,16 @@ RSpec.describe BlockService do
before do before do
stub_request(:post, 'http://example.com/inbox').to_return(status: 200) stub_request(:post, 'http://example.com/inbox').to_return(status: 200)
end
it 'creates a blocking relation and send block activity', :inline_jobs do
subject.call(sender, bob) subject.call(sender, bob)
end
it 'creates a blocking relation' do expect(sender)
expect(sender.blocking?(bob)).to be true .to be_blocking(bob)
end
it 'sends a block activity', :inline_jobs do expect(a_request(:post, 'http://example.com/inbox'))
expect(a_request(:post, 'http://example.com/inbox')).to have_been_made.once .to have_been_made.once
end end
end end
end end

View file

@ -24,30 +24,19 @@ RSpec.describe BulkImportService do
].map { |data| import.rows.create!(data: data) } ].map { |data| import.rows.create!(data: data) }
end end
before do before { account.follow!(Fabricate(:account)) }
account.follow!(Fabricate(:account))
end
it 'does not immediately change who the account follows' do it 'does not immediately change who the account follows, enqueues workers, sends follow requests after worker run' do
expect { subject.call(import) }.to_not(change { account.reload.active_relationships.to_a }) expect { subject.call(import) }
end .to_not(change { account.reload.active_relationships.to_a })
it 'enqueues workers for the expected rows' do expect(row_worker_job_args)
subject.call(import) .to match_array(rows.map(&:id))
expect(Import::RowWorker.jobs.pluck('args').flatten).to match_array(rows.map(&:id))
end
it 'requests to follow all the listed users once the workers have run' do stub_resolve_account_and_drain_workers
subject.call(import)
resolve_account_service_double = instance_double(ResolveAccountService) expect(FollowRequest.includes(:target_account).where(account: account).map { |follow_request| follow_request.target_account.acct })
allow(ResolveAccountService).to receive(:new).and_return(resolve_account_service_double) .to contain_exactly('user@foo.bar', 'unknown@unknown.bar')
allow(resolve_account_service_double).to receive(:call).with('user@foo.bar', any_args) { Fabricate(:account, username: 'user', domain: 'foo.bar', protocol: :activitypub) }
allow(resolve_account_service_double).to receive(:call).with('unknown@unknown.bar', any_args) { Fabricate(:account, username: 'unknown', domain: 'unknown.bar', protocol: :activitypub) }
Import::RowWorker.drain
expect(FollowRequest.includes(:target_account).where(account: account).map { |follow_request| follow_request.target_account.acct }).to contain_exactly('user@foo.bar', 'unknown@unknown.bar')
end end
end end
@ -71,31 +60,20 @@ RSpec.describe BulkImportService do
account.follow!(to_be_unfollowed) account.follow!(to_be_unfollowed)
end end
it 'unfollows user not present on list' do it 'updates the existing follow relationship as expected and unfollows user not on list, enqueues workers, sends follow reqs after worker run' do
subject.call(import) expect { subject.call(import) }
expect(account.following?(to_be_unfollowed)).to be false .to change { Follow.where(account: account, target_account: followed).pick(:show_reblogs, :notify, :languages) }.from([true, false, nil]).to([false, true, ['en']])
end
it 'updates the existing follow relationship as expected' do expect(account)
expect { subject.call(import) }.to change { Follow.where(account: account, target_account: followed).pick(:show_reblogs, :notify, :languages) }.from([true, false, nil]).to([false, true, ['en']]) .to_not be_following(to_be_unfollowed)
end
it 'enqueues workers for the expected rows' do expect(row_worker_job_args)
subject.call(import) .to match_array(rows[1..].map(&:id))
expect(Import::RowWorker.jobs.pluck('args').flatten).to match_array(rows[1..].map(&:id))
end
it 'requests to follow all the expected users once the workers have run' do stub_resolve_account_and_drain_workers
subject.call(import)
resolve_account_service_double = instance_double(ResolveAccountService) expect(FollowRequest.includes(:target_account).where(account: account).map { |follow_request| follow_request.target_account.acct })
allow(ResolveAccountService).to receive(:new).and_return(resolve_account_service_double) .to contain_exactly('user@foo.bar', 'unknown@unknown.bar')
allow(resolve_account_service_double).to receive(:call).with('user@foo.bar', any_args) { Fabricate(:account, username: 'user', domain: 'foo.bar', protocol: :activitypub) }
allow(resolve_account_service_double).to receive(:call).with('unknown@unknown.bar', any_args) { Fabricate(:account, username: 'unknown', domain: 'unknown.bar', protocol: :activitypub) }
Import::RowWorker.drain
expect(FollowRequest.includes(:target_account).where(account: account).map { |follow_request| follow_request.target_account.acct }).to contain_exactly('user@foo.bar', 'unknown@unknown.bar')
end end
end end
@ -110,30 +88,19 @@ RSpec.describe BulkImportService do
].map { |data| import.rows.create!(data: data) } ].map { |data| import.rows.create!(data: data) }
end end
before do before { account.block!(Fabricate(:account, username: 'already_blocked', domain: 'remote.org')) }
account.block!(Fabricate(:account, username: 'already_blocked', domain: 'remote.org'))
end
it 'does not immediately change who the account blocks' do it 'does not immediately change who the account blocks, enqueues worker, blocks after run' do
expect { subject.call(import) }.to_not(change { account.reload.blocking.to_a }) expect { subject.call(import) }
end .to_not(change { account.reload.blocking.to_a })
it 'enqueues workers for the expected rows' do expect(row_worker_job_args)
subject.call(import) .to match_array(rows.map(&:id))
expect(Import::RowWorker.jobs.pluck('args').flatten).to match_array(rows.map(&:id))
end
it 'blocks all the listed users once the workers have run' do stub_resolve_account_and_drain_workers
subject.call(import)
resolve_account_service_double = instance_double(ResolveAccountService) expect(account.reload.blocking.map(&:acct))
allow(ResolveAccountService).to receive(:new).and_return(resolve_account_service_double) .to contain_exactly('already_blocked@remote.org', 'user@foo.bar', 'unknown@unknown.bar')
allow(resolve_account_service_double).to receive(:call).with('user@foo.bar', any_args) { Fabricate(:account, username: 'user', domain: 'foo.bar', protocol: :activitypub) }
allow(resolve_account_service_double).to receive(:call).with('unknown@unknown.bar', any_args) { Fabricate(:account, username: 'unknown', domain: 'unknown.bar', protocol: :activitypub) }
Import::RowWorker.drain
expect(account.blocking.map(&:acct)).to contain_exactly('already_blocked@remote.org', 'user@foo.bar', 'unknown@unknown.bar')
end end
end end
@ -157,27 +124,18 @@ RSpec.describe BulkImportService do
account.block!(to_be_unblocked) account.block!(to_be_unblocked)
end end
it 'unblocks user not present on list' do it 'unblocks user not present on list, enqueues worker, requests follow after run' do
subject.call(import) subject.call(import)
expect(account.blocking?(to_be_unblocked)).to be false expect(account.blocking?(to_be_unblocked)).to be false
end
it 'enqueues workers for the expected rows' do expect(row_worker_job_args)
subject.call(import) .to match_array(rows[1..].map(&:id))
expect(Import::RowWorker.jobs.pluck('args').flatten).to match_array(rows[1..].map(&:id))
end
it 'requests to follow all the expected users once the workers have run' do stub_resolve_account_and_drain_workers
subject.call(import)
resolve_account_service_double = instance_double(ResolveAccountService) expect(account.blocking.map(&:acct))
allow(ResolveAccountService).to receive(:new).and_return(resolve_account_service_double) .to contain_exactly('blocked@foo.bar', 'user@foo.bar', 'unknown@unknown.bar')
allow(resolve_account_service_double).to receive(:call).with('user@foo.bar', any_args) { Fabricate(:account, username: 'user', domain: 'foo.bar', protocol: :activitypub) }
allow(resolve_account_service_double).to receive(:call).with('unknown@unknown.bar', any_args) { Fabricate(:account, username: 'unknown', domain: 'unknown.bar', protocol: :activitypub) }
Import::RowWorker.drain
expect(account.blocking.map(&:acct)).to contain_exactly('blocked@foo.bar', 'user@foo.bar', 'unknown@unknown.bar')
end end
end end
@ -192,30 +150,19 @@ RSpec.describe BulkImportService do
].map { |data| import.rows.create!(data: data) } ].map { |data| import.rows.create!(data: data) }
end end
before do before { account.mute!(Fabricate(:account, username: 'already_muted', domain: 'remote.org')) }
account.mute!(Fabricate(:account, username: 'already_muted', domain: 'remote.org'))
end
it 'does not immediately change who the account blocks' do it 'does not immediately change who the account blocks, enqueures worker, mutes users after worker run' do
expect { subject.call(import) }.to_not(change { account.reload.muting.to_a }) expect { subject.call(import) }
end .to_not(change { account.reload.muting.to_a })
it 'enqueues workers for the expected rows' do expect(row_worker_job_args)
subject.call(import) .to match_array(rows.map(&:id))
expect(Import::RowWorker.jobs.pluck('args').flatten).to match_array(rows.map(&:id))
end
it 'mutes all the listed users once the workers have run' do stub_resolve_account_and_drain_workers
subject.call(import)
resolve_account_service_double = instance_double(ResolveAccountService) expect(account.reload.muting.map(&:acct))
allow(ResolveAccountService).to receive(:new).and_return(resolve_account_service_double) .to contain_exactly('already_muted@remote.org', 'user@foo.bar', 'unknown@unknown.bar')
allow(resolve_account_service_double).to receive(:call).with('user@foo.bar', any_args) { Fabricate(:account, username: 'user', domain: 'foo.bar', protocol: :activitypub) }
allow(resolve_account_service_double).to receive(:call).with('unknown@unknown.bar', any_args) { Fabricate(:account, username: 'unknown', domain: 'unknown.bar', protocol: :activitypub) }
Import::RowWorker.drain
expect(account.muting.map(&:acct)).to contain_exactly('already_muted@remote.org', 'user@foo.bar', 'unknown@unknown.bar')
end end
end end
@ -239,31 +186,19 @@ RSpec.describe BulkImportService do
account.mute!(to_be_unmuted) account.mute!(to_be_unmuted)
end end
it 'updates the existing mute as expected' do it 'updates the existing mute as expected and unblocks user not on list, and enqueues worker, and requests follow after worker run' do
expect { subject.call(import) }.to change { Mute.where(account: account, target_account: muted).pick(:hide_notifications) }.from(false).to(true) expect { subject.call(import) }
end .to change { Mute.where(account: account, target_account: muted).pick(:hide_notifications) }.from(false).to(true)
it 'unblocks user not present on list' do
subject.call(import)
expect(account.muting?(to_be_unmuted)).to be false expect(account.muting?(to_be_unmuted)).to be false
end
it 'enqueues workers for the expected rows' do expect(row_worker_job_args)
subject.call(import) .to match_array(rows[1..].map(&:id))
expect(Import::RowWorker.jobs.pluck('args').flatten).to match_array(rows[1..].map(&:id))
end
it 'requests to follow all the expected users once the workers have run' do stub_resolve_account_and_drain_workers
subject.call(import)
resolve_account_service_double = instance_double(ResolveAccountService) expect(account.muting.map(&:acct))
allow(ResolveAccountService).to receive(:new).and_return(resolve_account_service_double) .to contain_exactly('muted@foo.bar', 'user@foo.bar', 'unknown@unknown.bar')
allow(resolve_account_service_double).to receive(:call).with('user@foo.bar', any_args) { Fabricate(:account, username: 'user', domain: 'foo.bar', protocol: :activitypub) }
allow(resolve_account_service_double).to receive(:call).with('unknown@unknown.bar', any_args) { Fabricate(:account, username: 'unknown', domain: 'unknown.bar', protocol: :activitypub) }
Import::RowWorker.drain
expect(account.muting.map(&:acct)).to contain_exactly('muted@foo.bar', 'user@foo.bar', 'unknown@unknown.bar')
end end
end end
@ -284,13 +219,11 @@ RSpec.describe BulkImportService do
account.block_domain!('blocked.com') account.block_domain!('blocked.com')
end end
it 'blocks all the new domains' do it 'blocks all the new domains and marks import finished' do
subject.call(import) subject.call(import)
expect(account.domain_blocks.pluck(:domain)).to contain_exactly('alreadyblocked.com', 'blocked.com', 'to-block.com')
end
it 'marks the import as finished' do expect(account.domain_blocks.pluck(:domain))
subject.call(import) .to contain_exactly('alreadyblocked.com', 'blocked.com', 'to-block.com')
expect(import.reload.state_finished?).to be true expect(import.reload.state_finished?).to be true
end end
end end
@ -312,14 +245,13 @@ RSpec.describe BulkImportService do
account.block_domain!('blocked.com') account.block_domain!('blocked.com')
end end
it 'blocks all the new domains' do it 'blocks all the new domains and marks import finished' do
subject.call(import) subject.call(import)
expect(account.domain_blocks.pluck(:domain)).to contain_exactly('blocked.com', 'to-block.com')
end
it 'marks the import as finished' do expect(account.domain_blocks.pluck(:domain))
subject.call(import) .to contain_exactly('blocked.com', 'to-block.com')
expect(import.reload.state_finished?).to be true expect(import.reload.state_finished?)
.to be true
end end
end end
@ -347,22 +279,16 @@ RSpec.describe BulkImportService do
account.bookmarks.create!(status: bookmarked) account.bookmarks.create!(status: bookmarked)
end end
it 'enqueues workers for the expected rows' do it 'enqueues workers for the expected rows and updates bookmarks after worker run' do
subject.call(import)
expect(Import::RowWorker.jobs.pluck('args').flatten).to match_array(rows.map(&:id))
end
it 'updates the bookmarks as expected once the workers have run' do
subject.call(import) subject.call(import)
service_double = instance_double(ActivityPub::FetchRemoteStatusService) expect(row_worker_job_args)
allow(ActivityPub::FetchRemoteStatusService).to receive(:new).and_return(service_double) .to match_array(rows.map(&:id))
allow(service_double).to receive(:call).with('https://domain.unknown/foo') { Fabricate(:status, uri: 'https://domain.unknown/foo') }
allow(service_double).to receive(:call).with('https://domain.unknown/private') { Fabricate(:status, uri: 'https://domain.unknown/private', visibility: :direct) }
Import::RowWorker.drain stub_fetch_remote_and_drain_workers
expect(account.bookmarks.map { |bookmark| bookmark.status.uri }).to contain_exactly(already_bookmarked.uri, status.uri, bookmarked.uri, 'https://domain.unknown/foo') expect(account.bookmarks.map { |bookmark| bookmark.status.uri })
.to contain_exactly(already_bookmarked.uri, status.uri, bookmarked.uri, 'https://domain.unknown/foo')
end end
end end
@ -390,23 +316,48 @@ RSpec.describe BulkImportService do
account.bookmarks.create!(status: bookmarked) account.bookmarks.create!(status: bookmarked)
end end
it 'enqueues workers for the expected rows' do it 'enqueues workers for the expected rows and updates bookmarks' do
subject.call(import)
expect(Import::RowWorker.jobs.pluck('args').flatten).to match_array(rows.map(&:id))
end
it 'updates the bookmarks as expected once the workers have run' do
subject.call(import) subject.call(import)
service_double = instance_double(ActivityPub::FetchRemoteStatusService) expect(row_worker_job_args)
allow(ActivityPub::FetchRemoteStatusService).to receive(:new).and_return(service_double) .to match_array(rows.map(&:id))
allow(service_double).to receive(:call).with('https://domain.unknown/foo') { Fabricate(:status, uri: 'https://domain.unknown/foo') }
allow(service_double).to receive(:call).with('https://domain.unknown/private') { Fabricate(:status, uri: 'https://domain.unknown/private', visibility: :direct) }
Import::RowWorker.drain stub_fetch_remote_and_drain_workers
expect(account.bookmarks.map { |bookmark| bookmark.status.uri }).to contain_exactly(status.uri, bookmarked.uri, 'https://domain.unknown/foo') expect(account.bookmarks.map { |bookmark| bookmark.status.uri })
.to contain_exactly(status.uri, bookmarked.uri, 'https://domain.unknown/foo')
end end
end end
def row_worker_job_args
Import::RowWorker
.jobs
.pluck('args')
.flatten
end
def stub_resolve_account_and_drain_workers
resolve_account_service_double = instance_double(ResolveAccountService)
allow(ResolveAccountService)
.to receive(:new)
.and_return(resolve_account_service_double)
allow(resolve_account_service_double)
.to receive(:call)
.with('user@foo.bar', any_args) { Fabricate(:account, username: 'user', domain: 'foo.bar', protocol: :activitypub) }
allow(resolve_account_service_double)
.to receive(:call)
.with('unknown@unknown.bar', any_args) { Fabricate(:account, username: 'unknown', domain: 'unknown.bar', protocol: :activitypub) }
Import::RowWorker.drain
end
def stub_fetch_remote_and_drain_workers
service_double = instance_double(ActivityPub::FetchRemoteStatusService)
allow(ActivityPub::FetchRemoteStatusService).to receive(:new).and_return(service_double)
allow(service_double).to receive(:call).with('https://domain.unknown/foo') { Fabricate(:status, uri: 'https://domain.unknown/foo') }
allow(service_double).to receive(:call).with('https://domain.unknown/private') { Fabricate(:status, uri: 'https://domain.unknown/private', visibility: :direct) }
Import::RowWorker.drain
end
end end
end end

View file

@ -11,11 +11,9 @@ RSpec.describe FavouriteService do
let(:bob) { Fabricate(:account) } let(:bob) { Fabricate(:account) }
let(:status) { Fabricate(:status, account: bob) } let(:status) { Fabricate(:status, account: bob) }
before do
subject.call(sender, status)
end
it 'creates a favourite' do it 'creates a favourite' do
subject.call(sender, status)
expect(status.favourites.first).to_not be_nil expect(status.favourites.first).to_not be_nil
end end
end end
@ -26,15 +24,16 @@ RSpec.describe FavouriteService do
before do before do
stub_request(:post, 'http://example.com/inbox').to_return(status: 200, body: '', headers: {}) stub_request(:post, 'http://example.com/inbox').to_return(status: 200, body: '', headers: {})
end
it 'creates a favourite and sends like activity', :inline_jobs do
subject.call(sender, status) subject.call(sender, status)
end
it 'creates a favourite' do expect(status.favourites.first)
expect(status.favourites.first).to_not be_nil .to_not be_nil
end
it 'sends a like activity', :inline_jobs do expect(a_request(:post, 'http://example.com/inbox'))
expect(a_request(:post, 'http://example.com/inbox')).to have_been_made.once .to have_been_made.once
end end
end end
end end

View file

@ -143,15 +143,16 @@ RSpec.describe FollowService do
before do before do
stub_request(:post, 'http://example.com/inbox').to_return(status: 200, body: '', headers: {}) stub_request(:post, 'http://example.com/inbox').to_return(status: 200, body: '', headers: {})
end
it 'creates follow request and sends an activity to inbox', :inline_jobs do
subject.call(sender, bob) subject.call(sender, bob)
end
it 'creates follow request' do expect(FollowRequest.find_by(account: sender, target_account: bob))
expect(FollowRequest.find_by(account: sender, target_account: bob)).to_not be_nil .to_not be_nil
end
it 'sends a follow activity to the inbox', :inline_jobs do expect(a_request(:post, 'http://example.com/inbox'))
expect(a_request(:post, 'http://example.com/inbox')).to have_been_made.once .to have_been_made.once
end end
end end
end end

View file

@ -16,20 +16,25 @@ RSpec.describe ProcessMentionsService do
before do before do
account.block!(individually_blocked_account) account.block!(individually_blocked_account)
account.domain_blocks.create!(domain: domain_blocked_account.domain) account.domain_blocks.create!(domain: domain_blocked_account.domain)
subject.call(status)
end end
it 'creates a mention to the non-blocked account' do it 'creates a mention to the non-blocked account but not the individually or domain blocked accounts' do
expect(non_blocked_account.mentions.where(status: status).count).to eq 1 expect { subject.call(status) }
.to create_mention_for_non_blocked
.and skip_mention_for_individual
.and skip_mention_for_domain_blocked
end end
it 'does not create a mention to the individually blocked account' do def create_mention_for_non_blocked
expect(individually_blocked_account.mentions.where(status: status).count).to eq 0 change { non_blocked_account.mentions.where(status: status).count }.to(1)
end end
it 'does not create a mention to the domain-blocked account' do def skip_mention_for_individual
expect(domain_blocked_account.mentions.where(status: status).count).to eq 0 not_change { individually_blocked_account.mentions.where(status: status).count }.from(0)
end
def skip_mention_for_domain_blocked
not_change { domain_blocked_account.mentions.where(status: status).count }.from(0)
end end
end end
@ -40,11 +45,9 @@ RSpec.describe ProcessMentionsService do
context 'with a valid remote user' do context 'with a valid remote user' do
let!(:remote_user) { Fabricate(:account, username: 'remote_user', protocol: :activitypub, domain: 'example.com', inbox_url: 'http://example.com/inbox') } let!(:remote_user) { Fabricate(:account, username: 'remote_user', protocol: :activitypub, domain: 'example.com', inbox_url: 'http://example.com/inbox') }
before do
subject.call(status)
end
it 'creates a mention' do it 'creates a mention' do
subject.call(status)
expect(remote_user.mentions.where(status: status).count).to eq 1 expect(remote_user.mentions.where(status: status).count).to eq 1
end end
end end
@ -53,11 +56,9 @@ RSpec.describe ProcessMentionsService do
let!(:remote_user) { Fabricate(:account, username: 'remote_user', protocol: :activitypub, domain: 'example.com', inbox_url: 'http://example.com/inbox') } let!(:remote_user) { Fabricate(:account, username: 'remote_user', protocol: :activitypub, domain: 'example.com', inbox_url: 'http://example.com/inbox') }
let(:status) { Fabricate(:status, account: account, text: "Hello @#{remote_user.acct} @#{remote_user.acct} @#{remote_user.acct}", visibility: :public) } let(:status) { Fabricate(:status, account: account, text: "Hello @#{remote_user.acct} @#{remote_user.acct} @#{remote_user.acct}", visibility: :public) }
before do
subject.call(status, save_records: false)
end
it 'creates exactly one mention' do it 'creates exactly one mention' do
subject.call(status, save_records: false)
expect(status.mentions.size).to eq 1 expect(status.mentions.size).to eq 1
end end
end end
@ -66,11 +67,9 @@ RSpec.describe ProcessMentionsService do
let!(:remote_user) { Fabricate(:account, username: 'sneak', protocol: :activitypub, domain: 'xn--hresiar-mxa.ch', inbox_url: 'http://example.com/inbox') } let!(:remote_user) { Fabricate(:account, username: 'sneak', protocol: :activitypub, domain: 'xn--hresiar-mxa.ch', inbox_url: 'http://example.com/inbox') }
let!(:status) { Fabricate(:status, account: account, text: 'Hello @sneak@hæresiar.ch') } let!(:status) { Fabricate(:status, account: account, text: 'Hello @sneak@hæresiar.ch') }
before do
subject.call(status)
end
it 'creates a mention' do it 'creates a mention' do
subject.call(status)
expect(remote_user.mentions.where(status: status).count).to eq 1 expect(remote_user.mentions.where(status: status).count).to eq 1
end end
end end
@ -79,11 +78,9 @@ RSpec.describe ProcessMentionsService do
let!(:remote_user) { Fabricate(:account, username: 'foo', protocol: :activitypub, domain: 'xn--y9a3aq.xn--y9a3aq', inbox_url: 'http://example.com/inbox') } let!(:remote_user) { Fabricate(:account, username: 'foo', protocol: :activitypub, domain: 'xn--y9a3aq.xn--y9a3aq', inbox_url: 'http://example.com/inbox') }
let!(:status) { Fabricate(:status, account: account, text: 'Hello @foo@հայ.հայ') } let!(:status) { Fabricate(:status, account: account, text: 'Hello @foo@հայ.հայ') }
before do
subject.call(status)
end
it 'creates a mention' do it 'creates a mention' do
subject.call(status)
expect(remote_user.mentions.where(status: status).count).to eq 1 expect(remote_user.mentions.where(status: status).count).to eq 1
end end
end end
@ -95,10 +92,11 @@ RSpec.describe ProcessMentionsService do
before do before do
stub_request(:get, 'https://example.com/.well-known/host-meta').to_return(status: 404) stub_request(:get, 'https://example.com/.well-known/host-meta').to_return(status: 404)
stub_request(:get, 'https://example.com/.well-known/webfinger?resource=acct:remote_user@example.com').to_return(status: 500) stub_request(:get, 'https://example.com/.well-known/webfinger?resource=acct:remote_user@example.com').to_return(status: 500)
subject.call(status)
end end
it 'creates a mention' do it 'creates a mention' do
subject.call(status)
expect(remote_user.mentions.where(status: status).count).to eq 1 expect(remote_user.mentions.where(status: status).count).to eq 1
end end
end end

View file

@ -10,17 +10,15 @@ RSpec.describe RejectFollowService do
describe 'local' do describe 'local' do
let(:bob) { Fabricate(:account) } let(:bob) { Fabricate(:account) }
before do before { FollowRequest.create(account: bob, target_account: sender) }
FollowRequest.create(account: bob, target_account: sender)
it 'removes follow request and does not create relation' do
subject.call(bob, sender) subject.call(bob, sender)
end
it 'removes follow request' do expect(bob)
expect(bob.requested?(sender)).to be false .to_not be_requested(sender)
end expect(bob)
.to_not be_following(sender)
it 'does not create follow relation' do
expect(bob.following?(sender)).to be false
end end
end end
@ -30,19 +28,17 @@ RSpec.describe RejectFollowService do
before do before do
FollowRequest.create(account: bob, target_account: sender) FollowRequest.create(account: bob, target_account: sender)
stub_request(:post, bob.inbox_url).to_return(status: 200) stub_request(:post, bob.inbox_url).to_return(status: 200)
end
it 'removes follow request, does not create relation, sends reject activity', :inline_jobs do
subject.call(bob, sender) subject.call(bob, sender)
end
it 'removes follow request' do expect(bob)
expect(bob.requested?(sender)).to be false .to_not be_requested(sender)
end expect(bob)
.to_not be_following(sender)
it 'does not create follow relation' do expect(a_request(:post, bob.inbox_url))
expect(bob.following?(sender)).to be false .to have_been_made.once
end
it 'sends a reject activity', :inline_jobs do
expect(a_request(:post, bob.inbox_url)).to have_been_made.once
end end
end end
end end

View file

@ -10,13 +10,13 @@ RSpec.describe RemoveFromFollowersService do
describe 'local' do describe 'local' do
let(:sender) { Fabricate(:account, username: 'alice') } let(:sender) { Fabricate(:account, username: 'alice') }
before do before { Follow.create(account: sender, target_account: bob) }
Follow.create(account: sender, target_account: bob)
subject.call(bob, sender)
end
it 'does not create follow relation' do it 'does not create follow relation' do
expect(bob.followed_by?(sender)).to be false subject.call(bob, sender)
expect(bob)
.to_not be_followed_by(sender)
end end
end end
@ -26,15 +26,16 @@ RSpec.describe RemoveFromFollowersService do
before do before do
Follow.create(account: sender, target_account: bob) Follow.create(account: sender, target_account: bob)
stub_request(:post, sender.inbox_url).to_return(status: 200) stub_request(:post, sender.inbox_url).to_return(status: 200)
end
it 'does not create follow relation and sends reject activity', :inline_jobs do
subject.call(bob, sender) subject.call(bob, sender)
end
it 'does not create follow relation' do expect(bob)
expect(bob.followed_by?(sender)).to be false .to_not be_followed_by(sender)
end
it 'sends a reject activity', :inline_jobs do expect(a_request(:post, sender.inbox_url))
expect(a_request(:post, sender.inbox_url)).to have_been_made.once .to have_been_made.once
end end
end end
end end

View file

@ -28,42 +28,38 @@ RSpec.describe RemoveStatusService, :inline_jobs do
Fabricate(:status, account: bill, reblog: status, uri: 'hoge') Fabricate(:status, account: bill, reblog: status, uri: 'hoge')
end end
it 'removes status from author\'s home feed' do it 'removes status from notifications and from author and local follower home feeds, publishes to media timeline, sends delete activities' do
subject.call(status)
expect(HomeFeed.new(alice).get(10).pluck(:id)).to_not include(status.id)
end
it 'removes status from local follower\'s home feed' do
subject.call(status)
expect(HomeFeed.new(jeff).get(10).pluck(:id)).to_not include(status.id)
end
it 'publishes to public media timeline' do
allow(redis).to receive(:publish).with(any_args) allow(redis).to receive(:publish).with(any_args)
subject.call(status) expect { subject.call(status) }
.to remove_status_from_notifications
expect(redis).to have_received(:publish).with('timeline:public:media', Oj.dump(event: :delete, payload: status.id.to_s)) expect(home_feed_ids(alice))
end .to_not include(status.id)
expect(home_feed_ids(jeff))
.to_not include(status.id)
it 'sends Delete activity to followers' do expect(redis)
subject.call(status) .to have_received(:publish).with('timeline:public:media', Oj.dump(event: :delete, payload: status.id.to_s))
expect(delete_delivery(hank, status)) expect(delete_delivery(hank, status))
.to have_been_made.once .to have_been_made.once
end
it 'sends Delete activity to rebloggers' do
subject.call(status)
expect(delete_delivery(bill, status)) expect(delete_delivery(bill, status))
.to have_been_made.once .to have_been_made.once
end end
it 'remove status from notifications' do def home_feed_ids(personage)
expect { subject.call(status) }.to change { HomeFeed
Notification.where(activity_type: 'Favourite', from_account: jeff, account: alice).count .new(personage)
}.from(1).to(0) .get(10)
.pluck(:id)
end
def remove_status_from_notifications
change { Notification.where(activity_type: 'Favourite', from_account: jeff, account: alice).count }
.from(1)
.to(0)
end end
def delete_delivery(target, status) def delete_delivery(target, status)

View file

@ -31,14 +31,13 @@ RSpec.describe ReportService do
context 'when forward is true', :inline_jobs do context 'when forward is true', :inline_jobs do
let(:forward) { true } let(:forward) { true }
it 'sends ActivityPub payload when forward is true' do it 'has a URI and sends ActivityPub payload' do
subject.call(source_account, remote_account, forward: forward)
expect(a_request(:post, 'http://example.com/inbox')).to have_been_made
end
it 'has an uri' do
report = subject.call(source_account, remote_account, forward: forward) report = subject.call(source_account, remote_account, forward: forward)
expect(report.uri).to_not be_nil
expect(report.uri)
.to_not be_nil
expect(a_request(:post, 'http://example.com/inbox'))
.to have_been_made
end end
context 'when reporting a reply on a different remote server' do context 'when reporting a reply on a different remote server' do
@ -122,13 +121,12 @@ RSpec.describe ReportService do
status.mentions.create(account: source_account) status.mentions.create(account: source_account)
end end
it 'creates a report' do it 'creates a report and attaches the DM to the report' do
expect { subject.call }.to change { target_account.targeted_reports.count }.from(0).to(1) expect { subject.call }
end .to change { target_account.targeted_reports.count }.from(0).to(1)
it 'attaches the DM to the report' do expect(target_account.targeted_reports.pluck(:status_ids))
subject.call .to eq [[status.id]]
expect(target_account.targeted_reports.pluck(:status_ids)).to eq [[status.id]]
end end
end end
@ -146,13 +144,12 @@ RSpec.describe ReportService do
status.mentions.create(account: source_account) status.mentions.create(account: source_account)
end end
it 'creates a report' do it 'creates a report and attaches DM to report' do
expect { subject.call }.to change { target_account.targeted_reports.count }.from(0).to(1) expect { subject.call }
end .to change { target_account.targeted_reports.count }.from(0).to(1)
it 'attaches the DM to the report' do expect(target_account.targeted_reports.pluck(:status_ids))
subject.call .to eq [[status.id]]
expect(target_account.targeted_reports.pluck(:status_ids)).to eq [[status.id]]
end end
end end

View file

@ -22,37 +22,38 @@ RSpec.describe ResolveAccountService do
context 'when domain is banned' do context 'when domain is banned' do
before { Fabricate(:domain_block, domain: 'ap.example.com', severity: :suspend) } before { Fabricate(:domain_block, domain: 'ap.example.com', severity: :suspend) }
it 'does not return an account' do it 'does not return an account or make a webfinger query' do
expect(subject.call('foo@ap.example.com', skip_webfinger: true)).to be_nil expect(subject.call('foo@ap.example.com', skip_webfinger: true))
end .to be_nil
expect(webfinger_discovery_request)
it 'does not make a webfinger query' do .to_not have_been_made
subject.call('foo@ap.example.com', skip_webfinger: true)
expect(a_request(:get, 'https://ap.example.com/.well-known/webfinger?resource=acct:foo@ap.example.com')).to_not have_been_made
end end
end end
context 'when domain is not banned' do context 'when domain is not banned' do
it 'returns the expected account' do it 'returns the expected account and does not make a webfinger query' do
expect(subject.call('foo@ap.example.com', skip_webfinger: true)).to eq remote_account expect(subject.call('foo@ap.example.com', skip_webfinger: true))
end .to eq remote_account
expect(webfinger_discovery_request)
it 'does not make a webfinger query' do .to_not have_been_made
subject.call('foo@ap.example.com', skip_webfinger: true)
expect(a_request(:get, 'https://ap.example.com/.well-known/webfinger?resource=acct:foo@ap.example.com')).to_not have_been_made
end end
end end
end end
context 'when account is not known' do context 'when account is not known' do
it 'does not return an account' do it 'does not return an account and does not make webfinger query' do
expect(subject.call('foo@ap.example.com', skip_webfinger: true)).to be_nil expect(subject.call('foo@ap.example.com', skip_webfinger: true))
.to be_nil
expect(webfinger_discovery_request)
.to_not have_been_made
end end
end
it 'does not make a webfinger query' do def webfinger_discovery_request
subject.call('foo@ap.example.com', skip_webfinger: true) a_request(
expect(a_request(:get, 'https://ap.example.com/.well-known/webfinger?resource=acct:foo@ap.example.com')).to_not have_been_made :get,
end 'https://ap.example.com/.well-known/webfinger?resource=acct:foo@ap.example.com'
)
end end
end end
@ -84,13 +85,11 @@ RSpec.describe ResolveAccountService do
allow(AccountDeletionWorker).to receive(:perform_async) allow(AccountDeletionWorker).to receive(:perform_async)
end end
it 'returns nil' do it 'returns nil and queues deletion worker' do
expect(subject.call('hoge@example.com')).to be_nil expect(subject.call('hoge@example.com'))
end .to be_nil
expect(AccountDeletionWorker)
it 'queues account deletion worker' do .to have_received(:perform_async)
subject.call('hoge@example.com')
expect(AccountDeletionWorker).to have_received(:perform_async)
end end
end end
@ -110,9 +109,12 @@ RSpec.describe ResolveAccountService do
it 'returns new remote account' do it 'returns new remote account' do
account = subject.call('Foo@redirected.example.com') account = subject.call('Foo@redirected.example.com')
expect(account.activitypub?).to be true expect(account)
expect(account.acct).to eq 'foo@ap.example.com' .to have_attributes(
expect(account.inbox_url).to eq 'https://ap.example.com/users/foo/inbox' activitypub?: true,
acct: 'foo@ap.example.com',
inbox_url: 'https://ap.example.com/users/foo/inbox'
)
end end
end end
@ -125,9 +127,12 @@ RSpec.describe ResolveAccountService do
it 'returns new remote account' do it 'returns new remote account' do
account = subject.call('Foo@redirected.example.com') account = subject.call('Foo@redirected.example.com')
expect(account.activitypub?).to be true expect(account)
expect(account.acct).to eq 'foo@ap.example.com' .to have_attributes(
expect(account.inbox_url).to eq 'https://ap.example.com/users/foo/inbox' activitypub?: true,
acct: 'foo@ap.example.com',
inbox_url: 'https://ap.example.com/users/foo/inbox'
)
end end
end end
@ -161,9 +166,12 @@ RSpec.describe ResolveAccountService do
it 'returns new remote account' do it 'returns new remote account' do
account = subject.call('foo@ap.example.com') account = subject.call('foo@ap.example.com')
expect(account.activitypub?).to be true expect(account)
expect(account.domain).to eq 'ap.example.com' .to have_attributes(
expect(account.inbox_url).to eq 'https://ap.example.com/users/foo/inbox' activitypub?: true,
domain: 'ap.example.com',
inbox_url: 'https://ap.example.com/users/foo/inbox'
)
end end
context 'with multiple types' do context 'with multiple types' do
@ -174,10 +182,13 @@ RSpec.describe ResolveAccountService do
it 'returns new remote account' do it 'returns new remote account' do
account = subject.call('foo@ap.example.com') account = subject.call('foo@ap.example.com')
expect(account.activitypub?).to be true expect(account)
expect(account.domain).to eq 'ap.example.com' .to have_attributes(
expect(account.inbox_url).to eq 'https://ap.example.com/users/foo/inbox' activitypub?: true,
expect(account.actor_type).to eq 'Person' domain: 'ap.example.com',
inbox_url: 'https://ap.example.com/users/foo/inbox',
actor_type: 'Person'
)
end end
end end
end end
@ -186,20 +197,21 @@ RSpec.describe ResolveAccountService do
let!(:duplicate) { Fabricate(:account, username: 'foo', domain: 'old.example.com', uri: 'https://ap.example.com/users/foo') } let!(:duplicate) { Fabricate(:account, username: 'foo', domain: 'old.example.com', uri: 'https://ap.example.com/users/foo') }
let!(:status) { Fabricate(:status, account: duplicate, text: 'foo') } let!(:status) { Fabricate(:status, account: duplicate, text: 'foo') }
it 'returns new remote account' do it 'returns new remote account and merges accounts', :inline_jobs do
account = subject.call('foo@ap.example.com') account = subject.call('foo@ap.example.com')
expect(account.activitypub?).to be true expect(account)
expect(account.domain).to eq 'ap.example.com' .to have_attributes(
expect(account.inbox_url).to eq 'https://ap.example.com/users/foo/inbox' activitypub?: true,
expect(account.uri).to eq 'https://ap.example.com/users/foo' domain: 'ap.example.com',
end inbox_url: 'https://ap.example.com/users/foo/inbox',
uri: 'https://ap.example.com/users/foo'
)
it 'merges accounts', :inline_jobs do expect(status.reload.account_id)
account = subject.call('foo@ap.example.com') .to eq account.id
expect(Account.where(uri: account.uri).count)
expect(status.reload.account_id).to eq account.id .to eq 1
expect(Account.where(uri: account.uri).count).to eq 1
end end
end end
@ -210,11 +222,15 @@ RSpec.describe ResolveAccountService do
it 'returns new remote account' do it 'returns new remote account' do
account = subject.call('foo@ap.example.com') account = subject.call('foo@ap.example.com')
expect(account.activitypub?).to be true expect(account)
expect(account.domain).to eq 'ap.example.com' .to have_attributes(
expect(account.inbox_url).to eq 'https://ap.example.com/users/foo/inbox' activitypub?: true,
expect(account.uri).to eq 'https://ap.example.com/users/foo' domain: 'ap.example.com',
expect(status.reload.account).to eq(account) inbox_url: 'https://ap.example.com/users/foo/inbox',
uri: 'https://ap.example.com/users/foo'
)
expect(status.reload.account)
.to eq(account)
end end
end end

View file

@ -51,12 +51,11 @@ RSpec.describe ResolveURLService do
let(:url) { 'https://example.com/@foo/42' } let(:url) { 'https://example.com/@foo/42' }
let(:uri) { 'https://example.com/users/foo/statuses/42' } let(:uri) { 'https://example.com/users/foo/statuses/42' }
it 'returns status by url' do it 'returns status by URL or URI' do
expect(subject.call(url, on_behalf_of: account)).to eq(status) expect(subject.call(url, on_behalf_of: account))
end .to eq(status)
expect(subject.call(uri, on_behalf_of: account))
it 'returns status by uri' do .to eq(status)
expect(subject.call(uri, on_behalf_of: account)).to eq(status)
end end
end end
@ -75,12 +74,11 @@ RSpec.describe ResolveURLService do
let(:url) { 'https://example.com/@foo/42' } let(:url) { 'https://example.com/@foo/42' }
let(:uri) { 'https://example.com/users/foo/statuses/42' } let(:uri) { 'https://example.com/users/foo/statuses/42' }
it 'does not return the status by url' do it 'does not return the status by URL or URI' do
expect(subject.call(url, on_behalf_of: account)).to be_nil expect(subject.call(url, on_behalf_of: account))
end .to be_nil
expect(subject.call(uri, on_behalf_of: account))
it 'does not return the status by uri' do .to be_nil
expect(subject.call(uri, on_behalf_of: account)).to be_nil
end end
end end
@ -107,22 +105,20 @@ RSpec.describe ResolveURLService do
account.follow!(poster) account.follow!(poster)
end end
it 'returns status by url' do it 'returns status by URL or URI' do
expect(subject.call(url, on_behalf_of: account)).to eq(status) expect(subject.call(url, on_behalf_of: account))
end .to eq(status)
expect(subject.call(uri, on_behalf_of: account))
it 'returns status by uri' do .to eq(status)
expect(subject.call(uri, on_behalf_of: account)).to eq(status)
end end
end end
context 'when the account does not follow the poster' do context 'when the account does not follow the poster' do
it 'does not return the status by url' do it 'does not return the status by URL or URI' do
expect(subject.call(url, on_behalf_of: account)).to be_nil expect(subject.call(url, on_behalf_of: account))
end .to be_nil
expect(subject.call(uri, on_behalf_of: account))
it 'does not return the status by uri' do .to be_nil
expect(subject.call(uri, on_behalf_of: account)).to be_nil
end end
end end
end end

View file

@ -32,20 +32,14 @@ RSpec.describe TranslateStatusService do
allow(TranslationService).to receive_messages(configured?: true, configured: translation_service) allow(TranslationService).to receive_messages(configured?: true, configured: translation_service)
end end
it 'returns translated status content' do it 'returns translated status content and source language and provider and original status' do
expect(service.call(status, 'es').content).to eq '<p>Hola</p>' expect(service.call(status, 'es'))
end .to have_attributes(
content: '<p>Hola</p>',
it 'returns source language' do detected_source_language: 'en',
expect(service.call(status, 'es').detected_source_language).to eq 'en' provider: 'Dummy',
end status: status
)
it 'returns translation provider' do
expect(service.call(status, 'es').provider).to eq 'Dummy'
end
it 'returns original status' do
expect(service.call(status, 'es').status).to eq status
end end
describe 'status has content with custom emoji' do describe 'status has content with custom emoji' do
@ -155,26 +149,16 @@ RSpec.describe TranslateStatusService do
let!(:source_texts) { service.send(:source_texts) } let!(:source_texts) { service.send(:source_texts) }
it 'returns formatted poll options' do it 'returns formatted poll options' do
expect(source_texts.size).to eq 3 expect(source_texts)
expect(source_texts.values).to eq %w(<p>Hello</p> Blue Green) .to have_attributes(
end size: 3,
values: %w(<p>Hello</p> Blue Green),
it 'has a first key with content' do keys: contain_exactly(
expect(source_texts.keys.first).to eq :content eq(:content),
end be_a(Poll::Option).and(have_attributes(id: '0', title: 'Blue')),
be_a(Poll::Option).and(have_attributes(id: '1', title: 'Green'))
it 'has the first option in the second key with correct options' do )
option1 = source_texts.keys.second )
expect(option1).to be_a Poll::Option
expect(option1.id).to eq '0'
expect(option1.title).to eq 'Blue'
end
it 'has the second option in the third key with correct options' do
option2 = source_texts.keys.third
expect(option2).to be_a Poll::Option
expect(option2.id).to eq '1'
expect(option2.title).to eq 'Green'
end end
end end
end end

View file

@ -12,26 +12,32 @@ RSpec.describe UnblockDomainService do
let!(:silenced) { Fabricate(:account, domain: 'example.com', silenced_at: domain_block.created_at) } let!(:silenced) { Fabricate(:account, domain: 'example.com', silenced_at: domain_block.created_at) }
let!(:suspended) { Fabricate(:account, domain: 'example.com', suspended_at: domain_block.created_at) } let!(:suspended) { Fabricate(:account, domain: 'example.com', suspended_at: domain_block.created_at) }
it 'unsilences accounts and removes block' do context 'with severity of silence' do
domain_block.update(severity: :silence) before { domain_block.update(severity: :silence) }
subject.call(domain_block) it 'unsilences accounts and removes block' do
expect_deleted_domain_block subject.call(domain_block)
expect(silenced.reload.silenced?).to be false
expect(suspended.reload.suspended?).to be true expect_deleted_domain_block
expect(independently_suspended.reload.suspended?).to be true expect(silenced.reload.silenced?).to be false
expect(independently_silenced.reload.silenced?).to be true expect(suspended.reload.suspended?).to be true
expect(independently_suspended.reload.suspended?).to be true
expect(independently_silenced.reload.silenced?).to be true
end
end end
it 'unsuspends accounts and removes block' do context 'with severity of suspend' do
domain_block.update(severity: :suspend) before { domain_block.update(severity: :suspend) }
subject.call(domain_block) it 'unsuspends accounts and removes block' do
expect_deleted_domain_block subject.call(domain_block)
expect(suspended.reload.suspended?).to be false
expect(silenced.reload.silenced?).to be false expect_deleted_domain_block
expect(independently_suspended.reload.suspended?).to be true expect(suspended.reload.suspended?).to be false
expect(independently_silenced.reload.silenced?).to be true expect(silenced.reload.silenced?).to be false
expect(independently_suspended.reload.suspended?).to be true
expect(independently_silenced.reload.silenced?).to be true
end
end end
end end

View file

@ -10,13 +10,13 @@ RSpec.describe UnblockService do
describe 'local' do describe 'local' do
let(:bob) { Fabricate(:account) } let(:bob) { Fabricate(:account) }
before do before { sender.block!(bob) }
sender.block!(bob)
subject.call(sender, bob)
end
it 'destroys the blocking relation' do it 'destroys the blocking relation' do
expect(sender.blocking?(bob)).to be false subject.call(sender, bob)
expect(sender)
.to_not be_blocking(bob)
end end
end end
@ -26,15 +26,15 @@ RSpec.describe UnblockService do
before do before do
sender.block!(bob) sender.block!(bob)
stub_request(:post, 'http://example.com/inbox').to_return(status: 200) stub_request(:post, 'http://example.com/inbox').to_return(status: 200)
end
it 'destroys the blocking relation and sends unblock activity', :inline_jobs do
subject.call(sender, bob) subject.call(sender, bob)
end
it 'destroys the blocking relation' do expect(sender)
expect(sender.blocking?(bob)).to be false .to_not be_blocking(bob)
end expect(a_request(:post, 'http://example.com/inbox'))
.to have_been_made.once
it 'sends an unblock activity', :inline_jobs do
expect(a_request(:post, 'http://example.com/inbox')).to have_been_made.once
end end
end end
end end

View file

@ -10,13 +10,13 @@ RSpec.describe UnfollowService do
describe 'local' do describe 'local' do
let(:bob) { Fabricate(:account, username: 'bob') } let(:bob) { Fabricate(:account, username: 'bob') }
before do before { sender.follow!(bob) }
sender.follow!(bob)
subject.call(sender, bob)
end
it 'destroys the following relation' do it 'destroys the following relation' do
expect(sender.following?(bob)).to be false subject.call(sender, bob)
expect(sender)
.to_not be_following(bob)
end end
end end
@ -26,15 +26,15 @@ RSpec.describe UnfollowService do
before do before do
sender.follow!(bob) sender.follow!(bob)
stub_request(:post, 'http://example.com/inbox').to_return(status: 200) stub_request(:post, 'http://example.com/inbox').to_return(status: 200)
end
it 'destroys the following relation and sends unfollow activity' do
subject.call(sender, bob) subject.call(sender, bob)
end
it 'destroys the following relation' do expect(sender)
expect(sender.following?(bob)).to be false .to_not be_following(bob)
end expect(a_request(:post, 'http://example.com/inbox'))
.to have_been_made.once
it 'sends an unfollow activity' do
expect(a_request(:post, 'http://example.com/inbox')).to have_been_made.once
end end
end end
@ -44,15 +44,15 @@ RSpec.describe UnfollowService do
before do before do
bob.follow!(sender) bob.follow!(sender)
stub_request(:post, 'http://example.com/inbox').to_return(status: 200) stub_request(:post, 'http://example.com/inbox').to_return(status: 200)
end
it 'destroys the following relation and sends a reject activity' do
subject.call(bob, sender) subject.call(bob, sender)
end
it 'destroys the following relation' do expect(sender)
expect(bob.following?(sender)).to be false .to_not be_following(bob)
end expect(a_request(:post, 'http://example.com/inbox'))
.to have_been_made.once
it 'sends a reject activity' do
expect(a_request(:post, 'http://example.com/inbox')).to have_been_made.once
end end
end end
end end

View file

@ -18,23 +18,19 @@ RSpec.describe UpdateAccountService do
FollowService.new.call(alice, account) FollowService.new.call(alice, account)
FollowService.new.call(bob, account) FollowService.new.call(bob, account)
FollowService.new.call(eve, account) FollowService.new.call(eve, account)
end
it 'auto accepts pending follow requests from appropriate accounts' do
subject.call(account, { locked: false }) subject.call(account, { locked: false })
end
it 'auto-accepts pending follow requests' do expect(alice).to be_following(account)
expect(alice.following?(account)).to be true expect(alice).to_not be_requested(account)
expect(alice.requested?(account)).to be false
end
it 'does not auto-accept pending follow requests from silenced users' do expect(bob).to_not be_following(account)
expect(bob.following?(account)).to be false expect(bob).to be_requested(account)
expect(bob.requested?(account)).to be true
end
it 'auto-accepts pending follow requests from muted users so as to not leak mute' do expect(eve).to be_following(account)
expect(eve.following?(account)).to be true expect(eve).to_not be_requested(account)
expect(eve.requested?(account)).to be false
end end
end end
end end

View file

@ -10,15 +10,15 @@ RSpec.describe UpdateStatusService do
before do before do
allow(ActivityPub::DistributionWorker).to receive(:perform_async) allow(ActivityPub::DistributionWorker).to receive(:perform_async)
end
it 'does not create an edit or notify anyone' do
subject.call(status, status.account_id, text: 'Foo') subject.call(status, status.account_id, text: 'Foo')
end
it 'does not create an edit' do expect(status.reload.edits)
expect(status.reload.edits).to be_empty .to be_empty
end expect(ActivityPub::DistributionWorker)
.to_not have_received(:perform_async)
it 'does not notify anyone' do
expect(ActivityPub::DistributionWorker).to_not have_received(:perform_async)
end end
end end
@ -28,18 +28,16 @@ RSpec.describe UpdateStatusService do
before do before do
PreviewCardsStatus.create(status: status, preview_card: preview_card) PreviewCardsStatus.create(status: status, preview_card: preview_card)
end
it 'updates text, resets card, saves edit history' do
subject.call(status, status.account_id, text: 'Bar') subject.call(status, status.account_id, text: 'Bar')
end
it 'updates text' do expect(status.reload)
expect(status.reload.text).to eq 'Bar' .to have_attributes(
end text: 'Bar',
preview_card: be_nil
it 'resets preview card' do )
expect(status.reload.preview_card).to be_nil
end
it 'saves edit history' do
expect(status.edits.ordered.pluck(:text)).to eq %w(Foo Bar) expect(status.edits.ordered.pluck(:text)).to eq %w(Foo Bar)
end end
end end
@ -50,15 +48,15 @@ RSpec.describe UpdateStatusService do
before do before do
PreviewCardsStatus.create(status: status, preview_card: preview_card) PreviewCardsStatus.create(status: status, preview_card: preview_card)
end
it 'updates content warning and saves history' do
subject.call(status, status.account_id, text: 'Foo', spoiler_text: 'Bar') subject.call(status, status.account_id, text: 'Foo', spoiler_text: 'Bar')
end
it 'updates content warning' do expect(status.reload.spoiler_text)
expect(status.reload.spoiler_text).to eq 'Bar' .to eq 'Bar'
end expect(status.edits.ordered.pluck(:text, :spoiler_text))
.to eq [['Foo', ''], ['Foo', 'Bar']]
it 'saves edit history' do
expect(status.edits.ordered.pluck(:text, :spoiler_text)).to eq [['Foo', ''], ['Foo', 'Bar']]
end end
end end
@ -69,23 +67,19 @@ RSpec.describe UpdateStatusService do
before do before do
status.media_attachments << detached_media_attachment status.media_attachments << detached_media_attachment
end
it 'updates media attachments, handles attachments, saves history' do
subject.call(status, status.account_id, text: 'Foo', media_ids: [attached_media_attachment.id.to_s]) subject.call(status, status.account_id, text: 'Foo', media_ids: [attached_media_attachment.id.to_s])
end
it 'updates media attachments' do expect(status.ordered_media_attachments)
expect(status.ordered_media_attachments).to eq [attached_media_attachment] .to eq [attached_media_attachment]
end expect(detached_media_attachment.reload.status_id)
.to eq status.id
it 'does not detach detached media attachments' do expect(attached_media_attachment.reload.status_id)
expect(detached_media_attachment.reload.status_id).to eq status.id .to eq status.id
end expect(status.edits.ordered.pluck(:ordered_media_attachment_ids))
.to eq [[detached_media_attachment.id], [attached_media_attachment.id]]
it 'attaches attached media attachments' do
expect(attached_media_attachment.reload.status_id).to eq status.id
end
it 'saves edit history' do
expect(status.edits.ordered.pluck(:ordered_media_attachment_ids)).to eq [[detached_media_attachment.id], [attached_media_attachment.id]]
end end
end end
@ -95,19 +89,18 @@ RSpec.describe UpdateStatusService do
before do before do
status.media_attachments << media_attachment status.media_attachments << media_attachment
end
it 'does not detach media attachment, updates description, and saves history' do
subject.call(status, status.account_id, text: 'Foo', media_ids: [media_attachment.id.to_s], media_attributes: [{ id: media_attachment.id, description: 'New description' }]) subject.call(status, status.account_id, text: 'Foo', media_ids: [media_attachment.id.to_s], media_attributes: [{ id: media_attachment.id, description: 'New description' }])
end
it 'does not detach media attachment' do expect(media_attachment.reload)
expect(media_attachment.reload.status_id).to eq status.id .to have_attributes(
end status_id: status.id,
description: 'New description'
it 'updates the media attachment description' do )
expect(media_attachment.reload.description).to eq 'New description' expect(status.edits.ordered.map { |edit| edit.ordered_media_attachments.map(&:description) })
end .to eq [['Old description'], ['New description']]
it 'saves edit history' do
expect(status.edits.ordered.map { |edit| edit.ordered_media_attachments.map(&:description) }).to eq [['Old description'], ['New description']]
end end
end end
@ -120,28 +113,27 @@ RSpec.describe UpdateStatusService do
before do before do
status.update(poll: poll) status.update(poll: poll)
VoteService.new.call(voter, poll, [0]) VoteService.new.call(voter, poll, [0])
end
it 'updates poll, resets votes, saves history, requeues notifications' do
subject.call(status, status.account_id, text: 'Foo', poll: { options: %w(Bar Baz Foo), expires_in: 5.days.to_i }) subject.call(status, status.account_id, text: 'Foo', poll: { options: %w(Bar Baz Foo), expires_in: 5.days.to_i })
end
it 'updates poll' do
poll = status.poll.reload poll = status.poll.reload
expect(poll.options).to eq %w(Bar Baz Foo)
end
it 'resets votes' do expect(poll)
poll = status.poll.reload .to have_attributes(
expect(poll.votes_count).to eq 0 options: %w(Bar Baz Foo),
expect(poll.votes.count).to eq 0 votes_count: 0,
expect(poll.cached_tallies).to eq [0, 0, 0] cached_tallies: [0, 0, 0]
end )
expect(poll.votes.count)
.to eq(0)
it 'saves edit history' do expect(status.edits.ordered.pluck(:poll_options))
expect(status.edits.ordered.pluck(:poll_options)).to eq [%w(Foo Bar), %w(Bar Baz Foo)] .to eq [%w(Foo Bar), %w(Bar Baz Foo)]
end
it 'requeues expiration notification' do expect(PollExpirationNotifyWorker)
poll = status.poll.reload .to have_enqueued_sidekiq_job(poll.id).at(poll.expires_at + 5.minutes)
expect(PollExpirationNotifyWorker).to have_enqueued_sidekiq_job(poll.id).at(poll.expires_at + 5.minutes)
end end
end end
@ -151,16 +143,13 @@ RSpec.describe UpdateStatusService do
let!(:bob) { Fabricate(:account, username: 'bob') } let!(:bob) { Fabricate(:account, username: 'bob') }
let!(:status) { PostStatusService.new.call(account, text: 'Hello @alice') } let!(:status) { PostStatusService.new.call(account, text: 'Hello @alice') }
before do it 'changes mentions and keeps old as silent' do
subject.call(status, status.account_id, text: 'Hello @bob') subject.call(status, status.account_id, text: 'Hello @bob')
end
it 'changes mentions' do expect(status.active_mentions.pluck(:account_id))
expect(status.active_mentions.pluck(:account_id)).to eq [bob.id] .to eq [bob.id]
end expect(status.mentions.pluck(:account_id))
.to contain_exactly(alice.id, bob.id)
it 'keeps old mentions as silent mentions' do
expect(status.mentions.pluck(:account_id)).to contain_exactly(alice.id, bob.id)
end end
end end
@ -168,11 +157,9 @@ RSpec.describe UpdateStatusService do
let!(:account) { Fabricate(:account) } let!(:account) { Fabricate(:account) }
let!(:status) { PostStatusService.new.call(account, text: 'Hello #foo') } let!(:status) { PostStatusService.new.call(account, text: 'Hello #foo') }
before do
subject.call(status, status.account_id, text: 'Hello #bar')
end
it 'changes tags' do it 'changes tags' do
subject.call(status, status.account_id, text: 'Hello #bar')
expect(status.tags.pluck(:name)).to eq %w(bar) expect(status.tags.pluck(:name)).to eq %w(bar)
end end
end end