libra/tests/Feature/Notifications/SystemErrorNotificationTest...

192 lines
5.9 KiB
PHP

<?php
use App\Contracts\ShouldNotifyAdmin;
use App\Notifications\SystemErrorNotification;
use App\Services\AdminNotificationService;
use Illuminate\Notifications\AnonymousNotifiable;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Notification;
beforeEach(function () {
config([
'libra.notifications.system_notifications_enabled' => true,
'libra.notifications.admin_email' => 'admin@libra.ps',
'libra.notifications.throttle_minutes' => 15,
]);
Cache::flush();
});
test('critical exception triggers admin notification via service', function () {
Notification::fake();
$exception = new class('Critical payment error') extends Exception implements ShouldNotifyAdmin {};
$service = app(AdminNotificationService::class);
if ($service->shouldNotifyAdmin($exception)) {
$service->notifyError($exception);
}
Notification::assertSentOnDemand(
SystemErrorNotification::class,
function ($notification, $channels, $notifiable) {
return $notifiable->routes['mail'] === config('libra.notifications.admin_email');
}
);
});
test('validation exception does not trigger admin notification', function () {
Notification::fake();
try {
validator([], ['email' => 'required'])->validate();
} catch (\Illuminate\Validation\ValidationException $e) {
$service = app(AdminNotificationService::class);
if ($service->shouldNotifyAdmin($e)) {
$service->notifyError($e);
}
}
Notification::assertNothingSent();
});
test('authentication exception does not trigger admin notification', function () {
Notification::fake();
$exception = new \Illuminate\Auth\AuthenticationException;
$service = app(AdminNotificationService::class);
if ($service->shouldNotifyAdmin($exception)) {
$service->notifyError($exception);
}
Notification::assertNothingSent();
});
test('authorization exception does not trigger admin notification', function () {
Notification::fake();
$exception = new \Illuminate\Auth\Access\AuthorizationException;
$service = app(AdminNotificationService::class);
if ($service->shouldNotifyAdmin($exception)) {
$service->notifyError($exception);
}
Notification::assertNothingSent();
});
test('model not found exception does not trigger admin notification', function () {
Notification::fake();
$exception = new \Illuminate\Database\Eloquent\ModelNotFoundException;
$service = app(AdminNotificationService::class);
if ($service->shouldNotifyAdmin($exception)) {
$service->notifyError($exception);
}
Notification::assertNothingSent();
});
test('http 404 exception does not trigger admin notification', function () {
Notification::fake();
$exception = new \Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
$service = app(AdminNotificationService::class);
if ($service->shouldNotifyAdmin($exception)) {
$service->notifyError($exception);
}
Notification::assertNothingSent();
});
test('system error notification contains required information', function () {
$exception = new RuntimeException('Test error message');
$notification = new SystemErrorNotification($exception);
$mail = $notification->toMail(new AnonymousNotifiable);
expect($mail->subject)->toContain('[URGENT]');
expect($mail->subject)->toContain('RuntimeException');
expect($mail->subject)->toContain('Libra Law Firm');
});
test('system error notification includes environment', function () {
$exception = new RuntimeException('Test error');
$notification = new SystemErrorNotification($exception);
$mail = $notification->toMail(new AnonymousNotifiable);
expect($mail->subject)->toContain('['.app()->environment().']');
});
test('system error notification sent immediately not queued', function () {
Notification::fake();
$exception = new class('Critical error') extends Exception implements ShouldNotifyAdmin {};
$service = app(AdminNotificationService::class);
$service->notifyError($exception);
Notification::assertSentOnDemand(SystemErrorNotification::class);
});
test('rate limiting prevents duplicate notifications within throttle period', function () {
Notification::fake();
$exception = new class('Critical error') extends Exception implements ShouldNotifyAdmin {};
$service = app(AdminNotificationService::class);
$service->notifyError($exception);
$service->notifyError($exception);
$service->notifyError($exception);
Notification::assertSentOnDemandTimes(SystemErrorNotification::class, 1);
});
test('different exceptions are not rate limited together', function () {
Notification::fake();
$exception1 = new class('Error 1') extends Exception implements ShouldNotifyAdmin {};
$exception2 = new class('Error 2') extends Exception implements ShouldNotifyAdmin {};
$service = app(AdminNotificationService::class);
$service->notifyError($exception1);
$service->notifyError($exception2);
Notification::assertSentOnDemandTimes(SystemErrorNotification::class, 2);
});
test('notification not sent when system notifications disabled', function () {
Notification::fake();
config(['libra.notifications.system_notifications_enabled' => false]);
$exception = new class('Critical error') extends Exception implements ShouldNotifyAdmin {};
$service = app(AdminNotificationService::class);
$service->notifyError($exception);
Notification::assertNothingSent();
});
test('notification not sent when admin email not configured', function () {
Notification::fake();
config(['libra.notifications.admin_email' => null]);
$exception = new class('Critical error') extends Exception implements ShouldNotifyAdmin {};
$service = app(AdminNotificationService::class);
$service->notifyError($exception);
Notification::assertNothingSent();
});