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  }