nim-confutils/tests/test_config_file.nim

211 lines
6.6 KiB
Nim

# nim-confutils
# Copyright (c) 2020-2024 Status Research & Development GmbH
# Licensed and distributed under either of
# * MIT license: [LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT
# * Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
# at your option. This file may not be copied, modified, or distributed except according to those terms.
import
std/[options, os],
unittest2,
stew/byteutils, ../confutils,
../confutils/[std/net]
import
toml_serialization, json_serialization,
../confutils/winreg/winreg_serialization
type
ValidatorPrivKey = object
field_a: int
field_b: string
GraffitiBytes = array[16, byte]
VCStartUpCmd = enum
VCNoCommand
`import`
ValidatorKeyPath = TypedInputFile[ValidatorPrivKey, Txt, "privkey"]
TestConf* = object
configFile* {.
desc: "Loads the configuration from a config file"
name: "config-file" }: Option[InputFile]
logLevel* {.
defaultValue: "DEBUG"
desc: "Sets the log level."
name: "log-level" }: string
logFile* {.
desc: "Specifies a path for the written Json log file"
name: "log-file" }: Option[OutFile]
dataDir* {.
defaultValue: config.defaultDataDir()
desc: "The directory where nimbus will store all blockchain data"
abbr: "d"
name: "data-dir" }: OutDir
nonInteractive* {.
desc: "Do not display interative prompts. Quit on missing configuration"
name: "non-interactive" }: bool
validators* {.
required
desc: "Attach a validator by supplying a keystore path"
abbr: "v"
name: "validator" }: seq[ValidatorKeyPath]
validatorsDirFlag* {.
desc: "A directory containing validator keystores"
name: "validators-dir" }: Option[InputDir]
secretsDirFlag* {.
desc: "A directory containing validator keystore passwords"
name: "secrets-dir" }: Option[InputDir]
case cmd* {.
command
defaultValue: VCNoCommand }: VCStartUpCmd
of VCNoCommand:
graffiti* {.
desc: "The graffiti value that will appear in proposed blocks. " &
"You can use a 0x-prefixed hex encoded string to specify raw bytes."
name: "graffiti" }: Option[GraffitiBytes]
stopAtEpoch* {.
defaultValue: 0
desc: "A positive epoch selects the epoch at which to stop"
name: "stop-at-epoch" }: uint64
rpcPort* {.
defaultValue: defaultEth2RpcPort
desc: "HTTP port of the server to connect to for RPC - for the validator duties in the pull model"
name: "rpc-port" }: Port
rpcAddress* {.
defaultValue: defaultAdminListenAddress(config)
desc: "Address of the server to connect to for RPC - for the validator duties in the pull model"
name: "rpc-address" }: IpAddress
restAddress* {.
defaultValue: defaultAdminListenAddress(config)
desc: "Address of the server to connect to for RPC - for the validator duties in the pull model"
name: "rest-address" }: IpAddress
retryDelay* {.
defaultValue: 10
desc: "Delay in seconds between retries after unsuccessful attempts to connect to a beacon node"
name: "retry-delay" }: int
of `import`:
blocksFile* {.
argument
desc: "Import RLP encoded block(s) from a file, validate, write to database and quit"
defaultValue: ""
name: "blocks-file" }: InputFile
`type`* {.
desc: "Test nnkAccQuoted of public sub command entry"
defaultValue: ""
name: "import-type" }: string
`seq` {.
desc: "Test nnkAccQuoted of private sub command entry"
defaultValue: ""
name: "import-seq" }: string
func defaultDataDir(conf: TestConf): string =
discard
func parseCmdArg*(T: type GraffitiBytes, input: string): T
{.raises: [ValueError, Defect].} =
discard
func completeCmdArg*(T: type GraffitiBytes, input: string): seq[string] =
@[]
func defaultAdminListenAddress*(conf: TestConf): IpAddress =
(static parseIpAddress("127.0.0.1"))
const
defaultEth2TcpPort* = 9000
defaultEth2RpcPort* = 9090
const
confPathCurrUser = "tests" / "config_files" / "current_user"
confPathSystemWide = "tests" / "config_files" / "system_wide"
# User might also need to extend the serializer capability
# for each of the registered formats.
# This is especially true for distinct types and some special types
# not covered by the standard implementation
proc readValue(r: var TomlReader,
value: var (InputFile | InputDir | OutFile | OutDir | ValidatorKeyPath)) =
type T = type value
value = T r.parseAsString()
proc readValue(r: var TomlReader, value: var IpAddress) =
try:
value = parseIpAddress(r.parseAsString())
except ValueError as ex:
raise newException(SerializationError, ex.msg)
proc readValue(r: var TomlReader, value: var Port) =
value = r.parseInt(int).Port
proc readValue(r: var TomlReader, value: var GraffitiBytes) =
try:
value = hexToByteArray[value.len](r.parseAsString())
except ValueError as ex:
raise newException(SerializationError, ex.msg)
proc readValue(r: var WinregReader,
value: var (InputFile | InputDir | OutFile | OutDir | ValidatorKeyPath)) =
type T = type value
value = r.readValue(string).T
proc readValue(r: var WinregReader, value: var IpAddress) {.used.} =
value = parseIpAddress(r.readValue(string))
proc readValue(r: var WinregReader, value: var Port) {.used.} =
value = r.readValue(int).Port
proc readValue(r: var WinregReader, value: var GraffitiBytes) {.used.} =
value = hexToByteArray[value.len](r.readValue(string))
proc testConfigFile() =
suite "config file test suite":
putEnv("PREFIX_DATA_DIR", "ENV VAR DATADIR")
test "basic config file":
let conf = TestConf.load(
envVarsPrefix="prefix",
secondarySources = proc (
config: TestConf, sources: ref SecondarySources
) {.raises: [ConfigurationError].} =
if config.configFile.isSome:
sources.addConfigFile(Toml, config.configFile.get)
else:
sources.addConfigFile(Toml, InputFile(confPathCurrUser / "testVendor" / "testApp.toml"))
sources.addConfigFile(Toml, InputFile(confPathSystemWide / "testVendor" / "testApp.toml"))
)
# dataDir is in env var
check conf.dataDir.string == "ENV VAR DATADIR"
# logFile is in current user config file
check conf.logFile.isSome()
check conf.logFile.get().string == "TOML CU LOGFILE"
# logLevel and rpcPort are in system wide config file
check conf.logLevel == "TOML SW DEBUG"
check conf.rpcPort.int == 1235
testConfigFile()