Skip to content

Instantly share code, notes, and snippets.

@r00k
Created November 13, 2012 19:24
Show Gist options
  • Save r00k/4067818 to your computer and use it in GitHub Desktop.
Save r00k/4067818 to your computer and use it in GitHub Desktop.
commit 093625b9f1ac705d8821cbbc2f9449558c555c01
Author: Ben Orenstein <[email protected]>
Date: Tue Nov 13 14:05:20 2012 -0500
Extract an OrderRefunder
diff --git a/app/models/order.rb b/app/models/order.rb
index 8726359..092643c 100644
--- a/app/models/order.rb
+++ b/app/models/order.rb
@@ -319,6 +319,12 @@ class Order < ActiveRecord::Base
end
end
+ def refund!(order_refunder=OrderRefunder.new(self))
+ transaction do
+ order_refunder.refund!
+ end
+ end
+
def self.refunded
with_state(:refunded).order 'orders.created_at'
end
@@ -428,26 +434,7 @@ class Order < ActiveRecord::Base
end
def before_refunded
- transaction do
- loyalty.add_credit loyalty_credit
- loyalty.unpunch self
- user.add_credit user_credit
- redemptions.each { |redemption| redemption.claim.add_value redemption.value }
-
- self.refund_type = case
- when balance.zero? then 'no_balance'
- when bundle.open? then 'bundle_refunded'
- when refund_balance.success? then 'braintree_refunded'
- when user.add_credit(balance) then 'user_credited'
- end
-
- create_refund_object! refund_type: refund_type
- end
- end
-
- def charge
- raise RuntimeError, "not implemented yet"
- puts "not implemented"
+ refund!
end
def interstitial_action
diff --git a/app/models/order_refunder.rb b/app/models/order_refunder.rb
new file mode 100644
index 0000000..7565b19
--- /dev/null
+++ b/app/models/order_refunder.rb
@@ -0,0 +1,24 @@
+class OrderRefunder
+ def initialize(order)
+ @order = order
+ end
+
+ delegate :balance, :bundle, :create_refund_object!, :loyalty, :loyalty_credit, :user,
+ :refund_balance, :redemptions, :refund_type, :user_credit, to: :@order
+
+ def refund!
+ loyalty.add_credit loyalty_credit
+ loyalty.unpunch @order
+ user.add_credit user_credit
+ redemptions.each { |redemption| redemption.claim.add_value redemption.value }
+
+ @order.refund_type = case
+ when balance.zero? then 'no_balance'
+ when bundle.open? then 'bundle_refunded'
+ when refund_balance.success? then 'braintree_refunded'
+ when user.add_credit(balance) then 'user_credited'
+ end
+
+ create_refund_object! refund_type: refund_type
+ end
+end
diff --git a/test/unit/order_refunder_test.rb b/test/unit/order_refunder_test.rb
new file mode 100644
index 0000000..f4de9cf
--- /dev/null
+++ b/test/unit/order_refunder_test.rb
@@ -0,0 +1,280 @@
+require 'test_helper'
+
+class Test < ActiveSupport::TestCase
+ context 'refund' do
+ setup do
+ stub_braintree_customer_sale_success
+ @order = create(:completed_order,
+ balance: 45.to_money,
+ claim_credit: 8.to_money,
+ earn: 9.to_money,
+ loyalty_credit: 3.to_money,
+ spend: 50.to_money,
+ tip: 10.to_money,
+ user_credit: 3.to_money)
+
+ @loyalty = @order.loyalty.tap do |loyalty|
+ loyalty.credit = 5.to_money
+ loyalty.first_order_created_at = @order.created_at
+ loyalty.last_order_created_at = @order.created_at
+ loyalty.orders_completed_count = 1
+ loyalty.savings = 15.to_money
+ loyalty.spend = 65.to_money
+ loyalty.spend_volume = 60.to_money
+ loyalty.tip_volume = 10.to_money
+ loyalty.save!
+ end
+
+ @user = @order.user.tap do |user|
+ user.credit = 2.to_money
+ user.save!
+ end
+
+ @bundle = create(:bundle, user: @user)
+ @bundle.orders << @order
+
+ @claim_one = create(:claim)
+ @claim_one.value_remaining = 1.to_money
+ @claim_one.save!
+ create :redemption, claim: @claim_one, order: @order, value: 3.to_money
+
+ @claim_two = create(:claim)
+ @claim_two.value_remaining = 0.to_money
+ @claim_two.save!
+ create :redemption, claim: @claim_two, order: @order, value: 5.to_money
+ end
+
+ context 'when state is refunded' do
+ setup do
+ @order.state = 'refunded'
+ @order.refund_type = 'braintree_refunded'
+ @order.refunded_at = Time.zone.now
+ @order.save!
+ @original_refunded_at = @order.refunded_at
+ @result = @order.refund
+ end
+
+ should 'not change loyalty.credit' do
+ assert_equal 5.to_money, @loyalty.credit
+ end
+
+ should 'not change loyalty.first_order_created_at' do
+ assert_equal @order.created_at, @loyalty.first_order_created_at
+ end
+
+ should 'not change loyalty.last_order_created_at' do
+ assert_equal @order.created_at, @loyalty.last_order_created_at
+ end
+
+ should 'not change loyalty.savings' do
+ assert_equal 15.to_money, @loyalty.savings
+ end
+
+ should 'not change loyalty.spend' do
+ assert_equal 65.to_money, @loyalty.spend
+ end
+
+ should 'not change loyalty.spend_volume' do
+ assert_equal 60.to_money, @loyalty.spend_volume
+ end
+
+ should 'not change loyalty.tip_volume' do
+ assert_equal 10, @loyalty.tip_volume
+ end
+
+ should 'not change loyalty.orders_completed_count' do
+ assert_equal 1, @loyalty.orders_completed_count
+ end
+
+ should 'not change user.credit' do
+ assert_equal 2.to_money, @user.credit
+ end
+
+ should 'not change value_remaining of first claim' do
+ assert_equal 1.to_money, @claim_one.reload.value_remaining
+ end
+
+ should 'not change value_remaining of second claim' do
+ assert_equal 0.to_money, @claim_two.reload.value_remaining
+ end
+
+ should 'not change refund_type' do
+ assert_equal 'braintree_refunded', @order.refund_type
+ end
+
+ should 'not change state' do
+ assert @order.refunded?
+ end
+
+ should 'not change refunded_at' do
+ assert_equal @original_refunded_at, @order.refunded_at
+ end
+
+ should_not_enqueue_delayed_job 'OrderRefundedJob'
+
+ should 'return false' do
+ assert !@result
+ end
+ end
+
+ context 'when state is completed' do
+ setup do
+ @order.spend = 5.to_money
+ @order.tip = 1.to_money
+ end
+
+ context 'and balance is zero' do
+ setup do
+ @order.balance = 0.to_money
+ @order.refund
+ end
+
+ should 'create a refund object with a correct refund type' do
+ assert_equal 'no_balance', @order.refund_object.refund_type
+ end
+
+ should 'set refund_type to no_balance' do
+ assert_equal 'no_balance', @order.refund_type
+ end
+ end
+
+ context 'and balance is nonzero' do
+ context 'and bundle is open' do
+ setup do
+ @order.bundle.stubs state: 'open'
+ @order.balance = 1.to_money
+ @order.refund
+ end
+
+ should 'create a refund object with a correct refund type' do
+ assert_equal 'bundle_refunded', @order.refund_object.refund_type
+ end
+
+ should 'set refund_type to bundle_refunded' do
+ assert_equal 'bundle_refunded', @order.refund_type
+ end
+ end
+
+ context 'and bundle is closed' do
+ setup do
+ stub_braintree_transaction_refund_success
+ @order.bundle.stubs state: 'closed'
+ @order.balance = 1.to_money
+ @order.refund
+ end
+
+ should 'create a refund object with a correct refund type' do
+ assert_equal 'braintree_refunded', @order.refund_object.refund_type
+ end
+
+ should 'set refund_type to braintree_refunded' do
+ assert_equal 'braintree_refunded', @order.refund_type
+ end
+ end
+ end
+
+ context 'and balance is nonzero and braintree refund fails and bundle is closed' do
+ setup do
+ stub_braintree_transaction_refund_validation_error
+ @order.bundle.stubs state: 'closed'
+ @order.balance = 1.to_money
+ @order.refund
+ end
+
+ should 'add user_credit and balance to user.credit' do
+ assert_equal 6.to_money, @user.credit
+ end
+
+ should 'create a refund object with a correct refund type' do
+ assert_equal 'user_credited', @order.refund_object.refund_type
+ end
+
+ should 'set refund_type to user_credited' do
+ assert_equal 'user_credited', @order.refund_type
+ end
+ end
+
+ context 'always' do
+ setup do
+ stub_braintree_transaction_refund_success
+ @time_with_zone = Time.zone.now
+ @order.spend = 50.to_money
+ @order.tip = 10.to_money
+
+ Timecop.freeze @time_with_zone do
+ @result = @order.refund
+ end
+ end
+
+ should 'add loyalty_credit to, then subtract earn from loyalty.credit' do
+ assert_equal 0.to_money, @loyalty.credit
+ end
+
+ should 'create a refund object' do
+ assert @order.refund_object
+ end
+
+ should 'set loyalty.first_order_created_at to nil' do
+ assert_nil @loyalty.first_order_created_at
+ end
+
+ should 'set loyalty.last_order_created_at to nil' do
+ assert_nil @loyalty.last_order_created_at
+ end
+
+ should 'subtract credit from loyalty.savings' do
+ assert_equal 0.to_money, @loyalty.savings
+ end
+
+ should 'subtract total from loyalty.spend' do
+ assert_equal 5.to_money, @loyalty.spend
+ end
+
+ should 'subtract spend from loyalty.spend_volume' do
+ assert_equal 10.to_money, @loyalty.spend_volume
+ end
+
+ should 'subtract tip from loyalty.tip_volume' do
+ assert_equal 0, @loyalty.tip_volume
+ end
+
+ should 'decrement loyalty.orders_completed_count by one' do
+ assert_equal 0, @loyalty.orders_completed_count
+ end
+
+ should 'add user_credit to user.credit' do
+ assert_equal 5.to_money, @user.credit
+ end
+
+ should 'add claim_credit to value_remaining of first claim' do
+ assert_equal 4.to_money, @claim_one.reload.value_remaining
+ end
+
+ should 'add claim_credit to value_remaining of second claim' do
+ assert_equal 5.to_money, @claim_two.reload.value_remaining
+ end
+
+ should 'set refund_type' do
+ assert_equal 'braintree_refunded', @order.refund_type
+ end
+
+ should 'mark the order as refunded' do
+ assert @order.refunded?
+ end
+
+ should 'set refunded_at' do
+ assert_equal @time_with_zone, @order.refunded_at
+ end
+
+ should_enqueue_delayed_job 'OrderRefundedJob' do
+ { order_id: @order.id }
+ end
+
+ should 'return true' do
+ assert @result
+ end
+ end
+ end
+ end
+
+end
diff --git a/test/unit/order_test.rb b/test/unit/order_test.rb
index 27a95fa..80ffcb8 100644
--- a/test/unit/order_test.rb
+++ b/test/unit/order_test.rb
@@ -1006,279 +1006,15 @@ class OrderTest < ActiveSupport::TestCase
end
end
- context 'refund' do
+ context 'refund!' do
setup do
- stub_braintree_customer_sale_success
- @order = create(:completed_order,
- balance: 45.to_money,
- claim_credit: 8.to_money,
- earn: 9.to_money,
- loyalty_credit: 3.to_money,
- spend: 50.to_money,
- tip: 10.to_money,
- user_credit: 3.to_money)
-
- @loyalty = @order.loyalty.tap do |loyalty|
- loyalty.credit = 5.to_money
- loyalty.first_order_created_at = @order.created_at
- loyalty.last_order_created_at = @order.created_at
- loyalty.orders_completed_count = 1
- loyalty.savings = 15.to_money
- loyalty.spend = 65.to_money
- loyalty.spend_volume = 60.to_money
- loyalty.tip_volume = 10.to_money
- loyalty.save!
- end
-
- @user = @order.user.tap do |user|
- user.credit = 2.to_money
- user.save!
- end
-
- @bundle = create(:bundle, user: @user)
- @bundle.orders << @order
-
- @claim_one = create(:claim)
- @claim_one.value_remaining = 1.to_money
- @claim_one.save!
- create :redemption, claim: @claim_one, order: @order, value: 3.to_money
-
- @claim_two = create(:claim)
- @claim_two.value_remaining = 0.to_money
- @claim_two.save!
- create :redemption, claim: @claim_two, order: @order, value: 5.to_money
- end
-
- context 'when state is refunded' do
- setup do
- @order.state = 'refunded'
- @order.refund_type = 'braintree_refunded'
- @order.refunded_at = Time.zone.now
- @order.save!
- @original_refunded_at = @order.refunded_at
- @result = @order.refund
- end
-
- should 'not change loyalty.credit' do
- assert_equal 5.to_money, @loyalty.credit
- end
-
- should 'not change loyalty.first_order_created_at' do
- assert_equal @order.created_at, @loyalty.first_order_created_at
- end
-
- should 'not change loyalty.last_order_created_at' do
- assert_equal @order.created_at, @loyalty.last_order_created_at
- end
-
- should 'not change loyalty.savings' do
- assert_equal 15.to_money, @loyalty.savings
- end
-
- should 'not change loyalty.spend' do
- assert_equal 65.to_money, @loyalty.spend
- end
-
- should 'not change loyalty.spend_volume' do
- assert_equal 60.to_money, @loyalty.spend_volume
- end
-
- should 'not change loyalty.tip_volume' do
- assert_equal 10, @loyalty.tip_volume
- end
-
- should 'not change loyalty.orders_completed_count' do
- assert_equal 1, @loyalty.orders_completed_count
- end
-
- should 'not change user.credit' do
- assert_equal 2.to_money, @user.credit
- end
-
- should 'not change value_remaining of first claim' do
- assert_equal 1.to_money, @claim_one.reload.value_remaining
- end
-
- should 'not change value_remaining of second claim' do
- assert_equal 0.to_money, @claim_two.reload.value_remaining
- end
-
- should 'not change refund_type' do
- assert_equal 'braintree_refunded', @order.refund_type
- end
-
- should 'not change state' do
- assert @order.refunded?
- end
-
- should 'not change refunded_at' do
- assert_equal @original_refunded_at, @order.refunded_at
- end
-
- should_not_enqueue_delayed_job 'OrderRefundedJob'
-
- should 'return false' do
- assert !@result
- end
+ @order = Order.new
+ @order_refunder = stub('order_refunder', :refund!)
+ @order.refund!(@order_refunder)
end
- context 'when state is completed' do
- setup do
- @order.spend = 5.to_money
- @order.tip = 1.to_money
- end
-
- context 'and balance is zero' do
- setup do
- @order.balance = 0.to_money
- @order.refund
- end
-
- should 'create a refund object with a correct refund type' do
- assert_equal 'no_balance', @order.refund_object.refund_type
- end
-
- should 'set refund_type to no_balance' do
- assert_equal 'no_balance', @order.refund_type
- end
- end
-
- context 'and balance is nonzero' do
- context 'and bundle is open' do
- setup do
- @order.bundle.stubs state: 'open'
- @order.balance = 1.to_money
- @order.refund
- end
-
- should 'create a refund object with a correct refund type' do
- assert_equal 'bundle_refunded', @order.refund_object.refund_type
- end
-
- should 'set refund_type to bundle_refunded' do
- assert_equal 'bundle_refunded', @order.refund_type
- end
- end
-
- context 'and bundle is closed' do
- setup do
- stub_braintree_transaction_refund_success
- @order.bundle.stubs state: 'closed'
- @order.balance = 1.to_money
- @order.refund
- end
-
- should 'create a refund object with a correct refund type' do
- assert_equal 'braintree_refunded', @order.refund_object.refund_type
- end
-
- should 'set refund_type to braintree_refunded' do
- assert_equal 'braintree_refunded', @order.refund_type
- end
- end
- end
-
- context 'and balance is nonzero and braintree refund fails and bundle is closed' do
- setup do
- stub_braintree_transaction_refund_validation_error
- @order.bundle.stubs state: 'closed'
- @order.balance = 1.to_money
- @order.refund
- end
-
- should 'add user_credit and balance to user.credit' do
- assert_equal 6.to_money, @user.credit
- end
-
- should 'create a refund object with a correct refund type' do
- assert_equal 'user_credited', @order.refund_object.refund_type
- end
-
- should 'set refund_type to user_credited' do
- assert_equal 'user_credited', @order.refund_type
- end
- end
-
- context 'always' do
- setup do
- stub_braintree_transaction_refund_success
- @time_with_zone = Time.zone.now
- @order.spend = 50.to_money
- @order.tip = 10.to_money
-
- Timecop.freeze @time_with_zone do
- @result = @order.refund
- end
- end
-
- should 'add loyalty_credit to, then subtract earn from loyalty.credit' do
- assert_equal 0.to_money, @loyalty.credit
- end
-
- should 'create a refund object' do
- assert @order.refund_object
- end
-
- should 'set loyalty.first_order_created_at to nil' do
- assert_nil @loyalty.first_order_created_at
- end
-
- should 'set loyalty.last_order_created_at to nil' do
- assert_nil @loyalty.last_order_created_at
- end
-
- should 'subtract credit from loyalty.savings' do
- assert_equal 0.to_money, @loyalty.savings
- end
-
- should 'subtract total from loyalty.spend' do
- assert_equal 5.to_money, @loyalty.spend
- end
-
- should 'subtract spend from loyalty.spend_volume' do
- assert_equal 10.to_money, @loyalty.spend_volume
- end
-
- should 'subtract tip from loyalty.tip_volume' do
- assert_equal 0, @loyalty.tip_volume
- end
-
- should 'decrement loyalty.orders_completed_count by one' do
- assert_equal 0, @loyalty.orders_completed_count
- end
-
- should 'add user_credit to user.credit' do
- assert_equal 5.to_money, @user.credit
- end
-
- should 'add claim_credit to value_remaining of first claim' do
- assert_equal 4.to_money, @claim_one.reload.value_remaining
- end
-
- should 'add claim_credit to value_remaining of second claim' do
- assert_equal 5.to_money, @claim_two.reload.value_remaining
- end
-
- should 'set refund_type' do
- assert_equal 'braintree_refunded', @order.refund_type
- end
-
- should 'mark the order as refunded' do
- assert @order.refunded?
- end
-
- should 'set refunded_at' do
- assert_equal @time_with_zone, @order.refunded_at
- end
-
- should_enqueue_delayed_job 'OrderRefundedJob' do
- { order_id: @order.id }
- end
-
- should 'return true' do
- assert @result
- end
- end
+ should 'call the expected methods on order refunder' do
+ assert_received @order_refunder, :refund!
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment