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" => IBR.new.tap do |ibr|
66 ibr.phone = "+15551234567"
67 end,
68 "customer_customerid_low@component" => IBR.new.tap do |ibr|
69 ibr.phone = "+15551234567"
70 end,
71 "customer_customerid_topup@component" => IBR.new.tap do |ibr|
72 ibr.phone = "+15551234567"
73 end,
74 "customer_customerid_limit@component" => IBR.new.tap do |ibr|
75 ibr.phone = "+15551234567"
76 end
77 }
78 )
79 )
80 )
81 Web.opts[:call_attempt_repo] = CallAttemptRepo.new(
82 db: FakeDB.new(
83 ["test_usd", "+15557654321", :outbound] => [{ "rate" => 0.01 }],
84 ["test_usd", "+15557654321", :inbound] => [{ "rate" => 0.01 }],
85 ["customerid_limit"] => [{ "a" => -1000 }],
86 ["customerid_low"] => [{ "a" => -1000 }],
87 ["customerid_topup"] => [{ "a" => -1000 }]
88 )
89 )
90 Web.opts[:common_logger] = FakeLog.new
91 Web.instance_variable_set(:@outbound_transfers, { "bcall" => "oocall" })
92 Web.app
93 end
94
95 def test_outbound_forwards
96 post(
97 "/outbound/calls",
98 {
99 from: "customerid",
100 to: "+15557654321",
101 callId: "acall"
102 }.to_json,
103 { "CONTENT_TYPE" => "application/json" }
104 )
105
106 assert last_response.ok?
107 assert_equal(
108 "<?xml version=\"1.0\" encoding=\"utf-8\" ?><Response>" \
109 "<Forward from=\"+15551234567\" to=\"+15557654321\" />" \
110 "</Response>",
111 last_response.body
112 )
113 end
114 em :test_outbound_forwards
115
116 def test_outbound_low_balance
117 ExpiringLock::REDIS.expect(
118 :exists,
119 EMPromise.resolve(1),
120 ["jmp_customer_low_balance-customerid_low"]
121 )
122
123 post(
124 "/outbound/calls",
125 {
126 from: "customerid_low",
127 to: "+15557654321",
128 callId: "acall"
129 }.to_json,
130 { "CONTENT_TYPE" => "application/json" }
131 )
132
133 assert last_response.ok?
134 assert_equal(
135 "<?xml version=\"1.0\" encoding=\"utf-8\" ?><Response>" \
136 "<SpeakSentence>Your balance of $0.01 is not enough to " \
137 "complete this call.</SpeakSentence></Response>",
138 last_response.body
139 )
140 assert_mock ExpiringLock::REDIS
141 end
142 em :test_outbound_low_balance
143
144 def test_outbound_low_balance_top_up
145 LowBalance::AutoTopUp::Transaction.expect(
146 :sale,
147 EMPromise.resolve(
148 OpenStruct.new(insert: EMPromise.resolve(nil), total: 15)
149 ),
150 [Customer, { amount: 15 }]
151 )
152
153 ExpiringLock::REDIS.expect(
154 :exists,
155 nil,
156 ["jmp_customer_low_balance-customerid_topup"]
157 )
158
159 ExpiringLock::REDIS.expect(
160 :setex,
161 nil,
162 ["jmp_customer_low_balance-customerid_topup", Integer, Time]
163 )
164
165 Customer::BLATHER.expect(
166 :<<,
167 nil,
168 [Blather::Stanza]
169 )
170
171 post(
172 "/outbound/calls",
173 {
174 from: "customerid_topup",
175 to: "+15557654321",
176 callId: "acall"
177 }.to_json,
178 { "CONTENT_TYPE" => "application/json" }
179 )
180
181 assert last_response.ok?
182 assert_equal(
183 "<?xml version=\"1.0\" encoding=\"utf-8\" ?><Response>" \
184 "<Forward from=\"+15551234567\" to=\"+15557654321\" />" \
185 "</Response>",
186 last_response.body
187 )
188 assert_mock ExpiringLock::REDIS
189 assert_mock Customer::BLATHER
190 assert_mock LowBalance::AutoTopUp::Transaction
191 end
192 em :test_outbound_low_balance_top_up
193
194 def test_outbound_unsupported
195 post(
196 "/outbound/calls",
197 {
198 from: "customerid_limit",
199 to: "+95557654321",
200 callId: "acall"
201 }.to_json,
202 { "CONTENT_TYPE" => "application/json" }
203 )
204
205 assert last_response.ok?
206 assert_equal(
207 "<?xml version=\"1.0\" encoding=\"utf-8\" ?><Response>" \
208 "<SpeakSentence>The number you have dialled is not " \
209 "supported on your account.</SpeakSentence></Response>",
210 last_response.body
211 )
212 end
213 em :test_outbound_unsupported
214
215 def test_outbound_atlimit
216 post(
217 "/outbound/calls",
218 {
219 from: "customerid_limit",
220 to: "+15557654321",
221 callId: "acall"
222 }.to_json,
223 { "CONTENT_TYPE" => "application/json" }
224 )
225
226 assert last_response.ok?
227 assert_equal(
228 "<?xml version=\"1.0\" encoding=\"utf-8\" ?><Response>" \
229 "<Gather gatherUrl=\"\/outbound/calls\" maxDigits=\"1\" " \
230 "repeatCount=\"3\"><SpeakSentence>This call will take you over " \
231 "your configured monthly overage limit.</SpeakSentence><SpeakSentence>" \
232 "Change your limit in your account settings or press 1 to accept the " \
233 "charges. You can hang up to cancel.</SpeakSentence></Gather></Response>",
234 last_response.body
235 )
236 end
237 em :test_outbound_atlimit
238
239 def test_outbound_atlimit_digits
240 post(
241 "/outbound/calls",
242 {
243 from: "customerid_limit",
244 to: "+15557654321",
245 callId: "acall",
246 digits: "1"
247 }.to_json,
248 { "CONTENT_TYPE" => "application/json" }
249 )
250
251 assert last_response.ok?
252 assert_equal(
253 "<?xml version=\"1.0\" encoding=\"utf-8\" ?><Response>" \
254 "<Forward from=\"+15551234567\" to=\"+15557654321\" />" \
255 "</Response>",
256 last_response.body
257 )
258 end
259 em :test_outbound_atlimit_digits
260
261 def test_inbound
262 CustomerFwd::BANDWIDTH_VOICE.expect(
263 :create_call,
264 OpenStruct.new(data: OpenStruct.new(call_id: "ocall")),
265 [
266 "test_bw_account",
267 Matching.new { |arg| assert_equal [:body], arg.keys }
268 ]
269 )
270
271 post(
272 "/inbound/calls",
273 {
274 from: "+15557654321",
275 to: "+15551234567",
276 callId: "acall"
277 }.to_json,
278 { "CONTENT_TYPE" => "application/json" }
279 )
280
281 assert last_response.ok?
282 assert_equal(
283 "<?xml version=\"1.0\" encoding=\"utf-8\" ?><Response>" \
284 "<Ring answerCall=\"false\" duration=\"300\" />" \
285 "</Response>",
286 last_response.body
287 )
288 assert_mock CustomerFwd::BANDWIDTH_VOICE
289 end
290 em :test_inbound
291
292 def test_inbound_low
293 ExpiringLock::REDIS.expect(
294 :exists,
295 EMPromise.resolve(1),
296 ["jmp_customer_low_balance-customerid_low"]
297 )
298
299 post(
300 "/inbound/calls",
301 {
302 from: "+15557654321",
303 to: "+15551234560",
304 callId: "acall"
305 }.to_json,
306 { "CONTENT_TYPE" => "application/json" }
307 )
308
309 assert last_response.ok?
310 assert_equal(
311 "<?xml version=\"1.0\" encoding=\"utf-8\" ?><Response>" \
312 "<Redirect redirectUrl=\"/inbound/calls/acall/voicemail\" />" \
313 "</Response>",
314 last_response.body
315 )
316 assert_mock CustomerFwd::BANDWIDTH_VOICE
317 assert_mock ExpiringLock::REDIS
318 end
319 em :test_inbound_low
320
321 def test_inbound_leg2
322 post(
323 "/inbound/calls/acall",
324 {
325 from: "+15557654321",
326 to: "+15551234567",
327 callId: "ocall"
328 }.to_json,
329 { "CONTENT_TYPE" => "application/json" }
330 )
331
332 assert last_response.ok?
333 assert_equal(
334 "<?xml version=\"1.0\" encoding=\"utf-8\" ?><Response>" \
335 "<Tag>connected</Tag><Bridge>acall</Bridge>" \
336 "</Response>",
337 last_response.body
338 )
339 end
340 em :test_inbound_leg2
341
342 def test_inbound_limit_leg2
343 post(
344 "/inbound/calls/acall",
345 {
346 from: "+15557654321",
347 to: "+15551234561",
348 callId: "ocall"
349 }.to_json,
350 { "CONTENT_TYPE" => "application/json" }
351 )
352
353 assert last_response.ok?
354 assert_equal(
355 "<?xml version=\"1.0\" encoding=\"utf-8\" ?><Response>" \
356 "<Gather gatherUrl=\"\/inbound/calls/acall\" maxDigits=\"1\" " \
357 "repeatCount=\"3\"><SpeakSentence>This call will take you over " \
358 "your configured monthly overage limit.</SpeakSentence><SpeakSentence>" \
359 "Change your limit in your account settings or press 1 to accept the " \
360 "charges. You can hang up to send the caller to voicemail." \
361 "</SpeakSentence></Gather></Response>",
362 last_response.body
363 )
364 end
365 em :test_inbound_limit_leg2
366
367 def test_inbound_limit_digits_leg2
368 post(
369 "/inbound/calls/acall",
370 {
371 from: "+15557654321",
372 to: "+15551234561",
373 callId: "ocall",
374 digits: "1"
375 }.to_json,
376 { "CONTENT_TYPE" => "application/json" }
377 )
378
379 assert last_response.ok?
380 assert_equal(
381 "<?xml version=\"1.0\" encoding=\"utf-8\" ?><Response>" \
382 "<Tag>connected</Tag><Bridge>acall</Bridge>" \
383 "</Response>",
384 last_response.body
385 )
386 end
387 em :test_inbound_limit_digits_leg2
388
389 def test_inbound_limit_hangup
390 Web::BANDWIDTH_VOICE.expect(
391 :modify_call,
392 nil,
393 [
394 "test_bw_account",
395 "bcall",
396 Matching.new do |arg|
397 assert_equal [:body], arg.keys
398 assert_equal(
399 "http://example.org/inbound/calls/oocall/voicemail",
400 arg[:body].redirect_url
401 )
402 end
403 ]
404 )
405
406 post(
407 "/inbound/calls/bcall/transfer_complete",
408 {
409 from: "+15557654321",
410 to: "+15551234561",
411 callId: "oocall",
412 cause: "hangup"
413 }.to_json,
414 { "CONTENT_TYPE" => "application/json" }
415 )
416
417 assert last_response.ok?
418 assert_mock Web::BANDWIDTH_VOICE
419 end
420 em :test_inbound_limit_hangup
421
422 def test_voicemail
423 Customer::BLATHER.expect(
424 :<<,
425 nil,
426 [Matching.new do |stanza|
427 assert_equal "+15557654321@component", stanza.from.to_s
428 assert_equal "customer@example.com", stanza.to.to_s
429 assert_equal "https://jmp.chat/media", OOB.find_or_create(stanza).url
430 end]
431 )
432
433 post(
434 "/inbound/calls/CALLID/voicemail/audio",
435 {
436 "startTime" => "2021-01-01T00:00:00Z",
437 "endTime" => "2021-01-01T00:00:06Z",
438 "mediaUrl" => "https://voice.bandwidth.com/api/v2/accounts/1/media",
439 "to" => "+15551234567",
440 "from" => "+15557654321"
441 }.to_json,
442 { "CONTENT_TYPE" => "application/json" }
443 )
444
445 assert last_response.ok?
446 assert_mock Customer::BLATHER
447 end
448 em :test_voicemail
449
450 def test_anonymous_voicemail
451 Customer::BLATHER.expect(
452 :<<,
453 nil,
454 [Matching.new do |stanza|
455 assert_equal(
456 "16;phone-context=anonymous.phone-context.soprani.ca@component",
457 stanza.from.to_s
458 )
459 assert_equal "customer@example.com", stanza.to.to_s
460 assert_equal "https://jmp.chat/media", OOB.find_or_create(stanza).url
461 end]
462 )
463
464 post(
465 "/inbound/calls/CALLID/voicemail/audio",
466 {
467 "startTime" => "2021-01-01T00:00:00Z",
468 "endTime" => "2021-01-01T00:00:06Z",
469 "mediaUrl" => "https://voice.bandwidth.com/api/v2/accounts/1/media",
470 "to" => "+15551234567",
471 "from" => "Anonymous"
472 }.to_json,
473 { "CONTENT_TYPE" => "application/json" }
474 )
475
476 assert last_response.ok?
477 assert_mock Customer::BLATHER
478 end
479 em :test_anonymous_voicemail
480
481 def test_voicemail_short
482 post(
483 "/inbound/calls/CALLID/voicemail/audio",
484 {
485 "startTime" => "2021-01-01T00:00:00Z",
486 "endTime" => "2021-01-01T00:00:05Z"
487 }.to_json,
488 { "CONTENT_TYPE" => "application/json" }
489 )
490
491 assert last_response.ok?
492 assert_mock Customer::BLATHER
493 end
494 em :test_voicemail_short
495end