mirror of
https://github.com/VibedByKaKi/t3-code-android-nightly.git
synced 2026-10-11 21:01:16 +02:00
137 lines
4.4 KiB
TypeScript
137 lines
4.4 KiB
TypeScript
import * as Cloudflare from "@/Cloudflare";
|
|
import * as Drizzle from "@/Drizzle";
|
|
import * as Test from "@/Test/Alchemy";
|
|
import { expect } from "alchemy-test";
|
|
import * as Data from "effect/Data";
|
|
import * as Effect from "effect/Effect";
|
|
import * as Layer from "effect/Layer";
|
|
import * as Schedule from "effect/Schedule";
|
|
import * as HttpClient from "effect/http/HttpClient";
|
|
import * as HttpClientRequest from "effect/http/HttpClientRequest";
|
|
import type * as HttpClientResponse from "effect/http/HttpClientResponse";
|
|
import Stack from "./fixtures/drizzle-stack.ts";
|
|
|
|
const { test, beforeAll, afterAll, deploy, destroy } = Test.make({
|
|
providers: Layer.mergeAll(Cloudflare.providers(), Drizzle.providers()),
|
|
state: Cloudflare.state(),
|
|
});
|
|
|
|
const HOOK_TIMEOUT = 300_000;
|
|
const TEST_TIMEOUT = 120_000;
|
|
|
|
class WorkerNotReady extends Data.TaggedError("WorkerNotReady")<{
|
|
status: number;
|
|
body: string;
|
|
}> {}
|
|
|
|
// Bounded spaced schedule — rides out fresh-workers.dev cold-start
|
|
// propagation without blowing past the test timeout.
|
|
const ready = Schedule.max([Schedule.spaced("2 seconds"), Schedule.recurs(45)]);
|
|
|
|
/** Retry an HTTP call until it returns 200. */
|
|
const untilOk = <E, R>(
|
|
eff: Effect.Effect<HttpClientResponse.HttpClientResponse, E, R>,
|
|
) =>
|
|
eff.pipe(
|
|
Effect.flatMap((res) =>
|
|
res.status === 200
|
|
? Effect.succeed(res)
|
|
: res.text.pipe(
|
|
Effect.flatMap((body) =>
|
|
Effect.fail(new WorkerNotReady({ status: res.status, body })),
|
|
),
|
|
),
|
|
),
|
|
Effect.retry({
|
|
while: (e): e is WorkerNotReady => e instanceof WorkerNotReady,
|
|
schedule: ready,
|
|
}),
|
|
);
|
|
|
|
/**
|
|
* End-to-end test of the Drizzle-on-D1 surface against a real Cloudflare
|
|
* Worker + D1 database:
|
|
*
|
|
* - deploy applies the checked-in migration (generated by drizzle-kit from
|
|
* `fixtures/drizzle-schema.ts`) via `migrationsDir` into the
|
|
* `drizzle_migrations` table;
|
|
* - `Drizzle.D1(d1, { relations })` drives insert/select/relational queries
|
|
* through the drizzle-orm `effect-d1` driver;
|
|
* - `SQL.D1(d1)` drives raw tagged-template SQL through `@effect/sql-d1`.
|
|
*/
|
|
const stack = beforeAll(deploy(Stack), { timeout: HOOK_TIMEOUT });
|
|
afterAll.skipIf(!!process.env.NO_DESTROY)(destroy(Stack), {
|
|
timeout: HOOK_TIMEOUT,
|
|
});
|
|
|
|
test(
|
|
"Drizzle.D1 + SQL.D1 exercise the deployed database",
|
|
Effect.gen(function* () {
|
|
const { url } = yield* stack;
|
|
|
|
// drizzle insert ... returning
|
|
const userRes = yield* untilOk(
|
|
HttpClient.execute(
|
|
HttpClientRequest.post(`${url}/users`).pipe(
|
|
HttpClientRequest.bodyJsonUnsafe({
|
|
name: "alice",
|
|
email: "alice@example.com",
|
|
}),
|
|
),
|
|
),
|
|
);
|
|
const { user } = (yield* userRes.json) as {
|
|
user: { id: number; name: string; email: string };
|
|
};
|
|
expect(user.name).toBe("alice");
|
|
expect(user.email).toBe("alice@example.com");
|
|
expect(typeof user.id).toBe("number");
|
|
|
|
const postRes = yield* untilOk(
|
|
HttpClient.execute(
|
|
HttpClientRequest.post(`${url}/posts`).pipe(
|
|
HttpClientRequest.bodyJsonUnsafe({
|
|
userId: user.id,
|
|
title: "hello world",
|
|
}),
|
|
),
|
|
),
|
|
);
|
|
const { post } = (yield* postRes.json) as {
|
|
post: { id: number; userId: number; title: string };
|
|
};
|
|
expect(post.userId).toBe(user.id);
|
|
expect(post.title).toBe("hello world");
|
|
|
|
// drizzle select
|
|
const listRes = yield* untilOk(HttpClient.get(`${url}/users`));
|
|
const { users } = (yield* listRes.json) as {
|
|
users: Array<{ id: number; name: string }>;
|
|
};
|
|
expect(users.map((u) => u.name)).toContain("alice");
|
|
|
|
// drizzle relational query (RQB v2): user with posts
|
|
const relRes = yield* untilOk(HttpClient.get(`${url}/users/${user.id}`));
|
|
const rel = (yield* relRes.json) as {
|
|
user: { id: number; posts: Array<{ title: string }> };
|
|
};
|
|
expect(rel.user.id).toBe(user.id);
|
|
expect(rel.user.posts.map((p) => p.title)).toContain("hello world");
|
|
|
|
// raw @effect/sql-d1 tagged-template client
|
|
const sqlRes = yield* untilOk(HttpClient.get(`${url}/sql/users`));
|
|
const { rows } = (yield* sqlRes.json) as {
|
|
rows: Array<{ id: number; name: string; email: string }>;
|
|
};
|
|
expect(rows.map((r) => r.email)).toContain("alice@example.com");
|
|
}),
|
|
{
|
|
tags: [
|
|
"provider:cloudflare",
|
|
"provider:cloudflare:d1",
|
|
"provider:cloudflare:worker",
|
|
"live",
|
|
],
|
|
timeout: TEST_TIMEOUT,
|
|
},
|
|
);
|