facett-arrow/tests/matrix.rs
//! **The functional matrix cell for facett-arrow** — the crate emitted NO
//! testmatrix row of either aspect (see
//! `.nornir/docs/test-matrix-inventory-history.md`).
//!
//! `facett-arrow` is an ADAPTER, not a pane: its contract is "an Arrow
//! `RecordBatch` becomes something facett can actually DRAW". So the oracle is
//! not a count — it is the adapter's output driven through the real render probe.
//! The batch is converted, handed to the pane that consumes it, and
//! `facett_core::testmatrix::probe_and_emit` proves the pane tessellates real
//! vertices from Arrow-derived data. ONE call per lane, shared probe (L5).
use std::sync::Arc;
use arrow_array::{Int64Array, RecordBatch, StringArray};
use arrow_schema::{DataType, Field, Schema};
use facett_arrow::{scene_from_edge_batch, table_from_batch};
/// A four-edge social batch with labels on both endpoints (so node colouring by
/// label is exercised) — the shared fixture for both lanes below.
fn edge_batch() -> RecordBatch {
RecordBatch::try_new(
Arc::new(Schema::new(vec![
Field::new("src", DataType::Int64, false),
Field::new("dst", DataType::Int64, false),
Field::new("src_label", DataType::Utf8, false),
Field::new("dst_label", DataType::Utf8, false),
])),
vec![
Arc::new(Int64Array::from(vec![1, 1, 2, 3])),
Arc::new(Int64Array::from(vec![2, 3, 4, 4])),
Arc::new(StringArray::from(vec!["Person", "Person", "Company", "Address"])),
Arc::new(StringArray::from(vec!["Company", "Address", "Bank", "Bank"])),
],
)
.unwrap()
}
#[test]
fn arrow_edges_become_a_graph_that_actually_draws() {
let scene = scene_from_edge_batch(&edge_batch()).expect("the batch converts");
assert_eq!(scene.nodes.len(), 4, "ids 1..4 are distinct nodes");
assert_eq!(scene.edges.len(), 4);
let mut view = facett_graph::GraphView::new(scene);
let probe = facett_core::testmatrix::probe_and_emit("facett-arrow.scene", &mut view, &[]);
assert!(probe.drew(), "an Arrow-derived scene must tessellate to vertices");
assert_eq!(probe.initial_state["nodes"], 4, "the pane sees every Arrow node");
assert_eq!(probe.initial_state["edges"], 4, "the pane sees every Arrow edge");
}
#[test]
fn arrow_columns_become_a_table_that_actually_draws() {
let batch = RecordBatch::try_new(
Arc::new(Schema::new(vec![
Field::new("id", DataType::Int64, false),
Field::new("name", DataType::Utf8, false),
])),
vec![
Arc::new(Int64Array::from(vec![1, 2, 3])),
Arc::new(StringArray::from(vec!["knut", "korp", "nornir"])),
],
)
.unwrap();
let mut view = table_from_batch(&batch, "repos");
let probe = facett_core::testmatrix::probe_and_emit("facett-arrow.table", &mut view, &[]);
assert!(probe.drew(), "an Arrow-derived table must tessellate to vertices");
assert_eq!(probe.initial_state["rows"], 3);
let cols: Vec<&str> =
probe.initial_state["columns"].as_array().unwrap().iter().map(|c| c.as_str().unwrap()).collect();
assert_eq!(cols, ["id", "name"]);
}
/// RED-when-broken: the error path had only an `is_err()` oracle with no message
/// contract. Pin WHICH column is reported missing — a converter that blames the
/// wrong column sends a host hunting the wrong schema field.
#[test]
fn a_batch_without_the_edge_columns_names_the_missing_one() {
let batch = RecordBatch::try_new(
Arc::new(Schema::new(vec![Field::new("x", DataType::Int64, false)])),
vec![Arc::new(Int64Array::from(vec![1]))],
)
.unwrap();
let Err(err) = scene_from_edge_batch(&batch) else { panic!("a schema without src/dst must be rejected") };
assert!(err.contains("src") || err.contains("dst"), "the error must name the column: {err}");
// A batch with `src` but no `dst` is equally rejected (not half-converted).
let half = RecordBatch::try_new(
Arc::new(Schema::new(vec![Field::new("src", DataType::Int64, false)])),
vec![Arc::new(Int64Array::from(vec![1]))],
)
.unwrap();
assert!(scene_from_edge_batch(&half).is_err(), "a half schema must not half-convert");
}