about summary refs log tree commit diff
path: root/spec
diff options
context:
space:
mode:
Diffstat (limited to 'spec')
-rw-r--r--spec/controllers/accounts_controller_spec.rb31
-rw-r--r--spec/controllers/activitypub/inboxes_controller_spec.rb4
-rw-r--r--spec/controllers/admin/accounts_controller_spec.rb38
-rw-r--r--spec/controllers/admin/subscriptions_controller_spec.rb32
-rw-r--r--spec/controllers/api/oembed_controller_spec.rb2
-rw-r--r--spec/controllers/api/push_controller_spec.rb59
-rw-r--r--spec/controllers/api/salmon_controller_spec.rb65
-rw-r--r--spec/controllers/api/subscriptions_controller_spec.rb68
-rw-r--r--spec/controllers/api/v1/follows_controller_spec.rb51
-rw-r--r--spec/controllers/application_controller_spec.rb4
-rw-r--r--spec/controllers/concerns/account_controller_concern_spec.rb2
-rw-r--r--spec/controllers/concerns/signature_verification_spec.rb2
-rw-r--r--spec/controllers/remote_unfollows_controller_spec.rb38
-rw-r--r--spec/controllers/statuses_controller_spec.rb20
-rw-r--r--spec/controllers/stream_entries_controller_spec.rb95
-rw-r--r--spec/fabricators/stream_entry_fabricator.rb5
-rw-r--r--spec/fixtures/requests/webfinger.txt2
-rw-r--r--spec/helpers/admin/account_moderation_notes_helper_spec.rb2
-rw-r--r--spec/helpers/statuses_helper_spec.rb (renamed from spec/helpers/stream_entries_helper_spec.rb)10
-rw-r--r--spec/lib/activitypub/tag_manager_spec.rb6
-rw-r--r--spec/lib/language_detector_spec.rb6
-rw-r--r--spec/lib/ostatus/atom_serializer_spec.rb1560
-rw-r--r--spec/lib/sanitize_config_spec.rb4
-rw-r--r--spec/lib/spam_check_spec.rb160
-rw-r--r--spec/lib/status_finder_spec.rb9
-rw-r--r--spec/lib/tag_manager_spec.rb42
-rw-r--r--spec/models/account_spec.rb12
-rw-r--r--spec/models/concerns/streamable_spec.rb63
-rw-r--r--spec/models/remote_profile_spec.rb143
-rw-r--r--spec/models/stream_entry_spec.rb192
-rw-r--r--spec/models/tag_spec.rb38
-rw-r--r--spec/requests/link_headers_spec.rb8
-rw-r--r--spec/services/authorize_follow_service_spec.rb7
-rw-r--r--spec/services/batched_remove_status_service_spec.rb13
-rw-r--r--spec/services/block_service_spec.rb7
-rw-r--r--spec/services/favourite_service_spec.rb7
-rw-r--r--spec/services/fetch_remote_account_service_spec.rb41
-rw-r--r--spec/services/fetch_resource_service_spec.rb (renamed from spec/services/fetch_atom_service_spec.rb)69
-rw-r--r--spec/services/follow_service_spec.rb68
-rw-r--r--spec/services/import_service_spec.rb30
-rw-r--r--spec/services/post_status_service_spec.rb2
-rw-r--r--spec/services/process_feed_service_spec.rb252
-rw-r--r--spec/services/process_interaction_service_spec.rb151
-rw-r--r--spec/services/process_mentions_service_spec.rb8
-rw-r--r--spec/services/pubsubhubbub/subscribe_service_spec.rb71
-rw-r--r--spec/services/pubsubhubbub/unsubscribe_service_spec.rb46
-rw-r--r--spec/services/reblog_service_spec.rb4
-rw-r--r--spec/services/reject_follow_service_spec.rb7
-rw-r--r--spec/services/remove_status_service_spec.rb13
-rw-r--r--spec/services/resolve_account_service_spec.rb93
-rw-r--r--spec/services/resolve_url_service_spec.rb44
-rw-r--r--spec/services/send_interaction_service_spec.rb7
-rw-r--r--spec/services/subscribe_service_spec.rb43
-rw-r--r--spec/services/suspend_account_service_spec.rb6
-rw-r--r--spec/services/unblock_service_spec.rb7
-rw-r--r--spec/services/unfollow_service_spec.rb7
-rw-r--r--spec/services/unsubscribe_service_spec.rb37
-rw-r--r--spec/services/update_remote_profile_service_spec.rb84
-rw-r--r--spec/spec_helper.rb1
-rw-r--r--spec/views/statuses/show.html.haml_spec.rb (renamed from spec/views/stream_entries/show.html.haml_spec.rb)13
-rw-r--r--spec/workers/after_remote_follow_request_worker_spec.rb59
-rw-r--r--spec/workers/after_remote_follow_worker_spec.rb59
-rw-r--r--spec/workers/pubsubhubbub/confirmation_worker_spec.rb88
-rw-r--r--spec/workers/pubsubhubbub/delivery_worker_spec.rb68
-rw-r--r--spec/workers/pubsubhubbub/distribution_worker_spec.rb46
-rw-r--r--spec/workers/scheduler/subscriptions_scheduler_spec.rb19
66 files changed, 315 insertions, 3935 deletions
diff --git a/spec/controllers/accounts_controller_spec.rb b/spec/controllers/accounts_controller_spec.rb
index b728d719f..3d2a0665d 100644
--- a/spec/controllers/accounts_controller_spec.rb
+++ b/spec/controllers/accounts_controller_spec.rb
@@ -48,37 +48,6 @@ RSpec.describe AccountsController, type: :controller do
       end
     end
 
-    context 'atom' do
-      let(:format) { 'atom' }
-      let(:content_type) { 'application/atom+xml' }
-
-      shared_examples 'responsed streams' do
-        it 'assigns @entries' do
-          entries = assigns(:entries).to_a
-          expect(entries.size).to eq expected_statuses.size
-          entries.each.zip(expected_statuses.each) do |entry, expected_status|
-            expect(entry.status).to eq expected_status
-          end
-        end
-      end
-
-      include_examples 'responses'
-
-      context 'without max_id nor since_id' do
-        let(:expected_statuses) { [status7, status6, status5, status4, status3, status2, status1] }
-
-        include_examples 'responsed streams'
-      end
-
-      context 'with max_id and since_id' do
-        let(:max_id) { status4.stream_entry.id }
-        let(:since_id) { status1.stream_entry.id }
-        let(:expected_statuses) { [status3, status2] }
-
-        include_examples 'responsed streams'
-      end
-    end
-
     context 'activitystreams2' do
       let(:format) { 'json' }
       let(:content_type) { 'application/activity+json' }
diff --git a/spec/controllers/activitypub/inboxes_controller_spec.rb b/spec/controllers/activitypub/inboxes_controller_spec.rb
index eab4b8c3e..a9ee75490 100644
--- a/spec/controllers/activitypub/inboxes_controller_spec.rb
+++ b/spec/controllers/activitypub/inboxes_controller_spec.rb
@@ -4,7 +4,7 @@ require 'rails_helper'
 
 RSpec.describe ActivityPub::InboxesController, type: :controller do
   describe 'POST #create' do
-    context 'if signed_request_account' do
+    context 'with signed_request_account' do
       it 'returns 202' do
         allow(controller).to receive(:signed_request_account) do
           Fabricate(:account)
@@ -15,7 +15,7 @@ RSpec.describe ActivityPub::InboxesController, type: :controller do
       end
     end
 
-    context 'not signed_request_account' do
+    context 'without signed_request_account' do
       it 'returns 401' do
         allow(controller).to receive(:signed_request_account) do
           false
diff --git a/spec/controllers/admin/accounts_controller_spec.rb b/spec/controllers/admin/accounts_controller_spec.rb
index a348ab3d7..608606ff9 100644
--- a/spec/controllers/admin/accounts_controller_spec.rb
+++ b/spec/controllers/admin/accounts_controller_spec.rb
@@ -75,44 +75,6 @@ RSpec.describe Admin::AccountsController, type: :controller do
     end
   end
 
-  describe 'POST #subscribe' do
-    subject { post :subscribe, params: { id: account.id } }
-
-    let(:current_user) { Fabricate(:user, admin: admin) }
-    let(:account) { Fabricate(:account) }
-
-    context 'when user is admin' do
-      let(:admin) { true }
-
-      it { is_expected.to redirect_to admin_account_path(account.id) }
-    end
-
-    context 'when user is not admin' do
-      let(:admin) { false }
-
-      it { is_expected.to have_http_status :forbidden }
-    end
-  end
-
-  describe 'POST #unsubscribe' do
-    subject { post :unsubscribe, params: { id: account.id } }
-
-    let(:current_user) { Fabricate(:user, admin: admin) }
-    let(:account) { Fabricate(:account) }
-
-    context 'when user is admin' do
-      let(:admin) { true }
-
-      it { is_expected.to redirect_to admin_account_path(account.id) }
-    end
-
-    context 'when user is not admin' do
-      let(:admin) { false }
-
-      it { is_expected.to have_http_status :forbidden }
-    end
-  end
-
   describe 'POST #memorialize' do
     subject { post :memorialize, params: { id: account.id } }
 
diff --git a/spec/controllers/admin/subscriptions_controller_spec.rb b/spec/controllers/admin/subscriptions_controller_spec.rb
deleted file mode 100644
index 967152abe..000000000
--- a/spec/controllers/admin/subscriptions_controller_spec.rb
+++ /dev/null
@@ -1,32 +0,0 @@
-# frozen_string_literal: true
-require 'rails_helper'
-
-RSpec.describe Admin::SubscriptionsController, type: :controller do
-  render_views
-
-  describe 'GET #index' do
-    around do |example|
-      default_per_page = Subscription.default_per_page
-      Subscription.paginates_per 1
-      example.run
-      Subscription.paginates_per default_per_page
-    end
-
-    before do
-      sign_in Fabricate(:user, admin: true), scope: :user
-    end
-
-    it 'renders subscriptions' do
-      Fabricate(:subscription)
-      specified = Fabricate(:subscription)
-
-      get :index
-
-      subscriptions = assigns(:subscriptions)
-      expect(subscriptions.count).to eq 1
-      expect(subscriptions[0]).to eq specified
-
-      expect(response).to have_http_status(200)
-    end
-  end
-end
diff --git a/spec/controllers/api/oembed_controller_spec.rb b/spec/controllers/api/oembed_controller_spec.rb
index 7fee15a35..b9082bde1 100644
--- a/spec/controllers/api/oembed_controller_spec.rb
+++ b/spec/controllers/api/oembed_controller_spec.rb
@@ -9,7 +9,7 @@ RSpec.describe Api::OEmbedController, type: :controller do
   describe 'GET #show' do
     before do
       request.host = Rails.configuration.x.local_domain
-      get :show, params: { url: account_stream_entry_url(alice, status.stream_entry) }, format: :json
+      get :show, params: { url: short_account_status_url(alice, status) }, format: :json
     end
 
     it 'returns http success' do
diff --git a/spec/controllers/api/push_controller_spec.rb b/spec/controllers/api/push_controller_spec.rb
deleted file mode 100644
index d769d8554..000000000
--- a/spec/controllers/api/push_controller_spec.rb
+++ /dev/null
@@ -1,59 +0,0 @@
-require 'rails_helper'
-
-RSpec.describe Api::PushController, type: :controller do
-  describe 'POST #update' do
-    context 'with hub.mode=subscribe' do
-      it 'creates a subscription' do
-        service = double(call: ['', 202])
-        allow(Pubsubhubbub::SubscribeService).to receive(:new).and_return(service)
-        account = Fabricate(:account)
-        account_topic_url = "https://#{Rails.configuration.x.local_domain}/users/#{account.username}.atom"
-        post :update, params: {
-          'hub.mode' => 'subscribe',
-          'hub.topic' => account_topic_url,
-          'hub.callback' => 'https://callback.host/api',
-          'hub.lease_seconds' => '3600',
-          'hub.secret' => 'as1234df',
-        }
-
-        expect(service).to have_received(:call).with(
-          account,
-          'https://callback.host/api',
-          'as1234df',
-          '3600',
-          nil
-        )
-        expect(response).to have_http_status(202)
-      end
-    end
-
-    context 'with hub.mode=unsubscribe' do
-      it 'unsubscribes the account' do
-        service = double(call: ['', 202])
-        allow(Pubsubhubbub::UnsubscribeService).to receive(:new).and_return(service)
-        account = Fabricate(:account)
-        account_topic_url = "https://#{Rails.configuration.x.local_domain}/users/#{account.username}.atom"
-        post :update, params: {
-          'hub.mode' => 'unsubscribe',
-          'hub.topic' => account_topic_url,
-          'hub.callback' => 'https://callback.host/api',
-        }
-
-        expect(service).to have_received(:call).with(
-          account,
-          'https://callback.host/api',
-        )
-        expect(response).to have_http_status(202)
-      end
-    end
-
-    context 'with unknown mode' do
-      it 'returns an unknown mode error' do
-        post :update, params: { 'hub.mode' => 'fake' }
-
-        expect(response).to have_http_status(422)
-        expect(response.body).to match(/Unknown mode/)
-      end
-    end
-  end
-end
diff --git a/spec/controllers/api/salmon_controller_spec.rb b/spec/controllers/api/salmon_controller_spec.rb
deleted file mode 100644
index 235a29af0..000000000
--- a/spec/controllers/api/salmon_controller_spec.rb
+++ /dev/null
@@ -1,65 +0,0 @@
-require 'rails_helper'
-
-RSpec.describe Api::SalmonController, type: :controller do
-  render_views
-
-  let(:account) { Fabricate(:user, account: Fabricate(:account, username: 'catsrgr8')).account }
-
-  before do
-    stub_request(:get, "https://quitter.no/.well-known/host-meta").to_return(request_fixture('.host-meta.txt'))
-    stub_request(:get, "https://quitter.no/.well-known/webfinger?resource=acct:gargron@quitter.no").to_return(request_fixture('webfinger.txt'))
-    stub_request(:get, "https://quitter.no/api/statuses/user_timeline/7477.atom").to_return(request_fixture('feed.txt'))
-    stub_request(:get, "https://quitter.no/avatar/7477-300-20160211190340.png").to_return(request_fixture('avatar.txt'))
-  end
-
-  describe 'POST #update' do
-    context 'with valid post data' do
-      before do
-        post :update, params: { id: account.id }, body: File.read(Rails.root.join('spec', 'fixtures', 'salmon', 'mention.xml'))
-      end
-
-      it 'contains XML in the request body' do
-        expect(request.body.read).to be_a String
-      end
-
-      it 'returns http success' do
-        expect(response).to have_http_status(202)
-      end
-
-      it 'creates remote account' do
-        expect(Account.find_by(username: 'gargron', domain: 'quitter.no')).to_not be_nil
-      end
-
-      it 'creates status' do
-        expect(Status.find_by(uri: 'tag:quitter.no,2016-03-20:noticeId=1276923:objectType=note')).to_not be_nil
-      end
-
-      it 'creates mention for target account' do
-        expect(account.mentions.count).to eq 1
-      end
-    end
-
-    context 'with empty post data' do
-      before do
-        post :update, params: { id: account.id }, body: ''
-      end
-
-      it 'returns http client error' do
-        expect(response).to have_http_status(400)
-      end
-    end
-
-    context 'with invalid post data' do
-      before do
-        service = double(call: false)
-        allow(VerifySalmonService).to receive(:new).and_return(service)
-
-        post :update, params: { id: account.id }, body: File.read(Rails.root.join('spec', 'fixtures', 'salmon', 'mention.xml'))
-      end
-
-      it 'returns http client error' do
-        expect(response).to have_http_status(401)
-      end
-    end
-  end
-end
diff --git a/spec/controllers/api/subscriptions_controller_spec.rb b/spec/controllers/api/subscriptions_controller_spec.rb
deleted file mode 100644
index 7a4252fe6..000000000
--- a/spec/controllers/api/subscriptions_controller_spec.rb
+++ /dev/null
@@ -1,68 +0,0 @@
-require 'rails_helper'
-
-RSpec.describe Api::SubscriptionsController, type: :controller do
-  render_views
-
-  let(:account) { Fabricate(:account, username: 'gargron', domain: 'quitter.no', remote_url: 'topic_url', secret: 'abc') }
-
-  describe 'GET #show' do
-    context 'with valid subscription' do
-      before do
-        get :show, params: { :id => account.id, 'hub.topic' => 'topic_url', 'hub.challenge' => '456', 'hub.lease_seconds' => "#{86400 * 30}" }
-      end
-
-      it 'returns http success' do
-        expect(response).to have_http_status(200)
-      end
-
-      it 'echoes back the challenge' do
-        expect(response.body).to match '456'
-      end
-    end
-
-    context 'with invalid subscription' do
-      before do
-        expect_any_instance_of(Account).to receive_message_chain(:subscription, :valid?).and_return(false)
-        get :show, params: { :id => account.id }
-      end
-
-      it 'returns http success' do
-        expect(response).to have_http_status(404)
-      end
-    end
-  end
-
-  describe 'POST #update' do
-    let(:feed) { File.read(Rails.root.join('spec', 'fixtures', 'push', 'feed.atom')) }
-
-    before do
-      stub_request(:post, "https://quitter.no/main/push/hub").to_return(:status => 200, :body => "", :headers => {})
-      stub_request(:get, "https://quitter.no/avatar/7477-300-20160211190340.png").to_return(request_fixture('avatar.txt'))
-      stub_request(:get, "https://quitter.no/notice/1269244").to_return(status: 404)
-      stub_request(:get, "https://quitter.no/notice/1265331").to_return(status: 404)
-      stub_request(:get, "https://community.highlandarrow.com/notice/54411").to_return(status: 404)
-      stub_request(:get, "https://community.highlandarrow.com/notice/53857").to_return(status: 404)
-      stub_request(:get, "https://community.highlandarrow.com/notice/51852").to_return(status: 404)
-      stub_request(:get, "https://social.umeahackerspace.se/notice/424348").to_return(status: 404)
-      stub_request(:get, "https://community.highlandarrow.com/notice/50467").to_return(status: 404)
-      stub_request(:get, "https://quitter.no/notice/1243309").to_return(status: 404)
-      stub_request(:get, "https://quitter.no/user/7477").to_return(status: 404)
-      stub_request(:any, "https://community.highlandarrow.com/user/1").to_return(status: 404)
-      stub_request(:any, "https://social.umeahackerspace.se/user/2").to_return(status: 404)
-      stub_request(:any, "https://gs.kawa-kun.com/user/2").to_return(status: 404)
-      stub_request(:any, "https://mastodon.social/users/Gargron").to_return(status: 404)
-
-      request.env['HTTP_X_HUB_SIGNATURE'] = "sha1=#{OpenSSL::HMAC.hexdigest('sha1', 'abc', feed)}"
-
-      post :update, params: { id: account.id }, body: feed
-    end
-
-    it 'returns http success' do
-      expect(response).to have_http_status(200)
-    end
-
-    it 'creates statuses for feed' do
-      expect(account.statuses.count).to_not eq 0
-    end
-  end
-end
diff --git a/spec/controllers/api/v1/follows_controller_spec.rb b/spec/controllers/api/v1/follows_controller_spec.rb
deleted file mode 100644
index 089e0fe5e..000000000
--- a/spec/controllers/api/v1/follows_controller_spec.rb
+++ /dev/null
@@ -1,51 +0,0 @@
-require 'rails_helper'
-
-RSpec.describe Api::V1::FollowsController, type: :controller do
-  render_views
-
-  let(:user)  { Fabricate(:user, account: Fabricate(:account, username: 'alice')) }
-  let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'write:follows') }
-
-  before do
-    allow(controller).to receive(:doorkeeper_token) { token }
-  end
-
-  describe 'POST #create' do
-    before do
-      stub_request(:get,  "https://quitter.no/.well-known/host-meta").to_return(request_fixture('.host-meta.txt'))
-      stub_request(:get,  "https://quitter.no/.well-known/webfinger?resource=acct:gargron@quitter.no").to_return(request_fixture('webfinger.txt'))
-      stub_request(:head, "https://quitter.no/api/statuses/user_timeline/7477.atom").to_return(:status => 405, :body => "", :headers => {})
-      stub_request(:get,  "https://quitter.no/api/statuses/user_timeline/7477.atom").to_return(request_fixture('feed.txt'))
-      stub_request(:get,  "https://quitter.no/avatar/7477-300-20160211190340.png").to_return(request_fixture('avatar.txt'))
-      stub_request(:post, "https://quitter.no/main/push/hub").to_return(:status => 200, :body => "", :headers => {})
-      stub_request(:post, "https://quitter.no/main/salmon/user/7477").to_return(:status => 200, :body => "", :headers => {})
-
-      post :create, params: { uri: 'gargron@quitter.no' }
-    end
-
-    it 'returns http success' do
-      expect(response).to have_http_status(200)
-    end
-
-    it 'creates account for remote user' do
-      expect(Account.find_by(username: 'gargron', domain: 'quitter.no')).to_not be_nil
-    end
-
-    it 'creates a follow relation between user and remote user' do
-      expect(user.account.following?(Account.find_by(username: 'gargron', domain: 'quitter.no'))).to be true
-    end
-
-    it 'sends a salmon slap to the remote user' do
-      expect(a_request(:post, "https://quitter.no/main/salmon/user/7477")).to have_been_made
-    end
-
-    it 'subscribes to remote hub' do
-      expect(a_request(:post, "https://quitter.no/main/push/hub")).to have_been_made
-    end
-
-    it 'returns http success if already following, too' do
-      post :create, params: { uri: 'gargron@quitter.no' }
-      expect(response).to have_http_status(200)
-    end
-  end
-end
diff --git a/spec/controllers/application_controller_spec.rb b/spec/controllers/application_controller_spec.rb
index ea443b80c..99015c82d 100644
--- a/spec/controllers/application_controller_spec.rb
+++ b/spec/controllers/application_controller_spec.rb
@@ -364,9 +364,5 @@ describe ApplicationController, type: :controller do
     context 'Status' do
       include_examples 'cacheable', :status, Status
     end
-
-    context 'StreamEntry' do
-      include_examples 'receives :with_includes', :stream_entry, StreamEntry
-    end
   end
 end
diff --git a/spec/controllers/concerns/account_controller_concern_spec.rb b/spec/controllers/concerns/account_controller_concern_spec.rb
index ea2b4a2a1..7ea214a7d 100644
--- a/spec/controllers/concerns/account_controller_concern_spec.rb
+++ b/spec/controllers/concerns/account_controller_concern_spec.rb
@@ -41,7 +41,7 @@ describe ApplicationController, type: :controller do
     it 'sets link headers' do
       account = Fabricate(:account, username: 'username', user: Fabricate(:user))
       get 'success', params: { account_username: 'username' }
-      expect(response.headers['Link'].to_s).to eq '<http://test.host/.well-known/webfinger?resource=acct%3Ausername%40cb6e6126.ngrok.io>; rel="lrdd"; type="application/xrd+xml", <http://test.host/users/username.atom>; rel="alternate"; type="application/atom+xml", <https://cb6e6126.ngrok.io/users/username>; rel="alternate"; type="application/activity+json"'
+      expect(response.headers['Link'].to_s).to eq '<http://test.host/.well-known/webfinger?resource=acct%3Ausername%40cb6e6126.ngrok.io>; rel="lrdd"; type="application/jrd+json", <https://cb6e6126.ngrok.io/users/username>; rel="alternate"; type="application/activity+json"'
     end
 
     it 'returns http success' do
diff --git a/spec/controllers/concerns/signature_verification_spec.rb b/spec/controllers/concerns/signature_verification_spec.rb
index 720690097..1fa19f54d 100644
--- a/spec/controllers/concerns/signature_verification_spec.rb
+++ b/spec/controllers/concerns/signature_verification_spec.rb
@@ -38,7 +38,7 @@ describe ApplicationController, type: :controller do
   end
 
   context 'with signature header' do
-    let!(:author) { Fabricate(:account) }
+    let!(:author) { Fabricate(:account, domain: 'example.com', uri: 'https://example.com/actor') }
 
     context 'without body' do
       before do
diff --git a/spec/controllers/remote_unfollows_controller_spec.rb b/spec/controllers/remote_unfollows_controller_spec.rb
deleted file mode 100644
index a1a55ede0..000000000
--- a/spec/controllers/remote_unfollows_controller_spec.rb
+++ /dev/null
@@ -1,38 +0,0 @@
-# frozen_string_literal: true
-
-require 'rails_helper'
-
-describe RemoteUnfollowsController do
-  render_views
-
-  describe '#create' do
-    subject { post :create, params: { acct: acct } }
-
-    let(:current_user) { Fabricate(:user, account: current_account) }
-    let(:current_account) { Fabricate(:account) }
-    let(:remote_account) { 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
-      sign_in current_user
-      current_account.follow!(remote_account)
-      stub_request(:post, 'http://example.com/inbox') { { status: 200 } }
-    end
-
-    context 'when successfully unfollow remote account' do
-      let(:acct) { "acct:#{remote_account.username}@#{remote_account.domain}" }
-
-      it do
-        is_expected.to render_template :success
-        expect(current_account.following?(remote_account)).to be false
-      end
-    end
-
-    context 'when fails to unfollow remote account' do
-      let(:acct) { "acct:#{remote_account.username + '_test'}@#{remote_account.domain}" }
-
-      it do
-        is_expected.to render_template :error
-        expect(current_account.following?(remote_account)).to be true
-      end
-    end
-  end
-end
diff --git a/spec/controllers/statuses_controller_spec.rb b/spec/controllers/statuses_controller_spec.rb
index 1bb6636c6..6905dae10 100644
--- a/spec/controllers/statuses_controller_spec.rb
+++ b/spec/controllers/statuses_controller_spec.rb
@@ -55,18 +55,6 @@ describe StatusesController do
         expect(assigns(:status)).to eq status
       end
 
-      it 'assigns @stream_entry' do
-        status = Fabricate(:status)
-        get :show, params: { account_username: status.account.username, id: status.id }
-        expect(assigns(:stream_entry)).to eq status.stream_entry
-      end
-
-      it 'assigns @type' do
-        status = Fabricate(:status)
-        get :show, params: { account_username: status.account.username, id: status.id }
-        expect(assigns(:type)).to eq 'status'
-      end
-
       it 'assigns @ancestors for ancestors of the status if it is a reply' do
         ancestor = Fabricate(:status)
         status = Fabricate(:status, in_reply_to_id: ancestor.id)
@@ -104,7 +92,7 @@ describe StatusesController do
       end
 
       it 'assigns @max_descendant_thread_id for the last thread if it is hitting the status limit' do
-        stub_const 'StatusesController::DESCENDANTS_LIMIT', 1
+        stub_const 'StatusControllerConcern::DESCENDANTS_LIMIT', 1
         status = Fabricate(:status)
         child = Fabricate(:status, in_reply_to_id: status.id)
 
@@ -115,7 +103,7 @@ describe StatusesController do
       end
 
       it 'assigns @descendant_threads for threads with :next_status key if they are hitting the depth limit' do
-        stub_const 'StatusesController::DESCENDANTS_DEPTH_LIMIT', 2
+        stub_const 'StatusControllerConcern::DESCENDANTS_DEPTH_LIMIT', 2
         status = Fabricate(:status)
         child0 = Fabricate(:status, in_reply_to_id: status.id)
         child1 = Fabricate(:status, in_reply_to_id: child0.id)
@@ -135,10 +123,10 @@ describe StatusesController do
         expect(response).to have_http_status(200)
       end
 
-      it 'renders stream_entries/show' do
+      it 'renders statuses/show' do
         status = Fabricate(:status)
         get :show, params: { account_username: status.account.username, id: status.id }
-        expect(response).to render_template 'stream_entries/show'
+        expect(response).to render_template 'statuses/show'
       end
     end
   end
diff --git a/spec/controllers/stream_entries_controller_spec.rb b/spec/controllers/stream_entries_controller_spec.rb
deleted file mode 100644
index eb7fdf9d7..000000000
--- a/spec/controllers/stream_entries_controller_spec.rb
+++ /dev/null
@@ -1,95 +0,0 @@
-require 'rails_helper'
-
-RSpec.describe StreamEntriesController, type: :controller do
-  render_views
-
-  shared_examples 'before_action' do |route|
-    context 'when account is not suspended and stream_entry is available' do
-      it 'assigns instance variables' do
-        status = Fabricate(:status)
-
-        get route, params: { account_username: status.account.username, id: status.stream_entry.id }
-
-        expect(assigns(:account)).to eq status.account
-        expect(assigns(:stream_entry)).to eq status.stream_entry
-        expect(assigns(:type)).to eq 'status'
-      end
-
-      it 'sets Link headers' do
-        alice = Fabricate(:account, username: 'alice')
-        status = Fabricate(:status, account: alice)
-
-        get route, params: { account_username: alice.username, id: status.stream_entry.id }
-
-        expect(response.headers['Link'].to_s).to eq "<http://test.host/users/alice/updates/#{status.stream_entry.id}.atom>; rel=\"alternate\"; type=\"application/atom+xml\", <https://cb6e6126.ngrok.io/users/alice/statuses/#{status.id}>; rel=\"alternate\"; type=\"application/activity+json\""
-      end
-    end
-
-    context 'when account is suspended' do
-      it 'returns http status 410' do
-        account = Fabricate(:account, suspended: true)
-        status = Fabricate(:status, account: account)
-
-        get route, params: { account_username: account.username, id: status.stream_entry.id }
-
-        expect(response).to have_http_status(410)
-      end
-    end
-
-    context 'when activity is nil' do
-      it 'raises ActiveRecord::RecordNotFound' do
-        account = Fabricate(:account)
-        stream_entry = Fabricate.build(:stream_entry, account: account, activity: nil, activity_type: 'Status')
-        stream_entry.save!(validate: false)
-
-        get route, params: { account_username: account.username, id: stream_entry.id }
-
-        expect(response).to have_http_status(404)
-      end
-    end
-
-    context 'when it is hidden and it is not permitted' do
-      it 'raises ActiveRecord::RecordNotFound' do
-        status = Fabricate(:status)
-        user = Fabricate(:user)
-        status.account.block!(user.account)
-        status.stream_entry.update!(hidden: true)
-
-        sign_in(user)
-        get route, params: { account_username: status.account.username, id: status.stream_entry.id }
-
-        expect(response).to have_http_status(404)
-      end
-    end
-  end
-
-  describe 'GET #show' do
-    include_examples 'before_action', :show
-
-    it 'redirects to status page' do
-      status = Fabricate(:status)
-
-      get :show, params: { account_username: status.account.username, id: status.stream_entry.id }
-
-      expect(response).to redirect_to(short_account_status_url(status.account, status))
-    end
-
-    it 'returns http success with Atom' do
-      status = Fabricate(:status)
-      get :show, params: { account_username: status.account.username, id: status.stream_entry.id }, format: 'atom'
-      expect(response).to have_http_status(200)
-    end
-  end
-
-  describe 'GET #embed' do
-    include_examples 'before_action', :embed
-
-    it 'redirects to new embed page' do
-      status = Fabricate(:status)
-
-      get :embed, params: { account_username: status.account.username, id: status.stream_entry.id }
-
-      expect(response).to redirect_to(embed_short_account_status_url(status.account, status))
-    end
-  end
-end
diff --git a/spec/fabricators/stream_entry_fabricator.rb b/spec/fabricators/stream_entry_fabricator.rb
deleted file mode 100644
index f33822c7c..000000000
--- a/spec/fabricators/stream_entry_fabricator.rb
+++ /dev/null
@@ -1,5 +0,0 @@
-Fabricator(:stream_entry) do
-  account
-  activity { Fabricate(:status) }
-  hidden { [true, false].sample }
-end
diff --git a/spec/fixtures/requests/webfinger.txt b/spec/fixtures/requests/webfinger.txt
index edb8a2dbb..f337ecae6 100644
--- a/spec/fixtures/requests/webfinger.txt
+++ b/spec/fixtures/requests/webfinger.txt
@@ -8,4 +8,4 @@ Access-Control-Allow-Origin: *
 Vary: Accept-Encoding,Cookie

 Strict-Transport-Security: max-age=31536000; includeSubdomains;

 

-{"subject":"acct:gargron@quitter.no","aliases":["https:\/\/quitter.no\/user\/7477","https:\/\/quitter.no\/gargron","https:\/\/quitter.no\/index.php\/user\/7477","https:\/\/quitter.no\/index.php\/gargron"],"links":[{"rel":"http:\/\/webfinger.net\/rel\/profile-page","type":"text\/html","href":"https:\/\/quitter.no\/gargron"},{"rel":"http:\/\/gmpg.org\/xfn\/11","type":"text\/html","href":"https:\/\/quitter.no\/gargron"},{"rel":"describedby","type":"application\/rdf+xml","href":"https:\/\/quitter.no\/gargron\/foaf"},{"rel":"http:\/\/apinamespace.org\/atom","type":"application\/atomsvc+xml","href":"https:\/\/quitter.no\/api\/statusnet\/app\/service\/gargron.xml"},{"rel":"http:\/\/apinamespace.org\/twitter","href":"https:\/\/quitter.no\/api\/"},{"rel":"http:\/\/specs.openid.net\/auth\/2.0\/provider","href":"https:\/\/quitter.no\/gargron"},{"rel":"http:\/\/schemas.google.com\/g\/2010#updates-from","type":"application\/atom+xml","href":"https:\/\/quitter.no\/api\/statuses\/user_timeline\/7477.atom"},{"rel":"magic-public-key","href":"data:application\/magic-public-key,RSA.1ZBkHTavLvxH3FzlKv4O6WtlILKRFfNami3_Rcu8EuogtXSYiS-bB6hElZfUCSHbC4uLemOA34PEhz__CDMozax1iI_t8dzjDnh1x0iFSup7pSfW9iXk_WU3Dm74yWWW2jildY41vWgrEstuQ1dJ8vVFfSJ9T_tO4c-T9y8vDI8=.AQAB"},{"rel":"salmon","href":"https:\/\/quitter.no\/main\/salmon\/user\/7477"},{"rel":"http:\/\/salmon-protocol.org\/ns\/salmon-replies","href":"https:\/\/quitter.no\/main\/salmon\/user\/7477"},{"rel":"http:\/\/salmon-protocol.org\/ns\/salmon-mention","href":"https:\/\/quitter.no\/main\/salmon\/user\/7477"},{"rel":"http:\/\/ostatus.org\/schema\/1.0\/subscribe","template":"https:\/\/quitter.no\/main\/ostatussub?profile={uri}"}]}
\ No newline at end of file
+{"subject":"acct:gargron@quitter.no","aliases":["https:\/\/quitter.no\/user\/7477","https:\/\/quitter.no\/gargron","https:\/\/quitter.no\/index.php\/user\/7477","https:\/\/quitter.no\/index.php\/gargron"],"links":[{"rel":"http:\/\/webfinger.net\/rel\/profile-page","type":"text\/html","href":"https:\/\/quitter.no\/gargron"},{"rel":"http:\/\/gmpg.org\/xfn\/11","type":"text\/html","href":"https:\/\/quitter.no\/gargron"},{"rel":"describedby","type":"application\/rdf+xml","href":"https:\/\/quitter.no\/gargron\/foaf"},{"rel":"http:\/\/apinamespace.org\/atom","type":"application\/atomsvc+xml","href":"https:\/\/quitter.no\/api\/statusnet\/app\/service\/gargron.xml"},{"rel":"http:\/\/apinamespace.org\/twitter","href":"https:\/\/quitter.no\/api\/"},{"rel":"http:\/\/specs.openid.net\/auth\/2.0\/provider","href":"https:\/\/quitter.no\/gargron"},{"rel":"http:\/\/schemas.google.com\/g\/2010#updates-from","type":"application\/atom+xml","href":"https:\/\/quitter.no\/api\/statuses\/user_timeline\/7477.atom"},{"rel":"magic-public-key","href":"data:application\/magic-public-key,RSA.1ZBkHTavLvxH3FzlKv4O6WtlILKRFfNami3_Rcu8EuogtXSYiS-bB6hElZfUCSHbC4uLemOA34PEhz__CDMozax1iI_t8dzjDnh1x0iFSup7pSfW9iXk_WU3Dm74yWWW2jildY41vWgrEstuQ1dJ8vVFfSJ9T_tO4c-T9y8vDI8=.AQAB"},{"rel":"salmon","href":"https:\/\/quitter.no\/main\/salmon\/user\/7477"},{"rel":"http:\/\/salmon-protocol.org\/ns\/salmon-replies","href":"https:\/\/quitter.no\/main\/salmon\/user\/7477"},{"rel":"http:\/\/salmon-protocol.org\/ns\/salmon-mention","href":"https:\/\/quitter.no\/main\/salmon\/user\/7477"},{"rel":"http:\/\/ostatus.org\/schema\/1.0\/subscribe","template":"https:\/\/quitter.no\/main\/ostatussub?profile={uri}"}]}

diff --git a/spec/helpers/admin/account_moderation_notes_helper_spec.rb b/spec/helpers/admin/account_moderation_notes_helper_spec.rb
index c07f6c4b8..ddfe8b46f 100644
--- a/spec/helpers/admin/account_moderation_notes_helper_spec.rb
+++ b/spec/helpers/admin/account_moderation_notes_helper_spec.rb
@@ -3,7 +3,7 @@
 require 'rails_helper'
 
 RSpec.describe Admin::AccountModerationNotesHelper, type: :helper do
-  include StreamEntriesHelper
+  include StatusesHelper
 
   describe '#admin_account_link_to' do
     context 'account is nil' do
diff --git a/spec/helpers/stream_entries_helper_spec.rb b/spec/helpers/statuses_helper_spec.rb
index 845b9974e..510955a2f 100644
--- a/spec/helpers/stream_entries_helper_spec.rb
+++ b/spec/helpers/statuses_helper_spec.rb
@@ -1,6 +1,6 @@
 require 'rails_helper'
 
-RSpec.describe StreamEntriesHelper, type: :helper do
+RSpec.describe StatusesHelper, type: :helper do
   describe '#display_name' do
     it 'uses the display name when it exists' do
       account = Account.new(display_name: "Display", username: "Username")
@@ -70,13 +70,13 @@ RSpec.describe StreamEntriesHelper, type: :helper do
   end
 
   def set_not_embedded_view
-    params[:controller] = "not_#{StreamEntriesHelper::EMBEDDED_CONTROLLER}"
-    params[:action] = "not_#{StreamEntriesHelper::EMBEDDED_ACTION}"
+    params[:controller] = "not_#{StatusesHelper::EMBEDDED_CONTROLLER}"
+    params[:action] = "not_#{StatusesHelper::EMBEDDED_ACTION}"
   end
 
   def set_embedded_view
-    params[:controller] = StreamEntriesHelper::EMBEDDED_CONTROLLER
-    params[:action] = StreamEntriesHelper::EMBEDDED_ACTION
+    params[:controller] = StatusesHelper::EMBEDDED_CONTROLLER
+    params[:action] = StatusesHelper::EMBEDDED_ACTION
   end
 
   describe '#style_classes' do
diff --git a/spec/lib/activitypub/tag_manager_spec.rb b/spec/lib/activitypub/tag_manager_spec.rb
index 6d246629e..1c5c6f0ed 100644
--- a/spec/lib/activitypub/tag_manager_spec.rb
+++ b/spec/lib/activitypub/tag_manager_spec.rb
@@ -143,12 +143,6 @@ RSpec.describe ActivityPub::TagManager do
       expect(subject.uri_to_resource(OStatus::TagManager.instance.uri_for(status), Status)).to eq status
     end
 
-    it 'returns the local status for OStatus StreamEntry URL' do
-      status = Fabricate(:status)
-      stream_entry_url = account_stream_entry_url(status.account, status.stream_entry)
-      expect(subject.uri_to_resource(stream_entry_url, Status)).to eq status
-    end
-
     it 'returns the remote status by matching URI without fragment part' do
       status = Fabricate(:status, uri: 'https://example.com/123')
       expect(subject.uri_to_resource('https://example.com/123#456', Status)).to eq status
diff --git a/spec/lib/language_detector_spec.rb b/spec/lib/language_detector_spec.rb
index 0cb70605a..b7ba0f6c4 100644
--- a/spec/lib/language_detector_spec.rb
+++ b/spec/lib/language_detector_spec.rb
@@ -32,11 +32,11 @@ describe LanguageDetector do
       expect(result).to eq 'Our website is and also'
     end
 
-    it 'strips #hashtags from strings before detection' do
-      string = 'Hey look at all the #animals and #fish'
+    it 'converts #hashtags back to normal text before detection' do
+      string = 'Hey look at all the #animals and #FishAndChips'
 
       result = described_class.instance.send(:prepare_text, string)
-      expect(result).to eq 'Hey look at all the and'
+      expect(result).to eq 'Hey look at all the animals and fish and chips'
     end
   end
 
diff --git a/spec/lib/ostatus/atom_serializer_spec.rb b/spec/lib/ostatus/atom_serializer_spec.rb
deleted file mode 100644
index 891871c1c..000000000
--- a/spec/lib/ostatus/atom_serializer_spec.rb
+++ /dev/null
@@ -1,1560 +0,0 @@
-require 'rails_helper'
-
-RSpec.describe OStatus::AtomSerializer do
-  shared_examples 'follow request salmon' do
-    it 'appends author element with account' do
-      account = Fabricate(:account, domain: nil, username: 'username')
-      follow_request = Fabricate(:follow_request, account: account)
-
-      follow_request_salmon = serialize(follow_request)
-
-      expect(follow_request_salmon.author.id.text).to eq 'https://cb6e6126.ngrok.io/users/username'
-    end
-
-    it 'appends activity:object-type element with activity type' do
-      follow_request = Fabricate(:follow_request)
-
-      follow_request_salmon = serialize(follow_request)
-
-      object_type = follow_request_salmon.nodes.find { |node| node.name == 'activity:object-type' }
-      expect(object_type.text).to eq OStatus::TagManager::TYPES[:activity]
-    end
-
-    it 'appends activity:verb element with request_friend type' do
-      follow_request = Fabricate(:follow_request)
-
-      follow_request_salmon = serialize(follow_request)
-
-      verb = follow_request_salmon.nodes.find { |node| node.name == 'activity:verb' }
-      expect(verb.text).to eq OStatus::TagManager::VERBS[:request_friend]
-    end
-
-    it 'appends activity:object with target account' do
-      target_account = Fabricate(:account, domain: 'domain.test', uri: 'https://domain.test/id')
-      follow_request = Fabricate(:follow_request, target_account: target_account)
-
-      follow_request_salmon = serialize(follow_request)
-
-      object = follow_request_salmon.nodes.find { |node| node.name == 'activity:object' }
-      expect(object.id.text).to eq 'https://domain.test/id'
-    end
-  end
-
-  shared_examples 'namespaces' do
-    it 'adds namespaces' do
-      element = serialize
-
-      expect(element['xmlns']).to eq OStatus::TagManager::XMLNS
-      expect(element['xmlns:thr']).to eq OStatus::TagManager::THR_XMLNS
-      expect(element['xmlns:activity']).to eq OStatus::TagManager::AS_XMLNS
-      expect(element['xmlns:poco']).to eq OStatus::TagManager::POCO_XMLNS
-      expect(element['xmlns:media']).to eq OStatus::TagManager::MEDIA_XMLNS
-      expect(element['xmlns:ostatus']).to eq OStatus::TagManager::OS_XMLNS
-      expect(element['xmlns:mastodon']).to eq OStatus::TagManager::MTDN_XMLNS
-    end
-  end
-
-  shared_examples 'no namespaces' do
-    it 'does not add namespaces' do
-      expect(serialize['xmlns']).to eq nil
-    end
-  end
-
-  shared_examples 'status attributes' do
-    it 'appends summary element with spoiler text if present' do
-      status = Fabricate(:status, language: :ca, spoiler_text: 'spoiler text')
-
-      element = serialize(status)
-
-      summary = element.summary
-      expect(summary['xml:lang']).to eq 'ca'
-      expect(summary.text).to eq 'spoiler text'
-    end
-
-    it 'does not append summary element with spoiler text if not present' do
-      status = Fabricate(:status, spoiler_text: '')
-      element = serialize(status)
-      element.nodes.each { |node| expect(node.name).not_to eq 'summary' }
-    end
-
-    it 'appends content element with formatted status' do
-      status = Fabricate(:status, language: :ca, text: 'text')
-
-      element = serialize(status)
-
-      content = element.content
-      expect(content[:type]).to eq 'html'
-      expect(content['xml:lang']).to eq 'ca'
-      expect(content.text).to eq '<p>text</p>'
-    end
-
-    it 'appends link elements for mentioned accounts' do
-      account = Fabricate(:account, username: 'username')
-      status = Fabricate(:status)
-      Fabricate(:mention, account: account, status: status)
-
-      element = serialize(status)
-
-      mentioned = element.nodes.find do |node|
-        node.name == 'link' &&
-          node[:rel] == 'mentioned' &&
-          node['ostatus:object-type'] == OStatus::TagManager::TYPES[:person]
-      end
-
-      expect(mentioned[:href]).to eq 'https://cb6e6126.ngrok.io/users/username'
-    end
-
-    it 'appends link elements for emojis' do
-      Fabricate(:custom_emoji)
-
-      status  = Fabricate(:status, text: ':coolcat:')
-      element = serialize(status)
-      emoji   = element.nodes.find { |node| node.name == 'link' && node[:rel] == 'emoji' }
-
-      expect(emoji[:name]).to eq 'coolcat'
-      expect(emoji[:href]).to_not be_blank
-    end
-  end
-
-  describe 'render' do
-    it 'returns XML with emojis' do
-      element = Ox::Element.new('tag')
-      element << '💩'
-      xml = OStatus::AtomSerializer.render(element)
-
-      expect(xml).to eq "<?xml version=\"1.0\"?>\n<tag>💩</tag>\n"
-    end
-
-    it 'returns XML, stripping invalid characters like \b and \v' do
-      element = Ox::Element.new('tag')
-      element << "im l33t\b haxo\b\vr"
-      xml = OStatus::AtomSerializer.render(element)
-
-      expect(xml).to eq "<?xml version=\"1.0\"?>\n<tag>im l33t haxor</tag>\n"
-    end
-  end
-
-  describe '#author' do
-    context 'when note is present' do
-      it 'appends poco:note element with note for local account' do
-        account = Fabricate(:account, domain: nil, note: '<p>note</p>')
-
-        author = OStatus::AtomSerializer.new.author(account)
-
-        note = author.nodes.find { |node| node.name == 'poco:note' }
-        expect(note.text).to eq '<p>note</p>'
-      end
-
-      it 'appends poco:note element with tags-stripped note for remote account' do
-        account = Fabricate(:account, domain: 'remote', note: '<p>note</p>')
-
-        author = OStatus::AtomSerializer.new.author(account)
-
-        note = author.nodes.find { |node| node.name == 'poco:note' }
-        expect(note.text).to eq 'note'
-      end
-
-      it 'appends summary element with type attribute and simplified note if present' do
-        account = Fabricate(:account, note: 'note')
-        author = OStatus::AtomSerializer.new.author(account)
-        expect(author.summary.text).to eq '<p>note</p>'
-        expect(author.summary[:type]).to eq 'html'
-      end
-    end
-
-    context 'when note is not present' do
-      it 'does not append poco:note element' do
-        account = Fabricate(:account, note: '')
-        author = OStatus::AtomSerializer.new.author(account)
-        author.nodes.each { |node| expect(node.name).not_to eq 'poco:note' }
-      end
-
-      it 'does not append summary element' do
-        account = Fabricate(:account, note: '')
-        author = OStatus::AtomSerializer.new.author(account)
-        author.nodes.each { |node| expect(node.name).not_to eq 'summary' }
-      end
-    end
-
-    it 'returns author element' do
-      account = Fabricate(:account)
-      author = OStatus::AtomSerializer.new.author(account)
-      expect(author.name).to eq 'author'
-    end
-
-    it 'appends activity:object-type element with person type' do
-      account = Fabricate(:account, domain: nil, username: 'username')
-
-      author = OStatus::AtomSerializer.new.author(account)
-
-      object_type = author.nodes.find { |node| node.name == 'activity:object-type' }
-      expect(object_type.text).to eq OStatus::TagManager::TYPES[:person]
-    end
-
-    it 'appends email element with username and domain for local account' do
-      account = Fabricate(:account, username: 'username')
-      author = OStatus::AtomSerializer.new.author(account)
-      expect(author.email.text).to eq 'username@cb6e6126.ngrok.io'
-    end
-
-    it 'appends email element with username and domain for remote user' do
-      account = Fabricate(:account, domain: 'domain', username: 'username')
-      author = OStatus::AtomSerializer.new.author(account)
-      expect(author.email.text).to eq 'username@domain'
-    end
-
-    it 'appends link element for an alternative' do
-      account = Fabricate(:account, domain: nil, username: 'username')
-
-      author = OStatus::AtomSerializer.new.author(account)
-
-      link = author.nodes.find { |node| node.name == 'link' && node[:rel] == 'alternate' && node[:type] == 'text/html' }
-      expect(link[:type]).to eq 'text/html'
-      expect(link[:rel]).to eq 'alternate'
-      expect(link[:href]).to eq 'https://cb6e6126.ngrok.io/@username'
-    end
-
-    it 'has link element for avatar if present' do
-      account = Fabricate(:account, avatar: attachment_fixture('avatar.gif'))
-
-      author = OStatus::AtomSerializer.new.author(account)
-
-      link = author.nodes.find { |node| node.name == 'link' && node[:rel] == 'avatar' }
-      expect(link[:type]).to eq 'image/gif'
-      expect(link['media:width']).to eq '120'
-      expect(link['media:height']).to eq '120'
-      expect(link[:href]).to match  /^https:\/\/cb6e6126.ngrok.io\/system\/accounts\/avatars\/.+\/original\/avatar.gif/
-    end
-
-    it 'does not have link element for avatar if not present' do
-      account = Fabricate(:account, avatar: nil)
-
-      author = OStatus::AtomSerializer.new.author(account)
-
-      author.nodes.each do |node|
-        expect(node[:rel]).not_to eq 'avatar' if node.name == 'link'
-      end
-    end
-
-    it 'appends link element for header if present' do
-      account = Fabricate(:account, header: attachment_fixture('avatar.gif'))
-
-      author = OStatus::AtomSerializer.new.author(account)
-
-      link = author.nodes.find { |node| node.name == 'link' && node[:rel] == 'header' }
-      expect(link[:type]).to eq 'image/gif'
-      expect(link['media:width']).to eq '700'
-      expect(link['media:height']).to eq '335'
-      expect(link[:href]).to match  /^https:\/\/cb6e6126.ngrok.io\/system\/accounts\/headers\/.+\/original\/avatar.gif/
-    end
-
-    it 'does not append link element for header if not present' do
-      account = Fabricate(:account, header: nil)
-
-      author = OStatus::AtomSerializer.new.author(account)
-
-      author.nodes.each do |node|
-        expect(node[:rel]).not_to eq 'header' if node.name == 'link'
-      end
-    end
-
-    it 'appends poco:displayName element with display name if present' do
-      account = Fabricate(:account, display_name: 'display name')
-
-      author = OStatus::AtomSerializer.new.author(account)
-
-      display_name = author.nodes.find { |node| node.name == 'poco:displayName' }
-      expect(display_name.text).to eq 'display name'
-    end
-
-    it 'does not append poco:displayName element with display name if not present' do
-      account = Fabricate(:account, display_name: '')
-      author = OStatus::AtomSerializer.new.author(account)
-      author.nodes.each { |node| expect(node.name).not_to eq 'poco:displayName' }
-    end
-
-    it "appends mastodon:scope element with 'private' if locked" do
-      account = Fabricate(:account, locked: true)
-
-      author = OStatus::AtomSerializer.new.author(account)
-
-      scope = author.nodes.find { |node| node.name == 'mastodon:scope' }
-      expect(scope.text).to eq 'private'
-    end
-
-    it "appends mastodon:scope element with 'public' if unlocked" do
-      account = Fabricate(:account, locked: false)
-
-      author = OStatus::AtomSerializer.new.author(account)
-
-      scope = author.nodes.find { |node| node.name == 'mastodon:scope' }
-      expect(scope.text).to eq 'public'
-    end
-
-    it 'includes URI' do
-      account = Fabricate(:account, domain: nil, username: 'username')
-
-      author = OStatus::AtomSerializer.new.author(account)
-
-      expect(author.id.text).to eq 'https://cb6e6126.ngrok.io/users/username'
-      expect(author.uri.text).to eq 'https://cb6e6126.ngrok.io/users/username'
-    end
-
-    it 'includes username' do
-      account = Fabricate(:account, username: 'username')
-
-      author = OStatus::AtomSerializer.new.author(account)
-
-      name = author.nodes.find { |node| node.name == 'name' }
-      username = author.nodes.find { |node| node.name == 'poco:preferredUsername' }
-      expect(name.text).to eq 'username'
-      expect(username.text).to eq 'username'
-    end
-  end
-
-  describe '#entry' do
-    shared_examples 'not root' do
-      include_examples 'no namespaces' do
-        def serialize
-          subject
-        end
-      end
-
-      it 'does not append author element' do
-        subject.nodes.each { |node| expect(node.name).not_to eq 'author' }
-      end
-    end
-
-    context 'it is root' do
-      include_examples 'namespaces' do
-        def serialize
-          stream_entry = Fabricate(:stream_entry)
-          OStatus::AtomSerializer.new.entry(stream_entry, true)
-        end
-      end
-
-      it 'appends author element' do
-        account = Fabricate(:account, username: 'username')
-        status = Fabricate(:status, account: account)
-
-        entry = OStatus::AtomSerializer.new.entry(status.stream_entry, true)
-
-        expect(entry.author.id.text).to eq 'https://cb6e6126.ngrok.io/users/username'
-      end
-    end
-
-    context 'if status is present' do
-      include_examples 'status attributes' do
-        def serialize(status)
-          OStatus::AtomSerializer.new.entry(status.stream_entry, true)
-        end
-      end
-
-      it 'appends link element for the public collection if status is publicly visible' do
-        status = Fabricate(:status, visibility: :public)
-
-        entry = OStatus::AtomSerializer.new.entry(status.stream_entry)
-
-        mentioned_person = entry.nodes.find do |node|
-          node.name == 'link' &&
-          node[:rel] == 'mentioned' &&
-          node['ostatus:object-type'] == OStatus::TagManager::TYPES[:collection]
-        end
-        expect(mentioned_person[:href]).to eq OStatus::TagManager::COLLECTIONS[:public]
-      end
-
-      it 'does not append link element for the public collection if status is not publicly visible' do
-        status = Fabricate(:status, visibility: :private)
-
-        entry = OStatus::AtomSerializer.new.entry(status.stream_entry)
-
-        entry.nodes.each do |node|
-          if node.name == 'link' &&
-             node[:rel] == 'mentioned' &&
-             node['ostatus:object-type'] == OStatus::TagManager::TYPES[:collection]
-            expect(mentioned_collection[:href]).not_to eq OStatus::TagManager::COLLECTIONS[:public]
-          end
-        end
-      end
-
-      it 'appends category elements for tags' do
-        tag = Fabricate(:tag, name: 'tag')
-        status = Fabricate(:status, tags: [ tag ])
-
-        entry = OStatus::AtomSerializer.new.entry(status.stream_entry)
-
-        expect(entry.category[:term]).to eq 'tag'
-      end
-
-      it 'appends link elements for media attachments' do
-        file = attachment_fixture('attachment.jpg')
-        media_attachment = Fabricate(:media_attachment, file: file)
-        status = Fabricate(:status, media_attachments: [ media_attachment ])
-
-        entry = OStatus::AtomSerializer.new.entry(status.stream_entry)
-
-        enclosure = entry.nodes.find { |node| node.name == 'link' && node[:rel] == 'enclosure' }
-        expect(enclosure[:type]).to eq 'image/jpeg'
-        expect(enclosure[:href]).to match /^https:\/\/cb6e6126.ngrok.io\/system\/media_attachments\/files\/.+\/original\/attachment.jpg$/
-      end
-
-      it 'appends mastodon:scope element with visibility' do
-        status = Fabricate(:status, visibility: :public)
-
-        entry = OStatus::AtomSerializer.new.entry(status.stream_entry)
-
-        scope = entry.nodes.find { |node| node.name == 'mastodon:scope' }
-        expect(scope.text).to eq 'public'
-      end
-
-      it 'returns element whose rendered view triggers creation when processed' do
-        remote_account = Account.create!(username: 'username')
-        remote_status = Fabricate(:status, account: remote_account, created_at: '2000-01-01T00:00:00Z')
-
-        entry = OStatus::AtomSerializer.new.entry(remote_status.stream_entry, true)
-        entry.nodes.delete_if { |node| node[:type] == 'application/activity+json' } # Remove ActivityPub link to simplify test
-        xml = OStatus::AtomSerializer.render(entry).gsub('cb6e6126.ngrok.io', 'remote.test')
-
-        remote_status.destroy!
-        remote_account.destroy!
-
-        account = Account.create!(
-          domain: 'remote.test',
-          username: 'username',
-          last_webfingered_at: Time.now.utc
-        )
-
-        ProcessFeedService.new.call(xml, account)
-
-        expect(Status.find_by(uri: "https://remote.test/users/#{remote_status.account.to_param}/statuses/#{remote_status.id}")).to be_instance_of Status
-      end
-    end
-
-    context 'if status is not present' do
-      it 'appends content element saying status is deleted' do
-        status = Fabricate(:status)
-        status.destroy!
-
-        entry = OStatus::AtomSerializer.new.entry(status.stream_entry)
-
-        expect(entry.content.text).to eq 'Deleted status'
-      end
-
-      it 'appends title element saying the status is deleted' do
-        account = Fabricate(:account, username: 'username')
-        status = Fabricate(:status, account: account)
-        status.destroy!
-
-        entry = OStatus::AtomSerializer.new.entry(status.stream_entry)
-
-        expect(entry.title.text).to eq 'username deleted status'
-      end
-    end
-
-    context 'it is not root' do
-      let(:stream_entry) { Fabricate(:stream_entry) }
-      subject { OStatus::AtomSerializer.new.entry(stream_entry, false) }
-      include_examples 'not root'
-    end
-
-    context 'without root parameter' do
-      let(:stream_entry) { Fabricate(:stream_entry) }
-      subject { OStatus::AtomSerializer.new.entry(stream_entry) }
-      include_examples 'not root'
-    end
-
-    it 'returns entry element' do
-      stream_entry = Fabricate(:stream_entry)
-      entry = OStatus::AtomSerializer.new.entry(stream_entry)
-      expect(entry.name).to eq 'entry'
-    end
-
-    it 'appends id element with unique tag' do
-      status = Fabricate(:status, reblog_of_id: nil, created_at: '2000-01-01T00:00:00Z')
-
-      entry = OStatus::AtomSerializer.new.entry(status.stream_entry)
-
-      expect(entry.id.text).to eq "https://cb6e6126.ngrok.io/users/#{status.account.to_param}/statuses/#{status.id}"
-    end
-
-    it 'appends published element with created date' do
-      stream_entry = Fabricate(:stream_entry, created_at: '2000-01-01T00:00:00Z')
-      entry = OStatus::AtomSerializer.new.entry(stream_entry)
-      expect(entry.published.text).to eq '2000-01-01T00:00:00Z'
-    end
-
-    it 'appends updated element with updated date' do
-      stream_entry = Fabricate(:stream_entry, updated_at: '2000-01-01T00:00:00Z')
-      entry = OStatus::AtomSerializer.new.entry(stream_entry)
-      expect(entry.updated.text).to eq '2000-01-01T00:00:00Z'
-    end
-
-    it 'appends title element with status title' do
-      account = Fabricate(:account, username: 'username')
-      status = Fabricate(:status, account: account, reblog_of_id: nil)
-      entry = OStatus::AtomSerializer.new.entry(status.stream_entry)
-      expect(entry.title.text).to eq 'New status by username'
-    end
-
-    it 'appends activity:object-type element with object type' do
-      status = Fabricate(:status)
-      entry = OStatus::AtomSerializer.new.entry(status.stream_entry)
-      object_type = entry.nodes.find { |node| node.name == 'activity:object-type' }
-      expect(object_type.text).to eq OStatus::TagManager::TYPES[:note]
-    end
-
-    it 'appends activity:verb element with object type' do
-      status = Fabricate(:status)
-
-      entry = OStatus::AtomSerializer.new.entry(status.stream_entry)
-
-      object_type = entry.nodes.find { |node| node.name == 'activity:verb' }
-      expect(object_type.text).to eq OStatus::TagManager::VERBS[:post]
-    end
-
-    it 'appends activity:object element with target if present' do
-      reblogged = Fabricate(:status, created_at: '2000-01-01T00:00:00Z')
-      reblog = Fabricate(:status, reblog: reblogged)
-
-      entry = OStatus::AtomSerializer.new.entry(reblog.stream_entry)
-
-      object = entry.nodes.find { |node| node.name == 'activity:object' }
-      expect(object.id.text).to eq "https://cb6e6126.ngrok.io/users/#{reblogged.account.to_param}/statuses/#{reblogged.id}"
-    end
-
-    it 'does not append activity:object element if target is not present' do
-      status = Fabricate(:status, reblog_of_id: nil)
-      entry = OStatus::AtomSerializer.new.entry(status.stream_entry)
-      entry.nodes.each { |node| expect(node.name).not_to eq 'activity:object' }
-    end
-
-    it 'appends link element for an alternative' do
-      account = Fabricate(:account, username: 'username')
-      status = Fabricate(:status, account: account)
-
-      entry = OStatus::AtomSerializer.new.entry(status.stream_entry)
-
-      link = entry.nodes.find { |node| node.name == 'link' && node[:rel] == 'alternate' && node[:type] == 'text/html' }
-      expect(link[:type]).to eq 'text/html'
-      expect(link[:href]).to eq "https://cb6e6126.ngrok.io/@username/#{status.id}"
-    end
-
-    it 'appends link element for itself' do
-      account = Fabricate(:account, username: 'username')
-      status = Fabricate(:status, account: account)
-
-      entry = OStatus::AtomSerializer.new.entry(status.stream_entry)
-
-      link = entry.nodes.find { |node| node.name == 'link' && node[:rel] == 'self' }
-      expect(link[:type]).to eq 'application/atom+xml'
-      expect(link[:href]).to eq "https://cb6e6126.ngrok.io/users/username/updates/#{status.stream_entry.id}.atom"
-    end
-
-    it 'appends thr:in-reply-to element if threaded' do
-      in_reply_to_status = Fabricate(:status, created_at: '2000-01-01T00:00:00Z', reblog_of_id: nil)
-      reply_status = Fabricate(:status, in_reply_to_id: in_reply_to_status.id)
-
-      entry = OStatus::AtomSerializer.new.entry(reply_status.stream_entry)
-
-      in_reply_to = entry.nodes.find { |node| node.name == 'thr:in-reply-to' }
-      expect(in_reply_to[:ref]).to eq "https://cb6e6126.ngrok.io/users/#{in_reply_to_status.account.to_param}/statuses/#{in_reply_to_status.id}"
-    end
-
-    it 'does not append thr:in-reply-to element if not threaded' do
-      status = Fabricate(:status)
-      entry = OStatus::AtomSerializer.new.entry(status.stream_entry)
-      entry.nodes.each { |node| expect(node.name).not_to eq 'thr:in-reply-to' }
-    end
-
-    it 'appends ostatus:conversation if conversation id is present' do
-      status = Fabricate(:status)
-      status.conversation.update!(created_at: '2000-01-01T00:00:00Z')
-
-      entry = OStatus::AtomSerializer.new.entry(status.stream_entry)
-
-      conversation = entry.nodes.find { |node| node.name == 'ostatus:conversation' }
-      expect(conversation[:ref]).to eq "tag:cb6e6126.ngrok.io,2000-01-01:objectId=#{status.conversation_id}:objectType=Conversation"
-    end
-
-    it 'does not append ostatus:conversation if conversation id is not present' do
-      status = Fabricate.build(:status, conversation_id: nil)
-      status.save!(validate: false)
-
-      entry = OStatus::AtomSerializer.new.entry(status.stream_entry)
-
-      entry.nodes.each { |node| expect(node.name).not_to eq 'ostatus:conversation' }
-    end
-  end
-
-  describe '#feed' do
-    include_examples 'namespaces' do
-      def serialize
-        account = Fabricate(:account)
-        OStatus::AtomSerializer.new.feed(account, [])
-      end
-    end
-
-    it 'returns feed element' do
-      account = Fabricate(:account)
-      feed = OStatus::AtomSerializer.new.feed(account, [])
-      expect(feed.name).to eq 'feed'
-    end
-
-    it 'appends id element with account Atom URL' do
-      account = Fabricate(:account, username: 'username')
-      feed = OStatus::AtomSerializer.new.feed(account, [])
-      expect(feed.id.text).to eq 'https://cb6e6126.ngrok.io/users/username.atom'
-    end
-
-    it 'appends title element with account display name if present' do
-      account = Fabricate(:account, display_name: 'display name')
-      feed = OStatus::AtomSerializer.new.feed(account, [])
-      expect(feed.title.text).to eq 'display name'
-    end
-
-    it 'does not append title element with account username if account display name is not present' do
-      account = Fabricate(:account, display_name: '', username: 'username')
-      feed = OStatus::AtomSerializer.new.feed(account, [])
-      expect(feed.title.text).to eq 'username'
-    end
-
-    it 'appends subtitle element with account note' do
-      account = Fabricate(:account, note: 'note')
-      feed = OStatus::AtomSerializer.new.feed(account, [])
-      expect(feed.subtitle.text).to eq 'note'
-    end
-
-    it 'appends updated element with date account got updated' do
-      account = Fabricate(:account, updated_at: '2000-01-01T00:00:00Z')
-      feed = OStatus::AtomSerializer.new.feed(account, [])
-      expect(feed.updated.text).to eq '2000-01-01T00:00:00Z'
-    end
-
-    it 'appends logo element with full asset URL for original account avatar' do
-      account = Fabricate(:account, avatar: attachment_fixture('avatar.gif'))
-      feed = OStatus::AtomSerializer.new.feed(account, [])
-      expect(feed.logo.text).to match /^https:\/\/cb6e6126.ngrok.io\/system\/accounts\/avatars\/.+\/original\/avatar.gif/
-    end
-
-    it 'appends author element' do
-      account = Fabricate(:account, username: 'username')
-      feed = OStatus::AtomSerializer.new.feed(account, [])
-      expect(feed.author.id.text).to eq 'https://cb6e6126.ngrok.io/users/username'
-    end
-
-    it 'appends link element for an alternative' do
-      account = Fabricate(:account, username: 'username')
-
-      feed = OStatus::AtomSerializer.new.feed(account, [])
-
-      link = feed.nodes.find { |node| node.name == 'link' && node[:rel] == 'alternate' && node[:type] == 'text/html' }
-      expect(link[:type]).to eq 'text/html'
-      expect(link[:href]).to eq 'https://cb6e6126.ngrok.io/@username'
-    end
-
-    it 'appends link element for itself' do
-      account = Fabricate(:account, username: 'username')
-
-      feed = OStatus::AtomSerializer.new.feed(account, [])
-
-      link = feed.nodes.find { |node| node.name == 'link' && node[:rel] == 'self' }
-      expect(link[:type]).to eq 'application/atom+xml'
-      expect(link[:href]).to eq 'https://cb6e6126.ngrok.io/users/username.atom'
-    end
-
-    it 'appends link element for the next if it has 20 stream entries' do
-      account = Fabricate(:account, username: 'username')
-      stream_entry = Fabricate(:stream_entry)
-
-      feed = OStatus::AtomSerializer.new.feed(account, Array.new(20, stream_entry))
-
-      link = feed.nodes.find { |node| node.name == 'link' && node[:rel] == 'next' }
-      expect(link[:type]).to eq 'application/atom+xml'
-      expect(link[:href]).to eq "https://cb6e6126.ngrok.io/users/username.atom?max_id=#{stream_entry.id}"
-    end
-
-    it 'does not append link element for the next if it does not have 20 stream entries' do
-      account = Fabricate(:account, username: 'username')
-
-      feed = OStatus::AtomSerializer.new.feed(account, [])
-
-      feed.nodes.each do |node|
-        expect(node[:rel]).not_to eq 'next' if node.name == 'link'
-      end
-    end
-
-    it 'appends link element for hub' do
-      account = Fabricate(:account, username: 'username')
-
-      feed = OStatus::AtomSerializer.new.feed(account, [])
-
-      link = feed.nodes.find { |node| node.name == 'link' && node[:rel] == 'hub' }
-      expect(link[:href]).to eq 'https://cb6e6126.ngrok.io/api/push'
-    end
-
-    it 'appends link element for Salmon' do
-      account = Fabricate(:account, username: 'username')
-
-      feed = OStatus::AtomSerializer.new.feed(account, [])
-
-      link = feed.nodes.find { |node| node.name == 'link' && node[:rel] == 'salmon' }
-      expect(link[:href]).to start_with 'https://cb6e6126.ngrok.io/api/salmon/'
-    end
-
-    it 'appends stream entries' do
-      account = Fabricate(:account, username: 'username')
-      status = Fabricate(:status, account: account)
-
-      feed = OStatus::AtomSerializer.new.feed(account, [status.stream_entry])
-
-      expect(feed.entry.title.text).to eq 'New status by username'
-    end
-  end
-
-  describe '#block_salmon' do
-    include_examples 'namespaces' do
-      def serialize
-        block = Fabricate(:block)
-        OStatus::AtomSerializer.new.block_salmon(block)
-      end
-    end
-
-    it 'returns entry element' do
-      block = Fabricate(:block)
-      block_salmon = OStatus::AtomSerializer.new.block_salmon(block)
-      expect(block_salmon.name).to eq 'entry'
-    end
-
-    it 'appends id element with unique tag' do
-      block = Fabricate(:block)
-
-      time_before = Time.zone.now
-      block_salmon = OStatus::AtomSerializer.new.block_salmon(block)
-      time_after = Time.zone.now
-
-      expect(block_salmon.id.text).to(
-        eq(OStatus::TagManager.instance.unique_tag(time_before.utc, block.id, 'Block'))
-          .or(eq(OStatus::TagManager.instance.unique_tag(time_after.utc, block.id, 'Block')))
-      )
-    end
-
-    it 'appends title element with description' do
-      account = Fabricate(:account, domain: nil, username: 'account')
-      target_account = Fabricate(:account, domain: 'remote', username: 'target_account')
-      block = Fabricate(:block, account: account, target_account: target_account)
-
-      block_salmon = OStatus::AtomSerializer.new.block_salmon(block)
-
-      expect(block_salmon.title.text).to eq 'account no longer wishes to interact with target_account@remote'
-    end
-
-    it 'appends author element with account' do
-      account = Fabricate(:account, domain: nil, username: 'account')
-      block = Fabricate(:block, account: account)
-
-      block_salmon = OStatus::AtomSerializer.new.block_salmon(block)
-
-      expect(block_salmon.author.id.text).to eq 'https://cb6e6126.ngrok.io/users/account'
-    end
-
-    it 'appends activity:object-type element with activity type' do
-      block = Fabricate(:block)
-
-      block_salmon = OStatus::AtomSerializer.new.block_salmon(block)
-
-      object_type = block_salmon.nodes.find { |node| node.name == 'activity:object-type' }
-      expect(object_type.text).to eq OStatus::TagManager::TYPES[:activity]
-    end
-
-    it 'appends activity:verb element with block' do
-      block = Fabricate(:block)
-
-      block_salmon = OStatus::AtomSerializer.new.block_salmon(block)
-
-      verb = block_salmon.nodes.find { |node| node.name == 'activity:verb' }
-      expect(verb.text).to eq OStatus::TagManager::VERBS[:block]
-    end
-
-    it 'appends activity:object element with target account' do
-      target_account = Fabricate(:account, domain: 'domain.test', uri: 'https://domain.test/id')
-      block = Fabricate(:block, target_account: target_account)
-
-      block_salmon = OStatus::AtomSerializer.new.block_salmon(block)
-
-      object = block_salmon.nodes.find { |node| node.name == 'activity:object' }
-      expect(object.id.text).to eq 'https://domain.test/id'
-    end
-
-    it 'returns element whose rendered view triggers block when processed' do
-      block = Fabricate(:block)
-      block_salmon = OStatus::AtomSerializer.new.block_salmon(block)
-      xml = OStatus::AtomSerializer.render(block_salmon)
-      envelope = OStatus2::Salmon.new.pack(xml, block.account.keypair)
-      block.destroy!
-
-      ProcessInteractionService.new.call(envelope, block.target_account)
-
-      expect(block.account.blocking?(block.target_account)).to be true
-    end
-  end
-
-  describe '#unblock_salmon' do
-    include_examples 'namespaces' do
-      def serialize
-        block = Fabricate(:block)
-        OStatus::AtomSerializer.new.unblock_salmon(block)
-      end
-    end
-
-    it 'returns entry element' do
-      block = Fabricate(:block)
-      unblock_salmon = OStatus::AtomSerializer.new.unblock_salmon(block)
-      expect(unblock_salmon.name).to eq 'entry'
-    end
-
-    it 'appends id element with unique tag' do
-      block = Fabricate(:block)
-
-      time_before = Time.zone.now
-      unblock_salmon = OStatus::AtomSerializer.new.unblock_salmon(block)
-      time_after = Time.zone.now
-
-      expect(unblock_salmon.id.text).to(
-        eq(OStatus::TagManager.instance.unique_tag(time_before.utc, block.id, 'Block'))
-          .or(eq(OStatus::TagManager.instance.unique_tag(time_after.utc, block.id, 'Block')))
-      )
-    end
-
-    it 'appends title element with description' do
-      account = Fabricate(:account, domain: nil, username: 'account')
-      target_account = Fabricate(:account, domain: 'remote', username: 'target_account')
-      block = Fabricate(:block, account: account, target_account: target_account)
-
-      unblock_salmon = OStatus::AtomSerializer.new.unblock_salmon(block)
-
-      expect(unblock_salmon.title.text).to eq 'account no longer blocks target_account@remote'
-    end
-
-    it 'appends author element with account' do
-      account = Fabricate(:account, domain: nil, username: 'account')
-      block = Fabricate(:block, account: account)
-
-      unblock_salmon = OStatus::AtomSerializer.new.unblock_salmon(block)
-
-      expect(unblock_salmon.author.id.text).to eq 'https://cb6e6126.ngrok.io/users/account'
-    end
-
-    it 'appends activity:object-type element with activity type' do
-      block = Fabricate(:block)
-
-      unblock_salmon = OStatus::AtomSerializer.new.unblock_salmon(block)
-
-      object_type = unblock_salmon.nodes.find { |node| node.name == 'activity:object-type' }
-      expect(object_type.text).to eq OStatus::TagManager::TYPES[:activity]
-    end
-
-    it 'appends activity:verb element with block' do
-      block = Fabricate(:block)
-
-      unblock_salmon = OStatus::AtomSerializer.new.unblock_salmon(block)
-
-      verb = unblock_salmon.nodes.find { |node| node.name == 'activity:verb' }
-      expect(verb.text).to eq OStatus::TagManager::VERBS[:unblock]
-    end
-
-    it 'appends activity:object element with target account' do
-      target_account = Fabricate(:account, domain: 'domain.test', uri: 'https://domain.test/id')
-      block = Fabricate(:block, target_account: target_account)
-
-      unblock_salmon = OStatus::AtomSerializer.new.unblock_salmon(block)
-
-      object = unblock_salmon.nodes.find { |node| node.name == 'activity:object' }
-      expect(object.id.text).to eq 'https://domain.test/id'
-    end
-
-    it 'returns element whose rendered view triggers block when processed' do
-      block = Fabricate(:block)
-      unblock_salmon = OStatus::AtomSerializer.new.unblock_salmon(block)
-      xml = OStatus::AtomSerializer.render(unblock_salmon)
-      envelope = OStatus2::Salmon.new.pack(xml, block.account.keypair)
-
-      ProcessInteractionService.new.call(envelope, block.target_account)
-
-      expect { block.reload }.to raise_error ActiveRecord::RecordNotFound
-    end
-  end
-
-  describe '#favourite_salmon' do
-    include_examples 'namespaces' do
-      def serialize
-        favourite = Fabricate(:favourite)
-        OStatus::AtomSerializer.new.favourite_salmon(favourite)
-      end
-    end
-
-    it 'returns entry element' do
-      favourite = Fabricate(:favourite)
-      favourite_salmon = OStatus::AtomSerializer.new.favourite_salmon(favourite)
-      expect(favourite_salmon.name).to eq 'entry'
-    end
-
-    it 'appends id element with unique tag' do
-      favourite = Fabricate(:favourite, created_at: '2000-01-01T00:00:00Z')
-      favourite_salmon = OStatus::AtomSerializer.new.favourite_salmon(favourite)
-      expect(favourite_salmon.id.text).to eq "tag:cb6e6126.ngrok.io,2000-01-01:objectId=#{favourite.id}:objectType=Favourite"
-    end
-
-    it 'appends author element with account' do
-      account = Fabricate(:account, domain: nil, username: 'username')
-      favourite = Fabricate(:favourite, account: account)
-
-      favourite_salmon = OStatus::AtomSerializer.new.favourite_salmon(favourite)
-
-      expect(favourite_salmon.author.id.text).to eq 'https://cb6e6126.ngrok.io/users/username'
-    end
-
-    it 'appends activity:object-type element with activity type' do
-      favourite = Fabricate(:favourite)
-
-      favourite_salmon = OStatus::AtomSerializer.new.favourite_salmon(favourite)
-
-      object_type = favourite_salmon.nodes.find { |node| node.name == 'activity:object-type' }
-      expect(object_type.text).to eq 'http://activitystrea.ms/schema/1.0/activity'
-    end
-
-    it 'appends activity:verb element with favorite' do
-      favourite = Fabricate(:favourite)
-
-      favourite_salmon = OStatus::AtomSerializer.new.favourite_salmon(favourite)
-
-      verb = favourite_salmon.nodes.find { |node| node.name == 'activity:verb' }
-      expect(verb.text).to eq OStatus::TagManager::VERBS[:favorite]
-    end
-
-    it 'appends activity:object element with status' do
-      status = Fabricate(:status, created_at: '2000-01-01T00:00:00Z')
-      favourite = Fabricate(:favourite, status: status)
-
-      favourite_salmon = OStatus::AtomSerializer.new.favourite_salmon(favourite)
-
-      object = favourite_salmon.nodes.find { |node| node.name == 'activity:object' }
-      expect(object.id.text).to eq "https://cb6e6126.ngrok.io/users/#{status.account.to_param}/statuses/#{status.id}"
-    end
-
-    it 'appends thr:in-reply-to element for status' do
-      status_account = Fabricate(:account, username: 'username')
-      status = Fabricate(:status, account: status_account, created_at: '2000-01-01T00:00:00Z')
-      favourite = Fabricate(:favourite, status: status)
-
-      favourite_salmon = OStatus::AtomSerializer.new.favourite_salmon(favourite)
-
-      in_reply_to = favourite_salmon.nodes.find { |node| node.name == 'thr:in-reply-to' }
-      expect(in_reply_to.ref).to eq "https://cb6e6126.ngrok.io/users/#{status.account.to_param}/statuses/#{status.id}"
-      expect(in_reply_to.href).to eq "https://cb6e6126.ngrok.io/@username/#{status.id}"
-    end
-
-    it 'includes description' do
-      account = Fabricate(:account, domain: nil, username: 'account')
-      status_account = Fabricate(:account, domain: 'remote', username: 'status_account')
-      status = Fabricate(:status, account: status_account)
-      favourite = Fabricate(:favourite, account: account, status: status)
-
-      favourite_salmon = OStatus::AtomSerializer.new.favourite_salmon(favourite)
-
-      expect(favourite_salmon.title.text).to eq 'account favourited a status by status_account@remote'
-      expect(favourite_salmon.content.text).to eq 'account favourited a status by status_account@remote'
-    end
-
-    it 'returns element whose rendered view triggers favourite when processed' do
-      favourite = Fabricate(:favourite)
-      favourite_salmon = OStatus::AtomSerializer.new.favourite_salmon(favourite)
-      xml = OStatus::AtomSerializer.render(favourite_salmon)
-      envelope = OStatus2::Salmon.new.pack(xml, favourite.account.keypair)
-      favourite.destroy!
-
-      ProcessInteractionService.new.call(envelope, favourite.status.account)
-      expect(favourite.account.favourited?(favourite.status)).to be true
-    end
-  end
-
-  describe '#unfavourite_salmon' do
-    include_examples 'namespaces' do
-      def serialize
-        favourite = Fabricate(:favourite)
-        OStatus::AtomSerializer.new.favourite_salmon(favourite)
-      end
-    end
-
-    it 'returns entry element' do
-      favourite = Fabricate(:favourite)
-      unfavourite_salmon = OStatus::AtomSerializer.new.unfavourite_salmon(favourite)
-      expect(unfavourite_salmon.name).to eq 'entry'
-    end
-
-    it 'appends id element with unique tag' do
-      favourite = Fabricate(:favourite)
-
-      time_before = Time.zone.now
-      unfavourite_salmon = OStatus::AtomSerializer.new.unfavourite_salmon(favourite)
-      time_after = Time.zone.now
-
-      expect(unfavourite_salmon.id.text).to(
-        eq(OStatus::TagManager.instance.unique_tag(time_before.utc, favourite.id, 'Favourite'))
-          .or(eq(OStatus::TagManager.instance.unique_tag(time_after.utc, favourite.id, 'Favourite')))
-      )
-    end
-
-    it 'appends author element with account' do
-      account = Fabricate(:account, domain: nil, username: 'username')
-      favourite = Fabricate(:favourite, account: account)
-
-      unfavourite_salmon = OStatus::AtomSerializer.new.unfavourite_salmon(favourite)
-
-      expect(unfavourite_salmon.author.id.text).to eq 'https://cb6e6126.ngrok.io/users/username'
-    end
-
-    it 'appends activity:object-type element with activity type' do
-      favourite = Fabricate(:favourite)
-
-      unfavourite_salmon = OStatus::AtomSerializer.new.unfavourite_salmon(favourite)
-
-      object_type = unfavourite_salmon.nodes.find { |node| node.name == 'activity:object-type' }
-      expect(object_type.text).to eq 'http://activitystrea.ms/schema/1.0/activity'
-    end
-
-    it 'appends activity:verb element with favorite' do
-      favourite = Fabricate(:favourite)
-
-      unfavourite_salmon = OStatus::AtomSerializer.new.unfavourite_salmon(favourite)
-
-      verb = unfavourite_salmon.nodes.find { |node| node.name == 'activity:verb' }
-      expect(verb.text).to eq OStatus::TagManager::VERBS[:unfavorite]
-    end
-
-    it 'appends activity:object element with status' do
-      status = Fabricate(:status, created_at: '2000-01-01T00:00:00Z')
-      favourite = Fabricate(:favourite, status: status)
-
-      unfavourite_salmon = OStatus::AtomSerializer.new.unfavourite_salmon(favourite)
-
-      object = unfavourite_salmon.nodes.find { |node| node.name == 'activity:object' }
-      expect(object.id.text).to eq "https://cb6e6126.ngrok.io/users/#{status.account.to_param}/statuses/#{status.id}"
-    end
-
-    it 'appends thr:in-reply-to element for status' do
-      status_account = Fabricate(:account, username: 'username')
-      status = Fabricate(:status, account: status_account, created_at: '2000-01-01T00:00:00Z')
-      favourite = Fabricate(:favourite, status: status)
-
-      unfavourite_salmon = OStatus::AtomSerializer.new.unfavourite_salmon(favourite)
-
-      in_reply_to = unfavourite_salmon.nodes.find { |node| node.name == 'thr:in-reply-to' }
-      expect(in_reply_to.ref).to eq "https://cb6e6126.ngrok.io/users/#{status.account.to_param}/statuses/#{status.id}"
-      expect(in_reply_to.href).to eq "https://cb6e6126.ngrok.io/@username/#{status.id}"
-    end
-
-    it 'includes description' do
-      account = Fabricate(:account, domain: nil, username: 'account')
-      status_account = Fabricate(:account, domain: 'remote', username: 'status_account')
-      status = Fabricate(:status, account: status_account)
-      favourite = Fabricate(:favourite, account: account, status: status)
-
-      unfavourite_salmon = OStatus::AtomSerializer.new.unfavourite_salmon(favourite)
-
-      expect(unfavourite_salmon.title.text).to eq 'account no longer favourites a status by status_account@remote'
-      expect(unfavourite_salmon.content.text).to eq 'account no longer favourites a status by status_account@remote'
-    end
-
-    it 'returns element whose rendered view triggers unfavourite when processed' do
-      favourite = Fabricate(:favourite)
-      unfavourite_salmon = OStatus::AtomSerializer.new.unfavourite_salmon(favourite)
-      xml = OStatus::AtomSerializer.render(unfavourite_salmon)
-      envelope = OStatus2::Salmon.new.pack(xml, favourite.account.keypair)
-
-      ProcessInteractionService.new.call(envelope, favourite.status.account)
-      expect { favourite.reload }.to raise_error ActiveRecord::RecordNotFound
-    end
-  end
-
-  describe '#follow_salmon' do
-    include_examples 'namespaces' do
-      def serialize
-        follow = Fabricate(:follow)
-        OStatus::AtomSerializer.new.follow_salmon(follow)
-      end
-    end
-
-    it 'returns entry element' do
-      follow = Fabricate(:follow)
-      follow_salmon = OStatus::AtomSerializer.new.follow_salmon(follow)
-      expect(follow_salmon.name).to eq 'entry'
-    end
-
-    it 'appends id element with unique tag' do
-      follow = Fabricate(:follow, created_at: '2000-01-01T00:00:00Z')
-      follow_salmon = OStatus::AtomSerializer.new.follow_salmon(follow)
-      expect(follow_salmon.id.text).to eq "tag:cb6e6126.ngrok.io,2000-01-01:objectId=#{follow.id}:objectType=Follow"
-    end
-
-    it 'appends author element with account' do
-      account = Fabricate(:account, domain: nil, username: 'username')
-      follow = Fabricate(:follow, account: account)
-
-      follow_salmon = OStatus::AtomSerializer.new.follow_salmon(follow)
-
-      expect(follow_salmon.author.id.text).to eq 'https://cb6e6126.ngrok.io/users/username'
-    end
-
-    it 'appends activity:object-type element with activity type' do
-      follow = Fabricate(:follow)
-
-      follow_salmon = OStatus::AtomSerializer.new.follow_salmon(follow)
-
-      object_type = follow_salmon.nodes.find { |node| node.name == 'activity:object-type' }
-      expect(object_type.text).to eq OStatus::TagManager::TYPES[:activity]
-    end
-
-    it 'appends activity:verb element with follow' do
-      follow = Fabricate(:follow)
-
-      follow_salmon = OStatus::AtomSerializer.new.follow_salmon(follow)
-
-      verb = follow_salmon.nodes.find { |node| node.name == 'activity:verb' }
-      expect(verb.text).to eq OStatus::TagManager::VERBS[:follow]
-    end
-
-    it 'appends activity:object element with target account' do
-      target_account = Fabricate(:account, domain: 'domain.test', uri: 'https://domain.test/id')
-      follow = Fabricate(:follow, target_account: target_account)
-
-      follow_salmon = OStatus::AtomSerializer.new.follow_salmon(follow)
-
-      object = follow_salmon.nodes.find { |node| node.name == 'activity:object' }
-      expect(object.id.text).to eq 'https://domain.test/id'
-    end
-
-    it 'includes description' do
-      account = Fabricate(:account, domain: nil, username: 'account')
-      target_account = Fabricate(:account, domain: 'remote', username: 'target_account')
-      follow = Fabricate(:follow, account: account, target_account: target_account)
-
-      follow_salmon = OStatus::AtomSerializer.new.follow_salmon(follow)
-
-      expect(follow_salmon.title.text).to eq 'account started following target_account@remote'
-      expect(follow_salmon.content.text).to eq 'account started following target_account@remote'
-    end
-
-    it 'returns element whose rendered view triggers follow when processed' do
-      follow = Fabricate(:follow)
-      follow_salmon = OStatus::AtomSerializer.new.follow_salmon(follow)
-      xml = OStatus::AtomSerializer.render(follow_salmon)
-      follow.destroy!
-      envelope = OStatus2::Salmon.new.pack(xml, follow.account.keypair)
-
-      ProcessInteractionService.new.call(envelope, follow.target_account)
-
-      expect(follow.account.following?(follow.target_account)).to be true
-    end
-  end
-
-  describe '#unfollow_salmon' do
-    include_examples 'namespaces' do
-      def serialize
-        follow = Fabricate(:follow)
-        follow.destroy!
-        OStatus::AtomSerializer.new.unfollow_salmon(follow)
-      end
-    end
-
-    it 'returns entry element' do
-      follow = Fabricate(:follow)
-      follow.destroy!
-
-      unfollow_salmon = OStatus::AtomSerializer.new.unfollow_salmon(follow)
-
-      expect(unfollow_salmon.name).to eq 'entry'
-    end
-
-    it 'appends id element with unique tag' do
-      follow = Fabricate(:follow)
-      follow.destroy!
-
-      time_before = Time.zone.now
-      unfollow_salmon = OStatus::AtomSerializer.new.unfollow_salmon(follow)
-      time_after = Time.zone.now
-
-      expect(unfollow_salmon.id.text).to(
-        eq(OStatus::TagManager.instance.unique_tag(time_before.utc, follow.id, 'Follow'))
-          .or(eq(OStatus::TagManager.instance.unique_tag(time_after.utc, follow.id, 'Follow')))
-      )
-    end
-
-    it 'appends title element with description' do
-      account = Fabricate(:account, domain: nil, username: 'account')
-      target_account = Fabricate(:account, domain: 'remote', username: 'target_account')
-      follow = Fabricate(:follow, account: account, target_account: target_account)
-      follow.destroy!
-
-      unfollow_salmon = OStatus::AtomSerializer.new.unfollow_salmon(follow)
-
-      expect(unfollow_salmon.title.text).to eq 'account is no longer following target_account@remote'
-    end
-
-    it 'appends content element with description' do
-      account = Fabricate(:account, domain: nil, username: 'account')
-      target_account = Fabricate(:account, domain: 'remote', username: 'target_account')
-      follow = Fabricate(:follow, account: account, target_account: target_account)
-      follow.destroy!
-
-      unfollow_salmon = OStatus::AtomSerializer.new.unfollow_salmon(follow)
-
-      expect(unfollow_salmon.content.text).to eq 'account is no longer following target_account@remote'
-    end
-
-    it 'appends author element with account' do
-      account = Fabricate(:account, domain: nil, username: 'username')
-      follow = Fabricate(:follow, account: account)
-      follow.destroy!
-
-      unfollow_salmon = OStatus::AtomSerializer.new.unfollow_salmon(follow)
-
-      expect(unfollow_salmon.author.id.text).to eq 'https://cb6e6126.ngrok.io/users/username'
-    end
-
-    it 'appends activity:object-type element with activity type' do
-      follow = Fabricate(:follow)
-      follow.destroy!
-
-      unfollow_salmon = OStatus::AtomSerializer.new.unfollow_salmon(follow)
-
-      object_type = unfollow_salmon.nodes.find { |node| node.name == 'activity:object-type' }
-      expect(object_type.text).to eq OStatus::TagManager::TYPES[:activity]
-    end
-
-    it 'appends activity:verb element with follow' do
-      follow = Fabricate(:follow)
-      follow.destroy!
-
-      unfollow_salmon = OStatus::AtomSerializer.new.unfollow_salmon(follow)
-
-      verb = unfollow_salmon.nodes.find { |node| node.name == 'activity:verb' }
-      expect(verb.text).to eq OStatus::TagManager::VERBS[:unfollow]
-    end
-
-    it 'appends activity:object element with target account' do
-      target_account = Fabricate(:account, domain: 'domain.test', uri: 'https://domain.test/id')
-      follow = Fabricate(:follow, target_account: target_account)
-      follow.destroy!
-
-      unfollow_salmon = OStatus::AtomSerializer.new.unfollow_salmon(follow)
-
-      object = unfollow_salmon.nodes.find { |node| node.name == 'activity:object' }
-      expect(object.id.text).to eq 'https://domain.test/id'
-    end
-
-    it 'returns element whose rendered view triggers unfollow when processed' do
-      follow = Fabricate(:follow)
-      follow.destroy!
-      unfollow_salmon = OStatus::AtomSerializer.new.unfollow_salmon(follow)
-      xml = OStatus::AtomSerializer.render(unfollow_salmon)
-      follow.account.follow!(follow.target_account)
-      envelope = OStatus2::Salmon.new.pack(xml, follow.account.keypair)
-
-      ProcessInteractionService.new.call(envelope, follow.target_account)
-
-      expect(follow.account.following?(follow.target_account)).to be false
-    end
-  end
-
-  describe '#follow_request_salmon' do
-    include_examples 'namespaces' do
-      def serialize
-        follow_request = Fabricate(:follow_request)
-        OStatus::AtomSerializer.new.follow_request_salmon(follow_request)
-      end
-    end
-
-    context do
-      def serialize(follow_request)
-        OStatus::AtomSerializer.new.follow_request_salmon(follow_request)
-      end
-
-      it_behaves_like 'follow request salmon'
-
-      it 'appends id element with unique tag' do
-        follow_request = Fabricate(:follow_request, created_at: '2000-01-01T00:00:00Z')
-        follow_request_salmon = serialize(follow_request)
-        expect(follow_request_salmon.id.text).to eq "tag:cb6e6126.ngrok.io,2000-01-01:objectId=#{follow_request.id}:objectType=FollowRequest"
-      end
-
-      it 'appends title element with description' do
-        account = Fabricate(:account, domain: nil, username: 'account')
-        target_account = Fabricate(:account, domain: 'remote', username: 'target_account')
-        follow_request = Fabricate(:follow_request, account: account, target_account: target_account)
-        follow_request_salmon = serialize(follow_request)
-        expect(follow_request_salmon.title.text).to eq 'account requested to follow target_account@remote'
-      end
-
-      it 'returns element whose rendered view triggers follow request when processed' do
-        follow_request = Fabricate(:follow_request)
-        follow_request_salmon = serialize(follow_request)
-        xml = OStatus::AtomSerializer.render(follow_request_salmon)
-        envelope = OStatus2::Salmon.new.pack(xml, follow_request.account.keypair)
-        follow_request.destroy!
-
-        ProcessInteractionService.new.call(envelope, follow_request.target_account)
-
-        expect(follow_request.account.requested?(follow_request.target_account)).to eq true
-      end
-    end
-  end
-
-  describe '#authorize_follow_request_salmon' do
-    include_examples 'namespaces' do
-      def serialize
-        follow_request = Fabricate(:follow_request)
-        OStatus::AtomSerializer.new.authorize_follow_request_salmon(follow_request)
-      end
-    end
-
-    it_behaves_like 'follow request salmon' do
-      def serialize(follow_request)
-        authorize_follow_request_salmon = OStatus::AtomSerializer.new.authorize_follow_request_salmon(follow_request)
-        authorize_follow_request_salmon.nodes.find { |node| node.name == 'activity:object' }
-      end
-    end
-
-    it 'appends id element with unique tag' do
-      follow_request = Fabricate(:follow_request)
-
-      time_before = Time.zone.now
-      authorize_follow_request_salmon = OStatus::AtomSerializer.new.authorize_follow_request_salmon(follow_request)
-      time_after = Time.zone.now
-
-      expect(authorize_follow_request_salmon.id.text).to(
-        eq(OStatus::TagManager.instance.unique_tag(time_before.utc, follow_request.id, 'FollowRequest'))
-          .or(eq(OStatus::TagManager.instance.unique_tag(time_after.utc, follow_request.id, 'FollowRequest')))
-      )
-    end
-
-    it 'appends title element with description' do
-      account = Fabricate(:account, domain: 'remote', username: 'account')
-      target_account = Fabricate(:account, domain: nil, username: 'target_account')
-      follow_request = Fabricate(:follow_request, account: account, target_account: target_account)
-
-      authorize_follow_request_salmon = OStatus::AtomSerializer.new.authorize_follow_request_salmon(follow_request)
-
-      expect(authorize_follow_request_salmon.title.text).to eq 'target_account authorizes follow request by account@remote'
-    end
-
-    it 'appends activity:object-type element with activity type' do
-      follow_request = Fabricate(:follow_request)
-
-      authorize_follow_request_salmon = OStatus::AtomSerializer.new.authorize_follow_request_salmon(follow_request)
-
-      object_type = authorize_follow_request_salmon.nodes.find { |node| node.name == 'activity:object-type' }
-      expect(object_type.text).to eq OStatus::TagManager::TYPES[:activity]
-    end
-
-    it 'appends activity:verb element with authorize' do
-      follow_request = Fabricate(:follow_request)
-
-      authorize_follow_request_salmon = OStatus::AtomSerializer.new.authorize_follow_request_salmon(follow_request)
-
-      verb = authorize_follow_request_salmon.nodes.find { |node| node.name == 'activity:verb' }
-      expect(verb.text).to eq OStatus::TagManager::VERBS[:authorize]
-    end
-
-    it 'returns element whose rendered view creates follow from follow request when processed' do
-      follow_request = Fabricate(:follow_request)
-      authorize_follow_request_salmon = OStatus::AtomSerializer.new.authorize_follow_request_salmon(follow_request)
-      xml = OStatus::AtomSerializer.render(authorize_follow_request_salmon)
-      envelope = OStatus2::Salmon.new.pack(xml, follow_request.target_account.keypair)
-
-      ProcessInteractionService.new.call(envelope, follow_request.account)
-
-      expect(follow_request.account.following?(follow_request.target_account)).to eq true
-      expect { follow_request.reload }.to raise_error ActiveRecord::RecordNotFound
-    end
-  end
-
-  describe '#reject_follow_request_salmon' do
-    include_examples 'namespaces' do
-      def serialize
-        follow_request = Fabricate(:follow_request)
-        OStatus::AtomSerializer.new.reject_follow_request_salmon(follow_request)
-      end
-    end
-
-    it_behaves_like 'follow request salmon' do
-      def serialize(follow_request)
-        reject_follow_request_salmon = OStatus::AtomSerializer.new.reject_follow_request_salmon(follow_request)
-        reject_follow_request_salmon.nodes.find { |node| node.name == 'activity:object' }
-      end
-    end
-
-    it 'appends id element with unique tag' do
-      follow_request = Fabricate(:follow_request)
-
-      time_before = Time.zone.now
-      reject_follow_request_salmon = OStatus::AtomSerializer.new.reject_follow_request_salmon(follow_request)
-      time_after = Time.zone.now
-
-      expect(reject_follow_request_salmon.id.text).to(
-        eq(OStatus::TagManager.instance.unique_tag(time_before.utc, follow_request.id, 'FollowRequest'))
-          .or(OStatus::TagManager.instance.unique_tag(time_after.utc, follow_request.id, 'FollowRequest'))
-      )
-    end
-
-    it 'appends title element with description' do
-      account = Fabricate(:account, domain: 'remote', username: 'account')
-      target_account = Fabricate(:account, domain: nil, username: 'target_account')
-      follow_request = Fabricate(:follow_request, account: account, target_account: target_account)
-      reject_follow_request_salmon = OStatus::AtomSerializer.new.reject_follow_request_salmon(follow_request)
-      expect(reject_follow_request_salmon.title.text).to eq 'target_account rejects follow request by account@remote'
-    end
-
-    it 'appends activity:object-type element with activity type' do
-      follow_request = Fabricate(:follow_request)
-      reject_follow_request_salmon = OStatus::AtomSerializer.new.reject_follow_request_salmon(follow_request)
-      object_type = reject_follow_request_salmon.nodes.find { |node| node.name == 'activity:object-type' }
-      expect(object_type.text).to eq OStatus::TagManager::TYPES[:activity]
-    end
-
-    it 'appends activity:verb element with authorize' do
-      follow_request = Fabricate(:follow_request)
-      reject_follow_request_salmon = OStatus::AtomSerializer.new.reject_follow_request_salmon(follow_request)
-      verb = reject_follow_request_salmon.nodes.find { |node| node.name == 'activity:verb' }
-      expect(verb.text).to eq OStatus::TagManager::VERBS[:reject]
-    end
-
-    it 'returns element whose rendered view deletes follow request when processed' do
-      follow_request = Fabricate(:follow_request)
-      reject_follow_request_salmon = OStatus::AtomSerializer.new.reject_follow_request_salmon(follow_request)
-      xml = OStatus::AtomSerializer.render(reject_follow_request_salmon)
-      envelope = OStatus2::Salmon.new.pack(xml, follow_request.target_account.keypair)
-
-      ProcessInteractionService.new.call(envelope, follow_request.account)
-
-      expect(follow_request.account.following?(follow_request.target_account)).to eq false
-      expect { follow_request.reload }.to raise_error ActiveRecord::RecordNotFound
-    end
-  end
-
-  describe '#object' do
-    include_examples 'status attributes' do
-      def serialize(status)
-        OStatus::AtomSerializer.new.object(status)
-      end
-    end
-
-    it 'returns activity:object element' do
-      status = Fabricate(:status)
-      object = OStatus::AtomSerializer.new.object(status)
-      expect(object.name).to eq 'activity:object'
-    end
-
-    it 'appends id element with URL for status' do
-      status = Fabricate(:status, created_at: '2000-01-01T00:00:00Z')
-      object = OStatus::AtomSerializer.new.object(status)
-      expect(object.id.text).to eq "https://cb6e6126.ngrok.io/users/#{status.account.to_param}/statuses/#{status.id}"
-    end
-
-    it 'appends published element with created date' do
-      status = Fabricate(:status, created_at: '2000-01-01T00:00:00Z')
-      object = OStatus::AtomSerializer.new.object(status)
-      expect(object.published.text).to eq '2000-01-01T00:00:00Z'
-    end
-
-    it 'appends updated element with updated date' do
-      status = Fabricate(:status)
-      status.updated_at = '2000-01-01T00:00:00Z'
-      object = OStatus::AtomSerializer.new.object(status)
-      expect(object.updated.text).to eq '2000-01-01T00:00:00Z'
-    end
-
-    it 'appends title element with title' do
-      account = Fabricate(:account, username: 'username')
-      status = Fabricate(:status, account: account)
-
-      object = OStatus::AtomSerializer.new.object(status)
-
-      expect(object.title.text).to eq 'New status by username'
-    end
-
-    it 'appends author element with account' do
-      account = Fabricate(:account, username: 'username')
-      status = Fabricate(:status, account: account)
-
-      entry = OStatus::AtomSerializer.new.object(status)
-
-      expect(entry.author.id.text).to eq 'https://cb6e6126.ngrok.io/users/username'
-    end
-
-    it 'appends activity:object-type element with object type' do
-      status = Fabricate(:status)
-
-      entry = OStatus::AtomSerializer.new.object(status)
-
-      object_type = entry.nodes.find { |node| node.name == 'activity:object-type' }
-      expect(object_type.text).to eq OStatus::TagManager::TYPES[:note]
-    end
-
-    it 'appends activity:verb element with verb' do
-      status = Fabricate(:status)
-
-      entry = OStatus::AtomSerializer.new.object(status)
-
-      object_type = entry.nodes.find { |node| node.name == 'activity:verb' }
-      expect(object_type.text).to eq OStatus::TagManager::VERBS[:post]
-    end
-
-    it 'appends link element for an alternative' do
-      account = Fabricate(:account, username: 'username')
-      status = Fabricate(:status, account: account)
-
-      entry = OStatus::AtomSerializer.new.object(status)
-
-      link = entry.nodes.find { |node| node.name == 'link' && node[:rel] == 'alternate' && node[:type] == 'text/html' }
-      expect(link[:type]).to eq 'text/html'
-      expect(link[:href]).to eq "https://cb6e6126.ngrok.io/@username/#{status.id}"
-    end
-
-    it 'appends thr:in-reply-to element if it is a reply and thread is not nil' do
-      account = Fabricate(:account, username: 'username')
-      thread = Fabricate(:status, account: account, created_at: '2000-01-01T00:00:00Z')
-      reply = Fabricate(:status, thread: thread)
-
-      entry = OStatus::AtomSerializer.new.object(reply)
-
-      in_reply_to = entry.nodes.find { |node| node.name == 'thr:in-reply-to' }
-      expect(in_reply_to.ref).to eq "https://cb6e6126.ngrok.io/users/#{thread.account.to_param}/statuses/#{thread.id}"
-      expect(in_reply_to.href).to eq "https://cb6e6126.ngrok.io/@username/#{thread.id}"
-    end
-
-    it 'does not append thr:in-reply-to element if thread is nil' do
-      status = Fabricate(:status, thread: nil)
-      entry = OStatus::AtomSerializer.new.object(status)
-      entry.nodes.each { |node| expect(node.name).not_to eq 'thr:in-reply-to' }
-    end
-
-    it 'does not append ostatus:conversation element if conversation_id is nil' do
-      status = Fabricate.build(:status, conversation_id: nil)
-      status.save!(validate: false)
-
-      entry = OStatus::AtomSerializer.new.object(status)
-
-      entry.nodes.each { |node| expect(node.name).not_to eq 'ostatus:conversation' }
-    end
-
-    it 'appends ostatus:conversation element if conversation_id is not nil' do
-      status = Fabricate(:status)
-      status.conversation.update!(created_at: '2000-01-01T00:00:00Z')
-
-      entry = OStatus::AtomSerializer.new.object(status)
-
-      conversation = entry.nodes.find { |node| node.name == 'ostatus:conversation' }
-      expect(conversation[:ref]).to eq "tag:cb6e6126.ngrok.io,2000-01-01:objectId=#{status.conversation.id}:objectType=Conversation"
-    end
-  end
-end
diff --git a/spec/lib/sanitize_config_spec.rb b/spec/lib/sanitize_config_spec.rb
index c5143bcef..faefac803 100644
--- a/spec/lib/sanitize_config_spec.rb
+++ b/spec/lib/sanitize_config_spec.rb
@@ -14,5 +14,9 @@ describe Sanitize::Config do
     it 'keeps ul' do
       expect(Sanitize.fragment('<p>Check out:</p><ul><li>Foo</li><li>Bar</li></ul>', subject)).to eq '<p>Check out:</p><ul><li>Foo</li><li>Bar</li></ul>'
     end
+
+    it 'keep links in lists' do
+      expect(Sanitize.fragment('<p>Check out:</p><ul><li><a href="https://joinmastodon.org" rel="nofollow noopener" target="_blank">joinmastodon.org</a></li><li>Bar</li></ul>', subject)).to eq '<p>Check out:</p><p><a href="https://joinmastodon.org" rel="nofollow noopener" target="_blank">joinmastodon.org</a><br>Bar</p>'
+    end
   end
 end
diff --git a/spec/lib/spam_check_spec.rb b/spec/lib/spam_check_spec.rb
new file mode 100644
index 000000000..c722dc642
--- /dev/null
+++ b/spec/lib/spam_check_spec.rb
@@ -0,0 +1,160 @@
+require 'rails_helper'
+
+RSpec.describe SpamCheck do
+  let!(:sender) { Fabricate(:account) }
+  let!(:alice) { Fabricate(:account, username: 'alice') }
+  let!(:bob) { Fabricate(:account, username: 'bob') }
+
+  def status_with_html(text, options = {})
+    status = PostStatusService.new.call(sender, { text: text }.merge(options))
+    status.update_columns(text: Formatter.instance.format(status), local: false)
+    status
+  end
+
+  describe '#hashable_text' do
+    it 'removes mentions from HTML for remote statuses' do
+      status = status_with_html('@alice Hello')
+      expect(described_class.new(status).hashable_text).to eq 'hello'
+    end
+
+    it 'removes mentions from text for local statuses' do
+      status = PostStatusService.new.call(alice, text: "Hey @#{sender.username}, how are you?")
+      expect(described_class.new(status).hashable_text).to eq 'hey , how are you?'
+    end
+  end
+
+  describe '#insufficient_data?' do
+    it 'returns true when there is no text' do
+      status = status_with_html('@alice')
+      expect(described_class.new(status).insufficient_data?).to be true
+    end
+
+    it 'returns false when there is text' do
+      status = status_with_html('@alice h')
+      expect(described_class.new(status).insufficient_data?).to be false
+    end
+  end
+
+  describe '#digest' do
+    it 'returns a string' do
+      status = status_with_html('@alice Hello world')
+      expect(described_class.new(status).digest).to be_a String
+    end
+  end
+
+  describe '#spam?' do
+    it 'returns false for a unique status' do
+      status = status_with_html('@alice Hello')
+      expect(described_class.new(status).spam?).to be false
+    end
+
+    it 'returns false for different statuses to the same recipient' do
+      status1 = status_with_html('@alice Hello')
+      described_class.new(status1).remember!
+      status2 = status_with_html('@alice Are you available to talk?')
+      expect(described_class.new(status2).spam?).to be false
+    end
+
+    it 'returns false for statuses with different content warnings' do
+      status1 = status_with_html('@alice Are you available to talk?')
+      described_class.new(status1).remember!
+      status2 = status_with_html('@alice Are you available to talk?', spoiler_text: 'This is a completely different matter than what I was talking about previously, I swear!')
+      expect(described_class.new(status2).spam?).to be false
+    end
+
+    it 'returns false for different statuses to different recipients' do
+      status1 = status_with_html('@alice How is it going?')
+      described_class.new(status1).remember!
+      status2 = status_with_html('@bob Are you okay?')
+      expect(described_class.new(status2).spam?).to be false
+    end
+
+    it 'returns false for very short different statuses to different recipients' do
+      status1 = status_with_html('@alice 🙄')
+      described_class.new(status1).remember!
+      status2 = status_with_html('@bob Huh?')
+      expect(described_class.new(status2).spam?).to be false
+    end
+
+    it 'returns false for statuses with no text' do
+      status1 = status_with_html('@alice')
+      described_class.new(status1).remember!
+      status2 = status_with_html('@bob')
+      expect(described_class.new(status2).spam?).to be false
+    end
+
+    it 'returns true for duplicate statuses to the same recipient' do
+      status1 = status_with_html('@alice Hello')
+      described_class.new(status1).remember!
+      status2 = status_with_html('@alice Hello')
+      expect(described_class.new(status2).spam?).to be true
+    end
+
+    it 'returns true for duplicate statuses to different recipients' do
+      status1 = status_with_html('@alice Hello')
+      described_class.new(status1).remember!
+      status2 = status_with_html('@bob Hello')
+      expect(described_class.new(status2).spam?).to be true
+    end
+
+    it 'returns true for nearly identical statuses with random numbers' do
+      source_text = 'Sodium, atomic number 11, was first isolated by Humphry Davy in 1807. A chemical component of salt, he named it Na in honor of the saltiest region on earth, North America.'
+      status1 = status_with_html('@alice ' + source_text + ' 1234')
+      described_class.new(status1).remember!
+      status2 = status_with_html('@bob ' + source_text + ' 9568')
+      expect(described_class.new(status2).spam?).to be true
+    end
+  end
+
+  describe '#skip?' do
+    it 'returns true when the sender is already silenced' do
+      status = status_with_html('@alice Hello')
+      sender.silence!
+      expect(described_class.new(status).skip?).to be true
+    end
+
+    it 'returns true when the mentioned person follows the sender' do
+      status = status_with_html('@alice Hello')
+      alice.follow!(sender)
+      expect(described_class.new(status).skip?).to be true
+    end
+
+    it 'returns false when even one mentioned person doesn\'t follow the sender' do
+      status = status_with_html('@alice @bob Hello')
+      alice.follow!(sender)
+      expect(described_class.new(status).skip?).to be false
+    end
+
+    it 'returns true when the sender is replying to a status that mentions the sender' do
+      parent = PostStatusService.new.call(alice, text: "Hey @#{sender.username}, how are you?")
+      status = status_with_html('@alice @bob Hello', thread: parent)
+      expect(described_class.new(status).skip?).to be true
+    end
+  end
+
+  describe '#remember!' do
+    pending
+  end
+
+  describe '#flag!' do
+    let!(:status1) { status_with_html('@alice General Kenobi you are a bold one') }
+    let!(:status2) { status_with_html('@alice @bob General Kenobi, you are a bold one') }
+
+    before do
+      described_class.new(status1).remember!
+      described_class.new(status2).flag!
+    end
+
+    it 'silences the account' do
+      expect(sender.silenced?).to be true
+    end
+
+    it 'creates a report about the account' do
+      expect(sender.targeted_reports.unresolved.count).to eq 1
+    end
+
+    it 'attaches both matching statuses to the report' do
+      expect(sender.targeted_reports.first.status_ids).to include(status1.id, status2.id)
+    end
+  end
+end
diff --git a/spec/lib/status_finder_spec.rb b/spec/lib/status_finder_spec.rb
index 6b4ee434f..61483f4bf 100644
--- a/spec/lib/status_finder_spec.rb
+++ b/spec/lib/status_finder_spec.rb
@@ -25,15 +25,6 @@ describe StatusFinder do
       end
     end
 
-    context 'with a stream entry url' do
-      let(:stream_entry) { Fabricate(:stream_entry) }
-      let(:url) { account_stream_entry_url(stream_entry.account, stream_entry) }
-
-      it 'finds the stream entry' do
-        expect(subject.status).to eq(stream_entry.status)
-      end
-    end
-
     context 'with a remote url even if id exists on local' do
       let(:status) { Fabricate(:status) }
       let(:url) { "https://example.com/users/test/statuses/#{status.id}" }
diff --git a/spec/lib/tag_manager_spec.rb b/spec/lib/tag_manager_spec.rb
index 3a804ac0f..e9a7aa934 100644
--- a/spec/lib/tag_manager_spec.rb
+++ b/spec/lib/tag_manager_spec.rb
@@ -119,46 +119,4 @@ RSpec.describe TagManager do
       expect(TagManager.instance.same_acct?('username', 'incorrect@Cb6E6126.nGrOk.Io')).to eq false
     end
   end
-
-  describe '#url_for' do
-    let(:alice) { Fabricate(:account, username: 'alice') }
-
-    subject { TagManager.instance.url_for(target) }
-
-    context 'activity object' do
-      let(:target) { Fabricate(:status, account: alice, reblog: Fabricate(:status)).stream_entry }
-
-      it 'returns the unique tag for status' do
-        expect(target.object_type).to eq :activity
-        is_expected.to eq "https://cb6e6126.ngrok.io/@alice/#{target.id}"
-      end
-    end
-
-    context 'comment object' do
-      let(:target) { Fabricate(:status, account: alice, reply: true) }
-
-      it 'returns the unique tag for status' do
-        expect(target.object_type).to eq :comment
-        is_expected.to eq "https://cb6e6126.ngrok.io/@alice/#{target.id}"
-      end
-    end
-
-    context 'note object' do
-      let(:target) { Fabricate(:status, account: alice, reply: false, thread: nil) }
-
-      it 'returns the unique tag for status' do
-        expect(target.object_type).to eq :note
-        is_expected.to eq "https://cb6e6126.ngrok.io/@alice/#{target.id}"
-      end
-    end
-
-    context 'person object' do
-      let(:target) { alice }
-
-      it 'returns the URL for account' do
-        expect(target.object_type).to eq :person
-        is_expected.to eq 'https://cb6e6126.ngrok.io/@alice'
-      end
-    end
-  end
 end
diff --git a/spec/models/account_spec.rb b/spec/models/account_spec.rb
index ce9ea250d..6495a6193 100644
--- a/spec/models/account_spec.rb
+++ b/spec/models/account_spec.rb
@@ -450,7 +450,7 @@ RSpec.describe Account, type: :model do
   describe '.domains' do
     it 'returns domains' do
       Fabricate(:account, domain: 'domain')
-      expect(Account.domains).to match_array(['domain'])
+      expect(Account.remote.domains).to match_array(['domain'])
     end
   end
 
@@ -665,7 +665,7 @@ RSpec.describe Account, type: :model do
           { username: 'b', domain: 'b' },
         ].map(&method(:Fabricate).curry(2).call(:account))
 
-        expect(Account.alphabetic).to eq matches
+        expect(Account.where('id > 0').alphabetic).to eq matches
       end
     end
 
@@ -732,7 +732,7 @@ RSpec.describe Account, type: :model do
         2.times { Fabricate(:account, domain: 'example.com') }
         Fabricate(:account, domain: 'example2.com')
 
-        results = Account.by_domain_accounts
+        results = Account.where('id > 0').by_domain_accounts
         expect(results.length).to eq 2
         expect(results.first.domain).to eq 'example.com'
         expect(results.first.accounts_count).to eq 2
@@ -745,7 +745,7 @@ RSpec.describe Account, type: :model do
       it 'returns an array of accounts who do not have a domain' do
         account_1 = Fabricate(:account, domain: nil)
         account_2 = Fabricate(:account, domain: 'example.com')
-        expect(Account.local).to match_array([account_1])
+        expect(Account.where('id > 0').local).to match_array([account_1])
       end
     end
 
@@ -756,14 +756,14 @@ RSpec.describe Account, type: :model do
           matches[index] = Fabricate(:account, domain: matches[index])
         end
 
-        expect(Account.partitioned).to match_array(matches)
+        expect(Account.where('id > 0').partitioned).to match_array(matches)
       end
     end
 
     describe 'recent' do
       it 'returns a relation of accounts sorted by recent creation' do
         matches = 2.times.map { Fabricate(:account) }
-        expect(Account.recent).to match_array(matches)
+        expect(Account.where('id > 0').recent).to match_array(matches)
       end
     end
 
diff --git a/spec/models/concerns/streamable_spec.rb b/spec/models/concerns/streamable_spec.rb
deleted file mode 100644
index b5f2d5192..000000000
--- a/spec/models/concerns/streamable_spec.rb
+++ /dev/null
@@ -1,63 +0,0 @@
-# frozen_string_literal: true
-
-require 'rails_helper'
-
-RSpec.describe Streamable do
-  class Parent
-    def title; end
-
-    def target; end
-
-    def thread; end
-
-    def self.has_one(*); end
-
-    def self.after_create; end
-  end
-
-  class Child < Parent
-    include Streamable
-  end
-
-  child = Child.new
-
-  describe '#title' do
-    it 'calls Parent#title' do
-      expect_any_instance_of(Parent).to receive(:title)
-      child.title
-    end
-  end
-
-  describe '#content' do
-    it 'calls #title' do
-      expect_any_instance_of(Parent).to receive(:title)
-      child.content
-    end
-  end
-
-  describe '#target' do
-    it 'calls Parent#target' do
-      expect_any_instance_of(Parent).to receive(:target)
-      child.target
-    end
-  end
-
-  describe '#object_type' do
-    it 'returns :activity' do
-      expect(child.object_type).to eq :activity
-    end
-  end
-
-  describe '#thread' do
-    it 'calls Parent#thread' do
-      expect_any_instance_of(Parent).to receive(:thread)
-      child.thread
-    end
-  end
-
-  describe '#hidden?' do
-    it 'returns false' do
-      expect(child.hidden?).to be false
-    end
-  end
-end
diff --git a/spec/models/remote_profile_spec.rb b/spec/models/remote_profile_spec.rb
deleted file mode 100644
index da5048f0a..000000000
--- a/spec/models/remote_profile_spec.rb
+++ /dev/null
@@ -1,143 +0,0 @@
-# frozen_string_literal: true
-
-require 'rails_helper'
-
-RSpec.describe RemoteProfile do
-  let(:remote_profile) { RemoteProfile.new(body) }
-  let(:body) do
-    <<-XML
-      <feed xmlns="http://www.w3.org/2005/Atom">
-      <author>John</author>
-    XML
-  end
-
-  describe '.initialize' do
-    it 'calls Nokogiri::XML.parse' do
-      expect(Nokogiri::XML).to receive(:parse).with(body, nil, 'utf-8')
-      RemoteProfile.new(body)
-    end
-
-    it 'sets document' do
-      remote_profile = RemoteProfile.new(body)
-      expect(remote_profile).not_to be nil
-    end
-  end
-
-  describe '#root' do
-    let(:document) { remote_profile.document }
-
-    it 'callse document.at_xpath' do
-      expect(document).to receive(:at_xpath).with(
-        '/atom:feed|/atom:entry',
-        atom: OStatus::TagManager::XMLNS
-      )
-
-      remote_profile.root
-    end
-  end
-
-  describe '#author' do
-    let(:root) { remote_profile.root }
-
-    it 'calls root.at_xpath' do
-      expect(root).to receive(:at_xpath).with(
-        './atom:author|./dfrn:owner',
-        atom: OStatus::TagManager::XMLNS,
-        dfrn: OStatus::TagManager::DFRN_XMLNS
-      )
-
-      remote_profile.author
-    end
-  end
-
-  describe '#hub_link' do
-    let(:root) { remote_profile.root }
-
-    it 'calls #link_href_from_xml' do
-      expect(remote_profile).to receive(:link_href_from_xml).with(root, 'hub')
-      remote_profile.hub_link
-    end
-  end
-
-  describe '#display_name' do
-    let(:author) { remote_profile.author }
-
-    it 'calls author.at_xpath.content' do
-      expect(author).to receive_message_chain(:at_xpath, :content).with(
-        './poco:displayName',
-        poco: OStatus::TagManager::POCO_XMLNS
-      ).with(no_args)
-
-      remote_profile.display_name
-    end
-  end
-
-  describe '#note' do
-    let(:author) { remote_profile.author }
-
-    it 'calls author.at_xpath.content' do
-      expect(author).to receive_message_chain(:at_xpath, :content).with(
-        './atom:summary|./poco:note',
-        atom: OStatus::TagManager::XMLNS,
-        poco: OStatus::TagManager::POCO_XMLNS
-      ).with(no_args)
-
-      remote_profile.note
-    end
-  end
-
-  describe '#scope' do
-    let(:author) { remote_profile.author }
-
-    it 'calls author.at_xpath.content' do
-      expect(author).to receive_message_chain(:at_xpath, :content).with(
-        './mastodon:scope',
-        mastodon: OStatus::TagManager::MTDN_XMLNS
-      ).with(no_args)
-
-      remote_profile.scope
-    end
-  end
-
-  describe '#avatar' do
-    let(:author) { remote_profile.author }
-
-    it 'calls #link_href_from_xml' do
-      expect(remote_profile).to receive(:link_href_from_xml).with(author, 'avatar')
-      remote_profile.avatar
-    end
-  end
-
-  describe '#header' do
-    let(:author) { remote_profile.author }
-
-    it 'calls #link_href_from_xml' do
-      expect(remote_profile).to receive(:link_href_from_xml).with(author, 'header')
-      remote_profile.header
-    end
-  end
-
-  describe '#locked?' do
-    before do
-      allow(remote_profile).to receive(:scope).and_return(scope)
-    end
-
-    subject { remote_profile.locked? }
-
-    context 'scope is private' do
-      let(:scope) { 'private' }
-
-      it 'returns true' do
-        is_expected.to be true
-      end
-    end
-
-    context 'scope is not private' do
-      let(:scope) { 'public' }
-
-      it 'returns false' do
-        is_expected.to be false
-      end
-    end
-  end
-end
diff --git a/spec/models/stream_entry_spec.rb b/spec/models/stream_entry_spec.rb
deleted file mode 100644
index 8f8bfbd58..000000000
--- a/spec/models/stream_entry_spec.rb
+++ /dev/null
@@ -1,192 +0,0 @@
-require 'rails_helper'
-
-RSpec.describe StreamEntry, type: :model do
-  let(:alice)     { Fabricate(:account, username: 'alice') }
-  let(:bob)       { Fabricate(:account, username: 'bob') }
-  let(:status)    { Fabricate(:status, account: alice) }
-  let(:reblog)    { Fabricate(:status, account: bob, reblog: status) }
-  let(:reply)     { Fabricate(:status, account: bob, thread: status) }
-  let(:stream_entry) { Fabricate(:stream_entry, activity: activity) }
-  let(:activity)     { reblog }
-
-  describe '#object_type' do
-    before do
-      allow(stream_entry).to receive(:orphaned?).and_return(orphaned)
-      allow(stream_entry).to receive(:targeted?).and_return(targeted)
-    end
-
-    subject { stream_entry.object_type }
-
-    context 'orphaned? is true' do
-      let(:orphaned) { true }
-      let(:targeted) { false }
-
-      it 'returns :activity' do
-        is_expected.to be :activity
-      end
-    end
-
-    context 'targeted? is true' do
-      let(:orphaned) { false }
-      let(:targeted) { true }
-
-      it 'returns :activity' do
-        is_expected.to be :activity
-      end
-    end
-
-    context 'orphaned? and targeted? are false' do
-      let(:orphaned) { false }
-      let(:targeted) { false }
-
-      context 'activity is reblog' do
-        let(:activity) { reblog }
-
-        it 'returns :note' do
-          is_expected.to be :note
-        end
-      end
-
-      context 'activity is reply' do
-        let(:activity) { reply }
-
-        it 'returns :comment' do
-          is_expected.to be :comment
-        end
-      end
-    end
-  end
-
-  describe '#verb' do
-    before do
-      allow(stream_entry).to receive(:orphaned?).and_return(orphaned)
-    end
-
-    subject { stream_entry.verb }
-
-    context 'orphaned? is true' do
-      let(:orphaned) { true }
-
-      it 'returns :delete' do
-        is_expected.to be :delete
-      end
-    end
-
-    context 'orphaned? is false' do
-      let(:orphaned) { false }
-
-      context 'activity is reblog' do
-        let(:activity) { reblog }
-
-        it 'returns :share' do
-          is_expected.to be :share
-        end
-      end
-
-      context 'activity is reply' do
-        let(:activity) { reply }
-
-        it 'returns :post' do
-          is_expected.to be :post
-        end
-      end
-    end
-  end
-
-  describe '#mentions' do
-    before do
-      allow(stream_entry).to receive(:orphaned?).and_return(orphaned)
-    end
-
-    subject { stream_entry.mentions }
-
-    context 'orphaned? is true' do
-      let(:orphaned) { true }
-
-      it 'returns []' do
-        is_expected.to eq []
-      end
-    end
-
-    context 'orphaned? is false' do
-      before do
-        reblog.mentions << Fabricate(:mention, account: alice)
-        reblog.mentions << Fabricate(:mention, account: bob)
-      end
-
-      let(:orphaned) { false }
-
-      it 'returns [Account] includes alice and bob' do
-        is_expected.to eq [alice, bob]
-      end
-    end
-  end
-
-  describe '#targeted?' do
-    it 'returns true for a reblog' do
-      expect(reblog.stream_entry.targeted?).to be true
-    end
-
-    it 'returns false otherwise' do
-      expect(status.stream_entry.targeted?).to be false
-    end
-  end
-
-  describe '#threaded?' do
-    it 'returns true for a reply' do
-      expect(reply.stream_entry.threaded?).to be true
-    end
-
-    it 'returns false otherwise' do
-      expect(status.stream_entry.threaded?).to be false
-    end
-  end
-
-  describe 'delegated methods' do
-    context 'with a nil status' do
-      subject { described_class.new(status: nil) }
-
-      it 'returns nil for target' do
-        expect(subject.target).to be_nil
-      end
-
-      it 'returns nil for title' do
-        expect(subject.title).to be_nil
-      end
-
-      it 'returns nil for content' do
-        expect(subject.content).to be_nil
-      end
-
-      it 'returns nil for thread' do
-        expect(subject.thread).to be_nil
-      end
-    end
-
-    context 'with a real status' do
-      let(:original) { Fabricate(:status, text: 'Test status') }
-      let(:status) { Fabricate(:status, reblog: original, thread: original) }
-      subject { described_class.new(status: status) }
-
-      it 'delegates target' do
-        expect(status.target).not_to be_nil
-        expect(subject.target).to eq(status.target)
-      end
-
-      it 'delegates title' do
-        expect(status.title).not_to be_nil
-        expect(subject.title).to eq(status.title)
-      end
-
-      it 'delegates content' do
-        expect(status.content).not_to be_nil
-        expect(subject.content).to eq(status.content)
-      end
-
-      it 'delegates thread' do
-        expect(status.thread).not_to be_nil
-        expect(subject.thread).to eq(status.thread)
-      end
-    end
-  end
-end
diff --git a/spec/models/tag_spec.rb b/spec/models/tag_spec.rb
index 1ca50cc29..161862392 100644
--- a/spec/models/tag_spec.rb
+++ b/spec/models/tag_spec.rb
@@ -31,7 +31,43 @@ RSpec.describe Tag, type: :model do
     end
 
     it 'matches #aesthetic' do
-      expect(subject.match('this is #aesthetic')).to_not be_nil
+      expect(subject.match('this is #aesthetic').to_s).to eq ' #aesthetic'
+    end
+
+    it 'matches digits at the start' do
+      expect(subject.match('hello #3d').to_s).to eq ' #3d'
+    end
+
+    it 'matches digits in the middle' do
+      expect(subject.match('hello #l33ts35k').to_s).to eq ' #l33ts35k'
+    end
+
+    it 'matches digits at the end' do
+      expect(subject.match('hello #world2016').to_s).to eq ' #world2016'
+    end
+
+    it 'matches underscores at the beginning' do
+      expect(subject.match('hello #_test').to_s).to eq ' #_test'
+    end
+
+    it 'matches underscores at the end' do
+      expect(subject.match('hello #test_').to_s).to eq ' #test_'
+    end
+
+    it 'matches underscores in the middle' do
+      expect(subject.match('hello #one_two_three').to_s).to eq ' #one_two_three'
+    end
+
+    it 'matches middle dots' do
+      expect(subject.match('hello #one·two·three').to_s).to eq ' #one·two·three'
+    end
+
+    it 'does not match middle dots at the start' do
+      expect(subject.match('hello #·one·two·three')).to be_nil
+    end
+
+    it 'does not match middle dots at the end' do
+      expect(subject.match('hello #one·two·three·').to_s).to eq ' #one·two·three'
     end
   end
 
diff --git a/spec/requests/link_headers_spec.rb b/spec/requests/link_headers_spec.rb
index 3dc408d92..712ee262b 100644
--- a/spec/requests/link_headers_spec.rb
+++ b/spec/requests/link_headers_spec.rb
@@ -11,16 +11,16 @@ describe 'Link headers' do
     end
 
     it 'contains webfinger url in link header' do
-      link_header = link_header_with_type('application/xrd+xml')
+      link_header = link_header_with_type('application/jrd+json')
 
       expect(link_header.href).to match 'http://www.example.com/.well-known/webfinger?resource=acct%3Atest%40cb6e6126.ngrok.io'
       expect(link_header.attr_pairs.first).to eq %w(rel lrdd)
     end
 
-    it 'contains atom url in link header' do
-      link_header = link_header_with_type('application/atom+xml')
+    it 'contains activitypub url in link header' do
+      link_header = link_header_with_type('application/activity+json')
 
-      expect(link_header.href).to eq 'http://www.example.com/users/test.atom'
+      expect(link_header.href).to eq 'https://cb6e6126.ngrok.io/users/test'
       expect(link_header.attr_pairs.first).to eq %w(rel alternate)
     end
 
diff --git a/spec/services/authorize_follow_service_spec.rb b/spec/services/authorize_follow_service_spec.rb
index 562ef0041..ce56d57a6 100644
--- a/spec/services/authorize_follow_service_spec.rb
+++ b/spec/services/authorize_follow_service_spec.rb
@@ -38,13 +38,6 @@ RSpec.describe AuthorizeFollowService, type: :service do
     it 'creates follow relation' do
       expect(bob.following?(sender)).to be true
     end
-
-    it 'sends a follow request authorization salmon slap' do
-      expect(a_request(:post, "http://salmon.example.com/").with { |req|
-        xml = OStatus2::Salmon.new.unpack(req.body)
-        xml.match(OStatus::TagManager::VERBS[:authorize])
-      }).to have_been_made.once
-    end
   end
 
   describe 'remote ActivityPub' do
diff --git a/spec/services/batched_remove_status_service_spec.rb b/spec/services/batched_remove_status_service_spec.rb
index e53623449..d52e7f484 100644
--- a/spec/services/batched_remove_status_service_spec.rb
+++ b/spec/services/batched_remove_status_service_spec.rb
@@ -49,19 +49,6 @@ RSpec.describe BatchedRemoveStatusService, type: :service do
     expect(Redis.current).to have_received(:publish).with('timeline:public', any_args).at_least(:once)
   end
 
-  it 'sends PuSH update to PuSH subscribers' do
-    expect(a_request(:post, 'http://example.com/push').with { |req|
-      matches = req.body.match(OStatus::TagManager::VERBS[:delete])
-    }).to have_been_made.at_least_once
-  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)
-      xml.match(OStatus::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
diff --git a/spec/services/block_service_spec.rb b/spec/services/block_service_spec.rb
index 6584bb90e..de20dd026 100644
--- a/spec/services/block_service_spec.rb
+++ b/spec/services/block_service_spec.rb
@@ -28,13 +28,6 @@ RSpec.describe BlockService, type: :service do
     it 'creates a blocking relation' do
       expect(sender.blocking?(bob)).to be true
     end
-
-    it 'sends a block salmon slap' do
-      expect(a_request(:post, "http://salmon.example.com/").with { |req|
-        xml = OStatus2::Salmon.new.unpack(req.body)
-        xml.match(OStatus::TagManager::VERBS[:block])
-      }).to have_been_made.once
-    end
   end
 
   describe 'remote ActivityPub' do
diff --git a/spec/services/favourite_service_spec.rb b/spec/services/favourite_service_spec.rb
index 0a20ccf6e..4c29ea77b 100644
--- a/spec/services/favourite_service_spec.rb
+++ b/spec/services/favourite_service_spec.rb
@@ -30,13 +30,6 @@ RSpec.describe FavouriteService, type: :service do
     it 'creates a favourite' do
       expect(status.favourites.first).to_not be_nil
     end
-
-    it 'sends a salmon slap' do
-      expect(a_request(:post, "http://salmon.example.com/").with { |req|
-        xml = OStatus2::Salmon.new.unpack(req.body)
-        xml.match(OStatus::TagManager::VERBS[:favorite])
-      }).to have_been_made.once
-    end
   end
 
   describe 'remote ActivityPub' do
diff --git a/spec/services/fetch_remote_account_service_spec.rb b/spec/services/fetch_remote_account_service_spec.rb
index 3cd86708b..ee7325be2 100644
--- a/spec/services/fetch_remote_account_service_spec.rb
+++ b/spec/services/fetch_remote_account_service_spec.rb
@@ -4,6 +4,7 @@ RSpec.describe FetchRemoteAccountService, type: :service do
   let(:url) { 'https://example.com/alice' }
   let(:prefetched_body) { nil }
   let(:protocol) { :ostatus }
+
   subject { FetchRemoteAccountService.new.call(url, prefetched_body, protocol) }
 
   let(:actor) do
@@ -36,36 +37,6 @@ RSpec.describe FetchRemoteAccountService, type: :service do
     include_examples 'return Account'
   end
 
-  context 'protocol is :ostatus' do
-    let(:prefetched_body) { xml }
-    let(:protocol) { :ostatus }
-
-    before do
-      stub_request(:get, "https://kickass.zone/.well-known/webfinger?resource=acct:localhost@kickass.zone").to_return(request_fixture('webfinger-hacker3.txt'))
-      stub_request(:get, "https://kickass.zone/api/statuses/user_timeline/7477.atom").to_return(request_fixture('feed.txt'))
-    end
-
-    include_examples 'return Account'
-
-    it 'does not update account information if XML comes from an unverified domain' do
-      feed_xml = <<-XML.squish
-        <?xml version="1.0" encoding="UTF-8"?>
-        <feed xml:lang="en-US" xmlns="http://www.w3.org/2005/Atom" xmlns:thr="http://purl.org/syndication/thread/1.0" xmlns:georss="http://www.georss.org/georss" xmlns:activity="http://activitystrea.ms/spec/1.0/" xmlns:media="http://purl.org/syndication/atommedia" xmlns:poco="http://portablecontacts.net/spec/1.0" xmlns:ostatus="http://ostatus.org/schema/1.0" xmlns:statusnet="http://status.net/schema/api/1/">
-          <author>
-            <activity:object-type>http://activitystrea.ms/schema/1.0/person</activity:object-type>
-            <uri>http://kickass.zone/users/localhost</uri>
-            <name>localhost</name>
-            <poco:preferredUsername>localhost</poco:preferredUsername>
-            <poco:displayName>Villain!!!</poco:displayName>
-          </author>
-        </feed>
-      XML
-
-      returned_account = described_class.new.call('https://real-fake-domains.com/alice', feed_xml, :ostatus)
-      expect(returned_account.display_name).to_not eq 'Villain!!!'
-    end
-  end
-
   context 'when prefetched_body is nil' do
     context 'protocol is :activitypub' do
       before do
@@ -75,15 +46,5 @@ RSpec.describe FetchRemoteAccountService, type: :service do
 
       include_examples 'return Account'
     end
-
-    context 'protocol is :ostatus' do
-      before do
-        stub_request(:get, url).to_return(status: 200, body: xml, headers: { 'Content-Type' => 'application/atom+xml' })
-        stub_request(:get, "https://kickass.zone/.well-known/webfinger?resource=acct:localhost@kickass.zone").to_return(request_fixture('webfinger-hacker3.txt'))
-        stub_request(:get, "https://kickass.zone/api/statuses/user_timeline/7477.atom").to_return(request_fixture('feed.txt'))
-      end
-
-      include_examples 'return Account'
-    end
   end
 end
diff --git a/spec/services/fetch_atom_service_spec.rb b/spec/services/fetch_resource_service_spec.rb
index 495540004..f836147d3 100644
--- a/spec/services/fetch_atom_service_spec.rb
+++ b/spec/services/fetch_resource_service_spec.rb
@@ -1,73 +1,80 @@
 require 'rails_helper'
 
-RSpec.describe FetchAtomService, type: :service do
+RSpec.describe FetchResourceService, type: :service do
   describe '#call' do
     let(:url) { 'http://example.com' }
-    subject { FetchAtomService.new.call(url) }
 
-    context 'url is blank' do
+    subject { described_class.new.call(url) }
+
+    context 'with blank url' do
       let(:url) { '' }
       it { is_expected.to be_nil }
     end
 
-    context 'request failed' do
+    context 'when request fails' do
       before do
-        WebMock.stub_request(:get, url).to_return(status: 500, body: '', headers: {})
+        stub_request(:get, url).to_return(status: 500, body: '', headers: {})
       end
 
       it { is_expected.to be_nil }
     end
 
-    context 'raise OpenSSL::SSL::SSLError' do
+    context 'when OpenSSL::SSL::SSLError is raised' do
       before do
-        allow(Request).to receive_message_chain(:new, :add_headers, :perform).and_raise(OpenSSL::SSL::SSLError)
+        allow(Request).to receive_message_chain(:new, :add_headers, :on_behalf_of, :perform).and_raise(OpenSSL::SSL::SSLError)
       end
 
-      it 'output log and return nil' do
-        expect_any_instance_of(ActiveSupport::Logger).to receive(:debug).with('SSL error: OpenSSL::SSL::SSLError')
-        is_expected.to be_nil
-      end
+      it { is_expected.to be_nil }
     end
 
-    context 'raise HTTP::ConnectionError' do
+    context 'when HTTP::ConnectionError is raised' do
       before do
-        allow(Request).to receive_message_chain(:new, :add_headers, :perform).and_raise(HTTP::ConnectionError)
+        allow(Request).to receive_message_chain(:new, :add_headers, :on_behalf_of, :perform).and_raise(HTTP::ConnectionError)
       end
 
-      it 'output log and return nil' do
-        expect_any_instance_of(ActiveSupport::Logger).to receive(:debug).with('HTTP ConnectionError: HTTP::ConnectionError')
-        is_expected.to be_nil
-      end
+      it { is_expected.to be_nil }
     end
 
-    context 'response success' do
+    context 'when request succeeds' do
       let(:body) { '' }
-      let(:headers) { { 'Content-Type' => content_type } }
-      let(:json) {
-        { id: 1,
+
+      let(:content_type) { 'application/json' }
+
+      let(:headers) do
+        { 'Content-Type' => content_type }
+      end
+
+      let(:json) do
+        {
+          id: 1,
           '@context': ActivityPub::TagManager::CONTEXT,
           type: 'Note',
         }.to_json
-      }
+      end
 
       before do
-        WebMock.stub_request(:get, url).to_return(status: 200, body: body, headers: headers)
+        stub_request(:get, url).to_return(status: 200, body: body, headers: headers)
+      end
+
+      it 'signs request' do
+        subject
+        expect(a_request(:get, url).with(headers: { 'Signature' => /keyId="#{Regexp.escape(ActivityPub::TagManager.instance.uri_for(Account.representative) + '#main-key')}"/ })).to have_been_made
       end
 
-      context 'content type is application/atom+xml' do
+      context 'when content type is application/atom+xml' do
         let(:content_type) { 'application/atom+xml' }
 
-        it { is_expected.to eq [url, { :prefetched_body => "" }, :ostatus] }
+        it { is_expected.to eq nil }
       end
 
-      context 'content_type is activity+json' do
+      context 'when content type is activity+json' do
         let(:content_type) { 'application/activity+json; charset=utf-8' }
         let(:body) { json }
 
         it { is_expected.to eq [1, { prefetched_body: body, id: true }, :activitypub] }
       end
 
-      context 'content_type is ld+json with profile' do
+      context 'when content type is ld+json with profile' do
         let(:content_type) { 'application/ld+json; profile="https://www.w3.org/ns/activitystreams"' }
         let(:body) { json }
 
@@ -75,17 +82,17 @@ RSpec.describe FetchAtomService, type: :service do
       end
 
       before do
-        WebMock.stub_request(:get, url).to_return(status: 200, body: body, headers: headers)
-        WebMock.stub_request(:get, 'http://example.com/foo').to_return(status: 200, body: json, headers: { 'Content-Type' => 'application/activity+json' })
+        stub_request(:get, url).to_return(status: 200, body: body, headers: headers)
+        stub_request(:get, 'http://example.com/foo').to_return(status: 200, body: json, headers: { 'Content-Type' => 'application/activity+json' })
       end
 
-      context 'has link header' do
+      context 'when link header is present' do
         let(:headers) { { 'Link' => '<http://example.com/foo>; rel="alternate"; type="application/activity+json"', } }
 
         it { is_expected.to eq [1, { prefetched_body: json, id: true }, :activitypub] }
       end
 
-      context 'content type is text/html' do
+      context 'when content type is text/html' do
         let(:content_type) { 'text/html' }
         let(:body) { '<html><head><link rel="alternate" href="http://example.com/foo" type="application/activity+json"/></head></html>' }
 
diff --git a/spec/services/follow_service_spec.rb b/spec/services/follow_service_spec.rb
index 3c4ec59be..86c85293e 100644
--- a/spec/services/follow_service_spec.rb
+++ b/spec/services/follow_service_spec.rb
@@ -96,74 +96,6 @@ RSpec.describe FollowService, type: :service do
     end
   end
 
-  context 'remote OStatus account' do
-    describe 'locked account' do
-      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 => {})
-        subject.call(sender, bob.acct)
-      end
-
-      it 'creates a follow request' do
-        expect(FollowRequest.find_by(account: sender, target_account: bob)).to_not be_nil
-      end
-
-      it 'sends a follow request salmon slap' do
-        expect(a_request(:post, "http://salmon.example.com/").with { |req|
-          xml = OStatus2::Salmon.new.unpack(req.body)
-          xml.match(OStatus::TagManager::VERBS[:request_friend])
-        }).to have_been_made.once
-      end
-    end
-
-    describe 'unlocked account' do
-      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 => {})
-        stub_request(:post, "http://hub.example.com/").to_return(status: 202)
-        subject.call(sender, bob.acct)
-      end
-
-      it 'creates a following relation' do
-        expect(sender.following?(bob)).to be true
-      end
-
-      it 'sends a follow salmon slap' do
-        expect(a_request(:post, "http://salmon.example.com/").with { |req|
-          xml = OStatus2::Salmon.new.unpack(req.body)
-          xml.match(OStatus::TagManager::VERBS[:follow])
-        }).to have_been_made.once
-      end
-
-      it 'subscribes to PuSH' do
-        expect(a_request(:post, "http://hub.example.com/")).to have_been_made.once
-      end
-    end
-
-    describe 'already followed account' do
-      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)
-        subject.call(sender, bob.acct)
-      end
-
-      it 'keeps a following relation' do
-        expect(sender.following?(bob)).to be true
-      end
-
-      it 'does not send a follow salmon slap' do
-        expect(a_request(:post, "http://salmon.example.com/")).not_to have_been_made
-      end
-
-      it 'does not subscribe to PuSH' do
-        expect(a_request(:post, "http://hub.example.com/")).not_to have_been_made
-      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 }
 
diff --git a/spec/services/import_service_spec.rb b/spec/services/import_service_spec.rb
index 5cf2dadf0..5355133f4 100644
--- a/spec/services/import_service_spec.rb
+++ b/spec/services/import_service_spec.rb
@@ -3,7 +3,11 @@ require 'rails_helper'
 RSpec.describe ImportService, type: :service do
   let!(:account) { Fabricate(:account, locked: false) }
   let!(:bob)     { Fabricate(:account, username: 'bob', locked: false) }
-  let!(:eve)     { Fabricate(:account, username: 'eve', domain: 'example.com', locked: false) }
+  let!(:eve)     { Fabricate(:account, username: 'eve', domain: 'example.com', locked: false, protocol: :activitypub, inbox_url: 'https://example.com/inbox') }
+
+  before do
+    stub_request(:post, "https://example.com/inbox").to_return(status: 200)
+  end
 
   context 'import old-style list of muted users' do
     subject { ImportService.new }
@@ -95,7 +99,8 @@ RSpec.describe ImportService, type: :service do
       let(:import) { Import.create(account: account, type: 'following', data: csv) }
       it 'follows the listed accounts, including boosts' do
         subject.call(import)
-        expect(account.following.count).to eq 2
+        expect(account.following.count).to eq 1
+        expect(account.follow_requests.count).to eq 1
         expect(Follow.find_by(account: account, target_account: bob).show_reblogs).to be true
       end
     end
@@ -106,7 +111,8 @@ RSpec.describe ImportService, type: :service do
       it 'follows the listed accounts, including notifications' do
         account.follow!(bob, reblogs: false)
         subject.call(import)
-        expect(account.following.count).to eq 2
+        expect(account.following.count).to eq 1
+        expect(account.follow_requests.count).to eq 1
         expect(Follow.find_by(account: account, target_account: bob).show_reblogs).to be true
       end
     end
@@ -117,7 +123,8 @@ RSpec.describe ImportService, type: :service do
       it 'mutes the listed accounts, including notifications' do
         account.follow!(bob, reblogs: false)
         subject.call(import)
-        expect(account.following.count).to eq 2
+        expect(account.following.count).to eq 1
+        expect(account.follow_requests.count).to eq 1
         expect(Follow.find_by(account: account, target_account: bob).show_reblogs).to be true
       end
     end
@@ -136,9 +143,10 @@ RSpec.describe ImportService, type: :service do
       let(:import) { Import.create(account: account, type: 'following', data: csv) }
       it 'follows the listed accounts, respecting boosts' do
         subject.call(import)
-        expect(account.following.count).to eq 2
+        expect(account.following.count).to eq 1
+        expect(account.follow_requests.count).to eq 1
         expect(Follow.find_by(account: account, target_account: bob).show_reblogs).to be true
-        expect(Follow.find_by(account: account, target_account: eve).show_reblogs).to be false
+        expect(FollowRequest.find_by(account: account, target_account: eve).show_reblogs).to be false
       end
     end
 
@@ -148,9 +156,10 @@ RSpec.describe ImportService, type: :service do
       it 'mutes the listed accounts, respecting notifications' do
         account.follow!(bob, reblogs: true)
         subject.call(import)
-        expect(account.following.count).to eq 2
+        expect(account.following.count).to eq 1
+        expect(account.follow_requests.count).to eq 1
         expect(Follow.find_by(account: account, target_account: bob).show_reblogs).to be true
-        expect(Follow.find_by(account: account, target_account: eve).show_reblogs).to be false
+        expect(FollowRequest.find_by(account: account, target_account: eve).show_reblogs).to be false
       end
     end
 
@@ -160,9 +169,10 @@ RSpec.describe ImportService, type: :service do
       it 'mutes the listed accounts, respecting notifications' do
         account.follow!(bob, reblogs: true)
         subject.call(import)
-        expect(account.following.count).to eq 2
+        expect(account.following.count).to eq 1
+        expect(account.follow_requests.count).to eq 1
         expect(Follow.find_by(account: account, target_account: bob).show_reblogs).to be true
-        expect(Follow.find_by(account: account, target_account: eve).show_reblogs).to be false
+        expect(FollowRequest.find_by(account: account, target_account: eve).show_reblogs).to be false
       end
     end
   end
diff --git a/spec/services/post_status_service_spec.rb b/spec/services/post_status_service_spec.rb
index facbe977f..bf06f50e9 100644
--- a/spec/services/post_status_service_spec.rb
+++ b/spec/services/post_status_service_spec.rb
@@ -144,7 +144,6 @@ RSpec.describe PostStatusService, type: :service 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)
@@ -152,7 +151,6 @@ RSpec.describe PostStatusService, type: :service do
     status = subject.call(account, text: "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(ActivityPub::DistributionWorker).to have_received(:perform_async).with(status.id)
   end
 
diff --git a/spec/services/process_feed_service_spec.rb b/spec/services/process_feed_service_spec.rb
deleted file mode 100644
index 9d3465f3f..000000000
--- a/spec/services/process_feed_service_spec.rb
+++ /dev/null
@@ -1,252 +0,0 @@
-require 'rails_helper'
-
-RSpec.describe ProcessFeedService, type: :service do
-  subject { ProcessFeedService.new }
-
-  describe 'processing a feed' do
-    let(:body) { File.read(Rails.root.join('spec', 'fixtures', 'xml', 'mastodon.atom')) }
-    let(:account) { Fabricate(:account, username: 'localhost', domain: 'kickass.zone') }
-
-    before do
-      stub_request(:post, "https://pubsubhubbub.superfeedr.com/").to_return(:status => 200, :body => "", :headers => {})
-      stub_request(:head, "http://kickass.zone/media/2").to_return(:status => 404)
-      stub_request(:head, "http://kickass.zone/media/3").to_return(:status => 404)
-      stub_request(:get, "http://kickass.zone/system/accounts/avatars/000/000/001/large/eris.png").to_return(request_fixture('avatar.txt'))
-      stub_request(:get, "http://kickass.zone/system/media_attachments/files/000/000/002/original/morpheus_linux.jpg?1476059910").to_return(request_fixture('attachment1.txt'))
-      stub_request(:get, "http://kickass.zone/system/media_attachments/files/000/000/003/original/gizmo.jpg?1476060065").to_return(request_fixture('attachment2.txt'))
-    end
-
-    context 'when domain does not reject media' do
-      before do
-        subject.call(body, account)
-      end
-
-      it 'updates remote user\'s account information' do
-        account.reload
-        expect(account.display_name).to eq '::1'
-        expect(account).to have_attached_file(:avatar)
-        expect(account.avatar_file_name).not_to be_nil
-      end
-
-      it 'creates posts' do
-        expect(Status.find_by(uri: 'tag:kickass.zone,2016-10-10:objectId=1:objectType=Status')).to_not be_nil
-        expect(Status.find_by(uri: 'tag:kickass.zone,2016-10-10:objectId=2:objectType=Status')).to_not be_nil
-      end
-
-      it 'marks replies as replies' do
-        status = Status.find_by(uri: 'tag:kickass.zone,2016-10-10:objectId=2:objectType=Status')
-        expect(status.reply?).to be true
-      end
-
-      it 'sets account being replied to when possible' do
-        status = Status.find_by(uri: 'tag:kickass.zone,2016-10-10:objectId=2:objectType=Status')
-        expect(status.in_reply_to_account_id).to eq status.account_id
-      end
-
-      it 'ignores delete statuses unless they existed before' do
-        expect(Status.find_by(uri: 'tag:kickass.zone,2016-10-10:objectId=3:objectType=Status')).to be_nil
-        expect(Status.find_by(uri: 'tag:kickass.zone,2016-10-10:objectId=12:objectType=Status')).to be_nil
-      end
-
-      it 'does not create statuses for follows' do
-        expect(Status.find_by(uri: 'tag:kickass.zone,2016-10-10:objectId=1:objectType=Follow')).to be_nil
-        expect(Status.find_by(uri: 'tag:kickass.zone,2016-10-10:objectId=2:objectType=Follow')).to be_nil
-        expect(Status.find_by(uri: 'tag:kickass.zone,2016-10-10:objectId=4:objectType=Follow')).to be_nil
-        expect(Status.find_by(uri: 'tag:kickass.zone,2016-10-10:objectId=7:objectType=Follow')).to be_nil
-      end
-
-      it 'does not create statuses for favourites' do
-        expect(Status.find_by(uri: 'tag:kickass.zone,2016-10-10:objectId=2:objectType=Favourite')).to be_nil
-        expect(Status.find_by(uri: 'tag:kickass.zone,2016-10-10:objectId=3:objectType=Favourite')).to be_nil
-      end
-
-      it 'creates posts with media' do
-        status = Status.find_by(uri: 'tag:kickass.zone,2016-10-10:objectId=14:objectType=Status')
-
-        expect(status).to_not be_nil
-        expect(status.media_attachments.first).to have_attached_file(:file)
-        expect(status.media_attachments.first.image?).to be true
-        expect(status.media_attachments.first.file_file_name).not_to be_nil
-      end
-    end
-
-    context 'when domain is set to reject media' do
-      let!(:domain_block) { Fabricate(:domain_block, domain: 'kickass.zone', reject_media: true) }
-
-      before do
-        subject.call(body, account)
-      end
-
-      it 'updates remote user\'s account information' do
-        account.reload
-        expect(account.display_name).to eq '::1'
-      end
-
-      it 'rejects remote user\'s avatar' do
-        account.reload
-        expect(account.display_name).to eq '::1'
-        expect(account.avatar_file_name).to be_nil
-      end
-
-      it 'creates posts' do
-        expect(Status.find_by(uri: 'tag:kickass.zone,2016-10-10:objectId=1:objectType=Status')).to_not be_nil
-        expect(Status.find_by(uri: 'tag:kickass.zone,2016-10-10:objectId=2:objectType=Status')).to_not be_nil
-      end
-
-      it 'creates posts with remote-only media' do
-        status = Status.find_by(uri: 'tag:kickass.zone,2016-10-10:objectId=14:objectType=Status')
-
-        expect(status).to_not be_nil
-        expect(status.media_attachments.first.file_file_name).to be_nil
-        expect(status.media_attachments.first.unknown?).to be true
-      end
-    end
-  end
-
-  it 'does not accept tampered reblogs' do
-    good_actor = Fabricate(:account, username: 'tracer', domain: 'overwatch.com')
-
-    real_body = <<XML
-<?xml version="1.0"?>
-<entry xmlns="http://www.w3.org/2005/Atom" xmlns:thr="http://purl.org/syndication/thread/1.0" xmlns:activity="http://activitystrea.ms/spec/1.0/" xmlns:poco="http://portablecontacts.net/spec/1.0" xmlns:media="http://purl.org/syndication/atommedia" xmlns:ostatus="http://ostatus.org/schema/1.0" xmlns:mastodon="http://mastodon.social/schema/1.0">
-  <id>tag:overwatch.com,2017-04-27:objectId=4467137:objectType=Status</id>
-  <published>2017-04-27T13:49:25Z</published>
-  <updated>2017-04-27T13:49:25Z</updated>
-  <activity:object-type>http://activitystrea.ms/schema/1.0/note</activity:object-type>
-  <activity:verb>http://activitystrea.ms/schema/1.0/post</activity:verb>
-  <author>
-    <id>https://overwatch.com/users/tracer</id>
-    <activity:object-type>http://activitystrea.ms/schema/1.0/person</activity:object-type>
-    <uri>https://overwatch.com/users/tracer</uri>
-    <name>tracer</name>
-  </author>
-  <content type="html">Overwatch rocks</content>
-</entry>
-XML
-
-    stub_request(:get, 'https://overwatch.com/users/tracer/updates/1').to_return(status: 200, body: real_body, headers: { 'Content-Type' => 'application/atom+xml' })
-
-    bad_actor = Fabricate(:account, username: 'sombra', domain: 'talon.xyz')
-
-    body = <<XML
-<?xml version="1.0"?>
-<entry xmlns="http://www.w3.org/2005/Atom" xmlns:thr="http://purl.org/syndication/thread/1.0" xmlns:activity="http://activitystrea.ms/spec/1.0/" xmlns:poco="http://portablecontacts.net/spec/1.0" xmlns:media="http://purl.org/syndication/atommedia" xmlns:ostatus="http://ostatus.org/schema/1.0" xmlns:mastodon="http://mastodon.social/schema/1.0">
-  <id>tag:talon.xyz,2017-04-27:objectId=4467137:objectType=Status</id>
-  <published>2017-04-27T13:49:25Z</published>
-  <updated>2017-04-27T13:49:25Z</updated>
-  <author>
-    <id>https://talon.xyz/users/sombra</id>
-    <activity:object-type>http://activitystrea.ms/schema/1.0/person</activity:object-type>
-    <uri>https://talon.xyz/users/sombra</uri>
-    <name>sombra</name>
-  </author>
-  <activity:object-type>http://activitystrea.ms/schema/1.0/activity</activity:object-type>
-  <activity:verb>http://activitystrea.ms/schema/1.0/share</activity:verb>
-  <content type="html">Overwatch SUCKS AHAHA</content>
-  <activity:object>
-    <id>tag:overwatch.com,2017-04-27:objectId=4467137:objectType=Status</id>
-    <activity:object-type>http://activitystrea.ms/schema/1.0/note</activity:object-type>
-    <activity:verb>http://activitystrea.ms/schema/1.0/post</activity:verb>
-    <author>
-      <id>https://overwatch.com/users/tracer</id>
-      <activity:object-type>http://activitystrea.ms/schema/1.0/person</activity:object-type>
-      <uri>https://overwatch.com/users/tracer</uri>
-      <name>tracer</name>
-    </author>
-    <content type="html">Overwatch SUCKS AHAHA</content>
-    <link rel="alternate" type="text/html" href="https://overwatch.com/users/tracer/updates/1" />
-  </activity:object>
-</entry>
-XML
-    created_statuses = subject.call(body, bad_actor)
-
-    expect(created_statuses.first.reblog?).to be true
-    expect(created_statuses.first.account_id).to eq bad_actor.id
-    expect(created_statuses.first.reblog.account_id).to eq good_actor.id
-    expect(created_statuses.first.reblog.text).to eq 'Overwatch rocks'
-  end
-
-  it 'ignores reblogs if it failed to retrieve reblogged statuses' do
-    stub_request(:get, 'https://overwatch.com/users/tracer/updates/1').to_return(status: 404)
-
-    actor = Fabricate(:account, username: 'tracer', domain: 'overwatch.com')
-
-    body = <<XML
-<?xml version="1.0"?>
-<entry xmlns="http://www.w3.org/2005/Atom" xmlns:thr="http://purl.org/syndication/thread/1.0" xmlns:activity="http://activitystrea.ms/spec/1.0/" xmlns:poco="http://portablecontacts.net/spec/1.0" xmlns:media="http://purl.org/syndication/atommedia" xmlns:ostatus="http://ostatus.org/schema/1.0" xmlns:mastodon="http://mastodon.social/schema/1.0">
-  <id>tag:overwatch.com,2017-04-27:objectId=4467137:objectType=Status</id>
-  <published>2017-04-27T13:49:25Z</published>
-  <updated>2017-04-27T13:49:25Z</updated>
-  <author>
-    <id>https://overwatch.com/users/tracer</id>
-    <activity:object-type>http://activitystrea.ms/schema/1.0/person</activity:object-type>
-    <uri>https://overwatch.com/users/tracer</uri>
-    <name>tracer</name>
-  </author>
-  <activity:object-type>http://activitystrea.ms/schema/1.0/activity</activity:object-type>
-  <activity:verb>http://activitystrea.ms/schema/1.0/share</activity:verb>
-  <content type="html">Overwatch rocks</content>
-  <activity:object>
-    <id>tag:overwatch.com,2017-04-27:objectId=4467137:objectType=Status</id>
-    <activity:object-type>http://activitystrea.ms/schema/1.0/note</activity:object-type>
-    <activity:verb>http://activitystrea.ms/schema/1.0/post</activity:verb>
-    <author>
-      <id>https://overwatch.com/users/tracer</id>
-      <activity:object-type>http://activitystrea.ms/schema/1.0/person</activity:object-type>
-      <uri>https://overwatch.com/users/tracer</uri>
-      <name>tracer</name>
-    </author>
-    <content type="html">Overwatch rocks</content>
-    <link rel="alternate" type="text/html" href="https://overwatch.com/users/tracer/updates/1" />
-  </activity:object>
-XML
-
-    created_statuses = subject.call(body, actor)
-
-    expect(created_statuses).to eq []
-  end
-
-  it 'ignores statuses with an out-of-order delete' do
-    sender = Fabricate(:account, username: 'tracer', domain: 'overwatch.com')
-
-    delete_body = <<XML
-<?xml version="1.0"?>
-<entry xmlns="http://www.w3.org/2005/Atom" xmlns:thr="http://purl.org/syndication/thread/1.0" xmlns:activity="http://activitystrea.ms/spec/1.0/" xmlns:poco="http://portablecontacts.net/spec/1.0" xmlns:media="http://purl.org/syndication/atommedia" xmlns:ostatus="http://ostatus.org/schema/1.0" xmlns:mastodon="http://mastodon.social/schema/1.0">
-  <id>tag:overwatch.com,2017-04-27:objectId=4487555:objectType=Status</id>
-  <published>2017-04-27T13:49:25Z</published>
-  <updated>2017-04-27T13:49:25Z</updated>
-  <activity:object-type>http://activitystrea.ms/schema/1.0/note</activity:object-type>
-  <activity:verb>http://activitystrea.ms/schema/1.0/delete</activity:verb>
-  <author>
-    <id>https://overwatch.com/users/tracer</id>
-    <activity:object-type>http://activitystrea.ms/schema/1.0/person</activity:object-type>
-    <uri>https://overwatch.com/users/tracer</uri>
-    <name>tracer</name>
-  </author>
-</entry>
-XML
-
-    status_body = <<XML
-<?xml version="1.0"?>
-<entry xmlns="http://www.w3.org/2005/Atom" xmlns:thr="http://purl.org/syndication/thread/1.0" xmlns:activity="http://activitystrea.ms/spec/1.0/" xmlns:poco="http://portablecontacts.net/spec/1.0" xmlns:media="http://purl.org/syndication/atommedia" xmlns:ostatus="http://ostatus.org/schema/1.0" xmlns:mastodon="http://mastodon.social/schema/1.0">
-  <id>tag:overwatch.com,2017-04-27:objectId=4487555:objectType=Status</id>
-  <published>2017-04-27T13:49:25Z</published>
-  <updated>2017-04-27T13:49:25Z</updated>
-  <activity:object-type>http://activitystrea.ms/schema/1.0/note</activity:object-type>
-  <activity:verb>http://activitystrea.ms/schema/1.0/post</activity:verb>
-  <author>
-    <id>https://overwatch.com/users/tracer</id>
-    <activity:object-type>http://activitystrea.ms/schema/1.0/person</activity:object-type>
-    <uri>https://overwatch.com/users/tracer</uri>
-    <name>tracer</name>
-  </author>
-  <content type="html">Overwatch rocks</content>
-</entry>
-XML
-
-    subject.call(delete_body, sender)
-    created_statuses = subject.call(status_body, sender)
-
-    expect(created_statuses).to be_empty
-  end
-end
diff --git a/spec/services/process_interaction_service_spec.rb b/spec/services/process_interaction_service_spec.rb
deleted file mode 100644
index b858c19d0..000000000
--- a/spec/services/process_interaction_service_spec.rb
+++ /dev/null
@@ -1,151 +0,0 @@
-require 'rails_helper'
-
-RSpec.describe ProcessInteractionService, type: :service do
-  let(:receiver) { Fabricate(:user, email: 'alice@example.com', account: Fabricate(:account, username: 'alice')).account }
-  let(:sender)   { Fabricate(:user, email: 'bob@example.com', account: Fabricate(:account, username: 'bob')).account }
-  let(:remote_sender) { Fabricate(:account, username: 'carol', domain: 'localdomain.com', uri: 'https://webdomain.com/users/carol') }
-
-  subject { ProcessInteractionService.new }
-
-  describe 'status delete slap' do
-    let(:remote_status) { Fabricate(:status, account: remote_sender) }
-    let(:envelope) { OStatus2::Salmon.new.pack(payload, sender.keypair) }
-    let(:payload) {
-      <<~XML
-        <entry xmlns="http://www.w3.org/2005/Atom" xmlns:activity="http://activitystrea.ms/spec/1.0/">
-          <author>
-            <email>carol@localdomain.com</email>
-            <name>carol</name>
-            <uri>https://webdomain.com/users/carol</uri>
-          </author>
-
-          <id>#{remote_status.id}</id>
-          <activity:verb>http://activitystrea.ms/schema/1.0/delete</activity:verb>
-        </entry>
-      XML
-    }
-
-    before do
-      receiver.update(locked: true)
-      remote_sender.update(private_key: sender.private_key, public_key: remote_sender.public_key)
-    end
-
-    it 'deletes a record' do
-      expect(RemovalWorker).to receive(:perform_async).with(remote_status.id)
-      subject.call(envelope, receiver)
-    end
-  end
-
-  describe 'follow request slap' do
-    before do
-      receiver.update(locked: true)
-
-      payload = <<XML
-<entry xmlns="http://www.w3.org/2005/Atom" xmlns:activity="http://activitystrea.ms/spec/1.0/">
-  <author>
-    <name>bob</name>
-    <uri>https://cb6e6126.ngrok.io/users/bob</uri>
-  </author>
-
-  <id>someIdHere</id>
-  <activity:verb>http://activitystrea.ms/schema/1.0/request-friend</activity:verb>
-</entry>
-XML
-
-      envelope = OStatus2::Salmon.new.pack(payload, sender.keypair)
-      subject.call(envelope, receiver)
-    end
-
-    it 'creates a record' do
-      expect(FollowRequest.find_by(account: sender, target_account: receiver)).to_not be_nil
-    end
-  end
-
-  describe 'follow request slap from known remote user identified by email' do
-    before do
-      receiver.update(locked: true)
-      # Copy already-generated key
-      remote_sender.update(private_key: sender.private_key, public_key: remote_sender.public_key)
-
-      payload = <<XML
-<entry xmlns="http://www.w3.org/2005/Atom" xmlns:activity="http://activitystrea.ms/spec/1.0/">
-  <author>
-    <email>carol@localdomain.com</email>
-    <name>carol</name>
-    <uri>https://webdomain.com/users/carol</uri>
-  </author>
-
-  <id>someIdHere</id>
-  <activity:verb>http://activitystrea.ms/schema/1.0/request-friend</activity:verb>
-</entry>
-XML
-
-      envelope = OStatus2::Salmon.new.pack(payload, remote_sender.keypair)
-      subject.call(envelope, receiver)
-    end
-
-    it 'creates a record' do
-      expect(FollowRequest.find_by(account: remote_sender, target_account: receiver)).to_not be_nil
-    end
-  end
-
-  describe 'follow request authorization slap' do
-    before do
-      receiver.update(locked: true)
-      FollowRequest.create(account: sender, target_account: receiver)
-
-      payload = <<XML
-<entry xmlns="http://www.w3.org/2005/Atom" xmlns:activity="http://activitystrea.ms/spec/1.0/">
-  <author>
-    <name>alice</name>
-    <uri>https://cb6e6126.ngrok.io/users/alice</uri>
-  </author>
-
-  <id>someIdHere</id>
-  <activity:verb>http://activitystrea.ms/schema/1.0/authorize</activity:verb>
-</entry>
-XML
-
-      envelope = OStatus2::Salmon.new.pack(payload, receiver.keypair)
-      subject.call(envelope, sender)
-    end
-
-    it 'creates a follow relationship' do
-      expect(Follow.find_by(account: sender, target_account: receiver)).to_not be_nil
-    end
-
-    it 'removes the follow request' do
-      expect(FollowRequest.find_by(account: sender, target_account: receiver)).to be_nil
-    end
-  end
-
-  describe 'follow request rejection slap' do
-    before do
-      receiver.update(locked: true)
-      FollowRequest.create(account: sender, target_account: receiver)
-
-      payload = <<XML
-<entry xmlns="http://www.w3.org/2005/Atom" xmlns:activity="http://activitystrea.ms/spec/1.0/">
-  <author>
-    <name>alice</name>
-    <uri>https://cb6e6126.ngrok.io/users/alice</uri>
-  </author>
-
-  <id>someIdHere</id>
-  <activity:verb>http://activitystrea.ms/schema/1.0/reject</activity:verb>
-</entry>
-XML
-
-      envelope = OStatus2::Salmon.new.pack(payload, receiver.keypair)
-      subject.call(envelope, sender)
-    end
-
-    it 'does not create a follow relationship' do
-      expect(Follow.find_by(account: sender, target_account: receiver)).to be_nil
-    end
-
-    it 'removes the follow request' do
-      expect(FollowRequest.find_by(account: sender, target_account: receiver)).to be_nil
-    end
-  end
-end
diff --git a/spec/services/process_mentions_service_spec.rb b/spec/services/process_mentions_service_spec.rb
index 8a6bb44ac..b1abd79b0 100644
--- a/spec/services/process_mentions_service_spec.rb
+++ b/spec/services/process_mentions_service_spec.rb
@@ -15,12 +15,8 @@ RSpec.describe ProcessMentionsService, type: :service do
       subject.call(status)
     end
 
-    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
+    it 'does not create a mention' do
+      expect(remote_user.mentions.where(status: status).count).to eq 0
     end
   end
 
diff --git a/spec/services/pubsubhubbub/subscribe_service_spec.rb b/spec/services/pubsubhubbub/subscribe_service_spec.rb
deleted file mode 100644
index 01c956230..000000000
--- a/spec/services/pubsubhubbub/subscribe_service_spec.rb
+++ /dev/null
@@ -1,71 +0,0 @@
-# frozen_string_literal: true
-
-require 'rails_helper'
-
-describe Pubsubhubbub::SubscribeService, type: :service do
-  describe '#call' do
-    subject { described_class.new }
-    let(:user_account) { Fabricate(:account) }
-
-    context 'with a nil account' do
-      it 'returns the invalid topic status results' do
-        result = service_call(account: nil)
-
-        expect(result).to eq invalid_topic_status
-      end
-    end
-
-    context 'with an invalid callback url' do
-      it 'returns invalid callback status when callback is blank' do
-        result = service_call(callback: '')
-
-        expect(result).to eq invalid_callback_status
-      end
-      it 'returns invalid callback status when callback is not a URI' do
-        result = service_call(callback: 'invalid-hostname')
-
-        expect(result).to eq invalid_callback_status
-      end
-    end
-
-    context 'with a blocked domain in the callback' do
-      it 'returns callback not allowed' do
-        Fabricate(:domain_block, domain: 'test.host', severity: :suspend)
-        result = service_call(callback: 'https://test.host/api')
-
-        expect(result).to eq not_allowed_callback_status
-      end
-    end
-
-    context 'with a valid account and callback' do
-      it 'returns success status and confirms subscription' do
-        allow(Pubsubhubbub::ConfirmationWorker).to receive(:perform_async).and_return(nil)
-        subscription = Fabricate(:subscription, account: user_account)
-
-        result = service_call(callback: subscription.callback_url)
-        expect(result).to eq success_status
-        expect(Pubsubhubbub::ConfirmationWorker).to have_received(:perform_async).with(subscription.id, 'subscribe', 'asdf', 3600)
-      end
-    end
-  end
-
-  def service_call(account: user_account, callback: 'https://callback.host', secret: 'asdf', lease_seconds: 3600)
-    subject.call(account, callback, secret, lease_seconds)
-  end
-
-  def invalid_topic_status
-    ['Invalid topic URL', 422]
-  end
-
-  def invalid_callback_status
-    ['Invalid callback URL', 422]
-  end
-
-  def not_allowed_callback_status
-    ['Callback URL not allowed', 403]
-  end
-
-  def success_status
-    ['', 202]
-  end
-end
diff --git a/spec/services/pubsubhubbub/unsubscribe_service_spec.rb b/spec/services/pubsubhubbub/unsubscribe_service_spec.rb
deleted file mode 100644
index 7ed9fc5af..000000000
--- a/spec/services/pubsubhubbub/unsubscribe_service_spec.rb
+++ /dev/null
@@ -1,46 +0,0 @@
-# frozen_string_literal: true
-
-require 'rails_helper'
-
-describe Pubsubhubbub::UnsubscribeService, type: :service do
-  describe '#call' do
-    subject { described_class.new }
-
-    context 'with a nil account' do
-      it 'returns an invalid topic status' do
-        result = subject.call(nil, 'callback.host')
-
-        expect(result).to eq invalid_topic_status
-      end
-    end
-
-    context 'with a valid account' do
-      let(:account) { Fabricate(:account) }
-
-      it 'returns a valid topic status and does not run confirm when no subscription' do
-        allow(Pubsubhubbub::ConfirmationWorker).to receive(:perform_async).and_return(nil)
-        result = subject.call(account, 'callback.host')
-
-        expect(result).to eq valid_topic_status
-        expect(Pubsubhubbub::ConfirmationWorker).not_to have_received(:perform_async)
-      end
-
-      it 'returns a valid topic status and does run confirm when there is a subscription' do
-        subscription = Fabricate(:subscription, account: account, callback_url: 'callback.host')
-        allow(Pubsubhubbub::ConfirmationWorker).to receive(:perform_async).and_return(nil)
-        result = subject.call(account, 'callback.host')
-
-        expect(result).to eq valid_topic_status
-        expect(Pubsubhubbub::ConfirmationWorker).to have_received(:perform_async).with(subscription.id, 'unsubscribe')
-      end
-    end
-
-    def invalid_topic_status
-      ['Invalid topic URL', 422]
-    end
-
-    def valid_topic_status
-      ['', 202]
-    end
-  end
-end
diff --git a/spec/services/reblog_service_spec.rb b/spec/services/reblog_service_spec.rb
index 9d84c41d5..58fb46f0f 100644
--- a/spec/services/reblog_service_spec.rb
+++ b/spec/services/reblog_service_spec.rb
@@ -46,10 +46,6 @@ RSpec.describe ReblogService, type: :service do
     it 'creates a reblog' do
       expect(status.reblogs.count).to eq 1
     end
-
-    it 'sends a Salmon slap for a remote reblog' do
-      expect(a_request(:post, 'http://salmon.example.com')).to have_been_made
-    end
   end
 
   context 'ActivityPub' do
diff --git a/spec/services/reject_follow_service_spec.rb b/spec/services/reject_follow_service_spec.rb
index e5ac37ed9..1aec060db 100644
--- a/spec/services/reject_follow_service_spec.rb
+++ b/spec/services/reject_follow_service_spec.rb
@@ -38,13 +38,6 @@ RSpec.describe RejectFollowService, type: :service do
     it 'does not create follow relation' do
       expect(bob.following?(sender)).to be false
     end
-
-    it 'sends a follow request rejection salmon slap' do
-      expect(a_request(:post, "http://salmon.example.com/").with { |req|
-        xml = OStatus2::Salmon.new.unpack(req.body)
-        xml.match(OStatus::TagManager::VERBS[:reject])
-      }).to have_been_made.once
-    end
   end
 
   describe 'remote ActivityPub' do
diff --git a/spec/services/remove_status_service_spec.rb b/spec/services/remove_status_service_spec.rb
index 7bba83a60..48191d47c 100644
--- a/spec/services/remove_status_service_spec.rb
+++ b/spec/services/remove_status_service_spec.rb
@@ -32,23 +32,10 @@ RSpec.describe RemoveStatusService, type: :service do
     expect(HomeFeed.new(jeff).get(10)).to_not include(@status.id)
   end
 
-  it 'sends PuSH update to PuSH subscribers' do
-    expect(a_request(:post, 'http://example.com/push').with { |req|
-      req.body.match(OStatus::TagManager::VERBS[:delete])
-    }).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)
-      xml.match(OStatus::TagManager::VERBS[:delete])
-    }).to have_been_made.once
-  end
-
   it 'sends delete activity to rebloggers' do
     expect(a_request(:post, 'http://example2.com/inbox')).to have_been_made
   end
diff --git a/spec/services/resolve_account_service_spec.rb b/spec/services/resolve_account_service_spec.rb
index 27a85af7c..cea942e39 100644
--- a/spec/services/resolve_account_service_spec.rb
+++ b/spec/services/resolve_account_service_spec.rb
@@ -6,19 +6,13 @@ RSpec.describe ResolveAccountService, type: :service do
   before do
     stub_request(:get, "https://quitter.no/.well-known/host-meta").to_return(request_fixture('.host-meta.txt'))
     stub_request(:get, "https://example.com/.well-known/webfinger?resource=acct:catsrgr8@example.com").to_return(status: 404)
-    stub_request(:get, "https://redirected.com/.well-known/host-meta").to_return(request_fixture('redirected.host-meta.txt'))
     stub_request(:get, "https://example.com/.well-known/host-meta").to_return(status: 404)
-    stub_request(:get, "https://quitter.no/.well-known/webfinger?resource=acct:gargron@quitter.no").to_return(request_fixture('webfinger.txt'))
-    stub_request(:get, "https://redirected.com/.well-known/webfinger?resource=acct:gargron@redirected.com").to_return(request_fixture('webfinger.txt'))
-    stub_request(:get, "https://redirected.com/.well-known/webfinger?resource=acct:hacker1@redirected.com").to_return(request_fixture('webfinger-hacker1.txt'))
-    stub_request(:get, "https://redirected.com/.well-known/webfinger?resource=acct:hacker2@redirected.com").to_return(request_fixture('webfinger-hacker2.txt'))
-    stub_request(:get, "https://quitter.no/.well-known/webfinger?resource=acct:catsrgr8@quitter.no").to_return(status: 404)
-    stub_request(:get, "https://quitter.no/api/statuses/user_timeline/7477.atom").to_return(request_fixture('feed.txt'))
     stub_request(:get, "https://quitter.no/avatar/7477-300-20160211190340.png").to_return(request_fixture('avatar.txt'))
-    stub_request(:get, "https://localdomain.com/.well-known/host-meta").to_return(request_fixture('localdomain-hostmeta.txt'))
-    stub_request(:get, "https://localdomain.com/.well-known/webfinger?resource=acct:foo@localdomain.com").to_return(status: 404)
-    stub_request(:get, "https://webdomain.com/.well-known/webfinger?resource=acct:foo@localdomain.com").to_return(request_fixture('localdomain-webfinger.txt'))
-    stub_request(:get, "https://webdomain.com/users/foo.atom").to_return(request_fixture('localdomain-feed.txt'))
+    stub_request(:get, "https://quitter.no/.well-known/webfinger?resource=acct:catsrgr8@quitter.no").to_return(status: 404)
+    stub_request(:get, "https://ap.example.com/.well-known/webfinger?resource=acct:foo@ap.example.com").to_return(request_fixture('activitypub-webfinger.txt'))
+    stub_request(:get, "https://ap.example.com/users/foo").to_return(request_fixture('activitypub-actor.txt'))
+    stub_request(:get, "https://ap.example.com/users/foo.atom").to_return(request_fixture('activitypub-feed.txt'))
+    stub_request(:get, %r{https://ap.example.com/users/foo/\w+}).to_return(status: 404)
   end
 
   it 'raises error if no such user can be resolved via webfinger' do
@@ -29,74 +23,7 @@ RSpec.describe ResolveAccountService, type: :service do
     expect(subject.call('catsrgr8@example.com')).to be_nil
   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'
-  end
-
-  it 'prevents hijacking inexisting accounts' do
-    expect(subject.call('hacker2@redirected.com')).to be_nil
-  end
-
-  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
-
   context 'with an ActivityPub account' do
-    before do
-      stub_request(:get, "https://ap.example.com/.well-known/webfinger?resource=acct:foo@ap.example.com").to_return(request_fixture('activitypub-webfinger.txt'))
-      stub_request(:get, "https://ap.example.com/users/foo").to_return(request_fixture('activitypub-actor.txt'))
-      stub_request(:get, "https://ap.example.com/users/foo.atom").to_return(request_fixture('activitypub-feed.txt'))
-      stub_request(:get, %r{https://ap.example.com/users/foo/\w+}).to_return(status: 404)
-    end
-
-    it 'fallback to OStatus if actor json could not be fetched' do
-      stub_request(:get, "https://ap.example.com/users/foo").to_return(status: 404)
-
-      account = subject.call('foo@ap.example.com')
-
-      expect(account.ostatus?).to eq true
-      expect(account.remote_url).to eq 'https://ap.example.com/users/foo.atom'
-    end
-
-    it 'fallback to OStatus if actor json did not have inbox_url' do
-      stub_request(:get, "https://ap.example.com/users/foo").to_return(request_fixture('activitypub-actor-noinbox.txt'))
-
-      account = subject.call('foo@ap.example.com')
-
-      expect(account.ostatus?).to eq true
-      expect(account.remote_url).to eq 'https://ap.example.com/users/foo.atom'
-    end
-
     it 'returns new remote account' do
       account = subject.call('foo@ap.example.com')
 
@@ -124,13 +51,19 @@ RSpec.describe ResolveAccountService, type: :service do
   it 'processes one remote account at a time using locks' do
     wait_for_start = true
     fail_occurred  = false
-    return_values  = []
+    return_values  = Concurrent::Array.new
+
+    # Preload classes that throw circular dependency errors in threads
+    Account
+    TagManager
+    DomainBlock
 
     threads = Array.new(5) do
       Thread.new do
         true while wait_for_start
+
         begin
-          return_values << described_class.new.call('foo@localdomain.com')
+          return_values << described_class.new.call('foo@ap.example.com')
         rescue ActiveRecord::RecordNotUnique
           fail_occurred = true
         end
diff --git a/spec/services/resolve_url_service_spec.rb b/spec/services/resolve_url_service_spec.rb
index 7bb5d1940..aa4204637 100644
--- a/spec/services/resolve_url_service_spec.rb
+++ b/spec/services/resolve_url_service_spec.rb
@@ -6,48 +6,14 @@ describe ResolveURLService, type: :service do
   subject { described_class.new }
 
   describe '#call' do
-    it 'returns nil when there is no atom url' do
-      url = 'http://example.com/missing-atom'
+    it 'returns nil when there is no resource url' do
+      url     = 'http://example.com/missing-resource'
       service = double
-      allow(FetchAtomService).to receive(:new).and_return service
-      allow(service).to receive(:call).with(url).and_return(nil)
-
-      result = subject.call(url)
-      expect(result).to be_nil
-    end
-
-    it 'fetches remote accounts for feed types' do
-      url = 'http://example.com/atom-feed'
-      service = double
-      allow(FetchAtomService).to receive(:new).and_return service
-      feed_url = 'http://feed-url'
-      feed_content = '<feed>contents</feed>'
-      allow(service).to receive(:call).with(url).and_return([feed_url, { prefetched_body: feed_content }])
-
-      account_service = double
-      allow(FetchRemoteAccountService).to receive(:new).and_return(account_service)
-      allow(account_service).to receive(:call)
-
-      _result = subject.call(url)
 
-      expect(account_service).to have_received(:call).with(feed_url, feed_content, nil)
-    end
-
-    it 'fetches remote statuses for entry types' do
-      url = 'http://example.com/atom-entry'
-      service = double
-      allow(FetchAtomService).to receive(:new).and_return service
-      feed_url = 'http://feed-url'
-      feed_content = '<entry>contents</entry>'
-      allow(service).to receive(:call).with(url).and_return([feed_url, { prefetched_body: feed_content }])
-
-      account_service = double
-      allow(FetchRemoteStatusService).to receive(:new).and_return(account_service)
-      allow(account_service).to receive(:call)
-
-      _result = subject.call(url)
+      allow(FetchResourceService).to receive(:new).and_return service
+      allow(service).to receive(:call).with(url).and_return(nil)
 
-      expect(account_service).to have_received(:call).with(feed_url, feed_content, nil)
+      expect(subject.call(url)).to be_nil
     end
   end
 end
diff --git a/spec/services/send_interaction_service_spec.rb b/spec/services/send_interaction_service_spec.rb
deleted file mode 100644
index 710d8184c..000000000
--- a/spec/services/send_interaction_service_spec.rb
+++ /dev/null
@@ -1,7 +0,0 @@
-require 'rails_helper'
-
-RSpec.describe SendInteractionService, type: :service do
-  subject { SendInteractionService.new }
-
-  it 'sends an XML envelope to the Salmon end point of remote user'
-end
diff --git a/spec/services/subscribe_service_spec.rb b/spec/services/subscribe_service_spec.rb
deleted file mode 100644
index 10bdb1ba8..000000000
--- a/spec/services/subscribe_service_spec.rb
+++ /dev/null
@@ -1,43 +0,0 @@
-require 'rails_helper'
-
-RSpec.describe SubscribeService, type: :service do
-  let(:account) { Fabricate(:account, username: 'bob', domain: 'example.com', hub_url: 'http://hub.example.com') }
-  subject { SubscribeService.new }
-
-  it 'sends subscription request to PuSH hub' do
-    stub_request(:post, 'http://hub.example.com/').to_return(status: 202)
-    subject.call(account)
-    expect(a_request(:post, 'http://hub.example.com/')).to have_been_made.once
-  end
-
-  it 'generates and keeps PuSH secret on successful call' do
-    stub_request(:post, 'http://hub.example.com/').to_return(status: 202)
-    subject.call(account)
-    expect(account.secret).to_not be_blank
-  end
-
-  it 'fails silently if PuSH hub forbids subscription' do
-    stub_request(:post, 'http://hub.example.com/').to_return(status: 403)
-    subject.call(account)
-  end
-
-  it 'fails silently if PuSH hub is not found' do
-    stub_request(:post, 'http://hub.example.com/').to_return(status: 404)
-    subject.call(account)
-  end
-
-  it 'fails loudly if there is a network error' do
-    stub_request(:post, 'http://hub.example.com/').to_raise(HTTP::Error)
-    expect { subject.call(account) }.to raise_error HTTP::Error
-  end
-
-  it 'fails loudly if PuSH hub is unavailable' do
-    stub_request(:post, 'http://hub.example.com/').to_return(status: 503)
-    expect { subject.call(account) }.to raise_error Mastodon::UnexpectedResponseError
-  end
-
-  it 'fails loudly if rate limited' do
-    stub_request(:post, 'http://hub.example.com/').to_return(status: 429)
-    expect { subject.call(account) }.to raise_error Mastodon::UnexpectedResponseError
-  end
-end
diff --git a/spec/services/suspend_account_service_spec.rb b/spec/services/suspend_account_service_spec.rb
index 6f45762aa..896ac17a3 100644
--- a/spec/services/suspend_account_service_spec.rb
+++ b/spec/services/suspend_account_service_spec.rb
@@ -27,14 +27,13 @@ RSpec.describe SuspendAccountService, type: :service do
         [
           account.statuses,
           account.media_attachments,
-          account.stream_entries,
           account.notifications,
           account.favourites,
           account.active_relationships,
           account.passive_relationships,
           account.subscriptions
         ].map(&:count)
-      }.from([1, 1, 1, 1, 1, 1, 1, 1]).to([0, 0, 0, 0, 0, 0, 0, 0])
+      }.from([1, 1, 1, 1, 1, 1, 1]).to([0, 0, 0, 0, 0, 0, 0])
     end
 
     it 'sends a delete actor activity to all known inboxes' do
@@ -70,14 +69,13 @@ RSpec.describe SuspendAccountService, type: :service do
         [
           remote_bob.statuses,
           remote_bob.media_attachments,
-          remote_bob.stream_entries,
           remote_bob.notifications,
           remote_bob.favourites,
           remote_bob.active_relationships,
           remote_bob.passive_relationships,
           remote_bob.subscriptions
         ].map(&:count)
-      }.from([1, 1, 1, 1, 1, 1, 1, 1]).to([0, 0, 0, 0, 0, 0, 0, 0])
+      }.from([1, 1, 1, 1, 1, 1, 1]).to([0, 0, 0, 0, 0, 0, 0])
     end
 
     it 'sends a reject follow to follwer inboxes' do
diff --git a/spec/services/unblock_service_spec.rb b/spec/services/unblock_service_spec.rb
index 5835b912b..6350c6834 100644
--- a/spec/services/unblock_service_spec.rb
+++ b/spec/services/unblock_service_spec.rb
@@ -30,13 +30,6 @@ RSpec.describe UnblockService, type: :service do
     it 'destroys the blocking relation' do
       expect(sender.blocking?(bob)).to be false
     end
-
-    it 'sends an unblock salmon slap' do
-      expect(a_request(:post, "http://salmon.example.com/").with { |req|
-        xml = OStatus2::Salmon.new.unpack(req.body)
-        xml.match(OStatus::TagManager::VERBS[:unblock])
-      }).to have_been_made.once
-    end
   end
 
   describe 'remote ActivityPub' do
diff --git a/spec/services/unfollow_service_spec.rb b/spec/services/unfollow_service_spec.rb
index 8a2881ab1..84b5dafbc 100644
--- a/spec/services/unfollow_service_spec.rb
+++ b/spec/services/unfollow_service_spec.rb
@@ -30,13 +30,6 @@ RSpec.describe UnfollowService, type: :service do
     it 'destroys the following relation' do
       expect(sender.following?(bob)).to be false
     end
-
-    it 'sends an unfollow salmon slap' do
-      expect(a_request(:post, "http://salmon.example.com/").with { |req|
-        xml = OStatus2::Salmon.new.unpack(req.body)
-        xml.match(OStatus::TagManager::VERBS[:unfollow])
-      }).to have_been_made.once
-    end
   end
 
   describe 'remote ActivityPub' do
diff --git a/spec/services/unsubscribe_service_spec.rb b/spec/services/unsubscribe_service_spec.rb
deleted file mode 100644
index 54d4b1b53..000000000
--- a/spec/services/unsubscribe_service_spec.rb
+++ /dev/null
@@ -1,37 +0,0 @@
-require 'rails_helper'
-
-RSpec.describe UnsubscribeService, type: :service do
-  let(:account) { Fabricate(:account, username: 'bob', domain: 'example.com', hub_url: 'http://hub.example.com') }
-  subject { UnsubscribeService.new }
-
-  it 'removes the secret and resets expiration on account' do
-    stub_request(:post, 'http://hub.example.com/').to_return(status: 204)
-    subject.call(account)
-    account.reload
-
-    expect(account.secret).to be_blank
-    expect(account.subscription_expires_at).to be_blank
-  end
-
-  it 'logs error on subscription failure' do
-    logger = stub_logger
-    stub_request(:post, 'http://hub.example.com/').to_return(status: 404)
-    subject.call(account)
-
-    expect(logger).to have_received(:debug).with(/unsubscribe for bob@example.com failed/)
-  end
-
-  it 'logs error on connection failure' do
-    logger = stub_logger
-    stub_request(:post, 'http://hub.example.com/').to_raise(HTTP::Error)
-    subject.call(account)
-
-    expect(logger).to have_received(:debug).with(/unsubscribe for bob@example.com failed/)
-  end
-
-  def stub_logger
-    double(debug: nil).tap do |logger|
-      allow(Rails).to receive(:logger).and_return(logger)
-    end
-  end
-end
diff --git a/spec/services/update_remote_profile_service_spec.rb b/spec/services/update_remote_profile_service_spec.rb
deleted file mode 100644
index f3ea70b80..000000000
--- a/spec/services/update_remote_profile_service_spec.rb
+++ /dev/null
@@ -1,84 +0,0 @@
-require 'rails_helper'
-
-RSpec.describe UpdateRemoteProfileService, type: :service do
-  let(:xml) { File.read(Rails.root.join('spec', 'fixtures', 'push', 'feed.atom')) }
-
-  subject { UpdateRemoteProfileService.new }
-
-  before do
-    stub_request(:get, 'https://quitter.no/avatar/7477-300-20160211190340.png').to_return(request_fixture('avatar.txt'))
-  end
-
-  context 'with updated details' do
-    let(:remote_account) { Fabricate(:account, username: 'bob', domain: 'example.com') }
-
-    before do
-      subject.call(xml, remote_account)
-    end
-
-    it 'downloads new avatar' do
-      expect(a_request(:get, 'https://quitter.no/avatar/7477-300-20160211190340.png')).to have_been_made
-    end
-
-    it 'sets the avatar remote url' do
-      expect(remote_account.reload.avatar_remote_url).to eq 'https://quitter.no/avatar/7477-300-20160211190340.png'
-    end
-
-    it 'sets display name' do
-      expect(remote_account.reload.display_name).to eq 'DIGITAL CAT'
-    end
-
-    it 'sets note' do
-      expect(remote_account.reload.note).to eq 'Software engineer, free time musician and DIGITAL SPORTS enthusiast. Likes cats. Warning: May contain memes'
-    end
-  end
-
-  context 'with unchanged details' do
-    let(:remote_account) { Fabricate(:account, username: 'bob', domain: 'example.com', display_name: 'DIGITAL CAT', note: 'Software engineer, free time musician and DIGITAL SPORTS enthusiast. Likes cats. Warning: May contain memes', avatar_remote_url: 'https://quitter.no/avatar/7477-300-20160211190340.png') }
-
-    before do
-      subject.call(xml, remote_account)
-    end
-
-    it 'does not re-download avatar' do
-      expect(a_request(:get, 'https://quitter.no/avatar/7477-300-20160211190340.png')).to have_been_made.once
-    end
-
-    it 'sets the avatar remote url' do
-      expect(remote_account.reload.avatar_remote_url).to eq 'https://quitter.no/avatar/7477-300-20160211190340.png'
-    end
-
-    it 'sets display name' do
-      expect(remote_account.reload.display_name).to eq 'DIGITAL CAT'
-    end
-
-    it 'sets note' do
-      expect(remote_account.reload.note).to eq 'Software engineer, free time musician and DIGITAL SPORTS enthusiast. Likes cats. Warning: May contain memes'
-    end
-  end
-
-  context 'with updated details from a domain set to reject media' do
-    let(:remote_account) { Fabricate(:account, username: 'bob', domain: 'example.com') }
-    let!(:domain_block) { Fabricate(:domain_block, domain: 'example.com', reject_media: true) }
-
-    before do
-      subject.call(xml, remote_account)
-    end
-
-    it 'does not the avatar remote url' do
-      expect(remote_account.reload.avatar_remote_url).to be_nil
-    end
-
-    it 'sets display name' do
-      expect(remote_account.reload.display_name).to eq 'DIGITAL CAT'
-    end
-
-    it 'sets note' do
-      expect(remote_account.reload.note).to eq 'Software engineer, free time musician and DIGITAL SPORTS enthusiast. Likes cats. Warning: May contain memes'
-    end
-
-    it 'does not set store the avatar' do
-      expect(remote_account.reload.avatar_file_name).to be_nil
-    end
-  end
-end
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
index 0cd1f91d0..45ba1bbd9 100644
--- a/spec/spec_helper.rb
+++ b/spec/spec_helper.rb
@@ -27,6 +27,7 @@ RSpec.configure do |config|
   end
 
   config.before :suite do
+    Rails.application.load_seed
     Chewy.strategy(:bypass)
   end
 
diff --git a/spec/views/stream_entries/show.html.haml_spec.rb b/spec/views/statuses/show.html.haml_spec.rb
index 93f0adb99..dbda3b665 100644
--- a/spec/views/stream_entries/show.html.haml_spec.rb
+++ b/spec/views/statuses/show.html.haml_spec.rb
@@ -2,10 +2,9 @@
 
 require 'rails_helper'
 
-describe 'stream_entries/show.html.haml', without_verify_partial_doubles: true do
+describe 'statuses/show.html.haml', without_verify_partial_doubles: true do
   before do
     double(:api_oembed_url => '')
-    double(:account_stream_entry_url => '')
     allow(view).to receive(:show_landing_strip?).and_return(true)
     allow(view).to receive(:site_title).and_return('example site')
     allow(view).to receive(:site_hostname).and_return('example.com')
@@ -23,9 +22,7 @@ describe 'stream_entries/show.html.haml', without_verify_partial_doubles: true d
     reply  =  Fabricate(:status, account: bob, thread: status, text: 'Hello Alice')
 
     assign(:status, status)
-    assign(:stream_entry, status.stream_entry)
     assign(:account, alice)
-    assign(:type, status.stream_entry.activity_type.downcase)
     assign(:descendant_threads, [])
 
     render
@@ -46,11 +43,9 @@ describe 'stream_entries/show.html.haml', without_verify_partial_doubles: true d
     comment =  Fabricate(:status, account: carl, thread: reply, text: 'Hello Bob')
 
     assign(:status, reply)
-    assign(:stream_entry, reply.stream_entry)
     assign(:account, alice)
-    assign(:type, reply.stream_entry.activity_type.downcase)
-    assign(:ancestors, reply.stream_entry.activity.ancestors(1, bob))
-    assign(:descendant_threads, [{ statuses: reply.stream_entry.activity.descendants(1) }])
+    assign(:ancestors, reply.ancestors(1, bob))
+    assign(:descendant_threads, [{ statuses: reply.descendants(1) }])
 
     render
 
@@ -71,9 +66,7 @@ describe 'stream_entries/show.html.haml', without_verify_partial_doubles: true d
     status  =  Fabricate(:status, account: alice, text: 'Hello World')
 
     assign(:status, status)
-    assign(:stream_entry, status.stream_entry)
     assign(:account, alice)
-    assign(:type, status.stream_entry.activity_type.downcase)
     assign(:descendant_threads, [])
 
     render
diff --git a/spec/workers/after_remote_follow_request_worker_spec.rb b/spec/workers/after_remote_follow_request_worker_spec.rb
deleted file mode 100644
index bd623cca5..000000000
--- a/spec/workers/after_remote_follow_request_worker_spec.rb
+++ /dev/null
@@ -1,59 +0,0 @@
-# frozen_string_literal: true
-
-require 'rails_helper'
-
-describe AfterRemoteFollowRequestWorker do
-  subject { described_class.new }
-  let(:follow_request) { Fabricate(:follow_request) }
-  describe 'perform' do
-    context 'when the follow_request does not exist' do
-      it 'catches a raise and returns true' do
-        allow(FollowService).to receive(:new)
-        result = subject.perform('aaa')
-
-        expect(result).to eq(true)
-        expect(FollowService).not_to have_received(:new)
-      end
-    end
-
-    context 'when the account cannot be updated' do
-      it 'returns nil and does not call service when account is nil' do
-        allow(FollowService).to receive(:new)
-        service = double(call: nil)
-        allow(FetchRemoteAccountService).to receive(:new).and_return(service)
-
-        result = subject.perform(follow_request.id)
-
-        expect(result).to be_nil
-        expect(FollowService).not_to have_received(:new)
-      end
-
-      it 'returns nil and does not call service when account is locked' do
-        allow(FollowService).to receive(:new)
-        service = double(call: double(locked?: true))
-        allow(FetchRemoteAccountService).to receive(:new).and_return(service)
-
-        result = subject.perform(follow_request.id)
-
-        expect(result).to be_nil
-        expect(FollowService).not_to have_received(:new)
-      end
-    end
-
-    context 'when the account is updated' do
-      it 'calls the follow service and destroys the follow' do
-        follow_service = double(call: nil)
-        allow(FollowService).to receive(:new).and_return(follow_service)
-        account = Fabricate(:account, locked: false)
-        service = double(call: account)
-        allow(FetchRemoteAccountService).to receive(:new).and_return(service)
-
-        result = subject.perform(follow_request.id)
-
-        expect(result).to be_nil
-        expect(follow_service).to have_received(:call).with(follow_request.account, account.acct)
-        expect { follow_request.reload }.to raise_error(ActiveRecord::RecordNotFound)
-      end
-    end
-  end
-end
diff --git a/spec/workers/after_remote_follow_worker_spec.rb b/spec/workers/after_remote_follow_worker_spec.rb
deleted file mode 100644
index d93c469f9..000000000
--- a/spec/workers/after_remote_follow_worker_spec.rb
+++ /dev/null
@@ -1,59 +0,0 @@
-# frozen_string_literal: true
-
-require 'rails_helper'
-
-describe AfterRemoteFollowWorker do
-  subject { described_class.new }
-  let(:follow) { Fabricate(:follow) }
-  describe 'perform' do
-    context 'when the follow does not exist' do
-      it 'catches a raise and returns true' do
-        allow(FollowService).to receive(:new)
-        result = subject.perform('aaa')
-
-        expect(result).to eq(true)
-        expect(FollowService).not_to have_received(:new)
-      end
-    end
-
-    context 'when the account cannot be updated' do
-      it 'returns nil and does not call service when account is nil' do
-        allow(FollowService).to receive(:new)
-        service = double(call: nil)
-        allow(FetchRemoteAccountService).to receive(:new).and_return(service)
-
-        result = subject.perform(follow.id)
-
-        expect(result).to be_nil
-        expect(FollowService).not_to have_received(:new)
-      end
-
-      it 'returns nil and does not call service when account is not locked' do
-        allow(FollowService).to receive(:new)
-        service = double(call: double(locked?: false))
-        allow(FetchRemoteAccountService).to receive(:new).and_return(service)
-
-        result = subject.perform(follow.id)
-
-        expect(result).to be_nil
-        expect(FollowService).not_to have_received(:new)
-      end
-    end
-
-    context 'when the account is updated' do
-      it 'calls the follow service and destroys the follow' do
-        follow_service = double(call: nil)
-        allow(FollowService).to receive(:new).and_return(follow_service)
-        account = Fabricate(:account, locked: true)
-        service = double(call: account)
-        allow(FetchRemoteAccountService).to receive(:new).and_return(service)
-
-        result = subject.perform(follow.id)
-
-        expect(result).to be_nil
-        expect(follow_service).to have_received(:call).with(follow.account, account.acct)
-        expect { follow.reload }.to raise_error(ActiveRecord::RecordNotFound)
-      end
-    end
-  end
-end
diff --git a/spec/workers/pubsubhubbub/confirmation_worker_spec.rb b/spec/workers/pubsubhubbub/confirmation_worker_spec.rb
deleted file mode 100644
index 1eecdd2b5..000000000
--- a/spec/workers/pubsubhubbub/confirmation_worker_spec.rb
+++ /dev/null
@@ -1,88 +0,0 @@
-# frozen_string_literal: true
-
-require 'rails_helper'
-
-describe Pubsubhubbub::ConfirmationWorker do
-  include RoutingHelper
-
-  subject { described_class.new }
-
-  let!(:alice) { Fabricate(:account, username: 'alice') }
-  let!(:subscription) { Fabricate(:subscription, account: alice, callback_url: 'http://example.com/api', confirmed: false, expires_at: 3.days.from_now, secret: nil) }
-
-  describe 'perform' do
-    describe 'with subscribe mode' do
-      it 'confirms and updates subscription when challenge matches' do
-        stub_random_value
-        stub_request(:get, url_for_mode('subscribe'))
-          .with(headers: http_headers)
-          .to_return(status: 200, body: challenge_value, headers: {})
-
-        seconds = 10.days.seconds.to_i
-        subject.perform(subscription.id, 'subscribe', 'asdf', seconds)
-
-        subscription.reload
-        expect(subscription.secret).to eq 'asdf'
-        expect(subscription.confirmed).to eq true
-        expect(subscription.expires_at).to be_within(5).of(10.days.from_now)
-      end
-
-      it 'does not update subscription when challenge does not match' do
-        stub_random_value
-        stub_request(:get, url_for_mode('subscribe'))
-          .with(headers: http_headers)
-          .to_return(status: 200, body: 'wrong value', headers: {})
-
-        seconds = 10.days.seconds.to_i
-        subject.perform(subscription.id, 'subscribe', 'asdf', seconds)
-
-        subscription.reload
-        expect(subscription.secret).to be_blank
-        expect(subscription.confirmed).to eq false
-        expect(subscription.expires_at).to be_within(5).of(3.days.from_now)
-      end
-    end
-
-    describe 'with unsubscribe mode' do
-      it 'confirms and destroys subscription when challenge matches' do
-        stub_random_value
-        stub_request(:get, url_for_mode('unsubscribe'))
-          .with(headers: http_headers)
-          .to_return(status: 200, body: challenge_value, headers: {})
-
-        seconds = 10.days.seconds.to_i
-        subject.perform(subscription.id, 'unsubscribe', 'asdf', seconds)
-
-        expect { subscription.reload }.to raise_error(ActiveRecord::RecordNotFound)
-      end
-
-      it 'does not destroy subscription when challenge does not match' do
-        stub_random_value
-        stub_request(:get, url_for_mode('unsubscribe'))
-          .with(headers: http_headers)
-          .to_return(status: 200, body: 'wrong value', headers: {})
-
-        seconds = 10.days.seconds.to_i
-        subject.perform(subscription.id, 'unsubscribe', 'asdf', seconds)
-
-        expect { subscription.reload }.not_to raise_error
-      end
-    end
-  end
-
-  def url_for_mode(mode)
-    "http://example.com/api?hub.challenge=#{challenge_value}&hub.lease_seconds=863999&hub.mode=#{mode}&hub.topic=https://#{Rails.configuration.x.local_domain}/users/alice.atom"
-  end
-
-  def stub_random_value
-    allow(SecureRandom).to receive(:hex).and_return(challenge_value)
-  end
-
-  def challenge_value
-    '1a2s3d4f'
-  end
-
-  def http_headers
-    { 'Connection' => 'close', 'Host' => 'example.com' }
-  end
-end
diff --git a/spec/workers/pubsubhubbub/delivery_worker_spec.rb b/spec/workers/pubsubhubbub/delivery_worker_spec.rb
deleted file mode 100644
index c0e0d5186..000000000
--- a/spec/workers/pubsubhubbub/delivery_worker_spec.rb
+++ /dev/null
@@ -1,68 +0,0 @@
-# frozen_string_literal: true
-
-require 'rails_helper'
-
-describe Pubsubhubbub::DeliveryWorker do
-  include RoutingHelper
-  subject { described_class.new }
-
-  let(:payload) { 'test' }
-
-  describe 'perform' do
-    it 'raises when subscription does not exist' do
-      expect { subject.perform 123, payload }.to raise_error(ActiveRecord::RecordNotFound)
-    end
-
-    it 'does not attempt to deliver when domain blocked' do
-      _domain_block = Fabricate(:domain_block, domain: 'example.com', severity: :suspend)
-      subscription = Fabricate(:subscription, callback_url: 'https://example.com/api', last_successful_delivery_at: 2.days.ago)
-
-      subject.perform(subscription.id, payload)
-
-      expect(subscription.reload.last_successful_delivery_at).to be_within(2).of(2.days.ago)
-    end
-
-    it 'raises when request fails' do
-      subscription = Fabricate(:subscription)
-
-      stub_request_to_respond_with(subscription, 500)
-      expect { subject.perform(subscription.id, payload) }.to raise_error Mastodon::UnexpectedResponseError
-    end
-
-    it 'updates subscriptions when delivery succeeds' do
-      subscription = Fabricate(:subscription)
-
-      stub_request_to_respond_with(subscription, 200)
-      subject.perform(subscription.id, payload)
-
-      expect(subscription.reload.last_successful_delivery_at).to be_within(2).of(Time.now.utc)
-    end
-
-    it 'updates subscription without a secret when delivery succeeds' do
-      subscription = Fabricate(:subscription, secret: nil)
-
-      stub_request_to_respond_with(subscription, 200)
-      subject.perform(subscription.id, payload)
-
-      expect(subscription.reload.last_successful_delivery_at).to be_within(2).of(Time.now.utc)
-    end
-
-    def stub_request_to_respond_with(subscription, code)
-      stub_request(:post, 'http://example.com/callback')
-        .with(body: payload, headers: expected_headers(subscription))
-        .to_return(status: code, body: '', headers: {})
-    end
-
-    def expected_headers(subscription)
-      {
-        'Connection' => 'close',
-        'Content-Type' => 'application/atom+xml',
-        'Host' => 'example.com',
-        'Link' => "<https://#{Rails.configuration.x.local_domain}/api/push>; rel=\"hub\", <https://#{Rails.configuration.x.local_domain}/users/#{subscription.account.username}.atom>; rel=\"self\"",
-      }.tap do |basic|
-        known_digest = OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new('sha1'), subscription.secret.to_s, payload)
-        basic.merge('X-Hub-Signature' => "sha1=#{known_digest}") if subscription.secret?
-      end
-    end
-  end
-end
diff --git a/spec/workers/pubsubhubbub/distribution_worker_spec.rb b/spec/workers/pubsubhubbub/distribution_worker_spec.rb
deleted file mode 100644
index 584485079..000000000
--- a/spec/workers/pubsubhubbub/distribution_worker_spec.rb
+++ /dev/null
@@ -1,46 +0,0 @@
-require 'rails_helper'
-
-describe Pubsubhubbub::DistributionWorker do
-  subject { Pubsubhubbub::DistributionWorker.new }
-
-  let!(:alice) { Fabricate(:account, username: 'alice') }
-  let!(:bob) { Fabricate(:account, username: 'bob', domain: 'example2.com') }
-  let!(:anonymous_subscription) { Fabricate(:subscription, account: alice, callback_url: 'http://example1.com', confirmed: true, lease_seconds: 3600) }
-  let!(:subscription_with_follower) { Fabricate(:subscription, account: alice, callback_url: 'http://example2.com', confirmed: true, lease_seconds: 3600) }
-
-  before do
-    bob.follow!(alice)
-  end
-
-  describe 'with public status' do
-    let(:status) { Fabricate(:status, account: alice, text: 'Hello', visibility: :public) }
-
-    it 'delivers payload to all subscriptions' do
-      allow(Pubsubhubbub::DeliveryWorker).to receive(:push_bulk)
-      subject.perform(status.stream_entry.id)
-      expect(Pubsubhubbub::DeliveryWorker).to have_received(:push_bulk).with([anonymous_subscription.id, subscription_with_follower.id])
-    end
-  end
-
-  context 'when OStatus privacy is not used' do
-    describe 'with private status' do
-      let(:status) { Fabricate(:status, account: alice, text: 'Hello', visibility: :private) }
-
-      it 'does not deliver anything' do
-        allow(Pubsubhubbub::DeliveryWorker).to receive(:push_bulk)
-        subject.perform(status.stream_entry.id)
-        expect(Pubsubhubbub::DeliveryWorker).to_not have_received(:push_bulk)
-      end
-    end
-
-    describe 'with direct status' do
-      let(:status) { Fabricate(:status, account: alice, text: 'Hello', visibility: :direct) }
-
-      it 'does not deliver payload' do
-        allow(Pubsubhubbub::DeliveryWorker).to receive(:push_bulk)
-        subject.perform(status.stream_entry.id)
-        expect(Pubsubhubbub::DeliveryWorker).to_not have_received(:push_bulk)
-      end
-    end
-  end
-end
diff --git a/spec/workers/scheduler/subscriptions_scheduler_spec.rb b/spec/workers/scheduler/subscriptions_scheduler_spec.rb
deleted file mode 100644
index a7d1046de..000000000
--- a/spec/workers/scheduler/subscriptions_scheduler_spec.rb
+++ /dev/null
@@ -1,19 +0,0 @@
-require 'rails_helper'
-
-describe Scheduler::SubscriptionsScheduler do
-  subject { Scheduler::SubscriptionsScheduler.new }
-
-  let!(:expiring_account1) { Fabricate(:account, subscription_expires_at: 20.minutes.from_now, domain: 'example.com', followers_count: 1, hub_url: 'http://hub.example.com') }
-  let!(:expiring_account2) { Fabricate(:account, subscription_expires_at: 4.hours.from_now, domain: 'example.org', followers_count: 1, hub_url: 'http://hub.example.org') }
-
-  before do
-    stub_request(:post, 'http://hub.example.com/').to_return(status: 202)
-    stub_request(:post, 'http://hub.example.org/').to_return(status: 202)
-  end
-
-  it 're-subscribes for all expiring accounts' do
-    subject.perform
-    expect(a_request(:post, 'http://hub.example.com/')).to have_been_made.once
-    expect(a_request(:post, 'http://hub.example.org/')).to have_been_made.once
-  end
-end