vitest icon indicating copy to clipboard operation
vitest copied to clipboard

Adding custom watch commands

Open danilofuchs opened this issue 1 year ago • 1 comments

Clear and concise description of the problem

We use testcontainers to run our integration tests with a live PostgreSQL database, in 5 parallel workers

All these DBs must be migrated using Prisma before starting vitest --watch, we do this in our global setup:

import { PostgreSqlContainer } from "@testcontainers/postgresql";
import { exec as execCb } from "child_process";
import { promisify } from "util";
import type { GlobalSetupContext } from "vitest/node";

const exec = promisify(execCb);

export default async function setup({ provide, config }: GlobalSetupContext) {
  const stopsFunctions: (() => Promise<void>)[] = [];
  const postgresUrls: Record<number, string> = {};

  await prismaGenerate();

  await Promise.all(
    [...Array(config.maxWorkers).keys()].map(async (index) => {
      const postgres = await buildPostgresContainer();
      postgresUrls[index + 1] = postgres.url;
      stopsFunctions.push(postgres.stop);
    }),
  );

  provide("postgresUrls", postgresUrls);

  return async () => {
    await Promise.all(
      stopsFunctions.map(async (stop) => {
        await stop();
      }),
    );
  };
}

const prismaGenerate = async () => {
  console.log("[Prisma] Generating...");
  await exec(`npx prisma generate`);
  console.log("[Prisma] Generated successfully!");
};

const buildPostgresContainer = async () => {
  console.log("[Postgres] Starting...");
  const postgres = await new PostgreSqlContainer("postgres:16")
    .withTmpFs({ "/dev/shm/postgres": "rw,noexec,nosuid,size=65536k" })
    // https://www.postgresql.org/docs/current/non-durability.html
    .withCommand([
      "postgres",
      "-c",
      "fsync=off",
      "-c",
      "synchronous_commit=off",
      "-c",
      "full_page_writes=off",
      "-c",
      "autovacuum=off",
    ])
    .start();

  const url = postgres.getConnectionUri();

  console.log("[Postgres] Migrating...");
  await exec(`DB_URL=${url} npx prisma migrate deploy`);
  console.log("[Postgres] Migrated successfully!");

  return {
    url,
    stop: async () => {
      await postgres.stop();
    },
  };
};

// Type safe access to `provide/inject` methods:
declare module "vitest" {
  export interface ProvidedContext {
    postgresUrls: Record<number, string>;
  }
}

If I write a new migration, it is not applied to my live test databases, I must end the Vitest process and start again (in this case, this takes ~30s because of all the containers)

Suggested solution

Would be nice if we could add a new quick command to the watch menu, such as m for triggering migrations inside the databases

Alternative

We could setup a parallel watching process which runs the migrations every time the migrations folder changes, but that seems flaky

Perhaps related to https://github.com/vitest-dev/vitest/issues/842

Additional context

No response

Validations

danilofuchs avatar Aug 28 '24 18:08 danilofuchs

I personally don't like extending the watch commands API. If you want to do something custom, you can always create your own script and run it in the terminal. But for this specific use case, maybe we can add something like onWatcherRerun to the globalSetup:

export default ({ onWatcherRerun }) => {
  onWatcherRerun(async () => {
    // do this before running tests
  })
}

sheremet-va avatar Sep 09 '24 06:09 sheremet-va