diff --git a/Gemfile.lock b/Gemfile.lock index 8af9c3c6e5..812a1e0146 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -613,7 +613,7 @@ GEM activesupport (>= 3.0.0) raabro (1.4.0) racc (1.8.1) - rack (2.2.12) + rack (2.2.13) rack-attack (6.7.0) rack (>= 1.0, < 4) rack-cors (2.0.2) diff --git a/app/controllers/api/v1/accounts/credentials_controller.rb b/app/controllers/api/v1/accounts/credentials_controller.rb index 1b64eb4ef2..b90036a5cd 100644 --- a/app/controllers/api/v1/accounts/credentials_controller.rb +++ b/app/controllers/api/v1/accounts/credentials_controller.rb @@ -14,7 +14,7 @@ class Api::V1::Accounts::CredentialsController < Api::BaseController @account = current_account UpdateAccountService.new.call(@account, account_params, raise_error: true) current_user.update(user_params) if user_params - ActivityPub::UpdateDistributionWorker.perform_async(@account.id) + ActivityPub::UpdateDistributionWorker.perform_in(ActivityPub::UpdateDistributionWorker::DEBOUNCE_DELAY, @account.id) render json: @account, serializer: REST::CredentialAccountSerializer rescue ActiveRecord::RecordInvalid => e render json: ValidationErrorFormatter.new(e).as_json, status: 422 diff --git a/app/controllers/api/v1/profile/avatars_controller.rb b/app/controllers/api/v1/profile/avatars_controller.rb index bc4d01a597..e6c954ed63 100644 --- a/app/controllers/api/v1/profile/avatars_controller.rb +++ b/app/controllers/api/v1/profile/avatars_controller.rb @@ -7,7 +7,7 @@ class Api::V1::Profile::AvatarsController < Api::BaseController def destroy @account = current_account UpdateAccountService.new.call(@account, { avatar: nil }, raise_error: true) - ActivityPub::UpdateDistributionWorker.perform_async(@account.id) + ActivityPub::UpdateDistributionWorker.perform_in(ActivityPub::UpdateDistributionWorker::DEBOUNCE_DELAY, @account.id) render json: @account, serializer: REST::CredentialAccountSerializer end end diff --git a/app/controllers/api/v1/profile/headers_controller.rb b/app/controllers/api/v1/profile/headers_controller.rb index 9f4daa2f77..4472a01b05 100644 --- a/app/controllers/api/v1/profile/headers_controller.rb +++ b/app/controllers/api/v1/profile/headers_controller.rb @@ -7,7 +7,7 @@ class Api::V1::Profile::HeadersController < Api::BaseController def destroy @account = current_account UpdateAccountService.new.call(@account, { header: nil }, raise_error: true) - ActivityPub::UpdateDistributionWorker.perform_async(@account.id) + ActivityPub::UpdateDistributionWorker.perform_in(ActivityPub::UpdateDistributionWorker::DEBOUNCE_DELAY, @account.id) render json: @account, serializer: REST::CredentialAccountSerializer end end diff --git a/app/controllers/settings/pictures_controller.rb b/app/controllers/settings/pictures_controller.rb index 58a4325307..7e61e6d580 100644 --- a/app/controllers/settings/pictures_controller.rb +++ b/app/controllers/settings/pictures_controller.rb @@ -8,7 +8,7 @@ module Settings def destroy if valid_picture? if UpdateAccountService.new.call(@account, { @picture => nil, "#{@picture}_remote_url" => '' }) - ActivityPub::UpdateDistributionWorker.perform_async(@account.id) + ActivityPub::UpdateDistributionWorker.perform_in(ActivityPub::UpdateDistributionWorker::DEBOUNCE_DELAY, @account.id) redirect_to settings_profile_path, notice: I18n.t('generic.changes_saved_msg'), status: 303 else redirect_to settings_profile_path diff --git a/app/controllers/settings/privacy_controller.rb b/app/controllers/settings/privacy_controller.rb index a5bb3b884f..96efa03ccf 100644 --- a/app/controllers/settings/privacy_controller.rb +++ b/app/controllers/settings/privacy_controller.rb @@ -8,7 +8,7 @@ class Settings::PrivacyController < Settings::BaseController def update if UpdateAccountService.new.call(@account, account_params.except(:settings)) current_user.update!(settings_attributes: account_params[:settings]) - ActivityPub::UpdateDistributionWorker.perform_async(@account.id) + ActivityPub::UpdateDistributionWorker.perform_in(ActivityPub::UpdateDistributionWorker::DEBOUNCE_DELAY, @account.id) redirect_to settings_privacy_path, notice: I18n.t('generic.changes_saved_msg') else render :show diff --git a/app/controllers/settings/profiles_controller.rb b/app/controllers/settings/profiles_controller.rb index 458f4148cc..efd8eb1440 100644 --- a/app/controllers/settings/profiles_controller.rb +++ b/app/controllers/settings/profiles_controller.rb @@ -9,7 +9,7 @@ class Settings::ProfilesController < Settings::BaseController def update if UpdateAccountService.new.call(@account, account_params) - ActivityPub::UpdateDistributionWorker.perform_async(@account.id) + ActivityPub::UpdateDistributionWorker.perform_in(ActivityPub::UpdateDistributionWorker::DEBOUNCE_DELAY, @account.id) redirect_to settings_profile_path, notice: I18n.t('generic.changes_saved_msg') else @account.build_fields diff --git a/app/controllers/settings/verifications_controller.rb b/app/controllers/settings/verifications_controller.rb index bed29dbeec..4b949ca72d 100644 --- a/app/controllers/settings/verifications_controller.rb +++ b/app/controllers/settings/verifications_controller.rb @@ -8,7 +8,7 @@ class Settings::VerificationsController < Settings::BaseController def update if UpdateAccountService.new.call(@account, account_params) - ActivityPub::UpdateDistributionWorker.perform_async(@account.id) + ActivityPub::UpdateDistributionWorker.perform_in(ActivityPub::UpdateDistributionWorker::DEBOUNCE_DELAY, @account.id) redirect_to settings_verification_path, notice: I18n.t('generic.changes_saved_msg') else render :show diff --git a/app/javascript/flavours/glitch/features/compose/components/action_bar.jsx b/app/javascript/flavours/glitch/features/compose/components/action_bar.jsx index 43c2899373..e277e300f8 100644 --- a/app/javascript/flavours/glitch/features/compose/components/action_bar.jsx +++ b/app/javascript/flavours/glitch/features/compose/components/action_bar.jsx @@ -1,4 +1,4 @@ -import { useCallback } from 'react'; +import { useMemo } from 'react'; import { defineMessages, useIntl } from 'react-intl'; @@ -28,28 +28,30 @@ export const ActionBar = () => { const dispatch = useDispatch(); const intl = useIntl(); - const handleLogoutClick = useCallback(() => { - dispatch(openModal({ modalType: 'CONFIRM_LOG_OUT' })); - }, [dispatch]); + const menu = useMemo(() => { + const handleLogoutClick = () => { + dispatch(openModal({ modalType: 'CONFIRM_LOG_OUT' })); + }; - let menu = []; - - menu.push({ text: intl.formatMessage(messages.edit_profile), href: '/settings/profile' }); - menu.push({ text: intl.formatMessage(messages.preferences), href: '/settings/preferences' }); - menu.push({ text: intl.formatMessage(messages.pins), to: '/pinned' }); - menu.push(null); - menu.push({ text: intl.formatMessage(messages.follow_requests), to: '/follow_requests' }); - menu.push({ text: intl.formatMessage(messages.favourites), to: '/favourites' }); - menu.push({ text: intl.formatMessage(messages.bookmarks), to: '/bookmarks' }); - menu.push({ text: intl.formatMessage(messages.lists), to: '/lists' }); - menu.push({ text: intl.formatMessage(messages.followed_tags), to: '/followed_tags' }); - menu.push(null); - menu.push({ text: intl.formatMessage(messages.mutes), to: '/mutes' }); - menu.push({ text: intl.formatMessage(messages.blocks), to: '/blocks' }); - menu.push({ text: intl.formatMessage(messages.domain_blocks), to: '/domain_blocks' }); - menu.push({ text: intl.formatMessage(messages.filters), href: '/filters' }); - menu.push(null); - menu.push({ text: intl.formatMessage(messages.logout), action: handleLogoutClick }); + return ([ + { text: intl.formatMessage(messages.edit_profile), href: '/settings/profile' }, + { text: intl.formatMessage(messages.preferences), href: '/settings/preferences' }, + { text: intl.formatMessage(messages.pins), to: '/pinned' }, + null, + { text: intl.formatMessage(messages.follow_requests), to: '/follow_requests' }, + { text: intl.formatMessage(messages.favourites), to: '/favourites' }, + { text: intl.formatMessage(messages.bookmarks), to: '/bookmarks' }, + { text: intl.formatMessage(messages.lists), to: '/lists' }, + { text: intl.formatMessage(messages.followed_tags), to: '/followed_tags' }, + null, + { text: intl.formatMessage(messages.mutes), to: '/mutes' }, + { text: intl.formatMessage(messages.blocks), to: '/blocks' }, + { text: intl.formatMessage(messages.domain_blocks), to: '/domain_blocks' }, + { text: intl.formatMessage(messages.filters), href: '/filters' }, + null, + { text: intl.formatMessage(messages.logout), action: handleLogoutClick }, + ]); + }, [intl, dispatch]); return ( <DropdownMenuContainer diff --git a/app/javascript/mastodon/features/compose/components/action_bar.jsx b/app/javascript/mastodon/features/compose/components/action_bar.jsx index 6c2f27b01b..f7339141ad 100644 --- a/app/javascript/mastodon/features/compose/components/action_bar.jsx +++ b/app/javascript/mastodon/features/compose/components/action_bar.jsx @@ -1,4 +1,4 @@ -import { useCallback } from 'react'; +import { useMemo } from 'react'; import { defineMessages, useIntl } from 'react-intl'; @@ -28,28 +28,30 @@ export const ActionBar = () => { const dispatch = useDispatch(); const intl = useIntl(); - const handleLogoutClick = useCallback(() => { - dispatch(openModal({ modalType: 'CONFIRM_LOG_OUT' })); - }, [dispatch]); + const menu = useMemo(() => { + const handleLogoutClick = () => { + dispatch(openModal({ modalType: 'CONFIRM_LOG_OUT' })); + }; - let menu = []; - - menu.push({ text: intl.formatMessage(messages.edit_profile), href: '/settings/profile' }); - menu.push({ text: intl.formatMessage(messages.preferences), href: '/settings/preferences' }); - menu.push({ text: intl.formatMessage(messages.pins), to: '/pinned' }); - menu.push(null); - menu.push({ text: intl.formatMessage(messages.follow_requests), to: '/follow_requests' }); - menu.push({ text: intl.formatMessage(messages.favourites), to: '/favourites' }); - menu.push({ text: intl.formatMessage(messages.bookmarks), to: '/bookmarks' }); - menu.push({ text: intl.formatMessage(messages.lists), to: '/lists' }); - menu.push({ text: intl.formatMessage(messages.followed_tags), to: '/followed_tags' }); - menu.push(null); - menu.push({ text: intl.formatMessage(messages.mutes), to: '/mutes' }); - menu.push({ text: intl.formatMessage(messages.blocks), to: '/blocks' }); - menu.push({ text: intl.formatMessage(messages.domain_blocks), to: '/domain_blocks' }); - menu.push({ text: intl.formatMessage(messages.filters), href: '/filters' }); - menu.push(null); - menu.push({ text: intl.formatMessage(messages.logout), action: handleLogoutClick }); + return ([ + { text: intl.formatMessage(messages.edit_profile), href: '/settings/profile' }, + { text: intl.formatMessage(messages.preferences), href: '/settings/preferences' }, + { text: intl.formatMessage(messages.pins), to: '/pinned' }, + null, + { text: intl.formatMessage(messages.follow_requests), to: '/follow_requests' }, + { text: intl.formatMessage(messages.favourites), to: '/favourites' }, + { text: intl.formatMessage(messages.bookmarks), to: '/bookmarks' }, + { text: intl.formatMessage(messages.lists), to: '/lists' }, + { text: intl.formatMessage(messages.followed_tags), to: '/followed_tags' }, + null, + { text: intl.formatMessage(messages.mutes), to: '/mutes' }, + { text: intl.formatMessage(messages.blocks), to: '/blocks' }, + { text: intl.formatMessage(messages.domain_blocks), to: '/domain_blocks' }, + { text: intl.formatMessage(messages.filters), href: '/filters' }, + null, + { text: intl.formatMessage(messages.logout), action: handleLogoutClick }, + ]); + }, [intl, dispatch]); return ( <DropdownMenuContainer diff --git a/app/workers/activitypub/update_distribution_worker.rb b/app/workers/activitypub/update_distribution_worker.rb index a04ac621f3..9a418f0f3d 100644 --- a/app/workers/activitypub/update_distribution_worker.rb +++ b/app/workers/activitypub/update_distribution_worker.rb @@ -1,6 +1,8 @@ # frozen_string_literal: true class ActivityPub::UpdateDistributionWorker < ActivityPub::RawDistributionWorker + DEBOUNCE_DELAY = 5.seconds + sidekiq_options queue: 'push', lock: :until_executed, lock_ttl: 1.day.to_i # Distribute an profile update to servers that might have a copy diff --git a/config/application.rb b/config/application.rb index 88c7b029bf..e1af98b448 100644 --- a/config/application.rb +++ b/config/application.rb @@ -34,11 +34,11 @@ require_relative '../lib/paperclip/transcoder' require_relative '../lib/paperclip/type_corrector' require_relative '../lib/paperclip/response_with_limit_adapter' require_relative '../lib/terrapin/multi_pipe_extensions' +require_relative '../lib/mastodon/middleware/public_file_server' +require_relative '../lib/mastodon/middleware/socket_cleanup' require_relative '../lib/mastodon/snowflake' require_relative '../lib/mastodon/feature' require_relative '../lib/mastodon/version' -require_relative '../lib/mastodon/rack_middleware' -require_relative '../lib/public_file_server_middleware' require_relative '../lib/devise/strategies/two_factor_ldap_authenticatable' require_relative '../lib/devise/strategies/two_factor_pam_authenticatable' require_relative '../lib/elasticsearch/client_extensions' @@ -88,9 +88,9 @@ module Mastodon # We use our own middleware for this config.public_file_server.enabled = false - config.middleware.use PublicFileServerMiddleware if Rails.env.local? || ENV['RAILS_SERVE_STATIC_FILES'] == 'true' + config.middleware.use Mastodon::Middleware::PublicFileServer if Rails.env.local? || ENV['RAILS_SERVE_STATIC_FILES'] == 'true' config.middleware.use Rack::Attack - config.middleware.use Mastodon::RackMiddleware + config.middleware.use Mastodon::Middleware::SocketCleanup config.before_configuration do require 'mastodon/redis_configuration' diff --git a/config/mastodon.yml b/config/mastodon.yml index 8256761e86..ea67306d5b 100644 --- a/config/mastodon.yml +++ b/config/mastodon.yml @@ -10,3 +10,5 @@ shared: version: metadata: <%= ['glitch', ENV.fetch('MASTODON_VERSION_METADATA', nil)].compact_blank.join('.') %> prerelease: <%= ENV.fetch('MASTODON_VERSION_PRERELEASE', nil) %> +test: + experimental_features: <%= [ENV.fetch('EXPERIMENTAL_FEATURES', nil), 'testing_only'].compact.join(',') %> diff --git a/lib/mastodon/feature.rb b/lib/mastodon/feature.rb index 18e6dc9639..650f1d7b8c 100644 --- a/lib/mastodon/feature.rb +++ b/lib/mastodon/feature.rb @@ -19,8 +19,8 @@ module Mastodon::Feature super end - def respond_to_missing?(name) - name.to_s.end_with?('_enabled?') + def respond_to_missing?(name, include_all = false) + name.to_s.end_with?('_enabled?') || super end end end diff --git a/lib/mastodon/middleware/public_file_server.rb b/lib/mastodon/middleware/public_file_server.rb new file mode 100644 index 0000000000..b9a1edb9f5 --- /dev/null +++ b/lib/mastodon/middleware/public_file_server.rb @@ -0,0 +1,52 @@ +# frozen_string_literal: true + +require 'action_dispatch/middleware/static' + +module Mastodon + module Middleware + class PublicFileServer + SERVICE_WORKER_TTL = 7.days.to_i + CACHE_TTL = 28.days.to_i + + def initialize(app) + @app = app + @file_handler = ActionDispatch::FileHandler.new(Rails.application.paths['public'].first) + end + + def call(env) + file = @file_handler.attempt(env) + + # If the request is not a static file, move on! + return @app.call(env) if file.nil? + + status, headers, response = file + + # Set cache headers on static files. Some paths require different cache headers + headers['Cache-Control'] = begin + request_path = env['REQUEST_PATH'] + + if request_path.start_with?('/sw.js') + "public, max-age=#{SERVICE_WORKER_TTL}, must-revalidate" + elsif request_path.start_with?(paperclip_root_url) + "public, max-age=#{CACHE_TTL}, immutable" + else + "public, max-age=#{CACHE_TTL}, must-revalidate" + end + end + + # Override the default CSP header set by the CSP middleware + headers['Content-Security-Policy'] = "default-src 'none'; form-action 'none'" if request_path.start_with?(paperclip_root_url) + + headers['X-Content-Type-Options'] = 'nosniff' + + [status, headers, response] + end + + private + + def paperclip_root_url + ENV.fetch('PAPERCLIP_ROOT_URL', '/system') + end + end + end +end diff --git a/lib/mastodon/middleware/socket_cleanup.rb b/lib/mastodon/middleware/socket_cleanup.rb new file mode 100644 index 0000000000..8b33cb0cec --- /dev/null +++ b/lib/mastodon/middleware/socket_cleanup.rb @@ -0,0 +1,34 @@ +# frozen_string_literal: true + +module Mastodon + module Middleware + class SocketCleanup + def initialize(app) + @app = app + end + + def call(env) + @app.call(env) + ensure + clean_up_sockets! + end + + private + + def clean_up_sockets! + clean_up_redis_socket! + clean_up_statsd_socket! + end + + def clean_up_redis_socket! + RedisConnection.pool.checkin if Thread.current[:redis] + Thread.current[:redis] = nil + end + + def clean_up_statsd_socket! + Thread.current[:statsd_socket]&.close + Thread.current[:statsd_socket] = nil + end + end + end +end diff --git a/lib/mastodon/rack_middleware.rb b/lib/mastodon/rack_middleware.rb deleted file mode 100644 index 0e452f06d6..0000000000 --- a/lib/mastodon/rack_middleware.rb +++ /dev/null @@ -1,30 +0,0 @@ -# frozen_string_literal: true - -class Mastodon::RackMiddleware - def initialize(app) - @app = app - end - - def call(env) - @app.call(env) - ensure - clean_up_sockets! - end - - private - - def clean_up_sockets! - clean_up_redis_socket! - clean_up_statsd_socket! - end - - def clean_up_redis_socket! - RedisConnection.pool.checkin if Thread.current[:redis] - Thread.current[:redis] = nil - end - - def clean_up_statsd_socket! - Thread.current[:statsd_socket]&.close - Thread.current[:statsd_socket] = nil - end -end diff --git a/lib/public_file_server_middleware.rb b/lib/public_file_server_middleware.rb deleted file mode 100644 index 7e02e37a08..0000000000 --- a/lib/public_file_server_middleware.rb +++ /dev/null @@ -1,48 +0,0 @@ -# frozen_string_literal: true - -require 'action_dispatch/middleware/static' - -class PublicFileServerMiddleware - SERVICE_WORKER_TTL = 7.days.to_i - CACHE_TTL = 28.days.to_i - - def initialize(app) - @app = app - @file_handler = ActionDispatch::FileHandler.new(Rails.application.paths['public'].first) - end - - def call(env) - file = @file_handler.attempt(env) - - # If the request is not a static file, move on! - return @app.call(env) if file.nil? - - status, headers, response = file - - # Set cache headers on static files. Some paths require different cache headers - headers['Cache-Control'] = begin - request_path = env['REQUEST_PATH'] - - if request_path.start_with?('/sw.js') - "public, max-age=#{SERVICE_WORKER_TTL}, must-revalidate" - elsif request_path.start_with?(paperclip_root_url) - "public, max-age=#{CACHE_TTL}, immutable" - else - "public, max-age=#{CACHE_TTL}, must-revalidate" - end - end - - # Override the default CSP header set by the CSP middleware - headers['Content-Security-Policy'] = "default-src 'none'; form-action 'none'" if request_path.start_with?(paperclip_root_url) - - headers['X-Content-Type-Options'] = 'nosniff' - - [status, headers, response] - end - - private - - def paperclip_root_url - ENV.fetch('PAPERCLIP_ROOT_URL', '/system') - end -end diff --git a/spec/controllers/admin/settings/branding_controller_spec.rb b/spec/controllers/admin/settings/branding_controller_spec.rb deleted file mode 100644 index 6b3621bb8a..0000000000 --- a/spec/controllers/admin/settings/branding_controller_spec.rb +++ /dev/null @@ -1,25 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' - -RSpec.describe Admin::Settings::BrandingController do - render_views - - describe 'When signed in as an admin' do - before do - sign_in Fabricate(:admin_user), scope: :user - end - - describe 'PUT #update' do - 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 have_http_status(400) - expect(Setting.new_setting_key).to be_nil - end - end - end -end diff --git a/spec/lib/mastodon/feature_spec.rb b/spec/lib/mastodon/feature_spec.rb index a7fe4fe90b..f8236d8959 100644 --- a/spec/lib/mastodon/feature_spec.rb +++ b/spec/lib/mastodon/feature_spec.rb @@ -3,28 +3,23 @@ require 'rails_helper' RSpec.describe Mastodon::Feature do - around do |example| - original_value = Rails.configuration.x.mastodon.experimental_features - Rails.configuration.x.mastodon.experimental_features = 'fasp,fetch_all_replies' - example.run - Rails.configuration.x.mastodon.experimental_features = original_value - end - - describe '::fasp_enabled?' do - subject { described_class.fasp_enabled? } - - it { is_expected.to be true } - end - - describe '::fetch_all_replies_enabled?' do - subject { described_class.fetch_all_replies_enabled? } + describe '::testing_only_enabled?' do + subject { described_class.testing_only_enabled? } it { is_expected.to be true } end describe '::unspecified_feature_enabled?' do - subject { described_class.unspecified_feature_enabled? } + context 'when example is not tagged with a feature' do + subject { described_class.unspecified_feature_enabled? } - it { is_expected.to be false } + it { is_expected.to be false } + end + + context 'when example is tagged with a feature', feature: 'unspecified_feature' do + subject { described_class.unspecified_feature_enabled? } + + it { is_expected.to be true } + end end end diff --git a/spec/requests/admin/settings/branding_spec.rb b/spec/requests/admin/settings/branding_spec.rb new file mode 100644 index 0000000000..e5206f056f --- /dev/null +++ b/spec/requests/admin/settings/branding_spec.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe 'Admin Settings Branding' do + describe 'When signed in as an admin' do + before { sign_in Fabricate(:admin_user) } + + describe 'PUT /admin/settings/branding' do + it 'cannot create a setting value for a non-admin key' do + expect { put admin_settings_branding_path, params: { form_admin_settings: { new_setting_key: 'New key value' } } } + .to_not change(Setting, :new_setting_key).from(nil) + + expect(response) + .to have_http_status(400) + end + end + end +end diff --git a/spec/requests/api/v1/accounts/credentials_spec.rb b/spec/requests/api/v1/accounts/credentials_spec.rb index c92f4c7973..68ea259481 100644 --- a/spec/requests/api/v1/accounts/credentials_spec.rb +++ b/spec/requests/api/v1/accounts/credentials_spec.rb @@ -53,8 +53,6 @@ RSpec.describe 'credentials API' do patch '/api/v1/accounts/update_credentials', headers: headers, params: params end - before { allow(ActivityPub::UpdateDistributionWorker).to receive(:perform_async) } - let(:params) do { avatar: fixture_file_upload('avatar.gif', 'image/gif'), @@ -113,7 +111,7 @@ RSpec.describe 'credentials API' do }) expect(ActivityPub::UpdateDistributionWorker) - .to have_received(:perform_async).with(user.account_id) + .to have_enqueued_sidekiq_job(user.account_id) end def expect_account_updates diff --git a/spec/requests/api/v1/profiles_spec.rb b/spec/requests/api/v1/profiles_spec.rb index fd3ab4bf58..de7a20b133 100644 --- a/spec/requests/api/v1/profiles_spec.rb +++ b/spec/requests/api/v1/profiles_spec.rb @@ -15,10 +15,6 @@ RSpec.describe 'Deleting profile images' do let(:headers) { { 'Authorization' => "Bearer #{token.token}" } } describe 'DELETE /api/v1/profile' do - before do - allow(ActivityPub::UpdateDistributionWorker).to receive(:perform_async) - end - context 'when deleting an avatar' do context 'with wrong scope' do before do @@ -38,7 +34,8 @@ RSpec.describe 'Deleting profile images' do account.reload expect(account.avatar).to_not exist expect(account.header).to exist - expect(ActivityPub::UpdateDistributionWorker).to have_received(:perform_async).with(account.id) + expect(ActivityPub::UpdateDistributionWorker) + .to have_enqueued_sidekiq_job(account.id) end end @@ -61,7 +58,8 @@ RSpec.describe 'Deleting profile images' do account.reload expect(account.avatar).to exist expect(account.header).to_not exist - expect(ActivityPub::UpdateDistributionWorker).to have_received(:perform_async).with(account.id) + expect(ActivityPub::UpdateDistributionWorker) + .to have_enqueued_sidekiq_job(account.id) end end end diff --git a/spec/support/feature_flags.rb b/spec/support/feature_flags.rb new file mode 100644 index 0000000000..186711163b --- /dev/null +++ b/spec/support/feature_flags.rb @@ -0,0 +1,8 @@ +# frozen_string_literal: true + +RSpec.configure do |config| + config.before(:example, :feature) do |example| + feature = example.metadata[:feature] + allow(Mastodon::Feature).to receive(:"#{feature}_enabled?").and_return(true) + end +end diff --git a/spec/support/stories/profile_stories.rb b/spec/support/stories/profile_stories.rb index 43a8e170cc..6e7d53454d 100644 --- a/spec/support/stories/profile_stories.rb +++ b/spec/support/stories/profile_stories.rb @@ -22,7 +22,11 @@ module ProfileStories def as_a_logged_in_user as_a_registered_user visit new_user_session_path + expect(page) + .to have_title(I18n.t('auth.login')) fill_in_auth_details(email, password) + expect(page) + .to have_css('.app-holder') end def as_a_logged_in_admin diff --git a/spec/system/new_statuses_spec.rb b/spec/system/new_statuses_spec.rb index 317508a0bb..480c77cf87 100644 --- a/spec/system/new_statuses_spec.rb +++ b/spec/system/new_statuses_spec.rb @@ -5,21 +5,15 @@ require 'rails_helper' RSpec.describe 'NewStatuses', :inline_jobs, :js, :streaming do include ProfileStories - subject { page } - let(:email) { 'test@example.com' } let(:password) { 'password' } let(:confirmed_at) { Time.zone.now } let(:finished_onboarding) { true } - before do - as_a_logged_in_user - visit root_path - end + before { as_a_logged_in_user } it 'can be posted' do - expect(subject).to have_css('div.app-holder') - + visit_homepage status_text = 'This is a new status!' within('.compose-form') do @@ -27,12 +21,12 @@ RSpec.describe 'NewStatuses', :inline_jobs, :js, :streaming do click_on 'Post' end - expect(subject).to have_css('.status__content__text', text: status_text) + expect(page) + .to have_css('.status__content__text', text: status_text) end it 'can be posted again' do - expect(subject).to have_css('div.app-holder') - + visit_homepage status_text = 'This is a second status!' within('.compose-form') do @@ -40,6 +34,15 @@ RSpec.describe 'NewStatuses', :inline_jobs, :js, :streaming do click_on 'Post' end - expect(subject).to have_css('.status__content__text', text: status_text) + expect(page) + .to have_css('.status__content__text', text: status_text) + end + + def visit_homepage + visit root_path + + expect(page) + .to have_css('div.app-holder') + .and have_css('form.compose-form') end end diff --git a/spec/system/report_interface_spec.rb b/spec/system/report_interface_spec.rb index 6a90aa5bc6..3df6b3714b 100644 --- a/spec/system/report_interface_spec.rb +++ b/spec/system/report_interface_spec.rb @@ -40,5 +40,7 @@ RSpec.describe 'report interface', :attachment_processing, :js, :streaming do within '.report-actions' do click_on I18n.t('admin.reports.mark_as_resolved') end + expect(page) + .to have_content(I18n.t('admin.reports.resolved_msg')) end end diff --git a/spec/system/settings/privacy_spec.rb b/spec/system/settings/privacy_spec.rb index 8cc2196d13..5e1498613e 100644 --- a/spec/system/settings/privacy_spec.rb +++ b/spec/system/settings/privacy_spec.rb @@ -11,8 +11,6 @@ RSpec.describe 'Settings Privacy' do before { user.account.update(discoverable: false) } context 'with a successful update' do - before { allow(ActivityPub::UpdateDistributionWorker).to receive(:perform_async) } - it 'updates user profile information' do # View settings page visit settings_privacy_path @@ -29,14 +27,13 @@ RSpec.describe 'Settings Privacy' do .to have_content(I18n.t('privacy.title')) .and have_content(success_message) expect(ActivityPub::UpdateDistributionWorker) - .to have_received(:perform_async).with(user.account.id) + .to have_enqueued_sidekiq_job(user.account.id) end end context 'with a failed update' do before do allow(UpdateAccountService).to receive(:new).and_return(failing_update_service) - allow(ActivityPub::UpdateDistributionWorker).to receive(:perform_async) end it 'updates user profile information' do @@ -54,7 +51,7 @@ RSpec.describe 'Settings Privacy' do expect(page) .to have_content(I18n.t('privacy.title')) expect(ActivityPub::UpdateDistributionWorker) - .to_not have_received(:perform_async) + .to_not have_enqueued_sidekiq_job(anything) end private diff --git a/spec/system/settings/profiles_spec.rb b/spec/system/settings/profiles_spec.rb index 322f5faa91..23d9ab2fda 100644 --- a/spec/system/settings/profiles_spec.rb +++ b/spec/system/settings/profiles_spec.rb @@ -7,7 +7,6 @@ RSpec.describe 'Settings profile page' do let(:account) { user.account } before do - allow(ActivityPub::UpdateDistributionWorker).to receive(:perform_async) sign_in user end @@ -24,7 +23,7 @@ RSpec.describe 'Settings profile page' do .to change { account.reload.display_name }.to('New name') .and(change { account.reload.avatar.instance.avatar_file_name }.from(nil).to(be_present)) expect(ActivityPub::UpdateDistributionWorker) - .to have_received(:perform_async).with(account.id) + .to have_enqueued_sidekiq_job(account.id) end def display_name_field