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); } #[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_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); } }