1import { adopt } from "@/AdoptPolicy";
2import * as Cloudflare from "@/Cloudflare";
3import { CloudflareEnvironment } from "@/Cloudflare/CloudflareEnvironment";
4import * as Provider from "@/Provider";
5import * as Test from "@/Test/Alchemy";
6import * as zeroTrust from "@distilled.cloud/cloudflare/zero-trust";
7import { expect } from "alchemy-test";
8import * as Effect from "effect/Effect";
9import * as Redacted from "effect/Redacted";
10import { MinimumLogLevel } from "effect/References";
11
12const { test } = Test.make({
13 providers: Cloudflare.providers(),
14 state: Cloudflare.state(),
15});
16
17const logLevel = Effect.provideService(
18 MinimumLogLevel,
19 process.env.DEBUG ? "Debug" : "Info",
20);
21
22// Read a WARP Connector tunnel out-of-band, mapping "gone" (TunnelNotFound)
23// and soft-deleted tunnels to undefined.
24const getLiveConnector = (accountId: string, tunnelId: string) =>
25 zeroTrust.getTunnelWarpConnector({ accountId, tunnelId }).pipe(
26 Effect.map((t) => (t.deletedAt ? undefined : t)),
27 Effect.catchTag("TunnelNotFound", () => Effect.succeed(undefined)),
28 );
29
30test.provider(
31 "create, rename by replacement, and destroy a WARP Connector tunnel",
32 (stack) =>
33 Effect.gen(function* () {
34 const { accountId } = yield* yield* CloudflareEnvironment;
35
36 yield* stack.destroy();
37
38 const connector = yield* stack.deploy(
39 Effect.gen(function* () {
40 return yield* Cloudflare.Tunnel.WarpConnector("SiteA", {
41 name: "alchemy-test-warp-connector",
42 }).pipe(adopt(true));
43 }),
44 );
45
46 expect(connector.tunnelId).toBeDefined();
47 expect(connector.accountId).toEqual(accountId);
48 expect(connector.name).toEqual("alchemy-test-warp-connector");
49 // The connector token is returned redacted and non-empty.
50 expect(Redacted.value(connector.token).length).toBeGreaterThan(0);
51 // A never-joined connector only ever shows API-visible CRUD state —
52 // do not wait for connection states; assert the record exists.
53 const live = yield* getLiveConnector(accountId, connector.tunnelId);
54 expect(live?.id).toEqual(connector.tunnelId);
55 expect(live?.name).toEqual("alchemy-test-warp-connector");
56
57 // Cloudflare returns TunnelNotFound from the documented PATCH endpoint
58 // for a live connector, so changing the name replaces the tunnel.
59 const renamed = yield* stack.deploy(
60 Effect.gen(function* () {
61 return yield* Cloudflare.Tunnel.WarpConnector("SiteA", {
62 name: "alchemy-test-warp-connector-v2",
63 }).pipe(adopt(true));
64 }),
65 );
66 expect(renamed.tunnelId).not.toEqual(connector.tunnelId);
67 expect(renamed.name).toEqual("alchemy-test-warp-connector-v2");
68
69 const renamedLive = yield* getLiveConnector(accountId, renamed.tunnelId);
70 expect(renamedLive?.name).toEqual("alchemy-test-warp-connector-v2");
71
72 yield* stack.destroy();
73
74 // Cloudflare soft-deletes tunnels; gone = 404 or deletedAt set.
75 const afterDestroy = yield* getLiveConnector(accountId, renamed.tunnelId);
76 expect(afterDestroy).toBeUndefined();
77 }).pipe(logLevel),
78 {
79 tags: ["provider:cloudflare", "provider:cloudflare:tunnel", "live"],
80 timeout: 90_000,
81 },
82);
83
84// Canonical `list()` test (account collection): deploy a WARP Connector
85// tunnel, resolve the provider via the typed `Provider.findProvider`, and
86// assert the deployed tunnel appears in the exhaustively-paginated result.
87test.provider(
88 "list enumerates the deployed WARP Connector tunnel",
89 (stack) =>
90 Effect.gen(function* () {
91 yield* stack.destroy();
92
93 const connector = yield* stack.deploy(
94 Effect.gen(function* () {
95 return yield* Cloudflare.Tunnel.WarpConnector("ListSite", {
96 name: "alchemy-test-warp-connector-list",
97 }).pipe(adopt(true));
98 }),
99 );
100
101 const provider = yield* Provider.findProvider(
102 Cloudflare.Tunnel.WarpConnector,
103 );
104 const all = yield* provider.list();
105
106 const found = all.find((t) => t.tunnelId === connector.tunnelId);
107 expect(found).toBeDefined();
108 expect(found?.name).toEqual("alchemy-test-warp-connector-list");
109 expect(found?.accountId).toEqual(connector.accountId);
110 expect(Redacted.value(found!.token).length).toBeGreaterThan(0);
111
112 yield* stack.destroy();
113 }).pipe(logLevel),
114 {
115 tags: ["provider:cloudflare", "provider:cloudflare:tunnel", "live"],
116 timeout: 90_000,
117 },
118);