Merge commit '36452845d78f6c3501af1e39391d06ab88a45a5a' into glitch-soc/backports-4.3

Conflicts:
- `.env.production.sample`:
  Upstream added a block of three environment variables, while
  glitch-soc has a different version of the file overall.
  Added upstream's changes.
This commit is contained in:
Claire 2024-10-16 12:42:12 +02:00
commit 198283a188
64 changed files with 739 additions and 435 deletions

View File

@ -73,6 +73,15 @@ DB_PORT=5432
SECRET_KEY_BASE=
OTP_SECRET=
# Encryption secrets
# ------------------
# Must be available (and set to same values) for all server processes
# These are private/secret values, do not share outside hosting environment
# Use `bin/rails db:encryption:init` to generate fresh secrets
# ------------------
# ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY=
# ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT=
# ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY=
# Web Push
# --------

View File

@ -32,6 +32,8 @@ jobs:
postgres:
- 14-alpine
- 15-alpine
- 16-alpine
- 17-alpine
services:
postgres:

View File

@ -143,7 +143,7 @@ jobs:
uses: ./.github/actions/setup-ruby
with:
ruby-version: ${{ matrix.ruby-version}}
additional-system-dependencies: ffmpeg libpam-dev
additional-system-dependencies: ffmpeg imagemagick libpam-dev
- name: Load database schema
run: |
@ -245,7 +245,7 @@ jobs:
uses: ./.github/actions/setup-ruby
with:
ruby-version: ${{ matrix.ruby-version}}
additional-system-dependencies: ffmpeg libpam-dev libyaml-dev
additional-system-dependencies: ffmpeg libpam-dev
- name: Load database schema
run: './bin/rails db:create db:schema:load db:seed'
@ -325,7 +325,7 @@ jobs:
uses: ./.github/actions/setup-ruby
with:
ruby-version: ${{ matrix.ruby-version}}
additional-system-dependencies: ffmpeg
additional-system-dependencies: ffmpeg imagemagick
- name: Set up Javascript environment
uses: ./.github/actions/setup-javascript
@ -445,7 +445,7 @@ jobs:
uses: ./.github/actions/setup-ruby
with:
ruby-version: ${{ matrix.ruby-version}}
additional-system-dependencies: ffmpeg
additional-system-dependencies: ffmpeg imagemagick
- name: Set up Javascript environment
uses: ./.github/actions/setup-javascript

View File

@ -23,6 +23,6 @@ class Api::V1::Statuses::TranslationsController < Api::V1::Statuses::BaseControl
private
def set_translation
@translation = TranslateStatusService.new.call(@status, content_locale)
@translation = TranslateStatusService.new.call(@status, I18n.locale.to_s)
end
end

View File

@ -116,6 +116,7 @@ export const MuteModal = ({ accountId, acct }) => {
<div className='safety-action-modal__bottom__collapsible'>
<div className='safety-action-modal__field-group'>
<RadioButtonLabel name='duration' value='0' label={intl.formatMessage(messages.indefinite)} currentValue={muteDuration} onChange={handleChangeMuteDuration} />
<RadioButtonLabel name='duration' value='21600' label={intl.formatMessage(messages.hours, { number: 6 })} currentValue={muteDuration} onChange={handleChangeMuteDuration} />
<RadioButtonLabel name='duration' value='86400' label={intl.formatMessage(messages.hours, { number: 24 })} currentValue={muteDuration} onChange={handleChangeMuteDuration} />
<RadioButtonLabel name='duration' value='604800' label={intl.formatMessage(messages.days, { number: 7 })} currentValue={muteDuration} onChange={handleChangeMuteDuration} />
<RadioButtonLabel name='duration' value='2592000' label={intl.formatMessage(messages.days, { number: 30 })} currentValue={muteDuration} onChange={handleChangeMuteDuration} />

View File

@ -10804,21 +10804,17 @@ noscript {
color: $darker-text-color;
-webkit-line-clamp: 4;
-webkit-box-orient: vertical;
max-height: 4 * 22px;
max-height: none;
overflow: hidden;
p {
display: none;
&:first-child {
display: initial;
}
}
p,
a {
color: inherit;
}
p {
margin-bottom: 8px;
}
}
.reply-indicator__attachments {

View File

@ -9,6 +9,8 @@ class TranslateStatusService < BaseService
def call(status, target_language)
@status = status
@source_texts = source_texts
target_language = target_language.split(/[_-]/).first unless target_languages.include?(target_language)
@target_language = target_language
raise Mastodon::NotPermittedError unless permitted?
@ -32,11 +34,15 @@ class TranslateStatusService < BaseService
def permitted?
return false unless @status.distributable? && TranslationService.configured?
languages[@status.language]&.include?(@target_language)
target_languages.include?(@target_language)
end
def languages
Rails.cache.fetch('translation_service/languages', expires_in: 7.days, race_condition_ttl: 1.hour) { TranslationService.configured.languages }
Rails.cache.fetch('translation_service/languages', expires_in: 7.days, race_condition_ttl: 1.hour) { translation_backend.languages }
end
def target_languages
languages[@status.language] || []
end
def content_hash

View File

@ -1,4 +1,4 @@
%tr
%tr{ id: dom_id(invite) }
%td
.input-copy
.input-copy__wrapper

View File

@ -1,59 +0,0 @@
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe Admin::InvitesController do
render_views
let(:user) { Fabricate(:user, role: UserRole.find_by(name: 'Admin')) }
before do
sign_in user, scope: :user
end
describe 'GET #index' do
subject { get :index, params: { available: true } }
let!(:invite) { Fabricate(:invite) }
it 'renders index page' do
expect(subject).to render_template :index
expect(response.body)
.to include(invite.code)
end
end
describe 'POST #create' do
subject { post :create, params: { invite: { max_uses: '10', expires_in: 1800 } } }
it 'succeeds to create a invite' do
expect { subject }.to change(Invite, :count).by(1)
expect(subject).to redirect_to admin_invites_path
expect(Invite.last).to have_attributes(user_id: user.id, max_uses: 10)
end
end
describe 'DELETE #destroy' do
subject { delete :destroy, params: { id: invite.id } }
let!(:invite) { Fabricate(:invite, expires_at: nil) }
it 'expires invite' do
expect(subject).to redirect_to admin_invites_path
expect(invite.reload).to be_expired
end
end
describe 'POST #deactivate_all' do
before { Fabricate(:invite, expires_at: nil) }
it 'expires all invites, then redirects to admin_invites_path' do
expect { post :deactivate_all }
.to change { Invite.exists?(expires_at: nil) }
.from(true)
.to(false)
expect(response).to redirect_to admin_invites_path
end
end
end

View File

@ -736,76 +736,4 @@ RSpec.describe StatusesController do
end
end
end
describe 'GET #embed' do
let(:account) { Fabricate(:account) }
let(:status) { Fabricate(:status, account: account) }
context 'when account is suspended' do
let(:account) { Fabricate(:account, suspended: true) }
before do
get :embed, params: { account_username: account.username, id: status.id }
end
it 'returns http gone' do
expect(response).to have_http_status(410)
end
end
context 'when status is a reblog' do
let(:original_account) { Fabricate(:account, domain: 'example.com') }
let(:original_status) { Fabricate(:status, account: original_account, url: 'https://example.com/123') }
let(:status) { Fabricate(:status, account: account, reblog: original_status) }
before do
get :embed, params: { account_username: status.account.username, id: status.id }
end
it 'returns http not found' do
expect(response).to have_http_status(404)
end
end
context 'when status is public' do
before do
get :embed, params: { account_username: status.account.username, id: status.id }
end
it 'renders status successfully', :aggregate_failures do
expect(response)
.to have_http_status(200)
.and render_template(:embed)
expect(response.headers).to include(
'Vary' => 'Accept, Accept-Language, Cookie',
'Cache-Control' => include('public'),
'Link' => include('activity+json')
)
end
end
context 'when status is private' do
let(:status) { Fabricate(:status, account: account, visibility: :private) }
before do
get :embed, params: { account_username: status.account.username, id: status.id }
end
it 'returns http not found' do
expect(response).to have_http_status(404)
end
end
context 'when status is direct' do
let(:status) { Fabricate(:status, account: account, visibility: :direct) }
before do
get :embed, params: { account_username: status.account.username, id: status.id }
end
it 'returns http not found' do
expect(response).to have_http_status(404)
end
end
end
end

View File

@ -0,0 +1,6 @@
# frozen_string_literal: true
Fabricator(:ip_block) do
severity { :sign_up_requires_approval }
ip { sequence(:ip) { |n| "10.0.0.#{n}" } }
end

View File

@ -38,16 +38,23 @@ RSpec.describe AccountReachFinder do
end
describe '#inboxes' do
it 'includes the preferred inbox URL of followers' do
expect(described_class.new(account).inboxes).to include(*[ap_follower_example_com, ap_follower_example_org, ap_follower_with_shared].map(&:preferred_inbox_url))
subject { described_class.new(account).inboxes }
it 'includes the preferred inbox URL of followers and recently mentioned accounts but not unrelated users' do
expect(subject)
.to include(*follower_inbox_urls)
.and include(*mentioned_account_inbox_urls)
.and not_include(unrelated_account.preferred_inbox_url)
end
it 'includes the preferred inbox URL of recently-mentioned accounts' do
expect(described_class.new(account).inboxes).to include(*[ap_mentioned_with_shared, ap_mentioned_example_com, ap_mentioned_example_org].map(&:preferred_inbox_url))
def follower_inbox_urls
[ap_follower_example_com, ap_follower_example_org, ap_follower_with_shared]
.map(&:preferred_inbox_url)
end
it 'does not include the inbox of unrelated users' do
expect(described_class.new(account).inboxes).to_not include(unrelated_account.preferred_inbox_url)
def mentioned_account_inbox_urls
[ap_mentioned_with_shared, ap_mentioned_example_com, ap_mentioned_example_org]
.map(&:preferred_inbox_url)
end
end
end

View File

@ -3,6 +3,8 @@
require 'rails_helper'
RSpec.describe ActivityPub::Activity::Block do
subject { described_class.new(json, sender) }
let(:sender) { Fabricate(:account) }
let(:recipient) { Fabricate(:account) }
@ -16,93 +18,65 @@ RSpec.describe ActivityPub::Activity::Block do
}.with_indifferent_access
end
context 'when the recipient does not follow the sender' do
describe '#perform' do
subject { described_class.new(json, sender) }
before do
subject.perform
end
describe '#perform' do
context 'when the recipient does not follow the sender' do
it 'creates a block from sender to recipient' do
expect(sender.blocking?(recipient)).to be true
subject.perform
expect(sender)
.to be_blocking(recipient)
end
end
end
context 'when the recipient is already blocked' do
before do
sender.block!(recipient, uri: 'old')
context 'when the recipient is already blocked' do
before { sender.block!(recipient, uri: 'old') }
it 'creates a block from sender to recipient and sets uri to last received block activity' do
subject.perform
expect(sender)
.to be_blocking(recipient)
expect(sender.block_relationships.find_by(target_account: recipient).uri)
.to eq 'foo'
end
end
describe '#perform' do
subject { described_class.new(json, sender) }
context 'when the recipient follows the sender' do
before { recipient.follow!(sender) }
it 'creates a block from sender to recipient and ensures recipient not following sender' do
subject.perform
expect(sender)
.to be_blocking(recipient)
expect(recipient)
.to_not be_following(sender)
end
end
context 'when a matching undo has been received first' do
let(:undo_json) do
{
'@context': 'https://www.w3.org/ns/activitystreams',
id: 'bar',
type: 'Undo',
actor: ActivityPub::TagManager.instance.uri_for(sender),
object: json,
}.with_indifferent_access
end
before do
recipient.follow!(sender)
ActivityPub::Activity::Undo.new(undo_json, sender).perform
end
it 'does not create a block from sender to recipient and ensures recipient not following sender' do
subject.perform
end
it 'creates a block from sender to recipient' do
expect(sender.blocking?(recipient)).to be true
end
it 'sets the uri to that of last received block activity' do
expect(sender.block_relationships.find_by(target_account: recipient).uri).to eq 'foo'
end
end
end
context 'when the recipient follows the sender' do
before do
recipient.follow!(sender)
end
describe '#perform' do
subject { described_class.new(json, sender) }
before do
subject.perform
end
it 'creates a block from sender to recipient' do
expect(sender.blocking?(recipient)).to be true
end
it 'ensures recipient is not following sender' do
expect(recipient.following?(sender)).to be false
end
end
end
context 'when a matching undo has been received first' do
let(:undo_json) do
{
'@context': 'https://www.w3.org/ns/activitystreams',
id: 'bar',
type: 'Undo',
actor: ActivityPub::TagManager.instance.uri_for(sender),
object: json,
}.with_indifferent_access
end
before do
recipient.follow!(sender)
ActivityPub::Activity::Undo.new(undo_json, sender).perform
end
describe '#perform' do
subject { described_class.new(json, sender) }
before do
subject.perform
end
it 'does not create a block from sender to recipient' do
expect(sender.blocking?(recipient)).to be false
end
it 'ensures recipient is not following sender' do
expect(recipient.following?(sender)).to be false
expect(sender)
.to_not be_blocking(recipient)
expect(recipient)
.to_not be_following(sender)
end
end
end

View File

@ -14,32 +14,24 @@ RSpec.describe Vacuum::AccessTokensVacuum do
let!(:expired_access_grant) { Fabricate(:access_grant, expires_in: 59.minutes.to_i, created_at: 1.hour.ago) }
let!(:active_access_grant) { Fabricate(:access_grant) }
before do
it 'deletes revoked/expired access tokens and revoked/expired grants, but preserves active tokens/grants' do
subject.perform
end
it 'deletes revoked access tokens' do
expect { revoked_access_token.reload }.to raise_error ActiveRecord::RecordNotFound
end
expect { revoked_access_token.reload }
.to raise_error ActiveRecord::RecordNotFound
expect { expired_access_token.reload }
.to raise_error ActiveRecord::RecordNotFound
it 'deletes expired access tokens' do
expect { expired_access_token.reload }.to raise_error ActiveRecord::RecordNotFound
end
expect { revoked_access_grant.reload }
.to raise_error ActiveRecord::RecordNotFound
expect { expired_access_grant.reload }
.to raise_error ActiveRecord::RecordNotFound
it 'deletes revoked access grants' do
expect { revoked_access_grant.reload }.to raise_error ActiveRecord::RecordNotFound
end
expect { active_access_token.reload }
.to_not raise_error
it 'deletes expired access grants' do
expect { expired_access_grant.reload }.to raise_error ActiveRecord::RecordNotFound
end
it 'does not delete active access tokens' do
expect { active_access_token.reload }.to_not raise_error
end
it 'does not delete active access grants' do
expect { active_access_grant.reload }.to_not raise_error
expect { active_access_grant.reload }
.to_not raise_error
end
end
end

View File

@ -11,16 +11,13 @@ RSpec.describe Vacuum::BackupsVacuum do
let!(:expired_backup) { Fabricate(:backup, created_at: (retention_period + 1.day).ago) }
let!(:current_backup) { Fabricate(:backup) }
before do
it 'deletes backups past the retention period but preserves those within the period' do
subject.perform
end
it 'deletes backups past the retention period' do
expect { expired_backup.reload }.to raise_error ActiveRecord::RecordNotFound
end
it 'does not delete backups within the retention period' do
expect { current_backup.reload }.to_not raise_error
expect { expired_backup.reload }
.to raise_error ActiveRecord::RecordNotFound
expect { current_backup.reload }
.to_not raise_error
end
end
end

View File

@ -14,11 +14,11 @@ RSpec.describe Vacuum::FeedsVacuum do
redis.zadd(feed_key_for(active_user), 1, 1)
redis.zadd(feed_key_for(inactive_user, 'reblogs'), 2, 2)
redis.sadd(feed_key_for(inactive_user, 'reblogs:2'), 3)
subject.perform
end
it 'clears feeds of inactive users and lists' do
subject.perform
expect(redis.zcard(feed_key_for(inactive_user))).to eq 0
expect(redis.zcard(feed_key_for(active_user))).to eq 1
expect(redis.exists?(feed_key_for(inactive_user, 'reblogs'))).to be false

View File

@ -17,9 +17,9 @@ RSpec.describe Vacuum::MediaAttachmentsVacuum do
let!(:old_unattached_media) { Fabricate(:media_attachment, account_id: nil, created_at: 10.days.ago) }
let!(:new_unattached_media) { Fabricate(:media_attachment, account_id: nil, created_at: 1.hour.ago) }
before { subject.perform }
it 'handles attachments based on metadata details' do
subject.perform
expect(old_remote_media.reload.file) # Remote and past retention period
.to be_blank
expect(old_local_media.reload.file) # Local and past retention

View File

@ -15,24 +15,22 @@ RSpec.describe Vacuum::PreviewCardsVacuum do
before do
old_preview_card.statuses << Fabricate(:status)
new_preview_card.statuses << Fabricate(:status)
end
it 'handles preview card cleanup' do
subject.perform
end
it 'deletes cache of preview cards last updated before the retention period' do
expect(old_preview_card.reload.image).to be_blank
end
expect(old_preview_card.reload.image) # last updated before retention period
.to be_blank
it 'does not delete cache of preview cards last updated within the retention period' do
expect(new_preview_card.reload.image).to_not be_blank
end
expect(new_preview_card.reload.image) # last updated within the retention period
.to_not be_blank
it 'does not delete attached preview cards' do
expect(new_preview_card.reload).to be_persisted
end
expect(new_preview_card.reload) # Keep attached preview cards
.to be_persisted
it 'does not delete orphaned preview cards in the retention period' do
expect(orphaned_preview_card.reload).to be_persisted
expect(orphaned_preview_card.reload) # keep orphaned cards in the retention period
.to be_persisted
end
end
end

View File

@ -15,24 +15,20 @@ RSpec.describe Vacuum::StatusesVacuum do
let!(:local_status_old) { Fabricate(:status, created_at: (retention_period + 2.days).ago) }
let!(:local_status_recent) { Fabricate(:status, created_at: (retention_period - 2.days).ago) }
before do
it 'deletes remote statuses past the retention period and keeps others' do
subject.perform
end
it 'deletes remote statuses past the retention period' do
expect { remote_status_old.reload }.to raise_error ActiveRecord::RecordNotFound
end
expect { remote_status_old.reload }
.to raise_error ActiveRecord::RecordNotFound
it 'does not delete local statuses past the retention period' do
expect { local_status_old.reload }.to_not raise_error
end
expect { local_status_old.reload }
.to_not raise_error
it 'does not delete remote statuses within the retention period' do
expect { remote_status_recent.reload }.to_not raise_error
end
expect { remote_status_recent.reload }
.to_not raise_error
it 'does not delete local statuses within the retention period' do
expect { local_status_recent.reload }.to_not raise_error
expect { local_status_recent.reload }
.to_not raise_error
end
end
end

View File

@ -16,6 +16,8 @@ RSpec.describe AccountStatusesCleanupPolicy do
describe 'save hooks' do
context 'when widening a policy' do
subject { account_statuses_cleanup_policy.last_inspected }
let!(:account_statuses_cleanup_policy) do
Fabricate(:account_statuses_cleanup_policy,
account: account,
@ -33,64 +35,64 @@ RSpec.describe AccountStatusesCleanupPolicy do
account_statuses_cleanup_policy.record_last_inspected(42)
end
it 'invalidates last_inspected when widened because of keep_direct' do
account_statuses_cleanup_policy.keep_direct = false
account_statuses_cleanup_policy.save
expect(account_statuses_cleanup_policy.last_inspected).to be_nil
context 'when widened because of keep_direct' do
before { account_statuses_cleanup_policy.update(keep_direct: false) }
it { is_expected.to be_nil }
end
it 'invalidates last_inspected when widened because of keep_pinned' do
account_statuses_cleanup_policy.keep_pinned = false
account_statuses_cleanup_policy.save
expect(account_statuses_cleanup_policy.last_inspected).to be_nil
context 'when widened because of keep_pinned' do
before { account_statuses_cleanup_policy.update(keep_pinned: false) }
it { is_expected.to be_nil }
end
it 'invalidates last_inspected when widened because of keep_polls' do
account_statuses_cleanup_policy.keep_polls = false
account_statuses_cleanup_policy.save
expect(account_statuses_cleanup_policy.last_inspected).to be_nil
context 'when widened because of keep_polls' do
before { account_statuses_cleanup_policy.update(keep_polls: false) }
it { is_expected.to be_nil }
end
it 'invalidates last_inspected when widened because of keep_media' do
account_statuses_cleanup_policy.keep_media = false
account_statuses_cleanup_policy.save
expect(account_statuses_cleanup_policy.last_inspected).to be_nil
context 'when widened because of keep_media' do
before { account_statuses_cleanup_policy.update(keep_media: false) }
it { is_expected.to be_nil }
end
it 'invalidates last_inspected when widened because of keep_self_fav' do
account_statuses_cleanup_policy.keep_self_fav = false
account_statuses_cleanup_policy.save
expect(account_statuses_cleanup_policy.last_inspected).to be_nil
context 'when widened because of keep_self_fav' do
before { account_statuses_cleanup_policy.update(keep_self_fav: false) }
it { is_expected.to be_nil }
end
it 'invalidates last_inspected when widened because of keep_self_bookmark' do
account_statuses_cleanup_policy.keep_self_bookmark = false
account_statuses_cleanup_policy.save
expect(account_statuses_cleanup_policy.last_inspected).to be_nil
context 'when widened because of keep_self_bookmark' do
before { account_statuses_cleanup_policy.update(keep_self_bookmark: false) }
it { is_expected.to be_nil }
end
it 'invalidates last_inspected when widened because of higher min_favs' do
account_statuses_cleanup_policy.min_favs = 5
account_statuses_cleanup_policy.save
expect(account_statuses_cleanup_policy.last_inspected).to be_nil
context 'when widened because of higher min_favs' do
before { account_statuses_cleanup_policy.update(min_favs: 5) }
it { is_expected.to be_nil }
end
it 'invalidates last_inspected when widened because of disabled min_favs' do
account_statuses_cleanup_policy.min_favs = nil
account_statuses_cleanup_policy.save
expect(account_statuses_cleanup_policy.last_inspected).to be_nil
context 'when widened because of disabled min_favs' do
before { account_statuses_cleanup_policy.update(min_favs: nil) }
it { is_expected.to be_nil }
end
it 'invalidates last_inspected when widened because of higher min_reblogs' do
account_statuses_cleanup_policy.min_reblogs = 5
account_statuses_cleanup_policy.save
expect(account_statuses_cleanup_policy.last_inspected).to be_nil
context 'when widened because of higher min_reblogs' do
before { account_statuses_cleanup_policy.update(min_reblogs: 5) }
it { is_expected.to be_nil }
end
it 'invalidates last_inspected when widened because of disable min_reblogs' do
account_statuses_cleanup_policy.min_reblogs = nil
account_statuses_cleanup_policy.save
expect(account_statuses_cleanup_policy.last_inspected).to be_nil
context 'when widened because of disable min_reblogs' do
before { account_statuses_cleanup_policy.update(min_reblogs: nil) }
it { is_expected.to be_nil }
end
end

View File

@ -3,11 +3,37 @@
require 'rails_helper'
RSpec.describe Block do
describe 'validations' do
describe 'Associations' do
it { is_expected.to belong_to(:account).required }
it { is_expected.to belong_to(:target_account).required }
end
describe '#local?' do
it { is_expected.to_not be_local }
end
describe 'Callbacks' do
describe 'Setting a URI' do
context 'when URI exists' do
subject { Fabricate.build :block, uri: 'https://uri/value' }
it 'does not change' do
expect { subject.save }
.to not_change(subject, :uri)
end
end
context 'when URI is blank' do
subject { Fabricate.build :follow, uri: nil }
it 'populates the value' do
expect { subject.save }
.to change(subject, :uri).to(be_present)
end
end
end
end
it 'removes blocking cache after creation' do
account = Fabricate(:account)
target_account = Fabricate(:account)

View File

@ -3,7 +3,7 @@
require 'rails_helper'
RSpec.describe Poll do
describe 'scopes' do
describe 'Scopes' do
let(:status) { Fabricate(:status) }
let(:attached_poll) { Fabricate(:poll, status: status) }
let(:not_attached_poll) do
@ -13,7 +13,7 @@ RSpec.describe Poll do
end
end
describe 'attached' do
describe '.attached' do
it 'finds the correct records' do
results = described_class.attached
@ -21,7 +21,7 @@ RSpec.describe Poll do
end
end
describe 'unattached' do
describe '.unattached' do
it 'finds the correct records' do
results = described_class.unattached
@ -30,11 +30,23 @@ RSpec.describe Poll do
end
end
describe 'validations' do
context 'when not valid' do
subject { Fabricate.build(:poll) }
describe '#reset_votes!' do
let(:poll) { Fabricate :poll, cached_tallies: [2, 3], votes_count: 5, voters_count: 5 }
let!(:vote) { Fabricate :poll_vote, poll: }
it { is_expected.to validate_presence_of(:expires_at) }
it 'resets vote data and deletes votes' do
expect { poll.reset_votes! }
.to change(poll, :cached_tallies).to([0, 0])
.and change(poll, :votes_count).to(0)
.and(change(poll, :voters_count).to(0))
expect { vote.reload }
.to raise_error(ActiveRecord::RecordNotFound)
end
end
describe 'Validations' do
subject { Fabricate.build(:poll) }
it { is_expected.to validate_presence_of(:expires_at) }
end
end

View File

@ -55,12 +55,10 @@ RSpec.describe PublicFeed do
context 'without a viewer' do
let(:viewer) { nil }
it 'includes remote instances statuses' do
expect(subject).to include(remote_status.id)
end
it 'includes local statuses' do
expect(subject).to include(local_status.id)
it 'includes remote instances statuses and local statuses' do
expect(subject)
.to include(remote_status.id)
.and include(local_status.id)
end
it 'does not include local-only statuses' do
@ -71,12 +69,10 @@ RSpec.describe PublicFeed do
context 'with a viewer' do
let(:viewer) { Fabricate(:account, username: 'viewer') }
it 'includes remote instances statuses' do
expect(subject).to include(remote_status.id)
end
it 'includes local statuses' do
expect(subject).to include(local_status.id)
it 'includes remote instances statuses and local statuses' do
expect(subject)
.to include(remote_status.id)
.and include(local_status.id)
end
it 'does not include local-only statuses' do

View File

@ -387,23 +387,43 @@ RSpec.describe User do
end
end
describe 'token_for_app' do
describe '#token_for_app' do
let(:user) { Fabricate(:user) }
let(:app) { Fabricate(:application, owner: user) }
it 'returns a token' do
expect(user.token_for_app(app)).to be_a(Doorkeeper::AccessToken)
context 'when user owns app but does not have tokens' do
let(:app) { Fabricate(:application, owner: user) }
it 'creates and returns a persisted token' do
expect { user.token_for_app(app) }
.to change(Doorkeeper::AccessToken.where(resource_owner_id: user.id, application: app), :count).by(1)
end
end
it 'persists a token' do
t = user.token_for_app(app)
expect(user.token_for_app(app)).to eql(t)
context 'when user owns app and already has tokens' do
let(:app) { Fabricate(:application, owner: user) }
let!(:token) { Fabricate :access_token, application: app, resource_owner_id: user.id }
it 'returns a persisted token' do
expect(user.token_for_app(app))
.to be_a(Doorkeeper::AccessToken)
.and eq(token)
end
end
it 'is nil if user does not own app' do
app.update!(owner: nil)
context 'when user does not own app' do
let(:app) { Fabricate(:application) }
expect(user.token_for_app(app)).to be_nil
it 'returns nil' do
expect(user.token_for_app(app))
.to be_nil
end
end
context 'when app is nil' do
it 'returns nil' do
expect(user.token_for_app(nil))
.to be_nil
end
end
end

View File

@ -1,7 +1,6 @@
# frozen_string_literal: true
require 'rails_helper'
require 'pundit/rspec'
RSpec.describe AccountModerationNotePolicy do
subject { described_class }
@ -12,13 +11,13 @@ RSpec.describe AccountModerationNotePolicy do
permissions :create? do
context 'when staff' do
it 'grants to create' do
expect(subject).to permit(admin, described_class)
expect(subject).to permit(admin, AccountModerationNote)
end
end
context 'when not staff' do
it 'denies to create' do
expect(subject).to_not permit(john, described_class)
expect(subject).to_not permit(john, AccountModerationNote)
end
end
end

View File

@ -1,7 +1,6 @@
# frozen_string_literal: true
require 'rails_helper'
require 'pundit/rspec'
RSpec.describe AccountPolicy do
subject { described_class }
@ -24,7 +23,7 @@ RSpec.describe AccountPolicy do
end
end
permissions :show?, :unsilence?, :unsensitive?, :remove_avatar?, :remove_header? do
permissions :show?, :unsilence?, :unsensitive?, :remove_avatar?, :remove_header?, :sensitive?, :warn? do
context 'when staff' do
it 'permits' do
expect(subject).to permit(admin, alice)

View File

@ -0,0 +1,42 @@
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe AccountWarningPolicy do
subject { described_class }
let(:admin) { Fabricate(:user, role: UserRole.find_by(name: 'Admin')).account }
let(:account) { Fabricate(:account) }
permissions :show? do
context 'with an admin' do
it { is_expected.to permit(admin, AccountWarning.new) }
end
context 'with a non-admin' do
context 'when account is not target' do
it { is_expected.to_not permit(account, AccountWarning.new) }
end
context 'when account is target' do
it { is_expected.to permit(account, AccountWarning.new(target_account_id: account.id)) }
end
end
end
permissions :appeal? do
context 'when account is not target' do
it { is_expected.to_not permit(account, AccountWarning.new) }
end
context 'when account is target' do
context 'when record is appealable' do
it { is_expected.to permit(account, AccountWarning.new(target_account_id: account.id, created_at: Appeal::MAX_STRIKE_AGE.ago + 1.hour)) }
end
context 'when record is not appealable' do
it { is_expected.to_not permit(account, AccountWarning.new(target_account_id: account.id, created_at: Appeal::MAX_STRIKE_AGE.ago - 1.hour)) }
end
end
end
end

View File

@ -1,7 +1,6 @@
# frozen_string_literal: true
require 'rails_helper'
require 'pundit/rspec'
RSpec.describe AccountWarningPresetPolicy do
let(:policy) { described_class }
@ -11,13 +10,13 @@ RSpec.describe AccountWarningPresetPolicy do
permissions :index?, :create?, :update?, :destroy? do
context 'with an admin' do
it 'permits' do
expect(policy).to permit(admin, Tag)
expect(policy).to permit(admin, AccountWarningPreset)
end
end
context 'with a non-admin' do
it 'denies' do
expect(policy).to_not permit(john, Tag)
expect(policy).to_not permit(john, AccountWarningPreset)
end
end
end

View File

@ -1,7 +1,6 @@
# frozen_string_literal: true
require 'rails_helper'
require 'pundit/rspec'
RSpec.describe Admin::StatusPolicy do
let(:policy) { described_class }
@ -13,13 +12,13 @@ RSpec.describe Admin::StatusPolicy do
permissions :index?, :update?, :review?, :destroy? do
context 'with an admin' do
it 'permits' do
expect(policy).to permit(admin, Tag)
expect(policy).to permit(admin, Status)
end
end
context 'with a non-admin' do
it 'denies' do
expect(policy).to_not permit(john, Tag)
expect(policy).to_not permit(john, Status)
end
end
end

View File

@ -1,7 +1,6 @@
# frozen_string_literal: true
require 'rails_helper'
require 'pundit/rspec'
RSpec.describe AnnouncementPolicy do
let(:policy) { described_class }
@ -11,13 +10,13 @@ RSpec.describe AnnouncementPolicy do
permissions :index?, :create?, :update?, :destroy? do
context 'with an admin' do
it 'permits' do
expect(policy).to permit(admin, Tag)
expect(policy).to permit(admin, Announcement)
end
end
context 'with a non-admin' do
it 'denies' do
expect(policy).to_not permit(john, Tag)
expect(policy).to_not permit(john, Announcement)
end
end
end

View File

@ -1,7 +1,6 @@
# frozen_string_literal: true
require 'rails_helper'
require 'pundit/rspec'
RSpec.describe AppealPolicy do
let(:policy) { described_class }
@ -12,18 +11,18 @@ RSpec.describe AppealPolicy do
permissions :index? do
context 'with an admin' do
it 'permits' do
expect(policy).to permit(admin, Tag)
expect(policy).to permit(admin, Appeal)
end
end
context 'with a non-admin' do
it 'denies' do
expect(policy).to_not permit(john, Tag)
expect(policy).to_not permit(john, Appeal)
end
end
end
permissions :reject? do
permissions :reject?, :approve? do
context 'with an admin' do
context 'with a pending appeal' do
before { allow(appeal).to receive(:pending?).and_return(true) }

View File

@ -0,0 +1,20 @@
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe AuditLogPolicy do
subject { described_class }
let(:admin) { Fabricate(:user, role: UserRole.find_by(name: 'Admin')).account }
let(:account) { Fabricate(:account) }
permissions :index? do
context 'with an admin' do
it { is_expected.to permit(admin, nil) }
end
context 'with a non-admin' do
it { is_expected.to_not permit(account, nil) }
end
end
end

View File

@ -1,7 +1,6 @@
# frozen_string_literal: true
require 'rails_helper'
require 'pundit/rspec'
RSpec.describe BackupPolicy do
subject { described_class }

View File

@ -1,7 +1,6 @@
# frozen_string_literal: true
require 'rails_helper'
require 'pundit/rspec'
RSpec.describe CanonicalEmailBlockPolicy do
let(:policy) { described_class }
@ -11,13 +10,13 @@ RSpec.describe CanonicalEmailBlockPolicy do
permissions :index?, :show?, :test?, :create?, :destroy? do
context 'with an admin' do
it 'permits' do
expect(policy).to permit(admin, Tag)
expect(policy).to permit(admin, CanonicalEmailBlock)
end
end
context 'with a non-admin' do
it 'denies' do
expect(policy).to_not permit(john, Tag)
expect(policy).to_not permit(john, CanonicalEmailBlock)
end
end
end

View File

@ -1,7 +1,6 @@
# frozen_string_literal: true
require 'rails_helper'
require 'pundit/rspec'
RSpec.describe CustomEmojiPolicy do
subject { described_class }

View File

@ -0,0 +1,20 @@
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe DashboardPolicy do
subject { described_class }
let(:admin) { Fabricate(:user, role: UserRole.find_by(name: 'Admin')).account }
let(:account) { Fabricate(:account) }
permissions :index? do
context 'with an admin' do
it { is_expected.to permit(admin, nil) }
end
context 'with a non-admin' do
it { is_expected.to_not permit(account, nil) }
end
end
end

View File

@ -1,7 +1,6 @@
# frozen_string_literal: true
require 'rails_helper'
require 'pundit/rspec'
RSpec.describe DeliveryPolicy do
let(:policy) { described_class }
@ -11,13 +10,13 @@ RSpec.describe DeliveryPolicy do
permissions :clear_delivery_errors?, :restart_delivery?, :stop_delivery? do
context 'with an admin' do
it 'permits' do
expect(policy).to permit(admin, Tag)
expect(policy).to permit(admin, nil)
end
end
context 'with a non-admin' do
it 'denies' do
expect(policy).to_not permit(john, Tag)
expect(policy).to_not permit(john, nil)
end
end
end

View File

@ -0,0 +1,24 @@
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe DomainAllowPolicy do
subject { described_class }
let(:admin) { Fabricate(:user, role: UserRole.find_by(name: 'Admin')).account }
let(:john) { Fabricate(:account) }
permissions :index?, :show?, :create?, :destroy? do
context 'when admin' do
it 'permits' do
expect(subject).to permit(admin, DomainAllow)
end
end
context 'when not admin' do
it 'denies' do
expect(subject).to_not permit(john, DomainAllow)
end
end
end
end

View File

@ -1,7 +1,6 @@
# frozen_string_literal: true
require 'rails_helper'
require 'pundit/rspec'
RSpec.describe DomainBlockPolicy do
subject { described_class }
@ -9,7 +8,7 @@ RSpec.describe DomainBlockPolicy do
let(:admin) { Fabricate(:user, role: UserRole.find_by(name: 'Admin')).account }
let(:john) { Fabricate(:account) }
permissions :index?, :show?, :create?, :destroy? do
permissions :index?, :show?, :create?, :destroy?, :update? do
context 'when admin' do
it 'permits' do
expect(subject).to permit(admin, DomainBlock)

View File

@ -1,7 +1,6 @@
# frozen_string_literal: true
require 'rails_helper'
require 'pundit/rspec'
RSpec.describe EmailDomainBlockPolicy do
subject { described_class }

View File

@ -1,7 +1,6 @@
# frozen_string_literal: true
require 'rails_helper'
require 'pundit/rspec'
RSpec.describe FollowRecommendationPolicy do
let(:policy) { described_class }
@ -11,13 +10,13 @@ RSpec.describe FollowRecommendationPolicy do
permissions :show?, :suppress?, :unsuppress? do
context 'with an admin' do
it 'permits' do
expect(policy).to permit(admin, Tag)
expect(policy).to permit(admin, FollowRecommendation)
end
end
context 'with a non-admin' do
it 'denies' do
expect(policy).to_not permit(john, Tag)
expect(policy).to_not permit(john, FollowRecommendation)
end
end
end

View File

@ -1,7 +1,6 @@
# frozen_string_literal: true
require 'rails_helper'
require 'pundit/rspec'
RSpec.describe InstancePolicy do
subject { described_class }

View File

@ -1,7 +1,6 @@
# frozen_string_literal: true
require 'rails_helper'
require 'pundit/rspec'
RSpec.describe InvitePolicy do
subject { described_class }

View File

@ -1,7 +1,6 @@
# frozen_string_literal: true
require 'rails_helper'
require 'pundit/rspec'
RSpec.describe IpBlockPolicy do
let(:policy) { described_class }
@ -11,13 +10,13 @@ RSpec.describe IpBlockPolicy do
permissions :index?, :show?, :create?, :update?, :destroy? do
context 'with an admin' do
it 'permits' do
expect(policy).to permit(admin, Tag)
expect(policy).to permit(admin, IpBlock)
end
end
context 'with a non-admin' do
it 'denies' do
expect(policy).to_not permit(john, Tag)
expect(policy).to_not permit(john, IpBlock)
end
end
end

View File

@ -0,0 +1,36 @@
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe PollPolicy do
subject { described_class }
let(:account) { Fabricate(:account) }
let(:poll) { Fabricate :poll }
permissions :vote? do
context 'when account cannot view status' do
before { poll.status.update(visibility: :private) }
it { is_expected.to_not permit(account, poll) }
end
context 'when account can view status' do
context 'when accounts do not block each other' do
it { is_expected.to permit(account, poll) }
end
context 'when view blocks poll creator' do
before { Fabricate :block, account: account, target_account: poll.account }
it { is_expected.to_not permit(account, poll) }
end
context 'when poll creator blocks viewer' do
before { Fabricate :block, account: poll.account, target_account: account }
it { is_expected.to_not permit(account, poll) }
end
end
end
end

View File

@ -1,7 +1,6 @@
# frozen_string_literal: true
require 'rails_helper'
require 'pundit/rspec'
RSpec.describe PreviewCardPolicy do
let(:policy) { described_class }
@ -11,13 +10,13 @@ RSpec.describe PreviewCardPolicy do
permissions :index?, :review? do
context 'with an admin' do
it 'permits' do
expect(policy).to permit(admin, Tag)
expect(policy).to permit(admin, PreviewCard)
end
end
context 'with a non-admin' do
it 'denies' do
expect(policy).to_not permit(john, Tag)
expect(policy).to_not permit(john, PreviewCard)
end
end
end

View File

@ -1,7 +1,6 @@
# frozen_string_literal: true
require 'rails_helper'
require 'pundit/rspec'
RSpec.describe PreviewCardProviderPolicy do
let(:policy) { described_class }
@ -11,13 +10,13 @@ RSpec.describe PreviewCardProviderPolicy do
permissions :index?, :review? do
context 'with an admin' do
it 'permits' do
expect(policy).to permit(admin, Tag)
expect(policy).to permit(admin, PreviewCardProvider)
end
end
context 'with a non-admin' do
it 'denies' do
expect(policy).to_not permit(john, Tag)
expect(policy).to_not permit(john, PreviewCardProvider)
end
end
end

View File

@ -1,7 +1,6 @@
# frozen_string_literal: true
require 'rails_helper'
require 'pundit/rspec'
RSpec.describe RelayPolicy do
subject { described_class }

View File

@ -1,7 +1,6 @@
# frozen_string_literal: true
require 'rails_helper'
require 'pundit/rspec'
RSpec.describe ReportNotePolicy do
subject { described_class }

View File

@ -1,7 +1,6 @@
# frozen_string_literal: true
require 'rails_helper'
require 'pundit/rspec'
RSpec.describe ReportPolicy do
subject { described_class }

View File

@ -1,7 +1,6 @@
# frozen_string_literal: true
require 'rails_helper'
require 'pundit/rspec'
RSpec.describe RulePolicy do
let(:policy) { described_class }
@ -11,13 +10,13 @@ RSpec.describe RulePolicy do
permissions :index?, :create?, :update?, :destroy? do
context 'with an admin' do
it 'permits' do
expect(policy).to permit(admin, Tag)
expect(policy).to permit(admin, Rule)
end
end
context 'with a non-admin' do
it 'denies' do
expect(policy).to_not permit(john, Tag)
expect(policy).to_not permit(john, Rule)
end
end
end

View File

@ -1,7 +1,6 @@
# frozen_string_literal: true
require 'rails_helper'
require 'pundit/rspec'
RSpec.describe SettingsPolicy do
subject { described_class }

View File

@ -1,7 +1,6 @@
# frozen_string_literal: true
require 'rails_helper'
require 'pundit/rspec'
RSpec.describe SoftwareUpdatePolicy do
subject { described_class }

View File

@ -1,7 +1,6 @@
# frozen_string_literal: true
require 'rails_helper'
require 'pundit/rspec'
RSpec.describe StatusPolicy, type: :model do
subject { described_class }

View File

@ -1,7 +1,6 @@
# frozen_string_literal: true
require 'rails_helper'
require 'pundit/rspec'
RSpec.describe TagPolicy do
subject { described_class }

View File

@ -1,7 +1,6 @@
# frozen_string_literal: true
require 'rails_helper'
require 'pundit/rspec'
RSpec.describe UserPolicy do
subject { described_class }
@ -112,4 +111,42 @@ RSpec.describe UserPolicy do
end
end
end
permissions :approve?, :reject? do
context 'when admin' do
context 'when user is approved' do
it { is_expected.to_not permit(admin, User.new(approved: true)) }
end
context 'when user is not approved' do
it { is_expected.to permit(admin, User.new(approved: false)) }
end
end
context 'when not admin' do
it { is_expected.to_not permit(john, User.new) }
end
end
permissions :change_role? do
context 'when not admin' do
it { is_expected.to_not permit(john, User.new) }
end
context 'when admin' do
let(:user) { User.new(role: role) }
context 'when role of admin overrides user role' do
let(:role) { UserRole.new(position: admin.user.role.position - 10, id: 123) }
it { is_expected.to permit(admin, user) }
end
context 'when role of admin does not override user role' do
let(:role) { UserRole.new(position: admin.user.role.position + 10, id: 123) }
it { is_expected.to_not permit(admin, user) }
end
end
end
end

View File

@ -0,0 +1,56 @@
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe UserRolePolicy do
subject { described_class }
let(:admin) { Fabricate(:user, role: UserRole.find_by(name: 'Admin')).account }
let(:account) { Fabricate(:account) }
permissions :index?, :create? do
context 'when admin' do
it { is_expected.to permit(admin, UserRole.new) }
end
context 'when not admin' do
it { is_expected.to_not permit(account, UserRole.new) }
end
end
permissions :update? do
context 'when admin' do
context 'when role of admin overrides relevant role' do
it { is_expected.to permit(admin, UserRole.new(position: admin.user.role.position - 10, id: 123)) }
end
context 'when role of admin does not override relevant role' do
it { is_expected.to_not permit(admin, UserRole.new(position: admin.user.role.position + 10, id: 123)) }
end
end
context 'when not admin' do
it { is_expected.to_not permit(account, UserRole.new) }
end
end
permissions :destroy? do
context 'when admin' do
context 'when role of admin overrides relevant role' do
it { is_expected.to permit(admin, UserRole.new(position: admin.user.role.position - 10)) }
end
context 'when role of admin does not override relevant role' do
it { is_expected.to_not permit(admin, UserRole.new(position: admin.user.role.position + 10)) }
end
context 'when everyone role' do
it { is_expected.to_not permit(admin, UserRole.everyone) }
end
end
context 'when not admin' do
it { is_expected.to_not permit(account, UserRole.new) }
end
end
end

View File

@ -1,7 +1,6 @@
# frozen_string_literal: true
require 'rails_helper'
require 'pundit/rspec'
RSpec.describe WebhookPolicy do
let(:policy) { described_class }

View File

@ -43,6 +43,7 @@ require 'paperclip/matchers'
require 'capybara/rspec'
require 'chewy/rspec'
require 'email_spec/rspec'
require 'pundit/rspec'
require 'test_prof/recipes/rspec/before_all'
Rails.root.glob('spec/support/**/*.rb').each { |f| require f }

View File

@ -0,0 +1,74 @@
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe 'Status embed' do
describe 'GET /users/:account_username/statuses/:id/embed' do
subject { get "/users/#{account.username}/statuses/#{status.id}/embed" }
let(:account) { Fabricate(:account) }
let(:status) { Fabricate(:status, account: account) }
context 'when account is suspended' do
let(:account) { Fabricate(:account, suspended: true) }
it 'returns http gone' do
subject
expect(response)
.to have_http_status(410)
end
end
context 'when status is a reblog' do
let(:original_account) { Fabricate(:account, domain: 'example.com') }
let(:original_status) { Fabricate(:status, account: original_account, url: 'https://example.com/123') }
let(:status) { Fabricate(:status, account: account, reblog: original_status) }
it 'returns http not found' do
subject
expect(response)
.to have_http_status(404)
end
end
context 'when status is public' do
it 'renders status successfully', :aggregate_failures do
subject
expect(response)
.to have_http_status(200)
expect(response.parsed_body.at('body.embed'))
.to be_present
expect(response.headers).to include(
'Vary' => 'Accept, Accept-Language, Cookie',
'Cache-Control' => include('public'),
'Link' => include('activity+json')
)
end
end
context 'when status is private' do
let(:status) { Fabricate(:status, account: account, visibility: :private) }
it 'returns http not found' do
subject
expect(response)
.to have_http_status(404)
end
end
context 'when status is direct' do
let(:status) { Fabricate(:status, account: account, visibility: :direct) }
it 'returns http not found' do
subject
expect(response)
.to have_http_status(404)
end
end
end
end

View File

@ -14,7 +14,7 @@ RSpec.describe ActivityPub::NoteSerializer do
let!(:reply_by_account_third) { Fabricate(:status, account: account, thread: parent, visibility: :public) }
let!(:reply_by_account_visibility_direct) { Fabricate(:status, account: account, thread: parent, visibility: :direct) }
it 'has the expected shape' do
it 'has the expected shape and replies collection' do
expect(subject).to include({
'@context' => include('https://www.w3.org/ns/activitystreams'),
'type' => 'Note',
@ -22,26 +22,23 @@ RSpec.describe ActivityPub::NoteSerializer do
'contentMap' => include({
'zh-TW' => a_kind_of(String),
}),
'replies' => replies_collection_values,
})
end
it 'has a replies collection' do
expect(subject['replies']['type']).to eql('Collection')
def replies_collection_values
include(
'type' => eql('Collection'),
'first' => include(
'type' => eql('CollectionPage'),
'items' => reply_items
)
)
end
it 'has a replies collection with a first Page' do
expect(subject['replies']['first']['type']).to eql('CollectionPage')
end
it 'includes public self-replies in its replies collection' do
expect(subject['replies']['first']['items']).to include(reply_by_account_first.uri, reply_by_account_next.uri, reply_by_account_third.uri)
end
it 'does not include replies from others in its replies collection' do
expect(subject['replies']['first']['items']).to_not include(reply_by_other_first.uri)
end
it 'does not include replies with direct visibility in its replies collection' do
expect(subject['replies']['first']['items']).to_not include(reply_by_account_visibility_direct.uri)
def reply_items
include(reply_by_account_first.uri, reply_by_account_next.uri, reply_by_account_third.uri) # Public self replies
.and(not_include(reply_by_other_first.uri)) # Replies from others
.and(not_include(reply_by_account_visibility_direct.uri)) # Replies with direct visibility
end
end

View File

@ -18,7 +18,7 @@ RSpec.describe TranslateStatusService do
describe '#call' do
before do
translation_service = TranslationService.new
allow(translation_service).to receive(:languages).and_return({ 'en' => ['es'] })
allow(translation_service).to receive(:languages).and_return({ 'en' => ['es', 'es-MX'] })
allow(translation_service).to receive(:translate) do |texts|
texts.map do |text|
TranslationService::Translation.new(
@ -37,6 +37,7 @@ RSpec.describe TranslateStatusService do
.to have_attributes(
content: '<p>Hola</p>',
detected_source_language: 'en',
language: 'es',
provider: 'Dummy',
status: status
)
@ -101,6 +102,16 @@ RSpec.describe TranslateStatusService do
expect(media_attachment.description).to eq 'Hola & :highfive:'
end
end
describe 'target language is regional' do
it 'uses regional variant' do
expect(service.call(status, 'es-MX').language).to eq 'es-MX'
end
it 'uses parent locale for unsupported regional variant' do
expect(service.call(status, 'es-XX').language).to eq 'es'
end
end
end
describe '#source_texts' do

View File

@ -0,0 +1,63 @@
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe 'Admin Invites' do
describe 'Invite interaction' do
let!(:invite) { Fabricate(:invite, expires_at: nil) }
let(:user) { Fabricate(:user, role: UserRole.find_by(name: 'Admin')) }
before { sign_in user }
it 'allows invite listing and creation' do
visit admin_invites_path
expect(page)
.to have_title(I18n.t('admin.invites.title'))
for_invite(invite) do
expect(find('input').value)
.to include(invite.code)
end
select I18n.t('invites.max_uses', count: 10), from: max_use_field
expect { generate_invite }
.to change(Invite, :count).by(1)
expect(user.invites.last)
.to have_attributes(max_uses: 10)
end
it 'allows invite expiration' do
visit admin_invites_path
for_invite(invite) do
expect { expire_invite }
.to change { invite.reload.expired? }.from(false).to(true)
end
end
it 'allows invite deactivation' do
visit admin_invites_path
expect { click_on I18n.t('admin.invites.deactivate_all') }
.to change { Invite.exists?(expires_at: nil) }.from(true).to(false)
end
def for_invite(invite, &block)
within("#invite_#{invite.id}", &block)
end
def expire_invite
click_on I18n.t('invites.delete')
end
def generate_invite
click_on I18n.t('invites.generate')
end
def max_use_field
I18n.t('simple_form.labels.defaults.max_uses')
end
end
end

View File

@ -5,9 +5,50 @@ require 'rails_helper'
RSpec.describe Scheduler::IpCleanupScheduler do
let(:worker) { described_class.new }
describe 'perform' do
it 'runs without error' do
expect { worker.perform }.to_not raise_error
describe '#perform' do
context 'with IP-related data past retention times' do
let!(:future_ip_block) { Fabricate :ip_block, expires_at: 1.week.from_now }
let!(:old_ip_block) { Fabricate :ip_block, expires_at: 1.week.ago }
let!(:session_past_retention) { Fabricate :session_activation, ip: '10.0.0.0', updated_at: 18.months.ago }
let!(:inactive_user) { Fabricate :user, current_sign_in_at: 18.months.ago, sign_up_ip: '10.0.0.0' }
let!(:old_login_activity) { Fabricate :login_activity, created_at: 18.months.ago }
let!(:old_token) { Fabricate :access_token, last_used_at: 18.months.ago, last_used_ip: '10.0.0.0' }
before { stub_const 'Scheduler::IpCleanupScheduler::SESSION_RETENTION_PERIOD', 10.years.to_i.seconds }
it 'deletes the expired block' do
expect { worker.perform }
.to_not raise_error
expect { old_ip_block.reload }
.to raise_error(ActiveRecord::RecordNotFound)
expect { old_login_activity.reload }
.to raise_error(ActiveRecord::RecordNotFound)
expect(session_past_retention.reload.ip)
.to be_nil
expect(inactive_user.reload.sign_up_ip)
.to be_nil
expect(old_token.reload.last_used_ip)
.to be_nil
expect(future_ip_block.reload)
.to be_present
end
end
context 'with old session data' do
let!(:new_activation) { Fabricate :session_activation, updated_at: 1.week.ago }
let!(:old_activation) { Fabricate :session_activation, updated_at: 1.month.ago }
before { stub_const 'Scheduler::IpCleanupScheduler::SESSION_RETENTION_PERIOD', 10.days.to_i.seconds }
it 'clears old sessions' do
expect { worker.perform }
.to_not raise_error
expect { old_activation.reload }
.to raise_error(ActiveRecord::RecordNotFound)
expect(new_activation.reload)
.to be_present
end
end
end
end