core tests: migrate apply patch turns to profiles (#20040)

## Summary
- Removes `SandboxPolicy` from the apply-patch CLI test suite.
- Uses the harness' profile-backed submit helper for danger/no-sandbox
turns instead of constructing `Op::UserTurn` manually with legacy
fields.
- Converts the workspace-write traversal cases to submit
`PermissionProfile::workspace_write_with(...)` directly.

## Verification
- `cargo check -p codex-core --tests`
- `just fmt`
This commit is contained in:
Michael Bolin
2026-04-28 17:18:19 -07:00
committed by GitHub
Unverified
parent 1dae5788e1
commit 1fed948c66
+25 -194
View File
@@ -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;
@@ -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,
@@ -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 {
@@ -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?;
@@ -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;
@@ -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 {
@@ -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;
@@ -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 {
@@ -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 {
@@ -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 {
@@ -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 {
@@ -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(