From 155fb8414150e78b4e61aa33d483cc7713161134 Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Thu, 16 Nov 2023 09:36:59 -0500 Subject: [PATCH] Improve spec coverage for collection of `workers/` classes (#27874) --- .../account_deletion_request_fabricator.rb | 5 ++ spec/fabricators/import_fabricator.rb | 7 +++ spec/workers/account_refresh_worker_spec.rb | 52 ++++++++++++++++ .../activitypub/post_upgrade_worker_spec.rb | 18 ++++++ ...ze_featured_tags_collection_worker_spec.rb | 29 +++++++++ spec/workers/admin/suspension_worker_spec.rb | 28 +++++++++ .../after_account_domain_block_worker_spec.rb | 29 +++++++++ spec/workers/backup_worker_spec.rb | 36 +++++++++++ spec/workers/delete_mute_worker_spec.rb | 42 +++++++++++++ spec/workers/feed_insert_worker_spec.rb | 21 ++++++- spec/workers/import_worker_spec.rb | 23 +++++++ .../workers/post_process_media_worker_spec.rb | 34 +++++++++-- ...blish_announcement_reaction_worker_spec.rb | 38 ++++++++++++ spec/workers/removal_worker_spec.rb | 28 +++++++++ .../scheduler/self_destruct_scheduler_spec.rb | 60 +++++++++++++++++++ spec/workers/webhooks/delivery_worker_spec.rb | 18 +++++- 16 files changed, 460 insertions(+), 8 deletions(-) create mode 100644 spec/fabricators/account_deletion_request_fabricator.rb create mode 100644 spec/fabricators/import_fabricator.rb create mode 100644 spec/workers/account_refresh_worker_spec.rb create mode 100644 spec/workers/activitypub/post_upgrade_worker_spec.rb create mode 100644 spec/workers/activitypub/synchronize_featured_tags_collection_worker_spec.rb create mode 100644 spec/workers/admin/suspension_worker_spec.rb create mode 100644 spec/workers/after_account_domain_block_worker_spec.rb create mode 100644 spec/workers/backup_worker_spec.rb create mode 100644 spec/workers/delete_mute_worker_spec.rb create mode 100644 spec/workers/import_worker_spec.rb create mode 100644 spec/workers/publish_announcement_reaction_worker_spec.rb create mode 100644 spec/workers/removal_worker_spec.rb create mode 100644 spec/workers/scheduler/self_destruct_scheduler_spec.rb diff --git a/spec/fabricators/account_deletion_request_fabricator.rb b/spec/fabricators/account_deletion_request_fabricator.rb new file mode 100644 index 0000000000..3d3d373988 --- /dev/null +++ b/spec/fabricators/account_deletion_request_fabricator.rb @@ -0,0 +1,5 @@ +# frozen_string_literal: true + +Fabricator(:account_deletion_request) do + account +end diff --git a/spec/fabricators/import_fabricator.rb b/spec/fabricators/import_fabricator.rb new file mode 100644 index 0000000000..4951bb9a4d --- /dev/null +++ b/spec/fabricators/import_fabricator.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +Fabricator(:import) do + account + type :following + data { attachment_fixture('imports.txt') } +end diff --git a/spec/workers/account_refresh_worker_spec.rb b/spec/workers/account_refresh_worker_spec.rb new file mode 100644 index 0000000000..361d69aa0a --- /dev/null +++ b/spec/workers/account_refresh_worker_spec.rb @@ -0,0 +1,52 @@ +# frozen_string_literal: true + +require 'rails_helper' + +describe AccountRefreshWorker do + let(:worker) { described_class.new } + let(:service) { instance_double(ResolveAccountService, call: true) } + + describe '#perform' do + before do + allow(ResolveAccountService).to receive(:new).and_return(service) + end + + context 'when account does not exist' do + it 'returns immediately without processing' do + worker.perform(123_123_123) + + expect(service).to_not have_received(:call) + end + end + + context 'when account exists' do + context 'when account does not need refreshing' do + let(:account) { Fabricate(:account, last_webfingered_at: recent_webfinger_at) } + + it 'returns immediately without processing' do + worker.perform(account.id) + + expect(service).to_not have_received(:call) + end + end + + context 'when account needs refreshing' do + let(:account) { Fabricate(:account, last_webfingered_at: outdated_webfinger_at) } + + it 'schedules an account update' do + worker.perform(account.id) + + expect(service).to have_received(:call) + end + end + + def recent_webfinger_at + (Account::BACKGROUND_REFRESH_INTERVAL - 3.days).ago + end + + def outdated_webfinger_at + (Account::BACKGROUND_REFRESH_INTERVAL + 3.days).ago + end + end + end +end diff --git a/spec/workers/activitypub/post_upgrade_worker_spec.rb b/spec/workers/activitypub/post_upgrade_worker_spec.rb new file mode 100644 index 0000000000..08de150ad9 --- /dev/null +++ b/spec/workers/activitypub/post_upgrade_worker_spec.rb @@ -0,0 +1,18 @@ +# frozen_string_literal: true + +require 'rails_helper' + +describe ActivityPub::PostUpgradeWorker do + let(:worker) { described_class.new } + + describe '#perform' do + let(:domain) { 'host.example' } + + it 'updates relevant values' do + account = Fabricate(:account, domain: domain, last_webfingered_at: 1.day.ago, protocol: :ostatus) + worker.perform(domain) + + expect(account.reload.last_webfingered_at).to be_nil + end + end +end diff --git a/spec/workers/activitypub/synchronize_featured_tags_collection_worker_spec.rb b/spec/workers/activitypub/synchronize_featured_tags_collection_worker_spec.rb new file mode 100644 index 0000000000..8cf13cb900 --- /dev/null +++ b/spec/workers/activitypub/synchronize_featured_tags_collection_worker_spec.rb @@ -0,0 +1,29 @@ +# frozen_string_literal: true + +require 'rails_helper' + +describe ActivityPub::SynchronizeFeaturedTagsCollectionWorker do + let(:worker) { described_class.new } + let(:service) { instance_double(ActivityPub::FetchFeaturedTagsCollectionService, call: true) } + + describe '#perform' do + before do + allow(ActivityPub::FetchFeaturedTagsCollectionService).to receive(:new).and_return(service) + end + + let(:account) { Fabricate(:account) } + let(:url) { 'https://host.example' } + + it 'sends the account and url to the service' do + worker.perform(account.id, url) + + expect(service).to have_received(:call).with(account, url) + end + + it 'returns true for non-existent record' do + result = worker.perform(123_123_123, url) + + expect(result).to be(true) + end + end +end diff --git a/spec/workers/admin/suspension_worker_spec.rb b/spec/workers/admin/suspension_worker_spec.rb new file mode 100644 index 0000000000..da12037edc --- /dev/null +++ b/spec/workers/admin/suspension_worker_spec.rb @@ -0,0 +1,28 @@ +# frozen_string_literal: true + +require 'rails_helper' + +describe Admin::SuspensionWorker do + let(:worker) { described_class.new } + let(:service) { instance_double(SuspendAccountService, call: true) } + + describe '#perform' do + before do + allow(SuspendAccountService).to receive(:new).and_return(service) + end + + let(:account) { Fabricate(:account) } + + it 'sends the account to the service' do + worker.perform(account.id) + + expect(service).to have_received(:call).with(account) + end + + it 'returns true for non-existent record' do + result = worker.perform(123_123_123) + + expect(result).to be(true) + end + end +end diff --git a/spec/workers/after_account_domain_block_worker_spec.rb b/spec/workers/after_account_domain_block_worker_spec.rb new file mode 100644 index 0000000000..54a113a2b3 --- /dev/null +++ b/spec/workers/after_account_domain_block_worker_spec.rb @@ -0,0 +1,29 @@ +# frozen_string_literal: true + +require 'rails_helper' + +describe AfterAccountDomainBlockWorker do + let(:worker) { described_class.new } + let(:service) { instance_double(AfterBlockDomainFromAccountService, call: true) } + + describe '#perform' do + before do + allow(AfterBlockDomainFromAccountService).to receive(:new).and_return(service) + end + + let(:account) { Fabricate(:account) } + let(:domain) { 'host.example' } + + it 'sends the account and domain to the service' do + worker.perform(account.id, domain) + + expect(service).to have_received(:call).with(account, domain) + end + + it 'returns true for non-existent record' do + result = worker.perform(123_123_123, domain) + + expect(result).to be(true) + end + end +end diff --git a/spec/workers/backup_worker_spec.rb b/spec/workers/backup_worker_spec.rb new file mode 100644 index 0000000000..1a169513e9 --- /dev/null +++ b/spec/workers/backup_worker_spec.rb @@ -0,0 +1,36 @@ +# frozen_string_literal: true + +require 'rails_helper' + +describe BackupWorker do + let(:worker) { described_class.new } + let(:service) { instance_double(BackupService, call: true) } + + describe '#perform' do + before do + allow(BackupService).to receive(:new).and_return(service) + end + + let(:backup) { Fabricate(:backup) } + let!(:other_backup) { Fabricate(:backup, user: backup.user) } + + it 'sends the backup to the service and removes other backups' do + expect do + worker.perform(backup.id) + end.to change(UserMailer.deliveries, :size).by(1) + + expect(service).to have_received(:call).with(backup) + expect { other_backup.reload }.to raise_error(ActiveRecord::RecordNotFound) + end + + context 'when sidekiq retries are exhausted' do + it 'destroys the backup' do + described_class.within_sidekiq_retries_exhausted_block({ 'args' => [backup.id] }) do + worker.perform(backup.id) + end + + expect { backup.reload }.to raise_error(ActiveRecord::RecordNotFound) + end + end + end +end diff --git a/spec/workers/delete_mute_worker_spec.rb b/spec/workers/delete_mute_worker_spec.rb new file mode 100644 index 0000000000..1fc84491c3 --- /dev/null +++ b/spec/workers/delete_mute_worker_spec.rb @@ -0,0 +1,42 @@ +# frozen_string_literal: true + +require 'rails_helper' + +describe DeleteMuteWorker do + let(:worker) { described_class.new } + let(:service) { instance_double(UnmuteService, call: true) } + + describe '#perform' do + before do + allow(UnmuteService).to receive(:new).and_return(service) + end + + context 'with an expired mute' do + let(:mute) { Fabricate(:mute, expires_at: 1.day.ago) } + + it 'sends the mute to the service' do + worker.perform(mute.id) + + expect(service).to have_received(:call).with(mute.account, mute.target_account) + end + end + + context 'with an unexpired mute' do + let(:mute) { Fabricate(:mute, expires_at: 1.day.from_now) } + + it 'does not send the mute to the service' do + worker.perform(mute.id) + + expect(service).to_not have_received(:call) + end + end + + context 'with a non-existent mute' do + it 'does not send the mute to the service' do + worker.perform(123_123_123) + + expect(service).to_not have_received(:call) + end + end + end +end diff --git a/spec/workers/feed_insert_worker_spec.rb b/spec/workers/feed_insert_worker_spec.rb index 97c73c5999..e9484879ff 100644 --- a/spec/workers/feed_insert_worker_spec.rb +++ b/spec/workers/feed_insert_worker_spec.rb @@ -8,6 +8,7 @@ describe FeedInsertWorker do describe 'perform' do let(:follower) { Fabricate(:account) } let(:status) { Fabricate(:status) } + let(:list) { Fabricate(:list) } context 'when there are no records' do it 'skips push with missing status' do @@ -42,11 +43,29 @@ describe FeedInsertWorker do it 'pushes the status onto the home timeline without filter' do instance = instance_double(FeedManager, push_to_home: nil, filter?: false) allow(FeedManager).to receive(:instance).and_return(instance) - result = subject.perform(status.id, follower.id) + result = subject.perform(status.id, follower.id, :home) expect(result).to be_nil expect(instance).to have_received(:push_to_home).with(follower, status, update: nil) end + + it 'pushes the status onto the tags timeline without filter' do + instance = instance_double(FeedManager, push_to_home: nil, filter?: false) + allow(FeedManager).to receive(:instance).and_return(instance) + result = subject.perform(status.id, follower.id, :tags) + + expect(result).to be_nil + expect(instance).to have_received(:push_to_home).with(follower, status, update: nil) + end + + it 'pushes the status onto the list timeline without filter' do + instance = instance_double(FeedManager, push_to_list: nil, filter?: false) + allow(FeedManager).to receive(:instance).and_return(instance) + result = subject.perform(status.id, list.id, :list) + + expect(result).to be_nil + expect(instance).to have_received(:push_to_list).with(list, status, update: nil) + end end end end diff --git a/spec/workers/import_worker_spec.rb b/spec/workers/import_worker_spec.rb new file mode 100644 index 0000000000..4095a5d354 --- /dev/null +++ b/spec/workers/import_worker_spec.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +require 'rails_helper' + +describe ImportWorker do + let(:worker) { described_class.new } + let(:service) { instance_double(ImportService, call: true) } + + describe '#perform' do + before do + allow(ImportService).to receive(:new).and_return(service) + end + + let(:import) { Fabricate(:import) } + + it 'sends the import to the service' do + worker.perform(import.id) + + expect(service).to have_received(:call).with(import) + expect { import.reload }.to raise_error(ActiveRecord::RecordNotFound) + end + end +end diff --git a/spec/workers/post_process_media_worker_spec.rb b/spec/workers/post_process_media_worker_spec.rb index 33072704bf..828da5244f 100644 --- a/spec/workers/post_process_media_worker_spec.rb +++ b/spec/workers/post_process_media_worker_spec.rb @@ -2,12 +2,38 @@ require 'rails_helper' -describe PostProcessMediaWorker do +describe PostProcessMediaWorker, :paperclip_processing do let(:worker) { described_class.new } - describe 'perform' do - it 'runs without error for missing record' do - expect { worker.perform(nil) }.to_not raise_error + describe '#perform' do + let(:media_attachment) { Fabricate(:media_attachment) } + + it 'reprocesses and updates the media attachment' do + worker.perform(media_attachment.id) + + expect(media_attachment.processing).to eq('complete') + end + + it 'returns true for non-existent record' do + result = worker.perform(123_123_123) + + expect(result).to be(true) + end + + context 'when sidekiq retries are exhausted' do + it 'sets state to failed' do + described_class.within_sidekiq_retries_exhausted_block({ 'args' => [media_attachment.id] }) do + worker.perform(media_attachment.id) + end + + expect(media_attachment.reload.processing).to eq('failed') + end + + it 'returns true for non-existent record' do + described_class.within_sidekiq_retries_exhausted_block({ 'args' => [123_123_123] }) do + expect(worker.perform(123_123_123)).to be(true) + end + end end end end diff --git a/spec/workers/publish_announcement_reaction_worker_spec.rb b/spec/workers/publish_announcement_reaction_worker_spec.rb new file mode 100644 index 0000000000..91668b5ada --- /dev/null +++ b/spec/workers/publish_announcement_reaction_worker_spec.rb @@ -0,0 +1,38 @@ +# frozen_string_literal: true + +require 'rails_helper' + +describe PublishAnnouncementReactionWorker do + let(:worker) { described_class.new } + + describe '#perform' do + before { Fabricate(:account, user: Fabricate(:user, current_sign_in_at: 1.hour.ago)) } + + let(:announcement) { Fabricate(:announcement) } + let(:name) { 'name value' } + + it 'sends the announcement and name to the service when subscribed' do + allow(redis).to receive(:exists?).and_return(true) + allow(redis).to receive(:publish) + + worker.perform(announcement.id, name) + + expect(redis).to have_received(:publish) + end + + it 'does not send the announcement and name to the service when not subscribed' do + allow(redis).to receive(:exists?).and_return(false) + allow(redis).to receive(:publish) + + worker.perform(announcement.id, name) + + expect(redis).to_not have_received(:publish) + end + + it 'returns true for non-existent record' do + result = worker.perform(123_123_123, name) + + expect(result).to be(true) + end + end +end diff --git a/spec/workers/removal_worker_spec.rb b/spec/workers/removal_worker_spec.rb new file mode 100644 index 0000000000..5071e882b6 --- /dev/null +++ b/spec/workers/removal_worker_spec.rb @@ -0,0 +1,28 @@ +# frozen_string_literal: true + +require 'rails_helper' + +describe RemovalWorker do + let(:worker) { described_class.new } + let(:service) { instance_double(RemoveStatusService, call: true) } + + describe '#perform' do + before do + allow(RemoveStatusService).to receive(:new).and_return(service) + end + + let(:status) { Fabricate(:status) } + + it 'sends the status to the service' do + worker.perform(status.id) + + expect(service).to have_received(:call).with(status) + end + + it 'returns true for non-existent record' do + result = worker.perform(123_123_123) + + expect(result).to be(true) + end + end +end diff --git a/spec/workers/scheduler/self_destruct_scheduler_spec.rb b/spec/workers/scheduler/self_destruct_scheduler_spec.rb new file mode 100644 index 0000000000..2bf5783571 --- /dev/null +++ b/spec/workers/scheduler/self_destruct_scheduler_spec.rb @@ -0,0 +1,60 @@ +# frozen_string_literal: true + +require 'rails_helper' + +describe Scheduler::SelfDestructScheduler do + let(:worker) { described_class.new } + + describe '#perform' do + let!(:account) { Fabricate(:account, domain: nil, suspended_at: nil) } + + context 'when not in self destruct mode' do + before do + allow(SelfDestructHelper).to receive(:self_destruct?).and_return(false) + end + + it 'returns without processing' do + worker.perform + + expect(account.reload.suspended_at).to be_nil + end + end + + context 'when in self-destruct mode' do + before do + allow(SelfDestructHelper).to receive(:self_destruct?).and_return(true) + end + + context 'when sidekiq is overwhelmed' do + before do + stats = instance_double(Sidekiq::Stats, enqueued: described_class::MAX_ENQUEUED**2) + allow(Sidekiq::Stats).to receive(:new).and_return(stats) + end + + it 'returns without processing' do + worker.perform + + expect(account.reload.suspended_at).to be_nil + end + end + + context 'when sidekiq is operational' do + it 'suspends local non-suspended accounts' do + worker.perform + + expect(account.reload.suspended_at).to_not be_nil + end + + it 'suspends local suspended accounts marked for deletion' do + account.update(suspended_at: 10.days.ago) + deletion_request = Fabricate(:account_deletion_request, account: account) + + worker.perform + + expect(account.reload.suspended_at).to be > 1.day.ago + expect { deletion_request.reload }.to raise_error(ActiveRecord::RecordNotFound) + end + end + end + end +end diff --git a/spec/workers/webhooks/delivery_worker_spec.rb b/spec/workers/webhooks/delivery_worker_spec.rb index daf8a3e285..6a5483d1d4 100644 --- a/spec/workers/webhooks/delivery_worker_spec.rb +++ b/spec/workers/webhooks/delivery_worker_spec.rb @@ -5,9 +5,21 @@ require 'rails_helper' describe Webhooks::DeliveryWorker do let(:worker) { described_class.new } - describe 'perform' do - it 'runs without error' do - expect { worker.perform(nil, nil) }.to_not raise_error + describe '#perform' do + let(:webhook) { Fabricate(:webhook) } + + it 'reprocesses and updates the webhook' do + stub_request(:post, webhook.url).to_return(status: 200, body: '') + + worker.perform(webhook.id, 'body') + + expect(a_request(:post, webhook.url)).to have_been_made.at_least_once + end + + it 'returns true for non-existent record' do + result = worker.perform(123_123_123, '') + + expect(result).to be(true) end end end