log_spec.rb 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498
  1. require 'rails_helper'
  2. RSpec.describe Cti::Log do
  3. subject(:user) { create(:user, roles: Role.where(name: 'Agent'), phone: phone) }
  4. let(:phone) { '' }
  5. let(:log) { create(:'cti/log') }
  6. describe '.log' do
  7. it 'returns a hash with :list and :assets keys' do
  8. expect(described_class.log(user)).to match(hash_including(:list, :assets))
  9. end
  10. context 'when over 60 Log records exist' do
  11. subject!(:cti_logs) do
  12. 61.times.map do |_i| # rubocop:disable Performance/TimesMap
  13. travel 1.second
  14. create(:'cti/log')
  15. end
  16. end
  17. it 'returns the 60 latest ones in the :list key' do
  18. expect(described_class.log(user)[:list]).to match_array(cti_logs.last(60))
  19. end
  20. end
  21. context 'when Log records have arrays of CallerId attributes in #preferences[:to] / #preferences[:from]' do
  22. subject!(:cti_log) { create(:'cti/log', preferences: { from: [caller_id] }) }
  23. let(:caller_id) { create(:caller_id) }
  24. let(:caller_user) { User.find_by(id: caller_id.user_id) }
  25. it 'returns a hash of the CallerId Users and their assets in the :assets key' do
  26. expect(described_class.log(user)[:assets]).to eq(caller_user.assets({}))
  27. end
  28. end
  29. context 'when a notify map is defined' do
  30. subject!(:cti_logs) do
  31. [create(:'cti/log', queue: 'queue0'),
  32. create(:'cti/log', queue: 'queue2'),
  33. create(:'cti/log', queue: 'queue3'),
  34. create(:'cti/log', queue: 'queue4')]
  35. end
  36. before do
  37. cti_config = Setting.get('cti_config')
  38. cti_config[:notify_map] = [ { queue: 'queue4', user_ids: [user.id.to_s] } ]
  39. Setting.set('cti_config', cti_config)
  40. end
  41. it 'returns one matching log record' do
  42. expect(described_class.log(user)[:list]).to match_array([cti_logs[3]])
  43. end
  44. end
  45. end
  46. describe '.push_caller_list_update?' do
  47. let!(:existing_logs) { create_list(:'cti/log', 60) }
  48. let(:log) { create(:'cti/log') }
  49. context 'when given log is older than existing logs' do
  50. before { travel(-10.seconds) }
  51. it 'return false' do
  52. expect(described_class.push_caller_list_update?(log)).to eq false
  53. end
  54. end
  55. context 'when given log is newer than existing logs' do
  56. before { travel(10.seconds) }
  57. it 'return true' do
  58. expect(described_class.push_caller_list_update?(log)).to eq true
  59. end
  60. end
  61. end
  62. describe '.process' do
  63. let(:attributes) do
  64. {
  65. 'cause' => cause,
  66. 'event' => event,
  67. 'user' => 'user 1',
  68. 'from' => '49123456',
  69. 'to' => '49123457',
  70. 'call_id' => '1',
  71. 'direction' => 'in',
  72. }
  73. end
  74. let(:cause) { '' }
  75. context 'for event "newCall"' do
  76. let(:event) { 'newCall' }
  77. context 'with unrecognized "call_id"' do
  78. it 'creates a new Log record' do
  79. expect { described_class.process(attributes) }
  80. .to change(described_class, :count).by(1)
  81. expect(described_class.last.attributes)
  82. .to include(
  83. 'call_id' => '1',
  84. 'state' => 'newCall',
  85. 'done' => false,
  86. 'queue' => '49123457',
  87. 'from' => '49123456',
  88. 'from_comment' => nil,
  89. 'from_pretty' => '49123456',
  90. 'start_at' => nil,
  91. 'end_at' => nil,
  92. 'to' => '49123457',
  93. 'to_comment' => 'user 1',
  94. 'to_pretty' => '49123457'
  95. )
  96. end
  97. context 'for direction "in", with a CallerId record matching the "from" number' do
  98. let!(:caller_id) { create(:caller_id, caller_id: '49123456') }
  99. before { attributes.merge!('direction' => 'in') }
  100. it 'saves that CallerId’s attributes in the new Log’s #preferences[:from] attribute' do
  101. described_class.process(attributes)
  102. expect(described_class.last.preferences[:from].first)
  103. .to include(caller_id.attributes.except('created_at')) # Checking equality of Time objects is error-prone
  104. end
  105. end
  106. context 'for direction "out", with a CallerId record matching the "to" number' do
  107. let!(:caller_id) { create(:caller_id, caller_id: '49123457') }
  108. before { attributes.merge!('direction' => 'out') }
  109. it 'saves that CallerId’s attributes in the new Log’s #preferences[:to] attribute' do
  110. described_class.process(attributes)
  111. expect(described_class.last.preferences[:to].first)
  112. .to include(caller_id.attributes.except('created_at')) # Checking equality of Time objects is error-prone
  113. end
  114. end
  115. end
  116. context 'with recognized "call_id"' do
  117. before { create(:'cti/log', call_id: '1') }
  118. it 'raises an error' do
  119. expect { described_class.process(attributes) }.to raise_error(/call_id \S+ already exists!/)
  120. end
  121. end
  122. end
  123. context 'for event "answer"' do
  124. let(:event) { 'answer' }
  125. context 'with unrecognized "call_id"' do
  126. it 'raises an error' do
  127. expect { described_class.process(attributes) }.to raise_error(/No such call_id/)
  128. end
  129. end
  130. context 'with recognized "call_id"' do
  131. context 'for Log with #state "newCall"' do
  132. let(:log) { create(:'cti/log', call_id: 1, state: 'newCall', done: false) }
  133. it 'returns early with no changes' do
  134. expect { described_class.process(attributes) }
  135. .to change { log.reload.state }.to('answer')
  136. .and change { log.reload.done }.to(true)
  137. end
  138. end
  139. context 'for Log with #state "hangup"' do
  140. let(:log) { create(:'cti/log', call_id: 1, state: 'hangup', done: false) }
  141. it 'returns early with no changes' do
  142. expect { described_class.process(attributes) }
  143. .not_to change(log, :reload)
  144. end
  145. end
  146. end
  147. end
  148. context 'for event "hangup"' do
  149. let(:event) { 'hangup' }
  150. context 'with unrecognized "call_id"' do
  151. it 'raises an error' do
  152. expect { described_class.process(attributes) }.to raise_error(/No such call_id/)
  153. end
  154. end
  155. context 'with recognized "call_id"' do
  156. context 'for Log with #state "newCall"' do
  157. let(:log) { create(:'cti/log', call_id: 1, state: 'newCall', done: false) }
  158. it 'sets attributes #state: "hangup", #done: false' do
  159. expect { described_class.process(attributes) }
  160. .to change { log.reload.state }.to('hangup')
  161. .and not_change { log.reload.done }
  162. end
  163. context 'when call is forwarded' do
  164. let(:cause) { 'forwarded' }
  165. it 'sets attributes #state: "hangup", #done: true' do
  166. expect { described_class.process(attributes) }
  167. .to change { log.reload.state }.to('hangup')
  168. .and change { log.reload.done }.to(true)
  169. end
  170. end
  171. end
  172. context 'for Log with #state "answer"' do
  173. let(:log) { create(:'cti/log', call_id: 1, state: 'answer', done: true) }
  174. it 'sets attributes #state: "hangup"' do
  175. expect { described_class.process(attributes) }
  176. .to change { log.reload.state }.to('hangup')
  177. .and not_change { log.reload.done }
  178. end
  179. context 'when call is sent to voicemail' do
  180. before { log.update(to_comment: 'voicemail') }
  181. it 'sets attributes #state: "hangup", #done: false' do
  182. expect { described_class.process(attributes) }
  183. .to change { log.reload.state }.to('hangup')
  184. .and change { log.reload.done }.to(false)
  185. end
  186. end
  187. end
  188. end
  189. end
  190. context 'for preferences.from verification' do
  191. subject(:log) do
  192. described_class.process(attributes)
  193. end
  194. let(:customer_user_of_ticket) { create(:customer_user) }
  195. let(:ticket_sample) do
  196. create(:ticket_article, created_by_id: customer_user_of_ticket.id, body: 'some text 0123457')
  197. Observer::Transaction.commit
  198. Scheduler.worker(true)
  199. end
  200. let(:caller_id) { '0123456' }
  201. let(:attributes) do
  202. {
  203. 'cause' => '',
  204. 'event' => 'newCall',
  205. 'user' => 'user 1',
  206. 'from' => caller_id,
  207. 'to' => '49123450',
  208. 'call_id' => '1',
  209. 'direction' => 'in',
  210. }
  211. end
  212. context 'with now related customer' do
  213. it 'gives no caller information' do
  214. expect(log.preferences[:from]).to eq(nil)
  215. end
  216. end
  217. context 'with related known customer' do
  218. let!(:customer_user) { create(:customer_user, phone: '0123456') }
  219. it 'gives caller information' do
  220. expect(log.preferences[:from].count).to eq(1)
  221. expect(log.preferences[:from].first)
  222. .to include(
  223. 'level' => 'known',
  224. 'user_id' => customer_user.id,
  225. )
  226. end
  227. end
  228. context 'with related known customers' do
  229. let!(:customer_user1) { create(:customer_user, phone: '0123456') }
  230. let!(:customer_user2) { create(:customer_user, phone: '0123456') }
  231. it 'gives caller information' do
  232. expect(log.preferences[:from].count).to eq(2)
  233. expect(log.preferences[:from].first)
  234. .to include(
  235. 'level' => 'known',
  236. 'user_id' => customer_user2.id,
  237. )
  238. end
  239. end
  240. context 'with related maybe customer' do
  241. let(:caller_id) { '0123457' }
  242. let!(:ticket) { ticket_sample }
  243. it 'gives caller information' do
  244. expect(log.preferences[:from].count).to eq(1)
  245. expect(log.preferences[:from].first)
  246. .to include(
  247. 'level' => 'maybe',
  248. 'user_id' => customer_user_of_ticket.id,
  249. )
  250. end
  251. end
  252. context 'with related maybe and known customer' do
  253. let(:caller_id) { '0123457' }
  254. let!(:customer) { create(:customer_user, phone: '0123457') }
  255. let!(:ticket) { ticket_sample }
  256. it 'gives caller information' do
  257. expect(log.preferences[:from].count).to eq(1)
  258. expect(log.preferences[:from].first)
  259. .to include(
  260. 'level' => 'known',
  261. 'user_id' => customer.id,
  262. )
  263. end
  264. end
  265. end
  266. end
  267. describe 'Callbacks -' do
  268. describe 'Updating agent sessions:' do
  269. before { allow(Sessions).to receive(:send_to).with(any_args) }
  270. context 'on creation' do
  271. it 'pushes "cti_list_push" event' do
  272. User.with_permissions('cti.agent').each do |u|
  273. expect(Sessions).to receive(:send_to).with(u.id, { event: 'cti_list_push' })
  274. end
  275. create(:cti_log)
  276. end
  277. context 'with over 60 existing Log records' do
  278. before { create_list(:cti_log, 60) }
  279. it '(always) pushes "cti_list_push" event' do
  280. User.with_permissions('cti.agent').each do |u|
  281. expect(Sessions).to receive(:send_to).with(u.id, { event: 'cti_list_push' })
  282. end
  283. create(:cti_log)
  284. end
  285. end
  286. end
  287. context 'on update' do
  288. subject!(:log) { create(:cti_log) }
  289. it 'pushes "cti_list_push" event' do
  290. User.with_permissions('cti.agent').each do |u|
  291. expect(Sessions).to receive(:send_to).with(u.id, { event: 'cti_list_push' })
  292. end
  293. log.touch
  294. end
  295. context 'when among the latest 60 Log records' do
  296. before { create_list(:cti_log, 59) }
  297. it 'pushes "cti_list_push" event' do
  298. User.with_permissions('cti.agent').each do |u|
  299. expect(Sessions).to receive(:send_to).with(u.id, { event: 'cti_list_push' })
  300. end
  301. log.touch
  302. end
  303. end
  304. context 'when not among the latest 60 Log records' do
  305. before { create_list(:cti_log, 60) }
  306. it 'does NOT push "cti_list_push" event' do
  307. User.with_permissions('cti.agent').each do |u|
  308. expect(Sessions).not_to receive(:send_to).with(u.id, { event: 'cti_list_push' })
  309. end
  310. log.touch
  311. end
  312. end
  313. end
  314. end
  315. end
  316. describe '#from_pretty' do
  317. context 'with complete, E164 international numbers' do
  318. subject(:log) { create(:cti_log, from: '4930609854180') }
  319. it 'gives the number in prettified format' do
  320. expect(log.from_pretty).to eq('+49 30 609854180')
  321. end
  322. end
  323. context 'with private network numbers' do
  324. subject(:log) { create(:cti_log, from: '007') }
  325. it 'gives the number unaltered' do
  326. expect(log.from_pretty).to eq('007')
  327. end
  328. end
  329. end
  330. describe '#to_pretty' do
  331. context 'with complete, E164 international numbers' do
  332. subject(:log) { create(:cti_log, to: '4930609811111') }
  333. it 'gives the number in prettified format' do
  334. expect(log.to_pretty).to eq('+49 30 609811111')
  335. end
  336. end
  337. context 'with private network numbers' do
  338. subject(:log) { create(:cti_log, to: '008') }
  339. it 'gives the number unaltered' do
  340. expect(log.to_pretty).to eq('008')
  341. end
  342. end
  343. end
  344. describe '.queues_of_user' do
  345. context 'without notify_map and no own phone number' do
  346. it 'gives an empty array' do
  347. expect(described_class.queues_of_user(user, Setting.get('cti_config'))).to eq([])
  348. end
  349. end
  350. context 'with notify_map and no own phone number' do
  351. before do
  352. cti_config = Setting.get('cti_config')
  353. cti_config[:notify_map] = [ { queue: 'queue4', user_ids: [user.id.to_s] } ]
  354. Setting.set('cti_config', cti_config)
  355. end
  356. it 'gives an array with queue' do
  357. expect(described_class.queues_of_user(user, Setting.get('cti_config'))).to eq(['queue4'])
  358. end
  359. end
  360. context 'with notify_map and with own phone number' do
  361. let(:phone) { '012345678' }
  362. before do
  363. cti_config = Setting.get('cti_config')
  364. cti_config[:notify_map] = [ { queue: 'queue4', user_ids: [user.id.to_s] } ]
  365. Setting.set('cti_config', cti_config)
  366. end
  367. it 'gives an array with queue and phone number' do
  368. expect(described_class.queues_of_user(user, Setting.get('cti_config'))).to eq(%w[queue4 4912345678])
  369. end
  370. end
  371. end
  372. describe '#best_customer_id_of_log_entry' do
  373. subject(:log1) do
  374. described_class.process(
  375. 'event' => 'newCall',
  376. 'user' => 'user 1',
  377. 'from' => '01234599',
  378. 'to' => '49123450',
  379. 'call_id' => '1',
  380. 'direction' => 'in',
  381. )
  382. end
  383. let!(:agent1) { create(:agent_user, phone: '01234599') }
  384. let!(:customer2) { create(:customer_user, phone: '') }
  385. let!(:ticket_article1) { create(:ticket_article, created_by_id: customer2.id, body: 'some text 01234599') }
  386. context 'with agent1 (known), customer1 (known) and customer2 (maybe)' do
  387. let!(:customer1) { create(:customer_user, phone: '01234599') }
  388. it 'gives customer1' do
  389. expect(log1.best_customer_id_of_log_entry).to eq(customer1.id)
  390. end
  391. end
  392. context 'with agent1 (known) and customer2 (maybe)' do
  393. it 'gives customer2' do
  394. expect(log1.best_customer_id_of_log_entry).to eq(agent1.id)
  395. end
  396. end
  397. end
  398. describe '#to_json' do
  399. it 'includes virtual attributes' do
  400. expect(log.as_json).to include('from_pretty', 'to_pretty')
  401. end
  402. end
  403. end