github.com/fluffy-bunny/viperEx@v0.0.32/surgical-update_test.go (about)

     1  // Copyright © 2020 Herb Stahl <ghstahl@gmail.com>.
     2  //
     3  // Use of this source code is governed by an MIT-style
     4  // license that can be found in the LICENSE file.
     5  
     6  // ViperEx adds some missing gap items from the awesome Viper project is a application configuration system.
     7  
     8  package viperEx
     9  
    10  import (
    11  	"encoding/json"
    12  	"fmt"
    13  	"os"
    14  	"path"
    15  	"path/filepath"
    16  	"runtime"
    17  	"testing"
    18  
    19  	"github.com/rs/zerolog/log"
    20  	"github.com/spf13/viper"
    21  	"github.com/stretchr/testify/assert"
    22  )
    23  
    24  type NestedMap struct {
    25  	Eggs map[string]Egg
    26  }
    27  type SettingsWithNestedMap struct {
    28  	Name      string
    29  	NestedMap *NestedMap
    30  	MasterEgg Egg
    31  }
    32  type Nest struct {
    33  	Name       string
    34  	CountInt   int
    35  	CountInt16 int16
    36  	MasterEgg  Egg
    37  	Eggs       []Egg
    38  	Tags       []string
    39  }
    40  type ValueContainer struct {
    41  	Value interface{} `json:"value"`
    42  }
    43  
    44  func (vc *ValueContainer) GetString() (string, bool) {
    45  	value, ok := vc.Value.(string)
    46  	return value, ok
    47  }
    48  
    49  type Egg struct {
    50  	Weight      int32            `json:"weight"`
    51  	SomeValues  []ValueContainer `json:"somevalues"`
    52  	SomeStrings []string         `json:"somestrings"`
    53  	Name        string           `json:"name"`
    54  }
    55  type Settings struct {
    56  	Name        string
    57  	Nest        *Nest
    58  	SomeStrings []string
    59  }
    60  
    61  func init() {
    62  	chdirToTestFolder()
    63  }
    64  
    65  const keyDelim = "__"
    66  
    67  func ReadAppsettings(rootPath string) (*viper.Viper, error) {
    68  	var err error
    69  	environment := os.Getenv("APPLICATION_ENVIRONMENT")
    70  	myViper := viper.NewWithOptions(viper.KeyDelimiter(keyDelim))
    71  	// Environment Variables override everything.
    72  	myViper.AutomaticEnv()
    73  	myViper.SetConfigType("json")
    74  	configFile := "appsettings.json"
    75  	configPath := path.Join(rootPath, configFile)
    76  	viper.SetConfigFile(configPath)
    77  	err = myViper.ReadInConfig()
    78  	if err == nil {
    79  		return nil, err
    80  	}
    81  	configFile = "appsettings." + environment + ".json"
    82  	myViper.SetConfigFile(configPath)
    83  	err = myViper.MergeInConfig()
    84  	return myViper, err
    85  }
    86  
    87  func getConfigPath() string {
    88  	var configPath string
    89  	_, err := os.Stat("./settings")
    90  	if !os.IsNotExist(err) {
    91  		configPath, _ = filepath.Abs("./settings")
    92  		log.Info().Str("path", configPath).Msg("Configuration Root Folder")
    93  	}
    94  	return configPath
    95  }
    96  func chdirToTestFolder() {
    97  	_, filename, _, _ := runtime.Caller(0)
    98  	// The ".." may change depending on you folder structure
    99  	dir := path.Join(path.Dir(filename), ".")
   100  	fmt.Println(filename)
   101  	fmt.Println(dir)
   102  	err := os.Chdir(dir)
   103  	if err != nil {
   104  		panic(err)
   105  	}
   106  }
   107  
   108  func TestViperExEnvUpdate(t *testing.T) {
   109  	configPath := getConfigPath()
   110  	myViper, err := ReadAppsettings(configPath)
   111  	assert.NoError(t, err)
   112  	if err != nil {
   113  		panic(err)
   114  	}
   115  	allSettings := myViper.AllSettings()
   116  	fmt.Println(PrettyJSON(allSettings))
   117  
   118  	myViperEx, err := New(allSettings, func(ve *ViperEx) error {
   119  		ve.KeyDelimiter = keyDelim
   120  		return nil
   121  	})
   122  	expectedURI := "https://www.blah.com/?ssl=true&a=b&c=d"
   123  	envs := map[string]string{
   124  
   125  		"APPLICATION_ENVIRONMENT":             "Test",
   126  		"nest__Eggs__1__Weight":               "5555",
   127  		"nest__Eggs__1__SomeValues__1__Value": "Heidi",
   128  		"nest__Eggs__1__SomeStrings__1":       "Zep",
   129  		"nest__masteregg__name":               expectedURI,
   130  	}
   131  	for k, v := range envs {
   132  		os.Setenv(k, v)
   133  	}
   134  	os.Setenv("APPLICATION_ENVIRONMENT", "Test")
   135  
   136  	err = myViperEx.UpdateFromEnv()
   137  	fmt.Println(PrettyJSON(allSettings))
   138  	assert.NoError(t, err)
   139  	if err != nil {
   140  		panic(err)
   141  	}
   142  	for k := range envs {
   143  		os.Remove(k)
   144  	}
   145  
   146  	settings := Settings{}
   147  	err = myViperEx.Unmarshal(&settings)
   148  	fmt.Println(PrettyJSON(allSettings))
   149  	assert.NoError(t, err)
   150  	if err != nil {
   151  		panic(err)
   152  	}
   153  
   154  	assert.Equal(t, expectedURI, settings.Nest.MasterEgg.Name)
   155  	assert.Equal(t, "straw", settings.Nest.Name)
   156  	assert.Equal(t, "Heidi", settings.Nest.Eggs[1].SomeValues[1].Value)
   157  	assert.Equal(t, "Zep", settings.Nest.Eggs[1].SomeStrings[1])
   158  	assert.Equal(t, int32(5555), settings.Nest.Eggs[1].Weight)
   159  }
   160  func TestViperSurgicalUpdate_URIWithArgs(t *testing.T) {
   161  	configPath := getConfigPath()
   162  	myViper, err := ReadAppsettings(configPath)
   163  	assert.NoError(t, err)
   164  	if err != nil {
   165  		panic(err)
   166  	}
   167  	allSettings := myViper.AllSettings()
   168  	fmt.Println(PrettyJSON(allSettings))
   169  	myViperEx, err := New(allSettings, func(ve *ViperEx) error {
   170  		ve.KeyDelimiter = "__"
   171  		return nil
   172  	})
   173  	expectedURI := "https://www.blah.com/?ssl=true&a=b&c=d"
   174  	envs := map[string]interface{}{
   175  		"name": expectedURI,
   176  		"nestedMap__Eggs__bob__SomeValues__1__Value": expectedURI,
   177  		"MasterEgg__name": expectedURI,
   178  	}
   179  	for k, v := range envs {
   180  		myViperEx.UpdateDeepPath(k, v)
   181  	}
   182  	fmt.Println(PrettyJSON(allSettings))
   183  
   184  	settings := SettingsWithNestedMap{}
   185  	err = myViperEx.Unmarshal(&settings)
   186  	assert.NoError(t, err)
   187  	if err != nil {
   188  		panic(err)
   189  	}
   190  	assert.Equal(t, expectedURI, settings.Name)
   191  	assert.Equal(t, expectedURI, settings.NestedMap.Eggs["bob"].SomeValues[1].Value)
   192  	assert.Equal(t, expectedURI, settings.MasterEgg.Name)
   193  }
   194  
   195  func TestViperSurgicalUpdate_NestedMap(t *testing.T) {
   196  	configPath := getConfigPath()
   197  	myViper, err := ReadAppsettings(configPath)
   198  	assert.NoError(t, err)
   199  	if err != nil {
   200  		panic(err)
   201  	}
   202  	allSettings := myViper.AllSettings()
   203  	fmt.Println(PrettyJSON(allSettings))
   204  
   205  	myViperEx, err := New(allSettings, func(ve *ViperEx) error {
   206  		ve.KeyDelimiter = "__"
   207  		return nil
   208  	})
   209  
   210  	envs := map[string]interface{}{
   211  		"name":                                         "bowie",
   212  		"nestedMap__Eggs__bob__Weight":                 1234,
   213  		"nestedMap__Eggs__bob__Weight__":               1234,
   214  		"nestedMap__Eggs__bob__SomeValues__1__Value":   "abcd",
   215  		"nestedMap__Eggs__bob__SomeStrings__1":         "abcd",
   216  		"nestedMap__Eggs__bob__SomeStrings__1__":       "abcd",
   217  		"nestedMap__Eggs__junk__SomeStrings__1__":      "abcd",
   218  		"nestedMap__Eggs__junk__SomeStrings__1__Value": "abcd",
   219  	}
   220  	for k, v := range envs {
   221  		myViperEx.UpdateDeepPath(k, v)
   222  	}
   223  
   224  	fmt.Println(PrettyJSON(allSettings))
   225  
   226  	settings := SettingsWithNestedMap{}
   227  	err = myViperEx.Unmarshal(&settings)
   228  	assert.NoError(t, err)
   229  	if err != nil {
   230  		panic(err)
   231  	}
   232  	assert.Equal(t, "bowie", settings.Name)
   233  	assert.Equal(t, int32(1234), settings.NestedMap.Eggs["bob"].Weight)
   234  	assert.Equal(t, "abcd", settings.NestedMap.Eggs["bob"].SomeValues[1].Value)
   235  	assert.Equal(t, "abcd", settings.NestedMap.Eggs["bob"].SomeStrings[1])
   236  	_, ok := allSettings["junk"]
   237  	assert.False(t, ok)
   238  
   239  	name := myViperEx.Find("name")
   240  	assert.NotNil(t, name)
   241  
   242  	nestJunk := myViperEx.Find("nestedMap__Eggs__junk")
   243  	assert.Nil(t, nestJunk)
   244  
   245  	nestJunk = myViperEx.Find("nestedMap__Eggs__junk__SomeStrings__1__Value")
   246  	assert.Nil(t, nestJunk)
   247  }
   248  func TestViperSurgicalUpdate(t *testing.T) {
   249  	configPath := getConfigPath()
   250  	myViper, err := ReadAppsettings(configPath)
   251  	assert.NoError(t, err)
   252  	if err != nil {
   253  		panic(err)
   254  	}
   255  	allSettings := myViper.AllSettings()
   256  	fmt.Println(PrettyJSON(allSettings))
   257  
   258  	myViperEx, err := New(allSettings, func(ve *ViperEx) error {
   259  		ve.KeyDelimiter = "__"
   260  		return nil
   261  	})
   262  	envs := map[string]interface{}{
   263  		"nest__MasterEgg__Weight":             1,
   264  		"nest__Eggs__0__Weight":               1234,
   265  		"nest__Eggs__0__Weight__":             1234,
   266  		"nest__Eggs__0__SomeValues__1__Value": "abcd",
   267  		"nest__Eggs__0__SomeStrings__1":       "abcd",
   268  		"nest__Eggs__0__SomeStrings__1__":     "abcd",
   269  		"junk__A":                             "abcd",
   270  		"nest__junk":                          "abcd",
   271  		"nest__tags__0":                       "abcd",
   272  		"nest__tags__1":                       "abcd",
   273  		"somestrings__0":                      "abcd",
   274  	}
   275  	for k, v := range envs {
   276  		myViperEx.UpdateDeepPath(k, v)
   277  	}
   278  
   279  	fmt.Println(PrettyJSON(allSettings))
   280  
   281  	settings := Settings{}
   282  	err = myViperEx.Unmarshal(&settings)
   283  	assert.NoError(t, err)
   284  	if err != nil {
   285  		panic(err)
   286  	}
   287  	assert.Equal(t, "abcd", settings.SomeStrings[0])
   288  	assert.Equal(t, "bob", settings.Name)
   289  	assert.Equal(t, int32(1), settings.Nest.MasterEgg.Weight)
   290  	assert.Equal(t, int32(1234), settings.Nest.Eggs[0].Weight)
   291  	assert.Equal(t, "abcd", settings.Nest.Eggs[0].SomeValues[1].Value)
   292  	assert.Equal(t, "abcd", settings.Nest.Eggs[0].SomeStrings[1])
   293  	assert.Equal(t, "abcd", settings.Nest.Tags[0])
   294  	_, ok := allSettings["junk"]
   295  	assert.False(t, ok)
   296  
   297  	name := myViperEx.Find("name")
   298  	assert.NotNil(t, name)
   299  
   300  	nestJunk := myViperEx.Find("nest__junk")
   301  	assert.Nil(t, nestJunk)
   302  
   303  	var item interface{}
   304  
   305  	item = myViperEx.Find("nest__Eggs")
   306  	assert.NotNil(t, item)
   307  
   308  	item = myViperEx.Find("nest__Eggs")
   309  	assert.NotNil(t, item)
   310  
   311  	item = myViperEx.Find("nest__Eggs__")
   312  	assert.Nil(t, item)
   313  
   314  	item = myViperEx.Find("nest__Eggs__0__SomeStrings__1")
   315  	assert.NotNil(t, item)
   316  
   317  	item = myViperEx.Find("nest__Eggs__0__SomeStrings__1__")
   318  	assert.Nil(t, item)
   319  
   320  	item = myViperEx.Find("nest__Eggs__0__Junk__1")
   321  	assert.Nil(t, item)
   322  
   323  	item = myViperEx.Find("nest__junk__0__Junk__1")
   324  	assert.Nil(t, item)
   325  
   326  	item = myViperEx.Find("nest__junk__0")
   327  	assert.Nil(t, item)
   328  }
   329  
   330  // PrettyJSON to string
   331  func PrettyJSON(obj interface{}) string {
   332  	jsonBytes, err := json.MarshalIndent(obj, "", "    ")
   333  	if err != nil {
   334  		panic(err)
   335  	}
   336  	return string(jsonBytes)
   337  }
   338  
   339  // JSON from object
   340  func JSON(obj interface{}) string {
   341  	jsonBytes, err := json.Marshal(obj)
   342  	if err != nil {
   343  		panic(err)
   344  	}
   345  	return string(jsonBytes)
   346  }