use super::*;
use crate::services::lifecycle::templates::TemplateSources;
const BOTH: &str = "templates are written once: at the top level or under [auth.lys], not both";
const ECHO: &str = "name = \"echo\"\ncommand = \"/bin/sh\"\nargs = [\"-c\", \"true\"]\n\
subjects = [\"echo\"]\nstop_timeout_ms = 500\n";
fn templates(place: &str) -> Result<TemplateSources, Box<dyn std::error::Error>> {
#[derive(serde::Deserialize)]
struct Wrap {
templates: TemplateSources,
}
let wrapped: Wrap = toml::from_str(&format!("[[templates]]\n{place}"))?;
Ok(wrapped.templates)
}
fn lys_auth(templates: &str) -> Result<AuthConfig, Box<dyn std::error::Error>> {
Ok(toml::from_str(&format!(
"token = \"s3cr3t\"\n[lys]\nissuer = \"https://lys.test\"\nnetwork_id = \"n\"\n\
keys_path = \"/k\"\napp_credential_path = \"/c\"\n{templates}"
))?)
}
#[test]
fn an_open_exchange_with_top_level_templates_validates() -> Result<(), Box<dyn std::error::Error>> {
let mut config = sample_config()?;
config.auth = None;
config.cluster = None;
config.templates = templates(ECHO)?;
let message = config_validation_message(validate(&mut config, None));
assert_eq!(message, "", "an open exchange with [[templates]] validates");
assert_eq!(config.template_sources().len(), 1);
assert_eq!(config.templates_place(), "templates");
Ok(())
}
#[test]
fn templates_in_both_places_are_refused_by_the_exact_words()
-> Result<(), Box<dyn std::error::Error>> {
let mut config = sample_config()?;
config.auth = Some(lys_auth(&format!("[[lys.templates]]\n{ECHO}"))?);
config.templates = templates(ECHO)?;
let message = config_validation_message(validate(&mut config, None));
assert!(
message.lines().any(|line| line == BOTH),
"missing {BOTH:?} in {message:?}"
);
Ok(())
}
#[test]
fn a_gated_exchange_with_top_level_templates_alone_validates()
-> Result<(), Box<dyn std::error::Error>> {
let mut config = sample_config()?;
config.auth = Some(lys_auth("")?);
config.templates = templates(ECHO)?;
let message = config_validation_message(validate(&mut config, None));
assert_eq!(
message, "",
"a gated exchange with [[templates]] alone validates"
);
assert_eq!(config.template_sources().len(), 1);
assert_eq!(config.templates_place(), "templates");
Ok(())
}
#[test]
fn an_open_exchange_with_top_level_templates_needs_a_store()
-> Result<(), Box<dyn std::error::Error>> {
let mut config = sample_config()?;
config.auth = None;
config.cluster = None;
config.persistence_path = None;
config.templates = templates(ECHO)?;
let message = config_validation_message(validate(&mut config, None));
let expected = "templates: a service registry needs persistence_path to be kept";
assert!(
message.lines().any(|line| line == expected),
"missing {expected:?} in {message:?}"
);
Ok(())
}
#[test]
fn a_token_only_exchange_with_templates_is_told_where_services_are_placed()
-> Result<(), Box<dyn std::error::Error>> {
let mut config = sample_config()?;
config.auth = Some(toml::from_str("token = \"s3cr3t\"\n")?);
config.templates = templates(ECHO)?;
let message = config_validation_message(validate(&mut config, None));
let expected =
"templates: a gated exchange places services in Lys, write [auth.lys] or remove [auth]";
assert!(
message.lines().any(|line| line == expected),
"missing {expected:?} in {message:?}"
);
Ok(())
}
#[test]
fn the_lys_place_still_answers_when_the_top_level_is_empty()
-> Result<(), Box<dyn std::error::Error>> {
let mut config = sample_config()?;
config.auth = Some(lys_auth(&format!("[[lys.templates]]\n{ECHO}"))?);
let message = config_validation_message(validate(&mut config, None));
assert_eq!(message, "");
assert_eq!(config.template_sources().len(), 1);
assert_eq!(config.templates_place(), "auth.lys.templates");
Ok(())
}