From 5c42419b02992ca56307b8795a1dfda390bced2b Mon Sep 17 00:00:00 2001 From: pakrym-oai Date: Sun, 5 Oct 2025 14:12:31 -0700 Subject: [PATCH] Use assert_matches (#4756) assert_matches is soon to be in std but is experimental for now. --- codex-rs/Cargo.lock | 13 ++++++++ codex-rs/Cargo.toml | 1 + codex-rs/apply-patch/Cargo.toml | 1 + codex-rs/apply-patch/src/lib.rs | 13 ++++---- codex-rs/cli/Cargo.toml | 1 + codex-rs/cli/src/main.rs | 9 +++--- codex-rs/core/Cargo.toml | 1 + codex-rs/core/src/client.rs | 8 ++--- codex-rs/core/src/config.rs | 10 +++---- codex-rs/core/src/conversation_manager.rs | 3 +- codex-rs/core/src/event_mapping.rs | 3 +- codex-rs/core/tests/chat_completions_sse.rs | 11 +++---- codex-rs/core/tests/suite/tool_harness.rs | 5 ++-- codex-rs/git-tooling/Cargo.toml | 1 + codex-rs/git-tooling/src/ghost_commits.rs | 5 ++-- codex-rs/ollama/Cargo.toml | 1 + codex-rs/ollama/src/parser.rs | 33 ++++++++------------- codex-rs/tui/Cargo.toml | 1 + codex-rs/tui/src/chatwidget/tests.rs | 13 +++----- codex-rs/utils/readiness/Cargo.toml | 1 + codex-rs/utils/readiness/src/lib.rs | 3 +- 21 files changed, 75 insertions(+), 62 deletions(-) diff --git a/codex-rs/Cargo.lock b/codex-rs/Cargo.lock index d579f293..29025095 100644 --- a/codex-rs/Cargo.lock +++ b/codex-rs/Cargo.lock @@ -300,6 +300,12 @@ dependencies = [ "wait-timeout", ] +[[package]] +name = "assert_matches" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b34d609dfbaf33d6889b2b7106d3ca345eacad44200913df5ba02bfd31d2ba9" + [[package]] name = "async-broadcast" version = "0.7.2" @@ -871,6 +877,7 @@ version = "0.0.0" dependencies = [ "anyhow", "assert_cmd", + "assert_matches", "pretty_assertions", "similar", "tempfile", @@ -933,6 +940,7 @@ version = "0.0.0" dependencies = [ "anyhow", "assert_cmd", + "assert_matches", "clap", "clap_complete", "codex-app-server", @@ -1022,6 +1030,7 @@ dependencies = [ "anyhow", "askama", "assert_cmd", + "assert_matches", "async-channel", "async-trait", "base64", @@ -1162,6 +1171,7 @@ dependencies = [ name = "codex-git-tooling" version = "0.0.0" dependencies = [ + "assert_matches", "pretty_assertions", "tempfile", "thiserror 2.0.16", @@ -1249,6 +1259,7 @@ dependencies = [ name = "codex-ollama" version = "0.0.0" dependencies = [ + "assert_matches", "async-stream", "bytes", "codex-core", @@ -1367,6 +1378,7 @@ version = "0.0.0" dependencies = [ "anyhow", "arboard", + "assert_matches", "async-stream", "base64", "chrono", @@ -1432,6 +1444,7 @@ dependencies = [ name = "codex-utils-readiness" version = "0.0.0" dependencies = [ + "assert_matches", "async-trait", "thiserror 2.0.16", "time", diff --git a/codex-rs/Cargo.toml b/codex-rs/Cargo.toml index f9cd0711..d796e967 100644 --- a/codex-rs/Cargo.toml +++ b/codex-rs/Cargo.toml @@ -83,6 +83,7 @@ ansi-to-tui = "7.0.0" anyhow = "1" arboard = "3" askama = "0.12" +assert_matches = "1.5.0" assert_cmd = "2" async-channel = "2.3.1" async-stream = "0.3.6" diff --git a/codex-rs/apply-patch/Cargo.toml b/codex-rs/apply-patch/Cargo.toml index 9445ae08..a239cd63 100644 --- a/codex-rs/apply-patch/Cargo.toml +++ b/codex-rs/apply-patch/Cargo.toml @@ -23,5 +23,6 @@ tree-sitter-bash = { workspace = true } [dev-dependencies] assert_cmd = { workspace = true } +assert_matches = { workspace = true } pretty_assertions = { workspace = true } tempfile = { workspace = true } diff --git a/codex-rs/apply-patch/src/lib.rs b/codex-rs/apply-patch/src/lib.rs index 3737c6ea..3d0052ed 100644 --- a/codex-rs/apply-patch/src/lib.rs +++ b/codex-rs/apply-patch/src/lib.rs @@ -843,6 +843,7 @@ pub fn print_summary( #[cfg(test)] mod tests { use super::*; + use assert_matches::assert_matches; use pretty_assertions::assert_eq; use std::fs; use std::string::ToString; @@ -894,10 +895,10 @@ mod tests { fn assert_not_match(script: &str) { let args = args_bash(script); - assert!(matches!( + assert_matches!( maybe_parse_apply_patch(&args), MaybeApplyPatch::NotApplyPatch - )); + ); } #[test] @@ -905,10 +906,10 @@ mod tests { let patch = "*** Begin Patch\n*** Add File: foo\n+hi\n*** End Patch".to_string(); let args = vec![patch]; let dir = tempdir().unwrap(); - assert!(matches!( + assert_matches!( maybe_parse_apply_patch_verified(&args, dir.path()), MaybeApplyPatchVerified::CorrectnessError(ApplyPatchError::ImplicitInvocation) - )); + ); } #[test] @@ -916,10 +917,10 @@ mod tests { let script = "*** Begin Patch\n*** Add File: foo\n+hi\n*** End Patch"; let args = args_bash(script); let dir = tempdir().unwrap(); - assert!(matches!( + assert_matches!( maybe_parse_apply_patch_verified(&args, dir.path()), MaybeApplyPatchVerified::CorrectnessError(ApplyPatchError::ImplicitInvocation) - )); + ); } #[test] diff --git a/codex-rs/cli/Cargo.toml b/codex-rs/cli/Cargo.toml index bcece872..453ab807 100644 --- a/codex-rs/cli/Cargo.toml +++ b/codex-rs/cli/Cargo.toml @@ -47,6 +47,7 @@ tokio = { workspace = true, features = [ ] } [dev-dependencies] +assert_matches = { workspace = true } assert_cmd = { workspace = true } predicates = { workspace = true } pretty_assertions = { workspace = true } diff --git a/codex-rs/cli/src/main.rs b/codex-rs/cli/src/main.rs index 51c13742..79a18793 100644 --- a/codex-rs/cli/src/main.rs +++ b/codex-rs/cli/src/main.rs @@ -472,6 +472,7 @@ fn print_completion(cmd: CompletionCommand) { #[cfg(test)] mod tests { use super::*; + use assert_matches::assert_matches; use codex_core::protocol::TokenUsage; use codex_protocol::ConversationId; @@ -604,14 +605,14 @@ mod tests { assert_eq!(interactive.model.as_deref(), Some("gpt-5-test")); assert!(interactive.oss); assert_eq!(interactive.config_profile.as_deref(), Some("my-profile")); - assert!(matches!( + assert_matches!( interactive.sandbox_mode, Some(codex_common::SandboxModeCliArg::WorkspaceWrite) - )); - assert!(matches!( + ); + assert_matches!( interactive.approval_policy, Some(codex_common::ApprovalModeCliArg::OnRequest) - )); + ); assert!(interactive.full_auto); assert_eq!( interactive.cwd.as_deref(), diff --git a/codex-rs/core/Cargo.toml b/codex-rs/core/Cargo.toml index 3d499f90..367ccbce 100644 --- a/codex-rs/core/Cargo.toml +++ b/codex-rs/core/Cargo.toml @@ -89,6 +89,7 @@ openssl-sys = { workspace = true, features = ["vendored"] } [dev-dependencies] assert_cmd = { workspace = true } +assert_matches = { workspace = true } core_test_support = { workspace = true } escargot = { workspace = true } maplit = { workspace = true } diff --git a/codex-rs/core/src/client.rs b/codex-rs/core/src/client.rs index 0bd7848d..3c21552d 100644 --- a/codex-rs/core/src/client.rs +++ b/codex-rs/core/src/client.rs @@ -932,6 +932,7 @@ fn is_context_window_error(error: &Error) -> bool { #[cfg(test)] mod tests { use super::*; + use assert_matches::assert_matches; use serde_json::json; use tokio::sync::mpsc; use tokio_test::io::Builder as IoBuilder; @@ -1391,10 +1392,7 @@ mod tests { let resp: ErrorResponse = serde_json::from_str(json).expect("should deserialize old schema"); - assert!(matches!( - resp.error.plan_type, - Some(PlanType::Known(KnownPlan::Pro)) - )); + assert_matches!(resp.error.plan_type, Some(PlanType::Known(KnownPlan::Pro))); let plan_json = serde_json::to_string(&resp.error.plan_type).expect("serialize plan_type"); assert_eq!(plan_json, "\"pro\""); @@ -1409,7 +1407,7 @@ mod tests { let resp: ErrorResponse = serde_json::from_str(json).expect("should deserialize old schema"); - assert!(matches!(resp.error.plan_type, Some(PlanType::Unknown(ref s)) if s == "vip")); + assert_matches!(resp.error.plan_type, Some(PlanType::Unknown(ref s)) if s == "vip"); let plan_json = serde_json::to_string(&resp.error.plan_type).expect("serialize plan_type"); assert_eq!(plan_json, "\"vip\""); diff --git a/codex-rs/core/src/config.rs b/codex-rs/core/src/config.rs index 6fb83cee..28ad84ba 100644 --- a/codex-rs/core/src/config.rs +++ b/codex-rs/core/src/config.rs @@ -2231,6 +2231,7 @@ trust_level = "trusted" #[cfg(test)] mod notifications_tests { use crate::config_types::Notifications; + use assert_matches::assert_matches; use serde::Deserialize; #[derive(Deserialize, Debug, PartialEq)] @@ -2250,10 +2251,7 @@ mod notifications_tests { notifications = true "#; let parsed: RootTomlTest = toml::from_str(toml).expect("deserialize notifications=true"); - assert!(matches!( - parsed.tui.notifications, - Notifications::Enabled(true) - )); + assert_matches!(parsed.tui.notifications, Notifications::Enabled(true)); } #[test] @@ -2264,9 +2262,9 @@ mod notifications_tests { "#; let parsed: RootTomlTest = toml::from_str(toml).expect("deserialize notifications=[\"foo\"]"); - assert!(matches!( + assert_matches!( parsed.tui.notifications, Notifications::Custom(ref v) if v == &vec!["foo".to_string()] - )); + ); } } diff --git a/codex-rs/core/src/conversation_manager.rs b/codex-rs/core/src/conversation_manager.rs index b2159612..aeb07807 100644 --- a/codex-rs/core/src/conversation_manager.rs +++ b/codex-rs/core/src/conversation_manager.rs @@ -210,6 +210,7 @@ fn truncate_before_nth_user_message(history: InitialHistory, n: usize) -> Initia mod tests { use super::*; use crate::codex::make_session_and_context; + use assert_matches::assert_matches; use codex_protocol::models::ContentItem; use codex_protocol::models::ReasoningItemReasoningSummary; use codex_protocol::models::ResponseItem; @@ -283,7 +284,7 @@ mod tests { .map(RolloutItem::ResponseItem) .collect(); let truncated2 = truncate_before_nth_user_message(InitialHistory::Forked(initial2), 2); - assert!(matches!(truncated2, InitialHistory::New)); + assert_matches!(truncated2, InitialHistory::New); } #[test] diff --git a/codex-rs/core/src/event_mapping.rs b/codex-rs/core/src/event_mapping.rs index 3ae7f785..cbbf1f44 100644 --- a/codex-rs/core/src/event_mapping.rs +++ b/codex-rs/core/src/event_mapping.rs @@ -127,6 +127,7 @@ mod tests { use super::map_response_item_to_event_messages; use crate::protocol::EventMsg; use crate::protocol::InputMessageKind; + use assert_matches::assert_matches; use codex_protocol::models::ContentItem; use codex_protocol::models::ResponseItem; use pretty_assertions::assert_eq; @@ -158,7 +159,7 @@ mod tests { match &events[0] { EventMsg::UserMessage(user) => { assert_eq!(user.message, "Hello world"); - assert!(matches!(user.kind, Some(InputMessageKind::Plain))); + assert_matches!(user.kind, Some(InputMessageKind::Plain)); assert_eq!(user.images, Some(vec![img1, img2])); } other => panic!("expected UserMessage, got {other:?}"), diff --git a/codex-rs/core/tests/chat_completions_sse.rs b/codex-rs/core/tests/chat_completions_sse.rs index d8a82129..1aab6ac3 100644 --- a/codex-rs/core/tests/chat_completions_sse.rs +++ b/codex-rs/core/tests/chat_completions_sse.rs @@ -1,3 +1,4 @@ +use assert_matches::assert_matches; use std::sync::Arc; use tracing_test::traced_test; @@ -178,7 +179,7 @@ async fn streams_text_without_reasoning() { other => panic!("expected terminal message, got {other:?}"), } - assert!(matches!(events[2], ResponseEvent::Completed { .. })); + assert_matches!(events[2], ResponseEvent::Completed { .. }); } #[tokio::test(flavor = "multi_thread", worker_threads = 2)] @@ -219,7 +220,7 @@ async fn streams_reasoning_from_string_delta() { other => panic!("expected message item, got {other:?}"), } - assert!(matches!(events[4], ResponseEvent::Completed { .. })); + assert_matches!(events[4], ResponseEvent::Completed { .. }); } #[tokio::test(flavor = "multi_thread", worker_threads = 2)] @@ -266,7 +267,7 @@ async fn streams_reasoning_from_object_delta() { other => panic!("expected message item, got {other:?}"), } - assert!(matches!(events[5], ResponseEvent::Completed { .. })); + assert_matches!(events[5], ResponseEvent::Completed { .. }); } #[tokio::test(flavor = "multi_thread", worker_threads = 2)] @@ -293,7 +294,7 @@ async fn streams_reasoning_from_final_message() { other => panic!("expected reasoning item, got {other:?}"), } - assert!(matches!(events[2], ResponseEvent::Completed { .. })); + assert_matches!(events[2], ResponseEvent::Completed { .. }); } #[tokio::test(flavor = "multi_thread", worker_threads = 2)] @@ -337,7 +338,7 @@ async fn streams_reasoning_before_tool_call() { other => panic!("expected function call, got {other:?}"), } - assert!(matches!(events[3], ResponseEvent::Completed { .. })); + assert_matches!(events[3], ResponseEvent::Completed { .. }); } #[tokio::test] diff --git a/codex-rs/core/tests/suite/tool_harness.rs b/codex-rs/core/tests/suite/tool_harness.rs index fc74c412..74cce5cf 100644 --- a/codex-rs/core/tests/suite/tool_harness.rs +++ b/codex-rs/core/tests/suite/tool_harness.rs @@ -1,5 +1,6 @@ #![cfg(not(target_os = "windows"))] +use assert_matches::assert_matches; use codex_core::model_family::find_family_for_model; use codex_core::protocol::AskForApproval; use codex_core::protocol::EventMsg; @@ -186,9 +187,9 @@ async fn update_plan_tool_emits_plan_update_event() -> anyhow::Result<()> { assert_eq!(update.explanation.as_deref(), Some("Tool harness check")); assert_eq!(update.plan.len(), 2); assert_eq!(update.plan[0].step, "Inspect workspace"); - assert!(matches!(update.plan[0].status, StepStatus::InProgress)); + assert_matches!(update.plan[0].status, StepStatus::InProgress); assert_eq!(update.plan[1].step, "Report results"); - assert!(matches!(update.plan[1].status, StepStatus::Pending)); + assert_matches!(update.plan[1].status, StepStatus::Pending); false } EventMsg::TaskComplete(_) => true, diff --git a/codex-rs/git-tooling/Cargo.toml b/codex-rs/git-tooling/Cargo.toml index 674a5e52..183221f3 100644 --- a/codex-rs/git-tooling/Cargo.toml +++ b/codex-rs/git-tooling/Cargo.toml @@ -17,4 +17,5 @@ walkdir = "2" workspace = true [dev-dependencies] +assert_matches = { workspace = true } pretty_assertions = "1.4.1" diff --git a/codex-rs/git-tooling/src/ghost_commits.rs b/codex-rs/git-tooling/src/ghost_commits.rs index 06b43211..6c2a8616 100644 --- a/codex-rs/git-tooling/src/ghost_commits.rs +++ b/codex-rs/git-tooling/src/ghost_commits.rs @@ -186,6 +186,7 @@ fn default_commit_identity() -> Vec<(OsString, OsString)> { mod tests { use super::*; use crate::operations::run_git_for_stdout; + use assert_matches::assert_matches; use pretty_assertions::assert_eq; use std::process::Command; @@ -348,7 +349,7 @@ mod tests { let options = CreateGhostCommitOptions::new(repo) .force_include(vec![PathBuf::from("../outside.txt")]); let err = create_ghost_commit(&options).unwrap_err(); - assert!(matches!(err, GitToolingError::PathEscapesRepository { .. })); + assert_matches!(err, GitToolingError::PathEscapesRepository { .. }); } #[test] @@ -356,7 +357,7 @@ mod tests { fn restore_requires_git_repository() { let temp = tempfile::tempdir().expect("tempdir"); let err = restore_to_commit(temp.path(), "deadbeef").unwrap_err(); - assert!(matches!(err, GitToolingError::NotAGitRepository { .. })); + assert_matches!(err, GitToolingError::NotAGitRepository { .. }); } #[test] diff --git a/codex-rs/ollama/Cargo.toml b/codex-rs/ollama/Cargo.toml index 587a1930..14dd6d2f 100644 --- a/codex-rs/ollama/Cargo.toml +++ b/codex-rs/ollama/Cargo.toml @@ -28,3 +28,4 @@ tracing = { workspace = true, features = ["log"] } wiremock = { workspace = true } [dev-dependencies] +assert_matches = { workspace = true } diff --git a/codex-rs/ollama/src/parser.rs b/codex-rs/ollama/src/parser.rs index c39df668..87f5b937 100644 --- a/codex-rs/ollama/src/parser.rs +++ b/codex-rs/ollama/src/parser.rs @@ -30,19 +30,21 @@ pub(crate) fn pull_events_from_value(value: &JsonValue) -> Vec { #[cfg(test)] mod tests { + use assert_matches::assert_matches; + use super::*; #[test] fn test_pull_events_decoder_status_and_success() { let v: JsonValue = serde_json::json!({"status":"verifying"}); let events = pull_events_from_value(&v); - assert!(matches!(events.as_slice(), [PullEvent::Status(s)] if s == "verifying")); + assert_matches!(events.as_slice(), [PullEvent::Status(s)] if s == "verifying"); let v2: JsonValue = serde_json::json!({"status":"success"}); let events2 = pull_events_from_value(&v2); assert_eq!(events2.len(), 2); - assert!(matches!(events2[0], PullEvent::Status(ref s) if s == "success")); - assert!(matches!(events2[1], PullEvent::Success)); + assert_matches!(events2[0], PullEvent::Status(ref s) if s == "success"); + assert_matches!(events2[1], PullEvent::Success); } #[test] @@ -50,33 +52,24 @@ mod tests { let v: JsonValue = serde_json::json!({"digest":"sha256:abc","total":100}); let events = pull_events_from_value(&v); assert_eq!(events.len(), 1); - match &events[0] { + assert_matches!( + &events[0], PullEvent::ChunkProgress { digest, total, completed, - } => { - assert_eq!(digest, "sha256:abc"); - assert_eq!(*total, Some(100)); - assert_eq!(*completed, None); - } - _ => panic!("expected ChunkProgress"), - } - + } if digest == "sha256:abc" && total == &Some(100) && completed.is_none() + ); let v2: JsonValue = serde_json::json!({"digest":"sha256:def","completed":42}); let events2 = pull_events_from_value(&v2); assert_eq!(events2.len(), 1); - match &events2[0] { + assert_matches!( + &events2[0], PullEvent::ChunkProgress { digest, total, completed, - } => { - assert_eq!(digest, "sha256:def"); - assert_eq!(*total, None); - assert_eq!(*completed, Some(42)); - } - _ => panic!("expected ChunkProgress"), - } + } if digest == "sha256:def" && total.is_none() && completed == &Some(42) + ); } } diff --git a/codex-rs/tui/Cargo.toml b/codex-rs/tui/Cargo.toml index 7356ae33..f42f555c 100644 --- a/codex-rs/tui/Cargo.toml +++ b/codex-rs/tui/Cargo.toml @@ -94,6 +94,7 @@ arboard = { workspace = true } [dev-dependencies] +assert_matches = { workspace = true } chrono = { workspace = true, features = ["serde"] } insta = { workspace = true } pretty_assertions = { workspace = true } diff --git a/codex-rs/tui/src/chatwidget/tests.rs b/codex-rs/tui/src/chatwidget/tests.rs index a17e0a75..0ab31daa 100644 --- a/codex-rs/tui/src/chatwidget/tests.rs +++ b/codex-rs/tui/src/chatwidget/tests.rs @@ -3,6 +3,7 @@ use crate::app_event::AppEvent; use crate::app_event_sender::AppEventSender; use crate::test_backend::VT100Backend; use crate::tui::FrameRequester; +use assert_matches::assert_matches; use codex_core::AuthManager; use codex_core::CodexAuth; use codex_core::config::Config; @@ -634,7 +635,7 @@ fn streaming_final_answer_keeps_task_running_state() { chat.queued_user_messages.front().unwrap().text, "queued submission" ); - assert!(matches!(op_rx.try_recv(), Err(TryRecvError::Empty))); + assert_matches!(op_rx.try_recv(), Err(TryRecvError::Empty)); chat.handle_key_event(KeyEvent::new(KeyCode::Char('c'), KeyModifiers::CONTROL)); match op_rx.try_recv() { @@ -1791,10 +1792,7 @@ fn apply_patch_approval_sends_op_with_submission_id() { while let Ok(app_ev) = rx.try_recv() { if let AppEvent::CodexOp(Op::PatchApproval { id, decision }) = app_ev { assert_eq!(id, "sub-123"); - assert!(matches!( - decision, - codex_core::protocol::ReviewDecision::Approved - )); + assert_matches!(decision, codex_core::protocol::ReviewDecision::Approved); found = true; break; } @@ -1841,10 +1839,7 @@ fn apply_patch_full_flow_integration_like() { match forwarded { Op::PatchApproval { id, decision } => { assert_eq!(id, "sub-xyz"); - assert!(matches!( - decision, - codex_core::protocol::ReviewDecision::Approved - )); + assert_matches!(decision, codex_core::protocol::ReviewDecision::Approved); } other => panic!("unexpected op forwarded: {other:?}"), } diff --git a/codex-rs/utils/readiness/Cargo.toml b/codex-rs/utils/readiness/Cargo.toml index faa5d4d1..cb76b52b 100644 --- a/codex-rs/utils/readiness/Cargo.toml +++ b/codex-rs/utils/readiness/Cargo.toml @@ -10,6 +10,7 @@ time = { workspace = true } tokio = { workspace = true, features = ["sync", "time"] } [dev-dependencies] +assert_matches = { workspace = true } tokio = { workspace = true, features = ["macros", "rt", "rt-multi-thread"] } [lints] diff --git a/codex-rs/utils/readiness/src/lib.rs b/codex-rs/utils/readiness/src/lib.rs index 9ff15e01..ac3a323f 100644 --- a/codex-rs/utils/readiness/src/lib.rs +++ b/codex-rs/utils/readiness/src/lib.rs @@ -176,6 +176,7 @@ mod tests { use super::ReadinessFlag; use super::Token; use super::errors::ReadinessError; + use assert_matches::assert_matches; #[tokio::test] async fn subscribe_and_mark_ready_roundtrip() -> Result<(), ReadinessError> { @@ -244,6 +245,6 @@ mod tests { .subscribe() .await .expect_err("contended subscribe should report a lock failure"); - assert!(matches!(err, ReadinessError::TokenLockFailed)); + assert_matches!(err, ReadinessError::TokenLockFailed); } }