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 zone = "us-central1-a";
18const runLifecycle = !!process.env.GCP_TEST_STORAGE_POOL && !process.env.FAST;
19
20const waitUntilGone = (
21 projectId: string,
22 poolZone: string,
23 storagePool: string,
24) =>
25 compute
26 .getStoragePools({
27 project: projectId,
28 zone: poolZone,
29 storagePool,
30 })
31 .pipe(
32 Effect.as("found" as const),
33 Effect.catchTag("NotFound", () => Effect.succeed("gone" as const)),
34 Effect.repeat({
35 schedule: Schedule.spaced("1 second"),
36 until: (status) => status === "gone",
37 times: 10,
38 }),
39 );
40
41test.provider(
42 "getStoragePools on a missing pool fails with NotFound",
43 (stack) =>
44 Effect.gen(function* () {
45 const { project } = yield* GcpEnvironment.current;
46 yield* stack.destroy();
47
48 const error = yield* Effect.flip(
49 compute.getStoragePools({
50 project,
51 zone,
52 storagePool: "alchemy-missing-storage-pool",
53 }),
54 );
55 expect(error._tag).toBe("NotFound");
56
57 yield* stack.destroy();
58 }).pipe(logLevel),
59 { tags: ["provider:gcp", "provider:gcp:compute", "live"], timeout: 90_000 },
60);
61
62test.provider.skipIf(!!process.env.GCP_TEST_STORAGE_POOL)(
63 "insertStoragePools below minimum capacity fails with BadRequest",
64 (stack) =>
65 Effect.gen(function* () {
66 const { project } = yield* GcpEnvironment.current;
67 yield* stack.destroy();
68
69 const error = yield* Effect.flip(
70 compute.insertStoragePools({
71 project,
72 zone,
73 body: {
74 name: "alchemy-storage-pool-probe",
75 storagePoolType: `projects/${project}/zones/${zone}/storagePoolTypes/hyperdisk-balanced`,
76 poolProvisionedCapacityGb: "10",
77 },
78 }),
79 );
80 expect(error._tag).toBe("BadRequest");
81
82 yield* stack.destroy();
83 }).pipe(logLevel),
84 { tags: ["provider:gcp", "provider:gcp:compute", "live"], timeout: 90_000 },
85);
86
87test.provider.skipIf(!runLifecycle)(
88 "create, update, and delete a storage pool",
89 (stack) =>
90 Effect.gen(function* () {
91 yield* stack.destroy();
92
93 const created = yield* stack.deploy(
94 Effect.gen(function* () {
95 return yield* GCP.Compute.StoragePool("Disks", {
96 zone,
97 description: "shared hyperdisk",
98 storagePoolType: "hyperdisk-balanced",
99 poolProvisionedCapacityGb: 10240,
100 poolProvisionedIops: 10000,
101 poolProvisionedThroughput: 1024,
102 labels: { env: "test" },
103 });
104 }),
105 );
106
107 expect(created.storagePoolName).toEqual(expect.any(String));
108 expect(created.zone).toEqual(zone);
109 expect(created.state).toEqual("READY");
110 expect(created.labels).toMatchObject({ env: "test" });
111 expect(created.poolProvisionedCapacityGb).toEqual("10240");
112
113 const fetched = yield* compute.getStoragePools({
114 project: created.project,
115 zone: created.zone,
116 storagePool: created.storagePoolName,
117 });
118 expect(fetched.name).toEqual(created.storagePoolName);
119 expect(fetched.state).toEqual("READY");
120 expect(fetched.labels?.env).toEqual("test");
121
122 const updated = yield* stack.deploy(
123 Effect.gen(function* () {
124 return yield* GCP.Compute.StoragePool("Disks", {
125 storagePoolName: created.storagePoolName,
126 zone,
127 description: "shared hyperdisk v2",
128 storagePoolType: "hyperdisk-balanced",
129 poolProvisionedCapacityGb: 10240,
130 poolProvisionedIops: 11000,
131 poolProvisionedThroughput: 1024,
132 labels: { env: "prod" },
133 });
134 }),
135 );
136
137 expect(updated.storagePoolName).toEqual(created.storagePoolName);
138 expect(updated.description).toEqual("shared hyperdisk v2");
139 expect(updated.labels).toMatchObject({ env: "prod" });
140
141 yield* stack.destroy();
142 const gone = yield* waitUntilGone(
143 created.project,
144 created.zone,
145 created.storagePoolName,
146 );
147 expect(gone).toEqual("gone");
148 }).pipe(logLevel),
149 { tags: ["provider:gcp", "provider:gcp:compute", "live"], timeout: 90_000 },
150);