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

nordisk/gunnar-ui

crates/gunnar-ui-web/src/room.rs

//! **The Room table's shapes and its one arithmetic** — the half of the usage
//! page that decides things, kept where `cargo test` can reach it.
//!
//! `pages::usage` is `#[cfg(target_arch = "wasm32")]` like every module that
//! touches the DOM, so nothing inside it is compiled — let alone tested — by
//! `cargo test` on the host. That is fine for a view. It is not fine for the
//! decision this table now makes, which is the one the auditor found wrong:
//! **is this cap a wall or a reading**, and what does the page draw when the
//! box did not say. A test that only ever ran in a browser nobody launches is
//! a test that does not exist, so the types and the rules live here — no
//! leptos, no `web-sys` — and `pages::usage` re-exports them and does nothing
//! but draw. `auth` and `fmt` are here for the same reason and it is the
//! module layout `lib.rs` already describes.

use std::collections::BTreeMap;

use serde::Deserialize;

use crate::fmt;

/// One namespace's meters, exactly as `GET /api/namespaces/{name}/usage` sends
/// them.
///
/// The field names are gunnar's own, and are kept so on purpose: `gunnar
/// control usage` prints these names, and a person comparing the terminal with
/// the browser must not have to work out which of ours is which of its.
#[derive(Debug, Clone, Deserialize, PartialEq)]
pub struct Usage {
    pub namespace: String,
    pub pack_bytes: u64,
    pub cache_bytes: u64,
    pub cold_bytes: u64,
    pub lfs_bytes: u64,
    pub tombstoned_bytes: u64,
    pub repos: u64,
    pub objects: u64,
    pub measured_at_unix_ms: Option<i64>,

    // ── FLOW: what moved today ────────────────────────────────────────────
    //
    // Every meter above is a STOCK meter and reads the same for a namespace
    // pushed to this morning as for one last touched in March. These are the
    // other kind, and they ride on the same read. `#[serde(default)]` on all of
    // them so a console served from a build newer than the server it is talking
    // to still renders the page rather than failing to parse it.
    #[serde(default)]
    pub bytes_in_today: u64,
    #[serde(default)]
    pub bytes_out_today: u64,
    #[serde(default)]
    pub pushes_today: u64,
    #[serde(default)]
    pub fetches_today: u64,
    #[serde(default)]
    pub refused_today: u64,
    /// The last transfer that put bytes in, over the whole kept window.
    /// **`None` = never**, and it is `None` rather than 0 because gunnar sends
    /// 0 for a namespace nothing has touched — see
    /// [`crate::activity::last_seen_line`], which is where that turns into the
    /// word.
    #[serde(default)]
    pub last_push_unix_ms: Option<i64>,
    /// The last transfer that took bytes out. Same rule.
    #[serde(default)]
    pub last_fetch_unix_ms: Option<i64>,

    /// Per meter, the cap the box holds. `None` = no cap, never 0.
    #[serde(default)]
    pub caps: PerMeter,
    /// Per meter, what a cap on it DOES on the box that answered:
    /// `enforced`, `measured`, `provisioned`.
    ///
    /// **A missing entry — and an empty map — is "the box did not say", not
    /// "nothing is enforced".** See [`shape_of`].
    #[serde(default)]
    pub enforcement: BTreeMap<String, String>,
    /// Per meter, what is left — the server's one arithmetic. `None` = nothing
    /// this console can read bounds it.
    #[serde(default)]
    pub remaining: PerMeter,
    /// The box, when the console may read it.
    #[serde(rename = "box", default)]
    pub machine: Option<Machine>,
    /// Why there is no box, in gunnar's own words; empty when there is one.
    #[serde(default)]
    pub box_unreadable: String,
}

/// One optional number per meter, named as gunnar names the meters.
#[derive(Debug, Clone, Default, Deserialize, PartialEq)]
pub struct PerMeter {
    #[serde(default)]
    pub pack_bytes: Option<u64>,
    #[serde(default)]
    pub cache_bytes: Option<u64>,
    #[serde(default)]
    pub cold_bytes: Option<u64>,
    #[serde(default)]
    pub lfs_bytes: Option<u64>,
    #[serde(default)]
    pub tombstoned_bytes: Option<u64>,
    #[serde(default)]
    pub repos: Option<u64>,
    #[serde(default)]
    pub objects: Option<u64>,
}

/// The box: what `Admin.Capacity` measured.
#[derive(Debug, Clone, Deserialize, PartialEq)]
pub struct Machine {
    pub total_bytes: u64,
    pub free_bytes: u64,
    pub servable_bytes: u64,
    /// `sealed`, `operator_only` or `unknown`.
    pub growth: String,
    /// Space that exists and may never be handed out. `#[serde(default)]` on
    /// this and on every field below it, so a console served from a build
    /// newer than the server it talks to still renders rather than failing to
    /// parse — the same rule the flow half already follows above.
    #[serde(default)]
    pub floor_bytes: u64,
    /// The cores gunnar may run on, ×1000. **`None` = not measured, never 0.**
    #[serde(default)]
    pub cpu_millicores_total: Option<u64>,
    /// The 1-minute load ×1000 — the whole box's runnable tasks, an
    /// approximation, and it can exceed [`Machine::cpu_millicores_total`].
    #[serde(default)]
    pub cpu_millicores_busy: Option<u64>,
    /// `MemTotal`, clamped by a cgroup limit. **`None` = not measured.**
    #[serde(default)]
    pub ram_bytes_total: Option<u64>,
    /// `MemAvailable`, clamped by a cgroup limit. **`None` = not measured.**
    #[serde(default)]
    pub ram_bytes_available: Option<u64>,
    /// How the box was measured, and the OS error behind any absence, in
    /// gunnar's own words. Empty only on a server too old to send it.
    #[serde(default)]
    pub machine_reason: String,
}

/// The growth verdict as the box line says it: nothing at all when unknown,
/// because a line that guessed would read as a promise.
pub fn growth_phrase(growth: &str) -> &'static str {
    match growth {
        "sealed" => "cannot grow while running",
        "operator_only" => "an operator can grow it",
        _ => "",
    }
}

/// **What this row IS**, which is a different question from what its number is.
///
/// The defect this exists to close: `Usage.Read` reports seven caps in one
/// shape, and three of them are a door while four are a reading. A fact
/// carrying `caps["repos"]=5` put a `5` in the Allowed column and nothing on
/// the box refuses the sixth repository. A number shown to a tenant as a limit
/// that no code enforces is a promise the product does not keep, and drawing
/// both kinds identically is what made it one.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Shape {
    /// **A WALL.** gunnar refuses when over, so the row is drawn with a bar
    /// and a ceiling: the cap is a place the tenant will actually arrive at,
    /// and a bar is the only thing on a page that says "and then it stops".
    Wall,
    /// **A READING.** The number is true and is billed from, and crossing it
    /// is not an event on this box. Drawn as a figure and NO bar — a bar
    /// implies a ceiling, and inventing one here is exactly the defect.
    Reading,
    /// **The box did not say.** An older gunnar sends no enforcement at all,
    /// and it still cuts a push mid-stream at `pack_bytes`. So this is not
    /// [`Shape::Reading`] with a shrug: no bar (this console cannot honestly
    /// draw a ceiling it was not told about) and no claim that nothing
    /// refuses either.
    NotSaid,
}

/// **What a cap on this meter does, in monetize's own words.**
///
/// The two sentences are `monetize_product::Enforcement::describe()` verbatim,
/// including the semicolon. gunnar deliberately put monetize's three names on
/// the wire rather than coining `is_a_wall` or `hard`/`soft`; writing a third
/// phrasing HERE — "hard limit", "advisory", "soft cap" — would put a second
/// vocabulary on the surface a tenant reads, and it is the one that would go
/// stale, because nothing compares a console's prose to another repository's
/// enum.
pub fn describe(word: &str) -> &'static str {
    match word {
        "enforced" => "the product refuses when over",
        "measured" => "counted and reported; nothing refuses",
        "provisioned" => "provisioned at the cloud provider; not policed by the product",
        // A word this build has not learned. The server already drops the codes
        // it cannot name, so this arm is reached only by a console older than
        // the gunnar it is talking to — and a guess there would be a sentence
        // about a tenant's plan that nobody wrote.
        _ => "",
    }
}

/// The shape to draw for one meter: gunnar's word, or [`Shape::NotSaid`].
///
/// `provisioned` is a reading here, and gunnar never sends it for a meter in
/// this table — the variant exists so that the enum is monetize's whole
/// vocabulary. If it ever arrives, "bought at the cloud provider" is certainly
/// not a wall this box refuses at, so the reading shape is the safe reading.
pub fn shape_of(enforcement: &BTreeMap<String, String>, meter: &str) -> Shape {
    match enforcement.get(meter).map(String::as_str) {
        Some("enforced") => Shape::Wall,
        Some("measured") | Some("provisioned") => Shape::Reading,
        _ => Shape::NotSaid,
    }
}

/// **How full the wall is, 0–100**, or `None` when there is no wall to fill.
///
/// Only ever computed for a [`Shape::Wall`] WITH a cap: a bar needs both ends,
/// and a wall with no cap is uncapped — the door exists and nothing has been
/// sold against it, so there is nothing to fill.
///
/// Saturates at 100 rather than running off the end. Over a cap is a real
/// state (a fact can be lowered under a namespace that is already past it) and
/// the honest drawing of it is a full bar; a 140%-wide bar would be a rendering
/// bug that reads as a data bug.
pub fn fill_pct(used: u64, cap: Option<u64>) -> Option<u32> {
    let cap = cap?;
    if cap == 0 {
        // gunnar reports a 0 cap as ABSENT, so this is unreachable from a real
        // answer. It is here because the alternative is a division by zero.
        return Some(100);
    }
    Some(((used.min(cap) as u128 * 100) / cap as u128) as u32)
}

/// One row of the Room table: the meter, its three numbers, and what a cap on
/// it does.
pub struct RoomRow {
    pub meter: &'static str,
    pub used: String,
    /// `None` = no cap.
    pub allowed: Option<String>,
    /// `None` = nothing bounds it that the console could read.
    pub remaining: Option<String>,
    /// Wall, reading, or a box that did not say.
    pub shape: Shape,
    /// monetize's own sentence for what a cap here does; empty when unsaid.
    pub phrase: &'static str,
    /// The bar's fill, 0–100. `Some` only for a wall with a cap in it.
    pub fill: Option<u32>,
}

/// The seven rows, bytes rendered as bytes and counts as counts — the same
/// `fmt` the panels below use, so a number is spelled once.
///
/// The label a person reads and the meter gunnar names are both carried: the
/// first is the row heading, the second is the key into the enforcement map,
/// and pairing them here is what stops a renamed heading silently detaching a
/// row from the word that says what it does.
pub fn room_rows(usage: &Usage) -> Vec<RoomRow> {
    let row = |label, key: &str, used: u64, cap: Option<u64>, left: Option<u64>, bytes: bool| {
        let shape = shape_of(&usage.enforcement, key);
        let unit = |n: u64| if bytes { fmt::bytes(n) } else { fmt::count(n) };
        RoomRow {
            meter: label,
            used: unit(used),
            allowed: cap.map(unit),
            remaining: left.map(unit),
            shape,
            phrase: usage.enforcement.get(key).map_or("", |word| describe(word)),
            // The bar is drawn for a WALL and for nothing else, which is the
            // whole point: a reading has no ceiling to draw and a box that did
            // not say has none this console may invent.
            fill: match shape {
                Shape::Wall => fill_pct(used, cap),
                Shape::Reading | Shape::NotSaid => None,
            },
        }
    };
    let (caps, left) = (&usage.caps, &usage.remaining);
    vec![
        row(
            "Not re-derivable",
            "pack_bytes",
            usage.pack_bytes,
            caps.pack_bytes,
            left.pack_bytes,
            true,
        ),
        row(
            "Read cache",
            "cache_bytes",
            usage.cache_bytes,
            caps.cache_bytes,
            left.cache_bytes,
            true,
        ),
        row(
            "Cold tier",
            "cold_bytes",
            usage.cold_bytes,
            caps.cold_bytes,
            left.cold_bytes,
            true,
        ),
        row(
            "LFS",
            "lfs_bytes",
            usage.lfs_bytes,
            caps.lfs_bytes,
            left.lfs_bytes,
            true,
        ),
        row(
            "Tombstoned",
            "tombstoned_bytes",
            usage.tombstoned_bytes,
            caps.tombstoned_bytes,
            left.tombstoned_bytes,
            true,
        ),
        row(
            "Repositories",
            "repos",
            usage.repos,
            caps.repos,
            left.repos,
            false,
        ),
        row(
            "Indexed objects",
            "objects",
            usage.objects,
            caps.objects,
            left.objects,
            false,
        ),
    ]
}

/// The box line, or `None` when the box could not be read.
pub fn box_line(machine: Option<&Machine>) -> Option<String> {
    let machine = machine?;
    let mut line = format!(
        "This box: {} free of {}",
        fmt::bytes(machine.free_bytes),
        fmt::bytes(machine.total_bytes)
    );
    let growth = growth_phrase(&machine.growth);
    if !growth.is_empty() {
        line.push_str(" \u{b7} ");
        line.push_str(growth);
    }
    Some(line)
}

/// **One resource of the BOX: used, allowed, remaining.**
///
/// The Room table above answers *what is this namespace allowed*. This one
/// answers the other half of the same question — *what room is there at all* —
/// and it is a different table on purpose, because the two have different
/// scopes and merging them is the mistake. `UI.md`'s "Scope is the PRODUCT's
/// call": gunnar meters **disk per tenant and CPU/RAM per product**, a tenant
/// does not own cores, and `Allowance` has no field for either. A cores row
/// inside the Room table would read as a cap somebody was sold.
///
/// Three rows, and no fourth is invented: disk, cores, RAM are exactly what
/// `Admin.Capacity` measures.
pub struct BoxRow {
    pub resource: &'static str,
    /// `None` = gunnar did not measure it. **Never rendered as 0.**
    pub used: Option<String>,
    pub allowed: Option<String>,
    pub remaining: Option<String>,
    /// [`Shape::Wall`] for disk — gunnar's disk gate refuses writes under the
    /// floor — and [`Shape::Reading`] for cores and RAM, which nothing in this
    /// product refuses on.
    pub shape: Shape,
    /// monetize's own sentence for what this row's ceiling does, from
    /// [`describe`]. Never a phrasing coined here.
    ///
    /// # Why cores and RAM are `measured` here and not `provisioned`
    ///
    /// `provisioned` is monetize's word and its sentence is *"provisioned at
    /// the cloud provider; not policed by the product"* — and monetize does
    /// file `cpu_millicores` and `ram_bytes` under it, because in monetize's
    /// world they were bought from UpCloud. On THIS page that sentence would
    /// be a claim about somebody's purchase, on a console that must carry
    /// none, and on a self-hoster's own metal it would be plainly false:
    /// there is no cloud provider, and the page has to be exactly as useful to
    /// that person. `measured` — *"counted and reported; nothing refuses"* —
    /// is true on metal and in a cloud alike, is what gunnar actually does
    /// with these two numbers, and is still monetize's own word rather than a
    /// third vocabulary invented on this surface.
    pub phrase: &'static str,
    /// The bar's fill. `Some` only on the wall, for the same reason as in
    /// [`RoomRow`]: a bar IS a ceiling, and cores and RAM have none this
    /// product enforces.
    pub fill: Option<u32>,
    /// A clause about what the number IS, where the number would otherwise be
    /// read as something it is not. Empty on the rows that need none.
    pub caveat: &'static str,
}

/// **used · allowed · remaining, from a total, what is left of it, and what is
/// held back and may never be handed out.**
///
/// Split out of [`box_rows`] so the closure can be asserted on the NUMBERS
/// rather than on their rounded words: `142.6 MiB` plus `37.8 GiB` does not
/// visibly equal `37.9 GiB`, and a test that could only read the cells could
/// not see the arithmetic it exists to check.
///
/// `used` is defined as `allowed − remaining` rather than as `total − left`,
/// which is the same number and closes **by construction**: both terms are
/// gunnar's own, and no third reading of the disk can drift from them.
pub fn triple(total: u64, left: u64, held_back: u64) -> (u64, u64, u64) {
    let allowed = total.saturating_sub(held_back);
    let remaining = left.min(allowed);
    (allowed - remaining, allowed, remaining)
}

/// [`triple`] over two measurements that may be absent. `None` in either
/// means the box did not say, and then no column is drawn — not a zero.
fn triple_of(total: Option<u64>, left: Option<u64>, held_back: u64) -> Option<(u64, u64, u64)> {
    Some(triple(total?, left?, held_back))
}

/// The three rows of the box table, or `None` when there is no box to read.
///
/// # The arithmetic, and why it closes
///
/// Every number here is `Admin.Capacity`'s own; the only derivation is
/// subtraction, and it is written so that **used + remaining = allowed**
/// exactly, on all three rows. A reader who checks a console's arithmetic and
/// finds it short has been told the console is wrong, and then does not
/// believe the next number either.
///
/// * **Disk** — allowed is `total − floor`, not `total`: the disk floor is
///   space that exists and may never be handed out, and calling it "allowed"
///   would offer 2 GiB that gunnar refuses to sell. Remaining is gunnar's own
///   `servable_bytes`.
/// * **RAM** — allowed is `MemTotal`, remaining is `MemAvailable` as the
///   kernel reports it (page cache counted as free), and nothing is held back.
/// * **Cores** — the ONE row that does not close, and it is written that way
///   deliberately. Allowed is the affinity mask ×1000; used is the one-minute
///   load, which counts every runnable task on the whole box and **can exceed
///   the cores gunnar may run on**; remaining saturates at zero rather than
///   wrapping. A loaded box therefore reads `6.20 used · 4.00 allowed · 0.00
///   remaining`, and that is the true reading — forcing it to add up would
///   mean either inventing cores the box does not have or hiding a load the
///   operator needs to see. [`BoxRow::caveat`] says what the number is.
///
/// # Absence is an answer
///
/// `cpu_*` and `ram_*` are `Option` and a `None` stays `None` all the way to
/// the cell. A row gunnar could not measure carries no number in any column —
/// there is nothing to subtract from — and the page prints gunnar's
/// `machine_reason` rather than drawing an empty machine. The two RAM fields
/// arrive together or not at all (`/proc/meminfo` was read or it was not), so
/// they are treated as one answer; the two CPU fields do not, and a box that
/// measured the mask but not the load keeps the mask.
pub fn box_rows(machine: Option<&Machine>) -> Option<Vec<BoxRow>> {
    let machine = machine?;

    // Disk. Present unconditionally: `Admin.Capacity` cannot answer at all
    // without the `statvfs` these come from — an unmeasured filesystem is
    // `CAPACITY_UNMEASURED` and there is no box on this page at all.
    let (disk_used, disk_allowed, disk_left) =
        triple(machine.total_bytes, machine.servable_bytes, machine.floor_bytes);
    let disk = BoxRow {
        resource: "Disk",
        used: Some(fmt::bytes(disk_used)),
        allowed: Some(fmt::bytes(disk_allowed)),
        remaining: Some(fmt::bytes(disk_left)),
        shape: Shape::Wall,
        phrase: describe("enforced"),
        fill: fill_pct(disk_used, Some(disk_allowed)),
        caveat: "the disk floor is held back out of \u{201c}allowed\u{201d} and is never \
                 handed to a tenant",
    };

    let cores = BoxRow {
        resource: "Cores",
        used: machine.cpu_millicores_busy.map(fmt::cores),
        allowed: machine.cpu_millicores_total.map(fmt::cores),
        remaining: match (machine.cpu_millicores_total, machine.cpu_millicores_busy) {
            (Some(total), Some(busy)) => Some(fmt::cores(total.saturating_sub(busy))),
            _ => None,
        },
        shape: Shape::Reading,
        phrase: describe("measured"),
        fill: None,
        caveat: "\u{201c}used\u{201d} is the one-minute load average of the WHOLE box, not \
                 Gunnar's own CPU time, and it is an approximation",
    };

    // RAM has no floor: every byte `MemTotal` counts may be used, so nothing
    // is held back out of "allowed".
    let ram = triple_of(machine.ram_bytes_total, machine.ram_bytes_available, 0);
    let ram = BoxRow {
        resource: "RAM",
        used: ram.map(|(used, _, _)| fmt::bytes(used)),
        allowed: ram.map(|(_, allowed, _)| fmt::bytes(allowed)),
        remaining: ram.map(|(_, _, left)| fmt::bytes(left)),
        shape: Shape::Reading,
        phrase: describe("measured"),
        fill: None,
        caveat: "\u{201c}remaining\u{201d} is MemAvailable, which counts the page cache as \
                 free, as the kernel does",
    };

    Some(vec![disk, cores, ram])
}

#[cfg(test)]
mod tests {
    use super::*;

    /// gunnar's own table (`gunnar_grpc::service::meters::METERS`) as it
    /// reaches this page: three walls, four readings, all seven present on
    /// every read whether or not there is a cap to put beside them.
    fn table() -> BTreeMap<String, String> {
        [
            ("pack_bytes", "enforced"),
            ("cache_bytes", "enforced"),
            ("cold_bytes", "measured"),
            ("lfs_bytes", "enforced"),
            ("tombstoned_bytes", "measured"),
            ("repos", "measured"),
            ("objects", "measured"),
        ]
        .into_iter()
        .map(|(m, w)| (m.to_owned(), w.to_owned()))
        .collect()
    }

    /// A namespace with a cap on a wall and a cap on a reading — the two rows
    /// the whole defect is about, side by side, so a renderer that drew them
    /// the same could not pass.
    fn usage(enforcement: BTreeMap<String, String>) -> Usage {
        Usage {
            namespace: "alpha".to_owned(),
            pack_bytes: 6_000_000,
            cache_bytes: 1_572_864,
            cold_bytes: 5_242_880,
            lfs_bytes: 0,
            tombstoned_bytes: 3_145_728,
            repos: 9,
            objects: 41_237,
            measured_at_unix_ms: Some(1_788_000_000_000),
            // The flow half. This fixture is about the Room table's shapes and
            // nothing here feeds it — the numbers are non-zero anyway, so a row
            // that started reading one of them by mistake would be visible.
            bytes_in_today: 811_923,
            bytes_out_today: 4_402_117,
            pushes_today: 3,
            fetches_today: 27,
            refused_today: 2,
            last_push_unix_ms: Some(1_787_999_100_000),
            last_fetch_unix_ms: Some(1_787_999_940_000),
            caps: PerMeter {
                pack_bytes: Some(8_000_000),
                // The auditor's example: five bought, nine held, and nothing
                // on the box refuses the tenth.
                repos: Some(12),
                ..PerMeter::default()
            },
            enforcement,
            remaining: PerMeter::default(),
            machine: None,
            box_unreadable: String::new(),
        }
    }

    fn row<'rows>(rows: &'rows [RoomRow], meter: &str) -> &'rows RoomRow {
        rows.iter()
            .find(|r| r.meter == meter)
            .expect("the row is in the table")
    }

    /// ★ **An enforced cap is a wall; a measured cap is a reading.**
    ///
    /// Seen RED against the table as it stood — every row drawn identically,
    /// which is the state this whole change is about:
    ///
    /// ```text
    /// ---- pages::usage::tests::a_wall_is_drawn_as_a_wall_and_a_reading_is_not stdout ----
    /// `Repositories` is a cap of 12 that nothing on the box refuses the
    /// thirteenth of, and it is drawn with a bar: Some(75)
    /// ```
    #[test]
    fn a_wall_is_drawn_as_a_wall_and_a_reading_is_not() {
        let rows = room_rows(&usage(table()));

        let pack = row(&rows, "Not re-derivable");
        assert_eq!(pack.shape, Shape::Wall);
        assert_eq!(
            pack.fill,
            Some(75),
            "6 MB of an 8 MB cap gunnar cuts a push at is three quarters of a wall"
        );
        assert_eq!(
            pack.phrase, "the product refuses when over",
            "monetize's own sentence, not this console's paraphrase"
        );

        let repos = row(&rows, "Repositories");
        assert_eq!(repos.shape, Shape::Reading);
        assert_eq!(
            repos.fill, None,
            "`Repositories` is a cap of 12 that nothing on the box refuses the thirteenth \
             of, and it is drawn with a bar: {:?}",
            repos.fill
        );
        assert_eq!(repos.phrase, "counted and reported; nothing refuses");
        // And the number is still SHOWN. Hiding it was the other candidate
        // fix and it is the wrong one: the tenant bought it.
        assert_eq!(repos.allowed.as_deref(), Some("12"));
    }

    /// **A wall with no cap has no bar**, because a bar needs both ends. The
    /// door on `cache_bytes` exists on every gunnar; on a namespace nothing was
    /// sold against, there is no ceiling to fill towards, and a full-width
    /// empty track would read as "you have room" — a claim about a cap that is
    /// not there.
    #[test]
    fn a_door_nothing_was_sold_against_draws_no_bar() {
        let rows = room_rows(&usage(table()));
        let cache = row(&rows, "Read cache");
        assert_eq!(
            cache.shape,
            Shape::Wall,
            "the explode budget is a door on every gunnar"
        );
        assert_eq!(cache.fill, None, "there is no cap here to fill towards");
        assert_eq!(cache.allowed, None, "…and the Allowed column says so");
        assert_eq!(cache.phrase, "the product refuses when over");
    }

    /// ★ **A gunnar that said nothing is not a gunnar with no doors.**
    ///
    /// The self-hoster's box: older than `Allowance.enforcement`, sends an
    /// empty map, and still cuts a push mid-stream at `pack_bytes`. Drawing
    /// that as seven readings would tell its operator their pack quota is a
    /// line item; drawing it as seven walls would invent three doors. It gets
    /// neither, and the cell says why.
    ///
    /// Seen red against a `shape_of` whose fallback was `Shape::Reading`:
    /// *"a box that did not say was read as a box that refuses nothing"*.
    #[test]
    fn a_box_that_did_not_say_is_neither_a_wall_nor_a_reading() {
        let rows = room_rows(&usage(BTreeMap::new()));

        for label in ["Not re-derivable", "Read cache", "Repositories"] {
            let r = row(&rows, label);
            assert_eq!(
                r.shape,
                Shape::NotSaid,
                "a box that did not say was read as a box that refuses nothing: {label}"
            );
            assert_eq!(
                r.fill, None,
                "{label}: no ceiling was reported, so none is drawn"
            );
            assert_eq!(
                r.phrase, "",
                "{label}: no sentence was sent, so none is quoted"
            );
        }
        // The caps themselves are unaffected. An older gunnar with a signed
        // fact still reports the numbers, and the page still shows them.
        assert_eq!(
            row(&rows, "Not re-derivable").allowed.as_deref(),
            Some("7.6 MiB")
        );
    }

    /// The bar's arithmetic, away from the table.
    ///
    /// Over the cap saturates at a full bar rather than running off the end: a
    /// fact can be lowered under a namespace that is already past it, which is
    /// a real state, and a 140 %-wide bar would be a rendering bug that reads
    /// as a data bug.
    #[test]
    fn the_bar_fills_towards_the_cap_and_stops_there() {
        assert_eq!(fill_pct(0, Some(100)), Some(0));
        assert_eq!(fill_pct(75, Some(100)), Some(75));
        assert_eq!(fill_pct(100, Some(100)), Some(100));
        assert_eq!(
            fill_pct(140, Some(100)),
            Some(100),
            "over the cap is a full bar, not 140 %"
        );
        assert_eq!(fill_pct(7, None), None, "no cap, no bar");
        // A byte count large enough to overflow `u64` when multiplied by 100 —
        // the arithmetic goes through `u128` for exactly this, and a wrapped
        // percentage would draw a nearly-empty bar for a nearly-full disk.
        assert_eq!(fill_pct(u64::MAX / 2, Some(u64::MAX)), Some(49));
    }

    /// The three words gunnar can send and the sentences they carry, held to
    /// `monetize_product::Enforcement::describe()` character for character.
    /// A word this build has not learned carries no sentence at all rather
    /// than a guess about somebody's plan.
    #[test]
    fn the_words_are_monetizes_own() {
        assert_eq!(describe("enforced"), "the product refuses when over");
        assert_eq!(
            describe("measured"),
            "counted and reported; nothing refuses"
        );
        assert_eq!(
            describe("provisioned"),
            "provisioned at the cloud provider; not policed by the product"
        );
        assert_eq!(describe("hard"), "", "a word from nowhere gets no sentence");

        let mut sent = BTreeMap::new();
        sent.insert("pack_bytes".to_owned(), "enforced".to_owned());
        assert_eq!(shape_of(&sent, "pack_bytes"), Shape::Wall);
        // A meter the map does not mention is the same silence as no map.
        assert_eq!(shape_of(&sent, "repos"), Shape::NotSaid);
        // `provisioned` is iron bought at the provider; it is certainly not a
        // door this box refuses at, so it draws as a reading.
        sent.insert("repos".to_owned(), "provisioned".to_owned());
        assert_eq!(shape_of(&sent, "repos"), Shape::Reading);
    }

    // ── the BOX table: what room there is, beside what a namespace is allowed ──

    /// The live appliance, verified against `gunnar control capacity --at
    /// 10.13.8.99:50051 --bytes 0` run from the front on 2026-09-20: a
    /// 4×10.0G raid0 xfs set, 4 000 millicores of affinity mask, a load of
    /// 0.01 and 15.6 GiB of RAM. The numbers are that read's, byte for byte,
    /// so a cell that changes shape here can be held against a real answer.
    fn the_box() -> Machine {
        Machine {
            total_bytes: 42_876_272_640,
            free_bytes: 42_726_699_008,
            servable_bytes: 40_579_215_360,
            growth: "at_runtime".to_owned(),
            floor_bytes: 2_147_483_648,
            cpu_millicores_total: Some(4_000),
            cpu_millicores_busy: Some(10),
            ram_bytes_total: Some(16_791_379_968),
            ram_bytes_available: Some(16_392_073_216),
            machine_reason: "This box: 4 CPUs = 4000 millicores gunnar may run on".to_owned(),
        }
    }

    fn box_row<'rows>(rows: &'rows [BoxRow], resource: &str) -> &'rows BoxRow {
        rows.iter()
            .find(|r| r.resource == resource)
            .expect("the row is in the box table")
    }

    /// ★ **The box's three resources, from the appliance's own answer.**
    ///
    /// Seen RED before `box_rows` existed — `cargo test -p gunnar-ui-web` on
    /// oden, `error[E0425]: cannot find function 'box_rows' in this scope`:
    /// the page had the box's disk in a sentence and had never been told the
    /// cores or the RAM at all, though `Admin.Capacity` has measured both
    /// since fields 14–18 landed on `CapacityResponse`.
    #[test]
    fn the_box_answers_for_disk_cores_and_ram() {
        let rows = box_rows(Some(&the_box())).expect("there is a box");
        assert_eq!(rows.len(), 3, "disk, cores, RAM — and no fourth invented");

        let disk = box_row(&rows, "Disk");
        assert_eq!(disk.used.as_deref(), Some("142.6 MiB"));
        assert_eq!(
            disk.allowed.as_deref(),
            Some("37.9 GiB"),
            "allowed is the set minus the 2.0 GiB floor, not the 39.9 GiB set"
        );
        assert_eq!(disk.remaining.as_deref(), Some("37.8 GiB"));

        let cores = box_row(&rows, "Cores");
        assert_eq!(cores.allowed.as_deref(), Some("4.00 cores"));
        assert_eq!(
            cores.used.as_deref(),
            Some("0.01 cores"),
            "a load of 0.01 is a true reading and must not round away to 0.0"
        );
        assert_eq!(cores.remaining.as_deref(), Some("3.99 cores"));

        let ram = box_row(&rows, "RAM");
        assert_eq!(ram.allowed.as_deref(), Some("15.6 GiB"));
        assert_eq!(ram.remaining.as_deref(), Some("15.3 GiB"));
        assert_eq!(ram.used.as_deref(), Some("380.8 MiB"));
    }

    /// ★ **Disk is a wall; cores and RAM are readings.**
    ///
    /// The same rule the Room table already enforces, and the reason it must
    /// hold here too: a bar IS a ceiling. gunnar's disk gate refuses a write
    /// under the floor, so the disk row has one. Nothing in this product
    /// refuses a push for load or for RAM — they are bought at the cloud
    /// provider — so a bar on either would be a door that does not exist.
    ///
    /// Seen red against a first draft that gave every box row `fill_pct`:
    /// *"Cores is drawn with a bar: Some(0)"*.
    #[test]
    fn only_the_disk_row_is_a_wall() {
        let rows = box_rows(Some(&the_box())).expect("there is a box");

        let disk = box_row(&rows, "Disk");
        assert_eq!(disk.shape, Shape::Wall);
        assert_eq!(disk.fill, Some(0), "142.6 MiB of 37.9 GiB rounds to 0 %");
        assert_eq!(disk.phrase, "the product refuses when over");

        for resource in ["Cores", "RAM"] {
            let row = box_row(&rows, resource);
            assert_eq!(row.shape, Shape::Reading, "{resource}");
            assert_eq!(row.fill, None, "{resource} is drawn with a bar: {:?}", row.fill);
            // `measured`, NOT `provisioned` — see [`BoxRow::phrase`]. The
            // `provisioned` sentence names a cloud provider, which is a claim
            // about a purchase on a console that carries none and is simply
            // untrue on a self-hoster's own metal.
            assert_eq!(
                row.phrase, "counted and reported; nothing refuses",
                "{resource}: monetize's own sentence, not a phrasing coined here"
            );
            assert!(
                !row.phrase.contains("cloud") && !row.phrase.contains("provider"),
                "{resource}: the open console told a self-hoster their cores came from a \
                 cloud provider: {}",
                row.phrase
            );
        }
    }

    /// ★ **used + remaining = allowed, on the numbers.**
    ///
    /// Asserted on [`triple`] and not on the cells, because `142.6 MiB` plus
    /// `37.8 GiB` does not visibly equal `37.9 GiB` and a test that read the
    /// words could not tell a closing sum from a short one.
    ///
    /// Seen red against a draft whose disk allowed was `total_bytes`: *"the
    /// floor was offered as room: 149 573 632 + 40 579 215 360 =
    /// 40 728 788 992, not 42 876 272 640"*.
    #[test]
    fn the_three_numbers_close() {
        let (used, allowed, left) = triple(42_876_272_640, 40_579_215_360, 2_147_483_648);
        assert_eq!(used + left, allowed, "the floor was offered as room");
        assert_eq!(allowed, 40_728_788_992);
        assert_eq!(used, 149_573_632);

        // No floor: allowed is the whole total and the sum still closes.
        let (used, allowed, left) = triple(16_791_379_968, 16_392_073_216, 0);
        assert_eq!(used + left, allowed);
        assert_eq!(used, 399_306_752);

        // More left than there is allowed — a floor raised under a set that
        // was already fuller than it. `remaining` is clamped to `allowed`
        // rather than making `used` wrap to a set-sized number.
        let (used, allowed, left) = triple(100, 90, 50);
        assert_eq!((used, allowed, left), (0, 50, 50));
    }

    /// **A box that did not measure a resource says nothing about it**, in
    /// every column, and never a zero.
    ///
    /// `Admin.Capacity` sends these fields `optional` for exactly this: a
    /// caller that reads a `0` has not been told the box is empty, it has been
    /// told nothing. A `0.00 cores` row would say this appliance has no CPU.
    ///
    /// Seen red against a draft that used `unwrap_or_default()`: *"an
    /// unmeasured resource was drawn as an empty machine: Some(\"0.00
    /// cores\")"*.
    #[test]
    fn an_unmeasured_resource_is_absent_not_zero() {
        let mut machine = the_box();
        machine.cpu_millicores_total = None;
        machine.cpu_millicores_busy = None;
        machine.ram_bytes_total = None;
        machine.ram_bytes_available = None;
        let rows = box_rows(Some(&machine)).expect("there is still a box");

        for resource in ["Cores", "RAM"] {
            let row = box_row(&rows, resource);
            assert_eq!(
                (&row.used, &row.allowed, &row.remaining),
                (&None, &None, &None),
                "an unmeasured resource was drawn as an empty machine: {resource}"
            );
        }
        // The disk is unaffected: it comes from the `statvfs` without which
        // there would be no box on the page at all.
        assert_eq!(box_row(&rows, "Disk").allowed.as_deref(), Some("37.9 GiB"));

        // Half an answer is still an answer: the mask was read, the load was
        // not, and the row keeps the column it has.
        let mut machine = the_box();
        machine.cpu_millicores_busy = None;
        let rows = box_rows(Some(&machine)).expect("there is a box");
        let cores = box_row(&rows, "Cores");
        assert_eq!(cores.allowed.as_deref(), Some("4.00 cores"));
        assert_eq!(cores.used, None, "no load was measured, so none is drawn");
        assert_eq!(cores.remaining, None, "and nothing may be subtracted from it");
    }

    /// **A load above the cores gunnar may run on saturates at no remainder**
    /// and is still reported as it was read. The one row that does not close,
    /// and the caveat is what makes it honest rather than wrong.
    #[test]
    fn a_load_above_the_mask_leaves_no_remainder() {
        let mut machine = the_box();
        machine.cpu_millicores_busy = Some(6_200);
        let rows = box_rows(Some(&machine)).expect("there is a box");
        let cores = box_row(&rows, "Cores");
        assert_eq!(cores.used.as_deref(), Some("6.20 cores"));
        assert_eq!(cores.allowed.as_deref(), Some("4.00 cores"));
        assert_eq!(
            cores.remaining.as_deref(),
            Some("0.00 cores"),
            "a negative remainder would have wrapped to 18 exa-cores"
        );
        assert!(
            cores.caveat.contains("approximation"),
            "the load is not gunnar's CPU time and the row must say so"
        );
    }

    /// No box, no table — and not a table of zeroes. The console that may
    /// read one namespace and not the server draws the Room table and nothing
    /// here, which is the behaviour `box_line` already had.
    #[test]
    fn no_box_draws_no_box_table() {
        assert!(box_rows(None).is_none());
    }
}