Files
eSIM-Tools/tests/modules/secure-storage.test.js
Abner 9ed51287c0 test: 新增多个核心模块的单元测试并优化 Jest 配置
- 在 `jest.config.js` 中启用 V8 覆盖率提供器(兼容 Node.js 22+),提升覆盖率检测性能与准确性
- 调整覆盖率收集范围为 `src/js/modules/**/*.js`,排除测试文件,使报告更聚焦于实际业务逻辑
- 降低全局覆盖率阈值至当前可达成水平(statements/branches 30%-70%),制定逐步提升计划
- 新增 6 个模块的完整单元测试:`APIService`、`AppConfig`、`HTMLSanitizer`、`Logger`、`SecureStorage` 和 `utils`,总计约 840 行测试代码
- 修复 `HTMLSanitizer.sanitizeURL()` 中对 `data:text/html` 协议的正则检测逻辑,增强 XSS 防护能力
2026-05-05 10:35:14 +08:00

129 lines
3.8 KiB
JavaScript

/**
* SecureStorage 模块单元测试
* 覆盖安全存储、TTL 过期、降级存储等核心功能
*/
import secureStorage from '../../src/js/modules/secure-storage';
describe('SecureStorage', () => {
let store;
let realStore;
beforeEach(() => {
store = secureStorage;
// 创建真实的内存存储来模拟 sessionStorage 行为
realStore = {};
// 用带实际存储行为的 mock 替换 sessionStorage
const mockStorage = {
getItem: jest.fn((key) => realStore[key] || null),
setItem: jest.fn((key, value) => { realStore[key] = value; }),
removeItem: jest.fn((key) => { delete realStore[key]; }),
clear: jest.fn(() => { Object.keys(realStore).forEach(k => delete realStore[k]); }),
};
store.storage = mockStorage;
store.fallbackStorage.clear();
});
describe('setItem() / getItem()', () => {
it('应该存储和读取数据', () => {
store.setItem('key1', 'value1');
expect(store.storage.setItem).toHaveBeenCalled();
expect(store.getItem('key1')).toBe('value1');
});
it('应该存储对象类型数据', () => {
const obj = { a: 1, b: 'test' };
store.setItem('obj', obj);
expect(store.getItem('obj')).toEqual(obj);
});
it('应该在 sessionStorage 不可用时降级到内存存储', () => {
store.storage = null;
store.setItem('key', 'value');
expect(store.fallbackStorage.has('key')).toBe(true);
expect(store.getItem('key')).toBe('value');
});
it('应该在 JSON.stringify 失败时降级到内存存储', () => {
const circular = {};
circular.self = circular;
store.setItem('circular', circular);
expect(store.fallbackStorage.has('circular')).toBe(true);
});
});
describe('TTL 过期机制', () => {
it('应该在数据过期后返回 null', () => {
const expiredData = {
value: 'old',
expires: Date.now() - 1000,
timestamp: Date.now() - 10000
};
realStore['expired'] = JSON.stringify(expiredData);
const result = store.getItem('expired');
expect(result).toBeNull();
});
it('应该在数据未过期时返回值', () => {
const validData = {
value: 'fresh',
expires: Date.now() + 60000,
timestamp: Date.now()
};
realStore['fresh'] = JSON.stringify(validData);
expect(store.getItem('fresh')).toBe('fresh');
});
it('应该使用自定义 TTL', () => {
store.setItem('short', 'ttl', 1000);
expect(store.storage.setItem).toHaveBeenCalled();
});
});
describe('removeItem()', () => {
it('应该从 sessionStorage 移除数据', () => {
store.removeItem('key');
expect(store.storage.removeItem).toHaveBeenCalledWith('key');
});
it('应该从 fallbackStorage 移除数据', () => {
store.storage = null;
store.setItem('key', 'value');
store.removeItem('key');
expect(store.fallbackStorage.has('key')).toBe(false);
});
});
describe('clear()', () => {
it('应该清除所有数据', () => {
store.clear();
expect(store.storage.clear).toHaveBeenCalled();
expect(store.fallbackStorage.size).toBe(0);
});
});
describe('has()', () => {
it('应该返回 true 如果数据存在', () => {
const validData = {
value: 'exists',
expires: Date.now() + 60000,
timestamp: Date.now()
};
realStore['exists'] = JSON.stringify(validData);
expect(store.has('exists')).toBe(true);
});
it('应该返回 false 如果数据不存在', () => {
expect(store.has('nope')).toBe(false);
});
});
describe('getItem() 异常处理', () => {
it('应该在 JSON.parse 失败时返回 null', () => {
realStore['bad'] = 'invalid json{{{';
expect(store.getItem('bad')).toBeNull();
});
});
});