Download codex-rs/core/src/tools/approvals_tests.rs from SaylorTwift/codex: direct link, hf CLI and curl.
- Browser
- Download file 9.04 kB
-
https://huggingface.co/SaylorTwift/codex/resolve/main/codex-rs/core/src/tools/approvals_tests.rs
- Command line
-
hf download hf://SaylorTwift/codex/codex-rs/core/src/tools/approvals_tests.rs
-
curl -L -o approvals_tests.rs https://huggingface.co/SaylorTwift/codex/resolve/main/codex-rs/core/src/tools/approvals_tests.rs
9.04 kB
| use super::*; | |
| use crate::session::tests::make_session_and_context_with_rx; | |
| use codex_models_manager::model_info::model_info_from_slug; | |
| use codex_protocol::approvals::NetworkPolicyAmendment; | |
| use pretty_assertions::assert_eq; | |
| fn approval_resolution_rejects_denied_network_policy_amendment() { | |
| let resolution = ApprovalResolution { | |
| decision: ReviewDecision::NetworkPolicyAmendment { | |
| network_policy_amendment: NetworkPolicyAmendment { | |
| host: "denied.example.com".to_string(), | |
| action: NetworkPolicyRuleAction::Deny, | |
| }, | |
| }, | |
| source: ApprovalResolutionSource::User, | |
| }; | |
| assert!(matches!( | |
| resolution.into_tool_result(&model_info_from_slug("acting-model")), | |
| Err(ToolError::Rejected(rejection)) if rejection == "rejected by user" | |
| )); | |
| } | |
| fn approval_resolution_rejects_mcp_policy_amendment() { | |
| let resolution = ApprovalResolution { | |
| decision: ReviewDecision::ApprovedMcpPolicyAmendment, | |
| source: ApprovalResolutionSource::User, | |
| }; | |
| assert!(matches!( | |
| resolution.into_tool_result(&model_info_from_slug("acting-model")), | |
| Err(ToolError::Rejected(rejection)) if rejection == "Error while requesting approval" | |
| )); | |
| } | |
| fn approval_resolution_aborts_turn_when_approval_is_aborted() { | |
| let resolution = ApprovalResolution { | |
| decision: ReviewDecision::Abort, | |
| source: ApprovalResolutionSource::User, | |
| }; | |
| assert!(matches!( | |
| resolution.into_tool_result(&model_info_from_slug("acting-model")), | |
| Err(ToolError::Codex(error)) | |
| if matches!( | |
| error.details(), | |
| codex_protocol::error::CodexErrorDetails::TurnAborted | |
| ) | |
| )); | |
| } | |
| fn approval_resolution_uses_acting_model_timeout_instructions() { | |
| let mut model = model_info_from_slug("acting-model"); | |
| for timeout_instructions in ["Catalog timeout instructions.", ""] { | |
| model.model_messages = Some( | |
| serde_json::from_value(serde_json::json!({ | |
| "auto_review": { | |
| "timeout_instructions": timeout_instructions, | |
| }, | |
| })) | |
| .expect("model messages should deserialize"), | |
| ); | |
| let resolution = ApprovalResolution { | |
| decision: ReviewDecision::TimedOut, | |
| source: ApprovalResolutionSource::Guardian, | |
| }; | |
| assert!(matches!( | |
| resolution.into_tool_result(&model), | |
| Err(ToolError::Rejected(rejection)) if rejection == timeout_instructions | |
| )); | |
| } | |
| } | |
| async fn non_utf8_cwd_preserves_approval_routing( | |
| reviewer: ApprovalsReviewer, | |
| decision: codex_extension_api::ApprovalDecision, | |
| ) -> anyhow::Result<()> { | |
| use anyhow::Context; | |
| use codex_extension_api::ApprovalDecision; | |
| use std::os::unix::ffi::OsStringExt; | |
| struct Contributor { | |
| cwd: codex_utils_path_uri::LegacyAppPathString, | |
| decision: ApprovalDecision, | |
| } | |
| impl codex_extension_api::ApprovalReviewContributor for Contributor { | |
| fn decide<'a>( | |
| &'a self, | |
| input: &'a codex_extension_api::ApprovalDecisionInput<'_>, | |
| ) -> codex_extension_api::ExtensionFuture<'a, Option<ApprovalDecision>> { | |
| Box::pin(async move { | |
| assert_eq!(input.action["cwd"], serde_json::json!(self.cwd)); | |
| Some(self.decision.clone()) | |
| }) | |
| } | |
| } | |
| let cwd = PathUri::from_abs_path(&AbsolutePathBuf::try_from(PathBuf::from( | |
| std::ffi::OsString::from_vec(b"/tmp/non-utf8-\xe9".to_vec()), | |
| ))?); | |
| let (mut session, turn, events) = make_session_and_context_with_rx().await; | |
| let mut extensions = codex_extension_api::ExtensionRegistryBuilder::new(); | |
| extensions.approval_review_contributor(Arc::new(Contributor { | |
| cwd: codex_utils_path_uri::LegacyAppPathString::from_path_uri(&cwd, PathConvention::Posix)?, | |
| decision, | |
| })); | |
| Arc::get_mut(&mut session) | |
| .context("session is uniquely owned")? | |
| .services | |
| .extensions = Arc::new(extensions.build()); | |
| *session.active_turn.lock().await = Some(crate::state::ActiveTurn::default()); | |
| let mut review_context = GuardianReviewContext::from(&turn); | |
| review_context.approval_policy = AskForApproval::OnRequest; | |
| review_context.approvals_reviewer = reviewer; | |
| let context = ApprovalContext { | |
| review_context, | |
| cancellation_token: None, | |
| call_id: "non-utf8-cwd".to_string(), | |
| tool_name: ToolName::plain("exec_command"), | |
| strict_auto_review: false, | |
| approval_reason: None, | |
| retry_reason: None, | |
| network_approval_context: None, | |
| }; | |
| let action = ApprovalAction::ExecCommand { | |
| id: context.call_id.clone(), | |
| environment_id: codex_exec_server::LOCAL_ENVIRONMENT_ID.to_string(), | |
| command: vec!["npm".to_string(), "install".to_string()], | |
| hook_command: "npm install".to_string(), | |
| cwd: cwd.clone(), | |
| sandbox_permissions: if reviewer == ApprovalsReviewer::User { | |
| SandboxPermissions::RequireEscalated | |
| } else { | |
| SandboxPermissions::UseDefault | |
| }, | |
| additional_permissions: None, | |
| justification: None, | |
| tty: false, | |
| proposed_execpolicy_amendment: None, | |
| }; | |
| let approval = session.request_reviewer_approval(action, &context); | |
| tokio::pin!(approval); | |
| let expected = if reviewer == ApprovalsReviewer::User { | |
| tokio::select! { | |
| resolution = &mut approval => panic!("expected a user prompt, got {resolution:?}"), | |
| event = events.recv() => { | |
| let codex_protocol::protocol::EventMsg::ExecApprovalRequest(request) = | |
| event.context("receive user prompt")?.msg | |
| else { | |
| panic!("expected a command approval prompt"); | |
| }; | |
| assert_eq!(request.cwd, codex_utils_path_uri::LegacyAppPathString::from(cwd)); | |
| assert_eq!(request.command, vec!["npm", "install"]); | |
| session.notify_approval(&request.call_id, ReviewDecision::Approved).await; | |
| } | |
| } | |
| ApprovalResolution { | |
| decision: ReviewDecision::Approved, | |
| source: ApprovalResolutionSource::User, | |
| } | |
| } else { | |
| ApprovalResolution { | |
| decision: ReviewDecision::Approved, | |
| source: ApprovalResolutionSource::Guardian, | |
| } | |
| }; | |
| assert_eq!(approval.await, expected); | |
| assert!(events.try_recv().is_err()); | |
| Ok(()) | |
| } | |
| async fn explicit_mcp_reviewer_override_takes_precedence_over_action_context() { | |
| let (session, turn, events) = make_session_and_context_with_rx().await; | |
| let action = ApprovalAction::McpToolCall { | |
| id: "mcp-override".to_string(), | |
| server: "example".to_string(), | |
| tool_name: "dangerous".to_string(), | |
| arguments: None, | |
| connector_id: None, | |
| connector_name: None, | |
| connector_description: None, | |
| connected_account_email: None, | |
| tool_title: None, | |
| tool_description: None, | |
| annotations: None, | |
| hook_tool_name: HookToolName::new("mcp__example__dangerous"), | |
| approval_policy: AskForApproval::OnRequest, | |
| reviewer: ApprovalsReviewer::User, | |
| approval_mode: AppToolApproval::Prompt, | |
| allow_session_remember: false, | |
| allow_persistent_approval: false, | |
| }; | |
| let mut review_context = GuardianReviewContext::from(&turn); | |
| review_context.approval_policy = AskForApproval::OnRequest; | |
| review_context.approvals_reviewer = ApprovalsReviewer::AutoReview; | |
| let context = ApprovalContext { | |
| review_context, | |
| cancellation_token: None, | |
| call_id: "mcp-override".to_string(), | |
| tool_name: ToolName::plain("dangerous"), | |
| strict_auto_review: false, | |
| approval_reason: None, | |
| retry_reason: None, | |
| network_approval_context: None, | |
| }; | |
| tokio::select! { | |
| resolution = session.request_reviewer_approval(action, &context) => { | |
| panic!("expected a user approval request, got {resolution:?}"); | |
| } | |
| event = events.recv() => { | |
| let codex_protocol::protocol::EventMsg::ElicitationRequest(request) = | |
| event.expect("receive user approval request").msg | |
| else { | |
| panic!("expected an MCP user approval request"); | |
| }; | |
| assert_eq!(request.server_name, "example"); | |
| assert_eq!( | |
| request.id, | |
| codex_protocol::mcp::RequestId::String( | |
| "mcp_tool_call_approval_mcp-override".to_string() | |
| ) | |
| ); | |
| } | |
| } | |
| } | |