Files
chatwoot/spec/lib/captain/reply_suggestion_service_spec.rb
Sony MathewandGitHub 66cfb26c77 feat: Add unread count filters feature flag (1/6) (#14885)
## Description

Adds the account-level `unread_count_for_filters` feature flag as the
dark-launch gate for filtered sidebar unread counts. This reuses the
deprecated `quoted_email_reply` flag slot, resets the reused bit for
existing accounts, and removes stale defaults so new accounts do not
reference the old flag.

This also adds the feature where we are now calculating the unread counts for built in filters like mentions, participating and unattended along with unread count for saved filters/folders.

Closes
[CW-7262](https://linear.app/chatwoot/issue/CW-7262/unread-counts-for-filters-folders)

## Type of change

- [ ] Bug fix (non-breaking change which fixes an issue)
- [x] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing
functionality not to work as expected)
- [ ] This change requires a documentation update
2026-07-08 23:50:40 +05:30

102 lines
4.0 KiB
Ruby

require 'rails_helper'
RSpec.describe Captain::ReplySuggestionService do
subject(:service) { described_class.new(account: account, conversation_display_id: conversation.display_id, user: agent) }
let(:account) { create(:account) }
let(:agent) { create(:user, account: account, name: 'Jane Smith') }
let(:inbox) { create(:inbox, account: account) }
let(:conversation) { create(:conversation, account: account, inbox: inbox) }
let(:captured_messages) { [] }
before do
create(:installation_config, name: 'CAPTAIN_OPEN_AI_API_KEY', value: 'test-key')
create(:message, conversation: conversation, message_type: :incoming, content: 'I need help')
allow(account).to receive(:feature_enabled?).and_call_original
allow(account).to receive(:feature_enabled?).with('captain_tasks').and_return(true)
mock_response = instance_double(RubyLLM::Message, content: 'Sure, I can help!', input_tokens: 50, output_tokens: 20)
mock_chat = instance_double(RubyLLM::Chat)
mock_context = instance_double(RubyLLM::Context, chat: mock_chat)
allow(Llm::Config).to receive(:with_api_key).and_yield(mock_context)
allow(mock_chat).to receive(:with_tool).and_return(mock_chat)
allow(mock_chat).to receive(:on_end_message).and_return(mock_chat)
allow(mock_chat).to receive(:with_instructions) { |msg| captured_messages << { role: 'system', content: msg } }
allow(mock_chat).to receive(:add_message) { |args| captured_messages << args }
allow(mock_chat).to receive(:ask) do |msg|
captured_messages << { role: 'user', content: msg }
mock_response
end
end
describe '#perform' do
it 'routes through the editor feature' do
expect(Llm::FeatureRouter).to receive(:resolve).with(feature: 'editor', account: account).and_call_original
service.perform
end
it 'returns the suggested reply' do
result = service.perform
expect(result[:message]).to eq('Sure, I can help!')
end
it 'formats conversation using LlmFormatter' do
service.perform
user_message = captured_messages.find { |m| m[:role] == 'user' }
expect(user_message[:content]).to include('Message History:')
expect(user_message[:content]).to include('User: I need help')
end
context 'with chat channel' do
it 'uses chat-specific instructions' do
service.perform
system_prompt = captured_messages.find { |m| m[:role] == 'system' }[:content]
expect(system_prompt).to include('CHAT conversation')
expect(system_prompt).to include('brief, conversational')
expect(system_prompt).not_to include('EMAIL conversation')
end
end
context 'with email channel' do
let(:email_channel) { create(:channel_email, account: account) }
let(:inbox) { create(:inbox, account: account, channel: email_channel) }
it 'uses email-specific instructions' do
service.perform
system_prompt = captured_messages.find { |m| m[:role] == 'system' }[:content]
expect(system_prompt).to include('EMAIL conversation')
expect(system_prompt).to include('professional email')
expect(system_prompt).not_to include('CHAT conversation')
end
context 'when agent has a signature' do
let(:agent) { create(:user, account: account, name: 'Jane Smith', message_signature: "Best,\nJane Smith") }
it 'includes the signature in the prompt' do
service.perform
system_prompt = captured_messages.find { |m| m[:role] == 'system' }[:content]
expect(system_prompt).to include("Best,\nJane Smith")
end
end
context 'when agent has no signature' do
let(:agent) { create(:user, account: account, name: 'Jane Smith', message_signature: nil) }
it 'falls back to agent name for sign-off' do
service.perform
system_prompt = captured_messages.find { |m| m[:role] == 'system' }[:content]
expect(system_prompt).to include("sign-off using the agent's name: Jane Smith")
end
end
end
end
end