Polishing.

Add support for Jedis Streams using JedisCluster. Add fromMany(…) for non-pipelined usage to JedisInvoker.

Reformat code, add author tags. Extract Jedis-specific stream type converters to StreamConverters. Properly convert StreamEntry and StreamEntryID into list/map. Update tests.

See #1711
Original pull request: #1977.
This commit is contained in:
Mark Paluch
2021-03-15 15:47:28 +01:00
parent a31c739ab6
commit 9b39c09389
18 changed files with 938 additions and 402 deletions

View File

@@ -3073,7 +3073,6 @@ public abstract class AbstractConnectionIntegrationTests {
@Test // DATAREDIS-864
@EnabledOnCommand("XADD")
@EnabledOnRedisDriver({ RedisDriver.LETTUCE })
void xAddShouldCreateStream() {
actual.add(connection.xAdd(KEY_1, Collections.singletonMap(KEY_2, VALUE_2)));
@@ -3087,8 +3086,7 @@ public abstract class AbstractConnectionIntegrationTests {
@Test // DATAREDIS-864
@EnabledOnCommand("XADD")
@EnabledOnRedisDriver({ RedisDriver.LETTUCE })
void xReadShouldReadMessage() {
public void xReadShouldReadMessage() {
actual.add(connection.xAdd(KEY_1, Collections.singletonMap(KEY_2, VALUE_2)));
actual.add(connection.xReadAsString(StreamOffset.create(KEY_1, ReadOffset.from("0"))));
@@ -3103,8 +3101,7 @@ public abstract class AbstractConnectionIntegrationTests {
@Test // DATAREDIS-864
@EnabledOnCommand("XADD")
@EnabledOnRedisDriver({ RedisDriver.LETTUCE })
void xReadGroupShouldReadMessage() {
public void xReadGroupShouldReadMessage() {
actual.add(connection.xAdd(KEY_1, Collections.singletonMap(KEY_2, VALUE_2)));
actual.add(connection.xGroupCreate(KEY_1, ReadOffset.from("0"), "my-group"));
@@ -3125,8 +3122,7 @@ public abstract class AbstractConnectionIntegrationTests {
@Test // DATAREDIS-864
@EnabledOnCommand("XADD")
@EnabledOnRedisDriver({ RedisDriver.LETTUCE })
void xGroupCreateShouldWorkWithAndWithoutExistingStream() {
public void xGroupCreateShouldWorkWithAndWithoutExistingStream() {
actual.add(connection.xGroupCreate(KEY_1, ReadOffset.from("0"), "my-group", true));
actual.add(connection.xAdd(KEY_1, Collections.singletonMap(KEY_2, VALUE_2)));
@@ -3148,7 +3144,6 @@ public abstract class AbstractConnectionIntegrationTests {
@Test // DATAREDIS-864
@EnabledOnCommand("XADD")
@EnabledOnRedisDriver({ RedisDriver.LETTUCE })
void xRangeShouldReportMessages() {
actual.add(connection.xAdd(KEY_1, Collections.singletonMap(KEY_2, VALUE_2)));
@@ -3169,8 +3164,7 @@ public abstract class AbstractConnectionIntegrationTests {
@Test // DATAREDIS-864
@EnabledOnCommand("XADD")
@EnabledOnRedisDriver({ RedisDriver.LETTUCE })
void xRevRangeShouldReportMessages() {
public void xRevRangeShouldReportMessages() {
actual.add(connection.xAdd(KEY_1, Collections.singletonMap(KEY_2, VALUE_2)));
actual.add(connection.xAdd(KEY_1, Collections.singletonMap(KEY_3, VALUE_3)));
@@ -3191,8 +3185,7 @@ public abstract class AbstractConnectionIntegrationTests {
@Test // DATAREDIS-1207
@EnabledOnCommand("XADD")
@EnabledOnRedisDriver({ RedisDriver.LETTUCE })
void xRevRangeShouldWorkWithBoundedRange() {
public void xRevRangeShouldWorkWithBoundedRange() {
actual.add(connection.xAdd(KEY_1, Collections.singletonMap(KEY_2, VALUE_2)));
actual.add(connection.xAdd(KEY_1, Collections.singletonMap(KEY_3, VALUE_3)));
@@ -3213,7 +3206,7 @@ public abstract class AbstractConnectionIntegrationTests {
@Test // DATAREDIS-1084
@EnabledOnCommand("XADD")
@EnabledOnRedisDriver({ RedisDriver.LETTUCE })
@EnabledOnRedisDriver(RedisDriver.LETTUCE)
void xPendingShouldLoadOverviewCorrectly() {
actual.add(connection.xAdd(KEY_1, Collections.singletonMap(KEY_2, VALUE_2)));
@@ -3235,7 +3228,7 @@ public abstract class AbstractConnectionIntegrationTests {
@Test // DATAREDIS-1084
@EnabledOnCommand("XADD")
@EnabledOnRedisDriver({ RedisDriver.LETTUCE })
@EnabledOnRedisDriver(RedisDriver.LETTUCE)
void xPendingShouldLoadEmptyOverviewCorrectly() {
actual.add(connection.xAdd(KEY_1, Collections.singletonMap(KEY_2, VALUE_2)));
@@ -3254,8 +3247,7 @@ public abstract class AbstractConnectionIntegrationTests {
@Test // DATAREDIS-1084
@EnabledOnCommand("XADD")
@EnabledOnRedisDriver({ RedisDriver.LETTUCE })
void xPendingShouldLoadPendingMessages() {
public void xPendingShouldLoadPendingMessages() {
actual.add(connection.xAdd(KEY_1, Collections.singletonMap(KEY_2, VALUE_2)));
actual.add(connection.xGroupCreate(KEY_1, ReadOffset.from("0"), "my-group"));
@@ -3277,8 +3269,7 @@ public abstract class AbstractConnectionIntegrationTests {
@Test // DATAREDIS-1207
@EnabledOnCommand("XADD")
@EnabledOnRedisDriver({ RedisDriver.LETTUCE })
void xPendingShouldWorkWithBoundedRange() {
public void xPendingShouldWorkWithBoundedRange() {
actual.add(connection.xAdd(KEY_1, Collections.singletonMap(KEY_2, VALUE_2)));
actual.add(connection.xGroupCreate(KEY_1, ReadOffset.from("0"), "my-group"));
@@ -3300,8 +3291,7 @@ public abstract class AbstractConnectionIntegrationTests {
@Test // DATAREDIS-1084
@EnabledOnCommand("XADD")
@EnabledOnRedisDriver({ RedisDriver.LETTUCE })
void xPendingShouldLoadPendingMessagesForConsumer() {
public void xPendingShouldLoadPendingMessagesForConsumer() {
actual.add(connection.xAdd(KEY_1, Collections.singletonMap(KEY_2, VALUE_2)));
actual.add(connection.xGroupCreate(KEY_1, ReadOffset.from("0"), "my-group"));
@@ -3324,8 +3314,7 @@ public abstract class AbstractConnectionIntegrationTests {
@Test // DATAREDIS-1084
@EnabledOnCommand("XADD")
@EnabledOnRedisDriver({ RedisDriver.LETTUCE })
void xPendingShouldLoadPendingMessagesForNonExistingConsumer() {
public void xPendingShouldLoadPendingMessagesForNonExistingConsumer() {
actual.add(connection.xAdd(KEY_1, Collections.singletonMap(KEY_2, VALUE_2)));
actual.add(connection.xGroupCreate(KEY_1, ReadOffset.from("0"), "my-group"));
@@ -3344,7 +3333,6 @@ public abstract class AbstractConnectionIntegrationTests {
@Test // DATAREDIS-1084
@EnabledOnCommand("XADD")
@EnabledOnRedisDriver({ RedisDriver.LETTUCE })
void xPendingShouldLoadEmptyPendingMessages() {
actual.add(connection.xAdd(KEY_1, Collections.singletonMap(KEY_2, VALUE_2)));
@@ -3361,7 +3349,6 @@ public abstract class AbstractConnectionIntegrationTests {
@Test // DATAREDIS-1084
@EnabledOnCommand("XADD")
@EnabledOnRedisDriver({ RedisDriver.LETTUCE })
public void xClaim() throws InterruptedException {
actual.add(connection.xAdd(KEY_1, Collections.singletonMap(KEY_2, VALUE_2)));
@@ -3382,8 +3369,7 @@ public abstract class AbstractConnectionIntegrationTests {
@Test // DATAREDIS-1119
@EnabledOnCommand("XADD")
@EnabledOnRedisDriver({ RedisDriver.LETTUCE })
void xinfo() {
public void xinfo() {
actual.add(connection.xGroupCreate(KEY_1, ReadOffset.from("0"), "my-group-without-stream", true));
actual.add(connection.xAdd(KEY_1, Collections.singletonMap(KEY_2, VALUE_2)));
@@ -3411,8 +3397,7 @@ public abstract class AbstractConnectionIntegrationTests {
@Test // DATAREDIS-1119
@EnabledOnCommand("XADD")
@EnabledOnRedisDriver({ RedisDriver.LETTUCE })
void xinfoNoGroup() {
public void xinfoNoGroup() {
actual.add(connection.xAdd(KEY_1, Collections.singletonMap(KEY_2, VALUE_2)));
actual.add(connection.xAdd(KEY_1, Collections.singletonMap(KEY_3, VALUE_3)));
@@ -3436,8 +3421,7 @@ public abstract class AbstractConnectionIntegrationTests {
@Test // DATAREDIS-1119
@EnabledOnCommand("XADD")
@EnabledOnRedisDriver({ RedisDriver.LETTUCE })
void xinfoGroups() {
public void xinfoGroups() {
actual.add(connection.xAdd(KEY_1, Collections.singletonMap(KEY_2, VALUE_2)));
actual.add(connection.xAdd(KEY_1, Collections.singletonMap(KEY_3, VALUE_3)));
@@ -3461,8 +3445,7 @@ public abstract class AbstractConnectionIntegrationTests {
@Test // DATAREDIS-1119
@EnabledOnCommand("XADD")
@EnabledOnRedisDriver({ RedisDriver.LETTUCE })
void xinfoGroupsNoGroup() {
public void xinfoGroupsNoGroup() {
actual.add(connection.xAdd(KEY_1, Collections.singletonMap(KEY_2, VALUE_2)));
actual.add(connection.xAdd(KEY_1, Collections.singletonMap(KEY_3, VALUE_3)));
@@ -3478,8 +3461,7 @@ public abstract class AbstractConnectionIntegrationTests {
@Test // DATAREDIS-1119
@EnabledOnCommand("XADD")
@EnabledOnRedisDriver({ RedisDriver.LETTUCE })
void xinfoGroupsNoConsumer() {
public void xinfoGroupsNoConsumer() {
actual.add(connection.xAdd(KEY_1, Collections.singletonMap(KEY_2, VALUE_2)));
actual.add(connection.xAdd(KEY_1, Collections.singletonMap(KEY_3, VALUE_3)));
@@ -3501,8 +3483,7 @@ public abstract class AbstractConnectionIntegrationTests {
@Test // DATAREDIS-1119
@EnabledOnCommand("XADD")
@EnabledOnRedisDriver({ RedisDriver.LETTUCE })
void xinfoConsumers() {
public void xinfoConsumers() {
actual.add(connection.xAdd(KEY_1, Collections.singletonMap(KEY_2, VALUE_2)));
actual.add(connection.xAdd(KEY_1, Collections.singletonMap(KEY_3, VALUE_3)));
@@ -3525,8 +3506,7 @@ public abstract class AbstractConnectionIntegrationTests {
@Test // DATAREDIS-1119
@EnabledOnCommand("XADD")
@EnabledOnRedisDriver({ RedisDriver.LETTUCE })
void xinfoConsumersNoConsumer() {
public void xinfoConsumersNoConsumer() {
actual.add(connection.xAdd(KEY_1, Collections.singletonMap(KEY_2, VALUE_2)));
actual.add(connection.xAdd(KEY_1, Collections.singletonMap(KEY_3, VALUE_3)));

View File

@@ -31,6 +31,7 @@ import org.springframework.data.redis.SettingsUtils;
import org.springframework.data.redis.connection.AbstractConnectionPipelineIntegrationTests;
import org.springframework.data.redis.connection.DefaultStringRedisConnection;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.test.condition.EnabledOnCommand;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit.jupiter.SpringExtension;
@@ -221,6 +222,108 @@ public class JedisConnectionPipelineIntegrationTests extends AbstractConnectionP
assertThatExceptionOfType(UnsupportedOperationException.class).isThrownBy(super::clientSetNameWorksCorrectly);
}
@Test // GH-1711
@EnabledOnCommand("XADD")
@Override
public void xReadShouldReadMessage() {
assertThatExceptionOfType(UnsupportedOperationException.class).isThrownBy(super::xReadShouldReadMessage);
}
@Test // GH-1711
@EnabledOnCommand("XADD")
@Override
public void xReadGroupShouldReadMessage() {
assertThatExceptionOfType(UnsupportedOperationException.class).isThrownBy(super::xReadGroupShouldReadMessage);
}
@Test // GH-1711
@EnabledOnCommand("XADD")
@Override
public void xGroupCreateShouldWorkWithAndWithoutExistingStream() {
assertThatExceptionOfType(UnsupportedOperationException.class)
.isThrownBy(super::xGroupCreateShouldWorkWithAndWithoutExistingStream);
}
@Test // GH-1711
@EnabledOnCommand("XADD")
@Override
public void xPendingShouldLoadPendingMessages() {
assertThatExceptionOfType(UnsupportedOperationException.class).isThrownBy(super::xPendingShouldLoadPendingMessages);
}
@Test // GH-1711
@EnabledOnCommand("XADD")
@Override
public void xPendingShouldWorkWithBoundedRange() {
assertThatExceptionOfType(UnsupportedOperationException.class)
.isThrownBy(super::xPendingShouldWorkWithBoundedRange);
}
@Test // GH-1711
@EnabledOnCommand("XADD")
@Override
public void xPendingShouldLoadPendingMessagesForConsumer() {
assertThatExceptionOfType(UnsupportedOperationException.class)
.isThrownBy(super::xPendingShouldLoadPendingMessagesForConsumer);
}
@Test // GH-1711
@EnabledOnCommand("XADD")
@Override
public void xPendingShouldLoadPendingMessagesForNonExistingConsumer() {
assertThatExceptionOfType(UnsupportedOperationException.class)
.isThrownBy(super::xPendingShouldLoadPendingMessagesForNonExistingConsumer);
}
@Test // GH-1711
@EnabledOnCommand("XADD")
@Override
public void xinfo() {
assertThatExceptionOfType(UnsupportedOperationException.class).isThrownBy(super::xinfo);
}
@Test
@EnabledOnCommand("XADD")
@Override
public void xinfoNoGroup() {
assertThatExceptionOfType(UnsupportedOperationException.class).isThrownBy(super::xinfoNoGroup);
}
@Test // GH-1711
@EnabledOnCommand("XADD")
@Override
public void xinfoGroups() {
assertThatExceptionOfType(UnsupportedOperationException.class).isThrownBy(super::xinfoGroups);
}
@Test // GH-1711
@EnabledOnCommand("XADD")
@Override
public void xinfoGroupsNoGroup() {
assertThatExceptionOfType(UnsupportedOperationException.class).isThrownBy(super::xinfoGroupsNoGroup);
}
@Test // GH-1711
@EnabledOnCommand("XADD")
@Override
public void xinfoGroupsNoConsumer() {
assertThatExceptionOfType(UnsupportedOperationException.class).isThrownBy(super::xinfoGroupsNoConsumer);
}
@Test // GH-1711
@EnabledOnCommand("XADD")
@Override
public void xinfoConsumers() {
assertThatExceptionOfType(UnsupportedOperationException.class).isThrownBy(super::xinfoConsumers);
}
@Test // GH-1711
@EnabledOnCommand("XADD")
@Override
public void xinfoConsumersNoConsumer() {
assertThatExceptionOfType(UnsupportedOperationException.class).isThrownBy(super::xinfoConsumersNoConsumer);
}
@Test
@Override
// DATAREDIS-268

View File

@@ -55,4 +55,5 @@ public class JedisConnectionPipelineTxIntegrationTests extends JedisConnectionTr
@Test
@Disabled
public void testListClientsContainsAtLeastOneElement() {}
}

View File

@@ -24,6 +24,7 @@ import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.data.redis.connection.AbstractConnectionTransactionIntegrationTests;
import org.springframework.data.redis.connection.ReturnType;
import org.springframework.data.redis.test.condition.EnabledOnCommand;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit.jupiter.SpringExtension;
@@ -187,4 +188,107 @@ public class JedisConnectionTransactionIntegrationTests extends AbstractConnecti
assertThatExceptionOfType(UnsupportedOperationException.class)
.isThrownBy(super::testListClientsContainsAtLeastOneElement);
}
@Test // GH-1711
@EnabledOnCommand("XADD")
@Override
public void xReadShouldReadMessage() {
assertThatExceptionOfType(UnsupportedOperationException.class).isThrownBy(super::xReadShouldReadMessage);
}
@Test // GH-1711
@EnabledOnCommand("XADD")
@Override
public void xReadGroupShouldReadMessage() {
assertThatExceptionOfType(UnsupportedOperationException.class).isThrownBy(super::xReadGroupShouldReadMessage);
}
@Test // GH-1711
@EnabledOnCommand("XADD")
@Override
public void xGroupCreateShouldWorkWithAndWithoutExistingStream() {
assertThatExceptionOfType(UnsupportedOperationException.class)
.isThrownBy(super::xGroupCreateShouldWorkWithAndWithoutExistingStream);
}
@Test // GH-1711
@EnabledOnCommand("XADD")
@Override
public void xPendingShouldLoadPendingMessages() {
assertThatExceptionOfType(UnsupportedOperationException.class).isThrownBy(super::xPendingShouldLoadPendingMessages);
}
@Test // GH-1711
@EnabledOnCommand("XADD")
@Override
public void xPendingShouldWorkWithBoundedRange() {
assertThatExceptionOfType(UnsupportedOperationException.class)
.isThrownBy(super::xPendingShouldWorkWithBoundedRange);
}
@Test // GH-1711
@EnabledOnCommand("XADD")
@Override
public void xPendingShouldLoadPendingMessagesForConsumer() {
assertThatExceptionOfType(UnsupportedOperationException.class)
.isThrownBy(super::xPendingShouldLoadPendingMessagesForConsumer);
}
@Test // GH-1711
@EnabledOnCommand("XADD")
@Override
public void xPendingShouldLoadPendingMessagesForNonExistingConsumer() {
assertThatExceptionOfType(UnsupportedOperationException.class)
.isThrownBy(super::xPendingShouldLoadPendingMessagesForNonExistingConsumer);
}
@Test // GH-1711
@EnabledOnCommand("XADD")
@Override
public void xinfo() {
assertThatExceptionOfType(UnsupportedOperationException.class).isThrownBy(super::xinfo);
}
@Test
@EnabledOnCommand("XADD")
@Override
public void xinfoNoGroup() {
assertThatExceptionOfType(UnsupportedOperationException.class).isThrownBy(super::xinfoNoGroup);
}
@Test // GH-1711
@EnabledOnCommand("XADD")
@Override
public void xinfoGroups() {
assertThatExceptionOfType(UnsupportedOperationException.class).isThrownBy(super::xinfoGroups);
}
@Test // GH-1711
@EnabledOnCommand("XADD")
@Override
public void xinfoGroupsNoGroup() {
assertThatExceptionOfType(UnsupportedOperationException.class).isThrownBy(super::xinfoGroupsNoGroup);
}
@Test // GH-1711
@EnabledOnCommand("XADD")
@Override
public void xinfoGroupsNoConsumer() {
assertThatExceptionOfType(UnsupportedOperationException.class).isThrownBy(super::xinfoGroupsNoConsumer);
}
@Test // GH-1711
@EnabledOnCommand("XADD")
@Override
public void xinfoConsumers() {
assertThatExceptionOfType(UnsupportedOperationException.class).isThrownBy(super::xinfoConsumers);
}
@Test // GH-1711
@EnabledOnCommand("XADD")
@Override
public void xinfoConsumersNoConsumer() {
assertThatExceptionOfType(UnsupportedOperationException.class).isThrownBy(super::xinfoConsumersNoConsumer);
}
}

View File

@@ -25,8 +25,8 @@ import org.springframework.data.redis.Person;
import org.springframework.data.redis.PersonObjectFactory;
import org.springframework.data.redis.RawObjectFactory;
import org.springframework.data.redis.StringObjectFactory;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.connection.lettuce.extension.LettuceConnectionFactoryExtension;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.jedis.extension.JedisConnectionFactoryExtension;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.GenericToStringSerializer;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
@@ -47,6 +47,11 @@ abstract public class AbstractOperationsTestParams {
// DATAREDIS-241
public static Collection<Object[]> testParams() {
return testParams(JedisConnectionFactoryExtension.getConnectionFactory(RedisStanalone.class));
}
// DATAREDIS-241
public static Collection<Object[]> testParams(RedisConnectionFactory connectionFactory) {
ObjectFactory<String> stringFactory = new StringObjectFactory();
ObjectFactory<Long> longFactory = new LongObjectFactory();
@@ -54,54 +59,52 @@ abstract public class AbstractOperationsTestParams {
ObjectFactory<byte[]> rawFactory = new RawObjectFactory();
ObjectFactory<Person> personFactory = new PersonObjectFactory();
LettuceConnectionFactory lettuceConnectionFactory = LettuceConnectionFactoryExtension
.getConnectionFactory(RedisStanalone.class);
RedisTemplate<String, String> stringTemplate = new StringRedisTemplate();
stringTemplate.setConnectionFactory(lettuceConnectionFactory);
stringTemplate.setConnectionFactory(connectionFactory);
stringTemplate.afterPropertiesSet();
RedisTemplate<String, Long> longTemplate = new RedisTemplate<>();
longTemplate.setKeySerializer(StringRedisSerializer.UTF_8);
longTemplate.setValueSerializer(new GenericToStringSerializer<>(Long.class));
longTemplate.setConnectionFactory(lettuceConnectionFactory);
longTemplate.setConnectionFactory(connectionFactory);
longTemplate.afterPropertiesSet();
RedisTemplate<String, Double> doubleTemplate = new RedisTemplate<>();
doubleTemplate.setKeySerializer(StringRedisSerializer.UTF_8);
doubleTemplate.setValueSerializer(new GenericToStringSerializer<>(Double.class));
doubleTemplate.setConnectionFactory(lettuceConnectionFactory);
doubleTemplate.setConnectionFactory(connectionFactory);
doubleTemplate.afterPropertiesSet();
RedisTemplate<byte[], byte[]> rawTemplate = new RedisTemplate<>();
rawTemplate.setEnableDefaultSerializer(false);
rawTemplate.setConnectionFactory(lettuceConnectionFactory);
rawTemplate.setConnectionFactory(connectionFactory);
rawTemplate.afterPropertiesSet();
RedisTemplate<String, Person> personTemplate = new RedisTemplate<>();
personTemplate.setConnectionFactory(lettuceConnectionFactory);
personTemplate.setConnectionFactory(connectionFactory);
personTemplate.afterPropertiesSet();
OxmSerializer serializer = XstreamOxmSerializerSingleton.getInstance();
RedisTemplate<String, String> xstreamStringTemplate = new RedisTemplate<>();
xstreamStringTemplate.setConnectionFactory(lettuceConnectionFactory);
xstreamStringTemplate.setConnectionFactory(connectionFactory);
xstreamStringTemplate.setDefaultSerializer(serializer);
xstreamStringTemplate.afterPropertiesSet();
RedisTemplate<String, Person> xstreamPersonTemplate = new RedisTemplate<>();
xstreamPersonTemplate.setConnectionFactory(lettuceConnectionFactory);
xstreamPersonTemplate.setConnectionFactory(connectionFactory);
xstreamPersonTemplate.setValueSerializer(serializer);
xstreamPersonTemplate.afterPropertiesSet();
Jackson2JsonRedisSerializer<Person> jackson2JsonSerializer = new Jackson2JsonRedisSerializer<>(Person.class);
RedisTemplate<String, Person> jackson2JsonPersonTemplate = new RedisTemplate<>();
jackson2JsonPersonTemplate.setConnectionFactory(lettuceConnectionFactory);
jackson2JsonPersonTemplate.setConnectionFactory(connectionFactory);
jackson2JsonPersonTemplate.setValueSerializer(jackson2JsonSerializer);
jackson2JsonPersonTemplate.afterPropertiesSet();
GenericJackson2JsonRedisSerializer genericJackson2JsonSerializer = new GenericJackson2JsonRedisSerializer();
RedisTemplate<String, Person> genericJackson2JsonPersonTemplate = new RedisTemplate<>();
genericJackson2JsonPersonTemplate.setConnectionFactory(lettuceConnectionFactory);
genericJackson2JsonPersonTemplate.setConnectionFactory(connectionFactory);
genericJackson2JsonPersonTemplate.setValueSerializer(genericJackson2JsonSerializer);
genericJackson2JsonPersonTemplate.afterPropertiesSet();

View File

@@ -18,6 +18,7 @@ package org.springframework.data.redis.core;
import static org.assertj.core.api.Assertions.*;
import static org.assertj.core.api.Assumptions.*;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
@@ -30,7 +31,9 @@ import org.springframework.data.redis.ObjectFactory;
import org.springframework.data.redis.Person;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.RedisZSetCommands.Limit;
import org.springframework.data.redis.connection.jedis.extension.JedisConnectionFactoryExtension;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.connection.lettuce.extension.LettuceConnectionFactoryExtension;
import org.springframework.data.redis.connection.stream.Consumer;
import org.springframework.data.redis.connection.stream.MapRecord;
import org.springframework.data.redis.connection.stream.ObjectRecord;
@@ -43,6 +46,8 @@ import org.springframework.data.redis.connection.stream.StreamReadOptions;
import org.springframework.data.redis.connection.stream.StreamRecords;
import org.springframework.data.redis.test.condition.EnabledOnCommand;
import org.springframework.data.redis.test.condition.EnabledOnRedisDriver;
import org.springframework.data.redis.test.extension.RedisCluster;
import org.springframework.data.redis.test.extension.RedisStanalone;
import org.springframework.data.redis.test.extension.parametrized.MethodSource;
import org.springframework.data.redis.test.extension.parametrized.ParameterizedRedisTest;
@@ -67,10 +72,6 @@ public class DefaultStreamOperationsIntegrationTests<K, HK, HV> {
public DefaultStreamOperationsIntegrationTests(RedisTemplate<K, ?> redisTemplate, ObjectFactory<K> keyFactory,
ObjectFactory<?> objectFactory) {
// Currently, only Lettuce supports Redis Streams.
// See https://github.com/xetorthio/jedis/issues/1820
assumeThat(redisTemplate.getRequiredConnectionFactory()).isInstanceOf(LettuceConnectionFactory.class);
this.redisTemplate = redisTemplate;
this.connectionFactory = redisTemplate.getRequiredConnectionFactory();
this.keyFactory = keyFactory;
@@ -80,7 +81,21 @@ public class DefaultStreamOperationsIntegrationTests<K, HK, HV> {
}
public static Collection<Object[]> testParams() {
return AbstractOperationsTestParams.testParams();
List<Object[]> params = new ArrayList<>();
params.addAll(AbstractOperationsTestParams
.testParams(JedisConnectionFactoryExtension.getConnectionFactory(RedisStanalone.class)));
params.addAll(AbstractOperationsTestParams
.testParams(JedisConnectionFactoryExtension.getConnectionFactory(RedisCluster.class)));
params.addAll(AbstractOperationsTestParams
.testParams(LettuceConnectionFactoryExtension.getConnectionFactory(RedisStanalone.class)));
params.addAll(AbstractOperationsTestParams
.testParams(LettuceConnectionFactoryExtension.getConnectionFactory(RedisCluster.class)));
return params;
}
@BeforeEach
@@ -315,6 +330,8 @@ public class DefaultStreamOperationsIntegrationTests<K, HK, HV> {
@ParameterizedRedisTest // DATAREDIS-1084
void pendingShouldReadMessageSummary() {
// XPENDING summary not supported by Jedis
assumeThat(redisTemplate.getRequiredConnectionFactory()).isInstanceOf(LettuceConnectionFactory.class);
K key = keyFactory.instance();
HK hashKey = hashKeyFactory.instance();

View File

@@ -1,216 +0,0 @@
/*
* Copyright 2018-2021 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.core;
import org.junit.jupiter.api.BeforeEach;
import org.springframework.data.domain.Range;
import org.springframework.data.redis.ObjectFactory;
import org.springframework.data.redis.RawObjectFactory;
import org.springframework.data.redis.StringObjectFactory;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.RedisZSetCommands;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.connection.jedis.extension.JedisConnectionFactoryExtension;
import org.springframework.data.redis.connection.stream.Consumer;
import org.springframework.data.redis.connection.stream.MapRecord;
import org.springframework.data.redis.connection.stream.PendingMessages;
import org.springframework.data.redis.connection.stream.ReadOffset;
import org.springframework.data.redis.connection.stream.RecordId;
import org.springframework.data.redis.connection.stream.StreamOffset;
import org.springframework.data.redis.test.condition.EnabledOnCommand;
import org.springframework.data.redis.test.extension.RedisStanalone;
import org.springframework.data.redis.test.extension.parametrized.MethodSource;
import org.springframework.data.redis.test.extension.parametrized.ParameterizedRedisTest;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Integration tests for JedisStreamOperations.
*
* @author dengliming
*/
@MethodSource("testParams")
@EnabledOnCommand("XREAD")
public class JedisStreamOperationsIntegrationTests<K, HK, HV> {
private final RedisConnectionFactory connectionFactory;
private final RedisTemplate<K, ?> redisTemplate;
private final ObjectFactory<K> keyFactory;
private final ObjectFactory<HK> hashKeyFactory;
private final ObjectFactory<HV> hashValueFactory;
private final StreamOperations<K, HK, HV> streamOps;
public JedisStreamOperationsIntegrationTests(RedisTemplate<K, ?> redisTemplate, ObjectFactory<K> keyFactory,
ObjectFactory<HV> objectFactory) {
this.redisTemplate = redisTemplate;
this.connectionFactory = redisTemplate.getRequiredConnectionFactory();
this.keyFactory = keyFactory;
this.hashKeyFactory = (ObjectFactory<HK>) keyFactory;
this.hashValueFactory = (ObjectFactory<HV>) objectFactory;
this.streamOps = redisTemplate.opsForStream();
}
public static Collection<Object[]> testParams() {
ObjectFactory<String> stringFactory = new StringObjectFactory();
ObjectFactory<byte[]> rawFactory = new RawObjectFactory();
JedisConnectionFactory jedisConnectionFactory = JedisConnectionFactoryExtension
.getConnectionFactory(RedisStanalone.class);
RedisTemplate<String, String> stringTemplate = new StringRedisTemplate();
stringTemplate.setConnectionFactory(jedisConnectionFactory);
stringTemplate.afterPropertiesSet();
RedisTemplate<byte[], byte[]> rawTemplate = new RedisTemplate<>();
rawTemplate.setConnectionFactory(jedisConnectionFactory);
rawTemplate.setEnableDefaultSerializer(false);
rawTemplate.afterPropertiesSet();
return Arrays.asList(
new Object[][] { { stringTemplate, stringFactory, stringFactory }, { rawTemplate, rawFactory, rawFactory } });
}
@BeforeEach
void before() {
redisTemplate.execute((RedisCallback<Object>) connection -> {
connection.flushDb();
return null;
});
}
@ParameterizedRedisTest // DATAREDIS-1140
void add() {
K key = keyFactory.instance();
HK hashKey = hashKeyFactory.instance();
HV value = hashValueFactory.instance();
RecordId messageId = streamOps.add(key, Collections.singletonMap(hashKey, value));
List<MapRecord<K, HK, HV>> messages = streamOps.range(key, Range.unbounded());
assertThat(messages).hasSize(1);
MapRecord<K, HK, HV> message = messages.get(0);
assertThat(message.getId()).isEqualTo(messageId);
assertThat(message.getStream()).isEqualTo(key);
if (!(key instanceof byte[] || value instanceof byte[])) {
assertThat(message.getValue()).containsEntry(hashKey, value);
}
}
@ParameterizedRedisTest // DATAREDIS-1140
void range() {
K key = keyFactory.instance();
HK hashKey = hashKeyFactory.instance();
HV value = hashValueFactory.instance();
RecordId messageId1 = streamOps.add(key, Collections.singletonMap(hashKey, value));
RecordId messageId2 = streamOps.add(key, Collections.singletonMap(hashKey, value));
List<MapRecord<K, HK, HV>> messages = streamOps.range(key,
Range.from(Range.Bound.inclusive(messageId1.getValue())).to(Range.Bound.inclusive(messageId2.getValue())),
RedisZSetCommands.Limit.limit().count(1));
assertThat(messages).hasSize(1);
MapRecord<K, HK, HV> message = messages.get(0);
assertThat(message.getId()).isEqualTo(messageId1);
}
@ParameterizedRedisTest // DATAREDIS-1140
void reverseRange() {
K key = keyFactory.instance();
HK hashKey = hashKeyFactory.instance();
HV value = hashValueFactory.instance();
RecordId messageId1 = streamOps.add(key, Collections.singletonMap(hashKey, value));
RecordId messageId2 = streamOps.add(key, Collections.singletonMap(hashKey, value));
List<MapRecord<K, HK, HV>> messages = streamOps.reverseRange(key, Range.unbounded());
assertThat(messages).hasSize(2).extracting("id").containsSequence(messageId2, messageId1);
}
@ParameterizedRedisTest // DATAREDIS-1140
void read() {
K key = keyFactory.instance();
HK hashKey = hashKeyFactory.instance();
HV value = hashValueFactory.instance();
RecordId messageId = streamOps.add(key, Collections.singletonMap(hashKey, value));
streamOps.createGroup(key, ReadOffset.from("0-0"), "my-group");
List<MapRecord<K, HK, HV>> messages = streamOps.read(Consumer.from("my-group", "my-consumer"),
StreamOffset.create(key, ReadOffset.lastConsumed()));
assertThat(messages).hasSize(1);
MapRecord<K, HK, HV> message = messages.get(0);
assertThat(message.getId()).isEqualTo(messageId);
assertThat(message.getStream()).isEqualTo(key);
if (!(key instanceof byte[] || value instanceof byte[])) {
assertThat(message.getValue()).containsEntry(hashKey, value);
}
}
@ParameterizedRedisTest // DATAREDIS-1140
void size() {
K key = keyFactory.instance();
HK hashKey = hashKeyFactory.instance();
HV value = hashValueFactory.instance();
streamOps.add(key, Collections.singletonMap(hashKey, value));
assertThat(streamOps.size(key)).isEqualTo(1);
streamOps.add(key, Collections.singletonMap(hashKey, value));
assertThat(streamOps.size(key)).isEqualTo(2);
}
@ParameterizedRedisTest // DATAREDIS-1140
void pending() {
K key = keyFactory.instance();
HK hashKey = hashKeyFactory.instance();
HV value = hashValueFactory.instance();
RecordId messageId = streamOps.add(key, Collections.singletonMap(hashKey, value));
streamOps.createGroup(key, ReadOffset.from("0-0"), "my-group");
streamOps.read(Consumer.from("my-group", "my-consumer"), StreamOffset.create(key, ReadOffset.lastConsumed()));
PendingMessages pending = streamOps.pending(key, "my-group", Range.unbounded(), 10L);
assertThat(pending).hasSize(1);
assertThat(pending.get(0).getGroupName()).isEqualTo("my-group");
assertThat(pending.get(0).getConsumerName()).isEqualTo("my-consumer");
assertThat(pending.get(0).getTotalDeliveryCount()).isOne();
}
}

View File

@@ -33,15 +33,11 @@ import org.awaitility.Awaitility;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestInstance;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.core.convert.ConversionFailedException;
import org.springframework.data.redis.SettingsUtils;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
import org.springframework.data.redis.connection.lettuce.LettuceConnection;
import org.springframework.data.redis.connection.lettuce.extension.LettuceConnectionFactoryExtension;
import org.springframework.data.redis.connection.stream.ByteRecord;
import org.springframework.data.redis.connection.stream.Consumer;
import org.springframework.data.redis.connection.stream.MapRecord;
@@ -62,13 +58,10 @@ import org.springframework.util.NumberUtils;
* @author Mark Paluch
* @author Christoph Strobl
*/
@ExtendWith(LettuceConnectionFactoryExtension.class)
@EnabledOnCommand("XREAD")
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
public class StreamMessageListenerContainerIntegrationTests {
abstract class AbstractStreamMessageListenerContainerIntegrationTests {
private static final RedisStandaloneConfiguration standaloneConfiguration = new RedisStandaloneConfiguration(
SettingsUtils.getHost(), SettingsUtils.getPort());
private static final Duration DEFAULT_TIMEOUT = Duration.ofSeconds(2);
private final RedisConnectionFactory connectionFactory;
@@ -76,7 +69,7 @@ public class StreamMessageListenerContainerIntegrationTests {
private final StreamMessageListenerContainerOptions<String, MapRecord<String, String, String>> containerOptions = StreamMessageListenerContainerOptions
.builder().pollTimeout(Duration.ofMillis(100)).build();
public StreamMessageListenerContainerIntegrationTests(RedisConnectionFactory connectionFactory) {
AbstractStreamMessageListenerContainerIntegrationTests(RedisConnectionFactory connectionFactory) {
this.connectionFactory = connectionFactory;
this.redisTemplate = new StringRedisTemplate(connectionFactory);
this.redisTemplate.afterPropertiesSet();
@@ -400,9 +393,18 @@ public class StreamMessageListenerContainerIntegrationTests {
private int getNumberOfPending(String stream, String group) {
String value = ((List) ((LettuceConnection) connectionFactory.getConnection()).execute("XPENDING",
new NestedMultiOutput<>(StringCodec.UTF8), new byte[][] { stream.getBytes(), group.getBytes() })).get(0)
.toString();
RedisConnection connection = connectionFactory.getConnection();
if (connection instanceof LettuceConnection) {
String value = ((List) ((LettuceConnection) connectionFactory.getConnection()).execute("XPENDING",
new NestedMultiOutput<>(StringCodec.UTF8), new byte[][] { stream.getBytes(), group.getBytes() })).get(0)
.toString();
return NumberUtils.parseNumber(value, Integer.class);
}
String value = ((List) connectionFactory.getConnection().execute("XPENDING", stream.getBytes(), group.getBytes()))
.get(0).toString();
return NumberUtils.parseNumber(value, Integer.class);
}

View File

@@ -0,0 +1,40 @@
/*
* Copyright 2018-2021 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.stream;
import org.junit.jupiter.api.TestInstance;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.jedis.extension.JedisConnectionFactoryExtension;
import org.springframework.data.redis.test.condition.EnabledOnCommand;
/**
* Integration tests for {@link StreamMessageListenerContainer} using Jedis.
*
* @author Mark Paluch
*/
@ExtendWith(JedisConnectionFactoryExtension.class)
@EnabledOnCommand("XREAD")
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
public class JedisStreamMessageListenerContainerIntegrationTests
extends AbstractStreamMessageListenerContainerIntegrationTests {
public JedisStreamMessageListenerContainerIntegrationTests(RedisConnectionFactory connectionFactory) {
super(connectionFactory);
}
}

View File

@@ -0,0 +1,41 @@
/*
* Copyright 2018-2021 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.stream;
import org.junit.jupiter.api.TestInstance;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.lettuce.extension.LettuceConnectionFactoryExtension;
import org.springframework.data.redis.test.condition.EnabledOnCommand;
/**
* Integration tests for {@link StreamMessageListenerContainer} using Lettuce.
*
* @author Mark Paluch
* @author Christoph Strobl
*/
@ExtendWith(LettuceConnectionFactoryExtension.class)
@EnabledOnCommand("XREAD")
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
public class LettuceStreamMessageListenerContainerIntegrationTests
extends AbstractStreamMessageListenerContainerIntegrationTests {
public LettuceStreamMessageListenerContainerIntegrationTests(RedisConnectionFactory connectionFactory) {
super(connectionFactory);
}
}