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 ["test_bw_account"],
295 body: Matching.new do |arg|
296 assert_equal(
297 "http://example.org/inbound/calls/acall?customer_id=customerid",
298 arg.answer_url
299 )
300 end
301 )
302
303 post(
304 "/inbound/calls",
305 {
306 from: "+15557654321",
307 to: "+15551234567",
308 callId: "acall"
309 }.to_json,
310 { "CONTENT_TYPE" => "application/json" }
311 )
312
313 assert last_response.ok?
314 assert_equal(
315 "<?xml version=\"1.0\" encoding=\"utf-8\" ?><Response>" \
316 "<Ring answerCall=\"false\" duration=\"300\" />" \
317 "</Response>",
318 last_response.body
319 )
320 assert_mock CustomerFwd::BANDWIDTH_VOICE
321 end
322 em :test_inbound
323
324 def test_inbound_low
325 ExpiringLock::REDIS.expect(
326 :set,
327 EMPromise.resolve(nil),
328 ["jmp_customer_low_balance-customerid_low", Time, "EX", 604800, "NX"]
329 )
330
331 post(
332 "/inbound/calls",
333 {
334 from: "+15557654321",
335 to: "+15551234560",
336 callId: "acall"
337 }.to_json,
338 { "CONTENT_TYPE" => "application/json" }
339 )
340
341 assert last_response.ok?
342 assert_equal(
343 "<?xml version=\"1.0\" encoding=\"utf-8\" ?><Response>" \
344 "<Redirect redirectUrl=\"/inbound/calls/acall/voicemail\" />" \
345 "</Response>",
346 last_response.body
347 )
348 assert_mock CustomerFwd::BANDWIDTH_VOICE
349 assert_mock ExpiringLock::REDIS
350 end
351 em :test_inbound_low
352
353 def test_inbound_leg2
354 post(
355 "/inbound/calls/acall?customer_id=customerid",
356 {
357 from: "+15557654321",
358 to: "sip:boop@example.com",
359 callId: "ocall"
360 }.to_json,
361 { "CONTENT_TYPE" => "application/json" }
362 )
363
364 assert last_response.ok?
365 assert_equal(
366 "<?xml version=\"1.0\" encoding=\"utf-8\" ?><Response>" \
367 "<Tag>connected</Tag><Bridge>acall</Bridge>" \
368 "</Response>",
369 last_response.body
370 )
371 end
372 em :test_inbound_leg2
373
374 def test_inbound_limit_leg2
375 path = "/inbound/calls/acall?customer_id=customerid_limit"
376
377 post(
378 path,
379 {
380 from: "+15557654321",
381 to: "sip:boop@example.com",
382 callId: "ocall"
383 }.to_json,
384 { "CONTENT_TYPE" => "application/json" }
385 )
386
387 assert last_response.ok?
388 assert_equal(
389 "<?xml version=\"1.0\" encoding=\"utf-8\" ?><Response>" \
390 "<Gather gatherUrl=\"#{path}\" maxDigits=\"1\" " \
391 "repeatCount=\"3\"><SpeakSentence>This call will take you over " \
392 "your configured monthly overage limit.</SpeakSentence><SpeakSentence>" \
393 "Change your limit in your account settings or press 1 to accept the " \
394 "charges. You can hang up to send the caller to voicemail." \
395 "</SpeakSentence></Gather></Response>",
396 last_response.body
397 )
398 end
399 em :test_inbound_limit_leg2
400
401 def test_inbound_limit_digits_leg2
402 post(
403 "/inbound/calls/acall?customer_id=customerid_limit",
404 {
405 from: "+15557654321",
406 to: "sip:boop@example.com",
407 callId: "ocall",
408 digits: "1"
409 }.to_json,
410 { "CONTENT_TYPE" => "application/json" }
411 )
412
413 assert last_response.ok?
414 assert_equal(
415 "<?xml version=\"1.0\" encoding=\"utf-8\" ?><Response>" \
416 "<Tag>connected</Tag><Bridge>acall</Bridge>" \
417 "</Response>",
418 last_response.body
419 )
420 end
421 em :test_inbound_limit_digits_leg2
422
423 def test_inbound_limit_hangup
424 Web::BANDWIDTH_VOICE.expect(
425 :modify_call,
426 nil,
427 [
428 "test_bw_account",
429 "bcall"
430 ],
431 body: Matching.new do |arg|
432 assert_equal(
433 "http://example.org/inbound/calls/oocall/voicemail",
434 arg.redirect_url
435 )
436 end
437 )
438
439 post(
440 "/inbound/calls/bcall/transfer_complete",
441 {
442 from: "+15557654321",
443 to: "+15551234561",
444 callId: "oocall",
445 cause: "hangup"
446 }.to_json,
447 { "CONTENT_TYPE" => "application/json" }
448 )
449
450 assert last_response.ok?
451 assert_mock Web::BANDWIDTH_VOICE
452 end
453 em :test_inbound_limit_hangup
454
455 def test_voicemail
456 language_id = stub_request(:post, "https://api.rev.ai/languageid/v1/jobs")
457 .with(body: {
458 metadata: {
459 media_url: "https://jmp.chat/media",
460 from_jid: "+15557654321@component",
461 customer_id: "customerid"
462 }.to_json,
463 source_config: {
464 url: "https://jmp.chat/media"
465 },
466 notification_config: {
467 url: "http://example.org/inbound/calls/CALLID/voicemail/language_id"
468 }
469 }.to_json)
470
471 Customer::BLATHER.expect(
472 :<<,
473 nil,
474 [Matching.new do |stanza|
475 assert_equal "+15557654321@component", stanza.from.to_s
476 assert_equal "customer@example.com", stanza.to.to_s
477 assert_equal "https://jmp.chat/media", OOB.find_or_create(stanza).url
478 end]
479 )
480
481 post(
482 "/inbound/calls/CALLID/voicemail/audio",
483 {
484 "startTime" => "2021-01-01T00:00:00Z",
485 "endTime" => "2021-01-01T00:00:06Z",
486 "mediaUrl" => "https://voice.bandwidth.com/api/v2/accounts/1/media",
487 "to" => "+15551234567",
488 "from" => "+15557654321"
489 }.to_json,
490 { "CONTENT_TYPE" => "application/json" }
491 )
492
493 assert last_response.ok?
494 assert_mock Customer::BLATHER
495 assert_requested language_id
496 end
497 em :test_voicemail
498
499 def test_anonymous_voicemail
500 language_id = stub_request(:post, "https://api.rev.ai/languageid/v1/jobs")
501 .with(body: {
502 metadata: {
503 media_url: "https://jmp.chat/media",
504 from_jid:
505 "16;phone-context=anonymous.phone-context.soprani.ca@component",
506 customer_id: "customerid"
507 }.to_json,
508 source_config: {
509 url: "https://jmp.chat/media"
510 },
511 notification_config: {
512 url: "http://example.org/inbound/calls/CALLID/voicemail/language_id"
513 }
514 }.to_json)
515
516 Customer::BLATHER.expect(
517 :<<,
518 nil,
519 [Matching.new do |stanza|
520 assert_equal(
521 "16;phone-context=anonymous.phone-context.soprani.ca@component",
522 stanza.from.to_s
523 )
524 assert_equal "customer@example.com", stanza.to.to_s
525 assert_equal "https://jmp.chat/media", OOB.find_or_create(stanza).url
526 end]
527 )
528
529 post(
530 "/inbound/calls/CALLID/voicemail/audio",
531 {
532 "startTime" => "2021-01-01T00:00:00Z",
533 "endTime" => "2021-01-01T00:00:06Z",
534 "mediaUrl" => "https://voice.bandwidth.com/api/v2/accounts/1/media",
535 "to" => "+15551234567",
536 "from" => "Anonymous"
537 }.to_json,
538 { "CONTENT_TYPE" => "application/json" }
539 )
540
541 assert last_response.ok?
542 assert_mock Customer::BLATHER
543 assert_requested language_id
544 end
545 em :test_anonymous_voicemail
546
547 def test_voicemail_short
548 post(
549 "/inbound/calls/CALLID/voicemail/audio",
550 {
551 "startTime" => "2021-01-01T00:00:00Z",
552 "endTime" => "2021-01-01T00:00:05Z"
553 }.to_json,
554 { "CONTENT_TYPE" => "application/json" }
555 )
556
557 assert last_response.ok?
558 assert_mock Customer::BLATHER
559 end
560 em :test_voicemail_short
561end