1 package database_test
2
3 import (
4 "encoding/hex"
5 "net/url"
6 "strings"
7 "testing"
8 )
9
10 const reservedChars = "!#$%&'()*+,/:;=?@[]"
11
12 const baseUsername = "username"
13
14
15
16 func TestUserUnencodedReservedURLChars(t *testing.T) {
17 scheme := "database://"
18 urlSuffix := "password@localhost:12345/myDB?someParam=true"
19 urlSuffixAndSep := ":" + urlSuffix
20
21 testcases := []struct {
22 char string
23 parses bool
24 expectedUsername string
25 encodedURL string
26 }{
27 {char: "!", parses: true, expectedUsername: baseUsername + "!",
28 encodedURL: scheme + baseUsername + "%21" + urlSuffixAndSep},
29 {char: "#", parses: true, expectedUsername: "",
30 encodedURL: scheme + baseUsername + "#" + urlSuffixAndSep},
31 {char: "$", parses: true, expectedUsername: baseUsername + "$",
32 encodedURL: scheme + baseUsername + "$" + urlSuffixAndSep},
33 {char: "%", parses: false},
34 {char: "&", parses: true, expectedUsername: baseUsername + "&",
35 encodedURL: scheme + baseUsername + "&" + urlSuffixAndSep},
36 {char: "'", parses: true, expectedUsername: "username'",
37 encodedURL: scheme + baseUsername + "%27" + urlSuffixAndSep},
38 {char: "(", parses: true, expectedUsername: "username(",
39 encodedURL: scheme + baseUsername + "%28" + urlSuffixAndSep},
40 {char: ")", parses: true, expectedUsername: "username)",
41 encodedURL: scheme + baseUsername + "%29" + urlSuffixAndSep},
42 {char: "*", parses: true, expectedUsername: "username*",
43 encodedURL: scheme + baseUsername + "%2A" + urlSuffixAndSep},
44 {char: "+", parses: true, expectedUsername: "username+",
45 encodedURL: scheme + baseUsername + "+" + urlSuffixAndSep},
46 {char: ",", parses: true, expectedUsername: "username,",
47 encodedURL: scheme + baseUsername + "," + urlSuffixAndSep},
48 {char: "/", parses: true, expectedUsername: "",
49 encodedURL: scheme + baseUsername + "/" + urlSuffixAndSep},
50 {char: ":", parses: true, expectedUsername: baseUsername,
51 encodedURL: scheme + baseUsername + ":%3A" + urlSuffix},
52 {char: ";", parses: true, expectedUsername: "username;",
53 encodedURL: scheme + baseUsername + ";" + urlSuffixAndSep},
54 {char: "=", parses: true, expectedUsername: "username=",
55 encodedURL: scheme + baseUsername + "=" + urlSuffixAndSep},
56 {char: "?", parses: true, expectedUsername: "",
57 encodedURL: scheme + baseUsername + "?" + urlSuffixAndSep},
58 {char: "@", parses: true, expectedUsername: "username@",
59 encodedURL: scheme + baseUsername + "%40" + urlSuffixAndSep},
60 {char: "[", parses: false},
61 {char: "]", parses: false},
62 }
63
64 testedChars := make([]string, 0, len(reservedChars))
65 for _, tc := range testcases {
66 testedChars = append(testedChars, tc.char)
67 t.Run("reserved char "+tc.char, func(t *testing.T) {
68 s := scheme + baseUsername + tc.char + urlSuffixAndSep
69 u, err := url.Parse(s)
70 if err == nil {
71 if !tc.parses {
72 t.Error("Unexpectedly parsed reserved character. url:", s)
73 return
74 }
75 var username string
76 if u.User != nil {
77 username = u.User.Username()
78 }
79 if username != tc.expectedUsername {
80 t.Error("Got unexpected username:", username, "!=", tc.expectedUsername)
81 }
82 if s := u.String(); s != tc.encodedURL {
83 t.Error("Got unexpected encoded URL:", s, "!=", tc.encodedURL)
84 }
85 } else {
86 if tc.parses {
87 t.Error("Failed to parse reserved character. url:", s)
88 }
89 }
90 })
91 }
92
93 t.Run("All reserved chars tested", func(t *testing.T) {
94 if s := strings.Join(testedChars, ""); s != reservedChars {
95 t.Error("Not all reserved URL characters were tested:", s, "!=", reservedChars)
96 }
97 })
98 }
99
100 func TestUserEncodedReservedURLChars(t *testing.T) {
101 scheme := "database://"
102 urlSuffix := "password@localhost:12345/myDB?someParam=true"
103 urlSuffixAndSep := ":" + urlSuffix
104
105 for _, c := range reservedChars {
106 c := string(c)
107 t.Run("reserved char "+c, func(t *testing.T) {
108 encodedChar := "%" + hex.EncodeToString([]byte(c))
109 s := scheme + baseUsername + encodedChar + urlSuffixAndSep
110 expectedUsername := baseUsername + c
111 u, err := url.Parse(s)
112 if err != nil {
113 t.Fatal("Failed to parse url with encoded reserved character. url:", s)
114 }
115 if u.User == nil {
116 t.Fatal("Failed to parse userinfo with encoded reserve character. url:", s)
117 }
118 if username := u.User.Username(); username != expectedUsername {
119 t.Fatal("Got unexpected username:", username, "!=", expectedUsername)
120 }
121 })
122 }
123 }
124
125
126
127 func TestPasswordUnencodedReservedURLChars(t *testing.T) {
128 username := baseUsername
129 schemeAndUsernameAndSep := "database://" + username + ":"
130 basePassword := "password"
131 urlSuffixAndSep := "@localhost:12345/myDB?someParam=true"
132
133 testcases := []struct {
134 char string
135 parses bool
136 expectedUsername string
137 expectedPassword string
138 encodedURL string
139 }{
140 {char: "!", parses: true, expectedUsername: username, expectedPassword: basePassword + "!",
141 encodedURL: schemeAndUsernameAndSep + basePassword + "%21" + urlSuffixAndSep},
142 {char: "#", parses: false},
143 {char: "$", parses: true, expectedUsername: username, expectedPassword: basePassword + "$",
144 encodedURL: schemeAndUsernameAndSep + basePassword + "$" + urlSuffixAndSep},
145 {char: "%", parses: false},
146 {char: "&", parses: true, expectedUsername: username, expectedPassword: basePassword + "&",
147 encodedURL: schemeAndUsernameAndSep + basePassword + "&" + urlSuffixAndSep},
148 {char: "'", parses: true, expectedUsername: username, expectedPassword: "password'",
149 encodedURL: schemeAndUsernameAndSep + basePassword + "%27" + urlSuffixAndSep},
150 {char: "(", parses: true, expectedUsername: username, expectedPassword: "password(",
151 encodedURL: schemeAndUsernameAndSep + basePassword + "%28" + urlSuffixAndSep},
152 {char: ")", parses: true, expectedUsername: username, expectedPassword: "password)",
153 encodedURL: schemeAndUsernameAndSep + basePassword + "%29" + urlSuffixAndSep},
154 {char: "*", parses: true, expectedUsername: username, expectedPassword: "password*",
155 encodedURL: schemeAndUsernameAndSep + basePassword + "%2A" + urlSuffixAndSep},
156 {char: "+", parses: true, expectedUsername: username, expectedPassword: "password+",
157 encodedURL: schemeAndUsernameAndSep + basePassword + "+" + urlSuffixAndSep},
158 {char: ",", parses: true, expectedUsername: username, expectedPassword: "password,",
159 encodedURL: schemeAndUsernameAndSep + basePassword + "," + urlSuffixAndSep},
160 {char: "/", parses: false},
161 {char: ":", parses: true, expectedUsername: username, expectedPassword: "password:",
162 encodedURL: schemeAndUsernameAndSep + basePassword + "%3A" + urlSuffixAndSep},
163 {char: ";", parses: true, expectedUsername: username, expectedPassword: "password;",
164 encodedURL: schemeAndUsernameAndSep + basePassword + ";" + urlSuffixAndSep},
165 {char: "=", parses: true, expectedUsername: username, expectedPassword: "password=",
166 encodedURL: schemeAndUsernameAndSep + basePassword + "=" + urlSuffixAndSep},
167 {char: "?", parses: false},
168 {char: "@", parses: true, expectedUsername: username, expectedPassword: "password@",
169 encodedURL: schemeAndUsernameAndSep + basePassword + "%40" + urlSuffixAndSep},
170 {char: "[", parses: false},
171 {char: "]", parses: false},
172 }
173
174 testedChars := make([]string, 0, len(reservedChars))
175 for _, tc := range testcases {
176 testedChars = append(testedChars, tc.char)
177 t.Run("reserved char "+tc.char, func(t *testing.T) {
178 s := schemeAndUsernameAndSep + basePassword + tc.char + urlSuffixAndSep
179 u, err := url.Parse(s)
180 if err == nil {
181 if !tc.parses {
182 t.Error("Unexpectedly parsed reserved character. url:", s)
183 return
184 }
185 var username, password string
186 if u.User != nil {
187 username = u.User.Username()
188 password, _ = u.User.Password()
189 }
190 if username != tc.expectedUsername {
191 t.Error("Got unexpected username:", username, "!=", tc.expectedUsername)
192 }
193 if password != tc.expectedPassword {
194 t.Error("Got unexpected password:", password, "!=", tc.expectedPassword)
195 }
196 if s := u.String(); s != tc.encodedURL {
197 t.Error("Got unexpected encoded URL:", s, "!=", tc.encodedURL)
198 }
199 } else {
200 if tc.parses {
201 t.Error("Failed to parse reserved character. url:", s)
202 }
203 }
204 })
205 }
206
207 t.Run("All reserved chars tested", func(t *testing.T) {
208 if s := strings.Join(testedChars, ""); s != reservedChars {
209 t.Error("Not all reserved URL characters were tested:", s, "!=", reservedChars)
210 }
211 })
212 }
213
214 func TestPasswordEncodedReservedURLChars(t *testing.T) {
215 username := baseUsername
216 schemeAndUsernameAndSep := "database://" + username + ":"
217 basePassword := "password"
218 urlSuffixAndSep := "@localhost:12345/myDB?someParam=true"
219
220 for _, c := range reservedChars {
221 c := string(c)
222 t.Run("reserved char "+c, func(t *testing.T) {
223 encodedChar := "%" + hex.EncodeToString([]byte(c))
224 s := schemeAndUsernameAndSep + basePassword + encodedChar + urlSuffixAndSep
225 expectedPassword := basePassword + c
226 u, err := url.Parse(s)
227 if err != nil {
228 t.Fatal("Failed to parse url with encoded reserved character. url:", s)
229 }
230 if u.User == nil {
231 t.Fatal("Failed to parse userinfo with encoded reserve character. url:", s)
232 }
233 if n := u.User.Username(); n != username {
234 t.Fatal("Got unexpected username:", n, "!=", username)
235 }
236 if p, _ := u.User.Password(); p != expectedPassword {
237 t.Fatal("Got unexpected password:", p, "!=", expectedPassword)
238 }
239 })
240 }
241 }
242
View as plain text