From 4230d1ee0a0752e0259e96e1590f7eb9fb1ebb9b Mon Sep 17 00:00:00 2001 From: Daniel M Brasil Date: Fri, 13 Oct 2023 09:04:37 -0300 Subject: [PATCH 01/24] Migrate to request specs in `/api/v1/timelines/tag/:hashtag` (#25797) --- .../api/v1/timelines/tag_controller_spec.rb | 71 ----------- spec/requests/api/v1/timelines/tag_spec.rb | 112 ++++++++++++++++++ 2 files changed, 112 insertions(+), 71 deletions(-) delete mode 100644 spec/controllers/api/v1/timelines/tag_controller_spec.rb create mode 100644 spec/requests/api/v1/timelines/tag_spec.rb diff --git a/spec/controllers/api/v1/timelines/tag_controller_spec.rb b/spec/controllers/api/v1/timelines/tag_controller_spec.rb deleted file mode 100644 index 1c60798fcf..0000000000 --- a/spec/controllers/api/v1/timelines/tag_controller_spec.rb +++ /dev/null @@ -1,71 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' - -describe Api::V1::Timelines::TagController do - render_views - - let(:user) { Fabricate(:user) } - let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'read:statuses') } - - before do - allow(controller).to receive(:doorkeeper_token) { token } - end - - describe 'GET #show' do - subject do - get :show, params: { id: 'test' } - end - - before do - PostStatusService.new.call(user.account, text: 'It is a #test') - end - - context 'when the instance allows public preview' do - context 'when the user is not authenticated' do - let(:token) { nil } - - it 'returns http success', :aggregate_failures do - subject - - expect(response).to have_http_status(200) - expect(response.headers['Link'].links.size).to eq(2) - end - end - - context 'when the user is authenticated' do - it 'returns http success', :aggregate_failures do - subject - - expect(response).to have_http_status(200) - expect(response.headers['Link'].links.size).to eq(2) - end - end - end - - context 'when the instance does not allow public preview' do - before do - Form::AdminSettings.new(timeline_preview: false).save - end - - context 'when the user is not authenticated' do - let(:token) { nil } - - it 'returns http unauthorized' do - subject - - expect(response).to have_http_status(401) - end - end - - context 'when the user is authenticated' do - it 'returns http success', :aggregate_failures do - subject - - expect(response).to have_http_status(200) - expect(response.headers['Link'].links.size).to eq(2) - end - end - end - end -end diff --git a/spec/requests/api/v1/timelines/tag_spec.rb b/spec/requests/api/v1/timelines/tag_spec.rb new file mode 100644 index 0000000000..a118af13e2 --- /dev/null +++ b/spec/requests/api/v1/timelines/tag_spec.rb @@ -0,0 +1,112 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe 'Tag' do + let(:user) { Fabricate(:user) } + let(:scopes) { 'read:statuses' } + let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) } + let(:headers) { { 'Authorization' => "Bearer #{token.token}" } } + + shared_examples 'a successful request to the tag timeline' do + it 'returns the expected statuses', :aggregate_failures do + subject + + expect(response).to have_http_status(200) + expect(body_as_json.pluck(:id)).to match_array(expected_statuses.map { |status| status.id.to_s }) + end + end + + describe 'GET /api/v1/timelines/tag/:hashtag' do + subject do + get "/api/v1/timelines/tag/#{hashtag}", headers: headers, params: params + end + + let(:account) { Fabricate(:account) } + let!(:private_status) { PostStatusService.new.call(account, visibility: :private, text: '#life could be a dream') } # rubocop:disable RSpec/LetSetup + let!(:life_status) { PostStatusService.new.call(account, text: 'tell me what is my #life without your #love') } + let!(:war_status) { PostStatusService.new.call(user.account, text: '#war, war never changes') } + let!(:love_status) { PostStatusService.new.call(account, text: 'what is #love?') } + let(:params) { {} } + let(:hashtag) { 'life' } + + context 'when given only one hashtag' do + let(:expected_statuses) { [life_status] } + + it_behaves_like 'a successful request to the tag timeline' + end + + context 'with any param' do + let(:expected_statuses) { [life_status, love_status] } + let(:params) { { any: %(love) } } + + it_behaves_like 'a successful request to the tag timeline' + end + + context 'with all param' do + let(:expected_statuses) { [life_status] } + let(:params) { { all: %w(love) } } + + it_behaves_like 'a successful request to the tag timeline' + end + + context 'with none param' do + let(:expected_statuses) { [war_status] } + let(:hashtag) { 'war' } + let(:params) { { none: %w(life love) } } + + it_behaves_like 'a successful request to the tag timeline' + end + + context 'with limit param' do + let(:hashtag) { 'love' } + let(:params) { { limit: 1 } } + + it 'returns only the requested number of statuses' do + subject + + expect(body_as_json.size).to eq(params[:limit]) + end + + it 'sets the correct pagination headers', :aggregate_failures do + subject + + headers = response.headers['Link'] + + expect(headers.find_link(%w(rel prev)).href).to eq(api_v1_timelines_tag_url(limit: 1, min_id: love_status.id.to_s)) + expect(headers.find_link(%w(rel next)).href).to eq(api_v1_timelines_tag_url(limit: 1, max_id: love_status.id.to_s)) + end + end + + context 'when the instance allows public preview' do + context 'when the user is not authenticated' do + let(:headers) { {} } + let(:expected_statuses) { [life_status] } + + it_behaves_like 'a successful request to the tag timeline' + end + end + + context 'when the instance does not allow public preview' do + before do + Form::AdminSettings.new(timeline_preview: false).save + end + + context 'when the user is not authenticated' do + let(:headers) { {} } + + it 'returns http unauthorized' do + subject + + expect(response).to have_http_status(401) + end + end + + context 'when the user is authenticated' do + let(:expected_statuses) { [life_status] } + + it_behaves_like 'a successful request to the tag timeline' + end + end + end +end From 7664e3b69207a221b036e3afb84b897ab9078e6a Mon Sep 17 00:00:00 2001 From: Daniel M Brasil Date: Fri, 13 Oct 2023 09:08:21 -0300 Subject: [PATCH 02/24] Migrate to request specs in `/api/v1/statuses/:status_id/source` (#25634) --- .../v1/statuses/sources_controller_spec.rb | 29 -------- spec/requests/api/v1/statuses/sources_spec.rb | 72 +++++++++++++++++++ 2 files changed, 72 insertions(+), 29 deletions(-) delete mode 100644 spec/controllers/api/v1/statuses/sources_controller_spec.rb create mode 100644 spec/requests/api/v1/statuses/sources_spec.rb diff --git a/spec/controllers/api/v1/statuses/sources_controller_spec.rb b/spec/controllers/api/v1/statuses/sources_controller_spec.rb deleted file mode 100644 index fbe6fa0be6..0000000000 --- a/spec/controllers/api/v1/statuses/sources_controller_spec.rb +++ /dev/null @@ -1,29 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' - -describe Api::V1::Statuses::SourcesController 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 'GET #show' do - let(:status) { Fabricate(:status, account: user.account) } - - before do - get :show, 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/requests/api/v1/statuses/sources_spec.rb b/spec/requests/api/v1/statuses/sources_spec.rb new file mode 100644 index 0000000000..723b81905e --- /dev/null +++ b/spec/requests/api/v1/statuses/sources_spec.rb @@ -0,0 +1,72 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe 'Sources' do + let(:user) { Fabricate(:user) } + let(:scopes) { 'read:statuses' } + let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) } + let(:headers) { { 'Authorization' => "Bearer #{token.token}" } } + + describe 'GET /api/v1/statuses/:status_id/source' do + subject do + get "/api/v1/statuses/#{status.id}/source", headers: headers + end + + let(:status) { Fabricate(:status) } + + it_behaves_like 'forbidden for wrong scope', 'write write:statuses' + + context 'with public status' do + it 'returns the source properties of the status', :aggregate_failures do + subject + + expect(response).to have_http_status(200) + expect(body_as_json).to eq({ + id: status.id.to_s, + text: status.text, + spoiler_text: status.spoiler_text, + }) + end + end + + context 'with private status of non-followed account' do + let(:status) { Fabricate(:status, visibility: :private) } + + it 'returns http not found' do + subject + + expect(response).to have_http_status(404) + end + end + + context 'with private status of followed account' do + let(:status) { Fabricate(:status, visibility: :private) } + + before do + user.account.follow!(status.account) + end + + it 'returns the source properties of the status', :aggregate_failures do + subject + + expect(response).to have_http_status(200) + expect(body_as_json).to eq({ + id: status.id.to_s, + text: status.text, + spoiler_text: status.spoiler_text, + }) + end + end + + context 'without an authorization header' do + let(:headers) { {} } + + it 'returns http unauthorized' do + subject + + expect(response).to have_http_status(401) + end + end + end +end From ecdb31d4799422245f73d74b280bc8733081a6d7 Mon Sep 17 00:00:00 2001 From: Daniel M Brasil Date: Fri, 13 Oct 2023 09:19:12 -0300 Subject: [PATCH 03/24] Migrate to request specs in `/api/v1/favourites` (#25518) --- .../api/v1/favourites_controller_spec.rb | 80 ------------------- spec/requests/api/v1/favourites_spec.rb | 71 ++++++++++++++++ 2 files changed, 71 insertions(+), 80 deletions(-) delete mode 100644 spec/controllers/api/v1/favourites_controller_spec.rb create mode 100644 spec/requests/api/v1/favourites_spec.rb diff --git a/spec/controllers/api/v1/favourites_controller_spec.rb b/spec/controllers/api/v1/favourites_controller_spec.rb deleted file mode 100644 index c9ca046be0..0000000000 --- a/spec/controllers/api/v1/favourites_controller_spec.rb +++ /dev/null @@ -1,80 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' - -RSpec.describe Api::V1::FavouritesController do - render_views - - let(:user) { Fabricate(:user) } - let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'read') } - - describe 'GET #index' do - context 'without token' do - it 'returns http unauthorized' do - get :index - expect(response).to have_http_status 401 - end - end - - context 'with token' do - context 'without read scope' do - before do - allow(controller).to receive(:doorkeeper_token) do - Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: '') - end - end - - it 'returns http forbidden' do - get :index - expect(response).to have_http_status 403 - end - end - - context 'without valid resource owner' do - before do - token = Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'read') - user.destroy! - - allow(controller).to receive(:doorkeeper_token) { token } - end - - it 'returns http unprocessable entity' do - get :index - expect(response).to have_http_status 422 - end - end - - context 'with read scope and valid resource owner' do - before do - allow(controller).to receive(:doorkeeper_token) do - Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'read:favourites') - end - end - - it 'shows favourites owned by the user' do - favourite_by_user = Fabricate(:favourite, account: user.account) - favourite_by_others = Fabricate(:favourite) - - get :index - - expect(assigns(:statuses)).to contain_exactly(favourite_by_user.status) - end - - it 'adds pagination headers if necessary' do - favourite = Fabricate(:favourite, account: user.account) - - get :index, params: { limit: 1 } - - expect(response.headers['Link'].find_link(%w(rel next)).href).to eq "http://test.host/api/v1/favourites?limit=1&max_id=#{favourite.id}" - expect(response.headers['Link'].find_link(%w(rel prev)).href).to eq "http://test.host/api/v1/favourites?limit=1&min_id=#{favourite.id}" - end - - it 'does not add pagination headers if not necessary' do - get :index - - expect(response.headers['Link']).to be_nil - end - end - end - end -end diff --git a/spec/requests/api/v1/favourites_spec.rb b/spec/requests/api/v1/favourites_spec.rb new file mode 100644 index 0000000000..713990592c --- /dev/null +++ b/spec/requests/api/v1/favourites_spec.rb @@ -0,0 +1,71 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe 'Favourites' do + let(:user) { Fabricate(:user) } + let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) } + let(:scopes) { 'read:favourites' } + let(:headers) { { Authorization: "Bearer #{token.token}" } } + + describe 'GET /api/v1/favourites' do + subject do + get '/api/v1/favourites', headers: headers, params: params + end + + let(:params) { {} } + let!(:favourites) { Fabricate.times(3, :favourite, account: user.account) } + + let(:expected_response) do + favourites.map do |favourite| + a_hash_including(id: favourite.status.id.to_s, account: a_hash_including(id: favourite.status.account.id.to_s)) + end + end + + it_behaves_like 'forbidden for wrong scope', 'write' + + it 'returns http success' do + subject + + expect(response).to have_http_status(200) + end + + it 'returns the favourites' do + subject + + expect(body_as_json).to match_array(expected_response) + end + + context 'with limit param' do + let(:params) { { limit: 2 } } + + it 'returns only the requested number of favourites' do + subject + + expect(body_as_json.size).to eq(params[:limit]) + end + + it 'sets the correct pagination header for the prev path' do + subject + + expect(response.headers['Link'].find_link(%w(rel prev)).href).to eq(api_v1_favourites_url(limit: params[:limit], min_id: favourites.last.id)) + end + + it 'sets the correct pagination header for the next path' do + subject + + expect(response.headers['Link'].find_link(%w(rel next)).href).to eq(api_v1_favourites_url(limit: params[:limit], max_id: favourites[1].id)) + end + end + + context 'without an authorization header' do + let(:headers) { {} } + + it 'returns http unauthorized' do + subject + + expect(response).to have_http_status(401) + end + end + end +end From fd9dea21d0da9d5c4184efca8f66cc2ffc5435f6 Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Fri, 13 Oct 2023 08:42:09 -0400 Subject: [PATCH 04/24] 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 From bc6cd27d9edc473da3403c9af7970db8114b2135 Mon Sep 17 00:00:00 2001 From: Daniel M Brasil Date: Fri, 13 Oct 2023 09:43:50 -0300 Subject: [PATCH 05/24] Migrate to request specs in `/api/v1/followed_tags` (#25472) --- .rubocop_todo.yml | 1 - .../api/v1/followed_tags_controller_spec.rb | 25 ------- spec/requests/api/v1/followed_tags_spec.rb | 65 +++++++++++++++++++ 3 files changed, 65 insertions(+), 26 deletions(-) delete mode 100644 spec/controllers/api/v1/followed_tags_controller_spec.rb create mode 100644 spec/requests/api/v1/followed_tags_spec.rb diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index 19cfcff7f7..d58bee4ba8 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -180,7 +180,6 @@ RSpec/LetSetup: - 'spec/controllers/api/v1/accounts/statuses_controller_spec.rb' - 'spec/controllers/api/v1/admin/accounts_controller_spec.rb' - 'spec/controllers/api/v1/filters_controller_spec.rb' - - 'spec/controllers/api/v1/followed_tags_controller_spec.rb' - 'spec/controllers/api/v2/admin/accounts_controller_spec.rb' - 'spec/controllers/api/v2/filters/keywords_controller_spec.rb' - 'spec/controllers/api/v2/filters/statuses_controller_spec.rb' diff --git a/spec/controllers/api/v1/followed_tags_controller_spec.rb b/spec/controllers/api/v1/followed_tags_controller_spec.rb deleted file mode 100644 index c1a366d4e3..0000000000 --- a/spec/controllers/api/v1/followed_tags_controller_spec.rb +++ /dev/null @@ -1,25 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' - -RSpec.describe Api::V1::FollowedTagsController do - render_views - - let(:user) { Fabricate(:user) } - let(:scopes) { 'read:follows' } - let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) } - - before { allow(controller).to receive(:doorkeeper_token) { token } } - - describe 'GET #index' do - let!(:tag_follows) { Fabricate.times(5, :tag_follow, account: user.account) } - - before do - get :index, params: { limit: 1 } - end - - it 'returns http success' do - expect(response).to have_http_status(:success) - end - end -end diff --git a/spec/requests/api/v1/followed_tags_spec.rb b/spec/requests/api/v1/followed_tags_spec.rb new file mode 100644 index 0000000000..9391c7bdc8 --- /dev/null +++ b/spec/requests/api/v1/followed_tags_spec.rb @@ -0,0 +1,65 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe 'Followed tags' do + let(:user) { Fabricate(:user) } + let(:scopes) { 'read:follows' } + let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) } + let(:headers) { { 'Authorization' => "Bearer #{token.token}" } } + + describe 'GET /api/v1/followed_tags' do + subject do + get '/api/v1/followed_tags', headers: headers, params: params + end + + let!(:tag_follows) { Fabricate.times(5, :tag_follow, account: user.account) } + let(:params) { {} } + + let(:expected_response) do + tag_follows.map do |tag_follow| + a_hash_including(name: tag_follow.tag.name, following: true) + end + end + + before do + Fabricate(:tag_follow) + end + + it_behaves_like 'forbidden for wrong scope', 'write write:follows' + + it 'returns http success' do + subject + + expect(response).to have_http_status(:success) + end + + it 'returns the followed tags correctly' do + subject + + expect(body_as_json).to match_array(expected_response) + end + + context 'with limit param' do + let(:params) { { limit: 3 } } + + it 'returns only the requested number of follow tags' do + subject + + expect(body_as_json.size).to eq(params[:limit]) + end + + it 'sets the correct pagination header for the prev path' do + subject + + expect(response.headers['Link'].find_link(%w(rel prev)).href).to eq(api_v1_followed_tags_url(limit: params[:limit], since_id: tag_follows.last.id)) + end + + it 'sets the correct pagination header for the next path' do + subject + + expect(response.headers['Link'].find_link(%w(rel next)).href).to eq(api_v1_followed_tags_url(limit: params[:limit], max_id: tag_follows[2].id)) + end + end + end +end From abf0e1fa39f67baecab5c3893845e34a0f7be198 Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Fri, 13 Oct 2023 09:19:40 -0400 Subject: [PATCH 06/24] Move `SignedReqestHelpers` rspec config to separate file (#25453) --- spec/rails_helper.rb | 20 -------------------- spec/support/signed_request_helpers.rb | 21 +++++++++++++++++++++ 2 files changed, 21 insertions(+), 20 deletions(-) create mode 100644 spec/support/signed_request_helpers.rb diff --git a/spec/rails_helper.rb b/spec/rails_helper.rb index 7b8dccb6a0..06e8418a0e 100644 --- a/spec/rails_helper.rb +++ b/spec/rails_helper.rb @@ -54,26 +54,6 @@ Devise::Test::ControllerHelpers.module_eval do end end -module SignedRequestHelpers - def get(path, headers: nil, sign_with: nil, **args) - return super path, headers: headers, **args if sign_with.nil? - - headers ||= {} - headers['Date'] = Time.now.utc.httpdate - headers['Host'] = ENV.fetch('LOCAL_DOMAIN') - signed_headers = headers.merge('(request-target)' => "get #{path}").slice('(request-target)', 'Host', 'Date') - - key_id = ActivityPub::TagManager.instance.key_uri_for(sign_with) - keypair = sign_with.keypair - signed_string = signed_headers.map { |key, value| "#{key.downcase}: #{value}" }.join("\n") - signature = Base64.strict_encode64(keypair.sign(OpenSSL::Digest.new('SHA256'), signed_string)) - - headers['Signature'] = "keyId=\"#{key_id}\",algorithm=\"rsa-sha256\",headers=\"#{signed_headers.keys.join(' ').downcase}\",signature=\"#{signature}\"" - - super path, headers: headers, **args - end -end - RSpec.configure do |config| # This is set before running spec:system, see lib/tasks/tests.rake config.filter_run_excluding type: lambda { |type| diff --git a/spec/support/signed_request_helpers.rb b/spec/support/signed_request_helpers.rb new file mode 100644 index 0000000000..33d7dba6b8 --- /dev/null +++ b/spec/support/signed_request_helpers.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +module SignedRequestHelpers + def get(path, headers: nil, sign_with: nil, **args) + return super path, headers: headers, **args if sign_with.nil? + + headers ||= {} + headers['Date'] = Time.now.utc.httpdate + headers['Host'] = ENV.fetch('LOCAL_DOMAIN') + signed_headers = headers.merge('(request-target)' => "get #{path}").slice('(request-target)', 'Host', 'Date') + + key_id = ActivityPub::TagManager.instance.key_uri_for(sign_with) + keypair = sign_with.keypair + signed_string = signed_headers.map { |key, value| "#{key.downcase}: #{value}" }.join("\n") + signature = Base64.strict_encode64(keypair.sign(OpenSSL::Digest.new('SHA256'), signed_string)) + + headers['Signature'] = "keyId=\"#{key_id}\",algorithm=\"rsa-sha256\",headers=\"#{signed_headers.keys.join(' ').downcase}\",signature=\"#{signature}\"" + + super path, headers: headers, **args + end +end From 058f73a4f5f17fc33bb842115d1f69a4b2e9f740 Mon Sep 17 00:00:00 2001 From: Daniel M Brasil Date: Fri, 13 Oct 2023 10:30:40 -0300 Subject: [PATCH 07/24] Add request specs for `/api/v1/reports` (#25475) --- spec/requests/api/v1/reports_spec.rb | 106 +++++++++++++++++++++++++++ 1 file changed, 106 insertions(+) create mode 100644 spec/requests/api/v1/reports_spec.rb diff --git a/spec/requests/api/v1/reports_spec.rb b/spec/requests/api/v1/reports_spec.rb new file mode 100644 index 0000000000..ce74f17c48 --- /dev/null +++ b/spec/requests/api/v1/reports_spec.rb @@ -0,0 +1,106 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe 'Reports' do + let(:user) { Fabricate(:user) } + let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) } + let(:scopes) { 'write:reports' } + let(:headers) { { 'Authorization' => "Bearer #{token.token}" } } + + describe 'POST /api/v1/reports' do + subject do + post '/api/v1/reports', headers: headers, params: params + end + + let!(:admin) { Fabricate(:user, role: UserRole.find_by(name: 'Admin')) } + let(:status) { Fabricate(:status) } + let(:target_account) { status.account } + let(:category) { 'other' } + let(:forward) { nil } + let(:rule_ids) { nil } + + let(:params) do + { + status_ids: [status.id], + account_id: target_account.id, + comment: 'reasons', + category: category, + rule_ids: rule_ids, + forward: forward, + } + end + + it_behaves_like 'forbidden for wrong scope', 'read read:reports' + + it 'returns http success' do + subject + + expect(response).to have_http_status(200) + end + + it 'returns the created report' do + subject + + expect(body_as_json).to match( + a_hash_including( + status_ids: [status.id.to_s], + category: category, + comment: 'reasons' + ) + ) + end + + it 'creates a report' do + subject + + expect(target_account.targeted_reports).to_not be_empty + end + + it 'sends e-mails to admins' do + perform_enqueued_jobs do + subject + + expect(ActionMailer::Base.deliveries.first.to).to eq([admin.email]) + end + end + + context 'when a status does not belong to the reported account' do + let(:target_account) { Fabricate(:account) } + + it 'returns http not found' do + subject + + expect(response).to have_http_status(404) + end + end + + context 'when a category is chosen' do + let(:category) { 'spam' } + + it 'saves category' do + subject + + expect(target_account.targeted_reports.first.spam?).to be true + end + end + + context 'when violated rules are chosen' do + let(:rule) { Fabricate(:rule) } + let(:category) { 'violation' } + let(:rule_ids) { [rule.id] } + + it 'saves category' do + subject + + expect(target_account.targeted_reports.first.violation?).to be true + end + + it 'saves rule_ids' do + subject + + expect(target_account.targeted_reports.first.rule_ids).to contain_exactly(rule.id) + end + end + end +end From cad8cc90baf721fc4269b8ac36d4411ce8fe8539 Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Fri, 13 Oct 2023 09:50:46 -0400 Subject: [PATCH 08/24] Speed-up on `MoveWorker` spec (#25528) --- spec/workers/move_worker_spec.rb | 20 ++++++-------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/spec/workers/move_worker_spec.rb b/spec/workers/move_worker_spec.rb index 7577f6e896..efad92c047 100644 --- a/spec/workers/move_worker_spec.rb +++ b/spec/workers/move_worker_spec.rb @@ -67,39 +67,31 @@ describe MoveWorker do end shared_examples 'block and mute handling' do - it 'makes blocks carry over and add a note' do + it 'makes blocks and mutes carry over and adds a note' do subject.perform(source_account.id, target_account.id) + expect(block_service).to have_received(:call).with(blocking_account, target_account) expect(AccountNote.find_by(account: blocking_account, target_account: target_account).comment).to include(source_account.acct) - end - it 'makes mutes carry over and add a note' do - subject.perform(source_account.id, target_account.id) expect(muting_account.muting?(target_account)).to be true expect(AccountNote.find_by(account: muting_account, target_account: target_account).comment).to include(source_account.acct) end end shared_examples 'followers count handling' do - it 'updates the source account followers count' do + it 'updates the source and target account followers counts' do subject.perform(source_account.id, target_account.id) - expect(source_account.reload.followers_count).to eq(source_account.passive_relationships.count) - end - it 'updates the target account followers count' do - subject.perform(source_account.id, target_account.id) + expect(source_account.reload.followers_count).to eq(source_account.passive_relationships.count) expect(target_account.reload.followers_count).to eq(target_account.passive_relationships.count) end end shared_examples 'lists handling' do - it 'puts the new account on the list' do + it 'puts the new account on the list and makes valid lists', sidekiq: :inline do subject.perform(source_account.id, target_account.id) - expect(list.accounts.include?(target_account)).to be true - end - it 'does not create invalid list memberships' do - subject.perform(source_account.id, target_account.id) + expect(list.accounts.include?(target_account)).to be true expect(ListAccount.all).to all be_valid end end From 71cfdd78650b85251b0253acd11e55227f72f506 Mon Sep 17 00:00:00 2001 From: Claire Date: Fri, 13 Oct 2023 16:10:43 +0200 Subject: [PATCH 09/24] Fix duplicate tests (#27395) --- .../api/v1/reports_controller_spec.rb | 63 ------------------- spec/requests/api/v1/reports_spec.rb | 45 +++++-------- 2 files changed, 14 insertions(+), 94 deletions(-) delete mode 100644 spec/controllers/api/v1/reports_controller_spec.rb diff --git a/spec/controllers/api/v1/reports_controller_spec.rb b/spec/controllers/api/v1/reports_controller_spec.rb deleted file mode 100644 index 624ceb213e..0000000000 --- a/spec/controllers/api/v1/reports_controller_spec.rb +++ /dev/null @@ -1,63 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' - -RSpec.describe Api::V1::ReportsController do - render_views - - let(:user) { Fabricate(:user) } - let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) } - - before do - allow(controller).to receive(:doorkeeper_token) { token } - end - - describe 'POST #create' do - let!(:admin) { Fabricate(:user, role: UserRole.find_by(name: 'Admin')) } - - let(:scopes) { 'write:reports' } - let(:status) { Fabricate(:status) } - let(:target_account) { status.account } - let(:category) { nil } - let(:forward) { nil } - let(:rule_ids) { nil } - - before 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 'creates a report', :aggregate_failures do - expect(response).to have_http_status(200) - expect(target_account.targeted_reports).to_not be_empty - expect(target_account.targeted_reports.first.comment).to eq 'reasons' - expect(ActionMailer::Base.deliveries.first.to).to eq([admin.email]) - end - - context 'when a status does not belong to the reported account' do - let(:target_account) { Fabricate(:account) } - - it 'returns http not found' do - expect(response).to have_http_status(404) - end - end - - context 'when a category is chosen' do - let(:category) { 'spam' } - - it 'saves category' do - expect(target_account.targeted_reports.first.spam?).to be true - end - end - - context 'when violated rules are chosen' do - let(:rule) { Fabricate(:rule) } - let(:category) { 'violation' } - let(:rule_ids) { [rule.id] } - - it 'saves category and rule_ids' do - expect(target_account.targeted_reports.first.violation?).to be true - expect(target_account.targeted_reports.first.rule_ids).to contain_exactly(rule.id) - end - end - end -end diff --git a/spec/requests/api/v1/reports_spec.rb b/spec/requests/api/v1/reports_spec.rb index ce74f17c48..ba3d2b3060 100644 --- a/spec/requests/api/v1/reports_spec.rb +++ b/spec/requests/api/v1/reports_spec.rb @@ -33,34 +33,22 @@ RSpec.describe 'Reports' do it_behaves_like 'forbidden for wrong scope', 'read read:reports' - it 'returns http success' do - subject - - expect(response).to have_http_status(200) - end - - it 'returns the created report' do - subject - - expect(body_as_json).to match( - a_hash_including( - status_ids: [status.id.to_s], - category: category, - comment: 'reasons' - ) - ) - end - - it 'creates a report' do - subject - - expect(target_account.targeted_reports).to_not be_empty - end - - it 'sends e-mails to admins' do + it 'creates a report', :aggregate_failures do perform_enqueued_jobs do subject + expect(response).to have_http_status(200) + expect(body_as_json).to match( + a_hash_including( + status_ids: [status.id.to_s], + category: category, + comment: 'reasons' + ) + ) + + expect(target_account.targeted_reports).to_not be_empty + expect(target_account.targeted_reports.first.comment).to eq 'reasons' + expect(ActionMailer::Base.deliveries.first.to).to eq([admin.email]) end end @@ -90,15 +78,10 @@ RSpec.describe 'Reports' do let(:category) { 'violation' } let(:rule_ids) { [rule.id] } - it 'saves category' do + it 'saves category and rule_ids' do subject expect(target_account.targeted_reports.first.violation?).to be true - end - - it 'saves rule_ids' do - subject - expect(target_account.targeted_reports.first.rule_ids).to contain_exactly(rule.id) end end From 1b195ce115bd1572eb883722c55c1db90eb3182b Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Fri, 13 Oct 2023 10:14:23 -0400 Subject: [PATCH 10/24] Speed-up on `Rack::Attack` spec (#25542) --- spec/config/initializers/rack_attack_spec.rb | 48 ++++++++++++++++---- 1 file changed, 38 insertions(+), 10 deletions(-) diff --git a/spec/config/initializers/rack_attack_spec.rb b/spec/config/initializers/rack_attack_spec.rb index 7cd4ac76bb..c9ce9e27d0 100644 --- a/spec/config/initializers/rack_attack_spec.rb +++ b/spec/config/initializers/rack_attack_spec.rb @@ -16,37 +16,63 @@ describe Rack::Attack, type: :request do # https://github.com/rack/rack-attack/blob/v6.6.1/lib/rack/attack/cache.rb#L64-L66 # So we want to minimize `Time.now.to_i % period` - travel_to Time.zone.at((Time.now.to_i / period.seconds).to_i * period.seconds) + travel_to Time.zone.at(counter_prefix * period.seconds) end context 'when the number of requests is lower than the limit' do + before do + below_limit.times { increment_counter } + end + it 'does not change the request status' do - limit.times do - request.call - expect(response).to_not have_http_status(429) - end + expect { request.call }.to change { throttle_count }.by(1) + + expect(response).to_not have_http_status(429) end end context 'when the number of requests is higher than the limit' do + before do + above_limit.times { increment_counter } + end + it 'returns http too many requests after limit and returns to normal status after period' do - (limit * 2).times do |i| - request.call - expect(response).to have_http_status(429) if i > limit - end + expect { request.call }.to change { throttle_count }.by(1) + expect(response).to have_http_status(429) travel period - request.call + expect { request.call }.to change { throttle_count }.by(1) expect(response).to_not have_http_status(429) end end + + def below_limit + limit - 1 + end + + def above_limit + limit * 2 + end + + def throttle_count + described_class.cache.read("#{counter_prefix}:#{throttle}:#{remote_ip}") || 0 + end + + def counter_prefix + (Time.now.to_i / period.seconds).to_i + end + + def increment_counter + described_class.cache.count("#{throttle}:#{remote_ip}", period) + end end let(:remote_ip) { '1.2.3.5' } describe 'throttle excessive sign-up requests by IP address' do context 'when accessed through the website' do + let(:throttle) { 'throttle_sign_up_attempts/ip' } let(:limit) { 25 } let(:period) { 5.minutes } let(:request) { -> { post path, headers: { 'REMOTE_ADDR' => remote_ip } } } @@ -65,6 +91,7 @@ describe Rack::Attack, type: :request do end context 'when accessed through the API' do + let(:throttle) { 'throttle_api_sign_up' } let(:limit) { 5 } let(:period) { 30.minutes } let(:request) { -> { post path, headers: { 'REMOTE_ADDR' => remote_ip } } } @@ -87,6 +114,7 @@ describe Rack::Attack, type: :request do end describe 'throttle excessive sign-in requests by IP address' do + let(:throttle) { 'throttle_login_attempts/ip' } let(:limit) { 25 } let(:period) { 5.minutes } let(:request) { -> { post path, headers: { 'REMOTE_ADDR' => remote_ip } } } From 82beeb2f7ec0eb07a1bdc688d827ef1db5a3427d Mon Sep 17 00:00:00 2001 From: Daniel M Brasil Date: Fri, 13 Oct 2023 11:23:16 -0300 Subject: [PATCH 11/24] Migrate to request specs in `/api/v1/admin/trends/links` (#25739) --- .../v1/admin/trends/links_controller_spec.rb | 52 ------- .../api/v1/admin/trends/links/links_spec.rb | 129 ++++++++++++++++++ 2 files changed, 129 insertions(+), 52 deletions(-) delete mode 100644 spec/controllers/api/v1/admin/trends/links_controller_spec.rb create mode 100644 spec/requests/api/v1/admin/trends/links/links_spec.rb diff --git a/spec/controllers/api/v1/admin/trends/links_controller_spec.rb b/spec/controllers/api/v1/admin/trends/links_controller_spec.rb deleted file mode 100644 index d9aa06824d..0000000000 --- a/spec/controllers/api/v1/admin/trends/links_controller_spec.rb +++ /dev/null @@ -1,52 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' - -describe Api::V1::Admin::Trends::LinksController do - render_views - - let(:role) { UserRole.find_by(name: 'Admin') } - let(:user) { Fabricate(:user, role: role) } - let(:scopes) { 'admin:read admin:write' } - let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) } - let(:account) { Fabricate(:account) } - let(:preview_card) { Fabricate(:preview_card) } - - 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 - - describe 'POST #approve' do - before do - post :approve, params: { id: preview_card.id } - end - - it_behaves_like 'forbidden for wrong scope', 'write:statuses' - it_behaves_like 'forbidden for wrong role', '' - - it 'returns http success' do - expect(response).to have_http_status(200) - end - end - - describe 'POST #reject' do - before do - post :reject, params: { id: preview_card.id } - end - - it_behaves_like 'forbidden for wrong scope', 'write:statuses' - it_behaves_like 'forbidden for wrong role', '' - - it 'returns http success' do - expect(response).to have_http_status(200) - end - end -end diff --git a/spec/requests/api/v1/admin/trends/links/links_spec.rb b/spec/requests/api/v1/admin/trends/links/links_spec.rb new file mode 100644 index 0000000000..05020b0fd0 --- /dev/null +++ b/spec/requests/api/v1/admin/trends/links/links_spec.rb @@ -0,0 +1,129 @@ +# frozen_string_literal: true + +require 'rails_helper' + +describe 'Links' do + let(:role) { UserRole.find_by(name: 'Admin') } + let(:user) { Fabricate(:user, role: role) } + let(:scopes) { 'admin:read admin:write' } + let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) } + let(:headers) { { 'Authorization' => "Bearer #{token.token}" } } + + describe 'GET /api/v1/admin/trends/links' do + subject do + get '/api/v1/admin/trends/links', headers: headers + end + + it 'returns http success' do + subject + + expect(response).to have_http_status(200) + end + end + + describe 'POST /api/v1/admin/trends/links/:id/approve' do + subject do + post "/api/v1/admin/trends/links/#{preview_card.id}/approve", headers: headers + end + + let(:preview_card) { Fabricate(:preview_card, trendable: false) } + + it_behaves_like 'forbidden for wrong scope', 'read write' + it_behaves_like 'forbidden for wrong role', '' + + it 'returns http success' do + subject + + expect(response).to have_http_status(200) + end + + it 'sets the link as trendable' do + expect { subject }.to change { preview_card.reload.trendable }.from(false).to(true) + end + + it 'returns the link data' do + subject + + expect(body_as_json).to match( + a_hash_including( + url: preview_card.url, + title: preview_card.title, + description: preview_card.description, + type: 'link', + requires_review: false + ) + ) + end + + context 'when the link does not exist' do + it 'returns http not found' do + post '/api/v1/admin/trends/links/-1/approve', headers: headers + + expect(response).to have_http_status(404) + end + end + + context 'without an authorization header' do + let(:headers) { {} } + + it 'returns http forbidden' do + subject + + expect(response).to have_http_status(403) + end + end + end + + describe 'POST /api/v1/admin/trends/links/:id/reject' do + subject do + post "/api/v1/admin/trends/links/#{preview_card.id}/reject", headers: headers + end + + let(:preview_card) { Fabricate(:preview_card, trendable: false) } + + it_behaves_like 'forbidden for wrong scope', 'read write' + it_behaves_like 'forbidden for wrong role', '' + + it 'returns http success' do + subject + + expect(response).to have_http_status(200) + end + + it 'does not set the link as trendable' do + expect { subject }.to_not(change { preview_card.reload.trendable }) + end + + it 'returns the link data' do + subject + + expect(body_as_json).to match( + a_hash_including( + url: preview_card.url, + title: preview_card.title, + description: preview_card.description, + type: 'link', + requires_review: false + ) + ) + end + + context 'when the link does not exist' do + it 'returns http not found' do + post '/api/v1/admin/trends/links/-1/reject', headers: headers + + expect(response).to have_http_status(404) + end + end + + context 'without an authorization header' do + let(:headers) { {} } + + it 'returns http forbidden' do + subject + + expect(response).to have_http_status(403) + end + end + end +end From e0da64bb4e0ae84fb9380b6e2bff228c997eea62 Mon Sep 17 00:00:00 2001 From: Renaud Chaput Date: Fri, 13 Oct 2023 19:00:53 +0200 Subject: [PATCH 12/24] Fix empty ENV variables not using default nil value (#27400) --- config/initializers/chewy.rb | 6 +++--- config/initializers/devise.rb | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/config/initializers/chewy.rb b/config/initializers/chewy.rb index 66008a0009..076f383324 100644 --- a/config/initializers/chewy.rb +++ b/config/initializers/chewy.rb @@ -3,9 +3,9 @@ enabled = ENV['ES_ENABLED'] == 'true' host = ENV.fetch('ES_HOST') { 'localhost' } port = ENV.fetch('ES_PORT') { 9200 } -user = ENV.fetch('ES_USER') { nil } -password = ENV.fetch('ES_PASS') { nil } -fallback_prefix = ENV.fetch('REDIS_NAMESPACE') { nil } +user = ENV.fetch('ES_USER', nil).presence +password = ENV.fetch('ES_PASS', nil).presence +fallback_prefix = ENV.fetch('REDIS_NAMESPACE', nil).presence prefix = ENV.fetch('ES_PREFIX') { fallback_prefix } Chewy.settings = { diff --git a/config/initializers/devise.rb b/config/initializers/devise.rb index 7bbaff71f0..41d0ee25b7 100644 --- a/config/initializers/devise.rb +++ b/config/initializers/devise.rb @@ -394,7 +394,7 @@ Devise.setup do |config| config.check_at_sign = true config.pam_default_suffix = ENV.fetch('PAM_EMAIL_DOMAIN') { ENV['LOCAL_DOMAIN'] } config.pam_default_service = ENV.fetch('PAM_DEFAULT_SERVICE') { 'rpam' } - config.pam_controlled_service = ENV.fetch('PAM_CONTROLLED_SERVICE') { nil } + config.pam_controlled_service = ENV.fetch('PAM_CONTROLLED_SERVICE', nil).presence end if ENV['LDAP_ENABLED'] == 'true' From f0ef87b84d7d56a9d59ce721062277eb58e6b3e9 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 16 Oct 2023 11:08:13 +0200 Subject: [PATCH 13/24] Update dependency i18n-tasks to v1.0.13 (#27403) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- Gemfile.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 0068df461b..ce21bc0a20 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -151,7 +151,7 @@ GEM erubi (>= 1.0.0) rack (>= 0.9.0) rouge (>= 1.0.0) - better_html (2.0.1) + better_html (2.0.2) actionview (>= 6.0) activesupport (>= 6.0) ast (~> 2.0) @@ -345,14 +345,14 @@ GEM rainbow (>= 2.0.0) i18n (1.14.1) concurrent-ruby (~> 1.0) - i18n-tasks (1.0.12) + i18n-tasks (1.0.13) activesupport (>= 4.0.2) ast (>= 2.1.0) better_html (>= 1.0, < 3.0) erubi highline (>= 2.0.0) i18n - parser (>= 2.2.3.0) + parser (>= 3.2.2.1) rails-i18n rainbow (>= 2.2.2, < 4.0) terminal-table (>= 1.5.1) @@ -561,7 +561,7 @@ GEM rails-html-sanitizer (1.6.0) loofah (~> 2.21) nokogiri (~> 1.14) - rails-i18n (7.0.7) + rails-i18n (7.0.8) i18n (>= 0.7, < 2) railties (>= 6.0.0, < 8) railties (7.0.8) From a00fc837014da27465695da1c3fd3a86e476937f Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 16 Oct 2023 11:09:20 +0200 Subject: [PATCH 14/24] Update dependency bufferutil to v4.0.8 (#27412) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/yarn.lock b/yarn.lock index b69e7b65f0..5684a034d6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3820,9 +3820,9 @@ buffer@^6.0.3: ieee754 "^1.2.1" bufferutil@^4.0.7: - version "4.0.7" - resolved "https://registry.yarnpkg.com/bufferutil/-/bufferutil-4.0.7.tgz#60c0d19ba2c992dd8273d3f73772ffc894c153ad" - integrity sha512-kukuqc39WOHtdxtw4UScxF/WVnMFVSQVKhtx3AjZJzhd0RGZZldcrfSEbVsWWe6KNH253574cq5F+wpv0G9pJw== + version "4.0.8" + resolved "https://registry.yarnpkg.com/bufferutil/-/bufferutil-4.0.8.tgz#1de6a71092d65d7766c4d8a522b261a6e787e8ea" + integrity sha512-4T53u4PdgsXqKaIctwF8ifXlRTTmEPJ8iEPWFdGZvcf7sbwYo6FKFEX9eNNAnzFZ7EzJAQ3CJeOtCRA4rDp7Pw== dependencies: node-gyp-build "^4.3.0" @@ -8766,9 +8766,9 @@ node-forge@^0.10.0: integrity sha512-PPmu8eEeG9saEUvI97fm4OYxXVB6bFvyNTyiUOBichBpFG8A1Ljw3bY62+5oOjDEMHRnd0Y7HQ+x7uzxOzC6JA== node-gyp-build@^4.3.0: - version "4.6.0" - resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.6.0.tgz#0c52e4cbf54bbd28b709820ef7b6a3c2d6209055" - integrity sha512-NTZVKn9IylLwUzaKjkas1e4u2DLNcV4rdYagA4PWdPwW87Bi7z+BznyKSRwS/761tV/lzCGXplWsiaMjLqP2zQ== + version "4.6.1" + resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.6.1.tgz#24b6d075e5e391b8d5539d98c7fc5c210cac8a3e" + integrity sha512-24vnklJmyRS8ViBNI8KbtK/r/DmXQMRiOMXTNz2nrTnAYUwjmEEbnnpB/+kt+yWRv73bPsSPRFddrcIbAxSiMQ== node-int64@^0.4.0: version "0.4.0" @@ -11473,6 +11473,7 @@ stringz@^2.1.0: char-regex "^1.0.2" "strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1: + name strip-ansi-cjs version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== From 13456efd7b3f95e5312eac350644679ae90d63db Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 16 Oct 2023 11:31:52 +0200 Subject: [PATCH 15/24] New Crowdin Translations (automated) (#27410) Co-authored-by: GitHub Actions --- app/javascript/mastodon/locales/eu.json | 2 +- app/javascript/mastodon/locales/fi.json | 16 ++-- app/javascript/mastodon/locales/ja.json | 2 +- app/javascript/mastodon/locales/sk.json | 2 +- app/javascript/mastodon/locales/zh-TW.json | 6 +- config/locales/devise.zh-TW.yml | 4 +- config/locales/fi.yml | 100 ++++++++++----------- config/locales/si.yml | 14 +-- config/locales/simple_form.fi.yml | 32 +++---- config/locales/simple_form.zh-TW.yml | 10 +-- config/locales/sk.yml | 1 + config/locales/zh-TW.yml | 28 +++--- 12 files changed, 107 insertions(+), 110 deletions(-) diff --git a/app/javascript/mastodon/locales/eu.json b/app/javascript/mastodon/locales/eu.json index b674f90c84..5b4fef59c0 100644 --- a/app/javascript/mastodon/locales/eu.json +++ b/app/javascript/mastodon/locales/eu.json @@ -613,7 +613,7 @@ "sign_in_banner.create_account": "Sortu kontua", "sign_in_banner.sign_in": "Hasi saioa", "sign_in_banner.sso_redirect": "Hasi saioa edo izena eman", - "sign_in_banner.text": "Hasi saioa profilak edo traolak jarraitzeko, bidalketak gogokoetara gehitzeko, partekatzeko edo erantzuteko. Zure kontutik ere komunika zaitezke beste zerbitzari ezberdin vatean.", + "sign_in_banner.text": "Hasi saioa profilak edo traolak jarraitzeko, bidalketak gogokoetara gehitzeko, partekatzeko edo erantzuteko. Zure kontutik ere komunika zaitezke beste zerbitzari ezberdin batean.", "status.admin_account": "Ireki @{name} erabiltzailearen moderazio interfazea", "status.admin_domain": "{domain}-(r)en moderazio-interfazea ireki", "status.admin_status": "Ireki bidalketa hau moderazio interfazean", diff --git a/app/javascript/mastodon/locales/fi.json b/app/javascript/mastodon/locales/fi.json index 91be7bfc0e..a7a68533d6 100644 --- a/app/javascript/mastodon/locales/fi.json +++ b/app/javascript/mastodon/locales/fi.json @@ -71,8 +71,8 @@ "account.unmute_notifications_short": "Poista ilmoitusten mykistys", "account.unmute_short": "Poista mykistys", "account_note.placeholder": "Lisรครค muistiinpano napsauttamalla", - "admin.dashboard.daily_retention": "Kรคyttรคjรคn sรคilyminen rekisterรถitymisen jรคlkeiseen pรคivรครคn mennessรค", - "admin.dashboard.monthly_retention": "Kรคyttรคjรคn sรคilyminen rekisterรถitymisen jรคlkeiseen kuukauteen mennessรค", + "admin.dashboard.daily_retention": "Kรคyttรคjรคn pysyminen rekisterรถitymisen jรคlkeiseen pรคivรครคn mennessรค", + "admin.dashboard.monthly_retention": "Kรคyttรคjรคn pysyminen rekisterรถitymisen jรคlkeiseen kuukauteen mennessรค", "admin.dashboard.retention.average": "Keskimรครคrin", "admin.dashboard.retention.cohort": "Kirjautumiset", "admin.dashboard.retention.cohort_size": "Uudet kรคyttรคjรคt", @@ -101,7 +101,7 @@ "bundle_modal_error.close": "Sulje", "bundle_modal_error.message": "Jotain meni pieleen komponenttia ladattaessa.", "bundle_modal_error.retry": "Yritรค uudelleen", - "closed_registrations.other_server_instructions": "Koska Mastodon on hajautettu, voit luoda tilin toiselle palvelimelle ja silti olla vuorovaikutuksessa tรคmรคn kanssa.", + "closed_registrations.other_server_instructions": "Koska Mastodon on hajautettu, voit luoda tilin toiselle palvelimelle ja olla silti vuorovaikutuksessa tรคmรคn kanssa.", "closed_registrations_modal.description": "Tilin luonti palveluun {domain} ei tรคllรค hetkellรค ole mahdollista, mutta huomioi, ettei Mastodonin kรคyttรถ edellytรค juuri kyseisen palvelun tiliรค.", "closed_registrations_modal.find_another_server": "Etsi toinen palvelin", "closed_registrations_modal.preamble": "Mastodon on hajautettu, joten riippumatta siitรค, missรค luot tilisi, voit seurata ja olla vuorovaikutuksessa kenen tahansa kanssa tรคllรค palvelimella. Voit jopa isรคnnรถidรค palvelinta!", @@ -154,9 +154,9 @@ "compose_form.publish_form": "Uusi julkaisu", "compose_form.publish_loud": "{publish}!", "compose_form.save_changes": "Tallenna muutokset", - "compose_form.sensitive.hide": "{count, plural, one {Merkitse media arkaluontoiseksi} other {Merkitse mediat arkaluontoiseksi}}", - "compose_form.sensitive.marked": "{count, plural, one {Media on merkitty arkaluontoiseksi} other {Mediat on merkitty arkaluontoiseksi}}", - "compose_form.sensitive.unmarked": "{count, plural, one {Mediaa ei ole merkitty arkaluontoiseksi} other {Medioja ei ole merkitty arkaluontoiseksi}}", + "compose_form.sensitive.hide": "{count, plural, one {Merkitse media arkaluonteiseksi} other {Merkitse mediat arkaluonteisiksi}}", + "compose_form.sensitive.marked": "{count, plural, one {Media on merkitty arkaluonteiseksi} other {Mediat on merkitty arkaluonteisiksi}}", + "compose_form.sensitive.unmarked": "{count, plural, one {Mediaa ei ole merkitty arkaluonteiseksi} other {Medioita ei ole merkitty arkaluonteisiksi}}", "compose_form.spoiler.marked": "Poista sisรคltรถvaroitus", "compose_form.spoiler.unmarked": "Lisรครค sisรคltรถvaroitus", "compose_form.spoiler_placeholder": "Kirjoita varoituksesi tรคhรคn", @@ -592,7 +592,7 @@ "search.search_or_paste": "Hae tai kirjoita URL-osoite", "search_popout.full_text_search_disabled_message": "Ei saatavilla palvelimella {domain}.", "search_popout.language_code": "ISO-kielikoodi", - "search_popout.options": "Haun asetukset", + "search_popout.options": "Hakuvalinnat", "search_popout.quick_actions": "Pikatoiminnot", "search_popout.recent": "Viimeaikaiset haut", "search_popout.specific_date": "tietty pรคivรคmรครคrรค", @@ -686,7 +686,7 @@ "timeline_hint.resources.followers": "Seuraajat", "timeline_hint.resources.follows": "seurattua", "timeline_hint.resources.statuses": "Vanhemmat julkaisut", - "trends.counter_by_accounts": "{count, plural, one {{counter} henkilรถ} other {{counter} henkilรถรค}} {days, plural, one {viimeisen pรคivรคn} other {viimeisten {days} pรคivรคn}} aikana", + "trends.counter_by_accounts": "{count, plural, one {{counter} henkilรถ} other {{counter} henkilรถรค}} {days, plural, one {viime pรคivรคnรค} other {viimeisenรค {days} pรคivรคnรค}}", "trends.trending_now": "Suosittua nyt", "ui.beforeunload": "Luonnos hรคviรครค, jos poistut Mastodonista.", "units.short.billion": "{count} mrd.", diff --git a/app/javascript/mastodon/locales/ja.json b/app/javascript/mastodon/locales/ja.json index 0c8e417b91..c5817c199e 100644 --- a/app/javascript/mastodon/locales/ja.json +++ b/app/javascript/mastodon/locales/ja.json @@ -47,7 +47,7 @@ "account.locked_info": "ใ“ใฎใ‚ขใ‚ซใ‚ฆใƒณใƒˆใฏๆ‰ฟ่ชๅˆถใ‚ขใ‚ซใ‚ฆใƒณใƒˆใงใ™ใ€‚็›ธๆ‰‹ใŒๆ‰ฟ่ชใ™ใ‚‹ใพใงใƒ•ใ‚ฉใƒญใƒผใฏๅฎŒไบ†ใ—ใพใ›ใ‚“ใ€‚", "account.media": "ใƒกใƒ‡ใ‚ฃใ‚ข", "account.mention": "@{name}ใ•ใ‚“ใซใƒกใƒณใ‚ทใƒงใƒณ", - "account.moved_to": "{name}ใ•ใ‚“ใŒใ‚ขใ‚ซใ‚ฆใƒณใƒˆใ‚’ๅผ•ใฃ่ถŠใ—ใพใ—ใŸ:", + "account.moved_to": "{name}ใ•ใ‚“ใฏใ“ใกใ‚‰ใฎใ‚ขใ‚ซใ‚ฆใƒณใƒˆใซๅผ•ใฃ่ถŠใ—ใพใ—ใŸ:", "account.mute": "@{name}ใ•ใ‚“ใ‚’ใƒŸใƒฅใƒผใƒˆ", "account.mute_notifications_short": "้€š็Ÿฅใ‚’ใ‚ชใƒ•ใซใ™ใ‚‹", "account.mute_short": "ใƒŸใƒฅใƒผใƒˆ", diff --git a/app/javascript/mastodon/locales/sk.json b/app/javascript/mastodon/locales/sk.json index da12eaa1b5..60760607f2 100644 --- a/app/javascript/mastodon/locales/sk.json +++ b/app/javascript/mastodon/locales/sk.json @@ -61,7 +61,7 @@ "account.requested_follow": "{name} ti poslal ลพiadosลฅ na sledovanie", "account.share": "Zdieฤพaj @{name} profil", "account.show_reblogs": "Ukรกลพ vyzdvihnutia od @{name}", - "account.statuses_counter": "{count, plural, one {{counter} Toot} other {{counter} Toots}}", + "account.statuses_counter": "{count, plural, one {{counter} prรญspevok} other {{counter} prรญspevkov}}", "account.unblock": "Odblokuj @{name}", "account.unblock_domain": "Prestaลˆ skrรฝvaลฅ {domain}", "account.unblock_short": "Odblokuj", diff --git a/app/javascript/mastodon/locales/zh-TW.json b/app/javascript/mastodon/locales/zh-TW.json index 27fd0ad35e..b1cd0ca753 100644 --- a/app/javascript/mastodon/locales/zh-TW.json +++ b/app/javascript/mastodon/locales/zh-TW.json @@ -204,7 +204,7 @@ "dismissable_banner.explore_links": "้€™ไบ›ๆ–ฐ่žๆ•…ไบ‹ๆญฃๅœจ่ขซๆญคไผบๆœๅ™จไปฅๅŠๅŽปไธญๅฟƒๅŒ–็ถฒ่ทฏไธŠ็š„ไบบๅ€‘็†ฑ็ƒˆ่จŽ่ซ–่‘—ใ€‚่ถŠๅคšไธๅŒไบบๆ‰€ๅ˜Ÿๅ‡บ็š„ๆ–ฐ่žๆŽ’ๅๆ›ด้ซ˜ใ€‚", "dismissable_banner.explore_statuses": "้€™ไบ›ๆ–ผๆญคไผบๆœๅ™จไปฅๅŠๅŽปไธญๅฟƒๅŒ–็ถฒ่ทฏไธญๅ…ถไป–ไผบๆœๅ™จ็™ผๅ‡บ็š„ๅ˜Ÿๆ–‡ๆญฃๅœจ่ขซๆญคไผบๆœๅ™จไธŠ็š„ไบบๅ€‘็†ฑ็ƒˆ่จŽ่ซ–่‘—ใ€‚่ถŠๅคšไธๅŒไบบ่ฝ‰ๅ˜ŸๅŠๆœ€ๆ„›ๆŽ’ๅๆ›ด้ซ˜ใ€‚", "dismissable_banner.explore_tags": "้€™ไบ›ไธป้กŒๆจ™็ฑคๆญฃๅœจ่ขซๆญคไผบๆœๅ™จไปฅๅŠๅŽปไธญๅฟƒๅŒ–็ถฒ่ทฏไธŠ็š„ไบบๅ€‘็†ฑ็ƒˆ่จŽ่ซ–่‘—ใ€‚่ถŠๅคšไธๅŒไบบๆ‰€ๅ˜Ÿๅ‡บ็š„ไธป้กŒๆจ™็ฑคๆŽ’ๅๆ›ด้ซ˜ใ€‚", - "dismissable_banner.public_timeline": "้€™ไบ›ๆ˜ฏไพ†่‡ช {domain} ไธŠไบบๅ€‘ๆ–ผ็คพ็พค็ถฒ็ซ™ไธญ่ทŸ้šจ่€…ๆ‰€็™ผ่กจไน‹ๆœ€่ฟ‘ๅ…ฌ้–‹ๅ˜Ÿๆ–‡ใ€‚", + "dismissable_banner.public_timeline": "้€™ไบ›ๆ˜ฏไพ†่‡ช {domain} ไฝฟ็”จ่€…ๅ€‘่ทŸ้šจไธญๅธณ่™Ÿๆ‰€็™ผ่กจไน‹ๆœ€ๆ–ฐๅ…ฌ้–‹ๅ˜Ÿๆ–‡ใ€‚", "embed.instructions": "่ฆๅœจๆ‚จ็š„็ถฒ็ซ™ๅตŒๅ…ฅๆญคๅ˜Ÿๆ–‡๏ผŒ่ซ‹่ค‡่ฃฝไปฅไธ‹็จ‹ๅผ็ขผใ€‚", "embed.preview": "ๅฎƒๅฐ‡้กฏ็คบๆˆ้€™ๆจฃ๏ผš", "emoji_button.activity": "ๆดปๅ‹•", @@ -271,8 +271,8 @@ "filter_modal.select_filter.title": "้Žๆฟพๆญคๅ˜Ÿๆ–‡", "filter_modal.title.status": "้Žๆฟพไธ€ๅ‰‡ๅ˜Ÿๆ–‡", "firehose.all": "ๅ…จ้ƒจ", - "firehose.local": "ๆญคไผบๆœๅ™จ", - "firehose.remote": "ๅ…ถไป–ไผบๆœๅ™จ", + "firehose.local": "ๆœฌ็ซ™", + "firehose.remote": "่ฏ้‚ฆๅฎ‡ๅฎ™", "follow_request.authorize": "ๆŽˆๆฌŠ", "follow_request.reject": "ๆ‹’็ต•", "follow_requests.unlocked_explanation": "ๅณไพฟๆ‚จ็š„ๅธณ่™Ÿๆœช่ขซ้Ž–ๅฎš๏ผŒ{domain} ็š„็ฎก็†ๅ“ก่ช็‚บๆ‚จๅฏ่ƒฝๆƒณ่ฆ่‡ชๅทฑๅฏฉๆ ธ้€™ไบ›ๅธณ่™Ÿ็š„่ทŸ้šจ่ซ‹ๆฑ‚ใ€‚", diff --git a/config/locales/devise.zh-TW.yml b/config/locales/devise.zh-TW.yml index ea1813e413..c01beb796b 100644 --- a/config/locales/devise.zh-TW.yml +++ b/config/locales/devise.zh-TW.yml @@ -4,7 +4,7 @@ zh-TW: confirmations: confirmed: ๆ‚จ็š„้›ปๅญ้ƒตไปถๅœฐๅ€ๅทฒ็ขบ่ชๆˆๅŠŸใ€‚ send_instructions: ๅนพๅˆ†้˜ๅพŒๆ‚จๅฐ‡ๆ”ถๅˆฐ็ขบ่ชไฟกไปถใ€‚่‹ฅๆœชๆ”ถๅˆฐๆญคไฟกไปถ๏ผŒ่ซ‹ๆชขๆŸฅๅžƒๅœพ้ƒตไปถ่ณ‡ๆ–™ๅคพใ€‚ - send_paranoid_instructions: ๅฆ‚ๆžœๆ‚จ็š„้›ปๅญ้ƒตไปถๅญ˜ๅœจๆ–ผๆˆ‘ๅ€‘็š„่ณ‡ๆ–™ๅบซ๏ผŒๆ‚จๅฐ‡ๆœƒๅœจๅนพๅˆ†้˜ๅ…งๆ”ถๅˆฐ็ขบ่ชไฟกใ€‚่‹ฅๆœชๆ”ถๅˆฐ่ซ‹ๆชขๆŸฅๅžƒๅœพ้ƒตไปถ่ณ‡ๆ–™ๅคพใ€‚ + send_paranoid_instructions: ๅฆ‚ๆžœๆ‚จ็š„้›ปๅญ้ƒตไปถๅญ˜ๅœจๆ–ผๆˆ‘ๅ€‘็š„่ณ‡ๆ–™ๅบซ๏ผŒๆ‚จๅฐ‡ๆ–ผๅนพๅˆ†้˜ๅ…งๆ”ถๅˆฐ็ขบ่ชไฟกใ€‚่‹ฅๆœชๆ”ถๅˆฐ่ซ‹ๆชขๆŸฅๅžƒๅœพ้ƒตไปถ่ณ‡ๆ–™ๅคพใ€‚ failure: already_authenticated: ๆ‚จๅทฒ็™ปๅ…ฅใ€‚ inactive: ๆ‚จ็š„ๅธณ่™Ÿๅฐšๆœชๅ•Ÿ็”จใ€‚ @@ -43,7 +43,7 @@ zh-TW: reset_password_instructions: action: ่ฎŠๆ›ดๅฏ†็ขผ explanation: ๆ‚จๅทฒ่ซ‹ๆฑ‚ๅธณ่™Ÿ็š„ๆ–ฐๅฏ†็ขผใ€‚ - extra: ่‹ฅๆ‚จไธฆๆœช่ซ‹ๆฑ‚๏ผŒ่ซ‹ๅฟฝ็•ฅๆญคไฟกไปถใ€‚ๆ‚จ็š„ๅฏ†็ขผๅœจๅญ˜ๅ–ไธŠๆ–น้€ฃ็ตไธฆๅปบ็ซ‹ๆ–ฐๅฏ†็ขผๅ‰ไธๆœƒ่ฎŠๆ›ดใ€‚ + extra: ่‹ฅๆ‚จไธฆๆœช่ซ‹ๆฑ‚๏ผŒ่ซ‹ๅฟฝ็•ฅๆญคไฟกไปถใ€‚ๆ‚จ็š„ๅฏ†็ขผๆ–ผๅญ˜ๅ–ไธŠๆ–น้€ฃ็ตไธฆๅปบ็ซ‹ๆ–ฐๅฏ†็ขผๅ‰ไธๆœƒ่ฎŠๆ›ดใ€‚ subject: Mastodon๏ผš้‡่จญๅฏ†็ขผๆŒ‡ๅผ• title: ้‡่จญๅฏ†็ขผ two_factor_disabled: diff --git a/config/locales/fi.yml b/config/locales/fi.yml index 0c36b795c9..cdc5d12678 100644 --- a/config/locales/fi.yml +++ b/config/locales/fi.yml @@ -12,7 +12,7 @@ fi: one: seuraaja other: seuraajaa following: seurattu(a) - instance_actor_flash: Tรคmรค tili on virtuaalinen toimija, jota kรคytetรครคn edustamaan itse palvelinta eikรค yksittรคistรค kรคyttรคjรครค. Sitรค kรคytetรครคn federointitarkoituksiin, eikรค sitรค tule jรครคdyttรครค. + instance_actor_flash: Tรคmรค tili on virtuaalinen toimija, jota kรคytetรครคn edustamaan itse palvelinta eikรค yksittรคistรค kรคyttรคjรครค. Sitรค kรคytetรครคn liittoutumistarkoituksiin, eikรค sitรค tule jรครคdyttรครค. last_active: viimeksi aktiivinen link_verified_on: Tรคmรคn linkin omistus on tarkastettu %{date} nothing_here: Tรครคllรค ei ole mitรครคn! @@ -138,7 +138,7 @@ fi: security_measures: only_password: Vain salasana password_and_2fa: Salasana ja kaksivaiheinen tunnistautuminen - sensitive: Pakotus arkaluonteiseksi + sensitive: Pakota arkaluonteiseksi sensitized: Merkitty arkaluonteiseksi shared_inbox_url: Jaetun saapuvan postilaatikon osoite show: @@ -157,7 +157,7 @@ fi: unblock_email: Poista sรคhkรถpostiosoitteen esto unblocked_email_msg: Kรคyttรคjรคn %{username} sรคhkรถpostiosoitteen esto kumottiin unconfirmed_email: Sรคhkรถpostia ei vahvistettu - undo_sensitized: Kumoa pakotus arkaluonteiseksi tiliksi + undo_sensitized: Kumoa pakotus arkaluonteiseksi undo_silenced: Kumoa rajoitus undo_suspension: Peru jรครคhy unsilenced_msg: Tilin %{username} rajoituksen kumoaminen onnistui @@ -167,7 +167,7 @@ fi: view_domain: Nรคytรค verkkotunnuksen yhteenveto warn: Varoita web: Verkko - whitelisted: Sallittu federoimaan + whitelisted: Sallittu liittoutua action_logs: action_types: approve_appeal: Hyvรคksy valitus @@ -214,12 +214,12 @@ fi: resend_user: Lรคhetรค vahvistusviesti uudelleen reset_password_user: Nollaa salasana resolve_report: Selvitรค raportti - sensitive_account: Pakotus arkaluontoiseksi tiliksi + sensitive_account: Pakotus arkaluonteiseksi tiliksi silence_account: Rajoita tiliรค suspend_account: Jรครคdytรค tili unassigned_report: Peruuta raportin mรครคritys unblock_email_account: Poista sรคhkรถpostiosoitteen esto - unsensitive_account: Kumoa pakotus arkaluontoiseksi tiliksi + unsensitive_account: Kumoa pakotus arkaluonteiseksi tiliksi unsilence_account: Kumoa tilin rajoitus unsuspend_account: Kumoa tilin jรครคdytys update_announcement: Pรคivitรค tiedote @@ -239,7 +239,7 @@ fi: create_announcement_html: "%{name} loi uuden tiedotteen %{target}" create_canonical_email_block_html: "%{name} esti sรคhkรถpostin tiivisteellรค %{target}" create_custom_emoji_html: "%{name} lรคhetti uuden emojin %{target}" - create_domain_allow_html: "%{name} salli federoinnin verkkotunnuksen %{target} kanssa" + create_domain_allow_html: "%{name} salli liittoutumisen verkkotunnuksen %{target} kanssa" create_domain_block_html: "%{name} esti verkkotunnuksen %{target}" create_email_domain_block_html: "%{name} esti sรคhkรถpostiverkkotunnuksen %{target}" create_ip_block_html: "%{name} loi IP-sรครคnnรถn %{target}" @@ -249,7 +249,7 @@ fi: destroy_announcement_html: "%{name} poisti tiedotteen %{target}" destroy_canonical_email_block_html: "%{name} poisti sรคhkรถpostin eston tiivisteellรค %{target}" destroy_custom_emoji_html: "%{name} poisti emojin %{target}" - destroy_domain_allow_html: "%{name} kielsi federoinnin verkkotunnuksen %{target} kanssa" + destroy_domain_allow_html: "%{name} kielsi liittoutumisen verkkotunnuksen %{target} kanssa" destroy_domain_block_html: "%{name} poisti verkkotunnuksen %{target} eston" destroy_email_domain_block_html: "%{name} poisti sรคhkรถpostiverkkotunnuksen %{target} eston" destroy_instance_html: "%{name} tyhjensi verkkotunnuksen %{target}" @@ -278,7 +278,7 @@ fi: suspend_account_html: "%{name} jรครคdytti kรคyttรคjรคn %{target} tilin" unassigned_report_html: "%{name} peruutti raportin mรครคrityksen %{target}" unblock_email_account_html: "%{name} poisti kรคyttรคjรคn %{target} sรคhkรถpostiosoitteen eston" - unsensitive_account_html: "%{name} poisti kรคyttรคjรคn %{target} median arkaluonteisen merkinnรคn" + unsensitive_account_html: "%{name} kumosi kรคyttรคjรคn %{target} median arkaluonteisuusmerkinnรคn" unsilence_account_html: "%{name} kumosi kรคyttรคjรคn %{target} rajoituksen" unsuspend_account_html: "%{name} kumosi kรคyttรคjรคn %{target} tilin jรครคdytyksen" update_announcement_html: "%{name} pรคivitti tiedotteen %{target}" @@ -375,12 +375,12 @@ fi: empty: Valituksia ei lรถytynyt. title: Valitukset domain_allows: - add_new: Salli liitto verkkotunnuksella - created_msg: Verkkotunnus on onnistuneesti sallittu federoinnille - destroyed_msg: Verkkotunnusta on kielletty federoimasta + add_new: Salli liittoutuminen tรคmรคn verkkotunnuksen kanssa + created_msg: Verkkotunnuksen on onnistuneesti sallittu liittoutua + destroyed_msg: Verkkotunnusta on kielletty liittoutumasta export: Vie import: Tuo - undo: Estรค liitto verkkotunnukselle + undo: Kiellรค liittoutuminen tรคmรคn verkkotunnuksen kanssa domain_blocks: add_new: Lisรครค uusi verkkotunnuksen esto confirm_suspension: @@ -527,7 +527,7 @@ fi: public_comment: Julkinen kommentti purge: Tyhjennรค purge_description_html: Jos uskot, ettรค tรคmรค verkkotunnus on offline-tilassa tarkoituksella, voit poistaa kaikki verkkotunnuksen tilitietueet ja niihin liittyvรคt tiedot tallennustilastasi. Tรคmรค voi kestรครค jonkin aikaa. - title: Federointi + title: Liittoutuminen total_blocked_by_us: Estรคmรคmme total_followed_by_them: Heidรคn seuraama total_followed_by_us: Meidรคn seuraama @@ -562,7 +562,7 @@ fi: relays: add_new: Lisรครค uusi vรคlittรคjรค delete: Poista - description_html: "Federointivรคlittรคjรค on vรคlityspalvelin, joka siirtรครค suuria mรครคriรค julkisia julkaisuja siihen liittyneiden palvelinten vรคlillรค. Se voi auttaa pieniรค ja keskisuuria palvelimia lรถytรคmรครคn fediversumin sisรคltรถรค, mikรค muutoin vaatisi paikallisia kรคyttรคjiรค seuraamaan etรคpalvalinten kรคyttรคjiรค manuaalisesti." + description_html: "Liittoutumisvรคlittรคjรค on vรคlityspalvelin, joka siirtรครค suuria mรครคriรค julkisia julkaisuja siihen liittyneiden palvelinten vรคlillรค. Se voi auttaa pieniรค ja keskisuuria palvelimia lรถytรคmรครคn fediversumin sisรคltรถรค, mikรค muutoin vaatisi paikallisia kรคyttรคjiรค seuraamaan etรคpalvalinten kรคyttรคjiรค manuaalisesti." disable: Poista kรคytรถstรค disabled: Poissa kรคytรถstรค enable: Ota kรคyttรถรถn @@ -671,56 +671,56 @@ fi: devops: DevOps invites: Kutsut moderation: Valvonta - special: Erikois + special: Erityistรค delete: Poista description_html: "Kรคyttรคjรคrooleilla voit muokata, mihin toimintoihin ja alueisiin kรคyttรคjรคsi pรครคsevรคt kรคsiksi." - edit: Muokkaa "%{name}" roolia - everyone: Oletus kรคyttรถoikeudet - everyone_full_description_html: Tรคmรค on perusrooli joka vaikuttaa kaikkiin kรคyttรคjiin, jopa ilman mรครคrรคttyรค roolia. Kaikki muut roolit perivรคt sen kรคyttรถoikeudet. + edit: Muokkaa roolia โ€%{name}โ€ + everyone: Oletuskรคyttรถoikeudet + everyone_full_description_html: Tรคmรค on perusrooli, joka vaikuttaa kaikkiin kรคyttรคjiin, jopa ilman mรครคrรคttyรค roolia. Kaikki muut roolit perivรคt sen kรคyttรถoikeudet. permissions_count: one: "%{count} kรคyttรถoikeus" other: "%{count} kรคyttรถoikeutta" privileges: administrator: Yllรคpitรคjรค administrator_description: Kรคyttรคjรคt, joilla on tรคmรค kรคyttรถoikeus, ohittavat jokaisen kรคyttรถoikeuden - delete_user_data: Poista kรคyttรคjรคn tiedot + delete_user_data: Poistaa kรคyttรคjรคn tiedot delete_user_data_description: Salli kรคyttรคjien poistaa muiden kรคyttรคjien tiedot viipymรคttรค - invite_users: Kutsu kรคyttรคjiรค + invite_users: Kutsua kรคyttรคjiรค invite_users_description: Sallii kรคyttรคjien kutsua uusia ihmisiรค palvelimelle - manage_announcements: Hallitse tiedotteita + manage_announcements: Hallita tiedotteita manage_announcements_description: Sallii kรคyttรคjien hallita tiedotteita palvelimella - manage_appeals: Hallitse valituksia + manage_appeals: Hallita valituksia manage_appeals_description: Sallii kรคyttรคjien tarkistaa valvontatoimia koskevia valituksia - manage_blocks: Hallitse estoja + manage_blocks: Hallita estoja manage_blocks_description: Sallii kรคyttรคjien estรครค sรคhkรถpostipalveluntarjoajia ja IP-osoitteita - manage_custom_emojis: Hallitse mukautettuja emojeita + manage_custom_emojis: Hallita mukautettuja emojeita manage_custom_emojis_description: Sallii kรคyttรคjien hallita mukautettuja emojeita palvelimella - manage_federation: Hallitse federointia - manage_federation_description: Sallii kรคyttรคjien estรครค tai sallia federointi muiden verkkotunnusten kanssa ja hallita toimitusta - manage_invites: Hallitse kutsuja + manage_federation: Hallita liittoutumista + manage_federation_description: Sallii kรคyttรคjien estรครค tai sallia liittoutuminen muiden verkkotunnusten kanssa ja hallita toimitusta + manage_invites: Hallita kutsuja manage_invites_description: Sallii kรคyttรคjien selata ja poistaa kutsulinkkejรค kรคytรถstรค - manage_reports: Hallitse raportteja + manage_reports: Hallita raportteja manage_reports_description: Sallii kรคyttรคjien tarkistaa raportteja ja suorittaa valvontatoimia niitรค vastaan - manage_roles: Hallitse rooleja + manage_roles: Hallita rooleja manage_roles_description: Sallii kรคyttรคjien hallita ja mรครคrittรครค rooleja heidรคn alapuolellaan - manage_rules: Hallitse sรครคntรถjรค + manage_rules: Hallita sรครคntรถjรค manage_rules_description: Sallii kรคyttรคjien muuttaa palvelimen sรครคntรถjรค - manage_settings: Hallitse asetuksia + manage_settings: Hallita asetuksia manage_settings_description: Sallii kรคyttรคjien muuttaa sivuston asetuksia - manage_taxonomies: Hallitse luokittelua + manage_taxonomies: Hallita luokittelua manage_taxonomies_description: Sallii kรคyttรคjien tarkistaa suositun sisรคllรถn ja pรคivittรครค aihetunnisteiden asetuksia - manage_user_access: Hallitse kรคyttรคjรคoikeuksia + manage_user_access: Hallita kรคyttรคjรคoikeuksia manage_user_access_description: Sallii kรคyttรคjien poistaa muiden kรคyttรคjien kaksivaiheinen todennus kรคytรถstรค, vaihtaa heidรคn sรคhkรถpostiosoitteensa ja nollata heidรคn salasanansa - manage_users: Hallitse kรคyttรคjiรค + manage_users: Hallita kรคyttรคjiรค manage_users_description: Sallii kรคyttรคjien tarkastella muiden kรคyttรคjien tietoja ja suorittaa valvontatoimia heitรค kohtaan - manage_webhooks: Hallitse webhookeja + manage_webhooks: Hallita webhookeja manage_webhooks_description: Sallii kรคyttรคjien luoda webhookeja hallinnollisiin tapahtumiin view_audit_log: Katsoa valvontalokia view_audit_log_description: Sallii kรคyttรคjien nรคhdรค palvelimen hallinnollisten toimien historian - view_dashboard: Nรคytรค koontinรคyttรถ + view_dashboard: Katsoa koontinรคyttรถรค view_dashboard_description: Sallii kรคyttรคjien kรคyttรครค kojelautaa ja erilaisia mittareita view_devops: DevOps - view_devops_description: Sallii kรคyttรคjille oikeuden kรคyttรครค Sidekiq ja pgHero dashboardeja + view_devops_description: Sallii kรคyttรคjille pรครคsyn Sidekiq- ja pgHero-hallintapaneeleihin title: Roolit rules: add_new: Lisรครค sรครคntรถ @@ -732,7 +732,7 @@ fi: settings: about: manage_rules: Hallitse palvelimen sรครคntรถjรค - preamble: Anna perusteellista tietoa siitรค, miten palvelinta kรคytetรครคn, valvotaan, rahoitetaan. + preamble: Anna perusteellista tietoa siitรค, miten palvelinta kรคytetรครคn, valvotaan ja rahoitetaan. rules_hint: On olemassa erityinen alue sรครคntรถjรค, joita kรคyttรคjien odotetaan noudattavan. title: Tietoja appearance: @@ -752,7 +752,7 @@ fi: title: Jรคtรค kรคyttรคjรคt oletusarvoisesti hakukoneindeksoinnin ulkopuolelle discovery: follow_recommendations: Seuraamissuositukset - preamble: Mielenkiintoisen sisรคllรถn esille tuominen auttaa saamaan uusia kรคyttรคjiรค, jotka eivรคt ehkรค tunne ketรครคn Mastodonista. Mรครคrittele, kuinka erilaiset etsintรคominaisuudet toimivat palvelimellasi. + preamble: Mielenkiintoisen sisรคllรถn esille tuominen auttaa saamaan uusia kรคyttรคjiรค, jotka eivรคt ehkรค tunne ketรครคn Mastodonista. Mรครคrittele, kuinka erilaiset lรถytรคmisominaisuudet toimivat palvelimellasi. profile_directory: Profiilihakemisto public_timelines: Julkiset aikajanat publish_discovered_servers: Julkaise lรถydetyt palvelimet @@ -765,17 +765,17 @@ fi: users: Kirjautuneille paikallisille kรคyttรคjille registrations: preamble: Mรครคritรค, kuka voi luoda tilin palvelimellesi. - title: Rekisterรถinnit + title: Rekisterรถityminen registrations_mode: modes: approved: Rekisterรถinti vaatii hyvรคksynnรคn none: Kukaan ei voi rekisterรถityรค open: Kaikki voivat rekisterรถityรค security: - authorized_fetch: Vaadi todennus yhdistetyiltรค palvelimilta - authorized_fetch_hint: Todennuksen vaatiminen federoiduilta palvelimilta mahdollistaa sekรค kรคyttรคjรค- ettรค palvelintason estojen tiukemman valvonnan. Tรคmรค tapahtuu kuitenkin suorituskyvyn kustannuksella, vรคhentรครค vastauksiesi tavoittavuutta ja voi aiheuttaa yhteensopivuusongelmia joidenkin federoitujen palvelujen kanssa. Tรคmรค ei myรถskรครคn estรค omistautuneita toimijoita hakemasta julkisia julkaisujasi ja tilejรคsi. + authorized_fetch: Vaadi todennus liittoutuvilta palvelimilta + authorized_fetch_hint: Todennuksen vaatiminen liittoutuvilta palvelimilta mahdollistaa sekรค kรคyttรคjรค- ettรค palvelintason estojen tiukemman valvonnan. Tรคmรค tapahtuu kuitenkin suorituskyvyn kustannuksella, vรคhentรครค vastauksiesi tavoittavuutta ja voi aiheuttaa yhteensopivuusongelmia joidenkin liittoutuvien palvelujen kanssa. Tรคmรค ei myรถskรครคn estรค omistautuneita toimijoita hakemasta julkisia julkaisujasi ja tilejรคsi. authorized_fetch_overridden_hint: Et voi tรคllรค hetkellรค muuttaa tรคtรค asetusta, koska se on ohitettu ympรคristรถmuuttujalla. - federation_authentication: Yhdistettyjen palvelinten todentamisen tรคytรคntรถรถnpano + federation_authentication: Liittoutumisen todentamisen tรคytรคntรถรถnpano title: Palvelimen asetukset site_uploads: delete: Poista ladattu tiedosto @@ -1239,7 +1239,7 @@ fi: deprecated_api_multiple_keywords: Nรคitรค parametreja ei voi muuttaa tรคstรค sovelluksesta, koska ne koskevat useampaa kuin yhtรค suodattimen avainsanaa. Kรคytรค uudempaa sovellusta tai selainkรคyttรถliittymรครค. invalid_context: Ei sisรคltรถรค tai se on virheellinen index: - contexts: Suodattimet %{contexts} + contexts: Suodattaa kontektissa %{contexts} delete: Poista empty: Sinulla ei ole suodattimia. expires_in: Vanhenee %{distance} @@ -1414,8 +1414,8 @@ fi: not_found: ei voitu lรถytรครค on_cooldown: Sinรค olet jรครคhyllรค followers_count: Seuraajat muuton aikana - incoming_migrations: Siirtyminen toiselta tililtรค - incoming_migrations_html: Siirtyรคksesi toisesta tilistรค tรคhรคn, sinun tรคytyy ensin luoda tilin alias. + incoming_migrations: Muutto toiselta tililtรค + incoming_migrations_html: Muuttaaksesi toisesta tilistรค tรคhรคn, sinun tรคytyy ensin luoda tilin alias. moved_msg: Tilisi ohjaa nyt kohteeseen %{acct} ja seuraajiasi siirretรครคn. not_redirecting: Tilisi ei ohjaa tรคllรค hetkellรค mihinkรครคn muuhun tiliin. on_cooldown: Olet siirtรคnyt tilisi รคskettรคin. Tรคmรค toiminto tulee saataville uudelleen %{count} pรคivรคn kuluttua. @@ -1707,7 +1707,7 @@ fi: keep_self_bookmark: Sรคilytรค kirjanmerkkeihin lisรครคmรคsi julkaisut keep_self_bookmark_hint: Ei poista julkaisujasi, jos olet lisรคnnyt ne kirjanmerkkeihin keep_self_fav: Sรคilytรค suosikkeihin lisรครคmรคsi julkaisut - keep_self_fav_hint: Ei poista julkaisujasi, jos olet lisรคnnyt ne suosikkeihin + keep_self_fav_hint: Ei poista julkaisujasi, jos olet lisรคnnyt ne suosikkeihisi min_age: '1209600': 2 viikkoa '15778476': 6 kuukautta @@ -1786,7 +1786,7 @@ fi: spam: Roskaposti violation: Sisรคltรถ rikkoo seuraavia yhteisรถn sรครคntรถjรค explanation: - delete_statuses: Joidenkin julkaisuistasi on havaittu rikkovan ainakin yhtรค yhteisรถn sรครคntรถรค, ja instanssin %{instance} valvojat ovat poistaneet ne. + delete_statuses: Joidenkin julkaisuistasi on havaittu rikkovan ainakin yhtรค yhteisรถn sรครคntรถรค, joten instanssin %{instance} valvojat ovat poistaneet ne. disable: Et voi enรครค kรคyttรครค tiliรคsi, mutta profiilisi ja muut tiedot pysyvรคt muuttumattomina. Voit pyytรครค varmuuskopiota tiedoistasi, vaihtaa tilin asetuksia tai poistaa tilisi. mark_statuses_as_sensitive: Palvelimen %{instance} valvojat ovat merkinneet osan julkaisuistasi arkaluonteisiksi. Tรคmรค tarkoittaa sitรค, ettรค ihmisten tรคytyy napauttaa mediaa ennen kuin sen esikatselu nรคytetรครคn. Voit merkitรค median itse arkaluonteiseksi, kun julkaiset tulevaisuudessa. sensitive: Tรคstรค lรคhtien kaikki ladatut mediatiedostot merkitรครคn arkaluonteisiksi ja piilotetaan napsautusvaroituksen taakse. @@ -1807,7 +1807,7 @@ fi: disable: Tili jรครคdytetty mark_statuses_as_sensitive: Julkaisut merkitty arkaluonteisiksi none: Varoitus - sensitive: Tili on merkitty arkaluonteiseksi + sensitive: Tili merkitty arkaluonteiseksi silence: Tiliรค rajoitettu suspend: Tili jรครคdytetty welcome: diff --git a/config/locales/si.yml b/config/locales/si.yml index 39368b3f85..2c2a8ae824 100644 --- a/config/locales/si.yml +++ b/config/locales/si.yml @@ -791,9 +791,9 @@ si: guide_link_text: เถดเถปเท’เท€เถปเทŠเถญเถšเถบเท’เถฑเทŠเถง เถฏเทเถบเถš เท€เท“เถธเถง เท„เทเถšเท’เถบ. sensitive_content: เทƒเถ‚เท€เทšเถฏเท“ เถ…เถฑเทŠเถญเถปเทŠเถœเถญ application_mailer: - notification_preferences: เถŠเถธเทšเถฝเทŠ เถธเถฑเทเถด เท€เท™เถฑเทƒเทŠ เถšเถปเถฑเทŠเถฑ + notification_preferences: เท€เท’-เถญเทเถดเทเถฝเทŠ เถ…เถทเท’เถดเทŠโ€เถปเทšเถญ เท€เท™เถฑเทƒเทŠ เถšเถปเถฑเทŠเถฑ salutation: "%{name}," - settings: 'เถŠเถธเทšเถฝเทŠ เถธเถฑเทเถด เท€เท™เถฑเทƒเทŠ เถšเถปเถฑเทŠเถฑ: %{link}' + settings: 'เท€เท’-เถญเทเถดเทเถฝเทŠ เถ…เถทเท’เถดเทŠโ€เถปเทšเถญ เท€เท™เถฑเทƒเทŠ เถšเถปเถฑเทŠเถฑ: %{link}' view: 'เถฏเทเถšเทŠเถธ:' view_profile: เถดเทเถญเท’เถšเถฉ เถถเถฝเถฑเทŠเถฑ view_status: เถฝเท’เถดเท’เถบ เถถเถฝเถฑเทŠเถฑ @@ -1405,11 +1405,11 @@ si: title: เทƒเถ‚เถปเถšเทŠเท‚เท’เถญ เถปเทเถœเท™เถฑ เถบเทเถธ suspicious_sign_in: change_password: เถธเท”เถปเถดเถฏเถบ เท€เท™เถฑเทƒเทŠ เถšเถปเถฑเทŠเถฑ - details: 'เถดเท”เถปเถฑเถบ เท€เท“เถธเทš เท€เท’เทƒเทŠเถญเถป เถธเท™เถฑเทŠเถฑ:' - explanation: เถ…เถดเท’ เถฑเท€ IP เถฝเท’เถดเท’เถฑเถบเถšเท’เถฑเทŠ เถ”เถถเถœเทš เถœเท’เถซเท”เถธเถง เถดเท”เถปเถฑเถบ เท€เท“เถธเถšเทŠ เถ…เถฑเทเท€เถปเถซเถบ เถšเถปเถœเท™เถฑ เถ‡เถญ. - further_actions_html: เถธเท™เถบ เถ”เถถ เถฑเทœเท€เทš เถฑเถธเทŠ, เถ…เถดเท’ เถ”เถถเถง เท€เท„เทเถธ %{action} เถฝเท™เทƒ เถฑเท’เถปเทŠเถฏเทšเท เถšเถป เถ”เถถเถœเทš เถœเท’เถซเท”เถธ เทƒเท”เถปเถšเทŠเท‚เท’เถญเท€ เถญเถถเท เถœเทเถฑเท“เถธเถง เทƒเทเถฐเถš เถฏเท™เถšเถš เทƒเถญเทŠโ€เถบเทเถดเถฑเถบ เทƒเถถเถฝ เถšเถปเถฑเทŠเถฑ. - subject: เถ”เถถเถœเทš เถœเท’เถซเท”เถธ เถฑเท€ IP เถฝเท’เถดเท’เถฑเถบเถšเท’เถฑเทŠ เถดเทŠโ€เถปเท€เทšเท เท€เท“ เถ‡เถญ - title: เถฑเท€ เถดเท”เถปเถฑเถบ เท€เท“เถธเถšเทŠ + details: 'เถดเทŠโ€เถปเท€เทšเทเถบเถง เถ…เถฏเทเท… เท€เท’เทƒเทŠเถญเถป:' + explanation: เถ”เถถเถœเทš เถœเท’เถซเท”เถธเถง เถฑเท€ เถ….เถขเท.เถšเท™. (IP) เถฝเท’เถดเท’เถฑเถบเถšเท’เถฑเทŠ เถดเทŠโ€เถปเท€เทšเทเถบเถšเทŠ เถ…เถฑเทเท€เถปเถซเถบ เท€เท“ เถ‡เถญ. + further_actions_html: เถธเทš เถ”เถถ เถฑเทœเท€เทš เถฑเถธเทŠ, เท€เท„เทเถธ %{action}. เถ”เถถเถœเทš เถœเท’เถซเท”เถธ เทƒเท”เถปเถšเทŠโ€เท‚เท’เถญเท€ เถญเถถเท เถœเทเถฑเท“เถธเถง เถฏเทŠเท€เท’-เทƒเทเถฐเถšเถบ เทƒเถถเถฝ เถšเถปเถฑเทŠเถฑ. + subject: เถ”เถถเถœเทš เถœเท’เถซเท”เถธเถง เถฑเท€ เถ….เถขเท.เถšเท™. (IP) เถฝเท’เถดเท’เถฑเถบเถšเท’เถฑเทŠ เถดเทŠโ€เถปเท€เทšเท เท€เท“ เถ‡เถญ + title: เถฑเท€ เถดเทŠโ€เถปเท€เทšเทเถบเถšเทŠ warning: appeal: เถ…เถทเท’เถบเทเถ เถฑเถบเถšเทŠ เถ‰เถฏเท’เถปเท’เถดเถญเทŠ เถšเถปเถฑเทŠเถฑ appeal_description: เถธเท™เถบ เถฏเทเท‚เถบเถšเทŠ เถถเท€ เถ”เถถ เท€เท’เทเทŠเท€เทเทƒ เถšเถปเถฑเทŠเถฑเทš เถฑเถธเทŠ, เถ”เถถเถง %{instance}เท„เท’ เถšเทเถปเทŠเถบ เถธเถซเทŠเถฉเถฝเถบเถง เถ…เถทเท’เถบเทเถ เถฑเถบเถšเทŠ เถ‰เถฏเท’เถปเท’เถดเถญเทŠ เถšเท… เท„เทเถš. diff --git a/config/locales/simple_form.fi.yml b/config/locales/simple_form.fi.yml index f73667f5cc..6c8875327b 100644 --- a/config/locales/simple_form.fi.yml +++ b/config/locales/simple_form.fi.yml @@ -25,7 +25,7 @@ fi: types: disable: Estรค kรคyttรคjรครค kรคyttรคmรคstรค tiliรครคn, mutta รคlรค poista tai piilota sen sisรคltรถรค. none: Kรคytรค tรคtรค lรคhettรครคksesi varoituksen kรคyttรคjรคlle kรคynnistรคmรคttรค mitรครคn muita toimintoja. - sensitive: Pakota kaikki tรคmรคn kรคyttรคjรคn mediatiedostot arkaluontoisiksi. + sensitive: Pakota kaikki tรคmรคn kรคyttรคjรคn mediatiedostot arkaluonteisiksi. silence: Estรค kรคyttรคjรครค lรคhettรคmรคstรค viestejรค julkisesti, piilota hรคnen viestinsรค ja ilmoituksensa ihmisiltรค, jotka eivรคt seuraa hรคntรค. Sulkee kaikki tรคmรคn tilin raportit. suspend: Estรค kaikki vuorovaikutus tรคltรค -tai tรคlle tilille ja poista sen kaikki sisรคltรถ. Pรครคtรถs voidaan peruuttaa 30 pรคivรคn aikana. Sulkee kaikki raportit tรคtรค tiliรค vasten. warning_preset_id: Valinnainen. Voit silti lisรคtรค mukautetun tekstin esiasetuksen loppuun @@ -79,14 +79,14 @@ fi: activity_api_enabled: Paikallisesti julkaistujen julkaisujen, aktiivisten kรคyttรคjien ja rekisterรถitymisten viikoittainen mรครคrรค backups_retention_period: Sรคilytรค luodut arkistot mรครคritetyn mรครคrรคn pรคiviรค. bootstrap_timeline_accounts: Nรคmรค tilit kiinnitetรครคn uusien kรคyttรคjien seuraamissuositusten ylรคpuolelle. - closed_registrations_message: Nรคkyy, kun ilmoittautuminen on suljettu - content_cache_retention_period: Viestit muilta palvelimilta poistetaan mรครคritetyn mรครคrรคn pรคiviรค jรคlkeen, kun arvo on asetettu positiiviseksi. Tรคmรค voi olla peruuttamatonta. + closed_registrations_message: Nรคkyy, kun rekisterรถityminen on suljettu + content_cache_retention_period: Kaikki julkaisut ja tehostukset muilta palvelimilta poistetaan, kun mรครคritelty mรครคrรค pรคiviรค on kulunut. Osaa julkaisuista voi olla mahdoton palauttaa. Kaikki julkaisuihin liittyvรคt kirjanmerkit, suosikit ja tehostukset menetetรครคn, eikรค niitรค voi palauttaa. custom_css: Voit kรคyttรครค mukautettuja tyylejรค Mastodonin verkkoversiossa. mascot: Ohittaa kuvituksen edistyneessรค selainkรคyttรถliittymรคssรค. media_cache_retention_period: Ladatut mediatiedostot poistetaan mรครคritetyn mรครคrรคn pรคiviรค jรคlkeen, kun arvo on positiivinen ja ladataan uudelleen pyynnรถstรค. - peers_api_enabled: Luettelo verkkotunnuksista, jotka tรคmรค palvelin on kohdannut fediversumissa. Se ei kerro, oletko liitossa tietyn palvelimen kanssa, vaan ettรค palvelimesi on ylipรครคtรครคn tietoinen siitรค. Tรคtรค tietoa kรคytetรครคn palveluissa, jotka kerรครคvรคt tilastoja liittoutumisesta yleisellรค tasolla. + peers_api_enabled: Luettelo verkkotunnuksista, jotka tรคmรค palvelin on kohdannut fediversumissa. Se ei kerro, oletko liitossa tietyn palvelimen kanssa, vaan ettรค palvelimesi on ylipรครคtรครคn tietoinen siitรค. Tรคtรค tietoa kรคytetรครคn palveluissa, jotka kerรครคvรคt tilastoja federoinnista yleisellรค tasolla. profile_directory: Profiilihakemisto lueteloi kaikki kรคyttรคjรคt, jotka ovat ilmoittaneet olevansa lรถydettรคvissรค. - require_invite_text: Kun kirjautuminen vaatii manuaalisen hyvรคksynnรคn, tee โ€Miksi haluat liittyรค?โ€ teksti syรถtetรครคn pakolliseksi eikรค vapaaehtoiseksi + require_invite_text: Kun rekisterรถityminen vaatii manuaalisen hyvรคksynnรคn, tee โ€Miksi haluat liittyรค?โ€ -tekstikentรคstรค pakollinen vapaaehtoisen sijaan site_contact_email: Kuinka ihmiset voivat tavoittaa sinut oikeudellisissa tai tukikysymyksissรค. site_contact_username: Miten ihmiset voivat tavoittaa sinut Mastodonissa. site_extended_description: Kaikki lisรคtiedot, jotka voivat olla hyรถdyllisiรค kรคvijรถille ja kรคyttรคjille. Voidaan jรคsentรครค Markdown-syntaksilla. @@ -96,10 +96,10 @@ fi: status_page_url: URL-osoite sivulle, jonka kautta tรคmรคn palvelimen tila voidaan ongelmatilanteissa tarkastaa theme: Teema, jonka uloskirjautuneet vierailijat ja uudet kรคyttรคjรคt nรคkevรคt. thumbnail: Noin 2:1 kuva nรคytetรครคn palvelimen tietojen rinnalla. - timeline_preview: Uloskirjautuneet vierailijat voivat selata uusimpia julkisia viestejรค, jotka ovat saatavilla palvelimella. + timeline_preview: Uloskirjautuneet vierailijat voivat selata uusimpia julkisia julkaisuja, jotka ovat saatavilla palvelimella. trendable_by_default: Ohita suositun sisรคllรถn manuaalinen tarkistus. Yksittรคisiรค kohteita voidaan edelleen poistaa jรคlkikรคteen. trends: Trendit osoittavat, mitkรค julkaisut, aihetunnisteet ja uutiset ovat saamassa vetoa palvelimellasi. - trends_as_landing_page: Nรคytรค vierailijoille ja uloskirjautuneille kรคyttรคjille suosittu sisรคltรถ palvelininstanssin kuvaustekstin sijaan. Edellytyksenรค on, ettรค suosittu sisรคltรถ -ominaisuus on kรคytรถssรค. + trends_as_landing_page: Nรคytรค vierailijoille ja uloskirjautuneille kรคyttรคjille suosittua sisรคltรถรค palvelimen kuvauksen sijaan. Edellyttรครค, ettรค trendit on otettu kรคyttรถรถn. form_challenge: current_password: Olet menossa suojatulle alueelle imports: @@ -129,9 +129,9 @@ fi: chosen_languages: Kun valittu, vain valituilla kielillรค kirjoitetut julkaisut nรคkyvรคt julkisilla aikajanoilla role: Rooli mรครคrรครค, mitkรค kรคyttรถoikeudet kรคyttรคjรคllรค on user_role: - color: Vรคri, jota kรคytetรครคn roolin koko kรคyttรถliittymรคssรค, RGB heksamuodossa + color: Vรคri, jota kรคytetรครคn roolille kaikkialla kรคyttรถliittymรคssรค, RGB-heksadesimaalimuodossa highlighted: Tรคmรค tekee roolista julkisesti nรคkyvรคn - name: Roolin julkinen nimi, jos rooli on asetettu nรคytettรคvรคksi mekkinรค + name: Roolin julkinen nimi, jos rooli on asetettu nรคytettรคvรคksi merkkinรค permissions_as_keys: Kรคyttรคjillรค, joilla on tรคmรค rooli, on kรคyttรถoikeus... position: Korkeampi rooli ratkaisee konfliktit tietyissรค tilanteissa. Tiettyjรค toimintoja voidaan suorittaa vain rooleille, joiden prioriteetti on pienempi webhook: @@ -238,10 +238,10 @@ fi: hide: Piilota kokonaan warn: Piilota ja nรคytรค varoitus form_admin_settings: - activity_api_enabled: Julkaise yhteenlasketut tilastot kรคyttรคjรคtoiminnasta rajapinnassa + activity_api_enabled: Julkaise yhteenlasketut tilastot kรคyttรคjรคtoiminnasta ohjelmointirajapinnassa backups_retention_period: Kรคyttรคjรคn arkiston sรคilytysaika bootstrap_timeline_accounts: Suosittele aina nรคitรค tilejรค uusille kรคyttรคjille - closed_registrations_message: Mukautettu viesti, kun kirjautumisia ei ole saatavilla + closed_registrations_message: Mukautettu viesti, kun rekisterรถityminen ei ole saatavilla content_cache_retention_period: Sisรคllรถn vรคlimuistin sรคilytysaika custom_css: Mukautettu CSS mascot: Mukautettu maskotti (legacy) @@ -251,7 +251,7 @@ fi: registrations_mode: Kuka voi rekisterรถityรค require_invite_text: Vaadi syy liittyรค show_domain_blocks: Nรคytรค verkkotunnusten estot - show_domain_blocks_rationale: Nรคytรค miksi verkkotunnukset on estetty + show_domain_blocks_rationale: Nรคytรค, miksi verkkotunnukset on estetty site_contact_email: Ota yhteyttรค sรคhkรถpostilla site_contact_username: Yhteyshenkilรถn kรคyttรคjรคnimi site_extended_description: Laajennettu kuvaus @@ -261,10 +261,10 @@ fi: status_page_url: Tilasivun URL-osoite theme: Oletusteema thumbnail: Palvelimen pikkukuva - timeline_preview: Salli todentamaton pรครคsy julkiselle aikajanalle + timeline_preview: Salli todentamaton pรครคsy julkisille aikajanoille trendable_by_default: Salli trendit ilman ennakkotarkastusta - trends: Trendit kรคyttรถรถn - trends_as_landing_page: Kรคytรค suosittua sisรคltรถรค aloitussivuna + trends: Ota trendit kรคyttรถรถn + trends_as_landing_page: Kรคytรค trendejรค aloitussivuna interactions: must_be_follower: Estรค ilmoitukset kรคyttรคjiltรค, jotka eivรคt seuraa sinua must_be_following: Estรค ilmoitukset kรคyttรคjiltรค, joita et seuraa @@ -313,7 +313,7 @@ fi: time_zone: Aikavyรถhyke user_role: color: Merkin vรคri - highlighted: Nรคyttรค rooli merkkinรค kรคyttรคjรคprofiileissa + highlighted: Nรคytรค rooli merkkinรค kรคyttรคjรคprofiileissa name: Nimi permissions_as_keys: Oikeudet position: Prioriteetti diff --git a/config/locales/simple_form.zh-TW.yml b/config/locales/simple_form.zh-TW.yml index 5108a97452..c48c659fa6 100644 --- a/config/locales/simple_form.zh-TW.yml +++ b/config/locales/simple_form.zh-TW.yml @@ -5,7 +5,7 @@ zh-TW: account: discoverable: ๅ…ฌ้–‹ๅ˜Ÿๆ–‡ๅŠๅ€‹ไบบๆช”ๆกˆๅฏ่ƒฝๆ–ผๅ„ Mastodon ๅŠŸ่ƒฝไธญ่ขซๆŽจ่–ฆ๏ผŒไธฆไธ”ๆ‚จ็š„ๅ€‹ไบบๆช”ๆกˆๅฏ่ƒฝ่ขซๆŽจ่–ฆ่‡ณๅ…ถไป–ไฝฟ็”จ่€…ใ€‚ display_name: ๅฎŒๆ•ดๅ็จฑๆˆ–ๆšฑ็จฑใ€‚ - fields: ็ƒ˜ๅŸน้›ž๏ผŒ่‡ชๆˆ‘่ชๅŒไปฃ็จฑ๏ผŒๅนด้ฝก๏ผŒๅŠไปปไฝ•ๆ‚จๆƒณๅˆ†ไบซ็š„ใ€‚ + fields: ็ƒ˜ๅŸน้›žใ€่‡ชๆˆ‘่ชๅŒไปฃ็จฑใ€ๅนด้ฝก๏ผŒๅŠไปปไฝ•ๆ‚จๆƒณๅˆ†ไบซ็š„ใ€‚ indexable: ๆ‚จ็š„ๅ…ฌ้–‹ๅ˜Ÿๆ–‡ๅฏ่ƒฝๆœƒ้กฏ็คบๆ–ผ Mastodon ไน‹ๆœๅฐ‹็ตๆžœไธญใ€‚ๆ›พ่ˆ‡ๆ‚จๅ˜Ÿๆ–‡ไบ’ๅ‹•้Ž็š„ไบบๅฏ่ƒฝ็„ก่ซ–ๅฆ‚ไฝ•้ƒฝ่ƒฝๆœๅฐ‹ๅฎƒๅ€‘ใ€‚ note: 'ๆ‚จๅฏไปฅ @mention ๅ…ถไป–ไบบๆˆ–่€…ไฝฟ็”จ #ไธป้กŒๆจ™็ฑคใ€‚' show_collections: ไบบๅ€‘ๅฐ‡่ƒฝ็€่ฆฝๆ‚จ่ทŸ้šจไธญๅŠ่ทŸ้šจ่€…ๅธณ่™Ÿใ€‚ๆ‚จๆ‰€่ทŸ้šจไน‹ไบบ่ƒฝๅพ—็Ÿฅๆ‚จๆญฃๅœจ่ทŸ้šจๅ…ถๅธณ่™Ÿใ€‚ @@ -31,7 +31,7 @@ zh-TW: warning_preset_id: ้ธ็”จใ€‚ๆ‚จไปๅฏๅœจ้ ่จญ็š„็ตๅฐพๆ–ฐๅขž่‡ช่จ‚ๆ–‡ๅญ— announcement: all_day: ๆ ธๅ–ๅพŒ๏ผŒๅชๆœƒ้กฏ็คบๅ‡บๆ™‚้–“็ฏ„ๅœไธญ็š„ๆ—ฅๆœŸ้ƒจๅˆ† - ends_at: ๅฏ้ธ็š„๏ผŒๅ…ฌๅ‘Šๆœƒๅœจ่ฉฒๆ™‚้–“้ปž่‡ชๅ‹•ๅ–ๆถˆ็™ผๅธƒ + ends_at: ๅฏ้ธ็š„๏ผŒๅ…ฌๅ‘Šๆœƒๆ–ผ่ฉฒๆ™‚้–“้ปž่‡ชๅ‹•ๅ–ๆถˆ็™ผๅธƒ scheduled_at: ็ฉบ็™ฝๅ‰‡็ซ‹ๅณ็™ผๅธƒๅ…ฌๅ‘Š starts_at: ๅฏ้ธ็š„๏ผŒ่ฎ“ๅ…ฌๅ‘Šๅœจ็‰นๅฎšๆ™‚้–“็ฏ„ๅœๅ…ง้กฏ็คบ text: ๆ‚จๅฏไปฅไฝฟ็”จๅ˜Ÿๆ–‡่ชžๆณ•๏ผŒไฝ†่ซ‹ๅฐๅฟƒๅˆฅ่ฎ“ๅ…ฌๅ‘Šๅคช้ดจ้œธ่€Œไฝ”ๆ“šไฝฟ็”จ่€…็š„ๆ•ดๅ€‹็‰ˆ้ขใ€‚ @@ -59,8 +59,8 @@ zh-TW: setting_display_media_default: ้šฑ่—ๆจ™็‚บๆ•ๆ„Ÿๅ…งๅฎน็š„ๅช’้ซ” setting_display_media_hide_all: ็ธฝๆ˜ฏ้šฑ่—ๆ‰€ๆœ‰ๅช’้ซ” setting_display_media_show_all: ็ธฝๆ˜ฏ้กฏ็คบๆจ™็‚บๆ•ๆ„Ÿๅ…งๅฎน็š„ๅช’้ซ” - setting_use_blurhash: ๅฝฉ่‰ฒๆผธๅฑคๅœ–ๆจฃๆ˜ฏๅŸบๆ–ผ้šฑ่—ๅช’้ซ”ๅ…งๅฎน้ก่‰ฒ็”ข็”Ÿ๏ผŒๆ‰€ๆœ‰็ดฐ็ฏ€ๆœƒ่ฎŠๅพ—ๆจก็ณŠ - setting_use_pending_items: ้—œ้–‰่‡ชๅ‹•ๆฒๅ‹•ๆ›ดๆ–ฐ๏ผŒๆ™‚้–“่ปธๅชๆœƒๅœจ้ปžๆ“ŠๅพŒๆ›ดๆ–ฐ + setting_use_blurhash: ๅฝฉ่‰ฒๆผธๅฑคๅœ–ๆจฃๆ˜ฏๅŸบๆ–ผ้šฑ่—ๅช’้ซ”ๅ…งๅฎน้ก่‰ฒ็”ข็”Ÿ๏ผŒๆ‰€ๆœ‰็ดฐ็ฏ€ๅฐ‡่ฎŠๅพ—ๆจก็ณŠ + setting_use_pending_items: ้—œ้–‰่‡ชๅ‹•ๆฒๅ‹•ๆ›ดๆ–ฐ๏ผŒๆ™‚้–“่ปธๅชๆœƒๆ–ผ้ปžๆ“ŠๅพŒๆ›ดๆ–ฐ username: ๆ‚จๅฏไปฅไฝฟ็”จๅญ—ๅน•ใ€ๆ•ธๅญ—่ˆ‡ๅบ•็ทš whole_word: ๅฆ‚ๆžœ้—œ้ตๅญ—ๆˆ–่ฉž็ต„ๅƒ…ๆœ‰ๅญ—ๆฏ่ˆ‡ๆ•ธๅญ—๏ผŒๅ‰‡ๅ…ถๅฐ‡ๅชๅœจ็ฌฆๅˆๆ•ดๅ€‹ๅ–ฎๅญ—็š„ๆ™‚ๅ€™ๆ‰ๆœƒๅฅ—็”จ domain_allow: @@ -126,7 +126,7 @@ zh-TW: tag: name: ๆ‚จๅช่ƒฝ่ฎŠๆ›ดๅคงๅฐๅฏซ๏ผŒไพ‹ๅฆ‚๏ผŒไปฅไฝฟๅ…ถๆ›ดๆ˜“่ฎ€ใ€‚ user: - chosen_languages: ็•ถ้ธๅ–ๆ™‚๏ผŒๅชๆœ‰้ธๅ–่ชž่จ€ไน‹ๅ˜Ÿๆ–‡ๆœƒๅœจๅ…ฌ้–‹ๆ™‚้–“่ปธไธญ้กฏ็คบ + chosen_languages: ็•ถ้ธๅ–ๆ™‚๏ผŒๅชๆœ‰้ธๅ–่ชž่จ€ไน‹ๅ˜Ÿๆ–‡ๆœƒๆ–ผๅ…ฌ้–‹ๆ™‚้–“่ปธไธญ้กฏ็คบ role: ่ง’่‰ฒๆŽงๅˆถไฝฟ็”จ่€…ๆœ‰ๅ“ชไบ›ๆฌŠ้™ user_role: color: ๅœจๆ•ดๅ€‹ไฝฟ็”จ่€…ไป‹้ขไธญ็”จๆ–ผ่ง’่‰ฒ็š„้ก่‰ฒ๏ผŒๅๅ…ญ้€ฒไฝๆ ผๅผ็š„ RGB diff --git a/config/locales/sk.yml b/config/locales/sk.yml index 78b9b87db3..c25fab6aeb 100644 --- a/config/locales/sk.yml +++ b/config/locales/sk.yml @@ -353,6 +353,7 @@ sk: silence: Obmedz suspend: Vylรบฤ title: Novรฉ blokovanie domรฉny + not_permitted: Nemรกลก povolenie na vykonanie tohto kroku obfuscate: Zatemniลฅ nรกzov domรฉny private_comment: Sรบkromnรฝ komentรกr private_comment_hint: Odรดvodni toto domรฉnovรฉ obmedzenie, pre vnรบtornรฉ vyrozumenie moderรกtorov. diff --git a/config/locales/zh-TW.yml b/config/locales/zh-TW.yml index 33870c0bb2..e139742ff1 100644 --- a/config/locales/zh-TW.yml +++ b/config/locales/zh-TW.yml @@ -390,7 +390,7 @@ zh-TW: domain: ็ซ™้ปž edit: ๆ›ดๆ”นๅฐ้Ž–็š„็ซ™ๅฐ existing_domain_block: ๆ‚จๅทฒๅฐ %{name} ๆ–ฝๅŠ ๆ›ดๅšดๆ ผ็š„้™ๅˆถใ€‚ - existing_domain_block_html: ๆ‚จๅทฒๅฐ %{name} ๆ–ฝๅŠ ๆ›ดๅšดๆ ผ็š„้™ๅˆถ๏ผŒๆ‚จ้œ€่ฆๅ…ˆ ่งฃ้™คๅฐ้Ž–ใ€‚ + existing_domain_block_html: ๆ‚จๅทฒๅฐ %{name} ๆ–ฝๅŠ ๆ›ดๅšดๆ ผ็š„้™ๅˆถ๏ผŒๆ‚จ้œ€่ฆๅ…ˆ่งฃ้™คๅฐ้Ž–ใ€‚ export: ๅŒฏๅ‡บ import: ๅŒฏๅ…ฅ new: @@ -451,9 +451,7 @@ zh-TW: title: ๅŒฏๅ…ฅ็ถฒๅŸŸ้ป‘ๅๅ–ฎ no_file: ๅฐšๆœช้ธๆ“‡ๆช”ๆกˆ follow_recommendations: - description_html: |- - ่ทŸ้šจๅปบ่ญฐๅนซๅŠฉๆ–ฐไฝฟ็”จ่€…ๅ€‘ๅฟซ้€Ÿๆ‰พๅˆฐๆœ‰่ถฃ็š„ๅ…งๅฎนใ€‚็•ถไฝฟ็”จ่€…ๆฒ’ๆœ‰่ˆ‡ๅ…ถไป–ๅธณ่™Ÿๆœ‰่ถณๅค ๅคš็š„ไบ’ๅ‹•ไปฅๅปบ็ซ‹ๅ€‹ไบบๅŒ–่ทŸ้šจๅปบ่ญฐๆ™‚๏ผŒ้€™ไบ›ๅธณ่™Ÿๅฐ‡ๆœƒ่ขซๆŽจ่–ฆใ€‚้€™ไบ›ๅธณ่™Ÿๅฐ‡ๅŸบๆ–ผๆŸ้ธๅฎš่ชž่จ€ไน‹้ซ˜ไบ’ๅ‹•ๅ’Œ้ซ˜ๆœฌๅœฐ่ทŸ้šจ่€…ๆ•ธ้‡ๅธณ่™Ÿ่€Œ - ๆฏๆ—ฅ้‡ๆ–ฐๆ›ดๆ–ฐใ€‚ + description_html: "่ทŸ้šจๅปบ่ญฐๅนซๅŠฉๆ–ฐไฝฟ็”จ่€…ๅ€‘ๅฟซ้€Ÿๆ‰พๅˆฐๆœ‰่ถฃ็š„ๅ…งๅฎนใ€‚็•ถไฝฟ็”จ่€…ๆฒ’ๆœ‰่ˆ‡ๅ…ถไป–ๅธณ่™Ÿๆœ‰่ถณๅค ๅคš็š„ไบ’ๅ‹•ไปฅๅปบ็ซ‹ๅ€‹ไบบๅŒ–่ทŸ้šจๅปบ่ญฐๆ™‚๏ผŒ้€™ไบ›ๅธณ่™Ÿๅฐ‡ๆœƒ่ขซๆŽจ่–ฆใ€‚้€™ไบ›ๅธณ่™Ÿๅฐ‡ๅŸบๆ–ผๆŸ้ธๅฎš่ชž่จ€ไน‹้ซ˜ไบ’ๅ‹•ๅ’Œ้ซ˜ๆœฌๅœฐ่ทŸ้šจ่€…ๆ•ธ้‡ๅธณ่™Ÿ่€Œๆฏๆ—ฅ้‡ๆ–ฐๆ›ดๆ–ฐใ€‚" language: ๅฐๆ–ผ่ชž่จ€ status: ็‹€ๆ…‹ suppress: ๅ–ๆถˆ่ทŸ้šจๅปบ่ญฐ @@ -553,7 +551,7 @@ zh-TW: relays: add_new: ๆ–ฐๅขžไธญ็นผ็ซ™ delete: ๅˆช้™ค - description_html: "่ฏ้‚ฆไธญ็นผ็ซ™ ๆ˜ฏ็จฎไธญ็นผไผบๆœๅ™จ๏ผŒๆœƒๅœจ่จ‚้–ฑไธฆๆŽจ้€่‡ณๆญคไธญ็นผ็ซ™็š„ไผบๆœๅ™จไน‹้–“ไบคๆ›ๅคง้‡็š„ๅ…ฌ้–‹ๅ˜Ÿๆ–‡ใ€‚ไธญ็นผ็ซ™ไนŸ่ƒฝๅ”ๅŠฉๅฐๅž‹ๆˆ–ไธญๅž‹ไผบๆœๅ™จๅพž่ฏ้‚ฆๅฎ‡ๅฎ™ไธญๆŽข็ดขๅ…งๅฎน๏ผŒ่€Œ็„ก้ ˆๆœฌๅœฐไฝฟ็”จ่€…ๆ‰‹ๅ‹•่ทŸ้šจ้ ็ซฏไผบๆœๅ™จ็š„ๅ…ถไป–ไฝฟ็”จ่€…ใ€‚" + description_html: "่ฏ้‚ฆไธญ็นผ็ซ™ ๆ˜ฏ็จฎไธญ็นผไผบๆœๅ™จ๏ผŒๆœƒๆ–ผ่จ‚้–ฑไธฆๆŽจ้€่‡ณๆญคไธญ็นผ็ซ™็š„ไผบๆœๅ™จไน‹้–“ไบคๆ›ๅคง้‡็š„ๅ…ฌ้–‹ๅ˜Ÿๆ–‡ใ€‚ไธญ็นผ็ซ™ไนŸ่ƒฝๅ”ๅŠฉๅฐๅž‹ๆˆ–ไธญๅž‹ไผบๆœๅ™จๅพž่ฏ้‚ฆๅฎ‡ๅฎ™ไธญๆŽข็ดขๅ…งๅฎน๏ผŒ่€Œ็„ก้ ˆๆœฌๅœฐไฝฟ็”จ่€…ๆ‰‹ๅ‹•่ทŸ้šจ้ ็ซฏไผบๆœๅ™จ็š„ๅ…ถไป–ไฝฟ็”จ่€…ใ€‚" disable: ๅœ็”จ disabled: ๅœ็”จ enable: ๅ•Ÿ็”จ @@ -988,7 +986,7 @@ zh-TW: aliases: add_new: ๅปบ็ซ‹ๅˆฅๅ created_msg: ๆˆๅŠŸๅปบ็ซ‹ๅˆฅๅใ€‚ๆ‚จๅฏไปฅ่‡ช่ˆŠๅธณ่™Ÿ้–‹ๅง‹่ฝ‰็งปใ€‚ - deleted_msg: ๆˆๅŠŸ็งป้™คๅˆฅๅใ€‚ๆ‚จๅฐ‡็„กๆณ•ๅ†็”ฑ่ˆŠๅธณ่™Ÿ่ฝ‰็งปๅˆฐ็›ฎๅ‰็š„ๅธณ่™Ÿใ€‚ + deleted_msg: ๆˆๅŠŸ็งป้™คๅˆฅๅใ€‚ๆ‚จๅฐ‡็„กๆณ•ๅ†็”ฑ่ˆŠๅธณ่™Ÿ่ฝ‰็งป่‡ณ็›ฎๅ‰็š„ๅธณ่™Ÿใ€‚ empty: ๆ‚จ็›ฎๅ‰ๆฒ’ๆœ‰ไปปไฝ•ๅˆฅๅใ€‚ hint_html: ๅฆ‚ๆžœๆƒณ็”ฑๅ…ถไป–ๅธณ่™Ÿ่ฝ‰็งป่‡ณๆญคๅธณ่™Ÿ๏ผŒๆ‚จๅฏไปฅๅœจๆญค่™•ๆ–ฐๅขžๅˆฅๅ๏ผŒ็จๅพŒ็ณป็ตฑๅฐ‡ๅฎน่จฑๆ‚จๅฐ‡่ทŸ้šจ่€…็”ฑ่ˆŠๅธณ่™Ÿ่ฝ‰็งป่‡ณๆญคใ€‚ๆญค้ …ไฝœๆฅญๆ˜ฏ็„กๅฎณไธ”ๅฏๅพฉๅŽŸ็š„ใ€‚ ๅธณ่™Ÿ็š„้ท็งป็จ‹ๅบ้œ€่ฆๅœจ่ˆŠๅธณ่™Ÿๅ•Ÿๅ‹•ใ€‚ remove: ๅ–ๆถˆ้€ฃ็ตๅˆฅๅ @@ -999,7 +997,7 @@ zh-TW: confirmation_dialogs: ็ขบ่ชๅฐ่ฉฑๆก† discovery: ๆŽข็ดข localization: - body: Mastodon ๆ˜ฏ็”ฑๅฟ—้ก˜่€…็ฟป่ญฏ็š„ใ€‚ + body: Mastodon ๆ˜ฏ็”ฑๅฟ—้ก˜่€…ๆ‰€็ฟป่ญฏใ€‚ guide_link: https://crowdin.com/project/mastodon guide_link_text: ๆฏๅ€‹ไบบ้ƒฝ่ƒฝ่ฒข็ปใ€‚ sensitive_content: ๆ•ๆ„Ÿๅ…งๅฎน @@ -1042,8 +1040,8 @@ zh-TW: log_in_with: ็™ปๅ…ฅ๏ผŒไฝฟ็”จ login: ็™ปๅ…ฅ logout: ็™ปๅ‡บ - migrate_account: ่ฝ‰็งปๅˆฐๅฆไธ€ๅ€‹ๅธณ่™Ÿ - migrate_account_html: ๅฆ‚ๆžœๆ‚จๅธŒๆœ›ๅผ•ๅฐŽไป–ไบบ่ทŸ้šจๅฆไธ€ๅ€‹ๅธณ่™Ÿ๏ผŒ่ซ‹ ๅˆฐ้€™่ฃก่จญๅฎšใ€‚ + migrate_account: ่ฝ‰็งป่‡ณๅฆไธ€ๅ€‹ๅธณ่™Ÿ + migrate_account_html: ๅฆ‚ๆžœๆ‚จๅธŒๆœ›ๅผ•ๅฐŽไป–ไบบ่ทŸ้šจๅฆไธ€ๅ€‹ๅธณ่™Ÿ๏ผŒ่ซ‹่‡ณ้€™่ฃก่จญๅฎšใ€‚ or_log_in_with: ๆˆ–้€้Žๅ…ถไป–ๆ–นๅผ็™ปๅ…ฅ privacy_policy_agreement_html: ๆˆ‘ๅทฒ้–ฑ่ฎ€ไธ”ๅŒๆ„ ้šฑ็งๆฌŠๆ”ฟ็ญ– progress: @@ -1072,7 +1070,7 @@ zh-TW: email_below_hint_html: ่ซ‹ๆชขๆŸฅๆ‚จ็š„ๅžƒๅœพ้ƒตไปถ่ณ‡ๆ–™ๅคพ๏ผŒๆˆ–ๆ˜ฏ่ซ‹ๆฑ‚ๅฆไธ€ๅ€‹ใ€‚ๅฆ‚ๆžœๆ˜ฏ้Œฏ็š„๏ผŒๆ‚จๅฏไปฅๆ›ดๆญฃๆ‚จ็š„้›ปๅญ้ƒตไปถๅœฐๅ€ใ€‚ email_settings_hint_html: ่ซ‹้ปžๆ“Šๆˆ‘ๅ€‘ๅฏ„็ตฆๆ‚จ้€ฃ็ตไปฅ้ฉ—่ญ‰ %{email}ใ€‚ๆˆ‘ๅ€‘ๅฐ‡ๆ–ผๆญค็จๅ€™ใ€‚ link_not_received: ็„กๆณ•ๅ–ๅพ—้€ฃ็ตๅ—Ž๏ผŸ - new_confirmation_instructions_sent: ๆ‚จๅฐ‡ๆœƒๅœจๅนพๅˆ†้˜ไน‹ๅ…งๆ”ถๅˆฐๆ–ฐ็š„ๅŒ…ๅซ็ขบ่ช้€ฃ็ต็š„้›ปๅญ้ƒตไปถ๏ผ + new_confirmation_instructions_sent: ๆ‚จๅฐ‡ๆ–ผๅนพๅˆ†้˜ไน‹ๅ…งๆ”ถๅˆฐๆ–ฐ็š„ๅŒ…ๅซ็ขบ่ช้€ฃ็ต็š„้›ปๅญ้ƒตไปถ๏ผ title: ่ซ‹ๆชขๆŸฅๆ‚จ็š„ๆ”ถไปถๅŒฃ sign_in: preamble_html: ่ซ‹ไฝฟ็”จๆ‚จๆ–ผ %{domain} ็š„ๅธณ่™Ÿๅฏ†็ขผ็™ปๅ…ฅใ€‚่‹ฅๆ‚จ็š„ๅธณ่™Ÿ่จ—็ฎกๆ–ผๅ…ถไป–ไผบๆœๅ™จ๏ผŒๆ‚จๅฐ‡็„กๆณ•ๅœจๆญค็™ปๅ…ฅใ€‚ @@ -1084,7 +1082,7 @@ zh-TW: status: account_status: ๅธณ่™Ÿ็‹€ๆ…‹ confirming: ็ญ‰ๅพ…้›ปๅญ้ƒตไปถ็ขบ่ชๅฎŒๆˆใ€‚ - functional: ๆ‚จ็š„ๅธณ่™Ÿๅฏไปฅๆญฃๅธธไฝฟ็”จไบ†ใ€‚ + functional: "ๆ‚จ็š„ๅธณ่™Ÿๅฏไปฅๆญฃๅธธไฝฟ็”จไบ†ใ€‚๐ŸŽ‰" pending: ็ฎก็ฎกๅ€‘ๆญฃๅœจ่™•็†ๆ‚จ็š„็”ณ่ซ‹๏ผŒ้€™ๅฏ่ƒฝ้œ€่ฆไธ€้ปžๆ™‚้–“่™•็†ใ€‚ๆˆ‘ๅ€‘ๅฐ‡ๆ–ผ็”ณ่ซ‹้€š้ŽๅพŒไปฅ้›ปๅญ้ƒตไปถๆ–นๅผ้€š็Ÿฅๆ‚จใ€‚ redirecting_to: ๆ‚จ็š„ๅธณ่™Ÿๅ› ็›ฎๅ‰้‡ๅฎšๅ‘่‡ณ %{acct} ่€Œ่ขซๅœ็”จใ€‚ view_strikes: ๆชข่ฆ–้‡ๅฐๆ‚จๅธณ่™Ÿ้ŽๅŽป็š„่ญฆ็คบ @@ -1410,7 +1408,7 @@ zh-TW: followers: ๆญคๅ‹•ไฝœๅฐ‡ๆœƒๅฐ‡็›ฎๅ‰ๅธณ่™Ÿ็š„ๆ‰€ๆœ‰่ทŸ้šจ่€…่ฝ‰็งป่‡ณๆ–ฐๅธณ่™Ÿ only_redirect_html: ๆˆ–่€…๏ผŒๆ‚จไนŸๅฏไปฅๅƒ…ๅœจๆ‚จ็š„ๅ€‹ไบบๆช”ๆกˆไธญ่จญๅฎš้‡ๆ–ฐๅฐŽๅ‘ใ€‚ other_data: ๅ…ถไป–่ณ‡ๆ–™ไธฆไธๆœƒ่‡ชๅ‹•่ฝ‰็งป - redirect: ๆ‚จ็›ฎๅ‰็š„ๅธณ่™Ÿๅฐ‡ๆœƒๅœจๅ€‹ไบบๆช”ๆกˆ้ ้ขๆ–ฐๅขž้‡ๆ–ฐๅฐŽๅ‘ๅ…ฌๅ‘Š๏ผŒไธฆๆœƒ่ขซๆŽ’้™คๅœจๆœๅฐ‹็ตๆžœไน‹ๅค– + redirect: ๆ‚จ็›ฎๅ‰็š„ๅธณ่™Ÿๅฐ‡ๆ–ผๅ€‹ไบบๆช”ๆกˆ้ ้ขๆ–ฐๅขž้‡ๆ–ฐๅฐŽๅ‘ๅ…ฌๅ‘Š๏ผŒไธฆๆœƒ่ขซๆŽ’้™คๅœจๆœๅฐ‹็ตๆžœไน‹ๅค– moderation: title: ็ซ™ๅ‹™ move_handler: @@ -1499,9 +1497,7 @@ zh-TW: posting_defaults: ๅ˜Ÿๆ–‡้ ่จญๅ€ผ public_timelines: ๅ…ฌ้–‹ๆ™‚้–“่ปธ privacy: - hint_html: |- - ่‡ช่จ‚ๆ‚จๅธŒๆœ›ๅฆ‚ไฝ•่ฎ“ๆ‚จ็š„ๅ€‹ไบบๆช”ๆกˆๅŠๅ˜Ÿๆ–‡่ขซๆ‰พๅˆฐใ€‚ - ่—‰็”ฑๅ•Ÿ็”จไธ€็ณปๅˆ— Mastodon ๅŠŸ่ƒฝไปฅๅนซๅŠฉๆ‚จ่งธๅŠๆ›ดๅปฃ็š„ๅ—็œพใ€‚็…ฉ่ซ‹่Šฑไบ›ๆ™‚้–“็ขบ่ชๆ‚จๆ˜ฏๅฆๆฌฒๅ•Ÿ็”จ้€™ไบ›่จญๅฎšใ€‚ + hint_html: "่‡ช่จ‚ๆ‚จๅธŒๆœ›ๅฆ‚ไฝ•่ฎ“ๆ‚จ็š„ๅ€‹ไบบๆช”ๆกˆๅŠๅ˜Ÿๆ–‡่ขซ็™ผ็พใ€‚่—‰็”ฑๅ•Ÿ็”จไธ€็ณปๅˆ— Mastodon ๅŠŸ่ƒฝไปฅๅนซๅŠฉๆ‚จ่งธๅŠๆ›ดๅปฃ็š„ๅ—็œพใ€‚็…ฉ่ซ‹่Šฑไบ›ๆ™‚้–“็ขบ่ชๆ‚จๆ˜ฏๅฆๆฌฒๅ•Ÿ็”จ้€™ไบ›่จญๅฎšใ€‚" privacy: ้šฑ็งๆฌŠ privacy_hint_html: ๆŽงๅˆถๆ‚จๅธŒๆœ›ๅ‘ๅ…ถไป–ไบบๆญ้œฒไน‹ๅ…งๅฎนใ€‚ไบบๅ€‘้€้Ž็€่ฆฝๅ…ถไป–ไบบ็š„่ทŸ้šจ่€…่ˆ‡ๅ…ถ็™ผๅ˜Ÿไน‹ๆ‡‰็”จ็จ‹ๅผ็™ผ็พๆœ‰่ถฃ็š„ๅ€‹ไบบๆช”ๆกˆๅ’Œ้…ท็‚ซ็š„ Mastodon ๆ‡‰็”จ็จ‹ๅผ๏ผŒไฝ†ๆ‚จ่ƒฝ้ธๆ“‡ๅฐ‡ๅ…ถ้šฑ่—ใ€‚ reach: ่งธๅŠ @@ -1665,7 +1661,7 @@ zh-TW: enabled: ่‡ชๅ‹•ๅˆช้™ค่ˆŠๅ˜Ÿๆ–‡ enabled_hint: ไธ€ๆ—ฆ้”ๅˆฐๆŒ‡ๅฎš็š„ไฟๅญ˜ๆœŸ้™๏ผŒๅฐฑๆœƒ่‡ชๅ‹•ๅˆช้™คๆ‚จ็š„ๅ˜Ÿๆ–‡๏ผŒ้™ค้ž่ฉฒๅ˜Ÿๆ–‡็ฌฆๅˆไธ‹ๅˆ—ไพ‹ๅค– exceptions: ไพ‹ๅค– - explanation: ๅ› ็‚บๅˆช้™คๅ˜Ÿๆ–‡ๆ˜ฏ่€—่ฒป่ณ‡ๆบ็š„ๆ“ไฝœ๏ผŒ็•ถไผบๆœๅ™จไธ้‚ฃ้บผๅฟ™็ขŒๆ™‚ๆ‰ๆœƒๆ…ขๆ…ขๅฎŒๆˆใ€‚ๅ› ๆญค๏ผŒๆ‚จ็š„ๅ˜Ÿๆ–‡ๆœƒๅœจๅˆฐ้”ไฟๅญ˜ๆœŸ้™ๅพŒไธ€ๆฎตๆ™‚้–“ๆ‰ๆœƒ่ขซๅˆช้™คใ€‚ + explanation: ๅ› ็‚บๅˆช้™คๅ˜Ÿๆ–‡ๆ˜ฏ่€—่ฒป่ณ‡ๆบ็š„ๆ“ไฝœ๏ผŒ็•ถไผบๆœๅ™จไธ้‚ฃ้บผๅฟ™็ขŒๆ™‚ๆ‰ๆœƒๆ…ขๆ…ขๅฎŒๆˆใ€‚ๅ› ๆญค๏ผŒๆ‚จ็š„ๅ˜Ÿๆ–‡ๅฐ‡ๆ–ผๅˆฐ้”ไฟๅญ˜ๆœŸ้™ๅพŒไธ€ๆฎตๆ™‚้–“ๆ‰ๆœƒ่ขซๅˆช้™คใ€‚ ignore_favs: ๅฟฝ็•ฅๆœ€ๆ„›ๆ•ธ ignore_reblogs: ๅฟฝ็•ฅ่ฝ‰ๅ˜Ÿๆ•ธ interaction_exceptions: ๅŸบๆ–ผไบ’ๅ‹•็š„ไพ‹ๅค–่ฆๅ‰‡ From 23f8e93c6405277515d018d4ff2d550fe64ac159 Mon Sep 17 00:00:00 2001 From: Wladimir Palant Date: Mon, 16 Oct 2023 13:39:25 +0200 Subject: [PATCH 16/24] Fixes #23135 - Allow cross origin request for /nodeinfo/2.0 API (#27413) --- config/initializers/cors.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/config/initializers/cors.rb b/config/initializers/cors.rb index 3d94e38e8e..6424477846 100644 --- a/config/initializers/cors.rb +++ b/config/initializers/cors.rb @@ -14,6 +14,7 @@ Rails.application.config.middleware.insert_before 0, Rack::Cors do with_options headers: :any, credentials: false do with_options methods: [:get] do resource '/.well-known/*' + resource '/nodeinfo/*' resource '/@:username' resource '/users/:username' end From 299aa71c8f85542ace4d9cb61cbdf896ff342213 Mon Sep 17 00:00:00 2001 From: Claire Date: Mon, 16 Oct 2023 15:24:14 +0200 Subject: [PATCH 17/24] Fix handling of `inLanguage` attribute in preview card processing (#27423) --- app/lib/link_details_extractor.rb | 3 ++- spec/lib/link_details_extractor_spec.rb | 10 ++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/app/lib/link_details_extractor.rb b/app/lib/link_details_extractor.rb index b95ec80519..a96612cab0 100644 --- a/app/lib/link_details_extractor.rb +++ b/app/lib/link_details_extractor.rb @@ -36,7 +36,8 @@ class LinkDetailsExtractor end def language - json['inLanguage'] + lang = json['inLanguage'] + lang.is_a?(Hash) ? (lang['alternateName'] || lang['name']) : lang end def type diff --git a/spec/lib/link_details_extractor_spec.rb b/spec/lib/link_details_extractor_spec.rb index 599bc4e6de..8c485cef2a 100644 --- a/spec/lib/link_details_extractor_spec.rb +++ b/spec/lib/link_details_extractor_spec.rb @@ -82,6 +82,10 @@ RSpec.describe LinkDetailsExtractor do 'name' => 'Pet News', 'url' => 'https://example.com', }, + 'inLanguage' => { + name: 'English', + alternateName: 'en', + }, }.to_json end @@ -115,6 +119,12 @@ RSpec.describe LinkDetailsExtractor do expect(subject.provider_name).to eq 'Pet News' end end + + describe '#language' do + it 'returns the language from structured data' do + expect(subject.language).to eq 'en' + end + end end context 'when is wrapped in CDATA tags' do From 00e92b40386e9ced25e5a6cad58f80dc78aca636 Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Mon, 16 Oct 2023 09:37:22 -0400 Subject: [PATCH 18/24] Add coverage for `CLI::Statuses` command (#25321) --- spec/lib/mastodon/cli/statuses_spec.rb | 22 ++++++++++++++++++++++ spec/rails_helper.rb | 6 ++++++ 2 files changed, 28 insertions(+) diff --git a/spec/lib/mastodon/cli/statuses_spec.rb b/spec/lib/mastodon/cli/statuses_spec.rb index 2430a88416..38ebcd9934 100644 --- a/spec/lib/mastodon/cli/statuses_spec.rb +++ b/spec/lib/mastodon/cli/statuses_spec.rb @@ -4,9 +4,31 @@ require 'rails_helper' require 'mastodon/cli/statuses' describe Mastodon::CLI::Statuses do + let(:cli) { described_class.new } + describe '.exit_on_failure?' do it 'returns true' do expect(described_class.exit_on_failure?).to be true end end + + describe '#remove', use_transactional_tests: false do + context 'with small batch size' do + let(:options) { { batch_size: 0 } } + + it 'exits with error message' do + expect { cli.invoke :remove, [], options }.to output( + a_string_including('Cannot run') + ).to_stdout.and raise_error(SystemExit) + end + end + + context 'with default batch size' do + it 'removes unreferenced statuses' do + expect { cli.invoke :remove }.to output( + a_string_including('Done after') + ).to_stdout + end + end + end end diff --git a/spec/rails_helper.rb b/spec/rails_helper.rb index 06e8418a0e..0bbf8fc52d 100644 --- a/spec/rails_helper.rb +++ b/spec/rails_helper.rb @@ -85,6 +85,12 @@ RSpec.configure do |config| config.include Redisable config.include SignedRequestHelpers, type: :request + config.around(:each, use_transactional_tests: false) do |example| + self.use_transactional_tests = false + example.run + self.use_transactional_tests = true + end + config.before :each, type: :cli do stub_stdout stub_reset_connection_pools From f8afa0f614a1ec4637d98baf6125a7fc1c47c23c Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Mon, 16 Oct 2023 09:38:27 -0400 Subject: [PATCH 19/24] Remove unused stub json ld context (#25454) --- .../requests/json-ld.activitystreams.txt | 391 ------------------ spec/fixtures/requests/json-ld.identity.txt | 100 ----- spec/fixtures/requests/json-ld.security.txt | 61 --- .../activitypub/linked_data_signature_spec.rb | 4 - spec/rails_helper.rb | 14 - ..._block_domain_from_account_service_spec.rb | 1 - 6 files changed, 571 deletions(-) delete mode 100644 spec/fixtures/requests/json-ld.activitystreams.txt delete mode 100644 spec/fixtures/requests/json-ld.identity.txt delete mode 100644 spec/fixtures/requests/json-ld.security.txt diff --git a/spec/fixtures/requests/json-ld.activitystreams.txt b/spec/fixtures/requests/json-ld.activitystreams.txt deleted file mode 100644 index 395797b272..0000000000 --- a/spec/fixtures/requests/json-ld.activitystreams.txt +++ /dev/null @@ -1,391 +0,0 @@ -HTTP/1.1 200 OK -Date: Tue, 01 May 2018 23:25:57 GMT -Content-Location: activitystreams.jsonld -Vary: negotiate,accept -TCN: choice -Last-Modified: Mon, 16 Apr 2018 00:28:23 GMT -ETag: "1eb0-569ec4caa97c0;d3-540ee27e0eec0" -Accept-Ranges: bytes -Content-Length: 7856 -Cache-Control: max-age=21600 -Expires: Wed, 02 May 2018 05:25:57 GMT -P3P: policyref="http://www.w3.org/2014/08/p3p.xml" -Access-Control-Allow-Origin: * -Content-Type: application/ld+json -Strict-Transport-Security: max-age=15552000; includeSubdomains; preload -Content-Security-Policy: upgrade-insecure-requests - -{ - "@context": { - "@vocab": "_:", - "xsd": "http://www.w3.org/2001/XMLSchema#", - "as": "https://www.w3.org/ns/activitystreams#", - "ldp": "http://www.w3.org/ns/ldp#", - "id": "@id", - "type": "@type", - "Accept": "as:Accept", - "Activity": "as:Activity", - "IntransitiveActivity": "as:IntransitiveActivity", - "Add": "as:Add", - "Announce": "as:Announce", - "Application": "as:Application", - "Arrive": "as:Arrive", - "Article": "as:Article", - "Audio": "as:Audio", - "Block": "as:Block", - "Collection": "as:Collection", - "CollectionPage": "as:CollectionPage", - "Relationship": "as:Relationship", - "Create": "as:Create", - "Delete": "as:Delete", - "Dislike": "as:Dislike", - "Document": "as:Document", - "Event": "as:Event", - "Follow": "as:Follow", - "Flag": "as:Flag", - "Group": "as:Group", - "Ignore": "as:Ignore", - "Image": "as:Image", - "Invite": "as:Invite", - "Join": "as:Join", - "Leave": "as:Leave", - "Like": "as:Like", - "Link": "as:Link", - "Mention": "as:Mention", - "Note": "as:Note", - "Object": "as:Object", - "Offer": "as:Offer", - "OrderedCollection": "as:OrderedCollection", - "OrderedCollectionPage": "as:OrderedCollectionPage", - "Organization": "as:Organization", - "Page": "as:Page", - "Person": "as:Person", - "Place": "as:Place", - "Profile": "as:Profile", - "Question": "as:Question", - "Reject": "as:Reject", - "Remove": "as:Remove", - "Service": "as:Service", - "TentativeAccept": "as:TentativeAccept", - "TentativeReject": "as:TentativeReject", - "Tombstone": "as:Tombstone", - "Undo": "as:Undo", - "Update": "as:Update", - "Video": "as:Video", - "View": "as:View", - "Listen": "as:Listen", - "Read": "as:Read", - "Move": "as:Move", - "Travel": "as:Travel", - "IsFollowing": "as:IsFollowing", - "IsFollowedBy": "as:IsFollowedBy", - "IsContact": "as:IsContact", - "IsMember": "as:IsMember", - "subject": { - "@id": "as:subject", - "@type": "@id" - }, - "relationship": { - "@id": "as:relationship", - "@type": "@id" - }, - "actor": { - "@id": "as:actor", - "@type": "@id" - }, - "attributedTo": { - "@id": "as:attributedTo", - "@type": "@id" - }, - "attachment": { - "@id": "as:attachment", - "@type": "@id" - }, - "bcc": { - "@id": "as:bcc", - "@type": "@id" - }, - "bto": { - "@id": "as:bto", - "@type": "@id" - }, - "cc": { - "@id": "as:cc", - "@type": "@id" - }, - "context": { - "@id": "as:context", - "@type": "@id" - }, - "current": { - "@id": "as:current", - "@type": "@id" - }, - "first": { - "@id": "as:first", - "@type": "@id" - }, - "generator": { - "@id": "as:generator", - "@type": "@id" - }, - "icon": { - "@id": "as:icon", - "@type": "@id" - }, - "image": { - "@id": "as:image", - "@type": "@id" - }, - "inReplyTo": { - "@id": "as:inReplyTo", - "@type": "@id" - }, - "items": { - "@id": "as:items", - "@type": "@id" - }, - "instrument": { - "@id": "as:instrument", - "@type": "@id" - }, - "orderedItems": { - "@id": "as:items", - "@type": "@id", - "@container": "@list" - }, - "last": { - "@id": "as:last", - "@type": "@id" - }, - "location": { - "@id": "as:location", - "@type": "@id" - }, - "next": { - "@id": "as:next", - "@type": "@id" - }, - "object": { - "@id": "as:object", - "@type": "@id" - }, - "oneOf": { - "@id": "as:oneOf", - "@type": "@id" - }, - "anyOf": { - "@id": "as:anyOf", - "@type": "@id" - }, - "closed": { - "@id": "as:closed", - "@type": "xsd:dateTime" - }, - "origin": { - "@id": "as:origin", - "@type": "@id" - }, - "accuracy": { - "@id": "as:accuracy", - "@type": "xsd:float" - }, - "prev": { - "@id": "as:prev", - "@type": "@id" - }, - "preview": { - "@id": "as:preview", - "@type": "@id" - }, - "replies": { - "@id": "as:replies", - "@type": "@id" - }, - "result": { - "@id": "as:result", - "@type": "@id" - }, - "audience": { - "@id": "as:audience", - "@type": "@id" - }, - "partOf": { - "@id": "as:partOf", - "@type": "@id" - }, - "tag": { - "@id": "as:tag", - "@type": "@id" - }, - "target": { - "@id": "as:target", - "@type": "@id" - }, - "to": { - "@id": "as:to", - "@type": "@id" - }, - "url": { - "@id": "as:url", - "@type": "@id" - }, - "altitude": { - "@id": "as:altitude", - "@type": "xsd:float" - }, - "content": "as:content", - "contentMap": { - "@id": "as:content", - "@container": "@language" - }, - "name": "as:name", - "nameMap": { - "@id": "as:name", - "@container": "@language" - }, - "duration": { - "@id": "as:duration", - "@type": "xsd:duration" - }, - "endTime": { - "@id": "as:endTime", - "@type": "xsd:dateTime" - }, - "height": { - "@id": "as:height", - "@type": "xsd:nonNegativeInteger" - }, - "href": { - "@id": "as:href", - "@type": "@id" - }, - "hreflang": "as:hreflang", - "latitude": { - "@id": "as:latitude", - "@type": "xsd:float" - }, - "longitude": { - "@id": "as:longitude", - "@type": "xsd:float" - }, - "mediaType": "as:mediaType", - "published": { - "@id": "as:published", - "@type": "xsd:dateTime" - }, - "radius": { - "@id": "as:radius", - "@type": "xsd:float" - }, - "rel": "as:rel", - "startIndex": { - "@id": "as:startIndex", - "@type": "xsd:nonNegativeInteger" - }, - "startTime": { - "@id": "as:startTime", - "@type": "xsd:dateTime" - }, - "summary": "as:summary", - "summaryMap": { - "@id": "as:summary", - "@container": "@language" - }, - "totalItems": { - "@id": "as:totalItems", - "@type": "xsd:nonNegativeInteger" - }, - "units": "as:units", - "updated": { - "@id": "as:updated", - "@type": "xsd:dateTime" - }, - "width": { - "@id": "as:width", - "@type": "xsd:nonNegativeInteger" - }, - "describes": { - "@id": "as:describes", - "@type": "@id" - }, - "formerType": { - "@id": "as:formerType", - "@type": "@id" - }, - "deleted": { - "@id": "as:deleted", - "@type": "xsd:dateTime" - }, - "inbox": { - "@id": "ldp:inbox", - "@type": "@id" - }, - "outbox": { - "@id": "as:outbox", - "@type": "@id" - }, - "following": { - "@id": "as:following", - "@type": "@id" - }, - "followers": { - "@id": "as:followers", - "@type": "@id" - }, - "streams": { - "@id": "as:streams", - "@type": "@id" - }, - "preferredUsername": "as:preferredUsername", - "endpoints": { - "@id": "as:endpoints", - "@type": "@id" - }, - "uploadMedia": { - "@id": "as:uploadMedia", - "@type": "@id" - }, - "proxyUrl": { - "@id": "as:proxyUrl", - "@type": "@id" - }, - "liked": { - "@id": "as:liked", - "@type": "@id" - }, - "oauthAuthorizationEndpoint": { - "@id": "as:oauthAuthorizationEndpoint", - "@type": "@id" - }, - "oauthTokenEndpoint": { - "@id": "as:oauthTokenEndpoint", - "@type": "@id" - }, - "provideClientKey": { - "@id": "as:provideClientKey", - "@type": "@id" - }, - "signClientKey": { - "@id": "as:signClientKey", - "@type": "@id" - }, - "sharedInbox": { - "@id": "as:sharedInbox", - "@type": "@id" - }, - "Public": { - "@id": "as:Public", - "@type": "@id" - }, - "source": "as:source", - "likes": { - "@id": "as:likes", - "@type": "@id" - }, - "shares": { - "@id": "as:shares", - "@type": "@id" - } - } -} diff --git a/spec/fixtures/requests/json-ld.identity.txt b/spec/fixtures/requests/json-ld.identity.txt deleted file mode 100644 index 8810526cb1..0000000000 --- a/spec/fixtures/requests/json-ld.identity.txt +++ /dev/null @@ -1,100 +0,0 @@ -HTTP/1.1 200 OK -Accept-Ranges: bytes -Access-Control-Allow-Headers: DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Accept-Encoding -Access-Control-Allow-Origin: * -Content-Type: application/ld+json -Date: Tue, 01 May 2018 23:28:21 GMT -Etag: "e26-547a6fc75b04a-gzip" -Last-Modified: Fri, 03 Feb 2017 21:30:09 GMT -Server: Apache/2.4.7 (Ubuntu) -Vary: Accept-Encoding -Transfer-Encoding: chunked - -{ - "@context": { - "id": "@id", - "type": "@type", - - "cred": "https://w3id.org/credentials#", - "dc": "http://purl.org/dc/terms/", - "identity": "https://w3id.org/identity#", - "perm": "https://w3id.org/permissions#", - "ps": "https://w3id.org/payswarm#", - "rdf": "http://www.w3.org/1999/02/22-rdf-syntax-ns#", - "rdfs": "http://www.w3.org/2000/01/rdf-schema#", - "sec": "https://w3id.org/security#", - "schema": "http://schema.org/", - "xsd": "http://www.w3.org/2001/XMLSchema#", - - "Group": "https://www.w3.org/ns/activitystreams#Group", - - "claim": {"@id": "cred:claim", "@type": "@id"}, - "credential": {"@id": "cred:credential", "@type": "@id"}, - "issued": {"@id": "cred:issued", "@type": "xsd:dateTime"}, - "issuer": {"@id": "cred:issuer", "@type": "@id"}, - "recipient": {"@id": "cred:recipient", "@type": "@id"}, - "Credential": "cred:Credential", - "CryptographicKeyCredential": "cred:CryptographicKeyCredential", - - "about": {"@id": "schema:about", "@type": "@id"}, - "address": {"@id": "schema:address", "@type": "@id"}, - "addressCountry": "schema:addressCountry", - "addressLocality": "schema:addressLocality", - "addressRegion": "schema:addressRegion", - "comment": "rdfs:comment", - "created": {"@id": "dc:created", "@type": "xsd:dateTime"}, - "creator": {"@id": "dc:creator", "@type": "@id"}, - "description": "schema:description", - "email": "schema:email", - "familyName": "schema:familyName", - "givenName": "schema:givenName", - "image": {"@id": "schema:image", "@type": "@id"}, - "label": "rdfs:label", - "name": "schema:name", - "postalCode": "schema:postalCode", - "streetAddress": "schema:streetAddress", - "title": "dc:title", - "url": {"@id": "schema:url", "@type": "@id"}, - "Person": "schema:Person", - "PostalAddress": "schema:PostalAddress", - "Organization": "schema:Organization", - - "identityService": {"@id": "identity:identityService", "@type": "@id"}, - "idp": {"@id": "identity:idp", "@type": "@id"}, - "Identity": "identity:Identity", - - "paymentProcessor": "ps:processor", - "preferences": {"@id": "ps:preferences", "@type": "@vocab"}, - - "cipherAlgorithm": "sec:cipherAlgorithm", - "cipherData": "sec:cipherData", - "cipherKey": "sec:cipherKey", - "digestAlgorithm": "sec:digestAlgorithm", - "digestValue": "sec:digestValue", - "domain": "sec:domain", - "expires": {"@id": "sec:expiration", "@type": "xsd:dateTime"}, - "initializationVector": "sec:initializationVector", - "member": {"@id": "schema:member", "@type": "@id"}, - "memberOf": {"@id": "schema:memberOf", "@type": "@id"}, - "nonce": "sec:nonce", - "normalizationAlgorithm": "sec:normalizationAlgorithm", - "owner": {"@id": "sec:owner", "@type": "@id"}, - "password": "sec:password", - "privateKey": {"@id": "sec:privateKey", "@type": "@id"}, - "privateKeyPem": "sec:privateKeyPem", - "publicKey": {"@id": "sec:publicKey", "@type": "@id"}, - "publicKeyPem": "sec:publicKeyPem", - "publicKeyService": {"@id": "sec:publicKeyService", "@type": "@id"}, - "revoked": {"@id": "sec:revoked", "@type": "xsd:dateTime"}, - "signature": "sec:signature", - "signatureAlgorithm": "sec:signatureAlgorithm", - "signatureValue": "sec:signatureValue", - "CryptographicKey": "sec:Key", - "EncryptedMessage": "sec:EncryptedMessage", - "GraphSignature2012": "sec:GraphSignature2012", - "LinkedDataSignature2015": "sec:LinkedDataSignature2015", - - "accessControl": {"@id": "perm:accessControl", "@type": "@id"}, - "writePermission": {"@id": "perm:writePermission", "@type": "@id"} - } -} diff --git a/spec/fixtures/requests/json-ld.security.txt b/spec/fixtures/requests/json-ld.security.txt deleted file mode 100644 index 0d29903e60..0000000000 --- a/spec/fixtures/requests/json-ld.security.txt +++ /dev/null @@ -1,61 +0,0 @@ -HTTP/1.1 200 OK -Accept-Ranges: bytes -Access-Control-Allow-Headers: DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Accept-Encoding -Access-Control-Allow-Origin: * -Content-Type: application/ld+json -Date: Wed, 02 May 2018 16:25:32 GMT -Etag: "7e3-5651ec0f7c5ed-gzip" -Last-Modified: Tue, 13 Feb 2018 21:34:04 GMT -Server: Apache/2.4.7 (Ubuntu) -Vary: Accept-Encoding -Content-Length: 2019 - -{ - "@context": { - "id": "@id", - "type": "@type", - - "dc": "http://purl.org/dc/terms/", - "sec": "https://w3id.org/security#", - "xsd": "http://www.w3.org/2001/XMLSchema#", - - "EcdsaKoblitzSignature2016": "sec:EcdsaKoblitzSignature2016", - "Ed25519Signature2018": "sec:Ed25519Signature2018", - "EncryptedMessage": "sec:EncryptedMessage", - "GraphSignature2012": "sec:GraphSignature2012", - "LinkedDataSignature2015": "sec:LinkedDataSignature2015", - "LinkedDataSignature2016": "sec:LinkedDataSignature2016", - "CryptographicKey": "sec:Key", - - "authenticationTag": "sec:authenticationTag", - "canonicalizationAlgorithm": "sec:canonicalizationAlgorithm", - "cipherAlgorithm": "sec:cipherAlgorithm", - "cipherData": "sec:cipherData", - "cipherKey": "sec:cipherKey", - "created": {"@id": "dc:created", "@type": "xsd:dateTime"}, - "creator": {"@id": "dc:creator", "@type": "@id"}, - "digestAlgorithm": "sec:digestAlgorithm", - "digestValue": "sec:digestValue", - "domain": "sec:domain", - "encryptionKey": "sec:encryptionKey", - "expiration": {"@id": "sec:expiration", "@type": "xsd:dateTime"}, - "expires": {"@id": "sec:expiration", "@type": "xsd:dateTime"}, - "initializationVector": "sec:initializationVector", - "iterationCount": "sec:iterationCount", - "nonce": "sec:nonce", - "normalizationAlgorithm": "sec:normalizationAlgorithm", - "owner": {"@id": "sec:owner", "@type": "@id"}, - "password": "sec:password", - "privateKey": {"@id": "sec:privateKey", "@type": "@id"}, - "privateKeyPem": "sec:privateKeyPem", - "publicKey": {"@id": "sec:publicKey", "@type": "@id"}, - "publicKeyBase58": "sec:publicKeyBase58", - "publicKeyPem": "sec:publicKeyPem", - "publicKeyService": {"@id": "sec:publicKeyService", "@type": "@id"}, - "revoked": {"@id": "sec:revoked", "@type": "xsd:dateTime"}, - "salt": "sec:salt", - "signature": "sec:signature", - "signatureAlgorithm": "sec:signingAlgorithm", - "signatureValue": "sec:signatureValue" - } -} diff --git a/spec/lib/activitypub/linked_data_signature_spec.rb b/spec/lib/activitypub/linked_data_signature_spec.rb index 6a6ad1a706..d5b713b347 100644 --- a/spec/lib/activitypub/linked_data_signature_spec.rb +++ b/spec/lib/activitypub/linked_data_signature_spec.rb @@ -18,10 +18,6 @@ RSpec.describe ActivityPub::LinkedDataSignature do let(:json) { raw_json.merge('signature' => signature) } - before do - stub_jsonld_contexts! - end - describe '#verify_actor!' do context 'when signature matches' do let(:raw_signature) do diff --git a/spec/rails_helper.rb b/spec/rails_helper.rb index 0bbf8fc52d..8d9677f6ce 100644 --- a/spec/rails_helper.rb +++ b/spec/rails_helper.rb @@ -100,14 +100,6 @@ RSpec.configure do |config| Capybara.current_driver = :rack_test end - config.before :each, type: :controller do - stub_jsonld_contexts! - end - - config.before :each, type: :service do - stub_jsonld_contexts! - end - config.before :suite do if RUN_SYSTEM_SPECS Webpacker.compile @@ -198,9 +190,3 @@ def stub_reset_connection_pools allow(ActiveRecord::Base).to receive(:establish_connection) allow(RedisConfiguration).to receive(:establish_pool) end - -def stub_jsonld_contexts! - stub_request(:get, 'https://www.w3.org/ns/activitystreams').to_return(request_fixture('json-ld.activitystreams.txt')) - stub_request(:get, 'https://w3id.org/identity/v1').to_return(request_fixture('json-ld.identity.txt')) - stub_request(:get, 'https://w3id.org/security/v1').to_return(request_fixture('json-ld.security.txt')) -end diff --git a/spec/services/after_block_domain_from_account_service_spec.rb b/spec/services/after_block_domain_from_account_service_spec.rb index 9bfaa35807..05af125997 100644 --- a/spec/services/after_block_domain_from_account_service_spec.rb +++ b/spec/services/after_block_domain_from_account_service_spec.rb @@ -9,7 +9,6 @@ RSpec.describe AfterBlockDomainFromAccountService, type: :service do let!(:alice) { Fabricate(:account, username: 'alice') } before do - stub_jsonld_contexts! allow(ActivityPub::DeliveryWorker).to receive(:perform_async) end From f5bc1f20e2ba4bafcd8e57c01413c60002696b58 Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Mon, 16 Oct 2023 09:41:23 -0400 Subject: [PATCH 20/24] Add coverage for `ExistingUsernameValidator` (#25592) Co-authored-by: Claire --- app/validators/existing_username_validator.rb | 43 ++++++---- .../existing_username_validator_spec.rb | 83 +++++++++++++++++++ 2 files changed, 112 insertions(+), 14 deletions(-) create mode 100644 spec/validators/existing_username_validator_spec.rb diff --git a/app/validators/existing_username_validator.rb b/app/validators/existing_username_validator.rb index 037d92f39b..09d53ca680 100644 --- a/app/validators/existing_username_validator.rb +++ b/app/validators/existing_username_validator.rb @@ -2,25 +2,40 @@ class ExistingUsernameValidator < ActiveModel::EachValidator def validate_each(record, attribute, value) - return if value.blank? + @value = value + return if @value.blank? - usernames_and_domains = value.split(',').filter_map do |str| - username, domain = str.strip.gsub(/\A@/, '').split('@', 2) + if options[:multiple] + record.errors.add(attribute, not_found_multiple_message) if usernames_with_no_accounts.any? + elsif usernames_with_no_accounts.any? || usernames_and_domains.size > 1 + record.errors.add(attribute, not_found_message) + end + end + + private + + def usernames_and_domains + @value.split(',').filter_map do |string| + username, domain = string.strip.gsub(/\A@/, '').split('@', 2) domain = nil if TagManager.instance.local_domain?(domain) next if username.blank? - [str, username, domain] - end - - usernames_with_no_accounts = usernames_and_domains.filter_map do |(str, username, domain)| - str unless Account.find_remote(username, domain) - end - - if options[:multiple] - record.errors.add(attribute, I18n.t('existing_username_validator.not_found_multiple', usernames: usernames_with_no_accounts.join(', '))) if usernames_with_no_accounts.any? - elsif usernames_with_no_accounts.any? || usernames_and_domains.size > 1 - record.errors.add(attribute, I18n.t('existing_username_validator.not_found')) + [string, username, domain] end end + + def usernames_with_no_accounts + usernames_and_domains.filter_map do |(string, username, domain)| + string unless Account.find_remote(username, domain) + end + end + + def not_found_multiple_message + I18n.t('existing_username_validator.not_found_multiple', usernames: usernames_with_no_accounts.join(', ')) + end + + def not_found_message + I18n.t('existing_username_validator.not_found') + end end diff --git a/spec/validators/existing_username_validator_spec.rb b/spec/validators/existing_username_validator_spec.rb new file mode 100644 index 0000000000..4f1dd55a17 --- /dev/null +++ b/spec/validators/existing_username_validator_spec.rb @@ -0,0 +1,83 @@ +# frozen_string_literal: true + +require 'rails_helper' + +describe ExistingUsernameValidator do + let(:record_class) do + Class.new do + include ActiveModel::Validations + attr_accessor :contact, :friends + + def self.name + 'Record' + end + + validates :contact, existing_username: true + validates :friends, existing_username: { multiple: true } + end + end + let(:record) { record_class.new } + + describe '#validate_each' do + context 'with a nil value' do + it 'does not add errors' do + record.contact = nil + + expect(record).to be_valid + expect(record.errors).to be_empty + end + end + + context 'when there are no accounts' do + it 'adds errors to the record' do + record.contact = 'user@example.com' + + expect(record).to_not be_valid + expect(record.errors.first.attribute).to eq(:contact) + expect(record.errors.first.type).to eq I18n.t('existing_username_validator.not_found') + end + end + + context 'when there are accounts' do + before { Fabricate(:account, domain: 'example.com', username: 'user') } + + context 'when the value does not match' do + it 'adds errors to the record' do + record.contact = 'friend@other.host' + + expect(record).to_not be_valid + expect(record.errors.first.attribute).to eq(:contact) + expect(record.errors.first.type).to eq I18n.t('existing_username_validator.not_found') + end + + context 'when multiple is true' do + it 'adds errors to the record' do + record.friends = 'friend@other.host' + + expect(record).to_not be_valid + expect(record.errors.first.attribute).to eq(:friends) + expect(record.errors.first.type).to eq I18n.t('existing_username_validator.not_found_multiple', usernames: 'friend@other.host') + end + end + end + + context 'when the value does match' do + it 'does not add errors to the record' do + record.contact = 'user@example.com' + + expect(record).to be_valid + expect(record.errors).to be_empty + end + + context 'when multiple is true' do + it 'does not add errors to the record' do + record.friends = 'user@example.com' + + expect(record).to be_valid + expect(record.errors).to be_empty + end + end + end + end + end +end From d9caa6ed240d02e4d0cd385ac558abfdd311efba Mon Sep 17 00:00:00 2001 From: Daniel M Brasil Date: Mon, 16 Oct 2023 10:49:12 -0300 Subject: [PATCH 21/24] Migrate to request specs in `/api/v1/admin/accounts` (#25563) --- .rubocop_todo.yml | 2 - .../api/v1/admin/accounts_controller_spec.rb | 174 -------- spec/requests/api/v1/admin/accounts_spec.rb | 401 ++++++++++++++++++ 3 files changed, 401 insertions(+), 176 deletions(-) delete mode 100644 spec/controllers/api/v1/admin/accounts_controller_spec.rb create mode 100644 spec/requests/api/v1/admin/accounts_spec.rb diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index d58bee4ba8..29d9f484c0 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -178,7 +178,6 @@ RSpec/LetSetup: - 'spec/controllers/admin/reports/actions_controller_spec.rb' - 'spec/controllers/admin/statuses_controller_spec.rb' - 'spec/controllers/api/v1/accounts/statuses_controller_spec.rb' - - 'spec/controllers/api/v1/admin/accounts_controller_spec.rb' - 'spec/controllers/api/v1/filters_controller_spec.rb' - 'spec/controllers/api/v2/admin/accounts_controller_spec.rb' - 'spec/controllers/api/v2/filters/keywords_controller_spec.rb' @@ -415,7 +414,6 @@ Rails/SkipsModelValidations: - 'lib/mastodon/cli/accounts.rb' - 'lib/mastodon/cli/main.rb' - 'lib/mastodon/cli/maintenance.rb' - - 'spec/controllers/api/v1/admin/accounts_controller_spec.rb' - 'spec/lib/activitypub/activity/follow_spec.rb' - 'spec/services/follow_service_spec.rb' - 'spec/services/update_account_service_spec.rb' diff --git a/spec/controllers/api/v1/admin/accounts_controller_spec.rb b/spec/controllers/api/v1/admin/accounts_controller_spec.rb deleted file mode 100644 index 4b56b25479..0000000000 --- a/spec/controllers/api/v1/admin/accounts_controller_spec.rb +++ /dev/null @@ -1,174 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' - -RSpec.describe Api::V1::Admin::AccountsController do - render_views - - let(:role) { UserRole.find_by(name: 'Moderator') } - let(:user) { Fabricate(:user, role: role) } - let(:scopes) { 'admin:read admin:write' } - let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) } - let(:account) { Fabricate(:account) } - - before do - allow(controller).to receive(:doorkeeper_token) { token } - end - - describe 'GET #index' do - let!(:remote_account) { Fabricate(:account, domain: 'example.org') } - let!(:other_remote_account) { Fabricate(:account, domain: 'foo.bar') } - let!(:suspended_account) { Fabricate(:account, suspended: true) } - let!(:suspended_remote) { Fabricate(:account, domain: 'foo.bar', suspended: true) } - let!(:disabled_account) { Fabricate(:user, disabled: true).account } - let!(:pending_account) { Fabricate(:user, approved: false).account } - let!(:admin_account) { user.account } - - let(:params) { {} } - - before do - pending_account.user.update(approved: false) - get :index, params: params - end - - it_behaves_like 'forbidden for wrong scope', 'write:statuses' - it_behaves_like 'forbidden for wrong role', '' - - [ - [{ active: 'true', local: 'true', staff: 'true' }, [:admin_account]], - [{ by_domain: 'example.org', remote: 'true' }, [:remote_account]], - [{ suspended: 'true' }, [:suspended_account]], - [{ disabled: 'true' }, [:disabled_account]], - [{ pending: 'true' }, [:pending_account]], - ].each do |params, expected_results| - context "when called with #{params.inspect}" do - let(:params) { params } - - it "returns the correct accounts (#{expected_results.inspect})", :aggregate_failures do - expect(response).to have_http_status(200) - - json = body_as_json - - expect(json.map { |a| a[:id].to_i }).to eq(expected_results.map { |symbol| send(symbol).id }) - end - end - end - end - - describe 'GET #show' do - before do - get :show, params: { id: account.id } - end - - it_behaves_like 'forbidden for wrong scope', 'write:statuses' - it_behaves_like 'forbidden for wrong role', '' - - it 'returns http success' do - expect(response).to have_http_status(200) - end - end - - describe 'POST #approve' do - before do - account.user.update(approved: false) - post :approve, params: { id: account.id } - end - - it_behaves_like 'forbidden for wrong scope', 'write:statuses' - it_behaves_like 'forbidden for wrong role', '' - - it 'approves user', :aggregate_failures do - expect(response).to have_http_status(200) - expect(account.reload.user_approved?).to be true - - log_item = Admin::ActionLog.last - - expect(log_item).to_not be_nil - expect(log_item.action).to eq :approve - expect(log_item.account_id).to eq user.account_id - expect(log_item.target_id).to eq account.user.id - end - end - - describe 'POST #reject' do - before do - account.user.update(approved: false) - post :reject, params: { id: account.id } - end - - it_behaves_like 'forbidden for wrong scope', 'write:statuses' - it_behaves_like 'forbidden for wrong role', '' - - it 'removes user', :aggregate_failures do - expect(response).to have_http_status(200) - expect(User.where(id: account.user.id).count).to eq 0 - - log_item = Admin::ActionLog.last - - expect(log_item).to_not be_nil - expect(log_item.action).to eq :reject - expect(log_item.account_id).to eq user.account_id - expect(log_item.target_id).to eq account.user.id - end - end - - describe 'POST #enable' do - before do - account.user.update(disabled: true) - post :enable, params: { id: account.id } - end - - it_behaves_like 'forbidden for wrong scope', 'write:statuses' - it_behaves_like 'forbidden for wrong role', '' - - it 'enables user', :aggregate_failures do - expect(response).to have_http_status(200) - expect(account.reload.user_disabled?).to be false - end - end - - describe 'POST #unsuspend' do - before do - account.suspend! - post :unsuspend, params: { id: account.id } - end - - it_behaves_like 'forbidden for wrong scope', 'write:statuses' - it_behaves_like 'forbidden for wrong role', '' - - it 'unsuspends account', :aggregate_failures do - expect(response).to have_http_status(200) - expect(account.reload.suspended?).to be false - end - end - - describe 'POST #unsensitive' do - before do - account.touch(:sensitized_at) - post :unsensitive, params: { id: account.id } - end - - it_behaves_like 'forbidden for wrong scope', 'write:statuses' - it_behaves_like 'forbidden for wrong role', '' - - it 'unsensitizes account', :aggregate_failures do - expect(response).to have_http_status(200) - expect(account.reload.sensitized?).to be false - end - end - - describe 'POST #unsilence' do - before do - account.touch(:silenced_at) - post :unsilence, params: { id: account.id } - end - - it_behaves_like 'forbidden for wrong scope', 'write:statuses' - it_behaves_like 'forbidden for wrong role', '' - - it 'unsilences account', :aggregate_failures do - expect(response).to have_http_status(200) - expect(account.reload.silenced?).to be false - end - end -end diff --git a/spec/requests/api/v1/admin/accounts_spec.rb b/spec/requests/api/v1/admin/accounts_spec.rb new file mode 100644 index 0000000000..8e158f623d --- /dev/null +++ b/spec/requests/api/v1/admin/accounts_spec.rb @@ -0,0 +1,401 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe 'Accounts' do + let(:role) { UserRole.find_by(name: 'Admin') } + let(:user) { Fabricate(:user, role: role) } + let(:scopes) { 'admin:read:accounts admin:write:accounts' } + let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) } + let(:headers) { { 'Authorization' => "Bearer #{token.token}" } } + + describe 'GET /api/v1/admin/accounts' do + subject do + get '/api/v1/admin/accounts', headers: headers, params: params + end + + shared_examples 'a successful request' do + it 'returns the correct accounts', :aggregate_failures do + subject + + expect(response).to have_http_status(200) + expect(body_as_json.pluck(:id)).to match_array(expected_results.map { |a| a.id.to_s }) + end + end + + let!(:remote_account) { Fabricate(:account, domain: 'example.org') } + let!(:suspended_account) { Fabricate(:account, suspended: true) } + let!(:disabled_account) { Fabricate(:user, disabled: true).account } + let!(:pending_account) { Fabricate(:user, approved: false).account } + let!(:admin_account) { user.account } + let(:params) { {} } + + it_behaves_like 'forbidden for wrong scope', 'read read:accounts admin:write admin:write:accounts' + it_behaves_like 'forbidden for wrong role', '' + + context 'when requesting active local staff accounts' do + let(:expected_results) { [admin_account] } + let(:params) { { active: 'true', local: 'true', staff: 'true' } } + + it_behaves_like 'a successful request' + end + + context 'when requesting remote accounts from a specified domain' do + let(:expected_results) { [remote_account] } + let(:params) { { by_domain: 'example.org', remote: 'true' } } + + before do + Fabricate(:account, domain: 'foo.bar') + end + + it_behaves_like 'a successful request' + end + + context 'when requesting suspended accounts' do + let(:expected_results) { [suspended_account] } + let(:params) { { suspended: 'true' } } + + before do + Fabricate(:account, domain: 'foo.bar', suspended: true) + end + + it_behaves_like 'a successful request' + end + + context 'when requesting disabled accounts' do + let(:expected_results) { [disabled_account] } + let(:params) { { disabled: 'true' } } + + it_behaves_like 'a successful request' + end + + context 'when requesting pending accounts' do + let(:expected_results) { [pending_account] } + let(:params) { { pending: 'true' } } + + before do + pending_account.user.update(approved: false) + end + + it_behaves_like 'a successful request' + end + + context 'when no parameter is given' do + let(:expected_results) { [disabled_account, pending_account, admin_account] } + + it_behaves_like 'a successful request' + end + + context 'with limit param' do + let(:params) { { limit: 2 } } + + it 'returns only the requested number of accounts', :aggregate_failures do + subject + + expect(response).to have_http_status(200) + expect(body_as_json.size).to eq(params[:limit]) + end + end + end + + describe 'GET /api/v1/admin/accounts/:id' do + subject do + get "/api/v1/admin/accounts/#{account.id}", headers: headers + end + + let(:account) { Fabricate(:account) } + + it_behaves_like 'forbidden for wrong scope', 'read read:accounts admin:write admin:write:accounts' + it_behaves_like 'forbidden for wrong role', '' + + it 'returns the requested account successfully', :aggregate_failures do + subject + + expect(response).to have_http_status(200) + expect(body_as_json).to match( + a_hash_including(id: account.id.to_s, username: account.username, email: account.user.email) + ) + end + + context 'when the account is not found' do + it 'returns http not found' do + get '/api/v1/admin/accounts/-1', headers: headers + + expect(response).to have_http_status(404) + end + end + end + + describe 'POST /api/v1/admin/accounts/:id/approve' do + subject do + post "/api/v1/admin/accounts/#{account.id}/approve", headers: headers + end + + let(:account) { Fabricate(:account) } + + context 'when the account is pending' do + before do + account.user.update(approved: false) + end + + it_behaves_like 'forbidden for wrong scope', 'write write:accounts read admin:read' + it_behaves_like 'forbidden for wrong role', '' + + it 'approves the user successfully', :aggregate_failures do + subject + + expect(response).to have_http_status(200) + expect(account.reload.user_approved?).to be(true) + end + + it 'logs action', :aggregate_failures do + subject + + log_item = Admin::ActionLog.last + + expect(log_item).to be_present + expect(log_item.action).to eq :approve + expect(log_item.account_id).to eq user.account_id + expect(log_item.target_id).to eq account.user.id + end + end + + context 'when the account is already approved' do + it 'returns http forbidden' do + subject + + expect(response).to have_http_status(403) + end + end + + context 'when the account is not found' do + it 'returns http not found' do + post '/api/v1/admin/accounts/-1/approve', headers: headers + + expect(response).to have_http_status(404) + end + end + end + + describe 'POST /api/v1/admin/accounts/:id/reject' do + subject do + post "/api/v1/admin/accounts/#{account.id}/reject", headers: headers + end + + let(:account) { Fabricate(:account) } + + context 'when the account is pending' do + before do + account.user.update(approved: false) + end + + it_behaves_like 'forbidden for wrong scope', 'write write:accounts read admin:read' + it_behaves_like 'forbidden for wrong role', '' + + it 'removes the user successfully', :aggregate_failures do + subject + + expect(response).to have_http_status(200) + expect(User.where(id: account.user.id)).to_not exist + end + + it 'logs action', :aggregate_failures do + subject + + log_item = Admin::ActionLog.last + + expect(log_item).to be_present + expect(log_item.action).to eq :reject + expect(log_item.account_id).to eq user.account_id + expect(log_item.target_id).to eq account.user.id + end + end + + context 'when account is already approved' do + it 'returns http forbidden' do + subject + + expect(response).to have_http_status(403) + end + end + + context 'when the account is not found' do + it 'returns http not found' do + post '/api/v1/admin/accounts/-1/reject', headers: headers + + expect(response).to have_http_status(404) + end + end + end + + describe 'POST /api/v1/admin/accounts/:id/enable' do + subject do + post "/api/v1/admin/accounts/#{account.id}/enable", headers: headers + end + + let(:account) { Fabricate(:account) } + + before do + account.user.update(disabled: true) + end + + it_behaves_like 'forbidden for wrong scope', 'write write:accounts read admin:read' + it_behaves_like 'forbidden for wrong role', '' + + it 'enables the user successfully', :aggregate_failures do + subject + + expect(response).to have_http_status(200) + expect(account.reload.user_disabled?).to be false + end + + context 'when the account is not found' do + it 'returns http not found' do + post '/api/v1/admin/accounts/-1/enable', headers: headers + + expect(response).to have_http_status(404) + end + end + end + + describe 'POST /api/v1/admin/accounts/:id/unsuspend' do + subject do + post "/api/v1/admin/accounts/#{account.id}/unsuspend", headers: headers + end + + let(:account) { Fabricate(:account) } + + context 'when the account is suspended' do + before do + account.suspend! + end + + it_behaves_like 'forbidden for wrong scope', 'write write:accounts read admin:read' + it_behaves_like 'forbidden for wrong role', '' + + it 'unsuspends the account successfully', :aggregate_failures do + subject + + expect(response).to have_http_status(200) + expect(account.reload.suspended?).to be false + end + end + + context 'when the account is not suspended' do + it 'returns http forbidden' do + subject + + expect(response).to have_http_status(403) + end + end + + context 'when the account is not found' do + it 'returns http not found' do + post '/api/v1/admin/accounts/-1/unsuspend', headers: headers + + expect(response).to have_http_status(404) + end + end + end + + describe 'POST /api/v1/admin/accounts/:id/unsensitive' do + subject do + post "/api/v1/admin/accounts/#{account.id}/unsensitive", headers: headers + end + + let(:account) { Fabricate(:account) } + + before do + account.update(sensitized_at: 10.days.ago) + end + + it_behaves_like 'forbidden for wrong scope', 'write write:accounts read admin:read' + it_behaves_like 'forbidden for wrong role', '' + + it 'unsensitizes the account successfully', :aggregate_failures do + subject + + expect(response).to have_http_status(200) + expect(account.reload.sensitized?).to be false + end + + context 'when the account is not found' do + it 'returns http not found' do + post '/api/v1/admin/accounts/-1/unsensitive', headers: headers + + expect(response).to have_http_status(404) + end + end + end + + describe 'POST /api/v1/admin/accounts/:id/unsilence' do + subject do + post "/api/v1/admin/accounts/#{account.id}/unsilence", headers: headers + end + + let(:account) { Fabricate(:account) } + + before do + account.update(silenced_at: 3.days.ago) + end + + it_behaves_like 'forbidden for wrong scope', 'write write:accounts read admin:read' + it_behaves_like 'forbidden for wrong role', '' + + it 'unsilences the account successfully', :aggregate_failures do + subject + + expect(response).to have_http_status(200) + expect(account.reload.silenced?).to be false + end + + context 'when the account is not found' do + it 'returns http not found' do + post '/api/v1/admin/accounts/-1/unsilence', headers: headers + + expect(response).to have_http_status(404) + end + end + end + + describe 'DELETE /api/v1/admin/accounts/:id' do + subject do + delete "/api/v1/admin/accounts/#{account.id}", headers: headers + end + + let(:account) { Fabricate(:account) } + + context 'when account is suspended' do + before do + account.suspend! + end + + it_behaves_like 'forbidden for wrong scope', 'write write:accounts read admin:read' + it_behaves_like 'forbidden for wrong role', '' + + it 'deletes the account successfully', :aggregate_failures do + allow(Admin::AccountDeletionWorker).to receive(:perform_async) + subject + + expect(response).to have_http_status(200) + expect(Admin::AccountDeletionWorker).to have_received(:perform_async).with(account.id).once + end + end + + context 'when account is not suspended' do + it 'returns http forbidden' do + subject + + expect(response).to have_http_status(403) + end + end + + context 'when the account is not found' do + it 'returns http not found' do + delete '/api/v1/admin/accounts/-1', headers: headers + + expect(response).to have_http_status(404) + end + end + end +end From 8e6116503d697ed3b9206ae10dd913f8adf64ae8 Mon Sep 17 00:00:00 2001 From: Daniel M Brasil Date: Mon, 16 Oct 2023 11:03:35 -0300 Subject: [PATCH 22/24] Migrate to request specs in `/api/v1/blocks` (#25517) --- .../api/v1/blocks_controller_spec.rb | 68 ---------------- spec/requests/api/v1/blocks_spec.rb | 80 +++++++++++++++++++ 2 files changed, 80 insertions(+), 68 deletions(-) delete mode 100644 spec/controllers/api/v1/blocks_controller_spec.rb create mode 100644 spec/requests/api/v1/blocks_spec.rb diff --git a/spec/controllers/api/v1/blocks_controller_spec.rb b/spec/controllers/api/v1/blocks_controller_spec.rb deleted file mode 100644 index ba63560a96..0000000000 --- a/spec/controllers/api/v1/blocks_controller_spec.rb +++ /dev/null @@ -1,68 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' - -RSpec.describe Api::V1::BlocksController do - render_views - - let(:user) { Fabricate(:user) } - let(:scopes) { 'read:blocks' } - let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) } - - before { allow(controller).to receive(:doorkeeper_token) { token } } - - describe 'GET #index' 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', :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', :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', :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', :aggregate_failures do - block = Fabricate(:block, account: user.account) - 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 - let(:scopes) { 'write:blocks' } - - it 'returns http forbidden' do - get :index - expect(response).to have_http_status(403) - end - end - end -end diff --git a/spec/requests/api/v1/blocks_spec.rb b/spec/requests/api/v1/blocks_spec.rb new file mode 100644 index 0000000000..62543157c3 --- /dev/null +++ b/spec/requests/api/v1/blocks_spec.rb @@ -0,0 +1,80 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe 'Blocks' do + let(:user) { Fabricate(:user) } + let(:scopes) { 'read:blocks' } + let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) } + let(:headers) { { 'Authorization' => "Bearer #{token.token}" } } + + describe 'GET /api/v1/blocks' do + subject do + get '/api/v1/blocks', headers: headers, params: params + end + + let!(:blocks) { Fabricate.times(3, :block, account: user.account) } + let(:params) { {} } + + let(:expected_response) do + blocks.map { |block| a_hash_including(id: block.target_account.id.to_s, username: block.target_account.username) } + end + + it_behaves_like 'forbidden for wrong scope', 'write write:blocks' + + it 'returns the blocked accounts', :aggregate_failures do + subject + + expect(response).to have_http_status(200) + expect(body_as_json).to match_array(expected_response) + end + + context 'with limit param' do + let(:params) { { limit: 2 } } + + it 'returns only the requested number of blocked accounts' do + subject + + expect(body_as_json.size).to eq(params[:limit]) + end + + it 'sets the correct pagination header for the prev path' do + subject + + expect(response.headers['Link'].find_link(%w(rel prev)).href).to eq(api_v1_blocks_url(limit: params[:limit], since_id: blocks.last.id)) + end + + it 'sets the correct pagination header for the next path' do + subject + + expect(response.headers['Link'].find_link(%w(rel next)).href).to eq(api_v1_blocks_url(limit: params[:limit], max_id: blocks[1].id)) + end + end + + context 'with max_id param' do + let(:params) { { max_id: blocks[1].id } } + + it 'queries the blocks in range according to max_id', :aggregate_failures do + subject + + response_body = body_as_json + + expect(response_body.size).to be 1 + expect(response_body[0][:id]).to eq(blocks[0].target_account.id.to_s) + end + end + + context 'with since_id param' do + let(:params) { { since_id: blocks[1].id } } + + it 'queries the blocks in range according to since_id', :aggregate_failures do + subject + + response_body = body_as_json + + expect(response_body.size).to be 1 + expect(response_body[0][:id]).to eq(blocks[2].target_account.id.to_s) + end + end + end +end From cafdaec6cfd4d0b4033f0495017651e0a81187ca Mon Sep 17 00:00:00 2001 From: Daniel M Brasil Date: Mon, 16 Oct 2023 11:03:48 -0300 Subject: [PATCH 23/24] Migrate to request specs in `/api/v1/lists/:id/accounts` (#25468) --- .../api/v1/lists/accounts_controller_spec.rb | 80 -------- spec/requests/api/v1/lists/accounts_spec.rb | 178 ++++++++++++++++++ 2 files changed, 178 insertions(+), 80 deletions(-) delete mode 100644 spec/controllers/api/v1/lists/accounts_controller_spec.rb create mode 100644 spec/requests/api/v1/lists/accounts_spec.rb diff --git a/spec/controllers/api/v1/lists/accounts_controller_spec.rb b/spec/controllers/api/v1/lists/accounts_controller_spec.rb deleted file mode 100644 index 21e155a508..0000000000 --- a/spec/controllers/api/v1/lists/accounts_controller_spec.rb +++ /dev/null @@ -1,80 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' - -describe Api::V1::Lists::AccountsController do - render_views - - let(:user) { Fabricate(:user) } - let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) } - let(:list) { Fabricate(:list, account: user.account) } - - before do - follow = Fabricate(:follow, account: user.account) - list.accounts << follow.target_account - allow(controller).to receive(:doorkeeper_token) { token } - end - - describe 'GET #index' do - let(:scopes) { 'read:lists' } - - it 'returns http success' do - get :show, params: { list_id: list.id } - - expect(response).to have_http_status(200) - end - end - - describe 'POST #create' do - let(:scopes) { 'write:lists' } - let(:bob) { Fabricate(:account, username: 'bob') } - - context 'when the added account is followed' do - before do - user.account.follow!(bob) - post :create, params: { list_id: list.id, account_ids: [bob.id] } - end - - it 'adds account to the list', :aggregate_failures do - expect(response).to have_http_status(200) - expect(list.accounts.include?(bob)).to be true - end - end - - context 'when the added account has been sent a follow request' do - before do - user.account.follow_requests.create!(target_account: bob) - post :create, params: { list_id: list.id, account_ids: [bob.id] } - end - - it 'adds account to the list', :aggregate_failures do - expect(response).to have_http_status(200) - expect(list.accounts.include?(bob)).to be true - end - end - - context 'when the added account is not followed' do - before do - post :create, params: { list_id: list.id, account_ids: [bob.id] } - end - - it 'does not add the account to the list', :aggregate_failures do - expect(response).to have_http_status(404) - expect(list.accounts.include?(bob)).to be false - end - end - end - - describe 'DELETE #destroy' do - let(:scopes) { 'write:lists' } - - before do - delete :destroy, params: { list_id: list.id, account_ids: [list.accounts.first.id] } - end - - it 'removes account from the list', :aggregate_failures do - expect(response).to have_http_status(200) - expect(list.accounts.count).to eq 0 - end - end -end diff --git a/spec/requests/api/v1/lists/accounts_spec.rb b/spec/requests/api/v1/lists/accounts_spec.rb new file mode 100644 index 0000000000..4d2a168b34 --- /dev/null +++ b/spec/requests/api/v1/lists/accounts_spec.rb @@ -0,0 +1,178 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe 'Accounts' do + let(:user) { Fabricate(:user) } + let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) } + let(:scopes) { 'read:lists write:lists' } + let(:headers) { { 'Authorization' => "Bearer #{token.token}" } } + + describe 'GET /api/v1/lists/:id/accounts' do + subject do + get "/api/v1/lists/#{list.id}/accounts", headers: headers, params: params + end + + let(:params) { { limit: 0 } } + let(:list) { Fabricate(:list, account: user.account) } + let(:accounts) { Fabricate.times(3, :account) } + + let(:expected_response) do + accounts.map do |account| + a_hash_including(id: account.id.to_s, username: account.username, acct: account.acct) + end + end + + before do + accounts.each { |account| user.account.follow!(account) } + list.accounts << accounts + end + + it_behaves_like 'forbidden for wrong scope', 'write write:lists' + + it 'returns the accounts in the requested list', :aggregate_failures do + subject + + expect(response).to have_http_status(200) + expect(body_as_json).to match_array(expected_response) + end + + context 'with limit param' do + let(:params) { { limit: 1 } } + + it 'returns only the requested number of accounts' do + subject + + expect(body_as_json.size).to eq(params[:limit]) + end + end + end + + describe 'POST /api/v1/lists/:id/accounts' do + subject do + post "/api/v1/lists/#{list.id}/accounts", headers: headers, params: params + end + + let(:list) { Fabricate(:list, account: user.account) } + let(:bob) { Fabricate(:account, username: 'bob') } + let(:params) { { account_ids: [bob.id] } } + + it_behaves_like 'forbidden for wrong scope', 'read read:lists' + + context 'when the added account is followed' do + before do + user.account.follow!(bob) + end + + it 'adds account to the list', :aggregate_failures do + subject + + expect(response).to have_http_status(200) + expect(list.accounts).to include(bob) + end + end + + context 'when the added account has been sent a follow request' do + before do + user.account.follow_requests.create!(target_account: bob) + end + + it 'adds account to the list', :aggregate_failures do + subject + + expect(response).to have_http_status(200) + expect(list.accounts).to include(bob) + end + end + + context 'when the added account is not followed' do + it 'does not add the account to the list', :aggregate_failures do + subject + + expect(response).to have_http_status(404) + expect(list.accounts).to_not include(bob) + end + end + + context 'when the list is not owned by the requesting user' do + let(:list) { Fabricate(:list) } + + before do + user.account.follow!(bob) + end + + it 'returns http not found' do + subject + + expect(response).to have_http_status(404) + end + end + + context 'when account is already in the list' do + before do + user.account.follow!(bob) + list.accounts << bob + end + + it 'returns http unprocessable entity' do + subject + + expect(response).to have_http_status(422) + end + end + end + + describe 'DELETE /api/v1/lists/:id/accounts' do + subject do + delete "/api/v1/lists/#{list.id}/accounts", headers: headers, params: params + end + + context 'when the list is owned by the requesting user' do + let(:list) { Fabricate(:list, account: user.account) } + let(:bob) { Fabricate(:account, username: 'bob') } + let(:peter) { Fabricate(:account, username: 'peter') } + let(:params) { { account_ids: [bob.id] } } + + before do + user.account.follow!(bob) + user.account.follow!(peter) + list.accounts << [bob, peter] + end + + it 'removes the specified account from the list', :aggregate_failures do + subject + + expect(response).to have_http_status(200) + expect(list.accounts).to_not include(bob) + end + + it 'does not remove any other account from the list' do + subject + + expect(list.accounts).to include(peter) + end + + context 'when the specified account is not in the list' do + let(:params) { { account_ids: [0] } } + + it 'does not remove any account from the list', :aggregate_failures do + subject + + expect(response).to have_http_status(200) + expect(list.accounts).to contain_exactly(bob, peter) + end + end + end + + context 'when the list is not owned by the requesting user' do + let(:list) { Fabricate(:list) } + let(:params) { {} } + + it 'returns http not found' do + subject + + expect(response).to have_http_status(404) + end + end + end +end From c91c0175db1cc8b954a977d29472886234ce9586 Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Mon, 16 Oct 2023 10:04:29 -0400 Subject: [PATCH 24/24] Extract `card` partial from `disputes/strikes/show` view (#27426) --- app/views/disputes/strikes/_card.html.haml | 38 ++++++++++++++++++ app/views/disputes/strikes/show.html.haml | 46 +--------------------- 2 files changed, 39 insertions(+), 45 deletions(-) create mode 100644 app/views/disputes/strikes/_card.html.haml diff --git a/app/views/disputes/strikes/_card.html.haml b/app/views/disputes/strikes/_card.html.haml new file mode 100644 index 0000000000..55551cc7d0 --- /dev/null +++ b/app/views/disputes/strikes/_card.html.haml @@ -0,0 +1,38 @@ +.strike-card + - unless strike.none_action? + %p= t "user_mailer.warning.explanation.#{strike.action}", instance: Rails.configuration.x.local_domain + - if strike.text.present? + = linkify(strike.text) + - if strike.report && !strike.report.other? + %p + %strong= t('user_mailer.warning.reason') + = t("user_mailer.warning.categories.#{strike.report.category}") + - if strike.report.violation? && strike.report.rule_ids.present? + %ul.strike-card__rules + - strike.report.rules.each do |rule| + %li + %span.strike-card__rules__text= rule.text + - if strike.status_ids.present? && !strike.status_ids.empty? + %p + %strong= t('user_mailer.warning.statuses') + .strike-card__statuses-list + - status_map = strike.statuses.includes(:application, :media_attachments).index_by(&:id) + - strike.status_ids.each do |status_id| + .strike-card__statuses-list__item + - if (status = status_map[status_id.to_i]) + .one-liner + .emojify= one_line_preview(status) + - status.ordered_media_attachments.each do |media_attachment| + %abbr{ title: media_attachment.description } + = fa_icon 'link' + = media_attachment.file_file_name + .strike-card__statuses-list__item__meta + = link_to ActivityPub::TagManager.instance.url_for(status), target: '_blank', rel: 'noopener noreferrer' do + %time.formatted{ datetime: status.created_at.iso8601, title: l(status.created_at) }= l(status.created_at) + - unless status.application.nil? + ยท + = status.application.name + - else + .one-liner= t('disputes.strikes.status', id: status_id) + .strike-card__statuses-list__item__meta + = t('disputes.strikes.status_removed') diff --git a/app/views/disputes/strikes/show.html.haml b/app/views/disputes/strikes/show.html.haml index de883bd873..1c16e0bbf5 100644 --- a/app/views/disputes/strikes/show.html.haml +++ b/app/views/disputes/strikes/show.html.haml @@ -21,51 +21,7 @@ .report-header .report-header__card - .strike-card - - unless @strike.none_action? - %p= t "user_mailer.warning.explanation.#{@strike.action}", instance: Rails.configuration.x.local_domain - - - if @strike.text.present? - = linkify(@strike.text) - - - if @strike.report && !@strike.report.other? - %p - %strong= t('user_mailer.warning.reason') - = t("user_mailer.warning.categories.#{@strike.report.category}") - - - if @strike.report.violation? && @strike.report.rule_ids.present? - %ul.strike-card__rules - - @strike.report.rules.each do |rule| - %li - %span.strike-card__rules__text= rule.text - - - if @strike.status_ids.present? && !@strike.status_ids.empty? - %p - %strong= t('user_mailer.warning.statuses') - - .strike-card__statuses-list - - status_map = @strike.statuses.includes(:application, :media_attachments).index_by(&:id) - - - @strike.status_ids.each do |status_id| - .strike-card__statuses-list__item - - if (status = status_map[status_id.to_i]) - .one-liner - .emojify= one_line_preview(status) - - - status.ordered_media_attachments.each do |media_attachment| - %abbr{ title: media_attachment.description } - = fa_icon 'link' - = media_attachment.file_file_name - .strike-card__statuses-list__item__meta - = link_to ActivityPub::TagManager.instance.url_for(status), target: '_blank', rel: 'noopener noreferrer' do - %time.formatted{ datetime: status.created_at.iso8601, title: l(status.created_at) }= l(status.created_at) - - unless status.application.nil? - ยท - = status.application.name - - else - .one-liner= t('disputes.strikes.status', id: status_id) - .strike-card__statuses-list__item__meta - = t('disputes.strikes.status_removed') + = render 'card', strike: @strike .report-header__details .report-header__details__item