import { describe, test, expect, beforeAll, afterAll, vi } from 'vitest' import { testClient } from 'hono/testing' import { agoraApiRoutes } from '@/server/api' import { AgoraTokenService } from '@/server/modules/agora/agora-token.service' import { authMiddleware } from '@/server/middleware/auth.middleware' // 模拟模块 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) })) })) })); vi.mock('@/server/middleware/auth.middleware'); // Mock用户数据 const mockUser = { id: 1, username: 'testuser', password: 'password123', phone: null, email: 'test@example.com', nickname: null, name: null, avatarFileId: null, avatarFile: null, isDisabled: 0, isDeleted: 0, roles: [], createdAt: new Date(), updatedAt: new Date() } describe('Agora Token API 集成测试', () => { let client: ReturnType>['api']['v1'] beforeAll(() => { // 设置测试环境变量 process.env.AGORA_APP_ID = 'test-app-id' process.env.AGORA_APP_SECRET = 'test-app-secret' process.env.AGORA_TOKEN_EXPIRY = '3600' // Mock auth middleware vi.mocked(authMiddleware).mockImplementation(async (c, next) => { const authHeader = c.req.header('Authorization') if (!authHeader) { return c.json({ message: 'Authorization header missing' }, 401) } c.set('user', mockUser) await next() }) // 创建测试客户端 client = testClient(agoraApiRoutes).api.v1 }) afterAll(() => { vi.clearAllMocks() }) test('AgoraTokenService参数验证功能', () => { const agoraTokenService = new AgoraTokenService() // 测试RTC Token参数验证 expect(() => { agoraTokenService.validateTokenParams('rtc', 'test-channel') }).not.toThrow() // 测试RTC Token缺少channel参数 expect(() => { agoraTokenService.validateTokenParams('rtc') }).toThrow('RTC Token需要提供channel参数') // 测试RTM Token参数验证 expect(() => { agoraTokenService.validateTokenParams('rtm', undefined, 'test-user') }).not.toThrow() // 测试RTM Token缺少userId参数 expect(() => { agoraTokenService.validateTokenParams('rtm') }).toThrow('RTM Token需要提供userId参数') // 测试频道名称长度限制 expect(() => { agoraTokenService.validateTokenParams('rtc', 'a'.repeat(65)) }).toThrow('频道名称长度不能超过64个字符') // 测试用户ID长度限制 expect(() => { agoraTokenService.validateTokenParams('rtm', undefined, 'a'.repeat(65)) }).toThrow('用户ID长度不能超过64个字符') }) test('AgoraTokenService Token生成功能', () => { const agoraTokenService = new AgoraTokenService() // 测试RTC Token生成 const rtcToken = agoraTokenService.generateRtcToken('test-channel', '123') expect(rtcToken).toBe('mock-rtc-token') // 测试RTM Token生成 const rtmToken = agoraTokenService.generateRtmToken('test-user') expect(rtmToken).toBe('mock-rtm-token') // 测试Token信息获取 const tokenInfo = agoraTokenService.getTokenInfo('test-token', 'rtc') expect(tokenInfo).toEqual({ token: 'test-token', type: 'rtc', expiresAt: expect.any(Number), expiresIn: 3600, generatedAt: expect.any(Number) }) }) test('未认证用户访问Token API应该返回401', async () => { // 临时修改mock以模拟未认证 vi.mocked(authMiddleware).mockImplementation(async (c) => { return c.json({ message: 'Authorization header missing' }, 401) }) const response = await client.agora.token.$get({ query: { type: 'rtc', channel: 'test-channel' } }) expect(response.status).toBe(401) // 恢复mock vi.mocked(authMiddleware).mockImplementation(async (c, next) => { const authHeader = c.req.header('Authorization') if (!authHeader) { return c.json({ message: 'Authorization header missing' }, 401) } c.set('user', mockUser) await next() }) }) test('认证用户生成RTC Token成功', async () => { const response = await client.agora.token.$get({ query: { type: 'rtc', channel: 'test-channel' } }, { headers: { 'Authorization': 'Bearer test-token' } }) expect(response.status).toBe(200) const data = await response.json() if ('token' in data) { 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 response = await client.agora.token.$get({ query: { type: 'rtm', userId: 'test-user-123' } }, { headers: { 'Authorization': 'Bearer test-token' } }) expect(response.status).toBe(200) const data = await response.json() if ('token' in data) { expect(data.token).toBe('mock-rtm-token') expect(data.type).toBe('rtm') expect(data.expiresIn).toBe(3600) } }) test('路由参数验证 - 无效Token类型', async () => { // 使用无效的type参数 const response = await client.agora.token.$get({ query: { type: 'rtc' as any, channel: 'test-channel' } }, { headers: { 'Authorization': 'Bearer test-token' } }) // 由于Zod验证,应该返回400错误 expect(response.status).toBe(400) }) test('路由参数验证 - 缺少必需参数', async () => { // 测试缺少channel参数 const response1 = await client.agora.token.$get({ query: { type: 'rtc' } // 缺少channel参数 }, { headers: { 'Authorization': 'Bearer test-token' } }) // 由于Zod验证,应该返回400错误 expect(response1.status).toBe(400) // 测试缺少userId参数 const response2 = await client.agora.token.$get({ query: { type: 'rtm' } // 缺少userId参数 }, { headers: { 'Authorization': 'Bearer test-token' } }) // 由于Zod验证,应该返回400错误 expect(response2.status).toBe(400) }) test('Token有效期和格式验证', async () => { const response = await client.agora.token.$get({ query: { type: 'rtc', channel: 'test-channel' } }, { headers: { 'Authorization': 'Bearer test-token' } }) expect(response.status).toBe(200) const data = await response.json() if ('token' in data) { // 验证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) } }) })