1import * as GCP from "@/GCP";
2import * as Test from "@/Test/Alchemy";
3import * as we from "@distilled.cloud/gcp/workspaceevents_v1";
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
16// Push configs attach to an existing Workspace Events task; set
17// GCP_TEST_WORKSPACE_EVENTS_TASK to run the lifecycle.
18const runLifecycle = !!process.env.GCP_TEST_WORKSPACE_EVENTS_TASK;
19
20const waitUntilGone = (name: string) =>
21 we.getTasksPushNotificationConfigs({ 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(
32 "getTasksPushNotificationConfigs on a missing config fails with a typed tag",
33 (stack) =>
34 Effect.gen(function* () {
35 yield* stack.destroy();
36
37 const error = yield* Effect.flip(
38 we.getTasksPushNotificationConfigs({
39 name: "tasks/alchemy-missing-task/pushNotificationConfigs/alchemy-missing",
40 }),
41 );
42 expect(error._tag).toEqual("TasksUnregisteredCaller");
43
44 yield* stack.destroy();
45 }).pipe(logLevel),
46 {
47 tags: ["provider:gcp", "provider:gcp:workspaceevents", "live"],
48 timeout: 90_000,
49 },
50);
51
52test.provider.skipIf(runLifecycle)(
53 "createTasksPushNotificationConfigs without access fails with a typed tag",
54 (stack) =>
55 Effect.gen(function* () {
56 yield* stack.destroy();
57
58 const error = yield* Effect.flip(
59 we.createTasksPushNotificationConfigs({
60 parent: "tasks/alchemy-missing-task",
61 configId: "alchemy-probe",
62 body: {
63 pushNotificationConfig: {
64 url: "https://example.com/workspace-events",
65 },
66 },
67 }),
68 );
69 expect(error._tag).toEqual("NotFound");
70
71 yield* stack.destroy();
72 }).pipe(logLevel),
73 {
74 tags: ["provider:gcp", "provider:gcp:workspaceevents", "live"],
75 timeout: 90_000,
76 },
77);
78
79test.provider.skipIf(!runLifecycle)(
80 "create, replace, and delete a task push notification config",
81 (stack) =>
82 Effect.gen(function* () {
83 yield* stack.destroy();
84
85 const task = process.env.GCP_TEST_WORKSPACE_EVENTS_TASK ?? "";
86
87 const created = yield* stack.deploy(
88 Effect.gen(function* () {
89 return yield* GCP.WorkspaceEvents.TasksPushNotificationConfig(
90 "Updates",
91 {
92 task,
93 url: "https://example.com/workspace-events",
94 },
95 );
96 }),
97 );
98
99 expect(created.name).toContain("/pushNotificationConfigs/");
100 expect(created.configId.length).toBeGreaterThan(0);
101 expect(created.url).toEqual("https://example.com/workspace-events");
102
103 const fetched = yield* we.getTasksPushNotificationConfigs({
104 name: created.name,
105 });
106 expect(fetched.name).toEqual(created.name);
107 expect(fetched.pushNotificationConfig?.token).toContain("[alchemy ");
108
109 const updated = yield* stack.deploy(
110 Effect.gen(function* () {
111 return yield* GCP.WorkspaceEvents.TasksPushNotificationConfig(
112 "Updates",
113 {
114 task,
115 configId: created.configId,
116 url: "https://example.com/workspace-events-v2",
117 },
118 );
119 }),
120 );
121
122 expect(updated.configId).toEqual(created.configId);
123 expect(updated.url).toEqual("https://example.com/workspace-events-v2");
124
125 yield* stack.destroy();
126
127 const gone = yield* waitUntilGone(created.name);
128 expect(gone).toEqual("gone");
129 }).pipe(logLevel),
130 {
131 tags: ["provider:gcp", "provider:gcp:workspaceevents", "live"],
132 timeout: 90_000,
133 },
134);