use std::path::PathBuf;
use gunnar_common::logtap::{self, TapLayer};
use gunnar_server::dashboard::{Facts, Screen, DEFAULT_COLS, DEFAULT_ROWS, EVENT_ROWS};
use tracing_subscriber::layer::SubscriberExt as _;
#[test]
fn a_frame_carries_this_box_and_the_lines_that_were_logged() {
let subscriber = tracing_subscriber::registry().with(TapLayer::new());
let rx = tracing::subscriber::with_default(subscriber, || {
let rx = logtap::take_receiver().expect("the tap's receiver, once");
let span = tracing::info_span!(
"ssh.connection",
peer = "10.0.2.2:40000",
principal = tracing::field::Empty,
repo = tracing::field::Empty,
service = tracing::field::Empty
);
span.record("principal", "alice");
span.record("repo", "ns/demo");
span.record("service", "git-receive-pack");
let _e = span.enter();
tracing::info!(target: "gunnar::ssh", event = "ssh.auth.accepted", fingerprint = "SHA256:ZZZZZZZZZZZZZZZZZZZZ", "publickey accepted");
tracing::info!(target: "gunnar::git", event = "git.transfer.finished", status = "ok", elapsed_ms = 812u64, "transfer finished");
tracing::info!(target: "gunnar::git", event = "git.upload_pack.advertised", bytes_out = 10u64, "not a row");
rx
});
let mut screen = Screen::new(Facts {
ssh_bind: "0.0.0.0:2222".parse().unwrap(),
grpc_bind: None,
data_root: PathBuf::from("/"),
build: "test-build".into(),
engine: None,
replication: gunnar_server::replication_view::ReplicationView::new(),
tenants: gunnar_server::dashboard::TenantView::new(),
flow: gunnar_server::dashboard::FlowView::new(),
});
screen.sample();
for line in rx.try_iter() {
screen.take(&line);
}
std::thread::sleep(std::time::Duration::from_millis(30));
screen.sample();
let frame = screen.render_frame(DEFAULT_COLS, DEFAULT_ROWS);
let raw: Vec<&str> = frame.trim_start_matches("\x1b[H").split("\r\n").collect();
assert!(
raw.iter().all(|l| l.ends_with("\x1b[K")),
"every row ends with ESC[K"
);
let lines: Vec<&str> = raw
.iter()
.map(|l| l.strip_suffix("\x1b[K").unwrap())
.collect();
assert_eq!(lines.len(), DEFAULT_ROWS);
assert!(lines.iter().all(|l| l.chars().count() == DEFAULT_COLS - 1));
assert!(
lines[0].contains("gunnar test-build")
&& lines[0].contains(" up ")
&& !lines[0].contains("up -"),
"{}",
lines[0]
);
assert!(
lines[2].contains("ram ") && !lines[2].contains("ram -"),
"{}",
lines[2]
);
assert!(
lines[3].contains("disk / ")
&& !lines[3].contains("disk / -")
&& lines[3].contains("ssh *:2222")
&& !lines[3].contains("grpc"),
"{}",
lines[3]
);
let mounts = std::fs::read_to_string("/proc/mounts").unwrap();
let root = gunnar_server::dashboard::probe::parse_mounts(&mounts, "/").expect("/ is a mount");
let fs = if root.fstype == "vfat" {
"fat"
} else {
root.fstype.as_str()
};
assert!(
lines[3].contains(&format!(" {fs} ssh")),
"the disk cell names the root's filesystem ({fs}): {}",
lines[3]
);
assert!(screen.cpus >= 1 && screen.uptime_secs.is_some());
let body: Vec<&str> = lines[5..]
.iter()
.copied()
.filter(|l| !l.trim().is_empty())
.collect();
assert_eq!(body.len(), 2, "{body:?}");
assert!(
body[0].contains("login alice ZZZZZZZZZZZZ\u{2026} 10.0.2.2:40000"),
"{}",
body[0]
);
assert!(
body[1].contains("push ns/demo alice ok 812 ms"),
"{}",
body[1]
);
assert!(!frame.contains("not a row"));
assert_eq!(
EVENT_ROWS + 5,
DEFAULT_ROWS,
"the ring and the band fill exactly one VGA screen"
);
}