| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483 |
- 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(mockAppId)
- })
- afterEach(async () => {
- if (manager.isJoined) {
- await manager.destroy()
- }
- })
- describe('join', () => {
- it('should join successfully with valid config', async () => {
- const config = {
- channel: 'test-channel',
- userId: 'test-user',
- userName: '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')
- expect(manager.userList).toHaveLength(1)
- expect(manager.userList[0]).toEqual({
- userId: 'test-user',
- userName: 'Test User',
- })
- // 验证 RTM 客户端被正确创建
- expect(AgoraRTM.RTM).toHaveBeenCalledWith(mockAppId, 'test-user', {})
- })
- it('should throw error when config is invalid', async () => {
- const config = {
- channel: '',
- userId: '',
- userName: '',
- }
- await expect(manager.join(config)).rejects.toThrow(SttError)
- await expect(manager.join(config)).rejects.toThrow(
- 'Missing required configuration parameters'
- )
- })
- it('should throw error when already joined', async () => {
- const config = {
- channel: 'test-channel',
- userId: 'test-user',
- userName: 'Test User',
- }
- await manager.join(config)
- await expect(manager.join(config)).rejects.toThrow(SttError)
- await expect(manager.join(config)).rejects.toThrow(
- 'RTM manager is already joined to a channel'
- )
- })
- 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)
- const config = {
- channel: 'test-channel',
- userId: 'test-user',
- userName: 'Test User',
- }
- await manager.join(config)
- expect(connectedHandler).toHaveBeenCalledTimes(1)
- expect(connectedHandler).toHaveBeenCalledWith({
- channel: 'test-channel',
- 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 mockRTM = vi.mocked(AgoraRTM.RTM)
- const mockClient = mockRTM.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', () => {
- 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)
- manager.on('metadataUpdated', updatedHandler)
- const data = {
- status: 'start',
- taskId: 'test-task-id',
- token: 'test-token',
- startTime: Date.now(),
- duration: 3600000,
- }
- await manager.updateSttData(data)
- expect(updatingHandler).toHaveBeenCalledTimes(1)
- expect(updatingHandler).toHaveBeenCalledWith({ data })
- expect(updatedHandler).toHaveBeenCalledTimes(1)
- expect(updatedHandler).toHaveBeenCalledWith({ data })
- // 验证存储方法被正确调用
- const mockRTM = vi.mocked(AgoraRTM.RTM)
- const mockClient = mockRTM.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(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 () => {
- // 重置存储方法的调用记录
- const mockRTM = vi.mocked(AgoraRTM.RTM)
- const mockClient = mockRTM.mock.results[0].value
- mockClient.storage.setChannelMetadata.mockClear()
- await manager.updateSttData({})
- // 验证存储方法没有被调用(因为数据为空)
- 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 mockRTM = vi.mocked(AgoraRTM.RTM)
- const mockClient = mockRTM.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 mockRTM = vi.mocked(AgoraRTM.RTM)
- const mockClient = mockRTM.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', () => {
- 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)
- manager.on('lockAcquired', acquiredHandler)
- await manager.acquireLock()
- expect(acquiringHandler).toHaveBeenCalledTimes(1)
- expect(acquiredHandler).toHaveBeenCalledTimes(1)
- // 验证锁方法被正确调用
- const mockRTM = vi.mocked(AgoraRTM.RTM)
- const mockClient = mockRTM.mock.results[0].value
- expect(mockClient.lock.acquireLock).toHaveBeenCalledWith(
- 'test-channel',
- 'MESSAGE',
- 'lock_stt'
- )
- })
- it('should throw error when not joined', async () => {
- 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', () => {
- 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)
- manager.on('lockReleased', releasedHandler)
- await manager.releaseLock()
- expect(releasingHandler).toHaveBeenCalledTimes(1)
- expect(releasedHandler).toHaveBeenCalledTimes(1)
- // 验证锁方法被正确调用
- const mockRTM = vi.mocked(AgoraRTM.RTM)
- const mockClient = mockRTM.mock.results[0].value
- expect(mockClient.lock.releaseLock).toHaveBeenCalledWith(
- 'test-channel',
- 'MESSAGE',
- 'lock_stt'
- )
- })
- it('should throw error when not joined', async () => {
- 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'
- )
- })
- })
- describe('destroy', () => {
- it('should destroy manager successfully', async () => {
- // 重置模拟调用次数
- vi.clearAllMocks()
- await manager.join({
- channel: 'test-channel',
- userId: 'test-user',
- userName: 'Test User',
- })
- const destroyingHandler = vi.fn()
- const destroyedHandler = vi.fn()
- manager.on('destroying', destroyingHandler)
- manager.on('destroyed', destroyedHandler)
- await manager.destroy()
- expect(manager.isJoined).toBe(false)
- expect(manager.config).toBeUndefined()
- expect(manager.userList).toHaveLength(0)
- expect(destroyingHandler).toHaveBeenCalledTimes(1)
- expect(destroyedHandler).toHaveBeenCalledTimes(1)
- // 验证 RTM 客户端被正确销毁
- const mockRTM = vi.mocked(AgoraRTM.RTM)
- const mockClient = mockRTM.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 mockRTM = vi.mocked(AgoraRTM.RTM)
- const mockClient = mockRTM.mock.results[0].value
- const presenceHandler = mockClient.eventHandlers?.presence
- if (presenceHandler) {
- // 模拟 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 mockRTM = vi.mocked(AgoraRTM.RTM)
- const mockClient = mockRTM.mock.results[0].value
- const storageHandler = mockClient.eventHandlers?.storage
- if (storageHandler) {
- // 模拟 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',
- })
- }
- })
- })
- })
|