diff --git a/CHANGELOG.md b/CHANGELOG.md index 6730c12791..c6e212247d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,5 @@ #### Changes +* Node: Added ZINTER and ZUNION commands ([#1556](https://github.com/aws/glide-for-redis/pull/1556)) * Node: Added EXPIRETIME and PEXPIRETIME commands ([#2063](https://github.com/valkey-io/valkey-glide/pull/2063)) * Node: Added SORT commands ([#2028](https://github.com/valkey-io/valkey-glide/pull/2028)) * Node: Added LASTSAVE command ([#2059](https://github.com/valkey-io/valkey-glide/pull/2059)) diff --git a/node/src/BaseClient.ts b/node/src/BaseClient.ts index 0ef0563708..4ad3790ab3 100644 --- a/node/src/BaseClient.ts +++ b/node/src/BaseClient.ts @@ -161,6 +161,7 @@ import { createZDiffStore, createZDiffWithScores, createZIncrBy, + createZInter, createZInterCard, createZInterstore, createZLexCount, @@ -180,6 +181,7 @@ import { createZRevRankWithScore, createZScan, createZScore, + createZUnion, } from "./Commands"; import { ClosingError, @@ -3051,9 +3053,9 @@ export class BaseClient { * await client.zadd("key1", {"member1": 10.5, "member2": 8.2}) * await client.zadd("key2", {"member1": 9.5}) * await client.zinterstore("my_sorted_set", ["key1", "key2"]) // Output: 1 - Indicates that the sorted set "my_sorted_set" contains one element. - * await client.zrange_withscores("my_sorted_set", RangeByIndex(0, -1)) // Output: {'member1': 20} - "member1" is now stored in "my_sorted_set" with score of 20. + * await client.zrangeWithScores("my_sorted_set", RangeByIndex(0, -1)) // Output: {'member1': 20} - "member1" is now stored in "my_sorted_set" with score of 20. * await client.zinterstore("my_sorted_set", ["key1", "key2"] , AggregationType.MAX ) // Output: 1 - Indicates that the sorted set "my_sorted_set" contains one element, and it's score is the maximum score between the sets. - * await client.zrange_withscores("my_sorted_set", RangeByIndex(0, -1)) // Output: {'member1': 10.5} - "member1" is now stored in "my_sorted_set" with score of 10.5. + * await client.zrangeWithScores("my_sorted_set", RangeByIndex(0, -1)) // Output: {'member1': 10.5} - "member1" is now stored in "my_sorted_set" with score of 10.5. * ``` */ public zinterstore( @@ -3066,6 +3068,134 @@ export class BaseClient { ); } + /** + * Computes the intersection of sorted sets given by the specified `keys` and returns a list of intersecting elements. + * To get the scores as well, see `zinterWithScores`. + * To store the result in a key as a sorted set, see `zinterStore`. + * + * When in cluster mode, all keys in `keys` must map to the same hash slot. + * + * since - Valkey version 6.2.0. + * + * See https://valkey.io/commands/zinter/ for more details. + * + * @param keys - The keys of the sorted sets. + * @returns The resulting array of intersecting elements. + * + * since Valkey version 6.2.0. + * + * @example + * ```typescript + * await client.zadd("key1", {"member1": 10.5, "member2": 8.2}); + * await client.zadd("key2", {"member1": 9.5}); + * const result = await client.zinter(["key1", "key2"]); + * console.log(result); // Output: ['member1'] + * ``` + */ + public zinter(keys: string[]): Promise { + return this.createWritePromise(createZInter(keys)); + } + + /** + * Computes the intersection of sorted sets given by the specified `keys` and returns a list of intersecting elements with scores. + * To get the elements only, see `zinter`. + * To store the result in a key as a sorted set, see `zinterStore`. + * + * When in cluster mode, all keys in `keys` must map to the same hash slot. + * + * See https://valkey.io/commands/zinter/ for more details. + * + * @param keys - The keys of the sorted sets with possible formats: + * string[] - for keys only. + * KeyWeight[] - for weighted keys with score multipliers. + * @param aggregationType - Specifies the aggregation strategy to apply when combining the scores of elements. See `AggregationType`. + * @returns The resulting sorted set with scores. + * + * since Valkey version 6.2.0. + * + * @example + * ```typescript + * await client.zadd("key1", {"member1": 10.5, "member2": 8.2}); + * await client.zadd("key2", {"member1": 9.5}); + * const result1 = await client.zinterWithScores(["key1", "key2"]); + * console.log(result1); // Output: {'member1': 20} - "member1" with score of 20 is the result + * const result2 = await client.zinterWithScores(["key1", "key2"], AggregationType.MAX) + * console.log(result2); // Output: {'member1': 10.5} - "member1" with score of 10.5 is the result. + * ``` + */ + public zinterWithScores( + keys: string[] | KeyWeight[], + aggregationType?: AggregationType, + ): Promise> { + return this.createWritePromise( + createZInter(keys, aggregationType, true), + ); + } + + /** + * Computes the union of sorted sets given by the specified `keys` and returns a list of union elements. + * To get the scores as well, see `zunionWithScores`. + * + * To store the result in a key as a sorted set, see `zunionStore`. + * + * When in cluster mode, all keys in `keys` must map to the same hash slot. + * + * since - Valkey version 6.2.0. + * + * See https://valkey.io/commands/zunion/ for more details. + * + * @param keys - The keys of the sorted sets. + * @returns The resulting array of union elements. + * + * since Valkey version 6.2.0. + * + * @example + * ```typescript + * await client.zadd("key1", {"member1": 10.5, "member2": 8.2}); + * await client.zadd("key2", {"member1": 9.5}); + * const result = await client.zunion(["key1", "key2"]); + * console.log(result); // Output: ['member1', 'member2'] + * ``` + */ + public zunion(keys: string[]): Promise { + return this.createWritePromise(createZUnion(keys)); + } + + /** + * Computes the intersection of sorted sets given by the specified `keys` and returns a list of union elements with scores. + * To get the elements only, see `zunion`. + * + * When in cluster mode, all keys in `keys` must map to the same hash slot. + * + * See https://valkey.io/commands/zunion/ for more details. + * + * @param keys - The keys of the sorted sets with possible formats: + * string[] - for keys only. + * KeyWeight[] - for weighted keys with score multipliers. + * @param aggregationType - Specifies the aggregation strategy to apply when combining the scores of elements. See `AggregationType`. + * @returns The resulting sorted set with scores. + * + * since Valkey version 6.2.0. + * + * @example + * ```typescript + * await client.zadd("key1", {"member1": 10.5, "member2": 8.2}); + * await client.zadd("key2", {"member1": 9.5}); + * const result1 = await client.zunionWithScores(["key1", "key2"]); + * console.log(result1); // {'member1': 20, 'member2': 8.2} + * const result2 = await client.zunionWithScores(["key1", "key2"], "MAX"); + * console.log(result2); // {'member1': 10.5, 'member2': 8.2} + * ``` + */ + public zunionWithScores( + keys: string[] | KeyWeight[], + aggregationType?: AggregationType, + ): Promise> { + return this.createWritePromise( + createZUnion(keys, aggregationType, true), + ); + } + /** * Returns a random member from the sorted set stored at `key`. * diff --git a/node/src/Commands.ts b/node/src/Commands.ts index c80e5db0e3..0438027f41 100644 --- a/node/src/Commands.ts +++ b/node/src/Commands.ts @@ -1406,16 +1406,51 @@ export function createZInterstore( keys: string[] | KeyWeight[], aggregationType?: AggregationType, ): command_request.Command { - const args = createZCmdStoreArgs(destination, keys, aggregationType); + const args = createZCmdArgs(keys, aggregationType, false, destination); return createCommand(RequestType.ZInterStore, args); } -function createZCmdStoreArgs( - destination: string, +/** + * @internal + */ +export function createZInter( + keys: string[] | KeyWeight[], + aggregationType?: AggregationType, + withScores?: boolean, +): command_request.Command { + const args = createZCmdArgs(keys, aggregationType, withScores); + return createCommand(RequestType.ZInter, args); +} + +/** + * @internal + */ +export function createZUnion( keys: string[] | KeyWeight[], aggregationType?: AggregationType, + withScores?: boolean, +): command_request.Command { + const args = createZCmdArgs(keys, aggregationType, withScores); + return createCommand(RequestType.ZUnion, args); +} + +/** + * @internal + * Helper function for Zcommands (ZInter, ZinterStore, ZUnion..) that arranges arguments in the server's required order. + */ +function createZCmdArgs( + keys: string[] | KeyWeight[], + aggregationType?: AggregationType, + withscores?: boolean, + destination?: string, ): string[] { - const args: string[] = [destination, keys.length.toString()]; + const args = []; + + if (destination) { + args.push(destination); + } + + args.push(keys.length.toString()); if (typeof keys[0] === "string") { args.push(...(keys as string[])); @@ -1430,6 +1465,10 @@ function createZCmdStoreArgs( args.push("AGGREGATE", aggregationType); } + if (withscores) { + args.push("WITHSCORES"); + } + return args; } diff --git a/node/src/Transaction.ts b/node/src/Transaction.ts index 1009b966c1..8194506e78 100644 --- a/node/src/Transaction.ts +++ b/node/src/Transaction.ts @@ -192,6 +192,7 @@ import { createZDiffStore, createZDiffWithScores, createZIncrBy, + createZInter, createZInterCard, createZInterstore, createZLexCount, @@ -211,6 +212,7 @@ import { createZRevRankWithScore, createZScan, createZScore, + createZUnion, } from "./Commands"; import { command_request } from "./ProtobufMessage"; @@ -1676,6 +1678,8 @@ export class BaseTransaction> { * KeyWeight[] - for weighted keys with score multipliers. * @param aggregationType - Specifies the aggregation strategy to apply when combining the scores of elements. See `AggregationType`. * Command Response - The number of elements in the resulting sorted set stored at `destination`. + * + * since Valkey version 6.2.0. */ public zinterstore( destination: string, @@ -1687,6 +1691,90 @@ export class BaseTransaction> { ); } + /** + * Computes the intersection of sorted sets given by the specified `keys` and returns a list of intersecting elements. + * To get the scores as well, see `zinter_withscores`. + * To store the result in a key as a sorted set, see `zinterstore`. + * + * When in cluster mode, all keys in `keys` must map to the same hash slot. + * + * See https://valkey.io/commands/zinter/ for more details. + * + * @param keys - The keys of the sorted sets. + * Command Response - The resulting array of intersecting elements. + * + * since Valkey version 6.2.0. + */ + public zinter(keys: string[]): T { + return this.addAndReturn(createZInter(keys)); + } + + /** + * Computes the intersection of sorted sets given by the specified `keys` and returns a list of intersecting elements with scores. + * To get the elements only, see `zinter`. + * To store the result in a key as a sorted set, see `zinterstore`. + * + * When in cluster mode, all keys in `keys` must map to the same hash slot. + * + * See https://valkey.io/commands/zinter/ for more details. + * + * @param keys - The keys of the sorted sets with possible formats: + * string[] - for keys only. + * KeyWeight[] - for weighted keys with score multipliers. + * @param aggregationType - Specifies the aggregation strategy to apply when combining the scores of elements. See `AggregationType`. + * Command Response - The resulting sorted set with scores. + * + * since Valkey version 6.2.0. + */ + public zinterWithScores( + keys: string[] | KeyWeight[], + aggregationType?: AggregationType, + ): T { + return this.addAndReturn(createZInter(keys, aggregationType, true)); + } + + /** + * Computes the union of sorted sets given by the specified `keys` and returns a list of union elements. + * To get the scores as well, see `zunion_withscores`. + * + * To store the result in a key as a sorted set, see `zunionstore`. + * + * When in cluster mode, all keys in `keys` must map to the same hash slot. + * + * See https://valkey.io/commands/zunion/ for more details. + * + * @param keys - The keys of the sorted sets. + * Command Response - The resulting array of union elements. + * + * since Valkey version 6.2.0. + */ + public zunion(keys: string[]): T { + return this.addAndReturn(createZUnion(keys)); + } + + /** + * Computes the intersection of sorted sets given by the specified `keys` and returns a list of union elements with scores. + * To get the elements only, see `zunion`. + * + * When in cluster mode, all keys in `keys` must map to the same hash slot. + * + * See https://valkey.io/commands/zunion/ for more details. + * + * @param keys - The keys of the sorted sets with possible formats: + * string[] - for keys only. + * KeyWeight[] - for weighted keys with score multipliers. + * @param aggregationType - Specifies the aggregation strategy to apply when combining the scores of elements. See `AggregationType`. + * Commnd Response - The resulting sorted set with scores. + * + * since Valkey version 6.2.0. + */ + public zunionWithScores( + keys: string[] | KeyWeight[], + aggregationType?: AggregationType, + ): T { + return this.addAndReturn(createZUnion(keys, aggregationType, true)); + } + /** * Returns a random member from the sorted set stored at `key`. * diff --git a/node/tests/GlideClusterClient.test.ts b/node/tests/GlideClusterClient.test.ts index 930df79c62..48d5d314b6 100644 --- a/node/tests/GlideClusterClient.test.ts +++ b/node/tests/GlideClusterClient.test.ts @@ -327,8 +327,12 @@ describe("GlideClusterClient", () => { client.smove("abc", "zxy", "value"), client.renamenx("abc", "zxy"), client.sinter(["abc", "zxy", "lkn"]), + client.zinter(["abc", "zxy", "lkn"]), + client.zinterWithScores(["abc", "zxy", "lkn"]), client.sinterstore("abc", ["zxy", "lkn"]), client.zinterstore("abc", ["zxy", "lkn"]), + client.zunion(["abc", "zxy", "lkn"]), + client.zunionWithScores(["abc", "zxy", "lkn"]), client.sunionstore("abc", ["zxy", "lkn"]), client.sunion(["abc", "zxy", "lkn"]), client.pfcount(["abc", "zxy", "lkn"]), diff --git a/node/tests/SharedTests.ts b/node/tests/SharedTests.ts index dd47041824..e3e7939814 100644 --- a/node/tests/SharedTests.ts +++ b/node/tests/SharedTests.ts @@ -3726,6 +3726,346 @@ export function runBaseTests(config: { config.timeout, ); + async function zinterBasicTest(client: BaseClient) { + const key1 = "{testKey}:1-" + uuidv4(); + const key2 = "{testKey}:2-" + uuidv4(); + + const membersScores1 = { one: 1.0, two: 2.0 }; + const membersScores2 = { one: 1.5, two: 2.5, three: 3.5 }; + + expect(await client.zadd(key1, membersScores1)).toEqual(2); + expect(await client.zadd(key2, membersScores2)).toEqual(3); + + const resultZinter = await client.zinter([key1, key2]); + const expectedZinter = ["one", "two"]; + expect(resultZinter).toEqual(expectedZinter); + } + + async function zinterWithScoresBasicTest(client: BaseClient) { + const key1 = "{testKey}:1-" + uuidv4(); + const key2 = "{testKey}:2-" + uuidv4(); + + const membersScores1 = { one: 1.0, two: 2.0 }; + const membersScores2 = { one: 1.5, two: 2.5, three: 3.5 }; + + expect(await client.zadd(key1, membersScores1)).toEqual(2); + expect(await client.zadd(key2, membersScores2)).toEqual(3); + + const resultZinterWithScores = await client.zinterWithScores([ + key1, + key2, + ]); + const expectedZinterWithScores = { + one: 2.5, + two: 4.5, + }; + expect(resultZinterWithScores).toEqual(expectedZinterWithScores); + } + + async function zinterWithScoresWithMaxAggregation(client: BaseClient) { + const key1 = "{testKey}:1-" + uuidv4(); + const key2 = "{testKey}:2-" + uuidv4(); + + const membersScores1 = { one: 1.0, two: 2.0 }; + const membersScores2 = { one: 1.5, two: 2.5, three: 3.5 }; + + expect(await client.zadd(key1, membersScores1)).toEqual(2); + expect(await client.zadd(key2, membersScores2)).toEqual(3); + + // Intersection results are aggregated by the MAX score of elements + const zinterWithScoresResults = await client.zinterWithScores( + [key1, key2], + "MAX", + ); + const expectedMapMax = { + one: 1.5, + two: 2.5, + }; + expect(zinterWithScoresResults).toEqual(expectedMapMax); + } + + async function zinterWithScoresWithMinAggregation(client: BaseClient) { + const key1 = "{testKey}:1-" + uuidv4(); + const key2 = "{testKey}:2-" + uuidv4(); + + const membersScores1 = { one: 1.0, two: 2.0 }; + const membersScores2 = { one: 1.5, two: 2.5, three: 3.5 }; + + expect(await client.zadd(key1, membersScores1)).toEqual(2); + expect(await client.zadd(key2, membersScores2)).toEqual(3); + + // Intersection results are aggregated by the MIN score of elements + const zinterWithScoresResults = await client.zinterWithScores( + [key1, key2], + "MIN", + ); + const expectedMapMin = { + one: 1.0, + two: 2.0, + }; + expect(zinterWithScoresResults).toEqual(expectedMapMin); + } + + async function zinterWithScoresWithSumAggregation(client: BaseClient) { + const key1 = "{testKey}:1-" + uuidv4(); + const key2 = "{testKey}:2-" + uuidv4(); + + const membersScores1 = { one: 1.0, two: 2.0 }; + const membersScores2 = { one: 1.5, two: 2.5, three: 3.5 }; + + expect(await client.zadd(key1, membersScores1)).toEqual(2); + expect(await client.zadd(key2, membersScores2)).toEqual(3); + + // Intersection results are aggregated by the SUM score of elements + const zinterWithScoresResults = await client.zinterWithScores( + [key1, key2], + "SUM", + ); + const expectedMapSum = { + one: 2.5, + two: 4.5, + }; + expect(zinterWithScoresResults).toEqual(expectedMapSum); + } + + async function zinterWithScoresWithWeightsAndAggregation( + client: BaseClient, + ) { + const key1 = "{testKey}:1-" + uuidv4(); + const key2 = "{testKey}:2-" + uuidv4(); + + const membersScores1 = { one: 1.0, two: 2.0 }; + const membersScores2 = { one: 1.5, two: 2.5, three: 3.5 }; + + expect(await client.zadd(key1, membersScores1)).toEqual(2); + expect(await client.zadd(key2, membersScores2)).toEqual(3); + + // Intersection results are aggregated by the SUM score of elements with weights + const zinterWithScoresResults = await client.zinterWithScores( + [ + [key1, 3], + [key2, 2], + ], + "SUM", + ); + const expectedMapSum = { + one: 6, + two: 11, + }; + expect(zinterWithScoresResults).toEqual(expectedMapSum); + } + + async function zinterEmptyCases(client: BaseClient) { + const key1 = "{testKey}:1-" + uuidv4(); + + // Non existing key zinter + expect( + await client.zinter([key1, "{testKey}-non_existing_key"]), + ).toEqual([]); + + // Non existing key zinterWithScores + expect( + await client.zinterWithScores([key1, "{testKey}-non_existing_key"]), + ).toEqual({}); + + // Empty list check zinter + await expect(client.zinter([])).rejects.toThrow(); + + // Empty list check zinterWithScores + await expect(client.zinterWithScores([])).rejects.toThrow(); + } + + it.each([ProtocolVersion.RESP2, ProtocolVersion.RESP3])( + `zinter test_%p`, + async (protocol) => { + await runTest(async (client: BaseClient) => { + await zinterBasicTest(client); + await zinterWithScoresBasicTest(client); + await zinterWithScoresWithMaxAggregation(client); + await zinterWithScoresWithMinAggregation(client); + await zinterWithScoresWithSumAggregation(client); + await zinterWithScoresWithWeightsAndAggregation(client); + await zinterEmptyCases(client); + }, protocol); + }, + config.timeout, + ); + + async function zunionBasicTest(client: BaseClient) { + const key1 = "{testKey}:1-" + uuidv4(); + const key2 = "{testKey}:2-" + uuidv4(); + + const membersScores1 = { one: 1.0, two: 2.0 }; + const membersScores2 = { one: 1.5, two: 2.5, three: 3.5 }; + + expect(await client.zadd(key1, membersScores1)).toEqual(2); + expect(await client.zadd(key2, membersScores2)).toEqual(3); + + const resultZunion = await client.zunion([key1, key2]); + const expectedZunion = ["one", "two", "three"]; + + expect(resultZunion.sort()).toEqual(expectedZunion.sort()); + } + + async function zunionWithScoresBasicTest(client: BaseClient) { + const key1 = "{testKey}:1-" + uuidv4(); + const key2 = "{testKey}:2-" + uuidv4(); + + const membersScores1 = { one: 1.0, two: 2.0 }; + const membersScores2 = { one: 1.5, two: 2.5, three: 3.5 }; + + expect(await client.zadd(key1, membersScores1)).toEqual(2); + expect(await client.zadd(key2, membersScores2)).toEqual(3); + + const resultZunionWithScores = await client.zunionWithScores([ + key1, + key2, + ]); + const expectedZunionWithScores = { + one: 2.5, + two: 4.5, + three: 3.5, + }; + expect(resultZunionWithScores).toEqual(expectedZunionWithScores); + } + + async function zunionWithScoresWithMaxAggregation(client: BaseClient) { + const key1 = "{testKey}:1-" + uuidv4(); + const key2 = "{testKey}:2-" + uuidv4(); + + const membersScores1 = { one: 1.0, two: 2.0 }; + const membersScores2 = { one: 1.5, two: 2.5, three: 3.5 }; + + expect(await client.zadd(key1, membersScores1)).toEqual(2); + expect(await client.zadd(key2, membersScores2)).toEqual(3); + + // Union results are aggregated by the MAX score of elements + const zunionWithScoresResults = await client.zunionWithScores( + [key1, key2], + "MAX", + ); + const expectedMapMax = { + one: 1.5, + two: 2.5, + three: 3.5, + }; + expect(zunionWithScoresResults).toEqual(expectedMapMax); + } + + async function zunionWithScoresWithMinAggregation(client: BaseClient) { + const key1 = "{testKey}:1-" + uuidv4(); + const key2 = "{testKey}:2-" + uuidv4(); + + const membersScores1 = { one: 1.0, two: 2.0 }; + const membersScores2 = { one: 1.5, two: 2.5, three: 3.5 }; + + expect(await client.zadd(key1, membersScores1)).toEqual(2); + expect(await client.zadd(key2, membersScores2)).toEqual(3); + + // Union results are aggregated by the MIN score of elements + const zunionWithScoresResults = await client.zunionWithScores( + [key1, key2], + "MIN", + ); + const expectedMapMin = { + one: 1.0, + two: 2.0, + three: 3.5, + }; + expect(zunionWithScoresResults).toEqual(expectedMapMin); + } + + async function zunionWithScoresWithSumAggregation(client: BaseClient) { + const key1 = "{testKey}:1-" + uuidv4(); + const key2 = "{testKey}:2-" + uuidv4(); + + const membersScores1 = { one: 1.0, two: 2.0 }; + const membersScores2 = { one: 1.5, two: 2.5, three: 3.5 }; + + expect(await client.zadd(key1, membersScores1)).toEqual(2); + expect(await client.zadd(key2, membersScores2)).toEqual(3); + + // Union results are aggregated by the SUM score of elements + const zunionWithScoresResults = await client.zunionWithScores( + [key1, key2], + "SUM", + ); + const expectedMapSum = { + one: 2.5, + two: 4.5, + three: 3.5, + }; + expect(zunionWithScoresResults).toEqual(expectedMapSum); + } + + async function zunionWithScoresWithWeightsAndAggregation( + client: BaseClient, + ) { + const key1 = "{testKey}:1-" + uuidv4(); + const key2 = "{testKey}:2-" + uuidv4(); + + const membersScores1 = { one: 1.0, two: 2.0 }; + const membersScores2 = { one: 1.5, two: 2.5, three: 3.5 }; + + expect(await client.zadd(key1, membersScores1)).toEqual(2); + expect(await client.zadd(key2, membersScores2)).toEqual(3); + + // Union results are aggregated by the SUM score of elements with weights + const zunionWithScoresResults = await client.zunionWithScores( + [ + [key1, 3], + [key2, 2], + ], + "SUM", + ); + const expectedMapSum = { + one: 6, + two: 11, + three: 7, + }; + expect(zunionWithScoresResults).toEqual(expectedMapSum); + } + + async function zunionEmptyCases(client: BaseClient) { + const key1 = "{testKey}:1-" + uuidv4(); + + const membersScores1 = { one: 1.0, two: 2.0 }; + + expect(await client.zadd(key1, membersScores1)).toEqual(2); + + // Non existing key zunion + expect( + await client.zunion([key1, "{testKey}-non_existing_key"]), + ).toEqual(["one", "two"]); + + // Non existing key zunionWithScores + expect( + await client.zunionWithScores([key1, "{testKey}-non_existing_key"]), + ).toEqual(membersScores1); + + // Empty list check zunion + await expect(client.zunion([])).rejects.toThrow(); + + // Empty list check zunionWithScores + await expect(client.zunionWithScores([])).rejects.toThrow(); + } + + it.each([ProtocolVersion.RESP2, ProtocolVersion.RESP3])( + `zunion test_%p`, + async (protocol) => { + await runTest(async (client: BaseClient) => { + await zunionBasicTest(client); + await zunionWithScoresBasicTest(client); + await zunionWithScoresWithMaxAggregation(client); + await zunionWithScoresWithMinAggregation(client); + await zunionWithScoresWithSumAggregation(client); + await zunionWithScoresWithWeightsAndAggregation(client); + await zunionEmptyCases(client); + }, protocol); + }, + config.timeout, + ); + it.each([ProtocolVersion.RESP2, ProtocolVersion.RESP3])( `type test_%p`, async (protocol) => { diff --git a/node/tests/TestUtilities.ts b/node/tests/TestUtilities.ts index bf840b29d4..4fd0851aa5 100644 --- a/node/tests/TestUtilities.ts +++ b/node/tests/TestUtilities.ts @@ -472,6 +472,8 @@ export async function transactionTest( const key22 = "{key}" + uuidv4(); // list for sort const key23 = "{key}" + uuidv4(); // zset random const key24 = "{key}" + uuidv4(); // list value + const key25 = "{key}" + uuidv4(); // sorted set + const key26 = "{key}" + uuidv4(); // sorted set const field = uuidv4(); const value = uuidv4(); // array of tuples - first element is test name/description, second - expected return value @@ -772,6 +774,22 @@ export async function transactionTest( responseData.push(['zmscore(key12, ["two", "one"]', [2.0, 1.0]]); baseTransaction.zinterstore(key12, [key12, key13]); responseData.push(["zinterstore(key12, [key12, key13])", 0]); + baseTransaction.zadd(key25, { one: 1, two: 2 }); + responseData.push(["zadd(key25, { one: 1, two: 2 })", 2]); + baseTransaction.zadd(key26, { one: 1, two: 2, three: 3.5 }); + responseData.push(["zadd(key26, { one: 1, two: 2, three: 3.5 })", 3]); + baseTransaction.zinter([key26, key25]); + responseData.push(["zinter([key26, key25])", ["one", "two"]]); + baseTransaction.zinterWithScores([key26, key25]); + responseData.push([ + "zinterWithScores([key26, key25])", + { one: 2, two: 4 }, + ]); + baseTransaction.zunionWithScores([key26, key25]); + responseData.push([ + "zunionWithScores([key26, key25])", + { one: 2, two: 4, three: 3.5 }, + ]); } else { baseTransaction.zinterstore(key12, [key12, key13]); responseData.push(["zinterstore(key12, [key12, key13])", 2]);