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";
9import { DEFAULT_NETWORK } from "../networkQuota.ts";
10
11const { test } = Test.make({ providers: GCP.providers() });
12
13const logLevel = Effect.provideService(
14 MinimumLogLevel,
15 process.env.DEBUG ? "Debug" : "Info",
16);
17
18const runLifecycle =
19 !!process.env.GCP_TEST_COMPUTE_INTERCONNECT && !process.env.FAST;
20
21const region = "us-central1";
22
23const waitUntilGone = (interconnectAttachment: string) =>
24 GcpEnvironment.current.pipe(
25 Effect.flatMap(({ project }) =>
26 compute
27 .getInterconnectAttachments({
28 project,
29 region,
30 interconnectAttachment,
31 })
32 .pipe(
33 Effect.as("found" as const),
34 Effect.catchTag("NotFound", () => Effect.succeed("gone" as const)),
35 Effect.repeat({
36 schedule: Schedule.spaced("1 second"),
37 until: (status) => status === "gone",
38 times: 10,
39 }),
40 ),
41 ),
42 );
43
44test.provider(
45 "getInterconnectAttachments on a missing attachment fails with NotFound",
46 (stack) =>
47 Effect.gen(function* () {
48 const { project } = yield* GcpEnvironment.current;
49 yield* stack.destroy();
50
51 const error = yield* Effect.flip(
52 compute.getInterconnectAttachments({
53 project,
54 region,
55 interconnectAttachment: "alchemy-missing-vlan",
56 }),
57 );
58 expect(error._tag).toBe("NotFound");
59
60 yield* stack.destroy();
61 }).pipe(logLevel),
62 { tags: ["provider:gcp", "provider:gcp:compute", "live"], timeout: 90_000 },
63);
64
65test.provider(
66 "insertInterconnectAttachments with a malformed router fails with BadRequest",
67 () =>
68 Effect.gen(function* () {
69 const { project } = yield* GcpEnvironment.current;
70 const error = yield* Effect.flip(
71 compute.insertInterconnectAttachments({
72 project,
73 region,
74 body: {
75 name: "alchemy-vlan-probe",
76 description: "alchemy entitlement probe",
77 router: "does-not-exist",
78 type: "PARTNER",
79 },
80 }),
81 );
82 expect(error._tag).toEqual("BadRequest");
83 }).pipe(logLevel),
84 { tags: ["provider:gcp", "provider:gcp:compute", "live"], timeout: 60_000 },
85);
86
87test.provider.skipIf(!runLifecycle)(
88 "create, update, and delete an interconnect attachment",
89 (stack) =>
90 Effect.gen(function* () {
91 yield* stack.destroy();
92
93 const created = yield* stack.deploy(
94 Effect.gen(function* () {
95 const router = yield* GCP.Compute.Router("Edge", {
96 region,
97 network: DEFAULT_NETWORK,
98 description: "interconnect router",
99 });
100 const attachment = yield* GCP.Compute.InterconnectAttachment("Vlan", {
101 region,
102 router: router.routerName,
103 type: "PARTNER",
104 description: "partner vlan",
105 });
106 return { router, attachment };
107 }),
108 );
109
110 expect(created.attachment.interconnectAttachmentName).toEqual(
111 expect.any(String),
112 );
113 expect(created.attachment.region).toEqual(region);
114 expect(created.attachment.description).toEqual("partner vlan");
115
116 const fetched = yield* compute.getInterconnectAttachments({
117 project: created.attachment.project,
118 region,
119 interconnectAttachment: created.attachment.interconnectAttachmentName,
120 });
121 expect(fetched.name).toEqual(
122 created.attachment.interconnectAttachmentName,
123 );
124 expect(fetched.description).toContain("[alchemy ");
125
126 const updated = yield* stack.deploy(
127 Effect.gen(function* () {
128 const router = yield* GCP.Compute.Router("Edge", {
129 routerName: created.router.routerName,
130 region,
131 network: DEFAULT_NETWORK,
132 description: "interconnect router",
133 });
134 return yield* GCP.Compute.InterconnectAttachment("Vlan", {
135 interconnectAttachmentName:
136 created.attachment.interconnectAttachmentName,
137 region,
138 router: router.routerName,
139 type: "PARTNER",
140 description: "updated partner vlan",
141 adminEnabled: false,
142 });
143 }),
144 );
145 expect(updated.description).toEqual("updated partner vlan");
146
147 yield* stack.destroy();
148 const gone = yield* waitUntilGone(
149 created.attachment.interconnectAttachmentName,
150 );
151 expect(gone).toEqual("gone");
152 }).pipe(logLevel),
153 { tags: ["provider:gcp", "provider:gcp:compute", "live"], timeout: 90_000 },
154);