Add initial support for ingesting and verifying remote quote posts (#34370)
This commit is contained in:
parent
a324edabdf
commit
df2611a10f
33 changed files with 1643 additions and 22 deletions
|
|
@ -5,7 +5,7 @@ require 'rails_helper'
|
|||
RSpec.describe ActivityPub::ProcessStatusUpdateService do
|
||||
subject { described_class.new }
|
||||
|
||||
let!(:status) { Fabricate(:status, text: 'Hello world', account: Fabricate(:account, domain: 'example.com')) }
|
||||
let!(:status) { Fabricate(:status, text: 'Hello world', uri: 'https://example.com/statuses/1234', account: Fabricate(:account, domain: 'example.com')) }
|
||||
let(:bogus_mention) { 'https://example.com/users/erroringuser' }
|
||||
let(:payload) do
|
||||
{
|
||||
|
|
@ -435,6 +435,398 @@ RSpec.describe ActivityPub::ProcessStatusUpdateService do
|
|||
end
|
||||
end
|
||||
|
||||
context 'when the status has an existing unverified quote and adds an approval link', feature: :inbound_quotes do
|
||||
let(:quoted_account) { Fabricate(:account, domain: 'quoted.example.com') }
|
||||
let(:quoted_status) { Fabricate(:status, account: quoted_account) }
|
||||
let!(:quote) { Fabricate(:quote, status: status, quoted_status: quoted_status, approval_uri: nil) }
|
||||
let(:approval_uri) { 'https://quoted.example.com/approvals/1' }
|
||||
|
||||
let(:payload) do
|
||||
{
|
||||
'@context': [
|
||||
'https://www.w3.org/ns/activitystreams',
|
||||
{
|
||||
'@id': 'https://w3id.org/fep/044f#quote',
|
||||
'@type': '@id',
|
||||
},
|
||||
{
|
||||
'@id': 'https://w3id.org/fep/044f#quoteAuthorization',
|
||||
'@type': '@id',
|
||||
},
|
||||
],
|
||||
id: 'foo',
|
||||
type: 'Note',
|
||||
summary: 'Show more',
|
||||
content: 'Hello universe',
|
||||
updated: '2021-09-08T22:39:25Z',
|
||||
quote: ActivityPub::TagManager.instance.uri_for(quoted_status),
|
||||
quoteAuthorization: approval_uri,
|
||||
}
|
||||
end
|
||||
|
||||
before do
|
||||
stub_request(:get, approval_uri).to_return(headers: { 'Content-Type': 'application/activity+json' }, body: Oj.dump({
|
||||
'@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: 'QuoteAuthorization',
|
||||
id: approval_uri,
|
||||
attributedTo: ActivityPub::TagManager.instance.uri_for(quoted_status.account),
|
||||
interactingObject: ActivityPub::TagManager.instance.uri_for(status),
|
||||
interactionTarget: ActivityPub::TagManager.instance.uri_for(quoted_status),
|
||||
}))
|
||||
end
|
||||
|
||||
it 'updates the approval URI and verifies the quote' do
|
||||
expect { subject.call(status, json, json) }
|
||||
.to change(quote, :approval_uri).to(approval_uri)
|
||||
.and change(quote, :state).to('accepted')
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the status has an existing verified quote and removes an approval link', feature: :inbound_quotes do
|
||||
let(:quoted_account) { Fabricate(:account, domain: 'quoted.example.com') }
|
||||
let(:quoted_status) { Fabricate(:status, account: quoted_account) }
|
||||
let!(:quote) { Fabricate(:quote, status: status, quoted_status: quoted_status, approval_uri: approval_uri, state: :accepted) }
|
||||
let(:approval_uri) { 'https://quoted.example.com/approvals/1' }
|
||||
|
||||
let(:payload) do
|
||||
{
|
||||
'@context': [
|
||||
'https://www.w3.org/ns/activitystreams',
|
||||
{
|
||||
'@id': 'https://w3id.org/fep/044f#quote',
|
||||
'@type': '@id',
|
||||
},
|
||||
{
|
||||
'@id': 'https://w3id.org/fep/044f#quoteAuthorization',
|
||||
'@type': '@id',
|
||||
},
|
||||
],
|
||||
id: 'foo',
|
||||
type: 'Note',
|
||||
summary: 'Show more',
|
||||
content: 'Hello universe',
|
||||
updated: '2021-09-08T22:39:25Z',
|
||||
quote: ActivityPub::TagManager.instance.uri_for(quoted_status),
|
||||
}
|
||||
end
|
||||
|
||||
it 'removes the approval URI and unverifies the quote' do
|
||||
expect { subject.call(status, json, json) }
|
||||
.to change(quote, :approval_uri).to(nil)
|
||||
.and change(quote, :state).to('pending')
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the status adds a verifiable quote', feature: :inbound_quotes do
|
||||
let(:quoted_account) { Fabricate(:account, domain: 'quoted.example.com') }
|
||||
let(:quoted_status) { Fabricate(:status, account: quoted_account) }
|
||||
let(:approval_uri) { 'https://quoted.example.com/approvals/1' }
|
||||
|
||||
let(:payload) do
|
||||
{
|
||||
'@context': [
|
||||
'https://www.w3.org/ns/activitystreams',
|
||||
{
|
||||
'@id': 'https://w3id.org/fep/044f#quote',
|
||||
'@type': '@id',
|
||||
},
|
||||
{
|
||||
'@id': 'https://w3id.org/fep/044f#quoteAuthorization',
|
||||
'@type': '@id',
|
||||
},
|
||||
],
|
||||
id: 'foo',
|
||||
type: 'Note',
|
||||
summary: 'Show more',
|
||||
content: 'Hello universe',
|
||||
updated: '2021-09-08T22:39:25Z',
|
||||
quote: ActivityPub::TagManager.instance.uri_for(quoted_status),
|
||||
quoteAuthorization: approval_uri,
|
||||
}
|
||||
end
|
||||
|
||||
before do
|
||||
stub_request(:get, approval_uri).to_return(headers: { 'Content-Type': 'application/activity+json' }, body: Oj.dump({
|
||||
'@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: 'QuoteAuthorization',
|
||||
id: approval_uri,
|
||||
attributedTo: ActivityPub::TagManager.instance.uri_for(quoted_status.account),
|
||||
interactingObject: ActivityPub::TagManager.instance.uri_for(status),
|
||||
interactionTarget: ActivityPub::TagManager.instance.uri_for(quoted_status),
|
||||
}))
|
||||
end
|
||||
|
||||
it 'updates the approval URI and verifies the quote' do
|
||||
expect { subject.call(status, json, json) }
|
||||
.to change(status, :quote).from(nil)
|
||||
expect(status.quote.approval_uri).to eq approval_uri
|
||||
expect(status.quote.state).to eq 'accepted'
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the status adds a unverifiable quote', feature: :inbound_quotes do
|
||||
let(:quoted_account) { Fabricate(:account, domain: 'quoted.example.com') }
|
||||
let(:quoted_status) { Fabricate(:status, account: quoted_account) }
|
||||
let(:approval_uri) { 'https://quoted.example.com/approvals/1' }
|
||||
|
||||
let(:payload) do
|
||||
{
|
||||
'@context': [
|
||||
'https://www.w3.org/ns/activitystreams',
|
||||
{
|
||||
'@id': 'https://w3id.org/fep/044f#quote',
|
||||
'@type': '@id',
|
||||
},
|
||||
{
|
||||
'@id': 'https://w3id.org/fep/044f#quoteAuthorization',
|
||||
'@type': '@id',
|
||||
},
|
||||
],
|
||||
id: 'foo',
|
||||
type: 'Note',
|
||||
summary: 'Show more',
|
||||
content: 'Hello universe',
|
||||
updated: '2021-09-08T22:39:25Z',
|
||||
quote: ActivityPub::TagManager.instance.uri_for(quoted_status),
|
||||
}
|
||||
end
|
||||
|
||||
it 'updates the approval URI but does not verify the quote' do
|
||||
expect { subject.call(status, json, json) }
|
||||
.to change(status, :quote).from(nil)
|
||||
expect(status.quote.approval_uri).to be_nil
|
||||
expect(status.quote.state).to eq 'pending'
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the status removes a verified quote', feature: :inbound_quotes do
|
||||
let(:quoted_account) { Fabricate(:account, domain: 'quoted.example.com') }
|
||||
let(:quoted_status) { Fabricate(:status, account: quoted_account) }
|
||||
let!(:quote) { Fabricate(:quote, status: status, quoted_status: quoted_status, approval_uri: approval_uri, state: :accepted) }
|
||||
let(:approval_uri) { 'https://quoted.example.com/approvals/1' }
|
||||
|
||||
let(:payload) do
|
||||
{
|
||||
'@context': 'https://www.w3.org/ns/activitystreams',
|
||||
id: 'foo',
|
||||
type: 'Note',
|
||||
summary: 'Show more',
|
||||
content: 'Hello universe',
|
||||
updated: '2021-09-08T22:39:25Z',
|
||||
}
|
||||
end
|
||||
|
||||
it 'removes the quote' do
|
||||
expect { subject.call(status, json, json) }
|
||||
.to change { status.reload.quote }.to(nil)
|
||||
|
||||
expect { quote.reload }.to raise_error(ActiveRecord::RecordNotFound)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the status removes an unverified quote', feature: :inbound_quotes do
|
||||
let(:quoted_account) { Fabricate(:account, domain: 'quoted.example.com') }
|
||||
let(:quoted_status) { Fabricate(:status, account: quoted_account) }
|
||||
let!(:quote) { Fabricate(:quote, status: status, quoted_status: quoted_status, approval_uri: nil, state: :pending) }
|
||||
|
||||
let(:payload) do
|
||||
{
|
||||
'@context': 'https://www.w3.org/ns/activitystreams',
|
||||
id: 'foo',
|
||||
type: 'Note',
|
||||
summary: 'Show more',
|
||||
content: 'Hello universe',
|
||||
updated: '2021-09-08T22:39:25Z',
|
||||
}
|
||||
end
|
||||
|
||||
it 'removes the quote' do
|
||||
expect { subject.call(status, json, json) }
|
||||
.to change { status.reload.quote }.to(nil)
|
||||
|
||||
expect { quote.reload }.to raise_error(ActiveRecord::RecordNotFound)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the status swaps a verified quote with an unverifiable quote', feature: :inbound_quotes do
|
||||
let(:quoted_account) { Fabricate(:account, domain: 'quoted.example.com') }
|
||||
let(:quoted_status) { Fabricate(:status, account: quoted_account) }
|
||||
let(:second_quoted_status) { Fabricate(:status, account: quoted_account) }
|
||||
let!(:quote) { Fabricate(:quote, status: status, quoted_status: quoted_status, approval_uri: approval_uri, state: :accepted) }
|
||||
let(:approval_uri) { 'https://quoted.example.com/approvals/1' }
|
||||
|
||||
let(:payload) do
|
||||
{
|
||||
'@context': [
|
||||
'https://www.w3.org/ns/activitystreams',
|
||||
{
|
||||
'@id': 'https://w3id.org/fep/044f#quote',
|
||||
'@type': '@id',
|
||||
},
|
||||
{
|
||||
'@id': 'https://w3id.org/fep/044f#quoteAuthorization',
|
||||
'@type': '@id',
|
||||
},
|
||||
],
|
||||
id: 'foo',
|
||||
type: 'Note',
|
||||
summary: 'Show more',
|
||||
content: 'Hello universe',
|
||||
updated: '2021-09-08T22:39:25Z',
|
||||
quote: ActivityPub::TagManager.instance.uri_for(second_quoted_status),
|
||||
quoteAuthorization: approval_uri,
|
||||
}
|
||||
end
|
||||
|
||||
before do
|
||||
stub_request(:get, approval_uri).to_return(headers: { 'Content-Type': 'application/activity+json' }, body: Oj.dump({
|
||||
'@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: 'QuoteAuthorization',
|
||||
id: approval_uri,
|
||||
attributedTo: ActivityPub::TagManager.instance.uri_for(quoted_status.account),
|
||||
interactingObject: ActivityPub::TagManager.instance.uri_for(status),
|
||||
interactionTarget: ActivityPub::TagManager.instance.uri_for(quoted_status),
|
||||
}))
|
||||
end
|
||||
|
||||
it 'updates the URI and unverifies the quote' do
|
||||
expect { subject.call(status, json, json) }
|
||||
.to change { status.quote.quoted_status }.from(quoted_status).to(second_quoted_status)
|
||||
.and change { status.quote.state }.from('accepted')
|
||||
|
||||
expect { quote.reload }.to raise_error(ActiveRecord::RecordNotFound)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the status swaps a verified quote with another verifiable quote', feature: :inbound_quotes do
|
||||
let(:quoted_account) { Fabricate(:account, domain: 'quoted.example.com') }
|
||||
let(:second_quoted_account) { Fabricate(:account, domain: 'second-quoted.example.com') }
|
||||
let(:quoted_status) { Fabricate(:status, account: quoted_account) }
|
||||
let(:second_quoted_status) { Fabricate(:status, account: second_quoted_account) }
|
||||
let!(:quote) { Fabricate(:quote, status: status, quoted_status: quoted_status, approval_uri: approval_uri, state: :accepted) }
|
||||
let(:approval_uri) { 'https://quoted.example.com/approvals/1' }
|
||||
let(:second_approval_uri) { 'https://second-quoted.example.com/approvals/2' }
|
||||
|
||||
let(:payload) do
|
||||
{
|
||||
'@context': [
|
||||
'https://www.w3.org/ns/activitystreams',
|
||||
{
|
||||
'@id': 'https://w3id.org/fep/044f#quote',
|
||||
'@type': '@id',
|
||||
},
|
||||
{
|
||||
'@id': 'https://w3id.org/fep/044f#quoteAuthorization',
|
||||
'@type': '@id',
|
||||
},
|
||||
],
|
||||
id: 'foo',
|
||||
type: 'Note',
|
||||
summary: 'Show more',
|
||||
content: 'Hello universe',
|
||||
updated: '2021-09-08T22:39:25Z',
|
||||
quote: ActivityPub::TagManager.instance.uri_for(second_quoted_status),
|
||||
quoteAuthorization: second_approval_uri,
|
||||
}
|
||||
end
|
||||
|
||||
before do
|
||||
stub_request(:get, second_approval_uri).to_return(headers: { 'Content-Type': 'application/activity+json' }, body: Oj.dump({
|
||||
'@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: 'QuoteAuthorization',
|
||||
id: second_approval_uri,
|
||||
attributedTo: ActivityPub::TagManager.instance.uri_for(second_quoted_status.account),
|
||||
interactingObject: ActivityPub::TagManager.instance.uri_for(status),
|
||||
interactionTarget: ActivityPub::TagManager.instance.uri_for(second_quoted_status),
|
||||
}))
|
||||
end
|
||||
|
||||
it 'updates the URI and unverifies the quote' do
|
||||
expect { subject.call(status, json, json) }
|
||||
.to change { status.quote.quoted_status }.from(quoted_status).to(second_quoted_status)
|
||||
.and change { status.quote.approval_uri }.from(approval_uri).to(second_approval_uri)
|
||||
.and(not_change { status.quote.state })
|
||||
|
||||
expect { quote.reload }.to raise_error(ActiveRecord::RecordNotFound)
|
||||
end
|
||||
end
|
||||
|
||||
def poll_option_json(name, votes)
|
||||
{ type: 'Note', name: name, replies: { type: 'Collection', totalItems: votes } }
|
||||
end
|
||||
|
|
|
|||
246
spec/services/activitypub/verify_quote_service_spec.rb
Normal file
246
spec/services/activitypub/verify_quote_service_spec.rb
Normal file
|
|
@ -0,0 +1,246 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
|
||||
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 unfetchable approval URI' do
|
||||
let(:approval_uri) { 'https://b.example.com/approvals/1234' }
|
||||
|
||||
before do
|
||||
stub_request(:get, approval_uri)
|
||||
.to_return(status: 404)
|
||||
end
|
||||
|
||||
context 'with an already-fetched post' do
|
||||
it 'does not update the status' do
|
||||
expect { subject.call(quote) }
|
||||
.to change(quote, :state).to('rejected')
|
||||
end
|
||||
end
|
||||
|
||||
context 'with an already-verified quote' do
|
||||
let(:quote) { Fabricate(:quote, status: status, quoted_status: quoted_status, approval_uri: approval_uri, state: :accepted) }
|
||||
|
||||
it 'rejects the quote' do
|
||||
expect { subject.call(quote) }
|
||||
.to change(quote, :state).to('revoked')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
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 a post that cannot be fetched but is inlined' do
|
||||
let(:quoted_status) { nil }
|
||||
|
||||
let(:approval_interaction_target) do
|
||||
{
|
||||
type: 'Note',
|
||||
id: 'https://b.example.com/unknown-quoted',
|
||||
to: 'https://www.w3.org/ns/activitystreams#Public',
|
||||
attributedTo: ActivityPub::TagManager.instance.uri_for(quoted_account),
|
||||
content: 'previously unknown post',
|
||||
}
|
||||
end
|
||||
|
||||
before do
|
||||
stub_request(:get, 'https://b.example.com/unknown-quoted')
|
||||
.to_return(status: 404)
|
||||
end
|
||||
|
||||
it 'updates the status' do
|
||||
expect { subject.call(quote, fetchable_quoted_uri: 'https://b.example.com/unknown-quoted') }
|
||||
.to change(quote, :state).to('accepted')
|
||||
|
||||
expect(a_request(:get, approval_uri))
|
||||
.to have_been_made.once
|
||||
|
||||
expect(quote.reload.quoted_status.content).to eq 'previously unknown post'
|
||||
end
|
||||
end
|
||||
|
||||
context 'with a valid activity for a post that cannot be fetched and is inlined from an untrusted source' do
|
||||
let(:quoted_status) { nil }
|
||||
|
||||
let(:approval_interaction_target) do
|
||||
{
|
||||
type: 'Note',
|
||||
id: 'https://example.com/unknown-quoted',
|
||||
to: 'https://www.w3.org/ns/activitystreams#Public',
|
||||
attributedTo: ActivityPub::TagManager.instance.uri_for(account),
|
||||
content: 'previously unknown post',
|
||||
}
|
||||
end
|
||||
|
||||
before do
|
||||
stub_request(:get, 'https://example.com/unknown-quoted')
|
||||
.to_return(status: 404)
|
||||
end
|
||||
|
||||
it 'does not update the status' do
|
||||
expect { subject.call(quote, fetchable_quoted_uri: 'https://example.com/unknown-quoted') }
|
||||
.to not_change(quote, :state)
|
||||
.and not_change(quote, :quoted_status)
|
||||
|
||||
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 an invalid approval document because of a mismatched ID' do
|
||||
let(:approval_id) { 'https://evil.com/approvals/1234' }
|
||||
|
||||
it 'does not accept the quote' do
|
||||
# NOTE: maybe we want to skip that instead of rejecting it?
|
||||
expect { subject.call(quote) }
|
||||
.to change(quote, :state).to('rejected')
|
||||
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
|
||||
Loading…
Add table
Add a link
Reference in a new issue