From ac972bdcd542b0c3a09cab2d654948b965ef8ab7 Mon Sep 17 00:00:00 2001 From: borislav ivanov Date: Wed, 8 Jan 2025 14:03:24 +0200 Subject: [PATCH] feat(test-utils): improve cluster testing - Add support for configuring replica authentication with 'masterauth' - Allow default client configuration during test cluster creation This improves the testing framework's flexibility by automatically configuring replica authentication when '--requirepass' is used and enabling custom client configurations across cluster nodes. --- package-lock.json | 1 + .../lib/entraid-credentials-provider.spec.ts | 18 ++++++-- packages/entraid/lib/test-utils.ts | 46 +++++++++++++++++++ packages/entraid/package.json | 3 +- packages/test-utils/lib/dockers.ts | 36 +++++++++++---- packages/test-utils/lib/index.ts | 3 +- 6 files changed, 93 insertions(+), 14 deletions(-) create mode 100644 packages/entraid/lib/test-utils.ts diff --git a/package-lock.json b/package-lock.json index 1ca16879d6..8fdd049a5b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8182,6 +8182,7 @@ "@azure/msal-node": "^2.16.1" }, "devDependencies": { + "@redis/test-utils": "*", "@types/express": "^4.17.21", "@types/express-session": "^1.18.0", "@types/node": "^22.9.0", diff --git a/packages/entraid/lib/entraid-credentials-provider.spec.ts b/packages/entraid/lib/entraid-credentials-provider.spec.ts index f94cbcc9ad..8967bd1883 100644 --- a/packages/entraid/lib/entraid-credentials-provider.spec.ts +++ b/packages/entraid/lib/entraid-credentials-provider.spec.ts @@ -1,8 +1,20 @@ -import { IdentityProvider, TokenManager, TokenResponse, TokenManagerConfig, BasicAuth } from '@redis/authx'; +import { AuthenticationResult } from '@azure/msal-node'; +import { IdentityProvider, TokenManager, TokenResponse, BasicAuth } from '@redis/authx'; import { EntraidCredentialsProvider } from './entraid-credentials-provider'; -import { strict as assert } from 'node:assert'; import { setTimeout } from 'timers/promises'; -import { AuthenticationResult } from '@azure/msal-common/node'; +import { strict as assert } from 'node:assert'; +import { GLOBAL, testUtils } from './test-utils' + + +describe('EntraID authentication in cluster mode', () => { + + testUtils.testWithCluster('sendCommand', async cluster => { + assert.equal( + await cluster.sendCommand(undefined, true, ['PING']), + 'PONG' + ); + }, GLOBAL.CLUSTERS.PASSWORD_WITH_REPLICAS); +}) describe('EntraID CredentialsProvider Subscription Behavior', () => { diff --git a/packages/entraid/lib/test-utils.ts b/packages/entraid/lib/test-utils.ts new file mode 100644 index 0000000000..04799f66a3 --- /dev/null +++ b/packages/entraid/lib/test-utils.ts @@ -0,0 +1,46 @@ +import { AuthenticationResult } from '@azure/msal-node'; +import { IdentityProvider, StreamingCredentialsProvider, TokenManager, TokenResponse } from '@redis/authx'; +import TestUtils from '@redis/test-utils'; +import { EntraidCredentialsProvider } from './entraid-credentials-provider'; + +export const testUtils = new TestUtils({ + dockerImageName: 'redis/redis-stack', + dockerImageVersionArgument: 'redis-version', + defaultDockerVersion: '7.4.0-v1' +}); + +const DEBUG_MODE_ARGS = testUtils.isVersionGreaterThan([7]) ? + ['--enable-debug-command', 'yes'] : + []; + +const idp: IdentityProvider = { + requestToken(): Promise> { + // @ts-ignore + return Promise.resolve({ + ttlMs: 100000, + token: { + accessToken: 'password' + } + }) + } +} + +const tokenManager = new TokenManager(idp, { expirationRefreshRatio: 0.8 }); +const entraIdCredentialsProvider: StreamingCredentialsProvider = new EntraidCredentialsProvider(tokenManager, idp) + +const PASSWORD_WITH_REPLICAS = { + serverArguments: ['--requirepass', 'password', ...DEBUG_MODE_ARGS], + numberOfMasters: 2, + numberOfReplicas: 1, + clusterConfiguration: { + defaults: { + credentialsProvider: entraIdCredentialsProvider + } + } +} + +export const GLOBAL = { + CLUSTERS: { + PASSWORD_WITH_REPLICAS + } +} diff --git a/packages/entraid/package.json b/packages/entraid/package.json index 867772b24c..f57ad09f46 100644 --- a/packages/entraid/package.json +++ b/packages/entraid/package.json @@ -27,7 +27,8 @@ "@types/node": "^22.9.0", "dotenv": "^16.3.1", "express": "^4.21.1", - "express-session": "^1.18.1" + "express-session": "^1.18.1", + "@redis/test-utils": "*" }, "engines": { "node": ">= 18" diff --git a/packages/test-utils/lib/dockers.ts b/packages/test-utils/lib/dockers.ts index a1cb63eb7b..bfb6660375 100644 --- a/packages/test-utils/lib/dockers.ts +++ b/packages/test-utils/lib/dockers.ts @@ -1,3 +1,4 @@ +import { RedisClusterClientOptions } from '@redis/client/dist/lib/cluster'; import { createConnection } from 'node:net'; import { once } from 'node:events'; import { createClient } from '@redis/client/index'; @@ -102,7 +103,8 @@ async function spawnRedisClusterNodeDockers( dockersConfig: RedisClusterDockersConfig, serverArguments: Array, fromSlot: number, - toSlot: number + toSlot: number, + clientConfig?: Partial ) { const range: Array = []; for (let i = fromSlot; i < toSlot; i++) { @@ -111,7 +113,8 @@ async function spawnRedisClusterNodeDockers( const master = await spawnRedisClusterNodeDocker( dockersConfig, - serverArguments + serverArguments, + clientConfig ); await master.client.clusterAddSlots(range); @@ -127,7 +130,13 @@ async function spawnRedisClusterNodeDockers( 'yes', '--cluster-node-timeout', '5000' - ]).then(async replica => { + ], clientConfig).then(async replica => { + + const requirePassIndex = serverArguments.findIndex((x)=>x==='--requirepass'); + if(requirePassIndex!==-1) { + const password = serverArguments[requirePassIndex+1]; + await replica.client.configSet({'masterauth': password}) + } await replica.client.clusterMeet('127.0.0.1', master.docker.port); while ((await replica.client.clusterSlots()).length === 0) { @@ -151,7 +160,8 @@ async function spawnRedisClusterNodeDockers( async function spawnRedisClusterNodeDocker( dockersConfig: RedisClusterDockersConfig, - serverArguments: Array + serverArguments: Array, + clientConfig?: Partial ) { const docker = await spawnRedisServerDocker(dockersConfig, [ ...serverArguments, @@ -163,7 +173,8 @@ async function spawnRedisClusterNodeDocker( client = createClient({ socket: { port: docker.port - } + }, + ...clientConfig }); await client.connect(); @@ -178,7 +189,8 @@ const SLOTS = 16384; async function spawnRedisClusterDockers( dockersConfig: RedisClusterDockersConfig, - serverArguments: Array + serverArguments: Array, + clientConfig?: Partial ): Promise> { const numberOfMasters = dockersConfig.numberOfMasters ?? 2, slotsPerNode = Math.floor(SLOTS / numberOfMasters), @@ -191,7 +203,8 @@ async function spawnRedisClusterDockers( dockersConfig, serverArguments, fromSlot, - toSlot + toSlot, + clientConfig ) ); } @@ -234,13 +247,18 @@ function totalNodes(slots: any) { const RUNNING_CLUSTERS = new Map, ReturnType>(); -export function spawnRedisCluster(dockersConfig: RedisClusterDockersConfig, serverArguments: Array): Promise> { +export function spawnRedisCluster( + dockersConfig: RedisClusterDockersConfig, + serverArguments: Array, + clientConfig?: Partial): Promise> { + const runningCluster = RUNNING_CLUSTERS.get(serverArguments); if (runningCluster) { return runningCluster; } - const dockersPromise = spawnRedisClusterDockers(dockersConfig, serverArguments); + const dockersPromise = spawnRedisClusterDockers(dockersConfig, serverArguments,clientConfig); + RUNNING_CLUSTERS.set(serverArguments, dockersPromise); return dockersPromise; } diff --git a/packages/test-utils/lib/index.ts b/packages/test-utils/lib/index.ts index 87ba34db7e..9dee350e31 100644 --- a/packages/test-utils/lib/index.ts +++ b/packages/test-utils/lib/index.ts @@ -290,7 +290,8 @@ export default class TestUtils { ...dockerImage, numberOfMasters: options.numberOfMasters, numberOfReplicas: options.numberOfReplicas - }, options.serverArguments); + }, options.serverArguments, + options.clusterConfiguration?.defaults); return dockersPromise; }); }