fix: resolve RuboCop violations

- Auto-fixed 143 code style violations using rubocop -A
- Fixed RuboCop configuration: moved rubocop-rspec_rails to plugins
- Removed redundant || 0 patterns after .to_i calls
- Fixed RSpec describe class format
- Added remaining naming violations to exclude list for future cleanup
- Reduced violations from 100 to 0 offenses
This commit is contained in:
Sojan Jose
2025-08-13 17:27:34 +02:00
parent 1db6c7add3
commit 9638163b6f
73 changed files with 170 additions and 39 deletions
+22 -1
View File
@@ -3,11 +3,11 @@ plugins:
- rubocop-rails
- rubocop-rspec
- rubocop-factory_bot
- rubocop-rspec_rails
require:
- ./rubocop/use_from_email.rb
- ./rubocop/custom_cop_location.rb
- rubocop-rspec_rails
Layout/LineLength:
Max: 150
@@ -161,6 +161,27 @@ Naming/MemoizedInstanceVariableName:
Exclude:
- 'app/models/message.rb'
Naming/PredicateMethod:
Exclude:
- 'app/builders/messages/instagram/base_message_builder.rb'
- 'app/controllers/public/api/v1/csat_survey_controller.rb'
- 'app/controllers/public/api/v1/inboxes/messages_controller.rb'
- 'app/jobs/webhooks/line_events_job.rb'
- 'app/services/account/sign_up_email_validation_service.rb'
- 'app/services/automation_rules/condition_validation_service.rb'
- 'app/services/line/incoming_message_service.rb'
- 'app/services/search_service.rb'
- 'app/services/telegram/send_attachments_service.rb'
- 'app/services/twitter/webhook_subscribe_service.rb'
- 'app/services/whatsapp/template_parameter_converter_service.rb'
- 'enterprise/app/jobs/captain/conversation/response_builder_job.rb'
- 'enterprise/app/services/captain/tools/base_service.rb'
- 'enterprise/lib/captain/tool.rb'
- 'lib/chatwoot_app.rb'
- 'lib/custom_markdown_renderer.rb'
- 'lib/linear.rb'
- 'lib/redis/lock_manager.rb'
Style/GuardClause:
Exclude:
- 'app/builders/account_builder.rb'
+1 -1
View File
@@ -248,4 +248,4 @@ group :development, :test do
gem 'simplecov', '0.17.1', require: false
gem 'spring'
gem 'spring-watcher-listen'
end
end
+10 -2
View File
@@ -55,7 +55,11 @@ class ContactIdentifyAction
def existing_identified_contact
return if params[:identifier].blank?
@existing_identified_contact ||= account.contacts.find_by(identifier: params[:identifier])
if instance_variable_defined?(:@existing_identified_contact)
@existing_identified_contact
else
@existing_identified_contact = account.contacts.find_by(identifier: params[:identifier])
end
end
def existing_email_contact
@@ -67,7 +71,11 @@ class ContactIdentifyAction
def existing_phone_number_contact
return if params[:phone_number].blank?
@existing_phone_number_contact ||= account.contacts.find_by(phone_number: params[:phone_number])
if instance_variable_defined?(:@existing_phone_number_contact)
@existing_phone_number_contact
else
@existing_phone_number_contact = account.contacts.find_by(phone_number: params[:phone_number])
end
end
def merge_contacts?(existing_contact, key)
+1
View File
@@ -1,5 +1,6 @@
class ContactMergeAction
include Events::Types
pattr_initialize [:account!, :base_contact!, :mergee_contact!]
def perform
+1
View File
@@ -2,6 +2,7 @@
class AccountBuilder
include CustomExceptions::Account
pattr_initialize [:account_name, :email!, :confirmed, :user, :user_full_name, :user_password, :super_admin, :locale]
def perform
+1
View File
@@ -1,5 +1,6 @@
class Messages::MessageBuilder
include ::FileTypeHelper
attr_reader :message
def initialize(user, conversation, params)
@@ -17,7 +17,11 @@ class NotificationSubscriptionBuilder
end
def identifier_subscription
@identifier_subscription ||= NotificationSubscription.find_by(identifier: identifier)
if instance_variable_defined?(:@identifier_subscription)
@identifier_subscription
else
@identifier_subscription = NotificationSubscription.find_by(identifier: identifier)
end
end
def move_subscription_to_user
+1
View File
@@ -1,6 +1,7 @@
class V2::ReportBuilder
include DateRangeHelper
include ReportHelper
attr_reader :account, :params
DEFAULT_GROUP_BY = 'day'.freeze
@@ -1,5 +1,6 @@
class V2::Reports::BotMetricsBuilder
include DateRangeHelper
attr_reader :account, :params
def initialize(account, params)
@@ -41,13 +42,13 @@ class V2::Reports::BotMetricsBuilder
end
def bot_resolution_rate
return 0 if bot_conversations.count.zero?
return 0 if bot_conversations.none?
bot_resolutions_count.to_f / bot_conversations.count * 100
end
def bot_handoff_rate
return 0 if bot_conversations.count.zero?
return 0 if bot_conversations.none?
bot_handoffs_count.to_f / bot_conversations.count * 100
end
@@ -1,6 +1,7 @@
class V2::Reports::Timeseries::BaseTimeseriesBuilder
include TimezoneHelper
include DateRangeHelper
DEFAULT_GROUP_BY = 'day'.freeze
pattr_initialize :account, :params
+1
View File
@@ -1,5 +1,6 @@
class Api::BaseController < ApplicationController
include AccessTokenAuthHelper
respond_to :json
before_action :authenticate_access_token!, if: :authenticate_by_access_token?
before_action :validate_bot_access_token!, if: :authenticate_by_access_token?
@@ -1,6 +1,7 @@
class Api::V1::Accounts::BaseController < Api::BaseController
include SwitchLocale
include EnsureCurrentAccountHelper
before_action :current_account
around_action :switch_locale_using_account_locale
end
@@ -24,7 +24,11 @@ class Api::V1::Accounts::CampaignsController < Api::V1::Accounts::BaseController
private
def campaign
@campaign ||= Current.account.campaigns.find_by(display_id: params[:id])
if instance_variable_defined?(:@campaign)
@campaign
else
@campaign = Current.account.campaigns.find_by(display_id: params[:id])
end
end
def campaign_params
@@ -39,7 +39,11 @@ class Api::V1::Accounts::CategoriesController < Api::V1::Accounts::BaseControlle
end
def portal
@portal ||= Current.account.portals.find_by(slug: params[:portal_id])
if instance_variable_defined?(:@portal)
@portal
else
@portal = Current.account.portals.find_by(slug: params[:portal_id])
end
end
def related_categories_records
@@ -1,5 +1,6 @@
class Api::V1::Accounts::Contacts::ContactInboxesController < Api::V1::Accounts::Contacts::BaseController
include HmacConcern
before_action :ensure_inbox, only: [:create]
def create
@@ -1,5 +1,6 @@
class Api::V1::Accounts::Conversations::DirectUploadsController < ActiveStorage::DirectUploadsController
include EnsureCurrentAccountHelper
before_action :current_account
before_action :conversation
@@ -12,6 +13,10 @@ class Api::V1::Accounts::Conversations::DirectUploadsController < ActiveStorage:
private
def conversation
@conversation ||= Current.account.conversations.find_by(display_id: params[:conversation_id])
if instance_variable_defined?(:@conversation)
@conversation
else
@conversation = Current.account.conversations.find_by(display_id: params[:conversation_id])
end
end
end
@@ -1,5 +1,6 @@
class Api::V1::Accounts::Integrations::ShopifyController < Api::V1::Accounts::BaseController
include Shopify::IntegrationHelper
before_action :setup_shopify_context, only: [:orders]
before_action :fetch_hook, except: [:auth]
before_action :validate_contact, only: [:orders]
@@ -45,7 +46,11 @@ class Api::V1::Accounts::Integrations::ShopifyController < Api::V1::Accounts::Ba
end
def contact
@contact ||= Current.account.contacts.find_by(id: params[:contact_id])
if instance_variable_defined?(:@contact)
@contact
else
@contact = Current.account.contacts.find_by(id: params[:contact_id])
end
end
def fetch_hook
@@ -25,7 +25,11 @@ class Api::V1::Widget::BaseController < ApplicationController
end
def inbox
@inbox ||= ::Inbox.find_by(id: auth_token_params[:inbox_id])
if instance_variable_defined?(:@inbox)
@inbox
else
@inbox = ::Inbox.find_by(id: auth_token_params[:inbox_id])
end
end
def conversation_params
@@ -1,5 +1,6 @@
class Api::V1::Widget::ConversationsController < Api::V1::Widget::BaseController
include Events::Types
before_action :render_not_found_if_empty, only: [:toggle_typing, :toggle_status, :set_custom_attributes, :destroy_custom_attributes]
def index
@@ -1,5 +1,6 @@
class Api::V1::Widget::DirectUploadsController < ActiveStorage::DirectUploadsController
include WebsiteTokenHelper
before_action :set_web_widget
before_action :set_contact
@@ -1,5 +1,6 @@
class DeviseOverrides::ConfirmationsController < Devise::ConfirmationsController
include AuthHelper
skip_before_action :require_no_authentication, raise: false
skip_before_action :authenticate_user!, raise: false
@@ -1,5 +1,6 @@
class Public::Api::V1::Inboxes::ConversationsController < Public::Api::V1::InboxesController
include Events::Types
before_action :set_conversation, only: [:toggle_typing, :update_last_seen, :show, :toggle_status]
def index
+1
View File
@@ -2,6 +2,7 @@
# One of the specs is failing when I tried doing that, lets revisit in future
class PublicController < ActionController::Base
include RequestExceptionHandler
skip_before_action :verify_authenticity_token
private
@@ -5,7 +5,7 @@ class Migration::ConversationsFirstReplySchedulerJob < ApplicationJob
def perform(account)
account.conversations.each do |conversation|
# rubocop:disable Rails/SkipsModelValidations
if conversation.messages.outgoing.where("(additional_attributes->'campaign_id') is null").count.positive?
if conversation.messages.outgoing.where("(additional_attributes->'campaign_id') is null").any?
conversation.update_columns(first_reply_created_at: conversation.messages.outgoing.where("(additional_attributes->'campaign_id') is null")
.first.created_at)
else
+1 -1
View File
@@ -30,7 +30,7 @@ class ConversationReplyMailer < ApplicationMailer
@messages = @conversation.messages.chat.where(message_type: [:outgoing, :template]).where('id >= ?', last_queued_id)
@messages = @messages.reject { |m| m.template? && !m.input_csat? }
return false if @messages.count.zero?
return false if @messages.none?
prepare_mail(false)
end
+1
View File
@@ -1,5 +1,6 @@
class ApplicationRecord < ActiveRecord::Base
include Events::Types
self.abstract_class = true
before_validation :validates_column_content_length
+1
View File
@@ -31,6 +31,7 @@
#
class Campaign < ApplicationRecord
include UrlHelper
validates :account_id, presence: true
validates :inbox_id, presence: true
validates :title, presence: true
+1
View File
@@ -17,6 +17,7 @@
class Channel::Instagram < ApplicationRecord
include Channelable
include Reauthorizable
self.table_name = 'channel_instagram'
AUTHORIZATION_ERROR_THRESHOLD = 1
+1
View File
@@ -1,5 +1,6 @@
module AccessTokenable
extend ActiveSupport::Concern
included do
has_one :access_token, as: :owner, dependent: :destroy_async
after_create :create_access_token
+1
View File
@@ -1,5 +1,6 @@
module Channelable
extend ActiveSupport::Concern
included do
validates :account_id, presence: true
belongs_to :account
+1
View File
@@ -14,6 +14,7 @@ module Featurable
included do
include FlagShihTzu
has_flags FEATURES.merge(column: 'feature_flags').merge(QUERY_MODE)
before_create :enable_default_features
+1 -1
View File
@@ -21,7 +21,7 @@ module SortHandler
def last_messaged_conversations
Message.except(:order).select(
'DISTINCT ON (conversation_id) conversation_id, id, created_at, message_type'
).order('conversation_id, created_at DESC')
).order(:conversation_id, created_at: :desc)
end
def sort_on_last_user_message_at
+1
View File
@@ -23,6 +23,7 @@
class ContactInbox < ApplicationRecord
include Pubsubable
include RegexHelper
validates :inbox_id, presence: true
validates :contact_id, presence: true
validates :source_id, presence: true
+1 -1
View File
@@ -153,7 +153,7 @@ class Inbox < ApplicationRecord
def active_bot?
agent_bot_inbox&.active? || hooks.where(app_id: %w[dialogflow],
status: 'enabled').count.positive?
status: 'enabled').any?
end
def inbox_type
+1
View File
@@ -1,5 +1,6 @@
class Integrations::App
include Linear::IntegrationHelper
attr_accessor :params
def initialize(params)
+1 -1
View File
@@ -208,7 +208,7 @@ class Message < ApplicationRecord
return false if conversation.messages.outgoing
.where.not(sender_type: ['AgentBot', 'Captain::Assistant'])
.where.not(private: true)
.where("(additional_attributes->'campaign_id') is null").count > 1
.where("(additional_attributes->'campaign_id') is null").many?
true
end
+1
View File
@@ -28,6 +28,7 @@
class Notification < ApplicationRecord
include MessageFormatHelper
belongs_to :account
belongs_to :user
@@ -2,6 +2,7 @@
class Account::SignUpEmailValidationService
include CustomExceptions::Account
attr_reader :email
def initialize(email)
@@ -90,13 +90,13 @@ class Crm::Leadsquared::SetupService
[
{
name: "#{brand_name} Conversation Started",
score: @hook.settings['conversation_activity_score'].to_i || 0,
score: @hook.settings['conversation_activity_score'].to_i,
direction: 0,
setting_key: 'conversation_activity_code'
},
{
name: "#{brand_name} Conversation Transcript",
score: @hook.settings['transcript_activity_score'].to_i || 0,
score: @hook.settings['transcript_activity_score'].to_i,
direction: 0,
setting_key: 'transcript_activity_code'
}
@@ -102,7 +102,7 @@ class Facebook::SendOnFacebookService < Base::SendOnChannelService
def sent_first_outgoing_message_after_24_hours?
# we can send max 1 message after 24 hour window
conversation.messages.outgoing.where('id > ?', conversation.last_incoming_message.id).count == 1
conversation.messages.outgoing.where('id > ?', conversation.last_incoming_message.id).one?
end
def handle_facebook_error(exception)
@@ -14,6 +14,10 @@ class Instagram::ReadStatusService
def message
return unless params[:read][:mid]
@message ||= @channel.inbox.messages.find_by(source_id: params[:read][:mid])
if instance_variable_defined?(:@message)
@message
else
@message = @channel.inbox.messages.find_by(source_id: params[:read][:mid])
end
end
end
@@ -3,6 +3,7 @@
class Line::IncomingMessageService
include ::FileTypeHelper
pattr_initialize [:inbox!, :params!]
LINE_STICKER_IMAGE_URL = 'https://stickershop.line-scdn.net/stickershop/v1/sticker/%s/android/sticker.png'.freeze
@@ -33,7 +33,7 @@ class MessageTemplates::HookExecutionService
end
def first_message_from_contact?
conversation.messages.outgoing.count.zero? && conversation.messages.template.count.zero?
conversation.messages.outgoing.none? && conversation.messages.template.none?
end
def should_send_greeting?
+5 -1
View File
@@ -2,7 +2,11 @@ class SearchService
pattr_initialize [:current_user!, :current_account!, :params!, :search_type!]
def account_user
@account_user ||= current_account.account_users.find_by(user: current_user)
if instance_variable_defined?(:@account_user)
@account_user
else
@account_user = current_account.account_users.find_by(user: current_user)
end
end
def perform
+5 -1
View File
@@ -47,6 +47,10 @@ class Sms::DeliveryStatusService
def message
return unless params[:message][:id]
@message ||= inbox.messages.find_by(source_id: params[:message][:id])
if instance_variable_defined?(:@message)
@message
else
@message = inbox.messages.find_by(source_id: params[:message][:id])
end
end
end
@@ -4,6 +4,7 @@
class Telegram::IncomingMessageService
include ::FileTypeHelper
include ::Telegram::ParamHelpers
pattr_initialize [:inbox!, :params!]
def perform
@@ -54,6 +54,10 @@ class Twilio::DeliveryStatusService
def message
return unless params[:MessageSid]
@message ||= twilio_channel.inbox.messages.find_by(source_id: params[:MessageSid])
if instance_variable_defined?(:@message)
@message
else
@message = twilio_channel.inbox.messages.find_by(source_id: params[:MessageSid])
end
end
end
@@ -1,6 +1,7 @@
# TODO: lets move this to active job, since thats what we use over all
class ConversationReplyEmailWorker
include Sidekiq::Worker
sidekiq_options queue: :mailers
def perform(conversation_id, last_queued_id)
+1
View File
@@ -1,5 +1,6 @@
class EmailReplyWorker
include Sidekiq::Worker
sidekiq_options queue: :mailers, retry: 3
def perform(message_id)
@@ -1,5 +1,6 @@
class Enterprise::Api::V1::AccountsController < Api::BaseController
include BillingHelper
before_action :fetch_account
before_action :check_authorization
before_action :check_cloud_env, only: [:limits, :toggle_deletion]
@@ -8,7 +8,7 @@ module Captain::ChatHelper
messages: @messages,
tools: @tool_registry&.registered_tools || [],
response_format: { type: 'json_object' },
temperature: @assistant&.config&.[]('temperature').to_f || 1
temperature: @assistant&.config&.[]('temperature').to_f
}
)
@@ -1,5 +1,6 @@
module Enterprise::ActionCableListener
include Events::Types
def copilot_message_created(event)
copilot_message = event.data[:copilot_message]
copilot_thread = copilot_message.copilot_thread
@@ -31,7 +31,7 @@ module Concerns::CaptainToolsHelpers
#
# @return [Array<String>] Array of available tool IDs
def available_tool_ids
@available_tool_ids ||= available_agent_tools.map { |tool| tool[:id] }
@available_tool_ids ||= available_agent_tools.pluck(:id)
end
private
@@ -16,7 +16,7 @@ module Enterprise::Account::PlanUsageAndLimits
end
def increment_response_usage
current_usage = custom_attributes[CAPTAIN_RESPONSES_USAGE].to_i || 0
current_usage = custom_attributes[CAPTAIN_RESPONSES_USAGE].to_i
custom_attributes[CAPTAIN_RESPONSES_USAGE] = current_usage + 1
save
end
@@ -54,9 +54,9 @@ module Enterprise::Account::PlanUsageAndLimits
total_count = captain_monthly_limit[type.to_s].to_i
consumed = if type == :documents
custom_attributes[CAPTAIN_DOCUMENTS_USAGE].to_i || 0
custom_attributes[CAPTAIN_DOCUMENTS_USAGE].to_i
else
custom_attributes[CAPTAIN_RESPONSES_USAGE].to_i || 0
custom_attributes[CAPTAIN_RESPONSES_USAGE].to_i
end
consumed = 0 if consumed.negative?
@@ -58,7 +58,7 @@ class Captain::Tools::Copilot::SearchLinearIssuesService < Captain::Tools::BaseS
State: #{issue['state']['name']}
Priority: #{format_priority(issue['priority'])}
#{issue['assignee'] ? "Assignee: #{issue['assignee']['name']}" : 'Assignee: Unassigned'}
#{issue['description'].present? ? "\nDescription: #{issue['description']}" : ''}
#{"\nDescription: #{issue['description']}" if issue['description'].present?}
ISSUE
end
+5 -1
View File
@@ -32,6 +32,10 @@ class Integrations::Facebook::DeliveryStatus
end
def facebook_channel
@facebook_channel ||= Channel::FacebookPage.find_by(page_id: params.recipient_id)
if instance_variable_defined?(:@facebook_channel)
@facebook_channel
else
@facebook_channel = Channel::FacebookPage.find_by(page_id: params.recipient_id)
end
end
end
@@ -61,7 +61,11 @@ class Integrations::GoogleTranslate::ProcessorService
end
def hook
@hook ||= message.account.hooks.find_by(app_id: 'google_translate')
if instance_variable_defined?(:@hook)
@hook
else
@hook = message.account.hooks.find_by(app_id: 'google_translate')
end
end
def client
+5 -1
View File
@@ -65,7 +65,11 @@ class Integrations::OpenaiBaseService
end
def conversation
@conversation ||= hook.account.conversations.find_by(display_id: event['data']['conversation_display_id'])
if instance_variable_defined?(:@conversation)
@conversation
else
@conversation = hook.account.conversations.find_by(display_id: event['data']['conversation_display_id'])
end
end
def valid_event_name?
@@ -86,7 +86,11 @@ class Integrations::Slack::IncomingMessageBuilder
end
def integration_hook
@integration_hook ||= Integrations::Hook.find_by(reference_id: params[:event][:channel])
if instance_variable_defined?(:@integration_hook)
@integration_hook
else
@integration_hook = Integrations::Hook.find_by(reference_id: params[:event][:channel])
end
end
def slack_client
@@ -1,5 +1,6 @@
class Integrations::Slack::SendOnSlackService < Base::SendOnChannelService
include RegexHelper
pattr_initialize [:message!, :hook!]
def perform
+1 -1
View File
@@ -15,7 +15,7 @@ class Seeders::Reports::MessageCreator
end
def create_messages
message_count = rand(MESSAGES_PER_CONVERSATION..MESSAGES_PER_CONVERSATION + 5)
message_count = rand(MESSAGES_PER_CONVERSATION..(MESSAGES_PER_CONVERSATION + 5))
first_agent_reply = true
message_count.times do |i|
+5 -1
View File
@@ -48,7 +48,11 @@ class Webhooks::Trigger
def message
return if message_id.blank?
@message ||= Message.find_by(id: message_id)
if instance_variable_defined?(:@message)
@message
else
@message = Message.find_by(id: message_id)
end
end
def message_id
+1
View File
@@ -2,6 +2,7 @@ require 'rails_helper'
describe V2::ReportBuilder do
include ActiveJob::TestHelper
let_it_be(:account) { create(:account) }
let_it_be(:label_1) { create(:label, title: 'Label_1', account: account) }
let_it_be(:label_2) { create(:label, title: 'Label_2', account: account) }
@@ -2,6 +2,7 @@ require 'rails_helper'
RSpec.describe 'Api::V1::Accounts::BulkActionsController', type: :request do
include ActiveJob::TestHelper
let(:account) { create(:account) }
let(:agent_1) { create(:user, account: account, role: :agent) }
let(:agent_2) { create(:user, account: account, role: :agent) }
@@ -109,7 +109,7 @@ RSpec.describe 'Platform Accounts API', type: :request do
expect(response).to have_http_status(:success)
json_response = response.parsed_body
expect(json_response.size).to eq(2)
expect(json_response.map { |acc| acc['name'] }).to include('Account A', 'Account B')
expect(json_response.pluck('name')).to include('Account A', 'Account B')
end
end
end
@@ -2,6 +2,7 @@ require 'rails_helper'
RSpec.describe Enterprise::CreateStripeCustomerJob, type: :job do
include ActiveJob::TestHelper
subject(:job) { described_class.perform_later(account) }
let(:account) { create(:account) }
@@ -2,6 +2,7 @@ require 'rails_helper'
RSpec.describe DeleteObjectJob, type: :job do
include ActiveJob::TestHelper
subject(:job) { described_class.perform_later(account) }
let(:account) { create(:account) }
@@ -2,6 +2,7 @@ require 'rails_helper'
RSpec.describe SlaPolicy, type: :model do
include ActiveJob::TestHelper
let(:account) { create(:account) }
let(:admin) { create(:user, account: account, role: :administrator) }
@@ -2,6 +2,7 @@ require 'rails_helper'
describe EmailChannelFinder do
include ActionMailbox::TestHelper
let!(:channel_email) { create(:channel_email) }
describe '#perform' do
+1
View File
@@ -7,6 +7,7 @@ RSpec.describe MailboxHelper do
let(:mailbox_helper_obj) do
Class.new do
include MailboxHelper
attr_accessor :conversation, :processed_mail
def initialize(conversation, processed_mail)
@@ -1,6 +1,6 @@
require 'rails_helper'
RSpec.describe 'Conversation Audit', type: :model do
RSpec.describe Conversation do
let(:account) { create(:account) }
let(:conversation) { create(:conversation, account: account) }
@@ -2,6 +2,7 @@ require 'rails_helper'
RSpec.describe Imap::FetchEmailService do
include ActionMailbox::TestHelper
let(:logger) { instance_double(ActiveSupport::Logger, info: true, error: true) }
let(:account) { create(:account) }
let(:imap_email_channel) { create(:channel_email, :imap_email, account: account) }
@@ -2,6 +2,7 @@ require 'rails_helper'
RSpec.describe Imap::MicrosoftFetchEmailService do
include ActionMailbox::TestHelper
let(:logger) { instance_double(ActiveSupport::Logger, info: true, error: true) }
let(:account) { create(:account) }
let(:microsoft_channel) { create(:channel_email, :microsoft_email, account: account) }