use decapod::core::store::{Store, StoreKind};
use decapod::plugins::policy::{
RiskLevel, RiskMap, RiskZone, approve_action, check_approval, derive_fingerprint, eval_risk,
initialize_policy_db, is_high_risk,
};
use tempfile::tempdir;
#[test]
fn test_eval_risk() {
let risk_map = RiskMap {
zones: vec![RiskZone {
path: ".decapod/".to_string(),
level: RiskLevel::CRITICAL,
rules: vec!["NO_AGENT_WRITE".to_string()],
}],
};
let (level, _) = eval_risk("todo.delete", None, &risk_map);
assert_eq!(level, RiskLevel::HIGH);
let (level, _) = eval_risk("todo.add", Some(".decapod/todo.db"), &risk_map);
assert_eq!(level, RiskLevel::CRITICAL);
let (level, _) = eval_risk("todo.list", Some("src/main.rs"), &risk_map);
assert_eq!(level, RiskLevel::LOW);
}
#[test]
fn test_eval_risk_commands() {
let risk_map = RiskMap { zones: vec![] };
let (level, reqs) = eval_risk("delete", None, &risk_map);
assert_eq!(level, RiskLevel::HIGH);
assert!(!reqs.is_empty());
let (level, _reqs) = eval_risk("archive", None, &risk_map);
assert_eq!(level, RiskLevel::HIGH);
let (level, _) = eval_risk("purge", None, &risk_map);
assert_eq!(level, RiskLevel::HIGH);
}
#[test]
fn test_eval_risk_zones() {
let risk_map = RiskMap {
zones: vec![
RiskZone {
path: "docs/specs/".to_string(),
level: RiskLevel::HIGH,
rules: vec!["OPERATOR_REVIEW_REQUIRED".to_string()],
},
RiskZone {
path: ".decapod/".to_string(),
level: RiskLevel::CRITICAL,
rules: vec!["NO_AGENT_WRITE".to_string()],
},
],
};
let (level, reqs) = eval_risk("todo.edit", Some("docs/specs/INTENT.md"), &risk_map);
assert_eq!(level, RiskLevel::HIGH);
assert!(reqs.iter().any(|r| r.contains("OPERATOR_REVIEW_REQUIRED")));
let (level, _) = eval_risk("todo.add", Some(".decapod/data/todo.db"), &risk_map);
assert_eq!(level, RiskLevel::CRITICAL);
}
#[test]
fn test_is_high_risk() {
assert!(!is_high_risk(RiskLevel::LOW));
assert!(!is_high_risk(RiskLevel::MEDIUM));
assert!(is_high_risk(RiskLevel::HIGH));
assert!(is_high_risk(RiskLevel::CRITICAL));
}
#[test]
fn test_risk_level_values() {
assert_eq!(RiskLevel::LOW as u8, 0);
assert_eq!(RiskLevel::MEDIUM as u8, 1);
assert_eq!(RiskLevel::HIGH as u8, 2);
assert_eq!(RiskLevel::CRITICAL as u8, 3);
}
#[test]
fn test_derive_fingerprint() {
let fp1 = derive_fingerprint("todo.add", Some("src/main.rs"), "repo");
let fp2 = derive_fingerprint("todo.add", Some("src/main.rs"), "repo");
let fp3 = derive_fingerprint("todo.add", Some("src/other.rs"), "repo");
let fp4 = derive_fingerprint("todo.add", Some("src/main.rs"), "user");
assert_eq!(fp1, fp2);
assert_ne!(fp1, fp3);
assert_ne!(fp1, fp4);
}
#[test]
fn test_approval_lifecycle() {
let tmp = tempdir().unwrap();
let store = Store {
kind: StoreKind::User,
root: tmp.path().to_path_buf(),
};
initialize_policy_db(&store.root).unwrap();
let cmd = "todo.archive";
let path = Some("docs/specs/INTENT.md");
assert!(!check_approval(&store, cmd, path, "global").unwrap());
approve_action(&store, cmd, path, "operator", "global").unwrap();
assert!(check_approval(&store, cmd, path, "global").unwrap());
assert!(!check_approval(&store, cmd, path, "local").unwrap());
}
#[test]
fn test_list_approvals() {
let tmp = tempdir().unwrap();
let store = Store {
kind: StoreKind::User,
root: tmp.path().to_path_buf(),
};
initialize_policy_db(&store.root).unwrap();
approve_action(&store, "cmd1", None, "user1", "global").unwrap();
approve_action(&store, "cmd2", Some("path/to/file"), "user2", "repo").unwrap();
}
#[test]
fn test_approval_different_scopes() {
let tmp = tempdir().unwrap();
let store = Store {
kind: StoreKind::User,
root: tmp.path().to_path_buf(),
};
initialize_policy_db(&store.root).unwrap();
let cmd = "todo.delete";
let path = Some("docs/");
approve_action(&store, cmd, path, "operator", "global").unwrap();
assert!(check_approval(&store, cmd, path, "global").unwrap());
assert!(!check_approval(&store, cmd, path, "docs").unwrap());
}