1import { AuthProviders } from "@/Auth/AuthProvider";
2import { ProfileStore } from "@/Auth/Profile";
3import * as CliKit from "@/Cli/CliKit";
4import { PrismaAuth } from "@/Prisma/AuthProvider";
5import { PrismaEnvironment, fromProfile } from "@/Prisma/PrismaEnvironment";
6import { describe, expect, it } from "alchemy-test";
7import * as NodeServices from "@effect/platform-node/NodeServices";
8import * as ConfigProvider from "effect/ConfigProvider";
9import * as Effect from "effect/Effect";
10import * as Layer from "effect/Layer";
11import * as Redacted from "effect/Redacted";
12import { makeFakeProfileStore } from "./fakes.ts";
13
14const makeProfile = (serviceToken: string): ProfileStore["Service"] =>
15 makeFakeProfileStore({
16 loadProviderConfig: <Config extends { method: string }>() =>
17 Effect.succeed({ method: "stored", serviceToken } as unknown as Config),
18 });
19
20const testLayer = (
21 config: Record<string, string>,
22 options: {
23 storedToken?: string;
24 } = {},
25) => {
26 const authProviders: AuthProviders["Service"] = {};
27 return fromProfile().pipe(
28 Layer.provideMerge(PrismaAuth),
29 Layer.provideMerge(Layer.succeed(AuthProviders, authProviders)),
30 Layer.provideMerge(
31 Layer.succeed(
32 ProfileStore,
33 makeProfile(options.storedToken ?? "test-token"),
34 ),
35 ),
36 Layer.provideMerge(
37 ConfigProvider.layer(ConfigProvider.fromUnknown(config)),
38 ),
39 Layer.provideMerge(NodeServices.layer),
40 Layer.provideMerge(CliKit.layer({ input: false })),
41 );
42};
43
44describe(
45 "PrismaEnvironment",
46 { tags: ["unit", "provider:prisma", "local"] },
47 () => {
48 it.effect("resolves stored credentials and API base URL from config", () =>
49 Effect.gen(function* () {
50 const env = yield* PrismaEnvironment;
51
52 expect(env.type).toBe("serviceToken");
53 expect(env.source).toEqual({ type: "stored" });
54 expect(Redacted.value(env.serviceToken)).toBe("test-token");
55 expect(env.baseUrl).toBe("https://control-plane.prisma.test");
56 }).pipe(
57 Effect.provide(
58 testLayer(
59 { PRISMA_API_URL: "https://control-plane.prisma.test" },
60 { storedToken: "test-token" },
61 ),
62 ),
63 ),
64 );
65
66 it.effect("prefers PRISMA_API_URL over PRISMA_MANAGEMENT_API_URL", () =>
67 Effect.gen(function* () {
68 const env = yield* PrismaEnvironment;
69
70 expect(env.baseUrl).toBe("https://api-url.prisma.test");
71 }).pipe(
72 Effect.provide(
73 testLayer(
74 {
75 PRISMA_API_URL: "https://api-url.prisma.test",
76 PRISMA_MANAGEMENT_API_URL: "https://management-url.prisma.test",
77 },
78 { storedToken: "test-token" },
79 ),
80 ),
81 ),
82 );
83
84 it.effect("resolves stored profile credentials", () =>
85 Effect.gen(function* () {
86 const env = yield* PrismaEnvironment;
87
88 expect(env.type).toBe("serviceToken");
89 expect(env.source).toEqual({ type: "stored" });
90 expect(Redacted.value(env.serviceToken)).toBe("stored-token");
91 expect(env.baseUrl).toBe("https://api.prisma.io");
92 }).pipe(
93 Effect.provide(
94 testLayer(
95 {},
96 {
97 storedToken: "stored-token",
98 },
99 ),
100 ),
101 ),
102 );
103
104 it.effect("allows HTTP only for loopback Management API URLs", () =>
105 Effect.gen(function* () {
106 const env = yield* PrismaEnvironment;
107 expect(env.baseUrl).toBe("http://127.0.0.1:8787");
108 }).pipe(
109 Effect.provide(
110 testLayer(
111 { PRISMA_API_URL: "http://127.0.0.1:8787/" },
112 { storedToken: "test-token" },
113 ),
114 ),
115 ),
116 );
117
118 it.effect("rejects insecure remote Management API URLs", () =>
119 Effect.gen(function* () {
120 const exit = yield* PrismaEnvironment.pipe(
121 Effect.provide(
122 testLayer(
123 { PRISMA_API_URL: "http://management.prisma.test" },
124 { storedToken: "test-token" },
125 ),
126 ),
127 Effect.exit,
128 );
129 expect(exit._tag).toBe("Failure");
130 if (exit._tag === "Failure") {
131 expect(String(exit.cause)).toContain("must use HTTPS");
132 }
133 }),
134 );
135
136 it.effect(
137 "rejects Management API URLs with credentials or path state",
138 () =>
139 Effect.gen(function* () {
140 const credentialExit = yield* PrismaEnvironment.pipe(
141 Effect.provide(
142 testLayer(
143 { PRISMA_API_URL: "https://token@api.prisma.test" },
144 { storedToken: "test-token" },
145 ),
146 ),
147 Effect.exit,
148 );
149 expect(credentialExit._tag).toBe("Failure");
150 if (credentialExit._tag === "Failure") {
151 expect(String(credentialExit.cause)).toContain(
152 "must not contain credentials",
153 );
154 }
155
156 const pathExit = yield* PrismaEnvironment.pipe(
157 Effect.provide(
158 testLayer(
159 { PRISMA_API_URL: "https://api.prisma.test/proxy" },
160 { storedToken: "test-token" },
161 ),
162 ),
163 Effect.exit,
164 );
165 expect(pathExit._tag).toBe("Failure");
166 if (pathExit._tag === "Failure") {
167 expect(String(pathExit.cause)).toContain("must be an origin");
168 }
169 }),
170 );
171 },
172);