190 lines
6.4 KiB
TypeScript
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);
|
|
});
|
|
});
|
|
});
|