| 1 | import * as Cloudflare from "@/Cloudflare"; |
| 2 | import * as Test from "@/Test/Alchemy"; |
| 3 | import { expect } from "alchemy-test"; |
| 4 | import * as Effect from "effect/Effect"; |
| 5 | import * as Schedule from "effect/Schedule"; |
| 6 | import * as HttpClient from "effect/http/HttpClient"; |
| 7 | import Stack from "./fixtures/worker-entrypoint-binding/stack.ts"; |
| 8 | |
| 9 | const { test, beforeAll, afterAll, deploy, destroy } = Test.make({ |
| 10 | providers: Cloudflare.providers(), |
| 11 | }); |
| 12 | |
| 13 | const stack = beforeAll(deploy(Stack)); |
| 14 | afterAll.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. |
| 18 | const coldStartRetry = Effect.retry({ |
| 19 | schedule: Schedule.min([ |
| 20 | Schedule.exponential("500 millis"), |
| 21 | Schedule.spaced("3 seconds"), |
| 22 | ]), |
| 23 | times: 30, |
| 24 | }); |
| 25 | |
| 26 | test( |
| 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 | |
| 41 | test( |
| 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. |
| 65 | test.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 | ); |