use std::error::Error;
use std::path::Path;
use super::{Config, Credential};
const FIELDS: [&str; 8] = [
"server",
"channel",
"token_file",
"frame_bytes",
"record_bytes",
"reply_timeout_ms",
"flow_steps",
"request_bytes",
];
fn written(token: &Path, without: Option<&str>, zero: Option<&str>) -> String {
let mut top = String::new();
let mut limits = String::new();
for field in FIELDS {
if without == Some(field) {
continue;
}
let value = match field {
"server" => "\"127.0.0.1:4100\"".to_owned(),
"channel" => "\"events\"".to_owned(),
"token_file" => format!("{:?}", token.display().to_string()),
_ if zero == Some(field) => "0".to_owned(),
_ => "16".to_owned(),
};
if matches!(field, "server" | "channel" | "token_file") {
top.extend([field, " = ", value.as_str(), "\n"]);
} else {
limits.extend([field, " = ", value.as_str(), "\n"]);
}
}
format!("{top}\n[limits]\n{limits}")
}
#[test]
fn config_refuses_each_missing_or_zero_field_by_name() -> Result<(), Box<dyn Error>> {
let dir = tempfile::tempdir()?;
let token = dir.path().join("token");
std::fs::write(&token, "pass-bytes\n")?;
let path = dir.path().join("ticket.toml");
std::fs::write(&path, written(&token, Some("server"), None))?;
assert_eq!(Config::load(&path)?.server, crate::seat::CENTRAL_SERVER);
for field in FIELDS[1..].iter().filter(|field| **field != "token_file") {
std::fs::write(&path, written(&token, Some(field), None))?;
let refusal = Config::load(&path).err().map(|e| e.to_string());
let refusal = refusal.ok_or("a missing field was accepted")?;
assert!(refusal.contains(field), "missing {field}: {refusal}");
}
for field in &FIELDS[3..] {
std::fs::write(&path, written(&token, None, Some(field)))?;
let refusal = Config::load(&path).err().map(|e| e.to_string());
let refusal = refusal.ok_or("a zero field was accepted")?;
assert!(refusal.contains(field), "zero {field}: {refusal}");
}
std::fs::write(&path, written(&token, None, None))?;
let config = Config::load(&path)?;
assert_eq!(config.limits.flow_steps, 16);
let Credential::Token(token) = &config.credential else {
return Err("a token_file configuration did not hold a token".into());
};
assert_eq!(token.as_str(), "pass-bytes");
Ok(())
}
#[test]
fn pass_bytes_appear_in_no_debug_and_no_error() -> Result<(), Box<dyn Error>> {
let dir = tempfile::tempdir()?;
let token = dir.path().join("token");
std::fs::write(&token, "distinct-pass-7f3a\n")?;
let path = dir.path().join("ticket.toml");
std::fs::write(&path, written(&token, None, None))?;
let config = Config::load(&path)?;
let debug = format!("{config:?}");
assert!(!debug.contains("distinct-pass-7f3a"), "{debug}");
assert!(debug.contains("Token(18 bytes)"), "{debug}");
std::fs::write(&token, "")?;
let refusal = Config::load(&path).err().map(|e| e.to_string());
let refusal = refusal.ok_or("an empty token was accepted")?;
assert!(refusal.contains("token file is empty"), "{refusal}");
Ok(())
}
#[test]
fn agt001_listen_defaults_and_every_zero_bound_are_named() -> Result<(), Box<dyn Error>> {
let directory = tempfile::tempdir()?;
let token = directory.path().join("token");
std::fs::write(&token, "fixture-pass")?;
let path = directory.path().join("seat.toml");
std::fs::write(&path, written(&token, None, None))?;
let config = Config::load(&path)?;
assert_eq!(config.listen.listen_bound, 16);
assert_eq!(config.listen.content_bytes, 65536);
for field in [
"listen_bound",
"content_bytes",
"join_bound",
"join_bytes",
"batch_messages",
"subject_bytes",
"key_bytes",
"agent_bytes",
"issuer_response_bytes",
"run_pass_bytes",
] {
let text = format!("{}\n[listen]\n{field} = 0\n", written(&token, None, None));
std::fs::write(&path, text)?;
let error = Config::load(&path)
.err()
.ok_or("a zero listen bound was accepted")?;
assert!(
error.to_string().contains(field),
"the listen refusal did not name its bound"
);
}
Ok(())
}
#[test]
fn agt001_listen_refuses_unknown_fields_and_non_subject_text() -> Result<(), Box<dyn Error>> {
let directory = tempfile::tempdir()?;
let token = directory.path().join("token");
std::fs::write(&token, "fixture-pass")?;
let path = directory.path().join("seat.toml");
for table in [
"[listen]\nsubjects = [\"a..b\"]\n",
"[listen]\nunknown = true\n",
] {
std::fs::write(&path, format!("{}\n{table}", written(&token, None, None)))?;
assert!(
Config::load(&path).is_err(),
"an invalid listen table was accepted"
);
}
Ok(())
}
#[test]
fn access010_an_open_seat_carries_no_credential_and_reaches_loopback_only()
-> Result<(), Box<dyn Error>> {
let dir = tempfile::tempdir()?;
let token = dir.path().join("token");
let path = dir.path().join("seat.toml");
for server in ["127.0.0.1:4100", "localhost:7900", "ws://[::1]:7902/ws"] {
let text = written(&token, Some("token_file"), None).replace("127.0.0.1:4100", server);
std::fs::write(&path, text)?;
let config = Config::load(&path)?;
assert!(matches!(config.credential, Credential::Open), "{config:?}");
assert_eq!(config.server, server);
}
assert!(!token.exists(), "an open seat reads no token file");
for server in ["192.168.50.40:7900", "wss://liminal.ablative.com.au/ws"] {
let text = written(&token, Some("token_file"), None).replace("127.0.0.1:4100", server);
std::fs::write(&path, text)?;
let refusal = Config::load(&path).err().map(|e| e.to_string());
let refusal = refusal.ok_or("an open seat off loopback was accepted")?;
assert!(
refusal.contains(&format!(
"an open seat (no token_file, no [lys]) reaches a loopback exchange only: server {server}"
)),
"{refusal}"
);
}
std::fs::write(&token, "pass-bytes\n")?;
let both = format!(
"{}\n[lys]\nissuer = \"http://127.0.0.1:8490\"\nagent = \"agent-x\"\n",
written(&token, None, None)
);
std::fs::write(&path, both)?;
let refusal = Config::load(&path).err().map(|e| e.to_string());
let refusal = refusal.ok_or("two credentials were accepted")?;
assert!(
refusal.contains("give exactly one of token_file and [lys]"),
"{refusal}"
);
Ok(())
}