# frozen_string_literal: true require 'rails_helper' RSpec.describe ActivityPub::Activity::Follow do let(:actor_type) { 'Person' } let(:display_name) { '' } let(:sender) { Fabricate(:account, domain: 'example.com', inbox_url: 'https://example.com/inbox', actor_type: actor_type, display_name: display_name) } let(:recipient) { Fabricate(:account) } let(:json) do { '@context': 'https://www.w3.org/ns/activitystreams', id: 'foo', type: 'Follow', actor: ActivityPub::TagManager.instance.uri_for(sender), object: ActivityPub::TagManager.instance.uri_for(recipient), }.with_indifferent_access end describe '#perform' do subject { described_class.new(json, sender) } context 'with no prior follow' do context 'with an unlocked account' do before do subject.perform end it 'creates a follow from sender to recipient' do expect(sender.following?(recipient)).to be true expect(sender.active_relationships.find_by(target_account: recipient).uri).to eq 'foo' end it 'does not create a follow request' do expect(sender.requested?(recipient)).to be false end end context 'with an unlocked account from friend server' do let!(:friend) { Fabricate(:friend_domain, domain: sender.domain, passive_state: :idle) } before do subject.perform end it 'creates a follow from sender to recipient' do expect(sender.following?(recipient)).to be true expect(sender.active_relationships.find_by(target_account: recipient).uri).to eq 'foo' end it 'does not change friend server passive status' do expect(friend.they_are_idle?).to be true end end context 'when silenced account following an unlocked account' do before do sender.touch(:silenced_at) subject.perform end it 'does not create a follow from sender to recipient' do expect(sender.following?(recipient)).to be false end it 'creates a follow request' do expect(sender.requested?(recipient)).to be true expect(sender.follow_requests.find_by(target_account: recipient).uri).to eq 'foo' end end context 'with an unlocked account muting the sender' do before do recipient.mute!(sender) subject.perform end it 'creates a follow from sender to recipient' do expect(sender.following?(recipient)).to be true expect(sender.active_relationships.find_by(target_account: recipient).uri).to eq 'foo' end it 'does not create a follow request' do expect(sender.requested?(recipient)).to be false end end context 'when locked account' do before do recipient.update(locked: true) subject.perform end it 'does not create a follow from sender to recipient' do expect(sender.following?(recipient)).to be false end it 'creates a follow request' do expect(sender.requested?(recipient)).to be true expect(sender.follow_requests.find_by(target_account: recipient).uri).to eq 'foo' end it 'does not create pending request' do expect(sender.pending_follow_requests.find_by(target_account: recipient)).to be_nil end end context 'when unlocked account but locked from bot' do let(:actor_type) { 'Service' } before do recipient.user.settings['lock_follow_from_bot'] = true recipient.user.save! subject.perform end it 'does not create a follow from sender to recipient' do expect(sender.following?(recipient)).to be false end it 'creates a follow request' do expect(sender.requested?(recipient)).to be true expect(sender.follow_requests.find_by(target_account: recipient).uri).to eq 'foo' end end context 'when unlocked misskey proxy account but locked from bot' do let(:display_name) { 'i am proxy.' } before do Fabricate(:instance_info, domain: 'example.com', software: 'misskey') recipient.user.settings['lock_follow_from_bot'] = true recipient.user.save! subject.perform end it 'does not create a follow from sender to recipient' do expect(sender.following?(recipient)).to be false end it 'creates a follow request' do expect(sender.requested?(recipient)).to be true expect(sender.follow_requests.find_by(target_account: recipient).uri).to eq 'foo' end end context 'when unlocked mastodon proxy account but locked from bot' do let(:display_name) { 'i am proxy.' } before do Fabricate(:instance_info, domain: 'example.com', software: 'mastodon') recipient.user.settings['lock_follow_from_bot'] = true recipient.user.save! subject.perform end it 'does not create a follow from sender to recipient' do expect(sender.following?(recipient)).to be true end end context 'when unlocked misskey normal account but locked from bot' do before do Fabricate(:instance_info, domain: 'example.com', software: 'misskey') recipient.user.settings['lock_follow_from_bot'] = true recipient.user.save! subject.perform end it 'does not create a follow from sender to recipient' do expect(sender.following?(recipient)).to be true end end context 'when remote pending account follows unlocked account' do before do sender.update(suspended_at: Time.now.utc, suspension_origin: :local, remote_pending: true) allow(LocalNotificationWorker).to receive(:perform_async).and_return(nil) subject.perform end it 'does not create a follow from sender to recipient' do expect(sender.following?(recipient)).to be false end it 'does not notify' do expect(LocalNotificationWorker).to_not have_received(:perform_async) end it 'creates a follow request' do expect(sender.requested?(recipient)).to be false follow_request = sender.pending_follow_requests.find_by(target_account: recipient) expect(follow_request).to_not be_nil expect(follow_request.uri).to eq 'foo' end end context 'when remote pending account follows unlocked account but has already existing request' do before do sender.update(suspended_at: Time.now.utc, suspension_origin: :local, remote_pending: true) Fabricate(:pending_follow_request, account: sender, target_account: recipient, uri: 'old') subject.perform end it 'does not create a follow from sender to recipient' do expect(sender.following?(recipient)).to be false expect(sender.requested?(recipient)).to be false end it 'changes follow request uri' do follow_request = sender.pending_follow_requests.find_by(target_account: recipient) expect(follow_request).to_not be_nil expect(follow_request.uri).to eq 'foo' end end context 'when domain block reject_straight_follow' do before do Fabricate(:domain_block, domain: 'example.com', reject_straight_follow: true) subject.perform end it 'does not create a follow from sender to recipient' do expect(sender.following?(recipient)).to be false end it 'creates a follow request' do expect(sender.requested?(recipient)).to be true expect(sender.follow_requests.find_by(target_account: recipient).uri).to eq 'foo' end end context 'when domain block reject_new_follow' do before do Fabricate(:domain_block, domain: 'example.com', reject_new_follow: true) stub_request(:post, 'https://example.com/inbox').to_return(status: 200, body: '', headers: {}) subject.perform end it 'does not create a follow from sender to recipient' do expect(sender.following?(recipient)).to be false expect(sender.requested?(recipient)).to be false end end context 'when ng rule is existing' do context 'when ng rule is match' do before do Fabricate(:ng_rule, account_domain: 'example.com', reaction_type: ['follow']) stub_request(:post, 'https://example.com/inbox').to_return(status: 200, body: '', headers: {}) subject.perform end it 'does not create a reblog by sender of status' do expect(sender.following?(recipient)).to be false expect(sender.requested?(recipient)).to be false end end context 'when ng rule is not match' do before do Fabricate(:ng_rule, account_domain: 'foo.bar', reaction_type: ['follow']) subject.perform end it 'creates a reblog by sender of status' do expect(sender.following?(recipient)).to be true expect(sender.requested?(recipient)).to be false end end end end context 'when a follow relationship already exists' do before do sender.active_relationships.create!(target_account: recipient, uri: 'bar') end context 'with an unlocked account' do before do subject.perform end it 'correctly sets the new URI' do expect(sender.active_relationships.find_by(target_account: recipient).uri).to eq 'foo' end it 'does not create a follow request' do expect(sender.requested?(recipient)).to be false end end context 'when silenced account following an unlocked account' do before do sender.touch(:silenced_at) subject.perform end it 'correctly sets the new URI' do expect(sender.active_relationships.find_by(target_account: recipient).uri).to eq 'foo' end it 'does not create a follow request' do expect(sender.requested?(recipient)).to be false end end context 'with an unlocked account muting the sender' do before do recipient.mute!(sender) subject.perform end it 'correctly sets the new URI' do expect(sender.active_relationships.find_by(target_account: recipient).uri).to eq 'foo' end it 'does not create a follow request' do expect(sender.requested?(recipient)).to be false end end context 'when locked account' do before do recipient.update(locked: true) subject.perform end it 'correctly sets the new URI' do expect(sender.active_relationships.find_by(target_account: recipient).uri).to eq 'foo' end it 'does not create a follow request' do expect(sender.requested?(recipient)).to be false end end end context 'when a follow request already exists' do before do sender.follow_requests.create!(target_account: recipient, uri: 'bar') end context 'when silenced account following an unlocked account' do before do sender.touch(:silenced_at) subject.perform end it 'does not create a follow from sender to recipient' do expect(sender.following?(recipient)).to be false end it 'correctly sets the new URI' do expect(sender.requested?(recipient)).to be true expect(sender.follow_requests.find_by(target_account: recipient).uri).to eq 'foo' end end context 'when locked account' do before do recipient.update(locked: true) subject.perform end it 'does not create a follow from sender to recipient' do expect(sender.following?(recipient)).to be false end it 'correctly sets the new URI' do expect(sender.requested?(recipient)).to be true expect(sender.follow_requests.find_by(target_account: recipient).uri).to eq 'foo' end end end end context 'when given a friend server' do subject { described_class.new(json, sender) } let(:sender) { Fabricate(:account, domain: 'abc.com', url: 'https://abc.com/#actor', shared_inbox_url: 'https://abc.com/shared_inbox') } let!(:friend) { Fabricate(:friend_domain, domain: 'abc.com', inbox_url: 'https://example.com/inbox', passive_state: :idle) } let!(:owner_user) { Fabricate(:user, role: UserRole.find_by(name: 'Owner')) } let!(:patch_user) { Fabricate(:user, role: Fabricate(:user_role, name: 'OhagiOps', permissions: UserRole::FLAGS[:manage_federation])) } let(:json) do { '@context': 'https://www.w3.org/ns/activitystreams', id: 'foo', type: 'Follow', actor: ActivityPub::TagManager.instance.uri_for(sender), object: 'https://www.w3.org/ns/activitystreams#Public', }.with_indifferent_access end it 'marks the friend as pending' do subject.perform expect(friend.reload.they_are_pending?).to be true expect(friend.passive_follow_activity_id).to eq 'foo' end context 'when no record' do before do friend.destroy! end it 'marks the friend as pending' do subject.perform friend = FriendDomain.find_by(domain: 'abc.com') expect(friend).to_not be_nil expect(friend.they_are_pending?).to be true expect(friend.passive_follow_activity_id).to eq 'foo' expect(friend.inbox_url).to eq 'https://abc.com/shared_inbox' end end context 'when old spec which no record and inbox_url is specified' do let(:json) do { '@context': 'https://www.w3.org/ns/activitystreams', id: 'foo', type: 'Follow', actor: ActivityPub::TagManager.instance.uri_for(sender), object: 'https://www.w3.org/ns/activitystreams#Public', inboxUrl: 'https://evil.org/bad_inbox', }.with_indifferent_access end before do friend.destroy! end it 'marks the friend as pending but inboxUrl is not working' do subject.perform friend = FriendDomain.find_by(domain: 'abc.com') expect(friend).to_not be_nil expect(friend.they_are_pending?).to be true expect(friend.passive_follow_activity_id).to eq 'foo' expect(friend.inbox_url).to eq 'https://abc.com/shared_inbox' end end context 'when my server is pending' do before do friend.update(active_state: :pending) end it 'marks me as idle' do subject.perform expect(friend.reload.they_are_pending?).to be true expect(friend.i_am_idle?).to be true end end context 'when my server is already accepted' do before do friend.update(active_state: :accepted) stub_request(:post, 'https://example.com/inbox') end it 'marks me as idle and the friend as accepted', :inline_jobs do subject.perform expect(friend.reload.they_are_accepted?).to be true expect(friend.i_am_idle?).to be true expect(a_request(:post, 'https://example.com/inbox').with(body: hash_including({ id: 'foo#accepts/friends', type: 'Accept', object: 'foo', }))).to have_been_made.once end end context 'with sending email' do around do |example| queue_adapter = ActiveJob::Base.queue_adapter ActiveJob::Base.queue_adapter = :test example.run ActiveJob::Base.queue_adapter = queue_adapter end it 'perform' do expect { subject.perform }.to have_enqueued_mail(AdminMailer, :new_pending_friend_server) .with(hash_including(params: { recipient: owner_user.account })).once .and(have_enqueued_mail(AdminMailer, :new_pending_friend_server).with(hash_including(params: { recipient: patch_user.account })).once) .and(have_enqueued_mail.at_most(2)) end end context 'when after rejected' do before do friend.update(passive_state: :rejected) end it 'marks the friend as pending' do subject.perform expect(friend.reload.they_are_pending?).to be true expect(friend.passive_follow_activity_id).to eq 'foo' end end context 'when unlocked on admin settings' do before do Form::AdminSettings.new(unlocked_friend: '1').save stub_request(:post, 'https://example.com/inbox') end it 'marks the friend as accepted', :inline_jobs do subject.perform friend = FriendDomain.find_by(domain: 'abc.com') expect(friend).to_not be_nil expect(friend.they_are_accepted?).to be true expect(a_request(:post, 'https://example.com/inbox').with(body: hash_including({ id: 'foo#accepts/friends', type: 'Accept', object: 'foo', }))).to have_been_made.once end end context 'when already accepted' do before do friend.update(passive_state: :accepted) stub_request(:post, 'https://example.com/inbox') end it 'marks the friend as accepted', :inline_jobs do subject.perform friend = FriendDomain.find_by(domain: 'abc.com') expect(friend).to_not be_nil expect(friend.they_are_accepted?).to be true expect(a_request(:post, 'https://example.com/inbox').with(body: hash_including({ id: 'foo#accepts/friends', type: 'Accept', object: 'foo', }))).to have_been_made.once end end context 'when domain blocked' do before do friend.destroy! end it 'marks the friend rejected' do Fabricate(:domain_block, domain: 'abc.com', reject_friend: true) subject.perform friend = FriendDomain.find_by(domain: 'abc.com') expect(friend).to be_nil end end end end