1import * as GCP from "@/GCP";
2import * as Test from "@/Test/Alchemy";
3import * as compute from "@distilled.cloud/gcp/compute_v1";
4import { expect } from "alchemy-test";
5import * as Effect from "effect/Effect";
6import { MinimumLogLevel } from "effect/References";
7import * as Schedule from "effect/Schedule";
8
9const { test } = Test.make({ providers: GCP.providers() });
10
11const logLevel = Effect.provideService(
12 MinimumLogLevel,
13 process.env.DEBUG ? "Debug" : "Info",
14);
15
16const waitUntilGone = (project: string, healthCheckName: string) =>
17 compute.getHealthChecks({ project, healthCheck: healthCheckName }).pipe(
18 Effect.as("found" as const),
19 Effect.catchTag("NotFound", () => Effect.succeed("gone" as const)),
20 Effect.repeat({
21 schedule: Schedule.spaced("1 second"),
22 until: (status) => status === "gone",
23 times: 10,
24 }),
25 );
26
27test.provider(
28 "create, update, and delete a health check",
29 (stack) =>
30 Effect.gen(function* () {
31 yield* stack.destroy();
32
33 const created = yield* stack.deploy(
34 Effect.gen(function* () {
35 return yield* GCP.Compute.HealthCheck("Api", {
36 description: "frontend probe",
37 httpHealthCheck: { port: 80, requestPath: "/health" },
38 });
39 }),
40 );
41
42 expect(created.healthCheckName).toEqual(expect.any(String));
43 expect(created.type).toEqual("HTTP");
44 expect(created.description).toEqual("frontend probe");
45 expect(created.httpHealthCheck?.requestPath).toEqual("/health");
46 expect(created.checkIntervalSec).toEqual(5);
47
48 const fetched = yield* compute.getHealthChecks({
49 project: created.project,
50 healthCheck: created.healthCheckName,
51 });
52 expect(fetched.name).toEqual(created.healthCheckName);
53 expect(fetched.type).toEqual("HTTP");
54 expect(fetched.httpHealthCheck?.requestPath).toEqual("/health");
55 expect(fetched.description).toContain("[alchemy ");
56 expect(fetched.description).toContain("frontend probe");
57
58 const updated = yield* stack.deploy(
59 Effect.gen(function* () {
60 return yield* GCP.Compute.HealthCheck("Api", {
61 healthCheckName: created.healthCheckName,
62 description: "ready probe",
63 checkIntervalSec: 10,
64 timeoutSec: 5,
65 healthyThreshold: 3,
66 httpHealthCheck: { port: 80, requestPath: "/ready" },
67 });
68 }),
69 );
70
71 expect(updated.healthCheckName).toEqual(created.healthCheckName);
72 expect(updated.type).toEqual("HTTP");
73 expect(updated.description).toEqual("ready probe");
74 expect(updated.checkIntervalSec).toEqual(10);
75 expect(updated.healthyThreshold).toEqual(3);
76 expect(updated.httpHealthCheck?.requestPath).toEqual("/ready");
77
78 const refetched = yield* compute.getHealthChecks({
79 project: updated.project,
80 healthCheck: updated.healthCheckName,
81 });
82 expect(refetched.type).toEqual("HTTP");
83 expect(refetched.httpHealthCheck?.requestPath).toEqual("/ready");
84 expect(refetched.checkIntervalSec).toEqual(10);
85
86 const replaced = yield* stack.deploy(
87 Effect.gen(function* () {
88 return yield* GCP.Compute.HealthCheck("Api", {
89 healthCheckName: created.healthCheckName,
90 description: "tcp probe",
91 type: "TCP",
92 tcpHealthCheck: { port: 8080 },
93 });
94 }),
95 );
96
97 expect(replaced.healthCheckName).toEqual(created.healthCheckName);
98 expect(replaced.type).toEqual("TCP");
99 expect(replaced.description).toEqual("tcp probe");
100 expect(replaced.tcpHealthCheck?.port).toEqual(8080);
101
102 const afterReplace = yield* compute.getHealthChecks({
103 project: replaced.project,
104 healthCheck: replaced.healthCheckName,
105 });
106 expect(afterReplace.type).toEqual("TCP");
107 expect(afterReplace.tcpHealthCheck?.port).toEqual(8080);
108 expect(afterReplace.httpHealthCheck).toBeUndefined();
109
110 yield* stack.destroy();
111
112 const gone = yield* waitUntilGone(
113 created.project,
114 created.healthCheckName,
115 );
116 expect(gone).toEqual("gone");
117 }).pipe(logLevel),
118 { tags: ["provider:gcp", "provider:gcp:compute", "live"], timeout: 90_000 },
119);