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";
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
17const location = "us-central1";
18
19const inspect = (infoTypes: string[]) => ({
20 infoTypes: infoTypes.map((name) => ({ name })),
21 includeQuote: true,
22});
23
24const waitUntilGone = (name: string) =>
25 dlp.getProjectsLocationsInspectTemplates({ 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 "getProjectsLocationsInspectTemplates on a missing template fails with a typed tag",
37 (stack) =>
38 Effect.gen(function* () {
39 const { project } = yield* GcpEnvironment.current;
40 yield* stack.destroy();
41
42 const error = yield* Effect.flip(
43 dlp.getProjectsLocationsInspectTemplates({
44 name: `projects/${project}/locations/${location}/inspectTemplates/alchemy-missing`,
45 }),
46 );
47 expect(error._tag).toEqual("NotFound");
48
49 yield* stack.destroy();
50 }).pipe(logLevel),
51 { tags: ["provider:gcp", "provider:gcp:dlp", "live"], timeout: 90_000 },
52);
53
54test.provider(
55 "create, update, replace, and delete a location inspect template",
56 (stack) =>
57 Effect.gen(function* () {
58 const { project } = yield* GcpEnvironment.current;
59 yield* stack.destroy();
60
61 const created = yield* stack.deploy(
62 Effect.gen(function* () {
63 return yield* GCP.DLP.LocationsInspectTemplate("Emails", {
64 location,
65 displayName: "emails",
66 description: "find email addresses",
67 inspectConfig: inspect(["EMAIL_ADDRESS"]),
68 });
69 }),
70 );
71
72 expect(created.templateId).toEqual(expect.any(String));
73 expect(created.location).toEqual(location);
74 expect(created.name).toEqual(
75 `projects/${project}/locations/${location}/inspectTemplates/${created.templateId}`,
76 );
77 expect(created.project).toEqual(project);
78 expect(created.displayName).toEqual("emails");
79 expect(created.description).toEqual("find email addresses");
80 expect(created.allowLimitedAvailabilityInfoTypes).toEqual(false);
81
82 const fetched = yield* dlp.getProjectsLocationsInspectTemplates({
83 name: created.name,
84 });
85 expect(fetched.name).toEqual(created.name);
86 expect(fetched.description).toContain("alchemy-id=");
87 expect(fetched.description).toContain("find email addresses");
88
89 const updated = yield* stack.deploy(
90 Effect.gen(function* () {
91 return yield* GCP.DLP.LocationsInspectTemplate("Emails", {
92 templateId: created.templateId,
93 location,
94 displayName: "emails and phones",
95 description: "find emails and phones",
96 inspectConfig: inspect(["EMAIL_ADDRESS", "PHONE_NUMBER"]),
97 });
98 }),
99 );
100
101 expect(updated.name).toEqual(created.name);
102 expect(updated.displayName).toEqual("emails and phones");
103 expect(updated.description).toEqual("find emails and phones");
104
105 const last = created.templateId.at(-1) ?? "a";
106 const nextId = `${created.templateId.slice(0, -1)}${last === "z" ? "0" : "z"}`;
107
108 const replaced = yield* stack.deploy(
109 Effect.gen(function* () {
110 return yield* GCP.DLP.LocationsInspectTemplate("Emails", {
111 templateId: nextId,
112 location,
113 displayName: "replaced",
114 inspectConfig: inspect(["EMAIL_ADDRESS"]),
115 });
116 }),
117 );
118
119 expect(replaced.templateId).not.toEqual(created.templateId);
120
121 const previousGone = yield* waitUntilGone(created.name);
122 expect(previousGone).toEqual("gone");
123
124 yield* stack.destroy();
125
126 const gone = yield* waitUntilGone(replaced.name);
127 expect(gone).toEqual("gone");
128 }).pipe(logLevel),
129 { tags: ["provider:gcp", "provider:gcp:dlp", "live"], timeout: 90_000 },
130);