github.com/atc0005/elbow@v0.8.8/internal/config/config_test.go (about) 1 // Copyright 2020 Adam Chalkley 2 // 3 // https://github.com/atc0005/elbow 4 // 5 // Licensed under the Apache License, Version 2.0 (the "License"); 6 // you may not use this file except in compliance with the License. 7 // You may obtain a copy of the License at 8 // 9 // https://www.apache.org/licenses/LICENSE-2.0 10 // 11 // Unless required by applicable law or agreed to in writing, software 12 // distributed under the License is distributed on an "AS IS" BASIS, 13 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 // See the License for the specific language governing permissions and 15 // limitations under the License. 16 17 package config 18 19 import ( 20 "bytes" 21 "errors" 22 "os" 23 "path/filepath" 24 "runtime" 25 "strings" 26 "testing" 27 28 "github.com/atc0005/elbow/internal/units" 29 "github.com/sirupsen/logrus" 30 ) 31 32 func GetBaseProjectDir(t *testing.T) string { 33 t.Helper() 34 35 // https://stackoverflow.com/questions/48570228/get-the-parent-path 36 wd, err := os.Getwd() 37 if err != nil { 38 t.Fatal(err) 39 } 40 41 // Our project root is two directories up from internal/config. 42 dir := filepath.Join(wd, "../..") 43 44 return dir 45 } 46 47 // TODO: Lots of variations here 48 func TestNewConfigFlagsOnly(t *testing.T) { 49 50 // https://stackoverflow.com/questions/33723300/how-to-test-the-passing-of-arguments-in-golang 51 52 // Save old command-line arguments so that we can restore them later 53 oldArgs := os.Args 54 55 // Defer restoring original command-line arguments 56 defer func() { os.Args = oldArgs }() 57 58 // TODO: A useful way to automate retrieving the app name? 59 appName := strings.ToLower(DefaultAppName) 60 if runtime.GOOS == WindowsOSName { 61 appName += WindowsAppSuffix 62 } 63 64 // Note to self: Don't add/escape double-quotes here. The shell strips 65 // them away and the application never sees them. 66 os.Args = []string{ 67 appName, 68 "--paths", "/tmp/elbow/path1", 69 "--keep", "1", 70 "--recurse", 71 "--keep-old", 72 "--log-level", "info", 73 "--use-syslog", 74 "--log-format", "text", 75 "--console-output", "stdout", 76 } 77 78 // TODO: Flesh this out 79 _, err := NewConfig() 80 if err != nil { 81 t.Errorf("Error encountered when instantiating configuration: %s", err) 82 } else { 83 t.Log("No errors encountered when instantiating configuration") 84 } 85 86 } 87 88 func TestLoadConfigFileBaseline(t *testing.T) { 89 90 // TODO: This currently mirrors the example config file. Replace with a read 91 // against that file? 92 var defaultConfigFile = []byte(` 93 [filehandling] 94 95 pattern = "reach-masterdev-" 96 file_extensions = [ 97 ".war", 98 ".tmp", 99 ] 100 101 file_age = 1 102 files_to_keep = 2 103 keep_oldest = false 104 remove = false 105 ignore_errors = true 106 107 [search] 108 109 paths = [ 110 "/tmp/elbow/path1", 111 "/tmp/elbow/path2", 112 ] 113 114 recursive_search = true 115 116 117 [logging] 118 119 log_level = "debug" 120 log_format = "text" 121 122 # If set, all output to the console will be muted and sent here instead 123 log_file_path = "/tmp/log.json" 124 125 console_output = "stdout" 126 use_syslog = false`) 127 128 // Construct a mostly empty config struct to load our config settings into. 129 // We only define values for settings that we have no intention of using 130 // from the config file, such as a logger object and an empty path to 131 // the log file that we already know the path to. 132 defaultConfigFilePath := "" 133 c := Config{ 134 ConfigFile: &defaultConfigFilePath, 135 logger: logrus.New(), 136 } 137 138 // Use our default in-memory config file settings 139 r := bytes.NewReader(defaultConfigFile) 140 141 if err := c.LoadConfigFile(r); err != nil { 142 t.Error("Unable to load in-memory configuration:", err) 143 } else { 144 t.Log("Loaded in-memory configuration file") 145 } 146 147 t.Log("LoadConfigFile wiped the existing struct, reconstructing AppMetadata fields to pass validation checks") 148 c.AppName = c.GetAppName() 149 c.AppVersion = c.GetAppVersion() 150 c.AppURL = c.GetAppURL() 151 c.AppDescription = c.GetAppDescription() 152 153 t.Logf("Current config settings: %s", c.String()) 154 155 if err := c.Validate(); err != nil { 156 t.Error("Unable to validate configuration:", err) 157 } else { 158 t.Log("Validation successful") 159 } 160 161 } 162 163 // This function is intended to test the example config file included in the 164 // repo. That example config file is a template of valid settings, so we 165 // should run tests against it to verify everything checks out. 166 func TestLoadConfigFileTemplate(t *testing.T) { 167 168 // Construct a mostly empty config struct to load our config settings into. 169 // We only define values for settings that we have no intention of using 170 // from the config file, such as a logger object and an empty path to 171 // the log file that we already know the path to. 172 defaultConfigFilePath := "" 173 c := Config{ 174 ConfigFile: &defaultConfigFilePath, 175 logger: logrus.New(), 176 } 177 178 cwd, err := os.Getwd() 179 if err != nil { 180 t.Fatalf(err.Error()) 181 } 182 t.Log("Current working directory:", cwd) 183 184 // this file is located in the base of the repo 185 exampleConfigFile := "config.example.toml" 186 187 // Get path above cwd in order to load config file (from base path of repo) 188 baseDir := GetBaseProjectDir(t) 189 if err := os.Chdir(baseDir); err != nil { 190 t.Error(err) 191 t.FailNow() 192 } else { 193 cwd, err := os.Getwd() 194 if err != nil { 195 t.Fatalf(err.Error()) 196 } 197 t.Log("New working directory:", cwd) 198 } 199 200 if fileDetails, err := os.Stat(exampleConfigFile); os.IsNotExist(err) { 201 t.Errorf("requested config file not found: %v", err) 202 } else { 203 t.Log("config file found") 204 t.Log("name:", fileDetails.Name()) 205 t.Log("size:", units.ByteCountSI(fileDetails.Size())) 206 t.Log("date/time stamp:", fileDetails.ModTime()) 207 208 fh, err := os.Open(exampleConfigFile) 209 if err != nil { 210 t.Errorf("Unable to open config file: %v", err) 211 } else { 212 t.Log("Successfully opened config file", exampleConfigFile) 213 } 214 215 // #nosec G307 216 // Believed to be a false-positive from recent gosec release 217 // https://github.com/securego/gosec/issues/714 218 defer func() { 219 if err := fh.Close(); err != nil { 220 // Ignore "file already closed" errors 221 if !errors.Is(err, os.ErrClosed) { 222 t.Errorf( 223 "failed to close file %q: %s", 224 exampleConfigFile, 225 err.Error(), 226 ) 227 } 228 } 229 }() 230 231 if err := c.LoadConfigFile(fh); err != nil { 232 t.Error("Unable to load configuration file:", err) 233 } else { 234 t.Log("Loaded configuration file") 235 } 236 237 t.Log("LoadConfigFile wiped the existing struct, reconstructing AppMetadata fields to pass validation checks") 238 c.AppName = c.GetAppName() 239 c.AppVersion = c.GetAppVersion() 240 c.AppURL = c.GetAppURL() 241 c.AppDescription = c.GetAppDescription() 242 243 t.Logf("Current config settings: %s", c.String()) 244 245 if err := c.Validate(); err != nil { 246 t.Error("Unable to validate configuration:", err) 247 } else { 248 t.Log("Validation successful") 249 } 250 251 } 252 }