saasy 0.0.1

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 (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