libra/tests/Unit/Services/AdminNotificationServiceTes...

188 lines
6.4 KiB
PHP

<?php
use App\Contracts\ShouldNotifyAdmin;
use App\Notifications\SystemErrorNotification;
use App\Services\AdminNotificationService;
use Illuminate\Auth\Access\AuthorizationException;
use Illuminate\Auth\AuthenticationException;
use Illuminate\Database\Eloquent\ModelNotFoundException;
use Illuminate\Database\QueryException;
use Illuminate\Session\TokenMismatchException;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Notification;
use Illuminate\Validation\ValidationException;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\Mailer\Exception\TransportException;
beforeEach(function () {
config([
'libra.notifications.system_notifications_enabled' => 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);
});
});