diff --git a/app/Http/Controllers/UsersController.php b/app/Http/Controllers/UsersController.php index e1d47ec..781b1c0 100644 --- a/app/Http/Controllers/UsersController.php +++ b/app/Http/Controllers/UsersController.php @@ -14,6 +14,7 @@ use Modules\Usermanagement\Http\Requests\User as UserRequest; use Modules\Usermanagement\Models\Role; use Modules\Usermanagement\Models\User; + use Illuminate\Support\Facades\Storage; /** * Class UsersController @@ -24,7 +25,7 @@ */ class UsersController extends Controller { - /** + /** * @var \Illuminate\Contracts\Auth\Authenticatable|null */ public $user; @@ -34,13 +35,10 @@ * * Initializes the user property with the authenticated user. */ - // public function __construct() - // { - // $this->middleware(function ($request, $next) { - // $this->user = Auth::guard('web')->user(); - // return $next($request); - // }); - // } + public function __construct() + { + $this->user = Auth::guard('web')->user(); + } /** * Display a listing of the resource. @@ -50,7 +48,7 @@ */ public function index() { - if (is_null($this->user) || !$this->user->can('users.view')) { + if (is_null($this->user) || !$this->user->can('usermanagement.read')) { //abort(403, 'Sorry! You are not allowed to view users.'); } @@ -67,7 +65,7 @@ */ public function dataForDatatables(Request $request) { - if (is_null($this->user) || !$this->user->can('users.view')) { + if (is_null($this->user) || !$this->user->can('usermanagement.view')) { //abort(403, 'Sorry! You are not allowed to view users.'); } @@ -137,7 +135,7 @@ */ public function edit($id) { - if (is_null($this->user) || !$this->user->can('users.edit')) { + if (is_null($this->user) || !$this->user->can('usermanagement.edit')) { //abort(403, 'Sorry! You are not allowed to edit users.'); } @@ -157,14 +155,14 @@ */ public function destroy($id) { - if (is_null($this->user) || !$this->user->can('users.delete')) { + if (is_null($this->user) || !$this->user->can('usermanagement.delete')) { //abort(403, 'Sorry! You are not allowed to delete users.'); } $user = User::find($id); $user->delete(); - echo json_encode(['message' => 'User deleted successfully.', 'success' => true]); + return response()->json(['message' => 'User deleted successfully.', 'success' => true]); } /** @@ -177,7 +175,7 @@ */ public function restore($id) { - if (is_null($this->user) || !$this->user->can('users.restore')) { + if (is_null($this->user) || !$this->user->can('usermanagement.restore')) { abort(403, 'Sorry! You are not allowed to restore users.'); } @@ -224,7 +222,7 @@ */ public function create() { - if (is_null($this->user) || !$this->user->can('users.create')) { + if (is_null($this->user) || !$this->user->can('usermanagement.create')) { //abort(403, 'Sorry! You are not allowed to create a user.'); } @@ -262,12 +260,17 @@ if ($request->hasFile('sign')) { // Delete old e-sign if exists if ($user->sign) { - Storage::delete('public/signatures/' . $user->id . '/' . $user->sign); + Storage::disk('public')->delete('signatures/' . $user->id . '/' . $user->sign); } $sign = $request->file('sign'); $signName = time() . '.' . $sign->getClientOriginalExtension(); - $sign->storeAs('public/signatures/' . $user->id, $signName); + + // Make sure the directory exists + Storage::disk('public')->makeDirectory('signatures/' . $user->id); + + // Store the file + $sign->storeAs('signatures/' . $user->id, $signName, 'public'); $user->sign = $signName; } @@ -312,7 +315,7 @@ */ public function update(UserRequest $request, $id) { - if (is_null($this->user) || !$this->user->can('users.update')) { + if (is_null($this->user) || !$this->user->can('usermanagement.update')) { //abort(403, 'Sorry! You are not allowed to update users.'); } diff --git a/resources/views/users/index.blade.php b/resources/views/users/index.blade.php index d7b7cb8..f28a717 100644 --- a/resources/views/users/index.blade.php +++ b/resources/views/users/index.blade.php @@ -19,29 +19,7 @@ -
- - - +
Export to Excel Add User diff --git a/tests/Feature/UsersControllerTest.php b/tests/Feature/UsersControllerTest.php new file mode 100644 index 0000000..0a1f021 --- /dev/null +++ b/tests/Feature/UsersControllerTest.php @@ -0,0 +1,466 @@ + 'usermanagement', + 'slug' => 'usermanagement' + ]); + + // Create usermanagement.read permission + Permission::create([ + 'name' => 'usermanagement.read', + 'guard_name' => 'web', + 'permission_group_id' => $permissionGroup->id + ]); + + // Create role with usermanagement.read permission + $this->adminRole = Role::create(['name' => 'admin', 'guard_name' => 'web']); + $this->adminRole->givePermissionTo('usermanagement.read'); + + // Create a user with admin role + // Create a user for testing + $this->user = User::factory()->create([ + 'name' => 'Original Name', + 'email' => 'original@example.com', + 'nik' => '123456', + 'sign' => 'old-signature.jpg' + ]); + + // Mock the storage + Storage::fake('public'); + $this->user->assignRole($this->adminRole); + + // Create test role for assignment to new user + Role::create(['name' => 'operator', 'guard_name' => 'web']); + + } + + #[Test] + public function should_display_users_index_page_when_user_has_users_view_permission() + { + $response = $this->actingAs($this->user) + ->get(route('users.index')); + + $response->assertStatus(200); + $response->assertViewIs('usermanagement::users.index'); + } + + #[Test] + public function should_return_json_response_with_correct_pagination_data_for_datatables() + { + // Create some test users + $testUsers = User::factory()->count(15)->create(); + + // Set up the request parameters + $requestData = [ + 'draw' => 1, + 'page' => 1, // Changed from 2 to 1 to match the controller logic + 'size' => 5, + 'search' => '', + 'sortField' => 'name', + 'sortOrder' => 'asc' + ]; + + // Make the request + $response = $this->actingAs($this->user) + ->getJson(route('users.datatables') . '?' . http_build_query($requestData)); + + // Assert response status and structure + $response->assertStatus(200); + $response->assertJsonStructure([ + 'draw', + 'recordsTotal', + 'recordsFiltered', + 'pageCount', + 'page', + 'totalCount', + 'data' + ]); + + // Get total count of users for verification (16 = 15 created + 1 from setup) + $totalUsers = User::count(); + + // Verify the pagination data + $responseData = $response->json(); + $this->assertEquals(1, $responseData['draw']); + $this->assertEquals($totalUsers, $responseData['recordsTotal']); + $this->assertEquals($totalUsers, $responseData['recordsFiltered']); + $this->assertEquals(ceil($totalUsers / $requestData['size']), $responseData['pageCount']); + $this->assertEquals($requestData['page'], $responseData['page']); + + // Verify that we have the correct number of users in the response + $this->assertCount(5, $responseData['data']); + + // Verify that the data is ordered correctly - get first page of sorted data + $this->assertEquals( + User::orderBy('name', 'asc')->take(5)->pluck('id')->toArray(), + collect($responseData['data'])->pluck('id')->toArray() + ); + } + + #[Test] + public function should_filter_users_by_search_term_when_search_parameter_is_provided() + { + // Create test users with specific names for testing search + $matchingUser1 = User::factory()->create(['name' => 'Test User One']); + $matchingUser2 = User::factory()->create(['email' => 'test@example.com']); + $nonMatchingUser = User::factory()->create(['name' => 'Different User', 'email' => 'different@example.com']); + + // Set up the request parameters with search term + $requestData = [ + 'draw' => 1, + 'page' => 1, + 'size' => 10, + 'search' => 'test', + 'sortField' => 'name', + 'sortOrder' => 'asc' + ]; + + // Make the request + $response = $this->actingAs($this->user) + ->getJson(route('users.datatables') . '?' . http_build_query($requestData)); + + // Assert response status and structure + $response->assertStatus(200); + $response->assertJsonStructure([ + 'draw', + 'recordsTotal', + 'recordsFiltered', + 'pageCount', + 'page', + 'totalCount', + 'data' + ]); + + // Get the response data + $responseData = $response->json(); + + // Verify that only matching users are returned + $this->assertEquals(2, $responseData['recordsFiltered']); + + // Extract user IDs from the response + $returnedUserIds = collect($responseData['data'])->pluck('id')->toArray(); + + // Verify the correct users are returned + $this->assertContains($matchingUser1->id, $returnedUserIds); + $this->assertContains($matchingUser2->id, $returnedUserIds); + $this->assertNotContains($nonMatchingUser->id, $returnedUserIds); + } + + #[Test] + public function should_correctly_sort_users_when_sortField_and_sortOrder_parameters_are_specified() + { + // Create test users with varying names to test different sort orders + $userA = User::factory()->create(['name' => 'Adam Smith']); + $userB = User::factory()->create(['name' => 'Brian Jones']); + $userC = User::factory()->create(['name' => 'Charlie Brown']); + + // Test ascending order + $requestDataAsc = [ + 'draw' => 1, + 'page' => 1, + 'size' => 10, + 'search' => '', + 'sortField' => 'name', + 'sortOrder' => 'asc' + ]; + + $responseAsc = $this->actingAs($this->user) + ->getJson(route('users.datatables') . '?' . http_build_query($requestDataAsc)); + + $responseAsc->assertStatus(200); + $responseDataAsc = $responseAsc->json(); + + // Check if sorted ascending by name + $userIdsAsc = collect($responseDataAsc['data'])->pluck('id')->toArray(); + $expectedOrderAsc = User::orderBy('name', 'asc')->pluck('id')->toArray(); + $this->assertEquals($expectedOrderAsc, $userIdsAsc); + + // Test descending order + $requestDataDesc = [ + 'draw' => 1, + 'page' => 1, + 'size' => 10, + 'search' => '', + 'sortField' => 'name', + 'sortOrder' => 'desc' + ]; + + $responseDesc = $this->actingAs($this->user) + ->getJson(route('users.datatables') . '?' . http_build_query($requestDataDesc)); + + $responseDesc->assertStatus(200); + $responseDataDesc = $responseDesc->json(); + + // Check if sorted descending by name + $userIdsDesc = collect($responseDataDesc['data'])->pluck('id')->toArray(); + $expectedOrderDesc = User::orderBy('name', 'desc')->pluck('id')->toArray(); + $this->assertEquals($expectedOrderDesc, $userIdsDesc); + + // Test sorting by a different field (email) + $requestDataEmail = [ + 'draw' => 1, + 'page' => 1, + 'size' => 10, + 'search' => '', + 'sortField' => 'email', + 'sortOrder' => 'asc' + ]; + + $responseEmail = $this->actingAs($this->user) + ->getJson(route('users.datatables') . '?' . http_build_query($requestDataEmail)); + + $responseEmail->assertStatus(200); + $responseDataEmail = $responseEmail->json(); + + // Check if sorted by email + $userIdsEmail = collect($responseDataEmail['data'])->pluck('id')->toArray(); + $expectedOrderEmail = User::orderBy('email', 'asc')->pluck('id')->toArray(); + $this->assertEquals($expectedOrderEmail, $userIdsEmail); + } + + #[Test] + public function should_successfully_create_a_new_user_and_assign_roles_when_valid_data_is_submitted() + { + // Prepare valid user data + $userData = [ + 'name' => 'Test User', + 'email' => 'test@example.com', + 'password' => 'password123', + 'password_confirmation' => 'password123', + 'nik' => '789234', + 'roles' => ['operator'] + ]; + + // Submit the request to create a new user + $response = $this->actingAs($this->user) + ->post(route('users.store'), $userData); + + // Assert redirect to users index page with success message + $response->assertRedirect(route('users.index')); + $response->assertSessionHas('success', 'User created successfully.'); + + // Assert the user was created in the database + $this->assertDatabaseHas('users', [ + 'name' => 'Test User', + 'email' => 'test@example.com', + 'nik' => '789234' + ]); + + // Assert the user was assigned the correct role + $newUser = User::where('email', 'test@example.com')->first(); + $this->assertTrue($newUser->hasRole('operator')); + } + + #[Test] + public function should_successfully_update_existing_user_information_and_role_assignments() + { + // Create a test user with admin role + $userToUpdate = User::factory()->create([ + 'name' => 'Original Name', + 'email' => 'originalee@example.com', + 'nik' => '987654' + ]); + $userToUpdate->assignRole($this->adminRole); + + // Create an additional role for the update test + $newRole = Role::create(['name' => 'editor', 'guard_name' => 'web']); + + // Prepare update data + $updateData = [ + 'name' => 'Updated Name', + 'email' => 'updated@example.com', + 'nik' => '654321', + 'roles' => ['operator'] // Change role from admin to operator + ]; + + // Make the request to update the user + $response = $this->actingAs($this->user) + ->put(route('users.update', $userToUpdate->id), $updateData); + + // Assert redirect to users index page with success message + $response->assertRedirect(route('users.index')); + $response->assertSessionHas('success', 'User updated successfully.'); + + // Assert the user was updated in the database + $this->assertDatabaseHas('users', [ + 'id' => $userToUpdate->id, + 'name' => 'Updated Name', + 'email' => 'updated@example.com', + 'nik' => '654321' + ]); + + // Refresh the user model from database + $userToUpdate->refresh(); + + // Assert the user has the new role and doesn't have the old role + $this->assertTrue($userToUpdate->hasRole('operator')); + $this->assertFalse($userToUpdate->hasRole('admin')); + } + + #[Test] + public function should_delete_a_user_when_the_authenticated_user_has_users_delete_permission() + { + // Create the permission for delete users + $permissionGroup = PermissionGroup::create([ + 'name' => 'usermanagement', + 'slug' => 'usermanagement' + ]); + + // Create delete permission + Permission::create([ + 'name' => 'usermanagement.delete', + 'guard_name' => 'web', + 'permission_group_id' => $permissionGroup->id + ]); + + // Create role with delete permission + $role = Role::create(['name' => 'manager', 'guard_name' => 'web']); + $role->givePermissionTo('usermanagement.delete'); + + // Create an admin user with the role that has delete permission + $adminUser = User::factory()->create(); + $adminUser->assignRole($role); + + // Create a user to be deleted + $userToDelete = User::factory()->create(); + + // Make the request to delete the user + $response = $this->actingAs($adminUser) + ->delete(route('users.destroy', $userToDelete->id)); + + // Assert the response is correct + $decodedResponse = json_decode($response->getContent(), true); + $this->assertEquals('User deleted successfully.', $decodedResponse['message']); + $this->assertTrue($decodedResponse['success']); + + // Assert the user was soft deleted + $this->assertSoftDeleted('users', ['id' => $userToDelete->id]); + } + + #[Test] + public function should_restore_a_soft_deleted_user_when_the_authenticated_user_has_users_restore_permission() + { + // Create permission group + $permissionGroup = PermissionGroup::create([ + 'name' => 'usermanagement', + 'slug' => 'usermanagement' + ]); + + // Create restore permission + Permission::create([ + 'name' => 'usermanagement.restore', + 'guard_name' => 'web', + 'permission_group_id' => $permissionGroup->id + ]); + + // Create role with restore permission + $role = Role::create(['name' => 'restorer', 'guard_name' => 'web']); + $role->givePermissionTo('usermanagement.restore'); + + // Create an admin user with the role that has restore permission + $adminUser = User::factory()->create(); + $adminUser->assignRole($role); + + // Create a user to be restored + $userToRestore = User::factory()->create(); + $userToRestore->delete(); // Soft delete the user + + // Verify the user is soft-deleted + $this->assertSoftDeleted('users', ['id' => $userToRestore->id]); + + // Make the request to restore the user + $response = $this->actingAs($adminUser) + ->get(route('users.restore', $userToRestore->id)); + + // Assert the response redirects to users.index with success message + $response->assertRedirect(route('users.index')); + $response->assertSessionHas('success', 'User restored successfully.'); + + // Assert the user was restored + $this->assertDatabaseHas('users', [ + 'id' => $userToRestore->id, + 'deleted_at' => null + ]); + } + + #[Test] + public function should_update_users_profile_including_signature_image_when_valid_data_is_submitted() + { + // Create a fake signature file + $file = UploadedFile::fake()->image('new-signature.jpg'); + + // Create a fake old signature file in storage + Storage::disk('public')->put( + 'signatures/' . $this->user->id . '/old-signature.jpg', + 'fake content' + ); + + // Prepare valid profile data with new signature + $profileData = [ + 'name' => 'Updated Name', + 'email' => 'updated@example.com', + 'nik' => '654321', + 'sign' => $file + ]; + + // Make the request to update the profile + $response = $this->actingAs($this->user) + ->put(route('users.update-profile'), $profileData); + + // Assert redirect to profile page with success message + $response->assertRedirect(route('users.profile')); + $response->assertSessionHas('success', 'Profile updated successfully.'); + + // Assert the user was updated in the database + $this->assertDatabaseHas('users', [ + 'id' => $this->user->id, + 'name' => 'Updated Name', + 'email' => 'updated@example.com', + 'nik' => '654321', + ]); + + // Refresh the user model from database + $this->user->refresh(); + + // Assert that the user has a sign value (any non-empty string) + $this->assertNotEmpty($this->user->sign); + + // Debug information + $files = Storage::disk('public')->allFiles('signatures/' . $this->user->id); + + // Assert the file has been stored in the expected location + // Use a more flexible check that doesn't rely on the exact filename + $signaturePath = 'signatures/' . $this->user->id; + $this->assertTrue( + Storage::disk('public')->exists($signaturePath . '/' . $this->user->sign), + "Signature file not found at expected location: {$signaturePath}/{$this->user->sign}" + ); + + // Verify old signature was deleted + Storage::disk('public')->assertMissing('signatures/' . $this->user->id . '/old-signature.jpg'); + } +}