Преглед изворни кода

✅ test(rtm-manager-adapter): 完善RTM管理器适配器测试用例

- 添加App ID验证测试,确保未提供App ID时抛出正确错误
- 增加事件监听器设置测试,验证RTM客户端事件处理
- 完善updateSttData测试,验证存储方法调用和数据格式
- 添加updateLanguages测试,覆盖语言配置更新场景
- 增强destroy测试,验证事件监听器移除和客户端销毁
- 添加事件处理测试,验证presence和storage事件响应
- 修复AgoraRTM模块模拟,确保默认导出正确
yourname пре 2 месеци
родитељ
комит
33e1c8a537

+ 271 - 10
packages/stt-sdk-core/tests/managers/rtm-manager-adapter.test.ts

@@ -1,12 +1,14 @@
 import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest'
 import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest'
 import { RtmManagerAdapter } from '../../src/managers/rtm-manager-adapter'
 import { RtmManagerAdapter } from '../../src/managers/rtm-manager-adapter'
 import { SttError } from '../../src/core/stt-error'
 import { SttError } from '../../src/core/stt-error'
+import AgoraRTM from 'agora-rtm'
 
 
 describe('RtmManagerAdapter', () => {
 describe('RtmManagerAdapter', () => {
   let manager: RtmManagerAdapter
   let manager: RtmManagerAdapter
+  const mockAppId = 'test-app-id'
 
 
   beforeEach(() => {
   beforeEach(() => {
-    manager = new RtmManagerAdapter()
+    manager = new RtmManagerAdapter(mockAppId)
   })
   })
 
 
   afterEach(async () => {
   afterEach(async () => {
@@ -34,6 +36,9 @@ describe('RtmManagerAdapter', () => {
         userId: 'test-user',
         userId: 'test-user',
         userName: 'Test User',
         userName: 'Test User',
       })
       })
+
+      // 验证 RTM 客户端被正确创建
+      expect(AgoraRTM.RTM).toHaveBeenCalledWith(mockAppId, 'test-user', {})
     })
     })
 
 
     it('should throw error when config is invalid', async () => {
     it('should throw error when config is invalid', async () => {
@@ -64,6 +69,20 @@ describe('RtmManagerAdapter', () => {
       )
       )
     })
     })
 
 
+    it('should throw error when appId is not provided', async () => {
+      const managerWithoutAppId = new RtmManagerAdapter()
+      const config = {
+        channel: 'test-channel',
+        userId: 'test-user',
+        userName: 'Test User',
+      }
+
+      await expect(managerWithoutAppId.join(config)).rejects.toThrow(SttError)
+      await expect(managerWithoutAppId.join(config)).rejects.toThrow(
+        'App ID is required for RTM connection'
+      )
+    })
+
     it('should emit connected event', async () => {
     it('should emit connected event', async () => {
       const connectedHandler = vi.fn()
       const connectedHandler = vi.fn()
       manager.on('connected', connectedHandler)
       manager.on('connected', connectedHandler)
@@ -82,16 +101,34 @@ describe('RtmManagerAdapter', () => {
         userId: 'test-user',
         userId: 'test-user',
       })
       })
     })
     })
+
+    it('should setup event listeners correctly', async () => {
+      const config = {
+        channel: 'test-channel',
+        userId: 'test-user',
+        userName: 'Test User',
+      }
+
+      await manager.join(config)
+
+      // 获取创建的 RTM 客户端实例
+      const mockClient = (AgoraRTM.RTM as any).mock.results[0].value
+      expect(mockClient.addEventListener).toHaveBeenCalledWith('status', expect.any(Function))
+      expect(mockClient.addEventListener).toHaveBeenCalledWith('presence', expect.any(Function))
+      expect(mockClient.addEventListener).toHaveBeenCalledWith('storage', expect.any(Function))
+    })
   })
   })
 
 
   describe('updateSttData', () => {
   describe('updateSttData', () => {
-    it('should update STT data when joined', async () => {
+    beforeEach(async () => {
       await manager.join({
       await manager.join({
         channel: 'test-channel',
         channel: 'test-channel',
         userId: 'test-user',
         userId: 'test-user',
         userName: 'Test User',
         userName: 'Test User',
       })
       })
+    })
 
 
+    it('should update STT data successfully', async () => {
       const updatingHandler = vi.fn()
       const updatingHandler = vi.fn()
       const updatedHandler = vi.fn()
       const updatedHandler = vi.fn()
       manager.on('metadataUpdating', updatingHandler)
       manager.on('metadataUpdating', updatingHandler)
@@ -100,6 +137,9 @@ describe('RtmManagerAdapter', () => {
       const data = {
       const data = {
         status: 'start',
         status: 'start',
         taskId: 'test-task-id',
         taskId: 'test-task-id',
+        token: 'test-token',
+        startTime: Date.now(),
+        duration: 3600000,
       }
       }
 
 
       await manager.updateSttData(data)
       await manager.updateSttData(data)
@@ -108,29 +148,123 @@ describe('RtmManagerAdapter', () => {
       expect(updatingHandler).toHaveBeenCalledWith({ data })
       expect(updatingHandler).toHaveBeenCalledWith({ data })
       expect(updatedHandler).toHaveBeenCalledTimes(1)
       expect(updatedHandler).toHaveBeenCalledTimes(1)
       expect(updatedHandler).toHaveBeenCalledWith({ data })
       expect(updatedHandler).toHaveBeenCalledWith({ data })
+
+      // 验证存储方法被正确调用
+      const mockClient = (AgoraRTM.RTM as any).mock.results[0].value
+      expect(mockClient.storage.setChannelMetadata).toHaveBeenCalledWith(
+        'test-channel',
+        'MESSAGE',
+        expect.arrayContaining([
+          { key: 'status', value: expect.any(String) },
+          { key: 'taskId', value: expect.any(String) },
+          { key: 'token', value: expect.any(String) },
+          { key: 'startTime', value: expect.any(String) },
+          { key: 'duration', value: expect.any(String) },
+        ])
+      )
     })
     })
 
 
     it('should throw error when not joined', async () => {
     it('should throw error when not joined', async () => {
+      const unjoinedManager = new RtmManagerAdapter(mockAppId)
       const data = {
       const data = {
         status: 'start',
         status: 'start',
         taskId: 'test-task-id',
         taskId: 'test-task-id',
       }
       }
 
 
-      await expect(manager.updateSttData(data)).rejects.toThrow(SttError)
-      await expect(manager.updateSttData(data)).rejects.toThrow(
+      await expect(unjoinedManager.updateSttData(data)).rejects.toThrow(SttError)
+      await expect(unjoinedManager.updateSttData(data)).rejects.toThrow(
         'RTM manager must be joined to a channel before updating STT data'
         'RTM manager must be joined to a channel before updating STT data'
       )
       )
     })
     })
+
+    it('should handle empty data gracefully', async () => {
+      await manager.updateSttData({})
+
+      // 验证存储方法没有被调用(因为数据为空)
+      const mockClient = (AgoraRTM.RTM as any).mock.results[0].value
+      expect(mockClient.storage.setChannelMetadata).not.toHaveBeenCalled()
+    })
+  })
+
+  describe('updateLanguages', () => {
+    beforeEach(async () => {
+      await manager.join({
+        channel: 'test-channel',
+        userId: 'test-user',
+        userName: 'Test User',
+      })
+    })
+
+    it('should update languages successfully', async () => {
+      const updatingHandler = vi.fn()
+      const updatedHandler = vi.fn()
+      manager.on('languagesUpdating', updatingHandler)
+      manager.on('languagesUpdated', updatedHandler)
+
+      const languages = [
+        { source: 'en-US', target: ['zh-CN', 'ja-JP'] },
+        { source: 'zh-CN', target: ['en-US'] },
+      ]
+
+      await manager.updateLanguages(languages)
+
+      expect(updatingHandler).toHaveBeenCalledTimes(1)
+      expect(updatingHandler).toHaveBeenCalledWith({ languages })
+      expect(updatedHandler).toHaveBeenCalledTimes(1)
+      expect(updatedHandler).toHaveBeenCalledWith({ languages })
+
+      // 验证 updateSttData 被正确调用
+      const mockClient = (AgoraRTM.RTM as any).mock.results[0].value
+      expect(mockClient.storage.setChannelMetadata).toHaveBeenCalledWith(
+        'test-channel',
+        'MESSAGE',
+        expect.arrayContaining([
+          { key: 'transcribe1', value: expect.any(String) },
+          { key: 'translate1List', value: expect.any(String) },
+          { key: 'transcribe2', value: expect.any(String) },
+          { key: 'translate2List', value: expect.any(String) },
+        ])
+      )
+    })
+
+    it('should throw error when not joined', async () => {
+      const unjoinedManager = new RtmManagerAdapter(mockAppId)
+      const languages = [{ source: 'en-US' }]
+
+      await expect(unjoinedManager.updateLanguages(languages)).rejects.toThrow(SttError)
+      await expect(unjoinedManager.updateLanguages(languages)).rejects.toThrow(
+        'RTM manager must be joined to a channel before updating languages'
+      )
+    })
+
+    it('should handle empty languages array', async () => {
+      await manager.updateLanguages([])
+
+      // 验证 updateSttData 被调用但数据为空
+      const mockClient = (AgoraRTM.RTM as any).mock.results[0].value
+      expect(mockClient.storage.setChannelMetadata).toHaveBeenCalledWith(
+        'test-channel',
+        'MESSAGE',
+        expect.arrayContaining([
+          { key: 'transcribe1', value: '""' },
+          { key: 'translate1List', value: '[]' },
+          { key: 'transcribe2', value: '""' },
+          { key: 'translate2List', value: '[]' },
+        ])
+      )
+    })
   })
   })
 
 
   describe('acquireLock', () => {
   describe('acquireLock', () => {
-    it('should acquire lock when joined', async () => {
+    beforeEach(async () => {
       await manager.join({
       await manager.join({
         channel: 'test-channel',
         channel: 'test-channel',
         userId: 'test-user',
         userId: 'test-user',
         userName: 'Test User',
         userName: 'Test User',
       })
       })
+    })
 
 
+    it('should acquire lock successfully', async () => {
       const acquiringHandler = vi.fn()
       const acquiringHandler = vi.fn()
       const acquiredHandler = vi.fn()
       const acquiredHandler = vi.fn()
       manager.on('lockAcquiring', acquiringHandler)
       manager.on('lockAcquiring', acquiringHandler)
@@ -140,24 +274,36 @@ describe('RtmManagerAdapter', () => {
 
 
       expect(acquiringHandler).toHaveBeenCalledTimes(1)
       expect(acquiringHandler).toHaveBeenCalledTimes(1)
       expect(acquiredHandler).toHaveBeenCalledTimes(1)
       expect(acquiredHandler).toHaveBeenCalledTimes(1)
+
+      // 验证锁方法被正确调用
+      const mockClient = (AgoraRTM.RTM as any).mock.results[0].value
+      expect(mockClient.lock.acquireLock).toHaveBeenCalledWith(
+        'test-channel',
+        'MESSAGE',
+        'lock_stt'
+      )
     })
     })
 
 
     it('should throw error when not joined', async () => {
     it('should throw error when not joined', async () => {
-      await expect(manager.acquireLock()).rejects.toThrow(SttError)
-      await expect(manager.acquireLock()).rejects.toThrow(
+      const unjoinedManager = new RtmManagerAdapter(mockAppId)
+
+      await expect(unjoinedManager.acquireLock()).rejects.toThrow(SttError)
+      await expect(unjoinedManager.acquireLock()).rejects.toThrow(
         'RTM manager must be joined to a channel before acquiring lock'
         'RTM manager must be joined to a channel before acquiring lock'
       )
       )
     })
     })
   })
   })
 
 
   describe('releaseLock', () => {
   describe('releaseLock', () => {
-    it('should release lock when joined', async () => {
+    beforeEach(async () => {
       await manager.join({
       await manager.join({
         channel: 'test-channel',
         channel: 'test-channel',
         userId: 'test-user',
         userId: 'test-user',
         userName: 'Test User',
         userName: 'Test User',
       })
       })
+    })
 
 
+    it('should release lock successfully', async () => {
       const releasingHandler = vi.fn()
       const releasingHandler = vi.fn()
       const releasedHandler = vi.fn()
       const releasedHandler = vi.fn()
       manager.on('lockReleasing', releasingHandler)
       manager.on('lockReleasing', releasingHandler)
@@ -167,11 +313,21 @@ describe('RtmManagerAdapter', () => {
 
 
       expect(releasingHandler).toHaveBeenCalledTimes(1)
       expect(releasingHandler).toHaveBeenCalledTimes(1)
       expect(releasedHandler).toHaveBeenCalledTimes(1)
       expect(releasedHandler).toHaveBeenCalledTimes(1)
+
+      // 验证锁方法被正确调用
+      const mockClient = (AgoraRTM.RTM as any).mock.results[0].value
+      expect(mockClient.lock.releaseLock).toHaveBeenCalledWith(
+        'test-channel',
+        'MESSAGE',
+        'lock_stt'
+      )
     })
     })
 
 
     it('should throw error when not joined', async () => {
     it('should throw error when not joined', async () => {
-      await expect(manager.releaseLock()).rejects.toThrow(SttError)
-      await expect(manager.releaseLock()).rejects.toThrow(
+      const unjoinedManager = new RtmManagerAdapter(mockAppId)
+
+      await expect(unjoinedManager.releaseLock()).rejects.toThrow(SttError)
+      await expect(unjoinedManager.releaseLock()).rejects.toThrow(
         'RTM manager must be joined to a channel before releasing lock'
         'RTM manager must be joined to a channel before releasing lock'
       )
       )
     })
     })
@@ -197,10 +353,115 @@ describe('RtmManagerAdapter', () => {
       expect(manager.userList).toHaveLength(0)
       expect(manager.userList).toHaveLength(0)
       expect(destroyingHandler).toHaveBeenCalledTimes(1)
       expect(destroyingHandler).toHaveBeenCalledTimes(1)
       expect(destroyedHandler).toHaveBeenCalledTimes(1)
       expect(destroyedHandler).toHaveBeenCalledTimes(1)
+
+      // 验证 RTM 客户端被正确销毁
+      const mockClient = (AgoraRTM.RTM as any).mock.results[0].value
+      expect(mockClient.logout).toHaveBeenCalledTimes(1)
     })
     })
 
 
     it('should handle destroy when not joined', async () => {
     it('should handle destroy when not joined', async () => {
       await expect(manager.destroy()).resolves.not.toThrow()
       await expect(manager.destroy()).resolves.not.toThrow()
     })
     })
+
+    it('should remove all event listeners', async () => {
+      await manager.join({
+        channel: 'test-channel',
+        userId: 'test-user',
+        userName: 'Test User',
+      })
+
+      const testHandler = vi.fn()
+      manager.on('connected', testHandler)
+
+      await manager.destroy()
+
+      // 验证事件监听器被移除
+      manager.emit('connected', { channel: 'test', userId: 'test' })
+      expect(testHandler).not.toHaveBeenCalled()
+    })
+  })
+
+  describe('event handling', () => {
+    beforeEach(async () => {
+      await manager.join({
+        channel: 'test-channel',
+        userId: 'test-user',
+        userName: 'Test User',
+      })
+    })
+
+    it('should handle presence events correctly', async () => {
+      const userListChangedHandler = vi.fn()
+      manager.on('userListChanged', userListChangedHandler)
+
+      // 模拟 presence 事件
+      const mockClient = (AgoraRTM.RTM as any).mock.results[0].value
+      const presenceHandler = mockClient.addEventListener.mock.calls.find(
+        (call: any) => call[0] === 'presence'
+      )[1]
+
+      // 模拟 SNAPSHOT 事件
+      presenceHandler({
+        channelName: 'test-channel',
+        eventType: 'SNAPSHOT',
+        snapshot: [
+          {
+            states: {
+              type: 'UserInfo',
+              userId: 'other-user',
+              userName: 'Other User',
+            },
+          },
+        ],
+      })
+
+      expect(userListChangedHandler).toHaveBeenCalledWith(
+        expect.arrayContaining([
+          { userId: 'test-user', userName: 'Test User' },
+          { userId: 'other-user', userName: 'Other User' },
+        ])
+      )
+    })
+
+    it('should handle storage events correctly', async () => {
+      const languagesChangedHandler = vi.fn()
+      const sttDataChangedHandler = vi.fn()
+      manager.on('languagesChanged', languagesChangedHandler)
+      manager.on('sttDataChanged', sttDataChangedHandler)
+
+      // 模拟 storage 事件
+      const mockClient = (AgoraRTM.RTM as any).mock.results[0].value
+      const storageHandler = mockClient.addEventListener.mock.calls.find(
+        (call: any) => call[0] === 'storage'
+      )[1]
+
+      // 模拟 UPDATE 事件
+      storageHandler({
+        channelName: 'test-channel',
+        eventType: 'UPDATE',
+        data: {
+          metadata: {
+            transcribe1: { value: '"en-US"' },
+            translate1List: { value: '["zh-CN","ja-JP"]' },
+            transcribe2: { value: '"zh-CN"' },
+            translate2List: { value: '["en-US"]' },
+            status: { value: '"start"' },
+            taskId: { value: '"test-task-id"' },
+          },
+        },
+      })
+
+      expect(languagesChangedHandler).toHaveBeenCalledWith({
+        transcribe1: 'en-US',
+        translate1List: ['zh-CN', 'ja-JP'],
+        transcribe2: 'zh-CN',
+        translate2List: ['en-US'],
+      })
+
+      expect(sttDataChangedHandler).toHaveBeenCalledWith({
+        status: 'start',
+        taskId: 'test-task-id',
+      })
+    })
   })
   })
 })
 })

+ 6 - 1
packages/stt-sdk-core/tests/setup.ts

@@ -26,7 +26,12 @@ vi.mock('agora-rtm', () => {
       },
       },
     })),
     })),
   }
   }
-  return mockRTM
+
+  // 返回包含默认导出的对象
+  return {
+    default: mockRTM,
+    ...mockRTM,
+  }
 })
 })
 
 
 // 模拟 fetch API
 // 模拟 fetch API