Skip to content

Commit 15b62a1

Browse files
authored
Add Valkey module (#906)
1 parent 5125cdf commit 15b62a1

File tree

13 files changed

+473
-1
lines changed

13 files changed

+473
-1
lines changed

docs/modules/valkey.md

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
# Valkey Module
2+
3+
[Valkey](https://valkey.io/) is a distributed, in-memory, key-value store.
4+
5+
## Install
6+
7+
```bash
8+
npm install @testcontainers/valkey --save-dev
9+
```
10+
11+
## Examples
12+
13+
<!--codeinclude-->
14+
15+
[Start container:](../../packages/modules/valkey/src/valkey-container.test.ts) inside_block:startContainer
16+
17+
<!--/codeinclude-->
18+
19+
<!--codeinclude-->
20+
21+
[Connect valkey client to container:](../../packages/modules/valkey/src/valkey-container.test.ts) inside_block:simpleConnect
22+
23+
<!--/codeinclude-->
24+
25+
<!--codeinclude-->
26+
27+
[Start container with password authentication:](../../packages/modules/valkey/src/valkey-container.test.ts) inside_block:startWithCredentials
28+
29+
<!--/codeinclude-->
30+
31+
<!--codeinclude-->
32+
33+
[Define volume for persistent/predefined data:](../../packages/modules/valkey/src/valkey-container.test.ts) inside_block:persistentData
34+
35+
<!--/codeinclude-->
36+
37+
<!--codeinclude-->
38+
39+
[Execute a command inside the container:](../../packages/modules/valkey/src/valkey-container.test.ts) inside_block:executeCommand
40+
41+
<!--/codeinclude-->

mkdocs.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,5 +71,6 @@ nav:
7171
- ScyllaDB: modules/scylladb.md
7272
- Selenium: modules/selenium.md
7373
- ToxiProxy: modules/toxiproxy.md
74+
- Valkey: modules/valkey.md
7475
- Weaviate: modules/weaviate.md
7576
- Configuration: configuration.md

package-lock.json

Lines changed: 131 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/modules/redis/src/redis-container.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,12 @@ export class RedisContainer extends GenericContainer {
3232
return this;
3333
}
3434

35+
protected override async containerStarted(container: StartedTestContainer): Promise<void> {
36+
if (this.initialImportScriptFile) {
37+
await this.importInitialData(container);
38+
}
39+
}
40+
3541
public override async start(): Promise<StartedRedisContainer> {
3642
this.withCommand([
3743
"redis-server",
@@ -60,7 +66,7 @@ export class RedisContainer extends GenericContainer {
6066
return startedRedisContainer;
6167
}
6268

63-
private async importInitialData(container: StartedRedisContainer) {
69+
private async importInitialData(container: StartedTestContainer) {
6470
const re = await container.exec(`/tmp/import.sh ${this.password}`);
6571
if (re.exitCode != 0 || re.output.includes("ERR"))
6672
throw Error(`Could not import initial data from ${this.initialImportScriptFile}: ${re.output}`);
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import type { Config } from "jest";
2+
import * as path from "path";
3+
4+
const config: Config = {
5+
preset: "ts-jest",
6+
moduleNameMapper: {
7+
"^testcontainers$": path.resolve(__dirname, "../../testcontainers/src"),
8+
},
9+
};
10+
11+
export default config;
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
{
2+
"name": "@testcontainers/valkey",
3+
"version": "10.17.1",
4+
"license": "MIT",
5+
"keywords": [
6+
"valkey",
7+
"testing",
8+
"docker",
9+
"testcontainers"
10+
],
11+
"description": "Valkey module for Testcontainers",
12+
"homepage": "https://github.com/testcontainers/testcontainers-node#readme",
13+
"repository": {
14+
"type": "git",
15+
"url": "https://github.com/testcontainers/testcontainers-node"
16+
},
17+
"bugs": {
18+
"url": "https://github.com/testcontainers/testcontainers-node/issues"
19+
},
20+
"main": "build/index.js",
21+
"files": [
22+
"build"
23+
],
24+
"publishConfig": {
25+
"access": "public"
26+
},
27+
"scripts": {
28+
"prepack": "shx cp ../../../README.md . && shx cp ../../../LICENSE .",
29+
"build": "tsc --project tsconfig.build.json"
30+
},
31+
"devDependencies": {
32+
"@types/redis": "^4.0.11",
33+
"redis": "^4.6.15"
34+
},
35+
"dependencies": {
36+
"testcontainers": "^10.17.1"
37+
}
38+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
#!/usr/bin/env bash
2+
set -e
3+
valkey-cli $([[ -n "$1" ]] && echo "-a $1") < "/tmp/import.valkey"
4+
echo "Imported"
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export { ValkeyContainer, StartedValkeyContainer } from "./valkey-container";
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +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"}'
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
import { createClient } from "redis";
2+
import { ValkeyContainer, StartedValkeyContainer } from "./valkey-container";
3+
import * as os from "os";
4+
import * as path from "path";
5+
import * as fs from "fs";
6+
7+
describe("ValkeyContainer", () => {
8+
jest.setTimeout(240_000);
9+
10+
it("should connect and execute set-get", async () => {
11+
const container = await new ValkeyContainer().start();
12+
13+
const client = await connectTo(container);
14+
15+
await client.set("key", "val");
16+
expect(await client.get("key")).toBe("val");
17+
18+
await client.disconnect();
19+
await container.stop();
20+
});
21+
22+
it("should connect with password and execute set-get", async () => {
23+
const container = await new ValkeyContainer().withPassword("test").start();
24+
25+
const client = await connectTo(container);
26+
27+
await client.set("key", "val");
28+
expect(await client.get("key")).toBe("val");
29+
30+
await client.disconnect();
31+
await container.stop();
32+
});
33+
34+
it("should reconnect with volume and persistence data", async () => {
35+
const sourcePath = fs.mkdtempSync(path.join(os.tmpdir(), "valkey-"));
36+
const container = await new ValkeyContainer().withPassword("test").withPersistence(sourcePath).start();
37+
let client = await connectTo(container);
38+
39+
await client.set("key", "val");
40+
await client.disconnect();
41+
await container.restart();
42+
client = await connectTo(container);
43+
expect(await client.get("key")).toBe("val");
44+
45+
await client.disconnect();
46+
await container.stop();
47+
try {
48+
fs.rmSync(sourcePath, { force: true, recursive: true });
49+
} catch (e) {
50+
//Ignore clean up, when have no access on fs.
51+
console.log(e);
52+
}
53+
});
54+
55+
it("should load initial data and can read it", async () => {
56+
const container = await new ValkeyContainer()
57+
.withPassword("test")
58+
.withInitialData(path.join(__dirname, "initData.valkey"))
59+
.start();
60+
const client = await connectTo(container);
61+
const user = {
62+
first_name: "David",
63+
last_name: "Bloom",
64+
dob: "03-MAR-1981",
65+
};
66+
expect(await client.get("user:002")).toBe(JSON.stringify(user));
67+
68+
await client.disconnect();
69+
await container.stop();
70+
});
71+
72+
it("should start with credentials and login", async () => {
73+
const password = "testPassword";
74+
75+
const container = await new ValkeyContainer().withPassword(password).start();
76+
expect(container.getConnectionUrl()).toEqual(`redis://:${password}@${container.getHost()}:${container.getPort()}`);
77+
78+
const client = await connectTo(container);
79+
80+
await client.set("key", "val");
81+
expect(await client.get("key")).toBe("val");
82+
83+
await client.disconnect();
84+
await container.stop();
85+
});
86+
87+
it("should execute container cmd and return the result", async () => {
88+
const container = await new ValkeyContainer().start();
89+
90+
const queryResult = await container.executeCliCmd("info", ["clients"]);
91+
expect(queryResult).toEqual(expect.stringContaining("connected_clients:1"));
92+
93+
await container.stop();
94+
});
95+
96+
async function connectTo(container: StartedValkeyContainer) {
97+
const client = createClient({
98+
url: container.getConnectionUrl(),
99+
});
100+
await client.connect();
101+
expect(client.isOpen).toBeTruthy();
102+
return client;
103+
}
104+
});

0 commit comments

Comments
 (0)