mirror of
https://gitee.com/wanwujie/sub2api
synced 2026-04-25 09:04:45 +08:00
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>
This commit is contained in:
137
frontend/src/composables/__tests__/useClipboard.spec.ts
Normal file
137
frontend/src/composables/__tests__/useClipboard.spec.ts
Normal file
@@ -0,0 +1,137 @@
|
||||
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'
|
||||
import { setActivePinia, createPinia } from 'pinia'
|
||||
|
||||
// Mock i18n
|
||||
vi.mock('@/i18n', () => ({
|
||||
i18n: {
|
||||
global: {
|
||||
t: (key: string) => key,
|
||||
},
|
||||
},
|
||||
}))
|
||||
|
||||
// Mock app store
|
||||
const mockShowSuccess = vi.fn()
|
||||
const mockShowError = vi.fn()
|
||||
|
||||
vi.mock('@/stores/app', () => ({
|
||||
useAppStore: () => ({
|
||||
showSuccess: mockShowSuccess,
|
||||
showError: mockShowError,
|
||||
}),
|
||||
}))
|
||||
|
||||
import { useClipboard } from '@/composables/useClipboard'
|
||||
|
||||
describe('useClipboard', () => {
|
||||
beforeEach(() => {
|
||||
setActivePinia(createPinia())
|
||||
vi.useFakeTimers()
|
||||
vi.clearAllMocks()
|
||||
|
||||
// 默认模拟安全上下文 + Clipboard API
|
||||
Object.defineProperty(window, 'isSecureContext', { value: true, writable: true })
|
||||
Object.defineProperty(navigator, 'clipboard', {
|
||||
value: {
|
||||
writeText: vi.fn().mockResolvedValue(undefined),
|
||||
},
|
||||
writable: true,
|
||||
configurable: true,
|
||||
})
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
vi.useRealTimers()
|
||||
// 恢复 execCommand
|
||||
if ('execCommand' in document) {
|
||||
delete (document as any).execCommand
|
||||
}
|
||||
})
|
||||
|
||||
it('复制成功后 copied 变为 true', async () => {
|
||||
const { copied, copyToClipboard } = useClipboard()
|
||||
|
||||
expect(copied.value).toBe(false)
|
||||
|
||||
await copyToClipboard('hello')
|
||||
|
||||
expect(copied.value).toBe(true)
|
||||
})
|
||||
|
||||
it('copied 在 2 秒后自动恢复为 false', async () => {
|
||||
const { copied, copyToClipboard } = useClipboard()
|
||||
|
||||
await copyToClipboard('hello')
|
||||
expect(copied.value).toBe(true)
|
||||
|
||||
vi.advanceTimersByTime(2000)
|
||||
|
||||
expect(copied.value).toBe(false)
|
||||
})
|
||||
|
||||
it('复制成功时调用 showSuccess', async () => {
|
||||
const { copyToClipboard } = useClipboard()
|
||||
|
||||
await copyToClipboard('hello', '已复制')
|
||||
|
||||
expect(mockShowSuccess).toHaveBeenCalledWith('已复制')
|
||||
})
|
||||
|
||||
it('无自定义消息时使用 i18n 默认消息', async () => {
|
||||
const { copyToClipboard } = useClipboard()
|
||||
|
||||
await copyToClipboard('hello')
|
||||
|
||||
expect(mockShowSuccess).toHaveBeenCalledWith('common.copiedToClipboard')
|
||||
})
|
||||
|
||||
it('空文本返回 false 且不复制', async () => {
|
||||
const { copyToClipboard, copied } = useClipboard()
|
||||
|
||||
const result = await copyToClipboard('')
|
||||
|
||||
expect(result).toBe(false)
|
||||
expect(copied.value).toBe(false)
|
||||
expect(navigator.clipboard.writeText).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('Clipboard API 失败时降级到 fallback', async () => {
|
||||
;(navigator.clipboard.writeText as any).mockRejectedValue(new Error('API failed'))
|
||||
|
||||
// jsdom 没有 execCommand,手动定义
|
||||
;(document as any).execCommand = vi.fn().mockReturnValue(true)
|
||||
|
||||
const { copyToClipboard, copied } = useClipboard()
|
||||
const result = await copyToClipboard('fallback text')
|
||||
|
||||
expect(result).toBe(true)
|
||||
expect(copied.value).toBe(true)
|
||||
expect(document.execCommand).toHaveBeenCalledWith('copy')
|
||||
})
|
||||
|
||||
it('非安全上下文使用 fallback', async () => {
|
||||
Object.defineProperty(window, 'isSecureContext', { value: false, writable: true })
|
||||
|
||||
;(document as any).execCommand = vi.fn().mockReturnValue(true)
|
||||
|
||||
const { copyToClipboard, copied } = useClipboard()
|
||||
const result = await copyToClipboard('insecure context text')
|
||||
|
||||
expect(result).toBe(true)
|
||||
expect(copied.value).toBe(true)
|
||||
expect(navigator.clipboard.writeText).not.toHaveBeenCalled()
|
||||
expect(document.execCommand).toHaveBeenCalledWith('copy')
|
||||
})
|
||||
|
||||
it('所有复制方式均失败时调用 showError', async () => {
|
||||
;(navigator.clipboard.writeText as any).mockRejectedValue(new Error('fail'))
|
||||
;(document as any).execCommand = vi.fn().mockReturnValue(false)
|
||||
|
||||
const { copyToClipboard, copied } = useClipboard()
|
||||
const result = await copyToClipboard('text')
|
||||
|
||||
expect(result).toBe(false)
|
||||
expect(copied.value).toBe(false)
|
||||
expect(mockShowError).toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user