discourse/plugins/chat/spec/lib/message_mover_spec.rb
Andrei Prigorshnev 69c7df2e56
DEV: do not fabricate a Notification when fabricating a ChatMention (#20450)
Initially, the chat_mention db table was created to support notifications. So when creating 
a `chat_mention` record we were always creating a related `notification` record. So did the
ChatMention fabricator. 

Now we want to use the chat_mention db table in other scenarios. So we started decoupling 
mentions from notification in 75b81b68.

This removes fabrication of Notifications from the ChatMention fabricator. We need to be able 
to fabricate a ChatMention without a Notification.
2023-02-27 14:41:28 +04:00

238 lines
9.1 KiB
Ruby

# frozen_string_literal: true
require "rails_helper"
describe Chat::MessageMover do
fab!(:acting_user) { Fabricate(:admin, username: "testmovechat") }
fab!(:source_channel) { Fabricate(:category_channel) }
fab!(:destination_channel) { Fabricate(:category_channel) }
fab!(:message1) do
Fabricate(
:chat_message,
chat_channel: source_channel,
created_at: 3.minutes.ago,
message: "the first to be moved",
)
end
fab!(:message2) do
Fabricate(
:chat_message,
chat_channel: source_channel,
created_at: 2.minutes.ago,
message: "message deux @testmovechat",
)
end
fab!(:message3) do
Fabricate(
:chat_message,
chat_channel: source_channel,
created_at: 1.minute.ago,
message: "the third message",
)
end
fab!(:message4) { Fabricate(:chat_message, chat_channel: destination_channel) }
fab!(:message5) { Fabricate(:chat_message, chat_channel: destination_channel) }
fab!(:message6) { Fabricate(:chat_message, chat_channel: destination_channel) }
let(:move_message_ids) { [message1.id, message2.id, message3.id] }
describe "#move_to_channel" do
def move!(move_message_ids = [message1.id, message2.id, message3.id])
described_class.new(
acting_user: acting_user,
source_channel: source_channel,
message_ids: move_message_ids,
).move_to_channel(destination_channel)
end
it "raises an error if either the source or destination channels are not public (they cannot be DM channels)" do
expect {
described_class.new(
acting_user: acting_user,
source_channel: Fabricate(:direct_message_channel),
message_ids: move_message_ids,
).move_to_channel(destination_channel)
}.to raise_error(Chat::MessageMover::InvalidChannel)
expect {
described_class.new(
acting_user: acting_user,
source_channel: source_channel,
message_ids: move_message_ids,
).move_to_channel(Fabricate(:direct_message_channel))
}.to raise_error(Chat::MessageMover::InvalidChannel)
end
it "raises an error if no messages are found using the message ids" do
other_channel = Fabricate(:chat_channel)
message1.update(chat_channel: other_channel)
message2.update(chat_channel: other_channel)
message3.update(chat_channel: other_channel)
expect { move! }.to raise_error(Chat::MessageMover::NoMessagesFound)
end
it "deletes the messages from the source channel and sends messagebus delete messages" do
messages = MessageBus.track_publish { move! }
expect(ChatMessage.where(id: move_message_ids)).to eq([])
deleted_messages = ChatMessage.with_deleted.where(id: move_message_ids).order(:id)
expect(deleted_messages.count).to eq(3)
expect(messages.first.channel).to eq("/chat/#{source_channel.id}")
expect(messages.first.data[:typ]).to eq("bulk_delete")
expect(messages.first.data[:deleted_ids]).to eq(deleted_messages.map(&:id))
expect(messages.first.data[:deleted_at]).not_to eq(nil)
end
it "creates a message in the source channel to indicate that the messages have been moved" do
move!
placeholder_message = ChatMessage.where(chat_channel: source_channel).order(:created_at).last
destination_first_moved_message =
ChatMessage.find_by(chat_channel: destination_channel, message: "the first to be moved")
expect(placeholder_message.message).to eq(
I18n.t(
"chat.channel.messages_moved",
count: move_message_ids.length,
acting_username: acting_user.username,
channel_name: destination_channel.title(acting_user),
first_moved_message_url: destination_first_moved_message.url,
),
)
end
it "preserves the order of the messages in the destination channel" do
move!
moved_messages =
ChatMessage.where(chat_channel: destination_channel).order("created_at ASC, id ASC").last(3)
expect(moved_messages.map(&:message)).to eq(
["the first to be moved", "message deux @testmovechat", "the third message"],
)
end
it "updates references for reactions, uploads, revisions, mentions, etc." do
reaction = Fabricate(:chat_message_reaction, chat_message: message1)
upload = Fabricate(:upload_reference, target: message1)
notification = Fabricate(:notification)
mention =
Fabricate(
:chat_mention,
chat_message: message2,
user: acting_user,
notification: notification,
)
revision = Fabricate(:chat_message_revision, chat_message: message3)
webhook_event = Fabricate(:chat_webhook_event, chat_message: message3)
move!
moved_messages =
ChatMessage.where(chat_channel: destination_channel).order("created_at ASC, id ASC").last(3)
expect(reaction.reload.chat_message_id).to eq(moved_messages.first.id)
expect(upload.reload.target_id).to eq(moved_messages.first.id)
expect(mention.reload.chat_message_id).to eq(moved_messages.second.id)
expect(revision.reload.chat_message_id).to eq(moved_messages.third.id)
expect(webhook_event.reload.chat_message_id).to eq(moved_messages.third.id)
end
it "does not preserve reply chains using in_reply_to_id" do
message3.update!(in_reply_to: message2)
message2.update!(in_reply_to: message1)
move!
moved_messages =
ChatMessage.where(chat_channel: destination_channel).order("created_at ASC, id ASC").last(3)
expect(moved_messages.pluck(:in_reply_to_id).uniq).to eq([nil])
end
it "clears in_reply_to_id for remaining messages when the messages they were replying to are moved" do
message3.update!(in_reply_to: message2)
message2.update!(in_reply_to: message1)
move!([message2.id])
expect(message3.reload.in_reply_to_id).to eq(nil)
end
context "when there is a thread" do
fab!(:thread) { Fabricate(:chat_thread, channel: source_channel, original_message: message1) }
before do
message1.update!(thread: thread)
message2.update!(thread: thread)
message3.update!(thread: thread)
end
it "does not preserve thread_ids" do
move!
moved_messages =
ChatMessage
.where(chat_channel: destination_channel)
.order("created_at ASC, id ASC")
.last(3)
expect(moved_messages.pluck(:thread_id).uniq).to eq([nil])
end
it "deletes the empty thread" do
move!
expect(ChatThread.exists?(id: thread.id)).to eq(false)
end
it "clears in_reply_to_id for remaining messages when the messages they were replying to are moved but leaves the thread_id" do
message3.update!(in_reply_to: message2)
message2.update!(in_reply_to: message1)
move!([message2.id])
expect(message3.reload.in_reply_to_id).to eq(nil)
expect(message3.reload.thread).to eq(thread)
end
context "when a thread original message is moved" do
it "creates a new thread for the messages left behind in the old channel" do
message4 =
Fabricate(
:chat_message,
chat_channel: source_channel,
message: "the fourth message",
in_reply_to: message3,
thread: thread,
)
message5 =
Fabricate(
:chat_message,
chat_channel: source_channel,
message: "the fifth message",
thread: thread,
)
expect { move! }.to change { ChatThread.count }.by(1)
new_thread = ChatThread.last
expect(message4.reload.thread_id).to eq(new_thread.id)
expect(message5.reload.thread_id).to eq(new_thread.id)
expect(new_thread.channel).to eq(source_channel)
expect(new_thread.original_message).to eq(message4)
end
end
context "when multiple thread original messages are moved" do
it "works the same as when one is" do
message4 =
Fabricate(:chat_message, chat_channel: source_channel, message: "the fourth message")
message5 =
Fabricate(
:chat_message,
chat_channel: source_channel,
in_reply_to: message5,
message: "the fifth message",
)
other_thread =
Fabricate(:chat_thread, channel: source_channel, original_message: message4)
message4.update!(thread: other_thread)
message5.update!(thread: other_thread)
expect { move!([message1.id, message4.id]) }.to change { ChatThread.count }.by(2)
new_threads = ChatThread.order(:created_at).last(2)
expect(message3.reload.thread_id).to eq(new_threads.first.id)
expect(message5.reload.thread_id).to eq(new_threads.second.id)
expect(new_threads.first.channel).to eq(source_channel)
expect(new_threads.second.channel).to eq(source_channel)
expect(new_threads.first.original_message).to eq(message2)
expect(new_threads.second.original_message).to eq(message5)
end
end
end
end
end