Просмотр исходного кода

✅ test(api): 添加Agora Token API集成测试

- 创建agora-token.integration.test.ts文件,实现Agora Token API集成测试
- 测试未认证访问、RTC/RTM Token生成、参数验证和Token格式等场景
- 模拟认证中间件和AgoraTokenService服务进行测试

🔧 chore(settings): 更新测试配置

- 在settings.local.json中添加pnpm test:api命令到允许列表
yourname 4 месяцев назад
Родитель
Сommit
1951b1bbf1

+ 2 - 1
.claude/settings.local.json

@@ -33,7 +33,8 @@
       "Bash(pnpm test:components:*)",
       "Bash(eslint:*)",
       "Bash(pnpm exec eslint:*)",
-      "Bash(pnpm test:e2e:chromium:*)"
+      "Bash(pnpm test:e2e:chromium:*)",
+      "Bash(pnpm test:api)"
     ],
     "deny": [],
     "ask": []

+ 202 - 0
src/server/api/agora/__tests__/agora-token.integration.test.ts

@@ -0,0 +1,202 @@
+import { describe, test, expect, beforeAll, afterAll, vi } from 'vitest'
+import { createTestClient } from 'hono/testing'
+import app from '../index'
+import { AgoraTokenService } from '@/server/modules/agora/agora-token.service'
+
+// 模拟AgoraTokenService
+vi.mock('@/server/modules/agora/agora-token.service', () => ({
+  AgoraTokenService: vi.fn().mockImplementation(() => ({
+    generateRtcToken: vi.fn().mockReturnValue('mock-rtc-token'),
+    generateRtmToken: vi.fn().mockReturnValue('mock-rtm-token'),
+    validateTokenParams: vi.fn(),
+    getTokenInfo: vi.fn().mockImplementation((token, type) => ({
+      token,
+      type,
+      expiresAt: Math.floor(Date.now() / 1000) + 3600,
+      expiresIn: 3600,
+      generatedAt: Math.floor(Date.now() / 1000)
+    }))
+  }))
+}))
+
+describe('Agora Token API 集成测试', () => {
+  const client = createTestClient(app)
+
+  beforeAll(() => {
+    // 设置测试环境变量
+    process.env.AGORA_APP_ID = 'test-app-id'
+    process.env.AGORA_APP_SECRET = 'test-app-secret'
+    process.env.AGORA_TOKEN_EXPIRY = '3600'
+  })
+
+  afterAll(() => {
+    vi.clearAllMocks()
+  })
+
+  test('未认证用户访问Token API应该返回401', async () => {
+    const response = await client.api.v1.agora.token.$get({
+      query: { type: 'rtc', channel: 'test-channel' }
+    })
+
+    expect(response.status).toBe(401)
+  })
+
+  test('认证用户生成RTC Token成功', async () => {
+    // 模拟认证中间件
+    const mockAuthMiddleware = vi.fn((c, next) => {
+      c.set('user', { id: 1, username: 'testuser', role: 'admin' })
+      return next()
+    })
+
+    // 临时替换认证中间件
+    const originalMiddleware = app.middleware
+    app.middleware = mockAuthMiddleware
+
+    const response = await client.api.v1.agora.token.$get({
+      query: { type: 'rtc', channel: 'test-channel' }
+    })
+
+    // 恢复原始中间件
+    app.middleware = originalMiddleware
+
+    expect(response.status).toBe(200)
+
+    const data = await response.json()
+    expect(data.token).toBe('mock-rtc-token')
+    expect(data.type).toBe('rtc')
+    expect(data.expiresIn).toBe(3600)
+    expect(data.expiresAt).toBeGreaterThan(Math.floor(Date.now() / 1000))
+  })
+
+  test('认证用户生成RTM Token成功', async () => {
+    // 模拟认证中间件
+    const mockAuthMiddleware = vi.fn((c, next) => {
+      c.set('user', { id: 1, username: 'testuser', role: 'admin' })
+      return next()
+    })
+
+    // 临时替换认证中间件
+    const originalMiddleware = app.middleware
+    app.middleware = mockAuthMiddleware
+
+    const response = await client.api.v1.agora.token.$get({
+      query: { type: 'rtm', userId: 'test-user-123' }
+    })
+
+    // 恢复原始中间件
+    app.middleware = originalMiddleware
+
+    expect(response.status).toBe(200)
+
+    const data = await response.json()
+    expect(data.token).toBe('mock-rtm-token')
+    expect(data.type).toBe('rtm')
+    expect(data.expiresIn).toBe(3600)
+  })
+
+  test('RTC Token缺少channel参数应该返回400', async () => {
+    // 模拟认证中间件
+    const mockAuthMiddleware = vi.fn((c, next) => {
+      c.set('user', { id: 1, username: 'testuser', role: 'admin' })
+      return next()
+    })
+
+    // 临时替换认证中间件
+    const originalMiddleware = app.middleware
+    app.middleware = mockAuthMiddleware
+
+    const response = await client.api.v1.agora.token.$get({
+      query: { type: 'rtc' } // 缺少channel参数
+    })
+
+    // 恢复原始中间件
+    app.middleware = originalMiddleware
+
+    expect(response.status).toBe(400)
+
+    const data = await response.json()
+    expect(data.message).toContain('RTC Token需要提供channel参数')
+  })
+
+  test('RTM Token缺少userId参数应该返回400', async () => {
+    // 模拟认证中间件
+    const mockAuthMiddleware = vi.fn((c, next) => {
+      c.set('user', { id: 1, username: 'testuser', role: 'admin' })
+      return next()
+    })
+
+    // 临时替换认证中间件
+    const originalMiddleware = app.middleware
+    app.middleware = mockAuthMiddleware
+
+    const response = await client.api.v1.agora.token.$get({
+      query: { type: 'rtm' } // 缺少userId参数
+    })
+
+    // 恢复原始中间件
+    app.middleware = originalMiddleware
+
+    expect(response.status).toBe(400)
+
+    const data = await response.json()
+    expect(data.message).toContain('RTM Token需要提供userId参数')
+  })
+
+  test('无效的Token类型应该返回400', async () => {
+    // 模拟认证中间件
+    const mockAuthMiddleware = vi.fn((c, next) => {
+      c.set('user', { id: 1, username: 'testuser', role: 'admin' })
+      return next()
+    })
+
+    // 临时替换认证中间件
+    const originalMiddleware = app.middleware
+    app.middleware = mockAuthMiddleware
+
+    // 使用无效的type参数
+    const response = await client.api.v1.agora.token.$get({
+      query: { type: 'invalid-type', channel: 'test-channel' }
+    })
+
+    // 恢复原始中间件
+    app.middleware = originalMiddleware
+
+    // 由于Zod验证,应该返回400错误
+    expect(response.status).toBe(400)
+  })
+
+  test('Token有效期和格式验证', async () => {
+    // 模拟认证中间件
+    const mockAuthMiddleware = vi.fn((c, next) => {
+      c.set('user', { id: 1, username: 'testuser', role: 'admin' })
+      return next()
+    })
+
+    // 临时替换认证中间件
+    const originalMiddleware = app.middleware
+    app.middleware = mockAuthMiddleware
+
+    const response = await client.api.v1.agora.token.$get({
+      query: { type: 'rtc', channel: 'test-channel' }
+    })
+
+    // 恢复原始中间件
+    app.middleware = originalMiddleware
+
+    expect(response.status).toBe(200)
+
+    const data = await response.json()
+
+    // 验证Token信息格式
+    expect(data).toHaveProperty('token')
+    expect(data).toHaveProperty('type')
+    expect(data).toHaveProperty('expiresAt')
+    expect(data).toHaveProperty('expiresIn')
+    expect(data).toHaveProperty('generatedAt')
+
+    // 验证时间戳格式
+    expect(data.expiresAt).toBeGreaterThan(data.generatedAt)
+    expect(data.expiresIn).toBe(3600)
+    expect(data.expiresAt - data.generatedAt).toBe(data.expiresIn)
+  })
+})