discourse/spec/models/post_action_spec.rb

438 lines
15 KiB
Ruby

require 'spec_helper'
require_dependency 'post_destroyer'
describe PostAction do
it { should belong_to :user }
it { should belong_to :post }
it { should belong_to :post_action_type }
it { should rate_limit }
let(:moderator) { Fabricate(:moderator) }
let(:codinghorror) { Fabricate(:coding_horror) }
let(:eviltrout) { Fabricate(:evil_trout) }
let(:admin) { Fabricate(:admin) }
let(:post) { Fabricate(:post) }
let(:second_post) { Fabricate(:post, topic_id: post.topic_id) }
let(:bookmark) { PostAction.new(user_id: post.user_id, post_action_type_id: PostActionType.types[:bookmark] , post_id: post.id) }
describe "messaging" do
it "doesn't generate title longer than 255 characters" do
topic = create_topic(title: "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc sit amet rutrum neque. Pellentesque suscipit vehicula facilisis. Phasellus lacus sapien, aliquam nec convallis sit amet, vestibulum laoreet ante. Curabitur et pellentesque tortor. Donec non.")
post = create_post(topic: topic)
-> { PostAction.act(admin, post, PostActionType.types[:notify_user], message: "WAT") }.should_not raise_error
end
it "notify moderators integration test" do
post = create_post
mod = moderator
Group.refresh_automatic_groups!
action = PostAction.act(codinghorror, post, PostActionType.types[:notify_moderators], message: "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
posts.count.should == 1
action.related_post_id.should == posts[0].id.to_i
posts[0].subtype.should == 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.topic_users(true).map {|x| x.user_id}
topic_user_ids.should include(codinghorror.id)
topic_user_ids.should include(mod.id)
# Notification level should be "Watching" for everyone
topic.topic_users(true).map(&:notification_level).uniq.should == [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
action.reload
action.deleted_at.should == nil
# Acting on the flag should post an automated status message
topic.posts.count.should == 2
PostAction.agree_flags!(post, admin)
topic.reload
topic.posts.count.should == 3
topic.posts.last.post_type.should == Post.types[:moderator_action]
# Clearing the flags should not post another automated status message
PostAction.act(mod, post, PostActionType.types[:notify_moderators], message: "another special message")
PostAction.clear_flags!(post, admin)
topic.reload
topic.posts.count.should == 3
end
describe 'notify_moderators' do
before do
PostAction.stubs(:create)
end
it "creates a pm if selected" do
post = build(:post, id: 1000)
PostCreator.any_instance.expects(:create).returns(post)
PostAction.act(build(:user), build(:post), PostActionType.types[:notify_moderators], message: "this is my special message");
end
end
describe "notify_user" do
before do
PostAction.stubs(:create)
post = build(:post)
post.user = build(:user)
end
it "sends an email to user if selected" do
PostCreator.any_instance.expects(:create).returns(build(:post))
PostAction.act(build(:user), post, PostActionType.types[:notify_user], message: "this is my special message");
end
end
end
describe "flag counts" do
before do
PostAction.update_flagged_posts_count
end
it "increments the numbers correctly" do
PostAction.flagged_posts_count.should == 0
PostAction.act(codinghorror, post, PostActionType.types[:off_topic])
PostAction.flagged_posts_count.should == 1
PostAction.clear_flags!(post, Discourse.system_user)
PostAction.flagged_posts_count.should == 0
end
it "should reset counts when a topic is deleted" do
PostAction.act(codinghorror, post, PostActionType.types[:off_topic])
post.topic.trash!
PostAction.flagged_posts_count.should == 0
end
it "should ignore validated flags" do
post = create_post
PostAction.act(codinghorror, post, PostActionType.types[:off_topic])
post.hidden.should == false
post.hidden_at.should be_blank
PostAction.defer_flags!(post, admin)
PostAction.flagged_posts_count.should == 0
post.reload
post.hidden.should == false
post.hidden_at.should be_blank
PostAction.hide_post!(post, PostActionType.types[:off_topic])
post.reload
post.hidden.should == true
post.hidden_at.should be_present
end
end
describe "update_counters" do
it "properly updates topic counters" do
PostAction.act(moderator, post, PostActionType.types[:like])
PostAction.act(codinghorror, second_post, PostActionType.types[:like])
post.topic.reload
post.topic.like_count.should == 2
end
end
describe "when a user bookmarks something" do
it "increases the post's bookmark count when saved" do
lambda { bookmark.save; post.reload }.should change(post, :bookmark_count).by(1)
end
it "increases the forum topic's bookmark count when saved" do
lambda { bookmark.save; post.topic.reload }.should change(post.topic, :bookmark_count).by(1)
end
describe 'when deleted' do
before do
bookmark.save
post.reload
@topic = post.topic
@topic.reload
bookmark.deleted_at = DateTime.now
bookmark.save
end
it 'reduces the bookmark count of the post' do
lambda { post.reload }.should change(post, :bookmark_count).by(-1)
end
it 'reduces the bookmark count of the forum topic' do
lambda { @topic.reload }.should change(post.topic, :bookmark_count).by(-1)
end
end
end
describe 'when a user likes something' do
it 'should increase the `like_count` and `like_score` when a user likes something' do
PostAction.act(codinghorror, post, PostActionType.types[:like])
post.reload
post.like_count.should == 1
post.like_score.should == 1
post.topic.reload
post.topic.like_count.should == 1
# When a staff member likes it
PostAction.act(moderator, post, PostActionType.types[:like])
post.reload
post.like_count.should == 2
post.like_score.should == 4
# Removing likes
PostAction.remove_act(codinghorror, post, PostActionType.types[:like])
post.reload
post.like_count.should == 1
post.like_score.should == 3
PostAction.remove_act(moderator, post, PostActionType.types[:like])
post.reload
post.like_count.should == 0
post.like_score.should == 0
end
end
describe "undo/redo repeatedly" do
it "doesn't create a second action for the same user/type" do
PostAction.act(codinghorror, post, PostActionType.types[:like])
PostAction.remove_act(codinghorror, post, PostActionType.types[:like])
PostAction.act(codinghorror, post, PostActionType.types[:like])
PostAction.where(post: post).with_deleted.count.should == 1
PostAction.remove_act(codinghorror, post, PostActionType.types[:like])
# Check that we don't lose consistency into negatives
post.reload.like_count.should == 0
end
end
describe 'when a user votes for something' do
it 'should increase the vote counts when a user votes' do
lambda {
PostAction.act(codinghorror, post, PostActionType.types[:vote])
post.reload
}.should change(post, :vote_count).by(1)
end
it 'should increase the forum topic vote count when a user votes' do
lambda {
PostAction.act(codinghorror, post, PostActionType.types[:vote])
post.topic.reload
}.should change(post.topic, :vote_count).by(1)
end
end
describe 'flagging' do
context "flag_counts_for" do
it "returns the correct flag counts" do
post = create_post
SiteSetting.stubs(:flags_required_to_hide_post).returns(7)
# A post with no flags has 0 for flag counts
PostAction.flag_counts_for(post.id).should == [0, 0]
flag = PostAction.act(eviltrout, post, PostActionType.types[:spam])
PostAction.flag_counts_for(post.id).should == [0, 1]
# If staff takes action, it is ranked higher
PostAction.act(admin, post, PostActionType.types[:spam], take_action: true)
PostAction.flag_counts_for(post.id).should == [0, 8]
# If a flag is dismissed
PostAction.clear_flags!(post, admin)
PostAction.flag_counts_for(post.id).should == [8, 0]
end
end
it 'does not allow you to flag stuff with the same reason more than once' do
post = Fabricate(:post)
PostAction.act(eviltrout, post, PostActionType.types[:spam])
lambda { PostAction.act(eviltrout, post, PostActionType.types[:off_topic]) }.should raise_error(PostAction::AlreadyActed)
end
it 'allows you to flag stuff with another reason' do
post = Fabricate(:post)
PostAction.act(eviltrout, post, PostActionType.types[:spam])
PostAction.remove_act(eviltrout, post, PostActionType.types[:spam])
lambda { PostAction.act(eviltrout, post, PostActionType.types[:off_topic]) }.should_not raise_error()
end
it 'should update counts when you clear flags' do
post = Fabricate(:post)
PostAction.act(eviltrout, post, PostActionType.types[:spam])
post.reload
post.spam_count.should == 1
PostAction.clear_flags!(post, Discourse.system_user)
post.reload
post.spam_count.should == 0
end
it 'should follow the rules for automatic hiding workflow' do
post = create_post
walterwhite = Fabricate(:walter_white)
SiteSetting.stubs(:flags_required_to_hide_post).returns(2)
Discourse.stubs(:site_contact_user).returns(admin)
PostAction.act(eviltrout, post, PostActionType.types[:spam])
PostAction.act(walterwhite, post, PostActionType.types[:spam])
post.reload
post.hidden.should == true
post.hidden_at.should be_present
post.hidden_reason_id.should == Post.hidden_reasons[:flag_threshold_reached]
post.topic.visible.should == false
post.revise(post.user, { raw: post.raw + " ha I edited it " })
post.reload
post.hidden.should == false
post.hidden_reason_id.should == nil
post.hidden_at.should be_blank
post.topic.visible.should == true
PostAction.act(eviltrout, post, PostActionType.types[:spam])
PostAction.act(walterwhite, post, PostActionType.types[:off_topic])
post.reload
post.hidden.should == true
post.hidden_at.should be_present
post.hidden_reason_id.should == Post.hidden_reasons[:flag_threshold_reached_again]
post.topic.visible.should == false
post.revise(post.user, { raw: post.raw + " ha I edited it again " })
post.reload
post.hidden.should == true
post.hidden_at.should be_present
post.hidden_reason_id.should == Post.hidden_reasons[:flag_threshold_reached_again]
post.topic.visible.should == false
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)
PostAction.act(Fabricate(:leader), post, PostActionType.types[:spam])
post.reload
post.hidden.should == true
post.hidden_at.should be_present
post.hidden_reason_id.should == Post.hidden_reasons[:flagged_by_tl3_user]
end
it "can flag the topic instead of a post" do
post1 = create_post
post2 = create_post(topic: post1.topic)
post_action = PostAction.act(Fabricate(:user), post1, PostActionType.types[:spam], { flag_topic: true })
post_action.targets_topic.should == 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
post_action = PostAction.act(Fabricate(:user), post, PostActionType.types[:spam], { flag_topic: true })
post_action.targets_topic.should == false
post_action.post_id.should == post.id
end
it "will unhide the post when a moderator undos the flag on which s/he took action" do
Discourse.stubs(:site_contact_user).returns(admin)
post = create_post
PostAction.act(moderator, post, PostActionType.types[:spam], { take_action: true })
post.reload
post.hidden.should == true
PostAction.remove_act(moderator, post, PostActionType.types[:spam])
post.reload
post.hidden.should == false
end
it "will automatically close a topic due to large community flagging" do
SiteSetting.stubs(:flags_required_to_hide_post).returns(0)
SiteSetting.stubs(:num_flags_to_close_topic).returns(3)
SiteSetting.stubs(:num_flaggers_to_close_topic).returns(2)
topic = Fabricate(:topic)
post1 = create_post(topic: topic)
post2 = create_post(topic: topic)
post3 = create_post(topic: topic)
flagger1 = Fabricate(:user)
flagger2 = Fabricate(:user)
# reaching `num_flaggers_to_close_topic` isn't enough
[flagger1, flagger2].each do |flagger|
PostAction.act(flagger, post1, PostActionType.types[:inappropriate])
end
topic.reload.closed.should == false
# clean up
PostAction.where(post: post1).delete_all
# reaching `num_flags_to_close_topic` isn't enough
[post1, post2, post3].each do |post|
PostAction.act(flagger1, post, PostActionType.types[:inappropriate])
end
topic.reload.closed.should == 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|
PostAction.act(flagger, post, PostActionType.types[:inappropriate])
end
end
topic.reload.closed.should == true
end
end
it "prevents user to act twice at the same time" do
post = Fabricate(:post)
# flags are already being tested
all_types_except_flags = PostActionType.types.except(PostActionType.flag_types)
all_types_except_flags.values.each do |action|
lambda do
PostAction.act(eviltrout, post, action)
PostAction.act(eviltrout, post, action)
end.should raise_error(PostAction::AlreadyActed)
end
end
end