mirror of
https://github.com/discourse/discourse.git
synced 2025-06-06 23:07:28 +08:00
DEV: Refactor UpdateUserLastRead
a little
We’re now using `contract` as the first step and validations for mandatory parameters have been added. To simplify specs a bit, we only assert the service contract is run as expected without testing each validation case. We’re now testing the contract itself in isolation.
This commit is contained in:

committed by
Loïc Guitaut

parent
60ad836313
commit
5f4623ba47
@ -17,9 +17,9 @@ module Chat
|
|||||||
# @param [Guardian] guardian
|
# @param [Guardian] guardian
|
||||||
# @return [Chat::Service::Base::Context]
|
# @return [Chat::Service::Base::Context]
|
||||||
|
|
||||||
|
contract
|
||||||
model :membership, :fetch_active_membership
|
model :membership, :fetch_active_membership
|
||||||
policy :invalid_access
|
policy :invalid_access
|
||||||
contract
|
|
||||||
policy :ensure_message_id_recency
|
policy :ensure_message_id_recency
|
||||||
policy :ensure_message_exists
|
policy :ensure_message_exists
|
||||||
step :update_last_read_message_id
|
step :update_last_read_message_id
|
||||||
@ -31,6 +31,8 @@ module Chat
|
|||||||
attribute :message_id, :integer
|
attribute :message_id, :integer
|
||||||
attribute :user_id, :integer
|
attribute :user_id, :integer
|
||||||
attribute :channel_id, :integer
|
attribute :channel_id, :integer
|
||||||
|
|
||||||
|
validates :message_id, :user_id, :channel_id, presence: true
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
@ -1,118 +1,119 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
RSpec.describe(Chat::Service::UpdateUserLastRead) do
|
RSpec.describe Chat::Service::UpdateUserLastRead do
|
||||||
subject(:result) { described_class.call(params) }
|
describe Chat::Service::UpdateUserLastRead::Contract, type: :model do
|
||||||
|
it { is_expected.to validate_presence_of :user_id }
|
||||||
fab!(:current_user) { Fabricate(:user) }
|
it { is_expected.to validate_presence_of :channel_id }
|
||||||
fab!(:channel) { Fabricate(:chat_channel) }
|
it { is_expected.to validate_presence_of :message_id }
|
||||||
fab!(:membership) do
|
|
||||||
Fabricate(:user_chat_channel_membership, user: current_user, chat_channel: channel)
|
|
||||||
end
|
|
||||||
fab!(:message_1) { Fabricate(:chat_message, chat_channel: membership.chat_channel) }
|
|
||||||
|
|
||||||
let(:guardian) { Guardian.new(current_user) }
|
|
||||||
let(:params) do
|
|
||||||
{
|
|
||||||
guardian: guardian,
|
|
||||||
user_id: current_user.id,
|
|
||||||
channel_id: channel.id,
|
|
||||||
message_id: message_1.id,
|
|
||||||
}
|
|
||||||
end
|
end
|
||||||
|
|
||||||
context "when channel_id is not provided" do
|
describe ".call" do
|
||||||
before { params.delete(:channel_id) }
|
subject(:result) { described_class.call(params) }
|
||||||
|
|
||||||
it { is_expected.to fail_to_find_a_model(:membership) }
|
fab!(:current_user) { Fabricate(:user) }
|
||||||
end
|
fab!(:channel) { Fabricate(:chat_channel) }
|
||||||
|
|
||||||
context "when user_id is not provided" do
|
|
||||||
before { params.delete(:user_id) }
|
|
||||||
|
|
||||||
it { is_expected.to fail_to_find_a_model(:membership) }
|
|
||||||
end
|
|
||||||
|
|
||||||
context "when user has no membership" do
|
|
||||||
before { membership.destroy! }
|
|
||||||
|
|
||||||
it { is_expected.to fail_to_find_a_model(:membership) }
|
|
||||||
end
|
|
||||||
|
|
||||||
context "when user can’t access the channel" do
|
|
||||||
fab!(:membership) do
|
fab!(:membership) do
|
||||||
Fabricate(
|
Fabricate(:user_chat_channel_membership, user: current_user, chat_channel: channel)
|
||||||
:user_chat_channel_membership,
|
end
|
||||||
user: current_user,
|
fab!(:message_1) { Fabricate(:chat_message, chat_channel: membership.chat_channel) }
|
||||||
chat_channel: Fabricate(:private_category_channel),
|
|
||||||
)
|
let(:guardian) { Guardian.new(current_user) }
|
||||||
|
let(:params) do
|
||||||
|
{
|
||||||
|
guardian: guardian,
|
||||||
|
user_id: current_user.id,
|
||||||
|
channel_id: channel.id,
|
||||||
|
message_id: message_1.id,
|
||||||
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
before { params[:channel_id] = membership.chat_channel.id }
|
context "when params are not valid" do
|
||||||
|
before { params.delete(:user_id) }
|
||||||
|
|
||||||
it { is_expected.to fail_a_policy(:invalid_access) }
|
it { is_expected.to fail_a_contract }
|
||||||
end
|
|
||||||
|
|
||||||
context "when message_id is older than membership's last_read_message_id" do
|
|
||||||
before do
|
|
||||||
params[:message_id] = -2
|
|
||||||
membership.update!(last_read_message_id: -1)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
it { is_expected.to fail_a_policy(:ensure_message_id_recency) }
|
context "when params are valid" do
|
||||||
end
|
context "when user has no membership" do
|
||||||
|
before { membership.destroy! }
|
||||||
|
|
||||||
context "when message doesn’t exist" do
|
it { is_expected.to fail_to_find_a_model(:membership) }
|
||||||
before do
|
end
|
||||||
params[:message_id] = 2
|
|
||||||
membership.update!(last_read_message_id: 1)
|
|
||||||
end
|
|
||||||
|
|
||||||
it { is_expected.to fail_a_policy(:ensure_message_exists) }
|
context "when user can’t access the channel" do
|
||||||
end
|
fab!(:membership) do
|
||||||
|
Fabricate(
|
||||||
|
:user_chat_channel_membership,
|
||||||
|
user: current_user,
|
||||||
|
chat_channel: Fabricate(:private_category_channel),
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
context "when params are valid" do
|
before { params[:channel_id] = membership.chat_channel.id }
|
||||||
before { Jobs.run_immediately! }
|
|
||||||
|
|
||||||
it "sets the service result as successful" do
|
it { is_expected.to fail_a_policy(:invalid_access) }
|
||||||
expect(result).to be_a_success
|
end
|
||||||
end
|
|
||||||
|
|
||||||
it "updates the last_read message id" do
|
context "when message_id is older than membership's last_read_message_id" do
|
||||||
expect { result }.to change { membership.reload.last_read_message_id }.to(message_1.id)
|
before do
|
||||||
end
|
params[:message_id] = -2
|
||||||
|
membership.update!(last_read_message_id: -1)
|
||||||
|
end
|
||||||
|
|
||||||
it "marks existing notifications related to the message as read" do
|
it { is_expected.to fail_a_policy(:ensure_message_id_recency) }
|
||||||
expect {
|
end
|
||||||
notification =
|
|
||||||
|
context "when message doesn’t exist" do
|
||||||
|
before do
|
||||||
|
params[:message_id] = 2
|
||||||
|
membership.update!(last_read_message_id: 1)
|
||||||
|
end
|
||||||
|
|
||||||
|
it { is_expected.to fail_a_policy(:ensure_message_exists) }
|
||||||
|
end
|
||||||
|
|
||||||
|
context "when everything is fine" do
|
||||||
|
fab!(:notification) do
|
||||||
Fabricate(
|
Fabricate(
|
||||||
:notification,
|
:notification,
|
||||||
notification_type: Notification.types[:chat_mention],
|
notification_type: Notification.types[:chat_mention],
|
||||||
user: current_user,
|
user: current_user,
|
||||||
)
|
)
|
||||||
|
end
|
||||||
|
|
||||||
# FIXME: we need a better way to create proper chat mention
|
let(:messages) { MessageBus.track_publish { result } }
|
||||||
ChatMention.create!(notification: notification, user: current_user, chat_message: message_1)
|
|
||||||
}.to change {
|
|
||||||
Notification.where(
|
|
||||||
notification_type: Notification.types[:chat_mention],
|
|
||||||
user: current_user,
|
|
||||||
read: false,
|
|
||||||
).count
|
|
||||||
}.by(1)
|
|
||||||
|
|
||||||
expect { result }.to change {
|
before do
|
||||||
Notification.where(
|
Jobs.run_immediately!
|
||||||
notification_type: Notification.types[:chat_mention],
|
ChatMention.create!(
|
||||||
user: current_user,
|
notification: notification,
|
||||||
read: false,
|
user: current_user,
|
||||||
).count
|
chat_message: message_1,
|
||||||
}.by(-1)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
it "publishes new last read to clients" do
|
it "sets the service result as successful" do
|
||||||
messages = MessageBus.track_publish { result }
|
expect(result).to be_a_success
|
||||||
|
end
|
||||||
|
|
||||||
expect(messages.map(&:channel)).to include("/chat/user-tracking-state/#{current_user.id}")
|
it "updates the last_read message id" do
|
||||||
|
expect { result }.to change { membership.reload.last_read_message_id }.to(message_1.id)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "marks existing notifications related to the message as read" do
|
||||||
|
expect { result }.to change {
|
||||||
|
Notification.where(
|
||||||
|
notification_type: Notification.types[:chat_mention],
|
||||||
|
user: current_user,
|
||||||
|
read: false,
|
||||||
|
).count
|
||||||
|
}.by(-1)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "publishes new last read to clients" do
|
||||||
|
expect(messages.map(&:channel)).to include("/chat/user-tracking-state/#{current_user.id}")
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
Reference in New Issue
Block a user