From a3bcda1dfb9d74e1a298ba3457682c7142748540 Mon Sep 17 00:00:00 2001 From: borislav ivanov Date: Tue, 16 Dec 2025 10:23:44 +0200 Subject: [PATCH] feat(test-utils): add testAllAuto method for automatic cluster test generation Created a new `testAllAuto()` helper method in the test-utils package that automatically runs tests against both single-node and cluster instances with auto-generated cluster configuration from client configuration. Previously, tests only ran against single-node clients using `testWithClient()`. To test both scenarios, developers had to either manually call `testWithCluster()` separately with hand-crafted cluster config, or use `testAll()` with both client and cluster configurations defined. The new `testAllAuto()` method converts client configuration to cluster configuration by separating cluster-level properties (`modules`, `functions`, `scripts`) from client-level properties (`password`, `socket`, etc.), placing them at `clusterConfiguration` level and `clusterConfiguration.defaults` respectively. Example usage: Before: ``` testUtils.testWithClient('client.ft.aggregate', async client => {...}, GLOBAL.SERVERS.OPEN) ``` After: ``` testUtils.testAllAuto('client.ft.aggregate', async client => {...}, GLOBAL.SERVERS.OPEN) ``` Changes: - Added `testAllAuto()` method in packages/test-utils/lib/index.ts - Updated AGGREGATE.spec.ts to use the new method as example --- .../search/lib/commands/AGGREGATE.spec.ts | 16 +++--- packages/test-utils/lib/index.ts | 55 +++++++++++++++++++ 2 files changed, 62 insertions(+), 9 deletions(-) diff --git a/packages/search/lib/commands/AGGREGATE.spec.ts b/packages/search/lib/commands/AGGREGATE.spec.ts index 420911c5600..3fc6b77722f 100644 --- a/packages/search/lib/commands/AGGREGATE.spec.ts +++ b/packages/search/lib/commands/AGGREGATE.spec.ts @@ -4,7 +4,7 @@ import AGGREGATE from './AGGREGATE'; import { parseArgs } from '@redis/client/lib/commands/generic-transformers'; import { DEFAULT_DIALECT } from '../dialect/default'; -describe('AGGREGATE', () => { +describe('AGGREGATE', () => { describe('transformArguments', () => { it('without options', () => { assert.deepEqual( @@ -27,7 +27,7 @@ describe('AGGREGATE', () => { parseArgs(AGGREGATE, 'index', '*', { ADDSCORES: true }), ['FT.AGGREGATE', 'index', '*', 'ADDSCORES', 'DIALECT', DEFAULT_DIALECT] ); - }); + }); describe('with LOAD', () => { describe('single', () => { @@ -477,13 +477,11 @@ describe('AGGREGATE', () => { }); testUtils.testWithClient('client.ft.aggregate', async client => { - await Promise.all([ - client.ft.create('index', { - field: 'NUMERIC' - }), - client.hSet('1', 'field', '1'), - client.hSet('2', 'field', '2') - ]); + await client.ft.create('index', { + field: 'NUMERIC' + }); + await client.hSet('1', 'field', '1'); + await client.hSet('2', 'field', '2'); assert.deepEqual( await client.ft.aggregate('index', '*', { diff --git a/packages/test-utils/lib/index.ts b/packages/test-utils/lib/index.ts index 1a9d1c9845a..7cb16bfa49c 100644 --- a/packages/test-utils/lib/index.ts +++ b/packages/test-utils/lib/index.ts @@ -590,6 +590,61 @@ export default class TestUtils { this.testWithCluster(`cluster.${title}`, fn, options.cluster); } + /** + * Tests with both a regular client and a cluster client, automatically generating the cluster + * configuration from the client configuration. + * + * Modules, functions, and scripts are placed at the cluster level, while other client options + * (password, socket, etc.) are placed under cluster defaults. + * + * @param title - The test title + * @param fn - The test function + * @param clientOptions - Client test options (cluster config will be auto-generated) + */ + testAllAuto< + M extends RedisModules = {}, + F extends RedisFunctions = {}, + S extends RedisScripts = {}, + RESP extends RespVersions = 2, + TYPE_MAPPING extends TypeMapping = {} + // POLICIES extends CommandPolicies = {} + >( + title: string, + fn: (client: RedisClientType | RedisClusterType) => unknown, + clientOptions: ClientTestOptions + ) { + this.testWithClient(`client.${title}`, fn, clientOptions); + + // Auto-generate cluster configuration from client options + const clusterOptions: ClusterTestOptions = { + serverArguments: clientOptions.serverArguments, + minimumDockerVersion: clientOptions.minimumDockerVersion, + skipTest: clientOptions.skipTest, + clusterConfiguration: {} + }; + + if (clientOptions.clientOptions) { + const { modules, functions, scripts, ...clientDefaults } = clientOptions.clientOptions as any; + + if (modules) { + clusterOptions.clusterConfiguration!.modules = modules; + } + if (functions) { + clusterOptions.clusterConfiguration!.functions = functions; + } + if (scripts) { + clusterOptions.clusterConfiguration!.scripts = scripts; + } + + // Other client options go under defaults + if (Object.keys(clientDefaults).length > 0) { + clusterOptions.clusterConfiguration!.defaults = clientDefaults; + } + } + + this.testWithCluster(`cluster.${title}`, fn, clusterOptions); + } + spawnRedisServer< M extends RedisModules = {},