1import * as Cloudflare from "@/Cloudflare";
2import * as Neon from "@/Neon";
3import * as Test from "@/Test/Alchemy";
4import { expect } from "alchemy-test";
5import * as Effect from "effect/Effect";
6import * as Layer from "effect/Layer";
7import { exerciseSqlSurface, postJson, putJson } from "./exercise.ts";
8import type { UserRow } from "./fixtures/routes.ts";
9import Stack from "./fixtures/postgres-stack.ts";
10
11const { test, beforeAll, afterAll, deploy, destroy } = Test.make({
12 providers: Layer.merge(Cloudflare.providers(), Neon.providers()),
13 state: Cloudflare.state(),
14});
15
16const HOOK_TIMEOUT = 300_000;
17const TEST_TIMEOUT = 120_000;
18
19/**
20 * End-to-end test of `SQL.Postgres` — the raw `@effect/sql-pg` client
21 * wrapped in alchemy's deferred `proxyChain` — against a real Cloudflare
22 * Worker fronting a Neon Postgres origin through Hyperdrive (caching
23 * disabled for read-after-write assertions).
24 *
25 * Runs the same shared exercise as the D1 suite, plus the Postgres-only
26 * surface: `withTransaction` (commit + rollback) and multi-row
27 * `sql.updateValues`.
28 */
29const stack = beforeAll(deploy(Stack), { timeout: HOOK_TIMEOUT });
30afterAll.skipIf(!!process.env.NO_DESTROY)(destroy(Stack), {
31 timeout: HOOK_TIMEOUT,
32});
33
34test(
35 "SQL.Postgres exercises the full client surface over a deployed Worker",
36 Effect.gen(function* () {
37 const { url } = yield* stack;
38 yield* exerciseSqlSurface(url);
39
40 // withTransaction — both inserts commit atomically.
41 const dave: UserRow = { id: 10, name: "dave", email: "dave@example.com" };
42 const erin: UserRow = { id: 11, name: "erin", email: "erin@example.com" };
43 const committed = (yield* postJson(`${url}/tx/commit`, [dave, erin])) as {
44 rows: UserRow[];
45 };
46 expect(committed.rows).toEqual([dave, erin]);
47
48 // withTransaction — a failing effect rolls the insert back.
49 const rolledBack = (yield* postJson(`${url}/tx/rollback`, {
50 id: 12,
51 name: "frank",
52 email: "frank@example.com",
53 })) as { error: string; rows: unknown[] };
54 expect(rolledBack.error).toBe("Rollback");
55 expect(rolledBack.rows).toEqual([]);
56
57 // sql.updateValues — multi-row update through a VALUES join.
58 const daveV2: UserRow = {
59 id: 10,
60 name: "david",
61 email: "david@example.com",
62 };
63 const erinV2: UserRow = {
64 id: 11,
65 name: "erina",
66 email: "erina@example.com",
67 };
68 const updated = (yield* putJson(`${url}/users/values`, [
69 daveV2,
70 erinV2,
71 ])) as { rows: UserRow[] };
72 expect(updated.rows).toEqual([daveV2, erinV2]);
73 }),
74 {
75 tags: [
76 "provider:cloudflare",
77 "provider:cloudflare:hyperdrive",
78 "provider:cloudflare:worker",
79 "provider:neon",
80 "provider:neon:project",
81 "live",
82 ],
83 timeout: TEST_TIMEOUT,
84 },
85);