fix: resolve mutex conflicts in Instagram webhook specs (#12154)

This PR fixes flaky test failures in the Instagram webhook specs that
were caused by Redis mutex lock conflicts when
   tests ran in parallel.

 ### The Problem:
The InstagramEventsJob uses a Redis mutex with a key based on sender_id
and ig_account_id to prevent race
conditions. However, all test factories were using the same hardcoded
sender_id: 'Sender-id-1', causing multiple
test instances to compete for the same mutex lock when running in
parallel.

 ### The Solution:
- Updated all Instagram event factories to generate unique sender IDs
using SecureRandom.hex(4)
- Modified test stubs and expectations to work with dynamic sender IDs
instead of hardcoded values
- Ensured each test instance gets its own unique mutex key, eliminating
lock contention
This commit is contained in:
Sojan Jose
2025-08-11 20:01:25 +02:00
committed by GitHub
parent 28452b300d
commit c31325e982
6 changed files with 252 additions and 147 deletions

View File

@@ -17,40 +17,34 @@ describe Messages::Instagram::MessageBuilder do
let!(:shared_reel_params) { build(:instagram_shared_reel_event).with_indifferent_access } let!(:shared_reel_params) { build(:instagram_shared_reel_event).with_indifferent_access }
let!(:instagram_story_reply_event) { build(:instagram_story_reply_event).with_indifferent_access } let!(:instagram_story_reply_event) { build(:instagram_story_reply_event).with_indifferent_access }
let!(:instagram_message_reply_event) { build(:instagram_message_reply_event).with_indifferent_access } let!(:instagram_message_reply_event) { build(:instagram_message_reply_event).with_indifferent_access }
let!(:contact) { create(:contact, id: 'Sender-id-1', name: 'Jane Dae') }
let!(:contact_inbox) { create(:contact_inbox, contact_id: contact.id, inbox_id: instagram_inbox.id, source_id: 'Sender-id-1') }
let(:conversation) do
create(:conversation, account_id: account.id, inbox_id: instagram_inbox.id, contact_id: contact.id)
end
let(:message) do
create(:message, account_id: account.id, inbox_id: instagram_inbox.id, conversation_id: conversation.id, message_type: 'outgoing',
source_id: 'message-id-1')
end
describe '#perform' do describe '#perform' do
before do before do
instagram_channel.update(access_token: 'valid_instagram_token') instagram_channel.update(access_token: 'valid_instagram_token')
stub_request(:get, %r{https://graph\.instagram\.com/.*?/Sender-id-1\?.*}) stub_request(:get, %r{https://graph\.instagram\.com/.*?/Sender-id-.*?\?.*})
.to_return( .to_return(
status: 200, status: 200,
body: { body: proc { |request|
name: 'Jane', sender_id = request.uri.path.split('/').last.split('?').first
username: 'some_user_name', {
profile_pic: 'https://chatwoot-assets.local/sample.png', name: 'Jane',
id: 'Sender-id-1', username: 'some_user_name',
follower_count: 100, profile_pic: 'https://chatwoot-assets.local/sample.png',
is_user_follow_business: true, id: sender_id,
is_business_follow_user: true, follower_count: 100,
is_verified_user: false is_user_follow_business: true,
}.to_json, is_business_follow_user: true,
is_verified_user: false
}.to_json
},
headers: { 'Content-Type' => 'application/json' } headers: { 'Content-Type' => 'application/json' }
) )
end end
it 'creates contact and message for the instagram direct inbox' do it 'creates contact and message for the instagram direct inbox' do
messaging = dm_params[:entry][0]['messaging'][0] messaging = dm_params[:entry][0]['messaging'][0]
contact_inbox create_instagram_contact_for_sender(messaging['sender']['id'], instagram_inbox)
described_class.new(messaging, instagram_inbox).perform described_class.new(messaging, instagram_inbox).perform
instagram_inbox.reload instagram_inbox.reload
@@ -63,13 +57,15 @@ describe Messages::Instagram::MessageBuilder do
end end
it 'discard echo message already sent by chatwoot' do it 'discard echo message already sent by chatwoot' do
conversation messaging = dm_params[:entry][0]['messaging'][0]
message contact = create_instagram_contact_for_sender(messaging['sender']['id'], instagram_inbox)
conversation = create(:conversation, account_id: account.id, inbox_id: instagram_inbox.id, contact_id: contact.id)
create(:message, account_id: account.id, inbox_id: instagram_inbox.id, conversation_id: conversation.id, message_type: 'outgoing',
source_id: 'message-id-1')
expect(instagram_inbox.conversations.count).to be 1 expect(instagram_inbox.conversations.count).to be 1
expect(instagram_inbox.messages.count).to be 1 expect(instagram_inbox.messages.count).to be 1
messaging = dm_params[:entry][0]['messaging'][0]
messaging[:message][:mid] = 'message-id-1' # Set same source_id as the existing message messaging[:message][:mid] = 'message-id-1' # Set same source_id as the existing message
described_class.new(messaging, instagram_inbox, outgoing_echo: true).perform described_class.new(messaging, instagram_inbox, outgoing_echo: true).perform
@@ -81,6 +77,7 @@ describe Messages::Instagram::MessageBuilder do
it 'discards duplicate messages from webhook events with the same message_id' do it 'discards duplicate messages from webhook events with the same message_id' do
messaging = dm_params[:entry][0]['messaging'][0] messaging = dm_params[:entry][0]['messaging'][0]
create_instagram_contact_for_sender(messaging['sender']['id'], instagram_inbox)
described_class.new(messaging, instagram_inbox).perform described_class.new(messaging, instagram_inbox).perform
initial_message_count = instagram_inbox.messages.count initial_message_count = instagram_inbox.messages.count
@@ -93,6 +90,7 @@ describe Messages::Instagram::MessageBuilder do
it 'creates message for shared reel' do it 'creates message for shared reel' do
messaging = shared_reel_params[:entry][0]['messaging'][0] messaging = shared_reel_params[:entry][0]['messaging'][0]
create_instagram_contact_for_sender(messaging['sender']['id'], instagram_inbox)
described_class.new(messaging, instagram_inbox).perform described_class.new(messaging, instagram_inbox).perform
message = instagram_inbox.messages.first message = instagram_inbox.messages.first
@@ -103,7 +101,9 @@ describe Messages::Instagram::MessageBuilder do
end end
it 'creates message with story id' do it 'creates message with story id' do
story_source_id = instagram_story_reply_event[:entry][0]['messaging'][0]['message']['mid'] messaging = instagram_story_reply_event[:entry][0]['messaging'][0]
create_instagram_contact_for_sender(messaging['sender']['id'], instagram_inbox)
story_source_id = messaging['message']['mid']
stub_request(:get, %r{https://graph\.instagram\.com/.*?/#{story_source_id}\?.*}) stub_request(:get, %r{https://graph\.instagram\.com/.*?/#{story_source_id}\?.*})
.to_return( .to_return(
@@ -121,7 +121,6 @@ describe Messages::Instagram::MessageBuilder do
headers: { 'Content-Type' => 'application/json' } headers: { 'Content-Type' => 'application/json' }
) )
messaging = instagram_story_reply_event[:entry][0]['messaging'][0]
described_class.new(messaging, instagram_inbox).perform described_class.new(messaging, instagram_inbox).perform
message = instagram_inbox.messages.first message = instagram_inbox.messages.first
@@ -134,10 +133,13 @@ describe Messages::Instagram::MessageBuilder do
it 'creates message with reply to mid' do it 'creates message with reply to mid' do
# Create first message to ensure reply to is valid # Create first message to ensure reply to is valid
first_messaging = dm_params[:entry][0]['messaging'][0] first_messaging = dm_params[:entry][0]['messaging'][0]
sender_id = first_messaging['sender']['id']
create_instagram_contact_for_sender(sender_id, instagram_inbox)
described_class.new(first_messaging, instagram_inbox).perform described_class.new(first_messaging, instagram_inbox).perform
# Create second message with reply to mid # Create second message with reply to mid, using same sender_id
messaging = instagram_message_reply_event[:entry][0]['messaging'][0] messaging = instagram_message_reply_event[:entry][0]['messaging'][0]
messaging['sender']['id'] = sender_id
described_class.new(messaging, instagram_inbox).perform described_class.new(messaging, instagram_inbox).perform
first_message = instagram_inbox.messages.first first_message = instagram_inbox.messages.first
@@ -148,12 +150,13 @@ describe Messages::Instagram::MessageBuilder do
end end
it 'handles deleted story' do it 'handles deleted story' do
story_source_id = story_mention_params[:entry][0][:messaging][0]['message']['mid'] messaging = story_mention_params[:entry][0][:messaging][0]
create_instagram_contact_for_sender(messaging['sender']['id'], instagram_inbox)
story_source_id = messaging['message']['mid']
stub_request(:get, %r{https://graph\.instagram\.com/.*?/#{story_source_id}\?.*}) stub_request(:get, %r{https://graph\.instagram\.com/.*?/#{story_source_id}\?.*})
.to_return(status: 404, body: { error: { message: 'Story not found', code: 1_609_005 } }.to_json) .to_return(status: 404, body: { error: { message: 'Story not found', code: 1_609_005 } }.to_json)
messaging = story_mention_params[:entry][0][:messaging][0]
described_class.new(messaging, instagram_inbox).perform described_class.new(messaging, instagram_inbox).perform
message = instagram_inbox.messages.first message = instagram_inbox.messages.first
@@ -163,11 +166,12 @@ describe Messages::Instagram::MessageBuilder do
end end
it 'does not create message for unsupported file type' do it 'does not create message for unsupported file type' do
conversation messaging = story_mention_params[:entry][0][:messaging][0]
contact = create_instagram_contact_for_sender(messaging['sender']['id'], instagram_inbox)
create(:conversation, account_id: account.id, inbox_id: instagram_inbox.id, contact_id: contact.id)
# try to create a message with unsupported file type # try to create a message with unsupported file type
story_mention_params[:entry][0][:messaging][0]['message']['attachments'][0]['type'] = 'unsupported_type' messaging['message']['attachments'][0]['type'] = 'unsupported_type'
messaging = story_mention_params[:entry][0][:messaging][0]
described_class.new(messaging, instagram_inbox, outgoing_echo: false).perform described_class.new(messaging, instagram_inbox, outgoing_echo: false).perform
@@ -177,7 +181,11 @@ describe Messages::Instagram::MessageBuilder do
end end
it 'does not create message if the message is already exists' do it 'does not create message if the message is already exists' do
message messaging = dm_params[:entry][0]['messaging'][0]
contact = create_instagram_contact_for_sender(messaging['sender']['id'], instagram_inbox)
conversation = create(:conversation, account_id: account.id, inbox_id: instagram_inbox.id, contact_id: contact.id)
create(:message, account_id: account.id, inbox_id: instagram_inbox.id, conversation_id: conversation.id, message_type: 'outgoing',
source_id: 'message-id-1')
expect(instagram_inbox.conversations.count).to be 1 expect(instagram_inbox.conversations.count).to be 1
expect(instagram_inbox.messages.count).to be 1 expect(instagram_inbox.messages.count).to be 1
@@ -194,7 +202,7 @@ describe Messages::Instagram::MessageBuilder do
instagram_channel.update(access_token: 'invalid_token') instagram_channel.update(access_token: 'invalid_token')
# Stub the request to return authorization error status # Stub the request to return authorization error status
stub_request(:get, %r{https://graph\.instagram\.com/.*?/Sender-id-1\?.*}) stub_request(:get, %r{https://graph\.instagram\.com/.*?/Sender-id-.*?\?.*})
.to_return( .to_return(
status: 401, status: 401,
body: { error: { message: 'unauthorized access token', code: 190 } }.to_json, body: { error: { message: 'unauthorized access token', code: 190 } }.to_json,
@@ -218,6 +226,7 @@ describe Messages::Instagram::MessageBuilder do
it 'creates a new conversation if existing conversation is not present' do it 'creates a new conversation if existing conversation is not present' do
initial_count = Conversation.count initial_count = Conversation.count
messaging = dm_params[:entry][0]['messaging'][0] messaging = dm_params[:entry][0]['messaging'][0]
create_instagram_contact_for_sender(messaging['sender']['id'], instagram_inbox)
described_class.new(messaging, instagram_inbox).perform described_class.new(messaging, instagram_inbox).perform
@@ -226,21 +235,23 @@ describe Messages::Instagram::MessageBuilder do
end end
it 'will not create a new conversation if last conversation is not resolved' do it 'will not create a new conversation if last conversation is not resolved' do
messaging = dm_params[:entry][0]['messaging'][0]
contact = create_instagram_contact_for_sender(messaging['sender']['id'], instagram_inbox)
existing_conversation = create(:conversation, account_id: account.id, inbox_id: instagram_inbox.id, existing_conversation = create(:conversation, account_id: account.id, inbox_id: instagram_inbox.id,
contact_id: contact.id, status: :open) contact_id: contact.id, status: :open)
messaging = dm_params[:entry][0]['messaging'][0]
described_class.new(messaging, instagram_inbox).perform described_class.new(messaging, instagram_inbox).perform
expect(instagram_inbox.conversations.last.id).to eq(existing_conversation.id) expect(instagram_inbox.conversations.last.id).to eq(existing_conversation.id)
end end
it 'creates a new conversation if last conversation is resolved' do it 'creates a new conversation if last conversation is resolved' do
messaging = dm_params[:entry][0]['messaging'][0]
contact = create_instagram_contact_for_sender(messaging['sender']['id'], instagram_inbox)
existing_conversation = create(:conversation, account_id: account.id, inbox_id: instagram_inbox.id, existing_conversation = create(:conversation, account_id: account.id, inbox_id: instagram_inbox.id,
contact_id: contact.id, status: :resolved) contact_id: contact.id, status: :resolved)
initial_count = Conversation.count initial_count = Conversation.count
messaging = dm_params[:entry][0]['messaging'][0]
described_class.new(messaging, instagram_inbox).perform described_class.new(messaging, instagram_inbox).perform
@@ -257,6 +268,7 @@ describe Messages::Instagram::MessageBuilder do
it 'creates a new conversation if existing conversation is not present' do it 'creates a new conversation if existing conversation is not present' do
initial_count = Conversation.count initial_count = Conversation.count
messaging = dm_params[:entry][0]['messaging'][0] messaging = dm_params[:entry][0]['messaging'][0]
create_instagram_contact_for_sender(messaging['sender']['id'], instagram_inbox)
described_class.new(messaging, instagram_inbox).perform described_class.new(messaging, instagram_inbox).perform
@@ -265,6 +277,8 @@ describe Messages::Instagram::MessageBuilder do
end end
it 'reopens last conversation if last conversation is resolved' do it 'reopens last conversation if last conversation is resolved' do
messaging = dm_params[:entry][0]['messaging'][0]
contact = create_instagram_contact_for_sender(messaging['sender']['id'], instagram_inbox)
existing_conversation = create(:conversation, account_id: account.id, inbox_id: instagram_inbox.id, existing_conversation = create(:conversation, account_id: account.id, inbox_id: instagram_inbox.id,
contact_id: contact.id, status: :resolved) contact_id: contact.id, status: :resolved)
@@ -307,6 +321,7 @@ describe Messages::Instagram::MessageBuilder do
it 'saves story information when story mention is processed' do it 'saves story information when story mention is processed' do
messaging = story_mention_params[:entry][0][:messaging][0] messaging = story_mention_params[:entry][0][:messaging][0]
create_instagram_contact_for_sender(messaging['sender']['id'], instagram_inbox)
described_class.new(messaging, instagram_inbox).perform described_class.new(messaging, instagram_inbox).perform
message = instagram_inbox.messages.first message = instagram_inbox.messages.first
@@ -328,6 +343,7 @@ describe Messages::Instagram::MessageBuilder do
) )
messaging = story_mention_params[:entry][0][:messaging][0] messaging = story_mention_params[:entry][0][:messaging][0]
create_instagram_contact_for_sender(messaging['sender']['id'], instagram_inbox)
described_class.new(messaging, instagram_inbox).perform described_class.new(messaging, instagram_inbox).perform
message = instagram_inbox.messages.first message = instagram_inbox.messages.first

View File

@@ -17,30 +17,23 @@ describe Messages::Instagram::Messenger::MessageBuilder do
let!(:instagram_story_reply_event) { build(:instagram_story_reply_event).with_indifferent_access } let!(:instagram_story_reply_event) { build(:instagram_story_reply_event).with_indifferent_access }
let!(:instagram_message_reply_event) { build(:instagram_message_reply_event).with_indifferent_access } let!(:instagram_message_reply_event) { build(:instagram_message_reply_event).with_indifferent_access }
let(:fb_object) { double } let(:fb_object) { double }
let(:contact) { create(:contact, id: 'Sender-id-1', name: 'Jane Dae') }
let(:contact_inbox) { create(:contact_inbox, contact_id: contact.id, inbox_id: instagram_messenger_inbox.id, source_id: 'Sender-id-1') }
let(:conversation) do
create(:conversation, account_id: account.id, inbox_id: instagram_messenger_inbox.id, contact_id: contact.id,
additional_attributes: { type: 'instagram_direct_message', conversation_language: 'en' })
end
let(:message) do
create(:message, account_id: account.id, inbox_id: instagram_messenger_inbox.id, conversation_id: conversation.id, message_type: 'outgoing',
source_id: 'message-id-1')
end
describe '#perform' do describe '#perform' do
it 'creates contact and message for the facebook inbox' do it 'creates contact and message for the facebook inbox' do
messaging = dm_params[:entry][0]['messaging'][0]
sender_id = messaging['sender']['id']
allow(Koala::Facebook::API).to receive(:new).and_return(fb_object) allow(Koala::Facebook::API).to receive(:new).and_return(fb_object)
allow(fb_object).to receive(:get_object).and_return( allow(fb_object).to receive(:get_object).and_return(
{ {
name: 'Jane', name: 'Jane',
id: 'Sender-id-1', id: sender_id,
account_id: instagram_messenger_inbox.account_id, account_id: instagram_messenger_inbox.account_id,
profile_pic: 'https://chatwoot-assets.local/sample.png' profile_pic: 'https://chatwoot-assets.local/sample.png'
}.with_indifferent_access }.with_indifferent_access
) )
messaging = dm_params[:entry][0]['messaging'][0]
contact_inbox create_instagram_contact_for_sender(sender_id, instagram_messenger_inbox)
described_class.new(messaging, instagram_messenger_inbox).perform described_class.new(messaging, instagram_messenger_inbox).perform
instagram_messenger_inbox.reload instagram_messenger_inbox.reload
@@ -56,7 +49,13 @@ describe Messages::Instagram::Messenger::MessageBuilder do
end end
it 'discard echo message already sent by chatwoot' do it 'discard echo message already sent by chatwoot' do
message messaging = dm_params[:entry][0]['messaging'][0]
sender_id = messaging['sender']['id']
contact = create_instagram_contact_for_sender(sender_id, instagram_messenger_inbox)
conversation = create(:conversation, account_id: account.id, inbox_id: instagram_messenger_inbox.id, contact_id: contact.id,
additional_attributes: { type: 'instagram_direct_message', conversation_language: 'en' })
create(:message, account_id: account.id, inbox_id: instagram_messenger_inbox.id, conversation_id: conversation.id, message_type: 'outgoing',
source_id: 'message-id-1')
expect(instagram_messenger_inbox.conversations.count).to be 1 expect(instagram_messenger_inbox.conversations.count).to be 1
expect(instagram_messenger_inbox.messages.count).to be 1 expect(instagram_messenger_inbox.messages.count).to be 1
@@ -65,13 +64,11 @@ describe Messages::Instagram::Messenger::MessageBuilder do
allow(fb_object).to receive(:get_object).and_return( allow(fb_object).to receive(:get_object).and_return(
{ {
name: 'Jane', name: 'Jane',
id: 'Sender-id-1', id: sender_id,
account_id: instagram_messenger_inbox.account_id, account_id: instagram_messenger_inbox.account_id,
profile_pic: 'https://chatwoot-assets.local/sample.png' profile_pic: 'https://chatwoot-assets.local/sample.png'
}.with_indifferent_access }.with_indifferent_access
) )
messaging = dm_params[:entry][0]['messaging'][0]
contact_inbox
described_class.new(messaging, instagram_messenger_inbox, outgoing_echo: true).perform described_class.new(messaging, instagram_messenger_inbox, outgoing_echo: true).perform
instagram_messenger_inbox.reload instagram_messenger_inbox.reload
@@ -81,17 +78,20 @@ describe Messages::Instagram::Messenger::MessageBuilder do
end end
it 'creates message for shared reel' do it 'creates message for shared reel' do
messaging = shared_reel_params[:entry][0]['messaging'][0]
sender_id = messaging['sender']['id']
allow(Koala::Facebook::API).to receive(:new).and_return(fb_object) allow(Koala::Facebook::API).to receive(:new).and_return(fb_object)
allow(fb_object).to receive(:get_object).and_return( allow(fb_object).to receive(:get_object).and_return(
{ {
name: 'Jane', name: 'Jane',
id: 'Sender-id-1', id: sender_id,
account_id: instagram_messenger_inbox.account_id, account_id: instagram_messenger_inbox.account_id,
profile_pic: 'https://chatwoot-assets.local/sample.png' profile_pic: 'https://chatwoot-assets.local/sample.png'
}.with_indifferent_access }.with_indifferent_access
) )
messaging = shared_reel_params[:entry][0]['messaging'][0]
contact_inbox create_instagram_contact_for_sender(sender_id, instagram_messenger_inbox)
described_class.new(messaging, instagram_messenger_inbox).perform described_class.new(messaging, instagram_messenger_inbox).perform
message = instagram_messenger_channel.inbox.messages.first message = instagram_messenger_channel.inbox.messages.first
@@ -102,18 +102,20 @@ describe Messages::Instagram::Messenger::MessageBuilder do
end end
it 'creates message with for reply with story id' do it 'creates message with for reply with story id' do
messaging = instagram_story_reply_event[:entry][0]['messaging'][0]
sender_id = messaging['sender']['id']
allow(Koala::Facebook::API).to receive(:new).and_return(fb_object) allow(Koala::Facebook::API).to receive(:new).and_return(fb_object)
allow(fb_object).to receive(:get_object).and_return( allow(fb_object).to receive(:get_object).and_return(
{ {
name: 'Jane', name: 'Jane',
id: 'Sender-id-1', id: sender_id,
account_id: instagram_messenger_inbox.account_id, account_id: instagram_messenger_inbox.account_id,
profile_pic: 'https://chatwoot-assets.local/sample.png' profile_pic: 'https://chatwoot-assets.local/sample.png'
}.with_indifferent_access }.with_indifferent_access
) )
messaging = instagram_story_reply_event[:entry][0]['messaging'][0]
contact_inbox
create_instagram_contact_for_sender(sender_id, instagram_messenger_inbox)
described_class.new(messaging, instagram_messenger_inbox).perform described_class.new(messaging, instagram_messenger_inbox).perform
message = instagram_messenger_channel.inbox.messages.first message = instagram_messenger_channel.inbox.messages.first
@@ -125,24 +127,26 @@ describe Messages::Instagram::Messenger::MessageBuilder do
end end
it 'creates message with for reply with mid' do it 'creates message with for reply with mid' do
# create first message to ensure reply to is valid
first_message_data = dm_params[:entry][0]['messaging'][0]
sender_id = first_message_data['sender']['id']
allow(Koala::Facebook::API).to receive(:new).and_return(fb_object) allow(Koala::Facebook::API).to receive(:new).and_return(fb_object)
allow(fb_object).to receive(:get_object).and_return( allow(fb_object).to receive(:get_object).and_return(
{ {
name: 'Jane', name: 'Jane',
id: 'Sender-id-1', id: sender_id,
account_id: instagram_messenger_inbox.account_id, account_id: instagram_messenger_inbox.account_id,
profile_pic: 'https://chatwoot-assets.local/sample.png' profile_pic: 'https://chatwoot-assets.local/sample.png'
}.with_indifferent_access }.with_indifferent_access
) )
# create first message to ensure reply to is valid
first_message = dm_params[:entry][0]['messaging'][0]
contact_inbox
described_class.new(first_message, instagram_messenger_inbox).perform
# create the second message with the reply to mid set create_instagram_contact_for_sender(sender_id, instagram_messenger_inbox)
described_class.new(first_message_data, instagram_messenger_inbox).perform
# create the second message with the reply to mid set, ensure same sender_id
messaging = instagram_message_reply_event[:entry][0]['messaging'][0] messaging = instagram_message_reply_event[:entry][0]['messaging'][0]
contact_inbox messaging['sender']['id'] = sender_id # Use the same sender_id
described_class.new(messaging, instagram_messenger_inbox).perform described_class.new(messaging, instagram_messenger_inbox).perform
first_message = instagram_messenger_channel.inbox.messages.first first_message = instagram_messenger_channel.inbox.messages.first
message = instagram_messenger_channel.inbox.messages.last message = instagram_messenger_channel.inbox.messages.last
@@ -153,14 +157,16 @@ describe Messages::Instagram::Messenger::MessageBuilder do
end end
it 'raises exception on deleted story' do it 'raises exception on deleted story' do
messaging = story_mention_params[:entry][0][:messaging][0]
sender_id = messaging['sender']['id']
allow(Koala::Facebook::API).to receive(:new).and_return(fb_object) allow(Koala::Facebook::API).to receive(:new).and_return(fb_object)
allow(fb_object).to receive(:get_object).and_raise(Koala::Facebook::ClientError.new( allow(fb_object).to receive(:get_object).and_raise(Koala::Facebook::ClientError.new(
190, 190,
'This Message has been deleted by the user or the business.' 'This Message has been deleted by the user or the business.'
)) ))
messaging = story_mention_params[:entry][0][:messaging][0] create_instagram_contact_for_sender(sender_id, instagram_messenger_inbox)
contact_inbox
described_class.new(messaging, instagram_messenger_inbox, outgoing_echo: false).perform described_class.new(messaging, instagram_messenger_inbox, outgoing_echo: false).perform
instagram_messenger_inbox.reload instagram_messenger_inbox.reload
@@ -180,22 +186,24 @@ describe Messages::Instagram::Messenger::MessageBuilder do
end end
it 'does not create message for unsupported file type' do it 'does not create message for unsupported file type' do
# create a message with unsupported file type
story_mention_params[:entry][0][:messaging][0]['message']['attachments'][0]['type'] = 'unsupported_type'
messaging = story_mention_params[:entry][0][:messaging][0]
sender_id = messaging['sender']['id']
allow(Koala::Facebook::API).to receive(:new).and_return(fb_object) allow(Koala::Facebook::API).to receive(:new).and_return(fb_object)
allow(fb_object).to receive(:get_object).and_return( allow(fb_object).to receive(:get_object).and_return(
{ {
name: 'Jane', name: 'Jane',
id: 'Sender-id-1', id: sender_id,
account_id: instagram_messenger_inbox.account_id, account_id: instagram_messenger_inbox.account_id,
profile_pic: 'https://chatwoot-assets.local/sample.png' profile_pic: 'https://chatwoot-assets.local/sample.png'
}.with_indifferent_access }.with_indifferent_access
) )
conversation contact = create_instagram_contact_for_sender(sender_id, instagram_messenger_inbox)
create(:conversation, account_id: account.id, inbox_id: instagram_messenger_inbox.id, contact_id: contact.id,
# create a message with unsupported file type additional_attributes: { type: 'instagram_direct_message', conversation_language: 'en' })
story_mention_params[:entry][0][:messaging][0]['message']['attachments'][0]['type'] = 'unsupported_type'
messaging = story_mention_params[:entry][0][:messaging][0]
described_class.new(messaging, instagram_messenger_inbox, outgoing_echo: false).perform described_class.new(messaging, instagram_messenger_inbox, outgoing_echo: false).perform
instagram_messenger_inbox.reload instagram_messenger_inbox.reload
@@ -218,18 +226,22 @@ describe Messages::Instagram::Messenger::MessageBuilder do
it 'creates a new conversation if existing conversation is not present' do it 'creates a new conversation if existing conversation is not present' do
inital_count = Conversation.count inital_count = Conversation.count
message = dm_params[:entry][0]['messaging'][0] message = dm_params[:entry][0]['messaging'][0]
contact_inbox sender_id = message['sender']['id']
create_instagram_contact_for_sender(sender_id, instagram_messenger_inbox)
described_class.new(message, instagram_messenger_inbox).perform described_class.new(message, instagram_messenger_inbox).perform
instagram_messenger_inbox.reload instagram_messenger_inbox.reload
contact_inbox.reload
expect(instagram_messenger_inbox.conversations.count).to eq(1) expect(instagram_messenger_inbox.conversations.count).to eq(1)
expect(Conversation.count).to eq(inital_count + 1) expect(Conversation.count).to eq(inital_count + 1)
end end
it 'will not create a new conversation if last conversation is not resolved' do it 'will not create a new conversation if last conversation is not resolved' do
message = dm_params[:entry][0]['messaging'][0]
sender_id = message['sender']['id']
contact = create_instagram_contact_for_sender(sender_id, instagram_messenger_inbox)
existing_conversation = create( existing_conversation = create(
:conversation, :conversation,
account_id: account.id, account_id: account.id,
@@ -239,18 +251,18 @@ describe Messages::Instagram::Messenger::MessageBuilder do
additional_attributes: { type: 'instagram_direct_message', conversation_language: 'en' } additional_attributes: { type: 'instagram_direct_message', conversation_language: 'en' }
) )
message = dm_params[:entry][0]['messaging'][0]
contact_inbox
described_class.new(message, instagram_messenger_inbox).perform described_class.new(message, instagram_messenger_inbox).perform
instagram_messenger_inbox.reload instagram_messenger_inbox.reload
contact_inbox.reload
expect(instagram_messenger_inbox.conversations.last.id).to eq(existing_conversation.id) expect(instagram_messenger_inbox.conversations.last.id).to eq(existing_conversation.id)
end end
it 'creates a new conversation if last conversation is resolved' do it 'creates a new conversation if last conversation is resolved' do
message = dm_params[:entry][0]['messaging'][0]
sender_id = message['sender']['id']
contact = create_instagram_contact_for_sender(sender_id, instagram_messenger_inbox)
existing_conversation = create( existing_conversation = create(
:conversation, :conversation,
account_id: account.id, account_id: account.id,
@@ -261,13 +273,9 @@ describe Messages::Instagram::Messenger::MessageBuilder do
) )
inital_count = Conversation.count inital_count = Conversation.count
message = dm_params[:entry][0]['messaging'][0]
contact_inbox
described_class.new(message, instagram_messenger_inbox).perform described_class.new(message, instagram_messenger_inbox).perform
instagram_messenger_inbox.reload instagram_messenger_inbox.reload
contact_inbox.reload
expect(instagram_messenger_inbox.conversations.last.id).not_to eq(existing_conversation.id) expect(instagram_messenger_inbox.conversations.last.id).not_to eq(existing_conversation.id)
expect(Conversation.count).to eq(inital_count + 1) expect(Conversation.count).to eq(inital_count + 1)
@@ -283,18 +291,22 @@ describe Messages::Instagram::Messenger::MessageBuilder do
it 'creates a new conversation if existing conversation is not present' do it 'creates a new conversation if existing conversation is not present' do
inital_count = Conversation.count inital_count = Conversation.count
message = dm_params[:entry][0]['messaging'][0] message = dm_params[:entry][0]['messaging'][0]
contact_inbox sender_id = message['sender']['id']
create_instagram_contact_for_sender(sender_id, instagram_messenger_inbox)
described_class.new(message, instagram_messenger_inbox).perform described_class.new(message, instagram_messenger_inbox).perform
instagram_messenger_inbox.reload instagram_messenger_inbox.reload
contact_inbox.reload
expect(instagram_messenger_inbox.conversations.count).to eq(1) expect(instagram_messenger_inbox.conversations.count).to eq(1)
expect(Conversation.count).to eq(inital_count + 1) expect(Conversation.count).to eq(inital_count + 1)
end end
it 'reopens last conversation if last conversation is resolved' do it 'reopens last conversation if last conversation is resolved' do
message = dm_params[:entry][0]['messaging'][0]
sender_id = message['sender']['id']
contact = create_instagram_contact_for_sender(sender_id, instagram_messenger_inbox)
existing_conversation = create( existing_conversation = create(
:conversation, :conversation,
account_id: account.id, account_id: account.id,
@@ -306,13 +318,9 @@ describe Messages::Instagram::Messenger::MessageBuilder do
inital_count = Conversation.count inital_count = Conversation.count
message = dm_params[:entry][0]['messaging'][0]
contact_inbox
described_class.new(message, instagram_messenger_inbox).perform described_class.new(message, instagram_messenger_inbox).perform
instagram_messenger_inbox.reload instagram_messenger_inbox.reload
contact_inbox.reload
expect(instagram_messenger_inbox.conversations.last.id).to eq(existing_conversation.id) expect(instagram_messenger_inbox.conversations.last.id).to eq(existing_conversation.id)
expect(Conversation.count).to eq(inital_count) expect(Conversation.count).to eq(inital_count)
@@ -344,7 +352,9 @@ describe Messages::Instagram::Messenger::MessageBuilder do
allow(fb_object).to receive(:get_object).and_return(story_data) allow(fb_object).to receive(:get_object).and_return(story_data)
messaging = story_mention_params[:entry][0][:messaging][0] messaging = story_mention_params[:entry][0][:messaging][0]
contact_inbox sender_id = messaging['sender']['id']
create_instagram_contact_for_sender(sender_id, instagram_messenger_inbox)
builder = described_class.new(messaging, instagram_messenger_inbox) builder = described_class.new(messaging, instagram_messenger_inbox)
builder.perform builder.perform
@@ -358,18 +368,20 @@ describe Messages::Instagram::Messenger::MessageBuilder do
end end
it 'handles story mentions specifically in the Instagram builder' do it 'handles story mentions specifically in the Instagram builder' do
messaging = story_mention_params[:entry][0][:messaging][0]
sender_id = messaging['sender']['id']
# First allow contact info fetch # First allow contact info fetch
allow(fb_object).to receive(:get_object).and_return({ allow(fb_object).to receive(:get_object).and_return({
name: 'Jane', name: 'Jane',
id: 'Sender-id-1' id: sender_id
}.with_indifferent_access) }.with_indifferent_access)
# Then allow story data fetch # Then allow story data fetch
allow(fb_object).to receive(:get_object).with(anything, fields: %w[story from]) allow(fb_object).to receive(:get_object).with(anything, fields: %w[story from])
.and_return(story_data) .and_return(story_data)
messaging = story_mention_params[:entry][0][:messaging][0] create_instagram_contact_for_sender(sender_id, instagram_messenger_inbox)
contact_inbox
described_class.new(messaging, instagram_messenger_inbox).perform described_class.new(messaging, instagram_messenger_inbox).perform
message = instagram_messenger_inbox.messages.first message = instagram_messenger_inbox.messages.first

View File

@@ -1,14 +1,18 @@
FactoryBot.define do FactoryBot.define do
factory :instagram_message_create_event, class: Hash do factory :instagram_message_create_event, class: Hash do
transient do
ig_entry_id { SecureRandom.uuid }
sender_id { "Sender-id-#{SecureRandom.hex(4)}" }
end
entry do entry do
[ [
{ {
'id': 'instagram-message-id-123', 'id': ig_entry_id,
'time': '2021-09-08T06:34:04+0000', 'time': '2021-09-08T06:34:04+0000',
'messaging': [ 'messaging': [
{ {
'sender': { 'sender': {
'id': 'Sender-id-1' 'id': sender_id
}, },
'recipient': { 'recipient': {
'id': 'chatwoot-app-user-id-1' 'id': 'chatwoot-app-user-id-1'
@@ -27,15 +31,19 @@ FactoryBot.define do
end end
factory :instagram_message_standby_event, class: Hash do factory :instagram_message_standby_event, class: Hash do
transient do
ig_entry_id { SecureRandom.uuid }
sender_id { "Sender-id-#{SecureRandom.hex(4)}" }
end
entry do entry do
[ [
{ {
'time': '2021-09-08T06:34:04+0000', 'time': '2021-09-08T06:34:04+0000',
'id': 'instagram-message-id-123', 'id': ig_entry_id,
'standby': [ 'standby': [
{ {
'sender': { 'sender': {
'id': 'Sender-id-1' 'id': sender_id
}, },
'recipient': { 'recipient': {
'id': 'chatwoot-app-user-id-1' 'id': 'chatwoot-app-user-id-1'
@@ -54,15 +62,19 @@ FactoryBot.define do
end end
factory :instagram_story_reply_event, class: Hash do factory :instagram_story_reply_event, class: Hash do
transient do
ig_entry_id { SecureRandom.uuid }
sender_id { "Sender-id-#{SecureRandom.hex(4)}" }
end
entry do entry do
[ [
{ {
'id': 'instagram-message-id-123', 'id': ig_entry_id,
'time': '2021-09-08T06:34:04+0000', 'time': '2021-09-08T06:34:04+0000',
'messaging': [ 'messaging': [
{ {
'sender': { 'sender': {
'id': 'Sender-id-1' 'id': sender_id
}, },
'recipient': { 'recipient': {
'id': 'chatwoot-app-user-id-1' 'id': 'chatwoot-app-user-id-1'
@@ -87,15 +99,19 @@ FactoryBot.define do
end end
factory :instagram_message_reply_event, class: Hash do factory :instagram_message_reply_event, class: Hash do
transient do
ig_entry_id { SecureRandom.uuid }
sender_id { "Sender-id-#{SecureRandom.hex(4)}" }
end
entry do entry do
[ [
{ {
'id': 'instagram-message-id-123', 'id': ig_entry_id,
'time': '2021-09-08T06:35:04+0000', 'time': '2021-09-08T06:35:04+0000',
'messaging': [ 'messaging': [
{ {
'sender': { 'sender': {
'id': 'Sender-id-1' 'id': sender_id
}, },
'recipient': { 'recipient': {
'id': 'chatwoot-app-user-id-1' 'id': 'chatwoot-app-user-id-1'
@@ -117,11 +133,14 @@ FactoryBot.define do
end end
factory :instagram_test_text_event, class: Hash do factory :instagram_test_text_event, class: Hash do
transient do
ig_entry_id { SecureRandom.uuid }
end
entry do entry do
[ [
{ {
'time' => 1_661_141_837_537, 'time' => 1_661_141_837_537,
'id' => '0', 'id' => ig_entry_id,
'messaging' => [ 'messaging' => [
{ {
'sender' => { 'sender' => {
@@ -144,15 +163,19 @@ FactoryBot.define do
end end
factory :instagram_message_unsend_event, class: Hash do factory :instagram_message_unsend_event, class: Hash do
transient do
ig_entry_id { SecureRandom.uuid }
sender_id { "Sender-id-#{SecureRandom.hex(4)}" }
end
entry do entry do
[ [
{ {
'id': 'instagram-message-id-123', 'id': ig_entry_id,
'time': '2021-09-08T06:34:04+0000', 'time': '2021-09-08T06:34:04+0000',
'messaging': [ 'messaging': [
{ {
'sender': { 'sender': {
'id': 'Sender-id-1' 'id': sender_id
}, },
'recipient': { 'recipient': {
'id': 'chatwoot-app-user-id-1' 'id': 'chatwoot-app-user-id-1'
@@ -171,15 +194,19 @@ FactoryBot.define do
end end
factory :instagram_message_attachment_event, class: Hash do factory :instagram_message_attachment_event, class: Hash do
transient do
ig_entry_id { SecureRandom.uuid }
sender_id { "Sender-id-#{SecureRandom.hex(4)}" }
end
entry do entry do
[ [
{ {
'id': 'instagram-message-id-1234', 'id': ig_entry_id,
'time': '2021-09-08T06:34:04+0000', 'time': '2021-09-08T06:34:04+0000',
'messaging': [ 'messaging': [
{ {
'sender': { 'sender': {
'id': 'Sender-id-1' 'id': sender_id
}, },
'recipient': { 'recipient': {
'id': 'chatwoot-app-user-id-1' 'id': 'chatwoot-app-user-id-1'
@@ -205,15 +232,19 @@ FactoryBot.define do
end end
factory :instagram_shared_reel_event, class: Hash do factory :instagram_shared_reel_event, class: Hash do
transient do
ig_entry_id { SecureRandom.uuid }
sender_id { "Sender-id-#{SecureRandom.hex(4)}" }
end
entry do entry do
[ [
{ {
'id': 'instagram-message-id-1234', 'id': ig_entry_id,
'time': '2021-09-08T06:34:04+0000', 'time': '2021-09-08T06:34:04+0000',
'messaging': [ 'messaging': [
{ {
'sender': { 'sender': {
'id': 'Sender-id-1' 'id': sender_id
}, },
'recipient': { 'recipient': {
'id': 'chatwoot-app-user-id-1' 'id': 'chatwoot-app-user-id-1'
@@ -241,15 +272,19 @@ FactoryBot.define do
end end
factory :instagram_story_mention_event, class: Hash do factory :instagram_story_mention_event, class: Hash do
transient do
ig_entry_id { SecureRandom.uuid }
sender_id { "Sender-id-#{SecureRandom.hex(4)}" }
end
entry do entry do
[ [
{ {
'id': 'instagram-message-id-1234', 'id': ig_entry_id,
'time': '2021-09-08T06:34:04+0000', 'time': '2021-09-08T06:34:04+0000',
'messaging': [ 'messaging': [
{ {
'sender': { 'sender': {
'id': 'Sender-id-1' 'id': sender_id
}, },
'recipient': { 'recipient': {
'id': 'chatwoot-app-user-id-1' 'id': 'chatwoot-app-user-id-1'
@@ -275,15 +310,19 @@ FactoryBot.define do
end end
factory :instagram_story_mention_event_with_echo, class: Hash do factory :instagram_story_mention_event_with_echo, class: Hash do
transient do
ig_entry_id { SecureRandom.uuid }
sender_id { "Sender-id-#{SecureRandom.hex(4)}" }
end
entry do entry do
[ [
{ {
'id': 'instagram-message-id-1234', 'id': ig_entry_id,
'time': '2021-09-08T06:34:04+0000', 'time': '2021-09-08T06:34:04+0000',
'messaging': [ 'messaging': [
{ {
'sender': { 'sender': {
'id': 'Sender-id-1' 'id': sender_id
}, },
'recipient': { 'recipient': {
'id': 'chatwoot-app-user-id-1' 'id': 'chatwoot-app-user-id-1'
@@ -310,15 +349,19 @@ FactoryBot.define do
end end
factory :instagram_message_unsupported_event, class: Hash do factory :instagram_message_unsupported_event, class: Hash do
transient do
ig_entry_id { SecureRandom.uuid }
sender_id { "Sender-id-#{SecureRandom.hex(4)}" }
end
entry do entry do
[ [
{ {
'id': 'instagram-message-unsupported-id-123', 'id': ig_entry_id,
'time': '2021-09-08T06:34:04+0000', 'time': '2021-09-08T06:34:04+0000',
'messaging': [ 'messaging': [
{ {
'sender': { 'sender': {
'id': 'Sender-id-1' 'id': sender_id
}, },
'recipient': { 'recipient': {
'id': 'chatwoot-app-user-id-1' 'id': 'chatwoot-app-user-id-1'
@@ -337,15 +380,19 @@ FactoryBot.define do
end end
factory :messaging_seen_event, class: Hash do factory :messaging_seen_event, class: Hash do
transient do
ig_entry_id { SecureRandom.uuid }
sender_id { "Sender-id-#{SecureRandom.hex(4)}" }
end
entry do entry do
[ [
{ {
'id': 'instagram-message-id-123', 'id': ig_entry_id,
'time': '2021-09-08T06:34:04+0000', 'time': '2021-09-08T06:34:04+0000',
'messaging': [ 'messaging': [
{ {
'sender': { 'sender': {
'id': 'Sender-id-1' 'id': sender_id
}, },
'recipient': { 'recipient': {
'id': 'chatwoot-app-user-id-1' 'id': 'chatwoot-app-user-id-1'

View File

@@ -10,19 +10,10 @@ describe Webhooks::InstagramEventsJob do
end end
let!(:account) { create(:account) } let!(:account) { create(:account) }
let(:return_object) do
{ name: 'Jane',
id: 'Sender-id-1',
account_id: instagram_messenger_inbox.account_id,
profile_pic: 'https://chatwoot-assets.local/sample.png',
username: 'some_user_name' }
end
let!(:instagram_messenger_channel) { create(:channel_instagram_fb_page, account: account, instagram_id: 'chatwoot-app-user-id-1') } let!(:instagram_messenger_channel) { create(:channel_instagram_fb_page, account: account, instagram_id: 'chatwoot-app-user-id-1') }
let!(:instagram_messenger_inbox) { create(:inbox, channel: instagram_messenger_channel, account: account, greeting_enabled: false) } let!(:instagram_messenger_inbox) { create(:inbox, channel: instagram_messenger_channel, account: account, greeting_enabled: false) }
let!(:instagram_channel) { create(:channel_instagram, account: account, instagram_id: 'chatwoot-app-user-id-1') } let!(:instagram_channel) { create(:channel_instagram, account: account, instagram_id: 'chatwoot-app-user-id-1') }
let!(:instagram_inbox) { create(:inbox, channel: instagram_channel, account: account, greeting_enabled: false) } let!(:instagram_inbox) { create(:inbox, channel: instagram_channel, account: account, greeting_enabled: false) }
# Combined message events into one helper # Combined message events into one helper
let(:message_events) do let(:message_events) do
{ {
@@ -37,6 +28,14 @@ describe Webhooks::InstagramEventsJob do
} }
end end
def return_object_for(sender_id)
{ name: 'Jane',
id: sender_id,
account_id: instagram_messenger_inbox.account_id,
profile_pic: 'https://chatwoot-assets.local/sample.png',
username: 'some_user_name' }
end
describe '#perform' do describe '#perform' do
context 'when handling messaging events for Instagram via Facebook page' do context 'when handling messaging events for Instagram via Facebook page' do
let(:fb_object) { double } let(:fb_object) { double }
@@ -47,8 +46,9 @@ describe Webhooks::InstagramEventsJob do
it 'creates incoming message in the instagram inbox' do it 'creates incoming message in the instagram inbox' do
allow(Koala::Facebook::API).to receive(:new).and_return(fb_object) allow(Koala::Facebook::API).to receive(:new).and_return(fb_object)
sender_id = message_events[:dm][:entry][0][:messaging][0][:sender][:id]
allow(fb_object).to receive(:get_object).and_return( allow(fb_object).to receive(:get_object).and_return(
return_object.with_indifferent_access return_object_for(sender_id).with_indifferent_access
) )
instagram_webhook.perform_now(message_events[:dm][:entry]) instagram_webhook.perform_now(message_events[:dm][:entry])
@@ -63,8 +63,9 @@ describe Webhooks::InstagramEventsJob do
it 'creates standby message in the instagram inbox' do it 'creates standby message in the instagram inbox' do
allow(Koala::Facebook::API).to receive(:new).and_return(fb_object) allow(Koala::Facebook::API).to receive(:new).and_return(fb_object)
sender_id = message_events[:standby][:entry][0][:standby][0][:sender][:id]
allow(fb_object).to receive(:get_object).and_return( allow(fb_object).to receive(:get_object).and_return(
return_object.with_indifferent_access return_object_for(sender_id).with_indifferent_access
) )
instagram_webhook.perform_now(message_events[:standby][:entry]) instagram_webhook.perform_now(message_events[:standby][:entry])
@@ -82,10 +83,11 @@ describe Webhooks::InstagramEventsJob do
it 'handle instagram unsend message event' do it 'handle instagram unsend message event' do
message = create(:message, inbox_id: instagram_messenger_inbox.id, source_id: 'message-id-to-delete') message = create(:message, inbox_id: instagram_messenger_inbox.id, source_id: 'message-id-to-delete')
allow(Koala::Facebook::API).to receive(:new).and_return(fb_object) allow(Koala::Facebook::API).to receive(:new).and_return(fb_object)
sender_id = message_events[:unsend][:entry][0][:messaging][0][:sender][:id]
allow(fb_object).to receive(:get_object).and_return( allow(fb_object).to receive(:get_object).and_return(
{ {
name: 'Jane', name: 'Jane',
id: 'Sender-id-1', id: sender_id,
account_id: instagram_messenger_inbox.account_id, account_id: instagram_messenger_inbox.account_id,
profile_pic: 'https://chatwoot-assets.local/sample.png' profile_pic: 'https://chatwoot-assets.local/sample.png'
}.with_indifferent_access }.with_indifferent_access
@@ -104,8 +106,9 @@ describe Webhooks::InstagramEventsJob do
it 'creates incoming message with attachments in the instagram inbox' do it 'creates incoming message with attachments in the instagram inbox' do
allow(Koala::Facebook::API).to receive(:new).and_return(fb_object) allow(Koala::Facebook::API).to receive(:new).and_return(fb_object)
sender_id = message_events[:attachment][:entry][0][:messaging][0][:sender][:id]
allow(fb_object).to receive(:get_object).and_return( allow(fb_object).to receive(:get_object).and_return(
return_object.with_indifferent_access return_object_for(sender_id).with_indifferent_access
) )
instagram_webhook.perform_now(message_events[:attachment][:entry]) instagram_webhook.perform_now(message_events[:attachment][:entry])
@@ -118,8 +121,9 @@ describe Webhooks::InstagramEventsJob do
it 'creates incoming message with attachments in the instagram inbox for story mention' do it 'creates incoming message with attachments in the instagram inbox for story mention' do
allow(Koala::Facebook::API).to receive(:new).and_return(fb_object) allow(Koala::Facebook::API).to receive(:new).and_return(fb_object)
sender_id = message_events[:story_mention][:entry][0][:messaging][0][:sender][:id]
allow(fb_object).to receive(:get_object).and_return( allow(fb_object).to receive(:get_object).and_return(
return_object.with_indifferent_access, return_object_for(sender_id).with_indifferent_access,
{ story: { story:
{ {
mention: { mention: {
@@ -165,8 +169,9 @@ describe Webhooks::InstagramEventsJob do
it 'handles unsupported message' do it 'handles unsupported message' do
allow(Koala::Facebook::API).to receive(:new).and_return(fb_object) allow(Koala::Facebook::API).to receive(:new).and_return(fb_object)
sender_id = message_events[:unsupported][:entry][0][:messaging][0][:sender][:id]
allow(fb_object).to receive(:get_object).and_return( allow(fb_object).to receive(:get_object).and_return(
return_object.with_indifferent_access return_object_for(sender_id).with_indifferent_access
) )
instagram_webhook.perform_now(message_events[:unsupported][:entry]) instagram_webhook.perform_now(message_events[:unsupported][:entry])
@@ -184,19 +189,22 @@ describe Webhooks::InstagramEventsJob do
before do before do
instagram_channel.update(access_token: 'valid_instagram_token') instagram_channel.update(access_token: 'valid_instagram_token')
stub_request(:get, %r{https://graph\.instagram\.com/v22\.0/Sender-id-1\?.*}) stub_request(:get, %r{https://graph\.instagram\.com/v22\.0/Sender-id-.*\?.*})
.to_return( .to_return(
status: 200, status: 200,
body: { body: proc { |request|
name: 'Jane', sender_id = request.uri.path.split('/').last.split('?').first
username: 'some_user_name', {
profile_pic: 'https://chatwoot-assets.local/sample.png', name: 'Jane',
id: 'Sender-id-1', username: 'some_user_name',
follower_count: 100, profile_pic: 'https://chatwoot-assets.local/sample.png',
is_user_follow_business: true, id: sender_id,
is_business_follow_user: true, follower_count: 100,
is_verified_user: false is_user_follow_business: true,
}.to_json, is_business_follow_user: true,
is_verified_user: false
}.to_json
},
headers: { 'Content-Type' => 'application/json' } headers: { 'Content-Type' => 'application/json' }
) )
end end
@@ -289,14 +297,15 @@ describe Webhooks::InstagramEventsJob do
stub_request(:get, %r{https://graph\.instagram\.com/v22\.0/.*\?.*}) stub_request(:get, %r{https://graph\.instagram\.com/v22\.0/.*\?.*})
.to_return(status: 401, body: { error: { message: 'No matching Instagram user', code: 9010 } }.to_json) .to_return(status: 401, body: { error: { message: 'No matching Instagram user', code: 9010 } }.to_json)
sender_id = message_events[:dm][:entry][0][:messaging][0][:sender][:id]
instagram_webhook.perform_now(message_events[:dm][:entry]) instagram_webhook.perform_now(message_events[:dm][:entry])
instagram_inbox.reload instagram_inbox.reload
expect(instagram_inbox.contacts.count).to be 1 expect(instagram_inbox.contacts.count).to be 1
expect(instagram_inbox.contacts.last.name).to eq 'Unknown (IG: Sender-id-1)' expect(instagram_inbox.contacts.last.name).to eq "Unknown (IG: #{sender_id})"
expect(instagram_inbox.contacts.last.contact_inboxes.count).to be 1 expect(instagram_inbox.contacts.last.contact_inboxes.count).to be 1
expect(instagram_inbox.contacts.last.contact_inboxes.first.source_id).to eq 'Sender-id-1' expect(instagram_inbox.contacts.last.contact_inboxes.first.source_id).to eq sender_id
expect(instagram_inbox.conversations.count).to eq 1 expect(instagram_inbox.conversations.count).to eq 1
expect(instagram_inbox.messages.count).to eq 1 expect(instagram_inbox.messages.count).to eq 1

View File

@@ -70,6 +70,7 @@ RSpec.configure do |config|
config.include SlackStubs config.include SlackStubs
config.include FileUploadHelpers config.include FileUploadHelpers
config.include CsvSpecHelpers config.include CsvSpecHelpers
config.include InstagramSpecHelpers
config.include Devise::Test::IntegrationHelpers, type: :request config.include Devise::Test::IntegrationHelpers, type: :request
config.include ActiveSupport::Testing::TimeHelpers config.include ActiveSupport::Testing::TimeHelpers
config.include ActionCable::TestHelper config.include ActionCable::TestHelper

View File

@@ -0,0 +1,20 @@
module InstagramSpecHelpers
def create_instagram_contact_for_sender(sender_id, inbox)
contact = Contact.find_by(identifier: sender_id)
if contact.nil?
contact = create(:contact, identifier: sender_id, name: 'Jane Dae')
create(:contact_inbox, contact_id: contact.id, inbox_id: inbox.id, source_id: sender_id)
end
contact
end
def instagram_user_response_object_for(sender_id, account_id)
{
name: 'Jane',
id: sender_id,
account_id: account_id,
profile_pic: 'https://chatwoot-assets.local/sample.png',
username: 'some_user_name'
}
end
end