1import { CloudflareEnvironment } from "@/Cloudflare/CloudflareEnvironment";
2import * as Cloudflare from "@/Cloudflare/index.ts";
3import { WorkerPreviewConfigError } from "@/Cloudflare/Workers/WorkerProvider.ts";
4import * as Test from "@/Test/Alchemy";
5import * as workers from "@distilled.cloud/cloudflare/workers";
6import { describe, expect } from "alchemy-test";
7import * as Effect from "effect/Effect";
8import { MinimumLogLevel } from "effect/References";
9import { expectUrlContains } from "../Utils/Http.ts";
10
11const { test } = Test.make({ providers: Cloudflare.providers() });
12
13const logLevel = Effect.provideService(
14 MinimumLogLevel,
15 process.env.DEBUG ? "Debug" : "Info",
16);
17
18const script = (marker: string) =>
19 `export default { fetch() { return new Response("${marker}"); } };`;
20
21describe.concurrent(
22 "Cloudflare.Worker preview",
23 { tags: ["provider:cloudflare", "provider:cloudflare:worker", "live"] },
24 () => {
25 test.provider(
26 "preview of a parent worker, updated, then deleted",
27 (stack) =>
28 Effect.gen(function* () {
29 yield* stack.destroy();
30
31 const v1 = yield* stack.deploy(
32 Effect.gen(function* () {
33 const parent = yield* Cloudflare.Worker("PreviewParent", {
34 script: script("parent-marker-v1"),
35 });
36 const preview = yield* Cloudflare.Worker("PreviewChild", {
37 script: script("preview-marker-v1"),
38 preview: { of: parent, message: "alchemy preview test" },
39 });
40 return { parent, preview };
41 }),
42 );
43
44 expect(v1.preview.previewOf).toEqual(v1.parent.workerName);
45 expect(v1.preview.workerName).toEqual(v1.parent.workerName);
46 expect(v1.preview.previewId).toBeDefined();
47 expect(v1.preview.previewName).toBeDefined();
48 expect(v1.preview.previewSlug).toBeDefined();
49 expect(v1.preview.deploymentId).toBeDefined();
50 expect(v1.preview.url).toBeDefined();
51 expect(v1.preview.url).toContain(
52 `${v1.preview.previewSlug}-${v1.parent.workerName}.`,
53 );
54 expect(v1.preview.versionOf).toBeUndefined();
55
56 yield* expectUrlContains(v1.parent.url!, "parent-marker-v1", {
57 label: "parent serves its own code",
58 });
59 yield* expectUrlContains(v1.preview.url!, "preview-marker-v1", {
60 label: "Preview URL serves the Preview's code",
61 });
62
63 const v2 = yield* stack.deploy(
64 Effect.gen(function* () {
65 const parent = yield* Cloudflare.Worker("PreviewParent", {
66 script: script("parent-marker-v1"),
67 });
68 const preview = yield* Cloudflare.Worker("PreviewChild", {
69 script: script("preview-marker-v2"),
70 preview: { of: parent },
71 });
72 return { parent, preview };
73 }),
74 );
75
76 expect(v2.preview.previewId).toEqual(v1.preview.previewId);
77 expect(v2.preview.previewName).toEqual(v1.preview.previewName);
78 expect(v2.preview.url).toEqual(v1.preview.url);
79 yield* expectUrlContains(v2.preview.url!, "preview-marker-v2", {
80 label: "updated Preview serves new code at the same URL",
81 });
82 yield* expectUrlContains(v2.parent.url!, "parent-marker-v1", {
83 label: "parent is untouched by the Preview update",
84 });
85
86 yield* stack.destroy();
87
88 const { accountId } = yield* yield* CloudflareEnvironment;
89 const gone = yield* workers
90 .getPreview({
91 accountId,
92 workerId: v1.parent.workerName,
93 previewId: v1.preview.previewId!,
94 })
95 .pipe(
96 Effect.map(() => false),
97 Effect.catchTag("PreviewNotFound", () => Effect.succeed(true)),
98 Effect.catchTag("WorkerNotFound", () => Effect.succeed(true)),
99 );
100 expect(gone).toBe(true);
101 }).pipe(logLevel),
102 { timeout: 240_000 },
103 );
104
105 test.provider(
106 "rejects preview combined with version.parent",
107 (stack) =>
108 Effect.gen(function* () {
109 yield* stack.destroy();
110
111 const { parent } = yield* stack.deploy(
112 Effect.gen(function* () {
113 const parent = yield* Cloudflare.Worker("ComboParent", {
114 script: script("combo-parent"),
115 });
116 return { parent };
117 }),
118 );
119
120 const error = yield* stack
121 .deploy(
122 Effect.gen(function* () {
123 yield* Cloudflare.Worker("ComboParent", {
124 script: script("combo-parent"),
125 });
126 return yield* Cloudflare.Worker("ComboPreview", {
127 script: script("combo-preview"),
128 preview: { of: parent.workerName },
129 version: { parent: parent.workerName },
130 });
131 }),
132 )
133 .pipe(Effect.flip);
134
135 expect(error).toBeInstanceOf(WorkerPreviewConfigError);
136 expect(String(error)).toContain("preview and version cannot be set");
137
138 yield* stack.destroy();
139 }).pipe(logLevel),
140 { timeout: 180_000 },
141 );
142
143 test.provider(
144 "rejects script-level settings on a Preview worker",
145 (stack) =>
146 Effect.gen(function* () {
147 yield* stack.destroy();
148
149 const { parent } = yield* stack.deploy(
150 Effect.gen(function* () {
151 const parent = yield* Cloudflare.Worker("SettingsParent", {
152 script: script("settings-parent"),
153 });
154 return { parent };
155 }),
156 );
157
158 const error = yield* stack
159 .deploy(
160 Effect.gen(function* () {
161 yield* Cloudflare.Worker("SettingsParent", {
162 script: script("settings-parent"),
163 });
164 return yield* Cloudflare.Worker("SettingsPreview", {
165 script: script("settings-preview"),
166 preview: { of: parent.workerName },
167 crons: ["*/5 * * * *"],
168 });
169 }),
170 )
171 .pipe(Effect.flip);
172
173 expect(error).toBeInstanceOf(WorkerPreviewConfigError);
174 expect(String(error)).toContain("script-level settings");
175
176 yield* stack.destroy();
177 }).pipe(logLevel),
178 { timeout: 180_000 },
179 );
180 },
181);