Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
219 changes: 25 additions & 194 deletions codex-rs/core/tests/suite/apply_patch_cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,9 @@ use std::sync::atomic::Ordering;
use std::time::Duration;

use codex_features::Feature;
use codex_protocol::protocol::AskForApproval;
use codex_protocol::models::PermissionProfile;
use codex_protocol::permissions::NetworkSandboxPolicy;
use codex_protocol::protocol::EventMsg;
use codex_protocol::protocol::Op;
use codex_protocol::protocol::SandboxPolicy;
use codex_protocol::user_input::UserInput;
#[cfg(target_os = "linux")]
use codex_sandboxing::landlock::CODEX_LINUX_SANDBOX_ARG0;
use core_test_support::assert_regex_match;
Expand Down Expand Up @@ -59,6 +57,15 @@ async fn apply_patch_harness_with(
Box::pin(TestCodexHarness::with_remote_aware_builder(builder)).await
}

fn restrictive_workspace_write_profile() -> PermissionProfile {
PermissionProfile::workspace_write_with(
&[],
NetworkSandboxPolicy::Restricted,
/*exclude_tmpdir_env_var*/ true,
/*exclude_slash_tmp*/ true,
)
}

pub async fn mount_apply_patch(
harness: &TestCodexHarness,
call_id: &str,
Expand Down Expand Up @@ -354,28 +361,7 @@ async fn apply_patch_cli_move_without_content_change_has_no_turn_diff(
let call_id = "apply-move-no-change";
mount_apply_patch(&harness, call_id, patch, "ok", model_output).await;

let model = test.session_configured.model.clone();
codex
.submit(Op::UserTurn {
environments: None,
items: vec![UserInput::Text {
text: "rename without content change".into(),
text_elements: Vec::new(),
}],
final_output_json_schema: None,
cwd: harness.cwd().to_path_buf(),
approval_policy: AskForApproval::Never,
approvals_reviewer: None,
sandbox_policy: SandboxPolicy::DangerFullAccess,
permission_profile: None,
model,
effort: None,
summary: None,
service_tier: None,
collaboration_mode: None,
personality: None,
})
.await?;
harness.submit("rename without content change").await?;

let mut saw_turn_diff = false;
wait_for_event(&codex, |event| match event {
Expand Down Expand Up @@ -641,16 +627,10 @@ async fn apply_patch_cli_rejects_path_traversal_outside_workspace(
let call_id = "apply-path-traversal";
mount_apply_patch(&harness, call_id, patch, "fail", model_output).await;

let sandbox_policy = SandboxPolicy::WorkspaceWrite {
writable_roots: vec![],
network_access: false,
exclude_tmpdir_env_var: true,
exclude_slash_tmp: true,
};
harness
.submit_with_policy(
.submit_with_permission_profile(
"attempt to escape workspace via apply_patch",
sandbox_policy,
restrictive_workspace_write_profile(),
)
.await?;

Expand Down Expand Up @@ -696,14 +676,11 @@ async fn apply_patch_cli_rejects_move_path_traversal_outside_workspace(
let call_id = "apply-move-traversal";
mount_apply_patch(&harness, call_id, patch, "fail", model_output).await;

let sandbox_policy = SandboxPolicy::WorkspaceWrite {
writable_roots: vec![],
network_access: false,
exclude_tmpdir_env_var: true,
exclude_slash_tmp: true,
};
harness
.submit_with_policy("attempt move traversal via apply_patch", sandbox_policy)
.submit_with_permission_profile(
"attempt move traversal via apply_patch",
restrictive_workspace_write_profile(),
)
.await?;

let out = harness.apply_patch_output(call_id, model_output).await;
Expand Down Expand Up @@ -992,27 +969,7 @@ async fn apply_patch_custom_tool_streaming_emits_updated_changes() -> Result<()>
)
.await;

codex
.submit(Op::UserTurn {
environments: None,
items: vec![UserInput::Text {
text: "create streamed file".into(),
text_elements: Vec::new(),
}],
final_output_json_schema: None,
cwd: harness.cwd().to_path_buf(),
approval_policy: AskForApproval::Never,
approvals_reviewer: None,
sandbox_policy: SandboxPolicy::DangerFullAccess,
permission_profile: None,
model: test.session_configured.model.clone(),
effort: None,
summary: None,
service_tier: None,
collaboration_mode: None,
personality: None,
})
.await?;
harness.submit("create streamed file").await?;

let mut updates = Vec::new();
wait_for_event(&codex, |event| match event {
Expand Down Expand Up @@ -1090,28 +1047,7 @@ async fn apply_patch_shell_command_heredoc_with_cd_emits_turn_diff() -> Result<(
];
mount_sse_sequence(harness.server(), bodies).await;

let model = test.session_configured.model.clone();
codex
.submit(Op::UserTurn {
environments: None,
items: vec![UserInput::Text {
text: "apply via shell heredoc with cd".into(),
text_elements: Vec::new(),
}],
final_output_json_schema: None,
cwd: harness.cwd().to_path_buf(),
approval_policy: AskForApproval::Never,
approvals_reviewer: None,
sandbox_policy: SandboxPolicy::DangerFullAccess,
permission_profile: None,
model,
effort: None,
summary: None,
service_tier: None,
collaboration_mode: None,
personality: None,
})
.await?;
harness.submit("apply via shell heredoc with cd").await?;

let mut saw_turn_diff = None;
let mut saw_patch_begin = false;
Expand Down Expand Up @@ -1176,28 +1112,7 @@ async fn apply_patch_shell_command_failure_propagates_error_and_skips_diff() ->
];
mount_sse_sequence(harness.server(), bodies).await;

let model = test.session_configured.model.clone();
codex
.submit(Op::UserTurn {
environments: None,
items: vec![UserInput::Text {
text: "apply patch via shell".into(),
text_elements: Vec::new(),
}],
final_output_json_schema: None,
cwd: harness.cwd().to_path_buf(),
approval_policy: AskForApproval::Never,
approvals_reviewer: None,
sandbox_policy: SandboxPolicy::DangerFullAccess,
permission_profile: None,
model,
effort: None,
summary: None,
service_tier: None,
collaboration_mode: None,
personality: None,
})
.await?;
harness.submit("apply patch via shell").await?;

let mut saw_turn_diff = false;
wait_for_event(&codex, |event| match event {
Expand Down Expand Up @@ -1333,28 +1248,7 @@ async fn apply_patch_emits_turn_diff_event_with_unified_diff(
let patch = format!("*** Begin Patch\n*** Add File: {file}\n+hello\n*** End Patch\n");
mount_apply_patch(&harness, call_id, patch.as_str(), "ok", model_output).await;

let model = test.session_configured.model.clone();
codex
.submit(Op::UserTurn {
environments: None,
items: vec![UserInput::Text {
text: "emit diff".into(),
text_elements: Vec::new(),
}],
final_output_json_schema: None,
cwd: harness.cwd().to_path_buf(),
approval_policy: AskForApproval::Never,
approvals_reviewer: None,
sandbox_policy: SandboxPolicy::DangerFullAccess,
permission_profile: None,
model,
effort: None,
summary: None,
service_tier: None,
collaboration_mode: None,
personality: None,
})
.await?;
harness.submit("emit diff").await?;

let mut saw_turn_diff = None;
wait_for_event(&codex, |event| match event {
Expand Down Expand Up @@ -1402,28 +1296,7 @@ async fn apply_patch_turn_diff_for_rename_with_content_change(
let patch = "*** Begin Patch\n*** Update File: old.txt\n*** Move to: new.txt\n@@\n-old\n+new\n*** End Patch";
mount_apply_patch(&harness, call_id, patch, "ok", model_output).await;

let model = test.session_configured.model.clone();
codex
.submit(Op::UserTurn {
environments: None,
items: vec![UserInput::Text {
text: "rename with change".into(),
text_elements: Vec::new(),
}],
final_output_json_schema: None,
cwd: harness.cwd().to_path_buf(),
approval_policy: AskForApproval::Never,
approvals_reviewer: None,
sandbox_policy: SandboxPolicy::DangerFullAccess,
permission_profile: None,
model,
effort: None,
summary: None,
service_tier: None,
collaboration_mode: None,
personality: None,
})
.await?;
harness.submit("rename with change").await?;

let mut last_diff: Option<String> = None;
wait_for_event(&codex, |event| match event {
Expand Down Expand Up @@ -1480,28 +1353,7 @@ async fn apply_patch_aggregates_diff_across_multiple_tool_calls() -> Result<()>
]);
mount_sse_sequence(harness.server(), vec![s1, s2, s3]).await;

let model = test.session_configured.model.clone();
codex
.submit(Op::UserTurn {
environments: None,
items: vec![UserInput::Text {
text: "aggregate diffs".into(),
text_elements: Vec::new(),
}],
final_output_json_schema: None,
cwd: harness.cwd().to_path_buf(),
approval_policy: AskForApproval::Never,
approvals_reviewer: None,
sandbox_policy: SandboxPolicy::DangerFullAccess,
permission_profile: None,
model,
effort: None,
summary: None,
service_tier: None,
collaboration_mode: None,
personality: None,
})
.await?;
harness.submit("aggregate diffs").await?;

let mut last_diff: Option<String> = None;
wait_for_event(&codex, |event| match event {
Expand Down Expand Up @@ -1558,28 +1410,7 @@ async fn apply_patch_aggregates_diff_preserves_success_after_failure() -> Result
];
mount_sse_sequence(harness.server(), responses).await;

let model = test.session_configured.model.clone();
codex
.submit(Op::UserTurn {
environments: None,
items: vec![UserInput::Text {
text: "apply patch twice with failure".into(),
text_elements: Vec::new(),
}],
final_output_json_schema: None,
cwd: harness.cwd().to_path_buf(),
approval_policy: AskForApproval::Never,
approvals_reviewer: None,
sandbox_policy: SandboxPolicy::DangerFullAccess,
permission_profile: None,
model,
effort: None,
summary: None,
service_tier: None,
collaboration_mode: None,
personality: None,
})
.await?;
harness.submit("apply patch twice with failure").await?;

let mut last_diff: Option<String> = None;
wait_for_event_with_timeout(
Expand Down
Loading