diff --git a/lib/command_list.rb b/lib/command_list.rb index 465125337c9d0c7f1219b49d2ddba5e213a5e703..d2700f3ce704034cc346e5fd06a49b6e74aa3728 100644 --- a/lib/command_list.rb +++ b/lib/command_list.rb @@ -3,12 +3,10 @@ class CommandList include Enumerable - def self.for(jid) - Customer.for_jid(jid).catch { nil }.then do |customer| - EMPromise.resolve(customer&.registered?).catch { nil }.then do |reg| - next Registered.for(customer, reg.phone) if reg - CommandList.new - end + def self.for(customer) + EMPromise.resolve(customer&.registered?).catch { nil }.then do |reg| + next Registered.for(customer, reg.phone) if reg + CommandList.new end end diff --git a/lib/customer.rb b/lib/customer.rb index 4698e213ea06f0a97b3b30a17da53f65edd93a89..cba919204fd0dfa501d5b16dd6679bd4b2df1281 100644 --- a/lib/customer.rb +++ b/lib/customer.rb @@ -12,37 +12,6 @@ require_relative "./plan" require_relative "./sip_account" class Customer - def self.for_jid(jid) - REDIS.get("jmp_customer_id-#{jid}").then do |customer_id| - raise "No customer id" unless customer_id - for_customer_id(customer_id) - end - end - - def self.for_customer_id(customer_id) - result = DB.query_defer(<<~SQL, [customer_id]) - SELECT COALESCE(balance,0) AS balance, plan_name, expires_at - FROM customer_plans LEFT JOIN balances USING (customer_id) - WHERE customer_id=$1 LIMIT 1 - SQL - result.then do |rows| - new(customer_id, **rows.first&.transform_keys(&:to_sym) || {}) - end - end - - def self.create(jid) - BRAINTREE.customer.create.then do |result| - raise "Braintree customer create failed" unless result.success? - cid = result.customer.id - REDIS.msetnx( - "jmp_customer_id-#{jid}", cid, "jmp_customer_jid-#{cid}", jid - ).then do |redis_result| - raise "Saving new customer to redis failed" unless redis_result == 1 - new(cid) - end - end - end - extend Forwardable attr_reader :customer_id, :balance diff --git a/lib/customer_repo.rb b/lib/customer_repo.rb new file mode 100644 index 0000000000000000000000000000000000000000..66af4effdcf90f0dc27829bef06618fd0ecfbc19 --- /dev/null +++ b/lib/customer_repo.rb @@ -0,0 +1,42 @@ +# frozen_string_literal: true + +require_relative "customer" + +class CustomerRepo + def initialize(redis: REDIS, db: DB, braintree: BRAINTREE) + @redis = redis + @db = db + @braintree = braintree + end + + def find(customer_id) + result = @db.query_defer(<<~SQL, [customer_id]) + SELECT COALESCE(balance,0) AS balance, plan_name, expires_at + FROM customer_plans LEFT JOIN balances USING (customer_id) + WHERE customer_id=$1 LIMIT 1 + SQL + result.then do |rows| + Customer.new(customer_id, **rows.first&.transform_keys(&:to_sym) || {}) + end + end + + def find_by_jid(jid) + @redis.get("jmp_customer_id-#{jid}").then do |customer_id| + raise "No customer id" unless customer_id + find(customer_id) + end + end + + def create(jid) + @braintree.customer.create.then do |result| + raise "Braintree customer create failed" unless result.success? + cid = result.customer.id + @redis.msetnx( + "jmp_customer_id-#{jid}", cid, "jmp_customer_jid-#{cid}", jid + ).then do |redis_result| + raise "Saving new customer to redis failed" unless redis_result == 1 + Customer.new(cid) + end + end + end +end diff --git a/sgx_jmp.rb b/sgx_jmp.rb index 8c24367470f6629163a67127cb7ffcd234a8029f..2613ca4b14bc8e8a3c33e559b9ea5e9570604718 100644 --- a/sgx_jmp.rb +++ b/sgx_jmp.rb @@ -44,6 +44,7 @@ require_relative "lib/btc_sell_prices" require_relative "lib/buy_account_credit_form" require_relative "lib/command_list" require_relative "lib/customer" +require_relative "lib/customer_repo" require_relative "lib/electrum" require_relative "lib/error_to_send" require_relative "lib/em" @@ -170,7 +171,7 @@ before nil, to: /\Acustomer_/, from: /(\A|@)#{CONFIG[:sgx]}(\/|\Z)/ do |s| StatsD.increment("stanza_customer") sentry_hub = new_sentry_hub(s, name: "stanza_customer") - Customer.for_customer_id( + CustomerRepo.new.find( s.to.node.delete_prefix("customer_") ).then { |customer| sentry_hub.current_scope.set_user( @@ -195,7 +196,7 @@ message( &.find { |el| el["jid"].to_s.start_with?("customer_") } pass unless address - Customer.for_customer_id( + CustomerRepo.new.find( Blather::JID.new(address["jid"].to_s).node.delete_prefix("customer_") ).then(&:jid).then { |customer_jid| m.from = m.from.with(domain: CONFIG[:component][:jid]) @@ -218,7 +219,7 @@ message do |m| sentry_hub = new_sentry_hub(m, name: "message") today = Time.now.utc.to_date - Customer.for_jid(m.from.stripped).then { |customer| + CustomerRepo.new.find_by_jid(m.from.stripped).then { |customer| sentry_hub.current_scope.set_user( id: customer.customer_id, jid: m.from.stripped.to_s ) @@ -337,7 +338,11 @@ disco_items node: "http://jabber.org/protocol/commands" do |iq| sentry_hub = new_sentry_hub(iq, name: iq.node) reply = iq.reply - CommandList.for(iq.from.stripped).then { |list| + CustomerRepo.new.find_by_jid(iq.from.stripped).catch { + nil + }.then { |customer| + CommandList.for(customer) + }.then { |list| reply.items = list.map do |item| Blather::Stanza::DiscoItems::Item.new( iq.to, @@ -370,12 +375,12 @@ command :execute?, node: "jabber:iq:register", sessionid: nil do |iq| sentry_hub = new_sentry_hub(iq, name: iq.node) EMPromise.resolve(nil).then { - Customer.for_jid(iq.from.stripped) + CustomerRepo.new.find_by_jid(iq.from.stripped) }.catch { sentry_hub.add_breadcrumb(Sentry::Breadcrumb.new( message: "Customer.create" )) - Customer.create(iq.from.stripped) + CustomerRepo.new.create(iq.from.stripped) }.then { |customer| sentry_hub.current_scope.set_user( id: customer.customer_id, @@ -412,7 +417,7 @@ command node: [ StatsD.increment("command", tags: ["node:#{iq.node}"]) sentry_hub = new_sentry_hub(iq, name: iq.node) - Customer.for_jid(iq.from.stripped).then { |customer| + CustomerRepo.new.find_by_jid(iq.from.stripped).then { |customer| sentry_hub.current_scope.set_user( id: customer.customer_id, jid: iq.from.stripped.to_s @@ -429,7 +434,7 @@ command :execute?, node: "credit cards", sessionid: nil do |iq| reply = iq.reply reply.status = :completed - Customer.for_jid(iq.from.stripped).then { |customer| + CustomerRepo.new.find_by_jid(iq.from.stripped).then { |customer| oob = OOB.find_or_create(reply.command) oob.url = CONFIG[:credit_card_url].call( reply.to.stripped.to_s.gsub("\\", "%5C"), @@ -451,7 +456,7 @@ command :execute?, node: "top up", sessionid: nil do |iq| reply = iq.reply reply.allowed_actions = [:complete] - Customer.for_jid(iq.from.stripped).then { |customer| + CustomerRepo.new.find_by_jid(iq.from.stripped).then { |customer| BuyAccountCreditForm.for(customer).then do |credit_form| credit_form.add_to_form(reply.form) COMMAND_MANAGER.write(reply).then { |iq2| [customer, credit_form, iq2] } @@ -480,7 +485,7 @@ command :execute?, node: "alt top up", sessionid: nil do |iq| reply.status = :executing reply.allowed_actions = [:complete] - Customer.for_jid(iq.from.stripped).then { |customer| + CustomerRepo.new.find_by_jid(iq.from.stripped).then { |customer| sentry_hub.current_scope.set_user( id: customer.customer_id, jid: iq.from.stripped.to_s @@ -500,7 +505,7 @@ command :execute?, node: "reset sip account", sessionid: nil do |iq| StatsD.increment("command", tags: ["node:#{iq.node}"]) sentry_hub = new_sentry_hub(iq, name: iq.node) - Customer.for_jid(iq.from.stripped).then { |customer| + CustomerRepo.new.find_by_jid(iq.from.stripped).then { |customer| sentry_hub.current_scope.set_user( id: customer.customer_id, jid: iq.from.stripped.to_s @@ -519,7 +524,7 @@ command :execute?, node: "usage", sessionid: nil do |iq| sentry_hub = new_sentry_hub(iq, name: iq.node) report_for = (Date.today..(Date.today << 1)) - Customer.for_jid(iq.from.stripped).then { |customer| + CustomerRepo.new.find_by_jid(iq.from.stripped).then { |customer| sentry_hub.current_scope.set_user( id: customer.customer_id, jid: iq.from.stripped.to_s diff --git a/test/test_command_list.rb b/test/test_command_list.rb index ebefdafdebab2c669dca7a622d3373302dd617c9..d8939f80a8eea6a1de9c4c1679e24065c5d94c77 100644 --- a/test/test_command_list.rb +++ b/test/test_command_list.rb @@ -8,22 +8,13 @@ CommandList::REDIS = Minitest::Mock.new class CommandListTest < Minitest::Test def test_for_no_customer - CommandList::Customer.expect( - :for_jid, - EMPromise.reject("not found"), - ["none"] - ) - assert_instance_of CommandList, CommandList.for("none").sync + assert_instance_of CommandList, CommandList.for(nil).sync end em :test_for_no_customer def test_for_unregistered - CommandList::Customer.expect( - :for_jid, - EMPromise.resolve(OpenStruct.new(registered?: false)), - ["unregistered"] - ) - assert_instance_of CommandList, CommandList.for("unregistered").sync + customer = OpenStruct.new(registered?: false) + assert_instance_of CommandList, CommandList.for(customer).sync end em :test_for_unregistered @@ -33,17 +24,13 @@ class CommandListTest < Minitest::Test EMPromise.resolve(nil), ["catapult_fwd-1"] ) - CommandList::Customer.expect( - :for_jid, - EMPromise.resolve(OpenStruct.new( - registered?: OpenStruct.new(phone: "1"), - payment_methods: EMPromise.resolve([]) - )), - ["registered"] + customer = OpenStruct.new( + registered?: OpenStruct.new(phone: "1"), + payment_methods: EMPromise.resolve([]) ) assert_equal( ["CommandList::Registered"], - CommandList.for("registered").sync + CommandList.for(customer).sync .class.ancestors.map(&:name).grep(/\ACommandList::/) ) end @@ -55,17 +42,13 @@ class CommandListTest < Minitest::Test EMPromise.resolve("tel:1"), ["catapult_fwd-1"] ) - CommandList::Customer.expect( - :for_jid, - EMPromise.resolve(OpenStruct.new( - registered?: OpenStruct.new(phone: "1"), - payment_methods: EMPromise.resolve([]) - )), - ["registered"] + customer = OpenStruct.new( + registered?: OpenStruct.new(phone: "1"), + payment_methods: EMPromise.resolve([]) ) assert_equal( CommandList::HAS_FORWARDING, - CommandList::HAS_FORWARDING & CommandList.for("registered").sync.to_a + CommandList::HAS_FORWARDING & CommandList.for(customer).sync.to_a ) end em :test_for_registered_with_fwd @@ -76,18 +59,14 @@ class CommandListTest < Minitest::Test EMPromise.resolve(nil), ["catapult_fwd-1"] ) - CommandList::Customer.expect( - :for_jid, - EMPromise.resolve(OpenStruct.new( - registered?: OpenStruct.new(phone: "1"), - plan_name: "test", - payment_methods: EMPromise.resolve([:boop]) - )), - ["registered"] + customer = OpenStruct.new( + registered?: OpenStruct.new(phone: "1"), + plan_name: "test", + payment_methods: EMPromise.resolve([:boop]) ) assert_equal( CommandList::HAS_CREDIT_CARD, - CommandList::HAS_CREDIT_CARD & CommandList.for("registered").sync.to_a + CommandList::HAS_CREDIT_CARD & CommandList.for(customer).sync.to_a ) end em :test_for_registered_with_credit_card @@ -98,18 +77,14 @@ class CommandListTest < Minitest::Test EMPromise.resolve(nil), ["catapult_fwd-1"] ) - CommandList::Customer.expect( - :for_jid, - EMPromise.resolve(OpenStruct.new( - registered?: OpenStruct.new(phone: "1"), - currency: :USD - )), - ["registered"] + customer = OpenStruct.new( + registered?: OpenStruct.new(phone: "1"), + currency: :USD ) assert_equal( CommandList::HAS_CURRENCY, - CommandList::HAS_CURRENCY & CommandList.for("registered").sync.to_a + CommandList::HAS_CURRENCY & CommandList.for(customer).sync.to_a ) end em :test_for_registered_with_currency diff --git a/test/test_customer.rb b/test/test_customer.rb index 79c008575defaf2253765cb462230a96474cc782..d41f88aafe2f4d13994b981ec54eb2b2c56f0be4 100644 --- a/test/test_customer.rb +++ b/test/test_customer.rb @@ -20,67 +20,6 @@ class SipAccount end class CustomerTest < Minitest::Test - def test_for_jid - Customer::REDIS.expect( - :get, - EMPromise.resolve(1), - ["jmp_customer_id-test@example.com"] - ) - Customer::DB.expect( - :query_defer, - EMPromise.resolve([{ balance: 1234, plan_name: "test_usd" }]), - [String, [1]] - ) - customer = Customer.for_jid("test@example.com").sync - assert_kind_of Customer, customer - assert_equal 1234, customer.balance - assert_equal "merchant_usd", customer.merchant_account - end - em :test_for_jid - - def test_for_jid_not_found - Customer::REDIS.expect( - :get, - EMPromise.resolve(nil), - ["jmp_customer_id-test2@example.com"] - ) - assert_raises do - Customer.for_jid("test2@example.com").sync - end - end - em :test_for_jid_not_found - - def test_for_customer_id_not_found - Customer::DB.expect( - :query_defer, - EMPromise.resolve([]), - [String, [7357]] - ) - customer = Customer.for_customer_id(7357).sync - assert_equal BigDecimal.new(0), customer.balance - end - em :test_for_customer_id_not_found - - def test_create - braintree_customer = Minitest::Mock.new - Customer::BRAINTREE.expect(:customer, braintree_customer) - braintree_customer.expect(:create, EMPromise.resolve( - OpenStruct.new(success?: true, customer: OpenStruct.new(id: "test")) - )) - Customer::REDIS.expect( - :msetnx, - EMPromise.resolve(1), - [ - "jmp_customer_id-test@example.com", "test", - "jmp_customer_jid-test", "test@example.com" - ] - ) - assert_kind_of Customer, Customer.create("test@example.com").sync - braintree_customer.verify - Customer::REDIS.verify - end - em :test_create - def test_bill_plan_activate CustomerPlan::DB.expect(:transaction, nil) do |&block| block.call diff --git a/test/test_customer_repo.rb b/test/test_customer_repo.rb new file mode 100644 index 0000000000000000000000000000000000000000..e92c3c7b99cda2a30080679b449c3231a1d2d4c4 --- /dev/null +++ b/test/test_customer_repo.rb @@ -0,0 +1,90 @@ +# frozen_string_literal: true + +require "test_helper" +require "customer_repo" + +class CustomerRepoTest < Minitest::Test + def mkrepo( + redis: Minitest::Mock.new, + db: Minitest::Mock.new, + braintree: Minitest::Mock.new + ) + CustomerRepo.new(redis: redis, db: db, braintree: braintree) + end + + def test_find_by_jid + redis = Minitest::Mock.new + db = Minitest::Mock.new + repo = mkrepo(redis: redis, db: db) + redis.expect( + :get, + EMPromise.resolve(1), + ["jmp_customer_id-test@example.com"] + ) + db.expect( + :query_defer, + EMPromise.resolve([{ balance: 1234, plan_name: "test_usd" }]), + [String, [1]] + ) + customer = repo.find_by_jid("test@example.com").sync + assert_kind_of Customer, customer + assert_equal 1234, customer.balance + assert_equal "merchant_usd", customer.merchant_account + assert_mock redis + assert_mock db + end + em :test_find_by_jid + + def test_find_by_jid_not_found + redis = Minitest::Mock.new + repo = mkrepo(redis: redis) + redis.expect( + :get, + EMPromise.resolve(nil), + ["jmp_customer_id-test2@example.com"] + ) + assert_raises do + repo.find_by_jid("test2@example.com").sync + end + assert_mock redis + end + em :test_find_by_jid_not_found + + def test_find_db_empty + db = Minitest::Mock.new + repo = mkrepo(db: db) + db.expect( + :query_defer, + EMPromise.resolve([]), + [String, [7357]] + ) + customer = repo.find(7357).sync + assert_equal BigDecimal.new(0), customer.balance + assert_mock db + end + em :test_find_db_empty + + def test_create + redis = Minitest::Mock.new + braintree = Minitest::Mock.new + repo = mkrepo(redis: redis, braintree: braintree) + braintree_customer = Minitest::Mock.new + braintree.expect(:customer, braintree_customer) + braintree_customer.expect(:create, EMPromise.resolve( + OpenStruct.new(success?: true, customer: OpenStruct.new(id: "test")) + )) + redis.expect( + :msetnx, + EMPromise.resolve(1), + [ + "jmp_customer_id-test@example.com", "test", + "jmp_customer_jid-test", "test@example.com" + ] + ) + assert_kind_of Customer, repo.create("test@example.com").sync + assert_mock braintree + assert_mock braintree_customer + assert_mock redis + end + em :test_create +end