server/lib/tests/the_meter_is_what_answers.rs
//! **The meter plane is live, and these are the four things that says.**
//!
//! Until the wiring lane, every meter loaded with `NoServices` — the three host
//! services refuse by name — and the metering doors counted inline. A module
//! could be installed, granted, verified and reported in the census, and still
//! learn nothing and decide nothing. "Complete but inert" is not a state a test
//! can tell from "working", which is why these exist.
//!
//! Each test here asserts a JOIN, not a shape:
//!
//! 1. the guest is what answers the usage door, and its readings say so;
//! 2. the guest's verdict decides a PUT and decides a promote, through the
//! `GateSet` the server actually builds — not through a `WasmGate` a test
//! made for itself;
//! 3. a refusal is a refusal, a notice is not, and an `Err` refuses;
//! 4. the grant table is still the only authority, and no service is reachable
//! without the matching capability.
use std::sync::Arc;
use holger_plugin_host::abi::v2::{
meters, AbiRange, Capability, Enforcement, GuestKind, Meter, MeterFacts, MeterHost, MeterScope,
ReadingValue, WireAuditRecord, WireCallContext, WireCapacity, WireInstant, WireManifest2,
WireMeterDecl, WireMeterReading, WireSelftest, WireVerb, WireVerdict,
};
use holger_plugin_host::{GrantTable, MeterCatalog, MeterHostServices};
use holger_server_lib::meter_services::{CallStamp, HolgerMeterServices};
use holger_server_lib::promote::{PromotedArtifact, PromotionContext};
use holger_server_lib::Holger;
// ───────────────────────── the guests these tests use ─────────────────────────
/// A meter whose verdict the test chooses and which passes the host's readings
/// through unchanged — the same shape as the open guest's `read`, so what comes
/// out of the usage door is what the SOURCE produced and not what this fixture
/// invented.
struct Passthrough {
name: &'static str,
verdict: Result<WireVerdict, String>,
}
impl Passthrough {
fn allowing(name: &'static str) -> Self {
Self {
name,
verdict: Ok(WireVerdict::Allow),
}
}
}
impl Meter for Passthrough {
fn manifest(&self) -> WireManifest2 {
WireManifest2 {
abi: AbiRange::new(2, 2),
kind: GuestKind::Meter,
name: self.name.to_string(),
requires: vec![Capability::UsageRead],
handler: None,
meter: Some(MeterFacts {
accepts_facts: false,
}),
identity: None,
unknown_optional: Vec::new(),
}
}
fn meters(&self) -> Result<Vec<WireMeterDecl>, String> {
Ok(Vec::new())
}
fn read(
&self,
host: &dyn MeterHost,
repository: &str,
) -> Result<Vec<WireMeterReading>, String> {
// The host counts; the guest never substitutes a number of its own.
host.usage_read(repository)
}
fn capacity(&self, host: &dyn MeterHost) -> Result<WireCapacity, String> {
let _ = host.usage_read(meters::PRODUCT_SCOPE)?;
Ok(WireCapacity {
total_bytes: 0,
servable_bytes: 0,
used_bytes: 0,
source: ReadingValue::Refused {
reason: "fixture".into(),
},
})
}
fn selftest(&self, _h: &dyn MeterHost) -> Result<WireSelftest, String> {
Ok(WireSelftest {
name: "fixture".into(),
passed: true,
detail: String::new(),
})
}
fn evaluate(&self, _h: &dyn MeterHost, _c: &WireCallContext) -> Result<WireVerdict, String> {
self.verdict.clone()
}
}
/// A guest that asks for everything. Used only to prove the grant refuses it.
struct Greedy;
impl Meter for Greedy {
fn manifest(&self) -> WireManifest2 {
WireManifest2 {
abi: AbiRange::new(2, 2),
kind: GuestKind::Meter,
name: "greedy".into(),
requires: vec![
Capability::UsageRead,
Capability::AuditWrite,
Capability::ClockRead,
Capability::StoreWrite,
],
handler: None,
meter: Some(MeterFacts {
accepts_facts: false,
}),
identity: None,
unknown_optional: Vec::new(),
}
}
fn meters(&self) -> Result<Vec<WireMeterDecl>, String> {
Ok(Vec::new())
}
fn read(&self, _h: &dyn MeterHost, _r: &str) -> Result<Vec<WireMeterReading>, String> {
Ok(Vec::new())
}
fn capacity(&self, _h: &dyn MeterHost) -> Result<WireCapacity, String> {
Err("greedy".into())
}
fn selftest(&self, _h: &dyn MeterHost) -> Result<WireSelftest, String> {
Err("greedy".into())
}
fn evaluate(&self, _h: &dyn MeterHost, _c: &WireCallContext) -> Result<WireVerdict, String> {
Ok(WireVerdict::Allow)
}
}
// ───────────────────────────── the server under test ─────────────────────────
/// A holger with one writable file-backed repository, wired the way the serve
/// path wires it. `wire_holger` also builds the meter plane, so what comes back
/// is a server whose plane is REAL — the same one `ReadUsage` and the gates use.
fn server(dir: &std::path::Path) -> Holger {
let d = dir.display();
let ron = format!(
r#"(
exposed_endpoints: [ ( ron_name: "dev", ron_url: "127.0.0.1:0", ron_http_url: None ) ],
storage_endpoints: [ ( ron_name: "store", ron_storage_type: "file", ron_path: "{d}/store" ) ],
repositories: [
( ron_name: "releases", ron_repo_type: "rust", ron_data_dir: Some("{d}/releases"), ron_base_url: Some("http://x"), ron_upstreams: [], ron_in: None, ron_out: Some(( ron_storage_endpoint: "store", ron_exposed_endpoint: "dev" )) ),
],
)
"#
);
let mut holger: Holger =
ron::de::from_reader(std::io::Cursor::new(ron.as_bytes())).expect("RON parses");
holger.instantiate_backends().expect("the backend builds");
holger_server_lib::wire_holger(&mut holger).expect("the config wires");
holger
}
/// Replace the plane `wire_holger` built with one holding `meter`, granted
/// `usage.read` and nothing else — the same `add_linked` path a compiled-in
/// guest takes, held to the same grant table a loaded module is.
fn with_linked_meter(holger: &mut Holger, meter: impl Meter + Send + Sync + 'static, name: &str) {
let grants = GrantTable::from_pairs([(name, vec!["usage.read"])]).expect("the grant parses");
let mut catalog = MeterCatalog::new(grants);
let services: Arc<dyn MeterHostServices> = Arc::new(HolgerMeterServices::new(
holger.exposed_endpoints[0]
.aggregated_routes
.clone()
.expect("the door has a route table"),
Arc::new(holger_server_lib::audit::NoopAuditLog),
));
catalog
.add_linked(
name,
meter,
Arc::new(holger_plugin_host::DirStore::new(dir_for_store())),
services,
)
.expect("the guest loads under its own grant");
holger.meter_plane = Some(Arc::new(catalog));
}
fn dir_for_store() -> std::path::PathBuf {
std::env::temp_dir().join(format!("holger-meter-plane-{}", std::process::id()))
}
// ───────────────────── 1. the guest is what answers ──────────────────────────
#[test]
fn the_loaded_guest_is_what_answers_the_usage_door_and_the_reading_says_so() {
let dir = tempfile::tempdir().unwrap();
let mut holger = server(dir.path());
with_linked_meter(&mut holger, Passthrough::allowing("narrator"), "narrator");
let plane = holger.meter_plane.clone().expect("the plane is built");
let rows = holger_server_lib::meter_services::read_through_guests(plane.meters(), "releases")
.expect("the guest answers");
// The six meters the door has always reported, narrated by the guest.
let names: Vec<&str> = rows.iter().map(|r| r.name.as_str()).collect();
for expected in [
holger_server_lib::metering::METER_ARTIFACTS,
holger_server_lib::metering::METER_ARCHIVE_BYTES,
holger_server_lib::metering::METER_BYTES_SERVED,
holger_server_lib::metering::METER_REQUESTS,
holger_server_lib::metering::METER_PROXY_UPSTREAM_HITS,
holger_server_lib::metering::METER_REPOS,
] {
assert!(
names.contains(&expected),
"{expected} missing from {names:?}"
);
}
// Every one of them came THROUGH the module, and says which module.
for r in &rows {
assert_eq!(
r.source, "meter:narrator",
"{} was not narrated by the guest",
r.name
);
}
// `repos` is a number this server counted and the guest carried unchanged.
let repos = rows.iter().find(|r| r.name == "repos").unwrap();
assert!(repos.counted);
assert_eq!(repos.value, 1);
}
#[test]
fn a_refusal_the_host_made_survives_the_guest_instead_of_becoming_a_zero() {
let dir = tempfile::tempdir().unwrap();
let mut holger = server(dir.path());
with_linked_meter(&mut holger, Passthrough::allowing("narrator"), "narrator");
let plane = holger.meter_plane.clone().unwrap();
// The product scope over a server wired with NO data root: holger refuses
// the three storage meters BY NAME, and the reason has to survive the ABI.
// (With a root it measures them -- see
// tests/the_storage_meters_measure_a_real_filesystem.rs.)
let rows = holger_server_lib::meter_services::read_through_guests(
plane.meters(),
holger_server_lib::meter_services::product_scope(),
)
.expect("the guest answers");
assert_eq!(rows.len(), 3, "{rows:?}");
for r in &rows {
assert!(!r.counted, "{} came back as a measurement", r.name);
assert_eq!(
r.value, 0,
"the field must hold something; `counted` is what disowns it"
);
assert!(
r.refusal_reason.contains("no data root"),
"the reason was lost across the ABI: {}",
r.refusal_reason
);
}
}
#[test]
fn the_guest_reads_the_same_counter_the_door_would_have_read() {
let dir = tempfile::tempdir().unwrap();
let mut holger = server(dir.path());
let routes = holger.exposed_endpoints[0]
.aggregated_routes
.clone()
.expect("routes");
with_linked_meter(&mut holger, Passthrough::allowing("narrator"), "narrator");
let plane = holger.meter_plane.clone().unwrap();
// The same inputs `HolgerMeterServices::new` hands the guest: no data root,
// no declared storage places.
let direct =
holger_server_lib::metering::count(&routes, "releases", None, Some(&Default::default()))
.expect("the door counts");
let through =
holger_server_lib::meter_services::read_through_guests(plane.meters(), "releases").unwrap();
assert_eq!(
direct.len(),
through.len(),
"the guest dropped or added a meter"
);
for d in direct {
let t = through
.iter()
.find(|r| r.name == d.name)
.expect("every meter survives");
match d.value {
Ok(v) => {
assert!(t.counted, "{} lost its measurement", d.name);
assert_eq!(t.value, v, "{} changed value across the ABI", d.name);
}
Err(reason) => {
assert!(!t.counted, "{} gained a measurement it never had", d.name);
assert_eq!(t.refusal_reason, reason, "{} lost its reason", d.name);
}
}
}
}
// ────────────── 2. the verdict decides a PUT and decides a promote ───────────
fn one_artifact() -> (traits::ArtifactId, Vec<u8>) {
(
traits::ArtifactId {
namespace: None,
name: "widget".into(),
version: "1.0.0".into(),
},
vec![7u8; 11],
)
}
/// Run `f` over a promotion context. An EMPTY `from_repo` is the ingest PUT
/// boundary; a named one is a promotion hop. That is the one field that differs.
fn over_context<R>(from_repo: &str, f: impl FnOnce(&PromotionContext<'_>) -> R) -> R {
let (id, bytes) = one_artifact();
let digest =
holger_server_lib::attest::ArtifactDigest::parse("sha256", &"ab".repeat(32)).unwrap();
let set = [PromotedArtifact {
id: &id,
ecosystem: "raw",
digest,
bytes: &bytes,
attestations: &[],
}];
let ctx = PromotionContext {
principal: "sub-123",
from_repo,
to_repo: "releases",
hop: None,
set: &set,
};
f(&ctx)
}
#[test]
fn the_guest_is_in_the_ingest_set_the_server_builds_and_it_runs_on_a_put() {
let dir = tempfile::tempdir().unwrap();
let mut holger = server(dir.path());
with_linked_meter(
&mut holger,
Passthrough {
name: "capper",
verdict: Ok(WireVerdict::Refuse {
code: "holger-metered-6".into(),
reason: "the ceiling is reached".into(),
url: Some("https://example.invalid/renew".into()),
}),
},
"capper",
);
// THE SERVER'S OWN SET — not a `WasmGate` this test built. This config has
// no `promotion:` block at all, so before the wiring it was empty.
let set = holger.ingest_gates().expect("the ingest set builds");
assert!(
set.names().contains(&"capper"),
"the meter is not in the set: {:?}",
set.names()
);
let decision = over_context("", |ctx| set.evaluate(ctx));
assert!(
!decision.is_allowed(),
"a refusing meter let a PUT through: {decision:?}"
);
let text = format!("{decision:?}");
assert!(text.contains("holger-metered-6"), "{text}");
assert!(text.contains("the ceiling is reached"), "{text}");
}
#[test]
fn the_guest_is_on_the_promotion_engine_and_it_runs_on_a_promote() {
let dir = tempfile::tempdir().unwrap();
let mut holger = server(dir.path());
with_linked_meter(&mut holger, Passthrough::allowing("capper"), "capper");
// Again with no `promotion:` block: a box with no rungs is not a box whose
// metering plane was switched off.
let engine = holger.promotion_engine().expect("the engine builds");
assert!(engine.has_gate(), "the promotion engine carries no gate");
// And the gate sees a PROMOTE there and a PUT at ingest — the one field that
// differs between the two boundaries.
let gates = holger.meter_gates();
assert_eq!(gates.len(), 1);
assert_eq!(gates[0].name(), "capper");
for (from_repo, _expected) in [("", WireVerb::Put), ("dev", WireVerb::Promote)] {
let v = over_context(from_repo, |ctx| gates[0].check(ctx));
assert!(v.is_ok(), "the allowing guest refused a {from_repo:?} hop");
}
}
#[test]
fn a_build_with_no_meter_loaded_gates_exactly_as_it_always_did() {
let dir = tempfile::tempdir().unwrap();
let holger = server(dir.path());
// The plane exists and is empty. Empty is not a gate.
assert!(holger.meter_plane.as_ref().unwrap().is_empty());
assert!(holger.meter_gates().is_empty());
assert!(holger.ingest_gates().unwrap().is_empty());
assert!(!holger.promotion_engine().unwrap().has_gate());
}
// ───────────── 3. a refusal is a refusal; a notice is not; Err refuses ───────
#[test]
fn a_notice_is_not_a_refusal_because_data_is_never_held_hostage() {
let dir = tempfile::tempdir().unwrap();
let mut holger = server(dir.path());
with_linked_meter(
&mut holger,
Passthrough {
name: "noticer",
verdict: Ok(WireVerdict::AllowWithNotice {
line: "80% of the ceiling".into(),
}),
},
"noticer",
);
let set = holger.ingest_gates().unwrap();
let decision = over_context("", |ctx| set.evaluate(ctx));
assert!(
decision.is_allowed(),
"a NOTICE was applied as a refusal: {decision:?}"
);
}
#[test]
fn a_guest_that_cannot_answer_refuses_the_hop_rather_than_waving_it_through() {
let dir = tempfile::tempdir().unwrap();
let mut holger = server(dir.path());
with_linked_meter(
&mut holger,
Passthrough {
name: "trapper",
verdict: Err("the guest trapped".into()),
},
"trapper",
);
let set = holger.ingest_gates().unwrap();
let decision = over_context("", |ctx| set.evaluate(ctx));
assert!(
!decision.is_allowed(),
"an Err from a metering guest became an allow: {decision:?}"
);
assert!(
format!("{decision:?}").contains("trapped"),
"the reason was swallowed: {decision:?}"
);
}
// ───────────────── 4. the grant table is still the authority ─────────────────
#[test]
fn a_guest_asking_for_more_than_the_grant_gives_is_refused_by_name() {
let dir = tempfile::tempdir().unwrap();
let holger = server(dir.path());
let routes = holger.exposed_endpoints[0]
.aggregated_routes
.clone()
.unwrap();
let grants = GrantTable::from_pairs([("greedy", vec!["usage.read"])]).unwrap();
let mut catalog = MeterCatalog::new(grants);
let services: Arc<dyn MeterHostServices> = Arc::new(HolgerMeterServices::new(
routes,
Arc::new(holger_server_lib::audit::NoopAuditLog),
));
let err = catalog
.add_linked(
"greedy",
Greedy,
Arc::new(holger_plugin_host::DirStore::new(dir_for_store())),
services,
)
.expect_err("the grant stops short of what the manifest declares");
// Named, not silently reduced: a typo that grants less produces a module
// that refuses at runtime for a reason nobody can find.
assert!(err.contains("audit.write"), "{err}");
assert!(
catalog.is_empty(),
"a refused module still reached the plane"
);
}
#[test]
fn the_wired_services_are_unreachable_without_the_matching_capability() {
// The bridge, with the open guest's grant. `usage.read` and `store.list`
// answer; the four that make a metering plugin dangerous do not — and none
// of them was made reachable by wiring a real implementation behind them.
let dir = tempfile::tempdir().unwrap();
let holger = server(dir.path());
let routes = holger.exposed_endpoints[0]
.aggregated_routes
.clone()
.unwrap();
let services: Arc<dyn MeterHostServices> = Arc::new(HolgerMeterServices::new(
routes,
Arc::new(holger_server_lib::audit::NoopAuditLog),
));
let host = holger_plugin_host::GrantedHost::new(
Arc::new(holger_plugin_host::DirStore::new(dir_for_store())),
services,
&[Capability::UsageRead, Capability::StoreList],
);
assert!(
host.usage_read("releases").is_ok(),
"the usage source is wired"
);
assert!(host.store_list("").is_ok(), "the store listing is wired");
assert!(host
.audit_write(&WireAuditRecord::default())
.unwrap_err()
.contains("audit.write"));
assert!(host.clock().unwrap_err().contains("clock.read"));
assert!(host.store_get("k").unwrap_err().contains("store.read"));
// The one that would let a meter move its own number.
assert!(host
.store_put("k", b"v")
.unwrap_err()
.contains("store.write"));
}
#[test]
fn a_granted_guest_reaches_all_three_services_and_the_host_stamps_the_call() {
let dir = tempfile::tempdir().unwrap();
let holger = server(dir.path());
let routes = holger.exposed_endpoints[0]
.aggregated_routes
.clone()
.unwrap();
let services: Arc<dyn MeterHostServices> = Arc::new(HolgerMeterServices::new(
routes,
Arc::new(holger_server_lib::audit::NoopAuditLog),
));
let host = holger_plugin_host::GrantedHost::new(
Arc::new(holger_plugin_host::DirStore::new(dir_for_store())),
services,
&[
Capability::UsageRead,
Capability::AuditWrite,
Capability::ClockRead,
],
);
// All three answer — which is the whole difference from `NoServices`, whose
// three answers are "this holger has no … wired".
let rows: Vec<WireMeterReading> = host.usage_read("releases").expect("the usage source");
assert!(rows
.iter()
.any(|r| r.meter == "repos" && r.scope == MeterScope::Product));
assert!(rows.iter().all(|r| r.enforcement == Enforcement::Measured));
let t: WireInstant = host.clock().expect("the clock");
assert!(t.unix_nanos > 0);
assert_eq!(
t.trusted_until_unix_nanos, 0,
"no signed time source, so no horizon"
);
holger_server_lib::meter_services::in_call(
CallStamp {
ident: "sub-7".into(),
source_ip: "10.0.0.2:443".into(),
},
|| {
host.audit_write(&WireAuditRecord::default())
.expect("the audit sink")
},
);
}