Your browser sucks, JavaScript disabled! Gunnar's console needs JavaScript and WebAssembly. Git over SSH on port 2222 does not.

nordisk/holger

server/lib/tests/metering_doors.rs

//! **The two doors monetize has been waiting for**, driven through the real
//! handlers rather than around them.
//!
//! What each test is actually worth:
//!
//! * [`usage_reports_every_meter_and_names_the_ones_it_cannot_scope`] — the six
//!   meters come back, and the three that are only ever measured server-wide say so
//!   ON THE WIRE. A per-repository number that is not per-repository is the one
//!   failure that would reach an invoice.
//! * [`the_conversion_is_what_monetize_signed`] — the wire fact and the signed fact
//!   are different shapes, and `metered::to_signed` is the only bridge. If it drifts
//!   every signature fails for a reason no message explains, so it is pinned against
//!   a fact signed here with a real Ed25519 key.
//! * [`a_forged_fact_is_refused_and_nothing_is_written`] — the signature check runs
//!   BEFORE the disk, and the refusal is `PERMISSION_DENIED` with the frozen code.
//! * [`a_cap_holger_cannot_police_is_refused_at_the_sale`] — a cap on a server-wide
//!   meter is refused rather than stored and silently ignored for the life of the
//!   plan. gunnar learned that one the expensive way.
//! * [`an_unconfigured_metered_build_refuses_rather_than_pretending`] — a metered
//!   binary with no key configured says `holger-metered-4`; it does not accept a
//!   fact it cannot verify.
//!
//! The open build's half of the story is `open_build_speaks_no_money.rs`: these
//! tests only run when the feature that contains the vocabulary is on.

use std::collections::{BTreeMap, BTreeSet};
use std::sync::Arc;

use ed25519_dalek::{Signer, SigningKey};
use holger_server_lib::grpc::holger_metered_proto::{EntitlementFact, EntitlementState};
use holger_server_lib::metered::{self, EntitlementStore, PutRefused};
use holger_server_lib::metering::{PER_REPOSITORY_METERS, SERVER_WIDE_METERS};
use monetize_product::{EntitlementFact as SignedFact, State, TenantId};

/// A deterministic key pair — the test signs exactly the way monetize-server does,
/// over `monetize_embed::signing::fact_message`, so a green here means the real
/// signer's bytes would verify too.
fn keypair() -> (SigningKey, [u8; 32]) {
    let signing = SigningKey::from_bytes(&[7u8; 32]);
    let public = signing.verifying_key().to_bytes();
    (signing, public)
}

/// Sign a Rust fact the way monetize does: Ed25519 over the canonical JSON of the
/// fact minus its signature.
fn sign(fact: &mut SignedFact, key: &SigningKey) {
    fact.signature = key
        .sign(&monetize_embed::signing::fact_message(fact))
        .to_bytes()
        .to_vec();
}

fn wire_fact(repo: &str, caps: &[(&str, u64)]) -> EntitlementFact {
    EntitlementFact {
        repository: repo.to_string(),
        plan: "holger/rust-dev/2026-09-16+10gb".to_string(),
        state: EntitlementState::Paid as i32,
        paid_until_unix_ms: Some(1_800_000_000_000),
        caps: caps.iter().map(|(k, v)| ((*k).to_string(), *v)).collect(),
        source: "payment:stripe:cs_live_test".to_string(),
        signature: vec![0u8; 64],
        // Unstamped, which monetize_embed accepts as the legal pre-2026-09-17
        // case. The stamped path has its own fixture below.
        issued_unix_ms: None,
        issued_signature: Vec::new(),
    }
}

fn store(dir: &std::path::Path, public: &[u8; 32]) -> EntitlementStore {
    EntitlementStore::new(
        dir,
        Arc::new(BTreeSet::from(["rust-dev".to_string()])),
        public,
    )
    .expect("a 32-byte key is a key")
}

/// **The conversion is the load-bearing part.** A fact signed as the Rust shape must
/// verify after a round trip through the wire shape — that is the whole contract
/// between monetize and holger, and the two shapes disagree about field names, the
/// state's spelling and the map type.
#[test]
fn the_conversion_is_what_monetize_signed() {
    let (signing, public) = keypair();
    let mut signed = SignedFact {
        tenant: TenantId("rust-dev".to_string()),
        plan: "holger/rust-dev/2026-09-16+10gb".to_string(),
        state: State::Paid,
        paid_until_unix_ms: Some(1_800_000_000_000),
        caps: BTreeMap::from([("artifacts".to_string(), 10_000u64)]),
        source: "payment:stripe:cs_live_test".to_string(),
        signature: Vec::new(),
        issued_unix_ms: None,
        issued_signature: Vec::new(),
    };
    sign(&mut signed, &signing);

    // The wire carries what monetize signed…
    let mut wire = wire_fact("rust-dev", &[("artifacts", 10_000)]);
    wire.signature = signed.signature.clone();

    // …and the conversion must reproduce the signed value EXACTLY, or the signature
    // it carries is meaningless.
    let back = metered::to_signed(&wire).expect("a well-formed fact converts");
    assert_eq!(
        back, signed,
        "to_signed must rebuild the value monetize signed"
    );

    // Proof the check is real: verify with the public key, as the store does.
    let key = monetize_embed::VerifyingKey::from_bytes(&public).unwrap();
    monetize_embed::signing::verify_fact(&back, &key).expect("the round trip verifies");
}

/// A fact signed by a DIFFERENT key is refused, and the store never wrote a byte.
#[test]
fn a_forged_fact_is_refused_and_nothing_is_written() {
    let (_, public) = keypair();
    let forger = SigningKey::from_bytes(&[9u8; 32]);
    let dir = tempfile::tempdir().unwrap();
    let store = store(dir.path(), &public);

    let mut signed = SignedFact {
        tenant: TenantId("rust-dev".to_string()),
        plan: "p".to_string(),
        state: State::Paid,
        paid_until_unix_ms: None,
        caps: BTreeMap::new(),
        source: "operator".to_string(),
        signature: Vec::new(),
        issued_unix_ms: None,
        issued_signature: Vec::new(),
    };
    sign(&mut signed, &forger);

    match store.put(&signed) {
        Err(PutRefused::Signature(_)) => {}
        other => panic!("a forged fact must be refused by signature, got {other:?}"),
    }
    // The refusal happened BEFORE the disk: nothing under the store root.
    let wrote_anything = std::fs::read_dir(dir.path())
        .map(|d| d.flatten().next().is_some())
        .unwrap_or(false);
    assert!(!wrote_anything, "a refused fact must leave no file behind");
    assert!(
        store.get("rust-dev").unwrap().is_none(),
        "…and nothing readable"
    );
}

/// The honestly-signed fact IS stored, so the refusal above discriminates rather
/// than refusing everything.
#[test]
fn an_honestly_signed_fact_is_stored_and_reads_back() {
    let (signing, public) = keypair();
    let dir = tempfile::tempdir().unwrap();
    let store = store(dir.path(), &public);

    let mut signed = SignedFact {
        tenant: TenantId("rust-dev".to_string()),
        plan: "holger/rust-dev/2026-09-16+10gb".to_string(),
        state: State::Paid,
        paid_until_unix_ms: Some(1_800_000_000_000),
        caps: BTreeMap::from([("archive_bytes".to_string(), 10 << 30)]),
        source: "payment:stripe:cs_live_test".to_string(),
        signature: Vec::new(),
        issued_unix_ms: None,
        issued_signature: Vec::new(),
    };
    sign(&mut signed, &signing);

    store
        .put(&signed)
        .expect("a fact signed by the configured key is stored");
    let back = store.get("rust-dev").unwrap().expect("it reads back");
    assert_eq!(back, signed);
    assert_eq!(store.list().len(), 1);
}

/// **A cap on a meter holger only measures server-wide is refused at the sale.**
/// Storing it would be a promise about a number that is not the tenant's, and the
/// operator would hear about it from the customer months later, or never.
#[test]
fn a_cap_holger_cannot_police_is_refused_at_the_sale() {
    // Every per-repository meter is accepted…
    for meter in PER_REPOSITORY_METERS {
        let fact = wire_fact("rust-dev", &[(meter, 1)]);
        metered::well_formed(&fact)
            .unwrap_or_else(|e| panic!("'{meter}' is measured per repository: {e}"));
    }
    // …and every server-wide one is refused BY NAME, with the frozen code.
    for meter in SERVER_WIDE_METERS {
        let fact = wire_fact("rust-dev", &[(meter, 1)]);
        let err =
            metered::well_formed(&fact).expect_err("a cap on a server-wide meter must be refused");
        assert_eq!(err.code(), tonic::Code::InvalidArgument);
        assert!(
            err.message().contains(meter),
            "the refusal must NAME the meter: {}",
            err.message()
        );
        assert!(
            err.message().contains("holger-metered-2"),
            "the refusal carries its frozen code: {}",
            err.message()
        );
    }
}

/// The shape checks tell a malformed push apart from a forged one.
#[test]
fn a_malformed_fact_is_invalid_argument_not_permission_denied() {
    let mut fact = wire_fact("rust-dev", &[]);
    fact.signature = vec![0u8; 63];
    let err = metered::well_formed(&fact).expect_err("63 bytes is not an Ed25519 signature");
    assert_eq!(err.code(), tonic::Code::InvalidArgument);
    assert!(
        err.message().contains("holger-metered-1"),
        "{}",
        err.message()
    );

    let mut fact = wire_fact("rust-dev", &[]);
    fact.source.clear();
    let err = metered::well_formed(&fact).expect_err("a fact must say where it came from");
    assert!(err.message().contains("source"), "{}", err.message());
}

/// A repository this server does not serve is NOT a place to write a plan file —
/// the roster gate every metadata store here applies.
#[test]
fn the_store_is_closed_to_the_repository_roster() {
    let (_, public) = keypair();
    let dir = tempfile::tempdir().unwrap();
    let store = store(dir.path(), &public);
    assert!(store.serves("rust-dev"));
    assert!(
        !store.serves("maven-prod"),
        "a repository this server never configured"
    );
}

// ── THE ISSUE STAMP (2026-09-17) ─────────────────────────────────────────────
//
// Every fixture above builds an UNSTAMPED fact (`issued_unix_ms: None`,
// `issued_signature: Vec::new()`), which monetize_embed accepts as the legal
// pre-2026-09-17 case. That is why the full-value `assert_eq!` in
// [`the_conversion_is_what_monetize_signed`] could not see a dropped stamp:
// `None` and an empty `Vec` survive a conversion that forgets both fields, and
// the signature keeps verifying because the V1 form never covered them. The
// replay guard would then be gone with the whole suite green — which is how
// close it came on the day the fields were added.
//
// The fixtures below are STAMPED, so the two carry-through lines in
// `metered::to_signed` and `metered::to_wire` are load-bearing: delete either
// one and these go red.

/// A stamped fact and the wire message that carries it, signed both halves the
/// way monetize-server signs them: V1 over everything but the three, V2 over the
/// V1 form plus `issued_unix_ms`.
fn stamped_pair(issued: u64, key: &SigningKey) -> (SignedFact, EntitlementFact) {
    let mut signed = SignedFact {
        tenant: TenantId("rust-dev".to_string()),
        plan: "holger/rust-dev/2026-09-16+10gb".to_string(),
        state: State::Paid,
        paid_until_unix_ms: Some(1_800_000_000_000),
        caps: BTreeMap::from([("artifacts".to_string(), 10_000u64)]),
        source: "payment:stripe:cs_live_test".to_string(),
        signature: Vec::new(),
        issued_unix_ms: Some(issued),
        issued_signature: Vec::new(),
    };
    sign(&mut signed, key);
    signed.issued_signature = key
        .sign(&monetize_embed::signing::fact_message_issued(&signed))
        .to_bytes()
        .to_vec();

    let mut wire = wire_fact("rust-dev", &[("artifacts", 10_000)]);
    wire.signature = signed.signature.clone();
    wire.issued_unix_ms = signed.issued_unix_ms;
    wire.issued_signature = signed.issued_signature.clone();
    (signed, wire)
}

/// **The stamp survives the wire→signed conversion, or the replay guard is gone.**
///
/// `to_signed` dropping `issued_unix_ms`/`issued_signature` does NOT break any
/// signature: the fact simply arrives at the verifier as the legal unstamped
/// case, which it accepts. So nothing but a stamped fixture can catch it. Both
/// halves are asserted separately from the whole-value compare, so the failure
/// NAMES the field rather than printing two large structs.
#[test]
fn the_conversion_carries_the_issue_stamp() {
    let (signing, public) = keypair();
    let (signed, wire) = stamped_pair(1_789_000_000_000, &signing);

    let back = metered::to_signed(&wire).expect("a well-formed stamped fact converts");
    assert_eq!(
        back.issued_unix_ms,
        Some(1_789_000_000_000),
        "to_signed dropped issued_unix_ms — a stamped fact reaches the verifier as the \
         LEGAL UNSTAMPED CASE and the replay guard is silently gone"
    );
    assert_eq!(
        back.issued_signature, signed.issued_signature,
        "to_signed dropped issued_signature — the issue time is then unsigned, which is \
         not a claim about anything"
    );
    assert_eq!(
        back, signed,
        "to_signed must rebuild the value monetize signed"
    );

    // Both halves verify: V1 over the fact, V2 over the fact plus the issue time.
    let key = monetize_embed::VerifyingKey::from_bytes(&public).unwrap();
    monetize_embed::signing::verify_fact(&back, &key).expect("a stamped round trip verifies");
}

/// The same for the OUTBOUND half. `Get` and `List` hand a stored fact back over
/// the wire through `to_wire`; a stamp lost there is a stamp the next holder
/// never sees, and the fact they replay is the legal unstamped one.
#[test]
fn the_wire_shape_carries_the_issue_stamp_back_out() {
    let (signing, public) = keypair();
    let (signed, _) = stamped_pair(1_789_000_000_000, &signing);

    let out = metered::to_wire(&signed);
    assert_eq!(
        out.issued_unix_ms,
        Some(1_789_000_000_000),
        "to_wire dropped issued_unix_ms — Get/List hand out an UNSTAMPED fact"
    );
    assert_eq!(
        out.issued_signature, signed.issued_signature,
        "to_wire dropped issued_signature"
    );

    // Round trip: out and back is the identity, and the result still verifies.
    let back = metered::to_signed(&out).expect("the wire shape converts back");
    assert_eq!(
        back, signed,
        "signed -> wire -> signed must be the identity"
    );
    let key = monetize_embed::VerifyingKey::from_bytes(&public).unwrap();
    monetize_embed::signing::verify_fact(&back, &key).expect("the stamped round trip verifies");
}

/// **The negative control for the fixture itself.** If `stamped_pair` ever stopped
/// producing a stamped fact, the two tests above would go quietly vacuous in
/// exactly the way the unstamped fixtures did. So: the fixture IS stamped, and a
/// fact with the stamp removed verifies anyway — which is the property that makes
/// a dropped stamp invisible to every signature check.
#[test]
fn an_unstamped_fact_verifies_too_which_is_why_the_stamp_needs_its_own_fixture() {
    let (signing, public) = keypair();
    let (signed, wire) = stamped_pair(1_789_000_000_000, &signing);
    assert!(
        signed.issued_unix_ms.is_some(),
        "the fixture must be STAMPED"
    );
    assert!(
        !signed.issued_signature.is_empty(),
        "the fixture must be STAMPED"
    );
    assert!(
        wire.issued_unix_ms.is_some(),
        "the wire fixture must be STAMPED"
    );

    // Strip the stamp, exactly as a forgetful `to_signed` would.
    let mut stripped = metered::to_signed(&wire).expect("converts");
    stripped.issued_unix_ms = None;
    stripped.issued_signature.clear();
    let key = monetize_embed::VerifyingKey::from_bytes(&public).unwrap();
    monetize_embed::signing::verify_fact(&stripped, &key).expect(
        "THE WHOLE POINT: a stamp-stripped fact still verifies, so no signature \
                 check can stand in for the assertions above",
    );
}