mirror of
https://github.com/lingble/chatwoot.git
synced 2025-11-01 03:27:52 +00:00
We now support searching within the actual message content, email subject lines, and audio transcriptions. This enables a faster, more accurate search experience going forward. Unlike the standard message search, which is limited to the last 3 months, this search has no time restrictions. The search engine also accounts for small variations in queries. Minor spelling mistakes, such as searching for slck instead of Slack, will still return the correct results. It also ignores differences in accents and diacritics, so searching for Deja vu will match content containing Déjà vu. We can also refine searches in the future by criteria such as: - Searching within a specific inbox - Filtering by sender or recipient - Limiting to messages sent by an agent Fixes https://github.com/chatwoot/chatwoot/issues/11656 Fixes https://github.com/chatwoot/chatwoot/issues/10669 Fixes https://github.com/chatwoot/chatwoot/issues/5910 --- Rake tasks to reindex all the messages. ```sh bundle exec rake search:all ``` Rake task to reindex messages from one account only ```sh bundle exec rake search:account ACCOUNT_ID=1 ```
670 lines
25 KiB
Ruby
670 lines
25 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
require 'rails_helper'
|
|
require Rails.root.join 'spec/models/concerns/liquidable_shared.rb'
|
|
|
|
RSpec.describe Message do
|
|
context 'with validations' do
|
|
it { is_expected.to validate_presence_of(:inbox_id) }
|
|
it { is_expected.to validate_presence_of(:conversation_id) }
|
|
it { is_expected.to validate_presence_of(:account_id) }
|
|
end
|
|
|
|
describe 'length validations' do
|
|
let!(:message) { create(:message) }
|
|
|
|
context 'when it validates name length' do
|
|
it 'valid when within limit' do
|
|
message.content = 'a' * 120_000
|
|
expect(message.valid?).to be true
|
|
end
|
|
|
|
it 'invalid when crossed the limit' do
|
|
message.content = 'a' * 150_001
|
|
message.processed_message_content = 'a' * 150_001
|
|
message.valid?
|
|
|
|
expect(message.errors[:processed_message_content]).to include('is too long (maximum is 150000 characters)')
|
|
expect(message.errors[:content]).to include('is too long (maximum is 150000 characters)')
|
|
end
|
|
|
|
it 'adds error in case of message flooding' do
|
|
with_modified_env 'CONVERSATION_MESSAGE_PER_MINUTE_LIMIT': '2' do
|
|
conversation = message.conversation
|
|
create(:message, conversation: conversation)
|
|
conv_new_message = build(:message, conversation: message.conversation)
|
|
|
|
expect(conv_new_message.valid?).to be false
|
|
expect(conv_new_message.errors[:base]).to eq(['Too many messages'])
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
describe 'concerns' do
|
|
it_behaves_like 'liqudable'
|
|
end
|
|
|
|
describe 'message_filter_helpers' do
|
|
context 'when webhook_sendable?' do
|
|
[
|
|
{ type: :incoming, expected: true },
|
|
{ type: :outgoing, expected: true },
|
|
{ type: :template, expected: true },
|
|
{ type: :activity, expected: false }
|
|
].each do |scenario|
|
|
it "returns #{scenario[:expected]} for #{scenario[:type]} message" do
|
|
message = create(:message, message_type: scenario[:type])
|
|
expect(message.webhook_sendable?).to eq(scenario[:expected])
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
describe '#push_event_data' do
|
|
subject(:push_event_data) { message.push_event_data }
|
|
|
|
let(:message) { create(:message, echo_id: 'random-echo_id') }
|
|
|
|
let(:expected_data) do
|
|
{
|
|
|
|
account_id: message.account_id,
|
|
additional_attributes: message.additional_attributes,
|
|
content_attributes: message.content_attributes,
|
|
content_type: message.content_type,
|
|
content: message.content,
|
|
conversation_id: message.conversation.display_id,
|
|
created_at: message.created_at.to_i,
|
|
external_source_ids: message.external_source_ids,
|
|
id: message.id,
|
|
inbox_id: message.inbox_id,
|
|
message_type: message.message_type_before_type_cast,
|
|
private: message.private,
|
|
processed_message_content: message.processed_message_content,
|
|
sender_id: message.sender_id,
|
|
sender_type: message.sender_type,
|
|
source_id: message.source_id,
|
|
status: message.status,
|
|
updated_at: message.updated_at,
|
|
conversation: {
|
|
assignee_id: message.conversation.assignee_id,
|
|
contact_inbox: {
|
|
source_id: message.conversation.contact_inbox.source_id
|
|
},
|
|
last_activity_at: message.conversation.last_activity_at.to_i,
|
|
unread_count: message.conversation.unread_incoming_messages.count
|
|
},
|
|
sentiment: {},
|
|
sender: message.sender.push_event_data,
|
|
echo_id: 'random-echo_id'
|
|
}
|
|
end
|
|
|
|
it 'returns push event payload' do
|
|
expect(push_event_data).to eq(expected_data)
|
|
end
|
|
end
|
|
|
|
describe 'message create event' do
|
|
let!(:conversation) { create(:conversation) }
|
|
|
|
before do
|
|
conversation.reload
|
|
end
|
|
|
|
it 'updates the conversation first reply created at if it is the first outgoing message' do
|
|
expect(conversation.first_reply_created_at).to be_nil
|
|
expect(conversation.waiting_since).to eq conversation.created_at
|
|
|
|
outgoing_message = create(:message, message_type: :outgoing, conversation: conversation)
|
|
|
|
expect(conversation.first_reply_created_at).to eq outgoing_message.created_at
|
|
expect(conversation.waiting_since).to be_nil
|
|
end
|
|
|
|
it 'does not update the conversation first reply created at if the message is incoming' do
|
|
expect(conversation.first_reply_created_at).to be_nil
|
|
expect(conversation.waiting_since).to eq conversation.created_at
|
|
|
|
create(:message, message_type: :incoming, conversation: conversation)
|
|
|
|
expect(conversation.first_reply_created_at).to be_nil
|
|
expect(conversation.waiting_since).to eq conversation.created_at
|
|
end
|
|
|
|
it 'does not update the conversation first reply created at if the message is template' do
|
|
expect(conversation.first_reply_created_at).to be_nil
|
|
expect(conversation.waiting_since).to eq conversation.created_at
|
|
|
|
create(:message, message_type: :template, conversation: conversation)
|
|
|
|
expect(conversation.first_reply_created_at).to be_nil
|
|
expect(conversation.waiting_since).to eq conversation.created_at
|
|
end
|
|
|
|
it 'does not update the conversation first reply created at if the message is activity' do
|
|
expect(conversation.first_reply_created_at).to be_nil
|
|
expect(conversation.waiting_since).to eq conversation.created_at
|
|
|
|
create(:message, message_type: :activity, conversation: conversation)
|
|
|
|
expect(conversation.first_reply_created_at).to be_nil
|
|
expect(conversation.waiting_since).to eq conversation.created_at
|
|
end
|
|
|
|
it 'does not update the conversation first reply created at if the message is a private message' do
|
|
expect(conversation.first_reply_created_at).to be_nil
|
|
expect(conversation.waiting_since).to eq conversation.created_at
|
|
|
|
create(:message, message_type: :outgoing, conversation: conversation, private: true)
|
|
|
|
expect(conversation.first_reply_created_at).to be_nil
|
|
expect(conversation.waiting_since).to eq conversation.created_at
|
|
|
|
next_message = create(:message, message_type: :outgoing, conversation: conversation)
|
|
expect(conversation.first_reply_created_at).to eq next_message.created_at
|
|
expect(conversation.waiting_since).to be_nil
|
|
end
|
|
|
|
it 'does not update first reply if the message is sent as campaign' do
|
|
expect(conversation.first_reply_created_at).to be_nil
|
|
expect(conversation.waiting_since).to eq conversation.created_at
|
|
|
|
create(:message, message_type: :outgoing, conversation: conversation, additional_attributes: { campaign_id: 1 })
|
|
|
|
expect(conversation.first_reply_created_at).to be_nil
|
|
expect(conversation.waiting_since).to eq conversation.created_at
|
|
end
|
|
|
|
it 'does not update first reply if the message is sent by automation' do
|
|
expect(conversation.first_reply_created_at).to be_nil
|
|
expect(conversation.waiting_since).to eq conversation.created_at
|
|
|
|
create(:message, message_type: :outgoing, conversation: conversation, content_attributes: { automation_rule_id: 1 })
|
|
|
|
expect(conversation.first_reply_created_at).to be_nil
|
|
expect(conversation.waiting_since).to eq conversation.created_at
|
|
end
|
|
end
|
|
|
|
describe '#reopen_conversation' do
|
|
let(:conversation) { create(:conversation) }
|
|
let(:message) { build(:message, message_type: :incoming, conversation: conversation) }
|
|
|
|
it 'reopens resolved conversation when the message is from a contact' do
|
|
conversation.resolved!
|
|
message.save!
|
|
expect(message.conversation.open?).to be true
|
|
end
|
|
|
|
it 'reopens snoozed conversation when the message is from a contact' do
|
|
conversation.snoozed!
|
|
message.save!
|
|
expect(message.conversation.open?).to be true
|
|
end
|
|
|
|
it 'will not reopen if the conversation is muted' do
|
|
conversation.resolved!
|
|
conversation.mute!
|
|
message.save!
|
|
expect(message.conversation.open?).to be false
|
|
end
|
|
|
|
it 'will mark the conversation as pending if the agent bot is active' do
|
|
agent_bot = create(:agent_bot)
|
|
inbox = conversation.inbox
|
|
inbox.agent_bot = agent_bot
|
|
inbox.save!
|
|
conversation.resolved!
|
|
message.save!
|
|
expect(conversation.open?).to be false
|
|
expect(conversation.pending?).to be true
|
|
end
|
|
end
|
|
|
|
describe '#waiting since' do
|
|
let(:conversation) { create(:conversation) }
|
|
let(:agent) { create(:user, account: conversation.account) }
|
|
let(:message) { build(:message, conversation: conversation) }
|
|
|
|
it 'resets the waiting_since if an agent sent a reply' do
|
|
message.message_type = :outgoing
|
|
message.sender = agent
|
|
message.save!
|
|
|
|
expect(conversation.waiting_since).to be_nil
|
|
end
|
|
|
|
it 'sets the waiting_since if there is an incoming message' do
|
|
conversation.update(waiting_since: nil)
|
|
message.message_type = :incoming
|
|
message.save!
|
|
|
|
expect(conversation.waiting_since).not_to be_nil
|
|
end
|
|
|
|
it 'does not overwrite the previous value if there are newer messages' do
|
|
old_waiting_since = conversation.waiting_since
|
|
message.message_type = :incoming
|
|
message.save!
|
|
conversation.reload
|
|
|
|
expect(conversation.waiting_since).to eq old_waiting_since
|
|
end
|
|
end
|
|
|
|
context 'with webhook_data' do
|
|
it 'contains the message attachment when attachment is present' do
|
|
message = create(:message)
|
|
attachment = message.attachments.new(account_id: message.account_id, file_type: :image)
|
|
attachment.file.attach(io: Rails.root.join('spec/assets/avatar.png').open, filename: 'avatar.png', content_type: 'image/png')
|
|
attachment.save!
|
|
expect(message.webhook_data.key?(:attachments)).to be true
|
|
end
|
|
|
|
it 'does not contain the message attachment when attachment is not present' do
|
|
message = create(:message)
|
|
expect(message.webhook_data.key?(:attachments)).to be false
|
|
end
|
|
|
|
it 'uses outgoing_content for webhook content' do
|
|
message = create(:message, content: 'Test content')
|
|
expect(message).to receive(:outgoing_content).and_return('Outgoing test content')
|
|
|
|
webhook_data = message.webhook_data
|
|
expect(webhook_data[:content]).to eq('Outgoing test content')
|
|
end
|
|
|
|
it 'includes CSAT survey link in webhook content for input_csat messages' do
|
|
inbox = create(:inbox, channel: create(:channel_api))
|
|
conversation = create(:conversation, inbox: inbox)
|
|
message = create(:message, conversation: conversation, content_type: 'input_csat', content: 'Rate your experience')
|
|
|
|
expect(message.outgoing_content).to include('survey/responses/')
|
|
expect(message.webhook_data[:content]).to include('survey/responses/')
|
|
end
|
|
end
|
|
|
|
context 'when message is created' do
|
|
let(:message) { build(:message, account: create(:account)) }
|
|
|
|
it 'updates conversation last_activity_at when created' do
|
|
message.save!
|
|
expect(message.created_at).to eq message.conversation.last_activity_at
|
|
end
|
|
|
|
it 'updates contact last_activity_at when created' do
|
|
expect { message.save! }.to(change { message.sender.last_activity_at })
|
|
end
|
|
|
|
it 'triggers ::MessageTemplates::HookExecutionService' do
|
|
hook_execution_service = double
|
|
allow(MessageTemplates::HookExecutionService).to receive(:new).and_return(hook_execution_service)
|
|
allow(hook_execution_service).to receive(:perform).and_return(true)
|
|
|
|
message.save!
|
|
|
|
expect(MessageTemplates::HookExecutionService).to have_received(:new).with(message: message)
|
|
expect(hook_execution_service).to have_received(:perform)
|
|
end
|
|
|
|
context 'with conversation continuity' do
|
|
it 'calls notify email method on after save for outgoing messages in website channel' do
|
|
allow(ConversationReplyEmailWorker).to receive(:perform_in).and_return(true)
|
|
message.message_type = 'outgoing'
|
|
message.save!
|
|
expect(ConversationReplyEmailWorker).to have_received(:perform_in)
|
|
end
|
|
|
|
it 'does not call notify email for website channel if continuity is disabled' do
|
|
message.inbox = create(:inbox, account: message.account,
|
|
channel: build(:channel_widget, account: message.account, continuity_via_email: false))
|
|
allow(ConversationReplyEmailWorker).to receive(:perform_in).and_return(true)
|
|
message.message_type = 'outgoing'
|
|
message.save!
|
|
expect(ConversationReplyEmailWorker).not_to have_received(:perform_in)
|
|
end
|
|
|
|
it 'wont call notify email method for private notes' do
|
|
message.private = true
|
|
allow(ConversationReplyEmailWorker).to receive(:perform_in).and_return(true)
|
|
message.save!
|
|
expect(ConversationReplyEmailWorker).not_to have_received(:perform_in)
|
|
end
|
|
|
|
it 'calls EmailReply worker if the channel is email' do
|
|
message.inbox = create(:inbox, account: message.account, channel: build(:channel_email, account: message.account))
|
|
allow(EmailReplyWorker).to receive(:perform_in).and_return(true)
|
|
message.message_type = 'outgoing'
|
|
message.content_attributes = { email: { text_content: { quoted: 'quoted text' } } }
|
|
message.save!
|
|
expect(EmailReplyWorker).to have_received(:perform_in).with(1.second, message.id)
|
|
end
|
|
|
|
it 'wont call notify email method unless its website or email channel' do
|
|
message.inbox = create(:inbox, account: message.account, channel: build(:channel_api, account: message.account))
|
|
allow(ConversationReplyEmailWorker).to receive(:perform_in).and_return(true)
|
|
message.save!
|
|
expect(ConversationReplyEmailWorker).not_to have_received(:perform_in)
|
|
end
|
|
end
|
|
end
|
|
|
|
context 'when content_type is blank' do
|
|
let(:message) { build(:message, content_type: nil, account: create(:account)) }
|
|
|
|
it 'sets content_type as text' do
|
|
message.save!
|
|
expect(message.content_type).to eq 'text'
|
|
end
|
|
end
|
|
|
|
context 'when processed_message_content is blank' do
|
|
let(:message) { build(:message, content_type: :text, account: create(:account), content: 'Processed message content') }
|
|
|
|
it 'sets content_type as text' do
|
|
message.save!
|
|
expect(message.processed_message_content).to eq message.content
|
|
end
|
|
end
|
|
|
|
context 'when attachments size maximum' do
|
|
let(:message) { build(:message, content_type: nil, account: create(:account)) }
|
|
|
|
it 'add errors to message for attachment size is more than allowed limit' do
|
|
16.times.each do
|
|
attachment = message.attachments.new(account_id: message.account_id, file_type: :image)
|
|
attachment.file.attach(io: Rails.root.join('spec/assets/avatar.png').open, filename: 'avatar.png', content_type: 'image/png')
|
|
end
|
|
|
|
expect(message.errors.messages).to eq({ attachments: ['exceeded maximum allowed'] })
|
|
end
|
|
end
|
|
|
|
context 'when email notifiable message' do
|
|
let(:message) { build(:message, content_type: nil, account: create(:account)) }
|
|
|
|
it 'return false if private message' do
|
|
message.private = true
|
|
message.message_type = 'outgoing'
|
|
expect(message.email_notifiable_message?).to be false
|
|
end
|
|
|
|
it 'return false if incoming message' do
|
|
message.private = false
|
|
message.message_type = 'incoming'
|
|
expect(message.email_notifiable_message?).to be false
|
|
end
|
|
|
|
it 'return false if activity message' do
|
|
message.private = false
|
|
message.message_type = 'activity'
|
|
expect(message.email_notifiable_message?).to be false
|
|
end
|
|
|
|
it 'return false if message type is template and content type is not input_csat or text' do
|
|
message.private = false
|
|
message.message_type = 'template'
|
|
message.content_type = 'incoming_email'
|
|
expect(message.email_notifiable_message?).to be false
|
|
end
|
|
|
|
it 'return true if not private and not incoming and message content type is input_csat or text' do
|
|
message.private = false
|
|
message.message_type = 'template'
|
|
message.content_type = 'text'
|
|
expect(message.email_notifiable_message?).to be true
|
|
end
|
|
end
|
|
|
|
context 'when facebook channel with unavailable story link' do
|
|
let(:instagram_message) { create(:message, :instagram_story_mention) }
|
|
|
|
before do
|
|
# stubbing the request to facebook api during the message creation
|
|
stub_request(:get, %r{https://graph.facebook.com/.*}).to_return(status: 200, body: {
|
|
story: { mention: { link: 'http://graph.facebook.com/test-story-mention', id: '17920786367196703' } },
|
|
from: { username: 'Sender-id-1', id: 'Sender-id-1' },
|
|
id: 'instagram-message-id-1234'
|
|
}.to_json, headers: {})
|
|
end
|
|
|
|
it 'keeps the attachment for deleted stories' do
|
|
expect(instagram_message.attachments.count).to eq 1
|
|
stub_request(:get, %r{https://graph.facebook.com/.*}).to_return(status: 404)
|
|
instagram_message.push_event_data
|
|
expect(instagram_message.reload.attachments.count).to eq 1
|
|
end
|
|
|
|
it 'keeps the attachment for expired stories' do
|
|
expect(instagram_message.attachments.count).to eq 1
|
|
# for expired stories, the link will be empty
|
|
stub_request(:get, %r{https://graph.facebook.com/.*}).to_return(status: 200, body: {
|
|
story: { mention: { link: '', id: '17920786367196703' } }
|
|
}.to_json, headers: {})
|
|
instagram_message.push_event_data
|
|
expect(instagram_message.reload.attachments.count).to eq 1
|
|
end
|
|
end
|
|
|
|
describe '#ensure_in_reply_to' do
|
|
let(:conversation) { create(:conversation) }
|
|
let(:message) { create(:message, conversation: conversation, source_id: 12_345) }
|
|
|
|
context 'when in_reply_to is present' do
|
|
let(:content_attributes) { { in_reply_to: message.id } }
|
|
let(:new_message) { build(:message, conversation: conversation, content_attributes: content_attributes) }
|
|
|
|
it 'sets in_reply_to_external_id based on the source_id of the referenced message' do
|
|
new_message.send(:ensure_in_reply_to)
|
|
expect(new_message.content_attributes[:in_reply_to_external_id]).to eq(message.source_id)
|
|
end
|
|
end
|
|
|
|
context 'when in_reply_to is not present' do
|
|
let(:content_attributes) { { in_reply_to_external_id: message.source_id } }
|
|
let(:new_message) { build(:message, conversation: conversation, content_attributes: content_attributes) }
|
|
|
|
it 'sets in_reply_to based on the source_id of the referenced message' do
|
|
new_message.send(:ensure_in_reply_to)
|
|
expect(new_message.content_attributes[:in_reply_to]).to eq(message.id)
|
|
end
|
|
end
|
|
|
|
context 'when the referenced message is not found' do
|
|
let(:content_attributes) { { in_reply_to: message.id + 1 } }
|
|
let(:new_message) { build(:message, conversation: conversation, content_attributes: content_attributes) }
|
|
|
|
it 'does not set in_reply_to_external_id' do
|
|
new_message.send(:ensure_in_reply_to)
|
|
expect(new_message.content_attributes[:in_reply_to_external_id]).to be_nil
|
|
end
|
|
end
|
|
|
|
context 'when the source message is not found' do
|
|
let(:content_attributes) { { in_reply_to_external_id: 'source-id-that-does-not-exist' } }
|
|
let(:new_message) { build(:message, conversation: conversation, content_attributes: content_attributes) }
|
|
|
|
it 'does not set in_reply_to' do
|
|
new_message.send(:ensure_in_reply_to)
|
|
expect(new_message.content_attributes[:in_reply_to]).to be_nil
|
|
end
|
|
end
|
|
end
|
|
|
|
describe '#content' do
|
|
let(:conversation) { create(:conversation) }
|
|
|
|
context 'when message is not input_csat' do
|
|
let(:message) { create(:message, conversation: conversation, content_type: 'text', content: 'Regular message') }
|
|
|
|
it 'returns original content' do
|
|
expect(message.content).to eq('Regular message')
|
|
end
|
|
end
|
|
|
|
context 'when message is input_csat' do
|
|
let(:message) { create(:message, conversation: conversation, content_type: 'input_csat', content: 'Rate your experience') }
|
|
|
|
context 'when inbox is web widget' do
|
|
before do
|
|
allow(message.inbox).to receive(:web_widget?).and_return(true)
|
|
end
|
|
|
|
it 'returns original content without survey URL' do
|
|
expect(message.content).to eq('Rate your experience')
|
|
end
|
|
end
|
|
|
|
context 'when inbox is not web widget' do
|
|
before do
|
|
allow(message.inbox).to receive(:web_widget?).and_return(false)
|
|
end
|
|
|
|
it 'returns only the stored content (clean for dashboard)' do
|
|
expect(message.content).to eq('Rate your experience')
|
|
end
|
|
|
|
it 'returns only the base content without URL when survey_url stored separately' do
|
|
message.content_attributes = { 'survey_url' => 'https://app.chatwoot.com/survey/responses/12345' }
|
|
expect(message.content).to eq('Rate your experience')
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
describe '#outgoing_content' do
|
|
let(:conversation) { create(:conversation) }
|
|
let(:message) { create(:message, conversation: conversation, content_type: 'text', content: 'Regular message') }
|
|
|
|
it 'delegates to MessageContentPresenter' do
|
|
presenter = instance_double(MessageContentPresenter)
|
|
allow(MessageContentPresenter).to receive(:new).with(message).and_return(presenter)
|
|
allow(presenter).to receive(:outgoing_content).and_return('Presented content')
|
|
|
|
expect(message.outgoing_content).to eq('Presented content')
|
|
expect(MessageContentPresenter).to have_received(:new).with(message)
|
|
expect(presenter).to have_received(:outgoing_content)
|
|
end
|
|
end
|
|
|
|
describe '#auto_reply_email?' do
|
|
context 'when message is not an incoming email and inbox is not email' do
|
|
let(:conversation) { create(:conversation) }
|
|
let(:message) { create(:message, conversation: conversation, message_type: :outgoing) }
|
|
|
|
it 'returns false' do
|
|
expect(message.auto_reply_email?).to be false
|
|
end
|
|
end
|
|
|
|
context 'when message is an incoming email' do
|
|
let(:email_channel) { create(:channel_email) }
|
|
let(:email_inbox) { create(:inbox, channel: email_channel) }
|
|
let(:conversation) { create(:conversation, inbox: email_inbox) }
|
|
|
|
it 'returns false when auto_reply is not set to true' do
|
|
message = create(
|
|
:message,
|
|
conversation: conversation,
|
|
message_type: :incoming,
|
|
content_type: 'incoming_email',
|
|
content_attributes: {}
|
|
)
|
|
expect(message.auto_reply_email?).to be false
|
|
end
|
|
|
|
it 'returns true when auto_reply is set to true' do
|
|
message = create(
|
|
:message,
|
|
conversation: conversation,
|
|
message_type: :incoming,
|
|
content_type: 'incoming_email',
|
|
content_attributes: { email: { auto_reply: true } }
|
|
)
|
|
expect(message.auto_reply_email?).to be true
|
|
end
|
|
end
|
|
|
|
context 'when inbox is email' do
|
|
let(:email_channel) { create(:channel_email) }
|
|
let(:email_inbox) { create(:inbox, channel: email_channel) }
|
|
let(:conversation) { create(:conversation, inbox: email_inbox) }
|
|
|
|
it 'returns false when auto_reply is not set to true' do
|
|
message = create(
|
|
:message,
|
|
conversation: conversation,
|
|
message_type: :outgoing,
|
|
content_attributes: {}
|
|
)
|
|
expect(message.auto_reply_email?).to be false
|
|
end
|
|
|
|
it 'returns true when auto_reply is set to true' do
|
|
message = create(
|
|
:message,
|
|
conversation: conversation,
|
|
message_type: :outgoing,
|
|
content_attributes: { email: { auto_reply: true } }
|
|
)
|
|
expect(message.auto_reply_email?).to be true
|
|
end
|
|
end
|
|
end
|
|
|
|
describe '#should_index?' do
|
|
let(:account) { create(:account) }
|
|
let(:conversation) { create(:conversation, account: account) }
|
|
let(:message) { create(:message, conversation: conversation, account: account) }
|
|
|
|
before do
|
|
allow(ChatwootApp).to receive(:advanced_search_allowed?).and_return(true)
|
|
account.enable_features('advanced_search')
|
|
end
|
|
|
|
context 'when advanced search is not allowed globally' do
|
|
before do
|
|
allow(ChatwootApp).to receive(:advanced_search_allowed?).and_return(false)
|
|
end
|
|
|
|
it 'returns false' do
|
|
expect(message.should_index?).to be false
|
|
end
|
|
end
|
|
|
|
context 'when advanced search feature is not enabled for account' do
|
|
before do
|
|
account.disable_features('advanced_search')
|
|
end
|
|
|
|
it 'returns false' do
|
|
expect(message.should_index?).to be false
|
|
end
|
|
end
|
|
|
|
context 'when message type is not incoming or outgoing' do
|
|
before do
|
|
message.message_type = 'activity'
|
|
end
|
|
|
|
it 'returns false' do
|
|
expect(message.should_index?).to be false
|
|
end
|
|
end
|
|
|
|
context 'when all conditions are met' do
|
|
it 'returns true for incoming message' do
|
|
message.message_type = 'incoming'
|
|
expect(message.should_index?).to be true
|
|
end
|
|
|
|
it 'returns true for outgoing message' do
|
|
message.message_type = 'outgoing'
|
|
expect(message.should_index?).to be true
|
|
end
|
|
end
|
|
end
|
|
end
|