crates/knut-bifrost/tests/wasm_guest.rs
//! End-to-end test of a **real `wasm32` guest** against the `WasmTransform`
//! host (`src/wasm.rs`). The guest is the crate in `wasm-guest/`, which reads
//! the input Arrow-IPC stream, **keeps only rows where `_gar_id` is even**, and
//! writes the result back as Arrow IPC — proving arrow-on-wasm works.
//!
//! `#[ignore]` because it depends on the wasm artifact being **pre-built**:
//!
//! ```sh
//! cargo build -p knut-bifrost-wasm-guest \
//! --target wasm32-unknown-unknown --release \
//! --manifest-path crates/knut-bifrost/wasm-guest/Cargo.toml
//! ```
//!
//! Then run it (feature `wasm`, not ignored):
//!
//! ```sh
//! cargo test -p knut-bifrost --features wasm --test wasm_guest -- --ignored
//! ```
#![cfg(feature = "wasm")]
use std::path::PathBuf;
use std::sync::Arc;
use arrow_array::{Int64Array, RecordBatch, StringArray};
use arrow_schema::{DataType, Field, Schema};
use knut_bifrost::transform::{TableContext, Transform};
use knut_bifrost::wasm::WasmTransform;
/// Path to the pre-built guest artifact, relative to this crate's manifest dir.
fn guest_wasm_path() -> PathBuf {
PathBuf::from(env!("CARGO_MANIFEST_DIR"))
.join("wasm-guest/target/wasm32-unknown-unknown/release/knut_bifrost_wasm_guest.wasm")
}
fn sample() -> RecordBatch {
RecordBatch::try_new(
Arc::new(Schema::new(vec![
Field::new("_gar_id", DataType::Int64, false),
Field::new("name", DataType::Utf8, false),
])),
vec![
Arc::new(Int64Array::from(vec![1, 2, 3, 4, 5, 6])),
Arc::new(StringArray::from(vec!["a", "b", "c", "d", "e", "f"])),
],
)
.unwrap()
}
#[test]
#[ignore = "needs the wasm32 guest artifact pre-built; see module docs"]
fn real_wasm_guest_keeps_only_even_gar_ids() {
let path = guest_wasm_path();
assert!(
path.exists(),
"guest wasm not built at {path:?}\n build it with:\n cargo build -p knut-bifrost-wasm-guest \
--target wasm32-unknown-unknown --release \
--manifest-path crates/knut-bifrost/wasm-guest/Cargo.toml"
);
let t = WasmTransform::from_file(&path, "Person", "_gar_id").expect("load guest wasm");
let mutation = t
.apply(
&TableContext {
table: "people".into(),
},
&[sample()],
)
.expect("guest transform runs");
assert_eq!(
mutation.vertices.len(),
1,
"one output batch → one vertex set"
);
let vs = &mutation.vertices[0];
assert_eq!(vs.label, "Person");
assert_eq!(vs.id_column, "_gar_id");
// Only even _gar_id rows survive: 2, 4, 6 (names b, d, f).
let ids = vs
.batch
.column_by_name("_gar_id")
.unwrap()
.as_any()
.downcast_ref::<Int64Array>()
.unwrap();
let got: Vec<i64> = ids.iter().map(|v| v.unwrap()).collect();
assert_eq!(got, vec![2, 4, 6], "guest kept only even _gar_id rows");
let names = vs
.batch
.column_by_name("name")
.unwrap()
.as_any()
.downcast_ref::<StringArray>()
.unwrap();
let got_names: Vec<&str> = names.iter().map(|v| v.unwrap()).collect();
assert_eq!(
got_names,
vec!["b", "d", "f"],
"rows stayed aligned through the filter"
);
}