propel_authentication 0.2.1 → 0.3.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 (34) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +60 -0
  3. data/lib/generators/propel_authentication/install_generator.rb +190 -228
  4. data/lib/generators/propel_authentication/templates/auth/passwords_controller.rb.tt +10 -13
  5. data/lib/generators/propel_authentication/templates/auth/signup_controller.rb.tt +6 -9
  6. data/lib/generators/propel_authentication/templates/auth/tokens_controller.rb.tt +4 -7
  7. data/lib/generators/propel_authentication/templates/core/configuration_methods.rb +38 -1
  8. data/lib/generators/propel_authentication/templates/db/migrate/create_agencies.rb +4 -2
  9. data/lib/generators/propel_authentication/templates/db/migrate/create_agents.rb +6 -1
  10. data/lib/generators/propel_authentication/templates/db/migrate/create_organizations.rb +4 -2
  11. data/lib/generators/propel_authentication/templates/db/migrate/create_users.rb +4 -2
  12. data/lib/generators/propel_authentication/templates/db/propel_seeds.rb.tt +145 -0
  13. data/lib/generators/propel_authentication/templates/doc/signup_flow.md +9 -4
  14. data/lib/generators/propel_authentication/templates/fixtures/agencies.yml.erb +23 -0
  15. data/lib/generators/propel_authentication/templates/fixtures/agents.yml.erb +71 -0
  16. data/lib/generators/propel_authentication/templates/fixtures/invitations.yml.erb +9 -0
  17. data/lib/generators/propel_authentication/templates/fixtures/organizations.yml.erb +21 -0
  18. data/lib/generators/propel_authentication/templates/fixtures/users.yml.erb +77 -0
  19. data/lib/generators/propel_authentication/templates/models/agency.rb.tt +8 -2
  20. data/lib/generators/propel_authentication/templates/models/agent.rb.tt +13 -2
  21. data/lib/generators/propel_authentication/templates/models/invitation.rb.tt +3 -1
  22. data/lib/generators/propel_authentication/templates/models/organization.rb.tt +15 -2
  23. data/lib/generators/propel_authentication/templates/models/user.rb.tt +18 -2
  24. data/lib/generators/propel_authentication/templates/propel_authentication.rb.tt +33 -6
  25. data/lib/generators/propel_authentication/templates/test/controllers/auth/lockable_integration_test.rb.tt +2 -2
  26. data/lib/generators/propel_authentication/templates/test/controllers/auth/password_reset_integration_test.rb.tt +9 -9
  27. data/lib/generators/propel_authentication/templates/test/controllers/auth/signup_controller_test.rb.tt +6 -6
  28. data/lib/generators/propel_authentication/templates/test/controllers/auth/tokens_controller_test.rb.tt +4 -4
  29. data/lib/generators/propel_authentication/test/generators/authentication/controllers/tokens_controller_test.rb +1 -1
  30. data/lib/generators/propel_authentication/test/generators/authentication/install_generator_test.rb +1 -1
  31. data/lib/generators/propel_authentication/test/integration/multi_version_generator_test.rb +12 -13
  32. data/lib/propel_authentication.rb +1 -1
  33. metadata +30 -5
  34. data/lib/generators/propel_authentication/templates/db/seeds.rb +0 -75
@@ -44,7 +44,9 @@ module PropelAuthentication
44
44
  :support_email,
45
45
  :enable_email_notifications,
46
46
  :enable_sms_notifications,
47
- :agency_tenancy,
47
+ :tenancy_enabled,
48
+ :organization_required,
49
+ :agency_required,
48
50
  :require_organization_id,
49
51
  :require_user_id,
50
52
  :namespace,
@@ -105,9 +107,13 @@ module PropelAuthentication
105
107
  @enable_email_notifications = true
106
108
  @enable_sms_notifications = false # Requires SMS provider configuration
107
109
 
108
- # Tenancy configuration
109
- # Controls whether multi-agency tenancy is enabled within organizations
110
- @agency_tenancy = true # Enable agency-level tenancy by default
110
+ # Tenancy configuration - hierarchical structure
111
+ # Master switch: enables/disables entire multi-tenancy system
112
+ @tenancy_enabled = <%= tenancy_enabled? %> # <%= tenancy_enabled? ? 'Enable' : 'Disable' %> multi-tenancy system
113
+
114
+ # Individual tenancy model requirements (only effective when tenancy_enabled = true)
115
+ @organization_required = <%= organization_required? %> # <%= organization_required? ? 'Require' : 'Optional' %> organization association
116
+ @agency_required = <%= agency_required? %> # <%= agency_required? ? 'Enable' : 'Disable' %> agency-level tenancy
111
117
 
112
118
  # API tenancy requirements (for PropelApi integration)
113
119
  # Controls whether APIs require explicit tenancy context in requests
@@ -120,6 +126,19 @@ module PropelAuthentication
120
126
  @version = nil
121
127
  @auth_scope = nil
122
128
  end
129
+
130
+ # Helper methods for template conditionals
131
+ def tenancy_enabled?
132
+ @tenancy_enabled
133
+ end
134
+
135
+ def organization_required?
136
+ @tenancy_enabled && @organization_required
137
+ end
138
+
139
+ def agency_required?
140
+ @tenancy_enabled && @agency_required
141
+ end
123
142
  end
124
143
  end
125
144
 
@@ -141,11 +160,19 @@ PropelAuthentication.configure do |config|
141
160
  # User registration settings
142
161
  config.allow_registration = true
143
162
 
144
- # Multi-tenancy settings
163
+ # Multi-tenancy settings - hierarchical configuration
164
+ # Master switch: enables/disables entire multi-tenancy system
165
+ # When false: Single-tenant mode (no tenancy models required)
166
+ config.tenancy_enabled = <%= tenancy_enabled? %>
167
+
168
+ # Individual tenancy model requirements (only effective when tenancy_enabled = true)
169
+ # Controls whether users must belong to organizations
170
+ config.organization_required = <%= organization_required? %>
171
+
145
172
  # Controls whether agency-level tenancy is enabled within organizations
146
173
  # When true: Organization -> Agency -> Resources structure
147
174
  # When false: Organization -> Resources structure (simpler)
148
- config.agency_tenancy = true
175
+ config.agency_required = <%= agency_required? %>
149
176
 
150
177
  # API tenancy requirements (for PropelApi integration)
151
178
  # Controls whether clients must provide explicit tenancy context in API requests
@@ -11,7 +11,7 @@ class <%= controller_namespace('tokens') %>LockableIntegrationTest < ActionDispa
11
11
  organization: @organization
12
12
  )
13
13
  # Create agent association for agency access (required for real-time agency lookup)
14
- Agent.create!(user: @user, agency: @agency, role: "member")
14
+ Agent.create!(user: @user, agency: @agency, organization: @organization, role: "member")
15
15
  end
16
16
 
17
17
  # CRITICAL: Real API integration with lockable functionality
@@ -148,7 +148,7 @@ class <%= controller_namespace('tokens') %>LockableIntegrationTest < ActionDispa
148
148
 
149
149
  post '<%= auth_route_prefix %>/unlock', params: {}
150
150
 
151
- assert_response :unprocessable_entity, "Missing token should return 422"
151
+ assert_response :unprocessable_content, "Missing token should return 422"
152
152
 
153
153
  json = JSON.parse(response.body)
154
154
  assert_match(/token.*required/i, json['error'], "Should indicate token is required")
@@ -13,7 +13,7 @@ class <%= controller_namespace('passwords') %>PasswordResetIntegrationTest < Act
13
13
  last_name: "User"
14
14
  )
15
15
  # Create agent association for agency access (required for real-time agency lookup)
16
- Agent.create!(user: @user, agency: @agency, role: "member")
16
+ Agent.create!(user: @user, agency: @agency, organization: @organization, role: "member")
17
17
  end
18
18
 
19
19
  # POST <%= auth_route_prefix %>/reset - Request Password Reset Tests
@@ -39,7 +39,7 @@ class <%= controller_namespace('passwords') %>PasswordResetIntegrationTest < Act
39
39
  post '<%= auth_route_prefix %>/reset', params: { email_address: "invalid-email-format" }
40
40
 
41
41
  # VERIFY VALIDATION ERROR: Should return validation error
42
- assert_response :unprocessable_entity, "Invalid email_address format should be rejected"
42
+ assert_response :unprocessable_content, "Invalid email_address format should be rejected"
43
43
 
44
44
  response_json = JSON.parse(response.body)
45
45
  assert_match(/email.*invalid/i, response_json['error'], "Should indicate email_address format error")
@@ -65,7 +65,7 @@ class <%= controller_namespace('passwords') %>PasswordResetIntegrationTest < Act
65
65
  post '<%= auth_route_prefix %>/reset', params: {}
66
66
 
67
67
  # VERIFY PARAMETER VALIDATION: Should return parameter error
68
- assert_response :unprocessable_entity, "Missing email_address should return validation error"
68
+ assert_response :unprocessable_content, "Missing email_address should return validation error"
69
69
 
70
70
  response_json = JSON.parse(response.body)
71
71
  assert_match(/email.*required/i, response_json['error'], "Should indicate email_address is required")
@@ -181,7 +181,7 @@ class <%= controller_namespace('passwords') %>PasswordResetIntegrationTest < Act
181
181
  }
182
182
 
183
183
  # VERIFY VALIDATION: Should return validation error
184
- assert_response :unprocessable_entity, "Mismatched passwords should be rejected"
184
+ assert_response :unprocessable_content, "Mismatched passwords should be rejected"
185
185
 
186
186
  response_json = JSON.parse(response.body)
187
187
  assert_match(/password.*confirmation.*match/i, response_json['error'], "Should indicate password mismatch")
@@ -205,7 +205,7 @@ class <%= controller_namespace('passwords') %>PasswordResetIntegrationTest < Act
205
205
  }
206
206
 
207
207
  # VERIFY VALIDATION: Should return validation error
208
- assert_response :unprocessable_entity, "Weak password should be rejected"
208
+ assert_response :unprocessable_content, "Weak password should be rejected"
209
209
 
210
210
  response_json = JSON.parse(response.body)
211
211
  assert_match(/password.*too short/i, response_json['error'], "Should indicate password strength issue")
@@ -226,7 +226,7 @@ class <%= controller_namespace('passwords') %>PasswordResetIntegrationTest < Act
226
226
  password_confirmation: "newpassword123"
227
227
  }
228
228
 
229
- assert_response :unprocessable_entity, "Missing token should be rejected"
229
+ assert_response :unprocessable_content, "Missing token should be rejected"
230
230
 
231
231
  # TEST MISSING PASSWORD
232
232
  patch '<%= auth_route_prefix %>/reset', params: {
@@ -234,7 +234,7 @@ class <%= controller_namespace('passwords') %>PasswordResetIntegrationTest < Act
234
234
  password_confirmation: "newpassword123"
235
235
  }
236
236
 
237
- assert_response :unprocessable_entity, "Missing password should be rejected"
237
+ assert_response :unprocessable_content, "Missing password should be rejected"
238
238
 
239
239
  # TEST MISSING CONFIRMATION
240
240
  patch '<%= auth_route_prefix %>/reset', params: {
@@ -242,7 +242,7 @@ class <%= controller_namespace('passwords') %>PasswordResetIntegrationTest < Act
242
242
  password: "newpassword123"
243
243
  }
244
244
 
245
- assert_response :unprocessable_entity, "Missing password confirmation should be rejected"
245
+ assert_response :unprocessable_content, "Missing password confirmation should be rejected"
246
246
  end
247
247
 
248
248
  test "should clear failed login attempts on successful password reset" do
@@ -356,7 +356,7 @@ class <%= controller_namespace('passwords') %>PasswordResetIntegrationTest < Act
356
356
  get '<%= auth_route_prefix %>/reset', params: {}
357
357
 
358
358
  # VERIFY PARAMETER VALIDATION: Should return parameter error
359
- assert_response :unprocessable_entity, "Missing token should return validation error"
359
+ assert_response :unprocessable_content, "Missing token should return validation error"
360
360
 
361
361
  response_json = JSON.parse(response.body)
362
362
  assert_match(/token.*required/i, response_json['error'], "Should indicate token is required")
@@ -111,7 +111,7 @@ class <%= controller_namespace('signup') %>SignupControllerTest < ActionDispatch
111
111
  params: invalid_params,
112
112
  as: :json
113
113
 
114
- assert_response :unprocessable_entity
114
+ assert_response :unprocessable_content
115
115
  response_body = JSON.parse(response.body)
116
116
  assert_includes response_body.keys, 'error'
117
117
  end
@@ -123,7 +123,7 @@ class <%= controller_namespace('signup') %>SignupControllerTest < ActionDispatch
123
123
  params: invalid_params,
124
124
  as: :json
125
125
 
126
- assert_response :unprocessable_entity
126
+ assert_response :unprocessable_content
127
127
  response_body = JSON.parse(response.body)
128
128
  assert_includes response_body.keys, 'error'
129
129
  end
@@ -136,7 +136,7 @@ class <%= controller_namespace('signup') %>SignupControllerTest < ActionDispatch
136
136
  params: invalid_params,
137
137
  as: :json
138
138
 
139
- assert_response :unprocessable_entity
139
+ assert_response :unprocessable_content
140
140
  response_body = JSON.parse(response.body)
141
141
  assert_includes response_body.keys, 'details'
142
142
  assert_includes response_body['details'].keys, 'email_address'
@@ -150,7 +150,7 @@ class <%= controller_namespace('signup') %>SignupControllerTest < ActionDispatch
150
150
  params: invalid_params,
151
151
  as: :json
152
152
 
153
- assert_response :unprocessable_entity
153
+ assert_response :unprocessable_content
154
154
  response_body = JSON.parse(response.body)
155
155
  assert_includes response_body.keys, 'details'
156
156
  end
@@ -172,7 +172,7 @@ class <%= controller_namespace('signup') %>SignupControllerTest < ActionDispatch
172
172
  params: duplicate_params,
173
173
  as: :json
174
174
 
175
- assert_response :unprocessable_entity
175
+ assert_response :unprocessable_content
176
176
  response_body = JSON.parse(response.body)
177
177
  assert_includes response_body.keys, 'details'
178
178
  assert_includes response_body['details'].keys, 'email_address'
@@ -186,7 +186,7 @@ class <%= controller_namespace('signup') %>SignupControllerTest < ActionDispatch
186
186
  params: @valid_signup_params,
187
187
  as: :json
188
188
 
189
- assert_response :unprocessable_entity
189
+ assert_response :unprocessable_content
190
190
  response_body = JSON.parse(response.body)
191
191
  assert_equal 'MISSING_AGENCY_DATA', response_body['code']
192
192
  end
@@ -51,9 +51,9 @@ class <%= controller_namespace('tokens') %>TokensControllerTest < ActionDispatch
51
51
  @agency = Agency.create!(name: "Main Agency", organization: @organization)
52
52
  @rival_agency = Agency.create!(name: "Rival Agency", organization: @rival_organization)
53
53
 
54
- Agent.create!(user: @valid_user, agency: @agency, role: "member")
55
- Agent.create!(user: @other_user, agency: @rival_agency, role: "member")
56
- Agent.create!(user: @inactive_user, agency: @agency, role: "member")
54
+ Agent.create!(user: @valid_user, agency: @agency, organization: @organization, role: "member")
55
+ Agent.create!(user: @other_user, agency: @rival_agency, organization: @rival_organization, role: "member")
56
+ Agent.create!(user: @inactive_user, agency: @agency, organization: @organization, role: "member")
57
57
  end
58
58
 
59
59
  test "POST <%= auth_route_prefix %>/login with valid credentials returns JWT token and user data" do
@@ -232,7 +232,7 @@ class <%= controller_namespace('tokens') %>TokensControllerTest < ActionDispatch
232
232
  }
233
233
 
234
234
  # VERIFY PARAMETER VALIDATION: Check required field enforcement
235
- assert_response :unprocessable_entity, "Missing required parameters should return unprocessable_entity"
235
+ assert_response :bad_request, "Missing required parameters should return bad_request"
236
236
  response_json = JSON.parse(response.body)
237
237
  assert response_json["error"].present?, "Should return validation error message"
238
238
  end
@@ -72,7 +72,7 @@ class TokensControllerTest < ActionDispatch::IntegrationTest
72
72
  }
73
73
  }, as: :json
74
74
 
75
- assert_response :unprocessable_entity
75
+ assert_response :unprocessable_content
76
76
 
77
77
  response_body = JSON.parse(response.body)
78
78
  assert response_body.key?("error"), "Should return error message"
@@ -151,7 +151,7 @@ class Propel::Authentication::InstallGeneratorTest < Rails::Generators::TestCase
151
151
  assert_match(/t\.references :organization/, migration)
152
152
 
153
153
  # JSON fields for flexible data storage
154
- assert_match(/t\.jsonb :meta, default: {}/, migration)
154
+ assert_match(/t\.jsonb :metadata, default: {}/, migration)
155
155
  assert_match(/t\.jsonb :settings, default: {}/, migration)
156
156
 
157
157
  # Proper indexes for JWT authentication performance
@@ -16,32 +16,31 @@ class MultiVersionGeneratorTest < Rails::Generators::TestCase
16
16
  super
17
17
  end
18
18
 
19
- test "generator creates dynamic controller structure with proper namespace configuration" do
20
- run_generator ["--namespace=api", "--version=v1"]
19
+ test "generator creates dynamic API versioning structure" do
20
+ run_generator ["--configurable-versioning"]
21
21
 
22
- # Verify direct controllers are created with proper class names (no inheritance)
23
- assert_file "app/controllers/api/v1/tokens_controller.rb" do |content|
24
- assert_match(/class Api::V1::TokensController < ApplicationController/, content)
25
- assert_match(/POST \/api\/v1\/login/, content)
22
+ # Verify base controllers are created
23
+ assert_file "app/controllers/api/auth/base_tokens_controller.rb" do |content|
24
+ assert_match(/class Api::Auth::BaseTokensController < ApplicationController/, content)
25
+ assert_match(/POST \/api\/\{version\}\/auth\/login/, content)
26
26
  assert_match(/def create/, content)
27
27
  assert_match(/def show/, content)
28
28
  assert_match(/def destroy/, content)
29
29
  assert_match(/def unlock/, content)
30
30
  end
31
31
 
32
- assert_file "app/controllers/api/v1/passwords_controller.rb" do |content|
33
- assert_match(/class Api::V1::PasswordsController < ApplicationController/, content)
34
- assert_match(/POST \/api\/v1\/reset/, content)
32
+ assert_file "app/controllers/api/auth/base_passwords_controller.rb" do |content|
33
+ assert_match(/class Api::Auth::BasePasswordsController < ApplicationController/, content)
34
+ assert_match(/POST \/api\/\{version\}\/auth\/reset/, content)
35
35
  assert_match(/def create/, content)
36
36
  assert_match(/def show/, content)
37
37
  assert_match(/def update/, content)
38
38
  end
39
39
 
40
- # Verify routes are created with proper namespacing and as: options
40
+ # Verify runtime configurable routes are created
41
41
  assert_file "config/routes.rb" do |content|
42
- assert_match(/namespace :api/, content)
43
- assert_match(/namespace :v1/, content)
44
- assert_match(/post 'login', to: 'tokens#create', as: :login/, content)
42
+ assert_match(/# Runtime configurable API versioning routes/, content)
43
+ assert_match(/api_version = PropelAccess\.configuration\.api_version/, content)
45
44
  assert_match(/namespace :api do/, content)
46
45
  assert_match(/namespace api_version do/, content)
47
46
  assert_match(/namespace :auth do/, content)
@@ -1,3 +1,3 @@
1
1
  module PropelAuthentication
2
- VERSION = "0.2.1"
2
+ VERSION = "0.3.1"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: propel_authentication
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.1
4
+ version: 0.3.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Propel Team
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2025-09-02 00:00:00.000000000 Z
11
+ date: 2025-09-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -50,14 +50,34 @@ dependencies:
50
50
  requirements:
51
51
  - - "~>"
52
52
  - !ruby/object:Gem::Version
53
- version: '2.7'
53
+ version: 3.1.2
54
54
  type: :runtime
55
55
  prerelease: false
56
56
  version_requirements: !ruby/object:Gem::Requirement
57
57
  requirements:
58
58
  - - "~>"
59
59
  - !ruby/object:Gem::Version
60
- version: '2.7'
60
+ version: 3.1.2
61
+ - !ruby/object:Gem::Dependency
62
+ name: noticed
63
+ requirement: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - ">="
66
+ - !ruby/object:Gem::Version
67
+ version: 2.0.0
68
+ - - "<"
69
+ - !ruby/object:Gem::Version
70
+ version: '3.0'
71
+ type: :runtime
72
+ prerelease: false
73
+ version_requirements: !ruby/object:Gem::Requirement
74
+ requirements:
75
+ - - ">="
76
+ - !ruby/object:Gem::Version
77
+ version: 2.0.0
78
+ - - "<"
79
+ - !ruby/object:Gem::Version
80
+ version: '3.0'
61
81
  description: A self-extracting generator that creates a JWT-based authentication system
62
82
  with agencies, agents, password resets, email confirmation, and account locking
63
83
  for Rails applications.
@@ -90,8 +110,13 @@ files:
90
110
  - lib/generators/propel_authentication/templates/db/migrate/create_invitations.rb
91
111
  - lib/generators/propel_authentication/templates/db/migrate/create_organizations.rb
92
112
  - lib/generators/propel_authentication/templates/db/migrate/create_users.rb
93
- - lib/generators/propel_authentication/templates/db/seeds.rb
113
+ - lib/generators/propel_authentication/templates/db/propel_seeds.rb.tt
94
114
  - lib/generators/propel_authentication/templates/doc/signup_flow.md
115
+ - lib/generators/propel_authentication/templates/fixtures/agencies.yml.erb
116
+ - lib/generators/propel_authentication/templates/fixtures/agents.yml.erb
117
+ - lib/generators/propel_authentication/templates/fixtures/invitations.yml.erb
118
+ - lib/generators/propel_authentication/templates/fixtures/organizations.yml.erb
119
+ - lib/generators/propel_authentication/templates/fixtures/users.yml.erb
95
120
  - lib/generators/propel_authentication/templates/models/agency.rb.tt
96
121
  - lib/generators/propel_authentication/templates/models/agent.rb.tt
97
122
  - lib/generators/propel_authentication/templates/models/invitation.rb.tt
@@ -1,75 +0,0 @@
1
- # This file should ensure the existence of records required to run the application in every environment (production,
2
- # development, test). The code here should be idempotent so that it can be executed at any point in every environment.
3
- # The data can then be loaded with the bin/rails db:seed command (or created alongside the database with db:setup).
4
- #
5
- # Example:
6
- #
7
- # ["Action", "Comedy", "Drama", "Horror"].each do |genre_name|
8
- # MovieGenre.find_or_create_by!(name: genre_name)
9
- # end
10
-
11
- # Create comprehensive tenancy structure for authentication testing
12
- puts "🏢 Creating test organization structure..."
13
-
14
- organization = Organization.find_or_create_by!(name: 'Test Organization') do |org|
15
- org.website = 'https://test.example.com'
16
- org.time_zone = 'UTC'
17
- end
18
-
19
- # Create agencies for proper tenancy structure
20
- main_agency = Agency.find_or_create_by!(name: 'Main Department', organization: organization) do |agency|
21
- agency.phone_number = '+1-555-0101'
22
- agency.address = '123 Main Street'
23
- end
24
-
25
- support_agency = Agency.find_or_create_by!(name: 'Support Department', organization: organization) do |agency|
26
- agency.phone_number = '+1-555-0102'
27
- agency.address = '123 Support Street'
28
- end
29
-
30
- # Create test users with proper confirmations
31
- user = User.find_or_create_by!(email_address: 'test@example.com') do |u|
32
- u.username = 'testuser'
33
- u.email_address = 'test@example.com'
34
- u.password = 'password123'
35
- u.password_confirmation = 'password123'
36
- u.organization = organization
37
- u.first_name = 'Test'
38
- u.last_name = 'User'
39
- u.confirmed_at = 1.week.ago # Confirmed user for easier testing
40
- end
41
-
42
- admin_user = User.find_or_create_by!(email_address: 'admin@example.com') do |u|
43
- u.username = 'adminuser'
44
- u.email_address = 'admin@example.com'
45
- u.password = 'password123'
46
- u.password_confirmation = 'password123'
47
- u.organization = organization
48
- u.first_name = 'Admin'
49
- u.last_name = 'User'
50
- u.confirmed_at = 1.week.ago # Confirmed admin for easier testing
51
- end
52
-
53
- # Create agent associations for agency access (CRITICAL for tenancy validation)
54
- Agent.find_or_create_by!(user: user, agency: main_agency) do |agent|
55
- agent.role = 'member'
56
- end
57
-
58
- Agent.find_or_create_by!(user: admin_user, agency: main_agency) do |agent|
59
- agent.role = 'manager'
60
- end
61
-
62
- Agent.find_or_create_by!(user: admin_user, agency: support_agency) do |agent|
63
- agent.role = 'admin'
64
- end
65
-
66
- puts "✅ Created test organization: #{organization.name}"
67
- puts "✅ Created agencies: #{Agency.where(organization: organization).pluck(:name).join(', ')}"
68
- puts "✅ Created test user: #{user.email_address} (password: password123)"
69
- puts "✅ Created admin user: #{admin_user.email_address} (password: password123)"
70
- puts "✅ Created agent associations for proper tenancy access"
71
- puts ""
72
- puts "🎯 Test user agency access: #{user.agency_ids}"
73
- puts "🎯 Admin user agency access: #{admin_user.agency_ids}"
74
- puts ""
75
- puts "🚀 You can now test login with POST /api/v1/login"