hara-native 0.1.13

HAL-free native host runtime and package launcher for Hara
Documentation
use std::collections::BTreeSet;

use crate::instrumentation::{
    Capability, EventDelivery, EventKind, InstrumentFilter, InstrumentMode, InstrumentRegistration,
    ProjectionRequest, RuntimeBackend, TargetKind,
};
use crate::live_session::{LiveSessionCommand, LiveSessionRequest, LiveSessionStatus, LiveSource};
use crate::{SessionId, SessionKernel};

fn set<T: Ord>(values: impl IntoIterator<Item = T>) -> BTreeSet<T> {
    values.into_iter().collect()
}

fn protocol_call_registration(
    id: &str,
    session_id: &str,
    target_id: &str,
) -> InstrumentRegistration {
    InstrumentRegistration {
        instrument_id: id.into(),
        session_id: session_id.into(),
        mode: InstrumentMode::Passive,
        capabilities: set([
            Capability::EventSemanticBoundary,
            Capability::EventLifecycle,
        ]),
        events: set([EventKind::ProtocolCall, EventKind::ExecutionTerminal]),
        filter: InstrumentFilter {
            session_id: Some(session_id.into()),
            target_ids: set([target_id.into()]),
            target_kinds: set([TargetKind::WholeWasm]),
            backends: set([RuntimeBackend::new("rust").expect("Rust is a valid backend id")]),
        },
        projection: ProjectionRequest::default(),
        delivery: EventDelivery::Queue { capacity: 16 },
    }
}

fn target_identity(payload: &serde_json::Value) -> (String, u64) {
    let target = payload
        .get("target")
        .and_then(serde_json::Value::as_object)
        .expect("live-session snapshot exposes a bounded target identity");
    let id = target
        .get("id")
        .and_then(serde_json::Value::as_str)
        .expect("target id is text")
        .to_owned();
    let generation = target
        .get("generation")
        .and_then(serde_json::Value::as_u64)
        .expect("target generation is an unsigned integer");
    (id, generation)
}

#[test]
fn trusted_native_binds_and_observes_real_whole_wasm_protocol_target() {
    let mut kernel = SessionKernel::new();
    let session_id = SessionId::parse("trusted-whole-wasm").unwrap();
    kernel.create_session(session_id.clone()).unwrap();
    let service = kernel
        .instrumentation(&session_id)
        .expect("trusted host service");
    let source = LiveSource::new(
        "whole-wasm.hal",
        "sha256:whole-wasm",
        "(std.protocol.icount.ICount/count [1 2 3])",
    )
    .unwrap();
    let started = kernel
        .session_mut(&session_id)
        .unwrap()
        .start_whole_wasm_live_session("debug-session", source)
        .expect("Whole-Wasm live session starts");
    let first_run = kernel
        .session_mut(&session_id)
        .unwrap()
        .dispatch_live_session(LiveSessionRequest::for_state(
            "run-whole-wasm-before-bind",
            &started,
            LiveSessionCommand::Run {
                boundary_limit: 1_000,
            },
        ))
        .expect("Whole-Wasm live target runs");
    assert_eq!(first_run.state.status, LiveSessionStatus::Returned);
    assert_eq!(first_run.payload["result"], 3);

    let (target_id, generation) = target_identity(&first_run.payload);
    let target = service
        .bind_target_identity(target_id.clone(), generation)
        .expect("trusted host binds the real Whole-Wasm target");
    let descriptor = service
        .target_descriptor(&target)
        .expect("bounded target descriptor");
    assert_eq!(descriptor.target_id, target_id);
    assert_eq!(descriptor.session_id, session_id.as_str());
    assert_eq!(descriptor.kind, TargetKind::WholeWasm);

    let instrument = service
        .register(protocol_call_registration(
            "host/whole-wasm-protocol",
            session_id.as_str(),
            target.target_id(),
        ))
        .expect("protocol-call instrument registration");
    service
        .attach(&instrument, &target)
        .expect("passive host attaches to Whole-Wasm target");

    let run = kernel
        .session_mut(&session_id)
        .unwrap()
        .dispatch_live_session(LiveSessionRequest::for_state(
            "run-whole-wasm-after-bind",
            &first_run.state,
            LiveSessionCommand::Run {
                boundary_limit: 1_000,
            },
        ))
        .expect("Whole-Wasm live target runs with instrumentation");
    assert_eq!(run.payload["result"], 3);
    let batch = service
        .drain_events(&instrument)
        .expect("host drains protocol-call events");
    assert!(batch.events.iter().any(|event| {
        event.envelope.event == EventKind::ProtocolCall
            && event.envelope.target_id == target.target_id()
            && event.envelope.session_id == session_id.as_str()
            && event.envelope.target_kind == TargetKind::WholeWasm
            && event.envelope.data.get("target")
                == Some(&"std.protocol.icount.ICount/count".to_owned())
            && event.envelope.data.get("status") == Some(&"return".to_owned())
    }));
    assert!(batch.events.iter().any(|event| {
        event.envelope.event == EventKind::ExecutionTerminal
            && event.envelope.target_id == target.target_id()
            && event.envelope.data.get("status") == Some(&"returned".to_owned())
    }));
}