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

V5 bringing RESP3, Sentinel and TypeMapping to node-redis

RESP3 Support
   - Some commands responses in RESP3 aren't stable yet and therefore return an "untyped" ReplyUnion.
 
Sentinel

TypeMapping

Correctly types Multi commands

Note: some API changes to be further documented in v4-to-v5.md
This commit is contained in:
Shaya Potter
2024-10-15 17:46:52 +03:00
committed by GitHub
parent 2fc79bdfb3
commit b2d35c5286
1174 changed files with 45931 additions and 36274 deletions

View File

@@ -1,23 +1,31 @@
import { strict as assert } from 'assert';
import testUtils from '../test-utils';
import { transformArguments } from './ACL_CAT';
import { strict as assert } from 'node:assert';
import testUtils, { GLOBAL } from '../test-utils';
import ACL_CAT from './ACL_CAT';
describe('ACL CAT', () => {
testUtils.isVersionGreaterThanHook([6]);
testUtils.isVersionGreaterThanHook([6]);
describe('transformArguments', () => {
it('simple', () => {
assert.deepEqual(
transformArguments(),
['ACL', 'CAT']
);
});
it('with categoryName', () => {
assert.deepEqual(
transformArguments('dangerous'),
['ACL', 'CAT', 'dangerous']
);
});
describe('transformArguments', () => {
it('simple', () => {
assert.deepEqual(
ACL_CAT.transformArguments(),
['ACL', 'CAT']
);
});
it('with categoryName', () => {
assert.deepEqual(
ACL_CAT.transformArguments('dangerous'),
['ACL', 'CAT', 'dangerous']
);
});
});
testUtils.testWithClient('client.aclCat', async client => {
const categories = await client.aclCat();
assert.ok(Array.isArray(categories));
for (const category of categories) {
assert.equal(typeof category, 'string');
}
}, GLOBAL.SERVERS.OPEN);
});

View File

@@ -1,13 +1,16 @@
import { RedisCommandArgument, RedisCommandArguments } from '.';
import { RedisArgument, ArrayReply, BlobStringReply, Command } from '../RESP/types';
export function transformArguments(categoryName?: RedisCommandArgument): RedisCommandArguments {
const args: RedisCommandArguments = ['ACL', 'CAT'];
export default {
FIRST_KEY_INDEX: undefined,
IS_READ_ONLY: true,
transformArguments(categoryName?: RedisArgument) {
const args: Array<RedisArgument> = ['ACL', 'CAT'];
if (categoryName) {
args.push(categoryName);
args.push(categoryName);
}
return args;
}
export declare function transformReply(): Array<RedisCommandArgument>;
},
transformReply: undefined as unknown as () => ArrayReply<BlobStringReply>
} as const satisfies Command;

View File

@@ -1,30 +1,30 @@
import { strict as assert } from 'assert';
import { strict as assert } from 'node:assert';
import testUtils, { GLOBAL } from '../test-utils';
import { transformArguments } from './ACL_DELUSER';
import ACL_DELUSER from './ACL_DELUSER';
describe('ACL DELUSER', () => {
testUtils.isVersionGreaterThanHook([6]);
testUtils.isVersionGreaterThanHook([6]);
describe('transformArguments', () => {
it('string', () => {
assert.deepEqual(
transformArguments('username'),
['ACL', 'DELUSER', 'username']
);
});
it('array', () => {
assert.deepEqual(
transformArguments(['1', '2']),
['ACL', 'DELUSER', '1', '2']
);
});
describe('transformArguments', () => {
it('string', () => {
assert.deepEqual(
ACL_DELUSER.transformArguments('username'),
['ACL', 'DELUSER', 'username']
);
});
testUtils.testWithClient('client.aclDelUser', async client => {
assert.equal(
await client.aclDelUser('dosenotexists'),
0
);
}, GLOBAL.SERVERS.OPEN);
it('array', () => {
assert.deepEqual(
ACL_DELUSER.transformArguments(['1', '2']),
['ACL', 'DELUSER', '1', '2']
);
});
});
testUtils.testWithClient('client.aclDelUser', async client => {
assert.equal(
typeof await client.aclDelUser('user'),
'number'
);
}, GLOBAL.SERVERS.OPEN);
});

View File

@@ -1,10 +1,11 @@
import { RedisCommandArgument, RedisCommandArguments } from '.';
import { pushVerdictArguments } from './generic-transformers';
import { NumberReply, Command } from '../RESP/types';
import { RedisVariadicArgument, pushVariadicArguments } from './generic-transformers';
export function transformArguments(
username: RedisCommandArgument | Array<RedisCommandArgument>
): RedisCommandArguments {
return pushVerdictArguments(['ACL', 'DELUSER'], username);
}
export declare function transformReply(): number;
export default {
FIRST_KEY_INDEX: undefined,
IS_READ_ONLY: true,
transformArguments(username: RedisVariadicArgument) {
return pushVariadicArguments(['ACL', 'DELUSER'], username);
},
transformReply: undefined as unknown as () => NumberReply
} as const satisfies Command;

View File

@@ -1,21 +1,21 @@
import { strict as assert } from 'assert';
import { strict as assert } from 'node:assert';
import testUtils, { GLOBAL } from '../test-utils';
import { transformArguments } from './ACL_DRYRUN';
import ACL_DRYRUN from './ACL_DRYRUN';
describe('ACL DRYRUN', () => {
testUtils.isVersionGreaterThanHook([7]);
testUtils.isVersionGreaterThanHook([7]);
it('transformArguments', () => {
assert.deepEqual(
transformArguments('default', ['GET', 'key']),
['ACL', 'DRYRUN', 'default', 'GET', 'key']
);
});
it('transformArguments', () => {
assert.deepEqual(
ACL_DRYRUN.transformArguments('default', ['GET', 'key']),
['ACL', 'DRYRUN', 'default', 'GET', 'key']
);
});
testUtils.testWithClient('client.aclDryRun', async client => {
assert.equal(
await client.aclDryRun('default', ['GET', 'key']),
'OK'
);
}, GLOBAL.SERVERS.OPEN);
testUtils.testWithClient('client.aclDryRun', async client => {
assert.equal(
await client.aclDryRun('default', ['GET', 'key']),
'OK'
);
}, GLOBAL.SERVERS.OPEN);
});

View File

@@ -1,18 +1,16 @@
import { RedisCommandArgument, RedisCommandArguments } from '.';
import { RedisArgument, SimpleStringReply, BlobStringReply, Command } from '../RESP/types';
export const IS_READ_ONLY = true;
export function transformArguments(
username: RedisCommandArgument,
command: Array<RedisCommandArgument>
): RedisCommandArguments {
export default {
FIRST_KEY_INDEX: undefined,
IS_READ_ONLY: true,
transformArguments(username: RedisArgument, command: Array<RedisArgument>) {
return [
'ACL',
'DRYRUN',
username,
...command
'ACL',
'DRYRUN',
username,
...command
];
}
export declare function transformReply(): RedisCommandArgument;
},
transformReply: undefined as unknown as () => SimpleStringReply<'OK'> | BlobStringReply
} as const satisfies Command;

View File

@@ -1,23 +1,30 @@
import { strict as assert } from 'assert';
import testUtils from '../test-utils';
import { transformArguments } from './ACL_GENPASS';
import { strict as assert } from 'node:assert';
import testUtils, { GLOBAL } from '../test-utils';
import ACL_GENPASS from './ACL_GENPASS';
describe('ACL GENPASS', () => {
testUtils.isVersionGreaterThanHook([6]);
testUtils.isVersionGreaterThanHook([6]);
describe('transformArguments', () => {
it('simple', () => {
assert.deepEqual(
transformArguments(),
['ACL', 'GENPASS']
);
});
it('with bits', () => {
assert.deepEqual(
transformArguments(128),
['ACL', 'GENPASS', '128']
);
});
describe('transformArguments', () => {
it('simple', () => {
assert.deepEqual(
ACL_GENPASS.transformArguments(),
['ACL', 'GENPASS']
);
});
it('with bits', () => {
assert.deepEqual(
ACL_GENPASS.transformArguments(128),
['ACL', 'GENPASS', '128']
);
});
});
testUtils.testWithClient('client.aclGenPass', async client => {
assert.equal(
typeof await client.aclGenPass(),
'string'
);
}, GLOBAL.SERVERS.OPEN);
});

View File

@@ -1,13 +1,17 @@
import { RedisCommandArgument, RedisCommandArguments } from '.';
import { BlobStringReply, Command } from '../RESP/types';
export function transformArguments(bits?: number): RedisCommandArguments {
export default {
FIRST_KEY_INDEX: undefined,
IS_READ_ONLY: true,
transformArguments(bits?: number) {
const args = ['ACL', 'GENPASS'];
if (bits) {
args.push(bits.toString());
args.push(bits.toString());
}
return args;
}
},
transformReply: undefined as unknown as () => BlobStringReply
} as const satisfies Command;
export declare function transformReply(): RedisCommandArgument;

View File

@@ -1,34 +1,34 @@
import { strict as assert } from 'assert';
import { strict as assert } from 'node:assert';
import testUtils, { GLOBAL } from '../test-utils';
import { transformArguments } from './ACL_GETUSER';
import ACL_GETUSER from './ACL_GETUSER';
describe('ACL GETUSER', () => {
testUtils.isVersionGreaterThanHook([6]);
testUtils.isVersionGreaterThanHook([6]);
it('transformArguments', () => {
assert.deepEqual(
transformArguments('username'),
['ACL', 'GETUSER', 'username']
);
});
it('transformArguments', () => {
assert.deepEqual(
ACL_GETUSER.transformArguments('username'),
['ACL', 'GETUSER', 'username']
);
});
testUtils.testWithClient('client.aclGetUser', async client => {
const reply = await client.aclGetUser('default');
testUtils.testWithClient('client.aclGetUser', async client => {
const reply = await client.aclGetUser('default');
assert.ok(Array.isArray(reply.passwords));
assert.equal(typeof reply.commands, 'string');
assert.ok(Array.isArray(reply.flags));
assert.ok(Array.isArray(reply.passwords));
assert.equal(typeof reply.commands, 'string');
assert.ok(Array.isArray(reply.flags));
if (testUtils.isVersionGreaterThan([7])) {
assert.equal(typeof reply.keys, 'string');
assert.equal(typeof reply.channels, 'string');
assert.ok(Array.isArray(reply.selectors));
} else {
assert.ok(Array.isArray(reply.keys));
if (testUtils.isVersionGreaterThan([7])) {
assert.equal(typeof reply.keys, 'string');
assert.equal(typeof reply.channels, 'string');
assert.ok(Array.isArray(reply.selectors));
} else {
assert.ok(Array.isArray(reply.keys));
if (testUtils.isVersionGreaterThan([6, 2])) {
assert.ok(Array.isArray(reply.channels));
}
}
}, GLOBAL.SERVERS.OPEN);
if (testUtils.isVersionGreaterThan([6, 2])) {
assert.ok(Array.isArray(reply.channels));
}
}
}, GLOBAL.SERVERS.OPEN);
});

View File

@@ -1,40 +1,43 @@
import { RedisCommandArgument, RedisCommandArguments } from '.';
import { RedisArgument, TuplesToMapReply, BlobStringReply, ArrayReply, UnwrapReply, Resp2Reply, Command } from '../RESP/types';
export function transformArguments(username: RedisCommandArgument): RedisCommandArguments {
type AclUser = TuplesToMapReply<[
[BlobStringReply<'flags'>, ArrayReply<BlobStringReply>],
[BlobStringReply<'passwords'>, ArrayReply<BlobStringReply>],
[BlobStringReply<'commands'>, BlobStringReply],
/** changed to BlobStringReply in 7.0 */
[BlobStringReply<'keys'>, ArrayReply<BlobStringReply> | BlobStringReply],
/** added in 6.2, changed to BlobStringReply in 7.0 */
[BlobStringReply<'channels'>, ArrayReply<BlobStringReply> | BlobStringReply],
/** added in 7.0 */
[BlobStringReply<'selectors'>, ArrayReply<TuplesToMapReply<[
[BlobStringReply<'commands'>, BlobStringReply],
[BlobStringReply<'keys'>, BlobStringReply],
[BlobStringReply<'channels'>, BlobStringReply]
]>>],
]>;
export default {
FIRST_KEY_INDEX: undefined,
IS_READ_ONLY: true,
transformArguments(username: RedisArgument) {
return ['ACL', 'GETUSER', username];
}
type AclGetUserRawReply = [
'flags',
Array<RedisCommandArgument>,
'passwords',
Array<RedisCommandArgument>,
'commands',
RedisCommandArgument,
'keys',
Array<RedisCommandArgument> | RedisCommandArgument,
'channels',
Array<RedisCommandArgument> | RedisCommandArgument,
'selectors' | undefined,
Array<Array<string>> | undefined
];
interface AclUser {
flags: Array<RedisCommandArgument>;
passwords: Array<RedisCommandArgument>;
commands: RedisCommandArgument;
keys: Array<RedisCommandArgument> | RedisCommandArgument;
channels: Array<RedisCommandArgument> | RedisCommandArgument;
selectors?: Array<Array<string>>;
}
export function transformReply(reply: AclGetUserRawReply): AclUser {
return {
flags: reply[1],
passwords: reply[3],
commands: reply[5],
keys: reply[7],
channels: reply[9],
selectors: reply[11]
};
}
},
transformReply: {
2: (reply: UnwrapReply<Resp2Reply<AclUser>>) => ({
flags: reply[1],
passwords: reply[3],
commands: reply[5],
keys: reply[7],
channels: reply[9],
selectors: (reply[11] as unknown as UnwrapReply<typeof reply[11]>)?.map(selector => {
const inferred = selector as unknown as UnwrapReply<typeof selector>;
return {
commands: inferred[1],
keys: inferred[3],
channels: inferred[5]
};
})
}),
3: undefined as unknown as () => AclUser
}
} as const satisfies Command;

View File

@@ -1,14 +1,22 @@
import { strict as assert } from 'assert';
import testUtils from '../test-utils';
import { transformArguments } from './ACL_LIST';
import { strict as assert } from 'node:assert';
import testUtils, { GLOBAL } from '../test-utils';
import ACL_LIST from './ACL_LIST';
describe('ACL LIST', () => {
testUtils.isVersionGreaterThanHook([6]);
testUtils.isVersionGreaterThanHook([6]);
it('transformArguments', () => {
assert.deepEqual(
transformArguments(),
['ACL', 'LIST']
);
});
it('transformArguments', () => {
assert.deepEqual(
ACL_LIST.transformArguments(),
['ACL', 'LIST']
);
});
testUtils.testWithClient('client.aclList', async client => {
const users = await client.aclList();
assert.ok(Array.isArray(users));
for (const user of users) {
assert.equal(typeof user, 'string');
}
}, GLOBAL.SERVERS.OPEN);
});

View File

@@ -1,7 +1,10 @@
import { RedisCommandArgument, RedisCommandArguments } from '.';
import { ArrayReply, BlobStringReply, Command } from '../RESP/types';
export function transformArguments(): RedisCommandArguments {
export default {
FIRST_KEY_INDEX: undefined,
IS_READ_ONLY: true,
transformArguments() {
return ['ACL', 'LIST'];
}
export declare function transformReply(): Array<RedisCommandArgument>;
},
transformReply: undefined as unknown as () => ArrayReply<BlobStringReply>
} as const satisfies Command;

View File

@@ -1,14 +1,14 @@
import { strict as assert } from 'assert';
import { strict as assert } from 'node:assert';
import testUtils from '../test-utils';
import { transformArguments } from './ACL_SAVE';
import ACL_LOAD from './ACL_LOAD';
describe('ACL SAVE', () => {
testUtils.isVersionGreaterThanHook([6]);
describe('ACL LOAD', () => {
testUtils.isVersionGreaterThanHook([6]);
it('transformArguments', () => {
assert.deepEqual(
transformArguments(),
['ACL', 'SAVE']
);
});
it('transformArguments', () => {
assert.deepEqual(
ACL_LOAD.transformArguments(),
['ACL', 'LOAD']
);
});
});

View File

@@ -1,7 +1,10 @@
import { RedisCommandArgument, RedisCommandArguments } from '.';
import { SimpleStringReply, Command } from '../RESP/types';
export function transformArguments(): RedisCommandArguments {
export default {
FIRST_KEY_INDEX: undefined,
IS_READ_ONLY: true,
transformArguments() {
return ['ACL', 'LOAD'];
}
export declare function transformReply(): RedisCommandArgument;
},
transformReply: undefined as unknown as () => SimpleStringReply<'OK'>
} as const satisfies Command;

View File

@@ -1,53 +1,50 @@
import { strict as assert } from 'assert';
import testUtils from '../test-utils';
import { transformArguments, transformReply } from './ACL_LOG';
import { strict as assert } from 'node:assert';
import testUtils, { GLOBAL } from '../test-utils';
import ACL_LOG from './ACL_LOG';
describe('ACL LOG', () => {
testUtils.isVersionGreaterThanHook([6]);
testUtils.isVersionGreaterThanHook([6]);
describe('transformArguments', () => {
it('simple', () => {
assert.deepEqual(
transformArguments(),
['ACL', 'LOG']
);
});
it('with count', () => {
assert.deepEqual(
transformArguments(10),
['ACL', 'LOG', '10']
);
});
describe('transformArguments', () => {
it('simple', () => {
assert.deepEqual(
ACL_LOG.transformArguments(),
['ACL', 'LOG']
);
});
it('transformReply', () => {
assert.deepEqual(
transformReply([[
'count',
1,
'reason',
'auth',
'context',
'toplevel',
'object',
'AUTH',
'username',
'someuser',
'age-seconds',
'4.096',
'client-info',
'id=6 addr=127.0.0.1:63026 fd=8 name= age=9 idle=0 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=48 qbuf-free=32720 obl=0 oll=0 omem=0 events=r cmd=auth user=default'
]]),
[{
count: 1,
reason: 'auth',
context: 'toplevel',
object: 'AUTH',
username: 'someuser',
ageSeconds: 4.096,
clientInfo: 'id=6 addr=127.0.0.1:63026 fd=8 name= age=9 idle=0 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=48 qbuf-free=32720 obl=0 oll=0 omem=0 events=r cmd=auth user=default'
}]
);
it('with count', () => {
assert.deepEqual(
ACL_LOG.transformArguments(10),
['ACL', 'LOG', '10']
);
});
});
testUtils.testWithClient('client.aclLog', async client => {
// make sure to create one log
await assert.rejects(
client.auth({
username: 'incorrect',
password: 'incorrect'
})
);
const logs = await client.aclLog();
assert.ok(Array.isArray(logs));
for (const log of logs) {
assert.equal(typeof log.count, '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['age-seconds'], 'number');
assert.equal(typeof log['client-info'], 'string');
if (testUtils.isVersionGreaterThan([7, 2])) {
assert.equal(typeof log['entry-id'], 'number');
assert.equal(typeof log['timestamp-created'], 'number');
assert.equal(typeof log['timestamp-last-updated'], 'number');
}
}
}, GLOBAL.SERVERS.OPEN);
});

View File

@@ -1,50 +1,52 @@
import { RedisCommandArgument, RedisCommandArguments } from '.';
import { ArrayReply, TuplesToMapReply, BlobStringReply, NumberReply, DoubleReply, UnwrapReply, Resp2Reply, Command, TypeMapping } from '../RESP/types';
import { transformDoubleReply } from './generic-transformers';
export function transformArguments(count?: number): RedisCommandArguments {
export type AclLogReply = ArrayReply<TuplesToMapReply<[
[BlobStringReply<'count'>, NumberReply],
[BlobStringReply<'reason'>, BlobStringReply],
[BlobStringReply<'context'>, BlobStringReply],
[BlobStringReply<'object'>, BlobStringReply],
[BlobStringReply<'username'>, BlobStringReply],
[BlobStringReply<'age-seconds'>, DoubleReply],
[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 {
FIRST_KEY_INDEX: undefined,
IS_READ_ONLY: true,
transformArguments(count?: number) {
const args = ['ACL', 'LOG'];
if (count) {
args.push(count.toString());
if (count !== undefined) {
args.push(count.toString());
}
return args;
}
type AclLogRawReply = [
_: RedisCommandArgument,
count: number,
_: RedisCommandArgument,
reason: RedisCommandArgument,
_: RedisCommandArgument,
context: RedisCommandArgument,
_: RedisCommandArgument,
object: RedisCommandArgument,
_: RedisCommandArgument,
username: RedisCommandArgument,
_: RedisCommandArgument,
ageSeconds: RedisCommandArgument,
_: RedisCommandArgument,
clientInfo: RedisCommandArgument
];
interface AclLog {
count: number;
reason: RedisCommandArgument;
context: RedisCommandArgument;
object: RedisCommandArgument;
username: RedisCommandArgument;
ageSeconds: number;
clientInfo: RedisCommandArgument;
}
export function transformReply(reply: Array<AclLogRawReply>): Array<AclLog> {
return reply.map(log => ({
count: log[1],
reason: log[3],
context: log[5],
object: log[7],
username: log[9],
ageSeconds: Number(log[11]),
clientInfo: log[13]
}));
}
},
transformReply: {
2: (reply: UnwrapReply<Resp2Reply<AclLogReply>>, preserve?: any, typeMapping?: TypeMapping) => {
return reply.map(item => {
const inferred = item as unknown as UnwrapReply<typeof item>;
return {
count: inferred[1],
reason: inferred[3],
context: inferred[5],
object: inferred[7],
username: inferred[9],
'age-seconds': transformDoubleReply[2](inferred[11], preserve, typeMapping),
'client-info': inferred[13],
'entry-id': inferred[15],
'timestamp-created': inferred[17],
'timestamp-last-updated': inferred[19]
};
})
},
3: undefined as unknown as () => AclLogReply
}
} as const satisfies Command;

View File

@@ -1,14 +1,21 @@
import { strict as assert } from 'assert';
import testUtils from '../test-utils';
import { transformArguments } from './ACL_LOG_RESET';
import { strict as assert } from 'node:assert';
import testUtils, { GLOBAL } from '../test-utils';
import ACL_LOG_RESET from './ACL_LOG_RESET';
describe('ACL LOG RESET', () => {
testUtils.isVersionGreaterThanHook([6]);
testUtils.isVersionGreaterThanHook([6]);
it('transformArguments', () => {
assert.deepEqual(
transformArguments(),
['ACL', 'LOG', 'RESET']
);
});
it('transformArguments', () => {
assert.deepEqual(
ACL_LOG_RESET.transformArguments(),
['ACL', 'LOG', 'RESET']
);
});
testUtils.testWithClient('client.aclLogReset', async client => {
assert.equal(
await client.aclLogReset(),
'OK'
);
}, GLOBAL.SERVERS.OPEN);
});

View File

@@ -1,7 +1,11 @@
import { RedisCommandArgument, RedisCommandArguments } from '.';
import { SimpleStringReply, Command } from '../RESP/types';
import ACL_LOG from './ACL_LOG';
export function transformArguments(): RedisCommandArguments {
export default {
FIRST_KEY_INDEX: undefined,
IS_READ_ONLY: ACL_LOG.IS_READ_ONLY,
transformArguments() {
return ['ACL', 'LOG', 'RESET'];
}
export declare function transformReply(): RedisCommandArgument;
},
transformReply: undefined as unknown as () => SimpleStringReply<'OK'>
} as const satisfies Command;

View File

@@ -1,14 +1,14 @@
import { strict as assert } from 'assert';
import { strict as assert } from 'node:assert';
import testUtils from '../test-utils';
import { transformArguments } from './ACL_LOAD';
import ACL_SAVE from './ACL_SAVE';
describe('ACL LOAD', () => {
testUtils.isVersionGreaterThanHook([6]);
describe('ACL SAVE', () => {
testUtils.isVersionGreaterThanHook([6]);
it('transformArguments', () => {
assert.deepEqual(
transformArguments(),
['ACL', 'LOAD']
);
});
it('transformArguments', () => {
assert.deepEqual(
ACL_SAVE.transformArguments(),
['ACL', 'SAVE']
);
});
});

View File

@@ -1,7 +1,10 @@
import { RedisCommandArgument, RedisCommandArguments } from '.';
import { SimpleStringReply, Command } from '../RESP/types';
export function transformArguments(): RedisCommandArguments {
export default {
FIRST_KEY_INDEX: undefined,
IS_READ_ONLY: true,
transformArguments() {
return ['ACL', 'SAVE'];
}
export declare function transformReply(): RedisCommandArgument;
},
transformReply: undefined as unknown as () => SimpleStringReply<'OK'>
} as const satisfies Command;

View File

@@ -1,23 +1,23 @@
import { strict as assert } from 'assert';
import { strict as assert } from 'node:assert';
import testUtils from '../test-utils';
import { transformArguments } from './ACL_SETUSER';
import ACL_SETUSER from './ACL_SETUSER';
describe('ACL SETUSER', () => {
testUtils.isVersionGreaterThanHook([6]);
testUtils.isVersionGreaterThanHook([6]);
describe('transformArguments', () => {
it('string', () => {
assert.deepEqual(
transformArguments('username', 'allkeys'),
['ACL', 'SETUSER', 'username', 'allkeys']
);
});
it('array', () => {
assert.deepEqual(
transformArguments('username', ['allkeys', 'allchannels']),
['ACL', 'SETUSER', 'username', 'allkeys', 'allchannels']
);
});
describe('transformArguments', () => {
it('string', () => {
assert.deepEqual(
ACL_SETUSER.transformArguments('username', 'allkeys'),
['ACL', 'SETUSER', 'username', 'allkeys']
);
});
it('array', () => {
assert.deepEqual(
ACL_SETUSER.transformArguments('username', ['allkeys', 'allchannels']),
['ACL', 'SETUSER', 'username', 'allkeys', 'allchannels']
);
});
});
});

View File

@@ -1,11 +1,11 @@
import { RedisCommandArgument, RedisCommandArguments } from '.';
import { pushVerdictArguments } from './generic-transformers';
import { RedisArgument, SimpleStringReply, Command } from '../RESP/types';
import { RedisVariadicArgument, pushVariadicArguments } from './generic-transformers';
export function transformArguments(
username: RedisCommandArgument,
rule: RedisCommandArgument | Array<RedisCommandArgument>
): RedisCommandArguments {
return pushVerdictArguments(['ACL', 'SETUSER', username], rule);
}
export declare function transformReply(): RedisCommandArgument;
export default {
FIRST_KEY_INDEX: undefined,
IS_READ_ONLY: true,
transformArguments(username: RedisArgument, rule: RedisVariadicArgument) {
return pushVariadicArguments(['ACL', 'SETUSER', username], rule);
},
transformReply: undefined as unknown as () => SimpleStringReply<'OK'>
} as const satisfies Command;

View File

@@ -1,14 +1,14 @@
import { strict as assert } from 'assert';
import { strict as assert } from 'node:assert';
import testUtils from '../test-utils';
import { transformArguments } from './ACL_USERS';
import ACL_USERS from './ACL_USERS';
describe('ACL USERS', () => {
testUtils.isVersionGreaterThanHook([6]);
testUtils.isVersionGreaterThanHook([6]);
it('transformArguments', () => {
assert.deepEqual(
transformArguments(),
['ACL', 'USERS']
);
});
it('transformArguments', () => {
assert.deepEqual(
ACL_USERS.transformArguments(),
['ACL', 'USERS']
);
});
});

View File

@@ -1,7 +1,10 @@
import { RedisCommandArgument, RedisCommandArguments } from '.';
import { ArrayReply, BlobStringReply, Command } from '../RESP/types';
export function transformArguments(): RedisCommandArguments {
export default {
FIRST_KEY_INDEX: undefined,
IS_READ_ONLY: true,
transformArguments() {
return ['ACL', 'USERS'];
}
export declare function transformReply(): Array<RedisCommandArgument>;
},
transformReply: undefined as unknown as () => ArrayReply<BlobStringReply>
} as const satisfies Command;

View File

@@ -1,14 +1,14 @@
import { strict as assert } from 'assert';
import { strict as assert } from 'node:assert';
import testUtils from '../test-utils';
import { transformArguments } from './ACL_WHOAMI';
import ACL_WHOAMI from './ACL_WHOAMI';
describe('ACL WHOAMI', () => {
testUtils.isVersionGreaterThanHook([6]);
testUtils.isVersionGreaterThanHook([6]);
it('transformArguments', () => {
assert.deepEqual(
transformArguments(),
['ACL', 'WHOAMI']
);
});
it('transformArguments', () => {
assert.deepEqual(
ACL_WHOAMI.transformArguments(),
['ACL', 'WHOAMI']
);
});
});

View File

@@ -1,7 +1,10 @@
import { RedisCommandArgument, RedisCommandArguments } from '.';
import { BlobStringReply, Command } from '../RESP/types';
export function transformArguments(): RedisCommandArguments {
export default {
FIRST_KEY_INDEX: undefined,
IS_READ_ONLY: true,
transformArguments() {
return ['ACL', 'WHOAMI'];
}
export declare function transformReply(): RedisCommandArgument;
},
transformReply: undefined as unknown as () => BlobStringReply
} as const satisfies Command;

View File

@@ -1,11 +1,22 @@
import { strict as assert } from 'assert';
import { transformArguments } from './APPEND';
import { strict as assert } from 'node:assert';
import testUtils, { GLOBAL } from '../test-utils';
import APPEND from './APPEND';
describe('APPEND', () => {
it('transformArguments', () => {
assert.deepEqual(
transformArguments('key', 'value'),
['APPEND', 'key', 'value']
);
});
it('transformArguments', () => {
assert.deepEqual(
APPEND.transformArguments('key', 'value'),
['APPEND', 'key', 'value']
);
});
testUtils.testAll('append', async client => {
assert.equal(
await client.append('key', 'value'),
5
);
}, {
client: GLOBAL.SERVERS.OPEN,
cluster: GLOBAL.CLUSTERS.OPEN
});
});

View File

@@ -1,12 +1,10 @@
import { RedisCommandArgument, RedisCommandArguments } from '.';
import { RedisArgument, NumberReply, Command } from '../RESP/types';
export const FIRST_KEY_INDEX = 1;
export function transformArguments(
key: RedisCommandArgument,
value: RedisCommandArgument
): RedisCommandArguments {
export default {
FIRST_KEY_INDEX: 1,
IS_READ_ONLY: false,
transformArguments(key: RedisArgument, value: RedisArgument) {
return ['APPEND', key, value];
}
export declare function transformReply(): number;
},
transformReply: undefined as unknown as () => NumberReply
} as const satisfies Command;

View File

@@ -1,11 +1,11 @@
import { strict as assert } from 'assert';
import { transformArguments } from './ASKING';
import { strict as assert } from 'node:assert';
import ASKING from './ASKING';
describe('ASKING', () => {
it('transformArguments', () => {
assert.deepEqual(
transformArguments(),
['ASKING']
);
});
it('transformArguments', () => {
assert.deepEqual(
ASKING.transformArguments(),
['ASKING']
);
});
});

View File

@@ -1,7 +1,10 @@
import { RedisCommandArguments, RedisCommandArgument } from '.';
import { SimpleStringReply, Command } from '../RESP/types';
export function transformArguments(): RedisCommandArguments {
export default {
FIRST_KEY_INDEX: undefined,
IS_READ_ONLY: true,
transformArguments() {
return ['ASKING'];
}
export declare function transformReply(): RedisCommandArgument;
},
transformReply: undefined as unknown as () => SimpleStringReply<'OK'>
} as const satisfies Command;

View File

@@ -1,25 +1,25 @@
import { strict as assert } from 'assert';
import { transformArguments } from './AUTH';
import { strict as assert } from 'node:assert';
import AUTH from './AUTH';
describe('AUTH', () => {
describe('transformArguments', () => {
it('password only', () => {
assert.deepEqual(
transformArguments({
password: 'password'
}),
['AUTH', 'password']
);
});
it('username & password', () => {
assert.deepEqual(
transformArguments({
username: 'username',
password: 'password'
}),
['AUTH', 'username', 'password']
);
});
describe('transformArguments', () => {
it('password only', () => {
assert.deepEqual(
AUTH.transformArguments({
password: 'password'
}),
['AUTH', 'password']
);
});
it('username & password', () => {
assert.deepEqual(
AUTH.transformArguments({
username: 'username',
password: 'password'
}),
['AUTH', 'username', 'password']
);
});
});
});

View File

@@ -1,16 +1,23 @@
import { RedisCommandArgument, RedisCommandArguments } from '.';
import { RedisArgument, SimpleStringReply, Command } from '../RESP/types';
export interface AuthOptions {
username?: RedisCommandArgument;
password: RedisCommandArgument;
username?: RedisArgument;
password: RedisArgument;
}
export function transformArguments({ username, password }: AuthOptions): RedisCommandArguments {
if (!username) {
return ['AUTH', password];
export default {
FIRST_KEY_INDEX: undefined,
IS_READ_ONLY: true,
transformArguments({ username, password }: AuthOptions) {
const args: Array<RedisArgument> = ['AUTH'];
if (username !== undefined) {
args.push(username);
}
return ['AUTH', username, password];
}
args.push(password);
export declare function transformReply(): RedisCommandArgument;
return args;
},
transformReply: undefined as unknown as () => SimpleStringReply<'OK'>
} as const satisfies Command;

View File

@@ -1,11 +1,19 @@
import { strict as assert } from 'assert';
import { transformArguments } from './BGREWRITEAOF';
import { strict as assert } from 'node:assert';
import testUtils, { GLOBAL } from '../test-utils';
import BGREWRITEAOF from './BGREWRITEAOF';
describe('BGREWRITEAOF', () => {
it('transformArguments', () => {
assert.deepEqual(
transformArguments(),
['BGREWRITEAOF']
);
});
it('transformArguments', () => {
assert.deepEqual(
BGREWRITEAOF.transformArguments(),
['BGREWRITEAOF']
);
});
testUtils.testWithClient('client.bgRewriteAof', async client => {
assert.equal(
typeof await client.bgRewriteAof(),
'string'
);
}, GLOBAL.SERVERS.OPEN);
});

View File

@@ -1,7 +1,10 @@
import { RedisCommandArgument, RedisCommandArguments } from '.';
import { SimpleStringReply, Command } from '../RESP/types';
export function transformArguments(): RedisCommandArguments {
export default {
FIRST_KEY_INDEX: undefined,
IS_READ_ONLY: true,
transformArguments() {
return ['BGREWRITEAOF'];
}
export declare function transformReply(): RedisCommandArgument;
},
transformReply: undefined as unknown as () => SimpleStringReply
} as const satisfies Command;

View File

@@ -1,23 +1,32 @@
import { strict as assert } from 'assert';
import { describe } from 'mocha';
import { transformArguments } from './BGSAVE';
import { strict as assert } from 'node:assert';
import testUtils, { GLOBAL } from '../test-utils';
import BGSAVE from './BGSAVE';
describe('BGSAVE', () => {
describe('transformArguments', () => {
it('simple', () => {
assert.deepEqual(
transformArguments(),
['BGSAVE']
);
});
it('with SCHEDULE', () => {
assert.deepEqual(
transformArguments({
SCHEDULE: true
}),
['BGSAVE', 'SCHEDULE']
);
});
describe('transformArguments', () => {
it('simple', () => {
assert.deepEqual(
BGSAVE.transformArguments(),
['BGSAVE']
);
});
it('with SCHEDULE', () => {
assert.deepEqual(
BGSAVE.transformArguments({
SCHEDULE: true
}),
['BGSAVE', 'SCHEDULE']
);
});
});
testUtils.testWithClient('client.bgSave', async client => {
assert.equal(
typeof await client.bgSave({
SCHEDULE: true // using `SCHEDULE` to make sure it won't throw an error
}),
'string'
);
}, GLOBAL.SERVERS.OPEN);
});

View File

@@ -1,17 +1,20 @@
import { RedisCommandArgument, RedisCommandArguments } from '.';
import { SimpleStringReply, Command } from '../RESP/types';
interface BgSaveOptions {
SCHEDULE?: true;
export interface BgSaveOptions {
SCHEDULE?: boolean;
}
export function transformArguments(options?: BgSaveOptions): RedisCommandArguments {
export default {
FIRST_KEY_INDEX: undefined,
IS_READ_ONLY: true,
transformArguments(options?: BgSaveOptions) {
const args = ['BGSAVE'];
if (options?.SCHEDULE) {
args.push('SCHEDULE');
args.push('SCHEDULE');
}
return args;
}
export declare function transformReply(): RedisCommandArgument;
},
transformReply: undefined as unknown as () => SimpleStringReply
} as const satisfies Command;

View File

@@ -1,44 +1,47 @@
import { strict as assert } from 'assert';
import { strict as assert } from 'node:assert';
import testUtils, { GLOBAL } from '../test-utils';
import { transformArguments } from './BITCOUNT';
import BITCOUNT from './BITCOUNT';
describe('BITCOUNT', () => {
describe('transformArguments', () => {
it('simple', () => {
assert.deepEqual(
transformArguments('key'),
['BITCOUNT', 'key']
);
});
describe('with range', () => {
it('simple', () => {
assert.deepEqual(
transformArguments('key', {
start: 0,
end: 1
}),
['BITCOUNT', 'key', '0', '1']
);
});
it('with mode', () => {
assert.deepEqual(
transformArguments('key', {
start: 0,
end: 1,
mode: 'BIT'
}),
['BITCOUNT', 'key', '0', '1', 'BIT']
);
});
});
describe('transformArguments', () => {
it('simple', () => {
assert.deepEqual(
BITCOUNT.transformArguments('key'),
['BITCOUNT', 'key']
);
});
testUtils.testWithClient('client.bitCount', async client => {
assert.equal(
await client.bitCount('key'),
0
describe('with range', () => {
it('simple', () => {
assert.deepEqual(
BITCOUNT.transformArguments('key', {
start: 0,
end: 1
}),
['BITCOUNT', 'key', '0', '1']
);
}, GLOBAL.SERVERS.OPEN);
});
it('with mode', () => {
assert.deepEqual(
BITCOUNT.transformArguments('key', {
start: 0,
end: 1,
mode: 'BIT'
}),
['BITCOUNT', 'key', '0', '1', 'BIT']
);
});
});
});
testUtils.testAll('bitCount', async client => {
assert.equal(
await client.bitCount('key'),
0
);
}, {
client: GLOBAL.SERVERS.OPEN,
cluster: GLOBAL.CLUSTERS.OPEN
});
});

View File

@@ -1,33 +1,29 @@
import { RedisCommandArgument, RedisCommandArguments } from '.';
import { RedisArgument, NumberReply, Command } from '../RESP/types';
export const FIRST_KEY_INDEX = 1;
export const IS_READ_ONLY = true;
interface BitCountRange {
start: number;
end: number;
mode?: 'BYTE' | 'BIT';
export interface BitCountRange {
start: number;
end: number;
mode?: 'BYTE' | 'BIT';
}
export function transformArguments(
key: RedisCommandArgument,
range?: BitCountRange
): RedisCommandArguments {
export default {
FIRST_KEY_INDEX: 1,
IS_READ_ONLY: true,
transformArguments(key: RedisArgument, range?: BitCountRange) {
const args = ['BITCOUNT', key];
if (range) {
args.push(
range.start.toString(),
range.end.toString()
);
args.push(
range.start.toString(),
range.end.toString()
);
if (range.mode) {
args.push(range.mode);
}
if (range.mode) {
args.push(range.mode);
}
}
return args;
}
export declare function transformReply(): number;
},
transformReply: undefined as unknown as () => NumberReply
} as const satisfies Command;

View File

@@ -1,46 +1,55 @@
import { strict as assert } from 'assert';
import { strict as assert } from 'node:assert';
import testUtils, { GLOBAL } from '../test-utils';
import { transformArguments } from './BITFIELD';
import BITFIELD from './BITFIELD';
describe('BITFIELD', () => {
it('transformArguments', () => {
assert.deepEqual(
transformArguments('key', [{
operation: 'OVERFLOW',
behavior: 'WRAP'
}, {
operation: 'GET',
encoding: 'i8',
offset: 0
}, {
operation: 'OVERFLOW',
behavior: 'SAT'
}, {
operation: 'SET',
encoding: 'i16',
offset: 1,
value: 0
}, {
operation: 'OVERFLOW',
behavior: 'FAIL'
}, {
operation: 'INCRBY',
encoding: 'i32',
offset: 2,
increment: 1
}]),
['BITFIELD', 'key', 'OVERFLOW', 'WRAP', 'GET', 'i8', '0', 'OVERFLOW', 'SAT', 'SET', 'i16', '1', '0', 'OVERFLOW', 'FAIL', 'INCRBY', 'i32', '2', '1']
);
});
it('transformArguments', () => {
assert.deepEqual(
BITFIELD.transformArguments('key', [{
operation: 'OVERFLOW',
behavior: 'WRAP'
}, {
operation: 'GET',
encoding: 'i8',
offset: 0
}, {
operation: 'OVERFLOW',
behavior: 'SAT'
}, {
operation: 'SET',
encoding: 'i16',
offset: 1,
value: 0
}, {
operation: 'OVERFLOW',
behavior: 'FAIL'
}, {
operation: 'INCRBY',
encoding: 'i32',
offset: 2,
increment: 1
}]),
['BITFIELD', 'key', 'OVERFLOW', 'WRAP', 'GET', 'i8', '0', 'OVERFLOW', 'SAT', 'SET', 'i16', '1', '0', 'OVERFLOW', 'FAIL', 'INCRBY', 'i32', '2', '1']
);
});
testUtils.testWithClient('client.bitField', async client => {
assert.deepEqual(
await client.bitField('key', [{
operation: 'GET',
encoding: 'i8',
offset: 0
}]),
[0]
);
}, GLOBAL.SERVERS.OPEN);
testUtils.testAll('bitField', async client => {
const a = client.bitField('key', [{
operation: 'GET',
encoding: 'i8',
offset: 0
}]);
assert.deepEqual(
await client.bitField('key', [{
operation: 'GET',
encoding: 'i8',
offset: 0
}]),
[0]
);
}, {
client: GLOBAL.SERVERS.OPEN,
cluster: GLOBAL.CLUSTERS.OPEN
});
});

View File

@@ -1,80 +1,87 @@
export const FIRST_KEY_INDEX = 1;
import { RedisArgument, ArrayReply, NumberReply, NullReply, Command } from '../RESP/types';
export type BitFieldEncoding = `${'i' | 'u'}${number}`;
export interface BitFieldOperation<S extends string> {
operation: S;
operation: S;
}
export interface BitFieldGetOperation extends BitFieldOperation<'GET'> {
encoding: BitFieldEncoding;
offset: number | string;
encoding: BitFieldEncoding;
offset: number | string;
}
interface BitFieldSetOperation extends BitFieldOperation<'SET'> {
encoding: BitFieldEncoding;
offset: number | string;
value: number;
export interface BitFieldSetOperation extends BitFieldOperation<'SET'> {
encoding: BitFieldEncoding;
offset: number | string;
value: number;
}
interface BitFieldIncrByOperation extends BitFieldOperation<'INCRBY'> {
encoding: BitFieldEncoding;
offset: number | string;
increment: number;
export interface BitFieldIncrByOperation extends BitFieldOperation<'INCRBY'> {
encoding: BitFieldEncoding;
offset: number | string;
increment: number;
}
interface BitFieldOverflowOperation extends BitFieldOperation<'OVERFLOW'> {
behavior: string;
export interface BitFieldOverflowOperation extends BitFieldOperation<'OVERFLOW'> {
behavior: string;
}
type BitFieldOperations = Array<
BitFieldGetOperation |
BitFieldSetOperation |
BitFieldIncrByOperation |
BitFieldOverflowOperation
export type BitFieldOperations = Array<
BitFieldGetOperation |
BitFieldSetOperation |
BitFieldIncrByOperation |
BitFieldOverflowOperation
>;
export function transformArguments(key: string, operations: BitFieldOperations): Array<string> {
export type BitFieldRoOperations = Array<
Omit<BitFieldGetOperation, 'operation'>
>;
export default {
FIRST_KEY_INDEX: 1,
IS_READ_ONLY: false,
transformArguments(key: RedisArgument, operations: BitFieldOperations) {
const args = ['BITFIELD', key];
for (const options of operations) {
switch (options.operation) {
case 'GET':
args.push(
'GET',
options.encoding,
options.offset.toString()
);
break;
switch (options.operation) {
case 'GET':
args.push(
'GET',
options.encoding,
options.offset.toString()
);
break;
case 'SET':
args.push(
'SET',
options.encoding,
options.offset.toString(),
options.value.toString()
);
break;
case 'SET':
args.push(
'SET',
options.encoding,
options.offset.toString(),
options.value.toString()
);
break;
case 'INCRBY':
args.push(
'INCRBY',
options.encoding,
options.offset.toString(),
options.increment.toString()
);
break;
case 'INCRBY':
args.push(
'INCRBY',
options.encoding,
options.offset.toString(),
options.increment.toString()
);
break;
case 'OVERFLOW':
args.push(
'OVERFLOW',
options.behavior
);
break;
}
case 'OVERFLOW':
args.push(
'OVERFLOW',
options.behavior
);
break;
}
}
return args;
}
export declare function transformReply(): Array<number | null>;
},
transformReply: undefined as unknown as () => ArrayReply<NumberReply | NullReply>
} as const satisfies Command;

View File

@@ -1,27 +1,30 @@
import { strict as assert } from 'assert';
import { strict as assert } from 'node:assert';
import testUtils, { GLOBAL } from '../test-utils';
import { transformArguments } from './BITFIELD_RO';
import BITFIELD_RO from './BITFIELD_RO';
describe('BITFIELD RO', () => {
testUtils.isVersionGreaterThanHook([6, 2]);
describe('BITFIELD_RO', () => {
testUtils.isVersionGreaterThanHook([6, 2]);
it('transformArguments', () => {
assert.deepEqual(
transformArguments('key', [{
encoding: 'i8',
offset: 0
}]),
['BITFIELD_RO', 'key', 'GET', 'i8', '0']
);
});
it('transformArguments', () => {
assert.deepEqual(
BITFIELD_RO.transformArguments('key', [{
encoding: 'i8',
offset: 0
}]),
['BITFIELD_RO', 'key', 'GET', 'i8', '0']
);
});
testUtils.testWithClient('client.bitFieldRo', async client => {
assert.deepEqual(
await client.bitFieldRo('key', [{
encoding: 'i8',
offset: 0
}]),
[0]
);
}, GLOBAL.SERVERS.OPEN);
testUtils.testAll('bitFieldRo', async client => {
assert.deepEqual(
await client.bitFieldRo('key', [{
encoding: 'i8',
offset: 0
}]),
[0]
);
}, {
client: GLOBAL.SERVERS.OPEN,
cluster: GLOBAL.CLUSTERS.OPEN
});
});

View File

@@ -1,26 +1,25 @@
import { RedisArgument, ArrayReply, NumberReply, Command } from '../RESP/types';
import { BitFieldGetOperation } from './BITFIELD';
export const FIRST_KEY_INDEX = 1;
export const IS_READ_ONLY = true;
type BitFieldRoOperations = Array<
Omit<BitFieldGetOperation, 'operation'> &
Partial<Pick<BitFieldGetOperation, 'operation'>>
export type BitFieldRoOperations = Array<
Omit<BitFieldGetOperation, 'operation'>
>;
export function transformArguments(key: string, operations: BitFieldRoOperations): Array<string> {
export default {
FIRST_KEY_INDEX: 1,
IS_READ_ONLY: true,
transformArguments(key: RedisArgument, operations: BitFieldRoOperations) {
const args = ['BITFIELD_RO', key];
for (const operation of operations) {
args.push(
'GET',
operation.encoding,
operation.offset.toString()
);
args.push(
'GET',
operation.encoding,
operation.offset.toString()
);
}
return args;
}
export declare function transformReply(): Array<number | null>;
},
transformReply: undefined as unknown as () => ArrayReply<NumberReply>
} as const satisfies Command;

View File

@@ -1,35 +1,31 @@
import { strict as assert } from 'assert';
import { strict as assert } from 'node:assert';
import testUtils, { GLOBAL } from '../test-utils';
import { transformArguments } from './BITOP';
import BITOP from './BITOP';
describe('BITOP', () => {
describe('transformArguments', () => {
it('single key', () => {
assert.deepEqual(
transformArguments('AND', 'destKey', 'key'),
['BITOP', 'AND', 'destKey', 'key']
);
});
it('multiple keys', () => {
assert.deepEqual(
transformArguments('AND', 'destKey', ['1', '2']),
['BITOP', 'AND', 'destKey', '1', '2']
);
});
describe('transformArguments', () => {
it('single key', () => {
assert.deepEqual(
BITOP.transformArguments('AND', 'destKey', 'key'),
['BITOP', 'AND', 'destKey', 'key']
);
});
testUtils.testWithClient('client.bitOp', async client => {
assert.equal(
await client.bitOp('AND', 'destKey', 'key'),
0
);
}, GLOBAL.SERVERS.OPEN);
it('multiple keys', () => {
assert.deepEqual(
BITOP.transformArguments('AND', 'destKey', ['1', '2']),
['BITOP', 'AND', 'destKey', '1', '2']
);
});
});
testUtils.testWithCluster('cluster.bitOp', async cluster => {
assert.equal(
await cluster.bitOp('AND', '{tag}destKey', '{tag}key'),
0
);
}, GLOBAL.CLUSTERS.OPEN);
testUtils.testAll('bitOp', async client => {
assert.equal(
await client.bitOp('AND', '{tag}destKey', '{tag}key'),
0
);
}, {
client: GLOBAL.SERVERS.OPEN,
cluster: GLOBAL.CLUSTERS.OPEN
});
});

View File

@@ -1,16 +1,17 @@
import { RedisCommandArgument, RedisCommandArguments } from '.';
import { pushVerdictArguments } from './generic-transformers';
import { NumberReply, Command, RedisArgument } from '../RESP/types';
import { RedisVariadicArgument, pushVariadicArguments } from './generic-transformers';
export const FIRST_KEY_INDEX = 2;
export type BitOperations = 'AND' | 'OR' | 'XOR' | 'NOT';
type BitOperations = 'AND' | 'OR' | 'XOR' | 'NOT';
export function transformArguments(
export default {
FIRST_KEY_INDEX: 2,
IS_READ_ONLY: false,
transformArguments(
operation: BitOperations,
destKey: RedisCommandArgument,
key: RedisCommandArgument | Array<RedisCommandArgument>
): RedisCommandArguments {
return pushVerdictArguments(['BITOP', operation, destKey], key);
}
export declare function transformReply(): number;
destKey: RedisArgument,
key: RedisVariadicArgument
) {
return pushVariadicArguments(['BITOP', operation, destKey], key);
},
transformReply: undefined as unknown as () => NumberReply
} as const satisfies Command;

View File

@@ -1,49 +1,45 @@
import { strict as assert } from 'assert';
import { strict as assert } from 'node:assert';
import testUtils, { GLOBAL } from '../test-utils';
import { transformArguments } from './BITPOS';
import BITPOS from './BITPOS';
describe('BITPOS', () => {
describe('transformArguments', () => {
it('simple', () => {
assert.deepEqual(
transformArguments('key', 1),
['BITPOS', 'key', '1']
);
});
it('with start', () => {
assert.deepEqual(
transformArguments('key', 1, 1),
['BITPOS', 'key', '1', '1']
);
});
it('with start and end', () => {
assert.deepEqual(
transformArguments('key', 1, 1, -1),
['BITPOS', 'key', '1', '1', '-1']
);
});
it('with start, end and mode', () => {
assert.deepEqual(
transformArguments('key', 1, 1, -1, 'BIT'),
['BITPOS', 'key', '1', '1', '-1', 'BIT']
);
});
describe('transformArguments', () => {
it('simple', () => {
assert.deepEqual(
BITPOS.transformArguments('key', 1),
['BITPOS', 'key', '1']
);
});
testUtils.testWithClient('client.bitPos', async client => {
assert.equal(
await client.bitPos('key', 1, 1),
-1
);
}, GLOBAL.SERVERS.OPEN);
it('with start', () => {
assert.deepEqual(
BITPOS.transformArguments('key', 1, 1),
['BITPOS', 'key', '1', '1']
);
});
testUtils.testWithCluster('cluster.bitPos', async cluster => {
assert.equal(
await cluster.bitPos('key', 1, 1),
-1
);
}, GLOBAL.CLUSTERS.OPEN);
it('with start and end', () => {
assert.deepEqual(
BITPOS.transformArguments('key', 1, 1, -1),
['BITPOS', 'key', '1', '1', '-1']
);
});
it('with start, end and mode', () => {
assert.deepEqual(
BITPOS.transformArguments('key', 1, 1, -1, 'BIT'),
['BITPOS', 'key', '1', '1', '-1', 'BIT']
);
});
});
testUtils.testAll('bitPos', async client => {
assert.equal(
await client.bitPos('key', 1, 1),
-1
);
}, {
client: GLOBAL.SERVERS.OPEN,
cluster: GLOBAL.CLUSTERS.OPEN
});
});

View File

@@ -1,32 +1,31 @@
import { RedisCommandArgument, RedisCommandArguments } from '.';
import { RedisArgument, NumberReply, Command } from '../RESP/types';
import { BitValue } from './generic-transformers';
export const FIRST_KEY_INDEX = 1;
export const IS_READ_ONLY = true;
export function transformArguments(
key: RedisCommandArgument,
export default {
FIRST_KEY_INDEX: 1,
IS_READ_ONLY: true,
transformArguments(
key: RedisArgument,
bit: BitValue,
start?: number,
end?: number,
mode?: 'BYTE' | 'BIT'
): RedisCommandArguments {
) {
const args = ['BITPOS', key, bit.toString()];
if (typeof start === 'number') {
args.push(start.toString());
if (start !== undefined) {
args.push(start.toString());
}
if (typeof end === 'number') {
args.push(end.toString());
if (end !== undefined) {
args.push(end.toString());
}
if (mode) {
args.push(mode);
args.push(mode);
}
return args;
}
export declare function transformReply(): number;
},
transformReply: undefined as unknown as () => NumberReply
} as const satisfies Command;

View File

@@ -1,43 +1,35 @@
import { strict as assert } from 'assert';
import testUtils, { GLOBAL } from '../test-utils';
import { transformArguments } from './BLMOVE';
import { commandOptions } from '../../index';
import { strict as assert } from 'node:assert';
import testUtils, { GLOBAL, BLOCKING_MIN_VALUE } from '../test-utils';
import BLMOVE from './BLMOVE';
describe('BLMOVE', () => {
testUtils.isVersionGreaterThanHook([6, 2]);
testUtils.isVersionGreaterThanHook([6, 2]);
it('transformArguments', () => {
assert.deepEqual(
transformArguments('source', 'destination', 'LEFT', 'RIGHT', 0),
['BLMOVE', 'source', 'destination', 'LEFT', 'RIGHT', '0']
);
});
it('transformArguments', () => {
assert.deepEqual(
BLMOVE.transformArguments('source', 'destination', 'LEFT', 'RIGHT', 0),
['BLMOVE', 'source', 'destination', 'LEFT', 'RIGHT', '0']
);
});
testUtils.testWithClient('client.blMove', async client => {
const [blMoveReply] = await Promise.all([
client.blMove(commandOptions({
isolated: true
}), 'source', 'destination', 'LEFT', 'RIGHT', 0),
client.lPush('source', 'element')
]);
testUtils.testAll('blMove - null', async client => {
assert.equal(
await client.blMove('{tag}source', '{tag}destination', 'LEFT', 'RIGHT', BLOCKING_MIN_VALUE),
null
);
}, {
client: GLOBAL.SERVERS.OPEN,
cluster: GLOBAL.CLUSTERS.OPEN
});
assert.equal(
blMoveReply,
'element'
);
}, GLOBAL.SERVERS.OPEN);
testUtils.testWithCluster('cluster.blMove', async cluster => {
const [blMoveReply] = await Promise.all([
cluster.blMove(commandOptions({
isolated: true
}), '{tag}source', '{tag}destination', 'LEFT', 'RIGHT', 0),
cluster.lPush('{tag}source', 'element')
]);
assert.equal(
blMoveReply,
'element'
);
}, GLOBAL.CLUSTERS.OPEN);
testUtils.testAll('blMove - with member', async client => {
const [, reply] = await Promise.all([
client.lPush('{tag}source', 'element'),
client.blMove('{tag}source', '{tag}destination', 'LEFT', 'RIGHT', BLOCKING_MIN_VALUE)
]);
assert.equal(reply, 'element');
}, {
client: GLOBAL.SERVERS.OPEN,
cluster: GLOBAL.CLUSTERS.OPEN
});
});

View File

@@ -1,23 +1,24 @@
import { RedisCommandArgument, RedisCommandArguments } from '.';
import { RedisArgument, BlobStringReply, NullReply, Command } from '../RESP/types';
import { ListSide } from './generic-transformers';
export const FIRST_KEY_INDEX = 1;
export function transformArguments(
source: RedisCommandArgument,
destination: RedisCommandArgument,
sourceDirection: ListSide,
destinationDirection: ListSide,
export default {
FIRST_KEY_INDEX: 1,
IS_READ_ONLY: false,
transformArguments(
source: RedisArgument,
destination: RedisArgument,
sourceSide: ListSide,
destinationSide: ListSide,
timeout: number
): RedisCommandArguments {
) {
return [
'BLMOVE',
source,
destination,
sourceDirection,
destinationDirection,
timeout.toString()
'BLMOVE',
source,
destination,
sourceSide,
destinationSide,
timeout.toString()
];
}
export declare function transformReply(): RedisCommandArgument | null;
},
transformReply: undefined as unknown as () => BlobStringReply | NullReply
} as const satisfies Command;

View File

@@ -1,32 +1,49 @@
import { strict as assert } from 'assert';
import testUtils, { GLOBAL } from '../test-utils';
import { transformArguments } from './BLMPOP';
import { strict as assert } from 'node:assert';
import testUtils, { GLOBAL, BLOCKING_MIN_VALUE } from '../test-utils';
import BLMPOP from './BLMPOP';
describe('BLMPOP', () => {
testUtils.isVersionGreaterThanHook([7]);
testUtils.isVersionGreaterThanHook([7]);
describe('transformArguments', () => {
it('simple', () => {
assert.deepEqual(
transformArguments(0, 'key', 'LEFT'),
['BLMPOP', '0', '1', 'key', 'LEFT']
);
});
it('with COUNT', () => {
assert.deepEqual(
transformArguments(0, 'key', 'LEFT', {
COUNT: 2
}),
['BLMPOP', '0', '1', 'key', 'LEFT', 'COUNT', '2']
);
});
describe('transformArguments', () => {
it('simple', () => {
assert.deepEqual(
BLMPOP.transformArguments(0, 'key', 'LEFT'),
['BLMPOP', '0', '1', 'key', 'LEFT']
);
});
testUtils.testWithClient('client.blmPop', async client => {
assert.deepEqual(
await client.blmPop(1, 'key', 'RIGHT'),
null
);
}, GLOBAL.SERVERS.OPEN);
it('with COUNT', () => {
assert.deepEqual(
BLMPOP.transformArguments(0, 'key', 'LEFT', {
COUNT: 1
}),
['BLMPOP', '0', '1', 'key', 'LEFT', 'COUNT', '1']
);
});
});
testUtils.testAll('blmPop - null', async client => {
assert.equal(
await client.blmPop(BLOCKING_MIN_VALUE, 'key', 'RIGHT'),
null
);
}, {
client: GLOBAL.SERVERS.OPEN,
cluster: GLOBAL.CLUSTERS.OPEN
});
testUtils.testAll('blmPop - with member', async client => {
const [, reply] = await Promise.all([
client.lPush('key', 'element'),
client.blmPop(BLOCKING_MIN_VALUE, 'key', 'RIGHT')
]);
assert.deepEqual(reply, [
'key',
['element']
]);
}, {
client: GLOBAL.SERVERS.OPEN,
cluster: GLOBAL.CLUSTERS.OPEN
});
});

View File

@@ -1,20 +1,17 @@
import { RedisCommandArgument, RedisCommandArguments } from '.';
import { transformLMPopArguments, LMPopOptions, ListSide } from './generic-transformers';
import { Command } from '../RESP/types';
import LMPOP, { LMPopArguments, transformLMPopArguments } from './LMPOP';
export const FIRST_KEY_INDEX = 3;
export function transformArguments(
export default {
FIRST_KEY_INDEX: 3,
IS_READ_ONLY: false,
transformArguments(
timeout: number,
keys: RedisCommandArgument | Array<RedisCommandArgument>,
side: ListSide,
options?: LMPopOptions
): RedisCommandArguments {
...args: LMPopArguments
) {
return transformLMPopArguments(
['BLMPOP', timeout.toString()],
keys,
side,
options
['BLMPOP', timeout.toString()],
...args
);
}
export { transformReply } from './LMPOP';
},
transformReply: LMPOP.transformReply
} as const satisfies Command;

View File

@@ -1,79 +1,46 @@
import { strict as assert } from 'assert';
import testUtils, { GLOBAL } from '../test-utils';
import { transformArguments, transformReply } from './BLPOP';
import { commandOptions } from '../../index';
import { strict as assert } from 'node:assert';
import testUtils, { GLOBAL, BLOCKING_MIN_VALUE } from '../test-utils';
import BLPOP from './BLPOP';
describe('BLPOP', () => {
describe('transformArguments', () => {
it('single', () => {
assert.deepEqual(
transformArguments('key', 0),
['BLPOP', 'key', '0']
);
});
it('multiple', () => {
assert.deepEqual(
transformArguments(['key1', 'key2'], 0),
['BLPOP', 'key1', 'key2', '0']
);
});
describe('transformArguments', () => {
it('single', () => {
assert.deepEqual(
BLPOP.transformArguments('key', 0),
['BLPOP', 'key', '0']
);
});
describe('transformReply', () => {
it('null', () => {
assert.equal(
transformReply(null),
null
);
});
it('member', () => {
assert.deepEqual(
transformReply(['key', 'element']),
{
key: 'key',
element: 'element'
}
);
});
it('multiple', () => {
assert.deepEqual(
BLPOP.transformArguments(['1', '2'], 0),
['BLPOP', '1', '2', '0']
);
});
});
testUtils.testWithClient('client.blPop', async client => {
const [ blPopReply ] = await Promise.all([
client.blPop(
commandOptions({ isolated: true }),
'key',
1
),
client.lPush('key', 'element'),
]);
testUtils.testAll('blPop - null', async client => {
assert.equal(
await client.blPop('key', BLOCKING_MIN_VALUE),
null
);
}, {
client: GLOBAL.SERVERS.OPEN,
cluster: GLOBAL.CLUSTERS.OPEN
});
assert.deepEqual(
blPopReply,
{
key: 'key',
element: 'element'
}
);
}, GLOBAL.SERVERS.OPEN);
testUtils.testAll('blPop - with member', async client => {
const [, reply] = await Promise.all([
client.lPush('key', 'element'),
client.blPop('key', 1)
]);
testUtils.testWithCluster('cluster.blPop', async cluster => {
const [ blPopReply ] = await Promise.all([
cluster.blPop(
commandOptions({ isolated: true }),
'key',
1
),
cluster.lPush('key', 'element')
]);
assert.deepEqual(
blPopReply,
{
key: 'key',
element: 'element'
}
);
}, GLOBAL.CLUSTERS.OPEN);
assert.deepEqual(reply, {
key: 'key',
element: 'element'
});
}, {
client: GLOBAL.SERVERS.OPEN,
cluster: GLOBAL.CLUSTERS.OPEN
});
});

View File

@@ -1,31 +1,23 @@
import { RedisCommandArgument, RedisCommandArguments } from '.';
import { pushVerdictArguments } from './generic-transformers';
import { UnwrapReply, NullReply, TuplesReply, BlobStringReply, Command } from '../RESP/types';
import { RedisVariadicArgument, pushVariadicArguments } from './generic-transformers';
export const FIRST_KEY_INDEX = 1;
export function transformArguments(
keys: RedisCommandArgument | Array<RedisCommandArgument>,
export default {
FIRST_KEY_INDEX: 1,
IS_READ_ONLY: true,
transformArguments(
key: RedisVariadicArgument,
timeout: number
): RedisCommandArguments {
const args = pushVerdictArguments(['BLPOP'], keys);
) {
const args = pushVariadicArguments(['BLPOP'], key);
args.push(timeout.toString());
return args;
}
type BLPopRawReply = null | [RedisCommandArgument, RedisCommandArgument];
type BLPopReply = null | {
key: RedisCommandArgument;
element: RedisCommandArgument;
};
export function transformReply(reply: BLPopRawReply): BLPopReply {
},
transformReply(reply: UnwrapReply<NullReply | TuplesReply<[BlobStringReply, BlobStringReply]>>) {
if (reply === null) return null;
return {
key: reply[0],
element: reply[1]
key: reply[0],
element: reply[1]
};
}
}
} as const satisfies Command;

View File

@@ -1,79 +1,46 @@
import { strict as assert } from 'assert';
import testUtils, { GLOBAL } from '../test-utils';
import { transformArguments, transformReply } from './BRPOP';
import { commandOptions } from '../../index';
import { strict as assert } from 'node:assert';
import testUtils, { GLOBAL, BLOCKING_MIN_VALUE } from '../test-utils';
import BRPOP from './BRPOP';
describe('BRPOP', () => {
describe('transformArguments', () => {
it('single', () => {
assert.deepEqual(
transformArguments('key', 0),
['BRPOP', 'key', '0']
);
});
it('multiple', () => {
assert.deepEqual(
transformArguments(['key1', 'key2'], 0),
['BRPOP', 'key1', 'key2', '0']
);
});
describe('transformArguments', () => {
it('single', () => {
assert.deepEqual(
BRPOP.transformArguments('key', 0),
['BRPOP', 'key', '0']
);
});
describe('transformReply', () => {
it('null', () => {
assert.equal(
transformReply(null),
null
);
});
it('member', () => {
assert.deepEqual(
transformReply(['key', 'element']),
{
key: 'key',
element: 'element'
}
);
});
it('multiple', () => {
assert.deepEqual(
BRPOP.transformArguments(['1', '2'], 0),
['BRPOP', '1', '2', '0']
);
});
});
testUtils.testWithClient('client.brPop', async client => {
const [ brPopReply ] = await Promise.all([
client.brPop(
commandOptions({ isolated: true }),
'key',
1
),
client.lPush('key', 'element'),
]);
testUtils.testAll('brPop - null', async client => {
assert.equal(
await client.brPop('key', BLOCKING_MIN_VALUE),
null
);
}, {
client: GLOBAL.SERVERS.OPEN,
cluster: GLOBAL.CLUSTERS.OPEN
});
assert.deepEqual(
brPopReply,
{
key: 'key',
element: 'element'
}
);
}, GLOBAL.SERVERS.OPEN);
testUtils.testAll('brPopblPop - with member', async client => {
const [, reply] = await Promise.all([
client.lPush('key', 'element'),
client.brPop('key', 1)
]);
testUtils.testWithCluster('cluster.brPop', async cluster => {
const [ brPopReply ] = await Promise.all([
cluster.brPop(
commandOptions({ isolated: true }),
'key',
1
),
cluster.lPush('key', 'element'),
]);
assert.deepEqual(
brPopReply,
{
key: 'key',
element: 'element'
}
);
}, GLOBAL.CLUSTERS.OPEN);
assert.deepEqual(reply, {
key: 'key',
element: 'element'
});
}, {
client: GLOBAL.SERVERS.OPEN,
cluster: GLOBAL.CLUSTERS.OPEN
});
});

View File

@@ -1,17 +1,17 @@
import { RedisCommandArgument, RedisCommandArguments } from '.';
import { pushVerdictArguments } from './generic-transformers';
import { Command } from '../RESP/types';
import { RedisVariadicArgument, pushVariadicArguments } from './generic-transformers';
import BLPOP from './BLPOP';
export const FIRST_KEY_INDEX = 1;
export function transformArguments(
key: RedisCommandArgument | Array<RedisCommandArgument>,
export default {
FIRST_KEY_INDEX: 1,
IS_READ_ONLY: true,
transformArguments(
key: RedisVariadicArgument,
timeout: number
): RedisCommandArguments {
const args = pushVerdictArguments(['BRPOP'], key);
) {
const args = pushVariadicArguments(['BRPOP'], key);
args.push(timeout.toString());
return args;
}
export { transformReply } from './BLPOP';
},
transformReply: BLPOP.transformReply
} as const satisfies Command;

View File

@@ -1,47 +1,42 @@
import { strict as assert } from 'assert';
import testUtils, { GLOBAL } from '../test-utils';
import { transformArguments } from './BRPOPLPUSH';
import { commandOptions } from '../../index';
import { strict as assert } from 'node:assert';
import testUtils, { GLOBAL, BLOCKING_MIN_VALUE } from '../test-utils';
import BRPOPLPUSH from './BRPOPLPUSH';
describe('BRPOPLPUSH', () => {
it('transformArguments', () => {
assert.deepEqual(
transformArguments('source', 'destination', 0),
['BRPOPLPUSH', 'source', 'destination', '0']
);
});
it('transformArguments', () => {
assert.deepEqual(
BRPOPLPUSH.transformArguments('source', 'destination', 0),
['BRPOPLPUSH', 'source', 'destination', '0']
);
});
testUtils.testWithClient('client.brPopLPush', async client => {
const [ popReply ] = await Promise.all([
client.brPopLPush(
commandOptions({ isolated: true }),
'source',
'destination',
0
),
client.lPush('source', 'element')
]);
testUtils.testAll('brPopLPush - null', async client => {
assert.equal(
await client.brPopLPush(
'{tag}source',
'{tag}destination',
BLOCKING_MIN_VALUE
),
null
);
}, {
client: GLOBAL.SERVERS.OPEN,
cluster: GLOBAL.CLUSTERS.OPEN
});
assert.equal(
popReply,
'element'
);
}, GLOBAL.SERVERS.OPEN);
testUtils.testAll('brPopLPush - with member', async client => {
const [, reply] = await Promise.all([
client.lPush('{tag}source', 'element'),
client.brPopLPush(
'{tag}source',
'{tag}destination',
0
)
]);
testUtils.testWithCluster('cluster.brPopLPush', async cluster => {
const [ popReply ] = await Promise.all([
cluster.brPopLPush(
commandOptions({ isolated: true }),
'{tag}source',
'{tag}destination',
0
),
cluster.lPush('{tag}source', 'element')
]);
assert.equal(
popReply,
'element'
);
}, GLOBAL.CLUSTERS.OPEN);
assert.equal(reply, 'element');
}, {
client: GLOBAL.SERVERS.OPEN,
cluster: GLOBAL.CLUSTERS.OPEN
});
});

View File

@@ -1,13 +1,14 @@
import { RedisCommandArgument, RedisCommandArguments } from '.';
import { RedisArgument, BlobStringReply, NullReply, Command } from '../RESP/types';
export const FIRST_KEY_INDEX = 1;
export function transformArguments(
source: RedisCommandArgument,
destination: RedisCommandArgument,
export default {
FIRST_KEY_INDEX: 1,
IS_READ_ONLY: false,
transformArguments(
source: RedisArgument,
destination: RedisArgument,
timeout: number
): RedisCommandArguments {
) {
return ['BRPOPLPUSH', source, destination, timeout.toString()];
}
export declare function transformReply(): RedisCommandArgument | null;
},
transformReply: undefined as unknown as () => BlobStringReply | NullReply
} as const satisfies Command;

View File

@@ -1,32 +1,55 @@
import { strict as assert } from 'assert';
import testUtils, { GLOBAL } from '../test-utils';
import { transformArguments } from './BZMPOP';
import { strict as assert } from 'node:assert';
import testUtils, { GLOBAL, BLOCKING_MIN_VALUE } from '../test-utils';
import BZMPOP from './BZMPOP';
describe('BZMPOP', () => {
testUtils.isVersionGreaterThanHook([7]);
testUtils.isVersionGreaterThanHook([7]);
describe('transformArguments', () => {
it('simple', () => {
assert.deepEqual(
transformArguments(0, 'key', 'MIN'),
['BZMPOP', '0', '1', 'key', 'MIN']
);
});
it('with COUNT', () => {
assert.deepEqual(
transformArguments(0, 'key', 'MIN', {
COUNT: 2
}),
['BZMPOP', '0', '1', 'key', 'MIN', 'COUNT', '2']
);
});
describe('transformArguments', () => {
it('simple', () => {
assert.deepEqual(
BZMPOP.transformArguments(0, 'key', 'MIN'),
['BZMPOP', '0', '1', 'key', 'MIN']
);
});
testUtils.testWithClient('client.bzmPop', async client => {
assert.deepEqual(
await client.bzmPop(1, 'key', 'MAX'),
null
);
}, GLOBAL.SERVERS.OPEN);
it('with COUNT', () => {
assert.deepEqual(
BZMPOP.transformArguments(0, 'key', 'MIN', {
COUNT: 2
}),
['BZMPOP', '0', '1', 'key', 'MIN', 'COUNT', '2']
);
});
});
testUtils.testAll('bzmPop - null', async client => {
assert.equal(
await client.bzmPop(BLOCKING_MIN_VALUE, 'key', 'MAX'),
null
);
}, {
client: GLOBAL.SERVERS.OPEN,
cluster: GLOBAL.SERVERS.OPEN
});
testUtils.testAll('bzmPop - with member', async client => {
const key = 'key',
member = {
value: 'a',
score: 1
},
[, reply] = await Promise.all([
client.zAdd(key, member),
client.bzmPop(BLOCKING_MIN_VALUE, key, 'MAX')
]);
assert.deepEqual(reply, {
key,
members: [member]
});
}, {
client: GLOBAL.SERVERS.OPEN,
cluster: GLOBAL.SERVERS.OPEN
});
});

View File

@@ -1,20 +1,11 @@
import { RedisCommandArgument, RedisCommandArguments } from '.';
import { SortedSetSide, transformZMPopArguments, ZMPopOptions } from './generic-transformers';
import { Command } from '../RESP/types';
import ZMPOP, { ZMPopArguments, transformZMPopArguments } from './ZMPOP';
export const FIRST_KEY_INDEX = 3;
export function transformArguments(
timeout: number,
keys: RedisCommandArgument | Array<RedisCommandArgument>,
side: SortedSetSide,
options?: ZMPopOptions
): RedisCommandArguments {
return transformZMPopArguments(
['BZMPOP', timeout.toString()],
keys,
side,
options
);
}
export { transformReply } from './ZMPOP';
export default {
FIRST_KEY_INDEX: 3,
IS_READ_ONLY: false,
transformArguments(timeout: number, ...args: ZMPopArguments) {
return transformZMPopArguments(['BZMPOP', timeout.toString()], ...args);
},
transformReply: ZMPOP.transformReply
} as const satisfies Command;

View File

@@ -1,65 +1,51 @@
import { strict as assert } from 'assert';
import testUtils, { GLOBAL } from '../test-utils';
import { transformArguments, transformReply } from './BZPOPMAX';
import { commandOptions } from '../../index';
import { strict as assert } from 'node:assert';
import testUtils, { GLOBAL, BLOCKING_MIN_VALUE } from '../test-utils';
import BZPOPMAX from './BZPOPMAX';
describe('BZPOPMAX', () => {
describe('transformArguments', () => {
it('single', () => {
assert.deepEqual(
transformArguments('key', 0),
['BZPOPMAX', 'key', '0']
);
});
it('multiple', () => {
assert.deepEqual(
transformArguments(['1', '2'], 0),
['BZPOPMAX', '1', '2', '0']
);
});
describe('transformArguments', () => {
it('single', () => {
assert.deepEqual(
BZPOPMAX.transformArguments('key', 0),
['BZPOPMAX', 'key', '0']
);
});
describe('transformReply', () => {
it('null', () => {
assert.equal(
transformReply(null),
null
);
});
it('member', () => {
assert.deepEqual(
transformReply(['key', 'value', '1']),
{
key: 'key',
value: 'value',
score: 1
}
);
});
it('multiple', () => {
assert.deepEqual(
BZPOPMAX.transformArguments(['1', '2'], 0),
['BZPOPMAX', '1', '2', '0']
);
});
});
testUtils.testWithClient('client.bzPopMax', async client => {
const [ bzPopMaxReply ] = await Promise.all([
client.bzPopMax(
commandOptions({ isolated: true }),
'key',
1
),
client.zAdd('key', [{
value: '1',
score: 1
}])
]);
testUtils.testAll('bzPopMax - null', async client => {
assert.equal(
await client.bzPopMax('key', BLOCKING_MIN_VALUE),
null
);
}, {
client: GLOBAL.SERVERS.OPEN,
cluster: GLOBAL.SERVERS.OPEN
});
assert.deepEqual(
bzPopMaxReply,
{
key: 'key',
value: '1',
score: 1
}
);
}, GLOBAL.SERVERS.OPEN);
testUtils.testAll('bzPopMax - with member', async client => {
const key = 'key',
member = {
value: 'a',
score: 1
},
[, reply] = await Promise.all([
client.zAdd(key, member),
client.bzPopMax(key, BLOCKING_MIN_VALUE)
]);
assert.deepEqual(reply, {
key,
...member
});
}, {
client: GLOBAL.SERVERS.OPEN,
cluster: GLOBAL.SERVERS.OPEN
});
});

View File

@@ -1,29 +1,43 @@
import { RedisCommandArgument, RedisCommandArguments } from '.';
import { pushVerdictArguments, transformNumberInfinityReply, ZMember } from './generic-transformers';
import { RedisArgument, NullReply, TuplesReply, BlobStringReply, DoubleReply, UnwrapReply, Command, TypeMapping } from '../RESP/types';
import { RedisVariadicArgument, pushVariadicArguments, transformDoubleReply } from './generic-transformers';
export const FIRST_KEY_INDEX = 1;
export function transformArguments(
key: RedisCommandArgument | Array<RedisCommandArgument>,
timeout: number
): RedisCommandArguments {
const args = pushVerdictArguments(['BZPOPMAX'], key);
args.push(timeout.toString());
return args;
export function transformBZPopArguments(
command: RedisArgument,
key: RedisVariadicArgument,
timeout: number
) {
const args = pushVariadicArguments([command], key);
args.push(timeout.toString());
return args;
}
type ZMemberRawReply = [key: RedisCommandArgument, value: RedisCommandArgument, score: RedisCommandArgument] | null;
export type BZPopArguments = typeof transformBZPopArguments extends (_: any, ...args: infer T) => any ? T : never;
type BZPopMaxReply = (ZMember & { key: RedisCommandArgument }) | null;
export function transformReply(reply: ZMemberRawReply): BZPopMaxReply | null {
if (!reply) return null;
return {
export default {
FIRST_KEY_INDEX: 1,
IS_READ_ONLY: false,
transformArguments(...args: BZPopArguments) {
return transformBZPopArguments('BZPOPMAX', ...args);
},
transformReply: {
2(
reply: UnwrapReply<NullReply | TuplesReply<[BlobStringReply, BlobStringReply, BlobStringReply]>>,
preserve?: any,
typeMapping?: TypeMapping
) {
return reply === null ? null : {
key: reply[0],
value: reply[1],
score: transformNumberInfinityReply(reply[2])
};
}
score: transformDoubleReply[2](reply[2], preserve, typeMapping)
};
},
3(reply: UnwrapReply<NullReply | TuplesReply<[BlobStringReply, BlobStringReply, DoubleReply]>>) {
return reply === null ? null : {
key: reply[0],
value: reply[1],
score: reply[2]
};
}
}
} as const satisfies Command;

View File

@@ -1,65 +1,51 @@
import { strict as assert } from 'assert';
import testUtils, { GLOBAL } from '../test-utils';
import { transformArguments, transformReply } from './BZPOPMIN';
import { commandOptions } from '../../index';
import { strict as assert } from 'node:assert';
import testUtils, { GLOBAL, BLOCKING_MIN_VALUE } from '../test-utils';
import BZPOPMIN from './BZPOPMIN';
describe('BZPOPMIN', () => {
describe('transformArguments', () => {
it('single', () => {
assert.deepEqual(
transformArguments('key', 0),
['BZPOPMIN', 'key', '0']
);
});
it('multiple', () => {
assert.deepEqual(
transformArguments(['1', '2'], 0),
['BZPOPMIN', '1', '2', '0']
);
});
describe('transformArguments', () => {
it('single', () => {
assert.deepEqual(
BZPOPMIN.transformArguments('key', 0),
['BZPOPMIN', 'key', '0']
);
});
describe('transformReply', () => {
it('null', () => {
assert.equal(
transformReply(null),
null
);
});
it('member', () => {
assert.deepEqual(
transformReply(['key', 'value', '1']),
{
key: 'key',
value: 'value',
score: 1
}
);
});
it('multiple', () => {
assert.deepEqual(
BZPOPMIN.transformArguments(['1', '2'], 0),
['BZPOPMIN', '1', '2', '0']
);
});
});
testUtils.testWithClient('client.bzPopMin', async client => {
const [ bzPopMinReply ] = await Promise.all([
client.bzPopMin(
commandOptions({ isolated: true }),
'key',
1
),
client.zAdd('key', [{
value: '1',
score: 1
}])
]);
testUtils.testAll('bzPopMin - null', async client => {
assert.equal(
await client.bzPopMin('key', BLOCKING_MIN_VALUE),
null
);
}, {
client: GLOBAL.SERVERS.OPEN,
cluster: GLOBAL.SERVERS.OPEN
});
assert.deepEqual(
bzPopMinReply,
{
key: 'key',
value: '1',
score: 1
}
);
}, GLOBAL.SERVERS.OPEN);
testUtils.testAll('bzPopMin - with member', async client => {
const key = 'key',
member = {
value: 'a',
score: 1
},
[, reply] = await Promise.all([
client.zAdd(key, member),
client.bzPopMin(key, BLOCKING_MIN_VALUE)
]);
assert.deepEqual(reply, {
key,
...member
});
}, {
client: GLOBAL.SERVERS.OPEN,
cluster: GLOBAL.SERVERS.OPEN
});
});

View File

@@ -1,17 +1,12 @@
import { RedisCommandArgument, RedisCommandArguments } from '.';
import { pushVerdictArguments } from './generic-transformers';
import { Command } from '../RESP/types';
import BZPOPMAX, { BZPopArguments, transformBZPopArguments } from './BZPOPMAX';
export const FIRST_KEY_INDEX = 1;
export default {
FIRST_KEY_INDEX: BZPOPMAX.FIRST_KEY_INDEX,
IS_READ_ONLY: BZPOPMAX.IS_READ_ONLY,
transformArguments(...args: BZPopArguments) {
return transformBZPopArguments('BZPOPMIN', ...args);
},
transformReply: BZPOPMAX.transformReply
} as const satisfies Command;
export function transformArguments(
key: RedisCommandArgument | Array<RedisCommandArgument>,
timeout: number
): RedisCommandArguments {
const args = pushVerdictArguments(['BZPOPMIN'], key);
args.push(timeout.toString());
return args;
}
export { transformReply } from './BZPOPMAX';

View File

@@ -1,20 +1,20 @@
import { strict as assert } from 'assert';
import { transformArguments } from './CLIENT_CACHING';
import { strict as assert } from 'node:assert';
import CLIENT_CACHING from './CLIENT_CACHING';
describe('CLIENT CACHING', () => {
describe('transformArguments', () => {
it('true', () => {
assert.deepEqual(
transformArguments(true),
['CLIENT', 'CACHING', 'YES']
);
});
it('false', () => {
assert.deepEqual(
transformArguments(false),
['CLIENT', 'CACHING', 'NO']
);
});
describe('transformArguments', () => {
it('true', () => {
assert.deepEqual(
CLIENT_CACHING.transformArguments(true),
['CLIENT', 'CACHING', 'YES']
);
});
it('false', () => {
assert.deepEqual(
CLIENT_CACHING.transformArguments(false),
['CLIENT', 'CACHING', 'NO']
);
});
});
});

View File

@@ -1,11 +1,14 @@
import { RedisCommandArguments } from '.';
import { SimpleStringReply, Command } from '../RESP/types';
export function transformArguments(value: boolean): RedisCommandArguments {
export default {
FIRST_KEY_INDEX: undefined,
IS_READ_ONLY: true,
transformArguments(value: boolean) {
return [
'CLIENT',
'CACHING',
value ? 'YES' : 'NO'
'CLIENT',
'CACHING',
value ? 'YES' : 'NO'
];
}
export declare function transformReply(): 'OK' | Buffer;
},
transformReply: undefined as unknown as () => SimpleStringReply<'OK'>
} as const satisfies Command;

View File

@@ -1,11 +1,19 @@
import { strict as assert } from 'assert';
import { transformArguments } from './CLIENT_GETNAME';
import { strict as assert } from 'node:assert';
import testUtils, { GLOBAL } from '../test-utils';
import CLIENT_GETNAME from './CLIENT_GETNAME';
describe('CLIENT GETNAME', () => {
it('transformArguments', () => {
assert.deepEqual(
transformArguments(),
['CLIENT', 'GETNAME']
);
});
it('transformArguments', () => {
assert.deepEqual(
CLIENT_GETNAME.transformArguments(),
['CLIENT', 'GETNAME']
);
});
testUtils.testWithClient('client.clientGetName', async client => {
assert.equal(
await client.clientGetName(),
null
);
}, GLOBAL.SERVERS.OPEN);
});

View File

@@ -1,7 +1,13 @@
import { RedisCommandArguments } from '.';
import { BlobStringReply, NullReply, Command } from '../RESP/types';
export function transformArguments(): RedisCommandArguments {
return ['CLIENT', 'GETNAME'];
}
export declare function transformReply(): string | null;
export default {
FIRST_KEY_INDEX: undefined,
IS_READ_ONLY: true,
transformArguments() {
return [
'CLIENT',
'GETNAME'
];
},
transformReply: undefined as unknown as () => BlobStringReply | NullReply
} as const satisfies Command;

View File

@@ -1,11 +1,11 @@
import { strict as assert } from 'assert';
import { transformArguments } from './CLIENT_GETREDIR';
import { strict as assert } from 'node:assert';
import CLIENT_GETREDIR from './CLIENT_GETREDIR';
describe('CLIENT GETREDIR', () => {
it('transformArguments', () => {
assert.deepEqual(
transformArguments(),
['CLIENT', 'GETREDIR']
);
});
it('transformArguments', () => {
assert.deepEqual(
CLIENT_GETREDIR.transformArguments(),
['CLIENT', 'GETREDIR']
);
});
});

View File

@@ -1,7 +1,10 @@
import { RedisCommandArguments } from '.';
import { NumberReply, Command } from '../RESP/types';
export function transformArguments(): RedisCommandArguments {
return ['CLIENT', 'GETREDIR'];
}
export declare function transformReply(): number;
export default {
FIRST_KEY_INDEX: undefined,
IS_READ_ONLY: true,
transformArguments() {
return ['CLIENT', 'GETREDIR']
},
transformReply: undefined as unknown as () => NumberReply
} as const satisfies Command;

View File

@@ -1,19 +1,19 @@
import { strict as assert } from 'assert';
import { strict as assert } from 'node:assert';
import testUtils, { GLOBAL } from '../test-utils';
import { transformArguments } from './CLIENT_ID';
import CLIENT_ID from './CLIENT_ID';
describe('CLIENT ID', () => {
it('transformArguments', () => {
assert.deepEqual(
transformArguments(),
['CLIENT', 'ID']
);
});
it('transformArguments', () => {
assert.deepEqual(
CLIENT_ID.transformArguments(),
['CLIENT', 'ID']
);
});
testUtils.testWithClient('client.clientId', async client => {
assert.equal(
typeof (await client.clientId()),
'number'
);
}, GLOBAL.SERVERS.OPEN);
testUtils.testWithClient('client.clientId', async client => {
assert.equal(
typeof (await client.clientId()),
'number'
);
}, GLOBAL.SERVERS.OPEN);
});

View File

@@ -1,7 +1,10 @@
export const IS_READ_ONLY = true;
import { NumberReply, Command } from '../RESP/types';
export function transformArguments(): Array<string> {
export default {
FIRST_KEY_INDEX: undefined,
IS_READ_ONLY: true,
transformArguments() {
return ['CLIENT', 'ID'];
}
export declare function transformReply(): number;
},
transformReply: undefined as unknown as () => NumberReply
} as const satisfies Command;

View File

@@ -1,50 +1,50 @@
import { strict as assert } from 'assert';
import { transformArguments, transformReply } from './CLIENT_INFO';
import { strict as assert } from 'node:assert';
import CLIENT_INFO from './CLIENT_INFO';
import testUtils, { GLOBAL } from '../test-utils';
describe('CLIENT INFO', () => {
testUtils.isVersionGreaterThanHook([6, 2]);
testUtils.isVersionGreaterThanHook([6, 2]);
it('transformArguments', () => {
assert.deepEqual(
transformArguments(),
['CLIENT', 'INFO']
);
});
it('transformArguments', () => {
assert.deepEqual(
CLIENT_INFO.transformArguments(),
['CLIENT', 'INFO']
);
});
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');
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])) {
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);
if (testUtils.isVersionGreaterThan([7, 0, 3])) {
assert.equal(typeof reply.ssub, 'number');
}
}
}, GLOBAL.SERVERS.OPEN);
});

View File

@@ -1,94 +1,116 @@
export const IS_READ_ONLY = true;
export function transformArguments(): Array<string> {
return ['CLIENT', 'INFO'];
}
import { Command, VerbatimStringReply } from '../RESP/types';
export interface ClientInfoReply {
id: number;
addr: string;
laddr?: string; // 6.2
fd: number;
name: string;
age: number;
idle: number;
flags: string;
db: number;
sub: number;
psub: number;
ssub?: number; // 7.0.3
multi: number;
qbuf: number;
qbufFree: number;
argvMem?: number; // 6.0
multiMem?: number; // 7.0
obl: number;
oll: number;
omem: number;
totMem?: number; // 6.0
events: string;
cmd: string;
user?: string; // 6.0
redir?: number; // 6.2
resp?: number; // 7.0
// 7.2
libName?: string;
libVer?: string;
id: number;
addr: string;
/**
* available since 6.2
*/
laddr?: string;
fd: number;
name: string;
age: number;
idle: number;
flags: string;
db: number;
sub: number;
psub: number;
/**
* available since 7.0.3
*/
ssub?: number;
multi: number;
qbuf: number;
qbufFree: number;
/**
* available since 6.0
*/
argvMem?: number;
/**
* available since 7.0
*/
multiMem?: number;
obl: number;
oll: number;
omem: number;
/**
* available since 6.0
*/
totMem?: number;
events: string;
cmd: string;
/**
* available since 6.0
*/
user?: string;
/**
* available since 6.2
*/
redir?: number;
/**
* available since 7.0
*/
resp?: number;
}
const CLIENT_INFO_REGEX = /([^\s=]+)=([^\s]*)/g;
export function transformReply(rawReply: string): ClientInfoReply {
export default {
FIRST_KEY_INDEX: undefined,
IS_READ_ONLY: true,
transformArguments() {
return ['CLIENT', 'INFO']
},
transformReply(rawReply: VerbatimStringReply) {
const map: Record<string, string> = {};
for (const item of rawReply.matchAll(CLIENT_INFO_REGEX)) {
map[item[1]] = item[2];
for (const item of rawReply.toString().matchAll(CLIENT_INFO_REGEX)) {
map[item[1]] = item[2];
}
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'],
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
};
if (map.laddr !== undefined) {
reply.laddr = map.laddr;
reply.laddr = map.laddr;
}
if (map.redir !== undefined) {
reply.redir = Number(map.redir);
reply.redir = Number(map.redir);
}
if (map.ssub !== undefined) {
reply.ssub = Number(map.ssub);
reply.ssub = Number(map.ssub);
}
if (map['multi-mem'] !== undefined) {
reply.multiMem = Number(map['multi-mem']);
reply.multiMem = Number(map['multi-mem']);
}
if (map.resp !== undefined) {
reply.resp = Number(map.resp);
reply.resp = Number(map.resp);
}
return reply;
}
}
} as const satisfies Command;

View File

@@ -1,120 +1,120 @@
import { strict as assert } from 'assert';
import { ClientKillFilters, transformArguments } from './CLIENT_KILL';
import { strict as assert } from 'node:assert';
import CLIENT_KILL, { CLIENT_KILL_FILTERS } from './CLIENT_KILL';
describe('CLIENT KILL', () => {
describe('transformArguments', () => {
it('ADDRESS', () => {
assert.deepEqual(
transformArguments({
filter: ClientKillFilters.ADDRESS,
address: 'ip:6379'
}),
['CLIENT', 'KILL', 'ADDR', 'ip:6379']
);
});
it('LOCAL_ADDRESS', () => {
assert.deepEqual(
transformArguments({
filter: ClientKillFilters.LOCAL_ADDRESS,
localAddress: 'ip:6379'
}),
['CLIENT', 'KILL', 'LADDR', 'ip:6379']
);
});
describe('ID', () => {
it('string', () => {
assert.deepEqual(
transformArguments({
filter: ClientKillFilters.ID,
id: '1'
}),
['CLIENT', 'KILL', 'ID', '1']
);
});
it('number', () => {
assert.deepEqual(
transformArguments({
filter: ClientKillFilters.ID,
id: 1
}),
['CLIENT', 'KILL', 'ID', '1']
);
});
});
it('TYPE', () => {
assert.deepEqual(
transformArguments({
filter: ClientKillFilters.TYPE,
type: 'master'
}),
['CLIENT', 'KILL', 'TYPE', 'master']
);
});
it('USER', () => {
assert.deepEqual(
transformArguments({
filter: ClientKillFilters.USER,
username: 'username'
}),
['CLIENT', 'KILL', 'USER', 'username']
);
});
it('MAXAGE', () => {
assert.deepEqual(
transformArguments({
filter: ClientKillFilters.MAXAGE,
maxAge: 10
}),
['CLIENT', 'KILL', 'MAXAGE', '10']
);
});
describe('SKIP_ME', () => {
it('undefined', () => {
assert.deepEqual(
transformArguments(ClientKillFilters.SKIP_ME),
['CLIENT', 'KILL', 'SKIPME']
);
});
it('true', () => {
assert.deepEqual(
transformArguments({
filter: ClientKillFilters.SKIP_ME,
skipMe: true
}),
['CLIENT', 'KILL', 'SKIPME', 'yes']
);
});
it('false', () => {
assert.deepEqual(
transformArguments({
filter: ClientKillFilters.SKIP_ME,
skipMe: false
}),
['CLIENT', 'KILL', 'SKIPME', 'no']
);
});
});
it('TYPE & SKIP_ME', () => {
assert.deepEqual(
transformArguments([
{
filter: ClientKillFilters.TYPE,
type: 'master'
},
ClientKillFilters.SKIP_ME
]),
['CLIENT', 'KILL', 'TYPE', 'master', 'SKIPME']
);
});
describe('transformArguments', () => {
it('ADDRESS', () => {
assert.deepEqual(
CLIENT_KILL.transformArguments({
filter: CLIENT_KILL_FILTERS.ADDRESS,
address: 'ip:6379'
}),
['CLIENT', 'KILL', 'ADDR', 'ip:6379']
);
});
it('LOCAL_ADDRESS', () => {
assert.deepEqual(
CLIENT_KILL.transformArguments({
filter: CLIENT_KILL_FILTERS.LOCAL_ADDRESS,
localAddress: 'ip:6379'
}),
['CLIENT', 'KILL', 'LADDR', 'ip:6379']
);
});
describe('ID', () => {
it('string', () => {
assert.deepEqual(
CLIENT_KILL.transformArguments({
filter: CLIENT_KILL_FILTERS.ID,
id: '1'
}),
['CLIENT', 'KILL', 'ID', '1']
);
});
it('number', () => {
assert.deepEqual(
CLIENT_KILL.transformArguments({
filter: CLIENT_KILL_FILTERS.ID,
id: 1
}),
['CLIENT', 'KILL', 'ID', '1']
);
});
});
it('TYPE', () => {
assert.deepEqual(
CLIENT_KILL.transformArguments({
filter: CLIENT_KILL_FILTERS.TYPE,
type: 'master'
}),
['CLIENT', 'KILL', 'TYPE', 'master']
);
});
it('USER', () => {
assert.deepEqual(
CLIENT_KILL.transformArguments({
filter: CLIENT_KILL_FILTERS.USER,
username: 'username'
}),
['CLIENT', 'KILL', 'USER', 'username']
);
});
it('MAXAGE', () => {
assert.deepEqual(
CLIENT_KILL.transformArguments({
filter: CLIENT_KILL_FILTERS.MAXAGE,
maxAge: 10
}),
['CLIENT', 'KILL', 'MAXAGE', '10']
);
});
describe('SKIP_ME', () => {
it('undefined', () => {
assert.deepEqual(
CLIENT_KILL.transformArguments(CLIENT_KILL_FILTERS.SKIP_ME),
['CLIENT', 'KILL', 'SKIPME']
);
});
it('true', () => {
assert.deepEqual(
CLIENT_KILL.transformArguments({
filter: CLIENT_KILL_FILTERS.SKIP_ME,
skipMe: true
}),
['CLIENT', 'KILL', 'SKIPME', 'yes']
);
});
it('false', () => {
assert.deepEqual(
CLIENT_KILL.transformArguments({
filter: CLIENT_KILL_FILTERS.SKIP_ME,
skipMe: false
}),
['CLIENT', 'KILL', 'SKIPME', 'no']
);
});
});
it('TYPE & SKIP_ME', () => {
assert.deepEqual(
CLIENT_KILL.transformArguments([
{
filter: CLIENT_KILL_FILTERS.TYPE,
type: 'master'
},
CLIENT_KILL_FILTERS.SKIP_ME
]),
['CLIENT', 'KILL', 'TYPE', 'master', 'SKIPME']
);
});
});
});

View File

@@ -1,104 +1,109 @@
import { RedisCommandArguments } from '.';
import { RedisArgument, NumberReply, Command } from '../RESP/types';
export enum ClientKillFilters {
ADDRESS = 'ADDR',
LOCAL_ADDRESS = 'LADDR',
ID = 'ID',
TYPE = 'TYPE',
USER = 'USER',
SKIP_ME = 'SKIPME',
MAXAGE = 'MAXAGE'
export const CLIENT_KILL_FILTERS = {
ADDRESS: 'ADDR',
LOCAL_ADDRESS: 'LADDR',
ID: 'ID',
TYPE: 'TYPE',
USER: 'USER',
SKIP_ME: 'SKIPME',
MAXAGE: 'MAXAGE'
} as const;
type CLIENT_KILL_FILTERS = typeof CLIENT_KILL_FILTERS;
export interface ClientKillFilterCommon<T extends CLIENT_KILL_FILTERS[keyof CLIENT_KILL_FILTERS]> {
filter: T;
}
interface KillFilter<T extends ClientKillFilters> {
filter: T;
export interface ClientKillAddress extends ClientKillFilterCommon<CLIENT_KILL_FILTERS['ADDRESS']> {
address: `${string}:${number}`;
}
interface KillAddress extends KillFilter<ClientKillFilters.ADDRESS> {
address: `${string}:${number}`;
export interface ClientKillLocalAddress extends ClientKillFilterCommon<CLIENT_KILL_FILTERS['LOCAL_ADDRESS']> {
localAddress: `${string}:${number}`;
}
interface KillLocalAddress extends KillFilter<ClientKillFilters.LOCAL_ADDRESS> {
localAddress: `${string}:${number}`;
export interface ClientKillId extends ClientKillFilterCommon<CLIENT_KILL_FILTERS['ID']> {
id: number | `${number}`;
}
interface KillId extends KillFilter<ClientKillFilters.ID> {
id: number | `${number}`;
export interface ClientKillType extends ClientKillFilterCommon<CLIENT_KILL_FILTERS['TYPE']> {
type: 'normal' | 'master' | 'replica' | 'pubsub';
}
interface KillType extends KillFilter<ClientKillFilters.TYPE> {
type: 'normal' | 'master' | 'replica' | 'pubsub';
export interface ClientKillUser extends ClientKillFilterCommon<CLIENT_KILL_FILTERS['USER']> {
username: string;
}
interface KillUser extends KillFilter<ClientKillFilters.USER> {
username: string;
}
type KillSkipMe = ClientKillFilters.SKIP_ME | (KillFilter<ClientKillFilters.SKIP_ME> & {
skipMe: boolean;
export type ClientKillSkipMe = CLIENT_KILL_FILTERS['SKIP_ME'] | (ClientKillFilterCommon<CLIENT_KILL_FILTERS['SKIP_ME']> & {
skipMe: boolean;
});
interface KillMaxAge extends KillFilter<ClientKillFilters.MAXAGE> {
maxAge: number;
export interface ClientKillMaxAge extends ClientKillFilterCommon<CLIENT_KILL_FILTERS['MAXAGE']> {
maxAge: number;
}
type KillFilters = KillAddress | KillLocalAddress | KillId | KillType | KillUser | KillSkipMe | KillMaxAge;
export type ClientKillFilter = ClientKillAddress | ClientKillLocalAddress | ClientKillId | ClientKillType | ClientKillUser | ClientKillSkipMe | ClientKillMaxAge;
export function transformArguments(filters: KillFilters | Array<KillFilters>): RedisCommandArguments {
export default {
FIRST_KEY_INDEX: undefined,
IS_READ_ONLY: true,
transformArguments(filters: ClientKillFilter | Array<ClientKillFilter>) {
const args = ['CLIENT', 'KILL'];
if (Array.isArray(filters)) {
for (const filter of filters) {
pushFilter(args, filter);
}
for (const filter of filters) {
pushFilter(args, filter);
}
} else {
pushFilter(args, filters);
pushFilter(args, filters);
}
return args;
},
transformReply: undefined as unknown as () => NumberReply
} as const satisfies Command;
function pushFilter(args: Array<RedisArgument>, filter: ClientKillFilter): void {
if (filter === CLIENT_KILL_FILTERS.SKIP_ME) {
args.push('SKIPME');
return;
}
args.push(filter.filter);
switch (filter.filter) {
case CLIENT_KILL_FILTERS.ADDRESS:
args.push(filter.address);
break;
case CLIENT_KILL_FILTERS.LOCAL_ADDRESS:
args.push(filter.localAddress);
break;
case CLIENT_KILL_FILTERS.ID:
args.push(
typeof filter.id === 'number' ?
filter.id.toString() :
filter.id
);
break;
case CLIENT_KILL_FILTERS.TYPE:
args.push(filter.type);
break;
case CLIENT_KILL_FILTERS.USER:
args.push(filter.username);
break;
case CLIENT_KILL_FILTERS.SKIP_ME:
args.push(filter.skipMe ? 'yes' : 'no');
break;
case CLIENT_KILL_FILTERS.MAXAGE:
args.push(filter.maxAge.toString());
break;
}
}
function pushFilter(args: RedisCommandArguments, filter: KillFilters): void {
if (filter === ClientKillFilters.SKIP_ME) {
args.push('SKIPME');
return;
}
args.push(filter.filter);
switch(filter.filter) {
case ClientKillFilters.ADDRESS:
args.push(filter.address);
break;
case ClientKillFilters.LOCAL_ADDRESS:
args.push(filter.localAddress);
break;
case ClientKillFilters.ID:
args.push(
typeof filter.id === 'number' ?
filter.id.toString() :
filter.id
);
break;
case ClientKillFilters.TYPE:
args.push(filter.type);
break;
case ClientKillFilters.USER:
args.push(filter.username);
break;
case ClientKillFilters.SKIP_ME:
args.push(filter.skipMe ? 'yes' : 'no');
break;
case ClientKillFilters.MAXAGE:
args.push(filter.maxAge.toString());
break;
}
}
export declare function transformReply(): number;

View File

@@ -1,78 +1,77 @@
import { strict as assert } from 'assert';
import { transformArguments, transformReply } from './CLIENT_LIST';
import { strict as assert } from 'node:assert';
import CLIENT_LIST 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']
);
});
describe('transformArguments', () => {
it('simple', () => {
assert.deepEqual(
CLIENT_LIST.transformArguments(),
['CLIENT', 'LIST']
);
});
testUtils.testWithClient('client.clientList', async client => {
const reply = await client.clientList();
assert.ok(Array.isArray(reply));
it('with TYPE', () => {
assert.deepEqual(
CLIENT_LIST.transformArguments({
TYPE: 'NORMAL'
}),
['CLIENT', 'LIST', 'TYPE', 'NORMAL']
);
});
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');
it('with ID', () => {
assert.deepEqual(
CLIENT_LIST.transformArguments({
ID: ['1', '2']
}),
['CLIENT', 'LIST', 'ID', '1', '2']
);
});
});
if (testUtils.isVersionGreaterThan([6, 0])) {
assert.equal(typeof item.argvMem, 'number');
assert.equal(typeof item.totMem, 'number');
assert.equal(typeof item.user, 'string');
}
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, 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([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');
assert.equal(typeof item.ssub, 'number');
}
}
}
}, GLOBAL.SERVERS.OPEN);
}
}
}, GLOBAL.SERVERS.OPEN);
});

View File

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

View File

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

View File

@@ -1,11 +1,14 @@
import { RedisCommandArguments } from '.';
import { SimpleStringReply, Command } from '../RESP/types';
export function transformArguments(value: boolean): RedisCommandArguments {
export default {
FIRST_KEY_INDEX: undefined,
IS_READ_ONLY: true,
transformArguments(value: boolean) {
return [
'CLIENT',
'NO-EVICT',
value ? 'ON' : 'OFF'
'CLIENT',
'NO-EVICT',
value ? 'ON' : 'OFF'
];
}
export declare function transformReply(): 'OK' | Buffer;
},
transformReply: undefined as unknown as () => SimpleStringReply<'OK'>
} as const satisfies Command;

View File

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

View File

@@ -1,11 +1,15 @@
import { RedisCommandArguments } from '.';
import { SimpleStringReply, Command } from '../RESP/types';
export function transformArguments(value: boolean): RedisCommandArguments {
export default {
FIRST_KEY_INDEX: undefined,
IS_READ_ONLY: true,
transformArguments(value: boolean) {
return [
'CLIENT',
'NO-TOUCH',
value ? 'ON' : 'OFF'
'CLIENT',
'NO-TOUCH',
value ? 'ON' : 'OFF'
];
}
},
transformReply: undefined as unknown as () => SimpleStringReply<'OK'>
} as const satisfies Command;
export declare function transformReply(): 'OK' | Buffer;

View File

@@ -1,28 +1,28 @@
import { strict as assert } from 'assert';
import { strict as assert } from 'node:assert';
import testUtils, { GLOBAL } from '../test-utils';
import { transformArguments } from './CLIENT_PAUSE';
import CLIENT_PAUSE from './CLIENT_PAUSE';
describe('CLIENT PAUSE', () => {
describe('transformArguments', () => {
it('simple', () => {
assert.deepEqual(
transformArguments(0),
['CLIENT', 'PAUSE', '0']
);
});
it('with mode', () => {
assert.deepEqual(
transformArguments(0, 'ALL'),
['CLIENT', 'PAUSE', '0', 'ALL']
);
});
describe('transformArguments', () => {
it('simple', () => {
assert.deepEqual(
CLIENT_PAUSE.transformArguments(0),
['CLIENT', 'PAUSE', '0']
);
});
testUtils.testWithClient('client.clientPause', async client => {
assert.equal(
await client.clientPause(0),
'OK'
);
}, GLOBAL.SERVERS.OPEN);
it('with mode', () => {
assert.deepEqual(
CLIENT_PAUSE.transformArguments(0, 'ALL'),
['CLIENT', 'PAUSE', '0', 'ALL']
);
});
});
testUtils.testWithClient('client.clientPause', async client => {
assert.equal(
await client.clientPause(0),
'OK'
);
}, GLOBAL.SERVERS.OPEN);
});

View File

@@ -1,20 +1,20 @@
import { RedisCommandArguments } from '.';
import { SimpleStringReply, Command } from '../RESP/types';
export function transformArguments(
timeout: number,
mode?: 'WRITE' | 'ALL'
): RedisCommandArguments {
export default {
FIRST_KEY_INDEX: undefined,
IS_READ_ONLY: true,
transformArguments(timeout: number, mode?: 'WRITE' | 'ALL') {
const args = [
'CLIENT',
'PAUSE',
timeout.toString()
'CLIENT',
'PAUSE',
timeout.toString()
];
if (mode) {
args.push(mode);
args.push(mode);
}
return args;
}
export declare function transformReply(): 'OK' | Buffer;
},
transformReply: undefined as unknown as () => SimpleStringReply<'OK'>
} as const satisfies Command;

View File

@@ -1,11 +1,20 @@
import { strict as assert } from 'assert';
import { transformArguments } from './CLIENT_SETNAME';
import { strict as assert } from 'node:assert';
import testUtils, { GLOBAL } from '../test-utils';
import CLIENT_SETNAME from './CLIENT_SETNAME';
describe('CLIENT SETNAME', () => {
it('transformArguments', () => {
assert.deepEqual(
transformArguments('name'),
['CLIENT', 'SETNAME', 'name']
);
});
it('transformArguments', () => {
assert.deepEqual(
CLIENT_SETNAME.transformArguments('name'),
['CLIENT', 'SETNAME', 'name']
);
});
testUtils.testWithClient('client.clientSetName', async client => {
assert.equal(
await client.clientSetName('name'),
'OK'
);
}, GLOBAL.SERVERS.OPEN);
});

View File

@@ -1,7 +1,10 @@
import { RedisCommandArgument, RedisCommandArguments } from '.';
import { RedisArgument, SimpleStringReply, Command } from '../RESP/types';
export function transformArguments(name: RedisCommandArgument): RedisCommandArguments {
export default {
FIRST_KEY_INDEX: undefined,
IS_READ_ONLY: true,
transformArguments(name: RedisArgument) {
return ['CLIENT', 'SETNAME', name];
}
export declare function transformReply(): RedisCommandArgument;
},
transformReply: undefined as unknown as () => SimpleStringReply<'OK'>
} as const satisfies Command;

View File

@@ -1,101 +1,101 @@
import { strict as assert } from 'assert';
import { strict as assert } from 'node:assert';
import testUtils, { GLOBAL } from '../test-utils';
import { transformArguments } from './CLIENT_TRACKING';
import CLIENT_TRACKING from './CLIENT_TRACKING';
describe('CLIENT TRACKING', () => {
testUtils.isVersionGreaterThanHook([6]);
testUtils.isVersionGreaterThanHook([6]);
describe('transformArguments', () => {
describe('true', () => {
it('simple', () => {
assert.deepEqual(
transformArguments(true),
['CLIENT', 'TRACKING', 'ON']
);
});
describe('transformArguments', () => {
describe('true', () => {
it('simple', () => {
assert.deepEqual(
CLIENT_TRACKING.transformArguments(true),
['CLIENT', 'TRACKING', 'ON']
);
});
it('with REDIRECT', () => {
assert.deepEqual(
transformArguments(true, {
REDIRECT: 1
}),
['CLIENT', 'TRACKING', 'ON', 'REDIRECT', '1']
);
});
it('with REDIRECT', () => {
assert.deepEqual(
CLIENT_TRACKING.transformArguments(true, {
REDIRECT: 1
}),
['CLIENT', 'TRACKING', 'ON', 'REDIRECT', '1']
);
});
describe('with BCAST', () => {
it('simple', () => {
assert.deepEqual(
transformArguments(true, {
BCAST: true
}),
['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']
);
});
describe('with BCAST', () => {
it('simple', () => {
assert.deepEqual(
CLIENT_TRACKING.transformArguments(true, {
BCAST: true
}),
['CLIENT', 'TRACKING', 'ON', 'BCAST']
);
});
it('false', () => {
describe('with PREFIX', () => {
it('string', () => {
assert.deepEqual(
transformArguments(false),
['CLIENT', 'TRACKING', 'OFF']
CLIENT_TRACKING.transformArguments(true, {
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 => {
assert.equal(
await client.clientTracking(false),
'OK'
);
}, GLOBAL.SERVERS.OPEN);
it('false', () => {
assert.deepEqual(
CLIENT_TRACKING.transformArguments(false),
['CLIENT', 'TRACKING', 'OFF']
);
});
});
testUtils.testWithClient('client.clientTracking', async client => {
assert.equal(
await client.clientTracking(false),
'OK'
);
}, GLOBAL.SERVERS.OPEN);
});

View File

@@ -1,83 +1,87 @@
import { RedisCommandArgument, RedisCommandArguments } from '.';
import { RedisArgument, SimpleStringReply, Command } from '../RESP/types';
import { RedisVariadicArgument } from './generic-transformers';
interface CommonOptions {
REDIRECT?: number;
NOLOOP?: boolean;
REDIRECT?: number;
NOLOOP?: boolean;
}
interface BroadcastOptions {
BCAST?: boolean;
PREFIX?: RedisCommandArgument | Array<RedisCommandArgument>;
BCAST?: boolean;
PREFIX?: RedisVariadicArgument;
}
interface OptInOptions {
OPTIN?: boolean;
OPTIN?: boolean;
}
interface OptOutOptions {
OPTOUT?: boolean;
OPTOUT?: boolean;
}
type ClientTrackingOptions = CommonOptions & (
BroadcastOptions |
OptInOptions |
OptOutOptions
export type ClientTrackingOptions = CommonOptions & (
BroadcastOptions |
OptInOptions |
OptOutOptions
);
export function transformArguments<M extends boolean>(
export default {
FIRST_KEY_INDEX: undefined,
IS_READ_ONLY: true,
transformArguments<M extends boolean>(
mode: M,
options?: M extends true ? ClientTrackingOptions : undefined
): RedisCommandArguments {
const args: RedisCommandArguments = [
'CLIENT',
'TRACKING',
mode ? 'ON' : 'OFF'
options?: M extends true ? ClientTrackingOptions : never
) {
const args: Array<RedisArgument> = [
'CLIENT',
'TRACKING',
mode ? 'ON' : 'OFF'
];
if (mode) {
if (options?.REDIRECT) {
args.push(
'REDIRECT',
options.REDIRECT.toString()
);
}
if (options?.REDIRECT) {
args.push(
'REDIRECT',
options.REDIRECT.toString()
);
}
if (isBroadcast(options)) {
args.push('BCAST');
if (isBroadcast(options)) {
args.push('BCAST');
if (options?.PREFIX) {
if (Array.isArray(options.PREFIX)) {
for (const prefix of options.PREFIX) {
args.push('PREFIX', prefix);
}
} else {
args.push('PREFIX', options.PREFIX);
}
if (options?.PREFIX) {
if (Array.isArray(options.PREFIX)) {
for (const prefix of options.PREFIX) {
args.push('PREFIX', prefix);
}
} else if (isOptIn(options)) {
args.push('OPTIN');
} else if (isOptOut(options)) {
args.push('OPTOUT');
} else {
args.push('PREFIX', options.PREFIX);
}
}
} else if (isOptIn(options)) {
args.push('OPTIN');
} else if (isOptOut(options)) {
args.push('OPTOUT');
}
if (options?.NOLOOP) {
args.push('NOLOOP');
}
if (options?.NOLOOP) {
args.push('NOLOOP');
}
}
return args;
}
},
transformReply: undefined as unknown as () => SimpleStringReply<'OK'>
} as const satisfies Command;
function isBroadcast(options?: ClientTrackingOptions): options is BroadcastOptions {
return (options as BroadcastOptions)?.BCAST === true;
return (options as BroadcastOptions)?.BCAST === true;
}
function isOptIn(options?: ClientTrackingOptions): options is OptInOptions {
return (options as OptInOptions)?.OPTIN === true;
return (options as OptInOptions)?.OPTIN === true;
}
function isOptOut(options?: ClientTrackingOptions): options is OptOutOptions {
return (options as OptOutOptions)?.OPTOUT === true;
return (options as OptOutOptions)?.OPTOUT === true;
}
export declare function transformReply(): 'OK' | Buffer;

View File

@@ -1,25 +1,25 @@
import { strict as assert } from 'assert';
import { strict as assert } from 'node:assert';
import testUtils, { GLOBAL } from '../test-utils';
import { transformArguments } from './CLIENT_TRACKINGINFO';
import CLIENT_TRACKINGINFO from './CLIENT_TRACKINGINFO';
describe('CLIENT TRACKINGINFO', () => {
testUtils.isVersionGreaterThanHook([6, 2]);
testUtils.isVersionGreaterThanHook([6, 2]);
it('transformArguments', () => {
assert.deepEqual(
transformArguments(),
['CLIENT', 'TRACKINGINFO']
);
});
it('transformArguments', () => {
assert.deepEqual(
CLIENT_TRACKINGINFO.transformArguments(),
['CLIENT', 'TRACKINGINFO']
);
});
testUtils.testWithClient('client.clientTrackingInfo', async client => {
assert.deepEqual(
await client.clientTrackingInfo(),
{
flags: new Set(['off']),
redirect: -1,
prefixes: []
}
);
}, GLOBAL.SERVERS.OPEN);
testUtils.testWithClient('client.clientTrackingInfo', async client => {
assert.deepEqual(
await client.clientTrackingInfo(),
{
flags: ['off'],
redirect: -1,
prefixes: []
}
);
}, GLOBAL.SERVERS.OPEN);
});

View File

@@ -1,28 +1,23 @@
import { RedisCommandArguments } from '.';
import { TuplesToMapReply, BlobStringReply, SetReply, NumberReply, ArrayReply, UnwrapReply, Resp2Reply, Command } from '../RESP/types';
export function transformArguments(): RedisCommandArguments {
type TrackingInfo = TuplesToMapReply<[
[BlobStringReply<'flags'>, SetReply<BlobStringReply>],
[BlobStringReply<'redirect'>, NumberReply],
[BlobStringReply<'prefixes'>, ArrayReply<BlobStringReply>]
]>;
export default {
FIRST_KEY_INDEX: undefined,
IS_READ_ONLY: true,
transformArguments() {
return ['CLIENT', 'TRACKINGINFO'];
}
type RawReply = [
'flags',
Array<string>,
'redirect',
number,
'prefixes',
Array<string>
];
interface Reply {
flags: Set<string>;
redirect: number;
prefixes: Array<string>;
}
export function transformReply(reply: RawReply): Reply {
return {
flags: new Set(reply[1]),
redirect: reply[3],
prefixes: reply[5]
};
}
},
transformReply: {
2: (reply: UnwrapReply<Resp2Reply<TrackingInfo>>) => ({
flags: reply[1],
redirect: reply[3],
prefixes: reply[5]
}),
3: undefined as unknown as () => TrackingInfo
}
} as const satisfies Command;

View File

@@ -1,21 +1,21 @@
import { strict as assert } from 'assert';
import { strict as assert } from 'node:assert';
import testUtils, { GLOBAL } from '../test-utils';
import { transformArguments } from './CLIENT_UNPAUSE';
import CLIENT_UNPAUSE from './CLIENT_UNPAUSE';
describe('CLIENT UNPAUSE', () => {
testUtils.isVersionGreaterThanHook([6, 2]);
testUtils.isVersionGreaterThanHook([6, 2]);
it('transformArguments', () => {
assert.deepEqual(
transformArguments(),
['CLIENT', 'UNPAUSE']
);
});
it('transformArguments', () => {
assert.deepEqual(
CLIENT_UNPAUSE.transformArguments(),
['CLIENT', 'UNPAUSE']
);
});
testUtils.testWithClient('client.unpause', async client => {
assert.equal(
await client.clientUnpause(),
'OK'
);
}, GLOBAL.SERVERS.OPEN);
testUtils.testWithClient('client.clientUnpause', async client => {
assert.equal(
await client.clientUnpause(),
'OK'
);
}, GLOBAL.SERVERS.OPEN);
});

View File

@@ -1,7 +1,10 @@
import { RedisCommandArguments } from '.';
import { SimpleStringReply, Command } from '../RESP/types';
export function transformArguments(): RedisCommandArguments {
export default {
FIRST_KEY_INDEX: undefined,
IS_READ_ONLY: true,
transformArguments() {
return ['CLIENT', 'UNPAUSE'];
}
export declare function transformReply(): 'OK' | Buffer;
},
transformReply: undefined as unknown as () => SimpleStringReply<'OK'>
} as const satisfies Command;

View File

@@ -1,20 +1,20 @@
import { strict as assert } from 'assert';
import { transformArguments } from './CLUSTER_ADDSLOTS';
import { strict as assert } from 'node:assert';
import CLUSTER_ADDSLOTS from './CLUSTER_ADDSLOTS';
describe('CLUSTER ADDSLOTS', () => {
describe('transformArguments', () => {
it('single', () => {
assert.deepEqual(
transformArguments(0),
['CLUSTER', 'ADDSLOTS', '0']
);
});
it('multiple', () => {
assert.deepEqual(
transformArguments([0, 1]),
['CLUSTER', 'ADDSLOTS', '0', '1']
);
});
describe('transformArguments', () => {
it('single', () => {
assert.deepEqual(
CLUSTER_ADDSLOTS.transformArguments(0),
['CLUSTER', 'ADDSLOTS', '0']
);
});
it('multiple', () => {
assert.deepEqual(
CLUSTER_ADDSLOTS.transformArguments([0, 1]),
['CLUSTER', 'ADDSLOTS', '0', '1']
);
});
});
});

View File

@@ -1,11 +1,14 @@
import { RedisCommandArguments } from '.';
import { pushVerdictNumberArguments } from './generic-transformers';
import { SimpleStringReply, Command } from '../RESP/types';
import { pushVariadicNumberArguments } from './generic-transformers';
export function transformArguments(slots: number | Array<number>): RedisCommandArguments {
return pushVerdictNumberArguments(
['CLUSTER', 'ADDSLOTS'],
slots
export default {
FIRST_KEY_INDEX: undefined,
IS_READ_ONLY: true,
transformArguments(slots: number | Array<number>) {
return pushVariadicNumberArguments(
['CLUSTER', 'ADDSLOTS'],
slots
);
}
export declare function transformReply(): string;
},
transformReply: undefined as unknown as () => SimpleStringReply<'OK'>
} as const satisfies Command;

View File

@@ -1,29 +1,32 @@
import { strict as assert } from 'assert';
import { transformArguments } from './CLUSTER_ADDSLOTSRANGE';
import { strict as assert } from 'node:assert';
import testUtils from '../test-utils';
import CLUSTER_ADDSLOTSRANGE from './CLUSTER_ADDSLOTSRANGE';
describe('CLUSTER ADDSLOTSRANGE', () => {
describe('transformArguments', () => {
it('single', () => {
assert.deepEqual(
transformArguments({
start: 0,
end: 1
}),
['CLUSTER', 'ADDSLOTSRANGE', '0', '1']
);
});
testUtils.isVersionGreaterThanHook([7, 0]);
it('multiple', () => {
assert.deepEqual(
transformArguments([{
start: 0,
end: 1
}, {
start: 2,
end: 3
}]),
['CLUSTER', 'ADDSLOTSRANGE', '0', '1', '2', '3']
);
});
describe('transformArguments', () => {
it('single', () => {
assert.deepEqual(
CLUSTER_ADDSLOTSRANGE.transformArguments({
start: 0,
end: 1
}),
['CLUSTER', 'ADDSLOTSRANGE', '0', '1']
);
});
it('multiple', () => {
assert.deepEqual(
CLUSTER_ADDSLOTSRANGE.transformArguments([{
start: 0,
end: 1
}, {
start: 2,
end: 3
}]),
['CLUSTER', 'ADDSLOTSRANGE', '0', '1', '2', '3']
);
});
});
});

View File

@@ -1,13 +1,14 @@
import { RedisCommandArguments } from '.';
import { SimpleStringReply, Command } from '../RESP/types';
import { pushSlotRangesArguments, SlotRange } from './generic-transformers';
export function transformArguments(
ranges: SlotRange | Array<SlotRange>
): RedisCommandArguments {
export default {
FIRST_KEY_INDEX: undefined,
IS_READ_ONLY: true,
transformArguments(ranges: SlotRange | Array<SlotRange>) {
return pushSlotRangesArguments(
['CLUSTER', 'ADDSLOTSRANGE'],
ranges
['CLUSTER', 'ADDSLOTSRANGE'],
ranges
);
}
export declare function transformReply(): 'OK';
},
transformReply: undefined as unknown as () => SimpleStringReply<'OK'>
} as const satisfies Command;

View File

@@ -1,20 +1,20 @@
import { strict as assert } from 'assert';
import { strict as assert } from 'node:assert';
import testUtils, { GLOBAL } from '../test-utils';
import { transformArguments } from './CLUSTER_BUMPEPOCH';
import CLUSTER_BUMPEPOCH from './CLUSTER_BUMPEPOCH';
describe('CLUSTER BUMPEPOCH', () => {
it('transformArguments', () => {
assert.deepEqual(
transformArguments(),
['CLUSTER', 'BUMPEPOCH']
);
});
it('transformArguments', () => {
assert.deepEqual(
CLUSTER_BUMPEPOCH.transformArguments(),
['CLUSTER', 'BUMPEPOCH']
);
});
testUtils.testWithCluster('clusterNode.clusterBumpEpoch', async cluster => {
const client = await cluster.nodeClient(cluster.masters[0]);
assert.equal(
typeof await client.clusterBumpEpoch(),
'string'
);
}, GLOBAL.SERVERS.OPEN);
testUtils.testWithCluster('clusterNode.clusterBumpEpoch', async cluster => {
const client = await cluster.nodeClient(cluster.masters[0]);
assert.equal(
typeof await client.clusterBumpEpoch(),
'string'
);
}, GLOBAL.SERVERS.OPEN);
});

View File

@@ -1,5 +1,10 @@
export function transformArguments(): Array<string> {
return ['CLUSTER', 'BUMPEPOCH'];
}
import { SimpleStringReply, Command } from '../RESP/types';
export declare function transformReply(): 'BUMPED' | 'STILL';
export default {
FIRST_KEY_INDEX: undefined,
IS_READ_ONLY: true,
transformArguments() {
return ['CLUSTER', 'BUMPEPOCH'];
},
transformReply: undefined as unknown as () => SimpleStringReply<'BUMPED' | 'STILL'>
} as const satisfies Command;

View File

@@ -1,22 +1,21 @@
import { strict as assert } from 'assert';
import { strict as assert } from 'node:assert';
import testUtils, { GLOBAL } from '../test-utils';
import { transformArguments } from './CLUSTER_COUNT-FAILURE-REPORTS';
import CLUSTER_COUNT_FAILURE_REPORTS from './CLUSTER_COUNT-FAILURE-REPORTS';
describe('CLUSTER COUNT-FAILURE-REPORTS', () => {
it('transformArguments', () => {
assert.deepEqual(
transformArguments('0'),
['CLUSTER', 'COUNT-FAILURE-REPORTS', '0']
);
});
it('transformArguments', () => {
assert.deepEqual(
CLUSTER_COUNT_FAILURE_REPORTS.transformArguments('0'),
['CLUSTER', 'COUNT-FAILURE-REPORTS', '0']
);
});
testUtils.testWithCluster('clusterNode.clusterCountFailureReports', async cluster => {
const client = await cluster.nodeClient(cluster.masters[0]);
assert.equal(
typeof await client.clusterCountFailureReports(
await client.clusterMyId()
),
'number'
);
}, GLOBAL.CLUSTERS.OPEN);
testUtils.testWithCluster('clusterNode.clusterCountFailureReports', async cluster => {
const [master] = cluster.masters,
client = await cluster.nodeClient(master);
assert.equal(
typeof await client.clusterCountFailureReports(master.id),
'number'
);
}, GLOBAL.CLUSTERS.OPEN);
});

View File

@@ -1,5 +1,10 @@
export function transformArguments(nodeId: string): Array<string> {
return ['CLUSTER', 'COUNT-FAILURE-REPORTS', nodeId];
}
import { RedisArgument, NumberReply, Command } from '../RESP/types';
export declare function transformReply(): number;
export default {
FIRST_KEY_INDEX: undefined,
IS_READ_ONLY: true,
transformArguments(nodeId: RedisArgument) {
return ['CLUSTER', 'COUNT-FAILURE-REPORTS', nodeId];
},
transformReply: undefined as unknown as () => NumberReply
} as const satisfies Command;

View File

@@ -1,20 +1,20 @@
import { strict as assert } from 'assert';
import { strict as assert } from 'node:assert';
import testUtils, { GLOBAL } from '../test-utils';
import { transformArguments } from './CLUSTER_COUNTKEYSINSLOT';
import CLUSTER_COUNTKEYSINSLOT from './CLUSTER_COUNTKEYSINSLOT';
describe('CLUSTER COUNTKEYSINSLOT', () => {
it('transformArguments', () => {
assert.deepEqual(
transformArguments(0),
['CLUSTER', 'COUNTKEYSINSLOT', '0']
);
});
it('transformArguments', () => {
assert.deepEqual(
CLUSTER_COUNTKEYSINSLOT.transformArguments(0),
['CLUSTER', 'COUNTKEYSINSLOT', '0']
);
});
testUtils.testWithCluster('clusterNode.clusterCountKeysInSlot', async cluster => {
const client = await cluster.nodeClient(cluster.masters[0]);
assert.equal(
typeof await client.clusterCountKeysInSlot(0),
'number'
);
}, GLOBAL.CLUSTERS.OPEN);
testUtils.testWithCluster('clusterNode.clusterCountKeysInSlot', async cluster => {
const client = await cluster.nodeClient(cluster.masters[0]);
assert.equal(
typeof await client.clusterCountKeysInSlot(0),
'number'
);
}, GLOBAL.CLUSTERS.OPEN);
});

View File

@@ -1,5 +1,10 @@
export function transformArguments(slot: number): Array<string> {
return ['CLUSTER', 'COUNTKEYSINSLOT', slot.toString()];
}
import { NumberReply, Command } from '../RESP/types';
export declare function transformReply(): number;
export default {
FIRST_KEY_INDEX: undefined,
IS_READ_ONLY: true,
transformArguments(slot: number) {
return ['CLUSTER', 'COUNTKEYSINSLOT', slot.toString()];
},
transformReply: undefined as unknown as () => NumberReply
} as const satisfies Command;

Some files were not shown because too many files have changed in this diff Show More