143 lines
4.6 KiB
TypeScript
143 lines
4.6 KiB
TypeScript
import { Test, TestingModule } from '@nestjs/testing';
|
|
import { ConfigService } from '@nestjs/config';
|
|
import { AesEncryptionService } from '../../../src/infrastructure/crypto/aes-encryption.service';
|
|
|
|
describe('AesEncryptionService', () => {
|
|
let service: AesEncryptionService;
|
|
|
|
const testKey = '0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef';
|
|
const testKeyId = 'test-key-v1';
|
|
|
|
beforeEach(async () => {
|
|
const module: TestingModule = await Test.createTestingModule({
|
|
providers: [
|
|
AesEncryptionService,
|
|
{
|
|
provide: ConfigService,
|
|
useValue: {
|
|
get: (key: string) => {
|
|
switch (key) {
|
|
case 'BACKUP_ENCRYPTION_KEY':
|
|
return testKey;
|
|
case 'BACKUP_ENCRYPTION_KEY_ID':
|
|
return testKeyId;
|
|
default:
|
|
return undefined;
|
|
}
|
|
},
|
|
},
|
|
},
|
|
],
|
|
}).compile();
|
|
|
|
service = module.get<AesEncryptionService>(AesEncryptionService);
|
|
});
|
|
|
|
it('should be defined', () => {
|
|
expect(service).toBeDefined();
|
|
});
|
|
|
|
describe('encrypt', () => {
|
|
it('should encrypt plaintext successfully', async () => {
|
|
const plaintext = 'Hello, World!';
|
|
|
|
const result = await service.encrypt(plaintext);
|
|
|
|
expect(result.encrypted).toBeDefined();
|
|
expect(result.keyId).toBe(testKeyId);
|
|
expect(result.encrypted).not.toBe(plaintext);
|
|
// Should contain 3 parts separated by ':'
|
|
expect(result.encrypted.split(':').length).toBe(3);
|
|
});
|
|
|
|
it('should produce different ciphertext for same plaintext (due to random IV)', async () => {
|
|
const plaintext = 'Same message';
|
|
|
|
const result1 = await service.encrypt(plaintext);
|
|
const result2 = await service.encrypt(plaintext);
|
|
|
|
expect(result1.encrypted).not.toBe(result2.encrypted);
|
|
});
|
|
});
|
|
|
|
describe('decrypt', () => {
|
|
it('should decrypt ciphertext back to original plaintext', async () => {
|
|
const plaintext = 'Secret message for testing';
|
|
|
|
const { encrypted, keyId } = await service.encrypt(plaintext);
|
|
const decrypted = await service.decrypt(encrypted, keyId);
|
|
|
|
expect(decrypted).toBe(plaintext);
|
|
});
|
|
|
|
it('should handle special characters', async () => {
|
|
const plaintext = 'Special chars: !@#$%^&*()_+-={}[]|\\:";\'<>?,./';
|
|
|
|
const { encrypted, keyId } = await service.encrypt(plaintext);
|
|
const decrypted = await service.decrypt(encrypted, keyId);
|
|
|
|
expect(decrypted).toBe(plaintext);
|
|
});
|
|
|
|
it('should handle unicode characters', async () => {
|
|
const plaintext = 'Unicode: 你好世界 🌍 مرحبا';
|
|
|
|
const { encrypted, keyId } = await service.encrypt(plaintext);
|
|
const decrypted = await service.decrypt(encrypted, keyId);
|
|
|
|
expect(decrypted).toBe(plaintext);
|
|
});
|
|
|
|
it('should handle large payloads', async () => {
|
|
const plaintext = 'a'.repeat(10000);
|
|
|
|
const { encrypted, keyId } = await service.encrypt(plaintext);
|
|
const decrypted = await service.decrypt(encrypted, keyId);
|
|
|
|
expect(decrypted).toBe(plaintext);
|
|
});
|
|
|
|
it('should throw error for invalid format', async () => {
|
|
await expect(service.decrypt('invalid-format', testKeyId)).rejects.toThrow(
|
|
'Invalid encrypted data format',
|
|
);
|
|
});
|
|
|
|
it('should throw error for non-existent key', async () => {
|
|
const { encrypted } = await service.encrypt('test');
|
|
|
|
await expect(service.decrypt(encrypted, 'non-existent-key')).rejects.toThrow(
|
|
'Decryption key not found',
|
|
);
|
|
});
|
|
});
|
|
|
|
describe('addKey', () => {
|
|
it('should add new key for rotation', async () => {
|
|
const newKeyId = 'key-v2';
|
|
const newKey = 'fedcba9876543210fedcba9876543210fedcba9876543210fedcba9876543210';
|
|
|
|
service.addKey(newKeyId, newKey);
|
|
|
|
// Encrypt with original key
|
|
const { encrypted: encrypted1 } = await service.encrypt('test');
|
|
|
|
// Should still be able to decrypt (uses current key)
|
|
const decrypted = await service.decrypt(encrypted1, testKeyId);
|
|
expect(decrypted).toBe('test');
|
|
});
|
|
|
|
it('should throw error for invalid key length', () => {
|
|
expect(() => service.addKey('bad-key', 'too-short')).toThrow(
|
|
'Key must be 256 bits',
|
|
);
|
|
});
|
|
});
|
|
|
|
describe('getCurrentKeyId', () => {
|
|
it('should return current key ID', () => {
|
|
expect(service.getCurrentKeyId()).toBe(testKeyId);
|
|
});
|
|
});
|
|
});
|