188 lines
6.4 KiB
PHP
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);
|
|
});
|
|
});
|