use super::*;
#[test]
fn format_bytes_scales_units() {
assert_eq!(format_bytes(512), "512B");
assert_eq!(format_bytes(1024), "1.0KiB");
assert_eq!(format_bytes(1536), "1.5KiB");
assert_eq!(format_bytes(1024 * 1024), "1.0MiB");
assert_eq!(format_bytes(3 * 1024 * 1024 * 1024), "3.0GiB");
}
#[test]
fn format_row_sums_network_and_shows_name() {
let mut network = HashMap::new();
network.insert("eth0".to_string(), NetStat { rx: 1024, tx: 2048 });
let s = ContainerStat {
name: "proj-web".into(),
cpu: 12.5,
mem_usage: 1024 * 1024,
mem_limit: 1024 * 1024 * 1024,
mem_perc: 0.1,
block_in: 0,
block_out: 0,
pids: 3,
network,
};
let row = format_row_with(&s, false, false);
assert!(row.contains("proj-web"));
assert!(row.contains("12.50%"));
assert!(row.contains("1.0MiB"));
assert!(row.contains('3'));
}
#[test]
fn truncate_name_shortens_long_names_with_ellipsis() {
let short = "proj-web-1";
assert_eq!(truncate_name(short, false), short);
let exact = "a".repeat(NAME_WIDTH);
assert_eq!(truncate_name(&exact, false), exact);
let long = "a".repeat(NAME_WIDTH + 10);
let cut = truncate_name(&long, false);
assert_eq!(cut.chars().count(), NAME_WIDTH);
assert!(cut.ends_with('…'));
assert_eq!(truncate_name(&long, true), long);
}
#[test]
fn format_row_truncates_long_name_but_no_trunc_keeps_it() {
let s = ContainerStat {
name: "really-long-project-web-container-name-1".into(),
..Default::default()
};
let row = format_row_with(&s, false, false);
assert!(row.contains('…'));
assert!(!row.contains(&s.name));
let row_full = format_row_with(&s, true, false);
assert!(row_full.contains(&s.name));
}
#[test]
fn stat_json_row_emits_numeric_fields() {
let mut network = HashMap::new();
network.insert("eth0".to_string(), NetStat { rx: 100, tx: 200 });
let s = ContainerStat {
name: "proj-db-1".into(),
cpu: 5.5,
mem_usage: 2048,
mem_limit: 4096,
mem_perc: 50.0,
block_in: 1,
block_out: 2,
pids: 7,
network,
};
let row = stat_json_row(&s);
assert_eq!(row["Name"], "proj-db-1");
assert_eq!(row["CPUPerc"], 5.5);
assert_eq!(row["MemUsage"], 2048);
assert_eq!(row["MemLimit"], 4096);
assert_eq!(row["NetInput"], 100);
assert_eq!(row["NetOutput"], 200);
assert_eq!(row["PIDs"], 7);
}
#[test]
fn frame_rows_keeps_running_and_adds_stopped_zeros_sorted() {
let report = StatsReport {
stats: vec![
ContainerStat {
name: "proj-web-1".into(),
cpu: 1.0,
..Default::default()
},
ContainerStat {
name: "other".into(),
..Default::default()
},
],
};
let mut running = HashSet::new();
running.insert("proj-web-1".to_string());
let stopped = vec!["proj-db-1".to_string()];
let rows = frame_rows(&report, &running, &stopped);
assert_eq!(rows.len(), 2);
assert_eq!(rows[0].name, "proj-db-1");
assert_eq!(rows[0].cpu, 0.0);
assert_eq!(rows[1].name, "proj-web-1");
assert_eq!(rows[1].cpu, 1.0);
}
#[test]
fn frame_rows_without_all_has_no_stopped_rows() {
let report = StatsReport::default();
let running = HashSet::new();
let rows = frame_rows(&report, &running, &[]);
assert!(rows.is_empty());
}
#[test]
fn stat_tolerates_null_network() {
let json = r#"{"Name":"proj-web","CPU":1.0,"Network":null}"#;
let stat: ContainerStat = serde_json::from_str(json).unwrap();
assert_eq!(stat.name, "proj-web");
assert!(stat.network.is_empty());
}
#[test]
fn stat_tolerates_missing_network() {
let json = r#"{"Name":"proj-web"}"#;
let stat: ContainerStat = serde_json::from_str(json).unwrap();
assert!(stat.network.is_empty());
}
#[test]
fn containers_query_repeats_param_per_container() {
let mut wanted = HashSet::new();
wanted.insert("proj-web-1".to_string());
wanted.insert("proj-db-1".to_string());
assert_eq!(
containers_query(&wanted),
"&containers=proj-db-1&containers=proj-web-1"
);
}
#[test]
fn containers_query_empty_when_none_wanted() {
assert_eq!(containers_query(&HashSet::new()), "");
}
fn name_set(names: &[&str]) -> HashSet<String> {
names.iter().map(|s| s.to_string()).collect()
}
#[test]
fn stats_stream_break_is_fatal_only_while_a_sampled_container_still_runs() {
let sampled = name_set(&["proj-web-1", "proj-db-1"]);
assert!(stats_stream_broke_mid_sample(
&sampled,
&name_set(&["proj-web-1"])
));
assert!(!stats_stream_broke_mid_sample(&sampled, &HashSet::new()));
assert!(!stats_stream_broke_mid_sample(
&sampled,
&name_set(&["other-app-1"])
));
}
#[test]
fn stats_stream_break_with_no_sampled_containers_is_never_fatal() {
assert!(!stats_stream_broke_mid_sample(
&HashSet::new(),
&name_set(&["proj-web-1"])
));
}
#[test]
fn first_unknown_service_flags_typos() {
let file =
crate::parse_str("services:\n web:\n image: nginx\n db:\n image: postgres\n")
.unwrap();
assert_eq!(first_unknown_service(&file, &[]), None);
assert_eq!(
first_unknown_service(&file, &["web".into(), "db".into()]),
None
);
assert_eq!(
first_unknown_service(&file, &["web".into(), "bogus".into()]),
Some("bogus")
);
}