diff --git a/docs/modules/couchdb.md b/docs/modules/couchdb.md new file mode 100644 index 000000000..93caec1a1 --- /dev/null +++ b/docs/modules/couchdb.md @@ -0,0 +1,31 @@ +# CouchDB + +## Install + +```bash +npm install @testcontainers/couchdb --save-dev +``` + +## Examples + +These examples use the following libraries: + +- [nano](https://www.npmjs.com/package/nano) + + npm install nano + +Choose an image from the [container registry](https://hub.docker.com/_/couchdb) and substitute `IMAGE`. + +### Execute a query + + +[](../../packages/modules/couchdb/src/couchdb-container.test.ts) inside_block:startContainer + + +### With credentials + +By default, this module uses `root:root` as credentials. + + +[](../../packages/modules/couchdb/src/couchdb-container.test.ts) inside_block:customCredentials + diff --git a/mkdocs.yml b/mkdocs.yml index cbd1158b9..8482c85f4 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -61,6 +61,7 @@ nav: - CockroachDB: modules/cockroachdb.md - CosmosDB: modules/cosmosdb.md - Couchbase: modules/couchbase.md + - CouchDB: modules/couchdb.md - Elasticsearch: modules/elasticsearch.md - Etcd: modules/etcd.md - GCloud: modules/gcloud.md diff --git a/package-lock.json b/package-lock.json index 72635079d..4593ac559 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7257,6 +7257,10 @@ "resolved": "packages/modules/couchbase", "link": true }, + "node_modules/@testcontainers/couchdb": { + "resolved": "packages/modules/couchdb", + "link": true + }, "node_modules/@testcontainers/elasticsearch": { "resolved": "packages/modules/elasticsearch", "link": true @@ -16124,6 +16128,16 @@ "integrity": "sha512-W7tfG7vMOGtD30sHoZSSc/JVYiyDPEyQVso/Zz+/uQd0B0L46gtC+pHha5FFMRpil6fm/AoEcRWyOVi4+E/f8w==", "optional": true }, + "node_modules/nano": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/nano/-/nano-11.0.0.tgz", + "integrity": "sha512-nnqzzkC3tPRPOdBG4wRcieSRxlgfu+VBGxaYWLo5AxNyqshOQZ9jasWgNFSvHnWCCBqC517m7G+2+qseOOUimw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=20.0" + } + }, "node_modules/nano-spawn": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/nano-spawn/-/nano-spawn-1.0.2.tgz", @@ -22007,6 +22021,17 @@ "couchbase": "4.5.0" } }, + "packages/modules/couchdb": { + "name": "@testcontainers/couchdb", + "version": "11.5.1", + "license": "MIT", + "dependencies": { + "testcontainers": "^11.5.1" + }, + "devDependencies": { + "nano": "^11.0.0" + } + }, "packages/modules/elasticsearch": { "name": "@testcontainers/elasticsearch", "version": "11.6.0", diff --git a/packages/modules/couchdb/Dockerfile b/packages/modules/couchdb/Dockerfile new file mode 100644 index 000000000..c2b1addc2 --- /dev/null +++ b/packages/modules/couchdb/Dockerfile @@ -0,0 +1 @@ +FROM couchdb:3.5 \ No newline at end of file diff --git a/packages/modules/couchdb/package.json b/packages/modules/couchdb/package.json new file mode 100644 index 000000000..0e357f123 --- /dev/null +++ b/packages/modules/couchdb/package.json @@ -0,0 +1,37 @@ +{ + "name": "@testcontainers/couchdb", + "version": "11.5.1", + "license": "MIT", + "keywords": [ + "couchdb", + "testing", + "docker", + "testcontainers" + ], + "description": "CouchDB module for Testcontainers", + "homepage": "https://github.com/testcontainers/testcontainers-node#readme", + "repository": { + "type": "git", + "url": "git+https://github.com/testcontainers/testcontainers-node.git" + }, + "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" + }, + "dependencies": { + "testcontainers": "^11.5.1" + }, + "devDependencies": { + "nano": "^11.0.0" + } +} diff --git a/packages/modules/couchdb/src/couchdb-container.test.ts b/packages/modules/couchdb/src/couchdb-container.test.ts new file mode 100644 index 000000000..cd9550e02 --- /dev/null +++ b/packages/modules/couchdb/src/couchdb-container.test.ts @@ -0,0 +1,62 @@ +import nano from "nano"; +import { expect } from "vitest"; +import { getImage } from "../../../testcontainers/src/utils/test-helper"; +import { CouchDBContainer } from "./couchdb-container"; + +const IMAGE = getImage(__dirname); + +type User = { + username: string; + email: string; +}; + +describe("CouchDBContainer", { timeout: 240_000 }, () => { + it("should write and read a collection", async () => { + // startContainer { + await using container = await new CouchDBContainer(IMAGE).start(); + + const client = nano({ + url: container.getUrl(), + }); + await client.db.create("users"); + const db = client.use("users"); + + const document = await db.insert({ + username: "j-doe", + email: "j-doe@mail.local", + }); + + expect(await db.get(document.id)).toEqual({ + _id: document.id, + _rev: document.rev, + username: "j-doe", + email: "j-doe@mail.local", + }); + // } + }); + + it("should use custom credentials", async function () { + // customCredentials { + await using container = await new CouchDBContainer(IMAGE).withUsername("admin").withPassword("foo").start(); + + const client = nano({ + url: container.getUrl(), + }); + await client.db.create("users"); + const db = client.use("users"); + + const document = await db.insert({ + username: "j-doe", + email: "j-doe@mail.local", + }); + + expect(container.getUrl()).toBe(`http://admin:foo@${container.getHost()}:${container.getPort()}`); + expect(await db.get(document.id)).toEqual({ + _id: document.id, + _rev: document.rev, + username: "j-doe", + email: "j-doe@mail.local", + }); + // } + }); +}); diff --git a/packages/modules/couchdb/src/couchdb-container.ts b/packages/modules/couchdb/src/couchdb-container.ts new file mode 100644 index 000000000..ba7ef9a6b --- /dev/null +++ b/packages/modules/couchdb/src/couchdb-container.ts @@ -0,0 +1,59 @@ +import { AbstractStartedContainer, GenericContainer, StartedTestContainer, Wait } from "testcontainers"; + +const COUCHDB_PORT = 5984; + +export class CouchDBContainer extends GenericContainer { + private username: string = "root"; + private password: string = "root"; + + constructor(image: string) { + super(image); + this.withExposedPorts(COUCHDB_PORT) + .withStartupTimeout(120_000) + .withWaitStrategy(Wait.forHttp("/_up", COUCHDB_PORT).forStatusCode(200).withStartupTimeout(60_000)); + } + + public withUsername(username: string): this { + this.username = username; + return this; + } + + public withPassword(password: string): this { + this.password = password; + return this; + } + + public override async start(): Promise { + this.withEnvironment({ + COUCHDB_USER: this.username, + COUCHDB_PASSWORD: this.password, + }); + return new StartedCouchDBContainer(await super.start(), this.username, this.password); + } +} + +export class StartedCouchDBContainer extends AbstractStartedContainer { + constructor( + startedTestContainer: StartedTestContainer, + private readonly username: string, + private readonly password: string + ) { + super(startedTestContainer); + } + + public getPort(): number { + return this.getMappedPort(COUCHDB_PORT); + } + + public getUsername(): string { + return this.username; + } + + public getPassword(): string { + return this.password; + } + + public getUrl(): string { + return `http://${this.username}:${this.password}@${this.getHost()}:${this.getPort()}`; + } +} diff --git a/packages/modules/couchdb/src/index.ts b/packages/modules/couchdb/src/index.ts new file mode 100644 index 000000000..c9a675bf5 --- /dev/null +++ b/packages/modules/couchdb/src/index.ts @@ -0,0 +1 @@ +export { CouchDBContainer, StartedCouchDBContainer } from "./couchdb-container"; diff --git a/packages/modules/couchdb/tsconfig.build.json b/packages/modules/couchdb/tsconfig.build.json new file mode 100644 index 000000000..ff7390b10 --- /dev/null +++ b/packages/modules/couchdb/tsconfig.build.json @@ -0,0 +1,12 @@ +{ + "extends": "./tsconfig.json", + "exclude": [ + "build", + "src/**/*.test.ts" + ], + "references": [ + { + "path": "../../testcontainers" + } + ] +} \ No newline at end of file diff --git a/packages/modules/couchdb/tsconfig.json b/packages/modules/couchdb/tsconfig.json new file mode 100644 index 000000000..4d74c3e41 --- /dev/null +++ b/packages/modules/couchdb/tsconfig.json @@ -0,0 +1,20 @@ +{ + "extends": "../../../tsconfig.base.json", + "compilerOptions": { + "rootDir": "src", + "outDir": "build", + "paths": { + "testcontainers": [ + "../../testcontainers/src" + ] + } + }, + "exclude": [ + "build" + ], + "references": [ + { + "path": "../../testcontainers" + } + ] +} \ No newline at end of file