discourse/spec/services/user/suspend_spec.rb
Bannon Tanner 938bf7ac25
FEATURE: Add background job and ability to delete posts from suspend user modal (#36813)
## Background
User wanted admin ability to delete all of a user's posts from the
suspend modal, to reduce the steps needed.

## Changes
Adds a background job that triggers when number of posts to be deleted
is past a certain threshold, which is determined by a new system
setting. Admins get a message in their inbox when the job is complete.

Modifies original admin "delete all posts" flow to utilize the
background job, when necessary.

Added option to the "What would you like to do with the associated post"
dropdown to delete all of a user's posts while suspending them. This
then also obeys the system setting to run the deletion as a background
or foreground job.
2026-01-07 10:25:01 -06:00

85 lines
2.5 KiB
Ruby
Vendored

# frozen_string_literal: true
RSpec.describe User::Suspend do
describe described_class::Contract, type: :model do
it { is_expected.to validate_presence_of(:user_id) }
it { is_expected.to validate_presence_of(:reason) }
it { is_expected.to validate_presence_of(:suspend_until) }
it { is_expected.to validate_length_of(:reason).is_at_most(300) }
it do
is_expected.to validate_length_of(:other_user_ids).as_array.is_at_most(
User::MAX_SIMILAR_USERS,
)
end
it do
is_expected.to validate_inclusion_of(:post_action).in_array(
%w[delete delete_replies delete_all edit none],
).allow_blank
end
end
describe ".call" do
subject(:result) { described_class.call(params:, **dependencies) }
fab!(:admin)
fab!(:user)
fab!(:other_user, :user)
let(:params) { { user_id:, reason:, suspend_until:, other_user_ids:, message: } }
let(:dependencies) { { guardian: } }
let(:guardian) { admin.guardian }
let(:user_id) { user.id }
let(:other_user_ids) { other_user.id }
let(:reason) { "spam" }
let(:message) { "it was spam" }
let(:suspend_until) { 3.hours.from_now.to_s }
context "when invalid data is provided" do
let(:user_id) { nil }
it { is_expected.to fail_a_contract }
end
context "when provided user does not exist" do
let(:user_id) { 0 }
it { is_expected.to fail_to_find_a_model(:user) }
end
context "when user is already suspended" do
before do
UserSuspender.new(user, by_user: admin, suspended_till: suspend_until, reason:).suspend
end
it { is_expected.to fail_a_policy(:not_suspended_already) }
end
context "when all users cannot be suspended" do
let(:other_user_ids) { [other_user.id, Fabricate(:admin).id].join(",") }
it { is_expected.to fail_a_policy(:can_suspend_all_users) }
end
context "when everything's ok" do
before { allow(User::Action::TriggerPostAction).to receive(:call) }
it "suspends all provided users" do
result
expect([user, other_user].map(&:reload)).to all be_suspended
end
it "triggers a post action" do
result
expect(User::Action::TriggerPostAction).to have_received(:call).with(
guardian:,
post: nil,
params: result[:params],
)
end
it "exposes the full reason in the result object" do
expect(result[:full_reason]).to eq("spam\n\nit was spam")
end
end
end
end