mastodon/spec/services/activitypub/verify_quote_service_spec.rb
2025-04-04 16:07:17 +02:00

170 lines
5.4 KiB
Ruby

# frozen_string_literal: true
require 'rails_helper'
# TODO: high-level tests
# - fetch unapproved post with a posteriori approval
# - fetch implicitly approved post
# - handle explicit revocation (`Delete`)
# - handle post getting unapproved (`Update`)
RSpec.describe ActivityPub::VerifyQuoteService do
subject { described_class.new }
let(:account) { Fabricate(:account, domain: 'a.example.com') }
let(:quoted_account) { Fabricate(:account, domain: 'b.example.com') }
let(:quoted_status) { Fabricate(:status, account: quoted_account) }
let(:status) { Fabricate(:status, account: account) }
let(:quote) { Fabricate(:quote, status: status, quoted_status: quoted_status, approval_uri: approval_uri) }
context 'with an approval URI' do
let(:approval_uri) { 'https://b.example.com/approvals/1234' }
let(:approval_type) { 'QuoteAuthorization' }
let(:approval_id) { approval_uri }
let(:approval_attributed_to) { ActivityPub::TagManager.instance.uri_for(quoted_account) }
let(:approval_interacting_object) { ActivityPub::TagManager.instance.uri_for(status) }
let(:approval_interaction_target) { ActivityPub::TagManager.instance.uri_for(quoted_status) }
let(:json) do
{
'@context': [
'https://www.w3.org/ns/activitystreams',
{
toot: 'http://joinmastodon.org/ns#',
QuoteAuthorization: 'toot:QuoteAuthorization',
gts: 'https://gotosocial.org/ns#',
interactionPolicy: {
'@id': 'gts:interactionPolicy',
'@type': '@id',
},
interactingObject: {
'@id': 'gts:interactingObject',
'@type': '@id',
},
interactionTarget: {
'@id': 'gts:interactionTarget',
'@type': '@id',
},
},
],
type: approval_type,
id: approval_id,
attributedTo: approval_attributed_to,
interactingObject: approval_interacting_object,
interactionTarget: approval_interaction_target,
}.with_indifferent_access
end
before do
stub_request(:get, approval_uri)
.to_return(status: 200, body: Oj.dump(json), headers: { 'Content-Type': 'application/activity+json' })
end
context 'with a valid activity for already-fetched posts' do
it 'updates the status' do
expect { subject.call(quote) }
.to change(quote, :state).to('accepted')
expect(a_request(:get, approval_uri))
.to have_been_made.once
end
end
context 'with a valid activity for already-fetched posts, with a pre-fetched approval' do
it 'updates the status without fetching the activity' do
expect { subject.call(quote, prefetched_body: Oj.dump(json)) }
.to change(quote, :state).to('accepted')
expect(a_request(:get, approval_uri))
.to_not have_been_made
end
end
context 'with an unverifiable approval' do
let(:approval_uri) { 'https://evil.com/approvals/1234' }
it 'does not update the status' do
expect { subject.call(quote) }
.to_not change(quote, :state)
end
end
context 'with a mismatched ID' do
let(:approval_id) { 'https://evil.com/approvals/1234' }
it 'does not update the status' do
expect { subject.call(quote) }
.to_not change(quote, :state)
end
end
context 'with an approval from the wrong account' do
let(:approval_attributed_to) { ActivityPub::TagManager.instance.uri_for(Fabricate(:account, domain: 'b.example.com')) }
it 'does not update the status' do
expect { subject.call(quote) }
.to_not change(quote, :state)
end
end
context 'with an approval for the wrong quoted post' do
let(:approval_interaction_target) { ActivityPub::TagManager.instance.uri_for(Fabricate(:status, account: quoted_account)) }
it 'does not update the status' do
expect { subject.call(quote) }
.to_not change(quote, :state)
end
end
context 'with an approval for the wrong quote post' do
let(:approval_interacting_object) { ActivityPub::TagManager.instance.uri_for(Fabricate(:status, account: account)) }
it 'does not update the status' do
expect { subject.call(quote) }
.to_not change(quote, :state)
end
end
context 'with an approval of the wrong type' do
let(:approval_type) { 'ReplyAuthorization' }
it 'does not update the status' do
expect { subject.call(quote) }
.to_not change(quote, :state)
end
end
end
context 'with fast-track authorizations' do
let(:approval_uri) { nil }
context 'without any fast-track condition' do
it 'does not update the status' do
expect { subject.call(quote) }
.to_not change(quote, :state)
end
end
context 'when the account and the quoted account are the same' do
let(:quoted_account) { account }
it 'updates the status' do
expect { subject.call(quote) }
.to change(quote, :state).to('accepted')
end
end
context 'when the account is mentioned by the quoted post' do
before do
quoted_status.mentions << Mention.new(account: account)
end
it 'updates the status' do
expect { subject.call(quote) }
.to change(quote, :state).to('accepted')
end
end
end
end