crates/knut-bifrost/src/testmatrix.rs
//! Functional-status emitters for the `nornir-testmatrix` matrix.
//!
//! A component reports whether it *actually works* from the inside: during a
//! self-test / headless path it calls [`emit`]`(component, check, ok, detail)`,
//! which forwards to `nornir_testmatrix::functional_status`. With the
//! `testmatrix` cargo feature ON the record lands in the matrix's process-global
//! buffer (drained into a RED/GREEN `functional` row by `Aspect::Functional`);
//! with the feature OFF (the release default) [`emit`] is an inlined no-op and
//! `nornir-testmatrix` is not even pulled into the build.
//!
//! The component name reported by bifröst's checks.
pub const COMPONENT: &str = "knut-bifrost";
/// Forward one functional-status check to the matrix. Feature ON → records;
/// feature OFF → inlined no-op (zero release cost). Call sites that build an
/// *expensive* `detail` string should additionally wrap the whole block in
/// `#[cfg(feature = "testmatrix")]` so the formatting is stripped too.
#[cfg(feature = "testmatrix")]
#[inline]
pub fn emit(component: &str, check: &str, ok: bool, detail: &str) {
nornir_testmatrix::functional_status(component, check, ok, detail);
}
/// No-op when the `testmatrix` feature is OFF (release builds).
#[cfg(not(feature = "testmatrix"))]
#[inline]
pub fn emit(_component: &str, _check: &str, _ok: bool, _detail: &str) {}
/// Headless self-test of bifröst's core transform→mutation path — no FalkorDB,
/// no Spark, no files. Builds a known two-row table, runs the real
/// [`crate::VertexTransform`] over it, and reports each key invariant as a
/// functional-status check the matrix reads. Returns `true` iff every check
/// passed. Safe to call from the CLI's `--self-test` and from a test.
pub fn self_test() -> bool {
use crate::transform::{TableContext, Transform, VertexTransform};
use arrow_array::{Int64Array, RecordBatch, StringArray};
use arrow_schema::{DataType, Field, Schema};
use std::sync::Arc;
let schema = Arc::new(Schema::new(vec![
Field::new("user_id", DataType::Int64, false),
Field::new("name", DataType::Utf8, false),
]));
let batch = RecordBatch::try_new(
schema,
vec![
Arc::new(Int64Array::from(vec![1_i64, 2])),
Arc::new(StringArray::from(vec!["alice", "bob"])),
],
)
.expect("self_test batch");
let t = VertexTransform {
label: "Person".into(),
id_column: "user_id".into(),
properties: vec!["user_id".into(), "name".into()],
rename: Default::default(),
};
let ctx = TableContext {
table: "users".into(),
};
let mut all_ok = true;
match t.apply(&ctx, &[batch]) {
Ok(m) => {
let one_vertex_set = m.vertices.len() == 1;
emit(
COMPONENT,
"self_test_vertex_set_built",
one_vertex_set,
&format!("{} vertex set(s)", m.vertices.len()),
);
all_ok &= one_vertex_set;
if let Some(set) = m.vertices.first() {
let label_ok = set.label == "Person";
emit(
COMPONENT,
"self_test_label_mapped",
label_ok,
&format!("label={}", set.label),
);
all_ok &= label_ok;
let rows = set.batch.num_rows();
let rows_ok = rows == 2;
emit(
COMPONENT,
"self_test_row_count",
rows_ok,
&format!("{rows} vertex rows"),
);
all_ok &= rows_ok;
} else {
all_ok = false;
}
}
Err(e) => {
emit(
COMPONENT,
"self_test_vertex_set_built",
false,
&format!("transform errored: {e}"),
);
all_ok = false;
}
}
emit(COMPONENT, "self_test", all_ok, "core transform path");
all_ok
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn self_test_passes_and_is_idempotent() {
// INJECT-ASSERT: the headless self-test runs the real transform path and
// every invariant must hold. (It also emits functional-status rows when
// built `--features testmatrix`.)
assert!(self_test(), "core transform self-test must pass");
assert!(self_test(), "self-test is idempotent");
emit(
COMPONENT,
"self_test_test_ran",
true,
"self_test() returned true twice",
);
}
}