Skip to content

Commit 3cf1da3

Browse files
authored
Added support for enabling Nats JetStream (#877)
1 parent 686a8c6 commit 3cf1da3

File tree

3 files changed

+92
-26
lines changed

3 files changed

+92
-26
lines changed

docs/modules/nats.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,3 +21,7 @@ npm install @testcontainers/nats --save-dev
2121
<!--codeinclude-->
2222
[Set credentials:](../../packages/modules/nats/src/nats-container.test.ts) inside_block:credentials
2323
<!--/codeinclude-->
24+
25+
<!--codeinclude-->
26+
[Enable JetStream:](../../packages/modules/nats/src/nats-container.test.ts) inside_block:jetstream
27+
<!--/codeinclude-->

packages/modules/nats/src/nats-container.test.ts

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,43 @@ describe("NatsContainer", () => {
6767
});
6868
// }
6969

70+
// jetstream {
71+
it("should start with JetStream ", async () => {
72+
// enable JetStream
73+
const container = await new NatsContainer().withJetStream().start();
74+
75+
const nc = await connect(container.getConnectionOptions());
76+
77+
// ensure JetStream is enabled, otherwise this will throw an error
78+
await nc.jetstream().jetstreamManager();
79+
80+
// close the connection
81+
await nc.close();
82+
// check if the close was OK
83+
const err = await nc.closed();
84+
expect(err).toBe(undefined);
85+
86+
await container.stop();
87+
});
88+
89+
it("should fail without JetStream ", async () => {
90+
const container = await new NatsContainer().start();
91+
92+
const nc = await connect(container.getConnectionOptions());
93+
94+
// ensure JetStream is not enabled, as this will throw an error
95+
await expect(nc.jetstream().jetstreamManager()).rejects.toThrow("503");
96+
97+
// close the connection
98+
await nc.close();
99+
// check if the close was OK
100+
const err = await nc.closed();
101+
expect(err).toBe(undefined);
102+
103+
await container.stop();
104+
});
105+
// }
106+
70107
it("should immediately end when started with version argument ", async () => {
71108
// for the complete list of available arguments see:
72109
// See Command Line Options section inside [NATS docker image documentation](https://hub.docker.com/_/nats)
@@ -75,6 +112,6 @@ describe("NatsContainer", () => {
75112
await connect(container.getConnectionOptions());
76113
}
77114

78-
await expect(outputVersionAndExit()).rejects.toThrowError();
115+
await expect(outputVersionAndExit()).rejects.toThrow();
79116
});
80117
});

packages/modules/nats/src/nats-container.ts

Lines changed: 50 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -7,44 +7,51 @@ const HTTP_MANAGEMENT_PORT = 8222;
77
const USER_ARGUMENT_KEY = "--user";
88
const PASS_ARGUMENT_KEY = "--pass";
99

10-
function buildCmdsFromArgs(args: { [p: string]: string }): string[] {
11-
const result: string[] = [];
12-
result.push("nats-server");
13-
14-
for (const argsKey in args) {
15-
result.push(argsKey);
16-
result.push(args[argsKey]);
17-
}
18-
return result;
19-
}
20-
2110
export class NatsContainer extends GenericContainer {
22-
private args: { [name: string]: string } = {};
11+
private args = new Set<string>();
12+
private values = new Map<string, string | undefined>();
2313

2414
constructor(image = "nats:2.8.4-alpine") {
2515
super(image);
2616

27-
this.args[USER_ARGUMENT_KEY] = "test";
28-
this.args[PASS_ARGUMENT_KEY] = "test";
17+
this.withUsername("test");
18+
this.withPass("test");
2919

3020
this.withExposedPorts(CLIENT_PORT, ROUTING_PORT_FOR_CLUSTERING, HTTP_MANAGEMENT_PORT)
3121
.withWaitStrategy(Wait.forLogMessage(/.*Server is ready.*/))
3222
.withStartupTimeout(120_000);
3323
}
3424

25+
/**
26+
* Enable JetStream
27+
*
28+
* @returns {this}
29+
*/
30+
public withJetStream(): this {
31+
this.withArg("--jetstream");
32+
return this;
33+
}
34+
3535
public withUsername(user: string): this {
36-
this.args[USER_ARGUMENT_KEY] = user;
36+
this.withArg(USER_ARGUMENT_KEY, user);
3737
return this;
3838
}
3939

4040
public withPass(pass: string): this {
41-
this.args[PASS_ARGUMENT_KEY] = pass;
41+
this.withArg(PASS_ARGUMENT_KEY, pass);
4242
return this;
4343
}
4444

45-
public withArg(name: string, value: string) {
46-
name = NatsContainer.ensureDashInFrontOfArgumentName(name);
47-
this.args[name] = value;
45+
public withArg(name: string, value: string): this;
46+
public withArg(name: string): this;
47+
public withArg(...args: [string, string] | [string]): this {
48+
const [name, value] = args;
49+
50+
const correctName = NatsContainer.ensureDashInFrontOfArgumentName(name);
51+
this.args.add(correctName);
52+
if (args.length === 2) {
53+
this.values.set(correctName, value);
54+
}
4855
return this;
4956
}
5057

@@ -61,23 +68,41 @@ export class NatsContainer extends GenericContainer {
6168
}
6269

6370
public override async start(): Promise<StartedNatsContainer> {
64-
this.withCommand(buildCmdsFromArgs(this.args));
71+
this.withCommand(this.getNormalizedCommand());
6572
return new StartedNatsContainer(await super.start(), this.getUser(), this.getPass());
6673
}
6774

68-
private getUser(): string {
69-
return this.args[USER_ARGUMENT_KEY];
75+
private getUser(): string | undefined {
76+
return this.values.get(USER_ARGUMENT_KEY);
7077
}
7178

72-
private getPass(): string {
73-
return this.args[PASS_ARGUMENT_KEY];
79+
private getPass(): string | undefined {
80+
return this.values.get(PASS_ARGUMENT_KEY);
81+
}
82+
83+
private getNormalizedCommand(): string[] {
84+
const result: string[] = ["nats-server"];
85+
for (const arg of this.args) {
86+
result.push(arg);
87+
if (this.values.has(arg)) {
88+
const value = this.values.get(arg);
89+
if (value) {
90+
result.push(value);
91+
}
92+
}
93+
}
94+
return result;
7495
}
7596
}
7697

7798
export class StartedNatsContainer extends AbstractStartedContainer {
7899
private readonly connectionOptions: NatsConnectionOptions;
79100

80-
constructor(startedTestContainer: StartedTestContainer, readonly username: string, readonly password: string) {
101+
constructor(
102+
startedTestContainer: StartedTestContainer,
103+
readonly username: string | undefined,
104+
readonly password: string | undefined
105+
) {
81106
super(startedTestContainer);
82107
const port = startedTestContainer.getMappedPort(CLIENT_PORT);
83108
this.connectionOptions = {

0 commit comments

Comments
 (0)