osp-cli 1.5.1

CLI and REPL for querying and managing OSP infrastructure data
Documentation
mod bootstrap_registry_contracts {
    use super::*;

    #[test]
    fn profile_default_is_bootstrap_only_and_rejects_invalid_scopes() {
        let spec = bootstrap_key_spec("profile.default").expect("spec should exist");
        assert_eq!(spec.key, "profile.default");
        assert_eq!(spec.phase, BootstrapPhase::Profile);
        assert!(!spec.runtime_visible);
        assert_eq!(spec.scope_rule, BootstrapScopeRule::GlobalOrTerminal);
        assert!(is_bootstrap_only_key("profile.default"));

        let schema = ConfigSchema::default();
        assert!(schema.is_known_key("profile.default"));
        assert!(!schema.is_runtime_visible_key("profile.default"));
        assert!(schema.is_runtime_visible_key("profile.active"));
        assert_eq!(
            schema.doc_for_key("profile.default"),
            Some("Default profile selected when no override is provided")
        );
        assert_eq!(
            schema.doc_for_key("profile.active"),
            Some("Active profile derived during resolution")
        );

        let err = validate_key_scope("profile.default", &Scope::profile("work"))
            .expect_err("profile scope should be rejected");
        match err {
            crate::config::ConfigError::InvalidBootstrapScope {
                key,
                profile,
                terminal,
            } => {
                assert_eq!(key, "profile.default");
                assert_eq!(profile.as_deref(), Some("work"));
                assert_eq!(terminal, None);
            }
            other => panic!("unexpected error: {other:?}"),
        }
    }

    #[test]
    fn bootstrap_and_alias_helpers_match_runtime_rules() {
        assert_eq!(ConfigSource::Environment.to_string(), "env");
        assert!(is_alias_key("alias.lookup"));
        assert!(!is_alias_key("ui.format"));

        validate_bootstrap_value("profile.default", &ConfigValue::String("work".to_string()))
            .expect("non-empty bootstrap default should validate");

        let err = validate_bootstrap_value("profile.default", &ConfigValue::String(String::new()))
            .expect_err("empty bootstrap default should fail");
        assert!(matches!(
            err,
            crate::config::ConfigError::InvalidBootstrapValue { key, .. } if key == "profile.default"
        ));

        let normalized = crate::config::normalize_scope(Scope {
            profile: Some("Work".to_string()),
            terminal: Some("RePl".to_string()),
        });
        assert_eq!(normalized.profile.as_deref(), Some("work"));
        assert_eq!(normalized.terminal.as_deref(), Some("repl"));
    }
}

mod schema_value_contracts {
    use super::*;

    #[test]
    fn schema_parsing_handles_scalars_lists_enums_and_derived_keys() {
        let schema = ConfigSchema::default();

        assert_eq!(
            schema
                .parse_input_value("ui.prompt.secrets", "true")
                .expect("bool should parse"),
            ConfigValue::Bool(true)
        );
        assert_eq!(
            schema
                .parse_input_value("session.cache.max_results", "42")
                .expect("integer should parse"),
            ConfigValue::Integer(42)
        );
        assert_eq!(
            schema
                .parse_input_value("theme.path", "custom, ./theme.toml ,")
                .expect("list should parse"),
            ConfigValue::List(vec![
                ConfigValue::String("custom".to_string()),
                ConfigValue::String("./theme.toml".to_string()),
            ])
        );
        assert_eq!(
            schema
                .parse_input_value("ui.format", "json")
                .expect("enum should parse"),
            ConfigValue::String("json".to_string())
        );

        let enum_err = schema
            .parse_input_value("ui.format", "yaml")
            .expect_err("invalid enum should fail");
        assert!(matches!(
            enum_err,
            crate::config::ConfigError::InvalidEnumValue {
                key,
                value,
                allowed
            } if key == "ui.format" && value == "yaml" && allowed.contains(&"json".to_string())
        ));

        let type_err = schema
            .parse_input_value("ui.prompt.secrets", "wat")
            .expect_err("invalid bool should fail");
        assert!(matches!(
            type_err,
            crate::config::ConfigError::InvalidValueType {
                key,
                expected: SchemaValueType::Bool,
                ..
            } if key == "ui.prompt.secrets"
        ));

        let read_only = schema
            .parse_input_value("profile.active", "ops")
            .expect_err("profile.active should be read-only");
        assert!(matches!(
            read_only,
            crate::config::ConfigError::ReadOnlyConfigKey { key, .. } if key == "profile.active"
        ));
        assert_eq!(schema.doc_for_key("ui.format"), Some("Default output format"));
    }

    #[test]
    fn config_values_cover_secret_interpolation_toml_and_display_paths() {
        let secret = ConfigValue::String("shh".to_string()).into_secret();
        assert!(secret.is_secret());
        assert_eq!(secret.reveal(), &ConfigValue::String("shh".to_string()));
        assert_eq!(secret.to_string(), "[REDACTED]");
        assert_eq!(
            format!("{:?}", SecretValue::new(ConfigValue::Bool(true))),
            "[REDACTED]"
        );

        let scalar = ConfigValue::Integer(7);
        assert_eq!(
            scalar
                .as_interpolation_string("ldap.port", "profile.ldap.port")
                .expect("scalar interpolation should work"),
            "7"
        );

        let interpolation_err = ConfigValue::List(vec![ConfigValue::String("x".to_string())])
            .as_interpolation_string("ldap.uri", "profiles")
            .expect_err("list interpolation should fail");
        assert!(matches!(
            interpolation_err,
            crate::config::ConfigError::NonScalarPlaceholder { key, placeholder }
                if key == "ldap.uri" && placeholder == "profiles"
        ));

        let dt = toml::Value::Datetime("2024-01-02T03:04:05Z".parse().expect("datetime"));
        assert_eq!(
            ConfigValue::from_toml("demo.time", &dt).expect("datetime should adapt"),
            ConfigValue::String("2024-01-02T03:04:05Z".to_string())
        );

        let table = toml::Value::Table(toml::map::Map::new());
        let table_err =
            ConfigValue::from_toml("demo.obj", &table).expect_err("table should fail");
        assert!(matches!(
            table_err,
            crate::config::ConfigError::UnsupportedTomlValue { path, kind }
                if path == "demo.obj" && kind == "table"
        ));

        let already_secret = ConfigValue::String("hidden".to_string()).into_secret();
        assert_eq!(already_secret.clone().into_secret(), already_secret);
        assert_eq!(
            SecretValue::new(ConfigValue::Integer(9)).into_inner(),
            ConfigValue::Integer(9)
        );
        assert_eq!(ConfigValue::from(true), ConfigValue::Bool(true));
        assert_eq!(ConfigValue::from(7_i64), ConfigValue::Integer(7));
        assert_eq!(ConfigValue::from(1.5_f64), ConfigValue::Float(1.5));
        assert_eq!(
            ConfigValue::List(vec![
                ConfigValue::String("a".to_string()),
                ConfigValue::Bool(true),
            ])
            .to_string(),
            "[a,true]"
        );
        assert_eq!(ConfigValue::Bool(true).to_string(), "true");
        assert_eq!(ConfigValue::Integer(7).to_string(), "7");
        assert_eq!(ConfigValue::Float(2.5).to_string(), "2.5");

        assert_eq!(ConfigSource::Cli.to_string(), "cli");
        assert_eq!(ConfigSource::Session.to_string(), "session");
        assert_eq!(ConfigSource::Derived.to_string(), "derived");
        assert_eq!(SchemaValueType::String.to_string(), "string");
        assert_eq!(SchemaValueType::Float.to_string(), "float");
        assert_eq!(SchemaValueType::StringList.to_string(), "list");
    }
}