diff --git a/codex-rs/core/src/config/config_tests.rs b/codex-rs/core/src/config/config_tests.rs index 80e369f696..3bf523d32d 100644 --- a/codex-rs/core/src/config/config_tests.rs +++ b/codex-rs/core/src/config/config_tests.rs @@ -16,7 +16,6 @@ use crate::config::types::Notifications; use crate::config::types::ToolSuggestDiscoverableType; use crate::config_loader::RequirementSource; use crate::plugins::PluginsManager; -use assert_matches::assert_matches; use codex_config::CONFIG_TOML_FILE; use codex_features::Feature; use codex_features::FeaturesToml; @@ -26,7 +25,6 @@ use codex_protocol::permissions::FileSystemSandboxEntry; use codex_protocol::permissions::FileSystemSandboxPolicy; use codex_protocol::permissions::FileSystemSpecialPath; use codex_protocol::permissions::NetworkSandboxPolicy; -use serde::Deserialize; use tempfile::tempdir; use super::*; @@ -225,25 +223,6 @@ web_search = true ); } -#[test] -fn tools_web_search_false_deserializes_to_none() { - let cfg: ConfigToml = toml::from_str( - r#" -[tools] -web_search = false -"#, - ) - .expect("TOML deserialization should succeed"); - - assert_eq!( - cfg.tools, - Some(ToolsToml { - web_search: None, - view_image: None, - }) - ); -} - #[test] fn rejects_provider_auth_with_env_key() { let err = toml::from_str::( @@ -5300,102 +5279,62 @@ fn derive_sandbox_policy_preserves_windows_downgrade_for_unsupported_fallback() } #[test] -fn test_resolve_oss_provider_explicit_override() { - let config_toml = ConfigToml::default(); - let result = resolve_oss_provider( - Some("custom-provider"), - &config_toml, - /*config_profile*/ None, - ); - assert_eq!(result, Some("custom-provider".to_string())); -} - -#[test] -fn test_resolve_oss_provider_from_profile() { - let mut profiles = std::collections::HashMap::new(); - let profile = ConfigProfile { - oss_provider: Some("profile-provider".to_string()), - ..Default::default() - }; - profiles.insert("test-profile".to_string(), profile); - let config_toml = ConfigToml { - profiles, - ..Default::default() - }; - - let result = resolve_oss_provider( - /*explicit_provider*/ None, - &config_toml, - Some("test-profile".to_string()), - ); - assert_eq!(result, Some("profile-provider".to_string())); -} - -#[test] -fn test_resolve_oss_provider_from_global_config() { - let config_toml = ConfigToml { +fn resolve_oss_provider_honors_explicit_profile_and_global_precedence() { + let profile_config = ConfigToml { oss_provider: Some("global-provider".to_string()), + profiles: HashMap::from([( + "profile-with-provider".to_string(), + ConfigProfile { + oss_provider: Some("profile-provider".to_string()), + ..Default::default() + }, + )]), ..Default::default() }; - - let result = resolve_oss_provider( - /*explicit_provider*/ None, - &config_toml, - /*config_profile*/ None, - ); - assert_eq!(result, Some("global-provider".to_string())); -} - -#[test] -fn test_resolve_oss_provider_profile_fallback_to_global() { - let mut profiles = std::collections::HashMap::new(); - let profile = ConfigProfile::default(); // No oss_provider set - profiles.insert("test-profile".to_string(), profile); - let config_toml = ConfigToml { + let profile_without_provider_config = ConfigToml { oss_provider: Some("global-provider".to_string()), - profiles, + profiles: HashMap::from([( + "profile-without-provider".to_string(), + ConfigProfile::default(), + )]), ..Default::default() }; - let result = resolve_oss_provider( - /*explicit_provider*/ None, - &config_toml, - Some("test-profile".to_string()), - ); - assert_eq!(result, Some("global-provider".to_string())); -} - -#[test] -fn test_resolve_oss_provider_none_when_not_configured() { - let config_toml = ConfigToml::default(); - let result = resolve_oss_provider( - /*explicit_provider*/ None, - &config_toml, - /*config_profile*/ None, - ); - assert_eq!(result, None); -} - -#[test] -fn test_resolve_oss_provider_explicit_overrides_all() { - let mut profiles = std::collections::HashMap::new(); - let profile = ConfigProfile { - oss_provider: Some("profile-provider".to_string()), - ..Default::default() - }; - profiles.insert("test-profile".to_string(), profile); - let config_toml = ConfigToml { - oss_provider: Some("global-provider".to_string()), - profiles, - ..Default::default() - }; - - let result = resolve_oss_provider( - Some("explicit-provider"), - &config_toml, - Some("test-profile".to_string()), - ); - assert_eq!(result, Some("explicit-provider".to_string())); + for (explicit_provider, config_toml, config_profile, expected) in [ + ( + Some("explicit-provider"), + profile_config.clone(), + Some("profile-with-provider".to_string()), + Some("explicit-provider".to_string()), + ), + ( + None, + profile_config, + Some("profile-with-provider".to_string()), + Some("profile-provider".to_string()), + ), + ( + None, + profile_without_provider_config, + Some("profile-without-provider".to_string()), + Some("global-provider".to_string()), + ), + ( + None, + ConfigToml { + oss_provider: Some("global-provider".to_string()), + ..Default::default() + }, + None, + Some("global-provider".to_string()), + ), + (None, ConfigToml::default(), None, None), + ] { + assert_eq!( + resolve_oss_provider(explicit_provider, &config_toml, config_profile), + expected + ); + } } #[test] @@ -6237,50 +6176,3 @@ speaker = "Desk Speakers" ); Ok(()) } - -#[derive(Deserialize, Debug, PartialEq)] -struct TuiTomlTest { - #[serde(default)] - notifications: Notifications, - #[serde(default)] - notification_method: NotificationMethod, -} - -#[derive(Deserialize, Debug, PartialEq)] -struct RootTomlTest { - tui: TuiTomlTest, -} - -#[test] -fn test_tui_notifications_true() { - let toml = r#" - [tui] - notifications = true - "#; - let parsed: RootTomlTest = toml::from_str(toml).expect("deserialize notifications=true"); - assert_matches!(parsed.tui.notifications, Notifications::Enabled(true)); -} - -#[test] -fn test_tui_notifications_custom_array() { - let toml = r#" - [tui] - notifications = ["foo"] - "#; - let parsed: RootTomlTest = toml::from_str(toml).expect("deserialize notifications=[\"foo\"]"); - assert_matches!( - parsed.tui.notifications, - Notifications::Custom(ref v) if v == &vec!["foo".to_string()] - ); -} - -#[test] -fn test_tui_notification_method() { - let toml = r#" - [tui] - notification_method = "bel" - "#; - let parsed: RootTomlTest = - toml::from_str(toml).expect("deserialize notification_method=\"bel\""); - assert_eq!(parsed.tui.notification_method, NotificationMethod::Bel); -} diff --git a/codex-rs/core/src/error_tests.rs b/codex-rs/core/src/error_tests.rs index 623831b2cf..eb5d9b6a22 100644 --- a/codex-rs/core/src/error_tests.rs +++ b/codex-rs/core/src/error_tests.rs @@ -182,20 +182,6 @@ fn usage_limit_reached_error_formats_free_plan() { ); } -#[test] -fn usage_limit_reached_error_formats_go_plan() { - let err = UsageLimitReachedError { - plan_type: Some(PlanType::Known(KnownPlan::Go)), - resets_at: None, - rate_limits: Some(Box::new(rate_limit_snapshot())), - promo_message: None, - }; - assert_eq!( - err.to_string(), - "You've hit your usage limit. Upgrade to Plus to continue using Codex (https://chatgpt.com/explore/plus), or try again later." - ); -} - #[test] fn usage_limit_reached_error_formats_default_when_none() { let err = UsageLimitReachedError { @@ -243,34 +229,6 @@ fn usage_limit_reached_error_formats_business_plan_without_reset() { ); } -#[test] -fn usage_limit_reached_error_formats_self_serve_business_usage_based_plan() { - let err = UsageLimitReachedError { - plan_type: Some(PlanType::Known(KnownPlan::SelfServeBusinessUsageBased)), - resets_at: None, - rate_limits: Some(Box::new(rate_limit_snapshot())), - promo_message: None, - }; - assert_eq!( - err.to_string(), - "You've hit your usage limit. To get more access now, send a request to your admin or try again later." - ); -} - -#[test] -fn usage_limit_reached_error_formats_enterprise_cbp_usage_based_plan() { - let err = UsageLimitReachedError { - plan_type: Some(PlanType::Known(KnownPlan::EnterpriseCbpUsageBased)), - resets_at: None, - rate_limits: Some(Box::new(rate_limit_snapshot())), - promo_message: None, - }; - assert_eq!( - err.to_string(), - "You've hit your usage limit. To get more access now, send a request to your admin or try again later." - ); -} - #[test] fn usage_limit_reached_error_formats_default_for_other_plans() { let err = UsageLimitReachedError { diff --git a/codex-rs/core/src/text_encoding_tests.rs b/codex-rs/core/src/text_encoding_tests.rs index 6368f38be4..2cb7e4299b 100644 --- a/codex-rs/core/src/text_encoding_tests.rs +++ b/codex-rs/core/src/text_encoding_tests.rs @@ -1,26 +1,5 @@ use super::*; -use encoding_rs::BIG5; -use encoding_rs::EUC_KR; -use encoding_rs::GBK; -use encoding_rs::ISO_8859_2; -use encoding_rs::ISO_8859_3; -use encoding_rs::ISO_8859_4; -use encoding_rs::ISO_8859_5; -use encoding_rs::ISO_8859_6; -use encoding_rs::ISO_8859_7; -use encoding_rs::ISO_8859_8; -use encoding_rs::ISO_8859_10; -use encoding_rs::ISO_8859_13; use encoding_rs::SHIFT_JIS; -use encoding_rs::WINDOWS_874; -use encoding_rs::WINDOWS_1250; -use encoding_rs::WINDOWS_1251; -use encoding_rs::WINDOWS_1253; -use encoding_rs::WINDOWS_1254; -use encoding_rs::WINDOWS_1255; -use encoding_rs::WINDOWS_1256; -use encoding_rs::WINDOWS_1257; -use encoding_rs::WINDOWS_1258; use pretty_assertions::assert_eq; #[test] @@ -98,194 +77,6 @@ fn test_windows_1252_privet_gibberish_is_preserved() { assert_eq!(bytes_to_string_smart(bytes), "Привет"); } -#[test] -fn test_iso8859_1_latin_text() { - // ISO-8859-1 (code page 28591) is the Latin segment used by LatArCyrHeb. - // encoding_rs unifies ISO-8859-1 with Windows-1252, so reuse that constant here. - let (encoded, _, had_errors) = WINDOWS_1252.encode("Hello"); - assert!(!had_errors, "failed to encode Latin sample"); - assert_eq!(bytes_to_string_smart(encoded.as_ref()), "Hello"); -} - -#[test] -fn test_iso8859_2_central_european_text() { - // ISO-8859-2 (code page 28592) covers additional Central European glyphs. - let (encoded, _, had_errors) = ISO_8859_2.encode("Příliš žluťoučký kůň"); - assert!(!had_errors, "failed to encode ISO-8859-2 sample"); - assert_eq!( - bytes_to_string_smart(encoded.as_ref()), - "Příliš žluťoučký kůň" - ); -} - -#[test] -fn test_iso8859_3_south_europe_text() { - // ISO-8859-3 (code page 28593) adds support for Maltese/Esperanto letters. - // chardetng rarely distinguishes ISO-8859-3 from neighboring Latin code pages, so we rely on - // an ASCII-only sample to ensure round-tripping still succeeds. - let (encoded, _, had_errors) = ISO_8859_3.encode("Esperanto and Maltese"); - assert!(!had_errors, "failed to encode ISO-8859-3 sample"); - assert_eq!( - bytes_to_string_smart(encoded.as_ref()), - "Esperanto and Maltese" - ); -} - -#[test] -fn test_iso8859_4_baltic_text() { - // ISO-8859-4 (code page 28594) targets the Baltic/Nordic repertoire. - let sample = "Šis ir rakstzīmju kodēšanas tests. Dažās valodās, kurās tiek \ - izmantotas latīņu valodas burti, lēmuma pieņemšanai mums ir nepieciešams \ - vairāk ieguldījuma."; - let (encoded, _, had_errors) = ISO_8859_4.encode(sample); - assert!(!had_errors, "failed to encode ISO-8859-4 sample"); - assert_eq!(bytes_to_string_smart(encoded.as_ref()), sample); -} - -#[test] -fn test_iso8859_5_cyrillic_text() { - // ISO-8859-5 (code page 28595) covers the Cyrillic portion. - let (encoded, _, had_errors) = ISO_8859_5.encode("Привет"); - assert!(!had_errors, "failed to encode Cyrillic sample"); - assert_eq!(bytes_to_string_smart(encoded.as_ref()), "Привет"); -} - -#[test] -fn test_iso8859_6_arabic_text() { - // ISO-8859-6 (code page 28596) covers the Arabic glyphs. - let (encoded, _, had_errors) = ISO_8859_6.encode("مرحبا"); - assert!(!had_errors, "failed to encode Arabic sample"); - assert_eq!(bytes_to_string_smart(encoded.as_ref()), "مرحبا"); -} - -#[test] -fn test_iso8859_7_greek_text() { - // ISO-8859-7 (code page 28597) is used for Greek locales. - let (encoded, _, had_errors) = ISO_8859_7.encode("Καλημέρα"); - assert!(!had_errors, "failed to encode ISO-8859-7 sample"); - assert_eq!(bytes_to_string_smart(encoded.as_ref()), "Καλημέρα"); -} - -#[test] -fn test_iso8859_8_hebrew_text() { - // ISO-8859-8 (code page 28598) covers the Hebrew glyphs. - let (encoded, _, had_errors) = ISO_8859_8.encode("שלום"); - assert!(!had_errors, "failed to encode Hebrew sample"); - assert_eq!(bytes_to_string_smart(encoded.as_ref()), "שלום"); -} - -#[test] -fn test_iso8859_9_turkish_text() { - // ISO-8859-9 (code page 28599) mirrors Latin-1 but inserts Turkish letters. - // encoding_rs exposes the equivalent Windows-1254 mapping. - let (encoded, _, had_errors) = WINDOWS_1254.encode("İstanbul"); - assert!(!had_errors, "failed to encode ISO-8859-9 sample"); - assert_eq!(bytes_to_string_smart(encoded.as_ref()), "İstanbul"); -} - -#[test] -fn test_iso8859_10_nordic_text() { - // ISO-8859-10 (code page 28600) adds additional Nordic letters. - let sample = "Þetta er prófun fyrir Ægir og Øystein."; - let (encoded, _, had_errors) = ISO_8859_10.encode(sample); - assert!(!had_errors, "failed to encode ISO-8859-10 sample"); - assert_eq!(bytes_to_string_smart(encoded.as_ref()), sample); -} - -#[test] -fn test_iso8859_11_thai_text() { - // ISO-8859-11 (code page 28601) mirrors TIS-620 / Windows-874 for Thai. - let sample = "ภาษาไทยสำหรับการทดสอบ ISO-8859-11"; - // encoding_rs exposes the equivalent Windows-874 encoding, so use that constant. - let (encoded, _, had_errors) = WINDOWS_874.encode(sample); - assert!(!had_errors, "failed to encode ISO-8859-11 sample"); - assert_eq!(bytes_to_string_smart(encoded.as_ref()), sample); -} - -// ISO-8859-12 was never standardized, and encodings 14–16 cannot be distinguished reliably -// without the heuristics we removed (chardetng generally reports neighboring Latin pages), so -// we intentionally omit coverage for those slots until the detector can identify them. - -#[test] -fn test_iso8859_13_baltic_text() { - // ISO-8859-13 (code page 28603) is common across Baltic languages. - let (encoded, _, had_errors) = ISO_8859_13.encode("Sveiki"); - assert!(!had_errors, "failed to encode ISO-8859-13 sample"); - assert_eq!(bytes_to_string_smart(encoded.as_ref()), "Sveiki"); -} - -#[test] -fn test_windows_1250_central_european_text() { - let (encoded, _, had_errors) = WINDOWS_1250.encode("Příliš žluťoučký kůň"); - assert!(!had_errors, "failed to encode Central European sample"); - assert_eq!( - bytes_to_string_smart(encoded.as_ref()), - "Příliš žluťoučký kůň" - ); -} - -#[test] -fn test_windows_1251_encoded_text() { - let (encoded, _, had_errors) = WINDOWS_1251.encode("Привет из Windows-1251"); - assert!(!had_errors, "failed to encode Windows-1251 sample"); - assert_eq!( - bytes_to_string_smart(encoded.as_ref()), - "Привет из Windows-1251" - ); -} - -#[test] -fn test_windows_1253_greek_text() { - let (encoded, _, had_errors) = WINDOWS_1253.encode("Γειά σου"); - assert!(!had_errors, "failed to encode Greek sample"); - assert_eq!(bytes_to_string_smart(encoded.as_ref()), "Γειά σου"); -} - -#[test] -fn test_windows_1254_turkish_text() { - let (encoded, _, had_errors) = WINDOWS_1254.encode("İstanbul"); - assert!(!had_errors, "failed to encode Turkish sample"); - assert_eq!(bytes_to_string_smart(encoded.as_ref()), "İstanbul"); -} - -#[test] -fn test_windows_1255_hebrew_text() { - let (encoded, _, had_errors) = WINDOWS_1255.encode("שלום"); - assert!(!had_errors, "failed to encode Windows-1255 Hebrew sample"); - assert_eq!(bytes_to_string_smart(encoded.as_ref()), "שלום"); -} - -#[test] -fn test_windows_1256_arabic_text() { - let (encoded, _, had_errors) = WINDOWS_1256.encode("مرحبا"); - assert!(!had_errors, "failed to encode Windows-1256 Arabic sample"); - assert_eq!(bytes_to_string_smart(encoded.as_ref()), "مرحبا"); -} - -#[test] -fn test_windows_1257_baltic_text() { - let (encoded, _, had_errors) = WINDOWS_1257.encode("Pērkons"); - assert!(!had_errors, "failed to encode Baltic sample"); - assert_eq!(bytes_to_string_smart(encoded.as_ref()), "Pērkons"); -} - -#[test] -fn test_windows_1258_vietnamese_text() { - let (encoded, _, had_errors) = WINDOWS_1258.encode("Xin chào"); - assert!(!had_errors, "failed to encode Vietnamese sample"); - assert_eq!(bytes_to_string_smart(encoded.as_ref()), "Xin chào"); -} - -#[test] -fn test_windows_874_thai_text() { - let (encoded, _, had_errors) = WINDOWS_874.encode("สวัสดีครับ นี่คือการทดสอบภาษาไทย"); - assert!(!had_errors, "failed to encode Thai sample"); - assert_eq!( - bytes_to_string_smart(encoded.as_ref()), - "สวัสดีครับ นี่คือการทดสอบภาษาไทย" - ); -} - #[test] fn test_windows_932_shift_jis_text() { let (encoded, _, had_errors) = SHIFT_JIS.encode("こんにちは"); @@ -293,30 +84,6 @@ fn test_windows_932_shift_jis_text() { assert_eq!(bytes_to_string_smart(encoded.as_ref()), "こんにちは"); } -#[test] -fn test_windows_936_gbk_text() { - let (encoded, _, had_errors) = GBK.encode("你好,世界,这是一个测试"); - assert!(!had_errors, "failed to encode GBK sample"); - assert_eq!( - bytes_to_string_smart(encoded.as_ref()), - "你好,世界,这是一个测试" - ); -} - -#[test] -fn test_windows_949_korean_text() { - let (encoded, _, had_errors) = EUC_KR.encode("안녕하세요"); - assert!(!had_errors, "failed to encode Korean sample"); - assert_eq!(bytes_to_string_smart(encoded.as_ref()), "안녕하세요"); -} - -#[test] -fn test_windows_950_big5_text() { - let (encoded, _, had_errors) = BIG5.encode("繁體"); - assert!(!had_errors, "failed to encode Big5 sample"); - assert_eq!(bytes_to_string_smart(encoded.as_ref()), "繁體"); -} - #[test] fn test_latin1_cafe() { // Latin-1 bytes remain common in Western-European locales; decode them directly. diff --git a/codex-rs/core/src/tools/js_repl/mod_tests.rs b/codex-rs/core/src/tools/js_repl/mod_tests.rs index bbc0f50dcd..d5446144cf 100644 --- a/codex-rs/core/src/tools/js_repl/mod_tests.rs +++ b/codex-rs/core/src/tools/js_repl/mod_tests.rs @@ -1606,248 +1606,6 @@ console.log("cell-complete"); Ok(()) } -#[tokio::test(flavor = "multi_thread", worker_threads = 2)] -async fn js_repl_emit_image_requires_explicit_mime_type_for_bytes() -> anyhow::Result<()> { - if !can_run_js_repl_runtime_tests().await { - return Ok(()); - } - - let (session, turn) = make_session_and_context().await; - if !turn - .model_info - .input_modalities - .contains(&InputModality::Image) - { - return Ok(()); - } - - let session = Arc::new(session); - let turn = Arc::new(turn); - *session.active_turn.lock().await = Some(crate::state::ActiveTurn::default()); - - let tracker = Arc::new(tokio::sync::Mutex::new(TurnDiffTracker::default())); - let manager = turn.js_repl.manager().await?; - let code = r#" -const png = Buffer.from( - "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR4nGP4z8DwHwAFAAH/iZk9HQAAAABJRU5ErkJggg==", - "base64" -); -await codex.emitImage({ bytes: png }); -"#; - - let err = manager - .execute( - Arc::clone(&session), - turn, - tracker, - JsReplArgs { - code: code.to_string(), - timeout_ms: Some(15_000), - }, - ) - .await - .expect_err("missing mimeType should fail"); - assert!(err.to_string().contains("expected a non-empty mimeType")); - assert!(session.get_pending_input().await.is_empty()); - - Ok(()) -} - -#[tokio::test(flavor = "multi_thread", worker_threads = 2)] -async fn js_repl_emit_image_rejects_non_data_url() -> anyhow::Result<()> { - if !can_run_js_repl_runtime_tests().await { - return Ok(()); - } - - let (session, turn) = make_session_and_context().await; - if !turn - .model_info - .input_modalities - .contains(&InputModality::Image) - { - return Ok(()); - } - - let session = Arc::new(session); - let turn = Arc::new(turn); - *session.active_turn.lock().await = Some(crate::state::ActiveTurn::default()); - - let tracker = Arc::new(tokio::sync::Mutex::new(TurnDiffTracker::default())); - let manager = turn.js_repl.manager().await?; - let code = r#" -await codex.emitImage("https://example.com/image.png"); -"#; - - let err = manager - .execute( - Arc::clone(&session), - turn, - tracker, - JsReplArgs { - code: code.to_string(), - timeout_ms: Some(15_000), - }, - ) - .await - .expect_err("non-data URLs should fail"); - assert!(err.to_string().contains("only accepts data URLs")); - assert!(session.get_pending_input().await.is_empty()); - - Ok(()) -} - -#[tokio::test(flavor = "multi_thread", worker_threads = 2)] -async fn js_repl_emit_image_accepts_case_insensitive_data_url() -> anyhow::Result<()> { - if !can_run_js_repl_runtime_tests().await { - return Ok(()); - } - - let (session, turn) = make_session_and_context().await; - if !turn - .model_info - .input_modalities - .contains(&InputModality::Image) - { - return Ok(()); - } - - let session = Arc::new(session); - let turn = Arc::new(turn); - *session.active_turn.lock().await = Some(crate::state::ActiveTurn::default()); - - let tracker = Arc::new(tokio::sync::Mutex::new(TurnDiffTracker::default())); - let manager = turn.js_repl.manager().await?; - let code = r#" -await codex.emitImage("DATA:image/png;base64,AAA"); -"#; - - let result = manager - .execute( - Arc::clone(&session), - turn, - tracker, - JsReplArgs { - code: code.to_string(), - timeout_ms: Some(15_000), - }, - ) - .await?; - assert_eq!( - result.content_items.as_slice(), - [FunctionCallOutputContentItem::InputImage { - image_url: "DATA:image/png;base64,AAA".to_string(), - detail: None, - }] - .as_slice() - ); - assert!(session.get_pending_input().await.is_empty()); - - Ok(()) -} - -#[tokio::test(flavor = "multi_thread", worker_threads = 2)] -async fn js_repl_emit_image_rejects_invalid_detail() -> anyhow::Result<()> { - if !can_run_js_repl_runtime_tests().await { - return Ok(()); - } - - let (session, turn) = make_session_and_context().await; - if !turn - .model_info - .input_modalities - .contains(&InputModality::Image) - { - return Ok(()); - } - - let session = Arc::new(session); - let turn = Arc::new(turn); - *session.active_turn.lock().await = Some(crate::state::ActiveTurn::default()); - - let tracker = Arc::new(tokio::sync::Mutex::new(TurnDiffTracker::default())); - let manager = turn.js_repl.manager().await?; - let code = r#" -const png = Buffer.from( - "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR4nGP4z8DwHwAFAAH/iZk9HQAAAABJRU5ErkJggg==", - "base64" -); -await codex.emitImage({ bytes: png, mimeType: "image/png", detail: "ultra" }); -"#; - - let err = manager - .execute( - Arc::clone(&session), - turn, - tracker, - JsReplArgs { - code: code.to_string(), - timeout_ms: Some(15_000), - }, - ) - .await - .expect_err("invalid detail should fail"); - assert!( - err.to_string() - .contains("only supports detail \"original\"") - ); - assert!(session.get_pending_input().await.is_empty()); - - Ok(()) -} - -#[tokio::test(flavor = "multi_thread", worker_threads = 2)] -async fn js_repl_emit_image_treats_null_detail_as_omitted() -> anyhow::Result<()> { - if !can_run_js_repl_runtime_tests().await { - return Ok(()); - } - - let (session, turn) = make_session_and_context().await; - if !turn - .model_info - .input_modalities - .contains(&InputModality::Image) - { - return Ok(()); - } - - let session = Arc::new(session); - let turn = Arc::new(turn); - *session.active_turn.lock().await = Some(crate::state::ActiveTurn::default()); - - let tracker = Arc::new(tokio::sync::Mutex::new(TurnDiffTracker::default())); - let manager = turn.js_repl.manager().await?; - let code = r#" -const png = Buffer.from( - "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR4nGP4z8DwHwAFAAH/iZk9HQAAAABJRU5ErkJggg==", - "base64" -); -await codex.emitImage({ bytes: png, mimeType: "image/png", detail: null }); -"#; - - let result = manager - .execute( - Arc::clone(&session), - turn, - tracker, - JsReplArgs { - code: code.to_string(), - timeout_ms: Some(15_000), - }, - ) - .await?; - assert_eq!( - result.content_items.as_slice(), - [FunctionCallOutputContentItem::InputImage { - image_url: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR4nGP4z8DwHwAFAAH/iZk9HQAAAABJRU5ErkJggg==".to_string(), - detail: None, - }] - .as_slice() - ); - assert!(session.get_pending_input().await.is_empty()); - - Ok(()) -} - #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn js_repl_emit_image_rejects_mixed_content() -> anyhow::Result<()> { if !can_run_js_repl_runtime_tests().await { diff --git a/codex-rs/core/src/tools/spec_tests.rs b/codex-rs/core/src/tools/spec_tests.rs index 4e73be3502..4e02fb8261 100644 --- a/codex-rs/core/src/tools/spec_tests.rs +++ b/codex-rs/core/src/tools/spec_tests.rs @@ -314,29 +314,6 @@ fn test_build_specs_gpt5_codex_default() { ); } -#[test] -fn test_build_specs_gpt51_codex_default() { - let features = Features::with_defaults(); - assert_default_model_tools( - "gpt-5.1-codex", - &features, - Some(WebSearchMode::Cached), - "shell_command", - &[ - "update_plan", - "request_user_input", - "apply_patch", - "web_search", - "view_image", - "spawn_agent", - "send_input", - "resume_agent", - "wait_agent", - "close_agent", - ], - ); -} - #[test] fn test_build_specs_gpt5_codex_unified_exec_web_search() { let mut features = Features::with_defaults(); @@ -362,77 +339,6 @@ fn test_build_specs_gpt5_codex_unified_exec_web_search() { ); } -#[test] -fn test_build_specs_gpt51_codex_unified_exec_web_search() { - let mut features = Features::with_defaults(); - features.enable(Feature::UnifiedExec); - assert_model_tools( - "gpt-5.1-codex", - &features, - Some(WebSearchMode::Live), - &[ - "exec_command", - "write_stdin", - "update_plan", - "request_user_input", - "apply_patch", - "web_search", - "view_image", - "spawn_agent", - "send_input", - "resume_agent", - "wait_agent", - "close_agent", - ], - ); -} - -#[test] -fn test_gpt_5_1_codex_max_defaults() { - let features = Features::with_defaults(); - assert_default_model_tools( - "gpt-5.1-codex-max", - &features, - Some(WebSearchMode::Cached), - "shell_command", - &[ - "update_plan", - "request_user_input", - "apply_patch", - "web_search", - "view_image", - "spawn_agent", - "send_input", - "resume_agent", - "wait_agent", - "close_agent", - ], - ); -} - -#[test] -fn test_codex_5_1_mini_defaults() { - let features = Features::with_defaults(); - assert_default_model_tools( - "gpt-5.1-codex-mini", - &features, - Some(WebSearchMode::Cached), - "shell_command", - &[ - "update_plan", - "request_user_input", - "apply_patch", - "web_search", - "view_image", - "spawn_agent", - "send_input", - "resume_agent", - "wait_agent", - "close_agent", - ], - ); -} - #[test] fn test_gpt_5_defaults() { let features = Features::with_defaults(); @@ -455,54 +361,6 @@ fn test_gpt_5_defaults() { ); } -#[test] -fn test_gpt_5_1_defaults() { - let features = Features::with_defaults(); - assert_default_model_tools( - "gpt-5.1", - &features, - Some(WebSearchMode::Cached), - "shell_command", - &[ - "update_plan", - "request_user_input", - "apply_patch", - "web_search", - "view_image", - "spawn_agent", - "send_input", - "resume_agent", - "wait_agent", - "close_agent", - ], - ); -} - -#[test] -fn test_gpt_5_1_codex_max_unified_exec_web_search() { - let mut features = Features::with_defaults(); - features.enable(Feature::UnifiedExec); - assert_model_tools( - "gpt-5.1-codex-max", - &features, - Some(WebSearchMode::Live), - &[ - "exec_command", - "write_stdin", - "update_plan", - "request_user_input", - "apply_patch", - "web_search", - "view_image", - "spawn_agent", - "send_input", - "resume_agent", - "wait_agent", - "close_agent", - ], - ); -} - #[test] fn test_build_specs_default_shell_present() { let config = test_config(); diff --git a/codex-rs/tools/src/tool_registry_plan_tests.rs b/codex-rs/tools/src/tool_registry_plan_tests.rs index c2c4a0aea1..c30d27e74c 100644 --- a/codex-rs/tools/src/tool_registry_plan_tests.rs +++ b/codex-rs/tools/src/tool_registry_plan_tests.rs @@ -1,5 +1,4 @@ use super::*; -use crate::AdditionalProperties; use crate::ConfiguredToolSpec; use crate::DiscoverablePluginInfo; use crate::DiscoverableTool; @@ -38,107 +37,6 @@ const DEFAULT_WAIT_TIMEOUT_MS: i64 = 30_000; const MIN_WAIT_TIMEOUT_MS: i64 = 10_000; const MAX_WAIT_TIMEOUT_MS: i64 = 3_600_000; -#[test] -fn test_full_toolset_specs_for_gpt5_codex_unified_exec_web_search() { - let model_info = model_info(); - let mut features = Features::with_defaults(); - features.enable(Feature::UnifiedExec); - let available_models = Vec::new(); - let config = ToolsConfig::new(&ToolsConfigParams { - model_info: &model_info, - available_models: &available_models, - features: &features, - web_search_mode: Some(WebSearchMode::Live), - session_source: SessionSource::Cli, - sandbox_policy: &SandboxPolicy::DangerFullAccess, - windows_sandbox_level: WindowsSandboxLevel::Disabled, - }); - let (tools, _) = build_specs( - &config, - /*mcp_tools*/ None, - /*app_tools*/ None, - &[], - ); - - let mut actual = BTreeMap::new(); - let mut duplicate_names = Vec::new(); - for tool in &tools { - let name = tool.name().to_string(); - if actual.insert(name.clone(), tool.spec.clone()).is_some() { - duplicate_names.push(name); - } - } - assert!( - duplicate_names.is_empty(), - "duplicate tool entries detected: {duplicate_names:?}" - ); - - let mut expected = BTreeMap::new(); - for spec in [ - create_exec_command_tool(CommandToolOptions { - allow_login_shell: true, - exec_permission_approvals_enabled: false, - }), - create_write_stdin_tool(), - create_update_plan_tool(), - request_user_input_tool_spec(/*default_mode_request_user_input*/ false), - create_apply_patch_freeform_tool(), - ToolSpec::WebSearch { - external_web_access: Some(true), - filters: None, - user_location: None, - search_context_size: None, - search_content_types: None, - }, - create_view_image_tool(ViewImageToolOptions { - can_request_original_image_detail: config.can_request_original_image_detail, - }), - ] { - expected.insert(spec.name().to_string(), spec); - } - let collab_specs = if config.multi_agent_v2 { - vec![ - create_spawn_agent_tool_v2(spawn_agent_tool_options(&config)), - create_send_message_tool(), - create_wait_agent_tool_v2(wait_agent_timeout_options()), - create_close_agent_tool_v2(), - ] - } else { - vec![ - create_spawn_agent_tool_v1(spawn_agent_tool_options(&config)), - create_send_input_tool_v1(), - create_wait_agent_tool_v1(wait_agent_timeout_options()), - create_close_agent_tool_v1(), - ] - }; - for spec in collab_specs { - expected.insert(spec.name().to_string(), spec); - } - if !config.multi_agent_v2 { - let spec = create_resume_agent_tool(); - expected.insert(spec.name().to_string(), spec); - } - - if config.exec_permission_approvals_enabled { - let spec = create_request_permissions_tool(request_permissions_tool_description()); - expected.insert(spec.name().to_string(), spec); - } - - assert_eq!( - actual.keys().collect::>(), - expected.keys().collect::>(), - "tool name set mismatch" - ); - - for name in expected.keys() { - let mut actual_spec = actual.get(name).expect("present").clone(); - let mut expected_spec = expected.get(name).expect("present").clone(); - strip_descriptions_tool(&mut actual_spec); - strip_descriptions_tool(&mut expected_spec); - assert_eq!(actual_spec, expected_spec, "spec mismatch for {name}"); - } -} - #[test] fn test_build_specs_collab_tools_enabled() { let model_info = model_info(); @@ -972,33 +870,6 @@ fn mcp_resource_tools_are_included_when_mcp_servers_are_present() { ); } -#[test] -#[ignore] -fn test_parallel_support_flags() { - let model_info = model_info(); - let mut features = Features::with_defaults(); - features.enable(Feature::UnifiedExec); - let available_models = Vec::new(); - let tools_config = ToolsConfig::new(&ToolsConfigParams { - model_info: &model_info, - available_models: &available_models, - features: &features, - web_search_mode: Some(WebSearchMode::Cached), - session_source: SessionSource::Cli, - sandbox_policy: &SandboxPolicy::DangerFullAccess, - windows_sandbox_level: WindowsSandboxLevel::Disabled, - }); - let (tools, _) = build_specs( - &tools_config, - /*mcp_tools*/ None, - /*app_tools*/ None, - &[], - ); - - assert!(find_tool(&tools, "exec_command").supports_parallel_tool_calls); - assert!(!find_tool(&tools, "write_stdin").supports_parallel_tool_calls); -} - #[test] fn test_test_model_info_includes_sync_tool() { let mut model_info = model_info(); @@ -1816,13 +1687,6 @@ fn request_user_input_tool_spec(default_mode_request_user_input: bool) -> ToolSp )) } -fn spawn_agent_tool_options(config: &ToolsConfig) -> SpawnAgentToolOptions<'_> { - SpawnAgentToolOptions { - available_models: &config.available_models, - agent_type_description: agent_type_description(config, DEFAULT_AGENT_TYPE_DESCRIPTION), - } -} - fn wait_agent_timeout_options() -> WaitAgentTimeoutOptions { WaitAgentTimeoutOptions { default_timeout_ms: DEFAULT_WAIT_TIMEOUT_MS, @@ -1837,42 +1701,3 @@ fn find_tool<'a>(tools: &'a [ConfiguredToolSpec], expected_name: &str) -> &'a Co .find(|tool| tool.name() == expected_name) .unwrap_or_else(|| panic!("expected tool {expected_name}")) } - -fn strip_descriptions_schema(schema: &mut JsonSchema) { - match schema { - JsonSchema::Boolean { description } - | JsonSchema::String { description } - | JsonSchema::Number { description } => { - *description = None; - } - JsonSchema::Array { items, description } => { - strip_descriptions_schema(items); - *description = None; - } - JsonSchema::Object { - properties, - required: _, - additional_properties, - } => { - for value in properties.values_mut() { - strip_descriptions_schema(value); - } - if let Some(AdditionalProperties::Schema(schema)) = additional_properties { - strip_descriptions_schema(schema); - } - } - } -} - -fn strip_descriptions_tool(spec: &mut ToolSpec) { - match spec { - ToolSpec::ToolSearch { parameters, .. } => strip_descriptions_schema(parameters), - ToolSpec::Function(ResponsesApiTool { parameters, .. }) => { - strip_descriptions_schema(parameters); - } - ToolSpec::Freeform(FreeformTool { .. }) - | ToolSpec::LocalShell {} - | ToolSpec::ImageGeneration { .. } - | ToolSpec::WebSearch { .. } => {} - } -}