1import * as Cloudflare from "@/Cloudflare";
2import { CloudflareEnvironment } from "@/Cloudflare/CloudflareEnvironment";
3import * as Provider from "@/Provider";
4import * as Test from "@/Test/Alchemy";
5import * as cni from "@distilled.cloud/cloudflare/network-interconnects";
6import { expect } from "alchemy-test";
7import * as Effect from "effect/Effect";
8import { MinimumLogLevel } from "effect/References";
9import * as Schedule from "effect/Schedule";
10
11const { test } = Test.make({ providers: Cloudflare.providers() });
12
13const logLevel = Effect.provideService(
14 MinimumLogLevel,
15 process.env.DEBUG ? "Debug" : "Info",
16);
17
18// The scoped API token the test harness mints propagates eventually-
19// consistently across Cloudflare's edge — a fresh token intermittently 403s.
20// Ride out the blips by retrying the typed `Forbidden` error (part of each
21// CNI setting operation's error union via distilled patches).
22const forbiddenRetrySchedule = Schedule.exponential("500 millis");
23
24const getSetting = (accountId: string) =>
25 cni.getSetting({ accountId }).pipe(
26 Effect.retry({
27 while: (e) => e._tag === "Forbidden",
28 schedule: forbiddenRetrySchedule,
29 times: 8,
30 }),
31 );
32
33// Normalize the singleton to a known baseline so each run starts from the
34// same cloud state regardless of what a previous (possibly interrupted)
35// run left behind. 13335 is Cloudflare's own ASN — the account default.
36const baselineAsn = 13335;
37
38const setBaseline = (accountId: string) =>
39 cni.putSetting({ accountId, defaultAsn: baselineAsn }).pipe(
40 Effect.retry({
41 while: (e) => e._tag === "Forbidden",
42 schedule: forbiddenRetrySchedule,
43 times: 8,
44 }),
45 );
46
47test.provider(
48 "pins the default ASN, updates in place, and restores the original on destroy",
49 (stack) =>
50 Effect.gen(function* () {
51 const { accountId } = yield* yield* CloudflareEnvironment;
52
53 yield* stack.destroy();
54
55 // CNI is entitlement-gated — on accounts without the Network
56 // Interconnect feature every call fails with the typed `Forbidden`
57 // error even after the token-propagation retries. Skip the
58 // lifecycle assertions on such accounts.
59 const probe = yield* getSetting(accountId).pipe(
60 Effect.catchTag("Forbidden", () => Effect.succeed(undefined)),
61 );
62 if (probe === undefined) {
63 yield* Effect.logWarning(
64 "Skipping: account lacks the Cloudflare Network Interconnect entitlement (Forbidden).",
65 );
66 return;
67 }
68
69 yield* setBaseline(accountId);
70
71 const settings = yield* stack.deploy(
72 Effect.gen(function* () {
73 return yield* Cloudflare.NetworkInterconnects.NetworkInterconnectSettings(
74 "CniSettings",
75 {
76 defaultAsn: 65000,
77 },
78 );
79 }),
80 );
81
82 expect(settings.accountId).toEqual(accountId);
83 expect(settings.defaultAsn).toEqual(65000);
84 // The pre-management value was captured for restore-on-destroy.
85 expect(settings.initialDefaultAsn).toEqual(baselineAsn);
86
87 // Out-of-band verification via the distilled API.
88 const live = yield* getSetting(accountId);
89 expect(live.defaultAsn).toEqual(65000);
90
91 // Update in place — same singleton, initialDefaultAsn survives.
92 const updated = yield* stack.deploy(
93 Effect.gen(function* () {
94 return yield* Cloudflare.NetworkInterconnects.NetworkInterconnectSettings(
95 "CniSettings",
96 {
97 defaultAsn: 64999,
98 },
99 );
100 }),
101 );
102 expect(updated.defaultAsn).toEqual(64999);
103 expect(updated.initialDefaultAsn).toEqual(baselineAsn);
104
105 const liveUpdated = yield* getSetting(accountId);
106 expect(liveUpdated.defaultAsn).toEqual(64999);
107
108 yield* stack.destroy();
109
110 // Destroy restored the value the singleton had before we managed it.
111 const restored = yield* getSetting(accountId);
112 expect(restored.defaultAsn).toEqual(baselineAsn);
113 }).pipe(logLevel),
114 {
115 tags: [
116 "provider:cloudflare",
117 "provider:cloudflare:networkinterconnects",
118 "live",
119 ],
120 timeout: 120_000,
121 },
122);
123
124// Canonical `list()` test (account singleton): there is no enumeration API
125// for the CNI settings object, so `list()` reads the single
126// `/accounts/{account_id}/cni/settings` and returns a one-element array.
127// CNI is enterprise-gated — on accounts without the entitlement the route
128// rejects with the typed `Forbidden` error, which `list()` maps to `[]`
129// ("unset"). The read-only assertions below hold in both cases; the
130// entitled-account content assertion is gated on a non-empty result.
131test.provider(
132 "list enumerates the CNI settings singleton",
133 (stack) =>
134 Effect.gen(function* () {
135 const { accountId } = yield* yield* CloudflareEnvironment;
136
137 yield* stack.destroy();
138
139 const provider = yield* Provider.findProvider(
140 Cloudflare.NetworkInterconnects.NetworkInterconnectSettings,
141 );
142 const all = yield* provider.list();
143
144 // Always a well-typed array (empty when the account lacks the
145 // entitlement, one element when CNI settings are present).
146 expect(Array.isArray(all)).toBe(true);
147 expect(all.length).toBeLessThanOrEqual(1);
148
149 if (all.length === 0) {
150 yield* Effect.logWarning(
151 "list() returned []: account lacks the Cloudflare Network Interconnect entitlement (Forbidden).",
152 );
153 } else {
154 const [settings] = all;
155 expect(settings.accountId).toEqual(accountId);
156 expect(typeof settings.defaultAsn).toBe("number");
157 expect(settings.initialDefaultAsn).toEqual(settings.defaultAsn);
158 }
159
160 yield* stack.destroy();
161 }).pipe(logLevel),
162 {
163 tags: [
164 "provider:cloudflare",
165 "provider:cloudflare:networkinterconnects",
166 "live",
167 ],
168 timeout: 120_000,
169 },
170);