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 runLifecycle =
18 !!process.env.GCP_TEST_COMPUTE_INTERCONNECT && !process.env.FAST;
19
20const waitUntilGone = (interconnect: string) =>
21 GcpEnvironment.current.pipe(
22 Effect.flatMap(({ project }) =>
23 compute.getInterconnects({ project, interconnect }).pipe(
24 Effect.as("found" as const),
25 Effect.catchTag("NotFound", () => Effect.succeed("gone" as const)),
26 Effect.repeat({
27 schedule: Schedule.spaced("1 second"),
28 until: (status) => status === "gone",
29 times: 10,
30 }),
31 ),
32 ),
33 );
34
35test.provider(
36 "getInterconnects on a missing interconnect fails with NotFound",
37 (stack) =>
38 Effect.gen(function* () {
39 const { project } = yield* GcpEnvironment.current;
40 yield* stack.destroy();
41
42 const error = yield* Effect.flip(
43 compute.getInterconnects({
44 project,
45 interconnect: "alchemy-missing-interconnect",
46 }),
47 );
48 expect(error._tag).toBe("NotFound");
49
50 yield* stack.destroy();
51 }).pipe(logLevel),
52 { tags: ["provider:gcp", "provider:gcp:compute", "live"], timeout: 90_000 },
53);
54
55test.provider(
56 "insertInterconnects without customerName fails with BadRequest",
57 () =>
58 Effect.gen(function* () {
59 const { project } = yield* GcpEnvironment.current;
60 const error = yield* Effect.flip(
61 compute.insertInterconnects({
62 project,
63 body: {
64 name: "alchemy-ix-probe",
65 description: "alchemy entitlement probe",
66 location: `projects/${project}/global/interconnectLocations/iad-zone1-1`,
67 interconnectType: "DEDICATED",
68 linkType: "LINK_TYPE_ETHERNET_10G_LR",
69 requestedLinkCount: 1,
70 },
71 }),
72 );
73 expect(error._tag).toEqual("BadRequest");
74 }).pipe(logLevel),
75 { tags: ["provider:gcp", "provider:gcp:compute", "live"], timeout: 60_000 },
76);
77
78test.provider.skipIf(!runLifecycle)(
79 "create, update, and delete an interconnect",
80 (stack) =>
81 Effect.gen(function* () {
82 const { project } = yield* GcpEnvironment.current;
83 yield* stack.destroy();
84
85 const created = yield* stack.deploy(
86 Effect.gen(function* () {
87 return yield* GCP.Compute.Interconnect("Pairing", {
88 location: `projects/${project}/global/interconnectLocations/iad-zone1-1`,
89 interconnectType: "DEDICATED",
90 linkType: "LINK_TYPE_ETHERNET_10G_LR",
91 requestedLinkCount: 1,
92 description: "dedicated pair",
93 });
94 }),
95 );
96
97 expect(created.interconnectName).toEqual(expect.any(String));
98 expect(created.description).toEqual("dedicated pair");
99 expect(created.interconnectType).toEqual("DEDICATED");
100
101 const fetched = yield* compute.getInterconnects({
102 project: created.project,
103 interconnect: created.interconnectName,
104 });
105 expect(fetched.name).toEqual(created.interconnectName);
106 expect(fetched.description).toContain("[alchemy ");
107
108 const updated = yield* stack.deploy(
109 Effect.gen(function* () {
110 return yield* GCP.Compute.Interconnect("Pairing", {
111 interconnectName: created.interconnectName,
112 location: `projects/${project}/global/interconnectLocations/iad-zone1-1`,
113 interconnectType: "DEDICATED",
114 linkType: "LINK_TYPE_ETHERNET_10G_LR",
115 requestedLinkCount: 1,
116 description: "updated pair",
117 });
118 }),
119 );
120 expect(updated.description).toEqual("updated pair");
121
122 yield* stack.destroy();
123 const gone = yield* waitUntilGone(created.interconnectName);
124 expect(gone).toEqual("gone");
125 }).pipe(logLevel),
126 { tags: ["provider:gcp", "provider:gcp:compute", "live"], timeout: 90_000 },
127);