diff options
author | Starfall <us@starfall.systems> | 2022-01-31 12:50:14 -0600 |
---|---|---|
committer | Starfall <us@starfall.systems> | 2022-01-31 12:50:14 -0600 |
commit | 17265f47f8f931e70699088dd8bd2a1c7b78112b (patch) | |
tree | a1dde2630cd8e481cc4c5d047c4af241a251def0 /spec/services | |
parent | 129962006c2ebcd195561ac556887dc87d32081c (diff) | |
parent | d6f3261c6cb810ea4eb6f74b9ee62af0d94cbd52 (diff) |
Merge branch 'glitchsoc'
Diffstat (limited to 'spec/services')
26 files changed, 687 insertions, 137 deletions
diff --git a/spec/services/account_statuses_cleanup_service_spec.rb b/spec/services/account_statuses_cleanup_service_spec.rb new file mode 100644 index 000000000..257655c41 --- /dev/null +++ b/spec/services/account_statuses_cleanup_service_spec.rb @@ -0,0 +1,101 @@ +require 'rails_helper' + +describe AccountStatusesCleanupService, type: :service do + let(:account) { Fabricate(:account, username: 'alice', domain: nil) } + let(:account_policy) { Fabricate(:account_statuses_cleanup_policy, account: account) } + let!(:unrelated_status) { Fabricate(:status, created_at: 3.years.ago) } + + describe '#call' do + context 'when the account has not posted anything' do + it 'returns 0 deleted toots' do + expect(subject.call(account_policy)).to eq 0 + end + end + + context 'when the account has posted several old statuses' do + let!(:very_old_status) { Fabricate(:status, created_at: 3.years.ago, account: account) } + let!(:old_status) { Fabricate(:status, created_at: 1.year.ago, account: account) } + let!(:another_old_status) { Fabricate(:status, created_at: 1.year.ago, account: account) } + let!(:recent_status) { Fabricate(:status, created_at: 1.day.ago, account: account) } + + context 'given a budget of 1' do + it 'reports 1 deleted toot' do + expect(subject.call(account_policy, 1)).to eq 1 + end + end + + context 'given a normal budget of 10' do + it 'reports 3 deleted statuses' do + expect(subject.call(account_policy, 10)).to eq 3 + end + + it 'records the last deleted id' do + subject.call(account_policy, 10) + expect(account_policy.last_inspected).to eq [old_status.id, another_old_status.id].max + end + + it 'actually deletes the statuses' do + subject.call(account_policy, 10) + expect(Status.find_by(id: [very_old_status.id, old_status.id, another_old_status.id])).to be_nil + end + end + + context 'when called repeatedly with a budget of 2' do + it 'reports 2 then 1 deleted statuses' do + expect(subject.call(account_policy, 2)).to eq 2 + expect(subject.call(account_policy, 2)).to eq 1 + end + + it 'actually deletes the statuses in the expected order' do + subject.call(account_policy, 2) + expect(Status.find_by(id: very_old_status.id)).to be_nil + subject.call(account_policy, 2) + expect(Status.find_by(id: [very_old_status.id, old_status.id, another_old_status.id])).to be_nil + end + end + + context 'when a self-faved toot is unfaved' do + let!(:self_faved) { Fabricate(:status, created_at: 6.months.ago, account: account) } + let!(:favourite) { Fabricate(:favourite, account: account, status: self_faved) } + + it 'deletes it once unfaved' do + expect(subject.call(account_policy, 20)).to eq 3 + expect(Status.find_by(id: self_faved.id)).to_not be_nil + expect(subject.call(account_policy, 20)).to eq 0 + favourite.destroy! + expect(subject.call(account_policy, 20)).to eq 1 + expect(Status.find_by(id: self_faved.id)).to be_nil + end + end + + context 'when there are more un-deletable old toots than the early search cutoff' do + before do + stub_const 'AccountStatusesCleanupPolicy::EARLY_SEARCH_CUTOFF', 5 + # Old statuses that should be cut-off + 10.times do + Fabricate(:status, created_at: 4.years.ago, visibility: :direct, account: account) + end + # New statuses that prevent cut-off id to reach the last status + 10.times do + Fabricate(:status, created_at: 4.seconds.ago, visibility: :direct, account: account) + end + end + + it 'reports 0 deleted statuses then 0 then 3 then 0 again' do + expect(subject.call(account_policy, 10)).to eq 0 + expect(subject.call(account_policy, 10)).to eq 0 + expect(subject.call(account_policy, 10)).to eq 3 + expect(subject.call(account_policy, 10)).to eq 0 + end + + it 'never causes the recorded id to get higher than oldest deletable toot' do + subject.call(account_policy, 10) + subject.call(account_policy, 10) + subject.call(account_policy, 10) + 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 diff --git a/spec/services/activitypub/fetch_remote_status_service_spec.rb b/spec/services/activitypub/fetch_remote_status_service_spec.rb index 1ecc46952..94574aa7f 100644 --- a/spec/services/activitypub/fetch_remote_status_service_spec.rb +++ b/spec/services/activitypub/fetch_remote_status_service_spec.rb @@ -67,7 +67,7 @@ RSpec.describe ActivityPub::FetchRemoteStatusService, type: :service do expect(status).to_not be_nil expect(status.url).to eq "https://#{valid_domain}/watch?v=12345" - expect(strip_tags(status.text)).to eq "Nyan Cat 10 hours remix https://#{valid_domain}/watch?v=12345" + expect(strip_tags(status.text)).to eq "Nyan Cat 10 hours remixhttps://#{valid_domain}/watch?v=12345" end end @@ -100,7 +100,7 @@ RSpec.describe ActivityPub::FetchRemoteStatusService, type: :service do expect(status).to_not be_nil expect(status.url).to eq "https://#{valid_domain}/watch?v=12345" - expect(strip_tags(status.text)).to eq "Nyan Cat 10 hours remix https://#{valid_domain}/watch?v=12345" + expect(strip_tags(status.text)).to eq "Nyan Cat 10 hours remixhttps://#{valid_domain}/watch?v=12345" end end @@ -120,7 +120,7 @@ RSpec.describe ActivityPub::FetchRemoteStatusService, type: :service do expect(status).to_not be_nil expect(status.url).to eq "https://#{valid_domain}/@foo/1234" - expect(strip_tags(status.text)).to eq "Let's change the world https://#{valid_domain}/@foo/1234" + expect(strip_tags(status.text)).to eq "Let's change the worldhttps://#{valid_domain}/@foo/1234" end end @@ -145,5 +145,46 @@ RSpec.describe ActivityPub::FetchRemoteStatusService, type: :service do expect(sender.statuses.first).to be_nil end end + + context 'with a valid Create activity' do + let(:object) do + { + '@context': 'https://www.w3.org/ns/activitystreams', + id: "https://#{valid_domain}/@foo/1234/create", + type: 'Create', + actor: ActivityPub::TagManager.instance.uri_for(sender), + object: note, + } + end + + it 'creates status' do + status = sender.statuses.first + + expect(status).to_not be_nil + expect(status.uri).to eq note[:id] + expect(status.text).to eq note[:content] + end + end + + context 'with a Create activity with a mismatching id' do + let(:object) do + { + '@context': 'https://www.w3.org/ns/activitystreams', + id: "https://#{valid_domain}/@foo/1234/create", + type: 'Create', + actor: ActivityPub::TagManager.instance.uri_for(sender), + object: { + id: "https://real.address/@foo/1234", + type: 'Note', + content: 'Lorem ipsum', + attributedTo: ActivityPub::TagManager.instance.uri_for(sender), + }, + } + end + + it 'does not create status' do + expect(sender.statuses.first).to be_nil + end + end end end diff --git a/spec/services/activitypub/process_account_service_spec.rb b/spec/services/activitypub/process_account_service_spec.rb index 56e7f8321..7728b9ba8 100644 --- a/spec/services/activitypub/process_account_service_spec.rb +++ b/spec/services/activitypub/process_account_service_spec.rb @@ -12,6 +12,7 @@ RSpec.describe ActivityPub::ProcessAccountService, type: :service do attachment: [ { type: 'PropertyValue', name: 'Pronouns', value: 'They/them' }, { type: 'PropertyValue', name: 'Occupation', value: 'Unit test' }, + { type: 'PropertyValue', name: 'non-string', value: ['foo', 'bar'] }, ], }.with_indifferent_access end @@ -29,51 +30,6 @@ RSpec.describe ActivityPub::ProcessAccountService, type: :service do end end - context 'identity proofs' do - let(:payload) do - { - id: 'https://foo.test', - type: 'Actor', - inbox: 'https://foo.test/inbox', - attachment: [ - { type: 'IdentityProof', name: 'Alice', signatureAlgorithm: 'keybase', signatureValue: 'a' * 66 }, - ], - }.with_indifferent_access - end - - it 'parses out of attachment' do - allow(ProofProvider::Keybase::Worker).to receive(:perform_async) - - account = subject.call('alice', 'example.com', payload) - - expect(account.identity_proofs.count).to eq 1 - - proof = account.identity_proofs.first - - expect(proof.provider).to eq 'keybase' - expect(proof.provider_username).to eq 'Alice' - expect(proof.token).to eq 'a' * 66 - end - - it 'removes no longer present proofs' do - allow(ProofProvider::Keybase::Worker).to receive(:perform_async) - - account = Fabricate(:account, username: 'alice', domain: 'example.com') - old_proof = Fabricate(:account_identity_proof, account: account, provider: 'keybase', provider_username: 'Bob', token: 'b' * 66) - - subject.call('alice', 'example.com', payload) - - expect(account.identity_proofs.count).to eq 1 - expect(account.identity_proofs.find_by(id: old_proof.id)).to be_nil - end - - it 'queues a validity check on the proof' do - allow(ProofProvider::Keybase::Worker).to receive(:perform_async) - account = subject.call('alice', 'example.com', payload) - expect(ProofProvider::Keybase::Worker).to have_received(:perform_async) - end - end - context 'when account is not suspended' do let!(:account) { Fabricate(:account, username: 'alice', domain: 'example.com') } diff --git a/spec/services/authorize_follow_service_spec.rb b/spec/services/authorize_follow_service_spec.rb index 8e5d8fb03..888d694b6 100644 --- a/spec/services/authorize_follow_service_spec.rb +++ b/spec/services/authorize_follow_service_spec.rb @@ -6,7 +6,7 @@ RSpec.describe AuthorizeFollowService, type: :service do subject { AuthorizeFollowService.new } describe 'local' do - let(:bob) { Fabricate(:user, email: 'bob@example.com', account: Fabricate(:account, username: 'bob')).account } + let(:bob) { Fabricate(:account, username: 'bob') } before do FollowRequest.create(account: bob, target_account: sender) @@ -23,7 +23,7 @@ RSpec.describe AuthorizeFollowService, type: :service do end describe 'remote ActivityPub' do - let(:bob) { Fabricate(:user, email: 'bob@example.com', account: Fabricate(:account, username: 'bob', domain: 'example.com', protocol: :activitypub, inbox_url: 'http://example.com/inbox')).account } + let(:bob) { Fabricate(:account, username: 'bob', domain: 'example.com', protocol: :activitypub, inbox_url: 'http://example.com/inbox') } before do FollowRequest.create(account: bob, target_account: sender) diff --git a/spec/services/batched_remove_status_service_spec.rb b/spec/services/batched_remove_status_service_spec.rb index 4203952c6..8f38908cd 100644 --- a/spec/services/batched_remove_status_service_spec.rb +++ b/spec/services/batched_remove_status_service_spec.rb @@ -5,7 +5,7 @@ RSpec.describe BatchedRemoveStatusService, type: :service do let!(:alice) { Fabricate(:account) } let!(:bob) { Fabricate(:account, username: 'bob', domain: 'example.com') } - let!(:jeff) { Fabricate(:user).account } + let!(:jeff) { Fabricate(:account) } let!(:hank) { Fabricate(:account, username: 'hank', protocol: :activitypub, domain: 'example.com', inbox_url: 'http://example.com/inbox') } let(:status1) { PostStatusService.new.call(alice, text: 'Hello @bob@example.com') } diff --git a/spec/services/block_service_spec.rb b/spec/services/block_service_spec.rb index 3714f09e9..a53e1f928 100644 --- a/spec/services/block_service_spec.rb +++ b/spec/services/block_service_spec.rb @@ -6,7 +6,7 @@ RSpec.describe BlockService, type: :service do subject { BlockService.new } describe 'local' do - let(:bob) { Fabricate(:user, email: 'bob@example.com', account: Fabricate(:account, username: 'bob')).account } + let(:bob) { Fabricate(:account, username: 'bob') } before do subject.call(sender, bob) @@ -18,7 +18,7 @@ RSpec.describe BlockService, type: :service do end describe 'remote ActivityPub' do - let(:bob) { Fabricate(:user, email: 'bob@example.com', account: Fabricate(:account, username: 'bob', protocol: :activitypub, domain: 'example.com', inbox_url: 'http://example.com/inbox')).account } + let(:bob) { Fabricate(:account, username: 'bob', protocol: :activitypub, domain: 'example.com', inbox_url: 'http://example.com/inbox') } before do stub_request(:post, 'http://example.com/inbox').to_return(status: 200) diff --git a/spec/services/bootstrap_timeline_service_spec.rb b/spec/services/bootstrap_timeline_service_spec.rb index 880ca4f0d..16f3e9962 100644 --- a/spec/services/bootstrap_timeline_service_spec.rb +++ b/spec/services/bootstrap_timeline_service_spec.rb @@ -1,4 +1,37 @@ require 'rails_helper' RSpec.describe BootstrapTimelineService, type: :service do + subject { BootstrapTimelineService.new } + + context 'when the new user has registered from an invite' do + let(:service) { double } + let(:autofollow) { false } + let(:inviter) { Fabricate(:user, confirmed_at: 2.days.ago) } + let(:invite) { Fabricate(:invite, user: inviter, max_uses: nil, expires_at: 1.hour.from_now, autofollow: autofollow) } + let(:new_user) { Fabricate(:user, invite_code: invite.code) } + + before do + allow(FollowService).to receive(:new).and_return(service) + allow(service).to receive(:call) + end + + context 'when the invite has auto-follow enabled' do + let(:autofollow) { true } + + it 'calls FollowService to follow the inviter' do + subject.call(new_user.account) + expect(service).to have_received(:call).with(new_user.account, inviter.account) + end + end + + context 'when the invite does not have auto-follow enable' do + let(:autofollow) { false } + + it 'calls FollowService to follow the inviter' do + subject.call(new_user.account) + expect(service).to_not have_received(:call) + end + end + + end end diff --git a/spec/services/delete_account_service_spec.rb b/spec/services/delete_account_service_spec.rb index cd7d32d59..b1da97036 100644 --- a/spec/services/delete_account_service_spec.rb +++ b/spec/services/delete_account_service_spec.rb @@ -21,6 +21,8 @@ RSpec.describe DeleteAccountService, type: :service do let!(:favourite_notification) { Fabricate(:notification, account: local_follower, activity: favourite, type: :favourite) } let!(:follow_notification) { Fabricate(:notification, account: local_follower, activity: active_relationship, type: :follow) } + let!(:account_note) { Fabricate(:account_note, account: account) } + subject do -> { described_class.new.call(account) } end @@ -35,8 +37,9 @@ RSpec.describe DeleteAccountService, type: :service do account.active_relationships, account.passive_relationships, account.polls, + account.account_notes, ].map(&:count) - }.from([2, 1, 1, 1, 1, 1, 1]).to([0, 0, 0, 0, 0, 0, 0]) + }.from([2, 1, 1, 1, 1, 1, 1, 1]).to([0, 0, 0, 0, 0, 0, 0, 0]) end it 'deletes associated target records' do diff --git a/spec/services/fan_out_on_write_service_spec.rb b/spec/services/fan_out_on_write_service_spec.rb index 538dc2592..aaf179ce5 100644 --- a/spec/services/fan_out_on_write_service_spec.rb +++ b/spec/services/fan_out_on_write_service_spec.rb @@ -1,37 +1,112 @@ require 'rails_helper' RSpec.describe FanOutOnWriteService, type: :service do - let(:author) { Fabricate(:account, username: 'tom') } - let(:status) { Fabricate(:status, text: 'Hello @alice #test', account: author) } - let(:alice) { Fabricate(:user, account: Fabricate(:account, username: 'alice')).account } - let(:follower) { Fabricate(:account, username: 'bob') } + let(:last_active_at) { Time.now.utc } - subject { FanOutOnWriteService.new } + let!(:alice) { Fabricate(:user, current_sign_in_at: last_active_at).account } + let!(:bob) { Fabricate(:user, current_sign_in_at: last_active_at, account_attributes: { username: 'bob' }).account } + let!(:tom) { Fabricate(:user, current_sign_in_at: last_active_at).account } + + subject { described_class.new } + + let(:status) { Fabricate(:status, account: alice, visibility: visibility, text: 'Hello @bob #hoge') } before do - alice - follower.follow!(author) + bob.follow!(alice) + tom.follow!(alice) ProcessMentionsService.new.call(status) ProcessHashtagsService.new.call(status) + allow(Redis.current).to receive(:publish) + subject.call(status) end - it 'delivers status to home timeline' do - expect(HomeFeed.new(author).get(10).map(&:id)).to include status.id + def home_feed_of(account) + HomeFeed.new(account).get(10).map(&:id) + end + + context 'when status is public' do + let(:visibility) { 'public' } + + it 'is added to the home feed of its author' do + expect(home_feed_of(alice)).to include status.id + end + + it 'is added to the home feed of a follower' do + expect(home_feed_of(bob)).to include status.id + expect(home_feed_of(tom)).to include status.id + end + + it 'is broadcast to the hashtag stream' do + expect(Redis.current).to have_received(:publish).with('timeline:hashtag:hoge', anything) + expect(Redis.current).to have_received(:publish).with('timeline:hashtag:hoge:local', anything) + end + + it 'is broadcast to the public stream' do + expect(Redis.current).to have_received(:publish).with('timeline:public', anything) + expect(Redis.current).to have_received(:publish).with('timeline:public:local', anything) + end end - it 'delivers status to local followers' do - pending 'some sort of problem in test environment causes this to sometimes fail' - expect(HomeFeed.new(follower).get(10).map(&:id)).to include status.id + context 'when status is limited' do + let(:visibility) { 'limited' } + + it 'is added to the home feed of its author' do + expect(home_feed_of(alice)).to include status.id + end + + it 'is added to the home feed of the mentioned follower' do + expect(home_feed_of(bob)).to include status.id + end + + it 'is not added to the home feed of the other follower' do + expect(home_feed_of(tom)).to_not include status.id + end + + it 'is not broadcast publicly' do + expect(Redis.current).to_not have_received(:publish).with('timeline:hashtag:hoge', anything) + expect(Redis.current).to_not have_received(:publish).with('timeline:public', anything) + end end - it 'delivers status to hashtag' do - expect(TagFeed.new(Tag.find_by(name: 'test'), alice).get(20).map(&:id)).to include status.id + context 'when status is private' do + let(:visibility) { 'private' } + + it 'is added to the home feed of its author' do + expect(home_feed_of(alice)).to include status.id + end + + it 'is added to the home feed of a follower' do + expect(home_feed_of(bob)).to include status.id + expect(home_feed_of(tom)).to include status.id + end + + it 'is not broadcast publicly' do + expect(Redis.current).to_not have_received(:publish).with('timeline:hashtag:hoge', anything) + expect(Redis.current).to_not have_received(:publish).with('timeline:public', anything) + end end - it 'delivers status to public timeline' do - expect(PublicFeed.new(alice).get(20).map(&:id)).to include status.id + context 'when status is direct' do + let(:visibility) { 'direct' } + + it 'is added to the home feed of its author' do + expect(home_feed_of(alice)).to include status.id + end + + it 'is added to the home feed of the mentioned follower' do + expect(home_feed_of(bob)).to include status.id + end + + it 'is not added to the home feed of the other follower' do + expect(home_feed_of(tom)).to_not include status.id + end + + it 'is not broadcast publicly' do + expect(Redis.current).to_not have_received(:publish).with('timeline:hashtag:hoge', anything) + expect(Redis.current).to_not have_received(:publish).with('timeline:public', anything) + end end end diff --git a/spec/services/favourite_service_spec.rb b/spec/services/favourite_service_spec.rb index fc7f58eb4..94a8111dd 100644 --- a/spec/services/favourite_service_spec.rb +++ b/spec/services/favourite_service_spec.rb @@ -6,7 +6,7 @@ RSpec.describe FavouriteService, type: :service do subject { FavouriteService.new } describe 'local' do - let(:bob) { Fabricate(:user, email: 'bob@example.com', account: Fabricate(:account, username: 'bob')).account } + let(:bob) { Fabricate(:account) } let(:status) { Fabricate(:status, account: bob) } before do @@ -19,7 +19,7 @@ RSpec.describe FavouriteService, type: :service do end describe 'remote ActivityPub' do - let(:bob) { Fabricate(:user, email: 'bob@example.com', account: Fabricate(:account, protocol: :activitypub, username: 'bob', domain: 'example.com', inbox_url: 'http://example.com/inbox')).account } + let(:bob) { Fabricate(:account, protocol: :activitypub, username: 'bob', domain: 'example.com', inbox_url: 'http://example.com/inbox') } let(:status) { Fabricate(:status, account: bob) } before do diff --git a/spec/services/fetch_link_card_service_spec.rb b/spec/services/fetch_link_card_service_spec.rb index 736a6078d..4914c2753 100644 --- a/spec/services/fetch_link_card_service_spec.rb +++ b/spec/services/fetch_link_card_service_spec.rb @@ -1,7 +1,7 @@ require 'rails_helper' RSpec.describe FetchLinkCardService, type: :service do - subject { FetchLinkCardService.new } + subject { described_class.new } before do stub_request(:get, 'http://example.xn--fiqs8s/').to_return(request_fixture('idn.txt')) diff --git a/spec/services/fetch_oembed_service_spec.rb b/spec/services/fetch_oembed_service_spec.rb index a4262b040..88f0113ed 100644 --- a/spec/services/fetch_oembed_service_spec.rb +++ b/spec/services/fetch_oembed_service_spec.rb @@ -13,6 +13,32 @@ describe FetchOEmbedService, type: :service do describe 'discover_provider' do context 'when status code is 200 and MIME type is text/html' do + context 'when OEmbed endpoint contains URL as parameter' do + before do + stub_request(:get, 'https://www.youtube.com/watch?v=IPSbNdBmWKE').to_return( + status: 200, + headers: { 'Content-Type': 'text/html' }, + body: request_fixture('oembed_youtube.html'), + ) + stub_request(:get, 'https://www.youtube.com/oembed?format=json&url=https%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3DIPSbNdBmWKE').to_return( + status: 200, + headers: { 'Content-Type': 'text/html' }, + body: request_fixture('oembed_json_empty.html') + ) + end + + it 'returns new OEmbed::Provider for JSON provider' do + subject.call('https://www.youtube.com/watch?v=IPSbNdBmWKE') + expect(subject.endpoint_url).to eq 'https://www.youtube.com/oembed?format=json&url=https%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3DIPSbNdBmWKE' + expect(subject.format).to eq :json + end + + it 'stores URL template' do + subject.call('https://www.youtube.com/watch?v=IPSbNdBmWKE') + expect(Rails.cache.read('oembed_endpoint:www.youtube.com')[:endpoint]).to eq 'https://www.youtube.com/oembed?format=json&url={url}' + end + end + context 'Both of JSON and XML provider are discoverable' do before do stub_request(:get, 'https://host.test/oembed.html').to_return( @@ -33,6 +59,11 @@ describe FetchOEmbedService, type: :service do expect(subject.endpoint_url).to eq 'https://host.test/provider.xml' expect(subject.format).to eq :xml end + + it 'does not cache OEmbed endpoint' do + subject.call('https://host.test/oembed.html', format: :xml) + expect(Rails.cache.exist?('oembed_endpoint:host.test')).to eq false + end end context 'JSON provider is discoverable while XML provider is not' do @@ -49,6 +80,11 @@ describe FetchOEmbedService, type: :service do expect(subject.endpoint_url).to eq 'https://host.test/provider.json' expect(subject.format).to eq :json end + + it 'does not cache OEmbed endpoint' do + subject.call('https://host.test/oembed.html') + expect(Rails.cache.exist?('oembed_endpoint:host.test')).to eq false + end end context 'XML provider is discoverable while JSON provider is not' do @@ -65,6 +101,11 @@ describe FetchOEmbedService, type: :service do expect(subject.endpoint_url).to eq 'https://host.test/provider.xml' expect(subject.format).to eq :xml end + + it 'does not cache OEmbed endpoint' do + subject.call('https://host.test/oembed.html') + expect(Rails.cache.exist?('oembed_endpoint:host.test')).to eq false + end end context 'Invalid XML provider is discoverable while JSON provider is not' do diff --git a/spec/services/follow_service_spec.rb b/spec/services/follow_service_spec.rb index 63d6eb3bd..02bc87c58 100644 --- a/spec/services/follow_service_spec.rb +++ b/spec/services/follow_service_spec.rb @@ -7,7 +7,7 @@ RSpec.describe FollowService, type: :service do context 'local account' do describe 'locked account' do - let(:bob) { Fabricate(:user, email: 'bob@example.com', account: Fabricate(:account, locked: true, username: 'bob')).account } + let(:bob) { Fabricate(:account, locked: true, username: 'bob') } before do subject.call(sender, bob) @@ -19,7 +19,7 @@ RSpec.describe FollowService, type: :service do end describe 'locked account, no reblogs' do - let(:bob) { Fabricate(:user, email: 'bob@example.com', account: Fabricate(:account, locked: true, username: 'bob')).account } + let(:bob) { Fabricate(:account, locked: true, username: 'bob') } before do subject.call(sender, bob, reblogs: false) @@ -31,7 +31,7 @@ RSpec.describe FollowService, type: :service do end describe 'unlocked account, from silenced account' do - let(:bob) { Fabricate(:user, email: 'bob@example.com', account: Fabricate(:account, username: 'bob')).account } + let(:bob) { Fabricate(:account, username: 'bob') } before do sender.touch(:silenced_at) @@ -44,7 +44,7 @@ RSpec.describe FollowService, type: :service do end describe 'unlocked account, from a muted account' do - let(:bob) { Fabricate(:user, email: 'bob@example.com', account: Fabricate(:account, username: 'bob')).account } + let(:bob) { Fabricate(:account, username: 'bob') } before do bob.mute!(sender) @@ -58,7 +58,7 @@ RSpec.describe FollowService, type: :service do end describe 'unlocked account' do - let(:bob) { Fabricate(:user, email: 'bob@example.com', account: Fabricate(:account, username: 'bob')).account } + let(:bob) { Fabricate(:account, username: 'bob') } before do subject.call(sender, bob) @@ -71,7 +71,7 @@ RSpec.describe FollowService, type: :service do end describe 'unlocked account, no reblogs' do - let(:bob) { Fabricate(:user, email: 'bob@example.com', account: Fabricate(:account, username: 'bob')).account } + let(:bob) { Fabricate(:account, username: 'bob') } before do subject.call(sender, bob, reblogs: false) @@ -84,7 +84,7 @@ RSpec.describe FollowService, type: :service do end describe 'already followed account' do - let(:bob) { Fabricate(:user, email: 'bob@example.com', account: Fabricate(:account, username: 'bob')).account } + let(:bob) { Fabricate(:account, username: 'bob') } before do sender.follow!(bob) @@ -97,7 +97,7 @@ RSpec.describe FollowService, type: :service do end describe 'already followed account, turning reblogs off' do - let(:bob) { Fabricate(:user, email: 'bob@example.com', account: Fabricate(:account, username: 'bob')).account } + let(:bob) { Fabricate(:account, username: 'bob') } before do sender.follow!(bob, reblogs: true) @@ -110,7 +110,7 @@ RSpec.describe FollowService, type: :service do end describe 'already followed account, turning reblogs on' do - let(:bob) { Fabricate(:user, email: 'bob@example.com', account: Fabricate(:account, username: 'bob')).account } + let(:bob) { Fabricate(:account, username: 'bob') } before do sender.follow!(bob, reblogs: false) @@ -124,7 +124,7 @@ RSpec.describe FollowService, type: :service do end context 'remote ActivityPub account' do - let(:bob) { Fabricate(:user, account: Fabricate(:account, username: 'bob', domain: 'example.com', protocol: :activitypub, inbox_url: 'http://example.com/inbox')).account } + let(:bob) { Fabricate(:account, username: 'bob', domain: 'example.com', protocol: :activitypub, inbox_url: 'http://example.com/inbox') } before do stub_request(:post, "http://example.com/inbox").to_return(:status => 200, :body => "", :headers => {}) diff --git a/spec/services/notify_service_spec.rb b/spec/services/notify_service_spec.rb index 118436f8b..83e62ff36 100644 --- a/spec/services/notify_service_spec.rb +++ b/spec/services/notify_service_spec.rb @@ -64,8 +64,9 @@ RSpec.describe NotifyService, type: :service do is_expected.to_not change(Notification, :count) end - context 'if the message chain initiated by recipient, but is not direct message' do + context 'if the message chain is initiated by recipient, but is not direct message' do let(:reply_to) { Fabricate(:status, account: recipient) } + let!(:mention) { Fabricate(:mention, account: sender, status: reply_to) } let(:activity) { Fabricate(:mention, account: recipient, status: Fabricate(:status, account: sender, visibility: :direct, thread: reply_to)) } it 'does not notify' do @@ -73,8 +74,20 @@ RSpec.describe NotifyService, type: :service do end end - context 'if the message chain initiated by recipient and is direct message' do + context 'if the message chain is initiated by recipient, but without a mention to the sender, even if the sender sends multiple messages in a row' do + let(:reply_to) { Fabricate(:status, account: recipient) } + let!(:mention) { Fabricate(:mention, account: sender, status: reply_to) } + let(:dummy_reply) { Fabricate(:status, account: sender, visibility: :direct, thread: reply_to) } + let(:activity) { Fabricate(:mention, account: recipient, status: Fabricate(:status, account: sender, visibility: :direct, thread: dummy_reply)) } + + it 'does not notify' do + is_expected.to_not change(Notification, :count) + end + end + + context 'if the message chain is initiated by the recipient with a mention to the sender' do let(:reply_to) { Fabricate(:status, account: recipient, visibility: :direct) } + let!(:mention) { Fabricate(:mention, account: sender, status: reply_to) } let(:activity) { Fabricate(:mention, account: recipient, status: Fabricate(:status, account: sender, visibility: :direct, thread: reply_to)) } it 'does notify' do diff --git a/spec/services/post_status_service_spec.rb b/spec/services/post_status_service_spec.rb index 147a59fc3..d21270c79 100644 --- a/spec/services/post_status_service_spec.rb +++ b/spec/services/post_status_service_spec.rb @@ -25,29 +25,33 @@ RSpec.describe PostStatusService, type: :service do expect(status.thread).to eq in_reply_to_status end - it 'schedules a status' do - account = Fabricate(:account) - future = Time.now.utc + 2.hours - - status = subject.call(account, text: 'Hi future!', scheduled_at: future) - - expect(status).to be_a ScheduledStatus - expect(status.scheduled_at).to eq future - expect(status.params['text']).to eq 'Hi future!' - end - - it 'does not immediately create a status when scheduling a status' do - account = Fabricate(:account) - media = Fabricate(:media_attachment) - future = Time.now.utc + 2.hours - - status = subject.call(account, text: 'Hi future!', media_ids: [media.id], scheduled_at: future) - - expect(status).to be_a ScheduledStatus - expect(status.scheduled_at).to eq future - expect(status.params['text']).to eq 'Hi future!' - expect(media.reload.status).to be_nil - expect(Status.where(text: 'Hi future!').exists?).to be_falsey + context 'when scheduling a status' do + let!(:account) { Fabricate(:account) } + let!(:future) { Time.now.utc + 2.hours } + let!(:previous_status) { Fabricate(:status, account: account) } + + it 'schedules a status' do + status = subject.call(account, text: 'Hi future!', scheduled_at: future) + expect(status).to be_a ScheduledStatus + expect(status.scheduled_at).to eq future + expect(status.params['text']).to eq 'Hi future!' + end + + it 'does not immediately create a status' do + media = Fabricate(:media_attachment, account: account) + status = subject.call(account, text: 'Hi future!', media_ids: [media.id], scheduled_at: future) + + expect(status).to be_a ScheduledStatus + expect(status.scheduled_at).to eq future + expect(status.params['text']).to eq 'Hi future!' + expect(status.params['media_ids']).to eq [media.id] + expect(media.reload.status).to be_nil + expect(Status.where(text: 'Hi future!').exists?).to be_falsey + end + + it 'does not change statuses count' do + expect { subject.call(account, text: 'Hi future!', scheduled_at: future, thread: previous_status) }.not_to change { [account.statuses_count, previous_status.replies_count] } + end end it 'creates response to the original status of boost' do diff --git a/spec/services/process_mentions_service_spec.rb b/spec/services/process_mentions_service_spec.rb index 3b2f9d698..89b265e9a 100644 --- a/spec/services/process_mentions_service_spec.rb +++ b/spec/services/process_mentions_service_spec.rb @@ -9,57 +9,55 @@ RSpec.describe ProcessMentionsService, type: :service do context 'ActivityPub' do context 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 - stub_request(:post, remote_user.inbox_url) subject.call(status) end it 'creates a mention' do expect(remote_user.mentions.where(status: status).count).to eq 1 end - - it 'sends activity to the inbox' do - expect(a_request(:post, remote_user.inbox_url)).to have_been_made.once - end end context 'with an IDN domain' do - 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!(: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") } before do - stub_request(:post, remote_user.inbox_url) subject.call(status) end it 'creates a mention' do expect(remote_user.mentions.where(status: status).count).to eq 1 end + end + + context 'with an IDN TLD' do + 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@հայ.հայ") } + + before do + subject.call(status) + end - it 'sends activity to the inbox' do - expect(a_request(:post, remote_user.inbox_url)).to have_been_made.once + it 'creates a mention' do + expect(remote_user.mentions.where(status: status).count).to eq 1 end end end context 'Temporarily-unreachable ActivityPub user' do - let(:remote_user) { Fabricate(:account, username: 'remote_user', protocol: :activitypub, domain: 'example.com', inbox_url: 'http://example.com/inbox', last_webfingered_at: nil) } + let!(:remote_user) { Fabricate(:account, username: 'remote_user', protocol: :activitypub, domain: 'example.com', inbox_url: 'http://example.com/inbox', last_webfingered_at: nil) } before do 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(:post, remote_user.inbox_url) subject.call(status) end it 'creates a mention' do expect(remote_user.mentions.where(status: status).count).to eq 1 end - - it 'sends activity to the inbox' do - expect(a_request(:post, remote_user.inbox_url)).to have_been_made.once - end end end diff --git a/spec/services/purge_domain_service_spec.rb b/spec/services/purge_domain_service_spec.rb new file mode 100644 index 000000000..59285f126 --- /dev/null +++ b/spec/services/purge_domain_service_spec.rb @@ -0,0 +1,27 @@ +require 'rails_helper' + +RSpec.describe PurgeDomainService, type: :service do + let!(:old_account) { Fabricate(:account, domain: 'obsolete.org') } + let!(:old_status1) { Fabricate(:status, account: old_account) } + let!(:old_status2) { Fabricate(:status, account: old_account) } + let!(:old_attachment) { Fabricate(:media_attachment, account: old_account, status: old_status2, file: attachment_fixture('attachment.jpg')) } + + subject { PurgeDomainService.new } + + describe 'for a suspension' do + before do + subject.call('obsolete.org') + end + + it 'removes the remote accounts\'s statuses and media attachments' do + expect { old_account.reload }.to raise_exception ActiveRecord::RecordNotFound + expect { old_status1.reload }.to raise_exception ActiveRecord::RecordNotFound + expect { old_status2.reload }.to raise_exception ActiveRecord::RecordNotFound + expect { old_attachment.reload }.to raise_exception ActiveRecord::RecordNotFound + end + + it 'refreshes instances view' do + expect(Instance.where(domain: 'obsolete.org').exists?).to be false + end + end +end diff --git a/spec/services/reject_follow_service_spec.rb b/spec/services/reject_follow_service_spec.rb index 732cb07f7..e14bfa78d 100644 --- a/spec/services/reject_follow_service_spec.rb +++ b/spec/services/reject_follow_service_spec.rb @@ -6,7 +6,7 @@ RSpec.describe RejectFollowService, type: :service do subject { RejectFollowService.new } describe 'local' do - let(:bob) { Fabricate(:user, email: 'bob@example.com', account: Fabricate(:account, username: 'bob')).account } + let(:bob) { Fabricate(:account) } before do FollowRequest.create(account: bob, target_account: sender) @@ -23,7 +23,7 @@ RSpec.describe RejectFollowService, type: :service do end describe 'remote ActivityPub' do - let(:bob) { Fabricate(:user, email: 'bob@example.com', account: Fabricate(:account, username: 'bob', domain: 'example.com', protocol: :activitypub, inbox_url: 'http://example.com/inbox')).account } + let(:bob) { Fabricate(:account, username: 'bob', domain: 'example.com', protocol: :activitypub, inbox_url: 'http://example.com/inbox') } before do FollowRequest.create(account: bob, target_account: sender) diff --git a/spec/services/remove_from_follwers_service_spec.rb b/spec/services/remove_from_follwers_service_spec.rb new file mode 100644 index 000000000..a83f6f49a --- /dev/null +++ b/spec/services/remove_from_follwers_service_spec.rb @@ -0,0 +1,38 @@ +require 'rails_helper' + +RSpec.describe RemoveFromFollowersService, type: :service do + let(:bob) { Fabricate(:account, username: 'bob') } + + subject { RemoveFromFollowersService.new } + + describe 'local' do + let(:sender) { Fabricate(:account, username: 'alice') } + + before do + Follow.create(account: sender, target_account: bob) + subject.call(bob, sender) + end + + it 'does not create follow relation' do + expect(bob.followed_by?(sender)).to be false + end + end + + describe 'remote ActivityPub' do + let(:sender) { Fabricate(:account, username: 'alice', domain: 'example.com', protocol: :activitypub, inbox_url: 'http://example.com/inbox') } + + before do + Follow.create(account: sender, target_account: bob) + stub_request(:post, sender.inbox_url).to_return(status: 200) + subject.call(bob, sender) + end + + it 'does not create follow relation' do + expect(bob.followed_by?(sender)).to be false + end + + it 'sends a reject activity' do + expect(a_request(:post, sender.inbox_url)).to have_been_made.once + end + end +end diff --git a/spec/services/remove_status_service_spec.rb b/spec/services/remove_status_service_spec.rb index 21fb0cd35..fb7c6b462 100644 --- a/spec/services/remove_status_service_spec.rb +++ b/spec/services/remove_status_service_spec.rb @@ -3,7 +3,7 @@ require 'rails_helper' RSpec.describe RemoveStatusService, type: :service do subject { RemoveStatusService.new } - let!(:alice) { Fabricate(:account, user: Fabricate(:user)) } + let!(:alice) { Fabricate(:account) } let!(:bob) { Fabricate(:account, username: 'bob', domain: 'example.com') } let!(:jeff) { Fabricate(:account) } let!(:hank) { Fabricate(:account, username: 'hank', protocol: :activitypub, domain: 'example.com', inbox_url: 'http://example.com/inbox') } diff --git a/spec/services/report_service_spec.rb b/spec/services/report_service_spec.rb index 454e4d896..7e6a113e0 100644 --- a/spec/services/report_service_spec.rb +++ b/spec/services/report_service_spec.rb @@ -3,7 +3,7 @@ require 'rails_helper' RSpec.describe ReportService, type: :service do subject { described_class.new } - let(:source_account) { Fabricate(:user).account } + let(:source_account) { Fabricate(:account) } context 'for a remote account' do let(:remote_account) { Fabricate(:account, domain: 'example.com', protocol: :activitypub, inbox_url: 'http://example.com/inbox') } diff --git a/spec/services/suspend_account_service_spec.rb b/spec/services/suspend_account_service_spec.rb new file mode 100644 index 000000000..cf7eb257a --- /dev/null +++ b/spec/services/suspend_account_service_spec.rb @@ -0,0 +1,85 @@ +require 'rails_helper' + +RSpec.describe SuspendAccountService, type: :service do + shared_examples 'common behavior' do + let!(:local_follower) { Fabricate(:user, current_sign_in_at: 1.hour.ago).account } + let!(:list) { Fabricate(:list, account: local_follower) } + + subject do + -> { described_class.new.call(account) } + end + + before do + allow(FeedManager.instance).to receive(:unmerge_from_home).and_return(nil) + allow(FeedManager.instance).to receive(:unmerge_from_list).and_return(nil) + + local_follower.follow!(account) + list.accounts << account + end + + it "unmerges from local followers' feeds" do + subject.call + expect(FeedManager.instance).to have_received(:unmerge_from_home).with(account, local_follower) + expect(FeedManager.instance).to have_received(:unmerge_from_list).with(account, list) + end + + it 'marks account as suspended' do + is_expected.to change { account.suspended? }.from(false).to(true) + end + end + + describe 'suspending a local account' do + def match_update_actor_request(req, account) + json = JSON.parse(req.body) + actor_id = ActivityPub::TagManager.instance.uri_for(account) + json['type'] == 'Update' && json['actor'] == actor_id && json['object']['id'] == actor_id && json['object']['suspended'] + end + + before do + stub_request(:post, 'https://alice.com/inbox').to_return(status: 201) + stub_request(:post, 'https://bob.com/inbox').to_return(status: 201) + end + + include_examples 'common behavior' do + let!(:account) { Fabricate(:account) } + let!(:remote_follower) { Fabricate(:account, uri: 'https://alice.com', inbox_url: 'https://alice.com/inbox', protocol: :activitypub) } + let!(:remote_reporter) { Fabricate(:account, uri: 'https://bob.com', inbox_url: 'https://bob.com/inbox', protocol: :activitypub) } + let!(:report) { Fabricate(:report, account: remote_reporter, target_account: account) } + + before do + remote_follower.follow!(account) + end + + it 'sends an update actor to followers and reporters' do + subject.call + expect(a_request(:post, remote_follower.inbox_url).with { |req| match_update_actor_request(req, account) }).to have_been_made.once + expect(a_request(:post, remote_reporter.inbox_url).with { |req| match_update_actor_request(req, account) }).to have_been_made.once + end + end + end + + describe 'suspending a remote account' do + def match_reject_follow_request(req, account, followee) + json = JSON.parse(req.body) + json['type'] == 'Reject' && json['actor'] == ActivityPub::TagManager.instance.uri_for(followee) && json['object']['actor'] == account.uri + end + + before do + stub_request(:post, 'https://bob.com/inbox').to_return(status: 201) + end + + include_examples 'common behavior' do + let!(:account) { Fabricate(:account, domain: 'bob.com', uri: 'https://bob.com', inbox_url: 'https://bob.com/inbox', protocol: :activitypub) } + let!(:local_followee) { Fabricate(:account) } + + before do + account.follow!(local_followee) + end + + it 'sends a reject follow' do + subject.call + expect(a_request(:post, account.inbox_url).with { |req| match_reject_follow_request(req, account, local_followee) }).to have_been_made.once + end + end + end +end diff --git a/spec/services/unblock_service_spec.rb b/spec/services/unblock_service_spec.rb index c43ab24b0..10448b340 100644 --- a/spec/services/unblock_service_spec.rb +++ b/spec/services/unblock_service_spec.rb @@ -6,7 +6,7 @@ RSpec.describe UnblockService, type: :service do subject { UnblockService.new } describe 'local' do - let(:bob) { Fabricate(:user, email: 'bob@example.com', account: Fabricate(:account, username: 'bob')).account } + let(:bob) { Fabricate(:account) } before do sender.block!(bob) @@ -19,7 +19,7 @@ RSpec.describe UnblockService, type: :service do end describe 'remote ActivityPub' do - let(:bob) { Fabricate(:user, email: 'bob@example.com', account: Fabricate(:account, username: 'bob', protocol: :activitypub, domain: 'example.com', inbox_url: 'http://example.com/inbox')).account } + let(:bob) { Fabricate(:account, username: 'bob', protocol: :activitypub, domain: 'example.com', inbox_url: 'http://example.com/inbox') } before do sender.block!(bob) diff --git a/spec/services/unfollow_service_spec.rb b/spec/services/unfollow_service_spec.rb index 7f0b575e4..bb5bef5c9 100644 --- a/spec/services/unfollow_service_spec.rb +++ b/spec/services/unfollow_service_spec.rb @@ -6,7 +6,7 @@ RSpec.describe UnfollowService, type: :service do subject { UnfollowService.new } describe 'local' do - let(:bob) { Fabricate(:user, email: 'bob@example.com', account: Fabricate(:account, username: 'bob')).account } + let(:bob) { Fabricate(:account, username: 'bob') } before do sender.follow!(bob) @@ -19,7 +19,7 @@ RSpec.describe UnfollowService, type: :service do end describe 'remote ActivityPub' do - let(:bob) { Fabricate(:user, email: 'bob@example.com', account: Fabricate(:account, username: 'bob', protocol: :activitypub, domain: 'example.com', inbox_url: 'http://example.com/inbox')).account } + let(:bob) { Fabricate(:account, username: 'bob', protocol: :activitypub, domain: 'example.com', inbox_url: 'http://example.com/inbox') } before do sender.follow!(bob) @@ -37,7 +37,7 @@ RSpec.describe UnfollowService, type: :service do end describe 'remote ActivityPub (reverse)' do - let(:bob) { Fabricate(:user, email: 'bob@example.com', account: Fabricate(:account, username: 'bob', protocol: :activitypub, domain: 'example.com', inbox_url: 'http://example.com/inbox')).account } + let(:bob) { Fabricate(:account, username: 'bob', protocol: :activitypub, domain: 'example.com', inbox_url: 'http://example.com/inbox') } before do bob.follow!(sender) diff --git a/spec/services/unsuspend_account_service_spec.rb b/spec/services/unsuspend_account_service_spec.rb new file mode 100644 index 000000000..d52cb6cc0 --- /dev/null +++ b/spec/services/unsuspend_account_service_spec.rb @@ -0,0 +1,135 @@ +require 'rails_helper' + +RSpec.describe UnsuspendAccountService, type: :service do + shared_examples 'common behavior' do + let!(:local_follower) { Fabricate(:user, current_sign_in_at: 1.hour.ago).account } + let!(:list) { Fabricate(:list, account: local_follower) } + + subject do + -> { described_class.new.call(account) } + end + + before do + allow(FeedManager.instance).to receive(:merge_into_home).and_return(nil) + allow(FeedManager.instance).to receive(:merge_into_list).and_return(nil) + + local_follower.follow!(account) + list.accounts << account + + account.suspend!(origin: :local) + end + end + + describe 'unsuspending a local account' do + def match_update_actor_request(req, account) + json = JSON.parse(req.body) + actor_id = ActivityPub::TagManager.instance.uri_for(account) + json['type'] == 'Update' && json['actor'] == actor_id && json['object']['id'] == actor_id && !json['object']['suspended'] + end + + before do + stub_request(:post, 'https://alice.com/inbox').to_return(status: 201) + stub_request(:post, 'https://bob.com/inbox').to_return(status: 201) + end + + it 'marks account as unsuspended' do + is_expected.to change { account.suspended? }.from(true).to(false) + end + + include_examples 'common behavior' do + let!(:account) { Fabricate(:account) } + let!(:remote_follower) { Fabricate(:account, uri: 'https://alice.com', inbox_url: 'https://alice.com/inbox', protocol: :activitypub) } + let!(:remote_reporter) { Fabricate(:account, uri: 'https://bob.com', inbox_url: 'https://bob.com/inbox', protocol: :activitypub) } + let!(:report) { Fabricate(:report, account: remote_reporter, target_account: account) } + + before do + remote_follower.follow!(account) + end + + it "merges back into local followers' feeds" do + subject.call + expect(FeedManager.instance).to have_received(:merge_into_home).with(account, local_follower) + expect(FeedManager.instance).to have_received(:merge_into_list).with(account, list) + end + + it 'sends an update actor to followers and reporters' do + subject.call + expect(a_request(:post, remote_follower.inbox_url).with { |req| match_update_actor_request(req, account) }).to have_been_made.once + expect(a_request(:post, remote_reporter.inbox_url).with { |req| match_update_actor_request(req, account) }).to have_been_made.once + end + end + end + + describe 'unsuspending a remote account' do + include_examples 'common behavior' do + let!(:account) { Fabricate(:account, domain: 'bob.com', uri: 'https://bob.com', inbox_url: 'https://bob.com/inbox', protocol: :activitypub) } + let!(:reslove_account_service) { double } + + before do + allow(ResolveAccountService).to receive(:new).and_return(reslove_account_service) + end + + context 'when the account is not remotely suspended' do + before do + allow(reslove_account_service).to receive(:call).with(account).and_return(account) + end + + it 're-fetches the account' do + subject.call + expect(reslove_account_service).to have_received(:call).with(account) + end + + it "merges back into local followers' feeds" do + subject.call + expect(FeedManager.instance).to have_received(:merge_into_home).with(account, local_follower) + expect(FeedManager.instance).to have_received(:merge_into_list).with(account, list) + end + + it 'marks account as unsuspended' do + is_expected.to change { account.suspended? }.from(true).to(false) + end + end + + context 'when the account is remotely suspended' do + before do + allow(reslove_account_service).to receive(:call).with(account) do |account| + account.suspend!(origin: :remote) + account + end + end + + it 're-fetches the account' do + subject.call + expect(reslove_account_service).to have_received(:call).with(account) + end + + it "does not merge back into local followers' feeds" do + subject.call + expect(FeedManager.instance).to_not have_received(:merge_into_home).with(account, local_follower) + expect(FeedManager.instance).to_not have_received(:merge_into_list).with(account, list) + end + + it 'does not mark the account as unsuspended' do + is_expected.not_to change { account.suspended? } + end + end + + context 'when the account is remotely deleted' do + before do + allow(reslove_account_service).to receive(:call).with(account).and_return(nil) + end + + it 're-fetches the account' do + subject.call + expect(reslove_account_service).to have_received(:call).with(account) + end + + it "does not merge back into local followers' feeds" do + subject.call + expect(FeedManager.instance).to_not have_received(:merge_into_home).with(account, local_follower) + expect(FeedManager.instance).to_not have_received(:merge_into_list).with(account, list) + end + end + end + end +end diff --git a/spec/services/update_account_service_spec.rb b/spec/services/update_account_service_spec.rb index 960b26891..c2dc791e4 100644 --- a/spec/services/update_account_service_spec.rb +++ b/spec/services/update_account_service_spec.rb @@ -5,9 +5,9 @@ RSpec.describe UpdateAccountService, type: :service do describe 'switching form locked to unlocked accounts' do let(:account) { Fabricate(:account, locked: true) } - let(:alice) { Fabricate(:user, email: 'alice@example.com', account: Fabricate(:account, username: 'alice')).account } - let(:bob) { Fabricate(:user, email: 'bob@example.com', account: Fabricate(:account, username: 'bob')).account } - let(:eve) { Fabricate(:user, email: 'eve@example.com', account: Fabricate(:account, username: 'eve')).account } + let(:alice) { Fabricate(:account) } + let(:bob) { Fabricate(:account) } + let(:eve) { Fabricate(:account) } before do bob.touch(:silenced_at) |