1import * as GCP from "@/GCP";
2import * as Test from "@/Test/Alchemy";
3import * as bigqueryreservation from "@distilled.cloud/gcp/bigqueryreservation_v1";
4import { expect } from "alchemy-test";
5import * as Effect from "effect/Effect";
6import { MinimumLogLevel } from "effect/References";
7import * as Schedule from "effect/Schedule";
8import { GcpEnvironment } from "@/GCP/Environment";
9
10const { test } = Test.make({ providers: GCP.providers() });
11
12const logLevel = Effect.provideService(
13 MinimumLogLevel,
14 process.env.DEBUG ? "Debug" : "Info",
15);
16
17// Reservation groups need reservation-based fairness on the project;
18// otherwise create fails with BadRequest "Can not use Reservation Groups
19// without Reservation Based Fairness enabled on the project." Set
20// GCP_TEST_BIGQUERY_RESERVATION_GROUPS=1 on such a project.
21const runLifecycle =
22 !process.env.FAST && process.env.GCP_TEST_BIGQUERY_RESERVATION_GROUPS === "1";
23
24const waitUntilGone = (name: string) =>
25 bigqueryreservation.getProjectsLocationsReservationGroups({ name }).pipe(
26 Effect.as("found" as const),
27 Effect.catchTag("NotFound", () => Effect.succeed("gone" as const)),
28 Effect.repeat({
29 schedule: Schedule.spaced("1 second"),
30 until: (status) => status === "gone",
31 times: 10,
32 }),
33 );
34
35test.provider(
36 "getProjectsLocationsReservationGroups on a missing group fails with NotFound",
37 (stack) =>
38 Effect.gen(function* () {
39 const { project } = yield* GcpEnvironment.current;
40 yield* stack.destroy();
41
42 const error = yield* Effect.flip(
43 bigqueryreservation.getProjectsLocationsReservationGroups({
44 name: `projects/${project}/locations/us-central1/reservationGroups/alchemy-bq-reservation-group-missing`,
45 }),
46 );
47 expect(error._tag).toBe("NotFound");
48
49 const page =
50 yield* bigqueryreservation.listProjectsLocationsReservationGroups({
51 parent: `projects/${project}/locations/us-central1`,
52 pageSize: 10,
53 });
54 expect(
55 (page.reservationGroups ?? []).map((item) =>
56 item.name?.split("/").pop(),
57 ),
58 ).not.toContain("alchemy-bq-reservation-group-missing");
59
60 yield* stack.destroy();
61 }).pipe(logLevel),
62 {
63 tags: ["provider:gcp", "provider:gcp:bigqueryreservation", "live"],
64 timeout: 90_000,
65 },
66);
67
68test.provider(
69 "createProjectsLocationsReservationGroups is gated on reservation-based fairness",
70 (stack) =>
71 Effect.gen(function* () {
72 const { project } = yield* GcpEnvironment.current;
73 yield* stack.destroy();
74
75 const error = yield* Effect.flip(
76 bigqueryreservation.createProjectsLocationsReservationGroups({
77 parent: `projects/${project}/locations/us-central1`,
78 reservationGroupId: "alchemy-bq-rg-fairness-probe",
79 body: {},
80 }),
81 );
82 expect(error._tag).toEqual("ReservationBasedFairnessRequired");
83
84 yield* stack.destroy();
85 }).pipe(logLevel),
86 {
87 tags: ["provider:gcp", "provider:gcp:bigqueryreservation", "live"],
88 timeout: 90_000,
89 },
90);
91
92test.provider.skipIf(!runLifecycle)(
93 "create, re-deploy, and delete a reservation group",
94 (stack) =>
95 Effect.gen(function* () {
96 yield* stack.destroy();
97
98 const created = yield* stack.deploy(
99 Effect.gen(function* () {
100 return yield* GCP.BigQueryReservation.ReservationGroup("Team", {
101 location: "us-central1",
102 });
103 }),
104 );
105
106 expect(created.name).toContain("/reservationGroups/");
107 expect(created.reservationGroupId).toEqual(expect.any(String));
108 expect(created.reservationGroupId.startsWith("alch-")).toEqual(true);
109 expect(created.location).toEqual("us-central1");
110
111 const fetched =
112 yield* bigqueryreservation.getProjectsLocationsReservationGroups({
113 name: created.name,
114 });
115 expect(fetched.name).toEqual(created.name);
116
117 const listed =
118 yield* bigqueryreservation.listProjectsLocationsReservationGroups({
119 parent: `projects/${created.project}/locations/${created.location}`,
120 });
121 expect(
122 (listed.reservationGroups ?? []).some(
123 (item) => item.name === created.name,
124 ),
125 ).toEqual(true);
126
127 const updated = yield* stack.deploy(
128 Effect.gen(function* () {
129 return yield* GCP.BigQueryReservation.ReservationGroup("Team", {
130 reservationGroupId: created.reservationGroupId,
131 location: "us-central1",
132 });
133 }),
134 );
135
136 expect(updated.name).toEqual(created.name);
137 expect(updated.reservationGroupId).toEqual(created.reservationGroupId);
138
139 yield* stack.destroy();
140
141 const gone = yield* waitUntilGone(created.name);
142 expect(gone).toEqual("gone");
143 }).pipe(logLevel),
144 {
145 tags: ["provider:gcp", "provider:gcp:bigqueryreservation", "live"],
146 timeout: 90_000,
147 },
148);