1import * as PgClient from "@effect/sql-pg/PgClient";
2import * as Effect from "effect/Effect";
3import * as Result from "effect/Result";
4import { resolveConnectionOptions, resolveSsl } from "@/SQL/PostgresTls.ts";
5import { describe, expect, it } from "alchemy-test";
6import * as Redacted from "effect/Redacted";
7
8const url = (s: string) => Redacted.make(s);
9
10describe("SQL/PostgresTls resolveSsl", { tags: ["unit", "local"] }, () => {
11 it("resolves sslmode=prefer|allow to TLS on when ssl is implicit", () => {
12 for (const mode of ["prefer", "allow"]) {
13 expect(
14 resolveSsl(
15 url(`postgres://u@ep-x.neon.tech/x?sslmode=${mode}`),
16 undefined,
17 ),
18 ).toBe(true);
19 expect(
20 resolveSsl(
21 url(`postgres://u@127.0.0.1:5432/x?sslmode=${mode}`),
22 undefined,
23 ),
24 ).toBe(true);
25 }
26 });
27
28 it("leaves every other URL to @effect/sql-pg", () => {
29 expect(
30 resolveSsl(url("postgres://u@db.example.com/x"), undefined),
31 ).toBeUndefined();
32 for (const mode of ["disable", "require", "verify-ca", "verify-full"]) {
33 expect(
34 resolveSsl(
35 url(`postgres://u@db.example.com/x?sslmode=${mode}`),
36 undefined,
37 ),
38 ).toBeUndefined();
39 }
40 });
41
42 it("never overrides an explicit ssl option", () => {
43 const explicit = { rejectUnauthorized: false, servername: "override" };
44 expect(
45 resolveSsl(url("postgres://u@db.example.com/x?sslmode=prefer"), explicit),
46 ).toBe(explicit);
47 expect(
48 resolveSsl(url("postgres://u@db.example.com/x?sslmode=prefer"), false),
49 ).toBe(false);
50 expect(
51 resolveSsl(url("postgres://u@db.example.com/x?sslmode=require"), true),
52 ).toBe(true);
53 });
54
55 it("passes malformed URLs through untouched", () => {
56 expect(resolveSsl(url("not a url"), undefined)).toBeUndefined();
57 expect(resolveSsl(url("not a url"), true)).toBe(true);
58 });
59});
60
61describe(
62 "SQL/PostgresTls resolveConnectionOptions",
63 { tags: ["unit", "local"] },
64 () => {
65 const railwayUrl = url(
66 "postgresql://user:p%40ss@db.railway.internal:5432/railway?sslmode=no-verify&connect_timeout=7&application_name=app",
67 );
68
69 it("preserves Railway no-verify TLS semantics in a driver-compatible URL", () => {
70 const options = resolveConnectionOptions(railwayUrl);
71 const parsed = new URL(Redacted.value(options.url));
72 expect(parsed.searchParams.get("sslmode")).toBe("require");
73 expect(parsed.searchParams.get("connect_timeout")).toBe("7");
74 expect(parsed.searchParams.get("application_name")).toBe("app");
75 expect(parsed.password).toBe("p%40ss");
76 expect(options.ssl).toEqual({
77 rejectUnauthorized: false,
78 });
79 expect(Redacted.value(railwayUrl)).toContain("sslmode=no-verify");
80 });
81
82 it("passes Railway URLs through the actual driver parser before opening a transport", async () => {
83 const stopped = new Error("Local transport sentinel");
84 let attempted = false;
85 const result = await Effect.runPromise(
86 Effect.gen(function* () {
87 const sql = yield* PgClient.PgClient;
88 return yield* sql`select 1`;
89 }).pipe(
90 Effect.provide(
91 PgClient.layer({
92 ...resolveConnectionOptions(railwayUrl),
93 stream: () => {
94 attempted = true;
95 throw stopped;
96 },
97 }),
98 ),
99 Effect.scoped,
100 Effect.result,
101 ),
102 );
103 expect(attempted).toBe(true);
104 expect(Result.isFailure(result)).toBe(true);
105 if (Result.isFailure(result)) {
106 expect(result.failure.reason.cause).toBe(stopped);
107 }
108 });
109
110 it("keeps explicitly configured verification and SNI authoritative", () => {
111 expect(resolveConnectionOptions(railwayUrl, true).ssl).toBe(true);
112 expect(
113 resolveConnectionOptions(railwayUrl, {
114 rejectUnauthorized: true,
115 servername: "custom.example.com",
116 ca: "test-ca",
117 }).ssl,
118 ).toEqual({
119 rejectUnauthorized: true,
120 servername: "custom.example.com",
121 ca: "test-ca",
122 });
123 expect(resolveConnectionOptions(railwayUrl, false).ssl).toBe(false);
124 expect(
125 new URL(
126 Redacted.value(resolveConnectionOptions(railwayUrl, false).url),
127 ).searchParams.get("sslmode"),
128 ).toBe("require");
129 });
130
131 it("does not invent SNI for a no-verify IP connection", () => {
132 expect(
133 resolveConnectionOptions(url("postgres://u@[::1]/db?sslmode=no-verify"))
134 .ssl,
135 ).toEqual({ rejectUnauthorized: false });
136 });
137
138 it("preserves unmodified and malformed URLs for driver validation", () => {
139 for (const value of [
140 "postgres://u@db.example/db?sslmode=verify-full",
141 "not a url",
142 ]) {
143 const original = url(value);
144 expect(resolveConnectionOptions(original).url).toBe(original);
145 }
146 });
147 },
148);