mirror of
https://github.com/openai/codex.git
synced 2026-08-23 13:09:46 +00:00
Consolidate deferred environment provisioning APIs (#37340)
## What changed - Remove the separate deferred registration handle, direct readiness publisher, and Noise environment upsert API from `EnvironmentManager`. - Use `report_environment_provisioning_status` and `materialize_pending_noise_environment` as the provisioning flow while keeping ordinary environments isolated from provisioning reports. - Cover readiness updates, invalid reports, duplicate materialization, and conflicts with ordinary environments in the deferred environment tests. GitOrigin-RevId: d14207c0abb0636d331be4f875fec6e53f3d2fe6
This commit is contained in:
@@ -89,13 +89,6 @@ pub struct EnvironmentReadyInfo {
|
||||
pub selected_capability_roots: Vec<SelectedCapabilityRoot>,
|
||||
}
|
||||
|
||||
/// The one-shot capability to complete a deferred environment registration.
|
||||
#[must_use = "the deferred environment cannot connect until registration is completed"]
|
||||
pub struct DeferredEnvironmentRegistration {
|
||||
environment_id: String,
|
||||
environment: Option<Arc<Environment>>,
|
||||
}
|
||||
|
||||
/// Maximum capability roots accepted from environment ready information.
|
||||
pub const MAX_SELECTED_CAPABILITY_ROOTS: usize = 256;
|
||||
|
||||
@@ -232,7 +225,16 @@ impl EnvironmentManager {
|
||||
local_runtime_paths,
|
||||
http_client_factory,
|
||||
};
|
||||
manager.upsert_noise_environment(REMOTE_ENVIRONMENT_ID.to_string(), connect_provider)?;
|
||||
let identity = noise_channel_identity()?;
|
||||
let environment = Arc::new(Environment::remote_with_transport(
|
||||
ExecServerTransportParams::NoiseRendezvous {
|
||||
provider: connect_provider,
|
||||
identity,
|
||||
},
|
||||
manager.local_runtime_paths.clone(),
|
||||
manager.http_client_factory.clone(),
|
||||
));
|
||||
manager.insert_environment(REMOTE_ENVIRONMENT_ID.to_string(), environment)?;
|
||||
Ok(manager)
|
||||
}
|
||||
|
||||
@@ -380,26 +382,6 @@ impl EnvironmentManager {
|
||||
.cloned()
|
||||
}
|
||||
|
||||
/// Publishes capability roots without changing an environment's provisioning state.
|
||||
pub fn publish_ready_info(
|
||||
&self,
|
||||
environment_id: &str,
|
||||
ready_info: EnvironmentReadyInfo,
|
||||
) -> Result<(), ExecServerError> {
|
||||
validate_environment_id(environment_id)?;
|
||||
validate_environment_ready_info(environment_id, &ready_info)?;
|
||||
|
||||
let environments = self
|
||||
.environments
|
||||
.read()
|
||||
.unwrap_or_else(std::sync::PoisonError::into_inner);
|
||||
let environment = environments.get(environment_id).ok_or_else(|| {
|
||||
ExecServerError::Protocol(format!("environment `{environment_id}` is not registered"))
|
||||
})?;
|
||||
environment.ready_info.store(Some(Arc::new(ready_info)));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Records a Ready or Failed provisioning result for an environment.
|
||||
///
|
||||
/// Ordinary environments are ignored. A provisioned environment keeps the same `Arc` from
|
||||
@@ -489,22 +471,6 @@ impl EnvironmentManager {
|
||||
self.insert_environment(environment_id, environment)
|
||||
}
|
||||
|
||||
/// Adds or replaces a Noise environment completed through its registration handle.
|
||||
pub fn register_deferred_noise_environment(
|
||||
&self,
|
||||
environment_id: String,
|
||||
provider: Arc<dyn NoiseRendezvousConnectProvider>,
|
||||
) -> Result<DeferredEnvironmentRegistration, ExecServerError> {
|
||||
validate_environment_id(&environment_id)?;
|
||||
let environment =
|
||||
Arc::new(self.provisioning_noise_environment(provider, /*initial_result*/ None)?);
|
||||
self.insert_environment(environment_id.clone(), Arc::clone(&environment))?;
|
||||
Ok(DeferredEnvironmentRegistration {
|
||||
environment_id,
|
||||
environment: Some(environment),
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns the stable environment for an ID, creating it as pending when absent.
|
||||
pub fn materialize_pending_noise_environment(
|
||||
&self,
|
||||
@@ -548,22 +514,6 @@ impl EnvironmentManager {
|
||||
Ok(environment)
|
||||
}
|
||||
|
||||
/// Adds or replaces a named remote environment using authenticated Noise rendezvous.
|
||||
pub fn upsert_noise_environment(
|
||||
&self,
|
||||
environment_id: String,
|
||||
provider: Arc<dyn NoiseRendezvousConnectProvider>,
|
||||
) -> Result<(), ExecServerError> {
|
||||
validate_environment_id(&environment_id)?;
|
||||
let identity = noise_channel_identity()?;
|
||||
let environment = Arc::new(Environment::remote_with_transport(
|
||||
ExecServerTransportParams::NoiseRendezvous { provider, identity },
|
||||
self.local_runtime_paths.clone(),
|
||||
self.http_client_factory.clone(),
|
||||
));
|
||||
self.insert_environment(environment_id, environment)
|
||||
}
|
||||
|
||||
fn insert_environment(
|
||||
&self,
|
||||
environment_id: String,
|
||||
@@ -582,36 +532,6 @@ impl EnvironmentManager {
|
||||
}
|
||||
}
|
||||
|
||||
impl DeferredEnvironmentRegistration {
|
||||
/// Completes provisioning with ready information or a terminal error message.
|
||||
pub fn complete(
|
||||
mut self,
|
||||
result: Result<EnvironmentReadyInfo, String>,
|
||||
) -> Result<(), ExecServerError> {
|
||||
let Some(environment) = self.environment.take() else {
|
||||
return Err(ExecServerError::Disconnected(
|
||||
"deferred environment registration is inactive".into(),
|
||||
));
|
||||
};
|
||||
|
||||
match result {
|
||||
Ok(ready_info) => environment.apply_ready_report(&self.environment_id, ready_info),
|
||||
Err(error) => environment.apply_error_report(&self.environment_id, error),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for DeferredEnvironmentRegistration {
|
||||
fn drop(&mut self) {
|
||||
if let Some(environment) = self.environment.take() {
|
||||
let _ = environment.apply_error_report(
|
||||
&self.environment_id,
|
||||
"environment registration ended before completion".to_string(),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn validate_environment_ready_info(
|
||||
environment_id: &str,
|
||||
ready_info: &EnvironmentReadyInfo,
|
||||
|
||||
@@ -81,7 +81,6 @@ pub use environment::CODEX_EXEC_SERVER_NOISE_CHATGPT_ACCOUNT_ID_ENV_VAR;
|
||||
pub use environment::CODEX_EXEC_SERVER_NOISE_ENVIRONMENT_ID_ENV_VAR;
|
||||
pub use environment::CODEX_EXEC_SERVER_NOISE_REGISTRY_URL_ENV_VAR;
|
||||
pub use environment::CODEX_EXEC_SERVER_URL_ENV_VAR;
|
||||
pub use environment::DeferredEnvironmentRegistration;
|
||||
pub use environment::Environment;
|
||||
pub use environment::EnvironmentConnectionState;
|
||||
pub use environment::EnvironmentManager;
|
||||
|
||||
@@ -54,367 +54,6 @@ fn ready_info(root_id: &str, environment_id: &str) -> anyhow::Result<Environment
|
||||
})
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn deferred_environment_waits_before_connecting() -> anyhow::Result<()> {
|
||||
let manager = environment_manager_without_environments();
|
||||
let provider = Arc::new(FailingNoiseConnectProvider::default());
|
||||
let registration =
|
||||
manager.register_deferred_noise_environment("tools".to_string(), provider.clone())?;
|
||||
let environment = manager.get_environment("tools").expect("environment");
|
||||
let connection_state = environment
|
||||
.subscribe_connection_state()
|
||||
.expect("remote environment connection state");
|
||||
let mut readiness = Box::pin(environment.wait_until_ready());
|
||||
|
||||
assert!(poll!(&mut readiness).is_pending());
|
||||
assert_eq!(provider.calls(), 0);
|
||||
assert!(environment.selected_capability_roots().is_empty());
|
||||
|
||||
let ready_info = ready_info("selected-root", "tools")?;
|
||||
registration.complete(Ok(ready_info.clone()))?;
|
||||
assert_eq!(
|
||||
environment.selected_capability_roots(),
|
||||
ready_info.selected_capability_roots
|
||||
);
|
||||
let error = readiness.await.unwrap_err();
|
||||
assert!(error.to_string().contains("test Noise provider called"));
|
||||
assert_eq!(provider.calls(), 1);
|
||||
assert!(!connection_state.has_changed()?);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn deferred_registration_replaces_an_ordinary_noise_environment() -> anyhow::Result<()> {
|
||||
let manager = environment_manager_without_environments();
|
||||
manager.upsert_noise_environment(
|
||||
"tools".to_string(),
|
||||
Arc::new(FailingNoiseConnectProvider::default()),
|
||||
)?;
|
||||
let ordinary = manager
|
||||
.get_environment("tools")
|
||||
.expect("ordinary environment");
|
||||
let deferred_provider = Arc::new(FailingNoiseConnectProvider::default());
|
||||
|
||||
let registration = manager
|
||||
.register_deferred_noise_environment("tools".to_string(), deferred_provider.clone())?;
|
||||
let deferred = manager
|
||||
.get_environment("tools")
|
||||
.expect("deferred environment");
|
||||
assert!(!Arc::ptr_eq(&ordinary, &deferred));
|
||||
let mut readiness = Box::pin(deferred.wait_until_ready());
|
||||
assert!(poll!(&mut readiness).is_pending());
|
||||
|
||||
registration.complete(Ok(ready_info("selected-root", "tools")?))?;
|
||||
let error = readiness.await.unwrap_err();
|
||||
assert!(error.to_string().contains("test Noise provider called"));
|
||||
assert_eq!(deferred_provider.calls(), 1);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn ordinary_noise_environment_replaces_a_deferred_registration() -> anyhow::Result<()> {
|
||||
let manager = environment_manager_without_environments();
|
||||
let deferred_provider = Arc::new(FailingNoiseConnectProvider::default());
|
||||
let registration = manager
|
||||
.register_deferred_noise_environment("tools".to_string(), deferred_provider.clone())?;
|
||||
let deferred = manager
|
||||
.get_environment("tools")
|
||||
.expect("deferred environment");
|
||||
let ordinary_provider = Arc::new(FailingNoiseConnectProvider::default());
|
||||
|
||||
manager.upsert_noise_environment("tools".to_string(), ordinary_provider.clone())?;
|
||||
let ordinary = manager
|
||||
.get_environment("tools")
|
||||
.expect("ordinary environment");
|
||||
assert!(!Arc::ptr_eq(&deferred, &ordinary));
|
||||
|
||||
drop(registration);
|
||||
let error = deferred.wait_until_ready().await.unwrap_err();
|
||||
assert!(
|
||||
error
|
||||
.to_string()
|
||||
.contains("registration ended before completion")
|
||||
);
|
||||
assert_eq!(deferred_provider.calls(), 0);
|
||||
let error = ordinary.wait_until_ready().await.unwrap_err();
|
||||
assert!(error.to_string().contains("test Noise provider called"));
|
||||
assert_eq!(ordinary_provider.calls(), 1);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn existing_environment_publishes_readiness_without_replacement() -> anyhow::Result<()> {
|
||||
let manager = environment_manager_without_environments();
|
||||
let existing_provider = Arc::new(FailingNoiseConnectProvider::default());
|
||||
manager.upsert_noise_environment("tools".to_string(), existing_provider)?;
|
||||
let existing_environment = manager
|
||||
.get_environment("tools")
|
||||
.expect("existing environment");
|
||||
|
||||
let ready_info = ready_info("selected-root", "tools")?;
|
||||
manager.publish_ready_info("tools", ready_info.clone())?;
|
||||
|
||||
let current_environment = manager
|
||||
.get_environment("tools")
|
||||
.expect("current environment");
|
||||
assert!(Arc::ptr_eq(&existing_environment, ¤t_environment));
|
||||
assert_eq!(
|
||||
existing_environment.selected_capability_roots(),
|
||||
ready_info.selected_capability_roots
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn readiness_updates_the_current_environment_after_replacement() -> anyhow::Result<()> {
|
||||
let manager = environment_manager_without_environments();
|
||||
manager.upsert_noise_environment(
|
||||
"tools".to_string(),
|
||||
Arc::new(FailingNoiseConnectProvider::default()),
|
||||
)?;
|
||||
let captured_environment = manager
|
||||
.get_environment("tools")
|
||||
.expect("captured environment");
|
||||
manager.upsert_noise_environment(
|
||||
"tools".to_string(),
|
||||
Arc::new(FailingNoiseConnectProvider::default()),
|
||||
)?;
|
||||
let current_environment = manager
|
||||
.get_environment("tools")
|
||||
.expect("replacement environment");
|
||||
let ready_info = ready_info("selected-root", "tools")?;
|
||||
|
||||
manager.publish_ready_info("tools", ready_info.clone())?;
|
||||
|
||||
assert!(!Arc::ptr_eq(&captured_environment, ¤t_environment));
|
||||
assert!(captured_environment.selected_capability_roots().is_empty());
|
||||
assert_eq!(
|
||||
current_environment.selected_capability_roots(),
|
||||
ready_info.selected_capability_roots
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn publishing_readiness_requires_existing_environment() -> anyhow::Result<()> {
|
||||
let manager = environment_manager_without_environments();
|
||||
|
||||
let error = manager
|
||||
.publish_ready_info("tools", ready_info("selected-root", "tools")?)
|
||||
.unwrap_err();
|
||||
|
||||
assert!(matches!(error, ExecServerError::Protocol(_)));
|
||||
assert!(manager.get_environment("tools").is_none());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn existing_environment_accepts_matching_readiness() -> anyhow::Result<()> {
|
||||
let manager = environment_manager_without_environments();
|
||||
manager.upsert_noise_environment(
|
||||
"tools".to_string(),
|
||||
Arc::new(FailingNoiseConnectProvider::default()),
|
||||
)?;
|
||||
let environment = manager.get_environment("tools").expect("environment");
|
||||
let ready_info = ready_info("selected-root", "tools")?;
|
||||
|
||||
for _ in 0..2 {
|
||||
manager.publish_ready_info("tools", ready_info.clone())?;
|
||||
}
|
||||
assert_eq!(
|
||||
environment.selected_capability_roots(),
|
||||
ready_info.selected_capability_roots
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn existing_environment_overwrites_published_readiness() -> anyhow::Result<()> {
|
||||
let manager = environment_manager_without_environments();
|
||||
manager.upsert_noise_environment(
|
||||
"tools".to_string(),
|
||||
Arc::new(FailingNoiseConnectProvider::default()),
|
||||
)?;
|
||||
let environment = manager.get_environment("tools").expect("environment");
|
||||
let selected_ready_info = ready_info("selected-root", "tools")?;
|
||||
manager.publish_ready_info("tools", selected_ready_info)?;
|
||||
|
||||
let updated_ready_info = ready_info("different-root", "tools")?;
|
||||
manager.publish_ready_info("tools", updated_ready_info.clone())?;
|
||||
assert_eq!(
|
||||
environment.selected_capability_roots(),
|
||||
updated_ready_info.selected_capability_roots
|
||||
);
|
||||
assert!(Arc::ptr_eq(
|
||||
&environment,
|
||||
&manager.get_environment("tools").expect("environment")
|
||||
));
|
||||
|
||||
manager.publish_ready_info("tools", EnvironmentReadyInfo::default())?;
|
||||
assert!(environment.selected_capability_roots().is_empty());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn publishing_readiness_before_deferred_completion_preserves_the_gate() -> anyhow::Result<()>
|
||||
{
|
||||
let manager = environment_manager_without_environments();
|
||||
let provider = Arc::new(FailingNoiseConnectProvider::default());
|
||||
let registration =
|
||||
manager.register_deferred_noise_environment("tools".to_string(), provider.clone())?;
|
||||
let environment = manager.get_environment("tools").expect("environment");
|
||||
let mut readiness = Box::pin(environment.wait_until_ready());
|
||||
|
||||
manager.publish_ready_info("tools", ready_info("published-root", "tools")?)?;
|
||||
assert!(poll!(&mut readiness).is_pending());
|
||||
assert_eq!(provider.calls(), 0);
|
||||
|
||||
let completed_ready_info = ready_info("completed-root", "tools")?;
|
||||
registration.complete(Ok(completed_ready_info.clone()))?;
|
||||
assert_eq!(
|
||||
environment.selected_capability_roots(),
|
||||
completed_ready_info.selected_capability_roots
|
||||
);
|
||||
let error = readiness.await.unwrap_err();
|
||||
assert!(error.to_string().contains("test Noise provider called"));
|
||||
assert_eq!(provider.calls(), 1);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn existing_environment_rejects_invalid_readiness() -> anyhow::Result<()> {
|
||||
let manager = environment_manager_without_environments();
|
||||
manager.upsert_noise_environment(
|
||||
"tools".to_string(),
|
||||
Arc::new(FailingNoiseConnectProvider::default()),
|
||||
)?;
|
||||
let existing_environment = manager
|
||||
.get_environment("tools")
|
||||
.expect("existing environment");
|
||||
let error = manager
|
||||
.publish_ready_info("tools", ready_info("selected-root", "other")?)
|
||||
.unwrap_err();
|
||||
|
||||
assert!(matches!(error, ExecServerError::Protocol(_)));
|
||||
assert!(existing_environment.selected_capability_roots().is_empty());
|
||||
let current_environment = manager
|
||||
.get_environment("tools")
|
||||
.expect("current environment");
|
||||
assert!(Arc::ptr_eq(&existing_environment, ¤t_environment));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn failure_and_dropped_registration_are_terminal() -> anyhow::Result<()> {
|
||||
let manager = environment_manager_without_environments();
|
||||
let failed_provider = Arc::new(FailingNoiseConnectProvider::default());
|
||||
let failed = manager
|
||||
.register_deferred_noise_environment("failed".to_string(), failed_provider.clone())?;
|
||||
let failed_environment = manager.get_environment("failed").expect("environment");
|
||||
failed.complete(Err("provisioning failed".to_string()))?;
|
||||
let error = failed_environment.wait_until_ready().await.unwrap_err();
|
||||
assert!(
|
||||
error
|
||||
.to_string()
|
||||
.ends_with("environment unavailable: provisioning failed")
|
||||
);
|
||||
assert_eq!(failed_provider.calls(), 0);
|
||||
|
||||
let dropped_provider = Arc::new(FailingNoiseConnectProvider::default());
|
||||
let dropped = manager
|
||||
.register_deferred_noise_environment("dropped".to_string(), dropped_provider.clone())?;
|
||||
let dropped_environment = manager.get_environment("dropped").expect("environment");
|
||||
drop(dropped);
|
||||
let error = dropped_environment.wait_until_ready().await.unwrap_err();
|
||||
assert!(
|
||||
error
|
||||
.to_string()
|
||||
.contains("registration ended before completion")
|
||||
);
|
||||
assert_eq!(dropped_provider.calls(), 0);
|
||||
assert!(manager.get_environment("failed").is_some());
|
||||
assert!(manager.get_environment("dropped").is_some());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn invalid_ready_info_is_terminal() -> anyhow::Result<()> {
|
||||
let manager = environment_manager_without_environments();
|
||||
let provider = Arc::new(FailingNoiseConnectProvider::default());
|
||||
let registration =
|
||||
manager.register_deferred_noise_environment("tools".to_string(), provider.clone())?;
|
||||
let environment = manager.get_environment("tools").expect("environment");
|
||||
|
||||
let error = registration
|
||||
.complete(Ok(ready_info("selected-root", "other")?))
|
||||
.unwrap_err();
|
||||
assert!(matches!(error, ExecServerError::Protocol(_)));
|
||||
let readiness_error = environment.wait_until_ready().await.unwrap_err();
|
||||
assert!(
|
||||
readiness_error
|
||||
.to_string()
|
||||
.contains("belong to environment")
|
||||
);
|
||||
assert!(environment.selected_capability_roots().is_empty());
|
||||
assert_eq!(provider.calls(), 0);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn late_completion_is_isolated_from_replacement() -> anyhow::Result<()> {
|
||||
let manager = environment_manager_without_environments();
|
||||
let old_provider = Arc::new(FailingNoiseConnectProvider::default());
|
||||
let old_registration =
|
||||
manager.register_deferred_noise_environment("tools".to_string(), old_provider.clone())?;
|
||||
let old_environment = manager.get_environment("tools").expect("old environment");
|
||||
let current_provider = Arc::new(FailingNoiseConnectProvider::default());
|
||||
let current_registration = manager
|
||||
.register_deferred_noise_environment("tools".to_string(), current_provider.clone())?;
|
||||
let current = manager.get_environment("tools").expect("current");
|
||||
|
||||
let old_ready_info = ready_info("old-root", "tools")?;
|
||||
old_registration.complete(Ok(old_ready_info.clone()))?;
|
||||
assert_eq!(
|
||||
old_environment.selected_capability_roots(),
|
||||
old_ready_info.selected_capability_roots
|
||||
);
|
||||
assert!(current.selected_capability_roots().is_empty());
|
||||
let old_error = old_environment.wait_until_ready().await.unwrap_err();
|
||||
assert!(old_error.to_string().contains("test Noise provider called"));
|
||||
assert_eq!(old_provider.calls(), 1);
|
||||
let mut current_readiness = Box::pin(current.wait_until_ready());
|
||||
assert!(poll!(&mut current_readiness).is_pending());
|
||||
assert_eq!(current_provider.calls(), 0);
|
||||
|
||||
let current_ready_info = ready_info("current-root", "tools")?;
|
||||
current_registration.complete(Ok(current_ready_info.clone()))?;
|
||||
assert_eq!(
|
||||
current.selected_capability_roots(),
|
||||
current_ready_info.selected_capability_roots
|
||||
);
|
||||
let current_error = current_readiness.await.unwrap_err();
|
||||
assert!(
|
||||
current_error
|
||||
.to_string()
|
||||
.contains("test Noise provider called")
|
||||
);
|
||||
assert_eq!(current_provider.calls(), 1);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn eager_noise_environment_connects_without_registration() -> anyhow::Result<()> {
|
||||
let manager = environment_manager_without_environments();
|
||||
let provider = Arc::new(FailingNoiseConnectProvider::default());
|
||||
manager.upsert_noise_environment("tools".to_string(), provider.clone())?;
|
||||
let environment = manager.get_environment("tools").expect("environment");
|
||||
|
||||
let error = environment.wait_until_ready().await.unwrap_err();
|
||||
assert!(error.to_string().contains("test Noise provider called"));
|
||||
assert_eq!(provider.calls(), 1);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn readiness_before_materialization_creates_the_stable_environment() -> anyhow::Result<()> {
|
||||
let manager = environment_manager_without_environments();
|
||||
@@ -469,6 +108,33 @@ async fn materialize_then_report_ready_reuses_the_pending_environment() -> anyho
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn ordinary_environment_ignores_provisioning_reports() -> anyhow::Result<()> {
|
||||
let manager = environment_manager_without_environments();
|
||||
manager.upsert_environment(
|
||||
"tools".to_string(),
|
||||
"ws://127.0.0.1:1".to_string(),
|
||||
Some(std::time::Duration::from_millis(1)),
|
||||
)?;
|
||||
let existing_environment = manager
|
||||
.get_environment("tools")
|
||||
.expect("existing environment");
|
||||
|
||||
let reported = manager.report_environment_provisioning_status(
|
||||
"tools".to_string(),
|
||||
Ok(ready_info("selected-root", "tools")?),
|
||||
Arc::new(FailingNoiseConnectProvider::default()),
|
||||
)?;
|
||||
|
||||
let current_environment = manager
|
||||
.get_environment("tools")
|
||||
.expect("current environment");
|
||||
assert!(Arc::ptr_eq(&existing_environment, ¤t_environment));
|
||||
assert!(reported.is_none());
|
||||
assert!(existing_environment.selected_capability_roots().is_empty());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn failure_before_materialization_is_terminal_without_connecting() -> anyhow::Result<()> {
|
||||
let manager = environment_manager_without_environments();
|
||||
@@ -537,15 +203,6 @@ async fn repeated_failure_preserves_the_first_error_and_rejects_ready() -> anyho
|
||||
.expect("repeated failure should be idempotent");
|
||||
assert!(Arc::ptr_eq(&failed, &repeated));
|
||||
|
||||
let error = manager
|
||||
.report_environment_provisioning_status(
|
||||
"tools".to_string(),
|
||||
Ok(ready_info("selected-root", "other")?),
|
||||
provider.clone(),
|
||||
)
|
||||
.unwrap_err();
|
||||
assert!(error.to_string().contains("first failure"));
|
||||
|
||||
let error = manager
|
||||
.report_environment_provisioning_status(
|
||||
"tools".to_string(),
|
||||
@@ -585,3 +242,141 @@ async fn ready_environment_rejects_a_later_failure() -> anyhow::Result<()> {
|
||||
assert_eq!(ready.selected_capability_roots().len(), 1);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn existing_environment_accepts_matching_readiness() -> anyhow::Result<()> {
|
||||
let manager = environment_manager_without_environments();
|
||||
let provider = Arc::new(FailingNoiseConnectProvider::default());
|
||||
let ready_info = ready_info("selected-root", "tools")?;
|
||||
|
||||
let environment = manager
|
||||
.report_environment_provisioning_status(
|
||||
"tools".to_string(),
|
||||
Ok(ready_info.clone()),
|
||||
provider.clone(),
|
||||
)?
|
||||
.expect("readiness report should create the environment");
|
||||
manager.report_environment_provisioning_status(
|
||||
"tools".to_string(),
|
||||
Ok(ready_info.clone()),
|
||||
provider,
|
||||
)?;
|
||||
assert_eq!(
|
||||
environment.selected_capability_roots(),
|
||||
ready_info.selected_capability_roots
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn existing_environment_overwrites_reported_readiness() -> anyhow::Result<()> {
|
||||
let manager = environment_manager_without_environments();
|
||||
let provider = Arc::new(FailingNoiseConnectProvider::default());
|
||||
let environment = manager
|
||||
.report_environment_provisioning_status(
|
||||
"tools".to_string(),
|
||||
Ok(ready_info("selected-root", "tools")?),
|
||||
provider.clone(),
|
||||
)?
|
||||
.expect("readiness report should create the environment");
|
||||
|
||||
let updated_ready_info = ready_info("different-root", "tools")?;
|
||||
manager.report_environment_provisioning_status(
|
||||
"tools".to_string(),
|
||||
Ok(updated_ready_info.clone()),
|
||||
provider,
|
||||
)?;
|
||||
assert_eq!(
|
||||
environment.selected_capability_roots(),
|
||||
updated_ready_info.selected_capability_roots
|
||||
);
|
||||
assert!(Arc::ptr_eq(
|
||||
&environment,
|
||||
&manager.get_environment("tools").expect("environment")
|
||||
));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn invalid_ready_report_fails_the_provisioning_gate() -> anyhow::Result<()> {
|
||||
let manager = environment_manager_without_environments();
|
||||
let provider = Arc::new(FailingNoiseConnectProvider::default());
|
||||
let environment =
|
||||
manager.materialize_pending_noise_environment("tools".to_string(), provider.clone())?;
|
||||
let readiness = Box::pin(environment.wait_until_ready());
|
||||
|
||||
let error = manager
|
||||
.report_environment_provisioning_status(
|
||||
"tools".to_string(),
|
||||
Ok(ready_info("selected-root", "other")?),
|
||||
provider.clone(),
|
||||
)
|
||||
.unwrap_err();
|
||||
|
||||
assert!(matches!(error, ExecServerError::Protocol(_)));
|
||||
let readiness_error = readiness.await.unwrap_err();
|
||||
assert!(readiness_error.to_string().contains(&error.to_string()));
|
||||
assert!(environment.selected_capability_roots().is_empty());
|
||||
assert_eq!(provider.calls(), 0);
|
||||
|
||||
let later_ready_error = manager
|
||||
.report_environment_provisioning_status(
|
||||
"tools".to_string(),
|
||||
Ok(ready_info("selected-root", "tools")?),
|
||||
provider.clone(),
|
||||
)
|
||||
.unwrap_err();
|
||||
assert!(
|
||||
later_ready_error
|
||||
.to_string()
|
||||
.contains("provisioning already failed")
|
||||
);
|
||||
assert!(environment.selected_capability_roots().is_empty());
|
||||
assert_eq!(provider.calls(), 0);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn duplicate_materialization_reuses_the_pending_environment() -> anyhow::Result<()> {
|
||||
let manager = environment_manager_without_environments();
|
||||
let provider = Arc::new(FailingNoiseConnectProvider::default());
|
||||
let environment =
|
||||
manager.materialize_pending_noise_environment("tools".to_string(), provider.clone())?;
|
||||
let replacement_provider = Arc::new(FailingNoiseConnectProvider::default());
|
||||
|
||||
let current = manager
|
||||
.materialize_pending_noise_environment("tools".to_string(), replacement_provider.clone())?;
|
||||
assert!(Arc::ptr_eq(&environment, ¤t));
|
||||
assert_eq!(provider.calls(), 0);
|
||||
assert_eq!(replacement_provider.calls(), 0);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn deferred_materialization_conflicts_with_an_existing_ordinary_environment()
|
||||
-> anyhow::Result<()> {
|
||||
let manager = environment_manager_without_environments();
|
||||
manager.upsert_environment(
|
||||
"tools".to_string(),
|
||||
"ws://127.0.0.1:1".to_string(),
|
||||
Some(std::time::Duration::from_millis(1)),
|
||||
)?;
|
||||
let existing_environment = manager.get_environment("tools").expect("environment");
|
||||
let deferred_provider = Arc::new(FailingNoiseConnectProvider::default());
|
||||
|
||||
let error = manager
|
||||
.materialize_pending_noise_environment("tools".to_string(), deferred_provider.clone())
|
||||
.unwrap_err();
|
||||
|
||||
assert!(matches!(
|
||||
error,
|
||||
ExecServerError::ProvisioningModeConflict { environment_id }
|
||||
if environment_id == "tools"
|
||||
));
|
||||
let current_environment = manager
|
||||
.get_environment("tools")
|
||||
.expect("ordinary environment should remain registered");
|
||||
assert!(Arc::ptr_eq(&existing_environment, ¤t_environment));
|
||||
assert_eq!(deferred_provider.calls(), 0);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user