test_web.rb

  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			"<Transfer transferCallerId=\"+15551234567\">" \
110			"<PhoneNumber>+15557654321</PhoneNumber></Transfer></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			"<Transfer transferCallerId=\"+15551234567\">" \
185			"<PhoneNumber>+15557654321</PhoneNumber></Transfer></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			"<Transfer transferCallerId=\"+15551234567\">" \
255			"<PhoneNumber>+15557654321</PhoneNumber></Transfer></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