1import * as Cloudflare from "@/Cloudflare";
2import * as Provider from "@/Provider";
3import * as Test from "@/Test/Alchemy";
4import { expect } from "alchemy-test";
5import * as Effect from "effect/Effect";
6import { MinimumLogLevel } from "effect/References";
7import * as Schedule from "effect/Schedule";
8
9const { test } = Test.make({ providers: Cloudflare.providers() });
10
11const logLevel = Effect.provideService(
12 MinimumLogLevel,
13 process.env.DEBUG ? "Debug" : "Info",
14);
15
16// A freshly minted scoped token propagates eventually-consistently across
17// Cloudflare's edge — retry the typed `Forbidden` blips on out-of-band calls.
18const forbiddenRetrySchedule = Schedule.exponential("500 millis");
19
20const retryForbidden = <A, E extends { _tag: string }, R>(
21 effect: Effect.Effect<A, E, R>,
22) =>
23 effect.pipe(
24 Effect.retry({
25 while: (e) => e._tag === "Forbidden",
26 schedule: forbiddenRetrySchedule,
27 times: 8,
28 }),
29 );
30
31// `list()` fans out across account BYOIP prefixes (`listPrefixes`) and lists
32// each prefix's service bindings (`listPrefixServiceBindings`). Both endpoints
33// are available regardless of the BYOIP entitlement — on an account with no
34// onboarded prefixes the result is an empty, well-typed
35// `ServiceBindingAttributes[]` (the exact shape `read` produces).
36test.provider(
37 "list enumerates service bindings across prefixes (read-only)",
38 (stack) =>
39 Effect.gen(function* () {
40 yield* stack.destroy();
41
42 const provider = yield* Provider.findProvider(
43 Cloudflare.Addressing.ServiceBinding,
44 );
45 const all = yield* retryForbidden(provider.list());
46
47 expect(Array.isArray(all)).toBe(true);
48 for (const b of all) {
49 expect(typeof b.bindingId).toBe("string");
50 expect(typeof b.prefixId).toBe("string");
51 expect(typeof b.accountId).toBe("string");
52 expect(typeof b.cidr).toBe("string");
53 expect(typeof b.serviceId).toBe("string");
54 expect(typeof b.provisioning).toBe("object");
55 }
56
57 yield* stack.destroy();
58 }).pipe(logLevel),
59 { tags: ["provider:cloudflare", "provider:cloudflare:addressing", "live"] },
60);