Add ActivityPub inbox (#4216)
* Add ActivityPub inbox * Handle ActivityPub deletes * Handle ActivityPub creates * Handle ActivityPub announces * Stubs for handling all activities that need to be handled * Add ActivityPub actor resolving * Handle conversation URI passing in ActivityPub * Handle content language in ActivityPub * Send accept header when fetching actor, handle JSON parse errors * Test for ActivityPub::FetchRemoteAccountService * Handle public key and icon/image when embedded/as array/as resolvable URI * Implement ActivityPub::FetchRemoteStatusService * Add stubs for more interactions * Undo activities implemented * Handle out of order activities * Hook up ActivityPub to ResolveRemoteAccountService, handle Update Account activities * Add fragment IDs to all transient activity serializers * Add tests and fixes * Add stubs for missing tests * Add more tests * Add more tests
This commit is contained in:
parent
dcbc1af38a
commit
dd7ef0dc41
50 changed files with 1652 additions and 21 deletions
7
spec/controllers/activitypub/inboxes_controller_spec.rb
Normal file
7
spec/controllers/activitypub/inboxes_controller_spec.rb
Normal file
|
@ -0,0 +1,7 @@
|
|||
require 'rails_helper'
|
||||
|
||||
RSpec.describe ActivityPub::InboxesController, type: :controller do
|
||||
describe 'POST #create' do
|
||||
pending
|
||||
end
|
||||
end
|
19
spec/controllers/activitypub/outboxes_controller_spec.rb
Normal file
19
spec/controllers/activitypub/outboxes_controller_spec.rb
Normal file
|
@ -0,0 +1,19 @@
|
|||
require 'rails_helper'
|
||||
|
||||
RSpec.describe ActivityPub::OutboxesController, type: :controller do
|
||||
let!(:account) { Fabricate(:account) }
|
||||
|
||||
before do
|
||||
Fabricate(:status, account: account)
|
||||
end
|
||||
|
||||
describe 'GET #show' do
|
||||
before do
|
||||
get :show, params: { account_username: account.username }
|
||||
end
|
||||
|
||||
it 'returns http success' do
|
||||
expect(response).to have_http_status(:success)
|
||||
end
|
||||
end
|
||||
end
|
35
spec/helpers/jsonld_helper_spec.rb
Normal file
35
spec/helpers/jsonld_helper_spec.rb
Normal file
|
@ -0,0 +1,35 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
|
||||
describe JsonLdHelper do
|
||||
describe '#equals_or_includes?' do
|
||||
it 'returns true when value equals' do
|
||||
expect(helper.equals_or_includes?('foo', 'foo')).to be true
|
||||
end
|
||||
|
||||
it 'returns false when value does not equal' do
|
||||
expect(helper.equals_or_includes?('foo', 'bar')).to be false
|
||||
end
|
||||
|
||||
it 'returns true when value is included' do
|
||||
expect(helper.equals_or_includes?(%w(foo baz), 'foo')).to be true
|
||||
end
|
||||
|
||||
it 'returns false when value is not included' do
|
||||
expect(helper.equals_or_includes?(%w(foo baz), 'bar')).to be false
|
||||
end
|
||||
end
|
||||
|
||||
describe '#first_of_value' do
|
||||
pending
|
||||
end
|
||||
|
||||
describe '#supported_context?' do
|
||||
pending
|
||||
end
|
||||
|
||||
describe '#fetch_resource' do
|
||||
pending
|
||||
end
|
||||
end
|
29
spec/lib/activitypub/activity/announce_spec.rb
Normal file
29
spec/lib/activitypub/activity/announce_spec.rb
Normal file
|
@ -0,0 +1,29 @@
|
|||
require 'rails_helper'
|
||||
|
||||
RSpec.describe ActivityPub::Activity::Announce do
|
||||
let(:sender) { Fabricate(:account) }
|
||||
let(:recipient) { Fabricate(:account) }
|
||||
let(:status) { Fabricate(:status, account: recipient) }
|
||||
|
||||
let(:json) do
|
||||
{
|
||||
'@context': 'https://www.w3.org/ns/activitystreams',
|
||||
id: 'foo',
|
||||
type: 'Announce',
|
||||
actor: ActivityPub::TagManager.instance.uri_for(sender),
|
||||
object: ActivityPub::TagManager.instance.uri_for(status),
|
||||
}.with_indifferent_access
|
||||
end
|
||||
|
||||
describe '#perform' do
|
||||
subject { described_class.new(json, sender) }
|
||||
|
||||
before do
|
||||
subject.perform
|
||||
end
|
||||
|
||||
it 'creates a reblog by sender of status' do
|
||||
expect(sender.reblogged?(status)).to be true
|
||||
end
|
||||
end
|
||||
end
|
28
spec/lib/activitypub/activity/block_spec.rb
Normal file
28
spec/lib/activitypub/activity/block_spec.rb
Normal file
|
@ -0,0 +1,28 @@
|
|||
require 'rails_helper'
|
||||
|
||||
RSpec.describe ActivityPub::Activity::Block do
|
||||
let(:sender) { Fabricate(:account) }
|
||||
let(:recipient) { Fabricate(:account) }
|
||||
|
||||
let(:json) do
|
||||
{
|
||||
'@context': 'https://www.w3.org/ns/activitystreams',
|
||||
id: 'foo',
|
||||
type: 'Block',
|
||||
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) }
|
||||
|
||||
before do
|
||||
subject.perform
|
||||
end
|
||||
|
||||
it 'creates a block from sender to recipient' do
|
||||
expect(sender.blocking?(recipient)).to be true
|
||||
end
|
||||
end
|
||||
end
|
221
spec/lib/activitypub/activity/create_spec.rb
Normal file
221
spec/lib/activitypub/activity/create_spec.rb
Normal file
|
@ -0,0 +1,221 @@
|
|||
require 'rails_helper'
|
||||
|
||||
RSpec.describe ActivityPub::Activity::Create do
|
||||
let(:sender) { Fabricate(:account, followers_url: 'http://example.com/followers') }
|
||||
|
||||
let(:json) do
|
||||
{
|
||||
'@context': 'https://www.w3.org/ns/activitystreams',
|
||||
id: 'foo',
|
||||
type: 'Create',
|
||||
actor: ActivityPub::TagManager.instance.uri_for(sender),
|
||||
object: object_json,
|
||||
}.with_indifferent_access
|
||||
end
|
||||
|
||||
subject { described_class.new(json, sender) }
|
||||
|
||||
before do
|
||||
stub_request(:get, 'http://example.com/attachment.png').to_return(request_fixture('avatar.txt'))
|
||||
end
|
||||
|
||||
describe '#perform' do
|
||||
before do
|
||||
subject.perform
|
||||
end
|
||||
|
||||
context 'standalone' do
|
||||
let(:object_json) do
|
||||
{
|
||||
id: 'bar',
|
||||
type: 'Note',
|
||||
content: 'Lorem ipsum',
|
||||
}
|
||||
end
|
||||
|
||||
it 'creates status' do
|
||||
status = sender.statuses.first
|
||||
|
||||
expect(status).to_not be_nil
|
||||
expect(status.text).to eq 'Lorem ipsum'
|
||||
end
|
||||
|
||||
it 'missing to/cc defaults to direct privacy' do
|
||||
status = sender.statuses.first
|
||||
|
||||
expect(status).to_not be_nil
|
||||
expect(status.visibility).to eq 'direct'
|
||||
end
|
||||
end
|
||||
|
||||
context 'public' do
|
||||
let(:object_json) do
|
||||
{
|
||||
id: 'bar',
|
||||
type: 'Note',
|
||||
content: 'Lorem ipsum',
|
||||
to: 'https://www.w3.org/ns/activitystreams#Public',
|
||||
}
|
||||
end
|
||||
|
||||
it 'creates status' do
|
||||
status = sender.statuses.first
|
||||
|
||||
expect(status).to_not be_nil
|
||||
expect(status.visibility).to eq 'public'
|
||||
end
|
||||
end
|
||||
|
||||
context 'unlisted' do
|
||||
let(:object_json) do
|
||||
{
|
||||
id: 'bar',
|
||||
type: 'Note',
|
||||
content: 'Lorem ipsum',
|
||||
cc: 'https://www.w3.org/ns/activitystreams#Public',
|
||||
}
|
||||
end
|
||||
|
||||
it 'creates status' do
|
||||
status = sender.statuses.first
|
||||
|
||||
expect(status).to_not be_nil
|
||||
expect(status.visibility).to eq 'unlisted'
|
||||
end
|
||||
end
|
||||
|
||||
context 'private' do
|
||||
let(:object_json) do
|
||||
{
|
||||
id: 'bar',
|
||||
type: 'Note',
|
||||
content: 'Lorem ipsum',
|
||||
to: 'http://example.com/followers',
|
||||
}
|
||||
end
|
||||
|
||||
it 'creates status' do
|
||||
status = sender.statuses.first
|
||||
|
||||
expect(status).to_not be_nil
|
||||
expect(status.visibility).to eq 'private'
|
||||
end
|
||||
end
|
||||
|
||||
context 'direct' do
|
||||
let(:recipient) { Fabricate(:account) }
|
||||
|
||||
let(:object_json) do
|
||||
{
|
||||
id: 'bar',
|
||||
type: 'Note',
|
||||
content: 'Lorem ipsum',
|
||||
to: ActivityPub::TagManager.instance.uri_for(recipient),
|
||||
}
|
||||
end
|
||||
|
||||
it 'creates status' do
|
||||
status = sender.statuses.first
|
||||
|
||||
expect(status).to_not be_nil
|
||||
expect(status.visibility).to eq 'direct'
|
||||
end
|
||||
end
|
||||
|
||||
context 'as a reply' do
|
||||
let(:original_status) { Fabricate(:status) }
|
||||
|
||||
let(:object_json) do
|
||||
{
|
||||
id: 'bar',
|
||||
type: 'Note',
|
||||
content: 'Lorem ipsum',
|
||||
inReplyTo: ActivityPub::TagManager.instance.uri_for(original_status),
|
||||
}
|
||||
end
|
||||
|
||||
it 'creates status' do
|
||||
status = sender.statuses.first
|
||||
|
||||
expect(status).to_not be_nil
|
||||
expect(status.thread).to eq original_status
|
||||
expect(status.reply?).to be true
|
||||
expect(status.in_reply_to_account).to eq original_status.account
|
||||
expect(status.conversation).to eq original_status.conversation
|
||||
end
|
||||
end
|
||||
|
||||
context 'with mentions' do
|
||||
let(:recipient) { Fabricate(:account) }
|
||||
|
||||
let(:object_json) do
|
||||
{
|
||||
id: 'bar',
|
||||
type: 'Note',
|
||||
content: 'Lorem ipsum',
|
||||
tag: [
|
||||
{
|
||||
type: 'Mention',
|
||||
href: ActivityPub::TagManager.instance.uri_for(recipient),
|
||||
},
|
||||
],
|
||||
}
|
||||
end
|
||||
|
||||
it 'creates status' do
|
||||
status = sender.statuses.first
|
||||
|
||||
expect(status).to_not be_nil
|
||||
expect(status.mentions.map(&:account)).to include(recipient)
|
||||
end
|
||||
end
|
||||
|
||||
context 'with media attachments' do
|
||||
let(:object_json) do
|
||||
{
|
||||
id: 'bar',
|
||||
type: 'Note',
|
||||
content: 'Lorem ipsum',
|
||||
attachment: [
|
||||
{
|
||||
type: 'Document',
|
||||
mime_type: 'image/png',
|
||||
url: 'http://example.com/attachment.png',
|
||||
},
|
||||
],
|
||||
}
|
||||
end
|
||||
|
||||
it 'creates status' do
|
||||
status = sender.statuses.first
|
||||
|
||||
expect(status).to_not be_nil
|
||||
expect(status.media_attachments.map(&:remote_url)).to include('http://example.com/attachment.png')
|
||||
end
|
||||
end
|
||||
|
||||
context 'with hashtags' do
|
||||
let(:object_json) do
|
||||
{
|
||||
id: 'bar',
|
||||
type: 'Note',
|
||||
content: 'Lorem ipsum',
|
||||
tag: [
|
||||
{
|
||||
type: 'Hashtag',
|
||||
href: 'http://example.com/blah',
|
||||
name: '#test',
|
||||
},
|
||||
],
|
||||
}
|
||||
end
|
||||
|
||||
it 'creates status' do
|
||||
status = sender.statuses.first
|
||||
|
||||
expect(status).to_not be_nil
|
||||
expect(status.tags.map(&:name)).to include('test')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
28
spec/lib/activitypub/activity/delete_spec.rb
Normal file
28
spec/lib/activitypub/activity/delete_spec.rb
Normal file
|
@ -0,0 +1,28 @@
|
|||
require 'rails_helper'
|
||||
|
||||
RSpec.describe ActivityPub::Activity::Delete do
|
||||
let(:sender) { Fabricate(:account) }
|
||||
let(:status) { Fabricate(:status, account: sender, uri: 'foobar') }
|
||||
|
||||
let(:json) do
|
||||
{
|
||||
'@context': 'https://www.w3.org/ns/activitystreams',
|
||||
id: 'foo',
|
||||
type: 'Delete',
|
||||
actor: ActivityPub::TagManager.instance.uri_for(sender),
|
||||
object: ActivityPub::TagManager.instance.uri_for(status),
|
||||
}.with_indifferent_access
|
||||
end
|
||||
|
||||
describe '#perform' do
|
||||
subject { described_class.new(json, sender) }
|
||||
|
||||
before do
|
||||
subject.perform
|
||||
end
|
||||
|
||||
it 'deletes sender\'s status' do
|
||||
expect(Status.find_by(id: status.id)).to be_nil
|
||||
end
|
||||
end
|
||||
end
|
28
spec/lib/activitypub/activity/follow_spec.rb
Normal file
28
spec/lib/activitypub/activity/follow_spec.rb
Normal file
|
@ -0,0 +1,28 @@
|
|||
require 'rails_helper'
|
||||
|
||||
RSpec.describe ActivityPub::Activity::Follow do
|
||||
let(:sender) { Fabricate(:account) }
|
||||
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) }
|
||||
|
||||
before do
|
||||
subject.perform
|
||||
end
|
||||
|
||||
it 'creates a follow from sender to recipient' do
|
||||
expect(sender.following?(recipient)).to be true
|
||||
end
|
||||
end
|
||||
end
|
29
spec/lib/activitypub/activity/like_spec.rb
Normal file
29
spec/lib/activitypub/activity/like_spec.rb
Normal file
|
@ -0,0 +1,29 @@
|
|||
require 'rails_helper'
|
||||
|
||||
RSpec.describe ActivityPub::Activity::Like do
|
||||
let(:sender) { Fabricate(:account) }
|
||||
let(:recipient) { Fabricate(:account) }
|
||||
let(:status) { Fabricate(:status, account: recipient) }
|
||||
|
||||
let(:json) do
|
||||
{
|
||||
'@context': 'https://www.w3.org/ns/activitystreams',
|
||||
id: 'foo',
|
||||
type: 'Like',
|
||||
actor: ActivityPub::TagManager.instance.uri_for(sender),
|
||||
object: ActivityPub::TagManager.instance.uri_for(status),
|
||||
}.with_indifferent_access
|
||||
end
|
||||
|
||||
describe '#perform' do
|
||||
subject { described_class.new(json, sender) }
|
||||
|
||||
before do
|
||||
subject.perform
|
||||
end
|
||||
|
||||
it 'creates a favourite from sender to status' do
|
||||
expect(sender.favourited?(status)).to be true
|
||||
end
|
||||
end
|
||||
end
|
107
spec/lib/activitypub/activity/undo_spec.rb
Normal file
107
spec/lib/activitypub/activity/undo_spec.rb
Normal file
|
@ -0,0 +1,107 @@
|
|||
require 'rails_helper'
|
||||
|
||||
RSpec.describe ActivityPub::Activity::Undo do
|
||||
let(:sender) { Fabricate(:account) }
|
||||
|
||||
let(:json) do
|
||||
{
|
||||
'@context': 'https://www.w3.org/ns/activitystreams',
|
||||
id: 'foo',
|
||||
type: 'Undo',
|
||||
actor: ActivityPub::TagManager.instance.uri_for(sender),
|
||||
object: object_json,
|
||||
}.with_indifferent_access
|
||||
end
|
||||
|
||||
subject { described_class.new(json, sender) }
|
||||
|
||||
describe '#perform' do
|
||||
context 'with Announce' do
|
||||
let(:status) { Fabricate(:status) }
|
||||
|
||||
let(:object_json) do
|
||||
{
|
||||
id: 'bar',
|
||||
type: 'Announce',
|
||||
actor: ActivityPub::TagManager.instance.uri_for(sender),
|
||||
object: ActivityPub::TagManager.instance.uri_for(status),
|
||||
}
|
||||
end
|
||||
|
||||
before do
|
||||
Fabricate(:status, reblog: status, account: sender, uri: 'bar')
|
||||
end
|
||||
|
||||
it 'deletes the reblog' do
|
||||
subject.perform
|
||||
expect(sender.reblogged?(status)).to be false
|
||||
end
|
||||
end
|
||||
|
||||
context 'with Block' do
|
||||
let(:recipient) { Fabricate(:account) }
|
||||
|
||||
let(:object_json) do
|
||||
{
|
||||
id: 'bar',
|
||||
type: 'Block',
|
||||
actor: ActivityPub::TagManager.instance.uri_for(sender),
|
||||
object: ActivityPub::TagManager.instance.uri_for(recipient),
|
||||
}
|
||||
end
|
||||
|
||||
before do
|
||||
sender.block!(recipient)
|
||||
end
|
||||
|
||||
it 'deletes block from sender to recipient' do
|
||||
subject.perform
|
||||
expect(sender.blocking?(recipient)).to be false
|
||||
end
|
||||
end
|
||||
|
||||
context 'with Follow' do
|
||||
let(:recipient) { Fabricate(:account) }
|
||||
|
||||
let(:object_json) do
|
||||
{
|
||||
id: 'bar',
|
||||
type: 'Follow',
|
||||
actor: ActivityPub::TagManager.instance.uri_for(sender),
|
||||
object: ActivityPub::TagManager.instance.uri_for(recipient),
|
||||
}
|
||||
end
|
||||
|
||||
before do
|
||||
sender.follow!(recipient)
|
||||
end
|
||||
|
||||
it 'deletes follow from sender to recipient' do
|
||||
subject.perform
|
||||
expect(sender.following?(recipient)).to be false
|
||||
end
|
||||
end
|
||||
|
||||
context 'with Like' do
|
||||
let(:status) { Fabricate(:status) }
|
||||
|
||||
let(:object_json) do
|
||||
{
|
||||
id: 'bar',
|
||||
type: 'Like',
|
||||
actor: ActivityPub::TagManager.instance.uri_for(sender),
|
||||
object: ActivityPub::TagManager.instance.uri_for(status),
|
||||
}
|
||||
end
|
||||
|
||||
before do
|
||||
Fabricate(:favourite, account: sender, status: status)
|
||||
end
|
||||
|
||||
it 'deletes favourite from sender to status' do
|
||||
subject.perform
|
||||
expect(sender.favourited?(status)).to be false
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
41
spec/lib/activitypub/activity/update_spec.rb
Normal file
41
spec/lib/activitypub/activity/update_spec.rb
Normal file
|
@ -0,0 +1,41 @@
|
|||
require 'rails_helper'
|
||||
|
||||
RSpec.describe ActivityPub::Activity::Update do
|
||||
let!(:sender) { Fabricate(:account) }
|
||||
|
||||
before do
|
||||
sender.update!(uri: ActivityPub::TagManager.instance.uri_for(sender))
|
||||
end
|
||||
|
||||
let(:modified_sender) do
|
||||
sender.dup.tap do |modified_sender|
|
||||
modified_sender.display_name = 'Totally modified now'
|
||||
end
|
||||
end
|
||||
|
||||
let(:actor_json) do
|
||||
ActiveModelSerializers::SerializableResource.new(modified_sender, serializer: ActivityPub::ActorSerializer, key_transform: :camel_lower).as_json
|
||||
end
|
||||
|
||||
let(:json) do
|
||||
{
|
||||
'@context': 'https://www.w3.org/ns/activitystreams',
|
||||
id: 'foo',
|
||||
type: 'Update',
|
||||
actor: ActivityPub::TagManager.instance.uri_for(sender),
|
||||
object: actor_json,
|
||||
}.with_indifferent_access
|
||||
end
|
||||
|
||||
describe '#perform' do
|
||||
subject { described_class.new(json, sender) }
|
||||
|
||||
before do
|
||||
subject.perform
|
||||
end
|
||||
|
||||
it 'updates profile' do
|
||||
expect(sender.reload.display_name).to eq 'Totally modified now'
|
||||
end
|
||||
end
|
||||
end
|
99
spec/lib/activitypub/tag_manager_spec.rb
Normal file
99
spec/lib/activitypub/tag_manager_spec.rb
Normal file
|
@ -0,0 +1,99 @@
|
|||
require 'rails_helper'
|
||||
|
||||
RSpec.describe ActivityPub::TagManager do
|
||||
include RoutingHelper
|
||||
|
||||
subject { described_class.instance }
|
||||
|
||||
describe '#url_for' do
|
||||
it 'returns a string' do
|
||||
account = Fabricate(:account)
|
||||
expect(subject.url_for(account)).to be_a String
|
||||
end
|
||||
end
|
||||
|
||||
describe '#uri_for' do
|
||||
it 'returns a string' do
|
||||
account = Fabricate(:account)
|
||||
expect(subject.uri_for(account)).to be_a String
|
||||
end
|
||||
end
|
||||
|
||||
describe '#to' do
|
||||
it 'returns public collection for public status' do
|
||||
status = Fabricate(:status, visibility: :public)
|
||||
expect(subject.to(status)).to eq ['https://www.w3.org/ns/activitystreams#Public']
|
||||
end
|
||||
|
||||
it 'returns followers collection for unlisted status' do
|
||||
status = Fabricate(:status, visibility: :unlisted)
|
||||
expect(subject.to(status)).to eq [account_followers_url(status.account)]
|
||||
end
|
||||
|
||||
it 'returns followers collection for private status' do
|
||||
status = Fabricate(:status, visibility: :private)
|
||||
expect(subject.to(status)).to eq [account_followers_url(status.account)]
|
||||
end
|
||||
|
||||
it 'returns URIs of mentions for direct status' do
|
||||
status = Fabricate(:status, visibility: :direct)
|
||||
mentioned = Fabricate(:account)
|
||||
status.mentions.create(account: mentioned)
|
||||
expect(subject.to(status)).to eq [subject.uri_for(mentioned)]
|
||||
end
|
||||
end
|
||||
|
||||
describe '#cc' do
|
||||
it 'returns followers collection for public status' do
|
||||
status = Fabricate(:status, visibility: :public)
|
||||
expect(subject.cc(status)).to eq [account_followers_url(status.account)]
|
||||
end
|
||||
|
||||
it 'returns public collection for unlisted status' do
|
||||
status = Fabricate(:status, visibility: :unlisted)
|
||||
expect(subject.cc(status)).to eq ['https://www.w3.org/ns/activitystreams#Public']
|
||||
end
|
||||
|
||||
it 'returns empty array for private status' do
|
||||
status = Fabricate(:status, visibility: :private)
|
||||
expect(subject.cc(status)).to eq []
|
||||
end
|
||||
|
||||
it 'returns empty array for direct status' do
|
||||
status = Fabricate(:status, visibility: :direct)
|
||||
expect(subject.cc(status)).to eq []
|
||||
end
|
||||
|
||||
it 'returns URIs of mentions for non-direct status' do
|
||||
status = Fabricate(:status, visibility: :public)
|
||||
mentioned = Fabricate(:account)
|
||||
status.mentions.create(account: mentioned)
|
||||
expect(subject.cc(status)).to include(subject.uri_for(mentioned))
|
||||
end
|
||||
end
|
||||
|
||||
describe '#local_uri?' do
|
||||
it 'returns false for non-local URI' do
|
||||
expect(subject.local_uri?('http://example.com/123')).to be false
|
||||
end
|
||||
|
||||
it 'returns true for local URIs' do
|
||||
account = Fabricate(:account)
|
||||
expect(subject.local_uri?(subject.uri_for(account))).to be true
|
||||
end
|
||||
end
|
||||
|
||||
describe '#uri_to_local_id' do
|
||||
it 'returns the local ID' do
|
||||
account = Fabricate(:account)
|
||||
expect(subject.uri_to_local_id(subject.uri_for(account), :username)).to eq account.username
|
||||
end
|
||||
end
|
||||
|
||||
describe '#uri_to_resource' do
|
||||
it 'returns the local resource' do
|
||||
account = Fabricate(:account)
|
||||
expect(subject.uri_to_resource(subject.uri_for(account), Account)).to eq account
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,96 @@
|
|||
require 'rails_helper'
|
||||
|
||||
RSpec.describe ActivityPub::FetchRemoteAccountService do
|
||||
subject { ActivityPub::FetchRemoteAccountService.new }
|
||||
|
||||
let!(:actor) do
|
||||
{
|
||||
'@context': 'https://www.w3.org/ns/activitystreams',
|
||||
id: 'https://example.com/alice',
|
||||
type: 'Person',
|
||||
preferredUsername: 'alice',
|
||||
name: 'Alice',
|
||||
summary: 'Foo bar',
|
||||
}
|
||||
end
|
||||
|
||||
describe '#call' do
|
||||
let(:account) { subject.call('https://example.com/alice') }
|
||||
|
||||
shared_examples 'sets profile data' do
|
||||
it 'returns an account' do
|
||||
expect(account).to be_an Account
|
||||
end
|
||||
|
||||
it 'sets display name' do
|
||||
expect(account.display_name).to eq 'Alice'
|
||||
end
|
||||
|
||||
it 'sets note' do
|
||||
expect(account.note).to eq 'Foo bar'
|
||||
end
|
||||
|
||||
it 'sets URL' do
|
||||
expect(account.url).to eq 'https://example.com/alice'
|
||||
end
|
||||
end
|
||||
|
||||
context 'when URI and WebFinger share the same host' do
|
||||
let!(:webfinger) { { subject: 'acct:alice@example.com', links: [{ rel: 'self', href: 'https://example.com/alice' }] } }
|
||||
|
||||
before do
|
||||
stub_request(:get, 'https://example.com/alice').to_return(body: Oj.dump(actor))
|
||||
stub_request(:get, 'https://example.com/.well-known/webfinger?resource=acct:alice@example.com').to_return(body: Oj.dump(webfinger), headers: { 'Content-Type': 'application/jrd+json' })
|
||||
end
|
||||
|
||||
it 'fetches resource' do
|
||||
account
|
||||
expect(a_request(:get, 'https://example.com/alice')).to have_been_made.once
|
||||
end
|
||||
|
||||
it 'looks up webfinger' do
|
||||
account
|
||||
expect(a_request(:get, 'https://example.com/.well-known/webfinger?resource=acct:alice@example.com')).to have_been_made.once
|
||||
end
|
||||
|
||||
it 'sets username and domain from webfinger' do
|
||||
expect(account.username).to eq 'alice'
|
||||
expect(account.domain).to eq 'example.com'
|
||||
end
|
||||
|
||||
include_examples 'sets profile data'
|
||||
end
|
||||
|
||||
context 'when WebFinger presents different domain than URI' do
|
||||
let!(:webfinger) { { subject: 'acct:alice@iscool.af', links: [{ rel: 'self', href: 'https://example.com/alice' }] } }
|
||||
|
||||
before do
|
||||
stub_request(:get, 'https://example.com/alice').to_return(body: Oj.dump(actor))
|
||||
stub_request(:get, 'https://example.com/.well-known/webfinger?resource=acct:alice@example.com').to_return(body: Oj.dump(webfinger), headers: { 'Content-Type': 'application/jrd+json' })
|
||||
stub_request(:get, 'https://iscool.af/.well-known/webfinger?resource=acct:alice@iscool.af').to_return(body: Oj.dump(webfinger), headers: { 'Content-Type': 'application/jrd+json' })
|
||||
end
|
||||
|
||||
it 'fetches resource' do
|
||||
account
|
||||
expect(a_request(:get, 'https://example.com/alice')).to have_been_made.once
|
||||
end
|
||||
|
||||
it 'looks up webfinger' do
|
||||
account
|
||||
expect(a_request(:get, 'https://example.com/.well-known/webfinger?resource=acct:alice@example.com')).to have_been_made.once
|
||||
end
|
||||
|
||||
it 'looks up "redirected" webfinger' do
|
||||
account
|
||||
expect(a_request(:get, 'https://iscool.af/.well-known/webfinger?resource=acct:alice@iscool.af')).to have_been_made.once
|
||||
end
|
||||
|
||||
it 'sets username and domain from final webfinger' do
|
||||
expect(account.username).to eq 'alice'
|
||||
expect(account.domain).to eq 'iscool.af'
|
||||
end
|
||||
|
||||
include_examples 'sets profile data'
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,5 @@
|
|||
require 'rails_helper'
|
||||
|
||||
RSpec.describe ActivityPub::FetchRemoteStatusService do
|
||||
pending
|
||||
end
|
|
@ -0,0 +1,5 @@
|
|||
require 'rails_helper'
|
||||
|
||||
RSpec.describe ActivityPub::ProcessAccountService do
|
||||
pending
|
||||
end
|
|
@ -0,0 +1,9 @@
|
|||
require 'rails_helper'
|
||||
|
||||
RSpec.describe ActivityPub::ProcessCollectionService do
|
||||
subject { ActivityPub::ProcessCollectionService.new }
|
||||
|
||||
describe '#call' do
|
||||
pending
|
||||
end
|
||||
end
|
Loading…
Add table
Add a link
Reference in a new issue