1import { AlchemyContext } from "@/AlchemyContext.ts";
2import { ArtifactStore, createArtifactStore } from "@/Artifacts.ts";
3import { AuthProviders } from "@/Auth/AuthProvider.ts";
4import * as CliKit from "@/Cli/CliKit/index.ts";
5import * as Cloudflare from "@/Cloudflare";
6import { CloudflareEnvironment } from "@/Cloudflare/CloudflareEnvironment.ts";
7import { Credentials } from "@/Cloudflare/Credentials.ts";
8import { Stack } from "@/Stack.ts";
9import { Stage } from "@/Stage.ts";
10import * as NodeServices from "@effect/platform-node/NodeServices";
11import { expect, it } from "alchemy-test";
12import * as Context from "effect/Context";
13import * as ConfigProvider from "effect/ConfigProvider";
14import * as Effect from "effect/Effect";
15import * as Layer from "effect/Layer";
16import * as Result from "effect/Result";
17import * as FetchHttpClient from "effect/http/FetchHttpClient";
18import { v4 as uuidv4 } from "uuid";
19
20it.live(
21 "Cloudflare providers defer unknown explicit profile errors until credentials are requested",
22 () =>
23 Effect.gen(function* () {
24 const providers = yield* Layer.build(Cloudflare.providers());
25 for (const resolve of [
26 Context.get(providers, CloudflareEnvironment).pipe(Effect.asVoid),
27 Context.get(providers, Credentials).pipe(Effect.asVoid),
28 ]) {
29 const result = yield* Effect.result(Effect.sandbox(resolve));
30 expect(Result.isFailure(result)).toBe(true);
31 if (Result.isFailure(result)) {
32 expect(String(result.failure)).toContain("does not exist");
33 expect(String(result.failure)).toContain("alchemy profile create");
34 }
35 }
36 }).pipe(
37 Effect.provide(
38 Layer.mergeAll(
39 Layer.succeed(AuthProviders, {}),
40 Layer.succeed(Stage, "test"),
41 Layer.succeed(Stack, {
42 name: "test",
43 stage: "test",
44 resources: {},
45 bindings: {},
46 actions: {},
47 }),
48 Layer.succeed(AlchemyContext, {
49 dev: false,
50 adopt: false,
51 dotAlchemy: ".alchemy",
52 }),
53 Layer.succeed(
54 ConfigProvider.ConfigProvider,
55 ConfigProvider.fromUnknown({
56 ALCHEMY_PROFILE: `non-existent-${uuidv4()}`,
57 }),
58 ),
59 Layer.sync(ArtifactStore, createArtifactStore),
60 NodeServices.layer,
61 FetchHttpClient.layer,
62 ),
63 ),
64 Effect.provide(CliKit.layer({ input: false })),
65 ),
66 { tags: ["unit", "provider:cloudflare", "local"] },
67);
68
69it.live(
70 "builds Cloudflare providers from CI environment credentials without a profile",
71 () =>
72 Effect.gen(function* () {
73 const providers = yield* Layer.build(Cloudflare.providers());
74 const environment = yield* Context.get(providers, CloudflareEnvironment);
75 expect(environment.accountId).toBe("0123456789abcdef0123456789abcdef");
76 const credentials = yield* Context.get(providers, Credentials);
77 expect(credentials.type).toBe("apiToken");
78 }).pipe(
79 Effect.provide(
80 Layer.mergeAll(
81 Layer.succeed(AuthProviders, {}),
82 Layer.succeed(Stage, "test"),
83 Layer.succeed(Stack, {
84 name: "test",
85 stage: "test",
86 resources: {},
87 bindings: {},
88 actions: {},
89 }),
90 Layer.succeed(AlchemyContext, {
91 dev: false,
92 adopt: false,
93 dotAlchemy: ".alchemy",
94 }),
95 Layer.succeed(
96 ConfigProvider.ConfigProvider,
97 ConfigProvider.fromUnknown({
98 CI: true,
99 CLOUDFLARE_API_TOKEN: "test-token",
100 CLOUDFLARE_ACCOUNT_ID: "0123456789abcdef0123456789abcdef",
101 ALCHEMY_PROFILE: `non-existent-${uuidv4()}`,
102 }),
103 ),
104 Layer.sync(ArtifactStore, createArtifactStore),
105 NodeServices.layer,
106 FetchHttpClient.layer,
107 ),
108 ),
109 Effect.provide(CliKit.layer({ input: false })),
110 ),
111 { tags: ["unit", "provider:cloudflare", "local"] },
112);