import { describe, expect, it, vi } from 'vitest'; import { Notification } from '../../domain/entities/Notification'; import { NotificationGateway, NotificationGatewayRegistry, NotificationDeliveryResult, } from './NotificationGateway'; describe('NotificationGateway - Interface Contract', () => { it('NotificationGateway interface defines send method', () => { const mockGateway: NotificationGateway = { send: vi.fn().mockResolvedValue({ success: true, channel: 'in_app', attemptedAt: new Date(), }), supportsChannel: vi.fn().mockReturnValue(true), isConfigured: vi.fn().mockReturnValue(true), getChannel: vi.fn().mockReturnValue('in_app'), }; const notification = Notification.create({ id: 'test-id', recipientId: 'driver-1', type: 'system_announcement', title: 'Test', body: 'Test body', channel: 'in_app', }); expect(mockGateway.send).toBeDefined(); expect(typeof mockGateway.send).toBe('function'); }); it('NotificationGateway interface defines supportsChannel method', () => { const mockGateway: NotificationGateway = { send: vi.fn().mockResolvedValue({ success: true, channel: 'in_app', attemptedAt: new Date(), }), supportsChannel: vi.fn().mockReturnValue(true), isConfigured: vi.fn().mockReturnValue(true), getChannel: vi.fn().mockReturnValue('in_app'), }; expect(mockGateway.supportsChannel).toBeDefined(); expect(typeof mockGateway.supportsChannel).toBe('function'); }); it('NotificationGateway interface defines isConfigured method', () => { const mockGateway: NotificationGateway = { send: vi.fn().mockResolvedValue({ success: true, channel: 'in_app', attemptedAt: new Date(), }), supportsChannel: vi.fn().mockReturnValue(true), isConfigured: vi.fn().mockReturnValue(true), getChannel: vi.fn().mockReturnValue('in_app'), }; expect(mockGateway.isConfigured).toBeDefined(); expect(typeof mockGateway.isConfigured).toBe('function'); }); it('NotificationGateway interface defines getChannel method', () => { const mockGateway: NotificationGateway = { send: vi.fn().mockResolvedValue({ success: true, channel: 'in_app', attemptedAt: new Date(), }), supportsChannel: vi.fn().mockReturnValue(true), isConfigured: vi.fn().mockReturnValue(true), getChannel: vi.fn().mockReturnValue('in_app'), }; expect(mockGateway.getChannel).toBeDefined(); expect(typeof mockGateway.getChannel).toBe('function'); }); it('NotificationDeliveryResult has required properties', () => { const result: NotificationDeliveryResult = { success: true, channel: 'in_app', attemptedAt: new Date(), }; expect(result).toHaveProperty('success'); expect(result).toHaveProperty('channel'); expect(result).toHaveProperty('attemptedAt'); }); it('NotificationDeliveryResult can have optional externalId', () => { const result: NotificationDeliveryResult = { success: true, channel: 'email', externalId: 'email-123', attemptedAt: new Date(), }; expect(result.externalId).toBe('email-123'); }); it('NotificationDeliveryResult can have optional error', () => { const result: NotificationDeliveryResult = { success: false, channel: 'discord', error: 'Failed to send to Discord', attemptedAt: new Date(), }; expect(result.error).toBe('Failed to send to Discord'); }); }); describe('NotificationGatewayRegistry - Interface Contract', () => { it('NotificationGatewayRegistry interface defines register method', () => { const mockRegistry: NotificationGatewayRegistry = { register: vi.fn(), getGateway: vi.fn().mockReturnValue(null), getAllGateways: vi.fn().mockReturnValue([]), send: vi.fn().mockResolvedValue({ success: true, channel: 'in_app', attemptedAt: new Date(), }), }; expect(mockRegistry.register).toBeDefined(); expect(typeof mockRegistry.register).toBe('function'); }); it('NotificationGatewayRegistry interface defines getGateway method', () => { const mockRegistry: NotificationGatewayRegistry = { register: vi.fn(), getGateway: vi.fn().mockReturnValue(null), getAllGateways: vi.fn().mockReturnValue([]), send: vi.fn().mockResolvedValue({ success: true, channel: 'in_app', attemptedAt: new Date(), }), }; expect(mockRegistry.getGateway).toBeDefined(); expect(typeof mockRegistry.getGateway).toBe('function'); }); it('NotificationGatewayRegistry interface defines getAllGateways method', () => { const mockRegistry: NotificationGatewayRegistry = { register: vi.fn(), getGateway: vi.fn().mockReturnValue(null), getAllGateways: vi.fn().mockReturnValue([]), send: vi.fn().mockResolvedValue({ success: true, channel: 'in_app', attemptedAt: new Date(), }), }; expect(mockRegistry.getAllGateways).toBeDefined(); expect(typeof mockRegistry.getAllGateways).toBe('function'); }); it('NotificationGatewayRegistry interface defines send method', () => { const mockRegistry: NotificationGatewayRegistry = { register: vi.fn(), getGateway: vi.fn().mockReturnValue(null), getAllGateways: vi.fn().mockReturnValue([]), send: vi.fn().mockResolvedValue({ success: true, channel: 'in_app', attemptedAt: new Date(), }), }; expect(mockRegistry.send).toBeDefined(); expect(typeof mockRegistry.send).toBe('function'); }); }); describe('NotificationGateway - Integration with Notification', () => { it('gateway can send notification and return delivery result', async () => { const mockGateway: NotificationGateway = { send: vi.fn().mockResolvedValue({ success: true, channel: 'in_app', externalId: 'msg-123', attemptedAt: new Date(), }), supportsChannel: vi.fn().mockReturnValue(true), isConfigured: vi.fn().mockReturnValue(true), getChannel: vi.fn().mockReturnValue('in_app'), }; const notification = Notification.create({ id: 'test-id', recipientId: 'driver-1', type: 'system_announcement', title: 'Test', body: 'Test body', channel: 'in_app', }); const result = await mockGateway.send(notification); expect(result.success).toBe(true); expect(result.channel).toBe('in_app'); expect(result.externalId).toBe('msg-123'); expect(mockGateway.send).toHaveBeenCalledWith(notification); }); it('gateway can handle failed delivery', async () => { const mockGateway: NotificationGateway = { send: vi.fn().mockResolvedValue({ success: false, channel: 'email', error: 'SMTP server unavailable', attemptedAt: new Date(), }), supportsChannel: vi.fn().mockReturnValue(true), isConfigured: vi.fn().mockReturnValue(true), getChannel: vi.fn().mockReturnValue('email'), }; const notification = Notification.create({ id: 'test-id', recipientId: 'driver-1', type: 'race_registration_open', title: 'Test', body: 'Test body', channel: 'email', }); const result = await mockGateway.send(notification); expect(result.success).toBe(false); expect(result.channel).toBe('email'); expect(result.error).toBe('SMTP server unavailable'); }); }); describe('NotificationGatewayRegistry - Integration', () => { it('registry can route notification to appropriate gateway', async () => { const inAppGateway: NotificationGateway = { send: vi.fn().mockResolvedValue({ success: true, channel: 'in_app', attemptedAt: new Date(), }), supportsChannel: vi.fn().mockReturnValue(true), isConfigured: vi.fn().mockReturnValue(true), getChannel: vi.fn().mockReturnValue('in_app'), }; const emailGateway: NotificationGateway = { send: vi.fn().mockResolvedValue({ success: true, channel: 'email', externalId: 'email-456', attemptedAt: new Date(), }), supportsChannel: vi.fn().mockReturnValue(true), isConfigured: vi.fn().mockReturnValue(true), getChannel: vi.fn().mockReturnValue('email'), }; const mockRegistry: NotificationGatewayRegistry = { register: vi.fn(), getGateway: vi.fn().mockImplementation((channel) => { if (channel === 'in_app') return inAppGateway; if (channel === 'email') return emailGateway; return null; }), getAllGateways: vi.fn().mockReturnValue([inAppGateway, emailGateway]), send: vi.fn().mockImplementation(async (notification) => { const gateway = mockRegistry.getGateway(notification.channel); if (gateway) { return gateway.send(notification); } return { success: false, channel: notification.channel, error: 'No gateway found', attemptedAt: new Date(), }; }), }; const inAppNotification = Notification.create({ id: 'test-1', recipientId: 'driver-1', type: 'system_announcement', title: 'Test', body: 'Test body', channel: 'in_app', }); const emailNotification = Notification.create({ id: 'test-2', recipientId: 'driver-1', type: 'race_registration_open', title: 'Test', body: 'Test body', channel: 'email', }); const inAppResult = await mockRegistry.send(inAppNotification); expect(inAppResult.success).toBe(true); expect(inAppResult.channel).toBe('in_app'); const emailResult = await mockRegistry.send(emailNotification); expect(emailResult.success).toBe(true); expect(emailResult.channel).toBe('email'); expect(emailResult.externalId).toBe('email-456'); }); });