1import * as Cloudflare from "@/Cloudflare";
2import * as Test from "@/Test/Alchemy";
3import { expect } from "alchemy-test";
4import * as Effect from "effect/Effect";
5import * as Schedule from "effect/Schedule";
6import * as HttpClient from "effect/http/HttpClient";
7import Stack from "./fixtures/worker-entrypoint-binding/stack.ts";
8
9const { test, beforeAll, afterAll, deploy, destroy } = Test.make({
10 providers: Cloudflare.providers(),
11});
12
13const stack = beforeAll(deploy(Stack));
14afterAll.skipIf(!!process.env.NO_DESTROY)(destroy(Stack));
15
16// Cold-start retry — fresh `workers.dev` URLs take a few seconds to start
17// answering, capped at 3s so the doubling sleeps can't blow the timeout.
18const coldStartRetry = Effect.retry({
19 schedule: Schedule.min([
20 Schedule.exponential("500 millis"),
21 Schedule.spaced("3 seconds"),
22 ]),
23 times: 30,
24});
25
26test(
27 "target worker's default entrypoint responds",
28 Effect.gen(function* () {
29 const { targetUrl } = yield* stack;
30 const client = HttpClient.filterStatusOk(yield* HttpClient.HttpClient);
31
32 const res = yield* client.get(targetUrl).pipe(coldStartRetry);
33 expect(yield* res.text).toBe("hello from EntrypointTargetWorker");
34 }),
35 {
36 tags: ["provider:cloudflare", "provider:cloudflare:worker", "live"],
37 timeout: 180_000,
38 },
39);
40
41test(
42 "caller reaches the NAMED entrypoint through the binding",
43 Effect.gen(function* () {
44 const { callerUrl } = yield* stack;
45 const client = HttpClient.filterStatusOk(yield* HttpClient.HttpClient);
46
47 // The default entrypoint has no `greet` — a greeting proves the
48 // binding targeted the named `Api` class.
49 const res = yield* client
50 .get(`${callerUrl}/greet?name=alice`)
51 .pipe(coldStartRetry);
52 expect(yield* res.text).toBe("hello alice from Api");
53 }),
54 {
55 tags: ["provider:cloudflare", "provider:cloudflare:worker", "live"],
56 timeout: 180_000,
57 },
58);
59
60// The Cloudflare API's service-binding schema does not carry `props` yet:
61// the distilled `workers` service drops the field at encode, so deployed
62// bindings deliver no ctx.props. Local dev already delivers them (see
63// WorkerEntrypoint.local.test.ts). Flip this on when the distilled patch
64// lands.
65test.skip(
66 "props reach the named entrypoint's ctx.props",
67 Effect.gen(function* () {
68 const { callerUrl } = yield* stack;
69 const client = HttpClient.filterStatusOk(yield* HttpClient.HttpClient);
70
71 const res = yield* client.get(`${callerUrl}/props`).pipe(coldStartRetry);
72 expect((yield* res.json) as Record<string, unknown>).toEqual({
73 tenant: "acme",
74 });
75 }),
76 {
77 tags: ["provider:cloudflare", "provider:cloudflare:worker", "live"],
78 timeout: 180_000,
79 },
80);