Skip to content

Commit

Permalink
feat(test-utils): improve cluster testing
Browse files Browse the repository at this point in the history
- 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.
  • Loading branch information
bobymicroby committed Jan 8, 2025
1 parent 0ce5e7a commit ac972bd
Show file tree
Hide file tree
Showing 6 changed files with 93 additions and 14 deletions.
1 change: 1 addition & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

18 changes: 15 additions & 3 deletions packages/entraid/lib/entraid-credentials-provider.spec.ts
Original file line number Diff line number Diff line change
@@ -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', () => {

Expand Down
46 changes: 46 additions & 0 deletions packages/entraid/lib/test-utils.ts
Original file line number Diff line number Diff line change
@@ -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<AuthenticationResult> = {
requestToken(): Promise<TokenResponse<AuthenticationResult>> {
// @ts-ignore
return Promise.resolve({
ttlMs: 100000,
token: {
accessToken: 'password'
}
})
}
}

const tokenManager = new TokenManager<AuthenticationResult>(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
}
}
3 changes: 2 additions & 1 deletion packages/entraid/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
36 changes: 27 additions & 9 deletions packages/test-utils/lib/dockers.ts
Original file line number Diff line number Diff line change
@@ -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';
Expand Down Expand Up @@ -102,7 +103,8 @@ async function spawnRedisClusterNodeDockers(
dockersConfig: RedisClusterDockersConfig,
serverArguments: Array<string>,
fromSlot: number,
toSlot: number
toSlot: number,
clientConfig?: Partial<RedisClusterClientOptions>
) {
const range: Array<number> = [];
for (let i = fromSlot; i < toSlot; i++) {
Expand All @@ -111,7 +113,8 @@ async function spawnRedisClusterNodeDockers(

const master = await spawnRedisClusterNodeDocker(
dockersConfig,
serverArguments
serverArguments,
clientConfig
);

await master.client.clusterAddSlots(range);
Expand All @@ -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) {
Expand All @@ -151,7 +160,8 @@ async function spawnRedisClusterNodeDockers(

async function spawnRedisClusterNodeDocker(
dockersConfig: RedisClusterDockersConfig,
serverArguments: Array<string>
serverArguments: Array<string>,
clientConfig?: Partial<RedisClusterClientOptions>
) {
const docker = await spawnRedisServerDocker(dockersConfig, [
...serverArguments,
Expand All @@ -163,7 +173,8 @@ async function spawnRedisClusterNodeDocker(
client = createClient({
socket: {
port: docker.port
}
},
...clientConfig
});

await client.connect();
Expand All @@ -178,7 +189,8 @@ const SLOTS = 16384;

async function spawnRedisClusterDockers(
dockersConfig: RedisClusterDockersConfig,
serverArguments: Array<string>
serverArguments: Array<string>,
clientConfig?: Partial<RedisClusterClientOptions>
): Promise<Array<RedisServerDocker>> {
const numberOfMasters = dockersConfig.numberOfMasters ?? 2,
slotsPerNode = Math.floor(SLOTS / numberOfMasters),
Expand All @@ -191,7 +203,8 @@ async function spawnRedisClusterDockers(
dockersConfig,
serverArguments,
fromSlot,
toSlot
toSlot,
clientConfig
)
);
}
Expand Down Expand Up @@ -234,13 +247,18 @@ function totalNodes(slots: any) {

const RUNNING_CLUSTERS = new Map<Array<string>, ReturnType<typeof spawnRedisClusterDockers>>();

export function spawnRedisCluster(dockersConfig: RedisClusterDockersConfig, serverArguments: Array<string>): Promise<Array<RedisServerDocker>> {
export function spawnRedisCluster(
dockersConfig: RedisClusterDockersConfig,
serverArguments: Array<string>,
clientConfig?: Partial<RedisClusterClientOptions>): Promise<Array<RedisServerDocker>> {

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;
}
Expand Down
3 changes: 2 additions & 1 deletion packages/test-utils/lib/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -290,7 +290,8 @@ export default class TestUtils {
...dockerImage,
numberOfMasters: options.numberOfMasters,
numberOfReplicas: options.numberOfReplicas
}, options.serverArguments);
}, options.serverArguments,
options.clusterConfiguration?.defaults);
return dockersPromise;
});
}
Expand Down

0 comments on commit ac972bd

Please sign in to comment.