UNPKG

pubnub

Version:

Publish & Subscribe Real-time Messaging with PubNub

331 lines (282 loc) 10.4 kB
/* global describe, beforeEach, it */ import assert from 'assert'; import { FetchMessagesRequest } from '../../../src/core/endpoints/fetch_messages'; import RequestOperation from '../../../src/core/constants/operations'; import { PubNubMessageType } from '../../../src/core/types/api/history'; import { KeySet } from '../../../src/core/types/api'; describe('FetchMessagesRequest', () => { let keySet: KeySet; let getFileUrl: (params: any) => string; beforeEach(() => { keySet = { subscribeKey: 'sub-key', publishKey: 'pub-key', }; getFileUrl = (params: any) => `https://example.com/files/${params.id}/${params.name}`; }); describe('validates required parameters', () => { it('should return error for missing subscribeKey', () => { const request = new FetchMessagesRequest({ keySet: { subscribeKey: '', publishKey: 'pub-key' }, channels: ['channel1'], getFileUrl, }); const error = request.validate(); assert.equal(error, 'Missing Subscribe Key'); }); it('should return error for missing channels', () => { // We can't test undefined/null channels because constructor accesses .length // But we can test the validate() method directly with a mock request const mockRequest = { parameters: { keySet: { subscribeKey: 'test-key' }, channels: null, getFileUrl: () => '', }, validate: FetchMessagesRequest.prototype.validate, }; const error = mockRequest.validate(); assert.equal(error, 'Missing channels'); }); it('should return error for includeMessageActions with multiple channels', () => { const request = new FetchMessagesRequest({ keySet, channels: ['channel1', 'channel2'], includeMessageActions: true, getFileUrl, }); const error = request.validate(); assert.equal( error, 'History can return actions data for a single channel only. Either pass a single channel or disable the includeMessageActions flag.' ); }); it('should return undefined for valid parameters', () => { const request = new FetchMessagesRequest({ keySet, channels: ['channel1'], getFileUrl, }); const error = request.validate(); assert.equal(error, undefined); }); }); describe('applies correct default values', () => { it('should default count to 100 for single channel', () => { const request = new FetchMessagesRequest({ keySet, channels: ['channel1'], getFileUrl, }); // Access private parameters property via type assertion const parameters = (request as any).parameters; assert.equal(parameters.count, 100); assert.equal(parameters.includeUUID, true); assert.equal(parameters.includeMessageType, true); assert.equal(parameters.stringifiedTimeToken, false); }); it('should default count to 25 for multiple channels', () => { const request = new FetchMessagesRequest({ keySet, channels: ['channel1', 'channel2'], getFileUrl, }); const parameters = (request as any).parameters; assert.equal(parameters.count, 25); }); it('should default count to 25 when includeMessageActions is true', () => { const request = new FetchMessagesRequest({ keySet, channels: ['channel1'], includeMessageActions: true, getFileUrl, }); const parameters = (request as any).parameters; assert.equal(parameters.count, 25); }); }); describe('constructs correct path for regular history', () => { it('should build path without includeMessageActions', () => { const request = new FetchMessagesRequest({ keySet, channels: ['channel1'], getFileUrl, }); const path = (request as any).path; assert.equal(path, '/v3/history/sub-key/sub-key/channel/channel1'); }); it('should encode channel names with special characters', () => { const request = new FetchMessagesRequest({ keySet, channels: ['channel#1', 'channel/2', 'channel 3'], getFileUrl, }); const path = (request as any).path; assert.equal(path, '/v3/history/sub-key/sub-key/channel/channel%231,channel%2F2,channel%203'); }); }); describe('constructs correct path for history-with-actions', () => { it('should build path with includeMessageActions=true', () => { const request = new FetchMessagesRequest({ keySet, channels: ['channel1'], includeMessageActions: true, getFileUrl, }); const path = (request as any).path; assert.equal(path, '/v3/history-with-actions/sub-key/sub-key/channel/channel1'); }); }); describe('builds query parameters correctly', () => { it('should include all optional parameters in query', () => { const request = new FetchMessagesRequest({ keySet, channels: ['channel1'], count: 50, start: '12345', end: '67890', includeUUID: true, includeMessageType: true, includeMeta: true, includeCustomMessageType: true, stringifiedTimeToken: true, getFileUrl, }); const queryParams = (request as any).queryParameters; assert.equal(queryParams.max, 50); assert.equal(queryParams.start, '12345'); assert.equal(queryParams.end, '67890'); assert.equal(queryParams.include_uuid, 'true'); assert.equal(queryParams.include_message_type, 'true'); assert.equal(queryParams.include_meta, 'true'); assert.equal(queryParams.include_custom_message_type, 'true'); assert.equal(queryParams.string_message_token, 'true'); }); it('should omit optional parameters when not provided', () => { const request = new FetchMessagesRequest({ keySet, channels: ['channel1'], getFileUrl, }); const queryParams = (request as any).queryParameters; assert.equal(queryParams.start, undefined); assert.equal(queryParams.end, undefined); assert.equal(queryParams.include_meta, undefined); assert.equal(queryParams.string_message_token, undefined); }); it('should handle includeCustomMessageType false value', () => { const request = new FetchMessagesRequest({ keySet, channels: ['channel1'], includeCustomMessageType: false, getFileUrl, }); const queryParams = (request as any).queryParameters; assert.equal(queryParams.include_custom_message_type, 'false'); }); }); describe('handles channel name encoding', () => { it('should properly encode special characters', () => { const request = new FetchMessagesRequest({ keySet, channels: ['channel#test', 'channel/test', 'channel test', 'channel@test', 'channel+test'], getFileUrl, }); const path = (request as any).path; assert(path.includes('channel%23test')); // # encoded assert(path.includes('channel%2Ftest')); // / encoded assert(path.includes('channel%20test')); // space encoded assert(path.includes('channel%40test')); // @ encoded assert(path.includes('channel%2Btest')); // + encoded }); it('should handle unicode characters', () => { const request = new FetchMessagesRequest({ keySet, channels: ['café', '测试'], getFileUrl, }); const path = (request as any).path; // Unicode characters should be properly encoded assert(path.includes('caf%C3%A9')); assert(path.includes('%E6%B5%8B%E8%AF%95')); }); }); describe('enforces count limits correctly', () => { it('should clamp count to 100 for single channel', () => { const request = new FetchMessagesRequest({ keySet, channels: ['channel1'], count: 150, getFileUrl, }); const parameters = (request as any).parameters; assert.equal(parameters.count, 100); }); it('should clamp count to 25 for multiple channels', () => { const request = new FetchMessagesRequest({ keySet, channels: ['channel1', 'channel2'], count: 50, getFileUrl, }); const parameters = (request as any).parameters; assert.equal(parameters.count, 25); }); it('should clamp count to 25 when includeMessageActions is true', () => { const request = new FetchMessagesRequest({ keySet, channels: ['channel1'], count: 50, includeMessageActions: true, getFileUrl, }); const parameters = (request as any).parameters; assert.equal(parameters.count, 25); }); }); describe('handles backward compatibility for includeUuid', () => { it('should map includeUuid to includeUUID when truthy', () => { const request = new FetchMessagesRequest({ keySet, channels: ['channel1'], includeUuid: true, getFileUrl, }); const parameters = (request as any).parameters; assert.equal(parameters.includeUUID, true); assert.equal(parameters.includeUuid, true); }); it('should use default includeUUID when includeUuid is falsy', () => { const request = new FetchMessagesRequest({ keySet, channels: ['channel1'], includeUuid: false, getFileUrl, }); const parameters = (request as any).parameters; assert.equal(parameters.includeUUID, true); // Should default to true assert.equal(parameters.includeUuid, false); // Original value preserved }); it('should prefer includeUUID over includeUuid when both provided', () => { const request = new FetchMessagesRequest({ keySet, channels: ['channel1'], includeUuid: false, includeUUID: true, getFileUrl, }); const parameters = (request as any).parameters; assert.equal(parameters.includeUUID, true); }); }); describe('operation type', () => { it('should return correct operation type', () => { const request = new FetchMessagesRequest({ keySet, channels: ['channel1'], getFileUrl, }); assert.equal(request.operation(), RequestOperation.PNFetchMessagesOperation); }); }); });