quo_vadis 2.0.0 → 2.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 (97) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/CHANGELOG.md +7 -0
  4. data/Gemfile +0 -3
  5. data/README.md +4 -5
  6. data/lib/quo_vadis/version.rb +1 -1
  7. data/quo_vadis.gemspec +5 -3
  8. data/test/dummy/README.markdown +1 -0
  9. data/test/dummy/Rakefile +3 -0
  10. data/test/dummy/app/controllers/application_controller.rb +2 -0
  11. data/test/dummy/app/controllers/articles_controller.rb +17 -0
  12. data/test/dummy/app/controllers/sign_ups_controller.rb +42 -0
  13. data/test/dummy/app/controllers/users_controller.rb +25 -0
  14. data/test/dummy/app/models/application_record.rb +3 -0
  15. data/test/dummy/app/models/article.rb +3 -0
  16. data/test/dummy/app/models/person.rb +6 -0
  17. data/test/dummy/app/models/user.rb +6 -0
  18. data/test/dummy/app/views/articles/also_secret.html.erb +1 -0
  19. data/test/dummy/app/views/articles/index.html.erb +1 -0
  20. data/test/dummy/app/views/articles/secret.html.erb +1 -0
  21. data/test/dummy/app/views/articles/very_secret.html.erb +2 -0
  22. data/test/dummy/app/views/layouts/application.html.erb +46 -0
  23. data/test/dummy/app/views/quo_vadis/confirmations/edit.html.erb +10 -0
  24. data/test/dummy/app/views/quo_vadis/confirmations/index.html.erb +5 -0
  25. data/test/dummy/app/views/quo_vadis/confirmations/new.html.erb +16 -0
  26. data/test/dummy/app/views/quo_vadis/logs/index.html.erb +28 -0
  27. data/test/dummy/app/views/quo_vadis/mailer/account_confirmation.text.erb +4 -0
  28. data/test/dummy/app/views/quo_vadis/mailer/email_change_notification.text.erb +8 -0
  29. data/test/dummy/app/views/quo_vadis/mailer/identifier_change_notification.text.erb +8 -0
  30. data/test/dummy/app/views/quo_vadis/mailer/password_change_notification.text.erb +8 -0
  31. data/test/dummy/app/views/quo_vadis/mailer/password_reset_notification.text.erb +8 -0
  32. data/test/dummy/app/views/quo_vadis/mailer/recovery_codes_generation_notification.text.erb +8 -0
  33. data/test/dummy/app/views/quo_vadis/mailer/reset_password.text.erb +4 -0
  34. data/test/dummy/app/views/quo_vadis/mailer/totp_reuse_notification.text.erb +6 -0
  35. data/test/dummy/app/views/quo_vadis/mailer/totp_setup_notification.text.erb +8 -0
  36. data/test/dummy/app/views/quo_vadis/mailer/twofa_deactivated_notification.text.erb +8 -0
  37. data/test/dummy/app/views/quo_vadis/password_resets/edit.html.erb +25 -0
  38. data/test/dummy/app/views/quo_vadis/password_resets/index.html.erb +5 -0
  39. data/test/dummy/app/views/quo_vadis/password_resets/new.html.erb +12 -0
  40. data/test/dummy/app/views/quo_vadis/passwords/edit.html.erb +30 -0
  41. data/test/dummy/app/views/quo_vadis/recovery_codes/challenge.html.erb +11 -0
  42. data/test/dummy/app/views/quo_vadis/recovery_codes/index.html.erb +25 -0
  43. data/test/dummy/app/views/quo_vadis/sessions/index.html.erb +26 -0
  44. data/test/dummy/app/views/quo_vadis/sessions/new.html.erb +24 -0
  45. data/test/dummy/app/views/quo_vadis/totps/challenge.html.erb +11 -0
  46. data/test/dummy/app/views/quo_vadis/totps/new.html.erb +17 -0
  47. data/test/dummy/app/views/quo_vadis/twofas/show.html.erb +20 -0
  48. data/test/dummy/app/views/sign_ups/new.html.erb +37 -0
  49. data/test/dummy/app/views/sign_ups/show.html.erb +5 -0
  50. data/test/dummy/app/views/users/new.html.erb +37 -0
  51. data/test/dummy/config.ru +7 -0
  52. data/test/dummy/config/application.rb +30 -0
  53. data/test/dummy/config/boot.rb +4 -0
  54. data/test/dummy/config/database.yml +10 -0
  55. data/test/dummy/config/environment.rb +4 -0
  56. data/test/dummy/config/initializers/quo_vadis.rb +7 -0
  57. data/test/dummy/config/routes.rb +13 -0
  58. data/test/dummy/db/migrate/202102121932_create_users.rb +10 -0
  59. data/test/dummy/db/migrate/202102121935_create_people.rb +10 -0
  60. data/test/dummy/db/schema.rb +92 -0
  61. data/test/dummy/public/favicon.ico +0 -0
  62. data/test/fixtures/quo_vadis/mailer/account_confirmation.text +4 -0
  63. data/test/fixtures/quo_vadis/mailer/email_change_notification.text +8 -0
  64. data/test/fixtures/quo_vadis/mailer/identifier_change_notification.text +8 -0
  65. data/test/fixtures/quo_vadis/mailer/password_change_notification.text +8 -0
  66. data/test/fixtures/quo_vadis/mailer/password_reset_notification.text +8 -0
  67. data/test/fixtures/quo_vadis/mailer/recovery_codes_generation_notification.text +8 -0
  68. data/test/fixtures/quo_vadis/mailer/reset_password.text +4 -0
  69. data/test/fixtures/quo_vadis/mailer/totp_reuse_notification.text +6 -0
  70. data/test/fixtures/quo_vadis/mailer/totp_setup_notification.text +8 -0
  71. data/test/fixtures/quo_vadis/mailer/twofa_deactivated_notification.text +8 -0
  72. data/test/integration/account_confirmation_test.rb +112 -0
  73. data/test/integration/controller_test.rb +280 -0
  74. data/test/integration/logging_test.rb +235 -0
  75. data/test/integration/password_change_test.rb +93 -0
  76. data/test/integration/password_login_test.rb +125 -0
  77. data/test/integration/password_reset_test.rb +136 -0
  78. data/test/integration/recovery_codes_test.rb +48 -0
  79. data/test/integration/sessions_test.rb +86 -0
  80. data/test/integration/sign_up_test.rb +35 -0
  81. data/test/integration/totps_test.rb +96 -0
  82. data/test/integration/twofa_test.rb +82 -0
  83. data/test/mailers/mailer_test.rb +200 -0
  84. data/test/models/account_test.rb +34 -0
  85. data/test/models/crypt_test.rb +22 -0
  86. data/test/models/log_test.rb +16 -0
  87. data/test/models/mask_ip_test.rb +27 -0
  88. data/test/models/model_test.rb +66 -0
  89. data/test/models/password_test.rb +163 -0
  90. data/test/models/recovery_code_test.rb +54 -0
  91. data/test/models/session_test.rb +67 -0
  92. data/test/models/token_test.rb +70 -0
  93. data/test/models/totp_test.rb +68 -0
  94. data/test/quo_vadis_test.rb +43 -0
  95. data/test/test_helper.rb +58 -0
  96. metadata +119 -4
  97. data/Gemfile.lock +0 -178
@@ -0,0 +1,200 @@
1
+ require 'test_helper'
2
+
3
+ class MailerTest < ActionMailer::TestCase
4
+
5
+ tests QuoVadis::Mailer
6
+
7
+ setup do
8
+ QuoVadis.mail_headers({from: 'Bar <bar@example.com>'})
9
+ end
10
+
11
+
12
+ test 'reset_password' do
13
+ email = QuoVadis::Mailer.with(
14
+ email: 'Foo <foo@example.com>',
15
+ url: 'http://example.com/pwd-reset/123abc'
16
+ ).reset_password
17
+
18
+ assert_emails 1 do
19
+ email.deliver_now
20
+ end
21
+
22
+ assert_equal ['foo@example.com'], email.to
23
+ assert_equal ['bar@example.com'], email.from
24
+ assert_equal 'Change your password', email.subject
25
+ assert_equal read_fixture('reset_password.text').join, email.body.to_s
26
+ end
27
+
28
+
29
+ test 'account_confirmation' do
30
+ email = QuoVadis::Mailer.with(
31
+ email: 'Foo <foo@example.com>',
32
+ url: 'http://example.com/confirm/123abc'
33
+ ).account_confirmation
34
+
35
+ assert_emails 1 do
36
+ email.deliver_now
37
+ end
38
+
39
+ assert_equal ['foo@example.com'], email.to
40
+ assert_equal ['bar@example.com'], email.from
41
+ assert_equal 'Please confirm your account', email.subject
42
+ assert_equal read_fixture('account_confirmation.text').join, email.body.to_s
43
+ end
44
+
45
+
46
+ test 'email change notification' do
47
+ email = QuoVadis::Mailer.with(email: 'Foo <foo@example.com>').email_change_notification
48
+
49
+ # freeze_time
50
+
51
+ assert_emails 1 do
52
+ QuoVadis::CurrentRequestDetails.set(ip: '1.2.3.4') do
53
+ email.deliver_now
54
+ end
55
+ end
56
+
57
+ assert_equal ['foo@example.com'], email.to
58
+ assert_equal ['bar@example.com'], email.from
59
+ assert_equal 'Your email address has been changed', email.subject
60
+ assert_equal with_timestamp(read_fixture('email_change_notification.text').join), email.body.to_s
61
+ end
62
+
63
+
64
+ test 'identifier change notification' do
65
+ email = QuoVadis::Mailer.with(email: 'Foo <foo@example.com>', identifier: 'email').identifier_change_notification
66
+
67
+ # freeze_time
68
+
69
+ assert_emails 1 do
70
+ QuoVadis::CurrentRequestDetails.set(ip: '1.2.3.4') do
71
+ email.deliver_now
72
+ end
73
+ end
74
+
75
+ assert_equal ['foo@example.com'], email.to
76
+ assert_equal ['bar@example.com'], email.from
77
+ assert_equal 'Your email has been changed', email.subject
78
+ assert_equal with_timestamp(read_fixture('identifier_change_notification.text').join), email.body.to_s
79
+ end
80
+
81
+
82
+ test 'password change notification' do
83
+ email = QuoVadis::Mailer.with(email: 'Foo <foo@example.com>').password_change_notification
84
+
85
+ # freeze_time
86
+
87
+ assert_emails 1 do
88
+ QuoVadis::CurrentRequestDetails.set(ip: '1.2.3.4') do
89
+ email.deliver_now
90
+ end
91
+ end
92
+
93
+ assert_equal ['foo@example.com'], email.to
94
+ assert_equal ['bar@example.com'], email.from
95
+ assert_equal 'Your password has been changed', email.subject
96
+ assert_equal with_timestamp(read_fixture('password_change_notification.text').join), email.body.to_s
97
+ end
98
+
99
+
100
+ test 'password reset notification' do
101
+ email = QuoVadis::Mailer.with(email: 'Foo <foo@example.com>').password_reset_notification
102
+
103
+ # freeze_time
104
+
105
+ assert_emails 1 do
106
+ QuoVadis::CurrentRequestDetails.set(ip: '1.2.3.4') do
107
+ email.deliver_now
108
+ end
109
+ end
110
+
111
+ assert_equal ['foo@example.com'], email.to
112
+ assert_equal ['bar@example.com'], email.from
113
+ assert_equal 'Your password has been reset', email.subject
114
+ assert_equal with_timestamp(read_fixture('password_reset_notification.text').join), email.body.to_s
115
+ end
116
+
117
+
118
+ test 'totp setup notification' do
119
+ email = QuoVadis::Mailer.with(email: 'Foo <foo@example.com>').totp_setup_notification
120
+
121
+ # freeze_time
122
+
123
+ assert_emails 1 do
124
+ QuoVadis::CurrentRequestDetails.set(ip: '1.2.3.4') do
125
+ email.deliver_now
126
+ end
127
+ end
128
+
129
+ assert_equal ['foo@example.com'], email.to
130
+ assert_equal ['bar@example.com'], email.from
131
+ assert_equal 'Two-factor authentication was set up just now', email.subject
132
+ assert_equal with_timestamp(read_fixture('totp_setup_notification.text').join), email.body.to_s
133
+ end
134
+
135
+
136
+ test 'totp reuse notification' do
137
+ email = QuoVadis::Mailer.with(email: 'Foo <foo@example.com>').totp_reuse_notification
138
+
139
+ # freeze_time
140
+
141
+ assert_emails 1 do
142
+ QuoVadis::CurrentRequestDetails.set(ip: '1.2.3.4') do
143
+ email.deliver_now
144
+ end
145
+ end
146
+
147
+ assert_equal ['foo@example.com'], email.to
148
+ assert_equal ['bar@example.com'], email.from
149
+ assert_equal 'Your two-factor authentication code was reused just now', email.subject
150
+ assert_equal with_timestamp(read_fixture('totp_reuse_notification.text').join), email.body.to_s
151
+ end
152
+
153
+
154
+ test '2fa deactivated notification' do
155
+ email = QuoVadis::Mailer.with(email: 'Foo <foo@example.com>').twofa_deactivated_notification
156
+
157
+ # freeze_time
158
+
159
+ assert_emails 1 do
160
+ QuoVadis::CurrentRequestDetails.set(ip: '1.2.3.4') do
161
+ email.deliver_now
162
+ end
163
+ end
164
+
165
+ assert_equal ['foo@example.com'], email.to
166
+ assert_equal ['bar@example.com'], email.from
167
+ assert_equal 'Two-factor authentication was deactivated just now', email.subject
168
+ assert_equal with_timestamp(read_fixture('twofa_deactivated_notification.text').join), email.body.to_s
169
+ end
170
+
171
+
172
+ test 'recovery codes generation notification' do
173
+ email = QuoVadis::Mailer.with(email: 'Foo <foo@example.com>').recovery_codes_generation_notification
174
+
175
+ # freeze_time
176
+
177
+ assert_emails 1 do
178
+ QuoVadis::CurrentRequestDetails.set(ip: '1.2.3.4') do
179
+ email.deliver_now
180
+ end
181
+ end
182
+
183
+ assert_equal ['foo@example.com'], email.to
184
+ assert_equal ['bar@example.com'], email.from
185
+ assert_equal 'Recovery codes have been generated for your account', email.subject
186
+ assert_equal with_timestamp(read_fixture('recovery_codes_generation_notification.text').join), email.body.to_s
187
+ end
188
+
189
+
190
+ private
191
+
192
+ def read_fixture(action)
193
+ IO.readlines(File.join(__dir__, '..', 'fixtures', self.class.mailer_class.name.underscore, action))
194
+ end
195
+
196
+ def with_timestamp(str)
197
+ str.sub 'TIMESTAMP', Time.now.strftime('%e %B at %H:%M (%Z)')
198
+ end
199
+
200
+ end
@@ -0,0 +1,34 @@
1
+ require 'test_helper'
2
+
3
+ class AccountTest < ActiveSupport::TestCase
4
+ include ActionMailer::TestHelper
5
+
6
+ test 'confirmed?' do
7
+ account = QuoVadis::Account.new
8
+ refute account.confirmed?
9
+
10
+ account.confirmed_at = Time.now
11
+ assert account.confirmed?
12
+ end
13
+
14
+
15
+ test 'notifies on identifier change when notifier is not email' do
16
+ p = Person.create! username: 'bob', email: 'bob@example.com', password: 'secretsecret'
17
+ assert_enqueued_email_with QuoVadis::Mailer, :identifier_change_notification, args: {email: 'bob@example.com', identifier: 'username'} do
18
+ assert_enqueued_emails 1 do
19
+ p.update username: 'robert@example.com'
20
+ end
21
+ end
22
+ end
23
+
24
+
25
+ test 'does not notify on identifier change when notifier is email' do
26
+ u = User.create! name: 'bob', email: 'bob@example.com', password: '123456789abc'
27
+ assert_enqueued_email_with QuoVadis::Mailer, :email_change_notification, args: {email: 'bob@example.com'} do
28
+ assert_enqueued_emails 1 do
29
+ u.update email: 'robert@example.com'
30
+ end
31
+ end
32
+ end
33
+
34
+ end
@@ -0,0 +1,22 @@
1
+ require 'test_helper'
2
+
3
+ class CryptTest < ActiveSupport::TestCase
4
+
5
+ setup do
6
+ @crypt = QuoVadis::Crypt
7
+ end
8
+
9
+ test 'round trip' do
10
+ plaintext = 'the quick brown fox'
11
+ ciphertext = @crypt.encrypt plaintext
12
+ refute_equal plaintext, ciphertext
13
+ assert_equal plaintext, @crypt.decrypt(ciphertext)
14
+ end
15
+
16
+ test 'same plaintext encrypts to different ciphertexts' do
17
+ plaintext = 'the quick brown fox'
18
+ ciphertext = @crypt.encrypt plaintext
19
+ refute_equal ciphertext, @crypt.encrypt(plaintext)
20
+ end
21
+
22
+ end
@@ -0,0 +1,16 @@
1
+ require 'test_helper'
2
+
3
+ class LogTest < ActiveSupport::TestCase
4
+
5
+ test 'smoke' do
6
+ user = User.create! name: 'bob', email: 'bob@example.com', password: '123456789abc'
7
+ account = user.qv_account
8
+
9
+ log = account.logs.create! action: QuoVadis::Log::LOGIN_SUCCESS, ip: '1.2.3.4', metadata: {foo: 'bar'}
10
+
11
+ assert_equal QuoVadis::Log::LOGIN_SUCCESS, log.action
12
+ assert_equal '1.2.3.4', log.ip
13
+ assert_equal 'bar', log.metadata['foo']
14
+ end
15
+
16
+ end
@@ -0,0 +1,27 @@
1
+ require 'test_helper'
2
+
3
+ class MaskIpTest < ActiveSupport::TestCase
4
+
5
+ setup do
6
+ @masking = QuoVadis.mask_ips
7
+ end
8
+
9
+ teardown do
10
+ QuoVadis.mask_ips @masking
11
+ end
12
+
13
+
14
+ test 'mask ips' do
15
+ [ QuoVadis::Log, QuoVadis::Session ].each do |klass|
16
+ QuoVadis.mask_ips false
17
+ instance = klass.new(ip: '1.2.3.4')
18
+ instance.valid?
19
+ assert_equal '1.2.3.4', instance.ip
20
+
21
+ QuoVadis.mask_ips true
22
+ instance.valid?
23
+ assert_equal '1.2.3.0', instance.ip
24
+ end
25
+ end
26
+
27
+ end
@@ -0,0 +1,66 @@
1
+ require 'test_helper'
2
+
3
+ class ModelTest < ActiveSupport::TestCase
4
+ include ActionMailer::TestHelper
5
+
6
+ test 'responds to' do
7
+ assert_respond_to User, :authenticates
8
+ u = User.new
9
+ assert_respond_to u, :password
10
+ assert_respond_to u, :password=
11
+ assert_respond_to u, :password_confirmation
12
+ assert_respond_to u, :password_confirmation=
13
+ end
14
+
15
+
16
+ test 'creates account' do
17
+ u = User.create! name: 'bob', email: 'bob@example.com', password: '123456789abc'
18
+ assert u.persisted?
19
+ assert u.qv_account.persisted?
20
+ end
21
+
22
+
23
+ test 'destroys account' do
24
+ u = User.create! name: 'bob', email: 'bob@example.com', password: '123456789abc'
25
+ ac = u.qv_account
26
+ u.destroy
27
+ assert ac.destroyed?
28
+ end
29
+
30
+
31
+ test 'copies model identifier to account' do
32
+ email = 'bob@example.com'
33
+ u = User.create! name: 'bob', email: email, password: '123456789abc'
34
+ assert_equal email, u.qv_account.identifier
35
+
36
+ email = 'b@foo.com'
37
+ u.update email: email
38
+ u.qv_account.reload
39
+ assert_equal email, u.qv_account.identifier
40
+
41
+ u.update name: nil, email: 'xyz' # nil name is invalid
42
+ u.qv_account.reload
43
+ refute_equal 'xyz', u.qv_account.identifier
44
+ end
45
+
46
+
47
+ test 'ensures uniqueness validation on identifier' do
48
+ Foo = Class.new ActiveRecord::Base
49
+ assert_raises NotImplementedError, 'missing uniqueness validation on ModelTest::Foo#email. Try adding: `validates :email, uniqueness: true`' do
50
+ Foo.instance_eval 'authenticates'
51
+ end
52
+
53
+ Bar = Class.new ActiveRecord::Base
54
+ Bar.instance_eval 'validates :email, uniqueness: true'
55
+ Bar.instance_eval 'authenticates'
56
+ # no error raised
57
+ end
58
+
59
+
60
+ test 'notifies on email change' do
61
+ u = User.create! name: 'bob', email: 'bob@example.com', password: '123456789abc'
62
+ assert_enqueued_email_with QuoVadis::Mailer, :email_change_notification, args: {email: 'bob@example.com'} do
63
+ u.update email: 'robert@example.com'
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,163 @@
1
+ require 'test_helper'
2
+
3
+ class PasswordTest < ActiveSupport::TestCase
4
+
5
+ VALID_PASSWORD = '123456789abc'
6
+ SIXTY_FOUR_CHARS = '1234567890123456789012345678901234567890123456789012345678901234'
7
+
8
+ test 'validations' do
9
+ pw = QuoVadis::Password.new
10
+ pw.valid?
11
+ refute_empty pw.errors[:password]
12
+
13
+ pw.password = nil
14
+ pw.valid?
15
+ refute_empty pw.errors[:password]
16
+
17
+ pw.password = ''
18
+ pw.valid?
19
+ refute_empty pw.errors[:password]
20
+
21
+ pw.password = 'x'
22
+ pw.valid?
23
+ refute_empty pw.errors[:password]
24
+
25
+ pw.password = VALID_PASSWORD
26
+ pw.valid?
27
+ assert_empty pw.errors[:password]
28
+ end
29
+
30
+
31
+ test 'confirmation' do
32
+ pw = QuoVadis::Password.new password: VALID_PASSWORD, password_confirmation: nil
33
+ pw.valid?
34
+ assert_empty pw.errors[:password_confirmation]
35
+
36
+ pw = QuoVadis::Password.new password: VALID_PASSWORD, password_confirmation: ''
37
+ pw.valid?
38
+ refute_empty pw.errors[:password_confirmation]
39
+
40
+ pw.password_confirmation = VALID_PASSWORD
41
+ pw.valid?
42
+ assert_empty pw.errors[:password_confirmation]
43
+ assert_empty pw.errors[:password]
44
+ end
45
+
46
+
47
+ test 'model passes through password to quo_vadis' do
48
+ user = User.new name: 'bob', email: 'bob@example.com'
49
+ refute user.valid?
50
+ refute_empty user.errors[:password]
51
+
52
+ user.password = 'x'
53
+ refute user.valid?
54
+ refute_empty user.errors[:password]
55
+
56
+ user.password = VALID_PASSWORD
57
+ assert user.save
58
+
59
+ _user = User.last
60
+ assert _user.valid?
61
+
62
+ _pw = _user.qv_account.password
63
+ assert _pw.valid?
64
+ end
65
+
66
+
67
+ test 'model passes through password confirmation to quo_vadis' do
68
+ user = User.new name: 'bob', email: 'bob@example.com', password: VALID_PASSWORD, password_confirmation: 'x'
69
+ refute user.valid?
70
+ refute_empty user.errors[:password_confirmation]
71
+
72
+ user.password_confirmation = VALID_PASSWORD
73
+ assert user.valid?
74
+ end
75
+
76
+
77
+ test 'change' do
78
+ user = User.create! name: 'bob', email: 'bob@example.com', password: VALID_PASSWORD
79
+ pw = user.qv_account.password
80
+
81
+ refute pw.change('wrong', '', '')
82
+ assert_equal ['is incorrect'], pw.errors[:password]
83
+
84
+ pw = QuoVadis::Password.find pw.id
85
+ refute pw.change(VALID_PASSWORD, '', '')
86
+ assert_equal ["can't be blank"], pw.errors[:new_password]
87
+
88
+ pw = QuoVadis::Password.find pw.id
89
+ refute pw.change(VALID_PASSWORD, 'x', 'x')
90
+ assert_equal ["is too short (minimum is #{QuoVadis.password_minimum_length} characters)"], pw.errors[:new_password]
91
+
92
+ pw = QuoVadis::Password.find pw.id
93
+ refute pw.change(VALID_PASSWORD, 'xxxxxxxxxxxx', 'yyyyyyyyyyyy')
94
+ assert_equal ["doesn't match Password"], pw.errors[:new_password_confirmation]
95
+
96
+ pw = QuoVadis::Password.find pw.id
97
+ assert pw.change(VALID_PASSWORD, 'xxxxxxxxxxxx', 'xxxxxxxxxxxx')
98
+ assert pw.authenticate 'xxxxxxxxxxxx'
99
+ end
100
+
101
+
102
+ test 'reset' do
103
+ user = User.create! name: 'bob', email: 'bob@example.com', password: VALID_PASSWORD
104
+ pw = user.qv_account.password
105
+
106
+ refute pw.reset('', '')
107
+ assert_equal ["can't be blank"], pw.errors[:password]
108
+
109
+ refute pw.reset('x', 'x')
110
+ assert_equal ["is too short (minimum is #{QuoVadis.password_minimum_length} characters)"], pw.errors[:password]
111
+
112
+ refute pw.reset('xxxxxxxxxxxx', 'yyyyyyyyyyyy')
113
+ assert_equal ["doesn't match Password"], pw.errors[:password_confirmation]
114
+
115
+ assert pw.reset('xxxxxxxxxxxx', 'xxxxxxxxxxxx')
116
+ assert pw.authenticate 'xxxxxxxxxxxx'
117
+ end
118
+
119
+
120
+ test 'cascade destroy' do
121
+ user = User.create! name: 'bob', email: 'bob@example.com', password: VALID_PASSWORD
122
+ assert user.qv_account.persisted?
123
+ assert user.qv_account.password.persisted?
124
+
125
+ user.destroy
126
+ assert user.qv_account.destroyed?
127
+ assert user.qv_account.password.destroyed?
128
+ end
129
+
130
+
131
+ test 'cannot override existing password' do
132
+ user = User.create! name: 'bob', email: 'bob@example.com', password: VALID_PASSWORD
133
+
134
+ assert_raises QuoVadis::PasswordExistsError do
135
+ user.password = 'cba987654321'
136
+ end
137
+ end
138
+
139
+
140
+ test 'passwords may be 64 characters or longer' do
141
+ pw = QuoVadis::Password.new password: SIXTY_FOUR_CHARS
142
+ pw.valid?
143
+ assert_empty pw.errors[:password]
144
+
145
+ pw.password = "#{SIXTY_FOUR_CHARS}abc"
146
+ pw.valid?
147
+ assert_empty pw.errors[:password]
148
+ end
149
+
150
+ test 'passwords may contain spaces, no truncation' do
151
+ pw = QuoVadis::Password.new password: ' '
152
+ pw.valid?
153
+ assert_empty pw.errors[:password]
154
+ end
155
+
156
+ test 'passwords may contain unicode characters' do
157
+ pw = QuoVadis::Password.new password: '★ ★ ★ ★ ★ ★ '
158
+ pw.valid?
159
+ assert_empty pw.errors[:password]
160
+ end
161
+
162
+
163
+ end