Merge commit 'b87bfb8c96c8491f1228e0258d05119f3420db05' into glitch-soc/merge-upstream
commit
46ddaffd40
|
@ -15,6 +15,6 @@ RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \
|
|||
RUN gem install foreman
|
||||
|
||||
# [Optional] Uncomment this line to install global node packages.
|
||||
RUN su vscode -c "source /usr/local/share/nvm/nvm.sh && npm install -g yarn" 2>&1
|
||||
RUN su vscode -c "source /usr/local/share/nvm/nvm.sh && corepack enable" 2>&1
|
||||
|
||||
COPY welcome-message.txt /usr/local/etc/vscode-dev-containers/first-run-notice.txt
|
||||
|
|
|
@ -11,6 +11,7 @@ bundle install
|
|||
git checkout -- Gemfile.lock
|
||||
|
||||
# Fetch Javascript dependencies
|
||||
corepack prepare
|
||||
yarn install --immutable
|
||||
|
||||
# [re]create, migrate, and seed the test database
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
// If we do not want a package to be grouped with others, we need to set its groupName
|
||||
// to `null` after any other rule set it to something.
|
||||
dependencyDashboardHeader: 'This issue lists Renovate updates and detected dependencies. Read the [Dependency Dashboard](https://docs.renovatebot.com/key-concepts/dashboard/) docs to learn more. Before approving any upgrade: read the description and comments in the [`renovate.json5` file](https://github.com/mastodon/mastodon/blob/main/.github/renovate.json5).',
|
||||
postUpdateOptions: ['yarnDedupeHighest'],
|
||||
packageRules: [
|
||||
{
|
||||
// Require Dependency Dashboard Approval for major version bumps of these node packages
|
||||
|
|
|
@ -31,4 +31,3 @@ linters:
|
|||
- 'app/views/admin/accounts/_buttons.html.haml'
|
||||
- 'app/views/admin/accounts/_local_account.html.haml'
|
||||
- 'app/views/admin/roles/_form.html.haml'
|
||||
- 'app/views/layouts/application.html.haml'
|
||||
|
|
|
@ -24,15 +24,6 @@ Lint/NonLocalExitFromIterator:
|
|||
Exclude:
|
||||
- 'app/helpers/jsonld_helper.rb'
|
||||
|
||||
# This cop supports safe autocorrection (--autocorrect).
|
||||
# Configuration parameters: IgnoreEmptyBlocks, AllowUnusedKeywordArguments.
|
||||
Lint/UnusedBlockArgument:
|
||||
Exclude:
|
||||
- 'config/initializers/content_security_policy.rb'
|
||||
- 'config/initializers/doorkeeper.rb'
|
||||
- 'config/initializers/paperclip.rb'
|
||||
- 'config/initializers/simple_form.rb'
|
||||
|
||||
# Configuration parameters: AllowedMethods, AllowedPatterns, CountRepeatedAttributes.
|
||||
Metrics/AbcSize:
|
||||
Max: 144
|
||||
|
@ -73,26 +64,6 @@ RSpec/AnyInstance:
|
|||
RSpec/ExampleLength:
|
||||
Max: 22
|
||||
|
||||
# Configuration parameters: AssignmentOnly.
|
||||
RSpec/InstanceVariable:
|
||||
Exclude:
|
||||
- 'spec/controllers/api/v1/streaming_controller_spec.rb'
|
||||
- 'spec/controllers/auth/confirmations_controller_spec.rb'
|
||||
- 'spec/controllers/auth/passwords_controller_spec.rb'
|
||||
- 'spec/controllers/auth/sessions_controller_spec.rb'
|
||||
- 'spec/controllers/concerns/export_controller_concern_spec.rb'
|
||||
- 'spec/controllers/home_controller_spec.rb'
|
||||
- 'spec/controllers/settings/two_factor_authentication/webauthn_credentials_controller_spec.rb'
|
||||
- 'spec/controllers/statuses_cleanup_controller_spec.rb'
|
||||
- 'spec/models/concerns/account_finder_concern_spec.rb'
|
||||
- 'spec/models/concerns/account_interactions_spec.rb'
|
||||
- 'spec/models/public_feed_spec.rb'
|
||||
- 'spec/serializers/activitypub/note_serializer_spec.rb'
|
||||
- 'spec/serializers/activitypub/update_poll_serializer_spec.rb'
|
||||
- 'spec/services/remove_status_service_spec.rb'
|
||||
- 'spec/services/search_service_spec.rb'
|
||||
- 'spec/services/unblock_domain_service_spec.rb'
|
||||
|
||||
RSpec/LetSetup:
|
||||
Exclude:
|
||||
- 'spec/controllers/api/v1/accounts/statuses_controller_spec.rb'
|
||||
|
@ -135,12 +106,6 @@ RSpec/LetSetup:
|
|||
- 'spec/services/unsuspend_account_service_spec.rb'
|
||||
- 'spec/workers/scheduler/user_cleanup_scheduler_spec.rb'
|
||||
|
||||
RSpec/MessageChain:
|
||||
Exclude:
|
||||
- 'spec/models/concerns/remotable_spec.rb'
|
||||
- 'spec/models/session_activation_spec.rb'
|
||||
- 'spec/models/setting_spec.rb'
|
||||
|
||||
RSpec/MultipleExpectations:
|
||||
Max: 8
|
||||
|
||||
|
@ -180,11 +145,6 @@ Rails/HasManyOrHasOneDependent:
|
|||
- 'app/models/user.rb'
|
||||
- 'app/models/web/push_subscription.rb'
|
||||
|
||||
Rails/I18nLocaleTexts:
|
||||
Exclude:
|
||||
- 'lib/tasks/mastodon.rake'
|
||||
- 'spec/helpers/flashes_helper_spec.rb'
|
||||
|
||||
# Configuration parameters: Include.
|
||||
# Include: app/controllers/**/*.rb, app/mailers/**/*.rb
|
||||
Rails/LexicallyScopedActionFilter:
|
||||
|
@ -560,14 +520,6 @@ Style/SingleArgumentDig:
|
|||
Exclude:
|
||||
- 'lib/webpacker/manifest_extensions.rb'
|
||||
|
||||
# This cop supports safe autocorrection (--autocorrect).
|
||||
# Configuration parameters: EnforcedStyle.
|
||||
# SupportedStyles: require_parentheses, require_no_parentheses
|
||||
Style/StabbyLambdaParentheses:
|
||||
Exclude:
|
||||
- 'config/environments/production.rb'
|
||||
- 'config/initializers/content_security_policy.rb'
|
||||
|
||||
# This cop supports safe autocorrection (--autocorrect).
|
||||
Style/StderrPuts:
|
||||
Exclude:
|
||||
|
@ -626,5 +578,3 @@ Style/TrailingCommaInHashLiteral:
|
|||
Style/WordArray:
|
||||
Exclude:
|
||||
- 'app/helpers/languages_helper.rb'
|
||||
- 'spec/controllers/settings/imports_controller_spec.rb'
|
||||
- 'spec/models/form/import_spec.rb'
|
||||
|
|
2
Gemfile
2
Gemfile
|
@ -16,7 +16,7 @@ gem 'dotenv-rails', '~> 2.8'
|
|||
|
||||
gem 'aws-sdk-s3', '~> 1.123', require: false
|
||||
gem 'fog-core', '<= 2.4.0'
|
||||
gem 'fog-openstack', '~> 0.3', require: false
|
||||
gem 'fog-openstack', '~> 1.0', require: false
|
||||
gem 'kt-paperclip', '~> 7.2'
|
||||
gem 'md-paperclip-azure', '~> 2.2', require: false
|
||||
gem 'blurhash', '~> 0.1'
|
||||
|
|
20
Gemfile.lock
20
Gemfile.lock
|
@ -263,7 +263,7 @@ GEM
|
|||
erubi (1.12.0)
|
||||
et-orbi (1.2.7)
|
||||
tzinfo
|
||||
excon (0.100.0)
|
||||
excon (0.104.0)
|
||||
fabrication (2.30.0)
|
||||
faker (3.2.2)
|
||||
i18n (>= 1.8.11, < 2)
|
||||
|
@ -298,19 +298,18 @@ GEM
|
|||
ffi-compiler (1.0.1)
|
||||
ffi (>= 1.0.0)
|
||||
rake
|
||||
fog-core (2.1.0)
|
||||
fog-core (2.3.0)
|
||||
builder
|
||||
excon (~> 0.58)
|
||||
formatador (~> 0.2)
|
||||
excon (~> 0.71)
|
||||
formatador (>= 0.2, < 2.0)
|
||||
mime-types
|
||||
fog-json (1.2.0)
|
||||
fog-core
|
||||
multi_json (~> 1.10)
|
||||
fog-openstack (0.3.10)
|
||||
fog-core (>= 1.45, <= 2.1.0)
|
||||
fog-openstack (1.1.0)
|
||||
fog-core (~> 2.1)
|
||||
fog-json (>= 1.0)
|
||||
ipaddress (>= 0.8)
|
||||
formatador (0.3.0)
|
||||
formatador (1.1.0)
|
||||
fugit (1.8.1)
|
||||
et-orbi (~> 1, >= 1.2.7)
|
||||
raabro (~> 1.4)
|
||||
|
@ -370,7 +369,6 @@ GEM
|
|||
terminal-table (>= 1.5.1)
|
||||
idn-ruby (0.1.5)
|
||||
io-console (0.6.0)
|
||||
ipaddress (0.8.3)
|
||||
irb (1.8.1)
|
||||
rdoc
|
||||
reline (>= 0.3.8)
|
||||
|
@ -452,7 +450,7 @@ GEM
|
|||
memory_profiler (1.0.1)
|
||||
mime-types (3.5.1)
|
||||
mime-types-data (~> 3.2015)
|
||||
mime-types-data (3.2023.0808)
|
||||
mime-types-data (3.2023.1003)
|
||||
mini_mime (1.1.5)
|
||||
mini_portile2 (2.8.5)
|
||||
minitest (5.20.0)
|
||||
|
@ -858,7 +856,7 @@ DEPENDENCIES
|
|||
fast_blank (~> 1.0)
|
||||
fastimage
|
||||
fog-core (<= 2.4.0)
|
||||
fog-openstack (~> 0.3)
|
||||
fog-openstack (~> 1.0)
|
||||
fuubar (~> 2.5)
|
||||
haml-rails (~> 2.0)
|
||||
haml_lint
|
||||
|
|
|
@ -31,6 +31,11 @@ module Admin
|
|||
|
||||
private
|
||||
|
||||
def batched_ordered_status_edits
|
||||
@status.edits.reorder(nil).includes(:account, status: [:account]).find_each(order: :asc)
|
||||
end
|
||||
helper_method :batched_ordered_status_edits
|
||||
|
||||
def admin_status_batch_action_params
|
||||
params.require(:admin_status_batch_action).permit(status_ids: [])
|
||||
end
|
||||
|
|
|
@ -91,6 +91,14 @@ module ApplicationHelper
|
|||
end
|
||||
end
|
||||
|
||||
def html_title
|
||||
safe_join(
|
||||
[content_for(:page_title).to_s.chomp, title]
|
||||
.select(&:present?),
|
||||
' - '
|
||||
)
|
||||
end
|
||||
|
||||
def title
|
||||
Rails.env.production? ? site_title : "#{site_title} (Dev)"
|
||||
end
|
||||
|
|
|
@ -298,5 +298,3 @@ module LanguagesHelper
|
|||
locale_name.to_sym if locale_name.present? && I18n.available_locales.include?(locale_name.to_sym)
|
||||
end
|
||||
end
|
||||
|
||||
# rubocop:enable Metrics/ModuleLength
|
||||
|
|
|
@ -42,4 +42,5 @@ export interface ApiAccountJSON {
|
|||
suspended?: boolean;
|
||||
limited?: boolean;
|
||||
memorial?: boolean;
|
||||
hide_collections: boolean;
|
||||
}
|
||||
|
|
|
@ -45,6 +45,7 @@ const mapStateToProps = (state, { params: { acct, id } }) => {
|
|||
hasMore: !!state.getIn(['user_lists', 'followers', accountId, 'next']),
|
||||
isLoading: state.getIn(['user_lists', 'followers', accountId, 'isLoading'], true),
|
||||
suspended: state.getIn(['accounts', accountId, 'suspended'], false),
|
||||
hideCollections: state.getIn(['accounts', accountId, 'hide_collections'], false),
|
||||
hidden: getAccountHidden(state, accountId),
|
||||
blockedBy: state.getIn(['relationships', accountId, 'blocked_by'], false),
|
||||
};
|
||||
|
@ -111,7 +112,7 @@ class Followers extends ImmutablePureComponent {
|
|||
}, 300, { leading: true });
|
||||
|
||||
render () {
|
||||
const { accountId, accountIds, hasMore, blockedBy, isAccount, multiColumn, isLoading, suspended, hidden, remote, remoteUrl } = this.props;
|
||||
const { accountId, accountIds, hasMore, blockedBy, isAccount, multiColumn, isLoading, suspended, hidden, remote, remoteUrl, hideCollections } = this.props;
|
||||
|
||||
if (!isAccount) {
|
||||
return (
|
||||
|
@ -137,6 +138,8 @@ class Followers extends ImmutablePureComponent {
|
|||
emptyMessage = <LimitedAccountHint accountId={accountId} />;
|
||||
} else if (blockedBy) {
|
||||
emptyMessage = <FormattedMessage id='empty_column.account_unavailable' defaultMessage='Profile unavailable' />;
|
||||
} else if (hideCollections && accountIds.isEmpty()) {
|
||||
emptyMessage = <FormattedMessage id='empty_column.account_hides_collections' defaultMessage='This user has chosen to not make this information available' />;
|
||||
} else if (remote && accountIds.isEmpty()) {
|
||||
emptyMessage = <RemoteHint url={remoteUrl} />;
|
||||
} else {
|
||||
|
|
|
@ -45,6 +45,7 @@ const mapStateToProps = (state, { params: { acct, id } }) => {
|
|||
hasMore: !!state.getIn(['user_lists', 'following', accountId, 'next']),
|
||||
isLoading: state.getIn(['user_lists', 'following', accountId, 'isLoading'], true),
|
||||
suspended: state.getIn(['accounts', accountId, 'suspended'], false),
|
||||
hideCollections: state.getIn(['accounts', accountId, 'hide_collections'], false),
|
||||
hidden: getAccountHidden(state, accountId),
|
||||
blockedBy: state.getIn(['relationships', accountId, 'blocked_by'], false),
|
||||
};
|
||||
|
@ -111,7 +112,7 @@ class Following extends ImmutablePureComponent {
|
|||
}, 300, { leading: true });
|
||||
|
||||
render () {
|
||||
const { accountId, accountIds, hasMore, blockedBy, isAccount, multiColumn, isLoading, suspended, hidden, remote, remoteUrl } = this.props;
|
||||
const { accountId, accountIds, hasMore, blockedBy, isAccount, multiColumn, isLoading, suspended, hidden, remote, remoteUrl, hideCollections } = this.props;
|
||||
|
||||
if (!isAccount) {
|
||||
return (
|
||||
|
@ -137,6 +138,8 @@ class Following extends ImmutablePureComponent {
|
|||
emptyMessage = <LimitedAccountHint accountId={accountId} />;
|
||||
} else if (blockedBy) {
|
||||
emptyMessage = <FormattedMessage id='empty_column.account_unavailable' defaultMessage='Profile unavailable' />;
|
||||
} else if (hideCollections && accountIds.isEmpty()) {
|
||||
emptyMessage = <FormattedMessage id='empty_column.account_hides_collections' defaultMessage='This user has chosen to not make this information available' />;
|
||||
} else if (remote && accountIds.isEmpty()) {
|
||||
emptyMessage = <RemoteHint url={remoteUrl} />;
|
||||
} else {
|
||||
|
|
|
@ -88,7 +88,7 @@
|
|||
"attachments_list.unprocessed": "(ausstehend)",
|
||||
"audio.hide": "Audio ausblenden",
|
||||
"autosuggest_hashtag.per_week": "{count} pro Woche",
|
||||
"boost_modal.combo": "Drücke {combo}, um das beim nächsten Mal zu überspringen",
|
||||
"boost_modal.combo": "Mit {combo} wird dieses Fenster beim nächsten Mal nicht mehr angezeigt",
|
||||
"bundle_column_error.copy_stacktrace": "Fehlerbericht kopieren",
|
||||
"bundle_column_error.error.body": "Die angeforderte Seite konnte nicht dargestellt werden. Dies könnte auf einen Fehler in unserem Code oder auf ein Browser-Kompatibilitätsproblem zurückzuführen sein.",
|
||||
"bundle_column_error.error.title": "Oh nein!",
|
||||
|
|
|
@ -222,6 +222,7 @@
|
|||
"emoji_button.search_results": "Search results",
|
||||
"emoji_button.symbols": "Symbols",
|
||||
"emoji_button.travel": "Travel & Places",
|
||||
"empty_column.account_hides_collections": "This user has chosen to not make this information available",
|
||||
"empty_column.account_suspended": "Account suspended",
|
||||
"empty_column.account_timeline": "No posts here!",
|
||||
"empty_column.account_unavailable": "Profile unavailable",
|
||||
|
|
|
@ -62,7 +62,7 @@
|
|||
"account.share": "שתף את הפרופיל של @{name}",
|
||||
"account.show_reblogs": "הצג הדהודים מאת @{name}",
|
||||
"account.statuses_counter": "{count, plural, one {הודעה} two {הודעותיים} many {{count} הודעות} other {{count} הודעות}}",
|
||||
"account.unblock": "הסר את החסימה של @{name}",
|
||||
"account.unblock": "להסיר חסימה ל- @{name}",
|
||||
"account.unblock_domain": "הסירי את החסימה של קהילת {domain}",
|
||||
"account.unblock_short": "הסר חסימה",
|
||||
"account.unendorse": "אל תקדם בפרופיל",
|
||||
|
|
|
@ -222,7 +222,7 @@
|
|||
"emoji_button.search_results": "Výsledky hľadania",
|
||||
"emoji_button.symbols": "Symboly",
|
||||
"emoji_button.travel": "Cestovanie a miesta",
|
||||
"empty_column.account_suspended": "Účet bol vylúčený",
|
||||
"empty_column.account_suspended": "Účet bol pozastavený",
|
||||
"empty_column.account_timeline": "Nie sú tu žiadne príspevky!",
|
||||
"empty_column.account_unavailable": "Profil nedostupný",
|
||||
"empty_column.blocks": "Ešte si nikoho nezablokoval/a.",
|
||||
|
|
|
@ -93,6 +93,7 @@ export const accountDefaultValues: AccountShape = {
|
|||
memorial: false,
|
||||
limited: false,
|
||||
moved: null,
|
||||
hide_collections: false,
|
||||
};
|
||||
|
||||
const AccountFactory = ImmutableRecord<AccountShape>(accountDefaultValues);
|
||||
|
|
|
@ -16,12 +16,36 @@ class StatusCacheHydrator
|
|||
# We take advantage of the fact that some relationships can only occur with an original status, not
|
||||
# the reblog that wraps it, so we can assume that some values are always false
|
||||
if payload[:reblog]
|
||||
hydrate_reblog_payload(payload, account_id)
|
||||
else
|
||||
hydrate_non_reblog_payload(payload, account_id)
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def hydrate_non_reblog_payload(empty_payload, account_id)
|
||||
empty_payload.tap do |payload|
|
||||
payload[:favourited] = Favourite.where(account_id: account_id, status_id: @status.id).exists?
|
||||
payload[:reblogged] = Status.where(account_id: account_id, reblog_of_id: @status.id).exists?
|
||||
payload[:muted] = ConversationMute.where(account_id: account_id, conversation_id: @status.conversation_id).exists?
|
||||
payload[:bookmarked] = Bookmark.where(account_id: account_id, status_id: @status.id).exists?
|
||||
payload[:pinned] = StatusPin.where(account_id: account_id, status_id: @status.id).exists? if @status.account_id == account_id
|
||||
payload[:filtered] = mapped_applied_custom_filter(account_id, @status)
|
||||
|
||||
if payload[:poll]
|
||||
payload[:poll][:voted] = @status.account_id == account_id
|
||||
payload[:poll][:own_votes] = []
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def hydrate_reblog_payload(empty_payload, account_id)
|
||||
empty_payload.tap do |payload|
|
||||
payload[:muted] = false
|
||||
payload[:bookmarked] = false
|
||||
payload[:pinned] = false if @status.account_id == account_id
|
||||
payload[:filtered] = CustomFilter
|
||||
.apply_cached_filters(CustomFilter.cached_filters_for(account_id), @status.reblog)
|
||||
.map { |filter| serialized_filter(filter) }
|
||||
payload[:filtered] = mapped_applied_custom_filter(account_id, @status.reblog)
|
||||
|
||||
# If the reblogged status is being delivered to the author who disabled the display of the application
|
||||
# used to create the status, we need to hydrate it here too
|
||||
|
@ -47,26 +71,14 @@ class StatusCacheHydrator
|
|||
|
||||
payload[:favourited] = payload[:reblog][:favourited]
|
||||
payload[:reblogged] = payload[:reblog][:reblogged]
|
||||
else
|
||||
payload[:favourited] = Favourite.where(account_id: account_id, status_id: @status.id).exists?
|
||||
payload[:reblogged] = Status.where(account_id: account_id, reblog_of_id: @status.id).exists?
|
||||
payload[:muted] = ConversationMute.where(account_id: account_id, conversation_id: @status.conversation_id).exists?
|
||||
payload[:bookmarked] = Bookmark.where(account_id: account_id, status_id: @status.id).exists?
|
||||
payload[:pinned] = StatusPin.where(account_id: account_id, status_id: @status.id).exists? if @status.account_id == account_id
|
||||
payload[:filtered] = CustomFilter
|
||||
.apply_cached_filters(CustomFilter.cached_filters_for(account_id), @status)
|
||||
end
|
||||
end
|
||||
|
||||
def mapped_applied_custom_filter(account_id, status)
|
||||
CustomFilter
|
||||
.apply_cached_filters(CustomFilter.cached_filters_for(account_id), status)
|
||||
.map { |filter| serialized_filter(filter) }
|
||||
|
||||
if payload[:poll]
|
||||
payload[:poll][:voted] = @status.account_id == account_id
|
||||
payload[:poll][:own_votes] = []
|
||||
end
|
||||
end
|
||||
|
||||
payload
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def serialized_filter(filter)
|
||||
ActiveModelSerializers::SerializableResource.new(
|
||||
|
|
|
@ -8,7 +8,7 @@ class REST::AccountSerializer < ActiveModel::Serializer
|
|||
|
||||
attributes :id, :username, :acct, :display_name, :locked, :bot, :discoverable, :group, :created_at,
|
||||
:note, :url, :uri, :avatar, :avatar_static, :header, :header_static,
|
||||
:followers_count, :following_count, :statuses_count, :last_status_at
|
||||
:followers_count, :following_count, :statuses_count, :last_status_at, :hide_collections
|
||||
|
||||
has_one :moved_to_account, key: :moved, serializer: REST::AccountSerializer, if: :moved_and_not_nested?
|
||||
|
||||
|
|
|
@ -47,7 +47,7 @@
|
|||
%h3= t('admin.statuses.history')
|
||||
|
||||
%ol.history
|
||||
- @status.edits.reorder(nil).includes(:account, status: [:account]).find_each(order: :asc).with_index do |status_edit, i|
|
||||
- batched_ordered_status_edits.with_index do |status_edit, i|
|
||||
%li
|
||||
.history__entry
|
||||
%h5
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
%meta{ name: 'theme-color', content: '#191b22' }/
|
||||
%meta{ name: 'apple-mobile-web-app-capable', content: 'yes' }/
|
||||
|
||||
%title= content_for?(:page_title) ? safe_join([yield(:page_title).chomp.html_safe, title], ' - ') : title
|
||||
%title= html_title
|
||||
|
||||
= javascript_pack_tag "common", crossorigin: 'anonymous'
|
||||
- if @theme
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
default: &default
|
||||
adapter: postgresql
|
||||
pool: <%= ENV["DB_POOL"] || ENV['MAX_THREADS'] || 5 %>
|
||||
pool: <%= ENV["DB_POOL"] || (if Sidekiq.server? then Sidekiq[:concurrency] else ENV['MAX_THREADS'] end) || 5 %>
|
||||
timeout: 5000
|
||||
connect_timeout: 15
|
||||
encoding: unicode
|
||||
|
|
|
@ -44,7 +44,7 @@ Rails.application.configure do
|
|||
config.force_ssl = true
|
||||
config.ssl_options = {
|
||||
redirect: {
|
||||
exclude: ->request { request.path.start_with?('/health') || request.headers["Host"].end_with?('.onion') || request.headers["Host"].end_with?('.i2p') }
|
||||
exclude: ->(request) { request.path.start_with?('/health') || request.headers["Host"].end_with?('.onion') || request.headers["Host"].end_with?('.i2p') }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -84,7 +84,7 @@ end
|
|||
# https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy-Report-Only
|
||||
# Rails.application.config.content_security_policy_report_only = true
|
||||
|
||||
Rails.application.config.content_security_policy_nonce_generator = ->request { SecureRandom.base64(16) }
|
||||
Rails.application.config.content_security_policy_nonce_generator = ->(_request) { SecureRandom.base64(16) }
|
||||
|
||||
Rails.application.config.content_security_policy_nonce_directives = %w(style-src)
|
||||
|
||||
|
@ -109,7 +109,7 @@ Rails.application.reloader.to_prepare do
|
|||
p.worker_src :none
|
||||
end
|
||||
|
||||
LetterOpenerWeb::LettersController.after_action do |p|
|
||||
LetterOpenerWeb::LettersController.after_action do
|
||||
request.content_security_policy_nonce_directives = %w(script-src)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -169,7 +169,7 @@ Doorkeeper.configure do
|
|||
# Under some circumstances you might want to have applications auto-approved,
|
||||
# so that the user skips the authorization step.
|
||||
# For example if dealing with a trusted application.
|
||||
skip_authorization do |resource_owner, client|
|
||||
skip_authorization do |_resource_owner, client|
|
||||
client.application.superapp?
|
||||
end
|
||||
|
||||
|
|
|
@ -11,7 +11,7 @@ Paperclip.interpolates :filename do |attachment, style|
|
|||
end
|
||||
end
|
||||
|
||||
Paperclip.interpolates :prefix_path do |attachment, style|
|
||||
Paperclip.interpolates :prefix_path do |attachment, _style|
|
||||
if attachment.storage_schema_version >= 1 && attachment.instance.respond_to?(:local?) && !attachment.instance.local?
|
||||
'cache' + File::SEPARATOR
|
||||
else
|
||||
|
@ -19,7 +19,7 @@ Paperclip.interpolates :prefix_path do |attachment, style|
|
|||
end
|
||||
end
|
||||
|
||||
Paperclip.interpolates :prefix_url do |attachment, style|
|
||||
Paperclip.interpolates :prefix_url do |attachment, _style|
|
||||
if attachment.storage_schema_version >= 1 && attachment.instance.respond_to?(:local?) && !attachment.instance.local?
|
||||
'cache/'
|
||||
else
|
||||
|
|
|
@ -175,7 +175,7 @@ SimpleForm.setup do |config|
|
|||
# config.item_wrapper_class = nil
|
||||
|
||||
# How the label text should be generated altogether with the required text.
|
||||
config.label_text = lambda { |label, required, explicit_label| "#{label} #{required}" }
|
||||
config.label_text = lambda { |label, required, _explicit_label| "#{label} #{required}" }
|
||||
|
||||
# You can define the class to use on all labels. Default is nil.
|
||||
# config.label_class = nil
|
||||
|
|
|
@ -1 +1,14 @@
|
|||
---
|
||||
lt:
|
||||
activerecord:
|
||||
errors:
|
||||
models:
|
||||
account:
|
||||
attributes:
|
||||
username:
|
||||
invalid: turi būti tik raidės, skaičiai ir pabraukimai.
|
||||
reserved: užimtas.
|
||||
admin/webhook:
|
||||
attributes:
|
||||
url:
|
||||
invalid: nėra tinkamas URL adresas.
|
||||
|
|
|
@ -556,6 +556,7 @@ be:
|
|||
total_reported: Скаргі на іх
|
||||
total_storage: Медыя дадаткі
|
||||
totals_time_period_hint_html: Паказаныя агульныя значэнні ніжэй уключаюць даныя за ўвесь час.
|
||||
unknown_instance: На дадзены момант няма запісаў аб гэтым дамене на гэтым серверы.
|
||||
invites:
|
||||
deactivate_all: Дэактываваць усё
|
||||
filter:
|
||||
|
@ -1076,6 +1077,14 @@ be:
|
|||
hint_html: Засталася яшчэ адна рэч! Каб не дапусціць спаму, нам трэба пацвердзіць, што вы чалавек. Разгадайце CAPTCHA ніжэй і націсніце «Працягнуць».
|
||||
title: Праверка бяспекі
|
||||
confirmations:
|
||||
awaiting_review: Ваш электронны адрас пацверджаны! Адміністрацыя %{domain} зараз разглядае вашу рэгістрацыю. Вы атрымаеце паведамленне па электроннай пошце, калі ваш уліковы запіс будзе ўхвалены!
|
||||
awaiting_review_title: Ваша рэгістрацыя разглядаецца
|
||||
clicking_this_link: націснуць на гэту спасылку
|
||||
login_link: увайсці
|
||||
proceed_to_login_html: Цяпер вы можаце перайсці да %{login_link}.
|
||||
redirect_to_app_html: Вы павінны былі быць перанакіраваны ў праграму <strong>%{app_name}</strong>. Калі гэтага не адбылося, паспрабуйце %{clicking_this_link} або вярніцеся да праграмы ўручную.
|
||||
registration_complete: Ваша рэгістрацыя на %{domain} завершана!
|
||||
welcome_title: Вітаем, %{name}!
|
||||
wrong_email_hint: Калі гэты адрас электроннай пошты памылковы, вы можаце змяніць яго ў наладах уліковага запісу.
|
||||
delete_account: Выдаліць уліковы запіс
|
||||
delete_account_html: Калі вы жадаеце выдаліць ваш уліковы запіс, можаце <a href="%{path}">працягнуць тут</a>. Ад вас будзе запатрабавана пацвярджэнне.
|
||||
|
@ -1137,6 +1146,7 @@ be:
|
|||
functional: Ваш уліковы запіс поўнасцю працуе.
|
||||
pending: Ваша заяўка разглядаецца нашым супрацоўнікам. Гэта можа заняць некаторы час. Вы атрымаеце электронны ліст, калі заяўка будзе ўхвалена.
|
||||
redirecting_to: Ваш уліковы запіс неактыўны, бо ў цяперашні час ён перанакіроўваецца на %{acct}.
|
||||
self_destruct: Паколькі %{domain} зачыняецца, вы атрымаеце толькі абмежаваны доступ да свайго уліковага запісу.
|
||||
view_strikes: Праглядзець мінулыя папярэджанні для вашага ўліковага запісу
|
||||
too_fast: Форма адпраўлена занадта хутка, паспрабуйце яшчэ раз.
|
||||
use_security_key: Выкарыстаеце ключ бяспекі
|
||||
|
@ -1622,6 +1632,9 @@ be:
|
|||
over_daily_limit: Вы перавысілі ліміт ў %{limit} запланаваных на сёння допісаў
|
||||
over_total_limit: Вы перавысілі ліміт ў %{limit} запланаваных допісаў
|
||||
too_soon: Запланаваная дата мусіць быць у будучыні
|
||||
self_destruct:
|
||||
lead_html: На жаль, дамен <strong>%{domain}</strong> зачыняецца назаўсёды. Калі ў вас быў уліковы запіс, вы не зможаце працягваць выкарыстоўваць яго, але вы ўсё яшчэ можаце запытаць рэзервовае капіраванне вашых даных.
|
||||
title: Гэты сервер зачыняецца
|
||||
sessions:
|
||||
activity: Апошняя актыўнасць
|
||||
browser: Браўзер
|
||||
|
|
|
@ -1600,7 +1600,7 @@ ko:
|
|||
windows: 윈도우
|
||||
windows_mobile: 윈도우 모바일
|
||||
windows_phone: 윈도우 폰
|
||||
revoke: 삭제
|
||||
revoke: 취소
|
||||
revoke_success: 세션을 성공적으로 취소하였습니다
|
||||
title: 세션
|
||||
view_authentication_history: 내 계정에 대한 인증 이력 보기
|
||||
|
|
|
@ -232,6 +232,12 @@ lt:
|
|||
unassign: Nepriskirti
|
||||
unresolved: Neišspręsti
|
||||
updated_at: Atnaujinti
|
||||
roles:
|
||||
everyone: Numatytieji leidimai
|
||||
everyone_full_description_html: Tai – <strong>bazinis vaidmuo</strong>, turintis įtakos <strong>visiems naudotojams</strong>, net ir tiems, kurie neturi priskirto vaidmens. Visi kiti vaidmenys iš jo paveldi teises.
|
||||
settings:
|
||||
domain_blocks:
|
||||
all: Visiems
|
||||
statuses:
|
||||
back_to_account: Atgal į paskyros puslapį
|
||||
media:
|
||||
|
@ -250,6 +256,10 @@ lt:
|
|||
body: "%{reporter} parašė skundą apie %{target}"
|
||||
body_remote: Kažkas iš %{domain} parašė skundą apie %{target}
|
||||
subject: Naujas skundas %{instance} (#%{id})
|
||||
appearance:
|
||||
localization:
|
||||
body: Mastodon verčia savanoriai.
|
||||
guide_link_text: Visi gali prisidėti.
|
||||
application_mailer:
|
||||
notification_preferences: Keisti el pašto parinktis
|
||||
settings: 'Keisti el pašto parinktis: %{link}'
|
||||
|
@ -458,9 +468,9 @@ lt:
|
|||
private: Tik sekėjams
|
||||
private_long: Rodyti tik sekėjams
|
||||
public: Viešas
|
||||
public_long: Matyti gali visi
|
||||
public_long: Visi gali matyti
|
||||
unlisted: Neįtrauktas į sąrašus
|
||||
unlisted_long: Matyti gali visi, tačiau nėra įtraukta į viešas laiko juostas
|
||||
unlisted_long: Matyti gali visi, tačiau nėra įtraukti į viešąsias laiko skales
|
||||
stream_entries:
|
||||
sensitive_content: Jautrus turinys
|
||||
themes:
|
||||
|
@ -507,4 +517,5 @@ lt:
|
|||
seamless_external_login: Jūs esate prisijungę per išorini įrenginį, todėl slaptąžodis ir el pašto nustatymai neprieinami.
|
||||
signed_in_as: 'Prisijungta kaip:'
|
||||
verification:
|
||||
hint_html: "<strong>Savo tapatybės patvirtinimas Mastodon skirtas visiems.</strong> Remiantis atviraisiais žiniatinklio standartais, dabar ir visam laikui nemokamas. Viskas, ko tau reikia, yra asmeninė svetainė, pagal kurią žmonės tave atpažįsta. Kai iš savo profilio pateiksi nuorodą į šią svetainę, patikrinsime, ar svetainėje yra nuoroda į tavo profilį, ir parodysime vizualinį indikatorių."
|
||||
verification: Patvirtinimas
|
||||
|
|
|
@ -1079,6 +1079,7 @@ ru:
|
|||
confirmations:
|
||||
awaiting_review: Ваш адрес электронной почты подтвержден! Сотрудники %{domain} проверяют вашу регистрацию. Вы получите письмо, если они подтвердят вашу учетную запись!
|
||||
awaiting_review_title: Ваша регистрация проверяется
|
||||
clicking_this_link: нажатие на эту ссылку
|
||||
login_link: войти
|
||||
proceed_to_login_html: Теперь вы можете перейти к %{login_link}.
|
||||
registration_complete: Ваша регистрация на %{domain} завершена!
|
||||
|
@ -1536,7 +1537,7 @@ ru:
|
|||
update:
|
||||
subject: "%{name} изменил(а) пост"
|
||||
notifications:
|
||||
administration_emails: E-mail уведомления администратора
|
||||
administration_emails: Уведомления администратора по электронной почте
|
||||
email_events: События для e-mail уведомлений
|
||||
email_events_hint: 'Выберите события, для которых вы хотели бы получать уведомления:'
|
||||
other_settings: Остальные настройки уведомлений
|
||||
|
@ -1631,6 +1632,7 @@ ru:
|
|||
over_total_limit: Вы превысили лимит на %{limit} запланированных постов
|
||||
too_soon: Запланированная дата должна быть в будущем
|
||||
self_destruct:
|
||||
lead_html: К сожалению, <strong>%{domain}</strong> закрывается навсегда. Если вас учётная запись находиться здесь вы не сможете продолжить использовать его, но вы можете запросить резервную копию ваших данных.
|
||||
title: Этот сервер закрывается
|
||||
sessions:
|
||||
activity: Последняя активность
|
||||
|
|
|
@ -1 +1,45 @@
|
|||
---
|
||||
lt:
|
||||
simple_form:
|
||||
hints:
|
||||
account:
|
||||
discoverable: Tavo vieši įrašai ir profilis gali būti rodomi arba rekomenduojami įvairiose Mastodon vietose, o profilis gali būti siūlomas kitiems naudotojams.
|
||||
display_name: Tavo pilnas vardas arba smagus vardas.
|
||||
fields: Tavo pagrindinis puslapis, įvardžiai, amžius, bet kas, ko tik nori.
|
||||
indexable: Tavo vieši įrašai gali būti rodomi Mastodon paieškos rezultatuose. Žmonės, kurie bendravo su tavo įrašais, gali jų ieškoti nepriklausomai nuo to.
|
||||
note: 'Gali @paminėti kitus žmones arba #saitažodžius.'
|
||||
show_collections: Žmonės galės peržiūrėti tavo sekimus ir sekėjus. Žmonės, kuriuos seki, matys, kad juos seki, nepaisant to.
|
||||
unlocked: Žmonės galės tave sekti nepaprašę patvirtinimo. Panaikink žymėjimą, jei nori peržiūrėti sekimo prašymus ir pasirinkti, ar priimti, ar atmesti naujus sekėjus.
|
||||
account_warning_preset:
|
||||
text: Gali naudoti įrašų sintaksę, pavyzdžiui, URL adresus, saitažodus ir paminėjimus
|
||||
defaults:
|
||||
header: PNG, GIF arba JPG. Ne daugiau kaip %{size}. Bus sumažintas iki %{dimensions}tšk.
|
||||
inbox_url: Nukopijuok URL adresą iš pradinio puslapio perdavėjo, kurį nori naudoti
|
||||
irreversible: Filtruoti įrašai išnyks negrįžtamai, net jei vėliau filtras bus pašalintas
|
||||
locale: Naudotojo sąsajos kalba, el. laiškai ir stumiamieji pranešimai
|
||||
password: Naudok bent 8 simbolius
|
||||
phrase: Bus suderinta, neatsižvelgiant į teksto korpusą arba įrašo turinio įspėjimą
|
||||
setting_display_media_hide_all: Visada slėpti žiniasklaidą
|
||||
setting_display_media_show_all: Visada rodyti žiniasklaidą
|
||||
setting_use_blurhash: Gradientai pagrįsti paslėptų vaizdų spalvomis, tačiau užgožia bet kokias detales
|
||||
setting_use_pending_items: Slėpti laiko skalės naujienas po paspaudimo, vietoj automatinio kanalo slinkimo
|
||||
featured_tag:
|
||||
name: 'Štai keletas pastaruoju metu dažniausiai saitažodžių, kurių tu naudojai:'
|
||||
form_admin_settings:
|
||||
peers_api_enabled: Domenų pavadinimų sąrašas, su kuriais šis serveris susidūrė fediverse. Čia nėra duomenų apie tai, ar tu bendrauji su tam tikru serveriu, tik apie tai, kad tavo serveris apie jį žino. Tai naudojama tarnybose, kurios renka federacijos statistiką bendrąja prasme.
|
||||
site_contact_email: Kaip žmonės gali su tavimi susisiekti teisiniais ar pagalbos užklausimais.
|
||||
site_contact_username: Kaip žmonės gali tave pasiekti Mastodon.
|
||||
site_extended_description: Bet kokia papildoma informacija, kuri gali būti naudinga lankytojams ir naudotojams. Gali būti struktūrizuota naudojant Markdown sintaksę.
|
||||
trends: Trendai rodo, kurios įrašai, saitažodžiai ir naujienų istorijos tavo serveryje sulaukia didžiausio susidomėjimo.
|
||||
sessions:
|
||||
webauthn: Jei tai USB raktas, būtinai jį įkišk ir, jei reikia, paliesk.
|
||||
settings:
|
||||
indexable: Tavo profilio puslapis gali būti rodomas paieškos rezultatuose Google, Bing ir kituose.
|
||||
labels:
|
||||
featured_tag:
|
||||
name: Saitažodis
|
||||
tag:
|
||||
listable: Leisti šį saitažodį rodyti paieškose ir pasiūlymuose
|
||||
name: Saitažodis
|
||||
trendable: Leisti šį saitažodį rodyti pagal trendus
|
||||
usable: Leisti įrašams naudoti šį saitažodį
|
||||
|
|
|
@ -98,7 +98,7 @@ sk:
|
|||
disabled: Blokovaný
|
||||
pending: Čakajúci
|
||||
silenced: Obmedzený
|
||||
suspended: Vylúčený/á
|
||||
suspended: Pozastavený/á
|
||||
title: Moderácia
|
||||
moderation_notes: Moderátorské poznámky
|
||||
most_recent_activity: Posledná aktivita
|
||||
|
@ -149,8 +149,8 @@ sk:
|
|||
statuses: Príspevkov
|
||||
strikes: Predchádzajúce údery
|
||||
subscribe: Odoberaj
|
||||
suspend: Vylúč
|
||||
suspended: Vylúčený/á
|
||||
suspend: Pozastav
|
||||
suspended: Pozastavený/á
|
||||
suspension_irreversible: Údaje tohto účtu boli nenávratne vymazané. Účet môžete zrušiť, aby sa dal používať, ale neobnovia sa žiadne údaje, ktoré predtým mal.
|
||||
suspension_reversible_hint_html: Účet bol pozastavený a údaje budú úplne odstránené dňa %{date}. Dovtedy je možné účet obnoviť bez akýchkoľvek nepriaznivých účinkov. Ak chcete okamžite odstrániť všetky údaje účtu, môžete tak urobiť nižšie.
|
||||
title: Účty
|
||||
|
@ -162,6 +162,7 @@ sk:
|
|||
undo_suspension: Zruš blokovanie
|
||||
unsilenced_msg: Úspešne zrušené obmedzenie účtu %{username}
|
||||
unsubscribe: Prestaň odoberať
|
||||
unsuspended_msg: "%{username} ov/in účet úspešne spojazdnený"
|
||||
username: Prezývka
|
||||
view_domain: Ukáž súhrn pre doménu
|
||||
warn: Varuj
|
||||
|
@ -209,7 +210,7 @@ sk:
|
|||
resolve_report: Vyrieš nahlásený problém
|
||||
sensitive_account: Vynúť všetky médiá na účte ako chúlostivé
|
||||
silence_account: Utíš účet
|
||||
suspend_account: Vylúč účet
|
||||
suspend_account: Pozastav účet
|
||||
unassigned_report: Odober priradenie nahlásenia
|
||||
unblock_email_account: Odblokuj emailovú adresu
|
||||
unsilence_account: Zvráť obmedzenie účtu
|
||||
|
@ -255,6 +256,7 @@ sk:
|
|||
silence_account_html: "%{name} obmedzil/a účet %{target}"
|
||||
suspend_account_html: "%{name} zablokoval/a účet používateľa %{target}"
|
||||
unassigned_report_html: "%{name} odobral/a report od %{target}"
|
||||
unsuspend_account_html: "%{name} spojazdnil/a účet %{target}"
|
||||
update_user_role_html: "%{name} zmenil/a rolu pre %{target}"
|
||||
deleted_account: zmazaný účet
|
||||
empty: Žiadne záznamy nenájdené.
|
||||
|
@ -341,6 +343,7 @@ sk:
|
|||
confirm_suspension:
|
||||
cancel: Zruš
|
||||
confirm: Vylúč
|
||||
preamble_html: Chystáš sa vylúčiť <strong>%{domain}</strong> a jej poddomény.
|
||||
title: Potvrď blokovanie domény %{domain}
|
||||
created_msg: Doména je v štádiu blokovania
|
||||
destroyed_msg: Blokovanie domény bolo zrušené
|
||||
|
@ -355,7 +358,7 @@ sk:
|
|||
severity:
|
||||
noop: Nič
|
||||
silence: Obmedz
|
||||
suspend: Vylúč
|
||||
suspend: Pozastav
|
||||
title: Nové blokovanie domény
|
||||
not_permitted: Nemáš povolenie na vykonanie tohto kroku
|
||||
obfuscate: Zatemniť názov domény
|
||||
|
@ -416,7 +419,7 @@ sk:
|
|||
reject_media: Zamietni médiá
|
||||
reject_reports: Zamietni hlásenia
|
||||
silence: Obmedzená
|
||||
suspend: Vylúč
|
||||
suspend: Pozastav
|
||||
policy: Zásady
|
||||
reason: Verejné odôvodnenie
|
||||
title: Zásady o obsahu
|
||||
|
@ -537,7 +540,7 @@ sk:
|
|||
statuses: Nahlásený obsah
|
||||
summary:
|
||||
action_preambles:
|
||||
suspend_html: 'Chystáš sa <strong>vylúčiť</strong> účet <strong>@%{acct}</strong>. To urobí:'
|
||||
suspend_html: 'Chystáš sa <strong>pozastaviť</strong> účet <strong>@%{acct}</strong>. To urobí:'
|
||||
actions:
|
||||
delete_html: Vymaž pohoršujúce príspevky
|
||||
mark_as_sensitive_html: Označ médiá pohoršujúcich príspevkov za chúlostivé
|
||||
|
|
|
@ -427,7 +427,11 @@ namespace :mastodon do
|
|||
from: env['SMTP_FROM_ADDRESS'],
|
||||
}
|
||||
|
||||
mail = ActionMailer::Base.new.mail to: send_to, subject: 'Test', body: 'Mastodon SMTP configuration works!'
|
||||
mail = ActionMailer::Base.new.mail(
|
||||
to: send_to,
|
||||
subject: 'Test', # rubocop:disable Rails/I18nLocaleTexts
|
||||
body: 'Mastodon SMTP configuration works!'
|
||||
)
|
||||
mail.deliver
|
||||
break
|
||||
rescue => e
|
||||
|
|
|
@ -211,7 +211,7 @@
|
|||
"husky": "^8.0.3",
|
||||
"jest": "^29.5.0",
|
||||
"jest-environment-jsdom": "^29.5.0",
|
||||
"lint-staged": "^13.2.2",
|
||||
"lint-staged": "^15.0.0",
|
||||
"prettier": "^3.0.0",
|
||||
"react-test-renderer": "^18.2.0",
|
||||
"stylelint": "^15.10.1",
|
||||
|
|
|
@ -26,7 +26,6 @@ describe Api::V1::StreamingController do
|
|||
context 'with streaming api on different host' do
|
||||
before do
|
||||
Rails.configuration.x.streaming_api_base_url = "wss://streaming-#{Rails.configuration.x.web_domain}"
|
||||
@streaming_host = URI.parse(Rails.configuration.x.streaming_api_base_url).host
|
||||
end
|
||||
|
||||
describe 'GET #index' do
|
||||
|
@ -38,7 +37,13 @@ describe Api::V1::StreamingController do
|
|||
[:scheme, :path, :query, :fragment].each do |part|
|
||||
expect(redirect_to_uri.send(part)).to eq(request_uri.send(part)), "redirect target #{part}"
|
||||
end
|
||||
expect(redirect_to_uri.host).to eq(@streaming_host), 'redirect target host'
|
||||
expect(redirect_to_uri.host).to eq(streaming_host), 'redirect target host'
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def streaming_host
|
||||
URI.parse(Rails.configuration.x.streaming_api_base_url).host
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -18,12 +18,14 @@ describe Auth::PasswordsController do
|
|||
|
||||
before do
|
||||
request.env['devise.mapping'] = Devise.mappings[:user]
|
||||
@token = user.send_reset_password_instructions
|
||||
end
|
||||
|
||||
context 'with valid reset_password_token' do
|
||||
it 'returns http success' do
|
||||
get :edit, params: { reset_password_token: @token }
|
||||
token = user.send_reset_password_instructions
|
||||
|
||||
get :edit, params: { reset_password_token: token }
|
||||
|
||||
expect(response).to have_http_status(200)
|
||||
end
|
||||
end
|
||||
|
@ -38,9 +40,9 @@ describe Auth::PasswordsController do
|
|||
|
||||
describe 'POST #update' do
|
||||
let(:user) { Fabricate(:user) }
|
||||
let(:password) { 'reset0password' }
|
||||
|
||||
before do
|
||||
@password = 'reset0password'
|
||||
request.env['devise.mapping'] = Devise.mappings[:user]
|
||||
end
|
||||
|
||||
|
@ -50,9 +52,9 @@ describe Auth::PasswordsController do
|
|||
let!(:web_push_subscription) { Fabricate(:web_push_subscription, access_token: access_token) }
|
||||
|
||||
before do
|
||||
@token = user.send_reset_password_instructions
|
||||
token = user.send_reset_password_instructions
|
||||
|
||||
post :update, params: { user: { password: @password, password_confirmation: @password, reset_password_token: @token } }
|
||||
post :update, params: { user: { password: password, password_confirmation: password, reset_password_token: token } }
|
||||
end
|
||||
|
||||
it 'redirect to sign in' do
|
||||
|
@ -63,7 +65,7 @@ describe Auth::PasswordsController do
|
|||
this_user = User.find(user.id)
|
||||
|
||||
expect(this_user).to_not be_nil
|
||||
expect(this_user.valid_password?(@password)).to be true
|
||||
expect(this_user.valid_password?(password)).to be true
|
||||
end
|
||||
|
||||
it 'deactivates all sessions' do
|
||||
|
@ -81,7 +83,7 @@ describe Auth::PasswordsController do
|
|||
|
||||
context 'with invalid reset_password_token' do
|
||||
before do
|
||||
post :update, params: { user: { password: @password, password_confirmation: @password, reset_password_token: 'some_invalid_value' } }
|
||||
post :update, params: { user: { password: password, password_confirmation: password, reset_password_token: 'some_invalid_value' } }
|
||||
end
|
||||
|
||||
it 'renders reset password' do
|
||||
|
|
|
@ -11,7 +11,7 @@ describe ExportControllerConcern do
|
|||
end
|
||||
|
||||
def export_data
|
||||
@export.account.username
|
||||
'body data value'
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -24,7 +24,7 @@ describe ExportControllerConcern do
|
|||
expect(response).to have_http_status(200)
|
||||
expect(response.media_type).to eq 'text/csv'
|
||||
expect(response.headers['Content-Disposition']).to start_with 'attachment; filename="anonymous.csv"'
|
||||
expect(response.body).to eq user.account.username
|
||||
expect(response.body).to eq 'body data value'
|
||||
end
|
||||
|
||||
it 'returns unauthorized when not signed in' do
|
||||
|
|
|
@ -194,7 +194,7 @@ RSpec.describe Settings::ImportsController do
|
|||
let!(:rows) do
|
||||
[
|
||||
{ 'acct' => 'foo@bar' },
|
||||
{ 'acct' => 'user@bar', 'show_reblogs' => false, 'notify' => true, 'languages' => ['fr', 'de'] },
|
||||
{ 'acct' => 'user@bar', 'show_reblogs' => false, 'notify' => true, 'languages' => %w(fr de) },
|
||||
].map { |data| Fabricate(:bulk_import_row, bulk_import: bulk_import, data: data) }
|
||||
end
|
||||
|
||||
|
|
|
@ -5,9 +5,10 @@ require 'rails_helper'
|
|||
RSpec.describe StatusesCleanupController do
|
||||
render_views
|
||||
|
||||
let!(:user) { Fabricate(:user) }
|
||||
|
||||
before do
|
||||
@user = Fabricate(:user)
|
||||
sign_in @user, scope: :user
|
||||
sign_in user, scope: :user
|
||||
end
|
||||
|
||||
describe 'GET #show' do
|
||||
|
@ -30,9 +31,9 @@ RSpec.describe StatusesCleanupController do
|
|||
end
|
||||
|
||||
it 'updates the account status cleanup policy' do
|
||||
expect(@user.account.statuses_cleanup_policy.enabled).to be true
|
||||
expect(@user.account.statuses_cleanup_policy.keep_direct).to be false
|
||||
expect(@user.account.statuses_cleanup_policy.keep_polls).to be true
|
||||
expect(user.account.statuses_cleanup_policy.enabled).to be true
|
||||
expect(user.account.statuses_cleanup_policy.keep_direct).to be false
|
||||
expect(user.account.statuses_cleanup_policy.keep_polls).to be true
|
||||
end
|
||||
|
||||
it 'redirects' do
|
||||
|
|
|
@ -2,5 +2,5 @@
|
|||
|
||||
Fabricator(:session_activation) do
|
||||
user { Fabricate.build(:user) }
|
||||
session_id 'MyString'
|
||||
session_id { sequence(:session_id) { |i| "session_id_#{i}" } }
|
||||
end
|
||||
|
|
|
@ -296,5 +296,51 @@ describe ApplicationHelper do
|
|||
expect(helper.title).to eq 'site title'
|
||||
expect(Rails.env).to have_received(:production?)
|
||||
end
|
||||
|
||||
it 'returns site title with note on non-production environment' do
|
||||
Setting.site_title = 'site title'
|
||||
allow(Rails.env).to receive(:production?).and_return(false)
|
||||
expect(helper.title).to eq 'site title (Dev)'
|
||||
expect(Rails.env).to have_received(:production?)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'html_title' do
|
||||
before do
|
||||
allow(Rails.env).to receive(:production?).and_return(true)
|
||||
end
|
||||
|
||||
around do |example|
|
||||
site_title = Setting.site_title
|
||||
example.run
|
||||
Setting.site_title = site_title
|
||||
end
|
||||
|
||||
context 'with a page_title content_for value' do
|
||||
it 'uses the value in the html title' do
|
||||
Setting.site_title = 'Site Title'
|
||||
helper.content_for(:page_title, 'Test Value')
|
||||
|
||||
expect(helper.html_title).to eq 'Test Value - Site Title'
|
||||
expect(helper.html_title).to be_html_safe
|
||||
end
|
||||
|
||||
it 'removes extra new lines' do
|
||||
Setting.site_title = 'Site Title'
|
||||
helper.content_for(:page_title, "Test Value\n")
|
||||
|
||||
expect(helper.html_title).to eq 'Test Value - Site Title'
|
||||
expect(helper.html_title).to be_html_safe
|
||||
end
|
||||
end
|
||||
|
||||
context 'without any page_title content_for value' do
|
||||
it 'returns the site title' do
|
||||
Setting.site_title = 'Site Title'
|
||||
|
||||
expect(helper.html_title).to eq 'Site Title'
|
||||
expect(helper.html_title).to be_html_safe
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -4,16 +4,23 @@ require 'rails_helper'
|
|||
|
||||
describe FlashesHelper do
|
||||
describe 'user_facing_flashes' do
|
||||
it 'returns user facing flashes' do
|
||||
before do
|
||||
# rubocop:disable Rails/I18nLocaleTexts
|
||||
flash[:alert] = 'an alert'
|
||||
flash[:error] = 'an error'
|
||||
flash[:notice] = 'a notice'
|
||||
flash[:success] = 'a success'
|
||||
flash[:not_user_facing] = 'a not user facing flash'
|
||||
expect(helper.user_facing_flashes).to eq 'alert' => 'an alert',
|
||||
# rubocop:enable Rails/I18nLocaleTexts
|
||||
end
|
||||
|
||||
it 'returns user facing flashes' do
|
||||
expect(helper.user_facing_flashes).to eq(
|
||||
'alert' => 'an alert',
|
||||
'error' => 'an error',
|
||||
'notice' => 'a notice',
|
||||
'success' => 'a success'
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -8,13 +8,15 @@ RSpec.describe TranslationService::DeepL do
|
|||
let(:plan) { 'advanced' }
|
||||
|
||||
before do
|
||||
stub_request(:get, 'https://api.deepl.com/v2/languages?type=source').to_return(
|
||||
%w(api-free.deepl.com api.deepl.com).each do |host|
|
||||
stub_request(:get, "https://#{host}/v2/languages?type=source").to_return(
|
||||
body: '[{"language":"EN","name":"English"},{"language":"UK","name":"Ukrainian"}]'
|
||||
)
|
||||
stub_request(:get, 'https://api.deepl.com/v2/languages?type=target').to_return(
|
||||
stub_request(:get, "https://#{host}/v2/languages?type=target").to_return(
|
||||
body: '[{"language":"EN-GB","name":"English (British)"},{"language":"ZH","name":"Chinese"}]'
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
describe '#translate' do
|
||||
it 'returns translation with specified source language' do
|
||||
|
@ -73,28 +75,25 @@ RSpec.describe TranslationService::DeepL do
|
|||
end
|
||||
end
|
||||
|
||||
describe '#request' do
|
||||
describe 'the paid and free plan api hostnames' do
|
||||
before do
|
||||
stub_request(:any, //)
|
||||
# rubocop:disable Lint/EmptyBlock
|
||||
service.send(:request, :get, '/v2/languages') { |res| }
|
||||
# rubocop:enable Lint/EmptyBlock
|
||||
service.languages
|
||||
end
|
||||
|
||||
it 'uses paid plan base URL' do
|
||||
expect(a_request(:get, 'https://api.deepl.com/v2/languages')).to have_been_made.once
|
||||
context 'without a plan set' do
|
||||
it 'uses paid plan base URL and sends an API key' do
|
||||
expect(a_request(:get, 'https://api.deepl.com/v2/languages?type=source').with(headers: { Authorization: 'DeepL-Auth-Key my-api-key' })).to have_been_made.once
|
||||
expect(a_request(:get, 'https://api.deepl.com/v2/languages?type=target').with(headers: { Authorization: 'DeepL-Auth-Key my-api-key' })).to have_been_made.once
|
||||
end
|
||||
end
|
||||
|
||||
context 'with free plan' do
|
||||
context 'with the free plan' do
|
||||
let(:plan) { 'free' }
|
||||
|
||||
it 'uses free plan base URL' do
|
||||
expect(a_request(:get, 'https://api-free.deepl.com/v2/languages')).to have_been_made.once
|
||||
end
|
||||
end
|
||||
|
||||
it 'sends API key' do
|
||||
expect(a_request(:get, 'https://api.deepl.com/v2/languages').with(headers: { Authorization: 'DeepL-Auth-Key my-api-key' })).to have_been_made.once
|
||||
it 'uses free plan base URL and sends an API key' do
|
||||
expect(a_request(:get, 'https://api-free.deepl.com/v2/languages?type=source').with(headers: { Authorization: 'DeepL-Auth-Key my-api-key' })).to have_been_made.once
|
||||
expect(a_request(:get, 'https://api-free.deepl.com/v2/languages?type=target').with(headers: { Authorization: 'DeepL-Auth-Key my-api-key' })).to have_been_made.once
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -4,17 +4,15 @@ require 'rails_helper'
|
|||
|
||||
describe AccountFinderConcern do
|
||||
describe 'local finders' do
|
||||
before do
|
||||
@account = Fabricate(:account, username: 'Alice')
|
||||
end
|
||||
let!(:account) { Fabricate(:account, username: 'Alice') }
|
||||
|
||||
describe '.find_local' do
|
||||
it 'returns case-insensitive result' do
|
||||
expect(Account.find_local('alice')).to eq(@account)
|
||||
expect(Account.find_local('alice')).to eq(account)
|
||||
end
|
||||
|
||||
it 'returns correctly cased result' do
|
||||
expect(Account.find_local('Alice')).to eq(@account)
|
||||
expect(Account.find_local('Alice')).to eq(account)
|
||||
end
|
||||
|
||||
it 'returns nil without a match' do
|
||||
|
@ -36,7 +34,7 @@ describe AccountFinderConcern do
|
|||
|
||||
describe '.find_local!' do
|
||||
it 'returns matching result' do
|
||||
expect(Account.find_local!('alice')).to eq(@account)
|
||||
expect(Account.find_local!('alice')).to eq(account)
|
||||
end
|
||||
|
||||
it 'raises on non-matching result' do
|
||||
|
@ -54,17 +52,15 @@ describe AccountFinderConcern do
|
|||
end
|
||||
|
||||
describe 'remote finders' do
|
||||
before do
|
||||
@account = Fabricate(:account, username: 'Alice', domain: 'mastodon.social')
|
||||
end
|
||||
let!(:account) { Fabricate(:account, username: 'Alice', domain: 'mastodon.social') }
|
||||
|
||||
describe '.find_remote' do
|
||||
it 'returns exact match result' do
|
||||
expect(Account.find_remote('alice', 'mastodon.social')).to eq(@account)
|
||||
expect(Account.find_remote('alice', 'mastodon.social')).to eq(account)
|
||||
end
|
||||
|
||||
it 'returns case-insensitive result' do
|
||||
expect(Account.find_remote('ALICE', 'MASTODON.SOCIAL')).to eq(@account)
|
||||
expect(Account.find_remote('ALICE', 'MASTODON.SOCIAL')).to eq(account)
|
||||
end
|
||||
|
||||
it 'returns nil when username does not match' do
|
||||
|
@ -90,7 +86,7 @@ describe AccountFinderConcern do
|
|||
|
||||
describe '.find_remote!' do
|
||||
it 'returns matching result' do
|
||||
expect(Account.find_remote!('alice', 'mastodon.social')).to eq(@account)
|
||||
expect(Account.find_remote!('alice', 'mastodon.social')).to eq(account)
|
||||
end
|
||||
|
||||
it 'raises on non-matching result' do
|
||||
|
|
|
@ -655,38 +655,36 @@ describe AccountInteractions do
|
|||
end
|
||||
|
||||
describe 'ignoring reblogs from an account' do
|
||||
before do
|
||||
@me = Fabricate(:account, username: 'Me')
|
||||
@you = Fabricate(:account, username: 'You')
|
||||
end
|
||||
let!(:me) { Fabricate(:account, username: 'Me') }
|
||||
let!(:you) { Fabricate(:account, username: 'You') }
|
||||
|
||||
context 'with the reblogs option unspecified' do
|
||||
before do
|
||||
@me.follow!(@you)
|
||||
me.follow!(you)
|
||||
end
|
||||
|
||||
it 'defaults to showing reblogs' do
|
||||
expect(@me.muting_reblogs?(@you)).to be(false)
|
||||
expect(me.muting_reblogs?(you)).to be(false)
|
||||
end
|
||||
end
|
||||
|
||||
context 'with the reblogs option set to false' do
|
||||
before do
|
||||
@me.follow!(@you, reblogs: false)
|
||||
me.follow!(you, reblogs: false)
|
||||
end
|
||||
|
||||
it 'does mute reblogs' do
|
||||
expect(@me.muting_reblogs?(@you)).to be(true)
|
||||
expect(me.muting_reblogs?(you)).to be(true)
|
||||
end
|
||||
end
|
||||
|
||||
context 'with the reblogs option set to true' do
|
||||
before do
|
||||
@me.follow!(@you, reblogs: true)
|
||||
me.follow!(you, reblogs: true)
|
||||
end
|
||||
|
||||
it 'does not mute reblogs' do
|
||||
expect(@me.muting_reblogs?(@you)).to be(false)
|
||||
expect(me.muting_reblogs?(you)).to be(false)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -69,7 +69,9 @@ RSpec.describe Remotable do
|
|||
|
||||
context 'with an invalid URL' do
|
||||
before do
|
||||
allow(Addressable::URI).to receive_message_chain(:parse, :normalize).with(url).with(no_args).and_raise(Addressable::URI::InvalidURIError)
|
||||
parsed = instance_double(Addressable::URI)
|
||||
allow(parsed).to receive(:normalize).with(no_args).and_raise(Addressable::URI::InvalidURIError)
|
||||
allow(Addressable::URI).to receive(:parse).with(url).and_return(parsed)
|
||||
end
|
||||
|
||||
it 'makes no request' do
|
||||
|
|
|
@ -296,7 +296,7 @@ RSpec.describe Form::Import do
|
|||
|
||||
it_behaves_like 'on successful import', 'following', 'merge', 'following_accounts.csv', [
|
||||
{ 'acct' => 'user@example.com', 'show_reblogs' => true, 'notify' => false, 'languages' => nil },
|
||||
{ 'acct' => 'user@test.com', 'show_reblogs' => true, 'notify' => true, 'languages' => ['en', 'fr'] },
|
||||
{ 'acct' => 'user@test.com', 'show_reblogs' => true, 'notify' => true, 'languages' => %w(en fr) },
|
||||
]
|
||||
|
||||
it_behaves_like 'on successful import', 'muting', 'merge', 'muted_accounts.csv', [
|
||||
|
|
|
@ -199,15 +199,13 @@ RSpec.describe PublicFeed do
|
|||
end
|
||||
|
||||
describe 'with an account passed in' do
|
||||
subject { described_class.new(@account).get(20).map(&:id) }
|
||||
subject { described_class.new(account).get(20).map(&:id) }
|
||||
|
||||
before do
|
||||
@account = Fabricate(:account)
|
||||
end
|
||||
let!(:account) { Fabricate(:account) }
|
||||
|
||||
it 'excludes statuses from accounts blocked by the account' do
|
||||
blocked = Fabricate(:account)
|
||||
@account.block!(blocked)
|
||||
account.block!(blocked)
|
||||
blocked_status = Fabricate(:status, account: blocked)
|
||||
|
||||
expect(subject).to_not include(blocked_status.id)
|
||||
|
@ -215,7 +213,7 @@ RSpec.describe PublicFeed do
|
|||
|
||||
it 'excludes statuses from accounts who have blocked the account' do
|
||||
blocker = Fabricate(:account)
|
||||
blocker.block!(@account)
|
||||
blocker.block!(account)
|
||||
blocked_status = Fabricate(:status, account: blocker)
|
||||
|
||||
expect(subject).to_not include(blocked_status.id)
|
||||
|
@ -223,7 +221,7 @@ RSpec.describe PublicFeed do
|
|||
|
||||
it 'excludes statuses from accounts muted by the account' do
|
||||
muted = Fabricate(:account)
|
||||
@account.mute!(muted)
|
||||
account.mute!(muted)
|
||||
muted_status = Fabricate(:status, account: muted)
|
||||
|
||||
expect(subject).to_not include(muted_status.id)
|
||||
|
@ -231,7 +229,7 @@ RSpec.describe PublicFeed do
|
|||
|
||||
it 'excludes statuses from accounts from personally blocked domains' do
|
||||
blocked = Fabricate(:account, domain: 'example.com')
|
||||
@account.block_domain!(blocked.domain)
|
||||
account.block_domain!(blocked.domain)
|
||||
blocked_status = Fabricate(:status, account: blocked)
|
||||
|
||||
expect(subject).to_not include(blocked_status.id)
|
||||
|
@ -239,7 +237,7 @@ RSpec.describe PublicFeed do
|
|||
|
||||
context 'with language preferences' do
|
||||
it 'excludes statuses in languages not allowed by the account user' do
|
||||
@account.user.update(chosen_languages: [:en, :es])
|
||||
account.user.update(chosen_languages: [:en, :es])
|
||||
en_status = Fabricate(:status, language: 'en')
|
||||
es_status = Fabricate(:status, language: 'es')
|
||||
fr_status = Fabricate(:status, language: 'fr')
|
||||
|
@ -250,7 +248,7 @@ RSpec.describe PublicFeed do
|
|||
end
|
||||
|
||||
it 'includes all languages when user does not have a setting' do
|
||||
@account.user.update(chosen_languages: nil)
|
||||
account.user.update(chosen_languages: nil)
|
||||
|
||||
en_status = Fabricate(:status, language: 'en')
|
||||
es_status = Fabricate(:status, language: 'es')
|
||||
|
@ -260,7 +258,7 @@ RSpec.describe PublicFeed do
|
|||
end
|
||||
|
||||
it 'includes all languages when account does not have a user' do
|
||||
@account.update(user: nil)
|
||||
account.update(user: nil)
|
||||
|
||||
en_status = Fabricate(:status, language: 'en')
|
||||
es_status = Fabricate(:status, language: 'es')
|
||||
|
|
|
@ -98,34 +98,44 @@ RSpec.describe SessionActivation do
|
|||
end
|
||||
|
||||
context 'when id exists' do
|
||||
let(:id) { '1' }
|
||||
let!(:session_activation) { Fabricate(:session_activation) }
|
||||
|
||||
it 'calls where.destroy_all' do
|
||||
expect(described_class).to receive_message_chain(:where, :destroy_all)
|
||||
.with(session_id: id).with(no_args)
|
||||
it 'destroys the record' do
|
||||
described_class.deactivate(session_activation.session_id)
|
||||
|
||||
described_class.deactivate(id)
|
||||
expect { session_activation.reload }.to raise_error(ActiveRecord::RecordNotFound)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '.purge_old' do
|
||||
it 'calls order.offset.destroy_all' do
|
||||
expect(described_class).to receive_message_chain(:order, :offset, :destroy_all)
|
||||
.with('created_at desc').with(Rails.configuration.x.max_session_activations).with(no_args)
|
||||
around do |example|
|
||||
before = Rails.configuration.x.max_session_activations
|
||||
Rails.configuration.x.max_session_activations = 1
|
||||
example.run
|
||||
Rails.configuration.x.max_session_activations = before
|
||||
end
|
||||
|
||||
let!(:oldest_session_activation) { Fabricate(:session_activation, created_at: 10.days.ago) }
|
||||
let!(:newest_session_activation) { Fabricate(:session_activation, created_at: 5.days.ago) }
|
||||
|
||||
it 'preserves the newest X records based on config' do
|
||||
described_class.purge_old
|
||||
|
||||
expect { oldest_session_activation.reload }.to raise_error(ActiveRecord::RecordNotFound)
|
||||
expect { newest_session_activation.reload }.to_not raise_error
|
||||
end
|
||||
end
|
||||
|
||||
describe '.exclusive' do
|
||||
let(:id) { '1' }
|
||||
let!(:unwanted_session_activation) { Fabricate(:session_activation) }
|
||||
let!(:wanted_session_activation) { Fabricate(:session_activation) }
|
||||
|
||||
it 'calls where.destroy_all' do
|
||||
expect(described_class).to receive_message_chain(:where, :not, :destroy_all)
|
||||
.with(session_id: id).with(no_args)
|
||||
it 'preserves supplied record and destroys all others' do
|
||||
described_class.exclusive(wanted_session_activation.session_id)
|
||||
|
||||
described_class.exclusive(id)
|
||||
expect { unwanted_session_activation.reload }.to raise_error(ActiveRecord::RecordNotFound)
|
||||
expect { wanted_session_activation.reload }.to_not raise_error
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -77,10 +77,13 @@ RSpec.describe Setting do
|
|||
let(:default_value) { { default_value: 'default_value' } }
|
||||
|
||||
it 'calls default_value.with_indifferent_access.merge!' do
|
||||
expect(default_value).to receive_message_chain(:with_indifferent_access, :merge!)
|
||||
.with(db_val.value)
|
||||
indifferent_hash = instance_double(Hash, merge!: nil)
|
||||
allow(default_value).to receive(:with_indifferent_access).and_return(indifferent_hash)
|
||||
|
||||
described_class[key]
|
||||
|
||||
expect(default_value).to have_received(:with_indifferent_access)
|
||||
expect(indifferent_hash).to have_received(:merge!).with(db_val.value)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -11,10 +11,6 @@ if RUN_SYSTEM_SPECS
|
|||
ENV['STREAMING_API_BASE_URL'] = "http://localhost:#{STREAMING_PORT}"
|
||||
end
|
||||
|
||||
if RUN_SEARCH_SPECS
|
||||
# Include any configuration or setups specific to search tests here
|
||||
end
|
||||
|
||||
require File.expand_path('../config/environment', __dir__)
|
||||
|
||||
abort('The Rails environment is running in production mode!') if Rails.env.production?
|
||||
|
@ -35,8 +31,6 @@ Sidekiq.logger = nil
|
|||
|
||||
# System tests config
|
||||
DatabaseCleaner.strategy = [:deletion]
|
||||
streaming_server_manager = StreamingServerManager.new
|
||||
search_data_manager = SearchDataManager.new
|
||||
|
||||
Devise::Test::ControllerHelpers.module_eval do
|
||||
alias_method :original_sign_in, :sign_in
|
||||
|
@ -100,26 +94,7 @@ RSpec.configure do |config|
|
|||
Capybara.current_driver = :rack_test
|
||||
end
|
||||
|
||||
config.before :suite do
|
||||
if RUN_SYSTEM_SPECS
|
||||
Webpacker.compile
|
||||
streaming_server_manager.start(port: STREAMING_PORT)
|
||||
end
|
||||
|
||||
if RUN_SEARCH_SPECS
|
||||
Chewy.strategy(:urgent)
|
||||
search_data_manager.prepare_test_data
|
||||
end
|
||||
end
|
||||
|
||||
config.after :suite do
|
||||
streaming_server_manager.stop
|
||||
|
||||
search_data_manager.cleanup_test_data if RUN_SEARCH_SPECS
|
||||
end
|
||||
|
||||
config.around :each, type: :system do |example|
|
||||
# driven_by :selenium, using: :chrome, screen_size: [1600, 1200]
|
||||
driven_by :selenium, using: :headless_chrome, screen_size: [1600, 1200]
|
||||
|
||||
# The streaming server needs access to the database
|
||||
|
@ -136,12 +111,6 @@ RSpec.configure do |config|
|
|||
self.use_transactional_tests = true
|
||||
end
|
||||
|
||||
config.around :each, type: :search do |example|
|
||||
search_data_manager.populate_indexes
|
||||
example.run
|
||||
search_data_manager.remove_indexes
|
||||
end
|
||||
|
||||
config.before do |example|
|
||||
unless example.metadata[:paperclip_processing]
|
||||
allow_any_instance_of(Paperclip::Attachment).to receive(:post_process).and_return(true) # rubocop:disable RSpec/AnyInstance
|
||||
|
|
|
@ -119,24 +119,26 @@ module TestEndpoints
|
|||
end
|
||||
|
||||
describe 'Caching behavior' do
|
||||
shared_examples 'cachable response' do
|
||||
it 'does not set cookies' do
|
||||
shared_examples 'cachable response' do |http_success: false|
|
||||
it 'does not set cookies or set public cache control', :aggregate_failures do
|
||||
expect(response.cookies).to be_empty
|
||||
end
|
||||
|
||||
it 'sets public cache control', :aggregate_failures do
|
||||
# expect(response.cache_control[:max_age]&.to_i).to be_positive
|
||||
expect(response.cache_control[:public]).to be_truthy
|
||||
expect(response.cache_control[:private]).to be_falsy
|
||||
expect(response.cache_control[:no_store]).to be_falsy
|
||||
expect(response.cache_control[:no_cache]).to be_falsy
|
||||
|
||||
expect(response).to have_http_status(200) if http_success
|
||||
end
|
||||
end
|
||||
|
||||
shared_examples 'non-cacheable response' do
|
||||
shared_examples 'non-cacheable response' do |http_success: false|
|
||||
it 'sets private cache control' do
|
||||
expect(response.cache_control[:private]).to be_truthy
|
||||
expect(response.cache_control[:no_store]).to be_truthy
|
||||
|
||||
expect(response).to have_http_status(200) if http_success
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -149,7 +151,7 @@ describe 'Caching behavior' do
|
|||
|
||||
shared_examples 'language-dependent' do
|
||||
it 'has a Vary on Accept-Language' do
|
||||
expect(response.headers['Vary']&.split(',')&.map { |x| x.strip.downcase }).to include('accept-language')
|
||||
expect(response_vary_headers).to include('accept-language')
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -202,7 +204,7 @@ describe 'Caching behavior' do
|
|||
it_behaves_like 'cachable response'
|
||||
|
||||
it 'has a Vary on Cookie' do
|
||||
expect(response.headers['Vary']&.split(',')&.map { |x| x.strip.downcase }).to include('cookie')
|
||||
expect(response_vary_headers).to include('cookie')
|
||||
end
|
||||
|
||||
it_behaves_like 'language-dependent' if TestEndpoints::LANGUAGE_DEPENDENT.include?(endpoint)
|
||||
|
@ -216,7 +218,7 @@ describe 'Caching behavior' do
|
|||
it_behaves_like 'cachable response'
|
||||
|
||||
it 'has a Vary on Authorization' do
|
||||
expect(response.headers['Vary']&.split(',')&.map { |x| x.strip.downcase }).to include('authorization')
|
||||
expect(response_vary_headers).to include('authorization')
|
||||
end
|
||||
|
||||
it_behaves_like 'language-dependent' if TestEndpoints::LANGUAGE_DEPENDENT.include?(endpoint)
|
||||
|
@ -302,7 +304,7 @@ describe 'Caching behavior' do
|
|||
it_behaves_like 'non-cacheable response'
|
||||
|
||||
it 'has a Vary on Cookie' do
|
||||
expect(response.headers['Vary']&.split(',')&.map { |x| x.strip.downcase }).to include('cookie')
|
||||
expect(response_vary_headers).to include('cookie')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -311,11 +313,7 @@ describe 'Caching behavior' do
|
|||
describe endpoint do
|
||||
before { get endpoint }
|
||||
|
||||
it_behaves_like 'non-cacheable response'
|
||||
|
||||
it 'returns HTTP success' do
|
||||
expect(response).to have_http_status(200)
|
||||
end
|
||||
it_behaves_like 'non-cacheable response', http_success: true
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -351,7 +349,7 @@ describe 'Caching behavior' do
|
|||
it_behaves_like 'non-cacheable response'
|
||||
|
||||
it 'has a Vary on Authorization' do
|
||||
expect(response.headers['Vary']&.split(',')&.map { |x| x.strip.downcase }).to include('authorization')
|
||||
expect(response_vary_headers).to include('authorization')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -362,11 +360,7 @@ describe 'Caching behavior' do
|
|||
get endpoint, headers: { 'Authorization' => "Bearer #{token.token}" }
|
||||
end
|
||||
|
||||
it_behaves_like 'non-cacheable response'
|
||||
|
||||
it 'returns HTTP success' do
|
||||
expect(response).to have_http_status(200)
|
||||
end
|
||||
it_behaves_like 'non-cacheable response', http_success: true
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -393,11 +387,7 @@ describe 'Caching behavior' do
|
|||
context 'when allowed for local users only' do
|
||||
let(:show_domain_blocks) { 'users' }
|
||||
|
||||
it_behaves_like 'non-cacheable response'
|
||||
|
||||
it 'returns HTTP success' do
|
||||
expect(response).to have_http_status(200)
|
||||
end
|
||||
it_behaves_like 'non-cacheable response', http_success: true
|
||||
end
|
||||
|
||||
context 'when disabled' do
|
||||
|
@ -421,11 +411,7 @@ describe 'Caching behavior' do
|
|||
get '/actor', sign_with: remote_actor, headers: { 'Accept' => 'application/activity+json' }
|
||||
end
|
||||
|
||||
it_behaves_like 'cachable response'
|
||||
|
||||
it 'returns HTTP success' do
|
||||
expect(response).to have_http_status(200)
|
||||
end
|
||||
it_behaves_like 'cachable response', http_success: true
|
||||
end
|
||||
|
||||
TestEndpoints::REQUIRE_SIGNATURE.each do |endpoint|
|
||||
|
@ -434,11 +420,7 @@ describe 'Caching behavior' do
|
|||
get endpoint, sign_with: remote_actor, headers: { 'Accept' => 'application/activity+json' }
|
||||
end
|
||||
|
||||
it_behaves_like 'non-cacheable response'
|
||||
|
||||
it 'returns HTTP success' do
|
||||
expect(response).to have_http_status(200)
|
||||
end
|
||||
it_behaves_like 'non-cacheable response', http_success: true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -456,11 +438,7 @@ describe 'Caching behavior' do
|
|||
get '/actor', headers: { 'Accept' => 'application/activity+json' }
|
||||
end
|
||||
|
||||
it_behaves_like 'cachable response'
|
||||
|
||||
it 'returns HTTP success' do
|
||||
expect(response).to have_http_status(200)
|
||||
end
|
||||
it_behaves_like 'cachable response', http_success: true
|
||||
end
|
||||
|
||||
(TestEndpoints::REQUIRE_SIGNATURE + TestEndpoints::AuthorizedFetch::REQUIRE_SIGNATURE).each do |endpoint|
|
||||
|
@ -487,11 +465,7 @@ describe 'Caching behavior' do
|
|||
get '/actor', sign_with: remote_actor, headers: { 'Accept' => 'application/activity+json' }
|
||||
end
|
||||
|
||||
it_behaves_like 'cachable response'
|
||||
|
||||
it 'returns HTTP success' do
|
||||
expect(response).to have_http_status(200)
|
||||
end
|
||||
it_behaves_like 'cachable response', http_success: true
|
||||
end
|
||||
|
||||
(TestEndpoints::REQUIRE_SIGNATURE + TestEndpoints::AuthorizedFetch::REQUIRE_SIGNATURE).each do |endpoint|
|
||||
|
@ -500,11 +474,7 @@ describe 'Caching behavior' do
|
|||
get endpoint, sign_with: remote_actor, headers: { 'Accept' => 'application/activity+json' }
|
||||
end
|
||||
|
||||
it_behaves_like 'non-cacheable response'
|
||||
|
||||
it 'returns HTTP success' do
|
||||
expect(response).to have_http_status(200)
|
||||
end
|
||||
it_behaves_like 'non-cacheable response', http_success: true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -528,11 +498,7 @@ describe 'Caching behavior' do
|
|||
get '/actor', headers: { 'Accept' => 'application/activity+json' }
|
||||
end
|
||||
|
||||
it_behaves_like 'cachable response'
|
||||
|
||||
it 'returns HTTP success' do
|
||||
expect(response).to have_http_status(200)
|
||||
end
|
||||
it_behaves_like 'cachable response', http_success: true
|
||||
end
|
||||
|
||||
(TestEndpoints::REQUIRE_SIGNATURE + TestEndpoints::AuthorizedFetch::REQUIRE_SIGNATURE).each do |endpoint|
|
||||
|
@ -560,11 +526,7 @@ describe 'Caching behavior' do
|
|||
get '/actor', sign_with: remote_actor, headers: { 'Accept' => 'application/activity+json' }
|
||||
end
|
||||
|
||||
it_behaves_like 'cachable response'
|
||||
|
||||
it 'returns HTTP success' do
|
||||
expect(response).to have_http_status(200)
|
||||
end
|
||||
it_behaves_like 'cachable response', http_success: true
|
||||
end
|
||||
|
||||
(TestEndpoints::REQUIRE_SIGNATURE + TestEndpoints::AuthorizedFetch::REQUIRE_SIGNATURE).each do |endpoint|
|
||||
|
@ -573,11 +535,7 @@ describe 'Caching behavior' do
|
|||
get endpoint, sign_with: remote_actor, headers: { 'Accept' => 'application/activity+json' }
|
||||
end
|
||||
|
||||
it_behaves_like 'non-cacheable response'
|
||||
|
||||
it 'returns HTTP success' do
|
||||
expect(response).to have_http_status(200)
|
||||
end
|
||||
it_behaves_like 'non-cacheable response', http_success: true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -591,11 +549,7 @@ describe 'Caching behavior' do
|
|||
get '/actor', sign_with: remote_actor, headers: { 'Accept' => 'application/activity+json' }
|
||||
end
|
||||
|
||||
it_behaves_like 'cachable response'
|
||||
|
||||
it 'returns HTTP success' do
|
||||
expect(response).to have_http_status(200)
|
||||
end
|
||||
it_behaves_like 'cachable response', http_success: true
|
||||
end
|
||||
|
||||
(TestEndpoints::REQUIRE_SIGNATURE + TestEndpoints::AuthorizedFetch::REQUIRE_SIGNATURE).each do |endpoint|
|
||||
|
@ -667,7 +621,7 @@ describe 'Caching behavior' do
|
|||
it_behaves_like 'non-cacheable response'
|
||||
|
||||
it 'has a Vary on Authorization' do
|
||||
expect(response.headers['Vary']&.split(',')&.map { |x| x.strip.downcase }).to include('authorization')
|
||||
expect(response_vary_headers).to include('authorization')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -678,13 +632,15 @@ describe 'Caching behavior' do
|
|||
get endpoint, headers: { 'Authorization' => "Bearer #{token.token}" }
|
||||
end
|
||||
|
||||
it_behaves_like 'non-cacheable response'
|
||||
it_behaves_like 'non-cacheable response', http_success: true
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
it 'returns HTTP success' do
|
||||
expect(response).to have_http_status(200)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
private
|
||||
|
||||
def response_vary_headers
|
||||
response.headers['Vary']&.split(',')&.map { |x| x.strip.downcase }
|
||||
end
|
||||
end
|
||||
|
|
|
@ -20,71 +20,70 @@ RSpec.describe RemoveStatusService, type: :service do
|
|||
end
|
||||
|
||||
context 'when removed status is not a reblog' do
|
||||
let!(:status) { PostStatusService.new.call(alice, text: 'Hello @bob@example.com ThisIsASecret') }
|
||||
|
||||
before do
|
||||
@status = PostStatusService.new.call(alice, text: 'Hello @bob@example.com ThisIsASecret')
|
||||
FavouriteService.new.call(jeff, @status)
|
||||
Fabricate(:status, account: bill, reblog: @status, uri: 'hoge')
|
||||
FavouriteService.new.call(jeff, status)
|
||||
Fabricate(:status, account: bill, reblog: status, uri: 'hoge')
|
||||
end
|
||||
|
||||
it 'removes status from author\'s home feed' do
|
||||
subject.call(@status)
|
||||
expect(HomeFeed.new(alice).get(10).pluck(:id)).to_not include(@status.id)
|
||||
subject.call(status)
|
||||
expect(HomeFeed.new(alice).get(10).pluck(:id)).to_not include(status.id)
|
||||
end
|
||||
|
||||
it 'removes status from local follower\'s home feed' do
|
||||
subject.call(@status)
|
||||
expect(HomeFeed.new(jeff).get(10).pluck(:id)).to_not include(@status.id)
|
||||
subject.call(status)
|
||||
expect(HomeFeed.new(jeff).get(10).pluck(:id)).to_not include(status.id)
|
||||
end
|
||||
|
||||
it 'sends Delete activity to followers' do
|
||||
subject.call(@status)
|
||||
subject.call(status)
|
||||
expect(a_request(:post, 'http://example.com/inbox').with(
|
||||
body: hash_including({
|
||||
'type' => 'Delete',
|
||||
'object' => {
|
||||
'type' => 'Tombstone',
|
||||
'id' => ActivityPub::TagManager.instance.uri_for(@status),
|
||||
'atomUri' => OStatus::TagManager.instance.uri_for(@status),
|
||||
'id' => ActivityPub::TagManager.instance.uri_for(status),
|
||||
'atomUri' => OStatus::TagManager.instance.uri_for(status),
|
||||
},
|
||||
})
|
||||
)).to have_been_made.once
|
||||
end
|
||||
|
||||
it 'sends Delete activity to rebloggers' do
|
||||
subject.call(@status)
|
||||
subject.call(status)
|
||||
expect(a_request(:post, 'http://example2.com/inbox').with(
|
||||
body: hash_including({
|
||||
'type' => 'Delete',
|
||||
'object' => {
|
||||
'type' => 'Tombstone',
|
||||
'id' => ActivityPub::TagManager.instance.uri_for(@status),
|
||||
'atomUri' => OStatus::TagManager.instance.uri_for(@status),
|
||||
'id' => ActivityPub::TagManager.instance.uri_for(status),
|
||||
'atomUri' => OStatus::TagManager.instance.uri_for(status),
|
||||
},
|
||||
})
|
||||
)).to have_been_made.once
|
||||
end
|
||||
|
||||
it 'remove status from notifications' do
|
||||
expect { subject.call(@status) }.to change {
|
||||
expect { subject.call(status) }.to change {
|
||||
Notification.where(activity_type: 'Favourite', from_account: jeff, account: alice).count
|
||||
}.from(1).to(0)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when removed status is a private self-reblog' do
|
||||
before do
|
||||
@original_status = Fabricate(:status, account: alice, text: 'Hello ThisIsASecret', visibility: :private)
|
||||
@status = ReblogService.new.call(alice, @original_status)
|
||||
end
|
||||
let!(:original_status) { Fabricate(:status, account: alice, text: 'Hello ThisIsASecret', visibility: :private) }
|
||||
let!(:status) { ReblogService.new.call(alice, original_status) }
|
||||
|
||||
it 'sends Undo activity to followers' do
|
||||
subject.call(@status)
|
||||
subject.call(status)
|
||||
expect(a_request(:post, 'http://example.com/inbox').with(
|
||||
body: hash_including({
|
||||
'type' => 'Undo',
|
||||
'object' => hash_including({
|
||||
'type' => 'Announce',
|
||||
'object' => ActivityPub::TagManager.instance.uri_for(@original_status),
|
||||
'object' => ActivityPub::TagManager.instance.uri_for(original_status),
|
||||
}),
|
||||
})
|
||||
)).to have_been_made.once
|
||||
|
@ -92,19 +91,17 @@ RSpec.describe RemoveStatusService, type: :service do
|
|||
end
|
||||
|
||||
context 'when removed status is public self-reblog' do
|
||||
before do
|
||||
@original_status = Fabricate(:status, account: alice, text: 'Hello ThisIsASecret', visibility: :public)
|
||||
@status = ReblogService.new.call(alice, @original_status)
|
||||
end
|
||||
let!(:original_status) { Fabricate(:status, account: alice, text: 'Hello ThisIsASecret', visibility: :public) }
|
||||
let!(:status) { ReblogService.new.call(alice, original_status) }
|
||||
|
||||
it 'sends Undo activity to followers' do
|
||||
subject.call(@status)
|
||||
subject.call(status)
|
||||
expect(a_request(:post, 'http://example.com/inbox').with(
|
||||
body: hash_including({
|
||||
'type' => 'Undo',
|
||||
'object' => hash_including({
|
||||
'type' => 'Announce',
|
||||
'object' => ActivityPub::TagManager.instance.uri_for(@original_status),
|
||||
'object' => ActivityPub::TagManager.instance.uri_for(original_status),
|
||||
}),
|
||||
})
|
||||
)).to have_been_made.once
|
||||
|
|
|
@ -19,17 +19,15 @@ describe SearchService, type: :service do
|
|||
end
|
||||
|
||||
describe 'with an url query' do
|
||||
before do
|
||||
@query = 'http://test.host/query'
|
||||
end
|
||||
let(:query) { 'http://test.host/query' }
|
||||
|
||||
context 'when it does not find anything' do
|
||||
it 'returns the empty results' do
|
||||
service = instance_double(ResolveURLService, call: nil)
|
||||
allow(ResolveURLService).to receive(:new).and_return(service)
|
||||
results = subject.call(@query, nil, 10, resolve: true)
|
||||
results = subject.call(query, nil, 10, resolve: true)
|
||||
|
||||
expect(service).to have_received(:call).with(@query, on_behalf_of: nil)
|
||||
expect(service).to have_received(:call).with(query, on_behalf_of: nil)
|
||||
expect(results).to eq empty_results
|
||||
end
|
||||
end
|
||||
|
@ -40,8 +38,8 @@ describe SearchService, type: :service do
|
|||
service = instance_double(ResolveURLService, call: account)
|
||||
allow(ResolveURLService).to receive(:new).and_return(service)
|
||||
|
||||
results = subject.call(@query, nil, 10, resolve: true)
|
||||
expect(service).to have_received(:call).with(@query, on_behalf_of: nil)
|
||||
results = subject.call(query, nil, 10, resolve: true)
|
||||
expect(service).to have_received(:call).with(query, on_behalf_of: nil)
|
||||
expect(results).to eq empty_results.merge(accounts: [account])
|
||||
end
|
||||
end
|
||||
|
@ -52,8 +50,8 @@ describe SearchService, type: :service do
|
|||
service = instance_double(ResolveURLService, call: status)
|
||||
allow(ResolveURLService).to receive(:new).and_return(service)
|
||||
|
||||
results = subject.call(@query, nil, 10, resolve: true)
|
||||
expect(service).to have_received(:call).with(@query, on_behalf_of: nil)
|
||||
results = subject.call(query, nil, 10, resolve: true)
|
||||
expect(service).to have_received(:call).with(query, on_behalf_of: nil)
|
||||
expect(results).to eq empty_results.merge(statuses: [status])
|
||||
end
|
||||
end
|
||||
|
|
|
@ -6,38 +6,36 @@ describe UnblockDomainService, type: :service do
|
|||
subject { described_class.new }
|
||||
|
||||
describe 'call' do
|
||||
before do
|
||||
@independently_suspended = Fabricate(:account, domain: 'example.com', suspended_at: 1.hour.ago)
|
||||
@independently_silenced = Fabricate(:account, domain: 'example.com', silenced_at: 1.hour.ago)
|
||||
@domain_block = Fabricate(:domain_block, domain: 'example.com')
|
||||
@silenced = Fabricate(:account, domain: 'example.com', silenced_at: @domain_block.created_at)
|
||||
@suspended = Fabricate(:account, domain: 'example.com', suspended_at: @domain_block.created_at)
|
||||
end
|
||||
let!(:independently_suspended) { Fabricate(:account, domain: 'example.com', suspended_at: 1.hour.ago) }
|
||||
let!(:independently_silenced) { Fabricate(:account, domain: 'example.com', silenced_at: 1.hour.ago) }
|
||||
let!(:domain_block) { Fabricate(:domain_block, domain: 'example.com') }
|
||||
let!(:silenced) { Fabricate(:account, domain: 'example.com', silenced_at: domain_block.created_at) }
|
||||
let!(:suspended) { Fabricate(:account, domain: 'example.com', suspended_at: domain_block.created_at) }
|
||||
|
||||
it 'unsilences accounts and removes block' do
|
||||
@domain_block.update(severity: :silence)
|
||||
domain_block.update(severity: :silence)
|
||||
|
||||
subject.call(@domain_block)
|
||||
subject.call(domain_block)
|
||||
expect_deleted_domain_block
|
||||
expect(@silenced.reload.silenced?).to be false
|
||||
expect(@suspended.reload.suspended?).to be true
|
||||
expect(@independently_suspended.reload.suspended?).to be true
|
||||
expect(@independently_silenced.reload.silenced?).to be true
|
||||
expect(silenced.reload.silenced?).to be false
|
||||
expect(suspended.reload.suspended?).to be true
|
||||
expect(independently_suspended.reload.suspended?).to be true
|
||||
expect(independently_silenced.reload.silenced?).to be true
|
||||
end
|
||||
|
||||
it 'unsuspends accounts and removes block' do
|
||||
@domain_block.update(severity: :suspend)
|
||||
domain_block.update(severity: :suspend)
|
||||
|
||||
subject.call(@domain_block)
|
||||
subject.call(domain_block)
|
||||
expect_deleted_domain_block
|
||||
expect(@suspended.reload.suspended?).to be false
|
||||
expect(@silenced.reload.silenced?).to be false
|
||||
expect(@independently_suspended.reload.suspended?).to be true
|
||||
expect(@independently_silenced.reload.silenced?).to be true
|
||||
expect(suspended.reload.suspended?).to be false
|
||||
expect(silenced.reload.silenced?).to be false
|
||||
expect(independently_suspended.reload.suspended?).to be true
|
||||
expect(independently_silenced.reload.silenced?).to be true
|
||||
end
|
||||
end
|
||||
|
||||
def expect_deleted_domain_block
|
||||
expect { @domain_block.reload }.to raise_error(ActiveRecord::RecordNotFound)
|
||||
expect { domain_block.reload }.to raise_error(ActiveRecord::RecordNotFound)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -41,3 +41,38 @@ class SearchDataManager
|
|||
Tag.destroy_all
|
||||
end
|
||||
end
|
||||
|
||||
RSpec.configure do |config|
|
||||
config.before :suite do
|
||||
if search_examples_present?
|
||||
# Configure chewy to use `urgent` strategy to index documents
|
||||
Chewy.strategy(:urgent)
|
||||
|
||||
# Create search data
|
||||
search_data_manager.prepare_test_data
|
||||
end
|
||||
end
|
||||
|
||||
config.after :suite do
|
||||
if search_examples_present?
|
||||
# Clean up after search data
|
||||
search_data_manager.cleanup_test_data
|
||||
end
|
||||
end
|
||||
|
||||
config.around :each, type: :search do |example|
|
||||
search_data_manager.populate_indexes
|
||||
example.run
|
||||
search_data_manager.remove_indexes
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def search_data_manager
|
||||
@search_data_manager ||= SearchDataManager.new
|
||||
end
|
||||
|
||||
def search_examples_present?
|
||||
RUN_SEARCH_SPECS
|
||||
end
|
||||
end
|
||||
|
|
|
@ -76,3 +76,32 @@ class StreamingServerManager
|
|||
@running_thread.join
|
||||
end
|
||||
end
|
||||
|
||||
RSpec.configure do |config|
|
||||
config.before :suite do
|
||||
if streaming_examples_present?
|
||||
# Compile assets
|
||||
Webpacker.compile
|
||||
|
||||
# Start the node streaming server
|
||||
streaming_server_manager.start(port: STREAMING_PORT)
|
||||
end
|
||||
end
|
||||
|
||||
config.after :suite do
|
||||
if streaming_examples_present?
|
||||
# Stop the node streaming server
|
||||
streaming_server_manager.stop
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def streaming_server_manager
|
||||
@streaming_server_manager ||= StreamingServerManager.new
|
||||
end
|
||||
|
||||
def streaming_examples_present?
|
||||
RUN_SYSTEM_SPECS
|
||||
end
|
||||
end
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe BlacklistedEmailValidator, type: :validator do
|
||||
RSpec.describe BlacklistedEmailValidator do
|
||||
describe '#validate' do
|
||||
subject { described_class.new.validate(user); errors }
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe DisallowedHashtagsValidator, type: :validator do
|
||||
RSpec.describe DisallowedHashtagsValidator do
|
||||
let(:disallowed_tags) { [] }
|
||||
|
||||
describe '#validate' do
|
||||
|
|
|
@ -2,48 +2,76 @@
|
|||
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe FollowLimitValidator, type: :validator do
|
||||
RSpec.describe FollowLimitValidator do
|
||||
describe '#validate' do
|
||||
context 'with a nil account' do
|
||||
it 'does not add validation errors to base' do
|
||||
follow = Fabricate.build(:follow, account: nil)
|
||||
|
||||
follow.valid?
|
||||
|
||||
expect(follow.errors[:base]).to be_empty
|
||||
end
|
||||
end
|
||||
|
||||
context 'with a non-local account' do
|
||||
it 'does not add validation errors to base' do
|
||||
follow = Fabricate.build(:follow, account: Account.new(domain: 'host.example'))
|
||||
|
||||
follow.valid?
|
||||
|
||||
expect(follow.errors[:base]).to be_empty
|
||||
end
|
||||
end
|
||||
|
||||
context 'with a local account' do
|
||||
let(:account) { Account.new }
|
||||
|
||||
context 'when the followers count is under the limit' do
|
||||
before do
|
||||
allow_any_instance_of(described_class).to receive(:limit_reached?).with(account) do
|
||||
limit_reached
|
||||
allow(account).to receive(:following_count).and_return(described_class::LIMIT - 100)
|
||||
end
|
||||
|
||||
described_class.new.validate(follow)
|
||||
end
|
||||
it 'does not add validation errors to base' do
|
||||
follow = Fabricate.build(:follow, account: account)
|
||||
|
||||
let(:follow) { instance_double(Follow, account: account, errors: errors) }
|
||||
let(:errors) { instance_double(ActiveModel::Errors, add: nil) }
|
||||
let(:account) { instance_double(Account, nil?: _nil, local?: local, following_count: 0, followers_count: 0) }
|
||||
let(:_nil) { true }
|
||||
let(:local) { false }
|
||||
follow.valid?
|
||||
|
||||
context 'with follow.account.nil? || !follow.account.local?' do
|
||||
let(:_nil) { true }
|
||||
|
||||
it 'not calls errors.add' do
|
||||
expect(errors).to_not have_received(:add).with(:base, any_args)
|
||||
expect(follow.errors[:base]).to be_empty
|
||||
end
|
||||
end
|
||||
|
||||
context 'with !(follow.account.nil? || !follow.account.local?)' do
|
||||
let(:_nil) { false }
|
||||
let(:local) { true }
|
||||
context 'when the following count is over the limit' do
|
||||
before do
|
||||
allow(account).to receive(:following_count).and_return(described_class::LIMIT + 100)
|
||||
end
|
||||
|
||||
context 'when limit_reached?' do
|
||||
let(:limit_reached) { true }
|
||||
context 'when the followers count is low' do
|
||||
before do
|
||||
allow(account).to receive(:followers_count).and_return(10)
|
||||
end
|
||||
|
||||
it 'calls errors.add' do
|
||||
expect(errors).to have_received(:add)
|
||||
.with(:base, I18n.t('users.follow_limit_reached', limit: FollowLimitValidator::LIMIT))
|
||||
it 'adds validation errors to base' do
|
||||
follow = Fabricate.build(:follow, account: account)
|
||||
|
||||
follow.valid?
|
||||
|
||||
expect(follow.errors[:base]).to include(I18n.t('users.follow_limit_reached', limit: FollowLimitValidator::LIMIT))
|
||||
end
|
||||
end
|
||||
|
||||
context 'with !limit_reached?' do
|
||||
let(:limit_reached) { false }
|
||||
context 'when the followers count is high' do
|
||||
before do
|
||||
allow(account).to receive(:followers_count).and_return(100_000)
|
||||
end
|
||||
|
||||
it 'not calls errors.add' do
|
||||
expect(errors).to_not have_received(:add).with(:base, any_args)
|
||||
it 'does not add validation errors to base' do
|
||||
follow = Fabricate.build(:follow, account: account)
|
||||
|
||||
follow.valid?
|
||||
|
||||
expect(follow.errors[:base]).to be_empty
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe PollValidator, type: :validator do
|
||||
RSpec.describe PollValidator do
|
||||
describe '#validate' do
|
||||
before do
|
||||
validator.validate(poll)
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe StatusPinValidator, type: :validator do
|
||||
RSpec.describe StatusPinValidator do
|
||||
describe '#validate' do
|
||||
before do
|
||||
subject.validate(pin)
|
||||
|
|
Loading…
Reference in New Issue