171 lines
5.5 KiB
TypeScript
171 lines
5.5 KiB
TypeScript
import {
|
|
BackupShare,
|
|
BackupShareStatus,
|
|
} from '../../../src/domain/entities/backup-share.entity';
|
|
import { DomainError } from '../../../src/domain/errors/domain.error';
|
|
|
|
describe('BackupShare Entity', () => {
|
|
const validParams = {
|
|
userId: BigInt(12345),
|
|
accountSequence: BigInt(1001),
|
|
publicKey: '02' + 'a'.repeat(64),
|
|
encryptedShareData: 'encrypted-data-base64',
|
|
encryptionKeyId: 'key-v1',
|
|
};
|
|
|
|
describe('create', () => {
|
|
it('should create a backup share with valid parameters', () => {
|
|
const share = BackupShare.create(validParams);
|
|
|
|
expect(share.userId).toBe(validParams.userId);
|
|
expect(share.accountSequence).toBe(validParams.accountSequence);
|
|
expect(share.publicKey).toBe(validParams.publicKey);
|
|
expect(share.encryptedShareData).toBe(validParams.encryptedShareData);
|
|
expect(share.encryptionKeyId).toBe(validParams.encryptionKeyId);
|
|
expect(share.partyIndex).toBe(2);
|
|
expect(share.threshold).toBe(2);
|
|
expect(share.totalParties).toBe(3);
|
|
expect(share.status).toBe(BackupShareStatus.ACTIVE);
|
|
expect(share.accessCount).toBe(0);
|
|
expect(share.shareId).toBeNull();
|
|
});
|
|
|
|
it('should allow custom threshold and totalParties', () => {
|
|
const share = BackupShare.create({
|
|
...validParams,
|
|
threshold: 3,
|
|
totalParties: 5,
|
|
});
|
|
|
|
expect(share.threshold).toBe(3);
|
|
expect(share.totalParties).toBe(5);
|
|
});
|
|
});
|
|
|
|
describe('recordAccess', () => {
|
|
it('should increment access count for active share', () => {
|
|
const share = BackupShare.create(validParams);
|
|
|
|
share.recordAccess();
|
|
|
|
expect(share.accessCount).toBe(1);
|
|
expect(share.lastAccessedAt).not.toBeNull();
|
|
});
|
|
|
|
it('should throw error when accessing revoked share', () => {
|
|
const share = BackupShare.create(validParams);
|
|
share.revoke('TEST_REASON');
|
|
|
|
expect(() => share.recordAccess()).toThrow(DomainError);
|
|
expect(() => share.recordAccess()).toThrow(
|
|
'Cannot access revoked or rotated share',
|
|
);
|
|
});
|
|
});
|
|
|
|
describe('revoke', () => {
|
|
it('should revoke an active share', () => {
|
|
const share = BackupShare.create(validParams);
|
|
|
|
share.revoke('SECURITY_BREACH');
|
|
|
|
expect(share.status).toBe(BackupShareStatus.REVOKED);
|
|
expect(share.revokedAt).not.toBeNull();
|
|
});
|
|
|
|
it('should throw error when revoking already revoked share', () => {
|
|
const share = BackupShare.create(validParams);
|
|
share.revoke('FIRST_REVOKE');
|
|
|
|
expect(() => share.revoke('SECOND_REVOKE')).toThrow(DomainError);
|
|
expect(() => share.revoke('SECOND_REVOKE')).toThrow(
|
|
'Share already revoked',
|
|
);
|
|
});
|
|
});
|
|
|
|
describe('rotate', () => {
|
|
it('should rotate encryption for active share', () => {
|
|
const share = BackupShare.create(validParams);
|
|
const newEncryptedData = 'new-encrypted-data';
|
|
const newKeyId = 'key-v2';
|
|
|
|
share.rotate(newEncryptedData, newKeyId);
|
|
|
|
expect(share.encryptedShareData).toBe(newEncryptedData);
|
|
expect(share.encryptionKeyId).toBe(newKeyId);
|
|
expect(share.status).toBe(BackupShareStatus.ACTIVE);
|
|
});
|
|
|
|
it('should throw error when rotating revoked share', () => {
|
|
const share = BackupShare.create(validParams);
|
|
share.revoke('TEST');
|
|
|
|
expect(() => share.rotate('new-data', 'key-v2')).toThrow(DomainError);
|
|
expect(() => share.rotate('new-data', 'key-v2')).toThrow(
|
|
'Cannot rotate revoked share',
|
|
);
|
|
});
|
|
});
|
|
|
|
describe('isActive', () => {
|
|
it('should return true for active share', () => {
|
|
const share = BackupShare.create(validParams);
|
|
|
|
expect(share.isActive()).toBe(true);
|
|
});
|
|
|
|
it('should return false for revoked share', () => {
|
|
const share = BackupShare.create(validParams);
|
|
share.revoke('TEST');
|
|
|
|
expect(share.isActive()).toBe(false);
|
|
});
|
|
});
|
|
|
|
describe('setShareId', () => {
|
|
it('should set share ID when not already set', () => {
|
|
const share = BackupShare.create(validParams);
|
|
|
|
share.setShareId(BigInt(1));
|
|
|
|
expect(share.shareId).toBe(BigInt(1));
|
|
});
|
|
|
|
it('should throw error when share ID already set', () => {
|
|
const share = BackupShare.create(validParams);
|
|
share.setShareId(BigInt(1));
|
|
|
|
expect(() => share.setShareId(BigInt(2))).toThrow(DomainError);
|
|
expect(() => share.setShareId(BigInt(2))).toThrow('Share ID already set');
|
|
});
|
|
});
|
|
|
|
describe('toProps', () => {
|
|
it('should return all properties', () => {
|
|
const share = BackupShare.create(validParams);
|
|
const props = share.toProps();
|
|
|
|
expect(props.userId).toBe(validParams.userId);
|
|
expect(props.accountSequence).toBe(validParams.accountSequence);
|
|
expect(props.publicKey).toBe(validParams.publicKey);
|
|
expect(props.status).toBe(BackupShareStatus.ACTIVE);
|
|
expect(props.partyIndex).toBe(2);
|
|
});
|
|
});
|
|
|
|
describe('reconstitute', () => {
|
|
it('should reconstitute from props', () => {
|
|
const original = BackupShare.create(validParams);
|
|
original.setShareId(BigInt(123));
|
|
const props = original.toProps();
|
|
|
|
const reconstituted = BackupShare.reconstitute(props);
|
|
|
|
expect(reconstituted.shareId).toBe(props.shareId);
|
|
expect(reconstituted.userId).toBe(props.userId);
|
|
expect(reconstituted.status).toBe(props.status);
|
|
});
|
|
});
|
|
});
|