1import * as GCP from "@/GCP";
2import * as Test from "@/Test/Alchemy";
3import * as managedidentities from "@distilled.cloud/gcp/managedidentities_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
17// Managed Microsoft AD takes 20-60 minutes to provision (~$0.40/hour).
18// The API is enabled on the testing project; get-missing returns
19// `NotFound`, and create with an invalid CIDR returns `BadRequest`
20// (`CIDR "not-a-cidr" is invalid`). Set GCP_TEST_MANAGEDIDENTITIES=1
21// to run the full lifecycle.
22const runLifecycle =
23 !process.env.FAST && process.env.GCP_TEST_MANAGEDIDENTITIES === "1";
24
25const waitUntilGone = (name: string) =>
26 managedidentities.getProjectsLocationsGlobalDomains({ name }).pipe(
27 Effect.as("found" as const),
28 Effect.catchTag("NotFound", () => Effect.succeed("gone" as const)),
29 Effect.repeat({
30 schedule: Schedule.spaced("5 seconds"),
31 until: (status) => status === "gone",
32 times: 10,
33 }),
34 );
35
36test.provider(
37 "getProjectsLocationsGlobalDomains on a missing domain fails with a typed tag",
38 (stack) =>
39 Effect.gen(function* () {
40 const { project } = yield* GcpEnvironment.current;
41 yield* stack.destroy();
42
43 const error = yield* Effect.flip(
44 managedidentities.getProjectsLocationsGlobalDomains({
45 name: `projects/${project}/locations/global/domains/missing.alch.test`,
46 }),
47 );
48 expect(error._tag).toEqual("NotFound");
49
50 yield* stack.destroy();
51 }).pipe(logLevel),
52 {
53 tags: ["provider:gcp", "provider:gcp:managedidentities", "live"],
54 timeout: 90_000,
55 },
56);
57
58test.provider.skipIf(runLifecycle)(
59 "create with an invalid reserved IP range fails with BadRequest",
60 (stack) =>
61 Effect.gen(function* () {
62 yield* stack.destroy();
63
64 const error = yield* Effect.flip(
65 stack.deploy(
66 Effect.gen(function* () {
67 return yield* GCP.ManagedIdentities.Domain("Ad", {
68 reservedIpRange: "not-a-cidr",
69 locations: ["us-central1"],
70 labels: { env: "test" },
71 });
72 }),
73 ),
74 );
75 expect(error._tag).toEqual("BadRequest");
76
77 yield* stack.destroy();
78 }).pipe(logLevel),
79 {
80 tags: ["provider:gcp", "provider:gcp:managedidentities", "live"],
81 timeout: 90_000,
82 },
83);
84
85test.provider.skipIf(!runLifecycle)(
86 "create, update, and delete a managed AD domain",
87 (stack) =>
88 Effect.gen(function* () {
89 yield* stack.destroy();
90
91 const created = yield* stack.deploy(
92 Effect.gen(function* () {
93 return yield* GCP.ManagedIdentities.Domain("Ad", {
94 reservedIpRange: "172.16.0.0/24",
95 locations: ["us-central1"],
96 authorizedNetworks: ["default"],
97 labels: { env: "test" },
98 });
99 }),
100 );
101
102 expect(created.name).toContain("/domains/");
103 expect(created.domainName).toEqual(expect.any(String));
104 expect(created.location).toEqual("global");
105 expect(created.labels).toMatchObject({ env: "test" });
106
107 const fetched =
108 yield* managedidentities.getProjectsLocationsGlobalDomains({
109 name: created.name,
110 });
111 expect(fetched.name).toEqual(created.name);
112 expect(fetched.labels?.env).toEqual("test");
113
114 const updated = yield* stack.deploy(
115 Effect.gen(function* () {
116 return yield* GCP.ManagedIdentities.Domain("Ad", {
117 domainName: created.domainName,
118 reservedIpRange: "172.16.0.0/24",
119 locations: ["us-central1"],
120 authorizedNetworks: ["default"],
121 auditLogsEnabled: true,
122 labels: { env: "prod", role: "identity" },
123 });
124 }),
125 );
126
127 expect(updated.name).toEqual(created.name);
128 expect(updated.auditLogsEnabled).toEqual(true);
129 expect(updated.labels).toMatchObject({ env: "prod", role: "identity" });
130
131 const refetched =
132 yield* managedidentities.getProjectsLocationsGlobalDomains({
133 name: created.name,
134 });
135 expect(refetched.labels?.env).toEqual("prod");
136 expect(refetched.labels?.role).toEqual("identity");
137
138 yield* stack.destroy();
139
140 const gone = yield* waitUntilGone(created.name);
141 expect(gone).toEqual("gone");
142 }).pipe(logLevel),
143 {
144 tags: ["provider:gcp", "provider:gcp:managedidentities", "live"],
145 timeout: 240_000,
146 },
147);