1import * as Alchemy from "alchemy";
2import * as Neon from "alchemy/Neon";
3import * as Test from "alchemy/Test/Bun";
4import { expect } from "bun:test";
5import * as Console from "effect/Console";
6import * as Effect from "effect/Effect";
7import Stack from "../alchemy.run.ts";
8
9const { test, beforeAll, afterAll, deploy, destroy } = Test.make({
10 providers: Neon.providers(),
11 state: Alchemy.localState(),
12 profile: process.env.ALCHEMY_PROFILE,
13});
14
15const stack = beforeAll(
16 destroy(Stack).pipe(Effect.andThen(deploy(Stack)), Effect.tap(Console.log)),
17 {
18 timeout: 120_000,
19 },
20);
21afterAll.skipIf(!!process.env.NO_DESTROY)(destroy(Stack), { timeout: 120_000 });
22
23const base = Effect.map(stack, ({ url }) => {
24 if (!url) throw new Error("Expected a deployed website URL");
25 return String(url).replace(/\/+$/, "");
26});
27
28test(
29 "serves the rendered home page",
30 Effect.gen(function* () {
31 const url = yield* base;
32 const response = yield* Test.getWhenReady(url);
33 expect(response.status).toBe(200);
34 expect(yield* response.text).toContain(
35 "Hello from TanStack Start on Neon!",
36 );
37 }),
38 { timeout: 120_000 },
39);
40
41test(
42 "serves a static asset without falling back to HTML",
43 Effect.gen(function* () {
44 const url = yield* base;
45 const response = yield* Test.getWhenReady(`${url}/example.json`);
46 expect(response.status).toBe(200);
47 expect(yield* response.json).toEqual({
48 framework: "TanStack Start",
49 greeting: "Hello from TanStack Start on Neon!",
50 });
51 }),
52 { timeout: 120_000 },
53);
54
55test(
56 "serves a request-dependent endpoint with runtime environment",
57 Effect.gen(function* () {
58 const url = yield* base;
59 for (const name of ["Neon", "Alchemy"]) {
60 const response = yield* Test.getWhenReady(
61 `${url}/api/hello?name=${name}`,
62 );
63 expect(response.status).toBe(200);
64 expect(yield* response.json).toEqual({
65 name,
66 greeting: "Hello from TanStack Start on Neon!",
67 });
68 }
69 }),
70 { timeout: 120_000 },
71);