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";
9
10const { test } = Test.make({ providers: Cloudflare.providers() });
11
12const logLevel = Effect.provideService(
13 MinimumLogLevel,
14 process.env.DEBUG ? "Debug" : "Info",
15);
16
17// DEX synthetic tests require the Digital Experience Monitoring
18// entitlement. On the standard testing account
19// `POST /accounts/{id}/dex/devices/dex_tests` fails with HTTP 403 code
20// 11007 "dex.api.entitlements.missing" — surfaced as the typed
21// `Forbidden` error. The lifecycle test is gated behind an entitled
22// account supplied via env; the probe test always runs and pins the
23// typed tag.
24const entitled = !!process.env.CLOUDFLARE_TEST_DEX;
25
26const TEST_NAME = "alchemy-test-dex";
27
28test.provider.skipIf(entitled)(
29 "unentitled accounts surface the typed Forbidden entitlement error",
30 (stack) =>
31 Effect.gen(function* () {
32 const { accountId } = yield* yield* CloudflareEnvironment;
33
34 yield* stack.destroy();
35
36 const error = yield* zeroTrust
37 .createDeviceDexTest({
38 accountId,
39 name: "alchemy-dex-entitlement-probe",
40 enabled: true,
41 interval: "0h30m0s",
42 data: { host: "https://example.com", kind: "http", method: "GET" },
43 })
44 .pipe(Effect.flip);
45 expect(error._tag).toEqual("Forbidden");
46 if (error._tag === "Forbidden") {
47 expect(error.message).toContain("dex.api.entitlements.missing");
48 }
49
50 yield* stack.destroy();
51 }).pipe(logLevel),
52 {
53 tags: ["provider:cloudflare", "provider:cloudflare:devices", "live"],
54 timeout: 90_000,
55 },
56);
57
58test.provider.skipIf(!entitled)(
59 "create, update in place, and destroy a DEX test",
60 (stack) =>
61 Effect.gen(function* () {
62 const { accountId } = yield* yield* CloudflareEnvironment;
63
64 yield* stack.destroy();
65
66 const created = yield* stack.deploy(
67 Effect.gen(function* () {
68 return yield* Cloudflare.Devices.DeviceDexTest("AppHealth", {
69 name: TEST_NAME,
70 data: {
71 host: "https://app.example.com/health",
72 kind: "http",
73 method: "GET",
74 },
75 interval: "0h30m0s",
76 description: "v1",
77 });
78 }),
79 );
80
81 expect(created.testId).toBeDefined();
82 expect(created.accountId).toEqual(accountId);
83 expect(created.name).toEqual(TEST_NAME);
84 expect(created.interval).toEqual("0h30m0s");
85 expect(created.enabled).toEqual(true);
86
87 const live = yield* zeroTrust.getDeviceDexTest({
88 accountId,
89 dexTestId: created.testId,
90 });
91 expect(live.name).toEqual(TEST_NAME);
92
93 // Interval + description update converges in place — same test id.
94 const updated = yield* stack.deploy(
95 Effect.gen(function* () {
96 return yield* Cloudflare.Devices.DeviceDexTest("AppHealth", {
97 name: TEST_NAME,
98 data: {
99 host: "https://app.example.com/health",
100 kind: "http",
101 method: "GET",
102 },
103 interval: "1h0m0s",
104 description: "v2",
105 enabled: false,
106 });
107 }),
108 );
109 expect(updated.testId).toEqual(created.testId);
110 expect(updated.interval).toEqual("1h0m0s");
111 expect(updated.description).toEqual("v2");
112 expect(updated.enabled).toEqual(false);
113
114 yield* stack.destroy();
115
116 const gone = yield* zeroTrust
117 .getDeviceDexTest({ accountId, dexTestId: created.testId })
118 .pipe(Effect.flip);
119 expect(gone._tag).toEqual("DexTestNotFound");
120 }).pipe(logLevel),
121 {
122 tags: ["provider:cloudflare", "provider:cloudflare:devices", "live"],
123 timeout: 90_000,
124 },
125);
126
127// Canonical `list()` test (account-scoped collection). The lifecycle requires
128// the DEX entitlement, so the deploy + presence assertion is gated behind an
129// entitled account. On unentitled accounts the provider's `list()` catches the
130// typed `Forbidden` (dex.api.entitlements.missing) and returns `[]`; the ungated
131// half of this test asserts that well-typed empty result so a clean skip run
132// still exercises `list()`.
133test.provider.skipIf(!entitled)(
134 "list enumerates the deployed DEX test",
135 (stack) =>
136 Effect.gen(function* () {
137 yield* stack.destroy();
138
139 const deployed = yield* stack.deploy(
140 Effect.gen(function* () {
141 return yield* Cloudflare.Devices.DeviceDexTest("ListResource", {
142 name: TEST_NAME,
143 data: {
144 host: "https://app.example.com/health",
145 kind: "http",
146 method: "GET",
147 },
148 interval: "0h30m0s",
149 description: "list",
150 });
151 }),
152 );
153
154 const provider = yield* Provider.findProvider(
155 Cloudflare.Devices.DeviceDexTest,
156 );
157 const all = yield* provider.list();
158
159 expect(all.some((t) => t.testId === deployed.testId)).toBe(true);
160
161 yield* stack.destroy();
162 }).pipe(logLevel),
163 {
164 tags: ["provider:cloudflare", "provider:cloudflare:devices", "live"],
165 timeout: 90_000,
166 },
167);
168
169test.provider.skipIf(entitled)(
170 "list returns a well-typed empty array on unentitled accounts",
171 (stack) =>
172 Effect.gen(function* () {
173 yield* stack.destroy();
174
175 const provider = yield* Provider.findProvider(
176 Cloudflare.Devices.DeviceDexTest,
177 );
178 const all = yield* provider.list();
179
180 expect(Array.isArray(all)).toBe(true);
181
182 yield* stack.destroy();
183 }).pipe(logLevel),
184 {
185 tags: ["provider:cloudflare", "provider:cloudflare:devices", "live"],
186 timeout: 90_000,
187 },
188);