estate-tests/tests/round_trip_against_monetizes_guardian.rs
//! ★ **A Holger box against monetize's OWN guardian code, in process**: the guardian's
//! store, its pin window (TOFU on the first call), its signed order queue under holger's
//! product key, its report record. The Link below is monetize-server's `Fleet` service
//! (`admit` + `report`/`orders`), minus tonic.
use std::cell::RefCell;
use std::collections::BTreeMap;
use std::sync::Arc;
use holger_fleet::exec::{Applied, HolgerBox};
use holger_fleet::{Addressee, BoxKey, Fault, Link, Puller, Signed};
use monetize::sign::{Keyring, Signer};
use monetize_embed::fleet::{FleetCall, SignedOrder};
use monetize_guardian::{FleetRefusal, Guardian, GuardianConfig, Store};
use monetize_product::pull::{GrowRoom, PurgeAnswer, PurgeTenant};
use monetize_product::{EntitlementFact, State, TenantId};
const NOW: u64 = 1_800_000_000_000;
struct InProcess {
g: Arc<Guardian>,
}
fn refusal(r: FleetRefusal) -> Fault {
Fault::new(
if matches!(r, FleetRefusal::Unauthenticated) {
"unauthenticated"
} else {
"refused"
},
r.to_string(),
)
}
impl InProcess {
fn admit(&self, s: &Signed) -> Result<FleetCall, Fault> {
if !s.box_key.is_empty() {
self.g
.first_call(&s.call, &s.signature, &s.box_key, NOW)
.map_err(refusal)?;
}
Ok(s.call.clone())
}
}
impl Link for InProcess {
fn report(&mut self, s: Signed) -> Result<u64, Fault> {
let c = self.admit(&s)?;
self.g.report(&c, &s.signature, NOW).map_err(refusal)?;
Ok(30_000)
}
fn orders(&mut self, s: Signed) -> Result<Vec<SignedOrder>, Fault> {
let c = self.admit(&s)?;
self.g
.orders(&c, &s.signature, NOW)
.map(|(o, _)| o)
.map_err(refusal)
}
fn ask(&mut self, s: Signed) -> Result<holger_fleet::Asked, Fault> {
let c = self.admit(&s)?;
self.g
.ask(&c, &s.signature, NOW)
.map(|a| holger_fleet::Asked {
case_id: a.case_id,
words: a.words,
url: String::new(),
})
.map_err(refusal)
}
}
#[derive(Default)]
struct Main {
facts: RefCell<BTreeMap<String, EntitlementFact>>,
did: RefCell<Vec<String>>,
}
impl HolgerBox for Main {
fn serving(&self) -> bool {
true
}
fn health(&self) -> BTreeMap<String, String> {
BTreeMap::from([
("serving".into(), "yes".into()),
("data".into(), "ok".into()),
])
}
fn product_report(&self) -> Option<String> {
Some(r#"{"tenants":[{"tenant":"libs","person":false,"seller":false}]}"#.into())
}
fn entitle(&self, fact: &EntitlementFact) -> Result<Applied, String> {
self.facts
.borrow_mut()
.insert(fact.tenant.0.clone(), fact.clone());
Ok(Applied::New(format!(
"{} is {:?}",
fact.tenant.0, fact.state
)))
}
fn purge(&self, p: &PurgeTenant) -> Result<PurgeAnswer, String> {
self.did.borrow_mut().push(format!("purge {}", p.tenant));
Ok(PurgeAnswer {
job: format!("purge:{}", p.tenant),
..Default::default()
})
}
fn want_room(&self, g: &GrowRoom) -> Result<String, String> {
self.did.borrow_mut().push(format!("room {}", g.need_bytes));
Ok("kept".into())
}
}
#[test]
fn a_holger_main_pins_reports_pulls_runs_and_acks_against_monetizes_guardian() {
let store = Arc::new(Store::in_memory().unwrap());
store.declare("acme", "holger").unwrap();
store.open_pin("acme", "main", NOW + 60_000).unwrap();
let keys = Arc::new(Keyring::new(Signer::from_bytes(&[6u8; 32])));
let holger_key = keys.product("holger").public_key();
let product_signer = keys.product("holger").clone();
let g = Arc::new(Guardian::new(
store.clone(),
keys,
GuardianConfig::default(),
));
// What monetize queues for the main: a paid repository, a room want, a purge, and a
// kind Holger has no concept of.
let mut fact = EntitlementFact {
tenant: TenantId("libs".into()),
plan: "o-1".into(),
state: State::Paid,
paid_until_unix_ms: Some(NOW + 86_400_000),
caps: Default::default(),
source: "payment:stripe:o-1".into(),
signature: Vec::new(),
issued_unix_ms: None,
issued_signature: Vec::new(),
};
fact = product_signer.sign_fact(fact);
for (kind, body) in [
("entitlement", serde_json::to_string(&fact).unwrap()),
(
"grow_room",
r#"{"need_bytes":1073741824,"reason":"libs"}"#.to_string(),
),
(
"purge_tenant",
r#"{"tenant":"old","reason":"nonpay"}"#.to_string(),
),
(
"add_principal",
r#"{"username":"alice","kind":"person","signup":"s-1"}"#.to_string(),
),
] {
g.queue_order(
Guardian::order("holger", "acme", "main", kind, body, NOW, 60_000),
|_| false,
)
.unwrap();
}
let me = Addressee {
product: "holger".into(),
instance: "acme".into(),
role: "main".into(),
};
let mut p = Puller::new(me, BoxKey::from_seed([3u8; 32]), "holger-test");
let mut link = InProcess { g: g.clone() };
let b = Main::default();
// The first report pins the box's key inside the window.
p.report(&mut link, b.health(), b.product_report(), None, NOW)
.expect("the first report pins the box");
let pulled = p
.pull(
&mut link,
&holger_fleet::Pumped(&b),
Some(&holger_key),
0,
NOW,
)
.expect("orders");
assert!(
pulled.forged.is_empty(),
"every order verifies under holger's product key: {pulled:?}"
);
let oks: Vec<(String, bool)> = pulled
.acks
.iter()
.map(|a| (a.detail.chars().take(40).collect(), a.ok))
.collect();
assert_eq!(pulled.acks.len(), 4, "{oks:?}");
assert_eq!(
pulled.acks.iter().map(|a| a.ok).collect::<Vec<_>>(),
[true, true, true, false],
"{oks:?}"
);
assert!(
pulled.acks[3].detail.contains("makes no repositories"),
"a box with no repositories refuses the signup by name: {}",
pulled.acks[3].detail
);
assert!(b.facts.borrow().contains_key("libs"));
assert_eq!(b.did.borrow().as_slice(), ["room 1073741824", "purge old"]);
// The acks ride the next report; the guardian records them and the queue empties.
p.report(&mut link, b.health(), b.product_report(), None, NOW)
.expect("report");
let rec = store
.box_record("acme", "main")
.expect("the guardian keeps the box's report");
assert_eq!(
rec.report.health.get("serving").map(String::as_str),
Some("yes")
);
assert!(rec
.report
.product
.as_deref()
.is_some_and(|p| p.contains("libs")));
assert_eq!(rec.acks.len(), 4, "{:?}", rec.acks);
assert!(
store.orders_for("acme", "main", 0).is_empty(),
"the acked orders left the queue"
);
// Another box presenting another key outside the window is refused.
let mut stranger = Puller::new(
Addressee {
product: "holger".into(),
instance: "acme".into(),
role: "main".into(),
},
BoxKey::from_seed([8u8; 32]),
"x",
);
assert_eq!(
stranger
.report(&mut link, BTreeMap::new(), None, None, NOW)
.unwrap_err()
.code,
"unauthenticated"
);
}