use super::dispatch_ops;
use kevy_resp::Argv;
use kevy_store::Store;
fn run(verb: &[u8], rest: &[&[u8]]) -> Vec<u8> {
run_on(&crate::KevyCommands::new(), verb, rest)
}
fn run_on(c: &crate::KevyCommands, verb: &[u8], rest: &[&[u8]]) -> Vec<u8> {
let mut a = Argv::default();
a.push(verb);
for r in rest {
a.push(r);
}
let mut out = Vec::new();
let mut store = Store::new();
let handled = dispatch_ops(&c.ctx(), verb, &mut store, &a, &mut out);
assert!(handled, "verb {:?} not handled", String::from_utf8_lossy(verb));
out
}
#[test]
fn info_returns_bulk_with_sections() {
let out = run(b"INFO", &[]);
let s = String::from_utf8(out).unwrap();
assert!(s.starts_with('$'), "INFO must reply as bulk string");
assert!(s.contains("# Server"));
assert!(s.contains("# Replication"));
assert!(s.contains("role:master"));
assert!(s.contains("cluster_enabled:0"));
}
#[test]
fn info_specific_section() {
let out = run(b"INFO", &[b"memory"]);
let s = String::from_utf8(out).unwrap();
assert!(s.contains("# Memory"));
assert!(!s.contains("# Server"));
}
#[test]
fn info_memory_reports_process_rss() {
let out = run(b"INFO", &[b"memory"]);
let s = String::from_utf8(out).unwrap();
let rss: u64 = s
.lines()
.find_map(|l| l.strip_prefix("process_rss_bytes:"))
.expect("process_rss_bytes line present")
.trim()
.parse()
.expect("a byte count");
assert!(rss > 0, "the probe must answer on dev platforms, got {rss}");
}
#[test]
fn info_persistence_reports_aof_format() {
let out = run(b"INFO", &[b"persistence"]);
let s = String::from_utf8(out).unwrap();
assert!(s.contains("aof_format:off"), "{s}");
}
#[test]
fn info_replication_master_default_shape() {
let ack = |off| Some(kevy_rt::ReplicaAck { acked_offset: off, ack_age_ms: 0 });
let replicas = vec![
(
"kevy-replica-7001#0".to_string(),
std::net::Ipv4Addr::new(10, 0, 0, 1),
50_001,
42u64,
ack(42),
),
(
"kevy-replica-7002#0".to_string(),
std::net::Ipv4Addr::new(10, 0, 0, 2),
50_002,
41u64,
ack(41),
),
(
"kevy-replica-7003#0".to_string(),
std::net::Ipv4Addr::new(10, 0, 0, 3),
50_003,
40u64,
ack(40),
),
];
let c = crate::KevyCommands::new();
c.state().obs.publish_repl_view(0, crate::state::ReplShardView { offset: 42, replicas });
let out = run_on(&c, b"INFO", &[b"replication"]);
let s = String::from_utf8(out).unwrap();
assert!(s.contains("role:master"), "got: {s}");
assert!(s.contains("connected_slaves:3"), "got: {s}");
assert!(s.contains("master_repl_offset:42"), "got: {s}");
assert!(s.contains("master_replid:"), "got: {s}");
assert!(!s.contains("master_host"), "got: {s}");
assert!(!s.contains("master_link_status"), "got: {s}");
}
#[test]
fn cluster_info_carries_standalone_markers() {
let out = run(b"CLUSTER", &[b"INFO"]);
let s = String::from_utf8(out).unwrap();
assert!(s.contains("cluster_enabled:0"));
assert!(s.contains("cluster_state:ok"));
}
#[test]
fn cluster_nodes_single_self_entry() {
let out = run(b"CLUSTER", &[b"NODES"]);
let s = String::from_utf8(out).unwrap();
assert!(s.contains("myself,master"));
assert!(s.contains("0-16383"));
}
#[test]
fn debug_sleep_zero_returns_immediately() {
let out = run(b"DEBUG", &[b"SLEEP", b"0"]);
assert_eq!(out, b"+OK\r\n");
}
#[test]
fn debug_sleep_small_actually_sleeps() {
let t = std::time::Instant::now();
let out = run(b"DEBUG", &[b"SLEEP", b"0.05"]);
let elapsed = t.elapsed();
assert!(elapsed.as_millis() >= 40, "expected ≥ 40ms, got {elapsed:?}");
assert_eq!(out, b"+OK\r\n");
}
#[test]
fn wait_returns_zero_replicas() {
let out = run(b"WAIT", &[b"3", b"1000"]);
assert_eq!(out, b":0\r\n");
}
#[test]
fn wait_wrong_args_errors() {
let out = run(b"WAIT", &[b"3"]);
assert!(out.starts_with(b"-ERR"));
}
#[test]
fn wait_non_integer_args_error() {
let out = run(b"WAIT", &[b"x", b"1000"]);
assert!(out.starts_with(b"-ERR value is not an integer"));
let out = run(b"WAIT", &[b"1", b"-3"]);
assert!(out.starts_with(b"-ERR value is not an integer"));
}
#[test]
fn repl_token_on_replica_flag_reports_runner_view() {
let mut a = Argv::default();
a.push(b"REPL.TOKEN");
let mut out = Vec::new();
let mut store = Store::new();
let c = crate::KevyCommands::new();
c.state().replication.force_replica_flag();
assert!(dispatch_ops(&c.ctx(), b"REPL.TOKEN", &mut store, &a, &mut out));
assert_eq!(out, b"*0\r\n");
}
#[test]
fn repl_wait_on_primary_is_ok_and_bad_token_errors() {
assert_eq!(run(b"REPL.WAIT", &[b"1", b"42"]), b"+OK\r\n");
assert!(run(b"REPL.WAIT", &[b"1"]).starts_with(b"-ERR REPL.WAIT"));
}