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 = !!process.env.GCP_TEST_WIRE_GROUP && !process.env.FAST;
18
19const waitUntilGone = (
20 projectId: string,
21 crossSiteNetwork: string,
22 wireGroup: string,
23) =>
24 compute
25 .getWireGroups({
26 project: projectId,
27 crossSiteNetwork,
28 wireGroup,
29 })
30 .pipe(
31 Effect.as("found" as const),
32 Effect.catchTag("NotFound", () => Effect.succeed("gone" as const)),
33 Effect.repeat({
34 schedule: Schedule.spaced("1 second"),
35 until: (status) => status === "gone",
36 times: 10,
37 }),
38 );
39
40test.provider(
41 "getWireGroups on a missing group fails with NotFound",
42 (stack) =>
43 Effect.gen(function* () {
44 const { project } = yield* GcpEnvironment.current;
45 yield* stack.destroy();
46
47 const error = yield* Effect.flip(
48 compute.getWireGroups({
49 project,
50 crossSiteNetwork: "alchemy-missing-csn",
51 wireGroup: "alchemy-missing-wire-group",
52 }),
53 );
54 expect(error._tag).toBe("NotFound");
55
56 yield* stack.destroy();
57 }).pipe(logLevel),
58 { tags: ["provider:gcp", "provider:gcp:compute", "live"], timeout: 90_000 },
59);
60
61test.provider.skipIf(!runLifecycle)(
62 "create, update, and delete a wire group",
63 (stack) =>
64 Effect.gen(function* () {
65 yield* stack.destroy();
66
67 const created = yield* stack.deploy(
68 Effect.gen(function* () {
69 return yield* GCP.Compute.WireGroup("Metro", {
70 crossSiteNetwork: process.env.GCP_TEST_CROSS_SITE_NETWORK ?? "",
71 description: "nyc-to-sfo",
72 adminEnabled: true,
73 wireProperties: {
74 bandwidthUnmetered: "10",
75 bandwidthAllocation: "SHARED_WITH_WIRE_GROUP",
76 },
77 endpoints: {
78 nyc: {
79 interconnects: {
80 a: {
81 interconnect:
82 process.env.GCP_TEST_INTERCONNECT_A ??
83 "global/interconnects/nyc-a",
84 vlanTags: [100],
85 },
86 },
87 },
88 sfo: {
89 interconnects: {
90 a: {
91 interconnect:
92 process.env.GCP_TEST_INTERCONNECT_B ??
93 "global/interconnects/sfo-a",
94 vlanTags: [100],
95 },
96 },
97 },
98 },
99 });
100 }),
101 );
102
103 expect(created.wireGroupName).toEqual(expect.any(String));
104 expect(created.adminEnabled).toEqual(true);
105 expect(created.description).toEqual("nyc-to-sfo");
106
107 const fetched = yield* compute.getWireGroups({
108 project: created.project,
109 crossSiteNetwork: created.crossSiteNetwork,
110 wireGroup: created.wireGroupName,
111 });
112 expect(fetched.name).toEqual(created.wireGroupName);
113 expect(fetched.description).toContain("[alchemy ");
114 expect(fetched.description).toContain("nyc-to-sfo");
115
116 const updated = yield* stack.deploy(
117 Effect.gen(function* () {
118 return yield* GCP.Compute.WireGroup("Metro", {
119 wireGroupName: created.wireGroupName,
120 crossSiteNetwork: created.crossSiteNetwork,
121 description: "nyc-to-sfo updated",
122 adminEnabled: false,
123 wireProperties: created.wireProperties,
124 endpoints: created.endpoints,
125 });
126 }),
127 );
128
129 expect(updated.wireGroupName).toEqual(created.wireGroupName);
130 expect(updated.description).toEqual("nyc-to-sfo updated");
131 expect(updated.adminEnabled).toEqual(false);
132
133 yield* stack.destroy();
134 const gone = yield* waitUntilGone(
135 created.project,
136 created.crossSiteNetwork,
137 created.wireGroupName,
138 );
139 expect(gone).toEqual("gone");
140 }).pipe(logLevel),
141 { tags: ["provider:gcp", "provider:gcp:compute", "live"], timeout: 90_000 },
142);