1import * as GCP from "@/GCP";
2import * as Test from "@/Test/Alchemy";
3import * as logging from "@distilled.cloud/gcp/logging_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 waitUntilGone = (name: string) =>
18 logging.getProjectsExclusions({ name }).pipe(
19 Effect.as("found" as const),
20 Effect.catchTag("NotFound", () => Effect.succeed("gone" as const)),
21 Effect.repeat({
22 schedule: Schedule.spaced("1 second"),
23 until: (status) => status === "gone",
24 times: 10,
25 }),
26 );
27
28test.provider(
29 "create, update, replace, and delete a logging exclusion",
30 (stack) =>
31 Effect.gen(function* () {
32 const { project } = yield* GcpEnvironment.current;
33
34 yield* stack.destroy();
35
36 const created = yield* stack.deploy(
37 Effect.gen(function* () {
38 return yield* GCP.Logging.Exclusion("DropDebug", {
39 filter: "severity=DEBUG",
40 description: "drop debug entries",
41 });
42 }),
43 );
44
45 expect(created.exclusionId).toEqual(expect.any(String));
46 expect(created.name).toEqual(
47 `projects/${project}/exclusions/${created.exclusionId}`,
48 );
49 expect(created.project).toEqual(project);
50 expect(created.filter).toEqual("severity=DEBUG");
51 expect(created.description).toEqual("drop debug entries");
52 expect(created.disabled).toEqual(false);
53
54 const fetched = yield* logging.getProjectsExclusions({
55 name: created.name,
56 });
57 expect(fetched.name).toEqual(created.exclusionId);
58 expect(fetched.filter).toEqual("severity=DEBUG");
59 expect(fetched.description).toContain("alchemy-id=");
60 expect(fetched.description).toContain("drop debug entries");
61
62 const updated = yield* stack.deploy(
63 Effect.gen(function* () {
64 return yield* GCP.Logging.Exclusion("DropDebug", {
65 exclusionId: created.exclusionId,
66 filter: "severity<ERROR",
67 description: "drop non-errors",
68 disabled: true,
69 });
70 }),
71 );
72
73 expect(updated.name).toEqual(created.name);
74 expect(updated.exclusionId).toEqual(created.exclusionId);
75 expect(updated.filter).toEqual("severity<ERROR");
76 expect(updated.description).toEqual("drop non-errors");
77 expect(updated.disabled).toEqual(true);
78 expect(updated.createTime).toEqual(created.createTime);
79
80 const fetchedUpdate = yield* logging.getProjectsExclusions({
81 name: created.name,
82 });
83 expect(fetchedUpdate.filter).toEqual("severity<ERROR");
84 expect(fetchedUpdate.disabled).toEqual(true);
85 expect(fetchedUpdate.description).toContain("drop non-errors");
86
87 const last = created.exclusionId.at(-1) ?? "a";
88 const nextExclusionId = `${created.exclusionId.slice(0, -1)}${last === "z" ? "0" : "z"}`;
89
90 const replaced = yield* stack.deploy(
91 Effect.gen(function* () {
92 return yield* GCP.Logging.Exclusion("DropDebug", {
93 exclusionId: nextExclusionId,
94 filter: "severity=DEBUG",
95 description: "replaced exclusion",
96 });
97 }),
98 );
99
100 expect(replaced.exclusionId).not.toEqual(created.exclusionId);
101 expect(replaced.name).toEqual(
102 `projects/${project}/exclusions/${replaced.exclusionId}`,
103 );
104 expect(replaced.description).toEqual("replaced exclusion");
105
106 const fetchedReplacement = yield* logging.getProjectsExclusions({
107 name: replaced.name,
108 });
109 expect(fetchedReplacement.name).toEqual(replaced.exclusionId);
110
111 const previousGone = yield* waitUntilGone(created.name);
112 expect(previousGone).toEqual("gone");
113
114 yield* stack.destroy();
115
116 const gone = yield* waitUntilGone(replaced.name);
117 expect(gone).toEqual("gone");
118 }).pipe(logLevel),
119 { tags: ["provider:gcp", "provider:gcp:logging", "live"], timeout: 90_000 },
120);