rwadurian/backend/services/backup-service/test/unit/domain/backup-share.entity.spec.ts

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);
});
});
});