Add notifications of severed relationships (#27511)
This commit is contained in:
parent
8a1423a474
commit
44bf7b8128
39 changed files with 781 additions and 54 deletions
|
@ -0,0 +1,6 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
Fabricator(:account_relationship_severance_event) do
|
||||
account
|
||||
relationship_severance_event
|
||||
end
|
|
@ -0,0 +1,6 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
Fabricator(:relationship_severance_event) do
|
||||
type { :domain_block }
|
||||
target_name { 'example.com' }
|
||||
end
|
8
spec/fabricators/severed_relationship_fabricator.rb
Normal file
8
spec/fabricators/severed_relationship_fabricator.rb
Normal file
|
@ -0,0 +1,8 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
Fabricator(:severed_relationship) do
|
||||
local_account { Fabricate.build(:account) }
|
||||
remote_account { Fabricate.build(:account) }
|
||||
relationship_severance_event { Fabricate.build(:relationship_severance_event) }
|
||||
direction { :active }
|
||||
end
|
49
spec/models/relationship_severance_event_spec.rb
Normal file
49
spec/models/relationship_severance_event_spec.rb
Normal file
|
@ -0,0 +1,49 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe RelationshipSeveranceEvent do
|
||||
let(:local_account) { Fabricate(:account) }
|
||||
let(:remote_account) { Fabricate(:account, domain: 'example.com') }
|
||||
let(:event) { Fabricate(:relationship_severance_event) }
|
||||
|
||||
describe '#import_from_active_follows!' do
|
||||
before do
|
||||
local_account.follow!(remote_account)
|
||||
end
|
||||
|
||||
it 'imports the follow relationships with the expected direction' do
|
||||
event.import_from_active_follows!(local_account.active_relationships)
|
||||
|
||||
relationships = event.severed_relationships.to_a
|
||||
expect(relationships.size).to eq 1
|
||||
expect(relationships[0].account).to eq local_account
|
||||
expect(relationships[0].target_account).to eq remote_account
|
||||
end
|
||||
end
|
||||
|
||||
describe '#import_from_passive_follows!' do
|
||||
before do
|
||||
remote_account.follow!(local_account)
|
||||
end
|
||||
|
||||
it 'imports the follow relationships with the expected direction' do
|
||||
event.import_from_passive_follows!(local_account.passive_relationships)
|
||||
|
||||
relationships = event.severed_relationships.to_a
|
||||
expect(relationships.size).to eq 1
|
||||
expect(relationships[0].account).to eq remote_account
|
||||
expect(relationships[0].target_account).to eq local_account
|
||||
end
|
||||
end
|
||||
|
||||
describe '#affected_local_accounts' do
|
||||
before do
|
||||
event.severed_relationships.create!(local_account: local_account, remote_account: remote_account, direction: :active)
|
||||
end
|
||||
|
||||
it 'correctly lists local accounts' do
|
||||
expect(event.affected_local_accounts.to_a).to contain_exactly(local_account)
|
||||
end
|
||||
end
|
||||
end
|
45
spec/models/severed_relationship_spec.rb
Normal file
45
spec/models/severed_relationship_spec.rb
Normal file
|
@ -0,0 +1,45 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe SeveredRelationship do
|
||||
let(:local_account) { Fabricate(:account) }
|
||||
let(:remote_account) { Fabricate(:account, domain: 'example.com') }
|
||||
let(:event) { Fabricate(:relationship_severance_event) }
|
||||
|
||||
describe '#account' do
|
||||
context 'when the local account is the follower' do
|
||||
let(:severed_relationship) { Fabricate(:severed_relationship, relationship_severance_event: event, local_account: local_account, remote_account: remote_account, direction: :active) }
|
||||
|
||||
it 'returns the local account' do
|
||||
expect(severed_relationship.account).to eq local_account
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the local account is being followed' do
|
||||
let(:severed_relationship) { Fabricate(:severed_relationship, relationship_severance_event: event, local_account: local_account, remote_account: remote_account, direction: :passive) }
|
||||
|
||||
it 'returns the remote account' do
|
||||
expect(severed_relationship.account).to eq remote_account
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#target_account' do
|
||||
context 'when the local account is the follower' do
|
||||
let(:severed_relationship) { Fabricate(:severed_relationship, relationship_severance_event: event, local_account: local_account, remote_account: remote_account, direction: :active) }
|
||||
|
||||
it 'returns the remote account' do
|
||||
expect(severed_relationship.target_account).to eq remote_account
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the local account is being followed' do
|
||||
let(:severed_relationship) { Fabricate(:severed_relationship, relationship_severance_event: event, local_account: local_account, remote_account: remote_account, direction: :passive) }
|
||||
|
||||
it 'returns the local account' do
|
||||
expect(severed_relationship.target_account).to eq local_account
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
23
spec/requests/severed_relationships_spec.rb
Normal file
23
spec/requests/severed_relationships_spec.rb
Normal file
|
@ -0,0 +1,23 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
|
||||
describe 'Severed relationships page' do
|
||||
include RoutingHelper
|
||||
|
||||
describe 'GET severed_relationships#index' do
|
||||
let(:user) { Fabricate(:user) }
|
||||
|
||||
before do
|
||||
sign_in user
|
||||
|
||||
Fabricate(:severed_relationship, local_account: user.account)
|
||||
end
|
||||
|
||||
it 'returns http success' do
|
||||
get severed_relationships_path
|
||||
|
||||
expect(response).to have_http_status(200)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -5,22 +5,33 @@ require 'rails_helper'
|
|||
RSpec.describe AfterBlockDomainFromAccountService do
|
||||
subject { described_class.new }
|
||||
|
||||
let!(:wolf) { Fabricate(:account, username: 'wolf', domain: 'evil.org', inbox_url: 'https://evil.org/inbox', protocol: :activitypub) }
|
||||
let!(:alice) { Fabricate(:account, username: 'alice') }
|
||||
let(:wolf) { Fabricate(:account, username: 'wolf', domain: 'evil.org', inbox_url: 'https://evil.org/wolf/inbox', protocol: :activitypub) }
|
||||
let(:dog) { Fabricate(:account, username: 'dog', domain: 'evil.org', inbox_url: 'https://evil.org/dog/inbox', protocol: :activitypub) }
|
||||
let(:alice) { Fabricate(:account, username: 'alice') }
|
||||
|
||||
before do
|
||||
allow(ActivityPub::DeliveryWorker).to receive(:perform_async)
|
||||
wolf.follow!(alice)
|
||||
alice.follow!(dog)
|
||||
end
|
||||
|
||||
it 'purge followers from blocked domain' do
|
||||
wolf.follow!(alice)
|
||||
around do |example|
|
||||
Sidekiq::Testing.fake! do
|
||||
example.run
|
||||
end
|
||||
end
|
||||
|
||||
it 'purges followers from blocked domain, sends them Reject->Follow, and records severed relationships', :aggregate_failures do
|
||||
subject.call(alice, 'evil.org')
|
||||
|
||||
expect(wolf.following?(alice)).to be false
|
||||
end
|
||||
expect(ActivityPub::DeliveryWorker.jobs.pluck('args')).to contain_exactly(
|
||||
[a_string_including('"type":"Reject"'), alice.id, wolf.inbox_url],
|
||||
[a_string_including('"type":"Undo"'), alice.id, dog.inbox_url]
|
||||
)
|
||||
|
||||
it 'sends Reject->Follow to followers from blocked domain' do
|
||||
wolf.follow!(alice)
|
||||
subject.call(alice, 'evil.org')
|
||||
expect(ActivityPub::DeliveryWorker).to have_received(:perform_async).once
|
||||
severed_relationships = alice.severed_relationships.to_a
|
||||
expect(severed_relationships.count).to eq 2
|
||||
expect(severed_relationships[0].relationship_severance_event).to eq severed_relationships[1].relationship_severance_event
|
||||
expect(severed_relationships.map { |rel| [rel.account, rel.target_account] }).to contain_exactly([wolf, alice], [alice, dog])
|
||||
end
|
||||
end
|
||||
|
|
|
@ -5,6 +5,8 @@ require 'rails_helper'
|
|||
RSpec.describe BlockDomainService do
|
||||
subject { described_class.new }
|
||||
|
||||
let(:local_account) { Fabricate(:account) }
|
||||
let(:bystander) { Fabricate(:account, domain: 'evil.org') }
|
||||
let!(:bad_account) { Fabricate(:account, username: 'badguy666', domain: 'evil.org') }
|
||||
let!(:bad_status_plain) { Fabricate(:status, account: bad_account, text: 'You suck') }
|
||||
let!(:bad_status_with_attachment) { Fabricate(:status, account: bad_account, text: 'Hahaha') }
|
||||
|
@ -13,62 +15,51 @@ RSpec.describe BlockDomainService do
|
|||
|
||||
describe 'for a suspension' do
|
||||
before do
|
||||
local_account.follow!(bad_account)
|
||||
bystander.follow!(local_account)
|
||||
end
|
||||
|
||||
it 'creates a domain block, suspends remote accounts with appropriate suspension date, records severed relationships', :aggregate_failures do
|
||||
subject.call(DomainBlock.create!(domain: 'evil.org', severity: :suspend))
|
||||
end
|
||||
|
||||
it 'creates a domain block' do
|
||||
expect(DomainBlock.blocked?('evil.org')).to be true
|
||||
end
|
||||
|
||||
it 'removes remote accounts from that domain' do
|
||||
# Suspends account with appropriate suspension date
|
||||
expect(bad_account.reload.suspended?).to be true
|
||||
end
|
||||
|
||||
it 'records suspension date appropriately' do
|
||||
expect(bad_account.reload.suspended_at).to eq DomainBlock.find_by(domain: 'evil.org').created_at
|
||||
end
|
||||
|
||||
it 'keeps already-banned accounts banned' do
|
||||
# Keep already-suspended account without updating the suspension date
|
||||
expect(already_banned_account.reload.suspended?).to be true
|
||||
end
|
||||
|
||||
it 'does not overwrite suspension date of already-banned accounts' do
|
||||
expect(already_banned_account.reload.suspended_at).to_not eq DomainBlock.find_by(domain: 'evil.org').created_at
|
||||
end
|
||||
|
||||
it 'removes the remote accounts\'s statuses and media attachments' do
|
||||
# Removes content
|
||||
expect { bad_status_plain.reload }.to raise_exception ActiveRecord::RecordNotFound
|
||||
expect { bad_status_with_attachment.reload }.to raise_exception ActiveRecord::RecordNotFound
|
||||
expect { bad_attachment.reload }.to raise_exception ActiveRecord::RecordNotFound
|
||||
|
||||
# Records severed relationships
|
||||
severed_relationships = local_account.severed_relationships.to_a
|
||||
expect(severed_relationships.count).to eq 2
|
||||
expect(severed_relationships[0].relationship_severance_event).to eq severed_relationships[1].relationship_severance_event
|
||||
expect(severed_relationships.map { |rel| [rel.account, rel.target_account] }).to contain_exactly([bystander, local_account], [local_account, bad_account])
|
||||
end
|
||||
end
|
||||
|
||||
describe 'for a silence with reject media' do
|
||||
before do
|
||||
it 'does not mark the domain as blocked, but silences accounts with an appropriate silencing date, clears media', :aggregate_failures, :sidekiq_inline do
|
||||
subject.call(DomainBlock.create!(domain: 'evil.org', severity: :silence, reject_media: true))
|
||||
end
|
||||
|
||||
it 'does not create a domain block' do
|
||||
expect(DomainBlock.blocked?('evil.org')).to be false
|
||||
end
|
||||
|
||||
it 'silences remote accounts from that domain' do
|
||||
# Silences account with appropriate silecing date
|
||||
expect(bad_account.reload.silenced?).to be true
|
||||
end
|
||||
|
||||
it 'records suspension date appropriately' do
|
||||
expect(bad_account.reload.silenced_at).to eq DomainBlock.find_by(domain: 'evil.org').created_at
|
||||
end
|
||||
|
||||
it 'keeps already-banned accounts banned' do
|
||||
# Keeps already-silenced accounts without updating the silecing date
|
||||
expect(already_banned_account.reload.silenced?).to be true
|
||||
end
|
||||
|
||||
it 'does not overwrite suspension date of already-banned accounts' do
|
||||
expect(already_banned_account.reload.silenced_at).to_not eq DomainBlock.find_by(domain: 'evil.org').created_at
|
||||
end
|
||||
|
||||
it 'leaves the domains status and attachments, but clears media', :sidekiq_inline do
|
||||
# Leaves posts but clears media
|
||||
expect { bad_status_plain.reload }.to_not raise_error
|
||||
expect { bad_status_with_attachment.reload }.to_not raise_error
|
||||
expect { bad_attachment.reload }.to_not raise_error
|
||||
|
|
|
@ -59,7 +59,7 @@ RSpec.describe SuspendAccountService, :sidekiq_inline do
|
|||
remote_follower.follow!(account)
|
||||
end
|
||||
|
||||
it 'sends an update actor to followers and reporters' do
|
||||
it 'sends an Update actor activity to followers and reporters' do
|
||||
subject
|
||||
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
|
||||
|
@ -85,9 +85,14 @@ RSpec.describe SuspendAccountService, :sidekiq_inline do
|
|||
account.follow!(local_followee)
|
||||
end
|
||||
|
||||
it 'sends a reject follow' do
|
||||
it 'sends a Reject Follow activity, and records severed relationships', :aggregate_failures do
|
||||
subject
|
||||
|
||||
expect(a_request(:post, account.inbox_url).with { |req| match_reject_follow_request(req, account, local_followee) }).to have_been_made.once
|
||||
|
||||
severed_relationships = local_followee.severed_relationships.to_a
|
||||
expect(severed_relationships.count).to eq 1
|
||||
expect(severed_relationships.map { |rel| [rel.account, rel.target_account] }).to contain_exactly([account, local_followee])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue