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