Rebase to current 4.3.0-alpha-glitch-soc, update theming files
This commit is contained in:
1 parent
aeb49bcba0
commit
fff7926498
71 files changed
+30937
-2221
No files matched your search
@@ -0,0 +1,77 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe Api::V1::ConversationsController do
|
||||
render_views
|
||||
|
||||
let!(:user) { Fabricate(:user, account_attributes: { username: 'alice' }) }
|
||||
let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) }
|
||||
let(:other) { Fabricate(:user) }
|
||||
|
||||
before do
|
||||
allow(controller).to receive(:doorkeeper_token) { token }
|
||||
end
|
||||
|
||||
describe 'GET #index' do
|
||||
let(:scopes) { 'read:statuses' }
|
||||
|
||||
before do
|
||||
PostStatusService.new.call(other.account, text: 'Hey @alice', visibility: 'direct')
|
||||
PostStatusService.new.call(user.account, text: 'Hey, nobody here', visibility: 'direct')
|
||||
end
|
||||
|
||||
it 'returns pagination headers', :aggregate_failures do
|
||||
get :index, params: { limit: 1 }
|
||||
|
||||
expect(response).to have_http_status(200)
|
||||
expect(response.headers['Link'].links.size).to eq(2)
|
||||
end
|
||||
|
||||
it 'returns conversations', :aggregate_failures do
|
||||
get :index
|
||||
json = body_as_json
|
||||
expect(json.size).to eq 2
|
||||
expect(json[0][:accounts].size).to eq 1
|
||||
<<<<<<< HEAD
|
||||
end
|
||||
|
||||
context 'with since_id' do
|
||||
context 'when requesting old posts' do
|
||||
it 'returns conversations' do
|
||||
get :index, params: { since_id: Mastodon::Snowflake.id_at(1.hour.ago, with_random: false) }
|
||||
json = body_as_json
|
||||
expect(json.size).to eq 2
|
||||
end
|
||||
end
|
||||
|
||||
context 'when requesting posts in the future' do
|
||||
it 'returns no conversation' do
|
||||
get :index, params: { since_id: Mastodon::Snowflake.id_at(1.hour.from_now, with_random: false) }
|
||||
json = body_as_json
|
||||
expect(json.size).to eq 0
|
||||
end
|
||||
end
|
||||
=======
|
||||
>>>>>>> 4c6c790f8 (Fix /api/v1/conversations sometimes returning empty accounts (#25499))
|
||||
end
|
||||
|
||||
context 'with since_id' do
|
||||
context 'when requesting old posts' do
|
||||
it 'returns conversations' do
|
||||
get :index, params: { since_id: Mastodon::Snowflake.id_at(1.hour.ago, with_random: false) }
|
||||
json = body_as_json
|
||||
expect(json.size).to eq 2
|
||||
end
|
||||
end
|
||||
|
||||
context 'when requesting posts in the future' do
|
||||
it 'returns no conversation' do
|
||||
get :index, params: { since_id: Mastodon::Snowflake.id_at(1.hour.from_now, with_random: false) }
|
||||
json = body_as_json
|
||||
expect(json.size).to eq 0
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,108 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
|
||||
describe RelationshipsController do
|
||||
render_views
|
||||
|
||||
let(:user) { Fabricate(:user) }
|
||||
|
||||
describe 'GET #show' do
|
||||
context 'when signed in' do
|
||||
before do
|
||||
sign_in user, scope: :user
|
||||
get :show, params: { page: 2, relationship: 'followed_by' }
|
||||
end
|
||||
|
||||
it 'returns http success' do
|
||||
expect(response).to have_http_status(200)
|
||||
end
|
||||
|
||||
it 'returns private cache control headers' do
|
||||
expect(response.headers['Cache-Control']).to include('private, no-store')
|
||||
end
|
||||
end
|
||||
|
||||
context 'when not signed in' do
|
||||
before do
|
||||
get :show, params: { page: 2, relationship: 'followed_by' }
|
||||
end
|
||||
|
||||
it 'redirects when not signed in' do
|
||||
expect(response).to redirect_to '/auth/sign_in'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'PATCH #update' do
|
||||
let(:alice) { Fabricate(:account, username: 'alice', domain: 'example.com') }
|
||||
|
||||
shared_examples 'redirects back to followers page' do
|
||||
it 'redirects back to followers page' do
|
||||
alice.follow!(user.account)
|
||||
|
||||
sign_in user, scope: :user
|
||||
subject
|
||||
|
||||
expect(response).to redirect_to(relationships_path)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when select parameter is not provided' do
|
||||
subject { patch :update }
|
||||
|
||||
include_examples 'redirects back to followers page'
|
||||
end
|
||||
|
||||
context 'when select parameter is provided' do
|
||||
<<<<<<< HEAD
|
||||
subject { patch :update, params: { form_account_batch: { account_ids: [alice.id] }, remove_domains_from_followers: '' } }
|
||||
=======
|
||||
subject { patch :update, params: { form_account_batch: { account_ids: [poopfeast.id] }, remove_domains_from_followers: '' } }
|
||||
>>>>>>> 0dc342df8 (Fix “Remove all followers from the selected domains” being more destructive than it claims (#23805))
|
||||
|
||||
it 'soft-blocks followers from selected domains' do
|
||||
alice.follow!(user.account)
|
||||
|
||||
sign_in user, scope: :user
|
||||
subject
|
||||
|
||||
expect(alice.following?(user.account)).to be false
|
||||
end
|
||||
|
||||
it 'does not unfollow users from selected domains' do
|
||||
user.account.follow!(alice)
|
||||
|
||||
sign_in user, scope: :user
|
||||
subject
|
||||
|
||||
expect(user.account.following?(alice)).to be true
|
||||
end
|
||||
|
||||
context 'when not signed in' do
|
||||
before do
|
||||
subject
|
||||
end
|
||||
|
||||
it 'redirects when not signed in' do
|
||||
expect(response).to redirect_to '/auth/sign_in'
|
||||
end
|
||||
end
|
||||
|
||||
<<<<<<< HEAD
|
||||
=======
|
||||
it 'does not unfollow users from selected domains' do
|
||||
user.account.follow!(poopfeast)
|
||||
|
||||
sign_in user, scope: :user
|
||||
subject
|
||||
|
||||
expect(user.account.following?(poopfeast)).to be true
|
||||
end
|
||||
|
||||
include_examples 'authenticate user'
|
||||
>>>>>>> 0dc342df8 (Fix “Remove all followers from the selected domains” being more destructive than it claims (#23805))
|
||||
include_examples 'redirects back to followers page'
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,87 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe AccountReachFinder do
|
||||
let(:account) { Fabricate(:account) }
|
||||
|
||||
<<<<<<< HEAD
|
||||
let(:ap_follower_example_com) { Fabricate(:account, protocol: :activitypub, inbox_url: 'https://example.com/inbox-1', domain: 'example.com') }
|
||||
let(:ap_follower_example_org) { Fabricate(:account, protocol: :activitypub, inbox_url: 'https://example.org/inbox-2', domain: 'example.org') }
|
||||
let(:ap_follower_with_shared) { Fabricate(:account, protocol: :activitypub, inbox_url: 'https://foo.bar/users/a/inbox', domain: 'foo.bar', shared_inbox_url: 'https://foo.bar/inbox') }
|
||||
|
||||
let(:ap_mentioned_with_shared) { Fabricate(:account, protocol: :activitypub, inbox_url: 'https://foo.bar/users/b/inbox', domain: 'foo.bar', shared_inbox_url: 'https://foo.bar/inbox') }
|
||||
let(:ap_mentioned_example_com) { Fabricate(:account, protocol: :activitypub, inbox_url: 'https://example.com/inbox-3', domain: 'example.com') }
|
||||
let(:ap_mentioned_example_org) { Fabricate(:account, protocol: :activitypub, inbox_url: 'https://example.org/inbox-4', domain: 'example.org') }
|
||||
|
||||
let(:unrelated_account) { Fabricate(:account, protocol: :activitypub, inbox_url: 'https://example.com/unrelated-inbox', domain: 'example.com') }
|
||||
|
||||
before do
|
||||
ap_follower_example_com.follow!(account)
|
||||
ap_follower_example_org.follow!(account)
|
||||
ap_follower_with_shared.follow!(account)
|
||||
|
||||
Fabricate(:status, account: account).tap do |status|
|
||||
status.mentions << Mention.new(account: ap_follower_example_com)
|
||||
status.mentions << Mention.new(account: ap_mentioned_with_shared)
|
||||
=======
|
||||
let(:follower1) { Fabricate(:account, protocol: :activitypub, inbox_url: 'https://example.com/inbox-1') }
|
||||
let(:follower2) { Fabricate(:account, protocol: :activitypub, inbox_url: 'https://example.com/inbox-2') }
|
||||
let(:follower3) { Fabricate(:account, protocol: :activitypub, inbox_url: 'https://foo.bar/users/a/inbox', shared_inbox_url: 'https://foo.bar/inbox') }
|
||||
|
||||
let(:mentioned1) { Fabricate(:account, protocol: :activitypub, inbox_url: 'https://foo.bar/users/b/inbox', shared_inbox_url: 'https://foo.bar/inbox') }
|
||||
let(:mentioned2) { Fabricate(:account, protocol: :activitypub, inbox_url: 'https://example.com/inbox-3') }
|
||||
let(:mentioned3) { Fabricate(:account, protocol: :activitypub, inbox_url: 'https://example.com/inbox-4') }
|
||||
|
||||
let(:unrelated_account) { Fabricate(:account, protocol: :activitypub, inbox_url: 'https://example.com/unrelated-inbox') }
|
||||
|
||||
before do
|
||||
follower1.follow!(account)
|
||||
follower2.follow!(account)
|
||||
follower3.follow!(account)
|
||||
|
||||
Fabricate(:status, account: account).tap do |status|
|
||||
status.mentions << Mention.new(account: follower1)
|
||||
status.mentions << Mention.new(account: mentioned1)
|
||||
>>>>>>> 99c2bbbec (Change profile updates to be sent to recently-mentioned servers (#24852))
|
||||
end
|
||||
|
||||
Fabricate(:status, account: account)
|
||||
|
||||
Fabricate(:status, account: account).tap do |status|
|
||||
<<<<<<< HEAD
|
||||
status.mentions << Mention.new(account: ap_mentioned_example_com)
|
||||
status.mentions << Mention.new(account: ap_mentioned_example_org)
|
||||
=======
|
||||
status.mentions << Mention.new(account: mentioned2)
|
||||
status.mentions << Mention.new(account: mentioned3)
|
||||
>>>>>>> 99c2bbbec (Change profile updates to be sent to recently-mentioned servers (#24852))
|
||||
end
|
||||
|
||||
Fabricate(:status).tap do |status|
|
||||
status.mentions << Mention.new(account: unrelated_account)
|
||||
end
|
||||
end
|
||||
|
||||
describe '#inboxes' do
|
||||
it 'includes the preferred inbox URL of followers' do
|
||||
<<<<<<< HEAD
|
||||
expect(described_class.new(account).inboxes).to include(*[ap_follower_example_com, ap_follower_example_org, ap_follower_with_shared].map(&: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))
|
||||
=======
|
||||
expect(described_class.new(account).inboxes).to include(*[follower1, follower2, follower3].map(&:preferred_inbox_url))
|
||||
end
|
||||
|
||||
it 'includes the preferred inbox URL of recently-mentioned accounts' do
|
||||
expect(described_class.new(account).inboxes).to include(*[mentioned1, mentioned2, mentioned3].map(&:preferred_inbox_url))
|
||||
>>>>>>> 99c2bbbec (Change profile updates to be sent to recently-mentioned servers (#24852))
|
||||
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)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,319 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe MediaAttachment, paperclip_processing: true do
|
||||
describe 'local?' do
|
||||
subject { media_attachment.local? }
|
||||
|
||||
let(:media_attachment) { described_class.new(remote_url: remote_url) }
|
||||
|
||||
context 'when remote_url is blank' do
|
||||
let(:remote_url) { '' }
|
||||
|
||||
it 'returns true' do
|
||||
expect(subject).to be true
|
||||
end
|
||||
end
|
||||
|
||||
context 'when remote_url is present' do
|
||||
let(:remote_url) { 'remote_url' }
|
||||
|
||||
it 'returns false' do
|
||||
expect(subject).to be false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'needs_redownload?' do
|
||||
subject { media_attachment.needs_redownload? }
|
||||
|
||||
let(:media_attachment) { described_class.new(remote_url: remote_url, file: file) }
|
||||
|
||||
context 'when file is blank' do
|
||||
let(:file) { nil }
|
||||
|
||||
context 'when remote_url is present' do
|
||||
let(:remote_url) { 'remote_url' }
|
||||
|
||||
it 'returns true' do
|
||||
expect(subject).to be true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when file is present' do
|
||||
let(:file) { attachment_fixture('avatar.gif') }
|
||||
|
||||
context 'when remote_url is blank' do
|
||||
let(:remote_url) { '' }
|
||||
|
||||
it 'returns false' do
|
||||
expect(subject).to be false
|
||||
end
|
||||
end
|
||||
|
||||
context 'when remote_url is present' do
|
||||
let(:remote_url) { 'remote_url' }
|
||||
|
||||
it 'returns true' do
|
||||
expect(subject).to be false
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#to_param' do
|
||||
let(:media_attachment) { Fabricate.build(:media_attachment, shortcode: shortcode, id: id) }
|
||||
|
||||
context 'when media attachment has a shortcode' do
|
||||
let(:shortcode) { 'foo' }
|
||||
let(:id) { 123 }
|
||||
|
||||
it 'returns shortcode' do
|
||||
expect(media_attachment.to_param).to eq shortcode
|
||||
end
|
||||
end
|
||||
|
||||
context 'when media attachment does not have a shortcode' do
|
||||
let(:shortcode) { nil }
|
||||
let(:id) { 123 }
|
||||
|
||||
it 'returns string representation of id' do
|
||||
expect(media_attachment.to_param).to eq id.to_s
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
shared_examples 'static 600x400 image' do |content_type, extension|
|
||||
after do
|
||||
media.destroy
|
||||
end
|
||||
|
||||
it 'saves media attachment with correct file metadata' do
|
||||
expect(media.persisted?).to be true
|
||||
expect(media.file).to_not be_nil
|
||||
|
||||
# completes processing
|
||||
expect(media.processing_complete?).to be true
|
||||
|
||||
# sets type
|
||||
expect(media.type).to eq 'image'
|
||||
|
||||
# sets content type
|
||||
expect(media.file_content_type).to eq content_type
|
||||
|
||||
# sets file extension
|
||||
expect(media.file_file_name).to end_with extension
|
||||
|
||||
# Rack::Mime (used by PublicFileServerMiddleware) recognizes file extension
|
||||
expect(Rack::Mime.mime_type(extension, nil)).to eq content_type
|
||||
end
|
||||
|
||||
it 'saves media attachment with correct size metadata' do
|
||||
# strips original file name
|
||||
expect(media.file_file_name).to_not start_with '600x400'
|
||||
|
||||
# sets meta for original
|
||||
expect(media.file.meta['original']['width']).to eq 600
|
||||
expect(media.file.meta['original']['height']).to eq 400
|
||||
expect(media.file.meta['original']['aspect']).to eq 1.5
|
||||
|
||||
# sets meta for thumbnail
|
||||
expect(media.file.meta['small']['width']).to eq 588
|
||||
expect(media.file.meta['small']['height']).to eq 392
|
||||
expect(media.file.meta['small']['aspect']).to eq 1.5
|
||||
end
|
||||
end
|
||||
|
||||
describe 'jpeg' do
|
||||
let(:media) { Fabricate(:media_attachment, file: attachment_fixture('600x400.jpeg')) }
|
||||
|
||||
it_behaves_like 'static 600x400 image', 'image/jpeg', '.jpeg'
|
||||
end
|
||||
|
||||
describe 'png' do
|
||||
let(:media) { Fabricate(:media_attachment, file: attachment_fixture('600x400.png')) }
|
||||
|
||||
it_behaves_like 'static 600x400 image', 'image/png', '.png'
|
||||
end
|
||||
|
||||
describe 'webp' do
|
||||
let(:media) { Fabricate(:media_attachment, file: attachment_fixture('600x400.webp')) }
|
||||
|
||||
it_behaves_like 'static 600x400 image', 'image/webp', '.webp'
|
||||
end
|
||||
|
||||
describe 'avif' do
|
||||
let(:media) { Fabricate(:media_attachment, file: attachment_fixture('600x400.avif')) }
|
||||
|
||||
it_behaves_like 'static 600x400 image', 'image/jpeg', '.jpeg'
|
||||
end
|
||||
|
||||
describe 'heic' do
|
||||
let(:media) { Fabricate(:media_attachment, file: attachment_fixture('600x400.heic')) }
|
||||
|
||||
it_behaves_like 'static 600x400 image', 'image/jpeg', '.jpeg'
|
||||
end
|
||||
|
||||
describe 'base64-encoded image' do
|
||||
let(:base64_attachment) { "data:image/jpeg;base64,#{Base64.encode64(attachment_fixture('600x400.jpeg').read)}" }
|
||||
let(:media) { Fabricate(:media_attachment, file: base64_attachment) }
|
||||
|
||||
it_behaves_like 'static 600x400 image', 'image/jpeg', '.jpeg'
|
||||
end
|
||||
|
||||
describe 'animated gif' do
|
||||
let(:media) { Fabricate(:media_attachment, file: attachment_fixture('avatar.gif')) }
|
||||
|
||||
it 'sets correct file metadata' do
|
||||
expect(media.type).to eq 'gifv'
|
||||
expect(media.file_content_type).to eq 'video/mp4'
|
||||
expect(media.file.meta['original']['width']).to eq 128
|
||||
expect(media.file.meta['original']['height']).to eq 128
|
||||
end
|
||||
end
|
||||
|
||||
describe 'static gif' do
|
||||
fixtures = [
|
||||
{ filename: 'attachment.gif', width: 600, height: 400, aspect: 1.5 },
|
||||
{ filename: 'mini-static.gif', width: 32, height: 32, aspect: 1.0 },
|
||||
]
|
||||
|
||||
fixtures.each do |fixture|
|
||||
context fixture[:filename] do
|
||||
let(:media) { Fabricate(:media_attachment, file: attachment_fixture(fixture[:filename])) }
|
||||
|
||||
it 'sets correct file metadata' do
|
||||
expect(media.type).to eq 'image'
|
||||
expect(media.file_content_type).to eq 'image/gif'
|
||||
expect(media.file.meta['original']['width']).to eq fixture[:width]
|
||||
expect(media.file.meta['original']['height']).to eq fixture[:height]
|
||||
expect(media.file.meta['original']['aspect']).to eq fixture[:aspect]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'ogg with cover art' do
|
||||
let(:media) { Fabricate(:media_attachment, file: attachment_fixture('boop.ogg')) }
|
||||
|
||||
it 'sets correct file metadata' do
|
||||
expect(media.type).to eq 'audio'
|
||||
expect(media.file.meta['original']['duration']).to be_within(0.05).of(0.235102)
|
||||
expect(media.thumbnail.present?).to be true
|
||||
expect(media.file.meta['colors']['background']).to eq '#3088d4'
|
||||
expect(media.file_file_name).to_not eq 'boop.ogg'
|
||||
end
|
||||
end
|
||||
|
||||
describe 'mp3 with large cover art' do
|
||||
let(:media) { Fabricate(:media_attachment, file: attachment_fixture('boop.mp3')) }
|
||||
|
||||
it 'detects it as an audio file' do
|
||||
expect(media.type).to eq 'audio'
|
||||
end
|
||||
|
||||
it 'sets meta for the duration' do
|
||||
expect(media.file.meta['original']['duration']).to be_within(0.05).of(0.235102)
|
||||
end
|
||||
|
||||
it 'extracts thumbnail' do
|
||||
expect(media.thumbnail.present?).to be true
|
||||
end
|
||||
|
||||
it 'gives the file a random name' do
|
||||
<<<<<<< HEAD
|
||||
expect(media.file_file_name).to_not eq 'boop.mp3'
|
||||
=======
|
||||
expect(media.file_file_name).to_not eq 'boop.ogg'
|
||||
end
|
||||
end
|
||||
|
||||
describe 'mp3 with large cover art' do
|
||||
let(:media) { described_class.create(account: Fabricate(:account), file: attachment_fixture('boop.mp3')) }
|
||||
|
||||
it 'detects it as an audio file' do
|
||||
expect(media.type).to eq 'audio'
|
||||
end
|
||||
|
||||
it 'sets meta for the duration' do
|
||||
expect(media.file.meta['original']['duration']).to be_within(0.05).of(0.235102)
|
||||
end
|
||||
|
||||
it 'extracts thumbnail' do
|
||||
expect(media.thumbnail.present?).to be true
|
||||
end
|
||||
|
||||
it 'gives the file a random name' do
|
||||
expect(media.file_file_name).to_not eq 'boop.mp3'
|
||||
end
|
||||
end
|
||||
|
||||
describe 'jpeg' do
|
||||
let(:media) { MediaAttachment.create(account: Fabricate(:account), file: attachment_fixture('attachment.jpg')) }
|
||||
|
||||
it 'sets meta for different style' do
|
||||
expect(media.file.meta["original"]["width"]).to eq 600
|
||||
expect(media.file.meta["original"]["height"]).to eq 400
|
||||
expect(media.file.meta["original"]["aspect"]).to eq 1.5
|
||||
expect(media.file.meta["small"]["width"]).to eq 588
|
||||
expect(media.file.meta["small"]["height"]).to eq 392
|
||||
expect(media.file.meta["small"]["aspect"]).to eq 1.5
|
||||
end
|
||||
|
||||
it 'gives the file a random name' do
|
||||
expect(media.file_file_name).to_not eq 'attachment.jpg'
|
||||
end
|
||||
end
|
||||
|
||||
describe 'base64-encoded jpeg' do
|
||||
let(:base64_attachment) { "data:image/jpeg;base64,#{Base64.encode64(attachment_fixture('attachment.jpg').read)}" }
|
||||
let(:media) { MediaAttachment.create(account: Fabricate(:account), file: base64_attachment) }
|
||||
|
||||
it 'saves media attachment' do
|
||||
expect(media.persisted?).to be true
|
||||
expect(media.file).to_not be_nil
|
||||
end
|
||||
|
||||
it 'gives the file a file name' do
|
||||
expect(media.file_file_name).to_not be_blank
|
||||
>>>>>>> 0aa0b71f2 (Merge pull request from GHSA-9928-3cp5-93fm)
|
||||
end
|
||||
end
|
||||
|
||||
it 'is invalid without file' do
|
||||
media = described_class.new
|
||||
|
||||
expect(media.valid?).to be false
|
||||
expect(media).to model_have_error_on_field(:file)
|
||||
end
|
||||
|
||||
describe 'size limit validation' do
|
||||
it 'rejects video files that are too large' do
|
||||
stub_const 'MediaAttachment::IMAGE_LIMIT', 100.megabytes
|
||||
stub_const 'MediaAttachment::VIDEO_LIMIT', 1.kilobyte
|
||||
expect { Fabricate(:media_attachment, file: attachment_fixture('attachment.webm')) }.to raise_error(ActiveRecord::RecordInvalid)
|
||||
end
|
||||
|
||||
it 'accepts video files that are small enough' do
|
||||
stub_const 'MediaAttachment::IMAGE_LIMIT', 1.kilobyte
|
||||
stub_const 'MediaAttachment::VIDEO_LIMIT', 100.megabytes
|
||||
media = Fabricate(:media_attachment, file: attachment_fixture('attachment.webm'))
|
||||
expect(media.valid?).to be true
|
||||
end
|
||||
|
||||
it 'rejects image files that are too large' do
|
||||
stub_const 'MediaAttachment::IMAGE_LIMIT', 1.kilobyte
|
||||
stub_const 'MediaAttachment::VIDEO_LIMIT', 100.megabytes
|
||||
expect { Fabricate(:media_attachment, file: attachment_fixture('attachment.jpg')) }.to raise_error(ActiveRecord::RecordInvalid)
|
||||
end
|
||||
|
||||
it 'accepts image files that are small enough' do
|
||||
stub_const 'MediaAttachment::IMAGE_LIMIT', 100.megabytes
|
||||
stub_const 'MediaAttachment::VIDEO_LIMIT', 1.kilobyte
|
||||
media = Fabricate(:media_attachment, file: attachment_fixture('attachment.jpg'))
|
||||
expect(media.valid?).to be true
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,273 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe FetchLinkCardService, type: :service do
|
||||
subject { described_class.new }
|
||||
|
||||
let(:html) { '<!doctype html><title>Hello world</title>' }
|
||||
let(:oembed_cache) { nil }
|
||||
|
||||
before do
|
||||
stub_request(:get, 'http://example.com/html').to_return(headers: { 'Content-Type' => 'text/html' }, body: html)
|
||||
stub_request(:get, 'http://example.com/not-found').to_return(status: 404, headers: { 'Content-Type' => 'text/html' }, body: html)
|
||||
stub_request(:get, 'http://example.com/text').to_return(status: 404, headers: { 'Content-Type' => 'text/plain' }, body: 'Hello')
|
||||
stub_request(:get, 'http://example.com/redirect').to_return(status: 302, headers: { 'Location' => 'http://example.com/html' })
|
||||
stub_request(:get, 'http://example.com/redirect-to-404').to_return(status: 302, headers: { 'Location' => 'http://example.com/not-found' })
|
||||
stub_request(:get, 'http://example.com/oembed?url=http://example.com/html').to_return(headers: { 'Content-Type' => 'application/json' }, body: '{ "version": "1.0", "type": "link", "title": "oEmbed title" }')
|
||||
stub_request(:get, 'http://example.com/oembed?format=json&url=http://example.com/html').to_return(headers: { 'Content-Type' => 'application/json' }, body: '{ "version": "1.0", "type": "link", "title": "oEmbed title" }')
|
||||
|
||||
stub_request(:get, 'http://example.xn--fiqs8s')
|
||||
stub_request(:get, 'http://example.com/日本語')
|
||||
stub_request(:get, 'http://example.com/test?data=file.gpx%5E1')
|
||||
stub_request(:get, 'http://example.com/test-')
|
||||
|
||||
stub_request(:get, 'http://example.com/sjis').to_return(request_fixture('sjis.txt'))
|
||||
stub_request(:get, 'http://example.com/sjis_with_wrong_charset').to_return(request_fixture('sjis_with_wrong_charset.txt'))
|
||||
stub_request(:get, 'http://example.com/koi8-r').to_return(request_fixture('koi8-r.txt'))
|
||||
<<<<<<< HEAD
|
||||
=======
|
||||
stub_request(:get, 'http://example.com/日本語').to_return(request_fixture('sjis.txt'))
|
||||
stub_request(:get, 'https://github.com/qbi/WannaCry').to_return(status: 404)
|
||||
stub_request(:get, 'http://example.com/test?data=file.gpx%5E1').to_return(status: 200)
|
||||
stub_request(:get, 'http://example.com/test-').to_return(request_fixture('idn.txt'))
|
||||
>>>>>>> 8eb1bb8ba (Allow carets in URL search params (#25216))
|
||||
stub_request(:get, 'http://example.com/windows-1251').to_return(request_fixture('windows-1251.txt'))
|
||||
|
||||
Rails.cache.write('oembed_endpoint:example.com', oembed_cache) if oembed_cache
|
||||
|
||||
subject.call(status)
|
||||
end
|
||||
|
||||
context 'with a local status' do
|
||||
context 'with URL of a regular HTML page' do
|
||||
let(:status) { Fabricate(:status, text: 'http://example.com/html') }
|
||||
|
||||
it 'creates preview card' do
|
||||
expect(status.preview_card).to_not be_nil
|
||||
expect(status.preview_card.url).to eq 'http://example.com/html'
|
||||
expect(status.preview_card.title).to eq 'Hello world'
|
||||
end
|
||||
end
|
||||
|
||||
context 'with URL of a page with no title' do
|
||||
let(:status) { Fabricate(:status, text: 'http://example.com/html') }
|
||||
let(:html) { '<!doctype html><title></title>' }
|
||||
|
||||
it 'does not create a preview card' do
|
||||
expect(status.preview_card).to be_nil
|
||||
end
|
||||
end
|
||||
|
||||
context 'with a URL of a plain-text page' do
|
||||
let(:status) { Fabricate(:status, text: 'http://example.com/text') }
|
||||
|
||||
it 'does not create a preview card' do
|
||||
expect(status.preview_card).to be_nil
|
||||
end
|
||||
end
|
||||
|
||||
context 'with multiple URLs' do
|
||||
let(:status) { Fabricate(:status, text: 'ftp://example.com http://example.com/html http://example.com/text') }
|
||||
|
||||
it 'fetches the first valid URL' do
|
||||
expect(a_request(:get, 'http://example.com/html')).to have_been_made
|
||||
end
|
||||
|
||||
it 'does not fetch the second valid URL' do
|
||||
expect(a_request(:get, 'http://example.com/text/')).to_not have_been_made
|
||||
end
|
||||
end
|
||||
|
||||
context 'with a redirect URL' do
|
||||
let(:status) { Fabricate(:status, text: 'http://example.com/redirect') }
|
||||
|
||||
it 'follows redirect' do
|
||||
expect(a_request(:get, 'http://example.com/redirect')).to have_been_made.once
|
||||
expect(a_request(:get, 'http://example.com/html')).to have_been_made.once
|
||||
end
|
||||
|
||||
it 'creates preview card' do
|
||||
expect(status.preview_card).to_not be_nil
|
||||
expect(status.preview_card.url).to eq 'http://example.com/html'
|
||||
expect(status.preview_card.title).to eq 'Hello world'
|
||||
end
|
||||
end
|
||||
|
||||
context 'with a broken redirect URL' do
|
||||
let(:status) { Fabricate(:status, text: 'http://example.com/redirect-to-404') }
|
||||
|
||||
it 'follows redirect' do
|
||||
expect(a_request(:get, 'http://example.com/redirect-to-404')).to have_been_made.once
|
||||
expect(a_request(:get, 'http://example.com/not-found')).to have_been_made.once
|
||||
end
|
||||
|
||||
it 'does not create a preview card' do
|
||||
expect(status.preview_card).to be_nil
|
||||
end
|
||||
end
|
||||
|
||||
context 'with a 404 URL' do
|
||||
let(:status) { Fabricate(:status, text: 'http://example.com/not-found') }
|
||||
|
||||
it 'does not create a preview card' do
|
||||
expect(status.preview_card).to be_nil
|
||||
end
|
||||
end
|
||||
|
||||
context 'with an IDN URL' do
|
||||
let(:status) { Fabricate(:status, text: 'Check out http://example.中国') }
|
||||
|
||||
it 'fetches the URL' do
|
||||
expect(a_request(:get, 'http://example.xn--fiqs8s/')).to have_been_made.once
|
||||
end
|
||||
end
|
||||
|
||||
context 'with a URL of a page in Shift JIS encoding' do
|
||||
let(:status) { Fabricate(:status, text: 'Check out http://example.com/sjis') }
|
||||
|
||||
it 'decodes the HTML' do
|
||||
expect(status.preview_cards.first.title).to eq('SJISのページ')
|
||||
end
|
||||
end
|
||||
|
||||
context 'with a URL of a page in Shift JIS encoding labeled as UTF-8' do
|
||||
let(:status) { Fabricate(:status, text: 'Check out http://example.com/sjis_with_wrong_charset') }
|
||||
|
||||
it 'decodes the HTML despite the wrong charset header' do
|
||||
expect(status.preview_cards.first.title).to eq('SJISのページ')
|
||||
end
|
||||
end
|
||||
|
||||
context 'with a URL of a page in KOI8-R encoding' do
|
||||
let(:status) { Fabricate(:status, text: 'Check out http://example.com/koi8-r') }
|
||||
|
||||
it 'decodes the HTML' do
|
||||
expect(status.preview_cards.first.title).to eq('Московя начинаетъ только въ XVI ст. привлекать внимане иностранцевъ.')
|
||||
end
|
||||
end
|
||||
|
||||
context 'with a URL of a page in Windows-1251 encoding' do
|
||||
let(:status) { Fabricate(:status, text: 'Check out http://example.com/windows-1251') }
|
||||
|
||||
it 'decodes the HTML' do
|
||||
expect(status.preview_cards.first.title).to eq('сэмпл текст')
|
||||
end
|
||||
end
|
||||
|
||||
context 'with a Japanese path URL' do
|
||||
let(:status) { Fabricate(:status, text: 'テストhttp://example.com/日本語') }
|
||||
|
||||
it 'fetches the URL' do
|
||||
expect(a_request(:get, 'http://example.com/日本語')).to have_been_made.once
|
||||
end
|
||||
end
|
||||
|
||||
context 'with a hyphen-suffixed URL' do
|
||||
let(:status) { Fabricate(:status, text: 'test http://example.com/test-') }
|
||||
|
||||
it 'fetches the URL' do
|
||||
expect(a_request(:get, 'http://example.com/test-')).to have_been_made.once
|
||||
end
|
||||
end
|
||||
|
||||
context 'with a caret-suffixed URL' do
|
||||
let(:status) { Fabricate(:status, text: 'test http://example.com/test?data=file.gpx^1') }
|
||||
|
||||
it 'fetches the URL' do
|
||||
expect(a_request(:get, 'http://example.com/test?data=file.gpx%5E1')).to have_been_made.once
|
||||
end
|
||||
|
||||
it 'does not strip the caret before fetching' do
|
||||
expect(a_request(:get, 'http://example.com/test?data=file.gpx')).to_not have_been_made
|
||||
end
|
||||
end
|
||||
|
||||
context 'with a non-isolated URL' do
|
||||
let(:status) { Fabricate(:status, text: 'testhttp://example.com/sjis') }
|
||||
|
||||
it 'does not fetch URLs not isolated from their surroundings' do
|
||||
expect(a_request(:get, 'http://example.com/sjis')).to_not have_been_made
|
||||
end
|
||||
end
|
||||
|
||||
<<<<<<< HEAD
|
||||
context 'with a URL of a page with oEmbed support' do
|
||||
let(:html) { '<!doctype html><title>Hello world</title><link rel="alternate" type="application/json+oembed" href="http://example.com/oembed?url=http://example.com/html">' }
|
||||
let(:status) { Fabricate(:status, text: 'http://example.com/html') }
|
||||
|
||||
it 'fetches the oEmbed URL' do
|
||||
expect(a_request(:get, 'http://example.com/oembed?url=http://example.com/html')).to have_been_made.once
|
||||
end
|
||||
|
||||
it 'creates preview card' do
|
||||
expect(status.preview_card).to_not be_nil
|
||||
expect(status.preview_card.url).to eq 'http://example.com/html'
|
||||
expect(status.preview_card.title).to eq 'oEmbed title'
|
||||
end
|
||||
|
||||
context 'when oEmbed endpoint cache populated' do
|
||||
let(:oembed_cache) { { endpoint: 'http://example.com/oembed?format=json&url={url}', format: :json } }
|
||||
|
||||
it 'uses the cached oEmbed response' do
|
||||
expect(a_request(:get, 'http://example.com/oembed?url=http://example.com/html')).to_not have_been_made
|
||||
expect(a_request(:get, 'http://example.com/oembed?format=json&url=http://example.com/html')).to have_been_made
|
||||
end
|
||||
|
||||
it 'creates preview card' do
|
||||
expect(status.preview_card).to_not be_nil
|
||||
expect(status.preview_card.url).to eq 'http://example.com/html'
|
||||
expect(status.preview_card.title).to eq 'oEmbed title'
|
||||
end
|
||||
end
|
||||
|
||||
# If the original HTML URL for whatever reason (e.g. DOS protection) redirects to
|
||||
# an error page, we can still use the cached oEmbed but should not use the
|
||||
# redirect URL on the card.
|
||||
context 'when oEmbed endpoint cache populated but page returns 404' do
|
||||
let(:status) { Fabricate(:status, text: 'http://example.com/redirect-to-404') }
|
||||
let(:oembed_cache) { { endpoint: 'http://example.com/oembed?url=http://example.com/html', format: :json } }
|
||||
|
||||
it 'uses the cached oEmbed response' do
|
||||
expect(a_request(:get, 'http://example.com/oembed?url=http://example.com/html')).to have_been_made
|
||||
end
|
||||
|
||||
it 'creates preview card' do
|
||||
expect(status.preview_card).to_not be_nil
|
||||
expect(status.preview_card.title).to eq 'oEmbed title'
|
||||
end
|
||||
|
||||
it 'uses the original URL' do
|
||||
expect(status.preview_card&.url).to eq 'http://example.com/redirect-to-404'
|
||||
end
|
||||
=======
|
||||
context do
|
||||
let(:status) { Fabricate(:status, text: 'test http://example.com/test?data=file.gpx^1') }
|
||||
|
||||
it 'does fetch URLs with a caret in search params' do
|
||||
expect(a_request(:get, 'http://example.com/test?data=file.gpx')).to_not have_been_made
|
||||
expect(a_request(:get, 'http://example.com/test?data=file.gpx%5E1')).to have_been_made.once
|
||||
>>>>>>> 8eb1bb8ba (Allow carets in URL search params (#25216))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'with a remote status' do
|
||||
let(:status) do
|
||||
Fabricate(:status, account: Fabricate(:account, domain: 'example.com'), text: <<-TEXT)
|
||||
Habt ihr ein paar gute Links zu <a>foo</a>
|
||||
#<span class="tag"><a href="https://quitter.se/tag/wannacry" target="_blank" rel="tag noopener noreferrer" title="https://quitter.se/tag/wannacry">Wannacry</a></span> herumfliegen?
|
||||
Ich will mal unter <br> <a href="http://example.com/not-found" target="_blank" rel="noopener noreferrer" title="http://example.com/not-found">http://example.com/not-found</a> was sammeln. !
|
||||
<a href="http://sn.jonkman.ca/group/416/id" target="_blank" rel="noopener noreferrer" title="http://sn.jonkman.ca/group/416/id">security</a>
|
||||
TEXT
|
||||
end
|
||||
|
||||
it 'parses out URLs' do
|
||||
expect(a_request(:get, 'http://example.com/not-found')).to have_been_made.once
|
||||
end
|
||||
|
||||
it 'ignores URLs to hashtags' do
|
||||
expect(a_request(:get, 'https://quitter.se/tag/wannacry')).to_not have_been_made
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,225 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
|
||||
describe Scheduler::AccountsStatusesCleanupScheduler do
|
||||
subject { described_class.new }
|
||||
|
||||
<<<<<<< HEAD
|
||||
let!(:account_alice) { Fabricate(:account, domain: nil, username: 'alice') }
|
||||
let!(:account_bob) { Fabricate(:account, domain: nil, username: 'bob') }
|
||||
let!(:account_chris) { Fabricate(:account, domain: nil, username: 'chris') }
|
||||
let!(:account_dave) { Fabricate(:account, domain: nil, username: 'dave') }
|
||||
let!(:account_erin) { Fabricate(:account, domain: nil, username: 'erin') }
|
||||
let!(:remote) { Fabricate(:account) }
|
||||
=======
|
||||
let!(:account1) { Fabricate(:account, domain: nil) }
|
||||
let!(:account2) { Fabricate(:account, domain: nil) }
|
||||
let!(:account3) { Fabricate(:account, domain: nil) }
|
||||
let!(:account4) { Fabricate(:account, domain: nil) }
|
||||
let!(:account5) { Fabricate(:account, domain: nil) }
|
||||
let!(:remote) { Fabricate(:account) }
|
||||
|
||||
let!(:policy1) { Fabricate(:account_statuses_cleanup_policy, account: account1) }
|
||||
let!(:policy2) { Fabricate(:account_statuses_cleanup_policy, account: account3) }
|
||||
let!(:policy3) { Fabricate(:account_statuses_cleanup_policy, account: account4, enabled: false) }
|
||||
let!(:policy4) { Fabricate(:account_statuses_cleanup_policy, account: account5) }
|
||||
>>>>>>> d9e45f2fa (Fix AccountsStatusesCleanupScheduler not spreading deletes across accounts correctly (#24607))
|
||||
|
||||
let(:queue_size) { 0 }
|
||||
let(:queue_latency) { 0 }
|
||||
let(:process_set_stub) do
|
||||
[
|
||||
{
|
||||
'concurrency' => 2,
|
||||
'queues' => %w(push default),
|
||||
},
|
||||
]
|
||||
end
|
||||
|
||||
before do
|
||||
queue_stub = instance_double(Sidekiq::Queue, size: queue_size, latency: queue_latency)
|
||||
allow(Sidekiq::Queue).to receive(:new).and_return(queue_stub)
|
||||
allow(Sidekiq::ProcessSet).to receive(:new).and_return(process_set_stub)
|
||||
|
||||
sidekiq_stats_stub = instance_double(Sidekiq::Stats)
|
||||
allow(Sidekiq::Stats).to receive(:new).and_return(sidekiq_stats_stub)
|
||||
<<<<<<< HEAD
|
||||
=======
|
||||
|
||||
# Create a bunch of old statuses
|
||||
10.times do
|
||||
Fabricate(:status, account: account1, created_at: 3.years.ago)
|
||||
Fabricate(:status, account: account2, created_at: 3.years.ago)
|
||||
Fabricate(:status, account: account3, created_at: 3.years.ago)
|
||||
Fabricate(:status, account: account4, created_at: 3.years.ago)
|
||||
Fabricate(:status, account: account5, created_at: 3.years.ago)
|
||||
Fabricate(:status, account: remote, created_at: 3.years.ago)
|
||||
end
|
||||
|
||||
# Create a bunch of newer statuses
|
||||
5.times do
|
||||
Fabricate(:status, account: account1, created_at: 3.minutes.ago)
|
||||
Fabricate(:status, account: account2, created_at: 3.minutes.ago)
|
||||
Fabricate(:status, account: account3, created_at: 3.minutes.ago)
|
||||
Fabricate(:status, account: account4, created_at: 3.minutes.ago)
|
||||
Fabricate(:status, account: remote, created_at: 3.minutes.ago)
|
||||
end
|
||||
>>>>>>> d9e45f2fa (Fix AccountsStatusesCleanupScheduler not spreading deletes across accounts correctly (#24607))
|
||||
end
|
||||
|
||||
describe '#under_load?' do
|
||||
context 'when nothing is queued' do
|
||||
it 'returns false' do
|
||||
expect(subject.under_load?).to be false
|
||||
end
|
||||
end
|
||||
|
||||
context 'when numerous jobs are queued' do
|
||||
let(:queue_size) { 5 }
|
||||
let(:queue_latency) { 120 }
|
||||
|
||||
it 'returns true' do
|
||||
expect(subject.under_load?).to be true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#compute_budget' do
|
||||
context 'with a single thread' do
|
||||
let(:process_set_stub) { [{ 'concurrency' => 1, 'queues' => %w(push default) }] }
|
||||
|
||||
it 'returns a low value' do
|
||||
expect(subject.compute_budget).to be < 10
|
||||
end
|
||||
end
|
||||
|
||||
context 'with a lot of threads' do
|
||||
let(:process_set_stub) do
|
||||
[
|
||||
{ 'concurrency' => 2, 'queues' => %w(push default) },
|
||||
{ 'concurrency' => 2, 'queues' => ['push'] },
|
||||
{ 'concurrency' => 2, 'queues' => ['push'] },
|
||||
{ 'concurrency' => 2, 'queues' => ['push'] },
|
||||
]
|
||||
end
|
||||
|
||||
it 'returns a larger value' do
|
||||
expect(subject.compute_budget).to be > 10
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#perform' do
|
||||
around do |example|
|
||||
Timeout.timeout(30) do
|
||||
example.run
|
||||
end
|
||||
end
|
||||
|
||||
before do
|
||||
# Policies for the accounts
|
||||
Fabricate(:account_statuses_cleanup_policy, account: account_alice)
|
||||
Fabricate(:account_statuses_cleanup_policy, account: account_chris)
|
||||
Fabricate(:account_statuses_cleanup_policy, account: account_dave, enabled: false)
|
||||
Fabricate(:account_statuses_cleanup_policy, account: account_erin)
|
||||
|
||||
# Create a bunch of old statuses
|
||||
4.times do
|
||||
Fabricate(:status, account: account_alice, created_at: 3.years.ago)
|
||||
Fabricate(:status, account: account_bob, created_at: 3.years.ago)
|
||||
Fabricate(:status, account: account_chris, created_at: 3.years.ago)
|
||||
Fabricate(:status, account: account_dave, created_at: 3.years.ago)
|
||||
Fabricate(:status, account: account_erin, created_at: 3.years.ago)
|
||||
Fabricate(:status, account: remote, created_at: 3.years.ago)
|
||||
end
|
||||
|
||||
# Create a bunch of newer statuses
|
||||
Fabricate(:status, account: account_alice, created_at: 3.minutes.ago)
|
||||
Fabricate(:status, account: account_bob, created_at: 3.minutes.ago)
|
||||
Fabricate(:status, account: account_chris, created_at: 3.minutes.ago)
|
||||
Fabricate(:status, account: account_dave, created_at: 3.minutes.ago)
|
||||
Fabricate(:status, account: remote, created_at: 3.minutes.ago)
|
||||
end
|
||||
|
||||
context 'when the budget is lower than the number of toots to delete' do
|
||||
it 'deletes the appropriate statuses' do
|
||||
expect(Status.count).to be > (subject.compute_budget) # Data check
|
||||
|
||||
expect { subject.perform }
|
||||
.to change(Status, :count).by(-subject.compute_budget) # Cleanable statuses
|
||||
.and (not_change { account_bob.statuses.count }) # No cleanup policy for account
|
||||
.and(not_change { account_dave.statuses.count }) # Disabled cleanup policy
|
||||
end
|
||||
|
||||
it 'eventually deletes every deletable toot given enough runs' do
|
||||
stub_const 'Scheduler::AccountsStatusesCleanupScheduler::MAX_BUDGET', 4
|
||||
|
||||
expect { 3.times { subject.perform } }.to change(Status, :count).by(-cleanable_statuses_count)
|
||||
end
|
||||
|
||||
it 'correctly round-trips between users across several runs' do
|
||||
stub_const 'Scheduler::AccountsStatusesCleanupScheduler::MAX_BUDGET', 3
|
||||
stub_const 'Scheduler::AccountsStatusesCleanupScheduler::PER_ACCOUNT_BUDGET', 2
|
||||
|
||||
expect { 3.times { subject.perform } }
|
||||
.to change(Status, :count).by(-3 * 3)
|
||||
.and change { account_alice.statuses.count }
|
||||
.and change { account_chris.statuses.count }
|
||||
.and(change { account_erin.statuses.count })
|
||||
end
|
||||
|
||||
<<<<<<< HEAD
|
||||
context 'when given a big budget' do
|
||||
let(:process_set_stub) { [{ 'concurrency' => 400, 'queues' => %w(push default) }] }
|
||||
|
||||
before do
|
||||
stub_const 'Scheduler::AccountsStatusesCleanupScheduler::MAX_BUDGET', 400
|
||||
end
|
||||
|
||||
it 'correctly handles looping in a single run' do
|
||||
expect(subject.compute_budget).to eq(400)
|
||||
expect { subject.perform }.to change(Status, :count).by(-cleanable_statuses_count)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when there is no work to be done' do
|
||||
let(:process_set_stub) { [{ 'concurrency' => 400, 'queues' => %w(push default) }] }
|
||||
|
||||
before do
|
||||
stub_const 'Scheduler::AccountsStatusesCleanupScheduler::MAX_BUDGET', 400
|
||||
subject.perform
|
||||
end
|
||||
|
||||
it 'does not get stuck' do
|
||||
expect(subject.compute_budget).to eq(400)
|
||||
expect { subject.perform }.to_not change(Status, :count)
|
||||
end
|
||||
end
|
||||
|
||||
def cleanable_statuses_count
|
||||
Status
|
||||
.where(account_id: [account_alice, account_chris, account_erin]) # Accounts with enabled policies
|
||||
.where('created_at < ?', 2.weeks.ago) # Policy defaults is 2.weeks
|
||||
.count
|
||||
=======
|
||||
it 'eventually deletes every deletable toot given enough runs' do
|
||||
stub_const 'Scheduler::AccountsStatusesCleanupScheduler::MAX_BUDGET', 4
|
||||
|
||||
expect { 10.times { subject.perform } }.to change { Status.count }.by(-30)
|
||||
end
|
||||
|
||||
it 'correctly round-trips between users across several runs' do
|
||||
stub_const 'Scheduler::AccountsStatusesCleanupScheduler::MAX_BUDGET', 3
|
||||
stub_const 'Scheduler::AccountsStatusesCleanupScheduler::PER_ACCOUNT_BUDGET', 2
|
||||
|
||||
expect { 3.times { subject.perform } }
|
||||
.to change { Status.count }.by(-3 * 3)
|
||||
.and change { account1.statuses.count }
|
||||
.and change { account3.statuses.count }
|
||||
.and change { account5.statuses.count }
|
||||
>>>>>>> d9e45f2fa (Fix AccountsStatusesCleanupScheduler not spreading deletes across accounts correctly (#24607))
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
Reference in new issue
Block a user