You've already forked node-redis
mirror of
https://github.com/redis/node-redis.git
synced 2025-08-07 13:22:56 +03:00
wip
This commit is contained in:
@@ -161,9 +161,15 @@ Some command arguments/replies have changed to align more closely to data types
|
|||||||
- `HELLO`: `protover` moved from the options object to it's own argument, `auth` -> `AUTH`, `clientName` -> `SETNAME`
|
- `HELLO`: `protover` moved from the options object to it's own argument, `auth` -> `AUTH`, `clientName` -> `SETNAME`
|
||||||
- `MODULE LIST`: `version` -> `ver` [^map-keys]
|
- `MODULE LIST`: `version` -> `ver` [^map-keys]
|
||||||
- `MEMORY STATS`: [^map-keys]
|
- `MEMORY STATS`: [^map-keys]
|
||||||
|
- `CLIENT TRACKINGINFO`: `flags` in RESP2 - `Set<string>` -> `Array<string>` (to match RESP3 default type mapping)
|
||||||
|
- `CLUSETER SETSLOT`: `ClusterSlotStates` -> `CLUSTER_SLOT_STATES` [^enum-to-constants]
|
||||||
|
- `FUNCTION RESTORE`: the second argument is `{ mode: string; }` instead of `string` [^future-proofing]
|
||||||
|
- `CLUSTER RESET`: the second argument is `{ mode: string; }` instead of `string` [^future-proofing]
|
||||||
|
|
||||||
[^enum-to-constants]: TODO
|
[^enum-to-constants]: TODO
|
||||||
|
|
||||||
[^boolean-to-number]: TODO
|
[^boolean-to-number]: TODO
|
||||||
|
|
||||||
[^map-keys]: [TODO](https://github.com/redis/node-redis/discussions/2506)
|
[^map-keys]: [TODO](https://github.com/redis/node-redis/discussions/2506)
|
||||||
|
|
||||||
|
[^future-proofing]: TODO
|
@@ -1,4 +1,4 @@
|
|||||||
export { RedisModules, RedisFunctions, RedisScripts, RespVersions } from './lib/RESP/types';
|
export { RedisModules, RedisFunctions, RedisScripts, RespVersions, TypeMapping, CommandPolicies } from './lib/RESP/types';
|
||||||
export { RESP_TYPES } from './lib/RESP/decoder';
|
export { RESP_TYPES } from './lib/RESP/decoder';
|
||||||
export { VerbatimString } from './lib/RESP/verbatim-string';
|
export { VerbatimString } from './lib/RESP/verbatim-string';
|
||||||
export { defineScript } from './lib/lua-script';
|
export { defineScript } from './lib/lua-script';
|
||||||
|
@@ -6,11 +6,14 @@ import { ChannelListeners, PubSub, PubSubCommand, PubSubListener, PubSubType, Pu
|
|||||||
import { AbortError, ErrorReply } from '../errors';
|
import { AbortError, ErrorReply } from '../errors';
|
||||||
import { EventEmitter } from 'stream';
|
import { EventEmitter } from 'stream';
|
||||||
|
|
||||||
export interface CommandOptions {
|
export interface CommandOptions<T = TypeMapping> {
|
||||||
chainId?: symbol;
|
chainId?: symbol;
|
||||||
asap?: boolean;
|
asap?: boolean;
|
||||||
abortSignal?: AbortSignal;
|
abortSignal?: AbortSignal;
|
||||||
typeMapping?: TypeMapping;
|
/**
|
||||||
|
* Maps bettween RESP and JavaScript types
|
||||||
|
*/
|
||||||
|
typeMapping?: T;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface CommandWaitingToBeSent extends CommandWaitingForReply {
|
export interface CommandWaitingToBeSent extends CommandWaitingForReply {
|
||||||
|
@@ -122,10 +122,10 @@ describe('Client', () => {
|
|||||||
client.connect()
|
client.connect()
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const promise = once(client, 'end');
|
await Promise.all([
|
||||||
console.log('listen to end', client.listeners('end'));
|
once(client, 'end'),
|
||||||
client.close();
|
client.close()
|
||||||
await promise;
|
]);
|
||||||
}, {
|
}, {
|
||||||
...GLOBAL.SERVERS.OPEN,
|
...GLOBAL.SERVERS.OPEN,
|
||||||
disableClientSetup: true
|
disableClientSetup: true
|
||||||
@@ -335,9 +335,7 @@ describe('Client', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
testUtils.testWithClient('duplicate should reuse command options', async client => {
|
testUtils.testWithClient('duplicate should reuse command options', async client => {
|
||||||
const duplicate = client.withTypeMapping({
|
const duplicate = client.duplicate();
|
||||||
[RESP_TYPES.SIMPLE_STRING]: Buffer
|
|
||||||
}).duplicate();
|
|
||||||
|
|
||||||
await duplicate.connect();
|
await duplicate.connect();
|
||||||
|
|
||||||
@@ -351,6 +349,13 @@ describe('Client', () => {
|
|||||||
}
|
}
|
||||||
}, {
|
}, {
|
||||||
...GLOBAL.SERVERS.OPEN,
|
...GLOBAL.SERVERS.OPEN,
|
||||||
|
clientOptions: {
|
||||||
|
commandOptions: {
|
||||||
|
typeMapping: {
|
||||||
|
[RESP_TYPES.SIMPLE_STRING]: Buffer
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
disableClientSetup: true,
|
disableClientSetup: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@@ -11,18 +11,27 @@ import { Command, CommandArguments, CommandSignature, TypeMapping, CommanderConf
|
|||||||
import RedisClientMultiCommand, { RedisClientMultiCommandType } from './multi-command';
|
import RedisClientMultiCommand, { RedisClientMultiCommandType } from './multi-command';
|
||||||
import { RedisMultiQueuedCommand } from '../multi-command';
|
import { RedisMultiQueuedCommand } from '../multi-command';
|
||||||
import HELLO, { HelloOptions } from '../commands/HELLO';
|
import HELLO, { HelloOptions } from '../commands/HELLO';
|
||||||
import { ReplyWithTypeMapping, CommandReply } from '../RESP/types';
|
|
||||||
import { ScanOptions, ScanCommonOptions } from '../commands/SCAN';
|
import { ScanOptions, ScanCommonOptions } from '../commands/SCAN';
|
||||||
import { RedisLegacyClient, RedisLegacyClientType } from './legacy-mode';
|
import { RedisLegacyClient, RedisLegacyClientType } from './legacy-mode';
|
||||||
// import { RedisClientPool } from './pool';
|
// import { RedisClientPool } from './pool';
|
||||||
|
|
||||||
|
interface ClientCommander<
|
||||||
|
M extends RedisModules,
|
||||||
|
F extends RedisFunctions,
|
||||||
|
S extends RedisScripts,
|
||||||
|
RESP extends RespVersions,
|
||||||
|
TYPE_MAPPING extends TypeMapping
|
||||||
|
> extends CommanderConfig<M, F, S, RESP>{
|
||||||
|
commandOptions?: CommandOptions<TYPE_MAPPING>;
|
||||||
|
}
|
||||||
|
|
||||||
export interface RedisClientOptions<
|
export interface RedisClientOptions<
|
||||||
M extends RedisModules = RedisModules,
|
M extends RedisModules = RedisModules,
|
||||||
F extends RedisFunctions = RedisFunctions,
|
F extends RedisFunctions = RedisFunctions,
|
||||||
S extends RedisScripts = RedisScripts,
|
S extends RedisScripts = RedisScripts,
|
||||||
RESP extends RespVersions = RespVersions,
|
RESP extends RespVersions = RespVersions,
|
||||||
TYPE_MAPPING extends TypeMapping = TypeMapping
|
TYPE_MAPPING extends TypeMapping = TypeMapping
|
||||||
> extends CommanderConfig<M, F, S, RESP>, TypeMappingOption<TYPE_MAPPING> {
|
> extends ClientCommander<M, F, S, RESP, TYPE_MAPPING> {
|
||||||
/**
|
/**
|
||||||
* `redis[s]://[[username][:password]@][host][:port][/db-number]`
|
* `redis[s]://[[username][:password]@][host][:port][/db-number]`
|
||||||
* See [`redis`](https://www.iana.org/assignments/uri-schemes/prov/redis) and [`rediss`](https://www.iana.org/assignments/uri-schemes/prov/rediss) IANA registration for more details
|
* See [`redis`](https://www.iana.org/assignments/uri-schemes/prov/redis) and [`rediss`](https://www.iana.org/assignments/uri-schemes/prov/rediss) IANA registration for more details
|
||||||
@@ -68,13 +77,6 @@ export interface RedisClientOptions<
|
|||||||
pingInterval?: number;
|
pingInterval?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface TypeMappingOption<TYPE_MAPPING extends TypeMapping> {
|
|
||||||
/**
|
|
||||||
* Maps bettwen RESP types to JavaScript types
|
|
||||||
*/
|
|
||||||
typeMapping?: TYPE_MAPPING;
|
|
||||||
}
|
|
||||||
|
|
||||||
type WithCommands<
|
type WithCommands<
|
||||||
RESP extends RespVersions,
|
RESP extends RespVersions,
|
||||||
TYPE_MAPPING extends TypeMapping
|
TYPE_MAPPING extends TypeMapping
|
||||||
@@ -134,7 +136,7 @@ export type RedisClientType<
|
|||||||
RedisClientExtensions<M, F, S, RESP, TYPE_MAPPING>
|
RedisClientExtensions<M, F, S, RESP, TYPE_MAPPING>
|
||||||
);
|
);
|
||||||
|
|
||||||
type ProxyClient = RedisClient<any, any, any, any, any> & { commandOptions?: CommandOptions };
|
type ProxyClient = RedisClient<any, any, any, any, any>;
|
||||||
|
|
||||||
type NamespaceProxyClient = { self: ProxyClient };
|
type NamespaceProxyClient = { self: ProxyClient };
|
||||||
|
|
||||||
@@ -153,7 +155,7 @@ export default class RedisClient<
|
|||||||
const transformReply = getTransformReply(command, resp);
|
const transformReply = getTransformReply(command, resp);
|
||||||
return async function (this: ProxyClient, ...args: Array<unknown>) {
|
return async function (this: ProxyClient, ...args: Array<unknown>) {
|
||||||
const redisArgs = command.transformArguments(...args),
|
const redisArgs = command.transformArguments(...args),
|
||||||
reply = await this.sendCommand(redisArgs, this.commandOptions);
|
reply = await this.sendCommand(redisArgs, this._commandOptions);
|
||||||
return transformReply ?
|
return transformReply ?
|
||||||
transformReply(reply, redisArgs.preserve) :
|
transformReply(reply, redisArgs.preserve) :
|
||||||
reply;
|
reply;
|
||||||
@@ -164,7 +166,7 @@ export default class RedisClient<
|
|||||||
const transformReply = getTransformReply(command, resp);
|
const transformReply = getTransformReply(command, resp);
|
||||||
return async function (this: NamespaceProxyClient, ...args: Array<unknown>) {
|
return async function (this: NamespaceProxyClient, ...args: Array<unknown>) {
|
||||||
const redisArgs = command.transformArguments(...args),
|
const redisArgs = command.transformArguments(...args),
|
||||||
reply = await this.self.sendCommand(redisArgs, this.self.commandOptions);
|
reply = await this.self.sendCommand(redisArgs, this.self._commandOptions);
|
||||||
return transformReply ?
|
return transformReply ?
|
||||||
transformReply(reply, redisArgs.preserve) :
|
transformReply(reply, redisArgs.preserve) :
|
||||||
reply;
|
reply;
|
||||||
@@ -178,7 +180,7 @@ export default class RedisClient<
|
|||||||
const fnArgs = fn.transformArguments(...args),
|
const fnArgs = fn.transformArguments(...args),
|
||||||
reply = await this.self.sendCommand(
|
reply = await this.self.sendCommand(
|
||||||
prefix.concat(fnArgs),
|
prefix.concat(fnArgs),
|
||||||
this.self.commandOptions
|
this.self._commandOptions
|
||||||
);
|
);
|
||||||
return transformReply ?
|
return transformReply ?
|
||||||
transformReply(reply, fnArgs.preserve) :
|
transformReply(reply, fnArgs.preserve) :
|
||||||
@@ -192,12 +194,12 @@ export default class RedisClient<
|
|||||||
return async function (this: ProxyClient, ...args: Array<unknown>) {
|
return async function (this: ProxyClient, ...args: Array<unknown>) {
|
||||||
const scriptArgs = script.transformArguments(...args),
|
const scriptArgs = script.transformArguments(...args),
|
||||||
redisArgs = prefix.concat(scriptArgs),
|
redisArgs = prefix.concat(scriptArgs),
|
||||||
reply = await this.sendCommand(redisArgs, this.commandOptions).catch((err: unknown) => {
|
reply = await this.sendCommand(redisArgs, this._commandOptions).catch((err: unknown) => {
|
||||||
if (!(err as Error)?.message?.startsWith?.('NOSCRIPT')) throw err;
|
if (!(err as Error)?.message?.startsWith?.('NOSCRIPT')) throw err;
|
||||||
|
|
||||||
redisArgs[0] = 'EVAL';
|
redisArgs[0] = 'EVAL';
|
||||||
redisArgs[1] = script.SCRIPT;
|
redisArgs[1] = script.SCRIPT;
|
||||||
return this.sendCommand(redisArgs, this.commandOptions);
|
return this.sendCommand(redisArgs, this._commandOptions);
|
||||||
});
|
});
|
||||||
return transformReply ?
|
return transformReply ?
|
||||||
transformReply(reply, scriptArgs.preserve) :
|
transformReply(reply, scriptArgs.preserve) :
|
||||||
@@ -211,7 +213,7 @@ export default class RedisClient<
|
|||||||
S extends RedisScripts = {},
|
S extends RedisScripts = {},
|
||||||
RESP extends RespVersions = 2,
|
RESP extends RespVersions = 2,
|
||||||
TYPE_MAPPING extends TypeMapping = {}
|
TYPE_MAPPING extends TypeMapping = {}
|
||||||
>(config?: CommanderConfig<M, F, S, RESP> & TypeMappingOption<TYPE_MAPPING>) {
|
>(config?: ClientCommander<M, F, S, RESP, TYPE_MAPPING>) {
|
||||||
const Client = attachConfig({
|
const Client = attachConfig({
|
||||||
BaseClass: RedisClient,
|
BaseClass: RedisClient,
|
||||||
commands: COMMANDS,
|
commands: COMMANDS,
|
||||||
@@ -282,10 +284,11 @@ export default class RedisClient<
|
|||||||
|
|
||||||
self = this;
|
self = this;
|
||||||
|
|
||||||
private readonly _options?: RedisClientOptions<M, F, S, RESP>;
|
private readonly _options?: RedisClientOptions<M, F, S, RESP, TYPE_MAPPING>;
|
||||||
private readonly _socket: RedisSocket;
|
private readonly _socket: RedisSocket;
|
||||||
private readonly _queue: RedisCommandsQueue;
|
private readonly _queue: RedisCommandsQueue;
|
||||||
private _selectedDB = 0;
|
private _selectedDB = 0;
|
||||||
|
private _commandOptions?: CommandOptions<TYPE_MAPPING>;
|
||||||
|
|
||||||
get options(): RedisClientOptions<M, F, S, RESP> | undefined {
|
get options(): RedisClientOptions<M, F, S, RESP> | undefined {
|
||||||
return this._options;
|
return this._options;
|
||||||
@@ -303,14 +306,14 @@ export default class RedisClient<
|
|||||||
return this._queue.isPubSubActive;
|
return this._queue.isPubSubActive;
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(options?: RedisClientOptions<M, F, S, RESP>) {
|
constructor(options?: RedisClientOptions<M, F, S, RESP, TYPE_MAPPING>) {
|
||||||
super();
|
super();
|
||||||
this._options = this._initiateOptions(options);
|
this._options = this._initiateOptions(options);
|
||||||
this._queue = this._initiateQueue();
|
this._queue = this._initiateQueue();
|
||||||
this._socket = this._initiateSocket();
|
this._socket = this._initiateSocket();
|
||||||
}
|
}
|
||||||
|
|
||||||
private _initiateOptions(options?: RedisClientOptions<M, F, S, RESP>): RedisClientOptions<M, F, S, RESP> | undefined {
|
private _initiateOptions(options?: RedisClientOptions<M, F, S, RESP, TYPE_MAPPING>): RedisClientOptions<M, F, S, RESP, TYPE_MAPPING> | undefined {
|
||||||
if (options?.url) {
|
if (options?.url) {
|
||||||
const parsed = RedisClient.parseURL(options.url);
|
const parsed = RedisClient.parseURL(options.url);
|
||||||
if (options.socket) {
|
if (options.socket) {
|
||||||
@@ -324,10 +327,8 @@ export default class RedisClient<
|
|||||||
this._selectedDB = options.database;
|
this._selectedDB = options.database;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (options?.typeMapping) {
|
if (options?.commandOptions) {
|
||||||
(this as unknown as ProxyClient).commandOptions = {
|
this._commandOptions = options.commandOptions;
|
||||||
typeMapping: options.typeMapping
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return options;
|
return options;
|
||||||
@@ -462,15 +463,18 @@ export default class RedisClient<
|
|||||||
}, this._options.pingInterval);
|
}, this._options.pingInterval);
|
||||||
}
|
}
|
||||||
|
|
||||||
withCommandOptions<T extends CommandOptions>(options: T) {
|
withCommandOptions<
|
||||||
|
OPTIONS extends CommandOptions<TYPE_MAPPING>,
|
||||||
|
TYPE_MAPPING extends TypeMapping
|
||||||
|
>(options: OPTIONS) {
|
||||||
const proxy = Object.create(this.self);
|
const proxy = Object.create(this.self);
|
||||||
proxy.commandOptions = options;
|
proxy._commandOptions = options;
|
||||||
return proxy as RedisClientType<
|
return proxy as RedisClientType<
|
||||||
M,
|
M,
|
||||||
F,
|
F,
|
||||||
S,
|
S,
|
||||||
RESP,
|
RESP,
|
||||||
T['typeMapping'] extends TypeMapping ? T['typeMapping'] : {}
|
TYPE_MAPPING extends TypeMapping ? TYPE_MAPPING : {}
|
||||||
>;
|
>;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -482,8 +486,8 @@ export default class RedisClient<
|
|||||||
value: V
|
value: V
|
||||||
) {
|
) {
|
||||||
const proxy = Object.create(this.self);
|
const proxy = Object.create(this.self);
|
||||||
proxy.commandOptions = Object.create((this as unknown as ProxyClient).commandOptions ?? null);
|
proxy._commandOptions = Object.create(this._commandOptions ?? null);
|
||||||
proxy.commandOptions[key] = value;
|
proxy._commandOptions[key] = value;
|
||||||
return proxy as RedisClientType<
|
return proxy as RedisClientType<
|
||||||
M,
|
M,
|
||||||
F,
|
F,
|
||||||
@@ -539,17 +543,11 @@ export default class RedisClient<
|
|||||||
_RESP extends RespVersions = RESP,
|
_RESP extends RespVersions = RESP,
|
||||||
_TYPE_MAPPING extends TypeMapping = TYPE_MAPPING
|
_TYPE_MAPPING extends TypeMapping = TYPE_MAPPING
|
||||||
>(overrides?: Partial<RedisClientOptions<_M, _F, _S, _RESP, _TYPE_MAPPING>>) {
|
>(overrides?: Partial<RedisClientOptions<_M, _F, _S, _RESP, _TYPE_MAPPING>>) {
|
||||||
const client = new (Object.getPrototypeOf(this).constructor)({
|
return new (Object.getPrototypeOf(this).constructor)({
|
||||||
...this._options,
|
...this._options,
|
||||||
|
commandOptions: this._commandOptions,
|
||||||
...overrides
|
...overrides
|
||||||
}) as RedisClientType<_M, _F, _S, _RESP, _TYPE_MAPPING>;
|
}) as RedisClientType<_M, _F, _S, _RESP, _TYPE_MAPPING>;
|
||||||
|
|
||||||
const { commandOptions } = this as ProxyClient;
|
|
||||||
if (commandOptions) {
|
|
||||||
return client.withCommandOptions(commandOptions);
|
|
||||||
}
|
|
||||||
|
|
||||||
return client;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
connect() {
|
connect() {
|
||||||
@@ -732,7 +730,7 @@ export default class RedisClient<
|
|||||||
|
|
||||||
const promise = Promise.all(
|
const promise = Promise.all(
|
||||||
commands.map(({ args }) => this._queue.addCommand(args, {
|
commands.map(({ args }) => this._queue.addCommand(args, {
|
||||||
typeMapping: (this as ProxyClient).commandOptions?.typeMapping
|
typeMapping: this._commandOptions?.typeMapping
|
||||||
}))
|
}))
|
||||||
);
|
);
|
||||||
this._scheduleWrite();
|
this._scheduleWrite();
|
||||||
@@ -750,7 +748,7 @@ export default class RedisClient<
|
|||||||
return Promise.reject(new ClientClosedError());
|
return Promise.reject(new ClientClosedError());
|
||||||
}
|
}
|
||||||
|
|
||||||
const typeMapping = (this as ProxyClient).commandOptions?.typeMapping,
|
const typeMapping = this._commandOptions?.typeMapping,
|
||||||
chainId = Symbol('MULTI Chain'),
|
chainId = Symbol('MULTI Chain'),
|
||||||
promises = [
|
promises = [
|
||||||
this._queue.addCommand(['MULTI'], { chainId }),
|
this._queue.addCommand(['MULTI'], { chainId }),
|
||||||
|
@@ -4,84 +4,84 @@ import { once } from 'events';
|
|||||||
import RedisSocket, { RedisSocketOptions } from './socket';
|
import RedisSocket, { RedisSocketOptions } from './socket';
|
||||||
|
|
||||||
describe('Socket', () => {
|
describe('Socket', () => {
|
||||||
function createSocket(options: RedisSocketOptions): RedisSocket {
|
function createSocket(options: RedisSocketOptions): RedisSocket {
|
||||||
const socket = new RedisSocket(
|
const socket = new RedisSocket(
|
||||||
() => Promise.resolve(),
|
() => Promise.resolve(),
|
||||||
options
|
options
|
||||||
);
|
);
|
||||||
|
|
||||||
socket.on('error', () => {
|
socket.on('error', () => {
|
||||||
// ignore errors
|
// ignore errors
|
||||||
});
|
|
||||||
|
|
||||||
return socket;
|
|
||||||
}
|
|
||||||
|
|
||||||
describe('reconnectStrategy', () => {
|
|
||||||
it('false', async () => {
|
|
||||||
const socket = createSocket({
|
|
||||||
host: 'error',
|
|
||||||
connectTimeout: 1,
|
|
||||||
reconnectStrategy: false
|
|
||||||
});
|
|
||||||
|
|
||||||
await assert.rejects(socket.connect());
|
|
||||||
|
|
||||||
assert.equal(socket.isOpen, false);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('0', async () => {
|
|
||||||
const socket = createSocket({
|
|
||||||
host: 'error',
|
|
||||||
connectTimeout: 1,
|
|
||||||
reconnectStrategy: 0
|
|
||||||
});
|
|
||||||
|
|
||||||
socket.connect();
|
|
||||||
await once(socket, 'error');
|
|
||||||
assert.equal(socket.isOpen, true);
|
|
||||||
assert.equal(socket.isReady, false);
|
|
||||||
socket.disconnect();
|
|
||||||
assert.equal(socket.isOpen, false);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('custom strategy', async () => {
|
|
||||||
const numberOfRetries = 3;
|
|
||||||
|
|
||||||
const reconnectStrategy = spy((retries: number) => {
|
|
||||||
assert.equal(retries + 1, reconnectStrategy.callCount);
|
|
||||||
|
|
||||||
if (retries === numberOfRetries) return new Error(`${numberOfRetries}`);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
});
|
|
||||||
|
|
||||||
const socket = createSocket({
|
|
||||||
host: 'error',
|
|
||||||
connectTimeout: 1,
|
|
||||||
reconnectStrategy
|
|
||||||
});
|
|
||||||
|
|
||||||
await assert.rejects(socket.connect(), {
|
|
||||||
message: `${numberOfRetries}`
|
|
||||||
});
|
|
||||||
|
|
||||||
assert.equal(socket.isOpen, false);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should handle errors', async () => {
|
|
||||||
const socket = createSocket({
|
|
||||||
host: 'error',
|
|
||||||
connectTimeout: 1,
|
|
||||||
reconnectStrategy(retries: number) {
|
|
||||||
if (retries === 1) return new Error('done');
|
|
||||||
throw new Error();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
await assert.rejects(socket.connect());
|
|
||||||
|
|
||||||
assert.equal(socket.isOpen, false);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
return socket;
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('reconnectStrategy', () => {
|
||||||
|
it('false', async () => {
|
||||||
|
const socket = createSocket({
|
||||||
|
host: 'error',
|
||||||
|
connectTimeout: 1,
|
||||||
|
reconnectStrategy: false
|
||||||
|
});
|
||||||
|
|
||||||
|
await assert.rejects(socket.connect());
|
||||||
|
|
||||||
|
assert.equal(socket.isOpen, false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('0', async () => {
|
||||||
|
const socket = createSocket({
|
||||||
|
host: 'error',
|
||||||
|
connectTimeout: 1,
|
||||||
|
reconnectStrategy: 0
|
||||||
|
});
|
||||||
|
|
||||||
|
socket.connect();
|
||||||
|
await once(socket, 'error');
|
||||||
|
assert.equal(socket.isOpen, true);
|
||||||
|
assert.equal(socket.isReady, false);
|
||||||
|
socket.destroy();
|
||||||
|
assert.equal(socket.isOpen, false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('custom strategy', async () => {
|
||||||
|
const numberOfRetries = 3;
|
||||||
|
|
||||||
|
const reconnectStrategy = spy((retries: number) => {
|
||||||
|
assert.equal(retries + 1, reconnectStrategy.callCount);
|
||||||
|
|
||||||
|
if (retries === numberOfRetries) return new Error(`${numberOfRetries}`);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
});
|
||||||
|
|
||||||
|
const socket = createSocket({
|
||||||
|
host: 'error',
|
||||||
|
connectTimeout: 1,
|
||||||
|
reconnectStrategy
|
||||||
|
});
|
||||||
|
|
||||||
|
await assert.rejects(socket.connect(), {
|
||||||
|
message: `${numberOfRetries}`
|
||||||
|
});
|
||||||
|
|
||||||
|
assert.equal(socket.isOpen, false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle errors', async () => {
|
||||||
|
const socket = createSocket({
|
||||||
|
host: 'error',
|
||||||
|
connectTimeout: 1,
|
||||||
|
reconnectStrategy(retries: number) {
|
||||||
|
if (retries === 1) return new Error('done');
|
||||||
|
throw new Error();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
await assert.rejects(socket.connect());
|
||||||
|
|
||||||
|
assert.equal(socket.isOpen, false);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@@ -10,17 +10,30 @@ import { RedisMultiQueuedCommand } from '../multi-command';
|
|||||||
import { PubSubListener } from '../client/pub-sub';
|
import { PubSubListener } from '../client/pub-sub';
|
||||||
import { ErrorReply } from '../errors';
|
import { ErrorReply } from '../errors';
|
||||||
|
|
||||||
|
interface ClusterCommander<
|
||||||
|
M extends RedisModules,
|
||||||
|
F extends RedisFunctions,
|
||||||
|
S extends RedisScripts,
|
||||||
|
RESP extends RespVersions,
|
||||||
|
TYPE_MAPPING extends TypeMapping,
|
||||||
|
POLICIES extends CommandPolicies
|
||||||
|
> extends CommanderConfig<M, F, S, RESP>{
|
||||||
|
commandOptions?: ClusterCommandOptions<TYPE_MAPPING, POLICIES>;
|
||||||
|
}
|
||||||
|
|
||||||
export type RedisClusterClientOptions = Omit<
|
export type RedisClusterClientOptions = Omit<
|
||||||
RedisClientOptions,
|
RedisClientOptions,
|
||||||
'modules' | 'functions' | 'scripts' | 'database' | 'RESP'
|
keyof ClusterCommander<RedisModules, RedisFunctions, RedisScripts, RespVersions, TypeMapping, CommandPolicies>
|
||||||
>;
|
>;
|
||||||
|
|
||||||
export interface RedisClusterOptions<
|
export interface RedisClusterOptions<
|
||||||
M extends RedisModules = RedisModules,
|
M extends RedisModules = RedisModules,
|
||||||
F extends RedisFunctions = RedisFunctions,
|
F extends RedisFunctions = RedisFunctions,
|
||||||
S extends RedisScripts = RedisScripts,
|
S extends RedisScripts = RedisScripts,
|
||||||
RESP extends RespVersions = RespVersions
|
RESP extends RespVersions = RespVersions,
|
||||||
> extends CommanderConfig<M, F, S, RESP> {
|
TYPE_MAPPING extends TypeMapping = TypeMapping,
|
||||||
|
POLICIES extends CommandPolicies = CommandPolicies
|
||||||
|
> extends ClusterCommander<M, F, S, RESP, TYPE_MAPPING, POLICIES> {
|
||||||
/**
|
/**
|
||||||
* Should contain details for some of the cluster nodes that the client will use to discover
|
* Should contain details for some of the cluster nodes that the client will use to discover
|
||||||
* the "cluster topology". We recommend including details for at least 3 nodes here.
|
* the "cluster topology". We recommend including details for at least 3 nodes here.
|
||||||
@@ -70,11 +83,14 @@ export type RedisClusterType<
|
|||||||
> = RedisCluster<M, F, S, RESP, TYPE_MAPPING, POLICIES> & WithCommands<RESP, TYPE_MAPPING, POLICIES>;
|
> = RedisCluster<M, F, S, RESP, TYPE_MAPPING, POLICIES> & WithCommands<RESP, TYPE_MAPPING, POLICIES>;
|
||||||
// & WithModules<M> & WithFunctions<F> & WithScripts<S>
|
// & WithModules<M> & WithFunctions<F> & WithScripts<S>
|
||||||
|
|
||||||
export interface ClusterCommandOptions extends CommandOptions {
|
export interface ClusterCommandOptions<
|
||||||
policies?: CommandPolicies;
|
TYPE_MAPPING extends TypeMapping = TypeMapping,
|
||||||
|
POLICIES extends CommandPolicies = CommandPolicies
|
||||||
|
> extends CommandOptions<TYPE_MAPPING> {
|
||||||
|
policies?: POLICIES;
|
||||||
}
|
}
|
||||||
|
|
||||||
type ProxyCluster = RedisCluster<RedisModules, RedisFunctions, RedisScripts, RespVersions, TypeMapping, CommandPolicies> & { commandOptions?: ClusterCommandOptions };
|
type ProxyCluster = RedisCluster<any, any, any, any, any, any>;
|
||||||
|
|
||||||
type NamespaceProxyCluster = { self: ProxyCluster };
|
type NamespaceProxyCluster = { self: ProxyCluster };
|
||||||
|
|
||||||
@@ -113,7 +129,7 @@ export default class RedisCluster<
|
|||||||
firstKey,
|
firstKey,
|
||||||
command.IS_READ_ONLY,
|
command.IS_READ_ONLY,
|
||||||
redisArgs,
|
redisArgs,
|
||||||
this.commandOptions,
|
this._commandOptions,
|
||||||
command.POLICIES
|
command.POLICIES
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -136,7 +152,7 @@ export default class RedisCluster<
|
|||||||
firstKey,
|
firstKey,
|
||||||
command.IS_READ_ONLY,
|
command.IS_READ_ONLY,
|
||||||
redisArgs,
|
redisArgs,
|
||||||
this.self.commandOptions,
|
this.self._commandOptions,
|
||||||
command.POLICIES
|
command.POLICIES
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -161,7 +177,7 @@ export default class RedisCluster<
|
|||||||
firstKey,
|
firstKey,
|
||||||
fn.IS_READ_ONLY,
|
fn.IS_READ_ONLY,
|
||||||
redisArgs,
|
redisArgs,
|
||||||
this.self.commandOptions,
|
this.self._commandOptions,
|
||||||
fn.POLICIES
|
fn.POLICIES
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -186,7 +202,7 @@ export default class RedisCluster<
|
|||||||
firstKey,
|
firstKey,
|
||||||
script.IS_READ_ONLY,
|
script.IS_READ_ONLY,
|
||||||
redisArgs,
|
redisArgs,
|
||||||
this.commandOptions,
|
this._commandOptions,
|
||||||
script.POLICIES
|
script.POLICIES
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -200,8 +216,10 @@ export default class RedisCluster<
|
|||||||
M extends RedisModules = {},
|
M extends RedisModules = {},
|
||||||
F extends RedisFunctions = {},
|
F extends RedisFunctions = {},
|
||||||
S extends RedisScripts = {},
|
S extends RedisScripts = {},
|
||||||
RESP extends RespVersions = 2
|
RESP extends RespVersions = 2,
|
||||||
>(config?: CommanderConfig<M, F, S, RESP>) {
|
TYPE_MAPPING extends TypeMapping = {},
|
||||||
|
POLICIES extends CommandPolicies = {}
|
||||||
|
>(config?: ClusterCommander<M, F, S, RESP, TYPE_MAPPING, POLICIES>) {
|
||||||
const Cluster = attachConfig({
|
const Cluster = attachConfig({
|
||||||
BaseClass: RedisCluster,
|
BaseClass: RedisCluster,
|
||||||
commands: COMMANDS,
|
commands: COMMANDS,
|
||||||
@@ -217,7 +235,7 @@ export default class RedisCluster<
|
|||||||
return (options?: Omit<RedisClusterOptions, keyof Exclude<typeof config, undefined>>) => {
|
return (options?: Omit<RedisClusterOptions, keyof Exclude<typeof config, undefined>>) => {
|
||||||
// returning a proxy of the client to prevent the namespaces.self to leak between proxies
|
// returning a proxy of the client to prevent the namespaces.self to leak between proxies
|
||||||
// namespaces will be bootstraped on first access per proxy
|
// namespaces will be bootstraped on first access per proxy
|
||||||
return Object.create(new Cluster(options)) as RedisClusterType<M, F, S, RESP>;
|
return Object.create(new Cluster(options)) as RedisClusterType<M, F, S, RESP, TYPE_MAPPING, POLICIES>;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -225,15 +243,19 @@ export default class RedisCluster<
|
|||||||
M extends RedisModules = {},
|
M extends RedisModules = {},
|
||||||
F extends RedisFunctions = {},
|
F extends RedisFunctions = {},
|
||||||
S extends RedisScripts = {},
|
S extends RedisScripts = {},
|
||||||
RESP extends RespVersions = 2
|
RESP extends RespVersions = 2,
|
||||||
>(options?: RedisClusterOptions<M, F, S, RESP>) {
|
TYPE_MAPPING extends TypeMapping = {},
|
||||||
|
POLICIES extends CommandPolicies = {}
|
||||||
|
>(options?: RedisClusterOptions<M, F, S, RESP, TYPE_MAPPING, POLICIES>) {
|
||||||
return RedisCluster.factory(options)(options);
|
return RedisCluster.factory(options)(options);
|
||||||
}
|
}
|
||||||
|
|
||||||
private readonly _options: RedisClusterOptions<M, F, S, RESP>;
|
private readonly _options: RedisClusterOptions<M, F, S, RESP, TYPE_MAPPING, POLICIES>;
|
||||||
|
|
||||||
private readonly _slots: RedisClusterSlots<M, F, S, RESP>;
|
private readonly _slots: RedisClusterSlots<M, F, S, RESP>;
|
||||||
|
|
||||||
|
private _commandOptions?: ClusterCommandOptions<TYPE_MAPPING, POLICIES>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An array of the cluster slots, each slot contain its `master` and `replicas`.
|
* An array of the cluster slots, each slot contain its `master` and `replicas`.
|
||||||
* Use with {@link RedisCluster.prototype.nodeClient} to get the client for a specific node (master or replica).
|
* Use with {@link RedisCluster.prototype.nodeClient} to get the client for a specific node (master or replica).
|
||||||
@@ -285,34 +307,49 @@ export default class RedisCluster<
|
|||||||
return this._slots.isOpen;
|
return this._slots.isOpen;
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(options: RedisClusterOptions<M, F, S, RESP>) {
|
constructor(options: RedisClusterOptions<M, F, S, RESP, TYPE_MAPPING, POLICIES>) {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
this._options = options;
|
this._options = options;
|
||||||
this._slots = new RedisClusterSlots(options, this.emit.bind(this));
|
this._slots = new RedisClusterSlots(options, this.emit.bind(this));
|
||||||
|
|
||||||
|
if (options?.commandOptions) {
|
||||||
|
this._commandOptions = options.commandOptions;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
duplicate(overrides?: Partial<RedisClusterOptions<M, F, S>>): RedisClusterType<M, F, S> {
|
duplicate<
|
||||||
|
_M extends RedisModules = M,
|
||||||
|
_F extends RedisFunctions = F,
|
||||||
|
_S extends RedisScripts = S,
|
||||||
|
_RESP extends RespVersions = RESP,
|
||||||
|
_TYPE_MAPPING extends TypeMapping = TYPE_MAPPING
|
||||||
|
>(overrides?: Partial<RedisClusterOptions<_M, _F, _S, _RESP, _TYPE_MAPPING>>) {
|
||||||
return new (Object.getPrototypeOf(this).constructor)({
|
return new (Object.getPrototypeOf(this).constructor)({
|
||||||
...this._options,
|
...this._options,
|
||||||
|
commandOptions: this._commandOptions,
|
||||||
...overrides
|
...overrides
|
||||||
});
|
}) as RedisClusterType<_M, _F, _S, _RESP, _TYPE_MAPPING>;
|
||||||
}
|
}
|
||||||
|
|
||||||
connect() {
|
connect() {
|
||||||
return this._slots.connect();
|
return this._slots.connect();
|
||||||
}
|
}
|
||||||
|
|
||||||
withCommandOptions<T extends ClusterCommandOptions>(options: T) {
|
withCommandOptions<
|
||||||
|
OPTIONS extends ClusterCommandOptions<TYPE_MAPPING, CommandPolicies>,
|
||||||
|
TYPE_MAPPING extends TypeMapping,
|
||||||
|
POLICIES extends CommandPolicies
|
||||||
|
>(options: OPTIONS) {
|
||||||
const proxy = Object.create(this);
|
const proxy = Object.create(this);
|
||||||
proxy.commandOptions = options;
|
proxy._commandOptions = options;
|
||||||
return proxy as RedisClusterType<
|
return proxy as RedisClusterType<
|
||||||
M,
|
M,
|
||||||
F,
|
F,
|
||||||
S,
|
S,
|
||||||
RESP,
|
RESP,
|
||||||
T['typeMapping'] extends TypeMapping ? T['typeMapping'] : {},
|
TYPE_MAPPING extends TypeMapping ? TYPE_MAPPING : {},
|
||||||
T['policies'] extends CommandPolicies ? T['policies'] : {}
|
POLICIES extends CommandPolicies ? POLICIES : {}
|
||||||
>;
|
>;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -324,8 +361,8 @@ export default class RedisCluster<
|
|||||||
value: V
|
value: V
|
||||||
) {
|
) {
|
||||||
const proxy = Object.create(this);
|
const proxy = Object.create(this);
|
||||||
proxy.commandOptions = Object.create((this as unknown as ProxyCluster).commandOptions ?? null);
|
proxy._commandOptions = Object.create(this._commandOptions ?? null);
|
||||||
proxy.commandOptions[key] = value;
|
proxy._commandOptions[key] = value;
|
||||||
return proxy as RedisClusterType<
|
return proxy as RedisClusterType<
|
||||||
M,
|
M,
|
||||||
F,
|
F,
|
||||||
|
@@ -22,32 +22,29 @@ describe('ACL LOG', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
testUtils.testWithClient('client.aclLog', async client => {
|
testUtils.testWithClient('client.aclLog', async client => {
|
||||||
// make sure to create at least one log
|
// make sure to create one log
|
||||||
await Promise.all([
|
await assert.rejects(
|
||||||
client.aclSetUser('test', 'on +@all'),
|
|
||||||
client.auth({
|
client.auth({
|
||||||
username: 'test',
|
username: 'incorrect',
|
||||||
password: 'test'
|
password: 'incorrect'
|
||||||
}),
|
|
||||||
client.auth({
|
|
||||||
username: 'default',
|
|
||||||
password: ''
|
|
||||||
})
|
})
|
||||||
]);
|
);
|
||||||
|
|
||||||
const logs = await client.aclLog();
|
const logs = await client.aclLog();
|
||||||
assert.ok(Array.isArray(logs));
|
assert.ok(Array.isArray(logs));
|
||||||
for (const log of logs) {
|
for (const log of logs) {
|
||||||
|
|
||||||
assert.equal(typeof log.count, 'number');
|
assert.equal(typeof log.count, 'number');
|
||||||
assert.equal(typeof log.timestamp, 'number');
|
assert.equal(typeof log.reason, 'string');
|
||||||
|
assert.equal(typeof log.context, 'string');
|
||||||
|
assert.equal(typeof log.object, 'string');
|
||||||
assert.equal(typeof log.username, 'string');
|
assert.equal(typeof log.username, 'string');
|
||||||
assert.equal(typeof log.clientId, 'string');
|
assert.equal(typeof log['age-seconds'], 'number');
|
||||||
assert.equal(typeof log.command, 'string');
|
assert.equal(typeof log['client-info'], 'string');
|
||||||
assert.equal(typeof log.args, 'string');
|
if (testUtils.isVersionGreaterThan([7, 2])) {
|
||||||
assert.equal(typeof log.key, 'string');
|
assert.equal(typeof log['entry-id'], 'number');
|
||||||
assert.equal(typeof log.result, 'number');
|
assert.equal(typeof log['timestamp-created'], 'number');
|
||||||
assert.equal(typeof log.duration, 'number');
|
assert.equal(typeof log['timestamp-last-updated'], 'number');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}, GLOBAL.SERVERS.OPEN);
|
}, GLOBAL.SERVERS.OPEN);
|
||||||
});
|
});
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
import { Resp2Reply } from '../RESP/types';
|
import { DoubleReply, Resp2Reply } from '../RESP/types';
|
||||||
import { ArrayReply, BlobStringReply, Command, NumberReply, TuplesToMapReply } from '../RESP/types';
|
import { ArrayReply, BlobStringReply, Command, NumberReply, TuplesToMapReply } from '../RESP/types';
|
||||||
|
|
||||||
export type AclLogReply = ArrayReply<TuplesToMapReply<[
|
export type AclLogReply = ArrayReply<TuplesToMapReply<[
|
||||||
@@ -7,8 +7,14 @@ export type AclLogReply = ArrayReply<TuplesToMapReply<[
|
|||||||
[BlobStringReply<'context'>, BlobStringReply],
|
[BlobStringReply<'context'>, BlobStringReply],
|
||||||
[BlobStringReply<'object'>, BlobStringReply],
|
[BlobStringReply<'object'>, BlobStringReply],
|
||||||
[BlobStringReply<'username'>, BlobStringReply],
|
[BlobStringReply<'username'>, BlobStringReply],
|
||||||
[BlobStringReply<'age-seconds'>, BlobStringReply],
|
[BlobStringReply<'age-seconds'>, DoubleReply],
|
||||||
[BlobStringReply<'client-info'>, BlobStringReply]
|
[BlobStringReply<'client-info'>, BlobStringReply],
|
||||||
|
/** added in 7.0 */
|
||||||
|
[BlobStringReply<'entry-id'>, NumberReply],
|
||||||
|
/** added in 7.0 */
|
||||||
|
[BlobStringReply<'timestamp-created'>, NumberReply],
|
||||||
|
/** added in 7.0 */
|
||||||
|
[BlobStringReply<'timestamp-last-updated'>, NumberReply]
|
||||||
]>>;
|
]>>;
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
@@ -24,15 +30,18 @@ export default {
|
|||||||
return args;
|
return args;
|
||||||
},
|
},
|
||||||
transformReply: {
|
transformReply: {
|
||||||
2: (reply: Resp2Reply<AclLogReply>) => ({
|
2: (reply: Resp2Reply<AclLogReply>) => reply.map(item => ({
|
||||||
count: Number(reply[1]),
|
count: item[1],
|
||||||
reason: reply[3],
|
reason: item[3],
|
||||||
context: reply[5],
|
context: item[5],
|
||||||
object: reply[7],
|
object: item[7],
|
||||||
username: reply[9],
|
username: item[9],
|
||||||
'age-seconds': Number(reply[11]),
|
'age-seconds': Number(item[11]),
|
||||||
'client-info': reply[13]
|
'client-info': item[13],
|
||||||
}),
|
'entry-id': item[15],
|
||||||
|
'timestamp-created': item[17],
|
||||||
|
'timestamp-last-updated': item[19]
|
||||||
|
})),
|
||||||
3: undefined as unknown as () => AclLogReply
|
3: undefined as unknown as () => AclLogReply
|
||||||
}
|
}
|
||||||
} as const satisfies Command;
|
} as const satisfies Command;
|
||||||
|
@@ -4,7 +4,7 @@ export default {
|
|||||||
FIRST_KEY_INDEX: undefined,
|
FIRST_KEY_INDEX: undefined,
|
||||||
IS_READ_ONLY: true,
|
IS_READ_ONLY: true,
|
||||||
transformArguments() {
|
transformArguments() {
|
||||||
return ['ACL', 'USERS'];
|
return ['ACL', 'WHOAMI'];
|
||||||
},
|
},
|
||||||
transformReply: undefined as unknown as () => BlobStringReply
|
transformReply: undefined as unknown as () => BlobStringReply
|
||||||
} as const satisfies Command;
|
} as const satisfies Command;
|
||||||
|
@@ -1,101 +1,101 @@
|
|||||||
import { strict as assert } from 'assert';
|
import { strict as assert } from 'assert';
|
||||||
import testUtils, { GLOBAL } from '../test-utils';
|
import testUtils, { GLOBAL } from '../test-utils';
|
||||||
import { transformArguments } from './CLIENT_TRACKING';
|
import CLIENT_TRACKING from './CLIENT_TRACKING';
|
||||||
|
|
||||||
describe('CLIENT TRACKING', () => {
|
describe('CLIENT TRACKING', () => {
|
||||||
testUtils.isVersionGreaterThanHook([6]);
|
testUtils.isVersionGreaterThanHook([6]);
|
||||||
|
|
||||||
describe('transformArguments', () => {
|
describe('transformArguments', () => {
|
||||||
describe('true', () => {
|
describe('true', () => {
|
||||||
it('simple', () => {
|
it('simple', () => {
|
||||||
assert.deepEqual(
|
assert.deepEqual(
|
||||||
transformArguments(true),
|
CLIENT_TRACKING.transformArguments(true),
|
||||||
['CLIENT', 'TRACKING', 'ON']
|
['CLIENT', 'TRACKING', 'ON']
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('with REDIRECT', () => {
|
it('with REDIRECT', () => {
|
||||||
assert.deepEqual(
|
assert.deepEqual(
|
||||||
transformArguments(true, {
|
CLIENT_TRACKING.transformArguments(true, {
|
||||||
REDIRECT: 1
|
REDIRECT: 1
|
||||||
}),
|
}),
|
||||||
['CLIENT', 'TRACKING', 'ON', 'REDIRECT', '1']
|
['CLIENT', 'TRACKING', 'ON', 'REDIRECT', '1']
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('with BCAST', () => {
|
describe('with BCAST', () => {
|
||||||
it('simple', () => {
|
it('simple', () => {
|
||||||
assert.deepEqual(
|
assert.deepEqual(
|
||||||
transformArguments(true, {
|
CLIENT_TRACKING.transformArguments(true, {
|
||||||
BCAST: true
|
BCAST: true
|
||||||
}),
|
}),
|
||||||
['CLIENT', 'TRACKING', 'ON', 'BCAST']
|
['CLIENT', 'TRACKING', 'ON', 'BCAST']
|
||||||
);
|
);
|
||||||
});
|
|
||||||
|
|
||||||
describe('with PREFIX', () => {
|
|
||||||
it('string', () => {
|
|
||||||
assert.deepEqual(
|
|
||||||
transformArguments(true, {
|
|
||||||
BCAST: true,
|
|
||||||
PREFIX: 'prefix'
|
|
||||||
}),
|
|
||||||
['CLIENT', 'TRACKING', 'ON', 'BCAST', 'PREFIX', 'prefix']
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('array', () => {
|
|
||||||
assert.deepEqual(
|
|
||||||
transformArguments(true, {
|
|
||||||
BCAST: true,
|
|
||||||
PREFIX: ['1', '2']
|
|
||||||
}),
|
|
||||||
['CLIENT', 'TRACKING', 'ON', 'BCAST', 'PREFIX', '1', 'PREFIX', '2']
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('with OPTIN', () => {
|
|
||||||
assert.deepEqual(
|
|
||||||
transformArguments(true, {
|
|
||||||
OPTIN: true
|
|
||||||
}),
|
|
||||||
['CLIENT', 'TRACKING', 'ON', 'OPTIN']
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('with OPTOUT', () => {
|
|
||||||
assert.deepEqual(
|
|
||||||
transformArguments(true, {
|
|
||||||
OPTOUT: true
|
|
||||||
}),
|
|
||||||
['CLIENT', 'TRACKING', 'ON', 'OPTOUT']
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('with NOLOOP', () => {
|
|
||||||
assert.deepEqual(
|
|
||||||
transformArguments(true, {
|
|
||||||
NOLOOP: true
|
|
||||||
}),
|
|
||||||
['CLIENT', 'TRACKING', 'ON', 'NOLOOP']
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('false', () => {
|
describe('with PREFIX', () => {
|
||||||
|
it('string', () => {
|
||||||
assert.deepEqual(
|
assert.deepEqual(
|
||||||
transformArguments(false),
|
CLIENT_TRACKING.transformArguments(true, {
|
||||||
['CLIENT', 'TRACKING', 'OFF']
|
BCAST: true,
|
||||||
|
PREFIX: 'prefix'
|
||||||
|
}),
|
||||||
|
['CLIENT', 'TRACKING', 'ON', 'BCAST', 'PREFIX', 'prefix']
|
||||||
);
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('array', () => {
|
||||||
|
assert.deepEqual(
|
||||||
|
CLIENT_TRACKING.transformArguments(true, {
|
||||||
|
BCAST: true,
|
||||||
|
PREFIX: ['1', '2']
|
||||||
|
}),
|
||||||
|
['CLIENT', 'TRACKING', 'ON', 'BCAST', 'PREFIX', '1', 'PREFIX', '2']
|
||||||
|
);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('with OPTIN', () => {
|
||||||
|
assert.deepEqual(
|
||||||
|
CLIENT_TRACKING.transformArguments(true, {
|
||||||
|
OPTIN: true
|
||||||
|
}),
|
||||||
|
['CLIENT', 'TRACKING', 'ON', 'OPTIN']
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('with OPTOUT', () => {
|
||||||
|
assert.deepEqual(
|
||||||
|
CLIENT_TRACKING.transformArguments(true, {
|
||||||
|
OPTOUT: true
|
||||||
|
}),
|
||||||
|
['CLIENT', 'TRACKING', 'ON', 'OPTOUT']
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('with NOLOOP', () => {
|
||||||
|
assert.deepEqual(
|
||||||
|
CLIENT_TRACKING.transformArguments(true, {
|
||||||
|
NOLOOP: true
|
||||||
|
}),
|
||||||
|
['CLIENT', 'TRACKING', 'ON', 'NOLOOP']
|
||||||
|
);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
testUtils.testWithClient('client.clientTracking', async client => {
|
it('false', () => {
|
||||||
assert.equal(
|
assert.deepEqual(
|
||||||
await client.clientTracking(false),
|
CLIENT_TRACKING.transformArguments(false),
|
||||||
'OK'
|
['CLIENT', 'TRACKING', 'OFF']
|
||||||
);
|
);
|
||||||
}, GLOBAL.SERVERS.OPEN);
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
testUtils.testWithClient('client.clientTracking', async client => {
|
||||||
|
assert.equal(
|
||||||
|
await client.clientTracking(false),
|
||||||
|
'OK'
|
||||||
|
);
|
||||||
|
}, GLOBAL.SERVERS.OPEN);
|
||||||
});
|
});
|
||||||
|
@@ -1,83 +1,86 @@
|
|||||||
// import { RedisCommandArgument, RedisCommandArguments } from '.';
|
import { RedisArgument, SimpleStringReply, Command } from '../RESP/types';
|
||||||
|
import { RedisVariadicArgument } from './generic-transformers';
|
||||||
|
interface CommonOptions {
|
||||||
|
REDIRECT?: number;
|
||||||
|
NOLOOP?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
// interface CommonOptions {
|
interface BroadcastOptions {
|
||||||
// REDIRECT?: number;
|
BCAST?: boolean;
|
||||||
// NOLOOP?: boolean;
|
PREFIX?: RedisVariadicArgument;
|
||||||
// }
|
}
|
||||||
|
|
||||||
// interface BroadcastOptions {
|
interface OptInOptions {
|
||||||
// BCAST?: boolean;
|
OPTIN?: boolean;
|
||||||
// PREFIX?: RedisCommandArgument | Array<RedisCommandArgument>;
|
}
|
||||||
// }
|
|
||||||
|
|
||||||
// interface OptInOptions {
|
interface OptOutOptions {
|
||||||
// OPTIN?: boolean;
|
OPTOUT?: boolean;
|
||||||
// }
|
}
|
||||||
|
|
||||||
// interface OptOutOptions {
|
type ClientTrackingOptions = CommonOptions & (
|
||||||
// OPTOUT?: boolean;
|
BroadcastOptions |
|
||||||
// }
|
OptInOptions |
|
||||||
|
OptOutOptions
|
||||||
|
);
|
||||||
|
|
||||||
// type ClientTrackingOptions = CommonOptions & (
|
export default {
|
||||||
// BroadcastOptions |
|
FIRST_KEY_INDEX: undefined,
|
||||||
// OptInOptions |
|
IS_READ_ONLY: true,
|
||||||
// OptOutOptions
|
transformArguments<M extends boolean>(
|
||||||
// );
|
mode: M,
|
||||||
|
options?: M extends true ? ClientTrackingOptions : never
|
||||||
|
) {
|
||||||
|
const args: Array<RedisArgument> = [
|
||||||
|
'CLIENT',
|
||||||
|
'TRACKING',
|
||||||
|
mode ? 'ON' : 'OFF'
|
||||||
|
];
|
||||||
|
|
||||||
// export function transformArguments<M extends boolean>(
|
if (mode) {
|
||||||
// mode: M,
|
if (options?.REDIRECT) {
|
||||||
// options?: M extends true ? ClientTrackingOptions : undefined
|
args.push(
|
||||||
// ): RedisCommandArguments {
|
'REDIRECT',
|
||||||
// const args: RedisCommandArguments = [
|
options.REDIRECT.toString()
|
||||||
// 'CLIENT',
|
);
|
||||||
// 'TRACKING',
|
}
|
||||||
// mode ? 'ON' : 'OFF'
|
|
||||||
// ];
|
|
||||||
|
|
||||||
// if (mode) {
|
if (isBroadcast(options)) {
|
||||||
// if (options?.REDIRECT) {
|
args.push('BCAST');
|
||||||
// args.push(
|
|
||||||
// 'REDIRECT',
|
|
||||||
// options.REDIRECT.toString()
|
|
||||||
// );
|
|
||||||
// }
|
|
||||||
|
|
||||||
// if (isBroadcast(options)) {
|
if (options?.PREFIX) {
|
||||||
// args.push('BCAST');
|
if (Array.isArray(options.PREFIX)) {
|
||||||
|
for (const prefix of options.PREFIX) {
|
||||||
|
args.push('PREFIX', prefix);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
args.push('PREFIX', options.PREFIX);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (isOptIn(options)) {
|
||||||
|
args.push('OPTIN');
|
||||||
|
} else if (isOptOut(options)) {
|
||||||
|
args.push('OPTOUT');
|
||||||
|
}
|
||||||
|
|
||||||
// if (options?.PREFIX) {
|
if (options?.NOLOOP) {
|
||||||
// if (Array.isArray(options.PREFIX)) {
|
args.push('NOLOOP');
|
||||||
// for (const prefix of options.PREFIX) {
|
}
|
||||||
// args.push('PREFIX', prefix);
|
}
|
||||||
// }
|
|
||||||
// } else {
|
|
||||||
// args.push('PREFIX', options.PREFIX);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// } else if (isOptIn(options)) {
|
|
||||||
// args.push('OPTIN');
|
|
||||||
// } else if (isOptOut(options)) {
|
|
||||||
// args.push('OPTOUT');
|
|
||||||
// }
|
|
||||||
|
|
||||||
// if (options?.NOLOOP) {
|
return args;
|
||||||
// args.push('NOLOOP');
|
},
|
||||||
// }
|
transformReply: undefined as unknown as () => SimpleStringReply<'OK'>
|
||||||
// }
|
} as const satisfies Command;
|
||||||
|
|
||||||
// return args;
|
function isBroadcast(options?: ClientTrackingOptions): options is BroadcastOptions {
|
||||||
// }
|
return (options as BroadcastOptions)?.BCAST === true;
|
||||||
|
}
|
||||||
|
|
||||||
// function isBroadcast(options?: ClientTrackingOptions): options is BroadcastOptions {
|
function isOptIn(options?: ClientTrackingOptions): options is OptInOptions {
|
||||||
// return (options as BroadcastOptions)?.BCAST === true;
|
return (options as OptInOptions)?.OPTIN === true;
|
||||||
// }
|
}
|
||||||
|
|
||||||
// function isOptIn(options?: ClientTrackingOptions): options is OptInOptions {
|
function isOptOut(options?: ClientTrackingOptions): options is OptOutOptions {
|
||||||
// return (options as OptInOptions)?.OPTIN === true;
|
return (options as OptOutOptions)?.OPTOUT === true;
|
||||||
// }
|
}
|
||||||
|
|
||||||
// function isOptOut(options?: ClientTrackingOptions): options is OptOutOptions {
|
|
||||||
// return (options as OptOutOptions)?.OPTOUT === true;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// export declare function transformReply(): 'OK' | Buffer;
|
|
||||||
|
@@ -1,25 +1,26 @@
|
|||||||
import { strict as assert } from 'assert';
|
import { strict as assert } from 'assert';
|
||||||
import testUtils, { GLOBAL } from '../test-utils';
|
import testUtils, { GLOBAL } from '../test-utils';
|
||||||
import { transformArguments } from './CLIENT_TRACKINGINFO';
|
import CLIENT_TRACKINGINFO from './CLIENT_TRACKINGINFO';
|
||||||
|
import { RESP_TYPES } from '../RESP/decoder';
|
||||||
|
|
||||||
describe('CLIENT TRACKINGINFO', () => {
|
describe('CLIENT TRACKINGINFO', () => {
|
||||||
testUtils.isVersionGreaterThanHook([6, 2]);
|
testUtils.isVersionGreaterThanHook([6, 2]);
|
||||||
|
|
||||||
it('transformArguments', () => {
|
it('transformArguments', () => {
|
||||||
assert.deepEqual(
|
assert.deepEqual(
|
||||||
transformArguments(),
|
CLIENT_TRACKINGINFO.transformArguments(),
|
||||||
['CLIENT', 'TRACKINGINFO']
|
['CLIENT', 'TRACKINGINFO']
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
testUtils.testWithClient('client.clientTrackingInfo', async client => {
|
testUtils.testWithClient('client.clientTrackingInfo', async client => {
|
||||||
assert.deepEqual(
|
assert.deepEqual(
|
||||||
await client.clientTrackingInfo(),
|
await client.clientTrackingInfo(),
|
||||||
{
|
{
|
||||||
flags: new Set(['off']),
|
flags: ['off'],
|
||||||
redirect: -1,
|
redirect: -1,
|
||||||
prefixes: []
|
prefixes: []
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}, GLOBAL.SERVERS.OPEN);
|
}, GLOBAL.SERVERS.OPEN);
|
||||||
});
|
});
|
||||||
|
@@ -1,28 +1,23 @@
|
|||||||
// import { RedisCommandArguments } from '.';
|
import { TuplesToMapReply, BlobStringReply, SetReply, NumberReply, ArrayReply, Resp2Reply, Command } from '../RESP/types';
|
||||||
|
|
||||||
// export function transformArguments(): RedisCommandArguments {
|
type TrackingInfo = TuplesToMapReply<[
|
||||||
// return ['CLIENT', 'TRACKINGINFO'];
|
[BlobStringReply<'flags'>, SetReply<BlobStringReply>],
|
||||||
// }
|
[BlobStringReply<'redirect'>, NumberReply],
|
||||||
|
[BlobStringReply<'prefixes'>, ArrayReply<BlobStringReply>]
|
||||||
|
]>;
|
||||||
|
|
||||||
// type RawReply = [
|
export default {
|
||||||
// 'flags',
|
FIRST_KEY_INDEX: undefined,
|
||||||
// Array<string>,
|
IS_READ_ONLY: true,
|
||||||
// 'redirect',
|
transformArguments() {
|
||||||
// number,
|
return ['CLIENT', 'TRACKINGINFO'];
|
||||||
// 'prefixes',
|
},
|
||||||
// Array<string>
|
transformReply: {
|
||||||
// ];
|
2: (reply: Resp2Reply<TrackingInfo>) => ({
|
||||||
|
flags: reply[1],
|
||||||
// interface Reply {
|
redirect: reply[3],
|
||||||
// flags: Set<string>;
|
prefixes: reply[5]
|
||||||
// redirect: number;
|
}),
|
||||||
// prefixes: Array<string>;
|
3: undefined as unknown as () => TrackingInfo
|
||||||
// }
|
}
|
||||||
|
} as const satisfies Command;
|
||||||
// export function transformReply(reply: RawReply): Reply {
|
|
||||||
// return {
|
|
||||||
// flags: new Set(reply[1]),
|
|
||||||
// redirect: reply[3],
|
|
||||||
// prefixes: reply[5]
|
|
||||||
// };
|
|
||||||
// }
|
|
||||||
|
@@ -1,21 +1,21 @@
|
|||||||
import { strict as assert } from 'assert';
|
import { strict as assert } from 'assert';
|
||||||
import testUtils, { GLOBAL } from '../test-utils';
|
import testUtils, { GLOBAL } from '../test-utils';
|
||||||
import { transformArguments } from './CLIENT_UNPAUSE';
|
import CLIENT_UNPAUSE from './CLIENT_UNPAUSE';
|
||||||
|
|
||||||
describe('CLIENT UNPAUSE', () => {
|
describe('CLIENT UNPAUSE', () => {
|
||||||
testUtils.isVersionGreaterThanHook([6, 2]);
|
testUtils.isVersionGreaterThanHook([6, 2]);
|
||||||
|
|
||||||
it('transformArguments', () => {
|
it('transformArguments', () => {
|
||||||
assert.deepEqual(
|
assert.deepEqual(
|
||||||
transformArguments(),
|
CLIENT_UNPAUSE.transformArguments(),
|
||||||
['CLIENT', 'UNPAUSE']
|
['CLIENT', 'UNPAUSE']
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
testUtils.testWithClient('client.unpause', async client => {
|
testUtils.testWithClient('client.clientUnpause', async client => {
|
||||||
assert.equal(
|
assert.equal(
|
||||||
await client.clientUnpause(),
|
await client.clientUnpause(),
|
||||||
'OK'
|
'OK'
|
||||||
);
|
);
|
||||||
}, GLOBAL.SERVERS.OPEN);
|
}, GLOBAL.SERVERS.OPEN);
|
||||||
});
|
});
|
||||||
|
@@ -1,20 +1,20 @@
|
|||||||
import { strict as assert } from 'assert';
|
import { strict as assert } from 'assert';
|
||||||
import { transformArguments } from './CLUSTER_ADDSLOTS';
|
import CLUSTER_ADDSLOTS from './CLUSTER_ADDSLOTS';
|
||||||
|
|
||||||
describe('CLUSTER ADDSLOTS', () => {
|
describe('CLUSTER ADDSLOTS', () => {
|
||||||
describe('transformArguments', () => {
|
describe('transformArguments', () => {
|
||||||
it('single', () => {
|
it('single', () => {
|
||||||
assert.deepEqual(
|
assert.deepEqual(
|
||||||
transformArguments(0),
|
CLUSTER_ADDSLOTS.transformArguments(0),
|
||||||
['CLUSTER', 'ADDSLOTS', '0']
|
['CLUSTER', 'ADDSLOTS', '0']
|
||||||
);
|
);
|
||||||
});
|
|
||||||
|
|
||||||
it('multiple', () => {
|
|
||||||
assert.deepEqual(
|
|
||||||
transformArguments([0, 1]),
|
|
||||||
['CLUSTER', 'ADDSLOTS', '0', '1']
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('multiple', () => {
|
||||||
|
assert.deepEqual(
|
||||||
|
CLUSTER_ADDSLOTS.transformArguments([0, 1]),
|
||||||
|
['CLUSTER', 'ADDSLOTS', '0', '1']
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@@ -1,29 +1,32 @@
|
|||||||
import { strict as assert } from 'assert';
|
import { strict as assert } from 'assert';
|
||||||
import { transformArguments } from './CLUSTER_ADDSLOTSRANGE';
|
import testUtils from '../test-utils';
|
||||||
|
import CLUSTER_ADDSLOTSRANGE from './CLUSTER_ADDSLOTSRANGE';
|
||||||
|
|
||||||
describe('CLUSTER ADDSLOTSRANGE', () => {
|
describe('CLUSTER ADDSLOTSRANGE', () => {
|
||||||
describe('transformArguments', () => {
|
testUtils.isVersionGreaterThanHook([7, 0]);
|
||||||
it('single', () => {
|
|
||||||
assert.deepEqual(
|
|
||||||
transformArguments({
|
|
||||||
start: 0,
|
|
||||||
end: 1
|
|
||||||
}),
|
|
||||||
['CLUSTER', 'ADDSLOTSRANGE', '0', '1']
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('multiple', () => {
|
describe('transformArguments', () => {
|
||||||
assert.deepEqual(
|
it('single', () => {
|
||||||
transformArguments([{
|
assert.deepEqual(
|
||||||
start: 0,
|
CLUSTER_ADDSLOTSRANGE.transformArguments({
|
||||||
end: 1
|
start: 0,
|
||||||
}, {
|
end: 1
|
||||||
start: 2,
|
}),
|
||||||
end: 3
|
['CLUSTER', 'ADDSLOTSRANGE', '0', '1']
|
||||||
}]),
|
);
|
||||||
['CLUSTER', 'ADDSLOTSRANGE', '0', '1', '2', '3']
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('multiple', () => {
|
||||||
|
assert.deepEqual(
|
||||||
|
CLUSTER_ADDSLOTSRANGE.transformArguments([{
|
||||||
|
start: 0,
|
||||||
|
end: 1
|
||||||
|
}, {
|
||||||
|
start: 2,
|
||||||
|
end: 3
|
||||||
|
}]),
|
||||||
|
['CLUSTER', 'ADDSLOTSRANGE', '0', '1', '2', '3']
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@@ -1,20 +1,20 @@
|
|||||||
import { strict as assert } from 'assert';
|
import { strict as assert } from 'assert';
|
||||||
import testUtils, { GLOBAL } from '../test-utils';
|
import testUtils, { GLOBAL } from '../test-utils';
|
||||||
import { transformArguments } from './CLUSTER_BUMPEPOCH';
|
import CLUSTER_BUMPEPOCH from './CLUSTER_BUMPEPOCH';
|
||||||
|
|
||||||
describe('CLUSTER BUMPEPOCH', () => {
|
describe('CLUSTER BUMPEPOCH', () => {
|
||||||
it('transformArguments', () => {
|
it('transformArguments', () => {
|
||||||
assert.deepEqual(
|
assert.deepEqual(
|
||||||
transformArguments(),
|
CLUSTER_BUMPEPOCH.transformArguments(),
|
||||||
['CLUSTER', 'BUMPEPOCH']
|
['CLUSTER', 'BUMPEPOCH']
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
testUtils.testWithCluster('clusterNode.clusterBumpEpoch', async cluster => {
|
testUtils.testWithCluster('clusterNode.clusterBumpEpoch', async cluster => {
|
||||||
const client = await cluster.nodeClient(cluster.masters[0]);
|
const client = await cluster.nodeClient(cluster.masters[0]);
|
||||||
assert.equal(
|
assert.equal(
|
||||||
typeof await client.clusterBumpEpoch(),
|
typeof await client.clusterBumpEpoch(),
|
||||||
'string'
|
'string'
|
||||||
);
|
);
|
||||||
}, GLOBAL.SERVERS.OPEN);
|
}, GLOBAL.SERVERS.OPEN);
|
||||||
});
|
});
|
||||||
|
@@ -1,20 +1,20 @@
|
|||||||
import { strict as assert } from 'assert';
|
import { strict as assert } from 'assert';
|
||||||
import testUtils, { GLOBAL } from '../test-utils';
|
import testUtils, { GLOBAL } from '../test-utils';
|
||||||
import { transformArguments } from './CLUSTER_KEYSLOT';
|
import CLUSTER_KEYSLOT from './CLUSTER_KEYSLOT';
|
||||||
|
|
||||||
describe('CLUSTER KEYSLOT', () => {
|
describe('CLUSTER KEYSLOT', () => {
|
||||||
it('transformArguments', () => {
|
it('transformArguments', () => {
|
||||||
assert.deepEqual(
|
assert.deepEqual(
|
||||||
transformArguments('key'),
|
CLUSTER_KEYSLOT.transformArguments('key'),
|
||||||
['CLUSTER', 'KEYSLOT', 'key']
|
['CLUSTER', 'KEYSLOT', 'key']
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
testUtils.testWithCluster('clusterNode.clusterKeySlot', async cluster => {
|
testUtils.testWithCluster('clusterNode.clusterKeySlot', async cluster => {
|
||||||
const client = await cluster.nodeClient(cluster.masters[0]);
|
const client = await cluster.nodeClient(cluster.masters[0]);
|
||||||
assert.equal(
|
assert.equal(
|
||||||
typeof await client.clusterKeySlot('key'),
|
typeof await client.clusterKeySlot('key'),
|
||||||
'number'
|
'number'
|
||||||
);
|
);
|
||||||
}, GLOBAL.CLUSTERS.OPEN);
|
}, GLOBAL.CLUSTERS.OPEN);
|
||||||
});
|
});
|
||||||
|
@@ -1,11 +1,11 @@
|
|||||||
import { strict as assert } from 'assert';
|
import { strict as assert } from 'assert';
|
||||||
import { transformArguments } from './CLUSTER_MEET';
|
import CLUSTER_MEET from './CLUSTER_MEET';
|
||||||
|
|
||||||
describe('CLUSTER MEET', () => {
|
describe('CLUSTER MEET', () => {
|
||||||
it('transformArguments', () => {
|
it('transformArguments', () => {
|
||||||
assert.deepEqual(
|
assert.deepEqual(
|
||||||
transformArguments('127.0.0.1', 6379),
|
CLUSTER_MEET.transformArguments('127.0.0.1', 6379),
|
||||||
['CLUSTER', 'MEET', '127.0.0.1', '6379']
|
['CLUSTER', 'MEET', '127.0.0.1', '6379']
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@@ -4,5 +4,5 @@ export default {
|
|||||||
transformArguments(host: string, port: number) {
|
transformArguments(host: string, port: number) {
|
||||||
return ['CLUSTER', 'MEET', host, port.toString()];
|
return ['CLUSTER', 'MEET', host, port.toString()];
|
||||||
},
|
},
|
||||||
transformReply: undefined as unknown as () => SimpleStringReply
|
transformReply: undefined as unknown as () => SimpleStringReply<'OK'>
|
||||||
} as const satisfies Command;
|
} as const satisfies Command;
|
||||||
|
@@ -1,11 +1,11 @@
|
|||||||
import { strict as assert } from 'assert';
|
import { strict as assert } from 'assert';
|
||||||
import { transformArguments } from './CLUSTER_REPLICATE';
|
import CLUSTER_REPLICATE from './CLUSTER_REPLICATE';
|
||||||
|
|
||||||
describe('CLUSTER REPLICATE', () => {
|
describe('CLUSTER REPLICATE', () => {
|
||||||
it('transformArguments', () => {
|
it('transformArguments', () => {
|
||||||
assert.deepEqual(
|
assert.deepEqual(
|
||||||
transformArguments('0'),
|
CLUSTER_REPLICATE.transformArguments('0'),
|
||||||
['CLUSTER', 'REPLICATE', '0']
|
['CLUSTER', 'REPLICATE', '0']
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@@ -1,20 +1,22 @@
|
|||||||
import { strict as assert } from 'assert';
|
import { strict as assert } from 'assert';
|
||||||
import { transformArguments } from './CLUSTER_RESET';
|
import CLUSTER_RESET from './CLUSTER_RESET';
|
||||||
|
|
||||||
describe('CLUSTER RESET', () => {
|
describe('CLUSTER RESET', () => {
|
||||||
describe('transformArguments', () => {
|
describe('transformArguments', () => {
|
||||||
it('simple', () => {
|
it('simple', () => {
|
||||||
assert.deepEqual(
|
assert.deepEqual(
|
||||||
transformArguments(),
|
CLUSTER_RESET.transformArguments(),
|
||||||
['CLUSTER', 'RESET']
|
['CLUSTER', 'RESET']
|
||||||
);
|
);
|
||||||
});
|
|
||||||
|
|
||||||
it('with mode', () => {
|
|
||||||
assert.deepEqual(
|
|
||||||
transformArguments('HARD'),
|
|
||||||
['CLUSTER', 'RESET', 'HARD']
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('with mode', () => {
|
||||||
|
assert.deepEqual(
|
||||||
|
CLUSTER_RESET.transformArguments({
|
||||||
|
mode: 'HARD'
|
||||||
|
}),
|
||||||
|
['CLUSTER', 'RESET', 'HARD']
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@@ -1,11 +1,20 @@
|
|||||||
export function transformArguments(mode?: 'HARD' | 'SOFT'): Array<string> {
|
import { SimpleStringReply, Command } from '../RESP/types';
|
||||||
|
|
||||||
|
export interface ClusterResetOptions {
|
||||||
|
mode?: 'HARD' | 'SOFT';
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
FIRST_KEY_INDEX: undefined,
|
||||||
|
IS_READ_ONLY: true,
|
||||||
|
transformArguments(options?: ClusterResetOptions) {
|
||||||
const args = ['CLUSTER', 'RESET'];
|
const args = ['CLUSTER', 'RESET'];
|
||||||
|
|
||||||
if (mode) {
|
if (options?.mode) {
|
||||||
args.push(mode);
|
args.push(options.mode);
|
||||||
}
|
}
|
||||||
|
|
||||||
return args;
|
return args;
|
||||||
}
|
},
|
||||||
|
transformReply: undefined as unknown as () => SimpleStringReply<'OK'>
|
||||||
export declare function transformReply(): string;
|
} as const satisfies Command;
|
||||||
|
@@ -1,20 +1,20 @@
|
|||||||
import { strict as assert } from 'assert';
|
import { strict as assert } from 'assert';
|
||||||
import testUtils, { GLOBAL } from '../test-utils';
|
import testUtils, { GLOBAL } from '../test-utils';
|
||||||
import { transformArguments } from './CLUSTER_SAVECONFIG';
|
import CLUSTER_SAVECONFIG from './CLUSTER_SAVECONFIG';
|
||||||
|
|
||||||
describe('CLUSTER SAVECONFIG', () => {
|
describe('CLUSTER SAVECONFIG', () => {
|
||||||
it('transformArguments', () => {
|
it('transformArguments', () => {
|
||||||
assert.deepEqual(
|
assert.deepEqual(
|
||||||
transformArguments(),
|
CLUSTER_SAVECONFIG.transformArguments(),
|
||||||
['CLUSTER', 'SAVECONFIG']
|
['CLUSTER', 'SAVECONFIG']
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
testUtils.testWithCluster('clusterNode.clusterSaveConfig', async cluster => {
|
testUtils.testWithCluster('clusterNode.clusterSaveConfig', async cluster => {
|
||||||
const client = await cluster.nodeClient(cluster.masters[0]);
|
const client = await cluster.nodeClient(cluster.masters[0]);
|
||||||
assert.equal(
|
assert.equal(
|
||||||
await client.clusterSaveConfig(),
|
await client.clusterSaveConfig(),
|
||||||
'OK'
|
'OK'
|
||||||
);
|
);
|
||||||
}, GLOBAL.CLUSTERS.OPEN);
|
}, GLOBAL.CLUSTERS.OPEN);
|
||||||
});
|
});
|
||||||
|
@@ -1,5 +1,11 @@
|
|||||||
export function transformArguments(): Array<string> {
|
import { SimpleStringReply, Command } from '../RESP/types';
|
||||||
return ['CLUSTER', 'SAVECONFIG'];
|
|
||||||
}
|
export default {
|
||||||
|
FIRST_KEY_INDEX: undefined,
|
||||||
|
IS_READ_ONLY: true,
|
||||||
|
transformArguments() {
|
||||||
|
return ['CLUSTER', 'SAVECONFIG'];
|
||||||
|
},
|
||||||
|
transformReply: undefined as unknown as () => SimpleStringReply<'OK'>
|
||||||
|
} as const satisfies Command;
|
||||||
|
|
||||||
export declare function transformReply(): 'OK';
|
|
||||||
|
@@ -1,11 +1,11 @@
|
|||||||
import { strict as assert } from 'assert';
|
import { strict as assert } from 'assert';
|
||||||
import { transformArguments } from './CLUSTER_SET-CONFIG-EPOCH';
|
import CLUSTER_SET_CONFIG_EPOCH from './CLUSTER_SET-CONFIG-EPOCH';
|
||||||
|
|
||||||
describe('CLUSTER SET-CONFIG-EPOCH', () => {
|
describe('CLUSTER SET-CONFIG-EPOCH', () => {
|
||||||
it('transformArguments', () => {
|
it('transformArguments', () => {
|
||||||
assert.deepEqual(
|
assert.deepEqual(
|
||||||
transformArguments(0),
|
CLUSTER_SET_CONFIG_EPOCH.transformArguments(0),
|
||||||
['CLUSTER', 'SET-CONFIG-EPOCH', '0']
|
['CLUSTER', 'SET-CONFIG-EPOCH', '0']
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@@ -1,5 +1,10 @@
|
|||||||
export function transformArguments(configEpoch: number): Array<string> {
|
import { SimpleStringReply, Command } from '../RESP/types';
|
||||||
return ['CLUSTER', 'SET-CONFIG-EPOCH', configEpoch.toString()];
|
|
||||||
}
|
|
||||||
|
|
||||||
export declare function transformReply(): 'OK';
|
export default {
|
||||||
|
FIRST_KEY_INDEX: undefined,
|
||||||
|
IS_READ_ONLY: true,
|
||||||
|
transformArguments(configEpoch: number) {
|
||||||
|
return ['CLUSTER', 'SET-CONFIG-EPOCH', configEpoch.toString() ];
|
||||||
|
},
|
||||||
|
transformReply: undefined as unknown as () => SimpleStringReply<'OK'>
|
||||||
|
} as const satisfies Command;
|
||||||
|
@@ -1,20 +1,20 @@
|
|||||||
import { strict as assert } from 'assert';
|
import { strict as assert } from 'assert';
|
||||||
import { ClusterSlotStates, transformArguments } from './CLUSTER_SETSLOT';
|
import CLUSTER_SETSLOT, { CLUSTER_SLOT_STATES } from './CLUSTER_SETSLOT';
|
||||||
|
|
||||||
describe('CLUSTER SETSLOT', () => {
|
describe('CLUSTER SETSLOT', () => {
|
||||||
describe('transformArguments', () => {
|
describe('transformArguments', () => {
|
||||||
it('simple', () => {
|
it('simple', () => {
|
||||||
assert.deepEqual(
|
assert.deepEqual(
|
||||||
transformArguments(0, ClusterSlotStates.IMPORTING),
|
CLUSTER_SETSLOT.transformArguments(0, CLUSTER_SLOT_STATES.IMPORTING),
|
||||||
['CLUSTER', 'SETSLOT', '0', 'IMPORTING']
|
['CLUSTER', 'SETSLOT', '0', 'IMPORTING']
|
||||||
);
|
);
|
||||||
});
|
|
||||||
|
|
||||||
it('with nodeId', () => {
|
|
||||||
assert.deepEqual(
|
|
||||||
transformArguments(0, ClusterSlotStates.IMPORTING, 'nodeId'),
|
|
||||||
['CLUSTER', 'SETSLOT', '0', 'IMPORTING', 'nodeId']
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('with nodeId', () => {
|
||||||
|
assert.deepEqual(
|
||||||
|
CLUSTER_SETSLOT.transformArguments(0, CLUSTER_SLOT_STATES.IMPORTING, 'nodeId'),
|
||||||
|
['CLUSTER', 'SETSLOT', '0', 'IMPORTING', 'nodeId']
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@@ -1,22 +1,25 @@
|
|||||||
export enum ClusterSlotStates {
|
import { SimpleStringReply, Command } from '../RESP/types';
|
||||||
IMPORTING = 'IMPORTING',
|
|
||||||
MIGRATING = 'MIGRATING',
|
|
||||||
STABLE = 'STABLE',
|
|
||||||
NODE = 'NODE'
|
|
||||||
}
|
|
||||||
|
|
||||||
export function transformArguments(
|
export const CLUSTER_SLOT_STATES = {
|
||||||
slot: number,
|
IMPORTING: 'IMPORTING',
|
||||||
state: ClusterSlotStates,
|
MIGRATING: 'MIGRATING',
|
||||||
nodeId?: string
|
STABLE: 'STABLE',
|
||||||
): Array<string> {
|
NODE: 'NODE'
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
export type ClusterSlotStates = typeof CLUSTER_SLOT_STATES[keyof typeof CLUSTER_SLOT_STATES];
|
||||||
|
|
||||||
|
export default {
|
||||||
|
FIRST_KEY_INDEX: undefined,
|
||||||
|
IS_READ_ONLY: true,
|
||||||
|
transformArguments(slot: number, state: ClusterSlotStates, nodeId?: string) {
|
||||||
const args = ['CLUSTER', 'SETSLOT', slot.toString(), state];
|
const args = ['CLUSTER', 'SETSLOT', slot.toString(), state];
|
||||||
|
|
||||||
if (nodeId) {
|
if (nodeId) {
|
||||||
args.push(nodeId);
|
args.push(nodeId);
|
||||||
}
|
}
|
||||||
|
|
||||||
return args;
|
return args;
|
||||||
}
|
},
|
||||||
|
transformReply: undefined as unknown as () => SimpleStringReply<'OK'>
|
||||||
export declare function transformReply(): 'OK';
|
} as const satisfies Command;
|
||||||
|
@@ -1,76 +1,30 @@
|
|||||||
import { strict as assert } from 'assert';
|
import { strict as assert } from 'assert';
|
||||||
import { transformArguments, transformReply } from './CLUSTER_SLOTS';
|
import testUtils, { GLOBAL } from '../test-utils';
|
||||||
|
import CLUSTER_SLOTS from './CLUSTER_SLOTS';
|
||||||
|
|
||||||
describe('CLUSTER SLOTS', () => {
|
describe('CLUSTER SLOTS', () => {
|
||||||
it('transformArguments', () => {
|
it('transformArguments', () => {
|
||||||
assert.deepEqual(
|
assert.deepEqual(
|
||||||
transformArguments(),
|
CLUSTER_SLOTS.transformArguments(),
|
||||||
['CLUSTER', 'SLOTS']
|
['CLUSTER', 'SLOTS']
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('transformReply', () => {
|
testUtils.testWithCluster('clusterNode.clusterSlots', async cluster => {
|
||||||
assert.deepEqual(
|
const client = await cluster.nodeClient(cluster.masters[0]),
|
||||||
transformReply([
|
slots = await client.clusterSlots();
|
||||||
[
|
assert.ok(Array.isArray(slots));
|
||||||
0,
|
for (const { from, to, master, replicas } of slots) {
|
||||||
5460,
|
assert.equal(typeof from, 'number');
|
||||||
['127.0.0.1', 30001, '09dbe9720cda62f7865eabc5fd8857c5d2678366'],
|
assert.equal(typeof to, 'number');
|
||||||
['127.0.0.1', 30004, '821d8ca00d7ccf931ed3ffc7e3db0599d2271abf']
|
assert.equal(typeof master.host, 'string');
|
||||||
],
|
assert.equal(typeof master.port, 'number');
|
||||||
[
|
assert.equal(typeof master.id, 'string');
|
||||||
5461,
|
for (const replica of replicas) {
|
||||||
10922,
|
assert.equal(typeof replica.host, 'string');
|
||||||
['127.0.0.1', 30002, 'c9d93d9f2c0c524ff34cc11838c2003d8c29e013'],
|
assert.equal(typeof replica.port, 'number');
|
||||||
['127.0.0.1', 30005, 'faadb3eb99009de4ab72ad6b6ed87634c7ee410f']
|
assert.equal(typeof replica.id, 'string');
|
||||||
],
|
}
|
||||||
[
|
}
|
||||||
10923,
|
}, GLOBAL.CLUSTERS.OPEN);
|
||||||
16383,
|
|
||||||
['127.0.0.1', 30003, '044ec91f325b7595e76dbcb18cc688b6a5b434a1'],
|
|
||||||
['127.0.0.1', 30006, '58e6e48d41228013e5d9c1c37c5060693925e97e']
|
|
||||||
]
|
|
||||||
]),
|
|
||||||
[{
|
|
||||||
from: 0,
|
|
||||||
to: 5460,
|
|
||||||
master: {
|
|
||||||
ip: '127.0.0.1',
|
|
||||||
port: 30001,
|
|
||||||
id: '09dbe9720cda62f7865eabc5fd8857c5d2678366'
|
|
||||||
},
|
|
||||||
replicas: [{
|
|
||||||
ip: '127.0.0.1',
|
|
||||||
port: 30004,
|
|
||||||
id: '821d8ca00d7ccf931ed3ffc7e3db0599d2271abf'
|
|
||||||
}]
|
|
||||||
}, {
|
|
||||||
from: 5461,
|
|
||||||
to: 10922,
|
|
||||||
master: {
|
|
||||||
ip: '127.0.0.1',
|
|
||||||
port: 30002,
|
|
||||||
id: 'c9d93d9f2c0c524ff34cc11838c2003d8c29e013'
|
|
||||||
},
|
|
||||||
replicas: [{
|
|
||||||
ip: '127.0.0.1',
|
|
||||||
port: 30005,
|
|
||||||
id: 'faadb3eb99009de4ab72ad6b6ed87634c7ee410f'
|
|
||||||
}]
|
|
||||||
}, {
|
|
||||||
from: 10923,
|
|
||||||
to: 16383,
|
|
||||||
master: {
|
|
||||||
ip: '127.0.0.1',
|
|
||||||
port: 30003,
|
|
||||||
id: '044ec91f325b7595e76dbcb18cc688b6a5b434a1'
|
|
||||||
},
|
|
||||||
replicas: [{
|
|
||||||
ip: '127.0.0.1',
|
|
||||||
port: 30006,
|
|
||||||
id: '58e6e48d41228013e5d9c1c37c5060693925e97e'
|
|
||||||
}]
|
|
||||||
}]
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
@@ -30,7 +30,9 @@ describe('FUNCTION RESTORE', () => {
|
|||||||
await client.withTypeMapping({
|
await client.withTypeMapping({
|
||||||
[RESP_TYPES.BLOB_STRING]: Buffer
|
[RESP_TYPES.BLOB_STRING]: Buffer
|
||||||
}).functionDump(),
|
}).functionDump(),
|
||||||
'FLUSH'
|
{
|
||||||
|
mode: 'REPLACE'
|
||||||
|
}
|
||||||
),
|
),
|
||||||
'OK'
|
'OK'
|
||||||
);
|
);
|
||||||
|
@@ -36,11 +36,33 @@ import CLIENT_LIST from './CLIENT_LIST';
|
|||||||
import CLIENT_NO_EVICT from './CLIENT_NO-EVICT';
|
import CLIENT_NO_EVICT from './CLIENT_NO-EVICT';
|
||||||
import CLIENT_PAUSE from './CLIENT_PAUSE';
|
import CLIENT_PAUSE from './CLIENT_PAUSE';
|
||||||
import CLIENT_SETNAME from './CLIENT_SETNAME';
|
import CLIENT_SETNAME from './CLIENT_SETNAME';
|
||||||
|
import CLIENT_TRACKING from './CLIENT_TRACKING';
|
||||||
|
import CLIENT_TRACKINGINFO from './CLIENT_TRACKINGINFO';
|
||||||
|
import CLIENT_UNPAUSE from './CLIENT_UNPAUSE';
|
||||||
import CLUSTER_ADDSLOTS from './CLUSTER_ADDSLOTS';
|
import CLUSTER_ADDSLOTS from './CLUSTER_ADDSLOTS';
|
||||||
import CLUSTER_SLOTS from './CLUSTER_SLOTS';
|
import CLUSTER_ADDSLOTSRANGE from './CLUSTER_ADDSLOTSRANGE';
|
||||||
|
import CLUSTER_BUMPEPOCH from './CLUSTER_BUMPEPOCH';
|
||||||
|
import CLUSTER_COUNT_FAILURE_REPORTS from './CLUSTER_COUNT-FAILURE-REPORTS';
|
||||||
|
import CLUSTER_COUNTKEYSINSLOT from './CLUSTER_COUNTKEYSINSLOT';
|
||||||
|
import CLUSTER_DELSLOTS from './CLUSTER_DELSLOTS';
|
||||||
|
import CLUSTER_DELSLOTSRANGE from './CLUSTER_DELSLOTSRANGE';
|
||||||
|
import CLUSTER_FAILOVER from './CLUSTER_FAILOVER';
|
||||||
|
import CLUSTER_FLUSHSLOTS from './CLUSTER_FLUSHSLOTS';
|
||||||
|
import CLUSTER_FORGET from './CLUSTER_FORGET';
|
||||||
|
import CLUSTER_GETKEYSINSLOT from './CLUSTER_GETKEYSINSLOT';
|
||||||
|
// import CLUSTER_INFO from './CLUSTER_INFO';
|
||||||
|
import CLUSTER_KEYSLOT from './CLUSTER_KEYSLOT';
|
||||||
|
// import CLUSTER_LINKS from './CLUSTER_LINKS';
|
||||||
import CLUSTER_MEET from './CLUSTER_MEET';
|
import CLUSTER_MEET from './CLUSTER_MEET';
|
||||||
import CLUSTER_MYID from './CLUSTER_MYID';
|
import CLUSTER_MYID from './CLUSTER_MYID';
|
||||||
|
// import CLUSTER_NODES from './CLUSTER_NODES';
|
||||||
|
// import CLUSTER_REPLICAS from './CLUSTER_REPLICAS';
|
||||||
import CLUSTER_REPLICATE from './CLUSTER_REPLICATE';
|
import CLUSTER_REPLICATE from './CLUSTER_REPLICATE';
|
||||||
|
import CLUSTER_RESET from './CLUSTER_RESET';
|
||||||
|
import CLUSTER_SAVECONFIG from './CLUSTER_SAVECONFIG';
|
||||||
|
import CLUSTER_SET_CONFIG_EPOCH from './CLUSTER_SET-CONFIG-EPOCH';
|
||||||
|
import CLUSTER_SETSLOT from './CLUSTER_SETSLOT';
|
||||||
|
import CLUSTER_SLOTS from './CLUSTER_SLOTS';
|
||||||
import COPY from './COPY';
|
import COPY from './COPY';
|
||||||
import DBSIZE from './DBSIZE';
|
import DBSIZE from './DBSIZE';
|
||||||
import DECR from './DECR';
|
import DECR from './DECR';
|
||||||
@@ -90,7 +112,7 @@ import FUNCTION_KILL from './FUNCTION_KILL';
|
|||||||
import FUNCTION_LIST_WITHCODE from './FUNCTION_LIST_WITHCODE';
|
import FUNCTION_LIST_WITHCODE from './FUNCTION_LIST_WITHCODE';
|
||||||
import FUNCTION_LIST from './FUNCTION_LIST';
|
import FUNCTION_LIST from './FUNCTION_LIST';
|
||||||
import FUNCTION_LOAD from './FUNCTION_LOAD';
|
import FUNCTION_LOAD from './FUNCTION_LOAD';
|
||||||
// import FUNCTION_RESTORE from './FUNCTION_RESTORE';
|
import FUNCTION_RESTORE from './FUNCTION_RESTORE';
|
||||||
// import FUNCTION_STATS from './FUNCTION_STATS';
|
// import FUNCTION_STATS from './FUNCTION_STATS';
|
||||||
import HDEL from './HDEL';
|
import HDEL from './HDEL';
|
||||||
import HELLO from './HELLO';
|
import HELLO from './HELLO';
|
||||||
@@ -297,11 +319,33 @@ type CLIENT_LIST = typeof import('./CLIENT_LIST').default;
|
|||||||
type CLIENT_NO_EVICT = typeof import('./CLIENT_NO-EVICT').default;
|
type CLIENT_NO_EVICT = typeof import('./CLIENT_NO-EVICT').default;
|
||||||
type CLIENT_PAUSE = typeof import('./CLIENT_PAUSE').default;
|
type CLIENT_PAUSE = typeof import('./CLIENT_PAUSE').default;
|
||||||
type CLIENT_SETNAME = typeof import('./CLIENT_SETNAME').default;
|
type CLIENT_SETNAME = typeof import('./CLIENT_SETNAME').default;
|
||||||
|
type CLIENT_TRACKING = typeof import('./CLIENT_TRACKING').default;
|
||||||
|
type CLIENT_TRACKINGINFO = typeof import('./CLIENT_TRACKINGINFO').default;
|
||||||
|
type CLIENT_UNPAUSE = typeof import('./CLIENT_UNPAUSE').default;
|
||||||
type CLUSTER_ADDSLOTS = typeof import('./CLUSTER_ADDSLOTS').default;
|
type CLUSTER_ADDSLOTS = typeof import('./CLUSTER_ADDSLOTS').default;
|
||||||
type CLUSTER_SLOTS = typeof import('./CLUSTER_SLOTS').default;
|
type CLUSTER_ADDSLOTSRANGE = typeof import('./CLUSTER_ADDSLOTSRANGE').default;
|
||||||
|
type CLUSTER_BUMPEPOCH = typeof import('./CLUSTER_BUMPEPOCH').default;
|
||||||
|
type CLUSTER_COUNT_FAILURE_REPORTS = typeof import('./CLUSTER_COUNT-FAILURE-REPORTS').default;
|
||||||
|
type CLUSTER_COUNTKEYSINSLOT = typeof import('./CLUSTER_COUNTKEYSINSLOT').default;
|
||||||
|
type CLUSTER_DELSLOTS = typeof import('./CLUSTER_DELSLOTS').default;
|
||||||
|
type CLUSTER_DELSLOTSRANGE = typeof import('./CLUSTER_DELSLOTSRANGE').default;
|
||||||
|
type CLUSTER_FAILOVER = typeof import('./CLUSTER_FAILOVER').default;
|
||||||
|
type CLUSTER_FLUSHSLOTS = typeof import('./CLUSTER_FLUSHSLOTS').default;
|
||||||
|
type CLUSTER_FORGET = typeof import('./CLUSTER_FORGET').default;
|
||||||
|
type CLUSTER_GETKEYSINSLOT = typeof import('./CLUSTER_GETKEYSINSLOT').default;
|
||||||
|
// type CLUSTER_INFO = typeof import('./CLUSTER_INFO').default;
|
||||||
|
type CLUSTER_KEYSLOT = typeof import('./CLUSTER_KEYSLOT').default;
|
||||||
|
// type CLUSTER_LINKS = typeof import('./CLUSTER_LINKS').default;
|
||||||
type CLUSTER_MEET = typeof import('./CLUSTER_MEET').default;
|
type CLUSTER_MEET = typeof import('./CLUSTER_MEET').default;
|
||||||
type CLUSTER_MYID = typeof import('./CLUSTER_MYID').default;
|
type CLUSTER_MYID = typeof import('./CLUSTER_MYID').default;
|
||||||
|
// type CLUSTER_NODES = typeof import('./CLUSTER_NODES').default;
|
||||||
|
// type CLUSTER_REPLICAS = typeof import('./CLUSTER_REPLICAS').default;
|
||||||
type CLUSTER_REPLICATE = typeof import('./CLUSTER_REPLICATE').default;
|
type CLUSTER_REPLICATE = typeof import('./CLUSTER_REPLICATE').default;
|
||||||
|
type CLUSTER_RESET = typeof import('./CLUSTER_RESET').default;
|
||||||
|
type CLUSTER_SAVECONFIG = typeof import('./CLUSTER_SAVECONFIG').default;
|
||||||
|
type CLUSTER_SET_CONFIG_EPOCH = typeof import('./CLUSTER_SET-CONFIG-EPOCH').default;
|
||||||
|
type CLUSTER_SETSLOT = typeof import('./CLUSTER_SETSLOT').default;
|
||||||
|
type CLUSTER_SLOTS = typeof import('./CLUSTER_SLOTS').default;
|
||||||
type COPY = typeof import('./COPY').default;
|
type COPY = typeof import('./COPY').default;
|
||||||
type DBSIZE = typeof DBSIZE;
|
type DBSIZE = typeof DBSIZE;
|
||||||
type DECR = typeof import('./DECR').default;
|
type DECR = typeof import('./DECR').default;
|
||||||
@@ -351,7 +395,7 @@ type FUNCTION_KILL = typeof import('./FUNCTION_KILL').default;
|
|||||||
type FUNCTION_LIST_WITHCODE = typeof import('./FUNCTION_LIST_WITHCODE').default;
|
type FUNCTION_LIST_WITHCODE = typeof import('./FUNCTION_LIST_WITHCODE').default;
|
||||||
type FUNCTION_LIST = typeof import('./FUNCTION_LIST').default;
|
type FUNCTION_LIST = typeof import('./FUNCTION_LIST').default;
|
||||||
type FUNCTION_LOAD = typeof import('./FUNCTION_LOAD').default;
|
type FUNCTION_LOAD = typeof import('./FUNCTION_LOAD').default;
|
||||||
// type FUNCTION_RESTORE = typeof import('./FUNCTION_RESTORE').default;
|
type FUNCTION_RESTORE = typeof import('./FUNCTION_RESTORE').default;
|
||||||
// type FUNCTION_STATS = typeof import('./FUNCTION_STATS').default;
|
// type FUNCTION_STATS = typeof import('./FUNCTION_STATS').default;
|
||||||
type HDEL = typeof import('./HDEL').default;
|
type HDEL = typeof import('./HDEL').default;
|
||||||
type HELLO = typeof import('./HELLO').default;
|
type HELLO = typeof import('./HELLO').default;
|
||||||
@@ -596,16 +640,60 @@ type Commands = {
|
|||||||
clientPause: CLIENT_PAUSE;
|
clientPause: CLIENT_PAUSE;
|
||||||
CLIENT_SETNAME: CLIENT_SETNAME;
|
CLIENT_SETNAME: CLIENT_SETNAME;
|
||||||
clientSetName: CLIENT_SETNAME;
|
clientSetName: CLIENT_SETNAME;
|
||||||
|
CLIENT_TRACKING: CLIENT_TRACKING;
|
||||||
|
clientTracking: CLIENT_TRACKING;
|
||||||
|
CLIENT_TRACKINGINFO: CLIENT_TRACKINGINFO;
|
||||||
|
clientTrackingInfo: CLIENT_TRACKINGINFO;
|
||||||
|
CLIENT_UNPAUSE: CLIENT_UNPAUSE;
|
||||||
|
clientUnpause: CLIENT_UNPAUSE;
|
||||||
CLUSTER_ADDSLOTS: CLUSTER_ADDSLOTS;
|
CLUSTER_ADDSLOTS: CLUSTER_ADDSLOTS;
|
||||||
clusterAddSlots: CLUSTER_ADDSLOTS;
|
clusterAddSlots: CLUSTER_ADDSLOTS;
|
||||||
CLUSTER_SLOTS: CLUSTER_SLOTS;
|
CLUSTER_ADDSLOTSRANGE: CLUSTER_ADDSLOTSRANGE;
|
||||||
clusterSlots: CLUSTER_SLOTS;
|
clusterAddSlotsRange: CLUSTER_ADDSLOTSRANGE;
|
||||||
|
CLUSTER_BUMPEPOCH: CLUSTER_BUMPEPOCH;
|
||||||
|
clusterBumpEpoch: CLUSTER_BUMPEPOCH;
|
||||||
|
'CLUSTER_COUNT-FAILURE-REPORTS': CLUSTER_COUNT_FAILURE_REPORTS;
|
||||||
|
clusterCountFailureReports: CLUSTER_COUNT_FAILURE_REPORTS;
|
||||||
|
CLUSTER_COUNTKEYSINSLOT: CLUSTER_COUNTKEYSINSLOT;
|
||||||
|
clusterCountKeysInSlot: CLUSTER_COUNTKEYSINSLOT;
|
||||||
|
CLUSTER_DELSLOTS: CLUSTER_DELSLOTS;
|
||||||
|
clusterDelSlots: CLUSTER_DELSLOTS;
|
||||||
|
CLUSTER_DELSLOTSRANGE: CLUSTER_DELSLOTSRANGE;
|
||||||
|
clusterDelSlotsRange: CLUSTER_DELSLOTSRANGE;
|
||||||
|
CLUSTER_FAILOVER: CLUSTER_FAILOVER;
|
||||||
|
clusterFailover: CLUSTER_FAILOVER;
|
||||||
|
CLUSTER_FLUSHSLOTS: CLUSTER_FLUSHSLOTS;
|
||||||
|
clusterFlushSlots: CLUSTER_FLUSHSLOTS;
|
||||||
|
CLUSTER_FORGET: CLUSTER_FORGET;
|
||||||
|
clusterForget: CLUSTER_FORGET;
|
||||||
|
CLUSTER_GETKEYSINSLOT: CLUSTER_GETKEYSINSLOT;
|
||||||
|
clusterGetKeysInSlot: CLUSTER_GETKEYSINSLOT;
|
||||||
|
// CLUSTER_INFO: CLUSTER_INFO;
|
||||||
|
// clusterInfo: CLUSTER_INFO;
|
||||||
|
CLUSTER_KEYSLOT: CLUSTER_KEYSLOT;
|
||||||
|
clusterKeySlot: CLUSTER_KEYSLOT;
|
||||||
|
// CLUSTER_LINKS: CLUSTER_LINKS;
|
||||||
|
// clusterLinks: CLUSTER_LINKS;
|
||||||
CLUSTER_MEET: CLUSTER_MEET;
|
CLUSTER_MEET: CLUSTER_MEET;
|
||||||
clusterMeet: CLUSTER_MEET;
|
clusterMeet: CLUSTER_MEET;
|
||||||
CLUSTER_MYID: CLUSTER_MYID;
|
CLUSTER_MYID: CLUSTER_MYID;
|
||||||
clusterMyId: CLUSTER_MYID;
|
clusterMyId: CLUSTER_MYID;
|
||||||
|
// CLUSTER_NODES: CLUSTER_NODES;
|
||||||
|
// clusterNodes: CLUSTER_NODES;
|
||||||
|
// CLUSTER_REPLICAS: CLUSTER_REPLICAS;
|
||||||
|
// clusterReplicas: CLUSTER_REPLICAS;
|
||||||
CLUSTER_REPLICATE: CLUSTER_REPLICATE;
|
CLUSTER_REPLICATE: CLUSTER_REPLICATE;
|
||||||
clusterReplicate: CLUSTER_REPLICATE;
|
clusterReplicate: CLUSTER_REPLICATE;
|
||||||
|
CLUSTER_RESET: CLUSTER_RESET;
|
||||||
|
clusterReset: CLUSTER_RESET;
|
||||||
|
CLUSTER_SAVECONFIG: CLUSTER_SAVECONFIG;
|
||||||
|
clusterSaveConfig: CLUSTER_SAVECONFIG;
|
||||||
|
'CLUSTER_SET-CONFIG-EPOCH': CLUSTER_SET_CONFIG_EPOCH;
|
||||||
|
clusterSetConfigEpoch: CLUSTER_SET_CONFIG_EPOCH;
|
||||||
|
CLUSTER_SETSLOT: CLUSTER_SETSLOT;
|
||||||
|
clusterSetSlot: CLUSTER_SETSLOT;
|
||||||
|
CLUSTER_SLOTS: CLUSTER_SLOTS;
|
||||||
|
clusterSlots: CLUSTER_SLOTS;
|
||||||
COPY: COPY;
|
COPY: COPY;
|
||||||
copy: COPY;
|
copy: COPY;
|
||||||
DBSIZE: DBSIZE;
|
DBSIZE: DBSIZE;
|
||||||
@@ -658,8 +746,8 @@ type Commands = {
|
|||||||
functionList: FUNCTION_LIST;
|
functionList: FUNCTION_LIST;
|
||||||
FUNCTION_LOAD: FUNCTION_LOAD;
|
FUNCTION_LOAD: FUNCTION_LOAD;
|
||||||
functionLoad: FUNCTION_LOAD;
|
functionLoad: FUNCTION_LOAD;
|
||||||
// FUNCTION_RESTORE: FUNCTION_RESTORE;
|
FUNCTION_RESTORE: FUNCTION_RESTORE;
|
||||||
// functionRestore: FUNCTION_RESTORE;
|
functionRestore: FUNCTION_RESTORE;
|
||||||
// FUNCTION_STATS: FUNCTION_STATS;
|
// FUNCTION_STATS: FUNCTION_STATS;
|
||||||
// functionStats: FUNCTION_STATS;
|
// functionStats: FUNCTION_STATS;
|
||||||
GEOADD: GEOADD;
|
GEOADD: GEOADD;
|
||||||
@@ -1119,16 +1207,60 @@ export default {
|
|||||||
clientPause: CLIENT_PAUSE,
|
clientPause: CLIENT_PAUSE,
|
||||||
CLIENT_SETNAME,
|
CLIENT_SETNAME,
|
||||||
clientSetName: CLIENT_SETNAME,
|
clientSetName: CLIENT_SETNAME,
|
||||||
|
CLIENT_TRACKING,
|
||||||
|
clientTracking: CLIENT_TRACKING,
|
||||||
|
CLIENT_TRACKINGINFO,
|
||||||
|
clientTrackingInfo: CLIENT_TRACKINGINFO,
|
||||||
|
CLIENT_UNPAUSE,
|
||||||
|
clientUnpause: CLIENT_UNPAUSE,
|
||||||
CLUSTER_ADDSLOTS,
|
CLUSTER_ADDSLOTS,
|
||||||
clusterAddSlots: CLUSTER_ADDSLOTS,
|
clusterAddSlots: CLUSTER_ADDSLOTS,
|
||||||
CLUSTER_SLOTS,
|
CLUSTER_ADDSLOTSRANGE,
|
||||||
clusterSlots: CLUSTER_SLOTS,
|
clusterAddSlotsRange: CLUSTER_ADDSLOTSRANGE,
|
||||||
|
CLUSTER_BUMPEPOCH,
|
||||||
|
clusterBumpEpoch: CLUSTER_BUMPEPOCH,
|
||||||
|
'CLUSTER_COUNT-FAILURE-REPORTS': CLUSTER_COUNT_FAILURE_REPORTS,
|
||||||
|
clusterCountFailureReports: CLUSTER_COUNT_FAILURE_REPORTS,
|
||||||
|
CLUSTER_COUNTKEYSINSLOT,
|
||||||
|
clusterCountKeysInSlot: CLUSTER_COUNTKEYSINSLOT,
|
||||||
|
CLUSTER_DELSLOTS,
|
||||||
|
clusterDelSlots: CLUSTER_DELSLOTS,
|
||||||
|
CLUSTER_DELSLOTSRANGE,
|
||||||
|
clusterDelSlotsRange: CLUSTER_DELSLOTSRANGE,
|
||||||
|
CLUSTER_FAILOVER,
|
||||||
|
clusterFailover: CLUSTER_FAILOVER,
|
||||||
|
CLUSTER_FLUSHSLOTS,
|
||||||
|
clusterFlushSlots: CLUSTER_FLUSHSLOTS,
|
||||||
|
CLUSTER_FORGET,
|
||||||
|
clusterForget: CLUSTER_FORGET,
|
||||||
|
CLUSTER_GETKEYSINSLOT,
|
||||||
|
clusterGetKeysInSlot: CLUSTER_GETKEYSINSLOT,
|
||||||
|
// CLUSTER_INFO,
|
||||||
|
// clusterInfo: CLUSTER_INFO,
|
||||||
|
CLUSTER_KEYSLOT,
|
||||||
|
clusterKeySlot: CLUSTER_KEYSLOT,
|
||||||
|
// CLUSTER_LINKS,
|
||||||
|
// clusterLinks: CLUSTER_LINKS,
|
||||||
CLUSTER_MEET,
|
CLUSTER_MEET,
|
||||||
clusterMeet: CLUSTER_MEET,
|
clusterMeet: CLUSTER_MEET,
|
||||||
CLUSTER_MYID,
|
CLUSTER_MYID,
|
||||||
clusterMyId: CLUSTER_MYID,
|
clusterMyId: CLUSTER_MYID,
|
||||||
|
// CLUSTER_NODES,
|
||||||
|
// clusterNodes: CLUSTER_NODES,
|
||||||
|
// CLUSTER_REPLICAS,
|
||||||
|
// clusterReplicas: CLUSTER_REPLICAS,
|
||||||
CLUSTER_REPLICATE,
|
CLUSTER_REPLICATE,
|
||||||
clusterReplicate: CLUSTER_REPLICATE,
|
clusterReplicate: CLUSTER_REPLICATE,
|
||||||
|
CLUSTER_RESET,
|
||||||
|
clusterReset: CLUSTER_RESET,
|
||||||
|
CLUSTER_SAVECONFIG,
|
||||||
|
clusterSaveConfig: CLUSTER_SAVECONFIG,
|
||||||
|
'CLUSTER_SET-CONFIG-EPOCH': CLUSTER_SET_CONFIG_EPOCH,
|
||||||
|
clusterSetConfigEpoch: CLUSTER_SET_CONFIG_EPOCH,
|
||||||
|
CLUSTER_SETSLOT,
|
||||||
|
clusterSetSlot: CLUSTER_SETSLOT,
|
||||||
|
CLUSTER_SLOTS,
|
||||||
|
clusterSlots: CLUSTER_SLOTS,
|
||||||
COPY,
|
COPY,
|
||||||
copy: COPY,
|
copy: COPY,
|
||||||
DBSIZE,
|
DBSIZE,
|
||||||
@@ -1563,4 +1695,4 @@ export default {
|
|||||||
zUnion: ZUNION,
|
zUnion: ZUNION,
|
||||||
ZUNIONSTORE,
|
ZUNIONSTORE,
|
||||||
zUnionStore: ZUNIONSTORE
|
zUnionStore: ZUNIONSTORE
|
||||||
} as const satisfies Record<string, Command> as Commands;
|
} satisfies Record<string, Command> as Commands;
|
||||||
|
@@ -4,7 +4,8 @@ import { setTimeout } from 'timers/promises';
|
|||||||
|
|
||||||
const utils = new TestUtils({
|
const utils = new TestUtils({
|
||||||
dockerImageName: 'redis',
|
dockerImageName: 'redis',
|
||||||
dockerImageVersionArgument: 'redis-version'
|
dockerImageVersionArgument: 'redis-version',
|
||||||
|
defaultDockerVersion: '7.2-rc'
|
||||||
});
|
});
|
||||||
|
|
||||||
export default utils;
|
export default utils;
|
||||||
|
@@ -3,12 +3,15 @@ import {
|
|||||||
RedisFunctions,
|
RedisFunctions,
|
||||||
RedisScripts,
|
RedisScripts,
|
||||||
RespVersions,
|
RespVersions,
|
||||||
|
TypeMapping,
|
||||||
|
CommandPolicies,
|
||||||
createClient,
|
createClient,
|
||||||
RedisClientOptions,
|
RedisClientOptions,
|
||||||
RedisClientType,
|
RedisClientType,
|
||||||
createCluster,
|
createCluster,
|
||||||
RedisClusterOptions,
|
RedisClusterOptions,
|
||||||
RedisClusterType
|
RedisClusterType,
|
||||||
|
RESP_TYPES
|
||||||
} from '@redis/client/index';
|
} from '@redis/client/index';
|
||||||
import { RedisServerDockerConfig, spawnRedisServer, spawnRedisCluster } from './dockers';
|
import { RedisServerDockerConfig, spawnRedisServer, spawnRedisCluster } from './dockers';
|
||||||
import yargs from 'yargs';
|
import yargs from 'yargs';
|
||||||
@@ -28,10 +31,11 @@ interface ClientTestOptions<
|
|||||||
M extends RedisModules,
|
M extends RedisModules,
|
||||||
F extends RedisFunctions,
|
F extends RedisFunctions,
|
||||||
S extends RedisScripts,
|
S extends RedisScripts,
|
||||||
RESP extends RespVersions
|
RESP extends RespVersions,
|
||||||
|
TYPE_MAPPING extends TypeMapping
|
||||||
> extends CommonTestOptions {
|
> extends CommonTestOptions {
|
||||||
serverArguments: Array<string>;
|
serverArguments: Array<string>;
|
||||||
clientOptions?: Partial<RedisClientOptions<M, F, S, RESP>>;
|
clientOptions?: Partial<RedisClientOptions<M, F, S, RESP, TYPE_MAPPING>>;
|
||||||
disableClientSetup?: boolean;
|
disableClientSetup?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -39,10 +43,12 @@ interface ClusterTestOptions<
|
|||||||
M extends RedisModules,
|
M extends RedisModules,
|
||||||
F extends RedisFunctions,
|
F extends RedisFunctions,
|
||||||
S extends RedisScripts,
|
S extends RedisScripts,
|
||||||
RESP extends RespVersions
|
RESP extends RespVersions,
|
||||||
|
TYPE_MAPPING extends TypeMapping,
|
||||||
|
POLICIES extends CommandPolicies
|
||||||
> extends CommonTestOptions {
|
> extends CommonTestOptions {
|
||||||
serverArguments: Array<string>;
|
serverArguments: Array<string>;
|
||||||
clusterConfiguration?: Partial<RedisClusterOptions<M, F, S, RESP>>;
|
clusterConfiguration?: Partial<RedisClusterOptions<M, F, S, RESP, TYPE_MAPPING, POLICIES>>;
|
||||||
numberOfMasters?: number;
|
numberOfMasters?: number;
|
||||||
numberOfReplicas?: number;
|
numberOfReplicas?: number;
|
||||||
}
|
}
|
||||||
@@ -51,10 +57,12 @@ interface AllTestOptions<
|
|||||||
M extends RedisModules,
|
M extends RedisModules,
|
||||||
F extends RedisFunctions,
|
F extends RedisFunctions,
|
||||||
S extends RedisScripts,
|
S extends RedisScripts,
|
||||||
RESP extends RespVersions
|
RESP extends RespVersions,
|
||||||
|
TYPE_MAPPING extends TypeMapping,
|
||||||
|
POLICIES extends CommandPolicies
|
||||||
> {
|
> {
|
||||||
client: ClientTestOptions<M, F, S, RESP>;
|
client: ClientTestOptions<M, F, S, RESP, TYPE_MAPPING>;
|
||||||
cluster: ClusterTestOptions<M, F, S, RESP>;
|
cluster: ClusterTestOptions<M, F, S, RESP, TYPE_MAPPING, POLICIES>;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Version {
|
interface Version {
|
||||||
@@ -135,11 +143,12 @@ export default class TestUtils {
|
|||||||
M extends RedisModules = {},
|
M extends RedisModules = {},
|
||||||
F extends RedisFunctions = {},
|
F extends RedisFunctions = {},
|
||||||
S extends RedisScripts = {},
|
S extends RedisScripts = {},
|
||||||
RESP extends RespVersions = 2
|
RESP extends RespVersions = 2,
|
||||||
|
TYPE_MAPPING extends TypeMapping = {}
|
||||||
>(
|
>(
|
||||||
title: string,
|
title: string,
|
||||||
fn: (client: RedisClientType<M, F, S, RESP>) => unknown,
|
fn: (client: RedisClientType<M, F, S, RESP, TYPE_MAPPING>) => unknown,
|
||||||
options: ClientTestOptions<M, F, S, RESP>
|
options: ClientTestOptions<M, F, S, RESP, TYPE_MAPPING>
|
||||||
): void {
|
): void {
|
||||||
let dockerPromise: ReturnType<typeof spawnRedisServer>;
|
let dockerPromise: ReturnType<typeof spawnRedisServer>;
|
||||||
if (this.isVersionGreaterThan(options.minimumDockerVersion)) {
|
if (this.isVersionGreaterThan(options.minimumDockerVersion)) {
|
||||||
@@ -187,8 +196,10 @@ export default class TestUtils {
|
|||||||
M extends RedisModules,
|
M extends RedisModules,
|
||||||
F extends RedisFunctions,
|
F extends RedisFunctions,
|
||||||
S extends RedisScripts,
|
S extends RedisScripts,
|
||||||
RESP extends RespVersions
|
RESP extends RespVersions,
|
||||||
>(cluster: RedisClusterType<M, F, S, RESP>): Promise<unknown> {
|
TYPE_MAPPING extends TypeMapping,
|
||||||
|
POLICIES extends CommandPolicies
|
||||||
|
>(cluster: RedisClusterType<M, F, S, RESP, TYPE_MAPPING, POLICIES>): Promise<unknown> {
|
||||||
return Promise.all(
|
return Promise.all(
|
||||||
cluster.masters.map(async ({ client }) => {
|
cluster.masters.map(async ({ client }) => {
|
||||||
if (client) {
|
if (client) {
|
||||||
@@ -202,11 +213,13 @@ export default class TestUtils {
|
|||||||
M extends RedisModules = {},
|
M extends RedisModules = {},
|
||||||
F extends RedisFunctions = {},
|
F extends RedisFunctions = {},
|
||||||
S extends RedisScripts = {},
|
S extends RedisScripts = {},
|
||||||
RESP extends RespVersions = 2
|
RESP extends RespVersions = 2,
|
||||||
|
TYPE_MAPPING extends TypeMapping = {},
|
||||||
|
POLICIES extends CommandPolicies = {}
|
||||||
>(
|
>(
|
||||||
title: string,
|
title: string,
|
||||||
fn: (cluster: RedisClusterType<M, F, S, RESP>) => unknown,
|
fn: (cluster: RedisClusterType<M, F, S, RESP, TYPE_MAPPING, POLICIES>) => unknown,
|
||||||
options: ClusterTestOptions<M, F, S, RESP>
|
options: ClusterTestOptions<M, F, S, RESP, TYPE_MAPPING, POLICIES>
|
||||||
): void {
|
): void {
|
||||||
let dockersPromise: ReturnType<typeof spawnRedisCluster>;
|
let dockersPromise: ReturnType<typeof spawnRedisCluster>;
|
||||||
if (this.isVersionGreaterThan(options.minimumDockerVersion)) {
|
if (this.isVersionGreaterThan(options.minimumDockerVersion)) {
|
||||||
@@ -225,7 +238,7 @@ export default class TestUtils {
|
|||||||
|
|
||||||
it(title, async function () {
|
it(title, async function () {
|
||||||
if (!dockersPromise) return this.skip();
|
if (!dockersPromise) return this.skip();
|
||||||
|
|
||||||
const dockers = await dockersPromise,
|
const dockers = await dockersPromise,
|
||||||
cluster = createCluster({
|
cluster = createCluster({
|
||||||
rootNodes: dockers.map(({ port }) => ({
|
rootNodes: dockers.map(({ port }) => ({
|
||||||
@@ -253,11 +266,13 @@ export default class TestUtils {
|
|||||||
M extends RedisModules = {},
|
M extends RedisModules = {},
|
||||||
F extends RedisFunctions = {},
|
F extends RedisFunctions = {},
|
||||||
S extends RedisScripts = {},
|
S extends RedisScripts = {},
|
||||||
RESP extends RespVersions = 2
|
RESP extends RespVersions = 2,
|
||||||
|
TYPE_MAPPING extends TypeMapping = {},
|
||||||
|
POLICIES extends CommandPolicies = {}
|
||||||
>(
|
>(
|
||||||
title: string,
|
title: string,
|
||||||
fn: (client: RedisClientType<M, F, S, RESP> | RedisClusterType<M, F, S, RESP>) => unknown,
|
fn: (client: RedisClientType<M, F, S, RESP, TYPE_MAPPING> | RedisClusterType<M, F, S, RESP, TYPE_MAPPING, POLICIES>) => unknown,
|
||||||
options: AllTestOptions<M, F, S, RESP>
|
options: AllTestOptions<M, F, S, RESP, TYPE_MAPPING, POLICIES>
|
||||||
) {
|
) {
|
||||||
this.testWithClient(`client.${title}`, fn, options.client);
|
this.testWithClient(`client.${title}`, fn, options.client);
|
||||||
this.testWithCluster(`cluster.${title}`, fn, options.cluster);
|
this.testWithCluster(`cluster.${title}`, fn, options.cluster);
|
||||||
|
Reference in New Issue
Block a user