1import * as GCP from "@/GCP";
2import * as Test from "@/Test/Alchemy";
3import * as scheduler from "@distilled.cloud/gcp/cloudscheduler_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
16const waitUntilGone = (name: string) =>
17 scheduler.getProjectsLocationsJobs({ 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 scheduler job",
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.CloudScheduler.Job("Ping", {
36 schedule: "0 0 1 1 *",
37 timeZone: "UTC",
38 description: "ping",
39 httpTarget: {
40 uri: "https://example.com/",
41 httpMethod: "GET",
42 },
43 });
44 }),
45 );
46
47 expect(created.jobId).toEqual(expect.any(String));
48 expect(created.name).toContain("/jobs/");
49 expect(created.location).toEqual("us-central1");
50 expect(created.schedule).toEqual("0 0 1 1 *");
51 expect(created.timeZone).toEqual("UTC");
52 expect(created.description).toEqual("ping");
53 expect(created.paused).toEqual(false);
54 expect(created.httpTarget?.uri).toEqual("https://example.com/");
55 expect(created.httpTarget?.httpMethod).toEqual("GET");
56
57 const fetched = yield* scheduler.getProjectsLocationsJobs({
58 name: created.name,
59 });
60 expect(fetched.name).toEqual(created.name);
61 expect(fetched.schedule).toEqual("0 0 1 1 *");
62 expect(fetched.state).toEqual("ENABLED");
63 expect(fetched.description).toEqual("ping");
64 expect(fetched.httpTarget?.uri).toEqual("https://example.com/");
65
66 const updated = yield* stack.deploy(
67 Effect.gen(function* () {
68 return yield* GCP.CloudScheduler.Job("Ping", {
69 jobId: created.jobId,
70 location: "us-central1",
71 schedule: "0 0 1 2 *",
72 timeZone: "America/Chicago",
73 description: "ping v2",
74 paused: true,
75 retryConfig: { retryCount: 2 },
76 httpTarget: {
77 uri: "https://example.com/health",
78 httpMethod: "GET",
79 },
80 });
81 }),
82 );
83
84 expect(updated.name).toEqual(created.name);
85 expect(updated.jobId).toEqual(created.jobId);
86 expect(updated.schedule).toEqual("0 0 1 2 *");
87 expect(updated.timeZone).toEqual("America/Chicago");
88 expect(updated.description).toEqual("ping v2");
89 expect(updated.retryConfig?.retryCount).toEqual(2);
90 expect(updated.paused).toEqual(true);
91 expect(updated.httpTarget?.uri).toEqual("https://example.com/health");
92
93 const fetchedUpdate = yield* scheduler.getProjectsLocationsJobs({
94 name: updated.name,
95 });
96 expect(fetchedUpdate.schedule).toEqual("0 0 1 2 *");
97 expect(fetchedUpdate.timeZone).toEqual("America/Chicago");
98 expect(fetchedUpdate.retryConfig?.retryCount).toEqual(2);
99 expect(fetchedUpdate.httpTarget?.uri).toEqual(
100 "https://example.com/health",
101 );
102 expect(fetchedUpdate.state).toEqual("PAUSED");
103
104 const replaced = yield* stack.deploy(
105 Effect.gen(function* () {
106 return yield* GCP.CloudScheduler.Job("Ping", {
107 jobId: created.jobId,
108 location: "us-east1",
109 schedule: "0 0 1 1 *",
110 httpTarget: {
111 uri: "https://example.com/",
112 httpMethod: "GET",
113 },
114 });
115 }),
116 );
117
118 expect(replaced.jobId).toEqual(created.jobId);
119 expect(replaced.location).toEqual("us-east1");
120 expect(replaced.name).toContain("/locations/us-east1/");
121 expect(replaced.name).not.toEqual(created.name);
122
123 const oldGone = yield* waitUntilGone(created.name);
124 expect(oldGone).toEqual("gone");
125
126 const fetchedReplace = yield* scheduler.getProjectsLocationsJobs({
127 name: replaced.name,
128 });
129 expect(fetchedReplace.name).toEqual(replaced.name);
130 expect(fetchedReplace.state).toEqual("ENABLED");
131
132 yield* stack.destroy();
133
134 const gone = yield* waitUntilGone(replaced.name);
135 expect(gone).toEqual("gone");
136 }).pipe(logLevel),
137 {
138 tags: ["provider:gcp", "provider:gcp:cloudscheduler", "live"],
139 timeout: 120_000,
140 },
141);