1
0
mirror of https://github.com/redis/node-redis.git synced 2025-08-06 02:15:48 +03:00

Update doctest client with latest v4 release (#2844)

This commit is contained in:
Shaya Potter
2024-09-29 13:19:06 +03:00
committed by GitHub
parent fd7b10be6c
commit 49fdb79897
167 changed files with 8227 additions and 9599 deletions

View File

@@ -13,32 +13,22 @@ describe('ACL GETUSER', () => {
});
testUtils.testWithClient('client.aclGetUser', async client => {
const expectedReply: any = {
passwords: [],
commands: '+@all',
};
const reply = await client.aclGetUser('default');
assert.ok(Array.isArray(reply.passwords));
assert.equal(typeof reply.commands, 'string');
assert.ok(Array.isArray(reply.flags));
if (testUtils.isVersionGreaterThan([7])) {
expectedReply.flags = ['on', 'nopass'];
expectedReply.keys = '~*';
expectedReply.channels = '&*';
expectedReply.selectors = [];
assert.equal(typeof reply.keys, 'string');
assert.equal(typeof reply.channels, 'string');
assert.ok(Array.isArray(reply.selectors));
} else {
expectedReply.keys = ['*'];
expectedReply.selectors = undefined;
assert.ok(Array.isArray(reply.keys));
if (testUtils.isVersionGreaterThan([6, 2])) {
expectedReply.flags = ['on', 'allkeys', 'allchannels', 'allcommands', 'nopass'];
expectedReply.channels = ['*'];
} else {
expectedReply.flags = ['on', 'allkeys', 'allcommands', 'nopass'];
expectedReply.channels = undefined;
}
if (testUtils.isVersionGreaterThan([6, 2])) {
assert.ok(Array.isArray(reply.channels));
}
}
assert.deepEqual(
await client.aclGetUser('default'),
expectedReply
);
}, GLOBAL.SERVERS.OPEN);
});

View File

@@ -1,7 +1,10 @@
import { strict as assert } from 'assert';
import { transformArguments, transformReply } from './CLIENT_INFO';
import testUtils, { GLOBAL } from '../test-utils';
describe('CLIENT INFO', () => {
testUtils.isVersionGreaterThanHook([6, 2]);
it('transformArguments', () => {
assert.deepEqual(
transformArguments(),
@@ -9,34 +12,39 @@ describe('CLIENT INFO', () => {
);
});
it('transformReply', () => {
assert.deepEqual(
transformReply('id=526512 addr=127.0.0.1:36244 laddr=127.0.0.1:6379 fd=8 name= age=11213 idle=0 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=26 qbuf-free=40928 argv-mem=10 obl=0 oll=0 omem=0 tot-mem=61466 events=r cmd=client user=default redir=-1\n'),
{
id: 526512,
addr: '127.0.0.1:36244',
laddr: '127.0.0.1:6379',
fd: 8,
name: '',
age: 11213,
idle: 0,
flags: 'N',
db: 0,
sub: 0,
psub: 0,
multi: -1,
qbuf: 26,
qbufFree: 40928,
argvMem: 10,
obl: 0,
oll: 0,
omem: 0,
totMem: 61466,
events: 'r',
cmd: 'client',
user: 'default',
redir: -1
}
);
});
testUtils.testWithClient('client.clientInfo', async client => {
const reply = await client.clientInfo();
assert.equal(typeof reply.id, 'number');
assert.equal(typeof reply.addr, 'string');
assert.equal(typeof reply.laddr, 'string');
assert.equal(typeof reply.fd, 'number');
assert.equal(typeof reply.name, 'string');
assert.equal(typeof reply.age, 'number');
assert.equal(typeof reply.idle, 'number');
assert.equal(typeof reply.flags, 'string');
assert.equal(typeof reply.db, 'number');
assert.equal(typeof reply.sub, 'number');
assert.equal(typeof reply.psub, 'number');
assert.equal(typeof reply.multi, 'number');
assert.equal(typeof reply.qbuf, 'number');
assert.equal(typeof reply.qbufFree, 'number');
assert.equal(typeof reply.argvMem, 'number');
assert.equal(typeof reply.obl, 'number');
assert.equal(typeof reply.oll, 'number');
assert.equal(typeof reply.omem, 'number');
assert.equal(typeof reply.totMem, 'number');
assert.equal(typeof reply.events, 'string');
assert.equal(typeof reply.cmd, 'string');
assert.equal(typeof reply.user, 'string');
assert.equal(typeof reply.redir, 'number');
if (testUtils.isVersionGreaterThan([7, 0])) {
assert.equal(typeof reply.multiMem, 'number');
assert.equal(typeof reply.resp, 'number');
}
if (testUtils.isVersionGreaterThan([7, 0, 3])) {
assert.equal(typeof reply.ssub, 'number');
}
}, GLOBAL.SERVERS.OPEN);
});

View File

@@ -1,11 +1,13 @@
export const IS_READ_ONLY = true;
export function transformArguments(): Array<string> {
return ['CLIENT', 'INFO'];
}
interface ClientInfoReply {
export interface ClientInfoReply {
id: number;
addr: string;
laddr: string;
laddr?: string; // 6.2
fd: number;
name: string;
age: number;
@@ -14,72 +16,79 @@ interface ClientInfoReply {
db: number;
sub: number;
psub: number;
ssub?: number; // 7.0.3
multi: number;
qbuf: number;
qbufFree: number;
argvMem: number;
argvMem?: number; // 6.0
multiMem?: number; // 7.0
obl: number;
oll: number;
omem: number;
totMem: number;
totMem?: number; // 6.0
events: string;
cmd: string;
user: string;
redir: number;
user?: string; // 6.0
redir?: number; // 6.2
resp?: number; // 7.0
// 7.2
libName?: string;
libVer?: string;
}
const REGEX = /=([^\s]*)/g;
const CLIENT_INFO_REGEX = /([^\s=]+)=([^\s]*)/g;
export function transformReply(reply: string): ClientInfoReply {
const [
[, id],
[, addr],
[, laddr],
[, fd],
[, name],
[, age],
[, idle],
[, flags],
[, db],
[, sub],
[, psub],
[, multi],
[, qbuf],
[, qbufFree],
[, argvMem],
[, obl],
[, oll],
[, omem],
[, totMem],
[, events],
[, cmd],
[, user],
[, redir]
] = [...reply.matchAll(REGEX)];
export function transformReply(rawReply: string): ClientInfoReply {
const map: Record<string, string> = {};
for (const item of rawReply.matchAll(CLIENT_INFO_REGEX)) {
map[item[1]] = item[2];
}
return {
id: Number(id),
addr,
laddr,
fd: Number(fd),
name,
age: Number(age),
idle: Number(idle),
flags,
db: Number(db),
sub: Number(sub),
psub: Number(psub),
multi: Number(multi),
qbuf: Number(qbuf),
qbufFree: Number(qbufFree),
argvMem: Number(argvMem),
obl: Number(obl),
oll: Number(oll),
omem: Number(omem),
totMem: Number(totMem),
events,
cmd,
user,
redir: Number(redir)
const reply: ClientInfoReply = {
id: Number(map.id),
addr: map.addr,
fd: Number(map.fd),
name: map.name,
age: Number(map.age),
idle: Number(map.idle),
flags: map.flags,
db: Number(map.db),
sub: Number(map.sub),
psub: Number(map.psub),
multi: Number(map.multi),
qbuf: Number(map.qbuf),
qbufFree: Number(map['qbuf-free']),
argvMem: Number(map['argv-mem']),
obl: Number(map.obl),
oll: Number(map.oll),
omem: Number(map.omem),
totMem: Number(map['tot-mem']),
events: map.events,
cmd: map.cmd,
user: map.user,
libName: map['lib-name'],
libVer: map['lib-ver'],
};
if (map.laddr !== undefined) {
reply.laddr = map.laddr;
}
if (map.redir !== undefined) {
reply.redir = Number(map.redir);
}
if (map.ssub !== undefined) {
reply.ssub = Number(map.ssub);
}
if (map['multi-mem'] !== undefined) {
reply.multiMem = Number(map['multi-mem']);
}
if (map.resp !== undefined) {
reply.resp = Number(map.resp);
}
return reply;
}

View File

@@ -65,6 +65,16 @@ describe('CLIENT KILL', () => {
);
});
it('MAXAGE', () => {
assert.deepEqual(
transformArguments({
filter: ClientKillFilters.MAXAGE,
maxAge: 10
}),
['CLIENT', 'KILL', 'MAXAGE', '10']
);
});
describe('SKIP_ME', () => {
it('undefined', () => {
assert.deepEqual(

View File

@@ -6,7 +6,8 @@ export enum ClientKillFilters {
ID = 'ID',
TYPE = 'TYPE',
USER = 'USER',
SKIP_ME = 'SKIPME'
SKIP_ME = 'SKIPME',
MAXAGE = 'MAXAGE'
}
interface KillFilter<T extends ClientKillFilters> {
@@ -37,7 +38,11 @@ type KillSkipMe = ClientKillFilters.SKIP_ME | (KillFilter<ClientKillFilters.SKIP
skipMe: boolean;
});
type KillFilters = KillAddress | KillLocalAddress | KillId | KillType | KillUser | KillSkipMe;
interface KillMaxAge extends KillFilter<ClientKillFilters.MAXAGE> {
maxAge: number;
}
type KillFilters = KillAddress | KillLocalAddress | KillId | KillType | KillUser | KillSkipMe | KillMaxAge;
export function transformArguments(filters: KillFilters | Array<KillFilters>): RedisCommandArguments {
const args = ['CLIENT', 'KILL'];
@@ -89,6 +94,10 @@ function pushFilter(args: RedisCommandArguments, filter: KillFilters): void {
case ClientKillFilters.SKIP_ME:
args.push(filter.skipMe ? 'yes' : 'no');
break;
case ClientKillFilters.MAXAGE:
args.push(filter.maxAge.toString());
break;
}
}

View File

@@ -0,0 +1,78 @@
import { strict as assert } from 'assert';
import { transformArguments, transformReply } from './CLIENT_LIST';
import testUtils, { GLOBAL } from '../test-utils';
describe('CLIENT LIST', () => {
describe('transformArguments', () => {
it('simple', () => {
assert.deepEqual(
transformArguments(),
['CLIENT', 'LIST']
);
});
it('with TYPE', () => {
assert.deepEqual(
transformArguments({
TYPE: 'NORMAL'
}),
['CLIENT', 'LIST', 'TYPE', 'NORMAL']
);
});
it('with ID', () => {
assert.deepEqual(
transformArguments({
ID: ['1', '2']
}),
['CLIENT', 'LIST', 'ID', '1', '2']
);
});
});
testUtils.testWithClient('client.clientList', async client => {
const reply = await client.clientList();
assert.ok(Array.isArray(reply));
for (const item of reply) {
assert.equal(typeof item.id, 'number');
assert.equal(typeof item.addr, 'string');
assert.equal(typeof item.fd, 'number');
assert.equal(typeof item.name, 'string');
assert.equal(typeof item.age, 'number');
assert.equal(typeof item.idle, 'number');
assert.equal(typeof item.flags, 'string');
assert.equal(typeof item.db, 'number');
assert.equal(typeof item.sub, 'number');
assert.equal(typeof item.psub, 'number');
assert.equal(typeof item.multi, 'number');
assert.equal(typeof item.qbuf, 'number');
assert.equal(typeof item.qbufFree, 'number');
assert.equal(typeof item.obl, 'number');
assert.equal(typeof item.oll, 'number');
assert.equal(typeof item.omem, 'number');
assert.equal(typeof item.events, 'string');
assert.equal(typeof item.cmd, 'string');
if (testUtils.isVersionGreaterThan([6, 0])) {
assert.equal(typeof item.argvMem, 'number');
assert.equal(typeof item.totMem, 'number');
assert.equal(typeof item.user, 'string');
}
if (testUtils.isVersionGreaterThan([6, 2])) {
assert.equal(typeof item.redir, 'number');
assert.equal(typeof item.laddr, 'string');
}
if (testUtils.isVersionGreaterThan([7, 0])) {
assert.equal(typeof item.multiMem, 'number');
assert.equal(typeof item.resp, 'number');
}
if (testUtils.isVersionGreaterThan([7, 0, 3])) {
assert.equal(typeof item.ssub, 'number');
}
}
}, GLOBAL.SERVERS.OPEN);
});

View File

@@ -0,0 +1,43 @@
import { RedisCommandArguments, RedisCommandArgument } from '.';
import { pushVerdictArguments } from './generic-transformers';
import { transformReply as transformClientInfoReply, ClientInfoReply } from './CLIENT_INFO';
interface ListFilterType {
TYPE: 'NORMAL' | 'MASTER' | 'REPLICA' | 'PUBSUB';
ID?: never;
}
interface ListFilterId {
ID: Array<RedisCommandArgument>;
TYPE?: never;
}
export type ListFilter = ListFilterType | ListFilterId;
export const IS_READ_ONLY = true;
export function transformArguments(filter?: ListFilter): RedisCommandArguments {
let args: RedisCommandArguments = ['CLIENT', 'LIST'];
if (filter) {
if (filter.TYPE !== undefined) {
args.push('TYPE', filter.TYPE);
} else {
args.push('ID');
args = pushVerdictArguments(args, filter.ID);
}
}
return args;
}
export function transformReply(rawReply: string): Array<ClientInfoReply> {
const split = rawReply.split('\n'),
length = split.length - 1,
reply: Array<ClientInfoReply> = [];
for (let i = 0; i < length; i++) {
reply.push(transformClientInfoReply(split[i]));
}
return reply;
}

View File

@@ -0,0 +1,30 @@
import { strict as assert } from 'assert';
import testUtils, { GLOBAL } from '../test-utils';
import { transformArguments } from './CLIENT_NO-TOUCH';
describe('CLIENT NO-TOUCH', () => {
testUtils.isVersionGreaterThanHook([7, 2]);
describe('transformArguments', () => {
it('true', () => {
assert.deepEqual(
transformArguments(true),
['CLIENT', 'NO-TOUCH', 'ON']
);
});
it('false', () => {
assert.deepEqual(
transformArguments(false),
['CLIENT', 'NO-TOUCH', 'OFF']
);
});
});
testUtils.testWithClient('client.clientNoTouch', async client => {
assert.equal(
await client.clientNoTouch(true),
'OK'
);
}, GLOBAL.SERVERS.OPEN);
});

View File

@@ -0,0 +1,11 @@
import { RedisCommandArguments } from '.';
export function transformArguments(value: boolean): RedisCommandArguments {
return [
'CLIENT',
'NO-TOUCH',
value ? 'ON' : 'OFF'
];
}
export declare function transformReply(): 'OK' | Buffer;

View File

@@ -11,8 +11,9 @@ describe('CLUSTER BUMPEPOCH', () => {
});
testUtils.testWithCluster('clusterNode.clusterBumpEpoch', async cluster => {
const client = await cluster.nodeClient(cluster.masters[0]);
assert.equal(
typeof await cluster.getSlotMaster(0).client.clusterBumpEpoch(),
typeof await client.clusterBumpEpoch(),
'string'
);
}, GLOBAL.SERVERS.OPEN);

View File

@@ -11,7 +11,7 @@ describe('CLUSTER COUNT-FAILURE-REPORTS', () => {
});
testUtils.testWithCluster('clusterNode.clusterCountFailureReports', async cluster => {
const { client } = cluster.getSlotMaster(0);
const client = await cluster.nodeClient(cluster.masters[0]);
assert.equal(
typeof await client.clusterCountFailureReports(
await client.clusterMyId()

View File

@@ -11,8 +11,9 @@ describe('CLUSTER COUNTKEYSINSLOT', () => {
});
testUtils.testWithCluster('clusterNode.clusterCountKeysInSlot', async cluster => {
const client = await cluster.nodeClient(cluster.masters[0]);
assert.equal(
typeof await cluster.getSlotMaster(0).client.clusterCountKeysInSlot(0),
typeof await client.clusterCountKeysInSlot(0),
'number'
);
}, GLOBAL.CLUSTERS.OPEN);

View File

@@ -11,7 +11,8 @@ describe('CLUSTER GETKEYSINSLOT', () => {
});
testUtils.testWithCluster('clusterNode.clusterGetKeysInSlot', async cluster => {
const reply = await cluster.getSlotMaster(0).client.clusterGetKeysInSlot(0, 1);
const client = await cluster.nodeClient(cluster.masters[0]),
reply = await client.clusterGetKeysInSlot(0, 1);
assert.ok(Array.isArray(reply));
for (const item of reply) {
assert.equal(typeof item, 'string');

View File

@@ -46,8 +46,9 @@ describe('CLUSTER INFO', () => {
});
testUtils.testWithCluster('clusterNode.clusterInfo', async cluster => {
const client = await cluster.nodeClient(cluster.masters[0]);
assert.notEqual(
await cluster.getSlotMaster(0).client.clusterInfo(),
await client.clusterInfo(),
null
);
}, GLOBAL.CLUSTERS.OPEN);

View File

@@ -11,8 +11,9 @@ describe('CLUSTER KEYSLOT', () => {
});
testUtils.testWithCluster('clusterNode.clusterKeySlot', async cluster => {
const client = await cluster.nodeClient(cluster.masters[0]);
assert.equal(
typeof await cluster.getSlotMaster(0).client.clusterKeySlot('key'),
typeof await client.clusterKeySlot('key'),
'number'
);
}, GLOBAL.CLUSTERS.OPEN);

View File

@@ -13,7 +13,8 @@ describe('CLUSTER LINKS', () => {
});
testUtils.testWithCluster('clusterNode.clusterLinks', async cluster => {
const links = await cluster.getSlotMaster(0).client.clusterLinks();
const client = await cluster.nodeClient(cluster.masters[0]),
links = await client.clusterLinks();
assert.ok(Array.isArray(links));
for (const link of links) {
assert.equal(typeof link.direction, 'string');

View File

@@ -11,9 +11,11 @@ describe('CLUSTER MYID', () => {
});
testUtils.testWithCluster('clusterNode.clusterMyId', async cluster => {
const [master] = cluster.masters,
client = await cluster.nodeClient(master);
assert.equal(
typeof await cluster.getSlotMaster(0).client.clusterMyId(),
'string'
await client.clusterMyId(),
master.id
);
}, GLOBAL.CLUSTERS.OPEN);
});

View File

@@ -0,0 +1,22 @@
import { strict as assert } from 'assert';
import testUtils, { GLOBAL } from '../test-utils';
import { transformArguments } from './CLUSTER_MYSHARDID';
describe('CLUSTER MYSHARDID', () => {
testUtils.isVersionGreaterThanHook([7, 2]);
it('transformArguments', () => {
assert.deepEqual(
transformArguments(),
['CLUSTER', 'MYSHARDID']
);
});
testUtils.testWithCluster('clusterNode.clusterMyShardId', async cluster => {
const client = await cluster.nodeClient(cluster.masters[0]);
assert.equal(
typeof await client.clusterMyShardId(),
'string'
);
}, GLOBAL.CLUSTERS.OPEN);
});

View File

@@ -0,0 +1,7 @@
export const IS_READ_ONLY = true;
export function transformArguments() {
return ['CLUSTER', 'MYSHARDID'];
}
export declare function transformReply(): string | Buffer;

View File

@@ -11,8 +11,9 @@ describe('CLUSTER SAVECONFIG', () => {
});
testUtils.testWithCluster('clusterNode.clusterSaveConfig', async cluster => {
const client = await cluster.nodeClient(cluster.masters[0]);
assert.equal(
await cluster.getSlotMaster(0).client.clusterSaveConfig(),
await client.clusterSaveConfig(),
'OK'
);
}, GLOBAL.CLUSTERS.OPEN);

View File

@@ -13,7 +13,7 @@ type ClusterSlotsRawReply = Array<[
...replicas: Array<ClusterSlotsRawNode>
]>;
type ClusterSlotsNode = {
export interface ClusterSlotsNode {
ip: string;
port: number;
id: string;

View File

@@ -0,0 +1,40 @@
import { strict as assert } from 'node:assert';
import testUtils, { GLOBAL } from '../test-utils';
import { transformArguments } from './HEXPIRE';
import { HASH_EXPIRATION_TIME } from './HEXPIRETIME';
describe('HEXPIRE', () => {
testUtils.isVersionGreaterThanHook([7, 4]);
describe('transformArguments', () => {
it('string', () => {
assert.deepEqual(
transformArguments('key', 'field', 1),
['HEXPIRE', 'key', '1', 'FIELDS', '1', 'field']
);
});
it('array', () => {
assert.deepEqual(
transformArguments('key', ['field1', 'field2'], 1),
['HEXPIRE', 'key', '1', 'FIELDS', '2', 'field1', 'field2']
);
});
it('with set option', () => {
assert.deepEqual(
transformArguments('key', ['field1'], 1, 'NX'),
['HEXPIRE', 'key', '1', 'NX', 'FIELDS', '1', 'field1']
);
});
});
testUtils.testWithClient('hexpire', async client => {
assert.deepEqual(
await client.hExpire('key', ['field1'], 0),
[HASH_EXPIRATION_TIME.FIELD_NOT_EXISTS]
);
}, {
...GLOBAL.SERVERS.OPEN
});
});

View File

@@ -0,0 +1,44 @@
import { RedisCommandArgument } from '.';
import { pushVerdictArgument } from './generic-transformers';
/**
* @readonly
* @enum {number}
*/
export const HASH_EXPIRATION = {
/** @property {number} */
/** The field does not exist */
FIELD_NOT_EXISTS: -2,
/** @property {number} */
/** Specified NX | XX | GT | LT condition not met */
CONDITION_NOT_MET: 0,
/** @property {number} */
/** Expiration time was set or updated */
UPDATED: 1,
/** @property {number} */
/** Field deleted because the specified expiration time is in the past */
DELETED: 2
} as const;
export type HashExpiration = typeof HASH_EXPIRATION[keyof typeof HASH_EXPIRATION];
export const FIRST_KEY_INDEX = 1;
export function transformArguments(
key: RedisCommandArgument,
fields: RedisCommandArgument| Array<RedisCommandArgument>,
seconds: number,
mode?: 'NX' | 'XX' | 'GT' | 'LT',
) {
const args = ['HEXPIRE', key, seconds.toString()];
if (mode) {
args.push(mode);
}
args.push('FIELDS');
return pushVerdictArgument(args, fields);
}
export declare function transformReply(): Array<HashExpiration>;

View File

@@ -0,0 +1,49 @@
import { strict as assert } from 'node:assert';
import testUtils, { GLOBAL } from '../test-utils';
import { transformArguments } from './HEXPIREAT';
import { HASH_EXPIRATION_TIME } from './HEXPIRETIME';
describe('HEXPIREAT', () => {
testUtils.isVersionGreaterThanHook([7, 4]);
describe('transformArguments', () => {
it('string + number', () => {
assert.deepEqual(
transformArguments('key', 'field', 1),
['HEXPIREAT', 'key', '1', 'FIELDS', '1', 'field']
);
});
it('array + number', () => {
assert.deepEqual(
transformArguments('key', ['field1', 'field2'], 1),
['HEXPIREAT', 'key', '1', 'FIELDS', '2', 'field1', 'field2']
);
});
it('date', () => {
const d = new Date();
assert.deepEqual(
transformArguments('key', ['field1'], d),
['HEXPIREAT', 'key', Math.floor(d.getTime() / 1000).toString(), 'FIELDS', '1', 'field1']
);
});
it('with set option', () => {
assert.deepEqual(
transformArguments('key', 'field1', 1, 'GT'),
['HEXPIREAT', 'key', '1', 'GT', 'FIELDS', '1', 'field1']
);
});
});
testUtils.testWithClient('expireAt', async client => {
assert.deepEqual(
await client.hExpireAt('key', 'field1', 1),
[HASH_EXPIRATION_TIME.FIELD_NOT_EXISTS]
);
}, {
...GLOBAL.SERVERS.OPEN,
});
});

View File

@@ -0,0 +1,28 @@
import { RedisCommandArgument } from '.';
import { pushVerdictArgument, transformEXAT } from './generic-transformers';
import { HashExpiration } from './HEXPIRE';
export const FIRST_KEY_INDEX = 1;
export function transformArguments(
key: RedisCommandArgument,
fields: RedisCommandArgument | Array<RedisCommandArgument>,
timestamp: number | Date,
mode?: 'NX' | 'XX' | 'GT' | 'LT'
) {
const args = [
'HEXPIREAT',
key,
transformEXAT(timestamp)
];
if (mode) {
args.push(mode);
}
args.push('FIELDS')
return pushVerdictArgument(args, fields);
}
export declare function transformReply(): Array<HashExpiration>;

View File

@@ -0,0 +1,32 @@
import { strict as assert } from 'node:assert';
import testUtils, { GLOBAL } from '../test-utils';
import { HASH_EXPIRATION_TIME, transformArguments } from './HEXPIRETIME';
describe('HEXPIRETIME', () => {
testUtils.isVersionGreaterThanHook([7, 4]);
describe('transformArguments', () => {
it('string', () => {
assert.deepEqual(
transformArguments('key', 'field'),
['HEXPIRETIME', 'key', 'FIELDS', '1', 'field']
);
});
it('array', () => {
assert.deepEqual(
transformArguments('key', ['field1', 'field2']),
['HEXPIRETIME', 'key', 'FIELDS', '2', 'field1', 'field2']
);
});
})
testUtils.testWithClient('hExpireTime', async client => {
assert.deepEqual(
await client.hExpireTime('key', 'field1'),
[HASH_EXPIRATION_TIME.FIELD_NOT_EXISTS]
);
}, {
...GLOBAL.SERVERS.OPEN,
});
});

View File

@@ -0,0 +1,21 @@
import { RedisCommandArgument } from '.';
import { pushVerdictArgument } from './generic-transformers';
export const HASH_EXPIRATION_TIME = {
/** @property {number} */
/** The field does not exist */
FIELD_NOT_EXISTS: -2,
/** @property {number} */
/** The field exists but has no associated expire */
NO_EXPIRATION: -1,
} as const;
export const FIRST_KEY_INDEX = 1
export const IS_READ_ONLY = true;
export function transformArguments(key: RedisCommandArgument, fields: RedisCommandArgument | Array<RedisCommandArgument>) {
return pushVerdictArgument(['HEXPIRETIME', key, 'FIELDS'], fields);
}
export declare function transformReply(): Array<number>;

View File

@@ -0,0 +1,33 @@
import { strict as assert } from 'node:assert';
import testUtils, { GLOBAL } from '../test-utils';
import { transformArguments } from './HPERSIST';
import { HASH_EXPIRATION_TIME } from './HEXPIRETIME';
describe('HPERSIST', () => {
testUtils.isVersionGreaterThanHook([7, 4]);
describe('transformArguments', () => {
it('string', () => {
assert.deepEqual(
transformArguments('key', 'field'),
['HPERSIST', 'key', 'FIELDS', '1', 'field']
);
});
it('array', () => {
assert.deepEqual(
transformArguments('key', ['field1', 'field2']),
['HPERSIST', 'key', 'FIELDS', '2', 'field1', 'field2']
);
});
})
testUtils.testWithClient('hPersist', async client => {
assert.deepEqual(
await client.hPersist('key', 'field1'),
[HASH_EXPIRATION_TIME.FIELD_NOT_EXISTS]
);
}, {
...GLOBAL.SERVERS.OPEN,
});
});

View File

@@ -0,0 +1,10 @@
import { RedisCommandArgument } from '.';
import { pushVerdictArgument } from './generic-transformers';
export const FIRST_KEY_INDEX = 1;
export function transformArguments(key: RedisCommandArgument, fields: RedisCommandArgument | Array<RedisCommandArgument>) {
return pushVerdictArgument(['HPERSIST', key, 'FIELDS'], fields);
}
export declare function transformReply(): Array<number> | null;

View File

@@ -0,0 +1,40 @@
import { strict as assert } from 'node:assert';
import testUtils, { GLOBAL } from '../test-utils';
import { transformArguments } from './HPEXPIRE';
import { HASH_EXPIRATION_TIME } from './HEXPIRETIME';
describe('HEXPIRE', () => {
testUtils.isVersionGreaterThanHook([7, 4]);
describe('transformArguments', () => {
it('string', () => {
assert.deepEqual(
transformArguments('key', 'field', 1),
['HPEXPIRE', 'key', '1', 'FIELDS', '1', 'field']
);
});
it('array', () => {
assert.deepEqual(
transformArguments('key', ['field1', 'field2'], 1),
['HPEXPIRE', 'key', '1', 'FIELDS', '2', 'field1', 'field2']
);
});
it('with set option', () => {
assert.deepEqual(
transformArguments('key', ['field1'], 1, 'NX'),
['HPEXPIRE', 'key', '1', 'NX', 'FIELDS', '1', 'field1']
);
});
});
testUtils.testWithClient('hexpire', async client => {
assert.deepEqual(
await client.hpExpire('key', ['field1'], 0),
[HASH_EXPIRATION_TIME.FIELD_NOT_EXISTS]
);
}, {
...GLOBAL.SERVERS.OPEN
});
});

View File

@@ -0,0 +1,24 @@
import { RedisCommandArgument } from '.';
import { pushVerdictArgument } from './generic-transformers';
import { HashExpiration } from "./HEXPIRE";
export const FIRST_KEY_INDEX = 1;
export function transformArguments(
key: RedisCommandArgument,
fields: RedisCommandArgument | Array<RedisCommandArgument>,
ms: number,
mode?: 'NX' | 'XX' | 'GT' | 'LT',
) {
const args = ['HPEXPIRE', key, ms.toString()];
if (mode) {
args.push(mode);
}
args.push('FIELDS')
return pushVerdictArgument(args, fields);
}
export declare function transformReply(): Array<HashExpiration> | null;

View File

@@ -0,0 +1,48 @@
import { strict as assert } from 'node:assert';
import testUtils, { GLOBAL } from '../test-utils';
import { transformArguments } from './HPEXPIREAT';
import { HASH_EXPIRATION_TIME } from './HEXPIRETIME';
describe('HPEXPIREAT', () => {
testUtils.isVersionGreaterThanHook([7, 4]);
describe('transformArguments', () => {
it('string + number', () => {
assert.deepEqual(
transformArguments('key', 'field', 1),
['HPEXPIREAT', 'key', '1', 'FIELDS', '1', 'field']
);
});
it('array + number', () => {
assert.deepEqual(
transformArguments('key', ['field1', 'field2'], 1),
['HPEXPIREAT', 'key', '1', 'FIELDS', '2', 'field1', 'field2']
);
});
it('date', () => {
const d = new Date();
assert.deepEqual(
transformArguments('key', ['field1'], d),
['HPEXPIREAT', 'key', d.getTime().toString(), 'FIELDS', '1', 'field1']
);
});
it('with set option', () => {
assert.deepEqual(
transformArguments('key', ['field1'], 1, 'XX'),
['HPEXPIREAT', 'key', '1', 'XX', 'FIELDS', '1', 'field1']
);
});
});
testUtils.testWithClient('hpExpireAt', async client => {
assert.deepEqual(
await client.hpExpireAt('key', ['field1'], 1),
[HASH_EXPIRATION_TIME.FIELD_NOT_EXISTS]
);
}, {
...GLOBAL.SERVERS.OPEN,
});
});

View File

@@ -0,0 +1,25 @@
import { RedisCommandArgument } from '.';
import { pushVerdictArgument, transformEXAT, transformPXAT } from './generic-transformers';
import { HashExpiration } from './HEXPIRE';
export const FIRST_KEY_INDEX = 1;
export const IS_READ_ONLY = true;
export function transformArguments(
key: RedisCommandArgument,
fields: RedisCommandArgument | Array<RedisCommandArgument>,
timestamp: number | Date,
mode?: 'NX' | 'XX' | 'GT' | 'LT'
) {
const args = ['HPEXPIREAT', key, transformPXAT(timestamp)];
if (mode) {
args.push(mode);
}
args.push('FIELDS')
return pushVerdictArgument(args, fields);
}
export declare function transformReply(): Array<HashExpiration> | null;

View File

@@ -0,0 +1,33 @@
import { strict as assert } from 'node:assert';
import testUtils, { GLOBAL } from '../test-utils';
import { transformArguments } from './HPEXPIRETIME';
import { HASH_EXPIRATION_TIME } from './HEXPIRETIME';
describe('HPEXPIRETIME', () => {
testUtils.isVersionGreaterThanHook([7, 4]);
describe('transformArguments', () => {
it('string', () => {
assert.deepEqual(
transformArguments('key', 'field'),
['HPEXPIRETIME', 'key', 'FIELDS', '1', 'field']
);
});
it('array', () => {
assert.deepEqual(
transformArguments('key', ['field1', 'field2']),
['HPEXPIRETIME', 'key', 'FIELDS', '2', 'field1', 'field2']
);
});
});
testUtils.testWithClient('hpExpireTime', async client => {
assert.deepEqual(
await client.hpExpireTime('key', 'field1'),
[HASH_EXPIRATION_TIME.FIELD_NOT_EXISTS]
);
}, {
...GLOBAL.SERVERS.OPEN
});
});

View File

@@ -0,0 +1,11 @@
import { RedisCommandArgument } from '.';
import { pushVerdictArgument } from './generic-transformers';
export const FIRST_KEY_INDEX = 1;
export const IS_READ_ONLY = true;
export function transformArguments(key: RedisCommandArgument, fields: RedisCommandArgument | Array<RedisCommandArgument>) {
return pushVerdictArgument(['HPEXPIRETIME', key, 'FIELDS'], fields);
}
export declare function transformReply(): Array<number> | null;

View File

@@ -0,0 +1,33 @@
import { strict as assert } from 'node:assert';
import testUtils, { GLOBAL } from '../test-utils';
import { transformArguments } from './HPTTL';
import { HASH_EXPIRATION_TIME } from './HEXPIRETIME';
describe('HPTTL', () => {
testUtils.isVersionGreaterThanHook([7, 4]);
describe('transformArguments', () => {
it('string', () => {
assert.deepEqual(
transformArguments('key', 'field'),
['HPTTL', 'key', 'FIELDS', '1', 'field']
);
});
it('array', () => {
assert.deepEqual(
transformArguments('key', ['field1', 'field2']),
['HPTTL', 'key', 'FIELDS', '2', 'field1', 'field2']
);
});
});
testUtils.testWithClient('hpTTL', async client => {
assert.deepEqual(
await client.hpTTL('key', 'field1'),
[HASH_EXPIRATION_TIME.FIELD_NOT_EXISTS]
);
}, {
...GLOBAL.SERVERS.OPEN
});
});

View File

@@ -0,0 +1,11 @@
import { RedisCommandArgument } from '.';
import { pushVerdictArgument } from './generic-transformers';
export const FIRST_KEY_INDEX = 1;
export const IS_READ_ONLY = true;
export function transformArguments(key: RedisCommandArgument, fields: RedisCommandArgument | Array<RedisCommandArgument>) {
return pushVerdictArgument(['HPTTL', key, 'FIELDS'], fields);
}
export declare function transformReply(): Array<number> | null;

View File

@@ -73,5 +73,18 @@ describe('HSCAN', () => {
tuples: []
}
);
await Promise.all([
client.hSet('key', 'a', '1'),
client.hSet('key', 'b', '2')
]);
assert.deepEqual(
await client.hScan('key', 0),
{
cursor: 0,
tuples: [{field: 'a', value: '1'}, {field: 'b', value: '2'}]
}
);
}, GLOBAL.SERVERS.OPEN);
});

View File

@@ -16,7 +16,7 @@ export function transformArguments(
], cursor, options);
}
type HScanRawReply = [RedisCommandArgument, Array<RedisCommandArgument>];
export type HScanRawReply = [RedisCommandArgument, Array<RedisCommandArgument>];
export interface HScanTuple {
field: RedisCommandArgument;

View File

@@ -0,0 +1,79 @@
import { strict as assert } from 'assert';
import testUtils, { GLOBAL } from '../test-utils';
import { transformArguments, transformReply } from './HSCAN_NOVALUES';
describe('HSCAN_NOVALUES', () => {
testUtils.isVersionGreaterThanHook([7, 4]);
describe('transformArguments', () => {
it('cusror only', () => {
assert.deepEqual(
transformArguments('key', 0),
['HSCAN', 'key', '0', 'NOVALUES']
);
});
it('with MATCH', () => {
assert.deepEqual(
transformArguments('key', 0, {
MATCH: 'pattern'
}),
['HSCAN', 'key', '0', 'MATCH', 'pattern', 'NOVALUES']
);
});
it('with COUNT', () => {
assert.deepEqual(
transformArguments('key', 0, {
COUNT: 1
}),
['HSCAN', 'key', '0', 'COUNT', '1', 'NOVALUES']
);
});
});
describe('transformReply', () => {
it('without keys', () => {
assert.deepEqual(
transformReply(['0', []]),
{
cursor: 0,
keys: []
}
);
});
it('with keys', () => {
assert.deepEqual(
transformReply(['0', ['key1', 'key2']]),
{
cursor: 0,
keys: ['key1', 'key2']
}
);
});
});
testUtils.testWithClient('client.hScanNoValues', async client => {
assert.deepEqual(
await client.hScanNoValues('key', 0),
{
cursor: 0,
keys: []
}
);
await Promise.all([
client.hSet('key', 'a', '1'),
client.hSet('key', 'b', '2')
]);
assert.deepEqual(
await client.hScanNoValues('key', 0),
{
cursor: 0,
keys: ['a', 'b']
}
);
}, GLOBAL.SERVERS.OPEN);
});

View File

@@ -0,0 +1,27 @@
import { RedisCommandArgument, RedisCommandArguments } from '.';
import { ScanOptions } from './generic-transformers';
import { HScanRawReply, transformArguments as transformHScanArguments } from './HSCAN';
export { FIRST_KEY_INDEX, IS_READ_ONLY } from './HSCAN';
export function transformArguments(
key: RedisCommandArgument,
cursor: number,
options?: ScanOptions
): RedisCommandArguments {
const args = transformHScanArguments(key, cursor, options);
args.push('NOVALUES');
return args;
}
interface HScanNoValuesReply {
cursor: number;
keys: Array<RedisCommandArgument>;
}
export function transformReply([cursor, rawData]: HScanRawReply): HScanNoValuesReply {
return {
cursor: Number(cursor),
keys: rawData
};
}

View File

@@ -0,0 +1,34 @@
import { strict as assert } from 'node:assert';
import testUtils, { GLOBAL } from '../test-utils';
import { transformArguments } from './HTTL';
import { HASH_EXPIRATION_TIME } from './HEXPIRETIME';
describe('HTTL', () => {
testUtils.isVersionGreaterThanHook([7, 4]);
describe('transformArguments', () => {
it('string', () => {
assert.deepEqual(
transformArguments('key', 'field'),
['HTTL', 'key', 'FIELDS', '1', 'field']
);
});
it('array', () => {
assert.deepEqual(
transformArguments('key', ['field1', 'field2']),
['HTTL', 'key', 'FIELDS', '2', 'field1', 'field2']
);
});
});
testUtils.testWithClient('hTTL', async client => {
assert.deepEqual(
await client.hTTL('key', 'field1'),
[HASH_EXPIRATION_TIME.FIELD_NOT_EXISTS]
);
}, {
...GLOBAL.SERVERS.OPEN
});
});

View File

@@ -0,0 +1,11 @@
import { RedisCommandArgument } from '.';
import { pushVerdictArgument } from './generic-transformers';
export const FIRST_KEY_INDEX = 1;
export const IS_READ_ONLY = true;
export function transformArguments(key: RedisCommandArgument, fields: RedisCommandArgument | Array<RedisCommandArgument>) {
return pushVerdictArgument(['HTTL', key, 'FIELDS'], fields);
}
export declare function transformReply(): Array<number> | null;

View File

@@ -24,9 +24,5 @@ describe('LATENCY GRAPH', () => {
typeof await client.latencyGraph('command'),
'string'
);
}, {
serverArguments: testUtils.isVersionGreaterThan([7]) ?
['--enable-debug-command', 'yes'] :
GLOBAL.SERVERS.OPEN.serverArguments
});
}, GLOBAL.SERVERS.OPEN);
});

View File

@@ -0,0 +1,26 @@
import {strict as assert} from 'assert';
import testUtils, {GLOBAL} from '../test-utils';
import { transformArguments } from './LATENCY_HISTORY';
describe('LATENCY HISTORY', () => {
it('transformArguments', () => {
assert.deepEqual(
transformArguments('command'),
['LATENCY', 'HISTORY', 'command']
);
});
testUtils.testWithClient('client.latencyHistory', async client => {
await Promise.all([
client.configSet('latency-monitor-threshold', '100'),
client.sendCommand(['DEBUG', 'SLEEP', '1'])
]);
const latencyHisRes = await client.latencyHistory('command');
assert.ok(Array.isArray(latencyHisRes));
for (const [timestamp, latency] of latencyHisRes) {
assert.equal(typeof timestamp, 'number');
assert.equal(typeof latency, 'number');
}
}, GLOBAL.SERVERS.OPEN);
});

View File

@@ -0,0 +1,27 @@
export type EventType = (
'active-defrag-cycle' |
'aof-fsync-always' |
'aof-stat' |
'aof-rewrite-diff-write' |
'aof-rename' |
'aof-write' |
'aof-write-active-child' |
'aof-write-alone' |
'aof-write-pending-fsync' |
'command' |
'expire-cycle' |
'eviction-cycle' |
'eviction-del' |
'fast-command' |
'fork' |
'rdb-unlink-temp-file'
);
export function transformArguments(event: EventType) {
return ['LATENCY', 'HISTORY', event];
}
export declare function transformReply(): Array<[
timestamp: number,
latency: number,
]>;

View File

@@ -0,0 +1,27 @@
import {strict as assert} from 'assert';
import testUtils, {GLOBAL} from '../test-utils';
import { transformArguments } from './LATENCY_LATEST';
describe('LATENCY LATEST', () => {
it('transformArguments', () => {
assert.deepEqual(
transformArguments(),
['LATENCY', 'LATEST']
);
});
testUtils.testWithClient('client.latencyLatest', async client => {
await Promise.all([
client.configSet('latency-monitor-threshold', '100'),
client.sendCommand(['DEBUG', 'SLEEP', '1'])
]);
const latency = await client.latencyLatest();
assert.ok(Array.isArray(latency));
for (const [name, timestamp, latestLatency, allTimeLatency] of latency) {
assert.equal(typeof name, 'string');
assert.equal(typeof timestamp, 'number');
assert.equal(typeof latestLatency, 'number');
assert.equal(typeof allTimeLatency, 'number');
}
}, GLOBAL.SERVERS.OPEN);
});

View File

@@ -0,0 +1,12 @@
import { RedisCommandArguments } from '.';
export function transformArguments(): RedisCommandArguments {
return ['LATENCY', 'LATEST'];
}
export declare function transformReply(): Array<[
name: string,
timestamp: number,
latestLatency: number,
allTimeLatency: number
]>;

View File

@@ -1,8 +1,24 @@
import { strict as assert } from 'assert';
import testUtils, { GLOBAL } from '../test-utils';
import RedisClient from '../client';
import { transformArguments } from './PING';
describe('PING', () => {
describe('transformArguments', () => {
it('default', () => {
assert.deepEqual(
transformArguments(),
['PING']
);
});
it('with message', () => {
assert.deepEqual(
transformArguments('message'),
['PING', 'message']
);
});
});
describe('client.ping', () => {
testUtils.testWithClient('string', async client => {
assert.equal(
@@ -13,7 +29,7 @@ describe('PING', () => {
testUtils.testWithClient('buffer', async client => {
assert.deepEqual(
await client.ping(RedisClient.commandOptions({ returnBuffers: true })),
await client.ping(client.commandOptions({ returnBuffers: true })),
Buffer.from('PONG')
);
}, GLOBAL.SERVERS.OPEN);

View File

@@ -1,7 +1,12 @@
import { RedisCommandArgument } from '.';
import { RedisCommandArgument, RedisCommandArguments } from '.';
export function transformArguments(): Array<string> {
return ['PING'];
export function transformArguments(message?: RedisCommandArgument): RedisCommandArguments {
const args: RedisCommandArguments = ['PING'];
if (message) {
args.push(message);
}
return args;
}
export declare function transformReply(): RedisCommandArgument;

View File

@@ -1,5 +1,7 @@
import { RedisCommandArgument, RedisCommandArguments } from '.';
export const IS_READ_ONLY = true;
export function transformArguments(
channel: RedisCommandArgument,
message: RedisCommandArgument

View File

@@ -0,0 +1,30 @@
import { strict as assert } from 'assert';
import testUtils, { GLOBAL } from '../test-utils';
import { transformArguments } from './PUBSUB_SHARDCHANNELS';
describe('PUBSUB SHARDCHANNELS', () => {
testUtils.isVersionGreaterThanHook([7]);
describe('transformArguments', () => {
it('without pattern', () => {
assert.deepEqual(
transformArguments(),
['PUBSUB', 'SHARDCHANNELS']
);
});
it('with pattern', () => {
assert.deepEqual(
transformArguments('patter*'),
['PUBSUB', 'SHARDCHANNELS', 'patter*']
);
});
});
testUtils.testWithClient('client.pubSubShardChannels', async client => {
assert.deepEqual(
await client.pubSubShardChannels(),
[]
);
}, GLOBAL.SERVERS.OPEN);
});

View File

@@ -0,0 +1,13 @@
import { RedisCommandArgument, RedisCommandArguments } from '.';
export const IS_READ_ONLY = true;
export function transformArguments(
pattern?: RedisCommandArgument
): RedisCommandArguments {
const args: RedisCommandArguments = ['PUBSUB', 'SHARDCHANNELS'];
if (pattern) args.push(pattern);
return args;
}
export declare function transformReply(): Array<RedisCommandArgument>;

View File

@@ -0,0 +1,48 @@
import { strict as assert } from 'assert';
import testUtils, { GLOBAL } from '../test-utils';
import { transformArguments } from './PUBSUB_SHARDNUMSUB';
describe('PUBSUB SHARDNUMSUB', () => {
testUtils.isVersionGreaterThanHook([7]);
describe('transformArguments', () => {
it('simple', () => {
assert.deepEqual(
transformArguments(),
['PUBSUB', 'SHARDNUMSUB']
);
});
it('string', () => {
assert.deepEqual(
transformArguments('channel'),
['PUBSUB', 'SHARDNUMSUB', 'channel']
);
});
it('array', () => {
assert.deepEqual(
transformArguments(['1', '2']),
['PUBSUB', 'SHARDNUMSUB', '1', '2']
);
});
});
testUtils.testWithClient('client.pubSubShardNumSub', async client => {
assert.deepEqual(
await client.pubSubShardNumSub(['foo', 'bar']),
Object.create(null, {
foo: {
value: 0,
configurable: true,
enumerable: true
},
bar: {
value: 0,
configurable: true,
enumerable: true
}
})
);
}, GLOBAL.SERVERS.OPEN);
});

View File

@@ -0,0 +1,24 @@
import { pushVerdictArguments } from './generic-transformers';
import { RedisCommandArgument, RedisCommandArguments } from '.';
export const IS_READ_ONLY = true;
export function transformArguments(
channels?: Array<RedisCommandArgument> | RedisCommandArgument
): RedisCommandArguments {
const args = ['PUBSUB', 'SHARDNUMSUB'];
if (channels) return pushVerdictArguments(args, channels);
return args;
}
export function transformReply(rawReply: Array<string | number>): Record<string, number> {
const transformedReply = Object.create(null);
for (let i = 0; i < rawReply.length; i += 2) {
transformedReply[rawReply[i]] = rawReply[i + 1];
}
return transformedReply;
}

View File

@@ -0,0 +1,74 @@
import { strict as assert } from 'assert';
import testUtils, { GLOBAL } from '../test-utils';
import { transformArguments } from './RESTORE';
describe('RESTORE', () => {
describe('transformArguments', () => {
it('simple', () => {
assert.deepEqual(
transformArguments('key', 0, 'value'),
['RESTORE', 'key', '0', 'value']
);
});
it('with REPLACE', () => {
assert.deepEqual(
transformArguments('key', 0, 'value', {
REPLACE: true
}),
['RESTORE', 'key', '0', 'value', 'REPLACE']
);
});
it('with ABSTTL', () => {
assert.deepEqual(
transformArguments('key', 0, 'value', {
ABSTTL: true
}),
['RESTORE', 'key', '0', 'value', 'ABSTTL']
);
});
it('with IDLETIME', () => {
assert.deepEqual(
transformArguments('key', 0, 'value', {
IDLETIME: 1
}),
['RESTORE', 'key', '0', 'value', 'IDLETIME', '1']
);
});
it('with FREQ', () => {
assert.deepEqual(
transformArguments('key', 0, 'value', {
FREQ: 1
}),
['RESTORE', 'key', '0', 'value', 'FREQ', '1']
);
});
it('with REPLACE, ABSTTL, IDLETIME and FREQ', () => {
assert.deepEqual(
transformArguments('key', 0, 'value', {
REPLACE: true,
ABSTTL: true,
IDLETIME: 1,
FREQ: 2
}),
['RESTORE', 'key', '0', 'value', 'REPLACE', 'ABSTTL', 'IDLETIME', '1', 'FREQ', '2']
);
});
});
testUtils.testWithClient('client.restore', async client => {
const [, dump] = await Promise.all([
client.set('source', 'value'),
client.dump(client.commandOptions({ returnBuffers: true }), 'source')
]);
assert.equal(
await client.restore('destination', 0, dump),
'OK'
);
}, GLOBAL.SERVERS.OPEN);
});

View File

@@ -0,0 +1,39 @@
import { RedisCommandArgument, RedisCommandArguments } from '.';
export const FIRST_KEY_INDEX = 1;
interface RestoreOptions {
REPLACE?: true;
ABSTTL?: true;
IDLETIME?: number;
FREQ?: number;
}
export function transformArguments(
key: RedisCommandArgument,
ttl: number,
serializedValue: RedisCommandArgument,
options?: RestoreOptions
): RedisCommandArguments {
const args = ['RESTORE', key, ttl.toString(), serializedValue];
if (options?.REPLACE) {
args.push('REPLACE');
}
if (options?.ABSTTL) {
args.push('ABSTTL');
}
if (options?.IDLETIME) {
args.push('IDLETIME', options.IDLETIME.toString());
}
if (options?.FREQ) {
args.push('FREQ', options.FREQ.toString());
}
return args;
}
export declare function transformReply(): 'OK';

View File

@@ -0,0 +1,21 @@
import { strict as assert } from 'assert';
import testUtils, { GLOBAL } from '../test-utils';
import { transformArguments } from './SPUBLISH';
describe('SPUBLISH', () => {
testUtils.isVersionGreaterThanHook([7]);
it('transformArguments', () => {
assert.deepEqual(
transformArguments('channel', 'message'),
['SPUBLISH', 'channel', 'message']
);
});
testUtils.testWithClient('client.sPublish', async client => {
assert.equal(
await client.sPublish('channel', 'message'),
0
);
}, GLOBAL.SERVERS.OPEN);
});

View File

@@ -0,0 +1,14 @@
import { RedisCommandArgument, RedisCommandArguments } from '.';
export const IS_READ_ONLY = true;
export const FIRST_KEY_INDEX = 1;
export function transformArguments(
channel: RedisCommandArgument,
message: RedisCommandArgument
): RedisCommandArguments {
return ['SPUBLISH', channel, message];
}
export declare function transformReply(): number;

View File

@@ -23,20 +23,76 @@ describe('XAUTOCLAIM', () => {
});
});
testUtils.testWithClient('client.xAutoClaim', async client => {
await Promise.all([
client.xGroupCreate('key', 'group', '$', {
MKSTREAM: true
}),
testUtils.testWithClient('client.xAutoClaim without messages', async client => {
const [,, reply] = await Promise.all([
client.xGroupCreate('key', 'group', '$', { MKSTREAM: true }),
client.xGroupCreateConsumer('key', 'group', 'consumer'),
client.xAutoClaim('key', 'group', 'consumer', 1, '0-0')
]);
assert.deepEqual(
await client.xAutoClaim('key', 'group', 'consumer', 1, '0-0'),
{
nextId: '0-0',
messages: []
}
);
assert.deepEqual(reply, {
nextId: '0-0',
messages: []
});
}, GLOBAL.SERVERS.OPEN);
testUtils.testWithClient('client.xAutoClaim with messages', async client => {
const [,, id,, reply] = await Promise.all([
client.xGroupCreate('key', 'group', '$', { MKSTREAM: true }),
client.xGroupCreateConsumer('key', 'group', 'consumer'),
client.xAdd('key', '*', { foo: 'bar' }),
client.xReadGroup('group', 'consumer', { key: 'key', id: '>' }),
client.xAutoClaim('key', 'group', 'consumer', 0, '0-0')
]);
assert.deepEqual(reply, {
nextId: '0-0',
messages: [{
id,
message: Object.create(null, {
foo: {
value: 'bar',
configurable: true,
enumerable: true
}
})
}]
});
}, GLOBAL.SERVERS.OPEN);
testUtils.testWithClient('client.xAutoClaim with trimmed messages', async client => {
const [,,,,, id,, reply] = await Promise.all([
client.xGroupCreate('key', 'group', '$', { MKSTREAM: true }),
client.xGroupCreateConsumer('key', 'group', 'consumer'),
client.xAdd('key', '*', { foo: 'bar' }),
client.xReadGroup('group', 'consumer', { key: 'key', id: '>' }),
client.xTrim('key', 'MAXLEN', 0),
client.xAdd('key', '*', { bar: 'baz' }),
client.xReadGroup('group', 'consumer', { key: 'key', id: '>' }),
client.xAutoClaim('key', 'group', 'consumer', 0, '0-0')
]);
assert.deepEqual(reply, {
nextId: '0-0',
messages: testUtils.isVersionGreaterThan([7, 0]) ? [{
id,
message: Object.create(null, {
bar: {
value: 'baz',
configurable: true,
enumerable: true
}
})
}] : [null, {
id,
message: Object.create(null, {
bar: {
value: 'baz',
configurable: true,
enumerable: true
}
})
}]
});
}, GLOBAL.SERVERS.OPEN);
});

View File

@@ -1,5 +1,5 @@
import { RedisCommandArgument, RedisCommandArguments } from '.';
import { StreamMessagesReply, transformStreamMessagesReply } from './generic-transformers';
import { StreamMessagesNullReply, transformStreamMessagesNullReply } from './generic-transformers';
export const FIRST_KEY_INDEX = 1;
@@ -28,12 +28,12 @@ type XAutoClaimRawReply = [RedisCommandArgument, Array<any>];
interface XAutoClaimReply {
nextId: RedisCommandArgument;
messages: StreamMessagesReply;
messages: StreamMessagesNullReply;
}
export function transformReply(reply: XAutoClaimRawReply): XAutoClaimReply {
return {
nextId: reply[0],
messages: transformStreamMessagesReply(reply[1])
messages: transformStreamMessagesNullReply(reply[1])
};
}

View File

@@ -83,8 +83,38 @@ describe('XCLAIM', () => {
});
assert.deepEqual(
await client.xClaim('key', 'group', 'consumer', 1, '0-0'),
await client.xClaim('key', 'group', 'consumer', 0, '0-0'),
[]
);
}, GLOBAL.SERVERS.OPEN);
testUtils.testWithClient('client.xClaim with a message', async client => {
await client.xGroupCreate('key', 'group', '$', { MKSTREAM: true });
const id = await client.xAdd('key', '*', { foo: 'bar' });
await client.xReadGroup('group', 'consumer', { key: 'key', id: '>' });
assert.deepEqual(
await client.xClaim('key', 'group', 'consumer', 0, id),
[{
id,
message: Object.create(null, { 'foo': {
value: 'bar',
configurable: true,
enumerable: true
} })
}]
);
}, GLOBAL.SERVERS.OPEN);
testUtils.testWithClient('client.xClaim with a trimmed message', async client => {
await client.xGroupCreate('key', 'group', '$', { MKSTREAM: true });
const id = await client.xAdd('key', '*', { foo: 'bar' });
await client.xReadGroup('group', 'consumer', { key: 'key', id: '>' });
await client.xTrim('key', 'MAXLEN', 0);
assert.deepEqual(
await client.xClaim('key', 'group', 'consumer', 0, id),
testUtils.isVersionGreaterThan([7, 0]) ? []: [null]
);
}, GLOBAL.SERVERS.OPEN);
});

View File

@@ -45,4 +45,4 @@ export function transformArguments(
return args;
}
export { transformStreamMessagesReply as transformReply } from './generic-transformers';
export { transformStreamMessagesNullReply as transformReply } from './generic-transformers';

View File

@@ -13,17 +13,19 @@ describe('XINFO CONSUMERS', () => {
it('transformReply', () => {
assert.deepEqual(
transformReply([
['name', 'Alice', 'pending', 1, 'idle', 9104628],
['name', 'Bob', 'pending', 1, 'idle', 83841983]
['name', 'Alice', 'pending', 1, 'idle', 9104628, 'inactive', 9281221],
['name', 'Bob', 'pending', 1, 'idle', 83841983, 'inactive', 7213871]
]),
[{
name: 'Alice',
pending: 1,
idle: 9104628
idle: 9104628,
inactive: 9281221,
}, {
name: 'Bob',
pending: 1,
idle: 83841983
idle: 83841983,
inactive: 7213871,
}]
);
});

View File

@@ -15,12 +15,14 @@ type XInfoConsumersReply = Array<{
name: RedisCommandArgument;
pending: number;
idle: number;
inactive: number;
}>;
export function transformReply(rawReply: Array<any>): XInfoConsumersReply {
return rawReply.map(consumer => ({
name: consumer[1],
pending: consumer[3],
idle: consumer[5]
idle: consumer[5],
inactive: consumer[7]
}));
}

View File

@@ -9,6 +9,7 @@ import {
transformStringNumberInfinityArgument,
transformTuplesReply,
transformStreamMessagesReply,
transformStreamMessagesNullReply,
transformStreamsMessagesReply,
transformSortedSetWithScoresReply,
pushGeoCountArgument,
@@ -219,6 +220,38 @@ describe('Generic Transformers', () => {
);
});
it('transformStreamMessagesNullReply', () => {
assert.deepEqual(
transformStreamMessagesNullReply([null, ['0-0', ['0key', '0value']]]),
[null, {
id: '0-0',
message: Object.create(null, {
'0key': {
value: '0value',
configurable: true,
enumerable: true
}
})
}]
);
});
it('transformStreamMessagesNullReply', () => {
assert.deepEqual(
transformStreamMessagesNullReply([null, ['0-1', ['11key', '11value']]]),
[null, {
id: '0-1',
message: Object.create(null, {
'11key': {
value: '11value',
configurable: true,
enumerable: true
}
})
}]
);
});
describe('transformStreamsMessagesReply', () => {
it('null', () => {
assert.equal(

View File

@@ -92,19 +92,27 @@ export interface StreamMessageReply {
message: Record<string, RedisCommandArgument>;
}
export function transformStreamMessageReply([id, message]: Array<any>): StreamMessageReply {
return {
id,
message: transformTuplesReply(message)
};
}
export function transformStreamMessageNullReply(reply: Array<any>): StreamMessageReply | null {
if (reply === null) return null;
return transformStreamMessageReply(reply);
}
export type StreamMessagesReply = Array<StreamMessageReply>;
export function transformStreamMessagesReply(reply: Array<any>): StreamMessagesReply {
const messages = [];
return reply.map(transformStreamMessageReply);
}
for (const [id, message] of reply) {
messages.push({
id,
message: transformTuplesReply(message)
});
}
return messages;
export type StreamMessagesNullReply = Array<StreamMessageReply | null>;
export function transformStreamMessagesNullReply(reply: Array<any>): StreamMessagesNullReply {
return reply.map(transformStreamMessageNullReply);
}
export type StreamsMessagesReply = Array<{
@@ -137,7 +145,6 @@ export function transformSortedSetMemberNullReply(
export function transformSortedSetMemberReply(
reply: [RedisCommandArgument, RedisCommandArgument]
): ZMember {
return {
value: reply[0],
score: transformNumberInfinityReply(reply[1])