2016-12-06 12:26:08 +00:00
|
|
|
|
require 'rails_helper'
|
|
|
|
|
|
|
|
|
|
RSpec.describe Cti::CallerId do
|
2019-01-14 09:30:51 +00:00
|
|
|
|
describe '.extract_numbers' do
|
|
|
|
|
context 'for strings containing arbitrary numbers (<6 digits long)' do
|
|
|
|
|
it 'returns an empty array' do
|
2019-01-17 06:57:03 +00:00
|
|
|
|
expect(described_class.extract_numbers(<<~INPUT.chomp)).to be_empty
|
|
|
|
|
some text
|
|
|
|
|
test 123
|
|
|
|
|
INPUT
|
2019-01-14 09:30:51 +00:00
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
context 'for strings containing a phone number with "(0)" after country code' do
|
|
|
|
|
it 'returns the number in an array, without the leading "(0)"' do
|
2019-01-17 06:57:03 +00:00
|
|
|
|
expect(described_class.extract_numbers(<<~INPUT.chomp)).to eq(['4930600000000'])
|
|
|
|
|
Lorem ipsum dolor sit amet, consectetuer +49 (0) 30 60 00 00 00-0
|
|
|
|
|
adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa.
|
|
|
|
|
Cum sociis natoque penatibus et magnis dis parturient montes, nascetur
|
|
|
|
|
ridiculus mus. Donec quam felis, ultricies nec, pellentesque eu,
|
|
|
|
|
pretium quis, sem. Nulla consequat massa quis enim. Donec pede
|
|
|
|
|
justo, fringilla vel.
|
|
|
|
|
INPUT
|
2019-01-14 09:30:51 +00:00
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
context 'for strings containing a phone number with leading 0 (no country code)' do
|
|
|
|
|
it 'returns the number in an array, using default country code (49)' do
|
2019-01-17 06:57:03 +00:00
|
|
|
|
expect(described_class.extract_numbers(<<~INPUT.chomp)).to eq(['4994221000'])
|
|
|
|
|
GS Oberalteich
|
|
|
|
|
Telefon 09422 1000
|
|
|
|
|
E-Mail:
|
|
|
|
|
INPUT
|
2019-01-14 09:30:51 +00:00
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
context 'for strings containing multiple phone numbers' do
|
|
|
|
|
it 'returns all numbers in an array' do
|
2019-01-17 06:57:03 +00:00
|
|
|
|
expect(described_class.extract_numbers(<<~INPUT.chomp)).to eq(%w[41812886393 41763467214])
|
|
|
|
|
Tel +41 81 288 63 93 / +41 76 346 72 14 ...
|
|
|
|
|
INPUT
|
2019-01-14 09:30:51 +00:00
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
context 'for strings containing US-formatted numbers' do
|
|
|
|
|
it 'returns the numbers in an array correctly' do
|
2019-01-17 06:57:03 +00:00
|
|
|
|
expect(described_class.extract_numbers(<<~INPUT.chomp)).to eq(%w[19494310000 19494310001])
|
|
|
|
|
P: +1 (949) 431 0000
|
|
|
|
|
F: +1 (949) 431 0001
|
|
|
|
|
W: http://znuny
|
|
|
|
|
INPUT
|
2019-01-14 09:30:51 +00:00
|
|
|
|
end
|
|
|
|
|
end
|
2016-12-06 12:26:08 +00:00
|
|
|
|
end
|
2018-07-23 08:24:23 +00:00
|
|
|
|
|
2019-01-14 09:30:51 +00:00
|
|
|
|
describe '.normalize_number' do
|
|
|
|
|
it 'does not modify digit-only strings (starting with 1-9)' do
|
|
|
|
|
expect(described_class.normalize_number('5754321')).to eq('5754321')
|
|
|
|
|
end
|
2018-07-23 08:24:23 +00:00
|
|
|
|
|
2019-01-14 09:30:51 +00:00
|
|
|
|
it 'strips whitespace' do
|
|
|
|
|
expect(described_class.normalize_number('622 32281')).to eq('62232281')
|
|
|
|
|
end
|
2018-07-23 08:24:23 +00:00
|
|
|
|
|
2019-01-14 09:30:51 +00:00
|
|
|
|
it 'strips hyphens' do
|
|
|
|
|
expect(described_class.normalize_number('1-888-407-4747')).to eq('18884074747')
|
2018-07-23 08:24:23 +00:00
|
|
|
|
end
|
|
|
|
|
|
2019-01-14 09:30:51 +00:00
|
|
|
|
it 'strips leading pluses' do
|
|
|
|
|
expect(described_class.normalize_number('+49 30 53 00 00 000')).to eq('4930530000000')
|
|
|
|
|
expect(described_class.normalize_number('+49 160 0000000')).to eq('491600000000')
|
|
|
|
|
end
|
2018-07-23 08:24:23 +00:00
|
|
|
|
|
2019-01-14 09:30:51 +00:00
|
|
|
|
it 'replaces a single leading zero with the default country code (49)' do
|
|
|
|
|
expect(described_class.normalize_number('092213212')).to eq('4992213212')
|
|
|
|
|
expect(described_class.normalize_number('0271233211')).to eq('49271233211')
|
|
|
|
|
expect(described_class.normalize_number('022 1234567')).to eq('49221234567')
|
|
|
|
|
expect(described_class.normalize_number('09 123 32112')).to eq('49912332112')
|
|
|
|
|
expect(described_class.normalize_number('021 2331231')).to eq('49212331231')
|
|
|
|
|
expect(described_class.normalize_number('021 321123123')).to eq('4921321123123')
|
|
|
|
|
expect(described_class.normalize_number('0150 12345678')).to eq('4915012345678')
|
|
|
|
|
expect(described_class.normalize_number('021-233-9123')).to eq('49212339123')
|
|
|
|
|
end
|
2018-07-23 08:24:23 +00:00
|
|
|
|
|
2019-01-14 09:30:51 +00:00
|
|
|
|
it 'strips two leading zeroes' do
|
|
|
|
|
expect(described_class.normalize_number('0049 1234 123456789')).to eq('491234123456789')
|
|
|
|
|
expect(described_class.normalize_number('0043 30 60 00 00 00-0')).to eq('4330600000000')
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
it 'strips leading zero from "(0x)" at start of number or after country code' do
|
|
|
|
|
expect(described_class.normalize_number('(09)1234321')).to eq('4991234321')
|
|
|
|
|
expect(described_class.normalize_number('+49 (0) 30 60 00 00 00-0')).to eq('4930600000000')
|
|
|
|
|
expect(described_class.normalize_number('0043 (0) 30 60 00 00 00-0')).to eq('4330600000000')
|
2018-07-23 08:24:23 +00:00
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2019-01-17 06:57:03 +00:00
|
|
|
|
describe '.lookup' do
|
|
|
|
|
context 'when given an unrecognized number' do
|
|
|
|
|
it 'returns an empty array' do
|
2019-04-15 01:41:17 +00:00
|
|
|
|
expect(described_class.lookup('1')).to eq([])
|
2019-01-17 06:57:03 +00:00
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
context 'when given a recognized number' do
|
2019-01-17 06:57:22 +00:00
|
|
|
|
subject!(:caller_id) { create(:caller_id, caller_id: number) }
|
2019-04-15 01:41:17 +00:00
|
|
|
|
|
2019-01-17 06:57:22 +00:00
|
|
|
|
let(:number) { '1234567890' }
|
2019-01-17 06:57:03 +00:00
|
|
|
|
|
|
|
|
|
it 'returns an array with the corresponding CallerId' do
|
2019-04-15 01:41:17 +00:00
|
|
|
|
expect(described_class.lookup(number)).to match_array([caller_id])
|
2019-01-17 06:57:03 +00:00
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
context 'shared by multiple CallerIds' do
|
2019-01-17 06:57:22 +00:00
|
|
|
|
context '(for different users)' do
|
|
|
|
|
subject!(:caller_ids) do
|
2019-07-04 11:16:55 +00:00
|
|
|
|
[create(:caller_id, caller_id: number, user: create(:user)),
|
|
|
|
|
create(:caller_id, caller_id: number, user: create(:user))]
|
2019-01-17 06:57:22 +00:00
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
it 'returns all corresponding CallerId records' do
|
2019-04-15 01:41:17 +00:00
|
|
|
|
expect(described_class.lookup(number)).to match_array(caller_ids)
|
2019-01-17 06:57:22 +00:00
|
|
|
|
end
|
2019-01-17 06:57:03 +00:00
|
|
|
|
end
|
|
|
|
|
|
2019-01-17 06:57:22 +00:00
|
|
|
|
context '(for the same user)' do
|
|
|
|
|
subject!(:caller_ids) { create_list(:caller_id, 2, caller_id: number) }
|
|
|
|
|
|
|
|
|
|
it 'returns one corresponding CallerId record by MAX(id)' do
|
2019-04-15 01:41:17 +00:00
|
|
|
|
expect(described_class.lookup(number)).to match_array(caller_ids.last(1))
|
2019-01-17 06:57:22 +00:00
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
context '(some for the same user, some for another)' do
|
|
|
|
|
subject!(:caller_ids) do
|
2019-07-04 11:16:55 +00:00
|
|
|
|
[*create_list(:caller_id, 2, caller_id: number, user: create(:user)),
|
|
|
|
|
create(:caller_id, caller_id: number, user: create(:user))]
|
2019-01-17 06:57:22 +00:00
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
it 'returns one CallerId record per unique #user_id, by MAX(id)' do
|
2019-04-15 01:41:17 +00:00
|
|
|
|
expect(described_class.lookup(number)).to match_array(caller_ids.last(2))
|
2019-01-17 06:57:22 +00:00
|
|
|
|
end
|
2019-01-17 06:57:03 +00:00
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
describe '.rebuild' do
|
|
|
|
|
context 'when a User record contains a valid phone number' do
|
2020-06-19 09:17:18 +00:00
|
|
|
|
let!(:user) { create(:agent, phone: '+49 123 456') }
|
2019-01-17 06:57:03 +00:00
|
|
|
|
|
|
|
|
|
context 'and no corresponding CallerId exists' do
|
|
|
|
|
it 'generates a CallerId record (with #level "known")' do
|
2019-04-15 01:41:17 +00:00
|
|
|
|
described_class.destroy_all # CallerId already generated in User callback
|
2019-01-17 06:57:03 +00:00
|
|
|
|
|
2019-04-15 01:41:17 +00:00
|
|
|
|
expect { described_class.rebuild }
|
|
|
|
|
.to change { described_class.exists?(user_id: user.id, caller_id: '49123456', level: 'known') }
|
2019-01-17 06:57:03 +00:00
|
|
|
|
.to(true)
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
it 'does not create duplicate CallerId records' do
|
2019-04-15 01:41:17 +00:00
|
|
|
|
expect { described_class.rebuild }.not_to change(described_class, :count)
|
2019-01-17 06:57:03 +00:00
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
context 'when no User exists for a given CallerId record' do
|
|
|
|
|
subject!(:caller_id) { create(:caller_id) }
|
|
|
|
|
|
|
|
|
|
it 'deletes the CallerId record' do
|
2019-04-15 01:41:17 +00:00
|
|
|
|
expect { described_class.rebuild }
|
|
|
|
|
.to change { described_class.exists?(caller_id.id) }.to(false)
|
2019-01-17 06:57:03 +00:00
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
context 'when two User records contains the same valid phone number' do
|
2020-06-19 09:17:18 +00:00
|
|
|
|
let!(:users) { create_list(:agent, 2, phone: '+49 123 456') }
|
2019-01-17 06:57:03 +00:00
|
|
|
|
|
|
|
|
|
it 'generates two corresponding CallerId records (with #level "known")' do
|
2019-04-15 01:41:17 +00:00
|
|
|
|
described_class.destroy_all # CallerId already generated in User callback
|
2019-01-17 06:57:03 +00:00
|
|
|
|
|
2019-04-15 01:41:17 +00:00
|
|
|
|
expect { described_class.rebuild }
|
|
|
|
|
.to change { described_class.exists?(user_id: users.first.id, caller_id: '49123456', level: 'known') }
|
2019-01-17 06:57:03 +00:00
|
|
|
|
.to(true)
|
2019-04-15 01:41:17 +00:00
|
|
|
|
.and change { described_class.exists?(user_id: users.last.id, caller_id: '49123456', level: 'known') }
|
2019-01-17 06:57:03 +00:00
|
|
|
|
.to(true)
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
context 'when an Article record contains a valid phone number in its body' do
|
|
|
|
|
let!(:article) { create(:ticket_article, body: <<~BODY, sender_name: sender_name) }
|
|
|
|
|
some message
|
|
|
|
|
Fon (GEL): +49 123-456 Mi-Fr
|
|
|
|
|
BODY
|
|
|
|
|
|
|
|
|
|
context 'and comes from a customer' do
|
|
|
|
|
let(:sender_name) { 'Customer' }
|
|
|
|
|
|
|
|
|
|
it 'generates a CallerId record (with #level "maybe")' do
|
2019-04-15 01:41:17 +00:00
|
|
|
|
described_class.destroy_all # CallerId already generated in Article observer job
|
2019-01-17 06:57:03 +00:00
|
|
|
|
|
2019-04-15 01:41:17 +00:00
|
|
|
|
expect { described_class.rebuild }
|
|
|
|
|
.to change { described_class.exists?(user_id: article.created_by_id, caller_id: '49123456', level: 'maybe') }
|
2019-01-17 06:57:03 +00:00
|
|
|
|
.to(true)
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
context 'and comes from an Agent' do
|
|
|
|
|
let(:sender_name) { 'Agent' }
|
|
|
|
|
|
|
|
|
|
it 'does not generate a CallerId record' do
|
2019-04-15 01:41:17 +00:00
|
|
|
|
expect { described_class.rebuild }
|
|
|
|
|
.not_to change { described_class.exists?(caller_id: '49123456') }
|
2019-01-17 06:57:03 +00:00
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2019-01-17 06:57:22 +00:00
|
|
|
|
describe '.maybe_add' do
|
|
|
|
|
let(:attributes) { attributes_for(:caller_id) }
|
|
|
|
|
|
|
|
|
|
it 'wraps .find_or_initialize_by (passing only five defining attributes)' do
|
2019-04-15 01:41:17 +00:00
|
|
|
|
expect(described_class)
|
2019-01-17 06:57:22 +00:00
|
|
|
|
.to receive(:find_or_initialize_by)
|
|
|
|
|
.with(attributes.slice(:caller_id, :level, :object, :o_id, :user_id))
|
|
|
|
|
.and_call_original
|
|
|
|
|
|
2019-04-15 01:41:17 +00:00
|
|
|
|
described_class.maybe_add(attributes)
|
2019-01-17 06:57:22 +00:00
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
context 'if no matching record found' do
|
|
|
|
|
it 'adds given #comment attribute' do
|
2019-04-15 01:41:17 +00:00
|
|
|
|
expect { described_class.maybe_add(attributes.merge(comment: 'foo')) }
|
|
|
|
|
.to change(described_class, :count).by(1)
|
2019-01-17 06:57:22 +00:00
|
|
|
|
|
2019-04-15 01:41:17 +00:00
|
|
|
|
expect(described_class.last.comment).to eq('foo')
|
2019-01-17 06:57:22 +00:00
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
context 'if matching record found' do
|
|
|
|
|
let(:attributes) { caller_id.attributes.symbolize_keys }
|
|
|
|
|
let(:caller_id) { create(:caller_id) }
|
|
|
|
|
|
|
|
|
|
it 'ignores given #comment attribute' do
|
2019-04-15 01:41:17 +00:00
|
|
|
|
expect(described_class.maybe_add(attributes.merge(comment: 'foo')))
|
2019-01-17 06:57:22 +00:00
|
|
|
|
.to eq(caller_id)
|
|
|
|
|
|
|
|
|
|
expect(caller_id.comment).to be_blank
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2019-07-09 22:07:32 +00:00
|
|
|
|
describe '.known_agents_by_number' do
|
|
|
|
|
context 'with known agent caller_id' do
|
2020-06-19 09:17:18 +00:00
|
|
|
|
let!(:agent1) { create(:agent, phone: '0123456') }
|
|
|
|
|
let!(:agent2) { create(:agent, phone: '0123457') }
|
2019-07-09 22:07:32 +00:00
|
|
|
|
|
|
|
|
|
it 'gives matching agents' do
|
|
|
|
|
expect(described_class.known_agents_by_number('49123456'))
|
2020-06-19 09:17:18 +00:00
|
|
|
|
.to match_array([agent1])
|
2019-07-09 22:07:32 +00:00
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
context 'with known customer caller_id' do
|
2020-06-19 09:17:18 +00:00
|
|
|
|
let!(:customer1) { create(:customer, phone: '0123456') }
|
2019-07-09 22:07:32 +00:00
|
|
|
|
|
|
|
|
|
it 'returns an empty array' do
|
|
|
|
|
expect(described_class.known_agents_by_number('49123456')).to eq([])
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
context 'with maybe caller_id' do
|
|
|
|
|
let(:ticket1) do
|
2020-06-19 09:17:18 +00:00
|
|
|
|
create(:ticket_article, created_by_id: customer2.id, body: 'some text 0123457') # create ticket
|
2019-07-09 22:07:32 +00:00
|
|
|
|
Observer::Transaction.commit
|
|
|
|
|
Scheduler.worker(true)
|
|
|
|
|
end
|
2020-06-19 09:17:18 +00:00
|
|
|
|
let!(:customer2) { create(:customer) }
|
2019-07-09 22:07:32 +00:00
|
|
|
|
|
|
|
|
|
it 'returns an empty array' do
|
|
|
|
|
expect(described_class.known_agents_by_number('49123457').count).to eq(0)
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2019-01-14 09:30:51 +00:00
|
|
|
|
describe 'callbacks' do
|
|
|
|
|
subject!(:caller_id) { build(:cti_caller_id, caller_id: phone) }
|
2019-04-15 01:41:17 +00:00
|
|
|
|
|
2019-01-14 09:30:51 +00:00
|
|
|
|
let(:phone) { '1234567890' }
|
|
|
|
|
|
|
|
|
|
describe 'on creation' do
|
|
|
|
|
it 'adopts CTI Logs from same number (via UpdateCtiLogsByCallerJob)' do
|
|
|
|
|
expect(UpdateCtiLogsByCallerJob).to receive(:perform_later)
|
|
|
|
|
|
|
|
|
|
caller_id.save
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
it 'splits job into fg and bg (for more responsive UI – see #2057)' do
|
|
|
|
|
expect(UpdateCtiLogsByCallerJob).to receive(:perform_now).with(phone, limit: 20)
|
|
|
|
|
expect(UpdateCtiLogsByCallerJob).to receive(:perform_later).with(phone, limit: 40, offset: 20)
|
|
|
|
|
|
|
|
|
|
caller_id.save
|
|
|
|
|
end
|
2020-06-19 16:17:34 +00:00
|
|
|
|
|
|
|
|
|
it 'skips jobs on import_mode true' do
|
|
|
|
|
Setting.set('import_mode', true)
|
|
|
|
|
|
|
|
|
|
expect(UpdateCtiLogsByCallerJob).not_to receive(:perform_now)
|
|
|
|
|
expect(UpdateCtiLogsByCallerJob).not_to receive(:perform_later)
|
|
|
|
|
|
|
|
|
|
caller_id.save
|
|
|
|
|
end
|
2019-01-14 09:30:51 +00:00
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
describe 'on destruction' do
|
|
|
|
|
before { caller_id.save }
|
|
|
|
|
|
|
|
|
|
it 'orphans CTI Logs from same number (via UpdateCtiLogsByCallerJob)' do
|
|
|
|
|
expect(UpdateCtiLogsByCallerJob).to receive(:perform_later).with(phone)
|
2018-07-23 08:24:23 +00:00
|
|
|
|
|
2019-01-14 09:30:51 +00:00
|
|
|
|
caller_id.destroy
|
|
|
|
|
end
|
2018-07-23 08:24:23 +00:00
|
|
|
|
end
|
|
|
|
|
end
|
2016-12-06 12:26:08 +00:00
|
|
|
|
end
|