// ---------------------------------------------------------------------------
// DbService integration test
// ---------------------------------------------------------------------------
//
// Regression coverage for the pg/CloudflareSocket hang (see
// personal-notes/pg-cloudflare-sockets-dev.md). This test:
//
// - Runs inside the Cloudflare Workers runtime via
// @cloudflare/vitest-pool-workers
// - Talks to a real Postgres (PGlite exposed over a TCP socket by
// scripts/test-globalsetup.ts)
// - Constructs DbService.Live across multiple independent Effect scopes,
// the way api.ts does per request
// - Performs real queries against the mirrored accounts/organizations
// tables through drizzle
//
// With the old `pg` + `drizzle-orm/node-postgres` stack, the second scope's
// query would hang indefinitely because CloudflareSocket cannot be reused
// across request contexts and `Client.end()` never resolved. With
// postgres.js the test passes: each scope acquires its own socket and
// releases it cleanly.
import { describe, it, expect } from "@effect/vitest";
import { Effect, Layer } from "effect";
import { isValidOrgSlug } from "@executor-js/api";
import { DbService } from "./db";
import { makeUserStore } from "../auth/user-store";
const program = (body: Effect.Effect) =>
Effect.runPromise(
body.pipe(Effect.provide(DbService.Live), Effect.scoped) as Effect.Effect,
);
describe("DbService", () => {
it("executes a trivial query end-to-end", async () => {
const result = await program(
Effect.gen(function* () {
const { db } = yield* DbService;
const rows = yield* Effect.promise(() => db.execute("select 1 as one"));
return rows;
}),
);
expect(
Array.isArray(result) ? result[0] : (result as { rows: unknown[] }).rows[0],
).toBeDefined();
});
it("round-trips an account through the user store", async () => {
const id = `user_${crypto.randomUUID()}`;
const account = await program(
Effect.gen(function* () {
const { db } = yield* DbService;
const store = makeUserStore(db);
return yield* Effect.promise(() => store.ensureAccount(id));
}),
);
expect(account.id).toBe(id);
});
it("supports multiple sequential scopes (regression: pg socket reuse hang)", async () => {
// With `pg`, the second scope's connect()/query would hang because the
// CloudflareSocket from the first scope cannot be reused and Client.end()
// never completes. postgres.js creates a fresh socket per scope.
const idA = `user_${crypto.randomUUID()}`;
const idB = `user_${crypto.randomUUID()}`;
await program(
Effect.gen(function* () {
const { db } = yield* DbService;
yield* Effect.promise(() => makeUserStore(db).ensureAccount(idA));
}),
);
const fetched = await program(
Effect.gen(function* () {
const { db } = yield* DbService;
const store = makeUserStore(db);
yield* Effect.promise(() => store.ensureAccount(idB));
return yield* Effect.promise(() => store.getAccount(idA));
}),
);
expect(fetched?.id).toBe(idA);
}, 15_000);
it("supports nested scopes within a single outer scope (regression: /api/scope pattern)", async () => {
// Mirrors api.ts: an outer scope resolves the org, then an inner scope
// (the HttpApi request handler) re-acquires DbService and queries again.
const organizationId = `org_${crypto.randomUUID()}`;
const outer = Layer.provide(
Layer.effectDiscard(
Effect.gen(function* () {
const { db } = yield* DbService;
yield* Effect.promise(() =>
makeUserStore(db).upsertOrganization({
id: organizationId,
name: "Acme",
updatedAt: new Date(),
}),
);
}),
),
DbService.Live,
);
const result = await Effect.runPromise(
Effect.gen(function* () {
// Outer "request" scope.
yield* Effect.scoped(Layer.build(outer).pipe(Effect.asVoid));
// Inner "handler" scope — fresh DbService.
return yield* Effect.scoped(
Effect.gen(function* () {
const { db } = yield* DbService;
return yield* Effect.promise(() => makeUserStore(db).getOrganization(organizationId));
}).pipe(Effect.provide(DbService.Live)),
) as Effect.Effect<{ id: string; name: string } | null, never, never>;
}) as Effect.Effect<{ id: string; name: string } | null, never, never>,
);
expect(result?.id).toBe(organizationId);
expect(result?.name).toBe("Acme");
}, 15_000);
});
describe("upsertOrganization · slug is minted at insert", () => {
const upsert = (org: { id: string; name: string; updatedAt?: Date }) =>
program(
Effect.gen(function* () {
const { db } = yield* DbService;
return yield* Effect.promise(() =>
makeUserStore(db).upsertOrganization({
updatedAt: new Date(),
...org,
}),
);
}),
);
it("mints a valid slug on insert", async () => {
const org = await upsert({
id: `org_${crypto.randomUUID()}`,
name: "Slug Mint Co",
});
expect(org.slug, "a new org row is born with a slug").toBeTruthy();
expect(isValidOrgSlug(org.slug), "the minted slug fits the URL grammar").toBe(true);
});
it("keeps the slug stable across renames (conflict updates name only)", async () => {
const id = `org_${crypto.randomUUID()}`;
const created = await upsert({ id, name: "Original Name" });
const renamed = await upsert({ id, name: "Renamed Org" });
expect(renamed.slug, "the slug survives a rename").toBe(created.slug);
expect(renamed.name, "the name is refreshed on conflict").toBe("Renamed Org");
});
it("refuses a name stamped earlier than the one it holds, and never renames a deleted org", async () => {
const id = `org_${crypto.randomUUID()}`;
const t1 = new Date("2026-01-01T00:00:00.000Z");
const t2 = new Date("2026-01-02T00:00:00.000Z");
const t3 = new Date("2026-01-03T00:00:00.000Z");
await upsert({ id, name: "Original Name", updatedAt: t2 });
// A payload fetched before the rename landed (a login that stalled).
const stale = await upsert({ id, name: "Stale Name", updatedAt: t1 });
expect(stale.name, "an older name never reverts a newer one").toBe("Original Name");
const replay = await upsert({ id, name: "Replayed Name", updatedAt: t2 });
expect(replay.name, "the same instant is accepted, so replays converge").toBe("Replayed Name");
await program(
Effect.gen(function* () {
const { db } = yield* DbService;
yield* Effect.promise(() => makeUserStore(db).deleteOrganizationCascade(id, t3));
}),
);
const afterDelete = await upsert({
id,
name: "Resurrected Name",
updatedAt: t3,
});
expect(afterDelete.deletedAt, "a deleted org stays deleted").toEqual(t3);
expect(afterDelete.name, "and keeps its last name").toBe("Replayed Name");
});
it("discriminates same-name collisions into distinct slugs", async () => {
// Same name → same slug base; the second insert collides on the unique
// index and gets a discriminated slug.
const name = `Collide ${crypto.randomUUID().slice(0, 8)}`;
const a = await upsert({ id: `org_${crypto.randomUUID()}`, name });
const b = await upsert({ id: `org_${crypto.randomUUID()}`, name });
expect(a.slug).not.toBe(b.slug);
expect(isValidOrgSlug(a.slug) && isValidOrgSlug(b.slug), "both slugs are valid").toBe(true);
});
});