propel_authentication 0.1.4 → 0.2.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 (47) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +51 -2
  3. data/README.md +6 -6
  4. data/lib/generators/propel_authentication/install_generator.rb +135 -153
  5. data/lib/generators/propel_authentication/templates/application_mailer.rb +6 -0
  6. data/lib/generators/propel_authentication/templates/auth/passwords_controller.rb.tt +84 -78
  7. data/lib/generators/propel_authentication/templates/auth/signup_controller.rb.tt +242 -0
  8. data/lib/generators/propel_authentication/templates/{tokens_controller.rb.tt → auth/tokens_controller.rb.tt} +39 -22
  9. data/lib/generators/propel_authentication/templates/auth_mailer.rb +3 -1
  10. data/lib/generators/propel_authentication/templates/authenticatable.rb +8 -2
  11. data/lib/generators/propel_authentication/templates/concerns/confirmable.rb +1 -1
  12. data/lib/generators/propel_authentication/templates/concerns/lockable.rb +4 -2
  13. data/lib/generators/propel_authentication/templates/concerns/{propel_authentication.rb → propel_authentication_concern.rb} +33 -3
  14. data/lib/generators/propel_authentication/templates/concerns/recoverable.rb +16 -6
  15. data/lib/generators/propel_authentication/templates/core/configuration_methods.rb +104 -64
  16. data/lib/generators/propel_authentication/templates/db/seeds.rb +50 -4
  17. data/lib/generators/propel_authentication/templates/doc/signup_flow.md +315 -0
  18. data/lib/generators/propel_authentication/templates/models/agency.rb.tt +13 -0
  19. data/lib/generators/propel_authentication/templates/models/agent.rb.tt +13 -0
  20. data/lib/generators/propel_authentication/templates/{invitation.rb → models/invitation.rb.tt} +6 -0
  21. data/lib/generators/propel_authentication/templates/models/organization.rb.tt +12 -0
  22. data/lib/generators/propel_authentication/templates/{user.rb → models/user.rb.tt} +5 -0
  23. data/lib/generators/propel_authentication/templates/propel_authentication.rb.tt +94 -9
  24. data/lib/generators/propel_authentication/templates/routes/auth_routes.rb.tt +55 -0
  25. data/lib/generators/propel_authentication/templates/services/auth_notification_service.rb +3 -3
  26. data/lib/generators/propel_authentication/templates/test/concerns/confirmable_test.rb.tt +34 -10
  27. data/lib/generators/propel_authentication/templates/test/concerns/propel_authentication_test.rb.tt +1 -1
  28. data/lib/generators/propel_authentication/templates/test/concerns/recoverable_test.rb.tt +4 -4
  29. data/lib/generators/propel_authentication/templates/test/controllers/auth/lockable_integration_test.rb.tt +18 -15
  30. data/lib/generators/propel_authentication/templates/test/controllers/auth/password_reset_integration_test.rb.tt +38 -40
  31. data/lib/generators/propel_authentication/templates/test/controllers/auth/signup_controller_test.rb.tt +201 -0
  32. data/lib/generators/propel_authentication/templates/test/controllers/auth/tokens_controller_test.rb.tt +33 -25
  33. data/lib/generators/propel_authentication/templates/test/mailers/auth_mailer_test.rb.tt +51 -36
  34. data/lib/generators/propel_authentication/templates/views/auth_mailer/email_confirmation.html.erb +2 -2
  35. data/lib/generators/propel_authentication/templates/views/auth_mailer/email_confirmation.text.erb +1 -1
  36. data/lib/generators/propel_authentication/test/generators/authentication/install_generator_test.rb +4 -4
  37. data/lib/generators/propel_authentication/test/generators/authentication/uninstall_generator_test.rb +1 -1
  38. data/lib/generators/propel_authentication/test/integration/generator_integration_test.rb +1 -1
  39. data/lib/generators/propel_authentication/test/integration/multi_version_generator_test.rb +13 -12
  40. data/lib/generators/propel_authentication/unpack_generator.rb +19 -15
  41. data/lib/propel_authentication.rb +1 -1
  42. metadata +14 -11
  43. data/lib/generators/propel_authentication/templates/agency.rb +0 -7
  44. data/lib/generators/propel_authentication/templates/agent.rb +0 -7
  45. data/lib/generators/propel_authentication/templates/auth/base_passwords_controller.rb.tt +0 -99
  46. data/lib/generators/propel_authentication/templates/auth/base_tokens_controller.rb.tt +0 -90
  47. data/lib/generators/propel_authentication/templates/organization.rb +0 -7
@@ -10,8 +10,8 @@ class ConfirmableTest < ActiveSupport::TestCase
10
10
 
11
11
  test "should generate confirmation token on user creation" do
12
12
  user = User.new(
13
- email_address: 'jane@example.com',
14
- username: 'jane_doe',
13
+ email_address: 'test_token@example.com',
14
+ username: 'test_token_user',
15
15
  password: 'securepassword123',
16
16
  password_confirmation: 'securepassword123',
17
17
  organization: @organization
@@ -23,7 +23,7 @@ class ConfirmableTest < ActiveSupport::TestCase
23
23
  user.save!
24
24
 
25
25
  assert_not_nil user.confirmation_token, "Confirmation token should be generated on save"
26
- assert_not_nil user.confirmation_sent_at, "Confirmation sent at should be set on save"
26
+ assert_not_nil user.confirmation_sent_at, "Confirmation sent at should be set after save due to after_create callback"
27
27
  assert user.confirmation_token.length >= 32, "Confirmation token should be at least 32 characters"
28
28
  end
29
29
 
@@ -114,8 +114,8 @@ class ConfirmableTest < ActiveSupport::TestCase
114
114
  test "should send confirmation email on user creation" do
115
115
  assert_emails 1 do
116
116
  User.create!(
117
- email_address: 'newuser@example.com',
118
- username: 'newuser',
117
+ email_address: 'test_email_creation@example.com',
118
+ username: 'test_email_creation_user',
119
119
  password: 'securepassword123',
120
120
  password_confirmation: 'securepassword123',
121
121
  organization: @organization
@@ -123,9 +123,21 @@ class ConfirmableTest < ActiveSupport::TestCase
123
123
  end
124
124
 
125
125
  email = ActionMailer::Base.deliveries.last
126
- assert_equal 'newuser@example.com', email.to.first, "Email should be sent to user's email address"
126
+ assert_equal 'test_email_creation@example.com', email.to.first, "Email should be sent to user's email address"
127
127
  assert_match(/confirm/i, email.subject, "Email subject should mention confirmation")
128
- assert_match(/confirm/i, email.body.to_s, "Email body should contain confirmation instructions")
128
+
129
+ # Test multipart email content properly
130
+ if email.multipart?
131
+ text_part = email.body.parts.find { |part| part.content_type.include?('text/plain') }
132
+ html_part = email.body.parts.find { |part| part.content_type.include?('text/html') }
133
+
134
+ assert_not_nil text_part, "Email should have text part"
135
+ assert_not_nil html_part, "Email should have HTML part"
136
+ assert_match(/confirm/i, text_part.body.to_s, "Text part should contain confirmation instructions")
137
+ assert_match(/confirm/i, html_part.body.to_s, "HTML part should contain confirmation instructions")
138
+ else
139
+ assert_match(/confirm/i, email.body.to_s, "Email body should contain confirmation instructions")
140
+ end
129
141
  end
130
142
 
131
143
  test "should send confirmation email when resending instructions" do
@@ -137,7 +149,18 @@ class ConfirmableTest < ActiveSupport::TestCase
137
149
 
138
150
  email = ActionMailer::Base.deliveries.last
139
151
  assert_equal @user.email_address, email.to.first, "Email should be sent to user's email address"
140
- assert_match(@user.confirmation_token, email.body.to_s, "Email should contain confirmation token")
152
+
153
+ # Test multipart email content properly for confirmation token
154
+ if email.multipart?
155
+ text_part = email.body.parts.find { |part| part.content_type.include?('text/plain') }
156
+ html_part = email.body.parts.find { |part| part.content_type.include?('text/html') }
157
+
158
+ assert_not_nil text_part, "Email should have text part"
159
+ assert_match(@user.confirmation_token, text_part.body.to_s, "Text part should contain confirmation token")
160
+ assert_match(@user.confirmation_token, html_part.body.to_s, "HTML part should contain confirmation token")
161
+ else
162
+ assert_match(@user.confirmation_token, email.body.to_s, "Email should contain confirmation token")
163
+ end
141
164
  end
142
165
 
143
166
  test "should not send confirmation email to already confirmed users" do
@@ -177,12 +200,13 @@ class ConfirmableTest < ActiveSupport::TestCase
177
200
  original_email = @user.email_address
178
201
 
179
202
  assert_emails 1 do
180
- @user.update!(email_address: 'newemail@example.com')
203
+ @user.update!(email_address: 'test_email_change@example.com')
181
204
  end
182
205
 
183
206
  @user.reload
184
207
  assert_not @user.confirmed?, "Should require reconfirmation after email change"
185
- assert_equal 'newemail@example.com', @user.email_address, "Email should be updated"
208
+ assert_equal original_email, @user.email_address, "Email should remain original until confirmed"
209
+ assert_equal 'test_email_change@example.com', @user.unconfirmed_email_address, "New email should be stored for confirmation"
186
210
  assert_not_nil @user.confirmation_token, "Should generate new confirmation token"
187
211
  end
188
212
 
@@ -2,7 +2,7 @@ require 'test_helper'
2
2
 
3
3
  # A dummy controller to test the concern
4
4
  class PropelAuthenticationTestController < ActionController::Base
5
- include PropelAuthentication
5
+ include PropelAuthenticationConcern
6
6
  before_action :authenticate_user
7
7
 
8
8
  def index
@@ -2,7 +2,7 @@ require 'test_helper'
2
2
 
3
3
  class RecoverableTest < ActiveSupport::TestCase
4
4
  def setup
5
- @user = users(:john_user)
5
+ @user = users(:confirmed_user)
6
6
  @organization = @user.organization
7
7
  end
8
8
 
@@ -234,7 +234,7 @@ class RecoverableTest < ActiveSupport::TestCase
234
234
  reset_token = @user.generate_password_reset_token
235
235
 
236
236
  # EXECUTE LOOKUP: Find user by reset token
237
- found_user = User.find_user_by_password_reset_token(reset_token)
237
+ found_user = User.find_by_jwt_password_reset_token(reset_token)
238
238
 
239
239
  # VERIFY LOOKUP: Should find correct user
240
240
  assert_equal @user.id, found_user.id, "Should find user by valid reset token"
@@ -247,7 +247,7 @@ class RecoverableTest < ActiveSupport::TestCase
247
247
  invalid_token = "invalid.jwt.token"
248
248
 
249
249
  # EXECUTE LOOKUP: Try to find user with invalid token
250
- found_user = User.find_user_by_password_reset_token(invalid_token)
250
+ found_user = User.find_by_jwt_password_reset_token(invalid_token)
251
251
 
252
252
  # VERIFY REJECTION: Should return nil
253
253
  assert_nil found_user, "Should return nil for invalid token"
@@ -267,7 +267,7 @@ class RecoverableTest < ActiveSupport::TestCase
267
267
  expired_token = JWT.encode(expired_payload, PropelAuthentication.configuration.jwt_secret, 'HS256')
268
268
 
269
269
  # EXECUTE LOOKUP: Try to find user with expired token
270
- found_user = User.find_user_by_password_reset_token(expired_token)
270
+ found_user = User.find_by_jwt_password_reset_token(expired_token)
271
271
 
272
272
  # VERIFY EXPIRATION: Should return nil
273
273
  assert_nil found_user, "Should return nil for expired token"
@@ -1,14 +1,17 @@
1
1
  require 'test_helper'
2
2
 
3
- class <%= controller_namespace %>::LockableIntegrationTest < ActionDispatch::IntegrationTest
3
+ class <%= controller_namespace('tokens') %>LockableIntegrationTest < ActionDispatch::IntegrationTest
4
4
  def setup
5
5
  @organization = Organization.create!(name: "Test Organization")
6
+ @agency = Agency.create!(name: "Test Agency", organization: @organization)
6
7
  @user = User.create!(
7
8
  email_address: "lockable-integration@example.com",
8
9
  username: "lockableintegration",
9
10
  password: "correctpassword123",
10
11
  organization: @organization
11
12
  )
13
+ # Create agent association for agency access (required for real-time agency lookup)
14
+ Agent.create!(user: @user, agency: @agency, role: "member")
12
15
  end
13
16
 
14
17
  # CRITICAL: Real API integration with lockable functionality
@@ -16,7 +19,7 @@ class <%= controller_namespace %>::LockableIntegrationTest < ActionDispatch::Int
16
19
  test "should track failed attempts during actual login API calls" do
17
20
  # BEHAVIOR: Verify failed login attempts are tracked through real API
18
21
  9.times do |i|
19
- post <%= login_path_helper %>, params: {
22
+ post '<%= auth_route_prefix %>/login', params: {
20
23
  user: { email_address: @user.email_address, password: "wrongpassword" }
21
24
  }
22
25
 
@@ -32,14 +35,14 @@ class <%= controller_namespace %>::LockableIntegrationTest < ActionDispatch::Int
32
35
 
33
36
  # First 9 attempts should not lock
34
37
  9.times do
35
- post <%= login_path_helper %>, params: {
38
+ post '<%= auth_route_prefix %>/login', params: {
36
39
  user: { email_address: @user.email_address, password: "wrongpassword" }
37
40
  }
38
41
  assert_response :unauthorized
39
42
  end
40
43
 
41
44
  # 10th attempt should lock the account
42
- post <%= login_path_helper %>, params: {
45
+ post '<%= auth_route_prefix %>/login', params: {
43
46
  user: { email_address: @user.email_address, password: "wrongpassword" }
44
47
  }
45
48
 
@@ -52,14 +55,14 @@ class <%= controller_namespace %>::LockableIntegrationTest < ActionDispatch::Int
52
55
  # BEHAVIOR: Verify locked accounts return proper HTTP status and error
53
56
  @user.lock_account!
54
57
 
55
- post <%= login_path_helper %>, params: {
58
+ post '<%= auth_route_prefix %>/login', params: {
56
59
  user: { email_address: @user.email_address, password: "correctpassword123" }
57
60
  }
58
61
 
59
62
  assert_response :locked, "Should return 423 locked status"
60
63
 
61
64
  json = JSON.parse(response.body)
62
- assert_match(/locked.*too many failed attempts/i, json['error'], "Should return descriptive error message")
65
+ assert_match(/locked.*too many.*failed.*attempts/i, json['error'], "Should return descriptive error message")
63
66
  assert_nil json['token'], "Should not return authentication token"
64
67
  assert_nil json['user'], "Should not return user data"
65
68
  end
@@ -69,7 +72,7 @@ class <%= controller_namespace %>::LockableIntegrationTest < ActionDispatch::Int
69
72
  @user.lock_account!
70
73
 
71
74
  # Try with correct password
72
- post <%= login_path_helper %>, params: {
75
+ post '<%= auth_route_prefix %>/login', params: {
73
76
  user: { email_address: @user.email_address, password: "correctpassword123" }
74
77
  }
75
78
 
@@ -85,7 +88,7 @@ class <%= controller_namespace %>::LockableIntegrationTest < ActionDispatch::Int
85
88
 
86
89
  # Build up some failed attempts
87
90
  5.times do
88
- post <%= login_path_helper %>, params: {
91
+ post '<%= auth_route_prefix %>/login', params: {
89
92
  user: { email_address: @user.email_address, password: "wrongpassword" }
90
93
  }
91
94
  end
@@ -94,7 +97,7 @@ class <%= controller_namespace %>::LockableIntegrationTest < ActionDispatch::Int
94
97
  assert_equal 5, @user.failed_login_attempts, "Should have 5 failed attempts"
95
98
 
96
99
  # Successful login should reset counter
97
- post <%= login_path_helper %>, params: {
100
+ post '<%= auth_route_prefix %>/login', params: {
98
101
  user: { email_address: @user.email_address, password: "correctpassword123" }
99
102
  }
100
103
 
@@ -111,7 +114,7 @@ class <%= controller_namespace %>::LockableIntegrationTest < ActionDispatch::Int
111
114
  @user.lock_account!
112
115
  unlock_token = @user.generate_unlock_token
113
116
 
114
- post <%= unlock_path_helper %>, params: { token: unlock_token }
117
+ post '<%= auth_route_prefix %>/unlock', params: { token: unlock_token }
115
118
 
116
119
  assert_response :ok, "Valid unlock token should return 200"
117
120
 
@@ -128,7 +131,7 @@ class <%= controller_namespace %>::LockableIntegrationTest < ActionDispatch::Int
128
131
  # API INTEGRATION: Verify unlock endpoint properly validates tokens
129
132
  @user.lock_account!
130
133
 
131
- post <%= unlock_path_helper %>, params: { token: "invalid_token_12345" }
134
+ post '<%= auth_route_prefix %>/unlock', params: { token: "invalid_token_12345" }
132
135
 
133
136
  assert_response :unauthorized, "Invalid token should return 401"
134
137
 
@@ -143,7 +146,7 @@ class <%= controller_namespace %>::LockableIntegrationTest < ActionDispatch::Int
143
146
  # API VALIDATION: Verify unlock endpoint validates required parameters
144
147
  @user.lock_account!
145
148
 
146
- post <%= unlock_path_helper %>, params: {}
149
+ post '<%= auth_route_prefix %>/unlock', params: {}
147
150
 
148
151
  assert_response :unprocessable_entity, "Missing token should return 422"
149
152
 
@@ -153,7 +156,7 @@ class <%= controller_namespace %>::LockableIntegrationTest < ActionDispatch::Int
153
156
 
154
157
  test "should not increment attempts when user does not exist" do
155
158
  # SECURITY: Verify failed attempts are not tracked for nonexistent users
156
- post <%= login_path_helper %>, params: {
159
+ post '<%= auth_route_prefix %>/login', params: {
157
160
  user: { email_address: "nonexistent@example.com", password: "anypassword" }
158
161
  }
159
162
 
@@ -166,7 +169,7 @@ class <%= controller_namespace %>::LockableIntegrationTest < ActionDispatch::Int
166
169
  test "should handle graceful error responses for authentication failures" do
167
170
  # ERROR HANDLING: Verify system handles authentication failures gracefully
168
171
 
169
- post <%= login_path_helper %>, params: {
172
+ post '<%= auth_route_prefix %>/login', params: {
170
173
  user: { email_address: "nonexistent@example.com", password: "wrongpassword" }
171
174
  }
172
175
 
@@ -182,7 +185,7 @@ class <%= controller_namespace %>::LockableIntegrationTest < ActionDispatch::Int
182
185
  # BEHAVIOR: Verify login timestamp tracking
183
186
  start_time = Time.current
184
187
 
185
- post <%= login_path_helper %>, params: {
188
+ post '<%= auth_route_prefix %>/login', params: {
186
189
  user: { email_address: @user.email_address, password: "correctpassword123" }
187
190
  }
188
191
 
@@ -1,8 +1,9 @@
1
1
  require 'test_helper'
2
2
 
3
- class <%= controller_namespace %>::PasswordResetIntegrationTest < ActionDispatch::IntegrationTest
3
+ class <%= controller_namespace('passwords') %>PasswordResetIntegrationTest < ActionDispatch::IntegrationTest
4
4
  def setup
5
5
  @organization = Organization.create!(name: "Test Organization")
6
+ @agency = Agency.create!(name: "Test Agency", organization: @organization)
6
7
  @user = User.create!(
7
8
  email_address: "password-reset@example.com",
8
9
  username: "passwordresetuser",
@@ -11,18 +12,15 @@ class <%= controller_namespace %>::PasswordResetIntegrationTest < ActionDispatch
11
12
  first_name: "Password",
12
13
  last_name: "User"
13
14
  )
15
+ # Create agent association for agency access (required for real-time agency lookup)
16
+ Agent.create!(user: @user, agency: @agency, role: "member")
14
17
  end
15
18
 
16
- def teardown
17
- User.destroy_all
18
- Organization.destroy_all
19
- end
20
-
21
- # POST <%= api_versioned? ? "/#{api_namespace}" : "" %>/auth/reset - Request Password Reset Tests
19
+ # POST <%= auth_route_prefix %>/reset - Request Password Reset Tests
22
20
  test "should initiate password reset with valid email_address" do
23
21
  # BEHAVIOR: Verify password reset request works with valid email_address
24
22
 
25
- post <%= reset_path_helper %>, params: { email_address: @user.email_address }
23
+ post '<%= auth_route_prefix %>/reset', params: { email_address: @user.email_address }
26
24
 
27
25
  # VERIFY API RESPONSE: Should return success status
28
26
  assert_response :ok, "Valid email_address should initiate password reset"
@@ -38,7 +36,7 @@ class <%= controller_namespace %>::PasswordResetIntegrationTest < ActionDispatch
38
36
  test "should reject password reset with invalid email_address format" do
39
37
  # VALIDATION: Verify email_address format validation
40
38
 
41
- post <%= reset_path_helper %>, params: { email_address: "invalid-email-format" }
39
+ post '<%= auth_route_prefix %>/reset', params: { email_address: "invalid-email-format" }
42
40
 
43
41
  # VERIFY VALIDATION ERROR: Should return validation error
44
42
  assert_response :unprocessable_entity, "Invalid email_address format should be rejected"
@@ -50,7 +48,7 @@ class <%= controller_namespace %>::PasswordResetIntegrationTest < ActionDispatch
50
48
  test "should handle password reset for nonexistent email_address safely" do
51
49
  # SECURITY: Verify nonexistent email_addresses don't reveal user information
52
50
 
53
- post <%= reset_path_helper %>, params: { email_address: "nonexistent@example.com" }
51
+ post '<%= auth_route_prefix %>/reset', params: { email_address: "nonexistent@example.com" }
54
52
 
55
53
  # VERIFY SECURITY RESPONSE: Should return same response as valid email_address (prevent enumeration)
56
54
  assert_response :ok, "Nonexistent email_address should return same response as valid email_address"
@@ -64,7 +62,7 @@ class <%= controller_namespace %>::PasswordResetIntegrationTest < ActionDispatch
64
62
  test "should require email_address parameter for password reset request" do
65
63
  # VALIDATION: Verify email_address parameter is required
66
64
 
67
- post <%= reset_path_helper %>, params: {}
65
+ post '<%= auth_route_prefix %>/reset', params: {}
68
66
 
69
67
  # VERIFY PARAMETER VALIDATION: Should return parameter error
70
68
  assert_response :unprocessable_entity, "Missing email_address should return validation error"
@@ -78,18 +76,18 @@ class <%= controller_namespace %>::PasswordResetIntegrationTest < ActionDispatch
78
76
 
79
77
  # EXECUTE MULTIPLE REQUESTS: Send multiple reset requests rapidly
80
78
  5.times do
81
- post <%= reset_path_helper %>, params: { email_address: @user.email_address }
79
+ post '<%= auth_route_prefix %>/reset', params: { email_address: @user.email_address }
82
80
  end
83
81
 
84
82
  # Make one more request that should be rate limited
85
- post <%= reset_path_helper %>, params: { email_address: @user.email_address }
83
+ post '<%= auth_route_prefix %>/reset', params: { email_address: @user.email_address }
86
84
 
87
85
  # VERIFY RATE LIMITING: Should eventually rate limit (implementation dependent)
88
86
  # For now, verify the endpoint is accessible (rate limiting will be added later)
89
87
  assert_response :ok, "Rate limiting test - endpoint should be accessible"
90
88
  end
91
89
 
92
- # PUT <%= api_versioned? ? "/#{api_namespace}" : "" %>/auth/reset - Confirm Password Reset Tests
90
+ # PUT <%= auth_route_prefix %>/reset - Confirm Password Reset Tests
93
91
  test "should reset password with valid token and new password" do
94
92
  # BEHAVIOR: Verify password reset confirmation works end-to-end
95
93
 
@@ -98,7 +96,7 @@ class <%= controller_namespace %>::PasswordResetIntegrationTest < ActionDispatch
98
96
  new_password = "newpassword456"
99
97
 
100
98
  # EXECUTE RESET: Confirm password reset with token
101
- put <%= reset_path_helper %>, params: {
99
+ patch '<%= auth_route_prefix %>/reset', params: {
102
100
  token: reset_token,
103
101
  password: new_password,
104
102
  password_confirmation: new_password
@@ -125,7 +123,7 @@ class <%= controller_namespace %>::PasswordResetIntegrationTest < ActionDispatch
125
123
  new_password = "attemptedpassword"
126
124
 
127
125
  # EXECUTE WITH INVALID TOKEN: Try to reset with invalid token
128
- put <%= reset_path_helper %>, params: {
126
+ patch '<%= auth_route_prefix %>/reset', params: {
129
127
  token: invalid_token,
130
128
  password: new_password,
131
129
  password_confirmation: new_password
@@ -154,10 +152,10 @@ class <%= controller_namespace %>::PasswordResetIntegrationTest < ActionDispatch
154
152
  exp: 1.second.ago.to_i, # Already expired
155
153
  password_hash: @user.password_digest[0..10]
156
154
  }
157
- expired_token = JWT.encode(expired_payload, PropelAuth.configuration.jwt_secret, 'HS256')
155
+ expired_token = JWT.encode(expired_payload, PropelAuthentication.configuration.jwt_secret, 'HS256')
158
156
 
159
157
  # EXECUTE WITH EXPIRED TOKEN: Try to reset with expired token
160
- put <%= reset_path_helper %>, params: {
158
+ patch '<%= auth_route_prefix %>/reset', params: {
161
159
  token: expired_token,
162
160
  password: "newpassword",
163
161
  password_confirmation: "newpassword"
@@ -176,7 +174,7 @@ class <%= controller_namespace %>::PasswordResetIntegrationTest < ActionDispatch
176
174
  reset_token = @user.generate_password_reset_token
177
175
 
178
176
  # EXECUTE WITH MISMATCHED PASSWORDS: Password and confirmation don't match
179
- put <%= reset_path_helper %>, params: {
177
+ patch '<%= auth_route_prefix %>/reset', params: {
180
178
  token: reset_token,
181
179
  password: "newpassword123",
182
180
  password_confirmation: "differentpassword456"
@@ -200,7 +198,7 @@ class <%= controller_namespace %>::PasswordResetIntegrationTest < ActionDispatch
200
198
  weak_password = "123" # Too short
201
199
 
202
200
  # EXECUTE WITH WEAK PASSWORD: Try to set weak password
203
- put <%= reset_path_helper %>, params: {
201
+ patch '<%= auth_route_prefix %>/reset', params: {
204
202
  token: reset_token,
205
203
  password: weak_password,
206
204
  password_confirmation: weak_password
@@ -223,7 +221,7 @@ class <%= controller_namespace %>::PasswordResetIntegrationTest < ActionDispatch
223
221
  reset_token = @user.generate_password_reset_token
224
222
 
225
223
  # TEST MISSING TOKEN
226
- put <%= reset_path_helper %>, params: {
224
+ patch '<%= auth_route_prefix %>/reset', params: {
227
225
  password: "newpassword123",
228
226
  password_confirmation: "newpassword123"
229
227
  }
@@ -231,7 +229,7 @@ class <%= controller_namespace %>::PasswordResetIntegrationTest < ActionDispatch
231
229
  assert_response :unprocessable_entity, "Missing token should be rejected"
232
230
 
233
231
  # TEST MISSING PASSWORD
234
- put <%= reset_path_helper %>, params: {
232
+ patch '<%= auth_route_prefix %>/reset', params: {
235
233
  token: reset_token,
236
234
  password_confirmation: "newpassword123"
237
235
  }
@@ -239,7 +237,7 @@ class <%= controller_namespace %>::PasswordResetIntegrationTest < ActionDispatch
239
237
  assert_response :unprocessable_entity, "Missing password should be rejected"
240
238
 
241
239
  # TEST MISSING CONFIRMATION
242
- put <%= reset_path_helper %>, params: {
240
+ patch '<%= auth_route_prefix %>/reset', params: {
243
241
  token: reset_token,
244
242
  password: "newpassword123"
245
243
  }
@@ -256,7 +254,7 @@ class <%= controller_namespace %>::PasswordResetIntegrationTest < ActionDispatch
256
254
 
257
255
  # EXECUTE RESET: Reset password successfully
258
256
  reset_token = @user.generate_password_reset_token
259
- put <%= reset_path_helper %>, params: {
257
+ patch '<%= auth_route_prefix %>/reset', params: {
260
258
  token: reset_token,
261
259
  password: "newpassword123",
262
260
  password_confirmation: "newpassword123"
@@ -279,7 +277,7 @@ class <%= controller_namespace %>::PasswordResetIntegrationTest < ActionDispatch
279
277
 
280
278
  # EXECUTE RESET: Reset password while locked
281
279
  reset_token = @user.generate_password_reset_token
282
- put <%= reset_path_helper %>, params: {
280
+ patch '<%= auth_route_prefix %>/reset', params: {
283
281
  token: reset_token,
284
282
  password: "newpassword123",
285
283
  password_confirmation: "newpassword123"
@@ -294,14 +292,14 @@ class <%= controller_namespace %>::PasswordResetIntegrationTest < ActionDispatch
294
292
  assert @user.authenticate("newpassword123"), "Should authenticate with new password"
295
293
  end
296
294
 
297
- # GET <%= api_versioned? ? "/#{api_namespace}" : "" %>/auth/reset/verify - Token Verification Tests
295
+ # GET <%= auth_route_prefix %>/reset/verify - Token Verification Tests
298
296
  test "should verify valid password reset token" do
299
297
  # BEHAVIOR: Verify token verification endpoint works
300
298
 
301
299
  reset_token = @user.generate_password_reset_token
302
300
 
303
301
  # EXECUTE VERIFICATION: Verify token is valid
304
- get <%= reset_path_helper %>, params: { token: reset_token }
302
+ get '<%= auth_route_prefix %>/reset', params: { token: reset_token }
305
303
 
306
304
  # VERIFY API RESPONSE: Should confirm token validity
307
305
  assert_response :ok, "Valid token should pass verification"
@@ -319,7 +317,7 @@ class <%= controller_namespace %>::PasswordResetIntegrationTest < ActionDispatch
319
317
  invalid_token = "invalid.jwt.token"
320
318
 
321
319
  # EXECUTE VERIFICATION: Verify invalid token
322
- get <%= reset_path_helper %>, params: { token: invalid_token }
320
+ get '<%= auth_route_prefix %>/reset', params: { token: invalid_token }
323
321
 
324
322
  # VERIFY REJECTION: Should indicate token is invalid
325
323
  assert_response :unauthorized, "Invalid token should be rejected"
@@ -340,10 +338,10 @@ class <%= controller_namespace %>::PasswordResetIntegrationTest < ActionDispatch
340
338
  exp: 1.second.ago.to_i, # Already expired
341
339
  password_hash: @user.password_digest[0..10]
342
340
  }
343
- expired_token = JWT.encode(expired_payload, PropelAuth.configuration.jwt_secret, 'HS256')
341
+ expired_token = JWT.encode(expired_payload, PropelAuthentication.configuration.jwt_secret, 'HS256')
344
342
 
345
343
  # EXECUTE VERIFICATION: Verify expired token
346
- get <%= reset_path_helper %>, params: { token: expired_token }
344
+ get '<%= auth_route_prefix %>/reset', params: { token: expired_token }
347
345
 
348
346
  # VERIFY EXPIRATION: Should indicate token is expired
349
347
  assert_response :unauthorized, "Expired token should be rejected"
@@ -355,7 +353,7 @@ class <%= controller_namespace %>::PasswordResetIntegrationTest < ActionDispatch
355
353
  test "should require token parameter for verification" do
356
354
  # VALIDATION: Verify token parameter is required for verification
357
355
 
358
- get <%= reset_path_helper %>, params: {}
356
+ get '<%= auth_route_prefix %>/reset', params: {}
359
357
 
360
358
  # VERIFY PARAMETER VALIDATION: Should return parameter error
361
359
  assert_response :unprocessable_entity, "Missing token should return validation error"
@@ -369,12 +367,12 @@ class <%= controller_namespace %>::PasswordResetIntegrationTest < ActionDispatch
369
367
  # INTEGRATION: Test complete password reset workflow from start to finish
370
368
 
371
369
  # STEP 1: REQUEST RESET - Send password reset request
372
- post <%= reset_path_helper %>, params: { email_address: @user.email_address }
370
+ post '<%= auth_route_prefix %>/reset', params: { email_address: @user.email_address }
373
371
  assert_response :ok, "Reset request should succeed"
374
372
 
375
373
  # STEP 2: VERIFY TOKEN - Check that we can verify tokens (simulate email link click)
376
374
  reset_token = @user.generate_password_reset_token # Simulate token from email
377
- get <%= reset_path_helper %>, params: { token: reset_token }
375
+ get '<%= auth_route_prefix %>/reset', params: { token: reset_token }
378
376
  assert_response :ok, "Token verification should succeed"
379
377
 
380
378
  verification_json = JSON.parse(response.body)
@@ -382,7 +380,7 @@ class <%= controller_namespace %>::PasswordResetIntegrationTest < ActionDispatch
382
380
 
383
381
  # STEP 3: RESET PASSWORD - Complete password reset
384
382
  new_password = "completeworkflow123"
385
- put <%= reset_path_helper %>, params: {
383
+ patch '<%= auth_route_prefix %>/reset', params: {
386
384
  token: reset_token,
387
385
  password: new_password,
388
386
  password_confirmation: new_password
@@ -390,7 +388,7 @@ class <%= controller_namespace %>::PasswordResetIntegrationTest < ActionDispatch
390
388
  assert_response :ok, "Password reset should succeed"
391
389
 
392
390
  # STEP 4: VERIFY NEW PASSWORD - Test login with new password
393
- post <%= login_path_helper %>, params: {
391
+ post '<%= auth_route_prefix %>/login', params: {
394
392
  user: { email_address: @user.email_address, password: new_password }
395
393
  }
396
394
  assert_response :ok, "Should login with new password"
@@ -399,7 +397,7 @@ class <%= controller_namespace %>::PasswordResetIntegrationTest < ActionDispatch
399
397
  assert login_json['token'].present?, "Should receive JWT token"
400
398
 
401
399
  # STEP 5: VERIFY OLD PASSWORD INVALID - Test old password doesn't work
402
- post <%= login_path_helper %>, params: {
400
+ post '<%= auth_route_prefix %>/login', params: {
403
401
  user: { email_address: @user.email_address, password: "originalpassword123" }
404
402
  }
405
403
  assert_response :unauthorized, "Should not login with old password"
@@ -411,7 +409,7 @@ class <%= controller_namespace %>::PasswordResetIntegrationTest < ActionDispatch
411
409
  reset_token = @user.generate_password_reset_token
412
410
 
413
411
  # FIRST RESET: Use token successfully
414
- put <%= reset_path_helper %>, params: {
412
+ patch '<%= auth_route_prefix %>/reset', params: {
415
413
  token: reset_token,
416
414
  password: "firstnewpassword123",
417
415
  password_confirmation: "firstnewpassword123"
@@ -419,7 +417,7 @@ class <%= controller_namespace %>::PasswordResetIntegrationTest < ActionDispatch
419
417
  assert_response :ok, "First password reset should succeed"
420
418
 
421
419
  # ATTEMPTED REUSE: Try to use same token again
422
- put <%= reset_path_helper %>, params: {
420
+ patch '<%= auth_route_prefix %>/reset', params: {
423
421
  token: reset_token,
424
422
  password: "secondnewpassword456",
425
423
  password_confirmation: "secondnewpassword456"
@@ -447,7 +445,7 @@ class <%= controller_namespace %>::PasswordResetIntegrationTest < ActionDispatch
447
445
  assert @user.valid_password_reset_token?(token2), "Second token should be valid"
448
446
 
449
447
  # USE FIRST TOKEN: Reset password with first token
450
- put <%= reset_path_helper %>, params: {
448
+ patch '<%= auth_route_prefix %>/reset', params: {
451
449
  token: token1,
452
450
  password: "concurrentpassword1",
453
451
  password_confirmation: "concurrentpassword1"
@@ -455,7 +453,7 @@ class <%= controller_namespace %>::PasswordResetIntegrationTest < ActionDispatch
455
453
  assert_response :ok, "First reset should succeed"
456
454
 
457
455
  # TRY SECOND TOKEN: Attempt reset with second token (should fail due to password change)
458
- put <%= reset_path_helper %>, params: {
456
+ patch '<%= auth_route_prefix %>/reset', params: {
459
457
  token: token2,
460
458
  password: "concurrentpassword2",
461
459
  password_confirmation: "concurrentpassword2"