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