Codex Handbook
exec-server/tests/file_system/shared.rs 634 lines
use anyhow::Context;use anyhow::Result;use codex_exec_server::CopyOptions;use codex_exec_server::CreateDirectoryOptions;use codex_exec_server::FILE_READ_CHUNK_SIZE;use codex_exec_server::FileMetadata;use codex_exec_server::ReadDirectoryEntry;use codex_exec_server::RemoveOptions;use codex_protocol::models::AdditionalPermissionProfile;use codex_protocol::models::FileSystemPermissions;use codex_protocol::models::PermissionProfile;use codex_sandboxing::policy_transforms::effective_file_system_sandbox_policy;use codex_sandboxing::policy_transforms::effective_network_sandbox_policy;use codex_utils_path_uri::PathUri;use futures::TryStreamExt;use pretty_assertions::assert_eq;use std::path::Path;use tempfile::TempDir;use test_case::test_case;use super::support::FileSystemImplementation;use super::support::absolute_path;use super::support::create_file_system_context;use super::support::read_only_sandbox;use super::support::workspace_write_sandbox;#[test]fn sandbox_context_from_profile_preserves_workspace_write_read_only_subpaths() -> Result<()> {    let tmp = TempDir::new()?;    let writable_dir = tmp.path().join("writable");    let git_dir = writable_dir.join(".git");    std::fs::create_dir_all(&git_dir)?;    let sandbox = workspace_write_sandbox(writable_dir.clone());    let permissions: PermissionProfile = sandbox.permissions.try_into()?;    let policy = permissions.file_system_sandbox_policy();    let cwd = absolute_path(writable_dir.clone());    let writable_roots = policy.get_writable_roots_with_cwd(cwd.as_path());    let writable_dir = absolute_path(std::fs::canonicalize(writable_dir)?);    let git_dir = absolute_path(std::fs::canonicalize(git_dir)?);    let Some(writable_root) = writable_roots        .iter()        .find(|writable_root| writable_root.root == writable_dir)    else {        panic!("writable root should be preserved");    };    assert!(writable_root.read_only_subpaths.contains(&git_dir));    Ok(())}#[test_case(FileSystemImplementation::Local ; "local")]#[test_case(FileSystemImplementation::Remote ; "remote")]#[tokio::test(flavor = "multi_thread", worker_threads = 2)]async fn file_system_get_metadata_reports_files_and_directories(    implementation: FileSystemImplementation,) -> Result<()> {    let context = create_file_system_context(implementation).await?;    let file_system = context.file_system;    let tmp = TempDir::new()?;    let file_path = tmp.path().join("note.txt");    let directory_path = tmp.path().join("notes");    std::fs::write(&file_path, "hello")?;    std::fs::create_dir(&directory_path)?;    let file_metadata = file_system        .get_metadata(&PathUri::from_path(&file_path)?, /*sandbox*/ None)        .await        .with_context(|| format!("mode={implementation}"))?;    assert_eq!(        file_metadata,        FileMetadata {            is_directory: false,            is_file: true,            is_symlink: false,            size: 5,            created_at_ms: file_metadata.created_at_ms,            modified_at_ms: file_metadata.modified_at_ms,        }    );    assert!(file_metadata.modified_at_ms > 0);    let directory_metadata = file_system        .get_metadata(&PathUri::from_path(&directory_path)?, /*sandbox*/ None)        .await        .with_context(|| format!("mode={implementation}"))?;    assert_eq!(        directory_metadata,        FileMetadata {            is_directory: true,            is_file: false,            is_symlink: false,            size: std::fs::metadata(&directory_path)?.len(),            created_at_ms: directory_metadata.created_at_ms,            modified_at_ms: directory_metadata.modified_at_ms,        }    );    assert!(directory_metadata.modified_at_ms > 0);    Ok(())}#[test_case(FileSystemImplementation::Local ; "local")]#[test_case(FileSystemImplementation::Remote ; "remote")]#[tokio::test(flavor = "multi_thread", worker_threads = 2)]async fn file_system_create_directory_creates_nested_directories(    implementation: FileSystemImplementation,) -> Result<()> {    let context = create_file_system_context(implementation).await?;    let file_system = context.file_system;    let tmp = TempDir::new()?;    let nested_dir = tmp.path().join("source").join("nested");    file_system        .create_directory(            &PathUri::from_path(&nested_dir)?,            CreateDirectoryOptions { recursive: true },            /*sandbox*/ None,        )        .await        .with_context(|| format!("mode={implementation}"))?;    assert!(nested_dir.is_dir());    Ok(())}#[test_case(FileSystemImplementation::Local ; "local")]#[test_case(FileSystemImplementation::Remote ; "remote")]#[tokio::test(flavor = "multi_thread", worker_threads = 2)]async fn file_system_write_file_writes_bytes(    implementation: FileSystemImplementation,) -> Result<()> {    let context = create_file_system_context(implementation).await?;    let file_system = context.file_system;    let tmp = TempDir::new()?;    let file_path = tmp.path().join("note.txt");    file_system        .write_file(            &PathUri::from_path(&file_path)?,            b"hello from trait".to_vec(),            /*sandbox*/ None,        )        .await        .with_context(|| format!("mode={implementation}"))?;    assert_eq!(std::fs::read(file_path)?, b"hello from trait");    Ok(())}#[test]fn path_uri_join_and_parent_preserve_lexical_paths() -> Result<()> {    let tmp = TempDir::new()?;    let source_dir = tmp.path().join("source");    let source_dir_uri = PathUri::from_path(&source_dir)?;    let joined_nested = source_dir_uri.join("nested/note.txt")?;    assert_eq!(        joined_nested,        PathUri::from_path(source_dir.join("nested").join("note.txt"))?    );    let joined_parent = joined_nested.parent();    assert_eq!(        joined_parent,        Some(PathUri::from_path(source_dir.join("nested"))?)    );    let joined_parent_traversal = source_dir_uri.join("../outside")?;    assert_eq!(        joined_parent_traversal,        PathUri::from_path(source_dir.join("../outside"))?    );    Ok(())}#[test_case(FileSystemImplementation::Local ; "local")]#[test_case(FileSystemImplementation::Remote ; "remote")]#[tokio::test(flavor = "multi_thread", worker_threads = 2)]async fn file_system_read_file_returns_bytes(    implementation: FileSystemImplementation,) -> Result<()> {    let context = create_file_system_context(implementation).await?;    let file_system = context.file_system;    let tmp = TempDir::new()?;    let file_path = tmp.path().join("note.txt");    std::fs::write(&file_path, "hello from trait")?;    let contents = file_system        .read_file(&PathUri::from_path(&file_path)?, /*sandbox*/ None)        .await        .with_context(|| format!("mode={implementation}"))?;    assert_eq!(contents, b"hello from trait");    Ok(())}#[test_case(FileSystemImplementation::Local ; "local")]#[test_case(FileSystemImplementation::Remote ; "remote")]#[tokio::test(flavor = "multi_thread", worker_threads = 2)]async fn file_system_read_file_stream_returns_bounded_chunks(    implementation: FileSystemImplementation,) -> Result<()> {    let context = create_file_system_context(implementation).await?;    let file_system = context.file_system;    let tmp = TempDir::new()?;    let file_path = tmp.path().join("blocks.bin");    let contents = (0..FILE_READ_CHUNK_SIZE * 2 + 17)        .map(|index| (index % 251) as u8)        .collect::<Vec<_>>();    std::fs::write(&file_path, &contents)?;    let chunks = file_system        .read_file_stream(&PathUri::from_path(file_path)?, /*sandbox*/ None)        .await        .with_context(|| format!("mode={implementation}"))?        .try_collect::<Vec<_>>()        .await?;    assert!(        chunks            .iter()            .all(|chunk| !chunk.is_empty() && chunk.len() <= FILE_READ_CHUNK_SIZE)    );    assert_eq!(        chunks            .iter()            .flat_map(|chunk| chunk.iter().copied())            .collect::<Vec<_>>(),        contents    );    Ok(())}#[test_case(FileSystemImplementation::Local ; "local")]#[test_case(FileSystemImplementation::Remote ; "remote")]#[tokio::test(flavor = "multi_thread", worker_threads = 2)]async fn file_system_read_file_text_returns_string(    implementation: FileSystemImplementation,) -> Result<()> {    let context = create_file_system_context(implementation).await?;    let file_system = context.file_system;    let tmp = TempDir::new()?;    let file_path = tmp.path().join("note.txt");    std::fs::write(&file_path, "hello from trait")?;    let contents = file_system        .read_file_text(&PathUri::from_path(&file_path)?, /*sandbox*/ None)        .await        .with_context(|| format!("mode={implementation}"))?;    assert_eq!(contents, "hello from trait");    Ok(())}#[test_case(FileSystemImplementation::Local ; "local")]#[test_case(FileSystemImplementation::Remote ; "remote")]#[tokio::test(flavor = "multi_thread", worker_threads = 2)]async fn file_system_copy_copies_file(implementation: FileSystemImplementation) -> Result<()> {    let context = create_file_system_context(implementation).await?;    let file_system = context.file_system;    let tmp = TempDir::new()?;    let source_file = tmp.path().join("source.txt");    let copied_file = tmp.path().join("copy.txt");    std::fs::write(&source_file, "hello from trait")?;    file_system        .copy(            &PathUri::from_path(&source_file)?,            &PathUri::from_path(&copied_file)?,            CopyOptions { recursive: false },            /*sandbox*/ None,        )        .await        .with_context(|| format!("mode={implementation}"))?;    assert_eq!(std::fs::read_to_string(copied_file)?, "hello from trait");    Ok(())}#[test_case(FileSystemImplementation::Local ; "local")]#[test_case(FileSystemImplementation::Remote ; "remote")]#[tokio::test(flavor = "multi_thread", worker_threads = 2)]async fn file_system_copy_copies_directory_recursively(    implementation: FileSystemImplementation,) -> Result<()> {    let context = create_file_system_context(implementation).await?;    let file_system = context.file_system;    let tmp = TempDir::new()?;    let source_dir = tmp.path().join("source");    let nested_dir = source_dir.join("nested");    let nested_file = nested_dir.join("note.txt");    let copied_dir = tmp.path().join("copied");    std::fs::create_dir_all(&nested_dir)?;    std::fs::write(&nested_file, "hello from trait")?;    file_system        .copy(            &PathUri::from_path(&source_dir)?,            &PathUri::from_path(&copied_dir)?,            CopyOptions { recursive: true },            /*sandbox*/ None,        )        .await        .with_context(|| format!("mode={implementation}"))?;    assert_eq!(        std::fs::read_to_string(copied_dir.join("nested").join("note.txt"))?,        "hello from trait"    );    Ok(())}#[test_case(FileSystemImplementation::Local ; "local")]#[test_case(FileSystemImplementation::Remote ; "remote")]#[tokio::test(flavor = "multi_thread", worker_threads = 2)]async fn file_system_read_directory_lists_entries(    implementation: FileSystemImplementation,) -> Result<()> {    let context = create_file_system_context(implementation).await?;    let file_system = context.file_system;    let tmp = TempDir::new()?;    let source_dir = tmp.path().join("source");    std::fs::create_dir_all(source_dir.join("nested"))?;    std::fs::write(source_dir.join("root.txt"), "hello")?;    let mut entries = file_system        .read_directory(&PathUri::from_path(&source_dir)?, /*sandbox*/ None)        .await        .with_context(|| format!("mode={implementation}"))?;    entries.sort_by(|left, right| left.file_name.cmp(&right.file_name));    assert_eq!(        entries,        vec![            ReadDirectoryEntry {                file_name: "nested".to_string(),                is_directory: true,                is_file: false,            },            ReadDirectoryEntry {                file_name: "root.txt".to_string(),                is_directory: false,                is_file: true,            },        ]    );    Ok(())}#[test_case(FileSystemImplementation::Local ; "local")]#[test_case(FileSystemImplementation::Remote ; "remote")]#[tokio::test(flavor = "multi_thread", worker_threads = 2)]async fn file_system_remove_removes_directory(    implementation: FileSystemImplementation,) -> Result<()> {    let context = create_file_system_context(implementation).await?;    let file_system = context.file_system;    let tmp = TempDir::new()?;    let directory_path = tmp.path().join("remove-me");    std::fs::create_dir_all(directory_path.join("nested"))?;    file_system        .remove(            &PathUri::from_path(&directory_path)?,            RemoveOptions {                recursive: true,                force: true,            },            /*sandbox*/ None,        )        .await        .with_context(|| format!("mode={implementation}"))?;    assert!(!directory_path.exists());    Ok(())}#[test_case(FileSystemImplementation::Local ; "local")]#[test_case(FileSystemImplementation::Remote ; "remote")]#[tokio::test(flavor = "multi_thread", worker_threads = 2)]async fn file_system_write_file_reports_missing_parent(    implementation: FileSystemImplementation,) -> Result<()> {    let context = create_file_system_context(implementation).await?;    let file_system = context.file_system;    let tmp = TempDir::new()?;    let missing_parent_path = tmp.path().join("missing").join("note.txt");    let error = match file_system        .write_file(            &PathUri::from_path(&missing_parent_path)?,            b"hello from trait".to_vec(),            /*sandbox*/ None,        )        .await    {        Ok(()) => anyhow::bail!("write should fail when parent directory is absent"),        Err(error) => error,    };    assert_eq!(        error.kind(),        std::io::ErrorKind::NotFound,        "mode={implementation}"    );    assert!(!missing_parent_path.exists(), "mode={implementation}");    Ok(())}#[test_case(FileSystemImplementation::Local ; "local")]#[test_case(FileSystemImplementation::Remote ; "remote")]#[tokio::test(flavor = "multi_thread", worker_threads = 2)]async fn file_system_copy_rejects_directory_without_recursive(    implementation: FileSystemImplementation,) -> Result<()> {    let context = create_file_system_context(implementation).await?;    let file_system = context.file_system;    let tmp = TempDir::new()?;    let source_dir = tmp.path().join("source");    std::fs::create_dir_all(&source_dir)?;    let error = file_system        .copy(            &PathUri::from_path(&source_dir)?,            &PathUri::from_path(tmp.path().join("dest"))?,            CopyOptions { recursive: false },            /*sandbox*/ None,        )        .await;    let error = error.expect_err("copying a directory without recursion should fail");    assert_eq!(error.kind(), std::io::ErrorKind::InvalidInput);    assert_eq!(        error.to_string(),        "fs/copy requires recursive: true when sourcePath is a directory"    );    Ok(())}#[test_case(FileSystemImplementation::Local ; "local")]#[test_case(FileSystemImplementation::Remote ; "remote")]#[tokio::test(flavor = "multi_thread", worker_threads = 2)]async fn file_system_sandboxed_metadata_and_read_allow_readable_root(    implementation: FileSystemImplementation,) -> Result<()> {    let context = create_file_system_context(implementation).await?;    let file_system = context.file_system;    let tmp = TempDir::new()?;    let allowed_dir = tmp.path().join("allowed");    let file_path = allowed_dir.join("note.txt");    std::fs::create_dir_all(&allowed_dir)?;    std::fs::write(&file_path, "sandboxed hello")?;    let sandbox = read_only_sandbox(allowed_dir);    let metadata = file_system        .get_metadata(&PathUri::from_path(&file_path)?, Some(&sandbox))        .await        .with_context(|| format!("mode={implementation}"))?;    assert_eq!(        metadata,        FileMetadata {            is_directory: false,            is_file: true,            is_symlink: false,            size: 15,            created_at_ms: metadata.created_at_ms,            modified_at_ms: metadata.modified_at_ms,        }    );    let contents = file_system        .read_file(&PathUri::from_path(&file_path)?, Some(&sandbox))        .await        .with_context(|| format!("mode={implementation}"))?;    assert_eq!(contents, b"sandboxed hello");    Ok(())}pub(crate) async fn assert_canonicalize_resolves_directory_alias(    implementation: FileSystemImplementation,    create_directory_alias: impl FnOnce(&Path, &Path) -> Result<()>,) -> Result<()> {    let context = create_file_system_context(implementation).await?;    let file_system = context.file_system;    let tmp = TempDir::new()?;    let source_dir = tmp.path().join("source");    let nested_dir = source_dir.join("nested");    let file_path = nested_dir.join("note.txt");    let alias_dir = tmp.path().join("source-alias");    std::fs::create_dir_all(&nested_dir)?;    std::fs::write(&file_path, "canonical hello")?;    create_directory_alias(&source_dir, &alias_dir)?;    let requested_path = PathUri::from_path(alias_dir.join("nested").join("note.txt"))?;    let expected_path = PathUri::from_path(std::fs::canonicalize(&file_path)?)?;    assert_ne!(requested_path, expected_path);    let canonical_path = file_system        .canonicalize(&requested_path, /*sandbox*/ None)        .await        .with_context(|| format!("mode={implementation}"))?;    assert_eq!(canonical_path, expected_path);    Ok(())}pub(crate) async fn assert_sandboxed_canonicalize_resolves_directory_alias(    implementation: FileSystemImplementation,    create_directory_alias: impl FnOnce(&Path, &Path) -> Result<()>,) -> Result<()> {    let context = create_file_system_context(implementation).await?;    let file_system = context.file_system;    let tmp = TempDir::new()?;    let source_dir = tmp.path().join("source");    let nested_dir = source_dir.join("nested");    let file_path = nested_dir.join("note.txt");    let alias_dir = tmp.path().join("source-alias");    std::fs::create_dir_all(&nested_dir)?;    std::fs::write(&file_path, "sandboxed canonical hello")?;    create_directory_alias(&source_dir, &alias_dir)?;    let sandbox = read_only_sandbox(tmp.path().to_path_buf());    let requested_path = PathUri::from_path(alias_dir.join("nested").join("note.txt"))?;    let expected_path = PathUri::from_path(std::fs::canonicalize(&file_path)?)?;    assert_ne!(requested_path, expected_path);    let canonical_path = file_system        .canonicalize(&requested_path, Some(&sandbox))        .await        .with_context(|| format!("mode={implementation}"))?;    assert_eq!(canonical_path, expected_path);    Ok(())}/// Verifies that effective additional permissions extend a read-only sandbox with a writable root.#[test_case(FileSystemImplementation::Local ; "local")]#[test_case(FileSystemImplementation::Remote ; "remote")]#[tokio::test(flavor = "multi_thread", worker_threads = 2)]async fn file_system_sandboxed_write_allows_additional_write_root(    implementation: FileSystemImplementation,) -> Result<()> {    let context = create_file_system_context(implementation).await?;    let file_system = context.file_system;    let tmp = TempDir::new()?;    let readable_dir = tmp.path().join("readable");    let writable_dir = tmp.path().join("writable");    let file_path = writable_dir.join("note.txt");    std::fs::create_dir_all(&readable_dir)?;    std::fs::create_dir_all(&writable_dir)?;    let mut sandbox = read_only_sandbox(readable_dir);    let additional_permissions = AdditionalPermissionProfile {        network: None,        file_system: Some(FileSystemPermissions::from_read_write_roots(            /*read*/ None,            Some(vec![absolute_path(writable_dir)]),        )),    };    let native_permissions: PermissionProfile = sandbox.permissions.clone().try_into()?;    let file_system_policy = effective_file_system_sandbox_policy(        &native_permissions.file_system_sandbox_policy(),        Some(&additional_permissions),    );    let network_policy = effective_network_sandbox_policy(        native_permissions.network_sandbox_policy(),        Some(&additional_permissions),    );    sandbox.permissions = PermissionProfile::from_runtime_permissions_with_enforcement(        native_permissions.enforcement(),        &file_system_policy,        network_policy,    )    .into();    file_system        .write_file(            &PathUri::from_path(&file_path)?,            b"created".to_vec(),            Some(&sandbox),        )        .await        .with_context(|| format!("write file through additional root mode={implementation}"))?;    assert_eq!(std::fs::read(&file_path)?, b"created");    Ok(())}#[test_case(FileSystemImplementation::Local ; "local")]#[test_case(FileSystemImplementation::Remote ; "remote")]#[tokio::test(flavor = "multi_thread", worker_threads = 2)]async fn file_system_copy_rejects_copying_directory_into_descendant(    implementation: FileSystemImplementation,) -> Result<()> {    let context = create_file_system_context(implementation).await?;    let file_system = context.file_system;    let tmp = TempDir::new()?;    let source_dir = tmp.path().join("source");    std::fs::create_dir_all(source_dir.join("nested"))?;    let error = file_system        .copy(            &PathUri::from_path(&source_dir)?,            &PathUri::from_path(source_dir.join("nested").join("copy"))?,            CopyOptions { recursive: true },            /*sandbox*/ None,        )        .await;    let error = error.expect_err("copying a directory into itself should fail");    assert_eq!(error.kind(), std::io::ErrorKind::InvalidInput);    assert_eq!(        error.to_string(),        "fs/copy cannot copy a directory to itself or one of its descendants"    );    Ok(())}