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		braintree_customer = Minitest::Mock.new
 263		CustomerFinancials::BRAINTREE.expect(:customer, braintree_customer)
 264		payment_methods = OpenStruct.new(payment_methods: [
 265			OpenStruct.new(default?: true, unique_number_identifier: "abcd")
 266		])
 267		braintree_customer.expect(
 268			:find,
 269			EMPromise.resolve(payment_methods),
 270			["customerid_topup"]
 271		)
 272
 273		Customer::BLATHER.expect(
 274			:<<,
 275			nil,
 276			[Blather::Stanza]
 277		)
 278
 279		post(
 280			"/outbound/calls",
 281			{
 282				from: "ccustomerid_topup",
 283				to: "+15557654321",
 284				callId: "acall"
 285			}.to_json,
 286			{ "CONTENT_TYPE" => "application/json" }
 287		)
 288
 289		assert last_response.ok?
 290		assert_equal(
 291			"<?xml version=\"1.0\" encoding=\"utf-8\" ?><Response>" \
 292			"<Transfer transferCallerId=\"+15551234567\">" \
 293			"<PhoneNumber>+15557654321</PhoneNumber></Transfer></Response>",
 294			last_response.body
 295		)
 296		assert_mock ExpiringLock::REDIS
 297		assert_mock Customer::BLATHER
 298		assert_mock LowBalance::AutoTopUp::CreditCardSale
 299		assert_mock CustomerPlan::DB
 300	end
 301	em :test_outbound_low_balance_top_up
 302
 303	def test_outbound_unsupported
 304		CustomerPlan::DB.expect(
 305			:query_one, {}, [String, "customerid"]
 306		)
 307
 308		post(
 309			"/outbound/calls",
 310			{
 311				from: "ccustomerid",
 312				to: "+95557654321",
 313				callId: "acall"
 314			}.to_json,
 315			{ "CONTENT_TYPE" => "application/json" }
 316		)
 317
 318		assert last_response.ok?
 319		assert_equal(
 320			"<?xml version=\"1.0\" encoding=\"utf-8\" ?><Response>" \
 321			"<SpeakSentence>The number you have dialled is not " \
 322			"supported on your account.</SpeakSentence></Response>",
 323			last_response.body
 324		)
 325		assert_mock CustomerPlan::DB
 326	end
 327	em :test_outbound_unsupported
 328
 329	def test_outbound_unsupported_short_numbers_911
 330		CustomerPlan::DB.expect(
 331			:query_one, {}, [String, "customerid"]
 332		)
 333
 334		post(
 335			"/outbound/calls",
 336			{
 337				from: "ccustomerid",
 338				to: "+1911",
 339				callId: "acall"
 340			}.to_json,
 341			{ "CONTENT_TYPE" => "application/json" }
 342		)
 343
 344		assert last_response.ok?
 345		assert_equal(
 346			"<?xml version=\"1.0\" encoding=\"utf-8\" ?><Response>" \
 347			"<SpeakSentence>The number you have dialled is not " \
 348			"supported on your account.</SpeakSentence></Response>",
 349			last_response.body
 350		)
 351		assert_mock CustomerPlan::DB
 352	end
 353	em :test_outbound_unsupported_short_numbers_911
 354
 355	def test_outbound_supported_9116
 356		CustomerPlan::DB.expect(
 357			:query_one, {}, [String, "customerid"]
 358		)
 359
 360		post(
 361			"/outbound/calls",
 362			{
 363				from: "ccustomerid",
 364				to: "+19116",
 365				callId: "acall"
 366			}.to_json,
 367			{ "CONTENT_TYPE" => "application/json" }
 368		)
 369
 370		assert last_response.ok?
 371		assert_equal(
 372			"<?xml version=\"1.0\" encoding=\"utf-8\" ?><Response>" \
 373			"<Transfer transferCallerId=\"+15551234567\">" \
 374			"<PhoneNumber>+19116</PhoneNumber></Transfer></Response>",
 375			last_response.body
 376		)
 377		assert_mock CustomerPlan::DB
 378	end
 379	em :test_outbound_supported_9116
 380
 381	def test_outbound_atlimit
 382		CustomerPlan::DB.expect(
 383			:query_one, {}, [String, "customerid_limit"]
 384		)
 385
 386		post(
 387			"/outbound/calls",
 388			{
 389				from: "ccustomerid_limit",
 390				to: "+15557654321",
 391				callId: "acall"
 392			}.to_json,
 393			{ "CONTENT_TYPE" => "application/json" }
 394		)
 395
 396		assert last_response.ok?
 397		assert_equal(
 398			"<?xml version=\"1.0\" encoding=\"utf-8\" ?><Response>" \
 399			"<Gather gatherUrl=\"\/outbound/calls\" maxDigits=\"1\" " \
 400			"repeatCount=\"3\"><SpeakSentence>This call will take you over " \
 401			"your configured monthly overage limit.</SpeakSentence><SpeakSentence>" \
 402			"Change your limit in your account settings or press 1 to accept the " \
 403			"charges. You can hang up to cancel.</SpeakSentence></Gather></Response>",
 404			last_response.body
 405		)
 406		assert_mock CustomerPlan::DB
 407	end
 408	em :test_outbound_atlimit
 409
 410	def test_outbound_no_customer
 411		post(
 412			"/outbound/calls",
 413			{
 414				from: "no_such_customer",
 415				to: "+15557654321",
 416				callId: "acall"
 417			}.to_json,
 418			{ "CONTENT_TYPE" => "application/json" }
 419		)
 420
 421		assert last_response.ok?
 422		assert_equal(
 423			"<?xml version=\"1.0\" encoding=\"utf-8\" ?><Response>" \
 424			"<SpeakSentence>Your credentials are invalid, please contact support." \
 425			"</SpeakSentence></Response>",
 426			last_response.body
 427		)
 428	end
 429	em :test_outbound_no_customer
 430
 431	def test_outbound_atlimit_digits
 432		CustomerPlan::DB.expect(
 433			:query_one, {}, [String, "customerid_limit"]
 434		)
 435
 436		post(
 437			"/outbound/calls",
 438			{
 439				from: "ccustomerid_limit",
 440				to: "+15557654321",
 441				callId: "acall",
 442				digits: "1"
 443			}.to_json,
 444			{ "CONTENT_TYPE" => "application/json" }
 445		)
 446
 447		assert last_response.ok?
 448		assert_equal(
 449			"<?xml version=\"1.0\" encoding=\"utf-8\" ?><Response>" \
 450			"<Transfer transferCallerId=\"+15551234567\">" \
 451			"<PhoneNumber>+15557654321</PhoneNumber></Transfer></Response>",
 452			last_response.body
 453		)
 454		assert_mock CustomerPlan::DB
 455	end
 456	em :test_outbound_atlimit_digits
 457
 458	def test_outbound_toll_free
 459		CustomerPlan::DB.expect(
 460			:query_one, {}, [String, "customerid"]
 461		)
 462
 463		post(
 464			"/outbound/calls",
 465			{
 466				from: "ccustomerid",
 467				to: "+18001234567",
 468				callId: "acall"
 469			}.to_json,
 470			{ "CONTENT_TYPE" => "application/json" }
 471		)
 472
 473		assert last_response.ok?
 474		assert_equal(
 475			"<?xml version=\"1.0\" encoding=\"utf-8\" ?><Response>" \
 476			"<Transfer transferCallerId=\"+15551234567\">" \
 477			"<PhoneNumber>+18001234567</PhoneNumber></Transfer></Response>",
 478			last_response.body
 479		)
 480		assert_mock CustomerPlan::DB
 481	end
 482	em :test_outbound_toll_free
 483
 484	def test_outbound_disconnect
 485		CustomerPlan::DB.expect(
 486			:query_one, {}, [String, "customerid"]
 487		)
 488
 489		post(
 490			"/outbound/calls/status",
 491			{
 492				eventType: "disconnect",
 493				from: "ccustomerid",
 494				to: "+15557654321",
 495				callId: "acall",
 496				startTime: Time.now.to_s,
 497				endTime: Time.now.to_s,
 498				cause: "hangup"
 499			}.to_json,
 500			{ "CONTENT_TYPE" => "application/json" }
 501		)
 502
 503		assert last_response.ok?
 504		assert_equal("OK", last_response.body)
 505		assert_mock CustomerPlan::DB
 506	end
 507	em :test_outbound_disconnect
 508
 509	def test_outbound_disconnect_tombed
 510		CustomerPlan::DB.expect(
 511			:query_one, {}, [String, "customerid_tombed"]
 512		)
 513
 514		@cdr_repo.stub(:put, ->(*) { raise "put called" }) do
 515			post(
 516				"/outbound/calls/status",
 517				{
 518					eventType: "disconnect",
 519					from: "ccustomerid_tombed",
 520					to: "+15557654321",
 521					callId: "acall",
 522					startTime: Time.now.to_s,
 523					endTime: Time.now.to_s,
 524					cause: "hangup"
 525				}.to_json,
 526				{ "CONTENT_TYPE" => "application/json" }
 527			)
 528		end
 529
 530		assert last_response.ok?
 531		assert_equal("OK", last_response.body)
 532		assert_mock CustomerPlan::DB
 533	end
 534	em :test_outbound_disconnect_tombed
 535
 536	def test_inbound
 537		CustomerPlan::DB.expect(
 538			:query_one, {}, [String, "customerid"]
 539		)
 540		CustomerFwd::BANDWIDTH_VOICE.expect(
 541			:create_call,
 542			OpenStruct.new(data: OpenStruct.new(call_id: "ocall")),
 543			["test_bw_account"],
 544			body: Matching.new do |arg|
 545				assert_equal(
 546					"http://example.org/inbound/calls/acall?customer_id=customerid",
 547					arg.answer_url
 548				)
 549			end
 550		)
 551
 552		post(
 553			"/inbound/calls",
 554			{
 555				from: "+15557654321",
 556				to: "+15551234567",
 557				callId: "acall"
 558			}.to_json,
 559			{ "CONTENT_TYPE" => "application/json" }
 560		)
 561
 562		assert last_response.ok?
 563		assert_equal(
 564			"<?xml version=\"1.0\" encoding=\"utf-8\" ?><Response>" \
 565			"<Ring answerCall=\"false\" duration=\"300\" />" \
 566			"</Response>",
 567			last_response.body
 568		)
 569		assert_mock CustomerFwd::BANDWIDTH_VOICE
 570		assert_mock CustomerPlan::DB
 571	end
 572	em :test_inbound
 573
 574	def test_inbound_from_reachability
 575		CustomerPlan::DB.expect(
 576			:query_one, {}, [String, "customerid"]
 577		)
 578		CustomerFwd::BANDWIDTH_VOICE.expect(
 579			:create_call,
 580			OpenStruct.new(data: OpenStruct.new(call_id: "ocall")),
 581			["test_bw_account"],
 582			body: Matching.new do |arg|
 583				assert_equal(
 584					"http://example.org/inbound/calls/acall?customer_id=customerid",
 585					arg.answer_url
 586				)
 587			end
 588		)
 589
 590		ReachableRedis.expect(
 591			:exists,
 592			EMPromise.resolve(0),
 593			["jmp_customer_reachability_voice-customerid"]
 594		)
 595
 596		post(
 597			"/inbound/calls",
 598			{
 599				from: "+14445556666",
 600				to: "+15551234567",
 601				callId: "acall"
 602			}.to_json,
 603			{ "CONTENT_TYPE" => "application/json" }
 604		)
 605
 606		assert last_response.ok?
 607		assert_equal(
 608			"<?xml version=\"1.0\" encoding=\"utf-8\" ?><Response>" \
 609			"<Ring answerCall=\"false\" duration=\"300\" />" \
 610			"</Response>",
 611			last_response.body
 612		)
 613		assert_mock CustomerFwd::BANDWIDTH_VOICE
 614		assert_mock ReachableRedis
 615		assert_mock CustomerPlan::DB
 616	end
 617	em :test_inbound_from_reachability
 618
 619	def test_inbound_no_bwmsgsv2
 620		CustomerPlan::DB.expect(
 621			:query_one, {}, [String, "customerid2"]
 622		)
 623		CustomerFwd::BANDWIDTH_VOICE.expect(
 624			:create_call,
 625			OpenStruct.new(data: OpenStruct.new(call_id: "ocall")),
 626			["test_bw_account"],
 627			body: Matching.new do |arg|
 628				assert_equal(
 629					"http://example.org/inbound/calls/acall?customer_id=customerid2",
 630					arg.answer_url
 631				)
 632			end
 633		)
 634
 635		post(
 636			"/inbound/calls",
 637			{
 638				from: "+15557654321",
 639				to: "+15551230000",
 640				callId: "acall"
 641			}.to_json,
 642			{ "CONTENT_TYPE" => "application/json" }
 643		)
 644
 645		assert last_response.ok?
 646		assert_equal(
 647			"<?xml version=\"1.0\" encoding=\"utf-8\" ?><Response>" \
 648			"<Ring answerCall=\"false\" duration=\"300\" />" \
 649			"</Response>",
 650			last_response.body
 651		)
 652		assert_mock CustomerFwd::BANDWIDTH_VOICE
 653		assert_mock CustomerPlan::DB
 654	end
 655	em :test_inbound_no_bwmsgsv2
 656
 657	def test_inbound_low
 658		CustomerPlan::DB.expect(
 659			:query_one, {}, [String, "customerid_low"]
 660		)
 661
 662		ExpiringLock::REDIS.expect(
 663			:set,
 664			EMPromise.resolve(nil),
 665			["jmp_customer_low_balance-customerid_low", Time, "EX", 604800, "NX"]
 666		)
 667
 668		post(
 669			"/inbound/calls",
 670			{
 671				from: "+15557654321",
 672				to: "+15551234560",
 673				callId: "acall"
 674			}.to_json,
 675			{ "CONTENT_TYPE" => "application/json" }
 676		)
 677
 678		assert last_response.ok?
 679		assert_equal(
 680			"<?xml version=\"1.0\" encoding=\"utf-8\" ?><Response>" \
 681			"<Redirect redirectUrl=\"/inbound/calls/acall/voicemail\" />" \
 682			"</Response>",
 683			last_response.body
 684		)
 685		assert_mock CustomerFwd::BANDWIDTH_VOICE
 686		assert_mock ExpiringLock::REDIS
 687		assert_mock CustomerPlan::DB
 688	end
 689	em :test_inbound_low
 690
 691	def test_inbound_leg2
 692		CustomerPlan::DB.expect(
 693			:query_one, {}, [String, "customerid"]
 694		)
 695
 696		post(
 697			"/inbound/calls/acall?customer_id=customerid",
 698			{
 699				from: "+15557654321",
 700				to: "sip:boop@example.com",
 701				callId: "ocall"
 702			}.to_json,
 703			{ "CONTENT_TYPE" => "application/json" }
 704		)
 705
 706		assert last_response.ok?
 707		assert_equal(
 708			"<?xml version=\"1.0\" encoding=\"utf-8\" ?><Response>" \
 709			"<Tag>connected</Tag><Bridge>acall</Bridge>" \
 710			"</Response>",
 711			last_response.body
 712		)
 713		assert_mock CustomerPlan::DB
 714	end
 715	em :test_inbound_leg2
 716
 717	def test_inbound_limit_leg2
 718		CustomerPlan::DB.expect(
 719			:query_one, {}, [String, "customerid_limit"]
 720		)
 721
 722		path = "/inbound/calls/acall?customer_id=customerid_limit"
 723
 724		post(
 725			path,
 726			{
 727				from: "+15557654321",
 728				to: "sip:boop@example.com",
 729				callId: "ocall"
 730			}.to_json,
 731			{ "CONTENT_TYPE" => "application/json" }
 732		)
 733
 734		assert last_response.ok?
 735		assert_equal(
 736			"<?xml version=\"1.0\" encoding=\"utf-8\" ?><Response>" \
 737			"<Gather gatherUrl=\"#{path}\" maxDigits=\"1\" " \
 738			"repeatCount=\"3\"><SpeakSentence>This call will take you over " \
 739			"your configured monthly overage limit.</SpeakSentence><SpeakSentence>" \
 740			"Change your limit in your account settings or press 1 to accept the " \
 741			"charges. You can hang up to send the caller to voicemail." \
 742			"</SpeakSentence></Gather></Response>",
 743			last_response.body
 744		)
 745		assert_mock CustomerPlan::DB
 746	end
 747	em :test_inbound_limit_leg2
 748
 749	def test_inbound_limit_digits_leg2
 750		CustomerPlan::DB.expect(
 751			:query_one, {}, [String, "customerid_limit"]
 752		)
 753
 754		post(
 755			"/inbound/calls/acall?customer_id=customerid_limit",
 756			{
 757				from: "+15557654321",
 758				to: "sip:boop@example.com",
 759				callId: "ocall",
 760				digits: "1"
 761			}.to_json,
 762			{ "CONTENT_TYPE" => "application/json" }
 763		)
 764
 765		assert last_response.ok?
 766		assert_equal(
 767			"<?xml version=\"1.0\" encoding=\"utf-8\" ?><Response>" \
 768			"<Tag>connected</Tag><Bridge>acall</Bridge>" \
 769			"</Response>",
 770			last_response.body
 771		)
 772		assert_mock CustomerPlan::DB
 773	end
 774	em :test_inbound_limit_digits_leg2
 775
 776	def test_inbound_limit_hangup
 777		Web::BANDWIDTH_VOICE.expect(
 778			:modify_call,
 779			nil,
 780			[
 781				"test_bw_account",
 782				"bcall"
 783			],
 784			body: Matching.new do |arg|
 785				assert_equal(
 786					"http://example.org/inbound/calls/oocall/voicemail",
 787					arg.redirect_url
 788				)
 789			end
 790		)
 791
 792		post(
 793			"/inbound/calls/bcall/transfer_complete",
 794			{
 795				from: "+15557654321",
 796				to: "+15551234561",
 797				callId: "oocall",
 798				cause: "hangup"
 799			}.to_json,
 800			{ "CONTENT_TYPE" => "application/json" }
 801		)
 802
 803		assert last_response.ok?
 804		assert_mock Web::BANDWIDTH_VOICE
 805	end
 806	em :test_inbound_limit_hangup
 807
 808	def test_voicemail
 809		language_id = stub_request(:post, "https://api.rev.ai/languageid/v1/jobs")
 810			.with(body: {
 811				metadata: {
 812					media_url: "https://jmp.chat/media",
 813					from_jid: "+15557654321@component",
 814					customer_id: "customerid"
 815				}.to_json,
 816				source_config: {
 817					url: "https://jmp.chat/media"
 818				},
 819				notification_config: {
 820					url: "http://example.org/inbound/calls/CALLID/voicemail/language_id"
 821				}
 822			}.to_json)
 823
 824		Customer::BLATHER.expect(
 825			:<<,
 826			nil,
 827			[Matching.new do |stanza|
 828				assert_equal "+15557654321@component", stanza.from.to_s
 829				assert_equal "customer@example.com", stanza.to.to_s
 830				assert_equal "https://jmp.chat/media", OOB.find_or_create(stanza).url
 831			end]
 832		)
 833
 834		post(
 835			"/inbound/calls/CALLID/voicemail/audio",
 836			{
 837				"startTime" => "2021-01-01T00:00:00Z",
 838				"endTime" => "2021-01-01T00:00:06Z",
 839				"mediaUrl" => "https://voice.bandwidth.com/api/v2/accounts/1/media",
 840				"to" => "+15551234567",
 841				"from" => "+15557654321"
 842			}.to_json,
 843			{ "CONTENT_TYPE" => "application/json" }
 844		)
 845
 846		assert last_response.ok?
 847		assert_mock Customer::BLATHER
 848		assert_requested language_id
 849	end
 850	em :test_voicemail
 851
 852	def test_anonymous_voicemail
 853		language_id = stub_request(:post, "https://api.rev.ai/languageid/v1/jobs")
 854			.with(body: {
 855				metadata: {
 856					media_url: "https://jmp.chat/media",
 857					from_jid:
 858						"16;phone-context=anonymous.phone-context.soprani.ca@component",
 859					customer_id: "customerid"
 860				}.to_json,
 861				source_config: {
 862					url: "https://jmp.chat/media"
 863				},
 864				notification_config: {
 865					url: "http://example.org/inbound/calls/CALLID/voicemail/language_id"
 866				}
 867			}.to_json)
 868
 869		Customer::BLATHER.expect(
 870			:<<,
 871			nil,
 872			[Matching.new do |stanza|
 873				assert_equal(
 874					"16;phone-context=anonymous.phone-context.soprani.ca@component",
 875					stanza.from.to_s
 876				)
 877				assert_equal "customer@example.com", stanza.to.to_s
 878				assert_equal "https://jmp.chat/media", OOB.find_or_create(stanza).url
 879			end]
 880		)
 881
 882		post(
 883			"/inbound/calls/CALLID/voicemail/audio",
 884			{
 885				"startTime" => "2021-01-01T00:00:00Z",
 886				"endTime" => "2021-01-01T00:00:06Z",
 887				"mediaUrl" => "https://voice.bandwidth.com/api/v2/accounts/1/media",
 888				"to" => "+15551234567",
 889				"from" => "Anonymous"
 890			}.to_json,
 891			{ "CONTENT_TYPE" => "application/json" }
 892		)
 893
 894		assert last_response.ok?
 895		assert_mock Customer::BLATHER
 896		assert_requested language_id
 897	end
 898	em :test_anonymous_voicemail
 899
 900	def test_voicemail_short
 901		post(
 902			"/inbound/calls/CALLID/voicemail/audio",
 903			{
 904				"startTime" => "2021-01-01T00:00:00Z",
 905				"endTime" => "2021-01-01T00:00:05Z"
 906			}.to_json,
 907			{ "CONTENT_TYPE" => "application/json" }
 908		)
 909
 910		assert last_response.ok?
 911		assert_mock Customer::BLATHER
 912	end
 913	em :test_voicemail_short
 914
 915	def test_voicemail_no_customer
 916		post(
 917			"/inbound/calls/CALLID/voicemail",
 918			{
 919				"startTime" => "2021-01-01T00:00:00Z",
 920				"endTime" => "2021-01-01T00:00:06Z",
 921				"mediaUrl" => "https://voice.bandwidth.com/api/v2/accounts/1/media",
 922				"to" => "+15551200000",
 923				"from" => "+15557654321"
 924			}.to_json,
 925			{ "CONTENT_TYPE" => "application/json" }
 926		)
 927
 928		assert last_response.ok?
 929		assert_equal(
 930			"<?xml version=\"1.0\" encoding=\"utf-8\" ?><Response>" \
 931			"<SpeakSentence>The number you have dialled is not in service." \
 932			"</SpeakSentence></Response>",
 933			last_response.body
 934		)
 935	end
 936	em :test_voicemail_no_customer
 937
 938	def test_inbound_from_reachability_during_reachability
 939		ReachableRedis.expect(
 940			:exists,
 941			EMPromise.resolve(1),
 942			["jmp_customer_reachability_voice-customerid_reach"]
 943		)
 944		ReachableRedis.expect(
 945			:incr,
 946			EMPromise.resolve(1),
 947			["jmp_customer_reachability_voice-customerid_reach"]
 948		)
 949
 950		post(
 951			"/inbound/calls",
 952			{
 953				from: "+14445556666",
 954				to: "+15551234563",
 955				callId: "acall"
 956			}.to_json,
 957			{ "CONTENT_TYPE" => "application/json" }
 958		)
 959
 960		assert last_response.ok?
 961		assert_equal(
 962			"<?xml version=\"1.0\" encoding=\"utf-8\" ?><Response>" \
 963			"<Hangup />" \
 964			"</Response>",
 965			last_response.body
 966		)
 967		assert_mock CustomerFwd::BANDWIDTH_VOICE
 968		assert_mock ReachableRedis
 969	end
 970	em :test_inbound_from_reachability_during_reachability
 971
 972	def test_bulk_order
 973		create_order = stub_request(
 974			:post,
 975			"https://dashboard.bandwidth.com/v1.0/accounts//orders"
 976		).to_return(status: 201, body: <<~RESPONSE)
 977			<OrderResponse>
 978				<Order>
 979					<id>test_order</id>
 980				</Order>
 981			</OrderResponse>
 982		RESPONSE
 983
 984		post(
 985			"/orders",
 986			{ quantity: 100 },
 987			"HTTP_ACCEPT" => "application/json",
 988			"HTTP_AUTHORIZATION" => "Bearer sometoken"
 989		)
 990
 991		assert last_response.ok?
 992		assert_equal(
 993			{ id: "test_order" }.to_json,
 994			last_response.body
 995		)
 996		assert_requested create_order
 997	end
 998	em :test_bulk_order
 999
1000	def test_bulk_order_low_balance
1001		post(
1002			"/orders",
1003			{ quantity: 100 },
1004			"HTTP_ACCEPT" => "application/json",
1005			"HTTP_AUTHORIZATION" => "Bearer lowtoken"
1006		)
1007
1008		assert_equal 402, last_response.status
1009	end
1010	em :test_bulk_order_low_balance
1011
1012	def test_bulk_order_bad_token
1013		post(
1014			"/orders",
1015			{ quantity: 100 },
1016			"HTTP_ACCEPT" => "application/json",
1017			"HTTP_AUTHORIZATION" => "Bearer badtoken"
1018		)
1019
1020		assert_equal 401, last_response.status
1021	end
1022	em :test_bulk_order_bad_token
1023
1024	def test_order_get
1025		stub_request(
1026			:get,
1027			"https://dashboard.bandwidth.com/v1.0/accounts//orders/testorder"
1028		).to_return(status: 200, body: <<~RESPONSE)
1029			<OrderResponse>
1030				<id>testorder</id>
1031				<OrderStatus>complete</OrderStatus>
1032				<CompletedNumbers>
1033					<TelephoneNumber>
1034						<FullNumber>5551234567</FullNumber>
1035					</TelephoneNumber>
1036				</CompletedNumbers>
1037			</OrderResponse>
1038		RESPONSE
1039
1040		Transaction::DB.expect(:transaction, nil) do |&blk|
1041			blk.call
1042			true
1043		end
1044		Transaction::DB.expect(
1045			:exec,
1046			nil,
1047			[String, Matching.new { |params|
1048				assert_equal "bulkcustomer", params[0]
1049				assert_equal "testorder", params[1]
1050				assert_equal(-1.75, params[4])
1051				assert_equal "Bulk order", params[5]
1052			}]
1053		)
1054
1055		get(
1056			"/orders/testorder",
1057			"",
1058			"HTTP_ACCEPT" => "application/json",
1059			"HTTP_AUTHORIZATION" => "Bearer sometoken"
1060		)
1061
1062		assert last_response.ok?
1063		assert_equal(
1064			{ id: "testorder", status: "complete", tels: ["+15551234567"] }.to_json,
1065			last_response.body
1066		)
1067		assert_mock Transaction::DB
1068	end
1069	em :test_order_get
1070
1071	def test_order_get_multi
1072		stub_request(
1073			:get,
1074			"https://dashboard.bandwidth.com/v1.0/accounts//orders/testorder"
1075		).to_return(status: 200, body: <<~RESPONSE)
1076			<OrderResponse>
1077				<id>testorder</id>
1078				<OrderStatus>complete</OrderStatus>
1079				<CompletedNumbers>
1080					<TelephoneNumber>
1081						<FullNumber>5551234567</FullNumber>
1082					</TelephoneNumber>
1083					<TelephoneNumber>
1084						<FullNumber>5551234568</FullNumber>
1085					</TelephoneNumber>
1086				</CompletedNumbers>
1087			</OrderResponse>
1088		RESPONSE
1089
1090		Transaction::DB.expect(:transaction, nil) do |&blk|
1091			blk.call
1092			true
1093		end
1094		Transaction::DB.expect(
1095			:exec,
1096			nil,
1097			[String, Matching.new { |params|
1098				assert_equal "bulkcustomer", params[0]
1099				assert_equal "testorder", params[1]
1100				assert_equal (-1.75 * 2), params[4]
1101				assert_equal "Bulk order", params[5]
1102			}]
1103		)
1104
1105		get(
1106			"/orders/testorder",
1107			"",
1108			"HTTP_ACCEPT" => "application/json",
1109			"HTTP_AUTHORIZATION" => "Bearer sometoken"
1110		)
1111
1112		assert last_response.ok?
1113		assert_equal(
1114			{
1115				id: "testorder",
1116				status: "complete",
1117				tels: ["+15551234567", "+15551234568"]
1118			}.to_json,
1119			last_response.body
1120		)
1121		assert_mock Transaction::DB
1122	end
1123	em :test_order_get_multi
1124
1125	def test_order_get_received
1126		stub_request(
1127			:get,
1128			"https://dashboard.bandwidth.com/v1.0/accounts//orders/testorder"
1129		).to_return(status: 200, body: <<~RESPONSE)
1130			<OrderResponse>
1131				<id>testorder</id>
1132				<OrderStatus>received</OrderStatus>
1133			</OrderResponse>
1134		RESPONSE
1135
1136		get(
1137			"/orders/testorder",
1138			"",
1139			"HTTP_ACCEPT" => "application/json",
1140			"HTTP_AUTHORIZATION" => "Bearer sometoken"
1141		)
1142
1143		assert last_response.ok?
1144		assert_equal(
1145			{ id: "testorder", status: "received" }.to_json,
1146			last_response.body
1147		)
1148	end
1149	em :test_order_get_received
1150
1151	def test_order_get_failed
1152		stub_request(
1153			:get,
1154			"https://dashboard.bandwidth.com/v1.0/accounts//orders/testorder"
1155		).to_return(status: 200, body: <<~RESPONSE)
1156			<OrderResponse>
1157				<id>testorder</id>
1158				<OrderStatus>failed</OrderStatus>
1159			</OrderResponse>
1160		RESPONSE
1161
1162		get(
1163			"/orders/testorder",
1164			"",
1165			"HTTP_ACCEPT" => "application/json",
1166			"HTTP_AUTHORIZATION" => "Bearer sometoken"
1167		)
1168
1169		assert last_response.ok?
1170		assert_equal(
1171			{ id: "testorder", status: "failed" }.to_json,
1172			last_response.body
1173		)
1174	end
1175	em :test_order_get_failed
1176
1177	def test_order_get_bad_token
1178		get(
1179			"/orders/testorder",
1180			"",
1181			"HTTP_ACCEPT" => "application/json",
1182			"HTTP_AUTHORIZATION" => "Bearer badtoken"
1183		)
1184
1185		assert_equal 401, last_response.status
1186	end
1187	em :test_order_get_bad_token
1188
1189	def test_delete_tel
1190		stub_request(
1191			:get,
1192			"https://dashboard.bandwidth.com/v1.0/tns/+15551234567/tndetails"
1193		).to_return(status: 200, body: <<~RESPONSE)
1194			<Response>
1195				<TelephoneNumberDetails>
1196					<SipPeer><PeerId>bulkpeer</PeerId></SipPeer>
1197				</TelephoneNumberDetails>
1198			</Response>
1199		RESPONSE
1200
1201		req = stub_request(
1202			:post,
1203			"https://dashboard.bandwidth.com/v1.0/accounts//disconnects"
1204		).to_return(status: 200, body: "")
1205
1206		delete(
1207			"/orders/tels/+15551234567",
1208			"",
1209			"HTTP_ACCEPT" => "application/json",
1210			"HTTP_AUTHORIZATION" => "Bearer sometoken"
1211		)
1212
1213		assert last_response.ok?
1214		assert_requested req
1215	end
1216	em :test_delete_tel
1217
1218	def test_delete_tel_not_yours
1219		stub_request(
1220			:get,
1221			"https://dashboard.bandwidth.com/v1.0/tns/+15551234567/tndetails"
1222		).to_return(status: 200, body: <<~RESPONSE)
1223			<Response>
1224				<TelephoneNumberDetails>
1225					<SipPeer><PeerId>mainpeer</PeerId></SipPeer>
1226				</TelephoneNumberDetails>
1227			</Response>
1228		RESPONSE
1229
1230		delete(
1231			"/orders/tels/+15551234567",
1232			"",
1233			"HTTP_ACCEPT" => "application/json",
1234			"HTTP_AUTHORIZATION" => "Bearer sometoken"
1235		)
1236
1237		assert_equal 401, last_response.status
1238	end
1239	em :test_delete_tel_not_yours
1240
1241	def test_delete_tel_bad_token
1242		delete(
1243			"/orders/tels/+15551234567",
1244			"",
1245			"HTTP_ACCEPT" => "application/json",
1246			"HTTP_AUTHORIZATION" => "Bearer badtoken"
1247		)
1248
1249		assert_equal 401, last_response.status
1250	end
1251	em :test_delete_tel_bad_token
1252end