Skip to content

Commit 2cfd1fb

Browse files
committedJun 11, 2017
Continues organizing API and tests all existing methods
1 parent 130052a commit 2cfd1fb

11 files changed

+334
-53
lines changed
 

‎.DS_Store

6 KB
Binary file not shown.

‎.rubocop.yml

+5
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ AllCops:
33
- db/migrate/*
44
- lib/pay/version.rb
55
- test/dummy/**/*
6+
- test/models/subscription_test.rb
7+
- test/pay/billable_test.rb
68
- test/test_helper.rb
79

810
Documentation:
@@ -16,3 +18,6 @@ ClassVars:
1618

1719
SpecialGlobalVars:
1820
Enabled: false
21+
22+
AmbiguousBlockAssociation:
23+
Enabled: false

‎Gemfile.lock

+4
Original file line numberDiff line numberDiff line change
@@ -67,12 +67,15 @@ GEM
6767
nokogiri (>= 1.5.9)
6868
mail (2.6.6)
6969
mime-types (>= 1.16, < 4)
70+
metaclass (0.0.4)
7071
method_source (0.8.2)
7172
mime-types (3.1)
7273
mime-types-data (~> 3.2015)
7374
mime-types-data (3.2016.0521)
7475
mini_portile2 (2.2.0)
7576
minitest (5.10.2)
77+
mocha (1.2.1)
78+
metaclass (~> 0.0.1)
7679
multi_json (1.12.1)
7780
netrc (0.11.0)
7881
nio4r (2.1.0)
@@ -157,6 +160,7 @@ PLATFORMS
157160

158161
DEPENDENCIES
159162
byebug
163+
mocha
160164
pay!
161165
pry
162166
rubocop

‎app/models/subscription.rb

+3-4
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@ class Subscription < ApplicationRecord
99
validates :processor_plan, presence: true
1010
validates :quantity, presence: true
1111

12+
# Scopes
13+
scope :for_name, ->(name) { where(name: name) }
14+
1215
def on_trial?
1316
trial_ends_at? && Time.zone.now < trial_ends_at
1417
end
@@ -53,8 +56,4 @@ def resume
5356
def processor_subscription
5457
owner.processor_subscription(processor_id)
5558
end
56-
57-
def find_trial_ends_at(subscription)
58-
subscription.trial_end.present? ? Time.at(subscription.trial_end) : nil
59-
end
6059
end

‎lib/pay/billable.rb

+12-4
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ module Billable
1717
end
1818

1919
def customer(token = nil)
20+
check_for_processor
2021
send("#{processor}_customer", token)
2122
end
2223

@@ -26,10 +27,15 @@ def subscribe(name = 'default', plan = 'default', processor = 'stripe')
2627
end
2728

2829
def update_card(token)
29-
raise StandardError, 'No processor selected' unless processor
30+
check_for_processor
3031
send("update_#{processor}_card", token)
3132
end
3233

34+
def processor_subscription(subscription_id)
35+
check_for_processor
36+
send("#{processor}_subscription", subscription_id)
37+
end
38+
3339
def subscribed?(name = 'default', plan = nil)
3440
subscription = subscription(name)
3541

@@ -40,11 +46,13 @@ def subscribed?(name = 'default', plan = nil)
4046
end
4147

4248
def subscription(name = 'default')
43-
subscriptions.where(name: name).last
49+
subscriptions.for_name(name).last
4450
end
4551

46-
def processor_subscription(subscription_id)
47-
send("#{processor}_subscription", subscription_id)
52+
private
53+
54+
def check_for_processor
55+
raise StandardError, 'No processor selected' unless processor
4856
end
4957
end
5058
end

‎lib/pay/billable/stripe.rb

+1-1
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ def update_stripe_card(token)
2727
end
2828

2929
def stripe_subscription(subscription_id)
30-
Stripe::Subscription.retrieve(subscription_id)
30+
::Stripe::Subscription.retrieve(subscription_id)
3131
end
3232

3333
private

‎pay.gemspec

+3-2
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@ Gem::Specification.new do |s|
1010
s.authors = ['Jason Charnes']
1111
s.email = ['jason@thecharnes.com']
1212
s.homepage = 'https://github.com/jasoncharnes/pay'
13-
s.summary = 'A wrapper for handing subscriptions in Rails.'
14-
s.description = 'A wrapper for handing subscriptions in Rails.'
13+
s.summary = 'A Ruby on Rails subscription engine.'
14+
s.description = 'A Ruby on Rails subscription engine.'
1515
s.license = 'MIT'
1616

1717
s.files = Dir[
@@ -26,6 +26,7 @@ Gem::Specification.new do |s|
2626
s.add_dependency 'braintree', '~> 2.75'
2727

2828
s.add_development_dependency 'byebug'
29+
s.add_development_dependency 'mocha'
2930
s.add_development_dependency 'pry'
3031
s.add_development_dependency 'rubocop'
3132
s.add_development_dependency 'sqlite3'

‎test/models/subscription_test.rb

+101
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,31 @@ class Pay::Subscription::Test < ActiveSupport::TestCase
1010
assert klass, 'User'
1111
end
1212

13+
test '.for_name(name) scope' do
14+
owner = User.create
15+
16+
subscription1 = Subscription.create!(
17+
name: 'default',
18+
owner: owner,
19+
processor: 'stripe',
20+
processor_id: '1',
21+
processor_plan: 'default',
22+
quantity: '1'
23+
)
24+
25+
subscription2 = Subscription.create!(
26+
name: 'superior',
27+
owner: owner,
28+
processor: 'stripe',
29+
processor_id: '1',
30+
processor_plan: 'superior',
31+
quantity: '1'
32+
)
33+
34+
assert_includes Subscription.for_name('default'), subscription1
35+
refute_includes Subscription.for_name('default'), subscription2
36+
end
37+
1338
test 'active trial' do
1439
@subscription.trial_ends_at = 5.minutes.from_now
1540
assert @subscription.on_trial?
@@ -25,6 +50,16 @@ class Pay::Subscription::Test < ActiveSupport::TestCase
2550
refute @subscription.on_trial?
2651
end
2752

53+
test 'cancelled' do
54+
@subscription.ends_at = 1.week.ago
55+
assert @subscription.cancelled?
56+
end
57+
58+
test 'not cancelled' do
59+
@subscription.ends_at = nil
60+
refute @subscription.cancelled?
61+
end
62+
2863
test 'on grace period' do
2964
@subscription.ends_at = 5.minutes.from_now
3065
assert @subscription.on_grace_period?
@@ -51,4 +86,70 @@ class Pay::Subscription::Test < ActiveSupport::TestCase
5186
@subscription.trial_ends_at = nil
5287
refute @subscription.active?
5388
end
89+
90+
test 'cancel' do
91+
expiration = 2.weeks.from_now
92+
93+
cancelled_stripe = mock('cancelled_stripe_subscription')
94+
cancelled_stripe.expects(:current_period_end).returns(expiration)
95+
96+
stripe_sub = mock('stripe_subscription')
97+
stripe_sub.expects(:delete).returns(cancelled_stripe)
98+
99+
@subscription.stubs(:processor_subscription).returns(stripe_sub)
100+
@subscription.cancel
101+
102+
assert @subscription.ends_at, expiration
103+
end
104+
105+
test 'cancel_now!' do
106+
expiration = DateTime.now
107+
108+
cancelled_stripe = mock('cancelled_stripe_subscription')
109+
cancelled_stripe.expects(:current_period_end).returns(expiration)
110+
111+
stripe_sub = mock('stripe_subscription')
112+
stripe_sub.expects(:delete).returns(cancelled_stripe)
113+
114+
@subscription.stubs(:processor_subscription).returns(stripe_sub)
115+
@subscription.cancel_now!
116+
117+
assert @subscription.ends_at, expiration
118+
end
119+
120+
test 'resume on grace period' do
121+
@subscription.ends_at = 2.weeks.from_now
122+
123+
stripe_sub = mock('stripe_subscription')
124+
stripe_sub.expects(:plan=)
125+
stripe_sub.expects(:trial_end=)
126+
stripe_sub.expects(:save).returns(true)
127+
128+
@subscription.processor_plan = 'default'
129+
130+
@subscription.stubs(:on_grace_period?).returns(true)
131+
@subscription.stubs(:processor_subscription).returns(stripe_sub)
132+
@subscription.stubs(:on_trial?).returns(false)
133+
134+
@subscription.resume
135+
136+
assert_nil @subscription.ends_at
137+
end
138+
139+
test 'resume off grace period' do
140+
@subscription.stubs(:on_grace_period?).returns(false)
141+
142+
assert_raises StandardError do
143+
@subscription.resume
144+
end
145+
end
146+
147+
test 'processor subscription' do
148+
user = mock('user')
149+
user.expects(:processor_subscription).returns(:result)
150+
151+
@subscription.stubs(:owner).returns(user)
152+
153+
assert :result, @subscription.processor_subscription
154+
end
54155
end

‎test/pay/billable/stripe_test.rb

+99
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
require 'test_helper'
2+
require 'stripe_mock'
3+
require 'minitest/mock'
4+
5+
class Pay::Billable::Stripe::Test < ActiveSupport::TestCase
6+
setup do
7+
StripeMock.start
8+
9+
@billable = User.new
10+
11+
@stripe_helper = StripeMock.create_test_helper
12+
@stripe_helper.create_plan(id: 'test-monthly', amount: 1500)
13+
end
14+
15+
teardown do
16+
StripeMock.stop
17+
end
18+
19+
test 'getting a stripe customer with a processor id' do
20+
customer = Stripe::Customer.create(
21+
email: 'johnny@appleseed.com',
22+
card: @stripe_helper.generate_card_token
23+
)
24+
25+
@billable.processor_id = customer.id
26+
27+
assert_equal @billable.stripe_customer, customer
28+
end
29+
30+
test 'getting a stripe customer without a processor id' do
31+
assert_nil @billable.processor
32+
assert_nil @billable.processor_id
33+
34+
@billable.email = 'gob.bluth@example.com'
35+
@billable.card_token = @stripe_helper.generate_card_token(
36+
brand: 'Visa',
37+
last4: '9191',
38+
exp_year: 1984
39+
)
40+
41+
@billable.stripe_customer
42+
43+
assert_equal @billable.processor, 'stripe'
44+
assert_not_nil @billable.processor_id
45+
end
46+
47+
test 'can create a subscription' do
48+
@billable.card_token = @stripe_helper.generate_card_token(
49+
brand: 'Visa',
50+
last4: '9191',
51+
exp_year: 1984
52+
)
53+
@billable.subscribe('default', 'test-monthly')
54+
55+
assert @billable.subscribed?
56+
assert_equal 'default', @billable.subscription.name
57+
assert_equal 'test-monthly', @billable.subscription.processor_plan
58+
end
59+
60+
test 'can update their card' do
61+
customer = Stripe::Customer.create(
62+
email: 'johnny@appleseed.com',
63+
card: @stripe_helper.generate_card_token
64+
)
65+
66+
@billable.stubs(:customer).returns(customer)
67+
card = @stripe_helper.generate_card_token(brand: 'Visa', last4: '4242')
68+
@billable.processor = 'stripe'
69+
@billable.update_card(card)
70+
71+
assert @billable.card_brand == 'Visa'
72+
assert @billable.card_last4 == '4242'
73+
74+
card = @stripe_helper.generate_card_token(
75+
brand: 'Discover',
76+
last4: '1117'
77+
)
78+
@billable.update_card(card)
79+
80+
assert @billable.card_brand == 'Discover'
81+
assert @billable.card_last4 == '1117'
82+
end
83+
84+
test 'retriving a stripe subscription' do
85+
@stripe_helper.create_plan(id: 'default', amount: 1500)
86+
87+
customer = Stripe::Customer.create(
88+
email: 'johnny@appleseed.com',
89+
source: @stripe_helper.generate_card_token(brand: 'Visa', last4: '4242')
90+
)
91+
92+
subscription = Stripe::Subscription.create(
93+
plan: 'default',
94+
customer: customer.id
95+
)
96+
97+
assert_equal @billable.stripe_subscription(subscription.id), subscription
98+
end
99+
end

‎test/pay/billable_test.rb

+103-41
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,8 @@
11
require 'test_helper'
2-
require 'stripe_mock'
3-
require 'minitest/mock'
42

53
class Pay::Billable::Test < ActiveSupport::TestCase
64
setup do
7-
StripeMock.start
8-
95
@billable = User.new
10-
@stripe_helper = StripeMock.create_test_helper
11-
@stripe_helper.create_plan(id: 'test-monthly', amount: 1500)
12-
end
13-
14-
teardown do
15-
StripeMock.stop
166
end
177

188
test 'truth' do
@@ -23,48 +13,120 @@ class Pay::Billable::Test < ActiveSupport::TestCase
2313
assert @billable.respond_to?(:subscriptions)
2414
end
2515

26-
test 'can create a subscription' do
27-
@billable.card_token = @stripe_helper.generate_card_token(
28-
brand: 'Visa',
29-
last4: '9191',
30-
exp_year: 1984
31-
)
32-
@billable.subscribe('default', 'test-monthly')
16+
test 'customer with stripe processor' do
17+
@billable.processor = 'stripe'
18+
@billable.expects(:stripe_customer).returns(:user)
19+
assert_equal :user, @billable.customer
20+
end
3321

34-
assert @billable.subscribed?
35-
assert @billable.subscription.name == 'default'
36-
assert @billable.subscription.processor_plan == 'test-monthly'
22+
test 'customer with undefined processor' do
23+
@billable.processor = 'pants'
24+
25+
assert_raises NoMethodError do
26+
@billable.customer
27+
end
3728
end
3829

39-
test 'cannot update their card without a prcocessor' do
40-
assert_raise StandardError do
41-
card = @stripe_helper.generate_card_token(brand: 'Visa', last4: '4242')
42-
@billable.update_card(card)
30+
test 'customer without processor' do
31+
assert_raises StandardError do
32+
@billable.customer
4333
end
4434
end
4535

46-
test 'can update their card' do
47-
customer = Stripe::Customer.create(
48-
email: 'johnny@appleseed.com',
49-
card: @stripe_helper.generate_card_token
36+
test 'subscribing a stripe customer' do
37+
@billable.expects(:create_stripe_subscription)
38+
.with('default', 'default')
39+
.returns(:user)
40+
41+
assert_equal :user, @billable.subscribe
42+
assert @billable.processor = 'stripe'
43+
end
44+
45+
test 'updating a stripe card' do
46+
@billable.processor = 'stripe'
47+
@billable.expects(:update_stripe_card).with('a1b2c3').returns(:card)
48+
49+
assert_equal :card, @billable.update_card('a1b2c3')
50+
end
51+
52+
test 'updating a card without a processor' do
53+
assert_raises StandardError do
54+
@billable.update_card('whoops')
55+
end
56+
end
57+
58+
test 'checking for a subscription without one' do
59+
@billable.stubs(:subscription).returns(nil)
60+
refute @billable.subscribed?
61+
end
62+
63+
test 'checking for a subscription with no plan and active subscription' do
64+
subscription = mock('subscription')
65+
subscription.stubs(:active?).returns(true)
66+
@billable.stubs(:subscription).returns(subscription)
67+
68+
assert @billable.subscribed?
69+
end
70+
71+
test 'checking for a subscription with no plan and inactive subscription' do
72+
subscription = mock('subscription')
73+
subscription.stubs(:active?).returns(false)
74+
@billable.stubs(:subscription).returns(subscription)
75+
76+
refute @billable.subscribed?
77+
end
78+
79+
test 'checking for a subscription that is inactive' do
80+
subscription = mock('subscription')
81+
subscription.stubs(:active?).returns(false)
82+
@billable.stubs(:subscription).returns(subscription)
83+
84+
refute @billable.subscribed?('default', 'default')
85+
end
86+
87+
test 'checking for a subscription that is active for another plan' do
88+
subscription = mock('subscription')
89+
subscription.stubs(:active?).returns(true)
90+
subscription.stubs(:plan).returns('superior')
91+
@billable.stubs(:subscription).returns(subscription)
92+
93+
refute @billable.subscribed?('default', 'default')
94+
end
95+
96+
test 'checking for a subscription that is active for a provided plan' do
97+
subscription = mock('subscription')
98+
subscription.stubs(:active?).returns(true)
99+
subscription.stubs(:plan).returns('default')
100+
@billable.stubs(:subscription).returns(subscription)
101+
102+
assert @billable.subscribed?('default', 'default')
103+
end
104+
105+
test 'getting a subscription by default name' do
106+
subscription = Subscription.create!(
107+
name: 'default',
108+
owner: @billable,
109+
processor: 'stripe',
110+
processor_id: '1',
111+
processor_plan: 'default',
112+
quantity: '1'
50113
)
51114

52-
@billable.stub :customer, customer do
53-
card = @stripe_helper.generate_card_token(brand: 'Visa', last4: '4242')
54-
@billable.processor = 'stripe'
55-
@billable.update_card(card)
115+
assert_equal subscription, @billable.subscription
116+
end
56117

57-
assert @billable.card_brand == 'Visa'
58-
assert @billable.card_last4 == '4242'
118+
test 'getting a stripe subscription' do
119+
@billable.processor = 'stripe'
120+
@billable.expects(:stripe_subscription)
121+
.with('123')
122+
.returns(:subscription)
59123

60-
card = @stripe_helper.generate_card_token(
61-
brand: 'Discover',
62-
last4: '1117'
63-
)
64-
@billable.update_card(card)
124+
assert_equal :subscription, @billable.processor_subscription('123')
125+
end
65126

66-
assert @billable.card_brand == 'Discover'
67-
assert @billable.card_last4 == '1117'
127+
test 'getting a processor subscription without a processor' do
128+
assert_raises StandardError do
129+
@billable.processor_subscription('123')
68130
end
69131
end
70132
end

‎test/test_helper.rb

+3-1
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,10 @@
2323
end
2424

2525
require 'braintree'
26-
2726
Braintree::Configuration.environment = :development
2827
Braintree::Configuration.merchant_id = "integration_merchant_id"
2928
Braintree::Configuration.public_key = "integration_public_key"
3029
Braintree::Configuration.private_key = "integration_private_key"
30+
31+
require 'minitest/mock'
32+
require 'mocha/mini_test'

0 commit comments

Comments
 (0)
Please sign in to comment.