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::CreditCardSale = Minitest::Mock.new
14
15ReachableRedis = Minitest::Mock.new
16
17class WebTest < Minitest::Test
18 include Rack::Test::Methods
19
20 def app
21 Web.opts[:customer_repo] = CustomerRepo.new(
22 redis: FakeRedis.new(
23 "jmp_customer_jid-customerid" => "customer@example.com",
24 "catapult_jid-+15551234567" => "customer_customerid@component",
25 "jmp_customer_jid-customerid_low" => "customer@example.com",
26 "catapult_jid-+15551234560" => "customer_customerid_low@component",
27 "jmp_customer_jid-customerid_topup" => "customer@example.com",
28 "jmp_customer_auto_top_up_amount-customerid_topup" => "15",
29 "jmp_customer_monthly_overage_limit-customerid_topup" => "99999",
30 "catapult_jid-+15551234562" => "customer_customerid_topup@component",
31 "jmp_customer_jid-customerid_limit" => "customer@example.com",
32 "catapult_jid-+15551234561" => "customer_customerid_limit@component",
33 "jmp_customer_jid-customerid_reach" => "customerid_reach@example.com",
34 "catapult_jid-+15551234563" => "customer_customerid_reach@component",
35 "jmp_customer_jid-customerid2" => "customer2@example.com",
36 "catapult_jid-+15551230000" => "customer_customerid2@component"
37 ),
38 db: FakeDB.new(
39 ["customerid"] => [{
40 "balance" => BigDecimal(10),
41 "plan_name" => "test_usd",
42 "expires_at" => Time.now + 100
43 }],
44 ["customerid2"] => [{
45 "balance" => BigDecimal(10),
46 "plan_name" => "test_usd",
47 "expires_at" => Time.now + 100
48 }],
49 ["customerid_low"] => [{
50 "balance" => BigDecimal("0.01"),
51 "plan_name" => "test_usd",
52 "expires_at" => Time.now + 100
53 }],
54 ["customerid_topup"] => [{
55 "balance" => BigDecimal("0.01"),
56 "plan_name" => "test_usd",
57 "expires_at" => Time.now + 100
58 }],
59 ["customerid_reach"] => [{
60 "balance" => BigDecimal(10),
61 "plan_name" => "test_usd",
62 "expires_at" => Time.now + 100
63 }],
64 ["customerid_limit"] => [{
65 "balance" => BigDecimal(10),
66 "plan_name" => "test_usd",
67 "expires_at" => Time.now + 100
68 }]
69 ),
70 sgx_repo: Bwmsgsv2Repo.new(
71 redis: FakeRedis.new(
72 "catapult_fwd-+15551234567" => "xmpp:customer@example.com",
73 "catapult_fwd_timeout-customer_customerid@component" => "30",
74 "catapult_fwd-+15551234560" => "xmpp:customer@example.com",
75 "catapult_fwd_timeout-customer_customerid_low@component" => "30",
76 "catapult_fwd-+15551234561" => "xmpp:customer@example.com",
77 "catapult_fwd_timeout-customer_customerid_limit@component" => "30",
78 "catapult_fwd-+15551234563" => "xmpp:customer@example.com",
79 "catapult_fwd_timeout-customer_customerid_reach@component" => "30",
80 "catapult_fwd-+15551230000" => "xmpp:customer2@example.com",
81 "catapult_fwd_timeout-customer_customerid2@component" => "30"
82 ),
83 ibr_repo: FakeIBRRepo.new(
84 "sgx" => {
85 "customer_customerid@component" =>
86 Blather::Stanza::Iq::IBR.new.tap do |ibr|
87 ibr.phone = "+15551234567"
88 end,
89 "customer_customerid_low@component" =>
90 Blather::Stanza::Iq::IBR.new.tap do |ibr|
91 ibr.phone = "+15551234567"
92 end,
93 "customer_customerid_topup@component" =>
94 Blather::Stanza::Iq::IBR.new.tap do |ibr|
95 ibr.phone = "+15551234567"
96 end,
97 "customer_customerid_reach@component" =>
98 Blather::Stanza::Iq::IBR.new.tap do |ibr|
99 ibr.phone = "+15551234563"
100 end,
101 "customer_customerid_limit@component" =>
102 Blather::Stanza::Iq::IBR.new.tap do |ibr|
103 ibr.phone = "+15551234567"
104 end
105 }
106 )
107 )
108 )
109 Web.opts[:call_attempt_repo] = CallAttemptRepo.new(
110 redis: FakeRedis.new,
111 db: FakeDB.new(
112 ["test_usd", "+15557654321", :outbound] => [{ "rate" => 0.01 }],
113 ["test_usd", "+15557654321", :inbound] => [{ "rate" => 0.01 }],
114 ["test_usd", "+14445556666", :inbound] => [{ "rate" => 0.01 }],
115 ["customerid_limit"] => FakeDB::MultiResult.new(
116 [{ "a" => 1000 }],
117 [{ "settled_amount" => 15 }]
118 ),
119 ["customerid_low"] => FakeDB::MultiResult.new(
120 [{ "a" => 1000 }],
121 [{ "settled_amount" => 15 }]
122 ),
123 ["customerid_topup"] => FakeDB::MultiResult.new(
124 [{ "a" => 1000 }],
125 [{ "settled_amount" => 15 }]
126 )
127 )
128 )
129 Web.opts[:common_logger] = FakeLog.new
130 Web.opts[:reachability_repo] = ReachabilityRepo::Voice.new(
131 redis: ReachableRedis,
132 senders: ["+14445556666"]
133 )
134 Web.instance_variable_set(:@outbound_transfers, { "bcall" => "oocall" })
135 Web.app
136 end
137
138 def test_outbound_forwards
139 post(
140 "/outbound/calls",
141 {
142 from: "ccustomerid",
143 to: "+15557654321",
144 callId: "acall"
145 }.to_json,
146 { "CONTENT_TYPE" => "application/json" }
147 )
148
149 assert last_response.ok?
150 assert_equal(
151 "<?xml version=\"1.0\" encoding=\"utf-8\" ?><Response>" \
152 "<Transfer transferCallerId=\"+15551234567\">" \
153 "<PhoneNumber>+15557654321</PhoneNumber></Transfer></Response>",
154 last_response.body
155 )
156 end
157 em :test_outbound_forwards
158
159 def test_outbound_low_balance
160 ExpiringLock::REDIS.expect(
161 :set,
162 EMPromise.resolve(nil),
163 ["jmp_customer_low_balance-customerid_low", Time, "EX", 604800, "NX"]
164 )
165
166 post(
167 "/outbound/calls",
168 {
169 from: "ccustomerid_low",
170 to: "+15557654321",
171 callId: "acall"
172 }.to_json,
173 { "CONTENT_TYPE" => "application/json" }
174 )
175
176 assert last_response.ok?
177 assert_equal(
178 "<?xml version=\"1.0\" encoding=\"utf-8\" ?><Response>" \
179 "<SpeakSentence>Your balance of $0.01 is not enough to " \
180 "complete this call.</SpeakSentence></Response>",
181 last_response.body
182 )
183 assert_mock ExpiringLock::REDIS
184 end
185 em :test_outbound_low_balance
186
187 def test_outbound_low_balance_top_up
188 LowBalance::AutoTopUp::CreditCardSale.expect(
189 :create,
190 EMPromise.resolve(
191 OpenStruct.new(total: 15)
192 ),
193 [Customer], amount: 15
194 )
195
196 ExpiringLock::REDIS.expect(
197 :set,
198 EMPromise.resolve("OK"),
199 ["jmp_customer_low_balance-customerid_topup", Time, "EX", 604800, "NX"]
200 )
201
202 CustomerFinancials::REDIS.expect(
203 :smembers,
204 EMPromise.resolve([]),
205 ["block_credit_cards"]
206 )
207 LowBalance::AutoTopUp::REDIS.expect(
208 :exists,
209 0,
210 ["jmp_auto_top_up_block-abcd"]
211 )
212 braintree_customer = Minitest::Mock.new
213 CustomerFinancials::BRAINTREE.expect(:customer, braintree_customer)
214 payment_methods = OpenStruct.new(payment_methods: [
215 OpenStruct.new(default?: true, unique_number_identifier: "abcd")
216 ])
217 braintree_customer.expect(
218 :find,
219 EMPromise.resolve(payment_methods),
220 ["customerid_topup"]
221 )
222
223 Customer::BLATHER.expect(
224 :<<,
225 nil,
226 [Blather::Stanza]
227 )
228
229 post(
230 "/outbound/calls",
231 {
232 from: "ccustomerid_topup",
233 to: "+15557654321",
234 callId: "acall"
235 }.to_json,
236 { "CONTENT_TYPE" => "application/json" }
237 )
238
239 assert last_response.ok?
240 assert_equal(
241 "<?xml version=\"1.0\" encoding=\"utf-8\" ?><Response>" \
242 "<Transfer transferCallerId=\"+15551234567\">" \
243 "<PhoneNumber>+15557654321</PhoneNumber></Transfer></Response>",
244 last_response.body
245 )
246 assert_mock ExpiringLock::REDIS
247 assert_mock Customer::BLATHER
248 assert_mock LowBalance::AutoTopUp::CreditCardSale
249 end
250 em :test_outbound_low_balance_top_up
251
252 def test_outbound_unsupported
253 post(
254 "/outbound/calls",
255 {
256 from: "ccustomerid_limit",
257 to: "+95557654321",
258 callId: "acall"
259 }.to_json,
260 { "CONTENT_TYPE" => "application/json" }
261 )
262
263 assert last_response.ok?
264 assert_equal(
265 "<?xml version=\"1.0\" encoding=\"utf-8\" ?><Response>" \
266 "<SpeakSentence>The number you have dialled is not " \
267 "supported on your account.</SpeakSentence></Response>",
268 last_response.body
269 )
270 end
271 em :test_outbound_unsupported
272
273 def test_outbound_atlimit
274 post(
275 "/outbound/calls",
276 {
277 from: "ccustomerid_limit",
278 to: "+15557654321",
279 callId: "acall"
280 }.to_json,
281 { "CONTENT_TYPE" => "application/json" }
282 )
283
284 assert last_response.ok?
285 assert_equal(
286 "<?xml version=\"1.0\" encoding=\"utf-8\" ?><Response>" \
287 "<Gather gatherUrl=\"\/outbound/calls\" maxDigits=\"1\" " \
288 "repeatCount=\"3\"><SpeakSentence>This call will take you over " \
289 "your configured monthly overage limit.</SpeakSentence><SpeakSentence>" \
290 "Change your limit in your account settings or press 1 to accept the " \
291 "charges. You can hang up to cancel.</SpeakSentence></Gather></Response>",
292 last_response.body
293 )
294 end
295 em :test_outbound_atlimit
296
297 def test_outbound_no_customer
298 post(
299 "/outbound/calls",
300 {
301 from: "no_such_customer",
302 to: "+15557654321",
303 callId: "acall"
304 }.to_json,
305 { "CONTENT_TYPE" => "application/json" }
306 )
307
308 assert last_response.ok?
309 assert_equal(
310 "<?xml version=\"1.0\" encoding=\"utf-8\" ?><Response>" \
311 "<SpeakSentence>Your credentials are invalid, please contact support." \
312 "</SpeakSentence></Response>",
313 last_response.body
314 )
315 end
316 em :test_outbound_no_customer
317
318 def test_outbound_atlimit_digits
319 post(
320 "/outbound/calls",
321 {
322 from: "ccustomerid_limit",
323 to: "+15557654321",
324 callId: "acall",
325 digits: "1"
326 }.to_json,
327 { "CONTENT_TYPE" => "application/json" }
328 )
329
330 assert last_response.ok?
331 assert_equal(
332 "<?xml version=\"1.0\" encoding=\"utf-8\" ?><Response>" \
333 "<Transfer transferCallerId=\"+15551234567\">" \
334 "<PhoneNumber>+15557654321</PhoneNumber></Transfer></Response>",
335 last_response.body
336 )
337 end
338 em :test_outbound_atlimit_digits
339
340 def test_inbound
341 CustomerFwd::BANDWIDTH_VOICE.expect(
342 :create_call,
343 OpenStruct.new(data: OpenStruct.new(call_id: "ocall")),
344 ["test_bw_account"],
345 body: Matching.new do |arg|
346 assert_equal(
347 "http://example.org/inbound/calls/acall?customer_id=customerid",
348 arg.answer_url
349 )
350 end
351 )
352
353 post(
354 "/inbound/calls",
355 {
356 from: "+15557654321",
357 to: "+15551234567",
358 callId: "acall"
359 }.to_json,
360 { "CONTENT_TYPE" => "application/json" }
361 )
362
363 assert last_response.ok?
364 assert_equal(
365 "<?xml version=\"1.0\" encoding=\"utf-8\" ?><Response>" \
366 "<Ring answerCall=\"false\" duration=\"300\" />" \
367 "</Response>",
368 last_response.body
369 )
370 assert_mock CustomerFwd::BANDWIDTH_VOICE
371 end
372 em :test_inbound
373
374 def test_inbound_from_reachability
375 CustomerFwd::BANDWIDTH_VOICE.expect(
376 :create_call,
377 OpenStruct.new(data: OpenStruct.new(call_id: "ocall")),
378 ["test_bw_account"],
379 body: Matching.new do |arg|
380 assert_equal(
381 "http://example.org/inbound/calls/acall?customer_id=customerid",
382 arg.answer_url
383 )
384 end
385 )
386
387 ReachableRedis.expect(
388 :exists,
389 EMPromise.resolve(0),
390 ["jmp_customer_reachability_voice-customerid"]
391 )
392
393 post(
394 "/inbound/calls",
395 {
396 from: "+14445556666",
397 to: "+15551234567",
398 callId: "acall"
399 }.to_json,
400 { "CONTENT_TYPE" => "application/json" }
401 )
402
403 assert last_response.ok?
404 assert_equal(
405 "<?xml version=\"1.0\" encoding=\"utf-8\" ?><Response>" \
406 "<Ring answerCall=\"false\" duration=\"300\" />" \
407 "</Response>",
408 last_response.body
409 )
410 assert_mock CustomerFwd::BANDWIDTH_VOICE
411 assert_mock ReachableRedis
412 end
413 em :test_inbound_from_reachability
414
415 def test_inbound_no_bwmsgsv2
416 CustomerFwd::BANDWIDTH_VOICE.expect(
417 :create_call,
418 OpenStruct.new(data: OpenStruct.new(call_id: "ocall")),
419 ["test_bw_account"],
420 body: Matching.new do |arg|
421 assert_equal(
422 "http://example.org/inbound/calls/acall?customer_id=customerid2",
423 arg.answer_url
424 )
425 end
426 )
427
428 post(
429 "/inbound/calls",
430 {
431 from: "+15557654321",
432 to: "+15551230000",
433 callId: "acall"
434 }.to_json,
435 { "CONTENT_TYPE" => "application/json" }
436 )
437
438 assert last_response.ok?
439 assert_equal(
440 "<?xml version=\"1.0\" encoding=\"utf-8\" ?><Response>" \
441 "<Ring answerCall=\"false\" duration=\"300\" />" \
442 "</Response>",
443 last_response.body
444 )
445 assert_mock CustomerFwd::BANDWIDTH_VOICE
446 end
447 em :test_inbound_no_bwmsgsv2
448
449 def test_inbound_low
450 ExpiringLock::REDIS.expect(
451 :set,
452 EMPromise.resolve(nil),
453 ["jmp_customer_low_balance-customerid_low", Time, "EX", 604800, "NX"]
454 )
455
456 post(
457 "/inbound/calls",
458 {
459 from: "+15557654321",
460 to: "+15551234560",
461 callId: "acall"
462 }.to_json,
463 { "CONTENT_TYPE" => "application/json" }
464 )
465
466 assert last_response.ok?
467 assert_equal(
468 "<?xml version=\"1.0\" encoding=\"utf-8\" ?><Response>" \
469 "<Redirect redirectUrl=\"/inbound/calls/acall/voicemail\" />" \
470 "</Response>",
471 last_response.body
472 )
473 assert_mock CustomerFwd::BANDWIDTH_VOICE
474 assert_mock ExpiringLock::REDIS
475 end
476 em :test_inbound_low
477
478 def test_inbound_leg2
479 post(
480 "/inbound/calls/acall?customer_id=customerid",
481 {
482 from: "+15557654321",
483 to: "sip:boop@example.com",
484 callId: "ocall"
485 }.to_json,
486 { "CONTENT_TYPE" => "application/json" }
487 )
488
489 assert last_response.ok?
490 assert_equal(
491 "<?xml version=\"1.0\" encoding=\"utf-8\" ?><Response>" \
492 "<Tag>connected</Tag><Bridge>acall</Bridge>" \
493 "</Response>",
494 last_response.body
495 )
496 end
497 em :test_inbound_leg2
498
499 def test_inbound_limit_leg2
500 path = "/inbound/calls/acall?customer_id=customerid_limit"
501
502 post(
503 path,
504 {
505 from: "+15557654321",
506 to: "sip:boop@example.com",
507 callId: "ocall"
508 }.to_json,
509 { "CONTENT_TYPE" => "application/json" }
510 )
511
512 assert last_response.ok?
513 assert_equal(
514 "<?xml version=\"1.0\" encoding=\"utf-8\" ?><Response>" \
515 "<Gather gatherUrl=\"#{path}\" maxDigits=\"1\" " \
516 "repeatCount=\"3\"><SpeakSentence>This call will take you over " \
517 "your configured monthly overage limit.</SpeakSentence><SpeakSentence>" \
518 "Change your limit in your account settings or press 1 to accept the " \
519 "charges. You can hang up to send the caller to voicemail." \
520 "</SpeakSentence></Gather></Response>",
521 last_response.body
522 )
523 end
524 em :test_inbound_limit_leg2
525
526 def test_inbound_limit_digits_leg2
527 post(
528 "/inbound/calls/acall?customer_id=customerid_limit",
529 {
530 from: "+15557654321",
531 to: "sip:boop@example.com",
532 callId: "ocall",
533 digits: "1"
534 }.to_json,
535 { "CONTENT_TYPE" => "application/json" }
536 )
537
538 assert last_response.ok?
539 assert_equal(
540 "<?xml version=\"1.0\" encoding=\"utf-8\" ?><Response>" \
541 "<Tag>connected</Tag><Bridge>acall</Bridge>" \
542 "</Response>",
543 last_response.body
544 )
545 end
546 em :test_inbound_limit_digits_leg2
547
548 def test_inbound_limit_hangup
549 Web::BANDWIDTH_VOICE.expect(
550 :modify_call,
551 nil,
552 [
553 "test_bw_account",
554 "bcall"
555 ],
556 body: Matching.new do |arg|
557 assert_equal(
558 "http://example.org/inbound/calls/oocall/voicemail",
559 arg.redirect_url
560 )
561 end
562 )
563
564 post(
565 "/inbound/calls/bcall/transfer_complete",
566 {
567 from: "+15557654321",
568 to: "+15551234561",
569 callId: "oocall",
570 cause: "hangup"
571 }.to_json,
572 { "CONTENT_TYPE" => "application/json" }
573 )
574
575 assert last_response.ok?
576 assert_mock Web::BANDWIDTH_VOICE
577 end
578 em :test_inbound_limit_hangup
579
580 def test_voicemail
581 language_id = stub_request(:post, "https://api.rev.ai/languageid/v1/jobs")
582 .with(body: {
583 metadata: {
584 media_url: "https://jmp.chat/media",
585 from_jid: "+15557654321@component",
586 customer_id: "customerid"
587 }.to_json,
588 source_config: {
589 url: "https://jmp.chat/media"
590 },
591 notification_config: {
592 url: "http://example.org/inbound/calls/CALLID/voicemail/language_id"
593 }
594 }.to_json)
595
596 Customer::BLATHER.expect(
597 :<<,
598 nil,
599 [Matching.new do |stanza|
600 assert_equal "+15557654321@component", stanza.from.to_s
601 assert_equal "customer@example.com", stanza.to.to_s
602 assert_equal "https://jmp.chat/media", OOB.find_or_create(stanza).url
603 end]
604 )
605
606 post(
607 "/inbound/calls/CALLID/voicemail/audio",
608 {
609 "startTime" => "2021-01-01T00:00:00Z",
610 "endTime" => "2021-01-01T00:00:06Z",
611 "mediaUrl" => "https://voice.bandwidth.com/api/v2/accounts/1/media",
612 "to" => "+15551234567",
613 "from" => "+15557654321"
614 }.to_json,
615 { "CONTENT_TYPE" => "application/json" }
616 )
617
618 assert last_response.ok?
619 assert_mock Customer::BLATHER
620 assert_requested language_id
621 end
622 em :test_voicemail
623
624 def test_anonymous_voicemail
625 language_id = stub_request(:post, "https://api.rev.ai/languageid/v1/jobs")
626 .with(body: {
627 metadata: {
628 media_url: "https://jmp.chat/media",
629 from_jid:
630 "16;phone-context=anonymous.phone-context.soprani.ca@component",
631 customer_id: "customerid"
632 }.to_json,
633 source_config: {
634 url: "https://jmp.chat/media"
635 },
636 notification_config: {
637 url: "http://example.org/inbound/calls/CALLID/voicemail/language_id"
638 }
639 }.to_json)
640
641 Customer::BLATHER.expect(
642 :<<,
643 nil,
644 [Matching.new do |stanza|
645 assert_equal(
646 "16;phone-context=anonymous.phone-context.soprani.ca@component",
647 stanza.from.to_s
648 )
649 assert_equal "customer@example.com", stanza.to.to_s
650 assert_equal "https://jmp.chat/media", OOB.find_or_create(stanza).url
651 end]
652 )
653
654 post(
655 "/inbound/calls/CALLID/voicemail/audio",
656 {
657 "startTime" => "2021-01-01T00:00:00Z",
658 "endTime" => "2021-01-01T00:00:06Z",
659 "mediaUrl" => "https://voice.bandwidth.com/api/v2/accounts/1/media",
660 "to" => "+15551234567",
661 "from" => "Anonymous"
662 }.to_json,
663 { "CONTENT_TYPE" => "application/json" }
664 )
665
666 assert last_response.ok?
667 assert_mock Customer::BLATHER
668 assert_requested language_id
669 end
670 em :test_anonymous_voicemail
671
672 def test_voicemail_short
673 post(
674 "/inbound/calls/CALLID/voicemail/audio",
675 {
676 "startTime" => "2021-01-01T00:00:00Z",
677 "endTime" => "2021-01-01T00:00:05Z"
678 }.to_json,
679 { "CONTENT_TYPE" => "application/json" }
680 )
681
682 assert last_response.ok?
683 assert_mock Customer::BLATHER
684 end
685 em :test_voicemail_short
686
687 def test_voicemail_no_customer
688 post(
689 "/inbound/calls/CALLID/voicemail",
690 {
691 "startTime" => "2021-01-01T00:00:00Z",
692 "endTime" => "2021-01-01T00:00:06Z",
693 "mediaUrl" => "https://voice.bandwidth.com/api/v2/accounts/1/media",
694 "to" => "+15551200000",
695 "from" => "+15557654321"
696 }.to_json,
697 { "CONTENT_TYPE" => "application/json" }
698 )
699
700 assert last_response.ok?
701 assert_equal(
702 "<?xml version=\"1.0\" encoding=\"utf-8\" ?><Response>" \
703 "<SpeakSentence>The number you have dialled is not in service." \
704 "</SpeakSentence></Response>",
705 last_response.body
706 )
707 end
708 em :test_voicemail_no_customer
709
710 def test_inbound_from_reachability_during_reachability
711 ReachableRedis.expect(
712 :exists,
713 EMPromise.resolve(1),
714 ["jmp_customer_reachability_voice-customerid_reach"]
715 )
716 ReachableRedis.expect(
717 :incr,
718 EMPromise.resolve(1),
719 ["jmp_customer_reachability_voice-customerid_reach"]
720 )
721
722 post(
723 "/inbound/calls",
724 {
725 from: "+14445556666",
726 to: "+15551234563",
727 callId: "acall"
728 }.to_json,
729 { "CONTENT_TYPE" => "application/json" }
730 )
731
732 assert last_response.ok?
733 assert_equal(
734 "<?xml version=\"1.0\" encoding=\"utf-8\" ?><Response>" \
735 "<Hangup />" \
736 "</Response>",
737 last_response.body
738 )
739 assert_mock CustomerFwd::BANDWIDTH_VOICE
740 assert_mock ReachableRedis
741 end
742 em :test_inbound_from_reachability_during_reachability
743end