use agent_client_protocol::schema::v1::{
PermissionOptionKind, RequestPermissionOutcome, SelectedPermissionOutcome, SessionId,
};
use pretty_assertions::assert_eq;
use rho_sdk::{ApprovalDecision, ApprovalRequest, CapabilityRequest, CapabilitySource, PathScope};
use std::sync::atomic::AtomicU64;
use super::{
decision_for, mode_id, next_placeholder_tool_call_id, parse_mode_id, permission_request,
};
use crate::permission::PermissionMode;
fn session_id() -> SessionId {
SessionId::new("session-1")
}
fn write_request(reason: &str) -> ApprovalRequest {
ApprovalRequest::new(
CapabilityRequest::write_path(
"/workspace/file.rs",
PathScope::PrimaryWorkspace,
CapabilitySource::built_in_tool("write"),
),
reason,
)
}
#[test]
fn permission_request_offers_three_session_scoped_options() {
let request = permission_request(&session_id(), &write_request("edit file"), "pending-1");
let options = request
.options
.iter()
.map(|option| (option.option_id.0.as_ref(), option.kind))
.collect::<Vec<_>>();
assert_eq!(
options,
[
("allow_once", PermissionOptionKind::AllowOnce),
("allow_always", PermissionOptionKind::AllowAlways),
("reject_once", PermissionOptionKind::RejectOnce),
]
);
assert_eq!(request.tool_call.tool_call_id.0.as_ref(), "pending-1");
}
#[test]
fn missing_tool_call_ids_do_not_reuse_a_placeholder() {
let counter = AtomicU64::new(0);
let first = permission_request(
&session_id(),
&write_request("first"),
&next_placeholder_tool_call_id(&counter),
);
let second = permission_request(
&session_id(),
&write_request("second"),
&next_placeholder_tool_call_id(&counter),
);
assert_ne!(
first.tool_call.tool_call_id.0.as_ref(),
second.tool_call.tool_call_id.0.as_ref()
);
}
#[test]
fn decision_for_maps_selected_cancelled_and_unknown() {
let cases = [
(
RequestPermissionOutcome::Cancelled,
ApprovalDecision::Deny {
reason: "permission request cancelled".into(),
},
),
(
RequestPermissionOutcome::Selected(SelectedPermissionOutcome::new("allow_once")),
ApprovalDecision::AllowOnce,
),
(
RequestPermissionOutcome::Selected(SelectedPermissionOutcome::new("allow_always")),
ApprovalDecision::AllowForSession,
),
(
RequestPermissionOutcome::Selected(SelectedPermissionOutcome::new("reject_once")),
ApprovalDecision::Deny {
reason: "permission rejected".into(),
},
),
(
RequestPermissionOutcome::Selected(SelectedPermissionOutcome::new("reject_always")),
ApprovalDecision::Deny {
reason: "unknown permission option".into(),
},
),
];
for (outcome, expected) in cases {
assert_eq!(decision_for(&outcome), expected);
}
}
#[test]
fn mode_ids_round_trip_every_permission_mode() {
for mode in PermissionMode::ALL {
assert_eq!(parse_mode_id(mode_id(mode).0.as_ref()), Some(mode));
}
assert_eq!(parse_mode_id("unknown"), None);
}