use super::*; use crate::McpServerOAuthConfig; use crate::McpServerToolConfig; use pretty_assertions::assert_eq; use std::collections::HashMap; use std::time::SystemTime; use std::time::UNIX_EPOCH; #[tokio::test] async fn replace_mcp_servers_serializes_per_tool_approval_overrides() -> anyhow::Result<()> { let unique_suffix = SystemTime::now().duration_since(UNIX_EPOCH)?.as_nanos(); let codex_home = std::env::temp_dir().join(format!( "codex-config-mcp-edit-test-{}-{unique_suffix}", std::process::id() )); let servers = BTreeMap::from([( "docs".to_string(), McpServerConfig { transport: McpServerTransportConfig::Stdio { command: "docs-server".to_string(), args: Vec::new(), env: None, env_vars: Vec::new(), cwd: None, }, environment_id: crate::DEFAULT_MCP_SERVER_ENVIRONMENT_ID.to_string(), enabled: true, required: false, supports_parallel_tool_calls: true, disabled_reason: None, startup_timeout_sec: None, tool_timeout_sec: None, default_tools_approval_mode: Some(AppToolApproval::Auto), enabled_tools: None, disabled_tools: None, scopes: None, oauth: None, oauth_resource: None, tools: HashMap::from([ ( "search".to_string(), McpServerToolConfig { approval_mode: Some(AppToolApproval::Approve), }, ), ( "read".to_string(), McpServerToolConfig { approval_mode: Some(AppToolApproval::Prompt), }, ), ]), }, )]); ConfigEditsBuilder::new(&codex_home) .replace_mcp_servers(&servers) .apply() .await?; let config_path = codex_home.join(CONFIG_TOML_FILE); let serialized = std::fs::read_to_string(&config_path)?; assert_eq!( serialized, r#"[mcp_servers.docs] command = "docs-server" supports_parallel_tool_calls = true default_tools_approval_mode = "auto" [mcp_servers.docs.tools] [mcp_servers.docs.tools.read] approval_mode = "prompt" [mcp_servers.docs.tools.search] approval_mode = "approve" "# ); let loaded = load_global_mcp_servers(&codex_home).await?; assert_eq!(loaded, servers); std::fs::remove_dir_all(&codex_home)?; Ok(()) } #[tokio::test] async fn replace_mcp_servers_serializes_oauth_client_id() -> anyhow::Result<()> { let unique_suffix = SystemTime::now().duration_since(UNIX_EPOCH)?.as_nanos(); let codex_home = std::env::temp_dir().join(format!( "codex-config-mcp-oauth-edit-test-{}-{unique_suffix}", std::process::id() )); let servers = BTreeMap::from([( "maas_outlook".to_string(), McpServerConfig { transport: McpServerTransportConfig::StreamableHttp { url: "https://example.com/mcp".to_string(), bearer_token_env_var: None, http_headers: None, env_http_headers: None, }, environment_id: crate::DEFAULT_MCP_SERVER_ENVIRONMENT_ID.to_string(), enabled: true, required: false, supports_parallel_tool_calls: false, disabled_reason: None, startup_timeout_sec: None, tool_timeout_sec: None, default_tools_approval_mode: None, enabled_tools: None, disabled_tools: None, scopes: None, oauth: Some(McpServerOAuthConfig { client_id: Some("eci-prd-pub-codex-123".to_string()), }), oauth_resource: None, tools: HashMap::new(), }, )]); ConfigEditsBuilder::new(&codex_home) .replace_mcp_servers(&servers) .apply() .await?; let config_path = codex_home.join(CONFIG_TOML_FILE); let serialized = std::fs::read_to_string(&config_path)?; assert_eq!( serialized, r#"[mcp_servers.maas_outlook] url = "https://example.com/mcp" [mcp_servers.maas_outlook.oauth] client_id = "eci-prd-pub-codex-123" "# ); let loaded = load_global_mcp_servers(&codex_home).await?; assert_eq!(loaded, servers); std::fs::remove_dir_all(&codex_home)?; Ok(()) }