Files
xinghuoapi/frontend/src/composables/__tests__/useForm.spec.ts
yangjianbo bb5a5dd65e test: 完善自动化测试体系(7个模块,73个任务)
系统性地修复、补充和强化项目的自动化测试能力:

1. 测试基础设施修复
   - 修复 stubConcurrencyCache 缺失方法和构造函数参数不匹配
   - 创建 testutil 共享包(stubs.go, fixtures.go, httptest.go)
   - 为所有 Stub 添加编译期接口断言

2. 中间件测试补充
   - 新增 JWT 认证中间件测试(有效/过期/篡改/缺失 Token)
   - 补充 rate_limiter 和 recovery 中间件测试场景

3. 网关核心路径测试
   - 新增账户选择、等待队列、流式响应、并发控制、计费、Claude Code 检测测试
   - 覆盖负载均衡、粘性会话、SSE 转发、槽位管理等关键逻辑

4. 前端测试体系(11个新测试文件,163个测试用例)
   - Pinia stores: auth, app, subscriptions
   - API client: 请求拦截器、响应拦截器、401 刷新
   - Router guards: 认证重定向、管理员权限、简易模式限制
   - Composables: useForm, useTableLoader, useClipboard
   - Components: LoginForm, ApiKeyCreate, Dashboard

5. CI/CD 流水线重构
   - 重构 backend-ci.yml 为统一的 ci.yml
   - 前后端 4 个并行 Job + Postgres/Redis services
   - Race 检测、覆盖率收集与门禁、Docker 构建验证

6. E2E 自动化测试
   - e2e-test.sh 自动化脚本(Docker 启动→健康检查→测试→清理)
   - 用户注册→登录→API Key→网关调用完整链路测试
   - Mock 模式和 API Key 脱敏支持

7. 修复预存问题
   - tlsfingerprint dialer_test.go 缺失 build tag 导致集成测试编译冲突

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 12:05:39 +08:00

144 lines
3.6 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { describe, it, expect, vi, beforeEach } from 'vitest'
import { setActivePinia, createPinia } from 'pinia'
import { useForm } from '@/composables/useForm'
import { useAppStore } from '@/stores/app'
// Mock API 依赖app store 内部引用了这些)
vi.mock('@/api/admin/system', () => ({
checkUpdates: vi.fn(),
}))
vi.mock('@/api/auth', () => ({
getPublicSettings: vi.fn(),
}))
describe('useForm', () => {
let appStore: ReturnType<typeof useAppStore>
beforeEach(() => {
setActivePinia(createPinia())
appStore = useAppStore()
vi.clearAllMocks()
})
it('submit 期间 loading 为 true完成后为 false', async () => {
let resolveSubmit: () => void
const submitFn = vi.fn(
() => new Promise<void>((resolve) => { resolveSubmit = resolve })
)
const { loading, submit } = useForm({
form: { name: 'test' },
submitFn,
})
expect(loading.value).toBe(false)
const submitPromise = submit()
// 提交中
expect(loading.value).toBe(true)
resolveSubmit!()
await submitPromise
expect(loading.value).toBe(false)
})
it('submit 成功时显示成功消息', async () => {
const submitFn = vi.fn().mockResolvedValue(undefined)
const showSuccessSpy = vi.spyOn(appStore, 'showSuccess')
const { submit } = useForm({
form: { name: 'test' },
submitFn,
successMsg: '保存成功',
})
await submit()
expect(showSuccessSpy).toHaveBeenCalledWith('保存成功')
})
it('submit 成功但无 successMsg 时不调用 showSuccess', async () => {
const submitFn = vi.fn().mockResolvedValue(undefined)
const showSuccessSpy = vi.spyOn(appStore, 'showSuccess')
const { submit } = useForm({
form: { name: 'test' },
submitFn,
})
await submit()
expect(showSuccessSpy).not.toHaveBeenCalled()
})
it('submit 失败时显示错误消息并抛出错误', async () => {
const error = Object.assign(new Error('提交失败'), {
response: { data: { message: '服务器错误' } },
})
const submitFn = vi.fn().mockRejectedValue(error)
const showErrorSpy = vi.spyOn(appStore, 'showError')
const { submit, loading } = useForm({
form: { name: 'test' },
submitFn,
})
await expect(submit()).rejects.toThrow('提交失败')
expect(showErrorSpy).toHaveBeenCalled()
expect(loading.value).toBe(false)
})
it('submit 失败时使用自定义 errorMsg', async () => {
const submitFn = vi.fn().mockRejectedValue(new Error('network'))
const showErrorSpy = vi.spyOn(appStore, 'showError')
const { submit } = useForm({
form: { name: 'test' },
submitFn,
errorMsg: '自定义错误提示',
})
await expect(submit()).rejects.toThrow()
expect(showErrorSpy).toHaveBeenCalledWith('自定义错误提示')
})
it('loading 中不会重复提交', async () => {
let resolveSubmit: () => void
const submitFn = vi.fn(
() => new Promise<void>((resolve) => { resolveSubmit = resolve })
)
const { submit } = useForm({
form: { name: 'test' },
submitFn,
})
// 第一次提交
const p1 = submit()
// 第二次提交(应被忽略,因为 loading=true
submit()
expect(submitFn).toHaveBeenCalledTimes(1)
resolveSubmit!()
await p1
})
it('传递 form 数据到 submitFn', async () => {
const formData = { name: 'test', email: 'test@example.com' }
const submitFn = vi.fn().mockResolvedValue(undefined)
const { submit } = useForm({
form: formData,
submitFn,
})
await submit()
expect(submitFn).toHaveBeenCalledWith(formData)
})
})