1import * as Provider from "@/Provider";
2import { Unowned } from "@/AdoptPolicy";
3import {
4 PrismaApiError,
5 PrismaClient,
6 type PrismaManagementClient,
7} from "@/Prisma/Client";
8import { connectEnvKeys } from "@/Prisma/Connect";
9import { Connection, ConnectionProvider } from "@/Prisma/Connection";
10import type {
11 DatabaseConnection,
12 DatabaseConnectionWithSecrets,
13} from "@/Prisma/Types";
14import { describe, expect, it } from "alchemy-test";
15import {
16 conflict,
17 data,
18 json,
19 makeFakeManagementApi,
20 notFound,
21 page,
22 unhandled,
23} from "./fixtures/FakeManagementApi.ts";
24import * as Effect from "effect/Effect";
25import * as Layer from "effect/Layer";
26import * as Redacted from "effect/Redacted";
27import { AlchemyContext } from "@/AlchemyContext";
28
29const createdAt = "2026-01-01T00:00:00.000Z";
30const instanceId = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
31
32const connection = (id: string, name: string): DatabaseConnection => ({
33 id,
34 type: "connection",
35 url: `https://api.prisma.test/v1/connections/${id}`,
36 name,
37 createdAt,
38 kind: "postgres",
39 endpoints: {
40 direct: { host: "db.prisma.test", port: 5432 },
41 pooled: { host: "pooled.db.prisma.test", port: 5432 },
42 },
43 database: {
44 id: "database-1",
45 url: "https://api.prisma.test/v1/databases/database-1",
46 name: "main",
47 },
48});
49
50const withSecrets = (
51 value: DatabaseConnection,
52 suffix: string,
53): DatabaseConnectionWithSecrets => ({
54 ...value,
55 endpoints: {
56 direct: {
57 host: "db.prisma.test",
58 port: 5432,
59 connectionString: `postgres://direct-${suffix}`,
60 },
61 pooled: {
62 host: "pooled.db.prisma.test",
63 port: 5432,
64 connectionString: `postgres://pooled-${suffix}`,
65 },
66 },
67});
68
69const liveProviderContext = Layer.succeed(AlchemyContext, {
70 dotAlchemy: ".alchemy-test",
71 dev: false,
72 adopt: false,
73});
74
75const providerLayer = (client: PrismaManagementClient) =>
76 ConnectionProvider().pipe(
77 Layer.provide(Layer.succeed(PrismaClient, client)),
78 Layer.provide(liveProviderContext),
79 );
80
81/**
82 * Serve the Management API's connection routes from the same hermetic
83 * handlers these suites already declare. The handlers are synchronous, so the
84 * fake runs them directly and maps their results onto the wire: a handler
85 * returning `undefined` is a 404, everything else a `{ data }` envelope.
86 */
87interface ConnectionHandlers {
88 listDatabaseConnections?: (
89 databaseId: string,
90 query?: any,
91 ) => Effect.Effect<any, any>;
92 getConnection?: (id: string) => Effect.Effect<any, any>;
93 createConnection?: (input: any) => Effect.Effect<any, any>;
94 rotateConnection?: (id: string) => Effect.Effect<any, any>;
95 deleteConnection?: (id: string) => Effect.Effect<any, any>;
96}
97
98const connectionApi = (handlers: ConnectionHandlers) =>
99 makeFakeManagementApi((request) => {
100 const segments = request.pathname.split("/").filter((s) => s.length > 0);
101 const respond = (value: unknown) =>
102 value === undefined ? notFound("not found") : data(value);
103
104 if (
105 segments.length === 4 &&
106 segments[1] === "databases" &&
107 segments[3] === "connections" &&
108 request.method === "GET"
109 ) {
110 return page(
111 Effect.runSync(handlers.listDatabaseConnections!(segments[2]!)),
112 );
113 }
114 if (request.pathname === "/v1/connections" && request.method === "POST") {
115 const created = Effect.runSync(
116 handlers.createConnection!(request.bodyJson),
117 );
118 // A handler may answer with a Response directly to inject a status.
119 return created instanceof Response
120 ? created
121 : data(created, { status: 201 });
122 }
123 if (
124 segments.length === 4 &&
125 segments[1] === "connections" &&
126 segments[3] === "rotate" &&
127 request.method === "POST"
128 ) {
129 return respond(Effect.runSync(handlers.rotateConnection!(segments[2]!)));
130 }
131 if (segments.length === 3 && segments[1] === "connections") {
132 const id = segments[2]!;
133 if (request.method === "GET") {
134 return respond(Effect.runSync(handlers.getConnection!(id)));
135 }
136 if (request.method === "DELETE") {
137 if (handlers.deleteConnection) {
138 Effect.runSync(handlers.deleteConnection(id));
139 }
140 return json(null, { status: 204 });
141 }
142 }
143 return unhandled(request);
144 });
145
146const connectionProps = { database: "database-1", name: "api" };
147
148const reconcileInput = (
149 output?: Connection["Attributes"],
150 olds?: typeof connectionProps,
151 reconcileInstanceId = instanceId,
152) => ({
153 id: "Connection",
154 fqn: "Connection",
155 instanceId: reconcileInstanceId,
156 news: connectionProps,
157 olds,
158 output,
159 session: undefined as never,
160 bindings: [],
161});
162
163const readInput = (
164 connectionsOutput?: Connection["Attributes"],
165 readInstanceId = "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
166) => ({
167 id: "Connection",
168 fqn: "Connection",
169 instanceId: readInstanceId,
170 olds: { database: "database-1", name: "api" },
171 output: connectionsOutput,
172});
173
174describe(
175 "Prisma Connection provider",
176 { tags: ["unit", "provider:prisma", "provider:prisma:connection", "local"] },
177 () => {
178 it.effect(
179 "recovers a crash after create without creating another key",
180 () => {
181 const connections: DatabaseConnection[] = [];
182 let creates = 0;
183 let rotations = 0;
184 const client = {
185 listDatabaseConnections: () => Effect.succeed(connections),
186 createConnection: (input: { name: string }) =>
187 Effect.sync(() => {
188 creates += 1;
189 const created = connection("connection-1", input.name);
190 connections.push(created);
191 return withSecrets(created, "created");
192 }),
193 getConnection: (id: string) =>
194 Effect.succeed(connections.find((item) => item.id === id)!),
195 rotateConnection: (id: string) =>
196 Effect.sync(() => {
197 rotations += 1;
198 return withSecrets(
199 connections.find((item) => item.id === id)!,
200 "rotated",
201 );
202 }),
203 } as unknown as PrismaManagementClient;
204
205 return Effect.gen(function* () {
206 const provider = yield* Provider.findProvider(Connection);
207 const first = yield* provider.reconcile(reconcileInput());
208 // Simulate create succeeding but state persistence failing. Refresh
209 // recovers the deterministic key without its one-time credentials.
210 const observed = yield* provider.read!(
211 readInput(undefined, instanceId),
212 );
213 expect(observed).toBeDefined();
214 expect(Unowned.is(observed)).toBe(false);
215 expect(observed?.directConnectionString).toBeUndefined();
216 const recovered = yield* provider.reconcile(
217 reconcileInput(observed!),
218 );
219
220 expect(creates).toBe(1);
221 expect(rotations).toBe(1);
222 expect(connections[0]?.name).toBe("api-aaaaaaaaaaaa");
223 expect(first.connectionId).toBe("connection-1");
224 expect(recovered.connectionId).toBe("connection-1");
225 expect(Redacted.value(recovered.directConnectionString!)).toBe(
226 "postgres://direct-rotated",
227 );
228 }).pipe(
229 Effect.provide(providerLayer(client)),
230 Effect.provide(connectionApi(client).layer),
231 );
232 },
233 { tags: ["provider:prisma:database"] },
234 );
235
236 it.effect(
237 "recovers credentials after a generated connection create conflict",
238 () => {
239 const connections: DatabaseConnection[] = [];
240 let creates = 0;
241 let rotations = 0;
242 const client = {
243 listDatabaseConnections: () => Effect.succeed(connections),
244 // Served as a real 409 below; distilled's status matcher is what
245 // turns it into the `Conflict` tag the recovery path catches.
246 createConnection: (input: { name: string }) =>
247 Effect.sync(() => {
248 creates += 1;
249 connections.push(connection("connection-1", input.name));
250 return conflict("already exists");
251 }),
252 rotateConnection: (id: string) =>
253 Effect.sync(() => {
254 rotations += 1;
255 return withSecrets(
256 connections.find((item) => item.id === id)!,
257 "recovered",
258 );
259 }),
260 } as unknown as PrismaManagementClient;
261
262 return Effect.gen(function* () {
263 const provider = yield* Connection.Provider;
264 const recovered = yield* provider.reconcile(reconcileInput());
265
266 expect(creates).toBe(1);
267 expect(rotations).toBe(1);
268 expect(recovered.connectionId).toBe("connection-1");
269 expect(Redacted.value(recovered.directConnectionString!)).toBe(
270 "postgres://direct-recovered",
271 );
272 }).pipe(
273 Effect.provide(providerLayer(client)),
274 Effect.provide(connectionApi(client).layer),
275 );
276 },
277 { tags: ["provider:prisma:database"] },
278 );
279
280 it.effect(
281 "recovers exact generated credentials and supports rotation",
282 () => {
283 const existing = connection("connection-1", "api-aaaaaaaaaaaa");
284 let rotations = 0;
285 const client = {
286 getConnection: () => Effect.succeed(existing),
287 rotateConnection: () =>
288 Effect.sync(() => {
289 rotations += 1;
290 return withSecrets(existing, "adopted");
291 }),
292 } as unknown as PrismaManagementClient;
293 const output: Connection["Attributes"] = {
294 connectionId: existing.id,
295 connectionName: existing.name,
296 databaseId: existing.database.id,
297 kind: existing.kind,
298 createdAt,
299 directConnectionString: undefined,
300 pooledConnectionString: undefined,
301 accelerateConnectionString: undefined,
302 host: "db.prisma.test",
303 user: undefined,
304 password: undefined,
305 databaseUrl: undefined,
306 origin: undefined,
307 pooledOrigin: undefined,
308 };
309
310 return Effect.gen(function* () {
311 const provider = yield* Connection.Provider;
312 const recovered = yield* provider.reconcile({
313 ...reconcileInput(output),
314 olds: undefined,
315 });
316 expect(Redacted.value(recovered.directConnectionString!)).toBe(
317 "postgres://direct-adopted",
318 );
319 expect(rotations).toBe(1);
320
321 const stable = yield* provider.reconcile(
322 reconcileInput(recovered, connectionProps),
323 );
324 expect(Redacted.value(stable.directConnectionString!)).toBe(
325 "postgres://direct-adopted",
326 );
327 expect(rotations).toBe(1);
328
329 const optedIn = yield* provider.reconcile({
330 ...reconcileInput(recovered, connectionProps),
331 news: { ...connectionProps, rotate: true },
332 });
333 expect(rotations).toBe(2);
334 expect(Redacted.value(optedIn.directConnectionString!)).toBe(
335 "postgres://direct-adopted",
336 );
337 }).pipe(
338 Effect.provide(providerLayer(client)),
339 Effect.provide(connectionApi(client).layer),
340 );
341 },
342 { tags: ["provider:prisma:database"] },
343 );
344
345 it.effect("rejects a blank connection name before calling Prisma", () => {
346 let listed = false;
347 const client = {
348 listDatabaseConnections: () =>
349 Effect.sync(() => {
350 listed = true;
351 return [];
352 }),
353 } as unknown as PrismaManagementClient;
354
355 return Effect.gen(function* () {
356 const provider = yield* Connection.Provider;
357 const error = yield* provider
358 .reconcile({
359 ...reconcileInput(),
360 news: { database: "database-1", name: " " },
361 })
362 .pipe(Effect.flip);
363
364 expect(String(error)).toContain(
365 "must contain at least one non-space character",
366 );
367 expect(listed).toBe(false);
368 }).pipe(
369 Effect.provide(providerLayer(client)),
370 Effect.provide(connectionApi(client).layer),
371 );
372 });
373
374 it.effect(
375 "does not rotate again when the rotate flag is reset",
376 () => {
377 const existing = connection("connection-1", "api-aaaaaaaaaaaa");
378 let rotations = 0;
379 const client = {
380 getConnection: () => Effect.succeed(existing),
381 rotateConnection: () =>
382 Effect.sync(() => {
383 rotations += 1;
384 return withSecrets(existing, "unexpected");
385 }),
386 } as unknown as PrismaManagementClient;
387 const output: Connection["Attributes"] = {
388 connectionId: existing.id,
389 connectionName: existing.name,
390 databaseId: existing.database.id,
391 kind: existing.kind,
392 createdAt,
393 directConnectionString: Redacted.make("postgres://direct-current"),
394 pooledConnectionString: Redacted.make("postgres://pooled-current"),
395 accelerateConnectionString: undefined,
396 host: "db.prisma.test",
397 user: "app",
398 password: Redacted.make("current-password"),
399 databaseUrl: Redacted.make("postgres://pooled-current"),
400 origin: undefined,
401 pooledOrigin: undefined,
402 };
403
404 return Effect.gen(function* () {
405 const provider = yield* Connection.Provider;
406 const recovered = yield* provider.reconcile({
407 ...reconcileInput(output),
408 news: { ...connectionProps, rotate: false },
409 olds: { ...connectionProps, rotate: true },
410 });
411
412 expect(rotations).toBe(0);
413 expect(Redacted.value(recovered.directConnectionString!)).toBe(
414 "postgres://direct-current",
415 );
416 }).pipe(
417 Effect.provide(providerLayer(client)),
418 Effect.provide(connectionApi(client).layer),
419 );
420 },
421 { tags: ["provider:prisma:database"] },
422 );
423
424 it.effect(
425 "rejects a persisted connection with mismatched identity",
426 () => {
427 const wrong = {
428 ...connection("connection-1", "api-aaaaaaaaaaaa"),
429 database: {
430 id: "database-foreign",
431 url: "https://api.prisma.test/v1/databases/database-foreign",
432 name: "foreign",
433 },
434 };
435 let rotations = 0;
436 const client = {
437 getConnection: () => Effect.succeed(wrong),
438 rotateConnection: () =>
439 Effect.sync(() => {
440 rotations += 1;
441 return withSecrets(wrong, "unexpected");
442 }),
443 } as unknown as PrismaManagementClient;
444 const output: Connection["Attributes"] = {
445 connectionId: wrong.id,
446 connectionName: wrong.name,
447 databaseId: "database-foreign",
448 kind: wrong.kind,
449 createdAt,
450 directConnectionString: undefined,
451 pooledConnectionString: undefined,
452 accelerateConnectionString: undefined,
453 host: undefined,
454 user: undefined,
455 password: undefined,
456 databaseUrl: undefined,
457 origin: undefined,
458 pooledOrigin: undefined,
459 };
460
461 return Effect.gen(function* () {
462 const provider = yield* Connection.Provider;
463 const diff = yield* provider.diff!({
464 id: "Connection",
465 fqn: "Connection",
466 instanceId,
467 olds: connectionProps,
468 news: connectionProps,
469 output,
470 oldBindings: [],
471 newBindings: [],
472 });
473 expect(diff).toEqual({ action: "replace" });
474
475 const error = yield* provider
476 .reconcile(reconcileInput(output, connectionProps))
477 .pipe(Effect.flip);
478 expect(String(error)).toContain("mismatched identity");
479 expect(rotations).toBe(0);
480 }).pipe(
481 Effect.provide(providerLayer(client)),
482 Effect.provide(connectionApi(client).layer),
483 );
484 },
485 { tags: ["provider:prisma:database"] },
486 );
487
488 it.effect(
489 "recovers its deterministic key as owned while creating",
490 () => {
491 const existing = connection("connection-1", "api-aaaaaaaaaaaa");
492 const client = {
493 listDatabaseConnections: () =>
494 Effect.succeed([
495 existing,
496 connection("connection-foreign", "api-bbbbbbbbbbbb"),
497 ]),
498 } as unknown as PrismaManagementClient;
499
500 return Effect.gen(function* () {
501 const provider = yield* Provider.findProvider(Connection);
502 const output = yield* provider.read!(
503 readInput(undefined, instanceId),
504 );
505
506 expect(output?.connectionId).toBe("connection-1");
507 expect(Unowned.is(output)).toBe(false);
508 }).pipe(
509 Effect.provide(providerLayer(client)),
510 Effect.provide(connectionApi(client).layer),
511 );
512 },
513 { tags: ["provider:prisma:database"] },
514 );
515
516 it.effect(
517 "reports a foreign generated key as unowned on a cold probe",
518 () => {
519 const existing = connection("connection-1", "api-aaaaaaaaaaaa");
520 const client = {
521 listDatabaseConnections: () => Effect.succeed([existing]),
522 } as unknown as PrismaManagementClient;
523
524 return Effect.gen(function* () {
525 const provider = yield* Provider.findProvider(Connection);
526 const output = yield* provider.read!(readInput());
527
528 expect(output?.connectionId).toBe("connection-1");
529 expect(Unowned.is(output)).toBe(true);
530 }).pipe(
531 Effect.provide(providerLayer(client)),
532 Effect.provide(connectionApi(client).layer),
533 );
534 },
535 { tags: ["provider:prisma:database"] },
536 );
537
538 it.effect(
539 "preserves an adopted connection from an older generated instance name",
540 () => {
541 const currentInstanceId = "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb";
542 const existing = connection("connection-1", "api-aaaaaaaaaaaa");
543 let creates = 0;
544 let rotations = 0;
545 const client = {
546 listDatabaseConnections: () => Effect.succeed([existing]),
547 getConnection: () => Effect.succeed(existing),
548 createConnection: () =>
549 Effect.sync(() => {
550 creates += 1;
551 return withSecrets(existing, "unexpected");
552 }),
553 rotateConnection: () =>
554 Effect.sync(() => {
555 rotations += 1;
556 return withSecrets(existing, "adopted-old-generated");
557 }),
558 } as unknown as PrismaManagementClient;
559
560 return Effect.gen(function* () {
561 const provider = yield* Provider.findProvider(Connection);
562 const observed = yield* provider.read!(
563 readInput(undefined, currentInstanceId),
564 );
565 expect(observed?.connectionName).toBe("api-aaaaaaaaaaaa");
566 expect(Unowned.is(observed)).toBe(true);
567
568 const adopted = yield* provider.reconcile(
569 reconcileInput(observed!, undefined, currentInstanceId),
570 );
571 expect(adopted.connectionName).toBe("api-aaaaaaaaaaaa");
572 expect(adopted.directConnectionString).toBeUndefined();
573 expect(creates).toBe(0);
574 expect(rotations).toBe(0);
575
576 const diff = yield* provider.diff!({
577 id: "Connection",
578 fqn: "Connection",
579 instanceId: currentInstanceId,
580 olds: connectionProps,
581 news: connectionProps,
582 output: adopted,
583 oldBindings: [],
584 newBindings: [],
585 });
586 expect(diff).toBeUndefined();
587
588 const rotated = yield* provider.reconcile({
589 ...reconcileInput(adopted, connectionProps, currentInstanceId),
590 news: { ...connectionProps, rotate: true },
591 });
592 expect(rotations).toBe(1);
593 expect(Redacted.value(rotated.directConnectionString!)).toBe(
594 "postgres://direct-adopted-old-generated",
595 );
596
597 const refreshed = yield* provider.read!(
598 readInput(rotated, currentInstanceId),
599 );
600 expect(refreshed?.connectionName).toBe("api-aaaaaaaaaaaa");
601 expect(Redacted.value(refreshed!.directConnectionString!)).toBe(
602 "postgres://direct-adopted-old-generated",
603 );
604 }).pipe(
605 Effect.provide(providerLayer(client)),
606 Effect.provide(connectionApi(client).layer),
607 );
608 },
609 { tags: ["provider:prisma:database"] },
610 );
611
612 it.effect(
613 "preserves an adopted connection with its natural name",
614 () => {
615 const existing = connection("connection-1", "api");
616 let creates = 0;
617 let rotations = 0;
618 const client = {
619 listDatabaseConnections: () => Effect.succeed([existing]),
620 getConnection: () => Effect.succeed(existing),
621 createConnection: () =>
622 Effect.sync(() => {
623 creates += 1;
624 return withSecrets(existing, "unexpected");
625 }),
626 rotateConnection: () =>
627 Effect.sync(() => {
628 rotations += 1;
629 return withSecrets(existing, "adopted-natural");
630 }),
631 } as unknown as PrismaManagementClient;
632
633 return Effect.gen(function* () {
634 const provider = yield* Provider.findProvider(Connection);
635 const observed = yield* provider.read!(
636 readInput(undefined, instanceId),
637 );
638 expect(observed?.connectionName).toBe("api");
639 expect(Unowned.is(observed)).toBe(true);
640
641 const adopted = yield* provider.reconcile(reconcileInput(observed!));
642 expect(adopted.connectionName).toBe("api");
643 expect(adopted.directConnectionString).toBeUndefined();
644 expect(creates).toBe(0);
645 expect(rotations).toBe(0);
646
647 const rotated = yield* provider.reconcile({
648 ...reconcileInput(adopted, connectionProps),
649 news: { ...connectionProps, rotate: true },
650 });
651 expect(rotations).toBe(1);
652 expect(Redacted.value(rotated.directConnectionString!)).toBe(
653 "postgres://direct-adopted-natural",
654 );
655 }).pipe(
656 Effect.provide(providerLayer(client)),
657 Effect.provide(connectionApi(client).layer),
658 );
659 },
660 { tags: ["provider:prisma:database"] },
661 );
662
663 it.effect(
664 "rejects drift from an adopted physical connection name",
665 () => {
666 const persisted = connection("connection-1", "api");
667 const renamed = connection("connection-1", "api-bbbbbbbbbbbb");
668 const client = {
669 getConnection: () => Effect.succeed(renamed),
670 } as unknown as PrismaManagementClient;
671 const output: Connection["Attributes"] = {
672 connectionId: persisted.id,
673 connectionName: persisted.name,
674 databaseId: persisted.database.id,
675 kind: persisted.kind,
676 createdAt: persisted.createdAt,
677 directConnectionString: undefined,
678 pooledConnectionString: undefined,
679 accelerateConnectionString: undefined,
680 host: persisted.endpoints.direct?.host,
681 user: undefined,
682 databaseUrl: undefined,
683 origin: undefined,
684 pooledOrigin: undefined,
685 password: undefined,
686 };
687
688 return Effect.gen(function* () {
689 const provider = yield* Provider.findProvider(Connection);
690 const error = yield* provider.read!(
691 readInput(output, instanceId),
692 ).pipe(Effect.flip);
693
694 expect(String(error)).toContain("no longer matches persisted");
695 expect(String(error)).toContain("name 'api'");
696 }).pipe(
697 Effect.provide(providerLayer(client)),
698 Effect.provide(connectionApi(client).layer),
699 );
700 },
701 { tags: ["provider:prisma:database"] },
702 );
703
704 it.effect(
705 "fails ambiguous generated-name recovery",
706 () => {
707 const client = {
708 listDatabaseConnections: () =>
709 Effect.succeed([
710 connection("connection-1", "api-aaaaaaaaaaaa"),
711 connection("connection-2", "api-bbbbbbbbbbbb"),
712 ]),
713 } as unknown as PrismaManagementClient;
714
715 return Effect.gen(function* () {
716 const provider = yield* Provider.findProvider(Connection);
717 const error = yield* provider.read!(
718 readInput(undefined, "cccccccccccccccccccccccccccccccc"),
719 ).pipe(Effect.flip);
720
721 expect(String(error)).toContain("has 2 connections named");
722 expect(String(error)).toContain("<instance-id>");
723 }).pipe(
724 Effect.provide(providerLayer(client)),
725 Effect.provide(connectionApi(client).layer),
726 );
727 },
728 { tags: ["provider:prisma:database"] },
729 );
730
731 it(
732 "does not collide binding keys after lossy normalization",
733 () => {
734 const hyphenated = connectEnvKeys({
735 FQN: "db-a",
736 LogicalId: "db-a",
737 });
738 const underscored = connectEnvKeys({
739 FQN: "db_a",
740 LogicalId: "db_a",
741 });
742
743 expect(hyphenated.directConnectionString).not.toBe(
744 underscored.directConnectionString,
745 );
746 },
747 { tags: ["provider:prisma:connect"] },
748 );
749
750 it.effect(
751 "defaults the connection name to the logical ID",
752 () => {
753 const created = withSecrets(
754 connection("connection-1", "Connection-aaaaaaaaaaaa"),
755 "new",
756 );
757 let createdName: string | undefined;
758 const client = {
759 getConnection: () => Effect.succeed(undefined),
760 listDatabaseConnections: () => Effect.succeed([]),
761 createConnection: (input: { name: string }) =>
762 Effect.sync(() => {
763 createdName = input.name;
764 return created;
765 }),
766 } as unknown as PrismaManagementClient;
767
768 return Effect.gen(function* () {
769 const provider = yield* Connection.Provider;
770 yield* provider.reconcile({
771 ...reconcileInput(),
772 news: { database: "database-1" },
773 });
774 // name omitted -> logical ID prefix + instance identity suffix
775 expect(createdName).toBe("Connection-aaaaaaaaaaaa");
776 }).pipe(
777 Effect.provide(providerLayer(client)),
778 Effect.provide(connectionApi(client).layer),
779 );
780 },
781 { tags: ["provider:prisma:database"] },
782 );
783
784 it.effect(
785 "materializes databaseUrl and parsed origins on reconcile",
786 () => {
787 const created = withSecrets(
788 connection("connection-1", "api-aaaaaaaaaaaa"),
789 "new",
790 );
791 const client = {
792 getConnection: () => Effect.succeed(undefined),
793 listDatabaseConnections: () => Effect.succeed([]),
794 createConnection: () => Effect.succeed(created),
795 } as unknown as PrismaManagementClient;
796
797 return Effect.gen(function* () {
798 const provider = yield* Connection.Provider;
799 const attrs = yield* provider.reconcile(reconcileInput());
800
801 // databaseUrl prefers the pooled endpoint for application traffic.
802 expect(Redacted.value(attrs.databaseUrl!)).toBe(
803 "postgres://pooled-new",
804 );
805 // origin parses the direct connection string into Hyperdrive's shape.
806 expect(attrs.origin?.host).toBe("direct-new");
807 expect(attrs.origin?.scheme).toBe("postgres");
808 expect(attrs.pooledOrigin?.host).toBe("pooled-new");
809 }).pipe(
810 Effect.provide(providerLayer(client)),
811 Effect.provide(connectionApi(client).layer),
812 );
813 },
814 { tags: ["provider:prisma:database"] },
815 );
816 },
817);