Ctrl K
ComunidadGuías…

vitest.globalSetup.ts

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
import { existsSync } from "node:fs";
import { fileURLToPath } from "node:url";
import { PostgreSqlContainer, type StartedPostgreSqlContainer } from "@testcontainers/postgresql";
import { sql } from "drizzle-orm";
import { drizzle } from "drizzle-orm/node-postgres";
import { migrate } from "drizzle-orm/node-postgres/migrator";
import { Pool } from "pg";
import type { TestProject } from "vitest/node";

// One Postgres container for the ENTIRE test run. We migrate a single template database
// once here; each test then clones it via CREATE DATABASE ... TEMPLATE (~15ms) instead of
// starting its own container and re-running every migration (~1.5s). Real Postgres, real
// migrations, real per-test isolation are all preserved (see CLAUDE.md: never mock the DB).
//
// The connection parts are handed to worker processes via Vitest's provide/inject channel;
// makeTestDb() (src/test/db.ts) reads them to clone and drop per-test databases.

const MIGRATIONS_FOLDER = fileURLToPath(new URL("./drizzle", import.meta.url));
const MIGRATIONS_JOURNAL = fileURLToPath(new URL("./drizzle/meta/_journal.json", import.meta.url));

export const TEMPLATE_DB = "wd_template";

export interface TestPgConnection {
  host: string;
  port: number;
  user: string;
  password: string;
  // The container's default database, used for admin DDL (CREATE/DROP DATABASE) since those
  // cannot run while connected to the database being created/dropped.
  adminDatabase: string;
  template: string;
}

// Typed provide/inject channel.
declare module "vitest" {
  interface ProvidedContext {
    testPg: TestPgConnection;
  }
}

let container: StartedPostgreSqlContainer | undefined;

export default async function setup(project: TestProject): Promise<() => Promise<void>> {
  // fsync/full_page_writes off is safe for a disposable test DB and meaningfully faster.
  container = await new PostgreSqlContainer("postgres:16-alpine")
    .withCommand([
      "postgres",
      "-c",
      "max_connections=300",
      "-c",
      "fsync=off",
      "-c",
      "full_page_writes=off",
    ])
    .start();

  const adminDatabase = container.getDatabase();
  const base = {
    host: container.getHost(),
    port: container.getPort(),
    user: container.getUsername(),
    password: container.getPassword(),
  };

  // Create the template database from the admin connection.
  const adminPool = new Pool({ ...base, database: adminDatabase, max: 2 });
  await adminPool.query(`CREATE DATABASE ${TEMPLATE_DB}`);
  await adminPool.end();

  // Enable required extensions, then apply real migrations, on the template only.
  const templatePool = new Pool({ ...base, database: TEMPLATE_DB, max: 2 });
  const templateDb = drizzle(templatePool);
  await templateDb.execute(sql`CREATE EXTENSION IF NOT EXISTS pgcrypto`);
  await templateDb.execute(sql`CREATE EXTENSION IF NOT EXISTS citext`);
  if (existsSync(MIGRATIONS_JOURNAL)) {
    await migrate(templateDb, { migrationsFolder: MIGRATIONS_FOLDER });
  }
  // MUST disconnect: CREATE DATABASE ... TEMPLATE fails if any session is attached to the template.
  await templatePool.end();

  project.provide("testPg", { ...base, adminDatabase, template: TEMPLATE_DB });

  return async () => {
    await container?.stop();
  };
}