Parcourir la source

✨ feat(error): 添加APP_ID_REQUIRED错误码

✨ feat(sdk): 为管理器添加appId参数传递
- 在SttManagerAdapter初始化时传入appId
- 在RtmManagerAdapter初始化时传入appId

🐛 fix(rtm): 修复元数据更新和用户状态处理逻辑
- 添加数据检查,避免空数据时设置元数据
- 修复用户ID判断条件,确保正确更新用户信息
- 优化语言变化处理,避免JSON解析错误

✅ test(setup): 增强测试模拟配置
- 改进RTM客户端模拟实现,支持事件监听
- 为fetch添加URL特定响应,模拟转录API完整结果
yourname il y a 2 mois
Parent
commit
6d18dfb597

+ 1 - 0
packages/stt-sdk-core/src/core/stt-error.ts

@@ -9,6 +9,7 @@ export type SttErrorCode =
   | 'PERMISSION_DENIED'
   | 'TIMEOUT'
   | 'UNKNOWN_ERROR'
+  | 'APP_ID_REQUIRED'
 
 export class SttError extends Error {
   public readonly code: SttErrorCode

+ 2 - 2
packages/stt-sdk-core/src/core/stt-sdk.ts

@@ -59,7 +59,7 @@ export class SttSdk
       throw new SttError('NOT_INITIALIZED', 'SDK must be initialized before creating managers')
     }
 
-    const manager = new SttManagerAdapter()
+    const manager = new SttManagerAdapter(undefined, this._config?.appId)
     this._sttManagers.add(manager)
 
     // 监听管理器错误事件并转发
@@ -75,7 +75,7 @@ export class SttSdk
       throw new SttError('NOT_INITIALIZED', 'SDK must be initialized before creating managers')
     }
 
-    const manager = new RtmManagerAdapter()
+    const manager = new RtmManagerAdapter(this._config?.appId)
     this._rtmManagers.add(manager)
 
     // 监听管理器错误事件并转发

+ 12 - 8
packages/stt-sdk-core/src/managers/rtm-manager-adapter.ts

@@ -100,8 +100,12 @@ export class RtmManagerAdapter extends AGEventEmitter<RtmEventMap> implements IR
     try {
       this.emit('metadataUpdating', { data })
 
-      // 设置频道元数据
-      if (this._client) {
+      // 检查数据是否为空
+      const hasData = Object.keys(data).some(
+        (key) => data[key as keyof RtmChannelMetadata] !== undefined
+      )
+
+      if (hasData && this._client) {
         const metadataItems: MetadataItem[] = []
         for (const key in data) {
           if (data[key as keyof RtmChannelMetadata] !== undefined) {
@@ -330,7 +334,7 @@ export class RtmManagerAdapter extends AGEventEmitter<RtmEventMap> implements IR
           userName: states.userName,
           userId: states.userId,
         }
-        if (userInfo.userId && userInfo.userId !== this._userId) {
+        if (userInfo.userId) {
           this._userMap.set(userInfo.userId, userInfo)
           changed = true
         }
@@ -345,11 +349,11 @@ export class RtmManagerAdapter extends AGEventEmitter<RtmEventMap> implements IR
   private _dealStorageDataChanged(metadata: any): void {
     // 处理语言变化
     const { transcribe1, translate1List, transcribe2, translate2List } = metadata
-    if (transcribe1?.value) {
-      const parseTranscribe1 = JSON.parse(transcribe1.value)
-      const parseTranslate1 = JSON.parse(translate1List.value)
-      const parseTranscribe2 = JSON.parse(transcribe2.value)
-      const parseTranslate2 = JSON.parse(translate2List.value)
+    if (transcribe1?.value || transcribe2?.value) {
+      const parseTranscribe1 = transcribe1?.value ? JSON.parse(transcribe1.value) : ''
+      const parseTranslate1 = translate1List?.value ? JSON.parse(translate1List.value) : []
+      const parseTranscribe2 = transcribe2?.value ? JSON.parse(transcribe2.value) : ''
+      const parseTranslate2 = translate2List?.value ? JSON.parse(translate2List.value) : []
 
       this.emit('languagesChanged', {
         transcribe1: parseTranscribe1,

+ 61 - 29
packages/stt-sdk-core/tests/setup.ts

@@ -2,29 +2,36 @@ import { vi } from 'vitest'
 
 // 全局模拟配置 - 模拟真实的 Agora RTM SDK
 vi.mock('agora-rtm', () => {
+  // 创建模拟的 RTM 客户端实例
+  const mockClientInstance = {
+    login: vi.fn().mockResolvedValue(undefined),
+    logout: vi.fn().mockResolvedValue(undefined),
+    subscribe: vi.fn().mockResolvedValue(undefined),
+    unsubscribe: vi.fn().mockResolvedValue(undefined),
+    addEventListener: vi.fn().mockImplementation(function (this: any, eventType, handler) {
+      // 记录事件监听器调用
+      this.eventHandlers = this.eventHandlers || {}
+      this.eventHandlers[eventType] = handler
+    }),
+    removeEventListener: vi.fn(),
+    presence: {
+      setState: vi.fn().mockResolvedValue(undefined),
+      whoNow: vi.fn().mockResolvedValue({ totalOccupancy: 1 }),
+    },
+    storage: {
+      setChannelMetadata: vi.fn().mockResolvedValue(undefined),
+      removeChannelMetadata: vi.fn().mockResolvedValue(undefined),
+    },
+    lock: {
+      setLock: vi.fn().mockResolvedValue(undefined),
+      acquireLock: vi.fn().mockResolvedValue(undefined),
+      releaseLock: vi.fn().mockResolvedValue(undefined),
+      getLock: vi.fn().mockResolvedValue({ lockDetails: [] }),
+    },
+  }
+
   const mockRTM = {
-    RTM: vi.fn((appId: string, userId: string, config: any) => ({
-      login: vi.fn().mockResolvedValue(undefined),
-      logout: vi.fn().mockResolvedValue(undefined),
-      subscribe: vi.fn().mockResolvedValue(undefined),
-      unsubscribe: vi.fn().mockResolvedValue(undefined),
-      addEventListener: vi.fn(),
-      removeEventListener: vi.fn(),
-      presence: {
-        setState: vi.fn().mockResolvedValue(undefined),
-        whoNow: vi.fn().mockResolvedValue({ totalOccupancy: 1 }),
-      },
-      storage: {
-        setChannelMetadata: vi.fn().mockResolvedValue(undefined),
-        removeChannelMetadata: vi.fn().mockResolvedValue(undefined),
-      },
-      lock: {
-        setLock: vi.fn().mockResolvedValue(undefined),
-        acquireLock: vi.fn().mockResolvedValue(undefined),
-        releaseLock: vi.fn().mockResolvedValue(undefined),
-        getLock: vi.fn().mockResolvedValue({ lockDetails: [] }),
-      },
-    })),
+    RTM: vi.fn(() => mockClientInstance),
   }
 
   // 返回包含默认导出的对象
@@ -39,13 +46,38 @@ const mockFetch = vi.fn()
 global.fetch = mockFetch
 
 // 设置默认的 fetch 响应
-mockFetch.mockResolvedValue({
-  ok: true,
-  status: 200,
-  json: vi.fn().mockResolvedValue({
-    tokenName: 'test-token',
-    taskId: 'test-task-id',
-  }),
+mockFetch.mockImplementation((url: string) => {
+  // 根据URL决定返回不同的响应
+  if (url.includes('/tasks/') && url.includes('?builderToken=')) {
+    // 查询转录API - 返回完整的结果
+    return Promise.resolve({
+      ok: true,
+      status: 200,
+      json: vi.fn().mockResolvedValue({
+        status: 'completed',
+        results: [
+          {
+            text: 'Hello world',
+            startTime: 0,
+            endTime: 1000,
+            confidence: 0.95,
+          },
+        ],
+        tokenName: 'test-token',
+        taskId: 'test-task-id',
+      }),
+    })
+  }
+
+  // 默认响应
+  return Promise.resolve({
+    ok: true,
+    status: 200,
+    json: vi.fn().mockResolvedValue({
+      tokenName: 'test-token',
+      taskId: 'test-task-id',
+    }),
+  })
 })
 
 // 全局测试配置