mirror of
https://github.com/mastodon/mastodon.git
synced 2026-03-14 22:44:08 +00:00
Fix poll notifications reappearing after dismissal (#37948)
When a remote server sends ActivityPub Update activities for an already-expired poll, queue_poll_notifications! would schedule a PollExpirationNotifyWorker with a past timestamp, causing Sidekiq to execute it immediately. If the user had already dismissed the notification, a new one would be created, leading to an infinite loop. Fix by adding an early return when the poll has already expired.
This commit is contained in:
parent
4d2a148ccb
commit
fe99ca80c1
|
|
@ -410,6 +410,11 @@ class ActivityPub::ProcessStatusUpdateService < BaseService
|
|||
|
||||
return unless poll.present? && poll.expires_at.present? && poll.votes.exists?
|
||||
|
||||
# Do not schedule notifications for already-expired polls, as the
|
||||
# notification worker would run immediately and re-create notifications
|
||||
# that users may have already dismissed (see #37948)
|
||||
return if poll.expired?
|
||||
|
||||
PollExpirationNotifyWorker.remove_from_scheduled(poll.id) if @previous_expires_at.present? && @previous_expires_at > poll.expires_at
|
||||
PollExpirationNotifyWorker.perform_at(poll.expires_at + 5.minutes, poll.id)
|
||||
end
|
||||
|
|
|
|||
|
|
@ -51,6 +51,10 @@ class VoteService < BaseService
|
|||
def queue_final_poll_check!
|
||||
return unless @poll.expires?
|
||||
|
||||
# Do not schedule if the poll has already expired, as the worker would
|
||||
# run immediately and potentially re-create dismissed notifications (#37948)
|
||||
return if @poll.expired?
|
||||
|
||||
PollExpirationNotifyWorker.perform_at(@poll.expires_at + 5.minutes, @poll.id)
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -136,6 +136,22 @@ RSpec.describe ActivityPub::ProcessStatusUpdateService do
|
|||
end
|
||||
end
|
||||
|
||||
context 'when the poll has already expired' do
|
||||
let(:poll) { Fabricate(:poll, status: status, account: status.account, expires_at: 1.day.ago) }
|
||||
|
||||
before do
|
||||
poll.votes.create!(account: Fabricate(:account, domain: nil), choice: 0)
|
||||
end
|
||||
|
||||
it 'does not schedule a PollExpirationNotifyWorker' do
|
||||
expect(PollExpirationNotifyWorker).to_not have_enqueued_sidekiq_job(poll.id)
|
||||
|
||||
subject.call(status, activity_json, object_json)
|
||||
|
||||
expect(PollExpirationNotifyWorker).to_not have_enqueued_sidekiq_job(poll.id)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the status changes a poll despite being not explicitly marked as updated' do
|
||||
let(:account) { Fabricate(:account, domain: 'example.com') }
|
||||
let!(:expiration) { 10.days.from_now.utc }
|
||||
|
|
|
|||
|
|
@ -24,6 +24,15 @@ RSpec.describe VoteService do
|
|||
end
|
||||
end
|
||||
|
||||
context 'when the remote poll has already expired' do
|
||||
let(:poll) { Fabricate(:poll, account: account, options: %w(Foo Bar), expires_at: 1.day.ago) }
|
||||
|
||||
it 'does not schedule a PollExpirationNotifyWorker' do
|
||||
expect { subject.call(voter, poll, [0]) }
|
||||
.to_not change { PollExpirationNotifyWorker.jobs.size }
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the poll was created by a remote account' do
|
||||
let(:account) { Fabricate(:account, domain: 'host.example') }
|
||||
|
||||
|
|
|
|||
|
|
@ -54,5 +54,23 @@ RSpec.describe PollExpirationNotifyWorker do
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when a voter has already dismissed the notification' do
|
||||
let(:voter) { Fabricate(:account, domain: nil) }
|
||||
let(:poll) { Fabricate(:poll, account: status.account, status: status, expires_at: 1.day.ago) }
|
||||
|
||||
before do
|
||||
poll.votes.create!(account: voter, choice: 0)
|
||||
# First run creates the notification
|
||||
described_class.new.perform(poll.id)
|
||||
# User dismisses the notification
|
||||
Notification.where(account: voter, activity: poll, type: :poll).destroy_all
|
||||
end
|
||||
|
||||
it 'does not re-create the notification on a second run' do
|
||||
expect { described_class.new.perform(poll.id) }
|
||||
.to_not change { Notification.where(account: voter, activity: poll, type: :poll).count }.from(0)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user