1import * as GCP from "@/GCP";
2import * as Test from "@/Test/Alchemy";
3import * as Core from "@/Test/Core";
4import * as bigtable from "@distilled.cloud/gcp/bigtableadmin_v2";
5import { describe, expect } from "alchemy-test";
6import * as Effect from "effect/Effect";
7import { dockerAvailable, expectProbe } from "../bindingHost.ts";
8import BigtableBindingsHost, {
9 Db,
10 Nodes,
11 Rows,
12} from "./fixtures/bindings-host.ts";
13
14const testOptions = { providers: GCP.providers() };
15const { test, beforeAll, afterAll } = Test.make(testOptions);
16const sharedStack = Core.scratchStack(testOptions, "BigtableBindings");
17
18let baseUrl: string;
19let hostAccount: string;
20let instanceName: string;
21let clusterName: string;
22let tableName: string;
23
24const hostRoles = (policy: bigtable.Policy) =>
25 (policy.bindings ?? [])
26 .filter((binding) =>
27 (binding.members ?? []).includes(`serviceAccount:${hostAccount}`),
28 )
29 .map((binding) => binding.role)
30 .sort();
31
32/** GetInstance and GetCluster grant bigtable.viewer on the instance only. */
33const expectInstanceGrants = Effect.gen(function* () {
34 const policy = yield* bigtable.getIamPolicyProjectsInstances({
35 resource: instanceName,
36 body: {},
37 });
38 expect(hostRoles(policy)).toEqual(["roles/bigtable.viewer"]);
39});
40
41/** GetTable grants bigtable.viewer on the table only. */
42const expectTableGrants = Effect.gen(function* () {
43 const policy = yield* bigtable.getIamPolicyProjectsInstancesTables({
44 resource: tableName,
45 body: {},
46 });
47 expect(hostRoles(policy)).toEqual(["roles/bigtable.viewer"]);
48});
49
50describe.skipIf(!dockerAvailable || !!process.env.FAST)(
51 "Bigtable Bindings",
52 {
53 tags: ["provider:gcp", "provider:gcp:bigtable", "provider:gcp:run", "live"],
54 },
55 () => {
56 beforeAll(
57 Effect.gen(function* () {
58 yield* sharedStack.destroy();
59 const out = yield* sharedStack.deploy(
60 Effect.gen(function* () {
61 const host = yield* BigtableBindingsHost;
62 return {
63 uri: host.uri,
64 serviceAccount: host.serviceAccount,
65 instance: (yield* Db).name,
66 cluster: (yield* Nodes).name,
67 table: (yield* Rows).name,
68 };
69 }),
70 );
71 baseUrl = out.uri!;
72 hostAccount = out.serviceAccount!;
73 instanceName = out.instance;
74 clusterName = out.cluster;
75 tableName = out.table;
76 }),
77 { timeout: 900_000 },
78 );
79
80 afterAll(sharedStack.destroy(), { timeout: 600_000 });
81
82 describe("GetInstance", () => {
83 test.provider(
84 "reads the instance as the host's service account, granted on the instance only",
85 (_stack) =>
86 Effect.gen(function* () {
87 const live = yield* expectProbe<bigtable.Instance>(
88 baseUrl,
89 "getInstance",
90 );
91 const actual = yield* bigtable.getProjectsInstances({
92 name: instanceName,
93 });
94 expect(live.name).toEqual(instanceName);
95 expect(live.displayName).toEqual(actual.displayName);
96 expect(live.state).toEqual("READY");
97 yield* expectInstanceGrants;
98 }),
99 {
100 tags: ["provider:gcp", "provider:gcp:bigtable", "live"],
101 timeout: 600_000,
102 },
103 );
104 });
105
106 describe("GetCluster", () => {
107 test.provider(
108 "reads the cluster as the host's service account, granted on the instance only",
109 (_stack) =>
110 Effect.gen(function* () {
111 const live = yield* expectProbe<bigtable.Cluster>(
112 baseUrl,
113 "getCluster",
114 );
115 expect(live.name).toEqual(clusterName);
116 expect(live.serveNodes).toEqual(1);
117 expect(live.defaultStorageType).toEqual("HDD");
118 yield* expectInstanceGrants;
119 }),
120 {
121 tags: ["provider:gcp", "provider:gcp:bigtable", "live"],
122 timeout: 600_000,
123 },
124 );
125 });
126
127 describe("GetTable", () => {
128 test.provider(
129 "reads the table as the host's service account, granted on the table only",
130 (_stack) =>
131 Effect.gen(function* () {
132 const live = yield* expectProbe<bigtable.Table>(
133 baseUrl,
134 "getTable",
135 );
136 expect(live.name).toEqual(tableName);
137 expect(Object.keys(live.columnFamilies ?? {})).toEqual(["cf"]);
138 yield* expectTableGrants;
139 }),
140 {
141 tags: ["provider:gcp", "provider:gcp:bigtable", "live"],
142 timeout: 600_000,
143 },
144 );
145 });
146 },
147);