fix: Send CSAT survey only when agent can reply in conversation (#11637)

This commit is contained in:
Muhsin Keloth
2025-06-11 22:45:32 +05:30
committed by GitHub
parent 459c22054a
commit 7a67799f35
7 changed files with 180 additions and 82 deletions

View File

@@ -13,6 +13,8 @@ describe CsatSurveyListener do
)
end
let!(:event) { Events::Base.new(event_name, Time.zone.now, message: message) }
let!(:csat_enabled_inbox) { create(:inbox, account: account, csat_survey_enabled: true) }
let!(:resolved_conversation) { create(:conversation, account: account, inbox: csat_enabled_inbox, status: :resolved) }
describe '#message_updated' do
let(:event_name) { 'message.updated' }
@@ -33,4 +35,33 @@ describe CsatSurveyListener do
end
end
end
describe '#conversation_status_changed' do
let(:event_name) { 'conversation.status_changed' }
let(:csat_service) { double }
before do
allow(resolved_conversation).to receive(:saved_change_to_status?).and_return(true)
end
context 'when conversation is resolved and CSAT is enabled' do
it 'triggers CSAT survey service' do
event = Events::Base.new(event_name, Time.zone.now, conversation: resolved_conversation)
expect(csat_service).to receive(:perform)
expect(CsatSurveyService).to receive(:new).with(conversation: resolved_conversation).and_return(csat_service)
listener.conversation_status_changed(event)
end
end
context 'when conversation is not resolved' do
it 'does not trigger CSAT survey service' do
open_conversation = create(:conversation, account: account, inbox: csat_enabled_inbox, status: :open)
event = Events::Base.new(event_name, Time.zone.now, conversation: open_conversation)
expect(CsatSurveyService).not_to receive(:new)
listener.conversation_status_changed(event)
end
end
end
end

View File

@@ -0,0 +1,91 @@
require 'rails_helper'
describe CsatSurveyService do
let(:account) { create(:account) }
let(:inbox) { create(:inbox, account: account, csat_survey_enabled: true) }
let(:conversation) { create(:conversation, inbox: inbox, account: account, status: :resolved) }
let(:service) { described_class.new(conversation: conversation) }
describe '#perform' do
let(:csat_template) { instance_double(MessageTemplates::Template::CsatSurvey) }
before do
allow(MessageTemplates::Template::CsatSurvey).to receive(:new).and_return(csat_template)
allow(csat_template).to receive(:perform)
allow(Conversations::ActivityMessageJob).to receive(:perform_later)
end
context 'when CSAT survey should be sent' do
before do
allow(conversation).to receive(:can_reply?).and_return(true)
end
it 'sends CSAT survey when within messaging window' do
service.perform
expect(MessageTemplates::Template::CsatSurvey).to have_received(:new).with(conversation: conversation)
expect(csat_template).to have_received(:perform)
end
end
context 'when outside messaging window' do
before do
allow(conversation).to receive(:can_reply?).and_return(false)
end
it 'creates activity message instead of sending survey' do
service.perform
expect(Conversations::ActivityMessageJob).to have_received(:perform_later).with(
conversation,
hash_including(content: I18n.t('conversations.activity.csat.not_sent_due_to_messaging_window'))
)
expect(MessageTemplates::Template::CsatSurvey).not_to have_received(:new)
end
end
context 'when CSAT survey should not be sent' do
it 'does nothing when conversation is not resolved' do
conversation.update(status: :open)
service.perform
expect(MessageTemplates::Template::CsatSurvey).not_to have_received(:new)
expect(Conversations::ActivityMessageJob).not_to have_received(:perform_later)
end
it 'does nothing when CSAT survey is not enabled' do
inbox.update(csat_survey_enabled: false)
service.perform
expect(MessageTemplates::Template::CsatSurvey).not_to have_received(:new)
expect(Conversations::ActivityMessageJob).not_to have_received(:perform_later)
end
it 'does nothing when CSAT already sent' do
create(:message, conversation: conversation, content_type: :input_csat)
service.perform
expect(MessageTemplates::Template::CsatSurvey).not_to have_received(:new)
expect(Conversations::ActivityMessageJob).not_to have_received(:perform_later)
end
it 'does nothing for Twitter conversations' do
twitter_channel = create(:channel_twitter_profile)
twitter_inbox = create(:inbox, channel: twitter_channel, csat_survey_enabled: true)
twitter_conversation = create(:conversation,
inbox: twitter_inbox,
status: :resolved,
additional_attributes: { type: 'tweet' })
twitter_service = described_class.new(conversation: twitter_conversation)
twitter_service.perform
expect(MessageTemplates::Template::CsatSurvey).not_to have_received(:new)
expect(Conversations::ActivityMessageJob).not_to have_received(:perform_later)
end
end
end
end

View File

@@ -111,69 +111,6 @@ describe MessageTemplates::HookExecutionService do
end
end
context 'when CSAT Survey' do
let(:csat_survey) { double }
let(:conversation) { create(:conversation) }
before do
allow(MessageTemplates::Template::CsatSurvey).to receive(:new).and_return(csat_survey)
allow(csat_survey).to receive(:perform).and_return(true)
create(:message, conversation: conversation, message_type: 'incoming')
end
it 'calls ::MessageTemplates::Template::CsatSurvey when a conversation is resolved in an inbox with survey enabled' do
conversation.inbox.update(csat_survey_enabled: true)
conversation.resolved!
Conversations::ActivityMessageJob.perform_now(conversation,
{ account_id: conversation.account_id, inbox_id: conversation.inbox_id, message_type: :activity,
content: 'Conversation marked resolved!!' })
expect(MessageTemplates::Template::CsatSurvey).to have_received(:new).with(conversation: conversation)
expect(csat_survey).to have_received(:perform)
end
it 'will not call ::MessageTemplates::Template::CsatSurvey when Csat is not enabled' do
conversation.inbox.update(csat_survey_enabled: false)
conversation.resolved!
Conversations::ActivityMessageJob.perform_now(conversation,
{ account_id: conversation.account_id, inbox_id: conversation.inbox_id, message_type: :activity,
content: 'Conversation marked resolved!!' })
expect(MessageTemplates::Template::CsatSurvey).not_to have_received(:new).with(conversation: conversation)
expect(csat_survey).not_to have_received(:perform)
end
it 'will not call ::MessageTemplates::Template::CsatSurvey if its a tweet conversation' do
twitter_channel = create(:channel_twitter_profile)
twitter_inbox = create(:inbox, channel: twitter_channel)
conversation = create(:conversation, inbox: twitter_inbox, additional_attributes: { type: 'tweet' })
conversation.inbox.update(csat_survey_enabled: true)
conversation.resolved!
Conversations::ActivityMessageJob.perform_now(conversation,
{ account_id: conversation.account_id, inbox_id: conversation.inbox_id, message_type: :activity,
content: 'Conversation marked resolved!!' })
expect(MessageTemplates::Template::CsatSurvey).not_to have_received(:new).with(conversation: conversation)
expect(csat_survey).not_to have_received(:perform)
end
it 'will not call ::MessageTemplates::Template::CsatSurvey if another Csat was already sent' do
conversation.inbox.update(csat_survey_enabled: true)
conversation.messages.create!(message_type: 'outgoing', content_type: :input_csat, account: conversation.account, inbox: conversation.inbox)
conversation.resolved!
Conversations::ActivityMessageJob.perform_now(conversation,
{ account_id: conversation.account_id, inbox_id: conversation.inbox_id, message_type: :activity,
content: 'Conversation marked resolved!!' })
expect(MessageTemplates::Template::CsatSurvey).not_to have_received(:new).with(conversation: conversation)
expect(csat_survey).not_to have_received(:perform)
end
end
context 'when it is after working hours' do
it 'calls ::MessageTemplates::Template::OutOfOffice' do
contact = create(:contact)