| mod common; |
|
|
| use codex_config::CONFIG_TOML_FILE; |
| use codex_config::ConfigLayerSource; |
| use codex_config::format_config_layer_source; |
| use codex_config::loader::project_trust_key; |
| use codex_exec_server::Environment; |
| use codex_exec_server::EnvironmentConfigLayer; |
| use codex_exec_server::EnvironmentConfigLayerStack; |
| use codex_exec_server::EnvironmentConfigReadParams; |
| use codex_exec_server::EnvironmentConfigReadResponse; |
| use codex_exec_server::ExecServerError; |
| use codex_utils_absolute_path::AbsolutePathBuf; |
| use codex_utils_path_uri::PathUri; |
| use common::exec_server::exec_server; |
| use pretty_assertions::assert_eq; |
|
|
| #[tokio::test(flavor = "multi_thread", worker_threads = 2)] |
| async fn remote_environment_reads_projected_executor_config() -> anyhow::Result<()> { |
| let mut server = exec_server().await?; |
| let codex_home = |
| AbsolutePathBuf::from_absolute_path(std::fs::canonicalize(server.codex_home())?)?; |
| let config_file = codex_home.join(CONFIG_TOML_FILE); |
| let project = codex_home.join("project"); |
| let dot_codex = project.join(".codex"); |
| tokio::fs::create_dir_all(dot_codex.as_path()).await?; |
| tokio::fs::write(project.join(".project-root").as_path(), "").await?; |
| let project_key = toml::Value::String(project_trust_key(project.as_path())).to_string(); |
| tokio::fs::write( |
| &config_file, |
| format!( |
| "project_root_markers = [\".project-root\"]\n[projects.{project_key}]\ntrust_level = \"trusted\"" |
| ), |
| ) |
| .await?; |
| tokio::fs::write( |
| dot_codex.join(CONFIG_TOML_FILE).as_path(), |
| r#" |
| [future_environment] |
| relative_path = "./executor-relative" |
| unselected = "do not return" |
| "#, |
| ) |
| .await?; |
|
|
| let environment = Environment::create_for_tests(Some(server.websocket_url().to_string()))?; |
| let environment_info = environment.info().await?; |
| assert!(environment_info.capabilities.environment_config_read); |
| assert_eq!( |
| environment_info.user_home_dir, |
| dirs::home_dir().and_then(|home_dir| PathUri::from_host_native_path(home_dir).ok()), |
| ); |
|
|
| let response = environment |
| .read_environment_config(EnvironmentConfigReadParams { |
| cwd: PathUri::from_abs_path(&project), |
| config_paths: vec![vec![ |
| "future_environment".to_string(), |
| "relative_path".to_string(), |
| ]], |
| requirements_paths: Vec::new(), |
| }) |
| .await?; |
|
|
| let projected_toml = toml::toml! { |
| [future_environment] |
| relative_path = "./executor-relative" |
| }; |
| assert_eq!( |
| response, |
| EnvironmentConfigReadResponse { |
| user_home_dir: dirs::home_dir() |
| .and_then(|home_dir| PathUri::from_host_native_path(home_dir).ok()), |
| codex_home_dir: PathUri::from_abs_path(&codex_home), |
| hostname: codex_config::host_name(), |
| config: EnvironmentConfigLayerStack { |
| layers: vec![EnvironmentConfigLayer { |
| source: format_config_layer_source( |
| &ConfigLayerSource::Project { |
| dot_codex_folder: dot_codex.clone(), |
| }, |
| CONFIG_TOML_FILE, |
| ), |
| base_dir: PathUri::from_abs_path(&dot_codex), |
| toml: toml::to_string(&projected_toml)?, |
| }], |
| cloud_insertion_index: 0, |
| }, |
| requirements: EnvironmentConfigLayerStack { |
| layers: Vec::new(), |
| cloud_insertion_index: 0, |
| }, |
| } |
| ); |
|
|
| server.shutdown().await?; |
| Ok(()) |
| } |
|
|
| #[tokio::test(flavor = "multi_thread", worker_threads = 2)] |
| async fn environment_config_read_rejects_empty_selectors() -> anyhow::Result<()> { |
| let mut server = exec_server().await?; |
| let codex_home = |
| AbsolutePathBuf::from_absolute_path(std::fs::canonicalize(server.codex_home())?)?; |
| let environment = Environment::create_for_tests(Some(server.websocket_url().to_string()))?; |
|
|
| for (config_paths, expected_message) in [ |
| ( |
| Vec::new(), |
| "at least one config or requirements path is required", |
| ), |
| ( |
| vec![Vec::new()], |
| "TOML paths must contain at least one key segment", |
| ), |
| ] { |
| let error = environment |
| .read_environment_config(EnvironmentConfigReadParams { |
| cwd: PathUri::from_abs_path(&codex_home), |
| config_paths, |
| requirements_paths: Vec::new(), |
| }) |
| .await |
| .expect_err("invalid selectors should fail"); |
| assert!( |
| matches!( |
| error, |
| ExecServerError::Server { code: -32602, ref message } |
| if message == expected_message |
| ), |
| "unexpected error: {error:?}" |
| ); |
| } |
|
|
| server.shutdown().await?; |
| Ok(()) |
| } |
|
|