use super::validate_templates;
use crate::config::types::ServerConfig;
fn config(templates: &str, persistence: bool) -> ServerConfig {
let persistence = if persistence {
"persistence_path = \"/tmp/liminal-templates-test\"\n"
} else {
""
};
let text = format!(
"listen_address = \"127.0.0.1:1\"\nhealth_listen_address = \"127.0.0.1:2\"\ndrain_timeout_ms = 5000\n\
routing_rules = []\n{persistence}\n[[channels]]\nname = \"a\"\ndurable = false\n\n\
[auth]\ntoken = \"t\"\n\n[auth.lys]\nissuer = \"https://lys.test\"\n\
network_id = \"n\"\nkeys_path = \"/k\"\napp_credential_path = \"/c\"\n\n{templates}"
);
toml::from_str(&text).unwrap_or_else(|error| unreachable!("fixture config parses: {error}"))
}
fn errors_of(config: &ServerConfig) -> Vec<String> {
let mut errors = Vec::new();
validate_templates(config, &mut errors);
errors
}
const SH: &str = "[[auth.lys.templates]]\nname = \"echo\"\ncommand = \"/bin/sh\"\n\
args = [\"-c\", \"true\"]\nsubjects = [\"echo\"]\nstop_timeout_ms = 500\n";
#[test]
fn config_reads_a_template_whole() {
let config = config(SH, true);
assert_eq!(errors_of(&config), Vec::<String>::new());
let lys = config.auth.as_ref().and_then(|auth| auth.lys.as_ref());
let template = lys.and_then(|lys| lys.templates.first());
assert_eq!(template.map(|t| t.name.as_str()), Some("echo"));
assert_eq!(template.map(|t| t.args.len()), Some(2));
assert_eq!(template.map(|t| t.stop_timeout_ms), Some(500));
}
#[test]
fn config_refuses_a_relative_template_command() {
let errors = errors_of(&config(&SH.replace("/bin/sh", "sh"), true));
assert!(
errors
.iter()
.any(|e| e.contains("'echo'") && e.contains("absolute path")),
"{errors:?}"
);
}
#[test]
fn config_refuses_a_command_that_is_not_executable() {
let errors = errors_of(&config(&SH.replace("/bin/sh", "/etc/hosts"), true));
assert!(
errors.iter().any(|e| e.contains("not an executable file")),
"{errors:?}"
);
}
#[test]
fn config_refuses_a_template_without_subjects() {
let errors = errors_of(&config(&SH.replace("[\"echo\"]", "[]"), true));
assert!(
errors.iter().any(|e| e.contains("at least one subject")),
"{errors:?}"
);
}
#[test]
fn config_refuses_two_templates_sharing_a_subject() {
let second = SH.replace("name = \"echo\"", "name = \"other\"");
let errors = errors_of(&config(&format!("{SH}\n{second}"), true));
assert!(
errors
.iter()
.any(|e| e.contains("'other'") && e.contains("another template's")),
"{errors:?}"
);
}
#[test]
fn config_refuses_templates_without_a_registry_path() {
let errors = errors_of(&config(SH, false));
assert!(
errors.iter().any(|e| e.contains("persistence_path")),
"{errors:?}"
);
}
#[test]
fn config_refuses_a_zero_stop_timeout_and_a_wildcard_subject() {
let bad = SH
.replace("stop_timeout_ms = 500", "stop_timeout_ms = 0")
.replace("[\"echo\"]", "[\"echo.*\"]");
let errors = errors_of(&config(&bad, true));
assert!(
errors.iter().any(|e| e.contains("stop_timeout_ms")),
"{errors:?}"
);
assert!(
errors.iter().any(|e| e.contains("no wildcard")),
"{errors:?}"
);
}