test: also test env vars

main
Jef Roosens 2022-12-28 18:13:20 +01:00
parent 44aa7c9dc9
commit 997e9611eb
Signed by: Jef Roosens
GPG Key ID: B75D4F293C7052DB
2 changed files with 83 additions and 18 deletions

37
conf.v
View File

@ -4,16 +4,25 @@ import os
import toml import toml
import datatypes { Set } import datatypes { Set }
[params]
pub struct LoadConfig {
prefix string
file_suffix string = '_FILE'
default_path string
// Allows overwriting
env map[string]string = os.environ()
}
// get_env_var tries to read the contents of the given environment variable. It // get_env_var tries to read the contents of the given environment variable. It
// looks for either `${env.prefix}${field_name.to_upper()}` or // looks for either `${env.prefix}${field_name.to_upper()}` or
// `${env.prefix}${field_name.to_upper()}${env.file_suffix}`, returning the // `${env.prefix}${field_name.to_upper()}${env.file_suffix}`, returning the
// contents of the file instead if the latter. If both or neither exist, the // contents of the file instead if the latter. If both or neither exist, the
// function returns an error. // function returns an error.
fn get_env_var(prefix string, field_name string, file_suffix string) !string { fn (ld LoadConfig) get_env_var(field_name string) !string {
env_var_name := '$prefix$field_name.to_upper()' env_var_name := '$ld.prefix$field_name.to_upper()'
env_file_name := '$prefix$field_name.to_upper()$file_suffix' env_file_name := '$ld.prefix$field_name.to_upper()$ld.file_suffix'
env_var := os.getenv(env_var_name) env_var := ld.env[env_var_name] or { '' }
env_file := os.getenv(env_file_name) env_file := ld.env[env_file_name] or { '' }
// If both are missing, we return an empty string // If both are missing, we return an empty string
if env_var == '' && env_file == '' { if env_var == '' && env_file == '' {
@ -39,18 +48,11 @@ fn get_env_var(prefix string, field_name string, file_suffix string) !string {
} }
} }
[params]
pub struct LoadConfig {
prefix string
file_suffix string = '_FILE'
default_path string
}
// load<T> attempts to create an object of type T from the given path to a toml // load<T> attempts to create an object of type T from the given path to a toml
// file & environment variables. For each field, it will select either a value // file & environment variables. For each field, it will select either a value
// given from an environment variable, a value defined in the config file or a // given from an environment variable, a value defined in the config file or a
// configured default if present, in that order. // configured default if present, in that order.
pub fn load<T>(conf LoadConfig) !T { pub fn load<T>(ld LoadConfig) !T {
mut res := T{} mut res := T{}
// This array allows us to determine later whether the variable is actually // This array allows us to determine later whether the variable is actually
@ -58,7 +60,7 @@ pub fn load<T>(conf LoadConfig) !T {
mut has_value := Set<string>{} mut has_value := Set<string>{}
// Later, this could be read from an env var as well. // Later, this could be read from an env var as well.
path := conf.default_path path := ld.default_path
if os.exists(path) { if os.exists(path) {
// We don't use reflect here because reflect also sets any fields not // We don't use reflect here because reflect also sets any fields not
@ -84,7 +86,7 @@ pub fn load<T>(conf LoadConfig) !T {
} }
$for field in T.fields { $for field in T.fields {
env_value := get_env_var(conf.prefix, field.name, conf.file_suffix)! env_value := ld.get_env_var(field.name)!
// The value of an env var will always take precedence over the toml // The value of an env var will always take precedence over the toml
// file. // file.
@ -117,12 +119,11 @@ pub fn load<T>(conf LoadConfig) !T {
} }
} }
// If there's no value provided in any way, we notify the user with an // If there's no value provided in any way, we notify the user with an
// error. // error.
if !has_value.exists(field.name) { if !has_value.exists(field.name) {
return error("Missing config variable '$field.name' with no provided default. Either add it to the config file or provide it using an environment variable.") return error("Missing config variable '$field.name' with no provided default. Either add it to the config file or provide it using an environment variable.")
} }
} }
return res return res
} }

View File

@ -8,6 +8,14 @@ struct SingleConfDefault {
some_string string = 'default' some_string string = 'default'
} }
const env = {
'SOME_STRING': 'env'
}
const prefix_env = {
'TEST_SOME_STRING': 'env'
}
fn test_string_present_no_default() { fn test_string_present_no_default() {
conf := load<SingleConf>(default_path: 'test/string.toml')! conf := load<SingleConf>(default_path: 'test/string.toml')!
assert conf == SingleConf{ assert conf == SingleConf{
@ -15,11 +23,35 @@ fn test_string_present_no_default() {
} }
} }
fn test_string_present_no_default_env() {
conf := load<SingleConf>(default_path: 'test/string.toml', env: .env)!
assert conf == SingleConf{
some_string: 'env'
}
conf2 := load<SingleConf>(default_path: 'test/string.toml', env: .prefix_env, prefix: 'TEST_')!
assert conf2 == SingleConf{
some_string: 'env'
}
}
fn test_string_absent_no_default() { fn test_string_absent_no_default() {
conf := load<SingleConf>(default_path: 'test/empty.toml') or { return } conf := load<SingleConf>(default_path: 'test/empty.toml') or { return }
assert false assert false
} }
fn test_string_absent_no_default_env() {
conf := load<SingleConf>(default_path: 'test/string.toml', env: .env)!
assert conf == SingleConf{
some_string: 'env'
}
conf2 := load<SingleConf>(default_path: 'test/string.toml', env: .prefix_env, prefix: 'TEST_')!
assert conf2 == SingleConf{
some_string: 'env'
}
}
fn test_string_present_default() { fn test_string_present_default() {
conf := load<SingleConfDefault>(default_path: 'test/string.toml')! conf := load<SingleConfDefault>(default_path: 'test/string.toml')!
assert conf == SingleConfDefault{ assert conf == SingleConfDefault{
@ -27,9 +59,41 @@ fn test_string_present_default() {
} }
} }
fn test_string_present_default_env() {
conf := load<SingleConfDefault>(default_path: 'test/string.toml', env: .env)!
assert conf == SingleConfDefault{
some_string: 'env'
}
conf2 := load<SingleConfDefault>(
default_path: 'test/string.toml'
env: .prefix_env
prefix: 'TEST_'
)!
assert conf2 == SingleConfDefault{
some_string: 'env'
}
}
fn test_string_absent_default() { fn test_string_absent_default() {
conf := load<SingleConfDefault>(default_path: 'test/empty.toml')! conf := load<SingleConfDefault>(default_path: 'test/empty.toml')!
assert conf == SingleConfDefault{ assert conf == SingleConfDefault{
some_string: 'default' some_string: 'default'
} }
} }
fn test_string_absent_default_env() {
conf := load<SingleConfDefault>(default_path: 'test/empty.toml', env: .env)!
assert conf == SingleConfDefault{
some_string: 'env'
}
conf2 := load<SingleConfDefault>(
default_path: 'test/empty.toml'
env: .prefix_env
prefix: 'TEST_'
)!
assert conf2 == SingleConfDefault{
some_string: 'env'
}
}