import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest' import { RtcManagerAdapter } from '../src/managers/rtc-manager-adapter' import { SttError } from '../src/core/stt-error' // 模拟 AgoraRTC SDK vi.mock('agora-rtc-sdk-ng', () => ({ default: { createClient: vi.fn(() => ({ join: vi.fn(), leave: vi.fn(), publish: vi.fn(), subscribe: vi.fn(), unsubscribe: vi.fn(), on: vi.fn(), })), createMicrophoneAndCameraTracks: vi.fn(() => Promise.resolve([{ close: vi.fn(), isPlaying: false, play: vi.fn() }, { close: vi.fn() }]) ), }, })) // 模拟 token 工具 vi.mock('../src/utils/token-utils', () => ({ generateAgoraToken: vi.fn(() => Promise.resolve('mock-token')), })) describe('RtcManagerAdapter', () => { let manager: RtcManagerAdapter beforeEach(() => { manager = new RtcManagerAdapter('test-app-id', 'test-certificate') }) afterEach(async () => { await manager.destroy() }) describe('constructor', () => { it('should create instance with appId and certificate', () => { expect(manager).toBeInstanceOf(RtcManagerAdapter) expect(manager.isJoined).toBe(false) expect(manager.config).toBeUndefined() }) it('should create instance without parameters', () => { const managerWithoutParams = new RtcManagerAdapter() expect(managerWithoutParams).toBeInstanceOf(RtcManagerAdapter) }) }) describe('join', () => { it('should join channel successfully', async () => { const config = { channel: 'test-channel', userId: 'test-user' } await manager.join(config) expect(manager.isJoined).toBe(true) expect(manager.config).toEqual(config) expect(manager.userId).toBe('test-user') expect(manager.channel).toBe('test-channel') }) it('should throw error when missing required parameters', async () => { await expect(manager.join({} as any)).rejects.toThrow(SttError) await expect(manager.join({ channel: 'test' } as any)).rejects.toThrow(SttError) await expect(manager.join({ userId: 'test' } as any)).rejects.toThrow(SttError) }) it('should throw error when already joined', async () => { const config = { channel: 'test-channel', userId: 'test-user' } await manager.join(config) await expect(manager.join(config)).rejects.toThrow(SttError) }) it('should throw error when missing appId', async () => { const managerWithoutAppId = new RtcManagerAdapter() const config = { channel: 'test-channel', userId: 'test-user' } await expect(managerWithoutAppId.join(config)).rejects.toThrow(SttError) }) it('should throw error when missing certificate', async () => { const managerWithoutCert = new RtcManagerAdapter('test-app-id') const config = { channel: 'test-channel', userId: 'test-user' } await expect(managerWithoutCert.join(config)).rejects.toThrow(SttError) }) it('should emit connecting and connected events', async () => { const config = { channel: 'test-channel', userId: 'test-user' } const connectingSpy = vi.fn() const connectedSpy = vi.fn() manager.on('connecting', connectingSpy) manager.on('connected', connectedSpy) await manager.join(config) expect(connectingSpy).toHaveBeenCalledWith({ channel: 'test-channel', userId: 'test-user', }) expect(connectedSpy).toHaveBeenCalledWith({ channel: 'test-channel', userId: 'test-user', }) }) }) describe('createTracks', () => { it('should create tracks successfully', async () => { const config = { channel: 'test-channel', userId: 'test-user' } await manager.join(config) // 先设置事件监听器 const localUserChangedSpy = vi.fn() manager.on('localUserChanged', localUserChangedSpy) await manager.createTracks() // 验证事件被触发 expect(localUserChangedSpy).toHaveBeenCalled() }) it('should throw error when not joined', async () => { await expect(manager.createTracks()).rejects.toThrow(SttError) }) }) describe('publish', () => { it('should publish tracks successfully', async () => { const config = { channel: 'test-channel', userId: 'test-user' } await manager.join(config) await manager.createTracks() await expect(manager.publish()).resolves.not.toThrow() }) it('should throw error when not joined', async () => { await expect(manager.publish()).rejects.toThrow(SttError) }) it('should throw error when tracks not created', async () => { const config = { channel: 'test-channel', userId: 'test-user' } await manager.join(config) await expect(manager.publish()).rejects.toThrow(SttError) }) }) describe('destroy', () => { it('should destroy manager successfully', async () => { const config = { channel: 'test-channel', userId: 'test-user' } await manager.join(config) await manager.destroy() expect(manager.isJoined).toBe(false) expect(manager.config).toBeUndefined() expect(manager.userId).toBe('') expect(manager.channel).toBe('') }) it('should emit destroying and destroyed events', async () => { const config = { channel: 'test-channel', userId: 'test-user' } await manager.join(config) const destroyingSpy = vi.fn() const destroyedSpy = vi.fn() manager.on('destroying', destroyingSpy) manager.on('destroyed', destroyedSpy) await manager.destroy() expect(destroyingSpy).toHaveBeenCalled() expect(destroyedSpy).toHaveBeenCalled() }) it('should handle destroy when not joined', async () => { await expect(manager.destroy()).resolves.not.toThrow() }) }) describe('event handling', () => { it('should handle network quality events', async () => { const config = { channel: 'test-channel', userId: 'test-user' } await manager.join(config) const networkQualitySpy = vi.fn() manager.on('networkQuality', networkQualitySpy) // 模拟网络质量事件 const mockClient = await import('agora-rtc-sdk-ng') const client = mockClient.default.createClient() const networkQualityHandler = client.on.mock.calls.find( (call: any) => call[0] === 'network-quality' )?.[1] if (networkQualityHandler) { networkQualityHandler({ uplink: 5, downlink: 5 }) expect(networkQualitySpy).toHaveBeenCalledWith({ uplink: 5, downlink: 5 }) } }) it('should handle user published events', async () => { const config = { channel: 'test-channel', userId: 'test-user' } await manager.join(config) const remoteUserChangedSpy = vi.fn() manager.on('remoteUserChanged', remoteUserChangedSpy) // 模拟用户发布事件 const mockClient = await import('agora-rtc-sdk-ng') const client = mockClient.default.createClient() const userPublishedHandler = client.on.mock.calls.find( (call: any) => call[0] === 'user-published' )?.[1] if (userPublishedHandler) { const mockUser = { uid: 'remote-user', audioTrack: {}, videoTrack: {} } await userPublishedHandler(mockUser, 'audio') expect(remoteUserChangedSpy).toHaveBeenCalledWith({ userId: 'remote-user', audioTrack: {}, videoTrack: {}, }) } }) }) })