Skip to content

Commit 832c74d

Browse files
authored
Add CockroachDB module (#910)
1 parent e533856 commit 832c74d

File tree

10 files changed

+321
-0
lines changed

10 files changed

+321
-0
lines changed

docs/modules/cockroachdb.md

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
# CockroachDB Module
2+
3+
[CockroachDB](https://github.com/cockroachdb/cockroach) is a cloud-native, postgresql compatible, distributed SQL database designed to build, scale, and manage modern, data-intensive applications.
4+
5+
6+
## Install
7+
8+
```bash
9+
npm install @testcontainers/cockroachdb --save-dev
10+
```
11+
12+
## Examples
13+
14+
<!--codeinclude-->
15+
[Connect and execute query:](../../packages/modules/cockroachdb/src/cockroachdb-container.test.ts) inside_block:connect
16+
<!--/codeinclude-->
17+
18+
<!--codeinclude-->
19+
[Connect and execute query using URI:](../../packages/modules/cockroachdb/src/cockroachdb-container.test.ts) inside_block:uriConnect
20+
<!--/codeinclude-->
21+
22+
<!--codeinclude-->
23+
[Set database:](../../packages/modules/cockroachdb/src/cockroachdb-container.test.ts) inside_block:setDatabase
24+
<!--/codeinclude-->
25+
26+
<!--codeinclude-->
27+
[Set username:](../../packages/modules/cockroachdb/src/cockroachdb-container.test.ts) inside_block:setUsername
28+
<!--/codeinclude-->

mkdocs.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ nav:
4747
- Cassandra: modules/cassandra.md
4848
- ChromaDB: modules/chromadb.md
4949
- Couchbase: modules/couchbase.md
50+
- CockroachDB: modules/cockroachdb.md
5051
- Elasticsearch: modules/elasticsearch.md
5152
- EventStoreDB: modules/eventstoredb.md
5253
- GCloud: modules/gcloud.md

package-lock.json

Lines changed: 16 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
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: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
{
2+
"name": "@testcontainers/cockroachdb",
3+
"version": "10.18.0",
4+
"license": "MIT",
5+
"keywords": [
6+
"cockroachdb",
7+
"crdb",
8+
"testing",
9+
"docker",
10+
"testcontainers"
11+
],
12+
"description": "CockroachDB module for Testcontainers",
13+
"homepage": "https://github.com/testcontainers/testcontainers-node#readme",
14+
"repository": {
15+
"type": "git",
16+
"url": "https://github.com/testcontainers/testcontainers-node"
17+
},
18+
"bugs": {
19+
"url": "https://github.com/testcontainers/testcontainers-node/issues"
20+
},
21+
"main": "build/index.js",
22+
"files": [
23+
"build"
24+
],
25+
"publishConfig": {
26+
"access": "public"
27+
},
28+
"scripts": {
29+
"prepack": "shx cp ../../../README.md . && shx cp ../../../LICENSE .",
30+
"build": "tsc --project tsconfig.build.json"
31+
},
32+
"devDependencies": {
33+
"@types/pg": "^8.11.6",
34+
"pg": "^8.12.0"
35+
},
36+
"dependencies": {
37+
"testcontainers": "^10.18.0"
38+
}
39+
}
Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
import { Client } from "pg";
2+
import { CockroachDbContainer } from "./cockroachdb-container";
3+
4+
describe("CockroachDbContainer", () => {
5+
jest.setTimeout(180_000);
6+
7+
// connect {
8+
it("should connect and return a query result", async () => {
9+
const container = await new CockroachDbContainer().start();
10+
11+
const client = new Client({
12+
host: container.getHost(),
13+
port: container.getPort(),
14+
database: container.getDatabase(),
15+
user: container.getUsername(),
16+
ssl: false,
17+
});
18+
19+
await client.connect();
20+
21+
const result = await client.query("SELECT 1");
22+
expect(result.rows[0]).toEqual({ "?column?": "1" });
23+
24+
await client.end();
25+
await container.stop();
26+
});
27+
// }
28+
29+
// uriConnect {
30+
it("should work with database URI", async () => {
31+
const container = await new CockroachDbContainer().start();
32+
33+
const client = new Client({
34+
connectionString: container.getConnectionUri(),
35+
});
36+
await client.connect();
37+
38+
const result = await client.query("SELECT 1");
39+
expect(result.rows[0]).toEqual({ "?column?": "1" });
40+
41+
await client.end();
42+
await container.stop();
43+
});
44+
// }
45+
46+
// setDatabase {
47+
it("should set database", async () => {
48+
const container = await new CockroachDbContainer().withDatabase("custom_database").start();
49+
50+
const client = new Client({
51+
host: container.getHost(),
52+
port: container.getPort(),
53+
database: container.getDatabase(),
54+
user: container.getUsername(),
55+
});
56+
await client.connect();
57+
58+
const result = await client.query("SELECT current_database()");
59+
expect(result.rows[0]).toEqual({ current_database: "custom_database" });
60+
61+
await client.end();
62+
await container.stop();
63+
});
64+
// }
65+
66+
// setUsername {
67+
it("should set username", async () => {
68+
const container = await new CockroachDbContainer().withUsername("custom_username").start();
69+
70+
const client = new Client({
71+
host: container.getHost(),
72+
port: container.getPort(),
73+
database: container.getDatabase(),
74+
user: container.getUsername(),
75+
});
76+
await client.connect();
77+
78+
const result = await client.query("SELECT current_user");
79+
expect(result.rows[0]).toEqual({ current_user: "custom_username" });
80+
81+
await client.end();
82+
await container.stop();
83+
});
84+
// }
85+
86+
it("should work with restarted container", async () => {
87+
const container = await new CockroachDbContainer().start();
88+
await container.restart();
89+
90+
const client = new Client({
91+
host: container.getHost(),
92+
port: container.getPort(),
93+
database: container.getDatabase(),
94+
user: container.getUsername(),
95+
});
96+
await client.connect();
97+
98+
const result = await client.query("SELECT 1");
99+
expect(result.rows[0]).toEqual({ "?column?": "1" });
100+
101+
await client.end();
102+
await container.stop();
103+
});
104+
105+
it("should allow custom healthcheck", async () => {
106+
const container = new CockroachDbContainer().withHealthCheck({
107+
test: ["CMD-SHELL", "exit 1"],
108+
interval: 100,
109+
retries: 0,
110+
timeout: 0,
111+
});
112+
113+
await expect(() => container.start()).rejects.toThrow();
114+
});
115+
});
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
import { AbstractStartedContainer, GenericContainer, StartedTestContainer, Wait } from "testcontainers";
2+
3+
const COCKROACH_PORT = 26257;
4+
const COCKROACH_HTTP_PORT = 26258;
5+
6+
export class CockroachDbContainer extends GenericContainer {
7+
private database = "test";
8+
private username = "test";
9+
10+
constructor(image = "cockroachdb/cockroach:v24.3.5") {
11+
super(image);
12+
this.withExposedPorts(COCKROACH_PORT, COCKROACH_HTTP_PORT)
13+
.withCommand(["start-single-node", "--insecure", `--http-addr=0.0.0.0:${COCKROACH_HTTP_PORT}`])
14+
.withWaitStrategy(Wait.forHealthCheck());
15+
}
16+
17+
public withDatabase(database: string): this {
18+
this.database = database;
19+
return this;
20+
}
21+
22+
public withUsername(username: string): this {
23+
this.username = username;
24+
return this;
25+
}
26+
27+
public override async start(): Promise<StartedCockroachDbContainer> {
28+
this.withEnvironment({
29+
COCKROACH_DATABASE: this.database,
30+
COCKROACH_USER: this.username,
31+
});
32+
if (!this.healthCheck) {
33+
this.withHealthCheck({
34+
test: ["CMD-SHELL", `cockroach sql --insecure -u ${this.username} -d ${this.database} -e "SELECT 1;"`],
35+
interval: 250,
36+
timeout: 1000,
37+
retries: 1000,
38+
});
39+
}
40+
return new StartedCockroachDbContainer(await super.start(), this.database, this.username);
41+
}
42+
}
43+
44+
export class StartedCockroachDbContainer extends AbstractStartedContainer {
45+
constructor(
46+
startedTestContainer: StartedTestContainer,
47+
private readonly database: string,
48+
private readonly username: string
49+
) {
50+
super(startedTestContainer);
51+
}
52+
53+
public getPort(): number {
54+
return this.startedTestContainer.getMappedPort(COCKROACH_PORT);
55+
}
56+
57+
public getDatabase(): string {
58+
return this.database;
59+
}
60+
61+
public getUsername(): string {
62+
return this.username;
63+
}
64+
65+
/**
66+
* @returns A connection URI in the form of `postgres[ql]://[username[:password]@][host[:port],]/database`
67+
*/
68+
public getConnectionUri(): string {
69+
const url = new URL("", "postgres://");
70+
url.hostname = this.getHost();
71+
url.port = this.getPort().toString();
72+
url.pathname = this.getDatabase();
73+
url.username = this.getUsername();
74+
return url.toString();
75+
}
76+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export { CockroachDbContainer, StartedCockroachDbContainer } from "./cockroachdb-container";
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
{
2+
"extends": "./tsconfig.json",
3+
"exclude": [
4+
"build",
5+
"jest.config.ts",
6+
"src/**/*.test.ts"
7+
],
8+
"references": [
9+
{
10+
"path": "../../testcontainers"
11+
}
12+
]
13+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
{
2+
"extends": "../../../tsconfig.base.json",
3+
"compilerOptions": {
4+
"rootDir": "src",
5+
"outDir": "build",
6+
"paths": {
7+
"testcontainers": [
8+
"../../testcontainers/src"
9+
]
10+
}
11+
},
12+
"exclude": [
13+
"build",
14+
"jest.config.ts"
15+
],
16+
"references": [
17+
{
18+
"path": "../../testcontainers"
19+
}
20+
]
21+
}

0 commit comments

Comments
 (0)