1
2
3
4
5 package envconfig
6
7 import (
8 "flag"
9 "fmt"
10 "net/url"
11 "os"
12 "strings"
13 "testing"
14 "time"
15 )
16
17 type HonorDecodeInStruct struct {
18 Value string
19 }
20
21 func (h *HonorDecodeInStruct) Decode(env string) error {
22 h.Value = "decoded"
23 return nil
24 }
25
26 type CustomURL struct {
27 Value *url.URL
28 }
29
30 func (cu *CustomURL) UnmarshalBinary(data []byte) error {
31 u, err := url.Parse(string(data))
32 cu.Value = u
33 return err
34 }
35
36 type Specification struct {
37 Embedded `desc:"can we document a struct"`
38 EmbeddedButIgnored `ignored:"true"`
39 Debug bool
40 Port int
41 Rate float32
42 User string
43 TTL uint32
44 Timeout time.Duration
45 AdminUsers []string
46 MagicNumbers []int
47 EmptyNumbers []int
48 ByteSlice []byte
49 ColorCodes map[string]int
50 MultiWordVar string
51 MultiWordVarWithAutoSplit uint32 `split_words:"true"`
52 MultiWordACRWithAutoSplit uint32 `split_words:"true"`
53 SomePointer *string
54 SomePointerWithDefault *string `default:"foo2baz" desc:"foorbar is the word"`
55 MultiWordVarWithAlt string `envconfig:"MULTI_WORD_VAR_WITH_ALT" desc:"what alt"`
56 MultiWordVarWithLowerCaseAlt string `envconfig:"multi_word_var_with_lower_case_alt"`
57 NoPrefixWithAlt string `envconfig:"SERVICE_HOST"`
58 DefaultVar string `default:"foobar"`
59 RequiredVar string `required:"True"`
60 NoPrefixDefault string `envconfig:"BROKER" default:"127.0.0.1"`
61 RequiredDefault string `required:"true" default:"foo2bar"`
62 Ignored string `ignored:"true"`
63 NestedSpecification struct {
64 Property string `envconfig:"inner"`
65 PropertyWithDefault string `default:"fuzzybydefault"`
66 } `envconfig:"outer"`
67 AfterNested string
68 DecodeStruct HonorDecodeInStruct `envconfig:"honor"`
69 Datetime time.Time
70 MapField map[string]string `default:"one:two,three:four"`
71 UrlValue CustomURL
72 UrlPointer *CustomURL
73 }
74
75 type Embedded struct {
76 Enabled bool `desc:"some embedded value"`
77 EmbeddedPort int
78 MultiWordVar string
79 MultiWordVarWithAlt string `envconfig:"MULTI_WITH_DIFFERENT_ALT"`
80 EmbeddedAlt string `envconfig:"EMBEDDED_WITH_ALT"`
81 EmbeddedIgnored string `ignored:"true"`
82 }
83
84 type EmbeddedButIgnored struct {
85 FirstEmbeddedButIgnored string
86 SecondEmbeddedButIgnored string
87 }
88
89 func TestProcess(t *testing.T) {
90 var s Specification
91 os.Clearenv()
92 os.Setenv("ENV_CONFIG_DEBUG", "true")
93 os.Setenv("ENV_CONFIG_PORT", "8080")
94 os.Setenv("ENV_CONFIG_RATE", "0.5")
95 os.Setenv("ENV_CONFIG_USER", "Kelsey")
96 os.Setenv("ENV_CONFIG_TIMEOUT", "2m")
97 os.Setenv("ENV_CONFIG_ADMINUSERS", "John,Adam,Will")
98 os.Setenv("ENV_CONFIG_MAGICNUMBERS", "5,10,20")
99 os.Setenv("ENV_CONFIG_EMPTYNUMBERS", "")
100 os.Setenv("ENV_CONFIG_BYTESLICE", "this is a test value")
101 os.Setenv("ENV_CONFIG_COLORCODES", "red:1,green:2,blue:3")
102 os.Setenv("SERVICE_HOST", "127.0.0.1")
103 os.Setenv("ENV_CONFIG_TTL", "30")
104 os.Setenv("ENV_CONFIG_REQUIREDVAR", "foo")
105 os.Setenv("ENV_CONFIG_IGNORED", "was-not-ignored")
106 os.Setenv("ENV_CONFIG_OUTER_INNER", "iamnested")
107 os.Setenv("ENV_CONFIG_AFTERNESTED", "after")
108 os.Setenv("ENV_CONFIG_HONOR", "honor")
109 os.Setenv("ENV_CONFIG_DATETIME", "2016-08-16T18:57:05Z")
110 os.Setenv("ENV_CONFIG_MULTI_WORD_VAR_WITH_AUTO_SPLIT", "24")
111 os.Setenv("ENV_CONFIG_MULTI_WORD_ACR_WITH_AUTO_SPLIT", "25")
112 os.Setenv("ENV_CONFIG_URLVALUE", "https://github.com/kelseyhightower/envconfig")
113 os.Setenv("ENV_CONFIG_URLPOINTER", "https://github.com/kelseyhightower/envconfig")
114 err := Process("env_config", &s)
115 if err != nil {
116 t.Error(err.Error())
117 }
118 if s.NoPrefixWithAlt != "127.0.0.1" {
119 t.Errorf("expected %v, got %v", "127.0.0.1", s.NoPrefixWithAlt)
120 }
121 if !s.Debug {
122 t.Errorf("expected %v, got %v", true, s.Debug)
123 }
124 if s.Port != 8080 {
125 t.Errorf("expected %d, got %v", 8080, s.Port)
126 }
127 if s.Rate != 0.5 {
128 t.Errorf("expected %f, got %v", 0.5, s.Rate)
129 }
130 if s.TTL != 30 {
131 t.Errorf("expected %d, got %v", 30, s.TTL)
132 }
133 if s.User != "Kelsey" {
134 t.Errorf("expected %s, got %s", "Kelsey", s.User)
135 }
136 if s.Timeout != 2*time.Minute {
137 t.Errorf("expected %s, got %s", 2*time.Minute, s.Timeout)
138 }
139 if s.RequiredVar != "foo" {
140 t.Errorf("expected %s, got %s", "foo", s.RequiredVar)
141 }
142 if len(s.AdminUsers) != 3 ||
143 s.AdminUsers[0] != "John" ||
144 s.AdminUsers[1] != "Adam" ||
145 s.AdminUsers[2] != "Will" {
146 t.Errorf("expected %#v, got %#v", []string{"John", "Adam", "Will"}, s.AdminUsers)
147 }
148 if len(s.MagicNumbers) != 3 ||
149 s.MagicNumbers[0] != 5 ||
150 s.MagicNumbers[1] != 10 ||
151 s.MagicNumbers[2] != 20 {
152 t.Errorf("expected %#v, got %#v", []int{5, 10, 20}, s.MagicNumbers)
153 }
154 if len(s.EmptyNumbers) != 0 {
155 t.Errorf("expected %#v, got %#v", []int{}, s.EmptyNumbers)
156 }
157 expected := "this is a test value"
158 if string(s.ByteSlice) != expected {
159 t.Errorf("expected %v, got %v", expected, string(s.ByteSlice))
160 }
161 if s.Ignored != "" {
162 t.Errorf("expected empty string, got %#v", s.Ignored)
163 }
164
165 if len(s.ColorCodes) != 3 ||
166 s.ColorCodes["red"] != 1 ||
167 s.ColorCodes["green"] != 2 ||
168 s.ColorCodes["blue"] != 3 {
169 t.Errorf(
170 "expected %#v, got %#v",
171 map[string]int{
172 "red": 1,
173 "green": 2,
174 "blue": 3,
175 },
176 s.ColorCodes,
177 )
178 }
179
180 if s.NestedSpecification.Property != "iamnested" {
181 t.Errorf("expected '%s' string, got %#v", "iamnested", s.NestedSpecification.Property)
182 }
183
184 if s.NestedSpecification.PropertyWithDefault != "fuzzybydefault" {
185 t.Errorf("expected default '%s' string, got %#v", "fuzzybydefault", s.NestedSpecification.PropertyWithDefault)
186 }
187
188 if s.AfterNested != "after" {
189 t.Errorf("expected default '%s' string, got %#v", "after", s.AfterNested)
190 }
191
192 if s.DecodeStruct.Value != "decoded" {
193 t.Errorf("expected default '%s' string, got %#v", "decoded", s.DecodeStruct.Value)
194 }
195
196 if expected := time.Date(2016, 8, 16, 18, 57, 05, 0, time.UTC); !s.Datetime.Equal(expected) {
197 t.Errorf("expected %s, got %s", expected.Format(time.RFC3339), s.Datetime.Format(time.RFC3339))
198 }
199
200 if s.MultiWordVarWithAutoSplit != 24 {
201 t.Errorf("expected %q, got %q", 24, s.MultiWordVarWithAutoSplit)
202 }
203
204 if s.MultiWordACRWithAutoSplit != 25 {
205 t.Errorf("expected %d, got %d", 25, s.MultiWordACRWithAutoSplit)
206 }
207
208 u, err := url.Parse("https://github.com/kelseyhightower/envconfig")
209 if err != nil {
210 t.Fatalf("unexpected error: %v", err)
211 }
212
213 if *s.UrlValue.Value != *u {
214 t.Errorf("expected %q, got %q", u, s.UrlValue.Value.String())
215 }
216
217 if *s.UrlPointer.Value != *u {
218 t.Errorf("expected %q, got %q", u, s.UrlPointer.Value.String())
219 }
220 }
221
222 func TestParseErrorBool(t *testing.T) {
223 var s Specification
224 os.Clearenv()
225 os.Setenv("ENV_CONFIG_DEBUG", "string")
226 os.Setenv("ENV_CONFIG_REQUIREDVAR", "foo")
227 err := Process("env_config", &s)
228 v, ok := err.(*ParseError)
229 if !ok {
230 t.Errorf("expected ParseError, got %v", v)
231 }
232 if v.FieldName != "Debug" {
233 t.Errorf("expected %s, got %v", "Debug", v.FieldName)
234 }
235 if s.Debug != false {
236 t.Errorf("expected %v, got %v", false, s.Debug)
237 }
238 }
239
240 func TestParseErrorFloat32(t *testing.T) {
241 var s Specification
242 os.Clearenv()
243 os.Setenv("ENV_CONFIG_RATE", "string")
244 os.Setenv("ENV_CONFIG_REQUIREDVAR", "foo")
245 err := Process("env_config", &s)
246 v, ok := err.(*ParseError)
247 if !ok {
248 t.Errorf("expected ParseError, got %v", v)
249 }
250 if v.FieldName != "Rate" {
251 t.Errorf("expected %s, got %v", "Rate", v.FieldName)
252 }
253 if s.Rate != 0 {
254 t.Errorf("expected %v, got %v", 0, s.Rate)
255 }
256 }
257
258 func TestParseErrorInt(t *testing.T) {
259 var s Specification
260 os.Clearenv()
261 os.Setenv("ENV_CONFIG_PORT", "string")
262 os.Setenv("ENV_CONFIG_REQUIREDVAR", "foo")
263 err := Process("env_config", &s)
264 v, ok := err.(*ParseError)
265 if !ok {
266 t.Errorf("expected ParseError, got %v", v)
267 }
268 if v.FieldName != "Port" {
269 t.Errorf("expected %s, got %v", "Port", v.FieldName)
270 }
271 if s.Port != 0 {
272 t.Errorf("expected %v, got %v", 0, s.Port)
273 }
274 }
275
276 func TestParseErrorUint(t *testing.T) {
277 var s Specification
278 os.Clearenv()
279 os.Setenv("ENV_CONFIG_TTL", "-30")
280 err := Process("env_config", &s)
281 v, ok := err.(*ParseError)
282 if !ok {
283 t.Errorf("expected ParseError, got %v", v)
284 }
285 if v.FieldName != "TTL" {
286 t.Errorf("expected %s, got %v", "TTL", v.FieldName)
287 }
288 if s.TTL != 0 {
289 t.Errorf("expected %v, got %v", 0, s.TTL)
290 }
291 }
292
293 func TestParseErrorSplitWords(t *testing.T) {
294 var s Specification
295 os.Clearenv()
296 os.Setenv("ENV_CONFIG_MULTI_WORD_VAR_WITH_AUTO_SPLIT", "shakespeare")
297 err := Process("env_config", &s)
298 v, ok := err.(*ParseError)
299 if !ok {
300 t.Errorf("expected ParseError, got %v", v)
301 }
302 if v.FieldName != "MultiWordVarWithAutoSplit" {
303 t.Errorf("expected %s, got %v", "", v.FieldName)
304 }
305 if s.MultiWordVarWithAutoSplit != 0 {
306 t.Errorf("expected %v, got %v", 0, s.MultiWordVarWithAutoSplit)
307 }
308 }
309
310 func TestErrInvalidSpecification(t *testing.T) {
311 m := make(map[string]string)
312 err := Process("env_config", &m)
313 if err != ErrInvalidSpecification {
314 t.Errorf("expected %v, got %v", ErrInvalidSpecification, err)
315 }
316 }
317
318 func TestUnsetVars(t *testing.T) {
319 var s Specification
320 os.Clearenv()
321 os.Setenv("USER", "foo")
322 os.Setenv("ENV_CONFIG_REQUIREDVAR", "foo")
323 if err := Process("env_config", &s); err != nil {
324 t.Error(err.Error())
325 }
326
327
328
329 if s.User != "" {
330 t.Errorf("expected %q, got %q", "", s.User)
331 }
332 }
333
334 func TestAlternateVarNames(t *testing.T) {
335 var s Specification
336 os.Clearenv()
337 os.Setenv("ENV_CONFIG_MULTI_WORD_VAR", "foo")
338 os.Setenv("ENV_CONFIG_MULTI_WORD_VAR_WITH_ALT", "bar")
339 os.Setenv("ENV_CONFIG_MULTI_WORD_VAR_WITH_LOWER_CASE_ALT", "baz")
340 os.Setenv("ENV_CONFIG_REQUIREDVAR", "foo")
341 if err := Process("env_config", &s); err != nil {
342 t.Error(err.Error())
343 }
344
345
346
347 if s.MultiWordVar != "" {
348 t.Errorf("expected %q, got %q", "", s.MultiWordVar)
349 }
350
351
352
353 if s.MultiWordVarWithAlt != "bar" {
354 t.Errorf("expected %q, got %q", "bar", s.MultiWordVarWithAlt)
355 }
356
357
358 if s.MultiWordVarWithLowerCaseAlt != "baz" {
359 t.Errorf("expected %q, got %q", "baz", s.MultiWordVarWithLowerCaseAlt)
360 }
361 }
362
363 func TestRequiredVar(t *testing.T) {
364 var s Specification
365 os.Clearenv()
366 os.Setenv("ENV_CONFIG_REQUIREDVAR", "foobar")
367 if err := Process("env_config", &s); err != nil {
368 t.Error(err.Error())
369 }
370
371 if s.RequiredVar != "foobar" {
372 t.Errorf("expected %s, got %s", "foobar", s.RequiredVar)
373 }
374 }
375
376 func TestRequiredMissing(t *testing.T) {
377 var s Specification
378 os.Clearenv()
379
380 err := Process("env_config", &s)
381 if err == nil {
382 t.Error("no failure when missing required variable")
383 }
384 }
385
386 func TestBlankDefaultVar(t *testing.T) {
387 var s Specification
388 os.Clearenv()
389 os.Setenv("ENV_CONFIG_REQUIREDVAR", "requiredvalue")
390 if err := Process("env_config", &s); err != nil {
391 t.Error(err.Error())
392 }
393
394 if s.DefaultVar != "foobar" {
395 t.Errorf("expected %s, got %s", "foobar", s.DefaultVar)
396 }
397
398 if *s.SomePointerWithDefault != "foo2baz" {
399 t.Errorf("expected %s, got %s", "foo2baz", *s.SomePointerWithDefault)
400 }
401 }
402
403 func TestNonBlankDefaultVar(t *testing.T) {
404 var s Specification
405 os.Clearenv()
406 os.Setenv("ENV_CONFIG_DEFAULTVAR", "nondefaultval")
407 os.Setenv("ENV_CONFIG_REQUIREDVAR", "requiredvalue")
408 if err := Process("env_config", &s); err != nil {
409 t.Error(err.Error())
410 }
411
412 if s.DefaultVar != "nondefaultval" {
413 t.Errorf("expected %s, got %s", "nondefaultval", s.DefaultVar)
414 }
415 }
416
417 func TestExplicitBlankDefaultVar(t *testing.T) {
418 var s Specification
419 os.Clearenv()
420 os.Setenv("ENV_CONFIG_DEFAULTVAR", "")
421 os.Setenv("ENV_CONFIG_REQUIREDVAR", "")
422
423 if err := Process("env_config", &s); err != nil {
424 t.Error(err.Error())
425 }
426
427 if s.DefaultVar != "" {
428 t.Errorf("expected %s, got %s", "\"\"", s.DefaultVar)
429 }
430 }
431
432 func TestAlternateNameDefaultVar(t *testing.T) {
433 var s Specification
434 os.Clearenv()
435 os.Setenv("BROKER", "betterbroker")
436 os.Setenv("ENV_CONFIG_REQUIREDVAR", "foo")
437 if err := Process("env_config", &s); err != nil {
438 t.Error(err.Error())
439 }
440
441 if s.NoPrefixDefault != "betterbroker" {
442 t.Errorf("expected %q, got %q", "betterbroker", s.NoPrefixDefault)
443 }
444
445 os.Clearenv()
446 os.Setenv("ENV_CONFIG_REQUIREDVAR", "foo")
447 if err := Process("env_config", &s); err != nil {
448 t.Error(err.Error())
449 }
450
451 if s.NoPrefixDefault != "127.0.0.1" {
452 t.Errorf("expected %q, got %q", "127.0.0.1", s.NoPrefixDefault)
453 }
454 }
455
456 func TestRequiredDefault(t *testing.T) {
457 var s Specification
458 os.Clearenv()
459 os.Setenv("ENV_CONFIG_REQUIREDVAR", "foo")
460 if err := Process("env_config", &s); err != nil {
461 t.Error(err.Error())
462 }
463
464 if s.RequiredDefault != "foo2bar" {
465 t.Errorf("expected %q, got %q", "foo2bar", s.RequiredDefault)
466 }
467 }
468
469 func TestPointerFieldBlank(t *testing.T) {
470 var s Specification
471 os.Clearenv()
472 os.Setenv("ENV_CONFIG_REQUIREDVAR", "foo")
473 if err := Process("env_config", &s); err != nil {
474 t.Error(err.Error())
475 }
476
477 if s.SomePointer != nil {
478 t.Errorf("expected <nil>, got %q", *s.SomePointer)
479 }
480 }
481
482 func TestEmptyMapFieldOverride(t *testing.T) {
483 var s Specification
484 os.Clearenv()
485 os.Setenv("ENV_CONFIG_REQUIREDVAR", "foo")
486 os.Setenv("ENV_CONFIG_MAPFIELD", "")
487 if err := Process("env_config", &s); err != nil {
488 t.Error(err.Error())
489 }
490
491 if s.MapField == nil {
492 t.Error("expected empty map, got <nil>")
493 }
494
495 if len(s.MapField) != 0 {
496 t.Errorf("expected empty map, got map of size %d", len(s.MapField))
497 }
498 }
499
500 func TestMustProcess(t *testing.T) {
501 var s Specification
502 os.Clearenv()
503 os.Setenv("ENV_CONFIG_DEBUG", "true")
504 os.Setenv("ENV_CONFIG_PORT", "8080")
505 os.Setenv("ENV_CONFIG_RATE", "0.5")
506 os.Setenv("ENV_CONFIG_USER", "Kelsey")
507 os.Setenv("SERVICE_HOST", "127.0.0.1")
508 os.Setenv("ENV_CONFIG_REQUIREDVAR", "foo")
509 MustProcess("env_config", &s)
510
511 defer func() {
512 if err := recover(); err != nil {
513 return
514 }
515
516 t.Error("expected panic")
517 }()
518 m := make(map[string]string)
519 MustProcess("env_config", &m)
520 }
521
522 func TestEmbeddedStruct(t *testing.T) {
523 var s Specification
524 os.Clearenv()
525 os.Setenv("ENV_CONFIG_REQUIREDVAR", "required")
526 os.Setenv("ENV_CONFIG_ENABLED", "true")
527 os.Setenv("ENV_CONFIG_EMBEDDEDPORT", "1234")
528 os.Setenv("ENV_CONFIG_MULTIWORDVAR", "foo")
529 os.Setenv("ENV_CONFIG_MULTI_WORD_VAR_WITH_ALT", "bar")
530 os.Setenv("ENV_CONFIG_MULTI_WITH_DIFFERENT_ALT", "baz")
531 os.Setenv("ENV_CONFIG_EMBEDDED_WITH_ALT", "foobar")
532 os.Setenv("ENV_CONFIG_SOMEPOINTER", "foobaz")
533 os.Setenv("ENV_CONFIG_EMBEDDED_IGNORED", "was-not-ignored")
534 if err := Process("env_config", &s); err != nil {
535 t.Error(err.Error())
536 }
537 if !s.Enabled {
538 t.Errorf("expected %v, got %v", true, s.Enabled)
539 }
540 if s.EmbeddedPort != 1234 {
541 t.Errorf("expected %d, got %v", 1234, s.EmbeddedPort)
542 }
543 if s.MultiWordVar != "foo" {
544 t.Errorf("expected %s, got %s", "foo", s.MultiWordVar)
545 }
546 if s.Embedded.MultiWordVar != "foo" {
547 t.Errorf("expected %s, got %s", "foo", s.Embedded.MultiWordVar)
548 }
549 if s.MultiWordVarWithAlt != "bar" {
550 t.Errorf("expected %s, got %s", "bar", s.MultiWordVarWithAlt)
551 }
552 if s.Embedded.MultiWordVarWithAlt != "baz" {
553 t.Errorf("expected %s, got %s", "baz", s.Embedded.MultiWordVarWithAlt)
554 }
555 if s.EmbeddedAlt != "foobar" {
556 t.Errorf("expected %s, got %s", "foobar", s.EmbeddedAlt)
557 }
558 if *s.SomePointer != "foobaz" {
559 t.Errorf("expected %s, got %s", "foobaz", *s.SomePointer)
560 }
561 if s.EmbeddedIgnored != "" {
562 t.Errorf("expected empty string, got %#v", s.Ignored)
563 }
564 }
565
566 func TestEmbeddedButIgnoredStruct(t *testing.T) {
567 var s Specification
568 os.Clearenv()
569 os.Setenv("ENV_CONFIG_REQUIREDVAR", "required")
570 os.Setenv("ENV_CONFIG_FIRSTEMBEDDEDBUTIGNORED", "was-not-ignored")
571 os.Setenv("ENV_CONFIG_SECONDEMBEDDEDBUTIGNORED", "was-not-ignored")
572 if err := Process("env_config", &s); err != nil {
573 t.Error(err.Error())
574 }
575 if s.FirstEmbeddedButIgnored != "" {
576 t.Errorf("expected empty string, got %#v", s.Ignored)
577 }
578 if s.SecondEmbeddedButIgnored != "" {
579 t.Errorf("expected empty string, got %#v", s.Ignored)
580 }
581 }
582
583 func TestNonPointerFailsProperly(t *testing.T) {
584 var s Specification
585 os.Clearenv()
586 os.Setenv("ENV_CONFIG_REQUIREDVAR", "snap")
587
588 err := Process("env_config", s)
589 if err != ErrInvalidSpecification {
590 t.Errorf("non-pointer should fail with ErrInvalidSpecification, was instead %s", err)
591 }
592 }
593
594 func TestCustomValueFields(t *testing.T) {
595 var s struct {
596 Foo string
597 Bar bracketed
598 Baz quoted
599 Struct setterStruct
600 }
601
602
603
604 s.Baz = quoted{new(bracketed)}
605
606 os.Clearenv()
607 os.Setenv("ENV_CONFIG_FOO", "foo")
608 os.Setenv("ENV_CONFIG_BAR", "bar")
609 os.Setenv("ENV_CONFIG_BAZ", "baz")
610 os.Setenv("ENV_CONFIG_STRUCT", "inner")
611
612 if err := Process("env_config", &s); err != nil {
613 t.Error(err.Error())
614 }
615
616 if want := "foo"; s.Foo != want {
617 t.Errorf("foo: got %#q, want %#q", s.Foo, want)
618 }
619
620 if want := "[bar]"; s.Bar.String() != want {
621 t.Errorf("bar: got %#q, want %#q", s.Bar, want)
622 }
623
624 if want := `["baz"]`; s.Baz.String() != want {
625 t.Errorf(`baz: got %#q, want %#q`, s.Baz, want)
626 }
627
628 if want := `setterstruct{"inner"}`; s.Struct.Inner != want {
629 t.Errorf(`Struct.Inner: got %#q, want %#q`, s.Struct.Inner, want)
630 }
631 }
632
633 func TestCustomPointerFields(t *testing.T) {
634 var s struct {
635 Foo string
636 Bar *bracketed
637 Baz *quoted
638 Struct *setterStruct
639 }
640
641
642
643 s.Bar = new(bracketed)
644 s.Baz = "ed{new(bracketed)}
645
646 os.Clearenv()
647 os.Setenv("ENV_CONFIG_FOO", "foo")
648 os.Setenv("ENV_CONFIG_BAR", "bar")
649 os.Setenv("ENV_CONFIG_BAZ", "baz")
650 os.Setenv("ENV_CONFIG_STRUCT", "inner")
651
652 if err := Process("env_config", &s); err != nil {
653 t.Error(err.Error())
654 }
655
656 if want := "foo"; s.Foo != want {
657 t.Errorf("foo: got %#q, want %#q", s.Foo, want)
658 }
659
660 if want := "[bar]"; s.Bar.String() != want {
661 t.Errorf("bar: got %#q, want %#q", s.Bar, want)
662 }
663
664 if want := `["baz"]`; s.Baz.String() != want {
665 t.Errorf(`baz: got %#q, want %#q`, s.Baz, want)
666 }
667
668 if want := `setterstruct{"inner"}`; s.Struct.Inner != want {
669 t.Errorf(`Struct.Inner: got %#q, want %#q`, s.Struct.Inner, want)
670 }
671 }
672
673 func TestEmptyPrefixUsesFieldNames(t *testing.T) {
674 var s Specification
675 os.Clearenv()
676 os.Setenv("REQUIREDVAR", "foo")
677
678 err := Process("", &s)
679 if err != nil {
680 t.Errorf("Process failed: %s", err)
681 }
682
683 if s.RequiredVar != "foo" {
684 t.Errorf(
685 `RequiredVar not populated correctly: expected "foo", got %q`,
686 s.RequiredVar,
687 )
688 }
689 }
690
691 func TestNestedStructVarName(t *testing.T) {
692 var s Specification
693 os.Clearenv()
694 os.Setenv("ENV_CONFIG_REQUIREDVAR", "required")
695 val := "found with only short name"
696 os.Setenv("INNER", val)
697 if err := Process("env_config", &s); err != nil {
698 t.Error(err.Error())
699 }
700 if s.NestedSpecification.Property != val {
701 t.Errorf("expected %s, got %s", val, s.NestedSpecification.Property)
702 }
703 }
704
705 func TestTextUnmarshalerError(t *testing.T) {
706 var s Specification
707 os.Clearenv()
708 os.Setenv("ENV_CONFIG_REQUIREDVAR", "foo")
709 os.Setenv("ENV_CONFIG_DATETIME", "I'M NOT A DATE")
710
711 err := Process("env_config", &s)
712
713 v, ok := err.(*ParseError)
714 if !ok {
715 t.Errorf("expected ParseError, got %v", v)
716 }
717 if v.FieldName != "Datetime" {
718 t.Errorf("expected %s, got %v", "Datetime", v.FieldName)
719 }
720
721 expectedLowLevelError := time.ParseError{
722 Layout: time.RFC3339,
723 Value: "I'M NOT A DATE",
724 LayoutElem: "2006",
725 ValueElem: "I'M NOT A DATE",
726 }
727
728 if v.Err.Error() != expectedLowLevelError.Error() {
729 t.Errorf("expected %s, got %s", expectedLowLevelError, v.Err)
730 }
731 }
732
733 func TestBinaryUnmarshalerError(t *testing.T) {
734 var s Specification
735 os.Clearenv()
736 os.Setenv("ENV_CONFIG_REQUIREDVAR", "foo")
737 os.Setenv("ENV_CONFIG_URLPOINTER", "http://%41:8080/")
738
739 err := Process("env_config", &s)
740
741 v, ok := err.(*ParseError)
742 if !ok {
743 t.Fatalf("expected ParseError, got %T %v", err, err)
744 }
745 if v.FieldName != "UrlPointer" {
746 t.Errorf("expected %s, got %v", "UrlPointer", v.FieldName)
747 }
748
749
750
751
752 ue, ok := v.Err.(*url.Error)
753 if !ok {
754 t.Errorf("expected error type to be \"*url.Error\", got %T", v.Err)
755 }
756
757 if ue.Op != "parse" {
758 t.Errorf("expected error op to be \"parse\", got %q", ue.Op)
759 }
760 }
761
762 func TestCheckDisallowedOnlyAllowed(t *testing.T) {
763 var s Specification
764 os.Clearenv()
765 os.Setenv("ENV_CONFIG_DEBUG", "true")
766 os.Setenv("UNRELATED_ENV_VAR", "true")
767 err := CheckDisallowed("env_config", &s)
768 if err != nil {
769 t.Errorf("expected no error, got %s", err)
770 }
771 }
772
773 func TestCheckDisallowedMispelled(t *testing.T) {
774 var s Specification
775 os.Clearenv()
776 os.Setenv("ENV_CONFIG_DEBUG", "true")
777 os.Setenv("ENV_CONFIG_ZEBUG", "false")
778 err := CheckDisallowed("env_config", &s)
779 if experr := "unknown environment variable ENV_CONFIG_ZEBUG"; err.Error() != experr {
780 t.Errorf("expected %s, got %s", experr, err)
781 }
782 }
783
784 func TestCheckDisallowedIgnored(t *testing.T) {
785 var s Specification
786 os.Clearenv()
787 os.Setenv("ENV_CONFIG_DEBUG", "true")
788 os.Setenv("ENV_CONFIG_IGNORED", "false")
789 err := CheckDisallowed("env_config", &s)
790 if experr := "unknown environment variable ENV_CONFIG_IGNORED"; err.Error() != experr {
791 t.Errorf("expected %s, got %s", experr, err)
792 }
793 }
794
795 func TestErrorMessageForRequiredAltVar(t *testing.T) {
796 var s struct {
797 Foo string `envconfig:"BAR" required:"true"`
798 }
799
800 os.Clearenv()
801 err := Process("env_config", &s)
802
803 if err == nil {
804 t.Error("no failure when missing required variable")
805 }
806
807 if !strings.Contains(err.Error(), " BAR ") {
808 t.Errorf("expected error message to contain BAR, got \"%v\"", err)
809 }
810 }
811
812 type bracketed string
813
814 func (b *bracketed) Set(value string) error {
815 *b = bracketed("[" + value + "]")
816 return nil
817 }
818
819 func (b bracketed) String() string {
820 return string(b)
821 }
822
823
824
825
826 type quoted struct{ flag.Value }
827
828 func (d quoted) Decode(value string) error {
829 return d.Set(`"` + value + `"`)
830 }
831
832 type setterStruct struct {
833 Inner string
834 }
835
836 func (ss *setterStruct) Set(value string) error {
837 ss.Inner = fmt.Sprintf("setterstruct{%q}", value)
838 return nil
839 }
840
841 func BenchmarkGatherInfo(b *testing.B) {
842 os.Clearenv()
843 os.Setenv("ENV_CONFIG_DEBUG", "true")
844 os.Setenv("ENV_CONFIG_PORT", "8080")
845 os.Setenv("ENV_CONFIG_RATE", "0.5")
846 os.Setenv("ENV_CONFIG_USER", "Kelsey")
847 os.Setenv("ENV_CONFIG_TIMEOUT", "2m")
848 os.Setenv("ENV_CONFIG_ADMINUSERS", "John,Adam,Will")
849 os.Setenv("ENV_CONFIG_MAGICNUMBERS", "5,10,20")
850 os.Setenv("ENV_CONFIG_COLORCODES", "red:1,green:2,blue:3")
851 os.Setenv("SERVICE_HOST", "127.0.0.1")
852 os.Setenv("ENV_CONFIG_TTL", "30")
853 os.Setenv("ENV_CONFIG_REQUIREDVAR", "foo")
854 os.Setenv("ENV_CONFIG_IGNORED", "was-not-ignored")
855 os.Setenv("ENV_CONFIG_OUTER_INNER", "iamnested")
856 os.Setenv("ENV_CONFIG_AFTERNESTED", "after")
857 os.Setenv("ENV_CONFIG_HONOR", "honor")
858 os.Setenv("ENV_CONFIG_DATETIME", "2016-08-16T18:57:05Z")
859 os.Setenv("ENV_CONFIG_MULTI_WORD_VAR_WITH_AUTO_SPLIT", "24")
860 for i := 0; i < b.N; i++ {
861 var s Specification
862 gatherInfo("env_config", &s)
863 }
864 }
865
View as plain text