use crate::Feature;
use crate::FeatureConfigSource;
use crate::FeatureOverrides;
use crate::FeatureToml;
use crate::Features;
use crate::FeaturesToml;
use crate::Stage;
use crate::feature_for_key;
use crate::unstable_features_warning_event;
use codex_protocol::protocol::EventMsg;
use codex_protocol::protocol::WarningEvent;
use pretty_assertions::assert_eq;
use std::collections::BTreeMap;
use toml::Table;
use toml::Value as TomlValue;

#[test]
fn under_development_features_are_disabled_by_default() {
    for spec in crate::FEATURES {
        if matches!(spec.stage, Stage::UnderDevelopment) {
            assert_eq!(
                spec.default_enabled, false,
                "feature `{}` is under development and must be disabled by default",
                spec.key
            );
        }
    }
}

#[test]
fn default_enabled_features_are_stable() {
    for spec in crate::FEATURES {
        if spec.default_enabled {
            assert!(
                matches!(spec.stage, Stage::Stable | Stage::Removed)
                    || spec.id == Feature::TerminalResizeReflow,
                "feature `{}` is enabled by default but is not stable/removed ({:?})",
                spec.key,
                spec.stage
            );
        }
    }
}

#[test]
fn use_legacy_landlock_is_deprecated_and_disabled_by_default() {
    assert_eq!(Feature::UseLegacyLandlock.stage(), Stage::Deprecated);
    assert_eq!(Feature::UseLegacyLandlock.default_enabled(), false);
}

#[test]
fn use_linux_sandbox_bwrap_is_removed_and_disabled_by_default() {
    assert_eq!(Feature::UseLinuxSandboxBwrap.stage(), Stage::Removed);
    assert_eq!(Feature::UseLinuxSandboxBwrap.default_enabled(), false);
}

#[test]
fn undo_is_removed_and_disabled_by_default() {
    assert_eq!(Feature::GhostCommit.stage(), Stage::Removed);
    assert_eq!(Feature::GhostCommit.default_enabled(), false);
}

#[test]
fn image_detail_original_is_removed_and_disabled_by_default() {
    assert_eq!(Feature::ImageDetailOriginal.stage(), Stage::Removed);
    assert_eq!(Feature::ImageDetailOriginal.default_enabled(), false);
}

#[test]
fn apply_patch_freeform_is_removed_and_disabled_by_default() {
    assert_eq!(Feature::ApplyPatchFreeform.stage(), Stage::Removed);
    assert_eq!(Feature::ApplyPatchFreeform.default_enabled(), false);
    assert_eq!(
        feature_for_key("apply_patch_freeform"),
        Some(Feature::ApplyPatchFreeform)
    );
}

#[test]
fn plugin_hooks_is_removed_and_disabled_by_default() {
    assert_eq!(Feature::PluginHooks.stage(), Stage::Removed);
    assert_eq!(Feature::PluginHooks.default_enabled(), false);
    assert_eq!(feature_for_key("plugin_hooks"), Some(Feature::PluginHooks));
}

#[test]
fn external_migration_is_removed_and_disabled_by_default() {
    assert_eq!(Feature::ExternalMigration.stage(), Stage::Removed);
    assert_eq!(Feature::ExternalMigration.default_enabled(), false);
    assert_eq!(
        feature_for_key("external_migration"),
        Some(Feature::ExternalMigration)
    );
}

#[test]
fn removed_apps_mcp_path_override_shapes_are_ignored() {
    let features = [
        toml::from_str::<FeaturesToml>("apps_mcp_path_override = true")
            .expect("boolean compatibility form should deserialize"),
        toml::from_str::<FeaturesToml>(
            r#"
[apps_mcp_path_override]
enabled = true
path = "/custom/mcp"
"#,
        )
        .expect("structured compatibility form should deserialize"),
    ];

    assert_eq!(
        features.map(|features| features.entries()),
        [BTreeMap::new(), BTreeMap::new()]
    );
}

#[test]
fn code_mode_only_requires_code_mode() {
    let mut features = Features::with_defaults();
    features.enable(Feature::CodeModeOnly);
    features.normalize_dependencies();

    assert_eq!(features.enabled(Feature::CodeModeOnly), true);
    assert_eq!(features.enabled(Feature::CodeMode), true);
}

#[test]
fn guardian_approval_is_stable_and_enabled_by_default() {
    let spec = Feature::GuardianApproval.info();

    assert_eq!(spec.stage, Stage::Stable);
    assert_eq!(Feature::GuardianApproval.default_enabled(), true);
}

#[test]
fn request_permissions_is_under_development() {
    assert_eq!(
        Feature::ExecPermissionApprovals.stage(),
        Stage::UnderDevelopment
    );
    assert_eq!(Feature::ExecPermissionApprovals.default_enabled(), false);
}

#[test]
fn request_permissions_tool_is_under_development() {
    assert_eq!(
        Feature::RequestPermissionsTool.stage(),
        Stage::UnderDevelopment
    );
    assert_eq!(Feature::RequestPermissionsTool.default_enabled(), false);
}

#[test]
fn terminal_resize_reflow_is_experimental_and_enabled_by_default() {
    assert_eq!(
        feature_for_key("terminal_resize_reflow"),
        Some(Feature::TerminalResizeReflow)
    );
    assert!(matches!(
        Feature::TerminalResizeReflow.stage(),
        Stage::Experimental { .. }
    ));
    assert_eq!(Feature::TerminalResizeReflow.default_enabled(), true);
}

#[test]
fn tool_suggest_is_stable_and_enabled_by_default() {
    assert_eq!(Feature::ToolSuggest.stage(), Stage::Stable);
    assert_eq!(Feature::ToolSuggest.default_enabled(), true);
}

#[test]
fn network_proxy_is_experimental_and_disabled_by_default() {
    assert_eq!(
        feature_for_key("network_proxy"),
        Some(Feature::NetworkProxy)
    );
    assert!(matches!(
        Feature::NetworkProxy.stage(),
        Stage::Experimental { .. }
    ));
    assert_eq!(Feature::NetworkProxy.default_enabled(), false);
}

#[test]
fn tool_search_is_removed_and_disabled_by_default() {
    assert_eq!(Feature::ToolSearch.stage(), Stage::Removed);
    assert_eq!(Feature::ToolSearch.default_enabled(), false);
    assert_eq!(feature_for_key("tool_search"), Some(Feature::ToolSearch));
}

#[test]
fn secret_auth_storage_defaults_to_windows_only() {
    assert_eq!(Feature::SecretAuthStorage.stage(), Stage::Stable);
    assert_eq!(Feature::SecretAuthStorage.default_enabled(), cfg!(windows));
    assert_eq!(
        feature_for_key("secret_auth_storage"),
        Some(Feature::SecretAuthStorage)
    );
}

#[test]
fn browser_controls_are_stable_and_enabled_by_default() {
    assert_eq!(Feature::InAppBrowser.stage(), Stage::Stable);
    assert_eq!(Feature::InAppBrowser.default_enabled(), true);
    assert_eq!(
        feature_for_key("in_app_browser"),
        Some(Feature::InAppBrowser)
    );

    assert_eq!(Feature::BrowserUse.stage(), Stage::Stable);
    assert_eq!(Feature::BrowserUse.default_enabled(), true);
    assert_eq!(feature_for_key("browser_use"), Some(Feature::BrowserUse));

    assert_eq!(Feature::BrowserUseExternal.stage(), Stage::Stable);
    assert_eq!(Feature::BrowserUseExternal.default_enabled(), true);
    assert_eq!(
        feature_for_key("browser_use_external"),
        Some(Feature::BrowserUseExternal)
    );

    assert_eq!(Feature::ComputerUse.stage(), Stage::Stable);
    assert_eq!(Feature::ComputerUse.default_enabled(), true);
    assert_eq!(feature_for_key("computer_use"), Some(Feature::ComputerUse));
}

#[test]
fn use_linux_sandbox_bwrap_is_a_removed_feature_key() {
    assert_eq!(
        feature_for_key("use_legacy_landlock"),
        Some(Feature::UseLegacyLandlock)
    );
    assert_eq!(
        feature_for_key("use_linux_sandbox_bwrap"),
        Some(Feature::UseLinuxSandboxBwrap)
    );
}

#[test]
fn image_generation_is_stable_and_enabled_by_default() {
    assert_eq!(Feature::ImageGeneration.stage(), Stage::Stable);
    assert_eq!(Feature::ImageGeneration.default_enabled(), true);
}

#[test]
fn image_generation_extension_is_under_development_and_disabled_by_default() {
    assert_eq!(Feature::ImageGenExt.stage(), Stage::UnderDevelopment);
    assert_eq!(Feature::ImageGenExt.default_enabled(), false);
    assert_eq!(feature_for_key("imagegenext"), Some(Feature::ImageGenExt));
}

#[test]
fn use_legacy_landlock_config_records_deprecation_notice() {
    let mut entries = BTreeMap::new();
    entries.insert("use_legacy_landlock".to_string(), true);

    let mut features = Features::with_defaults();
    features.apply_map(&entries);

    let usages = features.legacy_feature_usages().collect::<Vec<_>>();
    assert_eq!(usages.len(), 1);
    assert_eq!(usages[0].alias, "features.use_legacy_landlock");
    assert_eq!(usages[0].feature, Feature::UseLegacyLandlock);
    assert_eq!(
        usages[0].summary,
        "`[features].use_legacy_landlock` is deprecated and will be removed soon."
    );
    assert_eq!(
        usages[0].details.as_deref(),
        Some("Remove this setting to stop opting into the legacy Linux sandbox behavior.")
    );
}

#[test]
fn image_detail_original_is_a_removed_feature_key() {
    assert_eq!(
        feature_for_key("image_detail_original"),
        Some(Feature::ImageDetailOriginal)
    );
}

#[test]
fn js_repl_features_are_removed_feature_keys() {
    assert_eq!(Feature::JsRepl.stage(), Stage::Removed);
    assert_eq!(Feature::JsRepl.default_enabled(), false);
    assert_eq!(feature_for_key("js_repl"), Some(Feature::JsRepl));

    assert_eq!(Feature::JsReplToolsOnly.stage(), Stage::Removed);
    assert_eq!(Feature::JsReplToolsOnly.default_enabled(), false);
    assert_eq!(
        feature_for_key("js_repl_tools_only"),
        Some(Feature::JsReplToolsOnly)
    );
}

#[test]
fn tool_call_mcp_elicitation_is_stable_and_enabled_by_default() {
    assert_eq!(Feature::ToolCallMcpElicitation.stage(), Stage::Stable);
    assert_eq!(Feature::ToolCallMcpElicitation.default_enabled(), true);
}

#[test]
fn auth_elicitation_is_under_development() {
    assert_eq!(Feature::AuthElicitation.stage(), Stage::UnderDevelopment);
    assert_eq!(Feature::AuthElicitation.default_enabled(), false);
    assert_eq!(
        feature_for_key("auth_elicitation"),
        Some(Feature::AuthElicitation)
    );
}

#[test]
fn mentions_v2_is_stable_and_enabled_by_default() {
    assert_eq!(Feature::MentionsV2.stage(), Stage::Stable);
    assert_eq!(Feature::MentionsV2.default_enabled(), true);
    assert_eq!(feature_for_key("mentions_v2"), Some(Feature::MentionsV2));
}

#[test]
fn remote_control_is_removed_and_disabled_by_default() {
    assert_eq!(Feature::RemoteControl.stage(), Stage::Removed);
    assert_eq!(Feature::RemoteControl.default_enabled(), false);
    assert_eq!(
        feature_for_key("remote_control"),
        Some(Feature::RemoteControl)
    );
}

#[test]
fn remote_control_config_is_ignored() {
    let mut entries = BTreeMap::new();
    entries.insert("remote_control".to_string(), true);

    let mut features = Features::with_defaults();
    features.apply_map(&entries);

    assert_eq!(features.enabled(Feature::RemoteControl), false);
}

#[test]
fn workspace_dependencies_is_stable_and_enabled_by_default() {
    assert_eq!(Feature::WorkspaceDependencies.stage(), Stage::Stable);
    assert_eq!(Feature::WorkspaceDependencies.default_enabled(), true);
    assert_eq!(
        feature_for_key("workspace_dependencies"),
        Some(Feature::WorkspaceDependencies)
    );
}

#[test]
fn telepathy_is_legacy_alias_for_chronicle() {
    assert_eq!(Feature::Chronicle.stage(), Stage::UnderDevelopment);
    assert_eq!(Feature::Chronicle.default_enabled(), false);
    assert_eq!(feature_for_key("chronicle"), Some(Feature::Chronicle));
    assert_eq!(feature_for_key("telepathy"), Some(Feature::Chronicle));
}

#[test]
fn collab_is_legacy_alias_for_multi_agent() {
    assert_eq!(feature_for_key("multi_agent"), Some(Feature::Collab));
    assert_eq!(feature_for_key("collab"), Some(Feature::Collab));
}

#[test]
fn codex_hooks_is_legacy_alias_for_hooks() {
    assert_eq!(feature_for_key("hooks"), Some(Feature::CodexHooks));
    assert_eq!(feature_for_key("codex_hooks"), Some(Feature::CodexHooks));
}

#[test]
fn multi_agent_is_stable_and_enabled_by_default() {
    assert_eq!(Feature::Collab.stage(), Stage::Stable);
    assert_eq!(Feature::Collab.default_enabled(), true);
}

#[test]
fn enable_fanout_is_under_development() {
    assert_eq!(Feature::SpawnCsv.stage(), Stage::UnderDevelopment);
    assert_eq!(Feature::SpawnCsv.default_enabled(), false);
}

#[test]
fn enable_fanout_normalization_enables_multi_agent_one_way() {
    let mut enable_fanout_features = Features::with_defaults();
    enable_fanout_features.enable(Feature::SpawnCsv);
    enable_fanout_features.normalize_dependencies();
    assert_eq!(enable_fanout_features.enabled(Feature::SpawnCsv), true);
    assert_eq!(enable_fanout_features.enabled(Feature::Collab), true);

    let mut collab_features = Features::with_defaults();
    collab_features.enable(Feature::Collab);
    collab_features.normalize_dependencies();
    assert_eq!(collab_features.enabled(Feature::Collab), true);
    assert_eq!(collab_features.enabled(Feature::SpawnCsv), false);
}

#[test]
fn apps_require_feature_flag_and_chatgpt_auth() {
    let mut features = Features::with_defaults();
    assert!(!features.apps_enabled_for_auth(/*has_chatgpt_auth*/ false));

    features.enable(Feature::Apps);
    assert!(!features.apps_enabled_for_auth(/*has_chatgpt_auth*/ false));
    assert!(features.apps_enabled_for_auth(/*has_chatgpt_auth*/ true));
}

#[test]
fn from_sources_applies_base_profile_and_overrides() {
    let mut base_entries = BTreeMap::new();
    base_entries.insert("plugins".to_string(), true);
    let base_features = FeaturesToml {
        entries: base_entries,
        ..Default::default()
    };

    let mut profile_entries = BTreeMap::new();
    profile_entries.insert("code_mode_only".to_string(), true);
    let profile_features = FeaturesToml {
        entries: profile_entries,
        ..Default::default()
    };

    let features = Features::from_sources(
        FeatureConfigSource {
            features: Some(&base_features),
            ..Default::default()
        },
        FeatureConfigSource {
            features: Some(&profile_features),
            ..Default::default()
        },
        FeatureOverrides {
            web_search_request: Some(false),
        },
    );

    assert_eq!(features.enabled(Feature::Plugins), true);
    assert_eq!(features.enabled(Feature::CodeModeOnly), true);
    assert_eq!(features.enabled(Feature::CodeMode), true);
    assert_eq!(features.enabled(Feature::ApplyPatchFreeform), false);
    assert_eq!(features.enabled(Feature::WebSearchRequest), false);
}

#[test]
fn from_sources_ignores_removed_image_detail_original_feature_key() {
    let features_toml = FeaturesToml::from(BTreeMap::from([(
        "image_detail_original".to_string(),
        true,
    )]));

    let features = Features::from_sources(
        FeatureConfigSource {
            features: Some(&features_toml),
            ..Default::default()
        },
        FeatureConfigSource::default(),
        FeatureOverrides::default(),
    );

    assert_eq!(features, Features::with_defaults());
}

#[test]
fn from_sources_ignores_removed_undo_feature_key() {
    let features_toml = FeaturesToml::from(BTreeMap::from([("undo".to_string(), true)]));

    let features = Features::from_sources(
        FeatureConfigSource {
            features: Some(&features_toml),
            ..Default::default()
        },
        FeatureConfigSource::default(),
        FeatureOverrides::default(),
    );

    assert_eq!(features, Features::with_defaults());
}

#[test]
fn from_sources_ignores_removed_js_repl_feature_keys() {
    let features_toml = FeaturesToml::from(BTreeMap::from([
        ("js_repl".to_string(), true),
        ("js_repl_tools_only".to_string(), true),
    ]));

    let features = Features::from_sources(
        FeatureConfigSource {
            features: Some(&features_toml),
            ..Default::default()
        },
        FeatureConfigSource::default(),
        FeatureOverrides::default(),
    );

    assert_eq!(features, Features::with_defaults());
}

#[test]
fn from_sources_ignores_removed_apply_patch_freeform_feature_key() {
    let features_toml =
        FeaturesToml::from(BTreeMap::from([("apply_patch_freeform".to_string(), true)]));

    let features = Features::from_sources(
        FeatureConfigSource {
            features: Some(&features_toml),
            ..Default::default()
        },
        FeatureConfigSource::default(),
        FeatureOverrides::default(),
    );

    assert_eq!(features, Features::with_defaults());
}

#[test]
fn from_sources_ignores_removed_plugin_hooks_feature_key() {
    let features_toml = FeaturesToml::from(BTreeMap::from([("plugin_hooks".to_string(), true)]));

    let features = Features::from_sources(
        FeatureConfigSource {
            features: Some(&features_toml),
            ..Default::default()
        },
        FeatureConfigSource::default(),
        FeatureOverrides::default(),
    );

    assert_eq!(features, Features::with_defaults());
}

#[test]
fn multi_agent_v2_feature_config_deserializes_boolean_toggle() {
    let features: FeaturesToml = toml::from_str(
        r#"
multi_agent_v2 = true
"#,
    )
    .expect("features table should deserialize");

    assert_eq!(
        features.entries(),
        BTreeMap::from([("multi_agent_v2".to_string(), true)])
    );
    assert_eq!(features.multi_agent_v2, Some(FeatureToml::Enabled(true)));
}

#[test]
fn multi_agent_v2_feature_config_deserializes_table() {
    let features: FeaturesToml = toml::from_str(
        r#"
[multi_agent_v2]
enabled = true
max_concurrent_threads_per_session = 4
min_wait_timeout_ms = 2500
max_wait_timeout_ms = 120000
default_wait_timeout_ms = 30000
usage_hint_enabled = false
usage_hint_text = "Custom delegation guidance."
root_agent_usage_hint_text = "Root guidance."
subagent_usage_hint_text = "Subagent guidance."
tool_namespace = "agents"
hide_spawn_agent_metadata = true
non_code_mode_only = true
"#,
    )
    .expect("features table should deserialize");

    assert_eq!(
        features.entries(),
        BTreeMap::from([("multi_agent_v2".to_string(), true)])
    );
    assert_eq!(
        features.multi_agent_v2,
        Some(crate::FeatureToml::Config(crate::MultiAgentV2ConfigToml {
            enabled: Some(true),
            max_concurrent_threads_per_session: Some(4),
            min_wait_timeout_ms: Some(2500),
            max_wait_timeout_ms: Some(120000),
            default_wait_timeout_ms: Some(30000),
            usage_hint_enabled: Some(false),
            usage_hint_text: Some("Custom delegation guidance.".to_string()),
            root_agent_usage_hint_text: Some("Root guidance.".to_string()),
            subagent_usage_hint_text: Some("Subagent guidance.".to_string()),
            tool_namespace: Some("agents".to_string()),
            hide_spawn_agent_metadata: Some(true),
            non_code_mode_only: Some(true),
        }))
    );
}

#[test]
fn multi_agent_v2_feature_config_usage_hint_enabled_does_not_enable_feature() {
    let features_toml: FeaturesToml = toml::from_str(
        r#"
[multi_agent_v2]
usage_hint_enabled = false
"#,
    )
    .expect("features table should deserialize");
    let features = Features::from_sources(
        FeatureConfigSource {
            features: Some(&features_toml),
            ..Default::default()
        },
        FeatureConfigSource::default(),
        FeatureOverrides::default(),
    );

    assert_eq!(features.enabled(Feature::MultiAgentV2), false);
    assert_eq!(features_toml.entries(), BTreeMap::new());
    assert_eq!(
        features_toml.multi_agent_v2,
        Some(crate::FeatureToml::Config(crate::MultiAgentV2ConfigToml {
            enabled: None,
            max_concurrent_threads_per_session: None,
            min_wait_timeout_ms: None,
            max_wait_timeout_ms: None,
            default_wait_timeout_ms: None,
            usage_hint_enabled: Some(false),
            usage_hint_text: None,
            root_agent_usage_hint_text: None,
            subagent_usage_hint_text: None,
            tool_namespace: None,
            hide_spawn_agent_metadata: None,
            non_code_mode_only: None,
        }))
    );
}

#[test]
fn materialize_resolved_enabled_writes_all_features_and_preserves_custom_config() {
    let mut features = Features::with_defaults();
    features.enable(Feature::CodeMode);
    features.enable(Feature::MultiAgentV2);
    features.enable(Feature::NetworkProxy);

    let mut features_toml = FeaturesToml {
        multi_agent_v2: Some(FeatureToml::Config(crate::MultiAgentV2ConfigToml {
            enabled: Some(false),
            min_wait_timeout_ms: Some(2500),
            ..Default::default()
        })),
        network_proxy: Some(FeatureToml::Config(crate::NetworkProxyConfigToml {
            enabled: Some(false),
            proxy_url: Some("http://127.0.0.1:43128".to_string()),
            ..Default::default()
        })),
        entries: BTreeMap::new(),
        ..Default::default()
    };

    features_toml.materialize_resolved_enabled(&features);

    let entries = features_toml.entries();
    for spec in crate::FEATURES {
        assert_eq!(
            entries.get(spec.key),
            Some(&features.enabled(spec.id)),
            "{}",
            spec.key
        );
    }
    assert_eq!(
        features_toml.multi_agent_v2,
        Some(FeatureToml::Config(crate::MultiAgentV2ConfigToml {
            enabled: Some(true),
            min_wait_timeout_ms: Some(2500),
            ..Default::default()
        }))
    );
    assert_eq!(
        features_toml.network_proxy,
        Some(FeatureToml::Config(crate::NetworkProxyConfigToml {
            enabled: Some(true),
            proxy_url: Some("http://127.0.0.1:43128".to_string()),
            ..Default::default()
        }))
    );
    let replayed = Features::from_sources(
        FeatureConfigSource {
            features: Some(&features_toml),
            ..Default::default()
        },
        FeatureConfigSource::default(),
        FeatureOverrides::default(),
    );
    assert_eq!(replayed.enabled(Feature::ApplyPatchFreeform), false);
}

#[test]
fn unstable_warning_event_only_mentions_enabled_under_development_features() {
    let mut configured_features = Table::new();
    configured_features.insert("child_agents_md".to_string(), TomlValue::Boolean(true));
    configured_features.insert("personality".to_string(), TomlValue::Boolean(true));
    configured_features.insert("unknown".to_string(), TomlValue::Boolean(true));

    let mut features = Features::with_defaults();
    features.enable(Feature::ChildAgentsMd);

    let warning = unstable_features_warning_event(
        Some(&configured_features),
        /*suppress_unstable_features_warning*/ false,
        &features,
        "/tmp/config.toml",
    )
    .expect("warning event");

    let EventMsg::Warning(WarningEvent { message }) = warning.msg else {
        panic!("expected warning event");
    };
    assert!(message.contains("child_agents_md"));
    assert!(!message.contains("personality"));
    assert!(message.contains("/tmp/config.toml"));
}

#[test]
fn unstable_warning_event_mentions_enabled_structured_under_development_feature() {
    let configured_features: Table = toml::from_str(
        r#"
multi_agent_v2 = { enabled = true, tool_namespace = "agents" }
code_mode = true
"#,
    )
    .expect("features table should deserialize");

    let mut features = Features::with_defaults();
    features.enable(Feature::MultiAgentV2);
    features.enable(Feature::CodeMode);

    let warning = unstable_features_warning_event(
        Some(&configured_features),
        /*suppress_unstable_features_warning*/ false,
        &features,
        "/tmp/config.toml",
    )
    .expect("warning event");

    let EventMsg::Warning(WarningEvent { message }) = warning.msg else {
        panic!("expected warning event");
    };
    assert_eq!(
        "Under-development features enabled: code_mode, multi_agent_v2. Under-development features are incomplete and may behave unpredictably. To suppress this warning, set `suppress_unstable_features_warning = true` in /tmp/config.toml.".to_string(),
        message
    );
}
