1import { GcpEnvironment } from "@/GCP/Environment";
2import * as GCP from "@/GCP";
3import * as Test from "@/Test/Alchemy";
4import * as bigtable from "@distilled.cloud/gcp/bigtableadmin_v2";
5import { expect } from "alchemy-test";
6import * as Effect from "effect/Effect";
7import { MinimumLogLevel } from "effect/References";
8import * as Schedule from "effect/Schedule";
9
10const { test } = Test.make({ providers: GCP.providers() });
11
12const logLevel = Effect.provideService(
13 MinimumLogLevel,
14 process.env.DEBUG ? "Debug" : "Info",
15);
16
17const runLifecycle = !process.env.FAST;
18
19const waitUntilGone = (name: string) =>
20 bigtable.getProjectsInstancesClusters({ name }).pipe(
21 Effect.as("found" as const),
22 Effect.catchTag("NotFound", () => Effect.succeed("gone" as const)),
23 Effect.repeat({
24 schedule: Schedule.spaced("2 seconds"),
25 until: (status) => status === "gone",
26 times: 10,
27 }),
28 );
29
30test.provider(
31 "getProjectsInstancesClusters on a missing cluster fails with NotFound",
32 (stack) =>
33 Effect.gen(function* () {
34 const { project } = yield* GcpEnvironment.current;
35
36 yield* stack.destroy();
37
38 const error = yield* Effect.flip(
39 bigtable.getProjectsInstancesClusters({
40 name: `projects/${project}/instances/alchemy-bt-missing/clusters/missing`,
41 }),
42 );
43 expect(error._tag).toEqual("NotFound");
44
45 const page = yield* bigtable.listProjectsInstancesClusters({
46 parent: `projects/${project}/instances/-`,
47 });
48 expect((page.clusters ?? []).map((item) => item.name)).not.toContain(
49 `projects/${project}/instances/alchemy-bt-missing/clusters/missing`,
50 );
51
52 yield* stack.destroy();
53 }).pipe(logLevel),
54 { tags: ["provider:gcp", "provider:gcp:bigtable", "live"], timeout: 90_000 },
55);
56
57test.provider.skipIf(!runLifecycle)(
58 "create, update, and delete a bigtable cluster",
59 (stack) =>
60 Effect.gen(function* () {
61 yield* stack.destroy();
62
63 const created = yield* stack.deploy(
64 Effect.gen(function* () {
65 const instance = yield* GCP.Bigtable.Instance("Data", {
66 displayName: "alchemy-test-bt-cluster",
67 type: "PRODUCTION",
68 clusters: {
69 cluster: {
70 location: "us-central1-b",
71 serveNodes: 1,
72 defaultStorageType: "HDD",
73 },
74 },
75 });
76 const replica = yield* GCP.Bigtable.Cluster("Replica", {
77 instance: instance.name,
78 location: "us-central1-c",
79 serveNodes: 1,
80 defaultStorageType: "HDD",
81 });
82 return { instance, replica };
83 }),
84 );
85
86 expect(created.replica.name).toContain("/clusters/");
87 expect(created.replica.clusterId).toEqual(expect.any(String));
88 expect(created.replica.instance).toEqual(created.instance.name);
89 expect(created.replica.location).toEqual("us-central1-c");
90 expect(created.replica.serveNodes).toEqual(1);
91 expect(created.replica.state).toEqual("READY");
92
93 const fetched = yield* bigtable.getProjectsInstancesClusters({
94 name: created.replica.name,
95 });
96 expect(fetched.name).toEqual(created.replica.name);
97 expect(fetched.serveNodes).toEqual(1);
98
99 const updated = yield* stack.deploy(
100 Effect.gen(function* () {
101 const instance = yield* GCP.Bigtable.Instance("Data", {
102 instanceId: created.instance.instanceId,
103 displayName: "alchemy-test-bt-cluster",
104 type: "PRODUCTION",
105 clusters: {
106 cluster: {
107 location: "us-central1-b",
108 serveNodes: 1,
109 defaultStorageType: "HDD",
110 },
111 },
112 });
113 const replica = yield* GCP.Bigtable.Cluster("Replica", {
114 instance: instance.name,
115 clusterId: created.replica.clusterId,
116 location: "us-central1-c",
117 serveNodes: 2,
118 defaultStorageType: "HDD",
119 });
120 return { instance, replica };
121 }),
122 );
123
124 expect(updated.replica.name).toEqual(created.replica.name);
125 expect(updated.replica.serveNodes).toEqual(2);
126
127 const refetched = yield* bigtable.getProjectsInstancesClusters({
128 name: created.replica.name,
129 });
130 expect(refetched.serveNodes).toEqual(2);
131
132 yield* stack.destroy();
133
134 const gone = yield* waitUntilGone(created.replica.name);
135 expect(gone).toEqual("gone");
136 }).pipe(logLevel),
137 { tags: ["provider:gcp", "provider:gcp:bigtable", "live"], timeout: 120_000 },
138);