mirror of
https://github.com/openai/codex.git
synced 2026-11-08 20:40:33 +00:00
## What changed Add `RuntimeAuthProvider` to resolve request authentication through the runtime auth manager. Support `dual_header` mode, which sends a bearer credential alongside `x-openai-agent-assertion`, and `assertion_only` mode, which uses `Authorization: AgentAssertion …`. Replace stale authentication headers and sign a fresh assertion for each request attempt. Propagate registration failures without falling back to credential-only authentication, preserving policy errors and classifying transport failures, rate limits, and server errors as transient. Add the disabled-by-default `features.runtime_agent_instances` configuration with independent `enabled` and `mode` settings, schema support, and structured override merging. Selecting a mode alone does not enable the feature. ## Testing Add coverage for both modes with API key and ChatGPT credentials, stale header replacement, fresh assertions, credential refresh without runtime replacement, registration failures, and configuration parsing and merging. GitOrigin-RevId: 9e93d25762600f0ff592c5248fe4c8c2c8182c8d
754 lines
21 KiB
Rust
754 lines
21 KiB
Rust
use super::*;
|
|
use crate::config_toml::AgentsToml;
|
|
use crate::config_toml::ConfigToml;
|
|
use crate::types::MemoriesToml;
|
|
use pretty_assertions::assert_eq;
|
|
|
|
fn parse_toml(value: &str) -> TomlValue {
|
|
toml::from_str(value).expect("TOML should parse")
|
|
}
|
|
|
|
#[test]
|
|
fn merge_toml_values_normalizes_legacy_key_from_base_layer() {
|
|
let mut base = parse_toml(
|
|
r#"
|
|
[memories]
|
|
no_memories_if_mcp_or_web_search = false
|
|
"#,
|
|
);
|
|
let overlay = parse_toml(
|
|
r#"
|
|
[memories]
|
|
disable_on_external_context = true
|
|
"#,
|
|
);
|
|
|
|
merge_toml_values(&mut base, &overlay);
|
|
|
|
let expected = parse_toml(
|
|
r#"
|
|
[memories]
|
|
disable_on_external_context = true
|
|
"#,
|
|
);
|
|
assert_eq!(base, expected);
|
|
|
|
let config: ConfigToml = base.try_into().expect("merged config should deserialize");
|
|
assert_eq!(
|
|
config.memories,
|
|
Some(MemoriesToml {
|
|
disable_on_external_context: Some(true),
|
|
..Default::default()
|
|
})
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn merge_toml_values_normalizes_legacy_key_from_overlay_layer() {
|
|
let mut base = parse_toml(
|
|
r#"
|
|
[memories]
|
|
disable_on_external_context = false
|
|
"#,
|
|
);
|
|
let overlay = parse_toml(
|
|
r#"
|
|
[memories]
|
|
no_memories_if_mcp_or_web_search = true
|
|
"#,
|
|
);
|
|
|
|
merge_toml_values(&mut base, &overlay);
|
|
|
|
let expected = parse_toml(
|
|
r#"
|
|
[memories]
|
|
disable_on_external_context = true
|
|
"#,
|
|
);
|
|
assert_eq!(base, expected);
|
|
|
|
let config: ConfigToml = base.try_into().expect("merged config should deserialize");
|
|
assert_eq!(
|
|
config.memories,
|
|
Some(MemoriesToml {
|
|
disable_on_external_context: Some(true),
|
|
..Default::default()
|
|
})
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn merge_toml_values_prefers_canonical_key_when_one_layer_has_both_names() {
|
|
let mut base = TomlValue::Table(toml::map::Map::new());
|
|
let overlay = parse_toml(
|
|
r#"
|
|
[memories]
|
|
disable_on_external_context = true
|
|
no_memories_if_mcp_or_web_search = false
|
|
"#,
|
|
);
|
|
|
|
merge_toml_values(&mut base, &overlay);
|
|
|
|
let expected = parse_toml(
|
|
r#"
|
|
[memories]
|
|
disable_on_external_context = true
|
|
"#,
|
|
);
|
|
assert_eq!(base, expected);
|
|
}
|
|
|
|
#[test]
|
|
fn merge_toml_values_normalizes_legacy_agents_key_across_layers() {
|
|
let mut base = parse_toml(
|
|
r#"
|
|
[agents]
|
|
max_threads = 4
|
|
"#,
|
|
);
|
|
let overlay = parse_toml(
|
|
r#"
|
|
[agents]
|
|
max_concurrent_threads_per_session = 7
|
|
"#,
|
|
);
|
|
|
|
merge_toml_values(&mut base, &overlay);
|
|
|
|
let expected = parse_toml(
|
|
r#"
|
|
[agents]
|
|
max_concurrent_threads_per_session = 7
|
|
"#,
|
|
);
|
|
assert_eq!(base, expected);
|
|
|
|
let config: ConfigToml = base.try_into().expect("merged config should deserialize");
|
|
assert_eq!(
|
|
config.agents,
|
|
Some(AgentsToml {
|
|
max_concurrent_threads_per_session: Some(7),
|
|
..Default::default()
|
|
})
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn merge_toml_values_normalizes_legacy_agents_key_from_overlay() {
|
|
let mut base = parse_toml(
|
|
r#"
|
|
[agents]
|
|
max_concurrent_threads_per_session = 4
|
|
"#,
|
|
);
|
|
let overlay = parse_toml(
|
|
r#"
|
|
[agents]
|
|
max_threads = 7
|
|
"#,
|
|
);
|
|
|
|
merge_toml_values(&mut base, &overlay);
|
|
|
|
let expected = parse_toml(
|
|
r#"
|
|
[agents]
|
|
max_concurrent_threads_per_session = 7
|
|
"#,
|
|
);
|
|
assert_eq!(base, expected);
|
|
}
|
|
|
|
/// Feature tables added above legacy toggles retain the lower layer's enabled state.
|
|
#[test]
|
|
fn merge_multi_agent_v2_table_preserves_legacy_boolean_toggle() {
|
|
for feature_path in ["features", "profiles.work.features"] {
|
|
let mut base = parse_toml(&format!("[{feature_path}]\nmulti_agent_v2 = true\n"));
|
|
let overlay = parse_toml(&format!(
|
|
"[{feature_path}.multi_agent_v2]\nsubagent_usage_hint_text = \"Delegate carefully.\"\n",
|
|
));
|
|
|
|
merge_toml_values(&mut base, &overlay);
|
|
|
|
assert_eq!(
|
|
base,
|
|
parse_toml(&format!(
|
|
"[{feature_path}.multi_agent_v2]\nenabled = true\nsubagent_usage_hint_text = \"Delegate carefully.\"\n",
|
|
))
|
|
);
|
|
}
|
|
}
|
|
|
|
/// Legacy feature toggles update enabled state without discarding nested configuration.
|
|
#[test]
|
|
fn merge_multi_agent_v2_boolean_preserves_existing_feature_table() {
|
|
for feature_path in ["features", "profiles.work.features"] {
|
|
let mut base = parse_toml(&format!(
|
|
"[{feature_path}.multi_agent_v2]\nenabled = true\nsubagent_usage_hint_text = \"Delegate carefully.\"\n",
|
|
));
|
|
let overlay = parse_toml(&format!("[{feature_path}]\nmulti_agent_v2 = false\n"));
|
|
|
|
merge_toml_values(&mut base, &overlay);
|
|
|
|
assert_eq!(
|
|
base,
|
|
parse_toml(&format!(
|
|
"[{feature_path}.multi_agent_v2]\nenabled = false\nsubagent_usage_hint_text = \"Delegate carefully.\"\n",
|
|
))
|
|
);
|
|
}
|
|
}
|
|
|
|
/// Opaque desktop settings retain ordinary scalar/table replacement semantics.
|
|
#[test]
|
|
fn merge_multi_agent_v2_compatibility_excludes_opaque_desktop_paths() {
|
|
let cases = [
|
|
(
|
|
"[desktop.features.multi_agent_v2]\nenabled = true\n",
|
|
"[desktop.features]\nmulti_agent_v2 = false\n",
|
|
"[desktop.features]\nmulti_agent_v2 = false\n",
|
|
),
|
|
(
|
|
"[desktop.features]\nmulti_agent_v2 = true\n",
|
|
"[desktop.features.multi_agent_v2]\ncustom = true\n",
|
|
"[desktop.features.multi_agent_v2]\ncustom = true\n",
|
|
),
|
|
];
|
|
|
|
for (base, overlay, expected) in cases {
|
|
let mut base = parse_toml(base);
|
|
merge_toml_values(&mut base, &parse_toml(overlay));
|
|
assert_eq!(base, parse_toml(expected));
|
|
}
|
|
}
|
|
|
|
/// CLI overrides preserve the multi-agent toggle and nested options in either ordering.
|
|
#[test]
|
|
fn multi_agent_v2_cli_overrides_preserve_boolean_and_nested_configuration() {
|
|
for feature_path in ["features", "profiles.work.features"] {
|
|
let instructions = (
|
|
format!("{feature_path}.multi_agent_v2.subagent_usage_hint_text"),
|
|
TomlValue::String("Delegate carefully.".to_string()),
|
|
);
|
|
let enabled = (
|
|
format!("{feature_path}.multi_agent_v2"),
|
|
TomlValue::Boolean(true),
|
|
);
|
|
let feature_table = (
|
|
format!("{feature_path}.multi_agent_v2"),
|
|
parse_toml("subagent_usage_hint_text = \"Delegate carefully.\"\n"),
|
|
);
|
|
let expected = parse_toml(&format!(
|
|
"[{feature_path}.multi_agent_v2]\nenabled = true\nsubagent_usage_hint_text = \"Delegate carefully.\"\n",
|
|
));
|
|
|
|
for overrides in [
|
|
vec![enabled.clone(), instructions.clone()],
|
|
vec![instructions, enabled.clone()],
|
|
vec![enabled.clone(), feature_table.clone()],
|
|
vec![feature_table, enabled],
|
|
] {
|
|
assert_eq!(crate::build_cli_overrides_layer(&overrides), expected);
|
|
}
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn code_mode_overrides_preserve_enabled_state_and_cell_overhead() {
|
|
for feature_path in ["features", "profiles.work.features"] {
|
|
for enabled in [true, false] {
|
|
let toggle = (
|
|
format!("{feature_path}.code_mode"),
|
|
TomlValue::Boolean(enabled),
|
|
);
|
|
let overhead = (
|
|
format!("{feature_path}.code_mode.experimental_show_cell_overhead"),
|
|
TomlValue::Boolean(true),
|
|
);
|
|
let expected = parse_toml(&format!(
|
|
"[{feature_path}.code_mode]\nenabled = {enabled}\nexperimental_show_cell_overhead = true\n",
|
|
));
|
|
|
|
// Adding a display option must preserve the toggle in either override order.
|
|
for overrides in [
|
|
vec![toggle.clone(), overhead.clone()],
|
|
vec![overhead, toggle],
|
|
] {
|
|
assert_eq!(crate::build_cli_overrides_layer(&overrides), expected);
|
|
}
|
|
|
|
let boolean_layer = parse_toml(&format!("[{feature_path}]\ncode_mode = {enabled}\n"));
|
|
let table_layer = parse_toml(&format!(
|
|
"[{feature_path}.code_mode]\nexperimental_show_cell_overhead = true\n",
|
|
));
|
|
for (mut base, overlay) in [
|
|
(boolean_layer.clone(), table_layer.clone()),
|
|
(table_layer, boolean_layer),
|
|
] {
|
|
merge_toml_values(&mut base, &overlay);
|
|
assert_eq!(base, expected);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn structured_mode_overrides_preserve_disabled_state_and_mode() {
|
|
for (feature, selected_mode) in [
|
|
("sleep_tool", "always_on"),
|
|
("runtime_agent_instances", "assertion_only"),
|
|
] {
|
|
for feature_path in ["features", "profiles.work.features"] {
|
|
let disabled = (
|
|
format!("{feature_path}.{feature}"),
|
|
TomlValue::Boolean(false),
|
|
);
|
|
let mode = (
|
|
format!("{feature_path}.{feature}.mode"),
|
|
TomlValue::String(selected_mode.to_string()),
|
|
);
|
|
let expected = parse_toml(&format!(
|
|
"[{feature_path}.{feature}]\nenabled = false\nmode = \"{selected_mode}\"\n",
|
|
));
|
|
for overrides in [vec![disabled.clone(), mode.clone()], vec![mode, disabled]] {
|
|
assert_eq!(crate::build_cli_overrides_layer(&overrides), expected);
|
|
}
|
|
|
|
let boolean_layer = parse_toml(&format!("[{feature_path}]\n{feature} = false\n"));
|
|
let table_layer = parse_toml(&format!(
|
|
"[{feature_path}.{feature}]\nmode = \"{selected_mode}\"\n",
|
|
));
|
|
for (mut base, overlay) in [
|
|
(boolean_layer.clone(), table_layer.clone()),
|
|
(table_layer, boolean_layer),
|
|
] {
|
|
merge_toml_values(&mut base, &overlay);
|
|
assert_eq!(base, expected);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn network_proxy_feature_overrides_preserve_credential_broker_configuration() {
|
|
let enabled = (
|
|
"features.network_proxy".to_string(),
|
|
TomlValue::Boolean(true),
|
|
);
|
|
let broker = (
|
|
"features.network_proxy.credential_broker".to_string(),
|
|
TomlValue::Boolean(true),
|
|
);
|
|
let expected =
|
|
parse_toml("[features.network_proxy]\nenabled = true\ncredential_broker = true\n");
|
|
|
|
for overrides in [vec![enabled.clone(), broker.clone()], vec![broker, enabled]] {
|
|
assert_eq!(crate::build_cli_overrides_layer(&overrides), expected);
|
|
}
|
|
|
|
let mut base = parse_toml("[features]\nnetwork_proxy = true\n");
|
|
merge_toml_values(
|
|
&mut base,
|
|
&parse_toml("[features.network_proxy]\ncredential_broker = true\n"),
|
|
);
|
|
assert_eq!(base, expected);
|
|
}
|
|
|
|
#[test]
|
|
fn higher_priority_credential_provider_replaces_overlapping_sources() {
|
|
for (feature_path, base_source, overlay_source, keep_default) in [
|
|
("features", "VENDOR_TOKEN", "VENDOR_TOKEN", false),
|
|
(
|
|
"profiles.work.features",
|
|
"VENDOR_TOKEN",
|
|
"VENDOR_TOKEN",
|
|
false,
|
|
),
|
|
("features", "vendor_token", "VENDOR_TOKEN", !cfg!(windows)),
|
|
] {
|
|
let default_provider = format!(
|
|
"[{feature_path}.network_proxy.credentials.default]\n\
|
|
env = ['{base_source}']\n\
|
|
url_prefixes = ['https://default.example']\n"
|
|
);
|
|
let shared_provider = format!(
|
|
"[{feature_path}.network_proxy.credentials.shared]\n\
|
|
env = ['SHARED_TOKEN']\n\
|
|
patterns = ['shared_[a-z]+']\n"
|
|
);
|
|
let mut base = parse_toml(&format!("{default_provider}\n{shared_provider}"));
|
|
let overlay = parse_toml(&format!(
|
|
"[{feature_path}.network_proxy.credentials.override]\n\
|
|
env = ['{overlay_source}']\n\
|
|
url_prefixes = ['https://override.example']\n\
|
|
[{feature_path}.network_proxy.credentials.shared]\n\
|
|
url_prefixes = ['https://shared.example']\n"
|
|
));
|
|
|
|
merge_toml_values(&mut base, &overlay);
|
|
|
|
let retained_default = if keep_default { &default_provider } else { "" };
|
|
let expected = parse_toml(&format!(
|
|
"{retained_default}\n\
|
|
[{feature_path}.network_proxy.credentials.override]\n\
|
|
env = ['{overlay_source}']\n\
|
|
url_prefixes = ['https://override.example']\n\
|
|
[{feature_path}.network_proxy.credentials.shared]\n\
|
|
env = ['SHARED_TOKEN']\n\
|
|
patterns = ['shared_[a-z]+']\n\
|
|
url_prefixes = ['https://shared.example']\n"
|
|
));
|
|
assert_eq!(base, expected, "feature path: {feature_path}");
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn credential_provider_remapping_preserves_inherited_settings() {
|
|
let base = parse_toml(
|
|
r#"
|
|
[features.network_proxy.credentials.a]
|
|
env = ["OLD_AUTH"]
|
|
patterns = ["acme_[a-z]{24}"]
|
|
url_prefixes = ["https://a.example"]
|
|
auth = ["token"]
|
|
[features.network_proxy.credentials.b]
|
|
env = ["OTHER_AUTH"]
|
|
patterns = ["other_[a-z]{24}"]
|
|
url_prefixes = ["https://b.example"]
|
|
auth = ["bearer"]
|
|
"#,
|
|
);
|
|
|
|
for new_a in ["NEW_AUTH", "OTHER_AUTH"] {
|
|
let mut merged = base.clone();
|
|
let mut expected = base.clone();
|
|
let providers = &mut expected["features"]["network_proxy"]["credentials"];
|
|
providers["a"]["env"] = TomlValue::Array(vec![new_a.into()]);
|
|
providers["b"]["env"] = TomlValue::Array(vec!["OLD_AUTH".into()]);
|
|
let overlay = parse_toml(&format!(
|
|
"[features.network_proxy.credentials.a]\n\
|
|
env = ['{new_a}']\n\
|
|
[features.network_proxy.credentials.b]\n\
|
|
env = ['OLD_AUTH']\n"
|
|
));
|
|
|
|
merge_toml_values(&mut merged, &overlay);
|
|
|
|
assert_eq!(merged, expected);
|
|
}
|
|
}
|
|
|
|
/// Repeated opaque desktop overrides continue to replace their previous value.
|
|
#[test]
|
|
fn multi_agent_v2_cli_compatibility_excludes_opaque_desktop_paths() {
|
|
let path = "desktop.features.multi_agent_v2".to_string();
|
|
let enabled = (path.clone(), TomlValue::Boolean(true));
|
|
let feature_table = (path, parse_toml("custom = true\n"));
|
|
|
|
assert_eq!(
|
|
crate::build_cli_overrides_layer(&[enabled.clone(), feature_table.clone()]),
|
|
parse_toml("[desktop.features.multi_agent_v2]\ncustom = true\n")
|
|
);
|
|
assert_eq!(
|
|
crate::build_cli_overrides_layer(&[feature_table, enabled]),
|
|
parse_toml("[desktop.features]\nmulti_agent_v2 = true\n")
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn merge_toml_values_normalizes_permission_network_domains_before_overlaying() {
|
|
let mut base = parse_toml(
|
|
r#"
|
|
[permissions.dev.network.domains]
|
|
"example.com" = "deny"
|
|
"#,
|
|
);
|
|
let overlay = parse_toml(
|
|
r#"
|
|
[permissions.dev.network.domains]
|
|
"EXAMPLE.COM" = "allow"
|
|
"#,
|
|
);
|
|
|
|
merge_toml_values(&mut base, &overlay);
|
|
|
|
let expected = parse_toml(
|
|
r#"
|
|
[permissions.dev.network.domains]
|
|
"example.com" = "allow"
|
|
"#,
|
|
);
|
|
assert_eq!(base, expected);
|
|
}
|
|
|
|
#[test]
|
|
fn shell_environment_policy_legacy_array_overlay_replaces_legacy_array() {
|
|
let mut base = parse_toml(
|
|
r#"
|
|
[shell_environment_policy]
|
|
exclude = ["LOW_*", "SHARED_*"]
|
|
"#,
|
|
);
|
|
let overlay = parse_toml(
|
|
r#"
|
|
[shell_environment_policy]
|
|
exclude = ["HIGH_*"]
|
|
"#,
|
|
);
|
|
|
|
merge_toml_values(&mut base, &overlay);
|
|
|
|
assert_eq!(base, overlay);
|
|
}
|
|
|
|
#[test]
|
|
fn shell_environment_policy_set_overlay_preserves_case_distinct_keys() {
|
|
let mut base = parse_toml(
|
|
r#"
|
|
[features.network_proxy.credentials.stripe]
|
|
env = ["STRIPE_API_KEY"]
|
|
patterns = ["stripe_[a-z]+"]
|
|
url_prefix_from_env = "STRIPE_HOST"
|
|
|
|
[shell_environment_policy.set]
|
|
stripe_api_key = "stale"
|
|
stripe_host = "stale.example"
|
|
foo = "lowercase"
|
|
"#,
|
|
);
|
|
let overlay = parse_toml(
|
|
r#"
|
|
[shell_environment_policy.set]
|
|
STRIPE_API_KEY = "trusted"
|
|
STRIPE_HOST = "trusted.example"
|
|
FOO = "uppercase"
|
|
"#,
|
|
);
|
|
|
|
merge_toml_values(&mut base, &overlay);
|
|
|
|
let values = base["shell_environment_policy"]["set"]
|
|
.as_table()
|
|
.expect("shell environment overrides");
|
|
assert_eq!(
|
|
values.get("foo").and_then(TomlValue::as_str),
|
|
Some("lowercase")
|
|
);
|
|
assert_eq!(
|
|
values.get("FOO").and_then(TomlValue::as_str),
|
|
Some("uppercase")
|
|
);
|
|
assert_eq!(values.len(), 6);
|
|
}
|
|
|
|
#[test]
|
|
fn later_credential_provider_preserves_case_distinct_environment_overrides() {
|
|
let mut base = parse_toml(
|
|
"[shell_environment_policy.set]\n\
|
|
stripe_api_key = 'stale'\n\
|
|
stripe_host = 'stale.example'",
|
|
);
|
|
let override_layer = parse_toml(
|
|
"[shell_environment_policy.set]\n\
|
|
STRIPE_API_KEY = 'previous'\n\
|
|
STRIPE_HOST = 'previous.example'",
|
|
);
|
|
let final_override_layer = parse_toml(
|
|
"[shell_environment_policy.set]\n\
|
|
stripe_api_key = 'trusted'\n\
|
|
stripe_host = 'current.example'",
|
|
);
|
|
let provider_layer = parse_toml(
|
|
"[features.network_proxy.credentials.stripe]\n\
|
|
env = ['STRIPE_API_KEY']\n\
|
|
url_prefix_from_env = 'STRIPE_HOST'",
|
|
);
|
|
|
|
merge_toml_values(&mut base, &override_layer);
|
|
merge_toml_values(&mut base, &final_override_layer);
|
|
merge_toml_values(&mut base, &provider_layer);
|
|
|
|
assert_eq!(
|
|
base["shell_environment_policy"]["set"],
|
|
parse_toml(
|
|
"stripe_api_key = 'trusted'\n\
|
|
stripe_host = 'current.example'\n\
|
|
STRIPE_API_KEY = 'previous'\n\
|
|
STRIPE_HOST = 'previous.example'",
|
|
)
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn shell_environment_policy_filters_overlay_merges_by_key_case_insensitively() {
|
|
let mut base = parse_toml(
|
|
r#"
|
|
[shell_environment_policy.filters]
|
|
"FLIP_*" = "exclude"
|
|
"KEEP_*" = "include"
|
|
"#,
|
|
);
|
|
let overlay = parse_toml(
|
|
r#"
|
|
[shell_environment_policy.filters]
|
|
"ADD_*" = "exclude"
|
|
"flip_*" = "include"
|
|
"#,
|
|
);
|
|
|
|
merge_toml_values(&mut base, &overlay);
|
|
|
|
assert_eq!(
|
|
base,
|
|
parse_toml(
|
|
r#"
|
|
[shell_environment_policy.filters]
|
|
"add_*" = "exclude"
|
|
"flip_*" = "include"
|
|
"keep_*" = "include"
|
|
"#,
|
|
)
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn shell_environment_policy_filters_overlay_merges_unicode_keys_case_insensitively() {
|
|
let mut base = parse_toml(
|
|
r#"
|
|
[shell_environment_policy.filters]
|
|
"СЕКРЕТ_*" = "exclude"
|
|
"#,
|
|
);
|
|
let overlay = parse_toml(
|
|
r#"
|
|
[shell_environment_policy.filters]
|
|
"секрет_*" = "include"
|
|
"#,
|
|
);
|
|
|
|
merge_toml_values(&mut base, &overlay);
|
|
|
|
assert_eq!(base, overlay);
|
|
}
|
|
|
|
#[test]
|
|
fn shell_environment_policy_filters_replace_lower_legacy_filter_fields() {
|
|
let mut base = parse_toml(
|
|
r#"
|
|
[shell_environment_policy]
|
|
inherit = "core"
|
|
exclude = ["FLIP_TO_INCLUDE", "KEEP_EXCLUDED"]
|
|
include_only = ["FLIP_TO_EXCLUDE", "KEEP_INCLUDED"]
|
|
"#,
|
|
);
|
|
let overlay = parse_toml(
|
|
r#"
|
|
[shell_environment_policy.filters]
|
|
"ADD_INCLUDED" = "include"
|
|
"FLIP_TO_EXCLUDE" = "exclude"
|
|
"FLIP_TO_INCLUDE" = "include"
|
|
"#,
|
|
);
|
|
|
|
merge_toml_values(&mut base, &overlay);
|
|
|
|
assert_eq!(
|
|
base,
|
|
parse_toml(
|
|
r#"
|
|
[shell_environment_policy]
|
|
inherit = "core"
|
|
|
|
[shell_environment_policy.filters]
|
|
"ADD_INCLUDED" = "include"
|
|
"FLIP_TO_EXCLUDE" = "exclude"
|
|
"FLIP_TO_INCLUDE" = "include"
|
|
"#,
|
|
)
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn shell_environment_policy_legacy_arrays_replace_lower_filters() {
|
|
let mut base = parse_toml(
|
|
r#"
|
|
[shell_environment_policy]
|
|
inherit = "core"
|
|
|
|
[shell_environment_policy.filters]
|
|
"FLIP_TO_EXCLUDE" = "include"
|
|
"LOW_EXCLUDED" = "exclude"
|
|
"KEEP_INCLUDED" = "include"
|
|
"#,
|
|
);
|
|
let overlay = parse_toml(
|
|
r#"
|
|
[shell_environment_policy]
|
|
exclude = ["FLIP_TO_EXCLUDE", "HIGH_EXCLUDED"]
|
|
"#,
|
|
);
|
|
|
|
merge_toml_values(&mut base, &overlay);
|
|
|
|
assert_eq!(
|
|
base,
|
|
parse_toml(
|
|
r#"
|
|
[shell_environment_policy]
|
|
inherit = "core"
|
|
exclude = ["FLIP_TO_EXCLUDE", "HIGH_EXCLUDED"]
|
|
"#,
|
|
)
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn empty_shell_environment_filter_representations_replace_the_other_form() {
|
|
let cases = [
|
|
(
|
|
r#"[shell_environment_policy]
|
|
exclude = ["AWS_*"]
|
|
include_only = ["PATH"]
|
|
"#,
|
|
r#"[shell_environment_policy.filters]
|
|
"#,
|
|
),
|
|
(
|
|
r#"[shell_environment_policy.filters]
|
|
"AWS_*" = "include"
|
|
"#,
|
|
r#"[shell_environment_policy]
|
|
exclude = []
|
|
"#,
|
|
),
|
|
];
|
|
|
|
for (base, overlay) in cases {
|
|
let mut base = parse_toml(base);
|
|
let overlay = parse_toml(overlay);
|
|
|
|
merge_toml_values(&mut base, &overlay);
|
|
|
|
assert_eq!(base, overlay);
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn sparse_overlay_preserves_unvisited_base_aliases() {
|
|
let mut base = parse_toml(
|
|
"[memories]\nno_memories_if_mcp_or_web_search = false\n[agents]\nmax_threads = 2",
|
|
);
|
|
let overlay = parse_toml("[agents]\nmax_depth = 3");
|
|
|
|
merge_toml_values(&mut base, &overlay);
|
|
|
|
assert_eq!(
|
|
base,
|
|
parse_toml(
|
|
"[memories]\nno_memories_if_mcp_or_web_search = false\n[agents]\nmax_concurrent_threads_per_session = 2\nmax_depth = 3",
|
|
),
|
|
);
|
|
}
|