action = new DeleteUserAction(); // Create a planner for testing $this->planner = Planner::factory()->create(); } public function test_it_can_delete_a_user_successfully(): void { // Arrange $user = User::factory()->create([ 'planner_id' => $this->planner->id, 'name' => 'Test User' ]); $userId = $user->id; // Act $result = $this->action->execute($user); // Assert $this->assertTrue($result); $this->assertDatabaseMissing('users', ['id' => $userId]); } public function test_it_can_delete_a_user_with_associated_dishes(): void { // Arrange $user = User::factory()->create(['planner_id' => $this->planner->id]); $dish = Dish::factory()->create(['planner_id' => $this->planner->id]); // Associate user with dish UserDish::create([ 'user_id' => $user->id, 'dish_id' => $dish->id ]); $userId = $user->id; // Verify the association exists $this->assertDatabaseHas('user_dishes', [ 'user_id' => $userId, 'dish_id' => $dish->id ]); // Act $result = $this->action->execute($user); // Assert $this->assertTrue($result); $this->assertDatabaseMissing('users', ['id' => $userId]); // Verify cascade deletion removed the association $this->assertDatabaseMissing('user_dishes', ['user_id' => $userId]); // Verify the dish itself still exists $this->assertDatabaseHas('dishes', ['id' => $dish->id]); } public function test_it_logs_deletion_process(): void { // Arrange Log::spy(); $user = User::factory()->create(['planner_id' => $this->planner->id]); $userId = $user->id; $userName = $user->name; // Act $this->action->execute($user); // Assert Log::shouldHaveReceived('info') ->with('DeleteUserAction: Starting user deletion', [ 'user_id' => $userId, 'user_name' => $userName, 'planner_id' => $this->planner->id, ]); Log::shouldHaveReceived('info') ->with('DeleteUserAction: User successfully deleted', [ 'user_id' => $userId, 'user_name' => $userName, ]); } public function test_it_handles_database_transaction_rollback_on_failure(): void { // Arrange $user = User::factory()->create(['planner_id' => $this->planner->id]); // Mock the user to throw an exception during deletion $mockUser = \Mockery::mock(User::class); $mockUser->shouldReceive('getAttribute')->with('id')->andReturn($user->id); $mockUser->shouldReceive('getAttribute')->with('name')->andReturn($user->name); $mockUser->shouldReceive('getAttribute')->with('planner_id')->andReturn($user->planner_id); $mockUser->shouldReceive('userDishes')->andReturn($user->userDishes()); $mockUser->shouldReceive('dishes')->andReturn($user->dishes()); $mockUser->shouldReceive('delete')->andThrow(new \Exception('Database error')); // Act & Assert $this->expectException(\Exception::class); $this->expectExceptionMessage('Database error'); $this->action->execute($mockUser); // Verify original user still exists (transaction rolled back) $this->assertDatabaseHas('users', ['id' => $user->id]); } public function test_it_throws_exception_when_deletion_returns_false(): void { // Arrange $user = User::factory()->create(['planner_id' => $this->planner->id]); // Mock the user to return false on delete $mockUser = \Mockery::mock(User::class); $mockUser->shouldReceive('getAttribute')->with('id')->andReturn($user->id); $mockUser->shouldReceive('getAttribute')->with('name')->andReturn($user->name); $mockUser->shouldReceive('getAttribute')->with('planner_id')->andReturn($user->planner_id); $mockUser->shouldReceive('userDishes')->andReturn($user->userDishes()); $mockUser->shouldReceive('dishes')->andReturn($user->dishes()); $mockUser->shouldReceive('delete')->andReturn(false); // Act & Assert $this->expectException(\Exception::class); $this->expectExceptionMessage('User deletion returned false'); $this->action->execute($mockUser); } public function test_it_throws_exception_when_deletion_does_not_persist(): void { // This test is tricky to implement realistically since we can't easily // mock the User::find() call in a way that makes sense. // We'll skip this edge case for now, but in a real scenario you might // want to test database connection issues, etc. $this->markTestSkipped('Edge case test - difficult to implement without complex mocking'); } public function test_it_logs_errors_on_failure(): void { // Arrange Log::spy(); $user = User::factory()->create(['planner_id' => $this->planner->id]); // Mock the user to throw an exception during deletion $mockUser = \Mockery::mock(User::class); $mockUser->shouldReceive('getAttribute')->with('id')->andReturn($user->id); $mockUser->shouldReceive('getAttribute')->with('name')->andReturn($user->name); $mockUser->shouldReceive('getAttribute')->with('planner_id')->andReturn($user->planner_id); $mockUser->shouldReceive('userDishes')->andReturn($user->userDishes()); $mockUser->shouldReceive('dishes')->andReturn($user->dishes()); $mockUser->shouldReceive('delete')->andThrow(new \Exception('Test error')); // Act & Assert try { $this->action->execute($mockUser); } catch (\Exception $e) { // Expected } // Assert Log::shouldHaveReceived('error') ->with('DeleteUserAction: User deletion failed', \Mockery::on(function ($data) use ($user) { return $data['user_id'] === $user->id && $data['error'] === 'Test error' && isset($data['trace']); })); } protected function tearDown(): void { \Mockery::close(); parent::tearDown(); } }