- Add comprehensive health check system with multiple endpoints - Add Prometheus metrics endpoint - Add production logging configurations (5 strategies) - Add complete deployment documentation suite: * QUICKSTART.md - 30-minute deployment guide * DEPLOYMENT_CHECKLIST.md - Printable verification checklist * DEPLOYMENT_WORKFLOW.md - Complete deployment lifecycle * PRODUCTION_DEPLOYMENT.md - Comprehensive technical reference * production-logging.md - Logging configuration guide * ANSIBLE_DEPLOYMENT.md - Infrastructure as Code automation * README.md - Navigation hub * DEPLOYMENT_SUMMARY.md - Executive summary - Add deployment scripts and automation - Add DEPLOYMENT_PLAN.md - Concrete plan for immediate deployment - Update README with production-ready features All production infrastructure is now complete and ready for deployment.
192 lines
6.4 KiB
PHP
192 lines
6.4 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
use App\Framework\EventBus\EventBus;
|
|
use App\Framework\Notification\Notification;
|
|
use App\Framework\Notification\NotificationDispatcher;
|
|
use App\Framework\Notification\ValueObjects\NotificationChannel;
|
|
use App\Framework\Notification\ValueObjects\NotificationPriority;
|
|
use App\Framework\Notification\ValueObjects\NotificationType;
|
|
use App\Framework\Queue\InMemoryQueue;
|
|
|
|
// Mock EventBus for testing
|
|
class MockEventBus implements EventBus
|
|
{
|
|
public array $dispatchedEvents = [];
|
|
|
|
public function dispatch(object $event): void
|
|
{
|
|
$this->dispatchedEvents[] = $event;
|
|
}
|
|
}
|
|
|
|
describe('Notification System', function () {
|
|
beforeEach(function () {
|
|
// Setup test dependencies
|
|
$this->queue = new InMemoryQueue();
|
|
$this->eventBus = new MockEventBus();
|
|
});
|
|
|
|
it('can create a notification with required fields', function () {
|
|
$notification = Notification::create(
|
|
'user-123',
|
|
NotificationType::system(),
|
|
'System Update',
|
|
'Your system has been updated',
|
|
NotificationChannel::DATABASE,
|
|
NotificationChannel::EMAIL
|
|
);
|
|
|
|
expect($notification->recipientId)->toBe('user-123');
|
|
expect($notification->title)->toBe('System Update');
|
|
expect($notification->body)->toBe('Your system has been updated');
|
|
expect($notification->channels)->toHaveCount(2);
|
|
expect($notification->priority)->toBe(NotificationPriority::NORMAL);
|
|
});
|
|
|
|
it('can add action to notification', function () {
|
|
$notification = Notification::create(
|
|
'user-123',
|
|
NotificationType::system(),
|
|
'Action Required',
|
|
'Please review your settings',
|
|
NotificationChannel::DATABASE
|
|
)->withAction('/settings', 'Review Settings');
|
|
|
|
expect($notification->hasAction())->toBeTrue();
|
|
expect($notification->actionUrl)->toBe('/settings');
|
|
expect($notification->actionLabel)->toBe('Review Settings');
|
|
});
|
|
|
|
it('can set priority on notification', function () {
|
|
$notification = Notification::create(
|
|
'user-123',
|
|
NotificationType::security(),
|
|
'Security Alert',
|
|
'Unusual login detected',
|
|
NotificationChannel::DATABASE,
|
|
NotificationChannel::EMAIL
|
|
)->withPriority(NotificationPriority::URGENT);
|
|
|
|
expect($notification->priority)->toBe(NotificationPriority::URGENT);
|
|
expect($notification->priority->shouldInterruptUser())->toBeTrue();
|
|
});
|
|
|
|
it('can add custom data to notification', function () {
|
|
$notification = Notification::create(
|
|
'user-123',
|
|
NotificationType::social(),
|
|
'New Follower',
|
|
'John Doe started following you',
|
|
NotificationChannel::DATABASE
|
|
)->withData([
|
|
'follower_id' => 'user-456',
|
|
'follower_name' => 'John Doe',
|
|
'follower_avatar' => '/avatars/456.jpg',
|
|
]);
|
|
|
|
expect($notification->data)->toHaveKey('follower_id');
|
|
expect($notification->data['follower_name'])->toBe('John Doe');
|
|
});
|
|
|
|
it('validates required fields', function () {
|
|
try {
|
|
Notification::create(
|
|
'',
|
|
NotificationType::system(),
|
|
'Test',
|
|
'Test body',
|
|
NotificationChannel::DATABASE
|
|
);
|
|
expect(true)->toBeFalse('Should have thrown exception');
|
|
} catch (\InvalidArgumentException $e) {
|
|
expect($e->getMessage())->toContain('Recipient ID');
|
|
}
|
|
});
|
|
|
|
it('requires at least one channel', function () {
|
|
try {
|
|
new Notification(
|
|
id: \App\Framework\Notification\ValueObjects\NotificationId::generate(),
|
|
recipientId: 'user-123',
|
|
type: NotificationType::system(),
|
|
title: 'Test',
|
|
body: 'Test body',
|
|
data: [],
|
|
channels: [], // Empty channels
|
|
priority: NotificationPriority::NORMAL,
|
|
status: \App\Framework\Notification\ValueObjects\NotificationStatus::PENDING,
|
|
createdAt: \App\Framework\Core\ValueObjects\Timestamp::now()
|
|
);
|
|
expect(true)->toBeFalse('Should have thrown exception');
|
|
} catch (\InvalidArgumentException $e) {
|
|
expect($e->getMessage())->toContain('channel');
|
|
}
|
|
});
|
|
|
|
it('can mark notification as read', function () {
|
|
$notification = Notification::create(
|
|
'user-123',
|
|
NotificationType::system(),
|
|
'Test',
|
|
'Test body',
|
|
NotificationChannel::DATABASE
|
|
);
|
|
|
|
expect($notification->isRead())->toBeFalse();
|
|
|
|
$readNotification = $notification->markAsRead();
|
|
|
|
expect($readNotification->isRead())->toBeTrue();
|
|
expect($readNotification->readAt)->toBeInstanceOf(\App\Framework\Core\ValueObjects\Timestamp::class);
|
|
});
|
|
|
|
it('can convert notification to array', function () {
|
|
$notification = Notification::create(
|
|
'user-123',
|
|
NotificationType::transactional(),
|
|
'Payment Received',
|
|
'Your payment of $50 was processed',
|
|
NotificationChannel::DATABASE,
|
|
NotificationChannel::EMAIL
|
|
)->withData(['amount' => 50, 'currency' => 'USD']);
|
|
|
|
$array = $notification->toArray();
|
|
|
|
expect($array)->toHaveKey('id');
|
|
expect($array)->toHaveKey('recipient_id');
|
|
expect($array['title'])->toBe('Payment Received');
|
|
expect($array['data']['amount'])->toBe(50);
|
|
expect($array['channels'])->toContain('database');
|
|
expect($array['channels'])->toContain('email');
|
|
});
|
|
});
|
|
|
|
describe('Notification Dispatcher', function () {
|
|
it('can queue notification for async delivery', function () {
|
|
$queue = new InMemoryQueue();
|
|
$eventBus = new MockEventBus();
|
|
|
|
$dispatcher = new NotificationDispatcher(
|
|
channels: [],
|
|
queue: $queue,
|
|
eventBus: $eventBus
|
|
);
|
|
|
|
$notification = Notification::create(
|
|
'user-123',
|
|
NotificationType::system(),
|
|
'Test',
|
|
'Test body',
|
|
NotificationChannel::DATABASE
|
|
);
|
|
|
|
expect($queue->size())->toBe(0);
|
|
|
|
$dispatcher->sendLater($notification);
|
|
|
|
expect($queue->size())->toBe(1);
|
|
});
|
|
});
|