/** * @file 验证码模块测试用例 * @author AI助手 * @date 2024-12-27 * @description 验证码生成、验证和清理功能的测试 */ import { describe, it, expect, beforeAll, afterAll, beforeEach } from 'vitest'; import { app } from '@/app'; import { redisService } from '@/plugins/redis/redis.service'; import type { GenerateCaptchaRequest, VerifyCaptchaRequest } from './captcha.schema'; describe('Captcha API', () => { let captchaId: string; let captchaCode: string; beforeAll(async () => { // 初始化Redis服务 try { await redisService.initialize(); } catch (error) { console.warn('Redis初始化失败,跳过Redis相关测试:', error); } }); afterAll(async () => { // 关闭Redis连接 try { await redisService.close(); } catch (error) { console.warn('Redis关闭失败:', error); } }); beforeEach(async () => { // 每个测试前重置状态 captchaId = ''; captchaCode = ''; }); describe('POST /api/captcha/generate', () => { it('应该成功生成图形验证码', async () => { const payload: GenerateCaptchaRequest = { type: 'image', width: 200, height: 60, length: 4, expireTime: 300 }; const response = await app .handle(new Request('http://localhost/api/captcha/generate', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(payload), })); expect(response.status).toBe(200); const result = await response.json() as any; expect(result.code).toBe('SUCCESS'); expect(result.data.id).toBeDefined(); expect(result.data.image).toBeDefined(); expect(result.data.type).toBe('image'); expect(result.data.expireTime).toBeGreaterThan(Date.now()); // 保存验证码信息供后续测试使用 captchaId = result.data.id; captchaCode = 'TEST'; // 模拟验证码 }); it('应该使用默认参数生成验证码', async () => { const payload = {}; const response = await app .handle(new Request('http://localhost/api/captcha/generate', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(payload), })); expect(response.status).toBe(200); const result = await response.json() as any; expect(result.code).toBe('SUCCESS'); expect(result.data.type).toBe('image'); expect(result.data.image).toMatch(/^data:image\/png;base64,/); }); it('应该验证参数范围限制', async () => { const payload: GenerateCaptchaRequest = { width: 50, // 小于最小值100 height: 20, // 小于最小值40 length: 2, // 小于最小值4 expireTime: 30 // 小于最小值60 }; const response = await app .handle(new Request('http://localhost/api/captcha/generate', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(payload), })); expect(response.status).toBe(400); }); }); describe('POST /api/captcha/verify', () => { it('应该验证验证码不存在的情况', async () => { const payload: VerifyCaptchaRequest = { captchaId: 'nonexistent_captcha_id', captchaCode: '1234', scene: 'login' }; const response = await app .handle(new Request('http://localhost/api/captcha/verify', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(payload), })); expect(response.status).toBe(200); const result = await response.json() as any; expect(result.code).toBe('SUCCESS'); expect(result.data.valid).toBe(false); expect(result.data.message).toContain('验证码不存在或已过期'); }); it('应该验证参数格式', async () => { const payload = { captchaId: '', // 空字符串 captchaCode: '123', // 长度小于4 }; const response = await app .handle(new Request('http://localhost/api/captcha/verify', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(payload), })); expect(response.status).toBe(400); }); }); describe('POST /api/captcha/cleanup', () => { it('应该成功清理过期验证码', async () => { const response = await app .handle(new Request('http://localhost/api/captcha/cleanup', { method: 'POST', headers: { 'Content-Type': 'application/json' }, })); expect(response.status).toBe(200); const result = await response.json() as any; expect(result.code).toBe('SUCCESS'); expect(result.data.cleanedCount).toBeGreaterThanOrEqual(0); }); }); describe('验证码服务功能测试', () => { it('应该生成指定长度的随机验证码', async () => { const { captchaService } = await import('./captcha.service'); // 测试不同长度的验证码 const lengths = [4, 6, 8]; for (const length of lengths) { const code = (captchaService as any).generateRandomCode(length); expect(code).toHaveLength(length); expect(code).toMatch(/^[A-Z0-9]+$/); } }); it('应该生成Base64格式的图片数据', async () => { const { captchaService } = await import('./captcha.service'); const imageData = await (captchaService as any).generateImageCaptcha('TEST', 200, 60); expect(imageData).toMatch(/^data:image\/png;base64,/); expect(imageData.length).toBeGreaterThan(100); // 确保有实际的图片数据 }); }); describe('验证码安全性测试', () => { it('应该忽略验证码大小写', async () => { // 这个测试需要实际的验证码,这里只是验证逻辑 // 实际实现中,验证码存储时转为小写,验证时也转为小写比较 expect('ABC'.toLowerCase()).toBe('abc'.toLowerCase()); }); }); });