# frozen_string_literal: true RSpec.describe SpamRule::FlagSockpuppets do fab!(:user1) { Fabricate(:user, ip_address: "182.189.119.174") } fab!(:post1) { Fabricate(:post, user: user1, topic: Fabricate(:topic, user: user1)) } describe "#perform" do subject(:perform) { rule.perform } let(:rule) { described_class.new(post1) } it "does nothing if flag_sockpuppets is disabled" do SiteSetting.flag_sockpuppets = false rule.expects(:reply_is_from_sockpuppet?).never rule.expects(:flag_sockpuppet_users).never expect(perform).to eq(false) end context "when flag_sockpuppets is enabled" do before { SiteSetting.flag_sockpuppets = true } it "flags posts when it should" do rule.expects(:reply_is_from_sockpuppet?).returns(:true) rule.expects(:flag_sockpuppet_users).once expect(perform).to eq(true) end it "doesn't flag posts when it shouldn't" do rule.expects(:reply_is_from_sockpuppet?).returns(false) rule.expects(:flag_sockpuppet_users).never expect(perform).to eq(false) end end end describe "#reply_is_from_sockpuppet?" do it "is false for the first post in a topic" do expect(described_class.new(post1).reply_is_from_sockpuppet?).to eq(false) end it "is false if users have different IP addresses" do post2 = Fabricate(:post, user: Fabricate(:user, ip_address: "182.189.199.199"), topic: post1.topic) expect(described_class.new(post2).reply_is_from_sockpuppet?).to eq(false) end it "is true if users have the same IP address and are new" do post2 = Fabricate(:post, user: Fabricate(:user, ip_address: user1.ip_address), topic: post1.topic) expect(described_class.new(post2).reply_is_from_sockpuppet?).to eq(true) end it "is false if the ip address is allowlisted" do ScreenedIpAddress.stubs(:is_allowed?).with(user1.ip_address).returns(true) post2 = Fabricate(:post, user: Fabricate(:user, ip_address: user1.ip_address), topic: post1.topic) expect(described_class.new(post2).reply_is_from_sockpuppet?).to eq(false) end it "is false if reply and first post are from the same user" do post2 = Fabricate(:post, user: user1, topic: post1.topic) expect(described_class.new(post2).reply_is_from_sockpuppet?).to eq(false) end it "is false if first post user is staff" do staff1 = Fabricate(:admin, ip_address: "182.189.119.174") staff_post1 = Fabricate(:post, user: staff1, topic: Fabricate(:topic, user: staff1)) post2 = Fabricate( :post, user: Fabricate(:user, ip_address: staff1.ip_address), topic: staff_post1.topic, ) expect(described_class.new(post2).reply_is_from_sockpuppet?).to eq(false) end it "is false if second post user is staff" do post2 = Fabricate( :post, user: Fabricate(:moderator, ip_address: user1.ip_address), topic: post1.topic, ) expect(described_class.new(post2).reply_is_from_sockpuppet?).to eq(false) end it "is false if both users are staff" do staff1 = Fabricate(:moderator, ip_address: "182.189.119.174") staff_post1 = Fabricate(:post, user: staff1, topic: Fabricate(:topic, user: staff1)) post2 = Fabricate( :post, user: Fabricate(:admin, ip_address: staff1.ip_address), topic: staff_post1.topic, ) expect(described_class.new(post2).reply_is_from_sockpuppet?).to eq(false) end it "is true if first post user was created over 24 hours ago and has trust level higher than 0" do old_user = Fabricate( :user, ip_address: "182.189.119.174", created_at: 25.hours.ago, trust_level: TrustLevel[1], ) first_post = Fabricate(:post, user: old_user, topic: Fabricate(:topic, user: old_user)) post2 = Fabricate( :post, user: Fabricate(:user, ip_address: old_user.ip_address), topic: first_post.topic, ) expect(described_class.new(post2).reply_is_from_sockpuppet?).to eq(true) end it "is false if second post user was created over 24 hours ago and has trust level higher than 0" do post2 = Fabricate( :post, user: Fabricate( :user, ip_address: user1.ip_address, created_at: 25.hours.ago, trust_level: TrustLevel[1], ), topic: post1.topic, ) expect(described_class.new(post2).reply_is_from_sockpuppet?).to eq(false) end it "is true if first post user was created less that 24 hours ago and has trust level higher than 0" do new_user = Fabricate( :user, ip_address: "182.189.119.174", created_at: 1.hour.ago, trust_level: TrustLevel[1], ) first_post = Fabricate(:post, user: new_user, topic: Fabricate(:topic, user: new_user)) post2 = Fabricate( :post, user: Fabricate(:user, ip_address: new_user.ip_address), topic: first_post.topic, ) expect(described_class.new(post2).reply_is_from_sockpuppet?).to eq(true) end it "is true if second user was created less that 24 hours ago and has trust level higher than 0" do post2 = Fabricate( :post, user: Fabricate( :user, ip_address: user1.ip_address, created_at: 23.hours.ago, trust_level: TrustLevel[1], ), topic: post1.topic, ) expect(described_class.new(post2).reply_is_from_sockpuppet?).to eq(true) end # A weird case it "is false when user is nil on first post" do post1.user = nil post1.save! post2 = Fabricate(:post, user: Fabricate(:user), topic: post1.topic) expect(described_class.new(post2).reply_is_from_sockpuppet?).to eq(false) end end describe "#flag_sockpuppet_users" do fab!(:post2) do Fabricate(:post, user: Fabricate(:user, ip_address: user1.ip_address), topic: post1.topic) end let(:system) { Discourse.system_user } let(:spam) { PostActionType.types[:spam] } it "flags post and first post if both users are new" do described_class.new(post2).flag_sockpuppet_users expect(PostAction.where(user: system, post: post1, post_action_type_id: spam).exists?).to eq( true, ) expect(PostAction.where(user: system, post: post2, post_action_type_id: spam).exists?).to eq( true, ) end it "doesn't flag the first post more than once" do described_class.new(post2).flag_sockpuppet_users expect(PostAction.where(user: system, post: post2, post_action_type_id: spam).exists?).to eq( true, ) expect(PostAction.where(post: post2, post_action_type_id: spam).count).to eq(1) end it "doesn't flag the first post if the user is not new" do old_user = Fabricate( :user, ip_address: "182.189.119.174", created_at: 25.hours.ago, trust_level: TrustLevel[1], ) first_post = Fabricate(:post, user: old_user, topic: Fabricate(:topic, user: old_user)) post2 = Fabricate( :post, user: Fabricate(:user, ip_address: old_user.ip_address), topic: first_post.topic, ) described_class.new(post2).flag_sockpuppet_users expect(PostAction.where(user: system, post: post2, post_action_type_id: spam).exists?).to eq( true, ) expect( PostAction.where(user: system, post: first_post, post_action_type_id: spam).exists?, ).to eq(false) end it "doesn't create a flag if user is nil on first post" do post1.user_id = nil post1.save described_class.new(post2).flag_sockpuppet_users expect(PostAction.where(user: system, post: post2, post_action_type_id: spam).exists?).to eq( true, ) expect(PostAction.where(user: system, post: post1, post_action_type_id: spam).exists?).to eq( false, ) end it "doesn't flag the first post if it was already rejected by staff before" do flagged_post = Fabricate( :reviewable_flagged_post, target: post1, status: Reviewable.statuses[:rejected], target_created_by: post1.user, ) described_class.new(post2).perform expect(flagged_post.reload).to be_rejected end it "doesn't flag the post if another post of the same user was rejected by staff before" do another_post = Fabricate(:post, user: user1) flagged_post = Fabricate( :reviewable_flagged_post, target: another_post, status: Reviewable.statuses[:rejected], target_created_by: another_post.user, ) described_class.new(post2).perform expect(ReviewableFlaggedPost.where(target_created_by: user1).count).to eq(1) end end end