crates/monetize-server/tests/estate.rs
//! **`EstateAdmin.Draw` on the wire**: the estate picture has a door, and the
//! door keeps the module's rules.
//!
//! `monetize_server::estate_svg` was finished, tested and unreachable — a
//! renderer with an example and no caller. These tests are about the CALL: that
//! the bearer is required, that the two halves arrive the right way round, that
//! a product this server does not serve is refused by name rather than drawn as
//! a blank board, and that the case the live box is in today — no `[machines]`
//! block at all — answers a board instead of an error or a hang.
//!
//! Measure, never assert a round trip: every claim below is read off the SVG
//! that came back over gRPC, not off an `Estate` built in this file.
use std::sync::Arc;
use monetize::testing::MockProduct;
use monetize_product::Growth;
use monetize_proto::estate_admin_client::EstateAdminClient;
use monetize_proto::{self as pb};
use monetize_server::config::{Config, MachineConfig};
use monetize_server::registry::Registry;
use monetize_server::state::AppState;
use monetize_server::testing::TestServer;
use tonic::metadata::MetadataValue;
use tonic::transport::Channel;
use tonic::Request;
const BEARER: &str = "test-bearer-token-of-sufficient-length";
const GIB: u64 = 1 << 30;
struct Rig {
product: Arc<MockProduct>,
#[allow(dead_code)]
server: TestServer,
channel: Channel,
}
impl Rig {
/// A server whose config is exactly `cfg`, serving the gunnar double.
async fn up(cfg: Config, product: Arc<MockProduct>) -> Rig {
let product_dyn: Arc<monetize::testing::MockProduct> = product.clone();
let mut state = AppState::in_memory(Registry::new().shared_product(product_dyn));
*state.config.get_mut().unwrap() = cfg;
let server = TestServer::spawn(Arc::new(state), BEARER).await;
let channel = Channel::from_shared(server.endpoint())
.unwrap()
.connect()
.await
.unwrap();
Rig {
product,
server,
channel,
}
}
fn authed<T>(&self, body: T) -> Request<T> {
let mut r = Request::new(body);
r.metadata_mut().insert(
"authorization",
MetadataValue::try_from(format!("Bearer {BEARER}")).unwrap(),
);
r
}
async fn draw(&self, product: &str) -> Result<String, tonic::Status> {
EstateAdminClient::new(self.channel.clone())
.draw(self.authed(pb::DrawEstateRequest {
product: product.into(),
..Default::default()
}))
.await
.map(|r| r.into_inner().svg)
}
}
/// `[machines.front]`, `[machines.main]`, `[machines.twin]` — the gunnar.rs
/// shape, with a holger box on the same monetize to prove the filter.
fn three_machines() -> Config {
let m = |product: &str,
role: &str,
title: &str,
server: &str,
plan: &str,
vcpu: u32,
ram_mb: u64| MachineConfig {
product: product.into(),
role: role.into(),
title: title.into(),
cloud: "upcloud".into(),
server: server.into(),
plan: plan.into(),
vcpu,
ram_mb,
..MachineConfig::default()
};
Config {
machines: [
(
"front".to_string(),
m(
"gunnar",
"front",
"caddy · the console",
"0000-front",
"STARTER-4xCPU-8GB",
4,
8192,
),
),
(
"main".to_string(),
m(
"gunnar",
"appliance",
"the forge, no shell",
"0000-appliance",
"STARTER-4xCPU-16GB",
4,
16384,
),
),
(
"twin".to_string(),
m(
"gunnar",
"twin",
"the log-holder",
"0000-twin",
"STARTER-1xCPU-1GB",
1,
1024,
),
),
(
"holger-box".to_string(),
m(
"holger",
"appliance",
"somebody else's estate",
"0000-holger",
"STARTER-1xCPU-1GB",
1,
1024,
),
),
]
.into(),
..Config::default()
}
}
/// **The picture is served, and it is a picture.**
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn the_estate_is_drawn_as_one_self_contained_svg_document() {
let rig = Rig::up(three_machines(), Arc::new(MockProduct::gunnar())).await;
let svg = rig.draw("gunnar").await.expect("the board is drawn");
assert!(
svg.starts_with("<svg "),
"an SVG document, not a fragment: {}",
&svg[..svg.len().min(60)]
);
assert!(svg.trim_end().ends_with("</svg>"), "and a closed one");
// Self-contained: nothing is fetched when this is opened. A picture that
// pulled a font or a script would be a picture that leaks where it is read.
for forbidden in ["<script", "<image", "xlink:href", "@import", "url(http"] {
assert!(
!svg.contains(forbidden),
"the document must load nothing: {forbidden}"
);
}
assert!(
svg.contains("gunnar estate"),
"the board says whose estate it is"
);
}
/// **The board says how many machines it carries, in the same answer**
/// (owner 2026-10-02: the console says "this estate names no machines yet"
/// rather than showing a blank board) — counted off the roll call the picture
/// was drawn from, the other product's box not counted.
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn the_board_counts_the_machines_it_names_and_an_undeclared_estate_is_zero() {
let rig = Rig::up(three_machines(), Arc::new(MockProduct::gunnar())).await;
let out = EstateAdminClient::new(rig.channel.clone())
.draw(rig.authed(pb::DrawEstateRequest {
product: "gunnar".into(),
..Default::default()
}))
.await
.unwrap()
.into_inner();
assert_eq!(
out.machines,
Some(3),
"front, main and twin — holger's box is not on gunnar's board"
);
let rig = Rig::up(Config::default(), Arc::new(MockProduct::gunnar())).await;
let out = EstateAdminClient::new(rig.channel.clone())
.draw(rig.authed(pb::DrawEstateRequest {
product: "gunnar".into(),
..Default::default()
}))
.await
.unwrap()
.into_inner();
assert_eq!(
out.machines,
Some(0),
"no [machines] block: zero, said in the answer"
);
assert!(out.svg.starts_with("<svg "), "and still a board");
}
/// **The DECLARED half arrives from `[machines]`, and stays labelled declared.**
///
/// The plan's vCPU and RAM are the provider's claim about a box and nothing on
/// this estate reads them off it. The door must not be able to launder one into
/// a measurement on the way past.
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn a_declared_plan_reaches_the_board_still_labelled_as_a_claim() {
let rig = Rig::up(three_machines(), Arc::new(MockProduct::gunnar())).await;
let svg = rig.draw("gunnar").await.expect("the board is drawn");
assert!(
svg.contains("0000-appliance"),
"the provider id an operator pastes into a ticket"
);
assert!(
svg.contains("STARTER-4xCPU-16GB"),
"the plan name, verbatim"
);
assert!(
svg.contains("plan says · STARTER-4xCPU-16GB"),
"a declaration says so, beside the plan it came from"
);
assert!(
svg.contains("4 vCPU"),
"the plan's own count, not a reading"
);
assert!(
!svg.contains("measured ·"),
"nothing measured these boxes, so no caption may claim it did"
);
assert!(
!svg.contains("cores"),
"cores are what a MEASURED ceiling is drawn in; a plan is vCPU"
);
}
/// **The MEASURED half arrives from the plugin, and a box it refused is a dash.**
///
/// One product, three machines: the appliance measured, and the twin and the front
/// asked nothing at all (a box's report carries no row for either).
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn what_the_plugin_read_is_drawn_and_what_it_refused_is_a_dash() {
let product = Arc::new(MockProduct::gunnar());
product.can_serve_growing(
30 * GIB,
40 * GIB,
Growth::Sealed("this boot stack carries no engine".into()),
);
product.measure_ceiling("cpu_millicores", 4000);
let rig = Rig::up(three_machines(), product).await;
let svg = rig.draw("gunnar").await.expect("the board is drawn");
// The appliance: read off the box, so the caption changes word.
assert!(
svg.contains("measured · plan STARTER-4xCPU-16GB"),
"a reading beats a claim, and the caption says which it is"
);
assert!(svg.contains("4.0 cores"), "the measured ceiling, in cores");
assert!(
svg.contains("disk 10.0 GiB of 40.0 GiB"),
"40 total, 30 servable — used is the difference"
);
// The twin and the front are asked nothing: both are dashes. A zero here
// would draw an empty disk on a box nobody measured.
assert!(
svg.contains("disk — · not measured"),
"an unmeasured box is a dash, never a zero"
);
assert!(
svg.contains(">not asked<"),
"nobody asked the front, which is not the same as down"
);
}
/// **One board per product.** `[machines.<id>] product` filters, so another
/// product's iron is not on this estate.
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn the_board_carries_only_the_product_that_was_asked_for() {
let rig = Rig::up(three_machines(), Arc::new(MockProduct::gunnar())).await;
let svg = rig.draw("gunnar").await.expect("the board is drawn");
assert!(svg.contains("0000-appliance"), "gunnar's own box is on it");
assert!(
!svg.contains("0000-holger"),
"another product's estate is another board"
);
assert!(
!svg.contains("somebody else's estate"),
"and none of its words are on this one"
);
}
/// **A deployment with no `[machines]` draws a board, and does not 500.**
///
/// This is the case the live box is in today: the block is emitted by a tool
/// (`cargo xtask estate emit`) and no deployment has run it yet, so an empty
/// board is the FIRST thing this route ever serves. It is the honest drawing of
/// "this estate is not written down" — a picture with no chips on it — and it
/// must not be an error, because there is no error: nobody promised machines.
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn a_deployment_that_declares_no_machines_is_answered_with_an_empty_board() {
let rig = Rig::up(Config::default(), Arc::new(MockProduct::gunnar())).await;
let svg = rig
.draw("gunnar")
.await
.expect("an undeclared estate is still answered");
assert!(svg.starts_with("<svg "), "a board, not a refusal");
assert!(
svg.contains("gunnar estate"),
"titled, so the reader knows what is empty"
);
assert!(
!svg.contains("not measured"),
"no chips at all — an empty board carries no slots"
);
assert!(!svg.contains("plan says"));
}
/// **An undeclared estate does not disturb the product's box.**
///
/// With no machines on the board there is nothing for a reading to land on, so
/// the plugin is not called — which is what keeps a picture of an estate nobody
/// wrote down from hanging on a box it was never going to draw.
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn an_empty_board_is_drawn_without_asking_the_product_anything() {
let product = Arc::new(MockProduct::gunnar());
let rig = Rig::up(Config::default(), product).await;
let svg = rig
.draw("gunnar")
.await
.expect("a down box cannot stop an empty board");
assert!(svg.starts_with("<svg "));
// The same server, once the estate IS declared: the twin nobody could ask
// is drawn as such rather than as an error.
let rig = Rig::up(three_machines(), rig.product.clone()).await;
let svg = rig
.draw("gunnar")
.await
.expect("a refusing plugin is still a board");
assert!(
svg.contains(">not asked<"),
"the plugin refused, so nobody knows — not a zero and not a down"
);
}
/// **A product this server does not serve is refused by name.**
///
/// `estate_svg::from_config` would draw an empty board for a misspelt product, and
/// an empty board is what an undeclared estate looks like — so a typo would be
/// indistinguishable from the truth. The refusal names what IS linked in.
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn an_unknown_product_is_refused_rather_than_drawn_as_a_blank_board() {
let rig = Rig::up(three_machines(), Arc::new(MockProduct::gunnar())).await;
let err = rig.draw("idun").await.expect_err("idun is not served here");
assert_eq!(err.code(), tonic::Code::NotFound);
assert!(err.message().contains("idun"), "{}", err.message());
assert!(
err.message().contains("gunnar"),
"and it names what IS linked in: {}",
err.message()
);
}
/// **The picture is behind the bearer, like every other operator read.**
///
/// It names provider ids, plan names and per-machine disk. A caller without the
/// token gets nothing at all — not a redacted board.
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn a_caller_without_the_bearer_is_refused_the_estate() {
let rig = Rig::up(three_machines(), Arc::new(MockProduct::gunnar())).await;
let err = EstateAdminClient::new(rig.channel.clone())
.draw(pb::DrawEstateRequest {
product: "gunnar".into(),
..Default::default()
})
.await
.expect_err("no bearer, no board");
assert_eq!(err.code(), tonic::Code::Unauthenticated);
}
/// **The same estate draws the same bytes.** The renderer promises it; the door
/// must not undo it, because a caller hashing these bytes for an `ETag` is
/// relying on it.
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn two_draws_of_an_unchanged_estate_are_byte_for_byte_equal() {
let product = Arc::new(MockProduct::gunnar());
product.can_serve(30 * GIB, 40 * GIB);
let rig = Rig::up(three_machines(), product).await;
let once = rig.draw("gunnar").await.expect("drawn");
let twice = rig.draw("gunnar").await.expect("drawn again");
assert_eq!(once, twice, "a diff would have to mean the estate changed");
}