1import * as Alchemy from "alchemy";
2import * as Fly from "alchemy/Fly";
3import * as Test from "alchemy/Test/Bun";
4import { expect } from "bun:test";
5import * as Effect from "effect/Effect";
6import * as Schedule from "effect/Schedule";
7import * as HttpClient from "effect/http/HttpClient";
8import Stack from "../alchemy.run.ts";
9import { USERS } from "../src/users.ts";
10
11const { test, beforeAll, afterAll, deploy, destroy } = Test.make({
12 providers: Fly.providers(),
13 state: Alchemy.localState(),
14 profile: process.env.ALCHEMY_PROFILE,
15});
16
17const getJson = (url: string) =>
18 HttpClient.get(url).pipe(
19 Effect.flatMap((res) =>
20 res.status === 200
21 ? res.json
22 : Effect.fail(new Error(`HTTP ${res.status}`)),
23 ),
24 Effect.retry({ schedule: Schedule.exponential("500 millis"), times: 20 }),
25 );
26
27const stack = beforeAll(deploy(Stack), { timeout: 400_000 });
28
29afterAll.skipIf(!!process.env.NO_DESTROY)(destroy(Stack), {
30 timeout: 240_000,
31});
32
33test(
34 "the public gateway reaches private Services over the stack network",
35 Effect.gen(function* () {
36 const out = yield* stack;
37 expect(out.url).toMatch(/^https:\/\/.+\.fly\.dev$/);
38 expect(out.usersUrl).toBeUndefined();
39 expect(out.usersPrivateUrl).toMatch(/^http:\/\/.+\.flycast$/);
40 expect(out.ordersPrivateUrl).toMatch(/^http:\/\/.+\.flycast$/);
41 expect(out.network).toBeString();
42
43 expect((yield* getJson(`${out.url}/users`)) as unknown).toEqual(USERS);
44 const orders = (yield* getJson(`${out.url}/orders`)) as Array<{
45 user: { name: string };
46 }>;
47 expect(orders.map((order) => order.user.name)).toEqual(["Ada", "Grace"]);
48 }),
49 { timeout: 240_000 },
50);