From 7dbf0c5d39e9d9215b122ee9ec95a7650b62505a Mon Sep 17 00:00:00 2001 From: Phillip Davis Date: Tue, 29 Jul 2025 16:26:13 -0400 Subject: [PATCH] enhance AdminAction::NumberChange, can change sgx --- forms/admin_number_change.rb | 9 + lib/admin_actions/number_change.rb | 88 ++- lib/trivial_backend_sgx_repo.rb | 18 + test/test_admin_command.rb | 957 +++++++++++++++++++++++++++-- 4 files changed, 989 insertions(+), 83 deletions(-) diff --git a/forms/admin_number_change.rb b/forms/admin_number_change.rb index 3989c682bbe6321fbb32036fd6ea2d076b384a74..9d19ec4f214bb0e4c95b5bde3ce12f0d747dd418 100644 --- a/forms/admin_number_change.rb +++ b/forms/admin_number_change.rb @@ -15,3 +15,12 @@ field( label: "Should we delete the old number?", value: 0 ) + +field( + var: "new_backend", + type: "text-single", + label: "Backend to change to?", + description: + "Leave blank to keep current backend, or specify new backend JID", + value: "" +) diff --git a/lib/admin_actions/number_change.rb b/lib/admin_actions/number_change.rb index 18e90fc60e360e2af0ff259dbe9f8af26edf4d58..6e0be7296cbd88f75682f1723c742b84653c02b0 100644 --- a/lib/admin_actions/number_change.rb +++ b/lib/admin_actions/number_change.rb @@ -3,6 +3,7 @@ require "value_semantics/monkey_patched" require_relative "../admin_action" require_relative "../form_to_h" +require_relative "../utils" class AdminAction class NumberChange < AdminAction @@ -11,14 +12,17 @@ class AdminAction using FormToH def self.for(target_customer, reply:) + unless (registration = target_customer.registered?) + return EMPromise.reject("Customer not registered") + end + EMPromise.resolve( new( customer_id: target_customer.customer_id, - old_tel: target_customer.registered?.phone + old_backend: target_customer.sgx.strip!.to_s, + old_tel: registration.phone ) - ).then { |x| - reply.call(x.form).then(&x.method(:change)) - } + ).then { |x| reply.call(x.form).then(&x.method(:change)) } end def initialize(**bag) @@ -33,7 +37,12 @@ class AdminAction AdminAction::NumberChange.for( **@bag, **result.form.to_h - .reject { |_k, v| v == "nil" }.transform_keys(&:to_sym) + .reject { |_k, v| v == "nil" || v.to_s.empty? } + .tap { |form_h| + form_h["new_backend"] ||= @bag[:old_backend] + form_h["new_tel"] ||= @bag[:old_tel] + } + .transform_keys(&:to_sym) ) end end @@ -44,6 +53,12 @@ class AdminAction end } + UnknownBackend = Struct.new(:backend, :expected) { + def to_s + "Got unknown backend: #{backend}, expected one of #{expected}" + end + } + def customer_id @attributes[:customer_id] end @@ -56,36 +71,50 @@ class AdminAction @attributes[:new_tel] end + def old_backend + @attributes[:old_backend] + end + + def new_backend + @attributes[:new_backend] + end + def should_delete? ["1", "true"].include?(@attributes[:should_delete]) end def check_forward - EMPromise.all([ - check_noop, - check_exist - ]) + EMPromise.all([check_noop, check_cat_jid, check_backend]) end def forward - TrivialBackendSgxRepo.new(redis: REDIS).get(customer_id).then do |sgx| - EMPromise.all([ - REDIS.rename("catapult_fwd-#{old_tel}", "catapult_fwd-#{new_tel}"), - sgx.register!(new_tel), - should_delete? && first_time? ? disconnect_number : nil - ]).then { self } - end + EMPromise.all([ + new_tel != old_tel && change_catapult_fwd!, + TrivialBackendSgxRepo.new( + redis: REDIS + ).put(customer_id, new_backend, new_tel), + should_disconnect_old_number? && disconnect_number + ]).then { self } + end + + def change_catapult_fwd! + REDIS.rename("catapult_fwd-#{old_tel}", "catapult_fwd-#{new_tel}") end def to_reverse with( old_tel: new_tel, - new_tel: old_tel + new_tel: old_tel, + old_backend: new_backend, + new_backend: old_backend ) end def to_s - "number_change(#{customer_id}): #{old_tel} -> #{new_tel}#{delete_warning}" + base = "Change Number\n" + base += " [move backend?]: #{old_backend} -> #{new_backend}\n" + base += " [change number?]: #{old_tel} -> #{new_tel}" + base + delete_warning end protected @@ -107,14 +136,33 @@ class AdminAction end def check_noop - EMPromise.reject(NoOp.new) if new_tel == old_tel + if new_tel == old_tel && new_backend == old_backend + EMPromise.reject(NoOp.new) + else + EMPromise.resolve(nil) + end end - def check_exist + def check_cat_jid cat_jid = "catapult_jid-#{old_tel}" REDIS.exists(cat_jid).then { |v| EMPromise.reject(NilKey.new(cat_jid)) unless v == 1 } end + + def check_backend + unless (expected = CONFIG[:sgx_creds].keys.map( + &:to_s + ).push(CONFIG[:sgx])).include?(new_backend) + EMPromise.reject(UnknownBackend.new(new_backend, expected)) + end + end + + def should_disconnect_old_number? + should_delete? && + first_time? && + old_tel != new_tel && + old_backend == CONFIG[:sgx] + end end end diff --git a/lib/trivial_backend_sgx_repo.rb b/lib/trivial_backend_sgx_repo.rb index 72e7c5e236018ad204e89ef636439ce5b88faee1..f3c88253f7283f32cc0e8ae7bc5dad89fce5b540 100644 --- a/lib/trivial_backend_sgx_repo.rb +++ b/lib/trivial_backend_sgx_repo.rb @@ -34,8 +34,26 @@ class TrivialBackendSgxRepo end end + def put(cid, target_sgx, target_num) + get(cid).then(&:deregister!).then { + put_jid(cid, target_sgx) + }.then { + get(cid) + }.then { |sgx| + sgx.register!(target_num) + } + end + protected + def put_jid(cid, target_sgx) + if target_sgx == CONFIG[:sgx] + @redis.del("jmp_customer_backend_sgx-#{cid}") + else + @redis.set("jmp_customer_backend_sgx-#{cid}", target_sgx) + end + end + def default_jid_creds EMPromise.resolve([@jid, @creds]) end diff --git a/test/test_admin_command.rb b/test/test_admin_command.rb index 41aee4b24585f7e8509fc30af2aba94efbce58a9..6d8ec8110287fe9193a9a77e045be1e800b77153 100644 --- a/test/test_admin_command.rb +++ b/test/test_admin_command.rb @@ -24,6 +24,85 @@ class AdminCommandTest < Minitest::Test assert iq.note_text = note_text if note_text end + def assert_change_number_form(iq) + assert_equal( + iq.form.field("new_tel")&.type, + "text-single" + ) + assert_equal( + iq.form.field("new_backend")&.type, + "text-single" + ) + assert_equal( + iq.form.field("should_delete")&.type, + "boolean" + ) + end + + def change_number_form( + new_tel: nil, + new_backend: nil, + should_delete: false, + from: "test@example.com" + ) + iq = Blather::Stanza::Iq::Command.new + iq.form.fields = [ + { var: "new_tel", value: new_tel }, + { var: "should_delete", value: should_delete.to_s }, + { var: "new_backend", value: new_backend } + ] + iq.from = from + iq + end + + def setup_bandwidth_tn_repo_mock( + should_disconnect: false, + old_tel: nil, + new_tel: nil + ) + mock_repo = Minitest::Mock.new + if should_disconnect + expected_order_name = "cust test swap to #{new_tel}" + mock_repo.expect( + :disconnect, + EMPromise.resolve(nil), + [old_tel, expected_order_name] + ) + end + mock_repo + end + + def assert_ibr_register_form( + iq, + tel, + nick: "test_bw_account", + username: "test_bw_user", + password: "test_bw_password", + from: "customer_test@component", + target_backend_sgx: "sgx" + ) + assert iq.is_a?(Blather::Stanza::Iq::IBR) + assert_equal iq.type, :set + assert_equal iq.nick, nick + assert_equal iq.username, username + assert_equal iq.password, password + assert_equal iq.phone, tel + assert_equal iq.from, from + assert_equal iq.to, target_backend_sgx + end + + def assert_ibr_deregister_form( + iq, + from: "customer_test@component", + target_backend_sgx: "sgx" + ) + assert iq.is_a?(Blather::Stanza::Iq::IBR) + assert_equal iq.type, :set + assert_equal iq.to, target_backend_sgx + assert_equal iq.from, from + assert iq.remove? + end + def admin_command(tel="+15556667777", registered: OpenStruct.new(phone: tel)) sgx = Minitest::Mock.new(OpenStruct.new( registered?: registered @@ -352,24 +431,42 @@ class AdminCommandTest < Minitest::Test end em :test_action_cancel_account_keep_number - def test_change_num_default_sgx - # Same as default given by `sgx.registered?` returned by `admin_command` - old_tel = "+15556667777" - new_tel = "+12222222222" + def test_change_num_for_unregistered_customer + execute_command { |exe| + sgx = Minitest::Mock.new(OpenStruct.new( + registered?: nil, + jid: Blather::JID.new(CONFIG[:sgx]) # Uses Bandwidth backend + )) + target_customer = customer(sgx: sgx) + admin = AdminCommand.for(target_customer, exe.customer_repo) - admin_number_change_form = Blather::Stanza::Iq::Command.new - admin_number_change_form.form.fields = [ - { var: "new_tel", value: new_tel }, - { var: "should_delete", value: false } - ] - admin_number_change_form.from = "test@example.com" + error = assert_raises( + "number change for unregistered customer should raise" + ) { + admin.action_number_change.sync + } + + assert_equal error.to_s, "Customer not registered" + } + end + em :test_change_num_for_unregistered_customer + + def test_change_num_same_num_same_backend + # Same as default given by `sgx.registered?` returned by `admin_command` + tel = "+15556667777" execute_command { |exe| - sgx, admin = admin_command + sgx = Minitest::Mock.new(OpenStruct.new( + registered?: OpenStruct.new(phone: tel), + jid: Blather::JID.new(CONFIG[:sgx]) # Uses Bandwidth backend + )) + + target_customer = customer(sgx: sgx) + admin = AdminCommand.for(target_customer, exe.customer_repo) exe.customer_repo.expect( :find_by_jid, - EMPromise.resolve(customer(sgx: sgx)), + EMPromise.resolve(target_customer), [Matching.new do |jid| assert jid.is_a? Blather::JID assert_equal jid.stripped.to_s, "test@example.com" @@ -379,80 +476,814 @@ class AdminCommandTest < Minitest::Test AdminAction::NumberChange::REDIS.expect( :exists, EMPromise.resolve(1), - ["catapult_jid-#{old_tel}"] + ["catapult_jid-#{tel}"] ) - AdminAction::NumberChange::REDIS.expect( - :get, - EMPromise.resolve(nil), # Default SGX creds - ["jmp_customer_backend_sgx-test"] + Command::COMMAND_MANAGER.expect( + :write, + EMPromise.resolve(change_number_form( + new_tel: tel, + new_backend: "sgx" + )), + [Matching.new do |iq| + assert_undoable_form(iq) + assert_change_number_form(iq) + end] ) - AdminAction::NumberChange::REDIS.expect( - :rename, - EMPromise.resolve(nil), # Default SGX creds - ["catapult_fwd-#{old_tel}", "catapult_fwd-#{new_tel}"] - ) + error = admin.action_number_change + .catch_only(AdminAction::NoOp) { |e| e }.sync - BackendSgx::REDIS.expect( - :set, - EMPromise.resolve(nil), # Default SGX creds - ["catapult_jid-#{new_tel}", "customer_test@component"] - ) + assert error.is_a?(AdminAction::NoOp) + assert_mock Command::COMMAND_MANAGER + assert_mock exe.customer_repo + assert_mock AdminAction::NumberChange::REDIS + assert_mock sgx + } + end + em :test_change_num_same_num_same_backend - expected_xadd_args = { - customer_id: "test", - old_tel: old_tel, - new_tel: new_tel, - should_delete: nil, - actor_id: "test", - class: "NumberChange", - direction: :forward - } + def test_change_num_blank_num_blank_backend + # Same as default given by `sgx.registered?` returned by `admin_command` + tel = "+15556667777" - AdminActionRepo::REDIS.expect( - :xadd, - EMPromise.resolve(nil) - ) do |admin_actions, star, **xadd_args| - assert_equal admin_actions, "admin_actions" - assert_equal star, "*" + execute_command { |exe| + sgx = Minitest::Mock.new(OpenStruct.new( + registered?: OpenStruct.new(phone: tel), + jid: Blather::JID.new(CONFIG[:sgx]) # Uses Bandwidth backend + )) - xadd_args.each do |k, v| - assert_equal v, expected_xadd_args[k] - end - end + target_customer = customer(sgx: sgx) - # Make sure the IBR record was sent off for - # the new number. - BackendSgx::IQ_MANAGER.expect( - :write, - EMPromise.resolve(nil), - [Matching.new do |iq| - assert_equal iq.nick, "test_bw_account" - assert_equal iq.username, "test_bw_user" - assert_equal iq.password, "test_bw_password" - assert_equal iq.phone, new_tel + admin = AdminCommand.for(target_customer, exe.customer_repo) + + exe.customer_repo.expect( + :find_by_jid, + EMPromise.resolve(target_customer), + [Matching.new do |jid| + assert jid.is_a? Blather::JID + assert_equal jid.stripped.to_s, "test@example.com" end] ) + AdminAction::NumberChange::REDIS.expect( + :exists, + EMPromise.resolve(1), + ["catapult_jid-#{tel}"] + ) + Command::COMMAND_MANAGER.expect( :write, - EMPromise.resolve(admin_number_change_form), + EMPromise.resolve(change_number_form), [Matching.new do |iq| assert_undoable_form(iq) - assert iq.form.field("new_tel") - assert iq.form.field("should_delete") + assert_change_number_form(iq) end] ) - admin.action_number_change.sync + error = admin.action_number_change + .catch_only(AdminAction::NoOp) { |e| e }.sync + assert error.is_a?(AdminAction::NoOp) assert_mock Command::COMMAND_MANAGER assert_mock exe.customer_repo assert_mock AdminAction::NumberChange::REDIS - assert_mock AdminActionRepo::REDIS - assert_mock BackendSgx::IQ_MANAGER + assert_mock sgx } end - em :test_change_num_default_sgx + em :test_change_num_blank_num_blank_backend + + def test_change_num_different_num_same_backend + old_tel = "+15556667777" + new_tel = "+12222222222" + bandwidth_tn_repo_mock = setup_bandwidth_tn_repo_mock( + should_disconnect: false + ) + + BandwidthTnRepo.stub :new, bandwidth_tn_repo_mock do + execute_command { |exe| + sgx = Minitest::Mock.new(OpenStruct.new( + registered?: OpenStruct.new(phone: old_tel), + jid: Blather::JID.new(CONFIG[:sgx]) + )) + + target_customer = customer(sgx: sgx) + admin = AdminCommand.for(target_customer, exe.customer_repo) + + exe.customer_repo.expect( + :find_by_jid, + EMPromise.resolve(target_customer), + [Matching.new do |jid| + assert jid.is_a? Blather::JID + assert_equal jid.stripped.to_s, "test@example.com" + end] + ) + + AdminAction::NumberChange::REDIS.expect( + :rename, + EMPromise.resolve(nil), + ["catapult_fwd-#{old_tel}", "catapult_fwd-#{new_tel}"] + ) + + AdminAction::NumberChange::REDIS.expect( + :get, + EMPromise.resolve(nil), + ["jmp_customer_backend_sgx-test"] + ) + AdminAction::NumberChange::REDIS.expect( + :get, + EMPromise.resolve(nil), + ["jmp_customer_backend_sgx-test"] + ) + + BackendSgx::IQ_MANAGER.expect( + :write, + EMPromise.resolve(nil) + ) do |iq| + assert_ibr_deregister_form(iq) + end + + BackendSgx::IQ_MANAGER.expect( + :write, + EMPromise.resolve(nil) + ) do |iq| + assert_ibr_register_form( + iq, + "+12222222222" + ) + end + + AdminAction::NumberChange::REDIS.expect( + :del, + EMPromise.resolve(nil), + ["jmp_customer_backend_sgx-#{target_customer.customer_id}"] + ) + + BackendSgx::REDIS.expect( + :set, + EMPromise.resolve(nil), + ["catapult_jid-#{new_tel}", "customer_test@component"] + ) + + AdminAction::NumberChange::REDIS.expect( + :exists, + EMPromise.resolve(1), + ["catapult_jid-#{old_tel}"] + ) + + Command::COMMAND_MANAGER.expect( + :write, + EMPromise.resolve(change_number_form( + new_tel: "+12222222222" + )) + ) do |iq| + assert_undoable_form(iq) + assert_change_number_form(iq) + end + + expected_xadd_args = { + customer_id: "test", + old_tel: old_tel, + new_tel: new_tel, + should_delete: nil, + actor_id: "test", + class: "NumberChange", + direction: :forward + }.freeze + + AdminActionRepo::REDIS.expect( + :xadd, + EMPromise.resolve(nil) + ) do |admin_actions, star, **xadd_args| + assert_equal admin_actions, "admin_actions" + assert_equal star, "*" + + xadd_args.each do |k, v| + assert_equal v, expected_xadd_args[k] + end + end + + result = admin.action_number_change.sync + + slug, backend_report, number_report = result.lines + + assert_equal slug.strip, "Action : Change Number" + assert_equal( + backend_report.strip, + "[move backend?]: sgx -> sgx" + ) + assert_equal( + number_report.strip, + "[change number?]: +15556667777 -> +12222222222" + ) + + assert_mock Command::COMMAND_MANAGER + assert_mock exe.customer_repo + assert_mock AdminAction::NumberChange::REDIS + assert_mock AdminActionRepo::REDIS + assert_mock BackendSgx::REDIS + assert_mock BackendSgx::IQ_MANAGER + assert_mock bandwidth_tn_repo_mock + assert_mock sgx + } + end + end + em :test_change_num_different_num_same_backend + + def test_change_num_same_num_different_backend + target_backend_sgx = "route_value" + + bandwidth_tn_repo_mock = setup_bandwidth_tn_repo_mock( + should_disconnect: false + ) + + BandwidthTnRepo.stub :new, bandwidth_tn_repo_mock do + execute_command { |exe| + sgx = Minitest::Mock.new(OpenStruct.new( + registered?: OpenStruct.new(phone: "+15556667777"), + jid: Blather::JID.new(CONFIG[:sgx]) + )) + + target_customer = customer(sgx: sgx) + admin = AdminCommand.for(target_customer, exe.customer_repo) + + exe.customer_repo.expect( + :find_by_jid, + EMPromise.resolve(target_customer), + [Matching.new do |jid| + assert jid.is_a? Blather::JID + assert_equal jid.stripped.to_s, "test@example.com" + end] + ) + + Command::COMMAND_MANAGER.expect( + :write, + EMPromise.resolve( + change_number_form( + new_backend: target_backend_sgx + ) + ), + [Matching.new do |iq| + assert_undoable_form(iq) + assert_change_number_form(iq) + end] + ) + + BackendSgx::IQ_MANAGER.expect( + :write, + EMPromise.resolve(nil) + ) do |iq| + assert_ibr_deregister_form(iq) + end + + BackendSgx::IQ_MANAGER.expect( + :write, + EMPromise.resolve(nil) + ) do |iq| + assert_ibr_register_form( + iq, + "+15556667777", + target_backend_sgx: target_backend_sgx, + nick: "test_sgx_account", + username: "test_sgx_user", + password: "test_sgx_password" + ) + end + + AdminAction::NumberChange::REDIS.expect( + :get, + EMPromise.resolve(nil), + ["jmp_customer_backend_sgx-#{target_customer.customer_id}"] + ) + + AdminAction::NumberChange::REDIS.expect( + :set, + EMPromise.resolve(nil), + [ + "jmp_customer_backend_sgx-#{target_customer.customer_id}", + "route_value" + ] + ) + + AdminAction::NumberChange::REDIS.expect( + :get, + EMPromise.resolve("route_value"), + ["jmp_customer_backend_sgx-#{target_customer.customer_id}"] + ) + + AdminAction::NumberChange::REDIS.expect( + :exists, + EMPromise.resolve(1), + ["catapult_jid-+15556667777"] + ) + + BackendSgx::REDIS.expect( + :set, + EMPromise.resolve(nil), + [ + "catapult_jid-+15556667777", + "customer_test@component" + ] + ) + + expected_xadd_args = { + old_tel: "+15556667777", + new_tel: "+15556667777", + old_backend: "sgx", + new_backend: "route_value", + should_delete: nil, + actor_id: "test", + class: "NumberChange", + direction: :forward + }.freeze + + AdminActionRepo::REDIS.expect( + :xadd, + EMPromise.resolve(nil) + ) do |admin_actions, star, **xadd_args| + assert_equal admin_actions, "admin_actions" + assert_equal star, "*" + + xadd_args.each do |k, v| + assert_equal v, expected_xadd_args[k] + end + end + + result = admin.action_number_change.sync + + slug, backend_report, number_report = result.lines + + assert_equal slug.strip, "Action : Change Number" + assert_equal( + backend_report.strip, + "[move backend?]: sgx -> route_value" + ) + assert_equal( + number_report.strip, + "[change number?]: +15556667777 -> +15556667777" + ) + + assert_mock Command::COMMAND_MANAGER + assert_mock exe.customer_repo + assert_mock AdminAction::NumberChange::REDIS + assert_mock AdminActionRepo::REDIS + assert_mock BackendSgx::REDIS + assert_mock BackendSgx::IQ_MANAGER + assert_mock bandwidth_tn_repo_mock + assert_mock sgx + } + end + end + em :test_change_num_same_num_different_backend + + def test_change_num_different_num_different_backend + old_tel = "+15556667777" + new_tel = "+12222222222" + target_backend_sgx = "route_value" + + bandwidth_tn_repo_mock = setup_bandwidth_tn_repo_mock( + should_disconnect: false + ) + + BandwidthTnRepo.stub :new, bandwidth_tn_repo_mock do + execute_command { |exe| + sgx = Minitest::Mock.new(OpenStruct.new( + registered?: OpenStruct.new(phone: old_tel), + jid: Blather::JID.new(CONFIG[:sgx]) + )) + + target_customer = customer(sgx: sgx) + admin = AdminCommand.for(target_customer, exe.customer_repo) + + exe.customer_repo.expect( + :find_by_jid, + EMPromise.resolve(target_customer), + [Matching.new do |jid| + assert jid.is_a? Blather::JID + assert_equal jid.stripped.to_s, "test@example.com" + end] + ) + + Command::COMMAND_MANAGER.expect( + :write, + EMPromise.resolve( + change_number_form( + new_tel: new_tel, + new_backend: target_backend_sgx + ) + ), + [Matching.new do |iq| + assert_undoable_form(iq) + assert_change_number_form(iq) + end] + ) + + BackendSgx::IQ_MANAGER.expect( + :write, + EMPromise.resolve(nil) + ) do |iq| + assert_ibr_deregister_form(iq) + end + + BackendSgx::IQ_MANAGER.expect( + :write, + EMPromise.resolve(nil) + ) do |iq| + assert_ibr_register_form( + iq, + "+12222222222", + target_backend_sgx: target_backend_sgx, + nick: "test_sgx_account", + username: "test_sgx_user", + password: "test_sgx_password" + ) + end + + AdminAction::NumberChange::REDIS.expect( + :exists, + EMPromise.resolve(1), + ["catapult_jid-#{old_tel}"] + ) + + AdminAction::NumberChange::REDIS.expect( + :rename, + EMPromise.resolve(nil), + ["catapult_fwd-#{old_tel}", "catapult_fwd-#{new_tel}"] + ) + + AdminAction::NumberChange::REDIS.expect( + :get, + EMPromise.resolve(nil), + ["jmp_customer_backend_sgx-test"] + ) + + AdminAction::NumberChange::REDIS.expect( + :set, + EMPromise.resolve(nil), + [ + "jmp_customer_backend_sgx-#{target_customer.customer_id}", + "route_value" + ] + ) + + AdminAction::NumberChange::REDIS.expect( + :get, + EMPromise.resolve("route_value"), + ["jmp_customer_backend_sgx-test"] + ) + + BackendSgx::REDIS.expect( + :set, + EMPromise.resolve(nil), + [ + "catapult_jid-#{new_tel}", + "customer_test@component" + ] + ) + + expected_xadd_args = { + old_tel: "+15556667777", + new_tel: "+12222222222", + target_backend_sgx: "route_value", + should_delete: nil, + actor_id: "test", + class: "NumberChange", + direction: :forward + }.freeze + + AdminActionRepo::REDIS.expect( + :xadd, + EMPromise.resolve(nil) + ) do |admin_actions, star, **xadd_args| + assert_equal admin_actions, "admin_actions" + assert_equal star, "*" + + xadd_args.each do |k, v| + assert_equal v, expected_xadd_args[k] + end + end + + result = admin.action_number_change.sync + + slug, backend_report, number_report = result.lines + + assert_equal slug.strip, "Action : Change Number" + assert_equal( + backend_report.strip, + "[move backend?]: sgx -> route_value" + ) + assert_equal( + number_report.strip, + "[change number?]: +15556667777 -> +12222222222" + ) + + assert_mock Command::COMMAND_MANAGER + assert_mock exe.customer_repo + assert_mock AdminAction::NumberChange::REDIS + assert_mock AdminActionRepo::REDIS + assert_mock BackendSgx::REDIS + assert_mock BackendSgx::IQ_MANAGER + assert_mock bandwidth_tn_repo_mock + assert_mock sgx + } + end + end + em :test_change_num_different_num_different_backend + + def test_change_num_bandwidth_backend_with_delete_should_disconnect + old_tel = "+15556667777" + new_tel = "+12222222222" + + bandwidth_tn_repo_mock = setup_bandwidth_tn_repo_mock( + should_disconnect: true, + old_tel: old_tel, + new_tel: new_tel + ) + + BandwidthTnRepo.stub :new, bandwidth_tn_repo_mock do + execute_command { |exe| + sgx = Minitest::Mock.new(OpenStruct.new( + registered?: OpenStruct.new(phone: old_tel), + jid: Blather::JID.new(CONFIG[:sgx]) + )) + + target_customer = customer(sgx: sgx) + admin = AdminCommand.for(target_customer, exe.customer_repo) + + exe.customer_repo.expect( + :find_by_jid, + EMPromise.resolve(customer(sgx: sgx)), + [Matching.new do |jid| + assert jid.is_a? Blather::JID + assert_equal jid.stripped.to_s, "test@example.com" + end] + ) + + AdminAction::NumberChange::REDIS.expect( + :exists, + EMPromise.resolve(1), + ["catapult_jid-#{old_tel}"] + ) + + AdminAction::NumberChange::REDIS.expect( + :rename, + EMPromise.resolve(nil), + ["catapult_fwd-#{old_tel}", "catapult_fwd-#{new_tel}"] + ) + + expected_xadd_args = { + customer_id: "test", + old_tel: old_tel, + new_tel: new_tel, + should_delete: "true", + actor_id: "test", + class: "NumberChange", + direction: :forward + }.freeze + + AdminActionRepo::REDIS.expect( + :xadd, + EMPromise.resolve(nil) + ) do |admin_actions, star, **xadd_args| + assert_equal admin_actions, "admin_actions" + assert_equal star, "*" + + xadd_args.each do |k, v| + assert_equal v, expected_xadd_args[k] + end + end + + Command::COMMAND_MANAGER.expect( + :write, + EMPromise.resolve( + change_number_form( + new_tel: new_tel, + should_delete: true + ) + ), + [Matching.new do |iq| + assert_undoable_form(iq) + assert_change_number_form(iq) + end] + ) + + AdminAction::NumberChange::REDIS.expect( + :get, + EMPromise.resolve(nil), + ["jmp_customer_backend_sgx-test"] + ) + + AdminAction::NumberChange::REDIS.expect( + :del, + EMPromise.resolve(nil), + ["jmp_customer_backend_sgx-test"] + ) + + AdminAction::NumberChange::REDIS.expect( + :get, + EMPromise.resolve(nil), + ["jmp_customer_backend_sgx-test"] + ) + + BackendSgx::REDIS.expect( + :set, + EMPromise.resolve(nil), + [ + "catapult_jid-#{new_tel}", + "customer_test@component" + ] + ) + + BackendSgx::IQ_MANAGER.expect( + :write, + EMPromise.resolve(nil) + ) do |iq| + assert_ibr_deregister_form(iq) + end + + BackendSgx::IQ_MANAGER.expect( + :write, + EMPromise.resolve(nil) + ) do |iq| + assert_ibr_register_form( + iq, + new_tel, + nick: "test_bw_account", + username: "test_bw_user", + password: "test_bw_password" + ) + end + + result = admin.action_number_change.sync + + slug, backend_report, number_report = result.lines + + assert_equal slug.strip, "Action : Change Number" + assert_equal( + backend_report.strip, + "[move backend?]: sgx -> sgx" + ) + assert_equal( + number_report.strip, + "[change number?]: #{old_tel} -> #{new_tel}" + ) + + assert_mock Command::COMMAND_MANAGER + assert_mock exe.customer_repo + assert_mock AdminAction::NumberChange::REDIS + assert_mock AdminActionRepo::REDIS + assert_mock BackendSgx::REDIS + assert_mock BackendSgx::IQ_MANAGER + assert_mock bandwidth_tn_repo_mock + assert_mock sgx + } + end + end + em :test_change_num_bandwidth_backend_with_delete_should_disconnect + + def test_change_num_non_bw_backend_should_not_disconnect + old_tel = "+15556667777" + new_tel = "+12222222222" + non_bw_backend = "route_value" + + bandwidth_tn_repo_mock = setup_bandwidth_tn_repo_mock( + should_disconnect: false + ) + + BandwidthTnRepo.stub :new, bandwidth_tn_repo_mock do + execute_command { |exe| + Command::COMMAND_MANAGER.expect( + :write, + EMPromise.resolve(change_number_form(new_tel: new_tel)), + [Matching.new do |iq| + assert_undoable_form(iq) + assert_change_number_form(iq) + end] + ) + + non_bandwidth_sgx = Minitest::Mock.new(OpenStruct.new( + registered?: OpenStruct.new(phone: old_tel), + jid: Blather::JID.new(non_bw_backend) + )) + + target_customer = customer(sgx: non_bandwidth_sgx) + admin = AdminCommand.for(target_customer, exe.customer_repo) + + exe.customer_repo.expect( + :find_by_jid, + EMPromise.resolve(target_customer), + [Blather::JID.new("test@example.com")] + ) + + AdminAction::NumberChange::REDIS.expect( + :exists, + EMPromise.resolve(1), + ["catapult_jid-+15556667777"] + ) + + AdminAction::NumberChange::REDIS.expect( + :rename, + EMPromise.resolve(nil), + ["catapult_fwd-#{old_tel}", "catapult_fwd-#{new_tel}"] + ) + + AdminAction::NumberChange::REDIS.expect( + :get, + EMPromise.resolve("route_value"), + ["jmp_customer_backend_sgx-#{target_customer.customer_id}"] + ) + + BackendSgx::IQ_MANAGER.expect( + :write, + EMPromise.resolve(nil) + ) do |iq| + assert_ibr_deregister_form( + iq, + target_backend_sgx: "route_value" + ) + end + + AdminAction::NumberChange::REDIS.expect( + :set, + EMPromise.resolve(nil), + [ + "jmp_customer_backend_sgx-#{target_customer.customer_id}", + "route_value" + ] + ) + + AdminAction::NumberChange::REDIS.expect( + :get, + EMPromise.resolve("route_value"), + ["jmp_customer_backend_sgx-#{target_customer.customer_id}"] + ) + + BackendSgx::IQ_MANAGER.expect( + :write, + EMPromise.resolve(nil) + ) do |iq| + assert_ibr_register_form( + iq, + "+12222222222", + nick: "test_sgx_account", + username: "test_sgx_user", + password: "test_sgx_password", + target_backend_sgx: "route_value" + ) + end + + BackendSgx::REDIS.expect( + :set, + EMPromise.resolve(nil), + ["catapult_jid-+12222222222", "customer_test@component"] + ) + + expected_xadd_args = { + customer_id: "test", + old_tel: old_tel, + new_tel: new_tel, + should_delete: "true", + actor_id: "test", + class: "NumberChange", + direction: :forward + }.freeze + + AdminActionRepo::REDIS.expect( + :xadd, + EMPromise.resolve(nil) + ) do |admin_actions, star, **xadd_args| + assert_equal admin_actions, "admin_actions" + assert_equal star, "*" + + xadd_args.each do |k, v| + assert_equal v, expected_xadd_args[k] + end + end + + result = admin.action_number_change.sync + + slug, backend_report, number_report = result.lines + + assert_equal slug.strip, "Action : Change Number" + assert_equal( + backend_report.strip, + "[move backend?]: #{non_bw_backend} -> #{non_bw_backend}" + ) + assert_equal( + number_report.strip, + "[change number?]: +15556667777 -> +12222222222" + ) + + assert_mock Command::COMMAND_MANAGER + assert_mock exe.customer_repo + assert_mock AdminAction::NumberChange::REDIS + assert_mock AdminActionRepo::REDIS + assert_mock BackendSgx::REDIS + assert_mock BackendSgx::IQ_MANAGER + assert_mock bandwidth_tn_repo_mock + assert_mock non_bandwidth_sgx + } + end + end + em :test_change_num_non_bw_backend_should_not_disconnect end