liminal-server 0.24.5

Standalone server for the liminal messaging bus
Documentation
//! Supplemental kernel tests; real wire acceptance is in the ticket journey.

use super::test_support::{Fixture, TestResult, limits, spec};
use super::{SiteChange, validate_contracts};
use crate::services::lifecycle::ServiceState;
use lys_pass::drafts::Execution;

/// A registry directory that already exists is the operator's: these tests
/// make theirs private, as an operator would (SRV-036 R1).
fn private_tempdir() -> std::io::Result<tempfile::TempDir> {
    use std::os::unix::fs::PermissionsExt;
    tempfile::Builder::new()
        .permissions(std::fs::Permissions::from_mode(0o700))
        .tempdir()
}

#[test]
fn contracts_are_required_before_a_builder_can_exist() -> TestResult {
    let mut missing = spec();
    missing.contracts.clear();
    let refusal = validate_contracts(&missing, &limits()?)
        .err()
        .ok_or("missing contract admitted")?;
    assert_eq!(refusal.to_string(), "subject echo.request has no schema");
    validate_contracts(&spec(), &limits()?)?;
    Ok(())
}

#[test]
fn site_approval_bind_refusal_and_unbind_keep_exact_grants_and_history() -> TestResult {
    let fixture = Fixture::new()?;
    assert!(matches!(
        fixture.execute("create", SiteChange::CreateSite(spec()))?,
        Execution::Executed { .. }
    ));
    let record = fixture.lifecycle.record("echo")?;
    assert_eq!(record.state, ServiceState::Building);
    assert!(record.template.is_none());
    assert!(record.builder.is_none());
    assert!(record.contracts == spec().contracts);
    fixture.execute(
        "bind",
        SiteChange::BindBuilder {
            agent: "builder".into(),
        },
    )?;
    let grants = fixture
        .issuer
        .grants
        .lock()
        .map_err(|_| "fixture grants poisoned")?;
    let rights: Vec<_> = grants.values().cloned().collect();
    drop(grants);
    assert_eq!(rights.len(), 2);
    assert!(rights.contains(&("builder".into(), "echo".into(), "plan".into())));
    assert!(rights.contains(&("builder".into(), "echo".into(), "serve".into())));
    let refused = fixture.execute(
        "second",
        SiteChange::BindBuilder {
            agent: "second".into(),
        },
    )?;
    let Execution::RefusedOnExecution { reason, .. } = refused else {
        return Err("second bind admitted".into());
    };
    assert_eq!(reason, "site echo has builder builder");
    fixture.execute("unbind", SiteChange::UnbindBuilder)?;
    assert!(
        fixture
            .issuer
            .grants
            .lock()
            .map_err(|_| "fixture grants poisoned")?
            .is_empty()
    );
    assert_eq!(fixture.lifecycle.record("echo")?.transitions.len(), 3);
    Ok(())
}

#[test]
fn old_install_migrates_once_and_keeps_every_old_transition() -> TestResult {
    let directory = private_tempdir()?;
    let old = serde_json::json!({"id":"svc-old","name":"old","template":"echo","subjects":["echo.old"],
        "state":"held","created_by":"person-owner","transitions":[{"from":"asleep","to":"held","holder":"person-owner","at_ms":1}]});
    std::fs::write(
        directory.path().join("svc-old.json"),
        serde_json::to_vec(&old)?,
    )?;
    let first = crate::services::lifecycle::Lifecycle::open_bounded(
        &[],
        directory.path().into(),
        "net".into(),
        None,
        &limits()?,
    )?;
    let scheduler = liminal::channel::ChannelSupervisor::new()?;
    let retained = crate::agents::retained::Retained::new(
        crate::agents::config::AgentsConfig::default(),
        scheduler.clone(),
        "fixture",
    );
    first.configure_sites(
        limits()?,
        super::test_support::open_store(&directory.path().join("store"))?,
        None,
        std::sync::Arc::new(super::test_support::Issuer::default()),
        retained.clone(),
    )?;
    let record = first.record("svc-old")?;
    assert_eq!(record.template.as_deref(), Some("echo"));
    assert_eq!(record.transitions.len(), 1);
    assert!(record.purpose.is_none());
    assert!(record.contracts.is_empty());
    drop(first);
    std::fs::write(
        directory.path().join("svc-old.json"),
        b"archived bytes are not replayed",
    )?;
    let second = crate::services::lifecycle::Lifecycle::open_bounded(
        &[],
        directory.path().into(),
        "net".into(),
        None,
        &limits()?,
    )?;
    second.configure_sites(
        limits()?,
        super::test_support::open_store(&directory.path().join("store"))?,
        None,
        std::sync::Arc::new(super::test_support::Issuer::default()),
        retained,
    )?;
    assert_eq!(second.record("svc-old")?, record);
    scheduler.shutdown();
    Ok(())
}

#[test]
fn approved_act_has_one_mutation_batch_and_one_flush_beside_preparation() -> TestResult {
    let fixture = Fixture::new()?;
    let before = fixture.counts.snapshot();
    fixture.execute("create", SiteChange::CreateSite(spec()))?;
    let counts = fixture.counts.snapshot().since(&before);
    assert_eq!(counts.apply_routed_batch_calls, 2);
    assert_eq!(counts.apply_routed_batch_items, 2);
    assert_eq!(counts.flushes, 2);
    assert_eq!(counts.append_calls, 0);
    assert_eq!(counts.append_all_calls, 0);
    let before = fixture.counts.snapshot();
    fixture.execute("create", SiteChange::CreateSite(spec()))?;
    let replay = fixture.counts.snapshot().since(&before);
    assert_eq!(replay.apply_routed_batch_calls, 0);
    assert_eq!(replay.flushes, 0);
    assert_eq!(fixture.lifecycle.record("echo")?.transitions.len(), 1);
    Ok(())
}

#[test]
fn construction_migration_is_bounded_before_parsing_old_bytes() -> TestResult {
    let directory = private_tempdir()?;
    std::fs::write(directory.path().join("legacy.json"), [b' '; 128])?;
    let mut limits = limits()?;
    limits.snapshot_bytes = std::num::NonZeroUsize::new(64).ok_or("invalid bound")?;
    let refused = crate::services::lifecycle::Lifecycle::open_bounded(
        &[],
        directory.path().into(),
        "net".into(),
        None,
        &limits,
    )
    .err()
    .ok_or("oversized old record admitted")?;
    assert_eq!(
        refused.to_string(),
        "registry_unavailable: snapshot_bytes_bound_exceeded"
    );
    assert!(!directory.path().join("construction/registry.json").exists());
    Ok(())
}

#[test]
fn construction_projection_preserves_agents_and_other_devices() -> TestResult {
    let fixture = Fixture::new()?;
    fixture.execute("create", SiteChange::CreateSite(spec()))?;
    let document = serde_json::json!({"rev":1,"stations":[
        {"id":"local-worker","device":"fixture","kind":"worker","subjects":["echo.request"]},
        {"id":"remote-worker","device":"peer","kind":"worker","subjects":["echo.request"]},
        {"id":"agents.builder.inbox","device":"fixture","kind":"agent"}],
        "lines":[{"id":"echo.request","kind":"call","stops":["local-worker","remote-worker"]}]});
    let bytes = crate::console::topology::with_sites(
        &serde_json::to_vec(&document)?,
        &fixture.lifecycle,
        "fixture",
    )?;
    let after: serde_json::Value = serde_json::from_slice(&bytes)?;
    let stations = after["stations"].as_array().ok_or("stations missing")?;
    assert_eq!(stations.len(), 3);
    assert!(
        stations
            .iter()
            .any(|station| station["id"] == "remote-worker")
    );
    assert!(
        stations
            .iter()
            .any(|station| station["id"] == "agents.builder.inbox")
    );
    assert_eq!(
        after["lines"][0]["stops"],
        serde_json::json!(["services.echo", "remote-worker"])
    );
    let site = stations
        .iter()
        .find(|station| station["service"] == "echo")
        .ok_or("site missing")?;
    assert_eq!(site["state"], "building");
    assert_eq!(site["contracts"][0]["subject"], "echo.request");
    Ok(())
}

/// SRV-036 R1: the registry liminal makes is private whatever the umask it runs under.
#[test]
fn a_fresh_registry_is_created_private_whatever_the_umask() -> TestResult {
    use std::os::unix::fs::PermissionsExt;
    let root = tempfile::tempdir()?;
    let bounded = root.path().join("bounded");
    crate::services::lifecycle::Lifecycle::open_bounded(
        &[],
        bounded.clone(),
        "net".into(),
        None,
        &limits()?,
    )?;
    let plain = root.path().join("plain");
    crate::services::lifecycle::Lifecycle::open(&[], plain.clone(), "net".into(), None)?;
    for dir in [bounded, plain] {
        let mode = std::fs::metadata(&dir)?.permissions().mode() & 0o777;
        assert_eq!(
            mode,
            0o700,
            "{} was created with mode {mode:o}",
            dir.display()
        );
    }
    Ok(())
}