mirror of
https://github.com/discourse/discourse.git
synced 2024-11-23 02:50:00 +08:00
854b8b7093
Currently to handle stub topics after merging, there are only options to (1) never delete a stub topic and (2) delete a stub topic after X amount of days. This adds the option to immediately delete a stub topic upon merge. --------- Co-authored-by: Mark VanLandingham <markvanlan@gmail.com> Co-authored-by: Renato Atilio <renato@discourse.org>
2545 lines
93 KiB
Ruby
2545 lines
93 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
RSpec.describe PostMover do
|
|
fab!(:admin)
|
|
fab!(:evil_trout) { Fabricate(:evil_trout, refresh_auto_groups: true) }
|
|
|
|
describe "#move_types" do
|
|
context "when verifying enum sequence" do
|
|
before { @move_types = PostMover.move_types }
|
|
|
|
it "'new_topic' should be at 1st position" do
|
|
expect(@move_types[:new_topic]).to eq(1)
|
|
end
|
|
|
|
it "'existing_topic' should be at 2nd position" do
|
|
expect(@move_types[:existing_topic]).to eq(2)
|
|
end
|
|
end
|
|
end
|
|
|
|
describe "move_posts" do
|
|
context "with topics" do
|
|
before { freeze_time }
|
|
|
|
fab!(:user) { Fabricate(:admin) }
|
|
fab!(:another_user) { evil_trout }
|
|
fab!(:category) { Fabricate(:category, user: user) }
|
|
fab!(:topic) { Fabricate(:topic, user: user, created_at: 4.hours.ago) }
|
|
fab!(:p1) do
|
|
Fabricate(:post, topic: topic, user: user, created_at: 3.hours.ago, reply_count: 2)
|
|
end
|
|
|
|
fab!(:p2) do
|
|
Fabricate(
|
|
:post,
|
|
topic: topic,
|
|
user: another_user,
|
|
raw: "Has a link to [evil trout](http://eviltrout.com) which is a cool site.",
|
|
reply_to_post_number: p1.post_number,
|
|
reply_count: 1,
|
|
created_at: 2.hours.ago,
|
|
)
|
|
end
|
|
|
|
fab!(:p3) do
|
|
Fabricate(
|
|
:post,
|
|
topic: topic,
|
|
reply_to_post_number: p1.post_number,
|
|
user: user,
|
|
created_at: 1.hour.ago,
|
|
)
|
|
end
|
|
fab!(:p4) do
|
|
Fabricate(
|
|
:post,
|
|
topic: topic,
|
|
reply_to_post_number: p2.post_number,
|
|
user: user,
|
|
created_at: 45.minutes.ago,
|
|
)
|
|
end
|
|
fab!(:p5) { Fabricate(:post, created_at: 30.minutes.ago) }
|
|
let(:p6) { Fabricate(:post, topic: topic, created_at: 15.minutes.ago) }
|
|
|
|
before do
|
|
SiteSetting.tagging_enabled = true
|
|
Jobs.run_immediately!
|
|
p1.replies.push(p2, p3)
|
|
p2.replies.push(p4)
|
|
UserActionManager.enable
|
|
@like = PostActionCreator.like(another_user, p4)
|
|
end
|
|
|
|
def add_moderator_post_to(topic, post_type)
|
|
topic.add_moderator_post(user, "message", post_type: post_type, action_code: "split_topic")
|
|
end
|
|
|
|
context "with success" do
|
|
it "correctly handles notifications and bread crumbs" do
|
|
old_topic = p2.topic
|
|
|
|
old_topic_id = p2.topic_id
|
|
|
|
topic.move_posts(user, [p2.id, p4.id, p6.id], title: "new testing topic name")
|
|
|
|
p2.reload
|
|
expect(p2.topic_id).not_to eq(old_topic_id)
|
|
expect(p2.reply_to_post_number).to eq(nil)
|
|
expect(p2.reply_to_user_id).to eq(nil)
|
|
|
|
notification =
|
|
p2.user.notifications.where(notification_type: Notification.types[:moved_post]).first
|
|
|
|
expect(notification.topic_id).to eq(p2.topic_id)
|
|
expect(notification.topic_id).not_to eq(old_topic_id)
|
|
expect(notification.post_number).to eq(1)
|
|
|
|
# no message for person who made the move
|
|
expect(
|
|
p4.user.notifications.where(notification_type: Notification.types[:moved_post]).length,
|
|
).to eq(0)
|
|
|
|
# notify at the right spot in the stream
|
|
notification =
|
|
p6.user.notifications.where(notification_type: Notification.types[:moved_post]).first
|
|
|
|
expect(notification.topic_id).to eq(p2.topic_id)
|
|
expect(notification.topic_id).not_to eq(old_topic_id)
|
|
|
|
# this is the 3rd post we moved
|
|
expect(notification.post_number).to eq(3)
|
|
|
|
old_topic.reload
|
|
move_message = old_topic.posts.find_by(post_number: 2)
|
|
expect(move_message.post_type).to eq(Post.types[:small_action])
|
|
expect(move_message.raw).to include("3 posts were split")
|
|
end
|
|
|
|
it "correctly remaps quotes" do
|
|
raw = <<~RAW
|
|
[quote="dan, post:#{p2.post_number}, topic:#{p2.topic_id}, full:true"]
|
|
some quote from the other post
|
|
[/quote]
|
|
|
|
the quote above should be updated with new post number and topic id
|
|
RAW
|
|
|
|
p3.update!(raw: raw)
|
|
p3.rebake!
|
|
|
|
expect { topic.move_posts(user, [p2.id], title: "new testing topic name") }.to change {
|
|
p2.reload.topic_id
|
|
}.and change { p2.post_number }.and change { p3.reload.raw }.and change {
|
|
p2.baked_version
|
|
}.to(nil).and change { p3.baked_version }.to(nil)
|
|
|
|
expect(p3.raw).to include("post:#{p2.post_number}, topic:#{p2.topic_id}")
|
|
end
|
|
end
|
|
|
|
context "with errors" do
|
|
it "raises an error when one of the posts doesn't exist" do
|
|
non_existent_post_id = Post.maximum(:id)&.next || 1
|
|
expect {
|
|
topic.move_posts(user, [non_existent_post_id], title: "new testing topic name")
|
|
}.to raise_error(Discourse::InvalidParameters)
|
|
end
|
|
|
|
it "raises an error and does not create a topic if no posts were moved" do
|
|
Topic.count.tap do |original_topic_count|
|
|
expect { topic.move_posts(user, [], title: "new testing topic name") }.to raise_error(
|
|
Discourse::InvalidParameters,
|
|
)
|
|
|
|
expect(Topic.count).to eq original_topic_count
|
|
end
|
|
end
|
|
end
|
|
|
|
context "when successfully moved" do
|
|
before do
|
|
TopicUser.update_last_read(user, topic.id, p4.post_number, p4.post_number, 0)
|
|
TopicLink.extract_from(p2)
|
|
end
|
|
|
|
def create_post_timing(post, user, msecs)
|
|
PostTiming.create!(
|
|
topic_id: post.topic_id,
|
|
user_id: user.id,
|
|
post_number: post.post_number,
|
|
msecs: msecs,
|
|
)
|
|
end
|
|
|
|
context "with post replies" do
|
|
describe "when a post with replies is moved" do
|
|
it "should update post replies correctly" do
|
|
topic.move_posts(
|
|
user,
|
|
[p2.id],
|
|
title: "GOT is a very addictive show",
|
|
category_id: category.id,
|
|
)
|
|
|
|
expect(p2.reload.replies).to eq([])
|
|
end
|
|
|
|
it "doesn't raise errors with deleted replies" do
|
|
p4.trash!
|
|
topic.move_posts(
|
|
user,
|
|
[p2.id],
|
|
title: "GOT is a very addictive show",
|
|
category_id: category.id,
|
|
)
|
|
|
|
expect(p2.reload.replies).to eq([])
|
|
end
|
|
end
|
|
|
|
describe "when replies of a post have been moved" do
|
|
it "should update post replies correctly" do
|
|
p5 =
|
|
Fabricate(
|
|
:post,
|
|
topic: topic,
|
|
reply_to_post_number: p2.post_number,
|
|
user: another_user,
|
|
)
|
|
|
|
p2.replies << p5
|
|
|
|
topic.move_posts(
|
|
user,
|
|
[p4.id],
|
|
title: "GOT is a very addictive show",
|
|
category_id: category.id,
|
|
)
|
|
|
|
expect(p2.reload.replies).to eq([p5])
|
|
end
|
|
end
|
|
|
|
context "when only one reply is left behind" do
|
|
it "should update post replies correctly" do
|
|
p5 =
|
|
Fabricate(
|
|
:post,
|
|
topic: topic,
|
|
reply_to_post_number: p2.post_number,
|
|
user: another_user,
|
|
)
|
|
|
|
p2.replies << p5
|
|
|
|
topic.move_posts(
|
|
user,
|
|
[p2.id, p4.id],
|
|
title: "GOT is a very addictive show",
|
|
category_id: category.id,
|
|
)
|
|
|
|
expect(p2.reload.replies).to eq([p4])
|
|
end
|
|
end
|
|
end
|
|
|
|
context "when moved to a new topic" do
|
|
it "works correctly" do
|
|
topic.expects(:add_moderator_post).once
|
|
new_topic =
|
|
topic.move_posts(
|
|
user,
|
|
[p2.id, p4.id],
|
|
title: "new testing topic name",
|
|
category_id: category.id,
|
|
tags: %w[tag1 tag2],
|
|
)
|
|
|
|
expect(
|
|
TopicUser.find_by(user_id: user.id, topic_id: topic.id).last_read_post_number,
|
|
).to eq(p3.post_number)
|
|
|
|
expect(new_topic).to be_present
|
|
expect(new_topic.featured_user1_id).to eq(p4.user_id)
|
|
expect(new_topic.like_count).to eq(1)
|
|
|
|
expect(new_topic.category).to eq(category)
|
|
expect(new_topic.tags.pluck(:name)).to contain_exactly("tag1", "tag2")
|
|
expect(topic.featured_user1_id).to be_blank
|
|
expect(new_topic.posts.by_post_number).to match_array([p2, p4])
|
|
|
|
new_topic.reload
|
|
expect(new_topic.posts_count).to eq(2)
|
|
expect(new_topic.highest_post_number).to eq(2)
|
|
|
|
p4.reload
|
|
expect(new_topic.last_post_user_id).to eq(p4.user_id)
|
|
expect(new_topic.last_posted_at).to eq_time(p4.created_at)
|
|
expect(new_topic.bumped_at).to eq_time(Time.zone.now)
|
|
|
|
p2.reload
|
|
expect(p2.sort_order).to eq(1)
|
|
expect(p2.post_number).to eq(1)
|
|
expect(p2.topic_links.first.topic_id).to eq(new_topic.id)
|
|
|
|
expect(p4.post_number).to eq(2)
|
|
expect(p4.sort_order).to eq(2)
|
|
|
|
topic.reload
|
|
expect(topic.featured_user1_id).to be_blank
|
|
expect(topic.like_count).to eq(0)
|
|
expect(topic.posts_count).to eq(2)
|
|
expect(topic.posts.by_post_number).to match_array([p1, p3])
|
|
expect(topic.highest_post_number).to eq(p3.post_number)
|
|
|
|
# both the like and was_liked user actions should be correct
|
|
action = UserAction.find_by(user_id: another_user.id)
|
|
expect(action.target_topic_id).to eq(new_topic.id)
|
|
|
|
expect(
|
|
TopicUser.exists?(
|
|
user_id: another_user,
|
|
topic_id: new_topic.id,
|
|
notification_level: TopicUser.notification_levels[:watching],
|
|
notifications_reason_id: TopicUser.notification_reasons[:created_topic],
|
|
),
|
|
).to eq(true)
|
|
expect(TopicUser.exists?(user_id: user, topic_id: new_topic.id)).to eq(true)
|
|
end
|
|
|
|
it "moving all posts will close the topic" do
|
|
topic.expects(:add_moderator_post).twice
|
|
new_topic =
|
|
topic.move_posts(
|
|
user,
|
|
[p1.id, p2.id, p3.id, p4.id],
|
|
title: "new testing topic name",
|
|
category_id: category.id,
|
|
)
|
|
expect(new_topic).to be_present
|
|
|
|
topic.reload
|
|
expect(topic.closed).to eq(true)
|
|
end
|
|
|
|
it "does not move posts that do not belong to the existing topic" do
|
|
new_topic =
|
|
topic.move_posts(user, [p2.id, p3.id, p5.id], title: "Logan is a pretty good movie")
|
|
|
|
expect(new_topic.posts.pluck(:id).sort).to eq([p2.id, p3.id].sort)
|
|
end
|
|
|
|
it "uses default locale for moderator post" do
|
|
I18n.locale = "de"
|
|
|
|
new_topic =
|
|
topic.move_posts(
|
|
user,
|
|
[p2.id, p4.id],
|
|
title: "new testing topic name",
|
|
category_id: category.id,
|
|
)
|
|
post = Post.find_by(topic_id: topic.id, post_type: Post.types[:small_action])
|
|
|
|
expected_text =
|
|
I18n.with_locale(:en) do
|
|
I18n.t(
|
|
"move_posts.new_topic_moderator_post",
|
|
count: 2,
|
|
topic_link: "[#{new_topic.title}](#{new_topic.relative_url})",
|
|
)
|
|
end
|
|
|
|
expect(post.raw).to eq(expected_text)
|
|
end
|
|
|
|
it "does not try to move small action posts" do
|
|
small_action =
|
|
Fabricate(
|
|
:post,
|
|
topic: topic,
|
|
raw: "A small action",
|
|
post_type: Post.types[:small_action],
|
|
)
|
|
hidden_small_action = Fabricate(:post, topic: topic, post_type: Post.types[:whisper])
|
|
hidden_small_action.update_attribute(:raw, "")
|
|
new_topic =
|
|
topic.move_posts(
|
|
user,
|
|
[p2.id, p4.id, small_action.id, hidden_small_action.id],
|
|
title: "new testing topic name",
|
|
category_id: category.id,
|
|
)
|
|
|
|
expect(new_topic.posts_count).to eq(2)
|
|
expect(small_action.topic_id).to eq(topic.id)
|
|
expect(hidden_small_action.topic_id).to eq(topic.id)
|
|
|
|
moderator_post = topic.posts.last
|
|
expect(moderator_post.raw).to include("2 posts were split")
|
|
end
|
|
|
|
it "forces resulting topic owner to watch the new topic" do
|
|
new_topic =
|
|
topic.move_posts(
|
|
user,
|
|
[p2.id, p4.id],
|
|
title: "new testing topic name",
|
|
category_id: category.id,
|
|
)
|
|
|
|
expect(new_topic.posts_count).to eq(2)
|
|
|
|
expect(
|
|
TopicUser.exists?(
|
|
user_id: another_user,
|
|
topic_id: new_topic.id,
|
|
notification_level: TopicUser.notification_levels[:watching],
|
|
notifications_reason_id: TopicUser.notification_reasons[:created_topic],
|
|
),
|
|
).to eq(true)
|
|
end
|
|
|
|
it "updates existing notifications" do
|
|
n3 = Fabricate(:mentioned_notification, post: p3, user: another_user)
|
|
n4 = Fabricate(:mentioned_notification, post: p4, user: another_user)
|
|
|
|
new_topic = topic.move_posts(user, [p3.id], title: "new testing topic name")
|
|
|
|
n3 = Notification.find(n3.id)
|
|
expect(n3.topic_id).to eq(new_topic.id)
|
|
expect(n3.post_number).to eq(1)
|
|
expect(n3.data_hash[:topic_title]).to eq(new_topic.title)
|
|
|
|
n4 = Notification.find(n4.id)
|
|
expect(n4.topic_id).to eq(topic.id)
|
|
expect(n4.post_number).to eq(4)
|
|
end
|
|
|
|
it "doesn't update notifications of type 'watching_first_post'" do
|
|
n1 = Fabricate(:watching_first_post_notification, post: p1, user: another_user)
|
|
|
|
topic.move_posts(user, [p1.id], title: "new testing topic name")
|
|
|
|
n1 = Notification.find(n1.id)
|
|
expect(n1.topic_id).to eq(topic.id)
|
|
expect(n1.data_hash[:topic_title]).to eq(topic.title)
|
|
expect(n1.post_number).to eq(1)
|
|
end
|
|
|
|
it "deletes notifications for users not allowed to see the topic" do
|
|
another_admin = Fabricate(:admin)
|
|
staff_category = Fabricate(:private_category, group: Group[:staff])
|
|
user_notification = Fabricate(:mentioned_notification, post: p3, user: another_user)
|
|
admin_notification = Fabricate(:mentioned_notification, post: p3, user: another_admin)
|
|
|
|
topic.move_posts(
|
|
user,
|
|
[p3.id],
|
|
title: "new testing topic name",
|
|
category_id: staff_category.id,
|
|
)
|
|
|
|
expect(Notification.exists?(user_notification.id)).to eq(false)
|
|
expect(Notification.exists?(admin_notification.id)).to eq(true)
|
|
end
|
|
|
|
it "moves post timings" do
|
|
some_user = Fabricate(:user)
|
|
create_post_timing(p1, some_user, 500)
|
|
create_post_timing(p2, some_user, 1000)
|
|
create_post_timing(p3, some_user, 1500)
|
|
create_post_timing(p4, some_user, 750)
|
|
|
|
new_topic = topic.move_posts(user, [p1.id, p4.id], title: "new testing topic name")
|
|
|
|
expect(
|
|
PostTiming.where(topic_id: topic.id, user_id: some_user.id).pluck(
|
|
:post_number,
|
|
:msecs,
|
|
),
|
|
).to contain_exactly([1, 500], [2, 1000], [3, 1500])
|
|
|
|
expect(
|
|
PostTiming.where(topic_id: new_topic.id, user_id: some_user.id).pluck(
|
|
:post_number,
|
|
:msecs,
|
|
),
|
|
).to contain_exactly([1, 500], [2, 750])
|
|
end
|
|
|
|
it "makes sure the topic_user.bookmarked value is reflected for users in the source and destination topic" do
|
|
Jobs.run_immediately!
|
|
user1 = Fabricate(:user)
|
|
user2 = Fabricate(:user)
|
|
|
|
bookmark1 = Fabricate(:bookmark, bookmarkable: p1, user: user1)
|
|
bookmark2 = Fabricate(:bookmark, bookmarkable: p4, user: user1)
|
|
|
|
bookmark3 = Fabricate(:bookmark, bookmarkable: p3, user: user2)
|
|
bookmark4 = Fabricate(:bookmark, bookmarkable: p4, user: user2)
|
|
|
|
tu1 =
|
|
Fabricate(
|
|
:topic_user,
|
|
user: user1,
|
|
topic: p1.topic,
|
|
bookmarked: true,
|
|
notification_level: TopicUser.notification_levels[:watching],
|
|
last_read_post_number: 4,
|
|
last_emailed_post_number: 3,
|
|
)
|
|
tu2 =
|
|
Fabricate(
|
|
:topic_user,
|
|
user: user2,
|
|
topic: p1.topic,
|
|
bookmarked: true,
|
|
notification_level: TopicUser.notification_levels[:watching],
|
|
last_read_post_number: 4,
|
|
last_emailed_post_number: 3,
|
|
)
|
|
|
|
new_topic = topic.move_posts(user, [p1.id, p4.id], title: "new testing topic name")
|
|
new_topic_user1 = TopicUser.find_by(topic: new_topic, user: user1)
|
|
new_topic_user2 = TopicUser.find_by(topic: new_topic, user: user2)
|
|
|
|
original_topic_id = p1.topic_id
|
|
expect(p1.reload.topic_id).to eq(original_topic_id)
|
|
expect(p4.reload.topic_id).to eq(new_topic.id)
|
|
|
|
expect(tu1.reload.bookmarked).to eq(false)
|
|
expect(tu2.reload.bookmarked).to eq(true)
|
|
expect(new_topic_user1.bookmarked).to eq(true)
|
|
expect(new_topic_user2.bookmarked).to eq(true)
|
|
end
|
|
|
|
context "with read state and other stats per user" do
|
|
def create_topic_user(user, opts = {})
|
|
notification_level = opts.delete(:notification_level) || :regular
|
|
|
|
Fabricate(
|
|
:topic_user,
|
|
opts.merge(
|
|
notification_level: TopicUser.notification_levels[notification_level],
|
|
topic: topic,
|
|
user: user,
|
|
),
|
|
)
|
|
end
|
|
|
|
fab!(:user1) { Fabricate(:user) }
|
|
fab!(:user2) { Fabricate(:user, refresh_auto_groups: true) }
|
|
fab!(:user3) { Fabricate(:user, refresh_auto_groups: true) }
|
|
fab!(:admin1) { Fabricate(:admin) }
|
|
fab!(:admin2) { Fabricate(:admin) }
|
|
|
|
it "correctly moves topic_user records" do
|
|
create_topic_user(
|
|
user1,
|
|
last_read_post_number: 4,
|
|
last_emailed_post_number: 3,
|
|
notification_level: :tracking,
|
|
)
|
|
create_topic_user(
|
|
user2,
|
|
last_read_post_number: 2,
|
|
last_emailed_post_number: 2,
|
|
notification_level: :tracking,
|
|
)
|
|
create_topic_user(
|
|
user3,
|
|
last_read_post_number: 1,
|
|
last_emailed_post_number: 4,
|
|
notification_level: :watching,
|
|
)
|
|
|
|
p2.update!(user_id: user2.id)
|
|
new_topic = topic.move_posts(user, [p1.id, p2.id], title: "new testing topic name")
|
|
|
|
expect(TopicUser.where(topic_id: topic.id).count).to eq(4)
|
|
expect(TopicUser.find_by(topic: topic, user: user)).to have_attributes(
|
|
last_read_post_number: 4,
|
|
last_emailed_post_number: nil,
|
|
notification_level: TopicUser.notification_levels[:tracking],
|
|
)
|
|
expect(TopicUser.find_by(topic: topic, user: user1)).to have_attributes(
|
|
last_read_post_number: 4,
|
|
last_emailed_post_number: 3,
|
|
notification_level: TopicUser.notification_levels[:tracking],
|
|
)
|
|
expect(TopicUser.find_by(topic: topic, user: user2)).to have_attributes(
|
|
last_read_post_number: 2,
|
|
last_emailed_post_number: 2,
|
|
notification_level: TopicUser.notification_levels[:tracking],
|
|
)
|
|
expect(TopicUser.find_by(topic: topic, user: user3)).to have_attributes(
|
|
last_read_post_number: 1,
|
|
last_emailed_post_number: 4,
|
|
notification_level: TopicUser.notification_levels[:watching],
|
|
)
|
|
|
|
expect(TopicUser.where(topic_id: new_topic.id).count).to eq(4)
|
|
expect(TopicUser.find_by(topic: new_topic, user: user)).to have_attributes(
|
|
last_read_post_number: 1,
|
|
last_emailed_post_number: nil,
|
|
notification_level: TopicUser.notification_levels[:watching],
|
|
posted: true,
|
|
)
|
|
expect(TopicUser.find_by(topic: new_topic, user: user1)).to have_attributes(
|
|
last_read_post_number: 2,
|
|
last_emailed_post_number: 2,
|
|
notification_level: TopicUser.notification_levels[:tracking],
|
|
posted: false,
|
|
)
|
|
expect(TopicUser.find_by(topic: new_topic, user: user2)).to have_attributes(
|
|
last_read_post_number: 2,
|
|
last_emailed_post_number: 2,
|
|
notification_level: TopicUser.notification_levels[:tracking],
|
|
posted: true,
|
|
)
|
|
expect(TopicUser.find_by(topic: new_topic, user: user3)).to have_attributes(
|
|
last_read_post_number: 1,
|
|
last_emailed_post_number: 2,
|
|
notification_level: TopicUser.notification_levels[:watching],
|
|
posted: false,
|
|
)
|
|
end
|
|
end
|
|
end
|
|
|
|
context "when moved to an existing topic" do
|
|
fab!(:destination_topic) { Fabricate(:topic, user: another_user) }
|
|
fab!(:destination_op) do
|
|
Fabricate(:post, topic: destination_topic, user: another_user, created_at: 1.day.ago)
|
|
end
|
|
|
|
it "works correctly" do
|
|
topic.expects(:add_moderator_post).once
|
|
moved_to =
|
|
topic.move_posts(user, [p2.id, p4.id], destination_topic_id: destination_topic.id)
|
|
expect(moved_to).to eq(destination_topic)
|
|
|
|
# Check out new topic
|
|
moved_to.reload
|
|
expect(moved_to.posts_count).to eq(3)
|
|
expect(moved_to.highest_post_number).to eq(3)
|
|
expect(moved_to.user_id).to eq(destination_op.user_id)
|
|
expect(moved_to.like_count).to eq(1)
|
|
expect(moved_to.category_id).to eq(SiteSetting.uncategorized_category_id)
|
|
p4.reload
|
|
expect(moved_to.last_post_user_id).to eq(p4.user_id)
|
|
expect(moved_to.last_posted_at).to eq_time(p4.created_at)
|
|
expect(moved_to.bumped_at).to eq_time(Time.zone.now)
|
|
|
|
# Posts should be re-ordered
|
|
p2.reload
|
|
expect(p2.sort_order).to eq(2)
|
|
expect(p2.post_number).to eq(2)
|
|
expect(p2.topic_id).to eq(moved_to.id)
|
|
expect(p2.reply_count).to eq(1)
|
|
expect(p2.reply_to_post_number).to eq(nil)
|
|
|
|
expect(p4.post_number).to eq(3)
|
|
expect(p4.sort_order).to eq(3)
|
|
expect(p4.topic_id).to eq(moved_to.id)
|
|
expect(p4.reply_count).to eq(0)
|
|
expect(p4.reply_to_post_number).to eq(2)
|
|
|
|
# Check out the original topic
|
|
topic.reload
|
|
expect(topic.posts_count).to eq(2)
|
|
expect(topic.highest_post_number).to eq(3)
|
|
expect(topic.featured_user1_id).to be_blank
|
|
expect(topic.like_count).to eq(0)
|
|
expect(topic.posts_count).to eq(2)
|
|
expect(topic.posts.by_post_number).to match_array([p1, p3])
|
|
expect(topic.highest_post_number).to eq(p3.post_number)
|
|
|
|
# Should notify correctly
|
|
notification =
|
|
p2.user.notifications.where(notification_type: Notification.types[:moved_post]).first
|
|
|
|
expect(notification.topic_id).to eq(destination_topic.id)
|
|
expect(notification.post_number).to eq(p2.post_number)
|
|
|
|
# Should update last reads
|
|
expect(
|
|
TopicUser.find_by(user_id: user.id, topic_id: topic.id).last_read_post_number,
|
|
).to eq(p3.post_number)
|
|
end
|
|
|
|
it "moving all posts will close the topic" do
|
|
topic.expects(:add_moderator_post).twice
|
|
posts_to_move = [p1.id, p2.id, p3.id, p4.id]
|
|
moved_to =
|
|
topic.move_posts(user, posts_to_move, destination_topic_id: destination_topic.id)
|
|
expect(moved_to).to be_present
|
|
|
|
topic.reload
|
|
expect(topic).to be_closed
|
|
end
|
|
|
|
it "doesn't close the topic when not all posts were moved" do
|
|
topic.expects(:add_moderator_post).once
|
|
posts_to_move = [p2.id, p3.id]
|
|
moved_to =
|
|
topic.move_posts(user, posts_to_move, destination_topic_id: destination_topic.id)
|
|
expect(moved_to).to be_present
|
|
|
|
topic.reload
|
|
expect(topic).to_not be_closed
|
|
end
|
|
|
|
it "doesn't close the topic when all posts except the first one were moved" do
|
|
topic.expects(:add_moderator_post).once
|
|
posts_to_move = [p2.id, p3.id, p4.id]
|
|
moved_to =
|
|
topic.move_posts(user, posts_to_move, destination_topic_id: destination_topic.id)
|
|
expect(moved_to).to be_present
|
|
|
|
topic.reload
|
|
expect(topic).to_not be_closed
|
|
end
|
|
|
|
it "schedules topic deleting when all posts were moved" do
|
|
SiteSetting.delete_merged_stub_topics_after_days = 7
|
|
freeze_time
|
|
|
|
topic.expects(:add_moderator_post).twice
|
|
posts_to_move = [p1.id, p2.id, p3.id, p4.id]
|
|
moved_to =
|
|
topic.move_posts(user, posts_to_move, destination_topic_id: destination_topic.id)
|
|
expect(moved_to).to be_present
|
|
|
|
timer = topic.topic_timers.find_by(status_type: TopicTimer.types[:delete])
|
|
expect(timer).to be_present
|
|
expect(timer.execute_at).to eq_time(7.days.from_now)
|
|
end
|
|
|
|
it "doesn't schedule topic deleting when not all posts were moved" do
|
|
SiteSetting.delete_merged_stub_topics_after_days = 7
|
|
|
|
topic.expects(:add_moderator_post).once
|
|
posts_to_move = [p1.id, p2.id, p3.id]
|
|
moved_to =
|
|
topic.move_posts(user, posts_to_move, destination_topic_id: destination_topic.id)
|
|
expect(moved_to).to be_present
|
|
|
|
timer = topic.topic_timers.find_by(status_type: TopicTimer.types[:delete])
|
|
expect(timer).to be_nil
|
|
end
|
|
|
|
it "doesn't schedule topic deleting when all posts were moved if it's disabled (-1)" do
|
|
SiteSetting.delete_merged_stub_topics_after_days = -1
|
|
|
|
topic.expects(:add_moderator_post).twice
|
|
posts_to_move = [p1.id, p2.id, p3.id, p4.id]
|
|
moved_to =
|
|
topic.move_posts(user, posts_to_move, destination_topic_id: destination_topic.id)
|
|
expect(moved_to).to be_present
|
|
|
|
expect(Topic.with_deleted.find(topic.id).deleted_at).to be_nil
|
|
|
|
timer = topic.topic_timers.find_by(status_type: TopicTimer.types[:delete])
|
|
expect(timer).to be_nil
|
|
end
|
|
|
|
it "immediately deletes topic when delete_merged_stub_topics_after_days is 0" do
|
|
SiteSetting.delete_merged_stub_topics_after_days = 0
|
|
freeze_time
|
|
|
|
topic.expects(:add_moderator_post).twice
|
|
posts_to_move = [p1.id, p2.id, p3.id, p4.id]
|
|
moved_to =
|
|
topic.move_posts(user, posts_to_move, destination_topic_id: destination_topic.id)
|
|
expect(moved_to).to be_present
|
|
|
|
expect(Topic.with_deleted.find(topic.id).deleted_at).to be_present
|
|
end
|
|
|
|
it "ignores moderator posts and closes the topic if all regular posts were moved" do
|
|
add_moderator_post_to topic, Post.types[:moderator_action]
|
|
add_moderator_post_to topic, Post.types[:small_action]
|
|
|
|
posts_to_move = [p1.id, p2.id, p3.id, p4.id]
|
|
topic.move_posts(user, posts_to_move, destination_topic_id: destination_topic.id)
|
|
|
|
topic.reload
|
|
expect(topic).to be_closed
|
|
end
|
|
|
|
it "does not try to move small action posts" do
|
|
small_action =
|
|
Fabricate(
|
|
:post,
|
|
topic: topic,
|
|
raw: "A small action",
|
|
post_type: Post.types[:small_action],
|
|
)
|
|
moved_to =
|
|
topic.move_posts(
|
|
user,
|
|
[p1.id, p2.id, p3.id, p4.id, small_action.id],
|
|
destination_topic_id: destination_topic.id,
|
|
)
|
|
|
|
moved_to.reload
|
|
expect(moved_to.posts_count).to eq(5)
|
|
expect(small_action.topic_id).to eq(topic.id)
|
|
|
|
moderator_post = topic.posts.find_by(post_number: 2)
|
|
expect(moderator_post.raw).to include("4 posts were merged")
|
|
end
|
|
|
|
it "updates existing notifications" do
|
|
n3 = Fabricate(:mentioned_notification, post: p3, user: another_user)
|
|
n4 = Fabricate(:mentioned_notification, post: p4, user: another_user)
|
|
|
|
moved_to = topic.move_posts(user, [p3.id], destination_topic_id: destination_topic.id)
|
|
|
|
n3 = Notification.find(n3.id)
|
|
expect(n3.topic_id).to eq(moved_to.id)
|
|
expect(n3.post_number).to eq(2)
|
|
expect(n3.data_hash[:topic_title]).to eq(moved_to.title)
|
|
|
|
n4 = Notification.find(n4.id)
|
|
expect(n4.topic_id).to eq(topic.id)
|
|
expect(n4.post_number).to eq(4)
|
|
end
|
|
|
|
it "deletes notifications for users not allowed to see the topic" do
|
|
another_admin = Fabricate(:admin)
|
|
staff_category = Fabricate(:private_category, group: Group[:staff])
|
|
user_notification = Fabricate(:mentioned_notification, post: p3, user: another_user)
|
|
admin_notification = Fabricate(:mentioned_notification, post: p3, user: another_admin)
|
|
|
|
destination_topic.update!(category_id: staff_category.id)
|
|
topic.move_posts(user, [p3.id], destination_topic_id: destination_topic.id)
|
|
|
|
expect(Notification.exists?(user_notification.id)).to eq(false)
|
|
expect(Notification.exists?(admin_notification.id)).to eq(true)
|
|
end
|
|
|
|
context "with post timings" do
|
|
fab!(:some_user) { Fabricate(:user) }
|
|
|
|
it "successfully moves timings" do
|
|
create_post_timing(p1, some_user, 500)
|
|
create_post_timing(p2, some_user, 1000)
|
|
create_post_timing(p3, some_user, 1500)
|
|
create_post_timing(p4, some_user, 750)
|
|
|
|
moved_to =
|
|
topic.move_posts(user, [p1.id, p4.id], destination_topic_id: destination_topic.id)
|
|
|
|
expect(
|
|
PostTiming.where(topic_id: topic.id, user_id: some_user.id).pluck(
|
|
:post_number,
|
|
:msecs,
|
|
),
|
|
).to contain_exactly([1, 500], [2, 1000], [3, 1500])
|
|
|
|
expect(
|
|
PostTiming.where(topic_id: moved_to.id, user_id: some_user.id).pluck(
|
|
:post_number,
|
|
:msecs,
|
|
),
|
|
).to contain_exactly([2, 500], [3, 750])
|
|
end
|
|
|
|
it "moves timings when post timing exists in destination topic" do
|
|
PostTiming.create!(
|
|
topic_id: destination_topic.id,
|
|
user_id: some_user.id,
|
|
post_number: 2,
|
|
msecs: 800,
|
|
)
|
|
create_post_timing(p1, some_user, 500)
|
|
|
|
moved_to = topic.move_posts(user, [p1.id], destination_topic_id: destination_topic.id)
|
|
|
|
expect(
|
|
PostTiming.where(topic_id: moved_to.id, user_id: some_user.id).pluck(
|
|
:post_number,
|
|
:msecs,
|
|
),
|
|
).to contain_exactly([2, 500])
|
|
end
|
|
end
|
|
|
|
it "updates topic_user.liked values for both source and destination topics" do
|
|
expect(TopicUser.find_by(topic: topic, user: user).liked).to eq(false)
|
|
|
|
like =
|
|
Fabricate(
|
|
:post_action,
|
|
post: p3,
|
|
user: user,
|
|
post_action_type_id: PostActionType.types[:like],
|
|
)
|
|
expect(TopicUser.find_by(topic: topic, user: user).liked).to eq(true)
|
|
|
|
expect(TopicUser.find_by(topic: destination_topic, user: user)).to eq(nil)
|
|
topic.move_posts(user, [p3.id], destination_topic_id: destination_topic.id)
|
|
|
|
expect(TopicUser.find_by(topic: topic, user: user).liked).to eq(false)
|
|
expect(TopicUser.find_by(topic: destination_topic, user: user).liked).to eq(true)
|
|
end
|
|
|
|
it "copies the post revisions from first post to the new post" do
|
|
p1.revise(another_user, { raw: "A different raw content" })
|
|
|
|
moved_to = topic.move_posts(user, [p1.id], destination_topic_id: destination_topic.id)
|
|
new_post = moved_to.posts.last
|
|
|
|
expect(new_post.id).not_to eq(p1.id)
|
|
expect(new_post.version).to eq(2)
|
|
expect(new_post.public_version).to eq(2)
|
|
expect(new_post.post_revisions.size).to eq(1)
|
|
end
|
|
|
|
context "with subfolder installs" do
|
|
before { set_subfolder "/forum" }
|
|
|
|
it "creates a small action with correct post url" do
|
|
moved_to = topic.move_posts(user, [p2.id], destination_topic_id: destination_topic.id)
|
|
small_action = topic.posts.last
|
|
|
|
expect(small_action.post_type).to eq(Post.types[:small_action])
|
|
|
|
expected_text =
|
|
I18n.t(
|
|
"move_posts.existing_topic_moderator_post",
|
|
count: 1,
|
|
topic_link: "[#{moved_to.title}](#{p2.reload.relative_url})",
|
|
locale: :en,
|
|
)
|
|
|
|
expect(small_action.raw).to eq(expected_text)
|
|
end
|
|
end
|
|
|
|
context "with read state and other stats per user" do
|
|
def create_topic_user(user, topic, opts = {})
|
|
notification_level = opts.delete(:notification_level) || :regular
|
|
|
|
Fabricate(
|
|
:topic_user,
|
|
opts.merge(
|
|
notification_level: TopicUser.notification_levels[notification_level],
|
|
topic: topic,
|
|
user: user,
|
|
),
|
|
)
|
|
end
|
|
|
|
fab!(:user1) { Fabricate(:user) }
|
|
fab!(:user2) { Fabricate(:user) }
|
|
fab!(:user3) { Fabricate(:user) }
|
|
fab!(:admin1) { Fabricate(:admin) }
|
|
fab!(:admin2) { Fabricate(:admin) }
|
|
|
|
it "leaves post numbers unchanged when they were lower then the topic's highest post number" do
|
|
Fabricate(:post, topic: destination_topic)
|
|
Fabricate(:whisper, topic: destination_topic)
|
|
|
|
destination_topic.reload
|
|
expect(destination_topic.highest_post_number).to eq(2)
|
|
expect(destination_topic.highest_staff_post_number).to eq(3)
|
|
|
|
create_topic_user(user1, topic, last_read_post_number: 3, last_emailed_post_number: 3)
|
|
create_topic_user(
|
|
user1,
|
|
destination_topic,
|
|
last_read_post_number: 1,
|
|
last_emailed_post_number: 1,
|
|
)
|
|
|
|
create_topic_user(user2, topic, last_read_post_number: 3, last_emailed_post_number: 3)
|
|
create_topic_user(
|
|
user2,
|
|
destination_topic,
|
|
last_read_post_number: 2,
|
|
last_emailed_post_number: 2,
|
|
)
|
|
|
|
create_topic_user(
|
|
admin1,
|
|
topic,
|
|
last_read_post_number: 3,
|
|
last_emailed_post_number: 3,
|
|
)
|
|
create_topic_user(
|
|
admin1,
|
|
destination_topic,
|
|
last_read_post_number: 2,
|
|
last_emailed_post_number: 1,
|
|
)
|
|
|
|
create_topic_user(
|
|
admin2,
|
|
topic,
|
|
last_read_post_number: 3,
|
|
last_emailed_post_number: 3,
|
|
)
|
|
create_topic_user(
|
|
admin2,
|
|
destination_topic,
|
|
last_read_post_number: 3,
|
|
last_emailed_post_number: 3,
|
|
)
|
|
|
|
moved_to_topic =
|
|
topic.move_posts(user, [p1.id, p2.id], destination_topic_id: destination_topic.id)
|
|
|
|
expect(TopicUser.find_by(topic: moved_to_topic, user: user1)).to have_attributes(
|
|
last_read_post_number: 1,
|
|
last_emailed_post_number: 1,
|
|
)
|
|
|
|
expect(TopicUser.find_by(topic: moved_to_topic, user: user2)).to have_attributes(
|
|
last_read_post_number: 5,
|
|
last_emailed_post_number: 5,
|
|
)
|
|
|
|
expect(TopicUser.find_by(topic: moved_to_topic, user: admin1)).to have_attributes(
|
|
last_read_post_number: 2,
|
|
last_emailed_post_number: 1,
|
|
)
|
|
|
|
expect(TopicUser.find_by(topic: moved_to_topic, user: admin2)).to have_attributes(
|
|
last_read_post_number: 5,
|
|
last_emailed_post_number: 5,
|
|
)
|
|
end
|
|
|
|
it "correctly updates existing topic_user records" do
|
|
destination_topic.update!(created_at: 1.day.ago)
|
|
|
|
original_topic_user1 =
|
|
create_topic_user(
|
|
user1,
|
|
topic,
|
|
last_read_post_number: 5,
|
|
first_visited_at: 5.hours.ago,
|
|
last_visited_at: 30.minutes.ago,
|
|
notification_level: :tracking,
|
|
).reload
|
|
destination_topic_user1 =
|
|
create_topic_user(
|
|
user1,
|
|
destination_topic,
|
|
last_read_post_number: 5,
|
|
first_visited_at: 7.hours.ago,
|
|
last_visited_at: 2.hours.ago,
|
|
notification_level: :watching,
|
|
).reload
|
|
|
|
original_topic_user2 =
|
|
create_topic_user(
|
|
user2,
|
|
topic,
|
|
last_read_post_number: 5,
|
|
first_visited_at: 3.hours.ago,
|
|
last_visited_at: 1.hour.ago,
|
|
notification_level: :watching,
|
|
).reload
|
|
destination_topic_user2 =
|
|
create_topic_user(
|
|
user2,
|
|
destination_topic,
|
|
last_read_post_number: 5,
|
|
first_visited_at: 2.hours.ago,
|
|
last_visited_at: 1.hour.ago,
|
|
notification_level: :tracking,
|
|
).reload
|
|
|
|
new_topic =
|
|
topic.move_posts(user, [p1.id, p2.id], destination_topic_id: destination_topic.id)
|
|
|
|
expect(TopicUser.find_by(topic: new_topic, user: user)).to have_attributes(
|
|
notification_level: TopicUser.notification_levels[:tracking],
|
|
posted: true,
|
|
)
|
|
|
|
expect(TopicUser.find_by(topic: new_topic, user: user1)).to have_attributes(
|
|
first_visited_at: destination_topic_user1.first_visited_at,
|
|
last_visited_at: original_topic_user1.last_visited_at,
|
|
notification_level: destination_topic_user1.notification_level,
|
|
posted: false,
|
|
)
|
|
|
|
expect(TopicUser.find_by(topic: new_topic, user: user2)).to have_attributes(
|
|
first_visited_at: original_topic_user2.first_visited_at,
|
|
last_visited_at: destination_topic_user2.last_visited_at,
|
|
notification_level: destination_topic_user2.notification_level,
|
|
posted: false,
|
|
)
|
|
end
|
|
end
|
|
end
|
|
|
|
context "when moved to a message" do
|
|
it "works correctly" do
|
|
topic.expects(:add_moderator_post).once
|
|
new_topic =
|
|
topic.move_posts(
|
|
user,
|
|
[p2.id, p4.id],
|
|
title: "new testing topic name",
|
|
archetype: "private_message",
|
|
)
|
|
|
|
expect(
|
|
TopicUser.find_by(user_id: user.id, topic_id: topic.id).last_read_post_number,
|
|
).to eq(p3.post_number)
|
|
|
|
expect(new_topic).to be_present
|
|
expect(new_topic.featured_user1_id).to eq(p4.user_id)
|
|
expect(new_topic.like_count).to eq(1)
|
|
|
|
expect(new_topic.archetype).to eq(Archetype.private_message)
|
|
expect(topic.featured_user1_id).to be_blank
|
|
expect(new_topic.posts.by_post_number).to match_array([p2, p4])
|
|
|
|
new_topic.reload
|
|
expect(new_topic.posts_count).to eq(2)
|
|
expect(new_topic.highest_post_number).to eq(2)
|
|
|
|
p4.reload
|
|
expect(new_topic.last_post_user_id).to eq(p4.user_id)
|
|
expect(new_topic.last_posted_at).to eq_time(p4.created_at)
|
|
expect(new_topic.bumped_at).to eq_time(Time.zone.now)
|
|
|
|
p2.reload
|
|
expect(p2.sort_order).to eq(1)
|
|
expect(p2.post_number).to eq(1)
|
|
expect(p2.topic_links.first.topic_id).to eq(new_topic.id)
|
|
|
|
expect(p4.post_number).to eq(2)
|
|
expect(p4.sort_order).to eq(2)
|
|
|
|
topic.reload
|
|
expect(topic.featured_user1_id).to be_blank
|
|
expect(topic.like_count).to eq(0)
|
|
expect(topic.posts_count).to eq(2)
|
|
expect(topic.posts.by_post_number).to match_array([p1, p3])
|
|
expect(topic.highest_post_number).to eq(p3.post_number)
|
|
|
|
# both the like and was_liked user actions should be correct
|
|
action = UserAction.find_by(user_id: another_user.id)
|
|
expect(action.target_topic_id).to eq(new_topic.id)
|
|
|
|
expect(
|
|
TopicUser.exists?(
|
|
user_id: another_user,
|
|
topic_id: new_topic.id,
|
|
notification_level: TopicUser.notification_levels[:watching],
|
|
notifications_reason_id: TopicUser.notification_reasons[:created_topic],
|
|
),
|
|
).to eq(true)
|
|
expect(
|
|
TopicUser.exists?(
|
|
user_id: user,
|
|
topic_id: new_topic.id,
|
|
notification_level: TopicUser.notification_levels[:watching],
|
|
notifications_reason_id: TopicUser.notification_reasons[:created_post],
|
|
),
|
|
).to eq(true)
|
|
end
|
|
end
|
|
|
|
shared_examples "moves email related stuff" do
|
|
it "moves incoming email" do
|
|
Fabricate(:incoming_email, user: old_post.user, topic: old_post.topic, post: old_post)
|
|
|
|
new_topic = topic.move_posts(user, [old_post.id], title: "new testing topic name")
|
|
new_post = new_topic.first_post
|
|
email = new_post.incoming_email
|
|
|
|
expect(email).to be_present
|
|
expect(email.topic_id).to eq(new_topic.id)
|
|
expect(email.post_id).to eq(new_post.id)
|
|
|
|
unless old_post.id == new_post.id
|
|
expect(old_post.reload.incoming_email).to_not be_present
|
|
end
|
|
end
|
|
|
|
it "moves email log entries" do
|
|
old_topic = old_post.topic
|
|
|
|
2.times do
|
|
Fabricate(:email_log, user: old_post.user, post: old_post, email_type: :mailing_list)
|
|
end
|
|
|
|
some_post = Fabricate(:post)
|
|
|
|
Fabricate(:email_log, user: some_post.user, post: some_post, email_type: :mailing_list)
|
|
|
|
expect(EmailLog.where(post_id: old_post.id).count).to eq(2)
|
|
|
|
new_topic = old_topic.move_posts(user, [old_post.id], title: "new testing topic name")
|
|
|
|
new_post = new_topic.first_post
|
|
|
|
expect(EmailLog.where(post_id: new_post.id).count).to eq(2)
|
|
end
|
|
|
|
it "preserves post attributes" do
|
|
old_post.update_columns(
|
|
cook_method: Post.cook_methods[:email],
|
|
via_email: true,
|
|
raw_email: "raw email content",
|
|
)
|
|
|
|
new_topic =
|
|
old_post.topic.move_posts(user, [old_post.id], title: "new testing topic name")
|
|
new_post = new_topic.first_post
|
|
|
|
expect(new_post.cook_method).to eq(Post.cook_methods[:email])
|
|
expect(new_post.via_email).to eq(true)
|
|
expect(new_post.raw_email).to eq("raw email content")
|
|
end
|
|
end
|
|
|
|
context "when moving the first post" do
|
|
it "copies the OP, doesn't delete it" do
|
|
topic.expects(:add_moderator_post).once
|
|
new_topic = topic.move_posts(user, [p1.id, p2.id], title: "new testing topic name")
|
|
|
|
expect(new_topic).to be_present
|
|
expect(new_topic.posts.by_post_number.first.raw).to eq(p1.raw)
|
|
expect(new_topic.posts_count).to eq(2)
|
|
expect(new_topic.highest_post_number).to eq(2)
|
|
|
|
# First post didn't move
|
|
p1.reload
|
|
expect(p1.sort_order).to eq(1)
|
|
expect(p1.post_number).to eq(1)
|
|
expect(p1.topic_id).to eq(topic.id)
|
|
expect(p1.reply_count).to eq(1)
|
|
|
|
# New first post
|
|
new_first = new_topic.posts.where(post_number: 1).first
|
|
expect(new_first.reply_count).to eq(1)
|
|
expect(new_first.created_at).to eq_time(p1.created_at)
|
|
|
|
# Second post is in a new topic
|
|
p2.reload
|
|
expect(p2.post_number).to eq(2)
|
|
expect(p2.sort_order).to eq(2)
|
|
expect(p2.topic_id).to eq(new_topic.id)
|
|
expect(p2.reply_to_post_number).to eq(1)
|
|
expect(p2.reply_count).to eq(0)
|
|
|
|
topic.reload
|
|
expect(topic.posts.by_post_number).to match_array([p1, p3, p4])
|
|
expect(topic.highest_post_number).to eq(p4.post_number)
|
|
|
|
# updates replies for posts moved to same topic
|
|
expect(PostReply.where(reply_post_id: p2.id).pluck(:post_id)).to contain_exactly(
|
|
new_first.id,
|
|
)
|
|
|
|
# leaves replies to the first post of the original topic unchanged
|
|
expect(PostReply.where(reply_post_id: p3.id).pluck(:post_id)).to contain_exactly(p1.id)
|
|
end
|
|
|
|
it "preserves post actions in the new post" do
|
|
PostActionCreator.like(another_user, p1)
|
|
|
|
new_topic = topic.move_posts(user, [p1.id], title: "new testing topic name")
|
|
new_post = new_topic.posts.where(post_number: 1).first
|
|
|
|
expect(new_topic.like_count).to eq(1)
|
|
expect(new_post.like_count).to eq(1)
|
|
expect(new_post.post_actions.size).to eq(1)
|
|
end
|
|
|
|
it "preserves the custom_fields in the new post" do
|
|
custom_fields = { "some_field" => "payload" }
|
|
p1.custom_fields = custom_fields
|
|
p1.save_custom_fields
|
|
|
|
new_topic = topic.move_posts(user, [p1.id], title: "new testing topic name")
|
|
|
|
expect(new_topic.first_post.custom_fields).to eq(custom_fields)
|
|
end
|
|
|
|
it "preserves the post revisions in the new post" do
|
|
p1.revise(another_user, { raw: "A different raw content" })
|
|
|
|
new_topic = topic.move_posts(user, [p1.id], title: "new testing topic name")
|
|
new_post = new_topic.posts.where(post_number: 1).first
|
|
|
|
expect(new_post.id).not_to eq(p1.id)
|
|
expect(new_post.version).to eq(2)
|
|
expect(new_post.public_version).to eq(2)
|
|
expect(new_post.post_revisions.size).to eq(1)
|
|
end
|
|
|
|
include_examples "moves email related stuff" do
|
|
let!(:old_post) { p1 }
|
|
end
|
|
end
|
|
|
|
context "when moving replies" do
|
|
include_examples "moves email related stuff" do
|
|
let!(:old_post) { p3 }
|
|
end
|
|
end
|
|
|
|
context "when moving to an existing topic with a deleted post" do
|
|
before { topic.expects(:add_moderator_post) }
|
|
|
|
fab!(:destination_topic) { Fabricate(:topic, user: user) }
|
|
fab!(:destination_op) { Fabricate(:post, topic: destination_topic, user: user) }
|
|
fab!(:destination_deleted_reply) do
|
|
Fabricate(:post, topic: destination_topic, user: another_user)
|
|
end
|
|
let(:moved_to) do
|
|
topic.move_posts(user, [p2.id, p4.id], destination_topic_id: destination_topic.id)
|
|
end
|
|
|
|
it "works correctly" do
|
|
destination_deleted_reply.trash!
|
|
|
|
expect(moved_to).to eq(destination_topic)
|
|
|
|
# Check out new topic
|
|
moved_to.reload
|
|
expect(moved_to.posts_count).to eq(3)
|
|
expect(moved_to.highest_post_number).to eq(4)
|
|
|
|
# Posts should be re-ordered
|
|
p2.reload
|
|
expect(p2.sort_order).to eq(3)
|
|
expect(p2.post_number).to eq(3)
|
|
expect(p2.topic_id).to eq(moved_to.id)
|
|
expect(p2.reply_count).to eq(1)
|
|
expect(p2.reply_to_post_number).to eq(nil)
|
|
|
|
p4.reload
|
|
expect(p4.post_number).to eq(4)
|
|
expect(p4.sort_order).to eq(4)
|
|
expect(p4.topic_id).to eq(moved_to.id)
|
|
expect(p4.reply_to_post_number).to eq(p2.post_number)
|
|
end
|
|
end
|
|
|
|
context "when moving to an existing closed topic" do
|
|
fab!(:destination_topic) { Fabricate(:topic, closed: true) }
|
|
|
|
it "works correctly for admin" do
|
|
moved_to =
|
|
topic.move_posts(admin, [p1.id, p2.id], destination_topic_id: destination_topic.id)
|
|
expect(moved_to).to be_present
|
|
|
|
moved_to.reload
|
|
expect(moved_to.posts_count).to eq(2)
|
|
expect(moved_to.highest_post_number).to eq(2)
|
|
end
|
|
end
|
|
|
|
context "when moving chronologically to an existing topic" do
|
|
fab!(:destination_topic) { Fabricate(:topic, user: user) }
|
|
fab!(:destination_op) do
|
|
Fabricate(
|
|
:post,
|
|
topic: destination_topic,
|
|
user: user,
|
|
created_at: p2.created_at + 30.minutes,
|
|
)
|
|
end
|
|
|
|
it "works correctly with post_number gap in destination" do
|
|
destination_p6 =
|
|
Fabricate(
|
|
:post,
|
|
topic: destination_topic,
|
|
user: another_user,
|
|
created_at: p3.created_at + 5.minutes,
|
|
post_number: 6,
|
|
reply_count: 1,
|
|
)
|
|
destination_p7 =
|
|
Fabricate(
|
|
:post,
|
|
topic: destination_topic,
|
|
user: another_user,
|
|
created_at: p3.created_at + 10.minutes,
|
|
reply_to_post_number: destination_p6.post_number,
|
|
)
|
|
destination_p6.replies << destination_p7
|
|
|
|
# after: p2(-2h) destination_op p3(-1h) destination_p6 destination_p7 p4(-45min)
|
|
|
|
topic.expects(:add_moderator_post).once
|
|
moved_to =
|
|
topic.move_posts(
|
|
user,
|
|
[p2.id, p3.id, p4.id],
|
|
destination_topic_id: destination_topic.id,
|
|
chronological_order: true,
|
|
)
|
|
expect(moved_to).to eq(destination_topic)
|
|
|
|
# Check out destination topic
|
|
moved_to.reload
|
|
expect(moved_to.posts_count).to eq(6)
|
|
expect(moved_to.highest_post_number).to eq(8)
|
|
expect(moved_to.user_id).to eq(p2.user_id)
|
|
expect(moved_to.like_count).to eq(1)
|
|
expect(moved_to.category_id).to eq(SiteSetting.uncategorized_category_id)
|
|
p4.reload
|
|
expect(moved_to.last_post_user_id).to eq(p4.user_id)
|
|
expect(moved_to.last_posted_at).to eq_time(p4.created_at)
|
|
|
|
# Posts should be re-ordered
|
|
p2.reload
|
|
expect(p2.sort_order).to eq(1)
|
|
expect(p2.post_number).to eq(1)
|
|
expect(p2.topic_id).to eq(moved_to.id)
|
|
expect(p2.reply_count).to eq(1)
|
|
expect(p2.reply_to_post_number).to eq(nil)
|
|
|
|
destination_op.reload
|
|
expect(destination_op.sort_order).to eq(2)
|
|
expect(destination_op.post_number).to eq(2)
|
|
expect(destination_op.reply_count).to eq(0)
|
|
expect(destination_op.reply_to_post_number).to eq(nil)
|
|
|
|
p3.reload
|
|
expect(p3.post_number).to eq(3)
|
|
expect(p3.sort_order).to eq(3)
|
|
expect(p3.topic_id).to eq(moved_to.id)
|
|
expect(p3.reply_count).to eq(0)
|
|
expect(p3.reply_to_post_number).to eq(nil)
|
|
|
|
destination_p6.reload
|
|
expect(destination_p6.post_number).to eq(6)
|
|
expect(destination_p6.sort_order).to eq(6)
|
|
expect(destination_p6.reply_count).to eq(1)
|
|
expect(destination_p6.reply_to_post_number).to eq(nil)
|
|
|
|
destination_p7.reload
|
|
expect(destination_p7.post_number).to eq(7)
|
|
expect(destination_p7.sort_order).to eq(7)
|
|
expect(destination_p7.reply_count).to eq(0)
|
|
expect(destination_p7.reply_to_post_number).to eq(6)
|
|
|
|
p4.reload
|
|
expect(p4.post_number).to eq(8)
|
|
expect(p4.sort_order).to eq(8)
|
|
expect(p4.topic_id).to eq(moved_to.id)
|
|
expect(p4.reply_count).to eq(0)
|
|
expect(p4.reply_to_post_number).to eq(1)
|
|
|
|
# Check out the original topic
|
|
topic.reload
|
|
expect(topic.posts_count).to eq(1)
|
|
expect(topic.featured_user1_id).to be_blank
|
|
expect(topic.like_count).to eq(0)
|
|
expect(topic.posts.by_post_number).to match_array([p1])
|
|
expect(topic.highest_post_number).to eq(p1.post_number)
|
|
|
|
# Should notify correctly
|
|
notification =
|
|
p2.user.notifications.where(notification_type: Notification.types[:moved_post]).first
|
|
|
|
expect(notification.topic_id).to eq(destination_topic.id)
|
|
expect(notification.post_number).to eq(p2.post_number)
|
|
|
|
# Should update last reads
|
|
expect(
|
|
TopicUser.find_by(user_id: user.id, topic_id: topic.id).last_read_post_number,
|
|
).to eq(p1.post_number)
|
|
end
|
|
|
|
it "works correctly keeping replies in destination" do
|
|
destination_p2 =
|
|
Fabricate(
|
|
:post,
|
|
topic: destination_topic,
|
|
user: another_user,
|
|
created_at: p3.created_at - 10.minutes,
|
|
post_number: 2,
|
|
reply_count: 1,
|
|
)
|
|
destination_p3 =
|
|
Fabricate(
|
|
:post,
|
|
topic: destination_topic,
|
|
user: another_user,
|
|
created_at: p4.created_at + 5.minutes,
|
|
reply_to_post_number: destination_p2.post_number,
|
|
reply_count: 1,
|
|
)
|
|
destination_p4 =
|
|
Fabricate(
|
|
:post,
|
|
topic: destination_topic,
|
|
user: another_user,
|
|
created_at: p4.created_at + 10.minutes,
|
|
reply_to_post_number: destination_p3.post_number,
|
|
)
|
|
destination_p2.replies << destination_p3
|
|
destination_p3.replies << destination_p4
|
|
|
|
# after: destination_op destination_p2 p3(-1h) p4(-45min) destination_p3 destination_p4
|
|
|
|
topic.expects(:add_moderator_post).once
|
|
moved_to =
|
|
topic.move_posts(
|
|
user,
|
|
[p3.id, p4.id],
|
|
destination_topic_id: destination_topic.id,
|
|
chronological_order: true,
|
|
)
|
|
expect(moved_to).to eq(destination_topic)
|
|
|
|
# Check out destination topic
|
|
moved_to.reload
|
|
expect(moved_to.posts_count).to eq(6)
|
|
expect(moved_to.highest_post_number).to eq(6)
|
|
expect(moved_to.user_id).to eq(destination_op.user_id)
|
|
expect(moved_to.like_count).to eq(1)
|
|
expect(moved_to.category_id).to eq(SiteSetting.uncategorized_category_id)
|
|
|
|
# Posts should be re-ordered
|
|
destination_op.reload
|
|
expect(destination_op.sort_order).to eq(1)
|
|
expect(destination_op.post_number).to eq(1)
|
|
|
|
destination_p2.reload
|
|
expect(destination_p2.post_number).to eq(2)
|
|
expect(destination_p2.sort_order).to eq(2)
|
|
expect(destination_p2.reply_count).to eq(1)
|
|
expect(destination_p2.reply_to_post_number).to eq(nil)
|
|
|
|
p3.reload
|
|
expect(p3.post_number).to eq(3)
|
|
expect(p3.sort_order).to eq(3)
|
|
expect(p3.topic_id).to eq(moved_to.id)
|
|
expect(p3.reply_count).to eq(0)
|
|
expect(p3.reply_to_post_number).to eq(nil)
|
|
|
|
p4.reload
|
|
expect(p4.post_number).to eq(4)
|
|
expect(p4.sort_order).to eq(4)
|
|
expect(p4.topic_id).to eq(moved_to.id)
|
|
expect(p4.reply_count).to eq(0)
|
|
expect(p4.reply_to_post_number).to eq(nil)
|
|
|
|
destination_p3.reload
|
|
expect(destination_p3.post_number).to eq(5)
|
|
expect(destination_p3.sort_order).to eq(5)
|
|
expect(destination_p3.reply_count).to eq(1)
|
|
expect(destination_p3.reply_to_post_number).to eq(destination_p2.post_number)
|
|
|
|
destination_p4.reload
|
|
expect(destination_p4.post_number).to eq(6)
|
|
expect(destination_p4.sort_order).to eq(6)
|
|
expect(destination_p4.reply_count).to eq(0)
|
|
expect(destination_p4.reply_to_post_number).to eq(destination_p3.post_number)
|
|
|
|
# Check out the original topic
|
|
topic.reload
|
|
expect(topic.posts_count).to eq(2)
|
|
expect(topic.featured_user1_id).to eq(p2.user_id)
|
|
expect(topic.like_count).to eq(0)
|
|
expect(topic.posts.by_post_number).to match_array([p1, p2])
|
|
expect(topic.highest_post_number).to eq(p2.post_number)
|
|
|
|
# Should update last reads
|
|
expect(
|
|
TopicUser.find_by(user_id: user.id, topic_id: topic.id).last_read_post_number,
|
|
).to eq(p2.post_number)
|
|
end
|
|
|
|
it "works correctly when moving the first post" do
|
|
# forcing a different user_id than p1.user_id
|
|
destination_topic.update_column(:user_id, another_user.id)
|
|
destination_op.update_column(:user_id, another_user.id)
|
|
|
|
# after: p1(-3h) destination_op
|
|
|
|
topic.expects(:add_moderator_post).once
|
|
moved_to =
|
|
topic.move_posts(
|
|
user,
|
|
[p1.id],
|
|
destination_topic_id: destination_topic.id,
|
|
chronological_order: true,
|
|
)
|
|
expect(moved_to).to eq(destination_topic)
|
|
|
|
# Check out destination topic
|
|
moved_to.reload
|
|
expect(moved_to.posts_count).to eq(2)
|
|
expect(moved_to.highest_post_number).to eq(2)
|
|
expect(moved_to.user_id).to eq(p1.user_id)
|
|
expect(moved_to.like_count).to eq(0)
|
|
|
|
# First post didn't move
|
|
p1.reload
|
|
expect(p1.sort_order).to eq(1)
|
|
expect(p1.post_number).to eq(1)
|
|
expect(p1.topic_id).to eq(topic.id)
|
|
expect(p1.reply_count).to eq(2)
|
|
|
|
# New first post
|
|
new_first = moved_to.posts.where(post_number: 1).first
|
|
expect(new_first.sort_order).to eq(1)
|
|
expect(new_first.reply_count).to eq(0)
|
|
expect(new_first.created_at).to eq_time(p1.created_at)
|
|
|
|
destination_op.reload
|
|
expect(destination_op.sort_order).to eq(2)
|
|
expect(destination_op.post_number).to eq(2)
|
|
|
|
# Check out the original topic
|
|
topic.reload
|
|
expect(topic.posts_count).to eq(4)
|
|
expect(topic.featured_user1_id).to eq(p2.user_id)
|
|
expect(topic.like_count).to eq(1)
|
|
expect(topic.posts.by_post_number).to match_array([p1, p2, p3, p4])
|
|
expect(topic.highest_post_number).to eq(p4.post_number)
|
|
end
|
|
|
|
it "correctly remaps quotes for shifted posts on destination topic" do
|
|
destination_p8 =
|
|
Fabricate(
|
|
:post,
|
|
topic: destination_topic,
|
|
user: another_user,
|
|
created_at: p6.created_at + 10.minutes,
|
|
)
|
|
|
|
raw = <<~RAW
|
|
[quote="dan, post:#{destination_p8.post_number}, topic:#{destination_p8.topic_id}, full:true"]
|
|
some quote from the other post
|
|
[/quote]
|
|
|
|
the quote above should be updated with new post number and topic id
|
|
RAW
|
|
|
|
p3.update!(raw: raw)
|
|
p3.rebake!
|
|
|
|
expect {
|
|
topic.move_posts(
|
|
user,
|
|
[p6.id],
|
|
destination_topic_id: destination_topic.id,
|
|
chronological_order: true,
|
|
)
|
|
}.to change { p6.reload.topic_id }.and change {
|
|
destination_p8.reload.post_number
|
|
}.and change { p3.reload.raw }.and change { p3.baked_version }.to(nil)
|
|
|
|
expect(p3.raw).to include(
|
|
"post:#{destination_p8.post_number}, topic:#{destination_p8.topic_id}",
|
|
)
|
|
end
|
|
|
|
it "moving all posts will close the topic" do
|
|
topic.expects(:add_moderator_post).twice
|
|
posts_to_move = [p1.id, p2.id, p3.id, p4.id]
|
|
moved_to =
|
|
topic.move_posts(
|
|
user,
|
|
posts_to_move,
|
|
destination_topic_id: destination_topic.id,
|
|
chronological_order: true,
|
|
)
|
|
expect(moved_to).to be_present
|
|
|
|
topic.reload
|
|
expect(topic).to be_closed
|
|
end
|
|
|
|
it "doesn't close the topic when not all posts were moved" do
|
|
topic.expects(:add_moderator_post).once
|
|
posts_to_move = [p2.id, p3.id]
|
|
moved_to =
|
|
topic.move_posts(
|
|
user,
|
|
posts_to_move,
|
|
destination_topic_id: destination_topic.id,
|
|
chronological_order: true,
|
|
)
|
|
expect(moved_to).to be_present
|
|
|
|
topic.reload
|
|
expect(topic).to_not be_closed
|
|
end
|
|
|
|
it "doesn't close the topic when all posts except the first one were moved" do
|
|
topic.expects(:add_moderator_post).once
|
|
posts_to_move = [p2.id, p3.id, p4.id]
|
|
moved_to =
|
|
topic.move_posts(
|
|
user,
|
|
posts_to_move,
|
|
destination_topic_id: destination_topic.id,
|
|
chronological_order: true,
|
|
)
|
|
expect(moved_to).to be_present
|
|
|
|
topic.reload
|
|
expect(topic).to_not be_closed
|
|
end
|
|
|
|
it "schedules topic deleting when all posts were moved" do
|
|
SiteSetting.delete_merged_stub_topics_after_days = 7
|
|
freeze_time
|
|
|
|
topic.expects(:add_moderator_post).twice
|
|
posts_to_move = [p1.id, p2.id, p3.id, p4.id]
|
|
moved_to =
|
|
topic.move_posts(
|
|
user,
|
|
posts_to_move,
|
|
destination_topic_id: destination_topic.id,
|
|
chronological_order: true,
|
|
)
|
|
expect(moved_to).to be_present
|
|
|
|
timer = topic.topic_timers.find_by(status_type: TopicTimer.types[:delete])
|
|
expect(timer).to be_present
|
|
expect(timer.execute_at).to eq_time(7.days.from_now)
|
|
end
|
|
|
|
it "doesn't schedule topic deleting when not all posts were moved" do
|
|
SiteSetting.delete_merged_stub_topics_after_days = 7
|
|
|
|
topic.expects(:add_moderator_post).once
|
|
posts_to_move = [p1.id, p2.id, p3.id]
|
|
moved_to =
|
|
topic.move_posts(
|
|
user,
|
|
posts_to_move,
|
|
destination_topic_id: destination_topic.id,
|
|
chronological_order: true,
|
|
)
|
|
expect(moved_to).to be_present
|
|
|
|
timer = topic.topic_timers.find_by(status_type: TopicTimer.types[:delete])
|
|
expect(timer).to be_nil
|
|
end
|
|
|
|
it "doesn't schedule topic deleting when all posts were moved if it's disabled in settings" do
|
|
SiteSetting.delete_merged_stub_topics_after_days = 0
|
|
|
|
topic.expects(:add_moderator_post).twice
|
|
posts_to_move = [p1.id, p2.id, p3.id, p4.id]
|
|
moved_to =
|
|
topic.move_posts(
|
|
user,
|
|
posts_to_move,
|
|
destination_topic_id: destination_topic.id,
|
|
chronological_order: true,
|
|
)
|
|
expect(moved_to).to be_present
|
|
|
|
timer = topic.topic_timers.find_by(status_type: TopicTimer.types[:delete])
|
|
expect(timer).to be_nil
|
|
end
|
|
|
|
it "ignores moderator posts and closes the topic if all regular posts were moved" do
|
|
add_moderator_post_to topic, Post.types[:moderator_action]
|
|
add_moderator_post_to topic, Post.types[:small_action]
|
|
|
|
posts_to_move = [p1.id, p2.id, p3.id, p4.id]
|
|
topic.move_posts(
|
|
user,
|
|
posts_to_move,
|
|
destination_topic_id: destination_topic.id,
|
|
chronological_order: true,
|
|
)
|
|
|
|
topic.reload
|
|
expect(topic).to be_closed
|
|
end
|
|
|
|
it "does not try to move small action posts" do
|
|
small_action =
|
|
Fabricate(
|
|
:post,
|
|
topic: topic,
|
|
raw: "A small action",
|
|
post_type: Post.types[:small_action],
|
|
)
|
|
moved_to =
|
|
topic.move_posts(
|
|
user,
|
|
[p1.id, p2.id, p3.id, p4.id, small_action.id],
|
|
destination_topic_id: destination_topic.id,
|
|
chronological_order: true,
|
|
)
|
|
|
|
moved_to.reload
|
|
expect(moved_to.posts_count).to eq(5)
|
|
expect(small_action.topic_id).to eq(topic.id)
|
|
|
|
moderator_post = topic.posts.find_by(post_number: 2)
|
|
expect(moderator_post.raw).to include("4 posts were merged")
|
|
end
|
|
|
|
it "updates existing notifications" do
|
|
n2 = Fabricate(:mentioned_notification, post: p2, user: another_user)
|
|
n4 = Fabricate(:mentioned_notification, post: p4, user: another_user)
|
|
dest_nop = Fabricate(:mentioned_notification, post: destination_op, user: another_user)
|
|
|
|
moved_to =
|
|
topic.move_posts(
|
|
user,
|
|
[p2.id],
|
|
destination_topic_id: destination_topic.id,
|
|
chronological_order: true,
|
|
)
|
|
|
|
n2 = Notification.find(n2.id)
|
|
expect(n2.topic_id).to eq(moved_to.id)
|
|
expect(n2.post_number).to eq(1)
|
|
expect(n2.data_hash[:topic_title]).to eq(moved_to.title)
|
|
|
|
n4 = Notification.find(n4.id)
|
|
expect(n4.topic_id).to eq(topic.id)
|
|
expect(n4.post_number).to eq(4)
|
|
|
|
dest_nop = Notification.find(dest_nop.id)
|
|
expect(dest_nop.post_number).to eq(2)
|
|
end
|
|
|
|
it "deletes notifications for users not allowed to see the topic" do
|
|
another_admin = Fabricate(:admin)
|
|
staff_category = Fabricate(:private_category, group: Group[:staff])
|
|
user_notification = Fabricate(:mentioned_notification, post: p3, user: another_user)
|
|
admin_notification = Fabricate(:mentioned_notification, post: p3, user: another_admin)
|
|
|
|
destination_topic.update!(category_id: staff_category.id)
|
|
topic.move_posts(
|
|
user,
|
|
[p3.id],
|
|
destination_topic_id: destination_topic.id,
|
|
chronological_order: true,
|
|
)
|
|
|
|
expect(Notification.exists?(user_notification.id)).to eq(false)
|
|
expect(Notification.exists?(admin_notification.id)).to eq(true)
|
|
end
|
|
|
|
context "with post timings" do
|
|
fab!(:some_user) { Fabricate(:user) }
|
|
|
|
it "successfully moves timings" do
|
|
create_post_timing(p1, some_user, 500)
|
|
create_post_timing(p2, some_user, 1000)
|
|
create_post_timing(p3, some_user, 1500)
|
|
create_post_timing(p4, some_user, 750)
|
|
|
|
moved_to =
|
|
topic.move_posts(
|
|
user,
|
|
[p1.id, p4.id],
|
|
destination_topic_id: destination_topic.id,
|
|
chronological_order: true,
|
|
)
|
|
|
|
expect(
|
|
PostTiming.where(topic_id: topic.id, user_id: some_user.id).pluck(
|
|
:post_number,
|
|
:msecs,
|
|
),
|
|
).to contain_exactly([1, 500], [2, 1000], [3, 1500])
|
|
|
|
expect(
|
|
PostTiming.where(topic_id: moved_to.id, user_id: some_user.id).pluck(
|
|
:post_number,
|
|
:msecs,
|
|
),
|
|
).to contain_exactly([1, 500], [3, 750])
|
|
end
|
|
|
|
it "moves timings when post timing exists in destination topic" do
|
|
destination_p2 =
|
|
Fabricate(
|
|
:post,
|
|
topic: destination_topic,
|
|
user: another_user,
|
|
created_at: destination_op.created_at + 10.minutes,
|
|
post_number: 2,
|
|
)
|
|
destination_p3 =
|
|
Fabricate(
|
|
:post,
|
|
topic: destination_topic,
|
|
user: another_user,
|
|
created_at: destination_op.created_at + 15.minutes,
|
|
post_number: 3,
|
|
)
|
|
destination_topic.update!(highest_post_number: 3)
|
|
|
|
PostTiming.create!(
|
|
topic_id: destination_topic.id,
|
|
user_id: some_user.id,
|
|
post_number: 4,
|
|
msecs: 800,
|
|
)
|
|
create_post_timing(destination_op, some_user, 1500)
|
|
create_post_timing(destination_p2, some_user, 2000)
|
|
create_post_timing(destination_p3, some_user, 1250)
|
|
create_post_timing(p1, some_user, 500)
|
|
|
|
moved_to =
|
|
topic.move_posts(
|
|
user,
|
|
[p1.id],
|
|
destination_topic_id: destination_topic.id,
|
|
chronological_order: true,
|
|
)
|
|
|
|
expect(
|
|
PostTiming.where(topic_id: moved_to.id, user_id: some_user.id).pluck(
|
|
:post_number,
|
|
:msecs,
|
|
),
|
|
).to contain_exactly([1, 500], [2, 1500], [3, 2000], [4, 1250])
|
|
end
|
|
end
|
|
|
|
it "updates topic_user.liked values for both source and destination topics" do
|
|
expect(TopicUser.find_by(topic: topic, user: user).liked).to eq(false)
|
|
|
|
like =
|
|
Fabricate(
|
|
:post_action,
|
|
post: p3,
|
|
user: user,
|
|
post_action_type_id: PostActionType.types[:like],
|
|
)
|
|
expect(TopicUser.find_by(topic: topic, user: user).liked).to eq(true)
|
|
|
|
expect(TopicUser.find_by(topic: destination_topic, user: user)).to eq(nil)
|
|
topic.move_posts(
|
|
user,
|
|
[p3.id],
|
|
destination_topic_id: destination_topic.id,
|
|
chronological_order: true,
|
|
)
|
|
|
|
expect(TopicUser.find_by(topic: topic, user: user).liked).to eq(false)
|
|
expect(TopicUser.find_by(topic: destination_topic, user: user).liked).to eq(true)
|
|
end
|
|
|
|
context "with read state and other stats per user" do
|
|
def create_topic_user(user, topic, opts = {})
|
|
notification_level = opts.delete(:notification_level) || :regular
|
|
|
|
Fabricate(
|
|
:topic_user,
|
|
opts.merge(
|
|
notification_level: TopicUser.notification_levels[notification_level],
|
|
topic: topic,
|
|
user: user,
|
|
),
|
|
)
|
|
end
|
|
|
|
fab!(:user1) { Fabricate(:user) }
|
|
fab!(:user2) { Fabricate(:user) }
|
|
fab!(:user3) { Fabricate(:user) }
|
|
fab!(:admin1) { Fabricate(:admin) }
|
|
fab!(:admin2) { Fabricate(:admin) }
|
|
|
|
it "leaves post numbers unchanged when they were lower then the topic's highest post number" do
|
|
Fabricate(:post, topic: destination_topic)
|
|
Fabricate(:whisper, topic: destination_topic)
|
|
|
|
destination_topic.reload
|
|
expect(destination_topic.highest_post_number).to eq(2)
|
|
expect(destination_topic.highest_staff_post_number).to eq(3)
|
|
|
|
create_topic_user(user1, topic, last_read_post_number: 3, last_emailed_post_number: 3)
|
|
create_topic_user(
|
|
user1,
|
|
destination_topic,
|
|
last_read_post_number: 1,
|
|
last_emailed_post_number: 1,
|
|
)
|
|
|
|
create_topic_user(user2, topic, last_read_post_number: 3, last_emailed_post_number: 3)
|
|
create_topic_user(
|
|
user2,
|
|
destination_topic,
|
|
last_read_post_number: 2,
|
|
last_emailed_post_number: 2,
|
|
)
|
|
|
|
create_topic_user(
|
|
admin1,
|
|
topic,
|
|
last_read_post_number: 3,
|
|
last_emailed_post_number: 3,
|
|
)
|
|
create_topic_user(
|
|
admin1,
|
|
destination_topic,
|
|
last_read_post_number: 2,
|
|
last_emailed_post_number: 1,
|
|
)
|
|
|
|
create_topic_user(
|
|
admin2,
|
|
topic,
|
|
last_read_post_number: 3,
|
|
last_emailed_post_number: 3,
|
|
)
|
|
create_topic_user(
|
|
admin2,
|
|
destination_topic,
|
|
last_read_post_number: 3,
|
|
last_emailed_post_number: 3,
|
|
)
|
|
|
|
moved_to_topic =
|
|
topic.move_posts(
|
|
user,
|
|
[p1.id, p2.id],
|
|
destination_topic_id: destination_topic.id,
|
|
chronological_order: true,
|
|
)
|
|
|
|
expect(TopicUser.find_by(topic: moved_to_topic, user: user1)).to have_attributes(
|
|
last_read_post_number: 1,
|
|
last_emailed_post_number: 1,
|
|
)
|
|
|
|
expect(TopicUser.find_by(topic: moved_to_topic, user: user2)).to have_attributes(
|
|
last_read_post_number: 2,
|
|
last_emailed_post_number: 2,
|
|
)
|
|
|
|
expect(TopicUser.find_by(topic: moved_to_topic, user: admin1)).to have_attributes(
|
|
last_read_post_number: 2,
|
|
last_emailed_post_number: 1,
|
|
)
|
|
|
|
expect(TopicUser.find_by(topic: moved_to_topic, user: admin2)).to have_attributes(
|
|
last_read_post_number: 3,
|
|
last_emailed_post_number: 3,
|
|
)
|
|
end
|
|
|
|
it "correctly updates existing topic_user records" do
|
|
destination_topic.update!(created_at: 1.day.ago)
|
|
|
|
original_topic_user1 =
|
|
create_topic_user(
|
|
user1,
|
|
topic,
|
|
last_read_post_number: 5,
|
|
first_visited_at: 5.hours.ago,
|
|
last_visited_at: 30.minutes.ago,
|
|
notification_level: :tracking,
|
|
).reload
|
|
destination_topic_user1 =
|
|
create_topic_user(
|
|
user1,
|
|
destination_topic,
|
|
last_read_post_number: 5,
|
|
first_visited_at: 7.hours.ago,
|
|
last_visited_at: 2.hours.ago,
|
|
notification_level: :watching,
|
|
).reload
|
|
|
|
original_topic_user2 =
|
|
create_topic_user(
|
|
user2,
|
|
topic,
|
|
last_read_post_number: 5,
|
|
first_visited_at: 3.hours.ago,
|
|
last_visited_at: 1.hour.ago,
|
|
notification_level: :watching,
|
|
).reload
|
|
destination_topic_user2 =
|
|
create_topic_user(
|
|
user2,
|
|
destination_topic,
|
|
last_read_post_number: 5,
|
|
first_visited_at: 2.hours.ago,
|
|
last_visited_at: 1.hour.ago,
|
|
notification_level: :tracking,
|
|
).reload
|
|
|
|
new_topic =
|
|
topic.move_posts(
|
|
user,
|
|
[p1.id, p2.id],
|
|
destination_topic_id: destination_topic.id,
|
|
chronological_order: true,
|
|
)
|
|
|
|
expect(TopicUser.find_by(topic: new_topic, user: user)).to have_attributes(
|
|
notification_level: TopicUser.notification_levels[:tracking],
|
|
posted: true,
|
|
)
|
|
|
|
expect(TopicUser.find_by(topic: new_topic, user: user1)).to have_attributes(
|
|
first_visited_at: destination_topic_user1.first_visited_at,
|
|
last_visited_at: original_topic_user1.last_visited_at,
|
|
notification_level: destination_topic_user1.notification_level,
|
|
posted: false,
|
|
)
|
|
|
|
expect(TopicUser.find_by(topic: new_topic, user: user2)).to have_attributes(
|
|
first_visited_at: original_topic_user2.first_visited_at,
|
|
last_visited_at: destination_topic_user2.last_visited_at,
|
|
notification_level: destination_topic_user2.notification_level,
|
|
posted: false,
|
|
)
|
|
end
|
|
end
|
|
end
|
|
|
|
it "skips validations when moving posts" do
|
|
p1.update_attribute(:raw, "foo")
|
|
p2.update_attribute(:raw, "bar")
|
|
|
|
new_topic = topic.move_posts(user, [p1.id, p2.id], title: "new testing topic name")
|
|
|
|
expect(new_topic).to be_present
|
|
expect(new_topic.posts.by_post_number.first.raw).to eq(p1.raw)
|
|
expect(new_topic.posts.by_post_number.last.raw).to eq(p2.raw)
|
|
expect(new_topic.posts_count).to eq(2)
|
|
end
|
|
|
|
it "corrects reply_counts within original topic" do
|
|
expect do
|
|
topic.move_posts(user, [p4.id], title: "new testing topic name 1")
|
|
end.to change { PostReply.count }.by(-1)
|
|
expect(p1.reload.reply_count).to eq(2)
|
|
expect(p2.reload.reply_count).to eq(0)
|
|
|
|
expect do
|
|
topic.move_posts(user, [p2.id, p3.id], title: "new testing topic name 2")
|
|
end.to change { PostReply.count }.by(-2)
|
|
expect(p1.reload.reply_count).to eq(0)
|
|
end
|
|
end
|
|
end
|
|
|
|
context "with messages" do
|
|
fab!(:user)
|
|
fab!(:another_user) { Fabricate(:user) }
|
|
fab!(:regular_user) { Fabricate(:trust_level_4) }
|
|
fab!(:personal_message) { Fabricate(:private_message_topic, user: evil_trout) }
|
|
fab!(:p1) { Fabricate(:post, topic: personal_message, user: user, created_at: 4.hours.ago) }
|
|
fab!(:p2) do
|
|
Fabricate(
|
|
:post,
|
|
topic: personal_message,
|
|
reply_to_post_number: p1.post_number,
|
|
user: another_user,
|
|
created_at: 3.hours.ago,
|
|
)
|
|
end
|
|
fab!(:p3) do
|
|
Fabricate(
|
|
:post,
|
|
topic: personal_message,
|
|
reply_to_post_number: p1.post_number,
|
|
user: user,
|
|
created_at: 2.hours.ago,
|
|
)
|
|
end
|
|
fab!(:p4) do
|
|
Fabricate(
|
|
:post,
|
|
topic: personal_message,
|
|
reply_to_post_number: p2.post_number,
|
|
user: user,
|
|
created_at: 1.hour.ago,
|
|
)
|
|
end
|
|
fab!(:p5) do
|
|
Fabricate(:post, topic: personal_message, user: evil_trout, created_at: 30.minutes.ago)
|
|
end
|
|
let(:another_personal_message) do
|
|
Fabricate(
|
|
:private_message_topic,
|
|
user: user,
|
|
topic_allowed_users: [Fabricate.build(:topic_allowed_user, user: admin)],
|
|
)
|
|
end
|
|
let!(:p6) { Fabricate(:post, topic: another_personal_message, user: evil_trout) }
|
|
|
|
before do
|
|
SiteSetting.tagging_enabled = true
|
|
Jobs.run_immediately!
|
|
p1.replies << p3
|
|
p2.replies << p4
|
|
UserActionManager.enable
|
|
@like = PostActionCreator.like(another_user, p4)
|
|
end
|
|
|
|
context "when moving to new message" do
|
|
it "adds post users as topic allowed users" do
|
|
TopicUser.change(
|
|
user,
|
|
personal_message,
|
|
notification_level: TopicUser.notification_levels[:muted],
|
|
)
|
|
TopicUser.change(
|
|
another_user,
|
|
personal_message,
|
|
notification_level: TopicUser.notification_levels[:tracking],
|
|
)
|
|
|
|
personal_message.move_posts(
|
|
admin,
|
|
[p2.id, p3.id, p4.id, p5.id],
|
|
title: "new testing message name",
|
|
tags: %w[tag1 tag2],
|
|
archetype: "private_message",
|
|
)
|
|
|
|
p2.reload
|
|
destination_topic = p2.topic
|
|
expect(destination_topic.archetype).to eq(Archetype.private_message)
|
|
expect(destination_topic.topic_allowed_users.where(user_id: user.id).count).to eq(1)
|
|
expect(destination_topic.topic_allowed_users.where(user_id: another_user.id).count).to eq(
|
|
1,
|
|
)
|
|
expect(destination_topic.topic_allowed_users.where(user_id: evil_trout.id).count).to eq(1)
|
|
expect(destination_topic.tags.pluck(:name)).to eq([])
|
|
expect(
|
|
TopicUser.exists?(
|
|
user_id: another_user,
|
|
topic_id: destination_topic.id,
|
|
notification_level: TopicUser.notification_levels[:tracking],
|
|
notifications_reason_id: TopicUser.notification_reasons[:created_topic],
|
|
),
|
|
).to eq(true)
|
|
expect(
|
|
TopicUser.exists?(
|
|
user_id: user,
|
|
topic_id: destination_topic.id,
|
|
notification_level: TopicUser.notification_levels[:muted],
|
|
notifications_reason_id: TopicUser.notification_reasons[:created_post],
|
|
),
|
|
).to eq(true)
|
|
end
|
|
|
|
it "can add tags to new message when staff group is included in pm_tags_allowed_for_groups" do
|
|
SiteSetting.pm_tags_allowed_for_groups = "1|2|3"
|
|
SiteSetting.tag_topic_allowed_groups = "1|2|3"
|
|
personal_message.move_posts(
|
|
admin,
|
|
[p2.id, p5.id],
|
|
title: "new testing message name",
|
|
tags: %w[tag1 tag2],
|
|
archetype: "private_message",
|
|
)
|
|
|
|
p2.reload
|
|
expect(p2.topic.tags.pluck(:name)).to contain_exactly("tag1", "tag2")
|
|
end
|
|
|
|
it "correctly handles notifications" do
|
|
old_message = p2.topic
|
|
old_message_id = p2.topic_id
|
|
|
|
personal_message.move_posts(
|
|
admin,
|
|
[p2.id, p4.id],
|
|
title: "new testing message name",
|
|
archetype: "private_message",
|
|
)
|
|
|
|
p2.reload
|
|
expect(p2.topic_id).not_to eq(old_message_id)
|
|
expect(p2.reply_to_post_number).to eq(nil)
|
|
expect(p2.reply_to_user_id).to eq(nil)
|
|
|
|
notification =
|
|
p2.user.notifications.where(notification_type: Notification.types[:moved_post]).first
|
|
|
|
expect(notification.topic_id).to eq(p2.topic_id)
|
|
expect(notification.topic_id).not_to eq(old_message_id)
|
|
expect(notification.post_number).to eq(1)
|
|
|
|
# no message for person who made the move
|
|
expect(
|
|
admin.notifications.where(notification_type: Notification.types[:moved_post]).length,
|
|
).to eq(0)
|
|
|
|
old_message.reload
|
|
move_message = old_message.posts.find_by(post_number: 2)
|
|
expect(move_message.post_type).to eq(Post.types[:whisper])
|
|
expect(move_message.raw).to include("2 posts were split")
|
|
end
|
|
end
|
|
|
|
context "when moving to existing message" do
|
|
it "adds post users as topic allowed users" do
|
|
personal_message.move_posts(
|
|
admin,
|
|
[p2.id, p5.id],
|
|
destination_topic_id: another_personal_message.id,
|
|
archetype: "private_message",
|
|
)
|
|
|
|
p2.reload
|
|
expect(p2.topic_id).to eq(another_personal_message.id)
|
|
|
|
another_personal_message.reload
|
|
expect(
|
|
another_personal_message.topic_allowed_users.where(user_id: another_user.id).count,
|
|
).to eq(1)
|
|
expect(
|
|
another_personal_message.topic_allowed_users.where(user_id: evil_trout.id).count,
|
|
).to eq(1)
|
|
end
|
|
|
|
it "can add additional participants" do
|
|
personal_message.move_posts(
|
|
admin,
|
|
[p2.id, p5.id],
|
|
destination_topic_id: another_personal_message.id,
|
|
participants: [regular_user.username],
|
|
archetype: "private_message",
|
|
)
|
|
|
|
another_personal_message.reload
|
|
expect(
|
|
another_personal_message.topic_allowed_users.where(user_id: another_user.id).count,
|
|
).to eq(1)
|
|
expect(
|
|
another_personal_message.topic_allowed_users.where(user_id: evil_trout.id).count,
|
|
).to eq(1)
|
|
expect(
|
|
another_personal_message.topic_allowed_users.where(user_id: regular_user.id).count,
|
|
).to eq(1)
|
|
end
|
|
|
|
it "does not allow moving regular topic posts in personal message" do
|
|
topic = Fabricate(:topic, created_at: 4.hours.ago)
|
|
|
|
expect {
|
|
personal_message.move_posts(admin, [p2.id, p5.id], destination_topic_id: topic.id)
|
|
}.to raise_error(Discourse::InvalidParameters)
|
|
end
|
|
|
|
it "moving all posts will close the message" do
|
|
moved_to =
|
|
personal_message.move_posts(
|
|
admin,
|
|
[p1.id, p2.id, p3.id, p4.id, p5.id],
|
|
destination_topic_id: another_personal_message.id,
|
|
archetype: "private_message",
|
|
)
|
|
expect(moved_to).to be_present
|
|
|
|
personal_message.reload
|
|
expect(personal_message.closed).to eq(true)
|
|
expect(moved_to.posts_count).to eq(6)
|
|
end
|
|
|
|
it "uses the correct small action post" do
|
|
moved_to =
|
|
personal_message.move_posts(
|
|
admin,
|
|
[p2.id],
|
|
destination_topic_id: another_personal_message.id,
|
|
archetype: "private_message",
|
|
)
|
|
post = Post.find_by(topic_id: personal_message.id, post_type: Post.types[:whisper])
|
|
|
|
expected_text =
|
|
I18n.t(
|
|
"move_posts.existing_message_moderator_post",
|
|
count: 1,
|
|
topic_link: "[#{moved_to.title}](#{p2.reload.url})",
|
|
locale: :en,
|
|
)
|
|
|
|
expect(post.raw).to eq(expected_text)
|
|
end
|
|
end
|
|
|
|
context "when moving chronologically to existing message" do
|
|
it "adds post users as topic allowed users" do
|
|
personal_message.move_posts(
|
|
admin,
|
|
[p2.id, p5.id],
|
|
destination_topic_id: another_personal_message.id,
|
|
archetype: "private_message",
|
|
chronological_order: true,
|
|
)
|
|
|
|
p2.reload
|
|
expect(p2.topic_id).to eq(another_personal_message.id)
|
|
|
|
another_personal_message.reload
|
|
expect(
|
|
another_personal_message.topic_allowed_users.where(user_id: another_user.id).count,
|
|
).to eq(1)
|
|
expect(
|
|
another_personal_message.topic_allowed_users.where(user_id: evil_trout.id).count,
|
|
).to eq(1)
|
|
end
|
|
|
|
it "can add additional participants" do
|
|
personal_message.move_posts(
|
|
admin,
|
|
[p2.id, p5.id],
|
|
destination_topic_id: another_personal_message.id,
|
|
participants: [regular_user.username],
|
|
archetype: "private_message",
|
|
chronological_order: true,
|
|
)
|
|
|
|
another_personal_message.reload
|
|
expect(
|
|
another_personal_message.topic_allowed_users.where(user_id: another_user.id).count,
|
|
).to eq(1)
|
|
expect(
|
|
another_personal_message.topic_allowed_users.where(user_id: evil_trout.id).count,
|
|
).to eq(1)
|
|
expect(
|
|
another_personal_message.topic_allowed_users.where(user_id: regular_user.id).count,
|
|
).to eq(1)
|
|
end
|
|
|
|
it "does not allow moving regular topic posts in personal message" do
|
|
topic = Fabricate(:topic, created_at: 4.hours.ago)
|
|
|
|
expect {
|
|
personal_message.move_posts(
|
|
admin,
|
|
[p2.id, p5.id],
|
|
destination_topic_id: topic.id,
|
|
chronological_order: true,
|
|
)
|
|
}.to raise_error(Discourse::InvalidParameters)
|
|
end
|
|
|
|
it "moving all posts will close the message" do
|
|
moved_to =
|
|
personal_message.move_posts(
|
|
admin,
|
|
[p1.id, p2.id, p3.id, p4.id, p5.id],
|
|
destination_topic_id: another_personal_message.id,
|
|
archetype: "private_message",
|
|
chronological_order: true,
|
|
)
|
|
expect(moved_to).to be_present
|
|
|
|
personal_message.reload
|
|
expect(personal_message.closed).to eq(true)
|
|
expect(moved_to.posts_count).to eq(6)
|
|
end
|
|
|
|
it "uses the correct small action post" do
|
|
moved_to =
|
|
personal_message.move_posts(
|
|
admin,
|
|
[p2.id],
|
|
destination_topic_id: another_personal_message.id,
|
|
archetype: "private_message",
|
|
chronological_order: true,
|
|
)
|
|
post = Post.find_by(topic_id: personal_message.id, post_type: Post.types[:whisper])
|
|
|
|
expected_text =
|
|
I18n.t(
|
|
"move_posts.existing_message_moderator_post",
|
|
count: 1,
|
|
topic_link: "[#{moved_to.title}](#{moved_to.relative_url})",
|
|
locale: :en,
|
|
)
|
|
|
|
expect(post.raw).to eq(expected_text)
|
|
end
|
|
end
|
|
end
|
|
|
|
context "with banner topic" do
|
|
fab!(:regular_user) { Fabricate(:trust_level_4) }
|
|
fab!(:topic)
|
|
fab!(:personal_message) { Fabricate(:private_message_topic, user: regular_user) }
|
|
fab!(:banner_topic) { Fabricate(:banner_topic, user: evil_trout) }
|
|
fab!(:p1) { Fabricate(:post, topic: banner_topic, user: evil_trout) }
|
|
fab!(:p2) do
|
|
Fabricate(
|
|
:post,
|
|
topic: banner_topic,
|
|
reply_to_post_number: p1.post_number,
|
|
user: regular_user,
|
|
)
|
|
end
|
|
|
|
context "when moving to existing topic" do
|
|
it "allows moving banner topic posts in regular topic" do
|
|
banner_topic.move_posts(admin, [p2.id], destination_topic_id: topic.id)
|
|
expect(p2.reload.topic_id).to eq(topic.id)
|
|
end
|
|
|
|
it "does not allow moving banner topic posts in personal message" do
|
|
expect {
|
|
banner_topic.move_posts(admin, [p2.id], destination_topic_id: personal_message.id)
|
|
}.to raise_error(Discourse::InvalidParameters)
|
|
end
|
|
end
|
|
end
|
|
|
|
context "with event trigger" do
|
|
fab!(:topic_1) { Fabricate(:topic) }
|
|
fab!(:topic_2) { Fabricate(:topic) }
|
|
fab!(:post_1) { Fabricate(:post, topic: topic_1) }
|
|
fab!(:post_2) { Fabricate(:post, topic: topic_1) }
|
|
|
|
it "receives 2 post moved event triggers for the first post" do
|
|
post_mover = PostMover.new(topic_1, Discourse.system_user, [post_1.id])
|
|
events = DiscourseEvent.track_events { post_mover.to_topic(topic_2.id) }
|
|
filtered_events =
|
|
events.filter { |e| %i[first_post_moved post_moved].include? e[:event_name] }
|
|
|
|
expect(filtered_events.size).to eq(2)
|
|
end
|
|
|
|
it "uses first_post_moved trigger for first post" do
|
|
post_mover = PostMover.new(topic_1, Discourse.system_user, [post_1.id])
|
|
events = DiscourseEvent.track_events(:first_post_moved) { post_mover.to_topic(topic_2.id) }
|
|
expect(events.size).to eq(1)
|
|
|
|
new_post = Post.find_by(topic_id: topic_2.id, post_number: 1)
|
|
|
|
event = events.first
|
|
expect(event[:event_name]).to eq(:first_post_moved)
|
|
expect(event[:params][0]).to eq(new_post)
|
|
expect(event[:params][1]).to eq(post_1)
|
|
end
|
|
|
|
it "uses post_moved trigger for other posts" do
|
|
post_mover = PostMover.new(topic_1, Discourse.system_user, [post_2.id])
|
|
events = DiscourseEvent.track_events(:post_moved) { post_mover.to_topic(topic_2.id) }
|
|
expect(events.size).to eq(1)
|
|
|
|
event = events.first
|
|
expect(event[:event_name]).to eq(:post_moved)
|
|
expect(event[:params][0]).to eq(post_2)
|
|
expect(event[:params][1]).to eq(topic_1.id)
|
|
end
|
|
end
|
|
end
|
|
end
|