agent: config: Use rstest for unit tests

Use rstest for unit test rather than TestData arrays where
possible to make the code more compact, easier to read
and open the possibility to enhance test cases with a
description more easily.

Signed-off-by: stevenhorsman <steven@uk.ibm.com>
This commit is contained in:
stevenhorsman 2024-10-10 17:07:14 +01:00
parent 69509eff33
commit 4adb454ed0

View File

@ -89,7 +89,7 @@ pub enum GuestComponentsFeatures {
Resource, Resource,
} }
#[derive(Clone, Copy, Debug, Default, Display, Deserialize, EnumString, PartialEq)] #[derive(Clone, Copy, Debug, Default, Display, Deserialize, EnumString, PartialEq, Eq)]
/// Attestation-related processes that we want to spawn as children of the agent /// Attestation-related processes that we want to spawn as children of the agent
#[strum(serialize_all = "kebab-case")] #[strum(serialize_all = "kebab-case")]
#[serde(rename_all = "kebab-case")] #[serde(rename_all = "kebab-case")]
@ -630,6 +630,7 @@ mod tests {
use super::*; use super::*;
use anyhow::anyhow; use anyhow::anyhow;
use rstest::*;
use serial_test::serial; use serial_test::serial;
use std::fs::File; use std::fs::File;
use std::io::Write; use std::io::Write;
@ -1327,562 +1328,193 @@ mod tests {
assert_eq!(expected.tracing, config.tracing); assert_eq!(expected.tracing, config.tracing);
} }
#[test] #[rstest]
fn test_logrus_to_slog_level() { #[case("", Err(anyhow!(ERR_INVALID_LOG_LEVEL)))]
#[derive(Debug)] #[case("foo", Err(anyhow!(ERR_INVALID_LOG_LEVEL)))]
struct TestData<'a> { #[case("debugging", Err(anyhow!(ERR_INVALID_LOG_LEVEL)))]
logrus_level: &'a str, #[case("xdebug", Err(anyhow!(ERR_INVALID_LOG_LEVEL)))]
result: Result<slog::Level>, #[case("trace", Ok(slog::Level::Trace))]
} #[case("debug", Ok(slog::Level::Debug))]
#[case("info", Ok(slog::Level::Info))]
let tests = &[ #[case("warn", Ok(slog::Level::Warning))]
TestData { #[case("warning", Ok(slog::Level::Warning))]
logrus_level: "", #[case("error", Ok(slog::Level::Error))]
result: Err(anyhow!(ERR_INVALID_LOG_LEVEL)), #[case("critical", Ok(slog::Level::Critical))]
}, #[case("fatal", Ok(slog::Level::Critical))]
TestData { #[case("panic", Ok(slog::Level::Critical))]
logrus_level: "foo", fn test_logrus_to_slog_level(#[case] input: &str, #[case] expected: Result<slog::Level>) {
result: Err(anyhow!(ERR_INVALID_LOG_LEVEL)), let result = logrus_to_slog_level(input);
}, let msg = format!("expected: {:?}, result: {:?}", expected, result);
TestData { assert_result!(expected, result, msg);
logrus_level: "debugging",
result: Err(anyhow!(ERR_INVALID_LOG_LEVEL)),
},
TestData {
logrus_level: "xdebug",
result: Err(anyhow!(ERR_INVALID_LOG_LEVEL)),
},
TestData {
logrus_level: "trace",
result: Ok(slog::Level::Trace),
},
TestData {
logrus_level: "debug",
result: Ok(slog::Level::Debug),
},
TestData {
logrus_level: "info",
result: Ok(slog::Level::Info),
},
TestData {
logrus_level: "warn",
result: Ok(slog::Level::Warning),
},
TestData {
logrus_level: "warning",
result: Ok(slog::Level::Warning),
},
TestData {
logrus_level: "error",
result: Ok(slog::Level::Error),
},
TestData {
logrus_level: "critical",
result: Ok(slog::Level::Critical),
},
TestData {
logrus_level: "fatal",
result: Ok(slog::Level::Critical),
},
TestData {
logrus_level: "panic",
result: Ok(slog::Level::Critical),
},
];
for (i, d) in tests.iter().enumerate() {
let msg = format!("test[{}]: {:?}", i, d);
let result = logrus_to_slog_level(d.logrus_level);
let msg = format!("{}: result: {:?}", msg, result);
assert_result!(d.result, result, msg);
}
} }
#[test] #[rstest]
fn test_get_log_level() { #[case("",Err(anyhow!(ERR_INVALID_LOG_LEVEL_PARAM)))]
#[derive(Debug)] #[case("=",Err(anyhow!(ERR_INVALID_LOG_LEVEL_KEY)))]
struct TestData<'a> { #[case("x=",Err(anyhow!(ERR_INVALID_LOG_LEVEL_KEY)))]
param: &'a str, #[case("=y",Err(anyhow!(ERR_INVALID_LOG_LEVEL_KEY)))]
result: Result<slog::Level>, #[case("==",Err(anyhow!(ERR_INVALID_LOG_LEVEL_PARAM)))]
} #[case("= =",Err(anyhow!(ERR_INVALID_LOG_LEVEL_PARAM)))]
#[case("x=y",Err(anyhow!(ERR_INVALID_LOG_LEVEL_KEY)))]
let tests = &[ #[case("agent=debug",Err(anyhow!(ERR_INVALID_LOG_LEVEL_KEY)))]
TestData { #[case("agent.logg=debug",Err(anyhow!(ERR_INVALID_LOG_LEVEL_KEY)))]
param: "", #[case("agent.log=trace", Ok(slog::Level::Trace))]
result: Err(anyhow!(ERR_INVALID_LOG_LEVEL_PARAM)), #[case("agent.log=debug", Ok(slog::Level::Debug))]
}, #[case("agent.log=info", Ok(slog::Level::Info))]
TestData { #[case("agent.log=warn", Ok(slog::Level::Warning))]
param: "=", #[case("agent.log=warning", Ok(slog::Level::Warning))]
result: Err(anyhow!(ERR_INVALID_LOG_LEVEL_KEY)), #[case("agent.log=error", Ok(slog::Level::Error))]
}, #[case("agent.log=critical", Ok(slog::Level::Critical))]
TestData { #[case("agent.log=fatal", Ok(slog::Level::Critical))]
param: "x=", #[case("agent.log=panic", Ok(slog::Level::Critical))]
result: Err(anyhow!(ERR_INVALID_LOG_LEVEL_KEY)), fn test_get_log_level(#[case] input: &str, #[case] expected: Result<slog::Level>) {
}, let result = get_log_level(input);
TestData { let msg = format!("expected: {:?}, result: {:?}", expected, result);
param: "=y", assert_result!(expected, result, msg);
result: Err(anyhow!(ERR_INVALID_LOG_LEVEL_KEY)),
},
TestData {
param: "==",
result: Err(anyhow!(ERR_INVALID_LOG_LEVEL_PARAM)),
},
TestData {
param: "= =",
result: Err(anyhow!(ERR_INVALID_LOG_LEVEL_PARAM)),
},
TestData {
param: "x=y",
result: Err(anyhow!(ERR_INVALID_LOG_LEVEL_KEY)),
},
TestData {
param: "agent=debug",
result: Err(anyhow!(ERR_INVALID_LOG_LEVEL_KEY)),
},
TestData {
param: "agent.logg=debug",
result: Err(anyhow!(ERR_INVALID_LOG_LEVEL_KEY)),
},
TestData {
param: "agent.log=trace",
result: Ok(slog::Level::Trace),
},
TestData {
param: "agent.log=debug",
result: Ok(slog::Level::Debug),
},
TestData {
param: "agent.log=info",
result: Ok(slog::Level::Info),
},
TestData {
param: "agent.log=warn",
result: Ok(slog::Level::Warning),
},
TestData {
param: "agent.log=warning",
result: Ok(slog::Level::Warning),
},
TestData {
param: "agent.log=error",
result: Ok(slog::Level::Error),
},
TestData {
param: "agent.log=critical",
result: Ok(slog::Level::Critical),
},
TestData {
param: "agent.log=fatal",
result: Ok(slog::Level::Critical),
},
TestData {
param: "agent.log=panic",
result: Ok(slog::Level::Critical),
},
];
for (i, d) in tests.iter().enumerate() {
let msg = format!("test[{}]: {:?}", i, d);
let result = get_log_level(d.param);
let msg = format!("{}: result: {:?}", msg, result);
assert_result!(d.result, result, msg);
}
} }
#[test] #[rstest]
fn test_get_timeout() { #[case("", Err(anyhow!(ERR_INVALID_TIMEOUT)))]
#[derive(Debug)] #[case("agent.hotplug_timeout", Err(anyhow!(ERR_INVALID_TIMEOUT)))]
struct TestData<'a> { #[case("foo=bar", Err(anyhow!(ERR_INVALID_TIMEOUT_KEY)))]
param: &'a str, #[case("agent.hotplug_timeot=1", Err(anyhow!(ERR_INVALID_TIMEOUT_KEY)))]
result: Result<time::Duration>, #[case("agent.hotplug_timeout=1", Ok(time::Duration::from_secs(1)))]
} #[case("agent.hotplug_timeout=3", Ok(time::Duration::from_secs(3)))]
#[case("agent.hotplug_timeout=3600", Ok(time::Duration::from_secs(3600)))]
let tests = &[ #[case("agent.hotplug_timeout=0", Ok(time::Duration::from_secs(0)))]
TestData { #[case("agent.hotplug_timeout=-1", Err(anyhow!(
param: "", "unable to parse timeout
result: Err(anyhow!(ERR_INVALID_TIMEOUT)),
},
TestData {
param: "agent.hotplug_timeout",
result: Err(anyhow!(ERR_INVALID_TIMEOUT)),
},
TestData {
param: "foo=bar",
result: Err(anyhow!(ERR_INVALID_TIMEOUT_KEY)),
},
TestData {
param: "agent.hotplug_timeot=1",
result: Err(anyhow!(ERR_INVALID_TIMEOUT_KEY)),
},
TestData {
param: "agent.chd_api_timeout=1",
result: Err(anyhow!(ERR_INVALID_TIMEOUT_KEY)),
},
TestData {
param: "agent.hotplug_timeout=1",
result: Ok(time::Duration::from_secs(1)),
},
TestData {
param: "agent.hotplug_timeout=3",
result: Ok(time::Duration::from_secs(3)),
},
TestData {
param: "agent.hotplug_timeout=3600",
result: Ok(time::Duration::from_secs(3600)),
},
TestData {
param: "agent.cdh_api_timeout=600",
result: Ok(time::Duration::from_secs(600)),
},
TestData {
param: "agent.hotplug_timeout=0",
result: Ok(time::Duration::from_secs(0)),
},
TestData {
param: "agent.hotplug_timeout=-1",
result: Err(anyhow!(
"unable to parse timeout
Caused by: Caused by:
invalid digit found in string" invalid digit found in string"
)), )))]
}, #[case("agent.hotplug_timeout=4jbsdja", Err(anyhow!(
TestData { "unable to parse timeout
param: "agent.hotplug_timeout=4jbsdja",
result: Err(anyhow!(
"unable to parse timeout
Caused by: Caused by:
invalid digit found in string" invalid digit found in string"
)), )))]
}, #[case("agent.hotplug_timeout=foo", Err(anyhow!(
TestData { "unable to parse timeout
param: "agent.hotplug_timeout=foo",
result: Err(anyhow!(
"unable to parse timeout
Caused by: Caused by:
invalid digit found in string" invalid digit found in string"
)), )))]
}, #[case("agent.hotplug_timeout=j", Err(anyhow!(
TestData { "unable to parse timeout
param: "agent.hotplug_timeout=j",
result: Err(anyhow!(
"unable to parse timeout
Caused by: Caused by:
invalid digit found in string" invalid digit found in string"
)), )))]
}, #[case("agent.chd_api_timeout=1", Err(anyhow!(ERR_INVALID_TIMEOUT_KEY)))]
]; #[case("agent.cdh_api_timeout=600", Ok(time::Duration::from_secs(600)))]
fn test_timeout(#[case] param: &str, #[case] expected: Result<time::Duration>) {
for (i, d) in tests.iter().enumerate() { let result = get_timeout(param);
let msg = format!("test[{}]: {:?}", i, d); let msg = format!("expected: {:?}, result: {:?}", expected, result);
assert_result!(expected, result, msg);
let result = get_timeout(d.param);
let msg = format!("{}: result: {:?}", msg, result);
assert_result!(d.result, result, msg);
}
} }
#[test] #[rstest]
fn test_get_container_pipe_size() { #[case("", Err(anyhow!(ERR_INVALID_CONTAINER_PIPE_SIZE)))]
#[derive(Debug)] #[case("agent.container_pipe_size", Err(anyhow!(ERR_INVALID_CONTAINER_PIPE_SIZE)))]
struct TestData<'a> { #[case("foo=bar", Err(anyhow!(ERR_INVALID_CONTAINER_PIPE_SIZE_KEY)))]
param: &'a str, #[case("agent.container_pip_siz=1", Err(anyhow!(ERR_INVALID_CONTAINER_PIPE_SIZE_KEY)))]
result: Result<i32>, #[case("agent.container_pipe_size=1", Ok(1))]
} #[case("agent.container_pipe_size=3", Ok(3))]
#[case("agent.container_pipe_size=2097152", Ok(2097152))]
let tests = &[ #[case("agent.container_pipe_size=0", Ok(0))]
TestData { #[case("agent.container_pipe_size=-1", Err(anyhow!(ERR_INVALID_CONTAINER_PIPE_NEGATIVE)))]
param: "", #[case("agent.container_pipe_size=foobar", Err(anyhow!(
result: Err(anyhow!(ERR_INVALID_CONTAINER_PIPE_SIZE)), "unable to parse container pipe size
},
TestData {
param: "agent.container_pipe_size",
result: Err(anyhow!(ERR_INVALID_CONTAINER_PIPE_SIZE)),
},
TestData {
param: "foo=bar",
result: Err(anyhow!(ERR_INVALID_CONTAINER_PIPE_SIZE_KEY)),
},
TestData {
param: "agent.container_pip_siz=1",
result: Err(anyhow!(ERR_INVALID_CONTAINER_PIPE_SIZE_KEY)),
},
TestData {
param: "agent.container_pipe_size=1",
result: Ok(1),
},
TestData {
param: "agent.container_pipe_size=3",
result: Ok(3),
},
TestData {
param: "agent.container_pipe_size=2097152",
result: Ok(2097152),
},
TestData {
param: "agent.container_pipe_size=0",
result: Ok(0),
},
TestData {
param: "agent.container_pipe_size=-1",
result: Err(anyhow!(ERR_INVALID_CONTAINER_PIPE_NEGATIVE)),
},
TestData {
param: "agent.container_pipe_size=foobar",
result: Err(anyhow!(
"unable to parse container pipe size
Caused by: Caused by:
invalid digit found in string" invalid digit found in string"
)), )))]
}, #[case("agent.container_pipe_size=j", Err(anyhow!(
TestData { "unable to parse container pipe size
param: "agent.container_pipe_size=j",
result: Err(anyhow!(
"unable to parse container pipe size
Caused by: Caused by:
invalid digit found in string", invalid digit found in string",
)), )))]
}, #[case("agent.container_pipe_size=4jbsdja", Err(anyhow!(
TestData { "unable to parse container pipe size
param: "agent.container_pipe_size=4jbsdja",
result: Err(anyhow!(
"unable to parse container pipe size
Caused by: Caused by:
invalid digit found in string" invalid digit found in string"
)), )))]
}, #[case("agent.container_pipe_size=4294967296", Err(anyhow!(
TestData { "unable to parse container pipe size
param: "agent.container_pipe_size=4294967296",
result: Err(anyhow!(
"unable to parse container pipe size
Caused by: Caused by:
number too large to fit in target type" number too large to fit in target type"
)), )))]
}, fn test_get_container_pipe_size(#[case] param: &str, #[case] expected: Result<i32>) {
]; let result = get_container_pipe_size(param);
let msg = format!("expected: {:?}, result: {:?}", expected, result);
for (i, d) in tests.iter().enumerate() { assert_result!(expected, result, msg);
let msg = format!("test[{}]: {:?}", i, d);
let result = get_container_pipe_size(d.param);
let msg = format!("{}: result: {:?}", msg, result);
assert_result!(d.result, result, msg);
}
} }
#[test] #[rstest]
fn test_get_string_value() { #[case("", Err(anyhow!(ERR_INVALID_GET_VALUE_PARAM)))]
#[derive(Debug)] #[case("=", Err(anyhow!(ERR_INVALID_GET_VALUE_NO_NAME)))]
struct TestData<'a> { #[case("==", Err(anyhow!(ERR_INVALID_GET_VALUE_NO_NAME)))]
param: &'a str, #[case("x=", Err(anyhow!(ERR_INVALID_GET_VALUE_NO_VALUE)))]
result: Result<String>, #[case("x==", Ok("=".into()))]
} #[case("x===", Ok("==".into()))]
#[case("x==x", Ok("=x".into()))]
let tests = &[ #[case("x=x", Ok("x".into()))]
TestData { #[case("x=x=", Ok("x=".into()))]
param: "", #[case("x=x=x", Ok("x=x".into()))]
result: Err(anyhow!(ERR_INVALID_GET_VALUE_PARAM)), #[case("foo=bar", Ok("bar".into()))]
}, #[case("x= =", Ok(" =".into()))]
TestData { #[case("x= =", Ok(" =".into()))]
param: "=", #[case("x= = ", Ok(" = ".into()))]
result: Err(anyhow!(ERR_INVALID_GET_VALUE_NO_NAME)), fn test_get_string_value(#[case] param: &str, #[case] expected: Result<String>) {
}, let result = get_string_value(param);
TestData { let msg = format!("expected: {:?}, result: {:?}", expected, result);
param: "==", assert_result!(expected, result, msg);
result: Err(anyhow!(ERR_INVALID_GET_VALUE_NO_NAME)),
},
TestData {
param: "x=",
result: Err(anyhow!(ERR_INVALID_GET_VALUE_NO_VALUE)),
},
TestData {
param: "x==",
result: Ok("=".into()),
},
TestData {
param: "x===",
result: Ok("==".into()),
},
TestData {
param: "x==x",
result: Ok("=x".into()),
},
TestData {
param: "x=x",
result: Ok("x".into()),
},
TestData {
param: "x=x=",
result: Ok("x=".into()),
},
TestData {
param: "x=x=x",
result: Ok("x=x".into()),
},
TestData {
param: "foo=bar",
result: Ok("bar".into()),
},
TestData {
param: "x= =",
result: Ok(" =".into()),
},
TestData {
param: "x= =",
result: Ok(" =".into()),
},
TestData {
param: "x= = ",
result: Ok(" = ".into()),
},
];
for (i, d) in tests.iter().enumerate() {
let msg = format!("test[{}]: {:?}", i, d);
let result = get_string_value(d.param);
let msg = format!("{}: result: {:?}", msg, result);
assert_result!(d.result, result, msg);
}
} }
#[test] #[rstest]
fn test_get_guest_components_features_value() { #[case("", Err(anyhow!(ERR_INVALID_GET_VALUE_PARAM)))]
#[derive(Debug)] #[case("=", Err(anyhow!(ERR_INVALID_GET_VALUE_NO_NAME)))]
struct TestData<'a> { #[case("==", Err(anyhow!(ERR_INVALID_GET_VALUE_NO_NAME)))]
param: &'a str, #[case("x=all", Ok(GuestComponentsFeatures::All))]
result: Result<GuestComponentsFeatures>, #[case("x=attestation", Ok(GuestComponentsFeatures::Attestation))]
} #[case("x=resource", Ok(GuestComponentsFeatures::Resource))]
#[case("x===", Err(anyhow!(ERR_INVALID_GUEST_COMPONENTS_REST_API_VALUE)))]
let tests = &[ #[case("x==x", Err(anyhow!(ERR_INVALID_GUEST_COMPONENTS_REST_API_VALUE)))]
TestData { #[case("x=x", Err(anyhow!(ERR_INVALID_GUEST_COMPONENTS_REST_API_VALUE)))]
param: "", fn test_get_guest_components_features_value(
result: Err(anyhow!(ERR_INVALID_GET_VALUE_PARAM)), #[case] input: &str,
}, #[case] expected: Result<GuestComponentsFeatures>,
TestData { ) {
param: "=", let result = get_guest_components_features_value(input);
result: Err(anyhow!(ERR_INVALID_GET_VALUE_NO_NAME)), let msg = format!("expected: {:?}, result: {:?}", expected, result);
}, assert_result!(expected, result, msg);
TestData {
param: "==",
result: Err(anyhow!(ERR_INVALID_GET_VALUE_NO_NAME)),
},
TestData {
param: "x=all",
result: Ok(GuestComponentsFeatures::All),
},
TestData {
param: "x=attestation",
result: Ok(GuestComponentsFeatures::Attestation),
},
TestData {
param: "x=resource",
result: Ok(GuestComponentsFeatures::Resource),
},
TestData {
param: "x===",
result: Err(anyhow!(ERR_INVALID_GUEST_COMPONENTS_REST_API_VALUE)),
},
TestData {
param: "x==x",
result: Err(anyhow!(ERR_INVALID_GUEST_COMPONENTS_REST_API_VALUE)),
},
TestData {
param: "x=x",
result: Err(anyhow!(ERR_INVALID_GUEST_COMPONENTS_REST_API_VALUE)),
},
];
for (i, d) in tests.iter().enumerate() {
let msg = format!("test[{}]: {:?}", i, d);
let result = get_guest_components_features_value(d.param);
let msg = format!("{}: result: {:?}", msg, result);
assert_result!(d.result, result, msg);
}
} }
#[test] #[rstest]
fn test_get_guest_components_procs_value() { #[case("", Err(anyhow!(ERR_INVALID_GET_VALUE_PARAM)))]
#[derive(Debug)] #[case("=", Err(anyhow!(ERR_INVALID_GET_VALUE_NO_NAME)))]
struct TestData<'a> { #[case("==", Err(anyhow!(ERR_INVALID_GET_VALUE_NO_NAME)))]
param: &'a str, #[case("x=attestation-agent", Ok(GuestComponentsProcs::AttestationAgent))]
result: Result<GuestComponentsProcs>, #[case(
} "x=confidential-data-hub",
Ok(GuestComponentsProcs::ConfidentialDataHub)
let tests = &[ )]
TestData { #[case("x=none", Ok(GuestComponentsProcs::None))]
param: "", #[case("x=api-server-rest", Ok(GuestComponentsProcs::ApiServerRest))]
result: Err(anyhow!(ERR_INVALID_GET_VALUE_PARAM)), #[case("x===", Err(anyhow!(ERR_INVALID_GUEST_COMPONENTS_PROCS_VALUE)))]
}, #[case("x==x", Err(anyhow!(ERR_INVALID_GUEST_COMPONENTS_PROCS_VALUE)))]
TestData { #[case("x=x", Err(anyhow!(ERR_INVALID_GUEST_COMPONENTS_PROCS_VALUE)))]
param: "=", fn test_get_guest_components_procs_value(
result: Err(anyhow!(ERR_INVALID_GET_VALUE_NO_NAME)), #[case] param: &str,
}, #[case] expected: Result<GuestComponentsProcs>,
TestData { ) {
param: "==", let result = get_guest_components_procs_value(param);
result: Err(anyhow!(ERR_INVALID_GET_VALUE_NO_NAME)), let msg = format!("expected: {:?}, result: {:?}", expected, result);
}, assert_result!(expected, result, msg);
TestData {
param: "x=attestation-agent",
result: Ok(GuestComponentsProcs::AttestationAgent),
},
TestData {
param: "x=confidential-data-hub",
result: Ok(GuestComponentsProcs::ConfidentialDataHub),
},
TestData {
param: "x=none",
result: Ok(GuestComponentsProcs::None),
},
TestData {
param: "x=api-server-rest",
result: Ok(GuestComponentsProcs::ApiServerRest),
},
TestData {
param: "x===",
result: Err(anyhow!(ERR_INVALID_GUEST_COMPONENTS_PROCS_VALUE)),
},
TestData {
param: "x==x",
result: Err(anyhow!(ERR_INVALID_GUEST_COMPONENTS_PROCS_VALUE)),
},
TestData {
param: "x=x",
result: Err(anyhow!(ERR_INVALID_GUEST_COMPONENTS_PROCS_VALUE)),
},
];
for (i, d) in tests.iter().enumerate() {
let msg = format!("test[{}]: {:?}", i, d);
let result = get_guest_components_procs_value(d.param);
let msg = format!("{}: result: {:?}", msg, result);
assert_result!(d.result, result, msg);
}
} }
#[test] #[test]