Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added Missing Client Tests. #127

113 changes: 113 additions & 0 deletions java/integTest/src/test/java/glide/SharedClientTests.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
/** Copyright GLIDE-for-Redis Project Contributors - SPDX Identifier: Apache-2.0 */
package glide;

import static glide.TestUtilities.commonClientConfig;
import static glide.TestUtilities.commonClusterClientConfig;
import static glide.TestUtilities.getRandomString;
import static glide.api.BaseClient.OK;
import static org.junit.jupiter.api.Assertions.assertEquals;

import glide.api.BaseClient;
import glide.api.RedisClient;
import glide.api.RedisClusterClient;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.stream.Stream;
import lombok.Getter;
import lombok.SneakyThrows;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Timeout;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;

@Timeout(25)
public class SharedClientTests {

private static RedisClient standaloneClient = null;
private static RedisClusterClient clusterClient = null;

@Getter private static List<Arguments> clients;

@BeforeAll
@SneakyThrows
public static void init() {
standaloneClient = RedisClient.CreateClient(commonClientConfig().build()).get();
clusterClient =
RedisClusterClient.CreateClient(commonClusterClientConfig().requestTimeout(5000).build())
.get();

clients = List.of(Arguments.of(standaloneClient), Arguments.of(clusterClient));
}

@AfterAll
@SneakyThrows
public static void teardown() {
standaloneClient.close();
clusterClient.close();
}

@SneakyThrows
@ParameterizedTest
@MethodSource("getClients")
public void send_and_receive_large_values(BaseClient client) {
int length = 1 << 16;
String key = getRandomString(length);
String value = getRandomString(length);

assertEquals(length, key.length());
assertEquals(length, value.length());
assertEquals(OK, client.set(key, value).get());
assertEquals(value, client.get(key).get());
}

@SneakyThrows
@ParameterizedTest
@MethodSource("getClients")
public void send_and_receive_non_ascii_unicode(BaseClient client) {
String key = "foo";
String value = "שלום hello 汉字";

assertEquals(OK, client.set(key, value).get());
assertEquals(value, client.get(key).get());
}

private static Stream<Arguments> clientAndDataSize() {
return Stream.of(
Arguments.of(standaloneClient, 100),
Arguments.of(standaloneClient, 1 << 16),
Arguments.of(clusterClient, 100),
Arguments.of(clusterClient, 1 << 16));
}

@ParameterizedTest
@MethodSource("clientAndDataSize")
public void client_can_handle_concurrent_workload(BaseClient client, int valueSize) {
ExecutorService executorService = Executors.newCachedThreadPool();
CompletableFuture[] futures = new CompletableFuture[100];

for (int i = 0; i < 100; i++) {
futures[i] =
CompletableFuture.runAsync(
() -> {
String key = getRandomString(valueSize);
String value = getRandomString(valueSize);
try {
assertEquals(OK, client.set(key, value).get());
assertEquals(value, client.get(key).get());
} catch (InterruptedException | ExecutionException e) {
throw new RuntimeException(e);
}
},
executorService);
}

CompletableFuture.allOf(futures).join();

executorService.shutdown();
}
}
53 changes: 53 additions & 0 deletions java/integTest/src/test/java/glide/TestUtilities.java
Original file line number Diff line number Diff line change
@@ -1,9 +1,18 @@
/** Copyright GLIDE-for-Redis Project Contributors - SPDX Identifier: Apache-2.0 */
package glide;

import static glide.TestConfiguration.CLUSTER_PORTS;
import static glide.TestConfiguration.STANDALONE_PORTS;
import static org.junit.jupiter.api.Assertions.fail;

import glide.api.models.ClusterValue;
import glide.api.models.configuration.NodeAddress;
import glide.api.models.configuration.RedisClientConfiguration;
import glide.api.models.configuration.RedisClusterClientConfiguration;
import java.security.SecureRandom;
import java.util.HashMap;
import java.util.Map;
import java.util.stream.Collectors;
import lombok.experimental.UtilityClass;

@UtilityClass
Expand All @@ -23,4 +32,48 @@ public static int getValueFromInfo(String data, String value) {
public static <T> T getFirstEntryFromMultiValue(ClusterValue<T> data) {
return data.getMultiValue().get(data.getMultiValue().keySet().toArray(String[]::new)[0]);
}

/** Generates a random string of a specified length using ASCII letters. */
public static String getRandomString(int length) {
String asciiLetters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
SecureRandom random = new SecureRandom();
StringBuilder sb = new StringBuilder(length);

for (int i = 0; i < length; i++) {
int index = random.nextInt(asciiLetters.length());
char randomChar = asciiLetters.charAt(index);
sb.append(randomChar);
}

return sb.toString();
}

/**
* Transforms server info string into a Map, using lines with ":" to create key-value pairs,
* replacing duplicates with the last encountered value.
*/
public static Map<String, String> parseInfoResponseToMap(String serverInfo) {
return serverInfo
.lines()
.filter(line -> line.contains(":"))
.map(line -> line.split(":", 2))
.collect(
Collectors.toMap(
parts -> parts[0],
parts -> parts[1],
(existingValue, newValue) -> newValue,
HashMap::new));
}

public static RedisClientConfiguration.RedisClientConfigurationBuilder<?, ?>
commonClientConfig() {
return RedisClientConfiguration.builder()
.address(NodeAddress.builder().port(STANDALONE_PORTS[0]).build());
}

public static RedisClusterClientConfiguration.RedisClusterClientConfigurationBuilder<?, ?>
commonClusterClientConfig() {
return RedisClusterClientConfiguration.builder()
.address(NodeAddress.builder().port(CLUSTER_PORTS[0]).build());
}
}
48 changes: 0 additions & 48 deletions java/integTest/src/test/java/glide/cluster/ClientTests.java

This file was deleted.

160 changes: 160 additions & 0 deletions java/integTest/src/test/java/glide/cluster/ClusterClientTests.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
/** Copyright GLIDE-for-Redis Project Contributors - SPDX Identifier: Apache-2.0 */
package glide.cluster;

import static glide.TestConfiguration.REDIS_VERSION;
import static glide.TestUtilities.commonClusterClientConfig;
import static glide.TestUtilities.getRandomString;
import static glide.api.BaseClient.OK;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assumptions.assumeTrue;

import glide.api.RedisClusterClient;
import glide.api.models.configuration.RedisCredentials;
import glide.api.models.exceptions.ClosingException;
import glide.api.models.exceptions.RequestException;
import java.util.concurrent.ExecutionException;
import lombok.SneakyThrows;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.Timeout;

@Timeout(10)
SanHalacogluImproving marked this conversation as resolved.
Show resolved Hide resolved
public class ClusterClientTests {

@SneakyThrows
@Test
public void register_client_name_and_version() {
String minVersion = "7.2.0";
assumeTrue(
REDIS_VERSION.isGreaterThanOrEqualTo("7.2.0"), "Redis version required >= " + minVersion);
SanHalacogluImproving marked this conversation as resolved.
Show resolved Hide resolved

RedisClusterClient client =
RedisClusterClient.CreateClient(commonClusterClientConfig().build()).get();

String info =
(String) client.customCommand(new String[] {"CLIENT", "INFO"}).get().getSingleValue();
assertTrue(info.contains("lib-name=GlideJava"));
assertTrue(info.contains("lib-ver=unknown"));

client.close();
}

@SneakyThrows
@Test
public void can_connect_with_auth_requirepass() {
RedisClusterClient client =
RedisClusterClient.CreateClient(commonClusterClientConfig().build()).get();

String password = "TEST_AUTH";
client.customCommand(new String[] {"CONFIG", "SET", "requirepass", password}).get();

// Creation of a new client without a password should fail
ExecutionException exception =
assertThrows(
ExecutionException.class,
() -> RedisClusterClient.CreateClient(commonClusterClientConfig().build()).get());
assertTrue(exception.getCause() instanceof ClosingException);

// Creation of a new client with credentials
RedisClusterClient auth_client =
RedisClusterClient.CreateClient(
commonClusterClientConfig()
.credentials(RedisCredentials.builder().password(password).build())
.build())
.get();

String key = getRandomString(10);
String value = getRandomString(10);

assertEquals(OK, auth_client.set(key, value).get());
assertEquals(value, auth_client.get(key).get());

// Reset password
client.customCommand(new String[] {"CONFIG", "SET", "requirepass", ""}).get();

auth_client.close();
client.close();
}

@SneakyThrows
@Test
public void can_connect_with_auth_acl() {
RedisClusterClient client =
RedisClusterClient.CreateClient(commonClusterClientConfig().build()).get();

String username = "testuser";
String password = "TEST_AUTH";
assertEquals(
OK,
client
.customCommand(
new String[] {
"ACL",
"SETUSER",
username,
"on",
"allkeys",
"+get",
"+cluster",
"+ping",
"+info",
"+client",
">" + password,
})
.get()
.getSingleValue());

String key = getRandomString(10);
String value = getRandomString(10);

assertEquals(OK, client.set(key, value).get());

// Creation of a new cluster client with credentials
RedisClusterClient testUserClient =
RedisClusterClient.CreateClient(
commonClusterClientConfig()
.credentials(
RedisCredentials.builder().username(username).password(password).build())
.build())
.get();

assertEquals(value, testUserClient.get(key).get());

ExecutionException executionException =
assertThrows(ExecutionException.class, () -> testUserClient.set("foo", "bar").get());
assertTrue(executionException.getCause() instanceof RequestException);

client.customCommand(new String[] {"ACL", "DELUSER", username}).get();

testUserClient.close();
client.close();
}

@SneakyThrows
@Test
public void client_name() {
RedisClusterClient client =
SanHalacogluImproving marked this conversation as resolved.
Show resolved Hide resolved
RedisClusterClient.CreateClient(
commonClusterClientConfig().clientName("TEST_CLIENT_NAME").build())
.get();

String clientInfo =
(String) client.customCommand(new String[] {"CLIENT", "INFO"}).get().getSingleValue();
assertTrue(clientInfo.contains("name=TEST_CLIENT_NAME"));

client.close();
}

@Test
@SneakyThrows
public void close_client_throws_ExecutionException_with_ClosingException_cause() {
SanHalacogluImproving marked this conversation as resolved.
Show resolved Hide resolved
RedisClusterClient client =
RedisClusterClient.CreateClient(commonClusterClientConfig().build()).get();

client.close();
ExecutionException executionException =
assertThrows(ExecutionException.class, () -> client.set("foo", "bar").get());
assertTrue(executionException.getCause() instanceof ClosingException);
}
}
Loading
Loading