app/tests/Unit/Actions/User/DeleteUserActionTest.php

195 lines
6.9 KiB
PHP
Raw Normal View History

2025-12-29 19:58:58 +01:00
<?php
namespace Tests\Unit\Actions\User;
use App\Actions\User\DeleteUserAction;
use App\Models\Dish;
use App\Models\Planner;
use App\Models\User;
use App\Models\UserDish;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\Log;
use Tests\TestCase;
class DeleteUserActionTest extends TestCase
{
use RefreshDatabase;
private DeleteUserAction $action;
private Planner $planner;
protected function setUp(): void
{
parent::setUp();
$this->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();
}
}