user_plane 0.0.15
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/MIT-LICENSE +20 -0
- data/Rakefile +30 -0
- data/app/assets/javascripts/user_plane/application.js +13 -0
- data/app/assets/stylesheets/user_plane/application.css +15 -0
- data/app/concerns/null_object_persistable.rb +62 -0
- data/app/controllers/user/confirm_email_addresses_controller.rb +17 -0
- data/app/controllers/user/details_controller.rb +48 -0
- data/app/controllers/user/invites_controller.rb +69 -0
- data/app/controllers/user/reset_passwords_controller.rb +50 -0
- data/app/controllers/user/sign_ins_controller.rb +53 -0
- data/app/controllers/user/sign_ups_controller.rb +47 -0
- data/app/controllers/user_plane/application_controller.rb +5 -0
- data/app/helpers/user_plane/application_helper.rb +4 -0
- data/app/mailers/user_plane/application_mailer.rb +8 -0
- data/app/mailers/user_plane/invite_mailer.rb +14 -0
- data/app/mailers/user_plane/verification_mailer.rb +25 -0
- data/app/models/session_manager.rb +100 -0
- data/app/models/user.rb +5 -0
- data/app/models/user/account.rb +26 -0
- data/app/models/user/confirm_email_address.rb +55 -0
- data/app/models/user/guest.rb +18 -0
- data/app/models/user/identities.rb +5 -0
- data/app/models/user/identities/email.rb +98 -0
- data/app/models/user/identities/email_verification.rb +70 -0
- data/app/models/user/identities/facebook.rb +5 -0
- data/app/models/user/identities/github.rb +5 -0
- data/app/models/user/identities/id_token.rb +7 -0
- data/app/models/user/identities/o_auth.rb +67 -0
- data/app/models/user/identities/o_auth_endpoint.rb +28 -0
- data/app/models/user/identities/twitter.rb +5 -0
- data/app/models/user/identity.rb +26 -0
- data/app/models/user/reset_password.rb +59 -0
- data/app/models/user/send_password_reset.rb +25 -0
- data/app/models/user/send_sign_up_invite.rb +27 -0
- data/app/models/user/sign_in.rb +42 -0
- data/app/models/user/sign_up.rb +47 -0
- data/app/models/user/sign_up_invites.rb +5 -0
- data/app/models/user/sign_up_invites/invite.rb +46 -0
- data/app/models/user/sign_up_invites/stack.rb +22 -0
- data/app/models/user/sign_up_with_invite.rb +45 -0
- data/app/models/user/suspension.rb +7 -0
- data/app/models/user/update_details.rb +103 -0
- data/app/views/layouts/user_plane/application.html.erb +14 -0
- data/app/views/user/details/edit.html.erb +37 -0
- data/app/views/user/invites/edit.html.erb +34 -0
- data/app/views/user/invites/new.html.erb +21 -0
- data/app/views/user/reset_passwords/edit.html.erb +26 -0
- data/app/views/user/reset_passwords/new.html.erb +21 -0
- data/app/views/user/sign_ins/new.html.erb +25 -0
- data/app/views/user/sign_ups/new.html.erb +33 -0
- data/app/views/user_plane/invite_mailer/invite.html.erb +2 -0
- data/app/views/user_plane/invite_mailer/invite.text.erb +3 -0
- data/app/views/user_plane/verification_mailer/address_verification.html.erb +2 -0
- data/app/views/user_plane/verification_mailer/address_verification.text.erb +4 -0
- data/app/views/user_plane/verification_mailer/password_reset.html.erb +2 -0
- data/app/views/user_plane/verification_mailer/password_reset.text.erb +4 -0
- data/config/initializers/inflections.rb +3 -0
- data/config/locales/en.yml +63 -0
- data/config/locales/it.yml +62 -0
- data/config/routes.rb +5 -0
- data/db/migrate/20121128143404_create_user_accounts.rb +11 -0
- data/db/migrate/20121226202553_create_user_identities_o_auths.rb +12 -0
- data/db/migrate/20121226203032_create_user_identities_emails.rb +13 -0
- data/db/migrate/20121227144617_create_user_identities_email_verifications.rb +15 -0
- data/db/migrate/20130113120152_create_user_identities_id_tokens.rb +12 -0
- data/db/migrate/20141025230304_create_user_sign_up_invites_stacks.rb +10 -0
- data/db/migrate/20141025230500_create_user_sign_up_invites_invites.rb +13 -0
- data/db/migrate/20141026230208_create_user_suspensions.rb +13 -0
- data/lib/generators/user_plane/view/details_generator.rb +22 -0
- data/lib/generators/user_plane/view/helpers.rb +68 -0
- data/lib/generators/user_plane/view/invites_generator.rb +23 -0
- data/lib/generators/user_plane/view/reset_passwords_generator.rb +22 -0
- data/lib/generators/user_plane/view/sign_ins_generator.rb +18 -0
- data/lib/generators/user_plane/view/sign_ups_generator.rb +22 -0
- data/lib/generators/user_plane/views_generator.rb +32 -0
- data/lib/tasks/user_plane_tasks.rake +4 -0
- data/lib/user_plane.rb +43 -0
- data/lib/user_plane/command.rb +24 -0
- data/lib/user_plane/engine.rb +27 -0
- data/lib/user_plane/fresh_validator.rb +9 -0
- data/lib/user_plane/omniauth.rb +50 -0
- data/lib/user_plane/redirect_to_sign_in.rb +22 -0
- data/lib/user_plane/route_concerns.rb +167 -0
- data/lib/user_plane/session_manager_concern.rb +9 -0
- data/lib/user_plane/signed_in_constraint.rb +11 -0
- data/lib/user_plane/token_segment.rb +52 -0
- data/lib/user_plane/version.rb +3 -0
- data/spec/controllers/user/confirm_email_addresses_controller_spec.rb +5 -0
- data/spec/controllers/user/details_controller_spec.rb +5 -0
- data/spec/controllers/user/invites_controller_spec.rb +19 -0
- data/spec/controllers/user/reset_passwords_controller_spec.rb +5 -0
- data/spec/controllers/user/sign_ins_controller_spec.rb +34 -0
- data/spec/controllers/user/sign_ups_controller_spec.rb +5 -0
- data/spec/dummy/README.rdoc +28 -0
- data/spec/dummy/Rakefile +6 -0
- data/spec/dummy/app/assets/javascripts/application.js +13 -0
- data/spec/dummy/app/assets/stylesheets/application.css +15 -0
- data/spec/dummy/app/controllers/application_controller.rb +7 -0
- data/spec/dummy/app/controllers/welcome_controller.rb +3 -0
- data/spec/dummy/app/helpers/application_helper.rb +2 -0
- data/spec/dummy/app/views/layouts/application.html.erb +30 -0
- data/spec/dummy/app/views/welcome/index.html.erb +1 -0
- data/spec/dummy/bin/bundle +3 -0
- data/spec/dummy/bin/rails +4 -0
- data/spec/dummy/bin/rake +4 -0
- data/spec/dummy/config.ru +4 -0
- data/spec/dummy/config/application.rb +30 -0
- data/spec/dummy/config/boot.rb +5 -0
- data/spec/dummy/config/database.yml +49 -0
- data/spec/dummy/config/environment.rb +5 -0
- data/spec/dummy/config/environments/development.rb +37 -0
- data/spec/dummy/config/environments/production.rb +78 -0
- data/spec/dummy/config/environments/test.rb +39 -0
- data/spec/dummy/config/initializers/assets.rb +8 -0
- data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/spec/dummy/config/initializers/cookies_serializer.rb +3 -0
- data/spec/dummy/config/initializers/filter_parameter_logging.rb +4 -0
- data/spec/dummy/config/initializers/inflections.rb +16 -0
- data/spec/dummy/config/initializers/mime_types.rb +4 -0
- data/spec/dummy/config/initializers/session_store.rb +3 -0
- data/spec/dummy/config/initializers/user_plane.rb +5 -0
- data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/spec/dummy/config/locales/en.yml +23 -0
- data/spec/dummy/config/routes.rb +43 -0
- data/spec/dummy/config/secrets.yml +22 -0
- data/spec/dummy/db/schema.rb +101 -0
- data/spec/dummy/log/development.log +0 -0
- data/spec/dummy/log/test.log +20185 -0
- data/spec/dummy/public/404.html +67 -0
- data/spec/dummy/public/422.html +67 -0
- data/spec/dummy/public/500.html +66 -0
- data/spec/dummy/public/favicon.ico +0 -0
- data/spec/dummy/tmp/cache/assets/test/sprockets/v3.0/-pOuxJZhYk_qXqMNKgm23KfvzyUW71NynNLlcNBOubE.cache +0 -0
- data/spec/dummy/tmp/cache/assets/test/sprockets/v3.0/3AV9ywHBH56Leqey5LeznxK9vu4HD8fF3zSTk4MiDJA.cache +1 -0
- data/spec/dummy/tmp/cache/assets/test/sprockets/v3.0/5fzR1G0D8ukHkPkLXsUu6rP6qV82aIdx3hugKkDy6nM.cache +0 -0
- data/spec/dummy/tmp/cache/assets/test/sprockets/v3.0/9bKtJ2lkHPqtboGfbyknZ1OyH4xYO-aml7U3qhv-3kk.cache +0 -0
- data/spec/dummy/tmp/cache/assets/test/sprockets/v3.0/HjDmE9SFP2wimdNHU8Nff9cm3vFZ5soO1iw7Jdlb6z8.cache +0 -0
- data/spec/dummy/tmp/cache/assets/test/sprockets/v3.0/J9j6OdatarYW7VzVCVttmGphOhJKL0QXasdheyrgsTE.cache +2 -0
- data/spec/dummy/tmp/cache/assets/test/sprockets/v3.0/KqrOQSlg0Th0N3XXx-h4p5BVJCfN0D8rRLoA9VxvXrc.cache +1 -0
- data/spec/dummy/tmp/cache/assets/test/sprockets/v3.0/UCB1W65KwVU8ttOY8jnPRDp8HyyYYEjeTwwPD6R4qy8.cache +1 -0
- data/spec/dummy/tmp/cache/assets/test/sprockets/v3.0/YmMSaaBmIcNZWPVF9jXcGBi-kwEzMuxzwPT_Zrcj1Bo.cache +2 -0
- data/spec/dummy/tmp/cache/assets/test/sprockets/v3.0/g3wU8ajFWb5ZLPvujEt5l9DesbFCiAwqjx1WQgwTtHA.cache +1 -0
- data/spec/dummy/tmp/cache/assets/test/sprockets/v3.0/i5pp88VHKoqlxQJdgmQd_lkgX1-4em_uHqNDjQ4nyHA.cache +0 -0
- data/spec/dummy/tmp/cache/assets/test/sprockets/v3.0/jL74yXjxf8cb6Olkjbw1C28MH_HbZe221l8AI6WVeH0.cache +3 -0
- data/spec/dummy/tmp/cache/assets/test/sprockets/v3.0/puh7X4rfS3eDN9oHTXoQdAgqxivonrwAAdYZ4UB3GIg.cache +1 -0
- data/spec/dummy/tmp/cache/assets/test/sprockets/v3.0/qxPWFIWnE6gOCY-SsdBJe7Cgm5D3YUwaEne78Y7XdRg.cache +1 -0
- data/spec/dummy/tmp/cache/assets/test/sprockets/v3.0/rM9s67WgzKMZ1bRhUdA0yhPZDlyRE5a1kmdt7cS6m4c.cache +3 -0
- data/spec/dummy/tmp/cache/assets/test/sprockets/v3.0/rVoG8EHlrCOgY4ZkzOj64f0jiTcbteQ_SYNzq9RqY0I.cache +0 -0
- data/spec/fabricators/user_account_fabricator.rb +11 -0
- data/spec/fabricators/user_guest_fabricator.rb +2 -0
- data/spec/fabricators/user_identities_email_fabricator.rb +8 -0
- data/spec/fabricators/user_identities_email_verification_fabricator.rb +5 -0
- data/spec/fabricators/user_identities_id_token_fabricator.rb +2 -0
- data/spec/fabricators/user_sign_up_fabricator.rb +51 -0
- data/spec/fabricators/user_sign_up_invites_invite_fabricator.rb +3 -0
- data/spec/fabricators/user_sign_up_invites_stack_fabricator.rb +4 -0
- data/spec/fabricators/user_suspension_fabricator.rb +4 -0
- data/spec/fabricators/user_update_detail_fabricator.rb +2 -0
- data/spec/features/user_plane/user_plane_invites_spec.rb +31 -0
- data/spec/features/user_plane/user_plane_reset_passwords_spec.rb +31 -0
- data/spec/features/user_plane/user_plane_sign_ins_spec.rb +44 -0
- data/spec/features/user_plane/user_plane_signed_in_only_spec.rb +31 -0
- data/spec/features/user_plane/user_plane_update_details_spec.rb +43 -0
- data/spec/fixtures/user_plane/invite_mailer/invite +3 -0
- data/spec/fixtures/user_plane/verification_mailer/address_verification +3 -0
- data/spec/fixtures/user_plane/verification_mailer/password_reset +3 -0
- data/spec/lib/generators/views_generator_spec.rb +16 -0
- data/spec/lib/route_concerns_spec.rb +54 -0
- data/spec/mailers/previews/user_plane/invite_mailer_preview.rb +11 -0
- data/spec/mailers/previews/user_plane/verification_mailer_preview.rb +16 -0
- data/spec/mailers/user_plane/invite_mailer_spec.rb +25 -0
- data/spec/mailers/user_plane/verification_mailer_spec.rb +52 -0
- data/spec/models/session_manager_spec.rb +28 -0
- data/spec/models/user/account_spec.rb +26 -0
- data/spec/models/user/confirm_email_address_spec.rb +101 -0
- data/spec/models/user/guest_spec.rb +5 -0
- data/spec/models/user/identities/email_spec.rb +5 -0
- data/spec/models/user/identities/email_verification_spec.rb +42 -0
- data/spec/models/user/identities/facebook_spec.rb +5 -0
- data/spec/models/user/identities/github_spec.rb +5 -0
- data/spec/models/user/identities/id_token_spec.rb +5 -0
- data/spec/models/user/identities/o_auth_spec.rb +12 -0
- data/spec/models/user/identities/twitter_spec.rb +5 -0
- data/spec/models/user/reset_password_spec.rb +141 -0
- data/spec/models/user/send_password_reset_spec.rb +44 -0
- data/spec/models/user/send_sign_up_invite_spec.rb +30 -0
- data/spec/models/user/sign_in_spec.rb +31 -0
- data/spec/models/user/sign_up_invites/invite_spec.rb +13 -0
- data/spec/models/user/sign_up_invites/stack_spec.rb +21 -0
- data/spec/models/user/sign_up_spec.rb +58 -0
- data/spec/models/user/sign_up_with_invite_spec.rb +83 -0
- data/spec/models/user/suspension_spec.rb +5 -0
- data/spec/models/user/update_details_spec.rb +98 -0
- data/spec/routing/invites_spec.rb +49 -0
- data/spec/routing/reset_passwords_spec.rb +31 -0
- data/spec/routing/sign_ins_spec.rb +36 -0
- data/spec/routing/update_details_spec.rb +30 -0
- data/spec/shared_contexts/feature_helpers.rb +12 -0
- data/spec/shared_contexts/routing.rb +8 -0
- data/spec/shared_contexts/user.rb +67 -0
- data/spec/spec_helper.rb +38 -0
- data/spec/support/fabrication.rb +7 -0
- data/spec/support/omniauth.rb +4 -0
- metadata +770 -0
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe User::Identities::EmailVerification do
|
4
|
+
subject :password_reset do
|
5
|
+
described_class.password_reset.create(recipient: Faker::Internet.safe_email)
|
6
|
+
end
|
7
|
+
|
8
|
+
it 'can be spent' do
|
9
|
+
password_reset.spend
|
10
|
+
expect(password_reset).to be_valid
|
11
|
+
end
|
12
|
+
|
13
|
+
|
14
|
+
context 'after two weeks' do
|
15
|
+
subject :old_password_reset do
|
16
|
+
verification_life_span = 2.weeks + 1.day
|
17
|
+
Timecop.travel verification_life_span.ago do
|
18
|
+
described_class.password_reset.create(recipient: Faker::Internet.safe_email)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
it {is_expected.to be_stale}
|
23
|
+
|
24
|
+
it 'cannot be spent' do
|
25
|
+
old_password_reset.spend
|
26
|
+
expect(old_password_reset).not_to be_valid
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
context 'once spent' do
|
31
|
+
subject :spent_password_reset do
|
32
|
+
described_class.password_reset.create(recipient: Faker::Internet.safe_email, spent_at: Time.now)
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'cannot be spent again' do
|
36
|
+
spent_password_reset.spend
|
37
|
+
expect(spent_password_reset).not_to be_valid
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'shared_contexts/user'
|
3
|
+
|
4
|
+
describe User::Identities::OAuth do
|
5
|
+
include_context 'user'
|
6
|
+
|
7
|
+
it 'creates the identity from oauth data' do
|
8
|
+
oauth_identity = described_class.find_or_build_from_omniauth(facebook_oauth_data)
|
9
|
+
|
10
|
+
expect(oauth_identity).to be_a(User::Identities::Facebook)
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,141 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'shared_contexts/user'
|
3
|
+
|
4
|
+
describe User::ResetPassword do
|
5
|
+
include_context 'user'
|
6
|
+
|
7
|
+
def validated_reset_password token, password, confirmation=nil
|
8
|
+
confirmation = password if confirmation.nil?
|
9
|
+
reset_password = described_class.new(code: token,
|
10
|
+
password: password,
|
11
|
+
password_confirmation: confirmation)
|
12
|
+
reset_password.valid?
|
13
|
+
reset_password
|
14
|
+
end
|
15
|
+
|
16
|
+
|
17
|
+
context 'with a valid reset token' do
|
18
|
+
let (:reset_token) do
|
19
|
+
password_reset_verification = a_user.email.reset_password!
|
20
|
+
password_reset_verification.token
|
21
|
+
end
|
22
|
+
|
23
|
+
let! (:old_password) {a_user.email.password}
|
24
|
+
|
25
|
+
context 'does not miss validation callbacks' do
|
26
|
+
subject :reset_password do
|
27
|
+
described_class.new(code: reset_token,
|
28
|
+
password: new_password,
|
29
|
+
password_confirmation: new_password)
|
30
|
+
end
|
31
|
+
|
32
|
+
it 'resets the password without skipping the before_validation' do
|
33
|
+
reset_password.perform
|
34
|
+
a_user.reload
|
35
|
+
expect(a_user.email.authenticate(new_password)).not_to be false
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
context 'and a valid password' do
|
40
|
+
subject :reset_password do
|
41
|
+
described_class.new(code: reset_token,
|
42
|
+
password: new_password,
|
43
|
+
password_confirmation: new_password)
|
44
|
+
end
|
45
|
+
|
46
|
+
it {is_expected.to be_valid}
|
47
|
+
|
48
|
+
it 'resets the password' do
|
49
|
+
reset_password.perform!
|
50
|
+
a_user.reload
|
51
|
+
expect(a_user.email.authenticate(new_password)).not_to be false
|
52
|
+
end
|
53
|
+
|
54
|
+
context 'once performed' do
|
55
|
+
before do
|
56
|
+
reset_password.perform!
|
57
|
+
a_user.reload
|
58
|
+
end
|
59
|
+
|
60
|
+
it 'spends the verification token' do
|
61
|
+
reset_password.verification.reload
|
62
|
+
expect(reset_password.verification).to be_spent
|
63
|
+
end
|
64
|
+
|
65
|
+
it 'the new password can be used to authenticate' do
|
66
|
+
expect(a_user.email.authenticate(new_password)).not_to be false
|
67
|
+
end
|
68
|
+
|
69
|
+
it 'a wrong password cannot be used to authenticate' do
|
70
|
+
expect(a_user.email.authenticate('not the new password')).to be false
|
71
|
+
end
|
72
|
+
|
73
|
+
it 'the old password cannot be used to authenticate' do
|
74
|
+
expect(a_user.email.authenticate(old_password)).to be false
|
75
|
+
end
|
76
|
+
|
77
|
+
it 'cannot be redeemed anymore' do
|
78
|
+
another_reset = described_class.new(code: reset_token,
|
79
|
+
password: new_password,
|
80
|
+
password_confirmation: new_password)
|
81
|
+
expect(another_reset).not_to be_valid
|
82
|
+
expect(another_reset.errors[:code].size).to eq(1)
|
83
|
+
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
context 'and a bad password' do
|
89
|
+
|
90
|
+
|
91
|
+
let :empty_reset_password do
|
92
|
+
validated_reset_password(reset_token, '')
|
93
|
+
end
|
94
|
+
|
95
|
+
let :short_reset_password do
|
96
|
+
validated_reset_password(reset_token, 'pw')
|
97
|
+
end
|
98
|
+
|
99
|
+
let :mismatching_reset_password do
|
100
|
+
validated_reset_password(reset_token, new_password, 'mismatching confirmation')
|
101
|
+
end
|
102
|
+
|
103
|
+
it 'does not allow empty passwords' do
|
104
|
+
expect(empty_reset_password.errors.messages[:password].size).to eq(1)
|
105
|
+
end
|
106
|
+
|
107
|
+
it 'validates password length' do
|
108
|
+
expect(short_reset_password.errors.messages[:password].size).to eq(1)
|
109
|
+
end
|
110
|
+
|
111
|
+
it 'requires confirmation' do
|
112
|
+
expect(mismatching_reset_password.errors.messages[:password_confirmation].size).to eq(1)
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
context 'without a valid reset token' do
|
118
|
+
subject :reset_password do
|
119
|
+
validated_reset_password('invalid token', new_password)
|
120
|
+
end
|
121
|
+
|
122
|
+
it {expect {subject}.to raise_error ActiveRecord::RecordNotFound}
|
123
|
+
end
|
124
|
+
|
125
|
+
context 'with an expired verification token' do
|
126
|
+
let (:stale_verification_token) do
|
127
|
+
Timecop.travel (2.weeks + 1.day).ago do
|
128
|
+
address_verification = a_user.email.reset_password!
|
129
|
+
address_verification.token
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
subject :reset_password_with_stale_token do
|
134
|
+
validated_reset_password(stale_verification_token, new_password)
|
135
|
+
end
|
136
|
+
|
137
|
+
it {is_expected.not_to be_valid}
|
138
|
+
it {expect(reset_password_with_stale_token.errors.messages[:code].size).to eq(1)}
|
139
|
+
end
|
140
|
+
|
141
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'shared_contexts/user'
|
3
|
+
|
4
|
+
describe User::SendPasswordReset, type: :model do
|
5
|
+
include_context 'user'
|
6
|
+
|
7
|
+
let :dummy_verification do
|
8
|
+
double(User::Identities::EmailVerification, save: true, token: '123456')
|
9
|
+
end
|
10
|
+
|
11
|
+
context 'resetting the password for an existing account' do
|
12
|
+
subject(:send_reset) {described_class.new(email: a_user.email.address)}
|
13
|
+
|
14
|
+
it 'has a verification code' do
|
15
|
+
send_reset.perform!
|
16
|
+
expect(send_reset.code).not_to be_nil
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'does create an email verification' do
|
20
|
+
expect(User::Identities::EmailVerification).to receive(:new).and_return(dummy_verification)
|
21
|
+
send_reset.perform!
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
context 'with an unknown email address' do
|
26
|
+
before do
|
27
|
+
allow(User::Identities::Email).to receive(:find_by_address).and_return(nil)
|
28
|
+
end
|
29
|
+
|
30
|
+
subject(:send_reset) {described_class.new(email: 'dont.find.me@example.com')}
|
31
|
+
|
32
|
+
it {is_expected.to be_valid}
|
33
|
+
|
34
|
+
it 'has no verification code' do
|
35
|
+
send_reset.perform!
|
36
|
+
expect(send_reset.code).to be_nil
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'does not create an email verification' do
|
40
|
+
expect(User::Identities::EmailVerification).not_to receive(:new)
|
41
|
+
send_reset.perform!
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'shared_contexts/user'
|
3
|
+
|
4
|
+
describe User::SendSignUpInvite do
|
5
|
+
include_context 'user'
|
6
|
+
|
7
|
+
subject(:send_invite) {described_class.new(sender: a_user, recipient: invite_recipient)}
|
8
|
+
|
9
|
+
context 'When the sender has one invite remaining' do
|
10
|
+
before do
|
11
|
+
a_user.invites_stack.update({remaining_invites: 1})
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'is used to send invites' do
|
15
|
+
send_invite.perform!
|
16
|
+
expect(send_invite.invite).not_to be_nil
|
17
|
+
expect(send_invite.stack.remaining_invites).to eq(0)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
context 'if the user has no invites left' do
|
22
|
+
before do
|
23
|
+
a_user.invites_stack.update({remaining_invites: 0})
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'is used to send invites' do
|
27
|
+
expect(send_invite).not_to be_valid
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'shared_contexts/user'
|
3
|
+
|
4
|
+
describe User::SignIn do
|
5
|
+
include_context 'user'
|
6
|
+
|
7
|
+
context 'with a valid email/password combination' do
|
8
|
+
subject :sign_in do
|
9
|
+
described_class.new(email: a_user.email.address,
|
10
|
+
password: a_user.email.password)
|
11
|
+
.sign_in_with(User::Identities::Email)
|
12
|
+
end
|
13
|
+
|
14
|
+
it {is_expected.to be_valid}
|
15
|
+
end
|
16
|
+
|
17
|
+
context 'with a bad email/password combination' do
|
18
|
+
subject :sign_in do
|
19
|
+
described_class.new(email: a_user.email.address,
|
20
|
+
password: '')
|
21
|
+
.sign_in_with(User::Identities::Email)
|
22
|
+
end
|
23
|
+
|
24
|
+
before {subject.valid?}
|
25
|
+
|
26
|
+
it {is_expected.not_to be_valid}
|
27
|
+
it {expect(subject.errors).to include(:base)}
|
28
|
+
it {expect(subject.errors).not_to include(:identity)}
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe User::SignUpInvites::Invite, type: :model do
|
4
|
+
context 'validates the recipient format' do
|
5
|
+
subject(:invite) { described_class.new(recipient: 'not a vaild email') }
|
6
|
+
|
7
|
+
it 'is not allowed more invites' do
|
8
|
+
expect(invite).not_to be_valid
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe User::SignUpInvites::Stack, :type => :model do
|
4
|
+
|
5
|
+
subject(:invites_stack) {described_class.create(remaining_invites: 2)}
|
6
|
+
|
7
|
+
it 'decrements the available invites' do
|
8
|
+
invites_stack.invites.create(recipient: Faker::Internet.safe_email)
|
9
|
+
expect(invites_stack.remaining_invites).to eql(1)
|
10
|
+
end
|
11
|
+
|
12
|
+
context 'when the stack is out of invites' do
|
13
|
+
subject(:dry_invites_stack) {described_class.create(remaining_invites: 0)}
|
14
|
+
|
15
|
+
it 'is not allowed more invites' do
|
16
|
+
new_invite = dry_invites_stack.invites.create(recipient: Faker::Internet.safe_email)
|
17
|
+
expect(dry_invites_stack).not_to be_valid
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'shared_contexts/user'
|
3
|
+
|
4
|
+
describe User::SignUp do
|
5
|
+
include_context 'user'
|
6
|
+
|
7
|
+
context 'with valid details' do
|
8
|
+
subject :sign_up do
|
9
|
+
p = 'some secret'
|
10
|
+
sign_up = described_class.new(user_name: 'foo',
|
11
|
+
email: 'foo@example.com',
|
12
|
+
password: p,
|
13
|
+
password_confirmation: p)
|
14
|
+
sign_up.sign_up_with(User::Identities::Email)
|
15
|
+
end
|
16
|
+
|
17
|
+
it {is_expected.to be_valid}
|
18
|
+
|
19
|
+
context 'succeeds' do
|
20
|
+
before {sign_up.perform!}
|
21
|
+
|
22
|
+
it {expect { User::Account.find(sign_up.account.id) }.to_not raise_error}
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
context 'with bad details' do
|
27
|
+
subject :new_sign_up do
|
28
|
+
sign_up = described_class.new(user_name: a_user.name,
|
29
|
+
email: a_user.email.address,
|
30
|
+
password: p,
|
31
|
+
password_confirmation: p)
|
32
|
+
sign_up.sign_up_with(User::Identities::Email)
|
33
|
+
end
|
34
|
+
|
35
|
+
before {new_sign_up.valid?}
|
36
|
+
|
37
|
+
it {is_expected.not_to be_valid}
|
38
|
+
it {expect(new_sign_up.errors).to include(:email)}
|
39
|
+
it {expect(new_sign_up.errors).not_to include(:email_identity)}
|
40
|
+
end
|
41
|
+
|
42
|
+
context 'with social media' do
|
43
|
+
it 'can be invited and register with twitter'
|
44
|
+
|
45
|
+
context 'can be created logging in with facebook' do
|
46
|
+
# User fills up the signup form with his user name and clicks to register with facebook
|
47
|
+
subject :sign_up do
|
48
|
+
sign_up = described_class.new(user_name: Faker::Internet.user_name,
|
49
|
+
oauth_data: facebook_oauth_data)
|
50
|
+
sign_up.sign_up_with(User::Identities::OAuth)
|
51
|
+
end
|
52
|
+
|
53
|
+
it {is_expected.to be_valid}
|
54
|
+
it {expect(sign_up.identity).to be_a(User::Identities::Facebook)}
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'shared_contexts/user'
|
3
|
+
|
4
|
+
describe User::SignUpWithInvite do
|
5
|
+
include_context 'user'
|
6
|
+
|
7
|
+
context 'without an invite' do
|
8
|
+
subject :sign_up_without_invite do
|
9
|
+
sign_up = described_class.new(user_name: invite_recipient_user_name,
|
10
|
+
email: Faker::Internet.safe_email,
|
11
|
+
password: new_password,
|
12
|
+
password_confirmation: new_password)
|
13
|
+
end
|
14
|
+
|
15
|
+
before {sign_up_without_invite.valid?}
|
16
|
+
|
17
|
+
it {is_expected.not_to be_valid}
|
18
|
+
it {expect(sign_up_without_invite.errors).to include(:invite)}
|
19
|
+
end
|
20
|
+
|
21
|
+
context 'with a spent invite' do
|
22
|
+
|
23
|
+
let :spent_invite do
|
24
|
+
a_user.create_invite! recipient: invite_recipient
|
25
|
+
end
|
26
|
+
|
27
|
+
subject :sign_up_with_spent_invite do
|
28
|
+
user_name = Faker::Internet.user_name
|
29
|
+
sign_up = described_class.new(user_name: user_name,
|
30
|
+
code: spent_invite.code,
|
31
|
+
email: "#{user_name}@example.com",
|
32
|
+
password: new_password,
|
33
|
+
password_confirmation: new_password)
|
34
|
+
sign_up.sign_up_with(User::Identities::Email)
|
35
|
+
end
|
36
|
+
|
37
|
+
before {sign_up_with_spent_invite.valid?}
|
38
|
+
|
39
|
+
it {is_expected.not_to be_valid}
|
40
|
+
it {expect(sign_up_with_spent_invite.errors).to include(:invite)}
|
41
|
+
end
|
42
|
+
|
43
|
+
context 'with an invite' do
|
44
|
+
subject :sign_up_with_invite do
|
45
|
+
sign_up = described_class.new(user_name: invite_recipient,
|
46
|
+
code: a_sign_up_invite.code,
|
47
|
+
email: a_sign_up_invite.recipient,
|
48
|
+
password: new_password,
|
49
|
+
password_confirmation: new_password)
|
50
|
+
sign_up.sign_up_with(User::Identities::Email)
|
51
|
+
end
|
52
|
+
|
53
|
+
it {expect { sign_up_with_invite.perform! }.to_not raise_error}
|
54
|
+
|
55
|
+
end
|
56
|
+
|
57
|
+
context 'when redeemed' do
|
58
|
+
subject :sign_up_with_invite do
|
59
|
+
sign_up = described_class.new(user_name: invite_recipient,
|
60
|
+
code: a_sign_up_invite.code,
|
61
|
+
email: a_sign_up_invite.recipient,
|
62
|
+
password: new_password,
|
63
|
+
password_confirmation: new_password)
|
64
|
+
sign_up.sign_up_with(User::Identities::Email)
|
65
|
+
end
|
66
|
+
|
67
|
+
before {sign_up_with_invite.perform!}
|
68
|
+
|
69
|
+
it { expect(User::Account.where(id: sign_up_with_invite.account.id)).to exist }
|
70
|
+
end
|
71
|
+
|
72
|
+
it 'can be created logging in with facebook' do
|
73
|
+
# User fills up the signup form with his user name and clicks to register with facebook
|
74
|
+
sign_up = described_class.new(code: a_sign_up_invite.code,
|
75
|
+
oauth_data: facebook_oauth_data)
|
76
|
+
sign_up.sign_up_with(User::Identities::OAuth)
|
77
|
+
|
78
|
+
expect(sign_up).to be_valid
|
79
|
+
expect(sign_up.identity).to be_a(User::Identities::Facebook)
|
80
|
+
sign_up.perform
|
81
|
+
end
|
82
|
+
|
83
|
+
end
|