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 = (
17 project: string,
18 region: string,
19 healthCheckName: string,
20) =>
21 compute
22 .getRegionHealthChecks({
23 project,
24 region,
25 healthCheck: healthCheckName,
26 })
27 .pipe(
28 Effect.as("found" as const),
29 Effect.catchTag("NotFound", () => Effect.succeed("gone" as const)),
30 Effect.repeat({
31 schedule: Schedule.spaced("1 second"),
32 until: (status) => status === "gone",
33 times: 10,
34 }),
35 );
36
37test.provider(
38 "create, update, and delete a region health check",
39 (stack) =>
40 Effect.gen(function* () {
41 yield* stack.destroy();
42
43 const created = yield* stack.deploy(
44 Effect.gen(function* () {
45 return yield* GCP.Compute.RegionHealthCheck("Api", {
46 region: "us-central1",
47 description: "frontend probe",
48 httpHealthCheck: { port: 80, requestPath: "/health" },
49 });
50 }),
51 );
52
53 expect(created.healthCheckName).toEqual(expect.any(String));
54 expect(created.region).toEqual("us-central1");
55 expect(created.type).toEqual("HTTP");
56 expect(created.description).toEqual("frontend probe");
57 expect(created.httpHealthCheck?.requestPath).toEqual("/health");
58 expect(created.checkIntervalSec).toEqual(5);
59
60 const fetched = yield* compute.getRegionHealthChecks({
61 project: created.project,
62 region: created.region,
63 healthCheck: created.healthCheckName,
64 });
65 expect(fetched.name).toEqual(created.healthCheckName);
66 expect(fetched.type).toEqual("HTTP");
67 expect(fetched.httpHealthCheck?.requestPath).toEqual("/health");
68 expect(fetched.description).toContain("[alchemy ");
69 expect(fetched.description).toContain("frontend probe");
70 expect(lastSegment(fetched.region)).toEqual("us-central1");
71
72 const updated = yield* stack.deploy(
73 Effect.gen(function* () {
74 return yield* GCP.Compute.RegionHealthCheck("Api", {
75 healthCheckName: created.healthCheckName,
76 region: "us-central1",
77 description: "ready probe",
78 checkIntervalSec: 10,
79 timeoutSec: 5,
80 healthyThreshold: 3,
81 httpHealthCheck: { port: 80, requestPath: "/ready" },
82 });
83 }),
84 );
85
86 expect(updated.healthCheckName).toEqual(created.healthCheckName);
87 expect(updated.region).toEqual("us-central1");
88 expect(updated.type).toEqual("HTTP");
89 expect(updated.description).toEqual("ready probe");
90 expect(updated.checkIntervalSec).toEqual(10);
91 expect(updated.healthyThreshold).toEqual(3);
92 expect(updated.httpHealthCheck?.requestPath).toEqual("/ready");
93
94 const refetched = yield* compute.getRegionHealthChecks({
95 project: updated.project,
96 region: updated.region,
97 healthCheck: updated.healthCheckName,
98 });
99 expect(refetched.type).toEqual("HTTP");
100 expect(refetched.httpHealthCheck?.requestPath).toEqual("/ready");
101 expect(refetched.checkIntervalSec).toEqual(10);
102
103 const replaced = yield* stack.deploy(
104 Effect.gen(function* () {
105 return yield* GCP.Compute.RegionHealthCheck("Api", {
106 healthCheckName: created.healthCheckName,
107 region: "us-central1",
108 description: "tcp probe",
109 type: "TCP",
110 tcpHealthCheck: { port: 8080 },
111 });
112 }),
113 );
114
115 expect(replaced.healthCheckName).toEqual(created.healthCheckName);
116 expect(replaced.type).toEqual("TCP");
117 expect(replaced.description).toEqual("tcp probe");
118 expect(replaced.tcpHealthCheck?.port).toEqual(8080);
119
120 const afterReplace = yield* compute.getRegionHealthChecks({
121 project: replaced.project,
122 region: replaced.region,
123 healthCheck: replaced.healthCheckName,
124 });
125 expect(afterReplace.type).toEqual("TCP");
126 expect(afterReplace.tcpHealthCheck?.port).toEqual(8080);
127 expect(afterReplace.httpHealthCheck).toBeUndefined();
128
129 yield* stack.destroy();
130
131 const gone = yield* waitUntilGone(
132 created.project,
133 created.region,
134 created.healthCheckName,
135 );
136 expect(gone).toEqual("gone");
137 }).pipe(logLevel),
138 { tags: ["provider:gcp", "provider:gcp:compute", "live"], timeout: 90_000 },
139);
140
141const lastSegment = (value: string | undefined) => {
142 if (value === undefined || value.length === 0) return "";
143 const parts = value.split("/").filter((part) => part.length > 0);
144 return parts[parts.length - 1] ?? value;
145};