247 lines
7.2 KiB
Rust
247 lines
7.2 KiB
Rust
//! `gatewayd.toml` (M4a spec, section 3). Do not edit.
|
|
|
|
#[path = "support/tmp.rs"]
|
|
mod tmp;
|
|
|
|
use std::path::PathBuf;
|
|
|
|
use gatewayd::config::{Config, ConfigError, SecretSource, ServerUrl, parse_url, valid_id};
|
|
use tmp::TempDir;
|
|
|
|
const OWNER: &str = "abcdefghijklmnopqrstuvwxyz";
|
|
const CHANNEL: &str = "0123456789abcdefghijklmnop";
|
|
|
|
fn minimal() -> String {
|
|
format!(
|
|
"[mattermost]\nurl = \"https://straylight.scylla-hammerhead.ts.net\"\n\
|
|
[secrets.mattermost_token]\ncredential = \"mattermost-token\"\n\
|
|
[allow]\nusers = [\"{OWNER}\"]\n"
|
|
)
|
|
}
|
|
|
|
fn load(text: &str) -> Result<Config, ConfigError> {
|
|
let dir = TempDir::new("cfg");
|
|
let path = dir.write("gatewayd.toml", text);
|
|
Config::load(&path)
|
|
}
|
|
|
|
fn invalid(text: &str) -> String {
|
|
match load(text) {
|
|
Err(ConfigError::Invalid(_, why)) => why,
|
|
other => panic!("expected Invalid for {text:?}, got {other:?}"),
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn a_minimal_config_gets_every_default() {
|
|
let c = load(&minimal()).unwrap();
|
|
assert_eq!(
|
|
c.server().unwrap(),
|
|
ServerUrl {
|
|
tls: true,
|
|
host: "straylight.scylla-hammerhead.ts.net".to_string(),
|
|
port: 443
|
|
}
|
|
);
|
|
assert_eq!(c.mattermost.ca_file, None);
|
|
assert_eq!(
|
|
c.token_source().unwrap(),
|
|
SecretSource::Credential("mattermost-token".to_string())
|
|
);
|
|
assert_eq!(c.allow.users, vec![OWNER.to_string()]);
|
|
assert!(c.allow.channels.is_empty());
|
|
assert_eq!((c.limits.queue, c.limits.typing_every_ms), (20, 3_000));
|
|
assert_eq!(
|
|
(c.limits.ping_every_ms, c.limits.dead_after_ms),
|
|
(30_000, 60_000)
|
|
);
|
|
let home = std::env::var_os("BOXMAKER_HOME")
|
|
.map(PathBuf::from)
|
|
.unwrap_or_else(|| PathBuf::from("/var/lib/boxmaker"));
|
|
assert_eq!(c.loop_socket(), home.join("run/loop/loop.sock"));
|
|
assert_eq!(c.state_path(), home.join("gateway/state.json"));
|
|
}
|
|
|
|
#[test]
|
|
fn every_value_can_be_set() {
|
|
let text = format!(
|
|
"[mattermost]\nurl = \"http://127.0.0.1:8065\"\nca_file = \"/etc/boxmaker/ca.pem\"\n\
|
|
[secrets.mattermost_token]\nfile = \"/home/k/.config/boxmaker/token\"\n\
|
|
[allow]\nusers = [\"{OWNER}\"]\nchannels = [\"{CHANNEL}\"]\n\
|
|
[loop]\nsocket = \"/run/l.sock\"\n[paths]\nhome = \"/h\"\n\
|
|
[limits]\nqueue = 5\ntyping_every_ms = 1\nping_every_ms = 2\ndead_after_ms = 3\n"
|
|
);
|
|
let c = load(&text).unwrap();
|
|
assert_eq!(
|
|
c.server().unwrap(),
|
|
ServerUrl {
|
|
tls: false,
|
|
host: "127.0.0.1".to_string(),
|
|
port: 8065
|
|
}
|
|
);
|
|
assert_eq!(
|
|
c.mattermost.ca_file,
|
|
Some(PathBuf::from("/etc/boxmaker/ca.pem"))
|
|
);
|
|
assert_eq!(
|
|
c.token_source().unwrap(),
|
|
SecretSource::File(PathBuf::from("/home/k/.config/boxmaker/token"))
|
|
);
|
|
assert_eq!(c.allow.channels, vec![CHANNEL.to_string()]);
|
|
assert_eq!(c.loop_socket(), PathBuf::from("/run/l.sock"));
|
|
assert_eq!(c.state_path(), PathBuf::from("/h/gateway/state.json"));
|
|
assert_eq!(
|
|
(
|
|
c.limits.queue,
|
|
c.limits.typing_every_ms,
|
|
c.limits.ping_every_ms,
|
|
c.limits.dead_after_ms
|
|
),
|
|
(5, 1, 2, 3)
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn an_env_secret() {
|
|
let text = minimal().replace(
|
|
"credential = \"mattermost-token\"",
|
|
"env = \"BOXMAKER_MM_TOKEN\"",
|
|
);
|
|
assert_eq!(
|
|
load(&text).unwrap().token_source().unwrap(),
|
|
SecretSource::Env("BOXMAKER_MM_TOKEN".to_string())
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn urls() {
|
|
for (url, tls, host, port) in [
|
|
("https://a.example", true, "a.example", 443),
|
|
("https://a.example:8443", true, "a.example", 8443),
|
|
("http://localhost", false, "localhost", 80),
|
|
("http://127.0.0.1:8065", false, "127.0.0.1", 8065),
|
|
] {
|
|
assert_eq!(
|
|
parse_url(url),
|
|
Ok(ServerUrl {
|
|
tls,
|
|
host: host.to_string(),
|
|
port
|
|
}),
|
|
"{url}"
|
|
);
|
|
}
|
|
for url in [
|
|
"",
|
|
"a.example",
|
|
"ftp://a.example",
|
|
"https://",
|
|
"https://a.example/",
|
|
"https://a.example/api",
|
|
"https://A.example",
|
|
"https://a.example:0",
|
|
"https://a.example:65536",
|
|
"https://a.example:0443",
|
|
"https://a.example:",
|
|
"https://user@a.example",
|
|
"https://a.example?x",
|
|
"https://.a.example",
|
|
"https://a.example.",
|
|
"https://[::1]:443",
|
|
"https:// a.example",
|
|
] {
|
|
assert!(parse_url(url).is_err(), "{url:?} must be refused");
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn ids() {
|
|
assert!(valid_id(OWNER));
|
|
assert!(valid_id(CHANNEL));
|
|
for id in [
|
|
"",
|
|
"abc",
|
|
"abcdefghijklmnopqrstuvwxyZ",
|
|
"abcdefghijklmnopqrstuvwxy-",
|
|
"abcdefghijklmnopqrstuvwxyza",
|
|
] {
|
|
assert!(!valid_id(id), "{id:?}");
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn bad_values_are_named() {
|
|
let token = "credential = \"mattermost-token\"";
|
|
let cases: Vec<(String, &str)> = vec![
|
|
(
|
|
minimal().replace(
|
|
"https://straylight.scylla-hammerhead.ts.net",
|
|
"https://x.example/path",
|
|
),
|
|
"url",
|
|
),
|
|
(
|
|
minimal()
|
|
.replace(
|
|
"[secrets.mattermost_token]",
|
|
"[mattermost2]\n[secrets.other]",
|
|
)
|
|
.replace("[mattermost2]\n", ""),
|
|
"mattermost_token",
|
|
),
|
|
(
|
|
minimal().replace(token, "credential = \"a b\""),
|
|
"credential",
|
|
),
|
|
(minimal().replace(token, "env = \"lower\""), "env"),
|
|
(
|
|
minimal().replace(token, "file = \"relative/token\""),
|
|
"absolute",
|
|
),
|
|
(
|
|
minimal().replace(token, "credential = \"x\"\nenv = \"Y\""),
|
|
"exactly one",
|
|
),
|
|
(minimal().replace(token, ""), "exactly one"),
|
|
(
|
|
minimal().replace(&format!("[\"{OWNER}\"]"), "[]"),
|
|
"users is empty",
|
|
),
|
|
(minimal().replace(OWNER, "tooshort"), "not a Mattermost id"),
|
|
(
|
|
format!("{}channels = [\"NOTANID\"]\n", minimal()),
|
|
"not a Mattermost id",
|
|
),
|
|
(
|
|
minimal().replace("[mattermost]\n", "[mattermost]\nca_file = \"ca.pem\"\n"),
|
|
"ca_file",
|
|
),
|
|
(format!("{}[limits]\nqueue = 0\n", minimal()), "queue"),
|
|
(
|
|
format!("{}[limits]\ndead_after_ms = 0\n", minimal()),
|
|
"dead_after_ms",
|
|
),
|
|
];
|
|
for (text, word) in cases {
|
|
let why = invalid(&text);
|
|
assert!(why.contains(word), "{word}: {why}");
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn unknown_keys_and_missing_tables_are_parse_errors() {
|
|
for text in [
|
|
format!("{}[allow2]\n", minimal()),
|
|
minimal().replace("[mattermost]\n", "[mattermost]\nproxy = \"x\"\n"),
|
|
minimal().replace(
|
|
"credential = \"mattermost-token\"",
|
|
"credential = \"t\"\nkeyring = \"x\"",
|
|
),
|
|
minimal().replace(&format!("[allow]\nusers = [\"{OWNER}\"]\n"), ""),
|
|
format!("{}[limits]\nqueue = -1\n", minimal()),
|
|
] {
|
|
assert!(matches!(load(&text), Err(ConfigError::Parse(..))), "{text}");
|
|
}
|
|
}
|