From fd9dea21d0da9d5c4184efca8f66cc2ffc5435f6 Mon Sep 17 00:00:00 2001
From: Matt Jankowski <matt@jankowski.online>
Date: Fri, 13 Oct 2023 08:42:09 -0400
Subject: [PATCH] DB speedup in `API::` controller/request specs (#25516)

---
 .../controllers/api/oembed_controller_spec.rb |  5 +-
 .../accounts/credentials_controller_spec.rb   |  6 +-
 .../follower_accounts_controller_spec.rb      | 10 +--
 .../following_accounts_controller_spec.rb     | 10 +--
 .../api/v1/accounts/notes_controller_spec.rb  | 18 ++---
 .../api/v1/accounts/pins_controller_spec.rb   | 14 +---
 .../accounts/relationships_controller_spec.rb | 16 ++--
 .../v1/accounts/statuses_controller_spec.rb   | 14 +---
 .../api/v1/accounts_controller_spec.rb        | 80 +++----------------
 .../api/v1/admin/accounts_controller_spec.rb  | 38 ++-------
 .../reactions_controller_spec.rb              | 10 +--
 .../api/v1/announcements_controller_spec.rb   |  5 +-
 .../api/v1/blocks_controller_spec.rb          | 21 ++---
 .../api/v1/conversations_controller_spec.rb   | 11 +--
 .../api/v1/filters_controller_spec.rb         | 26 ++----
 .../translation_languages_controller_spec.rb  |  4 +-
 .../api/v1/lists/accounts_controller_spec.rb  | 20 +----
 .../api/v1/markers_controller_spec.rb         | 17 +---
 .../api/v1/media_controller_spec.rb           | 31 +------
 .../api/v1/polls/votes_controller_spec.rb     |  7 +-
 .../api/v1/reports_controller_spec.rb         | 16 +---
 .../api/v1/statuses/mutes_controller_spec.rb  | 10 +--
 .../reblogged_by_accounts_controller_spec.rb  |  6 +-
 .../v1/statuses/reblogs_controller_spec.rb    | 24 +-----
 .../api/v1/statuses_controller_spec.rb        | 57 ++++---------
 .../api/v2/admin/accounts_controller_spec.rb  | 10 +--
 .../v2/filters/keywords_controller_spec.rb    | 18 +----
 .../v2/filters/statuses_controller_spec.rb    | 14 +---
 .../api/v1/admin/account_actions_spec.rb      | 28 +------
 .../v1/admin/canonical_email_blocks_spec.rb   | 49 ++----------
 .../api/v1/admin/domain_allows_spec.rb        | 26 +-----
 .../api/v1/admin/domain_blocks_spec.rb        | 41 ++--------
 .../api/v1/admin/email_domain_blocks_spec.rb  | 26 +-----
 spec/requests/api/v1/admin/ip_blocks_spec.rb  | 33 +-------
 spec/requests/api/v1/admin/reports_spec.rb    | 55 +++----------
 spec/requests/api/v1/apps/credentials_spec.rb |  6 +-
 spec/requests/api/v1/apps_spec.rb             | 18 +----
 spec/requests/api/v1/domain_blocks_spec.rb    | 21 +----
 spec/requests/api/v1/follow_requests_spec.rb  | 33 +-------
 spec/requests/api/v1/lists_spec.rb            | 40 ++--------
 spec/requests/api/v1/tags_spec.rb             | 33 +-------
 41 files changed, 181 insertions(+), 746 deletions(-)

diff --git a/spec/controllers/api/oembed_controller_spec.rb b/spec/controllers/api/oembed_controller_spec.rb
index 70248c3982..5f0ca560d2 100644
--- a/spec/controllers/api/oembed_controller_spec.rb
+++ b/spec/controllers/api/oembed_controller_spec.rb
@@ -14,11 +14,8 @@ RSpec.describe Api::OEmbedController do
       get :show, params: { url: short_account_status_url(alice, status) }, format: :json
     end
 
-    it 'returns http success' do
+    it 'returns private cache control headers', :aggregate_failures do
       expect(response).to have_http_status(200)
-    end
-
-    it 'returns private cache control headers' do
       expect(response.headers['Cache-Control']).to include('private, no-store')
     end
   end
diff --git a/spec/controllers/api/v1/accounts/credentials_controller_spec.rb b/spec/controllers/api/v1/accounts/credentials_controller_spec.rb
index b5d5c37a9c..a62fa54e60 100644
--- a/spec/controllers/api/v1/accounts/credentials_controller_spec.rb
+++ b/spec/controllers/api/v1/accounts/credentials_controller_spec.rb
@@ -41,11 +41,9 @@ describe Api::V1::Accounts::CredentialsController do
           }
         end
 
-        it 'returns http success' do
+        it 'updates account info', :aggregate_failures do
           expect(response).to have_http_status(200)
-        end
 
-        it 'updates account info' do
           user.reload
           user.account.reload
 
@@ -55,9 +53,7 @@ describe Api::V1::Accounts::CredentialsController do
           expect(user.account.header).to exist
           expect(user.setting_default_privacy).to eq('unlisted')
           expect(user.setting_default_sensitive).to be(true)
-        end
 
-        it 'queues up an account update distribution' do
           expect(ActivityPub::UpdateDistributionWorker).to have_received(:perform_async).with(user.account_id)
         end
       end
diff --git a/spec/controllers/api/v1/accounts/follower_accounts_controller_spec.rb b/spec/controllers/api/v1/accounts/follower_accounts_controller_spec.rb
index 7a387f326f..510a47566b 100644
--- a/spec/controllers/api/v1/accounts/follower_accounts_controller_spec.rb
+++ b/spec/controllers/api/v1/accounts/follower_accounts_controller_spec.rb
@@ -18,23 +18,19 @@ describe Api::V1::Accounts::FollowerAccountsController do
   end
 
   describe 'GET #index' do
-    it 'returns http success' do
+    it 'returns accounts following the given account', :aggregate_failures do
       get :index, params: { account_id: account.id, limit: 2 }
 
       expect(response).to have_http_status(200)
-    end
-
-    it 'returns accounts following the given account' do
-      get :index, params: { account_id: account.id, limit: 2 }
-
       expect(body_as_json.size).to eq 2
       expect([body_as_json[0][:id], body_as_json[1][:id]]).to contain_exactly(alice.id.to_s, bob.id.to_s)
     end
 
-    it 'does not return blocked users' do
+    it 'does not return blocked users', :aggregate_failures do
       user.account.block!(bob)
       get :index, params: { account_id: account.id, limit: 2 }
 
+      expect(response).to have_http_status(200)
       expect(body_as_json.size).to eq 1
       expect(body_as_json[0][:id]).to eq alice.id.to_s
     end
diff --git a/spec/controllers/api/v1/accounts/following_accounts_controller_spec.rb b/spec/controllers/api/v1/accounts/following_accounts_controller_spec.rb
index b69b0bd395..a7d07a6bec 100644
--- a/spec/controllers/api/v1/accounts/following_accounts_controller_spec.rb
+++ b/spec/controllers/api/v1/accounts/following_accounts_controller_spec.rb
@@ -18,23 +18,19 @@ describe Api::V1::Accounts::FollowingAccountsController do
   end
 
   describe 'GET #index' do
-    it 'returns http success' do
+    it 'returns accounts followed by the given account', :aggregate_failures do
       get :index, params: { account_id: account.id, limit: 2 }
 
       expect(response).to have_http_status(200)
-    end
-
-    it 'returns accounts followed by the given account' do
-      get :index, params: { account_id: account.id, limit: 2 }
-
       expect(body_as_json.size).to eq 2
       expect([body_as_json[0][:id], body_as_json[1][:id]]).to contain_exactly(alice.id.to_s, bob.id.to_s)
     end
 
-    it 'does not return blocked users' do
+    it 'does not return blocked users', :aggregate_failures do
       user.account.block!(bob)
       get :index, params: { account_id: account.id, limit: 2 }
 
+      expect(response).to have_http_status(200)
       expect(body_as_json.size).to eq 1
       expect(body_as_json[0][:id]).to eq alice.id.to_s
     end
diff --git a/spec/controllers/api/v1/accounts/notes_controller_spec.rb b/spec/controllers/api/v1/accounts/notes_controller_spec.rb
index 4107105afd..75599b32b2 100644
--- a/spec/controllers/api/v1/accounts/notes_controller_spec.rb
+++ b/spec/controllers/api/v1/accounts/notes_controller_spec.rb
@@ -19,30 +19,24 @@ describe Api::V1::Accounts::NotesController do
       post :create, params: { account_id: account.id, comment: comment }
     end
 
-    context 'when account note has reasonable length' do
+    context 'when account note has reasonable length', :aggregate_failures do
       let(:comment) { 'foo' }
 
-      it 'returns http success' do
-        subject
-        expect(response).to have_http_status(200)
-      end
-
       it 'updates account note' do
         subject
+
+        expect(response).to have_http_status(200)
         expect(AccountNote.find_by(account_id: user.account.id, target_account_id: account.id).comment).to eq comment
       end
     end
 
-    context 'when account note exceeds allowed length' do
+    context 'when account note exceeds allowed length', :aggregate_failures do
       let(:comment) { 'a' * 2_001 }
 
-      it 'returns 422' do
-        subject
-        expect(response).to have_http_status(422)
-      end
-
       it 'does not create account note' do
         subject
+
+        expect(response).to have_http_status(422)
         expect(AccountNote.where(account_id: user.account.id, target_account_id: account.id)).to_not exist
       end
     end
diff --git a/spec/controllers/api/v1/accounts/pins_controller_spec.rb b/spec/controllers/api/v1/accounts/pins_controller_spec.rb
index b4aa9b7116..36f525e756 100644
--- a/spec/controllers/api/v1/accounts/pins_controller_spec.rb
+++ b/spec/controllers/api/v1/accounts/pins_controller_spec.rb
@@ -15,14 +15,11 @@ RSpec.describe Api::V1::Accounts::PinsController do
   describe 'POST #create' do
     subject { post :create, params: { account_id: kevin.account.id } }
 
-    it 'returns 200' do
-      expect(response).to have_http_status(200)
-    end
-
-    it 'creates account_pin' do
+    it 'creates account_pin', :aggregate_failures do
       expect do
         subject
       end.to change { AccountPin.where(account: john.account, target_account: kevin.account).count }.by(1)
+      expect(response).to have_http_status(200)
     end
   end
 
@@ -33,14 +30,11 @@ RSpec.describe Api::V1::Accounts::PinsController do
       Fabricate(:account_pin, account: john.account, target_account: kevin.account)
     end
 
-    it 'returns 200' do
-      expect(response).to have_http_status(200)
-    end
-
-    it 'destroys account_pin' do
+    it 'destroys account_pin', :aggregate_failures do
       expect do
         subject
       end.to change { AccountPin.where(account: john.account, target_account: kevin.account).count }.by(-1)
+      expect(response).to have_http_status(200)
     end
   end
 end
diff --git a/spec/controllers/api/v1/accounts/relationships_controller_spec.rb b/spec/controllers/api/v1/accounts/relationships_controller_spec.rb
index 993ead636a..5ba6f2a1f8 100644
--- a/spec/controllers/api/v1/accounts/relationships_controller_spec.rb
+++ b/spec/controllers/api/v1/accounts/relationships_controller_spec.rb
@@ -26,13 +26,10 @@ describe Api::V1::Accounts::RelationshipsController do
         get :index, params: { id: simon.id }
       end
 
-      it 'returns http success' do
-        expect(response).to have_http_status(200)
-      end
-
-      it 'returns JSON with correct data' do
+      it 'returns JSON with correct data', :aggregate_failures do
         json = body_as_json
 
+        expect(response).to have_http_status(200)
         expect(json).to be_a Enumerable
         expect(json.first[:following]).to be true
         expect(json.first[:followed_by]).to be false
@@ -51,11 +48,14 @@ describe Api::V1::Accounts::RelationshipsController do
       context 'when there is returned JSON data' do
         let(:json) { body_as_json }
 
-        it 'returns an enumerable json' do
+        it 'returns an enumerable json with correct elements', :aggregate_failures do
           expect(json).to be_a Enumerable
+
+          expect_simon_item_one
+          expect_lewis_item_two
         end
 
-        it 'returns a correct first element' do
+        def expect_simon_item_one
           expect(json.first[:id]).to eq simon.id.to_s
           expect(json.first[:following]).to be true
           expect(json.first[:showing_reblogs]).to be true
@@ -65,7 +65,7 @@ describe Api::V1::Accounts::RelationshipsController do
           expect(json.first[:domain_blocking]).to be false
         end
 
-        it 'returns a correct second element' do
+        def expect_lewis_item_two
           expect(json.second[:id]).to eq lewis.id.to_s
           expect(json.second[:following]).to be false
           expect(json.second[:showing_reblogs]).to be false
diff --git a/spec/controllers/api/v1/accounts/statuses_controller_spec.rb b/spec/controllers/api/v1/accounts/statuses_controller_spec.rb
index cb62afcf93..0e4fa93017 100644
--- a/spec/controllers/api/v1/accounts/statuses_controller_spec.rb
+++ b/spec/controllers/api/v1/accounts/statuses_controller_spec.rb
@@ -14,15 +14,10 @@ describe Api::V1::Accounts::StatusesController do
   end
 
   describe 'GET #index' do
-    it 'returns http success' do
+    it 'returns expected headers', :aggregate_failures do
       get :index, params: { account_id: user.account.id, limit: 1 }
 
       expect(response).to have_http_status(200)
-    end
-
-    it 'returns expected headers' do
-      get :index, params: { account_id: user.account.id, limit: 1 }
-
       expect(response.headers['Link'].links.size).to eq(2)
     end
 
@@ -44,14 +39,11 @@ describe Api::V1::Accounts::StatusesController do
         get :index, params: { account_id: user.account.id, exclude_replies: true }
       end
 
-      it 'returns http success' do
-        expect(response).to have_http_status(200)
-      end
-
-      it 'returns posts along with self replies' do
+      it 'returns posts along with self replies', :aggregate_failures do
         json = body_as_json
         post_ids = json.map { |item| item[:id].to_i }.sort
 
+        expect(response).to have_http_status(200)
         expect(post_ids).to eq [status.id, status_self_reply.id]
       end
     end
diff --git a/spec/controllers/api/v1/accounts_controller_spec.rb b/spec/controllers/api/v1/accounts_controller_spec.rb
index 0daec691a5..9d0bb73c7c 100644
--- a/spec/controllers/api/v1/accounts_controller_spec.rb
+++ b/spec/controllers/api/v1/accounts_controller_spec.rb
@@ -25,15 +25,10 @@ RSpec.describe Api::V1::AccountsController do
     context 'when given truthy agreement' do
       let(:agreement) { 'true' }
 
-      it 'returns http success' do
+      it 'creates a user', :aggregate_failures do
         expect(response).to have_http_status(200)
-      end
-
-      it 'returns a new access token as JSON' do
         expect(body_as_json[:access_token]).to_not be_blank
-      end
 
-      it 'creates a user' do
         user = User.find_by(email: 'hello@world.tld')
         expect(user).to_not be_nil
         expect(user.created_by_application_id).to eq app.id
@@ -59,18 +54,14 @@ RSpec.describe Api::V1::AccountsController do
       context 'with unlocked account' do
         let(:locked) { false }
 
-        it 'returns http success' do
+        it 'creates a following relation between user and target user', :aggregate_failures do
           expect(response).to have_http_status(200)
-        end
 
-        it 'returns JSON with following=true and requested=false' do
           json = body_as_json
 
           expect(json[:following]).to be true
           expect(json[:requested]).to be false
-        end
 
-        it 'creates a following relation between user and target user' do
           expect(user.account.following?(other_account)).to be true
         end
 
@@ -80,18 +71,14 @@ RSpec.describe Api::V1::AccountsController do
       context 'with locked account' do
         let(:locked) { true }
 
-        it 'returns http success' do
+        it 'creates a follow request relation between user and target user', :aggregate_failures do
           expect(response).to have_http_status(200)
-        end
 
-        it 'returns JSON with following=false and requested=true' do
           json = body_as_json
 
           expect(json[:following]).to be false
           expect(json[:requested]).to be true
-        end
 
-        it 'creates a follow request relation between user and target user' do
           expect(user.account.requested?(other_account)).to be true
         end
 
@@ -148,11 +135,8 @@ RSpec.describe Api::V1::AccountsController do
       post :unfollow, params: { id: other_account.id }
     end
 
-    it 'returns http success' do
+    it 'removes the following relation between user and target user', :aggregate_failures do
       expect(response).to have_http_status(200)
-    end
-
-    it 'removes the following relation between user and target user' do
       expect(user.account.following?(other_account)).to be false
     end
 
@@ -168,11 +152,8 @@ RSpec.describe Api::V1::AccountsController do
       post :remove_from_followers, params: { id: other_account.id }
     end
 
-    it 'returns http success' do
+    it 'removes the followed relation between user and target user', :aggregate_failures do
       expect(response).to have_http_status(200)
-    end
-
-    it 'removes the followed relation between user and target user' do
       expect(user.account.followed_by?(other_account)).to be false
     end
 
@@ -188,15 +169,9 @@ RSpec.describe Api::V1::AccountsController do
       post :block, params: { id: other_account.id }
     end
 
-    it 'returns http success' do
+    it 'creates a blocking relation', :aggregate_failures do
       expect(response).to have_http_status(200)
-    end
-
-    it 'removes the following relation between user and target user' do
       expect(user.account.following?(other_account)).to be false
-    end
-
-    it 'creates a blocking relation' do
       expect(user.account.blocking?(other_account)).to be true
     end
 
@@ -212,11 +187,8 @@ RSpec.describe Api::V1::AccountsController do
       post :unblock, params: { id: other_account.id }
     end
 
-    it 'returns http success' do
+    it 'removes the blocking relation between user and target user', :aggregate_failures do
       expect(response).to have_http_status(200)
-    end
-
-    it 'removes the blocking relation between user and target user' do
       expect(user.account.blocking?(other_account)).to be false
     end
 
@@ -232,19 +204,10 @@ RSpec.describe Api::V1::AccountsController do
       post :mute, params: { id: other_account.id }
     end
 
-    it 'returns http success' do
+    it 'mutes notifications', :aggregate_failures do
       expect(response).to have_http_status(200)
-    end
-
-    it 'does not remove the following relation between user and target user' do
       expect(user.account.following?(other_account)).to be true
-    end
-
-    it 'creates a muting relation' do
       expect(user.account.muting?(other_account)).to be true
-    end
-
-    it 'mutes notifications' do
       expect(user.account.muting_notifications?(other_account)).to be true
     end
 
@@ -260,19 +223,10 @@ RSpec.describe Api::V1::AccountsController do
       post :mute, params: { id: other_account.id, notifications: false }
     end
 
-    it 'returns http success' do
+    it 'does not mute notifications', :aggregate_failures do
       expect(response).to have_http_status(200)
-    end
-
-    it 'does not remove the following relation between user and target user' do
       expect(user.account.following?(other_account)).to be true
-    end
-
-    it 'creates a muting relation' do
       expect(user.account.muting?(other_account)).to be true
-    end
-
-    it 'does not mute notifications' do
       expect(user.account.muting_notifications?(other_account)).to be false
     end
 
@@ -288,19 +242,10 @@ RSpec.describe Api::V1::AccountsController do
       post :mute, params: { id: other_account.id, duration: 300 }
     end
 
-    it 'returns http success' do
+    it 'mutes notifications', :aggregate_failures do
       expect(response).to have_http_status(200)
-    end
-
-    it 'does not remove the following relation between user and target user' do
       expect(user.account.following?(other_account)).to be true
-    end
-
-    it 'creates a muting relation' do
       expect(user.account.muting?(other_account)).to be true
-    end
-
-    it 'mutes notifications' do
       expect(user.account.muting_notifications?(other_account)).to be true
     end
 
@@ -316,11 +261,8 @@ RSpec.describe Api::V1::AccountsController do
       post :unmute, params: { id: other_account.id }
     end
 
-    it 'returns http success' do
+    it 'removes the muting relation between user and target user', :aggregate_failures do
       expect(response).to have_http_status(200)
-    end
-
-    it 'removes the muting relation between user and target user' do
       expect(user.account.muting?(other_account)).to be false
     end
 
diff --git a/spec/controllers/api/v1/admin/accounts_controller_spec.rb b/spec/controllers/api/v1/admin/accounts_controller_spec.rb
index 36f6e398cb..4b56b25479 100644
--- a/spec/controllers/api/v1/admin/accounts_controller_spec.rb
+++ b/spec/controllers/api/v1/admin/accounts_controller_spec.rb
@@ -44,11 +44,9 @@ RSpec.describe Api::V1::Admin::AccountsController do
       context "when called with #{params.inspect}" do
         let(:params) { params }
 
-        it 'returns http success' do
+        it "returns the correct accounts (#{expected_results.inspect})", :aggregate_failures do
           expect(response).to have_http_status(200)
-        end
 
-        it "returns the correct accounts (#{expected_results.inspect})" do
           json = body_as_json
 
           expect(json.map { |a| a[:id].to_i }).to eq(expected_results.map { |symbol| send(symbol).id })
@@ -79,15 +77,10 @@ RSpec.describe Api::V1::Admin::AccountsController do
     it_behaves_like 'forbidden for wrong scope', 'write:statuses'
     it_behaves_like 'forbidden for wrong role', ''
 
-    it 'returns http success' do
+    it 'approves user', :aggregate_failures do
       expect(response).to have_http_status(200)
-    end
-
-    it 'approves user' do
       expect(account.reload.user_approved?).to be true
-    end
 
-    it 'logs action' do
       log_item = Admin::ActionLog.last
 
       expect(log_item).to_not be_nil
@@ -106,15 +99,10 @@ RSpec.describe Api::V1::Admin::AccountsController do
     it_behaves_like 'forbidden for wrong scope', 'write:statuses'
     it_behaves_like 'forbidden for wrong role', ''
 
-    it 'returns http success' do
+    it 'removes user', :aggregate_failures do
       expect(response).to have_http_status(200)
-    end
-
-    it 'removes user' do
       expect(User.where(id: account.user.id).count).to eq 0
-    end
 
-    it 'logs action' do
       log_item = Admin::ActionLog.last
 
       expect(log_item).to_not be_nil
@@ -133,11 +121,8 @@ RSpec.describe Api::V1::Admin::AccountsController do
     it_behaves_like 'forbidden for wrong scope', 'write:statuses'
     it_behaves_like 'forbidden for wrong role', ''
 
-    it 'returns http success' do
+    it 'enables user', :aggregate_failures do
       expect(response).to have_http_status(200)
-    end
-
-    it 'enables user' do
       expect(account.reload.user_disabled?).to be false
     end
   end
@@ -151,11 +136,8 @@ RSpec.describe Api::V1::Admin::AccountsController do
     it_behaves_like 'forbidden for wrong scope', 'write:statuses'
     it_behaves_like 'forbidden for wrong role', ''
 
-    it 'returns http success' do
+    it 'unsuspends account', :aggregate_failures do
       expect(response).to have_http_status(200)
-    end
-
-    it 'unsuspends account' do
       expect(account.reload.suspended?).to be false
     end
   end
@@ -169,11 +151,8 @@ RSpec.describe Api::V1::Admin::AccountsController do
     it_behaves_like 'forbidden for wrong scope', 'write:statuses'
     it_behaves_like 'forbidden for wrong role', ''
 
-    it 'returns http success' do
+    it 'unsensitizes account', :aggregate_failures do
       expect(response).to have_http_status(200)
-    end
-
-    it 'unsensitizes account' do
       expect(account.reload.sensitized?).to be false
     end
   end
@@ -187,11 +166,8 @@ RSpec.describe Api::V1::Admin::AccountsController do
     it_behaves_like 'forbidden for wrong scope', 'write:statuses'
     it_behaves_like 'forbidden for wrong role', ''
 
-    it 'returns http success' do
+    it 'unsilences account', :aggregate_failures do
       expect(response).to have_http_status(200)
-    end
-
-    it 'unsilences account' do
       expect(account.reload.silenced?).to be false
     end
   end
diff --git a/spec/controllers/api/v1/announcements/reactions_controller_spec.rb b/spec/controllers/api/v1/announcements/reactions_controller_spec.rb
index 10aaa553f5..c1debc33fe 100644
--- a/spec/controllers/api/v1/announcements/reactions_controller_spec.rb
+++ b/spec/controllers/api/v1/announcements/reactions_controller_spec.rb
@@ -25,11 +25,8 @@ RSpec.describe Api::V1::Announcements::ReactionsController do
         put :update, params: { announcement_id: announcement.id, id: '😂' }
       end
 
-      it 'returns http success' do
+      it 'creates reaction', :aggregate_failures do
         expect(response).to have_http_status(200)
-      end
-
-      it 'creates reaction' do
         expect(announcement.announcement_reactions.find_by(name: '😂', account: user.account)).to_not be_nil
       end
     end
@@ -53,11 +50,8 @@ RSpec.describe Api::V1::Announcements::ReactionsController do
         delete :destroy, params: { announcement_id: announcement.id, id: '😂' }
       end
 
-      it 'returns http success' do
+      it 'creates reaction', :aggregate_failures do
         expect(response).to have_http_status(200)
-      end
-
-      it 'creates reaction' do
         expect(announcement.announcement_reactions.find_by(name: '😂', account: user.account)).to be_nil
       end
     end
diff --git a/spec/controllers/api/v1/announcements_controller_spec.rb b/spec/controllers/api/v1/announcements_controller_spec.rb
index 15d94b4512..95ce8fd9fc 100644
--- a/spec/controllers/api/v1/announcements_controller_spec.rb
+++ b/spec/controllers/api/v1/announcements_controller_spec.rb
@@ -47,11 +47,8 @@ RSpec.describe Api::V1::AnnouncementsController do
         post :dismiss, params: { id: announcement.id }
       end
 
-      it 'returns http success' do
+      it 'dismisses announcement', :aggregate_failures do
         expect(response).to have_http_status(200)
-      end
-
-      it 'dismisses announcement' do
         expect(announcement.announcement_mutes.find_by(account: user.account)).to_not be_nil
       end
     end
diff --git a/spec/controllers/api/v1/blocks_controller_spec.rb b/spec/controllers/api/v1/blocks_controller_spec.rb
index eaafc1b4fa..ba63560a96 100644
--- a/spec/controllers/api/v1/blocks_controller_spec.rb
+++ b/spec/controllers/api/v1/blocks_controller_spec.rb
@@ -12,45 +12,48 @@ RSpec.describe Api::V1::BlocksController do
   before { allow(controller).to receive(:doorkeeper_token) { token } }
 
   describe 'GET #index' do
-    it 'limits according to limit parameter' do
+    it 'limits according to limit parameter', :aggregate_failures do
       Array.new(2) { Fabricate(:block, account: user.account) }
       get :index, params: { limit: 1 }
+
+      expect(response).to have_http_status(200)
       expect(body_as_json.size).to eq 1
     end
 
-    it 'queries blocks in range according to max_id' do
+    it 'queries blocks in range according to max_id', :aggregate_failures do
       blocks = Array.new(2) { Fabricate(:block, account: user.account) }
 
       get :index, params: { max_id: blocks[1] }
 
+      expect(response).to have_http_status(200)
       expect(body_as_json.size).to eq 1
       expect(body_as_json[0][:id]).to eq blocks[0].target_account_id.to_s
     end
 
-    it 'queries blocks in range according to since_id' do
+    it 'queries blocks in range according to since_id', :aggregate_failures do
       blocks = Array.new(2) { Fabricate(:block, account: user.account) }
 
       get :index, params: { since_id: blocks[0] }
 
+      expect(response).to have_http_status(200)
       expect(body_as_json.size).to eq 1
       expect(body_as_json[0][:id]).to eq blocks[1].target_account_id.to_s
     end
 
-    it 'sets pagination header for next path' do
+    it 'sets pagination header for next path', :aggregate_failures do
       blocks = Array.new(2) { Fabricate(:block, account: user.account) }
       get :index, params: { limit: 1, since_id: blocks[0] }
+
+      expect(response).to have_http_status(200)
       expect(response.headers['Link'].find_link(%w(rel next)).href).to eq api_v1_blocks_url(limit: 1, max_id: blocks[1])
     end
 
-    it 'sets pagination header for previous path' do
+    it 'sets pagination header for previous path', :aggregate_failures do
       block = Fabricate(:block, account: user.account)
       get :index
-      expect(response.headers['Link'].find_link(%w(rel prev)).href).to eq api_v1_blocks_url(since_id: block)
-    end
 
-    it 'returns http success' do
-      get :index
       expect(response).to have_http_status(200)
+      expect(response.headers['Link'].find_link(%w(rel prev)).href).to eq api_v1_blocks_url(since_id: block)
     end
 
     context 'with wrong scopes' do
diff --git a/spec/controllers/api/v1/conversations_controller_spec.rb b/spec/controllers/api/v1/conversations_controller_spec.rb
index 28d7c7f3ae..50e2a62efd 100644
--- a/spec/controllers/api/v1/conversations_controller_spec.rb
+++ b/spec/controllers/api/v1/conversations_controller_spec.rb
@@ -21,17 +21,14 @@ RSpec.describe Api::V1::ConversationsController do
       PostStatusService.new.call(user.account, text: 'Hey, nobody here', visibility: 'direct')
     end
 
-    it 'returns http success' do
-      get :index
-      expect(response).to have_http_status(200)
-    end
-
-    it 'returns pagination headers' do
+    it 'returns pagination headers', :aggregate_failures do
       get :index, params: { limit: 1 }
+
+      expect(response).to have_http_status(200)
       expect(response.headers['Link'].links.size).to eq(2)
     end
 
-    it 'returns conversations' do
+    it 'returns conversations', :aggregate_failures do
       get :index
       json = body_as_json
       expect(json.size).to eq 2
diff --git a/spec/controllers/api/v1/filters_controller_spec.rb b/spec/controllers/api/v1/filters_controller_spec.rb
index 8ccd2f4d66..8d5408cf54 100644
--- a/spec/controllers/api/v1/filters_controller_spec.rb
+++ b/spec/controllers/api/v1/filters_controller_spec.rb
@@ -31,12 +31,10 @@ RSpec.describe Api::V1::FiltersController do
       post :create, params: { phrase: 'magic', context: %w(home), irreversible: irreversible, whole_word: whole_word }
     end
 
-    it 'returns http success' do
-      expect(response).to have_http_status(200)
-    end
-
-    it 'creates a filter' do
+    it 'creates a filter', :aggregate_failures do
       filter = user.account.custom_filters.first
+
+      expect(response).to have_http_status(200)
       expect(filter).to_not be_nil
       expect(filter.keywords.pluck(:keyword, :whole_word)).to eq [['magic', whole_word]]
       expect(filter.context).to eq %w(home)
@@ -48,12 +46,10 @@ RSpec.describe Api::V1::FiltersController do
       let(:irreversible) { false }
       let(:whole_word)   { true }
 
-      it 'returns http success' do
-        expect(response).to have_http_status(200)
-      end
-
-      it 'creates a filter' do
+      it 'creates a filter', :aggregate_failures do
         filter = user.account.custom_filters.first
+
+        expect(response).to have_http_status(200)
         expect(filter).to_not be_nil
         expect(filter.keywords.pluck(:keyword, :whole_word)).to eq [['magic', whole_word]]
         expect(filter.context).to eq %w(home)
@@ -83,11 +79,8 @@ RSpec.describe Api::V1::FiltersController do
       put :update, params: { id: keyword.id, phrase: 'updated' }
     end
 
-    it 'returns http success' do
+    it 'updates the filter', :aggregate_failures do
       expect(response).to have_http_status(200)
-    end
-
-    it 'updates the filter' do
       expect(keyword.reload.phrase).to eq 'updated'
     end
   end
@@ -101,11 +94,8 @@ RSpec.describe Api::V1::FiltersController do
       delete :destroy, params: { id: keyword.id }
     end
 
-    it 'returns http success' do
+    it 'removes the filter', :aggregate_failures do
       expect(response).to have_http_status(200)
-    end
-
-    it 'removes the filter' do
       expect { keyword.reload }.to raise_error ActiveRecord::RecordNotFound
     end
   end
diff --git a/spec/controllers/api/v1/instances/translation_languages_controller_spec.rb b/spec/controllers/api/v1/instances/translation_languages_controller_spec.rb
index 88bcc40341..f79687df66 100644
--- a/spec/controllers/api/v1/instances/translation_languages_controller_spec.rb
+++ b/spec/controllers/api/v1/instances/translation_languages_controller_spec.rb
@@ -5,7 +5,7 @@ require 'rails_helper'
 describe Api::V1::Instances::TranslationLanguagesController do
   describe 'GET #show' do
     context 'when no translation service is configured' do
-      it 'returns empty language matrix' do
+      it 'returns empty language matrix', :aggregate_failures do
         get :show
 
         expect(response).to have_http_status(200)
@@ -19,7 +19,7 @@ describe Api::V1::Instances::TranslationLanguagesController do
         allow(TranslationService).to receive_messages(configured?: true, configured: service)
       end
 
-      it 'returns language matrix' do
+      it 'returns language matrix', :aggregate_failures do
         get :show
 
         expect(response).to have_http_status(200)
diff --git a/spec/controllers/api/v1/lists/accounts_controller_spec.rb b/spec/controllers/api/v1/lists/accounts_controller_spec.rb
index d4550dd769..21e155a508 100644
--- a/spec/controllers/api/v1/lists/accounts_controller_spec.rb
+++ b/spec/controllers/api/v1/lists/accounts_controller_spec.rb
@@ -35,11 +35,8 @@ describe Api::V1::Lists::AccountsController do
         post :create, params: { list_id: list.id, account_ids: [bob.id] }
       end
 
-      it 'returns http success' do
+      it 'adds account to the list', :aggregate_failures do
         expect(response).to have_http_status(200)
-      end
-
-      it 'adds account to the list' do
         expect(list.accounts.include?(bob)).to be true
       end
     end
@@ -50,11 +47,8 @@ describe Api::V1::Lists::AccountsController do
         post :create, params: { list_id: list.id, account_ids: [bob.id] }
       end
 
-      it 'returns http success' do
+      it 'adds account to the list', :aggregate_failures do
         expect(response).to have_http_status(200)
-      end
-
-      it 'adds account to the list' do
         expect(list.accounts.include?(bob)).to be true
       end
     end
@@ -64,11 +58,8 @@ describe Api::V1::Lists::AccountsController do
         post :create, params: { list_id: list.id, account_ids: [bob.id] }
       end
 
-      it 'returns http not found' do
+      it 'does not add the account to the list', :aggregate_failures do
         expect(response).to have_http_status(404)
-      end
-
-      it 'does not add the account to the list' do
         expect(list.accounts.include?(bob)).to be false
       end
     end
@@ -81,11 +72,8 @@ describe Api::V1::Lists::AccountsController do
       delete :destroy, params: { list_id: list.id, account_ids: [list.accounts.first.id] }
     end
 
-    it 'returns http success' do
+    it 'removes account from the list', :aggregate_failures do
       expect(response).to have_http_status(200)
-    end
-
-    it 'removes account from the list' do
       expect(list.accounts.count).to eq 0
     end
   end
diff --git a/spec/controllers/api/v1/markers_controller_spec.rb b/spec/controllers/api/v1/markers_controller_spec.rb
index 64e9dcafb6..e954bbd1b6 100644
--- a/spec/controllers/api/v1/markers_controller_spec.rb
+++ b/spec/controllers/api/v1/markers_controller_spec.rb
@@ -18,13 +18,10 @@ RSpec.describe Api::V1::MarkersController do
       get :index, params: { timeline: %w(home notifications) }
     end
 
-    it 'returns http success' do
-      expect(response).to have_http_status(200)
-    end
-
-    it 'returns markers' do
+    it 'returns markers', :aggregate_failures do
       json = body_as_json
 
+      expect(response).to have_http_status(200)
       expect(json.key?(:home)).to be true
       expect(json[:home][:last_read_id]).to eq '123'
       expect(json.key?(:notifications)).to be true
@@ -38,11 +35,8 @@ RSpec.describe Api::V1::MarkersController do
         post :create, params: { home: { last_read_id: '69420' } }
       end
 
-      it 'returns http success' do
+      it 'creates a marker', :aggregate_failures do
         expect(response).to have_http_status(200)
-      end
-
-      it 'creates a marker' do
         expect(user.markers.first.timeline).to eq 'home'
         expect(user.markers.first.last_read_id).to eq 69_420
       end
@@ -54,11 +48,8 @@ RSpec.describe Api::V1::MarkersController do
         post :create, params: { home: { last_read_id: '70120' } }
       end
 
-      it 'returns http success' do
+      it 'updates a marker', :aggregate_failures do
         expect(response).to have_http_status(200)
-      end
-
-      it 'updates a marker' do
         expect(user.markers.first.timeline).to eq 'home'
         expect(user.markers.first.last_read_id).to eq 70_120
       end
diff --git a/spec/controllers/api/v1/media_controller_spec.rb b/spec/controllers/api/v1/media_controller_spec.rb
index 94b2a0a98f..b574381f90 100644
--- a/spec/controllers/api/v1/media_controller_spec.rb
+++ b/spec/controllers/api/v1/media_controller_spec.rb
@@ -38,19 +38,10 @@ RSpec.describe Api::V1::MediaController do
         post :create, params: { file: fixture_file_upload('attachment.jpg', 'image/jpeg') }
       end
 
-      it 'returns http success' do
+      it 'creates a media attachment', :aggregate_failures do
         expect(response).to have_http_status(200)
-      end
-
-      it 'creates a media attachment' do
         expect(MediaAttachment.first).to_not be_nil
-      end
-
-      it 'uploads a file' do
         expect(MediaAttachment.first).to have_attached_file(:file)
-      end
-
-      it 'returns media ID in JSON' do
         expect(body_as_json[:id]).to eq MediaAttachment.first.id.to_s
       end
     end
@@ -60,19 +51,10 @@ RSpec.describe Api::V1::MediaController do
         post :create, params: { file: fixture_file_upload('attachment.gif', 'image/gif') }
       end
 
-      it 'returns http success' do
+      it 'creates a media attachment', :aggregate_failures do
         expect(response).to have_http_status(200)
-      end
-
-      it 'creates a media attachment' do
         expect(MediaAttachment.first).to_not be_nil
-      end
-
-      it 'uploads a file' do
         expect(MediaAttachment.first).to have_attached_file(:file)
-      end
-
-      it 'returns media ID in JSON' do
         expect(body_as_json[:id]).to eq MediaAttachment.first.id.to_s
       end
     end
@@ -82,17 +64,10 @@ RSpec.describe Api::V1::MediaController do
         post :create, params: { file: fixture_file_upload('attachment.webm', 'video/webm') }
       end
 
-      it do
-        # returns http success
+      it 'creates a media attachment', :aggregate_failures do
         expect(response).to have_http_status(200)
-
-        # creates a media attachment
         expect(MediaAttachment.first).to_not be_nil
-
-        # uploads a file
         expect(MediaAttachment.first).to have_attached_file(:file)
-
-        # returns media ID in JSON
         expect(body_as_json[:id]).to eq MediaAttachment.first.id.to_s
       end
     end
diff --git a/spec/controllers/api/v1/polls/votes_controller_spec.rb b/spec/controllers/api/v1/polls/votes_controller_spec.rb
index 7abd2a1b17..5de225a487 100644
--- a/spec/controllers/api/v1/polls/votes_controller_spec.rb
+++ b/spec/controllers/api/v1/polls/votes_controller_spec.rb
@@ -18,18 +18,13 @@ RSpec.describe Api::V1::Polls::VotesController do
       post :create, params: { poll_id: poll.id, choices: %w(1) }
     end
 
-    it 'returns http success' do
+    it 'creates a vote', :aggregate_failures do
       expect(response).to have_http_status(200)
-    end
-
-    it 'creates a vote' do
       vote = poll.votes.where(account: user.account).first
 
       expect(vote).to_not be_nil
       expect(vote.choice).to eq 1
-    end
 
-    it 'updates poll tallies' do
       expect(poll.reload.cached_tallies).to eq [0, 1]
     end
   end
diff --git a/spec/controllers/api/v1/reports_controller_spec.rb b/spec/controllers/api/v1/reports_controller_spec.rb
index f923ff0794..624ceb213e 100644
--- a/spec/controllers/api/v1/reports_controller_spec.rb
+++ b/spec/controllers/api/v1/reports_controller_spec.rb
@@ -26,19 +26,10 @@ RSpec.describe Api::V1::ReportsController do
       post :create, params: { status_ids: [status.id], account_id: target_account.id, comment: 'reasons', category: category, rule_ids: rule_ids, forward: forward }
     end
 
-    it 'returns http success' do
+    it 'creates a report', :aggregate_failures do
       expect(response).to have_http_status(200)
-    end
-
-    it 'creates a report' do
       expect(target_account.targeted_reports).to_not be_empty
-    end
-
-    it 'saves comment' do
       expect(target_account.targeted_reports.first.comment).to eq 'reasons'
-    end
-
-    it 'sends e-mails to admins' do
       expect(ActionMailer::Base.deliveries.first.to).to eq([admin.email])
     end
 
@@ -63,11 +54,8 @@ RSpec.describe Api::V1::ReportsController do
       let(:category) { 'violation' }
       let(:rule_ids) { [rule.id] }
 
-      it 'saves category' do
+      it 'saves category and rule_ids' do
         expect(target_account.targeted_reports.first.violation?).to be true
-      end
-
-      it 'saves rule_ids' do
         expect(target_account.targeted_reports.first.rule_ids).to contain_exactly(rule.id)
       end
     end
diff --git a/spec/controllers/api/v1/statuses/mutes_controller_spec.rb b/spec/controllers/api/v1/statuses/mutes_controller_spec.rb
index bffa9fe0d9..03274fe1cd 100644
--- a/spec/controllers/api/v1/statuses/mutes_controller_spec.rb
+++ b/spec/controllers/api/v1/statuses/mutes_controller_spec.rb
@@ -21,11 +21,8 @@ describe Api::V1::Statuses::MutesController do
         post :create, params: { status_id: status.id }
       end
 
-      it 'returns http success' do
+      it 'creates a conversation mute', :aggregate_failures do
         expect(response).to have_http_status(200)
-      end
-
-      it 'creates a conversation mute' do
         expect(ConversationMute.find_by(account: user.account, conversation_id: status.conversation_id)).to_not be_nil
       end
     end
@@ -38,11 +35,8 @@ describe Api::V1::Statuses::MutesController do
         post :destroy, params: { status_id: status.id }
       end
 
-      it 'returns http success' do
+      it 'destroys the conversation mute', :aggregate_failures do
         expect(response).to have_http_status(200)
-      end
-
-      it 'destroys the conversation mute' do
         expect(ConversationMute.find_by(account: user.account, conversation_id: status.conversation_id)).to be_nil
       end
     end
diff --git a/spec/controllers/api/v1/statuses/reblogged_by_accounts_controller_spec.rb b/spec/controllers/api/v1/statuses/reblogged_by_accounts_controller_spec.rb
index 756010af87..0d15cca75c 100644
--- a/spec/controllers/api/v1/statuses/reblogged_by_accounts_controller_spec.rb
+++ b/spec/controllers/api/v1/statuses/reblogged_by_accounts_controller_spec.rb
@@ -24,14 +24,12 @@ RSpec.describe Api::V1::Statuses::RebloggedByAccountsController do
         Fabricate(:status, account: bob, reblog_of_id: status.id)
       end
 
-      it 'returns http success' do
+      it 'returns accounts who reblogged the status', :aggregate_failures do
         get :index, params: { status_id: status.id, limit: 2 }
+
         expect(response).to have_http_status(200)
         expect(response.headers['Link'].links.size).to eq(2)
-      end
 
-      it 'returns accounts who reblogged the status' do
-        get :index, params: { status_id: status.id, limit: 2 }
         expect(body_as_json.size).to eq 2
         expect([body_as_json[0][:id], body_as_json[1][:id]]).to contain_exactly(alice.id.to_s, bob.id.to_s)
       end
diff --git a/spec/controllers/api/v1/statuses/reblogs_controller_spec.rb b/spec/controllers/api/v1/statuses/reblogs_controller_spec.rb
index 16ce95dc22..2f2b30b07d 100644
--- a/spec/controllers/api/v1/statuses/reblogs_controller_spec.rb
+++ b/spec/controllers/api/v1/statuses/reblogs_controller_spec.rb
@@ -28,19 +28,13 @@ describe Api::V1::Statuses::ReblogsController do
       end
 
       context 'with public status' do
-        it 'returns http success' do
+        it 'reblogs the status', :aggregate_failures do
           expect(response).to have_http_status(200)
-        end
 
-        it 'updates the reblogs count' do
           expect(status.reblogs.count).to eq 1
-        end
 
-        it 'updates the reblogged attribute' do
           expect(user.account.reblogged?(status)).to be true
-        end
 
-        it 'returns json with updated attributes' do
           hash_body = body_as_json
 
           expect(hash_body[:reblog][:id]).to eq status.id.to_s
@@ -67,19 +61,13 @@ describe Api::V1::Statuses::ReblogsController do
           post :destroy, params: { status_id: status.id }
         end
 
-        it 'returns http success' do
+        it 'destroys the reblog', :aggregate_failures do
           expect(response).to have_http_status(200)
-        end
 
-        it 'updates the reblogs count' do
           expect(status.reblogs.count).to eq 0
-        end
 
-        it 'updates the reblogged attribute' do
           expect(user.account.reblogged?(status)).to be false
-        end
 
-        it 'returns json with updated attributes' do
           hash_body = body_as_json
 
           expect(hash_body[:id]).to eq status.id.to_s
@@ -97,19 +85,13 @@ describe Api::V1::Statuses::ReblogsController do
           post :destroy, params: { status_id: status.id }
         end
 
-        it 'returns http success' do
+        it 'destroys the reblog', :aggregate_failures do
           expect(response).to have_http_status(200)
-        end
 
-        it 'updates the reblogs count' do
           expect(status.reblogs.count).to eq 0
-        end
 
-        it 'updates the reblogged attribute' do
           expect(user.account.reblogged?(status)).to be false
-        end
 
-        it 'returns json with updated attributes' do
           hash_body = body_as_json
 
           expect(hash_body[:id]).to eq status.id.to_s
diff --git a/spec/controllers/api/v1/statuses_controller_spec.rb b/spec/controllers/api/v1/statuses_controller_spec.rb
index c2bdba9ace..30bafe19ac 100644
--- a/spec/controllers/api/v1/statuses_controller_spec.rb
+++ b/spec/controllers/api/v1/statuses_controller_spec.rb
@@ -30,14 +30,11 @@ RSpec.describe Api::V1::StatusesController do
           user.account.custom_filters.create!(phrase: 'filter1', context: %w(home), action: :hide, keywords_attributes: [{ keyword: 'banned' }, { keyword: 'irrelevant' }])
         end
 
-        it 'returns http success' do
-          get :show, params: { id: status.id }
-          expect(response).to have_http_status(200)
-        end
-
-        it 'returns filter information' do
+        it 'returns filter information', :aggregate_failures do
           get :show, params: { id: status.id }
           json = body_as_json
+
+          expect(response).to have_http_status(200)
           expect(json[:filtered][0]).to include({
             filter: a_hash_including({
               id: user.account.custom_filters.first.id.to_s,
@@ -57,14 +54,11 @@ RSpec.describe Api::V1::StatusesController do
           filter.statuses.create!(status_id: status.id)
         end
 
-        it 'returns http success' do
-          get :show, params: { id: status.id }
-          expect(response).to have_http_status(200)
-        end
-
-        it 'returns filter information' do
+        it 'returns filter information', :aggregate_failures do
           get :show, params: { id: status.id }
           json = body_as_json
+
+          expect(response).to have_http_status(200)
           expect(json[:filtered][0]).to include({
             filter: a_hash_including({
               id: user.account.custom_filters.first.id.to_s,
@@ -83,14 +77,11 @@ RSpec.describe Api::V1::StatusesController do
           user.account.custom_filters.create!(phrase: 'filter1', context: %w(home), action: :hide, keywords_attributes: [{ keyword: 'banned' }, { keyword: 'irrelevant' }])
         end
 
-        it 'returns http success' do
-          get :show, params: { id: status.id }
-          expect(response).to have_http_status(200)
-        end
-
-        it 'returns filter information' do
+        it 'returns filter information', :aggregate_failures do
           get :show, params: { id: status.id }
           json = body_as_json
+
+          expect(response).to have_http_status(200)
           expect(json[:reblog][:filtered][0]).to include({
             filter: a_hash_including({
               id: user.account.custom_filters.first.id.to_s,
@@ -125,11 +116,8 @@ RSpec.describe Api::V1::StatusesController do
           post :create, params: { status: 'Hello world' }
         end
 
-        it 'returns http success' do
+        it 'returns rate limit headers', :aggregate_failures do
           expect(response).to have_http_status(200)
-        end
-
-        it 'returns rate limit headers' do
           expect(response.headers['X-RateLimit-Limit']).to eq RateLimiter::FAMILIES[:statuses][:limit].to_s
           expect(response.headers['X-RateLimit-Remaining']).to eq (RateLimiter::FAMILIES[:statuses][:limit] - 1).to_s
         end
@@ -143,11 +131,8 @@ RSpec.describe Api::V1::StatusesController do
           post :create, params: { status: '@alice hm, @bob is really annoying lately', allowed_mentions: [alice.id] }
         end
 
-        it 'returns http unprocessable entity' do
+        it 'returns serialized extra accounts in body', :aggregate_failures do
           expect(response).to have_http_status(422)
-        end
-
-        it 'returns serialized extra accounts in body' do
           expect(body_as_json[:unexpected_accounts].map { |a| a.slice(:id, :acct) }).to eq [{ id: bob.id.to_s, acct: bob.acct }]
         end
       end
@@ -157,11 +142,8 @@ RSpec.describe Api::V1::StatusesController do
           post :create, params: {}
         end
 
-        it 'returns http unprocessable entity' do
+        it 'returns rate limit headers', :aggregate_failures do
           expect(response).to have_http_status(422)
-        end
-
-        it 'returns rate limit headers' do
           expect(response.headers['X-RateLimit-Limit']).to eq RateLimiter::FAMILIES[:statuses][:limit].to_s
         end
       end
@@ -173,11 +155,8 @@ RSpec.describe Api::V1::StatusesController do
           post :create, params: { status: 'Hello world' }
         end
 
-        it 'returns http too many requests' do
+        it 'returns rate limit headers', :aggregate_failures do
           expect(response).to have_http_status(429)
-        end
-
-        it 'returns rate limit headers' do
           expect(response.headers['X-RateLimit-Limit']).to eq RateLimiter::FAMILIES[:statuses][:limit].to_s
           expect(response.headers['X-RateLimit-Remaining']).to eq '0'
         end
@@ -192,11 +171,8 @@ RSpec.describe Api::V1::StatusesController do
         post :destroy, params: { id: status.id }
       end
 
-      it 'returns http success' do
+      it 'removes the status', :aggregate_failures do
         expect(response).to have_http_status(200)
-      end
-
-      it 'removes the status' do
         expect(Status.find_by(id: status.id)).to be_nil
       end
     end
@@ -209,11 +185,8 @@ RSpec.describe Api::V1::StatusesController do
         put :update, params: { id: status.id, status: 'I am updated' }
       end
 
-      it 'returns http success' do
+      it 'updates the status', :aggregate_failures do
         expect(response).to have_http_status(200)
-      end
-
-      it 'updates the status' do
         expect(status.reload.text).to eq 'I am updated'
       end
     end
diff --git a/spec/controllers/api/v2/admin/accounts_controller_spec.rb b/spec/controllers/api/v2/admin/accounts_controller_spec.rb
index 635f645915..18b3950140 100644
--- a/spec/controllers/api/v2/admin/accounts_controller_spec.rb
+++ b/spec/controllers/api/v2/admin/accounts_controller_spec.rb
@@ -44,14 +44,14 @@ RSpec.describe Api::V2::Admin::AccountsController do
       context "when called with #{params.inspect}" do
         let(:params) { params }
 
-        it 'returns http success' do
+        it "returns the correct accounts (#{expected_results.inspect})" do
           expect(response).to have_http_status(200)
+
+          expect(body_json_ids).to eq(expected_results.map { |symbol| send(symbol).id })
         end
 
-        it "returns the correct accounts (#{expected_results.inspect})" do
-          json = body_as_json
-
-          expect(json.map { |a| a[:id].to_i }).to eq(expected_results.map { |symbol| send(symbol).id })
+        def body_json_ids
+          body_as_json.map { |a| a[:id].to_i }
         end
       end
     end
diff --git a/spec/controllers/api/v2/filters/keywords_controller_spec.rb b/spec/controllers/api/v2/filters/keywords_controller_spec.rb
index 057a9c3d00..5321f787a1 100644
--- a/spec/controllers/api/v2/filters/keywords_controller_spec.rb
+++ b/spec/controllers/api/v2/filters/keywords_controller_spec.rb
@@ -40,17 +40,13 @@ RSpec.describe Api::V2::Filters::KeywordsController do
       post :create, params: { filter_id: filter_id, keyword: 'magic', whole_word: false }
     end
 
-    it 'returns http success' do
+    it 'creates a filter', :aggregate_failures do
       expect(response).to have_http_status(200)
-    end
 
-    it 'returns a keyword' do
       json = body_as_json
       expect(json[:keyword]).to eq 'magic'
       expect(json[:whole_word]).to be false
-    end
 
-    it 'creates a keyword' do
       filter = user.account.custom_filters.first
       expect(filter).to_not be_nil
       expect(filter.keywords.pluck(:keyword)).to eq ['magic']
@@ -73,11 +69,9 @@ RSpec.describe Api::V2::Filters::KeywordsController do
       get :show, params: { id: keyword.id }
     end
 
-    it 'returns http success' do
+    it 'responds with the keyword', :aggregate_failures do
       expect(response).to have_http_status(200)
-    end
 
-    it 'returns expected data' do
       json = body_as_json
       expect(json[:keyword]).to eq 'foo'
       expect(json[:whole_word]).to be false
@@ -100,11 +94,9 @@ RSpec.describe Api::V2::Filters::KeywordsController do
       get :update, params: { id: keyword.id, keyword: 'updated' }
     end
 
-    it 'returns http success' do
+    it 'updates the keyword', :aggregate_failures do
       expect(response).to have_http_status(200)
-    end
 
-    it 'updates the keyword' do
       expect(keyword.reload.keyword).to eq 'updated'
     end
 
@@ -125,11 +117,9 @@ RSpec.describe Api::V2::Filters::KeywordsController do
       delete :destroy, params: { id: keyword.id }
     end
 
-    it 'returns http success' do
+    it 'destroys the keyword', :aggregate_failures do
       expect(response).to have_http_status(200)
-    end
 
-    it 'removes the filter' do
       expect { keyword.reload }.to raise_error ActiveRecord::RecordNotFound
     end
 
diff --git a/spec/controllers/api/v2/filters/statuses_controller_spec.rb b/spec/controllers/api/v2/filters/statuses_controller_spec.rb
index 588532ffd2..5c2a623954 100644
--- a/spec/controllers/api/v2/filters/statuses_controller_spec.rb
+++ b/spec/controllers/api/v2/filters/statuses_controller_spec.rb
@@ -41,16 +41,12 @@ RSpec.describe Api::V2::Filters::StatusesController do
       post :create, params: { filter_id: filter_id, status_id: status.id }
     end
 
-    it 'returns http success' do
+    it 'creates a filter', :aggregate_failures do
       expect(response).to have_http_status(200)
-    end
 
-    it 'returns a status filter' do
       json = body_as_json
       expect(json[:status_id]).to eq status.id.to_s
-    end
 
-    it 'creates a status filter' do
       filter = user.account.custom_filters.first
       expect(filter).to_not be_nil
       expect(filter.statuses.pluck(:status_id)).to eq [status.id]
@@ -73,11 +69,9 @@ RSpec.describe Api::V2::Filters::StatusesController do
       get :show, params: { id: status_filter.id }
     end
 
-    it 'returns http success' do
+    it 'responds with the filter', :aggregate_failures do
       expect(response).to have_http_status(200)
-    end
 
-    it 'returns expected data' do
       json = body_as_json
       expect(json[:status_id]).to eq status_filter.status_id.to_s
     end
@@ -99,11 +93,9 @@ RSpec.describe Api::V2::Filters::StatusesController do
       delete :destroy, params: { id: status_filter.id }
     end
 
-    it 'returns http success' do
+    it 'destroys the filter', :aggregate_failures do
       expect(response).to have_http_status(200)
-    end
 
-    it 'removes the filter' do
       expect { status_filter.reload }.to raise_error ActiveRecord::RecordNotFound
     end
 
diff --git a/spec/requests/api/v1/admin/account_actions_spec.rb b/spec/requests/api/v1/admin/account_actions_spec.rb
index 9295d262d6..bdf1f08e43 100644
--- a/spec/requests/api/v1/admin/account_actions_spec.rb
+++ b/spec/requests/api/v1/admin/account_actions_spec.rb
@@ -51,14 +51,9 @@ RSpec.describe 'Account actions' do
       it_behaves_like 'a successful notification delivery'
       it_behaves_like 'a successful logged action', :disable, :user
 
-      it 'returns http success' do
-        subject
-
-        expect(response).to have_http_status(200)
-      end
-
       it 'disables the target account' do
         expect { subject }.to change { target_account.reload.user_disabled? }.from(false).to(true)
+        expect(response).to have_http_status(200)
       end
     end
 
@@ -70,14 +65,9 @@ RSpec.describe 'Account actions' do
       it_behaves_like 'a successful notification delivery'
       it_behaves_like 'a successful logged action', :sensitive, :account
 
-      it 'returns http success' do
-        subject
-
-        expect(response).to have_http_status(200)
-      end
-
       it 'marks the target account as sensitive' do
         expect { subject }.to change { target_account.reload.sensitized? }.from(false).to(true)
+        expect(response).to have_http_status(200)
       end
     end
 
@@ -89,14 +79,9 @@ RSpec.describe 'Account actions' do
       it_behaves_like 'a successful notification delivery'
       it_behaves_like 'a successful logged action', :silence, :account
 
-      it 'returns http success' do
-        subject
-
-        expect(response).to have_http_status(200)
-      end
-
       it 'marks the target account as silenced' do
         expect { subject }.to change { target_account.reload.silenced? }.from(false).to(true)
+        expect(response).to have_http_status(200)
       end
     end
 
@@ -108,14 +93,9 @@ RSpec.describe 'Account actions' do
       it_behaves_like 'a successful notification delivery'
       it_behaves_like 'a successful logged action', :suspend, :account
 
-      it 'returns http success' do
-        subject
-
-        expect(response).to have_http_status(200)
-      end
-
       it 'marks the target account as suspended' do
         expect { subject }.to change { target_account.reload.suspended? }.from(false).to(true)
+        expect(response).to have_http_status(200)
       end
     end
 
diff --git a/spec/requests/api/v1/admin/canonical_email_blocks_spec.rb b/spec/requests/api/v1/admin/canonical_email_blocks_spec.rb
index 4382cb84e5..3f33b50f39 100644
--- a/spec/requests/api/v1/admin/canonical_email_blocks_spec.rb
+++ b/spec/requests/api/v1/admin/canonical_email_blocks_spec.rb
@@ -92,15 +92,10 @@ RSpec.describe 'Canonical Email Blocks' do
     it_behaves_like 'forbidden for wrong role', 'Moderator'
 
     context 'when the requested canonical email block exists' do
-      it 'returns http success' do
+      it 'returns the requested canonical email block data correctly', :aggregate_failures do
         subject
 
         expect(response).to have_http_status(200)
-      end
-
-      it 'returns the requested canonical email block data correctly' do
-        subject
-
         json = body_as_json
 
         expect(json[:id]).to eq(canonical_email_block.id.to_s)
@@ -142,29 +137,19 @@ RSpec.describe 'Canonical Email Blocks' do
       context 'when there is a matching canonical email block' do
         let!(:canonical_email_block) { CanonicalEmailBlock.create(params) }
 
-        it 'returns http success' do
+        it 'returns the expected canonical email hash', :aggregate_failures do
           subject
 
           expect(response).to have_http_status(200)
-        end
-
-        it 'returns the expected canonical email hash' do
-          subject
-
           expect(body_as_json[0][:canonical_email_hash]).to eq(canonical_email_block.canonical_email_hash)
         end
       end
 
       context 'when there is no matching canonical email block' do
-        it 'returns http success' do
+        it 'returns an empty list', :aggregate_failures do
           subject
 
           expect(response).to have_http_status(200)
-        end
-
-        it 'returns an empty list' do
-          subject
-
           expect(body_as_json).to be_empty
         end
       end
@@ -183,15 +168,10 @@ RSpec.describe 'Canonical Email Blocks' do
     it_behaves_like 'forbidden for wrong role', ''
     it_behaves_like 'forbidden for wrong role', 'Moderator'
 
-    it 'returns http success' do
+    it 'returns the canonical_email_hash correctly', :aggregate_failures do
       subject
 
       expect(response).to have_http_status(200)
-    end
-
-    it 'returns the canonical_email_hash correctly' do
-      subject
-
       expect(body_as_json[:canonical_email_hash]).to eq(canonical_email_block.canonical_email_hash)
     end
 
@@ -208,15 +188,10 @@ RSpec.describe 'Canonical Email Blocks' do
     context 'when the canonical_email_hash param is provided instead of email' do
       let(:params) { { canonical_email_hash: 'dd501ce4e6b08698f19df96f2f15737e48a75660b1fa79b6ff58ea25ee4851a4' } }
 
-      it 'returns http success' do
+      it 'returns the correct canonical_email_hash', :aggregate_failures do
         subject
 
         expect(response).to have_http_status(200)
-      end
-
-      it 'returns the correct canonical_email_hash' do
-        subject
-
         expect(body_as_json[:canonical_email_hash]).to eq(params[:canonical_email_hash])
       end
     end
@@ -224,15 +199,10 @@ RSpec.describe 'Canonical Email Blocks' do
     context 'when both email and canonical_email_hash params are provided' do
       let(:params) { { email: 'example@email.com', canonical_email_hash: 'dd501ce4e6b08698f19df96f2f15737e48a75660b1fa79b6ff58ea25ee4851a4' } }
 
-      it 'returns http success' do
+      it 'ignores the canonical_email_hash param', :aggregate_failures do
         subject
 
         expect(response).to have_http_status(200)
-      end
-
-      it 'ignores the canonical_email_hash param' do
-        subject
-
         expect(body_as_json[:canonical_email_hash]).to eq(canonical_email_block.canonical_email_hash)
       end
     end
@@ -262,15 +232,10 @@ RSpec.describe 'Canonical Email Blocks' do
     it_behaves_like 'forbidden for wrong role', ''
     it_behaves_like 'forbidden for wrong role', 'Moderator'
 
-    it 'returns http success' do
+    it 'deletes the canonical email block', :aggregate_failures do
       subject
 
       expect(response).to have_http_status(200)
-    end
-
-    it 'deletes the canonical email block' do
-      subject
-
       expect(CanonicalEmailBlock.find_by(id: canonical_email_block.id)).to be_nil
     end
 
diff --git a/spec/requests/api/v1/admin/domain_allows_spec.rb b/spec/requests/api/v1/admin/domain_allows_spec.rb
index 96000e3ef4..6db1ab6e30 100644
--- a/spec/requests/api/v1/admin/domain_allows_spec.rb
+++ b/spec/requests/api/v1/admin/domain_allows_spec.rb
@@ -75,15 +75,10 @@ RSpec.describe 'Domain Allows' do
     it_behaves_like 'forbidden for wrong role', ''
     it_behaves_like 'forbidden for wrong role', 'Moderator'
 
-    it 'returns http success' do
+    it 'returns the expected allowed domain name', :aggregate_failures do
       subject
 
       expect(response).to have_http_status(200)
-    end
-
-    it 'returns the expected allowed domain name' do
-      subject
-
       expect(body_as_json[:domain]).to eq domain_allow.domain
     end
 
@@ -108,21 +103,11 @@ RSpec.describe 'Domain Allows' do
     it_behaves_like 'forbidden for wrong role', 'Moderator'
 
     context 'with a valid domain name' do
-      it 'returns http success' do
+      it 'returns the expected domain name', :aggregate_failures do
         subject
 
         expect(response).to have_http_status(200)
-      end
-
-      it 'returns the expected domain name' do
-        subject
-
         expect(body_as_json[:domain]).to eq 'foo.bar.com'
-      end
-
-      it 'creates a domain allow' do
-        subject
-
         expect(DomainAllow.find_by(domain: 'foo.bar.com')).to be_present
       end
     end
@@ -171,15 +156,10 @@ RSpec.describe 'Domain Allows' do
     it_behaves_like 'forbidden for wrong role', ''
     it_behaves_like 'forbidden for wrong role', 'Moderator'
 
-    it 'returns http success' do
+    it 'deletes the allowed domain', :aggregate_failures do
       subject
 
       expect(response).to have_http_status(200)
-    end
-
-    it 'deletes the allowed domain' do
-      subject
-
       expect(DomainAllow.find_by(id: domain_allow.id)).to be_nil
     end
 
diff --git a/spec/requests/api/v1/admin/domain_blocks_spec.rb b/spec/requests/api/v1/admin/domain_blocks_spec.rb
index 7a5ac28c56..1fb6fc8228 100644
--- a/spec/requests/api/v1/admin/domain_blocks_spec.rb
+++ b/spec/requests/api/v1/admin/domain_blocks_spec.rb
@@ -89,15 +89,10 @@ RSpec.describe 'Domain Blocks' do
     it_behaves_like 'forbidden for wrong role', ''
     it_behaves_like 'forbidden for wrong role', 'Moderator'
 
-    it 'returns http success' do
+    it 'returns the expected domain block content', :aggregate_failures do
       subject
 
       expect(response).to have_http_status(200)
-    end
-
-    it 'returns the expected domain block content' do
-      subject
-
       expect(body_as_json).to eq(
         {
           id: domain_block.id.to_s,
@@ -133,27 +128,18 @@ RSpec.describe 'Domain Blocks' do
     it_behaves_like 'forbidden for wrong role', ''
     it_behaves_like 'forbidden for wrong role', 'Moderator'
 
-    it 'returns http success' do
-      subject
-
-      expect(response).to have_http_status(200)
-    end
-
-    it 'returns expected domain name and severity' do
+    it 'returns expected domain name and severity', :aggregate_failures do
       subject
 
       body = body_as_json
 
+      expect(response).to have_http_status(200)
       expect(body).to match a_hash_including(
         {
           domain: 'foo.bar.com',
           severity: 'silence',
         }
       )
-    end
-
-    it 'creates a domain block' do
-      subject
 
       expect(DomainBlock.find_by(domain: 'foo.bar.com')).to be_present
     end
@@ -163,15 +149,10 @@ RSpec.describe 'Domain Blocks' do
         Fabricate(:domain_block, domain: 'bar.com', severity: :suspend)
       end
 
-      it 'returns http unprocessable entity' do
+      it 'returns existing domain block in error', :aggregate_failures do
         subject
 
         expect(response).to have_http_status(422)
-      end
-
-      it 'returns existing domain block in error' do
-        subject
-
         expect(body_as_json[:existing_domain_block][:domain]).to eq('bar.com')
       end
     end
@@ -199,15 +180,10 @@ RSpec.describe 'Domain Blocks' do
     it_behaves_like 'forbidden for wrong role', ''
     it_behaves_like 'forbidden for wrong role', 'Moderator'
 
-    it 'returns http success' do
+    it 'returns the updated domain block', :aggregate_failures do
       subject
 
       expect(response).to have_http_status(200)
-    end
-
-    it 'returns the updated domain block' do
-      subject
-
       expect(body_as_json).to match a_hash_including(
         {
           id: domain_block.id.to_s,
@@ -241,15 +217,10 @@ RSpec.describe 'Domain Blocks' do
     it_behaves_like 'forbidden for wrong role', ''
     it_behaves_like 'forbidden for wrong role', 'Moderator'
 
-    it 'returns http success' do
+    it 'deletes the domain block', :aggregate_failures do
       subject
 
       expect(response).to have_http_status(200)
-    end
-
-    it 'deletes the domain block' do
-      subject
-
       expect(DomainBlock.find_by(id: domain_block.id)).to be_nil
     end
 
diff --git a/spec/requests/api/v1/admin/email_domain_blocks_spec.rb b/spec/requests/api/v1/admin/email_domain_blocks_spec.rb
index d512def866..16656e0202 100644
--- a/spec/requests/api/v1/admin/email_domain_blocks_spec.rb
+++ b/spec/requests/api/v1/admin/email_domain_blocks_spec.rb
@@ -93,15 +93,10 @@ RSpec.describe 'Email Domain Blocks' do
     it_behaves_like 'forbidden for wrong role', 'Moderator'
 
     context 'when email domain block exists' do
-      it 'returns http success' do
+      it 'returns the correct blocked domain', :aggregate_failures do
         subject
 
         expect(response).to have_http_status(200)
-      end
-
-      it 'returns the correct blocked domain' do
-        subject
-
         expect(body_as_json[:domain]).to eq(email_domain_block.domain)
       end
     end
@@ -126,15 +121,10 @@ RSpec.describe 'Email Domain Blocks' do
     it_behaves_like 'forbidden for wrong role', ''
     it_behaves_like 'forbidden for wrong role', 'Moderator'
 
-    it 'returns http success' do
+    it 'returns the correct blocked email domain', :aggregate_failures do
       subject
 
       expect(response).to have_http_status(200)
-    end
-
-    it 'returns the correct blocked email domain' do
-      subject
-
       expect(body_as_json[:domain]).to eq(params[:domain])
     end
 
@@ -182,21 +172,11 @@ RSpec.describe 'Email Domain Blocks' do
     it_behaves_like 'forbidden for wrong role', ''
     it_behaves_like 'forbidden for wrong role', 'Moderator'
 
-    it 'returns http success' do
+    it 'deletes email domain block', :aggregate_failures do
       subject
 
       expect(response).to have_http_status(200)
-    end
-
-    it 'returns an empty body' do
-      subject
-
       expect(body_as_json).to be_empty
-    end
-
-    it 'deletes email domain block' do
-      subject
-
       expect(EmailDomainBlock.find_by(id: email_domain_block.id)).to be_nil
     end
 
diff --git a/spec/requests/api/v1/admin/ip_blocks_spec.rb b/spec/requests/api/v1/admin/ip_blocks_spec.rb
index d03886c51b..fbcb39e3be 100644
--- a/spec/requests/api/v1/admin/ip_blocks_spec.rb
+++ b/spec/requests/api/v1/admin/ip_blocks_spec.rb
@@ -84,15 +84,10 @@ RSpec.describe 'IP Blocks' do
     it_behaves_like 'forbidden for wrong role', ''
     it_behaves_like 'forbidden for wrong role', 'Moderator'
 
-    it 'returns http success' do
+    it 'returns the correct ip block', :aggregate_failures do
       subject
 
       expect(response).to have_http_status(200)
-    end
-
-    it 'returns the correct ip block' do
-      subject
-
       json = body_as_json
 
       expect(json[:ip]).to eq("#{ip_block.ip}/#{ip_block.ip.prefix}")
@@ -119,15 +114,10 @@ RSpec.describe 'IP Blocks' do
     it_behaves_like 'forbidden for wrong role', ''
     it_behaves_like 'forbidden for wrong role', 'Moderator'
 
-    it 'returns http success' do
+    it 'returns the correct ip block', :aggregate_failures do
       subject
 
       expect(response).to have_http_status(200)
-    end
-
-    it 'returns the correct ip block' do
-      subject
-
       json = body_as_json
 
       expect(json[:ip]).to eq("#{params[:ip]}/32")
@@ -186,15 +176,10 @@ RSpec.describe 'IP Blocks' do
     let!(:ip_block) { IpBlock.create(ip: '185.200.13.3', severity: 'no_access', comment: 'Spam', expires_in: 48.hours) }
     let(:params)    { { severity: 'sign_up_requires_approval', comment: 'Decreasing severity' } }
 
-    it 'returns http success' do
+    it 'returns the correct ip block', :aggregate_failures do
       subject
 
       expect(response).to have_http_status(200)
-    end
-
-    it 'returns the correct ip block' do
-      subject
-
       expect(body_as_json).to match(hash_including({
         ip: "#{ip_block.ip}/#{ip_block.ip.prefix}",
         severity: 'sign_up_requires_approval',
@@ -226,21 +211,11 @@ RSpec.describe 'IP Blocks' do
 
     let!(:ip_block) { IpBlock.create(ip: '185.200.13.3', severity: 'no_access') }
 
-    it 'returns http success' do
+    it 'deletes the ip block', :aggregate_failures do
       subject
 
       expect(response).to have_http_status(200)
-    end
-
-    it 'returns an empty body' do
-      subject
-
       expect(body_as_json).to be_empty
-    end
-
-    it 'deletes the ip block' do
-      subject
-
       expect(IpBlock.find_by(id: ip_block.id)).to be_nil
     end
 
diff --git a/spec/requests/api/v1/admin/reports_spec.rb b/spec/requests/api/v1/admin/reports_spec.rb
index 91c3c11f5d..5403457db0 100644
--- a/spec/requests/api/v1/admin/reports_spec.rb
+++ b/spec/requests/api/v1/admin/reports_spec.rb
@@ -122,15 +122,10 @@ RSpec.describe 'Reports' do
     it_behaves_like 'forbidden for wrong scope', 'write:statuses'
     it_behaves_like 'forbidden for wrong role', ''
 
-    it 'returns http success' do
+    it 'returns the requested report content', :aggregate_failures do
       subject
 
       expect(response).to have_http_status(200)
-    end
-
-    it 'returns the requested report content' do
-      subject
-
       expect(body_as_json).to include(
         {
           id: report.id.to_s,
@@ -155,18 +150,10 @@ RSpec.describe 'Reports' do
     let!(:report) { Fabricate(:report, category: :other) }
     let(:params)  { { category: 'spam' } }
 
-    it 'returns http success' do
-      subject
+    it 'updates the report category', :aggregate_failures do
+      expect { subject }.to change { report.reload.category }.from('other').to('spam')
 
       expect(response).to have_http_status(200)
-    end
-
-    it 'updates the report category' do
-      expect { subject }.to change { report.reload.category }.from('other').to('spam')
-    end
-
-    it 'returns the updated report content' do
-      subject
 
       report.reload
 
@@ -196,14 +183,9 @@ RSpec.describe 'Reports' do
     it_behaves_like 'forbidden for wrong scope', 'write:statuses'
     it_behaves_like 'forbidden for wrong role', ''
 
-    it 'returns http success' do
-      subject
-
-      expect(response).to have_http_status(200)
-    end
-
-    it 'marks report as resolved' do
+    it 'marks report as resolved', :aggregate_failures do
       expect { subject }.to change { report.reload.unresolved? }.from(true).to(false)
+      expect(response).to have_http_status(200)
     end
   end
 
@@ -217,14 +199,9 @@ RSpec.describe 'Reports' do
     it_behaves_like 'forbidden for wrong scope', 'write:statuses'
     it_behaves_like 'forbidden for wrong role', ''
 
-    it 'returns http success' do
-      subject
-
-      expect(response).to have_http_status(200)
-    end
-
-    it 'marks report as unresolved' do
+    it 'marks report as unresolved', :aggregate_failures do
       expect { subject }.to change { report.reload.unresolved? }.from(false).to(true)
+      expect(response).to have_http_status(200)
     end
   end
 
@@ -238,14 +215,9 @@ RSpec.describe 'Reports' do
     it_behaves_like 'forbidden for wrong scope', 'write:statuses'
     it_behaves_like 'forbidden for wrong role', ''
 
-    it 'returns http success' do
-      subject
-
-      expect(response).to have_http_status(200)
-    end
-
-    it 'assigns report to the requesting user' do
+    it 'assigns report to the requesting user', :aggregate_failures do
       expect { subject }.to change { report.reload.assigned_account_id }.from(nil).to(user.account.id)
+      expect(response).to have_http_status(200)
     end
   end
 
@@ -259,14 +231,9 @@ RSpec.describe 'Reports' do
     it_behaves_like 'forbidden for wrong scope', 'write:statuses'
     it_behaves_like 'forbidden for wrong role', ''
 
-    it 'returns http success' do
-      subject
-
-      expect(response).to have_http_status(200)
-    end
-
-    it 'unassigns report from assignee' do
+    it 'unassigns report from assignee', :aggregate_failures do
       expect { subject }.to change { report.reload.assigned_account_id }.from(user.account.id).to(nil)
+      expect(response).to have_http_status(200)
     end
   end
 end
diff --git a/spec/requests/api/v1/apps/credentials_spec.rb b/spec/requests/api/v1/apps/credentials_spec.rb
index dafe168c56..1268b36f8a 100644
--- a/spec/requests/api/v1/apps/credentials_spec.rb
+++ b/spec/requests/api/v1/apps/credentials_spec.rb
@@ -12,14 +12,10 @@ describe 'Credentials' do
       let(:token)   { Fabricate(:accessible_access_token, scopes: 'read', application: Fabricate(:application)) }
       let(:headers) { { 'Authorization' => "Bearer #{token.token}" } }
 
-      it 'returns http success' do
+      it 'returns the app information correctly', :aggregate_failures do
         subject
 
         expect(response).to have_http_status(200)
-      end
-
-      it 'returns the app information correctly' do
-        subject
 
         expect(body_as_json).to match(
           a_hash_including(
diff --git a/spec/requests/api/v1/apps_spec.rb b/spec/requests/api/v1/apps_spec.rb
index 88f9eee360..acabbc93f0 100644
--- a/spec/requests/api/v1/apps_spec.rb
+++ b/spec/requests/api/v1/apps_spec.rb
@@ -23,20 +23,11 @@ RSpec.describe 'Apps' do
     end
 
     context 'with valid params' do
-      it 'returns http success' do
+      it 'creates an OAuth app', :aggregate_failures do
         subject
 
         expect(response).to have_http_status(200)
-      end
-
-      it 'creates an OAuth app' do
-        subject
-
         expect(Doorkeeper::Application.find_by(name: client_name)).to be_present
-      end
-
-      it 'returns client ID and client secret' do
-        subject
 
         body = body_as_json
 
@@ -58,15 +49,10 @@ RSpec.describe 'Apps' do
     context 'with many duplicate scopes' do
       let(:scopes) { (%w(read) * 40).join(' ') }
 
-      it 'returns http success' do
+      it 'only saves the scope once', :aggregate_failures do
         subject
 
         expect(response).to have_http_status(200)
-      end
-
-      it 'only saves the scope once' do
-        subject
-
         expect(Doorkeeper::Application.find_by(name: client_name).scopes.to_s).to eq 'read'
       end
     end
diff --git a/spec/requests/api/v1/domain_blocks_spec.rb b/spec/requests/api/v1/domain_blocks_spec.rb
index 0f4fd4e90e..954497ebe1 100644
--- a/spec/requests/api/v1/domain_blocks_spec.rb
+++ b/spec/requests/api/v1/domain_blocks_spec.rb
@@ -22,15 +22,10 @@ RSpec.describe 'Domain blocks' do
 
     it_behaves_like 'forbidden for wrong scope', 'write:blocks'
 
-    it 'returns http success' do
+    it 'returns the domains blocked by the requesting user', :aggregate_failures do
       subject
 
       expect(response).to have_http_status(200)
-    end
-
-    it 'returns the domains blocked by the requesting user' do
-      subject
-
       expect(body_as_json).to match_array(blocked_domains)
     end
 
@@ -54,15 +49,10 @@ RSpec.describe 'Domain blocks' do
 
     it_behaves_like 'forbidden for wrong scope', 'read read:blocks'
 
-    it 'returns http success' do
+    it 'creates a domain block', :aggregate_failures do
       subject
 
       expect(response).to have_http_status(200)
-    end
-
-    it 'creates a domain block' do
-      subject
-
       expect(user.account.domain_blocking?(params[:domain])).to be(true)
     end
 
@@ -100,15 +90,10 @@ RSpec.describe 'Domain blocks' do
 
     it_behaves_like 'forbidden for wrong scope', 'read read:blocks'
 
-    it 'returns http success' do
+    it 'deletes the specified domain block', :aggregate_failures do
       subject
 
       expect(response).to have_http_status(200)
-    end
-
-    it 'deletes the specified domain block' do
-      subject
-
       expect(user.account.domain_blocking?('example.com')).to be(false)
     end
 
diff --git a/spec/requests/api/v1/follow_requests_spec.rb b/spec/requests/api/v1/follow_requests_spec.rb
index 9d4ef8cd55..1d78c9be19 100644
--- a/spec/requests/api/v1/follow_requests_spec.rb
+++ b/spec/requests/api/v1/follow_requests_spec.rb
@@ -32,15 +32,10 @@ RSpec.describe 'Follow requests' do
 
     it_behaves_like 'forbidden for wrong scope', 'write write:follows'
 
-    it 'returns http success' do
+    it 'returns the expected content from accounts requesting to follow', :aggregate_failures do
       subject
 
       expect(response).to have_http_status(200)
-    end
-
-    it 'returns the expected content from accounts requesting to follow' do
-      subject
-
       expect(body_as_json).to match_array(expected_response)
     end
 
@@ -68,19 +63,9 @@ RSpec.describe 'Follow requests' do
 
     it_behaves_like 'forbidden for wrong scope', 'read read:follows'
 
-    it 'returns http success' do
-      subject
-
-      expect(response).to have_http_status(200)
-    end
-
-    it 'allows the requesting follower to follow' do
+    it 'allows the requesting follower to follow', :aggregate_failures do
       expect { subject }.to change { follower.following?(user.account) }.from(false).to(true)
-    end
-
-    it 'returns JSON with followed_by set to true' do
-      subject
-
+      expect(response).to have_http_status(200)
       expect(body_as_json[:followed_by]).to be true
     end
   end
@@ -98,21 +83,11 @@ RSpec.describe 'Follow requests' do
 
     it_behaves_like 'forbidden for wrong scope', 'read read:follows'
 
-    it 'returns http success' do
+    it 'removes the follow request', :aggregate_failures do
       subject
 
       expect(response).to have_http_status(200)
-    end
-
-    it 'removes the follow request' do
-      subject
-
       expect(FollowRequest.where(target_account: user.account, account: follower)).to_not exist
-    end
-
-    it 'returns JSON with followed_by set to false' do
-      subject
-
       expect(body_as_json[:followed_by]).to be false
     end
   end
diff --git a/spec/requests/api/v1/lists_spec.rb b/spec/requests/api/v1/lists_spec.rb
index 383e09d0c3..22dde43a19 100644
--- a/spec/requests/api/v1/lists_spec.rb
+++ b/spec/requests/api/v1/lists_spec.rb
@@ -39,15 +39,10 @@ RSpec.describe 'Lists' do
 
     it_behaves_like 'forbidden for wrong scope', 'write write:lists'
 
-    it 'returns http success' do
+    it 'returns the expected lists', :aggregate_failures do
       subject
 
       expect(response).to have_http_status(200)
-    end
-
-    it 'returns the expected lists' do
-      subject
-
       expect(body_as_json).to match_array(expected_response)
     end
   end
@@ -61,15 +56,10 @@ RSpec.describe 'Lists' do
 
     it_behaves_like 'forbidden for wrong scope', 'write write:lists'
 
-    it 'returns http success' do
+    it 'returns the requested list correctly', :aggregate_failures do
       subject
 
       expect(response).to have_http_status(200)
-    end
-
-    it 'returns the requested list correctly' do
-      subject
-
       expect(body_as_json).to eq({
         id: list.id.to_s,
         title: list.title,
@@ -106,21 +96,11 @@ RSpec.describe 'Lists' do
 
     it_behaves_like 'forbidden for wrong scope', 'read read:lists'
 
-    it 'returns http success' do
+    it 'returns the new list', :aggregate_failures do
       subject
 
       expect(response).to have_http_status(200)
-    end
-
-    it 'returns the new list' do
-      subject
-
       expect(body_as_json).to match(a_hash_including(title: 'my list', replies_policy: 'none', exclusive: true))
-    end
-
-    it 'creates a list' do
-      subject
-
       expect(List.where(account: user.account).count).to eq(1)
     end
 
@@ -155,15 +135,10 @@ RSpec.describe 'Lists' do
 
     it_behaves_like 'forbidden for wrong scope', 'read read:lists'
 
-    it 'returns http success' do
+    it 'returns the updated list', :aggregate_failures do
       subject
 
       expect(response).to have_http_status(200)
-    end
-
-    it 'returns the updated list' do
-      subject
-
       list.reload
 
       expect(body_as_json).to eq({
@@ -214,15 +189,10 @@ RSpec.describe 'Lists' do
 
     it_behaves_like 'forbidden for wrong scope', 'read read:lists'
 
-    it 'returns http success' do
+    it 'deletes the list', :aggregate_failures do
       subject
 
       expect(response).to have_http_status(200)
-    end
-
-    it 'deletes the list' do
-      subject
-
       expect(List.where(id: list.id)).to_not exist
     end
 
diff --git a/spec/requests/api/v1/tags_spec.rb b/spec/requests/api/v1/tags_spec.rb
index 300ddf805c..db74a6f037 100644
--- a/spec/requests/api/v1/tags_spec.rb
+++ b/spec/requests/api/v1/tags_spec.rb
@@ -17,15 +17,10 @@ RSpec.describe 'Tags' do
       let!(:tag) { Fabricate(:tag) }
       let(:name) { tag.name }
 
-      it 'returns http success' do
+      it 'returns the tag', :aggregate_failures do
         subject
 
         expect(response).to have_http_status(200)
-      end
-
-      it 'returns the tag' do
-        subject
-
         expect(body_as_json[:name]).to eq(name)
       end
     end
@@ -62,15 +57,10 @@ RSpec.describe 'Tags' do
     it_behaves_like 'forbidden for wrong scope', 'read read:follows'
 
     context 'when the tag exists' do
-      it 'returns http success' do
+      it 'creates follow', :aggregate_failures do
         subject
 
         expect(response).to have_http_status(:success)
-      end
-
-      it 'creates follow' do
-        subject
-
         expect(TagFollow.where(tag: tag, account: user.account)).to exist
       end
     end
@@ -78,21 +68,11 @@ RSpec.describe 'Tags' do
     context 'when the tag does not exist' do
       let(:name) { 'hoge' }
 
-      it 'returns http success' do
+      it 'creates a new tag with the specified name', :aggregate_failures do
         subject
 
         expect(response).to have_http_status(200)
-      end
-
-      it 'creates a new tag with the specified name' do
-        subject
-
         expect(Tag.where(name: name)).to exist
-      end
-
-      it 'creates follow' do
-        subject
-
         expect(TagFollow.where(tag: Tag.find_by(name: name), account: user.account)).to exist
       end
     end
@@ -133,15 +113,10 @@ RSpec.describe 'Tags' do
 
     it_behaves_like 'forbidden for wrong scope', 'read read:follows'
 
-    it 'returns http success' do
+    it 'removes the follow', :aggregate_failures do
       subject
 
       expect(response).to have_http_status(200)
-    end
-
-    it 'removes the follow' do
-      subject
-
       expect(TagFollow.where(tag: tag, account: user.account)).to_not exist
     end