lxmf-sdk 0.7.0

High-level Rust SDK for LXMF clients and RPC-backed LXMF workflows.
Documentation
use super::*;

#[test]
fn send_batch_uses_zmq_sdk_method_and_decodes_ordered_results() {
    let command_endpoint = unused_loopback_endpoint();
    let response_endpoint = unused_loopback_endpoint();
    let captured = Arc::new(Mutex::new(None));
    let server = spawn_single_response_zmq_server(
        command_endpoint.clone(),
        json!({
            "batch_id": "batch-typed-1",
            "accepted_count": 1,
            "rejected_count": 1,
            "results": [
                {
                    "id": "batch-msg-1",
                    "message_id": "batch-msg-1",
                    "accepted": true
                },
                {
                    "id": "batch-msg-2",
                    "accepted": false,
                    "error": {
                        "code": "SDK_QUEUE_FULL",
                        "message": "outbound delivery queue full",
                        "category": "Runtime",
                        "retryable": true
                    }
                }
            ]
        }),
        Arc::clone(&captured),
    );
    let mut config = ZmqPipelineBackendConfig::local_tcp(command_endpoint, response_endpoint);
    config.request_timeout = std::time::Duration::from_secs(2);
    let client = ZmqPipelineBackendClient::new(config).expect("zmq client");

    let result = client
        .send_batch(crate::BatchSendRequest {
            batch_id: "batch-typed-1".to_string(),
            source: "source-destination".to_string(),
            messages: vec![
                crate::BatchSendItem::new(
                    "batch-msg-1",
                    "peer-a",
                    json!({
                        "title": "hello a",
                        "body": "payload a https://example.invalid/a",
                        "FIELD_THREAD": "thread-a"
                    }),
                )
                .with_delivery_method("direct")
                .with_idempotency_key("batch-msg-1-send-once")
                .with_ttl_ms(30_000)
                .with_correlation_id("batch-msg-1-corr")
                .with_extension("burst_slot", json!(0))
                .with_include_ticket(false),
                crate::BatchSendItem::new(
                    "batch-msg-2",
                    "peer-b",
                    json!({
                        "title": "hello b",
                        "content": "payload b",
                        "FIELD_GROUP": "group-b"
                    }),
                )
                .with_try_propagation_on_fail(true)
                .with_stamp_cost(12),
            ],
        })
        .expect("batch send");

    assert_eq!(result.batch_id, "batch-typed-1");
    assert_eq!(result.accepted_count, 1);
    assert_eq!(result.rejected_count, 1);
    assert_eq!(result.results[0].message_id.as_deref(), Some("batch-msg-1"));
    assert!(result.results[0].accepted);
    assert!(!result.results[1].accepted);
    assert_eq!(result.results[1].error.as_ref().expect("error").code, "SDK_QUEUE_FULL");
    assert!(result.results[1].error.as_ref().expect("error").retryable);

    let captured = captured.lock().expect("captured request");
    let request = captured.as_ref().expect("zmq request");
    assert_eq!(request.method, "sdk_send_batch_v2");
    let params = request.params.as_ref().expect("params");
    assert_eq!(params["batch_id"], json!("batch-typed-1"));
    assert_eq!(params["source"], json!("source-destination"));
    assert_eq!(params["messages"][0]["id"], json!("batch-msg-1"));
    assert_eq!(params["messages"][0]["destination"], json!("peer-a"));
    assert_eq!(params["messages"][0]["title"], json!("hello a"));
    assert_eq!(params["messages"][0]["content"], json!("payload a https://example.invalid/a"));
    assert_eq!(params["messages"][0]["fields"]["FIELD_THREAD"], json!("thread-a"));
    assert_eq!(params["messages"][0]["fields"].get("body"), None);
    assert_eq!(params["messages"][0]["fields"].get("title"), None);
    assert_eq!(params["messages"][0]["fields"].get("_sdk"), None);
    assert_eq!(params["messages"][0]["method"], json!("direct"));
    assert_eq!(params["messages"][0]["include_ticket"], json!(false));
    assert_eq!(params["messages"][0]["idempotency_key"], json!("batch-msg-1-send-once"));
    assert_eq!(params["messages"][0]["ttl_ms"], json!(30_000));
    assert_eq!(params["messages"][0]["correlation_id"], json!("batch-msg-1-corr"));
    assert_eq!(params["messages"][0]["extensions"]["burst_slot"], json!(0));
    assert_eq!(params["messages"][1]["fields"]["FIELD_GROUP"], json!("group-b"));
    assert_eq!(params["messages"][1]["fields"].get("content"), None);
    assert_eq!(params["messages"][1]["fields"].get("title"), None);
    assert_eq!(params["messages"][1]["try_propagation_on_fail"], json!(true));
    assert_eq!(params["messages"][1]["stamp_cost"], json!(12));
    server.join().expect("server joined");
}