DEV: removes default service actions (#26078)

Previously services would let you define a high level default `def default_actions_for_service; end` which would define various handlers like `on_success`, after months of usage we consider the cons are superior to the pros here.

Two mains cons:
- people would often not understand where the handling was coming from
- it's easy to miss a case when you write your specs
This commit is contained in:
Joffrey JAFFEUX 2024-03-07 12:10:43 +01:00 committed by GitHub
parent 9f523af19c
commit 821402d024
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
29 changed files with 184 additions and 35 deletions

View File

@ -4,29 +4,51 @@ class Chat::Api::ChannelMessagesController < Chat::ApiController
def index def index
with_service(::Chat::ListChannelMessages) do with_service(::Chat::ListChannelMessages) do
on_success { render_serialized(result, ::Chat::MessagesSerializer, root: false) } on_success { render_serialized(result, ::Chat::MessagesSerializer, root: false) }
on_failure { render(json: failed_json, status: 422) }
on_failed_policy(:can_view_channel) { raise Discourse::InvalidAccess } on_failed_policy(:can_view_channel) { raise Discourse::InvalidAccess }
on_failed_policy(:target_message_exists) { raise Discourse::NotFound } on_failed_policy(:target_message_exists) { raise Discourse::NotFound }
on_model_not_found(:channel) { raise Discourse::NotFound } on_model_not_found(:channel) { raise Discourse::NotFound }
on_failed_contract do |contract|
render(json: failed_json.merge(errors: contract.errors.full_messages), status: 400)
end
end end
end end
def destroy def destroy
with_service(Chat::TrashMessage) { on_model_not_found(:message) { raise Discourse::NotFound } } with_service(Chat::TrashMessage) do
on_success { render(json: success_json) }
on_failure { render(json: failed_json, status: 422) }
on_model_not_found(:message) { raise Discourse::NotFound }
on_failed_policy(:invalid_access) { raise Discourse::InvalidAccess }
on_failed_contract do |contract|
render(json: failed_json.merge(errors: contract.errors.full_messages), status: 400)
end
end
end end
def restore def restore
with_service(Chat::RestoreMessage) do with_service(Chat::RestoreMessage) do
on_success { render(json: success_json) }
on_failure { render(json: failed_json, status: 422) }
on_failed_policy(:invalid_access) { raise Discourse::InvalidAccess }
on_model_not_found(:message) { raise Discourse::NotFound } on_model_not_found(:message) { raise Discourse::NotFound }
on_failed_contract do |contract|
render(json: failed_json.merge(errors: contract.errors.full_messages), status: 400)
end
end end
end end
def update def update
with_service(Chat::UpdateMessage) do with_service(Chat::UpdateMessage) do
on_success { render json: success_json.merge(message_id: result[:message].id) } on_success { render json: success_json.merge(message_id: result[:message].id) }
on_failure { render(json: failed_json, status: 422) }
on_model_not_found(:message) { raise Discourse::NotFound } on_model_not_found(:message) { raise Discourse::NotFound }
on_model_errors(:message) do |model| on_model_errors(:message) do |model|
render_json_error(model.errors.map(&:full_message).join(", ")) render_json_error(model.errors.map(&:full_message).join(", "))
end end
on_failed_contract do |contract|
render(json: failed_json.merge(errors: contract.errors.full_messages), status: 400)
end
end end
end end
@ -38,6 +60,7 @@ class Chat::Api::ChannelMessagesController < Chat::ApiController
with_service(Chat::CreateMessage) do with_service(Chat::CreateMessage) do
on_success { render json: success_json.merge(message_id: result[:message_instance].id) } on_success { render json: success_json.merge(message_id: result[:message_instance].id) }
on_failure { render(json: failed_json, status: 422) }
on_failed_policy(:no_silenced_user) { raise Discourse::InvalidAccess } on_failed_policy(:no_silenced_user) { raise Discourse::InvalidAccess }
on_model_not_found(:channel) { raise Discourse::NotFound } on_model_not_found(:channel) { raise Discourse::NotFound }
on_failed_policy(:allowed_to_join_channel) { raise Discourse::InvalidAccess } on_failed_policy(:allowed_to_join_channel) { raise Discourse::InvalidAccess }
@ -55,6 +78,9 @@ class Chat::Api::ChannelMessagesController < Chat::ApiController
on_model_errors(:message_instance) do |model| on_model_errors(:message_instance) do |model|
render_json_error(model.errors.map(&:full_message).join(", ")) render_json_error(model.errors.map(&:full_message).join(", "))
end end
on_failed_contract do |contract|
render(json: failed_json.merge(errors: contract.errors.full_messages), status: 400)
end
end end
end end
end end

View File

@ -12,11 +12,14 @@ class Chat::Api::ChannelThreadMessagesController < Chat::ApiController
include_thread_original_message: false, include_thread_original_message: false,
) )
end end
on_failure { render(json: failed_json, status: 422) }
on_failed_policy(:ensure_thread_enabled) { raise Discourse::NotFound } on_failed_policy(:ensure_thread_enabled) { raise Discourse::NotFound }
on_failed_policy(:target_message_exists) { raise Discourse::NotFound } on_failed_policy(:target_message_exists) { raise Discourse::NotFound }
on_failed_policy(:can_view_thread) { raise Discourse::InvalidAccess } on_failed_policy(:can_view_thread) { raise Discourse::InvalidAccess }
on_model_not_found(:thread) { raise Discourse::NotFound } on_model_not_found(:thread) { raise Discourse::NotFound }
on_failed_contract do |contract|
render(json: failed_json.merge(errors: contract.errors.full_messages), status: 400)
end
end end
end end
end end

View File

@ -22,6 +22,10 @@ class Chat::Api::ChannelThreadsController < Chat::ApiController
on_failed_policy(:can_view_channel) { raise Discourse::InvalidAccess } on_failed_policy(:can_view_channel) { raise Discourse::InvalidAccess }
on_model_not_found(:channel) { raise Discourse::NotFound } on_model_not_found(:channel) { raise Discourse::NotFound }
on_model_not_found(:threads) { render json: success_json.merge(threads: []) } on_model_not_found(:threads) { render json: success_json.merge(threads: []) }
on_failure { render(json: failed_json, status: 422) }
on_failed_contract do |contract|
render(json: failed_json.merge(errors: contract.errors.full_messages), status: 400)
end
end end
end end
@ -38,9 +42,13 @@ class Chat::Api::ChannelThreadsController < Chat::ApiController
participants: result.participants, participants: result.participants,
) )
end end
on_failed_policy(:invalid_access) { raise Discourse::InvalidAccess }
on_failed_policy(:threading_enabled_for_channel) { raise Discourse::NotFound } on_failed_policy(:threading_enabled_for_channel) { raise Discourse::NotFound }
on_model_not_found(:thread) { raise Discourse::NotFound } on_model_not_found(:thread) { raise Discourse::NotFound }
on_failure { render(json: failed_json, status: 422) }
on_failed_contract do |contract|
render(json: failed_json.merge(errors: contract.errors.full_messages), status: 400)
end
end end
end end
@ -53,6 +61,11 @@ class Chat::Api::ChannelThreadsController < Chat::ApiController
on_failed_step(:update) do on_failed_step(:update) do
render json: failed_json.merge(errors: [result["result.step.update"].error]), status: 422 render json: failed_json.merge(errors: [result["result.step.update"].error]), status: 422
end end
on_success { render(json: success_json) }
on_failure { render(json: failed_json, status: 422) }
on_failed_contract do |contract|
render(json: failed_json.merge(errors: contract.errors.full_messages), status: 400)
end
end end
end end
@ -73,6 +86,10 @@ class Chat::Api::ChannelThreadsController < Chat::ApiController
render json: failed_json.merge(errors: [result["result.step.create_thread"].error]), render json: failed_json.merge(errors: [result["result.step.create_thread"].error]),
status: 422 status: 422
end end
on_failure { render(json: failed_json, status: 422) }
on_failed_contract do |contract|
render(json: failed_json.merge(errors: contract.errors.full_messages), status: 400)
end
end end
end end
end end

View File

@ -13,6 +13,10 @@ class Chat::Api::ChannelThreadsCurrentUserNotificationsSettingsController < Chat
root: "membership", root: "membership",
) )
end end
on_failure { render(json: failed_json, status: 422) }
on_failed_contract do |contract|
render(json: failed_json.merge(errors: contract.errors.full_messages), status: 400)
end
end end
end end
end end

View File

@ -34,7 +34,13 @@ class Chat::Api::ChannelsController < Chat::ApiController
def destroy def destroy
with_service Chat::TrashChannel do with_service Chat::TrashChannel do
on_failed_policy(:invalid_access) { raise Discourse::InvalidAccess }
on_model_not_found(:channel) { raise ActiveRecord::RecordNotFound } on_model_not_found(:channel) { raise ActiveRecord::RecordNotFound }
on_success { render(json: success_json) }
on_failure { render(json: failed_json, status: 422) }
on_failed_contract do |contract|
render(json: failed_json.merge(errors: contract.errors.full_messages), status: 400)
end
end end
end end
@ -75,6 +81,10 @@ class Chat::Api::ChannelsController < Chat::ApiController
on_model_errors(:membership) do on_model_errors(:membership) do
render_json_error(result.membership, type: :record_invalid, status: 422) render_json_error(result.membership, type: :record_invalid, status: 422)
end end
on_failure { render(json: failed_json, status: 422) }
on_failed_contract do |contract|
render(json: failed_json.merge(errors: contract.errors.full_messages), status: 400)
end
end end
end end

View File

@ -12,6 +12,13 @@ class Chat::Api::ChannelsCurrentUserMembershipController < Chat::Api::ChannelsCo
end end
def destroy def destroy
with_service(Chat::LeaveChannel) { on_model_not_found(:channel) { raise Discourse::NotFound } } with_service(Chat::LeaveChannel) do
on_success { render(json: success_json) }
on_failure { render(json: failed_json, status: 422) }
on_model_not_found(:channel) { raise Discourse::NotFound }
on_failed_contract do |contract|
render(json: failed_json.merge(errors: contract.errors.full_messages), status: 400)
end
end
end end
end end

View File

@ -11,6 +11,10 @@ class Chat::Api::ChannelsCurrentUserMembershipFollowsController < Chat::Api::Cha
) )
end end
on_model_not_found(:channel) { raise Discourse::NotFound } on_model_not_found(:channel) { raise Discourse::NotFound }
on_failure { render(json: failed_json, status: 422) }
on_failed_contract do |contract|
render(json: failed_json.merge(errors: contract.errors.full_messages), status: 400)
end
end end
end end
end end

View File

@ -2,6 +2,13 @@
class Chat::Api::ChannelsDraftsController < Chat::ApiController class Chat::Api::ChannelsDraftsController < Chat::ApiController
def create def create
with_service(Chat::UpsertDraft) { on_model_not_found(:channel) { raise Discourse::NotFound } } with_service(Chat::UpsertDraft) do
on_success { render(json: success_json) }
on_failure { render(json: failed_json, status: 422) }
on_model_not_found(:channel) { raise Discourse::NotFound }
on_failed_contract do |contract|
render(json: failed_json.merge(errors: contract.errors.full_messages), status: 400)
end
end
end end
end end

View File

@ -3,8 +3,13 @@
class Chat::Api::ChannelsInvitesController < Chat::ApiController class Chat::Api::ChannelsInvitesController < Chat::ApiController
def create def create
with_service(Chat::InviteUsersToChannel) do with_service(Chat::InviteUsersToChannel) do
on_success { render(json: success_json) }
on_failure { render(json: failed_json, status: 422) }
on_failed_policy(:can_view_channel) { raise Discourse::InvalidAccess } on_failed_policy(:can_view_channel) { raise Discourse::InvalidAccess }
on_model_not_found(:channel) { raise Discourse::NotFound } on_model_not_found(:channel) { raise Discourse::NotFound }
on_failed_contract do |contract|
render(json: failed_json.merge(errors: contract.errors.full_messages), status: 400)
end
end end
end end
end end

View File

@ -31,9 +31,14 @@ class Chat::Api::ChannelsMembershipsController < Chat::Api::ChannelsController
def create def create
with_service(Chat::AddUsersToChannel) do with_service(Chat::AddUsersToChannel) do
on_success { render(json: success_json) }
on_failure { render(json: failed_json, status: 422) }
on_failed_policy(:can_add_users_to_channel) do on_failed_policy(:can_add_users_to_channel) do
render_json_error(I18n.t("chat.errors.users_cant_be_added_to_channel")) render_json_error(I18n.t("chat.errors.users_cant_be_added_to_channel"))
end end
on_failed_contract do |contract|
render(json: failed_json.merge(errors: contract.errors.full_messages), status: 400)
end
end end
end end
end end

View File

@ -5,8 +5,13 @@ class Chat::Api::ChannelsMessagesFlagsController < Chat::ApiController
RateLimiter.new(current_user, "flag_chat_message", 4, 1.minutes).performed! RateLimiter.new(current_user, "flag_chat_message", 4, 1.minutes).performed!
with_service(Chat::FlagMessage) do with_service(Chat::FlagMessage) do
on_success { render(json: success_json) }
on_failure { render(json: failed_json, status: 422) }
on_model_not_found(:message) { raise Discourse::NotFound } on_model_not_found(:message) { raise Discourse::NotFound }
on_failed_policy(:can_flag_message_in_channel) { raise Discourse::InvalidAccess } on_failed_policy(:can_flag_message_in_channel) { raise Discourse::InvalidAccess }
on_failed_contract do |contract|
render(json: failed_json.merge(errors: contract.errors.full_messages), status: 400)
end
end end
end end
end end

View File

@ -3,9 +3,14 @@
class Chat::Api::ChannelsMessagesStreamingController < Chat::Api::ChannelsController class Chat::Api::ChannelsMessagesStreamingController < Chat::Api::ChannelsController
def destroy def destroy
with_service(Chat::StopMessageStreaming) do with_service(Chat::StopMessageStreaming) do
on_success { render(json: success_json) }
on_failure { render(json: failed_json, status: 422) }
on_model_not_found(:message) { raise Discourse::NotFound } on_model_not_found(:message) { raise Discourse::NotFound }
on_failed_policy(:can_join_channel) { raise Discourse::InvalidAccess } on_failed_policy(:can_join_channel) { raise Discourse::InvalidAccess }
on_failed_policy(:can_stop_streaming) { raise Discourse::InvalidAccess } on_failed_policy(:can_stop_streaming) { raise Discourse::InvalidAccess }
on_failed_contract do |contract|
render(json: failed_json.merge(errors: contract.errors.full_messages), status: 400)
end
end end
end end
end end

View File

@ -6,6 +6,10 @@ class Chat::Api::ChannelsStatusController < Chat::Api::ChannelsController
on_success { render_serialized(result.channel, Chat::ChannelSerializer, root: "channel") } on_success { render_serialized(result.channel, Chat::ChannelSerializer, root: "channel") }
on_model_not_found(:channel) { raise ActiveRecord::RecordNotFound } on_model_not_found(:channel) { raise ActiveRecord::RecordNotFound }
on_failed_policy(:check_channel_permission) { raise Discourse::InvalidAccess } on_failed_policy(:check_channel_permission) { raise Discourse::InvalidAccess }
on_failure { render(json: failed_json, status: 422) }
on_failed_contract do |contract|
render(json: failed_json.merge(errors: contract.errors.full_messages), status: 400)
end
end end
end end
end end

View File

@ -3,8 +3,13 @@
class Chat::Api::ChannelsThreadsDraftsController < Chat::ApiController class Chat::Api::ChannelsThreadsDraftsController < Chat::ApiController
def create def create
with_service(Chat::UpsertDraft) do with_service(Chat::UpsertDraft) do
on_success { render(json: success_json) }
on_failure { render(json: failed_json, status: 422) }
on_model_not_found(:channel) { raise Discourse::NotFound } on_model_not_found(:channel) { raise Discourse::NotFound }
on_failed_step(:check_thread_exists) { raise Discourse::NotFound } on_failed_step(:check_thread_exists) { raise Discourse::NotFound }
on_failed_contract do |contract|
render(json: failed_json.merge(errors: contract.errors.full_messages), status: 400)
end
end end
end end
end end

View File

@ -6,6 +6,10 @@ class Chat::Api::ChatablesController < Chat::ApiController
def index def index
with_service(::Chat::SearchChatable) do with_service(::Chat::SearchChatable) do
on_success { render_serialized(result, ::Chat::ChatablesSerializer, root: false) } on_success { render_serialized(result, ::Chat::ChatablesSerializer, root: false) }
on_failure { render(json: failed_json, status: 422) }
on_failed_contract do |contract|
render(json: failed_json.merge(errors: contract.errors.full_messages), status: 400)
end
end end
end end
end end

View File

@ -19,6 +19,10 @@ class Chat::Api::CurrentUserThreadsController < Chat::ApiController
) )
end end
on_model_not_found(:threads) { render json: success_json.merge(threads: []) } on_model_not_found(:threads) { render json: success_json.merge(threads: []) }
on_failure { render(json: failed_json, status: 422) }
on_failed_contract do |contract|
render(json: failed_json.merge(errors: contract.errors.full_messages), status: 400)
end
end end
end end
end end

View File

@ -30,6 +30,10 @@ class Chat::Api::DirectMessagesController < Chat::ApiController
on_model_errors(:channel) do |model| on_model_errors(:channel) do |model|
render_json_error(model, type: :record_invalid, status: 422) render_json_error(model, type: :record_invalid, status: 422)
end end
on_failure { render(json: failed_json, status: 422) }
on_failed_contract do |contract|
render(json: failed_json.merge(errors: contract.errors.full_messages), status: 400)
end
end end
end end
end end

View File

@ -5,12 +5,18 @@ class Chat::Api::ReadsController < Chat::ApiController
params.require(%i[channel_id message_id]) params.require(%i[channel_id message_id])
with_service(Chat::UpdateUserLastRead) do with_service(Chat::UpdateUserLastRead) do
on_success { render(json: success_json) }
on_failure { render(json: failed_json, status: 422) }
on_failed_policy(:ensure_message_id_recency) do on_failed_policy(:ensure_message_id_recency) do
raise Discourse::InvalidParameters.new(:message_id) raise Discourse::InvalidParameters.new(:message_id)
end end
on_model_not_found(:message) { raise Discourse::NotFound } on_model_not_found(:message) { raise Discourse::NotFound }
on_model_not_found(:active_membership) { raise Discourse::NotFound } on_model_not_found(:active_membership) { raise Discourse::NotFound }
on_model_not_found(:channel) { raise Discourse::NotFound } on_model_not_found(:channel) { raise Discourse::NotFound }
on_failed_policy(:invalid_access) { raise Discourse::InvalidAccess }
on_failed_contract do |contract|
render(json: failed_json.merge(errors: contract.errors.full_messages), status: 400)
end
end end
end end
@ -19,6 +25,7 @@ class Chat::Api::ReadsController < Chat::ApiController
on_success do on_success do
render(json: success_json.merge(updated_memberships: result.updated_memberships)) render(json: success_json.merge(updated_memberships: result.updated_memberships))
end end
on_failure { render(json: failed_json, status: 422) }
end end
end end
end end

View File

@ -5,7 +5,13 @@ class Chat::Api::ThreadReadsController < Chat::ApiController
params.require(%i[channel_id thread_id]) params.require(%i[channel_id thread_id])
with_service(Chat::UpdateUserThreadLastRead) do with_service(Chat::UpdateUserThreadLastRead) do
on_success { render(json: success_json) }
on_failure { render(json: failed_json, status: 422) }
on_model_not_found(:thread) { raise Discourse::NotFound } on_model_not_found(:thread) { raise Discourse::NotFound }
on_failed_policy(:invalid_access) { raise Discourse::InvalidAccess }
on_failed_contract do |contract|
render(json: failed_json.merge(errors: contract.errors.full_messages), status: 400)
end
end end
end end
end end

View File

@ -3,18 +3,5 @@
module Chat module Chat
class ApiController < ::Chat::BaseController class ApiController < ::Chat::BaseController
include Chat::WithServiceHelper include Chat::WithServiceHelper
private
def default_actions_for_service
proc do
on_success { render(json: success_json) }
on_failure { render(json: failed_json, status: 422) }
on_failed_policy(:invalid_access) { raise Discourse::InvalidAccess }
on_failed_contract do |contract|
render(json: failed_json.merge(errors: contract.errors.full_messages), status: 400)
end
end
end
end end
end end

View File

@ -63,6 +63,7 @@ module Chat
incoming_chat_webhook: webhook, incoming_chat_webhook: webhook,
) do ) do
on_success { render json: success_json } on_success { render json: success_json }
on_failure { render(json: failed_json, status: 422) }
on_failed_contract do |contract| on_failed_contract do |contract|
raise Discourse::InvalidParameters.new(contract.errors.full_messages) raise Discourse::InvalidParameters.new(contract.errors.full_messages)
end end

View File

@ -8,14 +8,14 @@ module Chat
# @param service [Class] A class including {Chat::Service::Base} # @param service [Class] A class including {Chat::Service::Base}
# @param dependencies [kwargs] Any additional params to load into the service context, # @param dependencies [kwargs] Any additional params to load into the service context,
# in addition to controller @params. # in addition to controller @params.
def with_service(service, default_actions: true, **dependencies, &block) def with_service(service, **dependencies, &block)
object = self object = self
merged_block = ServiceRunner.call(
proc do service,
instance_exec(&object.method(:default_actions_for_service).call) if default_actions object,
instance_exec(&(block || proc {})) **dependencies,
end &proc { instance_exec(&(block || proc {})) }
ServiceRunner.call(service, object, **dependencies, &merged_block) )
end end
def run_service(service, dependencies) def run_service(service, dependencies)
@ -24,9 +24,5 @@ module Chat
@_result = @_result =
service.call(params.to_unsafe_h.merge(guardian: self.try(:guardian) || nil, **dependencies)) service.call(params.to_unsafe_h.merge(guardian: self.try(:guardian) || nil, **dependencies))
end end
def default_actions_for_service
proc {}
end
end end
end end

View File

@ -5,6 +5,7 @@ module Jobs
class AutoJoinChannelBatch < ServiceJob class AutoJoinChannelBatch < ServiceJob
def execute(args) def execute(args)
with_service(::Chat::AutoJoinChannelBatch, **args) do with_service(::Chat::AutoJoinChannelBatch, **args) do
on_failure { Rails.logger.error("Failed with unexpected error") }
on_failed_contract do |contract| on_failed_contract do |contract|
Rails.logger.error(contract.errors.full_messages.join(", ")) Rails.logger.error(contract.errors.full_messages.join(", "))
end end

View File

@ -42,6 +42,13 @@ module Chat
validates :channel_id, presence: true validates :channel_id, presence: true
attribute :limit, :integer attribute :limit, :integer
validates :limit,
numericality: {
less_than_or_equal_to: THREADS_LIMIT,
only_integer: true,
},
allow_nil: true
attribute :offset, :integer attribute :offset, :integer
end end

View File

@ -27,8 +27,6 @@
# argument to their block. `on_model_errors` receives the actual model so its # argument to their block. `on_model_errors` receives the actual model so its
# easier to inspect it. # easier to inspect it.
# #
# Default actions for each of these are defined in [Chat::ApiController#default_actions_for_service]
#
# @example In a controller # @example In a controller
# def create # def create
# with_service MyService do # with_service MyService do

View File

@ -28,6 +28,14 @@ RSpec.describe Chat::Api::ChannelMessagesController do
end end
end end
context "when params are invalid" do
it "returns a 400" do
get "/chat/api/channels/#{channel.id}/messages?page_size=9999"
expect(response.status).to eq(400)
end
end
context "when readonly mode" do context "when readonly mode" do
fab!(:message_1) { Fabricate(:chat_message, chat_channel: channel) } fab!(:message_1) { Fabricate(:chat_message, chat_channel: channel) }

View File

@ -73,5 +73,13 @@ RSpec.describe Chat::Api::ChannelThreadMessagesController do
expect(response.status).to eq(404) expect(response.status).to eq(404)
end end
end end
context "when params are invalid" do
it "returns a 400" do
get "/chat/api/channels/#{thread.channel.id}/threads/#{thread.id}/messages?page_size=9999"
expect(response.status).to eq(400)
end
end
end end
end end

View File

@ -168,6 +168,13 @@ RSpec.describe Chat::Api::ChannelThreadsController do
expect(response.status).to eq(404) expect(response.status).to eq(404)
end end
end end
context "when params are invalid" do
it "returns a 400" do
get "/chat/api/channels/#{public_channel.id}/threads?limit=9999"
expect(response.status).to eq(400)
end
end
end end
describe "update" do describe "update" do

View File

@ -1,7 +1,13 @@
# frozen_string_literal: true # frozen_string_literal: true
RSpec.describe ::Chat::LookupChannelThreads::Contract, type: :model do RSpec.describe ::Chat::LookupChannelThreads::Contract, type: :model do
it { is_expected.to validate_presence_of :channel_id } subject(:contract) { described_class.new(channel_id: 1) }
it { is_expected.to validate_presence_of(:channel_id) }
it { is_expected.to allow_values(1, 0, nil, "a").for(:limit) }
it do
is_expected.not_to allow_values(::Chat::LookupChannelThreads::THREADS_LIMIT + 1).for(:limit)
end
end end
RSpec.describe ::Chat::LookupChannelThreads do RSpec.describe ::Chat::LookupChannelThreads do
@ -30,9 +36,7 @@ RSpec.describe ::Chat::LookupChannelThreads do
context "when limit is over max" do context "when limit is over max" do
let(:limit) { described_class::THREADS_LIMIT + 1 } let(:limit) { described_class::THREADS_LIMIT + 1 }
it "defaults to a max value" do it { is_expected.to fail_a_contract }
expect(result.limit).to eq(described_class::THREADS_LIMIT)
end
end end
context "when limit is under min" do context "when limit is under min" do