liminal-server 0.27.1

Standalone server for the liminal messaging bus
Documentation
//! ACCESS-011 R2 (c): a site act whose ask Lys cannot answer proceeds
//! outright under `decisions = "advise"` with the receipt `advised`, and is
//! refused under `enforce` with the ask's own name.

use super::preparation::{VerifiedContext, hold_verified};
use super::test_support::{Fixture, TestResult, pass, spec};
use super::{SiteChange, SiteError};
use crate::auth_lys::advisory::Warned;
use crate::config::types::Decisions;

fn limits() -> Result<crate::auth_lys::drafts::DraftLimits, &'static str> {
    let bound = |value| std::num::NonZeroUsize::new(value).ok_or("test bound is zero");
    Ok(crate::auth_lys::drafts::DraftLimits {
        pages: bound(4)?,
        drafts: bound(16)?,
        approvers: bound(2)?,
        words_bytes: bound(4096)?,
        metadata_bytes: bound(256)?,
    })
}

fn hold(fixture: &Fixture, decisions: Decisions) -> Result<serde_json::Value, SiteError> {
    let runtime = tokio::runtime::Builder::new_current_thread()
        .enable_all()
        .build()
        .map_err(|_| SiteError::Store)?;
    // No draft keeper is installed, so the ask fails before any dial: the
    // "ask that failed" arm, with no Lys on the wire.
    let live =
        crate::auth_lys::live::HttpLysAsk::new("http://127.0.0.1:9/api/", runtime.handle().clone())
            .map_err(|_| SiteError::Store)?;
    let pass = pass("builder", "agent").map_err(|_| SiteError::Store)?;
    let warned = Warned::default();
    hold_verified(
        VerifiedContext {
            lifecycle: &fixture.lifecycle,
            pass: &pass,
            token: "fixture-pass",
            live: &live,
            at_ms: 40_000,
            decisions,
            warned: &warned,
            limits: limits().map_err(|_| SiteError::Store)?,
        },
        "op-advised",
        "echo",
        SiteChange::CreateSite(spec()),
    )
}

#[test]
fn a_site_act_lys_cannot_answer_proceeds_under_advise_and_is_refused_under_enforce() -> TestResult {
    let enforce = Fixture::new()?;
    let refused = hold(&enforce, Decisions::Enforce)
        .err()
        .ok_or("enforce let an unanswered site act through")?;
    assert!(
        matches!(&refused, SiteError::Pass(name) if name == "lys_could_not_be_asked"),
        "{refused:?}"
    );
    assert!(enforce.lifecycle.record("echo").is_err());

    let advise = Fixture::new()?;
    let answer = hold(&advise, Decisions::Advise)?;
    assert_eq!(answer["approved_by"], "lys", "{answer}");
    let record = advise.lifecycle.record("echo")?;
    let receipt = record.approvals.first().ok_or("no receipt")?;
    assert_eq!(receipt.draft, "grant:advised");
    Ok(())
}