@forklaunch/testing 0.0.4 → 0.0.5
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/lib/index.d.mts +69 -2
- package/lib/index.d.ts +69 -2
- package/lib/index.js +109 -2
- package/lib/index.js.map +1 -1
- package/lib/index.mjs +109 -2
- package/lib/index.mjs.map +1 -1
- package/package.json +1 -1
package/lib/index.d.mts
CHANGED
|
@@ -32,6 +32,26 @@ interface SQLiteConfig {
|
|
|
32
32
|
interface RedisConfig {
|
|
33
33
|
command?: string[];
|
|
34
34
|
}
|
|
35
|
+
interface KafkaConfig {
|
|
36
|
+
/** Kafka cluster ID */
|
|
37
|
+
clusterId?: string;
|
|
38
|
+
/** Number of partitions */
|
|
39
|
+
numPartitions?: number;
|
|
40
|
+
/** Replication factor */
|
|
41
|
+
replicationFactor?: number;
|
|
42
|
+
/** Additional environment variables */
|
|
43
|
+
env?: Record<string, string>;
|
|
44
|
+
}
|
|
45
|
+
interface S3Config {
|
|
46
|
+
/** MinIO root user (access key) */
|
|
47
|
+
rootUser?: string;
|
|
48
|
+
/** MinIO root password (secret key) */
|
|
49
|
+
rootPassword?: string;
|
|
50
|
+
/** Default bucket to create */
|
|
51
|
+
defaultBucket?: string;
|
|
52
|
+
/** Region */
|
|
53
|
+
region?: string;
|
|
54
|
+
}
|
|
35
55
|
type DatabaseConfig = PostgresConfig | MySQLConfig | MongoDBConfig | MSSQLConfig | SQLiteConfig;
|
|
36
56
|
/**
|
|
37
57
|
* Manages test containers (PostgreSQL, MySQL, MongoDB, Redis, etc.) for E2E testing
|
|
@@ -66,6 +86,14 @@ declare class TestContainerManager {
|
|
|
66
86
|
* Setup Redis test container
|
|
67
87
|
*/
|
|
68
88
|
setupRedisContainer(config?: RedisConfig): Promise<StartedTestContainer>;
|
|
89
|
+
/**
|
|
90
|
+
* Setup Kafka test container
|
|
91
|
+
*/
|
|
92
|
+
setupKafkaContainer(config?: KafkaConfig): Promise<StartedTestContainer>;
|
|
93
|
+
/**
|
|
94
|
+
* Setup MinIO (S3-compatible) test container
|
|
95
|
+
*/
|
|
96
|
+
setupS3Container(config?: S3Config): Promise<StartedTestContainer>;
|
|
69
97
|
/**
|
|
70
98
|
* Cleanup all containers
|
|
71
99
|
*/
|
|
@@ -76,6 +104,8 @@ interface TestEnvConfig {
|
|
|
76
104
|
database: StartedTestContainer | null;
|
|
77
105
|
databaseType?: DatabaseType;
|
|
78
106
|
redis?: StartedTestContainer;
|
|
107
|
+
kafka?: StartedTestContainer;
|
|
108
|
+
s3?: StartedTestContainer;
|
|
79
109
|
hmacSecret?: string;
|
|
80
110
|
customVars?: Record<string, string>;
|
|
81
111
|
}
|
|
@@ -141,6 +171,18 @@ interface BlueprintTestConfig {
|
|
|
141
171
|
* Whether the blueprint needs Redis
|
|
142
172
|
*/
|
|
143
173
|
needsRedis?: boolean;
|
|
174
|
+
/**
|
|
175
|
+
* Whether the blueprint needs Kafka
|
|
176
|
+
*/
|
|
177
|
+
needsKafka?: boolean;
|
|
178
|
+
/**
|
|
179
|
+
* Whether the blueprint needs S3 (MinIO)
|
|
180
|
+
*/
|
|
181
|
+
needsS3?: boolean;
|
|
182
|
+
/**
|
|
183
|
+
* S3 bucket name to create (default: 'test-bucket')
|
|
184
|
+
*/
|
|
185
|
+
s3Bucket?: string;
|
|
144
186
|
/**
|
|
145
187
|
* Custom environment variables to set
|
|
146
188
|
*/
|
|
@@ -153,6 +195,8 @@ interface BlueprintTestConfig {
|
|
|
153
195
|
interface TestSetupResult {
|
|
154
196
|
container: StartedTestContainer | null;
|
|
155
197
|
redisContainer?: StartedTestContainer;
|
|
198
|
+
kafkaContainer?: StartedTestContainer;
|
|
199
|
+
s3Container?: StartedTestContainer;
|
|
156
200
|
orm?: MikroORM;
|
|
157
201
|
redis?: Redis;
|
|
158
202
|
}
|
|
@@ -194,6 +238,29 @@ interface TestSetupResult {
|
|
|
194
238
|
* // ... run tests with setup.redis only (setup.orm is undefined)
|
|
195
239
|
* await harness.cleanup();
|
|
196
240
|
* ```
|
|
241
|
+
*
|
|
242
|
+
* @example With Kafka and S3
|
|
243
|
+
* ```typescript
|
|
244
|
+
* const harness = new BlueprintTestHarness({
|
|
245
|
+
* getConfig: async () => {
|
|
246
|
+
* const { default: config } = await import('../mikro-orm.config');
|
|
247
|
+
* return config;
|
|
248
|
+
* },
|
|
249
|
+
* databaseType: 'postgres',
|
|
250
|
+
* needsKafka: true,
|
|
251
|
+
* needsS3: true,
|
|
252
|
+
* s3Bucket: 'my-test-bucket',
|
|
253
|
+
* customEnvVars: {
|
|
254
|
+
* API_KEY: 'test_key'
|
|
255
|
+
* }
|
|
256
|
+
* });
|
|
257
|
+
*
|
|
258
|
+
* const setup = await harness.setup();
|
|
259
|
+
* // Access containers via setup.kafkaContainer and setup.s3Container
|
|
260
|
+
* // Kafka broker: process.env.KAFKA_BROKERS
|
|
261
|
+
* // S3 endpoint: process.env.S3_ENDPOINT
|
|
262
|
+
* await harness.cleanup();
|
|
263
|
+
* ```
|
|
197
264
|
*/
|
|
198
265
|
declare class BlueprintTestHarness {
|
|
199
266
|
private config;
|
|
@@ -201,7 +268,7 @@ declare class BlueprintTestHarness {
|
|
|
201
268
|
private result?;
|
|
202
269
|
constructor(config: BlueprintTestConfig);
|
|
203
270
|
/**
|
|
204
|
-
* Setup all test infrastructure (containers, ORM, Redis)
|
|
271
|
+
* Setup all test infrastructure (containers, ORM, Redis, Kafka, S3)
|
|
205
272
|
*/
|
|
206
273
|
setup(): Promise<TestSetupResult>;
|
|
207
274
|
/**
|
|
@@ -232,4 +299,4 @@ declare const TEST_TOKENS: {
|
|
|
232
299
|
readonly HMAC_INVALID: "HMAC keyId=invalid-key ts=1234567890 nonce=invalid-nonce signature=invalid-signature";
|
|
233
300
|
};
|
|
234
301
|
|
|
235
|
-
export { type BlueprintTestConfig, BlueprintTestHarness, type DatabaseConfig, type DatabaseType, type MSSQLConfig, type MikroOrmTestConfig, type MongoDBConfig, type MySQLConfig, type PostgresConfig, type RedisConfig, type SQLiteConfig, TEST_TOKENS, TestContainerManager, type TestEnvConfig, type TestSetupResult, clearTestDatabase, setupTestEnvironment, setupTestORM };
|
|
302
|
+
export { type BlueprintTestConfig, BlueprintTestHarness, type DatabaseConfig, type DatabaseType, type KafkaConfig, type MSSQLConfig, type MikroOrmTestConfig, type MongoDBConfig, type MySQLConfig, type PostgresConfig, type RedisConfig, type S3Config, type SQLiteConfig, TEST_TOKENS, TestContainerManager, type TestEnvConfig, type TestSetupResult, clearTestDatabase, setupTestEnvironment, setupTestORM };
|
package/lib/index.d.ts
CHANGED
|
@@ -32,6 +32,26 @@ interface SQLiteConfig {
|
|
|
32
32
|
interface RedisConfig {
|
|
33
33
|
command?: string[];
|
|
34
34
|
}
|
|
35
|
+
interface KafkaConfig {
|
|
36
|
+
/** Kafka cluster ID */
|
|
37
|
+
clusterId?: string;
|
|
38
|
+
/** Number of partitions */
|
|
39
|
+
numPartitions?: number;
|
|
40
|
+
/** Replication factor */
|
|
41
|
+
replicationFactor?: number;
|
|
42
|
+
/** Additional environment variables */
|
|
43
|
+
env?: Record<string, string>;
|
|
44
|
+
}
|
|
45
|
+
interface S3Config {
|
|
46
|
+
/** MinIO root user (access key) */
|
|
47
|
+
rootUser?: string;
|
|
48
|
+
/** MinIO root password (secret key) */
|
|
49
|
+
rootPassword?: string;
|
|
50
|
+
/** Default bucket to create */
|
|
51
|
+
defaultBucket?: string;
|
|
52
|
+
/** Region */
|
|
53
|
+
region?: string;
|
|
54
|
+
}
|
|
35
55
|
type DatabaseConfig = PostgresConfig | MySQLConfig | MongoDBConfig | MSSQLConfig | SQLiteConfig;
|
|
36
56
|
/**
|
|
37
57
|
* Manages test containers (PostgreSQL, MySQL, MongoDB, Redis, etc.) for E2E testing
|
|
@@ -66,6 +86,14 @@ declare class TestContainerManager {
|
|
|
66
86
|
* Setup Redis test container
|
|
67
87
|
*/
|
|
68
88
|
setupRedisContainer(config?: RedisConfig): Promise<StartedTestContainer>;
|
|
89
|
+
/**
|
|
90
|
+
* Setup Kafka test container
|
|
91
|
+
*/
|
|
92
|
+
setupKafkaContainer(config?: KafkaConfig): Promise<StartedTestContainer>;
|
|
93
|
+
/**
|
|
94
|
+
* Setup MinIO (S3-compatible) test container
|
|
95
|
+
*/
|
|
96
|
+
setupS3Container(config?: S3Config): Promise<StartedTestContainer>;
|
|
69
97
|
/**
|
|
70
98
|
* Cleanup all containers
|
|
71
99
|
*/
|
|
@@ -76,6 +104,8 @@ interface TestEnvConfig {
|
|
|
76
104
|
database: StartedTestContainer | null;
|
|
77
105
|
databaseType?: DatabaseType;
|
|
78
106
|
redis?: StartedTestContainer;
|
|
107
|
+
kafka?: StartedTestContainer;
|
|
108
|
+
s3?: StartedTestContainer;
|
|
79
109
|
hmacSecret?: string;
|
|
80
110
|
customVars?: Record<string, string>;
|
|
81
111
|
}
|
|
@@ -141,6 +171,18 @@ interface BlueprintTestConfig {
|
|
|
141
171
|
* Whether the blueprint needs Redis
|
|
142
172
|
*/
|
|
143
173
|
needsRedis?: boolean;
|
|
174
|
+
/**
|
|
175
|
+
* Whether the blueprint needs Kafka
|
|
176
|
+
*/
|
|
177
|
+
needsKafka?: boolean;
|
|
178
|
+
/**
|
|
179
|
+
* Whether the blueprint needs S3 (MinIO)
|
|
180
|
+
*/
|
|
181
|
+
needsS3?: boolean;
|
|
182
|
+
/**
|
|
183
|
+
* S3 bucket name to create (default: 'test-bucket')
|
|
184
|
+
*/
|
|
185
|
+
s3Bucket?: string;
|
|
144
186
|
/**
|
|
145
187
|
* Custom environment variables to set
|
|
146
188
|
*/
|
|
@@ -153,6 +195,8 @@ interface BlueprintTestConfig {
|
|
|
153
195
|
interface TestSetupResult {
|
|
154
196
|
container: StartedTestContainer | null;
|
|
155
197
|
redisContainer?: StartedTestContainer;
|
|
198
|
+
kafkaContainer?: StartedTestContainer;
|
|
199
|
+
s3Container?: StartedTestContainer;
|
|
156
200
|
orm?: MikroORM;
|
|
157
201
|
redis?: Redis;
|
|
158
202
|
}
|
|
@@ -194,6 +238,29 @@ interface TestSetupResult {
|
|
|
194
238
|
* // ... run tests with setup.redis only (setup.orm is undefined)
|
|
195
239
|
* await harness.cleanup();
|
|
196
240
|
* ```
|
|
241
|
+
*
|
|
242
|
+
* @example With Kafka and S3
|
|
243
|
+
* ```typescript
|
|
244
|
+
* const harness = new BlueprintTestHarness({
|
|
245
|
+
* getConfig: async () => {
|
|
246
|
+
* const { default: config } = await import('../mikro-orm.config');
|
|
247
|
+
* return config;
|
|
248
|
+
* },
|
|
249
|
+
* databaseType: 'postgres',
|
|
250
|
+
* needsKafka: true,
|
|
251
|
+
* needsS3: true,
|
|
252
|
+
* s3Bucket: 'my-test-bucket',
|
|
253
|
+
* customEnvVars: {
|
|
254
|
+
* API_KEY: 'test_key'
|
|
255
|
+
* }
|
|
256
|
+
* });
|
|
257
|
+
*
|
|
258
|
+
* const setup = await harness.setup();
|
|
259
|
+
* // Access containers via setup.kafkaContainer and setup.s3Container
|
|
260
|
+
* // Kafka broker: process.env.KAFKA_BROKERS
|
|
261
|
+
* // S3 endpoint: process.env.S3_ENDPOINT
|
|
262
|
+
* await harness.cleanup();
|
|
263
|
+
* ```
|
|
197
264
|
*/
|
|
198
265
|
declare class BlueprintTestHarness {
|
|
199
266
|
private config;
|
|
@@ -201,7 +268,7 @@ declare class BlueprintTestHarness {
|
|
|
201
268
|
private result?;
|
|
202
269
|
constructor(config: BlueprintTestConfig);
|
|
203
270
|
/**
|
|
204
|
-
* Setup all test infrastructure (containers, ORM, Redis)
|
|
271
|
+
* Setup all test infrastructure (containers, ORM, Redis, Kafka, S3)
|
|
205
272
|
*/
|
|
206
273
|
setup(): Promise<TestSetupResult>;
|
|
207
274
|
/**
|
|
@@ -232,4 +299,4 @@ declare const TEST_TOKENS: {
|
|
|
232
299
|
readonly HMAC_INVALID: "HMAC keyId=invalid-key ts=1234567890 nonce=invalid-nonce signature=invalid-signature";
|
|
233
300
|
};
|
|
234
301
|
|
|
235
|
-
export { type BlueprintTestConfig, BlueprintTestHarness, type DatabaseConfig, type DatabaseType, type MSSQLConfig, type MikroOrmTestConfig, type MongoDBConfig, type MySQLConfig, type PostgresConfig, type RedisConfig, type SQLiteConfig, TEST_TOKENS, TestContainerManager, type TestEnvConfig, type TestSetupResult, clearTestDatabase, setupTestEnvironment, setupTestORM };
|
|
302
|
+
export { type BlueprintTestConfig, BlueprintTestHarness, type DatabaseConfig, type DatabaseType, type KafkaConfig, type MSSQLConfig, type MikroOrmTestConfig, type MongoDBConfig, type MySQLConfig, type PostgresConfig, type RedisConfig, type S3Config, type SQLiteConfig, TEST_TOKENS, TestContainerManager, type TestEnvConfig, type TestSetupResult, clearTestDatabase, setupTestEnvironment, setupTestORM };
|
package/lib/index.js
CHANGED
|
@@ -174,6 +174,74 @@ var TestContainerManager = class {
|
|
|
174
174
|
this.containers.push(container);
|
|
175
175
|
return container;
|
|
176
176
|
}
|
|
177
|
+
/**
|
|
178
|
+
* Setup Kafka test container
|
|
179
|
+
*/
|
|
180
|
+
async setupKafkaContainer(config = {}) {
|
|
181
|
+
const {
|
|
182
|
+
clusterId = "test-cluster",
|
|
183
|
+
numPartitions = 1,
|
|
184
|
+
replicationFactor = 1,
|
|
185
|
+
env = {}
|
|
186
|
+
} = config;
|
|
187
|
+
const container = await new import_testcontainers.GenericContainer("confluentinc/cp-kafka:latest").withExposedPorts(9092, 9093).withEnvironment({
|
|
188
|
+
KAFKA_BROKER_ID: "1",
|
|
189
|
+
KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: "PLAINTEXT:PLAINTEXT,PLAINTEXT_HOST:PLAINTEXT",
|
|
190
|
+
KAFKA_ADVERTISED_LISTENERS: "PLAINTEXT://kafka:29092,PLAINTEXT_HOST://localhost:9092",
|
|
191
|
+
KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: replicationFactor.toString(),
|
|
192
|
+
KAFKA_TRANSACTION_STATE_LOG_MIN_ISR: "1",
|
|
193
|
+
KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR: "1",
|
|
194
|
+
KAFKA_GROUP_INITIAL_REBALANCE_DELAY_MS: "0",
|
|
195
|
+
KAFKA_NUM_PARTITIONS: numPartitions.toString(),
|
|
196
|
+
KAFKA_CLUSTER_ID: clusterId,
|
|
197
|
+
KAFKA_PROCESS_ROLES: "broker,controller",
|
|
198
|
+
KAFKA_NODE_ID: "1",
|
|
199
|
+
KAFKA_CONTROLLER_QUORUM_VOTERS: "1@localhost:9093",
|
|
200
|
+
KAFKA_LISTENERS: "PLAINTEXT://0.0.0.0:29092,CONTROLLER://0.0.0.0:9093,PLAINTEXT_HOST://0.0.0.0:9092",
|
|
201
|
+
KAFKA_INTER_BROKER_LISTENER_NAME: "PLAINTEXT",
|
|
202
|
+
KAFKA_CONTROLLER_LISTENER_NAMES: "CONTROLLER",
|
|
203
|
+
KAFKA_LOG_DIRS: "/tmp/kraft-combined-logs",
|
|
204
|
+
...env
|
|
205
|
+
}).start();
|
|
206
|
+
await new Promise((resolve) => setTimeout(resolve, 5e3));
|
|
207
|
+
this.containers.push(container);
|
|
208
|
+
return container;
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* Setup MinIO (S3-compatible) test container
|
|
212
|
+
*/
|
|
213
|
+
async setupS3Container(config = {}) {
|
|
214
|
+
const {
|
|
215
|
+
rootUser = "minioadmin",
|
|
216
|
+
rootPassword = "minioadmin",
|
|
217
|
+
defaultBucket = "test-bucket",
|
|
218
|
+
region = "us-east-1"
|
|
219
|
+
} = config;
|
|
220
|
+
const container = await new import_testcontainers.GenericContainer("minio/minio:latest").withExposedPorts(9e3, 9001).withEnvironment({
|
|
221
|
+
MINIO_ROOT_USER: rootUser,
|
|
222
|
+
MINIO_ROOT_PASSWORD: rootPassword,
|
|
223
|
+
MINIO_REGION: region
|
|
224
|
+
}).withCommand(["server", "/data", "--console-address", ":9001"]).start();
|
|
225
|
+
await new Promise((resolve) => setTimeout(resolve, 2e3));
|
|
226
|
+
if (defaultBucket) {
|
|
227
|
+
try {
|
|
228
|
+
await container.exec([
|
|
229
|
+
"mc",
|
|
230
|
+
"alias",
|
|
231
|
+
"set",
|
|
232
|
+
"local",
|
|
233
|
+
"http://localhost:9000",
|
|
234
|
+
rootUser,
|
|
235
|
+
rootPassword
|
|
236
|
+
]);
|
|
237
|
+
await container.exec(["mc", "mb", `local/${defaultBucket}`]);
|
|
238
|
+
} catch (error) {
|
|
239
|
+
console.warn("Could not create default bucket:", error);
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
this.containers.push(container);
|
|
243
|
+
return container;
|
|
244
|
+
}
|
|
177
245
|
/**
|
|
178
246
|
* Cleanup all containers
|
|
179
247
|
*/
|
|
@@ -216,6 +284,8 @@ function setupTestEnvironment(config) {
|
|
|
216
284
|
database,
|
|
217
285
|
databaseType,
|
|
218
286
|
redis,
|
|
287
|
+
kafka,
|
|
288
|
+
s3,
|
|
219
289
|
hmacSecret = "test-secret-key",
|
|
220
290
|
customVars = {}
|
|
221
291
|
} = config;
|
|
@@ -233,6 +303,22 @@ function setupTestEnvironment(config) {
|
|
|
233
303
|
}
|
|
234
304
|
if (redis) {
|
|
235
305
|
process.env.REDIS_URL = `redis://${redis.getHost()}:${redis.getMappedPort(6379)}`;
|
|
306
|
+
process.env.REDIS_HOST = redis.getHost();
|
|
307
|
+
process.env.REDIS_PORT = redis.getMappedPort(6379).toString();
|
|
308
|
+
}
|
|
309
|
+
if (kafka) {
|
|
310
|
+
const kafkaBroker = `${kafka.getHost()}:${kafka.getMappedPort(9092)}`;
|
|
311
|
+
process.env.KAFKA_BROKERS = kafkaBroker;
|
|
312
|
+
process.env.KAFKA_CLIENT_ID = "test-client";
|
|
313
|
+
process.env.KAFKA_GROUP_ID = "test-group";
|
|
314
|
+
}
|
|
315
|
+
if (s3) {
|
|
316
|
+
process.env.S3_ENDPOINT = `http://${s3.getHost()}:${s3.getMappedPort(9e3)}`;
|
|
317
|
+
process.env.S3_ACCESS_KEY_ID = "minioadmin";
|
|
318
|
+
process.env.S3_SECRET_ACCESS_KEY = "minioadmin";
|
|
319
|
+
process.env.S3_REGION = "us-east-1";
|
|
320
|
+
process.env.S3_BUCKET = "test-bucket";
|
|
321
|
+
process.env.S3_FORCE_PATH_STYLE = "true";
|
|
236
322
|
}
|
|
237
323
|
process.env.HMAC_SECRET_KEY = hmacSecret;
|
|
238
324
|
process.env.JWKS_PUBLIC_KEY_URL = "http://localhost:3000/.well-known/jwks.json";
|
|
@@ -361,15 +447,25 @@ var BlueprintTestHarness = class {
|
|
|
361
447
|
containers;
|
|
362
448
|
result;
|
|
363
449
|
/**
|
|
364
|
-
* Setup all test infrastructure (containers, ORM, Redis)
|
|
450
|
+
* Setup all test infrastructure (containers, ORM, Redis, Kafka, S3)
|
|
365
451
|
*/
|
|
366
452
|
async setup() {
|
|
367
453
|
let container = null;
|
|
368
454
|
let orm;
|
|
369
455
|
let redisContainer;
|
|
456
|
+
let kafkaContainer;
|
|
457
|
+
let s3Container;
|
|
370
458
|
if (this.config.needsRedis) {
|
|
371
459
|
redisContainer = await this.containers.setupRedisContainer();
|
|
372
460
|
}
|
|
461
|
+
if (this.config.needsKafka) {
|
|
462
|
+
kafkaContainer = await this.containers.setupKafkaContainer();
|
|
463
|
+
}
|
|
464
|
+
if (this.config.needsS3) {
|
|
465
|
+
s3Container = await this.containers.setupS3Container({
|
|
466
|
+
defaultBucket: this.config.s3Bucket
|
|
467
|
+
});
|
|
468
|
+
}
|
|
373
469
|
if (this.config.databaseType && this.config.getConfig) {
|
|
374
470
|
const databaseType = this.config.databaseType;
|
|
375
471
|
container = await this.containers.setupDatabaseContainer(databaseType);
|
|
@@ -377,6 +473,8 @@ var BlueprintTestHarness = class {
|
|
|
377
473
|
database: container,
|
|
378
474
|
databaseType,
|
|
379
475
|
redis: redisContainer,
|
|
476
|
+
kafka: kafkaContainer,
|
|
477
|
+
s3: s3Container,
|
|
380
478
|
customVars: this.config.customEnvVars
|
|
381
479
|
});
|
|
382
480
|
const mikroOrmConfig = await this.config.getConfig();
|
|
@@ -392,6 +490,8 @@ var BlueprintTestHarness = class {
|
|
|
392
490
|
database: null,
|
|
393
491
|
databaseType: void 0,
|
|
394
492
|
redis: redisContainer,
|
|
493
|
+
kafka: kafkaContainer,
|
|
494
|
+
s3: s3Container,
|
|
395
495
|
customVars: this.config.customEnvVars
|
|
396
496
|
});
|
|
397
497
|
}
|
|
@@ -404,7 +504,14 @@ var BlueprintTestHarness = class {
|
|
|
404
504
|
});
|
|
405
505
|
await redis.ping();
|
|
406
506
|
}
|
|
407
|
-
this.result = {
|
|
507
|
+
this.result = {
|
|
508
|
+
container,
|
|
509
|
+
redisContainer,
|
|
510
|
+
kafkaContainer,
|
|
511
|
+
s3Container,
|
|
512
|
+
orm,
|
|
513
|
+
redis
|
|
514
|
+
};
|
|
408
515
|
if (this.config.onSetup) {
|
|
409
516
|
await this.config.onSetup(this.result);
|
|
410
517
|
}
|
package/lib/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/containers.ts","../src/environment.ts","../src/database.ts","../src/harness.ts","../src/tokens.ts"],"sourcesContent":["/**\n * @forklaunch/testing\n *\n * Testing utilities for forklaunch-js blueprints\n *\n * @packageDocumentation\n */\n\n// Container management\nexport {\n DatabaseConfig,\n DatabaseType,\n MongoDBConfig,\n MSSQLConfig,\n MySQLConfig,\n PostgresConfig,\n RedisConfig,\n SQLiteConfig,\n TestContainerManager\n} from './containers';\n\nexport { setupTestEnvironment, TestEnvConfig } from './environment';\n\nexport {\n clearTestDatabase,\n MikroOrmTestConfig,\n setupTestORM\n} from './database';\n\nexport {\n BlueprintTestConfig,\n BlueprintTestHarness,\n TestSetupResult\n} from './harness';\n\nexport { TEST_TOKENS } from './tokens';\n","import { GenericContainer, StartedTestContainer } from 'testcontainers';\n\nexport type DatabaseType =\n | 'postgres'\n | 'postgresql'\n | 'mysql'\n | 'mariadb'\n | 'mongodb'\n | 'mongo'\n | 'mssql'\n | 'libsql'\n | 'sqlite'\n | 'better-sqlite';\n\nexport interface PostgresConfig {\n user?: string;\n password?: string;\n database?: string;\n command?: string[];\n}\n\nexport interface MySQLConfig {\n user?: string;\n password?: string;\n database?: string;\n rootPassword?: string;\n}\n\nexport interface MongoDBConfig {\n user?: string;\n password?: string;\n database?: string;\n}\n\nexport interface MSSQLConfig {\n user?: string;\n password?: string;\n database?: string;\n saPassword?: string;\n}\n\nexport interface SQLiteConfig {\n database?: string;\n}\n\nexport interface RedisConfig {\n command?: string[];\n}\n\nexport type DatabaseConfig =\n | PostgresConfig\n | MySQLConfig\n | MongoDBConfig\n | MSSQLConfig\n | SQLiteConfig;\n\n/**\n * Manages test containers (PostgreSQL, MySQL, MongoDB, Redis, etc.) for E2E testing\n */\nexport class TestContainerManager {\n private containers: StartedTestContainer[] = [];\n\n /**\n * Setup database container based on type\n */\n async setupDatabaseContainer(\n type: DatabaseType,\n config: DatabaseConfig = {}\n ): Promise<StartedTestContainer | null> {\n const normalizedType = this.normalizeDatabaseType(type);\n\n switch (normalizedType) {\n case 'postgres':\n return this.setupPostgresContainer(config as PostgresConfig);\n case 'mysql':\n return this.setupMySQLContainer(config as MySQLConfig);\n case 'mongodb':\n return this.setupMongoDBContainer(config as MongoDBConfig);\n case 'mssql':\n return this.setupMSSQLContainer(config as MSSQLConfig);\n case 'sqlite':\n // SQLite doesn't need a container (file-based)\n return null;\n default:\n throw new Error(`Unsupported database type: ${type}`);\n }\n }\n\n /**\n * Normalize database type aliases\n */\n private normalizeDatabaseType(\n type: DatabaseType\n ): 'postgres' | 'mysql' | 'mongodb' | 'mssql' | 'sqlite' {\n switch (type) {\n case 'postgres':\n case 'postgresql':\n return 'postgres';\n case 'mysql':\n case 'mariadb':\n return 'mysql';\n case 'mongodb':\n case 'mongo':\n return 'mongodb';\n case 'mssql':\n return 'mssql';\n case 'sqlite':\n case 'better-sqlite':\n case 'libsql':\n return 'sqlite';\n default:\n throw new Error(`Unknown database type: ${type}`);\n }\n }\n\n /**\n * Setup PostgreSQL test container\n */\n async setupPostgresContainer(\n config: PostgresConfig = {}\n ): Promise<StartedTestContainer> {\n const {\n user = 'test_user',\n password = 'test_password',\n database = 'test_db',\n command = ['postgres', '-c', 'log_statement=all']\n } = config;\n\n const container = await new GenericContainer('postgres:latest')\n .withExposedPorts(5432)\n .withEnvironment({\n POSTGRES_USER: user,\n POSTGRES_PASSWORD: password,\n POSTGRES_DB: database\n })\n .withCommand(command)\n .start();\n\n this.containers.push(container);\n return container;\n }\n\n /**\n * Setup MySQL test container\n */\n async setupMySQLContainer(\n config: MySQLConfig = {}\n ): Promise<StartedTestContainer> {\n const {\n user = 'test_user',\n password = 'test_password',\n database = 'test_db',\n rootPassword = 'root_password'\n } = config;\n\n const container = await new GenericContainer('mysql:8')\n .withExposedPorts(3306)\n .withEnvironment({\n MYSQL_ROOT_PASSWORD: rootPassword,\n MYSQL_DATABASE: database,\n MYSQL_USER: user,\n MYSQL_PASSWORD: password\n })\n .start();\n\n this.containers.push(container);\n return container;\n }\n\n /**\n * Setup MongoDB test container\n */\n async setupMongoDBContainer(\n config: MongoDBConfig = {}\n ): Promise<StartedTestContainer> {\n const {\n user = 'test_user',\n password = 'test_password',\n database = 'test_db'\n } = config;\n\n const container = await new GenericContainer('mongo:latest')\n .withExposedPorts(27017)\n .withEnvironment({\n MONGO_INITDB_ROOT_USERNAME: user,\n MONGO_INITDB_ROOT_PASSWORD: password,\n MONGO_INITDB_DATABASE: database\n })\n .start();\n\n this.containers.push(container);\n return container;\n }\n\n /**\n * Setup Microsoft SQL Server test container\n */\n async setupMSSQLContainer(\n config: MSSQLConfig = {}\n ): Promise<StartedTestContainer> {\n const {\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n user = 'SA',\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n password = 'Test_Password123!',\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n database = 'test_db',\n saPassword = 'Test_Password123!'\n } = config;\n\n const container = await new GenericContainer(\n 'mcr.microsoft.com/mssql/server:2022-latest'\n )\n .withExposedPorts(1433)\n .withEnvironment({\n ACCEPT_EULA: 'Y',\n SA_PASSWORD: saPassword,\n MSSQL_PID: 'Developer'\n })\n .start();\n\n // Wait a bit for SQL Server to be ready\n await new Promise((resolve) => setTimeout(resolve, 3000));\n\n this.containers.push(container);\n return container;\n }\n\n /**\n * Setup Redis test container\n */\n async setupRedisContainer(\n config: RedisConfig = {}\n ): Promise<StartedTestContainer> {\n const { command = ['redis-server', '--appendonly', 'yes'] } = config;\n\n const container = await new GenericContainer('redis:latest')\n .withExposedPorts(6379)\n .withCommand(command)\n .start();\n\n this.containers.push(container);\n return container;\n }\n\n /**\n * Cleanup all containers\n */\n async cleanup(): Promise<void> {\n await Promise.all(\n this.containers.map((container) =>\n container.stop({ remove: true, removeVolumes: true }).catch(() => {\n // Ignore cleanup errors\n })\n )\n );\n this.containers = [];\n }\n}\n","import { StartedTestContainer } from 'testcontainers';\nimport { DatabaseType } from './containers';\n\nexport interface TestEnvConfig {\n database: StartedTestContainer | null;\n databaseType?: DatabaseType;\n redis?: StartedTestContainer;\n hmacSecret?: string;\n customVars?: Record<string, string>;\n}\n\n/**\n * Get the default port for a database type\n */\nfunction getDatabasePort(type: DatabaseType): number {\n switch (type) {\n case 'postgres':\n case 'postgresql':\n return 5432;\n case 'mysql':\n case 'mariadb':\n return 3306;\n case 'mongodb':\n case 'mongo':\n return 27017;\n case 'mssql':\n return 1433;\n case 'sqlite':\n case 'better-sqlite':\n case 'libsql':\n return 0; // SQLite is file-based, no port\n default:\n return 5432;\n }\n}\n\n/**\n * Setup test environment variables for a blueprint test\n */\nexport function setupTestEnvironment(config: TestEnvConfig): void {\n const {\n database,\n databaseType,\n redis,\n hmacSecret = 'test-secret-key',\n customVars = {}\n } = config;\n\n // Only set database environment variables if database is configured\n if (databaseType) {\n const dbPort = getDatabasePort(databaseType);\n\n // Database environment variables\n process.env.DB_NAME = 'test_db';\n\n // SQLite databases are file-based, no container needed\n if (\n databaseType === 'sqlite' ||\n databaseType === 'better-sqlite' ||\n databaseType === 'libsql'\n ) {\n process.env.DB_PATH = ':memory:'; // In-memory SQLite for tests\n } else if (database) {\n process.env.DB_HOST = database.getHost();\n process.env.DB_USER = databaseType === 'mssql' ? 'SA' : 'test_user';\n process.env.DB_PASSWORD =\n databaseType === 'mssql' ? 'Test_Password123!' : 'test_password';\n process.env.DB_PORT = database.getMappedPort(dbPort).toString();\n }\n }\n\n // Redis environment variables (if provided)\n if (redis) {\n process.env.REDIS_URL = `redis://${redis.getHost()}:${redis.getMappedPort(6379)}`;\n }\n\n // Standard test environment variables\n process.env.HMAC_SECRET_KEY = hmacSecret;\n process.env.JWKS_PUBLIC_KEY_URL =\n 'http://localhost:3000/.well-known/jwks.json';\n process.env.OTEL_SERVICE_NAME = 'test-service';\n process.env.OTEL_EXPORTER_OTLP_ENDPOINT = 'http://localhost:4318';\n process.env.HOST = 'localhost';\n process.env.PORT = '3000';\n process.env.NODE_ENV = 'test';\n process.env.VERSION = 'v1';\n process.env.DOCS_PATH = '/docs';\n process.env.OTEL_LEVEL = 'info';\n process.env.DOTENV_FILE_PATH = '.env.test';\n\n // Custom environment variables\n Object.entries(customVars).forEach(([key, value]) => {\n process.env[key] = value;\n });\n}\n","import { MikroORM, Options } from '@mikro-orm/core';\nimport Redis from 'ioredis';\nimport { StartedTestContainer } from 'testcontainers';\nimport { DatabaseType } from './containers';\n\nexport interface MikroOrmTestConfig {\n /**\n * MikroORM config object (imported from mikro-orm.config)\n */\n mikroOrmConfig: Options;\n\n /**\n * Database type (postgres, mysql, mongodb, etc.)\n */\n databaseType: DatabaseType;\n\n /**\n * Whether to use migrations (true) or schema generation (false)\n * - true: IAM blueprints (uses getMigrator().up())\n * - false: Billing blueprints (uses getSchemaGenerator().createSchema())\n */\n useMigrations?: boolean;\n\n /**\n * Path to migrations directory (required if useMigrations is true)\n */\n migrationsPath?: string;\n\n /**\n * Database container instance (null for file-based databases like SQLite)\n */\n container: StartedTestContainer | null;\n}\n\n/**\n * Get the default port for a database type\n */\nfunction getDatabasePort(type: DatabaseType): number {\n switch (type) {\n case 'postgres':\n case 'postgresql':\n return 5432;\n case 'mysql':\n case 'mariadb':\n return 3306;\n case 'mongodb':\n case 'mongo':\n return 27017;\n case 'mssql':\n return 1433;\n case 'sqlite':\n case 'better-sqlite':\n case 'libsql':\n return 0; // SQLite is file-based, no port\n default:\n return 5432;\n }\n}\n\n/**\n * Setup MikroORM for testing with proper schema/migrations\n */\nexport async function setupTestORM(\n config: MikroOrmTestConfig\n): Promise<MikroORM> {\n const {\n mikroOrmConfig,\n databaseType,\n useMigrations = false,\n container\n } = config;\n\n const dbPort = getDatabasePort(databaseType);\n\n // SQLite databases are file-based\n let ormConfig: Options = {};\n if (\n databaseType === 'sqlite' ||\n databaseType === 'better-sqlite' ||\n databaseType === 'libsql'\n ) {\n ormConfig = {\n ...mikroOrmConfig,\n dbName: ':memory:', // In-memory SQLite for tests\n debug: false,\n ...(useMigrations\n ? {\n migrations: {\n path: config.migrationsPath,\n glob: '!(*.d).{js,ts}',\n dropTables: true\n }\n }\n : {\n schemaGenerator: {\n createForeignKeyConstraints: false\n }\n })\n };\n } else if (container) {\n // Container-based databases\n ormConfig = {\n ...mikroOrmConfig,\n dbName: 'test_db',\n host: container.getHost(),\n user: databaseType === 'mssql' ? 'SA' : 'test_user',\n password:\n databaseType === 'mssql' ? 'Test_Password123!' : 'test_password',\n port: container.getMappedPort(dbPort),\n debug: false,\n ...(useMigrations\n ? {\n migrations: {\n path: config.migrationsPath,\n glob: '!(*.d).{js,ts}',\n dropTables: true\n }\n }\n : {\n schemaGenerator: {\n createForeignKeyConstraints: false\n }\n })\n };\n }\n\n const orm = await MikroORM.init(ormConfig);\n\n // Initialize database schema\n if (useMigrations) {\n await orm.getMigrator().up();\n } else {\n await orm.getSchemaGenerator().createSchema();\n }\n\n return orm;\n}\n\n/**\n * Clear all data from the test database and/or cache\n */\nexport async function clearTestDatabase(\n orm?: MikroORM,\n redis?: Redis\n): Promise<void> {\n // Clear Redis if provided\n if (redis) {\n await redis.flushall();\n }\n\n // Clear all database entities (if ORM is provided)\n if (orm) {\n const em = orm.em.fork();\n const entities = Object.values(orm.getMetadata().getAll());\n\n // Delete in reverse order to avoid foreign key constraints\n for (const entity of entities.reverse()) {\n try {\n await em.nativeDelete(entity.class, {});\n } catch (error) {\n // Ignore \"table does not exist\" errors\n if (!(error as Error).message?.includes('does not exist')) {\n throw error;\n }\n }\n }\n\n await em.flush();\n }\n}\n","import { MikroORM, Options } from '@mikro-orm/core';\nimport Redis from 'ioredis';\nimport { StartedTestContainer } from 'testcontainers';\nimport { DatabaseType, TestContainerManager } from './containers';\nimport { clearTestDatabase, setupTestORM } from './database';\nimport { setupTestEnvironment } from './environment';\n\nexport interface BlueprintTestConfig {\n /**\n * Function that imports and returns the MikroORM config\n * This is called AFTER environment variables are set\n * Optional - if not provided, no database will be set up\n */\n getConfig?: () => Promise<Options>;\n\n /**\n * Database type (postgres, mysql, mongodb, etc.)\n * Optional - if not provided, no database will be set up\n */\n databaseType?: DatabaseType;\n\n /**\n * Whether to use migrations (true) or schema generation (false)\n */\n useMigrations?: boolean;\n\n /**\n * Path to migrations directory (required if useMigrations is true)\n */\n migrationsPath?: string;\n\n /**\n * Whether the blueprint needs Redis\n */\n needsRedis?: boolean;\n\n /**\n * Custom environment variables to set\n */\n customEnvVars?: Record<string, string>;\n\n /**\n * Custom setup hook called after containers and ORM are initialized\n */\n onSetup?: (setup: TestSetupResult) => Promise<void>;\n}\n\nexport interface TestSetupResult {\n container: StartedTestContainer | null;\n redisContainer?: StartedTestContainer;\n orm?: MikroORM;\n redis?: Redis;\n}\n\n/**\n * Complete test harness for blueprint E2E testing\n *\n * Handles container setup, environment configuration, and database initialization\n *\n * @example Database with ORM\n * ```typescript\n * const harness = new BlueprintTestHarness({\n * getConfig: async () => {\n * const { default: config } = await import('../mikro-orm.config');\n * return config;\n * },\n * databaseType: 'postgres',\n * useMigrations: false,\n * needsRedis: true,\n * customEnvVars: {\n * STRIPE_API_KEY: 'sk_test_...'\n * }\n * });\n *\n * const setup = await harness.setup();\n * // ... run tests with setup.orm and setup.redis\n * await harness.cleanup();\n * ```\n *\n * @example Cache-only (no database)\n * ```typescript\n * const harness = new BlueprintTestHarness({\n * needsRedis: true,\n * customEnvVars: {\n * API_KEY: 'test_key'\n * }\n * });\n *\n * const setup = await harness.setup();\n * // ... run tests with setup.redis only (setup.orm is undefined)\n * await harness.cleanup();\n * ```\n */\nexport class BlueprintTestHarness {\n private containers: TestContainerManager;\n private result?: TestSetupResult;\n\n constructor(private config: BlueprintTestConfig) {\n this.containers = new TestContainerManager();\n }\n\n /**\n * Setup all test infrastructure (containers, ORM, Redis)\n */\n async setup(): Promise<TestSetupResult> {\n // Setup database container only if database is needed\n let container: StartedTestContainer | null = null;\n let orm: MikroORM | undefined;\n let redisContainer: StartedTestContainer | undefined;\n\n // Setup Redis container if needed (for both database and cache-only modes)\n if (this.config.needsRedis) {\n redisContainer = await this.containers.setupRedisContainer();\n }\n\n if (this.config.databaseType && this.config.getConfig) {\n const databaseType = this.config.databaseType;\n\n // Setup database container\n container = await this.containers.setupDatabaseContainer(databaseType);\n\n // Setup environment variables\n setupTestEnvironment({\n database: container,\n databaseType,\n redis: redisContainer,\n customVars: this.config.customEnvVars\n });\n\n // Get the config AFTER environment is set\n const mikroOrmConfig = await this.config.getConfig();\n\n // Setup ORM\n orm = await setupTestORM({\n mikroOrmConfig,\n databaseType,\n useMigrations: this.config.useMigrations,\n migrationsPath: this.config.migrationsPath,\n container\n });\n } else {\n // Cache-only mode: no database\n setupTestEnvironment({\n database: null,\n databaseType: undefined,\n redis: redisContainer,\n customVars: this.config.customEnvVars\n });\n }\n\n // Setup Redis client if needed\n let redis: Redis | undefined;\n if (redisContainer) {\n redis = new Redis({\n host: redisContainer.getHost(),\n port: redisContainer.getMappedPort(6379),\n maxRetriesPerRequest: 3\n });\n await redis.ping();\n }\n\n this.result = { container, redisContainer, orm, redis };\n\n // Call custom setup hook\n if (this.config.onSetup) {\n await this.config.onSetup(this.result);\n }\n\n return this.result;\n }\n\n /**\n * Cleanup all test infrastructure\n */\n async cleanup(): Promise<void> {\n if (this.result?.redis) {\n await this.result.redis.quit();\n }\n if (this.result?.orm) {\n await this.result.orm.close();\n }\n await this.containers.cleanup();\n this.result = undefined;\n }\n\n /**\n * Clear all data from the database and/or cache\n */\n async clearDatabase(): Promise<void> {\n if (this.result) {\n await clearTestDatabase(this.result.orm, this.result.redis);\n }\n }\n}\n","/**\n * Standard mock authentication tokens for testing\n */\nexport const TEST_TOKENS = {\n /**\n * Mock Bearer token for testing\n */\n AUTH: 'Bearer test-token',\n\n /**\n * Mock valid HMAC token for testing\n */\n HMAC: 'HMAC keyId=test-key ts=1234567890 nonce=test-nonce signature=test-signature',\n\n /**\n * Mock invalid HMAC token for testing authentication failures\n */\n HMAC_INVALID:\n 'HMAC keyId=invalid-key ts=1234567890 nonce=invalid-nonce signature=invalid-signature'\n} as const;\n"],"mappings":"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","names":["getDatabasePort","Redis"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/containers.ts","../src/environment.ts","../src/database.ts","../src/harness.ts","../src/tokens.ts"],"sourcesContent":["/**\n * @forklaunch/testing\n *\n * Testing utilities for forklaunch-js blueprints\n *\n * @packageDocumentation\n */\n\n// Container management\nexport {\n DatabaseConfig,\n DatabaseType,\n KafkaConfig,\n MongoDBConfig,\n MSSQLConfig,\n MySQLConfig,\n PostgresConfig,\n RedisConfig,\n S3Config,\n SQLiteConfig,\n TestContainerManager\n} from './containers';\n\nexport { setupTestEnvironment, TestEnvConfig } from './environment';\n\nexport {\n clearTestDatabase,\n MikroOrmTestConfig,\n setupTestORM\n} from './database';\n\nexport {\n BlueprintTestConfig,\n BlueprintTestHarness,\n TestSetupResult\n} from './harness';\n\nexport { TEST_TOKENS } from './tokens';\n","import { GenericContainer, StartedTestContainer } from 'testcontainers';\n\nexport type DatabaseType =\n | 'postgres'\n | 'postgresql'\n | 'mysql'\n | 'mariadb'\n | 'mongodb'\n | 'mongo'\n | 'mssql'\n | 'libsql'\n | 'sqlite'\n | 'better-sqlite';\n\nexport interface PostgresConfig {\n user?: string;\n password?: string;\n database?: string;\n command?: string[];\n}\n\nexport interface MySQLConfig {\n user?: string;\n password?: string;\n database?: string;\n rootPassword?: string;\n}\n\nexport interface MongoDBConfig {\n user?: string;\n password?: string;\n database?: string;\n}\n\nexport interface MSSQLConfig {\n user?: string;\n password?: string;\n database?: string;\n saPassword?: string;\n}\n\nexport interface SQLiteConfig {\n database?: string;\n}\n\nexport interface RedisConfig {\n command?: string[];\n}\n\nexport interface KafkaConfig {\n /** Kafka cluster ID */\n clusterId?: string;\n /** Number of partitions */\n numPartitions?: number;\n /** Replication factor */\n replicationFactor?: number;\n /** Additional environment variables */\n env?: Record<string, string>;\n}\n\nexport interface S3Config {\n /** MinIO root user (access key) */\n rootUser?: string;\n /** MinIO root password (secret key) */\n rootPassword?: string;\n /** Default bucket to create */\n defaultBucket?: string;\n /** Region */\n region?: string;\n}\n\nexport type DatabaseConfig =\n | PostgresConfig\n | MySQLConfig\n | MongoDBConfig\n | MSSQLConfig\n | SQLiteConfig;\n\n/**\n * Manages test containers (PostgreSQL, MySQL, MongoDB, Redis, etc.) for E2E testing\n */\nexport class TestContainerManager {\n private containers: StartedTestContainer[] = [];\n\n /**\n * Setup database container based on type\n */\n async setupDatabaseContainer(\n type: DatabaseType,\n config: DatabaseConfig = {}\n ): Promise<StartedTestContainer | null> {\n const normalizedType = this.normalizeDatabaseType(type);\n\n switch (normalizedType) {\n case 'postgres':\n return this.setupPostgresContainer(config as PostgresConfig);\n case 'mysql':\n return this.setupMySQLContainer(config as MySQLConfig);\n case 'mongodb':\n return this.setupMongoDBContainer(config as MongoDBConfig);\n case 'mssql':\n return this.setupMSSQLContainer(config as MSSQLConfig);\n case 'sqlite':\n // SQLite doesn't need a container (file-based)\n return null;\n default:\n throw new Error(`Unsupported database type: ${type}`);\n }\n }\n\n /**\n * Normalize database type aliases\n */\n private normalizeDatabaseType(\n type: DatabaseType\n ): 'postgres' | 'mysql' | 'mongodb' | 'mssql' | 'sqlite' {\n switch (type) {\n case 'postgres':\n case 'postgresql':\n return 'postgres';\n case 'mysql':\n case 'mariadb':\n return 'mysql';\n case 'mongodb':\n case 'mongo':\n return 'mongodb';\n case 'mssql':\n return 'mssql';\n case 'sqlite':\n case 'better-sqlite':\n case 'libsql':\n return 'sqlite';\n default:\n throw new Error(`Unknown database type: ${type}`);\n }\n }\n\n /**\n * Setup PostgreSQL test container\n */\n async setupPostgresContainer(\n config: PostgresConfig = {}\n ): Promise<StartedTestContainer> {\n const {\n user = 'test_user',\n password = 'test_password',\n database = 'test_db',\n command = ['postgres', '-c', 'log_statement=all']\n } = config;\n\n const container = await new GenericContainer('postgres:latest')\n .withExposedPorts(5432)\n .withEnvironment({\n POSTGRES_USER: user,\n POSTGRES_PASSWORD: password,\n POSTGRES_DB: database\n })\n .withCommand(command)\n .start();\n\n this.containers.push(container);\n return container;\n }\n\n /**\n * Setup MySQL test container\n */\n async setupMySQLContainer(\n config: MySQLConfig = {}\n ): Promise<StartedTestContainer> {\n const {\n user = 'test_user',\n password = 'test_password',\n database = 'test_db',\n rootPassword = 'root_password'\n } = config;\n\n const container = await new GenericContainer('mysql:8')\n .withExposedPorts(3306)\n .withEnvironment({\n MYSQL_ROOT_PASSWORD: rootPassword,\n MYSQL_DATABASE: database,\n MYSQL_USER: user,\n MYSQL_PASSWORD: password\n })\n .start();\n\n this.containers.push(container);\n return container;\n }\n\n /**\n * Setup MongoDB test container\n */\n async setupMongoDBContainer(\n config: MongoDBConfig = {}\n ): Promise<StartedTestContainer> {\n const {\n user = 'test_user',\n password = 'test_password',\n database = 'test_db'\n } = config;\n\n const container = await new GenericContainer('mongo:latest')\n .withExposedPorts(27017)\n .withEnvironment({\n MONGO_INITDB_ROOT_USERNAME: user,\n MONGO_INITDB_ROOT_PASSWORD: password,\n MONGO_INITDB_DATABASE: database\n })\n .start();\n\n this.containers.push(container);\n return container;\n }\n\n /**\n * Setup Microsoft SQL Server test container\n */\n async setupMSSQLContainer(\n config: MSSQLConfig = {}\n ): Promise<StartedTestContainer> {\n const {\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n user = 'SA',\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n password = 'Test_Password123!',\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n database = 'test_db',\n saPassword = 'Test_Password123!'\n } = config;\n\n const container = await new GenericContainer(\n 'mcr.microsoft.com/mssql/server:2022-latest'\n )\n .withExposedPorts(1433)\n .withEnvironment({\n ACCEPT_EULA: 'Y',\n SA_PASSWORD: saPassword,\n MSSQL_PID: 'Developer'\n })\n .start();\n\n // Wait a bit for SQL Server to be ready\n await new Promise((resolve) => setTimeout(resolve, 3000));\n\n this.containers.push(container);\n return container;\n }\n\n /**\n * Setup Redis test container\n */\n async setupRedisContainer(\n config: RedisConfig = {}\n ): Promise<StartedTestContainer> {\n const { command = ['redis-server', '--appendonly', 'yes'] } = config;\n\n const container = await new GenericContainer('redis:latest')\n .withExposedPorts(6379)\n .withCommand(command)\n .start();\n\n this.containers.push(container);\n return container;\n }\n\n /**\n * Setup Kafka test container\n */\n async setupKafkaContainer(\n config: KafkaConfig = {}\n ): Promise<StartedTestContainer> {\n const {\n clusterId = 'test-cluster',\n numPartitions = 1,\n replicationFactor = 1,\n env = {}\n } = config;\n\n const container = await new GenericContainer('confluentinc/cp-kafka:latest')\n .withExposedPorts(9092, 9093)\n .withEnvironment({\n KAFKA_BROKER_ID: '1',\n KAFKA_LISTENER_SECURITY_PROTOCOL_MAP:\n 'PLAINTEXT:PLAINTEXT,PLAINTEXT_HOST:PLAINTEXT',\n KAFKA_ADVERTISED_LISTENERS:\n 'PLAINTEXT://kafka:29092,PLAINTEXT_HOST://localhost:9092',\n KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: replicationFactor.toString(),\n KAFKA_TRANSACTION_STATE_LOG_MIN_ISR: '1',\n KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR: '1',\n KAFKA_GROUP_INITIAL_REBALANCE_DELAY_MS: '0',\n KAFKA_NUM_PARTITIONS: numPartitions.toString(),\n KAFKA_CLUSTER_ID: clusterId,\n KAFKA_PROCESS_ROLES: 'broker,controller',\n KAFKA_NODE_ID: '1',\n KAFKA_CONTROLLER_QUORUM_VOTERS: '1@localhost:9093',\n KAFKA_LISTENERS:\n 'PLAINTEXT://0.0.0.0:29092,CONTROLLER://0.0.0.0:9093,PLAINTEXT_HOST://0.0.0.0:9092',\n KAFKA_INTER_BROKER_LISTENER_NAME: 'PLAINTEXT',\n KAFKA_CONTROLLER_LISTENER_NAMES: 'CONTROLLER',\n KAFKA_LOG_DIRS: '/tmp/kraft-combined-logs',\n ...env\n })\n .start();\n\n // Wait for Kafka to be ready\n await new Promise((resolve) => setTimeout(resolve, 5000));\n\n this.containers.push(container);\n return container;\n }\n\n /**\n * Setup MinIO (S3-compatible) test container\n */\n async setupS3Container(config: S3Config = {}): Promise<StartedTestContainer> {\n const {\n rootUser = 'minioadmin',\n rootPassword = 'minioadmin',\n defaultBucket = 'test-bucket',\n region = 'us-east-1'\n } = config;\n\n const container = await new GenericContainer('minio/minio:latest')\n .withExposedPorts(9000, 9001)\n .withEnvironment({\n MINIO_ROOT_USER: rootUser,\n MINIO_ROOT_PASSWORD: rootPassword,\n MINIO_REGION: region\n })\n .withCommand(['server', '/data', '--console-address', ':9001'])\n .start();\n\n // Wait for MinIO to be ready\n await new Promise((resolve) => setTimeout(resolve, 2000));\n\n // Create default bucket if specified\n if (defaultBucket) {\n try {\n // Using MinIO client command via exec\n await container.exec([\n 'mc',\n 'alias',\n 'set',\n 'local',\n 'http://localhost:9000',\n rootUser,\n rootPassword\n ]);\n await container.exec(['mc', 'mb', `local/${defaultBucket}`]);\n } catch (error) {\n // Bucket creation might fail, but container is still usable\n console.warn('Could not create default bucket:', error);\n }\n }\n\n this.containers.push(container);\n return container;\n }\n\n /**\n * Cleanup all containers\n */\n async cleanup(): Promise<void> {\n await Promise.all(\n this.containers.map((container) =>\n container.stop({ remove: true, removeVolumes: true }).catch(() => {\n // Ignore cleanup errors\n })\n )\n );\n this.containers = [];\n }\n}\n","import { StartedTestContainer } from 'testcontainers';\nimport { DatabaseType } from './containers';\n\nexport interface TestEnvConfig {\n database: StartedTestContainer | null;\n databaseType?: DatabaseType;\n redis?: StartedTestContainer;\n kafka?: StartedTestContainer;\n s3?: StartedTestContainer;\n hmacSecret?: string;\n customVars?: Record<string, string>;\n}\n\n/**\n * Get the default port for a database type\n */\nfunction getDatabasePort(type: DatabaseType): number {\n switch (type) {\n case 'postgres':\n case 'postgresql':\n return 5432;\n case 'mysql':\n case 'mariadb':\n return 3306;\n case 'mongodb':\n case 'mongo':\n return 27017;\n case 'mssql':\n return 1433;\n case 'sqlite':\n case 'better-sqlite':\n case 'libsql':\n return 0; // SQLite is file-based, no port\n default:\n return 5432;\n }\n}\n\n/**\n * Setup test environment variables for a blueprint test\n */\nexport function setupTestEnvironment(config: TestEnvConfig): void {\n const {\n database,\n databaseType,\n redis,\n kafka,\n s3,\n hmacSecret = 'test-secret-key',\n customVars = {}\n } = config;\n\n // Only set database environment variables if database is configured\n if (databaseType) {\n const dbPort = getDatabasePort(databaseType);\n\n // Database environment variables\n process.env.DB_NAME = 'test_db';\n\n // SQLite databases are file-based, no container needed\n if (\n databaseType === 'sqlite' ||\n databaseType === 'better-sqlite' ||\n databaseType === 'libsql'\n ) {\n process.env.DB_PATH = ':memory:'; // In-memory SQLite for tests\n } else if (database) {\n process.env.DB_HOST = database.getHost();\n process.env.DB_USER = databaseType === 'mssql' ? 'SA' : 'test_user';\n process.env.DB_PASSWORD =\n databaseType === 'mssql' ? 'Test_Password123!' : 'test_password';\n process.env.DB_PORT = database.getMappedPort(dbPort).toString();\n }\n }\n\n // Redis environment variables (if provided)\n if (redis) {\n process.env.REDIS_URL = `redis://${redis.getHost()}:${redis.getMappedPort(6379)}`;\n process.env.REDIS_HOST = redis.getHost();\n process.env.REDIS_PORT = redis.getMappedPort(6379).toString();\n }\n\n // Kafka environment variables (if provided)\n if (kafka) {\n const kafkaBroker = `${kafka.getHost()}:${kafka.getMappedPort(9092)}`;\n process.env.KAFKA_BROKERS = kafkaBroker;\n process.env.KAFKA_CLIENT_ID = 'test-client';\n process.env.KAFKA_GROUP_ID = 'test-group';\n }\n\n // S3/MinIO environment variables (if provided)\n if (s3) {\n process.env.S3_ENDPOINT = `http://${s3.getHost()}:${s3.getMappedPort(9000)}`;\n process.env.S3_ACCESS_KEY_ID = 'minioadmin';\n process.env.S3_SECRET_ACCESS_KEY = 'minioadmin';\n process.env.S3_REGION = 'us-east-1';\n process.env.S3_BUCKET = 'test-bucket';\n process.env.S3_FORCE_PATH_STYLE = 'true'; // Required for MinIO\n }\n\n // Standard test environment variables\n process.env.HMAC_SECRET_KEY = hmacSecret;\n process.env.JWKS_PUBLIC_KEY_URL =\n 'http://localhost:3000/.well-known/jwks.json';\n process.env.OTEL_SERVICE_NAME = 'test-service';\n process.env.OTEL_EXPORTER_OTLP_ENDPOINT = 'http://localhost:4318';\n process.env.HOST = 'localhost';\n process.env.PORT = '3000';\n process.env.NODE_ENV = 'test';\n process.env.VERSION = 'v1';\n process.env.DOCS_PATH = '/docs';\n process.env.OTEL_LEVEL = 'info';\n process.env.DOTENV_FILE_PATH = '.env.test';\n\n // Custom environment variables\n Object.entries(customVars).forEach(([key, value]) => {\n process.env[key] = value;\n });\n}\n","import { MikroORM, Options } from '@mikro-orm/core';\nimport Redis from 'ioredis';\nimport { StartedTestContainer } from 'testcontainers';\nimport { DatabaseType } from './containers';\n\nexport interface MikroOrmTestConfig {\n /**\n * MikroORM config object (imported from mikro-orm.config)\n */\n mikroOrmConfig: Options;\n\n /**\n * Database type (postgres, mysql, mongodb, etc.)\n */\n databaseType: DatabaseType;\n\n /**\n * Whether to use migrations (true) or schema generation (false)\n * - true: IAM blueprints (uses getMigrator().up())\n * - false: Billing blueprints (uses getSchemaGenerator().createSchema())\n */\n useMigrations?: boolean;\n\n /**\n * Path to migrations directory (required if useMigrations is true)\n */\n migrationsPath?: string;\n\n /**\n * Database container instance (null for file-based databases like SQLite)\n */\n container: StartedTestContainer | null;\n}\n\n/**\n * Get the default port for a database type\n */\nfunction getDatabasePort(type: DatabaseType): number {\n switch (type) {\n case 'postgres':\n case 'postgresql':\n return 5432;\n case 'mysql':\n case 'mariadb':\n return 3306;\n case 'mongodb':\n case 'mongo':\n return 27017;\n case 'mssql':\n return 1433;\n case 'sqlite':\n case 'better-sqlite':\n case 'libsql':\n return 0; // SQLite is file-based, no port\n default:\n return 5432;\n }\n}\n\n/**\n * Setup MikroORM for testing with proper schema/migrations\n */\nexport async function setupTestORM(\n config: MikroOrmTestConfig\n): Promise<MikroORM> {\n const {\n mikroOrmConfig,\n databaseType,\n useMigrations = false,\n container\n } = config;\n\n const dbPort = getDatabasePort(databaseType);\n\n // SQLite databases are file-based\n let ormConfig: Options = {};\n if (\n databaseType === 'sqlite' ||\n databaseType === 'better-sqlite' ||\n databaseType === 'libsql'\n ) {\n ormConfig = {\n ...mikroOrmConfig,\n dbName: ':memory:', // In-memory SQLite for tests\n debug: false,\n ...(useMigrations\n ? {\n migrations: {\n path: config.migrationsPath,\n glob: '!(*.d).{js,ts}',\n dropTables: true\n }\n }\n : {\n schemaGenerator: {\n createForeignKeyConstraints: false\n }\n })\n };\n } else if (container) {\n // Container-based databases\n ormConfig = {\n ...mikroOrmConfig,\n dbName: 'test_db',\n host: container.getHost(),\n user: databaseType === 'mssql' ? 'SA' : 'test_user',\n password:\n databaseType === 'mssql' ? 'Test_Password123!' : 'test_password',\n port: container.getMappedPort(dbPort),\n debug: false,\n ...(useMigrations\n ? {\n migrations: {\n path: config.migrationsPath,\n glob: '!(*.d).{js,ts}',\n dropTables: true\n }\n }\n : {\n schemaGenerator: {\n createForeignKeyConstraints: false\n }\n })\n };\n }\n\n const orm = await MikroORM.init(ormConfig);\n\n // Initialize database schema\n if (useMigrations) {\n await orm.getMigrator().up();\n } else {\n await orm.getSchemaGenerator().createSchema();\n }\n\n return orm;\n}\n\n/**\n * Clear all data from the test database and/or cache\n */\nexport async function clearTestDatabase(\n orm?: MikroORM,\n redis?: Redis\n): Promise<void> {\n // Clear Redis if provided\n if (redis) {\n await redis.flushall();\n }\n\n // Clear all database entities (if ORM is provided)\n if (orm) {\n const em = orm.em.fork();\n const entities = Object.values(orm.getMetadata().getAll());\n\n // Delete in reverse order to avoid foreign key constraints\n for (const entity of entities.reverse()) {\n try {\n await em.nativeDelete(entity.class, {});\n } catch (error) {\n // Ignore \"table does not exist\" errors\n if (!(error as Error).message?.includes('does not exist')) {\n throw error;\n }\n }\n }\n\n await em.flush();\n }\n}\n","import { MikroORM, Options } from '@mikro-orm/core';\nimport Redis from 'ioredis';\nimport { StartedTestContainer } from 'testcontainers';\nimport { DatabaseType, TestContainerManager } from './containers';\nimport { clearTestDatabase, setupTestORM } from './database';\nimport { setupTestEnvironment } from './environment';\n\nexport interface BlueprintTestConfig {\n /**\n * Function that imports and returns the MikroORM config\n * This is called AFTER environment variables are set\n * Optional - if not provided, no database will be set up\n */\n getConfig?: () => Promise<Options>;\n\n /**\n * Database type (postgres, mysql, mongodb, etc.)\n * Optional - if not provided, no database will be set up\n */\n databaseType?: DatabaseType;\n\n /**\n * Whether to use migrations (true) or schema generation (false)\n */\n useMigrations?: boolean;\n\n /**\n * Path to migrations directory (required if useMigrations is true)\n */\n migrationsPath?: string;\n\n /**\n * Whether the blueprint needs Redis\n */\n needsRedis?: boolean;\n\n /**\n * Whether the blueprint needs Kafka\n */\n needsKafka?: boolean;\n\n /**\n * Whether the blueprint needs S3 (MinIO)\n */\n needsS3?: boolean;\n\n /**\n * S3 bucket name to create (default: 'test-bucket')\n */\n s3Bucket?: string;\n\n /**\n * Custom environment variables to set\n */\n customEnvVars?: Record<string, string>;\n\n /**\n * Custom setup hook called after containers and ORM are initialized\n */\n onSetup?: (setup: TestSetupResult) => Promise<void>;\n}\n\nexport interface TestSetupResult {\n container: StartedTestContainer | null;\n redisContainer?: StartedTestContainer;\n kafkaContainer?: StartedTestContainer;\n s3Container?: StartedTestContainer;\n orm?: MikroORM;\n redis?: Redis;\n}\n\n/**\n * Complete test harness for blueprint E2E testing\n *\n * Handles container setup, environment configuration, and database initialization\n *\n * @example Database with ORM\n * ```typescript\n * const harness = new BlueprintTestHarness({\n * getConfig: async () => {\n * const { default: config } = await import('../mikro-orm.config');\n * return config;\n * },\n * databaseType: 'postgres',\n * useMigrations: false,\n * needsRedis: true,\n * customEnvVars: {\n * STRIPE_API_KEY: 'sk_test_...'\n * }\n * });\n *\n * const setup = await harness.setup();\n * // ... run tests with setup.orm and setup.redis\n * await harness.cleanup();\n * ```\n *\n * @example Cache-only (no database)\n * ```typescript\n * const harness = new BlueprintTestHarness({\n * needsRedis: true,\n * customEnvVars: {\n * API_KEY: 'test_key'\n * }\n * });\n *\n * const setup = await harness.setup();\n * // ... run tests with setup.redis only (setup.orm is undefined)\n * await harness.cleanup();\n * ```\n *\n * @example With Kafka and S3\n * ```typescript\n * const harness = new BlueprintTestHarness({\n * getConfig: async () => {\n * const { default: config } = await import('../mikro-orm.config');\n * return config;\n * },\n * databaseType: 'postgres',\n * needsKafka: true,\n * needsS3: true,\n * s3Bucket: 'my-test-bucket',\n * customEnvVars: {\n * API_KEY: 'test_key'\n * }\n * });\n *\n * const setup = await harness.setup();\n * // Access containers via setup.kafkaContainer and setup.s3Container\n * // Kafka broker: process.env.KAFKA_BROKERS\n * // S3 endpoint: process.env.S3_ENDPOINT\n * await harness.cleanup();\n * ```\n */\nexport class BlueprintTestHarness {\n private containers: TestContainerManager;\n private result?: TestSetupResult;\n\n constructor(private config: BlueprintTestConfig) {\n this.containers = new TestContainerManager();\n }\n\n /**\n * Setup all test infrastructure (containers, ORM, Redis, Kafka, S3)\n */\n async setup(): Promise<TestSetupResult> {\n // Setup database container only if database is needed\n let container: StartedTestContainer | null = null;\n let orm: MikroORM | undefined;\n let redisContainer: StartedTestContainer | undefined;\n let kafkaContainer: StartedTestContainer | undefined;\n let s3Container: StartedTestContainer | undefined;\n\n // Setup Redis container if needed (for both database and cache-only modes)\n if (this.config.needsRedis) {\n redisContainer = await this.containers.setupRedisContainer();\n }\n\n // Setup Kafka container if needed\n if (this.config.needsKafka) {\n kafkaContainer = await this.containers.setupKafkaContainer();\n }\n\n // Setup S3 container if needed\n if (this.config.needsS3) {\n s3Container = await this.containers.setupS3Container({\n defaultBucket: this.config.s3Bucket\n });\n }\n\n if (this.config.databaseType && this.config.getConfig) {\n const databaseType = this.config.databaseType;\n\n // Setup database container\n container = await this.containers.setupDatabaseContainer(databaseType);\n\n // Setup environment variables\n setupTestEnvironment({\n database: container,\n databaseType,\n redis: redisContainer,\n kafka: kafkaContainer,\n s3: s3Container,\n customVars: this.config.customEnvVars\n });\n\n // Get the config AFTER environment is set\n const mikroOrmConfig = await this.config.getConfig();\n\n // Setup ORM\n orm = await setupTestORM({\n mikroOrmConfig,\n databaseType,\n useMigrations: this.config.useMigrations,\n migrationsPath: this.config.migrationsPath,\n container\n });\n } else {\n // Cache-only mode: no database\n setupTestEnvironment({\n database: null,\n databaseType: undefined,\n redis: redisContainer,\n kafka: kafkaContainer,\n s3: s3Container,\n customVars: this.config.customEnvVars\n });\n }\n\n // Setup Redis client if needed\n let redis: Redis | undefined;\n if (redisContainer) {\n redis = new Redis({\n host: redisContainer.getHost(),\n port: redisContainer.getMappedPort(6379),\n maxRetriesPerRequest: 3\n });\n await redis.ping();\n }\n\n this.result = {\n container,\n redisContainer,\n kafkaContainer,\n s3Container,\n orm,\n redis\n };\n\n // Call custom setup hook\n if (this.config.onSetup) {\n await this.config.onSetup(this.result);\n }\n\n return this.result;\n }\n\n /**\n * Cleanup all test infrastructure\n */\n async cleanup(): Promise<void> {\n if (this.result?.redis) {\n await this.result.redis.quit();\n }\n if (this.result?.orm) {\n await this.result.orm.close();\n }\n await this.containers.cleanup();\n this.result = undefined;\n }\n\n /**\n * Clear all data from the database and/or cache\n */\n async clearDatabase(): Promise<void> {\n if (this.result) {\n await clearTestDatabase(this.result.orm, this.result.redis);\n }\n }\n}\n","/**\n * Standard mock authentication tokens for testing\n */\nexport const TEST_TOKENS = {\n /**\n * Mock Bearer token for testing\n */\n AUTH: 'Bearer test-token',\n\n /**\n * Mock valid HMAC token for testing\n */\n HMAC: 'HMAC keyId=test-key ts=1234567890 nonce=test-nonce signature=test-signature',\n\n /**\n * Mock invalid HMAC token for testing authentication failures\n */\n HMAC_INVALID:\n 'HMAC keyId=invalid-key ts=1234567890 nonce=invalid-nonce signature=invalid-signature'\n} as const;\n"],"mappings":"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","names":["getDatabasePort","Redis"]}
|
package/lib/index.mjs
CHANGED
|
@@ -133,6 +133,74 @@ var TestContainerManager = class {
|
|
|
133
133
|
this.containers.push(container);
|
|
134
134
|
return container;
|
|
135
135
|
}
|
|
136
|
+
/**
|
|
137
|
+
* Setup Kafka test container
|
|
138
|
+
*/
|
|
139
|
+
async setupKafkaContainer(config = {}) {
|
|
140
|
+
const {
|
|
141
|
+
clusterId = "test-cluster",
|
|
142
|
+
numPartitions = 1,
|
|
143
|
+
replicationFactor = 1,
|
|
144
|
+
env = {}
|
|
145
|
+
} = config;
|
|
146
|
+
const container = await new GenericContainer("confluentinc/cp-kafka:latest").withExposedPorts(9092, 9093).withEnvironment({
|
|
147
|
+
KAFKA_BROKER_ID: "1",
|
|
148
|
+
KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: "PLAINTEXT:PLAINTEXT,PLAINTEXT_HOST:PLAINTEXT",
|
|
149
|
+
KAFKA_ADVERTISED_LISTENERS: "PLAINTEXT://kafka:29092,PLAINTEXT_HOST://localhost:9092",
|
|
150
|
+
KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: replicationFactor.toString(),
|
|
151
|
+
KAFKA_TRANSACTION_STATE_LOG_MIN_ISR: "1",
|
|
152
|
+
KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR: "1",
|
|
153
|
+
KAFKA_GROUP_INITIAL_REBALANCE_DELAY_MS: "0",
|
|
154
|
+
KAFKA_NUM_PARTITIONS: numPartitions.toString(),
|
|
155
|
+
KAFKA_CLUSTER_ID: clusterId,
|
|
156
|
+
KAFKA_PROCESS_ROLES: "broker,controller",
|
|
157
|
+
KAFKA_NODE_ID: "1",
|
|
158
|
+
KAFKA_CONTROLLER_QUORUM_VOTERS: "1@localhost:9093",
|
|
159
|
+
KAFKA_LISTENERS: "PLAINTEXT://0.0.0.0:29092,CONTROLLER://0.0.0.0:9093,PLAINTEXT_HOST://0.0.0.0:9092",
|
|
160
|
+
KAFKA_INTER_BROKER_LISTENER_NAME: "PLAINTEXT",
|
|
161
|
+
KAFKA_CONTROLLER_LISTENER_NAMES: "CONTROLLER",
|
|
162
|
+
KAFKA_LOG_DIRS: "/tmp/kraft-combined-logs",
|
|
163
|
+
...env
|
|
164
|
+
}).start();
|
|
165
|
+
await new Promise((resolve) => setTimeout(resolve, 5e3));
|
|
166
|
+
this.containers.push(container);
|
|
167
|
+
return container;
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Setup MinIO (S3-compatible) test container
|
|
171
|
+
*/
|
|
172
|
+
async setupS3Container(config = {}) {
|
|
173
|
+
const {
|
|
174
|
+
rootUser = "minioadmin",
|
|
175
|
+
rootPassword = "minioadmin",
|
|
176
|
+
defaultBucket = "test-bucket",
|
|
177
|
+
region = "us-east-1"
|
|
178
|
+
} = config;
|
|
179
|
+
const container = await new GenericContainer("minio/minio:latest").withExposedPorts(9e3, 9001).withEnvironment({
|
|
180
|
+
MINIO_ROOT_USER: rootUser,
|
|
181
|
+
MINIO_ROOT_PASSWORD: rootPassword,
|
|
182
|
+
MINIO_REGION: region
|
|
183
|
+
}).withCommand(["server", "/data", "--console-address", ":9001"]).start();
|
|
184
|
+
await new Promise((resolve) => setTimeout(resolve, 2e3));
|
|
185
|
+
if (defaultBucket) {
|
|
186
|
+
try {
|
|
187
|
+
await container.exec([
|
|
188
|
+
"mc",
|
|
189
|
+
"alias",
|
|
190
|
+
"set",
|
|
191
|
+
"local",
|
|
192
|
+
"http://localhost:9000",
|
|
193
|
+
rootUser,
|
|
194
|
+
rootPassword
|
|
195
|
+
]);
|
|
196
|
+
await container.exec(["mc", "mb", `local/${defaultBucket}`]);
|
|
197
|
+
} catch (error) {
|
|
198
|
+
console.warn("Could not create default bucket:", error);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
this.containers.push(container);
|
|
202
|
+
return container;
|
|
203
|
+
}
|
|
136
204
|
/**
|
|
137
205
|
* Cleanup all containers
|
|
138
206
|
*/
|
|
@@ -175,6 +243,8 @@ function setupTestEnvironment(config) {
|
|
|
175
243
|
database,
|
|
176
244
|
databaseType,
|
|
177
245
|
redis,
|
|
246
|
+
kafka,
|
|
247
|
+
s3,
|
|
178
248
|
hmacSecret = "test-secret-key",
|
|
179
249
|
customVars = {}
|
|
180
250
|
} = config;
|
|
@@ -192,6 +262,22 @@ function setupTestEnvironment(config) {
|
|
|
192
262
|
}
|
|
193
263
|
if (redis) {
|
|
194
264
|
process.env.REDIS_URL = `redis://${redis.getHost()}:${redis.getMappedPort(6379)}`;
|
|
265
|
+
process.env.REDIS_HOST = redis.getHost();
|
|
266
|
+
process.env.REDIS_PORT = redis.getMappedPort(6379).toString();
|
|
267
|
+
}
|
|
268
|
+
if (kafka) {
|
|
269
|
+
const kafkaBroker = `${kafka.getHost()}:${kafka.getMappedPort(9092)}`;
|
|
270
|
+
process.env.KAFKA_BROKERS = kafkaBroker;
|
|
271
|
+
process.env.KAFKA_CLIENT_ID = "test-client";
|
|
272
|
+
process.env.KAFKA_GROUP_ID = "test-group";
|
|
273
|
+
}
|
|
274
|
+
if (s3) {
|
|
275
|
+
process.env.S3_ENDPOINT = `http://${s3.getHost()}:${s3.getMappedPort(9e3)}`;
|
|
276
|
+
process.env.S3_ACCESS_KEY_ID = "minioadmin";
|
|
277
|
+
process.env.S3_SECRET_ACCESS_KEY = "minioadmin";
|
|
278
|
+
process.env.S3_REGION = "us-east-1";
|
|
279
|
+
process.env.S3_BUCKET = "test-bucket";
|
|
280
|
+
process.env.S3_FORCE_PATH_STYLE = "true";
|
|
195
281
|
}
|
|
196
282
|
process.env.HMAC_SECRET_KEY = hmacSecret;
|
|
197
283
|
process.env.JWKS_PUBLIC_KEY_URL = "http://localhost:3000/.well-known/jwks.json";
|
|
@@ -320,15 +406,25 @@ var BlueprintTestHarness = class {
|
|
|
320
406
|
containers;
|
|
321
407
|
result;
|
|
322
408
|
/**
|
|
323
|
-
* Setup all test infrastructure (containers, ORM, Redis)
|
|
409
|
+
* Setup all test infrastructure (containers, ORM, Redis, Kafka, S3)
|
|
324
410
|
*/
|
|
325
411
|
async setup() {
|
|
326
412
|
let container = null;
|
|
327
413
|
let orm;
|
|
328
414
|
let redisContainer;
|
|
415
|
+
let kafkaContainer;
|
|
416
|
+
let s3Container;
|
|
329
417
|
if (this.config.needsRedis) {
|
|
330
418
|
redisContainer = await this.containers.setupRedisContainer();
|
|
331
419
|
}
|
|
420
|
+
if (this.config.needsKafka) {
|
|
421
|
+
kafkaContainer = await this.containers.setupKafkaContainer();
|
|
422
|
+
}
|
|
423
|
+
if (this.config.needsS3) {
|
|
424
|
+
s3Container = await this.containers.setupS3Container({
|
|
425
|
+
defaultBucket: this.config.s3Bucket
|
|
426
|
+
});
|
|
427
|
+
}
|
|
332
428
|
if (this.config.databaseType && this.config.getConfig) {
|
|
333
429
|
const databaseType = this.config.databaseType;
|
|
334
430
|
container = await this.containers.setupDatabaseContainer(databaseType);
|
|
@@ -336,6 +432,8 @@ var BlueprintTestHarness = class {
|
|
|
336
432
|
database: container,
|
|
337
433
|
databaseType,
|
|
338
434
|
redis: redisContainer,
|
|
435
|
+
kafka: kafkaContainer,
|
|
436
|
+
s3: s3Container,
|
|
339
437
|
customVars: this.config.customEnvVars
|
|
340
438
|
});
|
|
341
439
|
const mikroOrmConfig = await this.config.getConfig();
|
|
@@ -351,6 +449,8 @@ var BlueprintTestHarness = class {
|
|
|
351
449
|
database: null,
|
|
352
450
|
databaseType: void 0,
|
|
353
451
|
redis: redisContainer,
|
|
452
|
+
kafka: kafkaContainer,
|
|
453
|
+
s3: s3Container,
|
|
354
454
|
customVars: this.config.customEnvVars
|
|
355
455
|
});
|
|
356
456
|
}
|
|
@@ -363,7 +463,14 @@ var BlueprintTestHarness = class {
|
|
|
363
463
|
});
|
|
364
464
|
await redis.ping();
|
|
365
465
|
}
|
|
366
|
-
this.result = {
|
|
466
|
+
this.result = {
|
|
467
|
+
container,
|
|
468
|
+
redisContainer,
|
|
469
|
+
kafkaContainer,
|
|
470
|
+
s3Container,
|
|
471
|
+
orm,
|
|
472
|
+
redis
|
|
473
|
+
};
|
|
367
474
|
if (this.config.onSetup) {
|
|
368
475
|
await this.config.onSetup(this.result);
|
|
369
476
|
}
|
package/lib/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/containers.ts","../src/environment.ts","../src/database.ts","../src/harness.ts","../src/tokens.ts"],"sourcesContent":["import { GenericContainer, StartedTestContainer } from 'testcontainers';\n\nexport type DatabaseType =\n | 'postgres'\n | 'postgresql'\n | 'mysql'\n | 'mariadb'\n | 'mongodb'\n | 'mongo'\n | 'mssql'\n | 'libsql'\n | 'sqlite'\n | 'better-sqlite';\n\nexport interface PostgresConfig {\n user?: string;\n password?: string;\n database?: string;\n command?: string[];\n}\n\nexport interface MySQLConfig {\n user?: string;\n password?: string;\n database?: string;\n rootPassword?: string;\n}\n\nexport interface MongoDBConfig {\n user?: string;\n password?: string;\n database?: string;\n}\n\nexport interface MSSQLConfig {\n user?: string;\n password?: string;\n database?: string;\n saPassword?: string;\n}\n\nexport interface SQLiteConfig {\n database?: string;\n}\n\nexport interface RedisConfig {\n command?: string[];\n}\n\nexport type DatabaseConfig =\n | PostgresConfig\n | MySQLConfig\n | MongoDBConfig\n | MSSQLConfig\n | SQLiteConfig;\n\n/**\n * Manages test containers (PostgreSQL, MySQL, MongoDB, Redis, etc.) for E2E testing\n */\nexport class TestContainerManager {\n private containers: StartedTestContainer[] = [];\n\n /**\n * Setup database container based on type\n */\n async setupDatabaseContainer(\n type: DatabaseType,\n config: DatabaseConfig = {}\n ): Promise<StartedTestContainer | null> {\n const normalizedType = this.normalizeDatabaseType(type);\n\n switch (normalizedType) {\n case 'postgres':\n return this.setupPostgresContainer(config as PostgresConfig);\n case 'mysql':\n return this.setupMySQLContainer(config as MySQLConfig);\n case 'mongodb':\n return this.setupMongoDBContainer(config as MongoDBConfig);\n case 'mssql':\n return this.setupMSSQLContainer(config as MSSQLConfig);\n case 'sqlite':\n // SQLite doesn't need a container (file-based)\n return null;\n default:\n throw new Error(`Unsupported database type: ${type}`);\n }\n }\n\n /**\n * Normalize database type aliases\n */\n private normalizeDatabaseType(\n type: DatabaseType\n ): 'postgres' | 'mysql' | 'mongodb' | 'mssql' | 'sqlite' {\n switch (type) {\n case 'postgres':\n case 'postgresql':\n return 'postgres';\n case 'mysql':\n case 'mariadb':\n return 'mysql';\n case 'mongodb':\n case 'mongo':\n return 'mongodb';\n case 'mssql':\n return 'mssql';\n case 'sqlite':\n case 'better-sqlite':\n case 'libsql':\n return 'sqlite';\n default:\n throw new Error(`Unknown database type: ${type}`);\n }\n }\n\n /**\n * Setup PostgreSQL test container\n */\n async setupPostgresContainer(\n config: PostgresConfig = {}\n ): Promise<StartedTestContainer> {\n const {\n user = 'test_user',\n password = 'test_password',\n database = 'test_db',\n command = ['postgres', '-c', 'log_statement=all']\n } = config;\n\n const container = await new GenericContainer('postgres:latest')\n .withExposedPorts(5432)\n .withEnvironment({\n POSTGRES_USER: user,\n POSTGRES_PASSWORD: password,\n POSTGRES_DB: database\n })\n .withCommand(command)\n .start();\n\n this.containers.push(container);\n return container;\n }\n\n /**\n * Setup MySQL test container\n */\n async setupMySQLContainer(\n config: MySQLConfig = {}\n ): Promise<StartedTestContainer> {\n const {\n user = 'test_user',\n password = 'test_password',\n database = 'test_db',\n rootPassword = 'root_password'\n } = config;\n\n const container = await new GenericContainer('mysql:8')\n .withExposedPorts(3306)\n .withEnvironment({\n MYSQL_ROOT_PASSWORD: rootPassword,\n MYSQL_DATABASE: database,\n MYSQL_USER: user,\n MYSQL_PASSWORD: password\n })\n .start();\n\n this.containers.push(container);\n return container;\n }\n\n /**\n * Setup MongoDB test container\n */\n async setupMongoDBContainer(\n config: MongoDBConfig = {}\n ): Promise<StartedTestContainer> {\n const {\n user = 'test_user',\n password = 'test_password',\n database = 'test_db'\n } = config;\n\n const container = await new GenericContainer('mongo:latest')\n .withExposedPorts(27017)\n .withEnvironment({\n MONGO_INITDB_ROOT_USERNAME: user,\n MONGO_INITDB_ROOT_PASSWORD: password,\n MONGO_INITDB_DATABASE: database\n })\n .start();\n\n this.containers.push(container);\n return container;\n }\n\n /**\n * Setup Microsoft SQL Server test container\n */\n async setupMSSQLContainer(\n config: MSSQLConfig = {}\n ): Promise<StartedTestContainer> {\n const {\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n user = 'SA',\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n password = 'Test_Password123!',\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n database = 'test_db',\n saPassword = 'Test_Password123!'\n } = config;\n\n const container = await new GenericContainer(\n 'mcr.microsoft.com/mssql/server:2022-latest'\n )\n .withExposedPorts(1433)\n .withEnvironment({\n ACCEPT_EULA: 'Y',\n SA_PASSWORD: saPassword,\n MSSQL_PID: 'Developer'\n })\n .start();\n\n // Wait a bit for SQL Server to be ready\n await new Promise((resolve) => setTimeout(resolve, 3000));\n\n this.containers.push(container);\n return container;\n }\n\n /**\n * Setup Redis test container\n */\n async setupRedisContainer(\n config: RedisConfig = {}\n ): Promise<StartedTestContainer> {\n const { command = ['redis-server', '--appendonly', 'yes'] } = config;\n\n const container = await new GenericContainer('redis:latest')\n .withExposedPorts(6379)\n .withCommand(command)\n .start();\n\n this.containers.push(container);\n return container;\n }\n\n /**\n * Cleanup all containers\n */\n async cleanup(): Promise<void> {\n await Promise.all(\n this.containers.map((container) =>\n container.stop({ remove: true, removeVolumes: true }).catch(() => {\n // Ignore cleanup errors\n })\n )\n );\n this.containers = [];\n }\n}\n","import { StartedTestContainer } from 'testcontainers';\nimport { DatabaseType } from './containers';\n\nexport interface TestEnvConfig {\n database: StartedTestContainer | null;\n databaseType?: DatabaseType;\n redis?: StartedTestContainer;\n hmacSecret?: string;\n customVars?: Record<string, string>;\n}\n\n/**\n * Get the default port for a database type\n */\nfunction getDatabasePort(type: DatabaseType): number {\n switch (type) {\n case 'postgres':\n case 'postgresql':\n return 5432;\n case 'mysql':\n case 'mariadb':\n return 3306;\n case 'mongodb':\n case 'mongo':\n return 27017;\n case 'mssql':\n return 1433;\n case 'sqlite':\n case 'better-sqlite':\n case 'libsql':\n return 0; // SQLite is file-based, no port\n default:\n return 5432;\n }\n}\n\n/**\n * Setup test environment variables for a blueprint test\n */\nexport function setupTestEnvironment(config: TestEnvConfig): void {\n const {\n database,\n databaseType,\n redis,\n hmacSecret = 'test-secret-key',\n customVars = {}\n } = config;\n\n // Only set database environment variables if database is configured\n if (databaseType) {\n const dbPort = getDatabasePort(databaseType);\n\n // Database environment variables\n process.env.DB_NAME = 'test_db';\n\n // SQLite databases are file-based, no container needed\n if (\n databaseType === 'sqlite' ||\n databaseType === 'better-sqlite' ||\n databaseType === 'libsql'\n ) {\n process.env.DB_PATH = ':memory:'; // In-memory SQLite for tests\n } else if (database) {\n process.env.DB_HOST = database.getHost();\n process.env.DB_USER = databaseType === 'mssql' ? 'SA' : 'test_user';\n process.env.DB_PASSWORD =\n databaseType === 'mssql' ? 'Test_Password123!' : 'test_password';\n process.env.DB_PORT = database.getMappedPort(dbPort).toString();\n }\n }\n\n // Redis environment variables (if provided)\n if (redis) {\n process.env.REDIS_URL = `redis://${redis.getHost()}:${redis.getMappedPort(6379)}`;\n }\n\n // Standard test environment variables\n process.env.HMAC_SECRET_KEY = hmacSecret;\n process.env.JWKS_PUBLIC_KEY_URL =\n 'http://localhost:3000/.well-known/jwks.json';\n process.env.OTEL_SERVICE_NAME = 'test-service';\n process.env.OTEL_EXPORTER_OTLP_ENDPOINT = 'http://localhost:4318';\n process.env.HOST = 'localhost';\n process.env.PORT = '3000';\n process.env.NODE_ENV = 'test';\n process.env.VERSION = 'v1';\n process.env.DOCS_PATH = '/docs';\n process.env.OTEL_LEVEL = 'info';\n process.env.DOTENV_FILE_PATH = '.env.test';\n\n // Custom environment variables\n Object.entries(customVars).forEach(([key, value]) => {\n process.env[key] = value;\n });\n}\n","import { MikroORM, Options } from '@mikro-orm/core';\nimport Redis from 'ioredis';\nimport { StartedTestContainer } from 'testcontainers';\nimport { DatabaseType } from './containers';\n\nexport interface MikroOrmTestConfig {\n /**\n * MikroORM config object (imported from mikro-orm.config)\n */\n mikroOrmConfig: Options;\n\n /**\n * Database type (postgres, mysql, mongodb, etc.)\n */\n databaseType: DatabaseType;\n\n /**\n * Whether to use migrations (true) or schema generation (false)\n * - true: IAM blueprints (uses getMigrator().up())\n * - false: Billing blueprints (uses getSchemaGenerator().createSchema())\n */\n useMigrations?: boolean;\n\n /**\n * Path to migrations directory (required if useMigrations is true)\n */\n migrationsPath?: string;\n\n /**\n * Database container instance (null for file-based databases like SQLite)\n */\n container: StartedTestContainer | null;\n}\n\n/**\n * Get the default port for a database type\n */\nfunction getDatabasePort(type: DatabaseType): number {\n switch (type) {\n case 'postgres':\n case 'postgresql':\n return 5432;\n case 'mysql':\n case 'mariadb':\n return 3306;\n case 'mongodb':\n case 'mongo':\n return 27017;\n case 'mssql':\n return 1433;\n case 'sqlite':\n case 'better-sqlite':\n case 'libsql':\n return 0; // SQLite is file-based, no port\n default:\n return 5432;\n }\n}\n\n/**\n * Setup MikroORM for testing with proper schema/migrations\n */\nexport async function setupTestORM(\n config: MikroOrmTestConfig\n): Promise<MikroORM> {\n const {\n mikroOrmConfig,\n databaseType,\n useMigrations = false,\n container\n } = config;\n\n const dbPort = getDatabasePort(databaseType);\n\n // SQLite databases are file-based\n let ormConfig: Options = {};\n if (\n databaseType === 'sqlite' ||\n databaseType === 'better-sqlite' ||\n databaseType === 'libsql'\n ) {\n ormConfig = {\n ...mikroOrmConfig,\n dbName: ':memory:', // In-memory SQLite for tests\n debug: false,\n ...(useMigrations\n ? {\n migrations: {\n path: config.migrationsPath,\n glob: '!(*.d).{js,ts}',\n dropTables: true\n }\n }\n : {\n schemaGenerator: {\n createForeignKeyConstraints: false\n }\n })\n };\n } else if (container) {\n // Container-based databases\n ormConfig = {\n ...mikroOrmConfig,\n dbName: 'test_db',\n host: container.getHost(),\n user: databaseType === 'mssql' ? 'SA' : 'test_user',\n password:\n databaseType === 'mssql' ? 'Test_Password123!' : 'test_password',\n port: container.getMappedPort(dbPort),\n debug: false,\n ...(useMigrations\n ? {\n migrations: {\n path: config.migrationsPath,\n glob: '!(*.d).{js,ts}',\n dropTables: true\n }\n }\n : {\n schemaGenerator: {\n createForeignKeyConstraints: false\n }\n })\n };\n }\n\n const orm = await MikroORM.init(ormConfig);\n\n // Initialize database schema\n if (useMigrations) {\n await orm.getMigrator().up();\n } else {\n await orm.getSchemaGenerator().createSchema();\n }\n\n return orm;\n}\n\n/**\n * Clear all data from the test database and/or cache\n */\nexport async function clearTestDatabase(\n orm?: MikroORM,\n redis?: Redis\n): Promise<void> {\n // Clear Redis if provided\n if (redis) {\n await redis.flushall();\n }\n\n // Clear all database entities (if ORM is provided)\n if (orm) {\n const em = orm.em.fork();\n const entities = Object.values(orm.getMetadata().getAll());\n\n // Delete in reverse order to avoid foreign key constraints\n for (const entity of entities.reverse()) {\n try {\n await em.nativeDelete(entity.class, {});\n } catch (error) {\n // Ignore \"table does not exist\" errors\n if (!(error as Error).message?.includes('does not exist')) {\n throw error;\n }\n }\n }\n\n await em.flush();\n }\n}\n","import { MikroORM, Options } from '@mikro-orm/core';\nimport Redis from 'ioredis';\nimport { StartedTestContainer } from 'testcontainers';\nimport { DatabaseType, TestContainerManager } from './containers';\nimport { clearTestDatabase, setupTestORM } from './database';\nimport { setupTestEnvironment } from './environment';\n\nexport interface BlueprintTestConfig {\n /**\n * Function that imports and returns the MikroORM config\n * This is called AFTER environment variables are set\n * Optional - if not provided, no database will be set up\n */\n getConfig?: () => Promise<Options>;\n\n /**\n * Database type (postgres, mysql, mongodb, etc.)\n * Optional - if not provided, no database will be set up\n */\n databaseType?: DatabaseType;\n\n /**\n * Whether to use migrations (true) or schema generation (false)\n */\n useMigrations?: boolean;\n\n /**\n * Path to migrations directory (required if useMigrations is true)\n */\n migrationsPath?: string;\n\n /**\n * Whether the blueprint needs Redis\n */\n needsRedis?: boolean;\n\n /**\n * Custom environment variables to set\n */\n customEnvVars?: Record<string, string>;\n\n /**\n * Custom setup hook called after containers and ORM are initialized\n */\n onSetup?: (setup: TestSetupResult) => Promise<void>;\n}\n\nexport interface TestSetupResult {\n container: StartedTestContainer | null;\n redisContainer?: StartedTestContainer;\n orm?: MikroORM;\n redis?: Redis;\n}\n\n/**\n * Complete test harness for blueprint E2E testing\n *\n * Handles container setup, environment configuration, and database initialization\n *\n * @example Database with ORM\n * ```typescript\n * const harness = new BlueprintTestHarness({\n * getConfig: async () => {\n * const { default: config } = await import('../mikro-orm.config');\n * return config;\n * },\n * databaseType: 'postgres',\n * useMigrations: false,\n * needsRedis: true,\n * customEnvVars: {\n * STRIPE_API_KEY: 'sk_test_...'\n * }\n * });\n *\n * const setup = await harness.setup();\n * // ... run tests with setup.orm and setup.redis\n * await harness.cleanup();\n * ```\n *\n * @example Cache-only (no database)\n * ```typescript\n * const harness = new BlueprintTestHarness({\n * needsRedis: true,\n * customEnvVars: {\n * API_KEY: 'test_key'\n * }\n * });\n *\n * const setup = await harness.setup();\n * // ... run tests with setup.redis only (setup.orm is undefined)\n * await harness.cleanup();\n * ```\n */\nexport class BlueprintTestHarness {\n private containers: TestContainerManager;\n private result?: TestSetupResult;\n\n constructor(private config: BlueprintTestConfig) {\n this.containers = new TestContainerManager();\n }\n\n /**\n * Setup all test infrastructure (containers, ORM, Redis)\n */\n async setup(): Promise<TestSetupResult> {\n // Setup database container only if database is needed\n let container: StartedTestContainer | null = null;\n let orm: MikroORM | undefined;\n let redisContainer: StartedTestContainer | undefined;\n\n // Setup Redis container if needed (for both database and cache-only modes)\n if (this.config.needsRedis) {\n redisContainer = await this.containers.setupRedisContainer();\n }\n\n if (this.config.databaseType && this.config.getConfig) {\n const databaseType = this.config.databaseType;\n\n // Setup database container\n container = await this.containers.setupDatabaseContainer(databaseType);\n\n // Setup environment variables\n setupTestEnvironment({\n database: container,\n databaseType,\n redis: redisContainer,\n customVars: this.config.customEnvVars\n });\n\n // Get the config AFTER environment is set\n const mikroOrmConfig = await this.config.getConfig();\n\n // Setup ORM\n orm = await setupTestORM({\n mikroOrmConfig,\n databaseType,\n useMigrations: this.config.useMigrations,\n migrationsPath: this.config.migrationsPath,\n container\n });\n } else {\n // Cache-only mode: no database\n setupTestEnvironment({\n database: null,\n databaseType: undefined,\n redis: redisContainer,\n customVars: this.config.customEnvVars\n });\n }\n\n // Setup Redis client if needed\n let redis: Redis | undefined;\n if (redisContainer) {\n redis = new Redis({\n host: redisContainer.getHost(),\n port: redisContainer.getMappedPort(6379),\n maxRetriesPerRequest: 3\n });\n await redis.ping();\n }\n\n this.result = { container, redisContainer, orm, redis };\n\n // Call custom setup hook\n if (this.config.onSetup) {\n await this.config.onSetup(this.result);\n }\n\n return this.result;\n }\n\n /**\n * Cleanup all test infrastructure\n */\n async cleanup(): Promise<void> {\n if (this.result?.redis) {\n await this.result.redis.quit();\n }\n if (this.result?.orm) {\n await this.result.orm.close();\n }\n await this.containers.cleanup();\n this.result = undefined;\n }\n\n /**\n * Clear all data from the database and/or cache\n */\n async clearDatabase(): Promise<void> {\n if (this.result) {\n await clearTestDatabase(this.result.orm, this.result.redis);\n }\n }\n}\n","/**\n * Standard mock authentication tokens for testing\n */\nexport const TEST_TOKENS = {\n /**\n * Mock Bearer token for testing\n */\n AUTH: 'Bearer test-token',\n\n /**\n * Mock valid HMAC token for testing\n */\n HMAC: 'HMAC keyId=test-key ts=1234567890 nonce=test-nonce signature=test-signature',\n\n /**\n * Mock invalid HMAC token for testing authentication failures\n */\n HMAC_INVALID:\n 'HMAC keyId=invalid-key ts=1234567890 nonce=invalid-nonce signature=invalid-signature'\n} as const;\n"],"mappings":"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","names":["getDatabasePort"]}
|
|
1
|
+
{"version":3,"sources":["../src/containers.ts","../src/environment.ts","../src/database.ts","../src/harness.ts","../src/tokens.ts"],"sourcesContent":["import { GenericContainer, StartedTestContainer } from 'testcontainers';\n\nexport type DatabaseType =\n | 'postgres'\n | 'postgresql'\n | 'mysql'\n | 'mariadb'\n | 'mongodb'\n | 'mongo'\n | 'mssql'\n | 'libsql'\n | 'sqlite'\n | 'better-sqlite';\n\nexport interface PostgresConfig {\n user?: string;\n password?: string;\n database?: string;\n command?: string[];\n}\n\nexport interface MySQLConfig {\n user?: string;\n password?: string;\n database?: string;\n rootPassword?: string;\n}\n\nexport interface MongoDBConfig {\n user?: string;\n password?: string;\n database?: string;\n}\n\nexport interface MSSQLConfig {\n user?: string;\n password?: string;\n database?: string;\n saPassword?: string;\n}\n\nexport interface SQLiteConfig {\n database?: string;\n}\n\nexport interface RedisConfig {\n command?: string[];\n}\n\nexport interface KafkaConfig {\n /** Kafka cluster ID */\n clusterId?: string;\n /** Number of partitions */\n numPartitions?: number;\n /** Replication factor */\n replicationFactor?: number;\n /** Additional environment variables */\n env?: Record<string, string>;\n}\n\nexport interface S3Config {\n /** MinIO root user (access key) */\n rootUser?: string;\n /** MinIO root password (secret key) */\n rootPassword?: string;\n /** Default bucket to create */\n defaultBucket?: string;\n /** Region */\n region?: string;\n}\n\nexport type DatabaseConfig =\n | PostgresConfig\n | MySQLConfig\n | MongoDBConfig\n | MSSQLConfig\n | SQLiteConfig;\n\n/**\n * Manages test containers (PostgreSQL, MySQL, MongoDB, Redis, etc.) for E2E testing\n */\nexport class TestContainerManager {\n private containers: StartedTestContainer[] = [];\n\n /**\n * Setup database container based on type\n */\n async setupDatabaseContainer(\n type: DatabaseType,\n config: DatabaseConfig = {}\n ): Promise<StartedTestContainer | null> {\n const normalizedType = this.normalizeDatabaseType(type);\n\n switch (normalizedType) {\n case 'postgres':\n return this.setupPostgresContainer(config as PostgresConfig);\n case 'mysql':\n return this.setupMySQLContainer(config as MySQLConfig);\n case 'mongodb':\n return this.setupMongoDBContainer(config as MongoDBConfig);\n case 'mssql':\n return this.setupMSSQLContainer(config as MSSQLConfig);\n case 'sqlite':\n // SQLite doesn't need a container (file-based)\n return null;\n default:\n throw new Error(`Unsupported database type: ${type}`);\n }\n }\n\n /**\n * Normalize database type aliases\n */\n private normalizeDatabaseType(\n type: DatabaseType\n ): 'postgres' | 'mysql' | 'mongodb' | 'mssql' | 'sqlite' {\n switch (type) {\n case 'postgres':\n case 'postgresql':\n return 'postgres';\n case 'mysql':\n case 'mariadb':\n return 'mysql';\n case 'mongodb':\n case 'mongo':\n return 'mongodb';\n case 'mssql':\n return 'mssql';\n case 'sqlite':\n case 'better-sqlite':\n case 'libsql':\n return 'sqlite';\n default:\n throw new Error(`Unknown database type: ${type}`);\n }\n }\n\n /**\n * Setup PostgreSQL test container\n */\n async setupPostgresContainer(\n config: PostgresConfig = {}\n ): Promise<StartedTestContainer> {\n const {\n user = 'test_user',\n password = 'test_password',\n database = 'test_db',\n command = ['postgres', '-c', 'log_statement=all']\n } = config;\n\n const container = await new GenericContainer('postgres:latest')\n .withExposedPorts(5432)\n .withEnvironment({\n POSTGRES_USER: user,\n POSTGRES_PASSWORD: password,\n POSTGRES_DB: database\n })\n .withCommand(command)\n .start();\n\n this.containers.push(container);\n return container;\n }\n\n /**\n * Setup MySQL test container\n */\n async setupMySQLContainer(\n config: MySQLConfig = {}\n ): Promise<StartedTestContainer> {\n const {\n user = 'test_user',\n password = 'test_password',\n database = 'test_db',\n rootPassword = 'root_password'\n } = config;\n\n const container = await new GenericContainer('mysql:8')\n .withExposedPorts(3306)\n .withEnvironment({\n MYSQL_ROOT_PASSWORD: rootPassword,\n MYSQL_DATABASE: database,\n MYSQL_USER: user,\n MYSQL_PASSWORD: password\n })\n .start();\n\n this.containers.push(container);\n return container;\n }\n\n /**\n * Setup MongoDB test container\n */\n async setupMongoDBContainer(\n config: MongoDBConfig = {}\n ): Promise<StartedTestContainer> {\n const {\n user = 'test_user',\n password = 'test_password',\n database = 'test_db'\n } = config;\n\n const container = await new GenericContainer('mongo:latest')\n .withExposedPorts(27017)\n .withEnvironment({\n MONGO_INITDB_ROOT_USERNAME: user,\n MONGO_INITDB_ROOT_PASSWORD: password,\n MONGO_INITDB_DATABASE: database\n })\n .start();\n\n this.containers.push(container);\n return container;\n }\n\n /**\n * Setup Microsoft SQL Server test container\n */\n async setupMSSQLContainer(\n config: MSSQLConfig = {}\n ): Promise<StartedTestContainer> {\n const {\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n user = 'SA',\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n password = 'Test_Password123!',\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n database = 'test_db',\n saPassword = 'Test_Password123!'\n } = config;\n\n const container = await new GenericContainer(\n 'mcr.microsoft.com/mssql/server:2022-latest'\n )\n .withExposedPorts(1433)\n .withEnvironment({\n ACCEPT_EULA: 'Y',\n SA_PASSWORD: saPassword,\n MSSQL_PID: 'Developer'\n })\n .start();\n\n // Wait a bit for SQL Server to be ready\n await new Promise((resolve) => setTimeout(resolve, 3000));\n\n this.containers.push(container);\n return container;\n }\n\n /**\n * Setup Redis test container\n */\n async setupRedisContainer(\n config: RedisConfig = {}\n ): Promise<StartedTestContainer> {\n const { command = ['redis-server', '--appendonly', 'yes'] } = config;\n\n const container = await new GenericContainer('redis:latest')\n .withExposedPorts(6379)\n .withCommand(command)\n .start();\n\n this.containers.push(container);\n return container;\n }\n\n /**\n * Setup Kafka test container\n */\n async setupKafkaContainer(\n config: KafkaConfig = {}\n ): Promise<StartedTestContainer> {\n const {\n clusterId = 'test-cluster',\n numPartitions = 1,\n replicationFactor = 1,\n env = {}\n } = config;\n\n const container = await new GenericContainer('confluentinc/cp-kafka:latest')\n .withExposedPorts(9092, 9093)\n .withEnvironment({\n KAFKA_BROKER_ID: '1',\n KAFKA_LISTENER_SECURITY_PROTOCOL_MAP:\n 'PLAINTEXT:PLAINTEXT,PLAINTEXT_HOST:PLAINTEXT',\n KAFKA_ADVERTISED_LISTENERS:\n 'PLAINTEXT://kafka:29092,PLAINTEXT_HOST://localhost:9092',\n KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: replicationFactor.toString(),\n KAFKA_TRANSACTION_STATE_LOG_MIN_ISR: '1',\n KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR: '1',\n KAFKA_GROUP_INITIAL_REBALANCE_DELAY_MS: '0',\n KAFKA_NUM_PARTITIONS: numPartitions.toString(),\n KAFKA_CLUSTER_ID: clusterId,\n KAFKA_PROCESS_ROLES: 'broker,controller',\n KAFKA_NODE_ID: '1',\n KAFKA_CONTROLLER_QUORUM_VOTERS: '1@localhost:9093',\n KAFKA_LISTENERS:\n 'PLAINTEXT://0.0.0.0:29092,CONTROLLER://0.0.0.0:9093,PLAINTEXT_HOST://0.0.0.0:9092',\n KAFKA_INTER_BROKER_LISTENER_NAME: 'PLAINTEXT',\n KAFKA_CONTROLLER_LISTENER_NAMES: 'CONTROLLER',\n KAFKA_LOG_DIRS: '/tmp/kraft-combined-logs',\n ...env\n })\n .start();\n\n // Wait for Kafka to be ready\n await new Promise((resolve) => setTimeout(resolve, 5000));\n\n this.containers.push(container);\n return container;\n }\n\n /**\n * Setup MinIO (S3-compatible) test container\n */\n async setupS3Container(config: S3Config = {}): Promise<StartedTestContainer> {\n const {\n rootUser = 'minioadmin',\n rootPassword = 'minioadmin',\n defaultBucket = 'test-bucket',\n region = 'us-east-1'\n } = config;\n\n const container = await new GenericContainer('minio/minio:latest')\n .withExposedPorts(9000, 9001)\n .withEnvironment({\n MINIO_ROOT_USER: rootUser,\n MINIO_ROOT_PASSWORD: rootPassword,\n MINIO_REGION: region\n })\n .withCommand(['server', '/data', '--console-address', ':9001'])\n .start();\n\n // Wait for MinIO to be ready\n await new Promise((resolve) => setTimeout(resolve, 2000));\n\n // Create default bucket if specified\n if (defaultBucket) {\n try {\n // Using MinIO client command via exec\n await container.exec([\n 'mc',\n 'alias',\n 'set',\n 'local',\n 'http://localhost:9000',\n rootUser,\n rootPassword\n ]);\n await container.exec(['mc', 'mb', `local/${defaultBucket}`]);\n } catch (error) {\n // Bucket creation might fail, but container is still usable\n console.warn('Could not create default bucket:', error);\n }\n }\n\n this.containers.push(container);\n return container;\n }\n\n /**\n * Cleanup all containers\n */\n async cleanup(): Promise<void> {\n await Promise.all(\n this.containers.map((container) =>\n container.stop({ remove: true, removeVolumes: true }).catch(() => {\n // Ignore cleanup errors\n })\n )\n );\n this.containers = [];\n }\n}\n","import { StartedTestContainer } from 'testcontainers';\nimport { DatabaseType } from './containers';\n\nexport interface TestEnvConfig {\n database: StartedTestContainer | null;\n databaseType?: DatabaseType;\n redis?: StartedTestContainer;\n kafka?: StartedTestContainer;\n s3?: StartedTestContainer;\n hmacSecret?: string;\n customVars?: Record<string, string>;\n}\n\n/**\n * Get the default port for a database type\n */\nfunction getDatabasePort(type: DatabaseType): number {\n switch (type) {\n case 'postgres':\n case 'postgresql':\n return 5432;\n case 'mysql':\n case 'mariadb':\n return 3306;\n case 'mongodb':\n case 'mongo':\n return 27017;\n case 'mssql':\n return 1433;\n case 'sqlite':\n case 'better-sqlite':\n case 'libsql':\n return 0; // SQLite is file-based, no port\n default:\n return 5432;\n }\n}\n\n/**\n * Setup test environment variables for a blueprint test\n */\nexport function setupTestEnvironment(config: TestEnvConfig): void {\n const {\n database,\n databaseType,\n redis,\n kafka,\n s3,\n hmacSecret = 'test-secret-key',\n customVars = {}\n } = config;\n\n // Only set database environment variables if database is configured\n if (databaseType) {\n const dbPort = getDatabasePort(databaseType);\n\n // Database environment variables\n process.env.DB_NAME = 'test_db';\n\n // SQLite databases are file-based, no container needed\n if (\n databaseType === 'sqlite' ||\n databaseType === 'better-sqlite' ||\n databaseType === 'libsql'\n ) {\n process.env.DB_PATH = ':memory:'; // In-memory SQLite for tests\n } else if (database) {\n process.env.DB_HOST = database.getHost();\n process.env.DB_USER = databaseType === 'mssql' ? 'SA' : 'test_user';\n process.env.DB_PASSWORD =\n databaseType === 'mssql' ? 'Test_Password123!' : 'test_password';\n process.env.DB_PORT = database.getMappedPort(dbPort).toString();\n }\n }\n\n // Redis environment variables (if provided)\n if (redis) {\n process.env.REDIS_URL = `redis://${redis.getHost()}:${redis.getMappedPort(6379)}`;\n process.env.REDIS_HOST = redis.getHost();\n process.env.REDIS_PORT = redis.getMappedPort(6379).toString();\n }\n\n // Kafka environment variables (if provided)\n if (kafka) {\n const kafkaBroker = `${kafka.getHost()}:${kafka.getMappedPort(9092)}`;\n process.env.KAFKA_BROKERS = kafkaBroker;\n process.env.KAFKA_CLIENT_ID = 'test-client';\n process.env.KAFKA_GROUP_ID = 'test-group';\n }\n\n // S3/MinIO environment variables (if provided)\n if (s3) {\n process.env.S3_ENDPOINT = `http://${s3.getHost()}:${s3.getMappedPort(9000)}`;\n process.env.S3_ACCESS_KEY_ID = 'minioadmin';\n process.env.S3_SECRET_ACCESS_KEY = 'minioadmin';\n process.env.S3_REGION = 'us-east-1';\n process.env.S3_BUCKET = 'test-bucket';\n process.env.S3_FORCE_PATH_STYLE = 'true'; // Required for MinIO\n }\n\n // Standard test environment variables\n process.env.HMAC_SECRET_KEY = hmacSecret;\n process.env.JWKS_PUBLIC_KEY_URL =\n 'http://localhost:3000/.well-known/jwks.json';\n process.env.OTEL_SERVICE_NAME = 'test-service';\n process.env.OTEL_EXPORTER_OTLP_ENDPOINT = 'http://localhost:4318';\n process.env.HOST = 'localhost';\n process.env.PORT = '3000';\n process.env.NODE_ENV = 'test';\n process.env.VERSION = 'v1';\n process.env.DOCS_PATH = '/docs';\n process.env.OTEL_LEVEL = 'info';\n process.env.DOTENV_FILE_PATH = '.env.test';\n\n // Custom environment variables\n Object.entries(customVars).forEach(([key, value]) => {\n process.env[key] = value;\n });\n}\n","import { MikroORM, Options } from '@mikro-orm/core';\nimport Redis from 'ioredis';\nimport { StartedTestContainer } from 'testcontainers';\nimport { DatabaseType } from './containers';\n\nexport interface MikroOrmTestConfig {\n /**\n * MikroORM config object (imported from mikro-orm.config)\n */\n mikroOrmConfig: Options;\n\n /**\n * Database type (postgres, mysql, mongodb, etc.)\n */\n databaseType: DatabaseType;\n\n /**\n * Whether to use migrations (true) or schema generation (false)\n * - true: IAM blueprints (uses getMigrator().up())\n * - false: Billing blueprints (uses getSchemaGenerator().createSchema())\n */\n useMigrations?: boolean;\n\n /**\n * Path to migrations directory (required if useMigrations is true)\n */\n migrationsPath?: string;\n\n /**\n * Database container instance (null for file-based databases like SQLite)\n */\n container: StartedTestContainer | null;\n}\n\n/**\n * Get the default port for a database type\n */\nfunction getDatabasePort(type: DatabaseType): number {\n switch (type) {\n case 'postgres':\n case 'postgresql':\n return 5432;\n case 'mysql':\n case 'mariadb':\n return 3306;\n case 'mongodb':\n case 'mongo':\n return 27017;\n case 'mssql':\n return 1433;\n case 'sqlite':\n case 'better-sqlite':\n case 'libsql':\n return 0; // SQLite is file-based, no port\n default:\n return 5432;\n }\n}\n\n/**\n * Setup MikroORM for testing with proper schema/migrations\n */\nexport async function setupTestORM(\n config: MikroOrmTestConfig\n): Promise<MikroORM> {\n const {\n mikroOrmConfig,\n databaseType,\n useMigrations = false,\n container\n } = config;\n\n const dbPort = getDatabasePort(databaseType);\n\n // SQLite databases are file-based\n let ormConfig: Options = {};\n if (\n databaseType === 'sqlite' ||\n databaseType === 'better-sqlite' ||\n databaseType === 'libsql'\n ) {\n ormConfig = {\n ...mikroOrmConfig,\n dbName: ':memory:', // In-memory SQLite for tests\n debug: false,\n ...(useMigrations\n ? {\n migrations: {\n path: config.migrationsPath,\n glob: '!(*.d).{js,ts}',\n dropTables: true\n }\n }\n : {\n schemaGenerator: {\n createForeignKeyConstraints: false\n }\n })\n };\n } else if (container) {\n // Container-based databases\n ormConfig = {\n ...mikroOrmConfig,\n dbName: 'test_db',\n host: container.getHost(),\n user: databaseType === 'mssql' ? 'SA' : 'test_user',\n password:\n databaseType === 'mssql' ? 'Test_Password123!' : 'test_password',\n port: container.getMappedPort(dbPort),\n debug: false,\n ...(useMigrations\n ? {\n migrations: {\n path: config.migrationsPath,\n glob: '!(*.d).{js,ts}',\n dropTables: true\n }\n }\n : {\n schemaGenerator: {\n createForeignKeyConstraints: false\n }\n })\n };\n }\n\n const orm = await MikroORM.init(ormConfig);\n\n // Initialize database schema\n if (useMigrations) {\n await orm.getMigrator().up();\n } else {\n await orm.getSchemaGenerator().createSchema();\n }\n\n return orm;\n}\n\n/**\n * Clear all data from the test database and/or cache\n */\nexport async function clearTestDatabase(\n orm?: MikroORM,\n redis?: Redis\n): Promise<void> {\n // Clear Redis if provided\n if (redis) {\n await redis.flushall();\n }\n\n // Clear all database entities (if ORM is provided)\n if (orm) {\n const em = orm.em.fork();\n const entities = Object.values(orm.getMetadata().getAll());\n\n // Delete in reverse order to avoid foreign key constraints\n for (const entity of entities.reverse()) {\n try {\n await em.nativeDelete(entity.class, {});\n } catch (error) {\n // Ignore \"table does not exist\" errors\n if (!(error as Error).message?.includes('does not exist')) {\n throw error;\n }\n }\n }\n\n await em.flush();\n }\n}\n","import { MikroORM, Options } from '@mikro-orm/core';\nimport Redis from 'ioredis';\nimport { StartedTestContainer } from 'testcontainers';\nimport { DatabaseType, TestContainerManager } from './containers';\nimport { clearTestDatabase, setupTestORM } from './database';\nimport { setupTestEnvironment } from './environment';\n\nexport interface BlueprintTestConfig {\n /**\n * Function that imports and returns the MikroORM config\n * This is called AFTER environment variables are set\n * Optional - if not provided, no database will be set up\n */\n getConfig?: () => Promise<Options>;\n\n /**\n * Database type (postgres, mysql, mongodb, etc.)\n * Optional - if not provided, no database will be set up\n */\n databaseType?: DatabaseType;\n\n /**\n * Whether to use migrations (true) or schema generation (false)\n */\n useMigrations?: boolean;\n\n /**\n * Path to migrations directory (required if useMigrations is true)\n */\n migrationsPath?: string;\n\n /**\n * Whether the blueprint needs Redis\n */\n needsRedis?: boolean;\n\n /**\n * Whether the blueprint needs Kafka\n */\n needsKafka?: boolean;\n\n /**\n * Whether the blueprint needs S3 (MinIO)\n */\n needsS3?: boolean;\n\n /**\n * S3 bucket name to create (default: 'test-bucket')\n */\n s3Bucket?: string;\n\n /**\n * Custom environment variables to set\n */\n customEnvVars?: Record<string, string>;\n\n /**\n * Custom setup hook called after containers and ORM are initialized\n */\n onSetup?: (setup: TestSetupResult) => Promise<void>;\n}\n\nexport interface TestSetupResult {\n container: StartedTestContainer | null;\n redisContainer?: StartedTestContainer;\n kafkaContainer?: StartedTestContainer;\n s3Container?: StartedTestContainer;\n orm?: MikroORM;\n redis?: Redis;\n}\n\n/**\n * Complete test harness for blueprint E2E testing\n *\n * Handles container setup, environment configuration, and database initialization\n *\n * @example Database with ORM\n * ```typescript\n * const harness = new BlueprintTestHarness({\n * getConfig: async () => {\n * const { default: config } = await import('../mikro-orm.config');\n * return config;\n * },\n * databaseType: 'postgres',\n * useMigrations: false,\n * needsRedis: true,\n * customEnvVars: {\n * STRIPE_API_KEY: 'sk_test_...'\n * }\n * });\n *\n * const setup = await harness.setup();\n * // ... run tests with setup.orm and setup.redis\n * await harness.cleanup();\n * ```\n *\n * @example Cache-only (no database)\n * ```typescript\n * const harness = new BlueprintTestHarness({\n * needsRedis: true,\n * customEnvVars: {\n * API_KEY: 'test_key'\n * }\n * });\n *\n * const setup = await harness.setup();\n * // ... run tests with setup.redis only (setup.orm is undefined)\n * await harness.cleanup();\n * ```\n *\n * @example With Kafka and S3\n * ```typescript\n * const harness = new BlueprintTestHarness({\n * getConfig: async () => {\n * const { default: config } = await import('../mikro-orm.config');\n * return config;\n * },\n * databaseType: 'postgres',\n * needsKafka: true,\n * needsS3: true,\n * s3Bucket: 'my-test-bucket',\n * customEnvVars: {\n * API_KEY: 'test_key'\n * }\n * });\n *\n * const setup = await harness.setup();\n * // Access containers via setup.kafkaContainer and setup.s3Container\n * // Kafka broker: process.env.KAFKA_BROKERS\n * // S3 endpoint: process.env.S3_ENDPOINT\n * await harness.cleanup();\n * ```\n */\nexport class BlueprintTestHarness {\n private containers: TestContainerManager;\n private result?: TestSetupResult;\n\n constructor(private config: BlueprintTestConfig) {\n this.containers = new TestContainerManager();\n }\n\n /**\n * Setup all test infrastructure (containers, ORM, Redis, Kafka, S3)\n */\n async setup(): Promise<TestSetupResult> {\n // Setup database container only if database is needed\n let container: StartedTestContainer | null = null;\n let orm: MikroORM | undefined;\n let redisContainer: StartedTestContainer | undefined;\n let kafkaContainer: StartedTestContainer | undefined;\n let s3Container: StartedTestContainer | undefined;\n\n // Setup Redis container if needed (for both database and cache-only modes)\n if (this.config.needsRedis) {\n redisContainer = await this.containers.setupRedisContainer();\n }\n\n // Setup Kafka container if needed\n if (this.config.needsKafka) {\n kafkaContainer = await this.containers.setupKafkaContainer();\n }\n\n // Setup S3 container if needed\n if (this.config.needsS3) {\n s3Container = await this.containers.setupS3Container({\n defaultBucket: this.config.s3Bucket\n });\n }\n\n if (this.config.databaseType && this.config.getConfig) {\n const databaseType = this.config.databaseType;\n\n // Setup database container\n container = await this.containers.setupDatabaseContainer(databaseType);\n\n // Setup environment variables\n setupTestEnvironment({\n database: container,\n databaseType,\n redis: redisContainer,\n kafka: kafkaContainer,\n s3: s3Container,\n customVars: this.config.customEnvVars\n });\n\n // Get the config AFTER environment is set\n const mikroOrmConfig = await this.config.getConfig();\n\n // Setup ORM\n orm = await setupTestORM({\n mikroOrmConfig,\n databaseType,\n useMigrations: this.config.useMigrations,\n migrationsPath: this.config.migrationsPath,\n container\n });\n } else {\n // Cache-only mode: no database\n setupTestEnvironment({\n database: null,\n databaseType: undefined,\n redis: redisContainer,\n kafka: kafkaContainer,\n s3: s3Container,\n customVars: this.config.customEnvVars\n });\n }\n\n // Setup Redis client if needed\n let redis: Redis | undefined;\n if (redisContainer) {\n redis = new Redis({\n host: redisContainer.getHost(),\n port: redisContainer.getMappedPort(6379),\n maxRetriesPerRequest: 3\n });\n await redis.ping();\n }\n\n this.result = {\n container,\n redisContainer,\n kafkaContainer,\n s3Container,\n orm,\n redis\n };\n\n // Call custom setup hook\n if (this.config.onSetup) {\n await this.config.onSetup(this.result);\n }\n\n return this.result;\n }\n\n /**\n * Cleanup all test infrastructure\n */\n async cleanup(): Promise<void> {\n if (this.result?.redis) {\n await this.result.redis.quit();\n }\n if (this.result?.orm) {\n await this.result.orm.close();\n }\n await this.containers.cleanup();\n this.result = undefined;\n }\n\n /**\n * Clear all data from the database and/or cache\n */\n async clearDatabase(): Promise<void> {\n if (this.result) {\n await clearTestDatabase(this.result.orm, this.result.redis);\n }\n }\n}\n","/**\n * Standard mock authentication tokens for testing\n */\nexport const TEST_TOKENS = {\n /**\n * Mock Bearer token for testing\n */\n AUTH: 'Bearer test-token',\n\n /**\n * Mock valid HMAC token for testing\n */\n HMAC: 'HMAC keyId=test-key ts=1234567890 nonce=test-nonce signature=test-signature',\n\n /**\n * Mock invalid HMAC token for testing authentication failures\n */\n HMAC_INVALID:\n 'HMAC keyId=invalid-key ts=1234567890 nonce=invalid-nonce signature=invalid-signature'\n} as const;\n"],"mappings":"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","names":["getDatabasePort"]}
|
package/package.json
CHANGED