mirror of
				https://github.com/lingble/chatwoot.git
				synced 2025-10-30 18:47:51 +00:00 
			
		
		
		
	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:
		| @@ -17,40 +17,34 @@ describe Messages::Instagram::MessageBuilder do | ||||
|   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_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 | ||||
|     before do | ||||
|       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( | ||||
|           status: 200, | ||||
|           body: { | ||||
|           body: proc { |request| | ||||
|             sender_id = request.uri.path.split('/').last.split('?').first | ||||
|             { | ||||
|               name: 'Jane', | ||||
|               username: 'some_user_name', | ||||
|               profile_pic: 'https://chatwoot-assets.local/sample.png', | ||||
|             id: 'Sender-id-1', | ||||
|               id: sender_id, | ||||
|               follower_count: 100, | ||||
|               is_user_follow_business: true, | ||||
|               is_business_follow_user: true, | ||||
|               is_verified_user: false | ||||
|           }.to_json, | ||||
|             }.to_json | ||||
|           }, | ||||
|           headers: { 'Content-Type' => 'application/json' } | ||||
|         ) | ||||
|     end | ||||
|  | ||||
|     it 'creates contact and message for the instagram direct inbox' do | ||||
|       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 | ||||
|  | ||||
|       instagram_inbox.reload | ||||
| @@ -63,13 +57,15 @@ describe Messages::Instagram::MessageBuilder do | ||||
|     end | ||||
|  | ||||
|     it 'discard echo message already sent by chatwoot' do | ||||
|       conversation | ||||
|       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.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 | ||||
|       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 | ||||
|       messaging = dm_params[:entry][0]['messaging'][0] | ||||
|       create_instagram_contact_for_sender(messaging['sender']['id'], instagram_inbox) | ||||
|       described_class.new(messaging, instagram_inbox).perform | ||||
|  | ||||
|       initial_message_count = instagram_inbox.messages.count | ||||
| @@ -93,6 +90,7 @@ describe Messages::Instagram::MessageBuilder do | ||||
|  | ||||
|     it 'creates message for shared reel' do | ||||
|       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 | ||||
|  | ||||
|       message = instagram_inbox.messages.first | ||||
| @@ -103,7 +101,9 @@ describe Messages::Instagram::MessageBuilder do | ||||
|     end | ||||
|  | ||||
|     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}\?.*}) | ||||
|         .to_return( | ||||
| @@ -121,7 +121,6 @@ describe Messages::Instagram::MessageBuilder do | ||||
|           headers: { 'Content-Type' => 'application/json' } | ||||
|         ) | ||||
|  | ||||
|       messaging = instagram_story_reply_event[:entry][0]['messaging'][0] | ||||
|       described_class.new(messaging, instagram_inbox).perform | ||||
|  | ||||
|       message = instagram_inbox.messages.first | ||||
| @@ -134,10 +133,13 @@ describe Messages::Instagram::MessageBuilder do | ||||
|     it 'creates message with reply to mid' do | ||||
|       # Create first message to ensure reply to is valid | ||||
|       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 | ||||
|  | ||||
|       # 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['sender']['id'] = sender_id | ||||
|       described_class.new(messaging, instagram_inbox).perform | ||||
|  | ||||
|       first_message = instagram_inbox.messages.first | ||||
| @@ -148,12 +150,13 @@ describe Messages::Instagram::MessageBuilder do | ||||
|     end | ||||
|  | ||||
|     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}\?.*}) | ||||
|         .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 | ||||
|  | ||||
|       message = instagram_inbox.messages.first | ||||
| @@ -163,11 +166,12 @@ describe Messages::Instagram::MessageBuilder do | ||||
|     end | ||||
|  | ||||
|     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 | ||||
|       story_mention_params[:entry][0][:messaging][0]['message']['attachments'][0]['type'] = 'unsupported_type' | ||||
|       messaging = story_mention_params[:entry][0][:messaging][0] | ||||
|       messaging['message']['attachments'][0]['type'] = 'unsupported_type' | ||||
|  | ||||
|       described_class.new(messaging, instagram_inbox, outgoing_echo: false).perform | ||||
|  | ||||
| @@ -177,7 +181,11 @@ describe Messages::Instagram::MessageBuilder do | ||||
|     end | ||||
|  | ||||
|     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.messages.count).to be 1 | ||||
| @@ -194,7 +202,7 @@ describe Messages::Instagram::MessageBuilder do | ||||
|       instagram_channel.update(access_token: 'invalid_token') | ||||
|  | ||||
|       # 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( | ||||
|           status: 401, | ||||
|           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 | ||||
|       initial_count = Conversation.count | ||||
|       messaging = dm_params[:entry][0]['messaging'][0] | ||||
|       create_instagram_contact_for_sender(messaging['sender']['id'], instagram_inbox) | ||||
|  | ||||
|       described_class.new(messaging, instagram_inbox).perform | ||||
|  | ||||
| @@ -226,21 +235,23 @@ describe Messages::Instagram::MessageBuilder do | ||||
|     end | ||||
|  | ||||
|     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, | ||||
|                                                     contact_id: contact.id, status: :open) | ||||
|  | ||||
|       messaging = dm_params[:entry][0]['messaging'][0] | ||||
|       described_class.new(messaging, instagram_inbox).perform | ||||
|  | ||||
|       expect(instagram_inbox.conversations.last.id).to eq(existing_conversation.id) | ||||
|     end | ||||
|  | ||||
|     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, | ||||
|                                                     contact_id: contact.id, status: :resolved) | ||||
|  | ||||
|       initial_count = Conversation.count | ||||
|       messaging = dm_params[:entry][0]['messaging'][0] | ||||
|  | ||||
|       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 | ||||
|       initial_count = Conversation.count | ||||
|       messaging = dm_params[:entry][0]['messaging'][0] | ||||
|       create_instagram_contact_for_sender(messaging['sender']['id'], instagram_inbox) | ||||
|  | ||||
|       described_class.new(messaging, instagram_inbox).perform | ||||
|  | ||||
| @@ -265,6 +277,8 @@ describe Messages::Instagram::MessageBuilder do | ||||
|     end | ||||
|  | ||||
|     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, | ||||
|                                                     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 | ||||
|       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 | ||||
|  | ||||
|       message = instagram_inbox.messages.first | ||||
| @@ -328,6 +343,7 @@ describe Messages::Instagram::MessageBuilder do | ||||
|         ) | ||||
|  | ||||
|       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 | ||||
|  | ||||
|       message = instagram_inbox.messages.first | ||||
|   | ||||
| @@ -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_message_reply_event) { build(:instagram_message_reply_event).with_indifferent_access } | ||||
|   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 | ||||
|     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(fb_object).to receive(:get_object).and_return( | ||||
|         { | ||||
|           name: 'Jane', | ||||
|           id: 'Sender-id-1', | ||||
|           id: sender_id, | ||||
|           account_id: instagram_messenger_inbox.account_id, | ||||
|           profile_pic: 'https://chatwoot-assets.local/sample.png' | ||||
|         }.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 | ||||
|  | ||||
|       instagram_messenger_inbox.reload | ||||
| @@ -56,7 +49,13 @@ describe Messages::Instagram::Messenger::MessageBuilder do | ||||
|     end | ||||
|  | ||||
|     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.messages.count).to be 1 | ||||
| @@ -65,13 +64,11 @@ describe Messages::Instagram::Messenger::MessageBuilder do | ||||
|       allow(fb_object).to receive(:get_object).and_return( | ||||
|         { | ||||
|           name: 'Jane', | ||||
|           id: 'Sender-id-1', | ||||
|           id: sender_id, | ||||
|           account_id: instagram_messenger_inbox.account_id, | ||||
|           profile_pic: 'https://chatwoot-assets.local/sample.png' | ||||
|         }.with_indifferent_access | ||||
|       ) | ||||
|       messaging = dm_params[:entry][0]['messaging'][0] | ||||
|       contact_inbox | ||||
|       described_class.new(messaging, instagram_messenger_inbox, outgoing_echo: true).perform | ||||
|  | ||||
|       instagram_messenger_inbox.reload | ||||
| @@ -81,17 +78,20 @@ describe Messages::Instagram::Messenger::MessageBuilder do | ||||
|     end | ||||
|  | ||||
|     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(fb_object).to receive(:get_object).and_return( | ||||
|         { | ||||
|           name: 'Jane', | ||||
|           id: 'Sender-id-1', | ||||
|           id: sender_id, | ||||
|           account_id: instagram_messenger_inbox.account_id, | ||||
|           profile_pic: 'https://chatwoot-assets.local/sample.png' | ||||
|         }.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 | ||||
|  | ||||
|       message = instagram_messenger_channel.inbox.messages.first | ||||
| @@ -102,18 +102,20 @@ describe Messages::Instagram::Messenger::MessageBuilder do | ||||
|     end | ||||
|  | ||||
|     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(fb_object).to receive(:get_object).and_return( | ||||
|         { | ||||
|           name: 'Jane', | ||||
|           id: 'Sender-id-1', | ||||
|           id: sender_id, | ||||
|           account_id: instagram_messenger_inbox.account_id, | ||||
|           profile_pic: 'https://chatwoot-assets.local/sample.png' | ||||
|         }.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 | ||||
|  | ||||
|       message = instagram_messenger_channel.inbox.messages.first | ||||
| @@ -125,24 +127,26 @@ describe Messages::Instagram::Messenger::MessageBuilder do | ||||
|     end | ||||
|  | ||||
|     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(fb_object).to receive(:get_object).and_return( | ||||
|         { | ||||
|           name: 'Jane', | ||||
|           id: 'Sender-id-1', | ||||
|           id: sender_id, | ||||
|           account_id: instagram_messenger_inbox.account_id, | ||||
|           profile_pic: 'https://chatwoot-assets.local/sample.png' | ||||
|         }.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] | ||||
|       contact_inbox | ||||
|  | ||||
|       messaging['sender']['id'] = sender_id  # Use the same sender_id | ||||
|       described_class.new(messaging, instagram_messenger_inbox).perform | ||||
|       first_message = instagram_messenger_channel.inbox.messages.first | ||||
|       message = instagram_messenger_channel.inbox.messages.last | ||||
| @@ -153,14 +157,16 @@ describe Messages::Instagram::Messenger::MessageBuilder do | ||||
|     end | ||||
|  | ||||
|     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(fb_object).to receive(:get_object).and_raise(Koala::Facebook::ClientError.new( | ||||
|                                                            190, | ||||
|                                                            'This Message has been deleted by the user or the business.' | ||||
|                                                          )) | ||||
|  | ||||
|       messaging = story_mention_params[:entry][0][:messaging][0] | ||||
|       contact_inbox | ||||
|       create_instagram_contact_for_sender(sender_id, instagram_messenger_inbox) | ||||
|       described_class.new(messaging, instagram_messenger_inbox, outgoing_echo: false).perform | ||||
|  | ||||
|       instagram_messenger_inbox.reload | ||||
| @@ -180,22 +186,24 @@ describe Messages::Instagram::Messenger::MessageBuilder do | ||||
|     end | ||||
|  | ||||
|     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(fb_object).to receive(:get_object).and_return( | ||||
|         { | ||||
|           name: 'Jane', | ||||
|           id: 'Sender-id-1', | ||||
|           id: sender_id, | ||||
|           account_id: instagram_messenger_inbox.account_id, | ||||
|           profile_pic: 'https://chatwoot-assets.local/sample.png' | ||||
|         }.with_indifferent_access | ||||
|       ) | ||||
|  | ||||
|       conversation | ||||
|  | ||||
|       # 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] | ||||
|  | ||||
|       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, | ||||
|                             additional_attributes: { type: 'instagram_direct_message', conversation_language: 'en' }) | ||||
|       described_class.new(messaging, instagram_messenger_inbox, outgoing_echo: false).perform | ||||
|  | ||||
|       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 | ||||
|       inital_count = Conversation.count | ||||
|       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 | ||||
|  | ||||
|       instagram_messenger_inbox.reload | ||||
|       contact_inbox.reload | ||||
|  | ||||
|       expect(instagram_messenger_inbox.conversations.count).to eq(1) | ||||
|       expect(Conversation.count).to eq(inital_count + 1) | ||||
|     end | ||||
|  | ||||
|     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( | ||||
|         :conversation, | ||||
|         account_id: account.id, | ||||
| @@ -239,18 +251,18 @@ describe Messages::Instagram::Messenger::MessageBuilder do | ||||
|         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 | ||||
|  | ||||
|       instagram_messenger_inbox.reload | ||||
|       contact_inbox.reload | ||||
|  | ||||
|       expect(instagram_messenger_inbox.conversations.last.id).to eq(existing_conversation.id) | ||||
|     end | ||||
|  | ||||
|     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( | ||||
|         :conversation, | ||||
|         account_id: account.id, | ||||
| @@ -261,13 +273,9 @@ describe Messages::Instagram::Messenger::MessageBuilder do | ||||
|       ) | ||||
|  | ||||
|       inital_count = Conversation.count | ||||
|       message = dm_params[:entry][0]['messaging'][0] | ||||
|       contact_inbox | ||||
|  | ||||
|       described_class.new(message, instagram_messenger_inbox).perform | ||||
|  | ||||
|       instagram_messenger_inbox.reload | ||||
|       contact_inbox.reload | ||||
|  | ||||
|       expect(instagram_messenger_inbox.conversations.last.id).not_to eq(existing_conversation.id) | ||||
|       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 | ||||
|       inital_count = Conversation.count | ||||
|       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 | ||||
|  | ||||
|       instagram_messenger_inbox.reload | ||||
|       contact_inbox.reload | ||||
|  | ||||
|       expect(instagram_messenger_inbox.conversations.count).to eq(1) | ||||
|       expect(Conversation.count).to eq(inital_count + 1) | ||||
|     end | ||||
|  | ||||
|     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( | ||||
|         :conversation, | ||||
|         account_id: account.id, | ||||
| @@ -306,13 +318,9 @@ describe Messages::Instagram::Messenger::MessageBuilder do | ||||
|  | ||||
|       inital_count = Conversation.count | ||||
|  | ||||
|       message = dm_params[:entry][0]['messaging'][0] | ||||
|       contact_inbox | ||||
|  | ||||
|       described_class.new(message, instagram_messenger_inbox).perform | ||||
|  | ||||
|       instagram_messenger_inbox.reload | ||||
|       contact_inbox.reload | ||||
|  | ||||
|       expect(instagram_messenger_inbox.conversations.last.id).to eq(existing_conversation.id) | ||||
|       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) | ||||
|  | ||||
|       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.perform | ||||
|  | ||||
| @@ -358,18 +368,20 @@ describe Messages::Instagram::Messenger::MessageBuilder do | ||||
|     end | ||||
|  | ||||
|     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 | ||||
|       allow(fb_object).to receive(:get_object).and_return({ | ||||
|         name: 'Jane', | ||||
|         id: 'Sender-id-1' | ||||
|         id: sender_id | ||||
|       }.with_indifferent_access) | ||||
|  | ||||
|       # Then allow story data fetch | ||||
|       allow(fb_object).to receive(:get_object).with(anything, fields: %w[story from]) | ||||
|                                               .and_return(story_data) | ||||
|  | ||||
|       messaging = story_mention_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 | ||||
|  | ||||
|       message = instagram_messenger_inbox.messages.first | ||||
|   | ||||
| @@ -1,14 +1,18 @@ | ||||
| FactoryBot.define 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 | ||||
|       [ | ||||
|         { | ||||
|           'id': 'instagram-message-id-123', | ||||
|           'id': ig_entry_id, | ||||
|           'time': '2021-09-08T06:34:04+0000', | ||||
|           'messaging': [ | ||||
|             { | ||||
|               'sender': { | ||||
|                 'id': 'Sender-id-1' | ||||
|                 'id': sender_id | ||||
|               }, | ||||
|               'recipient': { | ||||
|                 'id': 'chatwoot-app-user-id-1' | ||||
| @@ -27,15 +31,19 @@ FactoryBot.define do | ||||
|   end | ||||
|  | ||||
|   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 | ||||
|       [ | ||||
|         { | ||||
|           'time': '2021-09-08T06:34:04+0000', | ||||
|           'id': 'instagram-message-id-123', | ||||
|           'id': ig_entry_id, | ||||
|           'standby': [ | ||||
|             { | ||||
|               'sender': { | ||||
|                 'id': 'Sender-id-1' | ||||
|                 'id': sender_id | ||||
|               }, | ||||
|               'recipient': { | ||||
|                 'id': 'chatwoot-app-user-id-1' | ||||
| @@ -54,15 +62,19 @@ FactoryBot.define do | ||||
|   end | ||||
|  | ||||
|   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 | ||||
|       [ | ||||
|         { | ||||
|           'id': 'instagram-message-id-123', | ||||
|           'id': ig_entry_id, | ||||
|           'time': '2021-09-08T06:34:04+0000', | ||||
|           'messaging': [ | ||||
|             { | ||||
|               'sender': { | ||||
|                 'id': 'Sender-id-1' | ||||
|                 'id': sender_id | ||||
|               }, | ||||
|               'recipient': { | ||||
|                 'id': 'chatwoot-app-user-id-1' | ||||
| @@ -87,15 +99,19 @@ FactoryBot.define do | ||||
|   end | ||||
|  | ||||
|   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 | ||||
|       [ | ||||
|         { | ||||
|           'id': 'instagram-message-id-123', | ||||
|           'id': ig_entry_id, | ||||
|           'time': '2021-09-08T06:35:04+0000', | ||||
|           'messaging': [ | ||||
|             { | ||||
|               'sender': { | ||||
|                 'id': 'Sender-id-1' | ||||
|                 'id': sender_id | ||||
|               }, | ||||
|               'recipient': { | ||||
|                 'id': 'chatwoot-app-user-id-1' | ||||
| @@ -117,11 +133,14 @@ FactoryBot.define do | ||||
|   end | ||||
|  | ||||
|   factory :instagram_test_text_event, class: Hash do | ||||
|     transient do | ||||
|       ig_entry_id { SecureRandom.uuid } | ||||
|     end | ||||
|     entry do | ||||
|       [ | ||||
|         { | ||||
|           'time' => 1_661_141_837_537, | ||||
|           'id' => '0', | ||||
|           'id' => ig_entry_id, | ||||
|           'messaging' => [ | ||||
|             { | ||||
|               'sender' => { | ||||
| @@ -144,15 +163,19 @@ FactoryBot.define do | ||||
|   end | ||||
|  | ||||
|   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 | ||||
|       [ | ||||
|         { | ||||
|           'id': 'instagram-message-id-123', | ||||
|           'id': ig_entry_id, | ||||
|           'time': '2021-09-08T06:34:04+0000', | ||||
|           'messaging': [ | ||||
|             { | ||||
|               'sender': { | ||||
|                 'id': 'Sender-id-1' | ||||
|                 'id': sender_id | ||||
|               }, | ||||
|               'recipient': { | ||||
|                 'id': 'chatwoot-app-user-id-1' | ||||
| @@ -171,15 +194,19 @@ FactoryBot.define do | ||||
|   end | ||||
|  | ||||
|   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 | ||||
|       [ | ||||
|         { | ||||
|           'id': 'instagram-message-id-1234', | ||||
|           'id': ig_entry_id, | ||||
|           'time': '2021-09-08T06:34:04+0000', | ||||
|           'messaging': [ | ||||
|             { | ||||
|               'sender': { | ||||
|                 'id': 'Sender-id-1' | ||||
|                 'id': sender_id | ||||
|               }, | ||||
|               'recipient': { | ||||
|                 'id': 'chatwoot-app-user-id-1' | ||||
| @@ -205,15 +232,19 @@ FactoryBot.define do | ||||
|   end | ||||
|  | ||||
|   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 | ||||
|       [ | ||||
|         { | ||||
|           'id': 'instagram-message-id-1234', | ||||
|           'id': ig_entry_id, | ||||
|           'time': '2021-09-08T06:34:04+0000', | ||||
|           'messaging': [ | ||||
|             { | ||||
|               'sender': { | ||||
|                 'id': 'Sender-id-1' | ||||
|                 'id': sender_id | ||||
|               }, | ||||
|               'recipient': { | ||||
|                 'id': 'chatwoot-app-user-id-1' | ||||
| @@ -241,15 +272,19 @@ FactoryBot.define do | ||||
|   end | ||||
|  | ||||
|   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 | ||||
|       [ | ||||
|         { | ||||
|           'id': 'instagram-message-id-1234', | ||||
|           'id': ig_entry_id, | ||||
|           'time': '2021-09-08T06:34:04+0000', | ||||
|           'messaging': [ | ||||
|             { | ||||
|               'sender': { | ||||
|                 'id': 'Sender-id-1' | ||||
|                 'id': sender_id | ||||
|               }, | ||||
|               'recipient': { | ||||
|                 'id': 'chatwoot-app-user-id-1' | ||||
| @@ -275,15 +310,19 @@ FactoryBot.define do | ||||
|   end | ||||
|  | ||||
|   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 | ||||
|       [ | ||||
|         { | ||||
|           'id': 'instagram-message-id-1234', | ||||
|           'id': ig_entry_id, | ||||
|           'time': '2021-09-08T06:34:04+0000', | ||||
|           'messaging': [ | ||||
|             { | ||||
|               'sender': { | ||||
|                 'id': 'Sender-id-1' | ||||
|                 'id': sender_id | ||||
|               }, | ||||
|               'recipient': { | ||||
|                 'id': 'chatwoot-app-user-id-1' | ||||
| @@ -310,15 +349,19 @@ FactoryBot.define do | ||||
|   end | ||||
|  | ||||
|   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 | ||||
|       [ | ||||
|         { | ||||
|           'id': 'instagram-message-unsupported-id-123', | ||||
|           'id': ig_entry_id, | ||||
|           'time': '2021-09-08T06:34:04+0000', | ||||
|           'messaging': [ | ||||
|             { | ||||
|               'sender': { | ||||
|                 'id': 'Sender-id-1' | ||||
|                 'id': sender_id | ||||
|               }, | ||||
|               'recipient': { | ||||
|                 'id': 'chatwoot-app-user-id-1' | ||||
| @@ -337,15 +380,19 @@ FactoryBot.define do | ||||
|   end | ||||
|  | ||||
|   factory :messaging_seen_event, class: Hash do | ||||
|     transient do | ||||
|       ig_entry_id { SecureRandom.uuid } | ||||
|       sender_id { "Sender-id-#{SecureRandom.hex(4)}" } | ||||
|     end | ||||
|     entry do | ||||
|       [ | ||||
|         { | ||||
|           'id': 'instagram-message-id-123', | ||||
|           'id': ig_entry_id, | ||||
|           'time': '2021-09-08T06:34:04+0000', | ||||
|           'messaging': [ | ||||
|             { | ||||
|               'sender': { | ||||
|                 'id': 'Sender-id-1' | ||||
|                 'id': sender_id | ||||
|               }, | ||||
|               'recipient': { | ||||
|                 'id': 'chatwoot-app-user-id-1' | ||||
|   | ||||
| @@ -10,19 +10,10 @@ describe Webhooks::InstagramEventsJob do | ||||
|   end | ||||
|  | ||||
|   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_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_inbox) { create(:inbox, channel: instagram_channel, account: account, greeting_enabled: false) } | ||||
|  | ||||
|   # Combined message events into one helper | ||||
|   let(:message_events) do | ||||
|     { | ||||
| @@ -37,6 +28,14 @@ describe Webhooks::InstagramEventsJob do | ||||
|     } | ||||
|   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 | ||||
|     context 'when handling messaging events for Instagram via Facebook page' do | ||||
|       let(:fb_object) { double } | ||||
| @@ -47,8 +46,9 @@ describe Webhooks::InstagramEventsJob do | ||||
|  | ||||
|       it 'creates incoming message in the instagram inbox' do | ||||
|         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( | ||||
|           return_object.with_indifferent_access | ||||
|           return_object_for(sender_id).with_indifferent_access | ||||
|         ) | ||||
|         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 | ||||
|         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( | ||||
|           return_object.with_indifferent_access | ||||
|           return_object_for(sender_id).with_indifferent_access | ||||
|         ) | ||||
|         instagram_webhook.perform_now(message_events[:standby][:entry]) | ||||
|  | ||||
| @@ -82,10 +83,11 @@ describe Webhooks::InstagramEventsJob do | ||||
|       it 'handle instagram unsend message event' do | ||||
|         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) | ||||
|         sender_id = message_events[:unsend][:entry][0][:messaging][0][:sender][:id] | ||||
|         allow(fb_object).to receive(:get_object).and_return( | ||||
|           { | ||||
|             name: 'Jane', | ||||
|             id: 'Sender-id-1', | ||||
|             id: sender_id, | ||||
|             account_id: instagram_messenger_inbox.account_id, | ||||
|             profile_pic: 'https://chatwoot-assets.local/sample.png' | ||||
|           }.with_indifferent_access | ||||
| @@ -104,8 +106,9 @@ describe Webhooks::InstagramEventsJob do | ||||
|  | ||||
|       it 'creates incoming message with attachments in the instagram inbox' do | ||||
|         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( | ||||
|           return_object.with_indifferent_access | ||||
|           return_object_for(sender_id).with_indifferent_access | ||||
|         ) | ||||
|         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 | ||||
|         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( | ||||
|           return_object.with_indifferent_access, | ||||
|           return_object_for(sender_id).with_indifferent_access, | ||||
|           { story: | ||||
|             { | ||||
|               mention: { | ||||
| @@ -165,8 +169,9 @@ describe Webhooks::InstagramEventsJob do | ||||
|  | ||||
|       it 'handles unsupported message' do | ||||
|         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( | ||||
|           return_object.with_indifferent_access | ||||
|           return_object_for(sender_id).with_indifferent_access | ||||
|         ) | ||||
|  | ||||
|         instagram_webhook.perform_now(message_events[:unsupported][:entry]) | ||||
| @@ -184,19 +189,22 @@ describe Webhooks::InstagramEventsJob do | ||||
|       before do | ||||
|         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( | ||||
|             status: 200, | ||||
|             body: { | ||||
|             body: proc { |request| | ||||
|               sender_id = request.uri.path.split('/').last.split('?').first | ||||
|               { | ||||
|                 name: 'Jane', | ||||
|                 username: 'some_user_name', | ||||
|                 profile_pic: 'https://chatwoot-assets.local/sample.png', | ||||
|               id: 'Sender-id-1', | ||||
|                 id: sender_id, | ||||
|                 follower_count: 100, | ||||
|                 is_user_follow_business: true, | ||||
|                 is_business_follow_user: true, | ||||
|                 is_verified_user: false | ||||
|             }.to_json, | ||||
|               }.to_json | ||||
|             }, | ||||
|             headers: { 'Content-Type' => 'application/json' } | ||||
|           ) | ||||
|       end | ||||
| @@ -289,14 +297,15 @@ describe Webhooks::InstagramEventsJob do | ||||
|         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) | ||||
|  | ||||
|         sender_id = message_events[:dm][:entry][0][:messaging][0][:sender][:id] | ||||
|         instagram_webhook.perform_now(message_events[:dm][:entry]) | ||||
|  | ||||
|         instagram_inbox.reload | ||||
|  | ||||
|         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.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.messages.count).to eq 1 | ||||
|   | ||||
| @@ -70,6 +70,7 @@ RSpec.configure do |config| | ||||
|   config.include SlackStubs | ||||
|   config.include FileUploadHelpers | ||||
|   config.include CsvSpecHelpers | ||||
|   config.include InstagramSpecHelpers | ||||
|   config.include Devise::Test::IntegrationHelpers, type: :request | ||||
|   config.include ActiveSupport::Testing::TimeHelpers | ||||
|   config.include ActionCable::TestHelper | ||||
|   | ||||
							
								
								
									
										20
									
								
								spec/support/instagram_spec_helpers.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								spec/support/instagram_spec_helpers.rb
									
									
									
									
									
										Normal 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 | ||||
		Reference in New Issue
	
	Block a user
	 Sojan Jose
					Sojan Jose