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));
}