1import * as GCP from "@/GCP";
2import * as Test from "@/Test/Alchemy";
3import * as monitoring from "@distilled.cloud/gcp/monitoring_v3";
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
11const logLevel = Effect.provideService(
12 MinimumLogLevel,
13 process.env.DEBUG ? "Debug" : "Info",
14);
15
16const waitUntilGone = (name: string) =>
17 monitoring.getProjectsNotificationChannels({ name }).pipe(
18 Effect.as("found" as const),
19 Effect.catchTag("NotFound", () => Effect.succeed("gone" as const)),
20 Effect.repeat({
21 schedule: Schedule.spaced("1 second"),
22 until: (status) => status === "gone",
23 times: 10,
24 }),
25 );
26
27test.provider(
28 "create, update, replace, and delete a notification channel",
29 (stack) =>
30 Effect.gen(function* () {
31 yield* stack.destroy();
32
33 const created = yield* stack.deploy(
34 Effect.gen(function* () {
35 return yield* GCP.Monitoring.NotificationChannel("Alerts", {
36 type: "email",
37 description: "oncall",
38 labels: { email_address: "alchemy-test@example.com" },
39 userLabels: { env: "test" },
40 });
41 }),
42 );
43
44 expect(created.name).toContain("/notificationChannels/");
45 expect(created.notificationChannelId).toEqual(expect.any(String));
46 expect(created.type).toEqual("email");
47 expect(created.description).toEqual("oncall");
48 expect(created.labels.email_address).toEqual("alchemy-test@example.com");
49 expect(created.userLabels).toMatchObject({ env: "test" });
50 expect(created.enabled).toEqual(true);
51
52 const fetched = yield* monitoring.getProjectsNotificationChannels({
53 name: created.name,
54 });
55 expect(fetched.name).toEqual(created.name);
56 expect(fetched.type).toEqual("email");
57 expect(fetched.labels?.email_address).toEqual("alchemy-test@example.com");
58 expect(fetched.userLabels?.env).toEqual("test");
59 expect(
60 Object.keys(fetched.userLabels ?? {}).some((key) =>
61 key.startsWith("alchemy-"),
62 ),
63 ).toEqual(true);
64
65 const updated = yield* stack.deploy(
66 Effect.gen(function* () {
67 return yield* GCP.Monitoring.NotificationChannel("Alerts", {
68 type: "email",
69 displayName: created.displayName,
70 description: "oncall v2",
71 labels: { email_address: "alchemy-alerts@example.com" },
72 userLabels: { env: "prod", role: "alerts" },
73 enabled: false,
74 });
75 }),
76 );
77
78 expect(updated.name).toEqual(created.name);
79 expect(updated.description).toEqual("oncall v2");
80 expect(updated.labels.email_address).toEqual(
81 "alchemy-alerts@example.com",
82 );
83 expect(updated.userLabels).toMatchObject({
84 env: "prod",
85 role: "alerts",
86 });
87 expect(updated.enabled).toEqual(false);
88
89 const fetchedUpdate = yield* monitoring.getProjectsNotificationChannels({
90 name: updated.name,
91 });
92 expect(fetchedUpdate.description).toEqual("oncall v2");
93 expect(fetchedUpdate.enabled).toEqual(false);
94 expect(fetchedUpdate.labels?.email_address).toEqual(
95 "alchemy-alerts@example.com",
96 );
97
98 const replaced = yield* stack.deploy(
99 Effect.gen(function* () {
100 return yield* GCP.Monitoring.NotificationChannel("Alerts", {
101 type: "webhook_tokenauth",
102 displayName: created.displayName,
103 labels: { url: "https://example.com/hooks/alerts" },
104 userLabels: { env: "prod" },
105 });
106 }),
107 );
108
109 expect(replaced.type).toEqual("webhook_tokenauth");
110 expect(replaced.labels.url).toEqual("https://example.com/hooks/alerts");
111 expect(replaced.name).not.toEqual(created.name);
112
113 const oldGone = yield* waitUntilGone(created.name);
114 expect(oldGone).toEqual("gone");
115
116 const fetchedReplace = yield* monitoring.getProjectsNotificationChannels({
117 name: replaced.name,
118 });
119 expect(fetchedReplace.type).toEqual("webhook_tokenauth");
120 expect(fetchedReplace.labels?.url).toEqual(
121 "https://example.com/hooks/alerts",
122 );
123
124 yield* stack.destroy();
125
126 const gone = yield* waitUntilGone(replaced.name);
127 expect(gone).toEqual("gone");
128 }).pipe(logLevel),
129 {
130 tags: ["provider:gcp", "provider:gcp:monitoring", "live"],
131 timeout: 90_000,
132 },
133);