rtc-manager-adapter.test.ts 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232
  1. import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest'
  2. import { RtcManagerAdapter } from '../src/managers/rtc-manager-adapter'
  3. import { SttError } from '../src/core/stt-error'
  4. // 模拟 AgoraRTC SDK
  5. vi.mock('agora-rtc-sdk-ng', () => ({
  6. default: {
  7. createClient: vi.fn(() => ({
  8. join: vi.fn(),
  9. leave: vi.fn(),
  10. publish: vi.fn(),
  11. subscribe: vi.fn(),
  12. unsubscribe: vi.fn(),
  13. on: vi.fn(),
  14. })),
  15. createMicrophoneAndCameraTracks: vi.fn(() =>
  16. Promise.resolve([{ close: vi.fn(), isPlaying: false, play: vi.fn() }, { close: vi.fn() }])
  17. ),
  18. },
  19. }))
  20. // 模拟 token 工具
  21. vi.mock('../src/utils/token-utils', () => ({
  22. generateAgoraToken: vi.fn(() => Promise.resolve('mock-token')),
  23. }))
  24. describe('RtcManagerAdapter', () => {
  25. let manager: RtcManagerAdapter
  26. beforeEach(() => {
  27. manager = new RtcManagerAdapter('test-app-id', 'test-certificate')
  28. })
  29. afterEach(async () => {
  30. await manager.destroy()
  31. })
  32. describe('constructor', () => {
  33. it('should create instance with appId and certificate', () => {
  34. expect(manager).toBeInstanceOf(RtcManagerAdapter)
  35. expect(manager.isJoined).toBe(false)
  36. expect(manager.config).toBeUndefined()
  37. })
  38. it('should create instance without parameters', () => {
  39. const managerWithoutParams = new RtcManagerAdapter()
  40. expect(managerWithoutParams).toBeInstanceOf(RtcManagerAdapter)
  41. })
  42. })
  43. describe('join', () => {
  44. it('should join channel successfully', async () => {
  45. const config = { channel: 'test-channel', userId: 'test-user' }
  46. await manager.join(config)
  47. expect(manager.isJoined).toBe(true)
  48. expect(manager.config).toEqual(config)
  49. expect(manager.userId).toBe('test-user')
  50. expect(manager.channel).toBe('test-channel')
  51. })
  52. it('should throw error when missing required parameters', async () => {
  53. await expect(manager.join({} as any)).rejects.toThrow(SttError)
  54. await expect(manager.join({ channel: 'test' } as any)).rejects.toThrow(SttError)
  55. await expect(manager.join({ userId: 'test' } as any)).rejects.toThrow(SttError)
  56. })
  57. it('should throw error when already joined', async () => {
  58. const config = { channel: 'test-channel', userId: 'test-user' }
  59. await manager.join(config)
  60. await expect(manager.join(config)).rejects.toThrow(SttError)
  61. })
  62. it('should throw error when missing appId', async () => {
  63. const managerWithoutAppId = new RtcManagerAdapter()
  64. const config = { channel: 'test-channel', userId: 'test-user' }
  65. await expect(managerWithoutAppId.join(config)).rejects.toThrow(SttError)
  66. })
  67. it('should throw error when missing certificate', async () => {
  68. const managerWithoutCert = new RtcManagerAdapter('test-app-id')
  69. const config = { channel: 'test-channel', userId: 'test-user' }
  70. await expect(managerWithoutCert.join(config)).rejects.toThrow(SttError)
  71. })
  72. it('should emit connecting and connected events', async () => {
  73. const config = { channel: 'test-channel', userId: 'test-user' }
  74. const connectingSpy = vi.fn()
  75. const connectedSpy = vi.fn()
  76. manager.on('connecting', connectingSpy)
  77. manager.on('connected', connectedSpy)
  78. await manager.join(config)
  79. expect(connectingSpy).toHaveBeenCalledWith({
  80. channel: 'test-channel',
  81. userId: 'test-user',
  82. })
  83. expect(connectedSpy).toHaveBeenCalledWith({
  84. channel: 'test-channel',
  85. userId: 'test-user',
  86. })
  87. })
  88. })
  89. describe('createTracks', () => {
  90. it('should create tracks successfully', async () => {
  91. const config = { channel: 'test-channel', userId: 'test-user' }
  92. await manager.join(config)
  93. // 先设置事件监听器
  94. const localUserChangedSpy = vi.fn()
  95. manager.on('localUserChanged', localUserChangedSpy)
  96. await manager.createTracks()
  97. // 验证事件被触发
  98. expect(localUserChangedSpy).toHaveBeenCalled()
  99. })
  100. it('should throw error when not joined', async () => {
  101. await expect(manager.createTracks()).rejects.toThrow(SttError)
  102. })
  103. })
  104. describe('publish', () => {
  105. it('should publish tracks successfully', async () => {
  106. const config = { channel: 'test-channel', userId: 'test-user' }
  107. await manager.join(config)
  108. await manager.createTracks()
  109. await expect(manager.publish()).resolves.not.toThrow()
  110. })
  111. it('should throw error when not joined', async () => {
  112. await expect(manager.publish()).rejects.toThrow(SttError)
  113. })
  114. it('should throw error when tracks not created', async () => {
  115. const config = { channel: 'test-channel', userId: 'test-user' }
  116. await manager.join(config)
  117. await expect(manager.publish()).rejects.toThrow(SttError)
  118. })
  119. })
  120. describe('destroy', () => {
  121. it('should destroy manager successfully', async () => {
  122. const config = { channel: 'test-channel', userId: 'test-user' }
  123. await manager.join(config)
  124. await manager.destroy()
  125. expect(manager.isJoined).toBe(false)
  126. expect(manager.config).toBeUndefined()
  127. expect(manager.userId).toBe('')
  128. expect(manager.channel).toBe('')
  129. })
  130. it('should emit destroying and destroyed events', async () => {
  131. const config = { channel: 'test-channel', userId: 'test-user' }
  132. await manager.join(config)
  133. const destroyingSpy = vi.fn()
  134. const destroyedSpy = vi.fn()
  135. manager.on('destroying', destroyingSpy)
  136. manager.on('destroyed', destroyedSpy)
  137. await manager.destroy()
  138. expect(destroyingSpy).toHaveBeenCalled()
  139. expect(destroyedSpy).toHaveBeenCalled()
  140. })
  141. it('should handle destroy when not joined', async () => {
  142. await expect(manager.destroy()).resolves.not.toThrow()
  143. })
  144. })
  145. describe('event handling', () => {
  146. it('should handle network quality events', async () => {
  147. const config = { channel: 'test-channel', userId: 'test-user' }
  148. await manager.join(config)
  149. const networkQualitySpy = vi.fn()
  150. manager.on('networkQuality', networkQualitySpy)
  151. // 模拟网络质量事件
  152. const mockClient = await import('agora-rtc-sdk-ng')
  153. const client = mockClient.default.createClient()
  154. const networkQualityHandler = client.on.mock.calls.find(
  155. (call: any) => call[0] === 'network-quality'
  156. )?.[1]
  157. if (networkQualityHandler) {
  158. networkQualityHandler({ uplink: 5, downlink: 5 })
  159. expect(networkQualitySpy).toHaveBeenCalledWith({ uplink: 5, downlink: 5 })
  160. }
  161. })
  162. it('should handle user published events', async () => {
  163. const config = { channel: 'test-channel', userId: 'test-user' }
  164. await manager.join(config)
  165. const remoteUserChangedSpy = vi.fn()
  166. manager.on('remoteUserChanged', remoteUserChangedSpy)
  167. // 模拟用户发布事件
  168. const mockClient = await import('agora-rtc-sdk-ng')
  169. const client = mockClient.default.createClient()
  170. const userPublishedHandler = client.on.mock.calls.find(
  171. (call: any) => call[0] === 'user-published'
  172. )?.[1]
  173. if (userPublishedHandler) {
  174. const mockUser = { uid: 'remote-user', audioTrack: {}, videoTrack: {} }
  175. await userPublishedHandler(mockUser, 'audio')
  176. expect(remoteUserChangedSpy).toHaveBeenCalledWith({
  177. userId: 'remote-user',
  178. audioTrack: {},
  179. videoTrack: {},
  180. })
  181. }
  182. })
  183. })
  184. })