'manage user directory', 'guard_name' => 'web']); Permission::firstOrCreate(['name' => 'view user directory', 'guard_name' => 'web']); $user->givePermissionTo($perm); } function defaultRole(): Role { return Role::firstOrCreate(['name' => 'member', 'guard_name' => 'web']); } const STRONG_PASSWORD = 'Str0ng!Passw0rd2026'; test('guest cannot access user index', function () { $this->get('/users')->assertRedirect('/login'); }); test('user without permission gets 403', function () { $u = User::factory()->create(); $this->actingAs($u)->get('/users')->assertForbidden(); }); test('store creates user, hashes password, assigns roles', function () { $admin = User::factory()->create(); grantManageUserDirectory($admin); $role = defaultRole(); $response = $this->actingAs($admin)->postJson('/users', [ 'name' => 'Jane Doe', 'email' => 'jane@example.com', 'password' => STRONG_PASSWORD, 'roles' => [$role->id], ]); $response->assertOk()->assertJson(['success' => true]); $created = User::where('email', 'jane@example.com')->first(); expect($created)->not->toBeNull(); expect($created->password)->not->toBe(STRONG_PASSWORD); expect($created->hasRole('member'))->toBeTrue(); }); test('store rejects weak password', function () { $admin = User::factory()->create(); grantManageUserDirectory($admin); $role = defaultRole(); $this->actingAs($admin)->postJson('/users', [ 'name' => 'Weak Pass', 'email' => 'weak@example.com', 'password' => 'short', 'roles' => [$role->id], ])->assertStatus(422); }); test('store rejects duplicate email', function () { $admin = User::factory()->create(); grantManageUserDirectory($admin); $role = defaultRole(); User::factory()->create(['email' => 'taken@example.com']); $this->actingAs($admin)->postJson('/users', [ 'name' => 'Dup Email', 'email' => 'taken@example.com', 'password' => STRONG_PASSWORD, 'roles' => [$role->id], ])->assertStatus(422); }); test('store rejects name with digits', function () { $admin = User::factory()->create(); grantManageUserDirectory($admin); $role = defaultRole(); $this->actingAs($admin)->postJson('/users', [ 'name' => 'John123', 'email' => 'john@example.com', 'password' => STRONG_PASSWORD, 'roles' => [$role->id], ])->assertStatus(422); }); test('store requires at least one role', function () { $admin = User::factory()->create(); grantManageUserDirectory($admin); $this->actingAs($admin)->postJson('/users', [ 'name' => 'No Role', 'email' => 'norole@example.com', 'password' => STRONG_PASSWORD, 'roles' => [], ])->assertStatus(422); }); test('update can change name and reset roles without touching password', function () { $admin = User::factory()->create(['name' => 'Admin User']); grantManageUserDirectory($admin); $r1 = defaultRole(); $r2 = Role::firstOrCreate(['name' => 'editor', 'guard_name' => 'web']); $target = User::factory()->create(['name' => 'Old Name']); $target->assignRole($r1); $originalHash = $target->password; $this->actingAs($admin)->putJson("/users/{$target->id}", [ 'name' => 'Renamed User', 'email' => $target->email, 'roles' => [$r2->id], ])->assertOk(); $target->refresh(); expect($target->name)->toBe('Renamed User'); expect($target->password)->toBe($originalHash); expect($target->hasRole('editor'))->toBeTrue(); expect($target->hasRole('member'))->toBeFalse(); }); test('update changes password when provided', function () { $admin = User::factory()->create(['name' => 'Admin User']); grantManageUserDirectory($admin); $role = defaultRole(); $target = User::factory()->create(['name' => 'Target User']); $target->assignRole($role); $original = $target->password; $this->actingAs($admin)->putJson("/users/{$target->id}", [ 'name' => 'Target User', 'email' => $target->email, 'password' => STRONG_PASSWORD, 'roles' => [$role->id], ])->assertOk(); expect($target->fresh()->password)->not->toBe($original); }); test('toggleStatus deactivates and activates user', function () { $admin = User::factory()->create(); grantManageUserDirectory($admin); $target = User::factory()->create(['is_active' => 1]); $this->actingAs($admin) ->postJson('/users/toggle-status', ['id' => $target->id, 'status' => 'deactivate']) ->assertOk(); expect((bool) $target->fresh()->is_active)->toBeFalse(); $this->actingAs($admin) ->postJson('/users/toggle-status', ['id' => $target->id, 'status' => 'activate']) ->assertOk(); expect((bool) $target->fresh()->is_active)->toBeTrue(); }); test('destroy soft deletes user', function () { $admin = User::factory()->create(); grantManageUserDirectory($admin); $target = User::factory()->create(); $this->actingAs($admin)->deleteJson("/users/{$target->id}")->assertOk(); expect(User::withTrashed()->find($target->id)->trashed())->toBeTrue(); }); test('restore brings soft-deleted user back', function () { $admin = User::factory()->create(); grantManageUserDirectory($admin); $target = User::factory()->create(); $target->delete(); $this->actingAs($admin)->postJson("/users/{$target->id}/restore")->assertOk(); expect(User::find($target->id)->trashed())->toBeFalse(); }); test('forceDelete blocks self-deletion', function () { $admin = User::factory()->create(); grantManageUserDirectory($admin); $admin->delete(); $this->actingAs($admin) ->deleteJson("/users/{$admin->id}/force") ->assertStatus(403) ->assertJson(['success' => false]); expect(User::withTrashed()->find($admin->id))->not->toBeNull(); }); test('forceDelete permanently removes another user', function () { $admin = User::factory()->create(); grantManageUserDirectory($admin); $target = User::factory()->create(); $target->delete(); $this->actingAs($admin)->deleteJson("/users/{$target->id}/force")->assertOk(); expect(User::withTrashed()->find($target->id))->toBeNull(); });