- Move 12 markdown files from root to docs/ subdirectories - Organize documentation by category: • docs/troubleshooting/ (1 file) - Technical troubleshooting guides • docs/deployment/ (4 files) - Deployment and security documentation • docs/guides/ (3 files) - Feature-specific guides • docs/planning/ (4 files) - Planning and improvement proposals Root directory cleanup: - Reduced from 16 to 4 markdown files in root - Only essential project files remain: • CLAUDE.md (AI instructions) • README.md (Main project readme) • CLEANUP_PLAN.md (Current cleanup plan) • SRC_STRUCTURE_IMPROVEMENTS.md (Structure improvements) This improves: ✅ Documentation discoverability ✅ Logical organization by purpose ✅ Clean root directory ✅ Better maintainability
195 lines
6.6 KiB
PHP
195 lines
6.6 KiB
PHP
<?php
|
|
|
|
use App\Domain\Common\ValueObject\Email;
|
|
use App\Framework\Notification\Channels\DatabaseChannel;
|
|
use App\Framework\Notification\Channels\EmailChannel;
|
|
use App\Framework\Notification\Channels\UserEmailResolver;
|
|
use App\Framework\Notification\Notification;
|
|
use App\Framework\Notification\NotificationDispatcher;
|
|
use App\Framework\Notification\Storage\DatabaseNotificationRepository;
|
|
use App\Framework\Notification\ValueObjects\NotificationChannel;
|
|
use App\Framework\Notification\ValueObjects\NotificationPriority;
|
|
use App\Framework\Notification\ValueObjects\NotificationType;
|
|
use App\Framework\EventBus\EventBus;
|
|
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);
|
|
});
|
|
});
|