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 location = "us-central1";
25
26const waitUntilGone = (name: string) =>
27 dlp.getOrganizationsLocationsInspectTemplates({ name }).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
37test.provider.skipIf(!organizationId)(
38 "getOrganizationsLocationsInspectTemplates on a missing template fails with a typed tag",
39 (stack) =>
40 Effect.gen(function* () {
41 yield* stack.destroy();
42
43 const error = yield* Effect.flip(
44 dlp.getOrganizationsLocationsInspectTemplates({
45 name: `${organization}/locations/${location}/inspectTemplates/alchemy-missing`,
46 }),
47 );
48 expect(error._tag).toEqual("NotFound");
49
50 yield* stack.destroy();
51 }).pipe(logLevel),
52 { tags: ["provider:gcp", "provider:gcp:dlp", "live"], timeout: 90_000 },
53);
54
55test.provider.skipIf(!organizationId)(
56 "create, update, and delete a location-scoped organization inspect template",
57 (stack) =>
58 Effect.gen(function* () {
59 yield* stack.destroy();
60
61 const parent = `${organization}/locations/${location}`;
62
63 const created = yield* stack.deploy(
64 Effect.gen(function* () {
65 return yield* GCP.DLP.OrganizationsLocationsInspectTemplate(
66 "Phones",
67 {
68 organization,
69 location,
70 displayName: "phones",
71 description: "detect phones",
72 inspectConfig: {
73 infoTypes: [{ name: "PHONE_NUMBER" }],
74 includeQuote: true,
75 },
76 },
77 );
78 }),
79 );
80
81 expect(created.location).toEqual(location);
82 expect(created.name).toEqual(
83 `${parent}/inspectTemplates/${created.templateId}`,
84 );
85
86 const fetched = yield* dlp.getOrganizationsLocationsInspectTemplates({
87 name: created.name,
88 });
89 expect(fetched.description).toContain("alchemy-id=");
90
91 const updated = yield* stack.deploy(
92 Effect.gen(function* () {
93 return yield* GCP.DLP.OrganizationsLocationsInspectTemplate(
94 "Phones",
95 {
96 organization,
97 location,
98 templateId: created.templateId,
99 displayName: "phones-v2",
100 description: "detect phones v2",
101 inspectConfig: {
102 infoTypes: [{ name: "PHONE_NUMBER" }],
103 includeQuote: false,
104 },
105 },
106 );
107 }),
108 );
109
110 expect(updated.name).toEqual(created.name);
111 expect(updated.displayName).toEqual("phones-v2");
112
113 yield* stack.destroy();
114
115 const gone = yield* waitUntilGone(created.name);
116 expect(gone).toEqual("gone");
117 }).pipe(logLevel),
118 { tags: ["provider:gcp", "provider:gcp:dlp", "live"], timeout: 90_000 },
119);