khive-runtime 0.10.0

Composable Service API: entity/note CRUD, graph traversal, hybrid search, curation.
Documentation
use crate::engine_config::{ConfigError, KhiveConfig};

use super::*;

const DECLARATION: &str = r#"
[[credentials]]
name = "receipt-key"
kind = "signing_key"
provider = "env"
env_var = "KHIVE_S1_RECEIPT_KEY"
"#;

const RING: &str = r#"
[visibility_receipts]
[[visibility_receipts.keys]]
id = "current-1"
credential = "receipt-key"
encrypt = true
"#;

fn parse(text: &str) -> Result<KhiveConfig, CredentialError> {
    let mut config: KhiveConfig = toml::from_str(text).unwrap();
    config::read_tables(text, &mut config).map(|()| config)
}

fn key(id: &str, encrypt: bool) -> VisibilityReceiptKeyConfig {
    VisibilityReceiptKeyConfig {
        id: id.to_owned(),
        credential: "receipt-key".to_owned(),
        encrypt,
    }
}

#[test]
fn config_load_accepts_references_without_resolving_environment() {
    let root = tempfile::tempdir().unwrap();
    let path = root.path().join("config.toml");
    let text = format!("{DECLARATION}{RING}");
    std::fs::write(&path, &text).unwrap();
    let config = KhiveConfig::load(Some(&path)).unwrap().unwrap();
    assert_eq!(config.credentials.len(), 1);
    let ring = config.visibility_receipts.as_ref().unwrap();
    assert_eq!(ring.keys.len(), 1);
    assert!(ring.keys[0].encrypt);
    assert!(KhiveConfig::load_with_home_fallback(Some(&path), None)
        .unwrap()
        .is_some());
    assert!(
        KhiveConfig::load_with_home_fallback_and_source(Some(&path), None)
            .unwrap()
            .is_some()
    );

    let custom = DECLARATION.replace(
        "provider = \"env\"\nenv_var = \"KHIVE_S1_RECEIPT_KEY\"",
        "provider = \"host-vault\"",
    );
    let config = parse(&format!("{custom}{RING}")).unwrap();
    config.validate().unwrap();
    let registry = CredentialRegistry::new(config.credentials).unwrap();
    assert!(matches!(
        registry.resolve("receipt-key"),
        Err(CredentialError::UnknownProvider { .. })
    ));
}

#[test]
fn closed_credential_and_receipt_tables_reject_unknown_fields_at_load() {
    let root = tempfile::tempdir().unwrap();
    let path = root.path().join("config.toml");
    for text in [
        format!("{DECLARATION}secret = 'disallowed-inline-value'\n{RING}"),
        format!("{DECLARATION}env_vra = 'disallowed-inline-value'\n{RING}"),
        format!(
            "{DECLARATION}{}",
            RING.replace(
                "[visibility_receipts]\n",
                "[visibility_receipts]\nunknown = 'disallowed-inline-value'\n"
            )
        ),
        format!("{DECLARATION}{RING}unknown = 'disallowed-inline-value'\n"),
        format!("{DECLARATION}{RING}key_bytes = 'disallowed-inline-value'\n"),
    ] {
        std::fs::write(&path, text).unwrap();
        for error in [
            KhiveConfig::load(Some(&path)).unwrap_err(),
            KhiveConfig::load_with_home_fallback(Some(&path), None).unwrap_err(),
            KhiveConfig::load_with_home_fallback_and_source(Some(&path), None).unwrap_err(),
        ] {
            let shown = format!("{error} {error:?}");
            assert!(!shown.contains("disallowed-inline-value"), "{shown}");
            let ConfigError::InFile { source, .. } = &error else {
                panic!("{shown}")
            };
            let ConfigError::Credential(source) = &**source else {
                panic!("{shown}")
            };
            assert!(
                matches!(source, CredentialError::InvalidConfig { .. }),
                "{shown}"
            );
        }
    }
}

#[test]
fn receipt_ring_requires_one_encrypting_key_and_valid_unique_ids() {
    let config = parse(DECLARATION).unwrap();
    for keys in [
        vec![],
        vec![key("current", false)],
        vec![key("current", true), key("old", true)],
        vec![key("same", true), key("same", false)],
        vec![key("", true)],
        vec![key(&"a".repeat(65), true)],
        vec![key("../bad", true)],
        vec![key("unicode-é", true)],
    ] {
        let ring = VisibilityReceiptConfig { keys };
        assert!(ring.validate(&config.credentials).is_err());
        let with_ring = KhiveConfig {
            visibility_receipts: Some(ring),
            ..config.clone()
        };
        assert!(with_ring.validate().is_err());
    }
    for id in [
        "a".to_owned(),
        "a".repeat(64),
        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789._".to_owned(),
    ] {
        VisibilityReceiptConfig {
            keys: vec![key(&id, true), key("old-key", false)],
        }
        .validate(&config.credentials)
        .unwrap();
    }
}

#[test]
fn logical_credential_errors_reject_all_startup_load_paths() {
    let root = tempfile::tempdir().unwrap();
    let path = root.path().join("config.toml");
    let valid = format!("{DECLARATION}{RING}");
    for text in [
        valid.replace("encrypt = true", "encrypt = false"),
        format!("{valid}\n[[visibility_receipts.keys]]\nid='other'\ncredential='receipt-key'\nencrypt=true\n"),
        valid.replace("credential = \"receipt-key\"", "credential = \"missing\""),
        valid.replace("kind = \"signing_key\"", "kind = \"basic\""),
        valid.replace("id = \"current-1\"", "id = \"bad/key\""),
        format!("{DECLARATION}{DECLARATION}{RING}"),
        valid.replace("env_var = \"KHIVE_S1_RECEIPT_KEY\"", "env_var = \"\""),
        format!("{DECLARATION}header = 'X-Api-Key'\n{RING}"),
        valid.replace("kind = \"signing_key\"", "kind = \"header\""),
    ] {
        std::fs::write(&path, text).unwrap();
        assert!(KhiveConfig::load(Some(&path)).is_err());
        assert!(KhiveConfig::load_with_home_fallback(Some(&path), None).is_err());
        assert!(KhiveConfig::load_with_home_fallback_and_source(Some(&path), None).is_err());
    }
}

#[test]
fn declaration_kinds_fields_and_default_decrypt_only_are_checked() {
    for kind in ["header", "basic", "cookie_jar", "signing_key"] {
        let header = if kind == "header" {
            "header='X-Api-Key'\n"
        } else {
            ""
        };
        let text = format!("[[credentials]]\nname='token'\nkind='{kind}'\nprovider='env'\nenv_var='TOKEN'\n{header}");
        let config = parse(&text).unwrap();
        config.validate().unwrap();
    }
    for replacement in ["name = \"\"", "provider = \"\"", "env_var = \"BAD=NAME\""] {
        let field = replacement.split(' ').next().unwrap();
        let text = DECLARATION
            .lines()
            .map(|line| {
                if line.starts_with(&format!("{field} =")) {
                    replacement
                } else {
                    line
                }
            })
            .collect::<Vec<_>>()
            .join("\n");
        let config = parse(&text).unwrap();
        assert!(config.validate().is_err());
    }
    let default_key: VisibilityReceiptKeyConfig =
        toml::from_str("id='old'\ncredential='receipt-key'").unwrap();
    assert!(!default_key.encrypt);
    let bad_kind = DECLARATION.replace("signing_key", "password");
    assert!(parse(&bad_kind).is_err());
    let bad_header = DECLARATION.replace("signing_key", "header") + "header='X-Invalid Header'\n";
    assert!(parse(&bad_header).unwrap().validate().is_err());
}