about summary refs log tree commit diff
path: root/spec
diff options
context:
space:
mode:
Diffstat (limited to 'spec')
-rw-r--r--spec/controllers/accounts_controller_spec.rb139
-rw-r--r--spec/controllers/api/v1/lists/accounts_controller_spec.rb54
-rw-r--r--spec/controllers/api/v1/lists_controller_spec.rb68
-rw-r--r--spec/controllers/api/v1/timelines/list_controller_spec.rb56
-rw-r--r--spec/controllers/api/v1/timelines/tag_controller_spec.rb2
-rw-r--r--spec/controllers/settings/applications_controller_spec.rb10
-rw-r--r--spec/fabricators/list_account_fabricator.rb5
-rw-r--r--spec/fabricators/list_fabricator.rb4
-rw-r--r--spec/fabricators/session_activation_fabricator.rb2
-rw-r--r--spec/fabricators/setting_fabricator.rb4
-rw-r--r--spec/fixtures/requests/attachment1.txtbin192053 -> 192052 bytes
-rw-r--r--spec/fixtures/requests/attachment2.txtbin109078 -> 109077 bytes
-rw-r--r--spec/fixtures/requests/avatar.txtbin109962 -> 109961 bytes
-rw-r--r--spec/fixtures/requests/idn.txt14
-rw-r--r--spec/helpers/stream_entries_helper_spec.rb4
-rw-r--r--spec/lib/feed_manager_spec.rb92
-rw-r--r--spec/lib/settings/scoped_settings_spec.rb35
-rw-r--r--spec/lib/user_settings_decorator_spec.rb2
-rw-r--r--spec/models/account_moderation_note_spec.rb2
-rw-r--r--spec/models/account_spec.rb100
-rw-r--r--spec/models/concerns/account_interactions_spec.rb18
-rw-r--r--spec/models/custom_emoji_spec.rb29
-rw-r--r--spec/models/email_domain_block_spec.rb5
-rw-r--r--spec/models/follow_request_spec.rb32
-rw-r--r--spec/models/home_feed_spec.rb (renamed from spec/models/feed_spec.rb)4
-rw-r--r--spec/models/list_account_spec.rb5
-rw-r--r--spec/models/list_spec.rb5
-rw-r--r--spec/models/media_attachment_spec.rb77
-rw-r--r--spec/models/notification_spec.rb113
-rw-r--r--spec/models/remote_follow_spec.rb67
-rw-r--r--spec/models/remote_profile_spec.rb143
-rw-r--r--spec/models/session_activation_spec.rb124
-rw-r--r--spec/models/setting_spec.rb184
-rw-r--r--spec/models/site_upload_spec.rb8
-rw-r--r--spec/models/status_spec.rb53
-rw-r--r--spec/models/stream_entry_spec.rb115
-rw-r--r--spec/models/tag_spec.rb7
-rw-r--r--spec/models/user_spec.rb39
-rw-r--r--spec/policies/status_policy_spec.rb6
-rw-r--r--spec/services/activitypub/fetch_remote_status_service_spec.rb2
-rw-r--r--spec/services/after_block_service_spec.rb4
-rw-r--r--spec/services/batched_remove_status_service_spec.rb4
-rw-r--r--spec/services/fan_out_on_write_service_spec.rb4
-rw-r--r--spec/services/mute_service_spec.rb4
-rw-r--r--spec/services/notify_service_spec.rb33
-rw-r--r--spec/services/remove_status_service_spec.rb4
-rw-r--r--spec/support/matchers/model/model_have_error_on_field.rb2
-rw-r--r--spec/workers/feed_insert_worker_spec.rb16
48 files changed, 1472 insertions, 228 deletions
diff --git a/spec/controllers/accounts_controller_spec.rb b/spec/controllers/accounts_controller_spec.rb
index 92f888590..a8ade790c 100644
--- a/spec/controllers/accounts_controller_spec.rb
+++ b/spec/controllers/accounts_controller_spec.rb
@@ -4,6 +4,7 @@ RSpec.describe AccountsController, type: :controller do
   render_views
 
   let(:alice)  { Fabricate(:account, username: 'alice') }
+  let(:eve)  { Fabricate(:user) }
 
   describe 'GET #show' do
     let!(:status1) { Status.create!(account: alice, text: 'Hello world') }
@@ -19,93 +20,123 @@ RSpec.describe AccountsController, type: :controller do
     let!(:status_pin3) { StatusPin.create!(account: alice, status: status7, created_at: 10.minutes.ago) }
 
     before do
+      alice.block!(eve.account)
       status3.media_attachments.create!(account: alice, file: fixture_file_upload('files/attachment.jpg', 'image/jpeg'))
     end
 
-    context 'atom' do
+    shared_examples 'responses' do
       before do
-        get :show, params: { username: alice.username, max_id: status4.stream_entry.id, since_id: status1.stream_entry.id }, format: 'atom'
+        sign_in(current_user) if defined? current_user
+        get :show, params: {
+          username: alice.username,
+          max_id: (max_id if defined? max_id),
+          since_id: (since_id if defined? since_id),
+          current_user: (current_user if defined? current_user),
+        }, format: format
       end
 
       it 'assigns @account' do
         expect(assigns(:account)).to eq alice
       end
 
-      it 'assigns @entries' do
-        entries = assigns(:entries).to_a
-        expect(entries.size).to eq 2
-        expect(entries[0].status).to eq status3
-        expect(entries[1].status).to eq status2
+      it 'returns http success' do
+        expect(response).to have_http_status(:success)
       end
 
-      it 'returns http success with Atom' do
-        expect(response).to have_http_status(:success)
+      it 'returns correct format' do
+        expect(response.content_type).to eq content_type
       end
     end
 
-    context 'activitystreams2' do
-      before do
-        get :show, params: { username: alice.username }, format: 'json'
-      end
+    context 'atom' do
+      let(:format) { 'atom' }
+      let(:content_type) { 'application/atom+xml' }
 
-      it 'assigns @account' do
-        expect(assigns(:account)).to eq alice
+      shared_examples 'responsed streams' do
+        it 'assigns @entries' do
+          entries = assigns(:entries).to_a
+          expect(entries.size).to eq expected_statuses.size
+          entries.each.zip(expected_statuses.each) do |entry, expected_status|
+            expect(entry.status).to eq expected_status
+          end
+        end
       end
 
-      it 'returns http success with Activity Streams 2.0' do
-        expect(response).to have_http_status(:success)
-      end
+      include_examples 'responses'
 
-      it 'returns application/activity+json' do
-        expect(response.content_type).to eq 'application/activity+json'
-      end
-    end
+      context 'without max_id nor since_id' do
+        let(:expected_statuses) { [status7, status6, status5, status4, status3, status2, status1] }
 
-    context 'html without since_id nor max_id' do
-      before do
-        get :show, params: { username: alice.username }
+        include_examples 'responsed streams'
       end
 
-      it 'assigns @account' do
-        expect(assigns(:account)).to eq alice
-      end
+      context 'with max_id and since_id' do
+        let(:max_id) { status4.stream_entry.id }
+        let(:since_id) { status1.stream_entry.id }
+        let(:expected_statuses) { [status3, status2] }
 
-      it 'assigns @pinned_statuses' do
-        pinned_statuses = assigns(:pinned_statuses).to_a
-        expect(pinned_statuses.size).to eq 3
-        expect(pinned_statuses[0]).to eq status7
-        expect(pinned_statuses[1]).to eq status5
-        expect(pinned_statuses[2]).to eq status6
+        include_examples 'responsed streams'
       end
+    end
 
-      it 'returns http success' do
-        expect(response).to have_http_status(:success)
-      end
+    context 'activitystreams2' do
+      let(:format) { 'json' }
+      let(:content_type) { 'application/activity+json' }
+
+      include_examples 'responses'
     end
 
-    context 'html with since_id and max_id' do
-      before do
-        get :show, params: { username: alice.username, max_id: status4.id, since_id: status1.id }
-      end
+    context 'html' do
+      let(:format) { nil }
+      let(:content_type) { 'text/html' }
 
-      it 'assigns @account' do
-        expect(assigns(:account)).to eq alice
-      end
+      shared_examples 'responsed statuses' do
+        it 'assigns @pinned_statuses' do
+          pinned_statuses = assigns(:pinned_statuses).to_a
+          expect(pinned_statuses.size).to eq expected_pinned_statuses.size
+          pinned_statuses.each.zip(expected_pinned_statuses.each) do |pinned_status, expected_pinned_status|
+            expect(pinned_status).to eq expected_pinned_status
+          end
+        end
 
-      it 'assigns @statuses' do
-        statuses = assigns(:statuses).to_a
-        expect(statuses.size).to eq 2
-        expect(statuses[0]).to eq status3
-        expect(statuses[1]).to eq status2
+        it 'assigns @statuses' do
+          statuses = assigns(:statuses).to_a
+          expect(statuses.size).to eq expected_statuses.size
+          statuses.each.zip(expected_statuses.each) do |status, expected_status|
+            expect(status).to eq expected_status
+          end
+        end
       end
 
-      it 'assigns an empty array to @pinned_statuses' do
-        pinned_statuses = assigns(:pinned_statuses).to_a
-        expect(pinned_statuses.size).to eq 0
+      include_examples 'responses'
+
+      context 'with anonymous visitor' do
+        context 'without since_id nor max_id' do
+          let(:expected_statuses) { [status7, status6, status5, status4, status3, status2, status1] }
+          let(:expected_pinned_statuses) { [status7, status5, status6] }
+
+          include_examples 'responsed statuses'
+        end
+
+        context 'with since_id nor max_id' do
+          let(:max_id) { status4.id }
+          let(:since_id) { status1.id }
+          let(:expected_statuses) { [status3, status2] }
+          let(:expected_pinned_statuses) { [] }
+
+          include_examples 'responsed statuses'
+        end
       end
 
-      it 'returns http success' do
-        expect(response).to have_http_status(:success)
+      context 'with blocked visitor' do
+        let(:current_user) { eve }
+
+        context 'without since_id nor max_id' do
+          let(:expected_statuses) { [] }
+          let(:expected_pinned_statuses) { [] }
+
+          include_examples 'responsed statuses'
+        end
       end
     end
   end
diff --git a/spec/controllers/api/v1/lists/accounts_controller_spec.rb b/spec/controllers/api/v1/lists/accounts_controller_spec.rb
new file mode 100644
index 000000000..953e5909d
--- /dev/null
+++ b/spec/controllers/api/v1/lists/accounts_controller_spec.rb
@@ -0,0 +1,54 @@
+require 'rails_helper'
+
+describe Api::V1::Lists::AccountsController do
+  render_views
+
+  let(:user)  { Fabricate(:user, account: Fabricate(:account, username: 'alice')) }
+  let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'read write') }
+  let(:list)  { Fabricate(:list, account: user.account) }
+
+  before do
+    follow = Fabricate(:follow, account: user.account)
+    list.accounts << follow.target_account
+    allow(controller).to receive(:doorkeeper_token) { token }
+  end
+
+  describe 'GET #index' do
+    it 'returns http success' do
+      get :show, params: { list_id: list.id }
+
+      expect(response).to have_http_status(:success)
+    end
+  end
+
+  describe 'POST #create' do
+    let(:bob) { Fabricate(:account, username: 'bob') }
+
+    before do
+      user.account.follow!(bob)
+      post :create, params: { list_id: list.id, account_ids: [bob.id] }
+    end
+
+    it 'returns http success' do
+      expect(response).to have_http_status(:success)
+    end
+
+    it 'adds account to the list' do
+      expect(list.accounts.include?(bob)).to be true
+    end
+  end
+
+  describe 'DELETE #destroy' do
+    before do
+      delete :destroy, params: { list_id: list.id, account_ids: [list.accounts.first.id] }
+    end
+
+    it 'returns http success' do
+      expect(response).to have_http_status(:success)
+    end
+
+    it 'removes account from the list' do
+      expect(list.accounts.count).to eq 0
+    end
+  end
+end
diff --git a/spec/controllers/api/v1/lists_controller_spec.rb b/spec/controllers/api/v1/lists_controller_spec.rb
new file mode 100644
index 000000000..be08c221f
--- /dev/null
+++ b/spec/controllers/api/v1/lists_controller_spec.rb
@@ -0,0 +1,68 @@
+require 'rails_helper'
+
+RSpec.describe Api::V1::ListsController, type: :controller do
+  render_views
+
+  let!(:user)  { Fabricate(:user, account: Fabricate(:account, username: 'alice')) }
+  let!(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'read write') }
+  let!(:list)  { Fabricate(:list, account: user.account) }
+
+  before { allow(controller).to receive(:doorkeeper_token) { token } }
+
+  describe 'GET #index' do
+    it 'returns http success' do
+      get :index
+      expect(response).to have_http_status(:success)
+    end
+  end
+
+  describe 'GET #show' do
+    it 'returns http success' do
+      get :show, params: { id: list.id }
+      expect(response).to have_http_status(:success)
+    end
+  end
+
+  describe 'POST #create' do
+    before do
+      post :create, params: { title: 'Foo bar' }
+    end
+
+    it 'returns http success' do
+      expect(response).to have_http_status(:success)
+    end
+
+    it 'creates list' do
+      expect(List.where(account: user.account).count).to eq 2
+      expect(List.last.title).to eq 'Foo bar'
+    end
+  end
+
+  describe 'PUT #update' do
+    before do
+      put :update, params: { id: list.id, title: 'Updated title' }
+    end
+
+    it 'returns http success' do
+      expect(response).to have_http_status(:success)
+    end
+
+    it 'updates the list' do
+      expect(list.reload.title).to eq 'Updated title'
+    end
+  end
+
+  describe 'DELETE #destroy' do
+    before do
+      delete :destroy, params: { id: list.id }
+    end
+
+    it 'returns http success' do
+      expect(response).to have_http_status(:success)
+    end
+
+    it 'deletes the list' do
+      expect(List.find_by(id: list.id)).to be_nil
+    end
+  end
+end
diff --git a/spec/controllers/api/v1/timelines/list_controller_spec.rb b/spec/controllers/api/v1/timelines/list_controller_spec.rb
new file mode 100644
index 000000000..07eba955a
--- /dev/null
+++ b/spec/controllers/api/v1/timelines/list_controller_spec.rb
@@ -0,0 +1,56 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe Api::V1::Timelines::ListController do
+  render_views
+
+  let(:user) { Fabricate(:user, account: Fabricate(:account, username: 'alice')) }
+  let(:list) { Fabricate(:list, account: user.account) }
+
+  before do
+    allow(controller).to receive(:doorkeeper_token) { token }
+  end
+
+  context 'with a user context' do
+    let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'read') }
+
+    describe 'GET #show' do
+      before do
+        follow = Fabricate(:follow, account: user.account)
+        list.accounts << follow.target_account
+        PostStatusService.new.call(follow.target_account, 'New status for user home timeline.')
+      end
+
+      it 'returns http success' do
+        get :show, params: { id: list.id }
+        expect(response).to have_http_status(:success)
+      end
+    end
+  end
+
+  context 'with the wrong user context' do
+    let(:other_user) { Fabricate(:user, account: Fabricate(:account, username: 'bob')) }
+    let(:token)      { Fabricate(:accessible_access_token, resource_owner_id: other_user.id, scopes: 'read') }
+
+    describe 'GET #show' do
+      it 'returns http not found' do
+        get :show, params: { id: list.id }
+        expect(response).to have_http_status(:not_found)
+      end
+    end
+  end
+
+  context 'without a user context' do
+    let(:token) { Fabricate(:accessible_access_token, resource_owner_id: nil, scopes: 'read') }
+
+    describe 'GET #show' do
+      it 'returns http unprocessable entity' do
+        get :show, params: { id: list.id }
+
+        expect(response).to have_http_status(:unprocessable_entity)
+        expect(response.headers['Link']).to be_nil
+      end
+    end
+  end
+end
diff --git a/spec/controllers/api/v1/timelines/tag_controller_spec.rb b/spec/controllers/api/v1/timelines/tag_controller_spec.rb
index 74de1e81f..6c66ee58e 100644
--- a/spec/controllers/api/v1/timelines/tag_controller_spec.rb
+++ b/spec/controllers/api/v1/timelines/tag_controller_spec.rb
@@ -5,7 +5,7 @@ require 'rails_helper'
 describe Api::V1::Timelines::TagController do
   render_views
 
-  let(:user)  { Fabricate(:user, account: Fabricate(:account, username: 'alice')) }
+  let(:user) { Fabricate(:user, account: Fabricate(:account, username: 'alice')) }
 
   before do
     allow(controller).to receive(:doorkeeper_token) { token }
diff --git a/spec/controllers/settings/applications_controller_spec.rb b/spec/controllers/settings/applications_controller_spec.rb
index ca66f8d23..90e6a63d5 100644
--- a/spec/controllers/settings/applications_controller_spec.rb
+++ b/spec/controllers/settings/applications_controller_spec.rb
@@ -2,10 +2,10 @@ require 'rails_helper'
 
 describe Settings::ApplicationsController do
   render_views
-  
+
   let!(:user) { Fabricate(:user) }
   let!(:app) { Fabricate(:application, owner: user) }
-  
+
   before do
     sign_in user, scope: :user
   end
@@ -21,7 +21,7 @@ describe Settings::ApplicationsController do
     end
   end
 
-  
+
   describe 'GET #show' do
     it 'returns http success' do
       get :show, params: { id: app.id }
@@ -110,7 +110,7 @@ describe Settings::ApplicationsController do
       end
     end
   end
-  
+
   describe 'PATCH #update' do
     context 'success' do
       let(:opts) {
@@ -131,7 +131,7 @@ describe Settings::ApplicationsController do
         call_update
         expect(app.reload.website).to eql(opts[:website])
       end
-      
+
       it 'redirects back to applications page' do
         expect(call_update).to redirect_to(settings_applications_path)
       end
diff --git a/spec/fabricators/list_account_fabricator.rb b/spec/fabricators/list_account_fabricator.rb
new file mode 100644
index 000000000..30e4004aa
--- /dev/null
+++ b/spec/fabricators/list_account_fabricator.rb
@@ -0,0 +1,5 @@
+Fabricator(:list_account) do
+  list    nil
+  account nil
+  follow  nil
+end
diff --git a/spec/fabricators/list_fabricator.rb b/spec/fabricators/list_fabricator.rb
new file mode 100644
index 000000000..d249c2029
--- /dev/null
+++ b/spec/fabricators/list_fabricator.rb
@@ -0,0 +1,4 @@
+Fabricator(:list) do
+  account nil
+  title   "MyString"
+end
diff --git a/spec/fabricators/session_activation_fabricator.rb b/spec/fabricators/session_activation_fabricator.rb
index 46050bdab..526faaec2 100644
--- a/spec/fabricators/session_activation_fabricator.rb
+++ b/spec/fabricators/session_activation_fabricator.rb
@@ -1,4 +1,4 @@
 Fabricator(:session_activation) do
-  user_id    1
+  user
   session_id "MyString"
 end
diff --git a/spec/fabricators/setting_fabricator.rb b/spec/fabricators/setting_fabricator.rb
new file mode 100644
index 000000000..336d7c355
--- /dev/null
+++ b/spec/fabricators/setting_fabricator.rb
@@ -0,0 +1,4 @@
+# frozen_string_literal: true
+
+Fabricator(:setting) do
+end
diff --git a/spec/fixtures/requests/attachment1.txt b/spec/fixtures/requests/attachment1.txt
index 77fd9c836..30bd456be 100644
--- a/spec/fixtures/requests/attachment1.txt
+++ b/spec/fixtures/requests/attachment1.txt
Binary files differdiff --git a/spec/fixtures/requests/attachment2.txt b/spec/fixtures/requests/attachment2.txt
index 917a1d398..2a252d2de 100644
--- a/spec/fixtures/requests/attachment2.txt
+++ b/spec/fixtures/requests/attachment2.txt
Binary files differdiff --git a/spec/fixtures/requests/avatar.txt b/spec/fixtures/requests/avatar.txt
index d57b0984f..d771f5dda 100644
--- a/spec/fixtures/requests/avatar.txt
+++ b/spec/fixtures/requests/avatar.txt
Binary files differdiff --git a/spec/fixtures/requests/idn.txt b/spec/fixtures/requests/idn.txt
index 3c76c59c0..5d07f2b79 100644
--- a/spec/fixtures/requests/idn.txt
+++ b/spec/fixtures/requests/idn.txt
@@ -6,7 +6,7 @@ Content-Length: 38111
 Last-Modified: Wed, 20 Jul 2016 02:50:52 GMT

 Connection: keep-alive

 Accept-Ranges: bytes

-

+
 <!DOCTYPE html>

 <html>

 	<head>

@@ -21,16 +21,16 @@ Accept-Ranges: bytes
             var s = document.getElementsByTagName("script")[0]; 

             s.parentNode.insertBefore(hm, s);

           })();

-	

+
     	</script>

-

-

+
+
 		<link rel="stylesheet" type="text/css" href="css/common.css"/>

 		<script src="js/jquery-1.11.1.min.js" type="text/javascript" charset="utf-8"></script>

 		<script src="js/common.js" type="text/javascript" charset="utf-8"></script>

 		<script src="js/carousel.js" type="text/javascript" charset="utf-8"></script>

 		<title>中国域名网站</title>

-

+
 	</head>

 	<body>

 		<div class="head-tips" id="headTip">

@@ -453,7 +453,7 @@ Accept-Ranges: bytes
 					<li><a href="http://新疆农业大学.中国" target="_blank">新疆农业大学.中国</a></li>

 					<li><a href="http://浙江万里学院.中国" target="_blank">浙江万里学院.中国</a></li>

 					<li><a href="http://重庆大学.中国" target="_blank">重庆大学.中国</a></li>

-					

+
 				</ul>

 			</div>

 		</div>

@@ -472,7 +472,7 @@ Accept-Ranges: bytes
 	<script>

 	$("#headTip").hide()

 	var hostname = window.location.hostname || "";

-

+
 	var tips =  "您所访问的域名 <font size='' color='#ff0000'>" + hostname +"</font> 无法到达,您可以尝试重新访问,或使用搜索相关信息"

 	if (hostname != "导航.中国") {

 		$("#headTip").html(tips);

diff --git a/spec/helpers/stream_entries_helper_spec.rb b/spec/helpers/stream_entries_helper_spec.rb
index 2c0d7b239..1de6691ba 100644
--- a/spec/helpers/stream_entries_helper_spec.rb
+++ b/spec/helpers/stream_entries_helper_spec.rb
@@ -77,7 +77,7 @@ RSpec.describe StreamEntriesHelper, type: :helper do
     params[:controller] = StreamEntriesHelper::EMBEDDED_CONTROLLER
     params[:action] = StreamEntriesHelper::EMBEDDED_ACTION
   end
-  
+
   describe '#style_classes' do
     it do
       status = double(reblog?: false)
@@ -202,7 +202,7 @@ RSpec.describe StreamEntriesHelper, type: :helper do
       expect(css_class).to eq 'h-cite'
     end
   end
-  
+
   describe '#rtl?' do
     it 'is false if text is empty' do
       expect(helper).not_to be_rtl ''
diff --git a/spec/lib/feed_manager_spec.rb b/spec/lib/feed_manager_spec.rb
index 0e4968440..f87ef383a 100644
--- a/spec/lib/feed_manager_spec.rb
+++ b/spec/lib/feed_manager_spec.rb
@@ -223,21 +223,11 @@ RSpec.describe FeedManager do
       account = Fabricate(:account)
       status = Fabricate(:status)
       members = FeedManager::MAX_ITEMS.times.map { |count| [count, count] }
-      Redis.current.zadd("feed:type:#{account.id}", members)
+      Redis.current.zadd("feed:home:#{account.id}", members)
 
-      FeedManager.instance.push('type', account, status)
+      FeedManager.instance.push_to_home(account, status)
 
-      expect(Redis.current.zcard("feed:type:#{account.id}")).to eq FeedManager::MAX_ITEMS
-    end
-
-    it 'sends push updates for non-home timelines' do
-      account = Fabricate(:account)
-      status = Fabricate(:status)
-      allow(Redis.current).to receive_messages(publish: nil)
-
-      FeedManager.instance.push('type', account, status)
-
-      expect(Redis.current).to have_received(:publish).with("timeline:#{account.id}", any_args).at_least(:once)
+      expect(Redis.current.zcard("feed:home:#{account.id}")).to eq FeedManager::MAX_ITEMS
     end
 
     context 'reblogs' do
@@ -246,7 +236,7 @@ RSpec.describe FeedManager do
         reblogged = Fabricate(:status)
         reblog = Fabricate(:status, reblog: reblogged)
 
-        expect(FeedManager.instance.push('type', account, reblog)).to be true
+        expect(FeedManager.instance.push_to_home(account, reblog)).to be true
       end
 
       it 'does not save a new reblog of a recent status' do
@@ -254,9 +244,9 @@ RSpec.describe FeedManager do
         reblogged = Fabricate(:status)
         reblog = Fabricate(:status, reblog: reblogged)
 
-        FeedManager.instance.push('type', account, reblogged)
+        FeedManager.instance.push_to_home(account, reblogged)
 
-        expect(FeedManager.instance.push('type', account, reblog)).to be false
+        expect(FeedManager.instance.push_to_home(account, reblog)).to be false
       end
 
       it 'saves a new reblog of an old status' do
@@ -264,14 +254,14 @@ RSpec.describe FeedManager do
         reblogged = Fabricate(:status)
         reblog = Fabricate(:status, reblog: reblogged)
 
-        FeedManager.instance.push('type', account, reblogged)
+        FeedManager.instance.push_to_home(account, reblogged)
 
         # Fill the feed with intervening statuses
         FeedManager::REBLOG_FALLOFF.times do
-          FeedManager.instance.push('type', account, Fabricate(:status))
+          FeedManager.instance.push_to_home(account, Fabricate(:status))
         end
 
-        expect(FeedManager.instance.push('type', account, reblog)).to be true
+        expect(FeedManager.instance.push_to_home(account, reblog)).to be true
       end
 
       it 'does not save a new reblog of a recently-reblogged status' do
@@ -280,10 +270,10 @@ RSpec.describe FeedManager do
         reblogs = 2.times.map { Fabricate(:status, reblog: reblogged) }
 
         # The first reblog will be accepted
-        FeedManager.instance.push('type', account, reblogs.first)
+        FeedManager.instance.push_to_home(account, reblogs.first)
 
         # The second reblog should be ignored
-        expect(FeedManager.instance.push('type', account, reblogs.last)).to be false
+        expect(FeedManager.instance.push_to_home(account, reblogs.last)).to be false
       end
 
       it 'does not save a new reblog of a multiply-reblogged-then-unreblogged status' do
@@ -292,14 +282,14 @@ RSpec.describe FeedManager do
         reblogs = 3.times.map { Fabricate(:status, reblog: reblogged) }
 
         # Accept the reblogs
-        FeedManager.instance.push('type', account, reblogs[0])
-        FeedManager.instance.push('type', account, reblogs[1])
+        FeedManager.instance.push_to_home(account, reblogs[0])
+        FeedManager.instance.push_to_home(account, reblogs[1])
 
         # Unreblog the first one
-        FeedManager.instance.unpush('type', account, reblogs[0])
+        FeedManager.instance.unpush_from_home(account, reblogs[0])
 
         # The last reblog should still be ignored
-        expect(FeedManager.instance.push('type', account, reblogs.last)).to be false
+        expect(FeedManager.instance.push_to_home(account, reblogs.last)).to be false
       end
 
       it 'saves a new reblog of a long-ago-reblogged status' do
@@ -308,15 +298,15 @@ RSpec.describe FeedManager do
         reblogs = 2.times.map { Fabricate(:status, reblog: reblogged) }
 
         # The first reblog will be accepted
-        FeedManager.instance.push('type', account, reblogs.first)
+        FeedManager.instance.push_to_home(account, reblogs.first)
 
         # Fill the feed with intervening statuses
         FeedManager::REBLOG_FALLOFF.times do
-          FeedManager.instance.push('type', account, Fabricate(:status))
+          FeedManager.instance.push_to_home(account, Fabricate(:status))
         end
 
         # The second reblog should also be accepted
-        expect(FeedManager.instance.push('type', account, reblogs.last)).to be true
+        expect(FeedManager.instance.push_to_home(account, reblogs.last)).to be true
       end
     end
   end
@@ -328,11 +318,11 @@ RSpec.describe FeedManager do
       reblogged      = Fabricate(:status)
       status         = Fabricate(:status, reblog: reblogged)
       another_status = Fabricate(:status, reblog: reblogged)
-      reblogs_key    = FeedManager.instance.key('type', receiver.id, 'reblogs')
-      reblog_set_key = FeedManager.instance.key('type', receiver.id, "reblogs:#{reblogged.id}")
+      reblogs_key    = FeedManager.instance.key('home', receiver.id, 'reblogs')
+      reblog_set_key = FeedManager.instance.key('home', receiver.id, "reblogs:#{reblogged.id}")
 
-      FeedManager.instance.push('type', receiver, status)
-      FeedManager.instance.push('type', receiver, another_status)
+      FeedManager.instance.push_to_home(receiver, status)
+      FeedManager.instance.push_to_home(receiver, another_status)
 
       # We should have a tracking set and an entry in reblogs.
       expect(Redis.current.exists(reblog_set_key)).to be true
@@ -340,12 +330,12 @@ RSpec.describe FeedManager do
 
       # Push everything off the end of the feed.
       FeedManager::MAX_ITEMS.times do
-        FeedManager.instance.push('type', receiver, Fabricate(:status))
+        FeedManager.instance.push_to_home(receiver, Fabricate(:status))
       end
 
       # `trim` should be called automatically, but do it anyway, as
       # we're testing `trim`, not side effects of `push`.
-      FeedManager.instance.trim('type', receiver.id)
+      FeedManager.instance.trim('home', receiver.id)
 
       # We should not have any reblog tracking data.
       expect(Redis.current.exists(reblog_set_key)).to be false
@@ -360,32 +350,32 @@ RSpec.describe FeedManager do
       reblogged = Fabricate(:status)
       status    = Fabricate(:status, reblog: reblogged)
 
-      FeedManager.instance.push('type', receiver, reblogged)
-      FeedManager::REBLOG_FALLOFF.times { FeedManager.instance.push('type', receiver, Fabricate(:status)) }
-      FeedManager.instance.push('type', receiver, status)
+      FeedManager.instance.push_to_home(receiver, reblogged)
+      FeedManager::REBLOG_FALLOFF.times { FeedManager.instance.push_to_home(receiver, Fabricate(:status)) }
+      FeedManager.instance.push_to_home(receiver, status)
 
       # The reblogging status should show up under normal conditions.
-      expect(Redis.current.zrange("feed:type:#{receiver.id}", 0, -1)).to include(status.id.to_s)
+      expect(Redis.current.zrange("feed:home:#{receiver.id}", 0, -1)).to include(status.id.to_s)
 
-      FeedManager.instance.unpush('type', receiver, status)
+      FeedManager.instance.unpush_from_home(receiver, status)
 
       # Restore original status
-      expect(Redis.current.zrange("feed:type:#{receiver.id}", 0, -1)).to_not include(status.id.to_s)
-      expect(Redis.current.zrange("feed:type:#{receiver.id}", 0, -1)).to include(reblogged.id.to_s)
+      expect(Redis.current.zrange("feed:home:#{receiver.id}", 0, -1)).to_not include(status.id.to_s)
+      expect(Redis.current.zrange("feed:home:#{receiver.id}", 0, -1)).to include(reblogged.id.to_s)
     end
 
     it 'removes a reblogged status if it was only reblogged once' do
       reblogged = Fabricate(:status)
       status    = Fabricate(:status, reblog: reblogged)
 
-      FeedManager.instance.push('type', receiver, status)
+      FeedManager.instance.push_to_home(receiver, status)
 
       # The reblogging status should show up under normal conditions.
-      expect(Redis.current.zrange("feed:type:#{receiver.id}", 0, -1)).to eq [status.id.to_s]
+      expect(Redis.current.zrange("feed:home:#{receiver.id}", 0, -1)).to eq [status.id.to_s]
 
-      FeedManager.instance.unpush('type', receiver, status)
+      FeedManager.instance.unpush_from_home(receiver, status)
 
-      expect(Redis.current.zrange("feed:type:#{receiver.id}", 0, -1)).to be_empty
+      expect(Redis.current.zrange("feed:home:#{receiver.id}", 0, -1)).to be_empty
     end
 
     it 'leaves a multiply-reblogged status if another reblog was in feed' do
@@ -393,26 +383,26 @@ RSpec.describe FeedManager do
       reblogs   = 3.times.map { Fabricate(:status, reblog: reblogged) }
 
       reblogs.each do |reblog|
-        FeedManager.instance.push('type', receiver, reblog)
+        FeedManager.instance.push_to_home(receiver, reblog)
       end
 
       # The reblogging status should show up under normal conditions.
-      expect(Redis.current.zrange("feed:type:#{receiver.id}", 0, -1)).to eq [reblogs.first.id.to_s]
+      expect(Redis.current.zrange("feed:home:#{receiver.id}", 0, -1)).to eq [reblogs.first.id.to_s]
 
       reblogs[0...-1].each do |reblog|
-        FeedManager.instance.unpush('type', receiver, reblog)
+        FeedManager.instance.unpush_from_home(receiver, reblog)
       end
 
-      expect(Redis.current.zrange("feed:type:#{receiver.id}", 0, -1)).to eq [reblogs.last.id.to_s]
+      expect(Redis.current.zrange("feed:home:#{receiver.id}", 0, -1)).to eq [reblogs.last.id.to_s]
     end
 
     it 'sends push updates' do
       status  = Fabricate(:status)
 
-      FeedManager.instance.push('type', receiver, status)
+      FeedManager.instance.push_to_home(receiver, status)
 
       allow(Redis.current).to receive_messages(publish: nil)
-      FeedManager.instance.unpush('type', receiver, status)
+      FeedManager.instance.unpush_from_home(receiver, status)
 
       deletion = Oj.dump(event: :delete, payload: status.id.to_s)
       expect(Redis.current).to have_received(:publish).with("timeline:#{receiver.id}", deletion)
diff --git a/spec/lib/settings/scoped_settings_spec.rb b/spec/lib/settings/scoped_settings_spec.rb
new file mode 100644
index 000000000..7566685b4
--- /dev/null
+++ b/spec/lib/settings/scoped_settings_spec.rb
@@ -0,0 +1,35 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+RSpec.describe Settings::ScopedSettings do
+  let(:object)         { Fabricate(:user) }
+  let(:scoped_setting) { described_class.new(object) }
+  let(:val)            { 'whatever' }
+  let(:methods)        { %i(auto_play_gif default_sensitive unfollow_modal boost_modal delete_modal reduce_motion system_font_ui noindex theme) }
+
+  describe '.initialize' do
+    it 'sets @object' do
+      scoped_setting = described_class.new(object)
+      expect(scoped_setting.instance_variable_get(:@object)).to be object
+    end
+  end
+
+  describe '#method_missing' do
+    it 'sets scoped_setting.method_name = val' do
+      methods.each do |key|
+        scoped_setting.send("#{key}=", val)
+        expect(scoped_setting.send(key)).to eq val
+      end
+    end
+  end
+
+  describe '#[]= and #[]' do
+    it 'sets [key] = val' do
+      methods.each do |key|
+        scoped_setting[key] = val
+        expect(scoped_setting[key]).to eq val
+      end
+    end
+  end
+end
diff --git a/spec/lib/user_settings_decorator_spec.rb b/spec/lib/user_settings_decorator_spec.rb
index 6fbf6536b..fee875373 100644
--- a/spec/lib/user_settings_decorator_spec.rb
+++ b/spec/lib/user_settings_decorator_spec.rb
@@ -62,7 +62,7 @@ describe UserSettingsDecorator do
       settings.update(values)
       expect(user.settings['auto_play_gif']).to eq false
     end
-    
+
     it 'updates the user settings value for system font in UI' do
       values = { 'setting_system_font_ui' => '0' }
 
diff --git a/spec/models/account_moderation_note_spec.rb b/spec/models/account_moderation_note_spec.rb
index c4be8c4af..16983b2e3 100644
--- a/spec/models/account_moderation_note_spec.rb
+++ b/spec/models/account_moderation_note_spec.rb
@@ -1,5 +1,5 @@
 require 'rails_helper'
 
 RSpec.describe AccountModerationNote, type: :model do
-  pending "add some examples to (or delete) #{__FILE__}"
+
 end
diff --git a/spec/models/account_spec.rb b/spec/models/account_spec.rb
index 361577eff..7501c498c 100644
--- a/spec/models/account_spec.rb
+++ b/spec/models/account_spec.rb
@@ -93,21 +93,44 @@ RSpec.describe Account, type: :model do
   end
 
   describe '#save_with_optional_media!' do
-    it 'sets default avatar, header, avatar_remote_url, and header_remote_url if some of them are invalid' do
+    before do
       stub_request(:get, 'https://remote/valid_avatar').to_return(request_fixture('avatar.txt'))
       stub_request(:get, 'https://remote/invalid_avatar').to_return(request_fixture('feed.txt'))
-      account = Fabricate(:account,
-                          avatar_remote_url: 'https://remote/valid_avatar',
-                          header_remote_url: 'https://remote/valid_avatar')
+    end
+
+    let(:account) do
+      Fabricate(:account,
+                avatar_remote_url: 'https://remote/valid_avatar',
+                header_remote_url: 'https://remote/valid_avatar')
+    end
+
+    let!(:expectation) { account.dup }
+
+    context 'with valid properties' do
+      before do
+        account.save_with_optional_media!
+      end
+
+      it 'unchanges avatar, header, avatar_remote_url, and header_remote_url' do
+        expect(account.avatar_remote_url).to eq expectation.avatar_remote_url
+        expect(account.header_remote_url).to eq expectation.header_remote_url
+        expect(account.avatar_file_name).to  eq expectation.avatar_file_name
+        expect(account.header_file_name).to  eq expectation.header_file_name
+      end
+    end
 
-      account.avatar_remote_url = 'https://remote/invalid_avatar'
-      account.save_with_optional_media!
+    context 'with invalid properties' do
+      before do
+        account.avatar_remote_url = 'https://remote/invalid_avatar'
+        account.save_with_optional_media!
+      end
 
-      account.reload
-      expect(account.avatar_remote_url).to eq ''
-      expect(account.header_remote_url).to eq ''
-      expect(account.avatar_file_name).to eq nil
-      expect(account.header_file_name).to eq nil
+      it 'sets default avatar, header, avatar_remote_url, and header_remote_url' do
+        expect(account.avatar_remote_url).to eq ''
+        expect(account.header_remote_url).to eq ''
+        expect(account.avatar_file_name).to  eq nil
+        expect(account.header_file_name).to  eq nil
+      end
     end
   end
 
@@ -123,6 +146,61 @@ RSpec.describe Account, type: :model do
     end
   end
 
+  describe '#possibly_stale?' do
+    let(:account) { Fabricate(:account, last_webfingered_at: last_webfingered_at) }
+
+    context 'last_webfingered_at is nil' do
+      let(:last_webfingered_at) { nil }
+
+      it 'returns true' do
+        expect(account.possibly_stale?).to be true
+      end
+    end
+
+    context 'last_webfingered_at is more than 24 hours before' do
+      let(:last_webfingered_at) { 25.hours.ago }
+
+      it 'returns true' do
+        expect(account.possibly_stale?).to be true
+      end
+    end
+
+    context 'last_webfingered_at is less than 24 hours before' do
+      let(:last_webfingered_at) { 23.hours.ago }
+
+      it 'returns false' do
+        expect(account.possibly_stale?).to be false
+      end
+    end
+  end
+
+  describe '#refresh!' do
+    let(:account) { Fabricate(:account, domain: domain) }
+    let(:acct)    { account.acct }
+
+    context 'domain is nil' do
+      let(:domain) { nil }
+
+      it 'returns nil' do
+        expect(account.refresh!).to be_nil
+      end
+
+      it 'calls not ResolveRemoteAccountService#call' do
+        expect_any_instance_of(ResolveRemoteAccountService).not_to receive(:call).with(acct)
+        account.refresh!
+      end
+    end
+
+    context 'domain is present' do
+      let(:domain) { 'example.com' }
+
+      it 'calls ResolveRemoteAccountService#call' do
+        expect_any_instance_of(ResolveRemoteAccountService).to receive(:call).with(acct).once
+        account.refresh!
+      end
+    end
+  end
+
   describe '#to_param' do
     it 'returns username' do
       account = Fabricate(:account, username: 'alice')
diff --git a/spec/models/concerns/account_interactions_spec.rb b/spec/models/concerns/account_interactions_spec.rb
index f47d9d057..1e238e27c 100644
--- a/spec/models/concerns/account_interactions_spec.rb
+++ b/spec/models/concerns/account_interactions_spec.rb
@@ -2,38 +2,36 @@ require 'rails_helper'
 
 describe AccountInteractions do
   describe 'muting an account' do
-    before do
-      @me = Fabricate(:account, username: 'Me')
-      @you = Fabricate(:account, username: 'You')
-    end
+    let(:me) { Fabricate(:account, username: 'Me') }
+    let(:you) { Fabricate(:account, username: 'You') }
 
     context 'with the notifications option unspecified' do
       before do
-        @me.mute!(@you)
+        me.mute!(you)
       end
 
       it 'defaults to muting notifications' do
-        expect(@me.muting_notifications?(@you)).to be(true)
+        expect(me.muting_notifications?(you)).to be true
       end
     end
 
     context 'with the notifications option set to false' do
       before do
-        @me.mute!(@you, notifications: false)
+        me.mute!(you, notifications: false)
       end
 
       it 'does not mute notifications' do
-        expect(@me.muting_notifications?(@you)).to be(false)
+        expect(me.muting_notifications?(you)).to be false
       end
     end
 
     context 'with the notifications option set to true' do
       before do
-        @me.mute!(@you, notifications: true)
+        me.mute!(you, notifications: true)
       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/custom_emoji_spec.rb b/spec/models/custom_emoji_spec.rb
index cb51e9519..bb150b837 100644
--- a/spec/models/custom_emoji_spec.rb
+++ b/spec/models/custom_emoji_spec.rb
@@ -1,6 +1,35 @@
 require 'rails_helper'
 
 RSpec.describe CustomEmoji, type: :model do
+  describe '#local?' do
+    let(:custom_emoji) { Fabricate(:custom_emoji, domain: domain) }
+
+    subject { custom_emoji.local? }
+
+    context 'domain is nil' do
+      let(:domain) { nil }
+
+      it 'returns true' do
+        is_expected.to be true
+      end
+    end
+
+    context 'domain is present' do
+      let(:domain) { 'example.com' }
+
+      it 'returns false' do
+        is_expected.to be false
+      end
+    end
+  end
+
+  describe '#object_type' do
+    it 'returns :emoji' do
+      custom_emoji = Fabricate(:custom_emoji)
+      expect(custom_emoji.object_type).to be :emoji
+    end
+  end
+
   describe '.from_text' do
     let!(:emojo) { Fabricate(:custom_emoji) }
 
diff --git a/spec/models/email_domain_block_spec.rb b/spec/models/email_domain_block_spec.rb
index 5f5d189d9..efd2853a9 100644
--- a/spec/models/email_domain_block_spec.rb
+++ b/spec/models/email_domain_block_spec.rb
@@ -13,9 +13,10 @@ RSpec.describe EmailDomainBlock, type: :model do
       Fabricate(:email_domain_block, domain: 'example.com')
       expect(EmailDomainBlock.block?('nyarn@example.com')).to eq true
     end
+
     it 'returns true if the domain is not registed' do
-      Fabricate(:email_domain_block, domain: 'domain')
-      expect(EmailDomainBlock.block?('example')).to eq false
+      Fabricate(:email_domain_block, domain: 'example.com')
+      expect(EmailDomainBlock.block?('nyarn@example.net')).to eq false
     end
   end
 end
diff --git a/spec/models/follow_request_spec.rb b/spec/models/follow_request_spec.rb
index 62bd724d7..7bc93a2aa 100644
--- a/spec/models/follow_request_spec.rb
+++ b/spec/models/follow_request_spec.rb
@@ -2,6 +2,17 @@ require 'rails_helper'
 
 RSpec.describe FollowRequest, type: :model do
   describe '#authorize!' do
+    let(:follow_request) { Fabricate(:follow_request, account: account, target_account: target_account) }
+    let(:account)        { Fabricate(:account) }
+    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)
+      expect(MergeWorker).to    receive(:perform_async).with(target_account.id, account.id)
+      expect(follow_request).to receive(:destroy!)
+      follow_request.authorize!
+    end
+
     it 'generates a Follow' do
       follow_request = Fabricate.create(:follow_request)
       follow_request.authorize!
@@ -23,25 +34,4 @@ RSpec.describe FollowRequest, type: :model do
       expect(follow_request.account.muting_reblogs?(target)).to be true
     end
   end
-
-  describe '#reject!'
-
-  describe 'validations' do
-    it 'has a valid fabricator' do
-      follow_request = Fabricate.build(:follow_request)
-      expect(follow_request).to be_valid
-    end
-
-    it 'is invalid without an account' do
-      follow_request = Fabricate.build(:follow_request, account: nil)
-      follow_request.valid?
-      expect(follow_request).to model_have_error_on_field(:account)
-    end
-
-    it 'is invalid without a target account' do
-      follow_request = Fabricate.build(:follow_request, target_account: nil)
-      follow_request.valid?
-      expect(follow_request).to model_have_error_on_field(:target_account)      
-    end
-  end
 end
diff --git a/spec/models/feed_spec.rb b/spec/models/home_feed_spec.rb
index 8719369db..3acb997f1 100644
--- a/spec/models/feed_spec.rb
+++ b/spec/models/home_feed_spec.rb
@@ -1,9 +1,9 @@
 require 'rails_helper'
 
-RSpec.describe Feed, type: :model do
+RSpec.describe HomeFeed, type: :model do
   let(:account) { Fabricate(:account) }
 
-  subject { described_class.new(:home, account) }
+  subject { described_class.new(account) }
 
   describe '#get' do
     before do
diff --git a/spec/models/list_account_spec.rb b/spec/models/list_account_spec.rb
new file mode 100644
index 000000000..a132e09b0
--- /dev/null
+++ b/spec/models/list_account_spec.rb
@@ -0,0 +1,5 @@
+require 'rails_helper'
+
+RSpec.describe ListAccount, type: :model do
+
+end
diff --git a/spec/models/list_spec.rb b/spec/models/list_spec.rb
new file mode 100644
index 000000000..c302482b4
--- /dev/null
+++ b/spec/models/list_spec.rb
@@ -0,0 +1,5 @@
+require 'rails_helper'
+
+RSpec.describe List, type: :model do
+
+end
diff --git a/spec/models/media_attachment_spec.rb b/spec/models/media_attachment_spec.rb
index 435b4f326..b40a641f7 100644
--- a/spec/models/media_attachment_spec.rb
+++ b/spec/models/media_attachment_spec.rb
@@ -1,6 +1,83 @@
 require 'rails_helper'
 
 RSpec.describe MediaAttachment, type: :model do
+  describe 'local?' do
+    let(:media_attachment) { Fabricate(:media_attachment, remote_url: remote_url) }
+
+    subject { media_attachment.local? }
+
+    context 'remote_url is blank' do
+      let(:remote_url) { '' }
+
+      it 'returns true' do
+        is_expected.to be true
+      end
+    end
+
+    context 'remote_url is present' do
+      let(:remote_url) { 'remote_url' }
+
+      it 'returns false' do
+        is_expected.to be false
+      end
+    end
+  end
+
+  describe 'needs_redownload?' do
+    let(:media_attachment) { Fabricate(:media_attachment, remote_url: remote_url, file: file) }
+
+    subject { media_attachment.needs_redownload? }
+
+    context 'file is blank' do
+      let(:file) { nil }
+
+      context 'remote_url is blank' do
+        let(:remote_url) { '' }
+
+        it 'returns false' do
+          is_expected.to be false
+        end
+      end
+
+      context 'remote_url is present' do
+        let(:remote_url) { 'remote_url' }
+
+        it 'returns true' do
+          is_expected.to be true
+        end
+      end
+    end
+
+    context 'file is present' do
+      let(:file) { attachment_fixture('avatar.gif') }
+
+      context 'remote_url is blank' do
+        let(:remote_url) { '' }
+
+        it 'returns false' do
+          is_expected.to be false
+        end
+      end
+
+      context 'remote_url is present' do
+        let(:remote_url) { 'remote_url' }
+
+        it 'returns true' do
+          is_expected.to be false
+        end
+      end
+    end
+  end
+
+  describe '#to_param' do
+    let(:media_attachment) { Fabricate(:media_attachment) }
+    let(:shortcode)        { media_attachment.shortcode }
+
+    it 'returns shortcode' do
+      expect(media_attachment.to_param).to eq shortcode
+    end
+  end
+
   describe 'animated gif conversion' do
     let(:media) { MediaAttachment.create(account: Fabricate(:account), file: attachment_fixture('avatar.gif')) }
 
diff --git a/spec/models/notification_spec.rb b/spec/models/notification_spec.rb
index 97e8095cd..763b1523f 100644
--- a/spec/models/notification_spec.rb
+++ b/spec/models/notification_spec.rb
@@ -5,6 +5,74 @@ RSpec.describe Notification, type: :model do
     pending
   end
 
+  describe '#target_status' do
+    before do
+      allow(notification).to receive(:type).and_return(type)
+      allow(notification).to receive(:activity).and_return(activity)
+    end
+
+    let(:notification) { Fabricate(:notification) }
+    let(:status)       { instance_double('Status') }
+    let(:favourite)    { instance_double('Favourite') }
+    let(:mention)      { instance_double('Mention') }
+
+    context 'type is :reblog' do
+      let(:type)     { :reblog }
+      let(:activity) { status }
+
+      it 'calls activity.reblog' do
+        expect(activity).to receive(:reblog)
+        notification.target_status
+      end
+    end
+
+    context 'type is :favourite' do
+      let(:type)     { :favourite }
+      let(:activity) { favourite }
+
+      it 'calls activity.status' do
+        expect(activity).to receive(:status)
+        notification.target_status
+      end
+    end
+
+    context 'type is :mention' do
+      let(:type)     { :mention }
+      let(:activity) { mention }
+
+      it 'calls activity.status' do
+        expect(activity).to receive(:status)
+        notification.target_status
+      end
+    end
+  end
+
+  describe '#browserable?' do
+    let(:notification) { Fabricate(:notification) }
+
+    subject { notification.browserable? }
+
+    context 'type is :follow_request' do
+      before do
+        allow(notification).to receive(:type).and_return(:follow_request)
+      end
+
+      it 'returns false' do
+        is_expected.to be false
+      end
+    end
+
+    context 'type is not :follow_request' do
+      before do
+        allow(notification).to receive(:type).and_return(:else)
+      end
+
+      it 'returns true' do
+        is_expected.to be true
+      end
+    end
+  end
+
   describe '#type' do
     it 'returns :reblog for a Status' do
       notification = Notification.new(activity: Status.new)
@@ -26,4 +94,49 @@ RSpec.describe Notification, type: :model do
       expect(notification.type).to eq :follow
     end
   end
+
+  describe '.reload_stale_associations!' do
+    context 'account_ids are empty' do
+      let(:cached_items) { [] }
+
+      subject { described_class.reload_stale_associations!(cached_items) }
+
+      it 'returns nil' do
+        is_expected.to be nil
+      end
+    end
+
+    context 'account_ids are present' do
+      before do
+        allow(accounts_with_ids).to receive(:[]).with(stale_account1.id).and_return(account1)
+        allow(accounts_with_ids).to receive(:[]).with(stale_account2.id).and_return(account2)
+        allow(Account).to receive_message_chain(:where, :map, :to_h).and_return(accounts_with_ids)
+      end
+
+      let(:cached_items) do
+        [
+          Fabricate(:notification, activity: Fabricate(:status)),
+          Fabricate(:notification, activity: Fabricate(:follow)),
+        ]
+      end
+
+      let(:stale_account1) { cached_items[0].from_account }
+      let(:stale_account2) { cached_items[1].from_account }
+
+      let(:account1) { Fabricate(:account) }
+      let(:account2) { Fabricate(:account) }
+
+      let(:accounts_with_ids) { { account1.id => account1, account2.id => account2 } }
+
+      it 'reloads associations' do
+        expect(cached_items[0].from_account).to be stale_account1
+        expect(cached_items[1].from_account).to be stale_account2
+
+        described_class.reload_stale_associations!(cached_items)
+
+        expect(cached_items[0].from_account).to be account1
+        expect(cached_items[1].from_account).to be account2
+      end
+    end
+  end
 end
diff --git a/spec/models/remote_follow_spec.rb b/spec/models/remote_follow_spec.rb
new file mode 100644
index 000000000..72c580f9f
--- /dev/null
+++ b/spec/models/remote_follow_spec.rb
@@ -0,0 +1,67 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+RSpec.describe RemoteFollow do
+  before do
+    stub_request(:get, 'https://quitter.no/.well-known/webfinger?resource=acct:gargron@quitter.no').to_return(request_fixture('webfinger.txt'))
+  end
+
+  let(:attrs)         { nil }
+  let(:remote_follow) { described_class.new(attrs) }
+
+  describe '.initialize' do
+    subject { remote_follow.acct }
+
+    context 'attrs with acct' do
+      let(:attrs) { { acct: 'gargron@quitter.no' } }
+
+      it 'returns acct' do
+        is_expected.to eq 'gargron@quitter.no'
+      end
+    end
+
+    context 'attrs without acct' do
+      let(:attrs) { {} }
+
+      it do
+        is_expected.to be_nil
+      end
+    end
+  end
+
+  describe '#valid?' do
+    subject { remote_follow.valid? }
+
+    context 'attrs with acct' do
+      let(:attrs) { { acct: 'gargron@quitter.no' }}
+
+      it do
+        is_expected.to be true
+      end
+    end
+
+    context 'attrs without acct' do
+      let(:attrs) { { } }
+
+      it do
+        is_expected.to be false
+      end
+    end
+  end
+
+  describe '#subscribe_address_for' do
+    before do
+      remote_follow.valid?
+    end
+
+    let(:attrs)   { { acct: 'gargron@quitter.no' } }
+    let(:account) { Fabricate(:account, username: 'alice') }
+
+    subject { remote_follow.subscribe_address_for(account) }
+
+    it 'returns subscribe address' do
+      is_expected.to eq 'https://quitter.no/main/ostatussub?profile=alice%40cb6e6126.ngrok.io'
+    end
+  end
+end
diff --git a/spec/models/remote_profile_spec.rb b/spec/models/remote_profile_spec.rb
new file mode 100644
index 000000000..da5048f0a
--- /dev/null
+++ b/spec/models/remote_profile_spec.rb
@@ -0,0 +1,143 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+RSpec.describe RemoteProfile do
+  let(:remote_profile) { RemoteProfile.new(body) }
+  let(:body) do
+    <<-XML
+      <feed xmlns="http://www.w3.org/2005/Atom">
+      <author>John</author>
+    XML
+  end
+
+  describe '.initialize' do
+    it 'calls Nokogiri::XML.parse' do
+      expect(Nokogiri::XML).to receive(:parse).with(body, nil, 'utf-8')
+      RemoteProfile.new(body)
+    end
+
+    it 'sets document' do
+      remote_profile = RemoteProfile.new(body)
+      expect(remote_profile).not_to be nil
+    end
+  end
+
+  describe '#root' do
+    let(:document) { remote_profile.document }
+
+    it 'callse document.at_xpath' do
+      expect(document).to receive(:at_xpath).with(
+        '/atom:feed|/atom:entry',
+        atom: OStatus::TagManager::XMLNS
+      )
+
+      remote_profile.root
+    end
+  end
+
+  describe '#author' do
+    let(:root) { remote_profile.root }
+
+    it 'calls root.at_xpath' do
+      expect(root).to receive(:at_xpath).with(
+        './atom:author|./dfrn:owner',
+        atom: OStatus::TagManager::XMLNS,
+        dfrn: OStatus::TagManager::DFRN_XMLNS
+      )
+
+      remote_profile.author
+    end
+  end
+
+  describe '#hub_link' do
+    let(:root) { remote_profile.root }
+
+    it 'calls #link_href_from_xml' do
+      expect(remote_profile).to receive(:link_href_from_xml).with(root, 'hub')
+      remote_profile.hub_link
+    end
+  end
+
+  describe '#display_name' do
+    let(:author) { remote_profile.author }
+
+    it 'calls author.at_xpath.content' do
+      expect(author).to receive_message_chain(:at_xpath, :content).with(
+        './poco:displayName',
+        poco: OStatus::TagManager::POCO_XMLNS
+      ).with(no_args)
+
+      remote_profile.display_name
+    end
+  end
+
+  describe '#note' do
+    let(:author) { remote_profile.author }
+
+    it 'calls author.at_xpath.content' do
+      expect(author).to receive_message_chain(:at_xpath, :content).with(
+        './atom:summary|./poco:note',
+        atom: OStatus::TagManager::XMLNS,
+        poco: OStatus::TagManager::POCO_XMLNS
+      ).with(no_args)
+
+      remote_profile.note
+    end
+  end
+
+  describe '#scope' do
+    let(:author) { remote_profile.author }
+
+    it 'calls author.at_xpath.content' do
+      expect(author).to receive_message_chain(:at_xpath, :content).with(
+        './mastodon:scope',
+        mastodon: OStatus::TagManager::MTDN_XMLNS
+      ).with(no_args)
+
+      remote_profile.scope
+    end
+  end
+
+  describe '#avatar' do
+    let(:author) { remote_profile.author }
+
+    it 'calls #link_href_from_xml' do
+      expect(remote_profile).to receive(:link_href_from_xml).with(author, 'avatar')
+      remote_profile.avatar
+    end
+  end
+
+  describe '#header' do
+    let(:author) { remote_profile.author }
+
+    it 'calls #link_href_from_xml' do
+      expect(remote_profile).to receive(:link_href_from_xml).with(author, 'header')
+      remote_profile.header
+    end
+  end
+
+  describe '#locked?' do
+    before do
+      allow(remote_profile).to receive(:scope).and_return(scope)
+    end
+
+    subject { remote_profile.locked? }
+
+    context 'scope is private' do
+      let(:scope) { 'private' }
+
+      it 'returns true' do
+        is_expected.to be true
+      end
+    end
+
+    context 'scope is not private' do
+      let(:scope) { 'public' }
+
+      it 'returns false' do
+        is_expected.to be false
+      end
+    end
+  end
+end
diff --git a/spec/models/session_activation_spec.rb b/spec/models/session_activation_spec.rb
index 49c72fbd4..2aa695037 100644
--- a/spec/models/session_activation_spec.rb
+++ b/spec/models/session_activation_spec.rb
@@ -1,5 +1,127 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe SessionActivation, type: :model do
-  pending "add some examples to (or delete) #{__FILE__}"
+  describe '#detection' do
+    let(:session_activation) { Fabricate(:session_activation, user_agent: 'Chrome/62.0.3202.89') }
+
+    it 'sets a Browser instance as detection' do
+      expect(session_activation.detection).to be_kind_of Browser::Chrome
+    end
+  end
+
+  describe '#browser' do
+    before do
+      allow(session_activation).to receive(:detection).and_return(detection)
+    end
+
+    let(:detection)          { double(id: 1) }
+    let(:session_activation) { Fabricate(:session_activation) }
+
+    it 'returns detection.id' do
+      expect(session_activation.browser).to be 1
+    end
+  end
+
+  describe '#platform' do
+    before do
+      allow(session_activation).to receive(:detection).and_return(detection)
+    end
+
+    let(:session_activation) { Fabricate(:session_activation) }
+    let(:detection)          { double(platform: double(id: 1)) }
+
+    it 'returns detection.platform.id' do
+      expect(session_activation.platform).to be 1
+    end
+  end
+
+  describe '.active?' do
+    subject { described_class.active?(id) }
+
+    context 'id is absent' do
+      let(:id) { nil }
+
+      it 'returns nil' do
+        is_expected.to be nil
+      end
+    end
+
+    context 'id is present' do
+      let(:id) { '1' }
+      let!(:session_activation) { Fabricate(:session_activation, session_id: id) }
+
+      context 'id exists as session_id' do
+        it 'returns true' do
+          is_expected.to be true
+        end
+      end
+
+      context 'id does not exist as session_id' do
+        before do
+          session_activation.update!(session_id: '2')
+        end
+
+        it 'returns false' do
+          is_expected.to be false
+        end
+      end
+    end
+  end
+
+  describe '.activate' do
+    let(:options) { { user: Fabricate(:user), session_id: '1' } }
+
+    it 'calls create! and purge_old' do
+      expect(described_class).to receive(:create!).with(options)
+      expect(described_class).to receive(:purge_old)
+      described_class.activate(options)
+    end
+
+    it 'returns an instance of SessionActivation' do
+      expect(described_class.activate(options)).to be_kind_of SessionActivation
+    end
+  end
+
+  describe '.deactivate' do
+    context 'id is absent' do
+      let(:id) { nil }
+
+      it 'returns nil' do
+        expect(described_class.deactivate(id)).to be nil
+      end
+    end
+
+    context 'id exists' do
+      let(:id) { '1' }
+
+      it 'calls where.destroy_all' do
+        expect(described_class).to receive_message_chain(:where, :destroy_all)
+          .with(session_id: id).with(no_args)
+
+        described_class.deactivate(id)
+      end
+    end
+  end
+
+  describe '.purge_old' do
+    it 'calls order.offset.destroy_all' do
+      expect(described_class).to receive_message_chain(:order, :offset, :destroy_all)
+        .with('created_at desc').with(Rails.configuration.x.max_session_activations).with(no_args)
+
+      described_class.purge_old
+    end
+  end
+
+  describe '.exclusive' do
+    let(:id) { '1' }
+
+    it 'calls where.destroy_all' do
+      expect(described_class).to receive_message_chain(:where, :destroy_all)
+        .with('session_id != ?', id).with(no_args)
+
+      described_class.exclusive(id)
+    end
+  end
 end
diff --git a/spec/models/setting_spec.rb b/spec/models/setting_spec.rb
new file mode 100644
index 000000000..e99dfc0d7
--- /dev/null
+++ b/spec/models/setting_spec.rb
@@ -0,0 +1,184 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+RSpec.describe Setting, type: :model do
+  describe '#to_param' do
+    let(:setting) { Fabricate(:setting, var: var) }
+    let(:var)     { 'var' }
+
+    it 'returns setting.var' do
+      expect(setting.to_param).to eq var
+    end
+  end
+
+  describe '.[]' do
+    before do
+      allow(described_class).to receive(:rails_initialized?).and_return(rails_initialized)
+    end
+
+    let(:key) { 'key' }
+
+    context 'rails_initialized? is falsey' do
+      let(:rails_initialized) { false }
+
+      it 'calls RailsSettings::Base#[]' do
+        expect(RailsSettings::Base).to receive(:[]).with(key)
+        described_class[key]
+      end
+    end
+
+    context 'rails_initialized? is truthy' do
+      before do
+        allow(RailsSettings::Base).to receive(:cache_key).with(key, nil).and_return(cache_key)
+      end
+
+      let(:rails_initialized) { true }
+      let(:cache_key)         { 'cache-key' }
+      let(:cache_value)       { 'cache-value' }
+
+      it 'calls not RailsSettings::Base#[]' do
+        expect(RailsSettings::Base).not_to receive(:[]).with(key)
+        described_class[key]
+      end
+
+      it 'calls Rails.cache.fetch' do
+        expect(Rails).to receive_message_chain(:cache, :fetch).with(cache_key)
+        described_class[key]
+      end
+
+      context 'Rails.cache does not exists' do
+        before do
+          allow(RailsSettings::Settings).to receive(:object).with(key).and_return(object)
+          allow(described_class).to receive(:default_settings).and_return(default_settings)
+          allow_any_instance_of(Settings::ScopedSettings).to receive(:thing_scoped).and_return(records)
+          Rails.cache.clear(cache_key)
+        end
+
+        let(:object)           { nil }
+        let(:default_value)    { 'default_value' }
+        let(:default_settings) { { key => default_value } }
+        let(:records)          { [Fabricate(:setting, var: key, value: nil)] }
+
+        it 'calls RailsSettings::Settings.object' do
+          expect(RailsSettings::Settings).to receive(:object).with(key)
+          described_class[key]
+        end
+
+        context 'RailsSettings::Settings.object returns truthy' do
+          let(:object) { db_val }
+          let(:db_val) { double(value: 'db_val') }
+
+          context 'default_value is a Hash' do
+            let(:default_value) { { default_value: 'default_value' } }
+
+            it 'calls default_value.with_indifferent_access.merge!' do
+              expect(default_value).to receive_message_chain(:with_indifferent_access, :merge!)
+                .with(db_val.value)
+
+              described_class[key]
+            end
+          end
+
+          context 'default_value is not a Hash' do
+            let(:default_value) { 'default_value' }
+
+            it 'returns db_val.value' do
+              expect(described_class[key]).to be db_val.value
+            end
+          end
+        end
+
+        context 'RailsSettings::Settings.object returns falsey' do
+          let(:object) { nil }
+
+          it 'returns default_settings[key]' do
+            expect(described_class[key]).to be default_settings[key]
+          end
+        end
+      end
+
+      context 'Rails.cache exists' do
+        before do
+          Rails.cache.write(cache_key, cache_value)
+        end
+
+        it 'returns the cached value' do
+          expect(described_class[key]).to eq cache_value
+        end
+      end
+    end
+  end
+
+  describe '.all_as_records' do
+    before do
+      allow_any_instance_of(Settings::ScopedSettings).to receive(:thing_scoped).and_return(records)
+      allow(described_class).to receive(:default_settings).and_return(default_settings)
+    end
+
+    let(:key)              { 'key' }
+    let(:default_value)    { 'default_value' }
+    let(:default_settings) { { key => default_value } }
+    let(:original_setting) { Fabricate(:setting, var: key, value: nil) }
+    let(:records)          { [original_setting] }
+
+    it 'returns a Hash' do
+      expect(described_class.all_as_records).to be_kind_of Hash
+    end
+
+    context 'records includes Setting with var as the key' do
+      let(:records) { [original_setting] }
+
+      it 'includes the original Setting' do
+        setting = described_class.all_as_records[key]
+        expect(setting).to eq original_setting
+      end
+    end
+
+    context 'records includes nothing' do
+      let(:records) { [] }
+
+      context 'default_value is not a Hash' do
+        it 'includes Setting with value of default_value' do
+          setting = described_class.all_as_records[key]
+
+          expect(setting).to be_kind_of Setting
+          expect(setting).to have_attributes(var: key)
+          expect(setting).to have_attributes(value: 'default_value')
+        end
+      end
+
+      context 'default_value is a Hash' do
+        let(:default_value) { { 'foo' => 'fuga' } }
+
+        it 'returns {}' do
+          expect(described_class.all_as_records).to eq({})
+        end
+      end
+    end
+  end
+
+  describe '.default_settings' do
+    before do
+      allow(RailsSettings::Default).to receive(:enabled?).and_return(enabled)
+    end
+
+    subject { described_class.default_settings }
+
+    context 'RailsSettings::Default.enabled? is false' do
+      let(:enabled) { false }
+
+      it 'returns {}' do
+        is_expected.to eq({})
+      end
+    end
+
+    context 'RailsSettings::Settings.enabled? is true' do
+      let(:enabled) { true }
+
+      it 'returns instance of RailsSettings::Default' do
+        is_expected.to be_kind_of RailsSettings::Default
+      end
+    end
+  end
+end
diff --git a/spec/models/site_upload_spec.rb b/spec/models/site_upload_spec.rb
index 8745d54b8..f7ea06921 100644
--- a/spec/models/site_upload_spec.rb
+++ b/spec/models/site_upload_spec.rb
@@ -1,5 +1,13 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe SiteUpload, type: :model do
+  describe '#cache_key' do
+    let(:site_upload) { SiteUpload.new(var: 'var') }
 
+    it 'returns cache_key' do
+      expect(site_upload.cache_key).to eq 'site_uploads/var'
+    end
+  end
 end
diff --git a/spec/models/status_spec.rb b/spec/models/status_spec.rb
index 12e857169..89ad3adcf 100644
--- a/spec/models/status_spec.rb
+++ b/spec/models/status_spec.rb
@@ -47,8 +47,27 @@ RSpec.describe Status, type: :model do
   end
 
   describe '#verb' do
-    it 'is always post' do
-      expect(subject.verb).to be :post
+    context 'if destroyed?' do
+      it 'returns :delete' do
+        subject.destroy!
+        expect(subject.verb).to be :delete
+      end
+    end
+
+    context 'unless destroyed?' do
+      context 'if reblog?' do
+        it 'returns :share' do
+          subject.reblog = other
+          expect(subject.verb).to be :share
+        end
+      end
+
+      context 'unless reblog?' do
+        it 'returns :post' do
+          subject.reblog = nil
+          expect(subject.verb).to be :post
+        end
+      end
     end
   end
 
@@ -69,6 +88,36 @@ RSpec.describe Status, type: :model do
     end
   end
 
+  describe '#hidden?' do
+    context 'if private_visibility?' do
+      it 'returns true' do
+        subject.visibility = :private
+        expect(subject.hidden?).to be true
+      end
+    end
+
+    context 'if direct_visibility?' do
+      it 'returns true' do
+        subject.visibility = :direct
+        expect(subject.hidden?).to be true
+      end
+    end
+
+    context 'if public_visibility?' do
+      it 'returns false' do
+        subject.visibility = :public
+        expect(subject.hidden?).to be false
+      end
+    end
+
+    context 'if unlisted_visibility?' do
+      it 'returns false' do
+        subject.visibility = :unlisted
+        expect(subject.hidden?).to be false
+      end
+    end
+  end
+
   describe '#content' do
     it 'returns the text of the status if it is not a reblog' do
       expect(subject.content).to eql subject.text
diff --git a/spec/models/stream_entry_spec.rb b/spec/models/stream_entry_spec.rb
index 3b7ff5143..8f8bfbd58 100644
--- a/spec/models/stream_entry_spec.rb
+++ b/spec/models/stream_entry_spec.rb
@@ -6,6 +6,121 @@ RSpec.describe StreamEntry, type: :model do
   let(:status)    { Fabricate(:status, account: alice) }
   let(:reblog)    { Fabricate(:status, account: bob, reblog: status) }
   let(:reply)     { Fabricate(:status, account: bob, thread: status) }
+  let(:stream_entry) { Fabricate(:stream_entry, activity: activity) }
+  let(:activity)     { reblog }
+
+  describe '#object_type' do
+    before do
+      allow(stream_entry).to receive(:orphaned?).and_return(orphaned)
+      allow(stream_entry).to receive(:targeted?).and_return(targeted)
+    end
+
+    subject { stream_entry.object_type }
+
+    context 'orphaned? is true' do
+      let(:orphaned) { true }
+      let(:targeted) { false }
+
+      it 'returns :activity' do
+        is_expected.to be :activity
+      end
+    end
+
+    context 'targeted? is true' do
+      let(:orphaned) { false }
+      let(:targeted) { true }
+
+      it 'returns :activity' do
+        is_expected.to be :activity
+      end
+    end
+
+    context 'orphaned? and targeted? are false' do
+      let(:orphaned) { false }
+      let(:targeted) { false }
+
+      context 'activity is reblog' do
+        let(:activity) { reblog }
+
+        it 'returns :note' do
+          is_expected.to be :note
+        end
+      end
+
+      context 'activity is reply' do
+        let(:activity) { reply }
+
+        it 'returns :comment' do
+          is_expected.to be :comment
+        end
+      end
+    end
+  end
+
+  describe '#verb' do
+    before do
+      allow(stream_entry).to receive(:orphaned?).and_return(orphaned)
+    end
+
+    subject { stream_entry.verb }
+
+    context 'orphaned? is true' do
+      let(:orphaned) { true }
+
+      it 'returns :delete' do
+        is_expected.to be :delete
+      end
+    end
+
+    context 'orphaned? is false' do
+      let(:orphaned) { false }
+
+      context 'activity is reblog' do
+        let(:activity) { reblog }
+
+        it 'returns :share' do
+          is_expected.to be :share
+        end
+      end
+
+      context 'activity is reply' do
+        let(:activity) { reply }
+
+        it 'returns :post' do
+          is_expected.to be :post
+        end
+      end
+    end
+  end
+
+  describe '#mentions' do
+    before do
+      allow(stream_entry).to receive(:orphaned?).and_return(orphaned)
+    end
+
+    subject { stream_entry.mentions }
+
+    context 'orphaned? is true' do
+      let(:orphaned) { true }
+
+      it 'returns []' do
+        is_expected.to eq []
+      end
+    end
+
+    context 'orphaned? is false' do
+      before do
+        reblog.mentions << Fabricate(:mention, account: alice)
+        reblog.mentions << Fabricate(:mention, account: bob)
+      end
+
+      let(:orphaned) { false }
+
+      it 'returns [Account] includes alice and bob' do
+        is_expected.to eq [alice, bob]
+      end
+    end
+  end
 
   describe '#targeted?' do
     it 'returns true for a reblog' do
diff --git a/spec/models/tag_spec.rb b/spec/models/tag_spec.rb
index f727fa1dd..1ca50cc29 100644
--- a/spec/models/tag_spec.rb
+++ b/spec/models/tag_spec.rb
@@ -35,6 +35,13 @@ RSpec.describe Tag, type: :model do
     end
   end
 
+  describe '#to_param' do
+    it 'returns name' do
+      tag = Fabricate(:tag, name: 'foo')
+      expect(tag.to_param).to eq 'foo'
+    end
+  end
+
   describe '.search_for' do
     it 'finds tag records with matching names' do
       tag = Fabricate(:tag, name: "match")
diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb
index 99aeca01b..77a12c26d 100644
--- a/spec/models/user_spec.rb
+++ b/spec/models/user_spec.rb
@@ -177,27 +177,10 @@ RSpec.describe User, type: :model do
     end
   end
 
-  describe '#setting_auto_play_gif' do
-    it 'returns auto-play gif setting' do
+  describe 'settings' do
+    it 'is instance of Settings::ScopedSettings' do
       user = Fabricate(:user)
-      user.settings[:auto_play_gif] = false
-      expect(user.setting_auto_play_gif).to eq false
-    end
-  end
-  
-  describe '#setting_system_font_ui' do
-    it 'returns system font ui setting' do
-      user = Fabricate(:user)
-      user.settings[:system_font_ui] = false
-      expect(user.setting_system_font_ui).to eq false
-    end
-  end
-
-  describe '#setting_boost_modal' do
-    it 'returns boost modal setting' do
-      user = Fabricate(:user)
-      user.settings[:boost_modal] = false
-      expect(user.setting_boost_modal).to eq false
+      expect(user.settings).to be_kind_of Settings::ScopedSettings
     end
   end
 
@@ -219,22 +202,6 @@ RSpec.describe User, type: :model do
     end
   end
 
-  describe '#setting_unfollow_modal' do
-    it 'returns unfollow modal setting' do
-      user = Fabricate(:user)
-      user.settings[:unfollow_modal] = true
-      expect(user.setting_unfollow_modal).to eq true
-    end
-  end
-
-  describe '#setting_delete_modal' do
-    it 'returns delete modal setting' do
-      user = Fabricate(:user)
-      user.settings[:delete_modal] = false
-      expect(user.setting_delete_modal).to eq false
-    end
-  end
-
   describe 'whitelist' do
     around(:each) do |example|
       old_whitelist = Rails.configuration.x.email_whitelist
diff --git a/spec/policies/status_policy_spec.rb b/spec/policies/status_policy_spec.rb
index bacb8fd9e..a90e22aad 100644
--- a/spec/policies/status_policy_spec.rb
+++ b/spec/policies/status_policy_spec.rb
@@ -71,6 +71,12 @@ RSpec.describe StatusPolicy, type: :model do
 
       expect(subject).to_not permit(viewer, status)
     end
+
+    it 'denies access when local-only and the viewer is not logged in' do
+      allow(status).to receive(:local_only?) { true }
+
+      expect(subject).to_not permit(nil, status)
+    end
   end
 
   permissions :reblog? do
diff --git a/spec/services/activitypub/fetch_remote_status_service_spec.rb b/spec/services/activitypub/fetch_remote_status_service_spec.rb
index ebf422392..51f3fe3a1 100644
--- a/spec/services/activitypub/fetch_remote_status_service_spec.rb
+++ b/spec/services/activitypub/fetch_remote_status_service_spec.rb
@@ -27,7 +27,7 @@ RSpec.describe ActivityPub::FetchRemoteStatusService do
 
       it 'creates status' do
         status = sender.statuses.first
-        
+
         expect(status).to_not be_nil
         expect(status.text).to eq 'Lorem ipsum'
       end
diff --git a/spec/services/after_block_service_spec.rb b/spec/services/after_block_service_spec.rb
index 65f36c7d1..1b115c938 100644
--- a/spec/services/after_block_service_spec.rb
+++ b/spec/services/after_block_service_spec.rb
@@ -18,8 +18,8 @@ RSpec.describe AfterBlockService do
     end
 
     it "clears account's statuses" do
-      FeedManager.instance.push(:home, account, status)
-      FeedManager.instance.push(:home, account, other_account_status)
+      FeedManager.instance.push_to_home(account, status)
+      FeedManager.instance.push_to_home(account, other_account_status)
 
       is_expected.to change {
         Redis.current.zrange(home_timeline_key, 0, -1)
diff --git a/spec/services/batched_remove_status_service_spec.rb b/spec/services/batched_remove_status_service_spec.rb
index c82c45e09..437da2a9d 100644
--- a/spec/services/batched_remove_status_service_spec.rb
+++ b/spec/services/batched_remove_status_service_spec.rb
@@ -30,11 +30,11 @@ RSpec.describe BatchedRemoveStatusService do
   end
 
   it 'removes statuses from author\'s home feed' do
-    expect(Feed.new(:home, alice).get(10)).to_not include([status1.id, status2.id])
+    expect(HomeFeed.new(alice).get(10)).to_not include([status1.id, status2.id])
   end
 
   it 'removes statuses from local follower\'s home feed' do
-    expect(Feed.new(:home, jeff).get(10)).to_not include([status1.id, status2.id])
+    expect(HomeFeed.new(jeff).get(10)).to_not include([status1.id, status2.id])
   end
 
   it 'notifies streaming API of followers' do
diff --git a/spec/services/fan_out_on_write_service_spec.rb b/spec/services/fan_out_on_write_service_spec.rb
index 6ee225c4c..764318e34 100644
--- a/spec/services/fan_out_on_write_service_spec.rb
+++ b/spec/services/fan_out_on_write_service_spec.rb
@@ -19,12 +19,12 @@ RSpec.describe FanOutOnWriteService do
   end
 
   it 'delivers status to home timeline' do
-    expect(Feed.new(:home, author).get(10).map(&:id)).to include status.id
+    expect(HomeFeed.new(author).get(10).map(&:id)).to include status.id
   end
 
   it 'delivers status to local followers' do
     pending 'some sort of problem in test environment causes this to sometimes fail'
-    expect(Feed.new(:home, follower).get(10).map(&:id)).to include status.id
+    expect(HomeFeed.new(follower).get(10).map(&:id)).to include status.id
   end
 
   it 'delivers status to hashtag' do
diff --git a/spec/services/mute_service_spec.rb b/spec/services/mute_service_spec.rb
index 800140b6f..2b3e3e152 100644
--- a/spec/services/mute_service_spec.rb
+++ b/spec/services/mute_service_spec.rb
@@ -18,8 +18,8 @@ RSpec.describe MuteService do
     end
 
     it "clears account's statuses" do
-      FeedManager.instance.push(:home, account, status)
-      FeedManager.instance.push(:home, account, other_account_status)
+      FeedManager.instance.push_to_home(account, status)
+      FeedManager.instance.push_to_home(account, other_account_status)
 
       is_expected.to change {
         Redis.current.zrange(home_timeline_key, 0, -1)
diff --git a/spec/services/notify_service_spec.rb b/spec/services/notify_service_spec.rb
index 250a880a2..a8ebc16b8 100644
--- a/spec/services/notify_service_spec.rb
+++ b/spec/services/notify_service_spec.rb
@@ -67,6 +67,39 @@ RSpec.describe NotifyService do
       is_expected.to_not change(Notification, :count)
     end
   end
+  
+  context 'for direct messages' do
+    let(:activity) { Fabricate(:mention, account: recipient, status: Fabricate(:status, account: sender, visibility: :direct)) }
+
+    before do
+      user.settings.interactions = user.settings.interactions.merge('must_be_following_dm' => enabled)
+    end
+
+    context 'if recipient is supposed to be following sender' do
+      let(:enabled) { true }
+
+      it 'does not notify' do
+        is_expected.to_not change(Notification, :count)
+      end
+
+      context 'if the message chain initiated by recipient' do
+        let(:reply_to) { Fabricate(:status, account: recipient) }
+        let(:activity) { Fabricate(:mention, account: recipient, status: Fabricate(:status, account: sender, visibility: :direct, thread: reply_to)) }
+
+        it 'does notify' do
+          is_expected.to change(Notification, :count)
+        end
+      end
+    end
+
+    context 'if recipient is NOT supposed to be following sender' do
+      let(:enabled) { false }
+
+      it 'does notify' do
+        is_expected.to change(Notification, :count)
+      end
+    end
+  end
 
   context do
     let(:asshole)  { Fabricate(:account, username: 'asshole') }
diff --git a/spec/services/remove_status_service_spec.rb b/spec/services/remove_status_service_spec.rb
index b60015928..5bb75b820 100644
--- a/spec/services/remove_status_service_spec.rb
+++ b/spec/services/remove_status_service_spec.rb
@@ -25,11 +25,11 @@ RSpec.describe RemoveStatusService do
   end
 
   it 'removes status from author\'s home feed' do
-    expect(Feed.new(:home, alice).get(10)).to_not include(@status.id)
+    expect(HomeFeed.new(alice).get(10)).to_not include(@status.id)
   end
 
   it 'removes status from local follower\'s home feed' do
-    expect(Feed.new(:home, jeff).get(10)).to_not include(@status.id)
+    expect(HomeFeed.new(jeff).get(10)).to_not include(@status.id)
   end
 
   it 'sends PuSH update to PuSH subscribers' do
diff --git a/spec/support/matchers/model/model_have_error_on_field.rb b/spec/support/matchers/model/model_have_error_on_field.rb
index 5d5fe1c7b..a5dfbf457 100644
--- a/spec/support/matchers/model/model_have_error_on_field.rb
+++ b/spec/support/matchers/model/model_have_error_on_field.rb
@@ -9,7 +9,7 @@ RSpec::Matchers.define :model_have_error_on_field do |expected|
 
   failure_message do |record|
     keys = record.errors.keys
-    
+
     "expect record.errors(#{keys}) to include #{expected}"
   end
 end
diff --git a/spec/workers/feed_insert_worker_spec.rb b/spec/workers/feed_insert_worker_spec.rb
index 71a3dea00..3509f1f50 100644
--- a/spec/workers/feed_insert_worker_spec.rb
+++ b/spec/workers/feed_insert_worker_spec.rb
@@ -11,41 +11,41 @@ describe FeedInsertWorker do
 
     context 'when there are no records' do
       it 'skips push with missing status' do
-        instance = double(push: nil)
+        instance = double(push_to_home: nil)
         allow(FeedManager).to receive(:instance).and_return(instance)
         result = subject.perform(nil, follower.id)
 
         expect(result).to eq true
-        expect(instance).not_to have_received(:push)
+        expect(instance).not_to have_received(:push_to_home)
       end
 
       it 'skips push with missing account' do
-        instance = double(push: nil)
+        instance = double(push_to_home: nil)
         allow(FeedManager).to receive(:instance).and_return(instance)
         result = subject.perform(status.id, nil)
 
         expect(result).to eq true
-        expect(instance).not_to have_received(:push)
+        expect(instance).not_to have_received(:push_to_home)
       end
     end
 
     context 'when there are real records' do
       it 'skips the push when there is a filter' do
-        instance = double(push: nil, filter?: true)
+        instance = double(push_to_home: nil, filter?: true)
         allow(FeedManager).to receive(:instance).and_return(instance)
         result = subject.perform(status.id, follower.id)
 
         expect(result).to be_nil
-        expect(instance).not_to have_received(:push)
+        expect(instance).not_to have_received(:push_to_home)
       end
 
       it 'pushes the status onto the home timeline without filter' do
-        instance = double(push: nil, filter?: false)
+        instance = double(push_to_home: nil, filter?: false)
         allow(FeedManager).to receive(:instance).and_return(instance)
         result = subject.perform(status.id, follower.id)
 
         expect(result).to be_nil
-        expect(instance).to have_received(:push).with(:home, follower, status)
+        expect(instance).to have_received(:push_to_home).with(follower, status)
       end
     end
   end