Merge remote-tracking branch 'origin/feature/stripe_v2' into feature/stripe_v2
This commit is contained in:
@@ -1,6 +1,8 @@
|
||||
require 'rails_helper'
|
||||
|
||||
describe Webhooks::Trigger do
|
||||
include ActiveJob::TestHelper
|
||||
|
||||
subject(:trigger) { described_class }
|
||||
|
||||
let!(:account) { create(:account) }
|
||||
@@ -8,8 +10,18 @@ describe Webhooks::Trigger do
|
||||
let!(:conversation) { create(:conversation, inbox: inbox) }
|
||||
let!(:message) { create(:message, account: account, inbox: inbox, conversation: conversation) }
|
||||
|
||||
let!(:webhook_type) { :api_inbox_webhook }
|
||||
let(:webhook_type) { :api_inbox_webhook }
|
||||
let!(:url) { 'https://test.com' }
|
||||
let(:agent_bot_error_content) { I18n.t('conversations.activity.agent_bot.error_moved_to_open') }
|
||||
|
||||
before do
|
||||
ActiveJob::Base.queue_adapter = :test
|
||||
end
|
||||
|
||||
after do
|
||||
clear_enqueued_jobs
|
||||
clear_performed_jobs
|
||||
end
|
||||
|
||||
describe '#execute' do
|
||||
it 'triggers webhook' do
|
||||
@@ -54,6 +66,57 @@ describe Webhooks::Trigger do
|
||||
).and_raise(RestClient::ExceptionWithResponse.new('error', 500)).once
|
||||
expect { trigger.execute(url, payload, webhook_type) }.to change { message.reload.status }.from('sent').to('failed')
|
||||
end
|
||||
|
||||
context 'when webhook type is agent bot' do
|
||||
let(:webhook_type) { :agent_bot_webhook }
|
||||
|
||||
it 'reopens conversation and enqueues activity message if pending' do
|
||||
conversation.update(status: :pending)
|
||||
payload = { event: 'message_created', conversation: { id: conversation.id }, id: message.id }
|
||||
|
||||
expect(RestClient::Request).to receive(:execute)
|
||||
.with(
|
||||
method: :post,
|
||||
url: url,
|
||||
payload: payload.to_json,
|
||||
headers: { content_type: :json, accept: :json },
|
||||
timeout: 5
|
||||
).and_raise(RestClient::ExceptionWithResponse.new('error', 500)).once
|
||||
|
||||
expect do
|
||||
perform_enqueued_jobs do
|
||||
trigger.execute(url, payload, webhook_type)
|
||||
end
|
||||
end.not_to(change { message.reload.status })
|
||||
|
||||
expect(conversation.reload.status).to eq('open')
|
||||
|
||||
activity_message = conversation.reload.messages.order(:created_at).last
|
||||
expect(activity_message.message_type).to eq('activity')
|
||||
expect(activity_message.content).to eq(agent_bot_error_content)
|
||||
end
|
||||
|
||||
it 'does not change message status or enqueue activity when conversation is not pending' do
|
||||
payload = { event: 'message_created', conversation: { id: conversation.id }, id: message.id }
|
||||
|
||||
expect(RestClient::Request).to receive(:execute)
|
||||
.with(
|
||||
method: :post,
|
||||
url: url,
|
||||
payload: payload.to_json,
|
||||
headers: { content_type: :json, accept: :json },
|
||||
timeout: 5
|
||||
).and_raise(RestClient::ExceptionWithResponse.new('error', 500)).once
|
||||
|
||||
expect do
|
||||
trigger.execute(url, payload, webhook_type)
|
||||
end.not_to(change { message.reload.status })
|
||||
|
||||
expect(Conversations::ActivityMessageJob).not_to have_been_enqueued
|
||||
|
||||
expect(conversation.reload.status).to eq('open')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
it 'does not update message status if webhook fails for other events' do
|
||||
|
||||
@@ -0,0 +1,113 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe ApplicationRecord do
|
||||
it_behaves_like 'encrypted external credential',
|
||||
factory: :channel_email,
|
||||
attribute: :smtp_password,
|
||||
value: 'smtp-secret'
|
||||
|
||||
it_behaves_like 'encrypted external credential',
|
||||
factory: :channel_email,
|
||||
attribute: :imap_password,
|
||||
value: 'imap-secret'
|
||||
|
||||
it_behaves_like 'encrypted external credential',
|
||||
factory: :channel_twilio_sms,
|
||||
attribute: :auth_token,
|
||||
value: 'twilio-secret'
|
||||
|
||||
it_behaves_like 'encrypted external credential',
|
||||
factory: :integrations_hook,
|
||||
attribute: :access_token,
|
||||
value: 'hook-secret'
|
||||
|
||||
it_behaves_like 'encrypted external credential',
|
||||
factory: :channel_facebook_page,
|
||||
attribute: :page_access_token,
|
||||
value: 'fb-page-secret'
|
||||
|
||||
it_behaves_like 'encrypted external credential',
|
||||
factory: :channel_facebook_page,
|
||||
attribute: :user_access_token,
|
||||
value: 'fb-user-secret'
|
||||
|
||||
it_behaves_like 'encrypted external credential',
|
||||
factory: :channel_instagram,
|
||||
attribute: :access_token,
|
||||
value: 'ig-secret'
|
||||
|
||||
it_behaves_like 'encrypted external credential',
|
||||
factory: :channel_line,
|
||||
attribute: :line_channel_secret,
|
||||
value: 'line-secret'
|
||||
|
||||
it_behaves_like 'encrypted external credential',
|
||||
factory: :channel_line,
|
||||
attribute: :line_channel_token,
|
||||
value: 'line-token-secret'
|
||||
|
||||
it_behaves_like 'encrypted external credential',
|
||||
factory: :channel_telegram,
|
||||
attribute: :bot_token,
|
||||
value: 'telegram-secret'
|
||||
|
||||
it_behaves_like 'encrypted external credential',
|
||||
factory: :channel_twitter_profile,
|
||||
attribute: :twitter_access_token,
|
||||
value: 'twitter-access-secret'
|
||||
|
||||
it_behaves_like 'encrypted external credential',
|
||||
factory: :channel_twitter_profile,
|
||||
attribute: :twitter_access_token_secret,
|
||||
value: 'twitter-secret-secret'
|
||||
|
||||
context 'when backfilling legacy plaintext' do
|
||||
before do
|
||||
skip('encryption keys missing; see run_mfa_spec workflow') unless Chatwoot.encryption_configured?
|
||||
end
|
||||
|
||||
it 'reads existing plaintext and encrypts on update' do
|
||||
account = create(:account)
|
||||
channel = create(:channel_email, account: account, smtp_password: nil)
|
||||
|
||||
# Simulate legacy plaintext by updating the DB directly
|
||||
sql = ActiveRecord::Base.send(
|
||||
:sanitize_sql_array,
|
||||
['UPDATE channel_email SET smtp_password = ? WHERE id = ?', 'legacy-plain', channel.id]
|
||||
)
|
||||
ActiveRecord::Base.connection.execute(sql)
|
||||
|
||||
legacy_record = Channel::Email.find(channel.id)
|
||||
expect(legacy_record.smtp_password).to eq('legacy-plain')
|
||||
|
||||
legacy_record.update!(smtp_password: 'encrypted-now')
|
||||
|
||||
stored_value = legacy_record.reload.read_attribute_before_type_cast(:smtp_password)
|
||||
expect(stored_value).to be_present
|
||||
expect(stored_value).not_to include('encrypted-now')
|
||||
expect(legacy_record.smtp_password).to eq('encrypted-now')
|
||||
end
|
||||
end
|
||||
|
||||
context 'when looking up telegram legacy records' do
|
||||
before do
|
||||
skip('encryption keys missing; see run_mfa_spec workflow') unless Chatwoot.encryption_configured?
|
||||
end
|
||||
|
||||
it 'finds plaintext records via fallback lookup' do
|
||||
channel = create(:channel_telegram, bot_token: 'legacy-token')
|
||||
|
||||
# Simulate legacy plaintext by updating the DB directly
|
||||
sql = ActiveRecord::Base.send(
|
||||
:sanitize_sql_array,
|
||||
['UPDATE channel_telegram SET bot_token = ? WHERE id = ?', 'legacy-token', channel.id]
|
||||
)
|
||||
ActiveRecord::Base.connection.execute(sql)
|
||||
|
||||
found = Channel::Telegram.find_by(bot_token: 'legacy-token')
|
||||
expect(found).to eq(channel)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -341,6 +341,58 @@ describe Whatsapp::IncomingMessageService do
|
||||
end
|
||||
end
|
||||
|
||||
describe 'When the incoming waid is an Argentine number with 9 after country code' do
|
||||
let(:wa_id) { '5491123456789' }
|
||||
|
||||
it 'creates appropriate conversations, message and contacts if contact does not exist' do
|
||||
described_class.new(inbox: whatsapp_channel.inbox, params: params).perform
|
||||
expect(whatsapp_channel.inbox.conversations.count).not_to eq(0)
|
||||
expect(Contact.all.first.name).to eq('Sojan Jose')
|
||||
expect(whatsapp_channel.inbox.messages.first.content).to eq('Test')
|
||||
expect(whatsapp_channel.inbox.contact_inboxes.first.source_id).to eq(wa_id)
|
||||
end
|
||||
|
||||
it 'appends to existing contact if contact inbox exists with normalized format' do
|
||||
# Normalized format removes the 9 after country code
|
||||
normalized_wa_id = '541123456789'
|
||||
contact_inbox = create(:contact_inbox, inbox: whatsapp_channel.inbox, source_id: normalized_wa_id)
|
||||
last_conversation = create(:conversation, inbox: whatsapp_channel.inbox, contact_inbox: contact_inbox)
|
||||
described_class.new(inbox: whatsapp_channel.inbox, params: params).perform
|
||||
# no new conversation should be created
|
||||
expect(whatsapp_channel.inbox.conversations.count).to eq(1)
|
||||
# message appended to the last conversation
|
||||
expect(last_conversation.messages.last.content).to eq(params[:messages].first[:text][:body])
|
||||
# should use the normalized wa_id from existing contact
|
||||
expect(whatsapp_channel.inbox.contact_inboxes.first.source_id).to eq(normalized_wa_id)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'When incoming waid is an Argentine number without 9 after country code' do
|
||||
let(:wa_id) { '541123456789' }
|
||||
|
||||
context 'when a contact inbox exists with the same format' do
|
||||
it 'appends to existing contact' do
|
||||
contact_inbox = create(:contact_inbox, inbox: whatsapp_channel.inbox, source_id: wa_id)
|
||||
last_conversation = create(:conversation, inbox: whatsapp_channel.inbox, contact_inbox: contact_inbox)
|
||||
described_class.new(inbox: whatsapp_channel.inbox, params: params).perform
|
||||
# no new conversation should be created
|
||||
expect(whatsapp_channel.inbox.conversations.count).to eq(1)
|
||||
# message appended to the last conversation
|
||||
expect(last_conversation.messages.last.content).to eq(params[:messages].first[:text][:body])
|
||||
end
|
||||
end
|
||||
|
||||
context 'when a contact inbox does not exist' do
|
||||
it 'creates contact inbox with the incoming waid' do
|
||||
described_class.new(inbox: whatsapp_channel.inbox, params: params).perform
|
||||
expect(whatsapp_channel.inbox.conversations.count).not_to eq(0)
|
||||
expect(Contact.all.first.name).to eq('Sojan Jose')
|
||||
expect(whatsapp_channel.inbox.messages.first.content).to eq('Test')
|
||||
expect(whatsapp_channel.inbox.contact_inboxes.first.source_id).to eq(wa_id)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'when message processing is in progress' do
|
||||
it 'ignores the current message creation request' do
|
||||
params = { 'contacts' => [{ 'profile' => { 'name' => 'Kedar' }, 'wa_id' => '919746334593' }],
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
RSpec.shared_examples 'encrypted external credential' do |factory:, attribute:, value: 'secret-token'|
|
||||
before do
|
||||
skip('encryption keys missing; see run_mfa_spec workflow') unless Chatwoot.encryption_configured?
|
||||
if defined?(Facebook::Messenger::Subscriptions)
|
||||
allow(Facebook::Messenger::Subscriptions).to receive(:subscribe).and_return(true)
|
||||
allow(Facebook::Messenger::Subscriptions).to receive(:unsubscribe).and_return(true)
|
||||
end
|
||||
end
|
||||
|
||||
it "encrypts #{attribute} at rest" do
|
||||
record = create(factory, attribute => value)
|
||||
|
||||
raw_stored_value = record.reload.read_attribute_before_type_cast(attribute).to_s
|
||||
expect(raw_stored_value).to be_present
|
||||
expect(raw_stored_value).not_to include(value)
|
||||
expect(record.public_send(attribute)).to eq(value)
|
||||
expect(record.encrypted_attribute?(attribute)).to be(true)
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user