1import * as GCP from "@/GCP";
2import * as Test from "@/Test/Alchemy";
3import * as dlp from "@distilled.cloud/gcp/dlp_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
11// Organization-scoped: set GOOGLE_ORGANIZATION_ID when the credentials
12// administer the organization (the testing service account does not).
13const organizationId = process.env.GOOGLE_ORGANIZATION_ID?.trim().replace(
14 /^organizations\//,
15 "",
16);
17const organization = `organizations/${organizationId}`;
18
19const logLevel = Effect.provideService(
20 MinimumLogLevel,
21 process.env.DEBUG ? "Debug" : "Info",
22);
23
24const redactConfig = {
25 infoTypeTransformations: {
26 transformations: [
27 { primitiveTransformation: { replaceWithInfoTypeConfig: {} } },
28 ],
29 },
30};
31
32const waitUntilGone = (name: string) =>
33 dlp.getOrganizationsDeidentifyTemplates({ name }).pipe(
34 Effect.as("found" as const),
35 Effect.catchTag("NotFound", () => Effect.succeed("gone" as const)),
36 Effect.repeat({
37 schedule: Schedule.spaced("1 second"),
38 until: (status) => status === "gone",
39 times: 10,
40 }),
41 );
42
43test.provider.skipIf(!organizationId)(
44 "getOrganizationsDeidentifyTemplates on a missing template fails with a typed tag",
45 (stack) =>
46 Effect.gen(function* () {
47 yield* stack.destroy();
48
49 const error = yield* Effect.flip(
50 dlp.getOrganizationsDeidentifyTemplates({
51 name: `${organization}/deidentifyTemplates/alchemy-missing`,
52 }),
53 );
54 expect(error._tag).toEqual("NotFound");
55
56 yield* stack.destroy();
57 }).pipe(logLevel),
58 { tags: ["provider:gcp", "provider:gcp:dlp", "live"], timeout: 90_000 },
59);
60
61test.provider.skipIf(!organizationId)(
62 "create, update, and delete an organization deidentify template",
63 (stack) =>
64 Effect.gen(function* () {
65 yield* stack.destroy();
66
67 const created = yield* stack.deploy(
68 Effect.gen(function* () {
69 return yield* GCP.DLP.OrganizationsDeidentifyTemplate("RedactEmail", {
70 organization,
71 displayName: "redact-email",
72 description: "redact emails",
73 deidentifyConfig: redactConfig,
74 });
75 }),
76 );
77
78 expect(created.templateId).toEqual(expect.any(String));
79 expect(created.organization).toEqual(organization);
80 expect(created.name).toEqual(
81 `${organization}/deidentifyTemplates/${created.templateId}`,
82 );
83 expect(created.description).toEqual("redact emails");
84
85 const fetched = yield* dlp.getOrganizationsDeidentifyTemplates({
86 name: created.name,
87 });
88 expect(fetched.name).toEqual(created.name);
89 expect(fetched.description).toContain("alchemy-id=");
90
91 const updated = yield* stack.deploy(
92 Effect.gen(function* () {
93 return yield* GCP.DLP.OrganizationsDeidentifyTemplate("RedactEmail", {
94 organization,
95 templateId: created.templateId,
96 displayName: "redact-email-v2",
97 description: "redact emails and phones",
98 deidentifyConfig: redactConfig,
99 });
100 }),
101 );
102
103 expect(updated.name).toEqual(created.name);
104 expect(updated.displayName).toEqual("redact-email-v2");
105 expect(updated.description).toEqual("redact emails and phones");
106
107 yield* stack.destroy();
108
109 const gone = yield* waitUntilGone(created.name);
110 expect(gone).toEqual("gone");
111 }).pipe(logLevel),
112 { tags: ["provider:gcp", "provider:gcp:dlp", "live"], timeout: 90_000 },
113);