195 lines
6.9 KiB
PHP
195 lines
6.9 KiB
PHP
|
|
<?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();
|
||
|
|
}
|
||
|
|
}
|