- 新增许可证服务器 (license-server/) - 新增 Agent 部署模块 (flnxi-agent/) - 添加许可证验证和激活页面 - 添加 Harbor 镜像同步脚本 - 更新素材系统支持许可证验证
172 lines
4.8 KiB
TypeScript
172 lines
4.8 KiB
TypeScript
/**
|
||
* 加密服务模块测试
|
||
* 测试 lib/license/crypto-service.ts 中的加密/解密函数
|
||
*/
|
||
|
||
import {
|
||
encryptLocal,
|
||
decryptLocal,
|
||
hashData,
|
||
generateId,
|
||
verifySignature,
|
||
verifyLicenseSignature,
|
||
} from '../license/crypto-service';
|
||
|
||
describe('本地加密/解密', () => {
|
||
describe('encryptLocal', () => {
|
||
it('应该成功加密字符串', () => {
|
||
const plaintext = 'Hello, World!';
|
||
const encrypted = encryptLocal(plaintext);
|
||
|
||
expect(encrypted).toBeDefined();
|
||
expect(encrypted).not.toBe(plaintext);
|
||
// 格式应该是 iv:authTag:encrypted
|
||
expect(encrypted.split(':').length).toBe(3);
|
||
});
|
||
|
||
it('应该对相同输入产生不同的加密结果(因为随机 IV)', () => {
|
||
const plaintext = 'Same text';
|
||
const encrypted1 = encryptLocal(plaintext);
|
||
const encrypted2 = encryptLocal(plaintext);
|
||
|
||
expect(encrypted1).not.toBe(encrypted2);
|
||
});
|
||
|
||
it('应该能加密空字符串', () => {
|
||
const encrypted = encryptLocal('');
|
||
expect(encrypted).toBeDefined();
|
||
expect(encrypted.split(':').length).toBe(3);
|
||
});
|
||
|
||
it('应该能加密中文字符', () => {
|
||
const plaintext = '你好,世界!';
|
||
const encrypted = encryptLocal(plaintext);
|
||
expect(encrypted).toBeDefined();
|
||
});
|
||
|
||
it('应该能加密 JSON 字符串', () => {
|
||
const jsonData = JSON.stringify({ key: 'value', number: 123 });
|
||
const encrypted = encryptLocal(jsonData);
|
||
expect(encrypted).toBeDefined();
|
||
});
|
||
});
|
||
|
||
describe('decryptLocal', () => {
|
||
it('应该成功解密加密的字符串', () => {
|
||
const plaintext = 'Hello, World!';
|
||
const encrypted = encryptLocal(plaintext);
|
||
const decrypted = decryptLocal(encrypted);
|
||
|
||
expect(decrypted).toBe(plaintext);
|
||
});
|
||
|
||
it('应该成功解密中文字符', () => {
|
||
const plaintext = '你好,世界!';
|
||
const encrypted = encryptLocal(plaintext);
|
||
const decrypted = decryptLocal(encrypted);
|
||
|
||
expect(decrypted).toBe(plaintext);
|
||
});
|
||
|
||
it('应该成功解密 JSON 字符串', () => {
|
||
const jsonData = { key: 'value', number: 123, nested: { a: 1 } };
|
||
const encrypted = encryptLocal(JSON.stringify(jsonData));
|
||
const decrypted = JSON.parse(decryptLocal(encrypted));
|
||
|
||
expect(decrypted).toEqual(jsonData);
|
||
});
|
||
|
||
it('应该成功解密空字符串', () => {
|
||
const encrypted = encryptLocal('');
|
||
const decrypted = decryptLocal(encrypted);
|
||
|
||
expect(decrypted).toBe('');
|
||
});
|
||
|
||
it('解密无效数据时应该抛出错误', () => {
|
||
expect(() => decryptLocal('invalid:data:here')).toThrow('数据解密失败');
|
||
});
|
||
|
||
it('解密格式错误的数据时应该抛出错误', () => {
|
||
expect(() => decryptLocal('not-valid-format')).toThrow();
|
||
});
|
||
});
|
||
});
|
||
|
||
describe('hashData', () => {
|
||
it('应该生成 SHA-256 哈希', () => {
|
||
const data = 'test data';
|
||
const hash = hashData(data);
|
||
|
||
expect(hash).toBeDefined();
|
||
expect(hash.length).toBe(64); // SHA-256 produces 64 hex characters
|
||
});
|
||
|
||
it('相同输入应该产生相同哈希', () => {
|
||
const data = 'same data';
|
||
const hash1 = hashData(data);
|
||
const hash2 = hashData(data);
|
||
|
||
expect(hash1).toBe(hash2);
|
||
});
|
||
|
||
it('不同输入应该产生不同哈希', () => {
|
||
const hash1 = hashData('data1');
|
||
const hash2 = hashData('data2');
|
||
|
||
expect(hash1).not.toBe(hash2);
|
||
});
|
||
|
||
it('应该能哈希空字符串', () => {
|
||
const hash = hashData('');
|
||
expect(hash).toBeDefined();
|
||
expect(hash.length).toBe(64);
|
||
});
|
||
});
|
||
|
||
describe('generateId', () => {
|
||
it('应该生成 32 字符的十六进制 ID', () => {
|
||
const id = generateId();
|
||
|
||
expect(id).toBeDefined();
|
||
expect(id.length).toBe(32);
|
||
expect(/^[0-9a-f]+$/.test(id)).toBe(true);
|
||
});
|
||
|
||
it('应该生成唯一的 ID', () => {
|
||
const ids = new Set<string>();
|
||
for (let i = 0; i < 100; i++) {
|
||
ids.add(generateId());
|
||
}
|
||
|
||
expect(ids.size).toBe(100);
|
||
});
|
||
});
|
||
|
||
describe('签名验证', () => {
|
||
describe('verifySignature', () => {
|
||
it('无效签名应该返回 false', () => {
|
||
const data = 'test data';
|
||
const invalidSignature = 'invalid-signature';
|
||
|
||
const result = verifySignature(data, invalidSignature);
|
||
expect(result).toBe(false);
|
||
});
|
||
|
||
it('空签名应该返回 false', () => {
|
||
const result = verifySignature('data', '');
|
||
expect(result).toBe(false);
|
||
});
|
||
});
|
||
|
||
describe('verifyLicenseSignature', () => {
|
||
it('无效签名应该返回 false', () => {
|
||
const licenseData = { type: 'FREE', expiresAt: null };
|
||
const invalidSignature = 'invalid-signature';
|
||
|
||
const result = verifyLicenseSignature(licenseData, invalidSignature);
|
||
expect(result).toBe(false);
|
||
});
|
||
});
|
||
});
|