diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index d7bd649900..89efdb6111 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -22,7 +22,7 @@ jobs: fail-fast: false matrix: node-version: ["18", "20", "22"] - redis-version: ["rs-7.2.0-v13", "rs-7.4.0-v1", "8.0.2"] + redis-version: ["rs-7.2.0-v13", "rs-7.4.0-v1", "8.0.2", "8.2-M01-pre"] steps: - uses: actions/checkout@v4 with: diff --git a/packages/bloom/lib/test-utils.ts b/packages/bloom/lib/test-utils.ts index 62a1f40e87..4396c94f72 100644 --- a/packages/bloom/lib/test-utils.ts +++ b/packages/bloom/lib/test-utils.ts @@ -4,7 +4,7 @@ import RedisBloomModules from '.'; export default TestUtils.createFromConfig({ dockerImageName: 'redislabs/client-libs-test', dockerImageVersionArgument: 'redis-version', - defaultDockerVersion: '8.0.2' + defaultDockerVersion: '8.2-M01-pre' }); export const GLOBAL = { diff --git a/packages/client/lib/commands/BITOP.spec.ts b/packages/client/lib/commands/BITOP.spec.ts index 25fe48fc13..65fe6f8633 100644 --- a/packages/client/lib/commands/BITOP.spec.ts +++ b/packages/client/lib/commands/BITOP.spec.ts @@ -1,6 +1,6 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; -import BITOP from './BITOP'; +import BITOP, { BitOperations } from './BITOP'; import { parseArgs } from './generic-transformers'; describe('BITOP', () => { @@ -20,13 +20,77 @@ describe('BITOP', () => { }); }); - testUtils.testAll('bitOp', async client => { + for (const op of ['AND', 'OR', 'XOR'] as BitOperations[]) { + testUtils.testAll(`bitOp ${op} with non-existing keys`, async client => { + assert.equal( + await client.bitOp(op, '{tag}destKey', ['{tag}key1', '{tag}key2']), + 0 + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); + + testUtils.testAll(`bitOp ${op} with existing keys`, async client => { + await client.set('{tag}key1', 'value1'); + await client.set('{tag}key2', 'value2'); + + assert.equal( + await client.bitOp(op, '{tag}destKey', ['{tag}key1', '{tag}key2']), + 6 + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); + } + + // NOT operation requires only one key + testUtils.testAll('bitOp NOT with non-existing keys', async client => { assert.equal( - await client.bitOp('AND', '{tag}destKey', '{tag}key'), + await client.bitOp('NOT', '{tag}destKey', '{tag}key'), 0 ); }, { client: GLOBAL.SERVERS.OPEN, cluster: GLOBAL.CLUSTERS.OPEN }); + + testUtils.testAll('bitOp NOT with existing keys', async client => { + await client.set('{tag}key', 'value'); + + assert.equal( + await client.bitOp('NOT', '{tag}destKey', '{tag}key'), + 5 + ); + }, { + client: GLOBAL.SERVERS.OPEN, + cluster: GLOBAL.CLUSTERS.OPEN + }); + + // newer operations supported since Redis 8.2 + for (const op of ['DIFF', 'DIFF1', 'ANDOR', 'ONE'] as BitOperations[]) { + testUtils.testAll(`bitOp ${op} with non-existing keys`, async client => { + assert.equal( + await client.bitOp(op, '{tag}destKey', ['{tag}key1', '{tag}key2']), + 0 + ); + }, { + client: { ...GLOBAL.SERVERS.OPEN, minimumDockerVersion: [8, 2] }, + cluster: { ...GLOBAL.CLUSTERS.OPEN, minimumDockerVersion: [8, 2] }, + }); + + testUtils.testAll(`bitOp ${op} with existing keys`, async client => { + await client.set('{tag}key1', 'value1'); + await client.set('{tag}key2', 'value2'); + + assert.equal( + await client.bitOp(op, '{tag}destKey', ['{tag}key1', '{tag}key2']), + 6 + ); + }, { + client: { ...GLOBAL.SERVERS.OPEN, minimumDockerVersion: [8, 2] }, + cluster: { ...GLOBAL.CLUSTERS.OPEN, minimumDockerVersion: [8, 2] }, + }); + } }); diff --git a/packages/client/lib/commands/BITOP.ts b/packages/client/lib/commands/BITOP.ts index cfce482fcb..da8b97ceda 100644 --- a/packages/client/lib/commands/BITOP.ts +++ b/packages/client/lib/commands/BITOP.ts @@ -2,14 +2,14 @@ import { CommandParser } from '../client/parser'; import { NumberReply, Command, RedisArgument } from '../RESP/types'; import { RedisVariadicArgument } from './generic-transformers'; -export type BitOperations = 'AND' | 'OR' | 'XOR' | 'NOT'; +export type BitOperations = 'AND' | 'OR' | 'XOR' | 'NOT' | 'DIFF' | 'DIFF1' | 'ANDOR' | 'ONE'; export default { IS_READ_ONLY: false, /** * Performs bitwise operations between strings * @param parser - The Redis command parser - * @param operation - Bitwise operation to perform: AND, OR, XOR, NOT + * @param operation - Bitwise operation to perform: AND, OR, XOR, NOT, DIFF, DIFF1, ANDOR, ONE * @param destKey - Destination key to store the result * @param key - Source key(s) to perform operation on */ diff --git a/packages/client/lib/sentinel/test-util.ts b/packages/client/lib/sentinel/test-util.ts index 06f8f74093..c8efa47f41 100644 --- a/packages/client/lib/sentinel/test-util.ts +++ b/packages/client/lib/sentinel/test-util.ts @@ -174,7 +174,7 @@ export class SentinelFramework extends DockerBase { this.#testUtils = TestUtils.createFromConfig({ dockerImageName: 'redislabs/client-libs-test', dockerImageVersionArgument: 'redis-version', - defaultDockerVersion: '8.0.2' + defaultDockerVersion: '8.2-M01-pre' }); this.#nodeMap = new Map>>>(); this.#sentinelMap = new Map>>>(); diff --git a/packages/client/lib/test-utils.ts b/packages/client/lib/test-utils.ts index 48736b56cd..809ee788e9 100644 --- a/packages/client/lib/test-utils.ts +++ b/packages/client/lib/test-utils.ts @@ -9,7 +9,7 @@ import RedisBloomModules from '@redis/bloom'; const utils = TestUtils.createFromConfig({ dockerImageName: 'redislabs/client-libs-test', dockerImageVersionArgument: 'redis-version', - defaultDockerVersion: '8.0.2' + defaultDockerVersion: '8.2-M01-pre' }); export default utils; diff --git a/packages/entraid/lib/test-utils.ts b/packages/entraid/lib/test-utils.ts index 1beac93913..3c561d4ba4 100644 --- a/packages/entraid/lib/test-utils.ts +++ b/packages/entraid/lib/test-utils.ts @@ -6,7 +6,7 @@ import { EntraidCredentialsProvider } from './entraid-credentials-provider'; export const testUtils = TestUtils.createFromConfig({ dockerImageName: 'redislabs/client-libs-test', dockerImageVersionArgument: 'redis-version', - defaultDockerVersion: '8.0.2' + defaultDockerVersion: '8.2-M01-pre' }); const DEBUG_MODE_ARGS = testUtils.isVersionGreaterThan([7]) ? diff --git a/packages/json/lib/test-utils.ts b/packages/json/lib/test-utils.ts index 0d250449ac..6b6859d61b 100644 --- a/packages/json/lib/test-utils.ts +++ b/packages/json/lib/test-utils.ts @@ -4,7 +4,7 @@ import RedisJSON from '.'; export default TestUtils.createFromConfig({ dockerImageName: 'redislabs/client-libs-test', dockerImageVersionArgument: 'redis-version', - defaultDockerVersion: '8.0.2' + defaultDockerVersion: '8.2-M01-pre' }); export const GLOBAL = { diff --git a/packages/search/lib/test-utils.ts b/packages/search/lib/test-utils.ts index 354ca16a28..a2b9c816da 100644 --- a/packages/search/lib/test-utils.ts +++ b/packages/search/lib/test-utils.ts @@ -5,7 +5,7 @@ import { RespVersions } from '@redis/client'; export default TestUtils.createFromConfig({ dockerImageName: 'redislabs/client-libs-test', dockerImageVersionArgument: 'redis-version', - defaultDockerVersion: '8.0.2' + defaultDockerVersion: '8.2-M01-pre' }); export const GLOBAL = { diff --git a/packages/time-series/lib/test-utils.ts b/packages/time-series/lib/test-utils.ts index ab3f1cebf7..8a664ee8df 100644 --- a/packages/time-series/lib/test-utils.ts +++ b/packages/time-series/lib/test-utils.ts @@ -4,7 +4,7 @@ import TimeSeries from '.'; export default TestUtils.createFromConfig({ dockerImageName: 'redislabs/client-libs-test', dockerImageVersionArgument: 'redis-version', - defaultDockerVersion: '8.0.2' + defaultDockerVersion: '8.2-M01-pre' }); export const GLOBAL = {