about summary refs log tree commit diff
path: root/spec
diff options
context:
space:
mode:
authorEugen Rochko <eugen@zeonfederated.com>2017-08-13 00:44:41 +0200
committerGitHub <noreply@github.com>2017-08-13 00:44:41 +0200
commitb7370ac8baa643d93ea727699b3b11f9d3a55bea (patch)
tree869a8c2d44f78d96255ae0bf20a84c150ca23702 /spec
parentccdd5a9576819cdc95946d98fea0e3c8bbd1d626 (diff)
ActivityPub delivery (#4566)
* Deliver ActivityPub Like

* Deliver ActivityPub Undo-Like

* Deliver ActivityPub Create/Announce activities

* Deliver ActivityPub creates from mentions

* Deliver ActivityPub Block/Undo-Block

* Deliver ActivityPub Accept/Reject-Follow

* Deliver ActivityPub Undo-Follow

* Deliver ActivityPub Follow

* Deliver ActivityPub Delete activities

Incidentally fix #889

* Adjust BatchedRemoveStatusService for ActivityPub

* Add tests for ActivityPub workers

* Add tests for FollowService

* Add tests for FavouriteService, UnfollowService and PostStatusService

* Add tests for ReblogService, BlockService, UnblockService, ProcessMentionsService

* Add tests for AuthorizeFollowService, RejectFollowService, RemoveStatusService

* Add tests for BatchedRemoveStatusService

* Deliver updates to a local account to ActivityPub followers

* Minor adjustments
Diffstat (limited to 'spec')
-rw-r--r--spec/controllers/api/v1/accounts/credentials_controller_spec.rb6
-rw-r--r--spec/controllers/settings/profiles_controller_spec.rb2
-rw-r--r--spec/services/authorize_follow_service_spec.rb24
-rw-r--r--spec/services/batched_remove_status_service_spec.rb7
-rw-r--r--spec/services/block_service_spec.rb19
-rw-r--r--spec/services/favourite_service_spec.rb22
-rw-r--r--spec/services/follow_service_spec.rb25
-rw-r--r--spec/services/post_status_service_spec.rb8
-rw-r--r--spec/services/process_mentions_service_spec.rb46
-rw-r--r--spec/services/reblog_service_spec.rb49
-rw-r--r--spec/services/reject_follow_service_spec.rb24
-rw-r--r--spec/services/remove_status_service_spec.rb8
-rw-r--r--spec/services/resolve_remote_account_service_spec.rb66
-rw-r--r--spec/services/unblock_service_spec.rb22
-rw-r--r--spec/services/unfollow_service_spec.rb22
-rw-r--r--spec/workers/activitypub/delivery_worker_spec.rb23
-rw-r--r--spec/workers/activitypub/distribution_worker_spec.rb48
-rw-r--r--spec/workers/activitypub/processing_worker_spec.rb15
-rw-r--r--spec/workers/activitypub/thread_resolve_worker_spec.rb16
-rw-r--r--spec/workers/activitypub/update_distribution_worker_spec.rb20
20 files changed, 403 insertions, 69 deletions
diff --git a/spec/controllers/api/v1/accounts/credentials_controller_spec.rb b/spec/controllers/api/v1/accounts/credentials_controller_spec.rb
index 4a3100348..bc89772b9 100644
--- a/spec/controllers/api/v1/accounts/credentials_controller_spec.rb
+++ b/spec/controllers/api/v1/accounts/credentials_controller_spec.rb
@@ -20,6 +20,8 @@ describe Api::V1::Accounts::CredentialsController do
   describe 'PATCH #update' do
     describe 'with valid data' do
       before do
+        allow(ActivityPub::UpdateDistributionWorker).to receive(:perform_async)
+
         patch :update, params: {
           display_name: "Alice Isn't Dead",
           note: "Hi!\n\nToot toot!",
@@ -40,6 +42,10 @@ describe Api::V1::Accounts::CredentialsController do
         expect(user.account.avatar).to exist
         expect(user.account.header).to exist
       end
+
+      it 'queues up an account update distribution' do
+        expect(ActivityPub::UpdateDistributionWorker).to have_received(:perform_async).with(user.account_id)
+      end
     end
 
     describe 'with invalid data' do
diff --git a/spec/controllers/settings/profiles_controller_spec.rb b/spec/controllers/settings/profiles_controller_spec.rb
index e502dbda7..ee3315be6 100644
--- a/spec/controllers/settings/profiles_controller_spec.rb
+++ b/spec/controllers/settings/profiles_controller_spec.rb
@@ -17,11 +17,13 @@ RSpec.describe Settings::ProfilesController, type: :controller do
 
   describe 'PUT #update' do
     it 'updates the user profile' do
+      allow(ActivityPub::UpdateDistributionWorker).to receive(:perform_async)
       account = Fabricate(:account, user: @user, display_name: 'Old name')
 
       put :update, params: { account: { display_name: 'New name' } }
       expect(account.reload.display_name).to eq 'New name'
       expect(response).to redirect_to(settings_profile_path)
+      expect(ActivityPub::UpdateDistributionWorker).to have_received(:perform_async).with(account.id)
     end
   end
 end
diff --git a/spec/services/authorize_follow_service_spec.rb b/spec/services/authorize_follow_service_spec.rb
index 3f3a2bc56..d74eb41a2 100644
--- a/spec/services/authorize_follow_service_spec.rb
+++ b/spec/services/authorize_follow_service_spec.rb
@@ -22,7 +22,7 @@ RSpec.describe AuthorizeFollowService do
     end
   end
 
-  describe 'remote' do
+  describe 'remote OStatus' do
     let(:bob) { Fabricate(:user, email: 'bob@example.com', account: Fabricate(:account, username: 'bob', domain: 'example.com', salmon_url: 'http://salmon.example.com')).account }
 
     before do
@@ -46,4 +46,26 @@ RSpec.describe AuthorizeFollowService do
       }).to have_been_made.once
     end
   end
+
+  describe 'remote ActivityPub' do
+    let(:bob) { Fabricate(:user, email: 'bob@example.com', account: Fabricate(:account, username: 'bob', domain: 'example.com', protocol: :activitypub, inbox_url: 'http://example.com/inbox')).account }
+
+    before do
+      FollowRequest.create(account: bob, target_account: sender)
+      stub_request(:post, bob.inbox_url).to_return(status: 200)
+      subject.call(bob, sender)
+    end
+
+    it 'removes follow request' do
+      expect(bob.requested?(sender)).to be false
+    end
+
+    it 'creates follow relation' do
+      expect(bob.following?(sender)).to be true
+    end
+
+    it 'sends an accept activity' do
+      expect(a_request(:post, bob.inbox_url)).to have_been_made.once
+    end
+  end
 end
diff --git a/spec/services/batched_remove_status_service_spec.rb b/spec/services/batched_remove_status_service_spec.rb
index c20085e25..2484d4b58 100644
--- a/spec/services/batched_remove_status_service_spec.rb
+++ b/spec/services/batched_remove_status_service_spec.rb
@@ -6,6 +6,7 @@ RSpec.describe BatchedRemoveStatusService do
   let!(:alice)  { Fabricate(:account) }
   let!(:bob)    { Fabricate(:account, username: 'bob', domain: 'example.com', salmon_url: 'http://example.com/salmon') }
   let!(:jeff)   { Fabricate(:account) }
+  let!(:hank)   { Fabricate(:account, username: 'hank', protocol: :activitypub, domain: 'example.com', inbox_url: 'http://example.com/inbox') }
 
   let(:status1) { PostStatusService.new.call(alice, 'Hello @bob@example.com') }
   let(:status2) { PostStatusService.new.call(alice, 'Another status') }
@@ -15,9 +16,11 @@ RSpec.describe BatchedRemoveStatusService do
 
     stub_request(:post, 'http://example.com/push').to_return(status: 200, body: '', headers: {})
     stub_request(:post, 'http://example.com/salmon').to_return(status: 200, body: '', headers: {})
+    stub_request(:post, 'http://example.com/inbox').to_return(status: 200)
 
     Fabricate(:subscription, account: alice, callback_url: 'http://example.com/push', confirmed: true, expires_at: 30.days.from_now)
     jeff.follow!(alice)
+    hank.follow!(alice)
 
     status1
     status2
@@ -58,4 +61,8 @@ RSpec.describe BatchedRemoveStatusService do
       xml.match(TagManager::VERBS[:delete])
     }).to have_been_made.once
   end
+
+  it 'sends delete activity to followers' do
+    expect(a_request(:post, 'http://example.com/inbox')).to have_been_made.at_least_once
+  end
 end
diff --git a/spec/services/block_service_spec.rb b/spec/services/block_service_spec.rb
index 2a54e032e..bd2ab3d53 100644
--- a/spec/services/block_service_spec.rb
+++ b/spec/services/block_service_spec.rb
@@ -17,7 +17,7 @@ RSpec.describe BlockService do
     end
   end
 
-  describe 'remote' do
+  describe 'remote OStatus' do
     let(:bob) { Fabricate(:user, email: 'bob@example.com', account: Fabricate(:account, username: 'bob', domain: 'example.com', salmon_url: 'http://salmon.example.com')).account }
 
     before do
@@ -36,4 +36,21 @@ RSpec.describe BlockService do
       }).to have_been_made.once
     end
   end
+
+  describe 'remote ActivityPub' do
+    let(:bob) { Fabricate(:user, email: 'bob@example.com', account: Fabricate(:account, username: 'bob', protocol: :activitypub, domain: 'example.com', inbox_url: 'http://example.com/inbox')).account }
+
+    before do
+      stub_request(:post, 'http://example.com/inbox').to_return(status: 200)
+      subject.call(sender, bob)
+    end
+
+    it 'creates a blocking relation' do
+      expect(sender.blocking?(bob)).to be true
+    end
+
+    it 'sends a block activity' do
+      expect(a_request(:post, 'http://example.com/inbox')).to have_been_made.once
+    end
+  end
 end
diff --git a/spec/services/favourite_service_spec.rb b/spec/services/favourite_service_spec.rb
index 36f1b64d4..2ab1f32ca 100644
--- a/spec/services/favourite_service_spec.rb
+++ b/spec/services/favourite_service_spec.rb
@@ -18,8 +18,8 @@ RSpec.describe FavouriteService do
     end
   end
 
-  describe 'remote' do
-    let(:bob)    { Fabricate(:user, email: 'bob@example.com', account: Fabricate(:account, username: 'bob', domain: 'example.com', salmon_url: 'http://salmon.example.com')).account }
+  describe 'remote OStatus' do
+    let(:bob)    { Fabricate(:user, email: 'bob@example.com', account: Fabricate(:account, username: 'bob', protocol: :ostatus, domain: 'example.com', salmon_url: 'http://salmon.example.com')).account }
     let(:status) { Fabricate(:status, account: bob, uri: 'tag:example.com:blahblah') }
 
     before do
@@ -38,4 +38,22 @@ RSpec.describe FavouriteService do
       }).to have_been_made.once
     end
   end
+
+  describe 'remote ActivityPub' do
+    let(:bob)    { Fabricate(:user, email: 'bob@example.com', account: Fabricate(:account, protocol: :activitypub, username: 'bob', domain: 'example.com', inbox_url: 'http://example.com/inbox')).account }
+    let(:status) { Fabricate(:status, account: bob) }
+
+    before do
+      stub_request(:post, "http://example.com/inbox").to_return(:status => 200, :body => "", :headers => {})
+      subject.call(sender, status)
+    end
+
+    it 'creates a favourite' do
+      expect(status.favourites.first).to_not be_nil
+    end
+
+    it 'sends a like activity' do
+      expect(a_request(:post, "http://example.com/inbox")).to have_been_made.once
+    end
+  end
 end
diff --git a/spec/services/follow_service_spec.rb b/spec/services/follow_service_spec.rb
index 32dedb3ad..1e2378031 100644
--- a/spec/services/follow_service_spec.rb
+++ b/spec/services/follow_service_spec.rb
@@ -44,9 +44,9 @@ RSpec.describe FollowService do
     end
   end
 
-  context 'remote account' do
+  context 'remote OStatus account' do
     describe 'locked account' do
-      let(:bob) { Fabricate(:user, email: 'bob@example.com', account: Fabricate(:account, locked: true, username: 'bob', domain: 'example.com', salmon_url: 'http://salmon.example.com')).account }
+      let(:bob) { Fabricate(:user, email: 'bob@example.com', account: Fabricate(:account, protocol: :ostatus, locked: true, username: 'bob', domain: 'example.com', salmon_url: 'http://salmon.example.com')).account }
 
       before do
         stub_request(:post, "http://salmon.example.com/").to_return(:status => 200, :body => "", :headers => {})
@@ -66,7 +66,7 @@ RSpec.describe FollowService do
     end
 
     describe 'unlocked account' do
-      let(:bob) { Fabricate(:user, email: 'bob@example.com', account: Fabricate(:account, username: 'bob', domain: 'example.com', salmon_url: 'http://salmon.example.com', hub_url: 'http://hub.example.com')).account }
+      let(:bob) { Fabricate(:user, email: 'bob@example.com', account: Fabricate(:account, protocol: :ostatus, username: 'bob', domain: 'example.com', salmon_url: 'http://salmon.example.com', hub_url: 'http://hub.example.com')).account }
 
       before do
         stub_request(:post, "http://salmon.example.com/").to_return(:status => 200, :body => "", :headers => {})
@@ -91,7 +91,7 @@ RSpec.describe FollowService do
     end
 
     describe 'already followed account' do
-      let(:bob) { Fabricate(:user, email: 'bob@example.com', account: Fabricate(:account, username: 'bob', domain: 'example.com', salmon_url: 'http://salmon.example.com', hub_url: 'http://hub.example.com')).account }
+      let(:bob) { Fabricate(:user, email: 'bob@example.com', account: Fabricate(:account, protocol: :ostatus, username: 'bob', domain: 'example.com', salmon_url: 'http://salmon.example.com', hub_url: 'http://hub.example.com')).account }
 
       before do
         sender.follow!(bob)
@@ -111,4 +111,21 @@ RSpec.describe FollowService do
       end
     end
   end
+
+  context 'remote ActivityPub account' do
+    let(:bob) { Fabricate(:user, account: Fabricate(:account, username: 'bob', domain: 'example.com', protocol: :activitypub, inbox_url: 'http://example.com/inbox')).account }
+
+    before do
+      stub_request(:post, "http://example.com/inbox").to_return(:status => 200, :body => "", :headers => {})
+      subject.call(sender, bob.acct)
+    end
+
+    it 'creates follow request' do
+      expect(FollowRequest.find_by(account: sender, target_account: bob)).to_not be_nil
+    end
+
+    it 'sends a follow activity to the inbox' do
+      expect(a_request(:post, 'http://example.com/inbox')).to have_been_made.once
+    end
+  end
 end
diff --git a/spec/services/post_status_service_spec.rb b/spec/services/post_status_service_spec.rb
index 57876dcc2..4182c4e1f 100644
--- a/spec/services/post_status_service_spec.rb
+++ b/spec/services/post_status_service_spec.rb
@@ -100,16 +100,18 @@ RSpec.describe PostStatusService do
     expect(hashtags_service).to have_received(:call).with(status)
   end
 
-  it 'pings PuSH hubs' do
+  it 'gets distributed' do
     allow(DistributionWorker).to receive(:perform_async)
     allow(Pubsubhubbub::DistributionWorker).to receive(:perform_async)
+    allow(ActivityPub::DistributionWorker).to receive(:perform_async)
+
     account = Fabricate(:account)
 
     status = subject.call(account, "test status update")
 
     expect(DistributionWorker).to have_received(:perform_async).with(status.id)
-    expect(Pubsubhubbub::DistributionWorker).
-      to have_received(:perform_async).with(status.stream_entry.id)
+    expect(Pubsubhubbub::DistributionWorker).to have_received(:perform_async).with(status.stream_entry.id)
+    expect(ActivityPub::DistributionWorker).to have_received(:perform_async).with(status.id)
   end
 
   it 'crawls links' do
diff --git a/spec/services/process_mentions_service_spec.rb b/spec/services/process_mentions_service_spec.rb
index 984d13746..09f8fa45b 100644
--- a/spec/services/process_mentions_service_spec.rb
+++ b/spec/services/process_mentions_service_spec.rb
@@ -1,22 +1,44 @@
 require 'rails_helper'
 
 RSpec.describe ProcessMentionsService do
-  let(:account)     { Fabricate(:account, username: 'alice') }
-  let(:remote_user) { Fabricate(:account, username: 'remote_user', domain: 'example.com', salmon_url: 'http://salmon.example.com') }
-  let(:status)      { Fabricate(:status, account: account, text: "Hello @#{remote_user.acct}") }
+  let(:account) { Fabricate(:account, username: 'alice') }
+  let(:status)  { Fabricate(:status, account: account, text: "Hello @#{remote_user.acct}") }
 
-  subject { ProcessMentionsService.new }
+  context 'OStatus' do
+    let(:remote_user) { Fabricate(:account, username: 'remote_user', protocol: :ostatus, domain: 'example.com', salmon_url: 'http://salmon.example.com') }
 
-  before do
-    stub_request(:post, remote_user.salmon_url)
-    subject.(status)
-  end
+    subject { ProcessMentionsService.new }
+
+    before do
+      stub_request(:post, remote_user.salmon_url)
+      subject.call(status)
+    end
 
-  it 'creates a mention' do
-    expect(remote_user.mentions.where(status: status).count).to eq 1
+    it 'creates a mention' do
+      expect(remote_user.mentions.where(status: status).count).to eq 1
+    end
+
+    it 'posts to remote user\'s Salmon end point' do
+      expect(a_request(:post, remote_user.salmon_url)).to have_been_made.once
+    end
   end
 
-  it 'posts to remote user\'s Salmon end point' do
-    expect(a_request(:post, remote_user.salmon_url)).to have_been_made
+  context 'ActivityPub' do
+    let(:remote_user) { Fabricate(:account, username: 'remote_user', protocol: :activitypub, domain: 'example.com', inbox_url: 'http://example.com/inbox') }
+
+    subject { ProcessMentionsService.new }
+
+    before do
+      stub_request(:post, remote_user.inbox_url)
+      subject.call(status)
+    end
+
+    it 'creates a mention' do
+      expect(remote_user.mentions.where(status: status).count).to eq 1
+    end
+
+    it 'sends activity to the inbox' do
+      expect(a_request(:post, remote_user.inbox_url)).to have_been_made.once
+    end
   end
 end
diff --git a/spec/services/reblog_service_spec.rb b/spec/services/reblog_service_spec.rb
index 5f89169e9..0ad5c5f6b 100644
--- a/spec/services/reblog_service_spec.rb
+++ b/spec/services/reblog_service_spec.rb
@@ -2,22 +2,49 @@ require 'rails_helper'
 
 RSpec.describe ReblogService do
   let(:alice)  { Fabricate(:account, username: 'alice') }
-  let(:bob)    { Fabricate(:account, username: 'bob', domain: 'example.com', salmon_url: 'http://salmon.example.com') }
-  let(:status) { Fabricate(:status, account: bob, uri: 'tag:example.com;something:something') }
 
-  subject { ReblogService.new }
+  context 'OStatus' do
+    let(:bob)    { Fabricate(:account, username: 'bob', domain: 'example.com', salmon_url: 'http://salmon.example.com') }
+    let(:status) { Fabricate(:status, account: bob, uri: 'tag:example.com;something:something') }
 
-  before do
-    stub_request(:post, 'http://salmon.example.com')
+    subject { ReblogService.new }
 
-    subject.(alice, status)
-  end
+    before do
+      stub_request(:post, 'http://salmon.example.com')
+      subject.call(alice, status)
+    end
+
+    it 'creates a reblog' do
+      expect(status.reblogs.count).to eq 1
+    end
 
-  it 'creates a reblog' do
-    expect(status.reblogs.count).to eq 1
+    it 'sends a Salmon slap for a remote reblog' do
+      expect(a_request(:post, 'http://salmon.example.com')).to have_been_made
+    end
   end
 
-  it 'sends a Salmon slap for a remote reblog' do
-    expect(a_request(:post, 'http://salmon.example.com')).to have_been_made
+  context 'ActivityPub' do
+    let(:bob)    { Fabricate(:account, username: 'bob', protocol: :activitypub, domain: 'example.com', inbox_url: 'http://example.com/inbox') }
+    let(:status) { Fabricate(:status, account: bob) }
+
+    subject { ReblogService.new }
+
+    before do
+      stub_request(:post, bob.inbox_url)
+      allow(ActivityPub::DistributionWorker).to receive(:perform_async)
+      subject.call(alice, status)
+    end
+
+    it 'creates a reblog' do
+      expect(status.reblogs.count).to eq 1
+    end
+
+    it 'distributes to followers' do
+      expect(ActivityPub::DistributionWorker).to have_received(:perform_async)
+    end
+
+    it 'sends an announce activity to the author' do
+      expect(a_request(:post, bob.inbox_url)).to have_been_made.once
+    end
   end
 end
diff --git a/spec/services/reject_follow_service_spec.rb b/spec/services/reject_follow_service_spec.rb
index 50749b633..2e06345b3 100644
--- a/spec/services/reject_follow_service_spec.rb
+++ b/spec/services/reject_follow_service_spec.rb
@@ -22,7 +22,7 @@ RSpec.describe RejectFollowService do
     end
   end
 
-  describe 'remote' do
+  describe 'remote OStatus' do
     let(:bob) { Fabricate(:user, email: 'bob@example.com', account: Fabricate(:account, username: 'bob', domain: 'example.com', salmon_url: 'http://salmon.example.com')).account }
 
     before do
@@ -46,4 +46,26 @@ RSpec.describe RejectFollowService do
       }).to have_been_made.once
     end
   end
+
+  describe 'remote ActivityPub' do
+    let(:bob) { Fabricate(:user, email: 'bob@example.com', account: Fabricate(:account, username: 'bob', domain: 'example.com', protocol: :activitypub, inbox_url: 'http://example.com/inbox')).account }
+
+    before do
+      FollowRequest.create(account: bob, target_account: sender)
+      stub_request(:post, bob.inbox_url).to_return(status: 200)
+      subject.call(bob, sender)
+    end
+
+    it 'removes follow request' do
+      expect(bob.requested?(sender)).to be false
+    end
+
+    it 'does not create follow relation' do
+      expect(bob.following?(sender)).to be false
+    end
+
+    it 'sends a reject activity' do
+      expect(a_request(:post, bob.inbox_url)).to have_been_made.once
+    end
+  end
 end
diff --git a/spec/services/remove_status_service_spec.rb b/spec/services/remove_status_service_spec.rb
index a3bce7613..dc6b350cb 100644
--- a/spec/services/remove_status_service_spec.rb
+++ b/spec/services/remove_status_service_spec.rb
@@ -6,13 +6,17 @@ RSpec.describe RemoveStatusService do
   let!(:alice)  { Fabricate(:account) }
   let!(:bob)    { Fabricate(:account, username: 'bob', domain: 'example.com', salmon_url: 'http://example.com/salmon') }
   let!(:jeff)   { Fabricate(:account) }
+  let!(:hank)   { Fabricate(:account, username: 'hank', protocol: :activitypub, domain: 'example.com', inbox_url: 'http://example.com/inbox') }
 
   before do
     stub_request(:post, 'http://example.com/push').to_return(status: 200, body: '', headers: {})
     stub_request(:post, 'http://example.com/salmon').to_return(status: 200, body: '', headers: {})
+    stub_request(:post, 'http://example.com/inbox').to_return(status: 200)
 
     Fabricate(:subscription, account: alice, callback_url: 'http://example.com/push', confirmed: true, expires_at: 30.days.from_now)
     jeff.follow!(alice)
+    hank.follow!(alice)
+
     @status = PostStatusService.new.call(alice, 'Hello @bob@example.com')
     subject.call(@status)
   end
@@ -31,6 +35,10 @@ RSpec.describe RemoveStatusService do
     }).to have_been_made
   end
 
+  it 'sends delete activity to followers' do
+    expect(a_request(:post, 'http://example.com/inbox')).to have_been_made.twice
+  end
+
   it 'sends Salmon slap to previously mentioned users' do
     expect(a_request(:post, "http://example.com/salmon").with { |req|
       xml = OStatus2::Salmon.new.unpack(req.body)
diff --git a/spec/services/resolve_remote_account_service_spec.rb b/spec/services/resolve_remote_account_service_spec.rb
index c3b902b34..d0eab2310 100644
--- a/spec/services/resolve_remote_account_service_spec.rb
+++ b/spec/services/resolve_remote_account_service_spec.rb
@@ -1,7 +1,7 @@
 require 'rails_helper'
 
 RSpec.describe ResolveRemoteAccountService do
-  subject { ResolveRemoteAccountService.new }
+  subject { described_class.new }
 
   before do
     stub_request(:get, "https://quitter.no/.well-known/host-meta").to_return(request_fixture('.host-meta.txt'))
@@ -29,29 +29,6 @@ RSpec.describe ResolveRemoteAccountService do
     expect(subject.call('catsrgr8@example.com')).to be_nil
   end
 
-  it 'returns an already existing remote account' do
-    old_account      = Fabricate(:account, username: 'gargron', domain: 'quitter.no')
-    returned_account = subject.call('gargron@quitter.no')
-
-    expect(old_account.id).to eq returned_account.id
-  end
-
-  it 'returns a new remote account' do
-    account = subject.call('gargron@quitter.no')
-
-    expect(account.username).to eq 'gargron'
-    expect(account.domain).to eq 'quitter.no'
-    expect(account.remote_url).to eq 'https://quitter.no/api/statuses/user_timeline/7477.atom'
-  end
-
-  it 'follows a legitimate account redirection' do
-    account = subject.call('gargron@redirected.com')
-
-    expect(account.username).to eq 'gargron'
-    expect(account.domain).to eq 'quitter.no'
-    expect(account.remote_url).to eq 'https://quitter.no/api/statuses/user_timeline/7477.atom'
-  end
-
   it 'prevents hijacking existing accounts' do
     account = subject.call('hacker1@redirected.com')
     expect(account.salmon_url).to_not eq 'https://hacker.com/main/salmon/user/7477'
@@ -61,12 +38,41 @@ RSpec.describe ResolveRemoteAccountService do
     expect(subject.call('hacker2@redirected.com')).to be_nil
   end
 
-  it 'returns a new remote account' do
-    account = subject.call('foo@localdomain.com')
+  context 'with an OStatus account' do
+    it 'returns an already existing remote account' do
+      old_account      = Fabricate(:account, username: 'gargron', domain: 'quitter.no')
+      returned_account = subject.call('gargron@quitter.no')
+
+      expect(old_account.id).to eq returned_account.id
+    end
+
+    it 'returns a new remote account' do
+      account = subject.call('gargron@quitter.no')
+
+      expect(account.username).to eq 'gargron'
+      expect(account.domain).to eq 'quitter.no'
+      expect(account.remote_url).to eq 'https://quitter.no/api/statuses/user_timeline/7477.atom'
+    end
+
+    it 'follows a legitimate account redirection' do
+      account = subject.call('gargron@redirected.com')
+
+      expect(account.username).to eq 'gargron'
+      expect(account.domain).to eq 'quitter.no'
+      expect(account.remote_url).to eq 'https://quitter.no/api/statuses/user_timeline/7477.atom'
+    end
+
+    it 'returns a new remote account' do
+      account = subject.call('foo@localdomain.com')
+
+      expect(account.username).to eq 'foo'
+      expect(account.domain).to eq 'localdomain.com'
+      expect(account.remote_url).to eq 'https://webdomain.com/users/foo.atom'
+    end
+  end
 
-    expect(account.username).to eq 'foo'
-    expect(account.domain).to eq 'localdomain.com'
-    expect(account.remote_url).to eq 'https://webdomain.com/users/foo.atom'
+  context 'with an ActivityPub account' do
+    pending
   end
 
   it 'processes one remote account at a time using locks' do
@@ -78,7 +84,7 @@ RSpec.describe ResolveRemoteAccountService do
       Thread.new do
         true while wait_for_start
         begin
-          return_values << ResolveRemoteAccountService.new.call('foo@localdomain.com')
+          return_values << described_class.new.call('foo@localdomain.com')
         rescue ActiveRecord::RecordNotUnique
           fail_occurred = true
         end
diff --git a/spec/services/unblock_service_spec.rb b/spec/services/unblock_service_spec.rb
index 1b9ae1239..def4981e7 100644
--- a/spec/services/unblock_service_spec.rb
+++ b/spec/services/unblock_service_spec.rb
@@ -18,7 +18,7 @@ RSpec.describe UnblockService do
     end
   end
 
-  describe 'remote' do
+  describe 'remote OStatus' do
     let(:bob) { Fabricate(:user, email: 'bob@example.com', account: Fabricate(:account, username: 'bob', domain: 'example.com', salmon_url: 'http://salmon.example.com')).account }
 
     before do
@@ -28,7 +28,7 @@ RSpec.describe UnblockService do
     end
 
     it 'destroys the blocking relation' do
-      expect(sender.following?(bob)).to be false
+      expect(sender.blocking?(bob)).to be false
     end
 
     it 'sends an unblock salmon slap' do
@@ -38,4 +38,22 @@ RSpec.describe UnblockService do
       }).to have_been_made.once
     end
   end
+
+  describe 'remote ActivityPub' do
+    let(:bob) { Fabricate(:user, email: 'bob@example.com', account: Fabricate(:account, username: 'bob', protocol: :activitypub, domain: 'example.com', inbox_url: 'http://example.com/inbox')).account }
+
+    before do
+      sender.block!(bob)
+      stub_request(:post, 'http://example.com/inbox').to_return(status: 200)
+      subject.call(sender, bob)
+    end
+
+    it 'destroys the blocking relation' do
+      expect(sender.blocking?(bob)).to be false
+    end
+
+    it 'sends an unblock activity' do
+      expect(a_request(:post, 'http://example.com/inbox')).to have_been_made.once
+    end
+  end
 end
diff --git a/spec/services/unfollow_service_spec.rb b/spec/services/unfollow_service_spec.rb
index 8ec2148a1..29040431e 100644
--- a/spec/services/unfollow_service_spec.rb
+++ b/spec/services/unfollow_service_spec.rb
@@ -18,8 +18,8 @@ RSpec.describe UnfollowService do
     end
   end
 
-  describe 'remote' do
-    let(:bob) { Fabricate(:user, email: 'bob@example.com', account: Fabricate(:account, username: 'bob', domain: 'example.com', salmon_url: 'http://salmon.example.com')).account }
+  describe 'remote OStatus' do
+    let(:bob) { Fabricate(:user, email: 'bob@example.com', account: Fabricate(:account, username: 'bob', protocol: :ostatus, domain: 'example.com', salmon_url: 'http://salmon.example.com')).account }
 
     before do
       sender.follow!(bob)
@@ -38,4 +38,22 @@ RSpec.describe UnfollowService do
       }).to have_been_made.once
     end
   end
+
+  describe 'remote ActivityPub' do
+    let(:bob) { Fabricate(:user, email: 'bob@example.com', account: Fabricate(:account, username: 'bob', protocol: :activitypub, domain: 'example.com', inbox_url: 'http://example.com/inbox')).account }
+
+    before do
+      sender.follow!(bob)
+      stub_request(:post, 'http://example.com/inbox').to_return(status: 200)
+      subject.call(sender, bob)
+    end
+
+    it 'destroys the following relation' do
+      expect(sender.following?(bob)).to be false
+    end
+
+    it 'sends an unfollow activity' do
+      expect(a_request(:post, 'http://example.com/inbox')).to have_been_made.once
+    end
+  end
 end
diff --git a/spec/workers/activitypub/delivery_worker_spec.rb b/spec/workers/activitypub/delivery_worker_spec.rb
new file mode 100644
index 000000000..351be185c
--- /dev/null
+++ b/spec/workers/activitypub/delivery_worker_spec.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe ActivityPub::DeliveryWorker do
+  subject { described_class.new }
+
+  let(:sender)  { Fabricate(:account) }
+  let(:payload) { 'test' }
+
+  describe 'perform' do
+    it 'performs a request' do
+      stub_request(:post, 'https://example.com/api').to_return(status: 200)
+      subject.perform(payload, sender.id, 'https://example.com/api')
+      expect(a_request(:post, 'https://example.com/api')).to have_been_made.once
+    end
+
+    it 'raises when request fails' do
+      stub_request(:post, 'https://example.com/api').to_return(status: 500)
+      expect { subject.perform(payload, sender.id, 'https://example.com/api') }.to raise_error Mastodon::UnexpectedResponseError
+    end
+  end
+end
diff --git a/spec/workers/activitypub/distribution_worker_spec.rb b/spec/workers/activitypub/distribution_worker_spec.rb
new file mode 100644
index 000000000..368ca025a
--- /dev/null
+++ b/spec/workers/activitypub/distribution_worker_spec.rb
@@ -0,0 +1,48 @@
+require 'rails_helper'
+
+describe ActivityPub::DistributionWorker do
+  subject { described_class.new }
+
+  let(:status)   { Fabricate(:status) }
+  let(:follower) { Fabricate(:account, protocol: :activitypub, inbox_url: 'http://example.com') }
+
+  describe '#perform' do
+    before do
+      allow(ActivityPub::DeliveryWorker).to receive(:push_bulk)
+      follower.follow!(status.account)
+    end
+
+    context 'with public status' do
+      before do
+        status.update(visibility: :public)
+      end
+
+      it 'delivers to followers' do
+        subject.perform(status.id)
+        expect(ActivityPub::DeliveryWorker).to have_received(:push_bulk).with(['http://example.com'])
+      end
+    end
+
+    context 'with private status' do
+      before do
+        status.update(visibility: :private)
+      end
+
+      it 'delivers to followers' do
+        subject.perform(status.id)
+        expect(ActivityPub::DeliveryWorker).to have_received(:push_bulk).with(['http://example.com'])
+      end
+    end
+
+    context 'with direct status' do
+      before do
+        status.update(visibility: :direct)
+      end
+
+      it 'does nothing' do
+        subject.perform(status.id)
+        expect(ActivityPub::DeliveryWorker).to_not have_received(:push_bulk)
+      end
+    end
+  end
+end
diff --git a/spec/workers/activitypub/processing_worker_spec.rb b/spec/workers/activitypub/processing_worker_spec.rb
new file mode 100644
index 000000000..b42c0bdbc
--- /dev/null
+++ b/spec/workers/activitypub/processing_worker_spec.rb
@@ -0,0 +1,15 @@
+require 'rails_helper'
+
+describe ActivityPub::ProcessingWorker do
+  subject { described_class.new }
+
+  let(:account) { Fabricate(:account) }
+
+  describe '#perform' do
+    it 'delegates to ActivityPub::ProcessCollectionService' do
+      allow(ActivityPub::ProcessCollectionService).to receive(:new).and_return(double(:service, call: nil))
+      subject.perform(account.id, '')
+      expect(ActivityPub::ProcessCollectionService).to have_received(:new)
+    end
+  end
+end
diff --git a/spec/workers/activitypub/thread_resolve_worker_spec.rb b/spec/workers/activitypub/thread_resolve_worker_spec.rb
new file mode 100644
index 000000000..b954cb62c
--- /dev/null
+++ b/spec/workers/activitypub/thread_resolve_worker_spec.rb
@@ -0,0 +1,16 @@
+require 'rails_helper'
+
+describe ActivityPub::ThreadResolveWorker do
+  subject { described_class.new }
+
+  let(:status) { Fabricate(:status) }
+  let(:parent) { Fabricate(:status) }
+
+  describe '#perform' do
+    it 'gets parent from ActivityPub::FetchRemoteStatusService and glues them together' do
+      allow(ActivityPub::FetchRemoteStatusService).to receive(:new).and_return(double(:service, call: parent))
+      subject.perform(status.id, 'http://example.com/123')
+      expect(status.reload.in_reply_to_id).to eq parent.id
+    end
+  end
+end
diff --git a/spec/workers/activitypub/update_distribution_worker_spec.rb b/spec/workers/activitypub/update_distribution_worker_spec.rb
new file mode 100644
index 000000000..688a424d5
--- /dev/null
+++ b/spec/workers/activitypub/update_distribution_worker_spec.rb
@@ -0,0 +1,20 @@
+require 'rails_helper'
+
+describe ActivityPub::UpdateDistributionWorker do
+  subject { described_class.new }
+
+  let(:account)  { Fabricate(:account) }
+  let(:follower) { Fabricate(:account, protocol: :activitypub, inbox_url: 'http://example.com') }
+
+  describe '#perform' do
+    before do
+      allow(ActivityPub::DeliveryWorker).to receive(:push_bulk)
+      follower.follow!(account)
+    end
+
+    it 'delivers to followers' do
+      subject.perform(account.id)
+      expect(ActivityPub::DeliveryWorker).to have_received(:push_bulk).with(['http://example.com'])
+    end
+  end
+end