1import * as Cloudflare from "@/Cloudflare";
2import { CloudflareEnvironment } from "@/Cloudflare/CloudflareEnvironment";
3import * as Provider from "@/Provider";
4import * as Test from "@/Test/Alchemy";
5import * as zeroTrust from "@distilled.cloud/cloudflare/zero-trust";
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// Ride out 403 blips (`Forbidden`) while the harness-minted token
19// propagates across Cloudflare's edge.
20const getLogging = (accountId: string) =>
21 zeroTrust.getGatewayLogging({ accountId }).pipe(
22 Effect.retry({
23 while: (e) => e._tag === "Forbidden",
24 schedule: Schedule.exponential("500 millis"),
25 times: 8,
26 }),
27 );
28
29test.provider(
30 "manage logging settings, update in place, restore on destroy",
31 (stack) =>
32 Effect.gen(function* () {
33 const { accountId } = yield* yield* CloudflareEnvironment;
34
35 yield* stack.destroy();
36
37 // Capture the account's pre-test state out-of-band so we can
38 // verify the capture-and-restore behaviour at the end.
39 const baseline = yield* getLogging(accountId);
40 const baselineRedactPii = baseline.redactPii ?? undefined;
41 const baselineDnsLogAll =
42 baseline.settingsByRuleType?.dns?.logAll ?? undefined;
43
44 // Drive both managed fields to the opposite of the baseline so the
45 // deploy is guaranteed to change something.
46 const flippedRedactPii = !(baselineRedactPii ?? false);
47 const flippedDnsLogAll = !(baselineDnsLogAll ?? true);
48
49 const logging = yield* stack.deploy(
50 Cloudflare.Gateway.Logging("Logging", {
51 redactPii: flippedRedactPii,
52 settingsByRuleType: {
53 dns: { logAll: flippedDnsLogAll },
54 },
55 }),
56 );
57 expect(logging.accountId).toEqual(accountId);
58 expect(logging.redactPii).toEqual(flippedRedactPii);
59 expect(logging.dns?.logAll).toEqual(flippedDnsLogAll);
60 // The pre-management snapshot was captured for restore.
61 expect(logging.initialSettings.redactPii ?? undefined).toEqual(
62 baselineRedactPii,
63 );
64
65 const live = yield* getLogging(accountId);
66 expect(live.redactPii).toEqual(flippedRedactPii);
67 expect(live.settingsByRuleType?.dns?.logAll).toEqual(flippedDnsLogAll);
68
69 // Update in place — flip redactPii back; the dns toggle stays.
70 const updated = yield* stack.deploy(
71 Cloudflare.Gateway.Logging("Logging", {
72 redactPii: !flippedRedactPii,
73 settingsByRuleType: {
74 dns: { logAll: flippedDnsLogAll },
75 },
76 }),
77 );
78 expect(updated.redactPii).toEqual(!flippedRedactPii);
79 expect(updated.dns?.logAll).toEqual(flippedDnsLogAll);
80 // The original capture survives the update.
81 expect(updated.initialSettings).toEqual(logging.initialSettings);
82
83 // Destroy restores the captured pre-management values.
84 yield* stack.destroy();
85 const restored = yield* getLogging(accountId);
86 expect(restored.redactPii ?? undefined).toEqual(baselineRedactPii);
87 expect(restored.settingsByRuleType?.dns?.logAll ?? undefined).toEqual(
88 baselineDnsLogAll,
89 );
90 }).pipe(logLevel),
91 {
92 tags: ["provider:cloudflare", "provider:cloudflare:gateway", "live"],
93 timeout: 120_000,
94 },
95);
96
97// Canonical `list()` test (account singleton): there is no enumeration API
98// for the Gateway logging settings — the object always exists for the
99// account. `list()` reads the single instance and returns it as a
100// one-element array. Assert exactly one well-typed Attributes for the
101// ambient account.
102test.provider(
103 "list returns the account's Gateway logging singleton",
104 (stack) =>
105 Effect.gen(function* () {
106 const { accountId } = yield* yield* CloudflareEnvironment;
107
108 yield* stack.destroy();
109
110 const provider = yield* Provider.findProvider(Cloudflare.Gateway.Logging);
111 const all = yield* provider.list();
112
113 expect(all.length).toEqual(1);
114 const [settings] = all;
115 expect(settings.accountId).toEqual(accountId);
116 // The singleton's observed snapshot is its own restore target.
117 expect(settings.initialSettings).toBeDefined();
118
119 yield* stack.destroy();
120 }).pipe(logLevel),
121 {
122 tags: ["provider:cloudflare", "provider:cloudflare:gateway", "live"],
123 timeout: 120_000,
124 },
125);