saucy 0.1.18 → 0.2.0
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.
- data/Gemfile +4 -1
- data/Gemfile.lock +12 -2
- data/README.md +75 -0
- data/app/controllers/accounts_controller.rb +2 -0
- data/app/controllers/billings_controller.rb +22 -0
- data/app/controllers/plans_controller.rb +12 -0
- data/app/models/limit.rb +5 -0
- data/app/models/signup.rb +20 -6
- data/app/views/accounts/_tab_bar.html.erb +3 -1
- data/app/views/accounts/edit.html.erb +6 -0
- data/app/views/accounts/new.html.erb +14 -4
- data/app/views/billings/_form.html.erb +8 -0
- data/app/views/billings/edit.html.erb +11 -0
- data/app/views/billings/show.html.erb +3 -0
- data/app/views/plans/_plan.html.erb +2 -0
- data/app/views/plans/edit.html.erb +23 -0
- data/config/routes.rb +2 -0
- data/features/run_features.feature +4 -0
- data/lib/generators/saucy/features/features_generator.rb +4 -0
- data/lib/generators/saucy/features/templates/factories.rb +14 -2
- data/lib/generators/saucy/features/templates/features/manage_account.feature +0 -2
- data/lib/generators/saucy/features/templates/features/manage_billing.feature +119 -0
- data/lib/generators/saucy/features/templates/features/sign_up.feature +2 -0
- data/lib/generators/saucy/features/templates/features/sign_up_paid.feature +76 -0
- data/lib/generators/saucy/features/templates/step_definitions/braintree_steps.rb +7 -0
- data/lib/generators/saucy/features/templates/step_definitions/html_steps.rb +22 -0
- data/lib/generators/saucy/features/templates/step_definitions/plan_steps.rb +9 -0
- data/lib/generators/saucy/features/templates/support/braintree.rb +5 -0
- data/lib/generators/saucy/install/templates/create_saucy_tables.rb +14 -1
- data/lib/generators/saucy/specs/specs_generator.rb +20 -0
- data/lib/generators/saucy/specs/templates/support/braintree.rb +5 -0
- data/lib/saucy/account.rb +134 -1
- data/lib/saucy/braintree.rb +100 -0
- data/lib/saucy/plan.rb +23 -0
- data/spec/controllers/accounts_controller_spec.rb +2 -2
- data/spec/environment.rb +4 -1
- data/spec/models/account_spec.rb +182 -0
- data/spec/models/limit_spec.rb +9 -0
- data/spec/models/plan_spec.rb +54 -0
- data/spec/models/signup_spec.rb +9 -0
- data/spec/support/braintree.rb +5 -0
- metadata +74 -8
- data/README +0 -38
@@ -0,0 +1,100 @@
|
|
1
|
+
require 'braintree'
|
2
|
+
|
3
|
+
Braintree::Configuration.environment = :production
|
4
|
+
Braintree::Configuration.merchant_id = "xxx"
|
5
|
+
Braintree::Configuration.public_key = "xxx"
|
6
|
+
Braintree::Configuration.private_key = "xxx"
|
7
|
+
|
8
|
+
require 'digest/md5'
|
9
|
+
require 'sham_rack'
|
10
|
+
|
11
|
+
class FakeBraintree
|
12
|
+
cattr_accessor :customers, :subscriptions, :failures
|
13
|
+
@@customers = {}
|
14
|
+
@@subscriptions = {}
|
15
|
+
@@failures = {}
|
16
|
+
|
17
|
+
def self.clear!
|
18
|
+
@@customers = {}
|
19
|
+
@@subscriptions = {}
|
20
|
+
@@failures = {}
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.failure?(card_number)
|
24
|
+
self.failures.include?(card_number)
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.failure_response(card_number)
|
28
|
+
failure = self.failures[card_number]
|
29
|
+
failure["errors"] ||= { "errors" => [] }
|
30
|
+
{ "message" => failure["message"], "verification" => { "status" => failure["status"], "processor_response_text" => failure["message"], "processor-response-code" => failure["code"], "gateway_rejection_reason" => "cvv", "cvv_response_code" => failure["code"] }, "errors" => failure["errors"], "params" => {}}
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
ShamRack.at("www.braintreegateway.com", 443).sinatra do
|
35
|
+
set :show_exceptions, false
|
36
|
+
set :dump_errors, true
|
37
|
+
set :raise_errors, true
|
38
|
+
|
39
|
+
post "/merchants/:merchant_id/customers" do
|
40
|
+
customer = Hash.from_xml(request.body).delete("customer")
|
41
|
+
if !FakeBraintree.failure?(customer["credit_card"]["number"])
|
42
|
+
customer["id"] ||= Digest::MD5.hexdigest("#{params[:merchant_id]}#{Time.now.to_f}")
|
43
|
+
customer["merchant-id"] = params[:merchant_id]
|
44
|
+
if customer["credit_card"] && customer["credit_card"].is_a?(Hash)
|
45
|
+
customer["credit_card"]["last_4"] = customer["credit_card"].delete("number")[-4..-1]
|
46
|
+
customer["credit_card"]["token"] = Digest::MD5.hexdigest("#{customer['merchant_id']}#{customer['id']}#{Time.now.to_f}")
|
47
|
+
credit_card = customer.delete("credit_card")
|
48
|
+
customer["credit_cards"] = [credit_card]
|
49
|
+
end
|
50
|
+
FakeBraintree.customers[customer["id"]] = customer
|
51
|
+
[201, { "Content-Encoding" => "gzip" }, ActiveSupport::Gzip.compress(customer.to_xml(:root => 'customer'))]
|
52
|
+
else
|
53
|
+
[422, { "Content-Encoding" => "gzip" }, ActiveSupport::Gzip.compress(FakeBraintree.failure_response(customer["credit_card"]["number"]).to_xml(:root => 'api_error_response'))]
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
get "/merchants/:merchant_id/customers/:id" do
|
58
|
+
customer = FakeBraintree.customers[params[:id]]
|
59
|
+
[200, { "Content-Encoding" => "gzip" }, ActiveSupport::Gzip.compress(customer.to_xml(:root => 'customer'))]
|
60
|
+
end
|
61
|
+
|
62
|
+
put "/merchants/:merchant_id/customers/:id" do
|
63
|
+
customer = Hash.from_xml(request.body).delete("customer")
|
64
|
+
customer["id"] = params[:id]
|
65
|
+
customer["merchant-id"] = params[:merchant_id]
|
66
|
+
if customer["credit_card"] && customer["credit_card"].is_a?(Hash)
|
67
|
+
customer["credit_card"]["last_4"] = customer["credit_card"].delete("number")[-4..-1]
|
68
|
+
customer["credit_card"]["token"] = Digest::MD5.hexdigest("#{customer['merchant_id']}#{customer['id']}#{Time.now.to_f}")
|
69
|
+
credit_card = customer.delete("credit_card")
|
70
|
+
customer["credit_cards"] = [credit_card]
|
71
|
+
end
|
72
|
+
FakeBraintree.customers[params["id"]] = customer
|
73
|
+
[200, { "Content-Encoding" => "gzip" }, ActiveSupport::Gzip.compress(customer.to_xml(:root => 'customer'))]
|
74
|
+
end
|
75
|
+
|
76
|
+
post "/merchants/:merchant_id/subscriptions" do
|
77
|
+
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<subscription>\n <plan-id type=\"integer\">2</plan-id>\n <payment-method-token>b22x</payment-method-token>\n</subscription>\n"
|
78
|
+
subscription = Hash.from_xml(request.body).delete("subscription")
|
79
|
+
subscription["id"] ||= Digest::MD5.hexdigest("#{subscription["payment_method_token"]}#{Time.now.to_f}")
|
80
|
+
subscription["transactions"] = []
|
81
|
+
subscription["add_ons"] = []
|
82
|
+
subscription["discounts"] = []
|
83
|
+
FakeBraintree.subscriptions[subscription["id"]] = subscription
|
84
|
+
[201, { "Content-Encoding" => "gzip" }, ActiveSupport::Gzip.compress(subscription.to_xml(:root => 'subscription'))]
|
85
|
+
end
|
86
|
+
|
87
|
+
get "/merchants/:merchant_id/subscriptions/:id" do
|
88
|
+
subscription = FakeBraintree.subscriptions[params[:id]]
|
89
|
+
[200, { "Content-Encoding" => "gzip" }, ActiveSupport::Gzip.compress(subscription.to_xml(:root => 'subscription'))]
|
90
|
+
end
|
91
|
+
|
92
|
+
put "/merchants/:merchant_id/subscriptions/:id" do
|
93
|
+
subscription = Hash.from_xml(request.body).delete("subscription")
|
94
|
+
subscription["transactions"] = []
|
95
|
+
subscription["add_ons"] = []
|
96
|
+
subscription["discounts"] = []
|
97
|
+
FakeBraintree.subscriptions[params["id"]] = subscription
|
98
|
+
[200, { "Content-Encoding" => "gzip" }, ActiveSupport::Gzip.compress(subscription.to_xml(:root => 'subscription'))]
|
99
|
+
end
|
100
|
+
end
|
data/lib/saucy/plan.rb
CHANGED
@@ -4,8 +4,31 @@ module Saucy
|
|
4
4
|
|
5
5
|
included do
|
6
6
|
has_many :accounts
|
7
|
+
has_many :limits
|
7
8
|
|
8
9
|
validates_presence_of :name
|
9
10
|
end
|
11
|
+
|
12
|
+
module InstanceMethods
|
13
|
+
def free?
|
14
|
+
price.zero?
|
15
|
+
end
|
16
|
+
|
17
|
+
def billed?
|
18
|
+
!free?
|
19
|
+
end
|
20
|
+
|
21
|
+
def can_add_more?(limit, amount)
|
22
|
+
limits.where(:name => limit, :value_type => :number).first.value > amount
|
23
|
+
end
|
24
|
+
|
25
|
+
def allows?(limit)
|
26
|
+
limits.where(:name => limit, :value_type => :boolean).first.value != 0
|
27
|
+
end
|
28
|
+
|
29
|
+
def limit(limit_name)
|
30
|
+
limits.where(:name => limit_name).first
|
31
|
+
end
|
32
|
+
end
|
10
33
|
end
|
11
34
|
end
|
@@ -32,7 +32,7 @@ end
|
|
32
32
|
|
33
33
|
describe AccountsController, "successful create for a confirmed user" do
|
34
34
|
let(:user) { Factory.stub(:user) }
|
35
|
-
let(:signup) { stub('signup', :user => user, :user= => nil) }
|
35
|
+
let(:signup) { stub('signup', :user => user, :user= => nil, :plan= => plan) }
|
36
36
|
let(:signup_attributes) { "attributes" }
|
37
37
|
let(:plan) { Factory(:plan) }
|
38
38
|
|
@@ -62,7 +62,7 @@ describe AccountsController, "successful create for a confirmed user" do
|
|
62
62
|
end
|
63
63
|
|
64
64
|
describe AccountsController, "failed create" do
|
65
|
-
let(:signup) { stub('signup', :user= => nil) }
|
65
|
+
let(:signup) { stub('signup', :user= => nil, :plan= => plan) }
|
66
66
|
let(:signup_attributes) { "attributes" }
|
67
67
|
let(:plan) { Factory(:plan) }
|
68
68
|
|
data/spec/environment.rb
CHANGED
@@ -23,6 +23,10 @@ class User < ActiveRecord::Base
|
|
23
23
|
include Saucy::User
|
24
24
|
end
|
25
25
|
|
26
|
+
class Plan < ActiveRecord::Base
|
27
|
+
include Saucy::Plan
|
28
|
+
end
|
29
|
+
|
26
30
|
module Testapp
|
27
31
|
class Application < Rails::Application
|
28
32
|
config.action_mailer.default_url_options = { :host => 'localhost' }
|
@@ -89,4 +93,3 @@ end
|
|
89
93
|
|
90
94
|
ClearanceCreateUsers.suppress_messages { ClearanceCreateUsers.migrate(:up) }
|
91
95
|
CreateSaucyTables.suppress_messages { CreateSaucyTables.migrate(:up) }
|
92
|
-
|
data/spec/models/account_spec.rb
CHANGED
@@ -6,11 +6,13 @@ describe Account do
|
|
6
6
|
it { should have_many(:memberships) }
|
7
7
|
it { should have_many(:users).through(:memberships) }
|
8
8
|
it { should have_many(:projects) }
|
9
|
+
it { should belong_to(:plan) }
|
9
10
|
|
10
11
|
it { should validate_uniqueness_of(:name) }
|
11
12
|
it { should validate_uniqueness_of(:keyword) }
|
12
13
|
it { should validate_presence_of( :name) }
|
13
14
|
it { should validate_presence_of(:keyword) }
|
15
|
+
it { should validate_presence_of(:plan_id) }
|
14
16
|
|
15
17
|
it { should_not allow_mass_assignment_of(:id) }
|
16
18
|
it { should_not allow_mass_assignment_of(:updated_at) }
|
@@ -63,5 +65,185 @@ describe Account do
|
|
63
65
|
|
64
66
|
result.should == expected
|
65
67
|
end
|
68
|
+
|
69
|
+
it "manifests braintree processor_declined errors as errors on number and doesn't save" do
|
70
|
+
FakeBraintree.failures["4111111111111112"] = { "message" => "Do Not Honor", "code" => "2000", "status" => "processor_declined" }
|
71
|
+
account = Factory.build(:account,
|
72
|
+
:cardholder_name => "Ralph Robot",
|
73
|
+
:billing_email => "ralph@example.com",
|
74
|
+
:card_number => "4111111111111112",
|
75
|
+
:expiration_month => 5,
|
76
|
+
:expiration_year => 2012,
|
77
|
+
:plan => Factory(:paid_plan))
|
78
|
+
account.save.should_not be
|
79
|
+
FakeBraintree.customers.should be_empty
|
80
|
+
account.persisted?.should_not be
|
81
|
+
account.errors[:card_number].any? { |e| e =~ /denied/ }.should be
|
82
|
+
end
|
83
|
+
|
84
|
+
it "manifests braintree gateway_rejected errors as errors on number and doesn't save" do
|
85
|
+
FakeBraintree.failures["4111111111111112"] = { "message" => "Gateway Rejected: cvv", "code" => "N", "status" => "gateway_rejected" }
|
86
|
+
account = Factory.build(:account,
|
87
|
+
:cardholder_name => "Ralph Robot",
|
88
|
+
:billing_email => "ralph@example.com",
|
89
|
+
:card_number => "4111111111111112",
|
90
|
+
:expiration_month => 5,
|
91
|
+
:expiration_year => 2012,
|
92
|
+
:verification_code => 200,
|
93
|
+
:plan => Factory(:paid_plan))
|
94
|
+
account.save.should_not be
|
95
|
+
FakeBraintree.customers.should be_empty
|
96
|
+
account.persisted?.should_not be
|
97
|
+
account.errors[:verification_code].any? { |e| e =~ /did not match/ }.should be
|
98
|
+
end
|
99
|
+
|
100
|
+
it "manifests braintree gateway_rejected errors as errors on number and doesn't save" do
|
101
|
+
FakeBraintree.failures["4111111111111111"] = { "message" => "Credit card number is invalid.", "errors" => { "customer" => { "errors" => [], "credit-card" => { "errors" => [{ "message" => "Credit card number is invalid.", "code" => 81715, "attribute" => :number }] }}}}
|
102
|
+
account = Factory.build(:account,
|
103
|
+
:cardholder_name => "Ralph Robot",
|
104
|
+
:billing_email => "ralph@example.com",
|
105
|
+
:card_number => "4111111111111111",
|
106
|
+
:expiration_month => 5,
|
107
|
+
:expiration_year => 2012,
|
108
|
+
:verification_code => 123,
|
109
|
+
:plan => Factory(:paid_plan))
|
110
|
+
account.save.should_not be
|
111
|
+
FakeBraintree.customers.should be_empty
|
112
|
+
account.persisted?.should_not be
|
113
|
+
account.errors[:card_number].any? { |e| e =~ /is invalid/ }.should be
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
describe Account, "with a paid plan" do
|
118
|
+
subject do
|
119
|
+
Factory(:account,
|
120
|
+
:cardholder_name => "Ralph Robot",
|
121
|
+
:billing_email => "ralph@example.com",
|
122
|
+
:card_number => "4111111111111111",
|
123
|
+
:verification_code => "123",
|
124
|
+
:expiration_month => 5,
|
125
|
+
:expiration_year => 2012,
|
126
|
+
:plan => Factory(:paid_plan))
|
127
|
+
end
|
128
|
+
|
129
|
+
it "has a customer_token" do
|
130
|
+
subject.customer_token.should_not be_nil
|
131
|
+
end
|
132
|
+
|
133
|
+
it "has a subscription_token" do
|
134
|
+
subject.subscription_token.should_not be_nil
|
135
|
+
end
|
136
|
+
|
137
|
+
it "has a customer" do
|
138
|
+
subject.customer.should_not be_nil
|
139
|
+
end
|
140
|
+
|
141
|
+
it "has a credit card" do
|
142
|
+
subject.credit_card.should_not be_nil
|
143
|
+
end
|
144
|
+
|
145
|
+
it "has a subscription" do
|
146
|
+
subject.subscription.should_not be_nil
|
147
|
+
end
|
148
|
+
|
149
|
+
it "creates a braintree customer, credit card, and subscription" do
|
150
|
+
FakeBraintree.customers[subject.customer_token].should_not be_nil
|
151
|
+
FakeBraintree.customers[subject.customer_token]["credit_cards"].first.should_not be_nil
|
152
|
+
FakeBraintree.subscriptions[subject.subscription_token].should_not be_nil
|
153
|
+
end
|
154
|
+
|
155
|
+
it "changes the subscription when the plan is changed" do
|
156
|
+
new_plan = Factory(:paid_plan, :name => "New Plan")
|
157
|
+
subject.save_braintree!(:plan_id => new_plan.id)
|
158
|
+
FakeBraintree.subscriptions[subject.subscription_token]["plan_id"].should == new_plan.id
|
159
|
+
end
|
160
|
+
|
161
|
+
it "updates the customer and credit card information when changed" do
|
162
|
+
subject.save_braintree!(:billing_email => "jrobot@example.com",
|
163
|
+
:cardholder_name => "Jim Robot",
|
164
|
+
:card_number => "4111111111111115",
|
165
|
+
:verification_code => "123",
|
166
|
+
:expiration_month => 5,
|
167
|
+
:expiration_year => 2013)
|
168
|
+
subject.customer.email.should == "jrobot@example.com"
|
169
|
+
subject.credit_card.cardholder_name.should == "Jim Robot"
|
170
|
+
end
|
66
171
|
end
|
67
172
|
|
173
|
+
describe Account, "with a free plan" do
|
174
|
+
subject do
|
175
|
+
Factory(:account, :plan => Factory(:plan))
|
176
|
+
end
|
177
|
+
|
178
|
+
it "has a customer_token" do
|
179
|
+
subject.customer_token.should_not be_nil
|
180
|
+
end
|
181
|
+
|
182
|
+
it "has a customer" do
|
183
|
+
subject.customer.should_not be_nil
|
184
|
+
end
|
185
|
+
|
186
|
+
it "doesn't have a credit_card" do
|
187
|
+
subject.credit_card.should be_nil
|
188
|
+
end
|
189
|
+
|
190
|
+
it "doesn't have a subscription_token" do
|
191
|
+
subject.subscription_token.should be_nil
|
192
|
+
end
|
193
|
+
|
194
|
+
it "doesn't have a subscription" do
|
195
|
+
subject.subscription.should be_nil
|
196
|
+
end
|
197
|
+
|
198
|
+
it "creates a braintree customer" do
|
199
|
+
FakeBraintree.customers[subject.customer_token].should_not be_nil
|
200
|
+
end
|
201
|
+
|
202
|
+
it "doesn't create a credit card, and subscription" do
|
203
|
+
FakeBraintree.customers[subject.customer_token]["credit_cards"].should be_nil
|
204
|
+
FakeBraintree.subscriptions[subject.subscription_token].should be_nil
|
205
|
+
end
|
206
|
+
|
207
|
+
it "creates a credit card, and subscription when the plan is changed and billing info is supplied" do
|
208
|
+
new_plan = Factory(:paid_plan, :name => "New Plan")
|
209
|
+
subject.save_braintree!(:plan_id => new_plan.id,
|
210
|
+
:cardholder_name => "Ralph Robot",
|
211
|
+
:billing_email => "ralph@example.com",
|
212
|
+
:card_number => "4111111111111111",
|
213
|
+
:verification_code => "123",
|
214
|
+
:expiration_month => 5,
|
215
|
+
:expiration_year => 2012)
|
216
|
+
|
217
|
+
FakeBraintree.customers[subject.customer_token]["credit_cards"].first.should_not be_nil
|
218
|
+
FakeBraintree.subscriptions[subject.subscription_token].should_not be_nil
|
219
|
+
FakeBraintree.subscriptions[subject.subscription_token]["plan_id"].should == new_plan.id
|
220
|
+
subject.credit_card.should_not be_nil
|
221
|
+
subject.subscription.should_not be_nil
|
222
|
+
end
|
223
|
+
end
|
224
|
+
|
225
|
+
describe Account, "with a plan and limits, and other plans" do
|
226
|
+
subject { Factory(:account) }
|
227
|
+
|
228
|
+
before do
|
229
|
+
Factory(:limit, :name => "users", :value => 1, :plan => subject.plan)
|
230
|
+
Factory(:limit, :name => "projects", :value => 1, :plan => subject.plan)
|
231
|
+
Factory(:limit, :name => "ssl", :value => 1, :value_type => :boolean, :plan => subject.plan)
|
232
|
+
@can_switch = Factory(:plan)
|
233
|
+
Factory(:limit, :name => "users", :value => 1, :plan => @can_switch)
|
234
|
+
Factory(:limit, :name => "projects", :value => 1, :plan => @can_switch)
|
235
|
+
Factory(:limit, :name => "ssl", :value => 0, :value_type => :boolean, :plan => @can_switch)
|
236
|
+
@cannot_switch = Factory(:plan)
|
237
|
+
Factory(:limit, :name => "users", :value => 0, :plan => @cannot_switch)
|
238
|
+
Factory(:limit, :name => "projects", :value => 0, :plan => @cannot_switch)
|
239
|
+
Factory(:limit, :name => "ssl", :value => 1, :value_type => :boolean, :plan => @cannot_switch)
|
240
|
+
|
241
|
+
Factory(:membership, :account => subject)
|
242
|
+
Factory(:project, :account => subject)
|
243
|
+
end
|
244
|
+
|
245
|
+
it "indicates whether the account can switch to another plan" do
|
246
|
+
subject.can_change_plan_to?(@can_switch).should be
|
247
|
+
subject.can_change_plan_to?(@cannot_switch).should_not be
|
248
|
+
end
|
249
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Plan do
|
4
|
+
subject { Factory(:plan) }
|
5
|
+
|
6
|
+
it { should have_many(:limits) }
|
7
|
+
it { should have_many(:accounts) }
|
8
|
+
it { should validate_presence_of(:name) }
|
9
|
+
end
|
10
|
+
|
11
|
+
describe Plan, "free" do
|
12
|
+
subject { Factory(:plan) }
|
13
|
+
|
14
|
+
it "is free" do
|
15
|
+
subject.free?.should be
|
16
|
+
end
|
17
|
+
|
18
|
+
it "is not billed" do
|
19
|
+
subject.billed?.should_not be
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
describe Plan, "paid" do
|
24
|
+
subject { Factory(:paid_plan) }
|
25
|
+
|
26
|
+
it "is not free" do
|
27
|
+
subject.free?.should_not be
|
28
|
+
end
|
29
|
+
|
30
|
+
it "is billed" do
|
31
|
+
subject.billed?.should be
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
describe Plan, "with limits" do
|
36
|
+
subject { Factory(:plan) }
|
37
|
+
|
38
|
+
before do
|
39
|
+
Factory(:limit, :name => "users", :value => 1, :plan => subject)
|
40
|
+
Factory(:limit, :name => "ssl", :value => 0, :value_type => :boolean, :plan => subject)
|
41
|
+
Factory(:limit, :name => "lighthouse", :value => 1, :value_type => :boolean, :plan => subject)
|
42
|
+
end
|
43
|
+
|
44
|
+
it "indicates whether or not more users can be created" do
|
45
|
+
subject.can_add_more?(:users, 0).should be
|
46
|
+
subject.can_add_more?(:users, 1).should_not be
|
47
|
+
subject.can_add_more?(:users, 2).should_not be
|
48
|
+
end
|
49
|
+
|
50
|
+
it "indicates whether a plan can do something or not" do
|
51
|
+
subject.allows?(:ssl).should_not be
|
52
|
+
subject.allows?(:lighthouse).should be
|
53
|
+
end
|
54
|
+
end
|
data/spec/models/signup_spec.rb
CHANGED
@@ -173,3 +173,12 @@ describe Signup, "invalid with an existing user and correct password" do
|
|
173
173
|
user.reload.accounts.should_not include(subject.account)
|
174
174
|
end
|
175
175
|
end
|
176
|
+
|
177
|
+
describe Signup, "with an account that doesn't save" do
|
178
|
+
subject { Factory.build(:signup) }
|
179
|
+
|
180
|
+
it "doesn't raise the transaction and returns false" do
|
181
|
+
Account.any_instance.stubs(:save!).raises(ActiveRecord::RecordNotSaved)
|
182
|
+
subject.save.should_not be
|
183
|
+
end
|
184
|
+
end
|