use super::*;
#[test]
fn status_json_reports_running_pid_and_address() {
let health = HealthInfo {
uptime_secs: 8123,
version: "1.2.3".to_string(),
};
let value: serde_json::Value =
serde_json::from_str(&status_json(true, Some(42), Some(&health))).unwrap();
assert_eq!(value["running"], serde_json::json!(true));
assert_eq!(value["pid"], serde_json::json!(42));
assert_eq!(value["address"], serde_json::json!(BIND_ADDR));
assert_eq!(value["uptime_secs"], serde_json::json!(8123));
assert_eq!(value["version"], serde_json::json!("1.2.3"));
}
#[test]
fn status_json_null_pid_when_unknown_or_down() {
let value: serde_json::Value = serde_json::from_str(&status_json(false, None, None)).unwrap();
assert_eq!(value["running"], serde_json::json!(false));
assert!(value["pid"].is_null());
assert_eq!(value["address"], serde_json::json!(BIND_ADDR));
assert!(value["uptime_secs"].is_null());
assert!(value["version"].is_null());
}
#[test]
fn parse_health_reads_uptime_and_version() {
let body = r#"{"status":"ok","uptime_secs":42,"running":true,"version":"9.9.9"}"#;
assert_eq!(
parse_health(body),
Some(HealthInfo {
uptime_secs: 42,
version: "9.9.9".to_string(),
})
);
}
#[test]
fn parse_health_rejects_malformed_or_incomplete_bodies() {
assert_eq!(parse_health("not json"), None);
assert_eq!(parse_health(r#"{"uptime_secs":1}"#), None);
assert_eq!(parse_health(r#"{"version":"1.0.0"}"#), None);
assert_eq!(
parse_health(r#"{"uptime_secs":"x","version":"1.0.0"}"#),
None
);
}
#[test]
fn fetch_health_parses_a_well_formed_health_response() {
let server = FakeServer::start(
200,
r#"{"status":"ok","uptime_secs":7,"running":true,"version":"3.2.1"}"#.to_string(),
);
let _addr = EnvGuard::set(BIND_ADDR_ENV, &server.addr);
assert_eq!(
fetch_health(),
Some(HealthInfo {
uptime_secs: 7,
version: "3.2.1".to_string(),
})
);
}
#[test]
fn fetch_health_is_none_on_non_200_status() {
let server = FakeServer::start(503, String::new());
let _addr = EnvGuard::set(BIND_ADDR_ENV, &server.addr);
assert_eq!(fetch_health(), None);
}
#[test]
fn fetch_health_is_none_when_no_server() {
let _addr = EnvGuard::set(BIND_ADDR_ENV, UNREACHABLE_ADDR);
assert_eq!(fetch_health(), None);
}
#[test]
fn wait_until_returns_true_immediately_when_check_already_passes() {
assert!(wait_until(|| true, Duration::from_secs(0)));
}
#[test]
fn wait_until_calls_check_at_least_once_even_with_zero_timeout() {
let calls = std::cell::Cell::new(0);
let succeeded = wait_until(
|| {
calls.set(calls.get() + 1);
false
},
Duration::from_secs(0),
);
assert!(!succeeded);
assert_eq!(calls.get(), 1);
}
#[test]
fn wait_until_polls_until_check_flips_true() {
let calls = std::cell::Cell::new(0);
let succeeded = wait_until(
|| {
calls.set(calls.get() + 1);
calls.get() >= 3
},
Duration::from_secs(5),
);
assert!(succeeded);
assert_eq!(calls.get(), 3);
}
#[test]
fn cleanup_json_reports_removed_and_running() {
let value: serde_json::Value = serde_json::from_str(&cleanup_json(3, 12345, true)).unwrap();
assert_eq!(value["running"], serde_json::json!(true));
assert_eq!(value["removed"], serde_json::json!(3));
assert_eq!(value["freed_bytes"], serde_json::json!(12345));
assert_eq!(value["address"], serde_json::json!(BIND_ADDR));
let down: serde_json::Value = serde_json::from_str(&cleanup_json(0, 0, false)).unwrap();
assert_eq!(down["running"], serde_json::json!(false));
assert_eq!(down["removed"], serde_json::json!(0));
assert_eq!(down["freed_bytes"], serde_json::json!(0));
assert_eq!(down["address"], serde_json::json!(BIND_ADDR));
}
#[test]
fn stop_json_reports_running_pid_and_address() {
let up: serde_json::Value = serde_json::from_str(&stop_json(true, Some(42))).unwrap();
assert_eq!(up["running"], serde_json::json!(true));
assert_eq!(up["pid"], serde_json::json!(42));
assert_eq!(up["address"], serde_json::json!(BIND_ADDR));
let down: serde_json::Value = serde_json::from_str(&stop_json(false, None)).unwrap();
assert_eq!(down["running"], serde_json::json!(false));
assert!(down["pid"].is_null());
assert_eq!(down["address"], serde_json::json!(BIND_ADDR));
}
fn json_key_set(json: &str) -> std::collections::BTreeSet<String> {
serde_json::from_str::<serde_json::Value>(json)
.unwrap()
.as_object()
.unwrap()
.keys()
.cloned()
.collect()
}
#[test]
fn status_and_stop_json_share_a_common_key_set() {
assert!(
json_key_set(&stop_json(true, Some(42))).is_subset(&json_key_set(&status_json(
true,
Some(42),
None
))),
"every key in stop --json must also appear in status --json (running branch)"
);
assert!(
json_key_set(&stop_json(false, None))
.is_subset(&json_key_set(&status_json(false, None, None))),
"every key in stop --json must also appear in status --json (down branch)"
);
}
#[test]
fn liveness_exit_code_maps_running_to_codes() {
assert_eq!(liveness_exit_code(true), 0);
assert_eq!(liveness_exit_code(false), EXIT_NOT_RUNNING);
assert_eq!(EXIT_NOT_RUNNING, 3);
}