import * as NodeServices from "@effect/platform-node/NodeServices";
import { describe, expect, it } from "@effect/vitest";
import * as NodeSqliteClient from "@t3tools/shared/nodeSqliteClient";
import * as Effect from "effect/Effect";
import * as FileSystem from "effect/FileSystem";
import * as Path from "effect/Path";
import * as Scope from "effect/Scope";
import * as SqlClient from "effect/unstable/sql/SqlClient";
import { snapshotCookieDatabase } from "./CookieDatabase.ts";
const runNode = (
effect: Effect.Effect,
) => effect.pipe(Effect.provide(NodeServices.layer), Effect.scoped);
describe("snapshotCookieDatabase", () => {
it.effect("includes committed WAL data in one consistent database", () =>
runNode(
Effect.gen(function* () {
const fileSystem = yield* FileSystem.FileSystem;
const path = yield* Path.Path;
const sourceDirectory = yield* fileSystem.makeTempDirectoryScoped({
prefix: "t3code-cookie-source-",
});
const source = path.join(sourceDirectory, "Cookies");
const snapshot = yield* Effect.gen(function* () {
const sql = yield* SqlClient.SqlClient;
yield* sql`PRAGMA journal_mode = WAL`;
yield* sql`PRAGMA wal_autocheckpoint = 0`;
yield* sql`CREATE TABLE cookies(name TEXT NOT NULL)`;
yield* sql`INSERT INTO cookies(name) VALUES (${"committed-in-wal"})`;
expect(yield* fileSystem.exists(`${source}-wal`)).toBe(true);
return yield* snapshotCookieDatabase(source);
}).pipe(Effect.provide(NodeSqliteClient.layer({ filename: source })));
const rows = yield* Effect.gen(function* () {
const sql = yield* SqlClient.SqlClient;
return yield* sql<{ readonly name: string }>`SELECT name FROM cookies`;
}).pipe(Effect.provide(NodeSqliteClient.layer({ filename: snapshot, readonly: true })));
expect(rows).toEqual([{ name: "committed-in-wal" }]);
}),
),
);
it.effect("propagates snapshot failures and removes its temporary directory", () =>
runNode(
Effect.gen(function* () {
const fileSystem = yield* FileSystem.FileSystem;
const path = yield* Path.Path;
const sourceDirectory = yield* fileSystem.makeTempDirectoryScoped({
prefix: "t3code-cookie-invalid-source-",
});
const source = path.join(sourceDirectory, "Cookies");
yield* fileSystem.writeFileString(source, "not a sqlite database");
const prefix = `t3code-cookie-failed-${process.pid}-`;
const error = yield* snapshotCookieDatabase(source, prefix).pipe(
Effect.scoped,
Effect.flip,
);
expect(error._tag).toBe("SqlError");
const temporaryEntries = yield* fileSystem.readDirectory(path.dirname(sourceDirectory));
expect(temporaryEntries.some((entry) => entry.startsWith(prefix))).toBe(false);
}),
),
);
it.effect("removes a successful snapshot when its scope closes", () =>
runNode(
Effect.gen(function* () {
const fileSystem = yield* FileSystem.FileSystem;
const path = yield* Path.Path;
const sourceDirectory = yield* fileSystem.makeTempDirectoryScoped({
prefix: "t3code-cookie-cleanup-source-",
});
const source = path.join(sourceDirectory, "Cookies");
yield* Effect.gen(function* () {
const sql = yield* SqlClient.SqlClient;
yield* sql`CREATE TABLE cookies(name TEXT NOT NULL)`;
}).pipe(Effect.provide(NodeSqliteClient.layer({ filename: source })));
const snapshot = yield* snapshotCookieDatabase(source).pipe(Effect.scoped);
expect(yield* fileSystem.exists(snapshot)).toBe(false);
}),
),
);
});