Skip to content

Commit c4cde21

Browse files
committed
feat: implement cli & config support, fix user config expression, refactor tests
1 parent ccf3e04 commit c4cde21

File tree

4 files changed

+104
-41
lines changed

4 files changed

+104
-41
lines changed

modules/valkey/src/main/java/org/testcontainers/valkey/ValkeyContainer.java

Lines changed: 60 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -25,18 +25,17 @@ private static class SnapshottingSettings {
2525
}
2626

2727
private static final DockerImageName DEFAULT_IMAGE = DockerImageName.parse(
28-
"valkey/valkey:7.2.5");
28+
"valkey/valkey:8.1");
2929

3030
private static final String DEFAULT_CONFIG_FILE = "/usr/local/valkey.conf";
3131

3232
private static final int CONTAINER_PORT = 6379;
3333

34-
@Getter
3534
private String username;
36-
@Getter
3735
private String password;
3836
private String persistenceVolume;
3937
private String initialImportScriptFile;
38+
private String configFile;
4039
private ValkeyLogLevel logLevel;
4140
private SnapshottingSettings snapshottingSettings;
4241

@@ -112,9 +111,7 @@ public ValkeyContainer withSnapshotting(int seconds, int changedKeys) {
112111
* Sets the config file to be used for the Valkey container.
113112
*/
114113
public ValkeyContainer withConfigFile(String configFile) {
115-
withCopyFileToContainer(MountableFile.forHostPath(configFile), DEFAULT_CONFIG_FILE);
116-
117-
// TODO check whether config path needs to be specified on startup
114+
this.configFile = configFile;
118115

119116
return this;
120117
}
@@ -124,13 +121,17 @@ public void start() {
124121
List<String> command = new ArrayList<>();
125122
command.add("valkey-server");
126123

124+
if (configFile != null && !configFile.isEmpty()) {
125+
withCopyToContainer(MountableFile.forHostPath(configFile), DEFAULT_CONFIG_FILE);
126+
command.add(DEFAULT_CONFIG_FILE);
127+
}
128+
127129
if (password != null && !password.isEmpty()) {
128130
command.add("--requirepass");
129131
command.add(password);
130132

131133
if (username != null && !username.isEmpty()) {
132-
command.add("--user");
133-
command.add(username + " on >" + password + " ~* +@all");
134+
command.add("--user " + username + " on >" + password + " ~* +@all");
134135
}
135136
}
136137

@@ -161,38 +162,56 @@ public void start() {
161162

162163
super.start();
163164

164-
if (initialImportScriptFile != null && !initialImportScriptFile.isEmpty()) {
165-
try {
166-
ExecResult result = this.execInContainer("/bin/sh", "/tmp/import.sh",
167-
password != null ? password : "");
168-
if (result.getExitCode() != 0 || result.getStdout().contains("ERR")) {
169-
throw new RuntimeException(
170-
"Could not import initial data: " + result.getStdout());
171-
}
172-
} catch (Exception e) {
173-
throw new RuntimeException(e);
174-
}
175-
}
165+
evaluateImportScript();
176166
}
177167

178168
public int getPort() {
179169
return getMappedPort(CONTAINER_PORT);
180170
}
181171

172+
/**
173+
* Executes a command in the Valkey CLI inside the container.
174+
*/
175+
public String executeCli(String cmd, String... flags) {
176+
try {
177+
List<String> args = new ArrayList<>();
178+
args.add("redis-cli");
179+
180+
if (password != null && !password.isEmpty()) {
181+
args.addAll(username != null && !username.isEmpty()
182+
? Arrays.asList("--user", username, "--pass", password)
183+
: Arrays.asList("--pass", password)
184+
);
185+
}
186+
187+
args.add(cmd);
188+
args.addAll(Arrays.asList(flags));
189+
190+
ExecResult result = execInContainer(args.toArray(new String[0]));
191+
if (result.getExitCode() != 0) {
192+
throw new RuntimeException(result.getStdout() + result.getStderr());
193+
}
194+
195+
return result.getStdout();
196+
} catch (Exception e) {
197+
throw new RuntimeException(e);
198+
}
199+
}
200+
182201
public String createConnectionUrl() {
183202
String userInfo = null;
184203
if (username != null && !username.isEmpty() && password != null && !password.isEmpty()) {
185204
userInfo = username + ":" + password;
186205
} else if (password != null && !password.isEmpty()) {
187-
userInfo = password;
206+
userInfo = ":" + password;
188207
}
189208

190209
try {
191210
URI uri = new URI(
192211
"redis",
193212
userInfo,
194-
this.getHost(),
195-
this.getPort(),
213+
getHost(),
214+
getPort(),
196215
null,
197216
null,
198217
null
@@ -203,4 +222,22 @@ public String createConnectionUrl() {
203222
}
204223
}
205224

225+
private void evaluateImportScript() {
226+
if (initialImportScriptFile == null || initialImportScriptFile.isEmpty()) {
227+
return;
228+
}
229+
230+
try {
231+
ExecResult result = execInContainer("/bin/sh", "/tmp/import.sh",
232+
password != null ? password : "");
233+
234+
if (result.getExitCode() != 0 || result.getStdout().contains("ERR")) {
235+
throw new RuntimeException(
236+
"Could not import initial data: " + result.getStdout());
237+
}
238+
} catch (Exception e) {
239+
throw new RuntimeException(e);
240+
}
241+
}
242+
206243
}

modules/valkey/src/test/java/org/testcontainers/valkey/ValkeyContainerTest.java

Lines changed: 41 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,10 @@
33
import static org.assertj.core.api.Assertions.assertThat;
44
import static org.assertj.core.api.Assertions.assertThatThrownBy;
55

6+
import io.valkey.Jedis;
67
import io.valkey.JedisPool;
7-
import java.nio.charset.StandardCharsets;
8-
import java.nio.file.Files;
9-
import java.nio.file.StandardOpenOption;
10-
import org.junit.jupiter.api.Disabled;
8+
import java.nio.file.Paths;
9+
import lombok.val;
1110
import org.junit.jupiter.api.Test;
1211
import org.junit.jupiter.api.io.TempDir;
1312

@@ -27,7 +26,7 @@ void shouldWriteAndReadEntry() {
2726
valkeyContainer.start();
2827
JedisPool jedisPool = new JedisPool(valkeyContainer.createConnectionUrl());
2928

30-
try (io.valkey.Jedis jedis = jedisPool.getResource()) {
29+
try (Jedis jedis = jedisPool.getResource()) {
3130
jedis.set("key", "value");
3231
assertThat(jedis.get("key")).isEqualTo("value");
3332
}
@@ -45,9 +44,9 @@ void shouldConfigureServiceWithAuthentication() {
4544
assertThat(url).contains("testuser:testpass");
4645

4746
JedisPool jedisPool = new JedisPool(url);
48-
try (io.valkey.Jedis jedis = jedisPool.getResource()) {
49-
jedis.set("authKey", "authValue");
50-
assertThat(jedis.get("authKey")).isEqualTo("authValue");
47+
try (Jedis jedis = jedisPool.getResource()) {
48+
jedis.set("k1", "v2");
49+
assertThat(jedis.get("k1")).isEqualTo("v2");
5150
}
5251
}
5352
}
@@ -64,7 +63,7 @@ void shouldPersistData() {
6463
valkeyContainer.start();
6564
JedisPool jedisPool = new JedisPool(valkeyContainer.createConnectionUrl());
6665

67-
try (io.valkey.Jedis jedis = jedisPool.getResource()) {
66+
try (Jedis jedis = jedisPool.getResource()) {
6867
jedis.set("persistKey", "persistValue");
6968
}
7069

@@ -74,7 +73,7 @@ void shouldPersistData() {
7473
restarted.start();
7574
JedisPool restartedPool = new JedisPool(restarted.createConnectionUrl());
7675

77-
try (io.valkey.Jedis jedis = restartedPool.getResource()) {
76+
try (Jedis jedis = restartedPool.getResource()) {
7877
assertThat(jedis.get("persistKey")).isEqualTo("persistValue");
7978
}
8079
}
@@ -94,22 +93,48 @@ void shouldValidateSnapshottingConfiguration() {
9493
}
9594

9695
@Test
97-
void shouldInitializeDatabaseWithInitialPayload() throws Exception {
98-
Path importFile = tempDir.resolve("import.data");
99-
String content = "SET key1 \"value1\"\nSET key2 \"value2\"";
100-
Files.write(importFile, content.getBytes(StandardCharsets.UTF_8), StandardOpenOption.CREATE,
101-
StandardOpenOption.TRUNCATE_EXISTING);
96+
void shouldInitializeDatabaseWithPayload() throws Exception {
97+
Path importFile = Paths.get(getClass().getResource("/initData.valkey").toURI());
10298

10399
try (ValkeyContainer valkeyContainer = new ValkeyContainer()
104100
.withInitialData(importFile.toString())) {
105101

106102
valkeyContainer.start();
107103
JedisPool jedisPool = new JedisPool(valkeyContainer.createConnectionUrl());
108104

109-
try (io.valkey.Jedis jedis = jedisPool.getResource()) {
105+
try (Jedis jedis = jedisPool.getResource()) {
110106
assertThat(jedis.get("key1")).isEqualTo("value1");
111107
assertThat(jedis.get("key2")).isEqualTo("value2");
112108
}
113109
}
114110
}
111+
112+
@Test
113+
void shouldExecuteContainerCmdAndReturnResult() {
114+
try (ValkeyContainer valkeyContainer = new ValkeyContainer()) {
115+
valkeyContainer.start();
116+
117+
String queryResult = valkeyContainer.executeCli("info", "clients");
118+
119+
assertThat(queryResult).contains("connected_clients:1");
120+
}
121+
}
122+
123+
@Test
124+
void shouldMountValkeyConfigToContainer() throws Exception {
125+
Path configFile = Paths.get(getClass().getResource("/valkey.conf").toURI());
126+
127+
try (ValkeyContainer valkeyContainer = new ValkeyContainer().withConfigFile(
128+
configFile.toString())) {
129+
valkeyContainer.start();
130+
131+
JedisPool jedisPool = new JedisPool(valkeyContainer.createConnectionUrl());
132+
133+
try (Jedis jedis = jedisPool.getResource()) {
134+
String maxMemory = jedis.configGet("maxmemory").get("maxmemory");
135+
136+
assertThat(maxMemory).isEqualTo("2097152");
137+
}
138+
}
139+
}
115140
}
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
SET "user:001" '{"first_name":"John","last_name":"Doe","dob":"12-JUN-1970"}'
2-
SET "user:002" '{"first_name":"David","last_name":"Bloom","dob":"03-MAR-1981"}'
1+
SET key1 "value1"
2+
SET key2 "value2"
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
maxmemory 2mb

0 commit comments

Comments
 (0)