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

nordisk/monetize

crates/estate-svg/tests/golden.rs

//! **The bytes, pinned.** One rich estate — every liveness, a full and an
//! unmeasured disk, measured and declared numbers, a note, the clouds — drawn
//! and compared with a committed file. The skade drawing (monetize's
//! `EstateAdmin.Draw`, gunnar-ui's Machine card) must not move when a theme is
//! added; the white buy page's drawing is pinned beside it.
//!
//! `ESTATE_SVG_BLESS=1 cargo test -p nordisk-estate-svg --test golden` writes
//! the files instead of comparing — after a deliberate change, and the diff is
//! then read in review.

use nordisk_estate_svg::*;

fn sample() -> Estate {
    let m =
        |id: &str, role: Role, live: Liveness, disk: Option<Disk>, measured: bool, note: &str| {
            Machine {
                id: id.into(),
                role,
                title: String::new(),
                cloud: "upcloud".into(),
                server_id: format!("00{}", id.len()),
                plan: Some("STARTER-4xCPU-8GB".into()),
                declared_vcpu: Some(4),
                declared_ram_mb: Some(8192),
                measured_cpu_millicores: measured.then_some(4000),
                measured_ram_bytes: measured.then_some(8 << 30),
                disk,
                liveness: live,
                note: note.into(),
                headline: String::new(),
            }
        };
    Estate {
        product: "gunnar".into(),
        machines: vec![
            m(
                "gunnar-front",
                Role::Front,
                Liveness::Up,
                Some(Disk {
                    used_bytes: 9 << 30,
                    total_bytes: 40 << 30,
                    growable: false,
                }),
                false,
                "",
            ),
            m(
                "gunnar-appliance",
                Role::Appliance,
                Liveness::Behind,
                Some(Disk {
                    used_bytes: 34 << 30,
                    total_bytes: 40 << 30,
                    growable: true,
                }),
                true,
                "grows in place & says so",
            ),
            m(
                "gunnar-twin",
                Role::Twin,
                Liveness::Silent,
                None,
                false,
                "the log-holder",
            ),
            m("console", Role::Console, Liveness::Unknown, None, false, ""),
        ],
        clouds: vec![
            CloudRow {
                name: "upcloud".into(),
                spends: true,
                is_default: true,
            },
            CloudRow {
                name: "server".into(),
                spends: false,
                is_default: false,
            },
        ],
        tenants: Some(3),
        subtitle: None,
        disk_only: false,
        live: false,
    }
}

fn pinned(file: &str, drawn: &str) {
    let path = std::path::Path::new(env!("CARGO_MANIFEST_DIR"))
        .join("golden")
        .join(file);
    if std::env::var_os("ESTATE_SVG_BLESS").is_some() {
        std::fs::create_dir_all(path.parent().unwrap()).unwrap();
        std::fs::write(&path, drawn).unwrap();
        return;
    }
    let want = std::fs::read_to_string(&path)
        .unwrap_or_else(|e| panic!("{}: {e} — bless it first", path.display()));
    assert!(want == drawn, "{} changed: the drawing is not the committed bytes (bless deliberately, then read the diff)", path.display());
}

#[test]
fn the_skade_drawing_is_byte_for_byte_the_committed_one() {
    pinned("skade.svg", &estate_svg(&sample()));
}

/// **The white buy page's drawing** (owner, 2026-10-04 18:15): black strokes
/// and text on white, grey for the secondary lines, and every state told by
/// line, fill and word — no hue anywhere.
#[test]
fn the_monochrome_drawing_is_black_and_white_and_pinned() {
    let drawn = estate_svg_themed(&sample(), &Theme::MONOCHROME);
    pinned("monochrome.svg", &drawn);
    // Every colour on the board is a grey: r == g == b.
    let mut i = 0;
    while let Some(at) = drawn[i..].find('#') {
        let hex = &drawn[i + at + 1..i + at + 7];
        if hex.chars().all(|c| c.is_ascii_hexdigit()) {
            assert!(
                hex[0..2] == hex[2..4] && hex[2..4] == hex[4..6],
                "#{hex} is not a grey"
            );
        }
        i += at + 1;
    }
    // front | main | twin on top, the console on the row below.
    assert!(drawn.contains(r##"<rect x="0" y="0" width="1180" height="900" fill="url(#board)"/>"##));
    assert!(
        drawn.contains(r##"stop-color="var(--estate-board-top, #ffffff)""##),
        "the ground is white"
    );
    // The states, by shape and word.
    for w in [">up<", ">behind<", ">silent<", ">not asked<"] {
        assert!(drawn.contains(w), "the word {w} is drawn");
    }
    assert!(
        drawn.contains(r#"stroke-dasharray="6 4""#),
        "a silent chip's outline is dashed"
    );
    assert!(
        drawn.contains(r#"stroke-dasharray="2 1.5""#),
        "a silent lamp is a dashed ring"
    );
}

#[test]
fn the_plain_entry_point_is_still_skade() {
    assert_eq!(
        estate_svg(&sample()),
        estate_svg_themed(&sample(), &Theme::SKADE)
    );
}

/// A quote's machines do not exist yet: lit, with the word `planned`, never `up`.
#[test]
fn a_planned_machine_is_lit_and_says_planned() {
    let mut e = sample();
    e.machines
        .iter_mut()
        .for_each(|m| m.liveness = Liveness::Planned);
    let drawn = estate_svg_themed(&e, &Theme::MONOCHROME);
    assert_eq!(drawn.matches(">planned<").count(), 4);
    assert!(!drawn.contains(">up<"));
    assert!(
        !drawn.contains(r#"stroke-dasharray="6 4""#),
        "a planned chip is not drawn dark"
    );
}

/// ★ **Each theme colour is its page token's fallback** (2026-10-05, the
/// theme port): the white buy page's board is drawn with the same
/// `var(--estate-<name>, …)` tokens as skade's, so a page that maps them
/// themes it; a page that maps nothing — the buy page — gets black and white.
#[test]
fn every_monochrome_colour_is_a_page_token_with_a_grey_fallback() {
    let mut e = sample();
    e.live = true;
    for drawn in [
        estate_svg_themed(&sample(), &Theme::MONOCHROME),
        estate_svg_themed(&e, &Theme::MONOCHROME),
    ] {
        let mut seen = 0;
        for attr in [" fill=\"", " stroke=\"", " stop-color=\""] {
            for (i, _) in drawn.match_indices(attr) {
                let v = &drawn[i + attr.len()..];
                let v = &v[..v.find('"').unwrap()];
                seen += 1;
                if v == "none" || v.starts_with("url(#") {
                    continue;
                }
                let inner = v
                    .strip_prefix("var(--estate-")
                    .and_then(|r| r.strip_suffix(')'))
                    .unwrap_or_else(|| panic!("{attr}{v} is not a page token"));
                let (name, hex) = inner
                    .split_once(", #")
                    .unwrap_or_else(|| panic!("{v} has no fallback"));
                assert!(
                    TOKENS.iter().any(|(t, _)| *t == format!("--estate-{name}")),
                    "--estate-{name} is not one of the board's tokens"
                );
                assert!(
                    hex.len() == 6 && hex[0..2] == hex[2..4] && hex[2..4] == hex[4..6],
                    "{v} falls back to a colour that is not a grey"
                );
            }
        }
        assert!(seen > 50, "the board draws at all: {seen}");
    }
}

/// Both themes fill the SAME tokens, in the same order, and skade's are
/// [`TOKENS`] — the list gunnar-ui's console maps.
#[test]
fn both_themes_fill_the_same_tokens_and_skades_are_the_published_list() {
    let (skade, mono) = (Theme::SKADE.tokens(), Theme::MONOCHROME.tokens());
    assert_eq!(&skade[..], TOKENS);
    assert_eq!(skade.map(|(n, _)| n), mono.map(|(n, _)| n));
}