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::CreditCardSale = Minitest::Mock.new
  14Transaction::DB = Minitest::Mock.new
  15
  16ReachableRedis = Minitest::Mock.new
  17
  18class WebTest < Minitest::Test
  19	include Rack::Test::Methods
  20
  21	def setup
  22		@cdr_repo = CDRRepo.new(db: FakeDB.new)
  23	end
  24
  25	def app
  26		Web.opts[:customer_repo] = CustomerRepo.new(
  27			redis: FakeRedis.new(
  28				"jmp_customer_jid-customerid" => "customer@example.com",
  29				"catapult_jid-+15551234567" => "customer_customerid@component",
  30				"jmp_customer_jid-customerid_low" => "customer@example.com",
  31				"catapult_jid-+15551234560" => "customer_customerid_low@component",
  32				"jmp_customer_jid-customerid_topup" => "customer@example.com",
  33				"jmp_customer_auto_top_up_amount-customerid_topup" => "15",
  34				"jmp_customer_monthly_overage_limit-customerid_topup" => "99999",
  35				"catapult_jid-+15551234562" => "customer_customerid_topup@component",
  36				"jmp_customer_jid-customerid_limit" => "customer@example.com",
  37				"catapult_jid-+15551234561" => "customer_customerid_limit@component",
  38				"jmp_customer_jid-customerid_reach" => "customerid_reach@example.com",
  39				"catapult_jid-+15551234563" => "customer_customerid_reach@component",
  40				"jmp_customer_jid-customerid2" => "customer2@example.com",
  41				"catapult_jid-+15551230000" => "customer_customerid2@component",
  42				"jmp_customer_jid-customerid_tombed" => "customer_tombed@example.com",
  43				"jmp_customer_jid-bulkcustomer" => "bulk@example.com"
  44			),
  45			db: FakeDB.new(
  46				["customerid"] => [{
  47					"balance" => BigDecimal(10),
  48					"plan_name" => "test_usd",
  49					"expires_at" => Time.now + 100
  50				}],
  51				["customerid2"] => [{
  52					"balance" => BigDecimal(10),
  53					"plan_name" => "test_usd",
  54					"expires_at" => Time.now + 100
  55				}],
  56				["customerid_low"] => [{
  57					"balance" => BigDecimal("0.01"),
  58					"plan_name" => "test_usd",
  59					"expires_at" => Time.now + 100
  60				}],
  61				["customerid_topup"] => [{
  62					"balance" => BigDecimal("0.01"),
  63					"plan_name" => "test_usd",
  64					"expires_at" => Time.now + 100
  65				}],
  66				["customerid_reach"] => [{
  67					"balance" => BigDecimal(10),
  68					"plan_name" => "test_usd",
  69					"expires_at" => Time.now + 100
  70				}],
  71				["customerid_limit"] => [{
  72					"balance" => BigDecimal(10),
  73					"plan_name" => "test_usd",
  74					"expires_at" => Time.now + 100
  75				}],
  76				["customerid_tombed"] => [{
  77					"balance" => BigDecimal(10),
  78					"plan_name" => "test_usd",
  79					"expires_at" => Time.now + 100
  80				}],
  81				["bulkcustomer"] => [{
  82					"balance" => BigDecimal(1000),
  83					"plan_name" => "test_usd",
  84					"expires_at" => Time.now + 100
  85				}]
  86			),
  87			sgx_repo: Bwmsgsv2Repo.new(
  88				redis: FakeRedis.new(
  89					"catapult_fwd-+15551234567" => "xmpp:customer@example.com",
  90					"catapult_fwd_timeout-customer_customerid@component" => "30",
  91					"catapult_fwd-+15551234560" => "xmpp:customer@example.com",
  92					"catapult_fwd_timeout-customer_customerid_low@component" => "30",
  93					"catapult_fwd-+15551234561" => "xmpp:customer@example.com",
  94					"catapult_fwd_timeout-customer_customerid_limit@component" => "30",
  95					"catapult_fwd-+15551234563" => "xmpp:customer@example.com",
  96					"catapult_fwd_timeout-customer_customerid_reach@component" => "30",
  97					"catapult_fwd-+15551230000" => "xmpp:customer2@example.com",
  98					"catapult_fwd_timeout-customer_customerid2@component" => "30"
  99				),
 100				ibr_repo: FakeIBRRepo.new(
 101					"sgx" => {
 102						"customer_customerid@component" =>
 103							Blather::Stanza::Iq::IBR.new.tap do |ibr|
 104								ibr.phone = "+15551234567"
 105							end,
 106						"customer_customerid_low@component" =>
 107							Blather::Stanza::Iq::IBR.new.tap do |ibr|
 108								ibr.phone = "+15551234567"
 109							end,
 110						"customer_customerid_topup@component" =>
 111							Blather::Stanza::Iq::IBR.new.tap do |ibr|
 112								ibr.phone = "+15551234567"
 113							end,
 114						"customer_customerid_reach@component" =>
 115							Blather::Stanza::Iq::IBR.new.tap do |ibr|
 116								ibr.phone = "+15551234563"
 117							end,
 118						"customer_customerid_limit@component" =>
 119							Blather::Stanza::Iq::IBR.new.tap do |ibr|
 120								ibr.phone = "+15551234567"
 121							end
 122					}
 123				)
 124			)
 125		)
 126		Web.opts[:call_attempt_repo] = CallAttemptRepo.new(
 127			redis: FakeRedis.new(
 128				"jmp_customer_activater-customerid" => "+15551234567",
 129				"jmp_customer_activater-customerid2" => "+15551234567",
 130				"jmp_customer_activater-customerid_limit" => "+15551234567",
 131				"jmp_customer_activater-customerid_low" => "+15551234567",
 132				"jmp_customer_activater-customerid_topup" => "+15551234567"
 133			),
 134			db: FakeDB.new(
 135				["test_usd", "+15557654321", :outbound] => [{ "rate" => 0.01 }],
 136				["test_usd", "+1911", :outbound] => [{ "rate" => 0.01 }],
 137				["test_usd", "+19116", :outbound] => [{ "rate" => 0.01 }],
 138				["test_usd", "+15557654321", :inbound] => [{ "rate" => 0.01 }],
 139				["test_usd", "+14445556666", :inbound] => [{ "rate" => 0.01 }],
 140				["test_usd", "+18001234567", :outbound] => [{ "rate" => 0.00 }],
 141				["customerid_limit"] => FakeDB::MultiResult.new(
 142					[{ "a" => 1000 }],
 143					[],
 144					[{ "settled_amount" => 15 }]
 145				),
 146				["customerid_low"] => FakeDB::MultiResult.new(
 147					[{ "a" => 1000 }],
 148					[],
 149					[{ "settled_amount" => 15 }]
 150				),
 151				["customerid_topup"] => FakeDB::MultiResult.new(
 152					[{ "a" => 1000 }],
 153					[],
 154					[{ "settled_amount" => 15 }]
 155				)
 156			)
 157		)
 158		Web.opts[:cdr_repo] = @cdr_repo
 159		Web.opts[:trust_level_repo] = TrustLevelRepo.new(
 160			db: FakeDB.new,
 161			redis: FakeRedis.new(
 162				"jmp_customer_trust_level-customerid_tombed" => "Tomb"
 163			)
 164		)
 165		Web.opts[:common_logger] = FakeLog.new
 166		Web.opts[:reachability_repo] = ReachabilityRepo::Voice.new(
 167			redis: ReachableRedis,
 168			senders: ["+14445556666"]
 169		)
 170		Web.instance_variable_set(:@outbound_transfers, { "bcall" => "oocall" })
 171		Web.app
 172	end
 173
 174	def test_outbound_forwards
 175		CustomerPlan::DB.expect(
 176			:query_one, {}, [String, "customerid"]
 177		)
 178
 179		post(
 180			"/outbound/calls",
 181			{
 182				from: "ccustomerid",
 183				to: "+15557654321",
 184				callId: "acall"
 185			}.to_json,
 186			{ "CONTENT_TYPE" => "application/json" }
 187		)
 188
 189		assert last_response.ok?
 190		assert_equal(
 191			"<?xml version=\"1.0\" encoding=\"utf-8\" ?><Response>" \
 192			"<Transfer transferCallerId=\"+15551234567\">" \
 193			"<PhoneNumber>+15557654321</PhoneNumber></Transfer></Response>",
 194			last_response.body
 195		)
 196		assert_mock CustomerPlan::DB
 197	end
 198	em :test_outbound_forwards
 199
 200	def test_outbound_low_balance
 201		CustomerPlan::DB.expect(
 202			:query_one, {}, [String, "customerid_low"]
 203		)
 204
 205		ExpiringLock::REDIS.expect(
 206			:set,
 207			EMPromise.resolve(nil),
 208			["jmp_customer_low_balance-customerid_low", Time, "EX", 604800, "NX"]
 209		)
 210
 211		post(
 212			"/outbound/calls",
 213			{
 214				from: "ccustomerid_low",
 215				to: "+15557654321",
 216				callId: "acall"
 217			}.to_json,
 218			{ "CONTENT_TYPE" => "application/json" }
 219		)
 220
 221		assert last_response.ok?
 222		assert_equal(
 223			"<?xml version=\"1.0\" encoding=\"utf-8\" ?><Response>" \
 224			"<SpeakSentence>Your balance of $0.01 is not enough to " \
 225			"complete this call.</SpeakSentence></Response>",
 226			last_response.body
 227		)
 228		assert_mock ExpiringLock::REDIS
 229		assert_mock CustomerPlan::DB
 230	end
 231	em :test_outbound_low_balance
 232
 233	def test_outbound_low_balance_top_up
 234		CustomerPlan::DB.expect(
 235			:query_one, {}, [String, "customerid_topup"]
 236		)
 237
 238		LowBalance::AutoTopUp::CreditCardSale.expect(
 239			:create,
 240			EMPromise.resolve(
 241				OpenStruct.new(total: 15)
 242			),
 243			[Customer], amount: 15
 244		)
 245
 246		ExpiringLock::REDIS.expect(
 247			:set,
 248			EMPromise.resolve("OK"),
 249			["jmp_customer_low_balance-customerid_topup", Time, "EX", 604800, "NX"]
 250		)
 251
 252		CustomerFinancials::REDIS.expect(
 253			:smembers,
 254			EMPromise.resolve([]),
 255			["block_credit_cards"]
 256		)
 257		LowBalance::AutoTopUp::REDIS.expect(
 258			:exists,
 259			0,
 260			["jmp_auto_top_up_block-abcd"]
 261		)
 262		LowBalance::AutoTopUp::REDIS.expect(
 263			:del,
 264			EMPromise.resolve(1),
 265			["jmp_customer_low_balance-customerid_topup"]
 266		)
 267		braintree_customer = Minitest::Mock.new
 268		CustomerFinancials::BRAINTREE.expect(:customer, braintree_customer)
 269		payment_methods = OpenStruct.new(payment_methods: [
 270			OpenStruct.new(default?: true, unique_number_identifier: "abcd")
 271		])
 272		braintree_customer.expect(
 273			:find,
 274			EMPromise.resolve(payment_methods),
 275			["customerid_topup"]
 276		)
 277
 278		Customer::BLATHER.expect(
 279			:<<,
 280			nil,
 281			[Blather::Stanza]
 282		)
 283
 284		post(
 285			"/outbound/calls",
 286			{
 287				from: "ccustomerid_topup",
 288				to: "+15557654321",
 289				callId: "acall"
 290			}.to_json,
 291			{ "CONTENT_TYPE" => "application/json" }
 292		)
 293
 294		assert last_response.ok?
 295		assert_equal(
 296			"<?xml version=\"1.0\" encoding=\"utf-8\" ?><Response>" \
 297			"<Transfer transferCallerId=\"+15551234567\">" \
 298			"<PhoneNumber>+15557654321</PhoneNumber></Transfer></Response>",
 299			last_response.body
 300		)
 301		assert_mock ExpiringLock::REDIS
 302		assert_mock Customer::BLATHER
 303		assert_mock LowBalance::AutoTopUp::CreditCardSale
 304		assert_mock LowBalance::AutoTopUp::REDIS
 305		assert_mock CustomerPlan::DB
 306	end
 307	em :test_outbound_low_balance_top_up
 308
 309	def test_outbound_unsupported
 310		CustomerPlan::DB.expect(
 311			:query_one, {}, [String, "customerid"]
 312		)
 313
 314		post(
 315			"/outbound/calls",
 316			{
 317				from: "ccustomerid",
 318				to: "+95557654321",
 319				callId: "acall"
 320			}.to_json,
 321			{ "CONTENT_TYPE" => "application/json" }
 322		)
 323
 324		assert last_response.ok?
 325		assert_equal(
 326			"<?xml version=\"1.0\" encoding=\"utf-8\" ?><Response>" \
 327			"<SpeakSentence>The number you have dialled is not " \
 328			"supported on your account.</SpeakSentence></Response>",
 329			last_response.body
 330		)
 331		assert_mock CustomerPlan::DB
 332	end
 333	em :test_outbound_unsupported
 334
 335	def test_outbound_unsupported_short_numbers_911
 336		CustomerPlan::DB.expect(
 337			:query_one, {}, [String, "customerid"]
 338		)
 339
 340		post(
 341			"/outbound/calls",
 342			{
 343				from: "ccustomerid",
 344				to: "+1911",
 345				callId: "acall"
 346			}.to_json,
 347			{ "CONTENT_TYPE" => "application/json" }
 348		)
 349
 350		assert last_response.ok?
 351		assert_equal(
 352			"<?xml version=\"1.0\" encoding=\"utf-8\" ?><Response>" \
 353			"<SpeakSentence>The number you have dialled is not " \
 354			"supported on your account.</SpeakSentence></Response>",
 355			last_response.body
 356		)
 357		assert_mock CustomerPlan::DB
 358	end
 359	em :test_outbound_unsupported_short_numbers_911
 360
 361	def test_outbound_supported_9116
 362		CustomerPlan::DB.expect(
 363			:query_one, {}, [String, "customerid"]
 364		)
 365
 366		post(
 367			"/outbound/calls",
 368			{
 369				from: "ccustomerid",
 370				to: "+19116",
 371				callId: "acall"
 372			}.to_json,
 373			{ "CONTENT_TYPE" => "application/json" }
 374		)
 375
 376		assert last_response.ok?
 377		assert_equal(
 378			"<?xml version=\"1.0\" encoding=\"utf-8\" ?><Response>" \
 379			"<Transfer transferCallerId=\"+15551234567\">" \
 380			"<PhoneNumber>+19116</PhoneNumber></Transfer></Response>",
 381			last_response.body
 382		)
 383		assert_mock CustomerPlan::DB
 384	end
 385	em :test_outbound_supported_9116
 386
 387	def test_outbound_atlimit
 388		CustomerPlan::DB.expect(
 389			:query_one, {}, [String, "customerid_limit"]
 390		)
 391
 392		post(
 393			"/outbound/calls",
 394			{
 395				from: "ccustomerid_limit",
 396				to: "+15557654321",
 397				callId: "acall"
 398			}.to_json,
 399			{ "CONTENT_TYPE" => "application/json" }
 400		)
 401
 402		assert last_response.ok?
 403		assert_equal(
 404			"<?xml version=\"1.0\" encoding=\"utf-8\" ?><Response>" \
 405			"<Gather gatherUrl=\"\/outbound/calls\" maxDigits=\"1\" " \
 406			"repeatCount=\"3\"><SpeakSentence>This call will take you over " \
 407			"your configured monthly overage limit.</SpeakSentence><SpeakSentence>" \
 408			"Change your limit in your account settings or press 1 to accept the " \
 409			"charges. You can hang up to cancel.</SpeakSentence></Gather></Response>",
 410			last_response.body
 411		)
 412		assert_mock CustomerPlan::DB
 413	end
 414	em :test_outbound_atlimit
 415
 416	def test_outbound_no_customer
 417		post(
 418			"/outbound/calls",
 419			{
 420				from: "no_such_customer",
 421				to: "+15557654321",
 422				callId: "acall"
 423			}.to_json,
 424			{ "CONTENT_TYPE" => "application/json" }
 425		)
 426
 427		assert last_response.ok?
 428		assert_equal(
 429			"<?xml version=\"1.0\" encoding=\"utf-8\" ?><Response>" \
 430			"<SpeakSentence>Your credentials are invalid, please contact support." \
 431			"</SpeakSentence></Response>",
 432			last_response.body
 433		)
 434	end
 435	em :test_outbound_no_customer
 436
 437	def test_outbound_atlimit_digits
 438		CustomerPlan::DB.expect(
 439			:query_one, {}, [String, "customerid_limit"]
 440		)
 441
 442		post(
 443			"/outbound/calls",
 444			{
 445				from: "ccustomerid_limit",
 446				to: "+15557654321",
 447				callId: "acall",
 448				digits: "1"
 449			}.to_json,
 450			{ "CONTENT_TYPE" => "application/json" }
 451		)
 452
 453		assert last_response.ok?
 454		assert_equal(
 455			"<?xml version=\"1.0\" encoding=\"utf-8\" ?><Response>" \
 456			"<Transfer transferCallerId=\"+15551234567\">" \
 457			"<PhoneNumber>+15557654321</PhoneNumber></Transfer></Response>",
 458			last_response.body
 459		)
 460		assert_mock CustomerPlan::DB
 461	end
 462	em :test_outbound_atlimit_digits
 463
 464	def test_outbound_toll_free
 465		CustomerPlan::DB.expect(
 466			:query_one, {}, [String, "customerid"]
 467		)
 468
 469		post(
 470			"/outbound/calls",
 471			{
 472				from: "ccustomerid",
 473				to: "+18001234567",
 474				callId: "acall"
 475			}.to_json,
 476			{ "CONTENT_TYPE" => "application/json" }
 477		)
 478
 479		assert last_response.ok?
 480		assert_equal(
 481			"<?xml version=\"1.0\" encoding=\"utf-8\" ?><Response>" \
 482			"<Transfer transferCallerId=\"+15551234567\">" \
 483			"<PhoneNumber>+18001234567</PhoneNumber></Transfer></Response>",
 484			last_response.body
 485		)
 486		assert_mock CustomerPlan::DB
 487	end
 488	em :test_outbound_toll_free
 489
 490	def test_outbound_disconnect
 491		CustomerPlan::DB.expect(
 492			:query_one, {}, [String, "customerid"]
 493		)
 494
 495		post(
 496			"/outbound/calls/status",
 497			{
 498				eventType: "disconnect",
 499				from: "ccustomerid",
 500				to: "+15557654321",
 501				callId: "acall",
 502				startTime: Time.now.to_s,
 503				endTime: Time.now.to_s,
 504				cause: "hangup"
 505			}.to_json,
 506			{ "CONTENT_TYPE" => "application/json" }
 507		)
 508
 509		assert last_response.ok?
 510		assert_equal("OK", last_response.body)
 511		assert_mock CustomerPlan::DB
 512	end
 513	em :test_outbound_disconnect
 514
 515	def test_outbound_disconnect_tombed
 516		CustomerPlan::DB.expect(
 517			:query_one, {}, [String, "customerid_tombed"]
 518		)
 519
 520		@cdr_repo.stub(:put, ->(*) { raise "put called" }) do
 521			post(
 522				"/outbound/calls/status",
 523				{
 524					eventType: "disconnect",
 525					from: "ccustomerid_tombed",
 526					to: "+15557654321",
 527					callId: "acall",
 528					startTime: Time.now.to_s,
 529					endTime: Time.now.to_s,
 530					cause: "hangup"
 531				}.to_json,
 532				{ "CONTENT_TYPE" => "application/json" }
 533			)
 534		end
 535
 536		assert last_response.ok?
 537		assert_equal("OK", last_response.body)
 538		assert_mock CustomerPlan::DB
 539	end
 540	em :test_outbound_disconnect_tombed
 541
 542	def test_inbound
 543		expected_answer_url =
 544			"http://example.org/inbound/calls/acall?customer_id=customerid"
 545
 546		CustomerPlan::DB.expect(
 547			:query_one, {}, [String, "customerid"]
 548		)
 549		CustomerFwd::BANDWIDTH_VOICE.expect(
 550			:create_call,
 551			OpenStruct.new(call_id: "ocall")
 552		) do |account, request, *|
 553			assert_equal("test_bw_account", account)
 554			assert_equal(expected_answer_url, request.answer_url)
 555		end
 556
 557		post(
 558			"/inbound/calls",
 559			{
 560				from: "+15557654321",
 561				to: "+15551234567",
 562				callId: "acall",
 563				applicationId: "app123"
 564			}.to_json,
 565			{ "CONTENT_TYPE" => "application/json" }
 566		)
 567
 568		assert last_response.ok?
 569		assert_equal(
 570			"<?xml version=\"1.0\" encoding=\"utf-8\" ?><Response>" \
 571			"<Ring answerCall=\"false\" duration=\"300\" />" \
 572			"</Response>",
 573			last_response.body
 574		)
 575		assert_mock CustomerFwd::BANDWIDTH_VOICE
 576		assert_mock CustomerPlan::DB
 577	end
 578	em :test_inbound
 579
 580	def test_inbound_from_reachability
 581		expected_answer_url =
 582			"http://example.org/inbound/calls/acall?customer_id=customerid"
 583
 584		CustomerPlan::DB.expect(
 585			:query_one, {}, [String, "customerid"]
 586		)
 587		CustomerFwd::BANDWIDTH_VOICE.expect(
 588			:create_call,
 589			OpenStruct.new(call_id: "ocall")
 590		) do |account, request, *|
 591			assert_equal("test_bw_account", account)
 592			assert_equal(expected_answer_url, request.answer_url)
 593		end
 594
 595		ReachableRedis.expect(
 596			:exists,
 597			EMPromise.resolve(0),
 598			["jmp_customer_reachability_voice-customerid"]
 599		)
 600
 601		post(
 602			"/inbound/calls",
 603			{
 604				from: "+14445556666",
 605				to: "+15551234567",
 606				callId: "acall",
 607				applicationId: "app123"
 608			}.to_json,
 609			{ "CONTENT_TYPE" => "application/json" }
 610		)
 611
 612		assert last_response.ok?
 613		assert_equal(
 614			"<?xml version=\"1.0\" encoding=\"utf-8\" ?><Response>" \
 615			"<Ring answerCall=\"false\" duration=\"300\" />" \
 616			"</Response>",
 617			last_response.body
 618		)
 619		assert_mock CustomerFwd::BANDWIDTH_VOICE
 620		assert_mock ReachableRedis
 621		assert_mock CustomerPlan::DB
 622	end
 623	em :test_inbound_from_reachability
 624
 625	def test_inbound_no_bwmsgsv2
 626		expected_answer_url =
 627			"http://example.org/inbound/calls/acall?customer_id=customerid2"
 628
 629		CustomerPlan::DB.expect(
 630			:query_one, {}, [String, "customerid2"]
 631		)
 632		CustomerFwd::BANDWIDTH_VOICE.expect(
 633			:create_call,
 634			OpenStruct.new(call_id: "ocall")
 635		) do |account, request, *|
 636			assert_equal("test_bw_account", account)
 637			assert_equal(expected_answer_url, request.answer_url)
 638		end
 639
 640		post(
 641			"/inbound/calls",
 642			{
 643				from: "+15557654321",
 644				to: "+15551230000",
 645				callId: "acall",
 646				applicationId: "app123"
 647			}.to_json,
 648			{ "CONTENT_TYPE" => "application/json" }
 649		)
 650
 651		assert last_response.ok?
 652		assert_equal(
 653			"<?xml version=\"1.0\" encoding=\"utf-8\" ?><Response>" \
 654			"<Ring answerCall=\"false\" duration=\"300\" />" \
 655			"</Response>",
 656			last_response.body
 657		)
 658		assert_mock CustomerFwd::BANDWIDTH_VOICE
 659		assert_mock CustomerPlan::DB
 660	end
 661	em :test_inbound_no_bwmsgsv2
 662
 663	def test_inbound_low
 664		CustomerPlan::DB.expect(
 665			:query_one, {}, [String, "customerid_low"]
 666		)
 667
 668		ExpiringLock::REDIS.expect(
 669			:set,
 670			EMPromise.resolve(nil),
 671			["jmp_customer_low_balance-customerid_low", Time, "EX", 604800, "NX"]
 672		)
 673
 674		post(
 675			"/inbound/calls",
 676			{
 677				from: "+15557654321",
 678				to: "+15551234560",
 679				callId: "acall"
 680			}.to_json,
 681			{ "CONTENT_TYPE" => "application/json" }
 682		)
 683
 684		assert last_response.ok?
 685		assert_equal(
 686			"<?xml version=\"1.0\" encoding=\"utf-8\" ?><Response>" \
 687			"<Redirect redirectUrl=\"/inbound/calls/acall/voicemail\" />" \
 688			"</Response>",
 689			last_response.body
 690		)
 691		assert_mock CustomerFwd::BANDWIDTH_VOICE
 692		assert_mock ExpiringLock::REDIS
 693		assert_mock CustomerPlan::DB
 694	end
 695	em :test_inbound_low
 696
 697	def test_inbound_leg2
 698		CustomerPlan::DB.expect(
 699			:query_one, {}, [String, "customerid"]
 700		)
 701
 702		post(
 703			"/inbound/calls/acall?customer_id=customerid",
 704			{
 705				from: "+15557654321",
 706				to: "sip:boop@example.com",
 707				callId: "ocall"
 708			}.to_json,
 709			{ "CONTENT_TYPE" => "application/json" }
 710		)
 711
 712		assert last_response.ok?
 713		assert_equal(
 714			"<?xml version=\"1.0\" encoding=\"utf-8\" ?><Response>" \
 715			"<Tag>connected</Tag><Bridge>acall</Bridge>" \
 716			"</Response>",
 717			last_response.body
 718		)
 719		assert_mock CustomerPlan::DB
 720	end
 721	em :test_inbound_leg2
 722
 723	def test_inbound_limit_leg2
 724		CustomerPlan::DB.expect(
 725			:query_one, {}, [String, "customerid_limit"]
 726		)
 727
 728		path = "/inbound/calls/acall?customer_id=customerid_limit"
 729
 730		post(
 731			path,
 732			{
 733				from: "+15557654321",
 734				to: "sip:boop@example.com",
 735				callId: "ocall"
 736			}.to_json,
 737			{ "CONTENT_TYPE" => "application/json" }
 738		)
 739
 740		assert last_response.ok?
 741		assert_equal(
 742			"<?xml version=\"1.0\" encoding=\"utf-8\" ?><Response>" \
 743			"<Gather gatherUrl=\"#{path}\" maxDigits=\"1\" " \
 744			"repeatCount=\"3\"><SpeakSentence>This call will take you over " \
 745			"your configured monthly overage limit.</SpeakSentence><SpeakSentence>" \
 746			"Change your limit in your account settings or press 1 to accept the " \
 747			"charges. You can hang up to send the caller to voicemail." \
 748			"</SpeakSentence></Gather></Response>",
 749			last_response.body
 750		)
 751		assert_mock CustomerPlan::DB
 752	end
 753	em :test_inbound_limit_leg2
 754
 755	def test_inbound_limit_digits_leg2
 756		CustomerPlan::DB.expect(
 757			:query_one, {}, [String, "customerid_limit"]
 758		)
 759
 760		post(
 761			"/inbound/calls/acall?customer_id=customerid_limit",
 762			{
 763				from: "+15557654321",
 764				to: "sip:boop@example.com",
 765				callId: "ocall",
 766				digits: "1"
 767			}.to_json,
 768			{ "CONTENT_TYPE" => "application/json" }
 769		)
 770
 771		assert last_response.ok?
 772		assert_equal(
 773			"<?xml version=\"1.0\" encoding=\"utf-8\" ?><Response>" \
 774			"<Tag>connected</Tag><Bridge>acall</Bridge>" \
 775			"</Response>",
 776			last_response.body
 777		)
 778		assert_mock CustomerPlan::DB
 779	end
 780	em :test_inbound_limit_digits_leg2
 781
 782	def test_inbound_limit_hangup
 783		Web::BANDWIDTH_VOICE.expect(:update_call, nil) do |acc, cid, body, **kw|
 784			assert_equal "test_bw_account", acc
 785			assert_equal "bcall", cid
 786			assert_equal(
 787				"http://example.org/inbound/calls/oocall/voicemail",
 788				body.redirect_url
 789			)
 790			assert_equal({}, kw)
 791		end
 792
 793		post(
 794			"/inbound/calls/bcall/transfer_complete",
 795			{
 796				from: "+15557654321",
 797				to: "+15551234561",
 798				callId: "oocall",
 799				cause: "hangup"
 800			}.to_json,
 801			{ "CONTENT_TYPE" => "application/json" }
 802		)
 803
 804		assert last_response.ok?
 805		assert_mock Web::BANDWIDTH_VOICE
 806	end
 807	em :test_inbound_limit_hangup
 808
 809	def test_voicemail
 810		language_id = stub_request(:post, "https://api.rev.ai/languageid/v1/jobs")
 811			.with(body: {
 812				metadata: {
 813					media_url: "https://jmp.chat/media",
 814					from_jid: "+15557654321@component",
 815					customer_id: "customerid"
 816				}.to_json,
 817				source_config: {
 818					url: "https://jmp.chat/media"
 819				},
 820				notification_config: {
 821					url: "http://example.org/inbound/calls/CALLID/voicemail/language_id"
 822				}
 823			}.to_json)
 824
 825		Customer::BLATHER.expect(
 826			:<<,
 827			nil,
 828			[Matching.new do |stanza|
 829				assert_equal "+15557654321@component", stanza.from.to_s
 830				assert_equal "customer@example.com", stanza.to.to_s
 831				assert_equal "https://jmp.chat/media", OOB.find_or_create(stanza).url
 832			end]
 833		)
 834
 835		post(
 836			"/inbound/calls/CALLID/voicemail/audio",
 837			{
 838				"startTime" => "2021-01-01T00:00:00Z",
 839				"endTime" => "2021-01-01T00:00:06Z",
 840				"mediaUrl" => "https://voice.bandwidth.com/api/v2/accounts/1/media",
 841				"to" => "+15551234567",
 842				"from" => "+15557654321"
 843			}.to_json,
 844			{ "CONTENT_TYPE" => "application/json" }
 845		)
 846
 847		assert last_response.ok?
 848		assert_mock Customer::BLATHER
 849		assert_requested language_id
 850	end
 851	em :test_voicemail
 852
 853	def test_anonymous_voicemail
 854		language_id = stub_request(:post, "https://api.rev.ai/languageid/v1/jobs")
 855			.with(body: {
 856				metadata: {
 857					media_url: "https://jmp.chat/media",
 858					from_jid:
 859						"16;phone-context=anonymous.phone-context.soprani.ca@component",
 860					customer_id: "customerid"
 861				}.to_json,
 862				source_config: {
 863					url: "https://jmp.chat/media"
 864				},
 865				notification_config: {
 866					url: "http://example.org/inbound/calls/CALLID/voicemail/language_id"
 867				}
 868			}.to_json)
 869
 870		Customer::BLATHER.expect(
 871			:<<,
 872			nil,
 873			[Matching.new do |stanza|
 874				assert_equal(
 875					"16;phone-context=anonymous.phone-context.soprani.ca@component",
 876					stanza.from.to_s
 877				)
 878				assert_equal "customer@example.com", stanza.to.to_s
 879				assert_equal "https://jmp.chat/media", OOB.find_or_create(stanza).url
 880			end]
 881		)
 882
 883		post(
 884			"/inbound/calls/CALLID/voicemail/audio",
 885			{
 886				"startTime" => "2021-01-01T00:00:00Z",
 887				"endTime" => "2021-01-01T00:00:06Z",
 888				"mediaUrl" => "https://voice.bandwidth.com/api/v2/accounts/1/media",
 889				"to" => "+15551234567",
 890				"from" => "Anonymous"
 891			}.to_json,
 892			{ "CONTENT_TYPE" => "application/json" }
 893		)
 894
 895		assert last_response.ok?
 896		assert_mock Customer::BLATHER
 897		assert_requested language_id
 898	end
 899	em :test_anonymous_voicemail
 900
 901	def test_voicemail_short
 902		post(
 903			"/inbound/calls/CALLID/voicemail/audio",
 904			{
 905				"startTime" => "2021-01-01T00:00:00Z",
 906				"endTime" => "2021-01-01T00:00:05Z"
 907			}.to_json,
 908			{ "CONTENT_TYPE" => "application/json" }
 909		)
 910
 911		assert last_response.ok?
 912		assert_mock Customer::BLATHER
 913	end
 914	em :test_voicemail_short
 915
 916	def test_voicemail_no_customer
 917		post(
 918			"/inbound/calls/CALLID/voicemail",
 919			{
 920				"startTime" => "2021-01-01T00:00:00Z",
 921				"endTime" => "2021-01-01T00:00:06Z",
 922				"mediaUrl" => "https://voice.bandwidth.com/api/v2/accounts/1/media",
 923				"to" => "+15551200000",
 924				"from" => "+15557654321"
 925			}.to_json,
 926			{ "CONTENT_TYPE" => "application/json" }
 927		)
 928
 929		assert last_response.ok?
 930		assert_equal(
 931			"<?xml version=\"1.0\" encoding=\"utf-8\" ?><Response>" \
 932			"<SpeakSentence>The number you have dialled is not in service." \
 933			"</SpeakSentence></Response>",
 934			last_response.body
 935		)
 936	end
 937	em :test_voicemail_no_customer
 938
 939	def test_inbound_from_reachability_during_reachability
 940		ReachableRedis.expect(
 941			:exists,
 942			EMPromise.resolve(1),
 943			["jmp_customer_reachability_voice-customerid_reach"]
 944		)
 945		ReachableRedis.expect(
 946			:incr,
 947			EMPromise.resolve(1),
 948			["jmp_customer_reachability_voice-customerid_reach"]
 949		)
 950
 951		post(
 952			"/inbound/calls",
 953			{
 954				from: "+14445556666",
 955				to: "+15551234563",
 956				callId: "acall"
 957			}.to_json,
 958			{ "CONTENT_TYPE" => "application/json" }
 959		)
 960
 961		assert last_response.ok?
 962		assert_equal(
 963			"<?xml version=\"1.0\" encoding=\"utf-8\" ?><Response>" \
 964			"<Hangup />" \
 965			"</Response>",
 966			last_response.body
 967		)
 968		assert_mock CustomerFwd::BANDWIDTH_VOICE
 969		assert_mock ReachableRedis
 970	end
 971	em :test_inbound_from_reachability_during_reachability
 972
 973	def test_bulk_order
 974		create_order = stub_request(
 975			:post,
 976			"https://dashboard.bandwidth.com/v1.0/accounts//orders"
 977		).to_return(status: 201, body: <<~RESPONSE)
 978			<OrderResponse>
 979				<Order>
 980					<id>test_order</id>
 981				</Order>
 982			</OrderResponse>
 983		RESPONSE
 984
 985		post(
 986			"/orders",
 987			{ quantity: 100 },
 988			"HTTP_ACCEPT" => "application/json",
 989			"HTTP_AUTHORIZATION" => "Bearer sometoken"
 990		)
 991
 992		assert last_response.ok?
 993		assert_equal(
 994			{ id: "test_order" }.to_json,
 995			last_response.body
 996		)
 997		assert_requested create_order
 998	end
 999	em :test_bulk_order
1000
1001	def test_bulk_order_low_balance
1002		post(
1003			"/orders",
1004			{ quantity: 100 },
1005			"HTTP_ACCEPT" => "application/json",
1006			"HTTP_AUTHORIZATION" => "Bearer lowtoken"
1007		)
1008
1009		assert_equal 402, last_response.status
1010	end
1011	em :test_bulk_order_low_balance
1012
1013	def test_bulk_order_bad_token
1014		post(
1015			"/orders",
1016			{ quantity: 100 },
1017			"HTTP_ACCEPT" => "application/json",
1018			"HTTP_AUTHORIZATION" => "Bearer badtoken"
1019		)
1020
1021		assert_equal 401, last_response.status
1022	end
1023	em :test_bulk_order_bad_token
1024
1025	def test_order_get
1026		stub_request(
1027			:get,
1028			"https://dashboard.bandwidth.com/v1.0/accounts//orders/testorder"
1029		).to_return(status: 200, body: <<~RESPONSE)
1030			<OrderResponse>
1031				<id>testorder</id>
1032				<OrderStatus>complete</OrderStatus>
1033				<CompletedNumbers>
1034					<TelephoneNumber>
1035						<FullNumber>5551234567</FullNumber>
1036					</TelephoneNumber>
1037				</CompletedNumbers>
1038			</OrderResponse>
1039		RESPONSE
1040
1041		Transaction::DB.expect(:transaction, nil) do |&blk|
1042			blk.call
1043			true
1044		end
1045		Transaction::DB.expect(
1046			:exec,
1047			nil,
1048			[String, Matching.new { |params|
1049				assert_equal "bulkcustomer", params[0]
1050				assert_equal "testorder", params[1]
1051				assert_equal(-1.75, params[4])
1052				assert_equal "Bulk order", params[5]
1053			}]
1054		)
1055
1056		get(
1057			"/orders/testorder",
1058			"",
1059			"HTTP_ACCEPT" => "application/json",
1060			"HTTP_AUTHORIZATION" => "Bearer sometoken"
1061		)
1062
1063		assert last_response.ok?
1064		assert_equal(
1065			{ id: "testorder", status: "complete", tels: ["+15551234567"] }.to_json,
1066			last_response.body
1067		)
1068		assert_mock Transaction::DB
1069	end
1070	em :test_order_get
1071
1072	def test_order_get_multi
1073		stub_request(
1074			:get,
1075			"https://dashboard.bandwidth.com/v1.0/accounts//orders/testorder"
1076		).to_return(status: 200, body: <<~RESPONSE)
1077			<OrderResponse>
1078				<id>testorder</id>
1079				<OrderStatus>complete</OrderStatus>
1080				<CompletedNumbers>
1081					<TelephoneNumber>
1082						<FullNumber>5551234567</FullNumber>
1083					</TelephoneNumber>
1084					<TelephoneNumber>
1085						<FullNumber>5551234568</FullNumber>
1086					</TelephoneNumber>
1087				</CompletedNumbers>
1088			</OrderResponse>
1089		RESPONSE
1090
1091		Transaction::DB.expect(:transaction, nil) do |&blk|
1092			blk.call
1093			true
1094		end
1095		Transaction::DB.expect(
1096			:exec,
1097			nil,
1098			[String, Matching.new { |params|
1099				assert_equal "bulkcustomer", params[0]
1100				assert_equal "testorder", params[1]
1101				assert_equal (-1.75 * 2), params[4]
1102				assert_equal "Bulk order", params[5]
1103			}]
1104		)
1105
1106		get(
1107			"/orders/testorder",
1108			"",
1109			"HTTP_ACCEPT" => "application/json",
1110			"HTTP_AUTHORIZATION" => "Bearer sometoken"
1111		)
1112
1113		assert last_response.ok?
1114		assert_equal(
1115			{
1116				id: "testorder",
1117				status: "complete",
1118				tels: ["+15551234567", "+15551234568"]
1119			}.to_json,
1120			last_response.body
1121		)
1122		assert_mock Transaction::DB
1123	end
1124	em :test_order_get_multi
1125
1126	def test_order_get_received
1127		stub_request(
1128			:get,
1129			"https://dashboard.bandwidth.com/v1.0/accounts//orders/testorder"
1130		).to_return(status: 200, body: <<~RESPONSE)
1131			<OrderResponse>
1132				<id>testorder</id>
1133				<OrderStatus>received</OrderStatus>
1134			</OrderResponse>
1135		RESPONSE
1136
1137		get(
1138			"/orders/testorder",
1139			"",
1140			"HTTP_ACCEPT" => "application/json",
1141			"HTTP_AUTHORIZATION" => "Bearer sometoken"
1142		)
1143
1144		assert last_response.ok?
1145		assert_equal(
1146			{ id: "testorder", status: "received" }.to_json,
1147			last_response.body
1148		)
1149	end
1150	em :test_order_get_received
1151
1152	def test_order_get_failed
1153		stub_request(
1154			:get,
1155			"https://dashboard.bandwidth.com/v1.0/accounts//orders/testorder"
1156		).to_return(status: 200, body: <<~RESPONSE)
1157			<OrderResponse>
1158				<id>testorder</id>
1159				<OrderStatus>failed</OrderStatus>
1160			</OrderResponse>
1161		RESPONSE
1162
1163		get(
1164			"/orders/testorder",
1165			"",
1166			"HTTP_ACCEPT" => "application/json",
1167			"HTTP_AUTHORIZATION" => "Bearer sometoken"
1168		)
1169
1170		assert last_response.ok?
1171		assert_equal(
1172			{ id: "testorder", status: "failed" }.to_json,
1173			last_response.body
1174		)
1175	end
1176	em :test_order_get_failed
1177
1178	def test_order_get_bad_token
1179		get(
1180			"/orders/testorder",
1181			"",
1182			"HTTP_ACCEPT" => "application/json",
1183			"HTTP_AUTHORIZATION" => "Bearer badtoken"
1184		)
1185
1186		assert_equal 401, last_response.status
1187	end
1188	em :test_order_get_bad_token
1189
1190	def test_delete_tel
1191		stub_request(
1192			:get,
1193			"https://dashboard.bandwidth.com/v1.0/tns/+15551234567/tndetails"
1194		).to_return(status: 200, body: <<~RESPONSE)
1195			<Response>
1196				<TelephoneNumberDetails>
1197					<SipPeer><PeerId>bulkpeer</PeerId></SipPeer>
1198				</TelephoneNumberDetails>
1199			</Response>
1200		RESPONSE
1201
1202		req = stub_request(
1203			:post,
1204			"https://dashboard.bandwidth.com/v1.0/accounts//disconnects"
1205		).to_return(status: 200, body: "")
1206
1207		delete(
1208			"/orders/tels/+15551234567",
1209			"",
1210			"HTTP_ACCEPT" => "application/json",
1211			"HTTP_AUTHORIZATION" => "Bearer sometoken"
1212		)
1213
1214		assert last_response.ok?
1215		assert_requested req
1216	end
1217	em :test_delete_tel
1218
1219	def test_delete_tel_not_yours
1220		stub_request(
1221			:get,
1222			"https://dashboard.bandwidth.com/v1.0/tns/+15551234567/tndetails"
1223		).to_return(status: 200, body: <<~RESPONSE)
1224			<Response>
1225				<TelephoneNumberDetails>
1226					<SipPeer><PeerId>mainpeer</PeerId></SipPeer>
1227				</TelephoneNumberDetails>
1228			</Response>
1229		RESPONSE
1230
1231		delete(
1232			"/orders/tels/+15551234567",
1233			"",
1234			"HTTP_ACCEPT" => "application/json",
1235			"HTTP_AUTHORIZATION" => "Bearer sometoken"
1236		)
1237
1238		assert_equal 401, last_response.status
1239	end
1240	em :test_delete_tel_not_yours
1241
1242	def test_delete_tel_bad_token
1243		delete(
1244			"/orders/tels/+15551234567",
1245			"",
1246			"HTTP_ACCEPT" => "application/json",
1247			"HTTP_AUTHORIZATION" => "Bearer badtoken"
1248		)
1249
1250		assert_equal 401, last_response.status
1251	end
1252	em :test_delete_tel_bad_token
1253end