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 runLifecycle = !process.env.FAST;
18
19const pausedTarget = () => ({
20 cloudStorageTarget: {
21 filter: { others: {} },
22 disabled: {},
23 },
24});
25
26const waitUntilGone = (name: string) =>
27 dlp.getProjectsLocationsDiscoveryConfigs({ 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(
38 "getProjectsLocationsDiscoveryConfigs on a missing config fails with a typed tag",
39 (stack) =>
40 Effect.gen(function* () {
41 const { project } = yield* GcpEnvironment.current;
42 yield* stack.destroy();
43
44 const error = yield* Effect.flip(
45 dlp.getProjectsLocationsDiscoveryConfigs({
46 name: `projects/${project}/locations/us/discoveryConfigs/alchemy-missing`,
47 }),
48 );
49 expect(error._tag).toEqual("NotFound");
50
51 yield* stack.destroy();
52 }).pipe(logLevel),
53 { tags: ["provider:gcp", "provider:gcp:dlp", "live"], timeout: 90_000 },
54);
55
56test.provider.skipIf(!runLifecycle)(
57 "create, update, and delete a paused discovery config",
58 (stack) =>
59 Effect.gen(function* () {
60 yield* stack.destroy();
61
62 const created = yield* stack.deploy(
63 Effect.gen(function* () {
64 const template = yield* GCP.DLP.LocationsInspectTemplate(
65 "EmailInspect",
66 {
67 location: "us",
68 displayName: "emails",
69 inspectConfig: { infoTypes: [{ name: "EMAIL_ADDRESS" }] },
70 },
71 );
72 return yield* GCP.DLP.DiscoveryConfig("Profiles", {
73 location: "us",
74 displayName: "paused storage",
75 status: "PAUSED",
76 inspectTemplates: [template.name],
77 targets: [pausedTarget()],
78 });
79 }),
80 );
81
82 expect(created.configId).toEqual(expect.any(String));
83 expect(created.location).toEqual("us");
84 expect(created.name).toContain("/discoveryConfigs/");
85 expect(created.displayName).toEqual("paused storage");
86 expect(created.status).toEqual("PAUSED");
87
88 const fetched = yield* dlp.getProjectsLocationsDiscoveryConfigs({
89 name: created.name,
90 });
91 expect(fetched.name).toEqual(created.name);
92 expect(fetched.displayName).toContain("alchemy-");
93
94 const updated = yield* stack.deploy(
95 Effect.gen(function* () {
96 const template = yield* GCP.DLP.LocationsInspectTemplate(
97 "EmailInspect",
98 {
99 location: "us",
100 displayName: "emails",
101 inspectConfig: { infoTypes: [{ name: "EMAIL_ADDRESS" }] },
102 },
103 );
104 return yield* GCP.DLP.DiscoveryConfig("Profiles", {
105 configId: created.configId,
106 location: "us",
107 displayName: "paused storage v2",
108 status: "PAUSED",
109 inspectTemplates: [template.name],
110 targets: [pausedTarget()],
111 });
112 }),
113 );
114
115 expect(updated.name).toEqual(created.name);
116 expect(updated.displayName).toEqual("paused storage v2");
117
118 yield* stack.destroy();
119
120 const gone = yield* waitUntilGone(created.name);
121 expect(gone).toEqual("gone");
122 }).pipe(logLevel),
123 { tags: ["provider:gcp", "provider:gcp:dlp", "live"], timeout: 90_000 },
124);