@forklaunch/testing 0.0.21 → 0.0.23

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.js CHANGED
@@ -361,33 +361,28 @@ function getDatabasePort2(type) {
361
361
  }
362
362
  }
363
363
  async function setupTestORM(config) {
364
- const {
365
- mikroOrmConfig,
366
- databaseType,
367
- useMigrations = false,
368
- container
369
- } = config;
370
- const dbPort = getDatabasePort2(databaseType);
371
- let ormConfig = {};
364
+ const { mikroOrmConfig, databaseType, useMigrations = false, container } = config;
365
+ const migrationOrSchemaConfig = useMigrations ? {
366
+ migrations: {
367
+ path: config.migrationsPath,
368
+ glob: "!(*.d).{js,ts}",
369
+ dropTables: true
370
+ }
371
+ } : {
372
+ schemaGenerator: {
373
+ createForeignKeyConstraints: false
374
+ }
375
+ };
376
+ let ormConfig;
372
377
  if (databaseType === "sqlite" || databaseType === "better-sqlite" || databaseType === "libsql") {
373
378
  ormConfig = {
374
379
  ...mikroOrmConfig,
375
380
  dbName: ":memory:",
376
- // In-memory SQLite for tests
377
381
  debug: false,
378
- ...useMigrations ? {
379
- migrations: {
380
- path: config.migrationsPath,
381
- glob: "!(*.d).{js,ts}",
382
- dropTables: true
383
- }
384
- } : {
385
- schemaGenerator: {
386
- createForeignKeyConstraints: false
387
- }
388
- }
382
+ ...migrationOrSchemaConfig
389
383
  };
390
384
  } else if (container) {
385
+ const dbPort = getDatabasePort2(databaseType);
391
386
  ormConfig = {
392
387
  ...mikroOrmConfig,
393
388
  dbName: "test_db",
@@ -396,24 +391,18 @@ async function setupTestORM(config) {
396
391
  password: databaseType === "mssql" ? "Test_Password123!" : "test_password",
397
392
  port: container.getMappedPort(dbPort),
398
393
  debug: false,
399
- ...useMigrations ? {
400
- migrations: {
401
- path: config.migrationsPath,
402
- glob: "!(*.d).{js,ts}",
403
- dropTables: true
404
- }
405
- } : {
406
- schemaGenerator: {
407
- createForeignKeyConstraints: false
408
- }
409
- }
394
+ ...migrationOrSchemaConfig
410
395
  };
396
+ } else {
397
+ throw new Error(
398
+ `Database type '${databaseType}' requires a container, but none was provided`
399
+ );
411
400
  }
412
401
  const orm = await import_core.MikroORM.init(ormConfig);
413
402
  if (useMigrations) {
414
- await orm.getMigrator().up();
403
+ await orm.migrator.up();
415
404
  } else {
416
- await orm.getSchemaGenerator().createSchema();
405
+ await orm.schema.create();
417
406
  }
418
407
  return orm;
419
408
  }
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 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 if (databaseType) {\n const dbPort = getDatabasePort(databaseType);\n\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 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 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 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 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 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 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(options?: {\n orm?: MikroORM;\n redis?: Redis;\n}): Promise<void> {\n const { orm, redis } = options || {};\n\n if (redis) {\n await redis.flushall();\n }\n\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({\n orm: this.result.orm,\n redis: this.result.redis\n });\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 if (databaseType) {\n const dbPort = getDatabasePort(databaseType);\n\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 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 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 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 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 { mikroOrmConfig, databaseType, useMigrations = false, container } =\n config;\n\n const migrationOrSchemaConfig = 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 let ormConfig: Options;\n if (\n databaseType === 'sqlite' ||\n databaseType === 'better-sqlite' ||\n databaseType === 'libsql'\n ) {\n ormConfig = {\n ...mikroOrmConfig,\n dbName: ':memory:',\n debug: false,\n ...migrationOrSchemaConfig\n };\n } else if (container) {\n const dbPort = getDatabasePort(databaseType);\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 ...migrationOrSchemaConfig\n };\n } else {\n throw new Error(\n `Database type '${databaseType}' requires a container, but none was provided`\n );\n }\n\n const orm = await MikroORM.init(ormConfig);\n\n if (useMigrations) {\n await orm.migrator.up();\n } else {\n await orm.schema.create();\n }\n\n return orm;\n}\n\n/**\n * Clear all data from the test database and/or cache\n */\nexport async function clearTestDatabase(options?: {\n orm?: MikroORM;\n redis?: Redis;\n}): Promise<void> {\n const { orm, redis } = options || {};\n\n if (redis) {\n await redis.flushall();\n }\n\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({\n orm: this.result.orm,\n redis: this.result.redis\n });\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
@@ -320,33 +320,28 @@ function getDatabasePort2(type) {
320
320
  }
321
321
  }
322
322
  async function setupTestORM(config) {
323
- const {
324
- mikroOrmConfig,
325
- databaseType,
326
- useMigrations = false,
327
- container
328
- } = config;
329
- const dbPort = getDatabasePort2(databaseType);
330
- let ormConfig = {};
323
+ const { mikroOrmConfig, databaseType, useMigrations = false, container } = config;
324
+ const migrationOrSchemaConfig = useMigrations ? {
325
+ migrations: {
326
+ path: config.migrationsPath,
327
+ glob: "!(*.d).{js,ts}",
328
+ dropTables: true
329
+ }
330
+ } : {
331
+ schemaGenerator: {
332
+ createForeignKeyConstraints: false
333
+ }
334
+ };
335
+ let ormConfig;
331
336
  if (databaseType === "sqlite" || databaseType === "better-sqlite" || databaseType === "libsql") {
332
337
  ormConfig = {
333
338
  ...mikroOrmConfig,
334
339
  dbName: ":memory:",
335
- // In-memory SQLite for tests
336
340
  debug: false,
337
- ...useMigrations ? {
338
- migrations: {
339
- path: config.migrationsPath,
340
- glob: "!(*.d).{js,ts}",
341
- dropTables: true
342
- }
343
- } : {
344
- schemaGenerator: {
345
- createForeignKeyConstraints: false
346
- }
347
- }
341
+ ...migrationOrSchemaConfig
348
342
  };
349
343
  } else if (container) {
344
+ const dbPort = getDatabasePort2(databaseType);
350
345
  ormConfig = {
351
346
  ...mikroOrmConfig,
352
347
  dbName: "test_db",
@@ -355,24 +350,18 @@ async function setupTestORM(config) {
355
350
  password: databaseType === "mssql" ? "Test_Password123!" : "test_password",
356
351
  port: container.getMappedPort(dbPort),
357
352
  debug: false,
358
- ...useMigrations ? {
359
- migrations: {
360
- path: config.migrationsPath,
361
- glob: "!(*.d).{js,ts}",
362
- dropTables: true
363
- }
364
- } : {
365
- schemaGenerator: {
366
- createForeignKeyConstraints: false
367
- }
368
- }
353
+ ...migrationOrSchemaConfig
369
354
  };
355
+ } else {
356
+ throw new Error(
357
+ `Database type '${databaseType}' requires a container, but none was provided`
358
+ );
370
359
  }
371
360
  const orm = await MikroORM.init(ormConfig);
372
361
  if (useMigrations) {
373
- await orm.getMigrator().up();
362
+ await orm.migrator.up();
374
363
  } else {
375
- await orm.getSchemaGenerator().createSchema();
364
+ await orm.schema.create();
376
365
  }
377
366
  return orm;
378
367
  }
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 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 if (databaseType) {\n const dbPort = getDatabasePort(databaseType);\n\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 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 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 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 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 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 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(options?: {\n orm?: MikroORM;\n redis?: Redis;\n}): Promise<void> {\n const { orm, redis } = options || {};\n\n if (redis) {\n await redis.flushall();\n }\n\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({\n orm: this.result.orm,\n redis: this.result.redis\n });\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 if (databaseType) {\n const dbPort = getDatabasePort(databaseType);\n\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 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 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 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 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 { mikroOrmConfig, databaseType, useMigrations = false, container } =\n config;\n\n const migrationOrSchemaConfig = 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 let ormConfig: Options;\n if (\n databaseType === 'sqlite' ||\n databaseType === 'better-sqlite' ||\n databaseType === 'libsql'\n ) {\n ormConfig = {\n ...mikroOrmConfig,\n dbName: ':memory:',\n debug: false,\n ...migrationOrSchemaConfig\n };\n } else if (container) {\n const dbPort = getDatabasePort(databaseType);\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 ...migrationOrSchemaConfig\n };\n } else {\n throw new Error(\n `Database type '${databaseType}' requires a container, but none was provided`\n );\n }\n\n const orm = await MikroORM.init(ormConfig);\n\n if (useMigrations) {\n await orm.migrator.up();\n } else {\n await orm.schema.create();\n }\n\n return orm;\n}\n\n/**\n * Clear all data from the test database and/or cache\n */\nexport async function clearTestDatabase(options?: {\n orm?: MikroORM;\n redis?: Redis;\n}): Promise<void> {\n const { orm, redis } = options || {};\n\n if (redis) {\n await redis.flushall();\n }\n\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({\n orm: this.result.orm,\n redis: this.result.redis\n });\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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@forklaunch/testing",
3
- "version": "0.0.21",
3
+ "version": "0.0.23",
4
4
  "description": "Testing utilities for forklaunch-js blueprints including testcontainer management",
5
5
  "homepage": "https://github.com/forklaunch/forklaunch-js#readme",
6
6
  "bugs": {
@@ -28,20 +28,20 @@
28
28
  "lib/**"
29
29
  ],
30
30
  "dependencies": {
31
- "@mikro-orm/core": "^6.6.9",
31
+ "@mikro-orm/core": "^7.0.1",
32
32
  "ioredis": "^5.10.0",
33
33
  "testcontainers": "^11.12.0"
34
34
  },
35
35
  "devDependencies": {
36
36
  "@eslint/js": "^10.0.1",
37
- "@types/node": "^25.3.5",
38
- "@typescript/native-preview": "7.0.0-dev.20260306.1",
37
+ "@types/node": "^25.4.0",
38
+ "@typescript/native-preview": "7.0.0-dev.20260311.1",
39
39
  "globals": "^17.4.0",
40
40
  "prettier": "^3.8.1",
41
41
  "ts-node": "^10.9.2",
42
42
  "tsup": "^8.5.1",
43
43
  "typescript": "^5.9.3",
44
- "typescript-eslint": "^8.56.1",
44
+ "typescript-eslint": "^8.57.0",
45
45
  "vitest": "^4.0.18"
46
46
  },
47
47
  "peerDependencies": {