ironflow-sdk 0.2.0

Type-safe Rust SDK for the Ironflow workflow engine API
Documentation
//! Deserialization tests for progenitor-generated types against JSON fixtures.

use ironflow_sdk::client::StatsHistoryParams;
use ironflow_sdk::types::{
    CreateRunRequest, CreatedByKind, MeResponse, RunResponse, RunStatus, ScopeEntry,
    SecretResponse, StatsHistoryBucketResponse, StatsResponse, WorkflowSummary,
};

#[test]
fn deserialize_run_response() {
    let json = r#"{
        "id": "01936f5a-0000-7000-8000-000000000001",
        "workflow_name": "deploy",
        "status": "completed",
        "trigger": {"kind": "manual"},
        "created_at": "2026-01-01T00:00:00Z",
        "updated_at": "2026-01-01T00:01:00Z",
        "cost_usd": 0.05,
        "duration_ms": 12345,
        "handler_version": "1.0.0",
        "labels": {"env": "prod"},
        "error": null,
        "max_retries": 0,
        "lease_recoveries": 3,
        "retry_count": 0,
        "payload": {},
        "scheduled_at": null,
        "completed_at": null,
        "started_at": null,
        "created_by": {
            "kind": "user",
            "id": "01936f5a-0000-7000-8000-0000000000aa",
            "label": "alice"
        }
    }"#;
    let run: RunResponse = serde_json::from_str(json).unwrap();
    assert_eq!(run.workflow_name, "deploy");
    assert_eq!(run.duration_ms, 12345);
    assert_eq!(run.lease_recoveries, 3);
    assert_eq!(run.created_by.kind, CreatedByKind::User);
    assert_eq!(run.created_by.label, "alice");
    assert!(run.created_by.id.is_some());
}

#[test]
fn deserialize_run_response_with_api_key_author() {
    let json = r#"{
        "id": "01936f5a-0000-7000-8000-000000000001",
        "workflow_name": "deploy",
        "status": "completed",
        "trigger": {"kind": "api"},
        "created_at": "2026-01-01T00:00:00Z",
        "updated_at": "2026-01-01T00:01:00Z",
        "cost_usd": 0.05,
        "duration_ms": 1,
        "handler_version": null,
        "labels": {},
        "error": null,
        "max_retries": 0,
        "lease_recoveries": 0,
        "retry_count": 0,
        "payload": {},
        "scheduled_at": null,
        "completed_at": null,
        "started_at": null,
        "created_by": {
            "kind": "api_key",
            "id": "01936f5a-0000-7000-8000-0000000000bb",
            "label": "ci-deploy (alice)"
        }
    }"#;
    let run: RunResponse = serde_json::from_str(json).unwrap();
    assert_eq!(run.created_by.kind, CreatedByKind::ApiKey);
    assert_eq!(run.created_by.label, "ci-deploy (alice)");
}

#[test]
fn deserialize_run_response_with_nullable_fields() {
    let json = r#"{
        "id": "01936f5a-0000-7000-8000-000000000001",
        "workflow_name": "test",
        "status": "failed",
        "trigger": {"kind": "webhook", "path": "/hooks/deploy"},
        "created_at": "2026-01-01T00:00:00Z",
        "updated_at": "2026-01-01T00:01:00Z",
        "cost_usd": 0.0,
        "duration_ms": 0,
        "handler_version": null,
        "labels": {},
        "error": "step 2 failed",
        "max_retries": 0,
        "lease_recoveries": 0,
        "retry_count": 0,
        "payload": {"key": "value"},
        "scheduled_at": null,
        "completed_at": null,
        "started_at": null,
        "created_by": {
            "kind": "system",
            "id": null,
            "label": "/hooks/deploy"
        }
    }"#;
    let run: RunResponse = serde_json::from_str(json).unwrap();
    assert_eq!(run.status, RunStatus::Failed);
    assert!(run.handler_version.is_none());
    assert_eq!(run.error.as_deref(), Some("step 2 failed"));
    assert_eq!(run.created_by.kind, CreatedByKind::System);
    assert!(run.created_by.id.is_none());
    assert_eq!(run.created_by.label, "/hooks/deploy");
}

#[test]
fn deserialize_stats_response() {
    let json = r#"{
        "total_runs": 100,
        "completed_runs": 80,
        "failed_runs": 10,
        "cancelled_runs": 3,
        "active_runs": 7,
        "awaiting_approval_runs": 2,
        "total_cost_usd": 12.50,
        "total_duration_ms": 500000,
        "success_rate_percent": 88.89
    }"#;
    let stats: StatsResponse = serde_json::from_str(json).unwrap();
    assert_eq!(stats.total_runs, 100);
    assert_eq!(stats.completed_runs, 80);
    assert_eq!(stats.awaiting_approval_runs, 2);
}

#[test]
fn deserialize_stats_history_bucket() {
    let json = r#"{
        "time": "2026-09-21T00:00:00Z",
        "completed": 4,
        "warning": 1,
        "failed": 0,
        "cancelled": 2,
        "pending": 3,
        "running": 5,
        "retrying": 1,
        "awaiting_approval": 1,
        "sleeping": 1,
        "paused": 0,
        "success_rate_percent": null,
        "avg_duration_ms": 1200,
        "p95_duration_ms": 3400,
        "total_cost_usd": 0.42
    }"#;
    let bucket: StatsHistoryBucketResponse = serde_json::from_str(json).unwrap();
    assert_eq!(bucket.completed, 4);
    assert_eq!(bucket.warning, 1);
    assert_eq!(bucket.pending, 3);
    assert_eq!(bucket.running, 5);
    assert_eq!(bucket.retrying, 1);
    assert_eq!(bucket.awaiting_approval, 1);
    assert_eq!(bucket.sleeping, 1);
    assert!(bucket.success_rate_percent.is_none());

    let json = r#"{
        "time": "2026-09-21T00:00:00Z",
        "completed": 1,
        "warning": 0,
        "failed": 1,
        "cancelled": 0,
        "pending": 0,
        "running": 0,
        "retrying": 0,
        "awaiting_approval": 0,
        "sleeping": 0,
        "paused": 0,
        "success_rate_percent": 50.0,
        "avg_duration_ms": 0,
        "p95_duration_ms": 0,
        "total_cost_usd": 0
    }"#;
    let bucket: StatsHistoryBucketResponse = serde_json::from_str(json).unwrap();
    assert_eq!(bucket.success_rate_percent, Some(50.0));
}

#[test]
fn deserialize_workflow_summary() {
    let json = r#"{
        "name": "deploy",
        "category": null,
        "version": null
    }"#;
    let wf: WorkflowSummary = serde_json::from_str(json).unwrap();
    assert_eq!(wf.name, "deploy");
}

#[test]
fn deserialize_me_response() {
    let json = r#"{
        "user_id": "01936f5a-0000-7000-8000-000000000001",
        "username": "alice",
        "email": "alice@example.com",
        "is_admin": true,
        "created_at": "2024-01-01T00:00:00Z"
    }"#;
    let me: MeResponse = serde_json::from_str(json).unwrap();
    assert_eq!(me.username, "alice");
    assert!(me.is_admin);
}

#[test]
fn deserialize_create_run_request() {
    let json = r#"{
        "workflow": "deploy",
        "payload": {"env": "staging"},
        "labels": {"team": "backend"}
    }"#;
    let req: CreateRunRequest = serde_json::from_str(json).unwrap();
    assert_eq!(req.workflow, "deploy");
}

#[test]
fn deserialize_secret_response() {
    let json = r#"{
        "id": "01936f5a-0000-7000-8000-000000000001",
        "key": "API_KEY",
        "created_at": "2026-01-01T00:00:00Z",
        "updated_at": "2026-01-01T00:00:00Z"
    }"#;
    let secret: SecretResponse = serde_json::from_str(json).unwrap();
    assert_eq!(secret.key, "API_KEY");
}

#[test]
fn deserialize_scope_entry() {
    let json = r#"{
        "value": "runs_read",
        "label": "Runs Read",
        "description": "Read run data"
    }"#;
    let scope: ScopeEntry = serde_json::from_str(json).unwrap();
    assert_eq!(scope.value, "runs_read");
    assert_eq!(scope.label, "Runs Read");
}

#[test]
fn deserialize_run_status_enum() {
    let variants = [
        ("\"pending\"", RunStatus::Pending),
        ("\"running\"", RunStatus::Running),
        ("\"completed\"", RunStatus::Completed),
        ("\"failed\"", RunStatus::Failed),
        ("\"cancelled\"", RunStatus::Cancelled),
    ];
    for (json, expected) in variants {
        let status: RunStatus = serde_json::from_str(json).unwrap();
        assert_eq!(status, expected);
    }
}

#[test]
fn stats_history_params_serialize_concurrency_group() {
    let params = StatsHistoryParams {
        concurrency_group: Some("g"),
        ..Default::default()
    };
    let value = serde_json::to_value(&params).unwrap();
    assert_eq!(value["concurrency_group"], "g");
}

#[test]
fn stats_history_params_omit_absent_concurrency_group() {
    let value = serde_json::to_value(StatsHistoryParams::default()).unwrap();
    assert!(value.get("concurrency_group").is_none());
}