1import * as Cloudflare from "alchemy/Cloudflare";
2import * as Test from "alchemy/Test/Bun";
3import { expect } from "bun:test";
4import * as Effect from "effect/Effect";
5import * as FileSystem from "effect/FileSystem";
6import * as Path from "effect/Path";
7import * as Schedule from "effect/Schedule";
8import Stack from "../alchemy.run.ts";
9
10const { test, beforeAll, afterAll, deploy, destroy } = Test.make({
11 providers: Cloudflare.providers(),
12 profile: process.env.ALCHEMY_PROFILE,
13 dev: true,
14});
15const stack = beforeAll(destroy(Stack).pipe(Effect.andThen(deploy(Stack))));
16afterAll(destroy(Stack));
17
18test(
19 "native vinext development serves bindings and hot reload",
20 Effect.gen(function* () {
21 const { url } = yield* stack;
22 expect(url).toMatch(/^http:\/\/(localhost|127\.0\.0\.1):\d+/);
23 const home = yield* Test.getWhenReady(String(url)).pipe(
24 Effect.flatMap((response) => response.text),
25 );
26 expect(home).toContain("Hello from vinext on Cloudflare!");
27 const api = yield* Test.getWhenReady(`${url}/api/hello`);
28 expect(yield* api.json).toEqual({
29 message: "Hello from vinext on Cloudflare!",
30 noteCount: 0,
31 });
32 for (const route of [
33 "/static",
34 "/isr",
35 "/use-cache",
36 "/notes",
37 "/robots.txt",
38 ]) {
39 expect((yield* Test.getWhenReady(`${url}${route}`)).status).toBe(200);
40 }
41 expect((yield* Test.getWhenReady(`${url}/admin`)).status).toBe(403);
42 const fs = yield* FileSystem.FileSystem;
43 const path = yield* Path.Path;
44 const page = yield* path.fromFileUrl(
45 new URL("../app/page.tsx", import.meta.url),
46 );
47 const source = yield* fs.readFileString(page);
48 const marker = "Updated by the vinext development test";
49 yield* Effect.acquireUseRelease(
50 fs.writeFileString(
51 page,
52 source.replace('title="SSR"', `title="${marker}"`),
53 ),
54 () =>
55 Test.getWhenReady(String(url)).pipe(
56 Effect.flatMap((response) => response.text),
57 Effect.repeat({
58 schedule: Schedule.spaced("500 millis"),
59 times: 20,
60 until: (body) => body.includes(marker),
61 }),
62 Effect.tap((body) =>
63 Effect.sync(() => expect(body).toContain(marker)),
64 ),
65 ),
66 () => fs.writeFileString(page, source).pipe(Effect.orDie),
67 );
68 }),
69 { timeout: 120_000 },
70);