saasy 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.md +114 -0
- data/Gemfile +26 -0
- data/README.md +118 -0
- data/Rakefile +38 -0
- data/app/controllers/accounts_controller.rb +68 -0
- data/app/controllers/billings_controller.rb +25 -0
- data/app/controllers/invitations_controller.rb +65 -0
- data/app/controllers/memberships_controller.rb +45 -0
- data/app/controllers/plans_controller.rb +24 -0
- data/app/controllers/profiles_controller.rb +19 -0
- data/app/helpers/limits_helper.rb +13 -0
- data/app/mailers/billing_mailer.rb +53 -0
- data/app/mailers/invitation_mailer.rb +18 -0
- data/app/models/invitation.rb +113 -0
- data/app/models/limit.rb +49 -0
- data/app/models/membership.rb +26 -0
- data/app/models/permission.rb +19 -0
- data/app/models/signup.rb +163 -0
- data/app/views/accounts/_account.html.erb +9 -0
- data/app/views/accounts/_blank_slate.html.erb +6 -0
- data/app/views/accounts/_projects.html.erb +12 -0
- data/app/views/accounts/_subnav.html.erb +10 -0
- data/app/views/accounts/edit.html.erb +34 -0
- data/app/views/accounts/index.html.erb +9 -0
- data/app/views/accounts/new.html.erb +36 -0
- data/app/views/billing_mailer/completed_trial.text.erb +13 -0
- data/app/views/billing_mailer/expiring_trial.text.erb +15 -0
- data/app/views/billing_mailer/new_unactivated.text.erb +1 -0
- data/app/views/billing_mailer/problem.html.erb +13 -0
- data/app/views/billing_mailer/problem.text.erb +14 -0
- data/app/views/billing_mailer/receipt.html.erb +41 -0
- data/app/views/billing_mailer/receipt.text.erb +25 -0
- data/app/views/billings/_form.html.erb +8 -0
- data/app/views/billings/edit.html.erb +13 -0
- data/app/views/billings/show.html.erb +29 -0
- data/app/views/invitation_mailer/invitation.text.erb +6 -0
- data/app/views/invitations/new.html.erb +17 -0
- data/app/views/invitations/show.html.erb +22 -0
- data/app/views/layouts/saucy.html.erb +36 -0
- data/app/views/limits/_meter.html.erb +13 -0
- data/app/views/memberships/edit.html.erb +21 -0
- data/app/views/memberships/index.html.erb +17 -0
- data/app/views/plans/_plan.html.erb +32 -0
- data/app/views/plans/_terms.html.erb +15 -0
- data/app/views/plans/edit.html.erb +33 -0
- data/app/views/plans/index.html.erb +12 -0
- data/app/views/profiles/_inputs.html.erb +5 -0
- data/app/views/profiles/edit.html.erb +36 -0
- data/app/views/projects/_form.html.erb +36 -0
- data/app/views/projects/edit.html.erb +22 -0
- data/app/views/projects/index.html.erb +28 -0
- data/app/views/projects/new.html.erb +13 -0
- data/app/views/projects/show.html.erb +0 -0
- data/app/views/shared/_project_dropdown.html.erb +55 -0
- data/app/views/shared/_saucy_javascript.html.erb +33 -0
- data/config/locales/en.yml +37 -0
- data/config/routes.rb +19 -0
- data/features/run_features.feature +83 -0
- data/features/step_definitions/clearance_steps.rb +45 -0
- data/features/step_definitions/rails_steps.rb +73 -0
- data/features/step_definitions/saucy_steps.rb +8 -0
- data/features/support/env.rb +4 -0
- data/features/support/file.rb +11 -0
- data/lib/generators/saucy/base.rb +18 -0
- data/lib/generators/saucy/features/features_generator.rb +91 -0
- data/lib/generators/saucy/features/templates/README +3 -0
- data/lib/generators/saucy/features/templates/factories.rb +71 -0
- data/lib/generators/saucy/features/templates/features/edit_profile.feature +9 -0
- data/lib/generators/saucy/features/templates/features/edit_project_permissions.feature +37 -0
- data/lib/generators/saucy/features/templates/features/edit_user_permissions.feature +47 -0
- data/lib/generators/saucy/features/templates/features/manage_account.feature +35 -0
- data/lib/generators/saucy/features/templates/features/manage_billing.feature +93 -0
- data/lib/generators/saucy/features/templates/features/manage_plan.feature +143 -0
- data/lib/generators/saucy/features/templates/features/manage_projects.feature +139 -0
- data/lib/generators/saucy/features/templates/features/manage_users.feature +142 -0
- data/lib/generators/saucy/features/templates/features/new_account.feature +33 -0
- data/lib/generators/saucy/features/templates/features/project_dropdown.feature +77 -0
- data/lib/generators/saucy/features/templates/features/sign_up.feature +32 -0
- data/lib/generators/saucy/features/templates/features/sign_up_paid.feature +65 -0
- data/lib/generators/saucy/features/templates/features/trial_plans.feature +82 -0
- data/lib/generators/saucy/features/templates/step_definitions/account_steps.rb +30 -0
- data/lib/generators/saucy/features/templates/step_definitions/braintree_steps.rb +25 -0
- data/lib/generators/saucy/features/templates/step_definitions/cron_steps.rb +23 -0
- data/lib/generators/saucy/features/templates/step_definitions/email_steps.rb +40 -0
- data/lib/generators/saucy/features/templates/step_definitions/factory_girl_steps.rb +1 -0
- data/lib/generators/saucy/features/templates/step_definitions/html_steps.rb +51 -0
- data/lib/generators/saucy/features/templates/step_definitions/plan_steps.rb +16 -0
- data/lib/generators/saucy/features/templates/step_definitions/project_steps.rb +4 -0
- data/lib/generators/saucy/features/templates/step_definitions/session_steps.rb +37 -0
- data/lib/generators/saucy/features/templates/step_definitions/user_steps.rb +100 -0
- data/lib/generators/saucy/features/templates/support/braintree.rb +5 -0
- data/lib/generators/saucy/install/install_generator.rb +40 -0
- data/lib/generators/saucy/install/templates/controllers/projects_controller.rb +3 -0
- data/lib/generators/saucy/install/templates/create_saucy_tables.rb +115 -0
- data/lib/generators/saucy/install/templates/models/account.rb +3 -0
- data/lib/generators/saucy/install/templates/models/plan.rb +3 -0
- data/lib/generators/saucy/install/templates/models/project.rb +3 -0
- data/lib/generators/saucy/specs/specs_generator.rb +20 -0
- data/lib/generators/saucy/specs/templates/support/braintree.rb +5 -0
- data/lib/generators/saucy/views/views_generator.rb +23 -0
- data/lib/saucy.rb +10 -0
- data/lib/saucy/account.rb +132 -0
- data/lib/saucy/account_authorization.rb +67 -0
- data/lib/saucy/configuration.rb +29 -0
- data/lib/saucy/engine.rb +35 -0
- data/lib/saucy/fake_braintree.rb +134 -0
- data/lib/saucy/layouts.rb +36 -0
- data/lib/saucy/plan.rb +54 -0
- data/lib/saucy/project.rb +125 -0
- data/lib/saucy/projects_controller.rb +94 -0
- data/lib/saucy/railties/tasks.rake +28 -0
- data/lib/saucy/routing_extensions.rb +121 -0
- data/lib/saucy/subscription.rb +237 -0
- data/lib/saucy/user.rb +30 -0
- data/spec/controllers/accounts_controller_spec.rb +228 -0
- data/spec/controllers/application_controller_spec.rb +32 -0
- data/spec/controllers/invitations_controller_spec.rb +215 -0
- data/spec/controllers/memberships_controller_spec.rb +117 -0
- data/spec/controllers/plans_controller_spec.rb +13 -0
- data/spec/controllers/profiles_controller_spec.rb +48 -0
- data/spec/controllers/projects_controller_spec.rb +216 -0
- data/spec/environment.rb +95 -0
- data/spec/layouts_spec.rb +21 -0
- data/spec/mailers/billing_mailer_spec.rb +68 -0
- data/spec/mailers/invitiation_mailer_spec.rb +19 -0
- data/spec/models/account_spec.rb +218 -0
- data/spec/models/invitation_spec.rb +320 -0
- data/spec/models/limit_spec.rb +70 -0
- data/spec/models/membership_spec.rb +37 -0
- data/spec/models/permission_spec.rb +30 -0
- data/spec/models/plan_spec.rb +81 -0
- data/spec/models/project_spec.rb +223 -0
- data/spec/models/signup_spec.rb +177 -0
- data/spec/models/subscription_spec.rb +481 -0
- data/spec/models/user_spec.rb +72 -0
- data/spec/route_extensions_spec.rb +51 -0
- data/spec/saucy_spec.rb +62 -0
- data/spec/scaffold/config/routes.rb +5 -0
- data/spec/spec_helper.rb +39 -0
- data/spec/support/authentication_helpers.rb +81 -0
- data/spec/support/authorization_helpers.rb +56 -0
- data/spec/support/braintree.rb +7 -0
- data/spec/support/clearance_matchers.rb +55 -0
- data/spec/support/notifications.rb +57 -0
- data/spec/views/accounts/_account.html.erb_spec.rb +37 -0
- 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
|