1
2
3
4
5
6
7 package connstring_test
8
9 import (
10 "fmt"
11 "testing"
12 "time"
13
14 "go.mongodb.org/mongo-driver/internal/assert"
15 "go.mongodb.org/mongo-driver/internal/require"
16 "go.mongodb.org/mongo-driver/x/mongo/driver/connstring"
17 )
18
19 func TestAppName(t *testing.T) {
20 tests := []struct {
21 s string
22 expected string
23 err bool
24 }{
25 {s: "appName=Funny", expected: "Funny"},
26 {s: "appName=awesome", expected: "awesome"},
27 {s: "appName=", expected: ""},
28 }
29
30 for _, test := range tests {
31 s := fmt.Sprintf("mongodb://localhost/?%s", test.s)
32 t.Run(s, func(t *testing.T) {
33 cs, err := connstring.ParseAndValidate(s)
34 if test.err {
35 require.Error(t, err)
36 } else {
37 require.NoError(t, err)
38 require.Equal(t, test.expected, cs.AppName)
39 }
40 })
41 }
42 }
43
44 func TestAuthMechanism(t *testing.T) {
45 tests := []struct {
46 s string
47 expected string
48 err bool
49 }{
50 {s: "authMechanism=scram-sha-1", expected: "scram-sha-1"},
51 {s: "authMechanism=scram-sha-256", expected: "scram-sha-256"},
52 {s: "authMechanism=mongodb-CR", expected: "mongodb-CR"},
53 {s: "authMechanism=plain", expected: "plain"},
54 }
55
56 for _, test := range tests {
57 s := fmt.Sprintf("mongodb://user:pass@localhost/?%s", test.s)
58 t.Run(s, func(t *testing.T) {
59 cs, err := connstring.ParseAndValidate(s)
60 if test.err {
61 require.Error(t, err)
62 } else {
63 require.NoError(t, err)
64 require.Equal(t, test.expected, cs.AuthMechanism)
65 }
66 })
67 }
68 }
69
70 func TestAuthSource(t *testing.T) {
71 tests := []struct {
72 s string
73 expected string
74 err bool
75 }{
76 {s: "foobar?authSource=bazqux", expected: "bazqux"},
77 {s: "foobar", expected: "foobar"},
78 {s: "", expected: "admin"},
79 }
80
81 for _, test := range tests {
82 s := fmt.Sprintf("mongodb://user:pass@localhost/%s", test.s)
83 t.Run(s, func(t *testing.T) {
84 cs, err := connstring.ParseAndValidate(s)
85 if test.err {
86 require.Error(t, err)
87 } else {
88 require.NoError(t, err)
89 require.Equal(t, test.expected, cs.AuthSource)
90 }
91 })
92 }
93 }
94
95 func TestConnect(t *testing.T) {
96 tests := []struct {
97 s string
98 expected connstring.ConnectMode
99 err bool
100 }{
101 {s: "connect=automatic", expected: connstring.AutoConnect},
102 {s: "connect=AUTOMATIC", expected: connstring.AutoConnect},
103 {s: "connect=direct", expected: connstring.SingleConnect},
104 {s: "connect=blah", err: true},
105
106
107 {s: "connect=automatic&directConnection=true", err: true},
108 {s: "connect=automatic&directConnection=false", expected: connstring.AutoConnect},
109 {s: "connect=direct&directConnection=true", expected: connstring.SingleConnect},
110 {s: "connect=direct&directConnection=false", err: true},
111
112 {s: "directConnection=true&connect=automatic", err: true},
113 {s: "directConnection=false&connect=automatic", expected: connstring.AutoConnect},
114 {s: "directConnection=true&connect=direct", expected: connstring.SingleConnect},
115 {s: "directConnection=false&connect=direct", err: true},
116 }
117
118 for _, test := range tests {
119 s := fmt.Sprintf("mongodb://localhost/?%s", test.s)
120 t.Run(s, func(t *testing.T) {
121 cs, err := connstring.ParseAndValidate(s)
122 if test.err {
123 require.Error(t, err)
124 } else {
125 require.NoError(t, err)
126 require.Equal(t, test.expected, cs.Connect)
127 }
128 })
129 }
130 }
131
132 func TestDirectConnection(t *testing.T) {
133 testCases := []struct {
134 s string
135 expected bool
136 err bool
137 }{
138 {"directConnection=true", true, false},
139 {"directConnection=false", false, false},
140 {"directConnection=TRUE", true, false},
141 {"directConnection=FALSE", false, false},
142 {"directConnection=blah", false, true},
143 }
144
145 for _, tc := range testCases {
146 s := fmt.Sprintf("mongodb://localhost/?%s", tc.s)
147 t.Run(s, func(t *testing.T) {
148 cs, err := connstring.ParseAndValidate(s)
149 if tc.err {
150 assert.NotNil(t, err, "expected error, got nil")
151 return
152 }
153
154 assert.Nil(t, err, "expected no error, got %v", err)
155 assert.Equal(t, tc.expected, cs.DirectConnection, "expected DirectConnection value %v, got %v", tc.expected,
156 cs.DirectConnection)
157 assert.True(t, cs.DirectConnectionSet, "expected DirectConnectionSet to be true, got false")
158 })
159 }
160 }
161
162 func TestConnectTimeout(t *testing.T) {
163 tests := []struct {
164 s string
165 expected time.Duration
166 err bool
167 }{
168 {s: "connectTimeoutMS=10", expected: time.Duration(10) * time.Millisecond},
169 {s: "connectTimeoutMS=100", expected: time.Duration(100) * time.Millisecond},
170 {s: "connectTimeoutMS=-2", err: true},
171 {s: "connectTimeoutMS=gsdge", err: true},
172 }
173
174 for _, test := range tests {
175 s := fmt.Sprintf("mongodb://localhost/?%s", test.s)
176 t.Run(s, func(t *testing.T) {
177 cs, err := connstring.ParseAndValidate(s)
178 if test.err {
179 require.Error(t, err)
180 } else {
181 require.NoError(t, err)
182 require.Equal(t, test.expected, cs.ConnectTimeout)
183 require.True(t, cs.ConnectTimeoutSet)
184 }
185 })
186 }
187 }
188
189 func TestHeartbeatInterval(t *testing.T) {
190 tests := []struct {
191 s string
192 expected time.Duration
193 err bool
194 }{
195 {s: "heartbeatIntervalMS=10", expected: time.Duration(10) * time.Millisecond},
196 {s: "heartbeatIntervalMS=100", expected: time.Duration(100) * time.Millisecond},
197 {s: "heartbeatIntervalMS=-2", err: true},
198 {s: "heartbeatIntervalMS=gsdge", err: true},
199 }
200
201 for _, test := range tests {
202 s := fmt.Sprintf("mongodb://localhost/?%s", test.s)
203 t.Run(s, func(t *testing.T) {
204 cs, err := connstring.ParseAndValidate(s)
205 if test.err {
206 require.Error(t, err)
207 } else {
208 require.NoError(t, err)
209 require.Equal(t, test.expected, cs.HeartbeatInterval)
210 }
211 })
212 }
213 }
214
215 func TestLocalThreshold(t *testing.T) {
216 tests := []struct {
217 s string
218 expected time.Duration
219 err bool
220 }{
221 {s: "localThresholdMS=0", expected: time.Duration(0) * time.Millisecond},
222 {s: "localThresholdMS=10", expected: time.Duration(10) * time.Millisecond},
223 {s: "localThresholdMS=100", expected: time.Duration(100) * time.Millisecond},
224 {s: "localThresholdMS=-2", err: true},
225 {s: "localThresholdMS=gsdge", err: true},
226 }
227
228 for _, test := range tests {
229 s := fmt.Sprintf("mongodb://localhost/?%s", test.s)
230 t.Run(s, func(t *testing.T) {
231 cs, err := connstring.ParseAndValidate(s)
232 if test.err {
233 require.Error(t, err)
234 } else {
235 require.NoError(t, err)
236 require.Equal(t, test.expected, cs.LocalThreshold)
237 }
238 })
239 }
240 }
241
242 func TestMaxConnIdleTime(t *testing.T) {
243 tests := []struct {
244 s string
245 expected time.Duration
246 err bool
247 }{
248 {s: "maxIdleTimeMS=10", expected: time.Duration(10) * time.Millisecond},
249 {s: "maxIdleTimeMS=100", expected: time.Duration(100) * time.Millisecond},
250 {s: "maxIdleTimeMS=-2", err: true},
251 {s: "maxIdleTimeMS=gsdge", err: true},
252 }
253
254 for _, test := range tests {
255 s := fmt.Sprintf("mongodb://localhost/?%s", test.s)
256 t.Run(s, func(t *testing.T) {
257 cs, err := connstring.ParseAndValidate(s)
258 if test.err {
259 require.Error(t, err)
260 } else {
261 require.NoError(t, err)
262 require.Equal(t, test.expected, cs.MaxConnIdleTime)
263 }
264 })
265 }
266 }
267
268 func TestMaxPoolSize(t *testing.T) {
269 tests := []struct {
270 s string
271 expected uint64
272 err bool
273 }{
274 {s: "maxPoolSize=10", expected: 10},
275 {s: "maxPoolSize=100", expected: 100},
276 {s: "maxPoolSize=-2", err: true},
277 {s: "maxPoolSize=gsdge", err: true},
278 }
279
280 for _, test := range tests {
281 s := fmt.Sprintf("mongodb://localhost/?%s", test.s)
282 t.Run(s, func(t *testing.T) {
283 cs, err := connstring.ParseAndValidate(s)
284 if test.err {
285 require.Error(t, err)
286 } else {
287 require.NoError(t, err)
288 require.True(t, cs.MaxPoolSizeSet)
289 require.Equal(t, test.expected, cs.MaxPoolSize)
290 }
291 })
292 }
293 }
294
295 func TestMinPoolSize(t *testing.T) {
296 tests := []struct {
297 s string
298 expected uint64
299 err bool
300 }{
301 {s: "minPoolSize=10", expected: 10},
302 {s: "minPoolSize=100", expected: 100},
303 {s: "minPoolSize=-2", err: true},
304 {s: "minPoolSize=gsdge", err: true},
305 }
306
307 for _, test := range tests {
308 s := fmt.Sprintf("mongodb://localhost/?%s", test.s)
309 t.Run(s, func(t *testing.T) {
310 cs, err := connstring.ParseAndValidate(s)
311 if test.err {
312 require.Error(t, err)
313 } else {
314 require.NoError(t, err)
315 require.True(t, cs.MinPoolSizeSet)
316 require.Equal(t, test.expected, cs.MinPoolSize)
317 }
318 })
319 }
320 }
321
322 func TestMaxConnecting(t *testing.T) {
323 tests := []struct {
324 s string
325 expected uint64
326 err bool
327 }{
328 {s: "maxConnecting=10", expected: 10},
329 {s: "maxConnecting=100", expected: 100},
330 {s: "maxConnecting=-2", err: true},
331 {s: "maxConnecting=gsdge", err: true},
332 }
333
334 for _, test := range tests {
335 s := fmt.Sprintf("mongodb://localhost/?%s", test.s)
336 t.Run(s, func(t *testing.T) {
337 cs, err := connstring.ParseAndValidate(s)
338 if test.err {
339 require.Error(t, err)
340 } else {
341 require.NoError(t, err)
342 require.True(t, cs.MaxConnectingSet)
343 require.Equal(t, test.expected, cs.MaxConnecting)
344 }
345 })
346 }
347 }
348
349 func TestReadPreference(t *testing.T) {
350 tests := []struct {
351 s string
352 expected string
353 err bool
354 }{
355 {s: "readPreference=primary", expected: "primary"},
356 {s: "readPreference=secondaryPreferred", expected: "secondaryPreferred"},
357 {s: "readPreference=something", expected: "something"},
358 }
359
360 for _, test := range tests {
361 s := fmt.Sprintf("mongodb://localhost/?%s", test.s)
362 t.Run(s, func(t *testing.T) {
363 cs, err := connstring.ParseAndValidate(s)
364 if test.err {
365 require.Error(t, err)
366 } else {
367 require.NoError(t, err)
368 require.Equal(t, test.expected, cs.ReadPreference)
369 }
370 })
371 }
372 }
373
374 func TestReadPreferenceTags(t *testing.T) {
375 tests := []struct {
376 s string
377 expected []map[string]string
378 err bool
379 }{
380 {s: "", expected: nil},
381 {s: "readPreferenceTags=one:1", expected: []map[string]string{{"one": "1"}}},
382 {s: "readPreferenceTags=one:1,two:2", expected: []map[string]string{{"one": "1", "two": "2"}}},
383 {s: "readPreferenceTags=one:1&readPreferenceTags=two:2", expected: []map[string]string{{"one": "1"}, {"two": "2"}}},
384 {s: "readPreferenceTags=one:1:3,two:2", err: true},
385 {s: "readPreferenceTags=one:1&readPreferenceTags=two:2&readPreferenceTags=", expected: []map[string]string{{"one": "1"}, {"two": "2"}, {}}},
386 {s: "readPreferenceTags=", expected: []map[string]string{{}}},
387 }
388
389 for _, test := range tests {
390 s := fmt.Sprintf("mongodb://localhost/?%s", test.s)
391 t.Run(s, func(t *testing.T) {
392 cs, err := connstring.ParseAndValidate(s)
393 if test.err {
394 require.Error(t, err)
395 } else {
396 require.NoError(t, err)
397 require.Equal(t, test.expected, cs.ReadPreferenceTagSets)
398 }
399 })
400 }
401 }
402
403 func TestMaxStaleness(t *testing.T) {
404 tests := []struct {
405 s string
406 expected time.Duration
407 err bool
408 }{
409 {s: "maxStaleness=10", expected: time.Duration(10) * time.Second},
410 {s: "maxStaleness=100", expected: time.Duration(100) * time.Second},
411 {s: "maxStaleness=-2", err: true},
412 {s: "maxStaleness=gsdge", err: true},
413 }
414 for _, test := range tests {
415 s := fmt.Sprintf("mongodb://localhost/?%s", test.s)
416 t.Run(s, func(t *testing.T) {
417 cs, err := connstring.ParseAndValidate(s)
418 if test.err {
419 require.Error(t, err)
420 } else {
421 require.NoError(t, err)
422 require.Equal(t, test.expected, cs.MaxStaleness)
423 }
424 })
425 }
426 }
427
428 func TestReplicaSet(t *testing.T) {
429 tests := []struct {
430 s string
431 expected string
432 err bool
433 }{
434 {s: "replicaSet=auto", expected: "auto"},
435 {s: "replicaSet=rs0", expected: "rs0"},
436 }
437
438 for _, test := range tests {
439 s := fmt.Sprintf("mongodb://localhost/?%s", test.s)
440 t.Run(s, func(t *testing.T) {
441 cs, err := connstring.ParseAndValidate(s)
442 if test.err {
443 require.Error(t, err)
444 } else {
445 require.NoError(t, err)
446 require.Equal(t, test.expected, cs.ReplicaSet)
447 }
448 })
449 }
450 }
451
452 func TestRetryWrites(t *testing.T) {
453 tests := []struct {
454 s string
455 expected bool
456 err bool
457 }{
458 {s: "retryWrites=true", expected: true},
459 {s: "retryWrites=false", expected: false},
460 {s: "retryWrites=foobar", expected: false, err: true},
461 }
462
463 for _, test := range tests {
464 s := fmt.Sprintf("mongodb://localhost/?%s", test.s)
465 t.Run(s, func(t *testing.T) {
466 cs, err := connstring.ParseAndValidate(s)
467 if test.err {
468 require.Error(t, err)
469 return
470 }
471 require.NoError(t, err)
472 require.Equal(t, test.expected, cs.RetryWrites)
473 require.True(t, cs.RetryWritesSet)
474 })
475 }
476 }
477
478 func TestRetryReads(t *testing.T) {
479 tests := []struct {
480 s string
481 expected bool
482 err bool
483 }{
484 {s: "retryReads=true", expected: true},
485 {s: "retryReads=false", expected: false},
486 {s: "retryReads=foobar", expected: false, err: true},
487 }
488
489 for _, test := range tests {
490 s := fmt.Sprintf("mongodb://localhost/?%s", test.s)
491 t.Run(s, func(t *testing.T) {
492 cs, err := connstring.ParseAndValidate(s)
493 if test.err {
494 require.Error(t, err)
495 return
496 }
497 require.NoError(t, err)
498 require.Equal(t, test.expected, cs.RetryReads)
499 require.True(t, cs.RetryReadsSet)
500 })
501 }
502 }
503
504 func TestScheme(t *testing.T) {
505
506
507 cs, err := connstring.ParseAndValidate("mongodb://localhost/")
508 require.NoError(t, err)
509 require.Equal(t, cs.Scheme, "mongodb")
510 require.Equal(t, cs.Scheme, connstring.SchemeMongoDB)
511 }
512
513 func TestServerSelectionTimeout(t *testing.T) {
514 tests := []struct {
515 s string
516 expected time.Duration
517 err bool
518 }{
519 {s: "serverSelectionTimeoutMS=10", expected: time.Duration(10) * time.Millisecond},
520 {s: "serverSelectionTimeoutMS=100", expected: time.Duration(100) * time.Millisecond},
521 {s: "serverSelectionTimeoutMS=-2", err: true},
522 {s: "serverSelectionTimeoutMS=gsdge", err: true},
523 }
524
525 for _, test := range tests {
526 s := fmt.Sprintf("mongodb://localhost/?%s", test.s)
527 t.Run(s, func(t *testing.T) {
528 cs, err := connstring.ParseAndValidate(s)
529 if test.err {
530 require.Error(t, err)
531 } else {
532 require.NoError(t, err)
533 require.Equal(t, test.expected, cs.ServerSelectionTimeout)
534 require.True(t, cs.ServerSelectionTimeoutSet)
535 }
536 })
537 }
538 }
539
540 func TestSocketTimeout(t *testing.T) {
541 tests := []struct {
542 s string
543 expected time.Duration
544 err bool
545 }{
546 {s: "socketTimeoutMS=10", expected: time.Duration(10) * time.Millisecond},
547 {s: "socketTimeoutMS=100", expected: time.Duration(100) * time.Millisecond},
548 {s: "socketTimeoutMS=-2", err: true},
549 {s: "socketTimeoutMS=gsdge", err: true},
550 }
551
552 for _, test := range tests {
553 s := fmt.Sprintf("mongodb://localhost/?%s", test.s)
554 t.Run(s, func(t *testing.T) {
555 cs, err := connstring.ParseAndValidate(s)
556 if test.err {
557 require.Error(t, err)
558 } else {
559 require.NoError(t, err)
560 require.Equal(t, test.expected, cs.SocketTimeout)
561 require.True(t, cs.SocketTimeoutSet)
562 }
563 })
564 }
565 }
566
567 func TestWTimeout(t *testing.T) {
568 tests := []struct {
569 s string
570 expected time.Duration
571 err bool
572 }{
573 {s: "wtimeoutMS=10", expected: time.Duration(10) * time.Millisecond},
574 {s: "wtimeoutMS=100", expected: time.Duration(100) * time.Millisecond},
575 {s: "wtimeoutMS=-2", err: true},
576 {s: "wtimeoutMS=gsdge", err: true},
577 }
578
579 for _, test := range tests {
580 s := fmt.Sprintf("mongodb://localhost/?%s", test.s)
581 t.Run(s, func(t *testing.T) {
582 cs, err := connstring.ParseAndValidate(s)
583 if test.err {
584 require.Error(t, err)
585 } else {
586 require.NoError(t, err)
587 require.Equal(t, test.expected, cs.WTimeout)
588 require.True(t, cs.WTimeoutSet)
589 }
590 })
591 }
592 }
593
594 func TestCompressionOptions(t *testing.T) {
595 tests := []struct {
596 name string
597 uriOptions string
598 compressors []string
599 zlibLevel int
600 zstdLevel int
601 err bool
602 }{
603 {name: "SingleCompressor", uriOptions: "compressors=zlib", compressors: []string{"zlib"}},
604 {name: "MultiCompressors", uriOptions: "compressors=snappy,zlib", compressors: []string{"snappy", "zlib"}},
605 {name: "ZlibWithLevel", uriOptions: "compressors=zlib&zlibCompressionLevel=7", compressors: []string{"zlib"}, zlibLevel: 7},
606 {name: "DefaultZlibLevel", uriOptions: "compressors=zlib&zlibCompressionLevel=-1", compressors: []string{"zlib"}, zlibLevel: 6},
607 {name: "InvalidZlibLevel", uriOptions: "compressors=zlib&zlibCompressionLevel=-2", compressors: []string{"zlib"}, err: true},
608 {name: "ZstdWithLevel", uriOptions: "compressors=zstd&zstdCompressionLevel=20", compressors: []string{"zstd"}, zstdLevel: 20},
609 {name: "DefaultZstdLevel", uriOptions: "compressors=zstd&zstdCompressionLevel=-1", compressors: []string{"zstd"}, zstdLevel: 6},
610 {name: "InvalidZstdLevel", uriOptions: "compressors=zstd&zstdCompressionLevel=30", compressors: []string{"zstd"}, err: true},
611 }
612
613 for _, tc := range tests {
614 uri := fmt.Sprintf("mongodb://localhost/?%s", tc.uriOptions)
615 t.Run(tc.name, func(t *testing.T) {
616 cs, err := connstring.ParseAndValidate(uri)
617 if tc.err {
618 assert.Error(t, err)
619 } else {
620 require.NoError(t, err)
621 assert.Equal(t, tc.compressors, cs.Compressors)
622 if tc.zlibLevel != 0 {
623 assert.Equal(t, tc.zlibLevel, cs.ZlibLevel)
624 }
625 if tc.zstdLevel != 0 {
626 assert.Equal(t, tc.zstdLevel, cs.ZstdLevel)
627 }
628 }
629 })
630 }
631 }
632
View as plain text