Skip to content

Commit

Permalink
Add CockroachDB module (#910)
Browse files Browse the repository at this point in the history
  • Loading branch information
leourbina authored Mar 4, 2025
1 parent e533856 commit 832c74d
Show file tree
Hide file tree
Showing 10 changed files with 321 additions and 0 deletions.
28 changes: 28 additions & 0 deletions docs/modules/cockroachdb.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# CockroachDB Module

[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.


## Install

```bash
npm install @testcontainers/cockroachdb --save-dev
```

## Examples

<!--codeinclude-->
[Connect and execute query:](../../packages/modules/cockroachdb/src/cockroachdb-container.test.ts) inside_block:connect
<!--/codeinclude-->

<!--codeinclude-->
[Connect and execute query using URI:](../../packages/modules/cockroachdb/src/cockroachdb-container.test.ts) inside_block:uriConnect
<!--/codeinclude-->

<!--codeinclude-->
[Set database:](../../packages/modules/cockroachdb/src/cockroachdb-container.test.ts) inside_block:setDatabase
<!--/codeinclude-->

<!--codeinclude-->
[Set username:](../../packages/modules/cockroachdb/src/cockroachdb-container.test.ts) inside_block:setUsername
<!--/codeinclude-->
1 change: 1 addition & 0 deletions mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ nav:
- Cassandra: modules/cassandra.md
- ChromaDB: modules/chromadb.md
- Couchbase: modules/couchbase.md
- CockroachDB: modules/cockroachdb.md
- Elasticsearch: modules/elasticsearch.md
- EventStoreDB: modules/eventstoredb.md
- GCloud: modules/gcloud.md
Expand Down
16 changes: 16 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

11 changes: 11 additions & 0 deletions packages/modules/cockroachdb/jest.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import type { Config } from "jest";
import * as path from "path";

const config: Config = {
preset: "ts-jest",
moduleNameMapper: {
"^testcontainers$": path.resolve(__dirname, "../../testcontainers/src"),
},
};

export default config;
39 changes: 39 additions & 0 deletions packages/modules/cockroachdb/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
{
"name": "@testcontainers/cockroachdb",
"version": "10.18.0",
"license": "MIT",
"keywords": [
"cockroachdb",
"crdb",
"testing",
"docker",
"testcontainers"
],
"description": "CockroachDB module for Testcontainers",
"homepage": "https://github.com/testcontainers/testcontainers-node#readme",
"repository": {
"type": "git",
"url": "https://github.com/testcontainers/testcontainers-node"
},
"bugs": {
"url": "https://github.com/testcontainers/testcontainers-node/issues"
},
"main": "build/index.js",
"files": [
"build"
],
"publishConfig": {
"access": "public"
},
"scripts": {
"prepack": "shx cp ../../../README.md . && shx cp ../../../LICENSE .",
"build": "tsc --project tsconfig.build.json"
},
"devDependencies": {
"@types/pg": "^8.11.6",
"pg": "^8.12.0"
},
"dependencies": {
"testcontainers": "^10.18.0"
}
}
115 changes: 115 additions & 0 deletions packages/modules/cockroachdb/src/cockroachdb-container.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
import { Client } from "pg";
import { CockroachDbContainer } from "./cockroachdb-container";

describe("CockroachDbContainer", () => {
jest.setTimeout(180_000);

// connect {
it("should connect and return a query result", async () => {
const container = await new CockroachDbContainer().start();

const client = new Client({
host: container.getHost(),
port: container.getPort(),
database: container.getDatabase(),
user: container.getUsername(),
ssl: false,
});

await client.connect();

const result = await client.query("SELECT 1");
expect(result.rows[0]).toEqual({ "?column?": "1" });

await client.end();
await container.stop();
});
// }

// uriConnect {
it("should work with database URI", async () => {
const container = await new CockroachDbContainer().start();

const client = new Client({
connectionString: container.getConnectionUri(),
});
await client.connect();

const result = await client.query("SELECT 1");
expect(result.rows[0]).toEqual({ "?column?": "1" });

await client.end();
await container.stop();
});
// }

// setDatabase {
it("should set database", async () => {
const container = await new CockroachDbContainer().withDatabase("custom_database").start();

const client = new Client({
host: container.getHost(),
port: container.getPort(),
database: container.getDatabase(),
user: container.getUsername(),
});
await client.connect();

const result = await client.query("SELECT current_database()");
expect(result.rows[0]).toEqual({ current_database: "custom_database" });

await client.end();
await container.stop();
});
// }

// setUsername {
it("should set username", async () => {
const container = await new CockroachDbContainer().withUsername("custom_username").start();

const client = new Client({
host: container.getHost(),
port: container.getPort(),
database: container.getDatabase(),
user: container.getUsername(),
});
await client.connect();

const result = await client.query("SELECT current_user");
expect(result.rows[0]).toEqual({ current_user: "custom_username" });

await client.end();
await container.stop();
});
// }

it("should work with restarted container", async () => {
const container = await new CockroachDbContainer().start();
await container.restart();

const client = new Client({
host: container.getHost(),
port: container.getPort(),
database: container.getDatabase(),
user: container.getUsername(),
});
await client.connect();

const result = await client.query("SELECT 1");
expect(result.rows[0]).toEqual({ "?column?": "1" });

await client.end();
await container.stop();
});

it("should allow custom healthcheck", async () => {
const container = new CockroachDbContainer().withHealthCheck({
test: ["CMD-SHELL", "exit 1"],
interval: 100,
retries: 0,
timeout: 0,
});

await expect(() => container.start()).rejects.toThrow();
});
});
76 changes: 76 additions & 0 deletions packages/modules/cockroachdb/src/cockroachdb-container.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import { AbstractStartedContainer, GenericContainer, StartedTestContainer, Wait } from "testcontainers";

const COCKROACH_PORT = 26257;
const COCKROACH_HTTP_PORT = 26258;

export class CockroachDbContainer extends GenericContainer {
private database = "test";
private username = "test";

constructor(image = "cockroachdb/cockroach:v24.3.5") {
super(image);
this.withExposedPorts(COCKROACH_PORT, COCKROACH_HTTP_PORT)
.withCommand(["start-single-node", "--insecure", `--http-addr=0.0.0.0:${COCKROACH_HTTP_PORT}`])
.withWaitStrategy(Wait.forHealthCheck());
}

public withDatabase(database: string): this {
this.database = database;
return this;
}

public withUsername(username: string): this {
this.username = username;
return this;
}

public override async start(): Promise<StartedCockroachDbContainer> {
this.withEnvironment({
COCKROACH_DATABASE: this.database,
COCKROACH_USER: this.username,
});
if (!this.healthCheck) {
this.withHealthCheck({
test: ["CMD-SHELL", `cockroach sql --insecure -u ${this.username} -d ${this.database} -e "SELECT 1;"`],
interval: 250,
timeout: 1000,
retries: 1000,
});
}
return new StartedCockroachDbContainer(await super.start(), this.database, this.username);
}
}

export class StartedCockroachDbContainer extends AbstractStartedContainer {
constructor(
startedTestContainer: StartedTestContainer,
private readonly database: string,
private readonly username: string
) {
super(startedTestContainer);
}

public getPort(): number {
return this.startedTestContainer.getMappedPort(COCKROACH_PORT);
}

public getDatabase(): string {
return this.database;
}

public getUsername(): string {
return this.username;
}

/**
* @returns A connection URI in the form of `postgres[ql]://[username[:password]@][host[:port],]/database`
*/
public getConnectionUri(): string {
const url = new URL("", "postgres://");
url.hostname = this.getHost();
url.port = this.getPort().toString();
url.pathname = this.getDatabase();
url.username = this.getUsername();
return url.toString();
}
}
1 change: 1 addition & 0 deletions packages/modules/cockroachdb/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { CockroachDbContainer, StartedCockroachDbContainer } from "./cockroachdb-container";
13 changes: 13 additions & 0 deletions packages/modules/cockroachdb/tsconfig.build.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"extends": "./tsconfig.json",
"exclude": [
"build",
"jest.config.ts",
"src/**/*.test.ts"
],
"references": [
{
"path": "../../testcontainers"
}
]
}
21 changes: 21 additions & 0 deletions packages/modules/cockroachdb/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"extends": "../../../tsconfig.base.json",
"compilerOptions": {
"rootDir": "src",
"outDir": "build",
"paths": {
"testcontainers": [
"../../testcontainers/src"
]
}
},
"exclude": [
"build",
"jest.config.ts"
],
"references": [
{
"path": "../../testcontainers"
}
]
}

0 comments on commit 832c74d

Please sign in to comment.