2023-04-04 09:30:38 +10:00
|
|
|
# frozen_string_literal: true
|
|
|
|
|
|
|
|
RSpec.describe Chat::TrashMessage do
|
|
|
|
fab!(:current_user) { Fabricate(:user) }
|
|
|
|
let!(:guardian) { Guardian.new(current_user) }
|
|
|
|
fab!(:message) { Fabricate(:chat_message, user: current_user) }
|
|
|
|
|
|
|
|
describe ".call" do
|
|
|
|
subject(:result) { described_class.call(params) }
|
|
|
|
|
|
|
|
context "when params are not valid" do
|
|
|
|
let(:params) { { guardian: guardian } }
|
|
|
|
|
|
|
|
it { is_expected.to fail_a_contract }
|
|
|
|
end
|
|
|
|
|
|
|
|
context "when params are valid" do
|
|
|
|
let(:params) do
|
|
|
|
{ guardian: guardian, message_id: message.id, channel_id: message.chat_channel_id }
|
|
|
|
end
|
|
|
|
|
|
|
|
context "when the user does not have permission to delete" do
|
|
|
|
before { message.update!(user: Fabricate(:admin)) }
|
|
|
|
|
|
|
|
it { is_expected.to fail_a_policy(:invalid_access) }
|
|
|
|
end
|
|
|
|
|
|
|
|
context "when the channel does not match the message" do
|
|
|
|
let(:params) do
|
|
|
|
{ guardian: guardian, message_id: message.id, channel_id: Fabricate(:chat_channel).id }
|
|
|
|
end
|
|
|
|
|
|
|
|
it { is_expected.to fail_to_find_a_model(:message) }
|
|
|
|
end
|
|
|
|
|
|
|
|
context "when the user has permission to delete" do
|
|
|
|
it "sets the service result as successful" do
|
|
|
|
expect(result).to be_a_success
|
|
|
|
end
|
|
|
|
|
|
|
|
it "trashes the message" do
|
|
|
|
result
|
|
|
|
expect(Chat::Message.find_by(id: message.id)).to be_nil
|
2023-07-13 10:16:15 +02:00
|
|
|
|
|
|
|
deleted_message = Chat::Message.unscoped.find_by(id: message.id)
|
|
|
|
expect(deleted_message.deleted_by_id).to eq(current_user.id)
|
|
|
|
expect(deleted_message.deleted_at).to be_within(1.minute).of(Time.zone.now)
|
2023-04-04 09:30:38 +10:00
|
|
|
end
|
|
|
|
|
2023-05-11 20:05:59 +04:00
|
|
|
it "destroys notifications for mentions" do
|
|
|
|
notification = Fabricate(:notification)
|
DEV: Redesign chat mentions (#24752)
At the moment, when someone is mentioning a group, or using here or
all mention, we create a chat_mention record per user. What we want
instead is to have special kinds of mentions, so we can create only one
chat_mention record in such cases. This PR implements that.
Note, that such mentions will still have N related notifications, one
notification per a user. We don't expect we'll have performance
problems on the notifications side, but if at some point we do, we
should be able to solve them on the side of notifications
(notifications are handled in jobs, also some little delays with
the notifications are acceptable, so we can make sure notifications
are properly queued, and that processing of every notification is
fast enough to make delays small enough).
The preparation work for this PR was done in fbd24fa, where we make
it possible for one mention to have several related notifications.
A pretty tricky part of this PR is schema and data migration, I've explained
related details inline on the migration files.
2024-01-17 15:24:01 +04:00
|
|
|
mention =
|
|
|
|
Fabricate(:user_chat_mention, chat_message: message, notifications: [notification])
|
2023-05-11 20:05:59 +04:00
|
|
|
|
2023-04-04 09:30:38 +10:00
|
|
|
result
|
2023-05-11 20:05:59 +04:00
|
|
|
|
|
|
|
mention = Chat::Mention.find_by(id: mention.id)
|
|
|
|
expect(mention).to be_present
|
DEV: Allow chat mentions to have several notifications (#24874)
This PR is a reworked version of https://github.com/discourse/discourse/pull/24670.
In chat, we need the ability to have several notifications per `chat_mention`.
Currently, we have one_to_one relationship between `chat_mentions` and `notifications`:
https://github.com/discourse/discourse/blob/d7a09fb08de31dbeed55428b84e0a660dbd5cf7a/plugins/chat/app/models/chat/mention.rb#L9
We want to have one_to_many relationship. This PR implements that by introducing
a join table between `chat_mentions` and `notifications`.
The main motivation for this is that we want to solve some performance problems
with mentions that we're having now. Let's say a user sends a message with @ all
in a channel with 50 members, we do two things in this case at the moment:
- create 50 chat_mentions
- create 50 notifications
We don't want to change how notifications work in core, but we want to be more
efficient in chat, and create only 1 `chat_mention` which would link to 50 notifications.
Also note, that on the side of notifications, having a lot of notifications is not so
big problem, because notifications processing can be queued.
Apart from improving performance, this change will make the code design better.
Note that I've marked the old `chat_mention.notification_id` column as ignored, but
I'm not deleting it in this PR. We'll delete it later in https://github.com/discourse/discourse/pull/24800.
2023-12-19 18:53:00 +04:00
|
|
|
expect(mention.notifications).to be_empty
|
2023-04-04 09:30:38 +10:00
|
|
|
end
|
|
|
|
|
|
|
|
it "publishes associated Discourse and MessageBus events" do
|
|
|
|
freeze_time
|
|
|
|
messages = nil
|
|
|
|
event =
|
2023-09-13 17:31:42 -03:00
|
|
|
DiscourseEvent
|
|
|
|
.track_events { messages = MessageBus.track_publish { result } }
|
|
|
|
.find { |e| e[:event_name] == :chat_message_trashed }
|
|
|
|
|
|
|
|
expect(event).to be_present
|
2023-04-04 09:30:38 +10:00
|
|
|
expect(event[:params]).to eq([message, message.chat_channel, current_user])
|
|
|
|
expect(messages.find { |m| m.channel == "/chat/#{message.chat_channel_id}" }.data).to eq(
|
2023-04-24 09:32:04 +10:00
|
|
|
{
|
|
|
|
"type" => "delete",
|
|
|
|
"deleted_id" => message.id,
|
2023-07-13 10:16:15 +02:00
|
|
|
"deleted_by_id" => current_user.id,
|
2023-04-24 09:32:04 +10:00
|
|
|
"deleted_at" => message.reload.deleted_at.iso8601(3),
|
2023-05-23 18:32:19 +02:00
|
|
|
"latest_not_deleted_message_id" => nil,
|
2023-04-24 09:32:04 +10:00
|
|
|
},
|
2023-04-04 09:30:38 +10:00
|
|
|
)
|
|
|
|
end
|
|
|
|
|
2023-05-05 15:28:48 +02:00
|
|
|
it "updates the tracking to the last non-deleted channel message for users whose last_read_message_id was the trashed message" do
|
|
|
|
other_message = Fabricate(:chat_message, chat_channel: message.chat_channel)
|
2023-04-04 09:30:38 +10:00
|
|
|
membership_1 =
|
|
|
|
Fabricate(
|
|
|
|
:user_chat_channel_membership,
|
|
|
|
chat_channel: message.chat_channel,
|
|
|
|
last_read_message: message,
|
|
|
|
)
|
|
|
|
membership_2 =
|
|
|
|
Fabricate(
|
|
|
|
:user_chat_channel_membership,
|
|
|
|
chat_channel: message.chat_channel,
|
|
|
|
last_read_message: message,
|
|
|
|
)
|
|
|
|
membership_3 =
|
|
|
|
Fabricate(
|
|
|
|
:user_chat_channel_membership,
|
|
|
|
chat_channel: message.chat_channel,
|
2023-05-05 15:28:48 +02:00
|
|
|
last_read_message: other_message,
|
|
|
|
)
|
|
|
|
result
|
|
|
|
expect(membership_1.reload.last_read_message_id).to eq(other_message.id)
|
|
|
|
expect(membership_2.reload.last_read_message_id).to eq(other_message.id)
|
|
|
|
expect(membership_3.reload.last_read_message_id).to eq(other_message.id)
|
|
|
|
end
|
|
|
|
|
|
|
|
it "updates the tracking to nil when there are no other messages left in the channnel" do
|
|
|
|
membership_1 =
|
|
|
|
Fabricate(
|
|
|
|
:user_chat_channel_membership,
|
|
|
|
chat_channel: message.chat_channel,
|
|
|
|
last_read_message: message,
|
|
|
|
)
|
|
|
|
membership_2 =
|
|
|
|
Fabricate(
|
|
|
|
:user_chat_channel_membership,
|
|
|
|
chat_channel: message.chat_channel,
|
|
|
|
last_read_message: message,
|
2023-04-04 09:30:38 +10:00
|
|
|
)
|
|
|
|
result
|
|
|
|
expect(membership_1.reload.last_read_message_id).to be_nil
|
|
|
|
expect(membership_2.reload.last_read_message_id).to be_nil
|
|
|
|
end
|
|
|
|
|
2023-07-13 10:28:11 +10:00
|
|
|
it "updates the channel last_message_id to the previous message in the channel" do
|
|
|
|
next_message =
|
|
|
|
Fabricate(:chat_message, chat_channel: message.chat_channel, user: current_user)
|
|
|
|
params[:message_id] = next_message.id
|
2023-08-24 15:22:51 +02:00
|
|
|
message.chat_channel.update!(last_message: next_message)
|
2023-07-13 10:28:11 +10:00
|
|
|
result
|
|
|
|
expect(message.chat_channel.reload.last_message).to eq(message)
|
|
|
|
end
|
|
|
|
|
2023-04-24 09:32:04 +10:00
|
|
|
context "when the message has a thread" do
|
|
|
|
fab!(:thread) { Fabricate(:chat_thread, channel: message.chat_channel) }
|
|
|
|
|
2023-07-13 10:28:11 +10:00
|
|
|
before do
|
|
|
|
message.update!(thread: thread)
|
|
|
|
thread.update!(last_message: message)
|
|
|
|
thread.original_message.update!(created_at: message.created_at - 2.hours)
|
|
|
|
end
|
2023-04-24 09:32:04 +10:00
|
|
|
|
|
|
|
it "decrements the thread reply count" do
|
|
|
|
thread.set_replies_count_cache(5)
|
|
|
|
result
|
|
|
|
expect(thread.replies_count_cache).to eq(4)
|
|
|
|
end
|
2023-05-11 14:35:26 +02:00
|
|
|
|
|
|
|
it "updates the tracking to the last non-deleted thread message for users whose last_read_message_id was the trashed message" do
|
|
|
|
other_message =
|
|
|
|
Fabricate(:chat_message, chat_channel: message.chat_channel, thread: thread)
|
|
|
|
membership_1 =
|
|
|
|
Fabricate(:user_chat_thread_membership, thread: thread, last_read_message: message)
|
|
|
|
membership_2 =
|
|
|
|
Fabricate(:user_chat_thread_membership, thread: thread, last_read_message: message)
|
|
|
|
membership_3 =
|
|
|
|
Fabricate(
|
|
|
|
:user_chat_thread_membership,
|
|
|
|
thread: thread,
|
|
|
|
last_read_message: other_message,
|
|
|
|
)
|
|
|
|
result
|
|
|
|
expect(membership_1.reload.last_read_message_id).to eq(other_message.id)
|
|
|
|
expect(membership_2.reload.last_read_message_id).to eq(other_message.id)
|
|
|
|
expect(membership_3.reload.last_read_message_id).to eq(other_message.id)
|
|
|
|
end
|
|
|
|
|
|
|
|
it "updates the tracking to nil when there are no other messages left in the thread" do
|
|
|
|
membership_1 =
|
|
|
|
Fabricate(:user_chat_thread_membership, thread: thread, last_read_message: message)
|
|
|
|
membership_2 =
|
|
|
|
Fabricate(:user_chat_thread_membership, thread: thread, last_read_message: message)
|
|
|
|
result
|
|
|
|
expect(membership_1.reload.last_read_message_id).to be_nil
|
|
|
|
expect(membership_2.reload.last_read_message_id).to be_nil
|
|
|
|
end
|
2023-07-13 10:28:11 +10:00
|
|
|
|
|
|
|
it "updates the thread last_message_id to the previous message in the thread" do
|
2023-08-24 15:22:51 +02:00
|
|
|
next_message =
|
|
|
|
Fabricate(
|
|
|
|
:chat_message,
|
|
|
|
thread: thread,
|
|
|
|
user: current_user,
|
|
|
|
chat_channel: message.chat_channel,
|
|
|
|
)
|
2023-07-13 10:28:11 +10:00
|
|
|
params[:message_id] = next_message.id
|
2023-08-24 15:22:51 +02:00
|
|
|
thread.update!(last_message: next_message)
|
2023-07-13 10:28:11 +10:00
|
|
|
result
|
|
|
|
expect(thread.reload.last_message).to eq(message)
|
|
|
|
end
|
|
|
|
|
|
|
|
context "when there are no other messages left in the thread except the original message" do
|
|
|
|
it "updates the thread last_message_id to the original message" do
|
|
|
|
expect(thread.last_message).to eq(message)
|
|
|
|
result
|
|
|
|
expect(thread.reload.last_message).to eq(thread.original_message)
|
|
|
|
end
|
|
|
|
end
|
2023-04-24 09:32:04 +10:00
|
|
|
end
|
|
|
|
|
2023-04-04 09:30:38 +10:00
|
|
|
context "when message is already deleted" do
|
|
|
|
before { message.trash! }
|
|
|
|
|
|
|
|
it { is_expected.to fail_to_find_a_model(:message) }
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|