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// Risk scoring (and its SSF push integrations) is an Enterprise Zero
18// Trust feature. On the standard testing account
19// `POST /accounts/{id}/zt_risk_scoring/integrations` fails with HTTP 403
20// code 3314 "Forbidden" — surfaced as the typed `Forbidden` error. The
21// lifecycle test is gated behind an entitled account (plus a real Okta
22// tenant) supplied via env; the probe test always runs and pins the
23// typed tag.
24const entitled = !!process.env.CLOUDFLARE_TEST_RISK_SCORING;
25
26test.provider.skipIf(entitled)(
27 "unentitled accounts surface the typed Forbidden error",
28 (stack) =>
29 Effect.gen(function* () {
30 const { accountId } = yield* yield* CloudflareEnvironment;
31
32 yield* stack.destroy();
33
34 const error = yield* zeroTrust
35 .createRiskScoringIntegration({
36 accountId,
37 integrationType: "Okta",
38 tenantUrl: "https://example.okta.com",
39 })
40 .pipe(Effect.flip);
41 expect(error._tag).toEqual("Forbidden");
42
43 yield* stack.destroy();
44 }).pipe(logLevel),
45 {
46 tags: ["provider:cloudflare", "provider:cloudflare:riskscoring", "live"],
47 timeout: 90_000,
48 },
49);
50
51test.provider.skipIf(!entitled)(
52 "create, toggle active in place, and destroy a risk scoring integration",
53 (stack) =>
54 Effect.gen(function* () {
55 const { accountId } = yield* yield* CloudflareEnvironment;
56 const tenantUrl = process.env.CLOUDFLARE_TEST_OKTA_TENANT_URL!;
57
58 yield* stack.destroy();
59
60 const created = yield* stack.deploy(
61 Effect.gen(function* () {
62 return yield* Cloudflare.RiskScoring.Integration("OktaSsf", {
63 tenantUrl,
64 });
65 }),
66 );
67
68 expect(created.integrationId).toBeDefined();
69 expect(created.accountId).toEqual(accountId);
70 expect(created.tenantUrl).toEqual(tenantUrl);
71 expect(created.active).toEqual(true);
72 expect(created.wellKnownUrl).toBeTruthy();
73
74 const live = yield* zeroTrust.getRiskScoringIntegration({
75 accountId,
76 integrationId: created.integrationId,
77 });
78 expect(live.id).toEqual(created.integrationId);
79
80 // Pausing exports converges in place — same integration id.
81 const paused = yield* stack.deploy(
82 Effect.gen(function* () {
83 return yield* Cloudflare.RiskScoring.Integration("OktaSsf", {
84 tenantUrl,
85 active: false,
86 });
87 }),
88 );
89 expect(paused.integrationId).toEqual(created.integrationId);
90 expect(paused.active).toEqual(false);
91
92 yield* stack.destroy();
93
94 const gone = yield* zeroTrust
95 .getRiskScoringIntegration({
96 accountId,
97 integrationId: created.integrationId,
98 })
99 .pipe(Effect.flip);
100 expect(gone._tag).toEqual("RiskScoringIntegrationNotFound");
101 }).pipe(logLevel),
102 {
103 tags: ["provider:cloudflare", "provider:cloudflare:riskscoring", "live"],
104 timeout: 90_000,
105 },
106);
107
108// Read-only list assertion. Always runs: on an unentitled account the
109// account-scoped list route 403s and `list()` returns [] (Forbidden is
110// caught typed); on an entitled account it returns the live integrations
111// in the exact `read` Attributes shape. Either way the result is a
112// well-typed array.
113test.provider(
114 "list enumerates risk scoring integrations",
115 (stack) =>
116 Effect.gen(function* () {
117 yield* stack.destroy();
118
119 const provider = yield* Provider.findProvider(
120 Cloudflare.RiskScoring.Integration,
121 );
122 const all = yield* provider.list();
123
124 expect(Array.isArray(all)).toBe(true);
125 for (const item of all) {
126 expect(typeof item.integrationId).toBe("string");
127 expect(typeof item.accountId).toBe("string");
128 expect(item.integrationType).toBe("Okta");
129 }
130
131 yield* stack.destroy();
132 }).pipe(logLevel),
133 {
134 tags: ["provider:cloudflare", "provider:cloudflare:riskscoring", "live"],
135 timeout: 90_000,
136 },
137);
138
139// Entitled-account variant: deploy an integration and assert `list()`
140// surfaces it (presence in the exhaustively-paginated result).
141test.provider.skipIf(!entitled)(
142 "list includes a freshly deployed integration",
143 (stack) =>
144 Effect.gen(function* () {
145 const tenantUrl = process.env.CLOUDFLARE_TEST_OKTA_TENANT_URL!;
146
147 yield* stack.destroy();
148
149 const deployed = yield* stack.deploy(
150 Effect.gen(function* () {
151 return yield* Cloudflare.RiskScoring.Integration("OktaSsf", {
152 tenantUrl,
153 });
154 }),
155 );
156
157 const provider = yield* Provider.findProvider(
158 Cloudflare.RiskScoring.Integration,
159 );
160 const all = yield* provider.list();
161
162 expect(all.some((x) => x.integrationId === deployed.integrationId)).toBe(
163 true,
164 );
165
166 yield* stack.destroy();
167 }).pipe(logLevel),
168 {
169 tags: ["provider:cloudflare", "provider:cloudflare:riskscoring", "live"],
170 timeout: 90_000,
171 },
172);