@@ -99,6 +99,53 @@ describe ::ContactInboxBuilder do
|
||||
end
|
||||
end
|
||||
|
||||
describe 'sms inbox' do
|
||||
let!(:sms_channel) { create(:channel_sms, account: account) }
|
||||
let!(:sms_inbox) { create(:inbox, channel: sms_channel, account: account) }
|
||||
|
||||
it 'does not create contact inbox when contact inbox already exists with the source id provided' do
|
||||
existing_contact_inbox = create(:contact_inbox, contact: contact, inbox: sms_inbox, source_id: contact.phone_number)
|
||||
contact_inbox = described_class.new(
|
||||
contact_id: contact.id,
|
||||
inbox_id: sms_inbox.id,
|
||||
source_id: contact.phone_number
|
||||
).perform
|
||||
|
||||
expect(contact_inbox.id).to be(existing_contact_inbox.id)
|
||||
end
|
||||
|
||||
it 'does not create contact inbox when contact inbox already exists with phone number and source id is not provided' do
|
||||
existing_contact_inbox = create(:contact_inbox, contact: contact, inbox: sms_inbox, source_id: contact.phone_number)
|
||||
contact_inbox = described_class.new(
|
||||
contact_id: contact.id,
|
||||
inbox_id: sms_inbox.id
|
||||
).perform
|
||||
|
||||
expect(contact_inbox.id).to be(existing_contact_inbox.id)
|
||||
end
|
||||
|
||||
it 'creates a new contact inbox when different source id is provided' do
|
||||
existing_contact_inbox = create(:contact_inbox, contact: contact, inbox: sms_inbox, source_id: contact.phone_number)
|
||||
contact_inbox = described_class.new(
|
||||
contact_id: contact.id,
|
||||
inbox_id: sms_inbox.id,
|
||||
source_id: '+224213223422'
|
||||
).perform
|
||||
|
||||
expect(contact_inbox.id).not_to be(existing_contact_inbox.id)
|
||||
expect(contact_inbox.source_id).not_to be('+224213223422')
|
||||
end
|
||||
|
||||
it 'creates a contact inbox with contact phone number when source id not provided and no contact inbox exists' do
|
||||
contact_inbox = described_class.new(
|
||||
contact_id: contact.id,
|
||||
inbox_id: sms_inbox.id
|
||||
).perform
|
||||
|
||||
expect(contact_inbox.source_id).not_to be(contact.phone_number)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'email inbox' do
|
||||
let!(:email_channel) { create(:channel_email, account: account) }
|
||||
let!(:email_inbox) { create(:inbox, channel: email_channel, account: account) }
|
||||
|
||||
@@ -309,6 +309,18 @@ RSpec.describe 'Inboxes API', type: :request do
|
||||
expect(response.body).to include('callback_webhook_url')
|
||||
end
|
||||
|
||||
it 'creates a sms inbox when administrator' do
|
||||
post "/api/v1/accounts/#{account.id}/inboxes",
|
||||
headers: admin.create_new_auth_token,
|
||||
params: { name: 'Sms Inbox',
|
||||
channel: { type: 'sms', phone_number: '+123456789', provider_config: { test: 'test' } } },
|
||||
as: :json
|
||||
|
||||
expect(response).to have_http_status(:success)
|
||||
expect(response.body).to include('Sms Inbox')
|
||||
expect(response.body).to include('+123456789')
|
||||
end
|
||||
|
||||
it 'creates the webwidget inbox that allow messages after conversation is resolved' do
|
||||
post "/api/v1/accounts/#{account.id}/inboxes",
|
||||
headers: admin.create_new_auth_token,
|
||||
|
||||
12
spec/controllers/webhooks/sms_controller_spec.rb
Normal file
12
spec/controllers/webhooks/sms_controller_spec.rb
Normal file
@@ -0,0 +1,12 @@
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe 'Webhooks::SmsController', type: :request do
|
||||
describe 'POST /webhooks/sms/{:phone_number}' do
|
||||
it 'call the sms events job with the params' do
|
||||
allow(Webhooks::SmsEventsJob).to receive(:perform_later)
|
||||
expect(Webhooks::SmsEventsJob).to receive(:perform_later)
|
||||
post '/webhooks/sms/123221321', params: { content: 'hello' }
|
||||
expect(response).to have_http_status(:success)
|
||||
end
|
||||
end
|
||||
end
|
||||
16
spec/factories/channel/channel_sms.rb
Normal file
16
spec/factories/channel/channel_sms.rb
Normal file
@@ -0,0 +1,16 @@
|
||||
FactoryBot.define do
|
||||
factory :channel_sms, class: 'Channel::Sms' do
|
||||
sequence(:phone_number) { |n| "+123456789#{n}1" }
|
||||
account
|
||||
provider_config do
|
||||
{ 'account_id' => '1',
|
||||
'application_id' => '1',
|
||||
'api_key' => '1',
|
||||
'api_secret' => '1' }
|
||||
end
|
||||
|
||||
after(:create) do |channel_sms|
|
||||
create(:inbox, channel: channel_sms, account: channel_sms.account)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -75,5 +75,14 @@ RSpec.describe SendReplyJob, type: :job do
|
||||
expect(process_service).to receive(:perform)
|
||||
described_class.perform_now(message.id)
|
||||
end
|
||||
|
||||
it 'calls ::Sms::SendOnSmsService when its sms message' do
|
||||
sms_channel = create(:channel_sms)
|
||||
message = create(:message, conversation: create(:conversation, inbox: sms_channel.inbox))
|
||||
allow(::Sms::SendOnSmsService).to receive(:new).with(message: message).and_return(process_service)
|
||||
expect(::Sms::SendOnSmsService).to receive(:new).with(message: message)
|
||||
expect(process_service).to receive(:perform)
|
||||
described_class.perform_now(message.id)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
56
spec/jobs/webhooks/sms_events_job_spec.rb
Normal file
56
spec/jobs/webhooks/sms_events_job_spec.rb
Normal file
@@ -0,0 +1,56 @@
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe Webhooks::SmsEventsJob, type: :job do
|
||||
subject(:job) { described_class.perform_later(params) }
|
||||
|
||||
let!(:sms_channel) { create(:channel_sms) }
|
||||
let!(:params) do
|
||||
{
|
||||
time: '2022-02-02T23:14:05.309Z',
|
||||
type: 'message-received',
|
||||
to: sms_channel.phone_number,
|
||||
description: 'Incoming message received',
|
||||
message: {
|
||||
'id': '3232420-2323-234324',
|
||||
'owner': sms_channel.phone_number,
|
||||
'applicationId': '2342349-324234d-32432432',
|
||||
'time': '2022-02-02T23:14:05.262Z',
|
||||
'segmentCount': 1,
|
||||
'direction': 'in',
|
||||
'to': [
|
||||
sms_channel.phone_number
|
||||
],
|
||||
'from': '+14234234234',
|
||||
'text': 'test message'
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
it 'enqueues the job' do
|
||||
expect { job }.to have_enqueued_job(described_class)
|
||||
.with(params)
|
||||
.on_queue('default')
|
||||
end
|
||||
|
||||
context 'when invalid params' do
|
||||
it 'returns nil when no bot_token' do
|
||||
expect(described_class.perform_now({})).to be_nil
|
||||
end
|
||||
|
||||
it 'returns nil when invalid type' do
|
||||
expect(described_class.perform_now({ type: 'invalid' })).to be_nil
|
||||
end
|
||||
end
|
||||
|
||||
context 'when valid params' do
|
||||
it 'calls Sms::IncomingMessageService' do
|
||||
process_service = double
|
||||
allow(Sms::IncomingMessageService).to receive(:new).and_return(process_service)
|
||||
allow(process_service).to receive(:perform)
|
||||
expect(Sms::IncomingMessageService).to receive(:new).with(inbox: sms_channel.inbox,
|
||||
params: params[:message].with_indifferent_access)
|
||||
expect(process_service).to receive(:perform)
|
||||
described_class.perform_now(params)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -78,6 +78,27 @@ RSpec.describe Campaign, type: :model do
|
||||
end
|
||||
end
|
||||
|
||||
context 'when SMS campaign' do
|
||||
let!(:sms_channel) { create(:channel_sms) }
|
||||
let!(:sms_inbox) { create(:inbox, channel: sms_channel) }
|
||||
let(:campaign) { build(:campaign, inbox: sms_inbox) }
|
||||
|
||||
it 'only saves campaign type as oneoff and wont leave scheduled_at empty' do
|
||||
campaign.campaign_type = 'ongoing'
|
||||
campaign.save!
|
||||
expect(campaign.reload.campaign_type).to eq 'one_off'
|
||||
expect(campaign.scheduled_at.present?).to eq true
|
||||
end
|
||||
|
||||
it 'calls sms service on trigger!' do
|
||||
sms_service = double
|
||||
expect(Sms::OneoffSmsCampaignService).to receive(:new).with(campaign: campaign).and_return(sms_service)
|
||||
expect(sms_service).to receive(:perform)
|
||||
campaign.save!
|
||||
campaign.trigger!
|
||||
end
|
||||
end
|
||||
|
||||
context 'when Website campaign' do
|
||||
let(:campaign) { build(:campaign) }
|
||||
|
||||
|
||||
@@ -15,8 +15,8 @@ describe Contacts::ContactableInboxesService do
|
||||
let!(:email_inbox) { create(:inbox, channel: email_channel, account: account) }
|
||||
let!(:api_channel) { create(:channel_api, account: account) }
|
||||
let!(:api_inbox) { create(:inbox, channel: api_channel, account: account) }
|
||||
let!(:website_channel) { create(:channel_widget, account: account) }
|
||||
let!(:website_inbox) { create(:inbox, channel: website_channel, account: account) }
|
||||
let!(:website_inbox) { create(:inbox, channel: create(:channel_widget, account: account), account: account) }
|
||||
let!(:sms_inbox) { create(:inbox, channel: create(:channel_sms, account: account), account: account) }
|
||||
|
||||
describe '#get' do
|
||||
it 'returns the contactable inboxes for the contact' do
|
||||
@@ -25,7 +25,7 @@ describe Contacts::ContactableInboxesService do
|
||||
expect(contactable_inboxes).to include({ source_id: contact.phone_number, inbox: twilio_sms_inbox })
|
||||
expect(contactable_inboxes).to include({ source_id: "whatsapp:#{contact.phone_number}", inbox: twilio_whatsapp_inbox })
|
||||
expect(contactable_inboxes).to include({ source_id: contact.email, inbox: email_inbox })
|
||||
expect(contactable_inboxes.pluck(:inbox)).to include(api_inbox)
|
||||
expect(contactable_inboxes).to include({ source_id: contact.phone_number, inbox: sms_inbox })
|
||||
end
|
||||
|
||||
it 'doest not return the non contactable inboxes for the contact' do
|
||||
|
||||
31
spec/services/sms/incoming_message_service_spec.rb
Normal file
31
spec/services/sms/incoming_message_service_spec.rb
Normal file
@@ -0,0 +1,31 @@
|
||||
require 'rails_helper'
|
||||
|
||||
describe Sms::IncomingMessageService do
|
||||
describe '#perform' do
|
||||
let!(:sms_channel) { create(:channel_sms) }
|
||||
|
||||
context 'when valid text message params' do
|
||||
it 'creates appropriate conversations, message and contacts' do
|
||||
params = {
|
||||
|
||||
'id': '3232420-2323-234324',
|
||||
'owner': sms_channel.phone_number,
|
||||
'applicationId': '2342349-324234d-32432432',
|
||||
'time': '2022-02-02T23:14:05.262Z',
|
||||
'segmentCount': 1,
|
||||
'direction': 'in',
|
||||
'to': [
|
||||
sms_channel.phone_number
|
||||
],
|
||||
'from': '+14234234234',
|
||||
'text': 'test message'
|
||||
|
||||
}.with_indifferent_access
|
||||
described_class.new(inbox: sms_channel.inbox, params: params).perform
|
||||
expect(sms_channel.inbox.conversations.count).not_to eq(0)
|
||||
expect(Contact.all.first.name).to eq('+1 423-423-4234')
|
||||
expect(sms_channel.inbox.messages.first.content).to eq('test message')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
47
spec/services/sms/oneoff_sms_campaign_service_spec.rb
Normal file
47
spec/services/sms/oneoff_sms_campaign_service_spec.rb
Normal file
@@ -0,0 +1,47 @@
|
||||
require 'rails_helper'
|
||||
|
||||
describe Sms::OneoffSmsCampaignService do
|
||||
subject(:sms_campaign_service) { described_class.new(campaign: campaign) }
|
||||
|
||||
let(:account) { create(:account) }
|
||||
let!(:sms_channel) { create(:channel_sms) }
|
||||
let!(:sms_inbox) { create(:inbox, channel: sms_channel) }
|
||||
let(:label1) { create(:label, account: account) }
|
||||
let(:label2) { create(:label, account: account) }
|
||||
let!(:campaign) do
|
||||
create(:campaign, inbox: sms_inbox, account: account,
|
||||
audience: [{ type: 'Label', id: label1.id }, { type: 'Label', id: label2.id }])
|
||||
end
|
||||
|
||||
describe 'perform' do
|
||||
before do
|
||||
stub_request(:post, 'https://messaging.bandwidth.com/api/v2/users/1/messages').to_return(
|
||||
status: 200,
|
||||
body: { 'id' => '1' }.to_json,
|
||||
headers: {}
|
||||
)
|
||||
end
|
||||
|
||||
it 'raises error if the campaign is completed' do
|
||||
campaign.completed!
|
||||
|
||||
expect { sms_campaign_service.perform }.to raise_error 'Completed Campaign'
|
||||
end
|
||||
|
||||
it 'raises error invalid campaign when its not a oneoff sms campaign' do
|
||||
campaign = create(:campaign)
|
||||
|
||||
expect { described_class.new(campaign: campaign).perform }.to raise_error "Invalid campaign #{campaign.id}"
|
||||
end
|
||||
|
||||
it 'send messages to contacts in the audience and marks the campaign completed' do
|
||||
contact_with_label1, contact_with_label2, contact_with_both_labels = FactoryBot.create_list(:contact, 3, :with_phone_number, account: account)
|
||||
contact_with_label1.update_labels([label1.title])
|
||||
contact_with_label2.update_labels([label2.title])
|
||||
contact_with_both_labels.update_labels([label1.title, label2.title])
|
||||
sms_campaign_service.perform
|
||||
assert_requested(:post, 'https://messaging.bandwidth.com/api/v2/users/1/messages', times: 3)
|
||||
expect(campaign.reload.completed?).to eq true
|
||||
end
|
||||
end
|
||||
end
|
||||
28
spec/services/sms/send_on_sms_service_spec.rb
Normal file
28
spec/services/sms/send_on_sms_service_spec.rb
Normal file
@@ -0,0 +1,28 @@
|
||||
require 'rails_helper'
|
||||
|
||||
describe Sms::SendOnSmsService do
|
||||
describe '#perform' do
|
||||
context 'when a valid message' do
|
||||
let(:sms_request) { double }
|
||||
let!(:sms_channel) { create(:channel_sms) }
|
||||
let!(:contact_inbox) { create(:contact_inbox, inbox: sms_channel.inbox, source_id: '+123456789') }
|
||||
let!(:conversation) { create(:conversation, contact_inbox: contact_inbox, inbox: sms_channel.inbox) }
|
||||
|
||||
it 'calls channel.send_message' do
|
||||
message = create(:message, message_type: :outgoing, content: 'test',
|
||||
conversation: conversation)
|
||||
allow(HTTParty).to receive(:post).and_return(sms_request)
|
||||
allow(sms_request).to receive(:success?).and_return(true)
|
||||
allow(sms_request).to receive(:parsed_response).and_return({ 'id' => '123456789' })
|
||||
expect(HTTParty).to receive(:post).with(
|
||||
'https://messaging.bandwidth.com/api/v2/users/1/messages',
|
||||
basic_auth: { username: '1', password: '1' },
|
||||
headers: { 'Content-Type' => 'application/json' },
|
||||
body: { 'to' => '+123456789', 'from' => sms_channel.phone_number, 'text' => 'test', 'applicationId' => '1' }.to_json
|
||||
)
|
||||
described_class.new(message: message).perform
|
||||
expect(message.reload.source_id).to eq('123456789')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user