cloudflare / workers-sdk

⛅️ Home to Wrangler, the CLI for Cloudflare Workers®
https://developers.cloudflare.com/workers/
Apache License 2.0
2.6k stars 674 forks source link

[Miniflare] unit testing environment d1 migrations support #4377

Open tyomo4ka opened 1 year ago

tyomo4ka commented 1 year ago

I'm trying to figure out how to apply migrations to d1 database using jest-environment-miniflare.

I have a config like this:

  testEnvironment: 'miniflare',
  testEnvironmentOptions: {
    ...
    d1Databases: ['__D1_BETA__DB'],
    ...
  }

And it works perfectly fine for my app. But the DB is created empty.

Of course, I can manually run DDL queries to create required tables. However, it will be amazing if there will be native migrations support. E.g. via providing path to the migrations directory.

Something like this:

  testEnvironment: 'miniflare',
  testEnvironmentOptions: {
    ...
    d1Databases: ['__D1_BETA__DB'],
    d1MigrationsDir: "./migrations",
    ...
  }

If miniflare can then apply all migrations as wrangler does it - it will be fantastic!

mrbbot commented 1 year ago

Hey! 👋 Thanks for the feature suggestion! Definitely something we'll consider as we rethink the testing environments with Miniflare 3. 👍

kosciak9 commented 1 year ago

just a heads up, we used to do this (we have disabled this sort of testing since we upgraded to wrangler 3):

/* tests.ts */
import fs from "fs";
import { Kysely } from "kysely";
import { D1Dialect } from "kysely-d1";
import path from "path";

import type { DB } from "@/types/database";

export const setupDatabase = async (database: D1Database) => {
  const db = new Kysely<DB>({
    dialect: new D1Dialect({ database }),
  });

  const migrationDir = "./migrations";

  // List migration files in the directory
  const files = fs.readdirSync(migrationDir);

  // Filter out any non-SQL files
  const sqlFiles = files.filter((file) => path.extname(file) === ".sql");

  // Sort files by filename
  // eslint-disable-next-line @typescript-eslint/require-array-sort-compare
  const sortedFiles = sqlFiles.sort();

  // Read file contents and add to an array
  const fileContents = sortedFiles.map((file) => {
    const filePath = path.join(migrationDir, file);
    const fileContent = fs.readFileSync(filePath, "utf8");
    return fileContent
      .split("\n")
      .map((line) => line.replace(/--.*$/g, "").trim())
      .filter((line) => line !== "")
      .join(" ");
  });

  for (const migration of fileContents) {
    await database.exec(migration);
  }

  return db;
};
/* testFile.test.ts */

import { expect, it } from "vitest";

import { setupDatabase } from "@/tests/index";

import { list } from "./list";

const describe = setupMiniflareIsolatedStorage();
const bindings = getMiniflareBindings();

describe("test", () => {
  it("returns empty list for empty database", async () => {
    const { __D1_BETA__firmowid: database } = bindings;
    const db = await setupDatabase(database as D1Database);

    const result = await list({ db });

    expect(result).toMatchObject([]);
  });
});

until the update, it worked quite well :)

mrbbot commented 10 months ago

Hey! 👋 Just as an update, we're planning on revamping the Vitest environment for Miniflare 3 soon, and support for D1 migrations/seeding data is something we'll consider.