config/config_test.go
2024-08-09 14:05:53 -06:00

282 lines
7.1 KiB
Go

package config
import (
"context"
"fmt"
"github.com/Masterminds/semver"
"github.com/spf13/afero"
"os"
filepath2 "path/filepath"
"strconv"
"strings"
"testing"
"github.com/cucumber/godog"
"github.com/cucumber/godog/colors"
"github.com/spf13/pflag"
a "github.com/stretchr/testify/assert"
)
var (
SupportedDataTypes = [4]string{"string", "integer", "float", "boolean"}
assertTest *a.Assertions
assertError = fmt.Errorf("assert error")
opts = godog.Options{
Output: colors.Colored(os.Stdout),
Format: "pretty",
Paths: []string{"features"},
}
originalCommandLine = os.Args
)
const (
StringDefaultValue = "default value"
StringConfigValue = "config file value"
StringEnvironmentValue = "environment variable value"
StringFlagValue = "command line value"
IntegerDefaultValue = 123
IntegerConfigValue = 234
IntegerEnvironmentValue = "345"
IntegerFlagValue = 456
FloatDefaultValue = 1.99
FloatConfigValue = 2.99
FloatEnvironmentValue = "3.99"
FloatFlagValue = 4.99
BoolDefaultValue = false
BoolConfigValue = true
BoolEnvironmentValue = "false"
BoolFlagValue = true
)
func init() {
godog.BindCommandLineFlags("godog.", &opts)
configFS = afero.NewMemMapFs()
}
func TestCucumber(t *testing.T) {
pflag.Parse()
assertTest = a.New(t)
opts.Paths = pflag.Args()
opts.TestingT = t
suite := godog.TestSuite{
Name: "pretty",
ScenarioInitializer: InitializeScenario,
TestSuiteInitializer: InitializeTestSuite,
Options: &opts,
}
if suite.Run() != 0 {
t.Fatal("cucumber tests failed")
}
}
func InitializeTestSuite(ctx *godog.TestSuiteContext) {
ctx.BeforeSuite(func() {
os.Args = []string{originalCommandLine[0]}
})
ctx.AfterSuite(func() {
os.Args = originalCommandLine
})
}
func InitializeScenario(ctx *godog.ScenarioContext) {
ctx.Given(`^an application with a name of "([^"]*)" and a prefix of "([^"]*)"$`, setAppNameAndPrefix)
ctx.Given(`^a version of "([^"]*)"$`, setAppVersion)
ctx.Given(`^a "([^"]*)" configuration item defines as$`, setupConfigItem)
ctx.When(`^I call GetConfig$`, callGetConfig)
ctx.Then(`^the "([^"]*)" result should return "([^"]*)"$`, checkConfigValue)
ctx.Then(`^the app name should return "([^"]*)"$`, theAppNameShouldReturn)
ctx.Then(`^the app prefix should return "([^"]*)"$`, theAppPrefixShouldReturn)
ctx.Then(`^the app version should pass the following constraint: "([^"]*)"$`, theAppVersionShouldPassTheFollowingConstraint)
}
// Given Callbacks
func setAppNameAndPrefix(ctx context.Context, name, prefix string) context.Context {
SetAppName(name, prefix)
return context.WithValue(ctx, "appPrefix", prefix)
}
func setAppVersion(_ context.Context, versionString string) {
SetAppVersion(versionString)
}
func setupConfigItem(ctx context.Context, itemType string, itemDefinition *godog.Table) (context.Context, error) {
if assertTest.Containsf(SupportedDataTypes, itemType, "config items of type '%s' are not yet supported", itemType) {
var (
data = make(map[string]string)
name string
shortFlag = ""
helpText = ""
fileContents = ""
defVal any
configVal any
envVal string
flagVal any
)
for idx, key := range itemDefinition.Rows[0].Cells {
data[key.Value] = itemDefinition.Rows[1].Cells[idx].Value
}
ctx = context.WithValue(ctx, "data", data)
name = data["name"]
switch itemType {
case "string":
defVal = StringDefaultValue
configVal = StringConfigValue
envVal = StringEnvironmentValue
flagVal = StringFlagValue
helpText = "String Config Item Test"
case "integer":
defVal = IntegerDefaultValue
configVal = IntegerConfigValue
envVal = IntegerEnvironmentValue
flagVal = IntegerFlagValue
helpText = "Integer Config Item Test"
case "float":
defVal = FloatDefaultValue
configVal = FloatConfigValue
envVal = FloatEnvironmentValue
flagVal = FloatFlagValue
helpText = "Float Config Item Test"
case "boolean":
defVal = BoolDefaultValue
configVal = BoolConfigValue
envVal = BoolEnvironmentValue
flagVal = BoolFlagValue
helpText = "Bool Config Item Test"
}
if data["has_file_entry"] == "true" {
var dirPath = filepath2.Join("/", "etc", Config.GetString("app.prefix"))
var filename = filepath2.Join(dirPath, "config.yaml")
fileContents = fmt.Sprintf("---\n%s: %v\n", name, configVal)
err := configFS.MkdirAll(dirPath, 0755)
if !assertTest.NoError(err, "unable to create test config folder") {
if !assertTest.DirExists(dirPath, "unable to create test config folder") {
return ctx, assertError
}
}
err = afero.WriteFile(configFS, filename, []byte(fileContents), 0755)
if !assertTest.NoError(err, "unable to write file contents") {
return ctx, fmt.Errorf(err.Error())
}
}
if data["has_env_var"] == "true" {
if data["has_env_var"] == "true" {
key := strings.ToUpper(
strings.ReplaceAll(
strings.ReplaceAll(
fmt.Sprintf("test_%s", name),
"-",
"_",
),
".",
"_",
),
)
_ = os.Setenv(key, envVal)
}
}
if data["commandline_flag"] != "" {
os.Args = append(os.Args, fmt.Sprintf("%s=%v", data["commandline_flag"], flagVal))
tagName := strings.Trim(data["commandline_flag"], "-")
if len(tagName) == 1 {
shortFlag = tagName
}
}
DefineConfigItem(name, shortFlag, defVal, helpText)
return ctx, nil
} else {
return ctx, assertError
}
}
// When Callbacks
func callGetConfig(_ context.Context) {
GetConfigs()
}
// Then Callbacks
func checkConfigValue(ctx context.Context, itemType, expectedString string) error {
var (
// key = ctx.Value("itemName").(string)
data = ctx.Value("data").(map[string]string)
key = data["name"]
err error
)
if assertTest.Containsf(SupportedDataTypes, itemType, "config items of type '%s' are not yet supported", itemType) {
var actual, expected interface{}
switch itemType {
case "string":
actual = strings.Trim(Config.GetString(key), "\"")
expected = expectedString
case "integer":
actual = Config.GetInt64(key)
expected, _ = strconv.ParseInt(expectedString, 10, 64)
case "float":
actual = Config.GetFloat64(key)
expected, _ = strconv.ParseFloat(expectedString, 64)
case "boolean":
actual = Config.GetBool(key)
expected, _ = strconv.ParseBool(expectedString)
}
if !assertTest.Equal(expected, actual, "config item should equal expected", os.Args, data) {
err = assertError
}
} else {
err = assertError
}
return err
}
func theAppNameShouldReturn(expected string) error {
if !assertTest.Equal(expected, Config.GetString("app.name")) {
return assertError
}
return nil
}
func theAppPrefixShouldReturn(expected string) error {
if !assertTest.Equal(expected, Config.GetString("app.prefix")) {
return assertError
}
return nil
}
func theAppVersionShouldPassTheFollowingConstraint(constraintString string) error {
var (
constraint, _ = semver.NewConstraint(constraintString)
actual = Config.Get("app.version").(*semver.Version)
)
if !assertTest.True(constraint.Check(actual), "version does not pass check") {
return assertError
}
return nil
}