aboutsummaryrefslogtreecommitdiff
path: root/pgruntime/target_test.go
blob: d62721cdd08b1c9663a41d95fd2b90893917eea1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
// SPDX-FileCopyrightText: 2026 Stefan Majewsky <majewsky@gmx.net>
// SPDX-License-Identifier: Apache-2.0

package pgruntime_test

import (
	"net/url"
	"strings"
	"testing"

	"go.xyrillian.de/gg/assert"
	"go.xyrillian.de/gg/pgruntime"
)

func TestParseConnectionTargetSuccess(t *testing.T) {
	testCases := map[string]pgruntime.ConnectionTarget{
		// minimal case: just the required fields are set
		`postgresql://alice@localhost/bookstore`: {
			HostName:     "localhost",
			UserName:     "alice",
			DatabaseName: "bookstore",
		},
		// maximal case: all required fields are set
		`postgres://alice:swordfish@db.example.com:5432/bookstore?sslmode=prefer&application_name=frontdesk`: {
			HostName:          "db.example.com",
			Port:              "5432",
			UserName:          "alice",
			Password:          "swordfish",
			DatabaseName:      "bookstore",
			ConnectionOptions: "sslmode=prefer&application_name=frontdesk",
		},
	}

	for input, expected := range testCases {
		t.Run(input, func(t *testing.T) {
			u, err := url.Parse(input)
			if !assert.ErrEqual(t, err, nil) {
				t.FailNow()
			}
			parsed, err := pgruntime.ParseConnectionTargetFromURL(u)
			if !assert.ErrEqual(t, err, nil) {
				t.FailNow()
			}
			assert.Equal(t, parsed, expected)
			serialized, err := parsed.IntoURL()
			if !assert.ErrEqual(t, err, nil) {
				t.FailNow()
			}
			// IntoURL() always uses postgres://, so this is not always an exact roundtrip
			assert.Equal(t, serialized.String(), strings.ReplaceAll(input, "postgresql:", "postgres:"))
		})
	}
}

func TestParseConnectionTargetFailure(t *testing.T) {
	testCases := map[string]string{
		`https://alice@db.example.com/foo?sslmode=prefer`:            `expected scheme "postgres" or "postgresql", but got "https"`,
		`postgres:foo?sslmode=prefer`:                                `expected authority component, but got rootless path "foo"`,
		`postgres://alice@db.example.com/foo#sslmode=prefer`:         `unexpected fragment "sslmode=prefer"`,
		`postgres://alice@db.example.com::5432/foo?sslmode=prefer`:   `malformed host "db.example.com::5432"`,
		`postgres://alice@db.example.com/foo/bar?sslmode=prefer`:     `malformed database name "foo/bar"`,
		`postgres://alice@db.example.com/foo?sslmode=prefer;foo=bar`: `malformed connection options: invalid semicolon separator in query`,
	}
	for input, expected := range testCases {
		t.Run(input, func(t *testing.T) {
			u, err := url.Parse(input)
			if !assert.ErrEqual(t, err, nil) {
				t.FailNow()
			}
			_, err = pgruntime.ParseConnectionTargetFromURL(u)
			assert.ErrEqual(t, err, "in ParseConnectionTargetFromURL: "+expected)
		})
	}
}

func TestSerializeConnectionOptions(t *testing.T) {
	ct := pgruntime.ConnectionTarget{
		HostName:     "localhost",
		UserName:     "alice",
		DatabaseName: "bookstore",
	}

	// cannot merge ConnectionOptions if the string part is malformed
	ct.ConnectionOptions = `sslmode=prefer;foo=bar`
	ct.ExtraConnectionOptions = url.Values{"application_name": {"frontdesk"}}
	_, err := ct.IntoURL()
	assert.ErrEqual(t, err, `in ConnectionTarget.IntoURL: malformed connection options: invalid semicolon separator in query`)

	// successful trivial merges
	ct.ConnectionOptions = `sslmode=prefer`
	ct.ExtraConnectionOptions = nil
	u, err := ct.IntoURL()
	if assert.ErrEqual(t, err, nil) {
		assert.Equal(t, u.RawQuery, `sslmode=prefer`)
	}

	ct.ConnectionOptions = ""
	ct.ExtraConnectionOptions = url.Values{"application_name": {"frontdesk"}}
	u, err = ct.IntoURL()
	if assert.ErrEqual(t, err, nil) {
		assert.Equal(t, u.RawQuery, `application_name=frontdesk`)
	}

	// successful complex merge
	ct.ConnectionOptions = `sslmode=prefer`
	ct.ExtraConnectionOptions = url.Values{"application_name": {"frontdesk"}}
	u, err = ct.IntoURL()
	if assert.ErrEqual(t, err, nil) {
		assert.Equal(t, u.RawQuery, `application_name=frontdesk&sslmode=prefer`)
	}
}