rwadurian/backend/services/backup-service/test/unit/application/store-backup-share.handler....

128 lines
4.6 KiB
TypeScript

import { Test, TestingModule } from '@nestjs/testing';
import { StoreBackupShareHandler } from '../../../src/application/commands/store-backup-share/store-backup-share.handler';
import { StoreBackupShareCommand } from '../../../src/application/commands/store-backup-share/store-backup-share.command';
import { BACKUP_SHARE_REPOSITORY } from '../../../src/domain';
import { AesEncryptionService } from '../../../src/infrastructure/crypto/aes-encryption.service';
import { AuditLogRepository } from '../../../src/infrastructure/persistence/repositories/audit-log.repository';
import { ApplicationError } from '../../../src/application/errors/application.error';
describe('StoreBackupShareHandler', () => {
let handler: StoreBackupShareHandler;
let mockRepository: any;
let mockEncryptionService: any;
let mockAuditLogRepository: any;
beforeEach(async () => {
mockRepository = {
findByUserId: jest.fn(),
findByPublicKey: jest.fn(),
save: jest.fn(),
};
mockEncryptionService = {
encrypt: jest.fn().mockResolvedValue({
encrypted: 'double-encrypted-data',
keyId: 'key-v1',
}),
};
mockAuditLogRepository = {
log: jest.fn().mockResolvedValue(undefined),
};
const module: TestingModule = await Test.createTestingModule({
providers: [
StoreBackupShareHandler,
{ provide: BACKUP_SHARE_REPOSITORY, useValue: mockRepository },
{ provide: AesEncryptionService, useValue: mockEncryptionService },
{ provide: AuditLogRepository, useValue: mockAuditLogRepository },
],
}).compile();
handler = module.get<StoreBackupShareHandler>(StoreBackupShareHandler);
});
it('should be defined', () => {
expect(handler).toBeDefined();
});
describe('execute', () => {
const validCommand = new StoreBackupShareCommand(
'12345',
1001,
'02' + 'a'.repeat(64),
'encrypted-share-data',
'identity-service',
'127.0.0.1',
);
it('should store backup share successfully', async () => {
mockRepository.findByUserId.mockResolvedValue(null);
mockRepository.findByPublicKey.mockResolvedValue(null);
mockRepository.save.mockImplementation((share: any) => {
share.setShareId(BigInt(1));
return Promise.resolve(share);
});
const result = await handler.execute(validCommand);
expect(result.shareId).toBe('1');
expect(mockRepository.findByUserId).toHaveBeenCalledWith(BigInt(12345));
expect(mockRepository.findByPublicKey).toHaveBeenCalledWith(validCommand.publicKey);
expect(mockEncryptionService.encrypt).toHaveBeenCalledWith(validCommand.encryptedShareData);
expect(mockRepository.save).toHaveBeenCalled();
expect(mockAuditLogRepository.log).toHaveBeenCalledWith(
expect.objectContaining({
action: 'STORE',
success: true,
}),
);
});
it('should throw error if share already exists for user', async () => {
mockRepository.findByUserId.mockResolvedValue({ userId: BigInt(12345) });
await expect(handler.execute(validCommand)).rejects.toThrow(ApplicationError);
await expect(handler.execute(validCommand)).rejects.toThrow(
'Backup share already exists for this user',
);
});
it('should throw error if share already exists for public key', async () => {
mockRepository.findByUserId.mockResolvedValue(null);
mockRepository.findByPublicKey.mockResolvedValue({ publicKey: validCommand.publicKey });
await expect(handler.execute(validCommand)).rejects.toThrow(ApplicationError);
await expect(handler.execute(validCommand)).rejects.toThrow(
'Backup share already exists for this public key',
);
});
it('should use custom threshold and totalParties if provided', async () => {
mockRepository.findByUserId.mockResolvedValue(null);
mockRepository.findByPublicKey.mockResolvedValue(null);
mockRepository.save.mockImplementation((share: any) => {
share.setShareId(BigInt(1));
expect(share.threshold).toBe(3);
expect(share.totalParties).toBe(5);
return Promise.resolve(share);
});
const commandWithCustomParams = new StoreBackupShareCommand(
'12345',
1001,
'02' + 'a'.repeat(64),
'encrypted-share-data',
'identity-service',
'127.0.0.1',
3, // threshold
5, // totalParties
);
await handler.execute(commandWithCustomParams);
expect(mockRepository.save).toHaveBeenCalled();
});
});
});