saasy 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (146) hide show
  1. data/CHANGELOG.md +114 -0
  2. data/Gemfile +26 -0
  3. data/README.md +118 -0
  4. data/Rakefile +38 -0
  5. data/app/controllers/accounts_controller.rb +68 -0
  6. data/app/controllers/billings_controller.rb +25 -0
  7. data/app/controllers/invitations_controller.rb +65 -0
  8. data/app/controllers/memberships_controller.rb +45 -0
  9. data/app/controllers/plans_controller.rb +24 -0
  10. data/app/controllers/profiles_controller.rb +19 -0
  11. data/app/helpers/limits_helper.rb +13 -0
  12. data/app/mailers/billing_mailer.rb +53 -0
  13. data/app/mailers/invitation_mailer.rb +18 -0
  14. data/app/models/invitation.rb +113 -0
  15. data/app/models/limit.rb +49 -0
  16. data/app/models/membership.rb +26 -0
  17. data/app/models/permission.rb +19 -0
  18. data/app/models/signup.rb +163 -0
  19. data/app/views/accounts/_account.html.erb +9 -0
  20. data/app/views/accounts/_blank_slate.html.erb +6 -0
  21. data/app/views/accounts/_projects.html.erb +12 -0
  22. data/app/views/accounts/_subnav.html.erb +10 -0
  23. data/app/views/accounts/edit.html.erb +34 -0
  24. data/app/views/accounts/index.html.erb +9 -0
  25. data/app/views/accounts/new.html.erb +36 -0
  26. data/app/views/billing_mailer/completed_trial.text.erb +13 -0
  27. data/app/views/billing_mailer/expiring_trial.text.erb +15 -0
  28. data/app/views/billing_mailer/new_unactivated.text.erb +1 -0
  29. data/app/views/billing_mailer/problem.html.erb +13 -0
  30. data/app/views/billing_mailer/problem.text.erb +14 -0
  31. data/app/views/billing_mailer/receipt.html.erb +41 -0
  32. data/app/views/billing_mailer/receipt.text.erb +25 -0
  33. data/app/views/billings/_form.html.erb +8 -0
  34. data/app/views/billings/edit.html.erb +13 -0
  35. data/app/views/billings/show.html.erb +29 -0
  36. data/app/views/invitation_mailer/invitation.text.erb +6 -0
  37. data/app/views/invitations/new.html.erb +17 -0
  38. data/app/views/invitations/show.html.erb +22 -0
  39. data/app/views/layouts/saucy.html.erb +36 -0
  40. data/app/views/limits/_meter.html.erb +13 -0
  41. data/app/views/memberships/edit.html.erb +21 -0
  42. data/app/views/memberships/index.html.erb +17 -0
  43. data/app/views/plans/_plan.html.erb +32 -0
  44. data/app/views/plans/_terms.html.erb +15 -0
  45. data/app/views/plans/edit.html.erb +33 -0
  46. data/app/views/plans/index.html.erb +12 -0
  47. data/app/views/profiles/_inputs.html.erb +5 -0
  48. data/app/views/profiles/edit.html.erb +36 -0
  49. data/app/views/projects/_form.html.erb +36 -0
  50. data/app/views/projects/edit.html.erb +22 -0
  51. data/app/views/projects/index.html.erb +28 -0
  52. data/app/views/projects/new.html.erb +13 -0
  53. data/app/views/projects/show.html.erb +0 -0
  54. data/app/views/shared/_project_dropdown.html.erb +55 -0
  55. data/app/views/shared/_saucy_javascript.html.erb +33 -0
  56. data/config/locales/en.yml +37 -0
  57. data/config/routes.rb +19 -0
  58. data/features/run_features.feature +83 -0
  59. data/features/step_definitions/clearance_steps.rb +45 -0
  60. data/features/step_definitions/rails_steps.rb +73 -0
  61. data/features/step_definitions/saucy_steps.rb +8 -0
  62. data/features/support/env.rb +4 -0
  63. data/features/support/file.rb +11 -0
  64. data/lib/generators/saucy/base.rb +18 -0
  65. data/lib/generators/saucy/features/features_generator.rb +91 -0
  66. data/lib/generators/saucy/features/templates/README +3 -0
  67. data/lib/generators/saucy/features/templates/factories.rb +71 -0
  68. data/lib/generators/saucy/features/templates/features/edit_profile.feature +9 -0
  69. data/lib/generators/saucy/features/templates/features/edit_project_permissions.feature +37 -0
  70. data/lib/generators/saucy/features/templates/features/edit_user_permissions.feature +47 -0
  71. data/lib/generators/saucy/features/templates/features/manage_account.feature +35 -0
  72. data/lib/generators/saucy/features/templates/features/manage_billing.feature +93 -0
  73. data/lib/generators/saucy/features/templates/features/manage_plan.feature +143 -0
  74. data/lib/generators/saucy/features/templates/features/manage_projects.feature +139 -0
  75. data/lib/generators/saucy/features/templates/features/manage_users.feature +142 -0
  76. data/lib/generators/saucy/features/templates/features/new_account.feature +33 -0
  77. data/lib/generators/saucy/features/templates/features/project_dropdown.feature +77 -0
  78. data/lib/generators/saucy/features/templates/features/sign_up.feature +32 -0
  79. data/lib/generators/saucy/features/templates/features/sign_up_paid.feature +65 -0
  80. data/lib/generators/saucy/features/templates/features/trial_plans.feature +82 -0
  81. data/lib/generators/saucy/features/templates/step_definitions/account_steps.rb +30 -0
  82. data/lib/generators/saucy/features/templates/step_definitions/braintree_steps.rb +25 -0
  83. data/lib/generators/saucy/features/templates/step_definitions/cron_steps.rb +23 -0
  84. data/lib/generators/saucy/features/templates/step_definitions/email_steps.rb +40 -0
  85. data/lib/generators/saucy/features/templates/step_definitions/factory_girl_steps.rb +1 -0
  86. data/lib/generators/saucy/features/templates/step_definitions/html_steps.rb +51 -0
  87. data/lib/generators/saucy/features/templates/step_definitions/plan_steps.rb +16 -0
  88. data/lib/generators/saucy/features/templates/step_definitions/project_steps.rb +4 -0
  89. data/lib/generators/saucy/features/templates/step_definitions/session_steps.rb +37 -0
  90. data/lib/generators/saucy/features/templates/step_definitions/user_steps.rb +100 -0
  91. data/lib/generators/saucy/features/templates/support/braintree.rb +5 -0
  92. data/lib/generators/saucy/install/install_generator.rb +40 -0
  93. data/lib/generators/saucy/install/templates/controllers/projects_controller.rb +3 -0
  94. data/lib/generators/saucy/install/templates/create_saucy_tables.rb +115 -0
  95. data/lib/generators/saucy/install/templates/models/account.rb +3 -0
  96. data/lib/generators/saucy/install/templates/models/plan.rb +3 -0
  97. data/lib/generators/saucy/install/templates/models/project.rb +3 -0
  98. data/lib/generators/saucy/specs/specs_generator.rb +20 -0
  99. data/lib/generators/saucy/specs/templates/support/braintree.rb +5 -0
  100. data/lib/generators/saucy/views/views_generator.rb +23 -0
  101. data/lib/saucy.rb +10 -0
  102. data/lib/saucy/account.rb +132 -0
  103. data/lib/saucy/account_authorization.rb +67 -0
  104. data/lib/saucy/configuration.rb +29 -0
  105. data/lib/saucy/engine.rb +35 -0
  106. data/lib/saucy/fake_braintree.rb +134 -0
  107. data/lib/saucy/layouts.rb +36 -0
  108. data/lib/saucy/plan.rb +54 -0
  109. data/lib/saucy/project.rb +125 -0
  110. data/lib/saucy/projects_controller.rb +94 -0
  111. data/lib/saucy/railties/tasks.rake +28 -0
  112. data/lib/saucy/routing_extensions.rb +121 -0
  113. data/lib/saucy/subscription.rb +237 -0
  114. data/lib/saucy/user.rb +30 -0
  115. data/spec/controllers/accounts_controller_spec.rb +228 -0
  116. data/spec/controllers/application_controller_spec.rb +32 -0
  117. data/spec/controllers/invitations_controller_spec.rb +215 -0
  118. data/spec/controllers/memberships_controller_spec.rb +117 -0
  119. data/spec/controllers/plans_controller_spec.rb +13 -0
  120. data/spec/controllers/profiles_controller_spec.rb +48 -0
  121. data/spec/controllers/projects_controller_spec.rb +216 -0
  122. data/spec/environment.rb +95 -0
  123. data/spec/layouts_spec.rb +21 -0
  124. data/spec/mailers/billing_mailer_spec.rb +68 -0
  125. data/spec/mailers/invitiation_mailer_spec.rb +19 -0
  126. data/spec/models/account_spec.rb +218 -0
  127. data/spec/models/invitation_spec.rb +320 -0
  128. data/spec/models/limit_spec.rb +70 -0
  129. data/spec/models/membership_spec.rb +37 -0
  130. data/spec/models/permission_spec.rb +30 -0
  131. data/spec/models/plan_spec.rb +81 -0
  132. data/spec/models/project_spec.rb +223 -0
  133. data/spec/models/signup_spec.rb +177 -0
  134. data/spec/models/subscription_spec.rb +481 -0
  135. data/spec/models/user_spec.rb +72 -0
  136. data/spec/route_extensions_spec.rb +51 -0
  137. data/spec/saucy_spec.rb +62 -0
  138. data/spec/scaffold/config/routes.rb +5 -0
  139. data/spec/spec_helper.rb +39 -0
  140. data/spec/support/authentication_helpers.rb +81 -0
  141. data/spec/support/authorization_helpers.rb +56 -0
  142. data/spec/support/braintree.rb +7 -0
  143. data/spec/support/clearance_matchers.rb +55 -0
  144. data/spec/support/notifications.rb +57 -0
  145. data/spec/views/accounts/_account.html.erb_spec.rb +37 -0
  146. metadata +325 -0
@@ -0,0 +1,70 @@
1
+ require 'spec_helper'
2
+
3
+ describe Limit do
4
+ subject { Factory(:limit) }
5
+
6
+ it { should belong_to(:plan) }
7
+ it { should validate_presence_of(:name) }
8
+ it { should validate_presence_of(:value) }
9
+ end
10
+
11
+ describe Limit, "various kinds" do
12
+ let!(:users) { Factory(:limit, :name => "users", :value => 1) }
13
+ let!(:ssl) { Factory(:limit, :name => "ssl", :value => 0, :value_type => :boolean) }
14
+ let!(:lighthouse) { Factory(:limit, :name => "lighthouse", :value => 1, :value_type => :boolean) }
15
+
16
+ it "gives the numbered limits" do
17
+ Limit.numbered.should == [users]
18
+ end
19
+
20
+ it "gives the boolean limits" do
21
+ Limit.boolean.should == [ssl, lighthouse]
22
+ end
23
+
24
+ it "gives the limits by name" do
25
+ Limit.named(:users).should == users
26
+ end
27
+
28
+ it "reports true for booleans with 1" do
29
+ lighthouse.allowed?.should be
30
+ end
31
+
32
+ it "reports false for booleans with 0" do
33
+ ssl.allowed?.should_not be
34
+ end
35
+ end
36
+
37
+ describe Limit, "with account and limits" do
38
+ subject { Factory(:limit, :name => "users", :value => 1) }
39
+
40
+ before do
41
+ @account = Factory(:account, :plan => subject.plan)
42
+ end
43
+
44
+ it "indicates whether the account is below the limit" do
45
+ subject.within?(@account)
46
+ end
47
+
48
+ it "gives the current count for the account of the limit" do
49
+ @account.stubs(:users_count => 99)
50
+ subject.current_count(@account).should == 99
51
+ end
52
+
53
+ it "can query whether the given account is below the specified limit" do
54
+ Limit.within?("users", @account)
55
+ end
56
+
57
+ it "indicates whether the specified account can add one" do
58
+ Limit.can_add_one?("users", @account).should be
59
+
60
+ Factory(:membership, :account => @account)
61
+ Factory(:project, :account => @account)
62
+
63
+ Limit.can_add_one?("users", @account).should_not be
64
+ end
65
+
66
+ it "returns true if there is no limit" do
67
+ Limit.can_add_one?("nomatch", @account).should be
68
+ Limit.within?("nomatch", @account).should be
69
+ end
70
+ end
@@ -0,0 +1,37 @@
1
+ require 'spec_helper'
2
+
3
+ describe Membership do
4
+ it { should belong_to(:account) }
5
+ it { should belong_to(:user) }
6
+ it { should validate_presence_of(:account_id) }
7
+ it { should validate_presence_of(:user_id) }
8
+ it { should have_many(:permissions).dependent(:destroy) }
9
+ it { should have_many(:projects).through(:permissions) }
10
+
11
+ describe "given an existing account membership" do
12
+ before { Factory(:membership) }
13
+ it { should validate_uniqueness_of(:user_id).scoped_to(:account_id) }
14
+ end
15
+
16
+ it "delegates the user's name" do
17
+ user = Factory(:user)
18
+ membership = Factory(:membership, :user => user)
19
+
20
+ membership.name.should == user.name
21
+ end
22
+
23
+ it "delegates the user's email" do
24
+ user = Factory(:user)
25
+ membership = Factory(:membership, :user => user)
26
+
27
+ membership.email.should == user.email
28
+ end
29
+
30
+ it "returns memberships by name" do
31
+ Factory(:membership, :user => Factory(:user, :name => "def"))
32
+ Factory(:membership, :user => Factory(:user, :name => "abc"))
33
+ Factory(:membership, :user => Factory(:user, :name => "ghi"))
34
+
35
+ Membership.by_name.map(&:name).should == %w(abc def ghi)
36
+ end
37
+ end
@@ -0,0 +1,30 @@
1
+ require 'spec_helper'
2
+
3
+ describe Permission do
4
+ it { should belong_to(:project) }
5
+ it { should belong_to(:membership) }
6
+ it { should belong_to(:user) }
7
+
8
+ it "doesn't allow the same member to be added to a project twice" do
9
+ original = Factory(:permission)
10
+ duplicate = Factory.build(:permission, :membership => original.membership, :project => original.project)
11
+ duplicate.should_not be_valid
12
+ end
13
+
14
+ it "allows different members to be added to a project" do
15
+ original = Factory(:permission)
16
+ duplicate = Factory.build(:permission, :project => original.project)
17
+ duplicate.should be_valid
18
+ end
19
+
20
+ it "caches the user from the account membership" do
21
+ membership = Factory(:membership)
22
+ permission = Factory(:permission, :membership => membership)
23
+ permission.user_id.should == membership.user_id
24
+ end
25
+
26
+ it "doesn't allow the user to be assigned" do
27
+ expect { subject.user = Factory.build(:user) }.to raise_error
28
+ end
29
+ end
30
+
@@ -0,0 +1,81 @@
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
+
10
+ it "finds ordered paid plans" do
11
+ Factory(:plan, :name => "Free", :price => 0)
12
+ Factory(:plan, :name => "Two", :price => 2)
13
+ Factory(:plan, :name => "One", :price => 1)
14
+ Factory(:plan, :name => "Three", :price => 3)
15
+
16
+ Plan.paid_by_price.to_a.map(&:name).should == %w(Three Two One)
17
+ end
18
+
19
+ it "finds the trial plan" do
20
+ paid = Factory(:plan, :name => "Paid", :price => 1)
21
+ trial = Factory(:plan, :name => "Free", :price => 0)
22
+
23
+ Plan.trial.should == trial
24
+ end
25
+ end
26
+
27
+ describe Plan, "free" do
28
+ subject { Factory(:plan) }
29
+
30
+ it "is free" do
31
+ subject.free?.should be
32
+ end
33
+
34
+ it "is not billed" do
35
+ subject.billed?.should_not be
36
+ end
37
+ end
38
+
39
+ describe Plan, "paid" do
40
+ subject { Factory(:paid_plan) }
41
+
42
+ it "is not free" do
43
+ subject.free?.should_not be
44
+ end
45
+
46
+ it "is billed" do
47
+ subject.billed?.should be
48
+ end
49
+ end
50
+
51
+ describe Plan, "with limits" do
52
+ subject { Factory(:plan) }
53
+
54
+ before do
55
+ Factory(:limit, :name => "users", :value => 1, :plan => subject)
56
+ Factory(:limit, :name => "ssl", :value => 0, :value_type => :boolean, :plan => subject)
57
+ Factory(:limit, :name => "lighthouse", :value => 1, :value_type => :boolean, :plan => subject)
58
+ end
59
+
60
+ it "indicates whether or not more users can be created" do
61
+ subject.can_add_more?(:users, 0).should be
62
+ subject.can_add_more?(:users, 1).should_not be
63
+ subject.can_add_more?(:users, 2).should_not be
64
+ end
65
+
66
+ it "indicates whether a plan can do something or not" do
67
+ subject.allows?(:ssl).should_not be
68
+ subject.allows?(:lighthouse).should be
69
+ end
70
+ end
71
+
72
+
73
+ describe Plan, "with prices" do
74
+ let!(:free) { Factory(:plan, :price => 0) }
75
+ let!(:least) { Factory(:plan, :price => 1) }
76
+ let!(:most) { Factory(:plan, :price => 2) }
77
+
78
+ it "gives them from most to least expensive when ordered" do
79
+ Plan.ordered.should == [most, least, free]
80
+ end
81
+ end
@@ -0,0 +1,223 @@
1
+ require 'spec_helper'
2
+
3
+ describe Project do
4
+ it { should belong_to(:account) }
5
+ it { should validate_presence_of(:account_id) }
6
+ it { should have_many(:permissions) }
7
+ it { should validate_presence_of(:keyword) }
8
+ it { should validate_presence_of(:name) }
9
+
10
+ it { should allow_value("hello").for(:keyword) }
11
+ it { should allow_value("0123").for(:keyword) }
12
+ it { should allow_value("hello_world").for(:keyword) }
13
+ it { should allow_value("hello-world").for(:keyword) }
14
+ it { should_not allow_value("HELLO").for(:keyword) }
15
+ it { should_not allow_value("hello world").for(:keyword) }
16
+
17
+ it { should_not allow_mass_assignment_of(:account_id) }
18
+ it { should_not allow_mass_assignment_of(:account) }
19
+
20
+ it "finds projects visible to a user" do
21
+ account = Factory(:account)
22
+ user = Factory(:user)
23
+ membership = Factory(:membership, :user => user, :account => account)
24
+ visible_projects = [Factory(:project, :account => account),
25
+ Factory(:project, :account => account)]
26
+ invisible_project = Factory(:project, :account => account)
27
+ visible_projects.each do |visible_project|
28
+ Factory(:permission, :project => visible_project,
29
+ :membership => membership)
30
+ end
31
+
32
+ Project.visible_to(user).to_a.should =~ visible_projects
33
+ end
34
+
35
+ it "returns projects by name" do
36
+ Factory(:project, :name => "def")
37
+ Factory(:project, :name => "abc")
38
+ Factory(:project, :name => "ghi")
39
+
40
+ Project.by_name.map(&:name).should == %w(abc def ghi)
41
+ end
42
+
43
+ context "archived and non-archived projects" do
44
+ before do
45
+ @archived = [Factory(:project, :archived => true), Factory(:project, :archived => true)]
46
+ @active = [Factory(:project, :archived => false)]
47
+ end
48
+
49
+ it "returns only archived projects with archived" do
50
+ Project.archived.all.should == @archived
51
+ end
52
+
53
+ it "returns only active projects with active" do
54
+ Project.active.all.should == @active
55
+ end
56
+ end
57
+
58
+ context "archived project for an account at its project limit" do
59
+ before do
60
+ @archived = Factory(:project, :archived => true)
61
+ @account = @archived.account
62
+ Limit.stubs(:can_add_one?).with("projects", @account).returns(false)
63
+ end
64
+
65
+ it "cannot be unarchived" do
66
+ @archived.archived = false
67
+ @archived.save.should_not be
68
+ @archived.errors[:archived].first.should match(/at your limit/)
69
+ end
70
+ end
71
+
72
+ context "project moving to an account at its project limit" do
73
+ before do
74
+ @project = Factory(:project)
75
+ @account = Factory(:account)
76
+ Limit.stubs(:can_add_one?).with("projects", @account).returns(false)
77
+ end
78
+
79
+ it "cannot be moved" do
80
+ @project.account = @account
81
+ @project.save.should_not be
82
+ @project.errors[:account_id].first.should match(/at your limit/)
83
+ end
84
+ end
85
+
86
+ it "should give its keyword for to_param" do
87
+ project = Factory(:project)
88
+ project.to_param.should == project.keyword
89
+ end
90
+ end
91
+
92
+ describe Project, "keyword uniqueness" do
93
+ let(:project) { Factory(:project) }
94
+ subject do
95
+ Factory.build(:project, :account => project.account)
96
+ end
97
+
98
+ it "validates uniqueness of it's keyword" do
99
+ subject.keyword = project.keyword
100
+ subject.save
101
+ subject.errors[:keyword].should include("has already been taken")
102
+ end
103
+ end
104
+
105
+ share_examples_for "default project permissions" do
106
+ it "is viewable by admins by default" do
107
+ admins.each do |admin|
108
+ subject.users.should include(admin)
109
+ end
110
+ end
111
+
112
+ it "isn't viewable by non-members" do
113
+ subject.users.should_not include(non_admin)
114
+ subject.users.should_not include(non_member)
115
+ end
116
+ end
117
+
118
+ describe Project, "for an account with admin and non-admin users" do
119
+ let!(:account) { Factory(:account, :name => "Account") }
120
+ let!(:other_account) { Factory(:account, :name => "Other") }
121
+ let!(:non_admin) { Factory(:user) }
122
+ let!(:admins) { [Factory(:user), Factory(:user)] }
123
+ let!(:non_member) { Factory(:user) }
124
+ subject { Factory.build(:project, :account => account) }
125
+
126
+ before do
127
+ Factory(:membership, :account => account, :user => non_admin, :admin => false)
128
+ Factory(:membership, :account => other_account,
129
+ :user => non_member,
130
+ :admin => true)
131
+ admins.each do |admin|
132
+ Factory(:membership, :user => admin, :account => account, :admin => true)
133
+ end
134
+
135
+ subject.assign_default_permissions
136
+ end
137
+
138
+ context "before saving" do
139
+ it_behaves_like "default project permissions"
140
+ end
141
+
142
+ context "after saving" do
143
+ before do
144
+ subject.save!
145
+ subject.reload
146
+ end
147
+
148
+ it_behaves_like "default project permissions"
149
+ end
150
+ end
151
+
152
+ describe Project, "saved" do
153
+ subject { Factory(:project) }
154
+
155
+ it "has a member with a membership" do
156
+ user = Factory(:user)
157
+ membership = Factory(:membership, :account => subject.account,
158
+ :user => user)
159
+ membership = Factory(:permission, :project => subject,
160
+ :membership => membership)
161
+ should have_member(user)
162
+ end
163
+
164
+ it "doesn't have a member without a membership" do
165
+ user = Factory(:user)
166
+ should_not have_member(user)
167
+ end
168
+ end
169
+
170
+ describe Project, "assigning users on update" do
171
+ subject { Factory(:project) }
172
+
173
+ let(:account) { subject.account }
174
+ let!(:user_to_remove) { Factory(:user) }
175
+ let!(:user_to_add) { Factory(:user) }
176
+ let!(:admin) { Factory(:user) }
177
+
178
+ before do
179
+ membership_to_remove =
180
+ Factory(:membership, :account => account, :user => user_to_remove)
181
+ membership_to_add =
182
+ Factory(:membership, :account => account, :user => user_to_add)
183
+ admin_membership =
184
+ Factory(:membership, :account => account, :user => admin, :admin => true)
185
+ Factory(:permission, :membership => membership_to_remove,
186
+ :project => subject)
187
+
188
+ subject.reload.update_attributes!(:user_ids => [user_to_add.id, ""])
189
+ end
190
+
191
+ it "adds an added user" do
192
+ user_to_add.should be_member_of(subject)
193
+ end
194
+
195
+ it "removes a removed user" do
196
+ user_to_remove.should_not be_member_of(subject)
197
+ end
198
+
199
+ it "keeps an admin" do
200
+ admin.should be_member_of(subject)
201
+ end
202
+ end
203
+
204
+ describe Project, "assigning users on create" do
205
+ subject { Factory.build(:project) }
206
+ let(:account) { subject.account }
207
+ let!(:member) { Factory(:user) }
208
+ let!(:admin) { Factory(:user) }
209
+
210
+ before do
211
+ Factory(:membership, :account => account, :user => member)
212
+ Factory(:membership, :account => account, :user => admin, :admin => true)
213
+ subject.save
214
+ end
215
+
216
+ it "adds admins to the project" do
217
+ admin.should be_member_of(subject)
218
+ end
219
+
220
+ it "ignores normal users" do
221
+ member.should_not be_member_of(subject)
222
+ end
223
+ end
@@ -0,0 +1,177 @@
1
+ require 'spec_helper'
2
+
3
+ describe Signup do
4
+ it "complies with the activemodel api" do
5
+ subject.class.should be_kind_of(ActiveModel::Naming)
6
+ should_not be_persisted
7
+ should be_kind_of(ActiveModel::Conversion)
8
+ should be_kind_of(ActiveModel::Validations)
9
+ end
10
+ end
11
+
12
+ describe Signup, "with attributes in the constructor" do
13
+ subject { Signup.new(:email => 'person@example.com', :password => 'password') }
14
+ it "assigns attributes" do
15
+ subject.email.should == 'person@example.com'
16
+ subject.password.should == 'password'
17
+ end
18
+ end
19
+
20
+ describe Signup, "with nil to the constructor" do
21
+ subject { Signup.new(nil) }
22
+
23
+ it "assigns no attributes" do
24
+ subject.email.should be_blank
25
+ subject.password.should be_blank
26
+ end
27
+ end
28
+
29
+ share_examples_for "invalid signup" do
30
+ before { @previous_user_count = User.count }
31
+
32
+ it "returns false" do
33
+ @result.should be_false
34
+ end
35
+
36
+ it "doesn't create a user" do
37
+ User.count.should == @previous_user_count
38
+ end
39
+
40
+ it "doesn't create an account" do
41
+ subject.account.should be_new_record
42
+ end
43
+ end
44
+
45
+ share_examples_for "valid signup" do
46
+ it "returns true" do
47
+ @result.should be_true
48
+ end
49
+
50
+ it "saves the account" do
51
+ subject.account.should be_persisted
52
+ end
53
+ end
54
+
55
+ describe Signup, "with a valid user and account" do
56
+ it_should_behave_like "valid signup"
57
+ let(:email) { "user@example.com" }
58
+ subject { Factory.build(:signup, :email => email) }
59
+
60
+ before do
61
+ @result = subject.save
62
+ end
63
+
64
+ it "saves the user" do
65
+ subject.user.should be_persisted
66
+ end
67
+
68
+ it "assigns the user to the account" do
69
+ subject.user.reload.accounts.should include(subject.account)
70
+ end
71
+
72
+ it "creates an admin user" do
73
+ subject.user.should be_admin_of(subject.account)
74
+ end
75
+
76
+ it "gives a friendly account name" do
77
+ subject.account.name.should == "user"
78
+ end
79
+
80
+ it "gives a friendly account keyword" do
81
+ subject.account.keyword.should =~ /^user.+/
82
+ end
83
+
84
+ it "gives it's account a name and keyword" do
85
+ subject.account.name.should_not be_blank
86
+ subject.account.keyword.should_not be_blank
87
+ end
88
+
89
+ it "gives it's user a name that is the first part of it's email" do
90
+ subject.user.name.should == "user"
91
+ end
92
+ end
93
+
94
+ describe Signup, "with an email with symbols in it" do
95
+ subject { Factory.build(:signup, :email => "user+extra@example.com") }
96
+
97
+ before do
98
+ subject.save
99
+ end
100
+
101
+ it "gives a friendly account name" do
102
+ subject.account.name.should == "user-extra"
103
+ end
104
+
105
+ it "gives a friendly account keyword" do
106
+ subject.account.keyword.should =~ /^user-extra+/
107
+ end
108
+ end
109
+
110
+ describe Signup, "with an invalid user" do
111
+ subject { Factory.build(:signup, :email => nil) }
112
+ it_should_behave_like "invalid signup"
113
+
114
+ before do
115
+ @result = subject.save
116
+ end
117
+
118
+ it "adds error messages" do
119
+ subject.errors[:email].should_not be_empty
120
+ end
121
+ end
122
+
123
+ describe Signup, "valid with an existing user and correct password" do
124
+ it_should_behave_like "valid signup"
125
+ let(:email) { "user@example.com" }
126
+ let(:password) { "test" }
127
+ let!(:user) { Factory(:user, :email => email, :password => password) }
128
+ subject { Factory.build(:signup, :email => email, :password => password) }
129
+ before { @result = subject.save }
130
+
131
+ it "doesn't create a user" do
132
+ User.count.should == 1
133
+ end
134
+
135
+ it "assigns the user to the account as an admin" do
136
+ user.reload.accounts.should include(subject.account)
137
+ user.should be_admin_of(subject.account)
138
+ end
139
+ end
140
+
141
+ describe Signup, "valid with a signed in user" do
142
+ it_should_behave_like "valid signup"
143
+ let!(:user) { Factory(:user) }
144
+ subject { Factory.build(:signup, :user => user, :password => '') }
145
+ before { @result = subject.save }
146
+
147
+ it "doesn't create a user" do
148
+ User.count.should == 1
149
+ end
150
+
151
+ it "assigns the user to the account as an admin" do
152
+ user.reload.accounts.should include(subject.account)
153
+ user.should be_admin_of(subject.account)
154
+ end
155
+ end
156
+
157
+ describe Signup, "valid with an existing user and incorrect password" do
158
+ it_should_behave_like "invalid signup"
159
+ let(:email) { "user@example.com" }
160
+ let(:password) { "test" }
161
+ let!(:user) { Factory(:user, :email => email, :password => password) }
162
+ subject { Factory.build(:signup, :email => email, :password => 'wrong') }
163
+ before { @result = subject.save }
164
+
165
+ it "adds error messages" do
166
+ subject.errors.full_messages.should include("Password is incorrect")
167
+ end
168
+ end
169
+
170
+ describe Signup, "with an account that doesn't save" do
171
+ subject { Factory.build(:signup) }
172
+
173
+ it "doesn't raise the transaction and returns false" do
174
+ Account.any_instance.stubs(:save!).raises(ActiveRecord::RecordNotSaved)
175
+ subject.save.should_not be
176
+ end
177
+ end