|
|
@@ -1,12 +1,14 @@
|
|
|
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest'
|
|
|
import { RtmManagerAdapter } from '../../src/managers/rtm-manager-adapter'
|
|
|
import { SttError } from '../../src/core/stt-error'
|
|
|
+import AgoraRTM from 'agora-rtm'
|
|
|
|
|
|
describe('RtmManagerAdapter', () => {
|
|
|
let manager: RtmManagerAdapter
|
|
|
+ const mockAppId = 'test-app-id'
|
|
|
|
|
|
beforeEach(() => {
|
|
|
- manager = new RtmManagerAdapter()
|
|
|
+ manager = new RtmManagerAdapter(mockAppId)
|
|
|
})
|
|
|
|
|
|
afterEach(async () => {
|
|
|
@@ -34,6 +36,9 @@ describe('RtmManagerAdapter', () => {
|
|
|
userId: 'test-user',
|
|
|
userName: 'Test User',
|
|
|
})
|
|
|
+
|
|
|
+ // 验证 RTM 客户端被正确创建
|
|
|
+ expect(AgoraRTM.RTM).toHaveBeenCalledWith(mockAppId, 'test-user', {})
|
|
|
})
|
|
|
|
|
|
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 () => {
|
|
|
const connectedHandler = vi.fn()
|
|
|
manager.on('connected', connectedHandler)
|
|
|
@@ -82,16 +101,34 @@ describe('RtmManagerAdapter', () => {
|
|
|
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', () => {
|
|
|
- it('should update STT data when joined', async () => {
|
|
|
+ beforeEach(async () => {
|
|
|
await manager.join({
|
|
|
channel: 'test-channel',
|
|
|
userId: 'test-user',
|
|
|
userName: 'Test User',
|
|
|
})
|
|
|
+ })
|
|
|
|
|
|
+ it('should update STT data successfully', async () => {
|
|
|
const updatingHandler = vi.fn()
|
|
|
const updatedHandler = vi.fn()
|
|
|
manager.on('metadataUpdating', updatingHandler)
|
|
|
@@ -100,6 +137,9 @@ describe('RtmManagerAdapter', () => {
|
|
|
const data = {
|
|
|
status: 'start',
|
|
|
taskId: 'test-task-id',
|
|
|
+ token: 'test-token',
|
|
|
+ startTime: Date.now(),
|
|
|
+ duration: 3600000,
|
|
|
}
|
|
|
|
|
|
await manager.updateSttData(data)
|
|
|
@@ -108,29 +148,123 @@ describe('RtmManagerAdapter', () => {
|
|
|
expect(updatingHandler).toHaveBeenCalledWith({ data })
|
|
|
expect(updatedHandler).toHaveBeenCalledTimes(1)
|
|
|
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 () => {
|
|
|
+ const unjoinedManager = new RtmManagerAdapter(mockAppId)
|
|
|
const data = {
|
|
|
status: 'start',
|
|
|
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'
|
|
|
)
|
|
|
})
|
|
|
+
|
|
|
+ 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', () => {
|
|
|
- it('should acquire lock when joined', async () => {
|
|
|
+ beforeEach(async () => {
|
|
|
await manager.join({
|
|
|
channel: 'test-channel',
|
|
|
userId: 'test-user',
|
|
|
userName: 'Test User',
|
|
|
})
|
|
|
+ })
|
|
|
|
|
|
+ it('should acquire lock successfully', async () => {
|
|
|
const acquiringHandler = vi.fn()
|
|
|
const acquiredHandler = vi.fn()
|
|
|
manager.on('lockAcquiring', acquiringHandler)
|
|
|
@@ -140,24 +274,36 @@ describe('RtmManagerAdapter', () => {
|
|
|
|
|
|
expect(acquiringHandler).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 () => {
|
|
|
- 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'
|
|
|
)
|
|
|
})
|
|
|
})
|
|
|
|
|
|
describe('releaseLock', () => {
|
|
|
- it('should release lock when joined', async () => {
|
|
|
+ beforeEach(async () => {
|
|
|
await manager.join({
|
|
|
channel: 'test-channel',
|
|
|
userId: 'test-user',
|
|
|
userName: 'Test User',
|
|
|
})
|
|
|
+ })
|
|
|
|
|
|
+ it('should release lock successfully', async () => {
|
|
|
const releasingHandler = vi.fn()
|
|
|
const releasedHandler = vi.fn()
|
|
|
manager.on('lockReleasing', releasingHandler)
|
|
|
@@ -167,11 +313,21 @@ describe('RtmManagerAdapter', () => {
|
|
|
|
|
|
expect(releasingHandler).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 () => {
|
|
|
- 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'
|
|
|
)
|
|
|
})
|
|
|
@@ -197,10 +353,115 @@ describe('RtmManagerAdapter', () => {
|
|
|
expect(manager.userList).toHaveLength(0)
|
|
|
expect(destroyingHandler).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 () => {
|
|
|
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',
|
|
|
+ })
|
|
|
+ })
|
|
|
})
|
|
|
})
|