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// The scoped API token the test harness mints propagates eventually-
19// consistently across Cloudflare's edge — ride out 403 blips
20// (`Forbidden`, declared in the distilled error union) on the test's
21// own out-of-band verification calls.
22const getSettings = (accountId: string) =>
23 zeroTrust.getDeviceSetting({ accountId }).pipe(
24 Effect.retry({
25 while: (e) => e._tag === "Forbidden",
26 schedule: Schedule.exponential("500 millis"),
27 times: 8,
28 }),
29 );
30
31test.provider(
32 "patches disableForTime and restores the original value on destroy",
33 (stack) =>
34 Effect.gen(function* () {
35 const { accountId } = yield* yield* CloudflareEnvironment;
36
37 yield* stack.destroy();
38
39 // Capture the live singleton before Alchemy manages it; the
40 // capture-and-restore contract must put it back on destroy.
41 const before = yield* getSettings(accountId);
42 const beforeDisable = before.disableForTime ?? null;
43
44 // Step 1 — set the WARP override-code time limit. `disableForTime`
45 // is the lowest-risk field on the singleton: it only affects how
46 // long manually-issued override codes last.
47 const a = yield* stack.deploy(
48 Cloudflare.Devices.DeviceSettings("Devices", { disableForTime: 3600 }),
49 );
50 expect(a.accountId).toEqual(accountId);
51 expect(a.disableForTime).toEqual(3600);
52 expect(a.initialSettings.disableForTime ?? null).toEqual(beforeDisable);
53
54 const live1 = yield* getSettings(accountId);
55 expect(live1.disableForTime).toEqual(3600);
56
57 // Step 2 — update in place; the initial snapshot must be preserved,
58 // not re-captured from the now-managed state.
59 const b = yield* stack.deploy(
60 Cloudflare.Devices.DeviceSettings("Devices", { disableForTime: 7200 }),
61 );
62 expect(b.disableForTime).toEqual(7200);
63 expect(b.initialSettings.disableForTime ?? null).toEqual(beforeDisable);
64
65 const live2 = yield* getSettings(accountId);
66 expect(live2.disableForTime).toEqual(7200);
67
68 // Step 3 — a no-op redeploy must not lose the snapshot either.
69 const c = yield* stack.deploy(
70 Cloudflare.Devices.DeviceSettings("Devices", { disableForTime: 7200 }),
71 );
72 expect(c.disableForTime).toEqual(7200);
73 expect(c.initialSettings.disableForTime ?? null).toEqual(beforeDisable);
74
75 // Destroy — the singleton must be restored to its pre-managed state.
76 yield* stack.destroy();
77 const after = yield* getSettings(accountId);
78 expect(after.disableForTime ?? null).toEqual(beforeDisable);
79 }).pipe(logLevel),
80 { tags: ["provider:cloudflare", "provider:cloudflare:devices", "live"] },
81);
82
83// Canonical `list()` test (account-scoped singleton): there is exactly one
84// device-settings object per account and no enumeration API, so `list()`
85// reads the single singleton and returns a one-element Attributes array.
86test.provider(
87 "list returns the account's device settings singleton",
88 (stack) =>
89 Effect.gen(function* () {
90 const { accountId } = yield* yield* CloudflareEnvironment;
91
92 yield* stack.destroy();
93
94 const provider = yield* Provider.findProvider(
95 Cloudflare.Devices.DeviceSettings,
96 );
97 const all = yield* provider.list();
98
99 // Exactly one element — the account-wide singleton — well-typed as
100 // DeviceSettings["Attributes"].
101 expect(all.length).toEqual(1);
102 expect(all[0].accountId).toEqual(accountId);
103 expect(all[0].initialSettings).toBeDefined();
104
105 yield* stack.destroy();
106 }).pipe(logLevel),
107 { tags: ["provider:cloudflare", "provider:cloudflare:devices", "live"] },
108);