From 683f68bc12c1b93f5e63f4fa971c6ab0d2341cd5 Mon Sep 17 00:00:00 2001 From: Yury-Fridlyand Date: Mon, 29 Jul 2024 16:42:58 -0700 Subject: [PATCH 1/3] Add `LCS` command. Signed-off-by: Yury-Fridlyand --- CHANGELOG.md | 1 + node/npm/glide/index.ts | 4 +- node/src/BaseClient.ts | 129 +++++++++++++++++-- node/src/Commands.ts | 25 ++++ node/src/Transaction.ts | 71 ++++++++++- node/tests/RedisClusterClient.test.ts | 11 +- node/tests/SharedTests.ts | 175 ++++++++++++++++++++++++++ node/tests/TestUtilities.ts | 71 ++++++++++- 8 files changed, 460 insertions(+), 27 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3271ffb9d9..ab5b65fc1b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,5 @@ #### Changes +* Node: Added LCS command ([#2049](https://github.com/valkey-io/valkey-glide/pull/2049)) * Node: Exported client configuration types ([#2023](https://github.com/valkey-io/valkey-glide/pull/2023)) * Java, Python: Update docs for GEOSEARCH command ([#2017](https://github.com/valkey-io/valkey-glide/pull/2017)) * Node: Added FUNCTION LIST command ([#2019](https://github.com/valkey-io/valkey-glide/pull/2019)) diff --git a/node/npm/glide/index.ts b/node/npm/glide/index.ts index b22818940d..f10da7e7fa 100644 --- a/node/npm/glide/index.ts +++ b/node/npm/glide/index.ts @@ -106,7 +106,6 @@ function initialize() { ListDirection, ExpireOptions, FlushMode, - GeoUnit, InfoOptions, InsertPosition, SetOptions, @@ -128,6 +127,7 @@ function initialize() { ConfigurationError, ExecAbortError, RedisError, + ReturnType, RequestError, TimeoutError, ConnectionError, @@ -177,7 +177,6 @@ function initialize() { ListDirection, ExpireOptions, FlushMode, - GeoUnit, InfoOptions, InsertPosition, SetOptions, @@ -199,6 +198,7 @@ function initialize() { ConfigurationError, ExecAbortError, RedisError, + ReturnType, RequestError, TimeoutError, ConnectionError, diff --git a/node/src/BaseClient.ts b/node/src/BaseClient.ts index 78f7fc92b9..13a2d23482 100644 --- a/node/src/BaseClient.ts +++ b/node/src/BaseClient.ts @@ -11,8 +11,8 @@ import * as net from "net"; import { Buffer, BufferWriter, Reader, Writer } from "protobufjs"; import { AggregationType, - BitmapIndexType, BitOffsetOptions, + BitmapIndexType, BitwiseOperation, CoordOrigin, // eslint-disable-line @typescript-eslint/no-unused-vars ExpireOptions, @@ -21,13 +21,13 @@ import { GeoCircleShape, // eslint-disable-line @typescript-eslint/no-unused-vars GeoSearchResultOptions, GeoSearchShape, - GeospatialData, GeoUnit, + GeospatialData, InsertPosition, - KeyWeight, - MemberOrigin, // eslint-disable-line @typescript-eslint/no-unused-vars + KeyWeight, // eslint-disable-line @typescript-eslint/no-unused-vars LPosOptions, ListDirection, + MemberOrigin, // eslint-disable-line @typescript-eslint/no-unused-vars RangeByIndex, RangeByLex, RangeByScore, @@ -41,9 +41,9 @@ import { ZAddOptions, createBLPop, createBRPop, + createBZMPop, createBitCount, createBitOp, - createBZMPop, createBitPos, createDecr, createDecrBy, @@ -76,6 +76,7 @@ import { createIncr, createIncrBy, createIncrByFloat, + createLCS, createLIndex, createLInsert, createLLen, @@ -175,10 +176,11 @@ import { /* eslint-disable-next-line @typescript-eslint/no-explicit-any */ type PromiseFunction = (value?: any) => void; type ErrorFunction = (error: RedisError) => void; -export type ReturnTypeMap = { [key: string]: ReturnType }; +export type ReturnTypeRecord = { [key: string]: ReturnType }; +export type ReturnTypeMap = Map; export type ReturnTypeAttribute = { value: ReturnType; - attributes: ReturnTypeMap; + attributes: ReturnTypeRecord; }; export enum ProtocolVersion { /** Use RESP2 to communicate with the server nodes. */ @@ -195,6 +197,7 @@ export type ReturnType = | bigint | Buffer | Set + | ReturnTypeRecord | ReturnTypeMap | ReturnTypeAttribute | ReturnType[]; @@ -3789,11 +3792,8 @@ export class BaseClient { * where each sub-array represents a single item in the following order: * * - The member (location) name. - * * - The distance from the center as a floating point `number`, in the same unit specified for `searchBy`, if `withDist` is set to `true`. - * * - The geohash of the location as a integer `number`, if `withHash` is set to `true`. - * * - The coordinates as a two item `array` of floating point `number`s, if `withCoord` is set to `true`. * * @example @@ -4022,7 +4022,7 @@ export class BaseClient { * See https://valkey.io/commands/geohash/ for more details. * * @param key - The key of the sorted set. - * @param members - The array of members whose GeoHash strings are to be retrieved. + * @param members - The array of members whose `GeoHash` strings are to be retrieved. * @returns An array of `GeoHash` strings representing the positions of the specified members stored at `key`. * If a member does not exist in the sorted set, a `null` value is returned for that member. * @@ -4040,6 +4040,113 @@ export class BaseClient { ); } + /** + * Returns all the longest common subsequences combined between strings stored at `key1` and `key2`. + * + * since Valkey version 7.0.0. + * + * @remarks When in cluster mode, `key1` and `key2` must map to the same hash slot. + * + * See https://valkey.io/commands/lcs/ for more details. + * + * @param key1 - The key that stores the first string. + * @param key2 - The key that stores the second string. + * @returns A `String` containing all the longest common subsequence combined between the 2 strings. + * An empty `String` is returned if the keys do not exist or have no common subsequences. + * + * @example + * ```typescript + * await client.mset({"testKey1": "abcd", "testKey2": "axcd"}); + * const result = await client.lcs("testKey1", "testKey2"); + * console.log(result); // Output: 'cd' + * ``` + */ + public async lcs(key1: string, key2: string): Promise { + return this.createWritePromise(createLCS(key1, key2)); + } + + /** + * Returns the total length of all the longest common subsequences between strings stored at `key1` and `key2`. + * + * since Valkey version 7.0.0. + * + * @remarks When in cluster mode, `key1` and `key2` must map to the same hash slot. + * + * See https://valkey.io/commands/lcs/ for more details. + * + * @param key1 - The key that stores the first string. + * @param key2 - The key that stores the second string. + * @returns The total length of all the longest common subsequences between the 2 strings. + * + * @example + * ```typescript + * await client.mset({"testKey1": "abcd", "testKey2": "axcd"}); + * const result = await client.lcsLen("testKey1", "testKey2"); + * console.log(result); // Output: 2 + * ``` + */ + public async lcsLen(key1: string, key2: string): Promise { + return this.createWritePromise(createLCS(key1, key2, { len: true })); + } + + /** + * Returns the indices and lengths of the longest common subsequences between strings stored at + * `key1` and `key2`. + * + * since Valkey version 7.0.0. + * + * @remarks When in cluster mode, `key1` and `key2` must map to the same hash slot. + * + * See https://valkey.io/commands/lcs/ for more details. + * + * @param key1 - The key that stores the first string. + * @param key2 - The key that stores the second string. + * @param withMatchLen - (Optional) If `true`, include the length of the substring matched for the each match. + * @param minMatchLen - (Optional) The minimum length of matches to include in the result. + * @returns A `Map` containing the indices of the longest common subsequences between the + * 2 strings and the lengths of the longest common subsequences. The resulting map contains two + * keys, "matches" and "len": + * + * - `"len"` is mapped to the total length of the all longest common subsequences between the 2 strings + * stored as an integer. This value doesn't count `minMatchLen` filter. + * + * - `"matches"` is mapped to a three dimensional array of integers that stores pairs + * of indices that represent the location of the common subsequences in the strings held + * by `key1` and `key2`. + * + * @example + * ```typescript + * await client.mset({"key1": "ohmytext", "key2": "mynewtext"}); + * const result = await client.lcsIdx("key1", "key2"); + * console.log(result); // Output: + * { + * "matches" : + * [ + * [ // first substring match is "text" + * [4, 7], // in `key1` it is located between indices 4 and 7 + * [5, 8], // and in `key2` - in between 5 and 8 + * 4 // the match length, returned if `withMatchLen` set to `true` + * ], + * [ // second substring match is "my" + * [2, 3], // in `key1` it is located between indices 2 and 3 + * [0, 1], // and in `key2` - in between 0 and 1 + * 2 // the match length, returned if `withMatchLen` set to `true` + * ] + * ], + * "len" : 6 // total length of the all matches found + * } + * ``` + */ + public async lcsIdx( + key1: string, + key2: string, + options?: { withMatchLen?: boolean; minMatchLen?: number }, + ): Promise> { + return this.createWritePromise( + createLCS(key1, key2, { idx: options ?? {} }), + ); + } + /** * @internal */ diff --git a/node/src/Commands.ts b/node/src/Commands.ts index 19f8ceed2e..97d4269553 100644 --- a/node/src/Commands.ts +++ b/node/src/Commands.ts @@ -2427,3 +2427,28 @@ export function createZRandMember( return createCommand(RequestType.ZRandMember, args); } + +/** @internal */ +export function createLCS( + key1: string, + key2: string, + options?: { + len?: boolean; + idx?: { withMatchLen?: boolean; minMatchLen?: number }; + }, +): command_request.Command { + const args = [key1, key2]; + + if (options) { + if (options.len) args.push("LEN"); + + if (options.idx) { + args.push("IDX"); + if (options.idx.withMatchLen) args.push("WITHMATCHLEN"); + if (options.idx.minMatchLen !== undefined) + args.push("MINMATCHLEN", options.idx.minMatchLen.toString()); + } + } + + return createCommand(RequestType.LCS, args); +} diff --git a/node/src/Transaction.ts b/node/src/Transaction.ts index 073194bc20..d0f589fe58 100644 --- a/node/src/Transaction.ts +++ b/node/src/Transaction.ts @@ -171,6 +171,7 @@ import { createZRevRankWithScore, createZScore, createZIncrBy, + createLCS, } from "./Commands"; import { command_request } from "./ProtobufMessage"; @@ -2261,11 +2262,8 @@ export class BaseTransaction> { * where each sub-array represents a single item in the following order: * * - The member (location) name. - * * - The distance from the center as a floating point `number`, in the same unit specified for `searchBy`. - * * - The geohash of the location as a integer `number`. - * * - The coordinates as a two item `array` of floating point `number`s. */ public geosearch( @@ -2393,7 +2391,7 @@ export class BaseTransaction> { * See https://valkey.io/commands/geohash/ for more details. * * @param key - The key of the sorted set. - * @param members - The array of members whose GeoHash strings are to be retrieved. + * @param members - The array of members whose `GeoHash` strings are to be retrieved. * * Command Response - An array of `GeoHash` strings representing the positions of the specified members stored at `key`. * If a member does not exist in the sorted set, a `null` value is returned for that member. @@ -2401,6 +2399,71 @@ export class BaseTransaction> { public geohash(key: string, members: string[]): T { return this.addAndReturn(createGeoHash(key, members)); } + + /** + * Returns all the longest common subsequences combined between strings stored at `key1` and `key2`. + * + * since Valkey version 7.0.0. + * + * See https://valkey.io/commands/lcs/ for more details. + * + * @param key1 - The key that stores the first string. + * @param key2 - The key that stores the second string. + * + * Command Response - A `String` containing all the longest common subsequence combined between the 2 strings. + * An empty `String` is returned if the keys do not exist or have no common subsequences. + */ + public lcs(key1: string, key2: string): T { + return this.addAndReturn(createLCS(key1, key2)); + } + + /** + * Returns the total length of all the longest common subsequences between strings stored at `key1` and `key2`. + * + * since Valkey version 7.0.0. + * + * See https://valkey.io/commands/lcs/ for more details. + * + * @param key1 - The key that stores the first string. + * @param key2 - The key that stores the second string. + * + * Command Response - The total length of all the longest common subsequences between the 2 strings. + */ + public lcsLen(key1: string, key2: string): T { + return this.addAndReturn(createLCS(key1, key2, { len: true })); + } + + /** + * Returns the indices and lengths of the longest common subsequences between strings stored at + * `key1` and `key2`. + * + * since Valkey version 7.0.0. + * + * See https://valkey.io/commands/lcs/ for more details. + * + * @param key1 - The key that stores the first string. + * @param key2 - The key that stores the second string. + * @param withMatchLen - (Optional) If `true`, include the length of the substring matched for the each match. + * @param minMatchLen - (Optional) The minimum length of matches to include in the result. + * + * Command Response - A `Map` containing the indices of the longest common subsequences between the + * 2 strings and the lengths of the longest common subsequences. The resulting map contains two + * keys, "matches" and "len": + * + * - `"len"` is mapped to the total length of the all longest common subsequences between the 2 strings + * stored as an integer. This value doesn't count `minMatchLen` filter. + * + * - `"matches"` is mapped to a three dimensional array of integers that stores pairs + * of indices that represent the location of the common subsequences in the strings held + * by `key1` and `key2`. + */ + public lcsIdx( + key1: string, + key2: string, + options?: { withMatchLen?: boolean; minMatchLen?: number }, + ): T { + return this.addAndReturn(createLCS(key1, key2, { idx: options ?? {} })); + } } /** diff --git a/node/tests/RedisClusterClient.test.ts b/node/tests/RedisClusterClient.test.ts index 9b29d6196a..54142773e4 100644 --- a/node/tests/RedisClusterClient.test.ts +++ b/node/tests/RedisClusterClient.test.ts @@ -336,17 +336,14 @@ describe("GlideClusterClient", () => { client.zintercard(["abc", "zxy", "lkn"]), client.zmpop(["abc", "zxy", "lkn"], ScoreFilter.MAX), client.bzmpop(["abc", "zxy", "lkn"], ScoreFilter.MAX, 0.1), + client.lcs("abc", "xyz"), + client.lcsLen("abc", "xyz"), + client.lcsIdx("abc", "xyz"), ); } for (const promise of promises) { - try { - await promise; - } catch (e) { - expect((e as Error).message.toLowerCase()).toContain( - "crossslot", - ); - } + await expect(promise).rejects.toThrowError(/crossslot/i); } client.close(); diff --git a/node/tests/SharedTests.ts b/node/tests/SharedTests.ts index cb43d3ce51..7e484a9824 100644 --- a/node/tests/SharedTests.ts +++ b/node/tests/SharedTests.ts @@ -24,6 +24,7 @@ import { InfoOptions, InsertPosition, ProtocolVersion, + ReturnType, RequestError, ScoreFilter, Script, @@ -5579,6 +5580,180 @@ export function runBaseTests(config: { }, config.timeout, ); + + it.each([ProtocolVersion.RESP2, ProtocolVersion.RESP3])( + `lcs %p`, + async (protocol) => { + await runTest(async (client: BaseClient, cluster) => { + if (cluster.checkIfServerVersionLessThan("7.0.0")) return; + + const key1 = "{lcs}" + uuidv4(); + const key2 = "{lcs}" + uuidv4(); + const key3 = "{lcs}" + uuidv4(); + const key4 = "{lcs}" + uuidv4(); + + // keys does not exist or is empty + checkSimple(await client.lcs(key1, key2)).toEqual(""); + checkSimple(await client.lcsLen(key1, key2)).toEqual(0); + checkSimple(await client.lcsIdx(key1, key2)).toEqual( + new Map([ + ["matches", []], + ["len", 0], + ]), + ); + + // LCS with some strings + checkSimple( + await client.mset({ + [key1]: "abcdefghijk", + [key2]: "defjkjuighijk", + [key3]: "123", + }), + ).toEqual("OK"); + checkSimple(await client.lcs(key1, key2)).toEqual("defghijk"); + checkSimple(await client.lcsLen(key1, key2)).toEqual(8); + + // LCS with only IDX + checkSimple(await client.lcsIdx(key1, key2)).toEqual( + new Map([ + [ + "matches", + [ + [ + [6, 10], + [8, 12], + ], + [ + [3, 5], + [0, 2], + ], + ], + ], + ["len", 8], + ]), + ); + checkSimple(await client.lcsIdx(key1, key2, {})).toEqual( + new Map([ + [ + "matches", + [ + [ + [6, 10], + [8, 12], + ], + [ + [3, 5], + [0, 2], + ], + ], + ], + ["len", 8], + ]), + ); + checkSimple( + await client.lcsIdx(key1, key2, { withMatchLen: false }), + ).toEqual( + new Map([ + [ + "matches", + [ + [ + [6, 10], + [8, 12], + ], + [ + [3, 5], + [0, 2], + ], + ], + ], + ["len", 8], + ]), + ); + + // LCS with IDX and WITHMATCHLEN + checkSimple( + await client.lcsIdx(key1, key2, { withMatchLen: true }), + ).toEqual( + new Map([ + [ + "matches", + [ + [[6, 10], [8, 12], 5], + [[3, 5], [0, 2], 3], + ], + ], + ["len", 8], + ]), + ); + + // LCS with IDX and MINMATCHLEN + checkSimple( + await client.lcsIdx(key1, key2, { minMatchLen: 4 }), + ).toEqual( + new Map([ + [ + "matches", + [ + [ + [6, 10], + [8, 12], + ], + ], + ], + ["len", 8], + ]), + ); + // LCS with IDX and a negative MINMATCHLEN + checkSimple( + await client.lcsIdx(key1, key2, { minMatchLen: -1 }), + ).toEqual( + new Map([ + [ + "matches", + [ + [ + [6, 10], + [8, 12], + ], + [ + [3, 5], + [0, 2], + ], + ], + ], + ["len", 8], + ]), + ); + + // LCS with IDX, MINMATCHLEN, and WITHMATCHLEN + checkSimple( + await client.lcsIdx(key1, key2, { + minMatchLen: 4, + withMatchLen: true, + }), + ).toEqual( + new Map([ + ["matches", [[[6, 10], [8, 12], 5]]], + ["len", 8], + ]), + ); + + // non-string keys are used + checkSimple(await client.sadd(key4, ["_"])).toEqual(1); + await expect(client.lcs(key1, key4)).rejects.toThrow( + RequestError, + ); + await expect(client.lcsLen(key1, key4)).rejects.toThrow( + RequestError, + ); + await expect(client.lcsIdx(key1, key4)).rejects.toThrow( + RequestError, + ); + }, protocol); + }, + config.timeout, + ); } export function runCommonTests(config: { diff --git a/node/tests/TestUtilities.ts b/node/tests/TestUtilities.ts index e13ba88666..74f846dfbb 100644 --- a/node/tests/TestUtilities.ts +++ b/node/tests/TestUtilities.ts @@ -430,9 +430,9 @@ export async function transactionTest( baseTransaction: Transaction | ClusterTransaction, version: string, ): Promise<[string, ReturnType][]> { - const key1 = "{key}" + uuidv4(); - const key2 = "{key}" + uuidv4(); - const key3 = "{key}" + uuidv4(); + const key1 = "{key}" + uuidv4(); // string + const key2 = "{key}" + uuidv4(); // string + const key3 = "{key}" + uuidv4(); // string const key4 = "{key}" + uuidv4(); const key5 = "{key}" + uuidv4(); const key6 = "{key}" + uuidv4(); @@ -999,6 +999,71 @@ export async function transactionTest( withCode: true, }); responseData.push(["functionList({ libName, true})", []]); + + baseTransaction + .mset({ [key1]: "abcd", [key2]: "bcde", [key3]: "wxyz" }) + .lcs(key1, key2) + .lcs(key1, key3) + .lcsLen(key1, key2) + .lcsLen(key1, key3) + .lcsIdx(key1, key2) + .lcsIdx(key1, key2, { minMatchLen: 1 }) + .lcsIdx(key1, key2, { withMatchLen: true }) + .lcsIdx(key1, key2, { withMatchLen: true, minMatchLen: 1 }) + .del([key1, key2, key3]); + + responseData.push( + ['mset({[key1]: "abcd", [key2]: "bcde", [key3]: "wxyz"})', "OK"], + ["lcs(key1, key2)", "bcd"], + ["lcs(key1, key3)", ""], + ["lcsLen(key1, key2)", 3], + ["lcsLen(key1, key3)", 0], + [ + "lcsIdx(key1, key2)", + new Map([ + [ + "matches", + [ + [ + [1, 3], + [0, 2], + ], + ], + ], + ["len", 3], + ]), + ], + [ + "lcsIdx(key1, key2, {minMatchLen: 1})", + new Map([ + [ + "matches", + [ + [ + [1, 3], + [0, 2], + ], + ], + ], + ["len", 3], + ]), + ], + [ + "lcsIdx(key1, key2, {withMatchLen: true})", + new Map([ + ["matches", [[[1, 3], [0, 2], 3]]], + ["len", 3], + ]), + ], + [ + "lcsIdx(key1, key2, {withMatchLen: true, minMatchLen: 1})", + new Map([ + ["matches", [[[1, 3], [0, 2], 3]]], + ["len", 3], + ]), + ], + ["del([key1, key2, key3])", 3], + ); } return responseData; From d4b2e5fe9b74287aa44d8139dd92f6af6e0d45cd Mon Sep 17 00:00:00 2001 From: Yury-Fridlyand Date: Tue, 30 Jul 2024 13:36:13 -0700 Subject: [PATCH 2/3] Signed-off-by: Yury-Fridlyand --- node/src/BaseClient.ts | 4 +--- node/src/Commands.ts | 3 +-- node/src/Transaction.ts | 10 ++++------ 3 files changed, 6 insertions(+), 11 deletions(-) diff --git a/node/src/BaseClient.ts b/node/src/BaseClient.ts index 13a2d23482..4dfd6ba10a 100644 --- a/node/src/BaseClient.ts +++ b/node/src/BaseClient.ts @@ -4106,10 +4106,8 @@ export class BaseClient { * @returns A `Map` containing the indices of the longest common subsequences between the * 2 strings and the lengths of the longest common subsequences. The resulting map contains two * keys, "matches" and "len": - * * - `"len"` is mapped to the total length of the all longest common subsequences between the 2 strings - * stored as an integer. This value doesn't count `minMatchLen` filter. - * + * stored as an integer. This value doesn't count towards the `minMatchLen` filter. * - `"matches"` is mapped to a three dimensional array of integers that stores pairs * of indices that represent the location of the common subsequences in the strings held * by `key1` and `key2`. diff --git a/node/src/Commands.ts b/node/src/Commands.ts index 97d4269553..eac04c52fc 100644 --- a/node/src/Commands.ts +++ b/node/src/Commands.ts @@ -2441,8 +2441,7 @@ export function createLCS( if (options) { if (options.len) args.push("LEN"); - - if (options.idx) { + else if (options.idx) { args.push("IDX"); if (options.idx.withMatchLen) args.push("WITHMATCHLEN"); if (options.idx.minMatchLen !== undefined) diff --git a/node/src/Transaction.ts b/node/src/Transaction.ts index d0f589fe58..aa730710f7 100644 --- a/node/src/Transaction.ts +++ b/node/src/Transaction.ts @@ -17,8 +17,8 @@ import { GeoCircleShape, // eslint-disable-line @typescript-eslint/no-unused-vars GeoSearchResultOptions, GeoSearchShape, - GeospatialData, GeoUnit, + GeospatialData, InfoOptions, InsertPosition, KeyWeight, @@ -90,6 +90,7 @@ import { createIncrBy, createIncrByFloat, createInfo, + createLCS, createLIndex, createLInsert, createLLen, @@ -154,6 +155,7 @@ import { createZDiff, createZDiffStore, createZDiffWithScores, + createZIncrBy, createZInterCard, createZInterstore, createZMPop, @@ -170,8 +172,6 @@ import { createZRevRank, createZRevRankWithScore, createZScore, - createZIncrBy, - createLCS, } from "./Commands"; import { command_request } from "./ProtobufMessage"; @@ -2449,10 +2449,8 @@ export class BaseTransaction> { * Command Response - A `Map` containing the indices of the longest common subsequences between the * 2 strings and the lengths of the longest common subsequences. The resulting map contains two * keys, "matches" and "len": - * * - `"len"` is mapped to the total length of the all longest common subsequences between the 2 strings - * stored as an integer. This value doesn't count `minMatchLen` filter. - * + * stored as an integer. This value doesn't count towards the `minMatchLen` filter. * - `"matches"` is mapped to a three dimensional array of integers that stores pairs * of indices that represent the location of the common subsequences in the strings held * by `key1` and `key2`. From a6138b1dffdc8e49642f87e85de86684d21e5d9d Mon Sep 17 00:00:00 2001 From: Yury-Fridlyand Date: Tue, 30 Jul 2024 13:40:50 -0700 Subject: [PATCH 3/3] Signed-off-by: Yury-Fridlyand --- node/tests/RedisClient.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/node/tests/RedisClient.test.ts b/node/tests/RedisClient.test.ts index 173f1b0b8b..8363b44e96 100644 --- a/node/tests/RedisClient.test.ts +++ b/node/tests/RedisClient.test.ts @@ -12,7 +12,7 @@ import { } from "@jest/globals"; import { BufferReader, BufferWriter } from "protobufjs"; import { v4 as uuidv4 } from "uuid"; -import { GlideClient, ProtocolVersion, Transaction , ListDirection } from ".."; +import { GlideClient, ProtocolVersion, Transaction, ListDirection } from ".."; import { RedisCluster } from "../../utils/TestUtils.js"; import { FlushMode } from "../build-ts/src/Commands"; import { command_request } from "../src/ProtobufMessage";