test_web.rb

  1# frozen_string_literal: true
  2
  3require "rack/test"
  4require "test_helper"
  5require_relative "../web"
  6
  7Customer::BLATHER = Minitest::Mock.new
  8CustomerFwd::BANDWIDTH_VOICE = Minitest::Mock.new
  9Web::BANDWIDTH_VOICE = Minitest::Mock.new
 10
 11class WebTest < Minitest::Test
 12	include Rack::Test::Methods
 13
 14	def app
 15		Web.opts[:customer_repo] = CustomerRepo.new(
 16			redis: FakeRedis.new(
 17				"jmp_customer_jid-customerid" => "customer@example.com",
 18				"catapult_jid-+15551234567" => "customer_customerid@component",
 19				"jmp_customer_jid-customerid_low" => "customer@example.com",
 20				"catapult_jid-+15551234560" => "customer_customerid_low@component",
 21				"jmp_customer_jid-customerid_limit" => "customer@example.com",
 22				"catapult_jid-+15551234561" => "customer_customerid_limit@component"
 23			),
 24			db: FakeDB.new(
 25				["customerid"] => [{
 26					"balance" => BigDecimal(10),
 27					"plan_name" => "test_usd",
 28					"expires_at" => Time.now + 100
 29				}],
 30				["customerid_low"] => [{
 31					"balance" => BigDecimal("0.01"),
 32					"plan_name" => "test_usd",
 33					"expires_at" => Time.now + 100
 34				}],
 35				["customerid_limit"] => [{
 36					"balance" => BigDecimal(10),
 37					"plan_name" => "test_usd",
 38					"expires_at" => Time.now + 100
 39				}]
 40			),
 41			sgx_repo: Bwmsgsv2Repo.new(
 42				redis: FakeRedis.new(
 43					"catapult_fwd-+15551234567" => "xmpp:customer@example.com",
 44					"catapult_fwd_timeout-customer_customerid@component" => "30",
 45					"catapult_fwd-+15551234560" => "xmpp:customer@example.com",
 46					"catapult_fwd_timeout-customer_customerid_low@component" => "30",
 47					"catapult_fwd-+15551234561" => "xmpp:customer@example.com",
 48					"catapult_fwd_timeout-customer_customerid_limit@component" => "30"
 49				),
 50				ibr_repo: FakeIBRRepo.new(
 51					"sgx" => {
 52						"customer_customerid@component" => IBR.new.tap do |ibr|
 53							ibr.phone = "+15551234567"
 54						end,
 55						"customer_customerid_limit@component" => IBR.new.tap do |ibr|
 56							ibr.phone = "+15551234567"
 57						end
 58					}
 59				)
 60			)
 61		)
 62		Web.opts[:call_attempt_repo] = CallAttemptRepo.new(
 63			db: FakeDB.new(
 64				["test_usd", "+15557654321", :outbound] => [{ "rate" => 0.01 }],
 65				["test_usd", "+15557654321", :inbound] => [{ "rate" => 0.01 }],
 66				["customerid_limit"] => [{ "a" => -1000 }],
 67				["customerid_low"] => [{ "a" => -1000 }]
 68			)
 69		)
 70		Web.opts[:common_logger] = FakeLog.new
 71		Web.instance_variable_set(:@outbound_transfers, { "bcall" => "oocall" })
 72		Web.app
 73	end
 74
 75	def test_outbound_forwards
 76		post(
 77			"/outbound/calls",
 78			{
 79				from: "customerid",
 80				to: "+15557654321",
 81				callId: "acall"
 82			}.to_json,
 83			{ "CONTENT_TYPE" => "application/json" }
 84		)
 85
 86		assert last_response.ok?
 87		assert_equal(
 88			"<?xml version=\"1.0\" encoding=\"utf-8\" ?><Response>" \
 89			"<Forward from=\"+15551234567\" to=\"+15557654321\" />" \
 90			"</Response>",
 91			last_response.body
 92		)
 93	end
 94	em :test_outbound_forwards
 95
 96	def test_outbound_low_balance
 97		post(
 98			"/outbound/calls",
 99			{
100				from: "customerid_low",
101				to: "+15557654321",
102				callId: "acall"
103			}.to_json,
104			{ "CONTENT_TYPE" => "application/json" }
105		)
106
107		assert last_response.ok?
108		assert_equal(
109			"<?xml version=\"1.0\" encoding=\"utf-8\" ?><Response>" \
110			"<SpeakSentence>Your balance of $0.01 is not enough to " \
111			"complete this call.</SpeakSentence></Response>",
112			last_response.body
113		)
114	end
115	em :test_outbound_low_balance
116
117	def test_outbound_unsupported
118		post(
119			"/outbound/calls",
120			{
121				from: "customerid_limit",
122				to: "+95557654321",
123				callId: "acall"
124			}.to_json,
125			{ "CONTENT_TYPE" => "application/json" }
126		)
127
128		assert last_response.ok?
129		assert_equal(
130			"<?xml version=\"1.0\" encoding=\"utf-8\" ?><Response>" \
131			"<SpeakSentence>The number you have dialled is not " \
132			"supported on your account.</SpeakSentence></Response>",
133			last_response.body
134		)
135	end
136	em :test_outbound_unsupported
137
138	def test_outbound_atlimit
139		post(
140			"/outbound/calls",
141			{
142				from: "customerid_limit",
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			"<Gather gatherUrl=\"\/outbound/calls\" maxDigits=\"1\" " \
153			"repeatCount=\"3\"><SpeakSentence>This call will take you over " \
154			"your configured monthly overage limit.</SpeakSentence><SpeakSentence>" \
155			"Change your limit in your account settings or press 1 to accept the " \
156			"charges. You can hang up to cancel.</SpeakSentence></Gather></Response>",
157			last_response.body
158		)
159	end
160	em :test_outbound_atlimit
161
162	def test_outbound_atlimit_digits
163		post(
164			"/outbound/calls",
165			{
166				from: "customerid_limit",
167				to: "+15557654321",
168				callId: "acall",
169				digits: "1"
170			}.to_json,
171			{ "CONTENT_TYPE" => "application/json" }
172		)
173
174		assert last_response.ok?
175		assert_equal(
176			"<?xml version=\"1.0\" encoding=\"utf-8\" ?><Response>" \
177			"<Forward from=\"+15551234567\" to=\"+15557654321\" />" \
178			"</Response>",
179			last_response.body
180		)
181	end
182	em :test_outbound_atlimit_digits
183
184	def test_inbound
185		CustomerFwd::BANDWIDTH_VOICE.expect(
186			:create_call,
187			OpenStruct.new(data: OpenStruct.new(call_id: "ocall")),
188			[
189				"test_bw_account",
190				Matching.new { |arg| assert_equal [:body], arg.keys }
191			]
192		)
193
194		post(
195			"/inbound/calls",
196			{
197				from: "+15557654321",
198				to: "+15551234567",
199				callId: "acall"
200			}.to_json,
201			{ "CONTENT_TYPE" => "application/json" }
202		)
203
204		assert last_response.ok?
205		assert_equal(
206			"<?xml version=\"1.0\" encoding=\"utf-8\" ?><Response>" \
207			"<Ring answerCall=\"false\" duration=\"300\" />" \
208			"</Response>",
209			last_response.body
210		)
211		assert_mock CustomerFwd::BANDWIDTH_VOICE
212	end
213	em :test_inbound
214
215	def test_inbound_low
216		post(
217			"/inbound/calls",
218			{
219				from: "+15557654321",
220				to: "+15551234560",
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			"<Redirect redirectUrl=\"/inbound/calls/acall/voicemail\" />" \
230			"</Response>",
231			last_response.body
232		)
233		assert_mock CustomerFwd::BANDWIDTH_VOICE
234	end
235	em :test_inbound_low
236
237	def test_inbound_leg2
238		post(
239			"/inbound/calls/acall",
240			{
241				from: "+15557654321",
242				to: "+15551234567",
243				callId: "ocall"
244			}.to_json,
245			{ "CONTENT_TYPE" => "application/json" }
246		)
247
248		assert last_response.ok?
249		assert_equal(
250			"<?xml version=\"1.0\" encoding=\"utf-8\" ?><Response>" \
251			"<Tag>connected</Tag><Bridge>acall</Bridge>" \
252			"</Response>",
253			last_response.body
254		)
255	end
256	em :test_inbound_leg2
257
258	def test_inbound_limit_leg2
259		post(
260			"/inbound/calls/acall",
261			{
262				from: "+15557654321",
263				to: "+15551234561",
264				callId: "ocall"
265			}.to_json,
266			{ "CONTENT_TYPE" => "application/json" }
267		)
268
269		assert last_response.ok?
270		assert_equal(
271			"<?xml version=\"1.0\" encoding=\"utf-8\" ?><Response>" \
272			"<Gather gatherUrl=\"\/inbound/calls/acall\" maxDigits=\"1\" " \
273			"repeatCount=\"3\"><SpeakSentence>This call will take you over " \
274			"your configured monthly overage limit.</SpeakSentence><SpeakSentence>" \
275			"Change your limit in your account settings or press 1 to accept the " \
276			"charges. You can hang up to send the caller to voicemail." \
277			"</SpeakSentence></Gather></Response>",
278			last_response.body
279		)
280	end
281	em :test_inbound_limit_leg2
282
283	def test_inbound_limit_digits_leg2
284		post(
285			"/inbound/calls/acall",
286			{
287				from: "+15557654321",
288				to: "+15551234561",
289				callId: "ocall",
290				digits: "1"
291			}.to_json,
292			{ "CONTENT_TYPE" => "application/json" }
293		)
294
295		assert last_response.ok?
296		assert_equal(
297			"<?xml version=\"1.0\" encoding=\"utf-8\" ?><Response>" \
298			"<Tag>connected</Tag><Bridge>acall</Bridge>" \
299			"</Response>",
300			last_response.body
301		)
302	end
303	em :test_inbound_limit_digits_leg2
304
305	def test_inbound_limit_hangup
306		Web::BANDWIDTH_VOICE.expect(
307			:modify_call,
308			nil,
309			[
310				"test_bw_account",
311				"bcall",
312				Matching.new do |arg|
313					assert_equal [:body], arg.keys
314					assert_equal(
315						"http://example.org/inbound/calls/oocall/voicemail",
316						arg[:body].redirect_url
317					)
318				end
319			]
320		)
321
322		post(
323			"/inbound/calls/bcall/transfer_complete",
324			{
325				from: "+15557654321",
326				to: "+15551234561",
327				callId: "oocall",
328				cause: "hangup"
329			}.to_json,
330			{ "CONTENT_TYPE" => "application/json" }
331		)
332
333		assert last_response.ok?
334		assert_mock Web::BANDWIDTH_VOICE
335	end
336	em :test_inbound_limit_hangup
337
338	def test_voicemail
339		Customer::BLATHER.expect(
340			:<<,
341			nil,
342			[Matching.new do |stanza|
343				assert_equal "+15557654321@component", stanza.from.to_s
344				assert_equal "customer@example.com", stanza.to.to_s
345				assert_equal "https://jmp.chat/media", OOB.find_or_create(stanza).url
346			end]
347		)
348
349		post(
350			"/inbound/calls/CALLID/voicemail/audio",
351			{
352				"startTime" => "2021-01-01T00:00:00Z",
353				"endTime" => "2021-01-01T00:00:06Z",
354				"mediaUrl" => "https://voice.bandwidth.com/api/v2/accounts/1/media",
355				"to" => "+15551234567",
356				"from" => "+15557654321"
357			}.to_json,
358			{ "CONTENT_TYPE" => "application/json" }
359		)
360
361		assert last_response.ok?
362		assert_mock Customer::BLATHER
363	end
364	em :test_voicemail
365
366	def test_anonymous_voicemail
367		Customer::BLATHER.expect(
368			:<<,
369			nil,
370			[Matching.new do |stanza|
371				assert_equal(
372					"16;phone-context=anonymous.phone-context.soprani.ca@component",
373					stanza.from.to_s
374				)
375				assert_equal "customer@example.com", stanza.to.to_s
376				assert_equal "https://jmp.chat/media", OOB.find_or_create(stanza).url
377			end]
378		)
379
380		post(
381			"/inbound/calls/CALLID/voicemail/audio",
382			{
383				"startTime" => "2021-01-01T00:00:00Z",
384				"endTime" => "2021-01-01T00:00:06Z",
385				"mediaUrl" => "https://voice.bandwidth.com/api/v2/accounts/1/media",
386				"to" => "+15551234567",
387				"from" => "Anonymous"
388			}.to_json,
389			{ "CONTENT_TYPE" => "application/json" }
390		)
391
392		assert last_response.ok?
393		assert_mock Customer::BLATHER
394	end
395	em :test_anonymous_voicemail
396
397	def test_voicemail_short
398		post(
399			"/inbound/calls/CALLID/voicemail/audio",
400			{
401				"startTime" => "2021-01-01T00:00:00Z",
402				"endTime" => "2021-01-01T00:00:05Z"
403			}.to_json,
404			{ "CONTENT_TYPE" => "application/json" }
405		)
406
407		assert last_response.ok?
408		assert_mock Customer::BLATHER
409	end
410	em :test_voicemail_short
411end