diff --git a/src/Infra/Listeners/LogClientSecretRotated.php b/src/Infra/Listeners/LogClientSecretRotated.php new file mode 100644 index 0000000..bc9ce73 --- /dev/null +++ b/src/Infra/Listeners/LogClientSecretRotated.php @@ -0,0 +1,33 @@ +logger->info( + sprintf( + 'Client secret rotated for client "%s" (ID %s)', + $event->clientIdentifier->value, + $event->clientId->value + ), + [ + 'client_id' => $event->clientId->value, + 'client_identifier' => $event->clientIdentifier->value, + 'rotated_at' => $event->generatedAt->format('Y-m-d H:i:s'), + ] + ); + } +} diff --git a/src/Infra/Listeners/LogClientStatusChanged.php b/src/Infra/Listeners/LogClientStatusChanged.php new file mode 100644 index 0000000..f93f3bd --- /dev/null +++ b/src/Infra/Listeners/LogClientStatusChanged.php @@ -0,0 +1,33 @@ +logger->info( + sprintf( + 'Client status changed for client "%s" (ID %s)', + $event->clientIdentifier->value, + $event->clientId->value + ), + [ + 'client_id' => $event->clientId->value, + 'client_identifier' => $event->clientIdentifier->value, + 'is_active' => $event->isActive, + 'toggled_at' => $event->toggledAt->format('Y-m-d H:i:s'), + ] + ); + } +} diff --git a/src/Infra/Providers/EventServiceProvider.php b/src/Infra/Providers/EventServiceProvider.php index e3bd0f7..a3a9382 100644 --- a/src/Infra/Providers/EventServiceProvider.php +++ b/src/Infra/Providers/EventServiceProvider.php @@ -4,10 +4,15 @@ declare(strict_types=1); namespace Infra\Providers; +use Application\Security\Events\ClientSecretGenerated; +use Application\Security\Events\ClientSecretRegenerated; +use Application\Security\Events\ClientStatusToggled; use Domain\Events\BrandCreated; use Illuminate\Support\Facades\Event; use Illuminate\Support\ServiceProvider; use Infra\Listeners\LogBrandCreated; +use Infra\Listeners\LogClientSecretRotated; +use Infra\Listeners\LogClientStatusChanged; class EventServiceProvider extends ServiceProvider { @@ -17,5 +22,20 @@ class EventServiceProvider extends ServiceProvider BrandCreated::class, LogBrandCreated::class, ); + + Event::listen( + ClientSecretGenerated::class, + LogClientSecretRotated::class, + ); + + Event::listen( + ClientSecretRegenerated::class, + LogClientSecretRotated::class, + ); + + Event::listen( + ClientStatusToggled::class, + LogClientStatusChanged::class, + ); } } diff --git a/tests/Unit/Infra/Listeners/LogClientSecretRotatedTest.php b/tests/Unit/Infra/Listeners/LogClientSecretRotatedTest.php new file mode 100644 index 0000000..8be5d06 --- /dev/null +++ b/tests/Unit/Infra/Listeners/LogClientSecretRotatedTest.php @@ -0,0 +1,94 @@ +shouldReceive('info') + ->once() + ->with( + sprintf( + 'Client secret rotated for client "%s" (ID %s)', + $clientIdentifier->value, + $clientId->value + ), + [ + 'client_id' => $clientId->value, + 'client_identifier' => $clientIdentifier->value, + 'rotated_at' => '2026-08-21 10:00:00', + ] + ); + + $listener = new LogClientSecretRotated($loggerMock); + + // WHEN + $listener->handle($event); + + // THEN + expect(true)->toBeTrue(); + }); + + it('logs informative message when client secret is regenerated', function (): void { + // GIVEN + $clientId = new ClientId(Uuid::uuid7()); + $clientIdentifier = new ClientIdentifier('arcadia_id_22222222222222222222222222222222'); + $generatedAt = new \DateTimeImmutable('2026-08-21 11:00:00'); + + $event = new ClientSecretRegenerated( + clientId: $clientId, + clientIdentifier: $clientIdentifier, + generatedAt: $generatedAt, + ); + + /** @var LoggerInterface&Mockery\MockInterface $loggerMock */ + $loggerMock = Mockery::mock(LoggerInterface::class); + $loggerMock->shouldReceive('info') + ->once() + ->with( + sprintf( + 'Client secret rotated for client "%s" (ID %s)', + $clientIdentifier->value, + $clientId->value + ), + [ + 'client_id' => $clientId->value, + 'client_identifier' => $clientIdentifier->value, + 'rotated_at' => '2026-08-21 11:00:00', + ] + ); + + $listener = new LogClientSecretRotated($loggerMock); + + // WHEN + $listener->handle($event); + + // THEN + expect(true)->toBeTrue(); + }); +}); diff --git a/tests/Unit/Infra/Listeners/LogClientStatusChangedTest.php b/tests/Unit/Infra/Listeners/LogClientStatusChangedTest.php new file mode 100644 index 0000000..190a9ee --- /dev/null +++ b/tests/Unit/Infra/Listeners/LogClientStatusChangedTest.php @@ -0,0 +1,97 @@ +shouldReceive('info') + ->once() + ->with( + sprintf( + 'Client status changed for client "%s" (ID %s)', + $clientIdentifier->value, + $clientId->value + ), + [ + 'client_id' => $clientId->value, + 'client_identifier' => $clientIdentifier->value, + 'is_active' => true, + 'toggled_at' => '2026-08-21 12:00:00', + ] + ); + + $listener = new LogClientStatusChanged($loggerMock); + + // WHEN + $listener->handle($event); + + // THEN + expect(true)->toBeTrue(); + }); + + it('logs informative message when client status is changed to inactive', function (): void { + // GIVEN + $clientId = new ClientId(Uuid::uuid7()); + $clientIdentifier = new ClientIdentifier('arcadia_id_44444444444444444444444444444444'); + $toggledAt = new \DateTimeImmutable('2026-08-21 12:30:00'); + + $event = new ClientStatusToggled( + clientId: $clientId, + clientIdentifier: $clientIdentifier, + isActive: false, + toggledAt: $toggledAt, + ); + + /** @var LoggerInterface&Mockery\MockInterface $loggerMock */ + $loggerMock = Mockery::mock(LoggerInterface::class); + $loggerMock->shouldReceive('info') + ->once() + ->with( + sprintf( + 'Client status changed for client "%s" (ID %s)', + $clientIdentifier->value, + $clientId->value + ), + [ + 'client_id' => $clientId->value, + 'client_identifier' => $clientIdentifier->value, + 'is_active' => false, + 'toggled_at' => '2026-08-21 12:30:00', + ] + ); + + $listener = new LogClientStatusChanged($loggerMock); + + // WHEN + $listener->handle($event); + + // THEN + expect(true)->toBeTrue(); + }); +});