...

Source file src/github.com/kelseyhightower/envconfig/envconfig_test.go

Documentation: github.com/kelseyhightower/envconfig

     1  // Copyright (c) 2013 Kelsey Hightower. All rights reserved.
     2  // Use of this source code is governed by the MIT License that can be found in
     3  // the LICENSE file.
     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  	// If the var is not defined the non-prefixed version should not be used
   328  	// unless the struct tag says so
   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  	// Setting the alt version of the var in the environment has no effect if
   346  	// the struct tag is not supplied
   347  	if s.MultiWordVar != "" {
   348  		t.Errorf("expected %q, got %q", "", s.MultiWordVar)
   349  	}
   350  
   351  	// Setting the alt version of the var in the environment correctly sets
   352  	// the value if the struct tag IS supplied
   353  	if s.MultiWordVarWithAlt != "bar" {
   354  		t.Errorf("expected %q, got %q", "bar", s.MultiWordVarWithAlt)
   355  	}
   356  
   357  	// Alt value is not case sensitive and is treated as all uppercase
   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  	// Set would panic when the receiver is nil,
   603  	// so make sure it has an initial value to replace.
   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  	// Set would panic when the receiver is nil,
   642  	// so make sure they have initial values to replace.
   643  	s.Bar = new(bracketed)
   644  	s.Baz = &quoted{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  	// To be compatible with go 1.5 and lower we should do a very basic check,
   750  	// because underlying error message varies in go 1.5 and go 1.6+.
   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  // quoted is used to test the precedence of Decode over Set.
   824  // The sole field is a flag.Value rather than a setter to validate that
   825  // all flag.Value implementations are also Setter implementations.
   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