rwadurian/backend/services/mpc-service/tests/unit/infrastructure/party-share.mapper.spec.ts

190 lines
6.4 KiB
TypeScript

/**
* PartyShareMapper Unit Tests
*/
import { PartyShareMapper, PartySharePersistence } from '../../../src/infrastructure/persistence/mappers/party-share.mapper';
import { PartyShare } from '../../../src/domain/entities/party-share.entity';
import {
ShareId,
PartyId,
SessionId,
Threshold,
ShareData,
PublicKey,
} from '../../../src/domain/value-objects';
import { PartyShareType, PartyShareStatus } from '../../../src/domain/enums';
describe('PartyShareMapper', () => {
let mapper: PartyShareMapper;
beforeEach(() => {
mapper = new PartyShareMapper();
});
const createTestShareData = () => ShareData.create(
Buffer.from('encrypted-share-data-here-for-testing'),
Buffer.from('123456789012'), // 12 bytes IV
Buffer.from('1234567890123456'), // 16 bytes authTag
);
const createTestPublicKey = () => PublicKey.fromHex(
'03' + '0'.repeat(64), // Compressed public key format (33 bytes)
);
const createDomainEntity = (): PartyShare => {
return PartyShare.create({
partyId: PartyId.create('user123-server'),
sessionId: SessionId.generate(),
shareType: PartyShareType.WALLET,
shareData: createTestShareData(),
publicKey: createTestPublicKey(),
threshold: Threshold.create(3, 2),
});
};
const createPersistenceRecord = (): PartySharePersistence => ({
id: 'share_1699887766123_abc123xyz',
partyId: 'user123-server',
sessionId: '550e8400-e29b-41d4-a716-446655440000',
shareType: 'wallet',
shareData: JSON.stringify({
data: Buffer.from('encrypted-share-data-here-for-testing').toString('base64'),
iv: Buffer.from('123456789012').toString('base64'),
authTag: Buffer.from('1234567890123456').toString('base64'),
}),
publicKey: '03' + '0'.repeat(64),
thresholdN: 3,
thresholdT: 2,
status: 'active',
createdAt: new Date('2024-01-01T00:00:00Z'),
updatedAt: new Date('2024-01-01T00:00:00Z'),
lastUsedAt: null,
});
describe('toPersistence', () => {
it('should map domain entity to persistence model', () => {
const entity = createDomainEntity();
const result = mapper.toPersistence(entity);
expect(result).toBeDefined();
expect(result.id).toBe(entity.id.value);
expect(result.partyId).toBe(entity.partyId.value);
expect(result.sessionId).toBe(entity.sessionId.value);
expect(result.shareType).toBe(entity.shareType);
expect(result.thresholdT).toBe(entity.threshold.t);
expect(result.thresholdN).toBe(entity.threshold.n);
expect(result.status).toBe(entity.status);
});
it('should convert public key to hex', () => {
const entity = createDomainEntity();
const result = mapper.toPersistence(entity);
expect(result.publicKey).toBeDefined();
expect(typeof result.publicKey).toBe('string');
expect(result.publicKey).toMatch(/^[0-9a-f]+$/i);
});
it('should serialize share data as JSON string', () => {
const entity = createDomainEntity();
const result = mapper.toPersistence(entity);
expect(typeof result.shareData).toBe('string');
const parsed = JSON.parse(result.shareData);
expect(parsed).toHaveProperty('data');
expect(parsed).toHaveProperty('iv');
expect(parsed).toHaveProperty('authTag');
});
it('should handle revoked status', () => {
const entity = createDomainEntity();
entity.revoke('Test revocation reason');
const result = mapper.toPersistence(entity);
expect(result.status).toBe(PartyShareStatus.REVOKED);
});
});
describe('toDomain', () => {
it('should map persistence model to domain entity', () => {
const record = createPersistenceRecord();
const result = mapper.toDomain(record);
expect(result).toBeInstanceOf(PartyShare);
expect(result.id.value).toBe(record.id);
expect(result.partyId.value).toBe(record.partyId);
expect(result.sessionId.value).toBe(record.sessionId);
expect(result.threshold.t).toBe(record.thresholdT);
expect(result.threshold.n).toBe(record.thresholdN);
});
it('should reconstruct value objects', () => {
const record = createPersistenceRecord();
const result = mapper.toDomain(record);
expect(result.id).toBeDefined();
expect(result.partyId).toBeDefined();
expect(result.threshold).toBeDefined();
expect(result.publicKey).toBeDefined();
expect(result.shareData).toBeDefined();
});
it('should preserve timestamps', () => {
const record = createPersistenceRecord();
const result = mapper.toDomain(record);
expect(result.createdAt).toEqual(record.createdAt);
expect(result.updatedAt).toEqual(record.updatedAt);
});
it('should handle null lastUsedAt', () => {
const record = createPersistenceRecord();
record.lastUsedAt = null;
const result = mapper.toDomain(record);
expect(result.lastUsedAt).toBeUndefined();
});
});
describe('bidirectional mapping', () => {
it('should preserve data through round-trip conversion', () => {
const original = createDomainEntity();
const persistence = mapper.toPersistence(original);
const restored = mapper.toDomain(persistence);
expect(restored.id.value).toBe(original.id.value);
expect(restored.partyId.value).toBe(original.partyId.value);
expect(restored.sessionId.value).toBe(original.sessionId.value);
expect(restored.threshold.t).toBe(original.threshold.t);
expect(restored.threshold.n).toBe(original.threshold.n);
expect(restored.publicKey.toHex()).toBe(original.publicKey.toHex());
});
});
describe('toDomainList', () => {
it('should map array of persistence records to domain entities', () => {
const records = [
createPersistenceRecord(),
{
...createPersistenceRecord(),
id: 'share_1699887766124_def456uvw',
sessionId: '660e8400-e29b-41d4-a716-446655440001',
},
];
const results = mapper.toDomainList(records);
expect(results).toHaveLength(2);
results.forEach((result) => {
expect(result).toBeInstanceOf(PartyShare);
});
});
it('should return empty array for empty input', () => {
const results = mapper.toDomainList([]);
expect(results).toHaveLength(0);
});
});
});