1import * as AWS from "@/AWS";
2import type { ProviderService } from "@/Provider";
3import * as Test from "@/Test/Alchemy";
4import { expect } from "alchemy-test";
5import * as Effect from "effect/Effect";
6
7const { test } = Test.make({ providers: AWS.providers() });
8
9/**
10 * Dual-mode AWS providers whose LOCAL variant is a process on this machine
11 * (a dev server), not the floci emulator. They have no emulator API for a
12 * binding client to route to, so `resolveLocalDataPlane` deliberately falls
13 * back to the real cloud for them (see Provider.ts). Every other AWS dual
14 * runs on floci and must say so.
15 */
16const PROCESS_HOSTED = new Set([
17 "Command.Dev",
18 "AWS.Website.Server",
19 "Website.Server",
20]);
21
22/**
23 * Every floci-backed dual provider must declare the emulator as its
24 * {@link ProviderService.localDataPlane}. A dual that doesn't is a silent
25 * hole in `alchemy dev`: a deploy-time binding client bound to it (an
26 * Action body, a plan-time `execute`) is left unrouted — a single-resource
27 * binding such as `InvokeFunction(fn)` then targets the real cloud, and a
28 * binding spanning a declaring sibling (`RunTask(cluster, task)`) dies with
29 * "Binding client spans mixed data planes" (the reported bug; its e2e is
30 * test/AWS/Local/Actions.local.test.ts).
31 *
32 * `flociDual` declares it for you; the hand-written floci providers
33 * (`ProviderLayer.dual` with a `Floci*Provider` local) have to say so
34 * explicitly — this walks the registry and pins that they all do.
35 */
36test.provider(
37 "every floci-backed dual AWS provider declares its local data plane",
38 () =>
39 Effect.gen(function* () {
40 const context = yield* Effect.context<never>();
41 const missing: string[] = [];
42 let duals = 0;
43 for (const [key, value] of context.mapUnsafe) {
44 const entry = value as Partial<ProviderService> & {
45 kind?: string;
46 providers?: Record<string, ProviderService>;
47 };
48 // Providers live in context either as individual `Provider` tags or
49 // inside a `ProviderCollection` keyed by resource type.
50 const candidates: [string, Partial<ProviderService>][] =
51 entry.kind === "ProviderCollection" && entry.providers
52 ? Object.entries(entry.providers)
53 : typeof entry.reconcile === "function"
54 ? [[String(key), entry]]
55 : [];
56 for (const [type, service] of candidates) {
57 if (service.modes === undefined || PROCESS_HOSTED.has(type)) continue;
58 duals += 1;
59 if (service.localDataPlane === undefined) missing.push(type);
60 }
61 }
62 // Sanity: the registry was actually walked (hundreds of duals).
63 expect(duals).toBeGreaterThan(100);
64 expect(missing).toEqual([]);
65 }),
66 { tags: ["provider:aws", "local"] },
67);