chore: apply Rails/SaveBang cop (#15)

* chore: apply Rails/SaveBang cop

* fix: correct locale validation in category model spec

* fix: update save methods to avoid Rails/SaveBang cop violations
This commit is contained in:
Gabriel Jablonski 2025-04-03 23:10:23 -03:00 committed by gabrieljablonski
parent e0708ca6f8
commit 659c3e7c2f
114 changed files with 301 additions and 298 deletions

View File

@ -170,3 +170,10 @@ AllCops:
Layout/LeadingCommentSpace:
Enabled: false
Rails/SaveBang:
Enabled: true
AllowedReceivers:
- Stripe::Subscription
- Stripe::Customer
- FactoryBot

View File

@ -77,7 +77,7 @@ class Messages::Messenger::MessageBuilder
rescue Koala::Facebook::ClientError => e
# The exception occurs when we are trying fetch the deleted story or blocked story.
@message.attachments.destroy_all
@message.update(content: I18n.t('conversations.messages.instagram_deleted_story_content'))
@message.update!(content: I18n.t('conversations.messages.instagram_deleted_story_content'))
Rails.logger.error e
{}
rescue StandardError => e

View File

@ -39,7 +39,7 @@ class Api::V1::Accounts::CallbacksController < Api::V1::Accounts::BaseController
return if response['instagram_business_account'].blank?
instagram_id = response['instagram_business_account']['id']
facebook_channel.update(instagram_id: instagram_id)
facebook_channel.update!(instagram_id: instagram_id)
rescue StandardError => e
Rails.logger.error "Error in set_instagram_id: #{e.message}"
end

View File

@ -16,7 +16,7 @@ class Api::V1::Accounts::Integrations::SlackController < Api::V1::Accounts::Base
end
def update
@hook = channel_builder.update(permitted_params[:reference_id])
@hook = channel_builder.update_reference_id(permitted_params[:reference_id])
render json: { error: I18n.t('errors.slack.invalid_channel_id') }, status: :unprocessable_entity if @hook.blank?
end

View File

@ -25,17 +25,17 @@ class Api::V1::Accounts::NotificationsController < Api::V1::Accounts::BaseContro
end
def update
@notification.update(read_at: DateTime.now.utc)
@notification.update!(read_at: DateTime.now.utc)
render json: @notification
end
def unread
@notification.update(read_at: nil)
@notification.update!(read_at: nil)
render json: @notification
end
def destroy
@notification.destroy
@notification.destroy!
head :ok
end
@ -55,7 +55,7 @@ class Api::V1::Accounts::NotificationsController < Api::V1::Accounts::BaseContro
def snooze
updated_meta = (@notification.meta || {}).merge('last_snoozed_at' => nil)
@notification.update(snoozed_until: parse_date_time(params[:snoozed_until].to_s), meta: updated_meta) if params[:snoozed_until]
@notification.update!(snoozed_until: parse_date_time(params[:snoozed_until].to_s), meta: updated_meta) if params[:snoozed_until]
render json: @notification
end

View File

@ -38,7 +38,7 @@ class Api::V1::Accounts::PortalsController < Api::V1::Accounts::BaseController
end
def archive
@portal.update(archive: true)
@portal.update!(archive: true)
head :ok
end

View File

@ -29,7 +29,7 @@ class Api::V1::ProfilesController < Api::BaseController
end
def set_active_account
@user.account_users.find_by(account_id: profile_params[:account_id]).update(active_at: Time.now.utc)
@user.account_users.find_by(account_id: profile_params[:account_id]).update!(active_at: Time.now.utc)
head :ok
end

View File

@ -19,7 +19,7 @@ class Api::V1::Widget::ContactsController < Api::V1::Widget::BaseController
contact = @contact
end
@contact_inbox.update(hmac_verified: true) if should_verify_hmac? && valid_hmac?
@contact_inbox.update!(hmac_verified: true) if should_verify_hmac? && valid_hmac?
identify_contact(contact)
end

View File

@ -5,7 +5,7 @@ class Platform::Api::V1::AccountsController < PlatformController
@resource = Account.create!(account_params)
update_resource_features
@resource.save!
@platform_app.platform_app_permissibles.find_or_create_by(permissible: @resource)
@platform_app.platform_app_permissibles.find_or_create_by!(permissible: @resource)
end
def update

View File

@ -12,7 +12,7 @@ class Platform::Api::V1::AgentBotsController < PlatformController
@resource = AgentBot.new(agent_bot_params.except(:avatar_url))
@resource.save!
process_avatar_from_url
@platform_app.platform_app_permissibles.find_or_create_by(permissible: @resource)
@platform_app.platform_app_permissibles.find_or_create_by!(permissible: @resource)
end
def update

View File

@ -31,7 +31,7 @@ class Public::Api::V1::Inboxes::ContactsController < Public::Api::V1::InboxesCon
return if params[:identifier_hash].blank? && !@inbox_channel.hmac_mandatory
raise StandardError, 'HMAC failed: Invalid Identifier Hash Provided' unless valid_hmac?
@contact_inbox.update(hmac_verified: true) if @contact_inbox.present?
@contact_inbox.update!(hmac_verified: true) if @contact_inbox.present?
end
def valid_hmac?

View File

@ -18,7 +18,7 @@ class SuperAdmin::AppConfigsController < SuperAdmin::ApplicationController
params['app_config'].each do |key, value|
next unless @allowed_configs.include?(key)
i = InstallationConfig.where(name: key).first_or_create(value: value, locked: false)
i = InstallationConfig.where(name: key).first_or_create!(value: value, locked: false)
i.value = value
i.save!
end

View File

@ -26,7 +26,7 @@ class BulkActionsJob < ApplicationJob
records.each do |conversation|
bulk_add_labels(conversation)
bulk_snoozed_until(conversation)
conversation.update(params) if params
conversation.update!(params) if params
end
end
@ -54,7 +54,7 @@ class BulkActionsJob < ApplicationJob
return unless @params[:labels] && @params[:labels][:remove]
labels = conversation.label_list - @params[:labels][:remove]
conversation.update(label_list: labels)
conversation.update!(label_list: labels)
end
def records_to_updated(ids)

View File

@ -17,7 +17,7 @@ class Conversations::UserMentionJob < ApplicationJob
account_id: account_id
)
else
mention.update(mentioned_at: Time.zone.now)
mention.update!(mentioned_at: Time.zone.now)
end
end
end

View File

@ -35,7 +35,7 @@ class ConversationReplyMailer < ApplicationMailer
init_conversation_attributes(message.conversation)
@message = message
reply_mail_object = prepare_mail(true)
message.update(source_id: reply_mail_object.message_id)
message.update!(source_id: reply_mail_object.message_id)
end
def conversation_transcript(conversation, to_email)

View File

@ -107,7 +107,7 @@ class Article < ApplicationRecord
root_article_id = self.class.find_root_article_id(article)
update(associated_article_id: root_article_id) if root_article_id.present?
update!(associated_article_id: root_article_id) if root_article_id.present?
end
# Make sure we always associate the parent's associated id to avoid the deeper associations od articles.

View File

@ -49,7 +49,7 @@ class Channel::TwilioSms < ApplicationRecord
params = send_message_from.merge(to: to, body: body)
params[:media_url] = media_url if media_url.present?
params[:status_callback] = twilio_delivery_status_index_url
client.messages.create(**params)
client.messages.create!(**params)
end
private

View File

@ -3,12 +3,12 @@ module ConversationMuteHelpers
def mute!
resolved!
contact.update(blocked: true)
contact.update!(blocked: true)
create_muted_message
end
def unmute!
contact.update(blocked: false)
contact.update!(blocked: false)
create_unmuted_message
end

View File

@ -27,7 +27,7 @@ module Featurable
def enable_features!(*)
enable_features(*)
save
save!
end
def disable_features(*names)
@ -38,7 +38,7 @@ module Featurable
def disable_features!(*)
disable_features(*)
save
save!
end
def feature_enabled?(name)

View File

@ -159,12 +159,12 @@ class Conversation < ApplicationRecord
# FIXME: implement state machine with aasm
self.status = open? ? :resolved : :open
self.status = :open if pending? || snoozed?
save
save!
end
def toggle_priority(priority = nil)
self.priority = priority.presence
save
save!
end
def bot_handoff!

View File

@ -268,7 +268,7 @@ class Message < ApplicationRecord
end
def update_contact_activity
sender.update(last_activity_at: DateTime.now) if sender.is_a?(Contact)
sender.update!(last_activity_at: DateTime.now) if sender.is_a?(Contact)
end
def update_waiting_since
@ -276,9 +276,9 @@ class Message < ApplicationRecord
Rails.configuration.dispatcher.dispatch(
REPLY_CREATED, Time.zone.now, waiting_since: conversation.waiting_since, message: self
)
conversation.update(waiting_since: nil)
conversation.update!(waiting_since: nil)
end
conversation.update(waiting_since: created_at) if incoming? && conversation.waiting_since.blank?
conversation.update!(waiting_since: created_at) if incoming? && conversation.waiting_since.blank?
end
def human_response?
@ -296,7 +296,7 @@ class Message < ApplicationRecord
if valid_first_reply?
Rails.configuration.dispatcher.dispatch(FIRST_REPLY_CREATED, Time.zone.now, message: self, performed_by: Current.executed_by)
conversation.update(first_reply_created_at: created_at, waiting_since: nil)
conversation.update!(first_reply_created_at: created_at, waiting_since: nil)
else
update_waiting_since
end

View File

@ -66,6 +66,6 @@ class Portal < ApplicationRecord
def config_json_format
config['default_locale'] = default_locale
denied_keys = config.keys - CONFIG_JSON_KEYS
errors.add(:cofig, "in portal on #{denied_keys.join(',')} is not supported.") if denied_keys.any?
errors.add(:config, "in portal on #{denied_keys.join(',')} is not supported.") if denied_keys.any?
end
end

View File

@ -36,7 +36,7 @@ class Team < ApplicationRecord
# @return [Array<User>] Array of newly added members
def add_members(user_ids)
team_members_to_create = user_ids.map { |user_id| { user_id: user_id } }
created_members = team_members.create(team_members_to_create)
created_members = team_members.create!(team_members_to_create)
added_users = created_members.filter_map(&:user)
update_account_cache

View File

@ -46,7 +46,7 @@ class ActionService
return if labels.empty?
labels = @conversation.label_list - labels
@conversation.update(label_list: labels)
@conversation.update!(label_list: labels)
end
def assign_team(team_ids = [])

View File

@ -10,7 +10,7 @@ class AutoAssignment::AgentAssignmentService
def perform
new_assignee = find_assignee
conversation.update(assignee: new_assignee) if new_assignee
conversation.update!(assignee: new_assignee) if new_assignee
end
private

View File

@ -53,7 +53,7 @@ class DataImport::ContactManager
contact.email = params[:email] if params[:email].present?
contact.phone_number = format_phone_number(params[:phone_number]) if params[:phone_number].present?
update_contact_attributes(params, contact)
contact.save
contact.save # rubocop:disable Rails/SaveBang
end
private

View File

@ -27,6 +27,6 @@ class Instagram::WebhooksBaseService
# TODO: Remove this once we show the social_instagram_user_name in the UI instead of the username
@contact.additional_attributes = @contact.additional_attributes.merge({ 'social_profiles': { 'instagram': user['username'] } })
@contact.additional_attributes = @contact.additional_attributes.merge({ 'social_instagram_user_name': user['username'] })
@contact.save
@contact.save!
end
end

View File

@ -29,7 +29,7 @@ class Twilio::WebhookSetupService
else
twilio_client
.incoming_phone_numbers(phonenumber_sid)
.update(sms_method: 'POST', sms_url: twilio_callback_index_url)
.update(sms_method: 'POST', sms_url: twilio_callback_index_url) # rubocop:disable Rails/SaveBang
end
end

View File

@ -28,7 +28,7 @@ class Whatsapp::Providers::Whatsapp360DialogService < Whatsapp::Providers::BaseS
# ensuring that channels with wrong provider config wouldn't keep trying to sync templates
whatsapp_channel.mark_message_templates_updated
response = HTTParty.get("#{api_base_path}/configs/templates", headers: api_headers)
whatsapp_channel.update(message_templates: response['waba_templates'], message_templates_last_updated: Time.now.utc) if response.success?
whatsapp_channel.update!(message_templates: response['waba_templates'], message_templates_last_updated: Time.now.utc) if response.success?
end
def validate_provider_config?

View File

@ -30,7 +30,7 @@ class Whatsapp::Providers::WhatsappCloudService < Whatsapp::Providers::BaseServi
# ensuring that channels with wrong provider config wouldn't keep trying to sync templates
whatsapp_channel.mark_message_templates_updated
templates = fetch_whatsapp_templates("#{business_account_path}/message_templates?access_token=#{whatsapp_channel.provider_config['api_key']}")
whatsapp_channel.update(message_templates: templates, message_templates_last_updated: Time.now.utc) if templates.present?
whatsapp_channel.update!(message_templates: templates, message_templates_last_updated: Time.now.utc) if templates.present?
end
def fetch_whatsapp_templates(url)

View File

@ -5,7 +5,7 @@ class ArticleKeyConverter
def process
new_content = replace(@article.content)
@article.update(content: new_content)
@article.update!(content: new_content)
end
private

View File

@ -40,7 +40,7 @@ class Api::V1::Accounts::Captain::AssistantResponsesController < Api::V1::Accoun
end
def destroy
@response.destroy
@response.destroy!
head :no_content
end

View File

@ -19,7 +19,7 @@ class Api::V1::Accounts::Captain::AssistantsController < Api::V1::Accounts::Base
end
def destroy
@assistant.destroy
@assistant.destroy!
head :no_content
end

View File

@ -37,7 +37,7 @@ class Api::V1::Accounts::Captain::BulkActionsController < Api::V1::Accounts::Bas
case params[:fields][:status]
when 'approve'
responses.pending.update(status: 'approved')
responses.pending.update!(status: 'approved')
responses
when 'delete'
responses.destroy_all

View File

@ -28,7 +28,7 @@ class Api::V1::Accounts::Captain::DocumentsController < Api::V1::Accounts::BaseC
end
def destroy
@document.destroy
@document.destroy!
head :no_content
end

View File

@ -6,7 +6,7 @@ class Enterprise::Api::V1::AccountsController < Api::BaseController
def subscription
if stripe_customer_id.blank? && @account.custom_attributes['is_creating_customer'].blank?
@account.update(custom_attributes: { is_creating_customer: true })
@account.update!(custom_attributes: { is_creating_customer: true })
Enterprise::CreateStripeCustomerJob.perform_later(@account)
end
head :no_content

View File

@ -6,7 +6,7 @@ module Enterprise::DeleteObjectJob
def create_audit_entry(object, user, ip)
return unless ['Inbox'].include?(object.class.to_s) && user.present?
Enterprise::AuditLog.create(
Enterprise::AuditLog.create!(
auditable: object,
audited_changes: object.attributes,
action: 'destroy',

View File

@ -18,18 +18,18 @@ module Enterprise::Account::PlanUsageAndLimits
def increment_response_usage
current_usage = custom_attributes[CAPTAIN_RESPONSES_USAGE].to_i || 0
custom_attributes[CAPTAIN_RESPONSES_USAGE] = current_usage + 1
save
save!
end
def reset_response_usage
custom_attributes[CAPTAIN_RESPONSES_USAGE] = 0
save
save!
end
def update_document_usage
# this will ensure that the document count is always accurate
custom_attributes[CAPTAIN_DOCUMENTS_USAGE] = captain_documents.count
save
save!
end
def subscribed_features

View File

@ -19,7 +19,7 @@ module Enterprise::Audit::InboxMember
def create_audit_log_entry(action)
return if inbox.blank?
Enterprise::AuditLog.create(
Enterprise::AuditLog.create!(
auditable_id: id,
auditable_type: 'InboxMember',
action: action,

View File

@ -19,7 +19,7 @@ module Enterprise::Audit::TeamMember
def create_audit_log_entry(action)
return if team.blank?
Enterprise::AuditLog.create(
Enterprise::AuditLog.create!(
auditable_id: id,
auditable_type: 'TeamMember',
action: action,

View File

@ -24,7 +24,7 @@ module Enterprise::Channelable
# skip audit log creation if the only change is whatsapp channel template update
return if messaging_template_updates?(audited_changes)
Enterprise::AuditLog.create(
Enterprise::AuditLog.create!(
auditable_id: auditable_id,
auditable_type: auditable_type,
action: 'update',

View File

@ -19,7 +19,7 @@ class Internal::AccountAnalysis::AccountUpdaterService
def save_error(error_message)
@account.internal_attributes['security_flagged'] = true
@account.internal_attributes['security_flag_reason'] = "Error: #{error_message}"
@account.save
@account.save!
end
def save_analysis_results(analysis)

View File

@ -33,7 +33,7 @@ class ActionView::Template::Handlers::Liquid
def drops
droppables = @controller.send(:liquid_droppables) if @controller.respond_to?(:liquid_droppables, true)
droppables.update(droppables) { |_, obj| obj.try(:to_drop) || nil }
droppables.update(droppables) { |_, obj| obj.try(:to_drop) || nil } # rubocop:disable Rails/SaveBang
end
def filters

View File

@ -86,6 +86,6 @@ class ConfigLoader
# update the existing feature flag values with default values and add new feature flags with default values
(account_features + config.value).uniq { |h| h['name'] }
end
config.update({ name: 'ACCOUNT_LEVEL_FEATURE_DEFAULTS', value: features, locked: true })
config.update!({ name: 'ACCOUNT_LEVEL_FEATURE_DEFAULTS', value: features, locked: true })
end
end

View File

@ -9,7 +9,7 @@ class GlobalConfigService
return if config_value.blank?
i = InstallationConfig.where(name: config_key).first_or_create(value: config_value, locked: false)
i = InstallationConfig.where(name: config_key).first_or_create!(value: config_value, locked: false)
# To clear a nil value that might have been cached in the previous call
GlobalConfig.clear_cache
i.value

View File

@ -9,8 +9,13 @@ class Integrations::Slack::ChannelBuilder
channels
end
def update(reference_id)
update_reference_id(reference_id)
def update_reference_id(reference_id)
channel = find_channel(reference_id)
return if channel.blank?
slack_client.conversations_join(channel: channel[:id]) if channel[:is_private] == false
@hook.update!(reference_id: channel[:id], settings: { channel_name: channel[:name] }, status: 'enabled')
@hook
end
private
@ -36,13 +41,4 @@ class Integrations::Slack::ChannelBuilder
def find_channel(reference_id)
channels.find { |channel| channel['id'] == reference_id }
end
def update_reference_id(reference_id)
channel = find_channel(reference_id)
return if channel.blank?
slack_client.conversations_join(channel: channel[:id]) if channel[:is_private] == false
@hook.update!(reference_id: channel[:id], settings: { channel_name: channel[:name] }, status: 'enabled')
@hook
end
end

View File

@ -17,7 +17,7 @@ class VapidService
public_key = ENV.fetch('VAPID_PUBLIC_KEY') { keys.public_key }
private_key = ENV.fetch('VAPID_PRIVATE_KEY') { keys.private_key }
i = InstallationConfig.where(name: 'VAPID_KEYS').first_or_create(value: { public_key: public_key, private_key: private_key })
i = InstallationConfig.where(name: 'VAPID_KEYS').first_or_create!(value: { public_key: public_key, private_key: private_key })
i.value
end

View File

@ -73,7 +73,7 @@ describe NotificationBuilder do
end
it 'will not create a notification if conversation contact is blocked and notification type is not conversation_mention' do
primary_actor.contact.update(blocked: true)
primary_actor.contact.update!(blocked: true)
expect do
described_class.new(
@ -86,7 +86,7 @@ describe NotificationBuilder do
end
it 'will create a notification if conversation contact is blocked and notification type is conversation_mention' do
primary_actor.contact.update(blocked: true)
primary_actor.contact.update!(blocked: true)
expect do
described_class.new(

View File

@ -173,7 +173,7 @@ describe V2::ReportBuilder do
conversations = account.conversations.where('created_at < ?', 1.day.ago)
conversations.each do |conversation|
conversation.pending!
conversation.messages.outgoing.all.update(sender: nil)
conversation.messages.outgoing.all.update!(sender: nil)
end
perform_enqueued_jobs do
@ -346,7 +346,7 @@ describe V2::ReportBuilder do
end
it 'returns average first response time' do
label_2.reporting_events.update(value: 1.5)
label_2.reporting_events.update!(value: 1.5)
params = {
metric: 'avg_first_response_time',

View File

@ -29,7 +29,7 @@ RSpec.describe 'Agents API', type: :request do
end
it 'returns custom fields on agents if present' do
agent.update(custom_attributes: { test: 'test' })
agent.update!(custom_attributes: { test: 'test' })
get "/api/v1/accounts/#{account.id}/agents",
headers: agent.create_new_auth_token,

View File

@ -119,8 +119,8 @@ RSpec.describe 'Api::V1::Accounts::BulkActionsController', type: :request do
end
it 'Bulk remove assignee id from conversations' do
Conversation.first.update(assignee_id: agent_1.id)
Conversation.second.update(assignee_id: agent_2.id)
Conversation.first.update!(assignee_id: agent_1.id)
Conversation.second.update!(assignee_id: agent_2.id)
params = { type: 'Conversation', fields: { assignee_id: nil }, ids: Conversation.first(3).pluck(:display_id) }
expect(Conversation.first.status).to eq('open')
@ -141,8 +141,8 @@ RSpec.describe 'Api::V1::Accounts::BulkActionsController', type: :request do
end
it 'Do not bulk update status to nil' do
Conversation.first.update(assignee_id: agent_1.id)
Conversation.second.update(assignee_id: agent_2.id)
Conversation.first.update!(assignee_id: agent_1.id)
Conversation.second.update!(assignee_id: agent_2.id)
params = { type: 'Conversation', fields: { status: nil }, ids: Conversation.first(3).pluck(:display_id) }
expect(Conversation.first.status).to eq('open')

View File

@ -358,7 +358,7 @@ RSpec.describe 'Contacts API', type: :request do
end
it 'searches contacts using company name' do
contact2.update(additional_attributes: { company_name: 'acme.inc' })
contact2.update!(additional_attributes: { company_name: 'acme.inc' })
get "/api/v1/accounts/#{account.id}/contacts/search",
params: { q: 'acme.inc' },
headers: admin.create_new_auth_token,
@ -658,7 +658,7 @@ RSpec.describe 'Contacts API', type: :request do
end
it 'allows unblocking of contact' do
contact.update(blocked: true)
contact.update!(blocked: true)
patch "/api/v1/accounts/#{account.id}/contacts/#{contact.id}",
params: { blocked: false },
headers: admin.create_new_auth_token,

View File

@ -18,7 +18,7 @@ RSpec.describe 'Custom Filters API', type: :request do
custom_attribute_type: ''
}
] }
custom_filter.save
custom_filter.save!
end
describe 'GET /api/v1/accounts/{account.id}/custom_filters' do

View File

@ -318,7 +318,7 @@ RSpec.describe 'Api::V1::Accounts::MacrosController', type: :request do
end
it 'Assign the agent when he is not inbox member' do
InboxMember.last.destroy
InboxMember.last.destroy!
expect(conversation.assignee).to be_nil

View File

@ -52,7 +52,7 @@ RSpec.describe 'Api::V1::Accounts::Portals', type: :request do
end
it 'returns portal articles metadata' do
portal.update(config: { allowed_locales: %w[en es], default_locale: 'en' })
portal.update!(config: { allowed_locales: %w[en es], default_locale: 'en' })
en_cat = create(:category, locale: :en, portal_id: portal.id, slug: 'en-cat')
es_cat = create(:category, locale: :es, portal_id: portal.id, slug: 'es-cat')
create(:article, category_id: en_cat.id, portal_id: portal.id, author_id: agent.id)

View File

@ -119,7 +119,7 @@ RSpec.describe 'Accounts API', type: :request do
context 'when it is an authenticated user' do
it 'shows an account' do
account.update(auto_resolve_duration: 30)
account.update!(auto_resolve_duration: 30)
get "/api/v1/accounts/#{account.id}",
headers: admin.create_new_auth_token,
@ -141,7 +141,7 @@ RSpec.describe 'Accounts API', type: :request do
let(:admin) { create(:user, account: account, role: :administrator) }
it 'returns cache_keys as expected' do
account.update(auto_resolve_duration: 30)
account.update!(auto_resolve_duration: 30)
get "/api/v1/accounts/#{account.id}/cache_keys",
headers: admin.create_new_auth_token,
@ -214,7 +214,7 @@ RSpec.describe 'Accounts API', type: :request do
end
it 'updates onboarding step to invite_team if onboarding step is present in account custom attributes' do
account.update(custom_attributes: { onboarding_step: 'account_update' })
account.update!(custom_attributes: { onboarding_step: 'account_update' })
put "/api/v1/accounts/#{account.id}",
params: params,
headers: admin.create_new_auth_token,

View File

@ -205,7 +205,7 @@ RSpec.describe '/api/v1/widget/conversations/toggle_typing', type: :request do
describe 'POST /api/v1/widget/conversations/transcript' do
context 'with a conversation' do
it 'sends transcript email' do
contact.update(email: 'test@test.com')
contact.update!(email: 'test@test.com')
mailer = double
allow(ConversationReplyMailer).to receive(:with).and_return(mailer)
allow(mailer).to receive(:conversation_transcript)
@ -217,7 +217,7 @@ RSpec.describe '/api/v1/widget/conversations/toggle_typing', type: :request do
expect(response).to have_http_status(:success)
expect(mailer).to have_received(:conversation_transcript).with(conversation, 'test@test.com')
contact.update(email: nil)
contact.update!(email: nil)
end
end
end

View File

@ -20,7 +20,7 @@ RSpec.describe Public::Api::V1::PortalsController, type: :request do
end
it 'Throws unauthorised error for unknown domain' do
portal.update(custom_domain: 'www.something.com')
portal.update!(custom_domain: 'www.something.com')
get "/hc/#{portal.slug}/en"

View File

@ -11,7 +11,7 @@ RSpec.describe 'Agents API', type: :request do
params = { name: 'NewUser', email: Faker::Internet.email, role: :agent }
before do
account.update(limits: { agents: 4 })
account.update!(limits: { agents: 4 })
create_list(:user, 4, account: account, role: :agent)
end
@ -31,7 +31,7 @@ RSpec.describe 'Agents API', type: :request do
context 'when exceeding agent limit' do
it 'prevents creating agents and returns a payment required status' do
# Set the limit to be less than the number of emails
account.update(limits: { agents: 2 })
account.update!(limits: { agents: 2 })
expect do
post "/api/v1/accounts/#{account.id}/agents/bulk_create", params: bulk_create_params, headers: admin.create_new_auth_token
@ -44,7 +44,7 @@ RSpec.describe 'Agents API', type: :request do
context 'when onboarding step is present in account custom attributes' do
it 'removes onboarding step from account custom attributes' do
account.update(custom_attributes: { onboarding_step: 'completed' })
account.update!(custom_attributes: { onboarding_step: 'completed' })
post "/api/v1/accounts/#{account.id}/agents/bulk_create", params: bulk_create_params, headers: admin.create_new_auth_token

View File

@ -116,8 +116,8 @@ RSpec.describe 'Applied SLAs API', type: :request do
it 'returns a CSV file with breached conversations' do
create(:applied_sla, sla_policy: sla_policy1, conversation: conversation1, sla_status: 'missed')
create(:applied_sla, sla_policy: sla_policy1, conversation: conversation2, sla_status: 'missed')
conversation1.update(status: 'open')
conversation2.update(status: 'resolved')
conversation1.update!(status: 'open')
conversation2.update!(status: 'resolved')
get "/api/v1/accounts/#{account.id}/applied_slas/download",
headers: administrator.create_new_auth_token

View File

@ -27,7 +27,7 @@ RSpec.describe 'Enterprise Conversations API', type: :request do
end
it 'throws error if conversation already has a different sla' do
conversation.update(sla_policy: create(:sla_policy, account: account))
conversation.update!(sla_policy: create(:sla_policy, account: account))
patch "/api/v1/accounts/#{account.id}/conversations/#{conversation.display_id}",
params: params,
headers: agent.create_new_auth_token,

View File

@ -122,8 +122,8 @@ RSpec.describe 'Enterprise Billing APIs', type: :request do
context 'when it is an authenticated user' do
before do
InstallationConfig.where(name: 'DEPLOYMENT_ENV').first_or_create(value: 'cloud')
InstallationConfig.where(name: 'CHATWOOT_CLOUD_PLANS').first_or_create(value: [{ 'name': 'Hacker' }])
InstallationConfig.where(name: 'DEPLOYMENT_ENV').first_or_create!(value: 'cloud')
InstallationConfig.where(name: 'CHATWOOT_CLOUD_PLANS').first_or_create!(value: [{ 'name': 'Hacker' }])
end
context 'when it is an agent' do
@ -158,8 +158,8 @@ RSpec.describe 'Enterprise Billing APIs', type: :request do
before do
create(:conversation, account: account)
create(:channel_api, account: account)
InstallationConfig.where(name: 'DEPLOYMENT_ENV').first_or_create(value: 'cloud')
InstallationConfig.where(name: 'CHATWOOT_CLOUD_PLANS').first_or_create(value: [{ 'name': 'Hacker' }])
InstallationConfig.where(name: 'DEPLOYMENT_ENV').first_or_create!(value: 'cloud')
InstallationConfig.where(name: 'CHATWOOT_CLOUD_PLANS').first_or_create!(value: [{ 'name': 'Hacker' }])
end
it 'returns the limits if the plan is default' do

View File

@ -132,7 +132,7 @@ RSpec.describe Account, type: :model do
describe 'when limits are configured for an account' do
before do
create(:installation_config, name: 'CAPTAIN_CLOUD_PLAN_LIMITS', value: captain_limits.to_json)
account.update(limits: { captain_documents: 5555, captain_responses: 9999 })
account.update!(limits: { captain_documents: 5555, captain_responses: 9999 })
end
it 'returns limits based on custom attributes' do
@ -149,7 +149,7 @@ RSpec.describe Account, type: :model do
end
it 'creates audit logs when account is updated' do
account.update(name: 'New Name')
account.update!(name: 'New Name')
expect(Audited::Audit.where(auditable_type: 'Account', action: 'update').count).to eq 1
end
end
@ -159,23 +159,23 @@ RSpec.describe Account, type: :model do
end
it 'returns max limits from account when enterprise version' do
account.update(limits: { agents: 10 })
account.update!(limits: { agents: 10 })
expect(account.usage_limits[:agents]).to eq(10)
end
it 'returns limits based on subscription' do
account.update(limits: { agents: 10 }, custom_attributes: { subscribed_quantity: 5 })
account.update!(limits: { agents: 10 }, custom_attributes: { subscribed_quantity: 5 })
expect(account.usage_limits[:agents]).to eq(5)
end
it 'returns max limits from global config if account limit is absent' do
account.update(limits: { agents: '' })
account.update!(limits: { agents: '' })
expect(account.usage_limits[:agents]).to eq(20)
end
it 'returns max limits from app limit if account limit and installation config is absent' do
account.update(limits: { agents: '' })
InstallationConfig.where(name: 'ACCOUNT_AGENTS_LIMIT').update(value: '')
account.update!(limits: { agents: '' })
InstallationConfig.where(name: 'ACCOUNT_AGENTS_LIMIT').update!(value: '')
expect(account.usage_limits[:agents]).to eq(ChatwootApp.max_limit)
end

View File

@ -14,7 +14,7 @@ RSpec.describe AutomationRule do
context 'when automation rule is updated' do
it 'has associated audit log created' do
automation_rule.update(name: 'automation rule 2')
automation_rule.update!(name: 'automation rule 2')
expect(Audited::Audit.where(auditable_type: 'AutomationRule', action: 'update').count).to eq 1
end
end

View File

@ -21,7 +21,7 @@ RSpec.describe InboxMember, type: :model do
context 'when inbox member is destroyed' do
it 'has associated audit log created' do
inbox_member.destroy
inbox_member.destroy!
audit_log = Audited::Audit.find_by(auditable: inbox_member, action: 'destroy')
expect(audit_log).to be_present
expect(audit_log.audited_changes['inbox_id']).to eq(inbox.id)

View File

@ -28,7 +28,7 @@ RSpec.describe Inbox do
it 'returns member ids with assignment capacity with inbox max_assignment_limit is configured' do
# agent 1 has 1 conversations, agent 2 has 2 conversations, agent 3 has 3 conversations and agent 4 with none
inbox.update(auto_assignment_config: { max_assignment_limit: 2 })
inbox.update!(auto_assignment_config: { max_assignment_limit: 2 })
expect(inbox.member_ids_with_assignment_capacity).to contain_exactly(inbox_member_1.user_id, inbox_member_4.user_id)
end
@ -46,7 +46,7 @@ RSpec.describe Inbox do
context 'when inbox is updated' do
it 'has associated audit log created' do
inbox.update(name: 'Updated Inbox')
inbox.update!(name: 'Updated Inbox')
expect(Audited::Audit.where(auditable_type: 'Inbox', action: 'update').count).to eq(1)
end
end
@ -55,7 +55,7 @@ RSpec.describe Inbox do
it 'has associated audit log created' do
previous_color = inbox.channel.widget_color
new_color = '#ff0000'
inbox.channel.update(widget_color: new_color)
inbox.channel.update!(widget_color: new_color)
# check if channel update creates an audit log against inbox
expect(Audited::Audit.where(auditable_type: 'Inbox', action: 'update').count).to eq(1)
@ -78,7 +78,7 @@ RSpec.describe Inbox do
context 'when inbox is updated' do
it 'has associated audit log created' do
inbox.update(name: 'Updated Inbox')
inbox.update!(name: 'Updated Inbox')
expect(Audited::Audit.where(auditable_type: 'Inbox', action: 'update').count).to eq(1)
end
end
@ -87,7 +87,7 @@ RSpec.describe Inbox do
it 'has associated audit log created' do
previous_webhook = inbox.channel.webhook_url
new_webhook = 'https://example2.com'
inbox.channel.update(webhook_url: new_webhook)
inbox.channel.update!(webhook_url: new_webhook)
# check if channel update creates an audit log against inbox
expect(Audited::Audit.where(auditable_type: 'Inbox', action: 'update').count).to eq(1)
@ -122,7 +122,7 @@ RSpec.describe Inbox do
context 'when inbox is updated' do
it 'has associated audit log created' do
inbox.update(name: 'Updated Inbox')
inbox.update!(name: 'Updated Inbox')
expect(Audited::Audit.where(auditable_type: 'Inbox', action: 'update').count).to eq(1)
end
end
@ -131,7 +131,7 @@ RSpec.describe Inbox do
it 'has associated audit log created' do
previous_phone_number = inbox.channel.phone_number
new_phone_number = '1234567890'
inbox.channel.update(phone_number: new_phone_number)
inbox.channel.update!(phone_number: new_phone_number)
# check if channel update creates an audit log against inbox
expect(Audited::Audit.where(auditable_type: 'Inbox', action: 'update').count).to eq(1)

View File

@ -15,7 +15,7 @@ RSpec.describe Macro do
context 'when macro is updated' do
it 'has associated audit log created' do
macro.update(name: 'awesome macro')
macro.update!(name: 'awesome macro')
expect(Audited::Audit.where(auditable_type: 'Macro', action: 'update').count).to eq 1
end
end

View File

@ -21,7 +21,7 @@ RSpec.describe TeamMember, type: :model do
context 'when team member is destroyed' do
it 'has associated audit log created' do
team_member.destroy
team_member.destroy!
audit_log = Audited::Audit.find_by(auditable: team_member, action: 'destroy')
expect(audit_log).to be_present
expect(audit_log.audited_changes['team_id']).to eq(team.id)

View File

@ -15,7 +15,7 @@ RSpec.describe Team do
context 'when team is updated' do
it 'has associated audit log created' do
team.update(description: 'awesome team')
team.update!(description: 'awesome team')
expect(Audited::Audit.where(auditable_type: 'Team', action: 'update').count).to eq 1
end
end

View File

@ -35,7 +35,7 @@ RSpec.describe User do
it 'will not add error when trying to update a existing user' do
allow(ChatwootHub).to receive(:pricing_plan_quantity).and_return(1)
existing_user.update(name: 'new name')
existing_user.update!(name: 'new name')
# since there is user and existing user, we are already over limits
existing_user.valid?
expect(existing_user.errors[:base]).to be_empty

View File

@ -15,7 +15,7 @@ RSpec.describe Webhook do
context 'when webhook is updated' do
it 'has associated audit log created' do
webhook.update(url: 'https://example.com')
webhook.update!(url: 'https://example.com')
expect(Audited::Audit.where(auditable_type: 'Webhook', action: 'update').count).to eq 1
end
end

View File

@ -60,7 +60,7 @@ RSpec.describe Enterprise::Conversations::PermissionFilterService do
# Create custom role with conversation_manage permission
test_custom_role = create(:custom_role, account: test_account, permissions: ['conversation_manage'])
account_user = AccountUser.find_by(user: test_agent, account: test_account)
account_user.update(role: :agent, custom_role: test_custom_role)
account_user.update!(role: :agent, custom_role: test_custom_role)
# Create some conversations
assigned_conversation = create(:conversation, account: test_account, inbox: test_inbox, assignee: test_agent)
@ -96,7 +96,7 @@ RSpec.describe Enterprise::Conversations::PermissionFilterService do
test_custom_role = create(:custom_role, account: test_account, permissions: %w[conversation_participating_manage])
account_user = AccountUser.find_by(user: test_agent, account: test_account)
account_user.update(role: :agent, custom_role: test_custom_role)
account_user.update!(role: :agent, custom_role: test_custom_role)
# Create some conversations
other_conversation = create(:conversation, account: test_account, inbox: test_inbox)
@ -131,7 +131,7 @@ RSpec.describe Enterprise::Conversations::PermissionFilterService do
test_custom_role = create(:custom_role, account: test_account, permissions: %w[conversation_unassigned_manage])
account_user = AccountUser.find_by(user: test_agent, account: test_account)
account_user.update(role: :agent, custom_role: test_custom_role)
account_user.update!(role: :agent, custom_role: test_custom_role)
# Create some conversations
assigned_conversation = create(:conversation, account: test_account, inbox: test_inbox, assignee: test_agent)
@ -170,7 +170,7 @@ RSpec.describe Enterprise::Conversations::PermissionFilterService do
test_custom_role = create(:custom_role, account: test_account, permissions: permissions)
account_user = AccountUser.find_by(user: test_agent, account: test_account)
account_user.update(role: :agent, custom_role: test_custom_role)
account_user.update!(role: :agent, custom_role: test_custom_role)
# Create some conversations
assigned_to_agent = create(:conversation, account: test_account, inbox: test_inbox, assignee: test_agent)

View File

@ -42,8 +42,8 @@ RSpec.describe Sla::EvaluateAppliedSlaService do
context 'when next response SLA is missed' do
before do
applied_sla.sla_policy.update(next_response_time_threshold: 1.hour)
conversation.update(first_reply_created_at: 5.hours.ago, waiting_since: 5.hours.ago)
applied_sla.sla_policy.update!(next_response_time_threshold: 1.hour)
conversation.update!(first_reply_created_at: 5.hours.ago, waiting_since: 5.hours.ago)
end
it 'updates the SLA status to missed and logs a warning' do
@ -89,7 +89,7 @@ RSpec.describe Sla::EvaluateAppliedSlaService do
context 'when resolved conversation with resolution time SLA is missed' do
before do
conversation.resolved!
applied_sla.sla_policy.update(resolution_time_threshold: 1.hour)
applied_sla.sla_policy.update!(resolution_time_threshold: 1.hour)
end
it 'does not update the SLA status to missed' do
@ -100,8 +100,8 @@ RSpec.describe Sla::EvaluateAppliedSlaService do
context 'when multiple SLAs are missed' do
before do
applied_sla.sla_policy.update(first_response_time_threshold: 1.hour, next_response_time_threshold: 1.hour, resolution_time_threshold: 1.hour)
conversation.update(first_reply_created_at: 5.hours.ago, waiting_since: 5.hours.ago)
applied_sla.sla_policy.update!(first_response_time_threshold: 1.hour, next_response_time_threshold: 1.hour, resolution_time_threshold: 1.hour)
conversation.update!(first_reply_created_at: 5.hours.ago, waiting_since: 5.hours.ago)
end
it 'updates the SLA status to missed and logs multiple warnings' do
@ -119,8 +119,8 @@ RSpec.describe Sla::EvaluateAppliedSlaService do
describe '#perform - SLA hits' do
context 'when first response SLA is hit' do
before do
applied_sla.sla_policy.update(first_response_time_threshold: 6.hours)
conversation.update(first_reply_created_at: 30.minutes.ago)
applied_sla.sla_policy.update!(first_response_time_threshold: 6.hours)
conversation.update!(first_reply_created_at: 30.minutes.ago)
end
it 'sla remains active until conversation is resolved' do
@ -142,8 +142,8 @@ RSpec.describe Sla::EvaluateAppliedSlaService do
context 'when next response SLA is hit' do
before do
applied_sla.sla_policy.update(next_response_time_threshold: 6.hours)
conversation.update(first_reply_created_at: 30.minutes.ago, waiting_since: nil)
applied_sla.sla_policy.update!(next_response_time_threshold: 6.hours)
conversation.update!(first_reply_created_at: 30.minutes.ago, waiting_since: nil)
end
it 'sla remains active until conversation is resolved' do
@ -164,7 +164,7 @@ RSpec.describe Sla::EvaluateAppliedSlaService do
context 'when resolution time SLA is hit' do
before do
applied_sla.sla_policy.update(resolution_time_threshold: 8.hours)
applied_sla.sla_policy.update!(resolution_time_threshold: 8.hours)
conversation.resolved!
end
@ -182,7 +182,7 @@ RSpec.describe Sla::EvaluateAppliedSlaService do
describe 'SLA evaluation with frt hit, multiple nrt misses and rt miss' do
before do
# Setup SLA Policy thresholds
applied_sla.sla_policy.update(
applied_sla.sla_policy.update!(
first_response_time_threshold: 2.hours, # Hit frt
next_response_time_threshold: 1.hour, # Miss nrt multiple times
resolution_time_threshold: 4.hours # Miss rt
@ -204,7 +204,7 @@ RSpec.describe Sla::EvaluateAppliedSlaService do
described_class.new(applied_sla: applied_sla).perform
# Conversation is resolved missing rt
conversation.update(status: 'resolved')
conversation.update!(status: 'resolved')
# this will not create a new notification for rt miss as conversation is resolved
# but we would have already created an rt miss notification during previous evaluation

View File

@ -9,7 +9,7 @@ describe EmailChannelFinder do
let(:reply_cc_mail) { create_inbound_email_from_fixture('reply_cc.eml') }
it 'return channel with cc email' do
channel_email.update(email: 'test@example.com')
channel_email.update!(email: 'test@example.com')
channel = described_class.new(reply_cc_mail.mail).perform
expect(channel).to eq(channel_email)
end
@ -19,21 +19,21 @@ describe EmailChannelFinder do
let(:reply_mail) { create_inbound_email_from_fixture('reply.eml') }
it 'return channel with to email' do
channel_email.update(email: 'test@example.com')
channel_email.update!(email: 'test@example.com')
reply_mail.mail['to'] = 'test@example.com'
channel = described_class.new(reply_mail.mail).perform
expect(channel).to eq(channel_email)
end
it 'return channel with to+extension email' do
channel_email.update(email: 'test@example.com')
channel_email.update!(email: 'test@example.com')
reply_mail.mail['to'] = 'test+123@example.com'
channel = described_class.new(reply_mail.mail).perform
expect(channel).to eq(channel_email)
end
it 'return channel with cc email' do
channel_email.update(email: 'test@example.com')
channel_email.update!(email: 'test@example.com')
reply_mail.mail['to'] = nil
reply_mail.mail['cc'] = 'test@example.com'
channel = described_class.new(reply_mail.mail).perform
@ -41,7 +41,7 @@ describe EmailChannelFinder do
end
it 'return channel with bcc email' do
channel_email.update(email: 'test@example.com')
channel_email.update!(email: 'test@example.com')
reply_mail.mail['to'] = nil
reply_mail.mail['bcc'] = 'test@example.com'
channel = described_class.new(reply_mail.mail).perform
@ -49,7 +49,7 @@ describe EmailChannelFinder do
end
it 'return channel with X-Original-To email' do
channel_email.update(email: 'test@example.com')
channel_email.update!(email: 'test@example.com')
reply_mail.mail['to'] = nil
reply_mail.mail['X-Original-To'] = 'test@example.com'
channel = described_class.new(reply_mail.mail).perform

View File

@ -11,7 +11,7 @@ RSpec.describe Account::ConversationsResolutionSchedulerJob do
end
it 'enqueues Conversations::ResolutionJob' do
account.update(auto_resolve_duration: 10)
account.update!(auto_resolve_duration: 10)
expect(Conversations::ResolutionJob).to receive(:perform_later).with(account: account).once
described_class.perform_now
end

View File

@ -18,15 +18,15 @@ RSpec.describe Conversations::ResolutionJob do
end
it 'resolves the issue if time of inactivity is more than the auto resolve duration' do
account.update(auto_resolve_duration: 10)
conversation.update(last_activity_at: 13.days.ago)
account.update!(auto_resolve_duration: 10)
conversation.update!(last_activity_at: 13.days.ago)
described_class.perform_now(account: account)
expect(conversation.reload.status).to eq('resolved')
end
it 'resolved only a limited number of conversations in a single execution' do
stub_const('Limits::BULK_ACTIONS_LIMIT', 2)
account.update(auto_resolve_duration: 10)
account.update!(auto_resolve_duration: 10)
create_list(:conversation, 3, account: account, last_activity_at: 13.days.ago)
described_class.perform_now(account: account)
expect(account.conversations.resolved.count).to eq(Limits::BULK_ACTIONS_LIMIT)

View File

@ -102,7 +102,7 @@ RSpec.describe Webhooks::WhatsappEventsJob do
context 'when default provider' do
it 'enqueue Whatsapp::IncomingMessageService' do
stub_request(:post, 'https://waba.360dialog.io/v1/configs/webhook')
channel.update(provider: 'default')
channel.update!(provider: 'default')
allow(Whatsapp::IncomingMessageService).to receive(:new).and_return(process_service)
expect(Whatsapp::IncomingMessageService).to receive(:new)
job.perform_now(params)

View File

@ -157,7 +157,7 @@ describe Integrations::Dialogflow::ProcessorService do
let(:processor) { described_class.new(event_name: event_name, hook: hook, event_data: event_data) }
before do
hook.update(settings: { 'project_id' => 'test', 'credentials' => 'creds' })
hook.update(settings: { 'project_id' => 'test', 'credentials' => 'creds' }) # rubocop:disable Rails/SaveBang
allow(google_dialogflow).to receive(:new).and_return(session_client)
allow(session_client).to receive(:detect_intent).and_return({ session: session, query_input: query_input })
end

View File

@ -46,7 +46,7 @@ describe Integrations::Facebook::DeliveryStatus do
end
it 'does not update the message status if the message was created after the watermark' do
message1.update(created_at: 1.day.from_now)
message1.update!(created_at: 1.day.from_now)
message_deliveries.delivery['watermark'] = 1.day.ago.to_i
described_class.new(params: message_deliveries).perform
expect(message1.reload.status).to eq('sent')
@ -73,7 +73,7 @@ describe Integrations::Facebook::DeliveryStatus do
end
it 'does not update the message status if the message was created after the watermark' do
message1.update(created_at: 1.day.from_now)
message1.update!(created_at: 1.day.from_now)
message_reads.read['watermark'] = 1.day.ago.to_i
described_class.new(params: message_reads).perform
expect(message1.reload.status).to eq('sent')

View File

@ -18,7 +18,7 @@ describe OnlineStatusTracker do
end
it 'returns agents who have auto offline configured false' do
user2.account_users.first.update(auto_offline: false)
user2.account_users.first.update!(auto_offline: false)
expect(described_class.get_available_users(account.id).keys).to contain_exactly(user1.id.to_s, user2.id.to_s)
end

View File

@ -38,7 +38,7 @@ describe ActionCableListener do
it 'sends message to all hmac verified contact inboxes' do
# HACK: to reload conversation inbox members
expect(conversation.inbox.reload.inbox_members.count).to eq(1)
conversation.contact_inbox.update(hmac_verified: true)
conversation.contact_inbox.update!(hmac_verified: true)
# creating a non verified contact inbox to ensure the events are not sent to it
create(:contact_inbox, contact: conversation.contact, inbox: inbox)
verified_contact_inbox = create(:contact_inbox, contact: conversation.contact, inbox: inbox, hmac_verified: true)

View File

@ -331,7 +331,7 @@ describe AutomationRuleListener do
}.with_indifferent_access
]
)
conversation.update(status: :snoozed)
conversation.update!(status: :snoozed)
end
let!(:event) do
@ -351,7 +351,7 @@ describe AutomationRuleListener do
end
it 'triggers automation rule to assign team with OR operator' do
conversation.update(status: :open)
conversation.update!(status: :open)
automation_rule.update!(
conditions: [
{
@ -380,7 +380,7 @@ describe AutomationRuleListener do
context 'when rule doesnt match' do
it 'when automation rule is triggered it will not assign team' do
conversation.update(status: :open)
conversation.update!(status: :open)
expect(conversation.team_id).not_to eq(team.id)
@ -391,7 +391,7 @@ describe AutomationRuleListener do
end
it 'when automation rule is triggers, it will not assign team on attribute_changed values' do
conversation.update(status: :snoozed)
conversation.update!(status: :snoozed)
event = Events::Base.new('conversation_updated', Time.zone.now, { conversation: conversation,
changed_attributes: { company: %w[Marvel DC] } })

View File

@ -126,7 +126,7 @@ describe NotificationListener do
it 'will not create duplicate new message notification for the same user for mentions participation & assignment' do
create(:inbox_member, user: first_agent, inbox: inbox)
conversation.update(assignee: first_agent)
conversation.update!(assignee: first_agent)
message = build(
:message,
@ -144,9 +144,9 @@ describe NotificationListener do
it 'will not create duplicate new message notifications for assignment & participation' do
create(:inbox_member, user: first_agent, inbox: inbox)
conversation.update(assignee: first_agent)
conversation.update!(assignee: first_agent)
# participants is created by async job. so creating it directly for testcase
conversation.conversation_participants.first_or_create(user: first_agent)
conversation.conversation_participants.first_or_create!(user: first_agent)
message = build(
:message,

View File

@ -50,7 +50,7 @@ describe ReportingEventListener do
end
it 'does not create a conversation_bot_resolved event if resolved conversation inbox does not have active bot' do
bot_resolved_conversation.update(inbox: inbox)
bot_resolved_conversation.update!(inbox: inbox)
event = Events::Base.new('conversation.resolved', Time.zone.now, conversation: bot_resolved_conversation)
listener.conversation_resolved(event)
expect(account.reporting_events.where(name: 'conversation_bot_resolved').count).to be 0

View File

@ -166,7 +166,7 @@ describe WebhookListener do
it 'triggers webhook' do
webhook = create(:webhook, inbox: inbox, account: account)
conversation.update(custom_attributes: { test: 'testing custom attri webhook' })
conversation.update!(custom_attributes: { test: 'testing custom attri webhook' })
expect(WebhookJob).to receive(:perform_later).with(
webhook.url,

View File

@ -43,7 +43,7 @@ RSpec.describe ApplicationMailbox do
it 'routes support emails to Support Mailbox when mail is to channel email' do
# this email is hardcoded in the support.eml, that's why we are updating this
channel_email.update(email: 'care@example.com')
channel_email.update!(email: 'care@example.com')
dbl = double
expect(SupportMailbox).to receive(:new).and_return(dbl)
expect(dbl).to receive(:perform_processing).and_return(true)
@ -52,7 +52,7 @@ RSpec.describe ApplicationMailbox do
it 'routes support emails to Support Mailbox when mail is to channel forward to email' do
# this email is hardcoded in the support.eml, that's why we are updating this
channel_email.update(forward_to_email: 'care@example.com')
channel_email.update!(forward_to_email: 'care@example.com')
dbl = double
expect(SupportMailbox).to receive(:new).and_return(dbl)
expect(dbl).to receive(:perform_processing).and_return(true)
@ -60,7 +60,7 @@ RSpec.describe ApplicationMailbox do
end
it 'routes support emails to Support Mailbox with cc email' do
channel_email.update(email: 'test@example.com')
channel_email.update!(email: 'test@example.com')
dbl = double
expect(SupportMailbox).to receive(:new).and_return(dbl)
expect(dbl).to receive(:perform_processing).and_return(true)

View File

@ -71,7 +71,7 @@ RSpec.describe ConversationReplyMailer do
it 'will not send email if conversation is already viewed by contact' do
create(:message, message_type: 'outgoing', account: account, conversation: conversation)
conversation.update(contact_last_seen_at: Time.zone.now)
conversation.update!(contact_last_seen_at: Time.zone.now)
expect(mail).to be_nil
end
@ -132,7 +132,7 @@ RSpec.describe ConversationReplyMailer do
it 'will not send email if conversation is already viewed by contact' do
create(:message, message_type: 'outgoing', account: account, conversation: conversation)
conversation.update(contact_last_seen_at: Time.zone.now)
conversation.update!(contact_last_seen_at: Time.zone.now)
expect(mail).to be_nil
end
end
@ -176,20 +176,20 @@ RSpec.describe ConversationReplyMailer do
end
it 'renders sender name even when assignee is not present' do
conversation.update(assignee_id: nil)
conversation.update!(assignee_id: nil)
mail = described_class.email_reply(message)
expect(mail['from'].value).to eq "#{message.sender.available_name} from #{smtp_email_channel.inbox.name} <#{smtp_email_channel.email}>"
end
it 'renders assignee name in the from address when sender_name not available' do
message.update(sender_id: nil)
message.update!(sender_id: nil)
mail = described_class.email_reply(message)
expect(mail['from'].value).to eq "#{conversation.assignee.available_name} from #{smtp_email_channel.inbox.name} <#{smtp_email_channel.email}>"
end
it 'renders inbox name as sender and assignee or business_name not present' do
message.update(sender_id: nil)
conversation.update(assignee_id: nil)
message.update!(sender_id: nil)
conversation.update!(assignee_id: nil)
mail = described_class.email_reply(message)
expect(mail['from'].value).to eq "Notifications from #{smtp_email_channel.inbox.name} <#{smtp_email_channel.email}>"
@ -197,14 +197,14 @@ RSpec.describe ConversationReplyMailer do
context 'when friendly name enabled' do
before do
conversation.inbox.update(sender_name_type: 0)
conversation.inbox.update(business_name: 'Business Name')
conversation.inbox.update!(sender_name_type: 0)
conversation.inbox.update!(business_name: 'Business Name')
end
it 'renders sender name as sender and assignee and business_name not present' do
message.update(sender_id: nil)
conversation.update(assignee_id: nil)
conversation.inbox.update(business_name: nil)
message.update!(sender_id: nil)
conversation.update!(assignee_id: nil)
conversation.inbox.update!(business_name: nil)
mail = described_class.email_reply(message)
@ -212,8 +212,8 @@ RSpec.describe ConversationReplyMailer do
end
it 'renders sender name as sender and assignee nil and business_name present' do
message.update(sender_id: nil)
conversation.update(assignee_id: nil)
message.update!(sender_id: nil)
conversation.update!(assignee_id: nil)
mail = described_class.email_reply(message)
@ -223,8 +223,8 @@ RSpec.describe ConversationReplyMailer do
end
it 'renders sender name as sender nil and assignee and business_name present' do
message.update(sender_id: nil)
conversation.update(assignee_id: agent.id)
message.update!(sender_id: nil)
conversation.update!(assignee_id: agent.id)
mail = described_class.email_reply(message)
expect(mail['from'].value).to eq "#{agent.available_name} from #{conversation.inbox.business_name} <#{smtp_email_channel.email}>"
@ -232,8 +232,8 @@ RSpec.describe ConversationReplyMailer do
it 'renders sender name as sender and assignee and business_name present' do
agent_2 = create(:user, email: 'agent2@example.com', account: account)
message.update(sender_id: agent_2.id)
conversation.update(assignee_id: agent.id)
message.update!(sender_id: agent_2.id)
conversation.update!(assignee_id: agent.id)
mail = described_class.email_reply(message)
expect(mail['from'].value).to eq "#{agent_2.available_name} from #{conversation.inbox.business_name} <#{smtp_email_channel.email}>"
@ -242,14 +242,14 @@ RSpec.describe ConversationReplyMailer do
context 'when friendly name disabled' do
before do
conversation.inbox.update(sender_name_type: 1)
conversation.inbox.update(business_name: 'Business Name')
conversation.inbox.update!(sender_name_type: 1)
conversation.inbox.update!(business_name: 'Business Name')
end
it 'renders sender name as business_name not present' do
message.update(sender_id: nil)
conversation.update(assignee_id: nil)
conversation.inbox.update(business_name: nil)
message.update!(sender_id: nil)
conversation.update!(assignee_id: nil)
conversation.inbox.update!(business_name: nil)
mail = described_class.email_reply(message)
@ -257,8 +257,8 @@ RSpec.describe ConversationReplyMailer do
end
it 'renders sender name as business_name present' do
message.update(sender_id: nil)
conversation.update(assignee_id: nil)
message.update!(sender_id: nil)
conversation.update!(assignee_id: nil)
mail = described_class.email_reply(message)

View File

@ -52,21 +52,21 @@ RSpec.describe Account do
let(:account) { create(:account) }
it 'returns the domain from inbox if inbox value is present' do
account.update(domain: 'test.com')
account.update!(domain: 'test.com')
with_modified_env MAILER_INBOUND_EMAIL_DOMAIN: 'test2.com' do
expect(account.inbound_email_domain).to eq('test.com')
end
end
it 'returns the domain from ENV if inbox value is nil' do
account.update(domain: nil)
account.update!(domain: nil)
with_modified_env MAILER_INBOUND_EMAIL_DOMAIN: 'test.com' do
expect(account.inbound_email_domain).to eq('test.com')
end
end
it 'returns the domain from ENV if inbox value is empty string' do
account.update(domain: '')
account.update!(domain: '')
with_modified_env MAILER_INBOUND_EMAIL_DOMAIN: 'test.com' do
expect(account.inbound_email_domain).to eq('test.com')
end
@ -77,21 +77,21 @@ RSpec.describe Account do
let(:account) { create(:account) }
it 'returns the support email from inbox if inbox value is present' do
account.update(support_email: 'support@chatwoot.com')
account.update!(support_email: 'support@chatwoot.com')
with_modified_env MAILER_SENDER_EMAIL: 'hello@chatwoot.com' do
expect(account.support_email).to eq('support@chatwoot.com')
end
end
it 'returns the support email from ENV if inbox value is nil' do
account.update(support_email: nil)
account.update!(support_email: nil)
with_modified_env MAILER_SENDER_EMAIL: 'hello@chatwoot.com' do
expect(account.support_email).to eq('hello@chatwoot.com')
end
end
it 'returns the support email from ENV if inbox value is empty string' do
account.update(support_email: '')
account.update!(support_email: '')
with_modified_env MAILER_SENDER_EMAIL: 'hello@chatwoot.com' do
expect(account.support_email).to eq('hello@chatwoot.com')
end
@ -104,7 +104,7 @@ RSpec.describe Account do
query = "select * from information_schema.sequences where sequence_name in ('camp_dpid_seq_#{account.id}', 'conv_dpid_seq_#{account.id}');"
expect(ActiveRecord::Base.connection.execute(query).count).to eq(2)
expect(account.locale).to eq('en')
account.destroy
account.destroy!
expect(ActiveRecord::Base.connection.execute(query).count).to eq(0)
end
end

View File

@ -23,7 +23,7 @@ RSpec.describe Category do
it 'returns erros when locale is not allowed in the portal' do
category = create(:category, slug: 'category_1', locale: 'en', portal_id: portal.id)
expect(category).to be_valid
category.update(locale: 'es')
expect(category.update(locale: 'es')).to be(false)
expect(category.errors.full_messages[0]).to eq("Locale es of category is not part of portal's [\"en\"].")
end
end

View File

@ -65,7 +65,7 @@ RSpec.describe Channel::TwilioSms do
end
it 'sends via twilio client' do
expect(twilio_messages).to receive(:create).with(
expect(twilio_messages).to receive(:create!).with(
messaging_service_sid: channel.messaging_service_sid,
to: '+15555550111',
body: 'hello world',
@ -79,7 +79,7 @@ RSpec.describe Channel::TwilioSms do
let(:channel) { create(:channel_twilio_sms, :with_phone_number) }
it 'sends via twilio client' do
expect(twilio_messages).to receive(:create).with(
expect(twilio_messages).to receive(:create!).with(
from: channel.phone_number,
to: '+15555550111',
body: 'hello world',
@ -92,7 +92,7 @@ RSpec.describe Channel::TwilioSms do
context 'with media urls' do
it 'supplies a media url' do
expect(twilio_messages).to receive(:create).with(
expect(twilio_messages).to receive(:create!).with(
messaging_service_sid: channel.messaging_service_sid,
to: '+15555550111',
body: 'hello world',

View File

@ -34,7 +34,7 @@ shared_examples_for 'assignment_handler' do
it 'changes assignee to nil if they doesnt belong to the team and allow_auto_assign is false' do
expect(team.allow_auto_assign).to be false
conversation.update(team: team)
conversation.update!(team: team)
expect(conversation.reload.assignee).to be_nil
end
@ -42,7 +42,7 @@ shared_examples_for 'assignment_handler' do
it 'changes assignee to a team member if allow_auto_assign is enabled' do
team.update!(allow_auto_assign: true)
conversation.update(team: team)
conversation.update!(team: team)
expect(conversation.reload.assignee).to eq agent
expect(Conversations::ActivityMessageJob).to(have_been_enqueued.at_least(:once)
@ -55,9 +55,9 @@ shared_examples_for 'assignment_handler' do
assignee = create(:user, account: conversation.account, role: :agent)
create(:inbox_member, user: assignee, inbox: conversation.inbox)
create(:team_member, team: team, user: assignee)
conversation.update(assignee: assignee)
conversation.update!(assignee: assignee)
conversation.update(team: team)
conversation.update!(team: team)
expect(conversation.reload.assignee).to eq assignee
end

View File

@ -27,7 +27,7 @@ shared_examples_for 'auto_assignment_handler' do
end
it 'will not auto assign agent if enable_auto_assignment is false' do
inbox.update(enable_auto_assignment: false)
inbox.update!(enable_auto_assignment: false)
expect(conversation.reload.assignee).to be_nil
end

View File

@ -14,7 +14,7 @@ RSpec.describe ContactInbox do
it 'does not get updated on object update' do
obj = contact_inbox
old_token = obj.pubsub_token
obj.update(source_id: '234234323')
obj.update!(source_id: '234234323')
expect(obj.pubsub_token).to eq(old_token)
end
@ -32,7 +32,7 @@ RSpec.describe ContactInbox do
expect(results.first['pubsub_token']).to be_nil
new_token = obj.pubsub_token
obj.update(source_id: '234234323')
obj.update!(source_id: '234234323')
# the generated token shoul be persisted in db
expect(obj.pubsub_token).to eq(new_token)
end

View File

@ -126,7 +126,7 @@ RSpec.describe Conversation do
end
it 'sends conversation updated event if labels are updated' do
conversation.update(label_list: [label.title])
conversation.update!(label_list: [label.title])
changed_attributes = conversation.previous_changes
expect(Rails.configuration.dispatcher).to have_received(:dispatch)
.with(
@ -140,7 +140,7 @@ RSpec.describe Conversation do
end
it 'runs after_update callbacks' do
conversation.update(
conversation.update!(
status: :resolved,
contact_last_seen_at: Time.zone.now,
assignee: new_assignee
@ -169,7 +169,7 @@ RSpec.describe Conversation do
end
it 'will not run conversation_updated event for non whitelisted keys' do
conversation.update(updated_at: DateTime.now.utc)
conversation.update!(updated_at: DateTime.now.utc)
expect(Rails.configuration.dispatcher).not_to have_received(:dispatch)
.with(described_class::CONVERSATION_UPDATED, kind_of(Time), conversation: conversation, notifiable_assignee_change: true)
end
@ -191,7 +191,7 @@ RSpec.describe Conversation do
end
it 'creates conversation activities' do
conversation.update(
conversation.update!(
status: :resolved,
contact_last_seen_at: Time.zone.now,
assignee: new_assignee,
@ -213,7 +213,7 @@ RSpec.describe Conversation do
end
it 'adds a message for system auto resolution if marked resolved by system' do
account.update(auto_resolve_duration: 40)
account.update!(auto_resolve_duration: 40)
conversation2 = create(:conversation, status: 'open', account: account, assignee: old_assignee)
Current.user = nil
@ -615,9 +615,9 @@ RSpec.describe Conversation do
context 'when instagram channel' do
it 'return true with HUMAN_AGENT if it is outside of 24 hour window' do
InstallationConfig.where(name: 'ENABLE_MESSENGER_CHANNEL_HUMAN_AGENT').first_or_create(value: true)
InstallationConfig.where(name: 'ENABLE_MESSENGER_CHANNEL_HUMAN_AGENT').first_or_create!(value: true)
conversation.update(additional_attributes: { type: 'instagram_direct_message' })
conversation.update!(additional_attributes: { type: 'instagram_direct_message' })
create(
:message,
account: conversation.account,
@ -630,9 +630,9 @@ RSpec.describe Conversation do
end
it 'return false without HUMAN_AGENT if it is outside of 24 hour window' do
InstallationConfig.where(name: 'ENABLE_MESSENGER_CHANNEL_HUMAN_AGENT').first_or_create(value: false)
InstallationConfig.where(name: 'ENABLE_MESSENGER_CHANNEL_HUMAN_AGENT').first_or_create!(value: false)
conversation.update(additional_attributes: { type: 'instagram_direct_message' })
conversation.update!(additional_attributes: { type: 'instagram_direct_message' })
create(
:message,
account: conversation.account,
@ -878,7 +878,7 @@ RSpec.describe Conversation do
let(:conversation) { create(:conversation) }
it 'returns the correct list of labels' do
conversation.update(label_list: %w[customer-support enterprise paid-customer])
conversation.update!(label_list: %w[customer-support enterprise paid-customer])
expect(conversation.cached_label_list_array).to eq %w[customer-support enterprise paid-customer]
end

View File

@ -226,7 +226,7 @@ RSpec.describe Inbox do
it 'set portal id in inbox' do
inbox.portal_id = portal.id
inbox.save
inbox.save!
expect(inbox.portal).to eq(portal)
end
@ -234,7 +234,7 @@ RSpec.describe Inbox do
it 'sends the inbox_created event if ENABLE_INBOX_EVENTS is true' do
with_modified_env ENABLE_INBOX_EVENTS: 'true' do
channel = inbox.channel
channel.update(widget_color: '#fff')
channel.update!(widget_color: '#fff')
expect(Rails.configuration.dispatcher).to have_received(:dispatch)
.with(
@ -248,7 +248,7 @@ RSpec.describe Inbox do
it 'sends the inbox_created event if ENABLE_INBOX_EVENTS is false' do
channel = inbox.channel
channel.update(widget_color: '#fff')
channel.update!(widget_color: '#fff')
expect(Rails.configuration.dispatcher).not_to have_received(:dispatch)
.with(
@ -261,7 +261,7 @@ RSpec.describe Inbox do
it 'resets cache key if there is an update in the channel' do
channel = inbox.channel
channel.update(widget_color: '#fff')
channel.update!(widget_color: '#fff')
expect(Rails.configuration.dispatcher).to have_received(:dispatch)
.with(
@ -274,7 +274,7 @@ RSpec.describe Inbox do
it 'updates the cache key after update' do
expect(inbox.account).to receive(:update_cache_key).with('inbox')
inbox.update(name: 'New Name')
inbox.update!(name: 'New Name')
end
it 'updates the cache key after touch' do

View File

@ -46,13 +46,13 @@ RSpec.describe Label do
it 'calls update job' do
expect(Labels::UpdateJob).to receive(:perform_later).with('new-title', label.title, label.account_id)
label.update(title: 'new-title')
label.update!(title: 'new-title')
end
it 'does not call update job if title is not updated' do
expect(Labels::UpdateJob).not_to receive(:perform_later)
label.update(description: 'new-description')
label.update!(description: 'new-description')
end
end
end

View File

@ -237,7 +237,7 @@ RSpec.describe Message do
end
it 'sets the waiting_since if there is an incoming message' do
conversation.update(waiting_since: nil)
conversation.update!(waiting_since: nil)
message.message_type = :incoming
message.save!

View File

@ -27,13 +27,13 @@ RSpec.describe Portal do
end
it 'Does not allow any other config than allowed_locales' do
portal.update(config: { 'some_other_key': 'test_value' })
expect(portal.update(config: { 'some_other_key': 'test_value' })).to be(false)
expect(portal).not_to be_valid
expect(portal.errors.full_messages[0]).to eq('Cofig in portal on some_other_key is not supported.')
expect(portal.errors.full_messages[0]).to eq('Config in portal on some_other_key is not supported.')
end
it 'converts empty string to nil' do
portal.update(custom_domain: '')
portal.update!(custom_domain: '')
expect(portal.custom_domain).to be_nil
end
end

View File

@ -56,7 +56,7 @@ RSpec.describe WorkingHour do
before do
Time.zone = 'UTC'
inbox.working_hours.find_by(day_of_week: 5).update(open_all_day: true)
inbox.working_hours.find_by(day_of_week: 5).update!(open_all_day: true)
travel_to '18.02.2022 11:00'.to_datetime
end
@ -74,7 +74,7 @@ RSpec.describe WorkingHour do
before do
Time.zone = 'UTC'
inbox.working_hours.find_by(day_of_week: 5).update(open_all_day: true)
inbox.working_hours.find_by(day_of_week: 5).update!(open_all_day: true)
travel_to '18.02.2022 11:00'.to_datetime
end
@ -94,7 +94,7 @@ RSpec.describe WorkingHour do
before do
Time.zone = 'Australia/Sydney'
inbox.update(timezone: 'Australia/Sydney')
inbox.update!(timezone: 'Australia/Sydney')
travel_to '10.10.2022 9:00 AEDT'
end

View File

@ -41,7 +41,7 @@ RSpec.describe 'Api::V1::Accounts::Integrations::Slacks' do
context 'when it is an authenticated user' do
it 'updates hook if the channel id is correct' do
channel_builder = double
expect(channel_builder).to receive(:update).and_return(hook)
expect(channel_builder).to receive(:update_reference_id).and_return(hook)
expect(Integrations::Slack::ChannelBuilder).to receive(:new).and_return(channel_builder)
put "/api/v1/accounts/#{account.id}/integrations/slack",
@ -55,7 +55,7 @@ RSpec.describe 'Api::V1::Accounts::Integrations::Slacks' do
it 'does not update the hook if the channel id is not correct' do
channel_builder = double
expect(channel_builder).to receive(:update)
expect(channel_builder).to receive(:update_reference_id)
expect(Integrations::Slack::ChannelBuilder).to receive(:new).and_return(channel_builder)
put "/api/v1/accounts/#{account.id}/integrations/slack",

Some files were not shown because too many files have changed in this diff Show More