1import * as Cloudflare from "@/Cloudflare";
2import * as Test from "@/Test/Alchemy";
3import { expect } from "alchemy-test";
4import * as Data from "effect/Data";
5import * as Effect from "effect/Effect";
6import * as Schedule from "effect/Schedule";
7import * as HttpClient from "effect/http/HttpClient";
8import Stack from "./fixtures/stack.ts";
9
10const { test, beforeAll, afterAll, deploy, destroy } = Test.make({
11 providers: Cloudflare.providers(),
12});
13
14class WorkerNotReady extends Data.TaggedError("WorkerNotReady")<{
15 status: number;
16 body: string;
17}> {}
18
19const readJson = (url: string) =>
20 HttpClient.HttpClient.pipe(
21 Effect.flatMap((client) => client.get(url)),
22 Effect.flatMap((res) =>
23 res.status === 200
24 ? res.json
25 : res.text.pipe(
26 Effect.flatMap((body) =>
27 Effect.fail(new WorkerNotReady({ status: res.status, body })),
28 ),
29 ),
30 ),
31 ).pipe(
32 Effect.retry({
33 while: (e): e is WorkerNotReady => e instanceof WorkerNotReady,
34 schedule: Schedule.max([
35 Schedule.exponential("500 millis"),
36 Schedule.recurs(20),
37 ]),
38 }),
39 );
40
41const stack = beforeAll(deploy(Stack));
42afterAll.skipIf(!!process.env.NO_DESTROY)(destroy(Stack));
43
44test(
45 "async worker renders a page title through Browser Rendering",
46 Effect.gen(function* () {
47 const { asyncWorkerUrl } = yield* stack;
48 const body = (yield* readJson(`${asyncWorkerUrl}/title`)) as {
49 mode: string;
50 title: string;
51 };
52
53 expect(body.mode).toBe("async");
54 expect(body.title).toBe("Example Domain");
55 }),
56 {
57 tags: ["provider:cloudflare", "provider:cloudflare:worker", "live"],
58 timeout: 180_000,
59 },
60);
61
62test(
63 "effect worker exercises content via quickAction wrapper",
64 Effect.gen(function* () {
65 const { effectWorkerUrl } = yield* stack;
66 const body = (yield* readJson(`${effectWorkerUrl}/content`)) as {
67 title: string;
68 contentLength: number;
69 };
70
71 expect(body.title).toBe("Example Domain");
72 expect(body.contentLength).toBeGreaterThan(0);
73 }),
74 {
75 tags: ["provider:cloudflare", "provider:cloudflare:worker", "live"],
76 timeout: 180_000,
77 },
78);
79
80test(
81 "effect worker converts a page to markdown",
82 Effect.gen(function* () {
83 const { effectWorkerUrl } = yield* stack;
84 const body = (yield* readJson(`${effectWorkerUrl}/markdown`)) as {
85 markdownLength: number;
86 };
87
88 expect(body.markdownLength).toBeGreaterThan(0);
89 }),
90 {
91 tags: ["provider:cloudflare", "provider:cloudflare:worker", "live"],
92 timeout: 180_000,
93 },
94);
95
96test(
97 "effect worker extracts links",
98 Effect.gen(function* () {
99 const { effectWorkerUrl } = yield* stack;
100 const body = (yield* readJson(`${effectWorkerUrl}/links`)) as {
101 linkCount: number;
102 };
103
104 expect(body.linkCount).toBeGreaterThan(0);
105 }),
106 {
107 tags: ["provider:cloudflare", "provider:cloudflare:worker", "live"],
108 timeout: 180_000,
109 },
110);
111
112test(
113 "effect worker scrapes elements by selector",
114 Effect.gen(function* () {
115 const { effectWorkerUrl } = yield* stack;
116 const body = (yield* readJson(`${effectWorkerUrl}/scrape`)) as {
117 text: string | null;
118 };
119
120 expect(body.text).toBe("Learn more");
121 }),
122 {
123 tags: ["provider:cloudflare", "provider:cloudflare:worker", "live"],
124 timeout: 180_000,
125 },
126);
127
128test(
129 "effect worker takes a page snapshot",
130 Effect.gen(function* () {
131 const { effectWorkerUrl } = yield* stack;
132 const body = (yield* readJson(`${effectWorkerUrl}/snapshot`)) as {
133 title: string;
134 screenshotLength: number;
135 };
136
137 expect(body.title).toBe("Example Domain");
138 expect(body.screenshotLength).toBeGreaterThan(0);
139 }),
140 {
141 tags: ["provider:cloudflare", "provider:cloudflare:worker", "live"],
142 timeout: 180_000,
143 },
144);
145
146test(
147 "effect worker streams a screenshot",
148 Effect.gen(function* () {
149 const { effectWorkerUrl } = yield* stack;
150 const body = (yield* readJson(`${effectWorkerUrl}/screenshot`)) as {
151 bytes: number;
152 };
153
154 expect(body.bytes).toBeGreaterThan(0);
155 }),
156 {
157 tags: ["provider:cloudflare", "provider:cloudflare:worker", "live"],
158 timeout: 180_000,
159 },
160);
161
162test(
163 "effect worker streams a PDF",
164 Effect.gen(function* () {
165 const { effectWorkerUrl } = yield* stack;
166 const body = (yield* readJson(`${effectWorkerUrl}/pdf`)) as {
167 bytes: number;
168 };
169
170 expect(body.bytes).toBeGreaterThan(0);
171 }),
172 {
173 tags: ["provider:cloudflare", "provider:cloudflare:worker", "live"],
174 timeout: 180_000,
175 },
176);
177
178test(
179 "effect worker extracts JSON with AI",
180 Effect.gen(function* () {
181 const { effectWorkerUrl } = yield* stack;
182 const body = (yield* readJson(`${effectWorkerUrl}/json`)) as {
183 success: boolean;
184 };
185
186 expect(body.success).toBe(true);
187 }),
188 {
189 tags: ["provider:cloudflare", "provider:cloudflare:worker", "live"],
190 timeout: 180_000,
191 },
192);
193
194test(
195 "effect worker calls the generic quickAction",
196 Effect.gen(function* () {
197 const { effectWorkerUrl } = yield* stack;
198 const body = (yield* readJson(`${effectWorkerUrl}/quickAction`)) as {
199 title: string;
200 };
201
202 expect(body.title).toBe("Example Domain");
203 }),
204 {
205 tags: ["provider:cloudflare", "provider:cloudflare:worker", "live"],
206 timeout: 180_000,
207 },
208);
209
210test(
211 "effect worker exposes the raw BrowserRun binding",
212 Effect.gen(function* () {
213 const { effectWorkerUrl } = yield* stack;
214 const body = (yield* readJson(`${effectWorkerUrl}/raw`)) as {
215 title: string;
216 };
217
218 expect(body.title).toBe("Example Domain");
219 }),
220 {
221 tags: ["provider:cloudflare", "provider:cloudflare:worker", "live"],
222 timeout: 180_000,
223 },
224);