1import * as GCP from "@/GCP";
2import * as Test from "@/Test/Alchemy";
3import * as bigquery from "@distilled.cloud/gcp/bigquery_v2";
4import { expect } from "alchemy-test";
5import * as Effect from "effect/Effect";
6import { MinimumLogLevel } from "effect/References";
7import * as Schedule from "effect/Schedule";
8
9const { test } = Test.make({ providers: GCP.providers() });
10
11const logLevel = Effect.provideService(
12 MinimumLogLevel,
13 process.env.DEBUG ? "Debug" : "Info",
14);
15
16const waitUntilGone = (
17 projectId: string,
18 datasetId: string,
19 routineId: string,
20) =>
21 bigquery
22 .getRoutines({
23 projectId,
24 datasetId,
25 routineId,
26 })
27 .pipe(
28 Effect.as("found" as const),
29 Effect.catchTag("NotFound", () => Effect.succeed("gone" as const)),
30 Effect.repeat({
31 schedule: Schedule.spaced("1 second"),
32 until: (status) => status === "gone",
33 times: 10,
34 }),
35 );
36
37test.provider(
38 "create, update, and delete a routine",
39 (stack) =>
40 Effect.gen(function* () {
41 yield* stack.destroy();
42
43 const created = yield* stack.deploy(
44 Effect.gen(function* () {
45 const dataset = yield* GCP.BigQuery.Dataset("Analytics", {
46 location: "US-CENTRAL1",
47 forceDestroy: true,
48 });
49 return yield* GCP.BigQuery.Routine("Triple", {
50 datasetId: dataset.datasetId,
51 routineType: "SCALAR_FUNCTION",
52 language: "SQL",
53 definitionBody: "x * 3",
54 arguments: [{ name: "x", dataType: { typeKind: "INT64" } }],
55 returnType: { typeKind: "INT64" },
56 description: "multiply by three",
57 });
58 }),
59 );
60
61 expect(created.routineId).toEqual(expect.any(String));
62 expect(created.routineId).toMatch(/^[a-zA-Z0-9_]+$/);
63 expect(created.datasetId).toEqual(expect.any(String));
64 expect(created.project).toEqual(expect.any(String));
65 expect(created.routineType).toEqual("SCALAR_FUNCTION");
66 expect(created.language).toEqual("SQL");
67 expect(created.definitionBody).toEqual("x * 3");
68 expect(created.description).toEqual("multiply by three");
69 expect(created.returnType).toMatchObject({ typeKind: "INT64" });
70 expect(created.arguments).toEqual(
71 expect.arrayContaining([
72 expect.objectContaining({
73 name: "x",
74 dataType: expect.objectContaining({ typeKind: "INT64" }),
75 }),
76 ]),
77 );
78
79 const fetched = yield* bigquery.getRoutines({
80 projectId: created.project,
81 datasetId: created.datasetId,
82 routineId: created.routineId,
83 });
84 expect(fetched.routineReference?.routineId).toEqual(created.routineId);
85 expect(fetched.routineType).toEqual("SCALAR_FUNCTION");
86 expect(fetched.language).toEqual("SQL");
87 expect(fetched.definitionBody).toEqual("x * 3");
88 expect(fetched.description).toContain("alchemy-id=");
89 expect(fetched.description).toContain("multiply by three");
90 expect(fetched.returnType?.typeKind).toEqual("INT64");
91
92 const queried = yield* bigquery.queryJobs({
93 projectId: created.project,
94 body: {
95 query: `SELECT \`${created.project}.${created.datasetId}.${created.routineId}\`(2) AS n`,
96 useLegacySql: false,
97 location: "US-CENTRAL1",
98 },
99 });
100 expect(queried.jobComplete).toEqual(true);
101 expect(queried.rows?.[0]?.f?.[0]?.v).toEqual("6");
102
103 const updated = yield* stack.deploy(
104 Effect.gen(function* () {
105 const dataset = yield* GCP.BigQuery.Dataset("Analytics", {
106 datasetId: created.datasetId,
107 location: "US-CENTRAL1",
108 forceDestroy: true,
109 });
110 return yield* GCP.BigQuery.Routine("Triple", {
111 datasetId: dataset.datasetId,
112 routineId: created.routineId,
113 routineType: "SCALAR_FUNCTION",
114 language: "SQL",
115 definitionBody: "x * 4",
116 arguments: [{ name: "x", dataType: { typeKind: "INT64" } }],
117 returnType: { typeKind: "INT64" },
118 description: "multiply by four",
119 });
120 }),
121 );
122
123 expect(updated.name).toEqual(created.name);
124 expect(updated.routineId).toEqual(created.routineId);
125 expect(updated.definitionBody).toEqual("x * 4");
126 expect(updated.description).toEqual("multiply by four");
127
128 const fetchedUpdate = yield* bigquery.getRoutines({
129 projectId: created.project,
130 datasetId: created.datasetId,
131 routineId: created.routineId,
132 });
133 expect(fetchedUpdate.definitionBody).toEqual("x * 4");
134 expect(fetchedUpdate.description).toContain("alchemy-id=");
135 expect(fetchedUpdate.description).toContain("multiply by four");
136
137 const queriedUpdate = yield* bigquery.queryJobs({
138 projectId: created.project,
139 body: {
140 query: `SELECT \`${created.project}.${created.datasetId}.${created.routineId}\`(2) AS n`,
141 useLegacySql: false,
142 location: "US-CENTRAL1",
143 },
144 });
145 expect(queriedUpdate.jobComplete).toEqual(true);
146 expect(queriedUpdate.rows?.[0]?.f?.[0]?.v).toEqual("8");
147
148 yield* stack.destroy();
149
150 const gone = yield* waitUntilGone(
151 created.project,
152 created.datasetId,
153 created.routineId,
154 );
155 expect(gone).toEqual("gone");
156 }).pipe(logLevel),
157 { tags: ["provider:gcp", "provider:gcp:bigquery", "live"], timeout: 90_000 },
158);