mirror of
https://github.com/Silentely/eSIM-Tools.git
synced 2026-09-03 06:24:20 +08:00
* ✨ feat: 打包 [email protected] 到本地 ES 模块 * ♻️ refactor(qrcode): 移除 CDN 加载逻辑,改用本地 import 引入 qrcode-generator * 🔧 chore: 移除 CDN preconnect 提示,QR 码库已内联 * 🔧 chore: 清理 CSP 配置,移除不再需要的 CDN 域名 * ✨ feat: 将 QR 码生成迁移到 Edge Function,消除后端冷启动延迟 - 在 Edge Function 中内联 qrcode-generator 库(~20KB),直接生成 QR 码 - 删除废弃的 Netlify Function (qrcode-generate.js) - 更新 server.js 移除对已删除函数的引用 - 更新测试文件适配新的 Edge 内联架构 * ♻️ refactor: 代码质量修复 — 移除死代码、消除变量遮蔽、添加交叉引用注释 * 🔧 chore: 修复非阻塞风险 — 补充 strict 模式、移除废弃 qrcode 依赖 - Edge Function qrcode-lib.js 补充 'use strict' 声明,与浏览器版保持一致 - 移除已废弃的 qrcode npm 依赖(原用于已删除的 Netlify Function) * ♻️ refactor: 消除魔法数字、补充脆耦合和 async 技术债注释 - Edge Function 中 QR margin 魔法数字 8 替换为 QR_MARGIN_MODULES 常量 - generateQRCodeLocal 补充 async 无 await 的技术债说明 - error.message.startsWith 条件补充校验函数耦合关系注释 * 🐛 fix: 修复边界条件 — null JSON body、vendor 字符串异常、CDN preconnect 残留 - Edge Function: null JSON body 解构移到 try/catch 内,添加 null/非对象检查 - qrcode-generator: 库 throw 字符串时统一转换为 Error 对象,避免 .startsWith 崩溃 - index.html: 移除不再需要的 CDN preconnect 提示(jsdelivr/cdnjs) * ♻️ refactor: 修复 CSP 恢复、异常归一化、自定义 Error 类、负面路径测试 - netlify.toml: 恢复 script-src 中 cdn.jsdelivr.net(Bootstrap JS 仍依赖) - Edge Function catch: 归一化非 Error 异常(库可能 throw 字符串) - Edge Function QR margin: createDataURL(cellSize, 2) → createDataURL(cellSize, cellSize * 2) 对齐模块边距 - Edge Function: 补充认证模型和 QR 格式变更注释 - qrcode-generator.js: 引入 QRCodeValidationError 替代 error.message.startsWith 脆耦合 - tests/bff-proxy: 添加 6 个 Edge QR 负面路径测试(无效 JSON、空/超长/非字符串 data、超范围 size) - tests/qrcode-generator: 添加库 throw 字符串异常的测试 - scripts/sync-qrcode-lib.js: 新增库代码同步验证脚本 * 🐛 fix: 修复 server.js CSP 缺少 jsdelivr、同步脚本 CRLF 归一化、fallback 拦截校验错误 - server.js: 恢复 script-src 中 cdn.jsdelivr.net(本地开发 Bootstrap JS 依赖) - sync-qrcode-lib.js: 归一化 CRLF 换行符避免跨平台误报 - qrcode-generator.js: generateQRCodeWithFallback 入口处拦截 QRCodeValidationError,避免无效输入触发无意义的后端降级
102 lines
3.7 KiB
JavaScript
102 lines
3.7 KiB
JavaScript
'use strict';
|
||
|
||
/**
|
||
* QR 码生成测试
|
||
* 验证 qrcode-lib.js(Edge Function 内联使用)的正确性
|
||
*/
|
||
|
||
import qrCodeLib from '../../src/js/modules/qrcode-lib.js';
|
||
|
||
// Edge Function 中的参数校验常量(与 bff-proxy.js 保持同步)
|
||
const QR_MIN_SIZE = 200;
|
||
const QR_MAX_SIZE = 600;
|
||
const QR_MAX_DATA_LENGTH = 2048;
|
||
|
||
describe('QR 码生成(qrcode-lib Edge 内联)', () => {
|
||
it('有效 LPA 数据应生成 data URL', () => {
|
||
const qr = qrCodeLib(0, 'M');
|
||
qr.addData('LPA:1$example.com$ACTCODE123');
|
||
qr.make();
|
||
|
||
const moduleCount = qr.getModuleCount();
|
||
const cellSize = Math.max(1, Math.floor(300 / (moduleCount + 8)));
|
||
const dataUrl = qr.createDataURL(cellSize, 2);
|
||
|
||
expect(moduleCount).toBeGreaterThan(0);
|
||
expect(cellSize).toBeGreaterThan(0);
|
||
expect(dataUrl).toMatch(/^data:image\/gif;base64,/);
|
||
expect(dataUrl.length).toBeGreaterThan(100);
|
||
});
|
||
|
||
it('空字符串参数校验应拒绝(Edge Function 层拦截)', () => {
|
||
// qrcode-lib 本身不校验空字符串,Edge Function 层的参数校验负责拦截
|
||
const data = '';
|
||
const valid = typeof data === 'string' && data.length >= 1 && data.length <= QR_MAX_DATA_LENGTH;
|
||
expect(valid).toBe(false);
|
||
});
|
||
|
||
it('超长数据应在 Edge Function 参数校验层被拒绝', () => {
|
||
// qrcode-lib 自动选择 QR 版本,不一定抛出异常;
|
||
// 但 Edge Function 层通过 QR_MAX_DATA_LENGTH 限制输入长度
|
||
const longData = 'A'.repeat(2049);
|
||
const valid = typeof longData === 'string' && longData.length >= 1 && longData.length <= QR_MAX_DATA_LENGTH;
|
||
expect(valid).toBe(false);
|
||
});
|
||
|
||
it('size 超出范围时参数校验应拒绝', () => {
|
||
// 模拟 Edge Function 的参数校验逻辑
|
||
const size = 601;
|
||
const numSize = Number(size);
|
||
const valid = Number.isInteger(numSize) && numSize >= QR_MIN_SIZE && numSize <= QR_MAX_SIZE;
|
||
expect(valid).toBe(false);
|
||
});
|
||
|
||
it('size 低于最小值时参数校验应拒绝', () => {
|
||
const size = 199;
|
||
const numSize = Number(size);
|
||
const valid = Number.isInteger(numSize) && numSize >= QR_MIN_SIZE && numSize <= QR_MAX_SIZE;
|
||
expect(valid).toBe(false);
|
||
});
|
||
|
||
it('size 为非整数时参数校验应拒绝', () => {
|
||
const size = 300.5;
|
||
const numSize = Number(size);
|
||
const valid = Number.isInteger(numSize) && numSize >= QR_MIN_SIZE && numSize <= QR_MAX_SIZE;
|
||
expect(valid).toBe(false);
|
||
});
|
||
|
||
it('非字符串 data 参数校验应拒绝', () => {
|
||
const data = 12345;
|
||
const valid = typeof data === 'string' && data.length >= 1 && data.length <= QR_MAX_DATA_LENGTH;
|
||
expect(valid).toBe(false);
|
||
});
|
||
|
||
it('有效请求参数校验应通过', () => {
|
||
const data = 'LPA:1$example.com$ACTCODE123';
|
||
const size = 300;
|
||
const dataValid = typeof data === 'string' && data.length >= 1 && data.length <= QR_MAX_DATA_LENGTH;
|
||
const numSize = Number(size);
|
||
const sizeValid = Number.isInteger(numSize) && numSize >= QR_MIN_SIZE && numSize <= QR_MAX_SIZE;
|
||
expect(dataValid).toBe(true);
|
||
expect(sizeValid).toBe(true);
|
||
});
|
||
|
||
it('不同 size 值应生成不同大小的 QR 码', () => {
|
||
const makeQr = (targetSize) => {
|
||
const qr = qrCodeLib(0, 'M');
|
||
qr.addData('test-data');
|
||
qr.make();
|
||
const moduleCount = qr.getModuleCount();
|
||
const cellSize = Math.max(1, Math.floor(targetSize / (moduleCount + 8)));
|
||
return { moduleCount, cellSize, dataUrl: qr.createDataURL(cellSize, 2) };
|
||
};
|
||
|
||
const small = makeQr(200);
|
||
const large = makeQr(600);
|
||
|
||
expect(small.moduleCount).toBe(large.moduleCount); // 相同数据,模块数相同
|
||
expect(large.cellSize).toBeGreaterThan(small.cellSize); // 更大尺寸 → 更大 cellSize
|
||
expect(large.dataUrl.length).toBeGreaterThan(small.dataUrl.length);
|
||
});
|
||
});
|