true, 'libra.notifications.admin_email' => 'admin@test.com', 'libra.notifications.throttle_minutes' => 15, ]); }); describe('shouldNotifyAdmin', function () { it('returns false for validation exceptions', function () { $service = new AdminNotificationService; $exception = ValidationException::withMessages(['field' => ['error']]); expect($service->shouldNotifyAdmin($exception))->toBeFalse(); }); it('returns false for authentication exceptions', function () { $service = new AdminNotificationService; $exception = new AuthenticationException; expect($service->shouldNotifyAdmin($exception))->toBeFalse(); }); it('returns false for authorization exceptions', function () { $service = new AdminNotificationService; $exception = new AuthorizationException; expect($service->shouldNotifyAdmin($exception))->toBeFalse(); }); it('returns false for model not found exceptions', function () { $service = new AdminNotificationService; $exception = new ModelNotFoundException; expect($service->shouldNotifyAdmin($exception))->toBeFalse(); }); it('returns false for not found http exceptions', function () { $service = new AdminNotificationService; $exception = new NotFoundHttpException; expect($service->shouldNotifyAdmin($exception))->toBeFalse(); }); it('returns false for token mismatch exceptions', function () { $service = new AdminNotificationService; $exception = new TokenMismatchException; expect($service->shouldNotifyAdmin($exception))->toBeFalse(); }); it('returns true for exceptions implementing ShouldNotifyAdmin', function () { $service = new AdminNotificationService; $exception = new class extends Exception implements ShouldNotifyAdmin {}; expect($service->shouldNotifyAdmin($exception))->toBeTrue(); }); it('returns true for mail transport exceptions', function () { $service = new AdminNotificationService; $exception = new TransportException('SMTP error'); expect($service->shouldNotifyAdmin($exception))->toBeTrue(); }); it('returns true for critical database connection errors', function () { $service = new AdminNotificationService; $pdo = new PDOException('Connection refused'); $exception = new QueryException('mysql', 'SELECT 1', [], $pdo); // Mock the code to be a critical code $reflection = new ReflectionClass($exception); $property = $reflection->getProperty('code'); $property->setAccessible(true); $property->setValue($exception, '2002'); expect($service->shouldNotifyAdmin($exception))->toBeTrue(); }); it('returns false for non-critical database errors', function () { $service = new AdminNotificationService; $pdo = new PDOException('Syntax error'); $exception = new QueryException('mysql', 'SELECT 1', [], $pdo); expect($service->shouldNotifyAdmin($exception))->toBeFalse(); }); }); describe('notifyError', function () { it('sends notification when enabled', function () { Notification::fake(); Cache::flush(); $service = new AdminNotificationService; $exception = new RuntimeException('Test error'); $service->notifyError($exception); Notification::assertSentOnDemand( SystemErrorNotification::class, function ($notification, $channels, $notifiable) { return $notifiable->routes['mail'] === 'admin@test.com'; } ); }); it('does not send notification when disabled', function () { Notification::fake(); config(['libra.notifications.system_notifications_enabled' => false]); $service = new AdminNotificationService; $exception = new RuntimeException('Test error'); $service->notifyError($exception); Notification::assertNothingSent(); }); it('does not send notification when admin email is not configured', function () { Notification::fake(); config(['libra.notifications.admin_email' => null]); $service = new AdminNotificationService; $exception = new RuntimeException('Test error'); $service->notifyError($exception); Notification::assertNothingSent(); }); it('rate limits duplicate notifications', function () { Notification::fake(); Cache::flush(); $service = new AdminNotificationService; $exception = new RuntimeException('Test error'); $service->notifyError($exception); $service->notifyError($exception); Notification::assertSentOnDemandTimes(SystemErrorNotification::class, 1); }); it('allows notifications for different error types', function () { Notification::fake(); Cache::flush(); $service = new AdminNotificationService; $exception1 = new RuntimeException('Error 1'); $exception2 = new InvalidArgumentException('Error 2'); $service->notifyError($exception1); $service->notifyError($exception2); Notification::assertSentOnDemandTimes(SystemErrorNotification::class, 2); }); it('allows notifications for same error type with different messages', function () { Notification::fake(); Cache::flush(); $service = new AdminNotificationService; $exception1 = new RuntimeException('Error message 1'); $exception2 = new RuntimeException('Error message 2'); $service->notifyError($exception1); $service->notifyError($exception2); Notification::assertSentOnDemandTimes(SystemErrorNotification::class, 2); }); });