UNPKG

@volley/recognition-client-sdk

Version:

Recognition Service TypeScript/Node.js Client SDK

253 lines (227 loc) 8.17 kB
/** * Unit tests for URL Builder */ import { RecognitionContextTypeV1, STAGES } from '@recog/shared-types'; // Mock the shared-config module BEFORE importing the module under test const mockGetRecognitionServiceBase = jest.fn(); jest.mock('@recog/shared-config', () => ({ getRecognitionServiceBase: mockGetRecognitionServiceBase })); import { buildWebSocketUrl, UrlBuilderConfig } from './url-builder.js'; describe('buildWebSocketUrl', () => { const baseConfig: UrlBuilderConfig = { audioUtteranceId: 'test-utterance-123' }; beforeEach(() => { // Clear and reset mock before each test mockGetRecognitionServiceBase.mockClear(); mockGetRecognitionServiceBase.mockReturnValue({ wsBase: 'wss://recognition.volley.com' }); }); it('should build URL with only audioUtteranceId', () => { const url = buildWebSocketUrl(baseConfig); expect(url).toContain('audioUtteranceId=test-utterance-123'); }); it('should use default production URL if neither url nor stage provided', () => { const url = buildWebSocketUrl(baseConfig); expect(url).toContain('wss://recognition.volley.com/ws/v1/recognize'); expect(mockGetRecognitionServiceBase).toHaveBeenCalledWith('production'); }); it('should use stage parameter to build URL', () => { mockGetRecognitionServiceBase.mockReturnValue({ wsBase: 'wss://recognition-staging.volley.com' }); const config = { ...baseConfig, stage: 'staging' }; const url = buildWebSocketUrl(config); expect(url).toContain('wss://recognition-staging.volley.com/ws/v1/recognize'); expect(mockGetRecognitionServiceBase).toHaveBeenCalledWith('staging'); }); it('should accept Stage enum as stage parameter', () => { mockGetRecognitionServiceBase.mockReturnValue({ wsBase: 'wss://recognition-dev.volley.com' }); const config = { ...baseConfig, stage: STAGES.DEV }; const url = buildWebSocketUrl(config); expect(url).toContain('wss://recognition-dev.volley.com/ws/v1/recognize'); expect(mockGetRecognitionServiceBase).toHaveBeenCalledWith(STAGES.DEV); }); it('should prioritize url over stage when both provided', () => { const config = { ...baseConfig, url: 'ws://localhost:3101/ws/v1/recognize', stage: 'staging' }; const url = buildWebSocketUrl(config); expect(url).toContain('ws://localhost:3101/ws/v1/recognize'); // Mock should not be called when explicit URL is provided expect(mockGetRecognitionServiceBase).not.toHaveBeenCalled(); }); it('should use custom URL if provided (backward compatibility)', () => { const config = { ...baseConfig, url: 'ws://localhost:3101/ws/v1/recognize' }; const url = buildWebSocketUrl(config); expect(url).toContain('ws://localhost:3101/ws/v1/recognize'); // Mock should not be called when explicit URL is provided expect(mockGetRecognitionServiceBase).not.toHaveBeenCalled(); }); it('should add userId to query parameters', () => { const config = { ...baseConfig, userId: 'user-123' }; const url = buildWebSocketUrl(config); expect(url).toContain('userId=user-123'); }); it('should add gameSessionId to query parameters', () => { const config = { ...baseConfig, gameSessionId: 'session-456' }; const url = buildWebSocketUrl(config); expect(url).toContain('gameSessionId=session-456'); }); it('should add deviceId to query parameters', () => { const config = { ...baseConfig, deviceId: 'device-789' }; const url = buildWebSocketUrl(config); expect(url).toContain('deviceId=device-789'); }); it('should add accountId to query parameters', () => { const config = { ...baseConfig, accountId: 'account-abc' }; const url = buildWebSocketUrl(config); expect(url).toContain('accountId=account-abc'); }); it('should add questionAnswerId to query parameters', () => { const config = { ...baseConfig, questionAnswerId: 'qa-xyz' }; const url = buildWebSocketUrl(config); expect(url).toContain('questionAnswerId=qa-xyz'); }); it('should add platform to query parameters', () => { const config = { ...baseConfig, platform: 'ios' }; const url = buildWebSocketUrl(config); expect(url).toContain('platform=ios'); }); it('should add gameId and gamePhase from gameContext', () => { const config = { ...baseConfig, gameContext: { type: RecognitionContextTypeV1.GAME_CONTEXT as const, gameId: 'test-game', gamePhase: 'test-phase' } }; const url = buildWebSocketUrl(config); expect(url).toContain('gameId=test-game'); expect(url).toContain('gamePhase=test-phase'); }); it('should serialize callbackUrls as JSON', () => { const callbackUrls = [ { url: 'https://example.com/callback', messageTypes: ['transcript'] }, { url: 'https://example.com/metadata', messageTypes: ['metadata'] } ]; const config = { ...baseConfig, callbackUrls }; const url = buildWebSocketUrl(config); expect(url).toContain('callbackUrls='); // Decode and verify JSON structure const urlObj = new URL(url); const callbackUrlsParam = urlObj.searchParams.get('callbackUrls'); expect(callbackUrlsParam).toBeDefined(); expect(JSON.parse(callbackUrlsParam!)).toEqual(callbackUrls); }); it('should not add callbackUrls if empty array', () => { const config = { ...baseConfig, callbackUrls: [] }; const url = buildWebSocketUrl(config); expect(url).not.toContain('callbackUrls='); }); it('should not add optional parameters if not provided', () => { const url = buildWebSocketUrl(baseConfig); expect(url).not.toContain('userId='); expect(url).not.toContain('gameSessionId='); expect(url).not.toContain('deviceId='); expect(url).not.toContain('accountId='); expect(url).not.toContain('questionAnswerId='); expect(url).not.toContain('platform='); expect(url).not.toContain('gameId='); expect(url).not.toContain('gamePhase='); }); it('should handle all parameters together', () => { const config: UrlBuilderConfig = { url: 'ws://localhost:3101/ws/v1/recognize', audioUtteranceId: 'test-utterance-123', userId: 'user-123', gameSessionId: 'session-456', deviceId: 'device-789', accountId: 'account-abc', questionAnswerId: 'qa-xyz', platform: 'ios', gameContext: { type: RecognitionContextTypeV1.GAME_CONTEXT as const, gameId: 'test-game', gamePhase: 'test-phase' }, callbackUrls: [ { url: 'https://example.com/callback', messageTypes: ['transcript'] } ] }; const url = buildWebSocketUrl(config); expect(url).toContain('ws://localhost:3101/ws/v1/recognize'); expect(url).toContain('audioUtteranceId=test-utterance-123'); expect(url).toContain('userId=user-123'); expect(url).toContain('gameSessionId=session-456'); expect(url).toContain('deviceId=device-789'); expect(url).toContain('accountId=account-abc'); expect(url).toContain('questionAnswerId=qa-xyz'); expect(url).toContain('platform=ios'); expect(url).toContain('gameId=test-game'); expect(url).toContain('gamePhase=test-phase'); expect(url).toContain('callbackUrls='); }); it('should properly encode special characters in parameters', () => { const config = { ...baseConfig, userId: 'user@example.com', platform: 'iOS 17.0' }; const url = buildWebSocketUrl(config); // URL should be properly encoded expect(url).toBeDefined(); const urlObj = new URL(url); expect(urlObj.searchParams.get('userId')).toBe('user@example.com'); expect(urlObj.searchParams.get('platform')).toBe('iOS 17.0'); }); it('should create valid URL object', () => { const config = { ...baseConfig, userId: 'user-123' }; const url = buildWebSocketUrl(config); // Should not throw expect(() => new URL(url)).not.toThrow(); }); });