Files
eSIM-Tools/tests/security/qrcode-generate.test.js
Abner 51e50b62ff 🐛 fix: 修复 QR 码生成中的标签解构与错误处理问题,增强安全性
- 修复 `createQRCodeContainer` 函数中 `labels` 参数为 `undefined` 时导致解构崩溃的问题,添加默认空对象
- 简化 QR 码库加载逻辑,移除脚本已存在且加载完成时直接返回的冗余检查
- 移除 `showQRResult` 方法中冗余的 `.catch()` 处理,错误已由 `generateQRCode` 内部统一处理
- 在 Netlify 函数中创建 sanitizedError 对象,避免包含 LPA 字符串的错误信息泄露到 Sentry 日志
- 更新测试用例以匹配重构后的函数名和错误消息,并移除无效的 jest mock 配置
2026-06-13 20:15:23 +08:00

168 lines
4.9 KiB
JavaScript

'use strict';
/**
* qrcode-generate Function 单元测试
*/
// 必须在 require 函数文件前 Mock qrcode 模块
jest.mock('qrcode', () => ({
toDataURL: jest.fn()
}));
const QRCode = require('qrcode');
describe('qrcode-generate Function', () => {
const originalEnv = process.env;
let qrcodeGenerate;
beforeAll(() => {
process.env = {
...originalEnv,
ACCESS_KEY: 'test-access-key',
ALLOWED_ORIGIN: 'https://esim.cosr.eu.org'
};
qrcodeGenerate = require('../../netlify/functions/qrcode-generate');
});
beforeEach(() => {
// 重置 Mock 计数器和实现
QRCode.toDataURL.mockClear();
QRCode.toDataURL.mockResolvedValue('data:image/png;base64,qrcode');
});
afterAll(() => {
process.env = originalEnv;
jest.restoreAllMocks();
});
it('有效请求应该返回 base64 PNG data URL', async () => {
const result = await qrcodeGenerate.handler({
httpMethod: 'POST',
headers: {
origin: 'https://esim.cosr.eu.org',
'x-esim-key': 'test-access-key'
},
body: JSON.stringify({ data: 'LPA:1$example', size: 300 }),
queryStringParameters: {}
}, { functionName: 'qrcode-generate' });
expect(result.statusCode).toBe(200);
expect(JSON.parse(result.body)).toEqual({
success: true,
qrcode: 'data:image/png;base64,qrcode'
});
expect(QRCode.toDataURL).toHaveBeenCalledWith('LPA:1$example', expect.objectContaining({
type: 'image/png',
width: 300
}));
});
it('缺少 data 时应该返回 400', async () => {
const result = await qrcodeGenerate.handler({
httpMethod: 'POST',
headers: {
origin: 'https://esim.cosr.eu.org',
'x-esim-key': 'test-access-key'
},
body: JSON.stringify({ size: 300 }),
queryStringParameters: {}
}, { functionName: 'qrcode-generate' });
expect(result.statusCode).toBe(400);
expect(result.body).toContain('data is required');
});
it('size 超出范围时应该返回 400', async () => {
const result = await qrcodeGenerate.handler({
httpMethod: 'POST',
headers: {
origin: 'https://esim.cosr.eu.org',
'x-esim-key': 'test-access-key'
},
body: JSON.stringify({ data: 'LPA:1$example', size: 601 }),
queryStringParameters: {}
}, { functionName: 'qrcode-generate' });
expect(result.statusCode).toBe(400);
expect(result.body).toContain('size must be between 200 and 600');
});
it('非 POST 请求应该返回 405', async () => {
const result = await qrcodeGenerate.handler({
httpMethod: 'GET',
headers: {
origin: 'https://esim.cosr.eu.org',
'x-esim-key': 'test-access-key'
},
body: '{}',
queryStringParameters: {}
}, { functionName: 'qrcode-generate' });
expect(result.statusCode).toBe(405);
});
it('size 为非整数时应该返回 400', async () => {
const result = await qrcodeGenerate.handler({
httpMethod: 'POST',
headers: {
origin: 'https://esim.cosr.eu.org',
'x-esim-key': 'test-access-key'
},
body: JSON.stringify({ data: 'LPA:1$example', size: 300.5 }),
queryStringParameters: {}
}, { functionName: 'qrcode-generate' });
expect(result.statusCode).toBe(400);
expect(result.body).toContain('size must be an integer');
});
it('size 低于最小值时应该返回 400', async () => {
const result = await qrcodeGenerate.handler({
httpMethod: 'POST',
headers: {
origin: 'https://esim.cosr.eu.org',
'x-esim-key': 'test-access-key'
},
body: JSON.stringify({ data: 'LPA:1$example', size: 199 }),
queryStringParameters: {}
}, { functionName: 'qrcode-generate' });
expect(result.statusCode).toBe(400);
expect(result.body).toContain('size must be between 200 and 600');
});
it('data 为非字符串时应该被拒绝', async () => {
const result = await qrcodeGenerate.handler({
httpMethod: 'POST',
headers: {
origin: 'https://esim.cosr.eu.org',
'x-esim-key': 'test-access-key'
},
body: JSON.stringify({ data: 12345, size: 300 }),
queryStringParameters: {}
}, { functionName: 'qrcode-generate' });
expect(result.statusCode).toBe(400);
expect(result.body).toContain('data must be of type string');
});
it('QRCode 生成失败时应该返回 500', async () => {
// Mock QRCode.toDataURL 抛出错误
QRCode.toDataURL.mockRejectedValueOnce(new Error('QR generation failed'));
const result = await qrcodeGenerate.handler({
httpMethod: 'POST',
headers: {
origin: 'https://esim.cosr.eu.org',
'x-esim-key': 'test-access-key'
},
body: JSON.stringify({ data: 'LPA:1$example', size: 300 }),
queryStringParameters: {}
}, { functionName: 'qrcode-generate' });
expect(result.statusCode).toBe(500);
expect(result.body).toContain('error');
});
});