0
0
Fork 0
mirror of https://github.com/discourse/discourse.git synced 2026-08-04 10:39:43 +08:00
discourse/spec/jobs/notify_reviewable_spec.rb
Alan Guo Xiang Tan b03428cfec
FIX: Resolve all of a deleted spammer's reviewables (#40780)
When a moderator picks "Delete User" or "Delete and Block User" on one
of the spammer's flagged posts, deleting the user is meant to settle all
of their reviewables, since there is no user left to act on. Previously
only part of that happened:

- `UserDestroyer#agree_with_flags` resolved the spammer's other flagged
posts by checking for the `agree_and_keep` action, but a flag on a
hidden post only offers `agree_and_keep_hidden`, so those flags were
skipped and stayed pending forever.
- Queued posts by the spammer were never touched, so they also stayed
pending.
- `UserDestroyer#destroy` skips resolving the spammer's account
reviewable when a `reviewable_id` option is present. That option is
meant to prevent the account reviewable from resolving itself twice when
the deletion starts from it, but the guard only checked presence, so a
deletion starting from a flagged post also skipped the account
reviewable.
- The browser updates review queue rows from message bus broadcasts.
Those can arrive after the page has stopped waiting, so the acting
moderator kept seeing the affected rows as "Pending" until a full
reload. Acting on one of those stale rows returns a 403 because the
target user no longer exists.

This PR resolves every reviewable tied to the deleted user and updates
the acting moderator's review queue without a reload.
2026-06-15 13:34:15 +08:00

124 lines
4.6 KiB
Ruby
Vendored

# frozen_string_literal: true
RSpec.describe Jobs::NotifyReviewable do
describe "#execute" do
fab!(:admin) { Fabricate(:admin, moderator: true) }
fab!(:moderator)
fab!(:group_user)
fab!(:group) { group_user.group }
fab!(:user) { group_user.user }
it "publishes status updates for handled reviewables as integers" do
reviewable = Fabricate(:reviewable, reviewable_by_moderator: false)
reviewable.update!(status: Reviewable.statuses[:approved])
messages =
MessageBus.track_publish("/reviewable_counts/#{admin.id}") do
described_class.new.execute(
reviewable_id: reviewable.id,
performing_username: admin.username,
updated_reviewable_ids: [reviewable.id],
)
end
expect(messages.first.data[:updates]).to eq(
reviewable.id => {
last_performing_username: admin.username,
status: Reviewable.statuses[:approved],
},
)
end
it "will notify users of new reviewable content for the user menu" do
SiteSetting.navigation_menu = "sidebar"
SiteSetting.enable_category_group_moderation = true
GroupUser.create!(group_id: group.id, user_id: moderator.id)
# Content for admins only
admin_reviewable = Fabricate(:reviewable, reviewable_by_moderator: false)
admin.update!(last_seen_reviewable_id: admin_reviewable.id)
messages =
MessageBus.track_publish { described_class.new.execute(reviewable_id: admin_reviewable.id) }
expect(messages.size).to eq(1)
admin_message = messages.first
expect(admin_message.channel).to eq("/reviewable_counts/#{admin.id}")
expect(admin_message.user_ids).to eq([admin.id])
expect(admin_message.data[:reviewable_count]).to eq(1)
expect(admin_message.data[:unseen_reviewable_count]).to eq(0)
# Content for moderators
moderator_reviewable = Fabricate(:reviewable, reviewable_by_moderator: true)
messages =
MessageBus.track_publish do
described_class.new.execute(reviewable_id: moderator_reviewable.id)
end
expect(messages.size).to eq(2)
admin_message = messages.find { |m| m.user_ids == [admin.id] }
expect(admin_message.channel).to eq("/reviewable_counts/#{admin.id}")
expect(admin_message.data[:reviewable_count]).to eq(2)
expect(admin_message.data[:unseen_reviewable_count]).to eq(1)
moderator_message = messages.find { |m| m.user_ids == [moderator.id] }
expect(moderator_message.channel).to eq("/reviewable_counts/#{moderator.id}")
expect(moderator_message.data[:reviewable_count]).to eq(1)
expect(moderator_message.data[:unseen_reviewable_count]).to eq(1)
moderator.update!(last_seen_reviewable_id: moderator_reviewable.id)
# Content for a group
category = Fabricate(:category)
Fabricate(:category_moderation_group, category:, group:)
group_reviewable = Fabricate(:reviewable, reviewable_by_moderator: true, category:)
messages =
MessageBus.track_publish { described_class.new.execute(reviewable_id: group_reviewable.id) }
expect(messages.size).to eq(3)
admin_message = messages.find { |m| m.user_ids == [admin.id] }
expect(admin_message.channel).to eq("/reviewable_counts/#{admin.id}")
expect(admin_message.data[:reviewable_count]).to eq(3)
expect(admin_message.data[:unseen_reviewable_count]).to eq(2)
moderator_message = messages.find { |m| m.user_ids == [moderator.id] }
expect(moderator_message.channel).to eq("/reviewable_counts/#{moderator.id}")
expect(moderator_message.data[:reviewable_count]).to eq(2)
expect(moderator_message.data[:unseen_reviewable_count]).to eq(1)
group_user_message = messages.find { |m| m.user_ids == [user.id] }
expect(group_user_message.channel).to eq("/reviewable_counts/#{user.id}")
expect(group_user_message.data[:reviewable_count]).to eq(1)
expect(group_user_message.data[:unseen_reviewable_count]).to eq(1)
end
it "won't notify a group when disabled" do
SiteSetting.enable_category_group_moderation = false
GroupUser.create!(group_id: group.id, user_id: moderator.id)
category = Fabricate(:category)
Fabricate(:category_moderation_group, category:, group:)
reviewable = Fabricate(:reviewable, reviewable_by_moderator: true, category:)
messages =
MessageBus.track_publish("/reviewable_counts") do
described_class.new.execute(reviewable_id: reviewable.id)
end
group_user_message = messages.find { |m| m.user_ids.include?(user.id) }
expect(group_user_message).to be_blank
end
end
end