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