245 lines
8.0 KiB
TypeScript
245 lines
8.0 KiB
TypeScript
/**
|
|
* Value Objects Unit Tests
|
|
*/
|
|
|
|
import {
|
|
SessionId,
|
|
PartyId,
|
|
ShareId,
|
|
Threshold,
|
|
ShareData,
|
|
PublicKey,
|
|
Signature,
|
|
MessageHash,
|
|
} from '../../../src/domain/value-objects';
|
|
|
|
describe('Value Objects', () => {
|
|
describe('SessionId', () => {
|
|
it('should create valid SessionId', () => {
|
|
const sessionId = SessionId.create('550e8400-e29b-41d4-a716-446655440000');
|
|
expect(sessionId.value).toBe('550e8400-e29b-41d4-a716-446655440000');
|
|
});
|
|
|
|
it('should generate unique SessionId', () => {
|
|
const sessionId1 = SessionId.generate();
|
|
const sessionId2 = SessionId.generate();
|
|
expect(sessionId1.value).not.toBe(sessionId2.value);
|
|
});
|
|
|
|
it('should throw error for invalid format', () => {
|
|
expect(() => SessionId.create('invalid')).toThrow();
|
|
expect(() => SessionId.create('')).toThrow();
|
|
});
|
|
|
|
it('should compare equality correctly', () => {
|
|
const sessionId1 = SessionId.create('550e8400-e29b-41d4-a716-446655440000');
|
|
const sessionId2 = SessionId.create('550e8400-e29b-41d4-a716-446655440000');
|
|
const sessionId3 = SessionId.create('660e8400-e29b-41d4-a716-446655440001');
|
|
|
|
expect(sessionId1.equals(sessionId2)).toBe(true);
|
|
expect(sessionId1.equals(sessionId3)).toBe(false);
|
|
});
|
|
});
|
|
|
|
describe('PartyId', () => {
|
|
it('should create valid PartyId', () => {
|
|
const partyId = PartyId.create('user123-server');
|
|
expect(partyId.value).toBe('user123-server');
|
|
});
|
|
|
|
it('should create from components', () => {
|
|
const partyId = PartyId.fromComponents('user123', 'server');
|
|
expect(partyId.value).toBe('user123-server');
|
|
});
|
|
|
|
it('should extract identifier and type', () => {
|
|
const partyId = PartyId.create('user123-server');
|
|
expect(partyId.getIdentifier()).toBe('user123');
|
|
expect(partyId.getType()).toBe('server');
|
|
});
|
|
|
|
it('should throw error for invalid format', () => {
|
|
expect(() => PartyId.create('')).toThrow();
|
|
expect(() => PartyId.create('nohyphen')).toThrow();
|
|
});
|
|
});
|
|
|
|
describe('ShareId', () => {
|
|
it('should create valid ShareId', () => {
|
|
const shareId = ShareId.create('share_1699887766123_abc123xyz');
|
|
expect(shareId.value).toBe('share_1699887766123_abc123xyz');
|
|
});
|
|
|
|
it('should generate unique ShareId', () => {
|
|
const shareId1 = ShareId.generate();
|
|
const shareId2 = ShareId.generate();
|
|
expect(shareId1.value).not.toBe(shareId2.value);
|
|
expect(shareId1.value).toMatch(/^share_\d+_[a-z0-9]+$/);
|
|
});
|
|
|
|
it('should throw error for invalid format', () => {
|
|
expect(() => ShareId.create('invalid')).toThrow();
|
|
});
|
|
});
|
|
|
|
describe('Threshold', () => {
|
|
it('should create valid threshold', () => {
|
|
const threshold = Threshold.create(3, 2);
|
|
expect(threshold.n).toBe(3);
|
|
expect(threshold.t).toBe(2);
|
|
expect(threshold.toString()).toBe('2-of-3');
|
|
});
|
|
|
|
it('should provide common configurations', () => {
|
|
const twoOfThree = Threshold.twoOfThree();
|
|
expect(twoOfThree.toString()).toBe('2-of-3');
|
|
|
|
const threeOfFive = Threshold.threeOfFive();
|
|
expect(threeOfFive.toString()).toBe('3-of-5');
|
|
});
|
|
|
|
it('should validate participants', () => {
|
|
const threshold = Threshold.create(3, 2);
|
|
expect(threshold.validateParticipants(2)).toBe(true);
|
|
expect(threshold.validateParticipants(3)).toBe(true);
|
|
expect(threshold.validateParticipants(1)).toBe(false);
|
|
expect(threshold.validateParticipants(4)).toBe(false);
|
|
});
|
|
|
|
it('should throw error for invalid values', () => {
|
|
expect(() => Threshold.create(0, 0)).toThrow();
|
|
expect(() => Threshold.create(2, 3)).toThrow(); // t > n
|
|
expect(() => Threshold.create(3, 1)).toThrow(); // t < 2
|
|
});
|
|
});
|
|
|
|
describe('ShareData', () => {
|
|
it('should create valid ShareData', () => {
|
|
const shareData = ShareData.create(
|
|
Buffer.from('encrypted-data'),
|
|
Buffer.from('123456789012'), // 12 bytes
|
|
Buffer.from('1234567890123456'), // 16 bytes
|
|
);
|
|
|
|
expect(shareData.encryptedData).toEqual(Buffer.from('encrypted-data'));
|
|
});
|
|
|
|
it('should serialize to JSON and back', () => {
|
|
const original = ShareData.create(
|
|
Buffer.from('encrypted-data'),
|
|
Buffer.from('123456789012'),
|
|
Buffer.from('1234567890123456'),
|
|
);
|
|
|
|
const json = original.toJSON();
|
|
const restored = ShareData.fromJSON(json);
|
|
|
|
expect(restored.encryptedData).toEqual(original.encryptedData);
|
|
expect(restored.iv).toEqual(original.iv);
|
|
expect(restored.authTag).toEqual(original.authTag);
|
|
});
|
|
|
|
it('should throw error for invalid IV length', () => {
|
|
expect(() => ShareData.create(
|
|
Buffer.from('data'),
|
|
Buffer.from('short'), // Less than 12 bytes
|
|
Buffer.from('1234567890123456'),
|
|
)).toThrow();
|
|
});
|
|
|
|
it('should throw error for invalid authTag length', () => {
|
|
expect(() => ShareData.create(
|
|
Buffer.from('data'),
|
|
Buffer.from('123456789012'),
|
|
Buffer.from('short'), // Less than 16 bytes
|
|
)).toThrow();
|
|
});
|
|
});
|
|
|
|
describe('PublicKey', () => {
|
|
it('should create from hex', () => {
|
|
const hex = '03' + '0'.repeat(64);
|
|
const publicKey = PublicKey.fromHex(hex);
|
|
expect(publicKey.toHex()).toBe(hex);
|
|
expect(publicKey.isCompressed()).toBe(true);
|
|
});
|
|
|
|
it('should create from base64', () => {
|
|
const buffer = Buffer.alloc(33);
|
|
buffer[0] = 0x03;
|
|
const publicKey = PublicKey.fromBase64(buffer.toString('base64'));
|
|
expect(publicKey.bytes).toHaveLength(33);
|
|
});
|
|
|
|
it('should detect compressed vs uncompressed', () => {
|
|
const compressed = PublicKey.create(Buffer.alloc(33, 0x03));
|
|
const uncompressed = PublicKey.create(Buffer.alloc(65, 0x04));
|
|
|
|
expect(compressed.isCompressed()).toBe(true);
|
|
expect(uncompressed.isCompressed()).toBe(false);
|
|
});
|
|
|
|
it('should throw error for invalid length', () => {
|
|
expect(() => PublicKey.create(Buffer.alloc(32))).toThrow();
|
|
expect(() => PublicKey.create(Buffer.alloc(64))).toThrow();
|
|
});
|
|
});
|
|
|
|
describe('Signature', () => {
|
|
it('should create from components', () => {
|
|
const r = Buffer.alloc(32, 0x01);
|
|
const s = Buffer.alloc(32, 0x02);
|
|
const signature = Signature.create(r, s, 0);
|
|
|
|
expect(signature.r).toEqual(r);
|
|
expect(signature.s).toEqual(s);
|
|
expect(signature.v).toBe(0);
|
|
});
|
|
|
|
it('should parse from hex', () => {
|
|
const hex = '0'.repeat(128); // 64 bytes = r + s
|
|
const signature = Signature.fromHex(hex);
|
|
expect(signature.r).toHaveLength(32);
|
|
expect(signature.s).toHaveLength(32);
|
|
});
|
|
|
|
it('should convert to hex', () => {
|
|
const r = Buffer.alloc(32, 0x01);
|
|
const s = Buffer.alloc(32, 0x02);
|
|
const signature = Signature.create(r, s);
|
|
|
|
const hex = signature.toHex();
|
|
expect(hex).toHaveLength(128);
|
|
});
|
|
|
|
it('should throw error for invalid component length', () => {
|
|
expect(() => Signature.create(Buffer.alloc(31), Buffer.alloc(32))).toThrow();
|
|
expect(() => Signature.create(Buffer.alloc(32), Buffer.alloc(31))).toThrow();
|
|
});
|
|
});
|
|
|
|
describe('MessageHash', () => {
|
|
it('should create from hex', () => {
|
|
const hex = '0x' + '0'.repeat(64);
|
|
const hash = MessageHash.fromHex(hex);
|
|
expect(hash.bytes).toHaveLength(32);
|
|
});
|
|
|
|
it('should create from hex without prefix', () => {
|
|
const hex = '0'.repeat(64);
|
|
const hash = MessageHash.fromHex(hex);
|
|
expect(hash.bytes).toHaveLength(32);
|
|
});
|
|
|
|
it('should convert to hex with prefix', () => {
|
|
const hex = '0'.repeat(64);
|
|
const hash = MessageHash.fromHex(hex);
|
|
expect(hash.toHex()).toBe('0x' + hex);
|
|
});
|
|
|
|
it('should throw error for invalid length', () => {
|
|
expect(() => MessageHash.fromHex('0x1234')).toThrow();
|
|
});
|
|
});
|
|
});
|