Skip to content

Commit efe3426

Browse files
authored
Add EventStoreDB module (#894)
1 parent 934328d commit efe3426

File tree

10 files changed

+288
-0
lines changed

10 files changed

+288
-0
lines changed

docs/modules/eventstoredb.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# EventStoreDB Module
2+
3+
[EventStoreDB](https://eventstore.com) is an event sourcing database that stores data in streams of immutable events.
4+
5+
## Install
6+
7+
```bash
8+
npm install @testcontainers/eventstoredb --save-dev
9+
```
10+
11+
## Examples
12+
13+
<!--codeinclude-->
14+
[Start container:](../../packages/modules/eventstoredb/src/eventstoredb-container.test.ts) inside_block:startContainer
15+
<!--/codeinclude-->
16+
17+
<!--codeinclude-->
18+
[Subscribe to standard projection:](../../packages/modules/eventstoredb/src/eventstoredb-container.test.ts) inside_block:usingStandardProjections
19+
<!--/codeinclude-->

mkdocs.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ nav:
4848
- ChromaDB: modules/chromadb.md
4949
- Couchbase: modules/couchbase.md
5050
- Elasticsearch: modules/elasticsearch.md
51+
- EventStoreDB: modules/eventstoredb.md
5152
- GCloud: modules/gcloud.md
5253
- HiveMQ: modules/hivemq.md
5354
- K3s: modules/k3s.md

package-lock.json

Lines changed: 55 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: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
{
2+
"name": "@testcontainers/eventstoredb",
3+
"version": "10.16.0",
4+
"description": "EventStoreDB module for Testcontainers",
5+
"main": "build/index.js",
6+
"scripts": {
7+
"prepack": "shx cp ../../../README.md . && shx cp ../../../LICENSE .",
8+
"build": "tsc --project tsconfig.build.json"
9+
},
10+
"repository": {
11+
"type": "git",
12+
"url": "git+https://github.com/testcontainers/testcontainers-node.git"
13+
},
14+
"keywords": [
15+
"eventstoredb",
16+
"testing",
17+
"docker",
18+
"testcontainers"
19+
],
20+
"author": "",
21+
"license": "MIT",
22+
"bugs": {
23+
"url": "https://github.com/testcontainers/testcontainers-node/issues"
24+
},
25+
"homepage": "https://github.com/testcontainers/testcontainers-node#readme",
26+
"dependencies": {
27+
"testcontainers": "^10.16.0"
28+
},
29+
"devDependencies": {
30+
"@eventstore/db-client": "^6.2.1"
31+
}
32+
}
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
import { EventStoreDBClient, StreamingRead, StreamSubscription } from "@eventstore/db-client";
2+
import { EventStoreDBContainer } from "./eventstoredb-container";
3+
4+
describe("EventStoreDBContainer", () => {
5+
jest.setTimeout(240_000);
6+
7+
// startContainer {
8+
it("should execute write and read", async () => {
9+
const container = await new EventStoreDBContainer().start();
10+
11+
const client = EventStoreDBClient.connectionString(container.getConnectionString());
12+
13+
await client.appendToStream("User-1", [
14+
{
15+
contentType: "application/json",
16+
data: { email: "[email protected]" },
17+
type: "UserCreated",
18+
id: "28ab6bca-d9ae-418b-a1af-eb65dd653c38",
19+
metadata: {
20+
someMetadata: "bar",
21+
},
22+
},
23+
]);
24+
25+
expect(await consumeSteamingRead(client.readStream("User-1"))).toEqual([
26+
expect.objectContaining({
27+
event: expect.objectContaining({
28+
data: {
29+
30+
},
31+
id: "28ab6bca-d9ae-418b-a1af-eb65dd653c38",
32+
isJson: true,
33+
metadata: {
34+
someMetadata: "bar",
35+
},
36+
revision: 0n,
37+
streamId: "User-1",
38+
type: "UserCreated",
39+
}),
40+
}),
41+
]);
42+
43+
await container.stop();
44+
});
45+
// }
46+
47+
// usingStandardProjections {
48+
it("should use built-in projections", async () => {
49+
const container = await new EventStoreDBContainer().start();
50+
const client = EventStoreDBClient.connectionString(container.getConnectionString());
51+
52+
await client.appendToStream("Todo-1", [
53+
{
54+
contentType: "application/json",
55+
data: { title: "Do something" },
56+
metadata: {},
57+
id: "7eccc3a7-0664-4348-a621-029125741e22",
58+
type: "TodoCreated",
59+
},
60+
]);
61+
const stream = client.subscribeToStream("$ce-Todo", { resolveLinkTos: true });
62+
63+
expect(await getStreamFirstEvent(stream)).toEqual(
64+
expect.objectContaining({
65+
event: expect.objectContaining({
66+
data: { title: "Do something" },
67+
id: "7eccc3a7-0664-4348-a621-029125741e22",
68+
isJson: true,
69+
metadata: {},
70+
revision: 0n,
71+
streamId: "Todo-1",
72+
type: "TodoCreated",
73+
}),
74+
link: expect.objectContaining({
75+
isJson: false,
76+
metadata: expect.objectContaining({
77+
$causedBy: "7eccc3a7-0664-4348-a621-029125741e22",
78+
$o: "Todo-1",
79+
}),
80+
revision: 0n,
81+
streamId: "$ce-Todo",
82+
type: "$>",
83+
}),
84+
})
85+
);
86+
await stream.unsubscribe();
87+
await container.stop();
88+
});
89+
// }
90+
});
91+
92+
async function consumeSteamingRead(read: StreamingRead<unknown>): Promise<unknown[]> {
93+
const events = [];
94+
95+
for await (const event of read) {
96+
events.push(event);
97+
}
98+
99+
return events;
100+
}
101+
102+
async function getStreamFirstEvent(stream: StreamSubscription): Promise<unknown> {
103+
for await (const event of stream) {
104+
return event;
105+
}
106+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import { AbstractStartedContainer, GenericContainer, Wait } from "testcontainers";
2+
3+
export class StartedEventStoreDBContainer extends AbstractStartedContainer {
4+
getConnectionString(): string {
5+
return `esdb://${this.getHost()}:${this.getFirstMappedPort()}?tls=false`;
6+
}
7+
}
8+
9+
const EVENT_STORE_DB_PORT = 2113;
10+
11+
export class EventStoreDBContainer extends GenericContainer {
12+
constructor(image = "eventstore/eventstore:24.10") {
13+
super(image);
14+
15+
this.withExposedPorts(EVENT_STORE_DB_PORT)
16+
.withEnvironment({
17+
EVENTSTORE_CLUSTER_SIZE: "1",
18+
EVENTSTORE_RUN_PROJECTIONS: "All",
19+
EVENTSTORE_START_STANDARD_PROJECTIONS: "true",
20+
EVENTSTORE_INSECURE: "true",
21+
})
22+
.withStartupTimeout(120_000)
23+
.withWaitStrategy(Wait.forHealthCheck());
24+
}
25+
26+
public override async start(): Promise<StartedEventStoreDBContainer> {
27+
return new StartedEventStoreDBContainer(await super.start());
28+
}
29+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export { StartedEventStoreDBContainer, EventStoreDBContainer } from "./eventstoredb-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)