"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
    var ownKeys = function(o) {
        ownKeys = Object.getOwnPropertyNames || function (o) {
            var ar = [];
            for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
            return ar;
        };
        return ownKeys(o);
    };
    return function (mod) {
        if (mod && mod.__esModule) return mod;
        var result = {};
        if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
        __setModuleDefault(result, mod);
        return result;
    };
})();
Object.defineProperty(exports, "__esModule", { value: true });
const testing_1 = require("@nestjs/testing");
const typeorm_1 = require("@nestjs/typeorm");
const config_1 = require("@nestjs/config");
const push_notification_service_1 = require("../services/push-notification.service");
const entities_1 = require("../entities");
const webpush = __importStar(require("web-push"));
describe('PushNotificationService', () => {
    let service;
    let deviceRepository;
    let logRepository;
    let configService;
    const mockDevice = {
        id: 'device-1',
        tenant_id: 'tenant-1',
        user_id: 'user-1',
        device_type: 'web',
        device_token: JSON.stringify({
            endpoint: 'https://fcm.googleapis.com/fcm/send/test',
            keys: { p256dh: 'test-key', auth: 'test-auth' },
        }),
        device_name: 'Chrome on Windows',
        browser: 'Chrome',
        browser_version: '120',
        os: 'Windows',
        os_version: '11',
        is_active: true,
        last_used_at: new Date(),
        created_at: new Date(),
    };
    const mockDeviceRepository = {
        find: jest.fn(),
        save: jest.fn(),
    };
    const mockLogRepository = {
        save: jest.fn(),
    };
    const createMockConfigService = () => ({
        get: jest.fn((key, defaultValue) => {
            const config = {
                VAPID_PUBLIC_KEY: 'BN4GvZtEZiZuqFJiLNpT1234567890',
                VAPID_PRIVATE_KEY: 'aB3cDefGh4IjKlM5nOpQr6StUvWxYz',
                VAPID_SUBJECT: 'mailto:admin@example.com',
            };
            return config[key] || defaultValue;
        }),
    });
    let mockConfigService;
    beforeEach(async () => {
        jest.clearAllMocks();
        mockConfigService = createMockConfigService();
        const module = await testing_1.Test.createTestingModule({
            providers: [
                push_notification_service_1.PushNotificationService,
                {
                    provide: (0, typeorm_1.getRepositoryToken)(entities_1.UserDevice),
                    useValue: mockDeviceRepository,
                },
                {
                    provide: (0, typeorm_1.getRepositoryToken)(entities_1.NotificationLog),
                    useValue: mockLogRepository,
                },
                {
                    provide: config_1.ConfigService,
                    useValue: mockConfigService,
                },
            ],
        }).compile();
        service = module.get(push_notification_service_1.PushNotificationService);
        deviceRepository = module.get((0, typeorm_1.getRepositoryToken)(entities_1.UserDevice));
        logRepository = module.get((0, typeorm_1.getRepositoryToken)(entities_1.NotificationLog));
        configService = module.get(config_1.ConfigService);
        service.onModuleInit();
    });
    describe('onModuleInit', () => {
        it('should configure VAPID if keys are provided', () => {
            expect(webpush.setVapidDetails).toHaveBeenCalledWith('mailto:admin@example.com', 'BN4GvZtEZiZuqFJiLNpT1234567890', 'aB3cDefGh4IjKlM5nOpQr6StUvWxYz');
            expect(service.isEnabled()).toBe(true);
        });
        it('should not configure if keys are missing', async () => {
            jest.clearAllMocks();
            mockConfigService.get.mockReturnValue(undefined);
            const module = await testing_1.Test.createTestingModule({
                providers: [
                    push_notification_service_1.PushNotificationService,
                    {
                        provide: (0, typeorm_1.getRepositoryToken)(entities_1.UserDevice),
                        useValue: mockDeviceRepository,
                    },
                    {
                        provide: (0, typeorm_1.getRepositoryToken)(entities_1.NotificationLog),
                        useValue: mockLogRepository,
                    },
                    {
                        provide: config_1.ConfigService,
                        useValue: { get: () => undefined },
                    },
                ],
            }).compile();
            const unconfiguredService = module.get(push_notification_service_1.PushNotificationService);
            unconfiguredService.onModuleInit();
            expect(unconfiguredService.isEnabled()).toBe(false);
        });
    });
    describe('getVapidPublicKey', () => {
        it('should return VAPID public key when configured', () => {
            const key = service.getVapidPublicKey();
            expect(key).toBe('BN4GvZtEZiZuqFJiLNpT1234567890');
        });
    });
    describe('sendToUser', () => {
        const payload = {
            title: 'Test Notification',
            body: 'This is a test',
            url: '/test',
        };
        it('should send to all active devices', async () => {
            mockDeviceRepository.find.mockResolvedValue([mockDevice]);
            webpush.sendNotification.mockResolvedValue({});
            mockDeviceRepository.save.mockResolvedValue(mockDevice);
            const results = await service.sendToUser('user-1', 'tenant-1', payload);
            expect(results).toHaveLength(1);
            expect(results[0].success).toBe(true);
            expect(webpush.sendNotification).toHaveBeenCalled();
        });
        it('should return empty array if no devices', async () => {
            mockDeviceRepository.find.mockResolvedValue([]);
            const results = await service.sendToUser('user-1', 'tenant-1', payload);
            expect(results).toHaveLength(0);
        });
        it('should handle expired subscription (410)', async () => {
            mockDeviceRepository.find.mockResolvedValue([mockDevice]);
            webpush.sendNotification.mockRejectedValue({
                statusCode: 410,
                message: 'Subscription expired',
            });
            mockDeviceRepository.save.mockResolvedValue({
                ...mockDevice,
                is_active: false,
            });
            const results = await service.sendToUser('user-1', 'tenant-1', payload);
            expect(results).toHaveLength(1);
            expect(results[0].success).toBe(false);
            expect(results[0].statusCode).toBe(410);
            expect(mockDeviceRepository.save).toHaveBeenCalledWith(expect.objectContaining({ is_active: false }));
        });
        it('should create log on success with notificationId', async () => {
            mockDeviceRepository.find.mockResolvedValue([mockDevice]);
            webpush.sendNotification.mockResolvedValue({});
            mockDeviceRepository.save.mockResolvedValue(mockDevice);
            mockLogRepository.save.mockResolvedValue({});
            await service.sendToUser('user-1', 'tenant-1', payload, 'notification-1');
            expect(mockLogRepository.save).toHaveBeenCalledWith(expect.objectContaining({
                notification_id: 'notification-1',
                channel: 'push',
                status: 'sent',
                provider: 'web-push',
            }));
        });
    });
    describe('validateSubscription', () => {
        it('should return true for valid subscription', () => {
            const validSubscription = JSON.stringify({
                endpoint: 'https://fcm.googleapis.com/fcm/send/test',
                keys: { p256dh: 'test-key', auth: 'test-auth' },
            });
            expect(service.validateSubscription(validSubscription)).toBe(true);
        });
        it('should return false for missing endpoint', () => {
            const invalidSubscription = JSON.stringify({
                keys: { p256dh: 'test-key', auth: 'test-auth' },
            });
            expect(service.validateSubscription(invalidSubscription)).toBe(false);
        });
        it('should return false for missing keys', () => {
            const invalidSubscription = JSON.stringify({
                endpoint: 'https://fcm.googleapis.com/fcm/send/test',
            });
            expect(service.validateSubscription(invalidSubscription)).toBe(false);
        });
        it('should return false for invalid JSON', () => {
            expect(service.validateSubscription('invalid-json')).toBe(false);
        });
    });
    describe('sendBroadcast', () => {
        const payload = {
            title: 'Broadcast Test',
            body: 'This is a broadcast',
        };
        it('should send to all tenant devices', async () => {
            mockDeviceRepository.find.mockResolvedValue([mockDevice, mockDevice]);
            webpush.sendNotification.mockResolvedValue({});
            mockDeviceRepository.save.mockResolvedValue(mockDevice);
            const result = await service.sendBroadcast('tenant-1', payload);
            expect(result.total).toBe(2);
            expect(result.successful).toBe(2);
            expect(result.failed).toBe(0);
        });
        it('should count failures correctly', async () => {
            mockDeviceRepository.find.mockResolvedValue([mockDevice, mockDevice]);
            webpush.sendNotification
                .mockResolvedValueOnce({})
                .mockRejectedValueOnce(new Error('Failed'));
            mockDeviceRepository.save.mockResolvedValue(mockDevice);
            const result = await service.sendBroadcast('tenant-1', payload);
            expect(result.total).toBe(2);
            expect(result.successful).toBe(1);
            expect(result.failed).toBe(1);
        });
        it('should return zeros when not configured', async () => {
            const module = await testing_1.Test.createTestingModule({
                providers: [
                    push_notification_service_1.PushNotificationService,
                    {
                        provide: (0, typeorm_1.getRepositoryToken)(entities_1.UserDevice),
                        useValue: mockDeviceRepository,
                    },
                    {
                        provide: (0, typeorm_1.getRepositoryToken)(entities_1.NotificationLog),
                        useValue: mockLogRepository,
                    },
                    {
                        provide: config_1.ConfigService,
                        useValue: { get: () => undefined },
                    },
                ],
            }).compile();
            const unconfiguredService = module.get(push_notification_service_1.PushNotificationService);
            unconfiguredService.onModuleInit();
            const result = await unconfiguredService.sendBroadcast('tenant-1', payload);
            expect(result).toEqual({ total: 0, successful: 0, failed: 0 });
            expect(mockDeviceRepository.find).not.toHaveBeenCalled();
        });
        it('should return zeros when no devices in tenant', async () => {
            mockDeviceRepository.find.mockResolvedValue([]);
            const result = await service.sendBroadcast('tenant-1', payload);
            expect(result.total).toBe(0);
            expect(result.successful).toBe(0);
            expect(result.failed).toBe(0);
        });
    });
    describe('sendToDevice', () => {
        const testPayload = JSON.stringify({
            title: 'Direct Device Test',
            body: 'Testing direct send',
        });
        it('should send push to valid device', async () => {
            webpush.sendNotification.mockResolvedValue({});
            mockDeviceRepository.save.mockResolvedValue(mockDevice);
            const result = await service.sendToDevice(mockDevice, testPayload);
            expect(result.success).toBe(true);
            expect(result.deviceId).toBe('device-1');
            expect(webpush.sendNotification).toHaveBeenCalledWith(JSON.parse(mockDevice.device_token), testPayload);
        });
        it('should update last_used_at on success', async () => {
            webpush.sendNotification.mockResolvedValue({});
            mockDeviceRepository.save.mockResolvedValue(mockDevice);
            await service.sendToDevice(mockDevice, testPayload);
            expect(mockDeviceRepository.save).toHaveBeenCalledWith(expect.objectContaining({
                last_used_at: expect.any(Date),
            }));
        });
        it('should create log on success with notificationId', async () => {
            webpush.sendNotification.mockResolvedValue({});
            mockDeviceRepository.save.mockResolvedValue(mockDevice);
            mockLogRepository.save.mockResolvedValue({});
            await service.sendToDevice(mockDevice, testPayload, 'notif-123');
            expect(mockLogRepository.save).toHaveBeenCalledWith(expect.objectContaining({
                notification_id: 'notif-123',
                channel: 'push',
                status: 'sent',
                provider: 'web-push',
                device_id: 'device-1',
                delivered_at: expect.any(Date),
            }));
        });
        it('should not create log when notificationId is not provided', async () => {
            webpush.sendNotification.mockResolvedValue({});
            mockDeviceRepository.save.mockResolvedValue(mockDevice);
            await service.sendToDevice(mockDevice, testPayload);
            expect(mockLogRepository.save).not.toHaveBeenCalled();
        });
        it('should handle invalid device (404)', async () => {
            webpush.sendNotification.mockRejectedValue({
                statusCode: 404,
                message: 'Subscription not found',
            });
            mockDeviceRepository.save.mockResolvedValue({
                ...mockDevice,
                is_active: false,
            });
            const result = await service.sendToDevice(mockDevice, testPayload);
            expect(result.success).toBe(false);
            expect(result.statusCode).toBe(404);
            expect(mockDeviceRepository.save).toHaveBeenCalledWith(expect.objectContaining({ is_active: false }));
        });
        it('should handle generic send error', async () => {
            webpush.sendNotification.mockRejectedValue({
                statusCode: 500,
                message: 'Internal server error',
            });
            const result = await service.sendToDevice(mockDevice, testPayload);
            expect(result.success).toBe(false);
            expect(result.statusCode).toBe(500);
            expect(result.error).toBe('Internal server error');
        });
        it('should create failure log with notificationId', async () => {
            webpush.sendNotification.mockRejectedValue({
                statusCode: 500,
                message: 'Server error',
            });
            mockLogRepository.save.mockResolvedValue({});
            await service.sendToDevice(mockDevice, testPayload, 'notif-456');
            expect(mockLogRepository.save).toHaveBeenCalledWith(expect.objectContaining({
                notification_id: 'notif-456',
                channel: 'push',
                status: 'failed',
                provider: 'web-push',
                device_id: 'device-1',
                error_code: '500',
                error_message: 'Server error',
            }));
        });
        it('should not create failure log without notificationId', async () => {
            webpush.sendNotification.mockRejectedValue({
                statusCode: 500,
                message: 'Server error',
            });
            await service.sendToDevice(mockDevice, testPayload);
            expect(mockLogRepository.save).not.toHaveBeenCalled();
        });
    });
    describe('sendToUser - additional cases', () => {
        const payload = {
            title: 'Test Notification',
            body: 'This is a test',
        };
        it('should return empty array when service not configured', async () => {
            const module = await testing_1.Test.createTestingModule({
                providers: [
                    push_notification_service_1.PushNotificationService,
                    {
                        provide: (0, typeorm_1.getRepositoryToken)(entities_1.UserDevice),
                        useValue: mockDeviceRepository,
                    },
                    {
                        provide: (0, typeorm_1.getRepositoryToken)(entities_1.NotificationLog),
                        useValue: mockLogRepository,
                    },
                    {
                        provide: config_1.ConfigService,
                        useValue: { get: () => undefined },
                    },
                ],
            }).compile();
            const unconfiguredService = module.get(push_notification_service_1.PushNotificationService);
            unconfiguredService.onModuleInit();
            const results = await unconfiguredService.sendToUser('user-1', 'tenant-1', payload);
            expect(results).toHaveLength(0);
            expect(mockDeviceRepository.find).not.toHaveBeenCalled();
        });
        it('should send to multiple devices and aggregate results', async () => {
            const device2 = {
                ...mockDevice,
                id: 'device-2',
                device_name: 'Firefox on Linux',
            };
            mockDeviceRepository.find.mockResolvedValue([mockDevice, device2]);
            webpush.sendNotification.mockResolvedValue({});
            mockDeviceRepository.save.mockResolvedValue(mockDevice);
            const results = await service.sendToUser('user-1', 'tenant-1', payload);
            expect(results).toHaveLength(2);
            expect(results.every((r) => r.success)).toBe(true);
            expect(webpush.sendNotification).toHaveBeenCalledTimes(2);
        });
        it('should continue sending to other devices after one fails', async () => {
            const device2 = {
                ...mockDevice,
                id: 'device-2',
            };
            mockDeviceRepository.find.mockResolvedValue([mockDevice, device2]);
            webpush.sendNotification
                .mockRejectedValueOnce({ statusCode: 500, message: 'Error' })
                .mockResolvedValueOnce({});
            mockDeviceRepository.save.mockResolvedValue(mockDevice);
            const results = await service.sendToUser('user-1', 'tenant-1', payload);
            expect(results).toHaveLength(2);
            expect(results[0].success).toBe(false);
            expect(results[1].success).toBe(true);
        });
        it('should include custom payload data and actions', async () => {
            const customPayload = {
                title: 'Test',
                body: 'Test body',
                icon: '/custom-icon.png',
                badge: '/custom-badge.png',
                url: '/custom-url',
                data: { orderId: '123' },
                actions: [{ action: 'open', title: 'Open' }],
            };
            mockDeviceRepository.find.mockResolvedValue([mockDevice]);
            webpush.sendNotification.mockResolvedValue({});
            mockDeviceRepository.save.mockResolvedValue(mockDevice);
            await service.sendToUser('user-1', 'tenant-1', customPayload);
            const sentPayload = JSON.parse(webpush.sendNotification.mock.calls[0][1]);
            expect(sentPayload.icon).toBe('/custom-icon.png');
            expect(sentPayload.badge).toBe('/custom-badge.png');
            expect(sentPayload.url).toBe('/custom-url');
            expect(sentPayload.data).toEqual({ orderId: '123' });
            expect(sentPayload.actions).toEqual([{ action: 'open', title: 'Open' }]);
        });
    });
    describe('getVapidPublicKey - additional cases', () => {
        it('should return null when not configured', async () => {
            const module = await testing_1.Test.createTestingModule({
                providers: [
                    push_notification_service_1.PushNotificationService,
                    {
                        provide: (0, typeorm_1.getRepositoryToken)(entities_1.UserDevice),
                        useValue: mockDeviceRepository,
                    },
                    {
                        provide: (0, typeorm_1.getRepositoryToken)(entities_1.NotificationLog),
                        useValue: mockLogRepository,
                    },
                    {
                        provide: config_1.ConfigService,
                        useValue: { get: () => undefined },
                    },
                ],
            }).compile();
            const unconfiguredService = module.get(push_notification_service_1.PushNotificationService);
            unconfiguredService.onModuleInit();
            expect(unconfiguredService.getVapidPublicKey()).toBeNull();
        });
    });
    describe('validateSubscription - additional cases', () => {
        it('should return false for missing p256dh key', () => {
            const invalidSubscription = JSON.stringify({
                endpoint: 'https://fcm.googleapis.com/fcm/send/test',
                keys: { auth: 'test-auth' },
            });
            expect(service.validateSubscription(invalidSubscription)).toBe(false);
        });
        it('should return false for missing auth key', () => {
            const invalidSubscription = JSON.stringify({
                endpoint: 'https://fcm.googleapis.com/fcm/send/test',
                keys: { p256dh: 'test-key' },
            });
            expect(service.validateSubscription(invalidSubscription)).toBe(false);
        });
        it('should return false for empty keys object', () => {
            const invalidSubscription = JSON.stringify({
                endpoint: 'https://fcm.googleapis.com/fcm/send/test',
                keys: {},
            });
            expect(service.validateSubscription(invalidSubscription)).toBe(false);
        });
        it('should return false for null keys', () => {
            const invalidSubscription = JSON.stringify({
                endpoint: 'https://fcm.googleapis.com/fcm/send/test',
                keys: null,
            });
            expect(service.validateSubscription(invalidSubscription)).toBe(false);
        });
        it('should return false for empty string', () => {
            expect(service.validateSubscription('')).toBe(false);
        });
    });
    describe('onModuleInit - additional cases', () => {
        it('should handle setVapidDetails error gracefully', async () => {
            jest.clearAllMocks();
            webpush.setVapidDetails.mockImplementation(() => {
                throw new Error('Invalid VAPID keys');
            });
            const module = await testing_1.Test.createTestingModule({
                providers: [
                    push_notification_service_1.PushNotificationService,
                    {
                        provide: (0, typeorm_1.getRepositoryToken)(entities_1.UserDevice),
                        useValue: mockDeviceRepository,
                    },
                    {
                        provide: (0, typeorm_1.getRepositoryToken)(entities_1.NotificationLog),
                        useValue: mockLogRepository,
                    },
                    {
                        provide: config_1.ConfigService,
                        useValue: mockConfigService,
                    },
                ],
            }).compile();
            const errorService = module.get(push_notification_service_1.PushNotificationService);
            expect(() => errorService.onModuleInit()).not.toThrow();
            expect(errorService.isEnabled()).toBe(false);
        });
        it('should use default VAPID subject when not provided', async () => {
            jest.clearAllMocks();
            const configWithoutSubject = {
                get: jest.fn((key, defaultValue) => {
                    const config = {
                        VAPID_PUBLIC_KEY: 'BN4GvZtEZiZuqFJiLNpT1234567890',
                        VAPID_PRIVATE_KEY: 'aB3cDefGh4IjKlM5nOpQr6StUvWxYz',
                    };
                    return config[key] || defaultValue;
                }),
            };
            const module = await testing_1.Test.createTestingModule({
                providers: [
                    push_notification_service_1.PushNotificationService,
                    {
                        provide: (0, typeorm_1.getRepositoryToken)(entities_1.UserDevice),
                        useValue: mockDeviceRepository,
                    },
                    {
                        provide: (0, typeorm_1.getRepositoryToken)(entities_1.NotificationLog),
                        useValue: mockLogRepository,
                    },
                    {
                        provide: config_1.ConfigService,
                        useValue: configWithoutSubject,
                    },
                ],
            }).compile();
            const serviceWithDefault = module.get(push_notification_service_1.PushNotificationService);
            serviceWithDefault.onModuleInit();
            expect(webpush.setVapidDetails).toHaveBeenCalledWith('mailto:admin@example.com', 'BN4GvZtEZiZuqFJiLNpT1234567890', 'aB3cDefGh4IjKlM5nOpQr6StUvWxYz');
        });
    });
});
//# sourceMappingURL=push-notification.service.spec.js.map