discourse/spec/models/post_action_spec.rb
Martin Brennan e62e93f83a
FEATURE: Introduce personal_message_enabled_groups setting (#18042)
This will replace `enable_personal_messages` and
`min_trust_to_send_messages`, this commit introduces
the setting `personal_message_enabled_groups`
and uses it in all places that `enable_personal_messages`
and `min_trust_to_send_messages` currently apply.

A migration is included to set `personal_message_enabled_groups`
based on the following rules:

* If `enable_personal_messages` was false, then set
  `personal_message_enabled_groups` to `3`, which is
  the staff auto group
* If `min_trust_to_send_messages` is not default (1)
  and the above condition is false, then set the
  `personal_message_enabled_groups` setting to
  the appropriate auto group based on the trust level
* Otherwise just set `personal_message_enabled_groups` to
  11 which is the TL1 auto group

After follow-up PRs to plugins using these old settings, we will be
able to drop the old settings from core, in the meantime I've added
 DEPRECATED notices to their descriptions and added them
to the deprecated site settings list.

This commit also introduces a `_map` shortcut method definition
for all `group_list` site settings, e.g. `SiteSetting.personal_message_enabled_groups`
also has `SiteSetting.personal_message_enabled_groups_map` available,
which automatically splits the setting by `|` and converts it into
an array of integers.
2022-09-26 13:58:40 +10:00

1030 lines
35 KiB
Ruby

# frozen_string_literal: true
RSpec.describe PostAction do
it { is_expected.to rate_limit }
fab!(:moderator) { Fabricate(:moderator) }
fab!(:codinghorror) { Fabricate(:coding_horror) }
fab!(:eviltrout) { Fabricate(:evil_trout) }
fab!(:admin) { Fabricate(:admin) }
fab!(:post) { Fabricate(:post) }
fab!(:second_post) { Fabricate(:post, topic: post.topic) }
def value_for(user_id, dt)
GivenDailyLike.find_for(user_id, dt).pluck(:likes_given)[0] || 0
end
it "disallows the same action from happening twice" do
PostAction.create(user: eviltrout, post: post, post_action_type_id: PostActionType.types[:like])
pa = PostAction.new(user: eviltrout, post: post, post_action_type_id: PostActionType.types[:like])
expect(pa).not_to be_valid
end
describe "messaging" do
it "notifies moderators (integration test)" do
post = create_post
mod = moderator
Group.refresh_automatic_groups!
result = PostActionCreator.notify_moderators(
codinghorror,
post,
"this is my special long message"
)
posts = Post.joins(:topic)
.select('posts.id, topics.subtype, posts.topic_id')
.where('topics.archetype' => Archetype.private_message)
.to_a
expect(posts.count).to eq(1)
expect(result.post_action.related_post_id).to eq(posts[0].id.to_i)
expect(result.reviewable_score.meta_topic_id).to eq(posts[0].topic_id)
expect(posts[0].subtype).to eq(TopicSubtype.notify_moderators)
topic = posts[0].topic
# Moderators should be invited to the private topic, otherwise they're not permitted to see it
topic_user_ids = topic.reload.topic_users.map { |x| x.user_id }
expect(topic_user_ids).to include(codinghorror.id)
expect(topic_user_ids).to include(mod.id)
expect(topic.topic_users.where(user_id: mod.id)
.pluck_first(:notification_level)).to eq(TopicUser.notification_levels[:tracking])
expect(topic.topic_users.where(user_id: codinghorror.id)
.pluck_first(:notification_level)).to eq(TopicUser.notification_levels[:watching])
# reply to PM should not clear flag
PostCreator.new(mod, topic_id: posts[0].topic_id, raw: "This is my test reply to the user, it should clear flags").create
result.post_action.reload
expect(result.post_action.deleted_at).to eq(nil)
# Acting on the flag should not post an automated status message (since a moderator already replied)
expect(topic.posts.count).to eq(2)
result.reviewable.perform(admin, :agree_and_keep)
topic.reload
expect(topic.posts.count).to eq(2)
# Clearing the flags should not post an automated status message
result = PostActionCreator.notify_moderators(mod, post, "another special message")
result.reviewable.perform(admin, :disagree)
topic.reload
expect(topic.posts.count).to eq(2)
# Acting on the flag should post an automated status message
another_post = create_post
result = PostActionCreator.notify_moderators(codinghorror, another_post, "foobar")
topic = result.post_action.related_post.topic
expect(topic.posts.count).to eq(1)
result.reviewable.perform(admin, :agree_and_keep)
topic.reload
expect(topic.posts.count).to eq(2)
expect(topic.posts.last.post_type).to eq(Post.types[:moderator_action])
expect(topic.message_archived?(mod)).to eq(true)
end
context "with category group moderators" do
fab!(:group_user) { Fabricate(:group_user) }
let(:group) { group_user.group }
before do
SiteSetting.enable_category_group_moderation = true
group.update!(messageable_level: Group::ALIAS_LEVELS[:nobody])
post.topic.category.update!(reviewable_by_group_id: group.id)
end
it "notifies via pm" do
result = PostActionCreator.notify_moderators(
codinghorror,
post,
"this is my special long message"
)
readable_by_groups = result.reviewable_score.meta_topic.topic_allowed_groups.map(&:group_id)
expect(readable_by_groups).to include(group.id)
end
end
end
describe "update_counters" do
it "properly updates topic counters" do
freeze_time Date.today
# we need this to test it
TopicUser.change(codinghorror, post.topic, posted: true)
expect(value_for(moderator.id, Date.today)).to eq(0)
PostActionCreator.like(moderator, post)
PostActionCreator.like(codinghorror, second_post)
post.topic.reload
expect(post.topic.like_count).to eq(2)
expect(value_for(moderator.id, Date.today)).to eq(1)
tu = TopicUser.get(post.topic, codinghorror)
expect(tu.liked).to be true
end
end
describe "undo/redo repeatedly" do
it "doesn't create a second action for the same user/type" do
PostActionCreator.like(codinghorror, post)
PostActionDestroyer.destroy(codinghorror, post, :like)
PostActionCreator.like(codinghorror, post)
expect(PostAction.where(post: post).with_deleted.count).to eq(1)
PostActionDestroyer.destroy(codinghorror, post, :like)
# Check that we don't lose consistency into negatives
expect(post.reload.like_count).to eq(0)
end
end
describe 'when a user likes something' do
before do
PostActionNotifier.enable
end
it 'should generate and remove notifications correctly' do
PostActionCreator.like(codinghorror, post)
expect(Notification.count).to eq(1)
notification = Notification.last
expect(notification.user_id).to eq(post.user_id)
expect(notification.notification_type).to eq(Notification.types[:liked])
PostActionDestroyer.destroy(codinghorror, post, :like)
expect(Notification.count).to eq(0)
PostActionCreator.like(codinghorror, post)
expect(Notification.count).to eq(1)
notification = Notification.last
expect(notification.user_id).to eq(post.user_id)
expect(notification.notification_type).to eq(Notification.types[:liked])
end
it 'should not notify when never is selected' do
post.user.user_option.update!(
like_notification_frequency:
UserOption.like_notification_frequency_type[:never]
)
expect do
PostActionCreator.like(codinghorror, post)
end.to_not change { Notification.count }
end
it 'notifies on likes correctly' do
SiteSetting.post_undo_action_window_mins = 120
PostActionCreator.like(eviltrout, post)
PostActionCreator.like(admin, post)
# one like
expect(Notification.where(post_number: 1, topic_id: post.topic_id).count)
.to eq(1)
post.user.user_option.update!(
like_notification_frequency: UserOption.like_notification_frequency_type[:always]
)
admin2 = Fabricate(:admin)
# Travel 1 hour in time to test that order post_actions by `created_at`
freeze_time 1.hour.from_now
expect do
PostActionCreator.like(admin2, post)
end.to_not change { Notification.count }
# adds info to the notification
notification = Notification.find_by(
post_number: 1,
topic_id: post.topic_id
)
expect(notification.data_hash["count"].to_i).to eq(2)
expect(notification.data_hash["username2"]).to eq(eviltrout.username)
# this is a tricky thing ... removing a like should fix up the notifications
PostActionDestroyer.destroy(eviltrout, post, :like)
# rebuilds the missing notification
expect(Notification.where(post_number: 1, topic_id: post.topic_id).count)
.to eq(1)
notification = Notification.find_by(
post_number: 1,
topic_id: post.topic_id
)
expect(notification.data_hash["count"]).to eq(2)
expect(notification.data_hash["username"]).to eq(admin2.username)
expect(notification.data_hash["username2"]).to eq(admin.username)
post.user.user_option.update!(
like_notification_frequency:
UserOption.like_notification_frequency_type[:first_time_and_daily]
)
# this gets skipped
admin3 = Fabricate(:admin)
PostActionCreator.like(admin3, post)
freeze_time 2.days.from_now
admin4 = Fabricate(:admin)
PostActionCreator.like(admin4, post)
# first happened within the same day, no need to notify
expect(Notification.where(post_number: 1, topic_id: post.topic_id).count)
.to eq(2)
end
describe 'likes consolidation' do
fab!(:liker) { Fabricate(:user) }
fab!(:liker2) { Fabricate(:user) }
fab!(:likee) { Fabricate(:user) }
it "can be disabled" do
SiteSetting.notification_consolidation_threshold = 0
expect do
PostActionCreator.like(liker, Fabricate(:post, user: likee))
end.to change { likee.reload.notifications.count }.by(1)
SiteSetting.notification_consolidation_threshold = 1
expect do
PostActionCreator.like(liker, Fabricate(:post, user: likee))
end.to_not change { likee.reload.notifications.count }
end
describe 'frequency first_time_and_daily' do
before do
likee.user_option.update!(
like_notification_frequency:
UserOption.like_notification_frequency_type[:first_time_and_daily]
)
end
it 'should consolidate likes notification when the threshold is reached' do
SiteSetting.notification_consolidation_threshold = 2
expect do
3.times do
PostActionCreator.like(liker, Fabricate(:post, user: likee))
end
end.to change { likee.reload.notifications.count }.by(1)
notification = likee.notifications.last
expect(notification.notification_type).to eq(
Notification.types[:liked_consolidated]
)
data = JSON.parse(notification.data)
expect(data["username"]).to eq(liker.username)
expect(data["display_username"]).to eq(liker.username)
expect(data["count"]).to eq(3)
notification.update!(read: true)
expect do
2.times do
PostActionCreator.like(liker, Fabricate(:post, user: likee))
end
end.to_not change { likee.reload.notifications.count }
data = JSON.parse(notification.reload.data)
expect(notification.read).to eq(false)
expect(data["count"]).to eq(5)
# Like from a different user shouldn't be consolidated
expect do
PostActionCreator.like(Fabricate(:user), Fabricate(:post, user: likee))
end.to change { likee.reload.notifications.count }.by(1)
notification = likee.notifications.last
expect(notification.notification_type).to eq(
Notification.types[:liked]
)
freeze_time((
SiteSetting.likes_notification_consolidation_window_mins.minutes +
1
).since)
expect do
PostActionCreator.like(liker, Fabricate(:post, user: likee))
end.to change { likee.reload.notifications.count }.by(1)
notification = likee.notifications.last
expect(notification.notification_type).to eq(Notification.types[:liked])
end
end
describe 'frequency always' do
before do
likee.user_option.update!(
like_notification_frequency:
UserOption.like_notification_frequency_type[:always]
)
end
it 'should consolidate liked notifications when threshold is reached' do
SiteSetting.notification_consolidation_threshold = 2
post = Fabricate(:post, user: likee)
expect do
[liker2, liker].each do |user|
PostActionCreator.like(user, post)
end
end.to change { likee.reload.notifications.count }.by(1)
notification = likee.notifications.last
data_hash = notification.data_hash
expect(data_hash["original_username"]).to eq(liker.username)
expect(data_hash["username2"]).to eq(liker2.username)
expect(data_hash["count"].to_i).to eq(2)
expect do
2.times do
PostActionCreator.like(liker, Fabricate(:post, user: likee))
end
end.to change { likee.reload.notifications.count }.by(2)
expect(likee.notifications.pluck(:notification_type).uniq)
.to contain_exactly(Notification.types[:liked])
expect do
PostActionCreator.like(liker, Fabricate(:post, user: likee))
end.to change { likee.reload.notifications.count }.by(-1)
notification = likee.notifications.last
expect(notification.notification_type).to eq(
Notification.types[:liked_consolidated]
)
expect(notification.data_hash["count"].to_i).to eq(3)
expect(notification.data_hash["username"]).to eq(liker.username)
end
end
end
it "should not generate a notification if liker has been muted" do
mutee = Fabricate(:user)
MutedUser.create!(user_id: post.user.id, muted_user_id: mutee.id)
expect do
PostActionCreator.like(mutee, post)
end.to_not change { Notification.count }
end
it 'should not generate a notification if liker has the topic muted' do
post = Fabricate(:post, user: eviltrout)
TopicUser.create!(
topic: post.topic,
user: eviltrout,
notification_level: TopicUser.notification_levels[:muted]
)
expect do
PostActionCreator.like(codinghorror, post)
end.to_not change { Notification.count }
end
it "should generate a notification if liker is an admin irregardless of \
muting" do
MutedUser.create!(user_id: post.user.id, muted_user_id: admin.id)
expect do
PostActionCreator.like(admin, post)
end.to change { Notification.count }.by(1)
notification = Notification.last
expect(notification.user_id).to eq(post.user_id)
expect(notification.notification_type).to eq(Notification.types[:liked])
end
it 'should not increase topic like count when liking a whisper' do
SiteSetting.set(:enable_whispers, true)
post.revise(admin, post_type: Post.types[:whisper])
PostActionCreator.like(admin, post)
expect(post.reload.like_count).to eq(1)
expect(post.topic.like_count).to eq(0)
end
it 'should increase the `like_count` and `like_score` when a user likes something' do
freeze_time Date.today
PostActionCreator.like(codinghorror, post)
post.reload
expect(post.like_count).to eq(1)
expect(post.like_score).to eq(1)
post.topic.reload
expect(post.topic.like_count).to eq(1)
expect(value_for(codinghorror.id, Date.today)).to eq(1)
# When a staff member likes it
PostActionCreator.like(moderator, post)
post.reload
expect(post.like_count).to eq(2)
expect(post.like_score).to eq(4)
expect(post.topic.like_count).to eq(2)
# Removing likes
PostActionDestroyer.destroy(codinghorror, post, :like)
post.reload
expect(post.like_count).to eq(1)
expect(post.like_score).to eq(3)
expect(post.topic.like_count).to eq(1)
expect(value_for(codinghorror.id, Date.today)).to eq(0)
PostActionDestroyer.destroy(moderator, post, :like)
post.reload
expect(post.like_count).to eq(0)
expect(post.like_score).to eq(0)
expect(post.topic.like_count).to eq(0)
end
it "shouldn't change given_likes unless likes are given or removed" do
freeze_time(Time.zone.now)
PostActionCreator.like(codinghorror, post)
expect(value_for(codinghorror.id, Date.today)).to eq(1)
PostActionType.types.each do |type_name, type_id|
post = Fabricate(:post)
PostActionCreator.create(codinghorror, post, type_name)
actual_count = value_for(codinghorror.id, Date.today)
expected_count = type_name == :like ? 2 : 1
expect(actual_count).to eq(expected_count), "Expected likes_given to be #{expected_count} when adding '#{type_name}', but got #{actual_count}"
PostActionDestroyer.new(codinghorror, post, type_id).perform
actual_count = value_for(codinghorror.id, Date.today)
expect(actual_count).to eq(1), "Expected likes_given to be 1 when removing '#{type_name}', but got #{actual_count}"
end
end
end
describe 'flagging' do
it 'does not allow you to flag stuff twice, even if the reason is different' do
expect(PostActionCreator.spam(eviltrout, post)).to be_success
expect(PostActionCreator.off_topic(eviltrout, post)).to be_failed
end
it 'allows you to flag stuff again if your previous flag was removed' do
PostActionCreator.spam(eviltrout, post)
PostActionDestroyer.destroy(eviltrout, post, :spam)
expect(PostActionCreator.spam(eviltrout, post)).to be_success
end
it 'should update counts when you clear flags' do
reviewable = PostActionCreator.spam(eviltrout, post).reviewable
expect(post.reload.spam_count).to eq(1)
reviewable.perform(Discourse.system_user, :disagree)
expect(post.reload.spam_count).to eq(0)
end
it "will not allow regular users to auto hide staff posts" do
mod = Fabricate(:moderator)
post = Fabricate(:post, user: mod)
Reviewable.set_priorities(high: 2.0)
SiteSetting.hide_post_sensitivity = Reviewable.sensitivities[:low]
Discourse.stubs(:site_contact_user).returns(admin)
PostActionCreator.spam(eviltrout, post)
PostActionCreator.spam(Fabricate(:walter_white), post)
expect(post.hidden).to eq(false)
expect(post.hidden_at).to be_blank
end
it "allows staff users to auto hide staff posts" do
mod = Fabricate(:moderator)
post = Fabricate(:post, user: mod)
Reviewable.set_priorities(high: 8.0)
SiteSetting.hide_post_sensitivity = Reviewable.sensitivities[:low]
Discourse.stubs(:site_contact_user).returns(admin)
PostActionCreator.spam(eviltrout, post)
PostActionCreator.spam(Fabricate(:admin), post)
post.reload
expect(post.hidden).to eq(true)
expect(post.hidden_at).to be_present
end
it "will not trigger auto hide on like" do
mod = Fabricate(:moderator)
post = Fabricate(:post, user: mod)
result = PostActionCreator.spam(eviltrout, post)
result.reviewable.update!(score: 1000.0)
PostActionCreator.like(Fabricate(:admin), post)
post.reload
expect(post.hidden).to eq(false)
end
it 'should follow the rules for automatic hiding workflow' do
post = create_post
walterwhite = Fabricate(:walter_white)
Reviewable.set_priorities(high: 3.0)
SiteSetting.hide_post_sensitivity = Reviewable.sensitivities[:low]
Discourse.stubs(:site_contact_user).returns(admin)
PostActionCreator.spam(eviltrout, post)
PostActionCreator.spam(walterwhite, post)
job_args = Jobs::SendSystemMessage.jobs.last["args"].first
expect(job_args["user_id"]).to eq(post.user.id)
expect(job_args["message_type"]).to eq("post_hidden")
post.reload
expect(post.hidden).to eq(true)
expect(post.hidden_at).to be_present
expect(post.hidden_reason_id).to eq(Post.hidden_reasons[:flag_threshold_reached])
expect(post.topic.visible).to eq(false)
post.revise(post.user, raw: post.raw + " ha I edited it ")
post.reload
expect(post.hidden).to eq(false)
expect(post.hidden_reason_id).to eq(Post.hidden_reasons[:flag_threshold_reached]) # keep most recent reason
expect(post.hidden_at).to be_present # keep the most recent hidden_at time
expect(post.topic.visible).to eq(true)
PostActionCreator.spam(eviltrout, post)
PostActionCreator.off_topic(walterwhite, post)
job_args = Jobs::SendSystemMessage.jobs.last["args"].first
expect(job_args["user_id"]).to eq(post.user.id)
expect(job_args["message_type"]).to eq("post_hidden_again")
post.reload
expect(post.hidden).to eq(true)
expect(post.hidden_at).to be_present
expect(post.hidden_reason_id).to eq(Post.hidden_reasons[:flag_threshold_reached_again])
expect(post.topic.visible).to eq(false)
post.revise(post.user, raw: post.raw + " ha I edited it again ")
post.reload
expect(post.hidden).to eq(true)
expect(post.hidden_at).to be_present
expect(post.hidden_reason_id).to eq(Post.hidden_reasons[:flag_threshold_reached_again])
expect(post.topic.visible).to eq(false)
end
it "doesn't fail when post has nil user" do
post = create_post
post.update!(user: nil)
PostActionCreator.new(moderator, post, PostActionType.types[:spam], take_action: true).perform
post.reload
expect(post.hidden).to eq(true)
end
it "hide tl0 posts that are flagged as spam by a tl3 user" do
newuser = Fabricate(:newuser)
post = create_post(user: newuser)
Discourse.stubs(:site_contact_user).returns(admin)
PostActionCreator.spam(Fabricate(:leader), post)
post.reload
expect(post.hidden).to eq(true)
expect(post.hidden_at).to be_present
expect(post.hidden_reason_id).to eq(Post.hidden_reasons[:flagged_by_tl3_user])
end
it "can flag the topic instead of a post" do
post1 = create_post
create_post(topic: post1.topic)
result = PostActionCreator.new(
Fabricate(:user),
post1,
PostActionType.types[:spam],
flag_topic: true
).perform
expect(result.post_action.targets_topic).to eq(true)
expect(result.reviewable.payload['targets_topic']).to eq(true)
end
it "will flag the first post if you flag a topic but there is only one post in the topic" do
post = create_post
result = PostActionCreator.new(
Fabricate(:user),
post,
PostActionType.types[:spam],
flag_topic: true
).perform
expect(result.post_action.targets_topic).to eq(false)
expect(result.post_action.post_id).to eq(post.id)
expect(result.reviewable.payload['targets_topic']).to eq(false)
end
it "will unhide the post when a moderator undoes the flag on which s/he took action" do
Discourse.stubs(:site_contact_user).returns(admin)
post = create_post
PostActionCreator.new(moderator, post, PostActionType.types[:spam], take_action: true).perform
post.reload
expect(post.hidden).to eq(true)
PostActionDestroyer.destroy(moderator, post, :spam)
post.reload
expect(post.hidden).to eq(false)
end
context "with topic auto closing" do
fab!(:topic) { Fabricate(:topic) }
let(:post1) { create_post(topic: topic) }
let(:post2) { create_post(topic: topic) }
let(:post3) { create_post(topic: topic) }
fab!(:flagger1) { Fabricate(:user) }
fab!(:flagger2) { Fabricate(:user) }
before do
SiteSetting.hide_post_sensitivity = Reviewable.sensitivities[:disabled]
Reviewable.set_priorities(high: 4.5)
SiteSetting.auto_close_topic_sensitivity = Reviewable.sensitivities[:low]
SiteSetting.num_flaggers_to_close_topic = 2
SiteSetting.num_hours_to_close_topic = 1
end
it "will automatically pause a topic due to large community flagging" do
freeze_time
# reaching `num_flaggers_to_close_topic` isn't enough
[flagger1, flagger2].each do |flagger|
PostActionCreator.inappropriate(flagger, post1)
end
expect(topic.reload.closed).to eq(false)
# clean up
PostAction.where(post: post1).delete_all
# reaching `num_flags_to_close_topic` isn't enough
[post1, post2, post3].each do |post|
PostActionCreator.inappropriate(flagger1, post)
end
expect(topic.reload.closed).to eq(false)
# clean up
PostAction.where(post: [post1, post2, post3]).delete_all
# reaching both should close the topic
[flagger1, flagger2].each do |flagger|
[post1, post2, post3].each do |post|
PostActionCreator.inappropriate(flagger, post)
end
end
expect(topic.reload.closed).to eq(true)
topic_status_update = TopicTimer.last
expect(topic_status_update.topic).to eq(topic)
expect(topic_status_update.execute_at).to eq_time(1.hour.from_now)
expect(topic_status_update.status_type).to eq(TopicTimer.types[:open])
end
context "when on a staff post" do
fab!(:staff_user) { Fabricate(:user, moderator: true) }
fab!(:topic) { Fabricate(:topic, user: staff_user) }
it "will not close topics opened by staff" do
[flagger1, flagger2].each do |flagger|
[post1, post2, post3].each do |post|
PostActionCreator.inappropriate(flagger, post)
end
end
expect(topic.reload.closed).to eq(false)
end
end
it "will keep the topic in closed status until the community flags are handled" do
freeze_time
SiteSetting.num_flaggers_to_close_topic = 1
Reviewable.set_priorities(high: 0.5)
SiteSetting.auto_close_topic_sensitivity = Reviewable.sensitivities[:low]
post = Fabricate(:post, topic: topic)
PostActionCreator.spam(flagger1, post)
expect(topic.reload.closed).to eq(true)
timer = TopicTimer.last
expect(timer.execute_at).to eq_time(1.hour.from_now)
freeze_time timer.execute_at
Jobs::OpenTopic.new.execute(topic_timer_id: timer.id)
expect(topic.reload.closed).to eq(true)
expect(timer.reload.execute_at).to eq_time(1.hour.from_now)
freeze_time timer.execute_at
SiteSetting.num_flaggers_to_close_topic = 10
Reviewable.set_priorities(high: 10.0)
SiteSetting.auto_close_topic_sensitivity = Reviewable.sensitivities[:low]
Jobs::ToggleTopicClosed.new.execute(topic_timer_id: timer.id, state: false)
expect(topic.reload.closed).to eq(false)
end
it "will reopen topic after the flags are auto handled" do
freeze_time
[flagger1, flagger2].each do |flagger|
[post1, post2, post3].each do |post|
PostActionCreator.inappropriate(flagger, post)
end
end
expect(topic.reload.closed).to eq(true)
freeze_time 61.days.from_now
Jobs::AutoQueueHandler.new.execute({})
Jobs::ToggleTopicClosed.new.execute(topic_timer_id: TopicTimer.last.id, state: false)
expect(topic.reload.closed).to eq(false)
end
end
end
it "prevents user to act twice at the same time" do
Group.refresh_automatic_groups!
# flags are already being tested
all_types_except_flags = PostActionType.types.except(*PostActionType.flag_types_without_custom.keys)
all_types_except_flags.values.each do |action|
expect(PostActionCreator.new(eviltrout, post, action).perform).to be_success
expect(PostActionCreator.new(eviltrout, post, action).perform).to be_failed
end
end
describe "messages" do
it "does not create a message when there is no message" do
result = PostActionCreator.spam(Discourse.system_user, post)
expect(result).to be_success
expect(result.post_action.related_post_id).to be_nil
expect(result.reviewable_score.meta_topic_id).to be_nil
end
[:notify_moderators, :notify_user, :spam].each do |post_action_type|
it "creates a message for #{post_action_type}" do
result = PostActionCreator.new(
Discourse.system_user,
post,
PostActionType.types[post_action_type],
message: 'WAT'
).perform
expect(result).to be_success
expect(result.post_action.related_post_id).to be_present
end
end
it "should raise the right errors when it fails to create a post" do
user = Fabricate(:user)
UserSilencer.new(user, Discourse.system_user).silence
result = PostActionCreator.notify_moderators(user, post, 'testing')
expect(result).to be_failed
end
it "should succeed even with low max title length" do
SiteSetting.max_topic_title_length = 50
post.topic.title = 'This is a test topic ' * 2
post.topic.save!
result = PostActionCreator.notify_moderators(Discourse.system_user, post, 'WAT')
expect(result).to be_success
expect(result.post_action.related_post_id).to be_present
end
end
describe ".lookup_for" do
it "returns the correct map" do
user = Fabricate(:user)
post_action = PostActionCreator.create(user, post, :like).post_action
map = PostAction.lookup_for(user, [post.topic], post_action.post_action_type_id)
expect(map).to eq(post.topic_id => [post.post_number])
end
end
describe "#add_moderator_post_if_needed" do
it "should not add a moderator post when it's disabled" do
post = create_post
result = PostActionCreator.create(moderator, post, :spam, message: "WAT")
topic = result.post_action.related_post.topic
expect(topic.posts.count).to eq(1)
SiteSetting.auto_respond_to_flag_actions = false
result.reviewable.perform(admin, :agree_and_keep)
expect(topic.reload.posts.count).to eq(1)
end
it "should create a notification in the related topic" do
Jobs.run_immediately!
user = Fabricate(:user)
stub_image_size
result = PostActionCreator.create(user, post, :spam, message: "WAT")
topic = result.post_action.related_post.topic
reviewable = result.reviewable
expect(user.notifications.count).to eq(0)
SiteSetting.auto_respond_to_flag_actions = true
reviewable.perform(admin, :agree_and_keep)
user_notifications = user.notifications
expect(user_notifications.last.topic).to eq(topic)
end
skip "should not add a moderator post when post is flagged via private message" do
Jobs.run_immediately!
user = Fabricate(:user)
result = PostActionCreator.create(user, post, :notify_user, message: "WAT")
action = result.post_action
action.reload.related_post.topic
expect(user.notifications.count).to eq(0)
SiteSetting.auto_respond_to_flag_actions = true
result.reviewable.perform(admin, :agree_and_keep)
expect(user.reload.user_stat.flags_agreed).to eq(0)
user_notifications = user.notifications
expect(user_notifications.count).to eq(0)
end
end
describe "rate limiting" do
def limiter(tl, type)
user = Fabricate.build(:user)
user.trust_level = tl
action = PostAction.new(user: user, post_action_type_id: PostActionType.types[type])
action.post_action_rate_limiter
end
it "should scale up likes limits depending on trust level" do
expect(limiter(0, :like).max).to eq SiteSetting.max_likes_per_day
expect(limiter(1, :like).max).to eq SiteSetting.max_likes_per_day
expect(limiter(2, :like).max).to eq (SiteSetting.max_likes_per_day * SiteSetting.tl2_additional_likes_per_day_multiplier).to_i
expect(limiter(3, :like).max).to eq (SiteSetting.max_likes_per_day * SiteSetting.tl3_additional_likes_per_day_multiplier).to_i
expect(limiter(4, :like).max).to eq (SiteSetting.max_likes_per_day * SiteSetting.tl4_additional_likes_per_day_multiplier).to_i
SiteSetting.tl2_additional_likes_per_day_multiplier = -1
expect(limiter(2, :like).max).to eq SiteSetting.max_likes_per_day
SiteSetting.tl2_additional_likes_per_day_multiplier = 0.8
expect(limiter(2, :like).max).to eq SiteSetting.max_likes_per_day
SiteSetting.tl2_additional_likes_per_day_multiplier = "bob"
expect(limiter(2, :like).max).to eq SiteSetting.max_likes_per_day
end
it "should scale up flag limits depending on trust level" do
%i(off_topic inappropriate spam notify_moderators).each do |type|
SiteSetting.tl2_additional_flags_per_day_multiplier = 1.5
expect(limiter(0, type).max).to eq SiteSetting.max_flags_per_day
expect(limiter(1, type).max).to eq SiteSetting.max_flags_per_day
expect(limiter(2, type).max).to eq (SiteSetting.max_flags_per_day * SiteSetting.tl2_additional_flags_per_day_multiplier).to_i
expect(limiter(3, type).max).to eq (SiteSetting.max_flags_per_day * SiteSetting.tl3_additional_flags_per_day_multiplier).to_i
expect(limiter(4, type).max).to eq (SiteSetting.max_flags_per_day * SiteSetting.tl4_additional_flags_per_day_multiplier).to_i
SiteSetting.tl2_additional_flags_per_day_multiplier = -1
expect(limiter(2, type).max).to eq SiteSetting.max_flags_per_day
SiteSetting.tl2_additional_flags_per_day_multiplier = 0.8
expect(limiter(2, type).max).to eq SiteSetting.max_flags_per_day
SiteSetting.tl2_additional_flags_per_day_multiplier = "bob"
expect(limiter(2, type).max).to eq SiteSetting.max_flags_per_day
end
end
end
describe '#is_flag?' do
describe 'when post action is a flag' do
it 'should return true' do
PostActionType.notify_flag_types.each do |_type, id|
post_action = PostAction.new(
user: codinghorror,
post_action_type_id: id
)
expect(post_action.is_flag?).to eq(true)
end
end
end
describe 'when post action is not a flag' do
it 'should return false' do
post_action = PostAction.new(
user: codinghorror,
post_action_type_id: 99
)
expect(post_action.is_flag?).to eq(false)
end
end
end
describe "triggers Discourse events" do
it 'triggers a flag_created event' do
event = DiscourseEvent.track(:flag_created) { PostActionCreator.spam(eviltrout, post) }
expect(event).to be_present
end
context "when resolving flags" do
let(:result) { PostActionCreator.spam(eviltrout, post) }
let(:post_action) { result.post_action }
let(:reviewable) { result.reviewable }
it 'creates events for agreed' do
events = DiscourseEvent.track_events { reviewable.perform(moderator, :agree_and_keep) }
reviewed_event = events.find { |e| e[:event_name] == :flag_reviewed }
expect(reviewed_event).to be_present
event = events.find { |e| e[:event_name] == :flag_agreed }
expect(event).to be_present
expect(event[:params]).to eq([post_action])
end
it 'creates events for disagreed' do
events = DiscourseEvent.track_events { reviewable.perform(moderator, :disagree) }
reviewed_event = events.find { |e| e[:event_name] == :flag_reviewed }
expect(reviewed_event).to be_present
event = events.find { |e| e[:event_name] == :flag_disagreed }
expect(event).to be_present
expect(event[:params]).to eq([post_action])
end
it 'creates events for ignored' do
events = DiscourseEvent.track_events { reviewable.perform(moderator, :ignore) }
reviewed_event = events.find { |e| e[:event_name] == :flag_reviewed }
expect(reviewed_event).to be_present
event = events.find { |e| e[:event_name] == :flag_deferred }
expect(event).to be_present
expect(event[:params]).to eq([post_action])
end
end
end
end