1import * as GCP from "@/GCP";
2import { GcpEnvironment } from "@/GCP/Environment";
3import * as Test from "@/Test/Alchemy";
4import * as dataplex from "@distilled.cloud/gcp/dataplex_v1";
5import * as GcpRetry from "@distilled.cloud/gcp/Retry";
6import { expect } from "alchemy-test";
7import * as Effect from "effect/Effect";
8import { MinimumLogLevel } from "effect/References";
9import * as Schedule from "effect/Schedule";
10import { withDataplexSlot } from "./quota.ts";
11
12const { test } = Test.make({ providers: GCP.providers() });
13
14const logLevel = Effect.provideService(
15 MinimumLogLevel,
16 process.env.DEBUG ? "Debug" : "Info",
17);
18
19const waitUntilGone = (name: string) =>
20 dataplex.getProjectsLocationsDataTaxonomies({ name }).pipe(
21 Effect.as("found" as const),
22 Effect.catchTag("NotFound", () => Effect.succeed("gone" as const)),
23 Effect.repeat({
24 schedule: Schedule.spaced("1 second"),
25 until: (status) => status === "gone",
26 times: 10,
27 }),
28 );
29
30test.provider(
31 "creating a data taxonomy returns InternalServerError (API sunset)",
32 () =>
33 Effect.gen(function* () {
34 const { project } = yield* GcpEnvironment.current;
35 const error = yield* Effect.flip(
36 dataplex
37 .createProjectsLocationsDataTaxonomies({
38 parent: `projects/${project}/locations/us-central1`,
39 dataTaxonomyId: "alchemy-probe-taxonomy",
40 validateOnly: true,
41 body: { displayName: "probe" },
42 })
43 .pipe(GcpRetry.none),
44 );
45 expect(error._tag).toEqual("InternalServerError");
46 }).pipe(logLevel, withDataplexSlot),
47 { tags: ["provider:gcp", "provider:gcp:dataplex", "live"], timeout: 900_000 },
48);
49
50// Data taxonomies are sunset: creates fail with InternalServerError (probe
51// above). Set GCP_TEST_DATATAXONOMY=1 on a project that still has access.
52test.provider.skipIf(!process.env.GCP_TEST_DATATAXONOMY)(
53 "create, update, and delete a data taxonomy",
54 (stack) =>
55 Effect.gen(function* () {
56 yield* stack.destroy();
57
58 const created = yield* stack.deploy(
59 Effect.gen(function* () {
60 return yield* GCP.Dataplex.DataTaxonomy("Pii", {
61 location: "us-central1",
62 displayName: "pii a",
63 description: "taxonomy a",
64 labels: { env: "test" },
65 });
66 }),
67 );
68
69 expect(created.name).toContain("/dataTaxonomies/");
70 expect(created.dataTaxonomyId).toEqual(expect.any(String));
71 expect(created.location).toEqual("us-central1");
72 expect(created.displayName).toEqual("pii a");
73 expect(created.description).toEqual("taxonomy a");
74 expect(created.labels).toMatchObject({ env: "test" });
75
76 const fetched = yield* dataplex.getProjectsLocationsDataTaxonomies({
77 name: created.name,
78 });
79 expect(fetched.name).toEqual(created.name);
80 expect(fetched.labels?.env).toEqual("test");
81 expect(
82 Object.keys(fetched.labels ?? {}).some((key) =>
83 key.startsWith("alchemy-"),
84 ),
85 ).toEqual(true);
86
87 const updated = yield* stack.deploy(
88 Effect.gen(function* () {
89 return yield* GCP.Dataplex.DataTaxonomy("Pii", {
90 dataTaxonomyId: created.dataTaxonomyId,
91 location: "us-central1",
92 displayName: "pii b",
93 description: "taxonomy b",
94 labels: { env: "prod", team: "data" },
95 });
96 }),
97 );
98
99 expect(updated.name).toEqual(created.name);
100 expect(updated.displayName).toEqual("pii b");
101 expect(updated.description).toEqual("taxonomy b");
102 expect(updated.labels).toMatchObject({ env: "prod", team: "data" });
103
104 yield* stack.destroy();
105 const gone = yield* waitUntilGone(created.name);
106 expect(gone).toEqual("gone");
107 }).pipe(logLevel, withDataplexSlot),
108 { tags: ["provider:gcp", "provider:gcp:dataplex", "live"], timeout: 900_000 },
109);