From d1657874c2aa8a15064bc40e6b1c9c3afcbad898 Mon Sep 17 00:00:00 2001 From: ggivo Date: Sun, 10 Nov 2024 21:04:00 +0200 Subject: [PATCH] Introduces test matrix based on Redis versions [8.0-M1, 7.4.1, 7.2.6, 6.2.16] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use docker composer to bring up the test env using `redislabs/client-libs-test` image. When run against older Redis version some tests are using commands available only in newer Redis server versions. To resolve this we are introducing two new annotations/rules - Introduce `SinceRedisVersion` annotation/Rule - for conditionally running tests based on Redis server version contacted - Introduce `EnableOnCommad` annotation/Rule - for conditionally running tests based on command availability on the server And mark respective tests with the least Redis Version required by the test - SinceRedisVersion ("7.4.0") - Mark tests using commands/modifiers introduced with Redis 7.4.0 - SinceRedisVersion ("7.2.0") - Mark tests using commands/modifiers introduced with Redis 7.2.0 - SinceRedisVersion ("7.0.0") - Mark tests using commands/modifiers introduced with Redis 7.0.0 The same approach used to mark CSC tests - Disabled client-side caching tests for versions below 7.4 Fix in Jedis Client against Redis server 6.x - Fix NPW on CommandInfo - some of the array elements returned are available based from given RedisServer aclCategories (as of Redis 6.0) , tips , (as of Redis 7.0) subcommands - Fix NPE AccessControlLogEntry when used against Redis 6 Starting with Redis version 7.2.0: Added entry ID, timestamp created, and timestamp last updated fields. Fix Test failures against 6.x - Fix JedisPooledClientSideCacheTest - Fix AccessControlListCommandsTest.aclLogTest:372 » NullPointer - Fix AccessControlListCommandsTest.aclLogWithEntryID:473 » NullPointer - Fix StreamsCommandsTest - Fix StreamsPipelineCommandsTest xadd - Starting with Redis version 7.0.0: Added support for the -* explicit ID form. - Test env migrated to use native Redis server TLS instead of using stunnel Not all tests were migrated - Disable Modules test in containerized test env ModuleTest uses custom test module to test load/unload/sendCommand. Requires pre-build test module on the same os like test container to avoid errors - Disable UDS tests in containerized test env No easy way to make unix sockets work on MAC with docker --- .github/workflows/integration.yml | 4 +- .github/workflows/test-on-docker.yml | 128 ++++++++ .gitignore | 2 + Makefile | 12 +- .../jedis/resps/AccessControlLogEntry.java | 10 +- .../clients/jedis/resps/CommandInfo.java | 11 +- .../test/annotations/EnabledOnCommand.java | 11 + .../test/annotations/SinceRedisVersion.java | 11 + .../test/utils/EnabledOnCommandRule.java | 126 ++++++++ .../java/io/redis/test/utils/RedisInfo.java | 67 ++++ .../io/redis/test/utils/RedisVersion.java | 92 ++++++ .../io/redis/test/utils/RedisVersionRule.java | 90 ++++++ .../io/redis/test/utils/RedisVersionUtil.java | 44 +++ .../redis/clients/jedis/ACLJedisPoolTest.java | 16 +- .../jedis/ACLJedisSentinelPoolTest.java | 16 +- .../redis/clients/jedis/ACLJedisTest.java | 5 +- .../redis/clients/jedis/EndpointConfig.java | 4 + .../redis/clients/jedis/JedisClusterTest.java | 4 +- .../clients/jedis/JedisClusterTestBase.java | 9 + .../java/redis/clients/jedis/JedisTest.java | 4 + .../clients/jedis/SSLACLJedisClusterTest.java | 252 +++++++++------ .../redis/clients/jedis/SSLACLJedisTest.java | 121 ++------ .../clients/jedis/SSLJedisClusterTest.java | 180 ++++++----- .../jedis/SSLJedisSentinelPoolTest.java | 49 ++- .../redis/clients/jedis/SSLJedisTest.java | 210 ++++--------- .../java/redis/clients/jedis/UdsTest.java | 8 + .../CommandObjectsBitmapCommandsTest.java | 3 + .../CommandObjectsGenericCommandsTest.java | 19 ++ .../CommandObjectsHashCommandsTest.java | 12 + .../CommandObjectsListCommandsTest.java | 3 + .../CommandObjectsScriptingCommandsTest.java | 15 + .../CommandObjectsSetCommandsTest.java | 2 + .../CommandObjectsSortedSetCommandsTest.java | 9 + .../CommandObjectsStandaloneTestBase.java | 8 + .../CommandObjectsStringCommandsTest.java | 3 + .../CommandObjectsTestBase.java | 13 +- .../jedis/AccessControlListCommandsTest.java | 45 +-- .../jedis/AllKindOfValuesCommandsTest.java | 14 +- .../jedis/commands/jedis/BitCommandsTest.java | 22 +- .../commands/jedis/ClientCommandsTest.java | 11 + .../commands/jedis/ClusterCommandsTest.java | 21 +- .../jedis/ClusterJedisCommandsTestBase.java | 13 +- .../jedis/ClusterScriptingCommandsTest.java | 3 + ...erShardedPublishSubscribeCommandsTest.java | 13 + .../commands/jedis/ControlCommandsTest.java | 13 + .../commands/jedis/HashesCommandsTest.java | 25 ++ .../commands/jedis/JedisCommandsTestBase.java | 17 +- .../commands/jedis/ListCommandsTest.java | 3 + .../jedis/commands/jedis/ModuleTest.java | 11 +- .../commands/jedis/ScriptingCommandsTest.java | 21 +- .../jedis/commands/jedis/SetCommandsTest.java | 2 + .../commands/jedis/SlowlogCommandsTest.java | 29 +- .../commands/jedis/SortedSetCommandsTest.java | 6 + .../commands/jedis/SortingCommandsTest.java | 3 + .../commands/jedis/StreamsCommandsTest.java | 55 +++- .../jedis/StringValuesCommandsTest.java | 3 + .../AllKindOfValuesCommandsTestBase.java | 8 + .../commands/unified/BitCommandsTestBase.java | 7 + .../unified/HashesCommandsTestBase.java | 14 + .../unified/ListCommandsTestBase.java | 3 + .../commands/unified/SetCommandsTestBase.java | 2 + .../unified/SortedSetCommandsTestBase.java | 6 + .../unified/StringValuesCommandsTestBase.java | 3 + .../ClusterAllKindOfValuesCommandsTest.java | 14 + .../cluster/ClusterBitCommandsTest.java | 18 +- .../cluster/ClusterCommandsTestHelper.java | 2 +- .../cluster/ClusterHashesCommandsTest.java | 14 + .../cluster/ClusterListCommandsTest.java | 17 ++ .../cluster/ClusterSetCommandsTest.java | 16 + .../cluster/ClusterSortedSetCommandsTest.java | 16 + .../ClusterStringValuesCommandsTest.java | 16 + .../pipeline/ListPipelineCommandsTest.java | 3 + .../pipeline/PipelineCommandsTestBase.java | 15 +- .../pipeline/SetPipelineCommandsTest.java | 2 + .../SortedSetPipelineCommandsTest.java | 5 + .../pipeline/StreamsPipelineCommandsTest.java | 42 ++- .../PooledAllKindOfValuesCommandsTest.java | 13 + .../unified/pooled/PooledBitCommandsTest.java | 12 + .../pooled/PooledCommandsTestHelper.java | 2 +- .../pooled/PooledHashesCommandsTest.java | 12 + .../pooled/PooledListCommandsTest.java | 13 + .../pooled/PooledMiscellaneousTest.java | 20 +- .../unified/pooled/PooledSetCommandsTest.java | 12 + .../pooled/PooledSortedSetCommandsTest.java | 12 + .../PooledStringValuesCommandsTest.java | 11 + .../csc/AllowAndDenyListCacheableTest.java | 2 + .../csc/ClientSideCacheFunctionalityTest.java | 21 +- .../jedis/csc/ClientSideCacheTestBase.java | 25 +- .../csc/JedisClusterClientSideCacheTest.java | 30 +- .../csc/JedisPooledClientSideCacheTest.java | 10 +- .../JedisPooledClientSideCacheTestBase.java | 2 + .../JedisSentineledClientSideCacheTest.java | 47 +-- .../SSLJedisPooledClientSideCacheTest.java | 23 +- .../UnifiedJedisClientSideCacheTestBase.java | 4 +- .../modules/RedisModuleCommandsTestBase.java | 9 +- .../clients/jedis/util/RedisVersionUtil.java | 32 -- .../redis/clients/jedis/util/TestEnvUtil.java | 19 ++ .../redis/clients/jedis/util/TlsUtil.java | 286 ++++++++++++++++++ .../config/node-sentinel5/redis.conf | 11 + src/test/resources/env/.env | 7 + src/test/resources/env/.env.v6.2.16 | 8 + .../config/node-7379-8379/redis.conf | 9 + .../config/node-7380-8380/redis.conf | 8 + .../config/node-7381-8381/redis.conf | 9 + .../config/node-7382-8382/redis.conf | 9 + .../config/node-7383-8383/redis.conf | 9 + .../node-sentinel-26381-36381/redis.conf | 9 + src/test/resources/env/docker-compose.yml | 160 ++++++++++ src/test/resources/env/endpoint.map | 20 ++ .../env/redis-uds/config/node-0/redis.conf | 2 + .../config/node-6379-6390/redis.conf | 11 + .../config/node-6380/redis.conf | 8 + .../config/node-6383-6391/redis.conf | 9 + .../config/node-6386/redis.conf | 6 + .../node-sentinel-26379-36379/redis.conf | 8 + .../redis10-11/config/node-6388/redis.conf | 3 + .../redis10-11/config/node-6389/redis.conf | 4 + .../config/node-6381-16381/redis.conf | 6 + .../config/node-6382-16382/redis.conf | 7 + .../node-sentinel-26380-36380/redis.conf | 8 + .../node-sentinel-26382-36382/redis.conf | 8 + .../config/node-6384/redis.conf | 5 + .../config/node-6385/redis.conf | 6 + .../node-sentinel-26381-36381/redis.conf | 7 + .../config/node-6387-16387/redis.conf | 9 + .../node-sentinel-26383-36383/redis.conf | 12 + src/test/resources/private.crt | 21 ++ 127 files changed, 2523 insertions(+), 619 deletions(-) create mode 100644 .github/workflows/test-on-docker.yml create mode 100644 src/test/java/io/redis/test/annotations/EnabledOnCommand.java create mode 100644 src/test/java/io/redis/test/annotations/SinceRedisVersion.java create mode 100644 src/test/java/io/redis/test/utils/EnabledOnCommandRule.java create mode 100644 src/test/java/io/redis/test/utils/RedisInfo.java create mode 100644 src/test/java/io/redis/test/utils/RedisVersion.java create mode 100644 src/test/java/io/redis/test/utils/RedisVersionRule.java create mode 100644 src/test/java/io/redis/test/utils/RedisVersionUtil.java delete mode 100644 src/test/java/redis/clients/jedis/util/RedisVersionUtil.java create mode 100644 src/test/java/redis/clients/jedis/util/TestEnvUtil.java create mode 100644 src/test/java/redis/clients/jedis/util/TlsUtil.java create mode 100644 src/test/resources/Arch/sentinel5/config/node-sentinel5/redis.conf create mode 100644 src/test/resources/env/.env create mode 100644 src/test/resources/env/.env.v6.2.16 create mode 100644 src/test/resources/env/cluster-unbound/config/node-7379-8379/redis.conf create mode 100644 src/test/resources/env/cluster-unbound/config/node-7380-8380/redis.conf create mode 100644 src/test/resources/env/cluster-unbound/config/node-7381-8381/redis.conf create mode 100644 src/test/resources/env/cluster-unbound/config/node-7382-8382/redis.conf create mode 100644 src/test/resources/env/cluster-unbound/config/node-7383-8383/redis.conf create mode 100644 src/test/resources/env/config/redis6-7/node-sentinel-26381-36381/redis.conf create mode 100644 src/test/resources/env/docker-compose.yml create mode 100644 src/test/resources/env/endpoint.map create mode 100644 src/test/resources/env/redis-uds/config/node-0/redis.conf create mode 100644 src/test/resources/env/redis1-2-5-10-sentinel/config/node-6379-6390/redis.conf create mode 100644 src/test/resources/env/redis1-2-5-10-sentinel/config/node-6380/redis.conf create mode 100644 src/test/resources/env/redis1-2-5-10-sentinel/config/node-6383-6391/redis.conf create mode 100644 src/test/resources/env/redis1-2-5-10-sentinel/config/node-6386/redis.conf create mode 100644 src/test/resources/env/redis1-2-5-10-sentinel/config/node-sentinel-26379-36379/redis.conf create mode 100644 src/test/resources/env/redis10-11/config/node-6388/redis.conf create mode 100644 src/test/resources/env/redis10-11/config/node-6389/redis.conf create mode 100644 src/test/resources/env/redis3-4-sentinel/config/node-6381-16381/redis.conf create mode 100644 src/test/resources/env/redis3-4-sentinel/config/node-6382-16382/redis.conf create mode 100644 src/test/resources/env/redis3-4-sentinel/config/node-sentinel-26380-36380/redis.conf create mode 100644 src/test/resources/env/redis3-4-sentinel/config/node-sentinel-26382-36382/redis.conf create mode 100644 src/test/resources/env/redis6-7-sentinel/config/node-6384/redis.conf create mode 100644 src/test/resources/env/redis6-7-sentinel/config/node-6385/redis.conf create mode 100644 src/test/resources/env/redis6-7-sentinel/config/node-sentinel-26381-36381/redis.conf create mode 100644 src/test/resources/env/redis9-sentinel/config/node-6387-16387/redis.conf create mode 100644 src/test/resources/env/redis9-sentinel/config/node-sentinel-26383-36383/redis.conf create mode 100644 src/test/resources/private.crt diff --git a/.github/workflows/integration.yml b/.github/workflows/integration.yml index 3df4e12271..70342266fe 100644 --- a/.github/workflows/integration.yml +++ b/.github/workflows/integration.yml @@ -34,8 +34,8 @@ jobs: - name: System setup run: | sudo apt update - sudo apt install -y stunnel make - make system-setup + sudo apt install -y make + make compile-module - name: Cache dependencies uses: actions/cache@v2 with: diff --git a/.github/workflows/test-on-docker.yml b/.github/workflows/test-on-docker.yml new file mode 100644 index 0000000000..bccdeab4e5 --- /dev/null +++ b/.github/workflows/test-on-docker.yml @@ -0,0 +1,128 @@ +--- + +name: Build and Test using containerized environment + +on: + push: + paths-ignore: + - 'docs/**' + - '**/*.md' + - '**/*.rst' + branches: + - master + - '[0-9].*' + pull_request: + branches: + - master + - '[0-9].*' + schedule: + - cron: '0 1 * * *' # nightly build + workflow_dispatch: + inputs: + specific_test: + description: 'Run specific test(s) (optional)' + required: false + default: '' +jobs: + + build: + name: Build and Test + runs-on: ubuntu-latest + env: + REDIS_ENV_WORK_DIR: ${{ github.workspace }}/redis-env-work + REDIS_ENV_CONF_DIR: ${{ github.workspace }}/src/test/resources/env + CLIENT_LIBS_IMAGE_PREFIX: "redislabs/client-libs-test" + strategy: + fail-fast: false + matrix: + redis_version: + - "8.0-M01" + - "7.4.1" + - "7.2.6" + - "6.2.16" + steps: + - uses: actions/checkout@v2 + - name: Set up publishing to maven central + uses: actions/setup-java@v2 + with: + java-version: '8' + distribution: 'temurin' + - name: System setup + run: | + sudo apt update + sudo apt install -y make + make compile-module + - name: Cache dependencies + uses: actions/cache@v2 + with: + path: | + ~/.m2/repository + /var/cache/apt + key: jedis-${{hashFiles('**/pom.xml')}} + # Set up Docker Compose environment + - name: Set up Docker Compose environment + run: | + mkdir -m 777 $REDIS_ENV_WORK_DIR + export CLIENT_LIBS_TEST_IMAGE="${CLIENT_LIBS_IMAGE_PREFIX}:${{ matrix.redis_version }}" + export COMPOSE_ENV_FILES="src/test/resources/env/.env" + if [[ "${{ matrix.redis_version }}" == "6.2.16" ]]; then + COMPOSE_ENV_FILES+=",src/test/resources/env/.env.v${{ matrix.redis_version }}" + fi + docker compose -f src/test/resources/env/docker-compose.yml up -d + - name: Maven offline + run: | + mvn -q dependency:go-offline + - name: Build docs + run: | + mvn javadoc:jar + # Run Tests + - name: Run Maven tests + run: | + export TEST_ENV_PROVIDER=docker + export TEST_WORK_FOLDER=$REDIS_ENV_WORK_DIR + echo $TEST_WORK_FOLDER + if [ -z "$TESTS" ]; then + mvn clean compile test + else + mvn -Dtest=$SPECIFIC_TEST clean compile test + fi + env: + TESTS: ${{ github.event.inputs.specific_test || '' }} + - name: Publish Test Results + uses: EnricoMi/publish-unit-test-result-action@v2 + if: always() + with: + files: | + target/surefire-reports/**/*.xml + # Collect logs on failure + - name: Collect logs on failure + if: failure() # This runs only if the previous steps failed + run: | + echo "Collecting logs from $WORK_DIR..." + ls -la $REDIS_ENV_WORK_DIR + # Upload logs as artifacts + - name: Upload logs on failure + if: failure() + uses: actions/upload-artifact@v3 + with: + name: redis-env-work-logs + path: ${{ env.REDIS_ENV_WORK_DIR }} + # Bring down the Docker Compose test environment + - name: Tear down Docker Compose environment + if: always() + run: | + docker compose $COMPOSE_ENV_FILES -f src/test/resources/env/docker-compose.yml down + continue-on-error: true + # Upload code coverage + - name: Upload coverage to Codecov + uses: codecov/codecov-action@v4 + with: + fail_ci_if_error: false + token: ${{ secrets.CODECOV_TOKEN }} + - name: Upload test results to Codecov + if: ${{ github.event_name == 'schedule' || (github.event_name == 'push') || github.event_name == 'workflow_dispatch'}} + uses: codecov/test-results-action@v1 + with: + fail_ci_if_error: false + files: ./target/surefire-reports/TEST* + token: ${{ secrets.CODECOV_TOKEN }} diff --git a/.gitignore b/.gitignore index 8cb08a2658..5477116e12 100644 --- a/.gitignore +++ b/.gitignore @@ -11,7 +11,9 @@ build/ bin/ tags .idea +.run *.aof *.rdb redis-git appendonlydir/ +.DS_Store diff --git a/Makefile b/Makefile index 1800f00d7e..45df2f9059 100644 --- a/Makefile +++ b/Makefile @@ -7,6 +7,7 @@ protected-mode no port 6379 requirepass foobared user acljedis on allcommands allkeys >fizzbuzz +user deploy on allcommands allkeys >verify pidfile /tmp/redis1.pid logfile /tmp/redis1.log save "" @@ -189,6 +190,7 @@ endef define REDIS_SENTINEL5 port 26383 +tlsport 36383 daemonize yes protected-mode no user default off @@ -525,8 +527,14 @@ mvn-release: mvn release:prepare mvn release:perform -DskipTests -system-setup: - sudo apt install -y gcc g++ +install-gcc: + @if [ "$(shell uname)" = "Darwin" ]; then \ + brew install gcc; \ + else \ + sudo apt install -y gcc g++; \ + fi + +system-setup: install-gcc [ ! -e redis-git ] && git clone https://github.com/redis/redis.git --branch unstable --single-branch redis-git || true $(MAKE) -C redis-git clean $(MAKE) -C redis-git diff --git a/src/main/java/redis/clients/jedis/resps/AccessControlLogEntry.java b/src/main/java/redis/clients/jedis/resps/AccessControlLogEntry.java index 930c9b064d..f95ade23cd 100644 --- a/src/main/java/redis/clients/jedis/resps/AccessControlLogEntry.java +++ b/src/main/java/redis/clients/jedis/resps/AccessControlLogEntry.java @@ -38,6 +38,10 @@ public class AccessControlLogEntry implements Serializable { private final long timestampCreated; private final long timestampLastUpdated; + /* + * Starting with Redis version 7.2.0: Added entry ID, timestamp created, and timestamp last updated. + * @see https://redis.io/docs/latest/commands/acl-log/ + */ public AccessControlLogEntry(Map map) { count = (long) map.get(COUNT); reason = (String) map.get(REASON); @@ -47,9 +51,9 @@ public AccessControlLogEntry(Map map) { ageSeconds = (Double) map.get(AGE_SECONDS); clientInfo = getMapFromRawClientInfo((String) map.get(CLIENT_INFO)); logEntry = map; - entryId = (long) map.get(ENTRY_ID); - timestampCreated = (long) map.get(TIMESTAMP_CREATED); - timestampLastUpdated = (long) map.get(TIMESTAMP_LAST_UPDATED); + entryId = map.get(ENTRY_ID) == null ? 0L : (long) map.get(ENTRY_ID); + timestampCreated = map.get(TIMESTAMP_CREATED) == null ? 0L : (long) map.get(TIMESTAMP_CREATED); + timestampLastUpdated = map.get(TIMESTAMP_LAST_UPDATED) == null ? 0L : (long) map.get(TIMESTAMP_LAST_UPDATED); } public long getCount() { diff --git a/src/main/java/redis/clients/jedis/resps/CommandInfo.java b/src/main/java/redis/clients/jedis/resps/CommandInfo.java index 9f3481b5a1..ac1ff9beb2 100644 --- a/src/main/java/redis/clients/jedis/resps/CommandInfo.java +++ b/src/main/java/redis/clients/jedis/resps/CommandInfo.java @@ -2,6 +2,7 @@ import redis.clients.jedis.Builder; +import java.util.Collections; import java.util.List; import static redis.clients.jedis.BuilderFactory.STRING_LIST; @@ -103,9 +104,13 @@ public CommandInfo build(Object data) { long firstKey = LONG.build(commandData.get(3)); long lastKey = LONG.build(commandData.get(4)); long step = LONG.build(commandData.get(5)); - List aclCategories = STRING_LIST.build(commandData.get(6)); - List tips = STRING_LIST.build(commandData.get(7)); - List subcommands = STRING_LIST.build(commandData.get(9)); + + // (as of Redis 6.0) + List aclCategories = commandData.size()>=6?STRING_LIST.build(commandData.get(6)):Collections.emptyList(); + + // (as of Redis 7.0) + List tips = commandData.size()>=8?STRING_LIST.build(commandData.get(7)):Collections.emptyList(); + List subcommands = commandData.size()>=10?STRING_LIST.build(commandData.get(9)): Collections.emptyList(); return new CommandInfo(arity, flags, firstKey, lastKey, step, aclCategories, tips, subcommands); } diff --git a/src/test/java/io/redis/test/annotations/EnabledOnCommand.java b/src/test/java/io/redis/test/annotations/EnabledOnCommand.java new file mode 100644 index 0000000000..1d3a963766 --- /dev/null +++ b/src/test/java/io/redis/test/annotations/EnabledOnCommand.java @@ -0,0 +1,11 @@ +package io.redis.test.annotations; + +import java.lang.annotation.*; + +@Inherited +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.METHOD, ElementType.TYPE}) +public @interface EnabledOnCommand { + String value(); + String subCommand() default ""; +} \ No newline at end of file diff --git a/src/test/java/io/redis/test/annotations/SinceRedisVersion.java b/src/test/java/io/redis/test/annotations/SinceRedisVersion.java new file mode 100644 index 0000000000..9ae1d3f353 --- /dev/null +++ b/src/test/java/io/redis/test/annotations/SinceRedisVersion.java @@ -0,0 +1,11 @@ +package io.redis.test.annotations; + +import java.lang.annotation.*; + +@Inherited +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.METHOD, ElementType.TYPE}) +public @interface SinceRedisVersion { + String value(); + String message() default ""; +} diff --git a/src/test/java/io/redis/test/utils/EnabledOnCommandRule.java b/src/test/java/io/redis/test/utils/EnabledOnCommandRule.java new file mode 100644 index 0000000000..d32152341f --- /dev/null +++ b/src/test/java/io/redis/test/utils/EnabledOnCommandRule.java @@ -0,0 +1,126 @@ +package io.redis.test.utils; + +import io.redis.test.annotations.EnabledOnCommand; +import org.junit.Assume; +import org.junit.rules.TestRule; +import org.junit.runner.Description; +import org.junit.runners.model.Statement; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import redis.clients.jedis.*; +import redis.clients.jedis.resps.CommandInfo; + +import java.lang.reflect.Method; +import java.util.Map; + + +public class EnabledOnCommandRule implements TestRule { + private static final Logger logger = LoggerFactory.getLogger(EnabledOnCommandRule.class); + + private final HostAndPort hostPort; + private final JedisClientConfig config; + + public EnabledOnCommandRule(HostAndPort hostPort, JedisClientConfig config) { + this.hostPort = hostPort; + this.config = config; + } + + public EnabledOnCommandRule(EndpointConfig endpointConfig) { + this.hostPort = endpointConfig.getHostAndPort(); + this.config = endpointConfig.getClientConfigBuilder().build(); + } + + @Override + public Statement apply(Statement base, Description description) { + return new Statement() { + @Override + public void evaluate() throws Throwable { + try (Jedis jedisClient = new Jedis(hostPort, config)) { + String[] command = getCommandFromAnnotations(description); + + if (command != null && !isCommandAvailable(jedisClient, command[0],command[1])) { + Assume.assumeTrue("Test requires Redis command '" + command[0] + " " + command[1] + "' to be available, but it was not found.", false); + } + + base.evaluate(); + } + } + + /** + * Retrieves the command from either class-level or method-level annotations. + * + * @param description The test description containing annotations. + * @return The Redis array containing command, subcommand from the annotations, or null if not found. + */ + private String[] getCommandFromAnnotations(Description description) { + // Retrieve class-level and method-level annotations + EnabledOnCommand descriptionCommandAnnotation = description.getAnnotation(EnabledOnCommand.class); + if (descriptionCommandAnnotation != null) { + return new String[] {descriptionCommandAnnotation.value(), descriptionCommandAnnotation.subCommand()}; + } + + EnabledOnCommand methodCommand = getMethodAnnotation(description); + if (methodCommand != null) { + return new String[] {methodCommand.value(), methodCommand.subCommand()}; + } + + EnabledOnCommand classCommand = description.getTestClass().getAnnotation(EnabledOnCommand.class); + if (classCommand != null) { + return new String[] {classCommand.value(), classCommand.subCommand()}; + } + + return null; + } + + private EnabledOnCommand getMethodAnnotation(Description description) { + try { + // description.getAnnotation() does not return anootaion when used + // with parametrised tests + String methodName = description.getMethodName(); + if (methodName != null) { + Class testClass = description.getTestClass(); + if (testClass != null) { + for (Method method : testClass.getDeclaredMethods()) { + if (method.getName().equals(methodName)) { + return method.getAnnotation(EnabledOnCommand.class); + } + } + } + } + } catch (Exception e) { + // Handle any potential exceptions here + throw new RuntimeException("Could not resolve EnabledOnCommand annotation",e); + } + return null; + } + + /** + * Checks if the specified Redis command is available. + */ + private boolean isCommandAvailable(Jedis jedisClient, String command, String subCommand) { + try { + Object raw = jedisClient.sendCommand(redis.clients.jedis.Protocol.Command.COMMAND); + Map commandList = BuilderFactory.COMMAND_INFO_RESPONSE.build(raw); + CommandInfo commandInfo = commandList.get(command.toLowerCase()); + if (commandInfo != null) { + // If a subCommand is provided, check for the subcommand under this command + if (subCommand != null && !subCommand.isEmpty()) { + // Check if this command supports the provided subcommand + for (String supportedSubCommand : commandInfo.getSubcommands()) { + if (subCommand.equalsIgnoreCase(supportedSubCommand)) { + return true; + } + } + return false; // Subcommand not found + } + return true; // Command found (no subcommand required) + } + return false; // Command not found + } catch (Exception e) { + logger.error("Error checking command '{}' availability: {}", command, e.getMessage()); + return false; + } + } + }; + } +} diff --git a/src/test/java/io/redis/test/utils/RedisInfo.java b/src/test/java/io/redis/test/utils/RedisInfo.java new file mode 100644 index 0000000000..fa69432fb2 --- /dev/null +++ b/src/test/java/io/redis/test/utils/RedisInfo.java @@ -0,0 +1,67 @@ +package io.redis.test.utils; + +import java.util.HashMap; +import java.util.Map; + +public class RedisInfo { + private final Map infoMap; + + public RedisInfo() { + this.infoMap = new HashMap<>(); + } + + public void setField(String key, String value) { + infoMap.put(key, value); + } + + public String getField(String key) { + return infoMap.get(key); + } + + public String getRedisVersion() { + return infoMap.get("redis_version"); + } + + public String getOs() { + return infoMap.get("os"); + } + + public String getMode() { + return infoMap.get("redis_mode"); + } + + public String getPorts() { + return infoMap.get("tcp_port"); // Assuming "tcp_port" is the key for ports + } + + @Override + public String toString() { + return "RedisInfo{" + + "infoMap=" + infoMap + + '}'; + } + + public static RedisInfo parseInfoServer(String infoOutput) { + RedisInfo redisInfo = new RedisInfo(); + + String[] lines = infoOutput.split("\n"); + + for (String line : lines) { + // Only parse lines that contain a colon (indicating a key-value pair) + if (line.contains(":")) { + String[] parts = line.split(":", 2); + if (parts.length == 2) { + redisInfo.setField(parts[0].trim(), parts[1].trim()); + } + } + } + + // You can still check for required fields if necessary + // Example: Ensure that specific fields are set + if (redisInfo.getField("redis_version") == null || redisInfo.getField("redis_mode") == null) { + throw new IllegalArgumentException("Missing required fields in Redis server info."); + } + + return redisInfo; + } +} \ No newline at end of file diff --git a/src/test/java/io/redis/test/utils/RedisVersion.java b/src/test/java/io/redis/test/utils/RedisVersion.java new file mode 100644 index 0000000000..a87d60262e --- /dev/null +++ b/src/test/java/io/redis/test/utils/RedisVersion.java @@ -0,0 +1,92 @@ +package io.redis.test.utils; + +public class RedisVersion implements Comparable{ + public static final RedisVersion V6_0_0 = RedisVersion.of("6.0.0"); + public static final RedisVersion V7_0_0 = RedisVersion.of("7.0.0"); + public static final RedisVersion V7_2_0 = RedisVersion.of("7.2.0"); + public static final RedisVersion V7_4 = RedisVersion.of("7.4"); + public static final RedisVersion V8_0_0 = RedisVersion.of("8.0.0"); + + private final int major; + private final int minor; + private final int patch; + + // Private constructor to enforce use of the static factory method + private RedisVersion(int major, int minor, int patch) { + this.major = major; + this.minor = minor; + this.patch = patch; + } + + // Static method to create a RedisVersion from a version string + public static RedisVersion of(String version) { + // Check for the "redis_version:" prefix and remove it if present + if (version.startsWith("redis_version:")) { + version = version.substring("redis_version:".length()); + } + + // Split the version string by the '.' character + String[] parts = version.split("\\."); + + // Parse each component, setting defaults for missing parts + int major = parts.length > 0 ? Integer.parseInt(parts[0]) : 0; + int minor = parts.length > 1 ? Integer.parseInt(parts[1]) : 0; + int patch = parts.length > 2 ? Integer.parseInt(parts[2]) : 0; + + return new RedisVersion(major, minor, patch); + } + + public int getMajor() { + return major; + } + + public int getMinor() { + return minor; + } + + public int getPatch() { + return patch; + } + + @Override + public String toString() { + return "RedisVersion{" + + "major=" + major + + ", minor=" + minor + + ", patch=" + patch + + '}'; + } + + @Override + public int compareTo(RedisVersion other) { + // Compare major, minor, and patch versions + if (this.major != other.major) { + return Integer.compare(this.major, other.major); + } + if (this.minor != other.minor) { + return Integer.compare(this.minor, other.minor); + } + return Integer.compare(this.patch, other.patch); + } + + public boolean isLessThanOrEqualTo(RedisVersion other) { + return this.compareTo(other) <= 0; + } + + public boolean isLessThan(RedisVersion other) { + return this.compareTo(other) < 0; + } + + public boolean isGreaterThanOrEqualTo(RedisVersion other) { + return this.compareTo(other) >= 0; + } + + public boolean isGreaterThan(RedisVersion other) { + return this.compareTo(other) > 0; + } + + // Static method to compare two RedisVersion instances + public static int compare(RedisVersion v1, RedisVersion v2) { + return v1.compareTo(v2); + } +} \ No newline at end of file diff --git a/src/test/java/io/redis/test/utils/RedisVersionRule.java b/src/test/java/io/redis/test/utils/RedisVersionRule.java new file mode 100644 index 0000000000..d8d8ee2770 --- /dev/null +++ b/src/test/java/io/redis/test/utils/RedisVersionRule.java @@ -0,0 +1,90 @@ +package io.redis.test.utils; + +import io.redis.test.annotations.SinceRedisVersion; +import org.junit.Assume; +import org.junit.rules.TestRule; +import org.junit.runner.Description; +import org.junit.runners.model.Statement; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import redis.clients.jedis.EndpointConfig; +import redis.clients.jedis.HostAndPort; +import redis.clients.jedis.Jedis; +import redis.clients.jedis.JedisClientConfig; + +import java.lang.reflect.Method; + +public class RedisVersionRule implements TestRule { + private static final Logger logger = LoggerFactory.getLogger(RedisVersionRule.class); + + private final HostAndPort hostPort; + private final JedisClientConfig config; + + public RedisVersionRule(EndpointConfig endpoint) { + this.hostPort = endpoint.getHostAndPort(); + this.config = endpoint.getClientConfigBuilder().build(); + } + + public RedisVersionRule(HostAndPort hostPort, JedisClientConfig config) { + this.hostPort = hostPort; + this.config = config; + } + + @Override + public Statement apply(Statement base, Description description) { + return new Statement() { + @Override + public void evaluate() throws Throwable { + try ( Jedis jedisClient = new Jedis(hostPort, config)) { + SinceRedisVersion descriptionVersionAnnotation = description.getAnnotation(SinceRedisVersion.class); + if (descriptionVersionAnnotation != null) { + checkRedisVersion(jedisClient, descriptionVersionAnnotation); + } + + SinceRedisVersion classVersionAnnotation = description.getTestClass().getAnnotation(SinceRedisVersion.class); + if (classVersionAnnotation != null) { + checkRedisVersion(jedisClient, classVersionAnnotation); + } + + SinceRedisVersion methodVersionAnnotation = getMethodAnnotation(description); + if (methodVersionAnnotation != null) { + checkRedisVersion(jedisClient, methodVersionAnnotation); + } + + // Return the base statement to execute the test + base.evaluate(); + } + } + private void checkRedisVersion(Jedis jedisClient, SinceRedisVersion versionAnnotation) { + RedisVersion minRequiredVersion = RedisVersion.of(versionAnnotation.value()); + RedisInfo info = RedisInfo.parseInfoServer(jedisClient.info("server")); + RedisVersion currentVersion = RedisVersion.of(info.getRedisVersion()); + if (currentVersion.isLessThan(minRequiredVersion)) { + Assume.assumeTrue("Test requires Redis version " + minRequiredVersion + " or later, but found " + currentVersion, false); + } + } + + private SinceRedisVersion getMethodAnnotation(Description description) { + try { + // description.getAnnotation() does not return any method level annotation when used + // with parametrised tests + String methodName = description.getMethodName(); + if (methodName != null) { + Class testClass = description.getTestClass(); + if (testClass != null) { + for (Method method : testClass.getDeclaredMethods()) { + if (method.getName().equals(methodName)) { + return method.getAnnotation(SinceRedisVersion.class); + } + } + } + } + } catch (Exception e) { + // Handle any potential exceptions here + throw new RuntimeException("Could not resolve EnabledOnCommand annotation", e); + } + return null; + } + }; + } +} diff --git a/src/test/java/io/redis/test/utils/RedisVersionUtil.java b/src/test/java/io/redis/test/utils/RedisVersionUtil.java new file mode 100644 index 0000000000..8ddcba16b8 --- /dev/null +++ b/src/test/java/io/redis/test/utils/RedisVersionUtil.java @@ -0,0 +1,44 @@ +package io.redis.test.utils; + +import redis.clients.jedis.*; +import redis.clients.jedis.util.SafeEncoder; + +import java.util.Map; + +import static redis.clients.jedis.util.TlsUtil.createTrustAllSslSocketFactory; + +public class RedisVersionUtil { + + + public static RedisVersion getRedisVersion(Connection conn) { + + try (Jedis jedis = new Jedis(conn)) { + Object response = SafeEncoder.encodeObject(jedis.sendCommand(Protocol.Command.INFO, "server")); + RedisInfo info = RedisInfo.parseInfoServer(response.toString()); + return RedisVersion.of(info.getRedisVersion()); + } + } + + public static RedisVersion getRedisVersion(UnifiedJedis jedis) { + + Object response = SafeEncoder.encodeObject(jedis.sendCommand(Protocol.Command.INFO, "server")); + RedisInfo info = RedisInfo.parseInfoServer(response.toString()); + return RedisVersion.of(info.getRedisVersion()); + } + + public static RedisVersion getRedisVersion(Jedis jedis) { + + RedisInfo info = RedisInfo.parseInfoServer(jedis.info("server")); + return RedisVersion.of(info.getRedisVersion()); + } + + public static RedisVersion getRedisVersion(EndpointConfig endpoint) { + DefaultJedisClientConfig.Builder builder = endpoint.getClientConfigBuilder(); + if (endpoint.isTls()) { + builder.sslSocketFactory(createTrustAllSslSocketFactory()); + } + try (Jedis jedis = new Jedis(endpoint.getHostAndPort(), builder.build())) { + return getRedisVersion(jedis); + } + } +} diff --git a/src/test/java/redis/clients/jedis/ACLJedisPoolTest.java b/src/test/java/redis/clients/jedis/ACLJedisPoolTest.java index 5d7579b367..0d160e8697 100644 --- a/src/test/java/redis/clients/jedis/ACLJedisPoolTest.java +++ b/src/test/java/redis/clients/jedis/ACLJedisPoolTest.java @@ -4,33 +4,33 @@ import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; +import static io.redis.test.utils.RedisVersionUtil.getRedisVersion; import java.net.URI; import java.net.URISyntaxException; + +import io.redis.test.annotations.SinceRedisVersion; +import io.redis.test.utils.RedisVersionRule; import org.apache.commons.pool2.impl.GenericObjectPoolConfig; -import org.junit.BeforeClass; +import org.junit.ClassRule; import org.junit.Test; import redis.clients.jedis.exceptions.InvalidURIException; import redis.clients.jedis.exceptions.JedisException; -import redis.clients.jedis.util.RedisVersionUtil; /** * This test class is a copy of {@link JedisPoolTest}. *

* This test is only executed when the server/cluster is Redis 6. or more. */ +@SinceRedisVersion("6.0.0") public class ACLJedisPoolTest { private static final EndpointConfig endpoint = HostAndPorts.getRedisEndpoint("standalone0-acl"); private static final EndpointConfig endpointWithDefaultUser = HostAndPorts.getRedisEndpoint("standalone0"); - @BeforeClass - public static void prepare() throws Exception { - // Use to check if the ACL test should be ran. ACL are available only in 6.0 and later - org.junit.Assume.assumeTrue("Not running ACL test on this version of Redis", - RedisVersionUtil.checkRedisMajorVersionNumber(6, endpoint)); - } + @ClassRule + public static RedisVersionRule versionRule = new RedisVersionRule(endpoint.getHostAndPort(), endpoint.getClientConfigBuilder().build()); @Test public void checkConnections() { diff --git a/src/test/java/redis/clients/jedis/ACLJedisSentinelPoolTest.java b/src/test/java/redis/clients/jedis/ACLJedisSentinelPoolTest.java index b62809f0bb..a14cfbbc9a 100644 --- a/src/test/java/redis/clients/jedis/ACLJedisSentinelPoolTest.java +++ b/src/test/java/redis/clients/jedis/ACLJedisSentinelPoolTest.java @@ -1,25 +1,26 @@ package redis.clients.jedis; import static org.junit.Assert.*; +import static io.redis.test.utils.RedisVersionUtil.getRedisVersion; import java.util.HashSet; import java.util.Set; import java.util.stream.Collectors; + +import io.redis.test.annotations.SinceRedisVersion; +import io.redis.test.utils.RedisVersionRule; import org.apache.commons.pool2.impl.GenericObjectPoolConfig; -import org.junit.After; -import org.junit.Before; -import org.junit.BeforeClass; -import org.junit.Test; +import org.junit.*; import redis.clients.jedis.exceptions.JedisConnectionException; import redis.clients.jedis.exceptions.JedisException; -import redis.clients.jedis.util.RedisVersionUtil; /** * This test class is mostly a copy of {@link JedisSentinelPoolTest}. *

* This tests are only executed when the server/cluster is Redis 6 or more. */ +@SinceRedisVersion("6.0.0") public class ACLJedisSentinelPoolTest { private static final String MASTER_NAME = "aclmaster"; @@ -31,9 +32,10 @@ public class ACLJedisSentinelPoolTest { @BeforeClass public static void prepare() throws Exception { EndpointConfig endpoint = HostAndPorts.getRedisEndpoint("standalone2-primary"); - org.junit.Assume.assumeTrue("Not running ACL test on this version of Redis", - RedisVersionUtil.checkRedisMajorVersionNumber(6, endpoint)); } + @ClassRule + public static RedisVersionRule versionRule = new RedisVersionRule(HostAndPorts.getRedisEndpoint("standalone2-primary")); + @Before public void setUp() throws Exception { diff --git a/src/test/java/redis/clients/jedis/ACLJedisTest.java b/src/test/java/redis/clients/jedis/ACLJedisTest.java index d1ef40b95b..1d72195a5d 100644 --- a/src/test/java/redis/clients/jedis/ACLJedisTest.java +++ b/src/test/java/redis/clients/jedis/ACLJedisTest.java @@ -1,6 +1,7 @@ package redis.clients.jedis; import static org.junit.Assert.assertEquals; +import static io.redis.test.utils.RedisVersionUtil.getRedisVersion; import java.net.URISyntaxException; import org.junit.BeforeClass; @@ -9,7 +10,7 @@ import org.junit.runners.Parameterized; import redis.clients.jedis.commands.jedis.JedisCommandsTestBase; -import redis.clients.jedis.util.RedisVersionUtil; +import io.redis.test.utils.RedisVersion; /** * This test class is a copy of {@link JedisTest}. @@ -28,7 +29,7 @@ public class ACLJedisTest extends JedisCommandsTestBase { @BeforeClass public static void prepare() throws Exception { org.junit.Assume.assumeTrue("Not running ACL test on this version of Redis", - RedisVersionUtil.checkRedisMajorVersionNumber(6, endpoint)); + getRedisVersion(endpoint).isGreaterThanOrEqualTo(RedisVersion.of("6.0.0"))); } public ACLJedisTest(RedisProtocol redisProtocol) { diff --git a/src/test/java/redis/clients/jedis/EndpointConfig.java b/src/test/java/redis/clients/jedis/EndpointConfig.java index 68f927be0e..cae5a2139c 100644 --- a/src/test/java/redis/clients/jedis/EndpointConfig.java +++ b/src/test/java/redis/clients/jedis/EndpointConfig.java @@ -50,6 +50,10 @@ public String getHost() { public int getPort() { return getHostAndPort().getPort(); } + public Boolean isTls() { + return tls; + } + public int getBdbId() { return bdbId; } diff --git a/src/test/java/redis/clients/jedis/JedisClusterTest.java b/src/test/java/redis/clients/jedis/JedisClusterTest.java index f3dfe630e5..f29e735617 100644 --- a/src/test/java/redis/clients/jedis/JedisClusterTest.java +++ b/src/test/java/redis/clients/jedis/JedisClusterTest.java @@ -26,6 +26,7 @@ import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; +import io.redis.test.annotations.EnabledOnCommand; import org.apache.commons.pool2.impl.GenericObjectPoolConfig; import org.junit.Test; @@ -667,7 +668,8 @@ public void testInvalidStartNodeNotAdded() { } @Test - public void clusterLinks2() throws InterruptedException { + @EnabledOnCommand(value = "CLUSTER", subCommand = "LINKS") + public void clusterLinks2() { Set mapKeys = new HashSet<>(Arrays.asList("direction", "node", "create-time", "events", "send-buffer-allocated", "send-buffer-used")); diff --git a/src/test/java/redis/clients/jedis/JedisClusterTestBase.java b/src/test/java/redis/clients/jedis/JedisClusterTestBase.java index bb6656a812..7d9813b760 100644 --- a/src/test/java/redis/clients/jedis/JedisClusterTestBase.java +++ b/src/test/java/redis/clients/jedis/JedisClusterTestBase.java @@ -2,8 +2,11 @@ import static redis.clients.jedis.Protocol.CLUSTER_HASHSLOTS; +import io.redis.test.utils.EnabledOnCommandRule; +import io.redis.test.utils.RedisVersionRule; import org.junit.After; import org.junit.Before; +import org.junit.Rule; import redis.clients.jedis.args.ClusterResetType; import redis.clients.jedis.util.JedisClusterTestUtil; @@ -23,6 +26,12 @@ public abstract class JedisClusterTestBase { protected static final String LOCAL_IP = "127.0.0.1"; + @Rule + public RedisVersionRule versionRule = new RedisVersionRule(nodeInfo1,DefaultJedisClientConfig.builder().password("cluster").build()); + @Rule + public EnabledOnCommandRule enabledOnCommandRule = new EnabledOnCommandRule(nodeInfo1, DefaultJedisClientConfig.builder().password("cluster").build()); + + @Before public void setUp() throws InterruptedException { node1 = new Jedis(nodeInfo1); diff --git a/src/test/java/redis/clients/jedis/JedisTest.java b/src/test/java/redis/clients/jedis/JedisTest.java index 560c5a0577..adab12acf4 100644 --- a/src/test/java/redis/clients/jedis/JedisTest.java +++ b/src/test/java/redis/clients/jedis/JedisTest.java @@ -16,6 +16,7 @@ import java.util.HashMap; import java.util.Map; +import io.redis.test.annotations.SinceRedisVersion; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; @@ -308,6 +309,7 @@ public void checkDisconnectOnQuit() { } @Test + @SinceRedisVersion(value = "7.2.0", message = "see https://redis.io/docs/latest/commands/client-setinfo/") public void clientSetInfoDefault() { try (Jedis jedis = new Jedis(endpoint.getHostAndPort(), endpoint.getClientConfigBuilder() .clientSetInfoConfig(ClientSetInfoConfig.DEFAULT).build())) { @@ -319,6 +321,7 @@ public void clientSetInfoDefault() { } @Test + @SinceRedisVersion(value = "7.2.0", message = "@see https://redis.io/docs/latest/commands/client-setinfo/") public void clientSetInfoDisabled() { try (Jedis jedis = new Jedis(endpoint.getHostAndPort(), endpoint.getClientConfigBuilder() .clientSetInfoConfig(ClientSetInfoConfig.DISABLED).build())) { @@ -330,6 +333,7 @@ public void clientSetInfoDisabled() { } @Test + @SinceRedisVersion(value = "7.2.0", message = "@see https://redis.io/docs/latest/commands/client-setinfo/") public void clientSetInfoLibNameSuffix() { final String libNameSuffix = "for-redis"; ClientSetInfoConfig setInfoConfig = ClientSetInfoConfig.withLibNameSuffix(libNameSuffix); diff --git a/src/test/java/redis/clients/jedis/SSLACLJedisClusterTest.java b/src/test/java/redis/clients/jedis/SSLACLJedisClusterTest.java index 16eb63c0e0..d0ab1cfd22 100644 --- a/src/test/java/redis/clients/jedis/SSLACLJedisClusterTest.java +++ b/src/test/java/redis/clients/jedis/SSLACLJedisClusterTest.java @@ -2,102 +2,147 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; +import static redis.clients.jedis.util.TlsUtil.*; import java.util.Collections; import java.util.Map; import javax.net.ssl.HostnameVerifier; import javax.net.ssl.SSLParameters; -import javax.net.ssl.SSLSession; -import javax.net.ssl.SSLSocketFactory; -import org.junit.Assert; -import org.junit.BeforeClass; -import org.junit.Test; +import io.redis.test.annotations.SinceRedisVersion; +import io.redis.test.utils.EnabledOnCommandRule; +import io.redis.test.utils.RedisVersion; +import io.redis.test.utils.RedisVersionRule; +import io.redis.test.utils.RedisVersionUtil; +import org.junit.*; +import redis.clients.jedis.util.TlsUtil; import redis.clients.jedis.exceptions.JedisClusterOperationException; -import redis.clients.jedis.SSLJedisTest.BasicHostnameVerifier; -import redis.clients.jedis.util.RedisVersionUtil; +@SinceRedisVersion("6.0.0") public class SSLACLJedisClusterTest extends JedisClusterTestBase { private static final int DEFAULT_REDIRECTIONS = 5; private static final ConnectionPoolConfig DEFAULT_POOL_CONFIG = new ConnectionPoolConfig(); - private final HostAndPortMapper hostAndPortMap = (HostAndPort hostAndPort) -> { - String host = hostAndPort.getHost(); - int port = hostAndPort.getPort(); - if (host.equals("127.0.0.1")) { - host = "localhost"; - port = port + 1000; - } - return new HostAndPort(host, port); - }; - // don't map IP addresses so that we try to connect with host 127.0.0.1 - private final HostAndPortMapper portMap = (HostAndPort hostAndPort) -> { - if ("localhost".equals(hostAndPort.getHost())) { - return hostAndPort; - } - return new HostAndPort(hostAndPort.getHost(), hostAndPort.getPort() + 1000); - }; + // legacy test env bootstrap uses stunnel causing redis server to report non-tls port instead tls one + // containerised test env enables tls directly on Redis nodes and in this case tls_port is correctly reported + // TODO : remove stunnel from legacy env + // static int tlsPortOffset = 0; + HostAndPortMapper hostAndPortMap = (hostAndPort) -> { + String host = hostAndPort.getHost(); + int port = hostAndPort.getPort(); + + if ("127.0.0.1".equals(host)) { + host = "localhost"; + //port += tlsPortOffset; // Apply the port offset + } + + return new HostAndPort(host, port); + }; + + // don't map IP addresses so that we try to connect with host 127.0.0.1 + HostAndPortMapper portMap = (hostAndPort) -> { + if ("localhost".equals(hostAndPort.getHost())) { + return hostAndPort; + } + return new HostAndPort(hostAndPort.getHost(), hostAndPort.getPort() /* + tlsPortOffset */); + }; + @BeforeClass public static void prepare() { - // We need to set up certificates first before connecting to the endpoint with enabled TLS - SSLJedisTest.setupTrustStore(); - - // TODO(imalinovskyi): Remove hardcoded connection settings - // once this test is refactored to support RE - org.junit.Assume.assumeTrue("Not running ACL test on this version of Redis", - RedisVersionUtil.checkRedisMajorVersionNumber(6, - new EndpointConfig(new HostAndPort("localhost", 8379), - "default", "cluster", true))); + TlsUtil.createAndSaveEnvTruststore("cluster-unbound", "changeit"); } @Test public void testSSLDiscoverNodesAutomatically() { + DefaultJedisClientConfig config = DefaultJedisClientConfig.builder() + .user("default") + .password("cluster") + .sslSocketFactory(sslSocketFactoryForEnv("cluster-unbound")) + .ssl(true) + .hostAndPortMapper(hostAndPortMap) + .build(); + try (JedisCluster jc = new JedisCluster(Collections.singleton(new HostAndPort("localhost", 8379)), - DefaultJedisClientConfig.builder().user("default").password("cluster").ssl(true) - .hostAndPortMapper(hostAndPortMap).build(), DEFAULT_REDIRECTIONS, DEFAULT_POOL_CONFIG)) { - Map clusterNodes = jc.getClusterNodes(); + config, + DEFAULT_REDIRECTIONS, DEFAULT_POOL_CONFIG) + ) { + Map clusterNodes = jc.getClusterNodes(); assertEquals(3, clusterNodes.size()); - assertTrue(clusterNodes.containsKey("127.0.0.1:7379")); - assertTrue(clusterNodes.containsKey("127.0.0.1:7380")); - assertTrue(clusterNodes.containsKey("127.0.0.1:7381")); + + /** + * In versions prior to Redis 7.x, Redis does not natively support automatic port switching between TLS and non-TLS ports for CLUSTER SLOTS. + * When using Redis 6.2.16 in a cluster mode with TLS, CLUSTER command returns the regular (non-TLS) port rather than the TLS port. + */ + if (RedisVersionUtil.getRedisVersion(jc.getConnectionFromSlot(0)).isLessThanOrEqualTo(RedisVersion.V7_0_0)) { + assertTrue(clusterNodes.containsKey("127.0.0.1:7379")); + assertTrue(clusterNodes.containsKey("127.0.0.1:7380")); + assertTrue(clusterNodes.containsKey("127.0.0.1:7381")); + } else { + assertTrue(clusterNodes.containsKey("127.0.0.1:8379")); + assertTrue(clusterNodes.containsKey("127.0.0.1:8380")); + assertTrue(clusterNodes.containsKey("127.0.0.1:8381")); + } jc.get("foo"); } try (JedisCluster jc2 = new JedisCluster(new HostAndPort("localhost", 8379), - DefaultJedisClientConfig.builder().user("default").password("cluster").ssl(true) - .hostAndPortMapper(hostAndPortMap).build(), DEFAULT_REDIRECTIONS, DEFAULT_POOL_CONFIG)) { + DefaultJedisClientConfig.builder() + .user("default") + .password("cluster") + .sslSocketFactory(sslSocketFactoryForEnv("cluster-unbound")) + .ssl(true) + .hostAndPortMapper(hostAndPortMap) + .build(), DEFAULT_REDIRECTIONS, DEFAULT_POOL_CONFIG)) { Map clusterNodes = jc2.getClusterNodes(); assertEquals(3, clusterNodes.size()); - assertTrue(clusterNodes.containsKey("127.0.0.1:7379")); - assertTrue(clusterNodes.containsKey("127.0.0.1:7380")); - assertTrue(clusterNodes.containsKey("127.0.0.1:7381")); + assertTrue(clusterNodes.containsKey("127.0.0.1:8379")); + assertTrue(clusterNodes.containsKey("127.0.0.1:8380")); + assertTrue(clusterNodes.containsKey("127.0.0.1:8381")); jc2.get("foo"); } } + @Test public void testSSLWithoutPortMap() { try (JedisCluster jc = new JedisCluster(Collections.singleton(new HostAndPort("localhost", 8379)), - DefaultJedisClientConfig.builder().user("default").password("cluster").ssl(true).build(), + DefaultJedisClientConfig.builder() + .user("default") + .password("cluster") + .sslSocketFactory(sslSocketFactoryForEnv("cluster-unbound")) + .ssl(true).build(), DEFAULT_REDIRECTIONS, DEFAULT_POOL_CONFIG)) { -// Map clusterNodes = jc.getClusterNodes(); Map clusterNodes = jc.getClusterNodes(); assertEquals(3, clusterNodes.size()); - assertTrue(clusterNodes.containsKey("127.0.0.1:7379")); - assertTrue(clusterNodes.containsKey("127.0.0.1:7380")); - assertTrue(clusterNodes.containsKey("127.0.0.1:7381")); + /** + * In versions prior to Redis 7.x, Redis does not natively support automatic port switching between TLS and non-TLS ports for CLUSTER SLOTS. + * When using Redis 6.2.16 in a cluster mode with TLS, CLUSTER command returns the regular (non-TLS) port rather than the TLS port. + */ + if (RedisVersionUtil.getRedisVersion(jc.getConnectionFromSlot(0)).isLessThanOrEqualTo(RedisVersion.V7_0_0)) { + assertTrue(clusterNodes.containsKey("127.0.0.1:7379")); + assertTrue(clusterNodes.containsKey("127.0.0.1:7380")); + assertTrue(clusterNodes.containsKey("127.0.0.1:7381")); + } else { + assertTrue(clusterNodes.containsKey("127.0.0.1:8379")); + assertTrue(clusterNodes.containsKey("127.0.0.1:8380")); + assertTrue(clusterNodes.containsKey("127.0.0.1:8381")); + } } } @Test public void connectByIpAddress() { - try (JedisCluster jc = new JedisCluster(new HostAndPort("127.0.0.1", 7379), - DefaultJedisClientConfig.builder().user("default").password("cluster").ssl(true) - .hostAndPortMapper(hostAndPortMap).build(), + try (JedisCluster jc = new JedisCluster(new HostAndPort("127.0.0.1", 8379), + DefaultJedisClientConfig.builder() + .user("default") + .password("cluster") + .sslSocketFactory(sslSocketFactoryForEnv("cluster-unbound")) + .ssl(true) + .hostAndPortMapper(hostAndPortMap).build(), DEFAULT_REDIRECTIONS, DEFAULT_POOL_CONFIG)) { jc.get("foo"); } @@ -109,12 +154,16 @@ public void connectToNodesFailsWithSSLParametersAndNoHostMapping() { sslParameters.setEndpointIdentificationAlgorithm("HTTPS"); try (JedisCluster jc = new JedisCluster(new HostAndPort("localhost", 8379), - DefaultJedisClientConfig.builder().user("default").password("cluster").ssl(true) - .sslParameters(sslParameters).hostAndPortMapper(portMap).build(), DEFAULT_REDIRECTIONS, - DEFAULT_POOL_CONFIG)) { + DefaultJedisClientConfig.builder() + .user("default") + .password("cluster") + .sslSocketFactory(sslSocketFactoryForEnv("cluster-unbound")) + .ssl(true) + .sslParameters(sslParameters) + .hostAndPortMapper(portMap) + .build(), DEFAULT_REDIRECTIONS, DEFAULT_POOL_CONFIG)) { jc.get("foo"); Assert.fail("It should fail after all cluster attempts."); -// } catch (JedisClusterMaxAttemptsException e) { } catch (JedisClusterOperationException e) { // initial connection to localhost works, but subsequent connections to nodes use 127.0.0.1 // and fail hostname verification @@ -128,8 +177,13 @@ public void connectToNodesSucceedsWithSSLParametersAndHostMapping() { sslParameters.setEndpointIdentificationAlgorithm("HTTPS"); try (JedisCluster jc = new JedisCluster(new HostAndPort("localhost", 8379), - DefaultJedisClientConfig.builder().user("default").password("cluster").ssl(true) - .sslParameters(sslParameters).hostAndPortMapper(hostAndPortMap).build(), + DefaultJedisClientConfig.builder() + .user("default") + .password("cluster") + .sslSocketFactory(sslSocketFactoryForEnv("cluster-unbound")) + .ssl(true) + .sslParameters(sslParameters) + .hostAndPortMapper(hostAndPortMap).build(), DEFAULT_REDIRECTIONS, DEFAULT_POOL_CONFIG)) { jc.get("foo"); } @@ -141,30 +195,33 @@ public void connectByIpAddressFailsWithSSLParameters() { sslParameters.setEndpointIdentificationAlgorithm("HTTPS"); try (JedisCluster jc = new JedisCluster(new HostAndPort("127.0.0.1", 8379), - DefaultJedisClientConfig.builder().user("default").password("cluster").ssl(true) - .sslParameters(sslParameters).hostAndPortMapper(hostAndPortMap).build(), + DefaultJedisClientConfig.builder() + .user("default") + .password("cluster") + .sslSocketFactory(sslSocketFactoryForEnv("cluster-unbound")) + .ssl(true) + .sslParameters(sslParameters).hostAndPortMapper(hostAndPortMap).build(), DEFAULT_REDIRECTIONS, DEFAULT_POOL_CONFIG)) { -// jc.get("key"); -// Assert.fail("There should be no reachable node in cluster."); -//// } catch (JedisNoReachableClusterNodeException e) { } catch (JedisClusterOperationException e) { -// assertEquals("No reachable node in cluster.", e.getMessage()); assertEquals("Could not initialize cluster slots cache.", e.getMessage()); } } @Test public void connectWithCustomHostNameVerifier() { - HostnameVerifier hostnameVerifier = new BasicHostnameVerifier(); - HostnameVerifier localhostVerifier = new LocalhostVerifier(); + HostnameVerifier hostnameVerifier = new TlsUtil.BasicHostnameVerifier(); + HostnameVerifier localhostVerifier = new TlsUtil.LocalhostVerifier(); try (JedisCluster jc = new JedisCluster(new HostAndPort("localhost", 8379), - DefaultJedisClientConfig.builder().user("default").password("cluster").ssl(true) - .hostnameVerifier(hostnameVerifier).hostAndPortMapper(portMap).build(), + DefaultJedisClientConfig.builder() + .user("default") + .password("cluster") + .sslSocketFactory(sslSocketFactoryForEnv("cluster-unbound")) + .ssl(true) + .hostnameVerifier(hostnameVerifier).hostAndPortMapper(portMap).build(), DEFAULT_REDIRECTIONS, DEFAULT_POOL_CONFIG)) { jc.get("foo"); Assert.fail("It should fail after all cluster attempts."); -// } catch (JedisClusterMaxAttemptsException e) { } catch (JedisClusterOperationException e) { // initial connection made with 'localhost' but subsequent connections to nodes use 127.0.0.1 // which causes custom hostname verification to fail @@ -172,34 +229,41 @@ public void connectWithCustomHostNameVerifier() { } try (JedisCluster jc2 = new JedisCluster(new HostAndPort("127.0.0.1", 8379), - DefaultJedisClientConfig.builder().user("default").password("cluster").ssl(true) - .hostnameVerifier(hostnameVerifier).hostAndPortMapper(portMap).build(), + DefaultJedisClientConfig.builder() + .user("default") + .password("cluster") + .sslSocketFactory(sslSocketFactoryForEnv("cluster-unbound")) + .ssl(true) + .hostnameVerifier(hostnameVerifier).hostAndPortMapper(portMap).build(), DEFAULT_REDIRECTIONS, DEFAULT_POOL_CONFIG)) { -// jc2.get("key"); -// Assert.fail("There should be no reachable node in cluster."); -//// } catch (JedisNoReachableClusterNodeException e) { } catch (JedisClusterOperationException e) { // JedisNoReachableClusterNodeException exception occurs from not being able to connect since // the socket factory fails the hostname verification -// assertEquals("No reachable node in cluster.", e.getMessage()); assertEquals("Could not initialize cluster slots cache.", e.getMessage()); } try (JedisCluster jc3 = new JedisCluster(new HostAndPort("localhost", 8379), - DefaultJedisClientConfig.builder().user("default").password("cluster").ssl(true) - .hostnameVerifier(localhostVerifier).hostAndPortMapper(portMap).build(), + DefaultJedisClientConfig.builder() + .user("default") + .password("cluster") + .sslSocketFactory(sslSocketFactoryForEnv("cluster-unbound")) + .ssl(true) + .hostnameVerifier(localhostVerifier) + .hostAndPortMapper(portMap).build(), DEFAULT_REDIRECTIONS, DEFAULT_POOL_CONFIG)) { jc3.get("foo"); } } @Test - public void connectWithCustomSocketFactory() throws Exception { - final SSLSocketFactory sslSocketFactory = SSLJedisTest.createTrustStoreSslSocketFactory(); - + public void connectWithCustomSocketFactory() { try (JedisCluster jc = new JedisCluster(new HostAndPort("localhost", 8379), - DefaultJedisClientConfig.builder().user("default").password("cluster").ssl(true) - .sslSocketFactory(sslSocketFactory).hostAndPortMapper(portMap).build(), + DefaultJedisClientConfig.builder() + .user("default") + .password("cluster") + .sslSocketFactory(sslSocketFactoryForEnv("cluster-unbound")) + .ssl(true) + .hostAndPortMapper(portMap).build(), DEFAULT_REDIRECTIONS, DEFAULT_POOL_CONFIG)) { assertEquals(3, jc.getClusterNodes().size()); } @@ -207,16 +271,14 @@ public void connectWithCustomSocketFactory() throws Exception { @Test public void connectWithEmptyTrustStore() throws Exception { - final SSLSocketFactory sslSocketFactory = SSLJedisTest.createTrustNoOneSslSocketFactory(); - try (JedisCluster jc = new JedisCluster(new HostAndPort("localhost", 8379), - DefaultJedisClientConfig.builder().user("default").password("cluster").ssl(true) - .sslSocketFactory(sslSocketFactory).build(), DEFAULT_REDIRECTIONS, DEFAULT_POOL_CONFIG)) { -// jc.get("key"); -// Assert.fail("There should be no reachable node in cluster."); -//// } catch (JedisNoReachableClusterNodeException e) { + DefaultJedisClientConfig.builder() + .user("default") + .password("cluster") + .sslSocketFactory(createTrustNoOneSslSocketFactory()) + .ssl(true) + .build(), DEFAULT_REDIRECTIONS, DEFAULT_POOL_CONFIG)) { } catch (JedisClusterOperationException e) { -// assertEquals("No reachable node in cluster.", e.getMessage()); assertEquals("Could not initialize cluster slots cache.", e.getMessage()); } } @@ -228,21 +290,11 @@ public void defaultHostAndPortUsedIfMapReturnsNull() { try (JedisCluster jc = new JedisCluster(new HostAndPort("localhost", 7379), DefaultJedisClientConfig.builder().user("default").password("cluster").ssl(false) .hostAndPortMapper(nullHostAndPortMap).build(), DEFAULT_REDIRECTIONS, DEFAULT_POOL_CONFIG)) { - Map clusterNodes = jc.getClusterNodes(); + Map clusterNodes = jc.getClusterNodes(); assertEquals(3, clusterNodes.size()); assertTrue(clusterNodes.containsKey("127.0.0.1:7379")); assertTrue(clusterNodes.containsKey("127.0.0.1:7380")); assertTrue(clusterNodes.containsKey("127.0.0.1:7381")); } } - - public class LocalhostVerifier extends BasicHostnameVerifier { - @Override - public boolean verify(String hostname, SSLSession session) { - if (hostname.equals("127.0.0.1")) { - hostname = "localhost"; - } - return super.verify(hostname, session); - } - } } diff --git a/src/test/java/redis/clients/jedis/SSLACLJedisTest.java b/src/test/java/redis/clients/jedis/SSLACLJedisTest.java index 8151729e6e..d90965aa0f 100644 --- a/src/test/java/redis/clients/jedis/SSLACLJedisTest.java +++ b/src/test/java/redis/clients/jedis/SSLACLJedisTest.java @@ -1,20 +1,17 @@ package redis.clients.jedis; +import static io.redis.test.utils.RedisVersionUtil.getRedisVersion; import static org.junit.Assert.*; +import static redis.clients.jedis.util.TlsUtil.*; -import java.io.FileInputStream; -import java.io.InputStream; -import java.security.InvalidAlgorithmParameterException; -import java.security.KeyStore; -import java.security.SecureRandom; -import java.security.cert.X509Certificate; -import javax.net.ssl.*; +import java.nio.file.Path; +import org.junit.AfterClass; +import io.redis.test.utils.RedisVersion; +import redis.clients.jedis.util.TlsUtil; import org.junit.BeforeClass; import org.junit.Test; -import redis.clients.jedis.util.RedisVersionUtil; - /** * This test class is a copy of {@link SSLJedisTest}. *

@@ -26,18 +23,28 @@ public class SSLACLJedisTest { protected static final EndpointConfig endpointWithDefaultUser = HostAndPorts.getRedisEndpoint("standalone0-tls"); + @BeforeClass public static void prepare() { - // We need to set up certificates first before connecting to the endpoint with enabled TLS - SSLJedisTest.setupTrustStore(); + Path trusStorePath = createAndSaveEnvTruststore("redis1-2-5-10-sentinel", "changeit"); + TlsUtil.setCustomTrustStore(trusStorePath, "changeit"); // Use to check if the ACL test should be ran. ACL are available only in 6.0 and later org.junit.Assume.assumeTrue("Not running ACL test on this version of Redis", - RedisVersionUtil.checkRedisMajorVersionNumber(6, endpoint)); + getRedisVersion(endpoint).isGreaterThanOrEqualTo(RedisVersion.V6_0_0)); + } + + @AfterClass + public static void teardownTrustStore() { + TlsUtil.restoreOriginalTrustStore(); } @Test public void connectWithSsl() { - try (Jedis jedis = new Jedis(endpoint.getHost(), endpoint.getPort(), true)) { + try (Jedis jedis = new Jedis(endpoint.getHost(), endpoint.getPort(), + DefaultJedisClientConfig.builder() + .sslSocketFactory(sslSocketFactoryForEnv("redis1-2-5-10-sentinel")) + .ssl(true) + .build())) { jedis.auth(endpoint.getUsername(), endpoint.getPassword()); assertEquals("PONG", jedis.ping()); } @@ -46,7 +53,9 @@ public void connectWithSsl() { @Test public void connectWithConfig() { try (Jedis jedis = new Jedis(endpoint.getHostAndPort(), - DefaultJedisClientConfig.builder().ssl(true).build())) { + DefaultJedisClientConfig.builder() + .sslSocketFactory(sslSocketFactoryForEnv("redis1-2-5-10-sentinel")) + .ssl(true).build())) { jedis.auth(endpoint.getUsername(), endpoint.getPassword()); assertEquals("PONG", jedis.ping()); } @@ -69,84 +78,18 @@ public void connectWithUrl() { public void connectWithUri() { // The "rediss" scheme instructs jedis to open a SSL/TLS connection. try (Jedis jedis = new Jedis( - endpointWithDefaultUser.getURIBuilder().defaultCredentials().build())) { + endpointWithDefaultUser.getURIBuilder() + .defaultCredentials().build(), + DefaultJedisClientConfig.builder() + .sslSocketFactory(sslSocketFactoryForEnv("redis1-2-5-10-sentinel")) + .build())) { assertEquals("PONG", jedis.ping()); } - try (Jedis jedis = new Jedis(endpoint.getURIBuilder().defaultCredentials().build())) { + try (Jedis jedis = new Jedis(endpoint.getURIBuilder().defaultCredentials().build(), + DefaultJedisClientConfig.builder() + .sslSocketFactory(sslSocketFactoryForEnv("redis1-2-5-10-sentinel")) + .build())) { assertEquals("PONG", jedis.ping()); } } - - /** - * Creates an SSLSocketFactory that trusts all certificates in truststore.jceks. - */ - static SSLSocketFactory createTrustStoreSslSocketFactory() throws Exception { - - KeyStore trustStore = KeyStore.getInstance("jceks"); - try (InputStream inputStream = new FileInputStream("src/test/resources/truststore.jceks")) { - trustStore.load(inputStream, null); - } - - TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance("PKIX"); - trustManagerFactory.init(trustStore); - TrustManager[] trustManagers = trustManagerFactory.getTrustManagers(); - - SSLContext sslContext = SSLContext.getInstance("TLS"); - sslContext.init(null, trustManagers, new SecureRandom()); - return sslContext.getSocketFactory(); - } - - /** - * Creates an SSLSocketFactory with a trust manager that does not trust any certificates. - */ - static SSLSocketFactory createTrustNoOneSslSocketFactory() throws Exception { - TrustManager[] unTrustManagers = new TrustManager[] { new X509TrustManager() { - public X509Certificate[] getAcceptedIssuers() { - return new X509Certificate[0]; - } - - public void checkClientTrusted(X509Certificate[] chain, String authType) { - throw new RuntimeException(new InvalidAlgorithmParameterException()); - } - - public void checkServerTrusted(X509Certificate[] chain, String authType) { - throw new RuntimeException(new InvalidAlgorithmParameterException()); - } - } }; - SSLContext sslContext = SSLContext.getInstance("TLS"); - sslContext.init(null, unTrustManagers, new SecureRandom()); - return sslContext.getSocketFactory(); - } - - /** - * Very basic hostname verifier implementation for testing. NOT recommended for production. - */ - static class BasicHostnameVerifier implements HostnameVerifier { - - private static final String COMMON_NAME_RDN_PREFIX = "CN="; - - @Override - public boolean verify(String hostname, SSLSession session) { - X509Certificate peerCertificate; - try { - peerCertificate = (X509Certificate) session.getPeerCertificates()[0]; - } catch (SSLPeerUnverifiedException e) { - throw new IllegalStateException("The session does not contain a peer X.509 certificate.", e); - } - String peerCertificateCN = getCommonName(peerCertificate); - return hostname.equals(peerCertificateCN); - } - - private String getCommonName(X509Certificate peerCertificate) { - String subjectDN = peerCertificate.getSubjectDN().getName(); - String[] dnComponents = subjectDN.split(","); - for (String dnComponent : dnComponents) { - dnComponent = dnComponent.trim(); - if (dnComponent.startsWith(COMMON_NAME_RDN_PREFIX)) { - return dnComponent.substring(COMMON_NAME_RDN_PREFIX.length()); - } - } - throw new IllegalArgumentException("The certificate has no common name."); - } - } } diff --git a/src/test/java/redis/clients/jedis/SSLJedisClusterTest.java b/src/test/java/redis/clients/jedis/SSLJedisClusterTest.java index f4763fe875..3abf60ef15 100644 --- a/src/test/java/redis/clients/jedis/SSLJedisClusterTest.java +++ b/src/test/java/redis/clients/jedis/SSLJedisClusterTest.java @@ -2,20 +2,22 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; +import static redis.clients.jedis.util.TlsUtil.*; import java.util.Collections; import java.util.Map; import javax.net.ssl.HostnameVerifier; import javax.net.ssl.SSLParameters; -import javax.net.ssl.SSLSession; import javax.net.ssl.SSLSocketFactory; +import io.redis.test.utils.RedisVersion; +import io.redis.test.utils.RedisVersionUtil; +import redis.clients.jedis.util.TlsUtil; import org.junit.Assert; import org.junit.BeforeClass; import org.junit.Test; import redis.clients.jedis.exceptions.JedisClusterOperationException; -import redis.clients.jedis.SSLJedisTest.BasicHostnameVerifier; public class SSLJedisClusterTest extends JedisClusterTestBase { @@ -27,7 +29,7 @@ public class SSLJedisClusterTest extends JedisClusterTestBase { int port = hostAndPort.getPort(); if (host.equals("127.0.0.1")) { host = "localhost"; - port = port + 1000; + //port = port + 1000; } return new HostAndPort(host, port); }; @@ -37,38 +39,52 @@ public class SSLJedisClusterTest extends JedisClusterTestBase { if ("localhost".equals(hostAndPort.getHost())) { return hostAndPort; } - return new HostAndPort(hostAndPort.getHost(), hostAndPort.getPort() + 1000); + //return new HostAndPort(hostAndPort.getHost(), hostAndPort.getPort() + 1000); + return new HostAndPort(hostAndPort.getHost(), hostAndPort.getPort() ); }; @BeforeClass public static void prepare() { - SSLJedisTest.setupTrustStore(); // set up trust store for SSL tests + TlsUtil.createAndSaveEnvTruststore("cluster-unbound", "changeit"); } @Test public void testSSLDiscoverNodesAutomatically() { try (JedisCluster jc = new JedisCluster(Collections.singleton(new HostAndPort("localhost", 8379)), - DefaultJedisClientConfig.builder().password("cluster").ssl(true) - .hostAndPortMapper(hostAndPortMap).build(), DEFAULT_REDIRECTIONS, DEFAULT_POOL_CONFIG)) { -// Map clusterNodes = jc.getClusterNodes(); + DefaultJedisClientConfig.builder().password("cluster") + .sslSocketFactory(sslSocketFactoryForEnv("cluster-unbound")) + .ssl(true) + .hostAndPortMapper(hostAndPortMap).build(), DEFAULT_REDIRECTIONS, DEFAULT_POOL_CONFIG)) { Map clusterNodes = jc.getClusterNodes(); assertEquals(3, clusterNodes.size()); - assertTrue(clusterNodes.containsKey("127.0.0.1:7379")); - assertTrue(clusterNodes.containsKey("127.0.0.1:7380")); - assertTrue(clusterNodes.containsKey("127.0.0.1:7381")); + /** + * In versions prior to Redis 7.x, Redis does not natively support automatic port switching between TLS and non-TLS ports for CLUSTER SLOTS. + * When using Redis 6.2.16 in a cluster mode with TLS, CLUSTER command returns the regular (non-TLS) port rather than the TLS port. + */ + if (RedisVersionUtil.getRedisVersion(jc.getConnectionFromSlot(0)).isLessThanOrEqualTo(RedisVersion.V7_0_0)) { + assertTrue(clusterNodes.containsKey("127.0.0.1:7379")); + assertTrue(clusterNodes.containsKey("127.0.0.1:7380")); + assertTrue(clusterNodes.containsKey("127.0.0.1:7381")); + } else { + assertTrue(clusterNodes.containsKey("127.0.0.1:8379")); + assertTrue(clusterNodes.containsKey("127.0.0.1:8380")); + assertTrue(clusterNodes.containsKey("127.0.0.1:8381")); + } jc.get("foo"); } try (JedisCluster jc2 = new JedisCluster(new HostAndPort("localhost", 8379), - DefaultJedisClientConfig.builder().password("cluster").ssl(true) - .hostAndPortMapper(hostAndPortMap).build(), DEFAULT_REDIRECTIONS, DEFAULT_POOL_CONFIG)) { -// Map clusterNodes = jc2.getClusterNodes(); + DefaultJedisClientConfig.builder() + .password("cluster") + .sslSocketFactory(sslSocketFactoryForEnv("cluster-unbound")) + .ssl(true) + .hostAndPortMapper(hostAndPortMap).build(), DEFAULT_REDIRECTIONS, DEFAULT_POOL_CONFIG)) { Map clusterNodes = jc2.getClusterNodes(); assertEquals(3, clusterNodes.size()); - assertTrue(clusterNodes.containsKey("127.0.0.1:7379")); - assertTrue(clusterNodes.containsKey("127.0.0.1:7380")); - assertTrue(clusterNodes.containsKey("127.0.0.1:7381")); + assertTrue(clusterNodes.containsKey("127.0.0.1:8379")); + assertTrue(clusterNodes.containsKey("127.0.0.1:8380")); + assertTrue(clusterNodes.containsKey("127.0.0.1:8381")); jc2.get("foo"); } } @@ -76,22 +92,37 @@ public void testSSLDiscoverNodesAutomatically() { @Test public void testSSLWithoutPortMap() { try (JedisCluster jc = new JedisCluster(Collections.singleton(new HostAndPort("localhost", 8379)), - DefaultJedisClientConfig.builder().password("cluster").ssl(true).build(), - DEFAULT_REDIRECTIONS, DEFAULT_POOL_CONFIG)) { -// Map clusterNodes = jc.getClusterNodes(); + DefaultJedisClientConfig.builder() + .password("cluster") + .sslSocketFactory(sslSocketFactoryForEnv("cluster-unbound")) + .ssl(true).build(), + DEFAULT_REDIRECTIONS, DEFAULT_POOL_CONFIG)) { Map clusterNodes = jc.getClusterNodes(); assertEquals(3, clusterNodes.size()); - assertTrue(clusterNodes.containsKey("127.0.0.1:7379")); - assertTrue(clusterNodes.containsKey("127.0.0.1:7380")); - assertTrue(clusterNodes.containsKey("127.0.0.1:7381")); + /** + * In versions prior to Redis 7.x, Redis does not natively support automatic port switching between TLS and non-TLS ports for CLUSTER SLOTS. + * When using Redis 6.2.16 in a cluster mode with TLS, CLUSTER command returns the regular (non-TLS) port rather than the TLS port. + */ + if (RedisVersionUtil.getRedisVersion(jc.getConnectionFromSlot(0)).isLessThanOrEqualTo(RedisVersion.V7_0_0)) { + assertTrue(clusterNodes.containsKey("127.0.0.1:7379")); + assertTrue(clusterNodes.containsKey("127.0.0.1:7380")); + assertTrue(clusterNodes.containsKey("127.0.0.1:7381")); + } else { + assertTrue(clusterNodes.containsKey("127.0.0.1:8379")); + assertTrue(clusterNodes.containsKey("127.0.0.1:8380")); + assertTrue(clusterNodes.containsKey("127.0.0.1:8381")); + } } } @Test public void connectByIpAddress() { - try (JedisCluster jc = new JedisCluster(new HostAndPort("127.0.0.1", 7379), - DefaultJedisClientConfig.builder().password("cluster").ssl(true) - .hostAndPortMapper(hostAndPortMap).build(), + try (JedisCluster jc = new JedisCluster(new HostAndPort("127.0.0.1", 8379), + DefaultJedisClientConfig.builder() + .password("cluster") + .sslSocketFactory(sslSocketFactoryForEnv("cluster-unbound")) + .ssl(true) + .hostAndPortMapper(hostAndPortMap).build(), DEFAULT_REDIRECTIONS, DEFAULT_POOL_CONFIG)) { jc.get("foo"); } @@ -103,12 +134,14 @@ public void connectToNodesFailsWithSSLParametersAndNoHostMapping() { sslParameters.setEndpointIdentificationAlgorithm("HTTPS"); try (JedisCluster jc = new JedisCluster(new HostAndPort("localhost", 8379), - DefaultJedisClientConfig.builder().password("cluster").ssl(true) - .sslParameters(sslParameters).hostAndPortMapper(portMap).build(), DEFAULT_REDIRECTIONS, + DefaultJedisClientConfig.builder() + .password("cluster") + .sslSocketFactory(sslSocketFactoryForEnv("cluster-unbound")) + .ssl(true) + .sslParameters(sslParameters).hostAndPortMapper(portMap).build(), DEFAULT_REDIRECTIONS, DEFAULT_POOL_CONFIG)) { jc.get("foo"); Assert.fail("It should fail after all cluster attempts."); -// } catch (JedisClusterMaxAttemptsException e) { } catch (JedisClusterOperationException e) { // initial connection to localhost works, but subsequent connections to nodes use 127.0.0.1 // and fail hostname verification @@ -122,8 +155,12 @@ public void connectToNodesSucceedsWithSSLParametersAndHostMapping() { sslParameters.setEndpointIdentificationAlgorithm("HTTPS"); try (JedisCluster jc = new JedisCluster(new HostAndPort("localhost", 8379), - DefaultJedisClientConfig.builder().password("cluster").ssl(true) - .sslParameters(sslParameters).hostAndPortMapper(hostAndPortMap).build(), + DefaultJedisClientConfig.builder() + .password("cluster") + .sslSocketFactory(sslSocketFactoryForEnv("cluster-unbound")) + .ssl(true) + .sslParameters(sslParameters) + .hostAndPortMapper(hostAndPortMap).build(), DEFAULT_REDIRECTIONS, DEFAULT_POOL_CONFIG)) { jc.get("foo"); } @@ -135,26 +172,30 @@ public void connectByIpAddressFailsWithSSLParameters() { sslParameters.setEndpointIdentificationAlgorithm("HTTPS"); try (JedisCluster jc = new JedisCluster(new HostAndPort("127.0.0.1", 8379), - DefaultJedisClientConfig.builder().password("cluster").ssl(true) - .sslParameters(sslParameters).hostAndPortMapper(hostAndPortMap).build(), + DefaultJedisClientConfig.builder() + .password("cluster") + .ssl(true) + .sslParameters(sslParameters) + .sslSocketFactory(sslSocketFactoryForEnv("cluster-unbound")) + .hostAndPortMapper(hostAndPortMap).build(), DEFAULT_REDIRECTIONS, DEFAULT_POOL_CONFIG)) { -// jc.get("key"); -// Assert.fail("There should be no reachable node in cluster."); -//// } catch (JedisNoReachableClusterNodeException e) { } catch (JedisClusterOperationException e) { -// assertEquals("No reachable node in cluster.", e.getMessage()); assertEquals("Could not initialize cluster slots cache.", e.getMessage()); } } @Test public void connectWithCustomHostNameVerifier() { - HostnameVerifier hostnameVerifier = new BasicHostnameVerifier(); - HostnameVerifier localhostVerifier = new LocalhostVerifier(); + HostnameVerifier hostnameVerifier = new TlsUtil.BasicHostnameVerifier(); + HostnameVerifier localhostVerifier = new TlsUtil.LocalhostVerifier(); try (JedisCluster jc = new JedisCluster(new HostAndPort("localhost", 8379), - DefaultJedisClientConfig.builder().password("cluster").ssl(true) - .hostnameVerifier(hostnameVerifier).hostAndPortMapper(portMap).build(), + DefaultJedisClientConfig.builder() + .password("cluster") + .sslSocketFactory(sslSocketFactoryForEnv("cluster-unbound")) + .ssl(true) + .hostnameVerifier(hostnameVerifier) + .hostAndPortMapper(portMap).build(), DEFAULT_REDIRECTIONS, DEFAULT_POOL_CONFIG)) { jc.get("foo"); Assert.fail("It should fail after all cluster attempts."); @@ -166,22 +207,23 @@ public void connectWithCustomHostNameVerifier() { } try (JedisCluster jc2 = new JedisCluster(new HostAndPort("127.0.0.1", 8379), - DefaultJedisClientConfig.builder().password("cluster").ssl(true) - .hostnameVerifier(hostnameVerifier).hostAndPortMapper(portMap).build(), + DefaultJedisClientConfig.builder() + .password("cluster") + .sslSocketFactory(sslSocketFactoryForEnv("cluster-unbound")) + .ssl(true) + .hostnameVerifier(hostnameVerifier) + .hostAndPortMapper(portMap).build(), DEFAULT_REDIRECTIONS, DEFAULT_POOL_CONFIG)) { -// jc2.get("foo"); -// Assert.fail("There should be no reachable node in cluster."); -//// } catch (JedisNoReachableClusterNodeException e) { } catch (JedisClusterOperationException e) { - // JedisNoReachableClusterNodeException exception occurs from not being able to connect - // since the socket factory fails the hostname verification -// assertEquals("No reachable node in cluster.", e.getMessage()); assertEquals("Could not initialize cluster slots cache.", e.getMessage()); } try (JedisCluster jc3 = new JedisCluster(new HostAndPort("localhost", 8379), - DefaultJedisClientConfig.builder().password("cluster").ssl(true) - .hostnameVerifier(localhostVerifier).hostAndPortMapper(portMap).build(), + DefaultJedisClientConfig.builder() + .password("cluster") + .sslSocketFactory(sslSocketFactoryForEnv("cluster-unbound")) + .ssl(true) + .hostnameVerifier(localhostVerifier).hostAndPortMapper(portMap).build(), DEFAULT_REDIRECTIONS, DEFAULT_POOL_CONFIG)) { jc3.get("foo"); } @@ -189,11 +231,13 @@ public void connectWithCustomHostNameVerifier() { @Test public void connectWithCustomSocketFactory() throws Exception { - final SSLSocketFactory sslSocketFactory = SSLJedisTest.createTrustStoreSslSocketFactory(); try (JedisCluster jc = new JedisCluster(new HostAndPort("localhost", 8379), - DefaultJedisClientConfig.builder().password("cluster").ssl(true) - .sslSocketFactory(sslSocketFactory).hostAndPortMapper(portMap).build(), + DefaultJedisClientConfig.builder() + .password("cluster") + .sslSocketFactory(sslSocketFactoryForEnv("cluster-unbound")) + .ssl(true) + .hostAndPortMapper(portMap).build(), DEFAULT_REDIRECTIONS, DEFAULT_POOL_CONFIG)) { assertEquals(3, jc.getClusterNodes().size()); } @@ -201,16 +245,13 @@ public void connectWithCustomSocketFactory() throws Exception { @Test public void connectWithEmptyTrustStore() throws Exception { - final SSLSocketFactory sslSocketFactory = SSLJedisTest.createTrustNoOneSslSocketFactory(); - try (JedisCluster jc = new JedisCluster(new HostAndPort("localhost", 8379), - DefaultJedisClientConfig.builder().password("cluster").ssl(true) - .sslSocketFactory(sslSocketFactory).build(), DEFAULT_REDIRECTIONS, DEFAULT_POOL_CONFIG)) { -// jc.get("key"); -// Assert.fail("There should be no reachable node in cluster."); -//// } catch (JedisNoReachableClusterNodeException e) { + DefaultJedisClientConfig.builder() + .password("cluster") + .sslSocketFactory(createTrustNoOneSslSocketFactory()) + .ssl(true) + .build(), DEFAULT_REDIRECTIONS, DEFAULT_POOL_CONFIG)) { } catch (JedisClusterOperationException e) { -// assertEquals("No reachable node in cluster.", e.getMessage()); assertEquals("Could not initialize cluster slots cache.", e.getMessage()); } } @@ -220,10 +261,11 @@ public void defaultHostAndPortUsedIfMapReturnsNull() { HostAndPortMapper nullHostAndPortMap = (HostAndPort hostAndPort) -> null; try (JedisCluster jc = new JedisCluster(new HostAndPort("localhost", 7379), - DefaultJedisClientConfig.builder().password("cluster").ssl(false) + DefaultJedisClientConfig.builder() + .password("cluster") + .ssl(false) .hostAndPortMapper(nullHostAndPortMap).build(), DEFAULT_REDIRECTIONS, DEFAULT_POOL_CONFIG)) { -// Map clusterNodes = jc.getClusterNodes(); Map clusterNodes = jc.getClusterNodes(); assertEquals(3, clusterNodes.size()); assertTrue(clusterNodes.containsKey("127.0.0.1:7379")); @@ -231,14 +273,4 @@ public void defaultHostAndPortUsedIfMapReturnsNull() { assertTrue(clusterNodes.containsKey("127.0.0.1:7381")); } } - - public class LocalhostVerifier extends BasicHostnameVerifier { - @Override - public boolean verify(String hostname, SSLSession session) { - if (hostname.equals("127.0.0.1")) { - hostname = "localhost"; - } - return super.verify(hostname, session); - } - } } diff --git a/src/test/java/redis/clients/jedis/SSLJedisSentinelPoolTest.java b/src/test/java/redis/clients/jedis/SSLJedisSentinelPoolTest.java index 7468c9abfa..1490cb5af5 100644 --- a/src/test/java/redis/clients/jedis/SSLJedisSentinelPoolTest.java +++ b/src/test/java/redis/clients/jedis/SSLJedisSentinelPoolTest.java @@ -2,10 +2,14 @@ import java.util.HashSet; import java.util.Set; + +import redis.clients.jedis.util.TlsUtil; import org.apache.commons.pool2.impl.GenericObjectPoolConfig; import org.junit.BeforeClass; import org.junit.Test; +import static redis.clients.jedis.util.TlsUtil.envTruststore; + public class SSLJedisSentinelPoolTest { private static final String MASTER_NAME = "aclmaster"; @@ -19,7 +23,8 @@ public class SSLJedisSentinelPoolTest { @BeforeClass public static void prepare() { - SSLJedisTest.setupTrustStore(); + TlsUtil.createAndSaveEnvTruststore("redis9-sentinel", "changeit"); + //TlsUtil.setJvmTrustStore(envTruststore("redis9-sentinel")); sentinels.add(HostAndPorts.getSentinelServers().get(4)); } @@ -28,11 +33,18 @@ public static void prepare() { public void sentinelWithoutSslConnectsToRedisWithSsl() { DefaultJedisClientConfig masterConfig = DefaultJedisClientConfig.builder() - .user("acljedis").password("fizzbuzz").clientName("master-client").ssl(true) - .hostAndPortMapper(SSL_PORT_MAPPER).build(); + .user("acljedis") + .password("fizzbuzz") + .clientName("master-client") + .sslSocketFactory(TlsUtil.sslSocketFactoryForEnv("redis9-sentinel")) + .ssl(true) + .hostAndPortMapper(SSL_PORT_MAPPER).build(); DefaultJedisClientConfig sentinelConfig = DefaultJedisClientConfig.builder() - .user("sentinel").password("foobared").clientName("sentinel-client").ssl(false).build(); + .user("sentinel") + .password("foobared") + .clientName("sentinel-client") + .ssl(false).build(); try (JedisSentinelPool pool = new JedisSentinelPool(MASTER_NAME, sentinels, masterConfig, sentinelConfig)) { pool.getResource().close(); @@ -48,11 +60,18 @@ public void sentinelWithoutSslConnectsToRedisWithSsl() { public void sentinelWithSslConnectsToRedisWithoutSsl() { DefaultJedisClientConfig masterConfig = DefaultJedisClientConfig.builder() - .user("acljedis").password("fizzbuzz").clientName("master-client").ssl(false).build(); + .user("acljedis") + .password("fizzbuzz") + .clientName("master-client") + .ssl(false).build(); DefaultJedisClientConfig sentinelConfig = DefaultJedisClientConfig.builder() - .user("sentinel").password("foobared").clientName("sentinel-client") - .ssl(true).hostAndPortMapper(SSL_PORT_MAPPER).build(); + .user("sentinel") + .password("foobared") + .clientName("sentinel-client") + .sslSocketFactory(TlsUtil.sslSocketFactoryForEnv("redis9-sentinel")) + .ssl(true) + .hostAndPortMapper(SSL_PORT_MAPPER).build(); try (JedisSentinelPool pool = new JedisSentinelPool(MASTER_NAME, sentinels, masterConfig, sentinelConfig)) { pool.getResource().close(); @@ -68,12 +87,20 @@ public void sentinelWithSslConnectsToRedisWithoutSsl() { public void sentinelWithSslConnectsToRedisWithSsl() { DefaultJedisClientConfig masterConfig = DefaultJedisClientConfig.builder() - .user("acljedis").password("fizzbuzz").clientName("master-client").ssl(true) - .hostAndPortMapper(SSL_PORT_MAPPER).build(); + .user("acljedis") + .password("fizzbuzz") + .clientName("master-client") + .sslSocketFactory(TlsUtil.sslSocketFactoryForEnv("redis9-sentinel")) + .ssl(true) + .hostAndPortMapper(SSL_PORT_MAPPER).build(); DefaultJedisClientConfig sentinelConfig = DefaultJedisClientConfig.builder() - .user("sentinel").password("foobared").clientName("sentinel-client") - .ssl(true).hostAndPortMapper(SSL_PORT_MAPPER).build(); + .user("sentinel") + .password("foobared") + .clientName("sentinel-client") + .sslSocketFactory(TlsUtil.sslSocketFactoryForEnv("redis9-sentinel")) + .ssl(true) + .hostAndPortMapper(SSL_PORT_MAPPER).build(); try (JedisSentinelPool pool = new JedisSentinelPool(MASTER_NAME, sentinels, masterConfig, sentinelConfig)) { pool.getResource().close(); diff --git a/src/test/java/redis/clients/jedis/SSLJedisTest.java b/src/test/java/redis/clients/jedis/SSLJedisTest.java index 4ef4f969bb..89a448fd30 100644 --- a/src/test/java/redis/clients/jedis/SSLJedisTest.java +++ b/src/test/java/redis/clients/jedis/SSLJedisTest.java @@ -1,174 +1,86 @@ package redis.clients.jedis; +import static redis.clients.jedis.util.TlsUtil.envTruststore; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; -import java.io.File; -import java.io.FileInputStream; -import java.io.InputStream; -import java.security.InvalidAlgorithmParameterException; -import java.security.KeyStore; -import java.security.SecureRandom; -import java.security.cert.X509Certificate; - -import javax.net.ssl.HostnameVerifier; -import javax.net.ssl.SSLContext; -import javax.net.ssl.SSLPeerUnverifiedException; -import javax.net.ssl.SSLSession; -import javax.net.ssl.SSLSocketFactory; -import javax.net.ssl.TrustManager; -import javax.net.ssl.TrustManagerFactory; -import javax.net.ssl.X509TrustManager; - +import org.junit.AfterClass; +import redis.clients.jedis.util.TlsUtil; import org.junit.BeforeClass; import org.junit.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; -public class SSLJedisTest { - - protected static final EndpointConfig endpoint = HostAndPorts.getRedisEndpoint("standalone0-tls"); - - @BeforeClass - public static void prepare() { - setupTrustStore(); - } - - public static void setupTrustStore() { - setJvmTrustStore("src/test/resources/truststore.jceks", "jceks"); - } +import java.nio.file.Path; - private static void setJvmTrustStore(String trustStoreFilePath, String trustStoreType) { - assertTrue(String.format("Could not find trust store at '%s'.", trustStoreFilePath), - new File(trustStoreFilePath).exists()); - System.setProperty("javax.net.ssl.trustStore", trustStoreFilePath); - System.setProperty("javax.net.ssl.trustStoreType", trustStoreType); - } - - @Test - public void connectWithSsl() { - try (Jedis jedis = new Jedis(endpoint.getHost(), endpoint.getPort(), true)) { - jedis.auth(endpoint.getPassword()); - assertEquals("PONG", jedis.ping()); - } - } +public class SSLJedisTest { + static Logger log = LoggerFactory.getLogger(SSLJedisTest.class); + protected static final EndpointConfig endpoint = HostAndPorts.getRedisEndpoint("standalone0-tls"); - @Test - public void connectWithConfig() { - try (Jedis jedis = new Jedis(endpoint.getHostAndPort(), - DefaultJedisClientConfig.builder().ssl(true).build())) { - jedis.auth(endpoint.getPassword()); - assertEquals("PONG", jedis.ping()); + @BeforeClass + public static void prepare() { + Path trusStorePath = TlsUtil.createAndSaveEnvTruststore("redis1-2-5-10-sentinel", "changeit"); + TlsUtil.setCustomTrustStore(trusStorePath, "changeit"); } - } - @Test - public void connectWithConfigInterface() { - try (Jedis jedis = new Jedis(endpoint.getHostAndPort(), - new JedisClientConfig() { - @Override - public boolean isSsl() { - return true; - } - })) { - jedis.auth(endpoint.getPassword()); - assertEquals("PONG", jedis.ping()); + @AfterClass + public static void teardownTrustStore() { + TlsUtil.restoreOriginalTrustStore(); } - } - /** - * Tests opening a default SSL/TLS connection to redis using "rediss://" scheme url. - */ - @Test - public void connectWithUrl() { - // The "rediss" scheme instructs jedis to open a SSL/TLS connection. - try (Jedis jedis = new Jedis(endpoint.getURI().toString())) { - jedis.auth(endpoint.getPassword()); - assertEquals("PONG", jedis.ping()); + @Test + public void connectWithSsl() { + try (Jedis jedis = new Jedis(endpoint.getHost(), endpoint.getPort(), true)) { + jedis.auth(endpoint.getPassword()); + assertEquals("PONG", jedis.ping()); + } } - } - /** - * Tests opening a default SSL/TLS connection to redis. - */ - @Test - public void connectWithUri() { - // The "rediss" scheme instructs jedis to open a SSL/TLS connection. - try (Jedis jedis = new Jedis(endpoint.getURI())) { - jedis.auth(endpoint.getPassword()); - assertEquals("PONG", jedis.ping()); + @Test + public void connectWithConfig() { + try (Jedis jedis = new Jedis(endpoint.getHostAndPort(), + DefaultJedisClientConfig.builder().ssl(true).build())) { + jedis.auth(endpoint.getPassword()); + assertEquals("PONG", jedis.ping()); + } } - } - - /** - * Creates an SSLSocketFactory that trusts all certificates in truststore.jceks. - */ - static SSLSocketFactory createTrustStoreSslSocketFactory() throws Exception { - KeyStore trustStore = KeyStore.getInstance("jceks"); - - try (InputStream inputStream = new FileInputStream("src/test/resources/truststore.jceks")) { - trustStore.load(inputStream, null); + @Test + public void connectWithConfigInterface() { + try (Jedis jedis = new Jedis(endpoint.getHostAndPort(), + new JedisClientConfig() { + @Override + public boolean isSsl() { + return true; + } + })) { + jedis.auth(endpoint.getPassword()); + assertEquals("PONG", jedis.ping()); + } } - TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance("PKIX"); - trustManagerFactory.init(trustStore); - TrustManager[] trustManagers = trustManagerFactory.getTrustManagers(); - - SSLContext sslContext = SSLContext.getInstance("TLS"); - sslContext.init(null, trustManagers, new SecureRandom()); - return sslContext.getSocketFactory(); - } - - /** - * Creates an SSLSocketFactory with a trust manager that does not trust any certificates. - */ - static SSLSocketFactory createTrustNoOneSslSocketFactory() throws Exception { - TrustManager[] unTrustManagers = new TrustManager[] { new X509TrustManager() { - public X509Certificate[] getAcceptedIssuers() { - return new X509Certificate[0]; - } - - public void checkClientTrusted(X509Certificate[] chain, String authType) { - throw new RuntimeException(new InvalidAlgorithmParameterException()); - } - - public void checkServerTrusted(X509Certificate[] chain, String authType) { - throw new RuntimeException(new InvalidAlgorithmParameterException()); - } - } }; - SSLContext sslContext = SSLContext.getInstance("TLS"); - sslContext.init(null, unTrustManagers, new SecureRandom()); - return sslContext.getSocketFactory(); - } - - /** - * Very basic hostname verifier implementation for testing. NOT recommended for production. - */ - static class BasicHostnameVerifier implements HostnameVerifier { - - private static final String COMMON_NAME_RDN_PREFIX = "CN="; - - @Override - public boolean verify(String hostname, SSLSession session) { - X509Certificate peerCertificate; - try { - peerCertificate = (X509Certificate) session.getPeerCertificates()[0]; - } catch (SSLPeerUnverifiedException e) { - throw new IllegalStateException("The session does not contain a peer X.509 certificate.", e); - } - String peerCertificateCN = getCommonName(peerCertificate); - return hostname.equals(peerCertificateCN); + /** + * Tests opening a default SSL/TLS connection to redis using "rediss://" scheme url. + */ + @Test + public void connectWithUrl() { + // The "rediss" scheme instructs jedis to open a SSL/TLS connection. + try (Jedis jedis = new Jedis(endpoint.getURI().toString())) { + jedis.auth(endpoint.getPassword()); + assertEquals("PONG", jedis.ping()); + } } - private String getCommonName(X509Certificate peerCertificate) { - String subjectDN = peerCertificate.getSubjectDN().getName(); - String[] dnComponents = subjectDN.split(","); - for (String dnComponent : dnComponents) { - dnComponent = dnComponent.trim(); - if (dnComponent.startsWith(COMMON_NAME_RDN_PREFIX)) { - return dnComponent.substring(COMMON_NAME_RDN_PREFIX.length()); + /** + * Tests opening a default SSL/TLS connection to redis. + */ + @Test + public void connectWithUri() { + // The "rediss" scheme instructs jedis to open a SSL/TLS connection. + try (Jedis jedis = new Jedis(endpoint.getURI())) { + jedis.auth(endpoint.getPassword()); + assertEquals("PONG", jedis.ping()); } - } - throw new IllegalArgumentException("The certificate has no common name."); } - } + } diff --git a/src/test/java/redis/clients/jedis/UdsTest.java b/src/test/java/redis/clients/jedis/UdsTest.java index e29c65dedd..7993b06107 100644 --- a/src/test/java/redis/clients/jedis/UdsTest.java +++ b/src/test/java/redis/clients/jedis/UdsTest.java @@ -3,14 +3,22 @@ import java.io.File; import java.io.IOException; import java.net.Socket; + +import org.junit.Assume; +import org.junit.BeforeClass; import org.junit.Test; import org.newsclub.net.unix.AFUNIXSocket; import org.newsclub.net.unix.AFUNIXSocketAddress; import redis.clients.jedis.exceptions.JedisConnectionException; +import redis.clients.jedis.util.TestEnvUtil; import static org.junit.Assert.assertEquals; public class UdsTest { + @BeforeClass + public static void checkDockerEnvironment() { + Assume.assumeFalse("Unix sockets tests not supported against dockerised test env yet!", TestEnvUtil.isContainerEnv()); + } @Test public void jedisConnectsToUds() { diff --git a/src/test/java/redis/clients/jedis/commands/commandobjects/CommandObjectsBitmapCommandsTest.java b/src/test/java/redis/clients/jedis/commands/commandobjects/CommandObjectsBitmapCommandsTest.java index 5427447100..3c511fa823 100644 --- a/src/test/java/redis/clients/jedis/commands/commandobjects/CommandObjectsBitmapCommandsTest.java +++ b/src/test/java/redis/clients/jedis/commands/commandobjects/CommandObjectsBitmapCommandsTest.java @@ -6,6 +6,7 @@ import java.util.List; +import io.redis.test.annotations.SinceRedisVersion; import org.junit.Test; import redis.clients.jedis.RedisProtocol; import redis.clients.jedis.args.BitCountOption; @@ -52,6 +53,7 @@ public void testSetbitAndGetbitBinary() { } @Test + @SinceRedisVersion(value = "7.0.0", message = "Starting with Redis version 7.0.0: Added the BYTE|BIT option.") public void testBitcount() { String key = "bitcountKey"; byte[] keyBytes = key.getBytes(); @@ -82,6 +84,7 @@ public void testBitcount() { } @Test + @SinceRedisVersion(value = "7.0.0", message="Starting with Redis version 7.0.0: Added the BYTE|BIT option.") public void testBitpos() { String key = "bitposKey"; byte[] keyBytes = key.getBytes(); diff --git a/src/test/java/redis/clients/jedis/commands/commandobjects/CommandObjectsGenericCommandsTest.java b/src/test/java/redis/clients/jedis/commands/commandobjects/CommandObjectsGenericCommandsTest.java index f558a4f686..d9072d9736 100644 --- a/src/test/java/redis/clients/jedis/commands/commandobjects/CommandObjectsGenericCommandsTest.java +++ b/src/test/java/redis/clients/jedis/commands/commandobjects/CommandObjectsGenericCommandsTest.java @@ -19,6 +19,7 @@ import java.util.List; import java.util.Set; +import io.redis.test.annotations.SinceRedisVersion; import org.junit.Test; import redis.clients.jedis.Jedis; import redis.clients.jedis.RedisProtocol; @@ -202,6 +203,7 @@ public void testDumpAndRestoreBinary() { } @Test + @SinceRedisVersion(value = "7.0.0") public void testExpireAndExpireTime() { String key = "expireKey"; String value = "value"; @@ -221,6 +223,7 @@ public void testExpireAndExpireTime() { } @Test + @SinceRedisVersion(value = "7.0.0") public void testExpireAndExpireTimeBinary() { byte[] key = "expireKey".getBytes(); byte[] value = "value".getBytes(); @@ -240,6 +243,7 @@ public void testExpireAndExpireTimeBinary() { } @Test + @SinceRedisVersion(value = "7.0.0") public void testExpireWithExpiryOption() { String key = "expireWithOptionKey"; String value = "value"; @@ -263,6 +267,7 @@ public void testExpireWithExpiryOption() { } @Test + @SinceRedisVersion(value = "7.0.0") public void testExpireWithExpiryOptionTimeBinary() { byte[] key = "expireWithOptionKey".getBytes(); byte[] value = "value".getBytes(); @@ -286,6 +291,7 @@ public void testExpireWithExpiryOptionTimeBinary() { } @Test + @SinceRedisVersion(value = "7.0.0") public void testPexpireAndPexpireTime() { String key = "pexpireKey"; String value = "value"; @@ -305,6 +311,7 @@ public void testPexpireAndPexpireTime() { } @Test + @SinceRedisVersion(value = "7.0.0") public void testPexpireAndPexpireTimeBinary() { byte[] key = "pexpireKey".getBytes(); byte[] value = "value".getBytes(); @@ -324,6 +331,7 @@ public void testPexpireAndPexpireTimeBinary() { } @Test + @SinceRedisVersion(value = "7.0.0") public void testPexpireWithOptionsAndPexpireTime() { String key = "pexpireWithOptionsKey"; String value = "value"; @@ -349,6 +357,7 @@ public void testPexpireWithOptionsAndPexpireTime() { } @Test + @SinceRedisVersion(value = "7.0.0", message = "Starting with Redis version 7.0.0: Added options: NX, XX, GT and LT.") public void testPexpireWithOptionsAndPexpireTimeBinary() { byte[] key = "pexpireWithOptionsKey".getBytes(); byte[] value = "value".getBytes(); @@ -374,6 +383,7 @@ public void testPexpireWithOptionsAndPexpireTimeBinary() { } @Test + @SinceRedisVersion(value = "7.0.0") public void testExpireAtAndExpireTime() { String key = "expireAtKey"; String value = "value"; @@ -399,6 +409,7 @@ public void testExpireAtAndExpireTime() { } @Test + @SinceRedisVersion(value = "7.0.0") public void testExpireAtAndExpireTimeBinary() { byte[] key = "expireAtKey".getBytes(); byte[] value = "value".getBytes(); @@ -424,6 +435,7 @@ public void testExpireAtAndExpireTimeBinary() { } @Test + @SinceRedisVersion(value = "7.0.0") public void testExpireAtWithOptionsAndExpireTime() { String key = "expireAtWithOptionsKey"; String value = "value"; @@ -456,6 +468,7 @@ public void testExpireAtWithOptionsAndExpireTime() { } @Test + @SinceRedisVersion(value = "7.0.0") public void testExpireAtWithOptionsAndExpireTimeBinary() { byte[] key = "expireAtWithOptionsKey".getBytes(); byte[] value = "value".getBytes(); @@ -488,6 +501,7 @@ public void testExpireAtWithOptionsAndExpireTimeBinary() { } @Test + @SinceRedisVersion(value = "7.0.0") public void testPexpireAtAndPexpireTime() { String key = "pexpireAtKey"; String value = "value"; @@ -512,6 +526,7 @@ public void testPexpireAtAndPexpireTime() { } @Test + @SinceRedisVersion(value = "7.0.0") public void testPexpireAtAndPexpireTimeBinary() { byte[] key = "pexpireAtKey".getBytes(); byte[] value = "value".getBytes(); @@ -536,6 +551,7 @@ public void testPexpireAtAndPexpireTimeBinary() { } @Test + @SinceRedisVersion(value = "7.0.0", message = "Starting with Redis version 7.0.0: Added options: NX, XX, GT and LT.") public void testPexpireAtWithOptionsAndPexpireTime() { String key = "pexpireAtWithOptionsKey"; String value = "value"; @@ -568,6 +584,7 @@ public void testPexpireAtWithOptionsAndPexpireTime() { } @Test + @SinceRedisVersion(value = "7.0.0") public void testPexpireAtWithOptionsAndPexpireTimeBinary() { byte[] key = "pexpireAtWithOptionsKey".getBytes(); byte[] value = "value".getBytes(); @@ -812,6 +829,7 @@ public void testSortWithParamsAndStoreBinary() { } @Test + @SinceRedisVersion(value = "7.0.0") public void testSortReadonly() { String listKey = "readonlySortList"; @@ -824,6 +842,7 @@ public void testSortReadonly() { } @Test + @SinceRedisVersion(value = "7.0.0") public void testSortReadonlyBinary() { byte[] listKey = "readonlySortList".getBytes(); diff --git a/src/test/java/redis/clients/jedis/commands/commandobjects/CommandObjectsHashCommandsTest.java b/src/test/java/redis/clients/jedis/commands/commandobjects/CommandObjectsHashCommandsTest.java index a8de3122ab..73013bb24e 100644 --- a/src/test/java/redis/clients/jedis/commands/commandobjects/CommandObjectsHashCommandsTest.java +++ b/src/test/java/redis/clients/jedis/commands/commandobjects/CommandObjectsHashCommandsTest.java @@ -21,6 +21,7 @@ import java.util.Map; import java.util.Set; +import io.redis.test.annotations.SinceRedisVersion; import org.junit.Test; import redis.clients.jedis.RedisProtocol; import redis.clients.jedis.args.ExpiryOption; @@ -351,6 +352,7 @@ public void testHashRandfield() { } @Test + @SinceRedisVersion("7.4.0") public void testHscan() { String key = "testHashScan"; byte[] bkey = key.getBytes(); @@ -416,6 +418,7 @@ public void testHashStrlen() { } @Test + @SinceRedisVersion("7.4.0") public void hexpireAndHttl() { long seconds1 = 20; long seconds2 = 10; @@ -433,6 +436,7 @@ public void hexpireAndHttl() { } @Test + @SinceRedisVersion("7.4.0") public void hexpireAndHttlBinary() { long seconds1 = 20; long seconds2 = 10; @@ -450,6 +454,7 @@ public void hexpireAndHttlBinary() { } @Test + @SinceRedisVersion("7.4.0") public void hpexpireAndHpttl() { long millis1 = 20_000; long millis2 = 10_000; @@ -465,6 +470,7 @@ public void hpexpireAndHpttl() { } @Test + @SinceRedisVersion("7.4.0") public void hpexpireAndHpttlBinary() { long millis1 = 20_000; long millis2 = 10_000; @@ -480,6 +486,7 @@ public void hpexpireAndHpttlBinary() { } @Test + @SinceRedisVersion("7.4.0") public void hexpireAtAndExpireTime() { long currSeconds = System.currentTimeMillis() / 1000; long seconds1 = currSeconds + 20; @@ -498,6 +505,7 @@ public void hexpireAtAndExpireTime() { } @Test + @SinceRedisVersion("7.4.0") public void hexpireAtAndExpireTimeBinary() { long currSeconds = System.currentTimeMillis() / 1000; long seconds1 = currSeconds + 20; @@ -516,6 +524,7 @@ public void hexpireAtAndExpireTimeBinary() { } @Test + @SinceRedisVersion("7.4.0") public void hpexpireAtAndPexpireTime() { long currMillis = System.currentTimeMillis(); long unixMillis = currMillis + 20_000; @@ -531,6 +540,7 @@ public void hpexpireAtAndPexpireTime() { } @Test + @SinceRedisVersion("7.4.0") public void hpexpireAtAndPexpireTimeBinary() { long currMillis = System.currentTimeMillis(); long unixMillis = currMillis + 20_000; @@ -546,6 +556,7 @@ public void hpexpireAtAndPexpireTimeBinary() { } @Test + @SinceRedisVersion("7.4.0") public void hpersist() { long seconds = 20; @@ -560,6 +571,7 @@ public void hpersist() { } @Test + @SinceRedisVersion("7.4.0") public void hpersistBinary() { long seconds = 20; diff --git a/src/test/java/redis/clients/jedis/commands/commandobjects/CommandObjectsListCommandsTest.java b/src/test/java/redis/clients/jedis/commands/commandobjects/CommandObjectsListCommandsTest.java index c7f84157d1..9ea0c1534f 100644 --- a/src/test/java/redis/clients/jedis/commands/commandobjects/CommandObjectsListCommandsTest.java +++ b/src/test/java/redis/clients/jedis/commands/commandobjects/CommandObjectsListCommandsTest.java @@ -9,6 +9,7 @@ import java.util.List; +import io.redis.test.annotations.SinceRedisVersion; import org.junit.Test; import redis.clients.jedis.RedisProtocol; import redis.clients.jedis.args.ListDirection; @@ -608,6 +609,7 @@ public void testLmoveAndBlmoveBinary() { } @Test + @SinceRedisVersion(value = "7.0.0") public void testLmpopAndBlmpop() { String key1 = "list1"; String key2 = "list2"; @@ -635,6 +637,7 @@ public void testLmpopAndBlmpop() { } @Test + @SinceRedisVersion(value = "7.0.0") public void testLmpopAndBlmpopBinary() { byte[] key1 = "list1".getBytes(); byte[] key2 = "list2".getBytes(); diff --git a/src/test/java/redis/clients/jedis/commands/commandobjects/CommandObjectsScriptingCommandsTest.java b/src/test/java/redis/clients/jedis/commands/commandobjects/CommandObjectsScriptingCommandsTest.java index 7b6c1feae9..814de156de 100644 --- a/src/test/java/redis/clients/jedis/commands/commandobjects/CommandObjectsScriptingCommandsTest.java +++ b/src/test/java/redis/clients/jedis/commands/commandobjects/CommandObjectsScriptingCommandsTest.java @@ -18,6 +18,7 @@ import java.util.List; import java.util.Map; +import io.redis.test.annotations.SinceRedisVersion; import org.junit.Test; import redis.clients.jedis.RedisProtocol; import redis.clients.jedis.args.FlushMode; @@ -146,6 +147,7 @@ public void testEvalWithScriptKeysAndArgsList() { } @Test + @SinceRedisVersion(value = "7.0.0") public void testEvalReadonlyWithScriptKeysAndArgsList() { exec(commandObjects.set("readonlyKey1", "readonlyValue1")); exec(commandObjects.set("readonlyKey2", "readonlyValue2")); @@ -282,6 +284,7 @@ public void testEvalWithScriptKeysAndArgsListSha() { } @Test + @SinceRedisVersion(value = "7.0.0") public void testEvalReadonlyWithScriptKeysAndArgsListSha() { exec(commandObjects.set("readonlyKey1", "readonlyValue1")); exec(commandObjects.set("readonlyKey2", "readonlyValue2")); @@ -468,6 +471,7 @@ public void testScriptKill() { } @Test + @SinceRedisVersion(value = "7.0.0") public void testSumValuesFunction() { String luaScript = "#!lua name=mylib\n" + "redis.register_function('sumValues', function(keys, args)\n" + @@ -517,6 +521,7 @@ public void testSumValuesFunction() { } @Test + @SinceRedisVersion(value = "7.0.0") public void testSumValuesFunctionReadonly() { String luaScript = "#!lua name=mylib\n" + "redis.register_function{function_name='sumValues', callback=function(keys, args)\n" + @@ -550,6 +555,7 @@ public void testSumValuesFunctionReadonly() { } @Test + @SinceRedisVersion(value = "7.0.0") public void testFunctionDeletion() { String luaScript = "#!lua name=mylib\n" + "redis.register_function('sumValues', function(keys, args) return 42 end)"; @@ -572,6 +578,7 @@ public void testFunctionDeletion() { } @Test + @SinceRedisVersion(value = "7.0.0") public void testFunctionDeletionBinary() { String luaScript = "#!lua name=mylib\n" + "redis.register_function('sumValues', function(keys, args) return 42 end)"; @@ -594,6 +601,7 @@ public void testFunctionDeletionBinary() { } @Test + @SinceRedisVersion(value = "7.0.0") public void testFunctionListing() { String luaScript = "#!lua name=mylib\n" + "redis.register_function('sumValues', function(keys, args) return 42 end)"; @@ -651,6 +659,7 @@ public void testFunctionListing() { } @Test + @SinceRedisVersion(value = "7.0.0") public void testFunctionReload() { String luaScript = "#!lua name=mylib\n" + "redis.register_function('dummy', function(keys, args) return 42 end)"; @@ -672,6 +681,7 @@ public void testFunctionReload() { } @Test + @SinceRedisVersion(value = "7.0.0") public void testFunctionReloadBinary() { String luaScript = "#!lua name=mylib\n" + "redis.register_function('dummy', function(keys, args) return 42 end)"; @@ -693,6 +703,7 @@ public void testFunctionReloadBinary() { } @Test + @SinceRedisVersion(value = "7.0.0") public void testFunctionStats() { String luaScript = "#!lua name=mylib\n" + "redis.register_function('dummy', function(keys, args) return 42 end)"; @@ -719,6 +730,7 @@ public void testFunctionStats() { } @Test + @SinceRedisVersion(value = "7.0.0") public void testFunctionDumpFlushRestore() { String luaScript = "#!lua name=mylib\n" + "redis.register_function('sumValues', function(keys, args) return 42 end)"; @@ -752,6 +764,7 @@ public void testFunctionDumpFlushRestore() { } @Test + @SinceRedisVersion(value = "7.0.0") public void testFunctionDumpFlushRestoreWithPolicy() { String luaScript = "#!lua name=mylib\n" + "redis.register_function('sumValues', function(keys, args) return 42 end)"; @@ -785,6 +798,7 @@ public void testFunctionDumpFlushRestoreWithPolicy() { } @Test + @SinceRedisVersion(value = "7.0.0") public void testFunctionFlushWithMode() { String luaScript = "#!lua name=mylib\n" + "redis.register_function('sumValues', function(keys, args) return 42 end)"; @@ -807,6 +821,7 @@ public void testFunctionFlushWithMode() { } @Test + @SinceRedisVersion(value = "7.0.0") public void testFunctionKill() { JedisException e = assertThrows(JedisException.class, () -> exec(commandObjects.functionKill())); diff --git a/src/test/java/redis/clients/jedis/commands/commandobjects/CommandObjectsSetCommandsTest.java b/src/test/java/redis/clients/jedis/commands/commandobjects/CommandObjectsSetCommandsTest.java index 6a6874ef4d..84c82ac78f 100644 --- a/src/test/java/redis/clients/jedis/commands/commandobjects/CommandObjectsSetCommandsTest.java +++ b/src/test/java/redis/clients/jedis/commands/commandobjects/CommandObjectsSetCommandsTest.java @@ -13,6 +13,7 @@ import java.util.List; import java.util.Set; +import io.redis.test.annotations.SinceRedisVersion; import org.junit.Test; import redis.clients.jedis.RedisProtocol; import redis.clients.jedis.params.ScanParams; @@ -228,6 +229,7 @@ public void testSdiffstoreBinary() { } @Test + @SinceRedisVersion(value = "7.0.0") public void testSinterAndSinterCard() { String key1 = "testSetInter1"; String key2 = "testSetInter2"; diff --git a/src/test/java/redis/clients/jedis/commands/commandobjects/CommandObjectsSortedSetCommandsTest.java b/src/test/java/redis/clients/jedis/commands/commandobjects/CommandObjectsSortedSetCommandsTest.java index 21a573c8db..292ed25572 100644 --- a/src/test/java/redis/clients/jedis/commands/commandobjects/CommandObjectsSortedSetCommandsTest.java +++ b/src/test/java/redis/clients/jedis/commands/commandobjects/CommandObjectsSortedSetCommandsTest.java @@ -17,6 +17,7 @@ import java.util.List; import java.util.Map; +import io.redis.test.annotations.SinceRedisVersion; import org.junit.Test; import redis.clients.jedis.RedisProtocol; import redis.clients.jedis.args.SortedSetOption; @@ -328,6 +329,7 @@ public void testZrankAndZrevrank() { } @Test + @SinceRedisVersion(value = "7.2.0") public void testZrankWithScoreAndZrevrankWithScore() { String key = "zset"; String member1 = "one"; @@ -1198,6 +1200,7 @@ public void testZdiffStoreBinary() { } @Test + @SinceRedisVersion(value = "7.2.0") public void testZinterAndZintercard() { ZParams params = new ZParams().aggregate(ZParams.Aggregate.SUM).weights(1, 2); @@ -1419,6 +1422,7 @@ public void testZunionstoreBinary() { } @Test + @SinceRedisVersion(value = "7.2.0") public void testZmpopAndZmpopWithCount() { String key1 = "sortedSet1"; String key2 = "sortedSet2"; @@ -1444,6 +1448,7 @@ public void testZmpopAndZmpopWithCount() { } @Test + @SinceRedisVersion(value = "7.2.0") public void testZmpopAndZmpopWithCountBinary() { byte[] key1 = "sortedSet1".getBytes(); byte[] key2 = "sortedSet2".getBytes(); @@ -1469,6 +1474,7 @@ public void testZmpopAndZmpopWithCountBinary() { } @Test + @SinceRedisVersion(value = "7.0.0") public void testBzmpop() { String key1 = "sortedSet1"; String key2 = "sortedSet2"; @@ -1496,6 +1502,7 @@ public void testBzmpop() { } @Test + @SinceRedisVersion(value = "7.2.0") public void testBzmpopBinary() { byte[] key1 = "sortedSet1".getBytes(); byte[] key2 = "sortedSet2".getBytes(); @@ -1523,6 +1530,7 @@ public void testBzmpopBinary() { } @Test + @SinceRedisVersion(value = "7.2.0") public void testBzmpopCount() { String key1 = "sortedSet1"; String key2 = "sortedSet2"; @@ -1542,6 +1550,7 @@ public void testBzmpopCount() { } @Test + @SinceRedisVersion(value = "7.0.0") public void testBzmpopCountBinary() { byte[] key1 = "sortedSet1".getBytes(); byte[] key2 = "sortedSet2".getBytes(); diff --git a/src/test/java/redis/clients/jedis/commands/commandobjects/CommandObjectsStandaloneTestBase.java b/src/test/java/redis/clients/jedis/commands/commandobjects/CommandObjectsStandaloneTestBase.java index 128642d2a7..00a57f1ff6 100644 --- a/src/test/java/redis/clients/jedis/commands/commandobjects/CommandObjectsStandaloneTestBase.java +++ b/src/test/java/redis/clients/jedis/commands/commandobjects/CommandObjectsStandaloneTestBase.java @@ -1,5 +1,8 @@ package redis.clients.jedis.commands.commandobjects; +import io.redis.test.utils.EnabledOnCommandRule; +import io.redis.test.utils.RedisVersionRule; +import org.junit.Rule; import redis.clients.jedis.HostAndPorts; import redis.clients.jedis.RedisProtocol; @@ -8,6 +11,11 @@ */ public abstract class CommandObjectsStandaloneTestBase extends CommandObjectsTestBase { + @Rule + public RedisVersionRule versionRule = new RedisVersionRule(endpoint.getHostAndPort(), endpoint.getClientConfigBuilder().build()); + @Rule + public EnabledOnCommandRule enabledOnCommandRule = new EnabledOnCommandRule(endpoint.getHostAndPort(), endpoint.getClientConfigBuilder().build()); + public CommandObjectsStandaloneTestBase(RedisProtocol protocol) { super(protocol, HostAndPorts.getRedisEndpoint("standalone0")); } diff --git a/src/test/java/redis/clients/jedis/commands/commandobjects/CommandObjectsStringCommandsTest.java b/src/test/java/redis/clients/jedis/commands/commandobjects/CommandObjectsStringCommandsTest.java index 9a35cd88e4..a192a462d9 100644 --- a/src/test/java/redis/clients/jedis/commands/commandobjects/CommandObjectsStringCommandsTest.java +++ b/src/test/java/redis/clients/jedis/commands/commandobjects/CommandObjectsStringCommandsTest.java @@ -10,6 +10,7 @@ import java.util.List; +import io.redis.test.annotations.SinceRedisVersion; import org.junit.Test; import redis.clients.jedis.RedisProtocol; import redis.clients.jedis.params.GetExParams; @@ -298,6 +299,7 @@ public void testIncrementOperationsBinary() { } @Test + @SinceRedisVersion(value = "7.0.0") public void testLcs() { String keyA = "keyA"; String keyB = "keyB"; @@ -338,6 +340,7 @@ public void testLcs() { } @Test + @SinceRedisVersion(value = "7.0.0") public void testLcsBinary() { byte[] keyA = "keyA".getBytes(); byte[] keyB = "keyB".getBytes(); diff --git a/src/test/java/redis/clients/jedis/commands/commandobjects/CommandObjectsTestBase.java b/src/test/java/redis/clients/jedis/commands/commandobjects/CommandObjectsTestBase.java index 45e0c71ad4..8e194105e0 100644 --- a/src/test/java/redis/clients/jedis/commands/commandobjects/CommandObjectsTestBase.java +++ b/src/test/java/redis/clients/jedis/commands/commandobjects/CommandObjectsTestBase.java @@ -5,6 +5,8 @@ import java.util.Collection; +import io.redis.test.utils.RedisVersion; +import io.redis.test.utils.RedisVersionUtil; import org.junit.Before; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; @@ -84,9 +86,14 @@ public void setUp() { commandExecutor.executeCommand(commandObjects.flushAll()), equalTo("OK")); - assertThat( - commandExecutor.executeCommand(commandObjects.functionFlush(FlushMode.SYNC)), - equalTo("OK")); + if ( RedisVersionUtil + .getRedisVersion(endpoint) + .isGreaterThanOrEqualTo(RedisVersion.V7_0_0)) { + assertThat( + commandExecutor.executeCommand(commandObjects.functionFlush(FlushMode.SYNC)), + equalTo("OK")); + + } } /** diff --git a/src/test/java/redis/clients/jedis/commands/jedis/AccessControlListCommandsTest.java b/src/test/java/redis/clients/jedis/commands/jedis/AccessControlListCommandsTest.java index 150211383e..a184dc52b8 100644 --- a/src/test/java/redis/clients/jedis/commands/jedis/AccessControlListCommandsTest.java +++ b/src/test/java/redis/clients/jedis/commands/jedis/AccessControlListCommandsTest.java @@ -11,9 +11,13 @@ import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; +import static io.redis.test.utils.RedisVersionUtil.getRedisVersion; import java.util.Arrays; import java.util.List; + +import io.redis.test.annotations.EnabledOnCommand; +import io.redis.test.annotations.SinceRedisVersion; import org.hamcrest.Matchers; import org.junit.After; import org.junit.BeforeClass; @@ -30,7 +34,7 @@ import redis.clients.jedis.exceptions.JedisDataException; import redis.clients.jedis.resps.AccessControlLogEntry; import redis.clients.jedis.resps.AccessControlUser; -import redis.clients.jedis.util.RedisVersionUtil; +import io.redis.test.utils.RedisVersion; import redis.clients.jedis.util.SafeEncoder; /** @@ -41,12 +45,13 @@ public class AccessControlListCommandsTest extends JedisCommandsTestBase { public static final String USER_NAME = "newuser"; public static final String USER_PASSWORD = "secret"; + public static final String USER_ANTIREZ = "antirez"; @BeforeClass public static void prepare() throws Exception { // Use to check if the ACL test should be ran. ACL are available only in 6.0 and later org.junit.Assume.assumeTrue("Not running ACL test on this version of Redis", - RedisVersionUtil.checkRedisMajorVersionNumber(6, endpoint)); + getRedisVersion(endpoint).isGreaterThanOrEqualTo(RedisVersion.V6_0_0)); } public AccessControlListCommandsTest(RedisProtocol protocol) { @@ -58,6 +63,7 @@ public AccessControlListCommandsTest(RedisProtocol protocol) { public void tearDown() throws Exception { try { jedis.aclDelUser(USER_NAME); + jedis.aclDelUser(USER_ANTIREZ); } catch (Exception e) { } super.tearDown(); } @@ -95,10 +101,11 @@ public void addAndRemoveUser() { @Test public void aclUsers() { List users = jedis.aclUsers(); - assertEquals(2, users.size()); + assertEquals(3, users.size()); assertThat(users, Matchers.hasItem("default")); - - assertEquals(2, jedis.aclUsersBinary().size()); // Test binary + assertThat(users, Matchers.hasItem("deploy")); + assertThat(users, Matchers.hasItem("acljedis")); + assertEquals(3, jedis.aclUsersBinary().size()); // Test binary } @Test @@ -109,6 +116,7 @@ public void aclGetUser() { assertFalse(userInfo.getFlags().isEmpty()); assertEquals(1, userInfo.getPassword().size()); assertEquals("+@all", userInfo.getCommands()); + assertEquals("~*", userInfo.getKeys()); // create new user @@ -219,13 +227,14 @@ public void aclExcudeSingleCommand() { fail("Should throw a NOPERM exception"); } catch (JedisAccessControlException e) { assertThat(e.getMessage(), startsWith("NOPERM ")); - assertThat(e.getMessage(), endsWith(" has no permissions to run the 'ping' command")); + assertThat(e.getMessage(), containsString(" has no permissions to run the 'ping' command")); } jedis2.close(); } @Test + @EnabledOnCommand(value = "ACL", subCommand = "DRYRUN") public void aclDryRun() { jedis.aclSetUser(USER_NAME, "nopass", "allkeys", "+set", "-get"); @@ -240,6 +249,7 @@ public void aclDryRun() { } @Test + @EnabledOnCommand(value = "ACL", subCommand = "DRYRUN") public void aclDryRunBinary() { byte[] username = USER_NAME.getBytes(); @@ -284,7 +294,7 @@ public void basicPermissionsTest() { fail("Should throw a NOPERM exception"); } catch (JedisAccessControlException e) { assertThat(e.getMessage(), startsWith("NOPERM ")); - assertThat(e.getMessage(), endsWith(" has no permissions to run the 'set' command")); + assertThat(e.getMessage(), containsString(" has no permissions to run the 'set' command")); } // change permissions of the user @@ -305,7 +315,7 @@ public void basicPermissionsTest() { } // allow user to access a subset of the key - jedis.aclSetUser(USER_NAME, "allcommands", "~foo:*", "~bar:*"); // TODO : Define a DSL + jedis.aclSetUser(USER_NAME, "allcommands", "~foo:*", "~bar:*"); // TODO : a DSL // create key foo, bar and zap jedis2.set("foo:1", "a"); @@ -347,9 +357,9 @@ public void aclLogTest() { assertTrue(jedis.aclLog().isEmpty()); // create new user and cconnect - jedis.aclSetUser("antirez", ">foo", "on", "+set", "~object:1234"); - jedis.aclSetUser("antirez", "+eval", "+multi", "+exec"); - jedis.auth("antirez", "foo"); + jedis.aclSetUser(USER_ANTIREZ, ">foo", "on", "+set", "~object:1234"); + jedis.aclSetUser(USER_ANTIREZ, "+eval", "+multi", "+exec"); + jedis.auth(USER_ANTIREZ, "foo"); // generate an error (antirez user does not have the permission to access foo) try { @@ -364,7 +374,7 @@ public void aclLogTest() { List aclEntries = jedis.aclLog(); assertEquals("Number of log messages ", 1, aclEntries.size()); assertEquals(1, aclEntries.get(0).getCount()); - assertEquals("antirez", aclEntries.get(0).getUsername()); + assertEquals(USER_ANTIREZ, aclEntries.get(0).getUsername()); assertEquals("toplevel", aclEntries.get(0).getContext()); assertEquals("command", aclEntries.get(0).getReason()); assertEquals("get", aclEntries.get(0).getObject()); @@ -373,7 +383,7 @@ public void aclLogTest() { jedis.aclLogReset(); assertTrue(jedis.aclLog().isEmpty()); - jedis.auth("antirez", "foo"); + jedis.auth(USER_ANTIREZ, "foo"); for (int i = 0; i < 10; i++) { // generate an error (antirez user does not have the permission to access foo) @@ -391,7 +401,7 @@ public void aclLogTest() { assertEquals("get", jedis.aclLog().get(0).getObject()); // Generate another type of error - jedis.auth("antirez", "foo"); + jedis.auth(USER_ANTIREZ, "foo"); try { jedis.set("somekeynotallowed", "1234"); fail("Should have thrown an JedisAccessControlException: user does not have the permission to set(\"somekeynotallowed\", \"1234\")"); @@ -408,7 +418,7 @@ public void aclLogTest() { jedis.aclLogReset(); assertTrue(jedis.aclLog().isEmpty()); - jedis.auth("antirez", "foo"); + jedis.auth(USER_ANTIREZ, "foo"); Transaction t = jedis.multi(); t.incr("foo"); try { @@ -425,7 +435,7 @@ public void aclLogTest() { assertEquals("incr", jedis.aclLog().get(0).getObject()); // ACL LOG can accept a numerical argument to show less entries - jedis.auth("antirez", "foo"); + jedis.auth(USER_ANTIREZ, "foo"); for (int i = 0; i < 5; i++) { try { jedis.incr("foo"); @@ -451,10 +461,11 @@ public void aclLogTest() { String status = jedis.aclLogReset(); assertEquals(status, "OK"); - jedis.aclDelUser("antirez"); + jedis.aclDelUser(USER_ANTIREZ); } @Test + @SinceRedisVersion(value = "7.2.0", message = "Starting with Redis version 7.2.0: Added entry ID, timestamp created, and timestamp last updated.") public void aclLogWithEntryID() { try { jedis.auth("wronguser", "wrongpass"); diff --git a/src/test/java/redis/clients/jedis/commands/jedis/AllKindOfValuesCommandsTest.java b/src/test/java/redis/clients/jedis/commands/jedis/AllKindOfValuesCommandsTest.java index 868195961e..821f61babd 100644 --- a/src/test/java/redis/clients/jedis/commands/jedis/AllKindOfValuesCommandsTest.java +++ b/src/test/java/redis/clients/jedis/commands/jedis/AllKindOfValuesCommandsTest.java @@ -1,6 +1,7 @@ package redis.clients.jedis.commands.jedis; import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.lessThanOrEqualTo; import static org.junit.Assert.*; import static redis.clients.jedis.Protocol.Command.BLPOP; @@ -14,7 +15,10 @@ import static redis.clients.jedis.params.ScanParams.SCAN_POINTER_START; import static redis.clients.jedis.params.ScanParams.SCAN_POINTER_START_BINARY; +import java.time.Duration; import java.util.*; + +import io.redis.test.annotations.SinceRedisVersion; import org.hamcrest.Matchers; import org.junit.Assume; import org.junit.Test; @@ -35,6 +39,8 @@ @RunWith(Parameterized.class) public class AllKindOfValuesCommandsTest extends JedisCommandsTestBase { + private static final long TIME_SKEW = Duration.ofMillis(5).toMillis(); + final byte[] bfoo = { 0x01, 0x02, 0x03, 0x04 }; final byte[] bfoo1 = { 0x01, 0x02, 0x03, 0x04, 0x0A }; final byte[] bfoo2 = { 0x01, 0x02, 0x03, 0x04, 0x0B }; @@ -305,6 +311,7 @@ public void dbSize() { } @Test + @SinceRedisVersion(value = "7.0.0", message = "Starting with Redis version 7.0.0: Added options: NX, XX, GT and LT.") public void expire() { assertEquals(0, jedis.expire("foo", 20L)); @@ -321,6 +328,7 @@ public void expire() { } @Test + @SinceRedisVersion(value = "7.0.0", message = "Starting with Redis version 7.0.0: Added options: NX, XX, GT and LT.") public void expireAt() { long unixTime = (System.currentTimeMillis() / 1000L) + 20; @@ -341,6 +349,7 @@ public void expireAt() { } @Test + @SinceRedisVersion(value = "7.0.0") public void expireTime() { long unixTime; @@ -626,7 +635,8 @@ public void restoreParams() { assertTrue(jedis2.pttl("foo") <= 1000); jedis2.restore("bar", System.currentTimeMillis() + 1000, serialized, RestoreParams.restoreParams().replace().absTtl()); - assertTrue(jedis2.pttl("bar") <= 1000); + assertThat("ttl", jedis2.pttl("bar"), lessThanOrEqualTo(1000l + TIME_SKEW)); + jedis2.restore("bar1", 1000, serialized, RestoreParams.restoreParams().replace().idleTime(1000)); assertEquals(1000, jedis2.objectIdletime("bar1").longValue()); @@ -639,6 +649,7 @@ public void restoreParams() { } @Test + @SinceRedisVersion(value = "7.0.0") public void pexpire() { assertEquals(0, jedis.pexpire("foo", 10000)); @@ -680,6 +691,7 @@ public void pexpireAt() { } @Test + @SinceRedisVersion(value = "7.0.0") public void pexpireTime() { long unixTime = (System.currentTimeMillis()) + 10000; diff --git a/src/test/java/redis/clients/jedis/commands/jedis/BitCommandsTest.java b/src/test/java/redis/clients/jedis/commands/jedis/BitCommandsTest.java index 01cc61b3c9..2976526e61 100644 --- a/src/test/java/redis/clients/jedis/commands/jedis/BitCommandsTest.java +++ b/src/test/java/redis/clients/jedis/commands/jedis/BitCommandsTest.java @@ -7,6 +7,8 @@ import static org.junit.Assert.fail; import java.util.List; + +import io.redis.test.annotations.SinceRedisVersion; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; @@ -153,13 +155,20 @@ public void bitposModifier() { assertEquals(1, jedis.bitpos("mykey", true, BitPosParams.bitPosParams())); assertEquals(18, jedis.bitpos("mykey", true, BitPosParams.bitPosParams().start(2))); assertEquals(18, jedis.bitpos("mykey", true, BitPosParams.bitPosParams().start(2).end(-1))); + } + + @Test + @SinceRedisVersion(value = "7.0.0", message = "7.0.0 Added the BYTE|BIT option.") + public void bitposModifierByte() { + jedis.set("mykey", "\\x00\\xff\\xf0"); assertEquals(18, jedis.bitpos("mykey", true, BitPosParams.bitPosParams().start(2).end(-1) - .modifier(BitCountOption.BYTE))); + .modifier(BitCountOption.BYTE))); assertEquals(9, jedis.bitpos("mykey", true, BitPosParams.bitPosParams().start(7).end(15) - .modifier(BitCountOption.BIT))); + .modifier(BitCountOption.BIT))); } @Test + @SinceRedisVersion("7.0.0") public void setAndgetrange() { jedis.set("key1", "Hello World"); assertEquals(11, jedis.setrange("key1", 6, "Jedis")); @@ -182,6 +191,15 @@ public void bitCount() { assertEquals(3, (long) jedis.bitcount("foo", 2L, 5L)); assertEquals(3, (long) jedis.bitcount("foo".getBytes(), 2L, 5L)); + } + + @Test + @SinceRedisVersion("7.0.0") + public void bitCountByteOptions() { + jedis.setbit("foo", 16, true); + jedis.setbit("foo", 24, true); + jedis.setbit("foo", 40, true); + jedis.setbit("foo", 56, true); assertEquals(3, (long) jedis.bitcount("foo", 2L, 5L, BitCountOption.BYTE)); assertEquals(3, (long) jedis.bitcount("foo".getBytes(), 2L, 5L, BitCountOption.BYTE)); diff --git a/src/test/java/redis/clients/jedis/commands/jedis/ClientCommandsTest.java b/src/test/java/redis/clients/jedis/commands/jedis/ClientCommandsTest.java index 1b221cfc55..24dd66e914 100644 --- a/src/test/java/redis/clients/jedis/commands/jedis/ClientCommandsTest.java +++ b/src/test/java/redis/clients/jedis/commands/jedis/ClientCommandsTest.java @@ -15,8 +15,12 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; +import io.redis.test.annotations.SinceRedisVersion; +import io.redis.test.utils.EnabledOnCommandRule; +import io.redis.test.utils.RedisVersionRule; import org.junit.After; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; @@ -38,6 +42,11 @@ public class ClientCommandsTest extends JedisCommandsTestBase { private Jedis client; + @Rule + public RedisVersionRule versionRule = new RedisVersionRule(endpoint.getHostAndPort(), endpoint.getClientConfigBuilder().build()); + @Rule + public EnabledOnCommandRule enabledOnCommandRule = new EnabledOnCommandRule(endpoint.getHostAndPort(), endpoint.getClientConfigBuilder().build()); + public ClientCommandsTest(RedisProtocol protocol) { super(protocol); } @@ -73,6 +82,7 @@ public void nameBinary() { } @Test + @SinceRedisVersion("7.2.0") public void clientSetInfoCommand() { String libName = "Jedis::A-Redis-Java-library"; String libVersion = "999.999.999"; @@ -243,6 +253,7 @@ public void killUser() { } @Test + @SinceRedisVersion(value = "7.4.0", message = "MAXAGE (since Redis 7.4)") public void killMaxAge() throws InterruptedException { long maxAge = 2; diff --git a/src/test/java/redis/clients/jedis/commands/jedis/ClusterCommandsTest.java b/src/test/java/redis/clients/jedis/commands/jedis/ClusterCommandsTest.java index 7e5c5db875..c94585ac3e 100644 --- a/src/test/java/redis/clients/jedis/commands/jedis/ClusterCommandsTest.java +++ b/src/test/java/redis/clients/jedis/commands/jedis/ClusterCommandsTest.java @@ -9,14 +9,14 @@ import java.util.List; import java.util.Map; +import io.redis.test.annotations.SinceRedisVersion; +import io.redis.test.utils.EnabledOnCommandRule; +import io.redis.test.utils.RedisVersionRule; import org.hamcrest.MatcherAssert; import org.hamcrest.Matchers; -import org.junit.After; -import org.junit.AfterClass; -import org.junit.Before; -import org.junit.BeforeClass; -import org.junit.Test; +import org.junit.*; +import redis.clients.jedis.DefaultJedisClientConfig; import redis.clients.jedis.HostAndPort; import redis.clients.jedis.Jedis; import redis.clients.jedis.args.ClusterResetType; @@ -35,6 +35,11 @@ public class ClusterCommandsTest { private static HostAndPort nodeInfo1 = HostAndPorts.getClusterServers().get(0); private static HostAndPort nodeInfo2 = HostAndPorts.getClusterServers().get(1); + @Rule + public RedisVersionRule versionRule = new RedisVersionRule(nodeInfo1, DefaultJedisClientConfig.builder().password("cluster").build()); + @Rule + public EnabledOnCommandRule enabledOnCommandRule = new EnabledOnCommandRule(nodeInfo1, DefaultJedisClientConfig.builder().password("cluster").build()); + @Before public void setUp() throws Exception { node1 = new Jedis(nodeInfo1); @@ -124,6 +129,7 @@ public void clusterInfo() { } @Test + @SinceRedisVersion("7.0.0") public void addAndDelSlotsRange() { // test add assertEquals("OK", node1.clusterAddSlotsRange(100, 105)); @@ -203,6 +209,7 @@ public void clusterSlots() { } @Test + @SinceRedisVersion("7.0.0") public void clusterShards() { assertEquals("OK", node1.clusterAddSlots(3100, 3101, 3102, 3105)); @@ -224,7 +231,7 @@ public void clusterShards() { assertNotNull(nodeInfo.getIp()); assertNull(nodeInfo.getHostname()); assertNotNull(nodeInfo.getPort()); - assertNull(nodeInfo.getTlsPort()); + assertNotNull(nodeInfo.getTlsPort()); assertNotNull(nodeInfo.getRole()); assertNotNull(nodeInfo.getReplicationOffset()); assertNotNull(nodeInfo.getHealth()); @@ -234,6 +241,7 @@ public void clusterShards() { } @Test + @SinceRedisVersion("7.0.0") public void clusterLinks() throws InterruptedException { List> links = node1.clusterLinks(); assertNotNull(links); @@ -264,6 +272,7 @@ public void clusterMyId() { } @Test + @SinceRedisVersion("7.2.0") public void clusterMyShardId() { MatcherAssert.assertThat(node1.clusterMyShardId(), Matchers.not(Matchers.isEmptyOrNullString())); } diff --git a/src/test/java/redis/clients/jedis/commands/jedis/ClusterJedisCommandsTestBase.java b/src/test/java/redis/clients/jedis/commands/jedis/ClusterJedisCommandsTestBase.java index 8cd4b8379d..e96a4896d3 100644 --- a/src/test/java/redis/clients/jedis/commands/jedis/ClusterJedisCommandsTestBase.java +++ b/src/test/java/redis/clients/jedis/commands/jedis/ClusterJedisCommandsTestBase.java @@ -5,14 +5,14 @@ import java.util.HashSet; import java.util.Set; +import io.redis.test.utils.EnabledOnCommandRule; +import io.redis.test.utils.RedisVersionRule; import org.junit.After; import org.junit.AfterClass; import org.junit.Before; -import redis.clients.jedis.HostAndPort; -import redis.clients.jedis.Jedis; -import redis.clients.jedis.HostAndPorts; -import redis.clients.jedis.JedisCluster; +import org.junit.Rule; +import redis.clients.jedis.*; import redis.clients.jedis.util.JedisClusterCRC16; public abstract class ClusterJedisCommandsTestBase { @@ -27,6 +27,11 @@ public abstract class ClusterJedisCommandsTestBase { private final Set jedisClusterNode = new HashSet<>(); JedisCluster cluster; + @Rule + public RedisVersionRule versionRule = new RedisVersionRule(nodeInfo1, DefaultJedisClientConfig.builder().password("cluster").build()); + @Rule + public EnabledOnCommandRule enabledOnCommandRule = new EnabledOnCommandRule(nodeInfo1, DefaultJedisClientConfig.builder().password("cluster").build()); + @Before public void setUp() throws InterruptedException { node1 = new Jedis(nodeInfo1); diff --git a/src/test/java/redis/clients/jedis/commands/jedis/ClusterScriptingCommandsTest.java b/src/test/java/redis/clients/jedis/commands/jedis/ClusterScriptingCommandsTest.java index 503337683e..bb5e4c4688 100644 --- a/src/test/java/redis/clients/jedis/commands/jedis/ClusterScriptingCommandsTest.java +++ b/src/test/java/redis/clients/jedis/commands/jedis/ClusterScriptingCommandsTest.java @@ -10,6 +10,8 @@ import java.util.Collections; import java.util.List; import java.util.Map; + +import io.redis.test.annotations.SinceRedisVersion; import org.junit.Test; import redis.clients.jedis.HostAndPort; @@ -115,6 +117,7 @@ public void broadcast() { } @Test + @SinceRedisVersion("7.0.0") public void broadcastWithError() { JedisBroadcastException error = assertThrows(JedisBroadcastException.class, () -> cluster.functionDelete("xyz")); diff --git a/src/test/java/redis/clients/jedis/commands/jedis/ClusterShardedPublishSubscribeCommandsTest.java b/src/test/java/redis/clients/jedis/commands/jedis/ClusterShardedPublishSubscribeCommandsTest.java index 65d36fd5df..c1863892bd 100644 --- a/src/test/java/redis/clients/jedis/commands/jedis/ClusterShardedPublishSubscribeCommandsTest.java +++ b/src/test/java/redis/clients/jedis/commands/jedis/ClusterShardedPublishSubscribeCommandsTest.java @@ -7,6 +7,11 @@ import java.util.HashMap; import java.util.Map; + +import io.redis.test.annotations.SinceRedisVersion; +import io.redis.test.utils.RedisVersion; +import io.redis.test.utils.RedisVersionRule; +import org.junit.Rule; import org.junit.Test; import redis.clients.jedis.BinaryJedisShardedPubSub; @@ -16,6 +21,7 @@ import redis.clients.jedis.util.JedisClusterCRC16; import redis.clients.jedis.util.SafeEncoder; +@SinceRedisVersion(value = "7.0.0", message = "SSUBSCRIBE") public class ClusterShardedPublishSubscribeCommandsTest extends ClusterJedisCommandsTestBase { private void publishOne(final String channel, final String message) { @@ -24,6 +30,7 @@ private void publishOne(final String channel, final String message) { } @Test + @SinceRedisVersion(value = "7.0.0", message = "SSUBSCRIBE") public void subscribe() throws InterruptedException { cluster.ssubscribe(new JedisShardedPubSub() { @Override public void onSMessage(String channel, String message) { @@ -48,6 +55,7 @@ public void subscribe() throws InterruptedException { } @Test + @SinceRedisVersion(value = "7.0.0", message = "SSUBSCRIBE") public void subscribeMany() { cluster.ssubscribe(new JedisShardedPubSub() { @Override public void onSMessage(String channel, String message) { @@ -62,6 +70,7 @@ public void subscribeMany() { } @Test + @SinceRedisVersion(value = "7.0.0", message = "SSUBSCRIBE") public void pubSubChannels() { cluster.ssubscribe(new JedisShardedPubSub() { private int count = 0; @@ -82,6 +91,7 @@ public void pubSubChannels() { } @Test + @SinceRedisVersion(value = "7.0.0", message = "SSUBSCRIBE") public void pubSubChannelsWithPattern() { cluster.ssubscribe(new JedisShardedPubSub() { private int count = 0; @@ -102,6 +112,7 @@ public void pubSubChannelsWithPattern() { } @Test + @SinceRedisVersion(value = "7.0.0", message = "SSUBSCRIBE") public void pubSubNumSub() { final Map expectedNumSub = new HashMap<>(); expectedNumSub.put("{testchannel}1", 1L); @@ -125,6 +136,7 @@ public void pubSubNumSub() { } @Test + @SinceRedisVersion(value = "7.0.0", message = "SSUBSCRIBE") public void binarySubscribe() { cluster.ssubscribe(new BinaryJedisShardedPubSub() { @Override public void onSMessage(byte[] channel, byte[] message) { @@ -147,6 +159,7 @@ public void binarySubscribe() { } @Test + @SinceRedisVersion(value = "7.0.0", message = "SSUBSCRIBE") public void binarySubscribeMany() { cluster.ssubscribe(new BinaryJedisShardedPubSub() { @Override public void onSMessage(byte[] channel, byte[] message) { diff --git a/src/test/java/redis/clients/jedis/commands/jedis/ControlCommandsTest.java b/src/test/java/redis/clients/jedis/commands/jedis/ControlCommandsTest.java index 4c9059f8d0..d82f9572db 100644 --- a/src/test/java/redis/clients/jedis/commands/jedis/ControlCommandsTest.java +++ b/src/test/java/redis/clients/jedis/commands/jedis/ControlCommandsTest.java @@ -21,6 +21,7 @@ import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; +import io.redis.test.annotations.SinceRedisVersion; import org.hamcrest.MatcherAssert; import org.hamcrest.Matchers; import org.junit.Test; @@ -253,6 +254,7 @@ public void configSetBinary() { } @Test + @SinceRedisVersion(value = "7.0.0", message = "Starting with Redis version 7.0.0: Added the ability to pass multiple pattern parameters in one call") public void configGetSetMulti() { String[] params = new String[]{"hash-max-listpack-entries", "set-max-intset-entries", "zset-max-listpack-entries"}; Map info = jedis.configGet(params); @@ -272,6 +274,7 @@ public void waitReplicas() { } @Test + @SinceRedisVersion("7.2.0") public void waitAof() { assertEquals(KeyValue.of(0L, 0L), jedis.waitAOF(0L, 0L, 100L)); } @@ -379,12 +382,14 @@ public void clientUnpause() { } @Test + @SinceRedisVersion("7.0.0") public void clientNoEvict() { assertEquals("OK", jedis.clientNoEvictOn()); assertEquals("OK", jedis.clientNoEvictOff()); } @Test + @SinceRedisVersion("7.2.0") public void clientNoTouch() { assertEquals("OK", jedis.clientNoTouchOn()); assertEquals("OK", jedis.clientNoTouchOff()); @@ -475,6 +480,7 @@ public void commandCount() { } @Test + @SinceRedisVersion("7.0.0") public void commandDocs() { Map docs = jedis.commandDocs("SORT", "SET"); @@ -496,12 +502,18 @@ public void commandGetKeys() { List keys = jedis.commandGetKeys("SORT", "mylist", "ALPHA", "STORE", "outlist"); assertEquals(2, keys.size()); + } + + @Test + @SinceRedisVersion("7.0.0") + public void commandGetKeysAndFlags() { List>> keySandFlags = jedis.commandGetKeysAndFlags("SET", "k1", "v1"); assertEquals("k1", keySandFlags.get(0).getKey()); assertEquals(2, keySandFlags.get(0).getValue().size()); } @Test + @SinceRedisVersion(value = "7.0.0", message = "Starting with Redis version 7.0.0: Allowed to be called with no argument to get info on all commands.") public void commandInfo() { Map infos = jedis.commandInfo("GET", "foo", "SET"); @@ -521,6 +533,7 @@ public void commandInfo() { } @Test + @SinceRedisVersion("7.0.0") public void commandList() { List commands = jedis.commandList(); assertTrue(commands.size() > 100); diff --git a/src/test/java/redis/clients/jedis/commands/jedis/HashesCommandsTest.java b/src/test/java/redis/clients/jedis/commands/jedis/HashesCommandsTest.java index 88ca543fc4..a3d85d2c92 100644 --- a/src/test/java/redis/clients/jedis/commands/jedis/HashesCommandsTest.java +++ b/src/test/java/redis/clients/jedis/commands/jedis/HashesCommandsTest.java @@ -31,10 +31,15 @@ import java.util.Set; import java.util.stream.Collectors; +import io.redis.test.annotations.SinceRedisVersion; +import io.redis.test.utils.EnabledOnCommandRule; +import io.redis.test.utils.RedisVersionRule; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; +import redis.clients.jedis.HostAndPorts; import redis.clients.jedis.Pipeline; import redis.clients.jedis.RedisProtocol; import redis.clients.jedis.Response; @@ -46,6 +51,13 @@ @RunWith(Parameterized.class) public class HashesCommandsTest extends JedisCommandsTestBase { + + @Rule + public RedisVersionRule versionRule = new RedisVersionRule(endpoint.getHostAndPort(), endpoint.getClientConfigBuilder().build()); + + @Rule + public EnabledOnCommandRule enabledOnCommandRule = new EnabledOnCommandRule(endpoint.getHostAndPort(), endpoint.getClientConfigBuilder().build()); + final byte[] bfoo = { 0x01, 0x02, 0x03, 0x04 }; final byte[] bbar = { 0x05, 0x06, 0x07, 0x08 }; final byte[] bcar = { 0x09, 0x0A, 0x0B, 0x0C }; @@ -479,6 +491,7 @@ public void hscanCount() { } @Test + @SinceRedisVersion("7.4.0") public void hscanNoValues() { jedis.hset("foo", "b", "y"); jedis.hset("foo", "a", "x"); @@ -502,6 +515,7 @@ public void hscanNoValues() { } @Test + @SinceRedisVersion(value = "7.4.0", message = "NOVALUES flag (since Redis 7.4)") public void hscanNoValuesMatch() { ScanParams params = new ScanParams(); params.match("a*"); @@ -534,6 +548,7 @@ public void hscanNoValuesMatch() { } @Test + @SinceRedisVersion(value = "7.4.0", message = "NOVALUES flag (since Redis 7.4)") public void hscanNoValuesCount() { ScanParams params = new ScanParams(); params.count(2); @@ -653,6 +668,7 @@ public void hrandfield() { } @Test + @SinceRedisVersion("7.4.0") public void hexpireAndHttl() { long seconds1 = 20; long seconds2 = 10; @@ -670,6 +686,7 @@ public void hexpireAndHttl() { } @Test + @SinceRedisVersion("7.4.0") public void hexpireAndHttlBinary() { long seconds1 = 20; long seconds2 = 10; @@ -687,6 +704,7 @@ public void hexpireAndHttlBinary() { } @Test + @SinceRedisVersion("7.4.0") public void hpexpireAndHpttl() { long millis1 = 20_000; long millis2 = 10_000; @@ -702,6 +720,7 @@ public void hpexpireAndHpttl() { } @Test + @SinceRedisVersion("7.4.0") public void hpexpireAndHpttlBinary() { long millis1 = 20_000; long millis2 = 10_000; @@ -717,6 +736,7 @@ public void hpexpireAndHpttlBinary() { } @Test + @SinceRedisVersion("7.4.0") public void hexpireAtAndExpireTime() { long currSeconds = System.currentTimeMillis() / 1000; long seconds1 = currSeconds + 20; @@ -735,6 +755,7 @@ public void hexpireAtAndExpireTime() { } @Test + @SinceRedisVersion("7.4.0") public void hexpireAtAndExpireTimeBinary() { long currSeconds = System.currentTimeMillis() / 1000; long seconds1 = currSeconds + 20; @@ -753,6 +774,7 @@ public void hexpireAtAndExpireTimeBinary() { } @Test + @SinceRedisVersion("7.4.0") public void hpexpireAtAndPexpireTime() { long currMillis = System.currentTimeMillis(); long unixMillis = currMillis + 20_000; @@ -768,6 +790,7 @@ public void hpexpireAtAndPexpireTime() { } @Test + @SinceRedisVersion("7.4.0") public void hpexpireAtAndPexpireTimeBinary() { long currMillis = System.currentTimeMillis(); long unixMillis = currMillis + 20_000; @@ -783,6 +806,7 @@ public void hpexpireAtAndPexpireTimeBinary() { } @Test + @SinceRedisVersion("7.4.0") public void hpersist() { long seconds = 20; @@ -797,6 +821,7 @@ public void hpersist() { } @Test + @SinceRedisVersion("7.4.0") public void hpersistBinary() { long seconds = 20; diff --git a/src/test/java/redis/clients/jedis/commands/jedis/JedisCommandsTestBase.java b/src/test/java/redis/clients/jedis/commands/jedis/JedisCommandsTestBase.java index cec36a0866..7a25858160 100644 --- a/src/test/java/redis/clients/jedis/commands/jedis/JedisCommandsTestBase.java +++ b/src/test/java/redis/clients/jedis/commands/jedis/JedisCommandsTestBase.java @@ -2,14 +2,27 @@ import java.util.Collection; +import io.redis.test.utils.EnabledOnCommandRule; +import io.redis.test.utils.RedisVersionRule; import org.junit.After; import org.junit.Before; +import org.junit.Rule; import org.junit.runners.Parameterized.Parameters; import redis.clients.jedis.*; import redis.clients.jedis.commands.CommandsTestsParameters; public abstract class JedisCommandsTestBase { + @Rule + public RedisVersionRule versionRule = new RedisVersionRule( + HostAndPorts.getRedisEndpoint("standalone0").getHostAndPort(), + HostAndPorts.getRedisEndpoint("standalone0").getClientConfigBuilder().build()); + + @Rule + public EnabledOnCommandRule enabledOnCommandRule = new EnabledOnCommandRule( + HostAndPorts.getRedisEndpoint("standalone0").getHostAndPort(), + HostAndPorts.getRedisEndpoint("standalone0").getClientConfigBuilder().build()); + /** * Input data for parameterized tests. In principle all subclasses of this * class should be parameterized tests, to run with several versions of RESP. @@ -26,7 +39,6 @@ public static Collection data() { protected final RedisProtocol protocol; protected Jedis jedis; - /** * The RESP protocol is to be injected by the subclasses, usually via JUnit * parameterized tests, because most of the subclassed tests are meant to be @@ -43,7 +55,8 @@ public JedisCommandsTestBase(RedisProtocol protocol) { @Before public void setUp() throws Exception { jedis = new Jedis(endpoint.getHostAndPort(), endpoint.getClientConfigBuilder() - .protocol(protocol).timeoutMillis(500).build()); + .protocol(protocol) + .timeoutMillis(500).build()); jedis.flushAll(); } diff --git a/src/test/java/redis/clients/jedis/commands/jedis/ListCommandsTest.java b/src/test/java/redis/clients/jedis/commands/jedis/ListCommandsTest.java index 194bed29ae..f052c7d58c 100644 --- a/src/test/java/redis/clients/jedis/commands/jedis/ListCommandsTest.java +++ b/src/test/java/redis/clients/jedis/commands/jedis/ListCommandsTest.java @@ -13,6 +13,7 @@ import java.util.Collections; import java.util.List; +import io.redis.test.annotations.SinceRedisVersion; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; @@ -881,6 +882,7 @@ public void blmove() { } @Test + @SinceRedisVersion("7.0.0") public void lmpop() { String mylist1 = "mylist1"; String mylist2 = "mylist2"; @@ -906,6 +908,7 @@ public void lmpop() { } @Test + @SinceRedisVersion("7.0.0") public void blmpopSimple() { String mylist1 = "mylist1"; String mylist2 = "mylist2"; diff --git a/src/test/java/redis/clients/jedis/commands/jedis/ModuleTest.java b/src/test/java/redis/clients/jedis/commands/jedis/ModuleTest.java index bef13001ef..dc84ef2efe 100644 --- a/src/test/java/redis/clients/jedis/commands/jedis/ModuleTest.java +++ b/src/test/java/redis/clients/jedis/commands/jedis/ModuleTest.java @@ -5,6 +5,10 @@ import java.util.Collections; import java.util.List; + +import org.junit.Assume; +import org.junit.BeforeClass; +import redis.clients.jedis.util.TestEnvUtil; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; @@ -17,6 +21,11 @@ @RunWith(Parameterized.class) public class ModuleTest extends JedisCommandsTestBase { + @BeforeClass + public static void checkDockerEnvironment() { + Assume.assumeFalse("Module tests not supported against dockerised test env yet!",TestEnvUtil.isContainerEnv()); + } + static enum ModuleCommand implements ProtocolCommand { SIMPLE("testmodule.simple"); @@ -40,7 +49,7 @@ public ModuleTest(RedisProtocol protocol) { @Test public void testModules() { try { - assertEquals("OK", jedis.moduleLoad("/tmp/testmodule.so")); + assertEquals("OK", jedis.moduleLoad(TestEnvUtil.testModuleSo())); List modules = jedis.moduleList(); diff --git a/src/test/java/redis/clients/jedis/commands/jedis/ScriptingCommandsTest.java b/src/test/java/redis/clients/jedis/commands/jedis/ScriptingCommandsTest.java index 9f4c60de0b..fd0a7a86c0 100644 --- a/src/test/java/redis/clients/jedis/commands/jedis/ScriptingCommandsTest.java +++ b/src/test/java/redis/clients/jedis/commands/jedis/ScriptingCommandsTest.java @@ -8,6 +8,9 @@ import java.util.List; import java.util.Map; +import io.redis.test.annotations.SinceRedisVersion; +import io.redis.test.utils.RedisVersion; +import io.redis.test.utils.RedisVersionUtil; import org.hamcrest.MatcherAssert; import org.hamcrest.Matchers; import org.junit.Before; @@ -39,7 +42,11 @@ public ScriptingCommandsTest(RedisProtocol redisProtocol) { @Override public void setUp() throws Exception { super.setUp(); - jedis.functionFlush(); + if ( RedisVersionUtil + .getRedisVersion(jedis) + .isGreaterThanOrEqualTo(RedisVersion.V7_0_0)) { + jedis.functionFlush(); + } } final byte[] bfoo = { 0x01, 0x02, 0x03, 0x04 }; @@ -142,6 +149,7 @@ public void evalNoArgs() { } @Test + @SinceRedisVersion(value = "7.0.0") public void evalReadonly() { String script = "return KEYS[1]"; List keys = new ArrayList(); @@ -165,6 +173,7 @@ public void evalsha() { } @Test + @SinceRedisVersion(value = "7.0.0") public void evalshaReadonly() { jedis.set("foo", "bar"); jedis.eval("return redis.call('get','foo')"); @@ -185,6 +194,7 @@ public void evalshaBinary() { } @Test + @SinceRedisVersion(value = "7.0.0") public void evalshaReadonlyBinary() { jedis.set(SafeEncoder.encode("foo"), SafeEncoder.encode("bar")); jedis.eval(SafeEncoder.encode("return redis.call('get','foo')")); @@ -335,6 +345,7 @@ public void emptyLuaTableReply() { } @Test + @SinceRedisVersion(value = "7.0.0") public void functionLoadAndDelete() { String engine = "Lua"; String library = "mylib"; @@ -354,6 +365,7 @@ public void functionLoadAndDelete() { } @Test + @SinceRedisVersion(value = "7.0.0") public void functionFlush() { String engine = "Lua"; String library = "mylib"; @@ -369,6 +381,7 @@ public void functionFlush() { } @Test + @SinceRedisVersion(value = "7.0.0") public void functionList() { String engine = "LUA"; String library = "mylib"; @@ -436,6 +449,7 @@ public void functionList() { } @Test + @SinceRedisVersion(value = "7.0.0") public void functionDumpRestore() { String engine = "Lua"; String library = "mylib"; @@ -455,6 +469,7 @@ public void functionDumpRestore() { } @Test + @SinceRedisVersion(value = "7.0.0") public void functionStatsWithoutRunning() { String engine = "Lua"; String library = "mylib"; @@ -490,6 +505,7 @@ public void functionStatsWithoutRunning() { // } @Test + @SinceRedisVersion(value = "7.0.0") public void functionKillWithoutRunningFunction() { String engine = "Lua"; String library = "mylib"; @@ -505,6 +521,7 @@ public void functionKillWithoutRunningFunction() { } @Test + @SinceRedisVersion(value = "7.0.0") public void fcall() { String engine = "Lua"; String library = "mylib"; @@ -516,6 +533,7 @@ public void fcall() { } @Test + @SinceRedisVersion(value = "7.0.0") public void fcallBinary() { String engine = "Lua"; String library = "mylib"; @@ -527,6 +545,7 @@ public void fcallBinary() { } @Test + @SinceRedisVersion(value = "7.0.0") public void fcallReadonly() { String engine = "Lua"; String library = "mylib"; diff --git a/src/test/java/redis/clients/jedis/commands/jedis/SetCommandsTest.java b/src/test/java/redis/clients/jedis/commands/jedis/SetCommandsTest.java index d2b660b5b4..67ede2b75c 100644 --- a/src/test/java/redis/clients/jedis/commands/jedis/SetCommandsTest.java +++ b/src/test/java/redis/clients/jedis/commands/jedis/SetCommandsTest.java @@ -19,6 +19,7 @@ import java.util.List; import java.util.Set; +import io.redis.test.annotations.SinceRedisVersion; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; @@ -364,6 +365,7 @@ public void sinterstore() { } @Test + @SinceRedisVersion("7.0.0") public void sintercard() { jedis.sadd("foo", "a"); jedis.sadd("foo", "b"); diff --git a/src/test/java/redis/clients/jedis/commands/jedis/SlowlogCommandsTest.java b/src/test/java/redis/clients/jedis/commands/jedis/SlowlogCommandsTest.java index 0377c481b4..a10c7c1290 100644 --- a/src/test/java/redis/clients/jedis/commands/jedis/SlowlogCommandsTest.java +++ b/src/test/java/redis/clients/jedis/commands/jedis/SlowlogCommandsTest.java @@ -5,8 +5,11 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; -import java.util.Arrays; -import java.util.List; +import java.net.InetAddress; +import java.net.NetworkInterface; +import java.net.SocketException; +import java.util.*; + import org.hamcrest.Matchers; import org.junit.After; import org.junit.Before; @@ -22,8 +25,6 @@ @RunWith(Parameterized.class) public class SlowlogCommandsTest extends JedisCommandsTestBase { - private static final List LOCAL_IPS = Arrays.asList("127.0.0.1", "[::1]"); - private static final String SLOWLOG_TIME_PARAM = "slowlog-log-slower-than"; private static final String ZERO_STRING = "0"; @@ -81,7 +82,7 @@ public void slowlog() { @Test public void slowlogObjectDetails() { - final String clientName = "slowlog-object-client"; + final String clientName = "slowlog-object-client-" + UUID.randomUUID(); jedis.clientSetname(clientName); jedis.slowlogReset(); jedis.configSet(SLOWLOG_TIME_PARAM, ZERO_STRING); @@ -90,6 +91,7 @@ public void slowlogObjectDetails() { //assertEquals(1, logs.size()); assertThat(logs.size(), Matchers.allOf(Matchers.greaterThanOrEqualTo(1), Matchers.lessThanOrEqualTo(2))); Slowlog log = logs.get(0); + assertEquals(clientName, log.getClientName()); assertThat(log.getId(), Matchers.greaterThan(0L)); assertThat(log.getTimeStamp(), Matchers.greaterThan(0L)); assertThat(log.getExecutionTime(), Matchers.greaterThanOrEqualTo(0L)); @@ -98,9 +100,8 @@ public void slowlogObjectDetails() { assertEquals(SafeEncoder.encode(Protocol.Keyword.SET.getRaw()), log.getArgs().get(1)); assertEquals(SLOWLOG_TIME_PARAM, log.getArgs().get(2)); assertEquals(ZERO_STRING, log.getArgs().get(3)); - assertThat(log.getClientIpPort().getHost(), Matchers.in(LOCAL_IPS)); + assertThat(log.getClientIpPort().getHost(), Matchers.in(getAllLocalIps())); assertThat(log.getClientIpPort().getPort(), Matchers.greaterThan(0)); - assertEquals(clientName, log.getClientName()); } @Test @@ -127,4 +128,18 @@ public void slowlogBinaryObjectDetails() { assertThat(((byte[]) log.get(4)).length, Matchers.greaterThanOrEqualTo(10)); // 'IP:PORT' assertArrayEquals(clientName, (byte[]) log.get(5)); } + + private static Set getAllLocalIps() { + Set allLocalIps = new HashSet<>(); + try { + for (NetworkInterface netIf : Collections.list(NetworkInterface.getNetworkInterfaces())) { + for (InetAddress addr : Collections.list(netIf.getInetAddresses())) { + allLocalIps.add(addr.getHostAddress()); + } + } + } catch (SocketException e) { + throw new RuntimeException(e); + } + return allLocalIps; + } } diff --git a/src/test/java/redis/clients/jedis/commands/jedis/SortedSetCommandsTest.java b/src/test/java/redis/clients/jedis/commands/jedis/SortedSetCommandsTest.java index 9e4cd71a53..d98941dc5b 100644 --- a/src/test/java/redis/clients/jedis/commands/jedis/SortedSetCommandsTest.java +++ b/src/test/java/redis/clients/jedis/commands/jedis/SortedSetCommandsTest.java @@ -7,6 +7,8 @@ import static redis.clients.jedis.util.AssertUtil.assertByteArrayListEquals; import java.util.*; + +import io.redis.test.annotations.SinceRedisVersion; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; @@ -400,6 +402,7 @@ public void zrank() { } @Test + @SinceRedisVersion("7.2.0") public void zrankWithScore() { jedis.zadd("foo", 1d, "a"); jedis.zadd("foo", 2d, "b"); @@ -1416,6 +1419,7 @@ public void zintertoreParams() { } @Test + @SinceRedisVersion("7.0.0") public void zintercard() { jedis.zadd("foo", 1, "a"); jedis.zadd("foo", 2, "b"); @@ -1658,6 +1662,7 @@ private Double getScoreFromByteMap(Map bhash, byte[] key) { } @Test + @SinceRedisVersion("7.0.0") public void zmpop() { jedis.zadd("foo", 1d, "a", ZAddParams.zAddParams().nx()); jedis.zadd("foo", 10d, "b", ZAddParams.zAddParams().nx()); @@ -1673,6 +1678,7 @@ public void zmpop() { } @Test + @SinceRedisVersion("7.0.0") public void bzmpopSimple() { jedis.zadd("foo", 1d, "a", ZAddParams.zAddParams().nx()); jedis.zadd("foo", 10d, "b", ZAddParams.zAddParams().nx()); diff --git a/src/test/java/redis/clients/jedis/commands/jedis/SortingCommandsTest.java b/src/test/java/redis/clients/jedis/commands/jedis/SortingCommandsTest.java index 1d0a976f41..4ce41722de 100644 --- a/src/test/java/redis/clients/jedis/commands/jedis/SortingCommandsTest.java +++ b/src/test/java/redis/clients/jedis/commands/jedis/SortingCommandsTest.java @@ -6,10 +6,12 @@ import java.util.ArrayList; import java.util.List; +import io.redis.test.annotations.EnabledOnCommand; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; +import redis.clients.jedis.Protocol; import redis.clients.jedis.RedisProtocol; import redis.clients.jedis.params.SortingParams; @@ -324,6 +326,7 @@ public void sortStore() { } @Test + @EnabledOnCommand("SORT_RO") public void sort_ro() { jedis.rpush("foo", "1", "3", "2"); diff --git a/src/test/java/redis/clients/jedis/commands/jedis/StreamsCommandsTest.java b/src/test/java/redis/clients/jedis/commands/jedis/StreamsCommandsTest.java index 0ac12f9d05..719014bdfc 100644 --- a/src/test/java/redis/clients/jedis/commands/jedis/StreamsCommandsTest.java +++ b/src/test/java/redis/clients/jedis/commands/jedis/StreamsCommandsTest.java @@ -18,6 +18,9 @@ import java.util.Map.Entry; import java.util.concurrent.atomic.AtomicReference; +import io.redis.test.annotations.SinceRedisVersion; +import io.redis.test.utils.RedisVersion; +import io.redis.test.utils.RedisVersionUtil; import org.hamcrest.Matchers; import org.junit.Test; import org.junit.runner.RunWith; @@ -173,11 +176,13 @@ public void xaddParamsId() { assertEquals(2, id.getTime()); assertEquals(3, id.getSequence()); - id = jedis.xadd(key, XAddParams.xAddParams().id(4), map); - assertNotNull(id); - assertEquals(4, id.getTime()); - assertEquals(0, id.getSequence()); - + // Starting with Redis version 7.0.0: Added support for the -* explicit ID form. + if (RedisVersionUtil.getRedisVersion(jedis).isGreaterThanOrEqualTo(RedisVersion.V7_0_0)) { + id = jedis.xadd(key, XAddParams.xAddParams().id(4), map); + assertNotNull(id); + assertEquals(4, id.getTime()); + assertEquals(0, id.getSequence()); + } id = jedis.xadd(key, XAddParams.xAddParams().id("5-6"), map); assertNotNull(id); assertEquals(5, id.getTime()); @@ -348,6 +353,31 @@ public void xreadAsMap() { assertEquals(2, streams2.size()); assertEquals(id1, streams2.get(stream1).get(0).getID()); assertEquals(id2, streams2.get(stream2).get(0).getID()); + } + + @Test + @SinceRedisVersion(value = "7.4.0", message = "From Redis 7.4, you can use the + sign as a special ID to request last entry") + public void xreadAsMapLastEntry() { + + final String stream1 = "xread-stream1"; + final String stream2 = "xread-stream2"; + + Map streamQeury1 = singletonMap(stream1, new StreamEntryID()); + + // Before creating Stream + assertNull(jedis.xreadAsMap(XReadParams.xReadParams().block(1), streamQeury1)); + assertNull(jedis.xreadAsMap(XReadParams.xReadParams(), streamQeury1)); + + Map map = new HashMap<>(); + map.put("f1", "v1"); + StreamEntryID id1 = new StreamEntryID(1); + StreamEntryID id2 = new StreamEntryID(2); + StreamEntryID id3 = new StreamEntryID(3); + + assertEquals(id1, jedis.xadd(stream1, id1, map)); + assertEquals(id2, jedis.xadd(stream2, id2, map)); + assertEquals(id3, jedis.xadd(stream1, id3, map)); + // Read from last entry Map streamQueryLE = singletonMap(stream1, StreamEntryID.XREAD_LAST_ENTRY); @@ -880,6 +910,8 @@ public void xinfo() throws InterruptedException { final String MY_CONSUMER = "myConsumer"; final String MY_CONSUMER2 = "myConsumer2"; + final RedisVersion redisVersion = RedisVersionUtil.getRedisVersion(jedis); + Map map1 = new HashMap<>(); map1.put(F1, V1); StreamEntryID id1 = jedis.xadd(STREAM_NAME, (StreamEntryID) null, map1); @@ -942,7 +974,10 @@ public void xinfo() throws InterruptedException { assertEquals(MY_CONSUMER, consumersInfo.get(0).getName()); assertEquals(0L, consumersInfo.get(0).getPending()); assertThat(consumersInfo.get(0).getIdle(), Matchers.greaterThanOrEqualTo(0L)); - assertThat(consumersInfo.get(0).getInactive(), Matchers.any(Long.class)); + + if ( redisVersion.isGreaterThanOrEqualTo(RedisVersion.V7_2_0)) { + assertThat(consumersInfo.get(0).getInactive(), Matchers.any(Long.class)); + } // Consumer info test assertEquals(MY_CONSUMER, @@ -954,7 +989,9 @@ public void xinfo() throws InterruptedException { assertEquals(MY_CONSUMER, consumerInfo.get(0).getName()); assertEquals(0L, consumerInfo.get(0).getPending()); assertThat(consumerInfo.get(0).getIdle(), Matchers.greaterThanOrEqualTo(0L)); - assertThat(consumerInfo.get(0).getInactive(), Matchers.any(Long.class)); + if (redisVersion.isGreaterThanOrEqualTo(RedisVersion.V7_2_0)) { + assertThat(consumerInfo.get(0).getInactive(), Matchers.any(Long.class)); + } // test with more groups and consumers jedis.xgroupCreate(STREAM_NAME, G2, StreamEntryID.XGROUP_LAST_ENTRY, false); @@ -1028,7 +1065,9 @@ public void xinfoStreamFullWithPending() { StreamConsumerFullInfo consumer = group.getConsumers().get(0); assertEquals("xreadGroup-consumer", consumer.getName()); assertThat(consumer.getSeenTime(), Matchers.greaterThanOrEqualTo(0L)); - assertThat(consumer.getActiveTime(), Matchers.greaterThanOrEqualTo(0L)); + if (RedisVersionUtil.getRedisVersion(jedis).isGreaterThanOrEqualTo(RedisVersion.V7_2_0)) { + assertThat(consumer.getActiveTime(), Matchers.greaterThanOrEqualTo(0L)); + } assertEquals(1, consumer.getPending().size()); List consumerPendingEntry = consumer.getPending().get(0); assertEquals(id1, consumerPendingEntry.get(0)); diff --git a/src/test/java/redis/clients/jedis/commands/jedis/StringValuesCommandsTest.java b/src/test/java/redis/clients/jedis/commands/jedis/StringValuesCommandsTest.java index 44525e072f..12bcd8aebe 100644 --- a/src/test/java/redis/clients/jedis/commands/jedis/StringValuesCommandsTest.java +++ b/src/test/java/redis/clients/jedis/commands/jedis/StringValuesCommandsTest.java @@ -6,6 +6,8 @@ import java.util.ArrayList; import java.util.List; + +import io.redis.test.annotations.SinceRedisVersion; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; @@ -247,6 +249,7 @@ public void psetex() { } @Test + @SinceRedisVersion("7.0.0") public void lcs() { jedis.mset("key1", "ohmytext", "key2", "mynewtext"); diff --git a/src/test/java/redis/clients/jedis/commands/unified/AllKindOfValuesCommandsTestBase.java b/src/test/java/redis/clients/jedis/commands/unified/AllKindOfValuesCommandsTestBase.java index f31c100988..866b2fc12d 100644 --- a/src/test/java/redis/clients/jedis/commands/unified/AllKindOfValuesCommandsTestBase.java +++ b/src/test/java/redis/clients/jedis/commands/unified/AllKindOfValuesCommandsTestBase.java @@ -22,6 +22,7 @@ import static redis.clients.jedis.params.ScanParams.SCAN_POINTER_START; import static redis.clients.jedis.params.ScanParams.SCAN_POINTER_START_BINARY; +import java.time.Duration; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -29,6 +30,8 @@ import java.util.HashSet; import java.util.List; import java.util.Set; + +import io.redis.test.annotations.SinceRedisVersion; import org.hamcrest.Matchers; import org.junit.Assume; import org.junit.Test; @@ -286,6 +289,7 @@ public void dbSize() { } @Test + @SinceRedisVersion(value="7.0.0", message = "Starting with Redis version 7.0.0: Added options: NX, XX, GT and LT.") public void expire() { assertEquals(0, jedis.expire("foo", 20L)); @@ -302,6 +306,7 @@ public void expire() { } @Test + @SinceRedisVersion(value="7.0.0", message = "Starting with Redis version 7.0.0: Added options: NX, XX, GT and LT.") public void expireAt() { long unixTime = (System.currentTimeMillis() / 1000L) + 20; @@ -322,6 +327,7 @@ public void expireAt() { } @Test + @SinceRedisVersion(value="7.0.0") public void expireTime() { long unixTime; @@ -458,6 +464,7 @@ public void restoreParams() { } @Test + @SinceRedisVersion(value="7.0.0", message = "Starting with Redis version 7.0.0: Added options: NX, XX, GT and LT.") public void pexpire() { assertEquals(0, jedis.pexpire("foo", 10000)); @@ -499,6 +506,7 @@ public void pexpireAt() { } @Test + @SinceRedisVersion(value="7.0.0") public void pexpireTime() { long unixTime = (System.currentTimeMillis()) + 10000; diff --git a/src/test/java/redis/clients/jedis/commands/unified/BitCommandsTestBase.java b/src/test/java/redis/clients/jedis/commands/unified/BitCommandsTestBase.java index 2101900889..7d559e1518 100644 --- a/src/test/java/redis/clients/jedis/commands/unified/BitCommandsTestBase.java +++ b/src/test/java/redis/clients/jedis/commands/unified/BitCommandsTestBase.java @@ -7,6 +7,10 @@ import static org.junit.Assert.fail; import java.util.List; + +import io.redis.test.annotations.SinceRedisVersion; +import io.redis.test.utils.RedisVersionRule; +import org.junit.Rule; import org.junit.Test; import redis.clients.jedis.Protocol; @@ -19,6 +23,7 @@ public abstract class BitCommandsTestBase extends UnifiedJedisCommandsTestBase { + public BitCommandsTestBase(RedisProtocol protocol) { super(protocol); } @@ -143,6 +148,7 @@ public void bitposWithNoMatchingBitExistWithinRange() { } @Test + @SinceRedisVersion(value="7.0.0", message="Starting with Redis version 7.0.0: Added the BYTE|BIT option.") public void bitposModifier() { jedis.set("mykey", "\\x00\\xff\\xf0"); assertEquals(0, jedis.bitpos("mykey", false)); @@ -168,6 +174,7 @@ public void setAndgetrange() { } @Test + @SinceRedisVersion(value="7.0.0", message="Starting with Redis version 7.0.0: Added the BYTE|BIT option.") public void bitCount() { jedis.setbit("foo", 16, true); jedis.setbit("foo", 24, true); diff --git a/src/test/java/redis/clients/jedis/commands/unified/HashesCommandsTestBase.java b/src/test/java/redis/clients/jedis/commands/unified/HashesCommandsTestBase.java index f84b0c2cc6..231b1c8aea 100644 --- a/src/test/java/redis/clients/jedis/commands/unified/HashesCommandsTestBase.java +++ b/src/test/java/redis/clients/jedis/commands/unified/HashesCommandsTestBase.java @@ -31,6 +31,7 @@ import java.util.Set; import java.util.stream.Collectors; +import io.redis.test.annotations.SinceRedisVersion; import org.junit.Test; import redis.clients.jedis.RedisProtocol; @@ -451,6 +452,7 @@ public void hscanCount() { } @Test + @SinceRedisVersion(value = "7.4.0", message = "NOVALUES flag (since Redis 7.4)") public void hscanNoValues() { jedis.hset("foo", "b", "y"); jedis.hset("foo", "a", "x"); @@ -474,6 +476,7 @@ public void hscanNoValues() { } @Test + @SinceRedisVersion(value = "7.4.0", message = "NOVALUES flag (since Redis 7.4)") public void hscanNoValuesMatch() { ScanParams params = new ScanParams(); params.match("a*"); @@ -506,6 +509,7 @@ public void hscanNoValuesMatch() { } @Test + @SinceRedisVersion(value = "7.4.0", message = "NOVALUES flag (since Redis 7.4)") public void hscanNoValuesCount() { ScanParams params = new ScanParams(); params.count(2); @@ -625,6 +629,7 @@ public void hrandfield() { } @Test + @SinceRedisVersion("7.4.0") public void hexpireAndHttl() { long seconds1 = 20; long seconds2 = 10; @@ -642,6 +647,7 @@ public void hexpireAndHttl() { } @Test + @SinceRedisVersion("7.4.0") public void hexpireAndHttlBinary() { long seconds1 = 20; long seconds2 = 10; @@ -659,6 +665,7 @@ public void hexpireAndHttlBinary() { } @Test + @SinceRedisVersion("7.4.0") public void hpexpireAndHpttl() { long millis1 = 20_000; long millis2 = 10_000; @@ -674,6 +681,7 @@ public void hpexpireAndHpttl() { } @Test + @SinceRedisVersion("7.4.0") public void hpexpireAndHpttlBinary() { long millis1 = 20_000; long millis2 = 10_000; @@ -689,6 +697,7 @@ public void hpexpireAndHpttlBinary() { } @Test + @SinceRedisVersion("7.4.0") public void hexpireAtAndExpireTime() { long currSeconds = System.currentTimeMillis() / 1000; long seconds1 = currSeconds + 20; @@ -707,6 +716,7 @@ public void hexpireAtAndExpireTime() { } @Test + @SinceRedisVersion("7.4.0") public void hexpireAtAndExpireTimeBinary() { long currSeconds = System.currentTimeMillis() / 1000; long seconds1 = currSeconds + 20; @@ -725,6 +735,7 @@ public void hexpireAtAndExpireTimeBinary() { } @Test + @SinceRedisVersion("7.4.0") public void hpexpireAtAndPexpireTime() { long currMillis = System.currentTimeMillis(); long unixMillis = currMillis + 20_000; @@ -740,6 +751,7 @@ public void hpexpireAtAndPexpireTime() { } @Test + @SinceRedisVersion("7.4.0") public void hpexpireAtAndPexpireTimeBinary() { long currMillis = System.currentTimeMillis(); long unixMillis = currMillis + 20_000; @@ -755,6 +767,7 @@ public void hpexpireAtAndPexpireTimeBinary() { } @Test + @SinceRedisVersion("7.4.0") public void hpersist() { long seconds = 20; @@ -769,6 +782,7 @@ public void hpersist() { } @Test + @SinceRedisVersion("7.4.0") public void hpersistBinary() { long seconds = 20; diff --git a/src/test/java/redis/clients/jedis/commands/unified/ListCommandsTestBase.java b/src/test/java/redis/clients/jedis/commands/unified/ListCommandsTestBase.java index 05c741de41..2d7372f000 100644 --- a/src/test/java/redis/clients/jedis/commands/unified/ListCommandsTestBase.java +++ b/src/test/java/redis/clients/jedis/commands/unified/ListCommandsTestBase.java @@ -13,6 +13,7 @@ import java.util.Collections; import java.util.List; +import io.redis.test.annotations.SinceRedisVersion; import org.junit.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -865,6 +866,7 @@ public void blmove() { } @Test + @SinceRedisVersion(value="7.0.0") public void lmpop() { String mylist1 = "mylist1"; String mylist2 = "mylist2"; @@ -890,6 +892,7 @@ public void lmpop() { } @Test + @SinceRedisVersion(value="7.0.0") public void blmpopSimple() { String mylist1 = "mylist1"; String mylist2 = "mylist2"; diff --git a/src/test/java/redis/clients/jedis/commands/unified/SetCommandsTestBase.java b/src/test/java/redis/clients/jedis/commands/unified/SetCommandsTestBase.java index d4c1456218..3f21a2f9f5 100644 --- a/src/test/java/redis/clients/jedis/commands/unified/SetCommandsTestBase.java +++ b/src/test/java/redis/clients/jedis/commands/unified/SetCommandsTestBase.java @@ -19,6 +19,7 @@ import java.util.List; import java.util.Set; +import io.redis.test.annotations.SinceRedisVersion; import org.junit.Test; import redis.clients.jedis.RedisProtocol; @@ -361,6 +362,7 @@ public void sinterstore() { } @Test + @SinceRedisVersion(value="7.0.0") public void sintercard() { jedis.sadd("foo", "a"); jedis.sadd("foo", "b"); diff --git a/src/test/java/redis/clients/jedis/commands/unified/SortedSetCommandsTestBase.java b/src/test/java/redis/clients/jedis/commands/unified/SortedSetCommandsTestBase.java index 126a884993..000a005eb6 100644 --- a/src/test/java/redis/clients/jedis/commands/unified/SortedSetCommandsTestBase.java +++ b/src/test/java/redis/clients/jedis/commands/unified/SortedSetCommandsTestBase.java @@ -7,6 +7,8 @@ import static redis.clients.jedis.util.AssertUtil.assertByteArrayListEquals; import java.util.*; + +import io.redis.test.annotations.SinceRedisVersion; import org.junit.Test; import redis.clients.jedis.RedisProtocol; @@ -454,6 +456,7 @@ public void zrank() { } @Test + @SinceRedisVersion(value="7.2.0") public void zrankWithScore() { jedis.zadd("foo", 1d, "a"); jedis.zadd("foo", 2d, "b"); @@ -1402,6 +1405,7 @@ public void zintertoreParams() { } @Test + @SinceRedisVersion(value="7.0.0") public void zintercard() { jedis.zadd("foo", 1, "a"); jedis.zadd("foo", 2, "b"); @@ -1644,6 +1648,7 @@ private Double getScoreFromByteMap(Map bhash, byte[] key) { } @Test + @SinceRedisVersion(value="7.0.0") public void zmpop() { jedis.zadd("foo", 1d, "a", ZAddParams.zAddParams().nx()); jedis.zadd("foo", 10d, "b", ZAddParams.zAddParams().nx()); @@ -1659,6 +1664,7 @@ public void zmpop() { } @Test + @SinceRedisVersion(value="7.0.0") public void bzmpopSimple() { jedis.zadd("foo", 1d, "a", ZAddParams.zAddParams().nx()); jedis.zadd("foo", 10d, "b", ZAddParams.zAddParams().nx()); diff --git a/src/test/java/redis/clients/jedis/commands/unified/StringValuesCommandsTestBase.java b/src/test/java/redis/clients/jedis/commands/unified/StringValuesCommandsTestBase.java index 8309978fad..f21876979e 100644 --- a/src/test/java/redis/clients/jedis/commands/unified/StringValuesCommandsTestBase.java +++ b/src/test/java/redis/clients/jedis/commands/unified/StringValuesCommandsTestBase.java @@ -6,6 +6,8 @@ import java.util.ArrayList; import java.util.List; + +import io.redis.test.annotations.SinceRedisVersion; import org.junit.Test; import redis.clients.jedis.RedisProtocol; @@ -244,6 +246,7 @@ public void psetex() { } @Test + @SinceRedisVersion(value="7.0.0") public void lcs() { jedis.mset("key1", "ohmytext", "key2", "mynewtext"); diff --git a/src/test/java/redis/clients/jedis/commands/unified/cluster/ClusterAllKindOfValuesCommandsTest.java b/src/test/java/redis/clients/jedis/commands/unified/cluster/ClusterAllKindOfValuesCommandsTest.java index e0f5c7af26..ca8be570ee 100644 --- a/src/test/java/redis/clients/jedis/commands/unified/cluster/ClusterAllKindOfValuesCommandsTest.java +++ b/src/test/java/redis/clients/jedis/commands/unified/cluster/ClusterAllKindOfValuesCommandsTest.java @@ -10,11 +10,16 @@ import java.util.HashSet; import java.util.Set; +import io.redis.test.utils.EnabledOnCommandRule; +import io.redis.test.utils.RedisVersionRule; import org.junit.After; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; +import redis.clients.jedis.DefaultJedisClientConfig; +import redis.clients.jedis.HostAndPorts; import redis.clients.jedis.RedisProtocol; import redis.clients.jedis.commands.unified.AllKindOfValuesCommandsTestBase; import redis.clients.jedis.params.ScanParams; @@ -23,6 +28,15 @@ @RunWith(Parameterized.class) public class ClusterAllKindOfValuesCommandsTest extends AllKindOfValuesCommandsTestBase { + @Rule + public RedisVersionRule versionRule = new RedisVersionRule( + HostAndPorts.getStableClusterServers().get(0), + DefaultJedisClientConfig.builder().password("cluster").build()); + @Rule + public EnabledOnCommandRule enabledOnCommandRule = new EnabledOnCommandRule( + HostAndPorts.getStableClusterServers().get(0), + DefaultJedisClientConfig.builder().password("cluster").build()); + public ClusterAllKindOfValuesCommandsTest(RedisProtocol protocol) { super(protocol); } diff --git a/src/test/java/redis/clients/jedis/commands/unified/cluster/ClusterBitCommandsTest.java b/src/test/java/redis/clients/jedis/commands/unified/cluster/ClusterBitCommandsTest.java index 078e8d8851..505722574b 100644 --- a/src/test/java/redis/clients/jedis/commands/unified/cluster/ClusterBitCommandsTest.java +++ b/src/test/java/redis/clients/jedis/commands/unified/cluster/ClusterBitCommandsTest.java @@ -2,12 +2,13 @@ import static org.junit.Assert.assertEquals; -import org.junit.After; -import org.junit.Before; -import org.junit.Ignore; -import org.junit.Test; +import io.redis.test.utils.EnabledOnCommandRule; +import io.redis.test.utils.RedisVersionRule; +import org.junit.*; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; +import redis.clients.jedis.DefaultJedisClientConfig; +import redis.clients.jedis.HostAndPorts; import redis.clients.jedis.RedisProtocol; import redis.clients.jedis.args.BitOP; import redis.clients.jedis.commands.unified.BitCommandsTestBase; @@ -16,6 +17,15 @@ @RunWith(Parameterized.class) public class ClusterBitCommandsTest extends BitCommandsTestBase { + @Rule + public RedisVersionRule versionRule = new RedisVersionRule( + HostAndPorts.getStableClusterServers().get(0) + ,DefaultJedisClientConfig.builder().password("cluster").build()); + @Rule + public EnabledOnCommandRule enabledOnCommandRule = new EnabledOnCommandRule( + HostAndPorts.getStableClusterServers().get(0), + DefaultJedisClientConfig.builder().password("cluster").build()); + public ClusterBitCommandsTest(RedisProtocol protocol) { super(protocol); } diff --git a/src/test/java/redis/clients/jedis/commands/unified/cluster/ClusterCommandsTestHelper.java b/src/test/java/redis/clients/jedis/commands/unified/cluster/ClusterCommandsTestHelper.java index 6c8f3e25cc..9fc35f96ab 100644 --- a/src/test/java/redis/clients/jedis/commands/unified/cluster/ClusterCommandsTestHelper.java +++ b/src/test/java/redis/clients/jedis/commands/unified/cluster/ClusterCommandsTestHelper.java @@ -14,7 +14,7 @@ static JedisCluster getCleanCluster(RedisProtocol protocol) { clearClusterData(); return new JedisCluster( Collections.singleton(HostAndPorts.getStableClusterServers().get(0)), - DefaultJedisClientConfig.builder().password("cluster").protocol(protocol).build()); + DefaultJedisClientConfig.builder().password("cluster").protocol(protocol).build()); } static void clearClusterData() { diff --git a/src/test/java/redis/clients/jedis/commands/unified/cluster/ClusterHashesCommandsTest.java b/src/test/java/redis/clients/jedis/commands/unified/cluster/ClusterHashesCommandsTest.java index a5590a2730..81a6889dd5 100644 --- a/src/test/java/redis/clients/jedis/commands/unified/cluster/ClusterHashesCommandsTest.java +++ b/src/test/java/redis/clients/jedis/commands/unified/cluster/ClusterHashesCommandsTest.java @@ -1,15 +1,29 @@ package redis.clients.jedis.commands.unified.cluster; +import io.redis.test.utils.EnabledOnCommandRule; +import io.redis.test.utils.RedisVersionRule; import org.junit.After; import org.junit.Before; +import org.junit.Rule; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; +import redis.clients.jedis.DefaultJedisClientConfig; +import redis.clients.jedis.HostAndPorts; import redis.clients.jedis.RedisProtocol; import redis.clients.jedis.commands.unified.HashesCommandsTestBase; @RunWith(Parameterized.class) public class ClusterHashesCommandsTest extends HashesCommandsTestBase { + @Rule + public RedisVersionRule versionRule = new RedisVersionRule( + HostAndPorts.getStableClusterServers().get(0), + DefaultJedisClientConfig.builder().password("cluster") .build()); + @Rule + public EnabledOnCommandRule enabledOnCommandRule = new EnabledOnCommandRule( + HostAndPorts.getStableClusterServers().get(0), + DefaultJedisClientConfig.builder().password("cluster").build()); + public ClusterHashesCommandsTest(RedisProtocol protocol) { super(protocol); } diff --git a/src/test/java/redis/clients/jedis/commands/unified/cluster/ClusterListCommandsTest.java b/src/test/java/redis/clients/jedis/commands/unified/cluster/ClusterListCommandsTest.java index 666752050b..ef24a93e3b 100644 --- a/src/test/java/redis/clients/jedis/commands/unified/cluster/ClusterListCommandsTest.java +++ b/src/test/java/redis/clients/jedis/commands/unified/cluster/ClusterListCommandsTest.java @@ -10,13 +10,19 @@ import java.util.Collections; import java.util.List; +import io.redis.test.annotations.SinceRedisVersion; +import io.redis.test.utils.EnabledOnCommandRule; +import io.redis.test.utils.RedisVersionRule; import org.junit.After; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import redis.clients.jedis.DefaultJedisClientConfig; +import redis.clients.jedis.HostAndPorts; import redis.clients.jedis.RedisProtocol; import redis.clients.jedis.args.ListDirection; import redis.clients.jedis.commands.unified.ListCommandsTestBase; @@ -27,6 +33,15 @@ public class ClusterListCommandsTest extends ListCommandsTestBase { private final Logger logger = LoggerFactory.getLogger(getClass()); + @Rule + public RedisVersionRule versionRule = new RedisVersionRule( + HostAndPorts.getStableClusterServers().get(0), + DefaultJedisClientConfig.builder().password("cluster").build()); + @Rule + public EnabledOnCommandRule enabledOnCommandRule = new EnabledOnCommandRule( + HostAndPorts.getStableClusterServers().get(0), + DefaultJedisClientConfig.builder().password("cluster").build()); + public ClusterListCommandsTest(RedisProtocol protocol) { super(protocol); } @@ -259,6 +274,7 @@ public void blmove() { } @Test + @SinceRedisVersion(value="7.0.0") public void lmpop() { String mylist1 = "mylist1{.}"; String mylist2 = "mylist2{.}"; @@ -284,6 +300,7 @@ public void lmpop() { } @Test + @SinceRedisVersion(value="7.0.0") public void blmpopSimple() { String mylist1 = "mylist1{.}"; String mylist2 = "mylist2{.}"; diff --git a/src/test/java/redis/clients/jedis/commands/unified/cluster/ClusterSetCommandsTest.java b/src/test/java/redis/clients/jedis/commands/unified/cluster/ClusterSetCommandsTest.java index 75d2b6bbdc..b71a7fe663 100644 --- a/src/test/java/redis/clients/jedis/commands/unified/cluster/ClusterSetCommandsTest.java +++ b/src/test/java/redis/clients/jedis/commands/unified/cluster/ClusterSetCommandsTest.java @@ -5,11 +5,17 @@ import java.util.HashSet; import java.util.Set; +import io.redis.test.annotations.SinceRedisVersion; +import io.redis.test.utils.EnabledOnCommandRule; +import io.redis.test.utils.RedisVersionRule; import org.junit.After; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; +import redis.clients.jedis.DefaultJedisClientConfig; +import redis.clients.jedis.HostAndPorts; import redis.clients.jedis.RedisProtocol; import redis.clients.jedis.commands.unified.SetCommandsTestBase; @@ -26,6 +32,15 @@ public ClusterSetCommandsTest(RedisProtocol protocol) { super(protocol); } + @Rule + public RedisVersionRule versionRule = new RedisVersionRule( + HostAndPorts.getStableClusterServers().get(0), + DefaultJedisClientConfig.builder().password("cluster").build()); + @Rule + public EnabledOnCommandRule enabledOnCommandRule = new EnabledOnCommandRule( + HostAndPorts.getStableClusterServers().get(0), + DefaultJedisClientConfig.builder().password("cluster").build()); + @Before public void setUp() { jedis = ClusterCommandsTestHelper.getCleanCluster(protocol); @@ -178,6 +193,7 @@ public void sdiffstore() { } @Test + @SinceRedisVersion(value="7.0.0") public void sintercard() { jedis.sadd("foo{.}", "a"); jedis.sadd("foo{.}", "b"); diff --git a/src/test/java/redis/clients/jedis/commands/unified/cluster/ClusterSortedSetCommandsTest.java b/src/test/java/redis/clients/jedis/commands/unified/cluster/ClusterSortedSetCommandsTest.java index b08d7b1773..095f4a24c7 100644 --- a/src/test/java/redis/clients/jedis/commands/unified/cluster/ClusterSortedSetCommandsTest.java +++ b/src/test/java/redis/clients/jedis/commands/unified/cluster/ClusterSortedSetCommandsTest.java @@ -10,11 +10,17 @@ import java.util.Collections; import java.util.List; +import io.redis.test.annotations.SinceRedisVersion; +import io.redis.test.utils.EnabledOnCommandRule; +import io.redis.test.utils.RedisVersionRule; import org.junit.After; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; +import redis.clients.jedis.DefaultJedisClientConfig; +import redis.clients.jedis.HostAndPorts; import redis.clients.jedis.RedisProtocol; import redis.clients.jedis.commands.unified.SortedSetCommandsTestBase; import redis.clients.jedis.params.ZAddParams; @@ -32,6 +38,15 @@ public class ClusterSortedSetCommandsTest extends SortedSetCommandsTestBase { final byte[] bb = { 0x0B }; final byte[] bc = { 0x0C }; + @Rule + public RedisVersionRule versionRule = new RedisVersionRule( + HostAndPorts.getStableClusterServers().get(0), + DefaultJedisClientConfig.builder().password("cluster").build()); + @Rule + public EnabledOnCommandRule enabledOnCommandRule = new EnabledOnCommandRule( + HostAndPorts.getStableClusterServers().get(0), + DefaultJedisClientConfig.builder().password("cluster").build()); + public ClusterSortedSetCommandsTest(RedisProtocol protocol) { super(protocol); } @@ -234,6 +249,7 @@ public void zrangestore() { } @Test + @SinceRedisVersion(value="7.0.0") public void zintercard() { jedis.zadd("foo{.}", 1, "a"); jedis.zadd("foo{.}", 2, "b"); diff --git a/src/test/java/redis/clients/jedis/commands/unified/cluster/ClusterStringValuesCommandsTest.java b/src/test/java/redis/clients/jedis/commands/unified/cluster/ClusterStringValuesCommandsTest.java index a8f980bd9a..8b2d62f05c 100644 --- a/src/test/java/redis/clients/jedis/commands/unified/cluster/ClusterStringValuesCommandsTest.java +++ b/src/test/java/redis/clients/jedis/commands/unified/cluster/ClusterStringValuesCommandsTest.java @@ -5,11 +5,17 @@ import java.util.ArrayList; import java.util.List; +import io.redis.test.annotations.SinceRedisVersion; +import io.redis.test.utils.EnabledOnCommandRule; +import io.redis.test.utils.RedisVersionRule; import org.junit.After; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; +import redis.clients.jedis.DefaultJedisClientConfig; +import redis.clients.jedis.HostAndPorts; import redis.clients.jedis.RedisProtocol; import redis.clients.jedis.commands.unified.StringValuesCommandsTestBase; import redis.clients.jedis.params.LCSParams; @@ -22,6 +28,15 @@ public ClusterStringValuesCommandsTest(RedisProtocol protocol) { super(protocol); } + @Rule + public RedisVersionRule versionRule = new RedisVersionRule( + HostAndPorts.getStableClusterServers().get(0), + DefaultJedisClientConfig.builder().password("cluster").build()); + @Rule + public EnabledOnCommandRule enabledOnCommandRule = new EnabledOnCommandRule( + HostAndPorts.getStableClusterServers().get(0), + DefaultJedisClientConfig.builder().password("cluster").build()); + @Before public void setUp() { jedis = ClusterCommandsTestHelper.getCleanCluster(protocol); @@ -84,6 +99,7 @@ public void msetnx() { } @Test + @SinceRedisVersion(value="7.0.0") public void lcs() { jedis.mset("key1{.}", "ohmytext", "key2{.}", "mynewtext"); diff --git a/src/test/java/redis/clients/jedis/commands/unified/pipeline/ListPipelineCommandsTest.java b/src/test/java/redis/clients/jedis/commands/unified/pipeline/ListPipelineCommandsTest.java index 36313c24ca..f9a17e8522 100644 --- a/src/test/java/redis/clients/jedis/commands/unified/pipeline/ListPipelineCommandsTest.java +++ b/src/test/java/redis/clients/jedis/commands/unified/pipeline/ListPipelineCommandsTest.java @@ -10,6 +10,7 @@ import java.util.List; +import io.redis.test.annotations.SinceRedisVersion; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; @@ -976,6 +977,7 @@ public void blmove() { } @Test + @SinceRedisVersion(value="7.0.0") public void lmpop() { String mylist1 = "mylist1"; String mylist2 = "mylist2"; @@ -1004,6 +1006,7 @@ public void lmpop() { } @Test + @SinceRedisVersion(value="7.0.0") public void blmpopSimple() { String mylist1 = "mylist1"; String mylist2 = "mylist2"; diff --git a/src/test/java/redis/clients/jedis/commands/unified/pipeline/PipelineCommandsTestBase.java b/src/test/java/redis/clients/jedis/commands/unified/pipeline/PipelineCommandsTestBase.java index 55dfec6e98..ecef988fa7 100644 --- a/src/test/java/redis/clients/jedis/commands/unified/pipeline/PipelineCommandsTestBase.java +++ b/src/test/java/redis/clients/jedis/commands/unified/pipeline/PipelineCommandsTestBase.java @@ -2,12 +2,13 @@ import java.util.Collection; +import io.redis.test.utils.EnabledOnCommandRule; +import io.redis.test.utils.RedisVersionRule; import org.junit.After; import org.junit.Before; +import org.junit.Rule; import org.junit.runners.Parameterized; -import redis.clients.jedis.JedisPooled; -import redis.clients.jedis.Pipeline; -import redis.clients.jedis.RedisProtocol; +import redis.clients.jedis.*; import redis.clients.jedis.commands.CommandsTestsParameters; import redis.clients.jedis.commands.unified.pooled.PooledCommandsTestHelper; @@ -29,6 +30,14 @@ public static Collection data() { protected final RedisProtocol protocol; + @Rule + public RedisVersionRule versionRule = new RedisVersionRule( + PooledCommandsTestHelper.nodeInfo.getHostAndPort(), + PooledCommandsTestHelper.nodeInfo.getClientConfigBuilder().build()); + @Rule + public EnabledOnCommandRule enabledOnCommandRule = new EnabledOnCommandRule( + HostAndPorts.getStableClusterServers().get(0), + DefaultJedisClientConfig.builder().password("cluster").build()); /** * The RESP protocol is to be injected by the subclasses, usually via JUnit * parameterized tests, because most of the subclassed tests are meant to be diff --git a/src/test/java/redis/clients/jedis/commands/unified/pipeline/SetPipelineCommandsTest.java b/src/test/java/redis/clients/jedis/commands/unified/pipeline/SetPipelineCommandsTest.java index 0dd0a18559..c311e1134c 100644 --- a/src/test/java/redis/clients/jedis/commands/unified/pipeline/SetPipelineCommandsTest.java +++ b/src/test/java/redis/clients/jedis/commands/unified/pipeline/SetPipelineCommandsTest.java @@ -20,6 +20,7 @@ import java.util.List; import java.util.Set; +import io.redis.test.annotations.SinceRedisVersion; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; @@ -378,6 +379,7 @@ public void sinterstore() { } @Test + @SinceRedisVersion(value="7.0.0") public void sintercard() { pipe.sadd("foo", "a"); pipe.sadd("foo", "b"); diff --git a/src/test/java/redis/clients/jedis/commands/unified/pipeline/SortedSetPipelineCommandsTest.java b/src/test/java/redis/clients/jedis/commands/unified/pipeline/SortedSetPipelineCommandsTest.java index a6915ce3a7..dfd46c7ae8 100644 --- a/src/test/java/redis/clients/jedis/commands/unified/pipeline/SortedSetPipelineCommandsTest.java +++ b/src/test/java/redis/clients/jedis/commands/unified/pipeline/SortedSetPipelineCommandsTest.java @@ -20,6 +20,7 @@ import java.util.Map; import java.util.stream.Collectors; +import io.redis.test.annotations.SinceRedisVersion; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; @@ -542,6 +543,7 @@ public void zrank() { } @Test + @SinceRedisVersion(value="7.2.0", message = "Starting with Redis version 7.2.0: Added the optional WITHSCORE argument.") public void zrankWithScore() { pipe.zadd("foo", 1d, "a"); pipe.zadd("foo", 2d, "b"); @@ -1516,6 +1518,7 @@ public void zintertoreParams() { } @Test + @SinceRedisVersion(value="7.0.0") public void zintercard() { pipe.zadd("foo", 1, "a"); pipe.zadd("foo", 2, "b"); @@ -1840,6 +1843,7 @@ private Double getScoreFromByteMap(Map bhash, byte[] key) { } @Test + @SinceRedisVersion(value="7.0.0") public void zmpop() { pipe.zadd("foo", 1d, "a", ZAddParams.zAddParams().nx()); pipe.zadd("foo", 10d, "b", ZAddParams.zAddParams().nx()); @@ -1863,6 +1867,7 @@ public void zmpop() { } @Test + @SinceRedisVersion(value="7.0.0") public void bzmpopSimple() { pipe.zadd("foo", 1d, "a", ZAddParams.zAddParams().nx()); pipe.zadd("foo", 10d, "b", ZAddParams.zAddParams().nx()); diff --git a/src/test/java/redis/clients/jedis/commands/unified/pipeline/StreamsPipelineCommandsTest.java b/src/test/java/redis/clients/jedis/commands/unified/pipeline/StreamsPipelineCommandsTest.java index af64b17576..78627e1cf3 100644 --- a/src/test/java/redis/clients/jedis/commands/unified/pipeline/StreamsPipelineCommandsTest.java +++ b/src/test/java/redis/clients/jedis/commands/unified/pipeline/StreamsPipelineCommandsTest.java @@ -27,6 +27,9 @@ import java.util.concurrent.atomic.AtomicReference; import java.util.stream.Collectors; +import io.redis.test.annotations.SinceRedisVersion; +import io.redis.test.utils.RedisVersion; +import io.redis.test.utils.RedisVersionUtil; import org.hamcrest.Matchers; import org.junit.Test; import org.junit.runner.RunWith; @@ -257,7 +260,7 @@ public void xaddParamsId() { pipe.xadd(key, XAddParams.xAddParams().id(new StreamEntryID(0, 1)), map); pipe.xadd(key, XAddParams.xAddParams().id(2, 3), map); - pipe.xadd(key, XAddParams.xAddParams().id(4), map); + pipe.xadd(key, XAddParams.xAddParams().id("4-0"), map); pipe.xadd(key, XAddParams.xAddParams().id("5-6"), map); pipe.xadd(key, XAddParams.xAddParams().id("7-8".getBytes()), map); pipe.xadd(key, XAddParams.xAddParams(), map); @@ -277,6 +280,31 @@ public void xaddParamsId() { greaterThan((StreamEntryID) results.get(4))); } + @Test + @SinceRedisVersion(value = "7.2.0", message = "Starting with Redis version 7.0.0: Added support for the -* explicit ID form.") + public void xaddParamsExplicitId() { + String key = "kk"; + Map map = singletonMap("ff", "vv"); + + pipe.xadd(key, XAddParams.xAddParams().id(new StreamEntryID(0, 1)), map); + pipe.xadd(key, XAddParams.xAddParams().id(2), map); + pipe.xadd(key, XAddParams.xAddParams().id(2), map); + pipe.xadd(key, XAddParams.xAddParams(), map); + + List results = pipe.syncAndReturnAll(); + + assertThat(results, contains( + equalTo(new StreamEntryID(0, 1)), + equalTo(new StreamEntryID(2, 0)), + equalTo(new StreamEntryID(2, 1)), + instanceOf(StreamEntryID.class) + )); + + assertThat((StreamEntryID) results.get(2), + greaterThan((StreamEntryID) results.get(1))); + } + + @Test public void xdel() { Map map1 = new HashMap<>(); @@ -1208,7 +1236,9 @@ public void xinfo() throws InterruptedException { assertEquals(MY_CONSUMER, consumersInfo.get(0).getName()); assertEquals(0L, consumersInfo.get(0).getPending()); assertThat(consumersInfo.get(0).getIdle(), Matchers.greaterThanOrEqualTo(0L)); - assertThat(consumersInfo.get(0).getInactive(), Matchers.any(Long.class)); + if (RedisVersionUtil.getRedisVersion(jedis).isGreaterThanOrEqualTo(RedisVersion.V7_0_0)) { + assertThat(consumersInfo.get(0).getInactive(), Matchers.any(Long.class)); + } // Consumer info test List consumerInfo = consumerInfoResponse.get(); @@ -1222,7 +1252,9 @@ public void xinfo() throws InterruptedException { assertEquals(MY_CONSUMER, consumerInfo.get(0).getName()); assertEquals(0L, consumerInfo.get(0).getPending()); assertThat(consumerInfo.get(0).getIdle(), Matchers.greaterThanOrEqualTo(0L)); - assertThat(consumerInfo.get(0).getInactive(), Matchers.any(Long.class)); + if (RedisVersionUtil.getRedisVersion(jedis).isGreaterThanOrEqualTo(RedisVersion.V7_0_0)) { + assertThat(consumerInfo.get(0).getInactive(), Matchers.any(Long.class)); + } // test with more groups and consumers pipe.xgroupCreate(STREAM_NAME, G2, StreamEntryID.XGROUP_LAST_ENTRY, false); @@ -1305,7 +1337,9 @@ public void xinfoStreamFullWithPending() { StreamConsumerFullInfo consumer = group.getConsumers().get(0); assertEquals("xreadGroup-consumer", consumer.getName()); assertThat(consumer.getSeenTime(), Matchers.greaterThanOrEqualTo(0L)); - assertThat(consumer.getActiveTime(), Matchers.greaterThanOrEqualTo(0L)); + if (RedisVersionUtil.getRedisVersion(jedis).isGreaterThanOrEqualTo(RedisVersion.V7_0_0)) { + assertThat(consumer.getActiveTime(), Matchers.greaterThanOrEqualTo(0L)); + } assertEquals(1, consumer.getPending().size()); List consumerPendingEntry = consumer.getPending().get(0); assertEquals(id1, consumerPendingEntry.get(0)); diff --git a/src/test/java/redis/clients/jedis/commands/unified/pooled/PooledAllKindOfValuesCommandsTest.java b/src/test/java/redis/clients/jedis/commands/unified/pooled/PooledAllKindOfValuesCommandsTest.java index a1b53de023..1e2fc5d94d 100644 --- a/src/test/java/redis/clients/jedis/commands/unified/pooled/PooledAllKindOfValuesCommandsTest.java +++ b/src/test/java/redis/clients/jedis/commands/unified/pooled/PooledAllKindOfValuesCommandsTest.java @@ -1,15 +1,28 @@ package redis.clients.jedis.commands.unified.pooled; +import io.redis.test.utils.EnabledOnCommandRule; +import io.redis.test.utils.RedisVersionRule; import org.junit.After; import org.junit.Before; +import org.junit.Rule; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; +import redis.clients.jedis.DefaultJedisClientConfig; +import redis.clients.jedis.HostAndPorts; import redis.clients.jedis.RedisProtocol; import redis.clients.jedis.commands.unified.AllKindOfValuesCommandsTestBase; @RunWith(Parameterized.class) public class PooledAllKindOfValuesCommandsTest extends AllKindOfValuesCommandsTestBase { + @Rule + public RedisVersionRule versionRule = new RedisVersionRule( + PooledCommandsTestHelper.nodeInfo.getHostAndPort(), + PooledCommandsTestHelper.nodeInfo.getClientConfigBuilder().build()); + @Rule + public EnabledOnCommandRule enabledOnCommandRule = new EnabledOnCommandRule( + PooledCommandsTestHelper.nodeInfo.getHostAndPort(), + PooledCommandsTestHelper.nodeInfo.getClientConfigBuilder().build()); public PooledAllKindOfValuesCommandsTest(RedisProtocol protocol) { super(protocol); diff --git a/src/test/java/redis/clients/jedis/commands/unified/pooled/PooledBitCommandsTest.java b/src/test/java/redis/clients/jedis/commands/unified/pooled/PooledBitCommandsTest.java index 5764a1bbfa..b1428d46ca 100644 --- a/src/test/java/redis/clients/jedis/commands/unified/pooled/PooledBitCommandsTest.java +++ b/src/test/java/redis/clients/jedis/commands/unified/pooled/PooledBitCommandsTest.java @@ -1,7 +1,10 @@ package redis.clients.jedis.commands.unified.pooled; +import io.redis.test.utils.EnabledOnCommandRule; +import io.redis.test.utils.RedisVersionRule; import org.junit.After; import org.junit.Before; +import org.junit.Rule; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; @@ -11,6 +14,15 @@ @RunWith(Parameterized.class) public class PooledBitCommandsTest extends BitCommandsTestBase { + @Rule + public RedisVersionRule versionRule = new RedisVersionRule( + PooledCommandsTestHelper.nodeInfo.getHostAndPort() + ,PooledCommandsTestHelper.nodeInfo.getClientConfigBuilder().build()); + @Rule + public EnabledOnCommandRule enabledOnCommandRule = new EnabledOnCommandRule( + PooledCommandsTestHelper.nodeInfo.getHostAndPort(), + PooledCommandsTestHelper.nodeInfo.getClientConfigBuilder().build()); + public PooledBitCommandsTest(RedisProtocol protocol) { super(protocol); } diff --git a/src/test/java/redis/clients/jedis/commands/unified/pooled/PooledCommandsTestHelper.java b/src/test/java/redis/clients/jedis/commands/unified/pooled/PooledCommandsTestHelper.java index ab69c57f4e..86e1f666de 100644 --- a/src/test/java/redis/clients/jedis/commands/unified/pooled/PooledCommandsTestHelper.java +++ b/src/test/java/redis/clients/jedis/commands/unified/pooled/PooledCommandsTestHelper.java @@ -4,7 +4,7 @@ public class PooledCommandsTestHelper { - private static final EndpointConfig nodeInfo = HostAndPorts.getRedisEndpoint("standalone0"); + public static final EndpointConfig nodeInfo = HostAndPorts.getRedisEndpoint("standalone0"); public static JedisPooled getPooled(RedisProtocol redisProtocol) { return new JedisPooled(nodeInfo.getHostAndPort(), nodeInfo.getClientConfigBuilder() diff --git a/src/test/java/redis/clients/jedis/commands/unified/pooled/PooledHashesCommandsTest.java b/src/test/java/redis/clients/jedis/commands/unified/pooled/PooledHashesCommandsTest.java index bb5741d967..95f4020549 100644 --- a/src/test/java/redis/clients/jedis/commands/unified/pooled/PooledHashesCommandsTest.java +++ b/src/test/java/redis/clients/jedis/commands/unified/pooled/PooledHashesCommandsTest.java @@ -1,7 +1,10 @@ package redis.clients.jedis.commands.unified.pooled; +import io.redis.test.utils.EnabledOnCommandRule; +import io.redis.test.utils.RedisVersionRule; import org.junit.After; import org.junit.Before; +import org.junit.Rule; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; @@ -11,6 +14,15 @@ @RunWith(Parameterized.class) public class PooledHashesCommandsTest extends HashesCommandsTestBase { + @Rule + public RedisVersionRule versionRule = new RedisVersionRule( + PooledCommandsTestHelper.nodeInfo.getHostAndPort(), + PooledCommandsTestHelper.nodeInfo.getClientConfigBuilder().build()); + @Rule + public EnabledOnCommandRule enabledOnCommandRule = new EnabledOnCommandRule( + PooledCommandsTestHelper.nodeInfo.getHostAndPort(), + PooledCommandsTestHelper.nodeInfo.getClientConfigBuilder().build()); + public PooledHashesCommandsTest(RedisProtocol protocol) { super(protocol); } diff --git a/src/test/java/redis/clients/jedis/commands/unified/pooled/PooledListCommandsTest.java b/src/test/java/redis/clients/jedis/commands/unified/pooled/PooledListCommandsTest.java index 5d38fe43d5..606e91bcc3 100644 --- a/src/test/java/redis/clients/jedis/commands/unified/pooled/PooledListCommandsTest.java +++ b/src/test/java/redis/clients/jedis/commands/unified/pooled/PooledListCommandsTest.java @@ -1,16 +1,29 @@ package redis.clients.jedis.commands.unified.pooled; +import io.redis.test.utils.EnabledOnCommandRule; +import io.redis.test.utils.RedisVersionRule; import org.junit.After; import org.junit.Before; +import org.junit.Rule; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; +import redis.clients.jedis.HostAndPorts; import redis.clients.jedis.RedisProtocol; import redis.clients.jedis.commands.unified.ListCommandsTestBase; @RunWith(Parameterized.class) public class PooledListCommandsTest extends ListCommandsTestBase { + @Rule + public RedisVersionRule versionRule = new RedisVersionRule( + PooledCommandsTestHelper.nodeInfo.getHostAndPort() + ,PooledCommandsTestHelper.nodeInfo.getClientConfigBuilder().build()); + @Rule + public EnabledOnCommandRule enabledOnCommandRule = new EnabledOnCommandRule( + PooledCommandsTestHelper.nodeInfo.getHostAndPort(), + PooledCommandsTestHelper.nodeInfo.getClientConfigBuilder().build()); + public PooledListCommandsTest(RedisProtocol protocol) { super(protocol); } diff --git a/src/test/java/redis/clients/jedis/commands/unified/pooled/PooledMiscellaneousTest.java b/src/test/java/redis/clients/jedis/commands/unified/pooled/PooledMiscellaneousTest.java index d6feb05fa3..4ef5361caa 100644 --- a/src/test/java/redis/clients/jedis/commands/unified/pooled/PooledMiscellaneousTest.java +++ b/src/test/java/redis/clients/jedis/commands/unified/pooled/PooledMiscellaneousTest.java @@ -7,22 +7,33 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; + +import io.redis.test.annotations.SinceRedisVersion; +import io.redis.test.utils.EnabledOnCommandRule; +import io.redis.test.utils.RedisVersionRule; import org.junit.After; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; -import redis.clients.jedis.AbstractPipeline; -import redis.clients.jedis.AbstractTransaction; -import redis.clients.jedis.RedisProtocol; -import redis.clients.jedis.Response; +import redis.clients.jedis.*; import redis.clients.jedis.commands.unified.UnifiedJedisCommandsTestBase; import redis.clients.jedis.exceptions.JedisDataException; @RunWith(Parameterized.class) public class PooledMiscellaneousTest extends UnifiedJedisCommandsTestBase { + @Rule + public RedisVersionRule versionRule = new RedisVersionRule( + PooledCommandsTestHelper.nodeInfo.getHostAndPort() + ,PooledCommandsTestHelper.nodeInfo.getClientConfigBuilder().build()); + @Rule + public EnabledOnCommandRule enabledOnCommandRule = new EnabledOnCommandRule( + PooledCommandsTestHelper.nodeInfo.getHostAndPort(), + PooledCommandsTestHelper.nodeInfo.getClientConfigBuilder().build()); + public PooledMiscellaneousTest(RedisProtocol protocol) { super(protocol); } @@ -148,6 +159,7 @@ public void broadcast() { } @Test + @SinceRedisVersion(value="7.0.0") public void broadcastWithError() { JedisDataException error = assertThrows(JedisDataException.class, () -> jedis.functionDelete("xyz")); diff --git a/src/test/java/redis/clients/jedis/commands/unified/pooled/PooledSetCommandsTest.java b/src/test/java/redis/clients/jedis/commands/unified/pooled/PooledSetCommandsTest.java index 2be9dbbf1c..f8a2aa05e6 100644 --- a/src/test/java/redis/clients/jedis/commands/unified/pooled/PooledSetCommandsTest.java +++ b/src/test/java/redis/clients/jedis/commands/unified/pooled/PooledSetCommandsTest.java @@ -1,7 +1,10 @@ package redis.clients.jedis.commands.unified.pooled; +import io.redis.test.utils.EnabledOnCommandRule; +import io.redis.test.utils.RedisVersionRule; import org.junit.After; import org.junit.Before; +import org.junit.Rule; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; @@ -11,6 +14,15 @@ @RunWith(Parameterized.class) public class PooledSetCommandsTest extends SetCommandsTestBase { + @Rule + public RedisVersionRule versionRule = new RedisVersionRule( + PooledCommandsTestHelper.nodeInfo.getHostAndPort(), + PooledCommandsTestHelper.nodeInfo.getClientConfigBuilder().build()); + @Rule + public EnabledOnCommandRule enabledOnCommandRule = new EnabledOnCommandRule( + PooledCommandsTestHelper.nodeInfo.getHostAndPort(), + PooledCommandsTestHelper.nodeInfo.getClientConfigBuilder().build()); + public PooledSetCommandsTest(RedisProtocol protocol) { super(protocol); } diff --git a/src/test/java/redis/clients/jedis/commands/unified/pooled/PooledSortedSetCommandsTest.java b/src/test/java/redis/clients/jedis/commands/unified/pooled/PooledSortedSetCommandsTest.java index c3d0b76ece..5499b0495f 100644 --- a/src/test/java/redis/clients/jedis/commands/unified/pooled/PooledSortedSetCommandsTest.java +++ b/src/test/java/redis/clients/jedis/commands/unified/pooled/PooledSortedSetCommandsTest.java @@ -1,7 +1,10 @@ package redis.clients.jedis.commands.unified.pooled; +import io.redis.test.utils.EnabledOnCommandRule; +import io.redis.test.utils.RedisVersionRule; import org.junit.After; import org.junit.Before; +import org.junit.Rule; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; @@ -11,6 +14,15 @@ @RunWith(Parameterized.class) public class PooledSortedSetCommandsTest extends SortedSetCommandsTestBase { + @Rule + public RedisVersionRule versionRule = new RedisVersionRule( + PooledCommandsTestHelper.nodeInfo.getHostAndPort(), + PooledCommandsTestHelper.nodeInfo.getClientConfigBuilder().build()); + @Rule + public EnabledOnCommandRule enabledOnCommandRule = new EnabledOnCommandRule( + PooledCommandsTestHelper.nodeInfo.getHostAndPort(), + PooledCommandsTestHelper.nodeInfo.getClientConfigBuilder().build()); + public PooledSortedSetCommandsTest(RedisProtocol protocol) { super(protocol); } diff --git a/src/test/java/redis/clients/jedis/commands/unified/pooled/PooledStringValuesCommandsTest.java b/src/test/java/redis/clients/jedis/commands/unified/pooled/PooledStringValuesCommandsTest.java index c9a1c39ae1..ab8da3db7a 100644 --- a/src/test/java/redis/clients/jedis/commands/unified/pooled/PooledStringValuesCommandsTest.java +++ b/src/test/java/redis/clients/jedis/commands/unified/pooled/PooledStringValuesCommandsTest.java @@ -1,7 +1,10 @@ package redis.clients.jedis.commands.unified.pooled; +import io.redis.test.utils.EnabledOnCommandRule; +import io.redis.test.utils.RedisVersionRule; import org.junit.After; import org.junit.Before; +import org.junit.Rule; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; @@ -10,6 +13,14 @@ @RunWith(Parameterized.class) public class PooledStringValuesCommandsTest extends StringValuesCommandsTestBase { + @Rule + public RedisVersionRule versionRule = new RedisVersionRule( + PooledCommandsTestHelper.nodeInfo.getHostAndPort(), + PooledCommandsTestHelper.nodeInfo.getClientConfigBuilder().build()); + @Rule + public EnabledOnCommandRule enabledOnCommandRule = new EnabledOnCommandRule( + PooledCommandsTestHelper.nodeInfo.getHostAndPort(), + PooledCommandsTestHelper.nodeInfo.getClientConfigBuilder().build()); public PooledStringValuesCommandsTest(RedisProtocol protocol) { super(protocol); diff --git a/src/test/java/redis/clients/jedis/csc/AllowAndDenyListCacheableTest.java b/src/test/java/redis/clients/jedis/csc/AllowAndDenyListCacheableTest.java index a0b7d68381..a2fbe4ea8a 100644 --- a/src/test/java/redis/clients/jedis/csc/AllowAndDenyListCacheableTest.java +++ b/src/test/java/redis/clients/jedis/csc/AllowAndDenyListCacheableTest.java @@ -3,12 +3,14 @@ import static java.util.Collections.singleton; import static org.junit.Assert.assertEquals; + import io.redis.test.annotations.SinceRedisVersion; import org.junit.Test; import redis.clients.jedis.JedisPooled; import redis.clients.jedis.Protocol; import redis.clients.jedis.csc.util.AllowAndDenyListWithStringKeys; +@SinceRedisVersion(value = "7.4.0", message = "Jedis client-side caching is only supported with Redis 7.4 or later.") public class AllowAndDenyListCacheableTest extends ClientSideCacheTestBase { private static CacheConfig createConfig(Cacheable cacheable) { diff --git a/src/test/java/redis/clients/jedis/csc/ClientSideCacheFunctionalityTest.java b/src/test/java/redis/clients/jedis/csc/ClientSideCacheFunctionalityTest.java index d2032e5d23..49cb6dafeb 100644 --- a/src/test/java/redis/clients/jedis/csc/ClientSideCacheFunctionalityTest.java +++ b/src/test/java/redis/clients/jedis/csc/ClientSideCacheFunctionalityTest.java @@ -1,5 +1,6 @@ package redis.clients.jedis.csc; +import static org.awaitility.Awaitility.await; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.aMapWithSize; import static org.hamcrest.Matchers.hasSize; @@ -8,23 +9,18 @@ import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; -import java.util.HashSet; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; +import java.util.*; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.locks.ReentrantLock; import java.util.stream.Collector; import java.util.stream.Collectors; +import io.redis.test.annotations.SinceRedisVersion; import org.hamcrest.Matchers; import org.junit.Assert; import org.junit.Test; @@ -34,6 +30,7 @@ import redis.clients.jedis.JedisPooled; import redis.clients.jedis.UnifiedJedis; +@SinceRedisVersion(value = "7.4.0", message = "Jedis client-side caching is only supported with Redis 7.4 or later.") public class ClientSideCacheFunctionalityTest extends ClientSideCacheTestBase { @Test // T.5.1 @@ -508,7 +505,7 @@ public void run() { @Test public void testNullValue() throws InterruptedException { int MAX_SIZE = 20; - String nonExisting = "non-existing-key"; + String nonExisting = "non-existing-key-"+ UUID.randomUUID().toString(); control.del(nonExisting); try (JedisPooled jedis = new JedisPooled(hnp, clientConfig.get(), CacheConfig.builder().maxSize(MAX_SIZE).build())) { @@ -529,8 +526,10 @@ public void testNullValue() throws InterruptedException { assertEquals(1, stats.getMissCount()); control.set(nonExisting, "bar"); - val = jedis.get(nonExisting); - assertEquals("bar", val); + await() + .atMost(5, TimeUnit.SECONDS) + .pollInterval(10, TimeUnit.MILLISECONDS) + .untilAsserted(() -> assertEquals("bar", jedis.get(nonExisting))); assertEquals(1, cache.getSize()); assertEquals("bar", cache.getCacheEntries().iterator().next().getValue()); assertEquals(1, stats.getHitCount()); diff --git a/src/test/java/redis/clients/jedis/csc/ClientSideCacheTestBase.java b/src/test/java/redis/clients/jedis/csc/ClientSideCacheTestBase.java index db53b085be..47f2d1142c 100644 --- a/src/test/java/redis/clients/jedis/csc/ClientSideCacheTestBase.java +++ b/src/test/java/redis/clients/jedis/csc/ClientSideCacheTestBase.java @@ -1,19 +1,19 @@ package redis.clients.jedis.csc; import java.util.function.Supplier; + +import io.redis.test.annotations.SinceRedisVersion; +import io.redis.test.utils.EnabledOnCommandRule; +import io.redis.test.utils.RedisVersionRule; import org.apache.commons.pool2.impl.GenericObjectPoolConfig; import org.junit.After; import org.junit.Before; -import redis.clients.jedis.Connection; -import redis.clients.jedis.ConnectionPoolConfig; -import redis.clients.jedis.EndpointConfig; -import redis.clients.jedis.HostAndPort; -import redis.clients.jedis.HostAndPorts; -import redis.clients.jedis.Jedis; -import redis.clients.jedis.JedisClientConfig; +import org.junit.Rule; +import redis.clients.jedis.*; -abstract class ClientSideCacheTestBase { +@SinceRedisVersion(value = "7.4.0", message = "Jedis client-side caching is only supported with Redis 7.4 or later.") +public abstract class ClientSideCacheTestBase { protected static final EndpointConfig endpoint = HostAndPorts.getRedisEndpoint("standalone1"); @@ -21,6 +21,15 @@ abstract class ClientSideCacheTestBase { protected Jedis control; + @Rule + public RedisVersionRule versionRule = new RedisVersionRule( + HostAndPorts.getRedisEndpoint("standalone1").getHostAndPort(), + HostAndPorts.getRedisEndpoint("standalone1").getClientConfigBuilder().build()); + @Rule + public EnabledOnCommandRule enabledOnCommandRule = new EnabledOnCommandRule( + HostAndPorts.getRedisEndpoint("standalone1").getHostAndPort(), + HostAndPorts.getRedisEndpoint("standalone1").getClientConfigBuilder().build()); + @Before public void setUp() throws Exception { control = new Jedis(hnp, endpoint.getClientConfigBuilder().build()); diff --git a/src/test/java/redis/clients/jedis/csc/JedisClusterClientSideCacheTest.java b/src/test/java/redis/clients/jedis/csc/JedisClusterClientSideCacheTest.java index 89114d154f..41d163e202 100644 --- a/src/test/java/redis/clients/jedis/csc/JedisClusterClientSideCacheTest.java +++ b/src/test/java/redis/clients/jedis/csc/JedisClusterClientSideCacheTest.java @@ -4,16 +4,14 @@ import java.util.Set; import java.util.function.Supplier; +import io.redis.test.annotations.SinceRedisVersion; +import io.redis.test.utils.RedisVersionRule; import org.apache.commons.pool2.impl.GenericObjectPoolConfig; -import redis.clients.jedis.Connection; -import redis.clients.jedis.ConnectionPoolConfig; -import redis.clients.jedis.DefaultJedisClientConfig; -import redis.clients.jedis.HostAndPort; -import redis.clients.jedis.HostAndPorts; -import redis.clients.jedis.JedisClientConfig; -import redis.clients.jedis.JedisCluster; +import org.junit.ClassRule; +import redis.clients.jedis.*; +@SinceRedisVersion(value = "7.4.0", message = "Jedis client-side caching is only supported with Redis 7.4 or later.") public class JedisClusterClientSideCacheTest extends UnifiedJedisClientSideCacheTestBase { private static final Set hnp = new HashSet<>(HostAndPorts.getStableClusterServers()); @@ -28,14 +26,16 @@ public class JedisClusterClientSideCacheTest extends UnifiedJedisClientSideCache return poolConfig; }; - @Override - protected JedisCluster createRegularJedis() { - return new JedisCluster(hnp, clientConfig.get()); - } + @ClassRule + public static RedisVersionRule versionRule = new RedisVersionRule(hnp.iterator().next(), clientConfig.get()); - @Override - protected JedisCluster createCachedJedis(CacheConfig cacheConfig) { - return new JedisCluster(hnp, clientConfig.get(), cacheConfig); - } + @Override + protected JedisCluster createRegularJedis() { + return new JedisCluster(hnp, clientConfig.get()); + } + @Override + protected JedisCluster createCachedJedis(CacheConfig cacheConfig) { + return new JedisCluster(hnp, clientConfig.get(), cacheConfig); + } } diff --git a/src/test/java/redis/clients/jedis/csc/JedisPooledClientSideCacheTest.java b/src/test/java/redis/clients/jedis/csc/JedisPooledClientSideCacheTest.java index d7b2bd4989..2bca0fe73a 100644 --- a/src/test/java/redis/clients/jedis/csc/JedisPooledClientSideCacheTest.java +++ b/src/test/java/redis/clients/jedis/csc/JedisPooledClientSideCacheTest.java @@ -1,13 +1,21 @@ package redis.clients.jedis.csc; +import io.redis.test.annotations.SinceRedisVersion; +import io.redis.test.utils.RedisVersionRule; import org.junit.BeforeClass; +import org.junit.ClassRule; import redis.clients.jedis.HostAndPorts; +@SinceRedisVersion(value = "7.4.0", message = "Jedis client-side caching is only supported with Redis 7.4 or later.") public class JedisPooledClientSideCacheTest extends JedisPooledClientSideCacheTestBase { + @ClassRule + public static RedisVersionRule versionRule = new RedisVersionRule( + HostAndPorts.getRedisEndpoint("standalone1").getHostAndPort(), + HostAndPorts.getRedisEndpoint("standalone1").getClientConfigBuilder().build()); + @BeforeClass public static void prepare() { endpoint = HostAndPorts.getRedisEndpoint("standalone1"); } - } diff --git a/src/test/java/redis/clients/jedis/csc/JedisPooledClientSideCacheTestBase.java b/src/test/java/redis/clients/jedis/csc/JedisPooledClientSideCacheTestBase.java index 133efcb3fc..d30ba58ba0 100644 --- a/src/test/java/redis/clients/jedis/csc/JedisPooledClientSideCacheTestBase.java +++ b/src/test/java/redis/clients/jedis/csc/JedisPooledClientSideCacheTestBase.java @@ -2,6 +2,8 @@ import static org.junit.Assert.assertEquals; +import io.redis.test.utils.RedisVersionRule; +import org.junit.ClassRule; import org.junit.Test; import redis.clients.jedis.EndpointConfig; diff --git a/src/test/java/redis/clients/jedis/csc/JedisSentineledClientSideCacheTest.java b/src/test/java/redis/clients/jedis/csc/JedisSentineledClientSideCacheTest.java index 82da0b14af..136448a73c 100644 --- a/src/test/java/redis/clients/jedis/csc/JedisSentineledClientSideCacheTest.java +++ b/src/test/java/redis/clients/jedis/csc/JedisSentineledClientSideCacheTest.java @@ -4,33 +4,42 @@ import java.util.HashSet; import java.util.Set; -import redis.clients.jedis.DefaultJedisClientConfig; -import redis.clients.jedis.HostAndPort; -import redis.clients.jedis.HostAndPorts; -import redis.clients.jedis.JedisClientConfig; -import redis.clients.jedis.JedisSentineled; +import org.junit.BeforeClass; +import redis.clients.jedis.*; +import io.redis.test.utils.RedisVersion; + +import static org.junit.Assume.assumeTrue; +import static io.redis.test.utils.RedisVersionUtil.getRedisVersion; public class JedisSentineledClientSideCacheTest extends UnifiedJedisClientSideCacheTestBase { - private static final String MASTER_NAME = "mymaster"; + private static final String MASTER_NAME = "mymaster"; - protected static final HostAndPort sentinel1 = HostAndPorts.getSentinelServers().get(1); - protected static final HostAndPort sentinel2 = HostAndPorts.getSentinelServers().get(3); + protected static final HostAndPort sentinel1 = HostAndPorts.getSentinelServers().get(1); + protected static final HostAndPort sentinel2 = HostAndPorts.getSentinelServers().get(3); - private static final Set sentinels = new HashSet<>(Arrays.asList(sentinel1, sentinel2)); + private static final Set sentinels = new HashSet<>(Arrays.asList(sentinel1, sentinel2)); - private static final JedisClientConfig masterClientConfig = DefaultJedisClientConfig.builder().resp3().password("foobared").build(); + private static final JedisClientConfig masterClientConfig = DefaultJedisClientConfig.builder().resp3().password("foobared").build(); - private static final JedisClientConfig sentinelClientConfig = DefaultJedisClientConfig.builder().resp3().build(); + private static final JedisClientConfig sentinelClientConfig = DefaultJedisClientConfig.builder().resp3().build(); - @Override - protected JedisSentineled createRegularJedis() { - return new JedisSentineled(MASTER_NAME, masterClientConfig, sentinels, sentinelClientConfig); - } + @Override + protected JedisSentineled createRegularJedis() { + return new JedisSentineled(MASTER_NAME, masterClientConfig, sentinels, sentinelClientConfig); + } - @Override - protected JedisSentineled createCachedJedis(CacheConfig cacheConfig) { - return new JedisSentineled(MASTER_NAME, masterClientConfig, cacheConfig, sentinels, sentinelClientConfig); - } + @Override + protected JedisSentineled createCachedJedis(CacheConfig cacheConfig) { + return new JedisSentineled(MASTER_NAME, masterClientConfig, cacheConfig, sentinels, sentinelClientConfig); + } + @BeforeClass + public static void prepare() { + try (JedisSentineled sentinelClient = new JedisSentineled(MASTER_NAME, masterClientConfig, sentinels, sentinelClientConfig); + Jedis master = new Jedis(sentinelClient.getCurrentMaster(),masterClientConfig)) { + assumeTrue("Jedis Client side caching is only supported with 'Redis 7.4' or later.", + getRedisVersion(master).isGreaterThanOrEqualTo(RedisVersion.V7_4)); + } + } } diff --git a/src/test/java/redis/clients/jedis/csc/SSLJedisPooledClientSideCacheTest.java b/src/test/java/redis/clients/jedis/csc/SSLJedisPooledClientSideCacheTest.java index b8df3910cd..8458e0ffdb 100644 --- a/src/test/java/redis/clients/jedis/csc/SSLJedisPooledClientSideCacheTest.java +++ b/src/test/java/redis/clients/jedis/csc/SSLJedisPooledClientSideCacheTest.java @@ -1,16 +1,35 @@ package redis.clients.jedis.csc; +import org.junit.AfterClass; + +import redis.clients.jedis.Jedis; +import io.redis.test.utils.RedisVersion; +import redis.clients.jedis.util.TlsUtil; import org.junit.BeforeClass; import redis.clients.jedis.HostAndPorts; -import redis.clients.jedis.SSLJedisTest; + +import java.nio.file.Path; + +import static org.junit.Assume.assumeTrue; +import static io.redis.test.utils.RedisVersionUtil.getRedisVersion; public class SSLJedisPooledClientSideCacheTest extends JedisPooledClientSideCacheTestBase { @BeforeClass public static void prepare() { - SSLJedisTest.setupTrustStore(); + Path trusStorePath = TlsUtil.createAndSaveEnvTruststore("redis1-2-5-10-sentinel", "changeit"); + TlsUtil.setCustomTrustStore(trusStorePath, "changeit"); endpoint = HostAndPorts.getRedisEndpoint("standalone0-tls"); + + try (Jedis jedis = new Jedis(endpoint.getHostAndPort(), endpoint.getClientConfigBuilder().build())) { + assumeTrue("Jedis Client side caching is only supported with 'Redis 7.4' or later.", + getRedisVersion(jedis).isGreaterThanOrEqualTo(RedisVersion.V7_4)); + } } + @AfterClass + public static void teardownTrustStore() { + TlsUtil.restoreOriginalTrustStore(); + } } diff --git a/src/test/java/redis/clients/jedis/csc/UnifiedJedisClientSideCacheTestBase.java b/src/test/java/redis/clients/jedis/csc/UnifiedJedisClientSideCacheTestBase.java index e07bf578ee..1dd11c238d 100644 --- a/src/test/java/redis/clients/jedis/csc/UnifiedJedisClientSideCacheTestBase.java +++ b/src/test/java/redis/clients/jedis/csc/UnifiedJedisClientSideCacheTestBase.java @@ -9,9 +9,7 @@ import java.util.List; import java.util.concurrent.TimeUnit; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; +import org.junit.*; import redis.clients.jedis.UnifiedJedis; diff --git a/src/test/java/redis/clients/jedis/modules/RedisModuleCommandsTestBase.java b/src/test/java/redis/clients/jedis/modules/RedisModuleCommandsTestBase.java index 9e21fba23f..9d51928aa7 100644 --- a/src/test/java/redis/clients/jedis/modules/RedisModuleCommandsTestBase.java +++ b/src/test/java/redis/clients/jedis/modules/RedisModuleCommandsTestBase.java @@ -70,12 +70,5 @@ public void setUp() { public void tearDown() throws Exception { client.close(); } -// -// public static void tearDown() { -// client.close(); -// } -// -// protected static Connection createConnection() { -// return new Connection(hnp); -// } + } diff --git a/src/test/java/redis/clients/jedis/util/RedisVersionUtil.java b/src/test/java/redis/clients/jedis/util/RedisVersionUtil.java deleted file mode 100644 index fed3055b4f..0000000000 --- a/src/test/java/redis/clients/jedis/util/RedisVersionUtil.java +++ /dev/null @@ -1,32 +0,0 @@ -package redis.clients.jedis.util; - -import redis.clients.jedis.EndpointConfig; -import redis.clients.jedis.Jedis; - -public class RedisVersionUtil { - - public static Integer getRedisMajorVersionNumber(EndpointConfig endpoint) { - String completeVersion = null; - - try (Jedis jedis = new Jedis(endpoint.getHostAndPort(), - endpoint.getClientConfigBuilder().build())) { - String info = jedis.info("server"); - String[] splitted = info.split("\\s+|:"); - for (int i = 0; i < splitted.length; i++) { - if (splitted[i].equalsIgnoreCase("redis_version")) { - completeVersion = splitted[i + 1]; - break; - } - } - } - - if (completeVersion == null) { - return null; - } - return Integer.parseInt(completeVersion.substring(0, completeVersion.indexOf("."))); - } - - public static boolean checkRedisMajorVersionNumber(int minVersion, EndpointConfig endpoint) { - return getRedisMajorVersionNumber(endpoint) >= minVersion; - } -} diff --git a/src/test/java/redis/clients/jedis/util/TestEnvUtil.java b/src/test/java/redis/clients/jedis/util/TestEnvUtil.java new file mode 100644 index 0000000000..a9da3bd029 --- /dev/null +++ b/src/test/java/redis/clients/jedis/util/TestEnvUtil.java @@ -0,0 +1,19 @@ +package redis.clients.jedis.util; + +import java.util.Optional; + +public class TestEnvUtil { + private static final String TEST_ENV_PROVIDER = System.getenv().getOrDefault("TEST_ENV_PROVIDER", "docker"); + private static final String TESTMODULE_SO = Optional.ofNullable(System.getenv("TESTMODULE_SO")) + .orElseGet(() -> isContainerEnv() + ? "/redis/work/modules/testmodule.so" + : "/tmp/testmodule.so"); + + public static String testModuleSo() { + return TESTMODULE_SO; + } + + public static boolean isContainerEnv() { + return TEST_ENV_PROVIDER.equals("docker"); + } +} diff --git a/src/test/java/redis/clients/jedis/util/TlsUtil.java b/src/test/java/redis/clients/jedis/util/TlsUtil.java new file mode 100644 index 0000000000..dedb89a57b --- /dev/null +++ b/src/test/java/redis/clients/jedis/util/TlsUtil.java @@ -0,0 +1,286 @@ +package redis.clients.jedis.util; + +import javax.net.ssl.*; +import java.io.*; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.security.*; +import java.security.cert.CertificateException; +import java.security.cert.CertificateFactory; +import java.security.cert.X509Certificate; + +import static org.junit.Assert.assertTrue; + +public class TlsUtil { + + private static final String TRUST_STORE_PROPERTY = "javax.net.ssl.trustStore"; + private static final String TRUST_STORE_PASSWORD_PROPERTY = "javax.net.ssl.trustStorePassword"; + private static final String TRUST_STORE_TYPE_PROPERTY = "javax.net.ssl.trustStoreType"; + + private static String originalTrustStore; + private static String originalTrustStoreType; + private static String originalTrustStorePassword; + + private static final String TRUST_STORE_TYPE = "JCEKS"; + private static final String CERTIFICATE_TYPE = "X.509"; + + private static final String TEST_WORK_FOLDER = System.getenv().getOrDefault("TEST_WORK_FOLDER", "./redis-env/"); + private static final String TEST_TRUSTSTORE = System.getenv().getOrDefault("TEST_TRUSTSTORE", "truststore.jceks"); + private static final String TEST_CA_CERT = System.getenv().getOrDefault("TEST_CA_CERT", "work/tls/ca.crt"); + + public static void setCustomTrustStore(Path customTrustStorePath, String customTrustStorePassword) { + // Store original properties + originalTrustStore = System.getProperty(TRUST_STORE_PROPERTY); + originalTrustStorePassword = System.getProperty(TRUST_STORE_PASSWORD_PROPERTY); + originalTrustStoreType = System.getProperty(TRUST_STORE_TYPE_PROPERTY, "JKS"); + // Set new properties for the custom truststore + System.setProperty(TRUST_STORE_PROPERTY, customTrustStorePath.toAbsolutePath().toString()); + System.setProperty(TRUST_STORE_TYPE_PROPERTY, TRUST_STORE_TYPE); + if (customTrustStorePassword != null) { + System.setProperty(TRUST_STORE_PASSWORD_PROPERTY, customTrustStorePassword); + } else { + System.clearProperty(TRUST_STORE_PASSWORD_PROPERTY); + } + } + + public static void restoreOriginalTrustStore() { + // Restore original properties + if (originalTrustStore != null) { + System.setProperty(TRUST_STORE_PROPERTY, originalTrustStore); + } else { + System.clearProperty(TRUST_STORE_PROPERTY); + } + + if ( originalTrustStoreType != null) { + System.setProperty(TRUST_STORE_TYPE_PROPERTY, originalTrustStoreType); + } else { + System.clearProperty(TRUST_STORE_TYPE_PROPERTY); + } + + if (originalTrustStorePassword != null) { + System.setProperty(TRUST_STORE_PASSWORD_PROPERTY, originalTrustStorePassword); + } else { + System.clearProperty(TRUST_STORE_PASSWORD_PROPERTY); + } + } + + public static Path envCa(String env) { + if (TestEnvUtil.isContainerEnv()) { + return Paths.get(TEST_WORK_FOLDER, env, TEST_CA_CERT); + } else { + return Paths.get("src/test/resources/private.crt"); + } + } + + public static Path envTruststore(String env) { + + if (TestEnvUtil.isContainerEnv()) { + return Paths.get(TEST_WORK_FOLDER, env + '-' + TEST_TRUSTSTORE); + } else { + return Paths.get("src/test/resources/truststore.jceks"); + } + } + + /** + * Loads the CA certificate from the provided file path. + * + * @param caCertPath Path to the CA certificate file (ca.crt). + * @return Loaded X509Certificate. + * @throws Exception If there's an error reading the certificate. + */ + public static X509Certificate loadCACertificate(String caCertPath) { + File caCertFile = new File(caCertPath); + try (FileInputStream fis = new FileInputStream(caCertFile)) { + CertificateFactory certificateFactory = CertificateFactory.getInstance(CERTIFICATE_TYPE); + return (X509Certificate) certificateFactory.generateCertificate(fis); + } catch (CertificateException | IOException e) { + throw new RuntimeException(e); + } + } + + public static Path createAndSaveEnvTruststore(String env, String truststorePassword) { + String caPath = envCa(env).toAbsolutePath().toString(); + String trustStorePath = envTruststore(env).toAbsolutePath().toString(); + return createAndSaveTruststore(caPath, trustStorePath, truststorePassword); + } + + /** + * Creates a truststore with the given CA certificate. + * + * @param caCertPath Path to the CA certificate file (ca.crt). + * @return A KeyStore object containing the CA certificate. + * @throws Exception If there's an error creating the truststore. + */ + public static KeyStore createTruststore(String caCertPath) throws Exception { + X509Certificate caCert = loadCACertificate(caCertPath); + + KeyStore trustStore = KeyStore.getInstance(TRUST_STORE_TYPE); + trustStore.load(null, null); + trustStore.setCertificateEntry("ca-cert", caCert); + + return trustStore; + } + + /** + * Creates a truststore with the given CA certificate and saves it to the specified path. + * + * @param caCertPath Path to the CA certificate file (ca.crt). + * @param truststorePath Path to save the generated truststore. + * @param truststorePassword Password for the truststore. + * @return Path to the saved truststore file. + * @throws Exception If there's an error creating or saving the truststore. + */ + public static Path createAndSaveTruststore(String caCertPath, String truststorePath, String truststorePassword) { + try { + KeyStore trustStore = createTruststore(caCertPath); + + // Save the truststore to the specified path + try (FileOutputStream fos = new FileOutputStream(truststorePath)) { + trustStore.store(fos, truststorePassword.toCharArray()); + } catch (IOException e) { + throw new RuntimeException("Failed to save truststore to " + truststorePath + ": " + e.getMessage(), e); + } + } catch (Exception e) { + throw new RuntimeException("Failed to create and save truststore: " + e.getMessage(), e); + } + + return Paths.get(truststorePath); + } + + + /** + * Creates an SSLSocketFactory that trusts all certificates in truststore.jceks. + * for given test environment + */ + public static SSLSocketFactory sslSocketFactoryForEnv(String envName){ + return sslSocketFactory(envCa(envName)); + } + + /** + * Returns SSLSocketFactory configured with Truststore containing provided CA cert + */ + public static SSLSocketFactory sslSocketFactory(Path caCertPath){ + + KeyStore truststore = null; + try { + truststore = createTruststore(caCertPath.toAbsolutePath().toString()); + + + TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance("PKIX"); + trustManagerFactory.init(truststore); + TrustManager[] trustManagers = trustManagerFactory.getTrustManagers(); + + SSLContext sslContext = SSLContext.getInstance("TLS"); + sslContext.init(null, trustManagers, new SecureRandom()); + return sslContext.getSocketFactory(); + } catch (Exception e) { + throw new RuntimeException("Failed to initialise SslSocketFactory for " + caCertPath, e); + } + + } + + /** + * Creates an SSLSocketFactory with a trust manager that does not trust any certificates. + */ + public static SSLSocketFactory createTrustNoOneSslSocketFactory() throws Exception { + TrustManager[] unTrustManagers = new TrustManager[]{new X509TrustManager() { + public X509Certificate[] getAcceptedIssuers() { + return new X509Certificate[0]; + } + + public void checkClientTrusted(X509Certificate[] chain, String authType) { + throw new RuntimeException(new InvalidAlgorithmParameterException()); + } + + public void checkServerTrusted(X509Certificate[] chain, String authType) { + throw new RuntimeException(new InvalidAlgorithmParameterException()); + } + }}; + SSLContext sslContext = SSLContext.getInstance("TLS"); + sslContext.init(null, unTrustManagers, new SecureRandom()); + return sslContext.getSocketFactory(); + } + + public static class LocalhostVerifier extends BasicHostnameVerifier { + @Override + public boolean verify(String hostname, SSLSession session) { + if (hostname.equals("127.0.0.1")) { + hostname = "localhost"; + } + return super.verify(hostname, session); + } + } + + /** + * Very basic hostname verifier implementation for testing. NOT recommended for production. + */ + public static class BasicHostnameVerifier implements HostnameVerifier { + + private static final String COMMON_NAME_RDN_PREFIX = "CN="; + + @Override + public boolean verify(String hostname, SSLSession session) { + X509Certificate peerCertificate; + try { + peerCertificate = (X509Certificate) session.getPeerCertificates()[0]; + } catch (SSLPeerUnverifiedException e) { + throw new IllegalStateException("The session does not contain a peer X.509 certificate.", e); + } + String peerCertificateCN = getCommonName(peerCertificate); + return hostname.equals(peerCertificateCN); + } + + private String getCommonName(X509Certificate peerCertificate) { + String subjectDN = peerCertificate.getSubjectDN().getName(); + String[] dnComponents = subjectDN.split(","); + for (String dnComponent : dnComponents) { + dnComponent = dnComponent.trim(); + if (dnComponent.startsWith(COMMON_NAME_RDN_PREFIX)) { + return dnComponent.substring(COMMON_NAME_RDN_PREFIX.length()); + } + } + throw new IllegalArgumentException("The certificate has no common name."); + } + } + + public static SSLSocketFactory createTrustAllSslSocketFactory() { + try { + TrustManager[] trustAllCerts = new TrustManager[]{ + new X509TrustManager() { + public X509Certificate[] getAcceptedIssuers() { + return new X509Certificate[0]; + } + + public void checkClientTrusted(X509Certificate[] chain, String authType) { + // Trust all clients + } + + public void checkServerTrusted(X509Certificate[] chain, String authType) { + // Trust all servers + } + } + }; + + SSLContext sslContext = SSLContext.getInstance("TLS"); + sslContext.init(null, trustAllCerts, new SecureRandom()); + return sslContext.getSocketFactory(); + } catch (GeneralSecurityException e) { + throw new RuntimeException("Failed to create a trust-all SSL socket factory", e); + } + } + + public static void main(String[] args) { + try { + + String truststorePassword = null; + + String caCertPath = "./work/redis1-2-5-10-sentinel/work/tls/ca.crt"; + String truststorePath = "./work/redis1-2-5-10-sentinel/work/truststore.jceks"; + Path truststore = createAndSaveTruststore(caCertPath, truststorePath, "change_me"); + System.out.println("Truststore saved at: " + truststore.toAbsolutePath()); + + } catch (Exception e) { + e.printStackTrace(); + } + } +} \ No newline at end of file diff --git a/src/test/resources/Arch/sentinel5/config/node-sentinel5/redis.conf b/src/test/resources/Arch/sentinel5/config/node-sentinel5/redis.conf new file mode 100644 index 0000000000..f3104c5ba6 --- /dev/null +++ b/src/test/resources/Arch/sentinel5/config/node-sentinel5/redis.conf @@ -0,0 +1,11 @@ +port 26383 +tls-port 36383 +tls-auth-clients no +user default off +user sentinel on allcommands allkeys allchannels >foobared +sentinel monitor aclmaster 127.0.0.1 6387 1 +sentinel auth-user aclmaster acljedis +sentinel auth-pass aclmaster fizzbuzz +sentinel down-after-milliseconds aclmaster 2000 +sentinel failover-timeout aclmaster 120000 +sentinel parallel-syncs aclmaster 1 \ No newline at end of file diff --git a/src/test/resources/env/.env b/src/test/resources/env/.env new file mode 100644 index 0000000000..ef6dc4179c --- /dev/null +++ b/src/test/resources/env/.env @@ -0,0 +1,7 @@ +CLIENT_LIBS_TEST_IMAGE=redislabs/client-libs-test:8.0-M01 +REDIS_IMAGE=redis:8.0-M01 +REDIS_ENV_CONF_DIR=./ +REDIS_MODULES_DIR=/tmp +REDIS_ENV_WORK_DIR=./redis-env-work + +ENABLE_MODULE_COMMAND_DIRECTIVE=--enable-module-command yes \ No newline at end of file diff --git a/src/test/resources/env/.env.v6.2.16 b/src/test/resources/env/.env.v6.2.16 new file mode 100644 index 0000000000..c285604372 --- /dev/null +++ b/src/test/resources/env/.env.v6.2.16 @@ -0,0 +1,8 @@ +CLIENT_LIBS_TEST_IMAGE=redislabs/client-libs-test:8.0-M01 +REDIS_IMAGE=redis:6.2.16 +REDIS_ENV_CONF_DIR=./ +REDIS_MODULES_DIR=/tmp +REDIS_ENV_WORK_DIR=./redis-env-work + +#REMOVE UNSUPPORTED DIRECTIVE +ENABLE_MODULE_COMMAND_DIRECTIVE= \ No newline at end of file diff --git a/src/test/resources/env/cluster-unbound/config/node-7379-8379/redis.conf b/src/test/resources/env/cluster-unbound/config/node-7379-8379/redis.conf new file mode 100644 index 0000000000..03a685ca8b --- /dev/null +++ b/src/test/resources/env/cluster-unbound/config/node-7379-8379/redis.conf @@ -0,0 +1,9 @@ +bind 0.0.0.0 +port 7379 +tls-port 8379 +requirepass cluster +tls-auth-clients no +cluster-node-timeout 15000 +save "" +appendonly no +cluster-enabled yes \ No newline at end of file diff --git a/src/test/resources/env/cluster-unbound/config/node-7380-8380/redis.conf b/src/test/resources/env/cluster-unbound/config/node-7380-8380/redis.conf new file mode 100644 index 0000000000..7991ea74bd --- /dev/null +++ b/src/test/resources/env/cluster-unbound/config/node-7380-8380/redis.conf @@ -0,0 +1,8 @@ +bind 0.0.0.0 +requirepass cluster +port 7380 +tls-port 8380 +tls-auth-clients no +save "" +appendonly no +cluster-enabled yes \ No newline at end of file diff --git a/src/test/resources/env/cluster-unbound/config/node-7381-8381/redis.conf b/src/test/resources/env/cluster-unbound/config/node-7381-8381/redis.conf new file mode 100644 index 0000000000..27e5faa26d --- /dev/null +++ b/src/test/resources/env/cluster-unbound/config/node-7381-8381/redis.conf @@ -0,0 +1,9 @@ +bind 0.0.0.0 +port 7381 +tls-port 8381 +requirepass cluster +tls-auth-clients no +cluster-node-timeout 15000 +save "" +appendonly no +cluster-enabled yes \ No newline at end of file diff --git a/src/test/resources/env/cluster-unbound/config/node-7382-8382/redis.conf b/src/test/resources/env/cluster-unbound/config/node-7382-8382/redis.conf new file mode 100644 index 0000000000..dbe32b3b7d --- /dev/null +++ b/src/test/resources/env/cluster-unbound/config/node-7382-8382/redis.conf @@ -0,0 +1,9 @@ +bind 0.0.0.0 +requirepass cluster +port 7382 +tls-port 8382 +tls-auth-clients no +cluster-node-timeout 15000 +save "" +appendonly no +cluster-enabled yes \ No newline at end of file diff --git a/src/test/resources/env/cluster-unbound/config/node-7383-8383/redis.conf b/src/test/resources/env/cluster-unbound/config/node-7383-8383/redis.conf new file mode 100644 index 0000000000..6928409ec7 --- /dev/null +++ b/src/test/resources/env/cluster-unbound/config/node-7383-8383/redis.conf @@ -0,0 +1,9 @@ +bind 0.0.0.0 +port 7383 +tls-port 8383 +requirepass cluster +tls-auth-clients no +cluster-node-timeout 15000 +save "" +appendonly no +cluster-enabled yes \ No newline at end of file diff --git a/src/test/resources/env/config/redis6-7/node-sentinel-26381-36381/redis.conf b/src/test/resources/env/config/redis6-7/node-sentinel-26381-36381/redis.conf new file mode 100644 index 0000000000..4268f7b62d --- /dev/null +++ b/src/test/resources/env/config/redis6-7/node-sentinel-26381-36381/redis.conf @@ -0,0 +1,9 @@ +port 26380 +tls-port 36380 +tls-auth-clients no +user deploy on allcommands allkeys >verify +sentinel monitor mymaster 127.0.0.1 6381 1 +sentinel auth-pass mymaster foobared +sentinel down-after-milliseconds mymaster 2000 +sentinel parallel-syncs mymaster 1 +sentinel failover-timeout mymaster 120000 \ No newline at end of file diff --git a/src/test/resources/env/docker-compose.yml b/src/test/resources/env/docker-compose.yml new file mode 100644 index 0000000000..c5858527ea --- /dev/null +++ b/src/test/resources/env/docker-compose.yml @@ -0,0 +1,160 @@ +services: + redis1-2-5-10-sentinel: + sysctls: + - net.ipv6.conf.all.disable_ipv6=1 + image: ${CLIENT_LIBS_TEST_IMAGE:-redislabs/client-libs-test:8.0-M01} + container_name: redis1-2-5-10-sentinel + #network_mode: host + environment: + - REDIS_CLUSTER=no + - REDIS_PASSWORD=foobared + - TLS_ENABLED=yes + ports: + - "6379:6379" + - "6380:6380" + - "6383:6383" + - "6386:6386" + - "6390:6390" + - "6391:6391" + - "26379:26379" # sentinel + - "36379:36379" # sentinel tls + command: ${ENABLE_MODULE_COMMAND_DIRECTIVE} + volumes: + - ${REDIS_ENV_CONF_DIR}/redis1-2-5-10-sentinel/config:/redis/config:r + - ${REDIS_ENV_WORK_DIR}/redis1-2-5-10-sentinel/work:/redis/work:rw + redis3-4-sentinel: + sysctls: + - net.ipv6.conf.all.disable_ipv6=1 + image: ${CLIENT_LIBS_TEST_IMAGE:-redislabs/client-libs-test:8.0-M01} + container_name: redis3-4-sentinel + #network_mode: host + environment: + - REDIS_CLUSTER=no + - TLS_ENABLED=yes + - REDIS_PASSWORD=foobared + ports: + - "6381:6381" + - "16381:16381" + - "6382:6382" + - "16382:16382" + - "26380:26380" # sentinel + - "36380:36380" # sentinel tls + - "26382:26382" # sentinel + - "36382:36382" # sentinel tls + volumes: + - ${REDIS_ENV_CONF_DIR}/redis3-4-sentinel/config:/redis/config:r + - ${REDIS_ENV_WORK_DIR}/redis3-4-sentinel/work:/redis/work:rw + + redis6-7-sentinel: + sysctls: + - net.ipv6.conf.all.disable_ipv6=1 + image: ${CLIENT_LIBS_TEST_IMAGE:-redislabs/client-libs-test:8.0-M01} + container_name: redis6-7-sentinel + #network_mode: host + environment: + - REDIS_CLUSTER=no + - REDIS_PASSWORD=foobared + ports: + - "6384:6384" + - "6385:6385" + - "26381:26381" # sentinel + - "36381:36381" # sentinel tls + volumes: + - ${REDIS_ENV_CONF_DIR}/redis6-7-sentinel/config:/redis/config:r + - ${REDIS_ENV_WORK_DIR}/redis6-7-sentinel/work:/redis/work:rw + redis9-sentinel: + sysctls: + - net.ipv6.conf.all.disable_ipv6=1 + image: ${CLIENT_LIBS_TEST_IMAGE:-redislabs/client-libs-test:8.0-M01} + container_name: redis9-sentinel + #network_mode: host + environment: + - REDIS_CLUSTER=no + - REDIS_CLIENT_USER=deploy + - REDIS_CLIENT_PASSWORD=verify + - TLS_ENABLED=yes + ports: + - "6387:6387" + - "16387:16387" + - "26383:26383" # sentinel + - "36383:36383" # sentinel tls + volumes: + - ${REDIS_ENV_CONF_DIR}/redis9-sentinel/config:/redis/config:r + - ${REDIS_ENV_WORK_DIR}/redis9-sentinel/work:/redis/work:rw + + redis10-11: + sysctls: + - net.ipv6.conf.all.disable_ipv6=1 + image: ${CLIENT_LIBS_TEST_IMAGE:-redislabs/client-libs-test:8.0-M01} + container_name: redis10-11 + #network_mode: host + environment: + - REDIS_CLUSTER=no + ports: + - "6388:6388" + - "6389:6389" + volumes: + - ${REDIS_ENV_CONF_DIR}/redis10-11/config:/redis/config:r + - ${REDIS_ENV_WORK_DIR}/redis10-11/work:/redis/work:rw + redis-unavailable: + sysctls: + - net.ipv6.conf.all.disable_ipv6=1 + image: ${CLIENT_LIBS_TEST_IMAGE:-redislabs/client-libs-test:8.0-M01} + container_name: redis-unavailable-1 + #network_mode: host + environment: + - REDIS_CLUSTER=no + - PORT=6400 + ports: + - "6400:6400" + volumes: + - ${REDIS_ENV_WORK_DIR}/redis-unavailable/work:/redis/work:rw + + cluster-unbound: + sysctls: + - net.ipv6.conf.all.disable_ipv6=1 + image: ${CLIENT_LIBS_TEST_IMAGE:-redislabs/client-libs-test:8.0-M01} + container_name: cluster-unbound-1 + environment: + - TLS_ENABLED=yes + - REDIS_PASSWORD=cluster + ports: + - "7379-7383:7379-7383" + - "8379-8383:8379-8383" + volumes: + - ${REDIS_ENV_CONF_DIR}/cluster-unbound/config:/redis/config:r + - ${REDIS_ENV_WORK_DIR}/cluster-unbound/work:/redis/work:rw + + cluster-stable: + sysctls: + - net.ipv6.conf.all.disable_ipv6=1 + image: ${CLIENT_LIBS_TEST_IMAGE:-redislabs/client-libs-test:8.0-M01} + container_name: cluster-stable-1 + #network_mode: host + command: --cluster-announce-ip 127.0.0.1 + environment: + - REDIS_CLUSTER=yes + - REDIS_PASSWORD=cluster + - PORT=7479 + - NODES=3 + - REPLICAS=0 + ports: + - "7479-7481:7479-7481" + volumes: + - ${REDIS_ENV_CONF_DIR}/cluster-stable/config:/redis/config:r + - ${REDIS_ENV_WORK_DIR}/cluster-stable/work:/redis/work:rw + + jedis-stack: + image: redis/redis-stack-server:edge + container_name: jedis-stack + ports: + - "6479:6379" +#todo find a way to connect from mac os host to exposed unix socket in container +# redis_uds: +# image: redis:8.0-M01 +# container_name: redis_uds +# #network_mode: host +# command: redis-server /etc/redis.conf +# volumes: +# - "./redis_uds/config/node-0/redis.conf:/etc/redis.conf" +# - "./work/redis_uds/work:/tmp/docker/" \ No newline at end of file diff --git a/src/test/resources/env/endpoint.map b/src/test/resources/env/endpoint.map new file mode 100644 index 0000000000..5070ec216b --- /dev/null +++ b/src/test/resources/env/endpoint.map @@ -0,0 +1,20 @@ +REDIS1_CONF 6379:6390 "standalone0" : "redis://localhost:6379" +REDIS1_CONF 6379:6390 "standalone0-acl" : "redis://localhost:6379", +REDIS1_CONF 6379:6390 "standalone0-tls" : "rediss://localhost:6390" +REDIS1_CONF 6379:6390 "standalone0-acl-tls" : "rediss://localhost:6390" +------------ +REDIS2_CONF 6380 "standalone1" : "redis://localhost:6380" +REDIS3_CONF 6381:16381 "standalone2-primary" : "redis://localhost:6381" +REDIS4_CONF 6382:16382 "standalone3-replica-of-standalone2" : "redis://localhost:6382" + (slaveof 6381) +REDIS5_CONF 6383 "standalone4-replica-of-standalone1" : "redis://localhost:6383" + (slaveof 6379) +REDIS6_CONF 6384 "standalone5-primary" : "redis://localhost:6384" +REDIS7_CONF 6385 "standalone6-replica-of-standalone5" : "redis://localhost:6385" + (slaveof 6384) +REDIS8_CONF 6386 "standalone7-with-lfu-policy" : "redis://localhost:6386" +REDIS9_CONF 6387:16387 ? missing +REDIS10_CONF 6388 "standalone9" : "redis://localhost:6388" +REDIS11_CONF 6389 "standalone10-replica-of-standalone9" : "redis://localhost:6389" + (replicaof 6388) +jedis-stack 6479 "modules-docker" : "redis://localhost:6479" diff --git a/src/test/resources/env/redis-uds/config/node-0/redis.conf b/src/test/resources/env/redis-uds/config/node-0/redis.conf new file mode 100644 index 0000000000..dbcfd53b5f --- /dev/null +++ b/src/test/resources/env/redis-uds/config/node-0/redis.conf @@ -0,0 +1,2 @@ +unixsocket /tmp/docker/redis.sock +unixsocketperm 777 \ No newline at end of file diff --git a/src/test/resources/env/redis1-2-5-10-sentinel/config/node-6379-6390/redis.conf b/src/test/resources/env/redis1-2-5-10-sentinel/config/node-6379-6390/redis.conf new file mode 100644 index 0000000000..d0b4944b89 --- /dev/null +++ b/src/test/resources/env/redis1-2-5-10-sentinel/config/node-6379-6390/redis.conf @@ -0,0 +1,11 @@ +port 6379 +tls-port 6390 +requirepass foobared +user deploy on allcommands allkeys >verify +user acljedis on allcommands allkeys >fizzbuzz +save "" +appendonly no +tls-auth-clients no + # Not supported on v6. provided as argument on node start +# enable-module-command yes +client-output-buffer-limit pubsub 256k 128k 5 \ No newline at end of file diff --git a/src/test/resources/env/redis1-2-5-10-sentinel/config/node-6380/redis.conf b/src/test/resources/env/redis1-2-5-10-sentinel/config/node-6380/redis.conf new file mode 100644 index 0000000000..8c1e0b9873 --- /dev/null +++ b/src/test/resources/env/redis1-2-5-10-sentinel/config/node-6380/redis.conf @@ -0,0 +1,8 @@ +protected-mode no +port 6380 +user deploy on allcommands allkeys >verify +requirepass foobared +pidfile /tmp/redis2.pid +logfile /tmp/redis2.log +save "" +appendonly no \ No newline at end of file diff --git a/src/test/resources/env/redis1-2-5-10-sentinel/config/node-6383-6391/redis.conf b/src/test/resources/env/redis1-2-5-10-sentinel/config/node-6383-6391/redis.conf new file mode 100644 index 0000000000..7ec2b09d49 --- /dev/null +++ b/src/test/resources/env/redis1-2-5-10-sentinel/config/node-6383-6391/redis.conf @@ -0,0 +1,9 @@ +port 6383 +tls-port 6391 +user deploy on allcommands allkeys >verify +requirepass foobared +masterauth foobared +tls-auth-clients no +save "" +appendonly no +slaveof localhost 6379 \ No newline at end of file diff --git a/src/test/resources/env/redis1-2-5-10-sentinel/config/node-6386/redis.conf b/src/test/resources/env/redis1-2-5-10-sentinel/config/node-6386/redis.conf new file mode 100644 index 0000000000..df268e026c --- /dev/null +++ b/src/test/resources/env/redis1-2-5-10-sentinel/config/node-6386/redis.conf @@ -0,0 +1,6 @@ +protected-mode no +port 6386 +user deploy on allcommands allkeys >verify +save "" +appendonly no +maxmemory-policy allkeys-lfu \ No newline at end of file diff --git a/src/test/resources/env/redis1-2-5-10-sentinel/config/node-sentinel-26379-36379/redis.conf b/src/test/resources/env/redis1-2-5-10-sentinel/config/node-sentinel-26379-36379/redis.conf new file mode 100644 index 0000000000..8ff68c24cb --- /dev/null +++ b/src/test/resources/env/redis1-2-5-10-sentinel/config/node-sentinel-26379-36379/redis.conf @@ -0,0 +1,8 @@ +port 26379 +tls-port 36379 +tls-auth-clients no +sentinel monitor mymaster 127.0.0.1 6379 1 +sentinel auth-pass mymaster foobared +sentinel down-after-milliseconds mymaster 2000 +sentinel failover-timeout mymaster 120000 +sentinel parallel-syncs mymaster 1 \ No newline at end of file diff --git a/src/test/resources/env/redis10-11/config/node-6388/redis.conf b/src/test/resources/env/redis10-11/config/node-6388/redis.conf new file mode 100644 index 0000000000..deab0b333c --- /dev/null +++ b/src/test/resources/env/redis10-11/config/node-6388/redis.conf @@ -0,0 +1,3 @@ +port 6388 +save "" +appendonly no \ No newline at end of file diff --git a/src/test/resources/env/redis10-11/config/node-6389/redis.conf b/src/test/resources/env/redis10-11/config/node-6389/redis.conf new file mode 100644 index 0000000000..bff698c1bd --- /dev/null +++ b/src/test/resources/env/redis10-11/config/node-6389/redis.conf @@ -0,0 +1,4 @@ +port 6389 +save "" +appendonly no +replicaof localhost 6388 \ No newline at end of file diff --git a/src/test/resources/env/redis3-4-sentinel/config/node-6381-16381/redis.conf b/src/test/resources/env/redis3-4-sentinel/config/node-6381-16381/redis.conf new file mode 100644 index 0000000000..2a8295ba66 --- /dev/null +++ b/src/test/resources/env/redis3-4-sentinel/config/node-6381-16381/redis.conf @@ -0,0 +1,6 @@ +port 6381 +tls-port 16381 +requirepass foobared +masterauth foobared +save "" +appendonly no \ No newline at end of file diff --git a/src/test/resources/env/redis3-4-sentinel/config/node-6382-16382/redis.conf b/src/test/resources/env/redis3-4-sentinel/config/node-6382-16382/redis.conf new file mode 100644 index 0000000000..e0b7881f88 --- /dev/null +++ b/src/test/resources/env/redis3-4-sentinel/config/node-6382-16382/redis.conf @@ -0,0 +1,7 @@ +port 6382 +tls-port 16382 +requirepass foobared +masterauth foobared +save "" +appendonly no +slaveof localhost 6381 \ No newline at end of file diff --git a/src/test/resources/env/redis3-4-sentinel/config/node-sentinel-26380-36380/redis.conf b/src/test/resources/env/redis3-4-sentinel/config/node-sentinel-26380-36380/redis.conf new file mode 100644 index 0000000000..1f15e2bb71 --- /dev/null +++ b/src/test/resources/env/redis3-4-sentinel/config/node-sentinel-26380-36380/redis.conf @@ -0,0 +1,8 @@ +port 26380 +tls-port 36380 +tls-auth-clients no +sentinel monitor mymaster 127.0.0.1 6381 1 +sentinel auth-pass mymaster foobared +sentinel down-after-milliseconds mymaster 2000 +sentinel parallel-syncs mymaster 1 +sentinel failover-timeout mymaster 120000 \ No newline at end of file diff --git a/src/test/resources/env/redis3-4-sentinel/config/node-sentinel-26382-36382/redis.conf b/src/test/resources/env/redis3-4-sentinel/config/node-sentinel-26382-36382/redis.conf new file mode 100644 index 0000000000..971b7115a5 --- /dev/null +++ b/src/test/resources/env/redis3-4-sentinel/config/node-sentinel-26382-36382/redis.conf @@ -0,0 +1,8 @@ +port 26382 +tls-port 36382 +tls-auth-clients no +sentinel monitor mymaster 127.0.0.1 6381 1 +sentinel auth-pass mymaster foobared +sentinel down-after-milliseconds mymaster 2000 +sentinel parallel-syncs mymaster 1 +sentinel failover-timeout mymaster 120000 \ No newline at end of file diff --git a/src/test/resources/env/redis6-7-sentinel/config/node-6384/redis.conf b/src/test/resources/env/redis6-7-sentinel/config/node-6384/redis.conf new file mode 100644 index 0000000000..6c9d2f611c --- /dev/null +++ b/src/test/resources/env/redis6-7-sentinel/config/node-6384/redis.conf @@ -0,0 +1,5 @@ +port 6384 +requirepass foobared +masterauth foobared +save "" +appendonly no \ No newline at end of file diff --git a/src/test/resources/env/redis6-7-sentinel/config/node-6385/redis.conf b/src/test/resources/env/redis6-7-sentinel/config/node-6385/redis.conf new file mode 100644 index 0000000000..b9820488cf --- /dev/null +++ b/src/test/resources/env/redis6-7-sentinel/config/node-6385/redis.conf @@ -0,0 +1,6 @@ +port 6385 +requirepass foobared +masterauth foobared +save "" +appendonly no +slaveof localhost 6384 \ No newline at end of file diff --git a/src/test/resources/env/redis6-7-sentinel/config/node-sentinel-26381-36381/redis.conf b/src/test/resources/env/redis6-7-sentinel/config/node-sentinel-26381-36381/redis.conf new file mode 100644 index 0000000000..e71d81bf5f --- /dev/null +++ b/src/test/resources/env/redis6-7-sentinel/config/node-sentinel-26381-36381/redis.conf @@ -0,0 +1,7 @@ +port 26381 +protected-mode no +sentinel monitor mymasterfailover 127.0.0.1 6384 1 +sentinel auth-pass mymasterfailover foobared +sentinel down-after-milliseconds mymasterfailover 2000 +sentinel failover-timeout mymasterfailover 120000 +sentinel parallel-syncs mymasterfailover 1 \ No newline at end of file diff --git a/src/test/resources/env/redis9-sentinel/config/node-6387-16387/redis.conf b/src/test/resources/env/redis9-sentinel/config/node-6387-16387/redis.conf new file mode 100644 index 0000000000..fae3fa276d --- /dev/null +++ b/src/test/resources/env/redis9-sentinel/config/node-6387-16387/redis.conf @@ -0,0 +1,9 @@ +port 6387 +tls-port 16387 +tls-auth-clients no +user default off +user deploy on allcommands allkeys >verify +user acljedis on allcommands allkeys >fizzbuzz +save "" +appendonly no +client-output-buffer-limit pubsub 256k 128k 5 \ No newline at end of file diff --git a/src/test/resources/env/redis9-sentinel/config/node-sentinel-26383-36383/redis.conf b/src/test/resources/env/redis9-sentinel/config/node-sentinel-26383-36383/redis.conf new file mode 100644 index 0000000000..c190c2c4f8 --- /dev/null +++ b/src/test/resources/env/redis9-sentinel/config/node-sentinel-26383-36383/redis.conf @@ -0,0 +1,12 @@ +port 26383 +tls-port 36383 +tls-auth-clients no +user default off +user deploy on allcommands allkeys >verify +user sentinel on allcommands allkeys allchannels >foobared +sentinel monitor aclmaster 127.0.0.1 6387 1 +sentinel auth-user aclmaster acljedis +sentinel auth-pass aclmaster fizzbuzz +sentinel down-after-milliseconds aclmaster 2000 +sentinel failover-timeout aclmaster 120000 +sentinel parallel-syncs aclmaster 1 \ No newline at end of file diff --git a/src/test/resources/private.crt b/src/test/resources/private.crt new file mode 100644 index 0000000000..4c560cfeae --- /dev/null +++ b/src/test/resources/private.crt @@ -0,0 +1,21 @@ +-----BEGIN CERTIFICATE----- +MIIDhzCCAm+gAwIBAgIJALGtKQQvKbGEMA0GCSqGSIb3DQEBCwUAMFoxCzAJBgNV +BAYTAlVTMQswCQYDVQQIDAJDQTEWMBQGA1UEBwwNU2FuIEZyYW5jaXNjbzESMBAG +A1UECgwJVHJ1U3BoZXJlMRIwEAYDVQQDDAlsb2NhbGhvc3QwHhcNMTYwMTA5MjEw +ODMyWhcNMjYwMTA2MjEwODMyWjBaMQswCQYDVQQGEwJVUzELMAkGA1UECAwCQ0Ex +FjAUBgNVBAcMDVNhbiBGcmFuY2lzY28xEjAQBgNVBAoMCVRydVNwaGVyZTESMBAG +A1UEAwwJbG9jYWxob3N0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA +4nb/9hXI5iFXIHh34gpvQMGTqTCf2im/FCvXYoDer/FTLKxdr3Bw0Cj9NarMpVZO +fBRykVdKir8v1+Gv0mUVwHkEJ2Mouxo6mqDQEKRt9G/gbzcC+AoMEUsnV4tKaptX +dWDLcZU9oer6wLicmAyV+4vlUJn+j6ADXBk4pJT+wbG/oawJ0op4vQNeVcJPr7Bc +fMLG60UOTABEPBM6YWqVe2pLPX4a4ec84kcFDLjRfU9ZTy+fD+BP5XQjIJyjyjTS +iOSy+qQM8Yk0CjJ/zfysChApCefP1/kOiIeStuE8jkmkVW6atprb9Vr+E5jnHKeh +SmZmXq9BRjF2OyUMG8m4DwIDAQABo1AwTjAdBgNVHQ4EFgQUSg+P0noapK2yuFoH +v9xe5HNYNiYwHwYDVR0jBBgwFoAUSg+P0noapK2yuFoHv9xe5HNYNiYwDAYDVR0T +BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAakaOSczmzRoPg3etzWiKTYht+CbA +/UtOdRxwTTHLauDUtGo6YTT+HG4tItwNxI9n3CZvcX68fNTi99fxzrHddiAwnCPv +DqEv2ASxed95/6QZIzPVFT7GZZDOUrwFkC01vawaVyl0f8UbeOWCly7eetu3mV2r +VFNPxPPT622cGn8uSqnpN1cQ4LdsDpVUR+YDAxIB8YbsWgtl79evwcnTWINy3CSc +QjAYQe1aC/kAs3VfIymdu9xEv2Er9NOidUx23RD54jrFCXNUYEBnSc2yi7YSTTzT +4cKnp7wuVdacd1noQRZFEEsVd6tmtiJKZhdllJ21Rb5g1q50dBlbq25hnw== +-----END CERTIFICATE----- \ No newline at end of file