propel_authentication 0.1.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 (102) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +21 -0
  3. data/README.md +290 -0
  4. data/Rakefile +12 -0
  5. data/lib/generators/propel_auth/install_generator.rb +486 -0
  6. data/lib/generators/propel_auth/pack_generator.rb +277 -0
  7. data/lib/generators/propel_auth/templates/agency.rb +7 -0
  8. data/lib/generators/propel_auth/templates/agent.rb +7 -0
  9. data/lib/generators/propel_auth/templates/auth/base_passwords_controller.rb.tt +99 -0
  10. data/lib/generators/propel_auth/templates/auth/base_tokens_controller.rb.tt +90 -0
  11. data/lib/generators/propel_auth/templates/auth/passwords_controller.rb.tt +126 -0
  12. data/lib/generators/propel_auth/templates/auth_mailer.rb +180 -0
  13. data/lib/generators/propel_auth/templates/authenticatable.rb +38 -0
  14. data/lib/generators/propel_auth/templates/concerns/confirmable.rb +145 -0
  15. data/lib/generators/propel_auth/templates/concerns/lockable.rb +123 -0
  16. data/lib/generators/propel_auth/templates/concerns/propel_authentication.rb +44 -0
  17. data/lib/generators/propel_auth/templates/concerns/rack_session_disable.rb +19 -0
  18. data/lib/generators/propel_auth/templates/concerns/recoverable.rb +124 -0
  19. data/lib/generators/propel_auth/templates/config/environments/development_email.rb +43 -0
  20. data/lib/generators/propel_auth/templates/db/migrate/create_agencies.rb +20 -0
  21. data/lib/generators/propel_auth/templates/db/migrate/create_agents.rb +11 -0
  22. data/lib/generators/propel_auth/templates/db/migrate/create_invitations.rb +28 -0
  23. data/lib/generators/propel_auth/templates/db/migrate/create_organizations.rb +18 -0
  24. data/lib/generators/propel_auth/templates/db/migrate/create_users.rb +43 -0
  25. data/lib/generators/propel_auth/templates/db/seeds.rb +29 -0
  26. data/lib/generators/propel_auth/templates/invitation.rb +133 -0
  27. data/lib/generators/propel_auth/templates/lib/propel_auth.rb +84 -0
  28. data/lib/generators/propel_auth/templates/organization.rb +7 -0
  29. data/lib/generators/propel_auth/templates/propel_auth.rb +132 -0
  30. data/lib/generators/propel_auth/templates/services/auth_notification_service.rb +89 -0
  31. data/lib/generators/propel_auth/templates/test/concerns/confirmable_test.rb.tt +247 -0
  32. data/lib/generators/propel_auth/templates/test/concerns/lockable_test.rb.tt +282 -0
  33. data/lib/generators/propel_auth/templates/test/concerns/propel_authentication_test.rb.tt +75 -0
  34. data/lib/generators/propel_auth/templates/test/concerns/recoverable_test.rb.tt +327 -0
  35. data/lib/generators/propel_auth/templates/test/controllers/auth/lockable_integration_test.rb.tt +196 -0
  36. data/lib/generators/propel_auth/templates/test/controllers/auth/password_reset_integration_test.rb.tt +471 -0
  37. data/lib/generators/propel_auth/templates/test/controllers/auth/tokens_controller_test.rb.tt +265 -0
  38. data/lib/generators/propel_auth/templates/test/mailers/auth_mailer_test.rb.tt +216 -0
  39. data/lib/generators/propel_auth/templates/test/mailers/previews/auth_mailer_preview.rb +161 -0
  40. data/lib/generators/propel_auth/templates/tokens_controller.rb.tt +96 -0
  41. data/lib/generators/propel_auth/templates/user.rb +21 -0
  42. data/lib/generators/propel_auth/templates/user_test.rb.tt +81 -0
  43. data/lib/generators/propel_auth/templates/views/auth_mailer/account_unlock.html.erb +213 -0
  44. data/lib/generators/propel_auth/templates/views/auth_mailer/account_unlock.text.erb +56 -0
  45. data/lib/generators/propel_auth/templates/views/auth_mailer/email_confirmation.html.erb +213 -0
  46. data/lib/generators/propel_auth/templates/views/auth_mailer/email_confirmation.text.erb +32 -0
  47. data/lib/generators/propel_auth/templates/views/auth_mailer/password_reset.html.erb +166 -0
  48. data/lib/generators/propel_auth/templates/views/auth_mailer/password_reset.text.erb +32 -0
  49. data/lib/generators/propel_auth/templates/views/auth_mailer/user_invitation.html.erb +194 -0
  50. data/lib/generators/propel_auth/templates/views/auth_mailer/user_invitation.text.erb +51 -0
  51. data/lib/generators/propel_auth/test/dummy/Dockerfile +72 -0
  52. data/lib/generators/propel_auth/test/dummy/Gemfile +63 -0
  53. data/lib/generators/propel_auth/test/dummy/Gemfile.lock +394 -0
  54. data/lib/generators/propel_auth/test/dummy/README.md +24 -0
  55. data/lib/generators/propel_auth/test/dummy/Rakefile +6 -0
  56. data/lib/generators/propel_auth/test/dummy/app/assets/stylesheets/application.css +10 -0
  57. data/lib/generators/propel_auth/test/dummy/app/controllers/application_controller.rb +4 -0
  58. data/lib/generators/propel_auth/test/dummy/app/helpers/application_helper.rb +2 -0
  59. data/lib/generators/propel_auth/test/dummy/app/jobs/application_job.rb +7 -0
  60. data/lib/generators/propel_auth/test/dummy/app/mailers/application_mailer.rb +4 -0
  61. data/lib/generators/propel_auth/test/dummy/app/models/application_record.rb +3 -0
  62. data/lib/generators/propel_auth/test/dummy/app/views/layouts/application.html.erb +27 -0
  63. data/lib/generators/propel_auth/test/dummy/app/views/layouts/mailer.html.erb +13 -0
  64. data/lib/generators/propel_auth/test/dummy/app/views/layouts/mailer.text.erb +1 -0
  65. data/lib/generators/propel_auth/test/dummy/app/views/pwa/manifest.json.erb +22 -0
  66. data/lib/generators/propel_auth/test/dummy/app/views/pwa/service-worker.js +26 -0
  67. data/lib/generators/propel_auth/test/dummy/bin/brakeman +7 -0
  68. data/lib/generators/propel_auth/test/dummy/bin/dev +2 -0
  69. data/lib/generators/propel_auth/test/dummy/bin/docker-entrypoint +14 -0
  70. data/lib/generators/propel_auth/test/dummy/bin/rails +4 -0
  71. data/lib/generators/propel_auth/test/dummy/bin/rake +4 -0
  72. data/lib/generators/propel_auth/test/dummy/bin/rubocop +8 -0
  73. data/lib/generators/propel_auth/test/dummy/bin/setup +34 -0
  74. data/lib/generators/propel_auth/test/dummy/bin/thrust +5 -0
  75. data/lib/generators/propel_auth/test/dummy/config/application.rb +42 -0
  76. data/lib/generators/propel_auth/test/dummy/config/boot.rb +4 -0
  77. data/lib/generators/propel_auth/test/dummy/config/cable.yml +10 -0
  78. data/lib/generators/propel_auth/test/dummy/config/credentials.yml.enc +1 -0
  79. data/lib/generators/propel_auth/test/dummy/config/database.yml +41 -0
  80. data/lib/generators/propel_auth/test/dummy/config/environment.rb +5 -0
  81. data/lib/generators/propel_auth/test/dummy/config/environments/development.rb +72 -0
  82. data/lib/generators/propel_auth/test/dummy/config/environments/production.rb +89 -0
  83. data/lib/generators/propel_auth/test/dummy/config/environments/test.rb +53 -0
  84. data/lib/generators/propel_auth/test/dummy/config/initializers/assets.rb +10 -0
  85. data/lib/generators/propel_auth/test/dummy/config/initializers/content_security_policy.rb +25 -0
  86. data/lib/generators/propel_auth/test/dummy/config/initializers/filter_parameter_logging.rb +8 -0
  87. data/lib/generators/propel_auth/test/dummy/config/initializers/inflections.rb +16 -0
  88. data/lib/generators/propel_auth/test/dummy/config/locales/en.yml +31 -0
  89. data/lib/generators/propel_auth/test/dummy/config/master.key +1 -0
  90. data/lib/generators/propel_auth/test/dummy/config/puma.rb +41 -0
  91. data/lib/generators/propel_auth/test/dummy/config/routes.rb +2 -0
  92. data/lib/generators/propel_auth/test/dummy/config/storage.yml +34 -0
  93. data/lib/generators/propel_auth/test/dummy/config.ru +6 -0
  94. data/lib/generators/propel_auth/test/dummy/db/schema.rb +14 -0
  95. data/lib/generators/propel_auth/test/generators/authentication/controllers/tokens_controller_test.rb +230 -0
  96. data/lib/generators/propel_auth/test/generators/authentication/install_generator_test.rb +490 -0
  97. data/lib/generators/propel_auth/test/generators/authentication/uninstall_generator_test.rb +408 -0
  98. data/lib/generators/propel_auth/test/integration/generator_integration_test.rb +158 -0
  99. data/lib/generators/propel_auth/test/integration/multi_version_generator_test.rb +125 -0
  100. data/lib/generators/propel_auth/unpack_generator.rb +345 -0
  101. data/lib/propel_auth.rb +3 -0
  102. metadata +195 -0
@@ -0,0 +1,490 @@
1
+ require "test_helper"
2
+ require "rails/generators/test_case"
3
+ require_relative '../../../lib/generators/propel/authentication/install_generator'
4
+
5
+ class Propel::Authentication::InstallGeneratorTest < Rails::Generators::TestCase
6
+ tests Propel::Authentication::InstallGenerator
7
+
8
+ setup do
9
+ self.destination_root = Rails.root.join("tmp/generators", Process.pid.to_s)
10
+ prepare_destination
11
+ prepare_rails_app_structure
12
+ run_generator
13
+ end
14
+
15
+ teardown do
16
+ FileUtils.rm_rf(destination_root)
17
+ end
18
+
19
+ test "generator creates propel access initializer and configuration file" do
20
+
21
+ assert_file "config/initializers/propel_access_configuration.rb" do |content|
22
+ assert_match(/module PropelAccess/, content)
23
+ assert_match(/class Configuration/, content)
24
+ assert_match(/attr_accessor :jwt_secret/, content)
25
+ end
26
+
27
+ assert_file "config/initializers/propel_access.rb" do |content|
28
+ assert_match(/require_relative 'propel_access_configuration'/, content)
29
+ assert_match(/PropelAccess\.configure/, content)
30
+ assert_match(/jwt_secret/, content)
31
+ assert_match(/jwt_expiration/, content)
32
+ assert_match(/password_length/, content)
33
+ assert_match(/24\.hours/, content)
34
+ end
35
+ end
36
+
37
+ test "generator creates all core models with proper JWT-ready associations" do
38
+ assert_file "app/models/user.rb" do |content|
39
+ assert_match(/class User < ApplicationRecord/, content)
40
+ assert_match(/include Authenticatable/, content)
41
+ assert_match(/has_many :invitations/, content)
42
+ assert_match(/belongs_to :organization/, content)
43
+ # Verify user model is set up for multi-tenant JWT system
44
+ end
45
+
46
+ assert_file "app/models/organization.rb" do |content|
47
+ assert_match(/class Organization < ApplicationRecord/, content)
48
+ assert_match(/has_many :users/, content)
49
+ assert_match(/has_many :agencies/, content)
50
+ # Verify organization structure for multi-tenant context
51
+ end
52
+
53
+ assert_file "app/models/agency.rb" do |content|
54
+ assert_match(/class Agency < ApplicationRecord/, content)
55
+ assert_match(/belongs_to :organization/, content)
56
+ assert_match(/has_many :agents/, content)
57
+ end
58
+
59
+ assert_file "app/models/agent.rb" do |content|
60
+ assert_match(/class Agent < ApplicationRecord/, content)
61
+ assert_match(/belongs_to :agency/, content)
62
+ assert_match(/belongs_to :user/, content)
63
+ # Verify agent model connects users to agencies
64
+ end
65
+
66
+ assert_file "app/models/invitation.rb" do |content|
67
+ assert_match(/class Invitation < ApplicationRecord/, content)
68
+ assert_match(/belongs_to :organization/, content)
69
+ assert_match(/belongs_to :inviter, class_name: 'User'/, content)
70
+ assert_match(/validates :email_address/, content)
71
+ # Verify invitation system supports JWT token-based invitations
72
+ end
73
+ end
74
+
75
+ test "generator creates database seeds file" do
76
+ assert_file "db/seeds.rb" do |content|
77
+ assert_match(/# Create test organization and user for authentication testing/, content)
78
+ assert_match(/organization = Organization\.find_or_create_by!/, content)
79
+ assert_match(/user = User\.find_or_create_by!/, content)
80
+ end
81
+ end
82
+
83
+ test "generator creates authenticatable concern with JWT token generation and validation" do
84
+ assert_file "app/models/concerns/authenticatable.rb" do |content|
85
+ assert_match(/module Authenticatable/, content)
86
+ assert_match(/extend ActiveSupport::Concern/, content)
87
+ assert_match(/has_secure_password/, content)
88
+ assert_match(/validates :email_address/, content)
89
+ assert_match(/validates :password/, content)
90
+
91
+ # EPIC 1 REQUIREMENT: JWT token generation functionality
92
+ assert_match(/def generate_jwt_token/, content)
93
+ assert_match(/JWT\.encode/, content)
94
+
95
+ # EPIC 1 REQUIREMENT: JWT token validation functionality
96
+ assert_match(/class_methods do/, content)
97
+ assert_match(/def find_by_jwt_token/, content)
98
+ assert_match(/JWT\.decode/, content)
99
+ end
100
+ end
101
+
102
+ test "generator creates JWT authentication controller for complete API functionality" do
103
+ assert_file "app/controllers/auth/tokens_controller.rb" do |content|
104
+ assert_match(/class Auth::TokensController < ApplicationController/, content)
105
+
106
+ # EPIC 1 REQUIREMENT: Login endpoint
107
+ assert_match(/def create/, content)
108
+ assert_match(/POST \/auth\/login/, content)
109
+
110
+ # EPIC 1 REQUIREMENT: Logout endpoint
111
+ assert_match(/def destroy/, content)
112
+ assert_match(/DELETE \/auth\/logout/, content)
113
+
114
+ # EPIC 1 REQUIREMENT: Current user endpoint
115
+ assert_match(/def me/, content)
116
+ assert_match(/GET \/auth\/me/, content)
117
+
118
+ # Verify proper JWT authentication middleware
119
+ assert_match(/include PropelAuthentication/, content)
120
+ assert_match(/return unless authenticate_user/, content)
121
+
122
+ # Verify proper API response format
123
+ assert_match(/render json:/, content)
124
+ assert_match(/status: :unauthorized/, content)
125
+ assert_match(/status: :unprocessable_entity/, content)
126
+ end
127
+ end
128
+
129
+ test "generator creates comprehensive migrations with proper database structure for JWT system" do
130
+ assert_migration "db/migrate/create_users.rb" do |migration|
131
+ assert_match(/create_table :users/, migration)
132
+ # Core authentication fields
133
+ assert_match(/t\.string :email_address, null: false/, migration)
134
+ assert_match(/t\.string :username, null: false/, migration)
135
+ assert_match(/t\.string :password_digest, null: false/, migration)
136
+ assert_match(/t\.string :phone_number/, migration)
137
+
138
+ # User profile fields
139
+ assert_match(/t\.string :first_name/, migration)
140
+ assert_match(/t\.string :last_name/, migration)
141
+ assert_match(/t\.string :time_zone/, migration)
142
+
143
+ # Authentication status fields for JWT system
144
+ assert_match(/t\.datetime :confirmed_at/, migration)
145
+ assert_match(/t\.integer :status, default: 0/, migration)
146
+ assert_match(/t\.datetime :last_login_at/, migration)
147
+ assert_match(/t\.integer :failed_login_attempts, default: 0/, migration)
148
+ assert_match(/t\.datetime :locked_at/, migration)
149
+
150
+ # Multi-tenant association
151
+ assert_match(/t\.references :organization/, migration)
152
+
153
+ # JSON fields for flexible data storage
154
+ assert_match(/t\.jsonb :meta, default: {}/, migration)
155
+ assert_match(/t\.jsonb :settings, default: {}/, migration)
156
+
157
+ # Proper indexes for JWT authentication performance
158
+ assert_match(/add_index :users, :email_address, unique: true/, migration)
159
+ assert_match(/add_index :users, :username, unique: true/, migration)
160
+ assert_match(/add_index :users, :phone_number, unique: true/, migration)
161
+ end
162
+
163
+ assert_migration "db/migrate/create_organizations.rb" do |migration|
164
+ assert_match(/create_table :organizations/, migration)
165
+ assert_match(/t\.string :name/, migration)
166
+ assert_match(/t\.jsonb :settings/, migration)
167
+ # Verify organization supports multi-tenant JWT context
168
+ end
169
+
170
+ assert_migration "db/migrate/create_agencies.rb" do |migration|
171
+ assert_match(/create_table :agencies/, migration)
172
+ assert_match(/t\.references :organization/, migration)
173
+ assert_match(/foreign_key: true/, migration)
174
+ # Verify proper foreign key relationships for multi-tenant system
175
+ end
176
+
177
+ assert_migration "db/migrate/create_agents.rb" do |migration|
178
+ assert_match(/create_table :agents/, migration)
179
+ assert_match(/t\.references :agency/, migration)
180
+ assert_match(/t\.references :user/, migration)
181
+ # Verify agent-user relationship for role-based JWT tokens
182
+ end
183
+
184
+ assert_migration "db/migrate/create_invitations.rb" do |migration|
185
+ assert_match(/create_table :invitations/, migration)
186
+ assert_match(/t\.references :organization/, migration)
187
+ assert_match(/t\.references :inviter/, migration)
188
+ assert_match(/t\.string :email_address/, migration)
189
+ assert_match(/t\.integer :status/, migration)
190
+ # Verify invitation system integrates with JWT authentication
191
+ end
192
+ end
193
+
194
+ test "generator injects complete JWT authentication routes into routes file" do
195
+ assert_file "config/routes.rb" do |content|
196
+ assert_match(/namespace :auth/, content)
197
+
198
+ # EPIC 1 REQUIREMENT: JWT token management routes with explicit declarations
199
+ assert_match(/post 'login', to: 'tokens#create'/, content)
200
+ assert_match(/delete 'logout', to: 'tokens#destroy'/, content)
201
+ assert_match(/get 'me', to: 'tokens#me'/, content)
202
+
203
+ # EPIC 1 REQUIREMENT: Password reset routes
204
+ assert_match(/post 'reset', to: 'passwords#create'/, content)
205
+ assert_match(/get 'reset', to: 'passwords#show'/, content)
206
+ assert_match(/patch 'reset', to: 'passwords#update'/, content)
207
+
208
+ # Verify no session-based routes (JWT-first approach)
209
+ assert_no_match(/sessions/, content)
210
+ end
211
+ end
212
+
213
+ test "generator adds required JWT and authentication gems to Gemfile" do
214
+ assert_file "Gemfile" do |content|
215
+ # EPIC 1 REQUIREMENT: Core authentication gems
216
+ assert_match(/gem ['"]bcrypt['"]/, content)
217
+ assert_match(/gem ['"]jwt['"]/, content)
218
+
219
+ # Verify gem versions are specified for security
220
+ assert_match(/bcrypt['"], ['"]~>/, content)
221
+ assert_match(/jwt['"], ['"]~>/, content)
222
+ end
223
+ end
224
+
225
+ test "generator creates functional model tests that verify complete JWT authentication workflow" do
226
+ assert_file "test/models/user_test.rb" do |content|
227
+ assert_match(/class UserTest < ActiveSupport::TestCase/, content)
228
+
229
+ # EPIC 1 REQUIREMENT: Test JWT token generation
230
+ assert_match(/test ['"]should generate valid JWT token['"]/, content)
231
+ assert_match(/user\.generate_jwt_token/, content)
232
+ assert_match(/JWT\.decode/, content)
233
+
234
+ # EPIC 1 REQUIREMENT: Test JWT token validation
235
+ assert_match(/test ['"]should validate JWT token and find user['"]/, content)
236
+ assert_match(/User\.find_by_jwt_token/, content)
237
+
238
+ # EPIC 1 REQUIREMENT: Test authentication workflow
239
+ assert_match(/test ['"]should authenticate with correct password['"]/, content)
240
+ assert_match(/user\.authenticate/, content)
241
+
242
+ # EPIC 1 REQUIREMENT: Test validation rules
243
+ assert_match(/test ['"]should validate email_address uniqueness['"]/, content)
244
+ assert_match(/test ['"]should validate password strength['"]/, content)
245
+
246
+ # Verify tests use actual JWT functionality, not mocks
247
+ assert_no_match(/mock/, content)
248
+ assert_no_match(/stub/, content)
249
+ end
250
+ end
251
+
252
+ test "generator creates JWT authentication controller that supports comprehensive testing" do
253
+ assert_file "app/controllers/auth/tokens_controller.rb" do |content|
254
+ # EPIC 1 REQUIREMENT: Verify controller supports JWT authentication testing
255
+ assert_match(/class Auth::TokensController < ApplicationController/, content)
256
+
257
+ # Verify login endpoint structure for testing
258
+ assert_match(/def create/, content)
259
+ assert_match(/render json:/, content)
260
+
261
+ # Verify logout endpoint structure for testing
262
+ assert_match(/def destroy/, content)
263
+
264
+ # Verify current user endpoint structure for testing
265
+ assert_match(/def me/, content)
266
+
267
+ # Verify JWT authentication support for testing
268
+ assert_match(/include PropelAuthentication/, content)
269
+ assert_match(/return unless authenticate_user/, content)
270
+
271
+ # Verify proper error handling for testing
272
+ assert_match(/status: :unauthorized/, content)
273
+ end
274
+ end
275
+
276
+ test "generator runs idempotently without creating duplicate content or breaking existing JWT functionality" do
277
+ # Run generator again
278
+ run_generator
279
+
280
+ # Should not raise errors and files should still exist with correct content
281
+ assert_file "app/models/user.rb"
282
+ assert_file "app/models/concerns/authenticatable.rb"
283
+ assert_file "app/controllers/auth/tokens_controller.rb"
284
+ assert_file "config/initializers/propel_access.rb"
285
+
286
+ # Check that no duplicate content was added to routes
287
+ assert_file "config/routes.rb" do |content|
288
+ # Should only have one namespace :auth block
289
+ auth_blocks = content.scan(/namespace :auth/).length
290
+ assert_equal 1, auth_blocks, "Should have exactly one auth namespace block"
291
+
292
+ # Should only have one set of JWT routes
293
+ login_routes = content.scan(/post 'login'/).length
294
+ assert_equal 1, login_routes, "Should have exactly one login route"
295
+ end
296
+
297
+ # Check that no duplicate gems were added
298
+ assert_file "Gemfile" do |content|
299
+ bcrypt_occurrences = content.scan(/gem ['"]bcrypt['"]/).length
300
+ jwt_occurrences = content.scan(/gem ['"]jwt['"]/).length
301
+ assert_equal 1, bcrypt_occurrences, "Should have exactly one bcrypt gem entry"
302
+ assert_equal 1, jwt_occurrences, "Should have exactly one jwt gem entry"
303
+ end
304
+ end
305
+
306
+ test "generated JWT authentication system has valid syntax and proper structure for real Rails API app" do
307
+ # Verify all generated Ruby files have valid syntax
308
+ ruby_files = [
309
+ "app/models/user.rb",
310
+ "app/models/organization.rb",
311
+ "app/models/agency.rb",
312
+ "app/models/agent.rb",
313
+ "app/models/invitation.rb",
314
+ "app/models/concerns/authenticatable.rb",
315
+ "app/controllers/auth/tokens_controller.rb",
316
+ "test/models/user_test.rb",
317
+ "config/initializers/propel_access.rb"
318
+ ]
319
+
320
+ ruby_files.each do |file_path|
321
+ assert_file file_path do |content|
322
+ # Basic syntax validation - no syntax errors should be present
323
+ assert_no_match(/syntax error/, content)
324
+ assert_no_match(/unexpected/, content)
325
+
326
+ # Ensure proper class/module structure
327
+ if file_path.include?("models/concerns/")
328
+ assert_match(/^module \w+/, content)
329
+ assert_match(/extend ActiveSupport::Concern/, content)
330
+ elsif file_path.include?("controllers/")
331
+ assert_match(/^class .*Controller < ApplicationController/, content) unless file_path.include?("_test.rb")
332
+ elsif file_path.include?("models/")
333
+ assert_match(/^class \w+ < ApplicationRecord/, content) unless file_path.include?("_test.rb")
334
+ end
335
+ end
336
+ end
337
+ end
338
+
339
+ test "generated user model integrates properly with JWT-enabled authenticatable concern" do
340
+ assert_file "app/models/user.rb" do |user_content|
341
+ assert_match(/include Authenticatable/, user_content)
342
+ assert_match(/belongs_to :organization/, user_content)
343
+ end
344
+
345
+ # Verify the concern provides the expected JWT functionality
346
+ assert_file "app/models/concerns/authenticatable.rb" do |concern_content|
347
+ assert_match(/has_secure_password/, concern_content)
348
+ assert_match(/validates :email_address/, concern_content)
349
+ assert_match(/validates :password/, concern_content)
350
+
351
+ # EPIC 1 REQUIREMENT: JWT functionality must be present
352
+ assert_match(/def generate_jwt_token/, concern_content)
353
+ assert_match(/JWT\.encode/, concern_content)
354
+ assert_match(/class_methods do/, concern_content)
355
+ assert_match(/def find_by_jwt_token/, concern_content)
356
+ assert_match(/JWT\.decode/, concern_content)
357
+ end
358
+ end
359
+
360
+ test "generated JWT authentication controller provides complete API functionality" do
361
+ assert_file "app/controllers/auth/tokens_controller.rb" do |content|
362
+ # Verify all required API endpoints are present
363
+ assert_match(/def create/, content) # Login
364
+ assert_match(/def destroy/, content) # Logout
365
+ assert_match(/def me/, content) # Current user
366
+
367
+ # Verify proper JSON API responses
368
+ assert_match(/render json:/, content)
369
+ assert_match(/status: :ok/, content)
370
+ assert_match(/status: :unauthorized/, content)
371
+
372
+ # Verify JWT token handling
373
+ assert_match(/include PropelAuthentication/, content)
374
+ assert_match(/return unless authenticate_user/, content)
375
+ end
376
+ end
377
+
378
+ test "generated migrations create proper foreign key relationships for multi-tenant JWT system" do
379
+ # Verify organizations are the root of the multi-tenant hierarchy
380
+ assert_migration "db/migrate/create_organizations.rb" do |migration|
381
+ assert_match(/create_table :organizations/, migration)
382
+ end
383
+
384
+ # Verify users belong to organizations (for JWT token context)
385
+ assert_migration "db/migrate/create_users.rb" do |migration|
386
+ assert_match(/t\.references :organization/, migration)
387
+ end
388
+
389
+ # Verify agencies reference organizations
390
+ assert_migration "db/migrate/create_agencies.rb" do |migration|
391
+ assert_match(/t\.references :organization/, migration)
392
+ assert_match(/foreign_key: true/, migration)
393
+ end
394
+
395
+ # Verify agents reference both agencies and users (for role-based JWT claims)
396
+ assert_migration "db/migrate/create_agents.rb" do |migration|
397
+ assert_match(/t\.references :agency/, migration)
398
+ assert_match(/t\.references :user/, migration)
399
+ end
400
+
401
+ # Verify invitations have polymorphic relationship (for flexible JWT-based invitations)
402
+ assert_migration "db/migrate/create_invitations.rb" do |migration|
403
+ assert_match(/t\.references :organization/, migration)
404
+ assert_match(/t\.references :inviter/, migration)
405
+ assert_match(/t\.string :email_address/, migration)
406
+ assert_match(/t\.integer :status/, migration)
407
+ end
408
+ end
409
+
410
+ test "generated JWT configuration provides secure defaults for production use" do
411
+ assert_file "config/initializers/propel_access.rb" do |content|
412
+ # Verify JWT configuration structure exists
413
+ assert_match(/PropelAccess\.configure/, content)
414
+
415
+ # EPIC 1 REQUIREMENT: Secure JWT configuration
416
+ assert_match(/jwt_secret/, content)
417
+ assert_match(/jwt_expiration/, content)
418
+
419
+ # Verify secure defaults
420
+ assert_match(/24\.hours/, content) # Reasonable token expiration
421
+ assert_match(/password_length.*8/, content) # Strong password requirement
422
+
423
+ # Verify environment-aware configuration
424
+ assert_match(/Rails\.env/, content)
425
+ end
426
+ end
427
+
428
+ test "generator creates complete JWT authentication system that works immediately for JavaScript frontends" do
429
+ # This is the EPIC 1 GOAL: Complete working system from day one
430
+
431
+ # Verify all components exist
432
+ assert_file "app/models/user.rb"
433
+ assert_file "app/models/concerns/authenticatable.rb"
434
+ assert_file "app/controllers/auth/tokens_controller.rb"
435
+ assert_file "config/initializers/propel_access.rb"
436
+
437
+ # Verify JWT routes are accessible
438
+ assert_file "config/routes.rb" do |content|
439
+ assert_match(/post 'login'/, content)
440
+ assert_match(/delete 'logout'/, content)
441
+ assert_match(/get 'me'/, content)
442
+ end
443
+
444
+ # Verify required gems are added
445
+ assert_file "Gemfile" do |content|
446
+ assert_match(/gem ['"]jwt['"]/, content)
447
+ assert_match(/gem ['"]bcrypt['"]/, content)
448
+ end
449
+
450
+ # Verify the system would work for a JavaScript frontend
451
+ controller_code = File.read(File.join(destination_root, "app/controllers/auth/tokens_controller.rb"))
452
+ assert_match(/render json:/, controller_code)
453
+ assert_match(/include PropelAuthentication/, controller_code)
454
+
455
+ # Verify JWT token functionality exists
456
+ authenticatable_code = File.read(File.join(destination_root, "app/models/concerns/authenticatable.rb"))
457
+ assert_match(/JWT\.encode/, authenticatable_code)
458
+ assert_match(/JWT\.decode/, authenticatable_code)
459
+ end
460
+
461
+ # test "generator does not create RackSessionDisable concern or include it in controller for API-only apps" do
462
+ # original_api_only = Rails.application.config.api_only
463
+ # Rails.application.config.api_only = true
464
+ # begin
465
+ # run_generator
466
+
467
+ # assert_no_file "app/controllers/concerns/rack_session_disable.rb"
468
+ # assert_file "app/controllers/auth/tokens_controller.rb" do |content|
469
+ # assert_no_match(/include RackSessionDisable/, content)
470
+ # end
471
+ # ensure
472
+ # Rails.application.config.api_only = original_api_only
473
+ # end
474
+ # end
475
+
476
+ # test "generator creates RackSessionDisable concern and includes it in controller for full stack apps" do
477
+ # original_api_only = Rails.application.config.api_only
478
+ # Rails.application.config.api_only = false
479
+ # begin
480
+ # run_generator
481
+
482
+ # assert_file "app/controllers/concerns/rack_session_disable.rb"
483
+ # assert_file "app/controllers/auth/tokens_controller.rb" do |content|
484
+ # assert_match(/include RackSessionDisable/, content)
485
+ # end
486
+ # ensure
487
+ # Rails.application.config.api_only = original_api_only
488
+ # end
489
+ # end
490
+ end