1import * as Cloudflare from "@/Cloudflare";
2import { CloudflareEnvironment } from "@/Cloudflare/CloudflareEnvironment";
3import { findZoneByName } from "@/Cloudflare/Zone/lookup";
4import * as Provider from "@/Provider";
5import * as Test from "@/Test/Alchemy";
6import * as logs from "@distilled.cloud/cloudflare/logs";
7import { expect } from "alchemy-test";
8import * as Effect from "effect/Effect";
9import { MinimumLogLevel } from "effect/References";
10import * as Schedule from "effect/Schedule";
11
12const { test } = Test.make({ providers: Cloudflare.providers() });
13
14const logLevel = Effect.provideService(
15 MinimumLogLevel,
16 process.env.DEBUG ? "Debug" : "Info",
17);
18
19const zoneName =
20 process.env.CLOUDFLARE_TEST_DNS_ZONE_NAME ?? "alchemy-test-2.us";
21
22const resolveZoneId = Effect.gen(function* () {
23 const { accountId } = yield* yield* CloudflareEnvironment;
24 const zone = yield* findZoneByName({ accountId, name: zoneName });
25 if (!zone) {
26 return yield* Effect.die(
27 new Error(`zone "${zoneName}" not found in account`),
28 );
29 }
30 return zone.id;
31});
32
33// Logpull (and its retention flag) is an Enterprise-only feature. On the
34// standard testing account every `/logs/control/retention/flag` call fails
35// with Cloudflare error code 10000 ("Unauthorized"), surfaced as the typed
36// `LogsControlNotAuthorized` error. The full lifecycle test below is gated
37// behind an entitled zone supplied via env.
38const entitled = !!process.env.CLOUDFLARE_TEST_LOGS_CONTROL;
39
40// The scoped API token the test harness mints propagates eventually-
41// consistently across Cloudflare's edge — ride out 403 blips (`Forbidden`,
42// declared in the distilled error union via patches) on out-of-band calls.
43const getFlag = (zoneId: string) =>
44 logs.getControlRetention({ zoneId }).pipe(
45 Effect.map((r) => r.flag ?? false),
46 Effect.retry({
47 while: (e) => e._tag === "Forbidden",
48 schedule: Schedule.exponential("500 millis"),
49 times: 8,
50 }),
51 );
52
53const setBaseline = (zoneId: string, flag: boolean) =>
54 logs.createControlRetention({ zoneId, flag }).pipe(
55 Effect.retry({
56 while: (e) => e._tag === "Forbidden",
57 schedule: Schedule.exponential("500 millis"),
58 times: 8,
59 }),
60 );
61
62test.provider.skipIf(entitled)(
63 "surfaces the typed LogsControlNotAuthorized error on unentitled zones",
64 (stack) =>
65 Effect.gen(function* () {
66 const zoneId = yield* resolveZoneId;
67
68 yield* stack.destroy();
69
70 // The testing zone has no Logpull entitlement — both reads and
71 // writes must fail with the typed authorization tag (Cloudflare
72 // error code 10000).
73 const readError = yield* logs
74 .getControlRetention({ zoneId })
75 .pipe(Effect.flip);
76 expect(readError._tag).toEqual("LogsControlNotAuthorized");
77
78 const writeError = yield* logs
79 .createControlRetention({ zoneId, flag: true })
80 .pipe(Effect.flip);
81 expect(writeError._tag).toEqual("LogsControlNotAuthorized");
82
83 yield* stack.destroy();
84 }).pipe(logLevel),
85 {
86 tags: [
87 "provider:cloudflare",
88 "provider:cloudflare:logscontrol",
89 "provider:cloudflare:zone",
90 "live",
91 ],
92 },
93);
94
95test.provider.skipIf(!entitled)(
96 "pins the retention flag, updates in place, and restores on destroy",
97 (stack) =>
98 Effect.gen(function* () {
99 const zoneId = yield* resolveZoneId;
100
101 yield* stack.destroy();
102 // Known baseline: retention disabled.
103 yield* setBaseline(zoneId, false);
104
105 const created = yield* stack.deploy(
106 Cloudflare.LogsControl.LogsRetentionFlag("Retention", {
107 zoneId,
108 flag: true,
109 }),
110 );
111 expect(created.zoneId).toEqual(zoneId);
112 expect(created.flag).toEqual(true);
113 // The pre-management value was captured for restore-on-destroy.
114 expect(created.initialFlag).toEqual(false);
115
116 const live = yield* getFlag(zoneId);
117 expect(live).toEqual(true);
118
119 // In-place update back to false — the captured initial value
120 // survives the update.
121 const updated = yield* stack.deploy(
122 Cloudflare.LogsControl.LogsRetentionFlag("Retention", {
123 zoneId,
124 flag: false,
125 }),
126 );
127 expect(updated.flag).toEqual(false);
128 expect(updated.initialFlag).toEqual(false);
129
130 const liveUpdated = yield* getFlag(zoneId);
131 expect(liveUpdated).toEqual(false);
132
133 // Flip it on again so destroy has something to restore.
134 yield* stack.deploy(
135 Cloudflare.LogsControl.LogsRetentionFlag("Retention", {
136 zoneId,
137 flag: true,
138 }),
139 );
140
141 yield* stack.destroy();
142
143 // Destroy restored the value the flag had before we managed it.
144 const restored = yield* getFlag(zoneId);
145 expect(restored).toEqual(false);
146 }).pipe(logLevel),
147 {
148 tags: [
149 "provider:cloudflare",
150 "provider:cloudflare:logscontrol",
151 "provider:cloudflare:zone",
152 "live",
153 ],
154 timeout: 120_000,
155 },
156);
157
158// Canonical `list()` test (zone-scoped singleton): there is no account-wide
159// API for this per-zone flag, so `list()` enumerates every zone via
160// `listAllZones` and reads the singleton in each. Logpull is Enterprise-only,
161// so on an unentitled account every per-zone read fails with the typed
162// `LogsControlNotAuthorized` (code 10000) and `list()` skips it — yielding an
163// array (typically empty) rather than throwing. This ungated case asserts that
164// the typed-skip path keeps `list()` total; the entitled case below asserts
165// the standing test zone is actually enumerated.
166test.provider(
167 "list enumerates the retention flag across all zones",
168 (stack) =>
169 Effect.gen(function* () {
170 const provider = yield* Provider.findProvider(
171 Cloudflare.LogsControl.LogsRetentionFlag,
172 );
173 const all = yield* provider.list();
174
175 expect(Array.isArray(all)).toBe(true);
176
177 // `stack` is unused here (the singleton always exists on every zone),
178 // but keep the destroy bookend so the harness state stays clean.
179 yield* stack.destroy();
180 }).pipe(logLevel),
181 { tags: ["provider:cloudflare", "provider:cloudflare:logscontrol", "live"] },
182);
183
184test.provider.skipIf(!entitled)(
185 "list contains the entitled test zone's retention flag",
186 (stack) =>
187 Effect.gen(function* () {
188 const zoneId = yield* resolveZoneId;
189
190 const provider = yield* Provider.findProvider(
191 Cloudflare.LogsControl.LogsRetentionFlag,
192 );
193 const all = yield* provider.list();
194
195 expect(all.length).toBeGreaterThan(0);
196 expect(all.some((f) => f.zoneId === zoneId)).toBe(true);
197
198 yield* stack.destroy();
199 }).pipe(logLevel),
200 {
201 tags: [
202 "provider:cloudflare",
203 "provider:cloudflare:logscontrol",
204 "provider:cloudflare:zone",
205 "live",
206 ],
207 timeout: 120_000,
208 },
209);