greentic-types 0.5.2

Shared primitives for Greentic: TenantCtx, InvocationEnvelope, NodeError, ids.
Documentation
#![cfg(feature = "serde")]

use std::collections::BTreeMap;

use greentic_types::{ProviderDecl, ProviderExtensionInline, ProviderManifest, ProviderRuntimeRef};

fn roundtrip_json<T>(value: &T) -> T
where
    T: serde::Serialize + serde::de::DeserializeOwned + PartialEq + core::fmt::Debug,
{
    let json = serde_json::to_string_pretty(value).expect("serialize json");
    let decoded: T = serde_json::from_str(&json).expect("json roundtrip");
    assert_eq!(&decoded, value);
    decoded
}

#[test]
fn provider_manifest_roundtrips_and_skips_optional_state() {
    let manifest = ProviderManifest {
        provider_type: "vendor.logging".into(),
        capabilities: vec!["events".into(), "metrics".into()],
        ops: vec!["start".into(), "stop".into()],
        config_schema_ref: Some("schemas/logging-config.json".into()),
        state_schema_ref: None,
    };

    let value = serde_json::to_value(&manifest).expect("serialize");
    assert!(
        value.get("state_schema_ref").is_none(),
        "optional state schema should be omitted when None"
    );

    let roundtrip = roundtrip_json(&manifest);
    assert_eq!(roundtrip, manifest);
}

#[test]
fn provider_decl_roundtrips_and_omits_empty_fields() {
    let runtime = ProviderRuntimeRef {
        component_ref: "vendor.provider.runtime".into(),
        export: "greentic_provider".into(),
        world: "greentic:provider/runtime".into(),
    };

    let decl = ProviderDecl {
        provider_type: "vendor.db".into(),
        capabilities: vec!["database".into(), "backup".into()],
        ops: vec!["connect".into(), "query".into(), "close".into()],
        config_schema_ref: "schemas/db-config.json".into(),
        state_schema_ref: Some("schemas/db-state.json".into()),
        runtime,
        docs_ref: Some("docs/providers/db.md".into()),
    };

    let value = serde_json::to_value(&decl).expect("serialize");
    assert_eq!(
        value.get("docs_ref").and_then(|v| v.as_str()),
        Some("docs/providers/db.md")
    );

    let roundtrip = roundtrip_json(&decl);
    assert_eq!(roundtrip, decl);

    let mut stripped = decl.clone();
    stripped.capabilities.clear();
    stripped.ops.clear();
    stripped.state_schema_ref = None;
    stripped.docs_ref = None;
    let stripped_value = serde_json::to_value(&stripped).expect("serialize stripped");
    assert!(stripped_value.get("capabilities").is_none());
    assert!(stripped_value.get("ops").is_none());
    assert!(stripped_value.get("state_schema_ref").is_none());
    assert!(stripped_value.get("docs_ref").is_none());
}

#[test]
fn provider_extension_inline_roundtrips() {
    let providers = vec![
        ProviderDecl {
            provider_type: "vendor.search".into(),
            capabilities: vec!["index".into()],
            ops: vec!["ingest".into(), "query".into()],
            config_schema_ref: "schemas/search-config.json".into(),
            state_schema_ref: None,
            runtime: ProviderRuntimeRef {
                component_ref: "vendor.search.runtime".into(),
                export: "greentic_provider".into(),
                world: "greentic:provider/runtime".into(),
            },
            docs_ref: Some("docs/providers/search.md".into()),
        },
        ProviderDecl {
            provider_type: "vendor.cache".into(),
            capabilities: Vec::new(),
            ops: vec!["set".into(), "get".into()],
            config_schema_ref: "schemas/cache-config.json".into(),
            state_schema_ref: Some("schemas/cache-state.json".into()),
            runtime: ProviderRuntimeRef {
                component_ref: "vendor.cache.runtime".into(),
                export: "greentic_provider".into(),
                world: "greentic:provider/runtime".into(),
            },
            docs_ref: None,
        },
    ];

    let inline = ProviderExtensionInline {
        providers,
        additional_fields: BTreeMap::new(),
    };

    let roundtrip = roundtrip_json(&inline);
    assert_eq!(roundtrip.providers.len(), 2);
    assert_eq!(roundtrip.providers[0].provider_type, "vendor.search");
    assert_eq!(roundtrip.providers[1].provider_type, "vendor.cache");
}