|
@@ -1,12 +1,33 @@
|
|
|
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest'
|
|
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest'
|
|
|
import { SttManagerAdapter } from '../../src/managers/stt-manager-adapter'
|
|
import { SttManagerAdapter } from '../../src/managers/stt-manager-adapter'
|
|
|
import { SttError } from '../../src/core/stt-error'
|
|
import { SttError } from '../../src/core/stt-error'
|
|
|
|
|
+import type { RtmManagerAdapter } from '../../src/managers/rtm-manager-adapter'
|
|
|
|
|
|
|
|
describe('SttManagerAdapter', () => {
|
|
describe('SttManagerAdapter', () => {
|
|
|
let manager: SttManagerAdapter
|
|
let manager: SttManagerAdapter
|
|
|
|
|
+ let mockRtmManager: RtmManagerAdapter
|
|
|
|
|
+ const mockAppId = 'test-app-id'
|
|
|
|
|
|
|
|
beforeEach(() => {
|
|
beforeEach(() => {
|
|
|
- manager = new SttManagerAdapter()
|
|
|
|
|
|
|
+ // 创建模拟的 RTM 管理器
|
|
|
|
|
+ mockRtmManager = {
|
|
|
|
|
+ join: vi.fn().mockResolvedValue(undefined),
|
|
|
|
|
+ updateSttData: vi.fn().mockResolvedValue(undefined),
|
|
|
|
|
+ updateLanguages: vi.fn().mockResolvedValue(undefined),
|
|
|
|
|
+ acquireLock: vi.fn().mockResolvedValue(undefined),
|
|
|
|
|
+ releaseLock: vi.fn().mockResolvedValue(undefined),
|
|
|
|
|
+ destroy: vi.fn().mockResolvedValue(undefined),
|
|
|
|
|
+ isJoined: false,
|
|
|
|
|
+ config: undefined,
|
|
|
|
|
+ userId: '',
|
|
|
|
|
+ channel: '',
|
|
|
|
|
+ userList: [],
|
|
|
|
|
+ on: vi.fn().mockReturnThis(),
|
|
|
|
|
+ off: vi.fn().mockReturnThis(),
|
|
|
|
|
+ emit: vi.fn().mockReturnThis(),
|
|
|
|
|
+ } as any
|
|
|
|
|
+
|
|
|
|
|
+ manager = new SttManagerAdapter(mockRtmManager, mockAppId)
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
afterEach(async () => {
|
|
afterEach(async () => {
|
|
@@ -29,6 +50,11 @@ describe('SttManagerAdapter', () => {
|
|
|
expect(manager.config).toEqual(config)
|
|
expect(manager.config).toEqual(config)
|
|
|
expect(manager.userId).toBe('test-user')
|
|
expect(manager.userId).toBe('test-user')
|
|
|
expect(manager.channel).toBe('test-channel')
|
|
expect(manager.channel).toBe('test-channel')
|
|
|
|
|
+ expect(mockRtmManager.join).toHaveBeenCalledWith({
|
|
|
|
|
+ channel: 'test-channel',
|
|
|
|
|
+ userId: 'test-user',
|
|
|
|
|
+ userName: 'Test User',
|
|
|
|
|
+ })
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
it('should throw error when config is invalid', async () => {
|
|
it('should throw error when config is invalid', async () => {
|
|
@@ -44,6 +70,20 @@ describe('SttManagerAdapter', () => {
|
|
|
)
|
|
)
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
|
|
+ it('should throw error when appId is not provided', async () => {
|
|
|
|
|
+ const managerWithoutAppId = new SttManagerAdapter(mockRtmManager)
|
|
|
|
|
+ const config = {
|
|
|
|
|
+ userId: 'test-user',
|
|
|
|
|
+ channel: 'test-channel',
|
|
|
|
|
+ userName: 'Test User',
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ await expect(managerWithoutAppId.init(config)).rejects.toThrow(SttError)
|
|
|
|
|
+ await expect(managerWithoutAppId.init(config)).rejects.toThrow(
|
|
|
|
|
+ 'App ID is required for STT operations'
|
|
|
|
|
+ )
|
|
|
|
|
+ })
|
|
|
|
|
+
|
|
|
it('should emit initialized event', async () => {
|
|
it('should emit initialized event', async () => {
|
|
|
const initializedHandler = vi.fn()
|
|
const initializedHandler = vi.fn()
|
|
|
manager.on('initialized', initializedHandler)
|
|
manager.on('initialized', initializedHandler)
|
|
@@ -65,13 +105,15 @@ describe('SttManagerAdapter', () => {
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
describe('startTranscription', () => {
|
|
describe('startTranscription', () => {
|
|
|
- it('should start transcription when initialized', async () => {
|
|
|
|
|
|
|
+ beforeEach(async () => {
|
|
|
await manager.init({
|
|
await manager.init({
|
|
|
userId: 'test-user',
|
|
userId: 'test-user',
|
|
|
channel: 'test-channel',
|
|
channel: 'test-channel',
|
|
|
userName: 'Test User',
|
|
userName: 'Test User',
|
|
|
})
|
|
})
|
|
|
|
|
+ })
|
|
|
|
|
|
|
|
|
|
+ it('should start transcription successfully', async () => {
|
|
|
const startingHandler = vi.fn()
|
|
const startingHandler = vi.fn()
|
|
|
const startedHandler = vi.fn()
|
|
const startedHandler = vi.fn()
|
|
|
manager.on('transcriptionStarting', startingHandler)
|
|
manager.on('transcriptionStarting', startingHandler)
|
|
@@ -86,28 +128,37 @@ describe('SttManagerAdapter', () => {
|
|
|
expect(startingHandler).toHaveBeenCalledTimes(1)
|
|
expect(startingHandler).toHaveBeenCalledTimes(1)
|
|
|
expect(startingHandler).toHaveBeenCalledWith({ languages: [{ source: 'en-US' }] })
|
|
expect(startingHandler).toHaveBeenCalledWith({ languages: [{ source: 'en-US' }] })
|
|
|
expect(startedHandler).toHaveBeenCalledTimes(1)
|
|
expect(startedHandler).toHaveBeenCalledTimes(1)
|
|
|
- expect(startedHandler.mock.calls[0][0].taskId).toMatch(/^task-\d+$/)
|
|
|
|
|
- expect(startedHandler.mock.calls[0][0].languages).toEqual([{ source: 'en-US' }])
|
|
|
|
|
|
|
+ expect(startedHandler).toHaveBeenCalledWith({
|
|
|
|
|
+ taskId: 'test-task-id',
|
|
|
|
|
+ languages: [{ source: 'en-US' }],
|
|
|
|
|
+ })
|
|
|
|
|
+
|
|
|
|
|
+ // 验证 RTM 管理器被正确调用
|
|
|
|
|
+ expect(mockRtmManager.acquireLock).toHaveBeenCalledTimes(1)
|
|
|
|
|
+ expect(mockRtmManager.releaseLock).toHaveBeenCalledTimes(1)
|
|
|
|
|
+ expect(mockRtmManager.updateLanguages).toHaveBeenCalledWith([{ source: 'en-US' }])
|
|
|
|
|
+ expect(mockRtmManager.updateSttData).toHaveBeenCalledWith({
|
|
|
|
|
+ status: 'start',
|
|
|
|
|
+ taskId: 'test-task-id',
|
|
|
|
|
+ token: 'test-token',
|
|
|
|
|
+ startTime: expect.any(Number),
|
|
|
|
|
+ duration: 3600000,
|
|
|
|
|
+ })
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
it('should throw error when not initialized', async () => {
|
|
it('should throw error when not initialized', async () => {
|
|
|
|
|
+ const uninitializedManager = new SttManagerAdapter(mockRtmManager, mockAppId)
|
|
|
const options = {
|
|
const options = {
|
|
|
languages: [{ source: 'en-US' }],
|
|
languages: [{ source: 'en-US' }],
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- await expect(manager.startTranscription(options)).rejects.toThrow(SttError)
|
|
|
|
|
- await expect(manager.startTranscription(options)).rejects.toThrow(
|
|
|
|
|
|
|
+ await expect(uninitializedManager.startTranscription(options)).rejects.toThrow(SttError)
|
|
|
|
|
+ await expect(uninitializedManager.startTranscription(options)).rejects.toThrow(
|
|
|
'SttManager must be initialized before starting transcription'
|
|
'SttManager must be initialized before starting transcription'
|
|
|
)
|
|
)
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
it('should throw error when languages are empty', async () => {
|
|
it('should throw error when languages are empty', async () => {
|
|
|
- await manager.init({
|
|
|
|
|
- userId: 'test-user',
|
|
|
|
|
- channel: 'test-channel',
|
|
|
|
|
- userName: 'Test User',
|
|
|
|
|
- })
|
|
|
|
|
-
|
|
|
|
|
const options = {
|
|
const options = {
|
|
|
languages: [],
|
|
languages: [],
|
|
|
}
|
|
}
|
|
@@ -117,16 +168,48 @@ describe('SttManagerAdapter', () => {
|
|
|
'At least one language must be provided'
|
|
'At least one language must be provided'
|
|
|
)
|
|
)
|
|
|
})
|
|
})
|
|
|
|
|
+
|
|
|
|
|
+ it('should handle API errors gracefully', async () => {
|
|
|
|
|
+ // 模拟 API 调用失败
|
|
|
|
|
+ global.fetch = vi.fn().mockResolvedValue({
|
|
|
|
|
+ ok: false,
|
|
|
|
|
+ status: 500,
|
|
|
|
|
+ json: vi.fn().mockResolvedValue({ message: 'Internal Server Error' }),
|
|
|
|
|
+ })
|
|
|
|
|
+
|
|
|
|
|
+ const options = {
|
|
|
|
|
+ languages: [{ source: 'en-US' }],
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ await expect(manager.startTranscription(options)).rejects.toThrow()
|
|
|
|
|
+
|
|
|
|
|
+ // 恢复默认的 fetch 模拟
|
|
|
|
|
+ global.fetch = vi.fn().mockResolvedValue({
|
|
|
|
|
+ ok: true,
|
|
|
|
|
+ status: 200,
|
|
|
|
|
+ json: vi.fn().mockResolvedValue({
|
|
|
|
|
+ tokenName: 'test-token',
|
|
|
|
|
+ taskId: 'test-task-id',
|
|
|
|
|
+ }),
|
|
|
|
|
+ })
|
|
|
|
|
+ })
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
describe('stopTranscription', () => {
|
|
describe('stopTranscription', () => {
|
|
|
- it('should stop transcription when initialized', async () => {
|
|
|
|
|
|
|
+ beforeEach(async () => {
|
|
|
await manager.init({
|
|
await manager.init({
|
|
|
userId: 'test-user',
|
|
userId: 'test-user',
|
|
|
channel: 'test-channel',
|
|
channel: 'test-channel',
|
|
|
userName: 'Test User',
|
|
userName: 'Test User',
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
|
|
+ // 先启动转录以设置 taskId 和 token
|
|
|
|
|
+ await manager.startTranscription({
|
|
|
|
|
+ languages: [{ source: 'en-US' }],
|
|
|
|
|
+ })
|
|
|
|
|
+ })
|
|
|
|
|
+
|
|
|
|
|
+ it('should stop transcription successfully', async () => {
|
|
|
const stoppingHandler = vi.fn()
|
|
const stoppingHandler = vi.fn()
|
|
|
const stoppedHandler = vi.fn()
|
|
const stoppedHandler = vi.fn()
|
|
|
manager.on('transcriptionStopping', stoppingHandler)
|
|
manager.on('transcriptionStopping', stoppingHandler)
|
|
@@ -136,38 +219,84 @@ describe('SttManagerAdapter', () => {
|
|
|
|
|
|
|
|
expect(stoppingHandler).toHaveBeenCalledTimes(1)
|
|
expect(stoppingHandler).toHaveBeenCalledTimes(1)
|
|
|
expect(stoppedHandler).toHaveBeenCalledTimes(1)
|
|
expect(stoppedHandler).toHaveBeenCalledTimes(1)
|
|
|
|
|
+
|
|
|
|
|
+ // 验证 RTM 管理器被正确调用
|
|
|
|
|
+ expect(mockRtmManager.acquireLock).toHaveBeenCalledTimes(2) // start 和 stop 各一次
|
|
|
|
|
+ expect(mockRtmManager.releaseLock).toHaveBeenCalledTimes(2)
|
|
|
|
|
+ expect(mockRtmManager.updateSttData).toHaveBeenCalledWith({
|
|
|
|
|
+ status: 'end',
|
|
|
|
|
+ })
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
it('should throw error when not initialized', async () => {
|
|
it('should throw error when not initialized', async () => {
|
|
|
- await expect(manager.stopTranscription()).rejects.toThrow(SttError)
|
|
|
|
|
- await expect(manager.stopTranscription()).rejects.toThrow(
|
|
|
|
|
|
|
+ const uninitializedManager = new SttManagerAdapter(mockRtmManager, mockAppId)
|
|
|
|
|
+
|
|
|
|
|
+ await expect(uninitializedManager.stopTranscription()).rejects.toThrow(SttError)
|
|
|
|
|
+ await expect(uninitializedManager.stopTranscription()).rejects.toThrow(
|
|
|
'SttManager must be initialized before stopping transcription'
|
|
'SttManager must be initialized before stopping transcription'
|
|
|
)
|
|
)
|
|
|
})
|
|
})
|
|
|
|
|
+
|
|
|
|
|
+ it('should throw error when no active task found', async () => {
|
|
|
|
|
+ const managerWithoutTask = new SttManagerAdapter(mockRtmManager, mockAppId)
|
|
|
|
|
+ await managerWithoutTask.init({
|
|
|
|
|
+ userId: 'test-user',
|
|
|
|
|
+ channel: 'test-channel',
|
|
|
|
|
+ userName: 'Test User',
|
|
|
|
|
+ })
|
|
|
|
|
+
|
|
|
|
|
+ await expect(managerWithoutTask.stopTranscription()).rejects.toThrow(SttError)
|
|
|
|
|
+ await expect(managerWithoutTask.stopTranscription()).rejects.toThrow(
|
|
|
|
|
+ 'No active transcription task found'
|
|
|
|
|
+ )
|
|
|
|
|
+ })
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
describe('queryTranscription', () => {
|
|
describe('queryTranscription', () => {
|
|
|
- it('should query transcription when initialized', async () => {
|
|
|
|
|
|
|
+ beforeEach(async () => {
|
|
|
await manager.init({
|
|
await manager.init({
|
|
|
userId: 'test-user',
|
|
userId: 'test-user',
|
|
|
channel: 'test-channel',
|
|
channel: 'test-channel',
|
|
|
userName: 'Test User',
|
|
userName: 'Test User',
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
|
|
+ // 先启动转录以设置 taskId 和 token
|
|
|
|
|
+ await manager.startTranscription({
|
|
|
|
|
+ languages: [{ source: 'en-US' }],
|
|
|
|
|
+ })
|
|
|
|
|
+ })
|
|
|
|
|
+
|
|
|
|
|
+ it('should query transcription successfully', async () => {
|
|
|
const result = await manager.queryTranscription()
|
|
const result = await manager.queryTranscription()
|
|
|
|
|
|
|
|
expect(result).toEqual({
|
|
expect(result).toEqual({
|
|
|
- status: 'completed',
|
|
|
|
|
- results: [],
|
|
|
|
|
|
|
+ tokenName: 'test-token',
|
|
|
|
|
+ taskId: 'test-task-id',
|
|
|
})
|
|
})
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
it('should throw error when not initialized', async () => {
|
|
it('should throw error when not initialized', async () => {
|
|
|
- await expect(manager.queryTranscription()).rejects.toThrow(SttError)
|
|
|
|
|
- await expect(manager.queryTranscription()).rejects.toThrow(
|
|
|
|
|
|
|
+ const uninitializedManager = new SttManagerAdapter(mockRtmManager, mockAppId)
|
|
|
|
|
+
|
|
|
|
|
+ await expect(uninitializedManager.queryTranscription()).rejects.toThrow(SttError)
|
|
|
|
|
+ await expect(uninitializedManager.queryTranscription()).rejects.toThrow(
|
|
|
'SttManager must be initialized before querying transcription'
|
|
'SttManager must be initialized before querying transcription'
|
|
|
)
|
|
)
|
|
|
})
|
|
})
|
|
|
|
|
+
|
|
|
|
|
+ it('should throw error when no active task found', async () => {
|
|
|
|
|
+ const managerWithoutTask = new SttManagerAdapter(mockRtmManager, mockAppId)
|
|
|
|
|
+ await managerWithoutTask.init({
|
|
|
|
|
+ userId: 'test-user',
|
|
|
|
|
+ channel: 'test-channel',
|
|
|
|
|
+ userName: 'Test User',
|
|
|
|
|
+ })
|
|
|
|
|
+
|
|
|
|
|
+ await expect(managerWithoutTask.queryTranscription()).rejects.toThrow(SttError)
|
|
|
|
|
+ await expect(managerWithoutTask.queryTranscription()).rejects.toThrow(
|
|
|
|
|
+ 'No active transcription task found'
|
|
|
|
|
+ )
|
|
|
|
|
+ })
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
describe('destroy', () => {
|
|
describe('destroy', () => {
|
|
@@ -189,10 +318,50 @@ describe('SttManagerAdapter', () => {
|
|
|
expect(manager.config).toBeUndefined()
|
|
expect(manager.config).toBeUndefined()
|
|
|
expect(destroyingHandler).toHaveBeenCalledTimes(1)
|
|
expect(destroyingHandler).toHaveBeenCalledTimes(1)
|
|
|
expect(destroyedHandler).toHaveBeenCalledTimes(1)
|
|
expect(destroyedHandler).toHaveBeenCalledTimes(1)
|
|
|
|
|
+ expect(mockRtmManager.destroy).toHaveBeenCalledTimes(1)
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
it('should handle destroy when not initialized', async () => {
|
|
it('should handle destroy when not initialized', async () => {
|
|
|
await expect(manager.destroy()).resolves.not.toThrow()
|
|
await expect(manager.destroy()).resolves.not.toThrow()
|
|
|
})
|
|
})
|
|
|
})
|
|
})
|
|
|
|
|
+
|
|
|
|
|
+ describe('extendDuration', () => {
|
|
|
|
|
+ beforeEach(async () => {
|
|
|
|
|
+ await manager.init({
|
|
|
|
|
+ userId: 'test-user',
|
|
|
|
|
+ channel: 'test-channel',
|
|
|
|
|
+ userName: 'Test User',
|
|
|
|
|
+ })
|
|
|
|
|
+ })
|
|
|
|
|
+
|
|
|
|
|
+ it('should extend duration successfully', async () => {
|
|
|
|
|
+ const extendingHandler = vi.fn()
|
|
|
|
|
+ const extendedHandler = vi.fn()
|
|
|
|
|
+ manager.on('durationExtending', extendingHandler)
|
|
|
|
|
+ manager.on('durationExtended', extendedHandler)
|
|
|
|
|
+
|
|
|
|
|
+ const options = {
|
|
|
|
|
+ startTime: Date.now(),
|
|
|
|
|
+ duration: 7200000, // 2小时
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ await manager.extendDuration(options)
|
|
|
|
|
+
|
|
|
|
|
+ expect(extendingHandler).toHaveBeenCalledTimes(1)
|
|
|
|
|
+ expect(extendingHandler).toHaveBeenCalledWith(options)
|
|
|
|
|
+ expect(extendedHandler).toHaveBeenCalledTimes(1)
|
|
|
|
|
+ expect(extendedHandler).toHaveBeenCalledWith(options)
|
|
|
|
|
+ expect(mockRtmManager.updateSttData).toHaveBeenCalledWith(options)
|
|
|
|
|
+ })
|
|
|
|
|
+
|
|
|
|
|
+ it('should throw error when not initialized', async () => {
|
|
|
|
|
+ const uninitializedManager = new SttManagerAdapter(mockRtmManager, mockAppId)
|
|
|
|
|
+
|
|
|
|
|
+ await expect(uninitializedManager.extendDuration({})).rejects.toThrow(SttError)
|
|
|
|
|
+ await expect(uninitializedManager.extendDuration({})).rejects.toThrow(
|
|
|
|
|
+ 'SttManager must be initialized before extending duration'
|
|
|
|
|
+ )
|
|
|
|
|
+ })
|
|
|
|
|
+ })
|
|
|
})
|
|
})
|