1import * as GCP from "@/GCP";
2import * as Test from "@/Test/Alchemy";
3import * as networkconnectivity from "@distilled.cloud/gcp/networkconnectivity_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, defaultNetworkSelfLink } 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 waitUntilGone = (name: string) =>
19 networkconnectivity.getProjectsLocationsInternalRanges({ name }).pipe(
20 Effect.as("found" as const),
21 Effect.catchTag("NotFound", () => Effect.succeed("gone" as const)),
22 Effect.repeat({
23 schedule: Schedule.spaced("1 second"),
24 until: (status) => status === "gone",
25 times: 10,
26 }),
27 );
28
29test.provider(
30 "getProjectsLocationsInternalRanges on a missing range fails with NotFound",
31 (stack) =>
32 Effect.gen(function* () {
33 const { project } = yield* GcpEnvironment.current;
34 yield* stack.destroy();
35
36 const error = yield* Effect.flip(
37 networkconnectivity.getProjectsLocationsInternalRanges({
38 name: `projects/${project}/locations/global/internalRanges/alchemy-ir-missing`,
39 }),
40 );
41 expect(error._tag).toBe("NotFound");
42
43 const page =
44 yield* networkconnectivity.listProjectsLocationsInternalRanges({
45 parent: `projects/${project}/locations/global`,
46 pageSize: 100,
47 });
48 expect(
49 (page.internalRanges ?? []).map((range) => range.name),
50 ).not.toContain(
51 `projects/${project}/locations/global/internalRanges/alchemy-ir-missing`,
52 );
53
54 yield* stack.destroy();
55 }).pipe(logLevel),
56 {
57 tags: ["provider:gcp", "provider:gcp:networkconnectivity", "live"],
58 timeout: 90_000,
59 },
60);
61
62test.provider.skipIf(!!process.env.FAST)(
63 "create, update, and delete an internal range",
64 (stack) =>
65 Effect.gen(function* () {
66 const { project } = yield* GcpEnvironment.current;
67 yield* stack.destroy();
68
69 const created = yield* stack.deploy(
70 Effect.gen(function* () {
71 const range = yield* GCP.NetworkConnectivity.InternalRange(
72 "Reserved",
73 {
74 network: defaultNetworkSelfLink(project),
75 usage: "FOR_VPC",
76 peering: "FOR_SELF",
77 ipCidrRange: "172.20.10.0/24",
78 description: "range a",
79 labels: { env: "test" },
80 },
81 );
82 return { range };
83 }),
84 );
85
86 expect(created.range.name).toContain("/internalRanges/");
87 expect(created.range.name).toContain("/locations/global/");
88 expect(created.range.internalRangeId).toEqual(expect.any(String));
89 expect(created.range.location).toEqual("global");
90 expect(created.range.project).toEqual(project);
91 expect(created.range.networkName).toEqual(DEFAULT_NETWORK);
92 expect(created.range.usage).toEqual("FOR_VPC");
93 expect(created.range.peering).toEqual("FOR_SELF");
94 expect(created.range.ipCidrRange).toEqual("172.20.10.0/24");
95 expect(created.range.description).toEqual("range a");
96 expect(created.range.labels).toMatchObject({ env: "test" });
97 expect(created.range.createTime).toEqual(expect.any(String));
98
99 const fetched =
100 yield* networkconnectivity.getProjectsLocationsInternalRanges({
101 name: created.range.name,
102 });
103 expect(fetched.name).toEqual(created.range.name);
104 expect(fetched.ipCidrRange).toEqual("172.20.10.0/24");
105 expect(fetched.usage).toEqual("FOR_VPC");
106 expect(fetched.peering).toEqual("FOR_SELF");
107 expect(fetched.description).toEqual("range a");
108 expect(fetched.labels?.env).toEqual("test");
109 expect(
110 Object.keys(fetched.labels ?? {}).some((key) =>
111 key.startsWith("alchemy-"),
112 ),
113 ).toEqual(true);
114
115 const updated = yield* stack.deploy(
116 Effect.gen(function* () {
117 return yield* GCP.NetworkConnectivity.InternalRange("Reserved", {
118 internalRangeId: created.range.internalRangeId,
119 network: defaultNetworkSelfLink(project),
120 usage: "FOR_VPC",
121 peering: "FOR_SELF",
122 ipCidrRange: "172.20.10.0/24",
123 description: "range b",
124 labels: { env: "prod", role: "ipam" },
125 });
126 }),
127 );
128
129 expect(updated.name).toEqual(created.range.name);
130 expect(updated.internalRangeId).toEqual(created.range.internalRangeId);
131 expect(updated.description).toEqual("range b");
132 expect(updated.labels).toMatchObject({ env: "prod", role: "ipam" });
133 expect(updated.ipCidrRange).toEqual("172.20.10.0/24");
134
135 const refetched =
136 yield* networkconnectivity.getProjectsLocationsInternalRanges({
137 name: created.range.name,
138 });
139 expect(refetched.description).toEqual("range b");
140 expect(refetched.labels?.env).toEqual("prod");
141 expect(refetched.labels?.role).toEqual("ipam");
142 expect(refetched.ipCidrRange).toEqual("172.20.10.0/24");
143
144 yield* stack.destroy();
145
146 const gone = yield* waitUntilGone(created.range.name);
147 expect(gone).toEqual("gone");
148 }).pipe(logLevel),
149 {
150 tags: ["provider:gcp", "provider:gcp:networkconnectivity", "live"],
151 timeout: 600_000,
152 },
153);