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";
8
9const { test } = Test.make({ providers: GCP.providers() });
10
11// Folder-scoped: set GOOGLE_FOLDER_ID when the credentials administer a
12// folder (the testing project sits directly under the organization).
13const folderId = process.env.GOOGLE_FOLDER_ID?.trim().replace(/^folders\//, "");
14
15const logLevel = Effect.provideService(
16 MinimumLogLevel,
17 process.env.DEBUG ? "Debug" : "Info",
18);
19
20const waitUntilGone = (name: string) =>
21 logging.getFoldersExclusions({ name }).pipe(
22 Effect.as("found" as const),
23 Effect.catchTag("NotFound", () => Effect.succeed("gone" as const)),
24 Effect.repeat({
25 schedule: Schedule.spaced("1 second"),
26 until: (status) => status === "gone",
27 times: 10,
28 }),
29 );
30
31test.provider.skipIf(!folderId)(
32 "getFoldersExclusions on a missing exclusion fails with NotFound",
33 (stack) =>
34 Effect.gen(function* () {
35 yield* stack.destroy();
36
37 const error = yield* Effect.flip(
38 logging.getFoldersExclusions({
39 name: `folders/${folderId}/exclusions/alchemy-missing`,
40 }),
41 );
42 expect(error._tag).toEqual("NotFound");
43
44 yield* stack.destroy();
45 }).pipe(logLevel),
46 { tags: ["provider:gcp", "provider:gcp:logging", "live"], timeout: 90_000 },
47);
48
49test.provider.skipIf(!folderId)(
50 "create, update, replace, and delete a folder exclusion",
51 (stack) =>
52 Effect.gen(function* () {
53 yield* stack.destroy();
54
55 const created = yield* stack.deploy(
56 Effect.gen(function* () {
57 return yield* GCP.Logging.FolderExclusion("DropDebug", {
58 folderId,
59 filter: "severity=DEBUG",
60 description: "drop debug entries",
61 });
62 }),
63 );
64
65 expect(created.exclusionId).toEqual(expect.any(String));
66 expect(created.folderId).toEqual(folderId);
67 expect(created.name).toEqual(
68 `folders/${folderId}/exclusions/${created.exclusionId}`,
69 );
70 expect(created.filter).toEqual("severity=DEBUG");
71 expect(created.description).toEqual("drop debug entries");
72
73 const fetched = yield* logging.getFoldersExclusions({
74 name: created.name,
75 });
76 expect(fetched.filter).toEqual("severity=DEBUG");
77 expect(fetched.description).toContain("alchemy-id=");
78
79 const updated = yield* stack.deploy(
80 Effect.gen(function* () {
81 return yield* GCP.Logging.FolderExclusion("DropDebug", {
82 folderId,
83 exclusionId: created.exclusionId,
84 filter: "severity<ERROR",
85 description: "drop non-errors",
86 disabled: true,
87 });
88 }),
89 );
90
91 expect(updated.filter).toEqual("severity<ERROR");
92 expect(updated.disabled).toEqual(true);
93
94 const last = created.exclusionId.at(-1) ?? "a";
95 const nextExclusionId = `${created.exclusionId.slice(0, -1)}${last === "z" ? "0" : "z"}`;
96
97 const replaced = yield* stack.deploy(
98 Effect.gen(function* () {
99 return yield* GCP.Logging.FolderExclusion("DropDebug", {
100 folderId,
101 exclusionId: nextExclusionId,
102 filter: "severity=DEBUG",
103 description: "replaced exclusion",
104 });
105 }),
106 );
107
108 expect(replaced.exclusionId).not.toEqual(created.exclusionId);
109
110 const previousGone = yield* waitUntilGone(created.name);
111 expect(previousGone).toEqual("gone");
112
113 yield* stack.destroy();
114
115 const gone = yield* waitUntilGone(replaced.name);
116 expect(gone).toEqual("gone");
117 }).pipe(logLevel),
118 { tags: ["provider:gcp", "provider:gcp:logging", "live"], timeout: 90_000 },
119);