@@ -109,3 +109,344 @@ def test_create_role_unauthenticated(unauth_client, test_organization):
109
109
)
110
110
111
111
assert response .status_code == 303
112
+
113
+
114
+ @pytest .fixture
115
+ def editor_user (session : Session , test_user : User , test_organization ):
116
+ """
117
+ Creates a user who has EDIT_ROLE permission assigned via a role in the
118
+ specified organization.
119
+ """
120
+ editor_role = Role (
121
+ name = "Editor Role" ,
122
+ organization_id = test_organization .id
123
+ )
124
+ edit_permission = session .exec (
125
+ select (Permission ).where (Permission .name == ValidPermissions .EDIT_ROLE )
126
+ ).first ()
127
+ if not edit_permission :
128
+ raise ValueError ("EDIT_ROLE permission not found in the Permission table. Check seeds/setup." )
129
+
130
+ editor_role .permissions .append (edit_permission )
131
+ session .add (editor_role )
132
+
133
+ # Assign the newly created 'Editor Role' to our test user
134
+ test_user .roles .append (editor_role )
135
+ session .commit ()
136
+ return test_user
137
+
138
+
139
+ def test_update_role_success (auth_client , editor_user , test_organization , session : Session ):
140
+ """
141
+ Test successfully updating a role's name and permissions.
142
+ Ensures a user with EDIT_ROLE permission can update the role.
143
+ """
144
+ # Create a role we will update
145
+ existing_role = Role (
146
+ name = "Old Role Name" ,
147
+ organization_id = test_organization .id
148
+ )
149
+ session .add (existing_role )
150
+ session .commit ()
151
+ session .refresh (existing_role )
152
+
153
+ # Add an existing permission to the role so we can test it being removed
154
+ perm_create = session .exec (
155
+ select (Permission ).where (Permission .name == ValidPermissions .CREATE_ROLE )
156
+ ).first ()
157
+ existing_role .permissions .append (perm_create )
158
+ session .commit ()
159
+
160
+ # Verify setup
161
+ assert existing_role .id is not None
162
+ original_id = existing_role .id
163
+
164
+ # Update the role using the /roles/update endpoint
165
+ response = auth_client .post (
166
+ "/roles/update" ,
167
+ data = {
168
+ "id" : existing_role .id ,
169
+ "name" : "New Role Name" ,
170
+ "organization_id" : test_organization .id ,
171
+ "permissions" : [ValidPermissions .EDIT_ROLE .value ] # remove CREATE_ROLE, add EDIT_ROLE
172
+ },
173
+ follow_redirects = False
174
+ )
175
+
176
+ assert response .status_code == 303
177
+
178
+ # Check that the role was updated in the database
179
+ updated_role = session .exec (
180
+ select (Role ).where (Role .id == original_id )
181
+ ).first ()
182
+ assert updated_role is not None
183
+ assert updated_role .name == "New Role Name"
184
+ perm_names = [p .name for p in updated_role .permissions ]
185
+ assert ValidPermissions .CREATE_ROLE not in perm_names
186
+ assert ValidPermissions .EDIT_ROLE in perm_names
187
+
188
+
189
+ def test_update_role_unauthorized (auth_client , test_user , test_organization , session : Session ):
190
+ """
191
+ Test that a user without EDIT_ROLE permission cannot update a role.
192
+ A 403 (InsufficientPermissionsError) is expected.
193
+ """
194
+ # Create a role in the same organization that we try to update
195
+ some_role = Role (
196
+ name = "Role Without Permission" ,
197
+ organization_id = test_organization .id
198
+ )
199
+ session .add (some_role )
200
+ session .commit ()
201
+ session .refresh (some_role )
202
+
203
+ response = auth_client .post (
204
+ "/roles/update" ,
205
+ data = {
206
+ "id" : some_role .id ,
207
+ "name" : "Attempted Update" ,
208
+ "organization_id" : test_organization .id ,
209
+ "permissions" : [ValidPermissions .EDIT_ROLE .value ]
210
+ },
211
+ follow_redirects = True
212
+ )
213
+ # Because the user has no EDIT_ROLE permission, the endpoint should raise 403
214
+ assert response .status_code == 403
215
+
216
+
217
+ def test_update_role_nonexistent (auth_client , editor_user , test_organization ):
218
+ """
219
+ Test attempting to update a role that does not exist.
220
+ A 404 (RoleNotFoundError) is expected.
221
+ """
222
+ response = auth_client .post (
223
+ "/roles/update" ,
224
+ data = {
225
+ "id" : 9999999 , # A role ID that doesn't exist
226
+ "name" : "Nonexistent Role" ,
227
+ "organization_id" : test_organization .id ,
228
+ "permissions" : [ValidPermissions .EDIT_ROLE .value ]
229
+ },
230
+ follow_redirects = True
231
+ )
232
+ assert response .status_code == 404
233
+
234
+
235
+ def test_update_role_duplicate_name (auth_client , editor_user , test_organization , session : Session ):
236
+ """
237
+ Test that updating a role to a name that already exists in the same organization
238
+ fails with 400 (RoleAlreadyExistsError).
239
+ """
240
+ # Create two roles in the same organization
241
+ role1 = Role (name = "Original Role" , organization_id = test_organization .id )
242
+ role2 = Role (name = "Conflict Role" , organization_id = test_organization .id )
243
+ session .add (role1 )
244
+ session .add (role2 )
245
+ session .commit ()
246
+
247
+ # Try to update 'role1' to have the same name as 'role2'
248
+ response = auth_client .post (
249
+ "/roles/update" ,
250
+ data = {
251
+ "id" : role1 .id ,
252
+ "name" : "Conflict Role" ,
253
+ "organization_id" : test_organization .id ,
254
+ "permissions" : [ValidPermissions .EDIT_ROLE .value ]
255
+ },
256
+ follow_redirects = True
257
+ )
258
+
259
+ assert response .status_code == 400
260
+
261
+
262
+ def test_update_role_invalid_permission (auth_client , editor_user , test_organization , session : Session ):
263
+ """
264
+ Test attempting to update a role with an invalid permission
265
+ that is not in the ValidPermissions enum. Expects a 400 status.
266
+ """
267
+ role_to_update = Role (
268
+ name = "Role With Bad Permission" ,
269
+ organization_id = test_organization .id
270
+ )
271
+ session .add (role_to_update )
272
+ session .commit ()
273
+ session .refresh (role_to_update )
274
+
275
+ # Provide an invalid permission string
276
+ response = auth_client .post (
277
+ "/roles/update" ,
278
+ data = {
279
+ "id" : role_to_update .id ,
280
+ "name" : "Invalid Permission Test" ,
281
+ "organization_id" : test_organization .id ,
282
+ "permissions" : ["NOT_A_VALID_PERMISSION" ]
283
+ },
284
+ follow_redirects = True
285
+ )
286
+
287
+ assert response .status_code == 422
288
+
289
+
290
+ def test_update_role_unauthenticated (unauth_client , test_organization , session : Session ):
291
+ """
292
+ Test that an unauthenticated user (no valid tokens) will not have access
293
+ to update a role. By default, the router requires login, so it should
294
+ redirect.
295
+ """
296
+ # Create a role
297
+ some_role = Role (
298
+ name = "Role For Unauth Test" ,
299
+ organization_id = test_organization .id
300
+ )
301
+ session .add (some_role )
302
+ session .commit ()
303
+ session .refresh (some_role )
304
+
305
+ response = unauth_client .post (
306
+ "/roles/update" ,
307
+ data = {
308
+ "id" : some_role .id ,
309
+ "name" : "Should Not Succeed" ,
310
+ "organization_id" : test_organization .id ,
311
+ "permissions" : [ValidPermissions .EDIT_ROLE .value ]
312
+ },
313
+ follow_redirects = False
314
+ )
315
+ assert response .status_code == 303
316
+
317
+
318
+ @pytest .fixture
319
+ def delete_role_user (session : Session , test_user : User , test_organization ):
320
+ """Create a user with DELETE_ROLE permission"""
321
+ delete_role = Role (
322
+ name = "Delete Role Permission" ,
323
+ organization_id = test_organization .id
324
+ )
325
+
326
+ delete_permission : Permission | None = session .exec (
327
+ select (Permission ).where (Permission .name == ValidPermissions .DELETE_ROLE )
328
+ ).first ()
329
+
330
+ if delete_permission is None :
331
+ raise ValueError ("Error during test setup: DELETE_ROLE permission not found" )
332
+
333
+ delete_role .permissions .append (delete_permission )
334
+ session .add (delete_role )
335
+
336
+ test_user .roles .append (delete_role )
337
+ session .commit ()
338
+
339
+ return test_user
340
+
341
+
342
+ def test_delete_role_success (auth_client , delete_role_user , test_organization , session : Session ):
343
+ """Test successful role deletion"""
344
+ # Create a role to delete
345
+ role_to_delete = Role (
346
+ name = "Role To Delete" ,
347
+ organization_id = test_organization .id
348
+ )
349
+ session .add (role_to_delete )
350
+ session .commit ()
351
+ session .refresh (role_to_delete )
352
+
353
+ response = auth_client .post (
354
+ "/roles/delete" ,
355
+ data = {
356
+ "id" : role_to_delete .id ,
357
+ "organization_id" : test_organization .id
358
+ },
359
+ follow_redirects = False
360
+ )
361
+
362
+ assert response .status_code == 303
363
+
364
+ # Verify role was deleted from database
365
+ deleted_role = session .exec (
366
+ select (Role ).where (Role .id == role_to_delete .id )
367
+ ).first ()
368
+ assert deleted_role is None
369
+
370
+
371
+ def test_delete_role_unauthorized (auth_client , test_user , test_organization , session : Session ):
372
+ """Test role deletion without proper permissions"""
373
+ # Create a role to attempt to delete
374
+ role = Role (
375
+ name = "Unauthorized Delete" ,
376
+ organization_id = test_organization .id
377
+ )
378
+ session .add (role )
379
+ session .commit ()
380
+
381
+ response = auth_client .post (
382
+ "/roles/delete" ,
383
+ data = {
384
+ "id" : role .id ,
385
+ "organization_id" : test_organization .id
386
+ },
387
+ follow_redirects = False
388
+ )
389
+
390
+ assert response .status_code == 403
391
+
392
+
393
+ def test_delete_nonexistent_role (auth_client , delete_role_user , test_organization ):
394
+ """Test attempting to delete a role that doesn't exist"""
395
+ response = auth_client .post (
396
+ "/roles/delete" ,
397
+ data = {
398
+ "id" : 99999 , # Non-existent role ID
399
+ "organization_id" : test_organization .id
400
+ },
401
+ follow_redirects = False
402
+ )
403
+
404
+ assert response .status_code == 404
405
+
406
+
407
+ def test_delete_role_with_users (auth_client , delete_role_user , test_organization , session : Session ):
408
+ """Test attempting to delete a role that has users assigned"""
409
+ # Create a role and assign it to a user
410
+ role_with_users = Role (
411
+ name = "Role With Users" ,
412
+ organization_id = test_organization .id
413
+ )
414
+ session .add (role_with_users )
415
+ session .commit ()
416
+
417
+ # Assign the role to our test user
418
+ delete_role_user .roles .append (role_with_users )
419
+ session .commit ()
420
+
421
+ response = auth_client .post (
422
+ "/roles/delete" ,
423
+ data = {
424
+ "id" : role_with_users .id ,
425
+ "organization_id" : test_organization .id
426
+ },
427
+ follow_redirects = False
428
+ )
429
+
430
+ assert response .status_code == 400
431
+
432
+
433
+ def test_delete_role_unauthenticated (unauth_client , test_organization , session : Session ):
434
+ """Test role deletion without authentication"""
435
+ # Create a role to attempt to delete
436
+ role = Role (
437
+ name = "Unauthenticated Delete" ,
438
+ organization_id = test_organization .id
439
+ )
440
+ session .add (role )
441
+ session .commit ()
442
+
443
+ response = unauth_client .post (
444
+ "/roles/delete" ,
445
+ data = {
446
+ "id" : role .id ,
447
+ "organization_id" : test_organization .id
448
+ },
449
+ follow_redirects = False
450
+ )
451
+
452
+ assert response .status_code == 303 # Redirects to login page
0 commit comments