1import * as Cloudflare from "@/Cloudflare";
2import { CloudflareEnvironment } from "@/Cloudflare/CloudflareEnvironment";
3import * as Provider from "@/Provider";
4import * as Test from "@/Test/Alchemy";
5import * as emailSecurity from "@distilled.cloud/cloudflare/email-security";
6import { expect } from "alchemy-test";
7import * as Effect from "effect/Effect";
8import { MinimumLogLevel } from "effect/References";
9import * as Schedule from "effect/Schedule";
10import * as Stream from "effect/Stream";
11
12const { test } = Test.make({ providers: Cloudflare.providers() });
13
14const logLevel = Effect.provideService(
15 MinimumLogLevel,
16 process.env.DEBUG ? "Debug" : "Info",
17);
18
19// Email Security (Area 1) is an enterprise add-on — the standard testing
20// account is not entitled (typed `EmailSecurityNotEntitled`), so the
21// lifecycle test is gated behind an entitled account flagged via env.
22const entitled = !!process.env.CLOUDFLARE_EMAIL_SECURITY;
23
24const forbiddenRetrySchedule = Schedule.exponential("500 millis");
25
26const name = "Alchemy Test VIP";
27const email = "alchemy-vip@alchemy-test-2.us";
28
29const findEntry = (accountId: string) =>
30 emailSecurity.listSettingImpersonationRegistries
31 .items({ accountId, search: email })
32 .pipe(
33 Stream.runCollect,
34 Effect.map((chunk) =>
35 Array.from(chunk).find((e) => e.name === name && e.email === email),
36 ),
37 Effect.retry({
38 while: (e) => e._tag === "Forbidden",
39 schedule: forbiddenRetrySchedule,
40 times: 8,
41 }),
42 );
43
44test.provider.skipIf(!entitled)(
45 "create, update in place, destroy",
46 (stack) =>
47 Effect.gen(function* () {
48 const { accountId } = yield* yield* CloudflareEnvironment;
49
50 yield* stack.destroy();
51
52 // Register the protected display name → legitimate address mapping.
53 const created = yield* stack.deploy(
54 Effect.gen(function* () {
55 return yield* Cloudflare.Email.ImpersonationRegistryEntry("Vip", {
56 name,
57 email,
58 comments: "v1",
59 });
60 }),
61 );
62 expect(created.entryId).toBeDefined();
63 expect(created.accountId).toEqual(accountId);
64 expect(created.name).toEqual(name);
65 expect(created.email).toEqual(email);
66 expect(created.isEmailRegex).toEqual(false);
67 expect(created.comments).toEqual("v1");
68 // Manually created entries carry the internal provenance.
69 expect(created.provenance).toEqual("A1S_INTERNAL");
70
71 // Out-of-band verification via the distilled API.
72 const live = yield* findEntry(accountId);
73 expect(live?.id).toEqual(created.entryId);
74
75 // Update mutable fields in place — same physical entry.
76 const updated = yield* stack.deploy(
77 Effect.gen(function* () {
78 return yield* Cloudflare.Email.ImpersonationRegistryEntry("Vip", {
79 name,
80 email,
81 comments: "v2",
82 });
83 }),
84 );
85 expect(updated.entryId).toEqual(created.entryId);
86 expect(updated.comments).toEqual("v2");
87
88 yield* stack.destroy();
89
90 // The entry is gone after destroy.
91 const gone = yield* findEntry(accountId).pipe(
92 Effect.repeat({
93 schedule: Schedule.spaced("2 seconds"),
94 until: (e) => e === undefined,
95 times: 10,
96 }),
97 );
98 expect(gone).toBeUndefined();
99 }).pipe(logLevel),
100 {
101 tags: ["provider:cloudflare", "provider:cloudflare:email", "live"],
102 timeout: 120_000,
103 },
104);
105
106// Read-only: `list()` enumerates the account registry. On the standard
107// (non-entitled) testing account the provider catches the typed
108// `EmailSecurityNotEntitled` error and returns a well-typed empty array, so
109// this runs everywhere and proves the wiring (typed `findProvider`, exact
110// Attributes shape).
111test.provider(
112 "list returns the account impersonation registry",
113 (stack) =>
114 Effect.gen(function* () {
115 yield* stack.destroy();
116
117 const provider = yield* Provider.findProvider(
118 Cloudflare.Email.ImpersonationRegistryEntry,
119 );
120 const all = yield* provider.list();
121
122 expect(Array.isArray(all)).toBe(true);
123 for (const entry of all) {
124 expect(typeof entry.entryId).toBe("string");
125 expect(typeof entry.accountId).toBe("string");
126 expect(typeof entry.name).toBe("string");
127 expect(typeof entry.email).toBe("string");
128 expect(typeof entry.isEmailRegex).toBe("boolean");
129 }
130
131 yield* stack.destroy();
132 }).pipe(logLevel),
133 {
134 tags: ["provider:cloudflare", "provider:cloudflare:email", "live"],
135 timeout: 120_000,
136 },
137);
138
139// Entitled accounts: deploy a real entry and assert `list()` includes it.
140test.provider.skipIf(!entitled)(
141 "list enumerates the deployed impersonation registry entry",
142 (stack) =>
143 Effect.gen(function* () {
144 yield* stack.destroy();
145
146 const created = yield* stack.deploy(
147 Effect.gen(function* () {
148 return yield* Cloudflare.Email.ImpersonationRegistryEntry("ListVip", {
149 name,
150 email,
151 comments: "list",
152 });
153 }),
154 );
155
156 const provider = yield* Provider.findProvider(
157 Cloudflare.Email.ImpersonationRegistryEntry,
158 );
159 const all = yield* provider.list();
160 expect(all.some((e) => e.entryId === created.entryId)).toBe(true);
161
162 yield* stack.destroy();
163 }).pipe(logLevel),
164 {
165 tags: ["provider:cloudflare", "provider:cloudflare:email", "live"],
166 timeout: 120_000,
167 },
168);