docs/utils/configuration_test.go

363 lines
9.6 KiB
Go

package utils
import (
"bytes"
"fmt"
"os"
"testing"
"github.com/Sirupsen/logrus"
"github.com/bugsnag/bugsnag-go"
"github.com/spf13/viper"
"github.com/stretchr/testify/assert"
)
const envPrefix = "NOTARY_TESTING_ENV_PREFIX"
// initializes a viper object with test configuration
func configure(jsonConfig string) *viper.Viper {
config := viper.New()
SetupViper(config, envPrefix)
config.SetConfigType("json")
config.ReadConfig(bytes.NewBuffer([]byte(jsonConfig)))
return config
}
// Sets the environment variables in the given map, prefixed by envPrefix.
func setupEnvironmentVariables(t *testing.T, vars map[string]string) {
for k, v := range vars {
err := os.Setenv(fmt.Sprintf("%s_%s", envPrefix, k), v)
assert.NoError(t, err)
}
}
// Unsets whatever environment variables were set with this map
func cleanupEnvironmentVariables(t *testing.T, vars map[string]string) {
for k := range vars {
err := os.Unsetenv(fmt.Sprintf("%s_%s", envPrefix, k))
assert.NoError(t, err)
}
}
// An error is returned if the log level is not parsable
func TestParseInvalidLogLevel(t *testing.T) {
_, err := ParseLogLevel(configure(`{"logging": {"level": "horatio"}}`),
logrus.DebugLevel)
assert.Error(t, err)
assert.Contains(t, err.Error(), "not a valid logrus Level")
}
// If there is no logging level configured it is set to the default level
func TestParseNoLogLevel(t *testing.T) {
empties := []string{`{}`, `{"logging": {}}`}
for _, configJSON := range empties {
lvl, err := ParseLogLevel(configure(configJSON), logrus.DebugLevel)
assert.NoError(t, err)
assert.Equal(t, logrus.DebugLevel, lvl)
}
}
// If there is logging level configured, it is set to the configured one
func TestParseLogLevel(t *testing.T) {
lvl, err := ParseLogLevel(configure(`{"logging": {"level": "error"}}`),
logrus.DebugLevel)
assert.NoError(t, err)
assert.Equal(t, logrus.ErrorLevel, lvl)
}
func TestParseLogLevelWithEnvironmentVariables(t *testing.T) {
vars := map[string]string{"LOGGING_LEVEL": "error"}
setupEnvironmentVariables(t, vars)
defer cleanupEnvironmentVariables(t, vars)
lvl, err := ParseLogLevel(configure(`{}`),
logrus.DebugLevel)
assert.NoError(t, err)
assert.Equal(t, logrus.ErrorLevel, lvl)
}
// An error is returned if there's no API key
func TestParseInvalidBugsnag(t *testing.T) {
_, err := ParseBugsnag(configure(
`{"reporting": {"bugsnag": {"endpoint": "http://12345"}}}`))
assert.Error(t, err)
assert.Contains(t, err.Error(), "must provide an API key")
}
// If there's no bugsnag, a nil pointer is returned
func TestParseNoBugsnag(t *testing.T) {
empties := []string{`{}`, `{"reporting": {}}`}
for _, configJSON := range empties {
bugconf, err := ParseBugsnag(configure(configJSON))
assert.NoError(t, err)
assert.Nil(t, bugconf)
}
}
func TestParseBugsnag(t *testing.T) {
config := configure(`{
"reporting": {
"bugsnag": {
"api_key": "12345",
"release_stage": "production",
"endpoint": "http://1234.com"
}
}
}`)
expected := bugsnag.Configuration{
APIKey: "12345",
ReleaseStage: "production",
Endpoint: "http://1234.com",
}
bugconf, err := ParseBugsnag(config)
assert.NoError(t, err)
assert.Equal(t, expected, *bugconf)
}
func TestParseBugsnagWithEnvironmentVariables(t *testing.T) {
config := configure(`{
"reporting": {
"bugsnag": {
"api_key": "12345",
"release_stage": "staging"
}
}
}`)
vars := map[string]string{
"REPORTING_BUGSNAG_RELEASE_STAGE": "production",
"REPORTING_BUGSNAG_ENDPOINT": "http://1234.com",
}
setupEnvironmentVariables(t, vars)
defer cleanupEnvironmentVariables(t, vars)
expected := bugsnag.Configuration{
APIKey: "12345",
ReleaseStage: "production",
Endpoint: "http://1234.com",
}
bugconf, err := ParseBugsnag(config)
assert.NoError(t, err)
assert.Equal(t, expected, *bugconf)
}
// If the storage backend is invalid or not provided, an error is returned.
func TestParseInvalidStorageBackend(t *testing.T) {
invalids := []string{
`{"storage": {"backend": "postgres", "db_url": "1234"}}`,
`{"storage": {"db_url": "12345"}}`,
`{"storage": {}}`,
`{}`,
}
for _, configJSON := range invalids {
_, err := ParseStorage(configure(configJSON),
[]string{MySQLBackend, SqliteBackend})
assert.Error(t, err, fmt.Sprintf("'%s' should be an error", configJSON))
assert.Contains(t, err.Error(),
"must specify one of these supported backends: mysql, sqlite3")
}
}
// If there is no DB url for non-memory backends, an error is returned.
func TestParseInvalidStorageNoDBSource(t *testing.T) {
invalids := []string{
`{"storage": {"backend": "%s"}}`,
`{"storage": {"backend": "%s", "db_url": ""}}`,
}
for _, backend := range []string{MySQLBackend, SqliteBackend} {
for _, configJSONFmt := range invalids {
configJSON := fmt.Sprintf(configJSONFmt, backend)
_, err := ParseStorage(configure(configJSON),
[]string{MySQLBackend, SqliteBackend})
assert.Error(t, err, fmt.Sprintf("'%s' should be an error", configJSON))
assert.Contains(t, err.Error(),
fmt.Sprintf("must provide a non-empty database source for %s", backend))
}
}
}
// If a memory storage backend is specified, no DB URL is necessary for a
// successful storage parse.
func TestParseStorageMemoryStore(t *testing.T) {
config := configure(`{"storage": {"backend": "MEMORY"}}`)
expected := Storage{Backend: MemoryBackend}
store, err := ParseStorage(config, []string{MySQLBackend, MemoryBackend})
assert.NoError(t, err)
assert.Equal(t, expected, *store)
}
// A supported backend with DB source will be successfully parsed.
func TestParseStorageDBStore(t *testing.T) {
config := configure(`{
"storage": {
"backend": "MySQL",
"db_url": "username:passord@tcp(hostname:1234)/dbname"
}
}`)
expected := Storage{
Backend: "mysql",
Source: "username:passord@tcp(hostname:1234)/dbname",
}
store, err := ParseStorage(config, []string{"mysql"})
assert.NoError(t, err)
assert.Equal(t, expected, *store)
}
func TestParseStorageWithEnvironmentVariables(t *testing.T) {
config := configure(`{
"storage": {
"db_url": "username:passord@tcp(hostname:1234)/dbname"
}
}`)
vars := map[string]string{"STORAGE_BACKEND": "MySQL"}
setupEnvironmentVariables(t, vars)
defer cleanupEnvironmentVariables(t, vars)
expected := Storage{
Backend: "mysql",
Source: "username:passord@tcp(hostname:1234)/dbname",
}
store, err := ParseStorage(config, []string{"mysql"})
assert.NoError(t, err)
assert.Equal(t, expected, *store)
}
// If TLS is required and the parameters are missing, an error is returned
func TestParseTLSNoTLSWhenRequired(t *testing.T) {
invalids := []string{
`{"server": {"tls_cert_file": "path/to/cert"}}`,
`{"server": {"tls_key_file": "path/to/key"}}`,
}
for _, configJSON := range invalids {
_, err := ParseServerTLS(configure(configJSON), true)
assert.Error(t, err)
assert.Contains(t, err.Error(),
"both the TLS certificate and key are mandatory")
}
}
// If TLS is not and the cert/key are partially provided, an error is returned
func TestParseTLSPartialTLS(t *testing.T) {
invalids := []string{
`{"server": {"tls_cert_file": "path/to/cert"}}`,
`{"server": {"tls_key_file": "path/to/key"}}`,
}
for _, configJSON := range invalids {
_, err := ParseServerTLS(configure(configJSON), false)
assert.Error(t, err)
assert.Contains(t, err.Error(),
"either include both a cert and key file, or neither to disable TLS")
}
}
func TestParseTLSNoTLSNotRequired(t *testing.T) {
config := configure(`{
"server": {}
}`)
tlsOpts, err := ParseServerTLS(config, false)
assert.NoError(t, err)
assert.Nil(t, tlsOpts)
}
func TestParseTLSWithTLS(t *testing.T) {
config := configure(`{
"server": {
"tls_cert_file": "path/to/cert",
"tls_key_file": "path/to/key",
"client_ca_file": "path/to/clientca"
}
}`)
expected := ServerTLSOpts{
ServerCertFile: "path/to/cert",
ServerKeyFile: "path/to/key",
ClientCAFile: "path/to/clientca",
}
tlsOpts, err := ParseServerTLS(config, false)
assert.NoError(t, err)
assert.Equal(t, expected, *tlsOpts)
}
func TestParseTLSWithTLSRelativeToConfigFile(t *testing.T) {
config := configure(`{
"server": {
"tls_cert_file": "path/to/cert",
"tls_key_file": "/abspath/to/key",
"client_ca_file": ""
}
}`)
config.SetConfigFile("/opt/me.json")
expected := ServerTLSOpts{
ServerCertFile: "/opt/path/to/cert",
ServerKeyFile: "/abspath/to/key",
ClientCAFile: "",
}
tlsOpts, err := ParseServerTLS(config, false)
assert.NoError(t, err)
assert.Equal(t, expected, *tlsOpts)
}
func TestParseTLSWithEnvironmentVariables(t *testing.T) {
config := configure(`{
"server": {
"tls_cert_file": "path/to/cert",
"client_ca_file": "nosuchfile"
}
}`)
vars := map[string]string{
"SERVER_TLS_KEY_FILE": "path/to/key",
"SERVER_CLIENT_CA_FILE": "path/to/clientca",
}
setupEnvironmentVariables(t, vars)
defer cleanupEnvironmentVariables(t, vars)
expected := ServerTLSOpts{
ServerCertFile: "path/to/cert",
ServerKeyFile: "path/to/key",
ClientCAFile: "path/to/clientca",
}
tlsOpts, err := ParseServerTLS(config, true)
assert.NoError(t, err)
assert.Equal(t, expected, *tlsOpts)
}
func TestParseViperWithInvalidFile(t *testing.T) {
v := viper.New()
SetupViper(v, envPrefix)
err := ParseViper(v, "Chronicle_Of_Dark_Secrets.json")
assert.Error(t, err)
assert.Contains(t, err.Error(), "Could not read config")
}
func TestParseViperWithValidFile(t *testing.T) {
file, err := os.Create("/tmp/Chronicle_Of_Dark_Secrets.json")
assert.NoError(t, err)
defer os.Remove(file.Name())
file.WriteString(`{"logging": {"level": "debug"}}`)
v := viper.New()
SetupViper(v, envPrefix)
err = ParseViper(v, "/tmp/Chronicle_Of_Dark_Secrets.json")
assert.NoError(t, err)
assert.Equal(t, "debug", v.GetString("logging.level"))
}