@modainteract/moda-interact-shared 0.7.0 → 0.7.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/testing/node.d.ts +127 -0
- package/dist/testing/node.js +296 -0
- package/dist/testing/node.js.map +1 -0
- package/package.json +10 -3
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
declare const DEFAULT_POSTGRES_IMAGE = "postgres:17.6-alpine";
|
|
2
|
+
declare const DEFAULT_REDIS_IMAGE = "redis:7.4.2-alpine";
|
|
3
|
+
declare const DEFAULT_HOST = "127.0.0.1";
|
|
4
|
+
declare const DEFAULT_POSTGRES_STARTUP_TIMEOUT_MS = 30000;
|
|
5
|
+
declare const DEFAULT_REDIS_STARTUP_TIMEOUT_MS = 15000;
|
|
6
|
+
declare const DEFAULT_POLL_INTERVAL_MS = 200;
|
|
7
|
+
declare const DEFAULT_COMMAND_TIMEOUT_MS = 120000;
|
|
8
|
+
declare const DEFAULT_DOCKER_COMMAND_TIMEOUT_MS = 30000;
|
|
9
|
+
type CommandResult = {
|
|
10
|
+
stdout: string;
|
|
11
|
+
stderr: string;
|
|
12
|
+
};
|
|
13
|
+
type CommandOptions = {
|
|
14
|
+
cwd?: string;
|
|
15
|
+
env?: NodeJS.ProcessEnv;
|
|
16
|
+
timeout?: number;
|
|
17
|
+
killSignal?: NodeJS.Signals;
|
|
18
|
+
};
|
|
19
|
+
type CommandRunner = (command: string, args: readonly string[], options?: CommandOptions) => Promise<CommandResult>;
|
|
20
|
+
type DockerRunner = (args: readonly string[], options?: CommandOptions) => Promise<CommandResult>;
|
|
21
|
+
type DisposableEnvironment = {
|
|
22
|
+
DATABASE_URL: string;
|
|
23
|
+
TEST_DATABASE_URL: string;
|
|
24
|
+
REDIS_URL: string;
|
|
25
|
+
TEST_REDIS_URL: string;
|
|
26
|
+
};
|
|
27
|
+
type PostgresConnectionDetails = {
|
|
28
|
+
host: string;
|
|
29
|
+
port: number;
|
|
30
|
+
database: string;
|
|
31
|
+
user: string;
|
|
32
|
+
password: string;
|
|
33
|
+
databaseUrl: string;
|
|
34
|
+
containerName: string;
|
|
35
|
+
};
|
|
36
|
+
type RedisConnectionDetails = {
|
|
37
|
+
host: string;
|
|
38
|
+
port: number;
|
|
39
|
+
url: string;
|
|
40
|
+
containerName: string;
|
|
41
|
+
};
|
|
42
|
+
type EphemeralPostgresOptions = {
|
|
43
|
+
image?: string;
|
|
44
|
+
host?: string;
|
|
45
|
+
database?: string;
|
|
46
|
+
user?: string;
|
|
47
|
+
password?: string;
|
|
48
|
+
containerName?: string;
|
|
49
|
+
startupTimeoutMs?: number;
|
|
50
|
+
pollIntervalMs?: number;
|
|
51
|
+
dockerBinary?: string;
|
|
52
|
+
runDocker?: DockerRunner;
|
|
53
|
+
};
|
|
54
|
+
type EphemeralRedisOptions = {
|
|
55
|
+
image?: string;
|
|
56
|
+
host?: string;
|
|
57
|
+
containerName?: string;
|
|
58
|
+
startupTimeoutMs?: number;
|
|
59
|
+
pollIntervalMs?: number;
|
|
60
|
+
dockerBinary?: string;
|
|
61
|
+
runDocker?: DockerRunner;
|
|
62
|
+
};
|
|
63
|
+
type MigrationDeployOptions = {
|
|
64
|
+
databaseUrl: string;
|
|
65
|
+
prismaSchemaPath: string;
|
|
66
|
+
cwd?: string;
|
|
67
|
+
prismaBinary?: string;
|
|
68
|
+
runCommand?: CommandRunner;
|
|
69
|
+
};
|
|
70
|
+
type DisposableIntegrationInfrastructureOptions = {
|
|
71
|
+
prismaSchemaPath: string;
|
|
72
|
+
cwd?: string;
|
|
73
|
+
prismaBinary?: string;
|
|
74
|
+
postgres?: EphemeralPostgresOptions;
|
|
75
|
+
redis?: EphemeralRedisOptions;
|
|
76
|
+
runCommand?: CommandRunner;
|
|
77
|
+
deployMigrations?: (options: MigrationDeployOptions) => Promise<void>;
|
|
78
|
+
};
|
|
79
|
+
type DisposableIntegrationInfrastructure = {
|
|
80
|
+
postgres: PostgresConnectionDetails;
|
|
81
|
+
redis: RedisConnectionDetails;
|
|
82
|
+
databaseUrl: string;
|
|
83
|
+
redisUrl: string;
|
|
84
|
+
environment: DisposableEnvironment;
|
|
85
|
+
};
|
|
86
|
+
type DisposableIntegrationCallback<T> = (infrastructure: DisposableIntegrationInfrastructure) => Promise<T> | T;
|
|
87
|
+
declare function createCommandRunner(binary?: string): CommandRunner;
|
|
88
|
+
declare function createDockerRunner(dockerBinary?: string): DockerRunner;
|
|
89
|
+
declare class EphemeralPostgres {
|
|
90
|
+
private readonly image;
|
|
91
|
+
private readonly host;
|
|
92
|
+
private readonly database;
|
|
93
|
+
private readonly user;
|
|
94
|
+
private readonly password;
|
|
95
|
+
private readonly containerName;
|
|
96
|
+
private readonly startupTimeoutMs;
|
|
97
|
+
private readonly pollIntervalMs;
|
|
98
|
+
private readonly runDocker;
|
|
99
|
+
private port;
|
|
100
|
+
private started;
|
|
101
|
+
constructor(options?: EphemeralPostgresOptions);
|
|
102
|
+
get databaseUrl(): string;
|
|
103
|
+
get connectionDetails(): PostgresConnectionDetails;
|
|
104
|
+
start(): Promise<PostgresConnectionDetails>;
|
|
105
|
+
waitForReady(): Promise<void>;
|
|
106
|
+
cleanup(): Promise<void>;
|
|
107
|
+
}
|
|
108
|
+
declare class EphemeralRedis {
|
|
109
|
+
private readonly image;
|
|
110
|
+
private readonly host;
|
|
111
|
+
private readonly containerName;
|
|
112
|
+
private readonly startupTimeoutMs;
|
|
113
|
+
private readonly pollIntervalMs;
|
|
114
|
+
private readonly runDocker;
|
|
115
|
+
private port;
|
|
116
|
+
private started;
|
|
117
|
+
constructor(options?: EphemeralRedisOptions);
|
|
118
|
+
get url(): string;
|
|
119
|
+
get connectionDetails(): RedisConnectionDetails;
|
|
120
|
+
start(): Promise<RedisConnectionDetails>;
|
|
121
|
+
waitForReady(): Promise<void>;
|
|
122
|
+
cleanup(): Promise<void>;
|
|
123
|
+
}
|
|
124
|
+
declare function deployPrismaMigrations(options: MigrationDeployOptions): Promise<void>;
|
|
125
|
+
declare function withDisposableIntegrationInfrastructure<T>(options: DisposableIntegrationInfrastructureOptions, callback: DisposableIntegrationCallback<T>): Promise<T>;
|
|
126
|
+
|
|
127
|
+
export { type CommandOptions, type CommandResult, type CommandRunner, DEFAULT_COMMAND_TIMEOUT_MS, DEFAULT_DOCKER_COMMAND_TIMEOUT_MS, DEFAULT_HOST, DEFAULT_POLL_INTERVAL_MS, DEFAULT_POSTGRES_IMAGE, DEFAULT_POSTGRES_STARTUP_TIMEOUT_MS, DEFAULT_REDIS_IMAGE, DEFAULT_REDIS_STARTUP_TIMEOUT_MS, type DisposableEnvironment, type DisposableIntegrationCallback, type DisposableIntegrationInfrastructure, type DisposableIntegrationInfrastructureOptions, type DockerRunner, EphemeralPostgres, type EphemeralPostgresOptions, EphemeralRedis, type EphemeralRedisOptions, type MigrationDeployOptions, type PostgresConnectionDetails, type RedisConnectionDetails, createCommandRunner, createDockerRunner, deployPrismaMigrations, withDisposableIntegrationInfrastructure };
|
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
// src/testing/node.ts
|
|
2
|
+
import { execFile } from "child_process";
|
|
3
|
+
import { promisify } from "util";
|
|
4
|
+
import { randomUUID } from "crypto";
|
|
5
|
+
var execFileAsync = promisify(execFile);
|
|
6
|
+
var DEFAULT_POSTGRES_IMAGE = "postgres:17.6-alpine";
|
|
7
|
+
var DEFAULT_REDIS_IMAGE = "redis:7.4.2-alpine";
|
|
8
|
+
var DEFAULT_HOST = "127.0.0.1";
|
|
9
|
+
var DEFAULT_POSTGRES_STARTUP_TIMEOUT_MS = 3e4;
|
|
10
|
+
var DEFAULT_REDIS_STARTUP_TIMEOUT_MS = 15e3;
|
|
11
|
+
var DEFAULT_POLL_INTERVAL_MS = 200;
|
|
12
|
+
var DEFAULT_COMMAND_TIMEOUT_MS = 12e4;
|
|
13
|
+
var DEFAULT_DOCKER_COMMAND_TIMEOUT_MS = 3e4;
|
|
14
|
+
function createCommandRunner(binary = "") {
|
|
15
|
+
return async (command, args, options = {}) => {
|
|
16
|
+
const executable = binary || command;
|
|
17
|
+
const commandArgs = binary ? [command, ...args] : args;
|
|
18
|
+
const result = await execFileAsync(executable, commandArgs, {
|
|
19
|
+
encoding: "utf8",
|
|
20
|
+
maxBuffer: 1024 * 1024,
|
|
21
|
+
timeout: DEFAULT_COMMAND_TIMEOUT_MS,
|
|
22
|
+
...options
|
|
23
|
+
});
|
|
24
|
+
return {
|
|
25
|
+
stdout: result.stdout.trim(),
|
|
26
|
+
stderr: result.stderr.trim()
|
|
27
|
+
};
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
function createDockerRunner(dockerBinary = "docker") {
|
|
31
|
+
const runCommand = createCommandRunner();
|
|
32
|
+
return (args, options = {}) => runCommand(dockerBinary, args, {
|
|
33
|
+
...options,
|
|
34
|
+
timeout: options.timeout ?? DEFAULT_DOCKER_COMMAND_TIMEOUT_MS
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
function parseMappedPort(output, serviceName) {
|
|
38
|
+
const match = output.match(/:(\d+)\s*$/m);
|
|
39
|
+
if (!match) {
|
|
40
|
+
throw new Error(`Docker did not return a mapped ${serviceName} port: ${output}`);
|
|
41
|
+
}
|
|
42
|
+
return Number(match[1]);
|
|
43
|
+
}
|
|
44
|
+
function sleep(milliseconds) {
|
|
45
|
+
return new Promise((resolve) => setTimeout(resolve, milliseconds));
|
|
46
|
+
}
|
|
47
|
+
function createSuffix() {
|
|
48
|
+
return randomUUID().replaceAll("-", "");
|
|
49
|
+
}
|
|
50
|
+
function requireStarted(value, serviceName) {
|
|
51
|
+
if (value === null) {
|
|
52
|
+
throw new Error(`Ephemeral ${serviceName} has not been started`);
|
|
53
|
+
}
|
|
54
|
+
return value;
|
|
55
|
+
}
|
|
56
|
+
var EphemeralPostgres = class {
|
|
57
|
+
image;
|
|
58
|
+
host;
|
|
59
|
+
database;
|
|
60
|
+
user;
|
|
61
|
+
password;
|
|
62
|
+
containerName;
|
|
63
|
+
startupTimeoutMs;
|
|
64
|
+
pollIntervalMs;
|
|
65
|
+
runDocker;
|
|
66
|
+
port = null;
|
|
67
|
+
started = false;
|
|
68
|
+
constructor(options = {}) {
|
|
69
|
+
const suffix = createSuffix();
|
|
70
|
+
this.image = options.image ?? DEFAULT_POSTGRES_IMAGE;
|
|
71
|
+
this.host = options.host ?? DEFAULT_HOST;
|
|
72
|
+
this.database = options.database ?? `moda_test_${suffix.slice(0, 12)}`;
|
|
73
|
+
this.user = options.user ?? `moda_test_${suffix.slice(0, 12)}`;
|
|
74
|
+
this.password = options.password ?? `test_${suffix}`;
|
|
75
|
+
this.containerName = options.containerName ?? `moda-test-postgres-${suffix}`;
|
|
76
|
+
this.startupTimeoutMs = options.startupTimeoutMs ?? DEFAULT_POSTGRES_STARTUP_TIMEOUT_MS;
|
|
77
|
+
this.pollIntervalMs = options.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS;
|
|
78
|
+
this.runDocker = options.runDocker ?? createDockerRunner(options.dockerBinary);
|
|
79
|
+
}
|
|
80
|
+
get databaseUrl() {
|
|
81
|
+
const port = requireStarted(this.port, "PostgreSQL");
|
|
82
|
+
return `postgresql://${this.user}:${this.password}@${this.host}:${port}/${this.database}`;
|
|
83
|
+
}
|
|
84
|
+
get connectionDetails() {
|
|
85
|
+
return {
|
|
86
|
+
host: this.host,
|
|
87
|
+
port: requireStarted(this.port, "PostgreSQL"),
|
|
88
|
+
database: this.database,
|
|
89
|
+
user: this.user,
|
|
90
|
+
password: this.password,
|
|
91
|
+
databaseUrl: this.databaseUrl,
|
|
92
|
+
containerName: this.containerName
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
async start() {
|
|
96
|
+
if (this.started) return this.connectionDetails;
|
|
97
|
+
try {
|
|
98
|
+
await this.runDocker([
|
|
99
|
+
"run",
|
|
100
|
+
"--detach",
|
|
101
|
+
"--name",
|
|
102
|
+
this.containerName,
|
|
103
|
+
"--env",
|
|
104
|
+
`POSTGRES_DB=${this.database}`,
|
|
105
|
+
"--env",
|
|
106
|
+
`POSTGRES_USER=${this.user}`,
|
|
107
|
+
"--env",
|
|
108
|
+
`POSTGRES_PASSWORD=${this.password}`,
|
|
109
|
+
"--publish",
|
|
110
|
+
"0:5432",
|
|
111
|
+
this.image
|
|
112
|
+
]);
|
|
113
|
+
const portResult = await this.runDocker(["port", this.containerName, "5432/tcp"]);
|
|
114
|
+
this.port = parseMappedPort(portResult.stdout, "PostgreSQL");
|
|
115
|
+
await this.waitForReady();
|
|
116
|
+
this.started = true;
|
|
117
|
+
return this.connectionDetails;
|
|
118
|
+
} catch (error) {
|
|
119
|
+
await this.cleanup();
|
|
120
|
+
throw new Error(
|
|
121
|
+
`Unable to start ephemeral PostgreSQL (${this.image}): ${errorMessage(error)}`,
|
|
122
|
+
{ cause: error }
|
|
123
|
+
);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
async waitForReady() {
|
|
127
|
+
const deadline = Date.now() + this.startupTimeoutMs;
|
|
128
|
+
let lastError;
|
|
129
|
+
while (Date.now() < deadline) {
|
|
130
|
+
try {
|
|
131
|
+
const result = await this.runDocker([
|
|
132
|
+
"exec",
|
|
133
|
+
this.containerName,
|
|
134
|
+
"pg_isready",
|
|
135
|
+
"--username",
|
|
136
|
+
this.user,
|
|
137
|
+
"--dbname",
|
|
138
|
+
this.database
|
|
139
|
+
]);
|
|
140
|
+
if (/accepting connections/i.test(result.stdout)) return;
|
|
141
|
+
lastError = new Error(`Unexpected PostgreSQL readiness response: ${result.stdout}`);
|
|
142
|
+
} catch (error) {
|
|
143
|
+
lastError = error;
|
|
144
|
+
}
|
|
145
|
+
await sleep(Math.min(this.pollIntervalMs, Math.max(0, deadline - Date.now())));
|
|
146
|
+
}
|
|
147
|
+
throw new Error(
|
|
148
|
+
`PostgreSQL readiness timed out after ${this.startupTimeoutMs}ms${lastError ? `: ${errorMessage(lastError)}` : ""}`
|
|
149
|
+
);
|
|
150
|
+
}
|
|
151
|
+
async cleanup() {
|
|
152
|
+
this.port = null;
|
|
153
|
+
this.started = false;
|
|
154
|
+
await this.runDocker(["rm", "--force", "--volumes", this.containerName]).catch(() => void 0);
|
|
155
|
+
}
|
|
156
|
+
};
|
|
157
|
+
var EphemeralRedis = class {
|
|
158
|
+
image;
|
|
159
|
+
host;
|
|
160
|
+
containerName;
|
|
161
|
+
startupTimeoutMs;
|
|
162
|
+
pollIntervalMs;
|
|
163
|
+
runDocker;
|
|
164
|
+
port = null;
|
|
165
|
+
started = false;
|
|
166
|
+
constructor(options = {}) {
|
|
167
|
+
const suffix = createSuffix();
|
|
168
|
+
this.image = options.image ?? DEFAULT_REDIS_IMAGE;
|
|
169
|
+
this.host = options.host ?? DEFAULT_HOST;
|
|
170
|
+
this.containerName = options.containerName ?? `moda-test-redis-${suffix}`;
|
|
171
|
+
this.startupTimeoutMs = options.startupTimeoutMs ?? DEFAULT_REDIS_STARTUP_TIMEOUT_MS;
|
|
172
|
+
this.pollIntervalMs = options.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS;
|
|
173
|
+
this.runDocker = options.runDocker ?? createDockerRunner(options.dockerBinary);
|
|
174
|
+
}
|
|
175
|
+
get url() {
|
|
176
|
+
const port = requireStarted(this.port, "Redis");
|
|
177
|
+
return `redis://${this.host}:${port}`;
|
|
178
|
+
}
|
|
179
|
+
get connectionDetails() {
|
|
180
|
+
return {
|
|
181
|
+
host: this.host,
|
|
182
|
+
port: requireStarted(this.port, "Redis"),
|
|
183
|
+
url: this.url,
|
|
184
|
+
containerName: this.containerName
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
async start() {
|
|
188
|
+
if (this.started) return this.connectionDetails;
|
|
189
|
+
try {
|
|
190
|
+
await this.runDocker([
|
|
191
|
+
"run",
|
|
192
|
+
"--detach",
|
|
193
|
+
"--name",
|
|
194
|
+
this.containerName,
|
|
195
|
+
"--publish",
|
|
196
|
+
"0:6379",
|
|
197
|
+
this.image
|
|
198
|
+
]);
|
|
199
|
+
const portResult = await this.runDocker(["port", this.containerName, "6379/tcp"]);
|
|
200
|
+
this.port = parseMappedPort(portResult.stdout, "Redis");
|
|
201
|
+
await this.waitForReady();
|
|
202
|
+
this.started = true;
|
|
203
|
+
return this.connectionDetails;
|
|
204
|
+
} catch (error) {
|
|
205
|
+
await this.cleanup();
|
|
206
|
+
throw new Error(
|
|
207
|
+
`Unable to start ephemeral Redis (${this.image}): ${errorMessage(error)}`,
|
|
208
|
+
{ cause: error }
|
|
209
|
+
);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
async waitForReady() {
|
|
213
|
+
const deadline = Date.now() + this.startupTimeoutMs;
|
|
214
|
+
let lastError;
|
|
215
|
+
while (Date.now() < deadline) {
|
|
216
|
+
try {
|
|
217
|
+
const result = await this.runDocker(["exec", this.containerName, "redis-cli", "ping"]);
|
|
218
|
+
if (result.stdout === "PONG") return;
|
|
219
|
+
lastError = new Error(`Unexpected Redis readiness response: ${result.stdout}`);
|
|
220
|
+
} catch (error) {
|
|
221
|
+
lastError = error;
|
|
222
|
+
}
|
|
223
|
+
await sleep(Math.min(this.pollIntervalMs, Math.max(0, deadline - Date.now())));
|
|
224
|
+
}
|
|
225
|
+
throw new Error(
|
|
226
|
+
`Redis readiness timed out after ${this.startupTimeoutMs}ms${lastError ? `: ${errorMessage(lastError)}` : ""}`
|
|
227
|
+
);
|
|
228
|
+
}
|
|
229
|
+
async cleanup() {
|
|
230
|
+
this.port = null;
|
|
231
|
+
this.started = false;
|
|
232
|
+
await this.runDocker(["rm", "--force", "--volumes", this.containerName]).catch(() => void 0);
|
|
233
|
+
}
|
|
234
|
+
};
|
|
235
|
+
async function deployPrismaMigrations(options) {
|
|
236
|
+
const runCommand = options.runCommand ?? createCommandRunner();
|
|
237
|
+
await runCommand(
|
|
238
|
+
options.prismaBinary ?? "prisma",
|
|
239
|
+
["migrate", "deploy", "--schema", options.prismaSchemaPath],
|
|
240
|
+
{
|
|
241
|
+
cwd: options.cwd,
|
|
242
|
+
env: { ...process.env, DATABASE_URL: options.databaseUrl }
|
|
243
|
+
}
|
|
244
|
+
);
|
|
245
|
+
}
|
|
246
|
+
async function withDisposableIntegrationInfrastructure(options, callback) {
|
|
247
|
+
const postgres = new EphemeralPostgres(options.postgres);
|
|
248
|
+
const redis = new EphemeralRedis(options.redis);
|
|
249
|
+
try {
|
|
250
|
+
const postgresDetails = await postgres.start();
|
|
251
|
+
const redisDetails = await redis.start();
|
|
252
|
+
const environment = {
|
|
253
|
+
DATABASE_URL: postgresDetails.databaseUrl,
|
|
254
|
+
TEST_DATABASE_URL: postgresDetails.databaseUrl,
|
|
255
|
+
REDIS_URL: redisDetails.url,
|
|
256
|
+
TEST_REDIS_URL: redisDetails.url
|
|
257
|
+
};
|
|
258
|
+
const deployMigrations = options.deployMigrations ?? deployPrismaMigrations;
|
|
259
|
+
await deployMigrations({
|
|
260
|
+
databaseUrl: postgresDetails.databaseUrl,
|
|
261
|
+
prismaSchemaPath: options.prismaSchemaPath,
|
|
262
|
+
cwd: options.cwd,
|
|
263
|
+
prismaBinary: options.prismaBinary,
|
|
264
|
+
runCommand: options.runCommand
|
|
265
|
+
});
|
|
266
|
+
return await callback({
|
|
267
|
+
postgres: postgresDetails,
|
|
268
|
+
redis: redisDetails,
|
|
269
|
+
databaseUrl: postgresDetails.databaseUrl,
|
|
270
|
+
redisUrl: redisDetails.url,
|
|
271
|
+
environment
|
|
272
|
+
});
|
|
273
|
+
} finally {
|
|
274
|
+
await Promise.all([postgres.cleanup(), redis.cleanup()]);
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
function errorMessage(error) {
|
|
278
|
+
return error instanceof Error ? error.message : String(error);
|
|
279
|
+
}
|
|
280
|
+
export {
|
|
281
|
+
DEFAULT_COMMAND_TIMEOUT_MS,
|
|
282
|
+
DEFAULT_DOCKER_COMMAND_TIMEOUT_MS,
|
|
283
|
+
DEFAULT_HOST,
|
|
284
|
+
DEFAULT_POLL_INTERVAL_MS,
|
|
285
|
+
DEFAULT_POSTGRES_IMAGE,
|
|
286
|
+
DEFAULT_POSTGRES_STARTUP_TIMEOUT_MS,
|
|
287
|
+
DEFAULT_REDIS_IMAGE,
|
|
288
|
+
DEFAULT_REDIS_STARTUP_TIMEOUT_MS,
|
|
289
|
+
EphemeralPostgres,
|
|
290
|
+
EphemeralRedis,
|
|
291
|
+
createCommandRunner,
|
|
292
|
+
createDockerRunner,
|
|
293
|
+
deployPrismaMigrations,
|
|
294
|
+
withDisposableIntegrationInfrastructure
|
|
295
|
+
};
|
|
296
|
+
//# sourceMappingURL=node.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/testing/node.ts"],"sourcesContent":["import { execFile } from \"node:child_process\";\nimport { promisify } from \"node:util\";\nimport { randomUUID } from \"node:crypto\";\n\nconst execFileAsync = promisify(execFile);\n\nexport const DEFAULT_POSTGRES_IMAGE = \"postgres:17.6-alpine\";\nexport const DEFAULT_REDIS_IMAGE = \"redis:7.4.2-alpine\";\nexport const DEFAULT_HOST = \"127.0.0.1\";\nexport const DEFAULT_POSTGRES_STARTUP_TIMEOUT_MS = 30_000;\nexport const DEFAULT_REDIS_STARTUP_TIMEOUT_MS = 15_000;\nexport const DEFAULT_POLL_INTERVAL_MS = 200;\nexport const DEFAULT_COMMAND_TIMEOUT_MS = 120_000;\nexport const DEFAULT_DOCKER_COMMAND_TIMEOUT_MS = 30_000;\n\nexport type CommandResult = {\n stdout: string;\n stderr: string;\n};\n\nexport type CommandOptions = {\n cwd?: string;\n env?: NodeJS.ProcessEnv;\n timeout?: number;\n killSignal?: NodeJS.Signals;\n};\n\nexport type CommandRunner = (\n command: string,\n args: readonly string[],\n options?: CommandOptions,\n) => Promise<CommandResult>;\n\nexport type DockerRunner = (\n args: readonly string[],\n options?: CommandOptions,\n) => Promise<CommandResult>;\n\nexport type DisposableEnvironment = {\n DATABASE_URL: string;\n TEST_DATABASE_URL: string;\n REDIS_URL: string;\n TEST_REDIS_URL: string;\n};\n\nexport type PostgresConnectionDetails = {\n host: string;\n port: number;\n database: string;\n user: string;\n password: string;\n databaseUrl: string;\n containerName: string;\n};\n\nexport type RedisConnectionDetails = {\n host: string;\n port: number;\n url: string;\n containerName: string;\n};\n\nexport type EphemeralPostgresOptions = {\n image?: string;\n host?: string;\n database?: string;\n user?: string;\n password?: string;\n containerName?: string;\n startupTimeoutMs?: number;\n pollIntervalMs?: number;\n dockerBinary?: string;\n runDocker?: DockerRunner;\n};\n\nexport type EphemeralRedisOptions = {\n image?: string;\n host?: string;\n containerName?: string;\n startupTimeoutMs?: number;\n pollIntervalMs?: number;\n dockerBinary?: string;\n runDocker?: DockerRunner;\n};\n\nexport type MigrationDeployOptions = {\n databaseUrl: string;\n prismaSchemaPath: string;\n cwd?: string;\n prismaBinary?: string;\n runCommand?: CommandRunner;\n};\n\nexport type DisposableIntegrationInfrastructureOptions = {\n prismaSchemaPath: string;\n cwd?: string;\n prismaBinary?: string;\n postgres?: EphemeralPostgresOptions;\n redis?: EphemeralRedisOptions;\n runCommand?: CommandRunner;\n deployMigrations?: (options: MigrationDeployOptions) => Promise<void>;\n};\n\nexport type DisposableIntegrationInfrastructure = {\n postgres: PostgresConnectionDetails;\n redis: RedisConnectionDetails;\n databaseUrl: string;\n redisUrl: string;\n environment: DisposableEnvironment;\n};\n\nexport type DisposableIntegrationCallback<T> = (\n infrastructure: DisposableIntegrationInfrastructure,\n) => Promise<T> | T;\n\nexport function createCommandRunner(binary = \"\"): CommandRunner {\n return async (command, args, options = {}) => {\n const executable = binary || command;\n const commandArgs = binary ? [command, ...args] : args;\n const result = await execFileAsync(executable, commandArgs, {\n encoding: \"utf8\",\n maxBuffer: 1024 * 1024,\n timeout: DEFAULT_COMMAND_TIMEOUT_MS,\n ...options,\n });\n return {\n stdout: result.stdout.trim(),\n stderr: result.stderr.trim(),\n };\n };\n}\n\nexport function createDockerRunner(dockerBinary = \"docker\"): DockerRunner {\n const runCommand = createCommandRunner();\n return (args, options = {}) =>\n runCommand(dockerBinary, args, {\n ...options,\n timeout: options.timeout ?? DEFAULT_DOCKER_COMMAND_TIMEOUT_MS,\n });\n}\n\nfunction parseMappedPort(output: string, serviceName: string): number {\n const match = output.match(/:(\\d+)\\s*$/m);\n if (!match) {\n throw new Error(`Docker did not return a mapped ${serviceName} port: ${output}`);\n }\n return Number(match[1]);\n}\n\nfunction sleep(milliseconds: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, milliseconds));\n}\n\nfunction createSuffix(): string {\n return randomUUID().replaceAll(\"-\", \"\");\n}\n\nfunction requireStarted<T>(value: T | null, serviceName: string): T {\n if (value === null) {\n throw new Error(`Ephemeral ${serviceName} has not been started`);\n }\n return value;\n}\n\nexport class EphemeralPostgres {\n private readonly image: string;\n private readonly host: string;\n private readonly database: string;\n private readonly user: string;\n private readonly password: string;\n private readonly containerName: string;\n private readonly startupTimeoutMs: number;\n private readonly pollIntervalMs: number;\n private readonly runDocker: DockerRunner;\n private port: number | null = null;\n private started = false;\n\n constructor(options: EphemeralPostgresOptions = {}) {\n const suffix = createSuffix();\n this.image = options.image ?? DEFAULT_POSTGRES_IMAGE;\n this.host = options.host ?? DEFAULT_HOST;\n this.database = options.database ?? `moda_test_${suffix.slice(0, 12)}`;\n this.user = options.user ?? `moda_test_${suffix.slice(0, 12)}`;\n this.password = options.password ?? `test_${suffix}`;\n this.containerName = options.containerName ?? `moda-test-postgres-${suffix}`;\n this.startupTimeoutMs = options.startupTimeoutMs ?? DEFAULT_POSTGRES_STARTUP_TIMEOUT_MS;\n this.pollIntervalMs = options.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS;\n this.runDocker = options.runDocker ?? createDockerRunner(options.dockerBinary);\n }\n\n get databaseUrl(): string {\n const port = requireStarted(this.port, \"PostgreSQL\");\n return `postgresql://${this.user}:${this.password}@${this.host}:${port}/${this.database}`;\n }\n\n get connectionDetails(): PostgresConnectionDetails {\n return {\n host: this.host,\n port: requireStarted(this.port, \"PostgreSQL\"),\n database: this.database,\n user: this.user,\n password: this.password,\n databaseUrl: this.databaseUrl,\n containerName: this.containerName,\n };\n }\n\n async start(): Promise<PostgresConnectionDetails> {\n if (this.started) return this.connectionDetails;\n\n try {\n await this.runDocker([\n \"run\",\n \"--detach\",\n \"--name\",\n this.containerName,\n \"--env\",\n `POSTGRES_DB=${this.database}`,\n \"--env\",\n `POSTGRES_USER=${this.user}`,\n \"--env\",\n `POSTGRES_PASSWORD=${this.password}`,\n \"--publish\",\n \"0:5432\",\n this.image,\n ]);\n const portResult = await this.runDocker([\"port\", this.containerName, \"5432/tcp\"]);\n this.port = parseMappedPort(portResult.stdout, \"PostgreSQL\");\n await this.waitForReady();\n this.started = true;\n return this.connectionDetails;\n } catch (error) {\n await this.cleanup();\n throw new Error(\n `Unable to start ephemeral PostgreSQL (${this.image}): ${errorMessage(error)}`,\n { cause: error },\n );\n }\n }\n\n async waitForReady(): Promise<void> {\n const deadline = Date.now() + this.startupTimeoutMs;\n let lastError: unknown;\n\n while (Date.now() < deadline) {\n try {\n const result = await this.runDocker([\n \"exec\",\n this.containerName,\n \"pg_isready\",\n \"--username\",\n this.user,\n \"--dbname\",\n this.database,\n ]);\n if (/accepting connections/i.test(result.stdout)) return;\n lastError = new Error(`Unexpected PostgreSQL readiness response: ${result.stdout}`);\n } catch (error) {\n lastError = error;\n }\n await sleep(Math.min(this.pollIntervalMs, Math.max(0, deadline - Date.now())));\n }\n\n throw new Error(\n `PostgreSQL readiness timed out after ${this.startupTimeoutMs}ms${lastError ? `: ${errorMessage(lastError)}` : \"\"}`,\n );\n }\n\n async cleanup(): Promise<void> {\n this.port = null;\n this.started = false;\n await this.runDocker([\"rm\", \"--force\", \"--volumes\", this.containerName]).catch(() => undefined);\n }\n}\n\nexport class EphemeralRedis {\n private readonly image: string;\n private readonly host: string;\n private readonly containerName: string;\n private readonly startupTimeoutMs: number;\n private readonly pollIntervalMs: number;\n private readonly runDocker: DockerRunner;\n private port: number | null = null;\n private started = false;\n\n constructor(options: EphemeralRedisOptions = {}) {\n const suffix = createSuffix();\n this.image = options.image ?? DEFAULT_REDIS_IMAGE;\n this.host = options.host ?? DEFAULT_HOST;\n this.containerName = options.containerName ?? `moda-test-redis-${suffix}`;\n this.startupTimeoutMs = options.startupTimeoutMs ?? DEFAULT_REDIS_STARTUP_TIMEOUT_MS;\n this.pollIntervalMs = options.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS;\n this.runDocker = options.runDocker ?? createDockerRunner(options.dockerBinary);\n }\n\n get url(): string {\n const port = requireStarted(this.port, \"Redis\");\n return `redis://${this.host}:${port}`;\n }\n\n get connectionDetails(): RedisConnectionDetails {\n return {\n host: this.host,\n port: requireStarted(this.port, \"Redis\"),\n url: this.url,\n containerName: this.containerName,\n };\n }\n\n async start(): Promise<RedisConnectionDetails> {\n if (this.started) return this.connectionDetails;\n\n try {\n await this.runDocker([\n \"run\",\n \"--detach\",\n \"--name\",\n this.containerName,\n \"--publish\",\n \"0:6379\",\n this.image,\n ]);\n const portResult = await this.runDocker([\"port\", this.containerName, \"6379/tcp\"]);\n this.port = parseMappedPort(portResult.stdout, \"Redis\");\n await this.waitForReady();\n this.started = true;\n return this.connectionDetails;\n } catch (error) {\n await this.cleanup();\n throw new Error(\n `Unable to start ephemeral Redis (${this.image}): ${errorMessage(error)}`,\n { cause: error },\n );\n }\n }\n\n async waitForReady(): Promise<void> {\n const deadline = Date.now() + this.startupTimeoutMs;\n let lastError: unknown;\n\n while (Date.now() < deadline) {\n try {\n const result = await this.runDocker([\"exec\", this.containerName, \"redis-cli\", \"ping\"]);\n if (result.stdout === \"PONG\") return;\n lastError = new Error(`Unexpected Redis readiness response: ${result.stdout}`);\n } catch (error) {\n lastError = error;\n }\n await sleep(Math.min(this.pollIntervalMs, Math.max(0, deadline - Date.now())));\n }\n\n throw new Error(\n `Redis readiness timed out after ${this.startupTimeoutMs}ms${lastError ? `: ${errorMessage(lastError)}` : \"\"}`,\n );\n }\n\n async cleanup(): Promise<void> {\n this.port = null;\n this.started = false;\n await this.runDocker([\"rm\", \"--force\", \"--volumes\", this.containerName]).catch(() => undefined);\n }\n}\n\nexport async function deployPrismaMigrations(options: MigrationDeployOptions): Promise<void> {\n const runCommand = options.runCommand ?? createCommandRunner();\n await runCommand(\n options.prismaBinary ?? \"prisma\",\n [\"migrate\", \"deploy\", \"--schema\", options.prismaSchemaPath],\n {\n cwd: options.cwd,\n env: { ...process.env, DATABASE_URL: options.databaseUrl },\n },\n );\n}\n\nexport async function withDisposableIntegrationInfrastructure<T>(\n options: DisposableIntegrationInfrastructureOptions,\n callback: DisposableIntegrationCallback<T>,\n): Promise<T> {\n const postgres = new EphemeralPostgres(options.postgres);\n const redis = new EphemeralRedis(options.redis);\n\n try {\n const postgresDetails = await postgres.start();\n const redisDetails = await redis.start();\n const environment: DisposableEnvironment = {\n DATABASE_URL: postgresDetails.databaseUrl,\n TEST_DATABASE_URL: postgresDetails.databaseUrl,\n REDIS_URL: redisDetails.url,\n TEST_REDIS_URL: redisDetails.url,\n };\n const deployMigrations = options.deployMigrations ?? deployPrismaMigrations;\n await deployMigrations({\n databaseUrl: postgresDetails.databaseUrl,\n prismaSchemaPath: options.prismaSchemaPath,\n cwd: options.cwd,\n prismaBinary: options.prismaBinary,\n runCommand: options.runCommand,\n });\n return await callback({\n postgres: postgresDetails,\n redis: redisDetails,\n databaseUrl: postgresDetails.databaseUrl,\n redisUrl: redisDetails.url,\n environment,\n });\n } finally {\n await Promise.all([postgres.cleanup(), redis.cleanup()]);\n }\n}\n\nfunction errorMessage(error: unknown): string {\n return error instanceof Error ? error.message : String(error);\n}"],"mappings":"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","names":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@modainteract/moda-interact-shared",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.2",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
@@ -21,8 +21,12 @@
|
|
|
21
21
|
"import": "./dist/merchant-communications.js"
|
|
22
22
|
},
|
|
23
23
|
"./merchant-communications/node": {
|
|
24
|
-
"types": "./dist/merchant-communications
|
|
25
|
-
"import": "./dist/merchant-communications
|
|
24
|
+
"types": "./dist/merchant-communications/node.d.ts",
|
|
25
|
+
"import": "./dist/merchant-communications/node.js"
|
|
26
|
+
},
|
|
27
|
+
"./testing/node": {
|
|
28
|
+
"types": "./dist/testing/node.d.ts",
|
|
29
|
+
"import": "./dist/testing/node.js"
|
|
26
30
|
},
|
|
27
31
|
"./shopify": {
|
|
28
32
|
"types": "./dist/shopify/index.d.ts",
|
|
@@ -65,6 +69,9 @@
|
|
|
65
69
|
"typecheck": "tsc --noEmit",
|
|
66
70
|
"test": "tsx --test \"src/**/*.test.ts\"",
|
|
67
71
|
"validate:internationalization-entrypoint": "npm run build && node scripts/validate-internationalization-entrypoint.mjs",
|
|
72
|
+
"validate:merchant-communications-node": "npm run build && node scripts/validate-merchant-communications-node.mjs",
|
|
73
|
+
"validate:testing-node": "npm run build && node scripts/validate-testing-node.mjs",
|
|
74
|
+
"validate:testing-node-live": "npm run build && node scripts/validate-testing-node-live.mjs",
|
|
68
75
|
"prepack": "npm run build"
|
|
69
76
|
},
|
|
70
77
|
"dependencies": {
|