bcode_server 0.0.1-alpha.0

Local bcode daemon runtime
use super::*;
use bcode_model::{ProviderErrorCategory, ProviderErrorSource};

#[test]
fn structured_provider_cause_survives_collection_and_response_decoding() {
    let error = ProviderError {
        code: "refresh_blocked".into(),
        category: ProviderErrorCategory::Auth,
        message: "Refresh unavailable; access_token=private-token".into(),
        retryable: false,
        provider_message: Some("private provider body".into()),
        failure: None,
        request_id: Some("request-17".into()),
        diagnostic_context: Box::new(std::collections::BTreeMap::from([(
            "http_status".into(),
            "401".into(),
        )])),
        sources: Box::new(vec![ProviderErrorSource {
            source: "auth-host".into(),
            code: Some("custody_unavailable".into()),
            message: Some("host denied refresh".into()),
        }]),
        retry: Some(Box::new(bcode_model::ProviderRetryHint {
            retry_after_ms: Some(1200),
            retry_at_unix: None,
            source: Some("retry-after".into()),
        })),
    };
    let event = bcode_model::ProviderTurnEvent::Error {
        error: error.clone(),
    };
    let collected = super::super::Collected::default()
        .apply(event.clone())
        .unwrap_err();
    assert!(matches!(&collected, GenerationError::Provider(value) if **value == error));
    let mut response = bcode_plugin_sdk::ServiceResponse::error("startup", "startup failed");
    response.payload = serde_json::to_vec(&event).unwrap();
    let decoded = super::super::decode_response::<serde_json::Value>(response).unwrap_err();
    assert!(matches!(&decoded, GenerationError::Provider(value) if **value == error));
    let diagnostic = decoded.diagnostic();
    assert_eq!(diagnostic["error"]["code"], "refresh_blocked");
    assert_eq!(diagnostic["error"]["category"], "auth");
    assert_eq!(diagnostic["error"]["retry"]["retry_after_ms"], 1200);
    assert_eq!(diagnostic["error"]["request_id"], "request-17");
    assert_eq!(
        diagnostic["error"]["diagnostic_context"]["http_status"],
        "401"
    );
    assert_eq!(
        diagnostic["error"]["sources"][0]["code"],
        "custody_unavailable"
    );
    assert!(!diagnostic.to_string().contains("private-token"));
    assert!(!diagnostic.to_string().contains("private provider body"));
    let combined = decoded.secondary(GenerationError::from("usage persistence failed"));
    assert_eq!(
        combined.diagnostic()["primary"]["error"]["code"],
        "refresh_blocked"
    );
    assert_eq!(
        combined.diagnostic()["secondary"]["message"],
        "usage persistence failed"
    );
}

#[test]
fn service_response_failure_is_not_treated_as_success_or_empty_output() {
    let response = bcode_plugin_sdk::ServiceResponse::error(
        "auth_refresh_unavailable",
        "durable refresh coordination unavailable",
    );
    let error = super::super::decode_response::<serde_json::Value>(response).unwrap_err();
    assert_eq!(error.diagnostic()["code"], "auth_refresh_unavailable");
    assert_eq!(
        error.diagnostic()["message"],
        "durable refresh coordination unavailable"
    );
}