Skip to content

Commit 418f79e

Browse files
committed
add hash field expiration commands ported over from v4
1 parent 629e790 commit 418f79e

19 files changed

+552
-0
lines changed
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import { strict as assert } from 'node:assert';
2+
import testUtils, { GLOBAL } from '../test-utils';
3+
import HEXPIRE from './HEXPIRE';
4+
import { HASH_EXPIRATION_TIME } from './HEXPIRETIME';
5+
6+
describe('HEXPIRE', () => {
7+
testUtils.isVersionGreaterThanHook([7, 4]);
8+
9+
describe('transformArguments', () => {
10+
it('string', () => {
11+
assert.deepEqual(
12+
HEXPIRE.transformArguments('key', 'field', 1),
13+
['HEXPIRE', 'key', '1', 'FIELDS', '1', 'field']
14+
);
15+
});
16+
17+
it('array', () => {
18+
assert.deepEqual(
19+
HEXPIRE.transformArguments('key', ['field1', 'field2'], 1),
20+
['HEXPIRE', 'key', '1', 'FIELDS', '2', 'field1', 'field2']
21+
);
22+
});
23+
24+
it('with set option', () => {
25+
assert.deepEqual(
26+
HEXPIRE.transformArguments('key', ['field1'], 1, 'NX'),
27+
['HEXPIRE', 'key', '1', 'NX', 'FIELDS', '1', 'field1']
28+
);
29+
});
30+
});
31+
32+
testUtils.testWithClient('hexpire', async client => {
33+
assert.deepEqual(
34+
await client.hExpire('key', ['field1'], 0),
35+
[HASH_EXPIRATION_TIME.FIELD_NOT_EXISTS]
36+
);
37+
}, {
38+
...GLOBAL.SERVERS.OPEN
39+
});
40+
});
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import { Command, RedisArgument } from '../RESP/types';
2+
import { pushVariadicArgument } from './generic-transformers';
3+
4+
/**
5+
* @readonly
6+
* @enum {number}
7+
*/
8+
export const HASH_EXPIRATION = {
9+
/** @property {number} */
10+
/** The field does not exist */
11+
FIELD_NOT_EXISTS: -2,
12+
/** @property {number} */
13+
/** Specified NX | XX | GT | LT condition not met */
14+
CONDITION_NOT_MET: 0,
15+
/** @property {number} */
16+
/** Expiration time was set or updated */
17+
UPDATED: 1,
18+
/** @property {number} */
19+
/** Field deleted because the specified expiration time is in the past */
20+
DELETED: 2
21+
} as const;
22+
23+
export type HashExpiration = typeof HASH_EXPIRATION[keyof typeof HASH_EXPIRATION];
24+
25+
export default {
26+
FIRST_KEY_INDEX: 1,
27+
transformArguments(key: RedisArgument,
28+
fields: RedisArgument | Array<RedisArgument>,
29+
seconds: number,
30+
mode?: 'NX' | 'XX' | 'GT' | 'LT',
31+
) {
32+
const args = ['HEXPIRE', key, seconds.toString()];
33+
34+
if (mode) {
35+
args.push(mode);
36+
}
37+
38+
args.push('FIELDS');
39+
40+
return pushVariadicArgument(args, fields);
41+
},
42+
transformReply: undefined as unknown as () => Array<HashExpiration>
43+
} as const satisfies Command;
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 HEXPIREAT from './HEXPIREAT';
4+
import { HASH_EXPIRATION_TIME } from './HEXPIRETIME';
5+
6+
describe('HEXPIREAT', () => {
7+
testUtils.isVersionGreaterThanHook([7, 4]);
8+
9+
describe('transformArguments', () => {
10+
it('string + number', () => {
11+
assert.deepEqual(
12+
HEXPIREAT.transformArguments('key', 'field', 1),
13+
['HEXPIREAT', 'key', '1', 'FIELDS', '1', 'field']
14+
);
15+
});
16+
17+
it('array + number', () => {
18+
assert.deepEqual(
19+
HEXPIREAT.transformArguments('key', ['field1', 'field2'], 1),
20+
['HEXPIREAT', 'key', '1', 'FIELDS', '2', 'field1', 'field2']
21+
);
22+
});
23+
24+
it('date', () => {
25+
const d = new Date();
26+
27+
assert.deepEqual(
28+
HEXPIREAT.transformArguments('key', ['field1'], d),
29+
['HEXPIREAT', 'key', Math.floor(d.getTime() / 1000).toString(), 'FIELDS', '1', 'field1']
30+
);
31+
});
32+
33+
it('with set option', () => {
34+
assert.deepEqual(
35+
HEXPIREAT.transformArguments('key', 'field1', 1, 'GT'),
36+
['HEXPIREAT', 'key', '1', 'GT', 'FIELDS', '1', 'field1']
37+
);
38+
});
39+
});
40+
41+
testUtils.testWithClient('expireAt', async client => {
42+
assert.deepEqual(
43+
await client.hExpireAt('key', 'field1', 1),
44+
[HASH_EXPIRATION_TIME.FIELD_NOT_EXISTS]
45+
);
46+
}, {
47+
...GLOBAL.SERVERS.OPEN,
48+
});
49+
});
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import { Command, RedisArgument } from '../RESP/types';
2+
import { pushVariadicArgument, RedisVariadicArgument, transformEXAT } from './generic-transformers';
3+
import { HashExpiration } from './HEXPIRE';
4+
5+
export default {
6+
FIRST_KEY_INDEX: 1,
7+
transformArguments(
8+
key: RedisArgument,
9+
fields: RedisVariadicArgument,
10+
timestamp: number | Date,
11+
mode?: 'NX' | 'XX' | 'GT' | 'LT'
12+
) {
13+
const args = [
14+
'HEXPIREAT',
15+
key,
16+
transformEXAT(timestamp)
17+
];
18+
19+
if (mode) {
20+
args.push(mode);
21+
}
22+
23+
args.push('FIELDS')
24+
25+
return pushVariadicArgument(args, fields);
26+
},
27+
transformReply: undefined as unknown as () => Array<HashExpiration>
28+
} as const satisfies Command;
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import { strict as assert } from 'node:assert';
2+
import testUtils, { GLOBAL } from '../test-utils';
3+
import HEXPIRETIME, { HASH_EXPIRATION_TIME } from './HEXPIRETIME';
4+
5+
describe('HEXPIRETIME', () => {
6+
testUtils.isVersionGreaterThanHook([7, 4]);
7+
8+
describe('transformArguments', () => {
9+
it('string', () => {
10+
assert.deepEqual(
11+
HEXPIRETIME.transformArguments('key', 'field'),
12+
['HEXPIRETIME', 'key', 'FIELDS', '1', 'field']
13+
);
14+
});
15+
16+
it('array', () => {
17+
assert.deepEqual(
18+
HEXPIRETIME.transformArguments('key', ['field1', 'field2']),
19+
['HEXPIRETIME', 'key', 'FIELDS', '2', 'field1', 'field2']
20+
);
21+
});
22+
})
23+
24+
testUtils.testWithClient('hExpireTime', async client => {
25+
assert.deepEqual(
26+
await client.hExpireTime('key', 'field1'),
27+
[HASH_EXPIRATION_TIME.FIELD_NOT_EXISTS]
28+
);
29+
}, {
30+
...GLOBAL.SERVERS.OPEN,
31+
});
32+
});
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import { ArrayReply, Command, NumberReply, RedisArgument } from '../RESP/types';
2+
import { pushVariadicArgument, RedisVariadicArgument } from './generic-transformers';
3+
4+
export const HASH_EXPIRATION_TIME = {
5+
/** @property {number} */
6+
/** The field does not exist */
7+
FIELD_NOT_EXISTS: -2,
8+
/** @property {number} */
9+
/** The field exists but has no associated expire */
10+
NO_EXPIRATION: -1,
11+
} as const;
12+
13+
export default {
14+
FIRST_KEY_INDEX: 1,
15+
IS_READ_ONLY: true,
16+
transformArguments(key: RedisArgument, fields: RedisVariadicArgument) {
17+
return pushVariadicArgument(['HEXPIRETIME', key, 'FIELDS'], fields);
18+
},
19+
transformReply: undefined as unknown as () => ArrayReply<NumberReply>
20+
} as const satisfies Command;
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import { strict as assert } from 'node:assert';
2+
import testUtils, { GLOBAL } from '../test-utils';
3+
import HPERSIST from './HPERSIST';
4+
import { HASH_EXPIRATION_TIME } from './HEXPIRETIME';
5+
6+
describe('HPERSIST', () => {
7+
testUtils.isVersionGreaterThanHook([7, 4]);
8+
9+
describe('transformArguments', () => {
10+
it('string', () => {
11+
assert.deepEqual(
12+
HPERSIST.transformArguments('key', 'field'),
13+
['HPERSIST', 'key', 'FIELDS', '1', 'field']
14+
);
15+
});
16+
17+
it('array', () => {
18+
assert.deepEqual(
19+
HPERSIST.transformArguments('key', ['field1', 'field2']),
20+
['HPERSIST', 'key', 'FIELDS', '2', 'field1', 'field2']
21+
);
22+
});
23+
})
24+
25+
testUtils.testWithClient('hPersist', async client => {
26+
assert.deepEqual(
27+
await client.hPersist('key', 'field1'),
28+
[HASH_EXPIRATION_TIME.FIELD_NOT_EXISTS]
29+
);
30+
}, {
31+
...GLOBAL.SERVERS.OPEN,
32+
});
33+
});
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import { ArrayReply, Command, NullReply, NumberReply, RedisArgument } from '../RESP/types';
2+
import { pushVariadicArgument, RedisVariadicArgument } from './generic-transformers';
3+
4+
export default {
5+
FIRST_KEY_INDEX: 1,
6+
IS_READ_ONLY: true,
7+
transformArguments(key: RedisArgument, fields: RedisVariadicArgument) {
8+
return pushVariadicArgument(['HPERSIST', key, 'FIELDS'], fields);
9+
},
10+
transformReply: undefined as unknown as () => ArrayReply<NumberReply> | NullReply
11+
} as const satisfies Command;
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import { strict as assert } from 'node:assert';
2+
import testUtils, { GLOBAL } from '../test-utils';
3+
import HPEXPIRE from './HPEXPIRE';
4+
import { HASH_EXPIRATION_TIME } from './HEXPIRETIME';
5+
6+
describe('HEXPIRE', () => {
7+
testUtils.isVersionGreaterThanHook([7, 4]);
8+
9+
describe('transformArguments', () => {
10+
it('string', () => {
11+
assert.deepEqual(
12+
HPEXPIRE.transformArguments('key', 'field', 1),
13+
['HPEXPIRE', 'key', '1', 'FIELDS', '1', 'field']
14+
);
15+
});
16+
17+
it('array', () => {
18+
assert.deepEqual(
19+
HPEXPIRE.transformArguments('key', ['field1', 'field2'], 1),
20+
['HPEXPIRE', 'key', '1', 'FIELDS', '2', 'field1', 'field2']
21+
);
22+
});
23+
24+
it('with set option', () => {
25+
assert.deepEqual(
26+
HPEXPIRE.transformArguments('key', ['field1'], 1, 'NX'),
27+
['HPEXPIRE', 'key', '1', 'NX', 'FIELDS', '1', 'field1']
28+
);
29+
});
30+
});
31+
32+
testUtils.testWithClient('hexpire', async client => {
33+
assert.deepEqual(
34+
await client.hpExpire('key', ['field1'], 0),
35+
[HASH_EXPIRATION_TIME.FIELD_NOT_EXISTS]
36+
);
37+
}, {
38+
...GLOBAL.SERVERS.OPEN
39+
});
40+
});
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import { ArrayReply, Command, NullReply, RedisArgument } from '../RESP/types';
2+
import { pushVariadicArgument, RedisVariadicArgument } from './generic-transformers';
3+
import { HashExpiration } from "./HEXPIRE";
4+
5+
export default {
6+
FIRST_KEY_INDEX: 1,
7+
transformArguments(
8+
key: RedisArgument,
9+
fields: RedisVariadicArgument,
10+
ms: number,
11+
mode?: 'NX' | 'XX' | 'GT' | 'LT',
12+
) {
13+
const args = ['HPEXPIRE', key, ms.toString()];
14+
15+
if (mode) {
16+
args.push(mode);
17+
}
18+
19+
args.push('FIELDS')
20+
21+
return pushVariadicArgument(args, fields);
22+
},
23+
transformReply: undefined as unknown as () => ArrayReply<HashExpiration> | NullReply
24+
} as const satisfies Command;

0 commit comments

Comments
 (0)