M .rubocop.yml => .rubocop.yml +4 -0
@@ 14,6 14,10 @@ Metrics/MethodLength:
Exclude:
- test/*
+Metrics/BlockLength:
+ Exclude:
+ - test/*
+
Metrics/ClassLength:
Exclude:
- test/*
M Gemfile => Gemfile +2 -2
@@ 10,12 10,12 @@ gem "em-hiredis"
gem "em-http-request"
gem "em-pg-client", git: "https://github.com/royaltm/ruby-em-pg-client"
gem "em-synchrony"
-gem "em_promise.rb", "~> 0.0.2"
+gem "em_promise.rb", "~> 0.0.3"
gem "eventmachine"
gem "money-open-exchange-rates"
gem "ougai"
gem "ruby-bandwidth-iris"
-gem "sentry-ruby"
+gem "sentry-ruby", "<= 4.3.1"
gem "statsd-instrument", git: "https://github.com/singpolyma/statsd-instrument.git", branch: "graphite"
gem "value_semantics", git: "https://github.com/singpolyma/value_semantics"
M lib/bandwidth_tn_order.rb => lib/bandwidth_tn_order.rb +2 -2
@@ 8,7 8,7 @@ require_relative "./catapult"
class BandwidthTNOrder
def self.get(id)
- EM.promise_fiber do
+ EMPromise.resolve(nil).then do
self.for(BandwidthIris::Order.get_order_response(
# https://github.com/Bandwidth/ruby-bandwidth-iris/issues/44
BandwidthIris::Client.new,
@@ 18,7 18,7 @@ class BandwidthTNOrder
end
def self.create(tel, name: "sgx-jmp order #{tel}")
- EM.promise_fiber do
+ EMPromise.resolve(nil).then do
Received.new(BandwidthIris::Order.create(
name: name,
site_id: CONFIG[:bandwidth_site],
A lib/command.rb => lib/command.rb +158 -0
@@ 0,0 1,158 @@
+# frozen_string_literal: true
+
+require "sentry-ruby"
+require "statsd-instrument"
+
+require_relative "customer_repo"
+
+class Command
+ def self.execution
+ Thread.current[:execution]
+ end
+
+ def self.reply(stanza=nil, &blk)
+ execution.reply(stanza, &blk)
+ end
+
+ def self.finish(*args, **kwargs, &blk)
+ execution.finish(*args, **kwargs, &blk)
+ end
+
+ def self.customer
+ execution.customer
+ end
+
+ def self.log
+ execution.log
+ end
+
+ class Execution
+ class FinalStanza
+ attr_reader :stanza
+
+ def initialize(stanza)
+ @stanza = stanza
+ end
+ end
+
+ attr_reader :customer_repo, :log, :iq
+
+ def initialize(customer_repo, blather, format_error, iq)
+ @customer_repo = customer_repo
+ @blather = blather
+ @format_error = format_error
+ @iq = iq
+ @log = LOG.child(node: iq.node)
+ end
+
+ def execute
+ StatsD.increment("command", tags: ["node:#{iq.node}"])
+ EMPromise.resolve(nil).then {
+ Thread.current[:execution] = self
+ sentry_hub
+ catch_after(yield self)
+ }.catch(&method(:panic))
+ end
+
+ def reply(stanza=nil)
+ stanza ||= iq.reply.tap do |reply|
+ reply.status = :executing
+ end
+ yield stanza if block_given?
+ COMMAND_MANAGER.write(stanza).then do |new_iq|
+ @iq = new_iq
+ end
+ end
+
+ def finish(text=nil, type: :info, status: :completed)
+ reply = @iq.reply
+ reply.status = status
+ yield reply if block_given?
+ if text
+ reply.note_type = type
+ reply.note_text = text
+ end
+ EMPromise.reject(FinalStanza.new(reply))
+ end
+
+ def sentry_hub
+ return @sentry_hub if @sentry_hub
+
+ # Stored on Fiber-local in 4.3.1 and earlier
+ # https://github.com/getsentry/sentry-ruby/issues/1495
+ @sentry_hub = Sentry.get_current_hub
+ raise "Sentry.init has not been called" unless @sentry_hub
+
+ @sentry_hub.push_scope
+ @sentry_hub.current_scope.clear_breadcrumbs
+ @sentry_hub.current_scope.set_transaction_name(@iq.node)
+ @sentry_hub.current_scope.set_user(jid: @iq.from.stripped.to_s)
+ @sentry_hub
+ end
+
+ def customer
+ @customer ||= @customer_repo.find_by_jid(@iq.from.stripped).then do |c|
+ sentry_hub.current_scope.set_user(
+ id: c.customer_id,
+ jid: @iq.from.stripped
+ )
+ c
+ end
+ end
+
+ protected
+
+ def catch_after(promise)
+ promise.catch_only(FinalStanza) { |e|
+ @blather << e.stanza
+ }.catch do |e|
+ log_error(e)
+ finish(
+ @format_error.call(e), type: :error
+ ).catch_only(FinalStanza) do |to_send|
+ @blather << to_send.stanza
+ end
+ end
+ end
+
+ def log_error(e)
+ @log.error(
+ "Error raised during #{iq.node}: #{e.class}",
+ e
+ )
+ if e.is_a?(::Exception)
+ sentry_hub.capture_exception(e)
+ else
+ sentry_hub.capture_message(e.to_s)
+ end
+ end
+ end
+
+ attr_reader :node, :name
+
+ def initialize(
+ node,
+ name,
+ list_for: ->(tel:, **) { !!tel },
+ format_error: ->(e) { e.respond_to?(:message) ? e.message : e.to_s },
+ &blk
+ )
+ @node = node
+ @name = name
+ @list_for = list_for
+ @format_error = format_error
+ @blk = blk
+ end
+
+ def register(blather)
+ blather.command(:execute?, node: @node, sessionid: nil) do |iq|
+ customer_repo = CustomerRepo.new
+ Execution.new(customer_repo, blather, @format_error, iq).execute(&@blk)
+ end
+ self
+ end
+
+ def list_for?(**kwargs)
+ @list_for.call(**kwargs)
+ end
+end
M lib/command_list.rb => lib/command_list.rb +22 -51
@@ 3,65 3,36 @@
class CommandList
include Enumerable
- def self.for(customer)
- EMPromise.resolve(customer&.registered?).catch { nil }.then do |reg|
- next Registered.for(customer, reg.phone) if reg
- CommandList.new
- end
+ def self.register(command)
+ @commands ||= []
+ @commands << command
end
- def each
- yield node: "jabber:iq:register", name: "Register"
- end
-
- class Registered < CommandList
- def self.for(customer, tel)
- EMPromise.all([
- REDIS.get("catapult_fwd-#{tel}"),
- customer.plan_name ? customer.payment_methods : []
- ]).then do |(fwd, payment_methods)|
- Registered.new(*[
- (HAS_CREDIT_CARD unless payment_methods.empty?),
- (HAS_CURRENCY if customer.currency),
- (HAS_FORWARDING if fwd)
- ].compact)
+ def self.for(customer)
+ EMPromise.resolve(customer&.registered?).catch { nil }.then do |reg|
+ args_for(customer, reg).then do |kwargs|
+ new(@commands.select { |c| c.list_for?(**kwargs) })
end
end
+ end
- def initialize(*args)
- @extra = args
- end
-
- ALWAYS = [
- { node: "number-display", name: "Display JMP Number" },
- { node: "configure-calls", name: "Configure Calls" },
- { node: "usage", name: "Show Monthly Usage" },
- { node: "reset sip account", name: "Create or Reset SIP Account" },
- {
- node: "credit cards",
- name: "Credit Card Settings and Management"
- }
- ].freeze
+ def self.args_for(customer, reg)
+ args = { customer: customer, tel: reg ? reg.phone : nil }
+ return EMPromise.resolve(args) unless args[:tel]
- def each
- super
- ([ALWAYS] + @extra).each do |commands|
- commands.each { |x| yield x }
- end
+ EMPromise.all([
+ REDIS.get("catapult_fwd-#{args[:tel]}"),
+ customer.plan_name ? customer.payment_methods : []
+ ]).then do |(fwd, payment_methods)|
+ args.merge(fwd: fwd, payment_methods: payment_methods)
end
end
- HAS_CURRENCY = [
- node: "alt top up",
- name: "Buy Account Credit by Bitcoin, Mail, or Interac eTransfer"
- ].freeze
-
- HAS_FORWARDING = [
- node: "record-voicemail-greeting",
- name: "Record Voicemail Greeting"
- ].freeze
+ def initialize(commands)
+ @commands = commands
+ end
- HAS_CREDIT_CARD = [
- node: "top up", name: "Buy Account Credit by Credit Card"
- ].freeze
+ def each(&blk)
+ @commands.map { |c| { node: c.node, name: c.name } }.each(&blk)
+ end
end
D lib/error_to_send.rb => lib/error_to_send.rb +0 -10
@@ 1,10 0,0 @@
-# frozen_string_literal: true
-
-class ErrorToSend < StandardError
- attr_reader :stanza
-
- def initialize(stanza)
- super(stanza.to_s)
- @stanza = stanza
- end
-end
A lib/polyfill.rb => lib/polyfill.rb +5 -0
@@ 0,0 1,5 @@
+# frozen_string_literal: true
+
+class Object
+ alias then yield_self
+end
M lib/registration.rb => lib/registration.rb +97 -121
@@ 5,61 5,51 @@ require "ruby-bandwidth-iris"
require "securerandom"
require_relative "./alt_top_up_form"
+require_relative "./command"
require_relative "./bandwidth_tn_order"
require_relative "./em"
-require_relative "./error_to_send"
require_relative "./oob"
require_relative "./web_register_manager"
class Registration
- def self.for(iq, customer, web_register_manager)
+ def self.for(customer, web_register_manager)
+ jid = Command.execution.iq.from.stripped
customer.registered?.then do |registered|
if registered
- Registered.new(iq, registered.phone)
+ Registered.new(registered.phone)
else
- web_register_manager.choose_tel(iq).then do |(riq, tel)|
- Activation.for(riq, customer, tel)
+ web_register_manager[jid].choose_tel.then do |tel|
+ Activation.for(customer, tel)
end
end
end
end
class Registered
- def initialize(iq, tel)
- @reply = iq.reply
- @reply.status = :completed
+ def initialize(tel)
@tel = tel
end
def write
- @reply.note_type = :info
- @reply.note_text = <<~NOTE
- You are already registered with JMP number #{@tel}
- NOTE
- BLATHER << @reply
- nil
+ Command.finish("You are already registered with JMP number #{@tel}")
end
end
class Activation
- def self.for(iq, customer, tel)
+ def self.for(customer, tel)
if customer.active?
- Finish.new(iq, customer, tel)
+ Finish.new(customer, tel)
else
- EMPromise.resolve(new(iq, customer, tel))
+ EMPromise.resolve(new(customer, tel))
end
end
- def initialize(iq, customer, tel)
- @reply = iq.reply
- @reply.status = :executing
- @reply.allowed_actions = [:next]
-
+ def initialize(customer, tel)
@customer = customer
@tel = tel
end
- attr_reader :reply, :customer, :tel
+ attr_reader :customer, :tel
FORM_FIELDS = [
{
@@ 130,17 120,16 @@ class Registration
end
def write
- rate_center.then do |center|
- form = reply.form
- form.type = :form
- form.title = "Activate JMP"
- add_instructions(form, center)
- form.fields = FORM_FIELDS
-
- COMMAND_MANAGER.write(reply).then { |iq|
- Payment.for(iq, customer, tel)
- }.then(&:write)
- end
+ rate_center.then { |center|
+ Command.reply do |reply|
+ reply.allowed_actions = [:next]
+ form = reply.form
+ form.type = :form
+ form.title = "Activate JMP"
+ add_instructions(form, center)
+ form.fields = FORM_FIELDS
+ end
+ }.then { |iq| Payment.for(iq, customer, tel) }.then(&:write)
end
protected
@@ 163,23 152,19 @@ class Registration
customer = customer.with_plan(plan_name)
kinds.fetch(iq.form.field("activation_method")&.value&.to_s&.to_sym) {
raise "Invalid activation method"
- }.call(iq, customer, tel)
+ }.call(customer, tel)
end
class Bitcoin
Payment.kinds[:bitcoin] = method(:new)
- def initialize(iq, customer, tel)
- @reply = iq.reply
- reply.note_type = :info
- reply.status = :canceled
-
+ def initialize(customer, tel)
@customer = customer
@customer_id = customer.customer_id
@tel = tel
end
- attr_reader :reply, :customer_id, :tel
+ attr_reader :customer_id, :tel
def legacy_session_save
sid = SecureRandom.hex
@@ 215,9 200,7 @@ class Registration
BTC_SELL_PRICES.public_send(@customer.currency.to_s.downcase)
]).then do |(addr, _, rate)|
min = CONFIG[:activation_amount] / rate
- reply.note_text = note_text(min, addr)
- BLATHER << reply
- nil
+ Command.finish(note_text(min, addr), status: :canceled)
end
end
@@ 233,31 216,23 @@ class Registration
class CreditCard
Payment.kinds[:credit_card] = ->(*args) { self.for(*args) }
- def self.for(iq, customer, tel)
+ def self.for(customer, tel)
customer.payment_methods.then do |payment_methods|
if (method = payment_methods.default_payment_method)
- Activate.new(iq, customer, method, tel)
+ Activate.new(customer, method, tel)
else
- new(iq, customer, tel)
+ new(customer, tel)
end
end
end
- def initialize(iq, customer, tel)
+ def initialize(customer, tel)
@customer = customer
@tel = tel
-
- @reply = iq.reply
- @reply.status = :executing
- @reply.allowed_actions = [:next]
- @reply.note_type = :info
- @reply.note_text = "#{oob.desc}: #{oob.url}"
end
- attr_reader :reply
-
- def oob
- oob = OOB.find_or_create(@reply.command)
+ def oob(reply)
+ oob = OOB.find_or_create(reply.command)
oob.url = CONFIG[:credit_card_url].call(
reply.to.stripped.to_s.gsub("\\", "%5C"),
@customer.customer_id
@@ 267,14 242,17 @@ class Registration
end
def write
- COMMAND_MANAGER.write(@reply).then do |riq|
- CreditCard.for(riq, @customer, @tel).write
+ Command.reply { |reply|
+ reply.allowed_actions = [:next]
+ reply.note_type = :info
+ reply.note_text = "#{oob(reply).desc}: #{oob(reply).url}"
+ }.then do
+ CreditCard.for(@customer, @tel).then(&:write)
end
end
class Activate
- def initialize(iq, customer, payment_method, tel)
- @iq = iq
+ def initialize(customer, payment_method, tel)
@customer = customer
@payment_method = payment_method
@tel = tel
@@ 297,7 275,7 @@ class Registration
tx.insert.then {
@customer.bill_plan
}.then do
- Finish.new(@iq, @customer, @tel).write
+ Finish.new(@customer, @tel).write
end
end
@@ 323,14 301,13 @@ class Registration
end
def declined
- reply = @iq.reply
- reply_oob = decline_oob(reply)
- reply.status = :executing
- reply.allowed_actions = [:next]
- reply.note_type = :error
- reply.note_text = "#{reply_oob.desc}: #{reply_oob.url}"
- COMMAND_MANAGER.write(reply).then do |riq|
- CreditCard.for(riq, @customer, @tel).write
+ Command.reply { |reply|
+ reply_oob = decline_oob(reply)
+ reply.allowed_actions = [:next]
+ reply.note_type = :error
+ reply.note_text = "#{reply_oob.desc}: #{reply_oob.url}"
+ }.then do
+ CreditCard.for(@customer, @tel).then(&:write)
end
end
end
@@ 348,45 325,49 @@ class Registration
required: true
}].freeze
- def initialize(iq, customer, tel, error: nil)
+ def initialize(customer, tel, error: nil)
@customer = customer
@tel = tel
- @reply = iq.reply
- @reply.status = :executing
- @reply.allowed_actions = [:next]
- @form = @reply.form
- @form.type = :form
- @form.title = "Enter Invite Code"
- @form.instructions = error
- @form.fields = FIELDS
+ @error = error
+ end
+
+ def add_form(reply)
+ form = reply.form
+ form.type = :form
+ form.title = "Enter Invite Code"
+ form.instructions = @error if @error
+ form.fields = FIELDS
end
def write
- COMMAND_MANAGER.write(@reply).then do |iq|
- guard_too_many_tries.then {
- verify(iq.form.field("code")&.value&.to_s)
- }.then {
- Finish.new(iq, @customer, @tel)
- }.catch_only(Invalid) { |e|
- invalid_code(iq, e)
- }.then(&:write)
- end
+ Command.reply { |reply|
+ reply.allowed_actions = [:next]
+ add_form(reply)
+ }.then(&method(:parse))
+ end
+
+ def parse(iq)
+ guard_too_many_tries.then {
+ verify(iq.form.field("code")&.value&.to_s)
+ }.then {
+ Finish.new(@customer, @tel)
+ }.catch_only(Invalid, &method(:invalid_code)).then(&:write)
end
protected
def guard_too_many_tries
- REDIS.get("jmp_invite_tries-#{@customer.customer_id}").then do |t|
+ REDIS.get("jmp_invite_tries-#{customer_id}").then do |t|
raise Invalid, "Too many wrong attempts" if t.to_i > 10
end
end
- def invalid_code(iq, e)
+ def invalid_code(e)
EMPromise.all([
- REDIS.incr("jmp_invite_tries-#{@customer.customer_id}").then do
- REDIS.expire("jmp_invite_tries-#{@customer.customer_id}", 60 * 60)
+ REDIS.incr("jmp_invite_tries-#{customer_id}").then do
+ REDIS.expire("jmp_invite_tries-#{customer_id}", 60 * 60)
end,
- InviteCode.new(iq, @customer, @tel, error: e.message)
+ InviteCode.new(@customer, @tel, error: e.message)
]).then(&:last)
end
@@ 395,7 376,7 @@ class Registration
end
def verify(code)
- EM.promise_fiber do
+ EMPromise.resolve(nil).then do
DB.transaction do
valid = DB.exec(<<~SQL, [customer_id, code]).cmd_tuples.positive?
UPDATE invites SET used_by_id=$1, used_at=LOCALTIMESTAMP
@@ 411,10 392,7 @@ class Registration
class Mail
Payment.kinds[:mail] = method(:new)
- def initialize(iq, _customer, _tel)
- @reply = iq.reply
- @reply.status = :canceled
- end
+ def initialize(_customer, _tel); end
def form
form = Blather::Stanza::X.new(:result)
@@ 437,18 415,15 @@ class Registration
end
def write
- @reply.command << form
- BLATHER << @reply
+ Command.finish(status: :canceled) do |reply|
+ reply.command << form
+ end
end
end
end
class Finish
- def initialize(iq, customer, tel)
- @reply = iq.reply
- @reply.status = :completed
- @reply.note_type = :info
- @reply.note_text = "Your JMP account has been activated as #{tel}"
+ def initialize(customer, tel)
@customer = customer
@tel = tel
end
@@ 457,11 432,11 @@ class Registration
BandwidthTNOrder.create(@tel).then(&:poll).then(
->(_) { customer_active_tel_purchased },
lambda do |_|
- @reply.note_type = :error
- @reply.note_text =
+ Command.finish(
"The JMP number #{@tel} is no longer available, " \
- "please visit https://jmp.chat and choose another."
- BLATHER << @reply
+ "please visit https://jmp.chat and choose another.",
+ type: :error
+ )
end
)
end
@@ 469,27 444,28 @@ class Registration
protected
def cheogram_sip_addr
- "sip:#{ERB::Util.url_encode(@reply.to.stripped.to_s)}@sip.cheogram.com"
+ jid = Command.execution.iq.from.stripped
+ "sip:#{ERB::Util.url_encode(jid)}@sip.cheogram.com"
end
- def raise_setup_error
- @reply.note_type = :error
- @reply.note_text =
+ def raise_setup_error(e)
+ Command.log.error "@customer.register! failed", e
+ Command.finish(
"There was an error setting up your number, " \
- "please contact JMP support."
- raise ErrorToSend, @reply
+ "please contact JMP support.",
+ type: :error
+ )
end
def customer_active_tel_purchased
- @customer.register!(@tel).catch { |e|
- LOG.error "@customer.register! failed", e
- raise_setup_error
- }.then {
+ @customer.register!(@tel).catch(&method(:raise_setup_error)).then {
EMPromise.all([
REDIS.set("catapult_fwd-#{@tel}", cheogram_sip_addr),
@customer.fwd_timeout = 25 # ~5 seconds / ring, 5 rings
])
- }.then { BLATHER << @reply }
+ }.then do
+ Command.finish("Your JMP account has been activated as #{@tel}")
+ end
end
end
end
M lib/web_register_manager.rb => lib/web_register_manager.rb +7 -15
@@ 1,7 1,5 @@
# frozen_string_literal: true
-require_relative "error_to_send"
-
class WebRegisterManager
def initialize
@tel_map = Hash.new { ChooseTel.new }
@@ 15,29 13,23 @@ class WebRegisterManager
@tel_map[jid.to_s]
end
- def choose_tel(iq)
- self[iq&.from&.stripped].choose_tel(iq)
- end
-
class HaveTel
def initialize(tel)
@tel = tel
end
- def choose_tel(iq)
- EMPromise.resolve([iq, @tel])
+ def choose_tel
+ EMPromise.resolve(@tel)
end
end
class ChooseTel
- def choose_tel(iq)
- reply = iq.reply
- reply.status = :completed
- reply.note_type = :error
- reply.note_text =
+ def choose_tel
+ Command.finish(
"You have not chosen a phone number yet, please return to " \
- "https://jmp.chat and choose one now."
- raise ErrorToSend, reply
+ "https://jmp.chat and choose one now.",
+ type: :error
+ )
end
end
end
M sgx_jmp.rb => sgx_jmp.rb +106 -151
@@ 55,17 55,18 @@ singleton_class.class_eval do
Blather::DSL.append_features(self)
end
+require_relative "lib/polyfill"
require_relative "lib/alt_top_up_form"
require_relative "lib/add_bitcoin_address"
require_relative "lib/backend_sgx"
require_relative "lib/bandwidth_tn_order"
require_relative "lib/btc_sell_prices"
require_relative "lib/buy_account_credit_form"
+require_relative "lib/command"
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"
require_relative "lib/payment_methods"
require_relative "lib/registration"
@@ 132,7 133,10 @@ end
BRAINTREE = AsyncBraintree.new(**CONFIG[:braintree])
def panic(e, hub=nil)
- LOG.fatal "Error raised during event loop: #{e.class}", e
+ (Thread.current[:log] || LOG).fatal(
+ "Error raised during event loop: #{e.class}",
+ e
+ )
if e.is_a?(::Exception)
(hub || Sentry).capture_exception(e, hint: { background: false })
else
@@ 389,174 393,125 @@ iq "/iq/ns:services", ns: "urn:xmpp:extdisco:2" do |iq|
self << reply
end
-command :execute?, node: "jabber:iq:register", sessionid: nil do |iq|
- StatsD.increment("command", tags: ["node:#{iq.node}"])
-
- sentry_hub = new_sentry_hub(iq, name: iq.node)
- EMPromise.resolve(nil).then {
- CustomerRepo.new.find_by_jid(iq.from.stripped)
- }.catch {
- sentry_hub.add_breadcrumb(Sentry::Breadcrumb.new(
- message: "Customer.create"
- ))
- CustomerRepo.new.create(iq.from.stripped)
+Command.new(
+ "jabber:iq:register",
+ "Register",
+ list_for: ->(*) { true }
+) {
+ Command.customer.catch {
+ Sentry.add_breadcrumb(Sentry::Breadcrumb.new(message: "Customer.create"))
+ Command.execution.customer_repo.create(Command.execution.iq.from.stripped)
}.then { |customer|
- sentry_hub.current_scope.set_user(
- id: customer.customer_id,
- jid: iq.from.stripped.to_s
- )
- sentry_hub.add_breadcrumb(Sentry::Breadcrumb.new(
- message: "Registration.for"
- ))
- Registration.for(
- iq,
- customer,
- web_register_manager
- ).then(&:write).then { StatsD.increment("registration.completed") }
- }.catch_only(ErrorToSend) { |e|
- self << e.stanza
- }.catch { |e| panic(e, sentry_hub) }
-end
-
-def reply_with_note(iq, text, type: :info)
- reply = iq.reply
- reply.status = :completed
- reply.note_type = type
- reply.note_text = text
-
- self << reply
-end
+ Sentry.add_breadcrumb(Sentry::Breadcrumb.new(message: "Registration.for"))
+ Registration.for(customer, web_register_manager).then(&:write)
+ }.then {
+ StatsD.increment("registration.completed")
+ }.catch_only(Command::Execution::FinalStanza) do |e|
+ StatsD.increment("registration.completed")
+ EMPromise.reject(e)
+ end
+}.register(self).then(&CommandList.method(:register))
# Commands that just pass through to the SGX
-command node: [
- "number-display",
- "configure-calls",
- "record-voicemail-greeting"
-] do |iq|
- StatsD.increment("command", tags: ["node:#{iq.node}"])
-
- sentry_hub = new_sentry_hub(iq, name: iq.node)
- 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
- )
-
- customer.stanza_from(iq)
- }.catch { |e| panic(e, sentry_hub) }
+{
+ "number-display" => ["Display JMP Number"],
+ "configure-calls" => ["Configure Calls"],
+ "record-voicemail-greeting" => [
+ "Record Voicemail Greeting",
+ list_for: ->(fwd: nil, **) { !!fwd }
+ ]
+}.each do |node, args|
+ Command.new(node, *args) {
+ Command.customer.then do |customer|
+ customer.stanza_from(Command.execution.iq)
+ end
+ }.register(self).then(&CommandList.method(:register))
end
-command :execute?, node: "credit cards", sessionid: nil do |iq|
- StatsD.increment("command", tags: ["node:#{iq.node}"])
-
- sentry_hub = new_sentry_hub(iq, name: iq.node)
- reply = iq.reply
- reply.status = :completed
-
- CustomerRepo.new.find_by_jid(iq.from.stripped).then { |customer|
- oob = OOB.find_or_create(reply.command)
- oob.url = CONFIG[:credit_card_url].call(
+Command.new(
+ "credit cards",
+ "Credit Card Settings and Management"
+) {
+ Command.customer.then do |customer|
+ url = CONFIG[:credit_card_url].call(
reply.to.stripped.to_s.gsub("\\", "%5C"),
customer.customer_id
)
- oob.desc = "Manage credits cards and settings"
-
- reply.note_type = :info
- reply.note_text = "#{oob.desc}: #{oob.url}"
-
- self << reply
- }.catch { |e| panic(e, sentry_hub) }
-end
-
-command :execute?, node: "top up", sessionid: nil do |iq|
- StatsD.increment("command", tags: ["node:#{iq.node}"])
-
- sentry_hub = new_sentry_hub(iq, name: iq.node)
- reply = iq.reply
- reply.allowed_actions = [:complete]
-
- CustomerRepo.new.find_by_jid(iq.from.stripped).then { |customer|
+ desc = "Manage credits cards and settings"
+ Command.finish("#{desc}: #{url}") do |reply|
+ oob = OOB.find_or_create(reply.command)
+ oob.url = url
+ oob.desc = desc
+ end
+ end
+}.register(self).then(&CommandList.method(:register))
+
+Command.new(
+ "top up",
+ "Buy Account Credit by Credit Card",
+ list_for: ->(payment_methods: [], **) { !payment_methods.empty? },
+ format_error: ->(e) { "Failed to buy credit, system said: #{e.message}" }
+) {
+ Command.customer.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] }
+ Command.reply { |reply|
+ reply.allowed_actions = [:complete]
+ credit_form.add_to_form(reply.form)
+ }.then do |iq|
+ Transaction.sale(customer, **credit_form.parse(iq.form))
+ end
end
- }.then { |(customer, credit_form, iq2)|
- iq = iq2 # This allows the catch to use it also
- Transaction.sale(customer, **credit_form.parse(iq2.form))
}.then { |transaction|
transaction.insert.then do
- reply_with_note(iq, "#{transaction} added to your account balance.")
+ Command.finish("#{transaction} added to your account balance.")
end
- }.catch_only(BuyAccountCreditForm::AmountValidationError) { |e|
- reply_with_note(iq, e.message, type: :error)
- }.catch { |e|
- sentry_hub.capture_exception(e)
- text = "Failed to buy credit, system said: #{e.message}"
- reply_with_note(iq, text, type: :error)
- }.catch { |e| panic(e, sentry_hub) }
-end
-
-command :execute?, node: "alt top up", sessionid: nil do |iq|
- StatsD.increment("command", tags: ["node:#{iq.node}"])
-
- sentry_hub = new_sentry_hub(iq, name: iq.node)
- reply = iq.reply
- reply.status = :executing
- reply.allowed_actions = [:complete]
-
- 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
- )
-
+ }.catch_only(BuyAccountCreditForm::AmountValidationError) do |e|
+ Command.finish(e.message, type: :error)
+ end
+}.register(self).then(&CommandList.method(:register))
+
+Command.new(
+ "alt top up",
+ "Buy Account Credit by Bitcoin, Mail, or Interac eTransfer",
+ list_for: ->(customer:, **) { !!customer.currency }
+) {
+ Command.customer.then { |customer|
EMPromise.all([AltTopUpForm.for(customer), customer])
- }.then { |(alt_form, customer)|
- reply.command << alt_form.form
-
- COMMAND_MANAGER.write(reply).then do |iq2|
- AddBitcoinAddress.for(iq2, alt_form, customer).write
+ }.then do |(alt_form, customer)|
+ Command.reply { |reply|
+ reply.allowed_actions = [:complete]
+ reply.command << alt_form.form
+ }.then do |iq|
+ AddBitcoinAddress.for(iq, alt_form, customer).write
end
- }.catch { |e| panic(e, sentry_hub) }
-end
-
-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)
- 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
- )
- customer.reset_sip_account
- }.then { |sip_account|
- reply = iq.reply
- reply.command << sip_account.form
- BLATHER << reply
- }.catch { |e| panic(e, sentry_hub) }
-end
-
-command :execute?, node: "usage", sessionid: nil do |iq|
- StatsD.increment("command", tags: ["node:#{iq.node}"])
+ end
+}.register(self).then(&CommandList.method(:register))
+
+Command.new(
+ "reset sip account",
+ "Create or Reset SIP Account"
+) {
+ Command.customer.then(&:reset_sip_account).then do |sip_account|
+ Command.finish do |reply|
+ reply.command << sip_account.form
+ end
+ end
+}.register(self).then(&CommandList.method(:register))
- sentry_hub = new_sentry_hub(iq, name: iq.node)
+Command.new(
+ "usage",
+ "Show Monthly Usage"
+) {
report_for = (Date.today..(Date.today << 1))
- 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
- )
-
+ Command.customer.then { |customer|
customer.usage_report(report_for)
- }.then { |usage_report|
- reply = iq.reply
- reply.status = :completed
- reply.command << usage_report.form
- BLATHER << reply
- }.catch { |e| panic(e, sentry_hub) }
-end
+ }.then do |usage_report|
+ Command.finish do |reply|
+ reply.command << usage_report.form
+ end
+ end
+}.register(self).then(&CommandList.method(:register))
command :execute?, node: "web-register", sessionid: nil do |iq|
StatsD.increment("command", tags: ["node:#{iq.node}"])
M test/test_command_list.rb => test/test_command_list.rb +35 -12
@@ 1,20 1,45 @@
# frozen_string_literal: true
require "test_helper"
+require "command"
require "command_list"
CommandList::Customer = Minitest::Mock.new
CommandList::REDIS = Minitest::Mock.new
class CommandListTest < Minitest::Test
+ SETUP = begin
+ [
+ Command.new("no_customer", "", list_for: ->(**) { true }),
+ Command.new("registered", "", list_for: ->(tel:, **) { !!tel }),
+ Command.new("fwd", "", list_for: ->(fwd: nil, **) { !!fwd }),
+ Command.new(
+ "currency", "",
+ list_for: ->(customer: nil, **) { !!customer&.currency }
+ ),
+ Command.new(
+ "cc", "",
+ list_for: ->(payment_methods: [], **) { !payment_methods.empty? }
+ )
+ ].each do |c|
+ CommandList.register(c)
+ end
+ end
+
def test_for_no_customer
- assert_instance_of CommandList, CommandList.for(nil).sync
+ assert_equal(
+ ["no_customer"],
+ CommandList.for(nil).sync.map { |c| c[:node] }
+ )
end
em :test_for_no_customer
def test_for_unregistered
customer = OpenStruct.new(registered?: false)
- assert_instance_of CommandList, CommandList.for(customer).sync
+ assert_equal(
+ ["no_customer"],
+ CommandList.for(customer).sync.map { |c| c[:node] }
+ )
end
em :test_for_unregistered
@@ 29,9 54,8 @@ class CommandListTest < Minitest::Test
payment_methods: EMPromise.resolve([])
)
assert_equal(
- ["CommandList::Registered"],
- CommandList.for(customer).sync
- .class.ancestors.map(&:name).grep(/\ACommandList::/)
+ ["no_customer", "registered"],
+ CommandList.for(customer).sync.map { |c| c[:node] }
)
end
em :test_for_registered
@@ 47,8 71,8 @@ class CommandListTest < Minitest::Test
payment_methods: EMPromise.resolve([])
)
assert_equal(
- CommandList::HAS_FORWARDING,
- CommandList::HAS_FORWARDING & CommandList.for(customer).sync.to_a
+ ["no_customer", "registered", "fwd"],
+ CommandList.for(customer).sync.map { |c| c[:node] }
)
end
em :test_for_registered_with_fwd
@@ 65,8 89,8 @@ class CommandListTest < Minitest::Test
payment_methods: EMPromise.resolve([:boop])
)
assert_equal(
- CommandList::HAS_CREDIT_CARD,
- CommandList::HAS_CREDIT_CARD & CommandList.for(customer).sync.to_a
+ ["no_customer", "registered", "cc"],
+ CommandList.for(customer).sync.map { |c| c[:node] }
)
end
em :test_for_registered_with_credit_card
@@ 81,10 105,9 @@ class CommandListTest < Minitest::Test
registered?: OpenStruct.new(phone: "1"),
currency: :USD
)
-
assert_equal(
- CommandList::HAS_CURRENCY,
- CommandList::HAS_CURRENCY & CommandList.for(customer).sync.to_a
+ ["no_customer", "registered", "currency"],
+ CommandList.for(customer).sync.map { |c| c[:node] }
)
end
em :test_for_registered_with_currency
M test/test_helper.rb => test/test_helper.rb +13 -0
@@ 10,6 10,7 @@ require "em_promise"
require "fiber"
require "minitest/autorun"
require "rantly/minitest_extensions"
+require "sentry-ruby"
require "webmock/minitest"
begin
require "pry-rescue/minitest"
@@ 34,6 35,8 @@ end
require "backend_sgx"
+Sentry.init
+
CONFIG = {
sgx: "sgx",
component: {
@@ 78,6 81,16 @@ CONFIG = {
electrum_notify_url: ->(*) { "http://notify.example.com" }
}.freeze
+def panic(e)
+ raise e
+end
+
+LOG = Class.new {
+ def child(*)
+ Minitest::Mock.new
+ end
+}.new.freeze
+
BLATHER = Class.new {
def <<(*); end
}.new.freeze
M test/test_registration.rb => test/test_registration.rb +235 -213
@@ 4,6 4,19 @@ require "test_helper"
require "customer"
require "registration"
+def execute_command(
+ iq=Blather::Stanza::Iq::Command.new.tap { |i| i.from = "test@example.com" },
+ blather: BLATHER,
+ &blk
+)
+ Command::Execution.new(
+ Minitest::Mock.new,
+ blather,
+ :to_s.to_proc,
+ iq
+ ).execute(&blk).sync
+end
+
class RegistrationTest < Minitest::Test
def test_for_registered
sgx = OpenStruct.new(
@@ 11,11 24,12 @@ class RegistrationTest < Minitest::Test
)
iq = Blather::Stanza::Iq::Command.new
iq.from = "test@example.com"
- result = Registration.for(
- iq,
- Customer.new("test", sgx: sgx),
- Minitest::Mock.new
- ).sync
+ result = execute_command(iq) do
+ Registration.for(
+ Customer.new("test", sgx: sgx),
+ Minitest::Mock.new
+ )
+ end
assert_kind_of Registration::Registered, result
end
em :test_for_registered
@@ 26,16 40,17 @@ class RegistrationTest < Minitest::Test
web_manager["test@example.com"] = "+15555550000"
iq = Blather::Stanza::Iq::Command.new
iq.from = "test@example.com"
- result = Registration.for(
- iq,
- Customer.new(
- "test",
- plan_name: "test_usd",
- expires_at: Time.now + 999,
- sgx: sgx
- ),
- web_manager
- ).sync
+ result = execute_command(iq) do
+ Registration.for(
+ Customer.new(
+ "test",
+ plan_name: "test_usd",
+ expires_at: Time.now + 999,
+ sgx: sgx
+ ),
+ web_manager
+ )
+ end
assert_kind_of Registration::Finish, result
end
em :test_for_activated
@@ 46,20 61,20 @@ class RegistrationTest < Minitest::Test
web_manager["test@example.com"] = "+15555550000"
iq = Blather::Stanza::Iq::Command.new
iq.from = "test@example.com"
- result = Registration.for(
- iq,
- Customer.new("test", sgx: sgx),
- web_manager
- ).sync
+ result = execute_command(iq) do
+ Registration.for(
+ Customer.new("test", sgx: sgx),
+ web_manager
+ )
+ end
assert_kind_of Registration::Activation, result
end
em :test_for_not_activated_with_customer_id
class ActivationTest < Minitest::Test
- Registration::Activation::COMMAND_MANAGER = Minitest::Mock.new
+ Command::COMMAND_MANAGER = Minitest::Mock.new
def setup
- iq = Blather::Stanza::Iq::Command.new
- @activation = Registration::Activation.new(iq, "test", "+15555550000")
+ @activation = Registration::Activation.new("test", "+15555550000")
end
def test_write
@@ 82,12 97,9 @@ class RegistrationTest < Minitest::Test
</TelephoneNumberDetails>
</TelephoneNumberResponse>
RESPONSE
- result = Minitest::Mock.new
- result.expect(:then, result)
- result.expect(:then, EMPromise.resolve(:test_result))
- Registration::Activation::COMMAND_MANAGER.expect(
+ Command::COMMAND_MANAGER.expect(
:write,
- result,
+ EMPromise.reject(:test_result),
[Matching.new do |iq|
assert_equal :form, iq.form.type
assert_equal(
@@ 96,7 108,11 @@ class RegistrationTest < Minitest::Test
)
end]
)
- assert_equal :test_result, @activation.write.sync
+ assert_equal(
+ :test_result,
+ execute_command { @activation.write.catch { |e| e } }
+ )
+ assert_mock Command::COMMAND_MANAGER
end
em :test_write
end
@@ 161,7 177,6 @@ class RegistrationTest < Minitest::Test
class BitcoinTest < Minitest::Test
Registration::Payment::Bitcoin::BTC_SELL_PRICES = Minitest::Mock.new
- Registration::Payment::Bitcoin::BLATHER = Minitest::Mock.new
Customer::REDIS = Minitest::Mock.new
def setup
@@ 172,9 187,7 @@ class RegistrationTest < Minitest::Test
:add_btc_address,
EMPromise.resolve("testaddr")
)
- iq = Blather::Stanza::Iq::Command.new
@bitcoin = Registration::Payment::Bitcoin.new(
- iq,
@customer,
"+15555550000"
)
@@ 192,7 205,8 @@ class RegistrationTest < Minitest::Test
You will receive a notification when your payment is complete.
NOTE
- Registration::Payment::Bitcoin::BLATHER.expect(
+ blather = Minitest::Mock.new
+ blather.expect(
:<<,
nil,
[Matching.new do |reply|
@@ 207,19 221,18 @@ class RegistrationTest < Minitest::Test
EMPromise.resolve(BigDecimal.new(1))
)
@bitcoin.stub(:save, EMPromise.resolve(nil)) do
- @bitcoin.write.sync
+ execute_command(blather: blather) do
+ @bitcoin.write
+ end
end
- Registration::Payment::Bitcoin::BLATHER.verify
+ assert_mock blather
end
em :test_write
end
class CreditCardTest < Minitest::Test
def setup
- @iq = Blather::Stanza::Iq::Command.new
- @iq.from = "test@example.com"
@credit_card = Registration::Payment::CreditCard.new(
- @iq,
Customer.new("test"),
"+15555550000"
)
@@ 234,7 247,6 @@ class RegistrationTest < Minitest::Test
assert_kind_of(
Registration::Payment::CreditCard::Activate,
Registration::Payment::CreditCard.for(
- @iq,
customer,
"+15555550000"
).sync
@@ 242,14 254,27 @@ class RegistrationTest < Minitest::Test
end
em :test_for
- def test_reply
- assert_equal [:execute, :next], @credit_card.reply.allowed_actions
- assert_equal(
- "Add credit card, then return here to continue: " \
- "http://creditcard.example.com",
- @credit_card.reply.note.content
- )
+ def test_write
+ result = execute_command do
+ Command::COMMAND_MANAGER.expect(
+ :write,
+ EMPromise.reject(:test_result),
+ [Matching.new do |reply|
+ assert_equal [:execute, :next], reply.allowed_actions
+ assert_equal(
+ "Add credit card, then return here to continue: " \
+ "http://creditcard.example.com",
+ reply.note.content
+ )
+ end]
+ )
+
+ @credit_card.write.catch { |e| e }
+ end
+
+ assert_equal :test_result, result
end
+ em :test_write
end
class ActivateTest < Minitest::Test
@@ 257,8 282,7 @@ class RegistrationTest < Minitest::Test
Minitest::Mock.new
Registration::Payment::CreditCard::Activate::Transaction =
Minitest::Mock.new
- Registration::Payment::CreditCard::Activate::COMMAND_MANAGER =
- Minitest::Mock.new
+ Command::COMMAND_MANAGER = Minitest::Mock.new
def test_write
transaction = PromiseMock.new
@@ 277,19 301,19 @@ class RegistrationTest < Minitest::Test
assert_equal CONFIG[:activation_amount], amount
assert_equal :test_default_method, payment_method
end
- iq = Blather::Stanza::Iq::Command.new
customer.expect(:bill_plan, nil)
Registration::Payment::CreditCard::Activate::Finish.expect(
:new,
OpenStruct.new(write: nil),
- [Blather::Stanza::Iq, customer, "+15555550000"]
+ [customer, "+15555550000"]
)
- Registration::Payment::CreditCard::Activate.new(
- iq,
- customer,
- :test_default_method,
- "+15555550000"
- ).write.sync
+ execute_command do
+ Registration::Payment::CreditCard::Activate.new(
+ customer,
+ :test_default_method,
+ "+15555550000"
+ ).write
+ end
Registration::Payment::CreditCard::Activate::Transaction.verify
transaction.verify
customer.verify
@@ 301,21 325,11 @@ class RegistrationTest < Minitest::Test
customer = Minitest::Mock.new(
Customer.new("test", plan_name: "test_usd")
)
- Registration::Payment::CreditCard::Activate::Transaction.expect(
- :sale,
- EMPromise.reject("declined")
- ) do |acustomer, amount:, payment_method:|
- assert_operator customer, :===, acustomer
- assert_equal CONFIG[:activation_amount], amount
- assert_equal :test_default_method, payment_method
- end
iq = Blather::Stanza::Iq::Command.new
iq.from = "test@example.com"
- result = Minitest::Mock.new
- result.expect(:then, nil)
- Registration::Payment::CreditCard::Activate::COMMAND_MANAGER.expect(
+ Command::COMMAND_MANAGER.expect(
:write,
- result,
+ EMPromise.reject(:test_result),
[Matching.new do |reply|
assert_equal :error, reply.note_type
assert_equal(
@@ 325,12 339,23 @@ class RegistrationTest < Minitest::Test
)
end]
)
- Registration::Payment::CreditCard::Activate.new(
- iq,
- customer,
- :test_default_method,
- "+15555550000"
- ).write.sync
+ result = execute_command do
+ Registration::Payment::CreditCard::Activate::Transaction.expect(
+ :sale,
+ EMPromise.reject("declined")
+ ) do |acustomer, amount:, payment_method:|
+ assert_operator customer, :===, acustomer
+ assert_equal CONFIG[:activation_amount], amount
+ assert_equal :test_default_method, payment_method
+ end
+
+ Registration::Payment::CreditCard::Activate.new(
+ customer,
+ :test_default_method,
+ "+15555550000"
+ ).write.catch { |e| e }
+ end
+ assert_equal :test_result, result
Registration::Payment::CreditCard::Activate::Transaction.verify
end
em :test_write_declines
@@ 341,164 366,157 @@ class RegistrationTest < Minitest::Test
Minitest::Mock.new
Registration::Payment::InviteCode::REDIS =
Minitest::Mock.new
- Registration::Payment::InviteCode::COMMAND_MANAGER =
- Minitest::Mock.new
+ Command::COMMAND_MANAGER = Minitest::Mock.new
Registration::Payment::InviteCode::Finish =
Minitest::Mock.new
-
def test_write
customer = Customer.new("test", plan_name: "test_usd")
- Registration::Payment::InviteCode::REDIS.expect(
- :get,
- EMPromise.resolve(nil),
- ["jmp_invite_tries-test"]
- )
- Registration::Payment::InviteCode::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::DB.expect(:transaction, true, [])
Registration::Payment::InviteCode::Finish.expect(
:new,
OpenStruct.new(write: nil),
[
- Blather::Stanza::Iq::Command,
customer,
"+15555550000"
]
)
- iq = Blather::Stanza::Iq::Command.new
- iq.from = "test@example.com"
- Registration::Payment::InviteCode.new(
- iq,
- customer,
- "+15555550000"
- ).write.sync
- Registration::Payment::InviteCode::COMMAND_MANAGER.verify
- Registration::Payment::InviteCode::DB.verify
- Registration::Payment::InviteCode::REDIS.verify
- Registration::Payment::InviteCode::Finish.verify
+ execute_command do
+ Registration::Payment::InviteCode::REDIS.expect(
+ :get,
+ EMPromise.resolve(nil),
+ ["jmp_invite_tries-test"]
+ )
+ 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,
+ "+15555550000"
+ ).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_bad_code
- customer = Customer.new("test", plan_name: "test_usd")
- Registration::Payment::InviteCode::REDIS.expect(
- :get,
- EMPromise.resolve(0),
- ["jmp_invite_tries-test"]
- )
- Registration::Payment::InviteCode::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::DB.expect(:transaction, []) do
- raise Registration::Payment::InviteCode::Invalid, "wut"
- end
- 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::COMMAND_MANAGER.expect(
- :write,
- EMPromise.reject(Promise::Error.new),
- [Matching.new do |reply|
- assert_equal :form, reply.form.type
- assert_equal "wut", reply.form.instructions
- end]
- )
- iq = Blather::Stanza::Iq::Command.new
- iq.from = "test@example.com"
- assert_raises Promise::Error do
+ result = execute_command do
+ customer = Customer.new("test", plan_name: "test_usd")
+ Registration::Payment::InviteCode::REDIS.expect(
+ :get,
+ EMPromise.resolve(0),
+ ["jmp_invite_tries-test"]
+ )
+ Registration::Payment::InviteCode::DB.expect(:transaction, []) do
+ raise Registration::Payment::InviteCode::Invalid, "wut"
+ end
+ 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]
+ )
+ 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 "wut", reply.form.instructions
+ end]
+ )
+
Registration::Payment::InviteCode.new(
- iq,
customer,
"+15555550000"
- ).write.sync
+ ).write.catch { |e| e }
end
- Registration::Payment::InviteCode::COMMAND_MANAGER.verify
- Registration::Payment::InviteCode::DB.verify
- Registration::Payment::InviteCode::REDIS.verify
+ 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_bad_code_over_limit
- customer = Customer.new("test", plan_name: "test_usd")
- Registration::Payment::InviteCode::REDIS.expect(
- :get,
- EMPromise.resolve(11),
- ["jmp_invite_tries-test"]
- )
- Registration::Payment::InviteCode::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::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::COMMAND_MANAGER.expect(
- :write,
- EMPromise.reject(Promise::Error.new),
- [Matching.new do |reply|
- assert_equal :form, reply.form.type
- assert_equal "Too many wrong attempts", reply.form.instructions
- end]
- )
- iq = Blather::Stanza::Iq::Command.new
- iq.from = "test@example.com"
- assert_raises Promise::Error do
+ result = execute_command do
+ customer = Customer.new("test", plan_name: "test_usd")
+ Registration::Payment::InviteCode::REDIS.expect(
+ :get,
+ EMPromise.resolve(11),
+ ["jmp_invite_tries-test"]
+ )
+ 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::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]
+ )
+ 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(
- iq,
customer,
"+15555550000"
- ).write.sync
+ ).write.catch { |e| e }
end
- Registration::Payment::InviteCode::COMMAND_MANAGER.verify
- Registration::Payment::InviteCode::REDIS.verify
+ 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
- Registration::Finish::BLATHER = Minitest::Mock.new
Registration::Finish::REDIS = Minitest::Mock.new
BackendSgx::REDIS = Minitest::Mock.new
@@ 507,7 525,6 @@ class RegistrationTest < Minitest::Test
iq = Blather::Stanza::Iq::Command.new
iq.from = "test\\40example.com@cheogram.com"
@finish = Registration::Finish.new(
- iq,
Customer.new("test", sgx: @sgx),
"+15555550000"
)
@@ 547,17 564,12 @@ class RegistrationTest < Minitest::Test
"Content-Type" => "application/json"
}
).to_return(status: 201)
- @sgx.expect(
- :register!,
- EMPromise.resolve(OpenStruct.new(error?: false)),
- ["+15555550000"]
- )
Registration::Finish::REDIS.expect(
:set,
nil,
[
"catapult_fwd-+15555550000",
- "sip:test%5C40example.com%40cheogram.com@sip.cheogram.com"
+ "sip:test%40example.com@sip.cheogram.com"
]
)
BackendSgx::REDIS.expect(
@@ 565,7 577,8 @@ class RegistrationTest < Minitest::Test
nil,
["catapult_fwd_timeout-customer_test@component", 25]
)
- Registration::Finish::BLATHER.expect(
+ blather = Minitest::Mock.new
+ blather.expect(
:<<,
nil,
[Matching.new do |reply|
@@ 577,12 590,20 @@ class RegistrationTest < Minitest::Test
)
end]
)
- @finish.write.sync
+ execute_command(blather: blather) do
+ @sgx.expect(
+ :register!,
+ EMPromise.resolve(OpenStruct.new(error?: false)),
+ ["+15555550000"]
+ )
+
+ @finish.write
+ end
assert_requested create_order
- @sgx.verify
- Registration::Finish::REDIS.verify
- BackendSgx::REDIS.verify
- Registration::Finish::BLATHER.verify
+ assert_mock @sgx
+ assert_mock Registration::Finish::REDIS
+ assert_mock BackendSgx::REDIS
+ assert_mock blather
end
em :test_write
@@ 605,7 626,8 @@ class RegistrationTest < Minitest::Test
<OrderStatus>FAILED</OrderStatus>
</OrderResponse>
RESPONSE
- Registration::Finish::BLATHER.expect(
+ blather = Minitest::Mock.new
+ blather.expect(
:<<,
nil,
[Matching.new do |reply|
@@ 618,9 640,9 @@ class RegistrationTest < Minitest::Test
)
end]
)
- @finish.write.sync
+ execute_command(blather: blather) { @finish.write }
assert_requested create_order
- Registration::Finish::BLATHER.verify
+ assert_mock blather
end
em :test_write_tn_fail
end
M test/test_web_register_manager.rb => test/test_web_register_manager.rb +3 -12
@@ 15,18 15,9 @@ class WebRegisterManagerTest < Minitest::Test
end
def test_choose_tel_have_tel
- @manager["jid@example.com"] = "+15555550000"
- iq = Blather::Stanza::Iq.new
- iq.from = "jid@example.com"
- assert_equal [iq, "+15555550000"], @manager.choose_tel(iq).sync
+ jid = "jid@example.com"
+ @manager[jid] = "+15555550000"
+ assert_equal "+15555550000", @manager[jid].choose_tel.sync
end
em :test_choose_tel_have_tel
-
- def test_choose_tel_not_have_tel
- skip "ChooseTel not implemented yet"
- iq = Blather::Stanza::Iq.new
- iq.from = "jid@example.com"
- @manager.choose_tel(iq).sync
- end
- em :test_choose_tel_not_have_tel
end