about summary refs log tree commit diff
path: root/spec
diff options
context:
space:
mode:
Diffstat (limited to 'spec')
-rw-r--r--spec/controllers/about_controller_spec.rb36
-rw-r--r--spec/controllers/account_follow_controller_spec.rb64
-rw-r--r--spec/controllers/account_unfollow_controller_spec.rb64
-rw-r--r--spec/controllers/accounts_controller_spec.rb196
-rw-r--r--spec/controllers/activitypub/collections_controller_spec.rb2
-rw-r--r--spec/controllers/activitypub/followers_synchronizations_controller_spec.rb2
-rw-r--r--spec/controllers/activitypub/inboxes_controller_spec.rb2
-rw-r--r--spec/controllers/activitypub/outboxes_controller_spec.rb2
-rw-r--r--spec/controllers/activitypub/replies_controller_spec.rb2
-rw-r--r--spec/controllers/admin/action_logs_controller_spec.rb13
-rw-r--r--spec/controllers/admin/settings/branding_controller_spec.rb53
-rw-r--r--spec/controllers/admin/settings_controller_spec.rb71
-rw-r--r--spec/controllers/api/v1/accounts_controller_spec.rb11
-rw-r--r--spec/controllers/api/v1/admin/account_actions_controller_spec.rb44
-rw-r--r--spec/controllers/api/v2/search_controller_spec.rb62
-rw-r--r--spec/controllers/authorize_interactions_controller_spec.rb4
-rw-r--r--spec/controllers/concerns/signature_verification_spec.rb45
-rw-r--r--spec/controllers/follower_accounts_controller_spec.rb21
-rw-r--r--spec/controllers/following_accounts_controller_spec.rb21
-rw-r--r--spec/controllers/home_controller_spec.rb20
-rw-r--r--spec/controllers/remote_follow_controller_spec.rb135
-rw-r--r--spec/controllers/remote_interaction_controller_spec.rb39
-rw-r--r--spec/controllers/settings/deletes_controller_spec.rb14
-rw-r--r--spec/controllers/settings/exports/following_accounts_controller_spec.rb2
-rw-r--r--spec/controllers/statuses_controller_spec.rb2
-rw-r--r--spec/controllers/tags_controller_spec.rb8
-rw-r--r--spec/fabricators/access_grant_fabricator.rb6
-rw-r--r--spec/fabricators/account_fabricator.rb1
-rw-r--r--spec/fabricators/assets/utah_teapot.pngbin248232 -> 195600 bytes
-rw-r--r--spec/fabricators/preview_card_fabricator.rb1
-rw-r--r--spec/features/profile_spec.rb26
-rw-r--r--spec/fixtures/files/emojo.pngbin29814 -> 29283 bytes
-rw-r--r--spec/helpers/admin/action_log_helper_spec.rb28
-rw-r--r--spec/lib/activitypub/activity/announce_spec.rb2
-rw-r--r--spec/lib/activitypub/dereferencer_spec.rb8
-rw-r--r--spec/lib/activitypub/linked_data_signature_spec.rb14
-rw-r--r--spec/lib/feed_manager_spec.rb12
-rw-r--r--spec/lib/permalink_redirector_spec.rb31
-rw-r--r--spec/lib/request_spec.rb2
-rw-r--r--spec/lib/status_cache_hydrator_spec.rb147
-rw-r--r--spec/lib/vacuum/access_tokens_vacuum_spec.rb33
-rw-r--r--spec/lib/vacuum/backups_vacuum_spec.rb24
-rw-r--r--spec/lib/vacuum/feeds_vacuum_spec.rb30
-rw-r--r--spec/lib/vacuum/media_attachments_vacuum_spec.rb47
-rw-r--r--spec/lib/vacuum/preview_cards_vacuum_spec.rb32
-rw-r--r--spec/lib/vacuum/statuses_vacuum_spec.rb36
-rw-r--r--spec/lib/vacuum/system_keys_vacuum_spec.rb22
-rw-r--r--spec/lib/webfinger_resource_spec.rb2
-rw-r--r--spec/mailers/notification_mailer_spec.rb31
-rw-r--r--spec/mailers/previews/admin_mailer_preview.rb2
-rw-r--r--spec/models/account/field_spec.rb138
-rw-r--r--spec/models/account_spec.rb8
-rw-r--r--spec/models/concerns/account_interactions_spec.rb6
-rw-r--r--spec/models/export_spec.rb4
-rw-r--r--spec/models/follow_request_spec.rb2
-rw-r--r--spec/models/media_attachment_spec.rb6
-rw-r--r--spec/models/preview_card_trend_spec.rb4
-rw-r--r--spec/models/status_spec.rb16
-rw-r--r--spec/models/status_trend_spec.rb4
-rw-r--r--spec/models/trends/statuses_spec.rb14
-rw-r--r--spec/policies/status_policy_spec.rb22
-rw-r--r--spec/presenters/instance_presenter_spec.rb45
-rw-r--r--spec/requests/account_show_page_spec.rb15
-rw-r--r--spec/requests/localization_spec.rb12
-rw-r--r--spec/routing/accounts_routing_spec.rb88
-rw-r--r--spec/services/account_search_service_spec.rb1
-rw-r--r--spec/services/activitypub/fetch_featured_collection_service_spec.rb2
-rw-r--r--spec/services/activitypub/fetch_featured_tags_collection_service_spec.rb95
-rw-r--r--spec/services/activitypub/fetch_remote_account_service_spec.rb52
-rw-r--r--spec/services/activitypub/fetch_remote_actor_service_spec.rb180
-rw-r--r--spec/services/activitypub/fetch_remote_key_service_spec.rb83
-rw-r--r--spec/services/activitypub/process_collection_service_spec.rb6
-rw-r--r--spec/services/fetch_resource_service_spec.rb2
-rw-r--r--spec/services/follow_service_spec.rb13
-rw-r--r--spec/services/process_mentions_service_spec.rb100
-rw-r--r--spec/services/report_service_spec.rb37
-rw-r--r--spec/services/resolve_account_service_spec.rb4
-rw-r--r--spec/views/about/show.html.haml_spec.rb46
-rw-r--r--spec/views/statuses/show.html.haml_spec.rb49
-rw-r--r--spec/workers/digest_mailer_worker_spec.rb36
-rw-r--r--spec/workers/move_worker_spec.rb15
-rw-r--r--spec/workers/refollow_worker_spec.rb4
-rw-r--r--spec/workers/scheduler/feed_cleanup_scheduler_spec.rb26
-rw-r--r--spec/workers/scheduler/media_cleanup_scheduler_spec.rb15
84 files changed, 1447 insertions, 1175 deletions
diff --git a/spec/controllers/about_controller_spec.rb b/spec/controllers/about_controller_spec.rb
index 03dddd8c1..97143ec43 100644
--- a/spec/controllers/about_controller_spec.rb
+++ b/spec/controllers/about_controller_spec.rb
@@ -8,44 +8,8 @@ RSpec.describe AboutController, type: :controller do
       get :show
     end
 
-    it 'assigns @instance_presenter' do
-      expect(assigns(:instance_presenter)).to be_kind_of InstancePresenter
-    end
-
-    it 'returns http success' do
-      expect(response).to have_http_status(200)
-    end
-  end
-
-  describe 'GET #more' do
-    before do
-      get :more
-    end
-
-    it 'assigns @instance_presenter' do
-      expect(assigns(:instance_presenter)).to be_kind_of InstancePresenter
-    end
-
     it 'returns http success' do
       expect(response).to have_http_status(200)
     end
   end
-
-  describe 'GET #terms' do
-    before do
-      get :terms
-    end
-
-    it 'returns http success' do
-      expect(response).to have_http_status(200)
-    end
-  end
-
-  describe 'helper_method :new_user' do
-    it 'returns a new User' do
-      user = @controller.view_context.new_user
-      expect(user).to be_kind_of User
-      expect(user.account).to be_kind_of Account
-    end
-  end
 end
diff --git a/spec/controllers/account_follow_controller_spec.rb b/spec/controllers/account_follow_controller_spec.rb
deleted file mode 100644
index d33cd0499..000000000
--- a/spec/controllers/account_follow_controller_spec.rb
+++ /dev/null
@@ -1,64 +0,0 @@
-require 'rails_helper'
-
-describe AccountFollowController do
-  render_views
-
-  let(:user) { Fabricate(:user) }
-  let(:alice) { Fabricate(:account, username: 'alice') }
-
-  describe 'POST #create' do
-    let(:service) { double }
-
-    subject { post :create, params: { account_username: alice.username } }
-
-    before do
-      allow(FollowService).to receive(:new).and_return(service)
-      allow(service).to receive(:call)
-    end
-
-    context 'when account is permanently suspended' do
-      before do
-        alice.suspend!
-        alice.deletion_request.destroy
-        subject
-      end
-
-      it 'returns http gone' do
-        expect(response).to have_http_status(410)
-      end
-    end
-
-    context 'when account is temporarily suspended' do
-      before do
-        alice.suspend!
-        subject
-      end
-
-      it 'returns http forbidden' do
-        expect(response).to have_http_status(403)
-      end
-    end
-
-    context 'when signed out' do
-      before do
-        subject
-      end
-
-      it 'does not follow' do
-        expect(FollowService).not_to receive(:new)
-      end
-    end
-
-    context 'when signed in' do
-      before do
-        sign_in(user)
-        subject
-      end
-
-      it 'redirects to account path' do
-        expect(service).to have_received(:call).with(user.account, alice, with_rate_limit: true)
-        expect(response).to redirect_to(account_path(alice))
-      end
-    end
-  end
-end
diff --git a/spec/controllers/account_unfollow_controller_spec.rb b/spec/controllers/account_unfollow_controller_spec.rb
deleted file mode 100644
index a11f7aa68..000000000
--- a/spec/controllers/account_unfollow_controller_spec.rb
+++ /dev/null
@@ -1,64 +0,0 @@
-require 'rails_helper'
-
-describe AccountUnfollowController do
-  render_views
-
-  let(:user) { Fabricate(:user) }
-  let(:alice) { Fabricate(:account, username: 'alice') }
-
-  describe 'POST #create' do
-    let(:service) { double }
-
-    subject { post :create, params: { account_username: alice.username } }
-
-    before do
-      allow(UnfollowService).to receive(:new).and_return(service)
-      allow(service).to receive(:call)
-    end
-
-    context 'when account is permanently suspended' do
-      before do
-        alice.suspend!
-        alice.deletion_request.destroy
-        subject
-      end
-
-      it 'returns http gone' do
-        expect(response).to have_http_status(410)
-      end
-    end
-
-    context 'when account is temporarily suspended' do
-      before do
-        alice.suspend!
-        subject
-      end
-
-      it 'returns http forbidden' do
-        expect(response).to have_http_status(403)
-      end
-    end
-
-    context 'when signed out' do
-      before do
-        subject
-      end
-
-      it 'does not unfollow' do
-        expect(UnfollowService).not_to receive(:new)
-      end
-    end
-
-    context 'when signed in' do
-      before do
-        sign_in(user)
-        subject
-      end
-
-      it 'redirects to account path' do
-        expect(service).to have_received(:call).with(user.account, alice)
-        expect(response).to redirect_to(account_path(alice))
-      end
-    end
-  end
-end
diff --git a/spec/controllers/accounts_controller_spec.rb b/spec/controllers/accounts_controller_spec.rb
index 662a89927..defa8b2d3 100644
--- a/spec/controllers/accounts_controller_spec.rb
+++ b/spec/controllers/accounts_controller_spec.rb
@@ -99,100 +99,6 @@ RSpec.describe AccountsController, type: :controller do
         end
 
         it_behaves_like 'common response characteristics'
-
-        it 'renders public status' do
-          expect(response.body).to include(ActivityPub::TagManager.instance.url_for(status))
-        end
-
-        it 'renders self-reply' do
-          expect(response.body).to include(ActivityPub::TagManager.instance.url_for(status_self_reply))
-        end
-
-        it 'renders status with media' do
-          expect(response.body).to include(ActivityPub::TagManager.instance.url_for(status_media))
-        end
-
-        it 'renders reblog' do
-          expect(response.body).to include(ActivityPub::TagManager.instance.url_for(status_reblog.reblog))
-        end
-
-        it 'renders pinned status' do
-          expect(response.body).to include(I18n.t('stream_entries.pinned'))
-        end
-
-        it 'does not render private status' do
-          expect(response.body).to_not include(ActivityPub::TagManager.instance.url_for(status_private))
-        end
-
-        it 'does not render direct status' do
-          expect(response.body).to_not include(ActivityPub::TagManager.instance.url_for(status_direct))
-        end
-
-        it 'does not render reply to someone else' do
-          expect(response.body).to_not include(ActivityPub::TagManager.instance.url_for(status_reply))
-        end
-      end
-
-      context 'when signed-in' do
-        let(:user) { Fabricate(:user) }
-
-        before do
-          sign_in(user)
-        end
-
-        context 'when user follows account' do
-          before do
-            user.account.follow!(account)
-            get :show, params: { username: account.username, format: format }
-          end
-
-          it 'does not render private status' do
-            expect(response.body).to_not include(ActivityPub::TagManager.instance.url_for(status_private))
-          end
-        end
-
-        context 'when user is blocked' do
-          before do
-            account.block!(user.account)
-            get :show, params: { username: account.username, format: format }
-          end
-
-          it 'renders unavailable message' do
-            expect(response.body).to include(I18n.t('accounts.unavailable'))
-          end
-
-          it 'does not render public status' do
-            expect(response.body).to_not include(ActivityPub::TagManager.instance.url_for(status))
-          end
-
-          it 'does not render self-reply' do
-            expect(response.body).to_not include(ActivityPub::TagManager.instance.url_for(status_self_reply))
-          end
-
-          it 'does not render status with media' do
-            expect(response.body).to_not include(ActivityPub::TagManager.instance.url_for(status_media))
-          end
-
-          it 'does not render reblog' do
-            expect(response.body).to_not include(ActivityPub::TagManager.instance.url_for(status_reblog.reblog))
-          end
-
-          it 'does not render pinned status' do
-            expect(response.body).to_not include(I18n.t('stream_entries.pinned'))
-          end
-
-          it 'does not render private status' do
-            expect(response.body).to_not include(ActivityPub::TagManager.instance.url_for(status_private))
-          end
-
-          it 'does not render direct status' do
-            expect(response.body).to_not include(ActivityPub::TagManager.instance.url_for(status_direct))
-          end
-
-          it 'does not render reply to someone else' do
-            expect(response.body).to_not include(ActivityPub::TagManager.instance.url_for(status_reply))
-          end
-        end
       end
 
       context 'with replies' do
@@ -202,38 +108,6 @@ RSpec.describe AccountsController, type: :controller do
         end
 
         it_behaves_like 'common response characteristics'
-
-        it 'renders public status' do
-          expect(response.body).to include(ActivityPub::TagManager.instance.url_for(status))
-        end
-
-        it 'renders self-reply' do
-          expect(response.body).to include(ActivityPub::TagManager.instance.url_for(status_self_reply))
-        end
-
-        it 'renders status with media' do
-          expect(response.body).to include(ActivityPub::TagManager.instance.url_for(status_media))
-        end
-
-        it 'renders reblog' do
-          expect(response.body).to include(ActivityPub::TagManager.instance.url_for(status_reblog.reblog))
-        end
-
-        it 'does not render pinned status' do
-          expect(response.body).to_not include(I18n.t('stream_entries.pinned'))
-        end
-
-        it 'does not render private status' do
-          expect(response.body).to_not include(ActivityPub::TagManager.instance.url_for(status_private))
-        end
-
-        it 'does not render direct status' do
-          expect(response.body).to_not include(ActivityPub::TagManager.instance.url_for(status_direct))
-        end
-
-        it 'renders reply to someone else' do
-          expect(response.body).to include(ActivityPub::TagManager.instance.url_for(status_reply))
-        end
       end
 
       context 'with media' do
@@ -243,38 +117,6 @@ RSpec.describe AccountsController, type: :controller do
         end
 
         it_behaves_like 'common response characteristics'
-
-        it 'does not render public status' do
-          expect(response.body).to_not include(ActivityPub::TagManager.instance.url_for(status))
-        end
-
-        it 'does not render self-reply' do
-          expect(response.body).to_not include(ActivityPub::TagManager.instance.url_for(status_self_reply))
-        end
-
-        it 'renders status with media' do
-          expect(response.body).to include(ActivityPub::TagManager.instance.url_for(status_media))
-        end
-
-        it 'does not render reblog' do
-          expect(response.body).to_not include(ActivityPub::TagManager.instance.url_for(status_reblog.reblog))
-        end
-
-        it 'does not render pinned status' do
-          expect(response.body).to_not include(I18n.t('stream_entries.pinned'))
-        end
-
-        it 'does not render private status' do
-          expect(response.body).to_not include(ActivityPub::TagManager.instance.url_for(status_private))
-        end
-
-        it 'does not render direct status' do
-          expect(response.body).to_not include(ActivityPub::TagManager.instance.url_for(status_direct))
-        end
-
-        it 'does not render reply to someone else' do
-          expect(response.body).to_not include(ActivityPub::TagManager.instance.url_for(status_reply))
-        end
       end
 
       context 'with tag' do
@@ -289,42 +131,6 @@ RSpec.describe AccountsController, type: :controller do
         end
 
         it_behaves_like 'common response characteristics'
-
-        it 'does not render public status' do
-          expect(response.body).to_not include(ActivityPub::TagManager.instance.url_for(status))
-        end
-
-        it 'does not render self-reply' do
-          expect(response.body).to_not include(ActivityPub::TagManager.instance.url_for(status_self_reply))
-        end
-
-        it 'does not render status with media' do
-          expect(response.body).to_not include(ActivityPub::TagManager.instance.url_for(status_media))
-        end
-
-        it 'does not render reblog' do
-          expect(response.body).to_not include(ActivityPub::TagManager.instance.url_for(status_reblog.reblog))
-        end
-
-        it 'does not render pinned status' do
-          expect(response.body).to_not include(I18n.t('stream_entries.pinned'))
-        end
-
-        it 'does not render private status' do
-          expect(response.body).to_not include(ActivityPub::TagManager.instance.url_for(status_private))
-        end
-
-        it 'does not render direct status' do
-          expect(response.body).to_not include(ActivityPub::TagManager.instance.url_for(status_direct))
-        end
-
-        it 'does not render reply to someone else' do
-          expect(response.body).to_not include(ActivityPub::TagManager.instance.url_for(status_reply))
-        end
-
-        it 'renders status with tag' do
-          expect(response.body).to include(ActivityPub::TagManager.instance.url_for(status_tag))
-        end
       end
     end
 
@@ -420,7 +226,7 @@ RSpec.describe AccountsController, type: :controller do
         let(:remote_account) { Fabricate(:account, domain: 'example.com') }
 
         before do
-          allow(controller).to receive(:signed_request_account).and_return(remote_account)
+          allow(controller).to receive(:signed_request_actor).and_return(remote_account)
           get :show, params: { username: account.username, format: format }
         end
 
diff --git a/spec/controllers/activitypub/collections_controller_spec.rb b/spec/controllers/activitypub/collections_controller_spec.rb
index 4d87f80ce..f78d9abbf 100644
--- a/spec/controllers/activitypub/collections_controller_spec.rb
+++ b/spec/controllers/activitypub/collections_controller_spec.rb
@@ -24,7 +24,7 @@ RSpec.describe ActivityPub::CollectionsController, type: :controller do
   end
 
   before do
-    allow(controller).to receive(:signed_request_account).and_return(remote_account)
+    allow(controller).to receive(:signed_request_actor).and_return(remote_account)
 
     Fabricate(:status_pin, account: account)
     Fabricate(:status_pin, account: account)
diff --git a/spec/controllers/activitypub/followers_synchronizations_controller_spec.rb b/spec/controllers/activitypub/followers_synchronizations_controller_spec.rb
index e233bd560..c19bb8cae 100644
--- a/spec/controllers/activitypub/followers_synchronizations_controller_spec.rb
+++ b/spec/controllers/activitypub/followers_synchronizations_controller_spec.rb
@@ -15,7 +15,7 @@ RSpec.describe ActivityPub::FollowersSynchronizationsController, type: :controll
   end
 
   before do
-    allow(controller).to receive(:signed_request_account).and_return(remote_account)
+    allow(controller).to receive(:signed_request_actor).and_return(remote_account)
   end
 
   describe 'GET #show' do
diff --git a/spec/controllers/activitypub/inboxes_controller_spec.rb b/spec/controllers/activitypub/inboxes_controller_spec.rb
index 973ad83bb..2f023197b 100644
--- a/spec/controllers/activitypub/inboxes_controller_spec.rb
+++ b/spec/controllers/activitypub/inboxes_controller_spec.rb
@@ -6,7 +6,7 @@ RSpec.describe ActivityPub::InboxesController, type: :controller do
   let(:remote_account) { nil }
 
   before do
-    allow(controller).to receive(:signed_request_account).and_return(remote_account)
+    allow(controller).to receive(:signed_request_actor).and_return(remote_account)
   end
 
   describe 'POST #create' do
diff --git a/spec/controllers/activitypub/outboxes_controller_spec.rb b/spec/controllers/activitypub/outboxes_controller_spec.rb
index 04f036447..74bf46a5e 100644
--- a/spec/controllers/activitypub/outboxes_controller_spec.rb
+++ b/spec/controllers/activitypub/outboxes_controller_spec.rb
@@ -28,7 +28,7 @@ RSpec.describe ActivityPub::OutboxesController, type: :controller do
   end
 
   before do
-    allow(controller).to receive(:signed_request_account).and_return(remote_account)
+    allow(controller).to receive(:signed_request_actor).and_return(remote_account)
   end
 
   describe 'GET #show' do
diff --git a/spec/controllers/activitypub/replies_controller_spec.rb b/spec/controllers/activitypub/replies_controller_spec.rb
index a35957f24..aee1a8b1a 100644
--- a/spec/controllers/activitypub/replies_controller_spec.rb
+++ b/spec/controllers/activitypub/replies_controller_spec.rb
@@ -168,7 +168,7 @@ RSpec.describe ActivityPub::RepliesController, type: :controller do
 
   before do
     stub_const 'ActivityPub::RepliesController::DESCENDANTS_LIMIT', 5
-    allow(controller).to receive(:signed_request_account).and_return(remote_querier)
+    allow(controller).to receive(:signed_request_actor).and_return(remote_querier)
 
     Fabricate(:status, thread: status, visibility: :public)
     Fabricate(:status, thread: status, visibility: :public)
diff --git a/spec/controllers/admin/action_logs_controller_spec.rb b/spec/controllers/admin/action_logs_controller_spec.rb
index c1957258f..7cd8cdf46 100644
--- a/spec/controllers/admin/action_logs_controller_spec.rb
+++ b/spec/controllers/admin/action_logs_controller_spec.rb
@@ -3,6 +3,19 @@
 require 'rails_helper'
 
 describe Admin::ActionLogsController, type: :controller do
+  render_views
+
+  # Action logs typically cause issues when their targets are not in the database
+  let!(:account) { Fabricate(:account) }
+
+  let!(:orphaned_logs) do
+    %w(
+      Account User UserRole Report DomainBlock DomainAllow
+      EmailDomainBlock UnavailableDomain Status AccountWarning
+      Announcement IpBlock Instance CustomEmoji CanonicalEmailBlock Appeal
+    ).map { |type| Admin::ActionLog.new(account: account, action: 'destroy', target_type: type, target_id: 1312).save! }
+  end
+
   describe 'GET #index' do
     it 'returns 200' do
       sign_in Fabricate(:user, role: UserRole.find_by(name: 'Admin'))
diff --git a/spec/controllers/admin/settings/branding_controller_spec.rb b/spec/controllers/admin/settings/branding_controller_spec.rb
new file mode 100644
index 000000000..ee1c441bc
--- /dev/null
+++ b/spec/controllers/admin/settings/branding_controller_spec.rb
@@ -0,0 +1,53 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+RSpec.describe Admin::Settings::BrandingController, type: :controller do
+  render_views
+
+  describe 'When signed in as an admin' do
+    before do
+      sign_in Fabricate(:user, role: UserRole.find_by(name: 'Admin')), scope: :user
+    end
+
+    describe 'GET #show' do
+      it 'returns http success' do
+        get :show
+
+        expect(response).to have_http_status(200)
+      end
+    end
+
+    describe 'PUT #update' do
+      before do
+        allow_any_instance_of(Form::AdminSettings).to receive(:valid?).and_return(true)
+      end
+
+      around do |example|
+        before = Setting.site_short_description
+        Setting.site_short_description = nil
+        example.run
+        Setting.site_short_description = before
+        Setting.new_setting_key = nil
+      end
+
+      it 'cannot create a setting value for a non-admin key' do
+        expect(Setting.new_setting_key).to be_blank
+
+        patch :update, params: { form_admin_settings: { new_setting_key: 'New key value' } }
+
+        expect(response).to redirect_to(admin_settings_branding_path)
+        expect(Setting.new_setting_key).to be_nil
+      end
+
+      it 'creates a settings value that didnt exist before for eligible key' do
+        expect(Setting.site_short_description).to be_blank
+
+        patch :update, params: { form_admin_settings: { site_short_description: 'New key value' } }
+
+        expect(response).to redirect_to(admin_settings_branding_path)
+        expect(Setting.site_short_description).to eq 'New key value'
+      end
+    end
+  end
+end
diff --git a/spec/controllers/admin/settings_controller_spec.rb b/spec/controllers/admin/settings_controller_spec.rb
deleted file mode 100644
index 46749f76c..000000000
--- a/spec/controllers/admin/settings_controller_spec.rb
+++ /dev/null
@@ -1,71 +0,0 @@
-# frozen_string_literal: true
-
-require 'rails_helper'
-
-RSpec.describe Admin::SettingsController, type: :controller do
-  render_views
-
-  describe 'When signed in as an admin' do
-    before do
-      sign_in Fabricate(:user, role: UserRole.find_by(name: 'Admin')), scope: :user
-    end
-
-    describe 'GET #edit' do
-      it 'returns http success' do
-        get :edit
-
-        expect(response).to have_http_status(200)
-      end
-    end
-
-    describe 'PUT #update' do
-      before do
-        allow_any_instance_of(Form::AdminSettings).to receive(:valid?).and_return(true)
-      end
-
-      describe 'for a record that doesnt exist' do
-        around do |example|
-          before = Setting.site_extended_description
-          Setting.site_extended_description = nil
-          example.run
-          Setting.site_extended_description = before
-          Setting.new_setting_key = nil
-        end
-
-        it 'cannot create a setting value for a non-admin key' do
-          expect(Setting.new_setting_key).to be_blank
-
-          patch :update, params: { form_admin_settings: { new_setting_key: 'New key value' } }
-
-          expect(response).to redirect_to(edit_admin_settings_path)
-          expect(Setting.new_setting_key).to be_nil
-        end
-
-        it 'creates a settings value that didnt exist before for eligible key' do
-          expect(Setting.site_extended_description).to be_blank
-
-          patch :update, params: { form_admin_settings: { site_extended_description: 'New key value' } }
-
-          expect(response).to redirect_to(edit_admin_settings_path)
-          expect(Setting.site_extended_description).to eq 'New key value'
-        end
-      end
-
-      context do
-        around do |example|
-          site_title = Setting.site_title
-          example.run
-          Setting.site_title = site_title
-        end
-
-        it 'updates a settings value' do
-          Setting.site_title = 'Original'
-          patch :update, params: { form_admin_settings: { site_title: 'New title' } }
-
-          expect(response).to redirect_to(edit_admin_settings_path)
-          expect(Setting.site_title).to eq 'New title'
-        end
-      end
-    end
-  end
-end
diff --git a/spec/controllers/api/v1/accounts_controller_spec.rb b/spec/controllers/api/v1/accounts_controller_spec.rb
index 5d5c245c5..d6bbcefd7 100644
--- a/spec/controllers/api/v1/accounts_controller_spec.rb
+++ b/spec/controllers/api/v1/accounts_controller_spec.rb
@@ -145,6 +145,17 @@ RSpec.describe Api::V1::AccountsController, type: :controller do
         expect(json[:showing_reblogs]).to be false
         expect(json[:notifying]).to be true
       end
+
+      it 'changes languages option' do
+        post :follow, params: { id: other_account.id, languages: %w(en es) }
+
+        json = body_as_json
+
+        expect(json[:following]).to be true
+        expect(json[:showing_reblogs]).to be false
+        expect(json[:notifying]).to be false
+        expect(json[:languages]).to match_array %w(en es)
+      end
     end
   end
 
diff --git a/spec/controllers/api/v1/admin/account_actions_controller_spec.rb b/spec/controllers/api/v1/admin/account_actions_controller_spec.rb
index 199395f55..462c2cfa9 100644
--- a/spec/controllers/api/v1/admin/account_actions_controller_spec.rb
+++ b/spec/controllers/api/v1/admin/account_actions_controller_spec.rb
@@ -30,28 +30,40 @@ RSpec.describe Api::V1::Admin::AccountActionsController, type: :controller do
   end
 
   describe 'POST #create' do
-    before do
-      post :create, params: { account_id: account.id, type: 'disable' }
-    end
+    context do
+      before do
+        post :create, params: { account_id: account.id, type: 'disable' }
+      end
 
-    it_behaves_like 'forbidden for wrong scope', 'write:statuses'
-    it_behaves_like 'forbidden for wrong role', ''
+      it_behaves_like 'forbidden for wrong scope', 'write:statuses'
+      it_behaves_like 'forbidden for wrong role', ''
 
-    it 'returns http success' do
-      expect(response).to have_http_status(200)
-    end
+      it 'returns http success' do
+        expect(response).to have_http_status(200)
+      end
+
+      it 'performs action against account' do
+        expect(account.reload.user_disabled?).to be true
+      end
+
+      it 'logs action' do
+        log_item = Admin::ActionLog.last
 
-    it 'performs action against account' do
-      expect(account.reload.user_disabled?).to be true
+        expect(log_item).to_not be_nil
+        expect(log_item.action).to eq :disable
+        expect(log_item.account_id).to eq user.account_id
+        expect(log_item.target_id).to eq account.user.id
+      end
     end
 
-    it 'logs action' do
-      log_item = Admin::ActionLog.last
+    context 'with no type' do
+      before do
+        post :create, params: { account_id: account.id }
+      end
 
-      expect(log_item).to_not be_nil
-      expect(log_item.action).to eq :disable
-      expect(log_item.account_id).to eq user.account_id
-      expect(log_item.target_id).to eq account.user.id
+      it 'returns http unprocessable entity' do
+        expect(response).to have_http_status(422)
+      end
     end
   end
 end
diff --git a/spec/controllers/api/v2/search_controller_spec.rb b/spec/controllers/api/v2/search_controller_spec.rb
index fa20e1e51..d417ea58c 100644
--- a/spec/controllers/api/v2/search_controller_spec.rb
+++ b/spec/controllers/api/v2/search_controller_spec.rb
@@ -5,18 +5,64 @@ require 'rails_helper'
 RSpec.describe Api::V2::SearchController, type: :controller do
   render_views
 
-  let(:user)  { Fabricate(:user) }
-  let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'read:search') }
+  context 'with token' do
+    let(:user)  { Fabricate(:user) }
+    let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'read:search') }
 
-  before do
-    allow(controller).to receive(:doorkeeper_token) { token }
+    before do
+      allow(controller).to receive(:doorkeeper_token) { token }
+    end
+
+    describe 'GET #index' do
+      before do
+        get :index, params: { q: 'test' }
+      end
+
+      it 'returns http success' do
+        expect(response).to have_http_status(200)
+      end
+    end
   end
 
-  describe 'GET #index' do
-    it 'returns http success' do
-      get :index, params: { q: 'test' }
+  context 'without token' do
+    describe 'GET #index' do
+      let(:search_params) {}
+
+      before do
+        get :index, params: search_params
+      end
+
+      context 'with a `q` shorter than 5 characters' do
+        let(:search_params) { { q: 'test' } }
+
+        it 'returns http success' do
+          expect(response).to have_http_status(200)
+        end
+      end
+
+      context 'with a `q` equal to or longer than 5 characters' do
+        let(:search_params) { { q: 'test1' } }
+
+        it 'returns http success' do
+          expect(response).to have_http_status(200)
+        end
+
+        context 'with truthy `resolve`' do
+          let(:search_params) { { q: 'test1', resolve: '1' } }
+
+          it 'returns http unauthorized' do
+            expect(response).to have_http_status(401)
+          end
+        end
+
+        context 'with `offset`' do
+          let(:search_params) { { q: 'test1', offset: 1 } }
 
-      expect(response).to have_http_status(200)
+          it 'returns http unauthorized' do
+            expect(response).to have_http_status(401)
+          end
+        end
+      end
     end
   end
 end
diff --git a/spec/controllers/authorize_interactions_controller_spec.rb b/spec/controllers/authorize_interactions_controller_spec.rb
index 99f3f6ffc..44f52df69 100644
--- a/spec/controllers/authorize_interactions_controller_spec.rb
+++ b/spec/controllers/authorize_interactions_controller_spec.rb
@@ -39,7 +39,7 @@ describe AuthorizeInteractionsController do
       end
 
       it 'sets resource from url' do
-        account = Account.new
+        account = Fabricate(:account)
         service = double
         allow(ResolveURLService).to receive(:new).and_return(service)
         allow(service).to receive(:call).with('http://example.com').and_return(account)
@@ -51,7 +51,7 @@ describe AuthorizeInteractionsController do
       end
 
       it 'sets resource from acct uri' do
-        account = Account.new
+        account = Fabricate(:account)
         service = double
         allow(ResolveAccountService).to receive(:new).and_return(service)
         allow(service).to receive(:call).with('found@hostname').and_return(account)
diff --git a/spec/controllers/concerns/signature_verification_spec.rb b/spec/controllers/concerns/signature_verification_spec.rb
index 05fb1445b..6e73643b4 100644
--- a/spec/controllers/concerns/signature_verification_spec.rb
+++ b/spec/controllers/concerns/signature_verification_spec.rb
@@ -3,6 +3,16 @@
 require 'rails_helper'
 
 describe ApplicationController, type: :controller do
+  class WrappedActor
+    attr_reader :wrapped_account
+
+    def initialize(wrapped_account)
+      @wrapped_account = wrapped_account
+    end
+
+    delegate :uri, :keypair, to: :wrapped_account
+  end
+
   controller do
     include SignatureVerification
 
@@ -73,6 +83,41 @@ describe ApplicationController, type: :controller do
       end
     end
 
+    context 'with a valid actor that is not an Account' do
+      let(:actor) { WrappedActor.new(author) }
+
+      before do
+        get :success
+
+        fake_request = Request.new(:get, request.url)
+        fake_request.on_behalf_of(author)
+
+        request.headers.merge!(fake_request.headers)
+
+        allow(ActivityPub::TagManager.instance).to receive(:uri_to_actor).with(anything) do
+          actor
+        end
+      end
+
+      describe '#signed_request?' do
+        it 'returns true' do
+          expect(controller.signed_request?).to be true
+        end
+      end
+
+      describe '#signed_request_account' do
+        it 'returns nil' do
+          expect(controller.signed_request_account).to be_nil
+        end
+      end
+
+      describe '#signed_request_actor' do
+        it 'returns the expected actor' do
+          expect(controller.signed_request_actor).to eq actor
+        end
+      end
+    end
+
     context 'with request older than a day' do
       before do
         get :success
diff --git a/spec/controllers/follower_accounts_controller_spec.rb b/spec/controllers/follower_accounts_controller_spec.rb
index 4d2a6e01a..ab2e82e85 100644
--- a/spec/controllers/follower_accounts_controller_spec.rb
+++ b/spec/controllers/follower_accounts_controller_spec.rb
@@ -34,27 +34,6 @@ describe FollowerAccountsController do
           expect(response).to have_http_status(403)
         end
       end
-
-      it 'assigns follows' do
-        expect(response).to have_http_status(200)
-
-        assigned = assigns(:follows).to_a
-        expect(assigned.size).to eq 2
-        expect(assigned[0]).to eq follow1
-        expect(assigned[1]).to eq follow0
-      end
-
-      it 'does not assign blocked users' do
-        user = Fabricate(:user)
-        user.account.block!(follower0)
-        sign_in(user)
-
-        expect(response).to have_http_status(200)
-
-        assigned = assigns(:follows).to_a
-        expect(assigned.size).to eq 1
-        expect(assigned[0]).to eq follow1
-      end
     end
 
     context 'when format is json' do
diff --git a/spec/controllers/following_accounts_controller_spec.rb b/spec/controllers/following_accounts_controller_spec.rb
index bb6d221ca..e43dbf882 100644
--- a/spec/controllers/following_accounts_controller_spec.rb
+++ b/spec/controllers/following_accounts_controller_spec.rb
@@ -34,27 +34,6 @@ describe FollowingAccountsController do
           expect(response).to have_http_status(403)
         end
       end
-
-      it 'assigns follows' do
-        expect(response).to have_http_status(200)
-
-        assigned = assigns(:follows).to_a
-        expect(assigned.size).to eq 2
-        expect(assigned[0]).to eq follow1
-        expect(assigned[1]).to eq follow0
-      end
-
-      it 'does not assign blocked users' do
-        user = Fabricate(:user)
-        user.account.block!(followee0)
-        sign_in(user)
-
-        expect(response).to have_http_status(200)
-
-        assigned = assigns(:follows).to_a
-        expect(assigned.size).to eq 1
-        expect(assigned[0]).to eq follow1
-      end
     end
 
     context 'when format is json' do
diff --git a/spec/controllers/home_controller_spec.rb b/spec/controllers/home_controller_spec.rb
index 70c5c42c5..d845ae01d 100644
--- a/spec/controllers/home_controller_spec.rb
+++ b/spec/controllers/home_controller_spec.rb
@@ -7,27 +7,21 @@ RSpec.describe HomeController, type: :controller do
     subject { get :index }
 
     context 'when not signed in' do
-      context 'when requested path is tag timeline' do
-        it 'redirects to the tag\'s permalink' do
-          @request.path = '/web/timelines/tag/name'
-          is_expected.to redirect_to '/tags/name'
-        end
-      end
-
-      it 'redirects to about page' do
+      it 'returns http success' do
         @request.path = '/'
-        is_expected.to redirect_to(about_path)
+        is_expected.to have_http_status(:success)
       end
     end
 
     context 'when signed in' do
       let(:user) { Fabricate(:user) }
 
-      before { sign_in(user) }
+      before do
+        sign_in(user)
+      end
 
-      it 'assigns @body_classes' do
-        subject
-        expect(assigns(:body_classes)).to eq 'app-body'
+      it 'returns http success' do
+        is_expected.to have_http_status(:success)
       end
     end
   end
diff --git a/spec/controllers/remote_follow_controller_spec.rb b/spec/controllers/remote_follow_controller_spec.rb
deleted file mode 100644
index 01d43f48c..000000000
--- a/spec/controllers/remote_follow_controller_spec.rb
+++ /dev/null
@@ -1,135 +0,0 @@
-# frozen_string_literal: true
-
-require 'rails_helper'
-
-describe RemoteFollowController do
-  render_views
-
-  describe '#new' do
-    it 'returns success when session is empty' do
-      account = Fabricate(:account)
-      get :new, params: { account_username: account.to_param }
-
-      expect(response).to have_http_status(200)
-      expect(response).to render_template(:new)
-      expect(assigns(:remote_follow).acct).to be_nil
-    end
-
-    it 'populates the remote follow with session data when session exists' do
-      session[:remote_follow] = 'user@example.com'
-      account = Fabricate(:account)
-      get :new, params: { account_username: account.to_param }
-
-      expect(response).to have_http_status(200)
-      expect(response).to render_template(:new)
-      expect(assigns(:remote_follow).acct).to eq 'user@example.com'
-    end
-  end
-
-  describe '#create' do
-    before do
-      @account = Fabricate(:account, username: 'test_user')
-    end
-
-    context 'with a valid acct' do
-      context 'when webfinger values are wrong' do
-        it 'renders new when redirect url is nil' do
-          resource_with_nil_link = double(link: nil)
-          allow_any_instance_of(WebfingerHelper).to receive(:webfinger!).with('acct:user@example.com').and_return(resource_with_nil_link)
-          post :create, params: { account_username: @account.to_param, remote_follow: { acct: 'user@example.com' } }
-
-          expect(response).to render_template(:new)
-          expect(response.body).to include(I18n.t('remote_follow.missing_resource'))
-        end
-
-        it 'renders new when template is nil' do
-          resource_with_link = double(link: nil)
-          allow_any_instance_of(WebfingerHelper).to receive(:webfinger!).with('acct:user@example.com').and_return(resource_with_link)
-          post :create, params: { account_username: @account.to_param, remote_follow: { acct: 'user@example.com' } }
-
-          expect(response).to render_template(:new)
-          expect(response.body).to include(I18n.t('remote_follow.missing_resource'))
-        end
-      end
-
-      context 'when webfinger values are good' do
-        before do
-          resource_with_link = double(link: 'http://example.com/follow_me?acct={uri}')
-          allow_any_instance_of(WebfingerHelper).to receive(:webfinger!).with('acct:user@example.com').and_return(resource_with_link)
-          post :create, params: { account_username: @account.to_param, remote_follow: { acct: 'user@example.com' } }
-        end
-
-        it 'saves the session' do
-          expect(session[:remote_follow]).to eq 'user@example.com'
-        end
-
-        it 'redirects to the remote location' do
-          expect(response).to redirect_to("http://example.com/follow_me?acct=https%3A%2F%2F#{Rails.configuration.x.local_domain}%2Fusers%2Ftest_user")
-        end
-      end
-    end
-
-    context 'with an invalid acct' do
-      it 'renders new when acct is missing' do
-        post :create, params: { account_username: @account.to_param, remote_follow: { acct: '' } }
-
-        expect(response).to render_template(:new)
-      end
-
-      it 'renders new with error when webfinger fails' do
-        allow_any_instance_of(WebfingerHelper).to receive(:webfinger!).with('acct:user@example.com').and_raise(Webfinger::Error)
-        post :create, params: { account_username: @account.to_param, remote_follow: { acct: 'user@example.com' } }
-
-        expect(response).to render_template(:new)
-        expect(response.body).to include(I18n.t('remote_follow.missing_resource'))
-      end
-
-      it 'renders new when occur HTTP::ConnectionError' do
-        allow_any_instance_of(WebfingerHelper).to receive(:webfinger!).with('acct:user@unknown').and_raise(HTTP::ConnectionError)
-        post :create, params: { account_username: @account.to_param, remote_follow: { acct: 'user@unknown' } }
-
-        expect(response).to render_template(:new)
-        expect(response.body).to include(I18n.t('remote_follow.missing_resource'))
-      end
-    end
-  end
-
-  context 'with a permanently suspended account' do
-    before do
-      @account = Fabricate(:account)
-      @account.suspend!
-      @account.deletion_request.destroy
-    end
-
-    it 'returns http gone on GET to #new' do
-      get :new, params: { account_username: @account.to_param }
-
-      expect(response).to have_http_status(410)
-    end
-
-    it 'returns http gone on POST to #create' do
-      post :create, params: { account_username: @account.to_param }
-
-      expect(response).to have_http_status(410)
-    end
-  end
-
-  context 'with a temporarily suspended account' do
-    before do
-      @account = Fabricate(:account)
-      @account.suspend!
-    end
-
-    it 'returns http forbidden on GET to #new' do
-      get :new, params: { account_username: @account.to_param }
-
-      expect(response).to have_http_status(403)
-    end
-
-    it 'returns http forbidden on POST to #create' do
-      post :create, params: { account_username: @account.to_param }
-
-      expect(response).to have_http_status(403)
-    end
-  end
-end
diff --git a/spec/controllers/remote_interaction_controller_spec.rb b/spec/controllers/remote_interaction_controller_spec.rb
deleted file mode 100644
index bb0074b11..000000000
--- a/spec/controllers/remote_interaction_controller_spec.rb
+++ /dev/null
@@ -1,39 +0,0 @@
-# frozen_string_literal: true
-
-require 'rails_helper'
-
-describe RemoteInteractionController, type: :controller do
-  render_views
-
-  let(:status) { Fabricate(:status) }
-
-  describe 'GET #new' do
-    it 'returns 200' do
-      get :new, params: { id: status.id }
-      expect(response).to have_http_status(200)
-    end
-  end
-
-  describe 'POST #create' do
-    context '@remote_follow is valid' do
-      it 'returns 302' do
-        allow_any_instance_of(RemoteFollow).to receive(:valid?) { true }
-        allow_any_instance_of(RemoteFollow).to receive(:addressable_template) do
-          Addressable::Template.new('https://hoge.com')
-        end
-
-        post :create, params: { id: status.id, remote_follow: { acct: '@hoge' } }
-        expect(response).to have_http_status(302)
-      end
-    end
-
-    context '@remote_follow is invalid' do
-      it 'returns 200' do
-        allow_any_instance_of(RemoteFollow).to receive(:valid?) { false }
-        post :create, params: { id: status.id, remote_follow: { acct: '@hoge' } }
-
-        expect(response).to have_http_status(200)
-      end
-    end
-  end
-end
diff --git a/spec/controllers/settings/deletes_controller_spec.rb b/spec/controllers/settings/deletes_controller_spec.rb
index cd36ecc35..a94dc042a 100644
--- a/spec/controllers/settings/deletes_controller_spec.rb
+++ b/spec/controllers/settings/deletes_controller_spec.rb
@@ -81,20 +81,6 @@ describe Settings::DeletesController do
           expect(response).to redirect_to settings_delete_path
         end
       end
-
-      context 'when account deletions are disabled' do
-        around do |example|
-          open_deletion = Setting.open_deletion
-          example.run
-          Setting.open_deletion = open_deletion
-        end
-
-        it 'redirects' do
-          Setting.open_deletion = false
-          delete :destroy
-          expect(response).to redirect_to root_path
-        end
-      end
     end
 
     context 'when not signed in' do
diff --git a/spec/controllers/settings/exports/following_accounts_controller_spec.rb b/spec/controllers/settings/exports/following_accounts_controller_spec.rb
index 78858e772..bfe010555 100644
--- a/spec/controllers/settings/exports/following_accounts_controller_spec.rb
+++ b/spec/controllers/settings/exports/following_accounts_controller_spec.rb
@@ -11,7 +11,7 @@ describe Settings::Exports::FollowingAccountsController do
       sign_in user, scope: :user
       get :index, format: :csv
 
-      expect(response.body).to eq "Account address,Show boosts\nusername@domain,true\n"
+      expect(response.body).to eq "Account address,Show boosts,Notify on new posts,Languages\nusername@domain,true,false,\n"
     end
   end
 end
diff --git a/spec/controllers/statuses_controller_spec.rb b/spec/controllers/statuses_controller_spec.rb
index 05fae67fa..6ed5d4bbb 100644
--- a/spec/controllers/statuses_controller_spec.rb
+++ b/spec/controllers/statuses_controller_spec.rb
@@ -426,7 +426,7 @@ describe StatusesController do
       let(:remote_account) { Fabricate(:account, domain: 'example.com') }
 
       before do
-        allow(controller).to receive(:signed_request_account).and_return(remote_account)
+        allow(controller).to receive(:signed_request_actor).and_return(remote_account)
       end
 
       context 'when account blocks account' do
diff --git a/spec/controllers/tags_controller_spec.rb b/spec/controllers/tags_controller_spec.rb
index 69def90cf..547bcfb39 100644
--- a/spec/controllers/tags_controller_spec.rb
+++ b/spec/controllers/tags_controller_spec.rb
@@ -14,17 +14,11 @@ RSpec.describe TagsController, type: :controller do
         get :show, params: { id: 'test', max_id: late.id }
         expect(response).to have_http_status(200)
       end
-
-      it 'renders application layout' do
-        get :show, params: { id: 'test', max_id: late.id }
-        expect(response).to render_template layout: 'public'
-      end
     end
 
     context 'when tag does not exist' do
-      it 'returns http missing for non-existent tag' do
+      it 'returns http not found' do
         get :show, params: { id: 'none' }
-
         expect(response).to have_http_status(404)
       end
     end
diff --git a/spec/fabricators/access_grant_fabricator.rb b/spec/fabricators/access_grant_fabricator.rb
new file mode 100644
index 000000000..ae1945f2b
--- /dev/null
+++ b/spec/fabricators/access_grant_fabricator.rb
@@ -0,0 +1,6 @@
+Fabricator :access_grant, from: 'Doorkeeper::AccessGrant' do
+  application
+  resource_owner_id { Fabricate(:user).id }
+  expires_in 3_600
+  redirect_uri { Doorkeeper.configuration.native_redirect_uri }
+end
diff --git a/spec/fabricators/account_fabricator.rb b/spec/fabricators/account_fabricator.rb
index f1cce281c..205706532 100644
--- a/spec/fabricators/account_fabricator.rb
+++ b/spec/fabricators/account_fabricator.rb
@@ -11,4 +11,5 @@ Fabricator(:account) do
   suspended_at        { |attrs| attrs[:suspended] ? Time.now.utc : nil }
   silenced_at         { |attrs| attrs[:silenced] ? Time.now.utc : nil }
   user                { |attrs| attrs[:domain].nil? ? Fabricate.build(:user, account: nil) : nil }
+  discoverable        true
 end
diff --git a/spec/fabricators/assets/utah_teapot.png b/spec/fabricators/assets/utah_teapot.png
index 6708361e5..ccf202de4 100644
--- a/spec/fabricators/assets/utah_teapot.png
+++ b/spec/fabricators/assets/utah_teapot.png
Binary files differdiff --git a/spec/fabricators/preview_card_fabricator.rb b/spec/fabricators/preview_card_fabricator.rb
index f119c117d..99b5edc43 100644
--- a/spec/fabricators/preview_card_fabricator.rb
+++ b/spec/fabricators/preview_card_fabricator.rb
@@ -3,4 +3,5 @@ Fabricator(:preview_card) do
   title { Faker::Lorem.sentence }
   description { Faker::Lorem.paragraph }
   type 'link'
+  image { attachment_fixture('attachment.jpg') }
 end
diff --git a/spec/features/profile_spec.rb b/spec/features/profile_spec.rb
index b6de3e9d1..ec4f9a53f 100644
--- a/spec/features/profile_spec.rb
+++ b/spec/features/profile_spec.rb
@@ -18,36 +18,16 @@ feature 'Profile' do
     visit account_path('alice')
 
     is_expected.to have_title("alice (@alice@#{local_domain})")
-
-    within('.public-account-header h1') do
-      is_expected.to have_content("alice @alice@#{local_domain}")
-    end
-
-    bio_elem = first('.public-account-bio')
-    expect(bio_elem).to have_content(alice_bio)
-    # The bio has hashtags made clickable
-    expect(bio_elem).to have_link('cryptology')
-    expect(bio_elem).to have_link('science')
-    # Nicknames are make clickable
-    expect(bio_elem).to have_link('@alice')
-    expect(bio_elem).to have_link('@bob')
-    # Nicknames not on server are not clickable
-    expect(bio_elem).not_to have_link('@pepe')
   end
 
   scenario 'I can change my account' do
     visit settings_profile_path
+
     fill_in 'Display name', with: 'Bob'
     fill_in 'Bio', with: 'Bob is silent'
-    first('.btn[type=submit]').click
-    is_expected.to have_content 'Changes successfully saved!'
 
-    # View my own public profile and see the changes
-    click_link "Bob @bob@#{local_domain}"
+    first('button[type=submit]').click
 
-    within('.public-account-header h1') do
-      is_expected.to have_content("Bob @bob@#{local_domain}")
-    end
-    expect(first('.public-account-bio')).to have_content('Bob is silent')
+    is_expected.to have_content 'Changes successfully saved!'
   end
 end
diff --git a/spec/fixtures/files/emojo.png b/spec/fixtures/files/emojo.png
index cb5993499..6ef0a5fbc 100644
--- a/spec/fixtures/files/emojo.png
+++ b/spec/fixtures/files/emojo.png
Binary files differdiff --git a/spec/helpers/admin/action_log_helper_spec.rb b/spec/helpers/admin/action_log_helper_spec.rb
index 60f5ecdcc..9d7ed4ab7 100644
--- a/spec/helpers/admin/action_log_helper_spec.rb
+++ b/spec/helpers/admin/action_log_helper_spec.rb
@@ -3,32 +3,4 @@
 require 'rails_helper'
 
 RSpec.describe Admin::ActionLogsHelper, type: :helper do
-  klass = Class.new do
-    include ActionView::Helpers
-    include Admin::ActionLogsHelper
-  end
-
-  let(:hoge) { klass.new }
-
-  describe '#log_target' do
-    after do
-      hoge.log_target(log)
-    end
-
-    context 'log.target' do
-      let(:log) { double(target: true) }
-
-      it 'calls linkable_log_target' do
-        expect(hoge).to receive(:linkable_log_target).with(log.target)
-      end
-    end
-
-    context '!log.target' do
-      let(:log) { double(target: false, target_type: :type, recorded_changes: :change) }
-
-      it 'calls log_target_from_history' do
-        expect(hoge).to receive(:log_target_from_history).with(log.target_type, log.recorded_changes)
-      end
-    end
-  end
 end
diff --git a/spec/lib/activitypub/activity/announce_spec.rb b/spec/lib/activitypub/activity/announce_spec.rb
index 41806b258..e9cd6c68c 100644
--- a/spec/lib/activitypub/activity/announce_spec.rb
+++ b/spec/lib/activitypub/activity/announce_spec.rb
@@ -115,7 +115,7 @@ RSpec.describe ActivityPub::Activity::Announce do
 
       let(:object_json) { 'https://example.com/actor/hello-world' }
 
-      subject { described_class.new(json, sender, relayed_through_account: relay_account) }
+      subject { described_class.new(json, sender, relayed_through_actor: relay_account) }
 
       before do
         stub_request(:get, 'https://example.com/actor/hello-world').to_return(body: Oj.dump(unknown_object_json))
diff --git a/spec/lib/activitypub/dereferencer_spec.rb b/spec/lib/activitypub/dereferencer_spec.rb
index ce30513d7..e50b497c7 100644
--- a/spec/lib/activitypub/dereferencer_spec.rb
+++ b/spec/lib/activitypub/dereferencer_spec.rb
@@ -4,10 +4,10 @@ RSpec.describe ActivityPub::Dereferencer do
   describe '#object' do
     let(:object) { { '@context': 'https://www.w3.org/ns/activitystreams', id: 'https://example.com/foo', type: 'Note', content: 'Hoge' } }
     let(:permitted_origin) { 'https://example.com' }
-    let(:signature_account) { nil }
+    let(:signature_actor) { nil }
     let(:uri) { nil }
 
-    subject { described_class.new(uri, permitted_origin: permitted_origin, signature_account: signature_account).object }
+    subject { described_class.new(uri, permitted_origin: permitted_origin, signature_actor: signature_actor).object }
 
     before do
       stub_request(:get, 'https://example.com/foo').to_return(body: Oj.dump(object), headers: { 'Content-Type' => 'application/activity+json' })
@@ -21,7 +21,7 @@ RSpec.describe ActivityPub::Dereferencer do
       end
 
       context 'with signature account' do
-        let(:signature_account) { Fabricate(:account) }
+        let(:signature_actor) { Fabricate(:account) }
 
         it 'makes signed request' do
           subject
@@ -52,7 +52,7 @@ RSpec.describe ActivityPub::Dereferencer do
       end
 
       context 'with signature account' do
-        let(:signature_account) { Fabricate(:account) }
+        let(:signature_actor) { Fabricate(:account) }
 
         it 'makes signed request' do
           subject
diff --git a/spec/lib/activitypub/linked_data_signature_spec.rb b/spec/lib/activitypub/linked_data_signature_spec.rb
index 2222c46fb..d55a7c7fa 100644
--- a/spec/lib/activitypub/linked_data_signature_spec.rb
+++ b/spec/lib/activitypub/linked_data_signature_spec.rb
@@ -20,7 +20,7 @@ RSpec.describe ActivityPub::LinkedDataSignature do
     stub_jsonld_contexts!
   end
 
-  describe '#verify_account!' do
+  describe '#verify_actor!' do
     context 'when signature matches' do
       let(:raw_signature) do
         {
@@ -32,7 +32,7 @@ RSpec.describe ActivityPub::LinkedDataSignature do
       let(:signature) { raw_signature.merge('type' => 'RsaSignature2017', 'signatureValue' => sign(sender, raw_signature, raw_json)) }
 
       it 'returns creator' do
-        expect(subject.verify_account!).to eq sender
+        expect(subject.verify_actor!).to eq sender
       end
     end
 
@@ -40,7 +40,7 @@ RSpec.describe ActivityPub::LinkedDataSignature do
       let(:signature) { nil }
 
       it 'returns nil' do
-        expect(subject.verify_account!).to be_nil
+        expect(subject.verify_actor!).to be_nil
       end
     end
 
@@ -55,7 +55,7 @@ RSpec.describe ActivityPub::LinkedDataSignature do
       let(:signature) { raw_signature.merge('type' => 'RsaSignature2017', 'signatureValue' => 's69F3mfddd99dGjmvjdjjs81e12jn121Gkm1') }
 
       it 'returns nil' do
-        expect(subject.verify_account!).to be_nil
+        expect(subject.verify_actor!).to be_nil
       end
     end
   end
@@ -73,14 +73,14 @@ RSpec.describe ActivityPub::LinkedDataSignature do
     end
 
     it 'can be verified again' do
-      expect(described_class.new(subject).verify_account!).to eq sender
+      expect(described_class.new(subject).verify_actor!).to eq sender
     end
   end
 
-  def sign(from_account, options, document)
+  def sign(from_actor, options, document)
     options_hash   = Digest::SHA256.hexdigest(canonicalize(options.merge('@context' => ActivityPub::LinkedDataSignature::CONTEXT)))
     document_hash  = Digest::SHA256.hexdigest(canonicalize(document))
     to_be_verified = options_hash + document_hash
-    Base64.strict_encode64(from_account.keypair.sign(OpenSSL::Digest.new('SHA256'), to_be_verified))
+    Base64.strict_encode64(from_actor.keypair.sign(OpenSSL::Digest.new('SHA256'), to_be_verified))
   end
 end
diff --git a/spec/lib/feed_manager_spec.rb b/spec/lib/feed_manager_spec.rb
index 4a43ae7e5..2b11ddf70 100644
--- a/spec/lib/feed_manager_spec.rb
+++ b/spec/lib/feed_manager_spec.rb
@@ -134,6 +134,18 @@ RSpec.describe FeedManager do
         reblog = Fabricate(:status, reblog: status, account: jeff)
         expect(FeedManager.instance.filter?(:home, reblog, alice)).to be true
       end
+
+      it 'returns true for German post when follow is set to English only' do
+        alice.follow!(bob, languages: %w(en))
+        status = Fabricate(:status, text: 'Hallo Welt', account: bob, language: 'de')
+        expect(FeedManager.instance.filter?(:home, status, alice)).to be true
+      end
+
+      it 'returns false for German post when follow is set to German' do
+        alice.follow!(bob, languages: %w(de))
+        status = Fabricate(:status, text: 'Hallo Welt', account: bob, language: 'de')
+        expect(FeedManager.instance.filter?(:home, status, alice)).to be false
+      end
     end
 
     context 'for mentions feed' do
diff --git a/spec/lib/permalink_redirector_spec.rb b/spec/lib/permalink_redirector_spec.rb
index b916b33b2..a00913656 100644
--- a/spec/lib/permalink_redirector_spec.rb
+++ b/spec/lib/permalink_redirector_spec.rb
@@ -3,40 +3,31 @@
 require 'rails_helper'
 
 describe PermalinkRedirector do
+  let(:remote_account) { Fabricate(:account, username: 'alice', domain: 'example.com', url: 'https://example.com/@alice', id: 2) }
+
   describe '#redirect_url' do
     before do
-      account = Fabricate(:account, username: 'alice', id: 1)
-      Fabricate(:status, account: account, id: 123)
+      Fabricate(:status, account: remote_account, id: 123, url: 'https://example.com/status-123')
     end
 
     it 'returns path for legacy account links' do
-      redirector = described_class.new('web/accounts/1')
-      expect(redirector.redirect_path).to eq 'https://cb6e6126.ngrok.io/@alice'
+      redirector = described_class.new('accounts/2')
+      expect(redirector.redirect_path).to eq 'https://example.com/@alice'
     end
 
     it 'returns path for legacy status links' do
-      redirector = described_class.new('web/statuses/123')
-      expect(redirector.redirect_path).to eq 'https://cb6e6126.ngrok.io/@alice/123'
-    end
-
-    it 'returns path for legacy tag links' do
-      redirector = described_class.new('web/timelines/tag/hoge')
-      expect(redirector.redirect_path).to eq '/tags/hoge'
+      redirector = described_class.new('statuses/123')
+      expect(redirector.redirect_path).to eq 'https://example.com/status-123'
     end
 
     it 'returns path for pretty account links' do
-      redirector = described_class.new('web/@alice')
-      expect(redirector.redirect_path).to eq 'https://cb6e6126.ngrok.io/@alice'
+      redirector = described_class.new('@alice@example.com')
+      expect(redirector.redirect_path).to eq 'https://example.com/@alice'
     end
 
     it 'returns path for pretty status links' do
-      redirector = described_class.new('web/@alice/123')
-      expect(redirector.redirect_path).to eq 'https://cb6e6126.ngrok.io/@alice/123'
-    end
-
-    it 'returns path for pretty tag links' do
-      redirector = described_class.new('web/tags/hoge')
-      expect(redirector.redirect_path).to eq '/tags/hoge'
+      redirector = described_class.new('@alice/123')
+      expect(redirector.redirect_path).to eq 'https://example.com/status-123'
     end
   end
 end
diff --git a/spec/lib/request_spec.rb b/spec/lib/request_spec.rb
index 2d300f18d..5eccf3201 100644
--- a/spec/lib/request_spec.rb
+++ b/spec/lib/request_spec.rb
@@ -63,7 +63,7 @@ describe Request do
         expect(a_request(:get, 'http://example.com').with(headers: subject.headers)).to have_been_made
       end
 
-      it 'closes underlaying connection' do
+      it 'closes underlying connection' do
         expect_any_instance_of(HTTP::Client).to receive(:close)
         expect { |block| subject.perform &block }.to yield_control
       end
diff --git a/spec/lib/status_cache_hydrator_spec.rb b/spec/lib/status_cache_hydrator_spec.rb
new file mode 100644
index 000000000..5c78de711
--- /dev/null
+++ b/spec/lib/status_cache_hydrator_spec.rb
@@ -0,0 +1,147 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe StatusCacheHydrator do
+  let(:status)  { Fabricate(:status) }
+  let(:account) { Fabricate(:account) }
+
+  describe '#hydrate' do
+    let(:compare_to_hash) { InlineRenderer.render(status, account, :status) }
+
+    shared_examples 'shared behavior' do
+      context 'when handling a new status' do
+        let(:poll) { Fabricate(:poll) }
+        let(:status) { Fabricate(:status, poll: poll) }
+
+        it 'renders the same attributes as a full render' do
+          expect(subject).to eql(compare_to_hash)
+        end
+      end
+
+      context 'when handling a new status with own poll' do
+        let(:poll) { Fabricate(:poll, account: account) }
+        let(:status) { Fabricate(:status, poll: poll, account: account) }
+
+        it 'renders the same attributes as a full render' do
+          expect(subject).to eql(compare_to_hash)
+        end
+      end
+
+      context 'when handling a filtered status' do
+        let(:status) { Fabricate(:status, text: 'this toot is about that banned word') }
+
+        before do
+          account.custom_filters.create!(phrase: 'filter1', context: %w(home), action: :hide, keywords_attributes: [{ keyword: 'banned' }, { keyword: 'irrelevant' }])
+        end
+
+        it 'renders the same attributes as a full render' do
+          expect(subject).to eql(compare_to_hash)
+        end
+      end
+
+      context 'when handling a reblog' do
+        let(:reblog) { Fabricate(:status) }
+        let(:status) { Fabricate(:status, reblog: reblog) }
+
+        context 'that has been favourited' do
+          before do
+            FavouriteService.new.call(account, reblog)
+          end
+
+          it 'renders the same attributes as a full render' do
+            expect(subject).to eql(compare_to_hash)
+          end
+        end
+
+        context 'that has been reblogged' do
+          before do
+            ReblogService.new.call(account, reblog)
+          end
+
+          it 'renders the same attributes as a full render' do
+            expect(subject).to eql(compare_to_hash)
+          end
+        end
+
+        context 'that has been pinned' do
+          let(:reblog) { Fabricate(:status, account: account) }
+
+          before do
+            StatusPin.create!(account: account, status: reblog)
+          end
+
+          it 'renders the same attributes as a full render' do
+            expect(subject).to eql(compare_to_hash)
+          end
+        end
+
+        context 'that has been followed tags' do
+          let(:followed_tag) { Fabricate(:tag) }
+
+          before do
+            reblog.tags << Fabricate(:tag)
+            reblog.tags << followed_tag
+            TagFollow.create!(tag: followed_tag, account: account, rate_limit: false)
+          end
+
+          it 'renders the same attributes as a full render' do
+            expect(subject).to eql(compare_to_hash)
+          end
+        end
+
+        context 'that has a poll authored by the user' do
+          let(:poll) { Fabricate(:poll, account: account) }
+          let(:reblog) { Fabricate(:status, poll: poll, account: account) }
+
+          it 'renders the same attributes as a full render' do
+            expect(subject).to eql(compare_to_hash)
+          end
+        end
+
+        context 'that has been voted in' do
+          let(:poll) { Fabricate(:poll, options: %w(Yellow Blue)) }
+          let(:reblog) { Fabricate(:status, poll: poll) }
+
+          before do
+            VoteService.new.call(account, poll, [0])
+          end
+
+          it 'renders the same attributes as a full render' do
+            expect(subject).to eql(compare_to_hash)
+          end
+        end
+
+        context 'that matches account filters' do
+          let(:reblog) { Fabricate(:status, text: 'this toot is about that banned word') }
+
+          before do
+            account.custom_filters.create!(phrase: 'filter1', context: %w(home), action: :hide, keywords_attributes: [{ keyword: 'banned' }, { keyword: 'irrelevant' }])
+          end
+
+          it 'renders the same attributes as a full render' do
+            expect(subject).to eql(compare_to_hash)
+          end
+        end
+      end
+    end
+
+    context 'when cache is warm' do
+      subject do
+        Rails.cache.write("fan-out/#{status.id}", InlineRenderer.render(status, nil, :status))
+        described_class.new(status).hydrate(account.id)
+      end
+
+      it_behaves_like 'shared behavior'
+    end
+
+    context 'when cache is cold' do
+      subject do
+        Rails.cache.delete("fan-out/#{status.id}")
+        described_class.new(status).hydrate(account.id)
+      end
+
+      it_behaves_like 'shared behavior'
+    end
+  end
+end
diff --git a/spec/lib/vacuum/access_tokens_vacuum_spec.rb b/spec/lib/vacuum/access_tokens_vacuum_spec.rb
new file mode 100644
index 000000000..0244c3449
--- /dev/null
+++ b/spec/lib/vacuum/access_tokens_vacuum_spec.rb
@@ -0,0 +1,33 @@
+require 'rails_helper'
+
+RSpec.describe Vacuum::AccessTokensVacuum do
+  subject { described_class.new }
+
+  describe '#perform' do
+    let!(:revoked_access_token) { Fabricate(:access_token, revoked_at: 1.minute.ago) }
+    let!(:active_access_token) { Fabricate(:access_token) }
+
+    let!(:revoked_access_grant) { Fabricate(:access_grant, revoked_at: 1.minute.ago) }
+    let!(:active_access_grant) { Fabricate(:access_grant) }
+
+    before do
+      subject.perform
+    end
+
+    it 'deletes revoked access tokens' do
+      expect { revoked_access_token.reload }.to raise_error ActiveRecord::RecordNotFound
+    end
+
+    it 'deletes revoked access grants' do
+      expect { revoked_access_grant.reload }.to raise_error ActiveRecord::RecordNotFound
+    end
+
+    it 'does not delete active access tokens' do
+      expect { active_access_token.reload }.to_not raise_error
+    end
+
+    it 'does not delete active access grants' do
+      expect { active_access_grant.reload }.to_not raise_error
+    end
+  end
+end
diff --git a/spec/lib/vacuum/backups_vacuum_spec.rb b/spec/lib/vacuum/backups_vacuum_spec.rb
new file mode 100644
index 000000000..4e2de083f
--- /dev/null
+++ b/spec/lib/vacuum/backups_vacuum_spec.rb
@@ -0,0 +1,24 @@
+require 'rails_helper'
+
+RSpec.describe Vacuum::BackupsVacuum do
+  let(:retention_period) { 7.days }
+
+  subject { described_class.new(retention_period) }
+
+  describe '#perform' do
+    let!(:expired_backup) { Fabricate(:backup, created_at: (retention_period + 1.day).ago) }
+    let!(:current_backup) { Fabricate(:backup) }
+
+    before do
+      subject.perform
+    end
+
+    it 'deletes backups past the retention period' do
+      expect { expired_backup.reload }.to raise_error ActiveRecord::RecordNotFound
+    end
+
+    it 'does not delete backups within the retention period' do
+      expect { current_backup.reload }.to_not raise_error
+    end
+  end
+end
diff --git a/spec/lib/vacuum/feeds_vacuum_spec.rb b/spec/lib/vacuum/feeds_vacuum_spec.rb
new file mode 100644
index 000000000..0aec26740
--- /dev/null
+++ b/spec/lib/vacuum/feeds_vacuum_spec.rb
@@ -0,0 +1,30 @@
+require 'rails_helper'
+
+RSpec.describe Vacuum::FeedsVacuum do
+  subject { described_class.new }
+
+  describe '#perform' do
+    let!(:active_user) { Fabricate(:user, current_sign_in_at: 2.days.ago) }
+    let!(:inactive_user) { Fabricate(:user, current_sign_in_at: 22.days.ago) }
+
+    before do
+      redis.zadd(feed_key_for(inactive_user), 1, 1)
+      redis.zadd(feed_key_for(active_user), 1, 1)
+      redis.zadd(feed_key_for(inactive_user, 'reblogs'), 2, 2)
+      redis.sadd(feed_key_for(inactive_user, 'reblogs:2'), 3)
+
+      subject.perform
+    end
+
+    it 'clears feeds of inactive users and lists' do
+      expect(redis.zcard(feed_key_for(inactive_user))).to eq 0
+      expect(redis.zcard(feed_key_for(active_user))).to eq 1
+      expect(redis.exists?(feed_key_for(inactive_user, 'reblogs'))).to be false
+      expect(redis.exists?(feed_key_for(inactive_user, 'reblogs:2'))).to be false
+    end
+  end
+
+  def feed_key_for(user, subtype = nil)
+    FeedManager.instance.key(:home, user.account_id, subtype)
+  end
+end
diff --git a/spec/lib/vacuum/media_attachments_vacuum_spec.rb b/spec/lib/vacuum/media_attachments_vacuum_spec.rb
new file mode 100644
index 000000000..be8458d9b
--- /dev/null
+++ b/spec/lib/vacuum/media_attachments_vacuum_spec.rb
@@ -0,0 +1,47 @@
+require 'rails_helper'
+
+RSpec.describe Vacuum::MediaAttachmentsVacuum do
+  let(:retention_period) { 7.days }
+
+  subject { described_class.new(retention_period) }
+
+  let(:remote_status) { Fabricate(:status, account: Fabricate(:account, domain: 'example.com')) }
+  let(:local_status) { Fabricate(:status) }
+
+  describe '#perform' do
+    let!(:old_remote_media) { Fabricate(:media_attachment, remote_url: 'https://example.com/foo.png', status: remote_status, created_at: (retention_period + 1.day).ago, updated_at: (retention_period + 1.day).ago) }
+    let!(:old_local_media) { Fabricate(:media_attachment, status: local_status, created_at: (retention_period + 1.day).ago, updated_at: (retention_period + 1.day).ago) }
+    let!(:new_remote_media) { Fabricate(:media_attachment, remote_url: 'https://example.com/foo.png', status: remote_status) }
+    let!(:new_local_media) { Fabricate(:media_attachment, status: local_status) }
+    let!(:old_unattached_media) { Fabricate(:media_attachment, account_id: nil, created_at: 10.days.ago) }
+    let!(:new_unattached_media) { Fabricate(:media_attachment, account_id: nil, created_at: 1.hour.ago) }
+
+    before do
+      subject.perform
+    end
+
+    it 'deletes cache of remote media attachments past the retention period' do
+      expect(old_remote_media.reload.file).to be_blank
+    end
+
+    it 'does not touch local media attachments past the retention period' do
+      expect(old_local_media.reload.file).to_not be_blank
+    end
+
+    it 'does not delete cache of remote media attachments within the retention period' do
+      expect(new_remote_media.reload.file).to_not be_blank
+    end
+
+    it 'does not touch local media attachments within the retention period' do
+      expect(new_local_media.reload.file).to_not be_blank
+    end
+
+    it 'deletes unattached media attachments past TTL' do
+      expect { old_unattached_media.reload }.to raise_error(ActiveRecord::RecordNotFound)
+    end
+
+    it 'does not delete unattached media attachments within TTL' do
+      expect(new_unattached_media.reload).to be_persisted
+    end
+  end
+end
diff --git a/spec/lib/vacuum/preview_cards_vacuum_spec.rb b/spec/lib/vacuum/preview_cards_vacuum_spec.rb
new file mode 100644
index 000000000..275f9ba92
--- /dev/null
+++ b/spec/lib/vacuum/preview_cards_vacuum_spec.rb
@@ -0,0 +1,32 @@
+require 'rails_helper'
+
+RSpec.describe Vacuum::PreviewCardsVacuum do
+  let(:retention_period) { 7.days }
+
+  subject { described_class.new(retention_period) }
+
+  describe '#perform' do
+    let!(:orphaned_preview_card) { Fabricate(:preview_card, created_at: 2.days.ago) }
+    let!(:old_preview_card) { Fabricate(:preview_card, updated_at: (retention_period + 1.day).ago) }
+    let!(:new_preview_card) { Fabricate(:preview_card) }
+
+    before do
+      old_preview_card.statuses << Fabricate(:status)
+      new_preview_card.statuses << Fabricate(:status)
+
+      subject.perform
+    end
+
+    it 'deletes cache of preview cards last updated before the retention period' do
+      expect(old_preview_card.reload.image).to be_blank
+    end
+
+    it 'does not delete cache of preview cards last updated within the retention period' do
+      expect(new_preview_card.reload.image).to_not be_blank
+    end
+
+    it 'does not delete attached preview cards' do
+      expect(new_preview_card.reload).to be_persisted
+    end
+  end
+end
diff --git a/spec/lib/vacuum/statuses_vacuum_spec.rb b/spec/lib/vacuum/statuses_vacuum_spec.rb
new file mode 100644
index 000000000..83f3c5c9f
--- /dev/null
+++ b/spec/lib/vacuum/statuses_vacuum_spec.rb
@@ -0,0 +1,36 @@
+require 'rails_helper'
+
+RSpec.describe Vacuum::StatusesVacuum do
+  let(:retention_period) { 7.days }
+
+  let(:remote_account) { Fabricate(:account, domain: 'example.com') }
+
+  subject { described_class.new(retention_period) }
+
+  describe '#perform' do
+    let!(:remote_status_old) { Fabricate(:status, account: remote_account, created_at: (retention_period + 2.days).ago) }
+    let!(:remote_status_recent) { Fabricate(:status, account: remote_account, created_at: (retention_period - 2.days).ago) }
+    let!(:local_status_old) { Fabricate(:status, created_at: (retention_period + 2.days).ago) }
+    let!(:local_status_recent) { Fabricate(:status, created_at: (retention_period - 2.days).ago) }
+
+    before do
+      subject.perform
+    end
+
+    it 'deletes remote statuses past the retention period' do
+      expect { remote_status_old.reload }.to raise_error ActiveRecord::RecordNotFound
+    end
+
+    it 'does not delete local statuses past the retention period' do
+      expect { local_status_old.reload }.to_not raise_error
+    end
+
+    it 'does not delete remote statuses within the retention period' do
+      expect { remote_status_recent.reload }.to_not raise_error
+    end
+
+    it 'does not delete local statuses within the retention period' do
+      expect { local_status_recent.reload }.to_not raise_error
+    end
+  end
+end
diff --git a/spec/lib/vacuum/system_keys_vacuum_spec.rb b/spec/lib/vacuum/system_keys_vacuum_spec.rb
new file mode 100644
index 000000000..565892f02
--- /dev/null
+++ b/spec/lib/vacuum/system_keys_vacuum_spec.rb
@@ -0,0 +1,22 @@
+require 'rails_helper'
+
+RSpec.describe Vacuum::SystemKeysVacuum do
+  subject { described_class.new }
+
+  describe '#perform' do
+    let!(:expired_system_key) { Fabricate(:system_key, created_at: (SystemKey::ROTATION_PERIOD * 4).ago) }
+    let!(:current_system_key) { Fabricate(:system_key) }
+
+    before do
+      subject.perform
+    end
+
+    it 'deletes the expired key' do
+      expect { expired_system_key.reload }.to raise_error ActiveRecord::RecordNotFound
+    end
+
+    it 'does not delete the current key' do
+      expect { current_system_key.reload }.to_not raise_error
+    end
+  end
+end
diff --git a/spec/lib/webfinger_resource_spec.rb b/spec/lib/webfinger_resource_spec.rb
index 236e9f3e2..5c7f475d6 100644
--- a/spec/lib/webfinger_resource_spec.rb
+++ b/spec/lib/webfinger_resource_spec.rb
@@ -50,7 +50,7 @@ describe WebfingerResource do
       end
 
       it 'finds the username in a mixed case http route' do
-        resource = 'HTTp://exAMPLEe.com/users/alice'
+        resource = 'HTTp://exAMPLe.com/users/alice'
 
         result = WebfingerResource.new(resource).username
         expect(result).to eq 'alice'
diff --git a/spec/mailers/notification_mailer_spec.rb b/spec/mailers/notification_mailer_spec.rb
index 2ca4e26fa..29bdc349b 100644
--- a/spec/mailers/notification_mailer_spec.rb
+++ b/spec/mailers/notification_mailer_spec.rb
@@ -101,35 +101,4 @@ RSpec.describe NotificationMailer, type: :mailer do
       expect(mail.body.encoded).to match("bob has requested to follow you")
     end
   end
-
-  describe 'digest' do
-    before do
-      mention = Fabricate(:mention, account: receiver.account, status: foreign_status)
-      Fabricate(:notification, account: receiver.account, activity: mention)
-      sender.follow!(receiver.account)
-    end
-
-    context do
-      let!(:mail) { NotificationMailer.digest(receiver.account, since: 5.days.ago) }
-
-      include_examples 'localized subject', 'notification_mailer.digest.subject', count: 1, name: 'bob'
-
-      it 'renders the headers' do
-        expect(mail.subject).to match('notification since your last')
-        expect(mail.to).to eq([receiver.email])
-      end
-
-      it 'renders the body' do
-        expect(mail.body.encoded).to match('brief summary')
-        expect(mail.body.encoded).to include 'The body of the foreign status'
-        expect(mail.body.encoded).to include sender.username
-      end
-    end
-
-    it 'includes activities since the receiver last signed in' do
-      receiver.update!(last_emailed_at: nil, current_sign_in_at: '2000-03-01T00:00:00Z')
-      mail = NotificationMailer.digest(receiver.account)
-      expect(mail.body.encoded).to include 'Mar 01, 2000, 00:00'
-    end
-  end
 end
diff --git a/spec/mailers/previews/admin_mailer_preview.rb b/spec/mailers/previews/admin_mailer_preview.rb
index 01436ba7a..0ec9e9882 100644
--- a/spec/mailers/previews/admin_mailer_preview.rb
+++ b/spec/mailers/previews/admin_mailer_preview.rb
@@ -8,7 +8,7 @@ class AdminMailerPreview < ActionMailer::Preview
 
   # Preview this email at http://localhost:3000/rails/mailers/admin_mailer/new_trends
   def new_trends
-    AdminMailer.new_trends(Account.first, PreviewCard.limit(3), Tag.limit(3), Status.where(reblog_of_id: nil).limit(3))
+    AdminMailer.new_trends(Account.first, PreviewCard.joins(:trend).limit(3), Tag.limit(3), Status.joins(:trend).where(reblog_of_id: nil).limit(3))
   end
 
   # Preview this email at http://localhost:3000/rails/mailers/admin_mailer/new_appeal
diff --git a/spec/models/account/field_spec.rb b/spec/models/account/field_spec.rb
new file mode 100644
index 000000000..fcb2a884a
--- /dev/null
+++ b/spec/models/account/field_spec.rb
@@ -0,0 +1,138 @@
+require 'rails_helper'
+
+RSpec.describe Account::Field, type: :model do
+  describe '#verified?' do
+    let(:account) { double('Account', local?: true) }
+
+    subject { described_class.new(account, 'name' => 'Foo', 'value' => 'Bar', 'verified_at' => verified_at) }
+
+    context 'when verified_at is set' do
+      let(:verified_at) { Time.now.utc.iso8601 }
+
+      it 'returns true' do
+        expect(subject.verified?).to be true
+      end
+    end
+
+    context 'when verified_at is not set' do
+      let(:verified_at) { nil }
+
+      it 'returns false' do
+        expect(subject.verified?).to be false
+      end
+    end
+  end
+
+  describe '#mark_verified!' do
+    let(:account) { double('Account', local?: true) }
+    let(:original_hash) { { 'name' => 'Foo', 'value' => 'Bar' } }
+
+    subject { described_class.new(account, original_hash) }
+
+    before do
+      subject.mark_verified!
+    end
+
+    it 'updates verified_at' do
+      expect(subject.verified_at).to_not be_nil
+    end
+
+    it 'updates original hash' do
+      expect(original_hash['verified_at']).to_not be_nil
+    end
+  end
+
+  describe '#verifiable?' do
+    let(:account) { double('Account', local?: local) }
+
+    subject { described_class.new(account, 'name' => 'Foo', 'value' => value) }
+
+    context 'for local accounts' do
+      let(:local) { true }
+
+      context 'for a URL with misleading authentication' do
+        let(:value) { 'https://spacex.com                                                                                            @h.43z.one' }
+
+        it 'returns false' do
+          expect(subject.verifiable?).to be false
+        end
+      end
+
+      context 'for a URL' do
+        let(:value) { 'https://example.com' }
+
+        it 'returns true' do
+          expect(subject.verifiable?).to be true
+        end
+      end
+
+      context 'for an IDN URL' do
+        let(:value) { 'http://twitter.com∕dougallj∕status∕1590357240443437057.ê.cc/twitter.html' }
+
+        it 'returns false' do
+          expect(subject.verifiable?).to be false
+        end
+      end
+
+      context 'for text that is not a URL' do
+        let(:value) { 'Hello world' }
+
+        it 'returns false' do
+          expect(subject.verifiable?).to be false
+        end
+      end
+
+      context 'for text that contains a URL' do
+        let(:value) { 'Hello https://example.com world' }
+
+        it 'returns false' do
+          expect(subject.verifiable?).to be false
+        end
+      end
+    end
+
+    context 'for remote accounts' do
+      let(:local) { false }
+
+      context 'for a link' do
+        let(:value) { '<a href="https://www.patreon.com/mastodon" target="_blank" rel="nofollow noopener noreferrer me"><span class="invisible">https://www.</span><span class="">patreon.com/mastodon</span><span class="invisible"></span></a>' }
+
+        it 'returns true' do
+          expect(subject.verifiable?).to be true
+        end
+      end
+
+      context 'for a link with misleading authentication' do
+        let(:value) { '<a href="https://google.com                                                                                            @h.43z.one" target="_blank" rel="nofollow noopener noreferrer me"><span class="invisible">https://</span><span class="">google.com</span><span class="invisible">                                                                                            @h.43z.one</span></a>' }
+
+        it 'returns false' do
+          expect(subject.verifiable?).to be false
+        end
+      end
+
+      context 'for HTML that has more than just a link' do
+        let(:value) { '<a href="https://google.com" target="_blank" rel="nofollow noopener noreferrer me"><span class="invisible">https://</span><span class="">google.com</span><span class="invisible"></span></a>                                                                                            @h.43z.one' }
+
+        it 'returns false' do
+          expect(subject.verifiable?).to be false
+        end
+      end
+
+      context 'for a link with different visible text' do
+        let(:value) { '<a href="https://google.com/bar">https://example.com/foo</a>' }
+
+        it 'returns false' do
+          expect(subject.verifiable?).to be false
+        end
+      end
+
+      context 'for text that is a URL but is not linked' do
+        let(:value) { 'https://example.com/foo' }
+
+        it 'returns false' do
+          expect(subject.verifiable?).to be false
+        end
+      end
+    end
+  end
+end
diff --git a/spec/models/account_spec.rb b/spec/models/account_spec.rb
index 467d41836..edae05f9d 100644
--- a/spec/models/account_spec.rb
+++ b/spec/models/account_spec.rb
@@ -255,7 +255,7 @@ RSpec.describe Account, type: :model do
         Fabricate(:status, reblog: original_status, account: author)
       end
 
-      it 'is is true when this account has favourited it' do
+      it 'is true when this account has favourited it' do
         Fabricate(:favourite, status: original_reblog, account: subject)
 
         expect(subject.favourited?(original_status)).to eq true
@@ -267,7 +267,7 @@ RSpec.describe Account, type: :model do
     end
 
     context 'when the status is an original status' do
-      it 'is is true when this account has favourited it' do
+      it 'is true when this account has favourited it' do
         Fabricate(:favourite, status: original_status, account: subject)
 
         expect(subject.favourited?(original_status)).to eq true
@@ -755,7 +755,7 @@ RSpec.describe Account, type: :model do
         expect(account).to model_have_error_on_field(:username)
       end
 
-      it 'is invalid if the username is longer then 30 characters' do
+      it 'is invalid if the username is longer than 30 characters' do
         account = Fabricate.build(:account, username: Faker::Lorem.characters(number: 31))
         account.valid?
         expect(account).to model_have_error_on_field(:username)
@@ -801,7 +801,7 @@ RSpec.describe Account, type: :model do
         expect(account).to model_have_error_on_field(:username)
       end
 
-      it 'is valid even if the username is longer then 30 characters' do
+      it 'is valid even if the username is longer than 30 characters' do
         account = Fabricate.build(:account, domain: 'domain', username: Faker::Lorem.characters(number: 31))
         account.valid?
         expect(account).not_to model_have_error_on_field(:username)
diff --git a/spec/models/concerns/account_interactions_spec.rb b/spec/models/concerns/account_interactions_spec.rb
index 656dd66cc..b5aecf6be 100644
--- a/spec/models/concerns/account_interactions_spec.rb
+++ b/spec/models/concerns/account_interactions_spec.rb
@@ -14,14 +14,14 @@ describe AccountInteractions do
     context 'account with Follow' do
       it 'returns { target_account_id => { reblogs: true } }' do
         Fabricate(:follow, account: account, target_account: target_account)
-        is_expected.to eq(target_account_id => { reblogs: true, notify: false })
+        is_expected.to eq(target_account_id => { reblogs: true, notify: false, languages: nil })
       end
     end
 
     context 'account with Follow but with reblogs disabled' do
       it 'returns { target_account_id => { reblogs: false } }' do
         Fabricate(:follow, account: account, target_account: target_account, show_reblogs: false)
-        is_expected.to eq(target_account_id => { reblogs: false, notify: false })
+        is_expected.to eq(target_account_id => { reblogs: false, notify: false, languages: nil })
       end
     end
 
@@ -647,7 +647,7 @@ describe AccountInteractions do
       end
 
       it 'does mute notifications' do
-        expect(me.muting_notifications?(you)).to be true 
+        expect(me.muting_notifications?(you)).to be true
       end
     end
   end
diff --git a/spec/models/export_spec.rb b/spec/models/export_spec.rb
index 4e6b824bb..135d7a36b 100644
--- a/spec/models/export_spec.rb
+++ b/spec/models/export_spec.rb
@@ -35,8 +35,8 @@ describe Export do
       results = export.strip.split("\n")
 
       expect(results.size).to eq 3
-      expect(results.first).to eq 'Account address,Show boosts'
-      expect(results.second).to eq 'one@local.host,true'
+      expect(results.first).to eq 'Account address,Show boosts,Notify on new posts,Languages'
+      expect(results.second).to eq 'one@local.host,true,false,'
     end
   end
 
diff --git a/spec/models/follow_request_spec.rb b/spec/models/follow_request_spec.rb
index b0e854f09..c456c285f 100644
--- a/spec/models/follow_request_spec.rb
+++ b/spec/models/follow_request_spec.rb
@@ -7,7 +7,7 @@ RSpec.describe FollowRequest, type: :model do
     let(:target_account) { Fabricate(:account) }
 
     it 'calls Account#follow!, MergeWorker.perform_async, and #destroy!' do
-      expect(account).to        receive(:follow!).with(target_account, reblogs: true, notify: false, uri: follow_request.uri, bypass_limit: true)
+      expect(account).to        receive(:follow!).with(target_account, reblogs: true, notify: false, uri: follow_request.uri, languages: nil, bypass_limit: true)
       expect(MergeWorker).to    receive(:perform_async).with(target_account.id, account.id)
       expect(follow_request).to receive(:destroy!)
       follow_request.authorize!
diff --git a/spec/models/media_attachment_spec.rb b/spec/models/media_attachment_spec.rb
index cbd9a09c5..29fd313ae 100644
--- a/spec/models/media_attachment_spec.rb
+++ b/spec/models/media_attachment_spec.rb
@@ -157,9 +157,9 @@ RSpec.describe MediaAttachment, type: :model do
       expect(media.file.meta["original"]["width"]).to eq 600
       expect(media.file.meta["original"]["height"]).to eq 400
       expect(media.file.meta["original"]["aspect"]).to eq 1.5
-      expect(media.file.meta["small"]["width"]).to eq 490
-      expect(media.file.meta["small"]["height"]).to eq 327
-      expect(media.file.meta["small"]["aspect"]).to eq 490.0 / 327
+      expect(media.file.meta["small"]["width"]).to eq 588
+      expect(media.file.meta["small"]["height"]).to eq 392
+      expect(media.file.meta["small"]["aspect"]).to eq 1.5
     end
 
     it 'gives the file a random name' do
diff --git a/spec/models/preview_card_trend_spec.rb b/spec/models/preview_card_trend_spec.rb
new file mode 100644
index 000000000..c7ab6ed14
--- /dev/null
+++ b/spec/models/preview_card_trend_spec.rb
@@ -0,0 +1,4 @@
+require 'rails_helper'
+
+RSpec.describe PreviewCardTrend, type: :model do
+end
diff --git a/spec/models/status_spec.rb b/spec/models/status_spec.rb
index d3b23726d..e0a7aba7e 100644
--- a/spec/models/status_spec.rb
+++ b/spec/models/status_spec.rb
@@ -288,22 +288,6 @@ RSpec.describe Status, type: :model do
     end
   end
 
-  describe '.in_chosen_languages' do
-    context 'for accounts with language filters' do
-      let(:user) { Fabricate(:user, chosen_languages: ['en']) }
-
-      it 'does not include statuses in not in chosen languages' do
-        status = Fabricate(:status, language: 'de')
-        expect(Status.in_chosen_languages(user.account)).not_to include status
-      end
-
-      it 'includes status with unknown language' do
-        status = Fabricate(:status, language: nil)
-        expect(Status.in_chosen_languages(user.account)).to include status
-      end
-    end
-  end
-
   describe '.as_direct_timeline' do
     let(:account) { Fabricate(:account) }
     let(:followed) { Fabricate(:account) }
diff --git a/spec/models/status_trend_spec.rb b/spec/models/status_trend_spec.rb
new file mode 100644
index 000000000..6b82204a6
--- /dev/null
+++ b/spec/models/status_trend_spec.rb
@@ -0,0 +1,4 @@
+require 'rails_helper'
+
+RSpec.describe StatusTrend, type: :model do
+end
diff --git a/spec/models/trends/statuses_spec.rb b/spec/models/trends/statuses_spec.rb
index 9cc67acbe..5f338a65e 100644
--- a/spec/models/trends/statuses_spec.rb
+++ b/spec/models/trends/statuses_spec.rb
@@ -9,8 +9,8 @@ RSpec.describe Trends::Statuses do
     let!(:query) { subject.query }
     let!(:today) { at_time }
 
-    let!(:status1) { Fabricate(:status, text: 'Foo', trendable: true, created_at: today) }
-    let!(:status2) { Fabricate(:status, text: 'Bar', trendable: true, created_at: today) }
+    let!(:status1) { Fabricate(:status, text: 'Foo', language: 'en', trendable: true, created_at: today) }
+    let!(:status2) { Fabricate(:status, text: 'Bar', language: 'en', trendable: true, created_at: today) }
 
     before do
       15.times { reblog(status1, today) }
@@ -69,9 +69,9 @@ RSpec.describe Trends::Statuses do
     let!(:today) { at_time }
     let!(:yesterday) { today - 1.day }
 
-    let!(:status1) { Fabricate(:status, text: 'Foo', trendable: true, created_at: yesterday) }
-    let!(:status2) { Fabricate(:status, text: 'Bar', trendable: true, created_at: today) }
-    let!(:status3) { Fabricate(:status, text: 'Baz', trendable: true, created_at: today) }
+    let!(:status1) { Fabricate(:status, text: 'Foo', language: 'en', trendable: true, created_at: yesterday) }
+    let!(:status2) { Fabricate(:status, text: 'Bar', language: 'en', trendable: true, created_at: today) }
+    let!(:status3) { Fabricate(:status, text: 'Baz', language: 'en', trendable: true, created_at: today) }
 
     before do
       13.times { reblog(status1, today) }
@@ -95,10 +95,10 @@ RSpec.describe Trends::Statuses do
 
     it 'decays scores' do
       subject.refresh(today)
-      original_score = subject.score(status2.id)
+      original_score = status2.trend.score
       expect(original_score).to be_a Float
       subject.refresh(today + subject.options[:score_halflife])
-      decayed_score = subject.score(status2.id)
+      decayed_score = status2.trend.reload.score
       expect(decayed_score).to be <= original_score / 2
     end
   end
diff --git a/spec/policies/status_policy_spec.rb b/spec/policies/status_policy_spec.rb
index c2dcc50df..2afcfe96e 100644
--- a/spec/policies/status_policy_spec.rb
+++ b/spec/policies/status_policy_spec.rb
@@ -108,10 +108,6 @@ RSpec.describe StatusPolicy, type: :model do
       expect(subject).to permit(status.account, status)
     end
 
-    it 'grants access when account is admin' do
-      expect(subject).to permit(admin.account, status)
-    end
-
     it 'denies access when account is not deleter' do
       expect(subject).to_not permit(bob, status)
     end
@@ -137,27 +133,9 @@ RSpec.describe StatusPolicy, type: :model do
     end
   end
 
-  permissions :index? do
-    it 'grants access if staff' do
-      expect(subject).to permit(admin.account)
-    end
-
-    it 'denies access unless staff' do
-      expect(subject).to_not permit(alice)
-    end
-  end
-
   permissions :update? do
-    it 'grants access if staff' do
-      expect(subject).to permit(admin.account, status)
-    end
-
     it 'grants access if owner' do
       expect(subject).to permit(status.account, status)
     end
-
-    it 'denies access unless staff' do
-      expect(subject).to_not permit(bob, status)
-    end
   end
 end
diff --git a/spec/presenters/instance_presenter_spec.rb b/spec/presenters/instance_presenter_spec.rb
index 81d8d0e98..f4c415d2f 100644
--- a/spec/presenters/instance_presenter_spec.rb
+++ b/spec/presenters/instance_presenter_spec.rb
@@ -3,21 +3,20 @@ require 'rails_helper'
 describe InstancePresenter do
   let(:instance_presenter) { InstancePresenter.new }
 
-  context do
+  describe '#description' do
     around do |example|
-      site_description = Setting.site_description
+      site_description = Setting.site_short_description
       example.run
-      Setting.site_description = site_description
+      Setting.site_short_description = site_description
     end
 
     it "delegates site_description to Setting" do
-      Setting.site_description = "Site desc"
-
-      expect(instance_presenter.site_description).to eq "Site desc"
+      Setting.site_short_description = "Site desc"
+      expect(instance_presenter.description).to eq "Site desc"
     end
   end
 
-  context do
+  describe '#extended_description' do
     around do |example|
       site_extended_description = Setting.site_extended_description
       example.run
@@ -26,12 +25,11 @@ describe InstancePresenter do
 
     it "delegates site_extended_description to Setting" do
       Setting.site_extended_description = "Extended desc"
-
-      expect(instance_presenter.site_extended_description).to eq "Extended desc"
+      expect(instance_presenter.extended_description).to eq "Extended desc"
     end
   end
 
-  context do
+  describe '#email' do
     around do |example|
       site_contact_email = Setting.site_contact_email
       example.run
@@ -40,12 +38,11 @@ describe InstancePresenter do
 
     it "delegates contact_email to Setting" do
       Setting.site_contact_email = "admin@example.com"
-
-      expect(instance_presenter.site_contact_email).to eq "admin@example.com"
+      expect(instance_presenter.contact.email).to eq "admin@example.com"
     end
   end
 
-  describe "contact_account" do
+  describe '#account' do
     around do |example|
       site_contact_username = Setting.site_contact_username
       example.run
@@ -55,12 +52,11 @@ describe InstancePresenter do
     it "returns the account for the site contact username" do
       Setting.site_contact_username = "aaa"
       account = Fabricate(:account, username: "aaa")
-
-      expect(instance_presenter.contact_account).to eq(account)
+      expect(instance_presenter.contact.account).to eq(account)
     end
   end
 
-  describe "user_count" do
+  describe '#user_count' do
     it "returns the number of site users" do
       Rails.cache.write 'user_count', 123
 
@@ -68,7 +64,7 @@ describe InstancePresenter do
     end
   end
 
-  describe "status_count" do
+  describe '#status_count' do
     it "returns the number of local statuses" do
       Rails.cache.write 'local_status_count', 234
 
@@ -76,7 +72,7 @@ describe InstancePresenter do
     end
   end
 
-  describe "domain_count" do
+  describe '#domain_count' do
     it "returns the number of known domains" do
       Rails.cache.write 'distinct_domain_count', 345
 
@@ -84,9 +80,9 @@ describe InstancePresenter do
     end
   end
 
-  describe '#version_number' do
-    it 'returns Mastodon::Version' do
-      expect(instance_presenter.version_number).to be(Mastodon::Version)
+  describe '#version' do
+    it 'returns string' do
+      expect(instance_presenter.version).to be_a String
     end
   end
 
@@ -103,13 +99,6 @@ describe InstancePresenter do
     end
   end
 
-  describe '#hero' do
-    it 'returns SiteUpload' do
-      hero = Fabricate(:site_upload, var: 'hero')
-      expect(instance_presenter.hero).to eq(hero)
-    end
-  end
-
   describe '#mascot' do
     it 'returns SiteUpload' do
       mascot = Fabricate(:site_upload, var: 'mascot')
diff --git a/spec/requests/account_show_page_spec.rb b/spec/requests/account_show_page_spec.rb
index 4e51cf7ef..e84c46c47 100644
--- a/spec/requests/account_show_page_spec.rb
+++ b/spec/requests/account_show_page_spec.rb
@@ -3,17 +3,6 @@
 require 'rails_helper'
 
 describe 'The account show page' do
-  it 'Has an h-feed with correct number of h-entry objects in it' do
-    alice = Fabricate(:account, username: 'alice', display_name: 'Alice')
-    _status = Fabricate(:status, account: alice, text: 'Hello World')
-    _status2 = Fabricate(:status, account: alice, text: 'Hello World Again')
-    _status3 = Fabricate(:status, account: alice, text: 'Are You Still There World?')
-
-    get '/@alice'
-
-    expect(h_feed_entries.size).to eq(3)
-  end
-
   it 'has valid opengraph tags' do
     alice = Fabricate(:account, username: 'alice', display_name: 'Alice')
     _status = Fabricate(:status, account: alice, text: 'Hello World')
@@ -33,8 +22,4 @@ describe 'The account show page' do
   def head_section
     Nokogiri::Slop(response.body).html.head
   end
-
-  def h_feed_entries
-    Nokogiri::HTML(response.body).search('.h-feed .h-entry')
-  end
 end
diff --git a/spec/requests/localization_spec.rb b/spec/requests/localization_spec.rb
index 175f02ae9..0bc2786ac 100644
--- a/spec/requests/localization_spec.rb
+++ b/spec/requests/localization_spec.rb
@@ -10,30 +10,30 @@ describe 'Localization' do
   it 'uses a specific region when provided' do
     headers = { 'Accept-Language' => 'zh-HK' }
 
-    get "/about", headers: headers
+    get "/auth/sign_in", headers: headers
 
     expect(response.body).to include(
-      I18n.t('about.tagline', locale: 'zh-HK')
+      I18n.t('auth.login', locale: 'zh-HK')
     )
   end
 
   it 'falls back to a locale when region missing' do
     headers = { 'Accept-Language' => 'es-FAKE' }
 
-    get "/about", headers: headers
+    get "/auth/sign_in", headers: headers
 
     expect(response.body).to include(
-      I18n.t('about.tagline', locale: 'es')
+      I18n.t('auth.login', locale: 'es')
     )
   end
 
   it 'falls back to english when locale is missing' do
     headers = { 'Accept-Language' => '12-FAKE' }
 
-    get "/about", headers: headers
+    get "/auth/sign_in", headers: headers
 
     expect(response.body).to include(
-      I18n.t('about.tagline', locale: 'en')
+      I18n.t('auth.login', locale: 'en')
     )
   end
 end
diff --git a/spec/routing/accounts_routing_spec.rb b/spec/routing/accounts_routing_spec.rb
index d04cb27f0..3f0e9b3e9 100644
--- a/spec/routing/accounts_routing_spec.rb
+++ b/spec/routing/accounts_routing_spec.rb
@@ -1,31 +1,83 @@
 require 'rails_helper'
 
 describe 'Routes under accounts/' do
-  describe 'the route for accounts who are followers of an account' do
-    it 'routes to the followers action with the right username' do
-      expect(get('/users/name/followers')).
-        to route_to('follower_accounts#index', account_username: 'name')
+  context 'with local username' do
+    let(:username) { 'alice' }
+
+    it 'routes /@:username' do
+      expect(get("/@#{username}")).to route_to('accounts#show', username: username)
     end
-  end
 
-  describe 'the route for accounts who are followed by an account' do
-    it 'routes to the following action with the right username' do
-      expect(get('/users/name/following')).
-        to route_to('following_accounts#index', account_username: 'name')
+    it 'routes /@:username.json' do
+      expect(get("/@#{username}.json")).to route_to('accounts#show', username: username, format: 'json')
+    end
+
+    it 'routes /@:username.rss' do
+      expect(get("/@#{username}.rss")).to route_to('accounts#show', username: username, format: 'rss')
+    end
+
+    it 'routes /@:username/:id' do
+      expect(get("/@#{username}/123")).to route_to('statuses#show', account_username: username, id: '123')
+    end
+
+    it 'routes /@:username/:id/embed' do
+      expect(get("/@#{username}/123/embed")).to route_to('statuses#embed', account_username: username, id: '123')
+    end
+
+    it 'routes /@:username/following' do
+      expect(get("/@#{username}/following")).to route_to('following_accounts#index', account_username: username)
+    end
+
+    it 'routes /@:username/followers' do
+      expect(get("/@#{username}/followers")).to route_to('follower_accounts#index', account_username: username)
+    end
+
+    it 'routes /@:username/with_replies' do
+      expect(get("/@#{username}/with_replies")).to route_to('accounts#show', username: username)
+    end
+
+    it 'routes /@:username/media' do
+      expect(get("/@#{username}/media")).to route_to('accounts#show', username: username)
     end
-  end
 
-  describe 'the route for following an account' do
-    it 'routes to the follow create action with the right username' do
-      expect(post('/users/name/follow')).
-        to route_to('account_follow#create', account_username: 'name')
+    it 'routes /@:username/tagged/:tag' do
+      expect(get("/@#{username}/tagged/foo")).to route_to('accounts#show', username: username, tag: 'foo')
     end
   end
 
-  describe 'the route for unfollowing an account' do
-    it 'routes to the unfollow create action with the right username' do
-      expect(post('/users/name/unfollow')).
-        to route_to('account_unfollow#create', account_username: 'name')
+  context 'with remote username' do
+    let(:username) { 'alice@example.com' }
+
+    it 'routes /@:username' do
+      expect(get("/@#{username}")).to route_to('home#index', username_with_domain: username)
+    end
+
+    it 'routes /@:username/:id' do
+      expect(get("/@#{username}/123")).to route_to('home#index', username_with_domain: username, any: '123')
+    end
+
+    it 'routes /@:username/:id/embed' do
+      expect(get("/@#{username}/123/embed")).to route_to('home#index', username_with_domain: username, any: '123/embed')
+    end
+
+    it 'routes /@:username/following' do
+      expect(get("/@#{username}/following")).to route_to('home#index', username_with_domain: username, any: 'following')
+    end
+
+    it 'routes /@:username/followers' do
+      expect(get("/@#{username}/followers")).to route_to('home#index', username_with_domain: username, any: 'followers')
+    end
+
+    it 'routes /@:username/with_replies' do
+      expect(get("/@#{username}/with_replies")).to route_to('home#index', username_with_domain: username, any: 'with_replies')
+    end
+
+    it 'routes /@:username/media' do
+      expect(get("/@#{username}/media")).to route_to('home#index', username_with_domain: username, any: 'media')
+    end
+
+    it 'routes /@:username/tagged/:tag' do
+      expect(get("/@#{username}/tagged/foo")).to route_to('home#index', username_with_domain: username, any: 'tagged/foo')
     end
   end
 end
diff --git a/spec/services/account_search_service_spec.rb b/spec/services/account_search_service_spec.rb
index 5b7182586..81cbc175e 100644
--- a/spec/services/account_search_service_spec.rb
+++ b/spec/services/account_search_service_spec.rb
@@ -45,7 +45,6 @@ describe AccountSearchService, type: :service do
 
         results = subject.call('e@example.com', nil, limit: 2)
 
-        expect(results.size).to eq 2
         expect(results).to eq([exact, remote]).or eq([exact, remote_too])
       end
     end
diff --git a/spec/services/activitypub/fetch_featured_collection_service_spec.rb b/spec/services/activitypub/fetch_featured_collection_service_spec.rb
index f552b9dc0..e6336dc1b 100644
--- a/spec/services/activitypub/fetch_featured_collection_service_spec.rb
+++ b/spec/services/activitypub/fetch_featured_collection_service_spec.rb
@@ -65,7 +65,7 @@ RSpec.describe ActivityPub::FetchFeaturedCollectionService, type: :service do
       stub_request(:get, 'https://example.com/account/pinned/3').to_return(status: 404)
       stub_request(:get, 'https://example.com/account/pinned/4').to_return(status: 200, body: Oj.dump(status_json_4))
 
-      subject.call(actor)
+      subject.call(actor, note: true, hashtag: false)
     end
 
     it 'sets expected posts as pinned posts' do
diff --git a/spec/services/activitypub/fetch_featured_tags_collection_service_spec.rb b/spec/services/activitypub/fetch_featured_tags_collection_service_spec.rb
new file mode 100644
index 000000000..6ca22c9fc
--- /dev/null
+++ b/spec/services/activitypub/fetch_featured_tags_collection_service_spec.rb
@@ -0,0 +1,95 @@
+require 'rails_helper'
+
+RSpec.describe ActivityPub::FetchFeaturedTagsCollectionService, type: :service do
+  let(:collection_url) { 'https://example.com/account/tags' }
+  let(:actor) { Fabricate(:account, domain: 'example.com', uri: 'https://example.com/account') }
+
+  let(:items) do
+    [
+      { type: 'Hashtag', href: 'https://example.com/account/tagged/foo', name: 'Foo' },
+      { type: 'Hashtag', href: 'https://example.com/account/tagged/bar', name: 'bar' },
+      { type: 'Hashtag', href: 'https://example.com/account/tagged/baz', name: 'baZ' },
+    ]
+  end
+
+  let(:payload) do
+    {
+      '@context': 'https://www.w3.org/ns/activitystreams',
+      type: 'Collection',
+      id: collection_url,
+      items: items,
+    }.with_indifferent_access
+  end
+
+  subject { described_class.new }
+
+  shared_examples 'sets featured tags' do
+    before do
+      subject.call(actor, collection_url)
+    end
+
+    it 'sets expected tags as pinned tags' do
+      expect(actor.featured_tags.map(&:display_name)).to match_array ['Foo', 'bar', 'baZ']
+    end
+  end
+
+  describe '#call' do
+    context 'when the endpoint is a Collection' do
+      before do
+        stub_request(:get, collection_url).to_return(status: 200, body: Oj.dump(payload))
+      end
+
+      it_behaves_like 'sets featured tags'
+    end
+
+    context 'when the account already has featured tags' do
+      before do
+        stub_request(:get, collection_url).to_return(status: 200, body: Oj.dump(payload))
+
+        actor.featured_tags.create!(name: 'FoO')
+        actor.featured_tags.create!(name: 'baz')
+        actor.featured_tags.create!(name: 'oh').update(name: nil)
+      end
+
+      it_behaves_like 'sets featured tags'
+    end
+
+    context 'when the endpoint is an OrderedCollection' do
+      let(:payload) do
+        {
+          '@context': 'https://www.w3.org/ns/activitystreams',
+          type: 'OrderedCollection',
+          id: collection_url,
+          orderedItems: items,
+        }.with_indifferent_access
+      end
+
+      before do
+        stub_request(:get, collection_url).to_return(status: 200, body: Oj.dump(payload))
+      end
+
+      it_behaves_like 'sets featured tags'
+    end
+
+    context 'when the endpoint is a paginated Collection' do
+      let(:payload) do
+        {
+          '@context': 'https://www.w3.org/ns/activitystreams',
+          type: 'Collection',
+          id: collection_url,
+          first: {
+            type: 'CollectionPage',
+            partOf: collection_url,
+            items: items,
+          }
+        }.with_indifferent_access
+      end
+
+      before do
+        stub_request(:get, collection_url).to_return(status: 200, body: Oj.dump(payload))
+      end
+
+      it_behaves_like 'sets featured tags'
+    end
+  end
+end
diff --git a/spec/services/activitypub/fetch_remote_account_service_spec.rb b/spec/services/activitypub/fetch_remote_account_service_spec.rb
index aa13f0a9b..ec6f1f41d 100644
--- a/spec/services/activitypub/fetch_remote_account_service_spec.rb
+++ b/spec/services/activitypub/fetch_remote_account_service_spec.rb
@@ -119,6 +119,58 @@ RSpec.describe ActivityPub::FetchRemoteAccountService, type: :service do
       include_examples 'sets profile data'
     end
 
+    context 'when WebFinger returns a different URI' do
+      let!(:webfinger) { { subject: 'acct:alice@example.com', links: [{ rel: 'self', href: 'https://example.com/bob' }] } }
+
+      before do
+        stub_request(:get, 'https://example.com/alice').to_return(body: Oj.dump(actor))
+        stub_request(:get, 'https://example.com/.well-known/webfinger?resource=acct:alice@example.com').to_return(body: Oj.dump(webfinger), headers: { 'Content-Type': 'application/jrd+json' })
+      end
+
+      it 'fetches resource' do
+        account
+        expect(a_request(:get, 'https://example.com/alice')).to have_been_made.once
+      end
+
+      it 'looks up webfinger' do
+        account
+        expect(a_request(:get, 'https://example.com/.well-known/webfinger?resource=acct:alice@example.com')).to have_been_made.once
+      end
+
+      it 'does not create account' do
+        expect(account).to be_nil
+      end
+    end
+
+    context 'when WebFinger returns a different URI after a redirection' do
+      let!(:webfinger) { { subject: 'acct:alice@iscool.af', links: [{ rel: 'self', href: 'https://example.com/bob' }] } }
+
+      before do
+        stub_request(:get, 'https://example.com/alice').to_return(body: Oj.dump(actor))
+        stub_request(:get, 'https://example.com/.well-known/webfinger?resource=acct:alice@example.com').to_return(body: Oj.dump(webfinger), headers: { 'Content-Type': 'application/jrd+json' })
+        stub_request(:get, 'https://iscool.af/.well-known/webfinger?resource=acct:alice@iscool.af').to_return(body: Oj.dump(webfinger), headers: { 'Content-Type': 'application/jrd+json' })
+      end
+
+      it 'fetches resource' do
+        account
+        expect(a_request(:get, 'https://example.com/alice')).to have_been_made.once
+      end
+
+      it 'looks up webfinger' do
+        account
+        expect(a_request(:get, 'https://example.com/.well-known/webfinger?resource=acct:alice@example.com')).to have_been_made.once
+      end
+
+      it 'looks up "redirected" webfinger' do
+        account
+        expect(a_request(:get, 'https://iscool.af/.well-known/webfinger?resource=acct:alice@iscool.af')).to have_been_made.once
+      end
+
+      it 'does not create account' do
+        expect(account).to be_nil
+      end
+    end
+
     context 'with wrong id' do
       it 'does not create account' do
         expect(subject.call('https://fake.address/@foo', prefetched_body: Oj.dump(actor))).to be_nil
diff --git a/spec/services/activitypub/fetch_remote_actor_service_spec.rb b/spec/services/activitypub/fetch_remote_actor_service_spec.rb
new file mode 100644
index 000000000..20117c66d
--- /dev/null
+++ b/spec/services/activitypub/fetch_remote_actor_service_spec.rb
@@ -0,0 +1,180 @@
+require 'rails_helper'
+
+RSpec.describe ActivityPub::FetchRemoteActorService, type: :service do
+  subject { ActivityPub::FetchRemoteActorService.new }
+
+  let!(:actor) do
+    {
+      '@context': 'https://www.w3.org/ns/activitystreams',
+      id: 'https://example.com/alice',
+      type: 'Person',
+      preferredUsername: 'alice',
+      name: 'Alice',
+      summary: 'Foo bar',
+      inbox: 'http://example.com/alice/inbox',
+    }
+  end
+
+  describe '#call' do
+    let(:account) { subject.call('https://example.com/alice', id: true) }
+
+    shared_examples 'sets profile data' do
+      it 'returns an account' do
+        expect(account).to be_an Account
+      end
+
+      it 'sets display name' do
+        expect(account.display_name).to eq 'Alice'
+      end
+
+      it 'sets note' do
+        expect(account.note).to eq 'Foo bar'
+      end
+
+      it 'sets URL' do
+        expect(account.url).to eq 'https://example.com/alice'
+      end
+    end
+
+    context 'when the account does not have a inbox' do
+      let!(:webfinger) { { subject: 'acct:alice@example.com', links: [{ rel: 'self', href: 'https://example.com/alice' }] } }
+
+      before do
+        actor[:inbox] = nil
+
+        stub_request(:get, 'https://example.com/alice').to_return(body: Oj.dump(actor))
+        stub_request(:get, 'https://example.com/.well-known/webfinger?resource=acct:alice@example.com').to_return(body: Oj.dump(webfinger), headers: { 'Content-Type': 'application/jrd+json' })
+      end
+
+      it 'fetches resource' do
+        account
+        expect(a_request(:get, 'https://example.com/alice')).to have_been_made.once
+      end
+
+      it 'looks up webfinger' do
+        account
+        expect(a_request(:get, 'https://example.com/.well-known/webfinger?resource=acct:alice@example.com')).to have_been_made.once
+      end
+
+      it 'returns nil' do
+        expect(account).to be_nil
+      end
+    end
+
+    context 'when URI and WebFinger share the same host' do
+      let!(:webfinger) { { subject: 'acct:alice@example.com', links: [{ rel: 'self', href: 'https://example.com/alice' }] } }
+
+      before do
+        stub_request(:get, 'https://example.com/alice').to_return(body: Oj.dump(actor))
+        stub_request(:get, 'https://example.com/.well-known/webfinger?resource=acct:alice@example.com').to_return(body: Oj.dump(webfinger), headers: { 'Content-Type': 'application/jrd+json' })
+      end
+
+      it 'fetches resource' do
+        account
+        expect(a_request(:get, 'https://example.com/alice')).to have_been_made.once
+      end
+
+      it 'looks up webfinger' do
+        account
+        expect(a_request(:get, 'https://example.com/.well-known/webfinger?resource=acct:alice@example.com')).to have_been_made.once
+      end
+
+      it 'sets username and domain from webfinger' do
+        expect(account.username).to eq 'alice'
+        expect(account.domain).to eq 'example.com'
+      end
+
+      include_examples 'sets profile data'
+    end
+
+    context 'when WebFinger presents different domain than URI' do
+      let!(:webfinger) { { subject: 'acct:alice@iscool.af', links: [{ rel: 'self', href: 'https://example.com/alice' }] } }
+
+      before do
+        stub_request(:get, 'https://example.com/alice').to_return(body: Oj.dump(actor))
+        stub_request(:get, 'https://example.com/.well-known/webfinger?resource=acct:alice@example.com').to_return(body: Oj.dump(webfinger), headers: { 'Content-Type': 'application/jrd+json' })
+        stub_request(:get, 'https://iscool.af/.well-known/webfinger?resource=acct:alice@iscool.af').to_return(body: Oj.dump(webfinger), headers: { 'Content-Type': 'application/jrd+json' })
+      end
+
+      it 'fetches resource' do
+        account
+        expect(a_request(:get, 'https://example.com/alice')).to have_been_made.once
+      end
+
+      it 'looks up webfinger' do
+        account
+        expect(a_request(:get, 'https://example.com/.well-known/webfinger?resource=acct:alice@example.com')).to have_been_made.once
+      end
+
+      it 'looks up "redirected" webfinger' do
+        account
+        expect(a_request(:get, 'https://iscool.af/.well-known/webfinger?resource=acct:alice@iscool.af')).to have_been_made.once
+      end
+
+      it 'sets username and domain from final webfinger' do
+        expect(account.username).to eq 'alice'
+        expect(account.domain).to eq 'iscool.af'
+      end
+
+      include_examples 'sets profile data'
+    end
+
+    context 'when WebFinger returns a different URI' do
+      let!(:webfinger) { { subject: 'acct:alice@example.com', links: [{ rel: 'self', href: 'https://example.com/bob' }] } }
+
+      before do
+        stub_request(:get, 'https://example.com/alice').to_return(body: Oj.dump(actor))
+        stub_request(:get, 'https://example.com/.well-known/webfinger?resource=acct:alice@example.com').to_return(body: Oj.dump(webfinger), headers: { 'Content-Type': 'application/jrd+json' })
+      end
+
+      it 'fetches resource' do
+        account
+        expect(a_request(:get, 'https://example.com/alice')).to have_been_made.once
+      end
+
+      it 'looks up webfinger' do
+        account
+        expect(a_request(:get, 'https://example.com/.well-known/webfinger?resource=acct:alice@example.com')).to have_been_made.once
+      end
+
+      it 'does not create account' do
+        expect(account).to be_nil
+      end
+    end
+
+    context 'when WebFinger returns a different URI after a redirection' do
+      let!(:webfinger) { { subject: 'acct:alice@iscool.af', links: [{ rel: 'self', href: 'https://example.com/bob' }] } }
+
+      before do
+        stub_request(:get, 'https://example.com/alice').to_return(body: Oj.dump(actor))
+        stub_request(:get, 'https://example.com/.well-known/webfinger?resource=acct:alice@example.com').to_return(body: Oj.dump(webfinger), headers: { 'Content-Type': 'application/jrd+json' })
+        stub_request(:get, 'https://iscool.af/.well-known/webfinger?resource=acct:alice@iscool.af').to_return(body: Oj.dump(webfinger), headers: { 'Content-Type': 'application/jrd+json' })
+      end
+
+      it 'fetches resource' do
+        account
+        expect(a_request(:get, 'https://example.com/alice')).to have_been_made.once
+      end
+
+      it 'looks up webfinger' do
+        account
+        expect(a_request(:get, 'https://example.com/.well-known/webfinger?resource=acct:alice@example.com')).to have_been_made.once
+      end
+
+      it 'looks up "redirected" webfinger' do
+        account
+        expect(a_request(:get, 'https://iscool.af/.well-known/webfinger?resource=acct:alice@iscool.af')).to have_been_made.once
+      end
+
+      it 'does not create account' do
+        expect(account).to be_nil
+      end
+    end
+
+    context 'with wrong id' do
+      it 'does not create account' do
+        expect(subject.call('https://fake.address/@foo', prefetched_body: Oj.dump(actor))).to be_nil
+      end
+    end
+  end
+end
diff --git a/spec/services/activitypub/fetch_remote_key_service_spec.rb b/spec/services/activitypub/fetch_remote_key_service_spec.rb
new file mode 100644
index 000000000..3186c4270
--- /dev/null
+++ b/spec/services/activitypub/fetch_remote_key_service_spec.rb
@@ -0,0 +1,83 @@
+require 'rails_helper'
+
+RSpec.describe ActivityPub::FetchRemoteKeyService, type: :service do
+  subject { ActivityPub::FetchRemoteKeyService.new }
+
+  let(:webfinger) { { subject: 'acct:alice@example.com', links: [{ rel: 'self', href: 'https://example.com/alice' }] } }
+
+  let(:public_key_pem) do
+    "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAu3L4vnpNLzVH31MeWI39\n4F0wKeJFsLDAsNXGeOu0QF2x+h1zLWZw/agqD2R3JPU9/kaDJGPIV2Sn5zLyUA9S\n6swCCMOtn7BBR9g9sucgXJmUFB0tACH2QSgHywMAybGfmSb3LsEMNKsGJ9VsvYoh\n8lDET6X4Pyw+ZJU0/OLo/41q9w+OrGtlsTm/PuPIeXnxa6BLqnDaxC+4IcjG/FiP\nahNCTINl/1F/TgSSDZ4Taf4U9XFEIFw8wmgploELozzIzKq+t8nhQYkgAkt64euW\npva3qL5KD1mTIZQEP+LZvh3s2WHrLi3fhbdRuwQ2c0KkJA2oSTFPDpqqbPGZ3Qvu\nHQIDAQAB\n-----END PUBLIC KEY-----\n"
+  end
+
+  let(:public_key_id) { 'https://example.com/alice#main-key' }
+
+  let(:key_json) do
+    {
+      id: public_key_id,
+      owner: 'https://example.com/alice',
+      publicKeyPem: public_key_pem,
+    }
+  end
+
+  let(:actor_public_key) { key_json }
+
+  let(:actor) do
+    {
+      '@context': [
+        'https://www.w3.org/ns/activitystreams',
+        'https://w3id.org/security/v1',
+      ],
+      id: 'https://example.com/alice',
+      type: 'Person',
+      preferredUsername: 'alice',
+      name: 'Alice',
+      summary: 'Foo bar',
+      inbox: 'http://example.com/alice/inbox',
+      publicKey: actor_public_key,
+    }
+  end
+
+  before do
+    stub_request(:get, 'https://example.com/alice').to_return(body: Oj.dump(actor))
+    stub_request(:get, 'https://example.com/.well-known/webfinger?resource=acct:alice@example.com').to_return(body: Oj.dump(webfinger), headers: { 'Content-Type': 'application/jrd+json' })
+  end
+
+  describe '#call' do
+    let(:account) { subject.call(public_key_id, id: false) }
+
+    context 'when the key is a sub-object from the actor' do
+      before do
+        stub_request(:get, public_key_id).to_return(body: Oj.dump(actor))
+      end
+
+      it 'returns the expected account' do
+        expect(account.uri).to eq 'https://example.com/alice'
+      end
+    end
+
+    context 'when the key is a separate document' do
+      let(:public_key_id) { 'https://example.com/alice-public-key.json' }
+
+      before do
+        stub_request(:get, public_key_id).to_return(body: Oj.dump(key_json.merge({ '@context': ['https://www.w3.org/ns/activitystreams', 'https://w3id.org/security/v1'] })))
+      end
+
+      it 'returns the expected account' do
+        expect(account.uri).to eq 'https://example.com/alice'
+      end
+    end
+
+    context 'when the key and owner do not match' do
+      let(:public_key_id) { 'https://example.com/fake-public-key.json' }
+      let(:actor_public_key) { 'https://example.com/alice-public-key.json' }
+
+      before do
+        stub_request(:get, public_key_id).to_return(body: Oj.dump(key_json.merge({ '@context': ['https://www.w3.org/ns/activitystreams', 'https://w3id.org/security/v1'] })))
+      end
+
+      it 'returns the nil' do
+        expect(account).to be_nil
+      end
+    end
+  end
+end
diff --git a/spec/services/activitypub/process_collection_service_spec.rb b/spec/services/activitypub/process_collection_service_spec.rb
index 3eccaab5b..093a188a2 100644
--- a/spec/services/activitypub/process_collection_service_spec.rb
+++ b/spec/services/activitypub/process_collection_service_spec.rb
@@ -68,7 +68,7 @@ RSpec.describe ActivityPub::ProcessCollectionService, type: :service do
       let(:forwarder) { Fabricate(:account, domain: 'example.com', uri: 'http://example.com/other_account') }
 
       it 'does not process payload if no signature exists' do
-        expect_any_instance_of(ActivityPub::LinkedDataSignature).to receive(:verify_account!).and_return(nil)
+        expect_any_instance_of(ActivityPub::LinkedDataSignature).to receive(:verify_actor!).and_return(nil)
         expect(ActivityPub::Activity).not_to receive(:factory)
 
         subject.call(json, forwarder)
@@ -77,7 +77,7 @@ RSpec.describe ActivityPub::ProcessCollectionService, type: :service do
       it 'processes payload with actor if valid signature exists' do
         payload['signature'] = { 'type' => 'RsaSignature2017' }
 
-        expect_any_instance_of(ActivityPub::LinkedDataSignature).to receive(:verify_account!).and_return(actor)
+        expect_any_instance_of(ActivityPub::LinkedDataSignature).to receive(:verify_actor!).and_return(actor)
         expect(ActivityPub::Activity).to receive(:factory).with(instance_of(Hash), actor, instance_of(Hash))
 
         subject.call(json, forwarder)
@@ -86,7 +86,7 @@ RSpec.describe ActivityPub::ProcessCollectionService, type: :service do
       it 'does not process payload if invalid signature exists' do
         payload['signature'] = { 'type' => 'RsaSignature2017' }
 
-        expect_any_instance_of(ActivityPub::LinkedDataSignature).to receive(:verify_account!).and_return(nil)
+        expect_any_instance_of(ActivityPub::LinkedDataSignature).to receive(:verify_actor!).and_return(nil)
         expect(ActivityPub::Activity).not_to receive(:factory)
 
         subject.call(json, forwarder)
diff --git a/spec/services/fetch_resource_service_spec.rb b/spec/services/fetch_resource_service_spec.rb
index ded05ffbc..c0c96ab69 100644
--- a/spec/services/fetch_resource_service_spec.rb
+++ b/spec/services/fetch_resource_service_spec.rb
@@ -66,7 +66,7 @@ RSpec.describe FetchResourceService, type: :service do
 
       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
+        expect(a_request(:get, url).with(headers: { 'Signature' => /keyId="#{Regexp.escape(ActivityPub::TagManager.instance.key_uri_for(Account.representative))}"/ })).to have_been_made
       end
 
       context 'when content type is application/atom+xml' do
diff --git a/spec/services/follow_service_spec.rb b/spec/services/follow_service_spec.rb
index 02bc87c58..88346ec54 100644
--- a/spec/services/follow_service_spec.rb
+++ b/spec/services/follow_service_spec.rb
@@ -121,6 +121,19 @@ RSpec.describe FollowService, type: :service do
         expect(sender.muting_reblogs?(bob)).to be false
       end
     end
+
+    describe 'already followed account, changing languages' do
+      let(:bob) { Fabricate(:account, username: 'bob') }
+
+      before do
+        sender.follow!(bob)
+        subject.call(sender, bob, languages: %w(en es))
+      end
+
+      it 'changes languages' do
+        expect(Follow.find_by(account: sender, target_account: bob)&.languages).to match_array %w(en es)
+      end
+    end
   end
 
   context 'remote ActivityPub account' do
diff --git a/spec/services/process_mentions_service_spec.rb b/spec/services/process_mentions_service_spec.rb
index 89b265e9a..5b9d17a4c 100644
--- a/spec/services/process_mentions_service_spec.rb
+++ b/spec/services/process_mentions_service_spec.rb
@@ -1,43 +1,85 @@
 require 'rails_helper'
 
 RSpec.describe ProcessMentionsService, type: :service do
-  let(:account)    { Fabricate(:account, username: 'alice') }
-  let(:visibility) { :public }
-  let(:status)     { Fabricate(:status, account: account, text: "Hello @#{remote_user.acct}", visibility: visibility) }
+  let(:account) { Fabricate(:account, username: 'alice') }
 
   subject { ProcessMentionsService.new }
 
-  context 'ActivityPub' do
-    context do
-      let!(:remote_user) { Fabricate(:account, username: 'remote_user', protocol: :activitypub, domain: 'example.com', inbox_url: 'http://example.com/inbox') }
+  context 'when mentions contain blocked accounts' do
+    let(:non_blocked_account)          { Fabricate(:account) }
+    let(:individually_blocked_account) { Fabricate(:account) }
+    let(:domain_blocked_account)       { Fabricate(:account, domain: 'evil.com') }
+    let(:status) { Fabricate(:status, account: account, text: "Hello @#{non_blocked_account.acct} @#{individually_blocked_account.acct} @#{domain_blocked_account.acct}", visibility: :public) }
 
-      before do
-        subject.call(status)
-      end
+    before do
+      account.block!(individually_blocked_account)
+      account.domain_blocks.create!(domain: domain_blocked_account.domain)
 
-      it 'creates a mention' do
-        expect(remote_user.mentions.where(status: status).count).to eq 1
-      end
+      subject.call(status)
+    end
+
+    it 'creates a mention to the non-blocked account' do
+      expect(non_blocked_account.mentions.where(status: status).count).to eq 1
     end
 
-    context 'with an IDN domain' do
-      let!(:remote_user) { Fabricate(:account, username: 'sneak', protocol: :activitypub, domain: 'xn--hresiar-mxa.ch', inbox_url: 'http://example.com/inbox') }
-      let!(:status) { Fabricate(:status, account: account, text: "Hello @sneak@hæresiar.ch") }
+    it 'does not create a mention to the individually blocked account' do
+      expect(individually_blocked_account.mentions.where(status: status).count).to eq 0
+    end
 
-      before do
-        subject.call(status)
+    it 'does not create a mention to the domain-blocked account' do
+      expect(domain_blocked_account.mentions.where(status: status).count).to eq 0
+    end
+  end
+
+  context 'resolving a mention to a remote account' do
+    let(:status) { Fabricate(:status, account: account, text: "Hello @#{remote_user.acct}", visibility: :public) }
+
+    context 'ActivityPub' do
+      context do
+        let!(:remote_user) { Fabricate(:account, username: 'remote_user', protocol: :activitypub, domain: 'example.com', inbox_url: 'http://example.com/inbox') }
+
+        before do
+          subject.call(status)
+        end
+
+        it 'creates a mention' do
+          expect(remote_user.mentions.where(status: status).count).to eq 1
+        end
       end
 
-      it 'creates a mention' do
-        expect(remote_user.mentions.where(status: status).count).to eq 1
+      context 'with an IDN domain' do
+        let!(:remote_user) { Fabricate(:account, username: 'sneak', protocol: :activitypub, domain: 'xn--hresiar-mxa.ch', inbox_url: 'http://example.com/inbox') }
+        let!(:status) { Fabricate(:status, account: account, text: "Hello @sneak@hæresiar.ch") }
+
+        before do
+          subject.call(status)
+        end
+
+        it 'creates a mention' do
+          expect(remote_user.mentions.where(status: status).count).to eq 1
+        end
+      end
+
+      context 'with an IDN TLD' do
+        let!(:remote_user) { Fabricate(:account, username: 'foo', protocol: :activitypub, domain: 'xn--y9a3aq.xn--y9a3aq', inbox_url: 'http://example.com/inbox') }
+        let!(:status) { Fabricate(:status, account: account, text: "Hello @foo@հայ.հայ") }
+
+        before do
+          subject.call(status)
+        end
+
+        it 'creates a mention' do
+          expect(remote_user.mentions.where(status: status).count).to eq 1
+        end
       end
     end
 
-    context 'with an IDN TLD' do
-      let!(:remote_user) { Fabricate(:account, username: 'foo', protocol: :activitypub, domain: 'xn--y9a3aq.xn--y9a3aq', inbox_url: 'http://example.com/inbox') }
-      let!(:status) { Fabricate(:status, account: account, text: "Hello @foo@հայ.հայ") }
+    context 'Temporarily-unreachable ActivityPub user' do
+      let!(:remote_user) { Fabricate(:account, username: 'remote_user', protocol: :activitypub, domain: 'example.com', inbox_url: 'http://example.com/inbox', last_webfingered_at: nil) }
 
       before do
+        stub_request(:get, "https://example.com/.well-known/host-meta").to_return(status: 404)
+        stub_request(:get, "https://example.com/.well-known/webfinger?resource=acct:remote_user@example.com").to_return(status: 500)
         subject.call(status)
       end
 
@@ -46,18 +88,4 @@ RSpec.describe ProcessMentionsService, type: :service do
       end
     end
   end
-
-  context 'Temporarily-unreachable ActivityPub user' do
-    let!(:remote_user) { Fabricate(:account, username: 'remote_user', protocol: :activitypub, domain: 'example.com', inbox_url: 'http://example.com/inbox', last_webfingered_at: nil) }
-
-    before do
-      stub_request(:get, "https://example.com/.well-known/host-meta").to_return(status: 404)
-      stub_request(:get, "https://example.com/.well-known/webfinger?resource=acct:remote_user@example.com").to_return(status: 500)
-      subject.call(status)
-    end
-
-    it 'creates a mention' do
-      expect(remote_user.mentions.where(status: status).count).to eq 1
-    end
-  end
 end
diff --git a/spec/services/report_service_spec.rb b/spec/services/report_service_spec.rb
index ea68b3344..02bc42ac1 100644
--- a/spec/services/report_service_spec.rb
+++ b/spec/services/report_service_spec.rb
@@ -42,13 +42,44 @@ RSpec.describe ReportService, type: :service do
       end
 
       it 'creates a report' do
-        is_expected.to change { target_account.targeted_reports.count }.from(0).to(1)
+        expect { subject.call }.to change { target_account.targeted_reports.count }.from(0).to(1)
+      end
+
+      it 'attaches the DM to the report' do
+        subject.call
+        expect(target_account.targeted_reports.pluck(:status_ids)).to eq [[status.id]]
       end
     end
 
     context 'when it is not addressed to the reporter' do
       it 'errors out' do
-        is_expected.to raise_error
+        expect { subject.call }.to raise_error(ActiveRecord::RecordNotFound)
+      end
+    end
+
+    context 'when the reporter is remote' do
+      let(:source_account) { Fabricate(:account, domain: 'example.com', uri: 'https://example.com/users/1') }
+
+      context 'when it is addressed to the reporter' do
+        before do
+          status.mentions.create(account: source_account)
+        end
+
+        it 'creates a report' do
+          expect { subject.call }.to change { target_account.targeted_reports.count }.from(0).to(1)
+        end
+
+        it 'attaches the DM to the report' do
+          subject.call
+          expect(target_account.targeted_reports.pluck(:status_ids)).to eq [[status.id]]
+        end
+      end
+
+      context 'when it is not addressed to the reporter' do
+        it 'does not add the DM to the report' do
+          subject.call
+          expect(target_account.targeted_reports.pluck(:status_ids)).to eq [[]]
+        end
       end
     end
   end
@@ -67,7 +98,7 @@ RSpec.describe ReportService, type: :service do
     end
 
     it 'does not send an e-mail' do
-      is_expected.to_not change(ActionMailer::Base.deliveries, :count).from(0)
+      expect { subject.call }.to_not change(ActionMailer::Base.deliveries, :count).from(0)
     end
   end
 end
diff --git a/spec/services/resolve_account_service_spec.rb b/spec/services/resolve_account_service_spec.rb
index 8c302e1d8..654606bea 100644
--- a/spec/services/resolve_account_service_spec.rb
+++ b/spec/services/resolve_account_service_spec.rb
@@ -137,8 +137,8 @@ RSpec.describe ResolveAccountService, type: :service do
       stub_request(:get, 'https://evil.example.com/.well-known/webfinger?resource=acct:foo@evil.example.com').to_return(body: Oj.dump(webfinger2), headers: { 'Content-Type': 'application/jrd+json' })
     end
 
-    it 'returns new remote account' do
-      expect { subject.call('Foo@redirected.example.com') }.to raise_error Webfinger::RedirectError
+    it 'does not return a new remote account' do
+      expect(subject.call('Foo@redirected.example.com')).to be_nil
     end
   end
 
diff --git a/spec/views/about/show.html.haml_spec.rb b/spec/views/about/show.html.haml_spec.rb
deleted file mode 100644
index 140f3fd41..000000000
--- a/spec/views/about/show.html.haml_spec.rb
+++ /dev/null
@@ -1,46 +0,0 @@
-# frozen_string_literal: true
-
-require 'rails_helper'
-
-describe 'about/show.html.haml', without_verify_partial_doubles: true do
-  let(:commit_hash) { '8925731c9869f55780644304e4420a1998e52607' }
-
-  before do
-    allow(view).to receive(:site_hostname).and_return('example.com')
-    allow(view).to receive(:site_title).and_return('example site')
-    allow(view).to receive(:new_user).and_return(User.new)
-    allow(view).to receive(:use_seamless_external_login?).and_return(false)
-    allow(view).to receive(:current_account).and_return(nil)
-  end
-
-  it 'has valid open graph tags' do
-    instance_presenter = double(
-      :instance_presenter,
-      site_title: 'something',
-      site_short_description: 'something',
-      site_description: 'something',
-      version_number: '1.0',
-      source_url: 'https://github.com/mastodon/mastodon',
-      open_registrations: false,
-      thumbnail: nil,
-      hero: nil,
-      mascot: nil,
-      user_count: 420,
-      status_count: 69,
-      active_user_count: 420,
-      commit_hash: commit_hash,
-      contact_account: nil,
-      sample_accounts: []
-    )
-
-    assign(:instance_presenter, instance_presenter)
-    render
-
-    header_tags = view.content_for(:header_tags)
-
-    expect(header_tags).to match(%r{<meta content=".+" property="og:title" />})
-    expect(header_tags).to match(%r{<meta content="website" property="og:type" />})
-    expect(header_tags).to match(%r{<meta content=".+" property="og:image" />})
-    expect(header_tags).to match(%r{<meta content="http://.+" property="og:url" />})
-  end
-end
diff --git a/spec/views/statuses/show.html.haml_spec.rb b/spec/views/statuses/show.html.haml_spec.rb
index 879a26959..eeea2f698 100644
--- a/spec/views/statuses/show.html.haml_spec.rb
+++ b/spec/views/statuses/show.html.haml_spec.rb
@@ -12,57 +12,10 @@ describe 'statuses/show.html.haml', without_verify_partial_doubles: true do
     allow(view).to receive(:local_time)
     allow(view).to receive(:local_time_ago)
     allow(view).to receive(:current_account).and_return(nil)
+    allow(view).to receive(:single_user_mode?).and_return(false)
     assign(:instance_presenter, InstancePresenter.new)
   end
 
-  it 'has valid author h-card and basic data for a detailed_status' do
-    alice  = Fabricate(:account, username: 'alice', display_name: 'Alice')
-    bob    = Fabricate(:account, username: 'bob', display_name: 'Bob')
-    status = Fabricate(:status, account: alice, text: 'Hello World')
-    media  = Fabricate(:media_attachment, account: alice, status: status, type: :video)
-    reply  = Fabricate(:status, account: bob, thread: status, text: 'Hello Alice')
-
-    assign(:status, status)
-    assign(:account, alice)
-    assign(:descendant_threads, [])
-
-    render
-
-    mf2 = Microformats.parse(rendered)
-
-    expect(mf2.entry.url.to_s).not_to be_empty
-    expect(mf2.entry.author.name.to_s).to eq alice.display_name
-    expect(mf2.entry.author.url.to_s).not_to be_empty
-  end
-
-  it 'has valid h-cites for p-in-reply-to and p-comment' do
-    alice   = Fabricate(:account, username: 'alice', display_name: 'Alice')
-    bob     = Fabricate(:account, username: 'bob', display_name: 'Bob')
-    carl    = Fabricate(:account, username: 'carl', display_name: 'Carl')
-    status  = Fabricate(:status, account: alice, text: 'Hello World')
-    media   = Fabricate(:media_attachment, account: alice, status: status, type: :video)
-    reply   = Fabricate(:status, account: bob, thread: status, text: 'Hello Alice')
-    comment = Fabricate(:status, account: carl, thread: reply, text: 'Hello Bob')
-
-    assign(:status, reply)
-    assign(:account, alice)
-    assign(:ancestors, reply.ancestors(1, bob))
-    assign(:descendant_threads, [{ statuses: reply.descendants(1) }])
-
-    render
-
-    mf2 = Microformats.parse(rendered)
-
-    expect(mf2.entry.url.to_s).not_to be_empty
-    expect(mf2.entry.comment.url.to_s).not_to be_empty
-    expect(mf2.entry.comment.author.name.to_s).to eq carl.display_name
-    expect(mf2.entry.comment.author.url.to_s).not_to be_empty
-
-    expect(mf2.entry.in_reply_to.url.to_s).not_to be_empty
-    expect(mf2.entry.in_reply_to.author.name.to_s).to eq alice.display_name
-    expect(mf2.entry.in_reply_to.author.url.to_s).not_to be_empty
-  end
-
   it 'has valid opengraph tags' do
     alice  = Fabricate(:account, username: 'alice', display_name: 'Alice')
     status = Fabricate(:status, account: alice, text: 'Hello World')
diff --git a/spec/workers/digest_mailer_worker_spec.rb b/spec/workers/digest_mailer_worker_spec.rb
deleted file mode 100644
index db3b1390d..000000000
--- a/spec/workers/digest_mailer_worker_spec.rb
+++ /dev/null
@@ -1,36 +0,0 @@
-# frozen_string_literal: true
-
-require 'rails_helper'
-
-describe DigestMailerWorker do
-  describe 'perform' do
-    let(:user) { Fabricate(:user, last_emailed_at: 3.days.ago) }
-
-    context 'for a user who receives digests' do
-      it 'sends the email' do
-        service = double(deliver_now!: nil)
-        allow(NotificationMailer).to receive(:digest).and_return(service)
-        update_user_digest_setting(true)
-        described_class.perform_async(user.id)
-
-        expect(NotificationMailer).to have_received(:digest)
-        expect(user.reload.last_emailed_at).to be_within(1).of(Time.now.utc)
-      end
-    end
-
-    context 'for a user who does not receive digests' do
-      it 'does not send the email' do
-        allow(NotificationMailer).to receive(:digest)
-        update_user_digest_setting(false)
-        described_class.perform_async(user.id)
-
-        expect(NotificationMailer).not_to have_received(:digest)
-        expect(user.last_emailed_at).to be_within(1).of(3.days.ago)
-      end
-    end
-
-    def update_user_digest_setting(value)
-      user.settings['notification_emails'] = user.settings['notification_emails'].merge('digest' => value)
-    end
-  end
-end
diff --git a/spec/workers/move_worker_spec.rb b/spec/workers/move_worker_spec.rb
index be02d3192..3ca6aaf4d 100644
--- a/spec/workers/move_worker_spec.rb
+++ b/spec/workers/move_worker_spec.rb
@@ -74,6 +74,18 @@ describe MoveWorker do
     end
   end
 
+  shared_examples 'followers count handling' do
+    it 'updates the source account followers count' do
+      subject.perform(source_account.id, target_account.id)
+      expect(source_account.reload.followers_count).to eq(source_account.passive_relationships.count)
+    end
+
+    it 'updates the target account followers count' do
+      subject.perform(source_account.id, target_account.id)
+      expect(target_account.reload.followers_count).to eq(target_account.passive_relationships.count)
+    end
+  end
+
   context 'both accounts are distant' do
     describe 'perform' do
       it 'calls UnfollowFollowWorker' do
@@ -83,6 +95,7 @@ describe MoveWorker do
 
       include_examples 'user note handling'
       include_examples 'block and mute handling'
+      include_examples 'followers count handling'
     end
   end
 
@@ -97,6 +110,7 @@ describe MoveWorker do
 
       include_examples 'user note handling'
       include_examples 'block and mute handling'
+      include_examples 'followers count handling'
     end
   end
 
@@ -112,6 +126,7 @@ describe MoveWorker do
 
       include_examples 'user note handling'
       include_examples 'block and mute handling'
+      include_examples 'followers count handling'
 
       it 'does not fail when a local user is already following both accounts' do
         double_follower = Fabricate(:account)
diff --git a/spec/workers/refollow_worker_spec.rb b/spec/workers/refollow_worker_spec.rb
index df6731b64..d9c2293b6 100644
--- a/spec/workers/refollow_worker_spec.rb
+++ b/spec/workers/refollow_worker_spec.rb
@@ -23,8 +23,8 @@ describe RefollowWorker do
       result = subject.perform(account.id)
 
       expect(result).to be_nil
-      expect(service).to have_received(:call).with(alice, account, reblogs: true, notify: false, bypass_limit: true)
-      expect(service).to have_received(:call).with(bob, account, reblogs: false, notify: false, bypass_limit: true)
+      expect(service).to have_received(:call).with(alice, account, reblogs: true, notify: false, languages: nil, bypass_limit: true)
+      expect(service).to have_received(:call).with(bob, account, reblogs: false, notify: false, languages: nil, bypass_limit: true)
     end
   end
 end
diff --git a/spec/workers/scheduler/feed_cleanup_scheduler_spec.rb b/spec/workers/scheduler/feed_cleanup_scheduler_spec.rb
deleted file mode 100644
index 82d794594..000000000
--- a/spec/workers/scheduler/feed_cleanup_scheduler_spec.rb
+++ /dev/null
@@ -1,26 +0,0 @@
-require 'rails_helper'
-
-describe Scheduler::FeedCleanupScheduler do
-  subject { described_class.new }
-
-  let!(:active_user) { Fabricate(:user, current_sign_in_at: 2.days.ago) }
-  let!(:inactive_user) { Fabricate(:user, current_sign_in_at: 22.days.ago) }
-
-  it 'clears feeds of inactives' do
-    redis.zadd(feed_key_for(inactive_user), 1, 1)
-    redis.zadd(feed_key_for(active_user), 1, 1)
-    redis.zadd(feed_key_for(inactive_user, 'reblogs'), 2, 2)
-    redis.sadd(feed_key_for(inactive_user, 'reblogs:2'), 3)
-
-    subject.perform
-
-    expect(redis.zcard(feed_key_for(inactive_user))).to eq 0
-    expect(redis.zcard(feed_key_for(active_user))).to eq 1
-    expect(redis.exists?(feed_key_for(inactive_user, 'reblogs'))).to be false
-    expect(redis.exists?(feed_key_for(inactive_user, 'reblogs:2'))).to be false
-  end
-
-  def feed_key_for(user, subtype = nil)
-    FeedManager.instance.key(:home, user.account_id, subtype)
-  end
-end
diff --git a/spec/workers/scheduler/media_cleanup_scheduler_spec.rb b/spec/workers/scheduler/media_cleanup_scheduler_spec.rb
deleted file mode 100644
index 8a0da67e1..000000000
--- a/spec/workers/scheduler/media_cleanup_scheduler_spec.rb
+++ /dev/null
@@ -1,15 +0,0 @@
-require 'rails_helper'
-
-describe Scheduler::MediaCleanupScheduler do
-  subject { described_class.new }
-
-  let!(:old_media) { Fabricate(:media_attachment, account_id: nil, created_at: 10.days.ago) }
-  let!(:new_media) { Fabricate(:media_attachment, account_id: nil, created_at: 1.hour.ago) }
-
-  it 'removes old media records' do
-    subject.perform
-
-    expect { old_media.reload }.to raise_error(ActiveRecord::RecordNotFound)
-    expect(new_media.reload).to be_persisted
-  end
-end