Axe 8 (sécurité) : - am audit : checksums SHA-256 des configs (embarquée, utilisateur, locale) stockés dans state.json, croisement avec events.jsonl pour dater les changements, détection des modifications manuelles hors de am (statut MANUAL), rapport lisible + --json stable (contrat) - backup automatique avant chaque am update (agent ou --all) : état + config utilisateur + répertoires d'installation + shims dans <state>/backups/<ts>/ avec manifest.json - am update --rollback [point] : restaure le dernier backup (ou un id précis, préfixe accepté), arrête les agents concernés d'abord, événement EventKind::Rollback journalisé - am update --rollback list : liste les points de restauration - rétention settings.backups_keep (défaut 5) appliquée après chaque backup - événements EventKind::Backup / EventKind::Rollback pour l'audit REPL : commande audit + update --rollback (parse, complétion), help specs, tip cheat sheet, man pages régénérées (50 pages). Version 0.4.6, ROADMAP cases cochées, 272 tests verts.
70 lines
2.3 KiB
Rust
70 lines
2.3 KiB
Rust
use agent_manager::commands::update_cmd;
|
|
use agent_manager::config::UpdatePolicy;
|
|
use agent_manager::state::{InstalledEntry, StateFile};
|
|
use clap::Parser;
|
|
use std::collections::BTreeMap;
|
|
|
|
mod common;
|
|
|
|
fn installed_state(name: &str, version: &str) -> StateFile {
|
|
StateFile {
|
|
version: 3,
|
|
installed: BTreeMap::from([(
|
|
name.to_string(),
|
|
InstalledEntry {
|
|
name: name.to_string(),
|
|
version: Some(version.to_string()),
|
|
method: "binary".to_string(),
|
|
install_dir: "/tmp".to_string(),
|
|
bins: vec![name.to_string()],
|
|
run: name.to_string(),
|
|
installed_at: "2026-01-01T00:00:00Z".to_string(),
|
|
updated_at: None,
|
|
pid: None,
|
|
started_at: None,
|
|
},
|
|
)]),
|
|
sessions_count: 0,
|
|
last_used: None,
|
|
annotations: BTreeMap::new(),
|
|
config_checksums: BTreeMap::new(),
|
|
}
|
|
}
|
|
|
|
fn agent_yaml(name: &str, pin: Option<&str>) -> String {
|
|
let pin_line = pin
|
|
.map(|v| format!(" pin_version: \"{v}\"\n"))
|
|
.unwrap_or_default();
|
|
format!(
|
|
"agents:\n - name: {name}\n display_name: {name}\n run: \"{name}\"\n install:\n type: binary\n binary: {name}\n{pin_line}"
|
|
)
|
|
}
|
|
|
|
#[test]
|
|
fn update_policy_none_blocks_all() {
|
|
let body = agent_yaml("pi", None);
|
|
let mut app = common::test_app("policy_none", &["--yes"]);
|
|
app.config.settings.update_policy = Some(UpdatePolicy::None);
|
|
app.state.save(&installed_state("pi", "0.1.0")).unwrap();
|
|
let code = update_cmd::run(&app, None, true, None).unwrap();
|
|
assert_eq!(code, 0);
|
|
}
|
|
|
|
#[test]
|
|
fn pinned_agent_is_skipped() {
|
|
let body = agent_yaml("pi", Some("0.1.0"));
|
|
let app = common::test_app("pinned_ok", &[]);
|
|
app.state.save(&installed_state("pi", "0.1.0")).unwrap();
|
|
let code = update_cmd::run(&app, Some("pi"), false, None).unwrap();
|
|
assert_eq!(code, 0);
|
|
}
|
|
|
|
#[test]
|
|
fn pinned_agent_mismatch_warns() {
|
|
let body = agent_yaml("pi", Some("0.1.0"));
|
|
let app = common::test_app("pinned_warn", &[]);
|
|
app.state.save(&installed_state("pi", "0.2.0")).unwrap();
|
|
let code = update_cmd::run(&app, Some("pi"), false, None).unwrap();
|
|
assert_eq!(code, 0);
|
|
}
|