1# frozen_string_literal: true
2
3require "rack/test"
4require "test_helper"
5require "bwmsgsv2_repo"
6require "customer_repo"
7require_relative "../web"
8
9ExpiringLock::REDIS = Minitest::Mock.new
10Customer::BLATHER = Minitest::Mock.new
11CustomerFwd::BANDWIDTH_VOICE = Minitest::Mock.new
12Web::BANDWIDTH_VOICE = Minitest::Mock.new
13LowBalance::AutoTopUp::Transaction = Minitest::Mock.new
14
15class WebTest < Minitest::Test
16 include Rack::Test::Methods
17
18 def app
19 Web.opts[:customer_repo] = CustomerRepo.new(
20 redis: FakeRedis.new(
21 "jmp_customer_jid-customerid" => "customer@example.com",
22 "catapult_jid-+15551234567" => "customer_customerid@component",
23 "jmp_customer_jid-customerid_low" => "customer@example.com",
24 "catapult_jid-+15551234560" => "customer_customerid_low@component",
25 "jmp_customer_jid-customerid_topup" => "customer@example.com",
26 "jmp_customer_auto_top_up_amount-customerid_topup" => "15",
27 "jmp_customer_monthly_overage_limit-customerid_topup" => "99999",
28 "catapult_jid-+15551234562" => "customer_customerid_topup@component",
29 "jmp_customer_jid-customerid_limit" => "customer@example.com",
30 "catapult_jid-+15551234561" => "customer_customerid_limit@component"
31 ),
32 db: FakeDB.new(
33 ["customerid"] => [{
34 "balance" => BigDecimal(10),
35 "plan_name" => "test_usd",
36 "expires_at" => Time.now + 100
37 }],
38 ["customerid_low"] => [{
39 "balance" => BigDecimal("0.01"),
40 "plan_name" => "test_usd",
41 "expires_at" => Time.now + 100
42 }],
43 ["customerid_topup"] => [{
44 "balance" => BigDecimal("0.01"),
45 "plan_name" => "test_usd",
46 "expires_at" => Time.now + 100
47 }],
48 ["customerid_limit"] => [{
49 "balance" => BigDecimal(10),
50 "plan_name" => "test_usd",
51 "expires_at" => Time.now + 100
52 }]
53 ),
54 sgx_repo: Bwmsgsv2Repo.new(
55 redis: FakeRedis.new(
56 "catapult_fwd-+15551234567" => "xmpp:customer@example.com",
57 "catapult_fwd_timeout-customer_customerid@component" => "30",
58 "catapult_fwd-+15551234560" => "xmpp:customer@example.com",
59 "catapult_fwd_timeout-customer_customerid_low@component" => "30",
60 "catapult_fwd-+15551234561" => "xmpp:customer@example.com",
61 "catapult_fwd_timeout-customer_customerid_limit@component" => "30"
62 ),
63 ibr_repo: FakeIBRRepo.new(
64 "sgx" => {
65 "customer_customerid@component" =>
66 Blather::Stanza::Iq::IBR.new.tap do |ibr|
67 ibr.phone = "+15551234567"
68 end,
69 "customer_customerid_low@component" =>
70 Blather::Stanza::Iq::IBR.new.tap do |ibr|
71 ibr.phone = "+15551234567"
72 end,
73 "customer_customerid_topup@component" =>
74 Blather::Stanza::Iq::IBR.new.tap do |ibr|
75 ibr.phone = "+15551234567"
76 end,
77 "customer_customerid_limit@component" =>
78 Blather::Stanza::Iq::IBR.new.tap do |ibr|
79 ibr.phone = "+15551234567"
80 end
81 }
82 )
83 )
84 )
85 Web.opts[:call_attempt_repo] = CallAttemptRepo.new(
86 redis: FakeRedis.new,
87 db: FakeDB.new(
88 ["test_usd", "+15557654321", :outbound] => [{ "rate" => 0.01 }],
89 ["test_usd", "+15557654321", :inbound] => [{ "rate" => 0.01 }],
90 ["customerid_limit"] => FakeDB::MultiResult.new(
91 [{ "a" => 1000 }],
92 [{ "settled_amount" => 15 }]
93 ),
94 ["customerid_low"] => FakeDB::MultiResult.new(
95 [{ "a" => 1000 }],
96 [{ "settled_amount" => 15 }]
97 ),
98 ["customerid_topup"] => FakeDB::MultiResult.new(
99 [{ "a" => 1000 }],
100 [{ "settled_amount" => 15 }]
101 )
102 )
103 )
104 Web.opts[:common_logger] = FakeLog.new
105 Web.instance_variable_set(:@outbound_transfers, { "bcall" => "oocall" })
106 Web.app
107 end
108
109 def test_outbound_forwards
110 post(
111 "/outbound/calls",
112 {
113 from: "customerid",
114 to: "+15557654321",
115 callId: "acall"
116 }.to_json,
117 { "CONTENT_TYPE" => "application/json" }
118 )
119
120 assert last_response.ok?
121 assert_equal(
122 "<?xml version=\"1.0\" encoding=\"utf-8\" ?><Response>" \
123 "<Transfer transferCallerId=\"+15551234567\">" \
124 "<PhoneNumber>+15557654321</PhoneNumber></Transfer></Response>",
125 last_response.body
126 )
127 end
128 em :test_outbound_forwards
129
130 def test_outbound_low_balance
131 ExpiringLock::REDIS.expect(
132 :set,
133 EMPromise.resolve(nil),
134 ["jmp_customer_low_balance-customerid_low", Time, "EX", 604800, "NX"]
135 )
136
137 post(
138 "/outbound/calls",
139 {
140 from: "customerid_low",
141 to: "+15557654321",
142 callId: "acall"
143 }.to_json,
144 { "CONTENT_TYPE" => "application/json" }
145 )
146
147 assert last_response.ok?
148 assert_equal(
149 "<?xml version=\"1.0\" encoding=\"utf-8\" ?><Response>" \
150 "<SpeakSentence>Your balance of $0.01 is not enough to " \
151 "complete this call.</SpeakSentence></Response>",
152 last_response.body
153 )
154 assert_mock ExpiringLock::REDIS
155 end
156 em :test_outbound_low_balance
157
158 def test_outbound_low_balance_top_up
159 LowBalance::AutoTopUp::Transaction.expect(
160 :sale,
161 EMPromise.resolve(
162 OpenStruct.new(insert: EMPromise.resolve(nil), total: 15)
163 ),
164 [Customer, { amount: 15 }]
165 )
166
167 ExpiringLock::REDIS.expect(
168 :set,
169 EMPromise.resolve("OK"),
170 ["jmp_customer_low_balance-customerid_topup", Time, "EX", 604800, "NX"]
171 )
172
173 CustomerFinancials::REDIS.expect(
174 :smembers,
175 EMPromise.resolve([]),
176 ["block_credit_cards"]
177 )
178 LowBalance::AutoTopUp::REDIS.expect(
179 :exists,
180 0,
181 ["jmp_auto_top_up_block-abcd"]
182 )
183 braintree_customer = Minitest::Mock.new
184 CustomerFinancials::BRAINTREE.expect(:customer, braintree_customer)
185 payment_methods = OpenStruct.new(payment_methods: [
186 OpenStruct.new(default?: true, unique_number_identifier: "abcd")
187 ])
188 braintree_customer.expect(
189 :find,
190 EMPromise.resolve(payment_methods),
191 ["customerid_topup"]
192 )
193
194 Customer::BLATHER.expect(
195 :<<,
196 nil,
197 [Blather::Stanza]
198 )
199
200 post(
201 "/outbound/calls",
202 {
203 from: "customerid_topup",
204 to: "+15557654321",
205 callId: "acall"
206 }.to_json,
207 { "CONTENT_TYPE" => "application/json" }
208 )
209
210 assert last_response.ok?
211 assert_equal(
212 "<?xml version=\"1.0\" encoding=\"utf-8\" ?><Response>" \
213 "<Transfer transferCallerId=\"+15551234567\">" \
214 "<PhoneNumber>+15557654321</PhoneNumber></Transfer></Response>",
215 last_response.body
216 )
217 assert_mock ExpiringLock::REDIS
218 assert_mock Customer::BLATHER
219 assert_mock LowBalance::AutoTopUp::Transaction
220 end
221 em :test_outbound_low_balance_top_up
222
223 def test_outbound_unsupported
224 post(
225 "/outbound/calls",
226 {
227 from: "customerid_limit",
228 to: "+95557654321",
229 callId: "acall"
230 }.to_json,
231 { "CONTENT_TYPE" => "application/json" }
232 )
233
234 assert last_response.ok?
235 assert_equal(
236 "<?xml version=\"1.0\" encoding=\"utf-8\" ?><Response>" \
237 "<SpeakSentence>The number you have dialled is not " \
238 "supported on your account.</SpeakSentence></Response>",
239 last_response.body
240 )
241 end
242 em :test_outbound_unsupported
243
244 def test_outbound_atlimit
245 post(
246 "/outbound/calls",
247 {
248 from: "customerid_limit",
249 to: "+15557654321",
250 callId: "acall"
251 }.to_json,
252 { "CONTENT_TYPE" => "application/json" }
253 )
254
255 assert last_response.ok?
256 assert_equal(
257 "<?xml version=\"1.0\" encoding=\"utf-8\" ?><Response>" \
258 "<Gather gatherUrl=\"\/outbound/calls\" maxDigits=\"1\" " \
259 "repeatCount=\"3\"><SpeakSentence>This call will take you over " \
260 "your configured monthly overage limit.</SpeakSentence><SpeakSentence>" \
261 "Change your limit in your account settings or press 1 to accept the " \
262 "charges. You can hang up to cancel.</SpeakSentence></Gather></Response>",
263 last_response.body
264 )
265 end
266 em :test_outbound_atlimit
267
268 def test_outbound_atlimit_digits
269 post(
270 "/outbound/calls",
271 {
272 from: "customerid_limit",
273 to: "+15557654321",
274 callId: "acall",
275 digits: "1"
276 }.to_json,
277 { "CONTENT_TYPE" => "application/json" }
278 )
279
280 assert last_response.ok?
281 assert_equal(
282 "<?xml version=\"1.0\" encoding=\"utf-8\" ?><Response>" \
283 "<Transfer transferCallerId=\"+15551234567\">" \
284 "<PhoneNumber>+15557654321</PhoneNumber></Transfer></Response>",
285 last_response.body
286 )
287 end
288 em :test_outbound_atlimit_digits
289
290 def test_inbound
291 CustomerFwd::BANDWIDTH_VOICE.expect(
292 :create_call,
293 OpenStruct.new(data: OpenStruct.new(call_id: "ocall")),
294 [
295 "test_bw_account",
296 Matching.new do |arg|
297 assert_equal(
298 "http://example.org/inbound/calls/acall?customer_id=customerid",
299 arg[:body].answer_url
300 )
301 assert_equal [:body], arg.keys
302 end
303 ]
304 )
305
306 post(
307 "/inbound/calls",
308 {
309 from: "+15557654321",
310 to: "+15551234567",
311 callId: "acall"
312 }.to_json,
313 { "CONTENT_TYPE" => "application/json" }
314 )
315
316 assert last_response.ok?
317 assert_equal(
318 "<?xml version=\"1.0\" encoding=\"utf-8\" ?><Response>" \
319 "<Ring answerCall=\"false\" duration=\"300\" />" \
320 "</Response>",
321 last_response.body
322 )
323 assert_mock CustomerFwd::BANDWIDTH_VOICE
324 end
325 em :test_inbound
326
327 def test_inbound_low
328 ExpiringLock::REDIS.expect(
329 :set,
330 EMPromise.resolve(nil),
331 ["jmp_customer_low_balance-customerid_low", Time, "EX", 604800, "NX"]
332 )
333
334 post(
335 "/inbound/calls",
336 {
337 from: "+15557654321",
338 to: "+15551234560",
339 callId: "acall"
340 }.to_json,
341 { "CONTENT_TYPE" => "application/json" }
342 )
343
344 assert last_response.ok?
345 assert_equal(
346 "<?xml version=\"1.0\" encoding=\"utf-8\" ?><Response>" \
347 "<Redirect redirectUrl=\"/inbound/calls/acall/voicemail\" />" \
348 "</Response>",
349 last_response.body
350 )
351 assert_mock CustomerFwd::BANDWIDTH_VOICE
352 assert_mock ExpiringLock::REDIS
353 end
354 em :test_inbound_low
355
356 def test_inbound_leg2
357 post(
358 "/inbound/calls/acall?customer_id=customerid",
359 {
360 from: "+15557654321",
361 to: "sip:boop@example.com",
362 callId: "ocall"
363 }.to_json,
364 { "CONTENT_TYPE" => "application/json" }
365 )
366
367 assert last_response.ok?
368 assert_equal(
369 "<?xml version=\"1.0\" encoding=\"utf-8\" ?><Response>" \
370 "<Tag>connected</Tag><Bridge>acall</Bridge>" \
371 "</Response>",
372 last_response.body
373 )
374 end
375 em :test_inbound_leg2
376
377 def test_inbound_limit_leg2
378 path = "/inbound/calls/acall?customer_id=customerid_limit"
379
380 post(
381 path,
382 {
383 from: "+15557654321",
384 to: "sip:boop@example.com",
385 callId: "ocall"
386 }.to_json,
387 { "CONTENT_TYPE" => "application/json" }
388 )
389
390 assert last_response.ok?
391 assert_equal(
392 "<?xml version=\"1.0\" encoding=\"utf-8\" ?><Response>" \
393 "<Gather gatherUrl=\"#{path}\" maxDigits=\"1\" " \
394 "repeatCount=\"3\"><SpeakSentence>This call will take you over " \
395 "your configured monthly overage limit.</SpeakSentence><SpeakSentence>" \
396 "Change your limit in your account settings or press 1 to accept the " \
397 "charges. You can hang up to send the caller to voicemail." \
398 "</SpeakSentence></Gather></Response>",
399 last_response.body
400 )
401 end
402 em :test_inbound_limit_leg2
403
404 def test_inbound_limit_digits_leg2
405 post(
406 "/inbound/calls/acall?customer_id=customerid_limit",
407 {
408 from: "+15557654321",
409 to: "sip:boop@example.com",
410 callId: "ocall",
411 digits: "1"
412 }.to_json,
413 { "CONTENT_TYPE" => "application/json" }
414 )
415
416 assert last_response.ok?
417 assert_equal(
418 "<?xml version=\"1.0\" encoding=\"utf-8\" ?><Response>" \
419 "<Tag>connected</Tag><Bridge>acall</Bridge>" \
420 "</Response>",
421 last_response.body
422 )
423 end
424 em :test_inbound_limit_digits_leg2
425
426 def test_inbound_limit_hangup
427 Web::BANDWIDTH_VOICE.expect(
428 :modify_call,
429 nil,
430 [
431 "test_bw_account",
432 "bcall",
433 Matching.new do |arg|
434 assert_equal [:body], arg.keys
435 assert_equal(
436 "http://example.org/inbound/calls/oocall/voicemail",
437 arg[:body].redirect_url
438 )
439 end
440 ]
441 )
442
443 post(
444 "/inbound/calls/bcall/transfer_complete",
445 {
446 from: "+15557654321",
447 to: "+15551234561",
448 callId: "oocall",
449 cause: "hangup"
450 }.to_json,
451 { "CONTENT_TYPE" => "application/json" }
452 )
453
454 assert last_response.ok?
455 assert_mock Web::BANDWIDTH_VOICE
456 end
457 em :test_inbound_limit_hangup
458
459 def test_voicemail
460 language_id = stub_request(:post, "https://api.rev.ai/languageid/v1/jobs")
461 .with(body: {
462 metadata: {
463 media_url: "https://jmp.chat/media",
464 from_jid: "+15557654321@component",
465 customer_id: "customerid"
466 }.to_json,
467 source_config: {
468 url: "https://jmp.chat/media"
469 },
470 notification_config: {
471 url: "http://example.org/inbound/calls/CALLID/voicemail/language_id"
472 }
473 }.to_json)
474
475 Customer::BLATHER.expect(
476 :<<,
477 nil,
478 [Matching.new do |stanza|
479 assert_equal "+15557654321@component", stanza.from.to_s
480 assert_equal "customer@example.com", stanza.to.to_s
481 assert_equal "https://jmp.chat/media", OOB.find_or_create(stanza).url
482 end]
483 )
484
485 post(
486 "/inbound/calls/CALLID/voicemail/audio",
487 {
488 "startTime" => "2021-01-01T00:00:00Z",
489 "endTime" => "2021-01-01T00:00:06Z",
490 "mediaUrl" => "https://voice.bandwidth.com/api/v2/accounts/1/media",
491 "to" => "+15551234567",
492 "from" => "+15557654321"
493 }.to_json,
494 { "CONTENT_TYPE" => "application/json" }
495 )
496
497 assert last_response.ok?
498 assert_mock Customer::BLATHER
499 assert_requested language_id
500 end
501 em :test_voicemail
502
503 def test_anonymous_voicemail
504 language_id = stub_request(:post, "https://api.rev.ai/languageid/v1/jobs")
505 .with(body: {
506 metadata: {
507 media_url: "https://jmp.chat/media",
508 from_jid:
509 "16;phone-context=anonymous.phone-context.soprani.ca@component",
510 customer_id: "customerid"
511 }.to_json,
512 source_config: {
513 url: "https://jmp.chat/media"
514 },
515 notification_config: {
516 url: "http://example.org/inbound/calls/CALLID/voicemail/language_id"
517 }
518 }.to_json)
519
520 Customer::BLATHER.expect(
521 :<<,
522 nil,
523 [Matching.new do |stanza|
524 assert_equal(
525 "16;phone-context=anonymous.phone-context.soprani.ca@component",
526 stanza.from.to_s
527 )
528 assert_equal "customer@example.com", stanza.to.to_s
529 assert_equal "https://jmp.chat/media", OOB.find_or_create(stanza).url
530 end]
531 )
532
533 post(
534 "/inbound/calls/CALLID/voicemail/audio",
535 {
536 "startTime" => "2021-01-01T00:00:00Z",
537 "endTime" => "2021-01-01T00:00:06Z",
538 "mediaUrl" => "https://voice.bandwidth.com/api/v2/accounts/1/media",
539 "to" => "+15551234567",
540 "from" => "Anonymous"
541 }.to_json,
542 { "CONTENT_TYPE" => "application/json" }
543 )
544
545 assert last_response.ok?
546 assert_mock Customer::BLATHER
547 assert_requested language_id
548 end
549 em :test_anonymous_voicemail
550
551 def test_voicemail_short
552 post(
553 "/inbound/calls/CALLID/voicemail/audio",
554 {
555 "startTime" => "2021-01-01T00:00:00Z",
556 "endTime" => "2021-01-01T00:00:05Z"
557 }.to_json,
558 { "CONTENT_TYPE" => "application/json" }
559 )
560
561 assert last_response.ok?
562 assert_mock Customer::BLATHER
563 end
564 em :test_voicemail_short
565end