diff --git a/cli/src/__tests__/utils/getBenchClientId.test.ts b/cli/src/__tests__/utils/getBenchClientId.test.ts new file mode 100644 index 000000000..f9cee9045 --- /dev/null +++ b/cli/src/__tests__/utils/getBenchClientId.test.ts @@ -0,0 +1,38 @@ +import { expect, describe, it } from '@jest/globals' +import getBenchClientId from '../../utils/getBenchClientId' + +describe('getBenchClientId', () => { + describe('single connection (count = 1)', () => { + it('should return original clientId when no placeholder', () => { + expect(getBenchClientId('mqtt_client', 1, 1)).toBe('mqtt_client') + }) + + it('should replace %i when has placeholder', () => { + expect(getBenchClientId('mqtt_client_%i', 1, 1)).toBe('mqtt_client_1') + }) + }) + + describe('multiple connections (count > 1)', () => { + it('should append index when no placeholder', () => { + expect(getBenchClientId('mqtt_client', 1, 5)).toBe('mqtt_client_1') + }) + + it('should replace %i when has placeholder', () => { + expect(getBenchClientId('mqtt_client_%i', 2, 5)).toBe('mqtt_client_2') + }) + + it('should handle multiple %i placeholders', () => { + expect(getBenchClientId('mqtt_%i_client_%i', 3, 5)).toBe('mqtt_3_client_3') + }) + }) + + describe('edge cases', () => { + it('should handle empty clientId', () => { + expect(getBenchClientId('', 1, 5)).toBe('_1') + }) + + it('should handle clientId with only %i', () => { + expect(getBenchClientId('%i', 1, 5)).toBe('1') + }) + }) +}) diff --git a/cli/src/lib/conn.ts b/cli/src/lib/conn.ts index 7bc3f1f10..fd2a9777d 100644 --- a/cli/src/lib/conn.ts +++ b/cli/src/lib/conn.ts @@ -5,6 +5,7 @@ import delay from '../utils/delay' import { handleSaveOptions, handleLoadOptions } from '../utils/options' import * as Debug from 'debug' import { triggerExitInfo } from '../utils/exitInfo' +import getBenchClientId from '../utils/getBenchClientId' const conn = (options: ConnectOptions) => { const { debug, saveOptions, loadOptions } = options @@ -83,7 +84,7 @@ const benchConn = async (options: BenchConnectOptions) => { ;((i: number, connOpts: mqtt.IClientOptions) => { const opts = { ...connOpts } - opts.clientId = clientId.includes('%i') ? clientId.replaceAll('%i', i.toString()) : `${clientId}_${i}` + opts.clientId = getBenchClientId(clientId, i, count) const client = mqtt.connect(opts) diff --git a/cli/src/lib/pub.ts b/cli/src/lib/pub.ts index 85bc44e41..ae99f8423 100644 --- a/cli/src/lib/pub.ts +++ b/cli/src/lib/pub.ts @@ -16,6 +16,7 @@ import { serializeProtobufToBuffer } from '../utils/protobuf' import { serializeAvroToBuffer } from '../utils/avro' import { loadSimulator } from '../utils/simulate' import { triggerExitInfo } from '../utils/exitInfo' +import getBenchClientId from '../utils/getBenchClientId' /** * Processes the outgoing message through two potential stages: @@ -390,7 +391,7 @@ const multiPub = async (commandType: CommandType, options: BenchPublishOptions | ;((i: number, connOpts: mqtt.IClientOptions) => { const opts = { ...connOpts } - opts.clientId = clientId.includes('%i') ? clientId.replaceAll('%i', i.toString()) : `${clientId}_${i}` + opts.clientId = getBenchClientId(clientId, i, count) let topicName = topic.replaceAll('%i', i.toString()).replaceAll('%c', clientId) username && (topicName = topicName.replaceAll('%u', username)) diff --git a/cli/src/lib/sub.ts b/cli/src/lib/sub.ts index ee968c731..8f47e493d 100644 --- a/cli/src/lib/sub.ts +++ b/cli/src/lib/sub.ts @@ -9,6 +9,7 @@ import { deserializeBufferToProtobuf } from '../utils/protobuf' import isSupportedBinaryFormatForMQTT from '../utils/binaryFormats' import * as Debug from 'debug' import { deserializeBufferToAvro } from '../utils/avro' +import getBenchClientId from '../utils/getBenchClientId' /** * @@ -266,7 +267,7 @@ const benchSub = async (options: BenchSubscribeOptions) => { ;((i: number, connOpts: mqtt.IClientOptions) => { const opts = { ...connOpts } - opts.clientId = clientId.includes('%i') ? clientId.replaceAll('%i', i.toString()) : `${clientId}_${i}` + opts.clientId = getBenchClientId(clientId, i, count) const client = mqtt.connect(opts) diff --git a/cli/src/utils/getBenchClientId.ts b/cli/src/utils/getBenchClientId.ts new file mode 100644 index 000000000..2a23dcb48 --- /dev/null +++ b/cli/src/utils/getBenchClientId.ts @@ -0,0 +1,18 @@ +/** + * Generate a unique client ID for benchmarking purposes + * @param clientId - Base client ID string that may contain '%i' placeholder + * @param index - Index number to replace placeholder or append to client ID + * @param count - Total count of clients being generated + * @returns Modified client ID string with index incorporated + * + * If clientId contains '%i', replaces all instances with index. + * If count > 1 and no '%i' placeholder exists, appends '_index' to clientId. + * Otherwise returns clientId unchanged. + */ +const getBenchClientId = (clientId: string, index: number, count: number) => { + const hasPlaceholder = clientId.includes('%i') + const baseClientId = hasPlaceholder ? clientId.replaceAll('%i', index.toString()) : clientId + return count > 1 && !hasPlaceholder ? `${baseClientId}_${index}` : baseClientId +} + +export default getBenchClientId