1import * as GCP from "@/GCP";
2import { GcpEnvironment } from "@/GCP/Environment";
3import * as Test from "@/Test/Alchemy";
4import * as Core from "@/Test/Core";
5import * as bigqueryconnection from "@distilled.cloud/gcp/bigqueryconnection_v1";
6import * as resourcemanager from "@distilled.cloud/gcp/cloudresourcemanager_v3";
7import { describe, expect } from "alchemy-test";
8import * as Effect from "effect/Effect";
9import { dockerAvailable, expectProbe } from "../bindingHost.ts";
10import BigQueryConnectionBindingsHost, {
11 Cloud,
12} from "./fixtures/bindings-host.ts";
13
14const testOptions = { providers: GCP.providers() };
15const { test, beforeAll, afterAll } = Test.make(testOptions);
16const sharedStack = Core.scratchStack(
17 testOptions,
18 "BigQueryConnectionBindings",
19);
20
21let baseUrl: string;
22let connectionName: string;
23let hostAccount: string;
24
25/** Every project-level role (and its IAM Condition) `account` holds. */
26const projectGrantsOf = (account: string) =>
27 Effect.gen(function* () {
28 const { project } = yield* GcpEnvironment.current;
29 const policy = yield* resourcemanager.getIamPolicyProjects({
30 resource: `projects/${project}`,
31 body: { options: { requestedPolicyVersion: 3 } },
32 });
33 return (policy.bindings ?? [])
34 .filter((binding) =>
35 (binding.members ?? []).includes(`serviceAccount:${account}`),
36 )
37 .map((binding) => ({
38 role: binding.role,
39 condition: binding.condition?.expression,
40 }));
41 });
42
43describe.skipIf(!dockerAvailable)(
44 "BigQueryConnection Bindings",
45 {
46 tags: [
47 "provider:gcp",
48 "provider:gcp:bigqueryconnection",
49 "provider:gcp:run",
50 "live",
51 ],
52 },
53 () => {
54 beforeAll(
55 Effect.gen(function* () {
56 yield* sharedStack.destroy();
57 const out = yield* sharedStack.deploy(
58 Effect.gen(function* () {
59 const host = yield* BigQueryConnectionBindingsHost;
60 const connection = yield* Cloud;
61 return {
62 uri: host.uri,
63 serviceAccount: host.serviceAccount,
64 connection: connection.name,
65 };
66 }),
67 );
68 baseUrl = out.uri!;
69 connectionName = out.connection;
70 hostAccount = out.serviceAccount!;
71 }),
72 { timeout: 900_000 },
73 );
74
75 afterAll(sharedStack.destroy(), { timeout: 600_000 });
76
77 describe("GetConnection", () => {
78 test.provider(
79 "reads the connection as the host's service account, granted on the connection only",
80 (_stack) =>
81 Effect.gen(function* () {
82 const live = yield* expectProbe<{
83 name?: string;
84 cloudResource?: { serviceAccountId?: string };
85 }>(baseUrl, "getConnection");
86 const expected =
87 yield* bigqueryconnection.getProjectsLocationsConnections({
88 name: connectionName,
89 });
90 expect(live.name).toEqual(connectionName);
91 expect(live.cloudResource?.serviceAccountId).toEqual(
92 expected.cloudResource?.serviceAccountId,
93 );
94
95 const policy =
96 yield* bigqueryconnection.getIamPolicyProjectsLocationsConnections(
97 {
98 resource: connectionName,
99 body: { options: { requestedPolicyVersion: 3 } },
100 },
101 );
102 const roles = (policy.bindings ?? [])
103 .filter((binding) =>
104 (binding.members ?? []).includes(
105 `serviceAccount:${hostAccount}`,
106 ),
107 )
108 .map((binding) => binding.role);
109 expect(roles).toEqual(["roles/bigquery.connectionUser"]);
110 expect(yield* projectGrantsOf(hostAccount)).toEqual([]);
111 }),
112 {
113 tags: ["provider:gcp", "provider:gcp:bigqueryconnection", "live"],
114 timeout: 600_000,
115 },
116 );
117 });
118 },
119);