Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/client/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ export const createSentinel = RedisSentinel.create;
export { GEO_REPLY_WITH, GeoReplyWith } from './lib/commands/GEOSEARCH_WITH';


export { SetOptions, CLIENT_KILL_FILTERS, CLIENT_UNBLOCK_MODES, FAILOVER_MODES, CLUSTER_SLOT_STATES, COMMAND_LIST_FILTER_BY, REDIS_FLUSH_MODES } from './lib/commands'
export { SetOptions, CLIENT_KILL_FILTERS, CLIENT_UNBLOCK_MODES, FAILOVER_MODES, CLUSTER_SLOT_STATES, COMMAND_LIST_FILTER_BY, REDIS_FLUSH_MODES, AR_PREDICATE_TYPES, AR_PREDICATE_COMBINATORS, AR_OPERATIONS } from './lib/commands'

export { BasicClientSideCache, BasicPooledClientSideCache } from './lib/client/cache';
export { OpenTelemetry } from './lib/opentelemetry';
Expand Down
21 changes: 21 additions & 0 deletions packages/client/lib/commands/ARCOUNT.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { strict as assert } from 'node:assert';
import testUtils, { GLOBAL } from '../test-utils';
import ARCOUNT from './ARCOUNT';
import { parseArgs } from './generic-transformers';

describe('ARCOUNT', () => {
it('transformArguments', () => {
assert.deepEqual(
parseArgs(ARCOUNT, 'key'),
['ARCOUNT', 'key']
);
});

testUtils.testWithClientIfVersionWithinRange([[8, 8], 'LATEST'], 'arCount', async client => {
await client.arSet('key', 0, ['v0', 'v1', 'v2']);
assert.equal(
await client.arCount('key'),
3
);
}, GLOBAL.SERVERS.OPEN);
});
11 changes: 11 additions & 0 deletions packages/client/lib/commands/ARCOUNT.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { CommandParser } from '../client/parser';
import { RedisArgument, NumberReply, Command } from '../RESP/types';

export default {
IS_READ_ONLY: true,
parseCommand(parser: CommandParser, key: RedisArgument) {
parser.push('ARCOUNT');
parser.pushKey(key);
},
transformReply: undefined as unknown as () => NumberReply
} as const satisfies Command;
43 changes: 43 additions & 0 deletions packages/client/lib/commands/ARDEL.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { strict as assert } from 'node:assert';
import testUtils, { GLOBAL } from '../test-utils';
import ARDEL from './ARDEL';
import { parseArgs } from './generic-transformers';

describe('ARDEL', () => {
describe('transformArguments', () => {
it('single index', () => {
assert.deepEqual(
parseArgs(ARDEL, 'key', 0),
['ARDEL', 'key', '0']
);
});

it('multiple indices', () => {
assert.deepEqual(
parseArgs(ARDEL, 'key', [0, 2, 4]),
['ARDEL', 'key', '0', '2', '4']
);
});
});

testUtils.testWithClientIfVersionWithinRange([[8, 8], 'LATEST'], 'arDel single index', async client => {
assert.equal(await client.arSet('key', 0, ['a', 'b', 'c']), 3);
assert.equal(await client.arDel('key', 1), 1);
assert.equal(await client.arGet('key', 1), null);
assert.equal(await client.arCount('key'), 2);
// already deleted → 0
assert.equal(await client.arDel('key', 1), 0);
}, GLOBAL.SERVERS.OPEN);

testUtils.testWithClientIfVersionWithinRange([[8, 8], 'LATEST'], 'arDel multiple indices', async client => {
assert.equal(await client.arSet('key', 0, ['a', 'b', 'c', 'd']), 4);
assert.equal(await client.arDel('key', [0, 1, 2]), 3);
assert.equal(await client.arCount('key'), 1);
}, GLOBAL.SERVERS.OPEN);

testUtils.testWithClientIfVersionWithinRange([[8, 8], 'LATEST'], 'arDel last element removes the key', async client => {
assert.equal(await client.arSet('key', 0, 'a'), 1);
assert.equal(await client.arDel('key', 0), 1);
assert.equal(await client.exists('key'), 0);
}, GLOBAL.SERVERS.OPEN);
});
17 changes: 17 additions & 0 deletions packages/client/lib/commands/ARDEL.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { CommandParser } from '../client/parser';
import { RedisArgument, NumberReply, Command } from '../RESP/types';

export type ArIndex = number | string;

export default {
parseCommand(parser: CommandParser, key: RedisArgument, indices: ArIndex | Array<ArIndex>) {
parser.push('ARDEL');
parser.pushKey(key);
if (Array.isArray(indices)) {
for (const i of indices) parser.push(i.toString());
} else {
parser.push(indices.toString());
}
},
transformReply: undefined as unknown as () => NumberReply
} as const satisfies Command;
45 changes: 45 additions & 0 deletions packages/client/lib/commands/ARDELRANGE.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { strict as assert } from 'node:assert';
import testUtils, { GLOBAL } from '../test-utils';
import ARDELRANGE from './ARDELRANGE';
import { parseArgs } from './generic-transformers';

describe('ARDELRANGE', () => {
describe('transformArguments', () => {
it('single range', () => {
assert.deepEqual(
parseArgs(ARDELRANGE, 'key', [[0, 4]]),
['ARDELRANGE', 'key', '0', '4']
);
});

it('multiple ranges', () => {
assert.deepEqual(
parseArgs(ARDELRANGE, 'key', [[0, 1], [3, 4]]),
['ARDELRANGE', 'key', '0', '1', '3', '4']
);
});
});

testUtils.testWithClientIfVersionWithinRange([[8, 8], 'LATEST'], 'arDelRange single range', async client => {
for (let i = 0; i < 10; i++) await client.arSet('key', i, (i * 10).toString());
assert.equal(await client.arCount('key'), 10);
assert.equal(await client.arDelRange('key', [[2, 6]]), 5);
assert.equal(await client.arCount('key'), 5);
}, GLOBAL.SERVERS.OPEN);

testUtils.testWithClientIfVersionWithinRange([[8, 8], 'LATEST'], 'arDelRange reverse range', async client => {
for (let i = 0; i < 10; i++) await client.arSet('key', i, (i * 10).toString());
// start > end still deletes the same 5 elements
assert.equal(await client.arDelRange('key', [[6, 2]]), 5);
assert.equal(await client.arCount('key'), 5);
}, GLOBAL.SERVERS.OPEN);

testUtils.testWithClientIfVersionWithinRange([[8, 8], 'LATEST'], 'arDelRange multiple ranges', async client => {
assert.equal(await client.arSet('key', 0, ['a', 'b', 'c', 'd', 'e', 'f']), 6);
assert.equal(await client.arDelRange('key', [[0, 1], [4, 5]]), 4);
assert.deepEqual(
await client.arGetRange('key', 0, 5),
[null, null, 'c', 'd', null, null]
);
}, GLOBAL.SERVERS.OPEN);
});
20 changes: 20 additions & 0 deletions packages/client/lib/commands/ARDELRANGE.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { CommandParser } from '../client/parser';
import { RedisArgument, NumberReply, Command } from '../RESP/types';

export type ArDelRangeRange = [start: number | string, end: number | string];

export default {
parseCommand(
parser: CommandParser,
key: RedisArgument,
ranges: Array<ArDelRangeRange>
) {
parser.push('ARDELRANGE');
parser.pushKey(key);

for (const [start, end] of ranges) {
parser.push(start.toString(), end.toString());
}
},
transformReply: undefined as unknown as () => NumberReply
} as const satisfies Command;
21 changes: 21 additions & 0 deletions packages/client/lib/commands/ARGET.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { strict as assert } from 'node:assert';
import testUtils, { GLOBAL } from '../test-utils';
import ARGET from './ARGET';
import { parseArgs } from './generic-transformers';

describe('ARGET', () => {
it('transformArguments', () => {
assert.deepEqual(
parseArgs(ARGET, 'key', 0),
['ARGET', 'key', '0']
);
});

testUtils.testWithClientIfVersionWithinRange([[8, 8], 'LATEST'], 'arGet', async client => {
await client.arSet('key', 0, 'v0');
assert.equal(
await client.arGet('key', 0),
'v0'
);
}, GLOBAL.SERVERS.OPEN);
});
12 changes: 12 additions & 0 deletions packages/client/lib/commands/ARGET.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { CommandParser } from '../client/parser';
import { RedisArgument, BlobStringReply, NullReply, Command } from '../RESP/types';

export default {
IS_READ_ONLY: true,
parseCommand(parser: CommandParser, key: RedisArgument, index: number | string) {
parser.push('ARGET');
parser.pushKey(key);
parser.push(index.toString());
},
transformReply: undefined as unknown as () => BlobStringReply | NullReply
} as const satisfies Command;
25 changes: 25 additions & 0 deletions packages/client/lib/commands/ARGETRANGE.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { strict as assert } from 'node:assert';
import testUtils, { GLOBAL } from '../test-utils';
import ARGETRANGE from './ARGETRANGE';
import { parseArgs } from './generic-transformers';

describe('ARGETRANGE', () => {
it('transformArguments', () => {
assert.deepEqual(
parseArgs(ARGETRANGE, 'key', 0, 10),
['ARGETRANGE', 'key', '0', '10']
);
});

testUtils.testWithClientIfVersionWithinRange([[8, 8], 'LATEST'], 'arGetRange forward + reverse', async client => {
assert.equal(await client.arSet('key', 0, ['a', 'b', 'c', 'd', 'e']), 5);
assert.deepEqual(await client.arGetRange('key', 1, 3), ['b', 'c', 'd']);
assert.deepEqual(await client.arGetRange('key', 3, 1), ['d', 'c', 'b']);
}, GLOBAL.SERVERS.OPEN);

testUtils.testWithClientIfVersionWithinRange([[8, 8], 'LATEST'], 'arGetRange errors when range exceeds maximum', async client => {
assert.equal(await client.arSet('key', 0, ['a', 'b', 'c', 'd', 'e']), 5);
await assert.rejects(() => client.arGetRange('key', 0, 1_000_000), /range exceeds maximum/i);
await assert.rejects(() => client.arGetRange('key', 1_000_000, 0), /range exceeds maximum/i);
}, GLOBAL.SERVERS.OPEN);
});
12 changes: 12 additions & 0 deletions packages/client/lib/commands/ARGETRANGE.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { CommandParser } from '../client/parser';
import { RedisArgument, ArrayReply, BlobStringReply, NullReply, Command } from '../RESP/types';

export default {
IS_READ_ONLY: true,
parseCommand(parser: CommandParser, key: RedisArgument, start: number | string, end: number | string) {
parser.push('ARGETRANGE');
parser.pushKey(key);
parser.push(start.toString(), end.toString());
},
transformReply: undefined as unknown as () => ArrayReply<BlobStringReply | NullReply>
} as const satisfies Command;
Loading
Loading