- #62: am lab --agents a,b --task <f> — séquentiel ou --parallel, capture durée/exit/sortie/coût estimé, rapport comparatif tableau + --json stable, tâches versionnables dans <state>/lab/ (--list), rejouables à l'identique - #75: plugins/ sous state_dir, déclenchés sur on_install/on_start/on_stop/ on_update (App::emit), contrat JSON stdin/stdout, timeout configurable, échec non bloquant, garde anti-récursion AM_PLUGINS_RUNNING, am plugins + am plugins --test <name> (CI), exemples notify + ci-webhook - intégration REPL (parse, complétion, bannière, is_am_command), help, tips, man pages (am-lab.1, am-plugins.1), README, ROADMAP (26/27), v0.5.4 - 334 tests verts (309 + 25) closes #62 closes #75
170 lines
5.7 KiB
Rust
170 lines
5.7 KiB
Rust
//! Integration tests: am lab (issue #62) and am plugins (issue #75).
|
|
|
|
mod common;
|
|
|
|
use agent_manager::cli::{Cli, LabArgs, PluginsArgs};
|
|
use agent_manager::commands::{lab_cmd, plugins_cmd};
|
|
use clap::Parser;
|
|
use std::path::PathBuf;
|
|
|
|
/// A config with two shell-based fake agents for the lab benchmark.
|
|
fn lab_config() -> String {
|
|
concat!(
|
|
"agents:\n",
|
|
" - name: bench-ok\n",
|
|
" installable: false\n",
|
|
" run: sh\n",
|
|
" args: [\"-c\", \"echo out: $0\"]\n",
|
|
" - name: bench-fail\n",
|
|
" installable: false\n",
|
|
" run: sh\n",
|
|
" args: [\"-c\", \"echo boom >&2; exit 3\"]\n",
|
|
)
|
|
.to_string()
|
|
}
|
|
|
|
#[test]
|
|
fn lab_runs_a_task_on_every_agent_and_reports() {
|
|
let dir = std::env::temp_dir().join(format!("am-lab-e2e-{}", std::process::id()));
|
|
std::fs::create_dir_all(&dir).unwrap();
|
|
let cfg = common::write_config(&dir, &lab_config());
|
|
let task = dir.join("hello.yaml");
|
|
std::fs::write(
|
|
&task,
|
|
"name: hello\ndescription: e2e\nargs: [\"hello\"]\ntimeout: 30\n",
|
|
)
|
|
.unwrap();
|
|
|
|
let cli = Cli::parse_from([
|
|
"am",
|
|
"--config",
|
|
cfg.to_str().unwrap(),
|
|
"lab",
|
|
"--agents",
|
|
"bench-ok",
|
|
"--task",
|
|
"hello.yaml",
|
|
]);
|
|
let mut app = agent_manager::app::App::from_cli(cli).expect("app should build");
|
|
common::isolate(&mut app, &dir);
|
|
|
|
let code = lab_cmd::run(
|
|
&app,
|
|
&LabArgs {
|
|
agents: Some("bench-ok, bench-fail".to_string()),
|
|
task: Some(task.clone()),
|
|
parallel: false,
|
|
timeout: None,
|
|
list: false,
|
|
},
|
|
)
|
|
.unwrap();
|
|
// One agent fails → the exit code is 1 (CI-friendly).
|
|
assert_eq!(code, 1);
|
|
|
|
// The benchmark is replayable with the same file.
|
|
let code2 = lab_cmd::run(
|
|
&app,
|
|
&LabArgs {
|
|
agents: Some("bench-ok".to_string()),
|
|
task: Some(task),
|
|
parallel: false,
|
|
timeout: None,
|
|
list: false,
|
|
},
|
|
)
|
|
.unwrap();
|
|
assert_eq!(code2, 0);
|
|
|
|
// Each run was journaled as a lab event.
|
|
let events = agent_manager::events::read_events(&app.events_dir(), 0);
|
|
let lab_events = events.iter().filter(|e| e.kind == agent_manager::events::EventKind::Lab);
|
|
assert!(lab_events.count() >= 3, "expected >= 3 lab events");
|
|
|
|
let _ = std::fs::remove_dir_all(&dir);
|
|
}
|
|
|
|
#[test]
|
|
fn lab_list_shows_the_tasks_of_the_lab_dir() {
|
|
let dir = std::env::temp_dir().join(format!("am-lab-list-{}", std::process::id()));
|
|
std::fs::create_dir_all(&dir).unwrap();
|
|
let cfg = common::write_config(&dir, "agents: []\n");
|
|
let cli = Cli::parse_from(["am", "--config", cfg.to_str().unwrap(), "lab", "--list"]);
|
|
let mut app = agent_manager::app::App::from_cli(cli).expect("app should build");
|
|
common::isolate(&mut app, &dir);
|
|
std::fs::create_dir_all(app.events_dir().join("lab")).unwrap();
|
|
std::fs::write(app.events_dir().join("lab/hello.yaml"), "name: hello\n").unwrap();
|
|
|
|
let code = lab_cmd::run(
|
|
&app,
|
|
&LabArgs {
|
|
agents: None,
|
|
task: None,
|
|
parallel: false,
|
|
timeout: None,
|
|
list: true,
|
|
},
|
|
)
|
|
.unwrap();
|
|
assert_eq!(code, 0);
|
|
let _ = std::fs::remove_dir_all(&dir);
|
|
}
|
|
|
|
#[test]
|
|
fn plugin_runs_on_an_emitted_event() {
|
|
let dir = std::env::temp_dir().join(format!("am-plugins-e2e-{}", std::process::id()));
|
|
std::fs::create_dir_all(&dir).unwrap();
|
|
let cfg = common::write_config(&dir, "agents: []\n");
|
|
let cli = Cli::parse_from(["am", "--config", cfg.to_str().unwrap(), "plugins"]);
|
|
let mut app = agent_manager::app::App::from_cli(cli).expect("app should build");
|
|
common::isolate(&mut app, &dir);
|
|
|
|
// Install an echo plugin: valid JSON on stdout (contract) + a marker
|
|
// file proving it ran. The absolute path keeps it independent of cwd.
|
|
let pdir = app.events_dir().join("plugins");
|
|
std::fs::create_dir_all(&pdir).unwrap();
|
|
let marker = pdir.join("pong.log");
|
|
let script = format!(
|
|
"#!/bin/sh\ncat >/dev/null\necho '{{\"ok\": true, \"message\": \"pong\"}}' | tee '{}'\n",
|
|
marker.display().to_string().replace('\\', "/")
|
|
);
|
|
std::fs::write(pdir.join("pong.sh"), script).unwrap();
|
|
let script_path = pdir.join("pong.sh").display().to_string().replace('\\', "/");
|
|
std::fs::write(pdir.join("pong.yaml"), format!("command: \"sh {script_path}\"\n")).unwrap();
|
|
|
|
// List: the plugin is discovered.
|
|
let code = plugins_cmd::run(&app, &PluginsArgs { test: None }).unwrap();
|
|
assert_eq!(code, 0);
|
|
|
|
// --test: the JSON contract is exercised.
|
|
let code = plugins_cmd::run(&app, &PluginsArgs { test: Some("pong".to_string()) }).unwrap();
|
|
assert_eq!(code, 0);
|
|
|
|
// A real emitted event fires the plugin (acceptance criterion).
|
|
app.emit(&agent_manager::events::Event::now(
|
|
agent_manager::events::EventKind::Start,
|
|
));
|
|
assert!(marker.exists(), "the plugin must have run on the emitted event");
|
|
|
|
let _ = std::fs::remove_dir_all(&dir);
|
|
}
|
|
|
|
#[test]
|
|
fn plugins_list_and_test_are_available_in_cli() {
|
|
let cli = Cli::parse_from(["am", "plugins", "--test", "notify"]);
|
|
match cli.command {
|
|
Some(agent_manager::cli::Command::Plugins(args)) => {
|
|
assert_eq!(args.test.as_deref(), Some("notify"));
|
|
}
|
|
other => panic!("unexpected: {other:?}"),
|
|
}
|
|
let cli = Cli::parse_from(["am", "lab", "--agents", "a,b", "--task", "t.yaml"]);
|
|
match cli.command {
|
|
Some(agent_manager::cli::Command::Lab(args)) => {
|
|
assert_eq!(args.agents.as_deref(), Some("a,b"));
|
|
assert_eq!(args.task, Some(PathBuf::from("t.yaml")));
|
|
}
|
|
other => panic!("unexpected: {other:?}"),
|
|
}
|
|
}
|