1import * as GCP from "@/GCP";
2import * as Test from "@/Test/Alchemy";
3import * as networksecurity from "@distilled.cloud/gcp/networksecurity_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 { withNetworkSlot } 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 networksecurity.getProjectsLocationsInterceptDeploymentGroups({ name }).pipe(
20 Effect.as("found" as const),
21 Effect.catchTag("NotFound", () => Effect.succeed("gone" as const)),
22 Effect.repeat({
23 schedule: Schedule.spaced("2 seconds"),
24 until: (status) => status === "gone",
25 times: 10,
26 }),
27 );
28
29test.provider(
30 "getProjectsLocationsInterceptDeploymentGroups on a missing group fails with a typed tag",
31 (stack) =>
32 Effect.gen(function* () {
33 yield* stack.destroy();
34
35 const { project } = yield* GcpEnvironment.current;
36 const error = yield* Effect.flip(
37 networksecurity.getProjectsLocationsInterceptDeploymentGroups({
38 name: `projects/${project}/locations/global/interceptDeploymentGroups/alchemy-missing`,
39 }),
40 );
41 expect(error._tag).toEqual("NotFound");
42
43 yield* stack.destroy();
44 }).pipe(logLevel),
45 {
46 tags: ["provider:gcp", "provider:gcp:networksecurity", "live"],
47 timeout: 90_000,
48 },
49);
50
51test.provider.skipIf(!!process.env.FAST)(
52 "create, update, and delete an intercept deployment group",
53 (stack) =>
54 Effect.gen(function* () {
55 yield* stack.destroy();
56
57 const created = yield* stack.deploy(
58 Effect.gen(function* () {
59 const vpc = yield* GCP.Compute.Network("Vpc", {
60 autoCreateSubnetworks: false,
61 description: "intercept group vpc",
62 });
63 const group = yield* GCP.NetworkSecurity.InterceptDeploymentGroup(
64 "Inspect",
65 {
66 location: "global",
67 network: vpc.selfLink.as<string>(),
68 description: "intercept group a",
69 labels: { env: "test" },
70 },
71 );
72 return { vpc, group };
73 }),
74 );
75
76 expect(created.group.name).toContain("/interceptDeploymentGroups/");
77 expect(created.group.location).toEqual("global");
78 expect(created.group.description).toEqual("intercept group a");
79 expect(created.group.labels).toMatchObject({ env: "test" });
80 expect(created.group.network).toContain(
81 `/global/networks/${created.vpc.networkName}`,
82 );
83
84 const fetched =
85 yield* networksecurity.getProjectsLocationsInterceptDeploymentGroups({
86 name: created.group.name,
87 });
88 expect(fetched.name).toEqual(created.group.name);
89 expect(fetched.description).toEqual("intercept group a");
90 expect(fetched.labels?.env).toEqual("test");
91 expect(
92 Object.keys(fetched.labels ?? {}).some((key) =>
93 key.startsWith("alchemy-"),
94 ),
95 ).toEqual(true);
96
97 const updated = yield* stack.deploy(
98 Effect.gen(function* () {
99 const vpc = yield* GCP.Compute.Network("Vpc", {
100 networkName: created.vpc.networkName,
101 autoCreateSubnetworks: false,
102 description: "intercept group vpc",
103 });
104 const group = yield* GCP.NetworkSecurity.InterceptDeploymentGroup(
105 "Inspect",
106 {
107 interceptDeploymentGroupId:
108 created.group.interceptDeploymentGroupId,
109 location: "global",
110 network: vpc.selfLink.as<string>(),
111 description: "intercept group b",
112 labels: { env: "prod", role: "intercept" },
113 },
114 );
115 return { vpc, group };
116 }),
117 );
118
119 expect(updated.group.name).toEqual(created.group.name);
120 expect(updated.group.description).toEqual("intercept group b");
121 expect(updated.group.labels).toMatchObject({
122 env: "prod",
123 role: "intercept",
124 });
125
126 const refetched =
127 yield* networksecurity.getProjectsLocationsInterceptDeploymentGroups({
128 name: created.group.name,
129 });
130 expect(refetched.description).toEqual("intercept group b");
131 expect(refetched.labels?.env).toEqual("prod");
132 expect(refetched.labels?.role).toEqual("intercept");
133
134 yield* stack.destroy();
135
136 const gone = yield* waitUntilGone(created.group.name);
137 expect(gone).toEqual("gone");
138 }).pipe(logLevel, withNetworkSlot),
139 {
140 tags: ["provider:gcp", "provider:gcp:networksecurity", "live"],
141 timeout: 600_000,
142 },
143);