# frozen_string_literal: true RSpec.describe ReviewableFlaggedPost, type: :model do def pending_count ReviewableFlaggedPost.default_visible.pending.count end fab!(:user) { Fabricate(:user, refresh_auto_groups: true) } fab!(:post) fab!(:moderator) { Fabricate(:moderator, refresh_auto_groups: true) } it "sets `potential_spam` when a spam flag is added" do reviewable = PostActionCreator.off_topic(user, post).reviewable expect(reviewable.potential_spam?).to eq(false) PostActionCreator.spam(Fabricate(:user, refresh_auto_groups: true), post) expect(reviewable.reload.potential_spam?).to eq(true) end it "sets `potentially_illegal` when an illegal flag is added" do reviewable = PostActionCreator.off_topic(user, post).reviewable expect(reviewable.potentially_illegal?).to eq(false) PostActionCreator.illegal(Fabricate(:user, refresh_auto_groups: true), post) expect(reviewable.reload.potentially_illegal?).to eq(true) end describe "actions" do let!(:result) { PostActionCreator.spam(user, post) } let(:reviewable) { result.reviewable } let(:score) { result.reviewable_score } let(:guardian) { Guardian.new(moderator) } describe "actions_for" do it "returns appropriate defaults" do actions = reviewable.actions_for(guardian) expect(actions.has?(:agree_and_hide)).to eq(true) expect(actions.has?(:agree_and_keep)).to eq(true) expect(actions.has?(:agree_and_edit)).to eq(true) expect(actions.has?(:agree_and_keep_hidden)).to eq(false) expect(actions.has?(:agree_and_silence)).to eq(true) expect(actions.has?(:agree_and_suspend)).to eq(true) expect(actions.has?(:delete_user)).to eq(true) expect(actions.has?(:delete_user_block)).to eq(true) expect(actions.has?(:disagree)).to eq(true) expect(actions.has?(:ignore_and_do_nothing)).to eq(true) expect(actions.has?(:delete_and_ignore)).to eq(true) expect(actions.has?(:delete_and_ignore_replies)).to eq(false) expect(actions.has?(:delete_and_agree)).to eq(true) expect(actions.has?(:delete_and_replies)).to eq(false) expect(actions.has?(:disagree_and_restore)).to eq(false) end it "doesn't include deletes for category topics" do c = Fabricate(:category_with_definition) flag = PostActionCreator.spam(user, c.topic.posts.first).reviewable actions = flag.actions_for(guardian) expect(actions.has?(:delete_and_ignore)).to eq(false) expect(actions.has?(:delete_and_ignore_replies)).to eq(false) expect(actions.has?(:delete_and_agree)).to eq(false) expect(actions.has?(:delete_and_replies)).to eq(false) end it "changes `agree_and_keep` to `agree_and_keep_hidden` if it's been hidden" do post.hidden = true actions = reviewable.actions_for(guardian) expect(actions.has?(:agree_and_keep)).to eq(false) expect(actions.has?(:agree_and_edit)).to eq(false) expect(actions.has?(:agree_and_keep_hidden)).to eq(true) end it "returns `agree_and_restore` if the post is user deleted" do post.update(user_deleted: true) expect(reviewable.actions_for(guardian).has?(:agree_and_restore)).to eq(true) end it "returns delete replies options if there are replies" do post.update(reply_count: 3) expect(reviewable.actions_for(guardian).has?(:delete_and_agree_replies)).to eq(true) end it "returns appropriate actions for a hidden post" do post.update(hidden: true, hidden_at: Time.now) expect(reviewable.actions_for(guardian).has?(:agree_and_hide)).to eq(false) expect(reviewable.actions_for(guardian).has?(:disagree_and_restore)).to eq(true) end it "won't return the penalty options if the user is not regular" do post.user.update(moderator: true) expect(reviewable.actions_for(guardian).has?(:agree_and_silence)).to eq(false) expect(reviewable.actions_for(guardian).has?(:agree_and_suspend)).to eq(false) end it "doesn't end up with an empty ignore bundle when the post is already hidden and deleted" do post.update!(hidden: true) post.topic.trash! post.trash! expect(reviewable.actions_for(guardian).has?(:ignore_and_do_nothing)).to eq(false) expect(reviewable.actions_for(guardian).has?(:delete_and_ignore)).to eq(false) expect( reviewable.actions_for(guardian).bundles.find { |bundle| bundle.id.include?("-ignore") }, ).to be_blank end context "when flagged as potential_spam" do before { reviewable.update!(potential_spam: true, potentially_illegal: false) } it "excludes delete action if the reviewer cannot delete the user" do post.user.user_stat.update!( first_post_created_at: 1.year.ago, post_count: User::MAX_STAFF_DELETE_POST_COUNT + 1, ) expect(reviewable.actions_for(guardian).has?(:delete_user)).to be false expect(reviewable.actions_for(guardian).has?(:delete_user_block)).to be false end it "includes delete actions if the reviewer can delete the user" do expect(reviewable.actions_for(guardian).has?(:delete_user)).to be true expect(reviewable.actions_for(guardian).has?(:delete_user_block)).to be true end end context "when flagged as illegal" do before { reviewable.update(potential_spam: false, potentially_illegal: true) } it "excludes delete action if the reviewer cannot delete the user" do post.user.user_stat.update!( first_post_created_at: 1.year.ago, post_count: User::MAX_STAFF_DELETE_POST_COUNT + 1, ) expect(reviewable.actions_for(guardian).has?(:delete_user)).to be false expect(reviewable.actions_for(guardian).has?(:delete_user_block)).to be false end it "includes delete actions if the reviewer can delete the user" do expect(reviewable.actions_for(guardian).has?(:delete_user)).to be true expect(reviewable.actions_for(guardian).has?(:delete_user_block)).to be true end end context "for ignore_and_do_nothing" do it "does not return `ignore_and_do_nothing` when post is hidden" do post.update(hidden: true) expect(reviewable.actions_for(guardian).has?(:ignore_and_do_nothing)).to eq(false) end it "returns `ignore_and_do_nothing` if the acting user is system" do post.update(hidden: true) system_guardian = Guardian.new(Discourse.system_user) expect(reviewable.actions_for(system_guardian).has?(:ignore_and_do_nothing)).to eq(true) end end end it "agree_and_keep agrees with the flags and keeps the post" do reviewable.perform(moderator, :agree_and_keep) expect(reviewable).to be_approved expect(score.reload).to be_agreed expect(post).not_to be_hidden end it "agree_and_keep agrees with the flags and edits the post" do reviewable.perform(moderator, :agree_and_keep) expect(reviewable).to be_approved expect(score.reload).to be_agreed expect(post).not_to be_hidden end describe "with reviewable claiming enabled" do fab!(:claimed) { Fabricate(:reviewable_claimed_topic, topic: post.topic, user: moderator) } it "clears the claimed topic on resolve" do SiteSetting.reviewable_claiming = "required" reviewable.perform(moderator, :agree_and_keep) expect(reviewable).to be_approved expect(score.reload).to be_agreed expect(post).not_to be_hidden expect(ReviewableClaimedTopic.where(topic_id: post.topic.id).exists?).to eq(false) expect( post .topic .reviewables .first .history .where(reviewable_history_type: ReviewableHistory.types[:unclaimed]) .size, ).to eq(1) end end it "agree_and_suspend agrees with the flags and keeps the post" do reviewable.perform(moderator, :agree_and_suspend) expect(reviewable).to be_approved expect(score.reload).to be_agreed expect(post).not_to be_hidden end it "agree_and_silence agrees with the flags and keeps the post" do reviewable.perform(moderator, :agree_and_silence) expect(reviewable).to be_approved expect(score.reload).to be_agreed expect(post).not_to be_hidden end it "agree_and_hide agrees with the flags and hides the post" do reviewable.perform(moderator, :agree_and_hide) expect(reviewable).to be_approved expect(score.reload).to be_agreed expect(post).to be_hidden end it "agree_and_restore agrees with the flags and restores the post" do post.update(user_deleted: true) reviewable.perform(moderator, :agree_and_restore) expect(reviewable).to be_approved expect(score.reload).to be_agreed expect(post.user_deleted?).to eq(false) end it "supports deleting a spammer" do reviewable.perform(moderator, :delete_user_block) expect(reviewable).to be_approved expect(score.reload).to be_agreed expect(post.reload.deleted_at).to be_present expect(User.find_by(id: reviewable.target_created_by_id)).to be_blank end it "ignores the flags" do reviewable.perform(moderator, :ignore_and_do_nothing) expect(reviewable).to be_ignored expect(score.reload).to be_ignored end it "delete_and_ignore ignores the flags and deletes post" do reviewable.perform(moderator, :delete_and_ignore) expect(reviewable).to be_ignored expect(score.reload).to be_ignored expect(post.reload.deleted_at).to be_present end it "delete_and_ignore_replies ignores the flags and deletes post + replies" do reply = create_reply(post) nested_reply = create_reply(reply) post.reload reviewable.perform(moderator, :delete_and_ignore_replies) expect(reviewable).to be_ignored expect(score.reload).to be_ignored expect(post.reload.deleted_at).to be_present expect(reply.reload.deleted_at).to be_present expect(nested_reply.reload.deleted_at).to be_present end it "delete_and_agree agrees with the flags and deletes post" do reviewable.perform(moderator, :delete_and_agree) expect(reviewable).to be_approved expect(score.reload).to be_agreed expect(post.reload.deleted_at).to be_present end it "delete_and_agree_replies agrees w/ the flags and deletes post + replies" do reply = create_reply(post) nested_reply = create_reply(reply) post.reload reviewable.perform(moderator, :delete_and_agree_replies) expect(reviewable).to be_approved expect(score.reload).to be_agreed expect(post.reload.deleted_at).to be_present expect(reply.reload.deleted_at).to be_present expect(nested_reply.reload.deleted_at).to be_present end it "disagrees with the flags" do reviewable.perform(moderator, :disagree) expect(reviewable).to be_rejected expect(score.reload).to be_disagreed end it "disagrees with the flags and restores the post" do post.update(hidden: true, hidden_at: Time.now) reviewable.perform(moderator, :disagree_and_restore) expect(reviewable).to be_rejected expect(score.reload).to be_disagreed expect(post.user_deleted?).to eq(false) expect(post.hidden?).to eq(false) end end describe "pending count" do it "increments the numbers correctly" do expect(pending_count).to eq(0) result = PostActionCreator.off_topic(user, post) expect(pending_count).to eq(1) result.reviewable.perform(Discourse.system_user, :disagree) expect(pending_count).to eq(0) end it "respects `reviewable_default_visibility`" do Reviewable.set_priorities(high: 7.5) SiteSetting.reviewable_default_visibility = "high" expect(pending_count).to eq(0) PostActionCreator.off_topic(user, post) expect(pending_count).to eq(0) PostActionCreator.spam(moderator, post) expect(pending_count).to eq(1) end it "should reset counts when a topic is deleted" do PostActionCreator.off_topic(user, post) expect(pending_count).to eq(1) PostDestroyer.new(moderator, post).destroy expect(pending_count).to eq(0) end it "should not review non-human users" do post = create_post(user: Discourse.system_user) reviewable = PostActionCreator.off_topic(user, post).reviewable expect(reviewable).to be_blank expect(pending_count).to eq(0) end it "should ignore handled flags" do post = create_post reviewable = PostActionCreator.off_topic(user, post).reviewable expect(post.hidden).to eq(false) expect(post.hidden_at).to be_blank reviewable.perform(moderator, :ignore_and_do_nothing) expect(pending_count).to eq(0) post.reload expect(post.hidden).to eq(false) expect(post.hidden_at).to be_blank post.hide!(PostActionType.types[:off_topic]) post.reload expect(post.hidden).to eq(true) expect(post.hidden_at).to be_present end end describe "#perform_delete_and_agree" do it "notifies the user about the flagged post deletion" do reviewable = Fabricate(:reviewable_flagged_post) reviewable.add_score( moderator, PostActionType.types[:spam], created_at: reviewable.created_at, ) reviewable.perform(moderator, :delete_and_agree) assert_pm_creation_enqueued(reviewable.post.user_id, "flags_agreed_and_post_deleted") end end describe "#perform_delete_and_agree_replies" do let(:flagged_post) { Fabricate(:reviewable_flagged_post) } let!(:reply) { create_reply(flagged_post.target) } before { flagged_post.target.update(reply_count: 1) } it "ignore flagged replies" do flagged_reply = Fabricate(:reviewable_flagged_post, target: reply) flagged_post.perform(moderator, :delete_and_agree_replies) expect(flagged_reply.reload).to be_ignored end it "notifies users that responded to flagged post" do SiteSetting.notify_users_after_responses_deleted_on_flagged_post = true flagged_post.perform(moderator, :delete_and_agree_replies) expect(Jobs::SendSystemMessage.jobs.size).to eq(2) expect(Jobs::SendSystemMessage.jobs.last["args"].first["message_type"]).to eq( "flags_agreed_and_post_deleted_for_responders", ) end it "skips responders notification when the score type doesn't match any post action flag type" do flagged_post.reviewable_scores.first.update!( reviewable_score_type: ReviewableScore.types[:needs_approval], ) expect { flagged_post.perform(moderator, :delete_and_agree_replies) }.not_to change( Jobs::SendSystemMessage.jobs, :size, ) end it "ignores flagged responses" do SiteSetting.notify_users_after_responses_deleted_on_flagged_post = true flagged_reply = Fabricate(:reviewable_flagged_post, target: reply) Fabricate( :post, reply_to_post_number: flagged_reply.target.post_number, topic: flagged_reply.target.topic, ) flagged_post.perform(moderator, :delete_and_agree_replies) expect(flagged_reply.reload).to be_ignored end end describe "#perform_disagree_and_restore" do it "notifies the user about the flagged post being restored" do reviewable = Fabricate(:reviewable_flagged_post) reviewable.post.update( hidden: true, hidden_at: Time.zone.now, hidden_reason_id: PostActionType.types[:spam], ) reviewable.perform(moderator, :disagree_and_restore) assert_pm_creation_enqueued(reviewable.post.user_id, "flags_disagreed") end end describe "recalculating the reviewable score" do let(:expected_score) { 8 } let(:reviewable) { Fabricate(:reviewable_flagged_post, score: expected_score) } it "doesn't recalculate the score after ignore" do reviewable.perform(moderator, :ignore_and_do_nothing) expect(reviewable.score).to eq(expected_score) end it "doesn't recalculate the score after disagree" do reviewable.perform(moderator, :disagree) expect(reviewable.score).to eq(expected_score) end end def assert_pm_creation_enqueued(user_id, pm_type) expect(Jobs::SendSystemMessage.jobs.length).to eq(1) job = Jobs::SendSystemMessage.jobs[0] expect(job["args"][0]["user_id"]).to eq(user_id) expect(job["args"][0]["message_type"]).to eq(pm_type) end def create_reply(post) PostCreator.create( Fabricate(:user), raw: "this is the reply text", reply_to_post_number: post.post_number, topic_id: post.topic, ) end end