test(engine): red — loader coerces non-numeric vector elements to 0.0

iss-loader-vector-element-coercion: the loader's FixedSizeList arm does
val.as_f64().unwrap_or(0.0) — a null element (what json! produces for a
non-finite float) or a string element silently zeroes the vector while
the mutation path rejects the same input loudly. No loader vector
validation had any test coverage; this also pins the existing dimension
check.

Fails as predicted against current code: the null-element load succeeds
(unwrap_err panics on Ok) instead of erroring.
This commit is contained in:
aaltshuler 2026-07-05 05:07:27 +03:00 committed by Andrew Altshuler
parent 10835ca788
commit abc92998bb

View file

@ -112,6 +112,63 @@ async fn init_with(schema: &str, data: &str) -> (tempfile::TempDir, Omnigraph) {
(dir, db)
}
// ─── Vector element validation (loader surface) ─────────────────────────────
const VECTOR_SCHEMA: &str = r#"
node Doc {
slug: String @key
embedding: Vector(3)
}
"#;
/// iss-loader-vector-element-coercion: the loader must reject non-numeric
/// vector elements loudly (parity with the mutation path's "vector elements
/// must be numeric"), never coerce them to 0.0 — a NaN serialized as JSON
/// null (JSON has no NaN) or a string element silently zeroed the vector's
/// direction while passing every dimension check.
#[tokio::test]
async fn non_numeric_vector_element_rejected_on_jsonl_load() {
let (_dir, mut db) = init_with(VECTOR_SCHEMA, "").await;
// A null element (what json! produces for a non-finite float).
let bad_null = r#"{"type":"Doc","data":{"slug":"d1","embedding":[0.1,null,0.3]}}"#;
let err = load_jsonl(&mut db, bad_null, LoadMode::Overwrite)
.await
.unwrap_err();
assert!(
err.to_string().contains("elements must be numeric"),
"null element must be rejected, got: {}",
err
);
// A string element.
let bad_str = r#"{"type":"Doc","data":{"slug":"d2","embedding":[0.1,"0.2",0.3]}}"#;
let err = load_jsonl(&mut db, bad_str, LoadMode::Overwrite)
.await
.unwrap_err();
assert!(
err.to_string().contains("elements must be numeric"),
"string element must be rejected, got: {}",
err
);
// Pin the adjacent (existing) dimension check while we are here — no
// loader vector validation had any coverage before this test.
let bad_dim = r#"{"type":"Doc","data":{"slug":"d3","embedding":[0.1,0.2]}}"#;
let err = load_jsonl(&mut db, bad_dim, LoadMode::Overwrite)
.await
.unwrap_err();
assert!(
err.to_string().contains("expects 3 dimensions, got 2"),
"dimension mismatch must be rejected, got: {}",
err
);
// A valid row still loads.
let good = r#"{"type":"Doc","data":{"slug":"d4","embedding":[0.1,0.2,0.3]}}"#;
load_jsonl(&mut db, good, LoadMode::Overwrite).await.unwrap();
}
// ─── Enum validation ─────────────────────────────────────────────────────────
#[tokio::test]