core tests: submit turns with permission profiles (#20010)

## Summary

- Add `PermissionProfile`-based turn submission helpers to
`core_test_support`, while keeping the legacy `SandboxPolicy` helper for
tests that intentionally exercise legacy fallback behavior.
- Switch the default `TestCodex::submit_turn()` path to send a real
`PermissionProfile` plus the required legacy compatibility projection in
`Op::UserTurn`.
- Migrate straightforward app/search/shell/truncation tests from
`SandboxPolicy::{DangerFullAccess, ReadOnly}` to
`PermissionProfile::{Disabled, read_only}`.
- Add a TUI compatibility projection helper for legacy app-server fields
so non-legacy writable roots are preserved instead of being downgraded
to read-only.
- Fix remote start/resume/fork sandbox-mode projection to classify any
managed profile with writable roots as workspace-write, not only
profiles that can write `cwd`.
- Reduce `SandboxPolicy` references in `codex-rs/core/tests` from 47
files to 41 files without changing production behavior.

## Testing

- `cargo check -p codex-core --tests`
- `cargo test -p codex-tui
compatibility_profile_preserves_unbridgeable_write_roots`
- `cargo test -p codex-tui
sandbox_mode_preserves_non_cwd_write_roots_for_remote_sessions`
- `just fmt`
- `just fix -p core_test_support`
- `just fix -p codex-core`
This commit is contained in:
Michael Bolin
2026-04-28 16:01:40 -07:00
committed by GitHub
parent 2dbde94aa9
commit 891722849d
11 changed files with 354 additions and 118 deletions

View File

@@ -28,6 +28,7 @@ use codex_model_provider_info::built_in_model_providers;
use codex_models_manager::bundled_models_response;
use codex_models_manager::collaboration_mode_presets::CollaborationModesConfig;
use codex_protocol::config_types::ServiceTier;
use codex_protocol::models::PermissionProfile;
use codex_protocol::openai_models::ModelsResponse;
use codex_protocol::protocol::AskForApproval;
use codex_protocol::protocol::EventMsg;
@@ -591,10 +592,19 @@ impl TestCodex {
}
pub async fn submit_turn(&self, prompt: &str) -> Result<()> {
self.submit_turn_with_policies(
self.submit_turn_with_permission_profile(prompt, PermissionProfile::Disabled)
.await
}
pub async fn submit_turn_with_permission_profile(
&self,
prompt: &str,
permission_profile: PermissionProfile,
) -> Result<()> {
self.submit_turn_with_approval_and_permission_profile(
prompt,
AskForApproval::Never,
SandboxPolicy::DangerFullAccess,
permission_profile,
)
.await
}
@@ -613,10 +623,10 @@ impl TestCodex {
prompt: &str,
service_tier: Option<ServiceTier>,
) -> Result<()> {
self.submit_turn_with_context(
self.submit_turn_with_permission_profile_context(
prompt,
AskForApproval::Never,
SandboxPolicy::DangerFullAccess,
PermissionProfile::Disabled,
Some(service_tier),
/*environments*/ None,
)
@@ -629,10 +639,30 @@ impl TestCodex {
approval_policy: AskForApproval,
sandbox_policy: SandboxPolicy,
) -> Result<()> {
let permission_profile = PermissionProfile::from_legacy_sandbox_policy_for_cwd(
&sandbox_policy,
self.config.cwd.as_path(),
);
self.submit_turn_with_context(
prompt,
approval_policy,
sandbox_policy,
permission_profile,
/*service_tier*/ None,
/*environments*/ None,
)
.await
}
pub async fn submit_turn_with_approval_and_permission_profile(
&self,
prompt: &str,
approval_policy: AskForApproval,
permission_profile: PermissionProfile,
) -> Result<()> {
self.submit_turn_with_permission_profile_context(
prompt,
approval_policy,
permission_profile,
/*service_tier*/ None,
/*environments*/ None,
)
@@ -644,24 +674,45 @@ impl TestCodex {
prompt: &str,
environments: Option<Vec<TurnEnvironmentSelection>>,
) -> Result<()> {
self.submit_turn_with_context(
self.submit_turn_with_permission_profile_context(
prompt,
AskForApproval::Never,
SandboxPolicy::DangerFullAccess,
PermissionProfile::Disabled,
/*service_tier*/ None,
environments,
)
.await
}
async fn submit_turn_with_permission_profile_context(
&self,
prompt: &str,
approval_policy: AskForApproval,
permission_profile: PermissionProfile,
service_tier: Option<Option<ServiceTier>>,
environments: Option<Vec<TurnEnvironmentSelection>>,
) -> Result<()> {
self.submit_turn_with_context(
prompt,
approval_policy,
permission_profile,
service_tier,
environments,
)
.await
}
async fn submit_turn_with_context(
&self,
prompt: &str,
approval_policy: AskForApproval,
sandbox_policy: SandboxPolicy,
permission_profile: PermissionProfile,
service_tier: Option<Option<ServiceTier>>,
environments: Option<Vec<TurnEnvironmentSelection>>,
) -> Result<()> {
let sandbox_policy = permission_profile
.to_legacy_sandbox_policy(self.config.cwd.as_path())
.unwrap_or_else(|_| SandboxPolicy::new_read_only_policy());
let session_model = self.session_configured.model.clone();
self.codex
.submit(Op::UserTurn {
@@ -675,7 +726,7 @@ impl TestCodex {
approval_policy,
approvals_reviewer: None,
sandbox_policy,
permission_profile: None,
permission_profile: Some(permission_profile),
model: session_model,
effort: None,
summary: None,
@@ -835,6 +886,16 @@ impl TestCodexHarness {
.await
}
pub async fn submit_with_permission_profile(
&self,
prompt: &str,
permission_profile: PermissionProfile,
) -> Result<()> {
self.test
.submit_turn_with_permission_profile(prompt, permission_profile)
.await
}
pub async fn request_bodies(&self) -> Vec<Value> {
let path_matcher = path_regex(".*/responses$");
self.server