about summary refs log tree commit diff
path: root/spec
diff options
context:
space:
mode:
authorClaire <claire.github-309c@sitedethib.com>2023-03-05 20:43:48 +0100
committerClaire <claire.github-309c@sitedethib.com>2023-03-05 20:46:56 +0100
commit7623e181247b4d2227b7774143514f6e1ca9253b (patch)
treeb9a82790b7cb1f075769e7e5ca757b2ede322620 /spec
parentbb4e211c86270de6de8a78da96295208ee77dce1 (diff)
parentdfa9843ac85d04e1facb2f757fd9288d8bb9fb2c (diff)
Merge branch 'main' into glitch-soc/merge-upstream
Conflicts:
- `README.md`:
  Upstream README has been changed, but we have a completely different one.
  Kept our `README.md`.
- `lib/sanitize_ext/sanitize_config.rb`:
  Upstream added support for more incoming HTML tags (a large subset of what
  glitch-soc accepts).
  Change the code style to match upstream's but otherwise do not change our
  code.
- `spec/lib/sanitize_config_spec.rb`:
  Upstream added support for more incoming HTML tags (a large subset of what
  glitch-soc accepts).
  Kept our version, since the tests are mostly glitch-soc's, except for cases
  which are purposefuly different.
Diffstat (limited to 'spec')
-rw-r--r--spec/controllers/admin/account_actions_controller_spec.rb23
-rw-r--r--spec/controllers/admin/announcements_controller_spec.rb21
-rw-r--r--spec/controllers/admin/follow_recommendations_controller_spec.rb21
-rw-r--r--spec/controllers/admin/ip_blocks_controller_spec.rb21
-rw-r--r--spec/controllers/admin/relationships_controller_spec.rb23
-rw-r--r--spec/controllers/admin/relays_controller_spec.rb21
-rw-r--r--spec/controllers/admin/reports/actions_controller_spec.rb27
-rw-r--r--spec/controllers/admin/rules_controller_spec.rb21
-rw-r--r--spec/controllers/admin/settings/about_controller_spec.rb21
-rw-r--r--spec/controllers/admin/settings/appearance_controller_spec.rb21
-rw-r--r--spec/controllers/admin/settings/content_retention_controller_spec.rb21
-rw-r--r--spec/controllers/admin/settings/discovery_controller_spec.rb21
-rw-r--r--spec/controllers/admin/settings/registrations_controller_spec.rb21
-rw-r--r--spec/controllers/admin/site_uploads_controller_spec.rb23
-rw-r--r--spec/controllers/admin/trends/links/preview_card_providers_controller_spec.rb21
-rw-r--r--spec/controllers/admin/trends/links_controller_spec.rb21
-rw-r--r--spec/controllers/admin/trends/statuses_controller_spec.rb21
-rw-r--r--spec/controllers/admin/trends/tags_controller_spec.rb21
-rw-r--r--spec/controllers/admin/warning_presets_controller_spec.rb21
-rw-r--r--spec/controllers/admin/webhooks/secrets_controller_spec.rb23
-rw-r--r--spec/controllers/admin/webhooks_controller_spec.rb21
-rw-r--r--spec/controllers/api/v1/accounts/familiar_followers_controller_spec.rb23
-rw-r--r--spec/controllers/api/v1/accounts/featured_tags_controller_spec.rb23
-rw-r--r--spec/controllers/api/v1/accounts/identity_proofs_controller_spec.rb23
-rw-r--r--spec/controllers/api/v1/accounts/lookup_controller_spec.rb23
-rw-r--r--spec/controllers/api/v1/admin/canonical_email_blocks_controller_spec.rb23
-rw-r--r--spec/controllers/api/v1/admin/dimensions_controller_spec.rb23
-rw-r--r--spec/controllers/api/v1/admin/email_domain_blocks_controller_spec.rb23
-rw-r--r--spec/controllers/api/v1/admin/ip_blocks_controller_spec.rb23
-rw-r--r--spec/controllers/api/v1/admin/measures_controller_spec.rb23
-rw-r--r--spec/controllers/api/v1/admin/retention_controller_spec.rb23
-rw-r--r--spec/controllers/api/v1/admin/trends/links_controller_spec.rb23
-rw-r--r--spec/controllers/api/v1/admin/trends/statuses_controller_spec.rb23
-rw-r--r--spec/controllers/api/v1/admin/trends/tags_controller_spec.rb23
-rw-r--r--spec/controllers/api/v1/directories_controller_spec.rb23
-rw-r--r--spec/controllers/api/v1/featured_tags/suggestions_controller_spec.rb23
-rw-r--r--spec/controllers/api/v1/featured_tags_controller_spec.rb23
-rw-r--r--spec/controllers/api/v1/instances/domain_blocks_controller_spec.rb16
-rw-r--r--spec/controllers/api/v1/instances/extended_descriptions_controller_spec.rb15
-rw-r--r--spec/controllers/api/v1/instances/privacy_policies_controller_spec.rb15
-rw-r--r--spec/controllers/api/v1/instances/rules_controller_spec.rb15
-rw-r--r--spec/controllers/api/v1/preferences_controller_spec.rb23
-rw-r--r--spec/controllers/api/v1/scheduled_statuses_controller_spec.rb23
-rw-r--r--spec/controllers/api/v1/statuses/translations_controller_spec.rb33
-rw-r--r--spec/controllers/api/v1/trends/links_controller_spec.rb15
-rw-r--r--spec/controllers/api/v1/trends/statuses_controller_spec.rb15
-rw-r--r--spec/controllers/relationships_controller_spec.rb11
-rw-r--r--spec/controllers/settings/aliases_controller_spec.rb21
-rw-r--r--spec/controllers/settings/exports/blocked_domains_controller_spec.rb20
-rw-r--r--spec/controllers/settings/exports/lists_controller_spec.rb21
-rw-r--r--spec/controllers/settings/login_activities_controller_spec.rb20
-rw-r--r--spec/controllers/settings/migration/redirects_controller_spec.rb20
-rw-r--r--spec/controllers/settings/pictures_controller_spec.rb22
-rw-r--r--spec/controllers/settings/preferences/appearance_controller_spec.rb20
-rw-r--r--spec/fabricators/account_alias_fabricator.rb7
-rw-r--r--spec/fabricators/account_deletion_request_fabricator.rb5
-rw-r--r--spec/fabricators/account_migration_fabricator.rb1
-rw-r--r--spec/fabricators/account_moderation_note_fabricator.rb3
-rw-r--r--spec/fabricators/account_pin_fabricator.rb5
-rw-r--r--spec/fabricators/account_stat_fabricator.rb8
-rw-r--r--spec/fabricators/account_tag_stat_fabricator.rb5
-rw-r--r--spec/fabricators/account_warning_preset_fabricator.rb2
-rw-r--r--spec/fabricators/admin_action_log_fabricator.rb2
-rw-r--r--spec/fabricators/announcement_mute_fabricator.rb6
-rw-r--r--spec/fabricators/announcement_reaction_fabricator.rb7
-rw-r--r--spec/fabricators/conversation_account_fabricator.rb8
-rw-r--r--spec/fabricators/conversation_mute_fabricator.rb4
-rw-r--r--spec/fabricators/custom_emoji_category_fabricator.rb5
-rw-r--r--spec/fabricators/encrypted_message_fabricator.rb10
-rw-r--r--spec/fabricators/featured_tag_fabricator.rb8
-rw-r--r--spec/fabricators/follow_recommendation_suppression_fabricator.rb5
-rw-r--r--spec/fabricators/identity_fabricator.rb2
-rw-r--r--spec/fabricators/import_fabricator.rb4
-rw-r--r--spec/fabricators/ip_block_fabricator.rb8
-rw-r--r--spec/fabricators/list_account_fabricator.rb6
-rw-r--r--spec/fabricators/one_time_key_fabricator.rb2
-rw-r--r--spec/fabricators/preview_card_provider_fabricator.rb5
-rw-r--r--spec/fabricators/setting_fabricator.rb1
-rw-r--r--spec/fabricators/site_upload_fabricator.rb1
-rw-r--r--spec/fabricators/status_edit_fabricator.rb9
-rw-r--r--spec/fabricators/status_stat_fabricator.rb8
-rw-r--r--spec/fabricators/unavailable_domain_fabricator.rb2
-rw-r--r--spec/fabricators/user_invite_request_fabricator.rb6
-rw-r--r--spec/fabricators/web_setting_fabricator.rb4
-rw-r--r--spec/fabricators_spec.rb12
-rw-r--r--spec/helpers/admin/account_moderation_notes_helper_spec.rb10
-rw-r--r--spec/helpers/admin/dashboard_helper_spec.rb69
-rw-r--r--spec/helpers/languages_helper_spec.rb48
-rw-r--r--spec/helpers/settings_helper_spec.rb37
-rw-r--r--spec/helpers/statuses_helper_spec.rb62
-rw-r--r--spec/lib/translation_service/deepl_spec.rb100
-rw-r--r--spec/lib/translation_service/libre_translate_spec.rb71
-rw-r--r--spec/mailers/user_mailer_spec.rb48
-rw-r--r--spec/models/account_spec.rb6
-rw-r--r--spec/models/account_warning_preset_spec.rb17
-rw-r--r--spec/models/appeal_spec.rb35
-rw-r--r--spec/models/block_spec.rb5
-rw-r--r--spec/models/custom_emoji_category_spec.rb11
-rw-r--r--spec/models/domain_allow_spec.rb15
-rw-r--r--spec/models/domain_block_spec.rb5
-rw-r--r--spec/models/email_domain_block_spec.rb7
-rw-r--r--spec/models/extended_description_spec.rb29
-rw-r--r--spec/models/follow_spec.rb5
-rw-r--r--spec/models/import_spec.rb5
-rw-r--r--spec/models/ip_block_spec.rb12
-rw-r--r--spec/models/marker_spec.rb13
-rw-r--r--spec/models/mention_spec.rb5
-rw-r--r--spec/models/one_time_key_spec.rb19
-rw-r--r--spec/models/poll_spec.rb29
-rw-r--r--spec/models/preview_card_provider_spec.rb42
-rw-r--r--spec/models/privacy_policy_spec.rb28
-rw-r--r--spec/models/report_spec.rb6
-rw-r--r--spec/models/rule_spec.rb16
-rw-r--r--spec/models/status_edit_spec.rb10
-rw-r--r--spec/models/status_spec.rb79
-rw-r--r--spec/models/trends/tags_spec.rb4
-rw-r--r--spec/policies/account_policy_spec.rb40
-rw-r--r--spec/policies/account_warning_preset_policy_spec.rb24
-rw-r--r--spec/policies/admin/status_policy_spec.rb51
-rw-r--r--spec/policies/announcement_policy_spec.rb24
-rw-r--r--spec/policies/appeal_policy_spec.rb51
-rw-r--r--spec/policies/canonical_email_block_policy_spec.rb24
-rw-r--r--spec/policies/delivery_policy_spec.rb24
-rw-r--r--spec/policies/email_domain_block_policy_spec.rb2
-rw-r--r--spec/policies/follow_recommendation_policy_spec.rb24
-rw-r--r--spec/policies/ip_block_policy_spec.rb24
-rw-r--r--spec/policies/preview_card_policy_spec.rb24
-rw-r--r--spec/policies/preview_card_provider_policy_spec.rb24
-rw-r--r--spec/policies/rule_policy_spec.rb24
-rw-r--r--spec/policies/settings_policy_spec.rb2
-rw-r--r--spec/policies/status_policy_spec.rb8
-rw-r--r--spec/policies/tag_policy_spec.rb2
-rw-r--r--spec/policies/webhook_policy_spec.rb24
-rw-r--r--spec/validators/email_mx_validator_spec.rb16
-rw-r--r--spec/workers/admin/account_deletion_worker_spec.rb19
-rw-r--r--spec/workers/cache_buster_worker_spec.rb19
-rw-r--r--spec/workers/poll_expiration_notify_worker_spec.rb13
-rw-r--r--spec/workers/post_process_media_worker_spec.rb13
-rw-r--r--spec/workers/push_conversation_worker_spec.rb13
-rw-r--r--spec/workers/push_encrypted_message_worker_spec.rb13
-rw-r--r--spec/workers/push_update_worker_spec.rb16
-rw-r--r--spec/workers/redownload_avatar_worker_spec.rb13
-rw-r--r--spec/workers/redownload_header_worker_spec.rb13
-rw-r--r--spec/workers/remove_featured_tag_worker_spec.rb15
-rw-r--r--spec/workers/resolve_account_worker_spec.rb13
-rw-r--r--spec/workers/scheduler/indexing_scheduler_spec.rb13
-rw-r--r--spec/workers/scheduler/instance_refresh_scheduler_spec.rb13
-rw-r--r--spec/workers/scheduler/ip_cleanup_scheduler_spec.rb13
-rw-r--r--spec/workers/scheduler/pghero_scheduler_spec.rb13
-rw-r--r--spec/workers/scheduler/scheduled_statuses_scheduler_spec.rb13
-rw-r--r--spec/workers/scheduler/suspended_user_cleanup_scheduler_spec.rb13
-rw-r--r--spec/workers/scheduler/trends/refresh_scheduler_spec.rb13
-rw-r--r--spec/workers/scheduler/trends/review_notifications_scheduler_spec.rb13
-rw-r--r--spec/workers/scheduler/vacuum_scheduler_spec.rb13
-rw-r--r--spec/workers/unpublish_announcement_worker_spec.rb13
-rw-r--r--spec/workers/webhooks/delivery_worker_spec.rb13
156 files changed, 2675 insertions, 197 deletions
diff --git a/spec/controllers/admin/account_actions_controller_spec.rb b/spec/controllers/admin/account_actions_controller_spec.rb
new file mode 100644
index 000000000..4eae51c7b
--- /dev/null
+++ b/spec/controllers/admin/account_actions_controller_spec.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe Admin::AccountActionsController do
+  render_views
+
+  let(:user) { Fabricate(:user, role: UserRole.find_by(name: 'Admin')) }
+
+  before do
+    sign_in user, scope: :user
+  end
+
+  describe 'GET #new' do
+    let(:account) { Fabricate(:account) }
+
+    it 'returns http success' do
+      get :new, params: { account_id: account.id }
+
+      expect(response).to have_http_status(:success)
+    end
+  end
+end
diff --git a/spec/controllers/admin/announcements_controller_spec.rb b/spec/controllers/admin/announcements_controller_spec.rb
new file mode 100644
index 000000000..288ac1d71
--- /dev/null
+++ b/spec/controllers/admin/announcements_controller_spec.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe Admin::AnnouncementsController do
+  render_views
+
+  let(:user) { Fabricate(:user, role: UserRole.find_by(name: 'Admin')) }
+
+  before do
+    sign_in user, scope: :user
+  end
+
+  describe 'GET #index' do
+    it 'returns http success' do
+      get :index
+
+      expect(response).to have_http_status(:success)
+    end
+  end
+end
diff --git a/spec/controllers/admin/follow_recommendations_controller_spec.rb b/spec/controllers/admin/follow_recommendations_controller_spec.rb
new file mode 100644
index 000000000..f62aa6e4b
--- /dev/null
+++ b/spec/controllers/admin/follow_recommendations_controller_spec.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe Admin::FollowRecommendationsController do
+  render_views
+
+  let(:user) { Fabricate(:user, role: UserRole.find_by(name: 'Admin')) }
+
+  before do
+    sign_in user, scope: :user
+  end
+
+  describe 'GET #show' do
+    it 'returns http success' do
+      get :show
+
+      expect(response).to have_http_status(:success)
+    end
+  end
+end
diff --git a/spec/controllers/admin/ip_blocks_controller_spec.rb b/spec/controllers/admin/ip_blocks_controller_spec.rb
new file mode 100644
index 000000000..873888afc
--- /dev/null
+++ b/spec/controllers/admin/ip_blocks_controller_spec.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe Admin::IpBlocksController do
+  render_views
+
+  let(:user) { Fabricate(:user, role: UserRole.find_by(name: 'Admin')) }
+
+  before do
+    sign_in user, scope: :user
+  end
+
+  describe 'GET #index' do
+    it 'returns http success' do
+      get :index
+
+      expect(response).to have_http_status(:success)
+    end
+  end
+end
diff --git a/spec/controllers/admin/relationships_controller_spec.rb b/spec/controllers/admin/relationships_controller_spec.rb
new file mode 100644
index 000000000..1099a37a3
--- /dev/null
+++ b/spec/controllers/admin/relationships_controller_spec.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe Admin::RelationshipsController do
+  render_views
+
+  let(:user) { Fabricate(:user, role: UserRole.find_by(name: 'Admin')) }
+
+  before do
+    sign_in user, scope: :user
+  end
+
+  describe 'GET #index' do
+    let(:account) { Fabricate(:account) }
+
+    it 'returns http success' do
+      get :index, params: { account_id: account.id }
+
+      expect(response).to have_http_status(:success)
+    end
+  end
+end
diff --git a/spec/controllers/admin/relays_controller_spec.rb b/spec/controllers/admin/relays_controller_spec.rb
new file mode 100644
index 000000000..dfb9f3c04
--- /dev/null
+++ b/spec/controllers/admin/relays_controller_spec.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe Admin::RelaysController do
+  render_views
+
+  let(:user) { Fabricate(:user, role: UserRole.find_by(name: 'Admin')) }
+
+  before do
+    sign_in user, scope: :user
+  end
+
+  describe 'GET #index' do
+    it 'returns http success' do
+      get :index
+
+      expect(response).to have_http_status(:success)
+    end
+  end
+end
diff --git a/spec/controllers/admin/reports/actions_controller_spec.rb b/spec/controllers/admin/reports/actions_controller_spec.rb
index 3e42e4cb1..4c2624a40 100644
--- a/spec/controllers/admin/reports/actions_controller_spec.rb
+++ b/spec/controllers/admin/reports/actions_controller_spec.rb
@@ -57,6 +57,9 @@ describe Admin::Reports::ActionsController do
     let!(:media)         { Fabricate(:media_attachment, account: target_account, status: statuses[0]) }
     let(:report)         { Fabricate(:report, target_account: target_account, status_ids: statuses.map(&:id)) }
     let(:text)           { 'hello' }
+    let(:common_params) do
+      { report_id: report.id, text: text }
+    end
 
     shared_examples 'common behavior' do
       it 'closes the report' do
@@ -72,6 +75,26 @@ describe Admin::Reports::ActionsController do
         subject
         expect(response).to redirect_to(admin_reports_path)
       end
+
+      context 'when text is unset' do
+        let(:common_params) do
+          { report_id: report.id }
+        end
+
+        it 'closes the report' do
+          expect { subject }.to change { report.reload.action_taken? }.from(false).to(true)
+        end
+
+        it 'creates a strike with the expected text' do
+          expect { subject }.to change { report.target_account.strikes.count }.by(1)
+          expect(report.target_account.strikes.last.text).to eq ''
+        end
+
+        it 'redirects' do
+          subject
+          expect(response).to redirect_to(admin_reports_path)
+        end
+      end
     end
 
     shared_examples 'all action types' do
@@ -124,13 +147,13 @@ describe Admin::Reports::ActionsController do
     end
 
     context 'action as submit button' do
-      subject { post :create, params: { report_id: report.id, text: text, action => '' } }
+      subject { post :create, params: common_params.merge({ action => '' }) }
 
       it_behaves_like 'all action types'
     end
 
     context 'action as submit button' do
-      subject { post :create, params: { report_id: report.id, text: text, moderation_action: action } }
+      subject { post :create, params: common_params.merge({ moderation_action: action }) }
 
       it_behaves_like 'all action types'
     end
diff --git a/spec/controllers/admin/rules_controller_spec.rb b/spec/controllers/admin/rules_controller_spec.rb
new file mode 100644
index 000000000..d7b633c04
--- /dev/null
+++ b/spec/controllers/admin/rules_controller_spec.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe Admin::RulesController do
+  render_views
+
+  let(:user) { Fabricate(:user, role: UserRole.find_by(name: 'Admin')) }
+
+  before do
+    sign_in user, scope: :user
+  end
+
+  describe 'GET #index' do
+    it 'returns http success' do
+      get :index
+
+      expect(response).to have_http_status(:success)
+    end
+  end
+end
diff --git a/spec/controllers/admin/settings/about_controller_spec.rb b/spec/controllers/admin/settings/about_controller_spec.rb
new file mode 100644
index 000000000..2ae26090b
--- /dev/null
+++ b/spec/controllers/admin/settings/about_controller_spec.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe Admin::Settings::AboutController do
+  render_views
+
+  let(:user) { Fabricate(:user, role: UserRole.find_by(name: 'Admin')) }
+
+  before do
+    sign_in user, scope: :user
+  end
+
+  describe 'GET #show' do
+    it 'returns http success' do
+      get :show
+
+      expect(response).to have_http_status(:success)
+    end
+  end
+end
diff --git a/spec/controllers/admin/settings/appearance_controller_spec.rb b/spec/controllers/admin/settings/appearance_controller_spec.rb
new file mode 100644
index 000000000..65b29acc3
--- /dev/null
+++ b/spec/controllers/admin/settings/appearance_controller_spec.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe Admin::Settings::AppearanceController do
+  render_views
+
+  let(:user) { Fabricate(:user, role: UserRole.find_by(name: 'Admin')) }
+
+  before do
+    sign_in user, scope: :user
+  end
+
+  describe 'GET #show' do
+    it 'returns http success' do
+      get :show
+
+      expect(response).to have_http_status(:success)
+    end
+  end
+end
diff --git a/spec/controllers/admin/settings/content_retention_controller_spec.rb b/spec/controllers/admin/settings/content_retention_controller_spec.rb
new file mode 100644
index 000000000..53ce84d18
--- /dev/null
+++ b/spec/controllers/admin/settings/content_retention_controller_spec.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe Admin::Settings::ContentRetentionController do
+  render_views
+
+  let(:user) { Fabricate(:user, role: UserRole.find_by(name: 'Admin')) }
+
+  before do
+    sign_in user, scope: :user
+  end
+
+  describe 'GET #show' do
+    it 'returns http success' do
+      get :show
+
+      expect(response).to have_http_status(:success)
+    end
+  end
+end
diff --git a/spec/controllers/admin/settings/discovery_controller_spec.rb b/spec/controllers/admin/settings/discovery_controller_spec.rb
new file mode 100644
index 000000000..c7307ffc8
--- /dev/null
+++ b/spec/controllers/admin/settings/discovery_controller_spec.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe Admin::Settings::DiscoveryController do
+  render_views
+
+  let(:user) { Fabricate(:user, role: UserRole.find_by(name: 'Admin')) }
+
+  before do
+    sign_in user, scope: :user
+  end
+
+  describe 'GET #show' do
+    it 'returns http success' do
+      get :show
+
+      expect(response).to have_http_status(:success)
+    end
+  end
+end
diff --git a/spec/controllers/admin/settings/registrations_controller_spec.rb b/spec/controllers/admin/settings/registrations_controller_spec.rb
new file mode 100644
index 000000000..3fc1f9d13
--- /dev/null
+++ b/spec/controllers/admin/settings/registrations_controller_spec.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe Admin::Settings::RegistrationsController do
+  render_views
+
+  let(:user) { Fabricate(:user, role: UserRole.find_by(name: 'Admin')) }
+
+  before do
+    sign_in user, scope: :user
+  end
+
+  describe 'GET #show' do
+    it 'returns http success' do
+      get :show
+
+      expect(response).to have_http_status(:success)
+    end
+  end
+end
diff --git a/spec/controllers/admin/site_uploads_controller_spec.rb b/spec/controllers/admin/site_uploads_controller_spec.rb
new file mode 100644
index 000000000..4ea37f396
--- /dev/null
+++ b/spec/controllers/admin/site_uploads_controller_spec.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe Admin::SiteUploadsController do
+  render_views
+
+  let(:user) { Fabricate(:user, role: UserRole.find_by(name: 'Admin')) }
+
+  before do
+    sign_in user, scope: :user
+  end
+
+  describe 'DELETE #destroy' do
+    let(:site_upload) { Fabricate(:site_upload, var: 'thumbnail') }
+
+    it 'returns http success' do
+      delete :destroy, params: { id: site_upload.id }
+
+      expect(response).to redirect_to(admin_settings_path)
+    end
+  end
+end
diff --git a/spec/controllers/admin/trends/links/preview_card_providers_controller_spec.rb b/spec/controllers/admin/trends/links/preview_card_providers_controller_spec.rb
new file mode 100644
index 000000000..95ed38d6b
--- /dev/null
+++ b/spec/controllers/admin/trends/links/preview_card_providers_controller_spec.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe Admin::Trends::Links::PreviewCardProvidersController do
+  render_views
+
+  let(:user) { Fabricate(:user, role: UserRole.find_by(name: 'Admin')) }
+
+  before do
+    sign_in user, scope: :user
+  end
+
+  describe 'GET #index' do
+    it 'returns http success' do
+      get :index
+
+      expect(response).to have_http_status(:success)
+    end
+  end
+end
diff --git a/spec/controllers/admin/trends/links_controller_spec.rb b/spec/controllers/admin/trends/links_controller_spec.rb
new file mode 100644
index 000000000..7c67f5e5a
--- /dev/null
+++ b/spec/controllers/admin/trends/links_controller_spec.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe Admin::Trends::LinksController do
+  render_views
+
+  let(:user) { Fabricate(:user, role: UserRole.find_by(name: 'Admin')) }
+
+  before do
+    sign_in user, scope: :user
+  end
+
+  describe 'GET #index' do
+    it 'returns http success' do
+      get :index
+
+      expect(response).to have_http_status(:success)
+    end
+  end
+end
diff --git a/spec/controllers/admin/trends/statuses_controller_spec.rb b/spec/controllers/admin/trends/statuses_controller_spec.rb
new file mode 100644
index 000000000..b752234d3
--- /dev/null
+++ b/spec/controllers/admin/trends/statuses_controller_spec.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe Admin::Trends::StatusesController do
+  render_views
+
+  let(:user) { Fabricate(:user, role: UserRole.find_by(name: 'Admin')) }
+
+  before do
+    sign_in user, scope: :user
+  end
+
+  describe 'GET #index' do
+    it 'returns http success' do
+      get :index
+
+      expect(response).to have_http_status(:success)
+    end
+  end
+end
diff --git a/spec/controllers/admin/trends/tags_controller_spec.rb b/spec/controllers/admin/trends/tags_controller_spec.rb
new file mode 100644
index 000000000..4f74a5545
--- /dev/null
+++ b/spec/controllers/admin/trends/tags_controller_spec.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe Admin::Trends::TagsController do
+  render_views
+
+  let(:user) { Fabricate(:user, role: UserRole.find_by(name: 'Admin')) }
+
+  before do
+    sign_in user, scope: :user
+  end
+
+  describe 'GET #index' do
+    it 'returns http success' do
+      get :index
+
+      expect(response).to have_http_status(:success)
+    end
+  end
+end
diff --git a/spec/controllers/admin/warning_presets_controller_spec.rb b/spec/controllers/admin/warning_presets_controller_spec.rb
new file mode 100644
index 000000000..6b48fc28b
--- /dev/null
+++ b/spec/controllers/admin/warning_presets_controller_spec.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe Admin::WarningPresetsController do
+  render_views
+
+  let(:user) { Fabricate(:user, role: UserRole.find_by(name: 'Admin')) }
+
+  before do
+    sign_in user, scope: :user
+  end
+
+  describe 'GET #index' do
+    it 'returns http success' do
+      get :index
+
+      expect(response).to have_http_status(:success)
+    end
+  end
+end
diff --git a/spec/controllers/admin/webhooks/secrets_controller_spec.rb b/spec/controllers/admin/webhooks/secrets_controller_spec.rb
new file mode 100644
index 000000000..291a10fba
--- /dev/null
+++ b/spec/controllers/admin/webhooks/secrets_controller_spec.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe Admin::Webhooks::SecretsController do
+  render_views
+
+  let(:user) { Fabricate(:user, role: UserRole.find_by(name: 'Admin')) }
+
+  before do
+    sign_in user, scope: :user
+  end
+
+  describe 'POST #rotate' do
+    let(:webhook) { Fabricate(:webhook) }
+
+    it 'returns http success' do
+      post :rotate, params: { webhook_id: webhook.id }
+
+      expect(response).to redirect_to(admin_webhook_path(webhook))
+    end
+  end
+end
diff --git a/spec/controllers/admin/webhooks_controller_spec.rb b/spec/controllers/admin/webhooks_controller_spec.rb
new file mode 100644
index 000000000..12727e142
--- /dev/null
+++ b/spec/controllers/admin/webhooks_controller_spec.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe Admin::WebhooksController do
+  render_views
+
+  let(:user) { Fabricate(:user, role: UserRole.find_by(name: 'Admin')) }
+
+  before do
+    sign_in user, scope: :user
+  end
+
+  describe 'GET #index' do
+    it 'returns http success' do
+      get :index
+
+      expect(response).to have_http_status(:success)
+    end
+  end
+end
diff --git a/spec/controllers/api/v1/accounts/familiar_followers_controller_spec.rb b/spec/controllers/api/v1/accounts/familiar_followers_controller_spec.rb
new file mode 100644
index 000000000..bb075261f
--- /dev/null
+++ b/spec/controllers/api/v1/accounts/familiar_followers_controller_spec.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe Api::V1::Accounts::FamiliarFollowersController do
+  render_views
+
+  let(:user)    { Fabricate(:user) }
+  let(:token)   { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'read:follows') }
+  let(:account) { Fabricate(:account) }
+
+  before do
+    allow(controller).to receive(:doorkeeper_token) { token }
+  end
+
+  describe 'GET #index' do
+    it 'returns http success' do
+      get :index, params: { account_id: account.id, limit: 2 }
+
+      expect(response).to have_http_status(200)
+    end
+  end
+end
diff --git a/spec/controllers/api/v1/accounts/featured_tags_controller_spec.rb b/spec/controllers/api/v1/accounts/featured_tags_controller_spec.rb
new file mode 100644
index 000000000..53ac1e2a7
--- /dev/null
+++ b/spec/controllers/api/v1/accounts/featured_tags_controller_spec.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe Api::V1::Accounts::FeaturedTagsController do
+  render_views
+
+  let(:user)    { Fabricate(:user) }
+  let(:token)   { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'read:accounts') }
+  let(:account) { Fabricate(:account) }
+
+  before do
+    allow(controller).to receive(:doorkeeper_token) { token }
+  end
+
+  describe 'GET #index' do
+    it 'returns http success' do
+      get :index, params: { account_id: account.id, limit: 2 }
+
+      expect(response).to have_http_status(200)
+    end
+  end
+end
diff --git a/spec/controllers/api/v1/accounts/identity_proofs_controller_spec.rb b/spec/controllers/api/v1/accounts/identity_proofs_controller_spec.rb
new file mode 100644
index 000000000..6351de761
--- /dev/null
+++ b/spec/controllers/api/v1/accounts/identity_proofs_controller_spec.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe Api::V1::Accounts::IdentityProofsController do
+  render_views
+
+  let(:user)    { Fabricate(:user) }
+  let(:token)   { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'read:accounts') }
+  let(:account) { Fabricate(:account) }
+
+  before do
+    allow(controller).to receive(:doorkeeper_token) { token }
+  end
+
+  describe 'GET #index' do
+    it 'returns http success' do
+      get :index, params: { account_id: account.id, limit: 2 }
+
+      expect(response).to have_http_status(200)
+    end
+  end
+end
diff --git a/spec/controllers/api/v1/accounts/lookup_controller_spec.rb b/spec/controllers/api/v1/accounts/lookup_controller_spec.rb
new file mode 100644
index 000000000..37407766f
--- /dev/null
+++ b/spec/controllers/api/v1/accounts/lookup_controller_spec.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe Api::V1::Accounts::LookupController do
+  render_views
+
+  let(:user)    { Fabricate(:user) }
+  let(:token)   { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'read:accounts') }
+  let(:account) { Fabricate(:account) }
+
+  before do
+    allow(controller).to receive(:doorkeeper_token) { token }
+  end
+
+  describe 'GET #show' do
+    it 'returns http success' do
+      get :show, params: { account_id: account.id, acct: account.acct }
+
+      expect(response).to have_http_status(200)
+    end
+  end
+end
diff --git a/spec/controllers/api/v1/admin/canonical_email_blocks_controller_spec.rb b/spec/controllers/api/v1/admin/canonical_email_blocks_controller_spec.rb
new file mode 100644
index 000000000..3acae843a
--- /dev/null
+++ b/spec/controllers/api/v1/admin/canonical_email_blocks_controller_spec.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe Api::V1::Admin::CanonicalEmailBlocksController do
+  render_views
+
+  let(:user)    { Fabricate(:user, role: UserRole.find_by(name: 'Admin')) }
+  let(:token)   { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'admin:read') }
+  let(:account) { Fabricate(:account) }
+
+  before do
+    allow(controller).to receive(:doorkeeper_token) { token }
+  end
+
+  describe 'GET #index' do
+    it 'returns http success' do
+      get :index, params: { account_id: account.id, limit: 2 }
+
+      expect(response).to have_http_status(200)
+    end
+  end
+end
diff --git a/spec/controllers/api/v1/admin/dimensions_controller_spec.rb b/spec/controllers/api/v1/admin/dimensions_controller_spec.rb
new file mode 100644
index 000000000..ea18efe38
--- /dev/null
+++ b/spec/controllers/api/v1/admin/dimensions_controller_spec.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe Api::V1::Admin::DimensionsController do
+  render_views
+
+  let(:user)    { Fabricate(:user, role: UserRole.find_by(name: 'Admin')) }
+  let(:token)   { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'admin:read') }
+  let(:account) { Fabricate(:account) }
+
+  before do
+    allow(controller).to receive(:doorkeeper_token) { token }
+  end
+
+  describe 'POST #create' do
+    it 'returns http success' do
+      post :create, params: { account_id: account.id, limit: 2 }
+
+      expect(response).to have_http_status(200)
+    end
+  end
+end
diff --git a/spec/controllers/api/v1/admin/email_domain_blocks_controller_spec.rb b/spec/controllers/api/v1/admin/email_domain_blocks_controller_spec.rb
new file mode 100644
index 000000000..a92a29869
--- /dev/null
+++ b/spec/controllers/api/v1/admin/email_domain_blocks_controller_spec.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe Api::V1::Admin::EmailDomainBlocksController do
+  render_views
+
+  let(:user)    { Fabricate(:user, role: UserRole.find_by(name: 'Admin')) }
+  let(:token)   { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'admin:read') }
+  let(:account) { Fabricate(:account) }
+
+  before do
+    allow(controller).to receive(:doorkeeper_token) { token }
+  end
+
+  describe 'GET #index' do
+    it 'returns http success' do
+      get :index, params: { account_id: account.id, limit: 2 }
+
+      expect(response).to have_http_status(200)
+    end
+  end
+end
diff --git a/spec/controllers/api/v1/admin/ip_blocks_controller_spec.rb b/spec/controllers/api/v1/admin/ip_blocks_controller_spec.rb
new file mode 100644
index 000000000..50e2ae968
--- /dev/null
+++ b/spec/controllers/api/v1/admin/ip_blocks_controller_spec.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe Api::V1::Admin::IpBlocksController do
+  render_views
+
+  let(:user)    { Fabricate(:user, role: UserRole.find_by(name: 'Admin')) }
+  let(:token)   { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'admin:read') }
+  let(:account) { Fabricate(:account) }
+
+  before do
+    allow(controller).to receive(:doorkeeper_token) { token }
+  end
+
+  describe 'GET #index' do
+    it 'returns http success' do
+      get :index, params: { account_id: account.id, limit: 2 }
+
+      expect(response).to have_http_status(200)
+    end
+  end
+end
diff --git a/spec/controllers/api/v1/admin/measures_controller_spec.rb b/spec/controllers/api/v1/admin/measures_controller_spec.rb
new file mode 100644
index 000000000..03727a632
--- /dev/null
+++ b/spec/controllers/api/v1/admin/measures_controller_spec.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe Api::V1::Admin::MeasuresController do
+  render_views
+
+  let(:user)    { Fabricate(:user, role: UserRole.find_by(name: 'Admin')) }
+  let(:token)   { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'admin:read') }
+  let(:account) { Fabricate(:account) }
+
+  before do
+    allow(controller).to receive(:doorkeeper_token) { token }
+  end
+
+  describe 'POST #create' do
+    it 'returns http success' do
+      post :create, params: { account_id: account.id, limit: 2 }
+
+      expect(response).to have_http_status(200)
+    end
+  end
+end
diff --git a/spec/controllers/api/v1/admin/retention_controller_spec.rb b/spec/controllers/api/v1/admin/retention_controller_spec.rb
new file mode 100644
index 000000000..2381dbcb4
--- /dev/null
+++ b/spec/controllers/api/v1/admin/retention_controller_spec.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe Api::V1::Admin::RetentionController do
+  render_views
+
+  let(:user)    { Fabricate(:user, role: UserRole.find_by(name: 'Admin')) }
+  let(:token)   { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'admin:read') }
+  let(:account) { Fabricate(:account) }
+
+  before do
+    allow(controller).to receive(:doorkeeper_token) { token }
+  end
+
+  describe 'POST #create' do
+    it 'returns http success' do
+      post :create, params: { account_id: account.id, limit: 2 }
+
+      expect(response).to have_http_status(200)
+    end
+  end
+end
diff --git a/spec/controllers/api/v1/admin/trends/links_controller_spec.rb b/spec/controllers/api/v1/admin/trends/links_controller_spec.rb
new file mode 100644
index 000000000..a64292f06
--- /dev/null
+++ b/spec/controllers/api/v1/admin/trends/links_controller_spec.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe Api::V1::Admin::Trends::LinksController do
+  render_views
+
+  let(:user)    { Fabricate(:user, role: UserRole.find_by(name: 'Admin')) }
+  let(:token)   { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'admin:read') }
+  let(:account) { Fabricate(:account) }
+
+  before do
+    allow(controller).to receive(:doorkeeper_token) { token }
+  end
+
+  describe 'GET #index' do
+    it 'returns http success' do
+      get :index, params: { account_id: account.id, limit: 2 }
+
+      expect(response).to have_http_status(200)
+    end
+  end
+end
diff --git a/spec/controllers/api/v1/admin/trends/statuses_controller_spec.rb b/spec/controllers/api/v1/admin/trends/statuses_controller_spec.rb
new file mode 100644
index 000000000..821cc499f
--- /dev/null
+++ b/spec/controllers/api/v1/admin/trends/statuses_controller_spec.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe Api::V1::Admin::Trends::StatusesController do
+  render_views
+
+  let(:user)    { Fabricate(:user, role: UserRole.find_by(name: 'Admin')) }
+  let(:token)   { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'admin:read') }
+  let(:account) { Fabricate(:account) }
+
+  before do
+    allow(controller).to receive(:doorkeeper_token) { token }
+  end
+
+  describe 'GET #index' do
+    it 'returns http success' do
+      get :index, params: { account_id: account.id, limit: 2 }
+
+      expect(response).to have_http_status(200)
+    end
+  end
+end
diff --git a/spec/controllers/api/v1/admin/trends/tags_controller_spec.rb b/spec/controllers/api/v1/admin/trends/tags_controller_spec.rb
new file mode 100644
index 000000000..480306ce7
--- /dev/null
+++ b/spec/controllers/api/v1/admin/trends/tags_controller_spec.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe Api::V1::Admin::Trends::TagsController do
+  render_views
+
+  let(:user)    { Fabricate(:user, role: UserRole.find_by(name: 'Admin')) }
+  let(:token)   { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'admin:read') }
+  let(:account) { Fabricate(:account) }
+
+  before do
+    allow(controller).to receive(:doorkeeper_token) { token }
+  end
+
+  describe 'GET #index' do
+    it 'returns http success' do
+      get :index, params: { account_id: account.id, limit: 2 }
+
+      expect(response).to have_http_status(200)
+    end
+  end
+end
diff --git a/spec/controllers/api/v1/directories_controller_spec.rb b/spec/controllers/api/v1/directories_controller_spec.rb
new file mode 100644
index 000000000..b18aedc4d
--- /dev/null
+++ b/spec/controllers/api/v1/directories_controller_spec.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe Api::V1::DirectoriesController do
+  render_views
+
+  let(:user)    { Fabricate(:user) }
+  let(:token)   { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'read:follows') }
+  let(:account) { Fabricate(:account) }
+
+  before do
+    allow(controller).to receive(:doorkeeper_token) { token }
+  end
+
+  describe 'GET #show' do
+    it 'returns http success' do
+      get :show
+
+      expect(response).to have_http_status(200)
+    end
+  end
+end
diff --git a/spec/controllers/api/v1/featured_tags/suggestions_controller_spec.rb b/spec/controllers/api/v1/featured_tags/suggestions_controller_spec.rb
new file mode 100644
index 000000000..54c63dcc6
--- /dev/null
+++ b/spec/controllers/api/v1/featured_tags/suggestions_controller_spec.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe Api::V1::FeaturedTags::SuggestionsController do
+  render_views
+
+  let(:user)    { Fabricate(:user) }
+  let(:token)   { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'read:accounts') }
+  let(:account) { Fabricate(:account) }
+
+  before do
+    allow(controller).to receive(:doorkeeper_token) { token }
+  end
+
+  describe 'GET #index' do
+    it 'returns http success' do
+      get :index, params: { account_id: account.id, limit: 2 }
+
+      expect(response).to have_http_status(200)
+    end
+  end
+end
diff --git a/spec/controllers/api/v1/featured_tags_controller_spec.rb b/spec/controllers/api/v1/featured_tags_controller_spec.rb
new file mode 100644
index 000000000..aac942901
--- /dev/null
+++ b/spec/controllers/api/v1/featured_tags_controller_spec.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe Api::V1::FeaturedTagsController do
+  render_views
+
+  let(:user)    { Fabricate(:user) }
+  let(:token)   { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'read:accounts') }
+  let(:account) { Fabricate(:account) }
+
+  before do
+    allow(controller).to receive(:doorkeeper_token) { token }
+  end
+
+  describe 'GET #index' do
+    it 'returns http success' do
+      get :index, params: { account_id: account.id, limit: 2 }
+
+      expect(response).to have_http_status(200)
+    end
+  end
+end
diff --git a/spec/controllers/api/v1/instances/domain_blocks_controller_spec.rb b/spec/controllers/api/v1/instances/domain_blocks_controller_spec.rb
new file mode 100644
index 000000000..08f505c3d
--- /dev/null
+++ b/spec/controllers/api/v1/instances/domain_blocks_controller_spec.rb
@@ -0,0 +1,16 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe Api::V1::Instances::DomainBlocksController do
+  render_views
+
+  describe 'GET #index' do
+    it 'returns http success' do
+      Setting.show_domain_blocks = 'all'
+      get :index
+
+      expect(response).to have_http_status(200)
+    end
+  end
+end
diff --git a/spec/controllers/api/v1/instances/extended_descriptions_controller_spec.rb b/spec/controllers/api/v1/instances/extended_descriptions_controller_spec.rb
new file mode 100644
index 000000000..58c0d4b8f
--- /dev/null
+++ b/spec/controllers/api/v1/instances/extended_descriptions_controller_spec.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe Api::V1::Instances::ExtendedDescriptionsController do
+  render_views
+
+  describe 'GET #show' do
+    it 'returns http success' do
+      get :show
+
+      expect(response).to have_http_status(200)
+    end
+  end
+end
diff --git a/spec/controllers/api/v1/instances/privacy_policies_controller_spec.rb b/spec/controllers/api/v1/instances/privacy_policies_controller_spec.rb
new file mode 100644
index 000000000..ac0bed9dc
--- /dev/null
+++ b/spec/controllers/api/v1/instances/privacy_policies_controller_spec.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe Api::V1::Instances::PrivacyPoliciesController do
+  render_views
+
+  describe 'GET #show' do
+    it 'returns http success' do
+      get :show
+
+      expect(response).to have_http_status(200)
+    end
+  end
+end
diff --git a/spec/controllers/api/v1/instances/rules_controller_spec.rb b/spec/controllers/api/v1/instances/rules_controller_spec.rb
new file mode 100644
index 000000000..5af50239b
--- /dev/null
+++ b/spec/controllers/api/v1/instances/rules_controller_spec.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe Api::V1::Instances::RulesController do
+  render_views
+
+  describe 'GET #index' do
+    it 'returns http success' do
+      get :index
+
+      expect(response).to have_http_status(200)
+    end
+  end
+end
diff --git a/spec/controllers/api/v1/preferences_controller_spec.rb b/spec/controllers/api/v1/preferences_controller_spec.rb
new file mode 100644
index 000000000..79cc3066e
--- /dev/null
+++ b/spec/controllers/api/v1/preferences_controller_spec.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe Api::V1::PreferencesController do
+  render_views
+
+  let(:user)    { Fabricate(:user) }
+  let(:token)   { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'read:accounts') }
+  let(:account) { Fabricate(:account) }
+
+  before do
+    allow(controller).to receive(:doorkeeper_token) { token }
+  end
+
+  describe 'GET #index' do
+    it 'returns http success' do
+      get :index
+
+      expect(response).to have_http_status(200)
+    end
+  end
+end
diff --git a/spec/controllers/api/v1/scheduled_statuses_controller_spec.rb b/spec/controllers/api/v1/scheduled_statuses_controller_spec.rb
new file mode 100644
index 000000000..256c4b272
--- /dev/null
+++ b/spec/controllers/api/v1/scheduled_statuses_controller_spec.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe Api::V1::ScheduledStatusesController do
+  render_views
+
+  let(:user)    { Fabricate(:user) }
+  let(:token)   { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'read:statuses') }
+  let(:account) { Fabricate(:account) }
+
+  before do
+    allow(controller).to receive(:doorkeeper_token) { token }
+  end
+
+  describe 'GET #index' do
+    it 'returns http success' do
+      get :index
+
+      expect(response).to have_http_status(200)
+    end
+  end
+end
diff --git a/spec/controllers/api/v1/statuses/translations_controller_spec.rb b/spec/controllers/api/v1/statuses/translations_controller_spec.rb
new file mode 100644
index 000000000..2deea9fc0
--- /dev/null
+++ b/spec/controllers/api/v1/statuses/translations_controller_spec.rb
@@ -0,0 +1,33 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe Api::V1::Statuses::TranslationsController do
+  render_views
+
+  let(:user)  { Fabricate(:user) }
+  let(:app)   { Fabricate(:application, name: 'Test app', website: 'http://testapp.com') }
+  let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'read:statuses', application: app) }
+
+  context 'with an oauth token' do
+    before do
+      allow(controller).to receive(:doorkeeper_token) { token }
+    end
+
+    describe 'POST #create' do
+      let(:status) { Fabricate(:status, account: user.account, text: 'Hola', language: 'es') }
+
+      before do
+        translation = TranslationService::Translation.new(text: 'Hello')
+        service = instance_double(TranslationService::DeepL, translate: translation, supported?: true)
+        allow(TranslationService).to receive(:configured?).and_return(true)
+        allow(TranslationService).to receive(:configured).and_return(service)
+        post :create, params: { status_id: status.id }
+      end
+
+      it 'returns http success' do
+        expect(response).to have_http_status(200)
+      end
+    end
+  end
+end
diff --git a/spec/controllers/api/v1/trends/links_controller_spec.rb b/spec/controllers/api/v1/trends/links_controller_spec.rb
new file mode 100644
index 000000000..71a7e2e47
--- /dev/null
+++ b/spec/controllers/api/v1/trends/links_controller_spec.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe Api::V1::Trends::LinksController do
+  render_views
+
+  describe 'GET #index' do
+    it 'returns http success' do
+      get :index
+
+      expect(response).to have_http_status(200)
+    end
+  end
+end
diff --git a/spec/controllers/api/v1/trends/statuses_controller_spec.rb b/spec/controllers/api/v1/trends/statuses_controller_spec.rb
new file mode 100644
index 000000000..e9892bb14
--- /dev/null
+++ b/spec/controllers/api/v1/trends/statuses_controller_spec.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe Api::V1::Trends::StatusesController do
+  render_views
+
+  describe 'GET #index' do
+    it 'returns http success' do
+      get :index
+
+      expect(response).to have_http_status(200)
+    end
+  end
+end
diff --git a/spec/controllers/relationships_controller_spec.rb b/spec/controllers/relationships_controller_spec.rb
index 39f455e03..53a5daa51 100644
--- a/spec/controllers/relationships_controller_spec.rb
+++ b/spec/controllers/relationships_controller_spec.rb
@@ -58,7 +58,7 @@ describe RelationshipsController do
     end
 
     context 'when select parameter is provided' do
-      subject { patch :update, params: { form_account_batch: { account_ids: [poopfeast.id] }, block_domains: '' } }
+      subject { patch :update, params: { form_account_batch: { account_ids: [poopfeast.id] }, remove_domains_from_followers: '' } }
 
       it 'soft-blocks followers from selected domains' do
         poopfeast.follow!(user.account)
@@ -69,6 +69,15 @@ describe RelationshipsController do
         expect(poopfeast.following?(user.account)).to be false
       end
 
+      it 'does not unfollow users from selected domains' do
+        user.account.follow!(poopfeast)
+
+        sign_in user, scope: :user
+        subject
+
+        expect(user.account.following?(poopfeast)).to be true
+      end
+
       include_examples 'authenticate user'
       include_examples 'redirects back to followers page'
     end
diff --git a/spec/controllers/settings/aliases_controller_spec.rb b/spec/controllers/settings/aliases_controller_spec.rb
new file mode 100644
index 000000000..805f65988
--- /dev/null
+++ b/spec/controllers/settings/aliases_controller_spec.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe Settings::AliasesController do
+  render_views
+
+  let!(:user) { Fabricate(:user) }
+  let(:account) { user.account }
+
+  before do
+    sign_in user, scope: :user
+  end
+
+  describe 'GET #index' do
+    it 'returns http success' do
+      get :index
+      expect(response).to have_http_status(200)
+    end
+  end
+end
diff --git a/spec/controllers/settings/exports/blocked_domains_controller_spec.rb b/spec/controllers/settings/exports/blocked_domains_controller_spec.rb
new file mode 100644
index 000000000..ac72fd9dd
--- /dev/null
+++ b/spec/controllers/settings/exports/blocked_domains_controller_spec.rb
@@ -0,0 +1,20 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe Settings::Exports::BlockedDomainsController do
+  render_views
+
+  describe 'GET #index' do
+    it 'returns a csv of the domains' do
+      account = Fabricate(:account, domain: 'example.com')
+      user = Fabricate(:user, account: account)
+      Fabricate(:account_domain_block, domain: 'example.com', account: account)
+
+      sign_in user, scope: :user
+      get :index, format: :csv
+
+      expect(response.body).to eq "example.com\n"
+    end
+  end
+end
diff --git a/spec/controllers/settings/exports/lists_controller_spec.rb b/spec/controllers/settings/exports/lists_controller_spec.rb
new file mode 100644
index 000000000..29623ba49
--- /dev/null
+++ b/spec/controllers/settings/exports/lists_controller_spec.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe Settings::Exports::ListsController do
+  render_views
+
+  describe 'GET #index' do
+    it 'returns a csv of the domains' do
+      account = Fabricate(:account)
+      user = Fabricate(:user, account: account)
+      list = Fabricate(:list, account: account, title: 'The List')
+      Fabricate(:list_account, list: list, account: account)
+
+      sign_in user, scope: :user
+      get :index, format: :csv
+
+      expect(response.body).to match 'The List'
+    end
+  end
+end
diff --git a/spec/controllers/settings/login_activities_controller_spec.rb b/spec/controllers/settings/login_activities_controller_spec.rb
new file mode 100644
index 000000000..6f1f3de31
--- /dev/null
+++ b/spec/controllers/settings/login_activities_controller_spec.rb
@@ -0,0 +1,20 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe Settings::LoginActivitiesController do
+  render_views
+
+  let!(:user) { Fabricate(:user) }
+
+  before do
+    sign_in user, scope: :user
+  end
+
+  describe 'GET #index' do
+    it 'returns http success' do
+      get :index
+      expect(response).to have_http_status(200)
+    end
+  end
+end
diff --git a/spec/controllers/settings/migration/redirects_controller_spec.rb b/spec/controllers/settings/migration/redirects_controller_spec.rb
new file mode 100644
index 000000000..50d9e1927
--- /dev/null
+++ b/spec/controllers/settings/migration/redirects_controller_spec.rb
@@ -0,0 +1,20 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe Settings::Migration::RedirectsController do
+  render_views
+
+  let!(:user) { Fabricate(:user) }
+
+  before do
+    sign_in user, scope: :user
+  end
+
+  describe 'GET #new' do
+    it 'returns http success' do
+      get :new
+      expect(response).to have_http_status(200)
+    end
+  end
+end
diff --git a/spec/controllers/settings/pictures_controller_spec.rb b/spec/controllers/settings/pictures_controller_spec.rb
new file mode 100644
index 000000000..2368dc55d
--- /dev/null
+++ b/spec/controllers/settings/pictures_controller_spec.rb
@@ -0,0 +1,22 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe Settings::PicturesController do
+  render_views
+
+  let!(:user) { Fabricate(:user) }
+
+  before do
+    sign_in user, scope: :user
+  end
+
+  describe 'DELETE #destroy' do
+    context 'with invalid picture id' do
+      it 'returns http bad request' do
+        delete :destroy, params: { id: 'invalid' }
+        expect(response).to have_http_status(400)
+      end
+    end
+  end
+end
diff --git a/spec/controllers/settings/preferences/appearance_controller_spec.rb b/spec/controllers/settings/preferences/appearance_controller_spec.rb
new file mode 100644
index 000000000..7c7f716b7
--- /dev/null
+++ b/spec/controllers/settings/preferences/appearance_controller_spec.rb
@@ -0,0 +1,20 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe Settings::Preferences::AppearanceController do
+  render_views
+
+  let!(:user) { Fabricate(:user) }
+
+  before do
+    sign_in user, scope: :user
+  end
+
+  describe 'GET #show' do
+    it 'returns http success' do
+      get :show
+      expect(response).to have_http_status(200)
+    end
+  end
+end
diff --git a/spec/fabricators/account_alias_fabricator.rb b/spec/fabricators/account_alias_fabricator.rb
deleted file mode 100644
index 4f434c078..000000000
--- a/spec/fabricators/account_alias_fabricator.rb
+++ /dev/null
@@ -1,7 +0,0 @@
-# frozen_string_literal: true
-
-Fabricator(:account_alias) do
-  account
-  acct 'test@example.com'
-  uri 'https://example.com/users/test'
-end
diff --git a/spec/fabricators/account_deletion_request_fabricator.rb b/spec/fabricators/account_deletion_request_fabricator.rb
deleted file mode 100644
index 3d3d37398..000000000
--- a/spec/fabricators/account_deletion_request_fabricator.rb
+++ /dev/null
@@ -1,5 +0,0 @@
-# frozen_string_literal: true
-
-Fabricator(:account_deletion_request) do
-  account
-end
diff --git a/spec/fabricators/account_migration_fabricator.rb b/spec/fabricators/account_migration_fabricator.rb
index fd453f6d2..ae6143a65 100644
--- a/spec/fabricators/account_migration_fabricator.rb
+++ b/spec/fabricators/account_migration_fabricator.rb
@@ -5,4 +5,5 @@ Fabricator(:account_migration) do
   target_account { |attrs| Fabricate(:account, also_known_as: [ActivityPub::TagManager.instance.uri_for(attrs[:account])]) }
   acct           { |attrs| attrs[:target_account].acct }
   followers_count 1234
+  created_at { 60.days.ago }
 end
diff --git a/spec/fabricators/account_moderation_note_fabricator.rb b/spec/fabricators/account_moderation_note_fabricator.rb
index 403870db6..341a24dea 100644
--- a/spec/fabricators/account_moderation_note_fabricator.rb
+++ b/spec/fabricators/account_moderation_note_fabricator.rb
@@ -2,5 +2,6 @@
 
 Fabricator(:account_moderation_note) do
   content 'MyText'
-  account nil
+  account
+  target_account { Fabricate(:account) }
 end
diff --git a/spec/fabricators/account_pin_fabricator.rb b/spec/fabricators/account_pin_fabricator.rb
index 7d8a77bb5..32a5f3bdb 100644
--- a/spec/fabricators/account_pin_fabricator.rb
+++ b/spec/fabricators/account_pin_fabricator.rb
@@ -1,6 +1,7 @@
 # frozen_string_literal: true
 
 Fabricator(:account_pin) do
-  account        nil
-  target_account nil
+  account
+  target_account(fabricator: :account)
+  before_create { |account_pin, _| account_pin.account.follow!(account_pin.target_account) }
 end
diff --git a/spec/fabricators/account_stat_fabricator.rb b/spec/fabricators/account_stat_fabricator.rb
index 45b1524ef..e6085c5f2 100644
--- a/spec/fabricators/account_stat_fabricator.rb
+++ b/spec/fabricators/account_stat_fabricator.rb
@@ -1,8 +1,8 @@
 # frozen_string_literal: true
 
 Fabricator(:account_stat) do
-  account         nil
-  statuses_count  ''
-  following_count ''
-  followers_count ''
+  account
+  statuses_count  '123'
+  following_count '456'
+  followers_count '789'
 end
diff --git a/spec/fabricators/account_tag_stat_fabricator.rb b/spec/fabricators/account_tag_stat_fabricator.rb
deleted file mode 100644
index 769015bd0..000000000
--- a/spec/fabricators/account_tag_stat_fabricator.rb
+++ /dev/null
@@ -1,5 +0,0 @@
-# frozen_string_literal: true
-
-Fabricator(:account_tag_stat) do
-  accounts_count ''
-end
diff --git a/spec/fabricators/account_warning_preset_fabricator.rb b/spec/fabricators/account_warning_preset_fabricator.rb
index 7588e7f9c..c50e08bf4 100644
--- a/spec/fabricators/account_warning_preset_fabricator.rb
+++ b/spec/fabricators/account_warning_preset_fabricator.rb
@@ -1,5 +1,5 @@
 # frozen_string_literal: true
 
 Fabricator(:account_warning_preset) do
-  text 'MyText'
+  text { Faker::Lorem.paragraph }
 end
diff --git a/spec/fabricators/admin_action_log_fabricator.rb b/spec/fabricators/admin_action_log_fabricator.rb
index eb738c01c..a259644bd 100644
--- a/spec/fabricators/admin_action_log_fabricator.rb
+++ b/spec/fabricators/admin_action_log_fabricator.rb
@@ -1,7 +1,7 @@
 # frozen_string_literal: true
 
 Fabricator('Admin::ActionLog') do
-  account nil
+  account
   action  'MyString'
   target  nil
 end
diff --git a/spec/fabricators/announcement_mute_fabricator.rb b/spec/fabricators/announcement_mute_fabricator.rb
deleted file mode 100644
index 109fec041..000000000
--- a/spec/fabricators/announcement_mute_fabricator.rb
+++ /dev/null
@@ -1,6 +0,0 @@
-# frozen_string_literal: true
-
-Fabricator(:announcement_mute) do
-  account
-  announcement
-end
diff --git a/spec/fabricators/announcement_reaction_fabricator.rb b/spec/fabricators/announcement_reaction_fabricator.rb
deleted file mode 100644
index 5da51caaa..000000000
--- a/spec/fabricators/announcement_reaction_fabricator.rb
+++ /dev/null
@@ -1,7 +0,0 @@
-# frozen_string_literal: true
-
-Fabricator(:announcement_reaction) do
-  account
-  announcement
-  name '🌿'
-end
diff --git a/spec/fabricators/conversation_account_fabricator.rb b/spec/fabricators/conversation_account_fabricator.rb
deleted file mode 100644
index f69d36855..000000000
--- a/spec/fabricators/conversation_account_fabricator.rb
+++ /dev/null
@@ -1,8 +0,0 @@
-# frozen_string_literal: true
-
-Fabricator(:conversation_account) do
-  account                 nil
-  conversation            nil
-  participant_account_ids ''
-  last_status             nil
-end
diff --git a/spec/fabricators/conversation_mute_fabricator.rb b/spec/fabricators/conversation_mute_fabricator.rb
deleted file mode 100644
index 5cf4dd3d5..000000000
--- a/spec/fabricators/conversation_mute_fabricator.rb
+++ /dev/null
@@ -1,4 +0,0 @@
-# frozen_string_literal: true
-
-Fabricator(:conversation_mute) do
-end
diff --git a/spec/fabricators/custom_emoji_category_fabricator.rb b/spec/fabricators/custom_emoji_category_fabricator.rb
deleted file mode 100644
index 6019baba2..000000000
--- a/spec/fabricators/custom_emoji_category_fabricator.rb
+++ /dev/null
@@ -1,5 +0,0 @@
-# frozen_string_literal: true
-
-Fabricator(:custom_emoji_category) do
-  name 'MyString'
-end
diff --git a/spec/fabricators/encrypted_message_fabricator.rb b/spec/fabricators/encrypted_message_fabricator.rb
deleted file mode 100644
index 289882754..000000000
--- a/spec/fabricators/encrypted_message_fabricator.rb
+++ /dev/null
@@ -1,10 +0,0 @@
-# frozen_string_literal: true
-
-Fabricator(:encrypted_message) do
-  device
-  from_account
-  from_device_id   { Faker::Number.number(digits: 5) }
-  type             0
-  body             ''
-  message_franking ''
-end
diff --git a/spec/fabricators/featured_tag_fabricator.rb b/spec/fabricators/featured_tag_fabricator.rb
deleted file mode 100644
index 4bfa3e924..000000000
--- a/spec/fabricators/featured_tag_fabricator.rb
+++ /dev/null
@@ -1,8 +0,0 @@
-# frozen_string_literal: true
-
-Fabricator(:featured_tag) do
-  account
-  tag
-  statuses_count 1_337
-  last_status_at Time.now.utc
-end
diff --git a/spec/fabricators/follow_recommendation_suppression_fabricator.rb b/spec/fabricators/follow_recommendation_suppression_fabricator.rb
deleted file mode 100644
index 6477baee1..000000000
--- a/spec/fabricators/follow_recommendation_suppression_fabricator.rb
+++ /dev/null
@@ -1,5 +0,0 @@
-# frozen_string_literal: true
-
-Fabricator(:follow_recommendation_suppression) do
-  account
-end
diff --git a/spec/fabricators/identity_fabricator.rb b/spec/fabricators/identity_fabricator.rb
index b83010111..58072c0d6 100644
--- a/spec/fabricators/identity_fabricator.rb
+++ b/spec/fabricators/identity_fabricator.rb
@@ -1,7 +1,7 @@
 # frozen_string_literal: true
 
 Fabricator(:identity) do
-  user     nil
+  user
   provider 'MyString'
   uid      'MyString'
 end
diff --git a/spec/fabricators/import_fabricator.rb b/spec/fabricators/import_fabricator.rb
deleted file mode 100644
index 11602f407..000000000
--- a/spec/fabricators/import_fabricator.rb
+++ /dev/null
@@ -1,4 +0,0 @@
-# frozen_string_literal: true
-
-Fabricator(:import) do
-end
diff --git a/spec/fabricators/ip_block_fabricator.rb b/spec/fabricators/ip_block_fabricator.rb
deleted file mode 100644
index a5da3f706..000000000
--- a/spec/fabricators/ip_block_fabricator.rb
+++ /dev/null
@@ -1,8 +0,0 @@
-# frozen_string_literal: true
-
-Fabricator(:ip_block) do
-  ip         ''
-  severity   ''
-  expires_at '2020-10-08 22:20:37'
-  comment    'MyText'
-end
diff --git a/spec/fabricators/list_account_fabricator.rb b/spec/fabricators/list_account_fabricator.rb
index b0af29e6f..00dde83cd 100644
--- a/spec/fabricators/list_account_fabricator.rb
+++ b/spec/fabricators/list_account_fabricator.rb
@@ -1,7 +1,7 @@
 # frozen_string_literal: true
 
 Fabricator(:list_account) do
-  list    nil
-  account nil
-  follow  nil
+  list
+  account
+  before_create { |list_account, _| list_account.list.account.follow!(account) }
 end
diff --git a/spec/fabricators/one_time_key_fabricator.rb b/spec/fabricators/one_time_key_fabricator.rb
index e317c28bd..cfb365cab 100644
--- a/spec/fabricators/one_time_key_fabricator.rb
+++ b/spec/fabricators/one_time_key_fabricator.rb
@@ -3,7 +3,7 @@
 Fabricator(:one_time_key) do
   device
   key_id { Faker::Alphanumeric.alphanumeric(number: 10) }
-  key    { Base64.strict_encode64(Ed25519::SigningKey.generate.verify_key.to_bytes) }
+  key { Base64.strict_encode64(Ed25519::SigningKey.generate.verify_key.to_bytes) }
 
   signature do |attrs|
     signing_key = Ed25519::SigningKey.generate
diff --git a/spec/fabricators/preview_card_provider_fabricator.rb b/spec/fabricators/preview_card_provider_fabricator.rb
new file mode 100644
index 000000000..78db71000
--- /dev/null
+++ b/spec/fabricators/preview_card_provider_fabricator.rb
@@ -0,0 +1,5 @@
+# frozen_string_literal: true
+
+Fabricator(:preview_card_provider) do
+  domain { Faker::Internet.domain_name }
+end
diff --git a/spec/fabricators/setting_fabricator.rb b/spec/fabricators/setting_fabricator.rb
index 336d7c355..ce9a48e90 100644
--- a/spec/fabricators/setting_fabricator.rb
+++ b/spec/fabricators/setting_fabricator.rb
@@ -1,4 +1,5 @@
 # frozen_string_literal: true
 
 Fabricator(:setting) do
+  var 'var'
 end
diff --git a/spec/fabricators/site_upload_fabricator.rb b/spec/fabricators/site_upload_fabricator.rb
index ad1b777cc..87553ccb8 100644
--- a/spec/fabricators/site_upload_fabricator.rb
+++ b/spec/fabricators/site_upload_fabricator.rb
@@ -2,4 +2,5 @@
 
 Fabricator(:site_upload) do
   file { Rails.root.join('spec', 'fabricators', 'assets', 'utah_teapot.png').open }
+  var 'thumbnail'
 end
diff --git a/spec/fabricators/status_edit_fabricator.rb b/spec/fabricators/status_edit_fabricator.rb
deleted file mode 100644
index 33735a459..000000000
--- a/spec/fabricators/status_edit_fabricator.rb
+++ /dev/null
@@ -1,9 +0,0 @@
-# frozen_string_literal: true
-
-Fabricator(:status_edit) do
-  status                    nil
-  account                   nil
-  text                      'MyText'
-  spoiler_text              'MyText'
-  media_attachments_changed false
-end
diff --git a/spec/fabricators/status_stat_fabricator.rb b/spec/fabricators/status_stat_fabricator.rb
deleted file mode 100644
index 8a358c51a..000000000
--- a/spec/fabricators/status_stat_fabricator.rb
+++ /dev/null
@@ -1,8 +0,0 @@
-# frozen_string_literal: true
-
-Fabricator(:status_stat) do
-  status_id        nil
-  replies_count    ''
-  reblogs_count    ''
-  favourites_count ''
-end
diff --git a/spec/fabricators/unavailable_domain_fabricator.rb b/spec/fabricators/unavailable_domain_fabricator.rb
index 300a9e7a1..cb9707020 100644
--- a/spec/fabricators/unavailable_domain_fabricator.rb
+++ b/spec/fabricators/unavailable_domain_fabricator.rb
@@ -1,5 +1,5 @@
 # frozen_string_literal: true
 
 Fabricator(:unavailable_domain) do
-  domain { Faker::Internet.domain }
+  domain { Faker::Internet.domain_name }
 end
diff --git a/spec/fabricators/user_invite_request_fabricator.rb b/spec/fabricators/user_invite_request_fabricator.rb
deleted file mode 100644
index 7736263e4..000000000
--- a/spec/fabricators/user_invite_request_fabricator.rb
+++ /dev/null
@@ -1,6 +0,0 @@
-# frozen_string_literal: true
-
-Fabricator(:user_invite_request) do
-  user
-  text { Faker::Lorem.sentence }
-end
diff --git a/spec/fabricators/web_setting_fabricator.rb b/spec/fabricators/web_setting_fabricator.rb
deleted file mode 100644
index 7c9f30079..000000000
--- a/spec/fabricators/web_setting_fabricator.rb
+++ /dev/null
@@ -1,4 +0,0 @@
-# frozen_string_literal: true
-
-Fabricator(:web_setting, from: Web::Setting) do
-end
diff --git a/spec/fabricators_spec.rb b/spec/fabricators_spec.rb
new file mode 100644
index 000000000..3b76c56ce
--- /dev/null
+++ b/spec/fabricators_spec.rb
@@ -0,0 +1,12 @@
+require 'rails_helper'
+
+Fabrication.manager.load_definitions if Fabrication.manager.empty?
+
+Fabrication.manager.schematics.map(&:first).each do |factory_name|
+  describe "The #{factory_name} factory" do
+    it 'is valid' do
+      factory = Fabricate(factory_name)
+      expect(factory).to be_valid
+    end
+  end
+end
diff --git a/spec/helpers/admin/account_moderation_notes_helper_spec.rb b/spec/helpers/admin/account_moderation_notes_helper_spec.rb
index 622ce8806..e01eba51d 100644
--- a/spec/helpers/admin/account_moderation_notes_helper_spec.rb
+++ b/spec/helpers/admin/account_moderation_notes_helper_spec.rb
@@ -42,13 +42,11 @@ RSpec.describe Admin::AccountModerationNotesHelper, type: :helper do
       let(:account) { Fabricate(:account) }
 
       it 'calls #link_to' do
-        expect(helper).to receive(:link_to).with(
-          admin_account_path(account.id),
-          class: name_tag_classes(account, true),
-          title: account.acct
-        )
+        result = helper.admin_account_inline_link_to(account)
 
-        helper.admin_account_inline_link_to(account)
+        expect(result).to match(name_tag_classes(account, true))
+        expect(result).to match(account.acct)
+        expect(result).to match(admin_account_path(account.id))
       end
     end
   end
diff --git a/spec/helpers/admin/dashboard_helper_spec.rb b/spec/helpers/admin/dashboard_helper_spec.rb
new file mode 100644
index 000000000..59062e483
--- /dev/null
+++ b/spec/helpers/admin/dashboard_helper_spec.rb
@@ -0,0 +1,69 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe Admin::DashboardHelper do
+  describe 'relevant_account_timestamp' do
+    context 'with an account with older sign in' do
+      let(:account) { Fabricate(:account) }
+      let(:stamp) { 10.days.ago }
+
+      it 'returns a time element' do
+        account.user.update(current_sign_in_at: stamp)
+        result = helper.relevant_account_timestamp(account)
+
+        expect(result).to match('time-ago')
+        expect(result).to match(I18n.l(stamp))
+      end
+    end
+
+    context 'with an account with newer sign in' do
+      let(:account) { Fabricate(:account) }
+
+      it 'returns a time element' do
+        account.user.update(current_sign_in_at: 10.hours.ago)
+        result = helper.relevant_account_timestamp(account)
+
+        expect(result).to eq(I18n.t('generic.today'))
+      end
+    end
+
+    context 'with an account where the user is pending' do
+      let(:account) { Fabricate(:account) }
+
+      it 'returns a time element' do
+        account.user.update(current_sign_in_at: nil)
+        account.user.update(approved: false)
+        result = helper.relevant_account_timestamp(account)
+
+        expect(result).to match('time-ago')
+        expect(result).to match(I18n.l(account.user.created_at))
+      end
+    end
+
+    context 'with an account with a last status value' do
+      let(:account) { Fabricate(:account) }
+      let(:stamp) { 5.minutes.ago }
+
+      it 'returns a time element' do
+        account.user.update(current_sign_in_at: nil)
+        account.account_stat.update(last_status_at: stamp)
+        result = helper.relevant_account_timestamp(account)
+
+        expect(result).to match('time-ago')
+        expect(result).to match(I18n.l(stamp))
+      end
+    end
+
+    context 'with an account without sign in or last status or pending' do
+      let(:account) { Fabricate(:account) }
+
+      it 'returns a time element' do
+        account.user.update(current_sign_in_at: nil)
+        result = helper.relevant_account_timestamp(account)
+
+        expect(result).to eq('-')
+      end
+    end
+  end
+end
diff --git a/spec/helpers/languages_helper_spec.rb b/spec/helpers/languages_helper_spec.rb
index 217c9b239..98c8064a3 100644
--- a/spec/helpers/languages_helper_spec.rb
+++ b/spec/helpers/languages_helper_spec.rb
@@ -10,14 +10,54 @@ describe LanguagesHelper do
   end
 
   describe 'native_locale_name' do
-    it 'finds the human readable native name from a key' do
-      expect(helper.native_locale_name(:de)).to eq('Deutsch')
+    context 'with a blank locale' do
+      it 'defaults to a generic value' do
+        expect(helper.native_locale_name(nil)).to eq(I18n.t('generic.none'))
+      end
+    end
+
+    context 'with a locale of `und`' do
+      it 'defaults to a generic value' do
+        expect(helper.native_locale_name('und')).to eq(I18n.t('generic.none'))
+      end
+    end
+
+    context 'with a supported locale' do
+      it 'finds the human readable native name from a key' do
+        expect(helper.native_locale_name(:de)).to eq('Deutsch')
+      end
+    end
+
+    context 'with a regional locale' do
+      it 'finds the human readable regional name from a key' do
+        expect(helper.native_locale_name('en-GB')).to eq('English (British)')
+      end
+    end
+
+    context 'with a non-existent locale' do
+      it 'returns the supplied locale value' do
+        expect(helper.native_locale_name(:xxx)).to eq(:xxx)
+      end
     end
   end
 
   describe 'standard_locale_name' do
-    it 'finds the human readable standard name from a key' do
-      expect(helper.standard_locale_name(:de)).to eq('German')
+    context 'with a blank locale' do
+      it 'defaults to a generic value' do
+        expect(helper.standard_locale_name(nil)).to eq(I18n.t('generic.none'))
+      end
+    end
+
+    context 'with a non-existent locale' do
+      it 'returns the supplied locale value' do
+        expect(helper.standard_locale_name(:xxx)).to eq(:xxx)
+      end
+    end
+
+    context 'with a supported locale' do
+      it 'finds the human readable standard name from a key' do
+        expect(helper.standard_locale_name(:de)).to eq('German')
+      end
     end
   end
 end
diff --git a/spec/helpers/settings_helper_spec.rb b/spec/helpers/settings_helper_spec.rb
new file mode 100644
index 000000000..cba5c6ee8
--- /dev/null
+++ b/spec/helpers/settings_helper_spec.rb
@@ -0,0 +1,37 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe SettingsHelper do
+  describe 'session_device_icon' do
+    context 'with a mobile device' do
+      let(:session) { SessionActivation.new(user_agent: 'Mozilla/5.0 (iPhone)') }
+
+      it 'detects the device and returns a descriptive string' do
+        result = helper.session_device_icon(session)
+
+        expect(result).to eq('mobile')
+      end
+    end
+
+    context 'with a tablet device' do
+      let(:session) { SessionActivation.new(user_agent: 'Mozilla/5.0 (iPad)') }
+
+      it 'detects the device and returns a descriptive string' do
+        result = helper.session_device_icon(session)
+
+        expect(result).to eq('tablet')
+      end
+    end
+
+    context 'with a desktop device' do
+      let(:session) { SessionActivation.new(user_agent: 'Mozilla/5.0 (Macintosh)') }
+
+      it 'detects the device and returns a descriptive string' do
+        result = helper.session_device_icon(session)
+
+        expect(result).to eq('desktop')
+      end
+    end
+  end
+end
diff --git a/spec/helpers/statuses_helper_spec.rb b/spec/helpers/statuses_helper_spec.rb
index ce2a4680e..c8ca2ed32 100644
--- a/spec/helpers/statuses_helper_spec.rb
+++ b/spec/helpers/statuses_helper_spec.rb
@@ -3,6 +3,68 @@
 require 'rails_helper'
 
 RSpec.describe StatusesHelper, type: :helper do
+  describe 'link_to_newer' do
+    it 'returns a link to newer content' do
+      url = 'https://example.com'
+      result = helper.link_to_newer(url)
+
+      expect(result).to match('load-more')
+      expect(result).to match(I18n.t('statuses.show_newer'))
+    end
+  end
+
+  describe 'link_to_older' do
+    it 'returns a link to older content' do
+      url = 'https://example.com'
+      result = helper.link_to_older(url)
+
+      expect(result).to match('load-more')
+      expect(result).to match(I18n.t('statuses.show_older'))
+    end
+  end
+
+  describe 'fa_visibility_icon' do
+    context 'with a status that is public' do
+      let(:status) { Status.new(visibility: 'public') }
+
+      it 'returns the correct fa icon' do
+        result = helper.fa_visibility_icon(status)
+
+        expect(result).to match('fa-globe')
+      end
+    end
+
+    context 'with a status that is unlisted' do
+      let(:status) { Status.new(visibility: 'unlisted') }
+
+      it 'returns the correct fa icon' do
+        result = helper.fa_visibility_icon(status)
+
+        expect(result).to match('fa-unlock')
+      end
+    end
+
+    context 'with a status that is private' do
+      let(:status) { Status.new(visibility: 'private') }
+
+      it 'returns the correct fa icon' do
+        result = helper.fa_visibility_icon(status)
+
+        expect(result).to match('fa-lock')
+      end
+    end
+
+    context 'with a status that is direct' do
+      let(:status) { Status.new(visibility: 'direct') }
+
+      it 'returns the correct fa icon' do
+        result = helper.fa_visibility_icon(status)
+
+        expect(result).to match('fa-at')
+      end
+    end
+  end
+
   describe '#stream_link_target' do
     it 'returns nil if it is not an embedded view' do
       set_not_embedded_view
diff --git a/spec/lib/translation_service/deepl_spec.rb b/spec/lib/translation_service/deepl_spec.rb
new file mode 100644
index 000000000..aa2473186
--- /dev/null
+++ b/spec/lib/translation_service/deepl_spec.rb
@@ -0,0 +1,100 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+RSpec.describe TranslationService::DeepL do
+  subject(:service) { described_class.new(plan, 'my-api-key') }
+
+  let(:plan) { 'advanced' }
+
+  before do
+    stub_request(:get, 'https://api.deepl.com/v2/languages?type=source').to_return(
+      body: '[{"language":"EN","name":"English"},{"language":"UK","name":"Ukrainian"}]'
+    )
+    stub_request(:get, 'https://api.deepl.com/v2/languages?type=target').to_return(
+      body: '[{"language":"EN-GB","name":"English (British)"},{"language":"ZH","name":"Chinese"}]'
+    )
+  end
+
+  describe '#supported?' do
+    it 'supports included languages as source and target languages' do
+      expect(service.supported?('uk', 'en')).to be true
+    end
+
+    it 'supports auto-detecting source language' do
+      expect(service.supported?(nil, 'en')).to be true
+    end
+
+    it 'supports "en" and "pt" as target languages though not included in language list' do
+      expect(service.supported?('uk', 'en')).to be true
+      expect(service.supported?('uk', 'pt')).to be true
+    end
+
+    it 'does not support non-included language as target language' do
+      expect(service.supported?('uk', 'nl')).to be false
+    end
+
+    it 'does not support non-included language as source language' do
+      expect(service.supported?('da', 'en')).to be false
+    end
+  end
+
+  describe '#translate' do
+    it 'returns translation with specified source language' do
+      stub_request(:post, 'https://api.deepl.com/v2/translate')
+        .with(body: 'text=Hasta+la+vista&source_lang=ES&target_lang=en&tag_handling=html')
+        .to_return(body: '{"translations":[{"detected_source_language":"ES","text":"See you soon"}]}')
+
+      translation = service.translate('Hasta la vista', 'es', 'en')
+      expect(translation.detected_source_language).to eq 'es'
+      expect(translation.provider).to eq 'DeepL.com'
+      expect(translation.text).to eq 'See you soon'
+    end
+
+    it 'returns translation with auto-detected source language' do
+      stub_request(:post, 'https://api.deepl.com/v2/translate')
+        .with(body: 'text=Guten+Tag&source_lang&target_lang=en&tag_handling=html')
+        .to_return(body: '{"translations":[{"detected_source_language":"DE","text":"Good Morning"}]}')
+
+      translation = service.translate('Guten Tag', nil, 'en')
+      expect(translation.detected_source_language).to eq 'de'
+      expect(translation.provider).to eq 'DeepL.com'
+      expect(translation.text).to eq 'Good Morning'
+    end
+  end
+
+  describe '#languages?' do
+    it 'returns source languages' do
+      expect(service.send(:languages, 'source')).to eq ['en', 'uk', nil]
+    end
+
+    it 'returns target languages' do
+      expect(service.send(:languages, 'target')).to eq %w(en-gb zh en pt)
+    end
+  end
+
+  describe '#request' do
+    before do
+      stub_request(:any, //)
+      # rubocop:disable Lint/EmptyBlock
+      service.send(:request, :get, '/v2/languages') { |res| }
+      # rubocop:enable Lint/EmptyBlock
+    end
+
+    it 'uses paid plan base URL' do
+      expect(a_request(:get, 'https://api.deepl.com/v2/languages')).to have_been_made.once
+    end
+
+    context 'with free plan' do
+      let(:plan) { 'free' }
+
+      it 'uses free plan base URL' do
+        expect(a_request(:get, 'https://api-free.deepl.com/v2/languages')).to have_been_made.once
+      end
+    end
+
+    it 'sends API key' do
+      expect(a_request(:get, 'https://api.deepl.com/v2/languages').with(headers: { Authorization: 'DeepL-Auth-Key my-api-key' })).to have_been_made.once
+    end
+  end
+end
diff --git a/spec/lib/translation_service/libre_translate_spec.rb b/spec/lib/translation_service/libre_translate_spec.rb
new file mode 100644
index 000000000..a6cb01884
--- /dev/null
+++ b/spec/lib/translation_service/libre_translate_spec.rb
@@ -0,0 +1,71 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+RSpec.describe TranslationService::LibreTranslate do
+  subject(:service) { described_class.new('https://libretranslate.example.com', 'my-api-key') }
+
+  before do
+    stub_request(:get, 'https://libretranslate.example.com/languages').to_return(
+      body: '[{"code": "en","name": "English","targets": ["de","es"]},{"code": "da","name": "Danish","targets": ["en","de"]}]'
+    )
+  end
+
+  describe '#supported?' do
+    it 'supports included language pair' do
+      expect(service.supported?('en', 'de')).to be true
+    end
+
+    it 'does not support reversed language pair' do
+      expect(service.supported?('de', 'en')).to be false
+    end
+
+    it 'supports auto-detecting source language' do
+      expect(service.supported?(nil, 'de')).to be true
+    end
+
+    it 'does not support auto-detecting for unsupported target language' do
+      expect(service.supported?(nil, 'pt')).to be false
+    end
+  end
+
+  describe '#languages' do
+    subject(:languages) { service.send(:languages) }
+
+    it 'includes supported source languages' do
+      expect(languages.keys).to eq ['en', 'da', nil]
+    end
+
+    it 'includes supported target languages for source language' do
+      expect(languages['en']).to eq %w(de es)
+    end
+
+    it 'includes supported target languages for auto-detected language' do
+      expect(languages[nil]).to eq %w(de es en)
+    end
+  end
+
+  describe '#translate' do
+    it 'returns translation with specified source language' do
+      stub_request(:post, 'https://libretranslate.example.com/translate')
+        .with(body: '{"q":"Hasta la vista","source":"es","target":"en","format":"html","api_key":"my-api-key"}')
+        .to_return(body: '{"translatedText": "See you"}')
+
+      translation = service.translate('Hasta la vista', 'es', 'en')
+      expect(translation.detected_source_language).to eq 'es'
+      expect(translation.provider).to eq 'LibreTranslate'
+      expect(translation.text).to eq 'See you'
+    end
+
+    it 'returns translation with auto-detected source language' do
+      stub_request(:post, 'https://libretranslate.example.com/translate')
+        .with(body: '{"q":"Guten Morgen","source":"auto","target":"en","format":"html","api_key":"my-api-key"}')
+        .to_return(body: '{"detectedLanguage":{"confidence":92,"language":"de"},"translatedText":"Good morning"}')
+
+      translation = service.translate('Guten Morgen', nil, 'en')
+      expect(translation.detected_source_language).to be_nil
+      expect(translation.provider).to eq 'LibreTranslate'
+      expect(translation.text).to eq 'Good morning'
+    end
+  end
+end
diff --git a/spec/mailers/user_mailer_spec.rb b/spec/mailers/user_mailer_spec.rb
index 9c22f60f1..30824e7b4 100644
--- a/spec/mailers/user_mailer_spec.rb
+++ b/spec/mailers/user_mailer_spec.rb
@@ -94,4 +94,52 @@ describe UserMailer, type: :mailer do
       expect(mail.body.encoded).to include strike.text
     end
   end
+
+  describe 'webauthn_credential_deleted' do
+    let(:credential) { Fabricate(:webauthn_credential, user_id: receiver.id) }
+    let(:mail) { UserMailer.webauthn_credential_deleted(receiver, credential) }
+
+    it 'renders webauthn credential deleted notification' do
+      receiver.update!(locale: nil)
+      expect(mail.body.encoded).to include I18n.t('devise.mailer.webauthn_credential.deleted.title')
+    end
+
+    include_examples 'localized subject',
+                     'devise.mailer.webauthn_credential.deleted.subject'
+  end
+
+  describe 'suspicious_sign_in' do
+    let(:ip) { '192.168.0.1' }
+    let(:agent) { 'NCSA_Mosaic/2.0 (Windows 3.1)' }
+    let(:timestamp) { Time.now.utc }
+    let(:mail) { UserMailer.suspicious_sign_in(receiver, ip, agent, timestamp) }
+
+    it 'renders suspicious sign in notification' do
+      receiver.update!(locale: nil)
+      expect(mail.body.encoded).to include I18n.t('user_mailer.suspicious_sign_in.explanation')
+    end
+
+    include_examples 'localized subject',
+                     'user_mailer.suspicious_sign_in.subject'
+  end
+
+  describe 'appeal_approved' do
+    let(:appeal) { Fabricate(:appeal, account: receiver.account, approved_at: Time.now.utc) }
+    let(:mail) { UserMailer.appeal_approved(receiver, appeal) }
+
+    it 'renders appeal_approved notification' do
+      expect(mail.subject).to eq I18n.t('user_mailer.appeal_approved.subject', date: I18n.l(appeal.created_at))
+      expect(mail.body.encoded).to include I18n.t('user_mailer.appeal_approved.title')
+    end
+  end
+
+  describe 'appeal_rejected' do
+    let(:appeal) { Fabricate(:appeal, account: receiver.account, rejected_at: Time.now.utc) }
+    let(:mail) { UserMailer.appeal_rejected(receiver, appeal) }
+
+    it 'renders appeal_rejected notification' do
+      expect(mail.subject).to eq I18n.t('user_mailer.appeal_rejected.subject', date: I18n.l(appeal.created_at))
+      expect(mail.body.encoded).to include I18n.t('user_mailer.appeal_rejected.title')
+    end
+  end
 end
diff --git a/spec/models/account_spec.rb b/spec/models/account_spec.rb
index 1e5a80963..ae4e5ee32 100644
--- a/spec/models/account_spec.rb
+++ b/spec/models/account_spec.rb
@@ -704,12 +704,6 @@ RSpec.describe Account, type: :model do
   end
 
   describe 'validations' do
-    it 'has a valid fabricator' do
-      account = Fabricate.build(:account)
-      account.valid?
-      expect(account).to be_valid
-    end
-
     it 'is invalid without a username' do
       account = Fabricate.build(:account, username: nil)
       account.valid?
diff --git a/spec/models/account_warning_preset_spec.rb b/spec/models/account_warning_preset_spec.rb
new file mode 100644
index 000000000..f171df7c9
--- /dev/null
+++ b/spec/models/account_warning_preset_spec.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe AccountWarningPreset do
+  describe 'alphabetical' do
+    let(:first) { Fabricate(:account_warning_preset, title: 'aaa', text: 'aaa') }
+    let(:second) { Fabricate(:account_warning_preset, title: 'bbb', text: 'aaa') }
+    let(:third) { Fabricate(:account_warning_preset, title: 'bbb', text: 'bbb') }
+
+    it 'returns records in order of title and text' do
+      results = described_class.alphabetic
+
+      expect(results).to eq([first, second, third])
+    end
+  end
+end
diff --git a/spec/models/appeal_spec.rb b/spec/models/appeal_spec.rb
index 6aa013aba..12373a949 100644
--- a/spec/models/appeal_spec.rb
+++ b/spec/models/appeal_spec.rb
@@ -2,6 +2,37 @@
 
 require 'rails_helper'
 
-RSpec.describe Appeal, type: :model do
-  pending "add some examples to (or delete) #{__FILE__}"
+describe Appeal do
+  describe 'scopes' do
+    describe 'approved' do
+      let(:approved_appeal) { Fabricate(:appeal, approved_at: 10.days.ago) }
+      let(:not_approved_appeal) { Fabricate(:appeal, approved_at: nil) }
+
+      it 'finds the correct records' do
+        results = described_class.approved
+        expect(results).to eq([approved_appeal])
+      end
+    end
+
+    describe 'rejected' do
+      let(:rejected_appeal) { Fabricate(:appeal, rejected_at: 10.days.ago) }
+      let(:not_rejected_appeal) { Fabricate(:appeal, rejected_at: nil) }
+
+      it 'finds the correct records' do
+        results = described_class.rejected
+        expect(results).to eq([rejected_appeal])
+      end
+    end
+
+    describe 'pending' do
+      let(:approved_appeal) { Fabricate(:appeal, approved_at: 10.days.ago) }
+      let(:rejected_appeal) { Fabricate(:appeal, rejected_at: 10.days.ago) }
+      let(:pending_appeal) { Fabricate(:appeal, rejected_at: nil, approved_at: nil) }
+
+      it 'finds the correct records' do
+        results = described_class.pending
+        expect(results).to eq([pending_appeal])
+      end
+    end
+  end
 end
diff --git a/spec/models/block_spec.rb b/spec/models/block_spec.rb
index 64c39fce6..6e31786d0 100644
--- a/spec/models/block_spec.rb
+++ b/spec/models/block_spec.rb
@@ -4,11 +4,6 @@ require 'rails_helper'
 
 RSpec.describe Block, type: :model do
   describe 'validations' do
-    it 'has a valid fabricator' do
-      block = Fabricate.build(:block)
-      expect(block).to be_valid
-    end
-
     it 'is invalid without an account' do
       block = Fabricate.build(:block, account: nil)
       block.valid?
diff --git a/spec/models/custom_emoji_category_spec.rb b/spec/models/custom_emoji_category_spec.rb
index 74881b26c..30de07bd8 100644
--- a/spec/models/custom_emoji_category_spec.rb
+++ b/spec/models/custom_emoji_category_spec.rb
@@ -2,6 +2,13 @@
 
 require 'rails_helper'
 
-RSpec.describe CustomEmojiCategory, type: :model do
-  pending "add some examples to (or delete) #{__FILE__}"
+describe CustomEmojiCategory do
+  describe 'validations' do
+    it 'validates name presence' do
+      record = described_class.new(name: nil)
+
+      expect(record).to_not be_valid
+      expect(record).to model_have_error_on_field(:name)
+    end
+  end
 end
diff --git a/spec/models/domain_allow_spec.rb b/spec/models/domain_allow_spec.rb
index 18cf5fe4c..49e16376e 100644
--- a/spec/models/domain_allow_spec.rb
+++ b/spec/models/domain_allow_spec.rb
@@ -2,6 +2,17 @@
 
 require 'rails_helper'
 
-RSpec.describe DomainAllow, type: :model do
-  pending "add some examples to (or delete) #{__FILE__}"
+describe DomainAllow do
+  describe 'scopes' do
+    describe 'matches_domain' do
+      let(:domain) { Fabricate(:domain_allow, domain: 'example.com') }
+      let(:other_domain) { Fabricate(:domain_allow, domain: 'example.biz') }
+
+      it 'returns the correct records' do
+        results = described_class.matches_domain('example.com')
+
+        expect(results).to eq([domain])
+      end
+    end
+  end
 end
diff --git a/spec/models/domain_block_spec.rb b/spec/models/domain_block_spec.rb
index 6a5925b89..9839ee9d4 100644
--- a/spec/models/domain_block_spec.rb
+++ b/spec/models/domain_block_spec.rb
@@ -4,11 +4,6 @@ require 'rails_helper'
 
 RSpec.describe DomainBlock, type: :model do
   describe 'validations' do
-    it 'has a valid fabricator' do
-      domain_block = Fabricate.build(:domain_block)
-      expect(domain_block).to be_valid
-    end
-
     it 'is invalid without a domain' do
       domain_block = Fabricate.build(:domain_block, domain: nil)
       domain_block.valid?
diff --git a/spec/models/email_domain_block_spec.rb b/spec/models/email_domain_block_spec.rb
index 01a7a0f0e..3321ffc81 100644
--- a/spec/models/email_domain_block_spec.rb
+++ b/spec/models/email_domain_block_spec.rb
@@ -3,13 +3,6 @@
 require 'rails_helper'
 
 RSpec.describe EmailDomainBlock, type: :model do
-  describe 'validations' do
-    it 'has a valid fabricator' do
-      email_domain_block = Fabricate.build(:email_domain_block)
-      expect(email_domain_block).to be_valid
-    end
-  end
-
   describe 'block?' do
     let(:input) { nil }
 
diff --git a/spec/models/extended_description_spec.rb b/spec/models/extended_description_spec.rb
new file mode 100644
index 000000000..ecc27c0f6
--- /dev/null
+++ b/spec/models/extended_description_spec.rb
@@ -0,0 +1,29 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe ExtendedDescription do
+  describe '.current' do
+    context 'with the default values' do
+      it 'makes a new instance' do
+        record = described_class.current
+
+        expect(record.text).to be_nil
+        expect(record.updated_at).to be_nil
+      end
+    end
+
+    context 'with a custom setting value' do
+      before do
+        setting = instance_double(Setting, value: 'Extended text', updated_at: 10.days.ago)
+        allow(Setting).to receive(:find_by).with(var: 'site_extended_description').and_return(setting)
+      end
+
+      it 'has the privacy text' do
+        record = described_class.current
+
+        expect(record.text).to eq('Extended text')
+      end
+    end
+  end
+end
diff --git a/spec/models/follow_spec.rb b/spec/models/follow_spec.rb
index f49d58532..a9a9af88a 100644
--- a/spec/models/follow_spec.rb
+++ b/spec/models/follow_spec.rb
@@ -9,11 +9,6 @@ RSpec.describe Follow, type: :model do
   describe 'validations' do
     subject { Follow.new(account: alice, target_account: bob, rate_limit: true) }
 
-    it 'has a valid fabricator' do
-      follow = Fabricate.build(:follow)
-      expect(follow).to be_valid
-    end
-
     it 'is invalid without an account' do
       follow = Fabricate.build(:follow, account: nil)
       follow.valid?
diff --git a/spec/models/import_spec.rb b/spec/models/import_spec.rb
index 81c75a964..1c8474413 100644
--- a/spec/models/import_spec.rb
+++ b/spec/models/import_spec.rb
@@ -23,6 +23,11 @@ RSpec.describe Import, type: :model do
       expect(import).to model_have_error_on_field(:data)
     end
 
+    it 'is invalid with malformed data' do
+      import = Import.create(account: account, type: type, data: StringIO.new('\"test'))
+      expect(import).to model_have_error_on_field(:data)
+    end
+
     it 'is invalid with too many rows in data' do
       import = Import.create(account: account, type: type, data: StringIO.new("foo@bar.com\n" * (ImportService::ROWS_PROCESSING_LIMIT + 10)))
       expect(import).to model_have_error_on_field(:data)
diff --git a/spec/models/ip_block_spec.rb b/spec/models/ip_block_spec.rb
index 4c4028576..ed5882667 100644
--- a/spec/models/ip_block_spec.rb
+++ b/spec/models/ip_block_spec.rb
@@ -2,6 +2,14 @@
 
 require 'rails_helper'
 
-RSpec.describe IpBlock, type: :model do
-  pending "add some examples to (or delete) #{__FILE__}"
+describe IpBlock do
+  describe 'to_log_human_identifier' do
+    let(:ip_block) { described_class.new(ip: '192.168.0.1') }
+
+    it 'combines the IP and prefix into a string' do
+      result = ip_block.to_log_human_identifier
+
+      expect(result).to eq('192.168.0.1/32')
+    end
+  end
 end
diff --git a/spec/models/marker_spec.rb b/spec/models/marker_spec.rb
index e8561c4c6..51dd58438 100644
--- a/spec/models/marker_spec.rb
+++ b/spec/models/marker_spec.rb
@@ -2,6 +2,15 @@
 
 require 'rails_helper'
 
-RSpec.describe Marker, type: :model do
-  pending "add some examples to (or delete) #{__FILE__}"
+describe Marker do
+  describe 'validations' do
+    describe 'timeline' do
+      it 'must be included in valid list' do
+        record = described_class.new(timeline: 'not real timeline')
+
+        expect(record).to_not be_valid
+        expect(record).to model_have_error_on_field(:timeline)
+      end
+    end
+  end
 end
diff --git a/spec/models/mention_spec.rb b/spec/models/mention_spec.rb
index 3de2b4a07..044bb80cf 100644
--- a/spec/models/mention_spec.rb
+++ b/spec/models/mention_spec.rb
@@ -4,11 +4,6 @@ require 'rails_helper'
 
 RSpec.describe Mention, type: :model do
   describe 'validations' do
-    it 'has a valid fabricator' do
-      mention = Fabricate.build(:mention)
-      expect(mention).to be_valid
-    end
-
     it 'is invalid without an account' do
       mention = Fabricate.build(:mention, account: nil)
       mention.valid?
diff --git a/spec/models/one_time_key_spec.rb b/spec/models/one_time_key_spec.rb
index 2a5fe8a9d..6ff7ffc5c 100644
--- a/spec/models/one_time_key_spec.rb
+++ b/spec/models/one_time_key_spec.rb
@@ -2,5 +2,22 @@
 
 require 'rails_helper'
 
-RSpec.describe OneTimeKey, type: :model do
+describe OneTimeKey do
+  describe 'validations' do
+    context 'with an invalid signature' do
+      let(:one_time_key) { Fabricate.build(:one_time_key, signature: 'wrong!') }
+
+      it 'is invalid' do
+        expect(one_time_key).to_not be_valid
+      end
+    end
+
+    context 'with an invalid key' do
+      let(:one_time_key) { Fabricate.build(:one_time_key, key: 'wrong!') }
+
+      it 'is invalid' do
+        expect(one_time_key).to_not be_valid
+      end
+    end
+  end
 end
diff --git a/spec/models/poll_spec.rb b/spec/models/poll_spec.rb
index 474399bf6..8ae04ca41 100644
--- a/spec/models/poll_spec.rb
+++ b/spec/models/poll_spec.rb
@@ -2,6 +2,31 @@
 
 require 'rails_helper'
 
-RSpec.describe Poll, type: :model do
-  pending "add some examples to (or delete) #{__FILE__}"
+describe Poll do
+  describe 'scopes' do
+    let(:status) { Fabricate(:status) }
+    let(:attached_poll) { Fabricate(:poll, status: status) }
+    let(:not_attached_poll) do
+      Fabricate(:poll).tap do |poll|
+        poll.status = nil
+        poll.save(validate: false)
+      end
+    end
+
+    describe 'attached' do
+      it 'finds the correct records' do
+        results = described_class.attached
+
+        expect(results).to eq([attached_poll])
+      end
+    end
+
+    describe 'unattached' do
+      it 'finds the correct records' do
+        results = described_class.unattached
+
+        expect(results).to eq([not_attached_poll])
+      end
+    end
+  end
 end
diff --git a/spec/models/preview_card_provider_spec.rb b/spec/models/preview_card_provider_spec.rb
new file mode 100644
index 000000000..7425b9394
--- /dev/null
+++ b/spec/models/preview_card_provider_spec.rb
@@ -0,0 +1,42 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe PreviewCardProvider do
+  describe 'scopes' do
+    let(:trendable_and_reviewed) { Fabricate(:preview_card_provider, trendable: true, reviewed_at: 5.days.ago) }
+    let(:not_trendable_and_not_reviewed) { Fabricate(:preview_card_provider, trendable: false, reviewed_at: nil) }
+
+    describe 'trendable' do
+      it 'returns the relevant records' do
+        results = described_class.trendable
+
+        expect(results).to eq([trendable_and_reviewed])
+      end
+    end
+
+    describe 'not_trendable' do
+      it 'returns the relevant records' do
+        results = described_class.not_trendable
+
+        expect(results).to eq([not_trendable_and_not_reviewed])
+      end
+    end
+
+    describe 'reviewed' do
+      it 'returns the relevant records' do
+        results = described_class.reviewed
+
+        expect(results).to eq([trendable_and_reviewed])
+      end
+    end
+
+    describe 'pending_review' do
+      it 'returns the relevant records' do
+        results = described_class.pending_review
+
+        expect(results).to eq([not_trendable_and_not_reviewed])
+      end
+    end
+  end
+end
diff --git a/spec/models/privacy_policy_spec.rb b/spec/models/privacy_policy_spec.rb
new file mode 100644
index 000000000..0d7471375
--- /dev/null
+++ b/spec/models/privacy_policy_spec.rb
@@ -0,0 +1,28 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe PrivacyPolicy do
+  describe '.current' do
+    context 'with the default values' do
+      it 'has the privacy text' do
+        policy = described_class.current
+
+        expect(policy.text).to eq(PrivacyPolicy::DEFAULT_PRIVACY_POLICY)
+      end
+    end
+
+    context 'with a custom setting value' do
+      before do
+        terms_setting = instance_double(Setting, value: 'Terms text', updated_at: 10.days.ago)
+        allow(Setting).to receive(:find_by).with(var: 'site_terms').and_return(terms_setting)
+      end
+
+      it 'has the privacy text' do
+        policy = described_class.current
+
+        expect(policy.text).to eq('Terms text')
+      end
+    end
+  end
+end
diff --git a/spec/models/report_spec.rb b/spec/models/report_spec.rb
index d5d40a34f..20a048c33 100644
--- a/spec/models/report_spec.rb
+++ b/spec/models/report_spec.rb
@@ -121,12 +121,6 @@ describe Report do
   end
 
   describe 'validations' do
-    it 'has a valid fabricator' do
-      report = Fabricate(:report)
-      report.valid?
-      expect(report).to be_valid
-    end
-
     it 'is invalid if comment is longer than 1000 characters' do
       report = Fabricate.build(:report, comment: Faker::Lorem.characters(number: 1001))
       report.valid?
diff --git a/spec/models/rule_spec.rb b/spec/models/rule_spec.rb
index d5ec13ddf..c9b9c5502 100644
--- a/spec/models/rule_spec.rb
+++ b/spec/models/rule_spec.rb
@@ -2,6 +2,18 @@
 
 require 'rails_helper'
 
-RSpec.describe Rule, type: :model do
-  pending "add some examples to (or delete) #{__FILE__}"
+describe Rule do
+  describe 'scopes' do
+    describe 'ordered' do
+      let(:deleted_rule) { Fabricate(:rule, deleted_at: 10.days.ago) }
+      let(:first_rule) { Fabricate(:rule, deleted_at: nil, priority: 1) }
+      let(:last_rule) { Fabricate(:rule, deleted_at: nil, priority: 10) }
+
+      it 'finds the correct records' do
+        results = described_class.ordered
+
+        expect(results).to eq([first_rule, last_rule])
+      end
+    end
+  end
 end
diff --git a/spec/models/status_edit_spec.rb b/spec/models/status_edit_spec.rb
index 0b9fa7087..2d3351452 100644
--- a/spec/models/status_edit_spec.rb
+++ b/spec/models/status_edit_spec.rb
@@ -2,6 +2,12 @@
 
 require 'rails_helper'
 
-RSpec.describe StatusEdit, type: :model do
-  pending "add some examples to (or delete) #{__FILE__}"
+describe StatusEdit do
+  describe '#reblog?' do
+    it 'returns false' do
+      record = described_class.new
+
+      expect(record).to_not be_a_reblog
+    end
+  end
 end
diff --git a/spec/models/status_spec.rb b/spec/models/status_spec.rb
index 7022c5f00..d1caf267c 100644
--- a/spec/models/status_spec.rb
+++ b/spec/models/status_spec.rb
@@ -114,6 +114,85 @@ RSpec.describe Status, type: :model do
     end
   end
 
+  describe '#translatable?' do
+    before do
+      allow(TranslationService).to receive(:configured?).and_return(true)
+      allow(TranslationService).to receive(:configured).and_return(TranslationService.new)
+      allow(TranslationService.configured).to receive(:supported?).with('es', 'en').and_return(true)
+
+      subject.language = 'es'
+      subject.visibility = :public
+    end
+
+    context 'all conditions are satisfied' do
+      it 'returns true' do
+        expect(subject.translatable?).to be true
+      end
+    end
+
+    context 'translation service is not configured' do
+      it 'returns false' do
+        allow(TranslationService).to receive(:configured?).and_return(false)
+        allow(TranslationService).to receive(:configured).and_raise(TranslationService::NotConfiguredError)
+        expect(subject.translatable?).to be false
+      end
+    end
+
+    context 'status language is nil' do
+      it 'returns true' do
+        subject.language = nil
+        allow(TranslationService.configured).to receive(:supported?).with(nil, 'en').and_return(true)
+        expect(subject.translatable?).to be true
+      end
+    end
+
+    context 'status language is same as default locale' do
+      it 'returns false' do
+        subject.language = I18n.locale
+        expect(subject.translatable?).to be false
+      end
+    end
+
+    context 'status language is unsupported' do
+      it 'returns false' do
+        subject.language = 'af'
+        allow(TranslationService.configured).to receive(:supported?).with('af', 'en').and_return(false)
+        expect(subject.translatable?).to be false
+      end
+    end
+
+    context 'default locale is unsupported' do
+      it 'returns false' do
+        allow(TranslationService.configured).to receive(:supported?).with('es', 'af').and_return(false)
+        I18n.with_locale('af') do
+          expect(subject.translatable?).to be false
+        end
+      end
+    end
+
+    context 'default locale has region' do
+      it 'returns true' do
+        I18n.with_locale('en-GB') do
+          expect(subject.translatable?).to be true
+        end
+      end
+    end
+
+    context 'status text is blank' do
+      it 'returns false' do
+        subject.text = ' '
+        expect(subject.translatable?).to be false
+      end
+    end
+
+    context 'status visiblity is hidden' do
+      it 'returns false' do
+        subject.visibility = 'limited'
+        expect(subject.translatable?).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/trends/tags_spec.rb b/spec/models/trends/tags_spec.rb
index a9473e15c..09ac918d0 100644
--- a/spec/models/trends/tags_spec.rb
+++ b/spec/models/trends/tags_spec.rb
@@ -24,7 +24,9 @@ RSpec.describe Trends::Tags do
   end
 
   describe '#query' do
-    pending
+    it 'returns a composable query scope' do
+      expect(subject.query).to be_a Trends::Query
+    end
   end
 
   describe '#refresh' do
diff --git a/spec/policies/account_policy_spec.rb b/spec/policies/account_policy_spec.rb
index 0f23fd97e..d96153233 100644
--- a/spec/policies/account_policy_spec.rb
+++ b/spec/policies/account_policy_spec.rb
@@ -116,4 +116,44 @@ RSpec.describe AccountPolicy do
       end
     end
   end
+
+  permissions :review? do
+    context 'admin' do
+      it 'permits' do
+        expect(subject).to permit(admin)
+      end
+    end
+
+    context 'not admin' do
+      it 'denies' do
+        expect(subject).to_not permit(john)
+      end
+    end
+  end
+
+  permissions :destroy? do
+    context 'admin' do
+      context 'with a temporarily suspended account' do
+        before { allow(alice).to receive(:suspended_temporarily?).and_return(true) }
+
+        it 'permits' do
+          expect(subject).to permit(admin, alice)
+        end
+      end
+
+      context 'with a not temporarily suspended account' do
+        before { allow(alice).to receive(:suspended_temporarily?).and_return(false) }
+
+        it 'denies' do
+          expect(subject).to_not permit(admin, alice)
+        end
+      end
+    end
+
+    context 'not admin' do
+      it 'denies' do
+        expect(subject).to_not permit(john, alice)
+      end
+    end
+  end
 end
diff --git a/spec/policies/account_warning_preset_policy_spec.rb b/spec/policies/account_warning_preset_policy_spec.rb
new file mode 100644
index 000000000..63bf33de2
--- /dev/null
+++ b/spec/policies/account_warning_preset_policy_spec.rb
@@ -0,0 +1,24 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+require 'pundit/rspec'
+
+describe AccountWarningPresetPolicy do
+  let(:policy) { described_class }
+  let(:admin)   { Fabricate(:user, role: UserRole.find_by(name: 'Admin')).account }
+  let(:john)    { Fabricate(:account) }
+
+  permissions :index?, :create?, :update?, :destroy? do
+    context 'with an admin' do
+      it 'permits' do
+        expect(policy).to permit(admin, Tag)
+      end
+    end
+
+    context 'with a non-admin' do
+      it 'denies' do
+        expect(policy).to_not permit(john, Tag)
+      end
+    end
+  end
+end
diff --git a/spec/policies/admin/status_policy_spec.rb b/spec/policies/admin/status_policy_spec.rb
new file mode 100644
index 000000000..9e81a4f5f
--- /dev/null
+++ b/spec/policies/admin/status_policy_spec.rb
@@ -0,0 +1,51 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+require 'pundit/rspec'
+
+describe Admin::StatusPolicy do
+  let(:policy) { described_class }
+  let(:admin)   { Fabricate(:user, role: UserRole.find_by(name: 'Admin')).account }
+  let(:john)    { Fabricate(:account) }
+  let(:status) { Fabricate(:status) }
+
+  permissions :index?, :update?, :review?, :destroy? do
+    context 'with an admin' do
+      it 'permits' do
+        expect(policy).to permit(admin, Tag)
+      end
+    end
+
+    context 'with a non-admin' do
+      it 'denies' do
+        expect(policy).to_not permit(john, Tag)
+      end
+    end
+  end
+
+  permissions :show? do
+    context 'with an admin' do
+      context 'with a public visible status' do
+        before { allow(status).to receive(:public_visibility?).and_return(true) }
+
+        it 'permits' do
+          expect(policy).to permit(admin, status)
+        end
+      end
+
+      context 'with a not public visible status' do
+        before { allow(status).to receive(:public_visibility?).and_return(false) }
+
+        it 'denies' do
+          expect(policy).to_not permit(admin, status)
+        end
+      end
+    end
+
+    context 'with a non admin' do
+      it 'denies' do
+        expect(policy).to_not permit(john, status)
+      end
+    end
+  end
+end
diff --git a/spec/policies/announcement_policy_spec.rb b/spec/policies/announcement_policy_spec.rb
new file mode 100644
index 000000000..3d230b3cb
--- /dev/null
+++ b/spec/policies/announcement_policy_spec.rb
@@ -0,0 +1,24 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+require 'pundit/rspec'
+
+describe AnnouncementPolicy do
+  let(:policy) { described_class }
+  let(:admin)   { Fabricate(:user, role: UserRole.find_by(name: 'Admin')).account }
+  let(:john)    { Fabricate(:account) }
+
+  permissions :index?, :create?, :update?, :destroy? do
+    context 'with an admin' do
+      it 'permits' do
+        expect(policy).to permit(admin, Tag)
+      end
+    end
+
+    context 'with a non-admin' do
+      it 'denies' do
+        expect(policy).to_not permit(john, Tag)
+      end
+    end
+  end
+end
diff --git a/spec/policies/appeal_policy_spec.rb b/spec/policies/appeal_policy_spec.rb
new file mode 100644
index 000000000..d7498eb9f
--- /dev/null
+++ b/spec/policies/appeal_policy_spec.rb
@@ -0,0 +1,51 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+require 'pundit/rspec'
+
+describe AppealPolicy do
+  let(:policy) { described_class }
+  let(:admin)   { Fabricate(:user, role: UserRole.find_by(name: 'Admin')).account }
+  let(:john)    { Fabricate(:account) }
+  let(:appeal) { Fabricate(:appeal) }
+
+  permissions :index? do
+    context 'with an admin' do
+      it 'permits' do
+        expect(policy).to permit(admin, Tag)
+      end
+    end
+
+    context 'with a non-admin' do
+      it 'denies' do
+        expect(policy).to_not permit(john, Tag)
+      end
+    end
+  end
+
+  permissions :reject? do
+    context 'with an admin' do
+      context 'with a pending appeal' do
+        before { allow(appeal).to receive(:pending?).and_return(true) }
+
+        it 'permits' do
+          expect(policy).to permit(admin, appeal)
+        end
+      end
+
+      context 'with a not pending appeal' do
+        before { allow(appeal).to receive(:pending?).and_return(false) }
+
+        it 'denies' do
+          expect(policy).to_not permit(admin, appeal)
+        end
+      end
+    end
+
+    context 'with a non admin' do
+      it 'denies' do
+        expect(policy).to_not permit(john, appeal)
+      end
+    end
+  end
+end
diff --git a/spec/policies/canonical_email_block_policy_spec.rb b/spec/policies/canonical_email_block_policy_spec.rb
new file mode 100644
index 000000000..0e55febfa
--- /dev/null
+++ b/spec/policies/canonical_email_block_policy_spec.rb
@@ -0,0 +1,24 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+require 'pundit/rspec'
+
+describe CanonicalEmailBlockPolicy do
+  let(:policy) { described_class }
+  let(:admin)   { Fabricate(:user, role: UserRole.find_by(name: 'Admin')).account }
+  let(:john)    { Fabricate(:account) }
+
+  permissions :index?, :show?, :test?, :create?, :destroy? do
+    context 'with an admin' do
+      it 'permits' do
+        expect(policy).to permit(admin, Tag)
+      end
+    end
+
+    context 'with a non-admin' do
+      it 'denies' do
+        expect(policy).to_not permit(john, Tag)
+      end
+    end
+  end
+end
diff --git a/spec/policies/delivery_policy_spec.rb b/spec/policies/delivery_policy_spec.rb
new file mode 100644
index 000000000..fbcbf390d
--- /dev/null
+++ b/spec/policies/delivery_policy_spec.rb
@@ -0,0 +1,24 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+require 'pundit/rspec'
+
+describe DeliveryPolicy do
+  let(:policy) { described_class }
+  let(:admin)   { Fabricate(:user, role: UserRole.find_by(name: 'Admin')).account }
+  let(:john)    { Fabricate(:account) }
+
+  permissions :clear_delivery_errors?, :restart_delivery?, :stop_delivery? do
+    context 'with an admin' do
+      it 'permits' do
+        expect(policy).to permit(admin, Tag)
+      end
+    end
+
+    context 'with a non-admin' do
+      it 'denies' do
+        expect(policy).to_not permit(john, Tag)
+      end
+    end
+  end
+end
diff --git a/spec/policies/email_domain_block_policy_spec.rb b/spec/policies/email_domain_block_policy_spec.rb
index 913075c3d..e7c455907 100644
--- a/spec/policies/email_domain_block_policy_spec.rb
+++ b/spec/policies/email_domain_block_policy_spec.rb
@@ -8,7 +8,7 @@ RSpec.describe EmailDomainBlockPolicy do
   let(:admin)   { Fabricate(:user, role: UserRole.find_by(name: 'Admin')).account }
   let(:john)    { Fabricate(:account) }
 
-  permissions :index?, :create?, :destroy? do
+  permissions :index?, :show?, :create?, :destroy? do
     context 'admin' do
       it 'permits' do
         expect(subject).to permit(admin, EmailDomainBlock)
diff --git a/spec/policies/follow_recommendation_policy_spec.rb b/spec/policies/follow_recommendation_policy_spec.rb
new file mode 100644
index 000000000..01f4da0be
--- /dev/null
+++ b/spec/policies/follow_recommendation_policy_spec.rb
@@ -0,0 +1,24 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+require 'pundit/rspec'
+
+describe FollowRecommendationPolicy do
+  let(:policy) { described_class }
+  let(:admin)   { Fabricate(:user, role: UserRole.find_by(name: 'Admin')).account }
+  let(:john)    { Fabricate(:account) }
+
+  permissions :show?, :suppress?, :unsuppress? do
+    context 'with an admin' do
+      it 'permits' do
+        expect(policy).to permit(admin, Tag)
+      end
+    end
+
+    context 'with a non-admin' do
+      it 'denies' do
+        expect(policy).to_not permit(john, Tag)
+      end
+    end
+  end
+end
diff --git a/spec/policies/ip_block_policy_spec.rb b/spec/policies/ip_block_policy_spec.rb
new file mode 100644
index 000000000..3cfa85863
--- /dev/null
+++ b/spec/policies/ip_block_policy_spec.rb
@@ -0,0 +1,24 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+require 'pundit/rspec'
+
+describe IpBlockPolicy do
+  let(:policy) { described_class }
+  let(:admin)   { Fabricate(:user, role: UserRole.find_by(name: 'Admin')).account }
+  let(:john)    { Fabricate(:account) }
+
+  permissions :index?, :show?, :create?, :update?, :destroy? do
+    context 'with an admin' do
+      it 'permits' do
+        expect(policy).to permit(admin, Tag)
+      end
+    end
+
+    context 'with a non-admin' do
+      it 'denies' do
+        expect(policy).to_not permit(john, Tag)
+      end
+    end
+  end
+end
diff --git a/spec/policies/preview_card_policy_spec.rb b/spec/policies/preview_card_policy_spec.rb
new file mode 100644
index 000000000..d6675c5b3
--- /dev/null
+++ b/spec/policies/preview_card_policy_spec.rb
@@ -0,0 +1,24 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+require 'pundit/rspec'
+
+describe PreviewCardPolicy do
+  let(:policy) { described_class }
+  let(:admin)   { Fabricate(:user, role: UserRole.find_by(name: 'Admin')).account }
+  let(:john)    { Fabricate(:account) }
+
+  permissions :index?, :review? do
+    context 'with an admin' do
+      it 'permits' do
+        expect(policy).to permit(admin, Tag)
+      end
+    end
+
+    context 'with a non-admin' do
+      it 'denies' do
+        expect(policy).to_not permit(john, Tag)
+      end
+    end
+  end
+end
diff --git a/spec/policies/preview_card_provider_policy_spec.rb b/spec/policies/preview_card_provider_policy_spec.rb
new file mode 100644
index 000000000..8d3715de9
--- /dev/null
+++ b/spec/policies/preview_card_provider_policy_spec.rb
@@ -0,0 +1,24 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+require 'pundit/rspec'
+
+describe PreviewCardProviderPolicy do
+  let(:policy) { described_class }
+  let(:admin)   { Fabricate(:user, role: UserRole.find_by(name: 'Admin')).account }
+  let(:john)    { Fabricate(:account) }
+
+  permissions :index?, :review? do
+    context 'with an admin' do
+      it 'permits' do
+        expect(policy).to permit(admin, Tag)
+      end
+    end
+
+    context 'with a non-admin' do
+      it 'denies' do
+        expect(policy).to_not permit(john, Tag)
+      end
+    end
+  end
+end
diff --git a/spec/policies/rule_policy_spec.rb b/spec/policies/rule_policy_spec.rb
new file mode 100644
index 000000000..0e45f6df0
--- /dev/null
+++ b/spec/policies/rule_policy_spec.rb
@@ -0,0 +1,24 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+require 'pundit/rspec'
+
+describe RulePolicy do
+  let(:policy) { described_class }
+  let(:admin)   { Fabricate(:user, role: UserRole.find_by(name: 'Admin')).account }
+  let(:john)    { Fabricate(:account) }
+
+  permissions :index?, :create?, :update?, :destroy? do
+    context 'with an admin' do
+      it 'permits' do
+        expect(policy).to permit(admin, Tag)
+      end
+    end
+
+    context 'with a non-admin' do
+      it 'denies' do
+        expect(policy).to_not permit(john, Tag)
+      end
+    end
+  end
+end
diff --git a/spec/policies/settings_policy_spec.rb b/spec/policies/settings_policy_spec.rb
index e16ee51a4..3268c1622 100644
--- a/spec/policies/settings_policy_spec.rb
+++ b/spec/policies/settings_policy_spec.rb
@@ -8,7 +8,7 @@ RSpec.describe SettingsPolicy do
   let(:admin)   { Fabricate(:user, role: UserRole.find_by(name: 'Admin')).account }
   let(:john)    { Fabricate(:account) }
 
-  permissions :update?, :show? do
+  permissions :update?, :show?, :destroy? do
     context 'admin?' do
       it 'permits' do
         expect(subject).to permit(admin, Settings)
diff --git a/spec/policies/status_policy_spec.rb b/spec/policies/status_policy_spec.rb
index 2afcfe96e..9a30aef3c 100644
--- a/spec/policies/status_policy_spec.rb
+++ b/spec/policies/status_policy_spec.rb
@@ -39,6 +39,14 @@ RSpec.describe StatusPolicy, type: :model do
       expect(subject).to permit(alice, status)
     end
 
+    it 'grants access when direct and non-owner viewer is mentioned and mentions are loaded' do
+      status.visibility = :direct
+      status.mentions = [Fabricate(:mention, account: bob)]
+      status.mentions.load
+
+      expect(subject).to permit(bob, status)
+    end
+
     it 'denies access when direct and viewer is not mentioned' do
       viewer = Fabricate(:account)
       status.visibility = :direct
diff --git a/spec/policies/tag_policy_spec.rb b/spec/policies/tag_policy_spec.rb
index 9be7140fc..fb09fdd3b 100644
--- a/spec/policies/tag_policy_spec.rb
+++ b/spec/policies/tag_policy_spec.rb
@@ -8,7 +8,7 @@ RSpec.describe TagPolicy do
   let(:admin)   { Fabricate(:user, role: UserRole.find_by(name: 'Admin')).account }
   let(:john)    { Fabricate(:account) }
 
-  permissions :index?, :show?, :update? do
+  permissions :index?, :show?, :update?, :review? do
     context 'staff?' do
       it 'permits' do
         expect(subject).to permit(admin, Tag)
diff --git a/spec/policies/webhook_policy_spec.rb b/spec/policies/webhook_policy_spec.rb
new file mode 100644
index 000000000..1eac8932d
--- /dev/null
+++ b/spec/policies/webhook_policy_spec.rb
@@ -0,0 +1,24 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+require 'pundit/rspec'
+
+describe WebhookPolicy do
+  let(:policy) { described_class }
+  let(:admin)   { Fabricate(:user, role: UserRole.find_by(name: 'Admin')).account }
+  let(:john)    { Fabricate(:account) }
+
+  permissions :index?, :create?, :show?, :update?, :enable?, :disable?, :rotate_secret?, :destroy? do
+    context 'with an admin' do
+      it 'permits' do
+        expect(policy).to permit(admin, Tag)
+      end
+    end
+
+    context 'with a non-admin' do
+      it 'denies' do
+        expect(policy).to_not permit(john, Tag)
+      end
+    end
+  end
+end
diff --git a/spec/validators/email_mx_validator_spec.rb b/spec/validators/email_mx_validator_spec.rb
index ffb6851d0..a11b8e01e 100644
--- a/spec/validators/email_mx_validator_spec.rb
+++ b/spec/validators/email_mx_validator_spec.rb
@@ -41,6 +41,22 @@ describe EmailMxValidator do
       expect(user.errors).to_not have_received(:add)
     end
 
+    it 'adds an error if the TagManager fails to normalize domain' do
+      double = instance_double(TagManager)
+      allow(TagManager).to receive(:instance).and_return(double)
+      allow(double).to receive(:normalize_domain).with('example.com').and_raise(Addressable::URI::InvalidURIError)
+
+      user = double(email: 'foo@example.com', errors: double(add: nil))
+      subject.validate(user)
+      expect(user.errors).to have_received(:add)
+    end
+
+    it 'adds an error if the domain email portion is blank' do
+      user = double(email: 'foo@', errors: double(add: nil))
+      subject.validate(user)
+      expect(user.errors).to have_received(:add)
+    end
+
     it 'adds an error if the email domain name contains empty labels' do
       resolver = double
 
diff --git a/spec/workers/admin/account_deletion_worker_spec.rb b/spec/workers/admin/account_deletion_worker_spec.rb
new file mode 100644
index 000000000..631cab664
--- /dev/null
+++ b/spec/workers/admin/account_deletion_worker_spec.rb
@@ -0,0 +1,19 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe Admin::AccountDeletionWorker do
+  let(:worker) { described_class.new }
+
+  describe 'perform' do
+    let(:account) { Fabricate(:account) }
+    let(:service) { instance_double(DeleteAccountService, call: true) }
+
+    it 'calls delete account service' do
+      allow(DeleteAccountService).to receive(:new).and_return(service)
+      worker.perform(account.id)
+
+      expect(service).to have_received(:call).with(account, { reserve_email: true, reserve_username: true })
+    end
+  end
+end
diff --git a/spec/workers/cache_buster_worker_spec.rb b/spec/workers/cache_buster_worker_spec.rb
new file mode 100644
index 000000000..adeb287fa
--- /dev/null
+++ b/spec/workers/cache_buster_worker_spec.rb
@@ -0,0 +1,19 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe CacheBusterWorker do
+  let(:worker) { described_class.new }
+
+  describe 'perform' do
+    let(:path) { 'https://example.com' }
+    let(:service) { instance_double(CacheBuster, bust: true) }
+
+    it 'calls the cache buster' do
+      allow(CacheBuster).to receive(:new).and_return(service)
+      worker.perform(path)
+
+      expect(service).to have_received(:bust).with(path)
+    end
+  end
+end
diff --git a/spec/workers/poll_expiration_notify_worker_spec.rb b/spec/workers/poll_expiration_notify_worker_spec.rb
new file mode 100644
index 000000000..8229db815
--- /dev/null
+++ b/spec/workers/poll_expiration_notify_worker_spec.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe PollExpirationNotifyWorker do
+  let(:worker) { described_class.new }
+
+  describe 'perform' do
+    it 'runs without error for missing record' do
+      expect { worker.perform(nil) }.to_not raise_error
+    end
+  end
+end
diff --git a/spec/workers/post_process_media_worker_spec.rb b/spec/workers/post_process_media_worker_spec.rb
new file mode 100644
index 000000000..33072704b
--- /dev/null
+++ b/spec/workers/post_process_media_worker_spec.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe PostProcessMediaWorker do
+  let(:worker) { described_class.new }
+
+  describe 'perform' do
+    it 'runs without error for missing record' do
+      expect { worker.perform(nil) }.to_not raise_error
+    end
+  end
+end
diff --git a/spec/workers/push_conversation_worker_spec.rb b/spec/workers/push_conversation_worker_spec.rb
new file mode 100644
index 000000000..5fbb4c685
--- /dev/null
+++ b/spec/workers/push_conversation_worker_spec.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe PushConversationWorker do
+  let(:worker) { described_class.new }
+
+  describe 'perform' do
+    it 'runs without error for missing record' do
+      expect { worker.perform(nil) }.to_not raise_error
+    end
+  end
+end
diff --git a/spec/workers/push_encrypted_message_worker_spec.rb b/spec/workers/push_encrypted_message_worker_spec.rb
new file mode 100644
index 000000000..3cd04ce7b
--- /dev/null
+++ b/spec/workers/push_encrypted_message_worker_spec.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe PushEncryptedMessageWorker do
+  let(:worker) { described_class.new }
+
+  describe 'perform' do
+    it 'runs without error for missing record' do
+      expect { worker.perform(nil) }.to_not raise_error
+    end
+  end
+end
diff --git a/spec/workers/push_update_worker_spec.rb b/spec/workers/push_update_worker_spec.rb
new file mode 100644
index 000000000..c8f94fa82
--- /dev/null
+++ b/spec/workers/push_update_worker_spec.rb
@@ -0,0 +1,16 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe PushUpdateWorker do
+  let(:worker) { described_class.new }
+
+  describe 'perform' do
+    it 'runs without error for missing record' do
+      account_id = nil
+      status_id = nil
+
+      expect { worker.perform(account_id, status_id) }.to_not raise_error
+    end
+  end
+end
diff --git a/spec/workers/redownload_avatar_worker_spec.rb b/spec/workers/redownload_avatar_worker_spec.rb
new file mode 100644
index 000000000..b44ae9f03
--- /dev/null
+++ b/spec/workers/redownload_avatar_worker_spec.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe RedownloadAvatarWorker do
+  let(:worker) { described_class.new }
+
+  describe 'perform' do
+    it 'runs without error for missing record' do
+      expect { worker.perform(nil) }.to_not raise_error
+    end
+  end
+end
diff --git a/spec/workers/redownload_header_worker_spec.rb b/spec/workers/redownload_header_worker_spec.rb
new file mode 100644
index 000000000..767ae7a5a
--- /dev/null
+++ b/spec/workers/redownload_header_worker_spec.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe RedownloadHeaderWorker do
+  let(:worker) { described_class.new }
+
+  describe 'perform' do
+    it 'runs without error for missing record' do
+      expect { worker.perform(nil) }.to_not raise_error
+    end
+  end
+end
diff --git a/spec/workers/remove_featured_tag_worker_spec.rb b/spec/workers/remove_featured_tag_worker_spec.rb
new file mode 100644
index 000000000..a64bd0605
--- /dev/null
+++ b/spec/workers/remove_featured_tag_worker_spec.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe RemoveFeaturedTagWorker do
+  let(:worker) { described_class.new }
+
+  describe 'perform' do
+    it 'runs without error for missing record' do
+      account_id = nil
+      featured_tag_id = nil
+      expect { worker.perform(account_id, featured_tag_id) }.to_not raise_error
+    end
+  end
+end
diff --git a/spec/workers/resolve_account_worker_spec.rb b/spec/workers/resolve_account_worker_spec.rb
new file mode 100644
index 000000000..6f3cff099
--- /dev/null
+++ b/spec/workers/resolve_account_worker_spec.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe ResolveAccountWorker do
+  let(:worker) { described_class.new }
+
+  describe 'perform' do
+    it 'runs without error for missing record' do
+      expect { worker.perform(nil) }.to_not raise_error
+    end
+  end
+end
diff --git a/spec/workers/scheduler/indexing_scheduler_spec.rb b/spec/workers/scheduler/indexing_scheduler_spec.rb
new file mode 100644
index 000000000..568f0fc84
--- /dev/null
+++ b/spec/workers/scheduler/indexing_scheduler_spec.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe Scheduler::IndexingScheduler do
+  let(:worker) { described_class.new }
+
+  describe 'perform' do
+    it 'runs without error' do
+      expect { worker.perform }.to_not raise_error
+    end
+  end
+end
diff --git a/spec/workers/scheduler/instance_refresh_scheduler_spec.rb b/spec/workers/scheduler/instance_refresh_scheduler_spec.rb
new file mode 100644
index 000000000..8f686a699
--- /dev/null
+++ b/spec/workers/scheduler/instance_refresh_scheduler_spec.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe Scheduler::InstanceRefreshScheduler do
+  let(:worker) { described_class.new }
+
+  describe 'perform' do
+    it 'runs without error' do
+      expect { worker.perform }.to_not raise_error
+    end
+  end
+end
diff --git a/spec/workers/scheduler/ip_cleanup_scheduler_spec.rb b/spec/workers/scheduler/ip_cleanup_scheduler_spec.rb
new file mode 100644
index 000000000..50af03011
--- /dev/null
+++ b/spec/workers/scheduler/ip_cleanup_scheduler_spec.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe Scheduler::IpCleanupScheduler do
+  let(:worker) { described_class.new }
+
+  describe 'perform' do
+    it 'runs without error' do
+      expect { worker.perform }.to_not raise_error
+    end
+  end
+end
diff --git a/spec/workers/scheduler/pghero_scheduler_spec.rb b/spec/workers/scheduler/pghero_scheduler_spec.rb
new file mode 100644
index 000000000..e404e5fe4
--- /dev/null
+++ b/spec/workers/scheduler/pghero_scheduler_spec.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe Scheduler::PgheroScheduler do
+  let(:worker) { described_class.new }
+
+  describe 'perform' do
+    it 'runs without error' do
+      expect { worker.perform }.to_not raise_error
+    end
+  end
+end
diff --git a/spec/workers/scheduler/scheduled_statuses_scheduler_spec.rb b/spec/workers/scheduler/scheduled_statuses_scheduler_spec.rb
new file mode 100644
index 000000000..13c853c62
--- /dev/null
+++ b/spec/workers/scheduler/scheduled_statuses_scheduler_spec.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe Scheduler::ScheduledStatusesScheduler do
+  let(:worker) { described_class.new }
+
+  describe 'perform' do
+    it 'runs without error' do
+      expect { worker.perform }.to_not raise_error
+    end
+  end
+end
diff --git a/spec/workers/scheduler/suspended_user_cleanup_scheduler_spec.rb b/spec/workers/scheduler/suspended_user_cleanup_scheduler_spec.rb
new file mode 100644
index 000000000..25f0e1fce
--- /dev/null
+++ b/spec/workers/scheduler/suspended_user_cleanup_scheduler_spec.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe Scheduler::SuspendedUserCleanupScheduler do
+  let(:worker) { described_class.new }
+
+  describe 'perform' do
+    it 'runs without error' do
+      expect { worker.perform }.to_not raise_error
+    end
+  end
+end
diff --git a/spec/workers/scheduler/trends/refresh_scheduler_spec.rb b/spec/workers/scheduler/trends/refresh_scheduler_spec.rb
new file mode 100644
index 000000000..c0c5f032b
--- /dev/null
+++ b/spec/workers/scheduler/trends/refresh_scheduler_spec.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe Scheduler::Trends::RefreshScheduler do
+  let(:worker) { described_class.new }
+
+  describe 'perform' do
+    it 'runs without error' do
+      expect { worker.perform }.to_not raise_error
+    end
+  end
+end
diff --git a/spec/workers/scheduler/trends/review_notifications_scheduler_spec.rb b/spec/workers/scheduler/trends/review_notifications_scheduler_spec.rb
new file mode 100644
index 000000000..cc971c24b
--- /dev/null
+++ b/spec/workers/scheduler/trends/review_notifications_scheduler_spec.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe Scheduler::Trends::ReviewNotificationsScheduler do
+  let(:worker) { described_class.new }
+
+  describe 'perform' do
+    it 'runs without error' do
+      expect { worker.perform }.to_not raise_error
+    end
+  end
+end
diff --git a/spec/workers/scheduler/vacuum_scheduler_spec.rb b/spec/workers/scheduler/vacuum_scheduler_spec.rb
new file mode 100644
index 000000000..36ecc93d8
--- /dev/null
+++ b/spec/workers/scheduler/vacuum_scheduler_spec.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe Scheduler::VacuumScheduler do
+  let(:worker) { described_class.new }
+
+  describe 'perform' do
+    it 'runs without error' do
+      expect { worker.perform }.to_not raise_error
+    end
+  end
+end
diff --git a/spec/workers/unpublish_announcement_worker_spec.rb b/spec/workers/unpublish_announcement_worker_spec.rb
new file mode 100644
index 000000000..c742c30bc
--- /dev/null
+++ b/spec/workers/unpublish_announcement_worker_spec.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe UnpublishAnnouncementWorker do
+  let(:worker) { described_class.new }
+
+  describe 'perform' do
+    it 'runs without error for missing record' do
+      expect { worker.perform(nil) }.to_not raise_error
+    end
+  end
+end
diff --git a/spec/workers/webhooks/delivery_worker_spec.rb b/spec/workers/webhooks/delivery_worker_spec.rb
new file mode 100644
index 000000000..daf8a3e28
--- /dev/null
+++ b/spec/workers/webhooks/delivery_worker_spec.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe Webhooks::DeliveryWorker do
+  let(:worker) { described_class.new }
+
+  describe 'perform' do
+    it 'runs without error' do
+      expect { worker.perform(nil, nil) }.to_not raise_error
+    end
+  end
+end