Skip to content

Commit 69d507a

Browse files
authored
refactor!: redis 8 compatibility improvements and test infrastructure updates (#2893)
* churn(test): use redislabs/client-libs-test for testing This switches our testing infrastructure from redis/redis-stack to redislabs/client-libs-test Docker image across all packages. This change also updates the default Docker version from 7.4.0-v1 to 8.0-M04-pre. * churn(test): verify CONFIG SET / GET compatibility with Redis 8 - Add tests for Redis 8 search configuration settings - Deprecate Redis Search CONFIG commands in favor of standard CONFIG - Test read-only config restrictions for Redis 8 * churn(test): handle Redis 8 coordinate precision in GEOPOS - Update GEOPOS tests to handle increased precision in Redis 8 (17 decimal places vs 14) - Add precision-aware coordinate comparison helper - Add comprehensive test suite for coordinate comparison function * test(search): adapt SUGGET tests for Redis 8 empty results - Update tests to expect empty array ([]) instead of null for SUGGET variants - Affects sugGet, sugGetWithPayloads, sugGetWithScores, and sugGetWithScoresWithPayloads * test(search): support Redis 8 INFO indexes_all field - Add indexes_all field introduced in Redis 8 to index definition test * refactor!(search): simplify PROFILE commands to return raw response - BREAKING CHANGE: FT.PROFILE now returns raw response, letting users implement their own parsing * test: improve version-specific test coverage - Add `testWithClientIfVersionWithinRange` method to run tests for specific Redis versions - Refactor TestUtils to handle version comparisons more accurately - Update test utilities across Redis modules to run tests against multiple versions, and not against latest only
1 parent 33cdc00 commit 69d507a

28 files changed

+1083
-422
lines changed

.github/workflows/tests.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,8 @@ jobs:
2121
strategy:
2222
fail-fast: false
2323
matrix:
24-
node-version: ['18', '20', '22']
25-
redis-version: ['6.2.6-v17', '7.2.0-v13', '7.4.0-v1']
24+
node-version: [ '18', '20', '22' ]
25+
redis-version: [ 'rs-7.2.0-v13', 'rs-7.4.0-v1', '8.0-M04-pre' ]
2626
steps:
2727
- uses: actions/checkout@v4
2828
with:

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
"./packages/*"
66
],
77
"scripts": {
8+
"test-single": "TS_NODE_PROJECT='./packages/test-utils/tsconfig.json' mocha --require ts-node/register/transpile-only ",
89
"test": "npm run test -ws --if-present",
910
"build": "tsc --build",
1011
"documentation": "typedoc --out ./documentation",

packages/bloom/lib/test-utils.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
import TestUtils from '@redis/test-utils';
22
import RedisBloomModules from '.';
33

4-
export default new TestUtils({
5-
dockerImageName: 'redis/redis-stack',
6-
dockerImageVersionArgument: 'redisbloom-version',
7-
defaultDockerVersion: '7.4.0-v1'
4+
export default TestUtils.createFromConfig({
5+
dockerImageName: 'redislabs/client-libs-test',
6+
dockerImageVersionArgument: 'redis-version',
7+
defaultDockerVersion: '8.0-M04-pre'
88
});
99

1010
export const GLOBAL = {

packages/client/lib/commands/CONFIG_GET.spec.ts

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ describe('CONFIG GET', () => {
1919
);
2020
});
2121
});
22-
2322

2423
testUtils.testWithClient('client.configGet', async client => {
2524
const config = await client.configGet('*');
@@ -29,4 +28,33 @@ describe('CONFIG GET', () => {
2928
assert.equal(typeof value, 'string');
3029
}
3130
}, GLOBAL.SERVERS.OPEN);
31+
32+
testUtils.testWithClient('client.configSet.getSearchConfigSettingTest | Redis >= 8', async client => {
33+
assert.ok(
34+
await client.configGet('search-timeout'),
35+
'OK'
36+
);
37+
}, GLOBAL.SERVERS.OPEN);
38+
39+
testUtils.testWithClient('client.configSet.getTSConfigSettingTest | Redis >= 8', async client => {
40+
assert.ok(
41+
await client.configGet('ts-retention-policy'),
42+
'OK'
43+
);
44+
}, GLOBAL.SERVERS.OPEN);
45+
46+
testUtils.testWithClient('client.configSet.getBFConfigSettingTest | Redis >= 8', async client => {
47+
assert.ok(
48+
await client.configGet('bf-error-rate'),
49+
'OK'
50+
);
51+
}, GLOBAL.SERVERS.OPEN);
52+
53+
testUtils.testWithClient('client.configSet.getCFConfigSettingTest | Redis >= 8', async client => {
54+
assert.ok(
55+
await client.configGet('cf-initial-size'),
56+
'OK'
57+
);
58+
}, GLOBAL.SERVERS.OPEN);
59+
3260
});

packages/client/lib/commands/CONFIG_SET.spec.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,4 +30,13 @@ describe('CONFIG SET', () => {
3030
'OK'
3131
);
3232
}, GLOBAL.SERVERS.OPEN);
33+
34+
testUtils.testWithClient('client.configSet.setReadOnlySearchConfigTest | Redis >= 8',
35+
async client => {
36+
assert.rejects(
37+
client.configSet('search-max-doctablesize', '0'),
38+
new Error('ERR CONFIG SET failed (possibly related to argument \'search-max-doctablesize\') - can\'t set immutable config')
39+
);
40+
}, GLOBAL.SERVERS.OPEN);
41+
3342
});

packages/client/lib/commands/GEOPOS.spec.ts

Lines changed: 119 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { strict as assert } from 'node:assert';
1+
import { strict as assert, fail } from 'node:assert';
22
import testUtils, { GLOBAL } from '../test-utils';
33
import GEOPOS from './GEOPOS';
44
import { parseArgs } from './generic-transformers';
@@ -41,12 +41,126 @@ describe('GEOPOS', () => {
4141
...coordinates
4242
});
4343

44-
assert.deepEqual(
45-
await client.geoPos('key', 'member'),
46-
[coordinates]
47-
);
44+
const result = await client.geoPos('key', 'member');
45+
46+
/**
47+
* - Redis < 8: Returns coordinates with 14 decimal places (e.g., "-122.06429868936539")
48+
* - Redis 8+: Returns coordinates with 17 decimal places (e.g., "-122.06429868936538696")
49+
*
50+
*/
51+
const PRECISION = 13; // Number of decimal places to compare
52+
53+
if (result && result.length === 1 && result[0] != null) {
54+
const { longitude, latitude } = result[0];
55+
56+
assert.ok(
57+
compareWithPrecision(longitude, coordinates.longitude, PRECISION),
58+
`Longitude mismatch: ${longitude} vs ${coordinates.longitude}`
59+
);
60+
assert.ok(
61+
compareWithPrecision(latitude, coordinates.latitude, PRECISION),
62+
`Latitude mismatch: ${latitude} vs ${coordinates.latitude}`
63+
);
64+
65+
} else {
66+
assert.fail('Expected a valid result');
67+
}
68+
69+
70+
4871
}, {
4972
client: GLOBAL.SERVERS.OPEN,
5073
cluster: GLOBAL.CLUSTERS.OPEN
5174
});
5275
});
76+
77+
describe('compareWithPrecision', () => {
78+
it('should match exact same numbers', () => {
79+
assert.strictEqual(
80+
compareWithPrecision('123.456789', '123.456789', 6),
81+
true
82+
);
83+
});
84+
85+
it('should match when actual has more precision than needed', () => {
86+
assert.strictEqual(
87+
compareWithPrecision('123.456789123456', '123.456789', 6),
88+
true
89+
);
90+
});
91+
92+
it('should match when expected has more precision than needed', () => {
93+
assert.strictEqual(
94+
compareWithPrecision('123.456789', '123.456789123456', 6),
95+
true
96+
);
97+
});
98+
99+
it('should fail when decimals differ within precision', () => {
100+
assert.strictEqual(
101+
compareWithPrecision('123.456689', '123.456789', 6),
102+
false
103+
);
104+
});
105+
106+
it('should handle negative numbers', () => {
107+
assert.strictEqual(
108+
compareWithPrecision('-122.06429868936538', '-122.06429868936539', 13),
109+
true
110+
);
111+
});
112+
113+
it('should fail when integer parts differ', () => {
114+
assert.strictEqual(
115+
compareWithPrecision('124.456789', '123.456789', 6),
116+
false
117+
);
118+
});
119+
120+
it('should handle zero decimal places', () => {
121+
assert.strictEqual(
122+
compareWithPrecision('123.456789', '123.456789', 0),
123+
true
124+
);
125+
});
126+
127+
it('should handle numbers without decimal points', () => {
128+
assert.strictEqual(
129+
compareWithPrecision('123', '123', 6),
130+
true
131+
);
132+
});
133+
134+
it('should handle one number without decimal point', () => {
135+
assert.strictEqual(
136+
compareWithPrecision('123', '123.000', 3),
137+
true
138+
);
139+
});
140+
141+
it('should match Redis coordinates with different precision', () => {
142+
assert.strictEqual(
143+
compareWithPrecision(
144+
'-122.06429868936538696',
145+
'-122.06429868936539',
146+
13
147+
),
148+
true
149+
);
150+
});
151+
152+
it('should match Redis latitude with different precision', () => {
153+
assert.strictEqual(
154+
compareWithPrecision(
155+
'37.37749628831998194',
156+
'37.37749628831998',
157+
14
158+
),
159+
true
160+
);
161+
});
162+
});
163+
164+
export const compareWithPrecision = (actual: string, expected: string, decimals: number): boolean => {
165+
return Math.abs(Number(actual) - Number(expected)) < Math.pow(10, -decimals);
166+
};

packages/client/lib/test-utils.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,10 @@ import { CredentialsProvider } from './authx';
55
import { Command } from './RESP/types';
66
import { BasicCommandParser } from './client/parser';
77

8-
const utils = new TestUtils({
9-
dockerImageName: 'redis/redis-stack',
8+
const utils = TestUtils.createFromConfig({
9+
dockerImageName: 'redislabs/client-libs-test',
1010
dockerImageVersionArgument: 'redis-version',
11-
defaultDockerVersion: '7.4.0-v1'
11+
defaultDockerVersion: '8.0-M04-pre'
1212
});
1313

1414
export default utils;

packages/entraid/lib/test-utils.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,10 @@ import { IdentityProvider, StreamingCredentialsProvider, TokenManager, TokenResp
33
import TestUtils from '@redis/test-utils';
44
import { EntraidCredentialsProvider } from './entraid-credentials-provider';
55

6-
export const testUtils = new TestUtils({
7-
dockerImageName: 'redis/redis-stack',
6+
export const testUtils = TestUtils.createFromConfig({
7+
dockerImageName: 'redislabs/client-libs-test',
88
dockerImageVersionArgument: 'redis-version',
9-
defaultDockerVersion: '7.4.0-v1'
9+
defaultDockerVersion: '8.0-M04-pre'
1010
});
1111

1212
const DEBUG_MODE_ARGS = testUtils.isVersionGreaterThan([7]) ?

packages/graph/lib/test-utils.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
import TestUtils from '@redis/test-utils';
22
import RedisGraph from '.';
33

4-
export default new TestUtils({
5-
dockerImageName: 'redis/redis-stack',
6-
dockerImageVersionArgument: 'redisgraph-version',
7-
defaultDockerVersion: '7.4.0-v1'
4+
5+
export default TestUtils.createFromConfig({
6+
dockerImageName: 'redislabs/client-libs-test',
7+
dockerImageVersionArgument: 'redis-version',
8+
defaultDockerVersion: '8.0-M04-pre'
89
});
910

1011
export const GLOBAL = {

packages/json/lib/test-utils.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
import TestUtils from '@redis/test-utils';
22
import RedisJSON from '.';
33

4-
export default new TestUtils({
5-
dockerImageName: 'redis/redis-stack',
6-
dockerImageVersionArgument: 'redisgraph-version',
7-
defaultDockerVersion: '7.4.0-v1'
4+
export default TestUtils.createFromConfig({
5+
dockerImageName: 'redislabs/client-libs-test',
6+
dockerImageVersionArgument: 'redis-version',
7+
defaultDockerVersion: '8.0-M04-pre'
88
});
99

1010
export const GLOBAL = {

packages/search/lib/commands/CONFIG_SET.spec.ts

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,4 +17,40 @@ describe('FT.CONFIG SET', () => {
1717
'OK'
1818
);
1919
}, GLOBAL.SERVERS.OPEN);
20+
21+
testUtils.testWithClientIfVersionWithinRange([[8], 'LATEST'], 'setSearchConfigGloballyTest', async client => {
22+
23+
const normalizeObject = obj => JSON.parse(JSON.stringify(obj));
24+
assert.equal(await client.configSet('search-default-dialect', '3'),
25+
'OK', 'CONFIG SET should return OK');
26+
27+
assert.deepEqual(
28+
normalizeObject(await client.configGet('search-default-dialect')),
29+
{ 'search-default-dialect': '3' },
30+
'CONFIG GET should return 3'
31+
);
32+
33+
assert.deepEqual(
34+
normalizeObject(await client.ft.configGet('DEFAULT_DIALECT')),
35+
{ 'DEFAULT_DIALECT': '3' },
36+
'FT.CONFIG GET should return 3'
37+
);
38+
39+
const ftConfigSetResult = await client.ft.configSet('DEFAULT_DIALECT', '2');
40+
assert.equal(normalizeObject(ftConfigSetResult), 'OK', 'FT.CONFIG SET should return OK');
41+
42+
assert.deepEqual(
43+
normalizeObject(await client.ft.configGet('DEFAULT_DIALECT')),
44+
{ 'DEFAULT_DIALECT': '2' },
45+
'FT.CONFIG GET should return 2'
46+
);
47+
48+
assert.deepEqual(
49+
normalizeObject(await client.configGet('search-default-dialect')),
50+
{ 'search-default-dialect': '2' },
51+
'CONFIG GET should return 22'
52+
);
53+
54+
}, GLOBAL.SERVERS.OPEN);
55+
2056
});

0 commit comments

Comments
 (0)