mesh-llm-payments 0.78.1

Durable inference settlement: ledger, pricing, budgets and payment gates over a pluggable wallet
Documentation
#[cfg(test)]
use super::*;

#[cfg(test)]
fn terms(id: &str, cap: u64) -> RequestTerms {
    RequestTerms {
        exchange_id: None,
        id: id.into(),
        peer: "peer".into(),
        payee: None,
        model: "model".into(),
        pricing: Pricing {
            input_msat_per_million: 1000,
            output_msat_per_million: 1000,
            minimum_invoice_msat: 1,
        },
        input_tokens: 10,
        max_output_tokens: 10,
        max_total_msat: cap,
        expires_at_ms: u64::MAX,
    }
}

#[test]
fn backend_context_allowance_is_durable_and_bounds_delivery() {
    let directory = tempfile::tempdir().unwrap();
    let ledger = Ledger::open(directory.path()).unwrap();
    ledger
        .begin_serving(
            "context",
            "peer",
            &terms("context", 1).pricing,
            u64::from(u32::MAX),
        )
        .unwrap();
    ledger
        .resolve_serving_output_allowance("context", 6000)
        .unwrap();
    assert!(
        ledger
            .resolve_serving_output_allowance("context", 6001)
            .is_err()
    );
    assert!(
        ledger
            .resolve_serving_output_allowance("context", 0)
            .is_err()
    );
    ledger.record_delivered_tokens("context", 5000).unwrap();
    assert!(
        ledger
            .resolve_serving_output_allowance("context", 6000)
            .is_err()
    );
    drop(ledger);
    let ledger = Ledger::open(directory.path()).unwrap();
    ledger.record_delivered_tokens("context", 6000).unwrap();
    assert!(ledger.record_delivered_tokens("context", 6001).is_err());
}

#[test]
fn restart_and_day_rollover_preserve_reservations() {
    let directory = tempfile::tempdir().unwrap();
    {
        let ledger = Ledger::open(directory.path()).unwrap();
        ledger
            .set_policy(&Policy {
                mode: ApprovalMode::Automatic,
                daily_budget_msat: Some(1000),
            })
            .unwrap();
        ledger.propose(&terms("one", 700)).unwrap();
        ledger.approve("one", 1000, 1).unwrap();
    }
    let ledger = Ledger::open(directory.path()).unwrap();
    ledger.propose(&terms("two", 400)).unwrap();
    assert!(ledger.approve("two", 1000, DAY_MS + 1).is_err());
    ledger.finish("one").unwrap();
    ledger.approve("two", 1000, DAY_MS + 1).unwrap();
}

#[test]
fn independent_connections_cannot_double_reserve() {
    let directory = tempfile::tempdir().unwrap();
    let first = Ledger::open(directory.path()).unwrap();
    let second = Ledger::open(directory.path()).unwrap();
    first
        .set_policy(&Policy {
            mode: ApprovalMode::Automatic,
            daily_budget_msat: Some(1000),
        })
        .unwrap();
    first.propose(&terms("one", 700)).unwrap();
    second.propose(&terms("two", 700)).unwrap();
    let barrier = std::sync::Barrier::new(2);
    let results = std::thread::scope(|scope| {
        let one = scope.spawn(|| {
            barrier.wait();
            first.approve("one", 1000, 1).is_ok()
        });
        let two = scope.spawn(|| {
            barrier.wait();
            second.approve("two", 1000, 1).is_ok()
        });
        (one.join().unwrap(), two.join().unwrap())
    });
    assert_ne!(results.0, results.1);
}

#[test]
fn approvals_cannot_change_terms_or_revive_rejected_requests() {
    let directory = tempfile::tempdir().unwrap();
    let ledger = Ledger::open(directory.path()).unwrap();
    ledger.propose(&terms("one", 700)).unwrap();
    assert!(ledger.propose(&terms("one", 800)).is_err());
    ledger.reject("one").unwrap();
    assert!(ledger.approve("one", 1000, 1).is_err());
    assert!(
        ledger
            .set_policy(&Policy {
                mode: ApprovalMode::Automatic,
                daily_budget_msat: None
            })
            .is_err()
    );
}

#[test]
fn evidence_correlation_survives_reopen_without_schema_change() -> Result<()> {
    let directory = tempfile::tempdir()?;
    let mut request = terms("private-recovery", 100);
    request.exchange_id = Some("public-exchange".into());
    let ledger = Ledger::open(directory.path())?;
    ledger.propose(&request)?;
    drop(ledger);
    let reopened = Ledger::open(directory.path())?;
    assert_eq!(
        reopened.requests()?[0].terms.exchange_id,
        request.exchange_id
    );
    let mut legacy = serde_json::to_value(&request)?;
    legacy.as_object_mut().unwrap().remove("exchange_id");
    assert!(
        serde_json::from_value::<RequestTerms>(legacy)?
            .exchange_id
            .is_none()
    );
    Ok(())
}

#[test]
fn single_policy_controls_eligibility_and_preserves_reservations() -> Result<()> {
    let directory = tempfile::tempdir()?;
    let ledger = Ledger::open(directory.path())?;
    assert_eq!(ledger.policy()?.mode, ApprovalMode::FreeOnly);
    assert!(matches!(
        ledger.payment_intent()?,
        crate::intent::PaymentIntent::FreeOnly
    ));
    assert_eq!(ledger.available_budget(1000, 1)?, 0);
    ledger.propose(&terms("disabled", 500))?;
    assert!(ledger.approve("disabled", 1000, 1).is_err());
    ledger.set_policy(&Policy {
        mode: ApprovalMode::Automatic,
        daily_budget_msat: Some(1000),
    })?;
    ledger.approve("disabled", 1000, 1)?;
    let status = ledger.policy_status(1)?;
    assert_eq!(status["reserved_msat"], 500);
    assert_eq!(status["remaining_daily_budget_msat"], 500);
    ledger.set_policy(&Policy::default())?;
    assert_eq!(ledger.policy_status(1)?["remaining_daily_budget_msat"], 0);
    assert_eq!(
        ledger.request_state("disabled")?.as_deref(),
        Some("approved")
    );
    Ok(())
}