# frozen_string_literal: true

require "test_helper"
require "customer"
require "registration"

BandwidthTnReservationRepo::REDIS = FakeRedis.new

class RegistrationTest < Minitest::Test
	def test_for_registered
		sgx = OpenStruct.new(
			registered?: OpenStruct.new(phone: "+15555550000")
		)
		iq = Blather::Stanza::Iq::Command.new
		iq.from = "test@example.com"
		result = execute_command(iq) do
			Registration.for(
				customer(sgx: sgx),
				nil,
				Minitest::Mock.new
			)
		end
		assert_kind_of Registration::Registered, result
	end
	em :test_for_registered

	def test_for_activated
		reservation_req = stub_request(
			:post,
			"https://dashboard.bandwidth.com/v1.0/accounts//tnreservation"
		).to_return(
			status: 201,
			headers: {
				location: "https://dashboard.bandwidth.com/api/accounts//TnReservation/8da0f39c-043c-4806-9f0f-497b2d197bc5"
			}
		)
		stub_request(
			:get, "https://dashboard.bandwidth.com/v1.0/accounts//tnreservation/8da0f39c-043c-4806-9f0f-497b2d197bc5"
		).to_return(status: 200, body: <<~RESPONSE)
			<ReservationResponse>
				<Reservation>
					<ReservationId>f342904f-b03a-4499-bac0-e8f43a2664a1</ReservationId>
					<AccountId>12346099</AccountId>
					<ReservationExpires>1492</ReservationExpires>
					<ReservedTn>4354776010</ReservedTn>
				</Reservation>
			</ReservationResponse>
		RESPONSE
		web_manager = TelSelections.new(
			redis: FakeRedis.new, db: FakeDB.new, memcache: FakeMemcache.new
		)
		web_manager.set(
			"test@example.net",
			TelSelections::ChooseTel::Tn.for_pending_value("+15555550000")
		)
		result = execute_command do
			sgx = OpenStruct.new(registered?: false)
			Registration.for(
				customer(
					plan_name: "test_usd",
					expires_at: Time.now + 999,
					sgx: sgx
				),
				nil,
				web_manager
			)
		end
		assert_kind_of Registration::Finish, result
		assert_requested reservation_req
	end
	em :test_for_activated

	def test_for_not_activated_approved
		sgx = OpenStruct.new(registered?: false)
		web_manager = TelSelections.new(
			redis: FakeRedis.new, db: FakeDB.new, memcache: FakeMemcache.new
		)
		web_manager.set(
			"test\\40approved.example.com@component",
			TelSelections::ChooseTel::Tn.for_pending_value("+15555550000")
		)
		iq = Blather::Stanza::Iq::Command.new
		iq.from = "test@approved.example.com"
		result = execute_command(iq) do
			Registration::Activation.for(
				customer(
					sgx: sgx,
					jid: Blather::JID.new("test\\40approved.example.com@component")
				),
				nil,
				web_manager
			)
		end
		assert_kind_of Registration::Activation::Allow, result
	end
	em :test_for_not_activated_approved

	def test_for_not_activated_googleplay
		sgx = OpenStruct.new(registered?: false)
		web_manager = TelSelections.new(
			redis: FakeRedis.new, db: FakeDB.new, memcache: FakeMemcache.new
		)
		web_manager.set(
			"test@example.net",
			TelSelections::ChooseTel::Tn.for_pending_value("+15555550000")
		)
		iq = Blather::Stanza::Iq::Command.new
		iq.from = "test@approved.example.com"
		result = execute_command(iq) do
			Registration::Activation.for(
				customer(sgx: sgx),
				"GARBLEDYGOOK==",
				web_manager
			)
		end
		assert_kind_of Registration::Activation::GooglePlay, result
	end
	em :test_for_not_activated_googleplay

	def test_for_not_activated_with_customer_id
		sgx = OpenStruct.new(registered?: false)
		web_manager = TelSelections.new(
			redis: FakeRedis.new, db: FakeDB.new, memcache: FakeMemcache.new
		)
		web_manager.set(
			"test@example.net",
			TelSelections::ChooseTel::Tn.for_pending_value("+15555550000")
		)
		iq = Blather::Stanza::Iq::Command.new
		iq.from = "test@example.com"
		result = execute_command(iq) do
			Registration::Activation.for(
				customer(sgx: sgx),
				nil,
				web_manager
			)
		end
		assert_kind_of Registration::Activation, result
	end
	em :test_for_not_activated_with_customer_id

	class ActivationTest < Minitest::Test
		Registration::Activation::DB = Minitest::Mock.new
		Registration::Activation::REDIS = FakeRedis.new(
			"jmp_parent_codes" => {
				"PARENT_CODE" => "1",
				"PARENT_TOMB" => "2",
				"PARENT_MANY_SUBACCOUNTS" => "many_subaccounts"
			},
			"jmp_customer_trust_level-2" => "Tomb"
		)
		Registration::Activation::Payment = Minitest::Mock.new
		Registration::Activation::Finish = Minitest::Mock.new
		Command::COMMAND_MANAGER = Minitest::Mock.new

		def setup
			@customer = Minitest::Mock.new(customer)
			@tel = TelSelections::ChooseTel::Tn.for_pending_value("+15555550000")
			@activation = Registration::Activation.new(@customer, @tel)
		end

		def test_write
			Command::COMMAND_MANAGER.expect(
				:write,
				EMPromise.resolve(Blather::Stanza::Iq::Command.new.tap { |iq|
					iq.form.fields = [{ var: "plan_name", value: "test_usd" }]
				}),
				[Matching.new do |iq|
					assert_equal :form, iq.form.type
					assert_equal(
						"You've selected (555) 555-0000 as your JMP number.",
						iq.form.instructions.lines.first.chomp
					)
				end]
			)
			@customer.expect(:with_plan, @customer) do |*args, **|
				assert_equal ["test_usd"], args
			end
			@customer.expect(:save_plan!, EMPromise.resolve(nil), [])
			Registration::Activation::Payment.expect(
				:for,
				EMPromise.reject(:test_result),
				[Blather::Stanza::Iq, @customer, @tel]
			)
			assert_equal(
				:test_result,
				execute_command { @activation.write.catch { |e| e } }
			)
			assert_mock Command::COMMAND_MANAGER
			assert_mock @customer
			assert_mock Registration::Activation::Payment
		end
		em :test_write

		def test_write_with_onboarding_jid
			Command::COMMAND_MANAGER.expect(
				:write,
				EMPromise.resolve(Blather::Stanza::Iq::Command.new.tap { |iq|
					iq.form.fields = [{ var: "plan_name", value: "test_usd" }]
				}),
				[Matching.new do |iq|
					assert_equal :form, iq.form.type
					assert_equal(
						"You've selected (555) 555-0000 as your JMP number.",
						iq.form.instructions.lines.first.chomp
					)
				end]
			)
			@customer.expect(
				:jid,
				Blather::JID.new("test\\40onboarding.example.com@proxy")
			)
			@customer.expect(:with_plan, @customer) do |*args, **|
				assert_equal ["test_usd"], args
			end
			@customer.expect(:save_plan!, EMPromise.resolve(nil), [])
			Registration::Activation::Payment.expect(
				:for,
				EMPromise.reject(:test_result),
				[Blather::Stanza::Iq, @customer, @tel]
			)
			assert_equal(
				:test_result,
				execute_command { @activation.write.catch { |e| e } }
			)
			assert_mock Command::COMMAND_MANAGER
			assert_mock @customer
			assert_mock Registration::Activation::Payment
		end
		em :test_write_with_onboarding_jid

		def test_write_bad_plan
			Command::COMMAND_MANAGER.expect(
				:write,
				EMPromise.resolve(Blather::Stanza::Iq::Command.new.tap { |iq|
					iq.form.fields = [{ var: "plan_name", value: "test_usd_no_register" }]
				}),
				[Matching.new do |iq|
					assert_equal :form, iq.form.type
					assert_equal(
						"You've selected (555) 555-0000 as your JMP number.",
						iq.form.instructions.lines.first.chomp
					)
				end]
			)
			assert_equal(
				"No registration plan by that name",
				execute_command { @activation.write.catch { |e| e } }.message
			)
			assert_mock Command::COMMAND_MANAGER
			assert_mock @customer
		end
		em :test_write_bad_plan

		def test_write_with_code
			Command::COMMAND_MANAGER.expect(
				:write,
				EMPromise.resolve(Blather::Stanza::Iq::Command.new.tap { |iq|
					iq.form.fields = [
						{ var: "plan_name", value: "test_usd" },
						{ var: "code", value: "123" }
					]
				}),
				[Matching.new do |iq|
					assert_equal :form, iq.form.type
					assert_equal(
						"You've selected (555) 555-0000 as your JMP number.",
						iq.form.instructions.lines.first.chomp
					)
				end]
			)
			@customer.expect(:with_plan, @customer) do |*args, **|
				assert_equal ["test_usd"], args
			end
			@customer.expect(:save_plan!, EMPromise.resolve(nil), [])
			@customer.expect(:activate_plan_starting_now, EMPromise.resolve(nil), [])
			Registration::Activation::DB.expect(:transaction, []) { |&blk| blk.call }
			Registration::Activation::DB.expect(
				:exec,
				OpenStruct.new(cmd_tuples: 1),
				[String, ["test", "123"]]
			)
			Registration::Activation::Finish.expect(
				:new,
				OpenStruct.new(write: EMPromise.reject(:test_result)),
				[@customer, @tel]
			)
			assert_equal(
				:test_result,
				execute_command { @activation.write.catch { |e| e } }
			)
			assert_mock Command::COMMAND_MANAGER
			assert_mock @customer
			assert_mock Registration::Activation::Payment
			assert_mock Registration::Activation::DB
		end
		em :test_write_with_code

		def test_write_with_group_code
			Command::COMMAND_MANAGER.expect(
				:write,
				EMPromise.resolve(Blather::Stanza::Iq::Command.new.tap { |iq|
					iq.form.fields = [
						{ var: "plan_name", value: "test_usd" },
						{ var: "code", value: "123" }
					]
				}),
				[Matching.new do |iq|
					assert_equal :form, iq.form.type
					assert_equal(
						"You've selected (555) 555-0000 as your JMP number.",
						iq.form.instructions.lines.first.chomp
					)
				end]
			)
			@customer.expect(:with_plan, @customer) do |*args, **|
				assert_equal ["test_usd"], args
			end
			@customer.expect(:save_plan!, EMPromise.resolve(nil), [])
			Registration::Activation::DB.expect(:transaction, []) { |&blk| blk.call }
			Registration::Activation::DB.expect(
				:exec,
				OpenStruct.new(cmd_tuples: 0),
				[String, ["test", "123"]]
			)
			Registration::Activation::Payment.expect(
				:for,
				EMPromise.reject(:test_result),
				[Blather::Stanza::Iq, @customer, @tel]
			)
			assert_equal(
				:test_result,
				execute_command { @activation.write.catch { |e| e } }
			)
			assert_equal(
				"123",
				Registration::Activation::REDIS.get(
					"jmp_customer_pending_invite-test"
				).sync
			)
			assert_mock Command::COMMAND_MANAGER
			assert_mock @customer
			assert_mock Registration::Activation::Payment
			assert_mock Registration::Activation::DB
		end
		em :test_write_with_group_code

		def test_write_with_parent_code
			execute_command do
				Command::COMMAND_MANAGER.expect(
					:write,
					EMPromise.resolve(Blather::Stanza::Iq::Command.new.tap { |iq|
						iq.form.fields = [
							{ var: "plan_name", value: "test_usd" },
							{ var: "code", value: "PARENT_CODE" }
						]
					}),
					[Matching.new do |iq|
						assert_equal :form, iq.form.type
						assert_equal(
							"You've selected (555) 555-0000 as your JMP number.",
							iq.form.instructions.lines.first.chomp
						)
					end]
				)
				Registration::Activation::DB.expect(
					:query_one, {}, [String, "1"], default: {}
				)
				Registration::Activation::DB.expect(
					:query_one, { c: 0 }, [String, "1"], default: { c: 0 }
				)
				@customer.expect(:with_plan, @customer) do |*args, **|
					assert_equal ["test_usd"], args
				end
				@customer.expect(:with_plan, @customer) do |*, **kwargs|
					assert_equal({ parent_customer_id: "1" }, kwargs)
				end
				@customer.expect(:save_plan!, EMPromise.resolve(nil), [])
				@customer.expect(:balance, 100, [])
				Command.execution.customer_repo.expect(
					:find,
					EMPromise.resolve(@customer), ["test"]
				)
				Registration::Payment::MaybeBill::BillPlan.expect(
					:new,
					EMPromise.reject(:test_result)
				) do |*args, **|
					assert_equal @tel, args[1]
				end
				assert_equal(
					:test_result,
					@activation.write.catch { |e| e }.sync
				)
			end
			assert_mock Command::COMMAND_MANAGER
			assert_mock @customer
			assert_mock Registration::Activation::Payment
			assert_mock Registration::Activation::DB
			assert_mock Registration::Payment::MaybeBill::BillPlan
		end
		em :test_write_with_parent_code

		def test_write_with_parent_code_onboarding_jid
			Command::COMMAND_MANAGER.expect(
				:write,
				EMPromise.resolve(Blather::Stanza::Iq::Command.new.tap { |iq|
					iq.form.fields = [
						{ var: "plan_name", value: "test_usd" },
						{ var: "code", value: "PARENT_CODE" }
					]
				}),
				[Matching.new do |iq|
					assert_equal :form, iq.form.type
					assert_equal(
						"You've selected (555) 555-0000 as your JMP number.",
						iq.form.instructions.lines.first.chomp
					)
				end]
			)
			Registration::Activation::DB.expect(
				:query_one, {}, [String, "1"], default: {}
			)
			Registration::Activation::DB.expect(
				:query_one, { c: 0 }, [String, "1"], default: { c: 0 }
			)
			@customer.expect(:with_plan, @customer, ["test_usd"])
			@customer.expect(
				:jid,
				Blather::JID.new("test\\40onboarding.example.com@proxy")
			)
			iq = Blather::Stanza::Iq::Command.new
			iq.from = "test\\40onboarding.example.com@proxied"
			assert_equal(
				"Please create a new Jabber ID before creating a subaccount.",
				execute_command(iq) { @activation.write.catch(&:to_s) }
			)
			assert_mock Command::COMMAND_MANAGER
			assert_mock @customer
			assert_mock Registration::Activation::Payment
			assert_mock Registration::Activation::DB
		end
		em :test_write_with_parent_code_onboarding_jid

		def test_write_with_parent_code_tombed_parent
			Command::COMMAND_MANAGER.expect(
				:write,
				EMPromise.resolve(Blather::Stanza::Iq::Command.new.tap { |iq|
					iq.form.fields = [
						{ var: "plan_name", value: "test_usd" },
						{ var: "code", value: "PARENT_TOMB" }
					]
				}),
				[Matching.new do |iq|
					assert_equal :form, iq.form.type
					assert_equal(
						"You've selected (555) 555-0000 as your JMP number.",
						iq.form.instructions.lines.first.chomp
					)
				end]
			)
			Registration::Activation::DB.expect(
				:query_one, {}, [String, "2"], default: {}
			)
			Registration::Activation::DB.expect(
				:query_one, { c: 0 }, [String, "2"], default: { c: 0 }
			)
			assert_equal(
				"Please contact support to create a subaccount",
				execute_command { @activation.write.catch(&:to_s) }
			)
			assert_mock Command::COMMAND_MANAGER
			assert_mock @customer
			assert_mock Registration::Activation::Payment
			assert_mock Registration::Activation::DB
		end
		em :test_write_with_parent_code_tombed_parent

		def test_write_with_parent_code_too_many
			Command::COMMAND_MANAGER.expect(
				:write,
				EMPromise.resolve(Blather::Stanza::Iq::Command.new.tap { |iq|
					iq.form.fields = [
						{ var: "plan_name", value: "test_usd" },
						{ var: "code", value: "PARENT_MANY_SUBACCOUNTS" }
					]
				}),
				[Matching.new do |iq|
					assert_equal :form, iq.form.type
					assert_equal(
						"You've selected (555) 555-0000 as your JMP number.",
						iq.form.instructions.lines.first.chomp
					)
				end]
			)
			Registration::Activation::DB.expect(
				:query_one, {}, [String, "many_subaccounts"], default: {}
			)
			Registration::Activation::DB.expect(
				:query_one, { c: 2 }, [String, "many_subaccounts"], default: { c: 0 }
			)
			assert_equal(
				"Please contact support to create a subaccount",
				execute_command { @activation.write.catch(&:to_s) }
			)
			assert_mock Command::COMMAND_MANAGER
			assert_mock @customer
			assert_mock Registration::Activation::Payment
			assert_mock Registration::Activation::DB
		end
		em :test_write_with_parent_code_too_many
	end

	class AllowTest < Minitest::Test
		Command::COMMAND_MANAGER = Minitest::Mock.new
		Registration::Activation::Allow::DB = Minitest::Mock.new

		def test_write_credit_to_nil
			tel = TelSelections::ChooseTel::Tn.for_pending_value("+15555550000")
			cust = Minitest::Mock.new(customer("test"))
			allow = Registration::Activation::Allow.new(cust, tel, nil)

			Command::COMMAND_MANAGER.expect(
				:write,
				EMPromise.resolve(Blather::Stanza::Iq::Command.new.tap { |iq|
					iq.form.fields = [{ var: "plan_name", value: "test_usd" }]
				}),
				[Matching.new do |iq|
					assert_equal :form, iq.form.type
					assert_equal(
						"You've selected (555) 555-0000 as your JMP number.",
						iq.form.instructions.lines.first.chomp
					)
					assert_equal 1, iq.form.fields.length
				end]
			)
			Registration::Activation::Allow::DB.expect(
				:transaction,
				EMPromise.reject(:test_result)
			) do |&blk|
				blk.call
				true
			end
			cust.expect(:with_plan, cust, ["test_usd"])
			cust.expect(:activate_plan_starting_now, nil)
			assert_equal(
				:test_result,
				execute_command { allow.write.catch { |e| e } }
			)
			assert_mock Command::COMMAND_MANAGER
		end
		em :test_write_credit_to_nil

		def test_write_credit_to_refercust
			cust = Minitest::Mock.new(customer("test"))
			tel = TelSelections::ChooseTel::Tn.for_pending_value("+15555550000")
			allow = Registration::Activation::Allow.new(
				cust, tel, "refercust"
			)

			Command::COMMAND_MANAGER.expect(
				:write,
				EMPromise.resolve(Blather::Stanza::Iq::Command.new.tap { |iq|
					iq.form.fields = [{ var: "plan_name", value: "test_usd" }]
				}),
				[Matching.new do |iq|
					assert_equal :form, iq.form.type
					assert_equal(
						"You've selected (555) 555-0000 as your JMP number.",
						iq.form.instructions.lines.first.chomp
					)
					assert_equal 1, iq.form.fields.length
				end]
			)
			Registration::Activation::Allow::DB.expect(
				:transaction,
				EMPromise.reject(:test_result)
			) do |&blk|
				blk.call
				true
			end
			Registration::Activation::Allow::DB.expect(
				:exec,
				nil,
				[String, ["refercust", "test"]]
			)
			cust.expect(:with_plan, cust, ["test_usd"])
			cust.expect(:activate_plan_starting_now, nil)
			assert_equal(
				:test_result,
				execute_command { allow.write.catch { |e| e } }
			)
			assert_mock Command::COMMAND_MANAGER
		end
		em :test_write_credit_to_refercust
	end

	class GooglePlayTest < Minitest::Test
		CustomerPlan::DB = Minitest::Mock.new
		Registration::Activation::GooglePlay::Finish = Minitest::Mock.new

		def setup
			@customer = customer
			@tel = TelSelections::ChooseTel::Tn.for_pending_value("+15555550000")
			@google_play = Registration::Activation::GooglePlay.new(
				@customer,
				"abcd",
				@tel
			)
		end

		def test_write
			Command::COMMAND_MANAGER.expect(
				:write,
				EMPromise.resolve(Blather::Stanza::Iq::Command.new.tap { |iq|
					iq.form.fields = [{ var: "plan_name", value: "test_usd" }]
				}),
				[Matching.new do |iq|
					assert_equal :form, iq.form.type
					assert_equal(
						"You've selected (555) 555-0000 as your JMP number.",
						iq.form.instructions.lines.first.chomp
					)
				end]
			)
			CustomerPlan::DB.expect(
				:exec,
				OpenStruct.new(cmd_tuples: 1),
				[String, ["test", "test_usd", nil]]
			)
			CustomerPlan::DB.expect(
				:exec,
				OpenStruct.new(cmd_tuples: 0),
				[String, ["test"]]
			)
			Transaction::DB.expect(:transaction, true, [])
			Registration::Activation::GooglePlay::Finish.expect(
				:new,
				OpenStruct.new(write: EMPromise.reject(:test_result)),
				[Customer, @tel]
			)
			result = execute_command { @google_play.write.catch { |e| e } }
			assert_equal :test_result, result
			assert_mock Command::COMMAND_MANAGER
			assert_mock CustomerPlan::DB
			assert_mock Transaction::DB
			assert_mock Registration::Activation::GooglePlay::Finish
		end
		em :test_write
	end

	class PaymentTest < Minitest::Test
		CustomerFinancials::BRAINTREE = Minitest::Mock.new

		def test_for_bitcoin
			tel = TelSelections::ChooseTel::Tn.for_pending_value("+15555550000")
			iq = Blather::Stanza::Iq::Command.new
			iq.form.fields = [
				{ var: "activation_method", value: "bitcoin" },
				{ var: "plan_name", value: "test_usd" }
			]
			result = Registration::Payment.for(iq, customer, tel)
			assert_kind_of Registration::Payment::Bitcoin, result
		end

		def test_for_bch
			tel = TelSelections::ChooseTel::Tn.for_pending_value("+15555550000")
			iq = Blather::Stanza::Iq::Command.new
			iq.form.fields = [
				{ var: "activation_method", value: "bch" },
				{ var: "plan_name", value: "test_usd" }
			]
			result = Registration::Payment.for(iq, customer, tel)
			assert_kind_of Registration::Payment::BCH, result
		end

		def test_for_credit_card
			iq = Blather::Stanza::Iq::Command.new
			iq.from = "test@example.com"
			iq.form.fields = [
				{ var: "activation_method", value: "credit_card" },
				{ var: "plan_name", value: "test_usd" }
			]
			cust = customer
			result = execute_command do
				Command.execution.customer_repo.expect(:find, cust, ["test"])
				Registration::Payment.for(
					iq,
					cust,
					""
				).sync
			end
			assert_kind_of Registration::Payment::CreditCard, result
		end
		em :test_for_credit_card

		def test_for_code
			iq = Blather::Stanza::Iq::Command.new
			iq.form.fields = [
				{ var: "activation_method", value: "code" },
				{ var: "plan_name", value: "test_usd" }
			]
			cust = customer
			result = execute_command do
				Command.execution.customer_repo.expect(:find, cust, ["test"])
				Registration::Payment.for(
					iq,
					cust,
					TelSelections::ChooseTel::Tn.for_pending_value("+15555550000")
				)
			end
			assert_kind_of Registration::Payment::InviteCode, result
		end
		em :test_for_code

		class BitcoinTest < Minitest::Test
			Registration::Payment::Bitcoin::BTC_SELL_PRICES = Minitest::Mock.new
			CustomerFinancials::REDIS = Minitest::Mock.new

			def setup
				@customer = Minitest::Mock.new(
					customer(plan_name: "test_usd")
				)
				@customer.expect(
					:add_btc_address,
					EMPromise.resolve("testaddr")
				)
				@tel = TelSelections::ChooseTel::Tn.for_pending_value("+15555550000")
				@bitcoin = Registration::Payment::Bitcoin.new(
					@customer,
					@tel
				)
			end

			def test_write
				CustomerFinancials::REDIS.expect(
					:smembers,
					EMPromise.resolve([]),
					["jmp_customer_btc_addresses-test"]
				)
				blather = Minitest::Mock.new
				Command::COMMAND_MANAGER.expect(
					:write,
					EMPromise.reject(SessionManager::Timeout.new),
					[Matching.new do |reply|
						assert_equal :canceled, reply.status
						assert_equal "1.000000", reply.form.field("amount").value
						assert_equal "testaddr", reply.form.field("btc_addresses").value
						true
					end]
				)
				Registration::Payment::Bitcoin::BTC_SELL_PRICES.expect(
					:usd,
					EMPromise.resolve(BigDecimal(1))
				)
				@bitcoin.stub(:save, EMPromise.resolve(nil)) do
					execute_command(blather: blather) do
						@bitcoin.write
					end
				end
				assert_mock blather
			end
			em :test_write
		end

		class BCHTest < Minitest::Test
			Registration::Payment::BCH::BCH_SELL_PRICES = Minitest::Mock.new
			CustomerFinancials::REDIS = Minitest::Mock.new

			def setup
				@customer = Minitest::Mock.new(
					customer(plan_name: "test_usd")
				)
				@customer.expect(
					:add_bch_address,
					EMPromise.resolve("testaddr")
				)
				@tel = TelSelections::ChooseTel::Tn.for_pending_value("+15555550000")
				@bch = Registration::Payment::BCH.new(
					@customer,
					@tel
				)
			end

			def test_write
				CustomerFinancials::REDIS.expect(
					:smembers,
					EMPromise.resolve([]),
					["jmp_customer_bch_addresses-test"]
				)
				blather = Minitest::Mock.new
				Command::COMMAND_MANAGER.expect(
					:write,
					EMPromise.reject(SessionManager::Timeout.new),
					[Matching.new do |reply|
						assert_equal :canceled, reply.status
						assert_equal "1.000000", reply.form.field("amount").value
						assert_equal "testaddr", reply.form.field("bch_addresses").value
						true
					end]
				)
				Registration::Payment::BCH::BCH_SELL_PRICES.expect(
					:usd,
					EMPromise.resolve(BigDecimal(1))
				)
				@bch.stub(:save, EMPromise.resolve(nil)) do
					execute_command(blather: blather) do
						@bch.write
					end
				end
				assert_mock blather
			end
			em :test_write
		end

		class CreditCardTest < Minitest::Test
			def setup
				@tel = TelSelections::ChooseTel::Tn.for_pending_value("+15555550000")
				@credit_card = Registration::Payment::CreditCard.new(
					customer,
					@tel
				)
			end

			def test_for
				cust = Minitest::Mock.new(customer)
				cust.expect(
					:payment_methods,
					EMPromise.resolve(OpenStruct.new(default_payment_method: :test))
				)
				execute_command do
					Command.execution.customer_repo.expect(:find, cust, ["test"])
					assert_kind_of(
						Registration::Payment::CreditCard,
						Registration::Payment::CreditCard.for(
							cust,
							"+15555550000"
						).sync
					)
				end
			end
			em :test_for

			def test_for_has_balance
				cust = Minitest::Mock.new(customer)
				cust.expect(:balance, 100)
				cust.expect(:payment_methods, EMPromise.resolve(nil))
				Registration::Payment::MaybeBill::BillPlan.expect(
					:new,
					Registration::BillPlan.new(nil, nil)
				) { true }
				execute_command do
					Command.execution.customer_repo.expect(:find, cust, ["test"])
					assert_kind_of(
						Registration::BillPlan,
						Registration::Payment::CreditCard.for(
							cust,
							"+15555550000"
						).sync
					)
				end
			end
			em :test_for_has_balance

			def test_write
				result = execute_command do
					Command::COMMAND_MANAGER.expect(
						:write,
						EMPromise.reject(:test_result),
						[Matching.new do |reply|
							assert_equal [:execute, :next, :prev], reply.allowed_actions
							assert_equal(
								"Pay by credit card, save, then next here to continue: " \
								"http://creditcard.example.com?&amount=1",
								reply.note.content
							)
						end]
					)

					@credit_card.write.catch { |e| e }
				end

				assert_equal :test_result, result
			end
			em :test_write
		end

		class MailTest < Minitest::Test
			def setup
				@tel = TelSelections::ChooseTel::Tn.for_pending_value("+15555550000")
				@mail = Registration::Payment::Mail.new(
					customer(plan_name: "test_cad"),
					@tel
				)
			end

			def test_write
				result = execute_command do
					Command::COMMAND_MANAGER.expect(
						:write,
						EMPromise.reject(:test_result),
						[Matching.new do |reply|
							assert_equal [:execute, :prev], reply.allowed_actions
							refute reply.form.instructions.empty?
							assert_equal(
								"A Mailing Address",
								reply.form.field("adr").value
							)
							assert_equal(
								"interac@example.com",
								reply.form.field("interac_email").value
							)
						end]
					)

					@mail.write.catch { |e| e }
				end

				assert_equal :test_result, result
			end
			em :test_write
		end

		class InviteCodeTest < Minitest::Test
			Registration::Payment::InviteCode::DB =
				Minitest::Mock.new
			Registration::Payment::InviteCode::REDIS =
				Minitest::Mock.new
			Command::COMMAND_MANAGER = Minitest::Mock.new
			Registration::Payment::InviteCode::Finish =
				Minitest::Mock.new
			Registration::Payment::MaybeBill::BillPlan =
				Minitest::Mock.new

			def setup
				@tel = TelSelections::ChooseTel::Tn.for_pending_value("+15555550000")
			end

			def test_write
				customer = customer(plan_name: "test_usd")
				Registration::Payment::InviteCode::DB.expect(:transaction, true, [])
				Registration::Payment::InviteCode::Finish.expect(
					:new,
					OpenStruct.new(write: nil),
					[
						customer,
						@tel
					]
				)
				execute_command do
					Registration::Payment::InviteCode::REDIS.expect(
						:get,
						EMPromise.resolve(nil),
						["jmp_invite_tries-test"]
					)
					Registration::Payment::InviteCode::REDIS.expect(
						:hget,
						EMPromise.resolve(nil),
						["jmp_parent_codes", "abc"]
					)
					Command::COMMAND_MANAGER.expect(
						:write,
						EMPromise.resolve(
							Blather::Stanza::Iq::Command.new.tap { |iq|
								iq.form.fields = [{ var: "code", value: "abc" }]
							}
						),
						[Matching.new do |reply|
							assert_equal :form, reply.form.type
							assert_nil reply.form.instructions
						end]
					)

					Registration::Payment::InviteCode.new(
						customer,
						@tel
					).write
				end
				assert_mock Command::COMMAND_MANAGER
				assert_mock Registration::Payment::InviteCode::DB
				assert_mock Registration::Payment::InviteCode::REDIS
				assert_mock Registration::Payment::InviteCode::Finish
			end
			em :test_write

			def test_write_parent_code
				customer = customer(plan_name: "test_usd")
				Registration::Payment::MaybeBill::BillPlan.expect(
					:new,
					OpenStruct.new(write: nil)
				) { |*| true }
				Registration::Payment::InviteCode::DB.expect(
					:query_one, {}, [String, "parent_customer"], default: {}
				)
				Registration::Payment::InviteCode::DB.expect(
					:query_one, { c: 0 }, [String, "parent_customer"], default: { c: 0 }
				)
				execute_command do
					Registration::Payment::InviteCode::REDIS.expect(
						:hget,
						EMPromise.resolve("parent_customer"),
						["jmp_parent_codes", "pabc"]
					)
					Registration::Payment::InviteCode::REDIS.expect(
						:get,
						EMPromise.resolve(nil),
						["jmp_customer_trust_level-parent_customer"]
					)
					CustomerPlan::DB.expect(
						:query,
						[{ "plan_name" => "test_usd" }],
						[String, ["parent_customer"]]
					)
					CustomerPlan::DB.expect(
						:exec_defer,
						EMPromise.resolve(nil),
						[String, ["test", "test_usd", "parent_customer"]]
					)
					Command.execution.customer_repo.expect(
						:find,
						customer.with_balance(10000),
						["test"]
					)
					Command::COMMAND_MANAGER.expect(
						:write,
						EMPromise.resolve(
							Blather::Stanza::Iq::Command.new.tap { |iq|
								iq.form.fields = [{ var: "code", value: "pabc" }]
							}
						),
						[Matching.new do |reply|
							assert_equal :form, reply.form.type
							assert_nil reply.form.instructions
						end]
					)

					Registration::Payment::InviteCode.new(
						customer,
						@tel
					).write
				end
				assert_mock Command::COMMAND_MANAGER
				assert_mock Registration::Payment::InviteCode::DB
				assert_mock Registration::Payment::InviteCode::REDIS
				assert_mock Registration::Payment::MaybeBill::BillPlan
			end
			em :test_write_parent_code

			def test_write_bad_code
				result = execute_command do
					customer = customer(plan_name: "test_usd")
					Registration::Payment::InviteCode::REDIS.expect(
						:set,
						EMPromise.resolve(nil),
						["jmp_customer_pending_invite-test", "abc"]
					)
					Registration::Payment::InviteCode::REDIS.expect(
						:get,
						EMPromise.resolve(0),
						["jmp_invite_tries-test"]
					)
					Registration::Payment::InviteCode::REDIS.expect(
						:hget,
						EMPromise.resolve(nil),
						["jmp_parent_codes", "abc"]
					)
					Registration::Payment::InviteCode::DB.expect(
						:transaction,
						[]
					) { |&blk| blk.call }
					Registration::Payment::InviteCode::DB.expect(
						:exec,
						OpenStruct.new(cmd_tuples: 0),
						[String, ["test", "abc"]]
					)
					Registration::Payment::InviteCode::REDIS.expect(
						:incr,
						EMPromise.resolve(nil),
						["jmp_invite_tries-test"]
					)
					Registration::Payment::InviteCode::REDIS.expect(
						:expire,
						EMPromise.resolve(nil),
						["jmp_invite_tries-test", 60 * 60]
					)
					Registration::Payment::InviteCode::REDIS.expect(
						:hexists,
						EMPromise.resolve(0),
						["jmp_group_codes", "abc"]
					)
					Command::COMMAND_MANAGER.expect(
						:write,
						EMPromise.resolve(
							Blather::Stanza::Iq::Command.new.tap { |iq|
								iq.form.fields = [{ var: "code", value: "abc" }]
							}
						),
						[Matching.new do |reply|
							assert_equal :form, reply.form.type
							assert_nil reply.form.instructions
						end]
					)
					Command::COMMAND_MANAGER.expect(
						:write,
						EMPromise.reject(:test_result),
						[Matching.new do |reply|
							assert_equal :form, reply.form.type
							assert_equal(
								"Not a valid invite code: abc",
								reply.form.instructions
							)
						end]
					)

					Registration::Payment::InviteCode.new(
						customer,
						@tel
					).write.catch { |e| e }
				end
				assert_equal :test_result, result
				assert_mock Command::COMMAND_MANAGER
				assert_mock Registration::Payment::InviteCode::DB
				assert_mock Registration::Payment::InviteCode::REDIS
			end
			em :test_write_bad_code

			def test_write_group_code
				result = execute_command do
					customer = customer(plan_name: "test_usd")
					Registration::Payment::InviteCode::REDIS.expect(
						:set,
						EMPromise.resolve(nil),
						["jmp_customer_pending_invite-test", "abc"]
					)
					Registration::Payment::InviteCode::REDIS.expect(
						:get,
						EMPromise.resolve(0),
						["jmp_invite_tries-test"]
					)
					Registration::Payment::InviteCode::REDIS.expect(
						:hget,
						EMPromise.resolve(nil),
						["jmp_parent_codes", "abc"]
					)
					Registration::Payment::InviteCode::DB.expect(
						:transaction,
						[]
					) { |&blk| blk.call }
					Registration::Payment::InviteCode::DB.expect(
						:exec,
						OpenStruct.new(cmd_tuples: 0),
						[String, ["test", "abc"]]
					)
					Registration::Payment::InviteCode::REDIS.expect(
						:incr,
						EMPromise.resolve(nil),
						["jmp_invite_tries-test"]
					)
					Registration::Payment::InviteCode::REDIS.expect(
						:expire,
						EMPromise.resolve(nil),
						["jmp_invite_tries-test", 60 * 60]
					)
					Registration::Payment::InviteCode::REDIS.expect(
						:hexists,
						EMPromise.resolve(1),
						["jmp_group_codes", "abc"]
					)
					Command::COMMAND_MANAGER.expect(
						:write,
						EMPromise.resolve(
							Blather::Stanza::Iq::Command.new.tap { |iq|
								iq.form.fields = [{ var: "code", value: "abc" }]
							}
						),
						[Matching.new do |reply|
							assert_equal :form, reply.form.type
							assert_nil reply.form.instructions
						end]
					)
					Command::COMMAND_MANAGER.expect(
						:write,
						EMPromise.reject(:test_result),
						[Matching.new do |reply|
							assert_equal :form, reply.form.type
							assert_equal(
								"abc is a post-payment referral",
								reply.form.instructions
							)
						end]
					)

					Registration::Payment::InviteCode.new(
						customer,
						@tel
					).write.catch { |e| e }
				end
				assert_equal :test_result, result
				assert_mock Command::COMMAND_MANAGER
				assert_mock Registration::Payment::InviteCode::DB
				assert_mock Registration::Payment::InviteCode::REDIS
			end
			em :test_write_group_code

			def test_write_bad_code_over_limit
				result = execute_command do
					customer = customer(plan_name: "test_usd")
					Registration::Payment::InviteCode::REDIS.expect(
						:get,
						EMPromise.resolve(11),
						["jmp_invite_tries-test"]
					)
					Registration::Payment::InviteCode::REDIS.expect(
						:hget,
						EMPromise.resolve(nil),
						["jmp_parent_codes", "abc"]
					)
					Command::COMMAND_MANAGER.expect(
						:write,
						EMPromise.resolve(
							Blather::Stanza::Iq::Command.new.tap { |iq|
								iq.form.fields = [{ var: "code", value: "abc" }]
							}
						),
						[Matching.new do |reply|
							assert_equal :form, reply.form.type
							assert_nil reply.form.instructions
						end]
					)
					Command::COMMAND_MANAGER.expect(
						:write,
						EMPromise.reject(:test_result),
						[Matching.new do |reply|
							assert_equal :form, reply.form.type
							assert_equal "Too many wrong attempts", reply.form.instructions
						end]
					)
					Registration::Payment::InviteCode.new(
						customer,
						@tel
					).write.catch { |e| e }
				end
				assert_equal :test_result, result
				assert_mock Command::COMMAND_MANAGER
				assert_mock Registration::Payment::InviteCode::REDIS
			end
			em :test_write_bad_code_over_limit
		end
	end

	class FinishTest < Minitest::Test
		Customer::BLATHER = Minitest::Mock.new
		Command::COMMAND_MANAGER = Minitest::Mock.new
		Registration::Finish::TEL_SELECTIONS = FakeTelSelections.new
		Registration::Finish::REDIS = Minitest::Mock.new
		Registration::Finish::DB = Minitest::Mock.new
		Bwmsgsv2Repo::REDIS = Minitest::Mock.new
		Registration::FinishOnboarding::DB = FakeDB.new
		Transaction::DB = Minitest::Mock.new

		def setup
			@sgx = Minitest::Mock.new(mksgx)
			iq = Blather::Stanza::Iq::Command.new
			iq.from = "test\\40example.com@cheogram.com"
			@finish = Registration::Finish.new(
				customer(sgx: @sgx, plan_name: "test_usd"),
				TelSelections::ChooseTel::Tn.for_pending_value("+15555550000")
			)
		end

		def test_write
			create_order = stub_request(
				:post,
				"https://dashboard.bandwidth.com/v1.0/accounts//orders"
			).to_return(status: 201, body: <<~RESPONSE)
				<OrderResponse>
					<Order>
						<id>test_order</id>
					</Order>
				</OrderResponse>
			RESPONSE
			stub_request(
				:get,
				"https://dashboard.bandwidth.com/v1.0/accounts//orders/test_order"
			).to_return(status: 201, body: <<~RESPONSE)
				<OrderResponse>
					<OrderStatus>COMPLETE</OrderStatus>
					<CompletedNumbers>
						<TelephoneNumber>
							<FullNumber>5555550000</FullNumber>
						</TelephoneNumber>
					</CompletedNumbers>
				</OrderResponse>
			RESPONSE
			stub_request(
				:post,
				"https://dashboard.bandwidth.com/v1.0/accounts//sites//sippeers//movetns"
			)
			Registration::Finish::REDIS.expect(
				:get,
				nil,
				["jmp_customer_pending_invite-test"]
			)
			Registration::Finish::REDIS.expect(
				:del,
				nil,
				["jmp_customer_pending_invite-test"]
			)
			Registration::Finish::REDIS.expect(
				:hget,
				nil,
				["jmp_group_codes", nil]
			)
			Bwmsgsv2Repo::REDIS.expect(
				:get,
				EMPromise.resolve(nil),
				["jmp_customer_backend_sgx-test"]
			)
			Bwmsgsv2Repo::REDIS.expect(
				:set,
				nil,
				[
					"catapult_fwd-+15555550000",
					"xmpp:test@example.net"
				]
			)
			Bwmsgsv2Repo::REDIS.expect(
				:set,
				nil,
				["catapult_fwd_timeout-customer_test@component", 25]
			)
			Customer::BLATHER.expect(
				:<<,
				nil,
				[Matching.new do |m|
					assert_equal :chat, m.type
					assert m.body =~ /^Welcome to JMP/
				end]
			)
			blather = Minitest::Mock.new
			blather.expect(
				:<<,
				nil,
				[Matching.new do |reply|
					assert_equal :completed, reply.status
					assert_equal :info, reply.note_type
					assert_equal(
						"Your JMP account has been activated as (555) 555-0000",
						reply.note.content
					)
				end]
			)
			execute_command(blather: blather) do
				@sgx.expect(
					:register!,
					EMPromise.resolve(@sgx.with(
						registered?: Blather::Stanza::Iq::IBR.new.tap do |ibr|
							ibr.phone = "+15555550000"
						end
					)),
					["+15555550000"]
				)

				@finish.write
			end
			assert_requested create_order
			assert_mock @sgx
			assert_mock Registration::Finish::REDIS
			assert_mock Bwmsgsv2Repo::REDIS
			assert_mock Customer::BLATHER
			assert_mock blather
		end
		em :test_write

		def test_write_with_pending_code
			create_order = stub_request(
				:post,
				"https://dashboard.bandwidth.com/v1.0/accounts//orders"
			).to_return(status: 201, body: <<~RESPONSE)
				<OrderResponse>
					<Order>
						<id>test_order</id>
					</Order>
				</OrderResponse>
			RESPONSE
			stub_request(
				:get,
				"https://dashboard.bandwidth.com/v1.0/accounts//orders/test_order"
			).to_return(status: 201, body: <<~RESPONSE)
				<OrderResponse>
					<OrderStatus>COMPLETE</OrderStatus>
					<CompletedNumbers>
						<TelephoneNumber>
							<FullNumber>5555550000</FullNumber>
						</TelephoneNumber>
					</CompletedNumbers>
				</OrderResponse>
			RESPONSE
			stub_request(
				:post,
				"https://dashboard.bandwidth.com/v1.0/accounts//sites//sippeers//movetns"
			)
			Registration::Finish::REDIS.expect(
				:get,
				EMPromise.resolve("123"),
				["jmp_customer_pending_invite-test"]
			)
			Registration::Finish::REDIS.expect(
				:del,
				nil,
				["jmp_customer_pending_invite-test"]
			)
			Registration::Finish::REDIS.expect(
				:hget,
				EMPromise.resolve("test-inviter"),
				["jmp_group_codes", "123"]
			)
			Registration::Finish::DB.expect(
				:exec,
				EMPromise.resolve(nil),
				[String, ["test-inviter", "test"]]
			)
			Transaction::DB.expect(:transaction, nil) do |&blk|
				blk.call
				true
			end
			Transaction::DB.expect(
				:exec,
				nil,
				[String, Matching.new { |params|
					assert_equal "test", params[0]
					assert params[1].start_with?("referral_")
					assert_equal 1, params[4]
					assert_equal "Referral Bonus", params[5]
				}]
			)
			Bwmsgsv2Repo::REDIS.expect(
				:get,
				EMPromise.resolve(nil),
				["jmp_customer_backend_sgx-test"]
			)
			Bwmsgsv2Repo::REDIS.expect(
				:set,
				nil,
				[
					"catapult_fwd-+15555550000",
					"xmpp:test@example.net"
				]
			)
			Bwmsgsv2Repo::REDIS.expect(
				:set,
				nil,
				["catapult_fwd_timeout-customer_test@component", 25]
			)
			Customer::BLATHER.expect(
				:<<,
				nil,
				[Matching.new do |m|
					assert_equal :chat, m.type
					assert m.body =~ /^Welcome to JMP/
				end]
			)
			blather = Minitest::Mock.new
			blather.expect(
				:<<,
				nil,
				[Matching.new do |reply|
					assert_equal :completed, reply.status
					assert_equal :info, reply.note_type
					assert_equal(
						"Your JMP account has been activated as (555) 555-0000",
						reply.note.content
					)
				end]
			)
			execute_command(blather: blather) do
				@sgx.expect(
					:register!,
					EMPromise.resolve(@sgx.with(
						registered?: Blather::Stanza::Iq::IBR.new.tap do |ibr|
							ibr.phone = "+15555550000"
						end
					)),
					["+15555550000"]
				)

				@finish.write
			end
			assert_requested create_order
			assert_mock @sgx
			assert_mock Registration::Finish::REDIS
			assert_mock Bwmsgsv2Repo::REDIS
			assert_mock Customer::BLATHER
			assert_mock blather
			assert_mock Transaction::DB
		end
		em :test_write_with_pending_code

		def test_write_onboarding
			create_order = stub_request(
				:post,
				"https://dashboard.bandwidth.com/v1.0/accounts//orders"
			).to_return(status: 201, body: <<~RESPONSE)
				<OrderResponse>
					<Order>
						<id>test_order</id>
					</Order>
				</OrderResponse>
			RESPONSE
			stub_request(
				:get,
				"https://dashboard.bandwidth.com/v1.0/accounts//orders/test_order"
			).to_return(status: 201, body: <<~RESPONSE)
				<OrderResponse>
					<OrderStatus>COMPLETE</OrderStatus>
					<CompletedNumbers>
						<TelephoneNumber>
							<FullNumber>5555550000</FullNumber>
						</TelephoneNumber>
					</CompletedNumbers>
				</OrderResponse>
			RESPONSE
			stub_request(
				:post,
				"https://dashboard.bandwidth.com/v1.0/accounts//sites//sippeers//movetns"
			)
			Registration::Finish::REDIS.expect(
				:get,
				nil,
				["jmp_customer_pending_invite-test"]
			)
			Registration::Finish::REDIS.expect(
				:del,
				nil,
				["jmp_customer_pending_invite-test"]
			)
			Registration::Finish::REDIS.expect(
				:hget,
				nil,
				["jmp_group_codes", nil]
			)
			Bwmsgsv2Repo::REDIS.expect(
				:get,
				EMPromise.resolve(nil),
				["jmp_customer_backend_sgx-test"]
			)
			Bwmsgsv2Repo::REDIS.expect(
				:set,
				nil,
				[
					"catapult_fwd-+15555550000",
					"xmpp:test\\40onboarding.example.com@proxy"
				]
			)
			Bwmsgsv2Repo::REDIS.expect(
				:set,
				nil,
				["catapult_fwd_timeout-customer_test@component", 25]
			)
			result = execute_command do
				@sgx.expect(
					:register!,
					EMPromise.resolve(@sgx.with(
						registered?: Blather::Stanza::Iq::IBR.new.tap do |ibr|
							ibr.phone = "+15555550000"
						end
					)),
					["+15555550000"]
				)

				Command::COMMAND_MANAGER.expect(
					:write,
					EMPromise.reject(:test_result),
					[Matching.new do |iq|
						assert_equal :form, iq.form.type
						assert iq.form.field("subdomain")
					end]
				)

				Registration::Finish.new(
					customer(
						sgx: @sgx,
						jid: Blather::JID.new("test\\40onboarding.example.com@proxy")
					),
					TelSelections::ChooseTel::Tn.for_pending_value("+15555550000")
				).write.catch { |e| e }
			end
			assert_equal :test_result, result
			assert_requested create_order
			assert_mock @sgx
			assert_mock Registration::Finish::REDIS
			assert_mock Bwmsgsv2Repo::REDIS
			assert_mock Command::COMMAND_MANAGER
		end
		em :test_write_onboarding

		def test_write_local_inventory
			stub_request(
				:post,
				"https://dashboard.bandwidth.com/v1.0/accounts/moveto/moveTns"
			).with(
				body: {
					CustomerOrderId: "test",
					SourceAccountId: "bandwidth_account_id",
					SiteId: "test_site",
					SipPeerId: "test_peer",
					TelephoneNumbers: { TelephoneNumber: "5555550000" }
				}.to_xml(indent: 0, root: "MoveTnsOrder")
			).to_return(status: 200, body: "", headers: {})

			Registration::Finish::REDIS.expect(
				:get,
				nil,
				["jmp_customer_pending_invite-test"]
			)
			Registration::Finish::REDIS.expect(
				:del,
				nil,
				["jmp_customer_pending_invite-test"]
			)
			Registration::Finish::REDIS.expect(
				:hget,
				nil,
				["jmp_group_codes", nil]
			)
			Registration::Finish::DB.expect(
				:exec_defer,
				EMPromise.resolve(OpenStruct.new(cmd_tuples: 1)),
				[String, ["+15555550000"]]
			)
			Bwmsgsv2Repo::REDIS.expect(
				:get,
				EMPromise.resolve(nil),
				["jmp_customer_backend_sgx-test"]
			)
			Bwmsgsv2Repo::REDIS.expect(
				:set,
				nil,
				[
					"catapult_fwd-+15555550000",
					"xmpp:test\\40onboarding.example.com@proxy"
				]
			)
			Bwmsgsv2Repo::REDIS.expect(
				:set,
				nil,
				["catapult_fwd_timeout-customer_test@component", 25]
			)
			result = execute_command do
				@sgx.expect(
					:register!,
					EMPromise.resolve(@sgx.with(
						registered?: Blather::Stanza::Iq::IBR.new.tap do |ibr|
							ibr.phone = "+15555550000"
						end
					)),
					["+15555550000"]
				)

				Command::COMMAND_MANAGER.expect(
					:write,
					EMPromise.reject(:test_result),
					[Matching.new do |iq|
						assert_equal :form, iq.form.type
						assert iq.form.field("subdomain")
					end]
				)

				Registration::Finish.new(
					customer(
						sgx: @sgx,
						jid: Blather::JID.new("test\\40onboarding.example.com@proxy")
					),
					TelSelections::ChooseTel::Tn::LocalInventory.new(
						TelSelections::ChooseTel::Tn.new("+15555550000"),
						"bandwidth_account_id"
					)
				).write.catch { |e| e }
			end
			assert_equal :test_result, result
			assert_mock @sgx
			assert_mock Registration::Finish::REDIS
			assert_mock Bwmsgsv2Repo::REDIS
			assert_mock Command::COMMAND_MANAGER
		end
		em :test_write_local_inventory

		def test_write_local_inventory_must_pay
			low_cust = customer(
				sgx: @sgx,
				jid: Blather::JID.new("test\\40onboarding.example.com@proxy")
			).with_balance(5.0)
			high_cust = customer(
				sgx: @sgx,
				jid: Blather::JID.new("test\\40onboarding.example.com@proxy")
			).with_balance(100.0)

			stub_request(
				:post,
				"https://dashboard.bandwidth.com/v1.0/accounts/moveto/moveTns"
			).with(
				body: {
					CustomerOrderId: "test",
					SourceAccountId: "bandwidth_account_id",
					SiteId: "test_site",
					SipPeerId: "test_peer",
					TelephoneNumbers: { TelephoneNumber: "5555550000" }
				}.to_xml(indent: 0, root: "MoveTnsOrder")
			).to_return(status: 200, body: "", headers: {})

			Registration::Finish::REDIS.expect(
				:get,
				nil,
				["jmp_customer_pending_invite-test"]
			)
			Registration::Finish::REDIS.expect(
				:del,
				nil,
				["jmp_customer_pending_invite-test"]
			)
			Registration::Finish::REDIS.expect(
				:hget,
				nil,
				["jmp_group_codes", nil]
			)
			Registration::Finish::DB.expect(
				:exec_defer,
				EMPromise.resolve(OpenStruct.new(cmd_tuples: 1)),
				[String, ["+15555550000"]]
			)
			Bwmsgsv2Repo::REDIS.expect(
				:get,
				EMPromise.resolve(nil),
				["jmp_customer_backend_sgx-test"]
			)
			Bwmsgsv2Repo::REDIS.expect(
				:set,
				nil,
				[
					"catapult_fwd-+15555550000",
					"xmpp:test\\40onboarding.example.com@proxy"
				]
			)
			Bwmsgsv2Repo::REDIS.expect(
				:set,
				nil,
				["catapult_fwd_timeout-customer_test@component", 25]
			)

			local_tel = TelSelections::ChooseTel::Tn::LocalInventory.new(
				TelSelections::ChooseTel::Tn.new("+15555550000"),
				"bandwidth_account_id",
				price: 10.0
			)

			Registration::Payment::CreditCard.stub(
				:new,
				OpenStruct.new(
					write: lambda {
						# simulates successful try_again
						Registration::Payment::CreditCard.for(
							high_cust,
							local_tel,
							# we know maybe_bill will be passed as JustCharge
							# since that's hardcoded
							maybe_bill: Registration::Payment::JustCharge
						)
					}
				)
			) do
				result = execute_command do
					@sgx.expect(
						:register!,
						EMPromise.resolve(@sgx.with(
							registered?: Blather::Stanza::Iq::IBR.new.tap do |ibr|
								ibr.phone = "+15555550000"
							end
						)),
						["+15555550000"]
					)

					Command::COMMAND_MANAGER.expect(
						:write,
						EMPromise.resolve(Blather::Stanza::Iq::Command.new.tap { |iq|
							iq.form.fields = [
								{ var: "activation_method", value: "credit_card" }
							]
						}),
						[Matching.new do |iq|
							assert_equal :form, iq.form.type
							assert iq.form.field("activation_method")
						end]
					)

					Command.execution.customer_repo.expect(
						:find,
						EMPromise.resolve(high_cust),
						["test"]
					)

					Command::COMMAND_MANAGER.expect(
						:write,
						EMPromise.reject(:test_result),
						[Matching.new do |iq|
							assert_equal :form, iq.form.type
							assert iq.form.field("subdomain")
						end]
					)

					Registration::Finish.new(
						low_cust,
						local_tel
					).write.catch { |e| e }
				end
				assert_equal :test_result, result
				assert_mock @sgx
				assert_mock Registration::Finish::REDIS
				assert_mock Bwmsgsv2Repo::REDIS
				assert_mock Command::COMMAND_MANAGER
			end
		end
		em :test_write_local_inventory_must_pay

		def test_write_tn_fail
			create_order = stub_request(
				:post,
				"https://dashboard.bandwidth.com/v1.0/accounts//orders"
			).to_return(status: 201, body: <<~RESPONSE)
				<OrderResponse>
					<Order>
						<id>test_order</id>
					</Order>
				</OrderResponse>
			RESPONSE
			stub_request(
				:get,
				"https://dashboard.bandwidth.com/v1.0/accounts//orders/test_order"
			).to_return(status: 201, body: <<~RESPONSE)
				<OrderResponse>
					<OrderStatus>FAILED</OrderStatus>
				</OrderResponse>
			RESPONSE

			result = execute_command do
				Command::COMMAND_MANAGER.expect(
					:write,
					EMPromise.reject(:test_result),
					[Matching.new do |iq|
						assert_equal :form, iq.form.type
						assert_equal(
							"The JMP number (555) 555-0000 is no longer available.",
							iq.form.instructions
						)
					end]
				)

				@finish.write.catch { |e| e }
			end

			assert_equal :test_result, result
			assert_mock Command::COMMAND_MANAGER
			assert_instance_of(
				TelSelections::ChooseTel,
				Registration::Finish::TEL_SELECTIONS["test@example.com"]
			)
			assert_requested create_order
		end
		em :test_write_tn_fail
	end

	class SnikketTest < Minitest::Test
		Command::COMMAND_MANAGER = Minitest::Mock.new
		Registration::FinishOnboarding::Snikket::IQ_MANAGER = Minitest::Mock.new

		def setup
			@sgx = Minitest::Mock.new(mksgx)
			@snikket = Registration::FinishOnboarding::Snikket.new(
				customer(sgx: @sgx),
				"+15555550000",
				db: FakeDB.new
			)
		end

		def test_write
			xmpp_uri = "xmpp:test.snikket.chat?register;preauth=NEWTOKEN"

			subdomain_form = Blather::Stanza::Iq::Command.new
			subdomain_form.form.fields = [
				{ var: "subdomain", value: "test" }
			]

			launched = Snikket::Launched.new
			launched << Niceogiri::XML::Node.new(
				:launched, launched.document, "xmpp:snikket.org/hosting/v1"
			).tap { |inner|
				inner << Niceogiri::XML::Node.new(
					:'instance-id', launched.document, "xmpp:snikket.org/hosting/v1"
				).tap { |id|
					id.content = "si-1234"
				}
				inner << Niceogiri::XML::Node.new(
					:bootstrap, launched.document, "xmpp:snikket.org/hosting/v1"
				).tap { |bootstrap|
					bootstrap << Niceogiri::XML::Node.new(
						:token, launched.document, "xmpp:snikket.org/hosting/v1"
					).tap { |token|
						token.content = "TOKEN"
					}
				}
			}

			blather = Minitest::Mock.new
			blather.expect(
				:<<,
				nil,
				[Matching.new do |reply|
					assert_equal :completed, reply.status
					assert_equal(
						xmpp_uri,
						OOB.find_or_create(reply.command).url
					)
				end]
			)

			execute_command(blather: blather) do
				Command::COMMAND_MANAGER.expect(
					:write,
					EMPromise.resolve(subdomain_form),
					[Matching.new do |iq|
						assert_equal :form, iq.form.type
						assert iq.form.field("subdomain")
					end]
				)

				Registration::FinishOnboarding::Snikket::IQ_MANAGER.expect(
					:write,
					EMPromise.resolve(launched),
					[Matching.new do |iq|
						assert_equal :set, iq.type
						assert_equal CONFIG[:snikket_hosting_api], iq.to.to_s
						assert_equal(
							"test.snikket.chat",
							iq.xpath(
								"./ns:launch/ns:domain",
								ns: "xmpp:snikket.org/hosting/v1"
							).text
						)
					end]
				)

				# Webmock doesn't support redirects properly, but they work live
				stub_request(
					:head, "https://test.snikket.chat/invites_bootstrap?token=TOKEN"
				).to_return(
					status: 200,
					headers: {
						"link" => "<#{xmpp_uri}>; rel=\"alternate\""
					}
				)

				@snikket.write
			end

			assert_mock Command::COMMAND_MANAGER
			assert_mock Registration::FinishOnboarding::Snikket::IQ_MANAGER
			assert_mock blather
		end
		em :test_write

		def test_write_not_yet
			subdomain_form = Blather::Stanza::Iq::Command.new
			subdomain_form.form.fields = [
				{ var: "subdomain", value: "test" }
			]

			launched = Snikket::Launched.new
			launched << Niceogiri::XML::Node.new(
				:launched, launched.document, "xmpp:snikket.org/hosting/v1"
			).tap { |inner|
				inner << Niceogiri::XML::Node.new(
					:'instance-id', launched.document, "xmpp:snikket.org/hosting/v1"
				).tap { |id|
					id.content = "si-1234"
				}
				inner << Niceogiri::XML::Node.new(
					:bootstrap, launched.document, "xmpp:snikket.org/hosting/v1"
				).tap { |bootstrap|
					bootstrap << Niceogiri::XML::Node.new(
						:token, launched.document, "xmpp:snikket.org/hosting/v1"
					).tap { |token|
						token.content = "TOKEN"
					}
				}
			}

			result = execute_command do
				Command::COMMAND_MANAGER.expect(
					:write,
					EMPromise.resolve(subdomain_form),
					[Matching.new do |iq|
						assert_equal :form, iq.form.type
						assert iq.form.field("subdomain")
					end]
				)

				Registration::FinishOnboarding::Snikket::IQ_MANAGER.expect(
					:write,
					EMPromise.resolve(launched),
					[Matching.new do |iq|
						assert_equal :set, iq.type
						assert_equal CONFIG[:snikket_hosting_api], iq.to.to_s
						assert_equal(
							"test.snikket.chat",
							iq.xpath(
								"./ns:launch/ns:domain",
								ns: "xmpp:snikket.org/hosting/v1"
							).text
						)
					end]
				)

				stub_request(
					:head, "https://test.snikket.chat/invites_bootstrap?token=TOKEN"
				).to_timeout

				Command::COMMAND_MANAGER.expect(
					:write,
					EMPromise.reject(:test_result),
					[Matching.new do |iq|
						assert_equal :result, iq.form.type
						assert iq.form.instructions =~ / test\.snikket\.chat /
						assert_equal "jid-single", iq.form.field("support").type
					end]
				)

				@snikket.write.catch { |e| e }
			end

			assert_equal :test_result, result
			assert_mock Command::COMMAND_MANAGER
			assert_mock Registration::FinishOnboarding::Snikket::IQ_MANAGER
		end
		em :test_write_not_yet

		def test_write_already_taken
			subdomain_form = Blather::Stanza::Iq::Command.new
			subdomain_form.form.fields = [
				{ var: "subdomain", value: "test" }
			]

			launched = Snikket::Launched.new.as_error(
				"internal-server-error",
				:wait,
				"This is an error"
			)

			result = execute_command do
				Command::COMMAND_MANAGER.expect(
					:write,
					EMPromise.resolve(subdomain_form),
					[Matching.new do |iq|
						assert_equal :form, iq.form.type
						assert iq.form.field("subdomain")
					end]
				)

				Registration::FinishOnboarding::Snikket::IQ_MANAGER.expect(
					:write,
					EMPromise.reject(launched),
					[Matching.new do |iq|
						assert_equal :set, iq.type
						assert_equal CONFIG[:snikket_hosting_api], iq.to.to_s
						assert_equal(
							"test.snikket.chat",
							iq.xpath(
								"./ns:launch/ns:domain",
								ns: "xmpp:snikket.org/hosting/v1"
							).text
						)
					end]
				)

				stub_request(
					:head, "https://test.snikket.chat/invites_bootstrap?token=TOKEN"
				).to_timeout

				Command::COMMAND_MANAGER.expect(
					:write,
					EMPromise.reject(:test_result),
					[Matching.new do |iq|
						assert iq.executing?
						assert_equal(
							"This is an error",
							iq.form.field("subdomain").desc
						)
					end]
				)

				@snikket.write.catch { |e| e }
			end

			assert_equal :test_result, result
			assert_mock Command::COMMAND_MANAGER
			assert_mock Registration::FinishOnboarding::Snikket::IQ_MANAGER
		end
		em :test_write_already_taken
	end

	class SnikketCustomDomainTest < Minitest::Test
		def setup
			@snikket = Registration::FinishOnboarding::CustomDomain.new(
				customer,
				"+15555550000",
				db: FakeDB.new
			)
		end

		def test_write_needs_dns
			domain_form = Blather::Stanza::Iq::Command.new
			domain_form.form.fields = [
				{ var: "domain", value: "snikket.example.com" }
			]

			launched = Snikket::Launched.new
			launched << Niceogiri::XML::Node.new(
				:launched, launched.document, "xmpp:snikket.org/hosting/v1"
			).tap { |inner|
				inner << Niceogiri::XML::Node.new(
					:'instance-id', launched.document, "xmpp:snikket.org/hosting/v1"
				).tap { |id|
					id.content = "si-1234"
				}
				inner << Niceogiri::XML::Node.new(
					:status, launched.document, "xmpp:snikket.org/hosting/v1"
				).tap { |id|
					id.content = "needs_dns"
				}
				inner << Niceogiri::XML::Node.new(
					:records, launched.document, "xmpp:snikket.org/hosting/v1"
				).tap { |records|
					records << Niceogiri::XML::Node.new(
						:record, launched.document, "xmpp:snikket.org/hosting/v1"
					).tap { |record|
						record << Niceogiri::XML::Node.new(
							:name, launched.document, "xmpp:snikket.org/hosting/v1"
						).tap { |name| name.content = "snikket.example.com" }
						record << Niceogiri::XML::Node.new(
							:type, launched.document, "xmpp:snikket.org/hosting/v1"
						).tap { |type| type.content = "AAAA" }
						record << Niceogiri::XML::Node.new(
							:status, launched.document, "xmpp:snikket.org/hosting/v1"
						).tap { |type| type.content = "incorrect" }
						record << Niceogiri::XML::Node.new(
							:expected, launched.document, "xmpp:snikket.org/hosting/v1"
						).tap { |expected|
							expected << Niceogiri::XML::Node.new(
								:value, launched.document, "xmpp:snikket.org/hosting/v1"
							).tap { |value| value.content = "1::2" }
						}
						record << Niceogiri::XML::Node.new(
							:found, launched.document, "xmpp:snikket.org/hosting/v1"
						).tap { |found|
							found << Niceogiri::XML::Node.new(
								:value, launched.document, "xmpp:snikket.org/hosting/v1"
							).tap { |value| value.content = "0::0" }
						}
					}
				}
			}

			result = execute_command do
				Command::COMMAND_MANAGER.expect(
					:write,
					EMPromise.resolve(domain_form),
					[Matching.new do |iq|
						assert_equal :form, iq.form.type
						assert iq.form.field("domain")
					end]
				)

				Registration::FinishOnboarding::Snikket::IQ_MANAGER.expect(
					:write,
					EMPromise.resolve(launched),
					[Matching.new do |iq|
						assert_equal :set, iq.type
						assert_equal CONFIG[:snikket_hosting_api], iq.to.to_s
						assert_equal(
							"snikket.example.com",
							iq.xpath(
								"./ns:launch/ns:domain",
								ns: "xmpp:snikket.org/hosting/v1"
							).text
						)
					end]
				)

				Command::COMMAND_MANAGER.expect(
					:write,
					EMPromise.reject(:test_result),
					[Matching.new do |iq|
						assert_equal :form, iq.form.type
						assert_equal(
							["snikket.example.com"],
							iq.form.xpath(
								"./ns:item/ns:field[@var='name']/ns:value",
								ns: "jabber:x:data"
							).map(&:content)
						)
						assert_equal(
							["1::2"],
							iq.form.xpath(
								"./ns:item/ns:field[@var='expected']/ns:value",
								ns: "jabber:x:data"
							).map(&:content)
						)
					end]
				)

				@snikket.write.catch { |e| e }
			end

			assert_equal :test_result, result
			assert_mock Command::COMMAND_MANAGER
			assert_mock Registration::FinishOnboarding::Snikket::IQ_MANAGER
		end
		em :test_write_needs_dns
	end
end
