inttegro 0.2.0

Official server-side Rust SDK for Inttegro
Documentation
use inttegro::{
    BalanceSnapshot, Client, CreateOrderRequest, CustomData, CustomDataPatch, Order,
    PurchaseIntent, RequestOptions,
};
use std::io::{Read, Write};
use std::net::TcpListener;

fn assert_send_sync<T: Send + Sync>() {}

#[test]
fn public_types_are_concurrency_safe() {
    assert_send_sync::<Client>();
    assert_send_sync::<Order>();
    assert_send_sync::<CreateOrderRequest>();
    let _ = RequestOptions::default();
}

#[test]
fn semantic_collections_control_custom_data_mutation() {
    let mut data = CustomData::new();
    data.insert("order", "first").unwrap();
    assert_eq!(data.get("order"), Some("first"));

    let oversized_key = "x".repeat(257);
    assert!(data.insert(oversized_key, "invalid").is_err());
    assert_eq!(data.len(), 1, "failed mutations must be rolled back");

    let mut patch = CustomDataPatch::new();
    patch.set("campaign", "winter").unwrap();
    patch.unset("legacy").unwrap();
    assert_eq!(
        serde_json::to_value(patch).unwrap(),
        serde_json::json!({"campaign": "winter", "legacy": null})
    );
}

#[test]
fn balance_snapshot_exposes_ghs_statically() {
    let balance: BalanceSnapshot = serde_json::from_value(serde_json::json!({
        "ghs": {
            "available": {"amount": 1000},
            "includes_transactions_before": "2026-09-09T12:00:00Z",
            "pending": {"amount": 200},
            "refund": {"amount": 50},
            "reserved": {"amount": 100}
        }
    }))
    .unwrap();

    assert_eq!(balance.ghs.available.amount, 1_000);
    assert_eq!(
        balance.ghs.includes_transactions_before.to_string(),
        "2026-09-09T12:00:00Z"
    );
    assert!(
        serde_json::from_value::<BalanceSnapshot>(serde_json::json!({
            "ghs": {
                "available": {"amount": 1000},
                "includes_transactions_before": "2026-09-09T12:00:00",
                "pending": {"amount": 200},
                "refund": {"amount": 50},
                "reserved": {"amount": 100}
            }
        }))
        .is_err()
    );
}

#[test]
fn purchase_intent_exposes_nested_response_types() {
    let intent: PurchaseIntent = serde_json::from_value(serde_json::json!({
        "activity": {"recent": [{
            "created_at": "2026-09-09T12:01:00Z",
            "id": "saleevt_123",
            "purchase_intent_id": "sale_123",
            "type": "viewed",
            "visitor": {"ip_address": "203.0.113.7"}
        }]},
        "allow_variants": false,
        "created_at": "2026-09-09T12:00:00Z",
        "id": "sale_123",
        "merchant": {"organization_name": "Tea House Ltd"},
        "product": {
            "active": true,
            "created_at": "2026-09-09T11:00:00Z",
            "dimensions": {"digital": {"bytes": 1024}},
            "id": "prod_123",
            "name": "Tea guide",
            "type": "digital"
        },
        "quantity": {"min": 1},
        "status": "active",
        "usage": {
            "order": {"created_at": "2026-09-09T12:02:00Z", "id": "or_123"},
            "single_use": true
        }
    }))
    .unwrap();

    assert_eq!(
        intent.activity.unwrap().recent.unwrap()[0]
            .visitor
            .as_ref()
            .unwrap()
            .ip_address
            .as_deref(),
        Some("203.0.113.7")
    );
    assert_eq!(
        intent.merchant.unwrap().organization_name.as_deref(),
        Some("Tea House Ltd")
    );
    assert_eq!(
        intent
            .product
            .unwrap()
            .dimensions
            .unwrap()
            .digital
            .unwrap()
            .bytes,
        Some(1_024.0)
    );
    assert_eq!(intent.usage.order.unwrap().id, "or_123");
}

#[tokio::test]
async fn unwraps_the_wire_envelope_into_a_domain_value() {
    let listener = TcpListener::bind("127.0.0.1:0").unwrap();
    let address = listener.local_addr().unwrap();
    let server = std::thread::spawn(move || {
        let (mut stream, _) = listener.accept().unwrap();
        let mut request = [0_u8; 4096];
        let _ = stream.read(&mut request).unwrap();
        let body = r#"{"app":{"id":"app_test","name":"Test","created_at":"2026-09-07T00:00:00Z"}}"#;
        write!(
            stream,
            "HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: {}
Connection: close

{}",
            body.len(),
            body
        )
        .unwrap();
    });
    let client = Client::builder("sk_test_example")
        .base_url(format!("http://{address}"))
        .build()
        .unwrap();
    let app = client.apps().lookup().await.unwrap();
    assert_eq!(app.id, "app_test");
    server.join().unwrap();
}