use reifydb_codec::json::{
NONE_MARKER,
from::{frames_from_envelope, frames_from_json},
none_marker,
};
use reifydb_value::value::{Value, column_view::ColumnView, frame::frame::Frame};
use serde_json::{Value as JsonValue, json, to_string};
fn one_column(ty: JsonValue, payload: Vec<String>) -> JsonValue {
json!([{ "columns": [{ "name": "a", "type": ty, "payload": payload }] }])
}
fn decode(json: JsonValue) -> Result<Vec<Frame>, String> {
frames_from_json(&to_string(&json).unwrap()).map_err(|error| error.to_string())
}
#[test]
fn a_none_marker_in_a_non_option_column_is_a_decode_error_not_a_placeholder() {
let result = decode(one_column(json!({"id": "Int4"}), vec![NONE_MARKER.to_string()]));
assert!(result.is_err(), "expected a decode error, got {result:?}");
}
#[test]
fn a_none_marker_deeper_than_the_option_column_is_a_decode_error_not_clamped() {
let ty = json!({"id": "Option", "underlying": {"id": "Int4"}});
let result = decode(one_column(ty, vec![none_marker(1)]));
assert!(result.is_err(), "expected a decode error, got {result:?}");
}
#[test]
fn an_unknown_frame_op_is_a_decode_error_not_an_absent_op() {
let mut json = one_column(json!({"id": "Int4"}), vec!["7".to_string()]);
json[0]["op"] = json!(9);
let result = decode(json).map(|frames| frames[0].op);
assert!(result.is_err(), "expected a decode error, got {result:?}");
}
#[test]
fn an_envelope_without_a_frames_key_is_a_decode_error_not_zero_frames() {
let result = frames_from_envelope(json!({ "rows": [] })).map(|frames| frames.len());
assert!(result.is_err(), "expected a decode error, got {result:?}");
}
#[test]
fn an_envelope_that_is_not_an_object_is_a_decode_error_not_zero_frames() {
for body in [json!([]), json!("frames"), JsonValue::Null] {
let result = frames_from_envelope(body.clone()).map(|frames| frames.len());
assert!(result.is_err(), "expected a decode error for {body}, got {result:?}");
}
}
#[test]
fn a_decode_error_names_the_column_row_type_and_a_truncated_cell() {
let long = "x".repeat(10_000);
let error = decode(one_column(json!({"id": "Int4"}), vec!["7".to_string(), long.clone()])).unwrap_err();
assert!(error.contains("'a'"), "column missing: {error}");
assert!(error.contains("row 1"), "row missing: {error}");
assert!(error.contains("Int4"), "type missing: {error}");
assert!(error.len() < 400, "cell not truncated, {} bytes", error.len());
}
#[test]
fn a_system_timestamp_key_is_rejected_by_name() {
let mut json = one_column(json!({"id": "Int4"}), vec!["7".to_string()]);
json[0]["updated_at"] = json!(["not a datetime"]);
let error = decode(json).unwrap_err();
assert!(error.contains("frame key updated_at is not supported, system columns travel as # columns"), "{error}");
}
#[test]
fn a_bare_digest_column_still_reads_an_empty_slot_marker_as_none() {
let ty = json!({"id": "Digest", "underlying": {"id": "Float8"}, "accuracy": 10000});
let frames = decode(one_column(ty, vec![NONE_MARKER.to_string()])).expect("an empty digest slot must decode");
let column = ColumnView::try_from((frames[0].batch.column(0), frames[0].batch.schema_ref().field(0))).unwrap();
assert!(matches!(column.get_value(0), Value::None { .. }));
}