101 lines
3.4 KiB
Rust
101 lines
3.4 KiB
Rust
//! top, report and log --follow (issues #37 #38).
|
|
|
|
mod common;
|
|
|
|
use agent_manager::commands::stats_cmd;
|
|
use agent_manager::events;
|
|
|
|
fn app_with_events(tag: &str, lines: &[&str]) -> agent_manager::app::App {
|
|
let app = common::test_app(tag, &["stats"]);
|
|
let month = chrono::Utc::now().format("%Y%m").to_string();
|
|
let path = app.events_dir().join(format!("events-{month}.jsonl"));
|
|
std::fs::create_dir_all(app.events_dir()).unwrap();
|
|
let mut text = String::new();
|
|
for l in lines {
|
|
text.push_str(l);
|
|
text.push('\n');
|
|
}
|
|
std::fs::write(&path, text).unwrap();
|
|
app
|
|
}
|
|
|
|
#[test]
|
|
fn top_sorts_by_launches() {
|
|
let app = app_with_events(
|
|
"top",
|
|
&[
|
|
r#"{"ts":"2026-08-01T10:00:00Z","kind":"run","agent":"a","exit_code":0}"#,
|
|
r#"{"ts":"2026-08-02T10:00:00Z","kind":"run","agent":"a","exit_code":0}"#,
|
|
r#"{"ts":"2026-08-03T10:00:00Z","kind":"run","agent":"b","exit_code":0}"#,
|
|
],
|
|
);
|
|
let rows = stats_cmd::top(&app, None).unwrap();
|
|
assert_eq!(rows.len(), 2);
|
|
assert_eq!(rows[0].agent, "a");
|
|
assert_eq!(rows[0].launches, 2);
|
|
}
|
|
|
|
#[test]
|
|
fn report_md_contains_numbers_and_table() {
|
|
let today = chrono::Utc::now().format("%Y-%m-%d").to_string();
|
|
let lines = [
|
|
format!(r#"{{"ts":"{today}T10:00:00Z","kind":"run","agent":"a","exit_code":0,"duration_s":10}}"#),
|
|
format!(r#"{{"ts":"{today}T10:01:00Z","kind":"run","agent":"a","exit_code":1,"duration_s":5}}"#),
|
|
format!(r#"{{"ts":"{today}T10:02:00Z","kind":"install","agent":"b"}}"#),
|
|
];
|
|
let refs: Vec<&str> = lines.iter().map(|s| s.as_str()).collect();
|
|
let app = app_with_events("report", &refs);
|
|
// Issue #74: the report follows the app language; force French so the
|
|
// assertions are deterministic whatever the host LANG says.
|
|
app.set_lang(agent_manager::i18n::Lang::Fr);
|
|
let md = stats_cmd::report_md(&app, 7);
|
|
assert!(md.contains("# Rapport agent-manager"), "md: {md}");
|
|
assert!(md.contains("Lancements : 2"));
|
|
assert!(md.contains("Échecs : 1"));
|
|
assert!(md.contains("Changements"));
|
|
assert!(md.contains("| a | 2 |"), "table top: {md}");
|
|
}
|
|
|
|
#[test]
|
|
fn report_command_writes_file() {
|
|
let app = app_with_events("reportcmd", &[]);
|
|
let out = app.events_dir().join("report-test.md");
|
|
let code = stats_cmd::run_report(&app, true, false, Some(&out)).unwrap();
|
|
assert_eq!(code, 0);
|
|
assert!(out.exists());
|
|
let _ = std::fs::remove_file(&out);
|
|
}
|
|
|
|
#[test]
|
|
fn text_after_is_utf8_safe() {
|
|
use agent_manager::commands::log_cmd;
|
|
let text = "héllo wörld
|
|
second line
|
|
";
|
|
assert_eq!(log_cmd::text_after(text, 0), text);
|
|
assert_eq!(log_cmd::text_after(text, 3), "llo wörld
|
|
second line
|
|
");
|
|
let full = text.len();
|
|
assert_eq!(log_cmd::text_after(text, full), "");
|
|
}
|
|
|
|
#[test]
|
|
fn follow_flag_parses() {
|
|
use agent_manager::cli::{Cli, Command};
|
|
use clap::Parser;
|
|
let cli = Cli::parse_from(["am", "log", "--follow"]);
|
|
match &cli.command {
|
|
Some(Command::Log { follow, .. }) => assert!(*follow),
|
|
other => panic!("unexpected: {other:?}"),
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn journal_still_reads_after_events_api() {
|
|
let app = app_with_events("api", &[]);
|
|
let mut log = events::EventLog::new(app.events_dir());
|
|
log.append(&events::Event::now(events::EventKind::Run).with_agent("x")).unwrap();
|
|
assert_eq!(events::read_events(&app.events_dir(), 0).len(), 1);
|
|
}
|