DEV: Refactor some services from chat
Extracted from https://github.com/discourse/discourse/pull/29129. This patch makes the code more compliant with the upcoming service docs best practices.
This commit is contained in:
parent
43a0ea876a
commit
08e9364573
|
@ -47,6 +47,10 @@ module Chat
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
|
def fetch_channel(contract:)
|
||||||
|
::Chat::Channel.includes(:chatable).find_by(id: contract.channel_id)
|
||||||
|
end
|
||||||
|
|
||||||
def can_add_users_to_channel(guardian:, channel:)
|
def can_add_users_to_channel(guardian:, channel:)
|
||||||
(guardian.user.admin? || channel.joined_by?(guardian.user)) &&
|
(guardian.user.admin? || channel.joined_by?(guardian.user)) &&
|
||||||
channel.direct_message_channel? && channel.chatable.group
|
channel.direct_message_channel? && channel.chatable.group
|
||||||
|
@ -60,10 +64,6 @@ module Chat
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
def fetch_channel(contract:)
|
|
||||||
::Chat::Channel.includes(:chatable).find_by(id: contract.channel_id)
|
|
||||||
end
|
|
||||||
|
|
||||||
def upsert_memberships(channel:, target_users:)
|
def upsert_memberships(channel:, target_users:)
|
||||||
always_level = ::Chat::UserChatChannelMembership::NOTIFICATION_LEVELS[:always]
|
always_level = ::Chat::UserChatChannelMembership::NOTIFICATION_LEVELS[:always]
|
||||||
|
|
||||||
|
|
|
@ -14,13 +14,13 @@ module Chat
|
||||||
class HandleCategoryUpdated
|
class HandleCategoryUpdated
|
||||||
include Service::Base
|
include Service::Base
|
||||||
|
|
||||||
|
policy :chat_enabled
|
||||||
contract do
|
contract do
|
||||||
attribute :category_id, :integer
|
attribute :category_id, :integer
|
||||||
|
|
||||||
validates :category_id, presence: true
|
validates :category_id, presence: true
|
||||||
end
|
end
|
||||||
step :assign_defaults
|
step :assign_defaults
|
||||||
policy :chat_enabled
|
|
||||||
model :category
|
model :category
|
||||||
model :category_channel_ids
|
model :category_channel_ids
|
||||||
model :users
|
model :users
|
||||||
|
|
|
@ -21,13 +21,13 @@ module Chat
|
||||||
class HandleDestroyedGroup
|
class HandleDestroyedGroup
|
||||||
include Service::Base
|
include Service::Base
|
||||||
|
|
||||||
|
policy :chat_enabled
|
||||||
contract do
|
contract do
|
||||||
attribute :destroyed_group_user_ids
|
attribute :destroyed_group_user_ids, :array
|
||||||
|
|
||||||
validates :destroyed_group_user_ids, presence: true
|
validates :destroyed_group_user_ids, presence: true
|
||||||
end
|
end
|
||||||
step :assign_defaults
|
step :assign_defaults
|
||||||
policy :chat_enabled
|
|
||||||
policy :not_everyone_allowed
|
policy :not_everyone_allowed
|
||||||
model :scoped_users
|
model :scoped_users
|
||||||
step :remove_users_outside_allowed_groups
|
step :remove_users_outside_allowed_groups
|
||||||
|
@ -48,7 +48,7 @@ module Chat
|
||||||
!SiteSetting.chat_allowed_groups_map.include?(Group::AUTO_GROUPS[:everyone])
|
!SiteSetting.chat_allowed_groups_map.include?(Group::AUTO_GROUPS[:everyone])
|
||||||
end
|
end
|
||||||
|
|
||||||
def fetch_scoped_users(destroyed_group_user_ids:)
|
def fetch_scoped_users(contract:)
|
||||||
User
|
User
|
||||||
.real
|
.real
|
||||||
.activated
|
.activated
|
||||||
|
@ -56,7 +56,7 @@ module Chat
|
||||||
.not_staged
|
.not_staged
|
||||||
.includes(:group_users)
|
.includes(:group_users)
|
||||||
.where("NOT admin AND NOT moderator")
|
.where("NOT admin AND NOT moderator")
|
||||||
.where(id: destroyed_group_user_ids)
|
.where(id: contract.destroyed_group_user_ids)
|
||||||
.joins(:user_chat_channel_memberships)
|
.joins(:user_chat_channel_memberships)
|
||||||
.distinct
|
.distinct
|
||||||
end
|
end
|
||||||
|
|
|
@ -20,13 +20,13 @@ module Chat
|
||||||
class HandleUserRemovedFromGroup
|
class HandleUserRemovedFromGroup
|
||||||
include Service::Base
|
include Service::Base
|
||||||
|
|
||||||
|
policy :chat_enabled
|
||||||
contract do
|
contract do
|
||||||
attribute :user_id, :integer
|
attribute :user_id, :integer
|
||||||
|
|
||||||
validates :user_id, presence: true
|
validates :user_id, presence: true
|
||||||
end
|
end
|
||||||
step :assign_defaults
|
step :assign_defaults
|
||||||
policy :chat_enabled
|
|
||||||
policy :not_everyone_allowed
|
policy :not_everyone_allowed
|
||||||
model :user
|
model :user
|
||||||
policy :user_not_staff
|
policy :user_not_staff
|
||||||
|
|
|
@ -37,7 +37,7 @@ module Chat
|
||||||
|
|
||||||
validates :message_id, presence: true
|
validates :message_id, presence: true
|
||||||
validates :channel_id, presence: true
|
validates :channel_id, presence: true
|
||||||
validates :flag_type_id, inclusion: { in: ->(_flag_type) { ::ReviewableScore.types.values } }
|
validates :flag_type_id, inclusion: { in: -> { ::ReviewableScore.types.values } }
|
||||||
end
|
end
|
||||||
model :message
|
model :message
|
||||||
policy :can_flag_message_in_channel
|
policy :can_flag_message_in_channel
|
||||||
|
|
|
@ -39,7 +39,6 @@ module Chat
|
||||||
},
|
},
|
||||||
allow_nil: true
|
allow_nil: true
|
||||||
end
|
end
|
||||||
|
|
||||||
model :channel
|
model :channel
|
||||||
policy :can_view_channel
|
policy :can_view_channel
|
||||||
model :membership, optional: true
|
model :membership, optional: true
|
||||||
|
|
|
@ -34,7 +34,7 @@ module Chat
|
||||||
private
|
private
|
||||||
|
|
||||||
def clean_term(contract:)
|
def clean_term(contract:)
|
||||||
context[:term] = contract.term&.downcase&.strip&.gsub(/^[@#]+/, "")
|
contract.term = contract.term&.downcase&.strip&.gsub(/^[@#]+/, "")
|
||||||
end
|
end
|
||||||
|
|
||||||
def fetch_memberships(guardian:)
|
def fetch_memberships(guardian:)
|
||||||
|
@ -44,31 +44,31 @@ module Chat
|
||||||
def fetch_users(guardian:, contract:)
|
def fetch_users(guardian:, contract:)
|
||||||
return unless contract.include_users
|
return unless contract.include_users
|
||||||
return unless guardian.can_create_direct_message?
|
return unless guardian.can_create_direct_message?
|
||||||
search_users(context, guardian)
|
search_users(contract, guardian)
|
||||||
end
|
end
|
||||||
|
|
||||||
def fetch_groups(guardian:, contract:)
|
def fetch_groups(guardian:, contract:)
|
||||||
return unless contract.include_groups
|
return unless contract.include_groups
|
||||||
return unless guardian.can_create_direct_message?
|
return unless guardian.can_create_direct_message?
|
||||||
search_groups(context, guardian)
|
search_groups(contract, guardian)
|
||||||
end
|
end
|
||||||
|
|
||||||
def fetch_category_channels(guardian:, contract:)
|
def fetch_category_channels(guardian:, contract:)
|
||||||
return unless contract.include_category_channels
|
return unless contract.include_category_channels
|
||||||
return unless SiteSetting.enable_public_channels
|
return unless SiteSetting.enable_public_channels
|
||||||
search_category_channels(context, guardian)
|
search_category_channels(contract, guardian)
|
||||||
end
|
end
|
||||||
|
|
||||||
def fetch_direct_message_channels(guardian:, contract:, users:)
|
def fetch_direct_message_channels(guardian:, contract:, users:)
|
||||||
return unless contract.include_direct_message_channels
|
return unless contract.include_direct_message_channels
|
||||||
return unless guardian.can_create_direct_message?
|
return unless guardian.can_create_direct_message?
|
||||||
search_direct_message_channels(context, guardian, contract, users)
|
search_direct_message_channels(guardian, contract, users)
|
||||||
end
|
end
|
||||||
|
|
||||||
def search_users(context, guardian)
|
def search_users(contract, guardian)
|
||||||
user_search = ::UserSearch.new(context.term, limit: SEARCH_RESULT_LIMIT)
|
user_search = ::UserSearch.new(contract.term, limit: SEARCH_RESULT_LIMIT)
|
||||||
|
|
||||||
if context.term.blank?
|
if contract.term.blank?
|
||||||
user_search = user_search.scoped_users
|
user_search = user_search.scoped_users
|
||||||
else
|
else
|
||||||
user_search = user_search.search
|
user_search = user_search.search
|
||||||
|
@ -80,45 +80,45 @@ module Chat
|
||||||
user_search = user_search.real(allowed_bot_user_ids: allowed_bot_user_ids)
|
user_search = user_search.real(allowed_bot_user_ids: allowed_bot_user_ids)
|
||||||
user_search = user_search.includes(:user_option)
|
user_search = user_search.includes(:user_option)
|
||||||
|
|
||||||
if context.excluded_memberships_channel_id
|
if contract.excluded_memberships_channel_id
|
||||||
user_search =
|
user_search =
|
||||||
user_search.where(
|
user_search.where(
|
||||||
"NOT EXISTS (SELECT 1 FROM user_chat_channel_memberships WHERE user_id = users.id AND chat_channel_id = ?)",
|
"NOT EXISTS (SELECT 1 FROM user_chat_channel_memberships WHERE user_id = users.id AND chat_channel_id = ?)",
|
||||||
context.excluded_memberships_channel_id,
|
contract.excluded_memberships_channel_id,
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
user_search
|
user_search
|
||||||
end
|
end
|
||||||
|
|
||||||
def search_groups(context, guardian)
|
def search_groups(contract, guardian)
|
||||||
Group
|
Group
|
||||||
.visible_groups(guardian.user)
|
.visible_groups(guardian.user)
|
||||||
.includes(users: :user_option)
|
.includes(users: :user_option)
|
||||||
.where(
|
.where(
|
||||||
"groups.name ILIKE :term_like OR groups.full_name ILIKE :term_like",
|
"groups.name ILIKE :term_like OR groups.full_name ILIKE :term_like",
|
||||||
term_like: "%#{context.term}%",
|
term_like: "%#{contract.term}%",
|
||||||
)
|
)
|
||||||
.limit(SEARCH_RESULT_LIMIT)
|
.limit(SEARCH_RESULT_LIMIT)
|
||||||
end
|
end
|
||||||
|
|
||||||
def search_category_channels(context, guardian)
|
def search_category_channels(contract, guardian)
|
||||||
::Chat::ChannelFetcher.secured_public_channel_search(
|
::Chat::ChannelFetcher.secured_public_channel_search(
|
||||||
guardian,
|
guardian,
|
||||||
status: :open,
|
status: :open,
|
||||||
filter: context.term,
|
filter: contract.term,
|
||||||
filter_on_category_name: false,
|
filter_on_category_name: false,
|
||||||
match_filter_on_starts_with: false,
|
match_filter_on_starts_with: false,
|
||||||
limit: SEARCH_RESULT_LIMIT,
|
limit: SEARCH_RESULT_LIMIT,
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
def search_direct_message_channels(context, guardian, contract, users)
|
def search_direct_message_channels(guardian, contract, users)
|
||||||
channels =
|
channels =
|
||||||
::Chat::ChannelFetcher.secured_direct_message_channels_search(
|
::Chat::ChannelFetcher.secured_direct_message_channels_search(
|
||||||
guardian.user.id,
|
guardian.user.id,
|
||||||
guardian,
|
guardian,
|
||||||
filter: context.term,
|
filter: contract.term,
|
||||||
match_filter_on_starts_with: false,
|
match_filter_on_starts_with: false,
|
||||||
limit: SEARCH_RESULT_LIMIT,
|
limit: SEARCH_RESULT_LIMIT,
|
||||||
) || []
|
) || []
|
||||||
|
|
|
@ -65,13 +65,11 @@ module Chat
|
||||||
end
|
end
|
||||||
|
|
||||||
def update_channel(channel:, contract:)
|
def update_channel(channel:, contract:)
|
||||||
channel.assign_attributes(contract.attributes)
|
channel.update!(contract.attributes)
|
||||||
context[:threading_enabled_changed] = channel.threading_enabled_changed?
|
|
||||||
channel.save!
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def mark_all_threads_as_read_if_needed(channel:)
|
def mark_all_threads_as_read_if_needed(channel:)
|
||||||
return if !(context.threading_enabled_changed && channel.threading_enabled)
|
return unless channel.threading_enabled_previously_changed?(to: true)
|
||||||
Jobs.enqueue(Jobs::Chat::MarkAllChannelThreadsRead, channel_id: channel.id)
|
Jobs.enqueue(Jobs::Chat::MarkAllChannelThreadsRead, channel_id: channel.id)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -17,7 +17,7 @@ module Chat
|
||||||
|
|
||||||
model :channel, :fetch_channel
|
model :channel, :fetch_channel
|
||||||
contract do
|
contract do
|
||||||
attribute :status
|
attribute :status, :string
|
||||||
|
|
||||||
validates :status, inclusion: { in: Chat::Channel.editable_statuses.keys }
|
validates :status, inclusion: { in: Chat::Channel.editable_statuses.keys }
|
||||||
end
|
end
|
||||||
|
@ -30,13 +30,13 @@ module Chat
|
||||||
Chat::Channel.find_by(id: channel_id)
|
Chat::Channel.find_by(id: channel_id)
|
||||||
end
|
end
|
||||||
|
|
||||||
def check_channel_permission(guardian:, channel:, status:)
|
def check_channel_permission(guardian:, channel:, contract:)
|
||||||
guardian.can_preview_chat_channel?(channel) &&
|
guardian.can_preview_chat_channel?(channel) &&
|
||||||
guardian.can_change_channel_status?(channel, status.to_sym)
|
guardian.can_change_channel_status?(channel, contract.status.to_sym)
|
||||||
end
|
end
|
||||||
|
|
||||||
def change_status(channel:, status:, guardian:)
|
def change_status(channel:, contract:, guardian:)
|
||||||
channel.public_send("#{status}!", guardian.user)
|
channel.public_send("#{contract.status}!", guardian.user)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -76,19 +76,19 @@ RSpec.describe Chat::Api::ChannelMessagesController do
|
||||||
|
|
||||||
describe "#create" do
|
describe "#create" do
|
||||||
context "when force_thread param is given" do
|
context "when force_thread param is given" do
|
||||||
it "removes it from params" do
|
let!(:message) { Fabricate(:chat_message, chat_channel: channel) }
|
||||||
sign_in(current_user)
|
|
||||||
|
|
||||||
message_1 = Fabricate(:chat_message, chat_channel: channel)
|
before { sign_in(current_user) }
|
||||||
|
|
||||||
|
it "ignores it" do
|
||||||
expect {
|
expect {
|
||||||
post "/chat/#{channel.id}.json",
|
post "/chat/#{channel.id}.json",
|
||||||
params: {
|
params: {
|
||||||
in_reply_to_id: message_1.id,
|
in_reply_to_id: message.id,
|
||||||
message: "test",
|
message: "test",
|
||||||
force_thread: true,
|
force_thread: true,
|
||||||
}
|
}
|
||||||
}.to change { Chat::Thread.where(force: false).count }.by(1)
|
}.not_to change { Chat::Thread.where(force: true).count }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -51,9 +51,7 @@ RSpec.describe Chat::AutoRemove::HandleCategoryUpdated do
|
||||||
updated_category.category_groups.delete_all
|
updated_category.category_groups.delete_all
|
||||||
end
|
end
|
||||||
|
|
||||||
it "sets the service result as successful" do
|
it { is_expected.to run_successfully }
|
||||||
expect(result).to be_a_success
|
|
||||||
end
|
|
||||||
|
|
||||||
it "does not kick any users since the default permission is Everyone (full)" do
|
it "does not kick any users since the default permission is Everyone (full)" do
|
||||||
expect { result }.not_to change {
|
expect { result }.not_to change {
|
||||||
|
@ -90,9 +88,7 @@ RSpec.describe Chat::AutoRemove::HandleCategoryUpdated do
|
||||||
group_2.add(user_1)
|
group_2.add(user_1)
|
||||||
end
|
end
|
||||||
|
|
||||||
it "sets the service result as successful" do
|
it { is_expected.to run_successfully }
|
||||||
expect(result).to be_a_success
|
|
||||||
end
|
|
||||||
|
|
||||||
it "kicks all regular users who are not in any groups with reply + see permissions" do
|
it "kicks all regular users who are not in any groups with reply + see permissions" do
|
||||||
expect { result }.to change {
|
expect { result }.to change {
|
||||||
|
|
|
@ -1,6 +1,10 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
RSpec.describe Chat::AutoRemove::HandleDestroyedGroup do
|
RSpec.describe Chat::AutoRemove::HandleDestroyedGroup do
|
||||||
|
describe described_class::Contract, type: :model do
|
||||||
|
it { is_expected.to validate_presence_of(:destroyed_group_user_ids) }
|
||||||
|
end
|
||||||
|
|
||||||
describe ".call" do
|
describe ".call" do
|
||||||
subject(:result) { described_class.call(params) }
|
subject(:result) { described_class.call(params) }
|
||||||
|
|
||||||
|
@ -45,9 +49,7 @@ RSpec.describe Chat::AutoRemove::HandleDestroyedGroup do
|
||||||
channel_1.add(admin_2)
|
channel_1.add(admin_2)
|
||||||
end
|
end
|
||||||
|
|
||||||
it "sets the service result as successful" do
|
it { is_expected.to run_successfully }
|
||||||
expect(result).to be_a_success
|
|
||||||
end
|
|
||||||
|
|
||||||
it "removes the destroyed_group_user_ids from all public channels" do
|
it "removes the destroyed_group_user_ids from all public channels" do
|
||||||
expect { result }.to change {
|
expect { result }.to change {
|
||||||
|
@ -132,9 +134,7 @@ RSpec.describe Chat::AutoRemove::HandleDestroyedGroup do
|
||||||
channel_1.add(admin_2)
|
channel_1.add(admin_2)
|
||||||
end
|
end
|
||||||
|
|
||||||
it "sets the service result as successful" do
|
it { is_expected.to run_successfully }
|
||||||
expect(result).to be_a_success
|
|
||||||
end
|
|
||||||
|
|
||||||
it "does not remove any memberships" do
|
it "does not remove any memberships" do
|
||||||
expect { result }.not_to change { Chat::UserChatChannelMembership.count }
|
expect { result }.not_to change { Chat::UserChatChannelMembership.count }
|
||||||
|
@ -196,9 +196,7 @@ RSpec.describe Chat::AutoRemove::HandleDestroyedGroup do
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
it "sets the service result as successful" do
|
it { is_expected.to run_successfully }
|
||||||
expect(result).to be_a_success
|
|
||||||
end
|
|
||||||
|
|
||||||
it "removes the destroyed_group_user_ids from the channel" do
|
it "removes the destroyed_group_user_ids from the channel" do
|
||||||
expect { result }.to change {
|
expect { result }.to change {
|
||||||
|
@ -237,9 +235,7 @@ RSpec.describe Chat::AutoRemove::HandleDestroyedGroup do
|
||||||
context "when one of the users is not in any of the groups" do
|
context "when one of the users is not in any of the groups" do
|
||||||
before { user_2.change_trust_level!(TrustLevel[3]) }
|
before { user_2.change_trust_level!(TrustLevel[3]) }
|
||||||
|
|
||||||
it "sets the service result as successful" do
|
it { is_expected.to run_successfully }
|
||||||
expect(result).to be_a_success
|
|
||||||
end
|
|
||||||
|
|
||||||
it "removes the destroyed_group_user_ids from the channel" do
|
it "removes the destroyed_group_user_ids from the channel" do
|
||||||
expect { result }.to change {
|
expect { result }.to change {
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
RSpec.describe Chat::CreateDirectMessageChannel do
|
RSpec.describe Chat::CreateDirectMessageChannel do
|
||||||
describe Chat::CreateDirectMessageChannel::Contract, type: :model do
|
describe described_class::Contract, type: :model do
|
||||||
subject(:contract) { described_class.new(params) }
|
subject(:contract) { described_class.new(params) }
|
||||||
|
|
||||||
let(:params) { { target_usernames: %w[lechuck elaine] } }
|
let(:params) { { target_usernames: %w[lechuck elaine] } }
|
||||||
|
@ -45,10 +45,6 @@ RSpec.describe Chat::CreateDirectMessageChannel do
|
||||||
context "when all steps pass" do
|
context "when all steps pass" do
|
||||||
it { is_expected.to run_successfully }
|
it { is_expected.to run_successfully }
|
||||||
|
|
||||||
it "sets the service result as successful" do
|
|
||||||
expect(result).to be_a_success
|
|
||||||
end
|
|
||||||
|
|
||||||
it "updates user count" do
|
it "updates user count" do
|
||||||
expect(result.channel.user_count).to eq(3) # current user + user_1 + user_2
|
expect(result.channel.user_count).to eq(3) # current user + user_1 + user_2
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
RSpec.describe Chat::CreateThread do
|
RSpec.describe Chat::CreateThread do
|
||||||
describe Chat::CreateThread::Contract, type: :model do
|
describe described_class::Contract, type: :model do
|
||||||
it { is_expected.to validate_presence_of :channel_id }
|
it { is_expected.to validate_presence_of :channel_id }
|
||||||
it { is_expected.to validate_presence_of :original_message_id }
|
it { is_expected.to validate_presence_of :original_message_id }
|
||||||
|
it { is_expected.to validate_length_of(:title).is_at_most(Chat::Thread::MAX_TITLE_LENGTH) }
|
||||||
end
|
end
|
||||||
|
|
||||||
describe ".call" do
|
describe ".call" do
|
||||||
|
@ -68,12 +69,6 @@ RSpec.describe Chat::CreateThread do
|
||||||
it { is_expected.to fail_a_contract }
|
it { is_expected.to fail_a_contract }
|
||||||
end
|
end
|
||||||
|
|
||||||
context "when title is too long" do
|
|
||||||
let(:title) { "a" * Chat::Thread::MAX_TITLE_LENGTH + "a" }
|
|
||||||
|
|
||||||
it { is_expected.to fail_a_contract }
|
|
||||||
end
|
|
||||||
|
|
||||||
context "when original message is not found" do
|
context "when original message is not found" do
|
||||||
fab!(:channel_2) { Fabricate(:chat_channel, threading_enabled: true) }
|
fab!(:channel_2) { Fabricate(:chat_channel, threading_enabled: true) }
|
||||||
|
|
||||||
|
|
|
@ -2,8 +2,6 @@
|
||||||
|
|
||||||
RSpec.describe Chat::FlagMessage do
|
RSpec.describe Chat::FlagMessage do
|
||||||
describe described_class::Contract, type: :model do
|
describe described_class::Contract, type: :model do
|
||||||
subject(:contract) { described_class.new }
|
|
||||||
|
|
||||||
it { is_expected.to validate_presence_of(:channel_id) }
|
it { is_expected.to validate_presence_of(:channel_id) }
|
||||||
it { is_expected.to validate_presence_of(:message_id) }
|
it { is_expected.to validate_presence_of(:message_id) }
|
||||||
|
|
||||||
|
@ -26,9 +24,6 @@ RSpec.describe Chat::FlagMessage do
|
||||||
let(:message) { nil }
|
let(:message) { nil }
|
||||||
let(:is_warning) { nil }
|
let(:is_warning) { nil }
|
||||||
let(:take_action) { nil }
|
let(:take_action) { nil }
|
||||||
|
|
||||||
before { SiteSetting.direct_message_enabled_groups = Group::AUTO_GROUPS[:everyone] }
|
|
||||||
|
|
||||||
let(:params) do
|
let(:params) do
|
||||||
{
|
{
|
||||||
guardian: guardian,
|
guardian: guardian,
|
||||||
|
@ -41,23 +36,34 @@ RSpec.describe Chat::FlagMessage do
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
before { SiteSetting.direct_message_enabled_groups = Group::AUTO_GROUPS[:everyone] }
|
||||||
|
|
||||||
context "when all steps pass" do
|
context "when all steps pass" do
|
||||||
fab!(:current_user) { Fabricate(:admin) }
|
fab!(:current_user) { Fabricate(:admin) }
|
||||||
|
|
||||||
|
let(:reviewable) { Reviewable.last }
|
||||||
|
|
||||||
it { is_expected.to run_successfully }
|
it { is_expected.to run_successfully }
|
||||||
|
|
||||||
it "flags the message" do
|
it "flags the message" do
|
||||||
expect { result }.to change { Reviewable.count }.by(1)
|
expect { result }.to change { Reviewable.count }.by(1)
|
||||||
|
expect(reviewable).to have_attributes(
|
||||||
reviewable = Reviewable.last
|
target: message_1,
|
||||||
expect(reviewable.target_type).to eq("ChatMessage")
|
created_by: current_user,
|
||||||
expect(reviewable.created_by_id).to eq(current_user.id)
|
target_created_by: message_1.user,
|
||||||
expect(reviewable.target_created_by_id).to eq(message_1.user.id)
|
payload: {
|
||||||
expect(reviewable.target_id).to eq(message_1.id)
|
"message_cooked" => message_1.cooked,
|
||||||
expect(reviewable.payload).to eq("message_cooked" => message_1.cooked)
|
},
|
||||||
|
)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context "when contract is invalid" do
|
||||||
|
let(:channel_id) { nil }
|
||||||
|
|
||||||
|
it { is_expected.to fail_a_contract }
|
||||||
|
end
|
||||||
|
|
||||||
context "when channel is not found" do
|
context "when channel is not found" do
|
||||||
before { params[:channel_id] = -999 }
|
before { params[:channel_id] = -999 }
|
||||||
|
|
||||||
|
|
|
@ -1,88 +1,93 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
RSpec.describe Chat::InviteUsersToChannel do
|
RSpec.describe Chat::InviteUsersToChannel do
|
||||||
subject(:result) { described_class.call(params) }
|
|
||||||
|
|
||||||
describe described_class::Contract, type: :model do
|
describe described_class::Contract, type: :model do
|
||||||
subject(:contract) { described_class.new }
|
|
||||||
|
|
||||||
it { is_expected.to validate_presence_of :channel_id }
|
it { is_expected.to validate_presence_of :channel_id }
|
||||||
it { is_expected.to validate_presence_of :user_ids }
|
it { is_expected.to validate_presence_of :user_ids }
|
||||||
end
|
end
|
||||||
|
|
||||||
fab!(:current_user) { Fabricate(:admin) }
|
describe ".call" do
|
||||||
fab!(:user_1) { Fabricate(:user) }
|
subject(:result) { described_class.call(**params, **dependencies) }
|
||||||
fab!(:user_2) { Fabricate(:user) }
|
|
||||||
fab!(:channel_1) { Fabricate(:chat_channel) }
|
|
||||||
fab!(:group_1) { Fabricate(:group) }
|
|
||||||
|
|
||||||
let(:guardian) { current_user.guardian }
|
fab!(:current_user) { Fabricate(:admin) }
|
||||||
let(:user_ids) { [user_1.id, user_2.id] }
|
fab!(:user_1) { Fabricate(:user) }
|
||||||
let(:message_id) { nil }
|
fab!(:user_2) { Fabricate(:user) }
|
||||||
let(:params) do
|
fab!(:channel_1) { Fabricate(:chat_channel) }
|
||||||
{ guardian: guardian, channel_id: channel_1.id, message_id: message_id, user_ids: user_ids }
|
fab!(:group_1) { Fabricate(:group) }
|
||||||
end
|
|
||||||
|
|
||||||
before do
|
let(:guardian) { current_user.guardian }
|
||||||
group_1.add(user_1)
|
let(:user_ids) { [user_1.id, user_2.id] }
|
||||||
SiteSetting.chat_allowed_groups = group_1.id
|
let(:message_id) { nil }
|
||||||
end
|
let(:params) { { channel_id: channel_1.id, message_id:, user_ids: } }
|
||||||
|
let(:dependencies) { { guardian: } }
|
||||||
|
|
||||||
context "when all steps pass" do
|
before do
|
||||||
it { is_expected.to run_successfully }
|
group_1.add(user_1)
|
||||||
|
SiteSetting.chat_allowed_groups = group_1.id
|
||||||
it "creates the notifications for allowed users" do
|
|
||||||
result
|
|
||||||
|
|
||||||
notification = user_1.reload.notifications.last
|
|
||||||
expect(notification.notification_type).to eq(::Notification.types[:chat_invitation])
|
|
||||||
|
|
||||||
notification = user_2.reload.notifications.last
|
|
||||||
expect(notification).to be_nil
|
|
||||||
end
|
end
|
||||||
|
|
||||||
it "doesnt create notifications for suspended users" do
|
context "when all steps pass" do
|
||||||
user_1.update!(suspended_till: 2.days.from_now, suspended_at: Time.now)
|
it { is_expected.to run_successfully }
|
||||||
|
|
||||||
result
|
it "creates the notifications for allowed users" do
|
||||||
|
|
||||||
notification = user_1.reload.notifications.last
|
|
||||||
expect(notification).to be_nil
|
|
||||||
end
|
|
||||||
|
|
||||||
it "doesnt create notifications for users with disabled chat" do
|
|
||||||
user_1.user_option.update!(chat_enabled: false)
|
|
||||||
|
|
||||||
result
|
|
||||||
|
|
||||||
notification = user_1.reload.notifications.last
|
|
||||||
expect(notification).to be_nil
|
|
||||||
end
|
|
||||||
|
|
||||||
context "when message id is provided" do
|
|
||||||
fab!(:message_1) { Fabricate(:chat_message, chat_channel: channel_1) }
|
|
||||||
|
|
||||||
let(:message_id) { message_1.id }
|
|
||||||
|
|
||||||
it "sets the message id on the notification" do
|
|
||||||
result
|
result
|
||||||
|
|
||||||
data = JSON.parse(user_1.reload.notifications.last.data)
|
notification = user_1.reload.notifications.last
|
||||||
expect(data["chat_message_id"]).to eq(message_id)
|
expect(notification.notification_type).to eq(::Notification.types[:chat_invitation])
|
||||||
|
|
||||||
|
notification = user_2.reload.notifications.last
|
||||||
|
expect(notification).to be_nil
|
||||||
|
end
|
||||||
|
|
||||||
|
it "doesnt create notifications for suspended users" do
|
||||||
|
user_1.update!(suspended_till: 2.days.from_now, suspended_at: Time.now)
|
||||||
|
|
||||||
|
result
|
||||||
|
|
||||||
|
notification = user_1.reload.notifications.last
|
||||||
|
expect(notification).to be_nil
|
||||||
|
end
|
||||||
|
|
||||||
|
it "doesnt create notifications for users with disabled chat" do
|
||||||
|
user_1.user_option.update!(chat_enabled: false)
|
||||||
|
|
||||||
|
result
|
||||||
|
|
||||||
|
notification = user_1.reload.notifications.last
|
||||||
|
expect(notification).to be_nil
|
||||||
|
end
|
||||||
|
|
||||||
|
context "when message id is provided" do
|
||||||
|
fab!(:message_1) { Fabricate(:chat_message, chat_channel: channel_1) }
|
||||||
|
|
||||||
|
let(:message_id) { message_1.id }
|
||||||
|
|
||||||
|
it "sets the message id on the notification" do
|
||||||
|
result
|
||||||
|
|
||||||
|
data = JSON.parse(user_1.reload.notifications.last.data)
|
||||||
|
expect(data["chat_message_id"]).to eq(message_id)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
|
||||||
|
|
||||||
context "when channel model is not found" do
|
context "when contract is invalid" do
|
||||||
before { params[:channel_id] = -1 }
|
let(:user_ids) { nil }
|
||||||
|
|
||||||
it { is_expected.to fail_to_find_a_model(:channel) }
|
it { is_expected.to fail_a_contract }
|
||||||
end
|
end
|
||||||
|
|
||||||
context "when current user can't view channel" do
|
context "when channel model is not found" do
|
||||||
fab!(:current_user) { Fabricate(:user) }
|
before { params[:channel_id] = -1 }
|
||||||
fab!(:channel_1) { Fabricate(:private_category_channel) }
|
|
||||||
|
|
||||||
it { is_expected.to fail_a_policy(:can_view_channel) }
|
it { is_expected.to fail_to_find_a_model(:channel) }
|
||||||
|
end
|
||||||
|
|
||||||
|
context "when current user can't view channel" do
|
||||||
|
fab!(:current_user) { Fabricate(:user) }
|
||||||
|
fab!(:channel_1) { Fabricate(:private_category_channel) }
|
||||||
|
|
||||||
|
it { is_expected.to fail_a_policy(:can_view_channel) }
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -47,10 +47,10 @@ RSpec.describe Chat::SearchChatable do
|
||||||
|
|
||||||
it "cleans the term" do
|
it "cleans the term" do
|
||||||
params[:term] = "#bob"
|
params[:term] = "#bob"
|
||||||
expect(result.term).to eq("bob")
|
expect(result.contract.term).to eq("bob")
|
||||||
|
|
||||||
params[:term] = "@bob"
|
params[:term] = "@bob"
|
||||||
expect(result.term).to eq("bob")
|
expect(result.contract.term).to eq("bob")
|
||||||
end
|
end
|
||||||
|
|
||||||
it "fetches user memberships" do
|
it "fetches user memberships" do
|
||||||
|
|
|
@ -1,6 +1,10 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
RSpec.describe Chat::StopMessageStreaming do
|
RSpec.describe Chat::StopMessageStreaming do
|
||||||
|
describe described_class::Contract, type: :model do
|
||||||
|
it { is_expected.to validate_presence_of(:message_id) }
|
||||||
|
end
|
||||||
|
|
||||||
describe ".call" do
|
describe ".call" do
|
||||||
subject(:result) { described_class.call(params) }
|
subject(:result) { described_class.call(params) }
|
||||||
|
|
||||||
|
|
|
@ -1,24 +1,29 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
RSpec.describe Chat::TrashMessage do
|
RSpec.describe Chat::TrashMessage do
|
||||||
fab!(:current_user) { Fabricate(:user) }
|
describe described_class::Contract, type: :model do
|
||||||
let!(:guardian) { Guardian.new(current_user) }
|
it { is_expected.to validate_presence_of(:message_id) }
|
||||||
fab!(:message) { Fabricate(:chat_message, user: current_user) }
|
it { is_expected.to validate_presence_of(:channel_id) }
|
||||||
|
end
|
||||||
|
|
||||||
describe ".call" do
|
describe ".call" do
|
||||||
subject(:result) { described_class.call(params) }
|
subject(:result) { described_class.call(**params, **dependencies) }
|
||||||
|
|
||||||
|
fab!(:current_user) { Fabricate(:user) }
|
||||||
|
fab!(:message) { Fabricate(:chat_message, user: current_user) }
|
||||||
|
|
||||||
|
let(:guardian) { Guardian.new(current_user) }
|
||||||
|
let(:params) { { message_id: message.id, channel_id: } }
|
||||||
|
let(:dependencies) { { guardian: } }
|
||||||
|
let(:channel_id) { message.chat_channel_id }
|
||||||
|
|
||||||
context "when params are not valid" do
|
context "when params are not valid" do
|
||||||
let(:params) { { guardian: guardian } }
|
let(:params) { {} }
|
||||||
|
|
||||||
it { is_expected.to fail_a_contract }
|
it { is_expected.to fail_a_contract }
|
||||||
end
|
end
|
||||||
|
|
||||||
context "when params are valid" do
|
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
|
context "when the user does not have permission to delete" do
|
||||||
before { message.update!(user: Fabricate(:admin)) }
|
before { message.update!(user: Fabricate(:admin)) }
|
||||||
|
|
||||||
|
@ -26,9 +31,7 @@ RSpec.describe Chat::TrashMessage do
|
||||||
end
|
end
|
||||||
|
|
||||||
context "when the channel does not match the message" do
|
context "when the channel does not match the message" do
|
||||||
let(:params) do
|
let(:channel_id) { -1 }
|
||||||
{ guardian: guardian, message_id: message.id, channel_id: Fabricate(:chat_channel).id }
|
|
||||||
end
|
|
||||||
|
|
||||||
it { is_expected.to fail_to_find_a_model(:message) }
|
it { is_expected.to fail_to_find_a_model(:message) }
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,26 +1,30 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
RSpec.describe Chat::TrashMessages do
|
RSpec.describe Chat::TrashMessages do
|
||||||
fab!(:current_user) { Fabricate(:user) }
|
describe described_class::Contract, type: :model do
|
||||||
let!(:guardian) { Guardian.new(current_user) }
|
it { is_expected.to validate_presence_of(:channel_id) }
|
||||||
fab!(:chat_channel) { Fabricate(:chat_channel) }
|
it { is_expected.to allow_values([1], (1..200).to_a).for(:message_ids) }
|
||||||
fab!(:message1) { Fabricate(:chat_message, user: current_user, chat_channel: chat_channel) }
|
it { is_expected.not_to allow_values([], (1..201).to_a).for(:message_ids) }
|
||||||
fab!(:message2) { Fabricate(:chat_message, user: current_user, chat_channel: chat_channel) }
|
end
|
||||||
|
|
||||||
describe ".call" do
|
describe ".call" do
|
||||||
subject(:result) { described_class.call(params) }
|
subject(:result) { described_class.call(**params, **dependencies) }
|
||||||
|
|
||||||
|
fab!(:current_user) { Fabricate(:user) }
|
||||||
|
fab!(:chat_channel) { Fabricate(:chat_channel) }
|
||||||
|
fab!(:message1) { Fabricate(:chat_message, user: current_user, chat_channel: chat_channel) }
|
||||||
|
fab!(:message2) { Fabricate(:chat_message, user: current_user, chat_channel: chat_channel) }
|
||||||
|
let(:guardian) { Guardian.new(current_user) }
|
||||||
|
let(:params) { { message_ids: [message1.id, message2.id], channel_id: chat_channel.id } }
|
||||||
|
let(:dependencies) { { guardian: } }
|
||||||
|
|
||||||
context "when params are not valid" do
|
context "when params are not valid" do
|
||||||
let(:params) { { guardian: guardian } }
|
let(:params) { {} }
|
||||||
|
|
||||||
it { is_expected.to fail_a_contract }
|
it { is_expected.to fail_a_contract }
|
||||||
end
|
end
|
||||||
|
|
||||||
context "when params are valid" do
|
context "when params are valid" do
|
||||||
let(:params) do
|
|
||||||
{ guardian: guardian, message_ids: [message1.id, message2.id], channel_id: chat_channel.id }
|
|
||||||
end
|
|
||||||
|
|
||||||
context "when the user does not have permission to delete" do
|
context "when the user does not have permission to delete" do
|
||||||
before { message1.update!(user: Fabricate(:admin)) }
|
before { message1.update!(user: Fabricate(:admin)) }
|
||||||
|
|
||||||
|
@ -29,11 +33,7 @@ RSpec.describe Chat::TrashMessages do
|
||||||
|
|
||||||
context "when the channel does not match the message" do
|
context "when the channel does not match the message" do
|
||||||
let(:params) do
|
let(:params) do
|
||||||
{
|
{ message_ids: [message1.id, message2.id], channel_id: Fabricate(:chat_channel).id }
|
||||||
guardian: guardian,
|
|
||||||
message_ids: [message1.id, message2.id],
|
|
||||||
channel_id: Fabricate(:chat_channel).id,
|
|
||||||
}
|
|
||||||
end
|
end
|
||||||
|
|
||||||
it { is_expected.to fail_to_find_a_model(:messages) }
|
it { is_expected.to fail_to_find_a_model(:messages) }
|
||||||
|
|
|
@ -1,52 +1,57 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
RSpec.describe(Chat::UpdateChannelStatus) do
|
RSpec.describe(Chat::UpdateChannelStatus) do
|
||||||
subject(:result) do
|
describe described_class::Contract, type: :model do
|
||||||
described_class.call(guardian: guardian, channel_id: channel.id, status: status)
|
it do
|
||||||
|
is_expected.to validate_inclusion_of(:status).in_array(Chat::Channel.editable_statuses.keys)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
fab!(:channel) { Fabricate(:chat_channel) }
|
describe ".call" do
|
||||||
fab!(:current_user) { Fabricate(:admin) }
|
subject(:result) { described_class.call(**params, **dependencies) }
|
||||||
|
|
||||||
let(:guardian) { Guardian.new(current_user) }
|
fab!(:channel) { Fabricate(:chat_channel) }
|
||||||
let(:status) { "open" }
|
fab!(:current_user) { Fabricate(:admin) }
|
||||||
|
|
||||||
context "when no channel_id is given" do
|
let(:params) { { channel_id:, status: } }
|
||||||
subject(:result) { described_class.call(guardian: guardian, status: status) }
|
let(:dependencies) { { guardian: } }
|
||||||
|
let(:guardian) { Guardian.new(current_user) }
|
||||||
|
let(:status) { "open" }
|
||||||
|
let(:channel_id) { channel.id }
|
||||||
|
|
||||||
it { is_expected.to fail_to_find_a_model(:channel) }
|
context "when no channel_id is given" do
|
||||||
end
|
let(:channel_id) { nil }
|
||||||
|
|
||||||
context "when user is not allowed to change channel status" do
|
it { is_expected.to fail_to_find_a_model(:channel) }
|
||||||
fab!(:current_user) { Fabricate(:user) }
|
end
|
||||||
|
|
||||||
it { is_expected.to fail_a_policy(:check_channel_permission) }
|
context "when user is not allowed to change channel status" do
|
||||||
end
|
let!(:current_user) { Fabricate(:user) }
|
||||||
|
|
||||||
context "when status is not allowed" do
|
it { is_expected.to fail_a_policy(:check_channel_permission) }
|
||||||
(Chat::Channel.statuses.keys - Chat::Channel.editable_statuses.keys).each do |na_status|
|
end
|
||||||
context "when status is '#{na_status}'" do
|
|
||||||
let(:status) { na_status }
|
|
||||||
|
|
||||||
it { is_expected.to fail_a_contract }
|
context "when contract is invalid" do
|
||||||
|
let(:status) { :invalid_status }
|
||||||
|
|
||||||
|
it { is_expected.to fail_a_contract }
|
||||||
|
end
|
||||||
|
|
||||||
|
context "when new status is the same than the existing one" do
|
||||||
|
let(:status) { channel.status }
|
||||||
|
|
||||||
|
it { is_expected.to fail_a_policy(:check_channel_permission) }
|
||||||
|
end
|
||||||
|
|
||||||
|
context "when everything's ok" do
|
||||||
|
let(:status) { "closed" }
|
||||||
|
|
||||||
|
it { is_expected.to run_successfully }
|
||||||
|
|
||||||
|
it "changes the status" do
|
||||||
|
result
|
||||||
|
expect(channel.reload).to be_closed
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context "when new status is the same than the existing one" do
|
|
||||||
let(:status) { channel.status }
|
|
||||||
|
|
||||||
it { is_expected.to fail_a_policy(:check_channel_permission) }
|
|
||||||
end
|
|
||||||
|
|
||||||
context "when status is allowed" do
|
|
||||||
let(:status) { "closed" }
|
|
||||||
|
|
||||||
it { is_expected.to run_successfully }
|
|
||||||
|
|
||||||
it "changes the status" do
|
|
||||||
result
|
|
||||||
expect(channel.reload).to be_closed
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,10 +1,13 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
RSpec.describe Chat::UpdateThreadNotificationSettings do
|
RSpec.describe Chat::UpdateThreadNotificationSettings do
|
||||||
describe Chat::UpdateThreadNotificationSettings::Contract, type: :model do
|
describe described_class::Contract, type: :model do
|
||||||
|
let(:notification_levels) { Chat::UserChatThreadMembership.notification_levels.values }
|
||||||
|
|
||||||
it { is_expected.to validate_presence_of :channel_id }
|
it { is_expected.to validate_presence_of :channel_id }
|
||||||
it { is_expected.to validate_presence_of :thread_id }
|
it { is_expected.to validate_presence_of :thread_id }
|
||||||
it { is_expected.to validate_presence_of :notification_level }
|
it { is_expected.to validate_presence_of :notification_level }
|
||||||
|
it { is_expected.to validate_inclusion_of(:notification_level).in_array(notification_levels) }
|
||||||
end
|
end
|
||||||
|
|
||||||
describe ".call" do
|
describe ".call" do
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
RSpec.describe Chat::UpdateThread do
|
RSpec.describe Chat::UpdateThread do
|
||||||
describe Chat::UpdateThread::Contract, type: :model do
|
describe described_class::Contract, type: :model do
|
||||||
it { is_expected.to validate_presence_of :thread_id }
|
it { is_expected.to validate_presence_of :thread_id }
|
||||||
|
it { is_expected.to validate_length_of(:title).is_at_most(Chat::Thread::MAX_TITLE_LENGTH) }
|
||||||
end
|
end
|
||||||
|
|
||||||
describe ".call" do
|
describe ".call" do
|
||||||
|
@ -42,12 +43,6 @@ RSpec.describe Chat::UpdateThread do
|
||||||
it { is_expected.to fail_a_contract }
|
it { is_expected.to fail_a_contract }
|
||||||
end
|
end
|
||||||
|
|
||||||
context "when title is too long" do
|
|
||||||
let(:title) { "a" * Chat::Thread::MAX_TITLE_LENGTH + "a" }
|
|
||||||
|
|
||||||
it { is_expected.to fail_a_contract }
|
|
||||||
end
|
|
||||||
|
|
||||||
context "when thread is not found" do
|
context "when thread is not found" do
|
||||||
before { thread.destroy! }
|
before { thread.destroy! }
|
||||||
|
|
||||||
|
|
|
@ -2,8 +2,6 @@
|
||||||
|
|
||||||
RSpec.describe Chat::UpsertDraft do
|
RSpec.describe Chat::UpsertDraft do
|
||||||
describe described_class::Contract, type: :model do
|
describe described_class::Contract, type: :model do
|
||||||
subject(:contract) { described_class.new(data: nil, channel_id: nil, thread_id: nil) }
|
|
||||||
|
|
||||||
it { is_expected.to validate_presence_of :channel_id }
|
it { is_expected.to validate_presence_of :channel_id }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue