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 do |arg|
268 assert_equal(
269 "http://example.org/inbound/calls/acall?customer_id=customerid",
270 arg[:body].answer_url
271 )
272 assert_equal [:body], arg.keys
273 end
274 ]
275 )
276
277 post(
278 "/inbound/calls",
279 {
280 from: "+15557654321",
281 to: "+15551234567",
282 callId: "acall"
283 }.to_json,
284 { "CONTENT_TYPE" => "application/json" }
285 )
286
287 assert last_response.ok?
288 assert_equal(
289 "<?xml version=\"1.0\" encoding=\"utf-8\" ?><Response>" \
290 "<Ring answerCall=\"false\" duration=\"300\" />" \
291 "</Response>",
292 last_response.body
293 )
294 assert_mock CustomerFwd::BANDWIDTH_VOICE
295 end
296 em :test_inbound
297
298 def test_inbound_low
299 ExpiringLock::REDIS.expect(
300 :exists,
301 EMPromise.resolve(1),
302 ["jmp_customer_low_balance-customerid_low"]
303 )
304
305 post(
306 "/inbound/calls",
307 {
308 from: "+15557654321",
309 to: "+15551234560",
310 callId: "acall"
311 }.to_json,
312 { "CONTENT_TYPE" => "application/json" }
313 )
314
315 assert last_response.ok?
316 assert_equal(
317 "<?xml version=\"1.0\" encoding=\"utf-8\" ?><Response>" \
318 "<Redirect redirectUrl=\"/inbound/calls/acall/voicemail\" />" \
319 "</Response>",
320 last_response.body
321 )
322 assert_mock CustomerFwd::BANDWIDTH_VOICE
323 assert_mock ExpiringLock::REDIS
324 end
325 em :test_inbound_low
326
327 def test_inbound_leg2
328 post(
329 "/inbound/calls/acall?customer_id=customerid",
330 {
331 from: "+15557654321",
332 to: "sip:boop@example.com",
333 callId: "ocall"
334 }.to_json,
335 { "CONTENT_TYPE" => "application/json" }
336 )
337
338 assert last_response.ok?
339 assert_equal(
340 "<?xml version=\"1.0\" encoding=\"utf-8\" ?><Response>" \
341 "<Tag>connected</Tag><Bridge>acall</Bridge>" \
342 "</Response>",
343 last_response.body
344 )
345 end
346 em :test_inbound_leg2
347
348 def test_inbound_limit_leg2
349 path = "/inbound/calls/acall?customer_id=customerid_limit"
350
351 post(
352 path,
353 {
354 from: "+15557654321",
355 to: "sip:boop@example.com",
356 callId: "ocall"
357 }.to_json,
358 { "CONTENT_TYPE" => "application/json" }
359 )
360
361 assert last_response.ok?
362 assert_equal(
363 "<?xml version=\"1.0\" encoding=\"utf-8\" ?><Response>" \
364 "<Gather gatherUrl=\"#{path}\" maxDigits=\"1\" " \
365 "repeatCount=\"3\"><SpeakSentence>This call will take you over " \
366 "your configured monthly overage limit.</SpeakSentence><SpeakSentence>" \
367 "Change your limit in your account settings or press 1 to accept the " \
368 "charges. You can hang up to send the caller to voicemail." \
369 "</SpeakSentence></Gather></Response>",
370 last_response.body
371 )
372 end
373 em :test_inbound_limit_leg2
374
375 def test_inbound_limit_digits_leg2
376 post(
377 "/inbound/calls/acall?customer_id=customerid_limit",
378 {
379 from: "+15557654321",
380 to: "sip:boop@example.com",
381 callId: "ocall",
382 digits: "1"
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 "<Tag>connected</Tag><Bridge>acall</Bridge>" \
391 "</Response>",
392 last_response.body
393 )
394 end
395 em :test_inbound_limit_digits_leg2
396
397 def test_inbound_limit_hangup
398 Web::BANDWIDTH_VOICE.expect(
399 :modify_call,
400 nil,
401 [
402 "test_bw_account",
403 "bcall",
404 Matching.new do |arg|
405 assert_equal [:body], arg.keys
406 assert_equal(
407 "http://example.org/inbound/calls/oocall/voicemail",
408 arg[:body].redirect_url
409 )
410 end
411 ]
412 )
413
414 post(
415 "/inbound/calls/bcall/transfer_complete",
416 {
417 from: "+15557654321",
418 to: "+15551234561",
419 callId: "oocall",
420 cause: "hangup"
421 }.to_json,
422 { "CONTENT_TYPE" => "application/json" }
423 )
424
425 assert last_response.ok?
426 assert_mock Web::BANDWIDTH_VOICE
427 end
428 em :test_inbound_limit_hangup
429
430 def test_voicemail
431 Customer::BLATHER.expect(
432 :<<,
433 nil,
434 [Matching.new do |stanza|
435 assert_equal "+15557654321@component", stanza.from.to_s
436 assert_equal "customer@example.com", stanza.to.to_s
437 assert_equal "https://jmp.chat/media", OOB.find_or_create(stanza).url
438 end]
439 )
440
441 post(
442 "/inbound/calls/CALLID/voicemail/audio",
443 {
444 "startTime" => "2021-01-01T00:00:00Z",
445 "endTime" => "2021-01-01T00:00:06Z",
446 "mediaUrl" => "https://voice.bandwidth.com/api/v2/accounts/1/media",
447 "to" => "+15551234567",
448 "from" => "+15557654321"
449 }.to_json,
450 { "CONTENT_TYPE" => "application/json" }
451 )
452
453 assert last_response.ok?
454 assert_mock Customer::BLATHER
455 end
456 em :test_voicemail
457
458 def test_anonymous_voicemail
459 Customer::BLATHER.expect(
460 :<<,
461 nil,
462 [Matching.new do |stanza|
463 assert_equal(
464 "16;phone-context=anonymous.phone-context.soprani.ca@component",
465 stanza.from.to_s
466 )
467 assert_equal "customer@example.com", stanza.to.to_s
468 assert_equal "https://jmp.chat/media", OOB.find_or_create(stanza).url
469 end]
470 )
471
472 post(
473 "/inbound/calls/CALLID/voicemail/audio",
474 {
475 "startTime" => "2021-01-01T00:00:00Z",
476 "endTime" => "2021-01-01T00:00:06Z",
477 "mediaUrl" => "https://voice.bandwidth.com/api/v2/accounts/1/media",
478 "to" => "+15551234567",
479 "from" => "Anonymous"
480 }.to_json,
481 { "CONTENT_TYPE" => "application/json" }
482 )
483
484 assert last_response.ok?
485 assert_mock Customer::BLATHER
486 end
487 em :test_anonymous_voicemail
488
489 def test_voicemail_short
490 post(
491 "/inbound/calls/CALLID/voicemail/audio",
492 {
493 "startTime" => "2021-01-01T00:00:00Z",
494 "endTime" => "2021-01-01T00:00:05Z"
495 }.to_json,
496 { "CONTENT_TYPE" => "application/json" }
497 )
498
499 assert last_response.ok?
500 assert_mock Customer::BLATHER
501 end
502 em :test_voicemail_short
503end