diff --git a/docs/modules/scylladb.md b/docs/modules/scylladb.md new file mode 100644 index 00000000..8aa928ed --- /dev/null +++ b/docs/modules/scylladb.md @@ -0,0 +1,21 @@ +# ScyllaDB Module + +[ScyllaDB](https://www.scylladb.com/) is a distributed NoSQL wide-column database for data-intensive apps that require high performance and low latency. It was designed to be compatible with Apache Cassandra while achieving significantly higher throughputs and lower latencies. + + + +## Install + +```bash +npm install @testcontainers/scylladb --save-dev +``` + +## Examples + + +[Connect:](../../packages/modules/scylladb/src/scylladb-container.test.ts) inside_block:connectWithDefaultCredentials + + + +[Insert & fetch data:](../../packages/modules/scylladb/src/scylladb-container.test.ts) inside_block:createAndFetchData + diff --git a/mkdocs.yml b/mkdocs.yml index 788c8eb9..463b6313 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -64,6 +64,7 @@ nav: - RabbitMQ: modules/rabbitmq.md - Redis: modules/redis.md - Redpanda: modules/redpanda.md + - ScyllaDB: modules/scylladb.md - Selenium: modules/selenium.md - Weaviate: modules/weaviate.md - Configuration: configuration.md diff --git a/package-lock.json b/package-lock.json index 74ce52cd..42e839ee 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5496,6 +5496,10 @@ "resolved": "packages/modules/redpanda", "link": true }, + "node_modules/@testcontainers/scylladb": { + "resolved": "packages/modules/scylladb", + "link": true + }, "node_modules/@testcontainers/selenium": { "resolved": "packages/modules/selenium", "link": true @@ -20145,6 +20149,16 @@ "kafkajs": "^2.2.4" } }, + "packages/modules/scylladb": { + "version": "10.14.0", + "license": "MIT", + "dependencies": { + "testcontainers": "^10.14.0" + }, + "devDependencies": { + "cassandra-driver": "^4.7.2" + } + }, "packages/modules/selenium": { "name": "@testcontainers/selenium", "version": "10.14.0", diff --git a/packages/modules/scylladb/jest.config.ts b/packages/modules/scylladb/jest.config.ts new file mode 100644 index 00000000..1f677baa --- /dev/null +++ b/packages/modules/scylladb/jest.config.ts @@ -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; diff --git a/packages/modules/scylladb/package.json b/packages/modules/scylladb/package.json new file mode 100644 index 00000000..a702e2df --- /dev/null +++ b/packages/modules/scylladb/package.json @@ -0,0 +1,37 @@ +{ + "name": "@testcontainers/scylladb", + "version": "10.14.0", + "license": "MIT", + "keywords": [ + "scylladb", + "testing", + "docker", + "testcontainers" + ], + "description": "ScyllaDB 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" + }, + "dependencies": { + "testcontainers": "^10.14.0" + }, + "devDependencies": { + "cassandra-driver": "^4.7.2" + } +} diff --git a/packages/modules/scylladb/src/index.ts b/packages/modules/scylladb/src/index.ts new file mode 100644 index 00000000..7fbe3f30 --- /dev/null +++ b/packages/modules/scylladb/src/index.ts @@ -0,0 +1 @@ +export { ScyllaContainer, StartedScyllaContainer } from "./scylladb-container"; diff --git a/packages/modules/scylladb/src/scylladb-container.test.ts b/packages/modules/scylladb/src/scylladb-container.test.ts new file mode 100644 index 00000000..c861722d --- /dev/null +++ b/packages/modules/scylladb/src/scylladb-container.test.ts @@ -0,0 +1,67 @@ +import { Client } from "cassandra-driver"; // Scylla uses Cassandra's driver in Node.js +import { ScyllaContainer } from "./scylladb-container"; + +describe("ScyllaDB", () => { + jest.setTimeout(240_000); + + // connectWithDefaultCredentials { + it("should connect and execute a query", async () => { + const container = await new ScyllaContainer("scylladb/scylla:6.2.0").start(); + + const client = new Client({ + contactPoints: [container.getContactPoint()], + localDataCenter: container.getDatacenter(), + keyspace: "system", + }); + + await client.connect(); + + const result = await client.execute("SELECT cql_version FROM system.local"); + expect(result.rows[0].cql_version).toBe("3.3.1"); + + await client.shutdown(); + await container.stop(); + }); + // } + + // createAndFetchData { + it("should create keyspace, a table, insert data, and retrieve it", async () => { + const container = await new ScyllaContainer().start(); + + const client = new Client({ + contactPoints: [container.getContactPoint()], + localDataCenter: container.getDatacenter(), + }); + + await client.connect(); + + // Create the keyspace + await client.execute(` + CREATE KEYSPACE IF NOT EXISTS test_keyspace + WITH replication = {'class': 'SimpleStrategy', 'replication_factor': '1'} + `); + + await client.execute("USE test_keyspace"); + + // Create the table. + await client.execute(` + CREATE TABLE IF NOT EXISTS test_keyspace.users ( + id UUID PRIMARY KEY, + name text + ) + `); + + // Insert a record + const id = "d002cd08-401a-47d6-92d7-bb4204d092f8"; // Fixed UUID for testing + const username = "Test McTestinson"; + await client.execute("INSERT INTO test_keyspace.users (id, name) VALUES (?, ?)", [id, username]); + + // Fetch and verify the record + const result = await client.execute("SELECT * FROM test_keyspace.users WHERE id = ?", [id], { prepare: true }); + expect(result.rows[0].name).toEqual(username); + + await client.shutdown(); + await container.stop(); + }); + // } +}); diff --git a/packages/modules/scylladb/src/scylladb-container.ts b/packages/modules/scylladb/src/scylladb-container.ts new file mode 100644 index 00000000..d2c3548a --- /dev/null +++ b/packages/modules/scylladb/src/scylladb-container.ts @@ -0,0 +1,42 @@ +import { AbstractStartedContainer, GenericContainer, type StartedTestContainer } from "testcontainers"; + +const SCYLLA_PORT = 9042; + +export class ScyllaContainer extends GenericContainer { + constructor(image = "scylladb/scylla:6.2.0") { + super(image); + this.withExposedPorts(SCYLLA_PORT); + this.withCommand(["--skip-wait-for-gossip-to-settle=0"]); + } + + public override async start(): Promise { + this.withEnvironment({ + SCYLLA_LISTEN_ADDRESS: "0.0.0.0", + SCYLLA_BROADCAST_ADDRESS: "0.0.0.0", + SCYLLA_RPC_ADDRESS: "0.0.0.0", + }); + const startedContainer = await super.start(); + return new StartedScyllaContainer(startedContainer); + } +} + +export class StartedScyllaContainer extends AbstractStartedContainer { + private readonly port: number; + + constructor(startedTestContainer: StartedTestContainer) { + super(startedTestContainer); + this.port = startedTestContainer.getMappedPort(SCYLLA_PORT); + } + + public getPort(): number { + return this.port; + } + + public getDatacenter(): string { + return "datacenter1"; // Forced in docker. + } + + public getContactPoint(): string { + return `${this.getHost()}:${this.getPort()}`; + } +} diff --git a/packages/modules/scylladb/tsconfig.build.json b/packages/modules/scylladb/tsconfig.build.json new file mode 100644 index 00000000..0222f6ff --- /dev/null +++ b/packages/modules/scylladb/tsconfig.build.json @@ -0,0 +1,13 @@ +{ + "extends": "./tsconfig.json", + "exclude": [ + "build", + "jest.config.ts", + "src/**/*.test.ts" + ], + "references": [ + { + "path": "../../testcontainers" + } + ] +} \ No newline at end of file diff --git a/packages/modules/scylladb/tsconfig.json b/packages/modules/scylladb/tsconfig.json new file mode 100644 index 00000000..39b16581 --- /dev/null +++ b/packages/modules/scylladb/tsconfig.json @@ -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" + } + ] +} \ No newline at end of file