1import { PgConnection } from "@effect/sql-pg"
2import { assert, describe, it } from "@effect/vitest"
3import { Effect, Fiber, Redacted } from "effect"
4import * as TestClock from "effect/testing/TestClock"
5
6describe("PgConnection config", () => {
7 it.effect.each([
8 { startupParameters: { REPLICATION: "database" } },
9 { startupParameters: { client_encoding: "LATIN1" } },
10 { startupParameters: { "": "value" } },
11 { startupParameters: { search_path: "public\0private" } },
12 { startupOptions: "-c lock_timeout=2345\0" },
13 { url: Redacted.make("postgres://test@localhost/db?options=%00") }
14 ])("rejects invalid startup config %j before connecting", (config) =>
15 Effect.gen(function*() {
16 let connected = false
17 const error = yield* Effect.flip(PgConnection.make({
18 ...config,
19 username: "test",
20 stream: () => {
21 connected = true
22 throw new Error("unexpected connection")
23 }
24 }))
25 assert.isFalse(connected)
26 assert.strictEqual(error.reason._tag, "ConnectionError")
27 }))
28
29 it.effect("interrupts a stalled password provider when connectTimeout expires", () =>
30 Effect.gen(function*() {
31 let connected = false
32 let interrupted = false
33 const fiber = yield* PgConnection.make({
34 username: "test",
35 password: Effect.never.pipe(Effect.onInterrupt(() =>
36 Effect.sync(() => {
37 interrupted = true
38 })
39 )),
40 connectTimeout: "1 second",
41 stream: () => {
42 connected = true
43 throw new Error("unexpected connection")
44 }
45 }).pipe(Effect.flip, Effect.forkScoped)
46
47 yield* TestClock.adjust("1 second")
48 const error = yield* Fiber.join(fiber)
49
50 assert.isTrue(interrupted)
51 assert.isFalse(connected)
52 assert.strictEqual(error.reason._tag, "ConnectionError")
53 assert.strictEqual(error.reason.message, "PgConnection: Connection timed out")
54 assert.isTrue(error.isRetryable)
55 }))
56
57 it.effect.each(["prefer", "allow"])("accepts sslmode=%s in a URL", (sslmode) =>
58 Effect.gen(function*() {
59 let connected = false
60 const error = yield* Effect.flip(PgConnection.make({
61 url: Redacted.make(`postgres://user@localhost/db?sslmode=${sslmode}`),
62 stream: () => {
63 connected = true
64 throw new Error("test connection")
65 }
66 }))
67 assert.isTrue(connected)
68 assert.strictEqual(error.reason._tag, "ConnectionError")
69 assert.strictEqual(error.reason.message, "PgConnection: Failed to connect")
70 }))
71
72 it.effect("rejects an unrecognized sslmode before connecting", () =>
73 Effect.gen(function*() {
74 let connected = false
75 const error = yield* Effect.flip(PgConnection.make({
76 url: Redacted.make("postgresql://user@localhost/db?sslmode=invalid"),
77 stream: () => {
78 connected = true
79 throw new Error("test connection")
80 }
81 }))
82 assert.isFalse(connected)
83 assert.strictEqual(error.reason._tag, "ConnectionError")
84 assert.strictEqual(error.reason.message, "PgConnection: Unrecognized sslmode in URL: \"invalid\"")
85 }))
86
87 it.effect("rejects a non-postgres URL protocol", () =>
88 Effect.gen(function*() {
89 const error = yield* Effect.flip(PgConnection.make({
90 url: Redacted.make("mysql://user@localhost/db")
91 }))
92 assert.strictEqual(error.reason._tag, "ConnectionError")
93 }))
94})