mod support;
use std::path::{Path, PathBuf};
use support::{Outcome, run, runner_manager};
const INVALID_ARGUMENT: i32 = 9;
fn cli(data_dir: &Path, args: &[&str]) -> Outcome {
run({
let mut command = runner_manager(data_dir);
command.args(args);
command
})
}
fn settings_file(data_dir: &Path) -> PathBuf {
data_dir.join("config").join("polling.toml")
}
fn polling(data_dir: &Path) -> serde_json::Value {
let status = cli(data_dir, &["status", "--json"]);
assert_eq!(status.code, 0, "{}", status.both());
let document: serde_json::Value =
serde_json::from_str(&status.stdout).expect("status --json is JSON");
document["polling"].clone()
}
#[test]
fn host_set_poll_interval_round_trips_through_polling_toml_and_the_host_row() {
let data = tempfile::tempdir().unwrap();
let defaults = polling(data.path());
assert_eq!(defaults["idle_interval_secs"], 10, "{defaults}");
assert_eq!(defaults["active_interval_secs"], 60, "{defaults}");
assert_eq!(defaults["measured"], serde_json::Value::Null);
let idle = cli(data.path(), &["host", "set-poll-interval", "--idle", "5s"]);
assert_eq!(idle.code, 0, "{}", idle.both());
assert!(
idle.stdout.contains("idle poll interval: 10s -> 5s"),
"{}",
idle.stdout
);
assert!(settings_file(data.path()).exists());
assert_eq!(polling(data.path())["idle_interval_secs"], 5);
let active = cli(
data.path(),
&["host", "set-poll-interval", "--active", "2m"],
);
assert_eq!(active.code, 0, "{}", active.both());
let after = polling(data.path());
assert_eq!(after["active_interval_secs"], 120, "{after}");
assert_eq!(
after["idle_interval_secs"], 5,
"the idle interval is untouched"
);
let prose = cli(data.path(), &["status"]);
assert_eq!(prose.code, 0, "{}", prose.both());
assert!(
prose
.stdout
.contains("projected if no poll were answered 304, of 2500 this host may spend"),
"{}",
prose.stdout
);
let shown = cli(data.path(), &["host", "show"]);
assert_eq!(shown.code, 0, "{}", shown.both());
assert!(
shown.stdout.contains("idle interval 5s"),
"{}",
shown.stdout
);
assert!(
shown.stdout.contains("active interval 120s"),
"{}",
shown.stdout
);
}
#[test]
fn host_set_poll_interval_refuses_what_the_daemon_would_refuse_and_writes_nothing() {
let data = tempfile::tempdir().unwrap();
for (args, why) in [
(&["--idle", "4s"][..], "under the idle floor"),
(&["--active", "29"][..], "under the active floor"),
(
&["--idle", "90s"][..],
"an idle interval longer than the active one",
),
(&["--idle", "ten"][..], "not a duration"),
] {
let mut full = vec!["host", "set-poll-interval"];
full.extend_from_slice(args);
let refused = cli(data.path(), &full);
assert_ne!(refused.code, 0, "{why}: {}", refused.both());
assert!(
!settings_file(data.path()).exists(),
"{why}: a refusal must write nothing"
);
}
let refused = cli(data.path(), &["host", "set-poll-interval", "--idle", "4s"]);
assert_eq!(refused.code, INVALID_ARGUMENT, "{}", refused.both());
let mixed = cli(
data.path(),
&[
"host",
"set-poll-interval",
"--active",
"45",
"--idle",
"4s",
],
);
assert_eq!(mixed.code, INVALID_ARGUMENT, "{}", mixed.both());
let neither = cli(data.path(), &["host", "set-poll-interval"]);
assert_ne!(neither.code, 0, "one of --idle or --active is required");
let unchanged = polling(data.path());
assert_eq!(unchanged["idle_interval_secs"], 10);
assert_eq!(unchanged["active_interval_secs"], 60);
}
#[test]
fn an_unreadable_polling_toml_is_reported_and_repaired_by_the_command_that_names_it() {
let data = tempfile::tempdir().unwrap();
std::fs::create_dir_all(data.path().join("config")).unwrap();
std::fs::write(
settings_file(data.path()),
"schema_version = 1\nidle_interval_secs = 1\n",
)
.unwrap();
let reported = polling(data.path());
assert_eq!(reported["idle_interval_secs"], 10, "the default stands in");
assert!(
reported["idle_interval_unreadable"].is_string(),
"{reported}"
);
let shown = cli(data.path(), &["host", "show"]);
assert_eq!(shown.code, 0, "host show still answers: {}", shown.both());
assert!(shown.stdout.contains("UNREADABLE"), "{}", shown.stdout);
let repaired = cli(data.path(), &["host", "set-poll-interval", "--idle", "10s"]);
assert_eq!(repaired.code, 0, "{}", repaired.both());
assert!(
repaired
.stdout
.contains("replaced an unreadable config/polling.toml"),
"{}",
repaired.stdout
);
let after = polling(data.path());
assert_eq!(after["idle_interval_unreadable"], serde_json::Value::Null);
assert_eq!(after["idle_interval_secs"], 10);
}