Files
boxmaker/crates/memoryd/tests/honcho_reasons.rs
T

339 lines
12 KiB
Rust

//! Every reason an invalid `[honcho]` section, `[[honcho.read]]` or Honcho key is refused with at
//! start (M5c spec, sections 6 and 10): each invalid section has one problem and one exact reason.
//! Do not edit.
use memoryd::config::Config;
use memoryd::honcho::config::{HonchoConfig, is_name};
/// The smallest valid section: the six keys, no read workspace, no key.
const SECTION: &str = "[honcho]
url = \"http://rift.scylla-hammerhead.ts.net:8000\"
agent = \"boxmaker0\"
owner = \"kyle\"
trusted = \"boxmaker0-trusted\"
untrusted = \"boxmaker0-untrusted\"
fleet = \"fleet\"
";
const URL_REASON: &str =
"[honcho] url must be http:// or https://, a host and an optional port, and nothing else";
/// `SECTION` with `from` (found exactly once) replaced by `to`, then `extra`.
fn with(from: &str, to: &str, extra: &str) -> String {
assert_eq!(SECTION.matches(from).count(), 1, "{from}");
format!("{}{extra}", SECTION.replace(from, to))
}
/// The checked section of `text`, which must parse.
fn checked(text: &str) -> Result<Option<HonchoConfig>, String> {
let config = Config::parse(text).unwrap_or_else(|e| panic!("{text}\n{e}"));
config.honcho_config()
}
fn reason(text: &str) -> String {
match checked(text) {
Ok(ok) => panic!("accepted:\n{text}\n{ok:?}"),
Err(why) => why,
}
}
fn name_reason(table: &str, key: &str, name: &str) -> String {
format!("{table} {key} {name:?} is not a Honcho name: 1 to 512 of a-z, A-Z, 0-9, _ and -")
}
#[test]
fn honcho_names_at_the_edges() {
let longest = "a".repeat(512);
for name in [
"a",
"Z",
"0",
"_",
"-",
"boxmaker0-trusted",
"A_b-9",
longest.as_str(),
] {
assert!(is_name(name), "{name}");
}
let too_long = "a".repeat(513);
for name in [
"",
too_long.as_str(),
"boxmaker.0",
"box maker",
"a/b",
"a:b",
"kyle@home",
"caf\u{e9}",
"tab\t",
"new\nline",
"\u{0660}",
] {
assert!(!is_name(name), "{name:?}");
}
// The longest name is accepted in every place a name goes.
let text = with("fleet = \"fleet\"", &format!("fleet = \"{longest}\""), "");
assert_eq!(checked(&text).unwrap().unwrap().fleet, longest);
let text = with("owner = \"kyle\"", &format!("owner = \"{too_long}\""), "");
assert_eq!(reason(&text), name_reason("[honcho]", "owner", &too_long));
}
#[test]
fn urls_that_are_accepted() {
for url in [
"http://rift.scylla-hammerhead.ts.net:8000",
"https://rift.scylla-hammerhead.ts.net",
"http://127.0.0.1:1",
"http://honcho:65535",
"https://Rift-2.Example.org:443",
"http://h",
] {
let text = with(
"url = \"http://rift.scylla-hammerhead.ts.net:8000\"",
&format!("url = \"{url}\""),
"",
);
assert_eq!(checked(&text).unwrap().unwrap().url, url);
}
}
#[test]
fn urls_that_are_refused_and_the_reason_never_quotes_them() {
for url in [
"",
"rift:8000",
"ftp://rift:8000",
"HTTP://rift:8000",
"http:/rift",
"http://",
"https://",
"http://:8000",
"http://rift:",
"http://rift:0",
"http://rift:65536",
"http://rift:+80",
"http://rift:80:90",
"http://rift:8000/",
"http://rift:8000/v3",
"http://rift/",
"http://kyle:hunter2@rift:8000",
"http://kyle@rift:8000",
"http://kyle@rift",
"http://rift?x=1",
"http://rift#top",
"http://ri ft",
" http://rift",
"http://rift_1",
"http://[::1]:8000",
] {
let text = with(
"url = \"http://rift.scylla-hammerhead.ts.net:8000\"",
&format!("url = \"{url}\""),
"",
);
assert_eq!(reason(&text), URL_REASON, "{url}");
}
}
#[test]
fn each_invalid_section_gives_its_reason() {
let read = |agent: &str, workspace: &str| {
format!("[[honcho.read]]\nagent = \"{agent}\"\nworkspace = \"{workspace}\"\n")
};
let cases: Vec<(String, String)> = vec![
(
with("agent = \"boxmaker0\"", "agent = \"boxmaker.0\"", ""),
name_reason("[honcho]", "agent", "boxmaker.0"),
),
(
with("owner = \"kyle\"", "owner = \"\"", ""),
name_reason("[honcho]", "owner", ""),
),
(
with(
"trusted = \"boxmaker0-trusted\"",
"trusted = \"boxmaker0 trusted\"",
"",
),
name_reason("[honcho]", "trusted", "boxmaker0 trusted"),
),
(
with(
"untrusted = \"boxmaker0-untrusted\"",
"untrusted = \"boxmaker0/untrusted\"",
"",
),
name_reason("[honcho]", "untrusted", "boxmaker0/untrusted"),
),
(
with("fleet = \"fleet\"", "fleet = \"fleet:all\"", ""),
name_reason("[honcho]", "fleet", "fleet:all"),
),
(
format!("{SECTION}{}", read("boxmaker.1", "boxmaker1-trusted")),
name_reason("[[honcho.read]]", "agent", "boxmaker.1"),
),
(
format!("{SECTION}{}", read("boxmaker1", "boxmaker1.trusted")),
name_reason("[[honcho.read]]", "workspace", "boxmaker1.trusted"),
),
(
with("owner = \"kyle\"", "owner = \"boxmaker0\"", ""),
"[honcho] agent and owner are both \"boxmaker0\": the agent's peer must not be the owner's"
.to_string(),
),
(
with(
"untrusted = \"boxmaker0-untrusted\"",
"untrusted = \"boxmaker0-trusted\"",
"",
),
"[honcho] workspace \"boxmaker0-trusted\" is named twice: trusted, untrusted and fleet must be three different workspaces"
.to_string(),
),
(
with("fleet = \"fleet\"", "fleet = \"boxmaker0-trusted\"", ""),
"[honcho] workspace \"boxmaker0-trusted\" is named twice: trusted, untrusted and fleet must be three different workspaces"
.to_string(),
),
(
with("fleet = \"fleet\"", "fleet = \"boxmaker0-untrusted\"", ""),
"[honcho] workspace \"boxmaker0-untrusted\" is named twice: trusted, untrusted and fleet must be three different workspaces"
.to_string(),
),
(
format!("{SECTION}{}", read("boxmaker0", "boxmaker1-trusted")),
"[[honcho.read]] agent \"boxmaker0\" is this agent ([honcho] agent)".to_string(),
),
(
format!(
"{SECTION}{}{}",
read("boxmaker1", "boxmaker1-trusted"),
read("boxmaker1", "boxmaker1-other")
),
"[[honcho.read]] agent \"boxmaker1\" is listed twice".to_string(),
),
(
format!("{SECTION}{}", read("boxmaker1", "boxmaker0-trusted")),
"[[honcho.read]] workspace \"boxmaker0-trusted\" is one of this agent's own"
.to_string(),
),
(
format!("{SECTION}{}", read("boxmaker1", "boxmaker0-untrusted")),
"[[honcho.read]] workspace \"boxmaker0-untrusted\" is one of this agent's own"
.to_string(),
),
(
format!("{SECTION}{}", read("boxmaker1", "fleet")),
"[[honcho.read]] workspace \"fleet\" is one of this agent's own".to_string(),
),
(
format!(
"{SECTION}{}trust = \"Trusted\"\n",
read("boxmaker1", "boxmaker1-trusted")
),
"[[honcho.read]] trust \"Trusted\" of agent \"boxmaker1\" is not \"trusted\" or \"untrusted\""
.to_string(),
),
(
format!(
"{SECTION}{}trust = \"\"\n",
read("boxmaker1", "boxmaker1-trusted")
),
"[[honcho.read]] trust \"\" of agent \"boxmaker1\" is not \"trusted\" or \"untrusted\""
.to_string(),
),
(
format!(
"{SECTION}{}class = \"top-secret\"\n",
read("boxmaker1", "boxmaker1-trusted")
),
"[[honcho.read]] class \"top-secret\" of agent \"boxmaker1\" is not \"public\", \"private\" or \"secret\""
.to_string(),
),
(
format!(
"{SECTION}{}class = \"Private\"\n",
read("boxmaker1", "boxmaker1-trusted")
),
"[[honcho.read]] class \"Private\" of agent \"boxmaker1\" is not \"public\", \"private\" or \"secret\""
.to_string(),
),
(
format!("{SECTION}[secrets.honcho_trust]\nenv = \"HONCHO_KEY\"\n"),
"[secrets.honcho_trust] is not a Honcho key: memoryd has only honcho_trusted, honcho_untrusted, honcho_fleet and honcho_read_<agent>"
.to_string(),
),
(
format!("{SECTION}[secrets.mattermost_token]\nenv = \"MM_TOKEN\"\n"),
"[secrets.mattermost_token] is not a Honcho key: memoryd has only honcho_trusted, honcho_untrusted, honcho_fleet and honcho_read_<agent>"
.to_string(),
),
(
format!(
"{SECTION}{}[secrets.honcho_read_boxmaker2]\nenv = \"HONCHO_KEY\"\n",
read("boxmaker1", "boxmaker1-trusted")
),
"[secrets.honcho_read_boxmaker2] is the key of agent \"boxmaker2\", which is not in [[honcho.read]]"
.to_string(),
),
(
format!("{SECTION}[secrets.honcho_read_boxmaker0]\nenv = \"HONCHO_KEY\"\n"),
"[secrets.honcho_read_boxmaker0] is the key of agent \"boxmaker0\", which is not in [[honcho.read]]"
.to_string(),
),
(
format!("{SECTION}[secrets.honcho_trusted]\n"),
"[secrets.honcho_trusted] needs exactly one of credential, env and file".to_string(),
),
(
format!(
"{SECTION}[secrets.honcho_fleet]\nenv = \"HONCHO_KEY\"\nfile = \"/etc/honcho.key\"\n"
),
"[secrets.honcho_fleet] needs exactly one of credential, env and file".to_string(),
),
(
format!("{SECTION}[secrets.honcho_untrusted]\nenv = \"honcho_key\"\n"),
"[secrets.honcho_untrusted] env \"honcho_key\" is not a variable name (A-Z, 0-9, _)"
.to_string(),
),
(
format!(
"{SECTION}{}[secrets.honcho_read_boxmaker1]\nfile = \"keys/boxmaker1\"\n",
read("boxmaker1", "boxmaker1-trusted")
),
"[secrets.honcho_read_boxmaker1] file \"keys/boxmaker1\" must be an absolute path"
.to_string(),
),
(
"[secrets.honcho_trusted]\nenv = \"HONCHO_KEY\"\n".to_string(),
"[secrets.honcho_trusted] is set, but there is no [honcho] section".to_string(),
),
];
for (text, want) in &cases {
assert_eq!(&reason(text), want, "\n{text}");
}
assert_eq!(cases.len(), 29);
}
#[test]
fn the_first_problem_is_the_one_reported() {
// A bad url and an owner that is the agent: the url is checked first.
let text = with(
"url = \"http://rift.scylla-hammerhead.ts.net:8000\"",
"url = \"rift\"",
"",
)
.replace("owner = \"kyle\"", "owner = \"boxmaker0\"");
assert_eq!(reason(&text), URL_REASON);
// A read agent that is this agent, with a bad trust: the agent is checked first.
let text = format!(
"{SECTION}[[honcho.read]]\nagent = \"boxmaker0\"\nworkspace = \"w\"\ntrust = \"no\"\n"
);
assert_eq!(
reason(&text),
"[[honcho.read]] agent \"boxmaker0\" is this agent ([honcho] agent)"
);
}