Skip to content

Commit 08f664a

Browse files
[CAE-686] Added hash field expiration commands
1 parent 8b4ed00 commit 08f664a

File tree

9 files changed

+418
-1
lines changed

9 files changed

+418
-1
lines changed
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import { strict as assert } from 'node:assert';
2+
import testUtils, { GLOBAL } from '../test-utils';
3+
import { BasicCommandParser } from '../client/parser';
4+
import HGETDEL from './HGETDEL';
5+
6+
describe('HGETDEL parseCommand', () => {
7+
it('hGetDel parseCommand base', () => {
8+
const parser = new BasicCommandParser;
9+
HGETDEL.parseCommand(parser, 'key', 'field');
10+
assert.deepEqual(parser.redisArgs, ['HGETDEL', 'key', 'FIELDS', '1', 'field']);
11+
});
12+
13+
it('hGetDel parseCommand variadic', () => {
14+
const parser = new BasicCommandParser;
15+
HGETDEL.parseCommand(parser, 'key', ['field1', 'field2']);
16+
assert.deepEqual(parser.redisArgs, ['HGETDEL', 'key', 'FIELDS', '2', 'field1', 'field2']);
17+
});
18+
});
19+
20+
21+
// TODO: enable when new test container is released
22+
describe.skip('HGETDEL call', () => {
23+
testUtils.testWithClientIfVersionWithinRange([[8], 'LATEST'], 'hGetDel empty single field', async client => {
24+
assert.deepEqual(
25+
await client.hGetDel('key', 'filed1'),
26+
[null]
27+
);
28+
}, GLOBAL.SERVERS.OPEN);
29+
30+
testUtils.testWithClientIfVersionWithinRange([[8], 'LATEST'], 'hGetDel empty multiple fields', async client => {
31+
assert.deepEqual(
32+
await client.hGetDel('key', ['filed1', 'field2']),
33+
[null, null]
34+
);
35+
}, GLOBAL.SERVERS.OPEN);
36+
37+
testUtils.testWithClientIfVersionWithinRange([[8], 'LATEST'], 'hGetDel partially populated multiple fields', async client => {
38+
await client.hSet('key', 'field1', 'value1')
39+
assert.deepEqual(
40+
await client.hGetDel('key', ['filed1', 'field2']),
41+
['value1', null]
42+
);
43+
44+
assert.deepEqual(
45+
await client.hGetDel('key', 'field1'),
46+
[null]
47+
);
48+
}, GLOBAL.SERVERS.OPEN);
49+
});
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { CommandParser } from '../client/parser';
2+
import { RedisVariadicArgument } from './generic-transformers';
3+
import { RedisArgument, ArrayReply, BlobStringReply, NullReply, Command } from '../RESP/types';
4+
5+
export default {
6+
parseCommand(parser: CommandParser, key: RedisArgument, fields: RedisVariadicArgument) {
7+
parser.push('HGETDEL');
8+
parser.pushKey(key);
9+
parser.push('FIELDS')
10+
parser.pushVariadicWithLength(fields);
11+
},
12+
transformReply: undefined as unknown as () => ArrayReply<BlobStringReply | NullReply>
13+
} as const satisfies Command;
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
import { strict as assert } from 'node:assert';
2+
import testUtils,{ GLOBAL, sleep } from '../test-utils';
3+
import { BasicCommandParser } from '../client/parser';
4+
import HGETEX from './HGETEX';
5+
6+
describe('HGETEX parseCommand', () => {
7+
it('hGetEx parseCommand base', () => {
8+
const parser = new BasicCommandParser;
9+
HGETEX.parseCommand(parser, 'key', 'field');
10+
assert.deepEqual(parser.redisArgs, ['HGETEX', 'key', 'FIELDS', '1', 'field']);
11+
});
12+
13+
it('hGetEx parseCommand expiration PERSIST string', () => {
14+
const parser = new BasicCommandParser;
15+
HGETEX.parseCommand(parser, 'key', 'field', {expiration: 'PERSIST'});
16+
assert.deepEqual(parser.redisArgs, ['HGETEX', 'key', 'PERSIST', 'FIELDS', '1', 'field']);
17+
});
18+
19+
it('hGetEx parseCommand expiration PERSIST obj', () => {
20+
const parser = new BasicCommandParser;
21+
HGETEX.parseCommand(parser, 'key', 'field', {expiration: {type: 'PERSIST'}});
22+
assert.deepEqual(parser.redisArgs, ['HGETEX', 'key', 'PERSIST', 'FIELDS', '1', 'field']);
23+
});
24+
25+
it('hGetEx parseCommand expiration EX obj', () => {
26+
const parser = new BasicCommandParser;
27+
HGETEX.parseCommand(parser, 'key', 'field', {expiration: {type: 'EX', value: 1000}});
28+
assert.deepEqual(parser.redisArgs, ['HGETEX', 'key', 'EX', '1000', 'FIELDS', '1', 'field']);
29+
});
30+
31+
it('hGetEx parseCommand expiration EXAT obj variadic', () => {
32+
const parser = new BasicCommandParser;
33+
HGETEX.parseCommand(parser, 'key', ['field1', 'field2'], {expiration: {type: 'EXAT', value: 1000}});
34+
assert.deepEqual(parser.redisArgs, ['HGETEX', 'key', 'EXAT', '1000', 'FIELDS', '2', 'field1', 'field2']);
35+
});
36+
});
37+
38+
39+
// TODO: enable when new test container is released
40+
describe.skip('HGETEX call', () => {
41+
testUtils.testWithClientIfVersionWithinRange([[8], 'LATEST'], 'hGetEx empty single field', async client => {
42+
assert.deepEqual(
43+
await client.hGetEx('key', 'filed1', {expiration: 'PERSIST'}),
44+
[null]
45+
);
46+
}, GLOBAL.SERVERS.OPEN);
47+
48+
testUtils.testWithClientIfVersionWithinRange([[8], 'LATEST'], 'hGetEx empty multiple fields', async client => {
49+
assert.deepEqual(
50+
await client.hGetEx('key', ['filed1', 'field2'], {expiration: 'PERSIST'}),
51+
[null, null]
52+
);
53+
}, GLOBAL.SERVERS.OPEN);
54+
55+
testUtils.testWithClientIfVersionWithinRange([[8], 'LATEST'], 'hGetEx set expiry', async client => {
56+
await client.hSet('key', 'field', 'value')
57+
assert.deepEqual(
58+
await client.hGetEx('key', 'field', {expiration: {type: 'PX', value: 500}}),
59+
['value']
60+
);
61+
62+
await sleep(600)
63+
assert.deepEqual(
64+
await client.hGet('key', 'field'),
65+
null
66+
);
67+
}, GLOBAL.SERVERS.OPEN);
68+
69+
testUtils.testWithClientIfVersionWithinRange([[8], 'LATEST'], 'gGetEx set expiry PERSIST', async client => {
70+
await client.hSet('key', 'field', 'value')
71+
await client.hGetEx('key', 'field', {expiration: {type: 'PX', value: 500}})
72+
await client.hGetEx('key', 'field', {expiration: 'PERSIST'})
73+
await sleep(600)
74+
assert.deepEqual(
75+
await client.hGet('key', 'field'),
76+
'value'
77+
)
78+
}, GLOBAL.SERVERS.OPEN);
79+
});
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import { CommandParser } from '../client/parser';
2+
import { RedisVariadicArgument } from './generic-transformers';
3+
import { ArrayReply, Command, BlobStringReply, NullReply, RedisArgument } from '../RESP/types';
4+
5+
export interface HGetExOptions {
6+
expiration?: {
7+
type: 'EX' | 'PX' | 'EXAT' | 'PXAT';
8+
value: number;
9+
} | {
10+
type: 'PERSIST';
11+
} | 'PERSIST';
12+
}
13+
14+
export default {
15+
parseCommand(
16+
parser: CommandParser,
17+
key: RedisArgument,
18+
fields: RedisVariadicArgument,
19+
options?: HGetExOptions
20+
) {
21+
parser.push('HGETEX');
22+
parser.pushKey(key);
23+
24+
if (options?.expiration) {
25+
if (typeof options.expiration === 'string') {
26+
parser.push(options.expiration);
27+
} else if (options.expiration.type === 'PERSIST') {
28+
parser.push('PERSIST');
29+
} else {
30+
parser.push(
31+
options.expiration.type,
32+
options.expiration.value.toString()
33+
);
34+
}
35+
}
36+
37+
parser.push('FIELDS')
38+
39+
parser.pushVariadicWithLength(fields);
40+
},
41+
transformReply: undefined as unknown as () => ArrayReply<BlobStringReply | NullReply>
42+
} as const satisfies Command;
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
import { strict as assert } from 'node:assert';
2+
import testUtils,{ GLOBAL, sleep } from '../test-utils';
3+
import { BasicCommandParser } from '../client/parser';
4+
import HSETEX from './HSETEX';
5+
6+
describe('HSETEX parseCommand', () => {
7+
it('hSetEx parseCommand base', () => {
8+
const parser = new BasicCommandParser;
9+
HSETEX.parseCommand(parser, 'key', {}, 'field', 'value');
10+
assert.deepEqual(parser.redisArgs, ['HSETEX', 'key', 'FIELDS', '1', 'field', 'value']);
11+
});
12+
13+
it('hSetEx parseCommand base empty obj', () => {
14+
const parser = new BasicCommandParser;
15+
assert.throws(() => {HSETEX.parseCommand(parser, 'key', {}, {})});
16+
});
17+
18+
it('hSetEx parseCommand base one key obj', () => {
19+
const parser = new BasicCommandParser;
20+
HSETEX.parseCommand(parser, 'key', {}, {'k': 'v'});
21+
assert.deepEqual(parser.redisArgs, ['HSETEX', 'key', 'FIELDS', '1', 'k', 'v']);
22+
});
23+
24+
it('hSetEx parseCommand array', () => {
25+
const parser = new BasicCommandParser;
26+
HSETEX.parseCommand(parser, 'key', {}, ['field1', 'value1', 'field2', 'value2']);
27+
assert.deepEqual(parser.redisArgs, ['HSETEX', 'key', 'FIELDS', '2', 'field1', 'value1', 'field2', 'value2']);
28+
});
29+
30+
it('hSetEx parseCommand array invalid args, thows error', () => {
31+
const parser = new BasicCommandParser;
32+
assert.throws(() => {HSETEX.parseCommand(parser, 'key', {}, ['field1', 'value1', 'field2'])});
33+
});
34+
35+
it('hSetEx parseCommand array in array', () => {
36+
const parser1 = new BasicCommandParser;
37+
HSETEX.parseCommand(parser1, 'key', {}, [['field1', 'value1'], ['field2', 'value2']]);
38+
assert.deepEqual(parser1.redisArgs, ['HSETEX', 'key', 'FIELDS', '2', 'field1', 'value1', 'field2', 'value2']);
39+
40+
const parser2 = new BasicCommandParser;
41+
HSETEX.parseCommand(parser2, 'key', {}, [['field1', 'value1'], ['field2', 'value2'], ['field3', 'value3']]);
42+
assert.deepEqual(parser2.redisArgs, ['HSETEX', 'key', 'FIELDS', '3', 'field1', 'value1', 'field2', 'value2', 'field3', 'value3']);
43+
});
44+
45+
it('hSetEx parseCommand map', () => {
46+
const parser1 = new BasicCommandParser;
47+
HSETEX.parseCommand(parser1, 'key', {}, new Map([['field1', 'value1'], ['field2', 'value2']]));
48+
assert.deepEqual(parser1.redisArgs, ['HSETEX', 'key', 'FIELDS', '2', 'field1', 'value1', 'field2', 'value2']);
49+
});
50+
51+
it('hSetEx parseCommand obj', () => {
52+
const parser1 = new BasicCommandParser;
53+
HSETEX.parseCommand(parser1, 'key', {}, {field1: "value1", field2: "value2"});
54+
assert.deepEqual(parser1.redisArgs, ['HSETEX', 'key', 'FIELDS', '2', 'field1', 'value1', 'field2', 'value2']);
55+
});
56+
57+
it('hSetEx parseCommand options FNX KEEPTTL', () => {
58+
const parser = new BasicCommandParser;
59+
HSETEX.parseCommand(parser, 'key', {mode: 'FNX', expiration: 'KEEPTTL'}, 'field', 'value');
60+
assert.deepEqual(parser.redisArgs, ['HSETEX', 'key', 'FNX', 'KEEPTTL', 'FIELDS', '1', 'field', 'value']);
61+
});
62+
63+
it('hSetEx parseCommand options FXX EX 500', () => {
64+
const parser = new BasicCommandParser;
65+
HSETEX.parseCommand(parser, 'key', {mode: 'FXX', expiration: {type: 'EX', value: 500}}, 'field', 'value');
66+
assert.deepEqual(parser.redisArgs, ['HSETEX', 'key', 'FXX', 'EX', '500', 'FIELDS', '1', 'field', 'value']);
67+
});
68+
});
69+
70+
71+
// TODO: enable when new test container is released
72+
describe.skip('HSETEX call', () => {
73+
testUtils.testWithClientIfVersionWithinRange([[8], 'LATEST'], 'hSetEx empty single field', async client => {
74+
assert.deepEqual(
75+
await client.hSetEx('key', {expiration: {type: "EX", value: 500}, mode: "FNX"}, 'filed1', 'value1'),
76+
1
77+
);
78+
79+
assert.deepEqual(
80+
await client.hSetEx('key', {expiration: {type: "EX", value: 500}, mode: "FXX"}, ['filed1', 'value1', 'field2', 'value2']),
81+
0
82+
);
83+
84+
assert.deepEqual(
85+
await client.hSetEx('key', {expiration: {type: "EX", value: 500}, mode: "FNX"}, ['filed1', 'value1', 'field2', 'value2']),
86+
1
87+
);
88+
89+
assert.deepEqual(
90+
await client.hSetEx('key', {expiration: {type: "EX", value: 500}, mode: "FNX"}, 'filed2', 'value2'),
91+
1
92+
);
93+
94+
assert.deepEqual(
95+
await client.hSetEx('key', {expiration: {type: "EX", value: 500}, mode: "FXX"}, ['filed1', 'value1', 'field2', 'value2']),
96+
1
97+
);
98+
}, GLOBAL.SERVERS.OPEN);
99+
});

0 commit comments

Comments
 (0)