mirror of
https://github.com/pchuan98/codex.git
synced 2026-07-01 00:31:56 +08:00
d45cd26248
## Summary - Adapts the moved `codex-cloud-config` crate from the legacy cloud requirements endpoint to the new config bundle endpoint. - Switches runtime consumers from `CloudRequirementsLoader` to `CloudConfigBundleLoader` so one shared bundle supplies cloud-delivered config and requirements. - Removes the legacy cloud requirements domain loader path. ## Details This intentionally keeps `codex-cloud-config` monolithic for review lineage: the previous PR establishes the crate move, and this PR shows the behavior change against that moved implementation. A follow-up PR splits the module back into focused files. The new bundle path preserves the important cloud requirements loader semantics where intended: account-scoped signed cache, 30 minute TTL, 5 minute refresh cadence, retry/backoff, auth recovery, and fail-closed startup loading. The cached payload changes from a single requirements TOML string to the backend-delivered bundle, and validation rejects malformed config or requirements fragments before cache write/use.
126 lines
4.4 KiB
Rust
126 lines
4.4 KiB
Rust
use super::*;
|
|
use crate::ConfigLayerSource;
|
|
use crate::ConfigRequirementsToml;
|
|
use crate::compose_requirements;
|
|
use codex_protocol::protocol::AskForApproval;
|
|
use pretty_assertions::assert_eq;
|
|
use std::sync::Arc;
|
|
use std::sync::atomic::AtomicUsize;
|
|
use std::sync::atomic::Ordering;
|
|
use tempfile::tempdir;
|
|
|
|
#[tokio::test]
|
|
async fn shared_future_runs_once() {
|
|
let counter = Arc::new(AtomicUsize::new(0));
|
|
let counter_clone = Arc::clone(&counter);
|
|
let loader = CloudConfigBundleLoader::new(async move {
|
|
counter_clone.fetch_add(1, Ordering::SeqCst);
|
|
Ok(Some(CloudConfigBundle::default()))
|
|
});
|
|
|
|
let (first, second) = tokio::join!(loader.get(), loader.get());
|
|
assert_eq!(first, second);
|
|
assert_eq!(counter.load(Ordering::SeqCst), 1);
|
|
}
|
|
|
|
#[test]
|
|
fn bundle_layers_preserve_enterprise_managed_bucket_order() {
|
|
let tempdir = tempdir().expect("tempdir");
|
|
let base_dir = AbsolutePathBuf::from_absolute_path(tempdir.path()).expect("absolute path");
|
|
let layers = CloudConfigBundleLayers::from_bundle(
|
|
CloudConfigBundle {
|
|
config_toml: CloudConfigTomlBundle {
|
|
enterprise_managed: vec![
|
|
CloudConfigFragment {
|
|
id: "cfg_high".to_string(),
|
|
name: "High config".to_string(),
|
|
contents: "model = \"high\"".to_string(),
|
|
},
|
|
CloudConfigFragment {
|
|
id: "cfg_low".to_string(),
|
|
name: "Low config".to_string(),
|
|
contents: "model = \"low\"".to_string(),
|
|
},
|
|
],
|
|
},
|
|
requirements_toml: CloudRequirementsTomlBundle {
|
|
enterprise_managed: vec![
|
|
CloudRequirementsFragment {
|
|
id: "req_high".to_string(),
|
|
name: "High requirements".to_string(),
|
|
contents: "allowed_approval_policies = [\"on-request\"]".to_string(),
|
|
},
|
|
CloudRequirementsFragment {
|
|
id: "req_low".to_string(),
|
|
name: "Low requirements".to_string(),
|
|
contents: "allowed_approval_policies = [\"never\"]".to_string(),
|
|
},
|
|
],
|
|
},
|
|
},
|
|
&base_dir,
|
|
)
|
|
.expect("bundle should be converted into layers");
|
|
|
|
assert_eq!(
|
|
layers
|
|
.enterprise_managed_config
|
|
.iter()
|
|
.map(|layer| layer.name.clone())
|
|
.collect::<Vec<_>>(),
|
|
vec![
|
|
ConfigLayerSource::EnterpriseManaged {
|
|
id: "cfg_low".to_string(),
|
|
name: "Low config".to_string(),
|
|
},
|
|
ConfigLayerSource::EnterpriseManaged {
|
|
id: "cfg_high".to_string(),
|
|
name: "High config".to_string(),
|
|
},
|
|
]
|
|
);
|
|
assert_eq!(
|
|
compose_requirements(layers.enterprise_managed_requirements)
|
|
.expect("requirements should compose")
|
|
.expect("requirements should be present")
|
|
.into_toml(),
|
|
ConfigRequirementsToml {
|
|
allowed_approval_policies: Some(vec![AskForApproval::OnRequest]),
|
|
..Default::default()
|
|
}
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn bundle_layers_can_strict_validate_enterprise_managed_config() {
|
|
let tempdir = tempdir().expect("tempdir");
|
|
let base_dir = AbsolutePathBuf::from_absolute_path(tempdir.path()).expect("absolute path");
|
|
let err = CloudConfigBundleLayers::from_bundle_strict_config(
|
|
CloudConfigBundle {
|
|
config_toml: CloudConfigTomlBundle {
|
|
enterprise_managed: vec![CloudConfigFragment {
|
|
id: "cfg".to_string(),
|
|
name: "Cloud config".to_string(),
|
|
contents: "unknown_key = true".to_string(),
|
|
}],
|
|
},
|
|
requirements_toml: CloudRequirementsTomlBundle {
|
|
enterprise_managed: Vec::new(),
|
|
},
|
|
},
|
|
&base_dir,
|
|
)
|
|
.expect_err("strict config should reject unknown fields");
|
|
|
|
assert_eq!(
|
|
err,
|
|
CloudConfigLayerError::Invalid {
|
|
fragment: crate::CloudConfigFragmentSource {
|
|
id: "cfg".to_string(),
|
|
name: "Cloud config".to_string(),
|
|
},
|
|
message: "unknown configuration field `unknown_key`".to_string(),
|
|
}
|
|
);
|
|
}
|