diff --git a/pom.xml b/pom.xml index 2bfef0f8f3..cc064abce2 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.springframework.data spring-data-redis - 4.1.0-SNAPSHOT + 4.1.0-3250-SNAPSHOT Spring Data Redis Spring Data module for Redis diff --git a/src/main/java/org/springframework/data/redis/connection/ReactiveStringCommands.java b/src/main/java/org/springframework/data/redis/connection/ReactiveStringCommands.java index ae941374f0..af8f785490 100644 --- a/src/main/java/org/springframework/data/redis/connection/ReactiveStringCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/ReactiveStringCommands.java @@ -1289,7 +1289,7 @@ default Mono bitPos(ByteBuffer key, boolean bit, Range range) { } /** - * Emmit the the position of the first bit set to given {@code bit} in a string. Get the length of the value stored at + * Emit the position of the first bit set to given {@code bit} in a string. Get the length of the value stored at * {@literal key}. * * @param commands must not be {@literal null}. diff --git a/src/main/java/org/springframework/data/redis/connection/RedisStringCommands.java b/src/main/java/org/springframework/data/redis/connection/RedisStringCommands.java index 9f3c7c15db..a8b55174a7 100644 --- a/src/main/java/org/springframework/data/redis/connection/RedisStringCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/RedisStringCommands.java @@ -32,13 +32,35 @@ * @author Christoph Strobl * @author Mark Paluch * @author Marcin Grzejszczak + * @author Viktoriya Kutsarova * @see RedisCommands */ @NullUnmarked public interface RedisStringCommands { enum BitOperation { - AND, OR, XOR, NOT; + + AND, OR, XOR, NOT, + + /** + * @since 4.1 + */ + DIFF, + + /** + * @since 4.1 + */ + DIFF1, + + /** + * @since 4.1 + */ + ANDOR, + + /** + * @since 4.1 + */ + ONE; } /** diff --git a/src/main/java/org/springframework/data/redis/connection/jedis/JedisClusterSetCommands.java b/src/main/java/org/springframework/data/redis/connection/jedis/JedisClusterSetCommands.java index de3c66e270..57c105fce9 100644 --- a/src/main/java/org/springframework/data/redis/connection/jedis/JedisClusterSetCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/jedis/JedisClusterSetCommands.java @@ -35,6 +35,7 @@ import org.springframework.data.redis.core.ScanIteration; import org.springframework.data.redis.core.ScanOptions; import org.springframework.data.redis.util.ByteUtils; +import org.springframework.data.redis.util.KeyUtils; import org.springframework.util.Assert; /** @@ -320,25 +321,25 @@ public Set sDiff(byte[]... keys) { } } - byte[] source = keys[0]; - byte[][] others = Arrays.copyOfRange(keys, 1, keys.length); + return KeyUtils.splitKeys(keys, (source, others) -> { - ByteArraySet values = new ByteArraySet(sMembers(source)); - Collection> resultList = connection.getClusterCommandExecutor() - .executeMultiKeyCommand( - (JedisMultiKeyClusterCommandCallback>) (client, key) -> client.smembers(key), - Arrays.asList(others)) - .resultsAsList(); + ByteArraySet values = new ByteArraySet(sMembers(source)); + Collection> resultList = connection.getClusterCommandExecutor() + .executeMultiKeyCommand( + (JedisMultiKeyClusterCommandCallback>) (client, key) -> client.smembers(key), + Arrays.asList(others)) + .resultsAsList(); - if (values.isEmpty()) { - return Collections.emptySet(); - } + if (values.isEmpty()) { + return Collections.emptySet(); + } - for (Set singleNodeValue : resultList) { - values.removeAll(singleNodeValue); - } + for (Set singleNodeValue : resultList) { + values.removeAll(singleNodeValue); + } - return values.asRawSet(); + return values.asRawSet(); + }); } @Override diff --git a/src/main/java/org/springframework/data/redis/connection/jedis/JedisConverters.java b/src/main/java/org/springframework/data/redis/connection/jedis/JedisConverters.java index 98ba6a5525..ed87de789f 100644 --- a/src/main/java/org/springframework/data/redis/connection/jedis/JedisConverters.java +++ b/src/main/java/org/springframework/data/redis/connection/jedis/JedisConverters.java @@ -109,6 +109,7 @@ * @author Guy Korland * @author dengliming * @author John Blum + * @author Viktoriya Kutsarova */ @SuppressWarnings("ConstantConditions") abstract class JedisConverters extends Converters { @@ -285,6 +286,10 @@ public static BitOP toBitOp(BitOperation bitOp) { case OR -> BitOP.OR; case NOT -> BitOP.NOT; case XOR -> BitOP.XOR; + case DIFF -> BitOP.DIFF; + case DIFF1 -> BitOP.DIFF1; + case ANDOR -> BitOP.ANDOR; + case ONE -> BitOP.ONE; }; } diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceClusterSetCommands.java b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceClusterSetCommands.java index 6d67a448bd..046a42d61f 100644 --- a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceClusterSetCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceClusterSetCommands.java @@ -26,6 +26,7 @@ import org.springframework.data.redis.connection.lettuce.LettuceClusterConnection.LettuceMultiKeyClusterCommandCallback; import org.springframework.data.redis.connection.util.ByteArraySet; import org.springframework.data.redis.util.ByteUtils; +import org.springframework.data.redis.util.KeyUtils; import org.springframework.util.Assert; /** @@ -191,24 +192,24 @@ public Set sDiff(byte[]... keys) { return super.sDiff(keys); } - byte[] source = keys[0]; - byte[][] others = Arrays.copyOfRange(keys, 1, keys.length); + return KeyUtils.splitKeys(keys, (source, others) -> { - ByteArraySet values = new ByteArraySet(sMembers(source)); - Collection> nodeResult = connection.getClusterCommandExecutor() - .executeMultiKeyCommand((LettuceMultiKeyClusterCommandCallback>) RedisSetCommands::smembers, - Arrays.asList(others)) - .resultsAsList(); + ByteArraySet values = new ByteArraySet(sMembers(source)); + Collection> nodeResult = connection.getClusterCommandExecutor() + .executeMultiKeyCommand((LettuceMultiKeyClusterCommandCallback>) RedisSetCommands::smembers, + Arrays.asList(others)) + .resultsAsList(); - if (values.isEmpty()) { - return Collections.emptySet(); - } + if (values.isEmpty()) { + return Collections.emptySet(); + } - for (Set toSubstract : nodeResult) { - values.removeAll(toSubstract); - } + for (Set toSubstract : nodeResult) { + values.removeAll(toSubstract); + } - return values.asRawSet(); + return values.asRawSet(); + }); } @Override diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveStringCommands.java b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveStringCommands.java index 20cee30e23..d6c618db16 100644 --- a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveStringCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveStringCommands.java @@ -38,6 +38,7 @@ import org.springframework.data.redis.connection.ReactiveStringCommands; import org.springframework.data.redis.connection.RedisStringCommands; import org.springframework.data.redis.core.types.Expiration; +import org.springframework.data.redis.util.KeyUtils; import org.springframework.util.Assert; /** @@ -49,6 +50,7 @@ * @author Michele Mancioppi * @author John Blum * @author Marcin Grzejszczak + * @author Viktoriya Kutsarova * @since 2.0 */ class LettuceReactiveStringCommands implements ReactiveStringCommands { @@ -368,7 +370,13 @@ public Flux> bitOp(Publisher c Assert.isTrue(sourceKeys.length == 1, "BITOP NOT does not allow more than 1 source key."); yield reactiveCommands.bitopNot(destinationKey, sourceKeys[0]); } - default -> throw new IllegalArgumentException("Unknown BITOP '%s'".formatted(command.getBitOp())); + case DIFF -> KeyUtils.splitKeys(sourceKeys, + (first, remaining) -> reactiveCommands.bitopDiff(destinationKey, first, remaining)); + case DIFF1 -> KeyUtils.splitKeys(sourceKeys, + (first, remaining) -> reactiveCommands.bitopDiff1(destinationKey, first, remaining)); + case ANDOR -> KeyUtils.splitKeys(sourceKeys, + (first, remaining) -> reactiveCommands.bitopAndor(destinationKey, first, remaining)); + case ONE -> reactiveCommands.bitopOne(destinationKey, sourceKeys); }; return result.map(value -> new NumericResponse<>(command, value)); diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceStringCommands.java b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceStringCommands.java index 87ab4b3e83..1c6b21c69b 100644 --- a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceStringCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceStringCommands.java @@ -24,11 +24,13 @@ import org.jspecify.annotations.NonNull; import org.jspecify.annotations.NullUnmarked; import org.jspecify.annotations.Nullable; + import org.springframework.data.domain.Range; import org.springframework.data.redis.connection.BitFieldSubCommands; import org.springframework.data.redis.connection.RedisStringCommands; import org.springframework.data.redis.connection.convert.Converters; import org.springframework.data.redis.core.types.Expiration; +import org.springframework.data.redis.util.KeyUtils; import org.springframework.util.Assert; /** @@ -39,6 +41,7 @@ * @author dengliming * @author John Blum * @author Marcin Grzejszczak + * @author Viktoriya Kutsarova * @since 2.0 */ @NullUnmarked @@ -307,6 +310,10 @@ public Long bitOp(@NonNull BitOperation op, byte @NonNull [] destination, byte @ } yield it.bitopNot(destination, keys[0]); } + case DIFF -> KeyUtils.splitKeys(keys, (first, remaining) -> it.bitopDiff(destination, first, remaining)); + case DIFF1 -> KeyUtils.splitKeys(keys, (first, remaining) -> it.bitopDiff1(destination, first, remaining)); + case ANDOR -> KeyUtils.splitKeys(keys, (first, remaining) -> it.bitopAndor(destination, first, remaining)); + case ONE -> it.bitopOne(destination, keys); }); } diff --git a/src/main/java/org/springframework/data/redis/util/KeyUtils.java b/src/main/java/org/springframework/data/redis/util/KeyUtils.java new file mode 100644 index 0000000000..4794c70b91 --- /dev/null +++ b/src/main/java/org/springframework/data/redis/util/KeyUtils.java @@ -0,0 +1,81 @@ +/* + * Copyright 2025 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.redis.util; + +import java.util.Arrays; + +/** + * Utility class for Redis keys. + * + * @author Mark Paluch + * @since 4.1 + */ +public abstract class KeyUtils { + + // --------------------------------------------------------------------- + // General convenience methods for working with keys + // --------------------------------------------------------------------- + + /** + * Utility method to split an array concatenated of keys into the first key and the remaining keys and invoke the + * given function. + * + * @param keys array of keys to be separated into the first one and the remaining ones. + * @param function function to be invoked with the first and remaining keys as input arguments. + * @return result of the {@link SourceKeysFunction}. + */ + public static R splitKeys(T[] keys, SourceKeysFunction function) { + + if (keys.length == 0) { + throw new IllegalArgumentException("Keys array must contain at least one element"); + } + + T firstKey = keys[0]; + T[] otherKeys = Arrays.copyOfRange(keys, 1, keys.length); + return function.apply(firstKey, otherKeys); + } + + /** + * Represents a function that accepts two arguments of the same base type and produces a result while the second + * argument is an array of {@code T}. This is similar to a {@link java.util.function.BiFunction} but restricts both + * arguments to be of the same type. Typically used in arrangements where a composite collection of keys is split into + * the first key and the remaining keys. + *

+ * This is a functional interface whose functional method is + * {@link #apply(Object, Object)}. + * + * @param the type of the first argument to the function. + * @param the type of the result of the function. + */ + public interface SourceKeysFunction { + + /** + * Applies this function to the given arguments. + * + * @param firstKey the first key argument. + * @param otherKeys the other keys function argument. + * @return the function result. + */ + R apply(T firstKey, T[] otherKeys); + + } + + // utility constructor + private KeyUtils() { + + } + +} diff --git a/src/test/java/org/springframework/data/redis/connection/AbstractConnectionIntegrationTests.java b/src/test/java/org/springframework/data/redis/connection/AbstractConnectionIntegrationTests.java index d09bb78ddf..5493467b40 100644 --- a/src/test/java/org/springframework/data/redis/connection/AbstractConnectionIntegrationTests.java +++ b/src/test/java/org/springframework/data/redis/connection/AbstractConnectionIntegrationTests.java @@ -117,6 +117,7 @@ * @author Roman Osadchuk * @author Tihomir Mateev * @author Jeonggyu Choi + * @author Viktoriya Kutsarova */ public abstract class AbstractConnectionIntegrationTests { @@ -571,6 +572,42 @@ void testBitOpNotMultipleSources() { .isThrownBy(() -> connection.bitOp(BitOperation.NOT, "key3", "key1", "key2")); } + @Test // GH-3250 + void testBitOpDiff() { + + actual.add(connection.set("key1", "foobar")); + actual.add(connection.set("key2", "abcdef")); + actual.add(connection.bitOp(BitOperation.DIFF, "key3", "key1", "key2")); + verifyResults(Arrays.asList(Boolean.TRUE, Boolean.TRUE, 6L)); + } + + @Test // GH-3250 + void testBitOpDiff1() { + + actual.add(connection.set("key1", "foobar")); + actual.add(connection.set("key2", "abcdef")); + actual.add(connection.bitOp(BitOperation.DIFF1, "key3", "key1", "key2")); + verifyResults(Arrays.asList(Boolean.TRUE, Boolean.TRUE, 6L)); + } + + @Test // GH-3250 + void testBitOpAndor() { + + actual.add(connection.set("key1", "foo")); + actual.add(connection.set("key2", "bar")); + actual.add(connection.bitOp(BitOperation.ANDOR, "key3", "key1", "key2")); + verifyResults(Arrays.asList(Boolean.TRUE, Boolean.TRUE, 3L)); + } + + @Test // GH-3250 + void testBitOpOne() { + + actual.add(connection.set("key1", "foo")); + actual.add(connection.set("key2", "bar")); + actual.add(connection.bitOp(BitOperation.ONE, "key3", "key1", "key2")); + verifyResults(Arrays.asList(Boolean.TRUE, Boolean.TRUE, 3L)); + } + @Test @EnabledOnCommand("COPY") void testCopy() { diff --git a/src/test/java/org/springframework/data/redis/connection/DefaultStringRedisConnectionPipelineTests.java b/src/test/java/org/springframework/data/redis/connection/DefaultStringRedisConnectionPipelineTests.java index 0d977e00fa..bf3e962eb7 100644 --- a/src/test/java/org/springframework/data/redis/connection/DefaultStringRedisConnectionPipelineTests.java +++ b/src/test/java/org/springframework/data/redis/connection/DefaultStringRedisConnectionPipelineTests.java @@ -38,6 +38,7 @@ * @author Ninad Divadkar * @author Mark Paluch * @author dengliming + * @author Viktoriya Kutsarova */ public class DefaultStringRedisConnectionPipelineTests extends DefaultStringRedisConnectionTests { @@ -1010,6 +1011,90 @@ public void testBitOp() { super.testBitOp(); } + @Test // GH-3250 + public void testBitOpOrBytes() { + doReturn(Collections.singletonList(5L)).when(nativeConnection).closePipeline(); + super.testBitOpOrBytes(); + } + + @Test // GH-3250 + public void testBitOpOr() { + doReturn(Collections.singletonList(5L)).when(nativeConnection).closePipeline(); + super.testBitOpOr(); + } + + @Test // GH-3250 + public void testBitOpXorBytes() { + doReturn(Collections.singletonList(5L)).when(nativeConnection).closePipeline(); + super.testBitOpXorBytes(); + } + + @Test // GH-3250 + public void testBitOpXor() { + doReturn(Collections.singletonList(5L)).when(nativeConnection).closePipeline(); + super.testBitOpXor(); + } + + @Test // GH-3250 + public void testBitOpNotBytes() { + doReturn(Collections.singletonList(5L)).when(nativeConnection).closePipeline(); + super.testBitOpNotBytes(); + } + + @Test // GH-3250 + public void testBitOpNot() { + doReturn(Collections.singletonList(5L)).when(nativeConnection).closePipeline(); + super.testBitOpNot(); + } + + @Test // GH-3250 + public void testBitOpDiffBytes() { + doReturn(Collections.singletonList(5L)).when(nativeConnection).closePipeline(); + super.testBitOpDiffBytes(); + } + + @Test // GH-3250 + public void testBitOpDiff() { + doReturn(Collections.singletonList(5L)).when(nativeConnection).closePipeline(); + super.testBitOpDiff(); + } + + @Test // GH-3250 + public void testBitOpDiff1Bytes() { + doReturn(Collections.singletonList(5L)).when(nativeConnection).closePipeline(); + super.testBitOpDiff1Bytes(); + } + + @Test // GH-3250 + public void testBitOpDiff1() { + doReturn(Collections.singletonList(5L)).when(nativeConnection).closePipeline(); + super.testBitOpDiff1(); + } + + @Test // GH-3250 + public void testBitOpAndorBytes() { + doReturn(Collections.singletonList(5L)).when(nativeConnection).closePipeline(); + super.testBitOpAndorBytes(); + } + + @Test // GH-3250 + public void testBitOpAndor() { + doReturn(Collections.singletonList(5L)).when(nativeConnection).closePipeline(); + super.testBitOpAndor(); + } + + @Test // GH-3250 + public void testBitOpOneBytes() { + doReturn(Collections.singletonList(5L)).when(nativeConnection).closePipeline(); + super.testBitOpOneBytes(); + } + + @Test // GH-3250 + public void testBitOpOne() { + doReturn(Collections.singletonList(5L)).when(nativeConnection).closePipeline(); + super.testBitOpOne(); + } + @Test public void testSUnionBytes() { doReturn(Collections.singletonList(bytesSet)).when(nativeConnection).closePipeline(); diff --git a/src/test/java/org/springframework/data/redis/connection/DefaultStringRedisConnectionPipelineTxTests.java b/src/test/java/org/springframework/data/redis/connection/DefaultStringRedisConnectionPipelineTxTests.java index 6ad92f531b..a0fb3f2faf 100644 --- a/src/test/java/org/springframework/data/redis/connection/DefaultStringRedisConnectionPipelineTxTests.java +++ b/src/test/java/org/springframework/data/redis/connection/DefaultStringRedisConnectionPipelineTxTests.java @@ -37,6 +37,7 @@ * @author Ninad Divadkar * @author Mark Paluch * @author dengliming + * @author Viktoriya Kutsarova */ public class DefaultStringRedisConnectionPipelineTxTests extends DefaultStringRedisConnectionTxTests { @@ -1025,6 +1026,90 @@ public void testBitOp() { super.testBitOp(); } + @Test // GH-3250 + public void testBitOpOrBytes() { + doReturn(Collections.singletonList(Collections.singletonList(5L))).when(nativeConnection).closePipeline(); + super.testBitOpOrBytes(); + } + + @Test // GH-3250 + public void testBitOpOr() { + doReturn(Collections.singletonList(Collections.singletonList(5L))).when(nativeConnection).closePipeline(); + super.testBitOpOr(); + } + + @Test // GH-3250 + public void testBitOpXorBytes() { + doReturn(Collections.singletonList(Collections.singletonList(5L))).when(nativeConnection).closePipeline(); + super.testBitOpXorBytes(); + } + + @Test // GH-3250 + public void testBitOpXor() { + doReturn(Collections.singletonList(Collections.singletonList(5L))).when(nativeConnection).closePipeline(); + super.testBitOpXor(); + } + + @Test // GH-3250 + public void testBitOpNotBytes() { + doReturn(Collections.singletonList(Collections.singletonList(5L))).when(nativeConnection).closePipeline(); + super.testBitOpNotBytes(); + } + + @Test // GH-3250 + public void testBitOpNot() { + doReturn(Collections.singletonList(Collections.singletonList(5L))).when(nativeConnection).closePipeline(); + super.testBitOpNot(); + } + + @Test // GH-3250 + public void testBitOpDiffBytes() { + doReturn(Collections.singletonList(Collections.singletonList(5L))).when(nativeConnection).closePipeline(); + super.testBitOpDiffBytes(); + } + + @Test // GH-3250 + public void testBitOpDiff() { + doReturn(Collections.singletonList(Collections.singletonList(5L))).when(nativeConnection).closePipeline(); + super.testBitOpDiff(); + } + + @Test // GH-3250 + public void testBitOpDiff1Bytes() { + doReturn(Collections.singletonList(Collections.singletonList(5L))).when(nativeConnection).closePipeline(); + super.testBitOpDiff1Bytes(); + } + + @Test // GH-3250 + public void testBitOpDiff1() { + doReturn(Collections.singletonList(Collections.singletonList(5L))).when(nativeConnection).closePipeline(); + super.testBitOpDiff1(); + } + + @Test // GH-3250 + public void testBitOpAndorBytes() { + doReturn(Collections.singletonList(Collections.singletonList(5L))).when(nativeConnection).closePipeline(); + super.testBitOpAndorBytes(); + } + + @Test // GH-3250 + public void testBitOpAndor() { + doReturn(Collections.singletonList(Collections.singletonList(5L))).when(nativeConnection).closePipeline(); + super.testBitOpAndor(); + } + + @Test // GH-3250 + public void testBitOpOneBytes() { + doReturn(Collections.singletonList(Collections.singletonList(5L))).when(nativeConnection).closePipeline(); + super.testBitOpOneBytes(); + } + + @Test // GH-3250 + public void testBitOpOne() { + doReturn(Collections.singletonList(Collections.singletonList(5L))).when(nativeConnection).closePipeline(); + super.testBitOpOne(); + } + @Test public void testSUnionBytes() { doReturn(Collections.singletonList(Collections.singletonList(bytesSet))).when(nativeConnection).closePipeline(); diff --git a/src/test/java/org/springframework/data/redis/connection/DefaultStringRedisConnectionTests.java b/src/test/java/org/springframework/data/redis/connection/DefaultStringRedisConnectionTests.java index e57589012a..20947d6c63 100644 --- a/src/test/java/org/springframework/data/redis/connection/DefaultStringRedisConnectionTests.java +++ b/src/test/java/org/springframework/data/redis/connection/DefaultStringRedisConnectionTests.java @@ -78,6 +78,7 @@ * @author Mark Paluch * @author dengliming * @author ihaohong + * @author Viktoriya Kutsarova */ @ExtendWith(MockitoExtension.class) @MockitoSettings(strictness = Strictness.LENIENT) @@ -1266,6 +1267,104 @@ public void testBitOp() { verifyResults(Collections.singletonList(5L)); } + @Test + public void testBitOpOrBytes() { + doReturn(5L).when(nativeConnection).bitOp(BitOperation.OR, fooBytes, barBytes); + actual.add(connection.bitOp(BitOperation.OR, fooBytes, barBytes)); + verifyResults(Collections.singletonList(5L)); + } + + @Test + public void testBitOpOr() { + doReturn(5L).when(nativeConnection).bitOp(BitOperation.OR, fooBytes, barBytes); + actual.add(connection.bitOp(BitOperation.OR, foo, bar)); + verifyResults(Collections.singletonList(5L)); + } + + @Test + public void testBitOpXorBytes() { + doReturn(5L).when(nativeConnection).bitOp(BitOperation.XOR, fooBytes, barBytes); + actual.add(connection.bitOp(BitOperation.XOR, fooBytes, barBytes)); + verifyResults(Collections.singletonList(5L)); + } + + @Test + public void testBitOpXor() { + doReturn(5L).when(nativeConnection).bitOp(BitOperation.XOR, fooBytes, barBytes); + actual.add(connection.bitOp(BitOperation.XOR, foo, bar)); + verifyResults(Collections.singletonList(5L)); + } + + @Test + public void testBitOpNotBytes() { + doReturn(5L).when(nativeConnection).bitOp(BitOperation.NOT, fooBytes, barBytes); + actual.add(connection.bitOp(BitOperation.NOT, fooBytes, barBytes)); + verifyResults(Collections.singletonList(5L)); + } + + @Test + public void testBitOpNot() { + doReturn(5L).when(nativeConnection).bitOp(BitOperation.NOT, fooBytes, barBytes); + actual.add(connection.bitOp(BitOperation.NOT, foo, bar)); + verifyResults(Collections.singletonList(5L)); + } + + @Test // GH-3250 + public void testBitOpDiffBytes() { + doReturn(5L).when(nativeConnection).bitOp(BitOperation.DIFF, fooBytes, barBytes); + actual.add(connection.bitOp(BitOperation.DIFF, fooBytes, barBytes)); + verifyResults(Collections.singletonList(5L)); + } + + @Test // GH-3250 + public void testBitOpDiff() { + doReturn(5L).when(nativeConnection).bitOp(BitOperation.DIFF, fooBytes, barBytes); + actual.add(connection.bitOp(BitOperation.DIFF, foo, bar)); + verifyResults(Collections.singletonList(5L)); + } + + @Test // GH-3250 + public void testBitOpDiff1Bytes() { + doReturn(5L).when(nativeConnection).bitOp(BitOperation.DIFF1, fooBytes, barBytes); + actual.add(connection.bitOp(BitOperation.DIFF1, fooBytes, barBytes)); + verifyResults(Collections.singletonList(5L)); + } + + @Test // GH-3250 + public void testBitOpDiff1() { + doReturn(5L).when(nativeConnection).bitOp(BitOperation.DIFF1, fooBytes, barBytes); + actual.add(connection.bitOp(BitOperation.DIFF1, foo, bar)); + verifyResults(Collections.singletonList(5L)); + } + + @Test // GH-3250 + public void testBitOpAndorBytes() { + doReturn(5L).when(nativeConnection).bitOp(BitOperation.ANDOR, fooBytes, barBytes); + actual.add(connection.bitOp(BitOperation.ANDOR, fooBytes, barBytes)); + verifyResults(Collections.singletonList(5L)); + } + + @Test // GH-3250 + public void testBitOpAndor() { + doReturn(5L).when(nativeConnection).bitOp(BitOperation.ANDOR, fooBytes, barBytes); + actual.add(connection.bitOp(BitOperation.ANDOR, foo, bar)); + verifyResults(Collections.singletonList(5L)); + } + + @Test // GH-3250 + public void testBitOpOneBytes() { + doReturn(5L).when(nativeConnection).bitOp(BitOperation.ONE, fooBytes, barBytes); + actual.add(connection.bitOp(BitOperation.ONE, fooBytes, barBytes)); + verifyResults(Collections.singletonList(5L)); + } + + @Test // GH-3250 + public void testBitOpOne() { + doReturn(5L).when(nativeConnection).bitOp(BitOperation.ONE, fooBytes, barBytes); + actual.add(connection.bitOp(BitOperation.ONE, foo, bar)); + verifyResults(Collections.singletonList(5L)); + } + @Test public void testSUnionBytes() { doReturn(bytesSet).when(nativeConnection).sUnion(fooBytes, barBytes); diff --git a/src/test/java/org/springframework/data/redis/connection/DefaultStringRedisConnectionTxTests.java b/src/test/java/org/springframework/data/redis/connection/DefaultStringRedisConnectionTxTests.java index 0e8fa6c1fb..051d391478 100644 --- a/src/test/java/org/springframework/data/redis/connection/DefaultStringRedisConnectionTxTests.java +++ b/src/test/java/org/springframework/data/redis/connection/DefaultStringRedisConnectionTxTests.java @@ -34,6 +34,7 @@ * @author Jennifer Hickey * @author Christoph Strobl * @author Ninad Divadkar + * @author Viktoriya Kutsarova */ public class DefaultStringRedisConnectionTxTests extends DefaultStringRedisConnectionTests { @@ -995,6 +996,90 @@ public void testBitOp() { super.testBitOp(); } + @Test // GH-3250 + public void testBitOpOrBytes() { + doReturn(Collections.singletonList(5L)).when(nativeConnection).exec(); + super.testBitOpOrBytes(); + } + + @Test // GH-3250 + public void testBitOpOr() { + doReturn(Collections.singletonList(5L)).when(nativeConnection).exec(); + super.testBitOpOr(); + } + + @Test // GH-3250 + public void testBitOpXorBytes() { + doReturn(Collections.singletonList(5L)).when(nativeConnection).exec(); + super.testBitOpXorBytes(); + } + + @Test // GH-3250 + public void testBitOpXor() { + doReturn(Collections.singletonList(5L)).when(nativeConnection).exec(); + super.testBitOpXor(); + } + + @Test // GH-3250 + public void testBitOpNotBytes() { + doReturn(Collections.singletonList(5L)).when(nativeConnection).exec(); + super.testBitOpNotBytes(); + } + + @Test // GH-3250 + public void testBitOpNot() { + doReturn(Collections.singletonList(5L)).when(nativeConnection).exec(); + super.testBitOpNot(); + } + + @Test // GH-3250 + public void testBitOpDiffBytes() { + doReturn(Collections.singletonList(5L)).when(nativeConnection).exec(); + super.testBitOpDiffBytes(); + } + + @Test // GH-3250 + public void testBitOpDiff() { + doReturn(Collections.singletonList(5L)).when(nativeConnection).exec(); + super.testBitOpDiff(); + } + + @Test // GH-3250 + public void testBitOpDiff1Bytes() { + doReturn(Collections.singletonList(5L)).when(nativeConnection).exec(); + super.testBitOpDiff1Bytes(); + } + + @Test // GH-3250 + public void testBitOpDiff1() { + doReturn(Collections.singletonList(5L)).when(nativeConnection).exec(); + super.testBitOpDiff1(); + } + + @Test // GH-3250 + public void testBitOpAndorBytes() { + doReturn(Collections.singletonList(5L)).when(nativeConnection).exec(); + super.testBitOpAndorBytes(); + } + + @Test // GH-3250 + public void testBitOpAndor() { + doReturn(Collections.singletonList(5L)).when(nativeConnection).exec(); + super.testBitOpAndor(); + } + + @Test // GH-3250 + public void testBitOpOneBytes() { + doReturn(Collections.singletonList(5L)).when(nativeConnection).exec(); + super.testBitOpOneBytes(); + } + + @Test // GH-3250 + public void testBitOpOne() { + doReturn(Collections.singletonList(5L)).when(nativeConnection).exec(); + super.testBitOpOne(); + } + @Test public void testSUnionBytes() { doReturn(Collections.singletonList(bytesSet)).when(nativeConnection).exec(); diff --git a/src/test/java/org/springframework/data/redis/connection/jedis/JedisClusterConnectionTests.java b/src/test/java/org/springframework/data/redis/connection/jedis/JedisClusterConnectionTests.java index 08ee13c910..72beaa1321 100644 --- a/src/test/java/org/springframework/data/redis/connection/jedis/JedisClusterConnectionTests.java +++ b/src/test/java/org/springframework/data/redis/connection/jedis/JedisClusterConnectionTests.java @@ -78,6 +78,7 @@ * @author Pavel Khokhlov * @author Dennis Neufeld * @author Tihomir Mateev + * @author Viktoriya Kutsarova */ @EnabledOnRedisClusterAvailable @ExtendWith(JedisExtension.class) @@ -191,6 +192,82 @@ void bitOpShouldWorkCorrectly() { assertThat(nativeConnection.get(SAME_SLOT_KEY_3)).isEqualTo("bab"); } + @Test // GH-3250 + void bitOpOrShouldWorkCorrectly() { + + nativeConnection.set(SAME_SLOT_KEY_1, "foo"); + nativeConnection.set(SAME_SLOT_KEY_2, "ugh"); + + clusterConnection.bitOp(BitOperation.OR, SAME_SLOT_KEY_3_BYTES, SAME_SLOT_KEY_1_BYTES, SAME_SLOT_KEY_2_BYTES); + + assertThat(nativeConnection.get(SAME_SLOT_KEY_3)).isEqualTo("woo"); + } + + @Test // GH-3250 + void bitOpXorShouldWorkCorrectly() { + + nativeConnection.set(SAME_SLOT_KEY_1, "aaa"); + nativeConnection.set(SAME_SLOT_KEY_2, "___"); + + clusterConnection.bitOp(BitOperation.XOR, SAME_SLOT_KEY_3_BYTES, SAME_SLOT_KEY_1_BYTES, SAME_SLOT_KEY_2_BYTES); + + assertThat(nativeConnection.get(SAME_SLOT_KEY_3)).isEqualTo(">>>"); + } + + @Test // GH-3250 + void bitOpNotShouldWorkCorrectly() { + + nativeConnection.set(SAME_SLOT_KEY_1, "foo"); + + clusterConnection.bitOp(BitOperation.NOT, SAME_SLOT_KEY_3_BYTES, SAME_SLOT_KEY_1_BYTES); + + assertThat(nativeConnection.get(SAME_SLOT_KEY_3)).isNotNull(); + } + + @Test // GH-3250 + void bitOpDiffShouldWorkCorrectly() { + + nativeConnection.set(SAME_SLOT_KEY_1, "foobar"); + nativeConnection.set(SAME_SLOT_KEY_2, "abcdef"); + + clusterConnection.bitOp(BitOperation.DIFF, SAME_SLOT_KEY_3_BYTES, SAME_SLOT_KEY_1_BYTES, SAME_SLOT_KEY_2_BYTES); + + assertThat(nativeConnection.get(SAME_SLOT_KEY_3)).isNotNull(); + } + + @Test // GH-3250 + void bitOpDiff1ShouldWorkCorrectly() { + + nativeConnection.set(SAME_SLOT_KEY_1, "foobar"); + nativeConnection.set(SAME_SLOT_KEY_2, "abcdef"); + + clusterConnection.bitOp(BitOperation.DIFF1, SAME_SLOT_KEY_3_BYTES, SAME_SLOT_KEY_1_BYTES, SAME_SLOT_KEY_2_BYTES); + + assertThat(nativeConnection.get(SAME_SLOT_KEY_3)).isNotNull(); + } + + @Test // GH-3250 + void bitOpAndorShouldWorkCorrectly() { + + nativeConnection.set(SAME_SLOT_KEY_1, "foo"); + nativeConnection.set(SAME_SLOT_KEY_2, "bar"); + + clusterConnection.bitOp(BitOperation.ANDOR, SAME_SLOT_KEY_3_BYTES, SAME_SLOT_KEY_1_BYTES, SAME_SLOT_KEY_2_BYTES); + + assertThat(nativeConnection.get(SAME_SLOT_KEY_3)).isNotNull(); + } + + @Test // GH-3250 + void bitOpOneShouldWorkCorrectly() { + + nativeConnection.set(SAME_SLOT_KEY_1, "foo"); + nativeConnection.set(SAME_SLOT_KEY_2, "bar"); + + clusterConnection.bitOp(BitOperation.ONE, SAME_SLOT_KEY_3_BYTES, SAME_SLOT_KEY_1_BYTES, SAME_SLOT_KEY_2_BYTES); + + assertThat(nativeConnection.get(SAME_SLOT_KEY_3)).isNotNull(); + } + @Test // DATAREDIS-315 public void blPopShouldPopElementCorrectly() { diff --git a/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceClusterConnectionTests.java b/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceClusterConnectionTests.java index af6285dad9..80d7e4db23 100644 --- a/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceClusterConnectionTests.java +++ b/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceClusterConnectionTests.java @@ -76,6 +76,7 @@ * @author Mark Paluch * @author Dennis Neufeld * @author Tihomir Mateev + * @author Viktoriya Kutsarova */ @SuppressWarnings("deprecation") @EnabledOnRedisClusterAvailable @@ -279,6 +280,82 @@ void bitOpShouldWorkCorrectly() { assertThat(nativeConnection.get(SAME_SLOT_KEY_3)).isEqualTo("bab"); } + @Test // GH-3250 + void bitOpOrShouldWorkCorrectly() { + + nativeConnection.set(SAME_SLOT_KEY_1, "foo"); + nativeConnection.set(SAME_SLOT_KEY_2, "ugh"); + + clusterConnection.bitOp(BitOperation.OR, SAME_SLOT_KEY_3_BYTES, SAME_SLOT_KEY_1_BYTES, SAME_SLOT_KEY_2_BYTES); + + assertThat(nativeConnection.get(SAME_SLOT_KEY_3)).isEqualTo("woo"); + } + + @Test // GH-3250 + void bitOpXorShouldWorkCorrectly() { + + nativeConnection.set(SAME_SLOT_KEY_1, "aaa"); + nativeConnection.set(SAME_SLOT_KEY_2, "___"); + + clusterConnection.bitOp(BitOperation.XOR, SAME_SLOT_KEY_3_BYTES, SAME_SLOT_KEY_1_BYTES, SAME_SLOT_KEY_2_BYTES); + + assertThat(nativeConnection.get(SAME_SLOT_KEY_3)).isEqualTo(">>>"); + } + + @Test // GH-3250 + void bitOpNotShouldWorkCorrectly() { + + nativeConnection.set(SAME_SLOT_KEY_1, "foo"); + + clusterConnection.bitOp(BitOperation.NOT, SAME_SLOT_KEY_3_BYTES, SAME_SLOT_KEY_1_BYTES); + + assertThat(nativeConnection.get(SAME_SLOT_KEY_3)).isNotNull(); + } + + @Test // GH-3250 + void bitOpDiffShouldWorkCorrectly() { + + nativeConnection.set(SAME_SLOT_KEY_1, "foobar"); + nativeConnection.set(SAME_SLOT_KEY_2, "abcdef"); + + clusterConnection.bitOp(BitOperation.DIFF, SAME_SLOT_KEY_3_BYTES, SAME_SLOT_KEY_1_BYTES, SAME_SLOT_KEY_2_BYTES); + + assertThat(nativeConnection.get(SAME_SLOT_KEY_3)).isNotNull(); + } + + @Test // GH-3250 + void bitOpDiff1ShouldWorkCorrectly() { + + nativeConnection.set(SAME_SLOT_KEY_1, "foobar"); + nativeConnection.set(SAME_SLOT_KEY_2, "abcdef"); + + clusterConnection.bitOp(BitOperation.DIFF1, SAME_SLOT_KEY_3_BYTES, SAME_SLOT_KEY_1_BYTES, SAME_SLOT_KEY_2_BYTES); + + assertThat(nativeConnection.get(SAME_SLOT_KEY_3)).isNotNull(); + } + + @Test // GH-3250 + void bitOpAndorShouldWorkCorrectly() { + + nativeConnection.set(SAME_SLOT_KEY_1, "foo"); + nativeConnection.set(SAME_SLOT_KEY_2, "bar"); + + clusterConnection.bitOp(BitOperation.ANDOR, SAME_SLOT_KEY_3_BYTES, SAME_SLOT_KEY_1_BYTES, SAME_SLOT_KEY_2_BYTES); + + assertThat(nativeConnection.get(SAME_SLOT_KEY_3)).isNotNull(); + } + + @Test // GH-3250 + void bitOpOneShouldWorkCorrectly() { + + nativeConnection.set(SAME_SLOT_KEY_1, "foo"); + nativeConnection.set(SAME_SLOT_KEY_2, "bar"); + + clusterConnection.bitOp(BitOperation.ONE, SAME_SLOT_KEY_3_BYTES, SAME_SLOT_KEY_1_BYTES, SAME_SLOT_KEY_2_BYTES); + + assertThat(nativeConnection.get(SAME_SLOT_KEY_3)).isNotNull(); + } + @Test // DATAREDIS-315 public void blPopShouldPopElementCorrectly() { diff --git a/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveClusterStringCommandsIntegrationTests.java b/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveClusterStringCommandsIntegrationTests.java index 1f56137cf2..8078a58513 100644 --- a/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveClusterStringCommandsIntegrationTests.java +++ b/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveClusterStringCommandsIntegrationTests.java @@ -29,6 +29,7 @@ /** * @author Christoph Strobl + * @author Viktoriya Kutsarova * @since 2.0 */ class LettuceReactiveClusterStringCommandsIntegrationTests extends LettuceReactiveClusterTestSupport { @@ -83,6 +84,27 @@ void bitOpOrShouldWorkAsExpectedWhenKeysMapToSameSlot() { assertThat(nativeCommands.get(SAME_SLOT_KEY_3)).isEqualTo(VALUE_3); } + @Test // GH-3250 + void bitOpXorShouldWorkAsExpectedWhenKeysMapToSameSlot() { + + nativeCommands.set(SAME_SLOT_KEY_1, VALUE_1); + nativeCommands.set(SAME_SLOT_KEY_2, VALUE_2); + + assertThat(connection.stringCommands().bitOp(Arrays.asList(SAME_SLOT_KEY_1_BBUFFER, SAME_SLOT_KEY_2_BBUFFER), + RedisStringCommands.BitOperation.XOR, SAME_SLOT_KEY_3_BBUFFER).block()).isEqualTo(7L); + assertThat(nativeCommands.get(SAME_SLOT_KEY_3)).isNotNull(); + } + + @Test // GH-3250 + void bitOpNotShouldWorkAsExpectedWhenKeysMapToSameSlot() { + + nativeCommands.set(SAME_SLOT_KEY_1, VALUE_1); + + assertThat(connection.stringCommands().bitOp(Arrays.asList(SAME_SLOT_KEY_1_BBUFFER), + RedisStringCommands.BitOperation.NOT, SAME_SLOT_KEY_3_BBUFFER).block()).isEqualTo(7L); + assertThat(nativeCommands.get(SAME_SLOT_KEY_3)).isNotNull(); + } + @Test // DATAREDIS-525 void bitNotShouldThrowExceptionWhenMoreThanOnSourceKeyAndKeysMapToSameSlot() { assertThatIllegalArgumentException().isThrownBy( @@ -90,4 +112,48 @@ void bitNotShouldThrowExceptionWhenMoreThanOnSourceKeyAndKeysMapToSameSlot() { RedisStringCommands.BitOperation.NOT, SAME_SLOT_KEY_3_BBUFFER).block()); } + @Test // GH-3250 + void bitOpDiffShouldWorkAsExpectedWhenKeysMapToSameSlot() { + + nativeCommands.set(SAME_SLOT_KEY_1, "foobar"); + nativeCommands.set(SAME_SLOT_KEY_2, "abcdef"); + + assertThat(connection.stringCommands().bitOp(Arrays.asList(SAME_SLOT_KEY_1_BBUFFER, SAME_SLOT_KEY_2_BBUFFER), + RedisStringCommands.BitOperation.DIFF, SAME_SLOT_KEY_3_BBUFFER).block()).isEqualTo(6L); + assertThat(nativeCommands.get(SAME_SLOT_KEY_3)).isNotNull(); + } + + @Test // GH-3250 + void bitOpDiff1ShouldWorkAsExpectedWhenKeysMapToSameSlot() { + + nativeCommands.set(SAME_SLOT_KEY_1, "foobar"); + nativeCommands.set(SAME_SLOT_KEY_2, "abcdef"); + + assertThat(connection.stringCommands().bitOp(Arrays.asList(SAME_SLOT_KEY_1_BBUFFER, SAME_SLOT_KEY_2_BBUFFER), + RedisStringCommands.BitOperation.DIFF1, SAME_SLOT_KEY_3_BBUFFER).block()).isEqualTo(6L); + assertThat(nativeCommands.get(SAME_SLOT_KEY_3)).isNotNull(); + } + + @Test // GH-3250 + void bitOpAndorShouldWorkAsExpectedWhenKeysMapToSameSlot() { + + nativeCommands.set(SAME_SLOT_KEY_1, VALUE_1); + nativeCommands.set(SAME_SLOT_KEY_2, VALUE_2); + + assertThat(connection.stringCommands().bitOp(Arrays.asList(SAME_SLOT_KEY_1_BBUFFER, SAME_SLOT_KEY_2_BBUFFER), + RedisStringCommands.BitOperation.ANDOR, SAME_SLOT_KEY_3_BBUFFER).block()).isEqualTo(7L); + assertThat(nativeCommands.get(SAME_SLOT_KEY_3)).isNotNull(); + } + + @Test // GH-3250 + void bitOpOneShouldWorkAsExpectedWhenKeysMapToSameSlot() { + + nativeCommands.set(SAME_SLOT_KEY_1, VALUE_1); + nativeCommands.set(SAME_SLOT_KEY_2, VALUE_2); + + assertThat(connection.stringCommands().bitOp(Arrays.asList(SAME_SLOT_KEY_1_BBUFFER, SAME_SLOT_KEY_2_BBUFFER), + RedisStringCommands.BitOperation.ONE, SAME_SLOT_KEY_3_BBUFFER).block()).isEqualTo(7L); + assertThat(nativeCommands.get(SAME_SLOT_KEY_3)).isNotNull(); + } + } diff --git a/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveStringCommandsIntegrationTests.java b/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveStringCommandsIntegrationTests.java index 5f233cd40c..b0ab1bb84f 100644 --- a/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveStringCommandsIntegrationTests.java +++ b/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveStringCommandsIntegrationTests.java @@ -61,6 +61,7 @@ * @author Christoph Strobl * @author Mark Paluch * @author Michele Mancioppi + * @author Viktoriya Kutsarova */ @ParameterizedClass public class LettuceReactiveStringCommandsIntegrationTests extends LettuceReactiveCommandsTestSupport { @@ -508,6 +509,70 @@ void bitNotShouldThrowExceptionWhenMoreThanOnSourceKey() { .verify(); } + @Test // GH-3250 + void bitOpDiffShouldWorkAsExpected() { + + assumeTrue(connectionProvider instanceof StandaloneConnectionProvider); + + nativeCommands.set(KEY_1, "foobar"); + nativeCommands.set(KEY_2, "abcdef"); + + connection.stringCommands().bitOp(Arrays.asList(KEY_1_BBUFFER, KEY_2_BBUFFER), BitOperation.DIFF, KEY_3_BBUFFER) + .as(StepVerifier::create) // + .expectNext(6L) // + .verifyComplete(); + + assertThat(nativeCommands.get(KEY_3)).isNotNull(); + } + + @Test // GH-3250 + void bitOpDiff1ShouldWorkAsExpected() { + + assumeTrue(connectionProvider instanceof StandaloneConnectionProvider); + + nativeCommands.set(KEY_1, "foobar"); + nativeCommands.set(KEY_2, "abcdef"); + + connection.stringCommands().bitOp(Arrays.asList(KEY_1_BBUFFER, KEY_2_BBUFFER), BitOperation.DIFF1, KEY_3_BBUFFER) + .as(StepVerifier::create) // + .expectNext(6L) // + .verifyComplete(); + + assertThat(nativeCommands.get(KEY_3)).isNotNull(); + } + + @Test // GH-3250 + void bitOpAndorShouldWorkAsExpected() { + + assumeTrue(connectionProvider instanceof StandaloneConnectionProvider); + + nativeCommands.set(KEY_1, "foo"); + nativeCommands.set(KEY_2, "bar"); + + connection.stringCommands().bitOp(Arrays.asList(KEY_1_BBUFFER, KEY_2_BBUFFER), BitOperation.ANDOR, KEY_3_BBUFFER) + .as(StepVerifier::create) // + .expectNext(3L) // + .verifyComplete(); + + assertThat(nativeCommands.get(KEY_3)).isNotNull(); + } + + @Test // GH-3250 + void bitOpOneShouldWorkAsExpected() { + + assumeTrue(connectionProvider instanceof StandaloneConnectionProvider); + + nativeCommands.set(KEY_1, VALUE_1); + nativeCommands.set(KEY_2, VALUE_2); + + connection.stringCommands().bitOp(Arrays.asList(KEY_1_BBUFFER, KEY_2_BBUFFER), BitOperation.ONE, KEY_3_BBUFFER) + .as(StepVerifier::create) // + .expectNext(7L) // + .verifyComplete(); + + assertThat(nativeCommands.get(KEY_3)).isNotNull(); + } + @Test // DATAREDIS-525 void strLenShouldReturnValueCorrectly() {