"use strict";
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 common_1 = require("@nestjs/common");
const invitation_service_1 = require("../services/invitation.service");
const invitation_entity_1 = require("../entities/invitation.entity");
const user_entity_1 = require("../../auth/entities/user.entity");
const tenant_entity_1 = require("../../tenants/entities/tenant.entity");
const email_service_1 = require("../../email/services/email.service");
const invite_user_dto_1 = require("../dto/invite-user.dto");
describe('InvitationService', () => {
    let service;
    let invitationRepository;
    let userRepository;
    let tenantRepository;
    let emailService;
    let configService;
    const mockUser = {
        id: 'user-123',
        tenant_id: 'tenant-123',
        email: 'inviter@example.com',
        first_name: 'John',
        last_name: 'Doe',
        get fullName() {
            return 'John Doe';
        },
    };
    const mockTenant = {
        id: 'tenant-123',
        name: 'Test Organization',
        slug: 'test-org',
    };
    const mockInvitation = {
        id: 'inv-123',
        tenant_id: 'tenant-123',
        email: 'invited@example.com',
        token: 'secure-token-123',
        status: 'pending',
        expires_at: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000),
        created_at: new Date(),
        created_by: 'user-123',
        metadata: { role: 'member' },
    };
    beforeEach(async () => {
        const module = await testing_1.Test.createTestingModule({
            providers: [
                invitation_service_1.InvitationService,
                {
                    provide: (0, typeorm_1.getRepositoryToken)(invitation_entity_1.Invitation),
                    useValue: {
                        findOne: jest.fn(),
                        find: jest.fn(),
                        create: jest.fn(),
                        save: jest.fn(),
                        remove: jest.fn(),
                        createQueryBuilder: jest.fn(),
                    },
                },
                {
                    provide: (0, typeorm_1.getRepositoryToken)(user_entity_1.User),
                    useValue: {
                        findOne: jest.fn(),
                    },
                },
                {
                    provide: (0, typeorm_1.getRepositoryToken)(tenant_entity_1.Tenant),
                    useValue: {
                        findOne: jest.fn(),
                    },
                },
                {
                    provide: email_service_1.EmailService,
                    useValue: {
                        sendTemplateEmail: jest.fn().mockResolvedValue({ success: true }),
                    },
                },
                {
                    provide: config_1.ConfigService,
                    useValue: {
                        get: jest.fn((key, defaultValue) => {
                            const config = {
                                APP_URL: 'http://localhost:3000',
                                APP_NAME: 'Test App',
                            };
                            return config[key] || defaultValue;
                        }),
                    },
                },
            ],
        }).compile();
        service = module.get(invitation_service_1.InvitationService);
        invitationRepository = module.get((0, typeorm_1.getRepositoryToken)(invitation_entity_1.Invitation));
        userRepository = module.get((0, typeorm_1.getRepositoryToken)(user_entity_1.User));
        tenantRepository = module.get((0, typeorm_1.getRepositoryToken)(tenant_entity_1.Tenant));
        emailService = module.get(email_service_1.EmailService);
        configService = module.get(config_1.ConfigService);
        const mockQueryBuilder = {
            update: jest.fn().mockReturnThis(),
            set: jest.fn().mockReturnThis(),
            where: jest.fn().mockReturnThis(),
            andWhere: jest.fn().mockReturnThis(),
            execute: jest.fn().mockResolvedValue({ affected: 0 }),
        };
        invitationRepository.createQueryBuilder.mockReturnValue(mockQueryBuilder);
    });
    afterEach(() => {
        jest.clearAllMocks();
    });
    describe('invite', () => {
        const inviteDto = {
            email: 'newuser@example.com',
            role: invite_user_dto_1.InviteRole.MEMBER,
        };
        it('should create and send an invitation successfully', async () => {
            const createdInvitation = {
                ...mockInvitation,
                email: inviteDto.email.toLowerCase(),
                metadata: { role: inviteDto.role },
            };
            userRepository.findOne
                .mockResolvedValueOnce(null)
                .mockResolvedValueOnce(mockUser);
            invitationRepository.findOne.mockResolvedValue(null);
            tenantRepository.findOne.mockResolvedValue(mockTenant);
            invitationRepository.create.mockReturnValue(createdInvitation);
            invitationRepository.save.mockResolvedValue(createdInvitation);
            const result = await service.invite(inviteDto, 'user-123', 'tenant-123');
            expect(result).toHaveProperty('id');
            expect(result).toHaveProperty('email', inviteDto.email.toLowerCase());
            expect(result).toHaveProperty('role', inviteDto.role);
            expect(result).toHaveProperty('status', 'pending');
            expect(result).toHaveProperty('expires_at');
            expect(emailService.sendTemplateEmail).toHaveBeenCalledTimes(1);
        });
        it('should throw ConflictException if email already registered in tenant', async () => {
            userRepository.findOne.mockResolvedValue(mockUser);
            await expect(service.invite(inviteDto, 'user-123', 'tenant-123')).rejects.toThrow(common_1.ConflictException);
            expect(emailService.sendTemplateEmail).not.toHaveBeenCalled();
        });
        it('should throw ConflictException if pending invitation exists', async () => {
            userRepository.findOne.mockResolvedValue(null);
            invitationRepository.findOne.mockResolvedValue(mockInvitation);
            await expect(service.invite(inviteDto, 'user-123', 'tenant-123')).rejects.toThrow(common_1.ConflictException);
            expect(emailService.sendTemplateEmail).not.toHaveBeenCalled();
        });
        it('should throw NotFoundException if inviter not found', async () => {
            userRepository.findOne
                .mockResolvedValueOnce(null)
                .mockResolvedValueOnce(null);
            invitationRepository.findOne.mockResolvedValue(null);
            tenantRepository.findOne.mockResolvedValue(mockTenant);
            await expect(service.invite(inviteDto, 'user-123', 'tenant-123')).rejects.toThrow(common_1.NotFoundException);
        });
        it('should throw NotFoundException if tenant not found', async () => {
            userRepository.findOne
                .mockResolvedValueOnce(null)
                .mockResolvedValueOnce(mockUser);
            invitationRepository.findOne.mockResolvedValue(null);
            tenantRepository.findOne.mockResolvedValue(null);
            await expect(service.invite(inviteDto, 'user-123', 'tenant-123')).rejects.toThrow(common_1.NotFoundException);
        });
        it('should normalize email to lowercase', async () => {
            const uppercaseEmailDto = {
                email: 'UPPERCASE@EXAMPLE.COM',
                role: invite_user_dto_1.InviteRole.ADMIN,
            };
            userRepository.findOne
                .mockResolvedValueOnce(null)
                .mockResolvedValueOnce(mockUser);
            invitationRepository.findOne.mockResolvedValue(null);
            tenantRepository.findOne.mockResolvedValue(mockTenant);
            invitationRepository.create.mockReturnValue(mockInvitation);
            invitationRepository.save.mockResolvedValue(mockInvitation);
            await service.invite(uppercaseEmailDto, 'user-123', 'tenant-123');
            expect(userRepository.findOne).toHaveBeenCalledWith({
                where: { email: 'uppercase@example.com', tenant_id: 'tenant-123' },
            });
        });
    });
    describe('findAllByTenant', () => {
        it('should return all invitations for a tenant', async () => {
            const invitations = [mockInvitation];
            invitationRepository.find.mockResolvedValue(invitations);
            const result = await service.findAllByTenant('tenant-123');
            expect(result).toHaveLength(1);
            expect(result[0]).toHaveProperty('id', 'inv-123');
            expect(result[0]).toHaveProperty('email', 'invited@example.com');
        });
        it('should return empty array when no invitations exist', async () => {
            invitationRepository.find.mockResolvedValue([]);
            const result = await service.findAllByTenant('tenant-123');
            expect(result).toEqual([]);
        });
    });
    describe('resend', () => {
        it('should resend invitation with new token and expiration', async () => {
            const originalToken = mockInvitation.token;
            invitationRepository.findOne.mockResolvedValue({ ...mockInvitation });
            userRepository.findOne.mockResolvedValue(mockUser);
            tenantRepository.findOne.mockResolvedValue(mockTenant);
            invitationRepository.save.mockImplementation(async (inv) => inv);
            const result = await service.resend(originalToken, 'user-123', 'tenant-123');
            expect(result).toHaveProperty('id');
            expect(invitationRepository.save).toHaveBeenCalled();
            expect(emailService.sendTemplateEmail).toHaveBeenCalledTimes(1);
        });
        it('should throw NotFoundException if invitation not found', async () => {
            invitationRepository.findOne.mockResolvedValue(null);
            await expect(service.resend('invalid-token', 'user-123', 'tenant-123')).rejects.toThrow(common_1.NotFoundException);
        });
        it('should throw BadRequestException if invitation is not pending', async () => {
            const acceptedInvitation = { ...mockInvitation, status: 'accepted' };
            invitationRepository.findOne.mockResolvedValue(acceptedInvitation);
            await expect(service.resend('token', 'user-123', 'tenant-123')).rejects.toThrow(common_1.BadRequestException);
        });
    });
    describe('cancel', () => {
        it('should cancel a pending invitation', async () => {
            invitationRepository.findOne.mockResolvedValue(mockInvitation);
            invitationRepository.remove.mockResolvedValue(mockInvitation);
            await service.cancel('inv-123', 'tenant-123');
            expect(invitationRepository.remove).toHaveBeenCalledWith(mockInvitation);
        });
        it('should throw NotFoundException if invitation not found', async () => {
            invitationRepository.findOne.mockResolvedValue(null);
            await expect(service.cancel('invalid-id', 'tenant-123')).rejects.toThrow(common_1.NotFoundException);
        });
        it('should throw BadRequestException if invitation is not pending', async () => {
            const acceptedInvitation = { ...mockInvitation, status: 'accepted' };
            invitationRepository.findOne.mockResolvedValue(acceptedInvitation);
            await expect(service.cancel('inv-123', 'tenant-123')).rejects.toThrow(common_1.BadRequestException);
        });
    });
    describe('findByToken', () => {
        it('should return invitation by token', async () => {
            invitationRepository.findOne.mockResolvedValue(mockInvitation);
            const result = await service.findByToken('secure-token-123');
            expect(result).toEqual(mockInvitation);
        });
        it('should return null if invitation not found', async () => {
            invitationRepository.findOne.mockResolvedValue(null);
            const result = await service.findByToken('invalid-token');
            expect(result).toBeNull();
        });
        it('should mark invitation as expired if past expiration date', async () => {
            const expiredInvitation = {
                ...mockInvitation,
                expires_at: new Date(Date.now() - 1000),
                status: 'pending',
            };
            invitationRepository.findOne.mockResolvedValue(expiredInvitation);
            invitationRepository.save.mockResolvedValue({ ...expiredInvitation, status: 'expired' });
            const result = await service.findByToken('token');
            expect(result?.status).toBe('expired');
            expect(invitationRepository.save).toHaveBeenCalled();
        });
    });
});
//# sourceMappingURL=invitation.service.spec.js.map