saddle-core 0.3.26

Shared contracts for Saddle components
Documentation
use super::*;
use crate::{ApplicationId, ModuleId, OperationId, RpcCorrelationId, ServiceId, SpanId, TraceId};

fn call(rpc: &str, span: u64) -> CallContext {
    CallContext::new(
        ApplicationId::from("app"),
        ModuleId::from("module"),
        ServiceId::from("svc"),
        OperationId::from("operation"),
        TraceId::from_u128(1),
        SpanId::from_u64(span),
    )
    .with_rpc_correlation_id(RpcCorrelationId::new(rpc))
}
fn group(request: &str) -> RequestIdentityGroup {
    RequestIdentityGroup::from_validated(
        &call("0", 1),
        request,
        "route",
        1,
        ContextFact::Present(ContextLabel::checked("local").unwrap()),
    )
    .unwrap()
}
fn owner() -> RequestRootPublisher {
    RequestRootPublisher::create(
        ContextLabel::checked("app").unwrap(),
        ContextFact::NotEstablished,
    )
    .unwrap()
}

#[test]
fn frozen_u2_publication_is_atomic_and_old_view_stays_early() {
    let mut root = owner();
    let read = root.reference();
    let early = read.view(RequestLocalFacts::new(RequestViewPhase::Reading));
    let before = serde_json::to_value(&early).unwrap();
    assert_eq!(before["trace_id"]["state"], "not_established");
    assert!(root.publish(group("request-a")).is_ok());
    assert!(root.publish(group("request-a")).is_ok());
    assert_eq!(serde_json::to_value(&early).unwrap(), before);
    let current = early.refresh(&read).unwrap();
    let bound = serde_json::to_value(&current).unwrap();
    let rejected = root.publish(group("request-b"));
    assert!(matches!(rejected, Err((ContextConflict::IdentityGroup, _))));
    assert_eq!(serde_json::to_value(&current).unwrap(), bound);
    assert_eq!(bound["request"]["value"], "request-a");
    assert!(early.refresh(&owner().reference()).is_err());
}

#[test]
fn frozen_u1_u3_single_root_shared_local_and_child_are_immutable() {
    let mut root = owner();
    assert!(root.publish(group("request-a")).is_ok());
    let read = root.reference();
    let first = read.view(
        RequestLocalFacts::new(RequestViewPhase::Database)
            .with_task(ContextFact::Present(7))
            .with_db_operation(RegisteredContextOperation::checked("db.first").unwrap()),
    );
    let held = first.clone();
    assert!(first.same_view(&held));
    let second = first.with_db_operation(RegisteredContextOperation::checked("db.second").unwrap());
    assert!(first.same_request(&second));
    assert!(!first.same_view(&second));
    assert_eq!(
        serde_json::to_value(&held).unwrap()["db_operation"]["value"],
        "db.first"
    );
    let child = first
        .child(&call("0.1", 2), "request-a", "child", 1)
        .unwrap();
    assert_eq!(serde_json::to_value(&child).unwrap()["task"]["value"], 7);
    assert_eq!(serde_json::to_value(&held).unwrap()["rpc_id"]["value"], "0");
    assert!(
        first
            .child(&call("0.1.1", 2), "request-a", "child", 1)
            .is_err()
    );
    assert!(first.child(&call("0.1", 2), "foreign", "child", 1).is_err());
    assert!(
        first
            .child(&call("0.1", 1), "request-a", "child", 1)
            .is_err()
    );
    let id = root.root.local;
    drop((root, read, first, held, second, child));
    let events = EVENTS.lock().unwrap();
    let count = |kind| {
        events
            .iter()
            .filter(|(r, e)| *r == id && *e == kind)
            .count()
    };
    assert_eq!(count("root_create"), 1);
    assert_eq!(count("root_drop"), 1);
    assert_eq!(count("view_create"), 3);
    assert_eq!(count("view_drop"), 3);
    let local_events = LOCAL_EVENTS.lock().unwrap();
    let created: Vec<_> = local_events
        .iter()
        .filter(|(_, r, _, e)| *r == id && *e == "create")
        .map(|(id, _, kind, _)| (*id, *kind))
        .collect();
    assert_eq!(created.len(), 4); // three views and one actual child payload
    for (object, kind) in created {
        assert_eq!(
            local_events
                .iter()
                .filter(|(o, r, k, e)| *o == object && *r == id && *k == kind && *e == "destroy")
                .count(),
            1
        );
    }
    let ordered = ORDERED_EVENTS.lock().unwrap();
    let root_end = ordered
        .iter()
        .find(|(_, r, _, _, e)| *r == id && *e == "root_drop")
        .unwrap()
        .0;
    assert!(
        ordered
            .iter()
            .filter(|(_, r, _, k, e)| *r == id && *k == "view" && *e == "destroy")
            .all(|(s, _, _, _, _)| *s < root_end)
    );
}

#[test]
fn frozen_k3_unread_bytes_do_not_retain_objects() {
    let mut root = owner();
    assert!(root.publish(group("request-a")).is_ok());
    let reference = root.reference();
    let view = reference.view(RequestLocalFacts::new(RequestViewPhase::Handler));
    let (tx, rx) = std::sync::mpsc::sync_channel(1);
    tx.try_send(serde_json::to_vec(&view).unwrap()).unwrap();
    let id = root.root.local;
    drop((root, reference, view));
    assert_eq!(
        EVENTS
            .lock()
            .unwrap()
            .iter()
            .filter(|(r, e)| *r == id && *e == "root_drop")
            .count(),
        1
    );
    let json: serde_json::Value = serde_json::from_slice(&rx.recv().unwrap()).unwrap();
    assert_eq!(json["request"]["value"], "request-a");
}

#[test]
fn frozen_u3_scope_pair_foreign_and_next_operation_keep_original() {
    let root = owner();
    let view = root
        .reference()
        .view(RequestLocalFacts::new(RequestViewPhase::Database));
    let (_, issuer) = crate::DbPhysicalDispositionIssuer::issue().into_startup_and_request_issuer();
    let (request, execution) = issuer.issue_request().unwrap();
    let checked = request
        .project_diagnostic_context(&execution, view.clone())
        .ok()
        .unwrap();
    let scoped = view.in_db_scope(&checked).unwrap();
    let before = serde_json::to_value(&scoped).unwrap();
    assert_eq!(before["scope"]["state"], "present");
    let foreign = owner()
        .reference()
        .view(RequestLocalFacts::new(RequestViewPhase::Database));
    assert!(matches!(
        foreign.in_db_scope(&checked),
        Err(ContextConflict::ForeignRoot)
    ));
    let second = scoped.with_db_operation(RegisteredContextOperation::checked("next").unwrap());
    assert_eq!(
        serde_json::to_value(&second).unwrap()["scope"],
        before["scope"]
    );
    assert_eq!(serde_json::to_value(&scoped).unwrap(), before);
}

#[test]
fn frozen_o2_bounded_safe_inputs_and_layout_are_explicit() {
    assert!(ContextIdentity::checked(&"a".repeat(256)).is_ok());
    assert!(ContextIdentity::checked(&"a".repeat(257)).is_err());
    assert!(ContextIdentity::checked("trace\n").is_err());
    assert!(ContextLabel::checked("https://user:password@example").is_err());
    for (payload, shared) in request_context_layouts() {
        assert!(shared.size() >= payload.size() + 2 * size_of::<usize>());
        assert!(shared.align() >= payload.align());
    }
}