lenso-platform-core 0.1.0

Core runtime primitives for the Lenso backend framework.
Documentation
use platform_core::{
    CorrelationId, RemoteHttpProxyCallRecord, RequestContext, RequestId, TraceContext,
    apply_migrations, insert_remote_http_proxy_call, remote_proxy_call_story_event_id,
    story_events::http_request_story_event_id,
};
use serde_json::json;
use sqlx::Row;

mod support;
use support::{SequentialIdGenerator, TestDatabase};

#[tokio::test]
async fn remote_proxy_call_records_request_and_trace_context() {
    let Some(db) = TestDatabase::create().await else {
        return;
    };

    apply_migrations(&db.pool, platform_core::PLATFORM_MIGRATIONS)
        .await
        .expect("platform migrations should apply");

    let mut request_ctx = RequestContext::new(
        RequestId::new("req_remote_trace"),
        CorrelationId::new("corr_remote_trace"),
    );
    request_ctx.trace = TraceContext {
        trace_id: Some("00000000000000000000000000000011".to_owned()),
        span_id: Some("0000000000000011".to_owned()),
        baggage: Vec::new(),
    };
    request_ctx.causation_id = Some(http_request_story_event_id(&request_ctx));
    let record = RemoteHttpProxyCallRecord {
        module_name: "remote-crm".to_owned(),
        method: "GET".to_owned(),
        declared_path: "/contacts/{id}".to_owned(),
        remote_path: "/contacts/contact_1".to_owned(),
        capability: Some("remote_crm.contacts.read".to_owned()),
        display_name: Some("Fetch Contact".to_owned()),
        story_title: Some("Fetch Contact".to_owned()),
        remote_status: Some(200),
        duration_ms: 42,
        success: true,
        error_code: None,
        retryable: false,
        path_params: json!({ "id": "contact_1" }),
        error_details: json!({ "ignored": true }),
    };

    let id = insert_remote_http_proxy_call(
        &db.pool,
        &SequentialIdGenerator::default(),
        &request_ctx,
        record,
    )
    .await
    .expect("remote proxy call should insert");

    let row = sqlx::query(
        r#"
        select
            id,
            request_id,
            correlation_id,
            trace_id,
            span_id,
            path_params,
            error_details
        from platform.remote_http_proxy_calls
        where id = $1
        "#,
    )
    .bind(&id)
    .fetch_one(&db.pool)
    .await
    .expect("remote proxy call should query");

    assert_eq!(row.try_get::<String, _>("id").expect("id"), "rproxy_1");
    assert_eq!(
        row.try_get::<String, _>("request_id").expect("request_id"),
        "req_remote_trace"
    );
    assert_eq!(
        row.try_get::<String, _>("correlation_id")
            .expect("correlation_id"),
        "corr_remote_trace"
    );
    assert_eq!(
        row.try_get::<Option<String>, _>("trace_id")
            .expect("trace_id")
            .as_deref(),
        Some("00000000000000000000000000000011")
    );
    assert_eq!(
        row.try_get::<Option<String>, _>("span_id")
            .expect("span_id")
            .as_deref(),
        Some("0000000000000011")
    );
    assert_eq!(
        row.try_get::<serde_json::Value, _>("path_params")
            .expect("path_params"),
        json!({ "id": "contact_1" })
    );
    assert_eq!(
        row.try_get::<serde_json::Value, _>("error_details")
            .expect("error_details"),
        json!([])
    );

    let story_row = sqlx::query(
        r#"
        select
            id,
            source_type,
            source_id,
            node_type,
            name,
            status,
            service,
            correlation_id,
            causation_id,
            duration_ms,
            error,
            metadata,
            trace_id,
            span_id
        from platform.story_events
        where source_type = 'remote_proxy_call'
            and source_id = $1
        "#,
    )
    .bind(&id)
    .fetch_one(&db.pool)
    .await
    .expect("remote proxy story node should query");

    assert_eq!(
        story_row.try_get::<String, _>("id").expect("story id"),
        remote_proxy_call_story_event_id("rproxy_1")
    );
    assert_eq!(
        story_row
            .try_get::<String, _>("source_type")
            .expect("source_type"),
        "remote_proxy_call"
    );
    assert_eq!(
        story_row
            .try_get::<String, _>("source_id")
            .expect("source_id"),
        "rproxy_1"
    );
    assert_eq!(
        story_row
            .try_get::<String, _>("node_type")
            .expect("node_type"),
        "remote_proxy_call"
    );
    assert_eq!(
        story_row.try_get::<String, _>("name").expect("name"),
        "Fetch Contact"
    );
    assert_eq!(
        story_row.try_get::<String, _>("status").expect("status"),
        "completed"
    );
    assert_eq!(
        story_row.try_get::<String, _>("service").expect("service"),
        "remote-crm"
    );
    assert_eq!(
        story_row
            .try_get::<String, _>("correlation_id")
            .expect("story correlation_id"),
        "corr_remote_trace"
    );
    assert_eq!(
        story_row
            .try_get::<Option<String>, _>("causation_id")
            .expect("causation_id")
            .as_deref(),
        Some("httpreq_req_remote_trace")
    );
    assert_eq!(
        story_row
            .try_get::<i64, _>("duration_ms")
            .expect("duration_ms"),
        42
    );
    assert_eq!(
        story_row
            .try_get::<Option<String>, _>("error")
            .expect("error"),
        None
    );
    assert_eq!(
        story_row
            .try_get::<Option<String>, _>("trace_id")
            .expect("story trace_id")
            .as_deref(),
        Some("00000000000000000000000000000011")
    );
    assert_eq!(
        story_row
            .try_get::<Option<String>, _>("span_id")
            .expect("story span_id")
            .as_deref(),
        Some("0000000000000011")
    );
    let metadata = story_row
        .try_get::<serde_json::Value, _>("metadata")
        .expect("metadata");
    assert_eq!(metadata["remote_proxy_call_id"], "rproxy_1");
    assert_eq!(metadata["module_name"], "remote-crm");
    assert_eq!(metadata["method"], "GET");
    assert_eq!(metadata["declared_path"], "/contacts/{id}");
    assert_eq!(metadata["display_name"], "Fetch Contact");
    assert_eq!(metadata["story_title"], "Fetch Contact");
    assert_eq!(metadata["remote_path"], "/contacts/contact_1");
    assert_eq!(metadata["remote_status"], 200);
    assert_eq!(metadata["duration_ms"], 42);
    assert_eq!(metadata["request_id"], "req_remote_trace");
    assert_eq!(metadata["trace_id"], "00000000000000000000000000000011");
    assert_eq!(metadata["span_id"], "0000000000000011");
    assert_eq!(metadata["path_params"]["id"], "contact_1");

    let http_story_count = sqlx::query_scalar::<_, i64>(
        r#"
        select count(*)::bigint
        from platform.story_events
        where correlation_id = $1
            and node_type = 'http_request'
        "#,
    )
    .bind("corr_remote_trace")
    .fetch_one(&db.pool)
    .await
    .expect("HTTP story count should query");
    assert_eq!(http_story_count, 0);

    db.cleanup().await;
}