1import * as Cloudflare from "@/Cloudflare/index.ts";
2import * as Test from "@/Test/Alchemy";
3import { expect } from "alchemy-test";
4import * as Effect from "effect/Effect";
5
6const { test } = Test.make({ providers: Cloudflare.providers() });
7
8const script = `export default { fetch() { return new Response("ok"); } };`;
9
10/**
11 * Deploy-time binding validation rejects a script upload whose bindings
12 * reference a resource that doesn't exist. The Durable Object
13 * cross-script binding is the one deterministic engine-level trigger:
14 * `scriptName` is a plain string, so a stack can name a script that was
15 * never deployed. (Every other binding type can only reference a
16 * resource declared in the stack, and its reconciler heals a missing
17 * target before the Worker deploys — those hit not-found only via
18 * propagation races, which `putWorkerScript`'s bounded retry rides
19 * out.)
20 *
21 * The deploy must fail with the resource-specific typed error after the
22 * bounded retry exhausts — not an UnknownCloudflareError, and not a
23 * success.
24 */
25test.provider(
26 "durable object binding to a script that doesn't exist surfaces the typed error",
27 (stack) =>
28 Effect.gen(function* () {
29 yield* stack.destroy();
30
31 const error = yield* stack
32 .deploy(
33 Effect.gen(function* () {
34 return yield* Cloudflare.Worker("do-consumer-worker", {
35 script,
36 env: {
37 Counter: Cloudflare.DurableObject("Counter", {
38 scriptName: "alchemy-test-nonexistent-do-host",
39 }),
40 },
41 });
42 }),
43 )
44 .pipe(Effect.flip);
45
46 expect(error._tag).toEqual("DurableObjectClassNotFound");
47
48 yield* stack.destroy();
49 }),
50 {
51 tags: ["provider:cloudflare", "provider:cloudflare:worker", "live"],
52 timeout: 240_000,
53 },
54);