freemium-ajb 0.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (93) hide show
  1. data/.coveralls.yml +1 -0
  2. data/.gitignore +27 -0
  3. data/.rspec +1 -0
  4. data/.ruby-gemset +1 -0
  5. data/.ruby-version +1 -0
  6. data/.travis.yml +6 -0
  7. data/Gemfile +4 -0
  8. data/LICENSE.md +20 -0
  9. data/README.md +1 -0
  10. data/app/mailers/freemium_mailer.rb +36 -0
  11. data/app/views/subscription_mailer/admin_report.text.erb +4 -0
  12. data/app/views/subscription_mailer/expiration_notice.text.erb +1 -0
  13. data/app/views/subscription_mailer/expiration_warning.text.erb +1 -0
  14. data/app/views/subscription_mailer/invoice.text.erb +5 -0
  15. data/freemium.gemspec +29 -0
  16. data/lib/freemium.rb +25 -0
  17. data/lib/freemium/configuration.rb +27 -0
  18. data/lib/freemium/coupon.rb +37 -0
  19. data/lib/freemium/coupon_redemption.rb +59 -0
  20. data/lib/freemium/credit_card.rb +222 -0
  21. data/lib/freemium/engine.rb +7 -0
  22. data/lib/freemium/gateways/base.rb +65 -0
  23. data/lib/freemium/gateways/brain_tree.rb +175 -0
  24. data/lib/freemium/gateways/test.rb +36 -0
  25. data/lib/freemium/rates.rb +33 -0
  26. data/lib/freemium/response.rb +24 -0
  27. data/lib/freemium/subscription.rb +384 -0
  28. data/lib/freemium/subscription_change.rb +20 -0
  29. data/lib/freemium/subscription_plan.rb +26 -0
  30. data/lib/freemium/testing/app/controllers/application_controller.rb +7 -0
  31. data/lib/freemium/testing/application.rb +46 -0
  32. data/lib/freemium/testing/config/database.yml +11 -0
  33. data/lib/freemium/testing/config/routes.rb +3 -0
  34. data/lib/freemium/transaction.rb +15 -0
  35. data/lib/freemium/version.rb +3 -0
  36. data/lib/generators/freemium/install/install_generator.rb +58 -0
  37. data/lib/generators/freemium/install/templates/db/migrate/create_coupon_redemptions.rb +18 -0
  38. data/lib/generators/freemium/install/templates/db/migrate/create_coupons.rb +28 -0
  39. data/lib/generators/freemium/install/templates/db/migrate/create_credit_cards.rb +14 -0
  40. data/lib/generators/freemium/install/templates/db/migrate/create_subscription_changes.rb +21 -0
  41. data/lib/generators/freemium/install/templates/db/migrate/create_subscription_plans.rb +14 -0
  42. data/lib/generators/freemium/install/templates/db/migrate/create_subscriptions.rb +31 -0
  43. data/lib/generators/freemium/install/templates/db/migrate/create_transactions.rb +17 -0
  44. data/lib/generators/freemium/install/templates/freemium.rb +16 -0
  45. data/lib/generators/freemium/install/templates/models/coupon.rb +3 -0
  46. data/lib/generators/freemium/install/templates/models/coupon_redemption.rb +3 -0
  47. data/lib/generators/freemium/install/templates/models/credit_card.rb +3 -0
  48. data/lib/generators/freemium/install/templates/models/subscription.rb +3 -0
  49. data/lib/generators/freemium/install/templates/models/subscription_change.rb +3 -0
  50. data/lib/generators/freemium/install/templates/models/subscription_plan.rb +3 -0
  51. data/lib/generators/freemium/install/templates/models/transaction.rb +3 -0
  52. data/lib/generators/views/USAGE +3 -0
  53. data/lib/generators/views/views_generator.rb +39 -0
  54. data/lib/tasks/freemium.rake +17 -0
  55. data/spec/dummy/Rakefile +7 -0
  56. data/spec/dummy/app/controllers/application_controller.rb +3 -0
  57. data/spec/dummy/app/helpers/application_helper.rb +2 -0
  58. data/spec/dummy/app/mailers/mailers.rb +1 -0
  59. data/spec/dummy/app/models/models.rb +31 -0
  60. data/spec/dummy/app/views/layouts/application.html.erb +14 -0
  61. data/spec/dummy/config.ru +4 -0
  62. data/spec/dummy/config/application.rb +45 -0
  63. data/spec/dummy/config/boot.rb +10 -0
  64. data/spec/dummy/config/database.yml +21 -0
  65. data/spec/dummy/config/environment.rb +6 -0
  66. data/spec/dummy/config/environments/development.rb +26 -0
  67. data/spec/dummy/config/environments/production.rb +49 -0
  68. data/spec/dummy/config/environments/test.rb +36 -0
  69. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  70. data/spec/dummy/config/initializers/freemium.rb +16 -0
  71. data/spec/dummy/config/initializers/inflections.rb +10 -0
  72. data/spec/dummy/config/initializers/mem_db.rb +12 -0
  73. data/spec/dummy/config/initializers/mime_types.rb +5 -0
  74. data/spec/dummy/config/initializers/secret_token.rb +7 -0
  75. data/spec/dummy/config/initializers/session_store.rb +8 -0
  76. data/spec/dummy/config/locales/en.yml +5 -0
  77. data/spec/dummy/config/routes.rb +58 -0
  78. data/spec/dummy/db/schema.rb +90 -0
  79. data/spec/dummy/script/rails +6 -0
  80. data/spec/fixtures/credit_cards.yml +11 -0
  81. data/spec/fixtures/subscription_plans.yml +15 -0
  82. data/spec/fixtures/subscriptions.yml +28 -0
  83. data/spec/fixtures/users.yml +16 -0
  84. data/spec/lib/tasks/run_billing_rake_spec.rb +14 -0
  85. data/spec/models/coupon_redemption_spec.rb +287 -0
  86. data/spec/models/credit_card_spec.rb +124 -0
  87. data/spec/models/manual_billing_spec.rb +165 -0
  88. data/spec/models/subscription_plan_spec.rb +46 -0
  89. data/spec/models/subscription_spec.rb +386 -0
  90. data/spec/spec_helper.rb +19 -0
  91. data/spec/support/helpers.rb +18 -0
  92. data/spec/support/shared_contexts/rake.rb +19 -0
  93. metadata +270 -0
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+ # This command will automatically be run when you run "rails" with Rails 3 gems installed from the root of your application.
3
+
4
+ APP_PATH = File.expand_path('../../config/application', __FILE__)
5
+ require File.expand_path('../../config/boot', __FILE__)
6
+ require 'rails/commands'
@@ -0,0 +1,11 @@
1
+ bobs_credit_card:
2
+ id: 1
3
+ expiration_date: <%= (Time.now + 3.years).to_s :db %>
4
+ display_number: xxxx-xxxx-xxxx-2222
5
+ card_type: master
6
+ steves_credit_card:
7
+ id: 2
8
+ expiration_date: <%= (Time.now + 3.years).to_s :db %>
9
+ display_number: xxxx-xxxx-xxxx-1111
10
+ card_type: visa
11
+
@@ -0,0 +1,15 @@
1
+ free:
2
+ id: 1
3
+ key: free
4
+ name: Free Access
5
+ rate_cents: 0
6
+ basic:
7
+ id: 2
8
+ key: basic
9
+ name: Basic
10
+ rate_cents: 1295
11
+ premium:
12
+ id: 3
13
+ key: premium
14
+ name: Premium
15
+ rate_cents: 2495
@@ -0,0 +1,28 @@
1
+ bobs_subscription:
2
+ id: 1
3
+ subscribable_id: 1
4
+ subscribable_type: User
5
+ subscription_plan_id: 2
6
+ credit_card_id: 1
7
+ paid_through: <%= (Date.today + 20).to_s :db %>
8
+ billing_key: tc00001
9
+ started_on: <%= (Date.today - 60).to_s :db %>
10
+ last_transaction_at: 2007-09-15 12:34:56
11
+ sues_subscription:
12
+ id: 2
13
+ subscribable_id: 2
14
+ subscribable_type: User
15
+ subscription_plan_id: 1
16
+ paid_through:
17
+ last_transaction_at:
18
+ started_on: <%= (Date.today - 60).to_s :db %>
19
+ steves_subscription:
20
+ id: 3
21
+ subscribable_id: 4
22
+ subscribable_type: User
23
+ subscription_plan_id: 3
24
+ credit_card_id: 1
25
+ paid_through: <%= (Date.today + 20).to_s :db %>
26
+ billing_key: tc00002
27
+ started_on: <%= (Date.today - 60).to_s :db %>
28
+ last_transaction_at: 2007-09-15 12:34:56
@@ -0,0 +1,16 @@
1
+ bob:
2
+ id: 1
3
+ name: Bob
4
+ email: bob@example.com
5
+ sue:
6
+ id: 2
7
+ name: Sue
8
+ email: sue@example.com
9
+ sally:
10
+ id: 3
11
+ name: Sally
12
+ email: sally@example.com
13
+ steve:
14
+ id: 4
15
+ name: Steve
16
+ email: steve@example.com
@@ -0,0 +1,14 @@
1
+ require 'spec_helper'
2
+
3
+ describe "freemium:run_billing" do
4
+ include_context "rake"
5
+
6
+ before do
7
+ end
8
+
9
+ # its(:prerequisites) { should include("environment") }
10
+
11
+ it "generates a registrations report" do
12
+ subject.invoke
13
+ end
14
+ end
@@ -0,0 +1,287 @@
1
+ require 'spec_helper'
2
+
3
+ describe CouponRedemption do
4
+ fixtures :users, :subscriptions, :subscription_plans, :credit_cards
5
+
6
+ before(:each) do
7
+ @subscription = subscriptions(:bobs_subscription)
8
+ @original_price = @subscription.rate
9
+ @coupon = Coupon.create(:description => "30% off", :discount_percentage => 30, :redemption_key => "30OFF")
10
+ end
11
+
12
+ it "should be applied" do
13
+ @subscription.paid_through = Date.today + 30
14
+ @original_remaining_value = @subscription.remaining_value
15
+ @original_daily_rate = @subscription.daily_rate
16
+ @subscription.coupon_key = nil
17
+
18
+ @subscription.coupon_redemptions.create(:coupon => @coupon).should be_true
19
+ @subscription.rate.cents.should eql(@coupon.discount(@original_price).cents)
20
+ @subscription.daily_rate.cents.should eql(@coupon.discount(@original_daily_rate).cents)
21
+ @subscription.remaining_value.cents.should eql((@coupon.discount(@original_daily_rate) * @subscription.remaining_days).cents)
22
+ end
23
+
24
+ it "should be applied using coupon accessor" do
25
+ @subscription = build_subscription(:coupon => @coupon, :credit_card => CreditCard.sample)
26
+ @subscription.save!
27
+
28
+ @subscription.coupon.should_not be_nil
29
+ @subscription.coupon_redemptions.first.coupon.should_not be_nil
30
+ @subscription.coupon_redemptions.first.subscription.should_not be_nil
31
+ @subscription.coupon_redemptions.empty?.should be_false
32
+ @subscription.rate.cents.should eql(@coupon.discount(@subscription.subscription_plan.rate).cents)
33
+ end
34
+
35
+ it "should be applied using coupon key accessor" do
36
+ @subscription = build_subscription(:coupon_key => @coupon.redemption_key, :credit_card => CreditCard.sample)
37
+ @subscription.save!
38
+
39
+ @subscription.coupon.should_not be_nil
40
+ @subscription.coupon_redemptions.first.coupon.should_not be_nil
41
+ @subscription.coupon_redemptions.first.subscription.should_not be_nil
42
+ @subscription.coupon_redemptions.empty?.should be_false
43
+ @subscription.rate.cents.should eql(@coupon.discount(@subscription.subscription_plan.rate).cents)
44
+ end
45
+
46
+ it "should be applied multiple" do
47
+ @coupon_1 = Coupon.new(:description => "10% off", :discount_percentage => 10)
48
+ @subscription.coupon_redemptions.create(:coupon => @coupon_1).should be_true
49
+
50
+ @coupon_2 = Coupon.new(:description => "30% off", :discount_percentage => 30)
51
+ @subscription.coupon_redemptions.create(:coupon => @coupon_2).should be_true
52
+
53
+ @coupon_3 = Coupon.new(:description => "20% off", :discount_percentage => 20)
54
+ @subscription.coupon_redemptions.create(:coupon => @coupon_3).should be_true
55
+
56
+ # Should use the highest discounted coupon
57
+ @subscription.rate.cents.should eql(@coupon_2.discount(@original_price).cents)
58
+ end
59
+
60
+ it "should be destroyed" do
61
+ @subscription.coupon_redemptions.create(:coupon => @coupon).should be_true
62
+ @subscription.rate.cents.should eql(@coupon.discount(@original_price).cents)
63
+
64
+ @coupon.destroy
65
+ @subscription.reload
66
+
67
+ @subscription.coupon_redemptions.empty?.should be_true
68
+ @subscription.rate.cents.should eql(@original_price.cents)
69
+ end
70
+
71
+ it "should test coupon duration" do
72
+ @subscription.coupon_redemptions.create(:coupon => @coupon).should be_true
73
+ @subscription.rate.cents.should eql(@coupon.discount(@original_price).cents)
74
+
75
+ @coupon.duration_in_months = 3
76
+ @coupon.save!
77
+
78
+ @subscription.rate({:date => (Date.today + 3.months - 1)}).cents.should eql(@coupon.discount(@original_price).cents)
79
+ @subscription.rate({:date => (Date.today + 3.months + 1)}).cents.should eql(@original_price.cents)
80
+
81
+ safe_date = Date.today + 3.months - 1
82
+ Date.stub!(:today => safe_date)
83
+ @subscription.rate.cents.should eql(@coupon.discount(@original_price).cents)
84
+
85
+ safe_date = Date.today + 1
86
+ Date.stub!(:today => safe_date)
87
+ @subscription.rate.cents.should eql(@coupon.discount(@original_price).cents)
88
+
89
+ safe_date = Date.today + 1
90
+ Date.stub!(:today => safe_date)
91
+ @subscription.rate.cents.should eql(@original_price.cents)
92
+ end
93
+
94
+ it "should be applied complimentary" do
95
+ @coupon.discount_percentage = 100
96
+
97
+ @subscription = build_subscription(:coupon => @coupon, :credit_card => CreditCard.sample, :subscription_plan => subscription_plans(:premium))
98
+
99
+ @subscription.save.should be_true
100
+ @subscription.coupon.should_not be_nil
101
+ @subscription.rate.cents.should eql(0)
102
+ @subscription.paid?.should be_false
103
+ end
104
+
105
+ ##
106
+ ## Plan-specific coupons
107
+ ##
108
+
109
+ it "should be applied only on new premium plan" do
110
+ set_coupon_to_premium_only
111
+
112
+ @subscription = build_subscription(:coupon => @coupon, :credit_card => CreditCard.sample,
113
+ :subscription_plan => subscription_plans(:premium))
114
+
115
+ @subscription.save.should be_true
116
+ @subscription.coupon.should_not be_nil
117
+
118
+ @subscription = build_subscription(:coupon => @coupon, :credit_card => CreditCard.sample,
119
+ :subscription_plan => subscription_plans(:basic))
120
+
121
+ @subscription.save.should be_false
122
+ @subscription.should have(1).errors_on(:coupon_redemptions)
123
+ end
124
+
125
+ it "should be applied only on existent premium plan" do
126
+ set_coupon_to_premium_only
127
+
128
+ @subscription.coupon = @coupon
129
+
130
+ @subscription.subscription_plan.should eql(subscription_plans(:basic))
131
+ @subscription.save.should be_false
132
+ @subscription.should have(1).errors_on(:coupon_redemptions)
133
+
134
+ @subscription.subscription_plan = subscription_plans(:premium)
135
+
136
+ @subscription.subscription_plan.should eql(subscription_plans(:premium))
137
+ @subscription.save.should be_true
138
+ @subscription.coupon.should_not be_nil
139
+ end
140
+
141
+ it "should automatically upgrade the plan, as long as discount percentage is 100%" do
142
+ hundred_coupon = Coupon.create(:description => "100% off", :discount_percentage => 100, :redemption_key => "100OFF")
143
+ hundred_coupon.subscription_plans << subscription_plans(:premium)
144
+
145
+ @subscription.coupon = hundred_coupon
146
+
147
+ @subscription.subscription_plan.should eql(subscription_plans(:premium))
148
+ @subscription.save.should be_true
149
+ @subscription.rate.cents.should eql(0)
150
+ end
151
+
152
+ it "should not automatically downgrade the plan, no matter what" do
153
+ @subscription.subscription_plan = subscription_plans(:premium)
154
+
155
+ hundred_coupon = Coupon.create(:description => "100% off", :discount_percentage => 100, :redemption_key => "100OFF")
156
+ hundred_coupon.subscription_plans << subscription_plans(:basic)
157
+
158
+ @subscription.coupon = hundred_coupon
159
+
160
+ @subscription.subscription_plan.should eql(subscription_plans(:premium))
161
+ @subscription.save.should be_false
162
+ @subscription.should have(1).errors_on(:coupon_redemptions)
163
+ end
164
+
165
+ it 'should remove the coupon when upgrading the subscription plan' do
166
+ @subscription.subscription_plan = subscription_plans(:free)
167
+
168
+ hundred_coupon = Coupon.create(:description => "100% off", :discount_percentage => 100, :redemption_key => "100OFF")
169
+ hundred_coupon.subscription_plans << subscription_plans(:basic)
170
+
171
+ @subscription.coupon = hundred_coupon
172
+
173
+ @subscription.subscription_plan.should eql(subscription_plans(:basic))
174
+ @subscription.save.should be_true
175
+ @subscription.rate.cents.should eql(0)
176
+
177
+ cc = CreditCard.sample
178
+ cc.save
179
+
180
+ @subscription.update_attributes(
181
+ subscription_plan_id: subscription_plans(:premium).id,
182
+ credit_card: cc
183
+ )
184
+
185
+ @subscription.reload
186
+
187
+ @subscription.credit_card.should eql(cc)
188
+ @subscription.rate.cents.should_not eql(0)
189
+ @subscription.paid_through.should_not eql(nil)
190
+ @subscription.coupon.should eql(nil)
191
+ end
192
+
193
+ ##
194
+ ## applying coupons
195
+ ##
196
+
197
+ it "should be applied to subscription" do
198
+ @subscription.coupon = @coupon
199
+ @subscription.should be_valid
200
+ @subscription.coupon.should_not be_nil
201
+ end
202
+
203
+ it "should validate coupon key" do
204
+ @subscription.coupon_key = @coupon.redemption_key + "xxxxx"
205
+
206
+ @subscription.should_not be_valid
207
+ @subscription.coupon.should be_nil
208
+ @subscription.should have(1).errors_on(:coupon)
209
+ end
210
+
211
+ it "should validate coupon by coupon plan" do
212
+ set_coupon_to_premium_only
213
+
214
+ @subscription.subscription_plan.should_not eql(subscription_plans(:premium))
215
+
216
+ @subscription.coupon = @coupon
217
+ @subscription.should_not be_valid
218
+ @subscription.should have(1).errors_on(:coupon_redemptions)
219
+ end
220
+
221
+ protected
222
+
223
+ def set_coupon_to_premium_only
224
+ @coupon.subscription_plans << subscription_plans(:premium)
225
+ @coupon.save!
226
+ end
227
+
228
+
229
+ public
230
+
231
+ ##
232
+ ## Validation Tests
233
+ ##
234
+
235
+ it "should validate coupon" do
236
+ s = CouponRedemption.new(:subscription => subscriptions(:bobs_subscription))
237
+ s.save.should be_false
238
+ s.should have(1).errors_on(:coupon)
239
+ end
240
+
241
+ it "should not be applied to unpaid subscription" do
242
+ subscriptions(:sues_subscription).paid?.should be_false
243
+ s = CouponRedemption.new(:subscription => subscriptions(:sues_subscription), :coupon => @coupon)
244
+ s.save.should be_false
245
+ s.should have(1).errors_on(:subscription)
246
+ end
247
+
248
+ it "should not be applied twice" do
249
+ s = CouponRedemption.new(:subscription => subscriptions(:bobs_subscription), :coupon => @coupon)
250
+ s.save.should be_true
251
+
252
+ s = CouponRedemption.new(:subscription => subscriptions(:bobs_subscription), :coupon => @coupon)
253
+ s.save.should be_false
254
+ s.should have(1).errors_on(:coupon_id)
255
+ end
256
+
257
+ it "should not be applied with redemption expired coupon" do
258
+ @coupon.redemption_expiration = Date.today-1
259
+ @coupon.save!
260
+
261
+ s = CouponRedemption.new(:subscription => subscriptions(:bobs_subscription), :coupon => @coupon)
262
+ s.save.should be_false
263
+ s.should have(1).errors_on(:coupon)
264
+ end
265
+
266
+ it "should not be applied with too many redemptions" do
267
+ @coupon.redemption_limit = 1
268
+ @coupon.save!
269
+
270
+ s = CouponRedemption.new(:subscription => subscriptions(:bobs_subscription), :coupon => @coupon)
271
+ s.save!
272
+
273
+ s = CouponRedemption.new(:subscription => subscriptions(:steves_subscription), :coupon => @coupon)
274
+ s.save.should be_false
275
+ s.should have(1).errors_on(:coupon)
276
+ end
277
+
278
+ protected
279
+
280
+ def build_subscription(options = {})
281
+ Subscription.new({
282
+ :subscription_plan => subscription_plans(:basic),
283
+ :subscribable => users(:sue)
284
+ }.merge(options))
285
+ end
286
+
287
+ end
@@ -0,0 +1,124 @@
1
+ require 'spec_helper'
2
+
3
+ describe CreditCard do
4
+ fixtures :users, :subscriptions, :subscription_plans, :credit_cards
5
+
6
+ before(:each) do
7
+ @subscription = Subscription.new(:subscription_plan => subscription_plans(:premium), :subscribable => users(:sally))
8
+ @credit_card = CreditCard.create(CreditCard.sample_params.merge(:subscription => @subscription))
9
+ Freemium.configuration.gateway = Freemium::Gateways::Test.new
10
+ Freemium.configuration.gateway.username = 'demo'
11
+ Freemium.configuration.gateway.password = 'password'
12
+
13
+ Freemium.configuration.gateway.stub!(:validate => Freemium::Response.new(true))
14
+ end
15
+
16
+ it "should create" do
17
+ @subscription.credit_card = @credit_card
18
+
19
+ @subscription.save.should be_true
20
+ @subscription = Subscription.find(@subscription.id)
21
+ @subscription.billing_key.should_not be_nil
22
+ @subscription.credit_card.display_number.should_not be_nil
23
+ @subscription.credit_card.card_type.should_not be_nil
24
+ @subscription.credit_card.expiration_date.should_not be_nil
25
+ end
26
+
27
+ it "should be created with billing validation failure" do
28
+ response = Freemium::Response.new(false, 'responsetext' => 'FAILED')
29
+ response.message = 'FAILED'
30
+ Freemium.configuration.gateway.stub!(:validate => response)
31
+
32
+ @subscription.credit_card = @credit_card
33
+
34
+ @subscription.save.should be_false
35
+ @subscription.should have(1).errors_on(:base)
36
+ end
37
+
38
+ it "should be updated" do
39
+ @subscription.credit_card = @credit_card
40
+
41
+ @subscription.save.should be_true
42
+ @subscription = Subscription.find(@subscription.id)
43
+ @subscription.billing_key.should_not be_nil
44
+
45
+ original_key = @subscription.billing_key
46
+ original_expiration = @subscription.credit_card.expiration_date
47
+
48
+ @subscription.credit_card = CreditCard.new(CreditCard.sample_params.merge(:zip_code => 95060, :number => "5431111111111111", :card_type => "master", :year => 2020))
49
+ @subscription.save.should be_true
50
+ @subscription = Subscription.find(@subscription.id)
51
+ @subscription.billing_key.should eql(original_key)
52
+ original_expiration.should < @subscription.credit_card.expiration_date
53
+ @subscription.credit_card.reload.zip_code.should eql("95060")
54
+ end
55
+
56
+ ##
57
+ ## Test Validations
58
+ ##
59
+
60
+ it "should validate card number" do
61
+ @credit_card.number = "foo"
62
+ @credit_card.should_not be_valid
63
+ @credit_card.save.should be_false
64
+ end
65
+
66
+ it "should validate expiration date of card" do
67
+ @credit_card.year = 2001
68
+ @credit_card.should_not be_valid
69
+ @credit_card.save.should be_false
70
+ end
71
+
72
+ it "should be changed" do
73
+ # We're overriding AR#changed? to include instance vars that aren't persisted to see if a new card is being set
74
+ @credit_card.changed?.should be_true #New card is changed
75
+ end
76
+
77
+ it "should be changed after reload" do
78
+ @credit_card.save!
79
+ @credit_card = CreditCard.find(@credit_card.id)
80
+ @credit_card.reload.changed?.should be_false #Saved card is NOT changed
81
+ end
82
+
83
+ it "should not be chanegd for existent card" do
84
+ credit_cards(:bobs_credit_card).changed?.should be_false
85
+ end
86
+
87
+ it "should be chnaged after update" do
88
+ credit_cards(:bobs_credit_card).number = "foo"
89
+ credit_cards(:bobs_credit_card).changed?.should be_true
90
+ end
91
+
92
+ it "should be valid" do
93
+ @credit_card.should be_valid #New card is valid
94
+ end
95
+
96
+ it "should be valid and unchanged for existent cards" do
97
+ # existing cards on file are valid ...
98
+ credit_cards(:bobs_credit_card).changed?.should be_false #Existing card has not changed
99
+ credit_cards(:bobs_credit_card).should be_valid #Existing card is valid
100
+ end
101
+
102
+ it "should validate number of existent card" do
103
+ # ... unless theres an attempt to update them
104
+ credit_cards(:bobs_credit_card).number = "foo"
105
+ credit_cards(:bobs_credit_card).should_not be_valid #Partially changed existing card is not valid
106
+ end
107
+
108
+ it "should validate card type of existent card" do
109
+ # ... unless theres an attempt to update them
110
+ credit_cards(:bobs_credit_card).card_type = "visa"
111
+ credit_cards(:bobs_credit_card).should_not be_valid #Partially changed existing card is not valid
112
+ end
113
+
114
+ it "should automatically determine card type" do
115
+ c = CreditCard.create(number: '4111111111111111', month: '2', year: '2013')
116
+ c.valid?
117
+ c.card_type.should eql(:visa)
118
+ end
119
+
120
+ it 'should not choke when there is no expiration date provided' do
121
+ c = CreditCard.new(number: '4111111111111111')
122
+ c.valid?.should eql(false)
123
+ end
124
+ end