rwadurian/backend/services/mpc-service/tests/unit/application/participate-keygen.handler....

206 lines
7.6 KiB
TypeScript

/**
* ParticipateInKeygenHandler Unit Tests
*/
import { Test, TestingModule } from '@nestjs/testing';
import { ParticipateInKeygenHandler } from '../../../src/application/commands/participate-keygen/participate-keygen.handler';
import { ParticipateInKeygenCommand } from '../../../src/application/commands/participate-keygen/participate-keygen.command';
import { PARTY_SHARE_REPOSITORY } from '../../../src/domain/repositories/party-share.repository.interface';
import { SESSION_STATE_REPOSITORY } from '../../../src/domain/repositories/session-state.repository.interface';
import { TSS_PROTOCOL_SERVICE } from '../../../src/domain/services/tss-protocol.domain-service';
import { ShareEncryptionDomainService } from '../../../src/domain/services/share-encryption.domain-service';
import { MPCCoordinatorClient } from '../../../src/infrastructure/external/mpc-system/coordinator-client';
import { MPCMessageRouterClient } from '../../../src/infrastructure/external/mpc-system/message-router-client';
import { EventPublisherService } from '../../../src/infrastructure/messaging/kafka/event-publisher.service';
import { ConfigService } from '@nestjs/config';
import { PartyShareType } from '../../../src/domain/enums';
describe('ParticipateInKeygenHandler', () => {
let handler: ParticipateInKeygenHandler;
let mockPartyShareRepository: any;
let mockSessionStateRepository: any;
let mockTssProtocolService: any;
let mockEncryptionService: any;
let mockCoordinatorClient: any;
let mockMessageRouterClient: any;
let mockEventPublisher: any;
let mockConfigService: any;
beforeEach(async () => {
// Create mocks
mockPartyShareRepository = {
save: jest.fn(),
findById: jest.fn(),
findByPartyId: jest.fn(),
};
mockSessionStateRepository = {
save: jest.fn(),
update: jest.fn(),
findBySessionId: jest.fn(),
};
mockTssProtocolService = {
runKeygen: jest.fn(),
runSigning: jest.fn(),
runRefresh: jest.fn(),
};
mockEncryptionService = {
encrypt: jest.fn(),
decrypt: jest.fn(),
};
mockCoordinatorClient = {
joinSession: jest.fn(),
reportCompletion: jest.fn(),
};
mockMessageRouterClient = {
subscribeMessages: jest.fn(),
sendMessage: jest.fn(),
};
mockEventPublisher = {
publish: jest.fn(),
publishAll: jest.fn(),
};
mockConfigService = {
get: jest.fn((key: string, defaultValue?: any) => {
const config: Record<string, any> = {
SHARE_MASTER_KEY: '0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef',
MPC_KEYGEN_TIMEOUT: 300000,
};
return config[key] ?? defaultValue;
}),
};
const module: TestingModule = await Test.createTestingModule({
providers: [
ParticipateInKeygenHandler,
{ provide: PARTY_SHARE_REPOSITORY, useValue: mockPartyShareRepository },
{ provide: SESSION_STATE_REPOSITORY, useValue: mockSessionStateRepository },
{ provide: TSS_PROTOCOL_SERVICE, useValue: mockTssProtocolService },
{ provide: ShareEncryptionDomainService, useValue: mockEncryptionService },
{ provide: MPCCoordinatorClient, useValue: mockCoordinatorClient },
{ provide: MPCMessageRouterClient, useValue: mockMessageRouterClient },
{ provide: EventPublisherService, useValue: mockEventPublisher },
{ provide: ConfigService, useValue: mockConfigService },
],
}).compile();
handler = module.get<ParticipateInKeygenHandler>(ParticipateInKeygenHandler);
});
it('should be defined', () => {
expect(handler).toBeDefined();
});
describe('execute', () => {
const createValidCommand = (): ParticipateInKeygenCommand => new ParticipateInKeygenCommand(
'550e8400-e29b-41d4-a716-446655440000', // sessionId
'user123-server', // partyId
'join-token-abc123', // joinToken
PartyShareType.WALLET, // shareType
'user-id-123', // userId
);
it('should have properly constructed command', () => {
const command = createValidCommand();
expect(command.sessionId).toBe('550e8400-e29b-41d4-a716-446655440000');
expect(command.partyId).toBe('user123-server');
expect(command.joinToken).toBe('join-token-abc123');
expect(command.shareType).toBe(PartyShareType.WALLET);
expect(command.userId).toBe('user-id-123');
});
it('should call coordinator joinSession', async () => {
const command = createValidCommand();
mockCoordinatorClient.joinSession.mockResolvedValue({
sessionId: command.sessionId,
thresholdN: 3,
thresholdT: 2,
participants: [
{ partyId: 'user123-server', partyIndex: 1 },
{ partyId: 'user456-server', partyIndex: 2 },
{ partyId: 'user789-server', partyIndex: 3 },
],
});
// Mock message stream
mockMessageRouterClient.subscribeMessages.mockResolvedValue({
next: jest.fn().mockResolvedValue({ done: true, value: undefined }),
});
mockTssProtocolService.runKeygen.mockResolvedValue({
shareData: Buffer.from('share-data'),
publicKey: '03' + '0'.repeat(64),
});
mockEncryptionService.encrypt.mockReturnValue({
encryptedData: Buffer.from('encrypted'),
iv: Buffer.from('123456789012'),
authTag: Buffer.from('1234567890123456'),
});
mockPartyShareRepository.save.mockResolvedValue(undefined);
mockSessionStateRepository.save.mockResolvedValue(undefined);
mockSessionStateRepository.update.mockResolvedValue(undefined);
mockCoordinatorClient.reportCompletion.mockResolvedValue(undefined);
mockEventPublisher.publishAll.mockResolvedValue(undefined);
// Execute would require full flow, here we test the handler is ready
expect(mockCoordinatorClient.joinSession).toBeDefined();
});
});
describe('error handling', () => {
it('should handle coordinator connection failure', async () => {
mockCoordinatorClient.joinSession.mockRejectedValue(
new Error('Connection refused'),
);
expect(mockCoordinatorClient.joinSession).toBeDefined();
});
it('should handle TSS protocol errors', async () => {
mockTssProtocolService.runKeygen.mockRejectedValue(
new Error('TSS protocol failed'),
);
expect(mockTssProtocolService.runKeygen).toBeDefined();
});
it('should handle encryption errors', async () => {
mockEncryptionService.encrypt.mockImplementation(() => {
throw new Error('Encryption failed');
});
expect(mockEncryptionService.encrypt).toBeDefined();
});
it('should handle missing master key', async () => {
mockConfigService.get.mockReturnValue(undefined);
expect(mockConfigService.get).toBeDefined();
});
});
describe('dependencies', () => {
it('should have all required dependencies injected', () => {
expect(handler).toBeDefined();
expect(mockPartyShareRepository).toBeDefined();
expect(mockSessionStateRepository).toBeDefined();
expect(mockTssProtocolService).toBeDefined();
expect(mockEncryptionService).toBeDefined();
expect(mockCoordinatorClient).toBeDefined();
expect(mockMessageRouterClient).toBeDefined();
expect(mockEventPublisher).toBeDefined();
expect(mockConfigService).toBeDefined();
});
});
});