cleanlib-client 0.1.14

HTTP client SDK for the CleanLibrary verdict API — VerdictEnvelopeV1 types, derive_status logic, transport, config, and risk-acceptance YAML emitter shared between cleanlib-cli and other CleanLibrary consumers.
Documentation
//! CLEANLIB-518 (§2) — null verdict label tolerance parity.
//!
//! Before: a `verdict: null` (or `verdict_label: null`) wire rejected the whole
//! parse in Rust (`invalid type: null, expected a string` — Test 743061), while
//! sdk-py/js/go degraded tolerantly to WARN + VERDICT_NOT_YET_ASSESSED. This
//! brings Rust to parity: a null label parses to the empty label, which the
//! status/reason mapper resolves to the fail-closed default.

use cleanlib_client::types::Verdict;
use cleanlib_client::verdict_to_envelope::verdict_to_envelope_v1;

#[test]
fn null_verdict_label_parses_and_fails_closed_to_warn() {
    // `verdict: null` — must NOT reject the parse anymore.
    let wire = r#"{
        "verdict_id": "v-null",
        "verdict": null,
        "source": "INSUFFICIENT_DATA",
        "severity": "NONE",
        "reasoning": "label absent on wire"
    }"#;

    let v: Verdict = serde_json::from_str(wire)
        .expect("CLEANLIB-518 §2: a null verdict label must parse (tolerant), not throw");
    assert_eq!(v.verdict, "", "null label degrades to the empty label");

    // The empty label resolves to the fail-closed default — parity with the
    // other three SDKs.
    let env = verdict_to_envelope_v1(&v);
    assert_eq!(env.status, "WARN");
    assert_eq!(env.reason_code, "VERDICT_NOT_YET_ASSESSED");
}

#[test]
fn present_string_and_object_labels_still_parse() {
    // Regression guard: the null tolerance must not disturb the existing
    // string-or-object (envelope-v2 Path A) acceptance.
    let flat = r#"{"verdict_id":"v","verdict":"VECTOR_VERDICT","source":"VECTOR_VERDICT","severity":"HIGH","reasoning":"x"}"#;
    assert_eq!(serde_json::from_str::<Verdict>(flat).unwrap().verdict, "VECTOR_VERDICT");

    let nested = r#"{"verdict_id":"v","verdict":{"type":"VECTOR_VERDICT","status":"DENY"},"source":"VECTOR_VERDICT","severity":"HIGH","reasoning":"x"}"#;
    assert_eq!(serde_json::from_str::<Verdict>(nested).unwrap().verdict, "VECTOR_VERDICT");
}