Skip to content

Commit

Permalink
refactor!: redis 8 compatibility improvements and test infrastructure…
Browse files Browse the repository at this point in the history
… 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
  • Loading branch information
bobymicroby authored Feb 27, 2025
1 parent 33cdc00 commit 69d507a
Show file tree
Hide file tree
Showing 28 changed files with 1,083 additions and 422 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ jobs:
strategy:
fail-fast: false
matrix:
node-version: ['18', '20', '22']
redis-version: ['6.2.6-v17', '7.2.0-v13', '7.4.0-v1']
node-version: [ '18', '20', '22' ]
redis-version: [ 'rs-7.2.0-v13', 'rs-7.4.0-v1', '8.0-M04-pre' ]
steps:
- uses: actions/checkout@v4
with:
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
"./packages/*"
],
"scripts": {
"test-single": "TS_NODE_PROJECT='./packages/test-utils/tsconfig.json' mocha --require ts-node/register/transpile-only ",
"test": "npm run test -ws --if-present",
"build": "tsc --build",
"documentation": "typedoc --out ./documentation",
Expand Down
8 changes: 4 additions & 4 deletions packages/bloom/lib/test-utils.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import TestUtils from '@redis/test-utils';
import RedisBloomModules from '.';

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

export const GLOBAL = {
Expand Down
30 changes: 29 additions & 1 deletion packages/client/lib/commands/CONFIG_GET.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ describe('CONFIG GET', () => {
);
});
});


testUtils.testWithClient('client.configGet', async client => {
const config = await client.configGet('*');
Expand All @@ -29,4 +28,33 @@ describe('CONFIG GET', () => {
assert.equal(typeof value, 'string');
}
}, GLOBAL.SERVERS.OPEN);

testUtils.testWithClient('client.configSet.getSearchConfigSettingTest | Redis >= 8', async client => {
assert.ok(
await client.configGet('search-timeout'),
'OK'
);
}, GLOBAL.SERVERS.OPEN);

testUtils.testWithClient('client.configSet.getTSConfigSettingTest | Redis >= 8', async client => {
assert.ok(
await client.configGet('ts-retention-policy'),
'OK'
);
}, GLOBAL.SERVERS.OPEN);

testUtils.testWithClient('client.configSet.getBFConfigSettingTest | Redis >= 8', async client => {
assert.ok(
await client.configGet('bf-error-rate'),
'OK'
);
}, GLOBAL.SERVERS.OPEN);

testUtils.testWithClient('client.configSet.getCFConfigSettingTest | Redis >= 8', async client => {
assert.ok(
await client.configGet('cf-initial-size'),
'OK'
);
}, GLOBAL.SERVERS.OPEN);

});
9 changes: 9 additions & 0 deletions packages/client/lib/commands/CONFIG_SET.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,13 @@ describe('CONFIG SET', () => {
'OK'
);
}, GLOBAL.SERVERS.OPEN);

testUtils.testWithClient('client.configSet.setReadOnlySearchConfigTest | Redis >= 8',
async client => {
assert.rejects(
client.configSet('search-max-doctablesize', '0'),
new Error('ERR CONFIG SET failed (possibly related to argument \'search-max-doctablesize\') - can\'t set immutable config')
);
}, GLOBAL.SERVERS.OPEN);

});
124 changes: 119 additions & 5 deletions packages/client/lib/commands/GEOPOS.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { strict as assert } from 'node:assert';
import { strict as assert, fail } from 'node:assert';
import testUtils, { GLOBAL } from '../test-utils';
import GEOPOS from './GEOPOS';
import { parseArgs } from './generic-transformers';
Expand Down Expand Up @@ -41,12 +41,126 @@ describe('GEOPOS', () => {
...coordinates
});

assert.deepEqual(
await client.geoPos('key', 'member'),
[coordinates]
);
const result = await client.geoPos('key', 'member');

/**
* - Redis < 8: Returns coordinates with 14 decimal places (e.g., "-122.06429868936539")
* - Redis 8+: Returns coordinates with 17 decimal places (e.g., "-122.06429868936538696")
*
*/
const PRECISION = 13; // Number of decimal places to compare

if (result && result.length === 1 && result[0] != null) {
const { longitude, latitude } = result[0];

assert.ok(
compareWithPrecision(longitude, coordinates.longitude, PRECISION),
`Longitude mismatch: ${longitude} vs ${coordinates.longitude}`
);
assert.ok(
compareWithPrecision(latitude, coordinates.latitude, PRECISION),
`Latitude mismatch: ${latitude} vs ${coordinates.latitude}`
);

} else {
assert.fail('Expected a valid result');
}



}, {
client: GLOBAL.SERVERS.OPEN,
cluster: GLOBAL.CLUSTERS.OPEN
});
});

describe('compareWithPrecision', () => {
it('should match exact same numbers', () => {
assert.strictEqual(
compareWithPrecision('123.456789', '123.456789', 6),
true
);
});

it('should match when actual has more precision than needed', () => {
assert.strictEqual(
compareWithPrecision('123.456789123456', '123.456789', 6),
true
);
});

it('should match when expected has more precision than needed', () => {
assert.strictEqual(
compareWithPrecision('123.456789', '123.456789123456', 6),
true
);
});

it('should fail when decimals differ within precision', () => {
assert.strictEqual(
compareWithPrecision('123.456689', '123.456789', 6),
false
);
});

it('should handle negative numbers', () => {
assert.strictEqual(
compareWithPrecision('-122.06429868936538', '-122.06429868936539', 13),
true
);
});

it('should fail when integer parts differ', () => {
assert.strictEqual(
compareWithPrecision('124.456789', '123.456789', 6),
false
);
});

it('should handle zero decimal places', () => {
assert.strictEqual(
compareWithPrecision('123.456789', '123.456789', 0),
true
);
});

it('should handle numbers without decimal points', () => {
assert.strictEqual(
compareWithPrecision('123', '123', 6),
true
);
});

it('should handle one number without decimal point', () => {
assert.strictEqual(
compareWithPrecision('123', '123.000', 3),
true
);
});

it('should match Redis coordinates with different precision', () => {
assert.strictEqual(
compareWithPrecision(
'-122.06429868936538696',
'-122.06429868936539',
13
),
true
);
});

it('should match Redis latitude with different precision', () => {
assert.strictEqual(
compareWithPrecision(
'37.37749628831998194',
'37.37749628831998',
14
),
true
);
});
});

export const compareWithPrecision = (actual: string, expected: string, decimals: number): boolean => {
return Math.abs(Number(actual) - Number(expected)) < Math.pow(10, -decimals);
};
6 changes: 3 additions & 3 deletions packages/client/lib/test-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@ import { CredentialsProvider } from './authx';
import { Command } from './RESP/types';
import { BasicCommandParser } from './client/parser';

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

export default utils;
Expand Down
6 changes: 3 additions & 3 deletions packages/entraid/lib/test-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@ import { IdentityProvider, StreamingCredentialsProvider, TokenManager, TokenResp
import TestUtils from '@redis/test-utils';
import { EntraidCredentialsProvider } from './entraid-credentials-provider';

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

const DEBUG_MODE_ARGS = testUtils.isVersionGreaterThan([7]) ?
Expand Down
9 changes: 5 additions & 4 deletions packages/graph/lib/test-utils.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import TestUtils from '@redis/test-utils';
import RedisGraph from '.';

export default new TestUtils({
dockerImageName: 'redis/redis-stack',
dockerImageVersionArgument: 'redisgraph-version',
defaultDockerVersion: '7.4.0-v1'

export default TestUtils.createFromConfig({
dockerImageName: 'redislabs/client-libs-test',
dockerImageVersionArgument: 'redis-version',
defaultDockerVersion: '8.0-M04-pre'
});

export const GLOBAL = {
Expand Down
8 changes: 4 additions & 4 deletions packages/json/lib/test-utils.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import TestUtils from '@redis/test-utils';
import RedisJSON from '.';

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

export const GLOBAL = {
Expand Down
36 changes: 36 additions & 0 deletions packages/search/lib/commands/CONFIG_SET.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,40 @@ describe('FT.CONFIG SET', () => {
'OK'
);
}, GLOBAL.SERVERS.OPEN);

testUtils.testWithClientIfVersionWithinRange([[8], 'LATEST'], 'setSearchConfigGloballyTest', async client => {

const normalizeObject = obj => JSON.parse(JSON.stringify(obj));
assert.equal(await client.configSet('search-default-dialect', '3'),
'OK', 'CONFIG SET should return OK');

assert.deepEqual(
normalizeObject(await client.configGet('search-default-dialect')),
{ 'search-default-dialect': '3' },
'CONFIG GET should return 3'
);

assert.deepEqual(
normalizeObject(await client.ft.configGet('DEFAULT_DIALECT')),
{ 'DEFAULT_DIALECT': '3' },
'FT.CONFIG GET should return 3'
);

const ftConfigSetResult = await client.ft.configSet('DEFAULT_DIALECT', '2');
assert.equal(normalizeObject(ftConfigSetResult), 'OK', 'FT.CONFIG SET should return OK');

assert.deepEqual(
normalizeObject(await client.ft.configGet('DEFAULT_DIALECT')),
{ 'DEFAULT_DIALECT': '2' },
'FT.CONFIG GET should return 2'
);

assert.deepEqual(
normalizeObject(await client.configGet('search-default-dialect')),
{ 'search-default-dialect': '2' },
'CONFIG GET should return 22'
);

}, GLOBAL.SERVERS.OPEN);

});
Loading

0 comments on commit 69d507a

Please sign in to comment.