1import * as GCP from "@/GCP";
2import * as Test from "@/Test/Alchemy";
3import * as workloadmanager from "@distilled.cloud/gcp/workloadmanager_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 parentOf = (project: string) =>
18 `projects/${project}/locations/us-central1`;
19const missingDeploymentOf = (project: string) =>
20 `${parentOf(project)}/deployments/alchemy-missing-deployment`;
21
22// Workload Manager API is entitlement-gated on the default testing project
23// (`ServiceDisabled`: "Workload Manager API has not been used in project
24// alchemy-gcp-testing-83661 before or it is disabled."). Set
25// GCP_TEST_WORKLOADMANAGER=1 on an entitled project to run the lifecycle.
26const entitled = process.env.GCP_TEST_WORKLOADMANAGER === "1";
27const runLifecycle = entitled && !process.env.FAST;
28
29const waitUntilGone = (name: string) =>
30 workloadmanager.getProjectsLocationsDeploymentsActuations({ name }).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 "getProjectsLocationsDeploymentsActuations on a missing actuation fails with a typed tag",
42 (stack) =>
43 Effect.gen(function* () {
44 const { project } = yield* GcpEnvironment.current;
45 const missingDeployment = missingDeploymentOf(project);
46 yield* stack.destroy();
47
48 const error = yield* Effect.flip(
49 workloadmanager.getProjectsLocationsDeploymentsActuations({
50 name: `${missingDeployment}/actuations/alchemy-missing-actuation`,
51 }),
52 );
53 expect(error._tag).toEqual("ServiceDisabled");
54
55 yield* stack.destroy();
56 }).pipe(logLevel),
57 {
58 tags: ["provider:gcp", "provider:gcp:workloadmanager", "live"],
59 timeout: 90_000,
60 },
61);
62
63test.provider.skipIf(entitled)(
64 "createProjectsLocationsDeploymentsActuations is rejected with ServiceDisabled while Workload Manager is disabled",
65 (stack) =>
66 Effect.gen(function* () {
67 const { project } = yield* GcpEnvironment.current;
68 const missingDeployment = missingDeploymentOf(project);
69 yield* stack.destroy();
70
71 const error = yield* Effect.flip(
72 workloadmanager.createProjectsLocationsDeploymentsActuations({
73 parent: missingDeployment,
74 body: {
75 name: `${missingDeployment}/actuations/alchemy-actuation-probe`,
76 },
77 }),
78 );
79 expect(error._tag).toEqual("ServiceDisabled");
80
81 yield* stack.destroy();
82 }).pipe(logLevel),
83 {
84 tags: ["provider:gcp", "provider:gcp:workloadmanager", "live"],
85 timeout: 90_000,
86 },
87);
88
89test.provider.skipIf(!runLifecycle)(
90 "create against a missing deployment is rejected with a typed tag",
91 (stack) =>
92 Effect.gen(function* () {
93 const { project } = yield* GcpEnvironment.current;
94 const missingDeployment = missingDeploymentOf(project);
95 yield* stack.destroy();
96
97 const error = yield* Effect.flip(
98 stack.deploy(
99 Effect.gen(function* () {
100 return yield* GCP.WorkloadManager.DeploymentsActuation(
101 "Bootstrap",
102 {
103 deployment: missingDeployment,
104 },
105 );
106 }),
107 ),
108 );
109 expect(error._tag).toEqual("ServiceDisabled");
110
111 yield* stack.destroy();
112 }).pipe(logLevel),
113 {
114 tags: ["provider:gcp", "provider:gcp:workloadmanager", "live"],
115 timeout: 90_000,
116 },
117);
118
119test.provider.skipIf(
120 !runLifecycle || !process.env.GCP_TEST_WORKLOADMANAGER_DEPLOYMENT,
121)(
122 "create and delete an actuation under an entitled deployment",
123 (stack) =>
124 Effect.gen(function* () {
125 const { project } = yield* GcpEnvironment.current;
126 yield* stack.destroy();
127
128 const deploymentName = process.env.GCP_TEST_WORKLOADMANAGER_DEPLOYMENT;
129 expect(deploymentName).toEqual(expect.any(String));
130
131 const created = yield* stack.deploy(
132 Effect.gen(function* () {
133 return yield* GCP.WorkloadManager.DeploymentsActuation("Bootstrap", {
134 deployment: deploymentName!,
135 });
136 }),
137 );
138
139 expect(created.name).toContain("/actuations/");
140 expect(created.actuationId).toEqual(expect.any(String));
141 expect(created.deployment).toEqual(deploymentName);
142 expect(created.project).toEqual(project);
143
144 const fetched =
145 yield* workloadmanager.getProjectsLocationsDeploymentsActuations({
146 name: created.name,
147 });
148 expect(fetched.name).toEqual(created.name);
149
150 yield* stack.destroy();
151
152 const gone = yield* waitUntilGone(created.name);
153 expect(gone).toEqual("gone");
154 }).pipe(logLevel),
155 {
156 tags: ["provider:gcp", "provider:gcp:workloadmanager", "live"],
157 timeout: 120_000,
158 },
159);