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";
8import { GcpEnvironment } from "@/GCP/Environment";
9
10const { test } = Test.make({ providers: GCP.providers() });
11
12const logLevel = Effect.provideService(
13 MinimumLogLevel,
14 process.env.DEBUG ? "Debug" : "Info",
15);
16
17const waitUntilGone = (targetHttpProxyName: string) =>
18 GcpEnvironment.current.pipe(
19 Effect.flatMap(({ project }) =>
20 compute
21 .getTargetHttpProxies({ project, targetHttpProxy: targetHttpProxyName })
22 .pipe(
23 Effect.as("found" as const),
24 Effect.catchTag("NotFound", () => Effect.succeed("gone" as const)),
25 Effect.repeat({
26 schedule: Schedule.spaced("1 second"),
27 until: (status) => status === "gone",
28 times: 10,
29 }),
30 ),
31 ),
32 );
33
34const resourceTail = (value: string | undefined): string => {
35 if (value === undefined || value.length === 0) return "";
36 const parts = value.split("/").filter((part) => part.length > 0);
37 return parts[parts.length - 1] ?? "";
38};
39
40test.provider(
41 "create, update, and delete a target http proxy",
42 (stack) =>
43 Effect.gen(function* () {
44 const { project } = yield* GcpEnvironment.current;
45 yield* stack.destroy();
46
47 const created = yield* stack.deploy(
48 Effect.gen(function* () {
49 const map = yield* GCP.Compute.UrlMap("Web", {
50 description: "https redirect",
51 defaultUrlRedirect: {
52 httpsRedirect: true,
53 hostRedirect: "example.com",
54 stripQuery: false,
55 },
56 });
57 return yield* GCP.Compute.TargetHttpProxy("Proxy", {
58 description: "http frontend",
59 urlMap: map.urlMapName,
60 });
61 }),
62 );
63
64 expect(created.targetHttpProxyName).toEqual(expect.any(String));
65 expect(created.description).toEqual("http frontend");
66 expect(resourceTail(created.urlMap).length).toBeGreaterThan(0);
67
68 const fetched = yield* compute.getTargetHttpProxies({
69 project,
70 targetHttpProxy: created.targetHttpProxyName,
71 });
72 expect(fetched.name).toEqual(created.targetHttpProxyName);
73 expect(resourceTail(fetched.urlMap)).toEqual(
74 resourceTail(created.urlMap),
75 );
76 expect(fetched.description).toContain("[alchemy ");
77 expect(fetched.description).toContain("http frontend");
78
79 const updated = yield* stack.deploy(
80 Effect.gen(function* () {
81 yield* GCP.Compute.UrlMap("Web", {
82 description: "https redirect",
83 defaultUrlRedirect: {
84 httpsRedirect: true,
85 hostRedirect: "example.com",
86 stripQuery: false,
87 },
88 });
89 const other = yield* GCP.Compute.UrlMap("Other", {
90 description: "alt redirect",
91 defaultUrlRedirect: {
92 httpsRedirect: true,
93 hostRedirect: "example.org",
94 stripQuery: true,
95 },
96 });
97 return yield* GCP.Compute.TargetHttpProxy("Proxy", {
98 targetHttpProxyName: created.targetHttpProxyName,
99 description: "updated frontend",
100 urlMap: other.urlMapName,
101 });
102 }),
103 );
104
105 expect(updated.targetHttpProxyName).toEqual(created.targetHttpProxyName);
106 expect(updated.description).toEqual("updated frontend");
107 expect(resourceTail(updated.urlMap)).toEqual(expect.any(String));
108
109 const refetched = yield* compute.getTargetHttpProxies({
110 project,
111 targetHttpProxy: updated.targetHttpProxyName,
112 });
113 expect(refetched.description).toContain("updated frontend");
114 expect(resourceTail(refetched.urlMap)).toEqual(
115 resourceTail(updated.urlMap),
116 );
117 expect(resourceTail(refetched.urlMap)).not.toEqual(
118 resourceTail(created.urlMap),
119 );
120
121 yield* stack.destroy();
122
123 const gone = yield* waitUntilGone(created.targetHttpProxyName);
124 expect(gone).toEqual("gone");
125 }).pipe(logLevel),
126 { tags: ["provider:gcp", "provider:gcp:compute", "live"], timeout: 90_000 },
127);