| 1 | import * as GCP from "@/GCP"; |
| 2 | import * as Test from "@/Test/Alchemy"; |
| 3 | import * as bigquery from "@distilled.cloud/gcp/bigquery_v2"; |
| 4 | import { expect } from "alchemy-test"; |
| 5 | import * as Effect from "effect/Effect"; |
| 6 | import { MinimumLogLevel } from "effect/References"; |
| 7 | import * as Schedule from "effect/Schedule"; |
| 8 | |
| 9 | const { test } = Test.make({ providers: GCP.providers() }); |
| 10 | |
| 11 | const logLevel = Effect.provideService( |
| 12 | MinimumLogLevel, |
| 13 | process.env.DEBUG ? "Debug" : "Info", |
| 14 | ); |
| 15 | |
| 16 | const 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 | |
| 37 | test.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 | ); |