1import * as machines from "@distilled.cloud/fly-io/machines";
2import * as Fly from "@/Fly";
3import type { MachineProps } from "@/Fly/Machine";
4import type { ScratchStack } from "@/Test/Alchemy";
5import { expect } from "alchemy-test";
6import * as Effect from "effect/Effect";
7import * as Schedule from "effect/Schedule";
8import * as FetchHttpClient from "effect/http/FetchHttpClient";
9
10export const checks = {
11 ready: {
12 type: "http" as const,
13 port: 80,
14 path: "/",
15 interval: "2s",
16 timeout: "1s",
17 },
18};
19
20export const deployWorker = (
21 stack: ScratchStack,
22 version: string,
23 props: Partial<Omit<Extract<MachineProps, { image: string }>, "app">> = {},
24) =>
25 stack.deploy(
26 Effect.gen(function* () {
27 const app = yield* Fly.App("Site");
28 return yield* Fly.Machine("Worker", {
29 app,
30 image: "nginx:alpine",
31 env: { VERSION: version },
32 checks,
33 deploy: { strategy: "bluegreen", healthTimeout: "20 seconds" },
34 shutdown: { signal: "SIGQUIT", timeout: "5 seconds" },
35 ...props,
36 });
37 }),
38 );
39
40export const census = (appName: string) =>
41 machines.listMachines({ app_name: appName }).pipe(
42 Effect.map((listed) =>
43 listed.filter((machine) => machine.state !== "destroyed"),
44 ),
45 Effect.provide(FetchHttpClient.layer),
46 );
47
48export const assertAppGone = (appName: string) =>
49 Effect.gen(function* () {
50 const gone = yield* machines.getApp({ app_name: appName }).pipe(
51 Effect.as(false),
52 Effect.catchTag("NotFound", () => Effect.succeed(true)),
53 Effect.repeat({
54 schedule: Schedule.spaced("1 second"),
55 times: 8,
56 until: (gone) => gone,
57 }),
58 Effect.provide(FetchHttpClient.layer),
59 );
60 expect(gone).toBe(true);
61 });
62
63export const assertCommitted = (appName: string, ids: string[]) =>
64 Effect.gen(function* () {
65 const live = yield* census(appName);
66 expect(live.map((machine) => machine.id).sort()).toEqual([...ids].sort());
67 expect(
68 live.every(
69 (machine) => machine.config?.metadata?.["alchemy.phase"] === "active",
70 ),
71 ).toBe(true);
72 expect(live.every((machine) => !!machine.image_ref?.digest)).toBe(true);
73 expect(new Set(live.map((machine) => machine.image_ref?.digest)).size).toBe(
74 1,
75 );
76 return live;
77 });