about summary refs log tree commit diff
path: root/spec
diff options
context:
space:
mode:
authorThibG <thib@sitedethib.com>2019-07-21 00:53:28 +0200
committerHolly 'Frinkel' Lotor <admin@frinkel.tech>2020-01-20 16:55:55 -0500
commitb0630ddc8261250c5edbf2907648695041649e98 (patch)
tree9ca374e4e98598efced200e4a993875245ab93b6 /spec
parent9447566b8ed8e532c13dd97ecd53681029fdcec0 (diff)
Original upstream merge
Diffstat (limited to 'spec')
-rw-r--r--spec/controllers/activitypub/inboxes_controller_spec.rb4
-rw-r--r--spec/controllers/api/oembed_controller_spec.rb2
-rw-r--r--spec/controllers/api/v1/follows_controller_spec.rb43
-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.rb89
-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/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/stream_entry_spec.rb192
-rw-r--r--spec/models/tag_spec.rb42
-rw-r--r--spec/requests/link_headers_spec.rb9
-rw-r--r--spec/services/fetch_resource_service_spec.rb (renamed from spec/services/fetch_atom_service_spec.rb)63
-rw-r--r--spec/services/import_service_spec.rb34
-rw-r--r--spec/services/resolve_account_service_spec.rb25
-rw-r--r--spec/services/resolve_url_service_spec.rb44
-rw-r--r--spec/services/suspend_account_service_spec.rb2
-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
31 files changed, 315 insertions, 743 deletions
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/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/v1/follows_controller_spec.rb b/spec/controllers/api/v1/follows_controller_spec.rb
deleted file mode 100644
index df335cc5d..000000000
--- a/spec/controllers/api/v1/follows_controller_spec.rb
+++ /dev/null
@@ -1,43 +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 '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 6c58ca559..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", <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 e2f3fe4cb..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', 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 46bbbe1f1..000000000
--- a/spec/controllers/stream_entries_controller_spec.rb
+++ /dev/null
@@ -1,89 +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 "<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
-  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/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 1fb63a3ed..917411ab5 100644
--- a/spec/models/account_spec.rb
+++ b/spec/models/account_spec.rb
@@ -431,7 +431,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
 
@@ -646,7 +646,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
 
@@ -681,7 +681,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
@@ -694,7 +694,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
 
@@ -705,14 +705,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/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..9a30ceaa5 100644
--- a/spec/models/tag_spec.rb
+++ b/spec/models/tag_spec.rb
@@ -31,7 +31,47 @@ 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
+
+    it 'does not match purely-numeric hashtags' do
+      expect(subject.match('hello #0123456')).to be_nil
     end
   end
 
diff --git a/spec/requests/link_headers_spec.rb b/spec/requests/link_headers_spec.rb
index bcd3d1da1..712ee262b 100644
--- a/spec/requests/link_headers_spec.rb
+++ b/spec/requests/link_headers_spec.rb
@@ -11,12 +11,19 @@ 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 activitypub url in link header' do
+      link_header = link_header_with_type('application/activity+json')
+
+      expect(link_header.href).to eq 'https://cb6e6126.ngrok.io/users/test'
+      expect(link_header.attr_pairs.first).to eq %w(rel alternate)
+    end
+
     def link_header_with_type(type)
       response.headers['Link'].links.find do |link|
         link.attr_pairs.any? { |pair| pair == ['type', type] }
diff --git a/spec/services/fetch_atom_service_spec.rb b/spec/services/fetch_resource_service_spec.rb
index 32c284243..839ad024c 100644
--- a/spec/services/fetch_atom_service_spec.rb
+++ b/spec/services/fetch_resource_service_spec.rb
@@ -1,57 +1,64 @@
 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 activity+json' do
@@ -61,7 +68,7 @@ RSpec.describe FetchAtomService, type: :service do
         it { is_expected.to eq [1, { prefetched_body: body, id: true }] }
       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 }
 
@@ -69,17 +76,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 }] }
       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/import_service_spec.rb b/spec/services/import_service_spec.rb
index 35a5dbc51..c63907c1f 100644
--- a/spec/services/import_service_spec.rb
+++ b/spec/services/import_service_spec.rb
@@ -3,10 +3,10 @@ require 'rails_helper'
 RSpec.describe ImportService, type: :service do
   let!(:account) { Fabricate(:account, locked: false) }
   let!(:bob)     { Fabricate(:account, username: 'bob', locked: false) }
-  let!(:foo)     { Fabricate(:account, username: 'foo', domain: 'ap.example.com', inbox_url: 'https://ap.example.com/inbox', locked: false) }
+  let!(:eve)     { Fabricate(:account, username: 'eve', domain: 'example.com', locked: false, inbox_url: 'https://example.com/inbox') }
 
   before do
-    stub_request(:post, "https://ap.example.com/inbox").to_return(:status => 200, :body => "", :headers => {})
+    stub_request(:post, "https://example.com/inbox").to_return(status: 200)
   end
 
   context 'import old-style list of muted users' do
@@ -96,6 +96,10 @@ RSpec.describe ImportService, type: :service do
       it 'follows the listed accounts, including boosts' do
         subject.call(import)
         expect(account.following.count).to eq 1
+<<<<<<< HEAD
+=======
+        expect(account.follow_requests.count).to eq 1
+>>>>>>> f1597e1ab... Merge pull request #1158 from ThibG/glitch-soc/merge-upstream
         expect(Follow.find_by(account: account, target_account: bob).show_reblogs).to be true
         expect(FollowRequest.find_by(account: account, target_account: foo)).to_not be_nil
       end
@@ -108,6 +112,10 @@ RSpec.describe ImportService, type: :service do
         account.follow!(bob, reblogs: false)
         subject.call(import)
         expect(account.following.count).to eq 1
+<<<<<<< HEAD
+=======
+        expect(account.follow_requests.count).to eq 1
+>>>>>>> f1597e1ab... Merge pull request #1158 from ThibG/glitch-soc/merge-upstream
         expect(Follow.find_by(account: account, target_account: bob).show_reblogs).to be true
         expect(FollowRequest.find_by(account: account, target_account: foo)).to_not be_nil
       end
@@ -120,6 +128,10 @@ RSpec.describe ImportService, type: :service do
         account.follow!(bob, reblogs: false)
         subject.call(import)
         expect(account.following.count).to eq 1
+<<<<<<< HEAD
+=======
+        expect(account.follow_requests.count).to eq 1
+>>>>>>> f1597e1ab... Merge pull request #1158 from ThibG/glitch-soc/merge-upstream
         expect(Follow.find_by(account: account, target_account: bob).show_reblogs).to be true
         expect(FollowRequest.find_by(account: account, target_account: foo)).to_not be_nil
       end
@@ -136,8 +148,14 @@ RSpec.describe ImportService, type: :service do
       it 'follows the listed accounts, respecting boosts' do
         subject.call(import)
         expect(account.following.count).to eq 1
+<<<<<<< HEAD
         expect(Follow.find_by(account: account, target_account: bob).show_reblogs).to be true
         expect(FollowRequest.find_by(account: account, target_account: foo)).to_not be_nil
+=======
+        expect(account.follow_requests.count).to eq 1
+        expect(Follow.find_by(account: account, target_account: bob).show_reblogs).to be true
+        expect(FollowRequest.find_by(account: account, target_account: eve).show_reblogs).to be false
+>>>>>>> f1597e1ab... Merge pull request #1158 from ThibG/glitch-soc/merge-upstream
       end
     end
 
@@ -148,8 +166,14 @@ RSpec.describe ImportService, type: :service do
         account.follow!(bob, reblogs: true)
         subject.call(import)
         expect(account.following.count).to eq 1
+<<<<<<< HEAD
         expect(Follow.find_by(account: account, target_account: bob).show_reblogs).to be true
         expect(FollowRequest.find_by(account: account, target_account: foo)).to_not be_nil
+=======
+        expect(account.follow_requests.count).to eq 1
+        expect(Follow.find_by(account: account, target_account: bob).show_reblogs).to be true
+        expect(FollowRequest.find_by(account: account, target_account: eve).show_reblogs).to be false
+>>>>>>> f1597e1ab... Merge pull request #1158 from ThibG/glitch-soc/merge-upstream
       end
     end
 
@@ -160,8 +184,14 @@ RSpec.describe ImportService, type: :service do
         account.follow!(bob, reblogs: true)
         subject.call(import)
         expect(account.following.count).to eq 1
+<<<<<<< HEAD
         expect(Follow.find_by(account: account, target_account: bob).show_reblogs).to be true
         expect(FollowRequest.find_by(account: account, target_account: foo)).to_not be_nil
+=======
+        expect(account.follow_requests.count).to eq 1
+        expect(Follow.find_by(account: account, target_account: bob).show_reblogs).to be true
+        expect(FollowRequest.find_by(account: account, target_account: eve).show_reblogs).to be false
+>>>>>>> f1597e1ab... Merge pull request #1158 from ThibG/glitch-soc/merge-upstream
       end
     end
   end
diff --git a/spec/services/resolve_account_service_spec.rb b/spec/services/resolve_account_service_spec.rb
index 709ec4497..9ecf3039f 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'))
@@ -33,15 +27,6 @@ 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 ActivityPub account' do
     it 'returns new remote account' do
       account = subject.call('foo@ap.example.com')
@@ -68,11 +53,17 @@ 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@ap.example.com')
         rescue ActiveRecord::RecordNotUnique
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/suspend_account_service_spec.rb b/spec/services/suspend_account_service_spec.rb
index 98a224b04..80379829c 100644
--- a/spec/services/suspend_account_service_spec.rb
+++ b/spec/services/suspend_account_service_spec.rb
@@ -26,7 +26,6 @@ RSpec.describe SuspendAccountService, type: :service do
         [
           account.statuses,
           account.media_attachments,
-          account.stream_entries,
           account.notifications,
           account.favourites,
           account.active_relationships,
@@ -67,7 +66,6 @@ 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,
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