//! Every widget the registry knows must be listed in the node catalog. //! //! The editor's palette is built from `init_node_types()`. A widget that exists in //! `WidgetRegistry` but has no catalog entry is real, executes, generates code — and //! cannot be found by a person using the tool. It is the same gap that had the instrument //! catalogue, the forge, energy metering and dialect import reachable by an agent and by //! nobody else, one level further in. use flowrs_core::graph::nodes::init_node_types; use flowrs_core::graph::widget::WidgetRegistry; use std::collections::HashSet; #[test] fn every_registry_widget_is_in_the_palette() { let catalog: HashSet = init_node_types().iter().map(|n| n.name.clone()).collect(); let reg = WidgetRegistry::with_builtins(); // A comparison between two empty sets passes and proves nothing; several guards in // this repo have done exactly that. Pin that both sides are populated. assert!( catalog.len() > 200, "catalog looks empty: {}", catalog.len() ); assert!( reg.all().len() > 100, "registry looks empty: {}", reg.all().len() ); let missing: Vec = reg .all() .iter() .map(|w| w.id()) .filter(|id| !catalog.contains(id)) .collect(); assert!( missing.is_empty(), "{} widget(s) exist and cannot be found in the palette:\n {}", missing.len(), missing.join("\n ") ); } /// And the other direction: a catalog entry with no widget and no primitive behind it /// would be a palette tile that produces a node nothing can run or emit. #[test] fn every_palette_entry_has_something_behind_it() { let reg = WidgetRegistry::with_builtins(); let orphans: Vec = init_node_types() .iter() .filter(|n| reg.get(&n.name).is_none()) // Primitives are implemented in codegen rather than the registry; `node_coverage` // is what proves those emit, so an entry is only an orphan if neither knows it. .filter(|n| { let mut g = flowrs_core::graph::Graph::new(); g.add_node(&n.name, 0.0, 0.0); match g.to_rust() { Ok(src) => src.contains(&format!("// TODO: unsupported node type `{}`", n.name)), Err(e) => { !e.contains("is not connected") && !e.contains("only valid inside a loop") } } }) .map(|n| n.name.clone()) .collect(); assert!( orphans.is_empty(), "{} palette entries have no widget and emit nothing:\n {}", orphans.len(), orphans.join("\n ") ); }