master
go 636 lines 16.3 KB
Raw
1 package config
2
3 import (
4 "bytes"
5 "encoding/json"
6 "testing"
7 "time"
8
9 "github.com/stretchr/testify/assert"
10 "github.com/stretchr/testify/require"
11 )
12
13 func TestOptionalDuration(t *testing.T) {
14 makeDurationPointer := func(d time.Duration) *time.Duration { return &d }
15
16 t.Run("marshalling and unmarshalling", func(t *testing.T) {
17 out, err := json.Marshal(OptionalDuration{value: makeDurationPointer(time.Second)})
18 if err != nil {
19 t.Fatal(err)
20 }
21 expected := "\"1s\""
22 if string(out) != expected {
23 t.Fatalf("expected %s, got %s", expected, string(out))
24 }
25 var d OptionalDuration
26
27 if err := json.Unmarshal(out, &d); err != nil {
28 t.Fatal(err)
29 }
30 if *d.value != time.Second {
31 t.Fatal("expected a second")
32 }
33 })
34
35 t.Run("default value", func(t *testing.T) {
36 for _, jsonStr := range []string{"null", "\"null\"", "\"\"", "\"default\""} {
37 var d OptionalDuration
38 if !d.IsDefault() {
39 t.Fatal("expected value to be the default initially")
40 }
41 if err := json.Unmarshal([]byte(jsonStr), &d); err != nil {
42 t.Fatalf("%s failed to unmarshall with %s", jsonStr, err)
43 }
44 if dur := d.WithDefault(time.Hour); dur != time.Hour {
45 t.Fatalf("expected default value to be used, got %s", dur)
46 }
47 if !d.IsDefault() {
48 t.Fatal("expected value to be the default")
49 }
50 }
51 })
52
53 t.Run("omitempty with default value", func(t *testing.T) {
54 type Foo struct {
55 D *OptionalDuration `json:",omitempty"`
56 }
57 // marshall to JSON without empty field
58 out, err := json.Marshal(new(Foo))
59 if err != nil {
60 t.Fatal(err)
61 }
62 if string(out) != "{}" {
63 t.Fatalf("expected omitempty to omit the duration, got %s", out)
64 }
65 // unmarshall missing value and get the default
66 var foo2 Foo
67 if err := json.Unmarshal(out, &foo2); err != nil {
68 t.Fatalf("%s failed to unmarshall with %s", string(out), err)
69 }
70 if dur := foo2.D.WithDefault(time.Hour); dur != time.Hour {
71 t.Fatalf("expected default value to be used, got %s", dur)
72 }
73 if !foo2.D.IsDefault() {
74 t.Fatal("expected value to be the default")
75 }
76 })
77
78 t.Run("roundtrip including the default values", func(t *testing.T) {
79 for jsonStr, goValue := range map[string]OptionalDuration{
80 // there are various footguns user can hit, normalize them to the canonical default
81 "null": {}, // JSON null → default value
82 "\"null\"": {}, // JSON string "null" sent/set by "ipfs config" cli → default value
83 "\"default\"": {}, // explicit "default" as string
84 "\"\"": {}, // user removed custom value, empty string should also parse as default
85 "\"1s\"": {value: makeDurationPointer(time.Second)},
86 "\"42h1m3s\"": {value: makeDurationPointer(42*time.Hour + 1*time.Minute + 3*time.Second)},
87 } {
88 var d OptionalDuration
89 err := json.Unmarshal([]byte(jsonStr), &d)
90 if err != nil {
91 t.Fatal(err)
92 }
93
94 if goValue.value == nil && d.value == nil {
95 } else if goValue.value == nil && d.value != nil {
96 t.Errorf("expected nil for %s, got %s", jsonStr, d)
97 } else if *d.value != *goValue.value {
98 t.Fatalf("expected %s for %s, got %s", goValue, jsonStr, d)
99 }
100
101 // Test Reverse
102 out, err := json.Marshal(goValue)
103 if err != nil {
104 t.Fatal(err)
105 }
106 if goValue.value == nil {
107 if !bytes.Equal(out, []byte("null")) {
108 t.Fatalf("expected JSON null for %s, got %s", jsonStr, string(out))
109 }
110 continue
111 }
112 if string(out) != jsonStr {
113 t.Fatalf("expected %s, got %s", jsonStr, string(out))
114 }
115 }
116 })
117
118 t.Run("invalid duration values", func(t *testing.T) {
119 for _, invalid := range []string{
120 "\"s\"", "\"\"", "\"-1\"", "\"1H\"", "\"day\"",
121 } {
122 var d OptionalDuration
123 err := json.Unmarshal([]byte(invalid), &d)
124 if err == nil {
125 t.Errorf("expected to fail to decode %s as an OptionalDuration, got %s instead", invalid, d)
126 }
127 }
128 })
129 }
130
131 func TestOneStrings(t *testing.T) {
132 out, err := json.Marshal(Strings{"one"})
133 if err != nil {
134 t.Fatal(err)
135 }
136 expected := "\"one\""
137 if string(out) != expected {
138 t.Fatalf("expected %s, got %s", expected, string(out))
139 }
140 }
141
142 func TestNoStrings(t *testing.T) {
143 out, err := json.Marshal(Strings{})
144 if err != nil {
145 t.Fatal(err)
146 }
147 expected := "null"
148 if string(out) != expected {
149 t.Fatalf("expected %s, got %s", expected, string(out))
150 }
151 }
152
153 func TestManyStrings(t *testing.T) {
154 out, err := json.Marshal(Strings{"one", "two"})
155 if err != nil {
156 t.Fatal(err)
157 }
158 expected := "[\"one\",\"two\"]"
159 if string(out) != expected {
160 t.Fatalf("expected %s, got %s", expected, string(out))
161 }
162 }
163
164 func TestFunkyStrings(t *testing.T) {
165 toParse := " [ \"one\", \"two\" ] "
166 var s Strings
167 if err := json.Unmarshal([]byte(toParse), &s); err != nil {
168 t.Fatal(err)
169 }
170 if len(s) != 2 || s[0] != "one" && s[1] != "two" {
171 t.Fatalf("unexpected result: %v", s)
172 }
173 }
174
175 func TestFlag(t *testing.T) {
176 // make sure we have the right zero value.
177 var defaultFlag Flag
178 if defaultFlag != Default {
179 t.Errorf("expected default flag to be %q, got %q", Default, defaultFlag)
180 }
181
182 if defaultFlag.WithDefault(true) != true {
183 t.Error("expected default & true to be true")
184 }
185
186 if defaultFlag.WithDefault(false) != false {
187 t.Error("expected default & false to be false")
188 }
189
190 if True.WithDefault(false) != true {
191 t.Error("default should only apply to default")
192 }
193
194 if False.WithDefault(true) != false {
195 t.Error("default should only apply to default")
196 }
197
198 if True.WithDefault(true) != true {
199 t.Error("true & true is true")
200 }
201
202 if False.WithDefault(true) != false {
203 t.Error("false & false is false")
204 }
205
206 for jsonStr, goValue := range map[string]Flag{
207 "null": Default,
208 "true": True,
209 "false": False,
210 } {
211 var d Flag
212 err := json.Unmarshal([]byte(jsonStr), &d)
213 if err != nil {
214 t.Fatal(err)
215 }
216 if d != goValue {
217 t.Fatalf("expected %s, got %s", goValue, d)
218 }
219
220 // Reverse
221 out, err := json.Marshal(goValue)
222 if err != nil {
223 t.Fatal(err)
224 }
225 if string(out) != jsonStr {
226 t.Fatalf("expected %s, got %s", jsonStr, string(out))
227 }
228 }
229
230 type Foo struct {
231 F Flag `json:",omitempty"`
232 }
233 out, err := json.Marshal(new(Foo))
234 if err != nil {
235 t.Fatal(err)
236 }
237 expected := "{}"
238 if string(out) != expected {
239 t.Fatal("expected omitempty to omit the flag")
240 }
241 }
242
243 func TestPriority(t *testing.T) {
244 // make sure we have the right zero value.
245 var defaultPriority Priority
246 if defaultPriority != DefaultPriority {
247 t.Errorf("expected default priority to be %q, got %q", DefaultPriority, defaultPriority)
248 }
249
250 if _, ok := defaultPriority.WithDefault(Disabled); ok {
251 t.Error("should have been disabled")
252 }
253
254 if p, ok := defaultPriority.WithDefault(1); !ok || p != 1 {
255 t.Errorf("priority should have been 1, got %d", p)
256 }
257
258 if p, ok := defaultPriority.WithDefault(DefaultPriority); !ok || p != 0 {
259 t.Errorf("priority should have been 0, got %d", p)
260 }
261
262 for jsonStr, goValue := range map[string]Priority{
263 "null": DefaultPriority,
264 "false": Disabled,
265 "1": 1,
266 "2": 2,
267 "100": 100,
268 } {
269 var d Priority
270 err := json.Unmarshal([]byte(jsonStr), &d)
271 if err != nil {
272 t.Fatal(err)
273 }
274 if d != goValue {
275 t.Fatalf("expected %s, got %s", goValue, d)
276 }
277
278 // Reverse
279 out, err := json.Marshal(goValue)
280 if err != nil {
281 t.Fatal(err)
282 }
283 if string(out) != jsonStr {
284 t.Fatalf("expected %s, got %s", jsonStr, string(out))
285 }
286 }
287
288 type Foo struct {
289 P Priority `json:",omitempty"`
290 }
291 out, err := json.Marshal(new(Foo))
292 if err != nil {
293 t.Fatal(err)
294 }
295 expected := "{}"
296 if string(out) != expected {
297 t.Fatal("expected omitempty to omit the flag")
298 }
299 for _, invalid := range []string{
300 "0", "-1", "-2", "1.1", "0.0",
301 } {
302 var p Priority
303 err := json.Unmarshal([]byte(invalid), &p)
304 if err == nil {
305 t.Errorf("expected to fail to decode %s as a priority", invalid)
306 }
307 }
308 }
309
310 func TestOptionalInteger(t *testing.T) {
311 makeInt64Pointer := func(v int64) *int64 {
312 return &v
313 }
314
315 var defaultOptionalInt OptionalInteger
316 if !defaultOptionalInt.IsDefault() {
317 t.Fatal("should be the default")
318 }
319 if val := defaultOptionalInt.WithDefault(0); val != 0 {
320 t.Errorf("optional integer should have been 0, got %d", val)
321 }
322
323 if val := defaultOptionalInt.WithDefault(1); val != 1 {
324 t.Errorf("optional integer should have been 1, got %d", val)
325 }
326
327 if val := defaultOptionalInt.WithDefault(-1); val != -1 {
328 t.Errorf("optional integer should have been -1, got %d", val)
329 }
330
331 var filledInt OptionalInteger
332 filledInt = OptionalInteger{value: makeInt64Pointer(1)}
333 if filledInt.IsDefault() {
334 t.Fatal("should not be the default")
335 }
336 if val := filledInt.WithDefault(0); val != 1 {
337 t.Errorf("optional integer should have been 1, got %d", val)
338 }
339
340 if val := filledInt.WithDefault(-1); val != 1 {
341 t.Errorf("optional integer should have been 1, got %d", val)
342 }
343
344 filledInt = OptionalInteger{value: makeInt64Pointer(0)}
345 if val := filledInt.WithDefault(1); val != 0 {
346 t.Errorf("optional integer should have been 0, got %d", val)
347 }
348
349 for jsonStr, goValue := range map[string]OptionalInteger{
350 "null": {},
351 "0": {value: makeInt64Pointer(0)},
352 "1": {value: makeInt64Pointer(1)},
353 "-1": {value: makeInt64Pointer(-1)},
354 } {
355 var d OptionalInteger
356 err := json.Unmarshal([]byte(jsonStr), &d)
357 if err != nil {
358 t.Fatal(err)
359 }
360
361 if goValue.value == nil && d.value == nil {
362 } else if goValue.value == nil && d.value != nil {
363 t.Errorf("expected default, got %s", d)
364 } else if *d.value != *goValue.value {
365 t.Fatalf("expected %s, got %s", goValue, d)
366 }
367
368 // Reverse
369 out, err := json.Marshal(goValue)
370 if err != nil {
371 t.Fatal(err)
372 }
373 if string(out) != jsonStr {
374 t.Fatalf("expected %s, got %s", jsonStr, string(out))
375 }
376 }
377
378 // marshal with omitempty
379 type Foo struct {
380 I *OptionalInteger `json:",omitempty"`
381 }
382 out, err := json.Marshal(new(Foo))
383 if err != nil {
384 t.Fatal(err)
385 }
386 expected := "{}"
387 if string(out) != expected {
388 t.Fatal("expected omitempty to omit the optional integer")
389 }
390
391 // unmarshal from omitempty output and get default value
392 var foo2 Foo
393 if err := json.Unmarshal(out, &foo2); err != nil {
394 t.Fatalf("%s failed to unmarshall with %s", string(out), err)
395 }
396 if i := foo2.I.WithDefault(42); i != 42 {
397 t.Fatalf("expected default value to be used, got %d", i)
398 }
399 if !foo2.I.IsDefault() {
400 t.Fatal("expected value to be the default")
401 }
402
403 // test invalid values
404 for _, invalid := range []string{
405 "foo", "-1.1", "1.1", "0.0", "[]",
406 } {
407 var p OptionalInteger
408 err := json.Unmarshal([]byte(invalid), &p)
409 if err == nil {
410 t.Errorf("expected to fail to decode %s as a priority", invalid)
411 }
412 }
413 }
414
415 func TestOptionalString(t *testing.T) {
416 makeStringPointer := func(v string) *string {
417 return &v
418 }
419
420 var defaultOptionalString OptionalString
421 if !defaultOptionalString.IsDefault() {
422 t.Fatal("should be the default")
423 }
424 if val := defaultOptionalString.WithDefault(""); val != "" {
425 t.Errorf("optional string should have been empty, got %s", val)
426 }
427 if val := defaultOptionalString.String(); val != "default" {
428 t.Fatalf("default optional string should be the 'default' string, got %s", val)
429 }
430 if val := defaultOptionalString.WithDefault("foo"); val != "foo" {
431 t.Errorf("optional string should have been foo, got %s", val)
432 }
433
434 var filledStr OptionalString
435 filledStr = OptionalString{value: makeStringPointer("foo")}
436 if filledStr.IsDefault() {
437 t.Fatal("should not be the default")
438 }
439 if val := filledStr.WithDefault("bar"); val != "foo" {
440 t.Errorf("optional string should have been foo, got %s", val)
441 }
442 if val := filledStr.String(); val != "foo" {
443 t.Fatalf("optional string should have been foo, got %s", val)
444 }
445 filledStr = OptionalString{value: makeStringPointer("")}
446 if val := filledStr.WithDefault("foo"); val != "" {
447 t.Errorf("optional string should have been 0, got %s", val)
448 }
449
450 for jsonStr, goValue := range map[string]OptionalString{
451 "null": {},
452 "\"0\"": {value: makeStringPointer("0")},
453 "\"\"": {value: makeStringPointer("")},
454 `"1"`: {value: makeStringPointer("1")},
455 `"-1"`: {value: makeStringPointer("-1")},
456 `"qwerty"`: {value: makeStringPointer("qwerty")},
457 } {
458 var d OptionalString
459 err := json.Unmarshal([]byte(jsonStr), &d)
460 if err != nil {
461 t.Fatal(err)
462 }
463
464 if goValue.value == nil && d.value == nil {
465 } else if goValue.value == nil && d.value != nil {
466 t.Errorf("expected default, got %s", d)
467 } else if *d.value != *goValue.value {
468 t.Fatalf("expected %s, got %s", goValue, d)
469 }
470
471 // Reverse
472 out, err := json.Marshal(goValue)
473 if err != nil {
474 t.Fatal(err)
475 }
476 if string(out) != jsonStr {
477 t.Fatalf("expected %s, got %s", jsonStr, string(out))
478 }
479 }
480
481 // marshal with omitempty
482 type Foo struct {
483 S *OptionalString `json:",omitempty"`
484 }
485 out, err := json.Marshal(new(Foo))
486 if err != nil {
487 t.Fatal(err)
488 }
489 expected := "{}"
490 if string(out) != expected {
491 t.Fatal("expected omitempty to omit the optional integer")
492 }
493 // unmarshal from omitempty output and get default value
494 var foo2 Foo
495 if err := json.Unmarshal(out, &foo2); err != nil {
496 t.Fatalf("%s failed to unmarshall with %s", string(out), err)
497 }
498 if s := foo2.S.WithDefault("foo"); s != "foo" {
499 t.Fatalf("expected default value to be used, got %s", s)
500 }
501 if !foo2.S.IsDefault() {
502 t.Fatal("expected value to be the default")
503 }
504
505 for _, invalid := range []string{
506 "[]", "{}", "0", "a", "'b'",
507 } {
508 var p OptionalString
509 err := json.Unmarshal([]byte(invalid), &p)
510 if err == nil {
511 t.Errorf("expected to fail to decode %s as an optional string", invalid)
512 }
513 }
514 }
515
516 func TestOptionalBytes(t *testing.T) {
517 makeStringPointer := func(v string) *string { return &v }
518
519 t.Run("default value", func(t *testing.T) {
520 var b OptionalBytes
521 assert.True(t, b.IsDefault())
522 assert.Equal(t, uint64(0), b.WithDefault(0))
523 assert.Equal(t, uint64(1024), b.WithDefault(1024))
524 assert.Equal(t, "default", b.String())
525 })
526
527 t.Run("non-default value", func(t *testing.T) {
528 b := OptionalBytes{OptionalString{value: makeStringPointer("1MiB")}}
529 assert.False(t, b.IsDefault())
530 assert.Equal(t, uint64(1048576), b.WithDefault(512))
531 assert.Equal(t, "1MiB", b.String())
532 })
533
534 t.Run("JSON roundtrip", func(t *testing.T) {
535 testCases := []struct {
536 jsonInput string
537 jsonOutput string
538 expectedValue string
539 }{
540 {"null", "null", ""},
541 {"\"256KiB\"", "\"256KiB\"", "256KiB"},
542 {"\"1MiB\"", "\"1MiB\"", "1MiB"},
543 {"\"5GiB\"", "\"5GiB\"", "5GiB"},
544 {"\"256KB\"", "\"256KB\"", "256KB"},
545 {"1048576", "\"1048576\"", "1048576"},
546 }
547
548 for _, tc := range testCases {
549 t.Run(tc.jsonInput, func(t *testing.T) {
550 var b OptionalBytes
551 err := json.Unmarshal([]byte(tc.jsonInput), &b)
552 require.NoError(t, err)
553
554 if tc.expectedValue == "" {
555 assert.Nil(t, b.value)
556 } else {
557 require.NotNil(t, b.value)
558 assert.Equal(t, tc.expectedValue, *b.value)
559 }
560
561 out, err := json.Marshal(b)
562 require.NoError(t, err)
563 assert.Equal(t, tc.jsonOutput, string(out))
564 })
565 }
566 })
567
568 t.Run("parsing byte sizes", func(t *testing.T) {
569 testCases := []struct {
570 input string
571 expected uint64
572 }{
573 {"256KiB", 262144},
574 {"1MiB", 1048576},
575 {"5GiB", 5368709120},
576 {"256KB", 256000},
577 {"1048576", 1048576},
578 }
579
580 for _, tc := range testCases {
581 t.Run(tc.input, func(t *testing.T) {
582 var b OptionalBytes
583 err := json.Unmarshal([]byte("\""+tc.input+"\""), &b)
584 require.NoError(t, err)
585 assert.Equal(t, tc.expected, b.WithDefault(0))
586 })
587 }
588 })
589
590 t.Run("omitempty", func(t *testing.T) {
591 type Foo struct {
592 B *OptionalBytes `json:",omitempty"`
593 }
594
595 out, err := json.Marshal(new(Foo))
596 require.NoError(t, err)
597 assert.Equal(t, "{}", string(out))
598
599 var foo2 Foo
600 err = json.Unmarshal(out, &foo2)
601 require.NoError(t, err)
602
603 if foo2.B != nil {
604 assert.Equal(t, uint64(1024), foo2.B.WithDefault(1024))
605 assert.True(t, foo2.B.IsDefault())
606 } else {
607 // When field is omitted, pointer is nil which is also considered default
608 t.Log("B is nil, which is acceptable for omitempty")
609 }
610 })
611
612 t.Run("invalid values", func(t *testing.T) {
613 invalidInputs := []string{
614 "\"5XiB\"", "\"invalid\"", "\"\"", "[]", "{}",
615 }
616
617 for _, invalid := range invalidInputs {
618 t.Run(invalid, func(t *testing.T) {
619 var b OptionalBytes
620 err := json.Unmarshal([]byte(invalid), &b)
621 assert.Error(t, err)
622 })
623 }
624 })
625
626 t.Run("panic on invalid stored value", func(t *testing.T) {
627 // This tests that if somehow an invalid value gets stored
628 // (bypassing UnmarshalJSON validation), WithDefault will panic
629 invalidValue := "invalid-size"
630 b := OptionalBytes{OptionalString{value: &invalidValue}}
631
632 assert.Panics(t, func() {
633 b.WithDefault(1024)
634 }, "should panic on invalid stored value")
635 })
636 }