Files
discourse/plugins/chat/spec/models/chat/thread_spec.rb
Martin Brennan 180e3e11d1 FEATURE: Better thread reply counter cache (#21108)
This commit introduces a redis cache over the top of the thread
replies_count DB cache, so that we can quickly and accurately
increment/decrement the reply count for all users and not have
to constantly update the database-level count. This is done so
the UI can have a count that is displayed to the users on each
thread indicator, that appears to live update on each chat
message create/trash/recover inside the thread.

This commit also introduces the `Chat::RestoreMessage` service
and moves the restore endpoint into the `Api::ChannelMessages`
controller as part of incremental migrations to move things out
of ChatController.

Finally, this commit refactors `Chat::Publisher` to be less repetitive
with its `MessageBus` sending code.
2023-04-18 14:01:01 +10:00

163 lines
5.8 KiB
Ruby

# frozen_string_literal: true
RSpec.describe Chat::Thread do
describe ".ensure_consistency!" do
fab!(:channel) { Fabricate(:category_channel) }
fab!(:thread_1) { Fabricate(:chat_thread, channel: channel) }
fab!(:thread_2) { Fabricate(:chat_thread, channel: channel) }
fab!(:thread_3) { Fabricate(:chat_thread, channel: channel) }
before do
Fabricate(:chat_message, chat_channel: channel, thread: thread_1)
Fabricate(:chat_message, chat_channel: channel, thread: thread_1)
Fabricate(:chat_message, chat_channel: channel, thread: thread_1)
Fabricate(:chat_message, chat_channel: channel, thread: thread_2)
Fabricate(:chat_message, chat_channel: channel, thread: thread_2)
Fabricate(:chat_message, chat_channel: channel, thread: thread_2)
Fabricate(:chat_message, chat_channel: channel, thread: thread_2)
Fabricate(:chat_message, chat_channel: channel, thread: thread_3)
end
describe "updating replies_count for all threads" do
it "counts correctly and does not include the original message" do
described_class.ensure_consistency!
expect(thread_1.reload.replies_count).to eq(3)
expect(thread_2.reload.replies_count).to eq(4)
expect(thread_3.reload.replies_count).to eq(1)
end
it "does not count deleted messages" do
thread_1.chat_messages.last.trash!
described_class.ensure_consistency!
expect(thread_1.reload.replies_count).to eq(2)
end
it "sets the replies count to 0 if all the messages but the original message are deleted" do
thread_1.replies.delete_all
described_class.ensure_consistency!
expect(thread_1.reload.replies_count).to eq(0)
end
it "clears the affected replies_count caches" do
thread_1.set_replies_count_cache(100)
expect(thread_1.replies_count_cache).to eq(100)
expect(thread_1.replies_count_cache_updated_at).not_to eq(nil)
described_class.ensure_consistency!
expect(Discourse.redis.get(Chat::Thread.replies_count_cache_redis_key(thread_1.id))).to eq(
nil,
)
expect(
Discourse.redis.get(Chat::Thread.replies_count_cache_updated_at_redis_key(thread_1.id)),
).to eq(nil)
end
end
end
describe ".grouped_messages" do
fab!(:channel) { Fabricate(:category_channel) }
fab!(:thread_1) { Fabricate(:chat_thread, channel: channel) }
fab!(:thread_2) { Fabricate(:chat_thread, channel: channel) }
fab!(:message_1) { Fabricate(:chat_message, chat_channel: channel, thread: thread_1) }
fab!(:message_2) { Fabricate(:chat_message, chat_channel: channel, thread: thread_1) }
fab!(:message_3) { Fabricate(:chat_message, chat_channel: channel, thread: thread_2) }
let(:result) { Chat::Thread.grouped_messages(**params) }
context "when thread_ids provided" do
let(:params) { { thread_ids: [thread_1.id, thread_2.id] } }
it "groups all the message ids in each thread by thread ID" do
expect(result.find { |res| res.thread_id == thread_1.id }.to_h).to eq(
{
thread_message_ids: [thread_1.original_message_id, message_1.id, message_2.id],
thread_id: thread_1.id,
original_message_id: thread_1.original_message_id,
},
)
expect(result.find { |res| res.thread_id == thread_2.id }.to_h).to eq(
{
thread_message_ids: [thread_2.original_message_id, message_3.id],
thread_id: thread_2.id,
original_message_id: thread_2.original_message_id,
},
)
end
context "when include_original_message is false" do
let(:params) { { thread_ids: [thread_1.id, thread_2.id], include_original_message: false } }
it "does not include the original message in the thread_message_ids" do
expect(result.find { |res| res.thread_id == thread_1.id }.to_h).to eq(
{
thread_message_ids: [message_1.id, message_2.id],
thread_id: thread_1.id,
original_message_id: thread_1.original_message_id,
},
)
end
end
end
context "when message_ids provided" do
let(:params) do
{
message_ids: [
thread_1.original_message_id,
thread_2.original_message_id,
message_1.id,
message_2.id,
message_3.id,
],
}
end
it "groups all the message ids in each thread by thread ID" do
expect(result.find { |res| res.thread_id == thread_1.id }.to_h).to eq(
{
thread_message_ids: [thread_1.original_message_id, message_1.id, message_2.id],
thread_id: thread_1.id,
original_message_id: thread_1.original_message_id,
},
)
expect(result.find { |res| res.thread_id == thread_2.id }.to_h).to eq(
{
thread_message_ids: [thread_2.original_message_id, message_3.id],
thread_id: thread_2.id,
original_message_id: thread_2.original_message_id,
},
)
end
context "when include_original_message is false" do
let(:params) do
{
message_ids: [
thread_1.original_message_id,
thread_2.original_message_id,
message_1.id,
message_2.id,
message_3.id,
],
include_original_message: false,
}
end
it "does not include the original message in the thread_message_ids" do
expect(result.find { |res| res.thread_id == thread_1.id }.to_h).to eq(
{
thread_message_ids: [message_1.id, message_2.id],
thread_id: thread_1.id,
original_message_id: thread_1.original_message_id,
},
)
end
end
end
end
end