facett-flow/tests/contracts.rs
use facett_core::{Elm, Facet, LookTheme};
use facett_flow::{Camera, FlowGraph, Graph, Msg};
use std::sync::Arc;
#[test]
fn real_10k_fixture_and_state_roundtrip() {
let graph = Graph::demo(10_000);
assert_eq!(graph.nodes.len(), 10_000);
assert_eq!(graph.edges.len(), 16_000);
graph.validate().unwrap();
let flow = FlowGraph::new("test", graph).unwrap();
let model = serde_json::from_value(flow.state_json()).unwrap();
let loaded = FlowGraph::from_model(model).unwrap();
assert_eq!(loaded.state(), flow.state());
}
#[test]
fn rejects_invalid_graph_and_state_without_mutation() {
let mut graph = Graph::demo(100);
graph.edges[0].to = "missing".into();
assert!(graph.validate().is_err());
graph = Graph::demo(100);
graph.nodes[0].position[1] = f32::INFINITY;
assert!(graph.validate().is_err());
let mut flow = FlowGraph::new("test", Graph::demo(100)).unwrap();
let before = flow.state().clone();
Elm::update(&mut flow, Msg::Replace(Arc::new(graph)));
Elm::update(&mut flow, Msg::Tick(f64::NAN));
Elm::update(
&mut flow,
Msg::Camera(Camera {
centre: [0.0, 0.0],
zoom: f32::NAN,
}),
);
Elm::update(&mut flow, Msg::Select(Some("absent".into())));
assert_eq!(&before, flow.state());
let mut invalid = before;
invalid.camera.zoom = 0.0;
assert!(FlowGraph::from_model(invalid).is_err());
}
#[test]
fn pause_reduced_motion_and_extreme_clock_are_safe() {
let mut flow = FlowGraph::local();
Elm::update(&mut flow, Msg::Tick(2.0));
Elm::update(&mut flow, Msg::Running(false));
Elm::update(&mut flow, Msg::Tick(1.0));
assert_eq!(flow.state().seconds, 2.0);
Elm::update(&mut flow, Msg::Running(true));
Elm::update(&mut flow, Msg::ReducedMotion(true));
Elm::update(&mut flow, Msg::Tick(1.0));
assert_eq!(flow.state().seconds, 2.0);
Elm::update(&mut flow, Msg::ReducedMotion(false));
Elm::update(&mut flow, Msg::Tick(f64::MAX));
assert!(flow.state().seconds.is_finite());
}
#[test]
fn stable_selection_survives_reorder_and_clears_deletion() {
let mut flow = FlowGraph::new("test", Graph::demo(100)).unwrap();
Elm::update(&mut flow, Msg::Select(Some("node-0000000".into())));
let mut graph = (*flow.state().graph).clone();
graph.nodes.reverse();
Elm::update(&mut flow, Msg::Replace(Arc::new(graph)));
assert_eq!(flow.state().selected.as_deref(), Some("node-0000000"));
Elm::update(
&mut flow,
Msg::Replace(Arc::new(Graph {
nodes: Vec::new(),
edges: Vec::new(),
})),
);
assert!(flow.state().selected.is_none());
}
#[test]
fn bounded_cpu_preview_paints_with_each_host_theme() {
for theme in [
LookTheme::nordisk_dark(),
LookTheme::nordisk_light(),
LookTheme::device(),
] {
let mut flow = FlowGraph::local();
let report = facett_core::harness::render_themed_sized(
&mut flow,
theme.to_legacy_palette(),
(1000.0, 700.0),
);
assert!(report.drew());
}
}
#[test]
fn flow_shader_parses_and_validates_before_window_start() {
let source = include_str!("../src/flow.wgsl");
let module = naga::front::wgsl::parse_str(source)
.unwrap_or_else(|e| panic!("{}", e.emit_to_string(source)));
let mut validator = naga::valid::Validator::new(
naga::valid::ValidationFlags::all(),
naga::valid::Capabilities::empty(),
);
validator
.validate(&module)
.unwrap_or_else(|e| panic!("{}", e.emit_to_string(source)));
}
#[test]
fn hundred_thousand_node_fixture_has_balanced_bounds_and_exact_counts() {
let flow = FlowGraph::new("100k", Graph::demo(100_000)).unwrap();
assert_eq!(flow.render_counts(), (100_000, 160_000));
let bounds = flow.bounds();
assert!((1.0..2.0).contains(&(bounds.width() / bounds.height())));
assert!(
flow.state()
.graph
.nodes
.iter()
.all(|n| bounds.contains(egui::pos2(n.position[0], n.position[1])))
);
let mut flow = flow;
Elm::update(&mut flow, Msg::Clustered(true));
assert_eq!(flow.render_counts().0, 160);
assert!(flow.render_counts().1 > 0 && flow.render_counts().1 < 160_000);
Elm::update(&mut flow, Msg::Clustered(false));
assert_eq!(flow.render_counts(), (100_000, 160_000));
}
#[test]
fn central_hub_fixture_preserves_every_node_and_flow_direction() {
let graph = Graph::hub_demo(100_000);
graph.validate().unwrap();
assert_eq!(graph.nodes.len(), 100_000);
assert_eq!(graph.edges.len(), 99_999);
let hub = &graph.nodes[0];
assert_eq!(hub.kind, 3);
assert_eq!(hub.position, [0.0, 0.0]);
for (node, edge) in graph.nodes.iter().skip(1).zip(&graph.edges) {
if node.kind == 0 {
assert_eq!(edge.from, node.id);
assert_eq!(edge.to, hub.id);
} else {
assert_eq!(edge.from, hub.id);
assert_eq!(edge.to, node.id);
}
}
let mut flow = FlowGraph::new("hub", graph).unwrap();
Elm::update(&mut flow, Msg::Clustered(true));
assert_eq!(flow.render_counts(), (25, 24));
Elm::update(&mut flow, Msg::Clustered(false));
assert_eq!(flow.render_counts(), (100_000, 99_999));
}