use super::*;
#[test]
fn key_null() {
let encoded = encode_key_value(&Value::Null);
let (decoded, n) = decode_key_value(&encoded).unwrap();
assert_eq!(n, 1);
assert_eq!(decoded, Value::Null);
}
#[test]
fn key_boolean() {
let f_enc = encode_key_value(&Value::Boolean(false));
let t_enc = encode_key_value(&Value::Boolean(true));
assert!(f_enc < t_enc);
let (f_dec, _) = decode_key_value(&f_enc).unwrap();
let (t_dec, _) = decode_key_value(&t_enc).unwrap();
assert_eq!(f_dec, Value::Boolean(false));
assert_eq!(t_dec, Value::Boolean(true));
}
#[test]
fn key_integer_roundtrip() {
let test_values = [
i64::MIN,
-1_000_000,
-256,
-1,
0,
1,
127,
128,
255,
256,
65535,
1_000_000,
i64::MAX,
];
for &v in &test_values {
let encoded = encode_key_value(&Value::Integer(v));
let (decoded, _) = decode_key_value(&encoded).unwrap();
assert_eq!(decoded, Value::Integer(v), "roundtrip failed for {v}");
}
}
#[test]
fn key_integer_sort_order() {
let values: Vec<i64> = vec![i64::MIN, -1_000_000, -1, 0, 1, 1_000_000, i64::MAX];
let encoded: Vec<Vec<u8>> = values
.iter()
.map(|&v| encode_key_value(&Value::Integer(v)))
.collect();
for i in 0..encoded.len() - 1 {
assert!(
encoded[i] < encoded[i + 1],
"sort order broken: {} vs {}",
values[i],
values[i + 1]
);
}
}
#[test]
fn key_real_roundtrip() {
let test_values = [
f64::NEG_INFINITY,
-1e100,
-1.0,
-f64::MIN_POSITIVE,
-0.0,
0.0,
f64::MIN_POSITIVE,
0.5,
1.0,
1e100,
f64::INFINITY,
];
for &v in &test_values {
let encoded = encode_key_value(&Value::Real(v));
let (decoded, _) = decode_key_value(&encoded).unwrap();
match decoded {
Value::Real(r) => {
assert!(
v.to_bits() == r.to_bits(),
"roundtrip failed for {v}: got {r}"
);
}
_ => panic!("expected Real"),
}
}
}
#[test]
fn key_real_sort_order() {
let values = [
f64::NEG_INFINITY,
-100.0,
-1.0,
-0.0,
0.0,
1.0,
100.0,
f64::INFINITY,
];
let encoded: Vec<Vec<u8>> = values
.iter()
.map(|&v| encode_key_value(&Value::Real(v)))
.collect();
for i in 0..encoded.len() - 1 {
assert!(
encoded[i] <= encoded[i + 1],
"sort order broken: {} vs {}",
values[i],
values[i + 1]
);
}
}
#[test]
fn key_text_roundtrip() {
let test_values = ["", "hello", "world", "hello\0world", "\0\0\0"];
for &v in &test_values {
let encoded = encode_key_value(&Value::Text(v.into()));
let (decoded, _) = decode_key_value(&encoded).unwrap();
assert_eq!(decoded, Value::Text(v.into()), "roundtrip failed for {v:?}");
}
}
#[test]
fn key_text_sort_order() {
let values = ["", "a", "ab", "b", "ba", "z"];
let encoded: Vec<Vec<u8>> = values
.iter()
.map(|&v| encode_key_value(&Value::Text(v.into())))
.collect();
for i in 0..encoded.len() - 1 {
assert!(
encoded[i] < encoded[i + 1],
"sort order broken: {:?} vs {:?}",
values[i],
values[i + 1]
);
}
}
#[test]
fn key_blob_roundtrip() {
let test_values: Vec<Vec<u8>> = vec![
vec![],
vec![0x00],
vec![0x00, 0xFF],
vec![0xFF, 0x00],
vec![0x00, 0x00, 0x00],
];
for v in &test_values {
let encoded = encode_key_value(&Value::Blob(v.clone()));
let (decoded, _) = decode_key_value(&encoded).unwrap();
assert_eq!(decoded, Value::Blob(v.clone()));
}
}
#[test]
fn key_composite_roundtrip() {
let values = vec![
Value::Integer(42),
Value::Text("hello".into()),
Value::Boolean(true),
];
let encoded = encode_composite_key(&values);
let decoded = decode_composite_key(&encoded, 3).unwrap();
assert_eq!(decoded[0], Value::Integer(42));
assert_eq!(decoded[1], Value::Text("hello".into()));
assert_eq!(decoded[2], Value::Boolean(true));
}
#[test]
fn key_composite_sort_order() {
let k1 = encode_composite_key(&[Value::Integer(1), Value::Text("b".into())]);
let k2 = encode_composite_key(&[Value::Integer(1), Value::Text("c".into())]);
let k3 = encode_composite_key(&[Value::Integer(2), Value::Text("a".into())]);
assert!(k1 < k2);
assert!(k2 < k3);
}
#[test]
fn key_cross_type_ordering() {
let null = encode_key_value(&Value::Null);
let bool_val = encode_key_value(&Value::Boolean(false));
let int = encode_key_value(&Value::Integer(0));
let text = encode_key_value(&Value::Text("".into()));
let blob = encode_key_value(&Value::Blob(vec![]));
assert!(null < blob);
assert!(blob < text);
assert!(text < bool_val);
assert!(bool_val < int);
}
#[test]
fn row_roundtrip_simple() {
let values = vec![
Value::Integer(42),
Value::Text("hello".into()),
Value::Boolean(true),
];
let encoded = encode_row(&values);
let decoded = decode_row(&encoded).unwrap();
assert_eq!(decoded.len(), 3);
assert_eq!(decoded[0], Value::Integer(42));
assert_eq!(decoded[1], Value::Text("hello".into()));
assert_eq!(decoded[2], Value::Boolean(true));
}
#[test]
fn row_roundtrip_with_nulls() {
let values = vec![
Value::Integer(1),
Value::Null,
Value::Text("test".into()),
Value::Null,
];
let encoded = encode_row(&values);
let decoded = decode_row(&encoded).unwrap();
assert_eq!(decoded.len(), 4);
assert_eq!(decoded[0], Value::Integer(1));
assert!(decoded[1].is_null());
assert_eq!(decoded[2], Value::Text("test".into()));
assert!(decoded[3].is_null());
}
#[test]
fn row_roundtrip_empty() {
let values: Vec<Value> = vec![];
let encoded = encode_row(&values);
let decoded = decode_row(&encoded).unwrap();
assert!(decoded.is_empty());
}
#[test]
fn row_roundtrip_all_types() {
let values = vec![
Value::Integer(-100),
Value::Real(3.15),
Value::Text("hello world".into()),
Value::Blob(vec![0xDE, 0xAD, 0xBE, 0xEF]),
Value::Boolean(false),
Value::Null,
];
let encoded = encode_row(&values);
let decoded = decode_row(&encoded).unwrap();
assert_eq!(decoded.len(), 6);
assert_eq!(decoded[0], Value::Integer(-100));
assert_eq!(decoded[1], Value::Real(3.15));
assert_eq!(decoded[2], Value::Text("hello world".into()));
assert_eq!(decoded[3], Value::Blob(vec![0xDE, 0xAD, 0xBE, 0xEF]));
assert_eq!(decoded[4], Value::Boolean(false));
assert!(decoded[5].is_null());
}
#[test]
fn null_escaped_with_embedded_nulls() {
let text = "before\0after";
let encoded = encode_key_value(&Value::Text(text.into()));
let (decoded, _) = decode_key_value(&encoded).unwrap();
assert_eq!(decoded, Value::Text(text.into()));
}
#[test]
fn key_integer_edge_cases() {
for v in [i64::MIN, i64::MIN + 1, -1, 0, 1, i64::MAX - 1, i64::MAX] {
let encoded = encode_key_value(&Value::Integer(v));
let (decoded, n) = decode_key_value(&encoded).unwrap();
assert_eq!(n, encoded.len());
assert_eq!(decoded, Value::Integer(v), "edge case failed for {v}");
}
}
#[test]
fn decode_columns_single() {
let values = vec![
Value::Integer(42),
Value::Text("hello".into()),
Value::Boolean(true),
];
let encoded = encode_row(&values);
let cols = decode_columns(&encoded, &[1]).unwrap();
assert_eq!(cols.len(), 1);
assert_eq!(cols[0], Value::Text("hello".into()));
}
#[test]
fn decode_columns_multiple() {
let values = vec![
Value::Integer(1),
Value::Real(2.5),
Value::Text("skip".into()),
Value::Boolean(false),
Value::Blob(vec![0xAB]),
];
let encoded = encode_row(&values);
let cols = decode_columns(&encoded, &[0, 3, 4]).unwrap();
assert_eq!(cols.len(), 3);
assert_eq!(cols[0], Value::Integer(1));
assert_eq!(cols[1], Value::Boolean(false));
assert_eq!(cols[2], Value::Blob(vec![0xAB]));
}
#[test]
fn decode_columns_with_nulls() {
let values = vec![
Value::Integer(10),
Value::Null,
Value::Text("after_null".into()),
Value::Null,
Value::Boolean(true),
];
let encoded = encode_row(&values);
let cols = decode_columns(&encoded, &[1, 2, 4]).unwrap();
assert_eq!(cols.len(), 3);
assert!(cols[0].is_null());
assert_eq!(cols[1], Value::Text("after_null".into()));
assert_eq!(cols[2], Value::Boolean(true));
}
#[test]
fn decode_columns_first_and_last() {
let values = vec![
Value::Text("first".into()),
Value::Integer(99),
Value::Boolean(false),
Value::Real(3.125),
];
let encoded = encode_row(&values);
let cols = decode_columns(&encoded, &[0, 3]).unwrap();
assert_eq!(cols.len(), 2);
assert_eq!(cols[0], Value::Text("first".into()));
assert_eq!(cols[1], Value::Real(3.125));
}
#[test]
fn decode_columns_empty_targets() {
let values = vec![Value::Integer(1)];
let encoded = encode_row(&values);
let cols = decode_columns(&encoded, &[]).unwrap();
assert!(cols.is_empty());
}
#[test]
fn decode_columns_all_matches_full_decode() {
let values = vec![
Value::Integer(-100),
Value::Real(3.15),
Value::Text("hello world".into()),
Value::Blob(vec![0xDE, 0xAD]),
Value::Boolean(false),
Value::Null,
];
let encoded = encode_row(&values);
let full = decode_row(&encoded).unwrap();
let selective = decode_columns(&encoded, &[0, 1, 2, 3, 4, 5]).unwrap();
assert_eq!(full, selective);
}
#[test]
fn raw_column_integer() {
let values = vec![Value::Integer(42), Value::Text("hello".into())];
let encoded = encode_row(&values);
let raw = decode_column_raw(&encoded, 0).unwrap();
assert!(matches!(raw, RawColumn::Integer(42)));
assert_eq!(raw.to_value(), Value::Integer(42));
}
#[test]
fn raw_column_text_borrows() {
let values = vec![Value::Integer(1), Value::Text("hello".into())];
let encoded = encode_row(&values);
let raw = decode_column_raw(&encoded, 1).unwrap();
match raw {
RawColumn::Text(s) => assert_eq!(s, "hello"),
other => panic!("expected Text, got {other:?}"),
}
}
#[test]
fn raw_column_null() {
let values = vec![Value::Integer(1), Value::Null, Value::Boolean(true)];
let encoded = encode_row(&values);
let raw = decode_column_raw(&encoded, 1).unwrap();
assert!(matches!(raw, RawColumn::Null));
}
#[test]
fn raw_column_last() {
let values = vec![
Value::Integer(1),
Value::Text("skip".into()),
Value::Real(3.15),
];
let encoded = encode_row(&values);
let raw = decode_column_raw(&encoded, 2).unwrap();
match raw {
RawColumn::Real(r) => assert!((r - 3.15).abs() < 1e-10),
other => panic!("expected Real, got {other:?}"),
}
}
#[test]
fn raw_column_out_of_bounds_returns_null() {
let values = vec![Value::Integer(1)];
let encoded = encode_row(&values);
assert!(matches!(
decode_column_raw(&encoded, 1).unwrap(),
RawColumn::Null
));
}
#[test]
fn raw_column_eq_value() {
let raw_int = RawColumn::Integer(42);
assert!(raw_int.eq_value(&Value::Integer(42)));
assert!(!raw_int.eq_value(&Value::Integer(43)));
assert!(raw_int.eq_value(&Value::Real(42.0)));
let raw_text = RawColumn::Text("hello");
assert!(raw_text.eq_value(&Value::Text("hello".into())));
assert!(!raw_text.eq_value(&Value::Text("world".into())));
}
#[test]
fn raw_column_cmp_value() {
use std::cmp::Ordering;
let raw = RawColumn::Integer(42);
assert_eq!(raw.cmp_value(&Value::Integer(42)), Some(Ordering::Equal));
assert_eq!(raw.cmp_value(&Value::Integer(50)), Some(Ordering::Less));
assert_eq!(raw.cmp_value(&Value::Integer(10)), Some(Ordering::Greater));
assert_eq!(raw.cmp_value(&Value::Null), None);
}
#[test]
fn raw_column_as_numeric() {
assert_eq!(RawColumn::Integer(42).as_i64(), Some(42));
assert_eq!(RawColumn::Integer(42).as_f64(), Some(42.0));
assert_eq!(RawColumn::Real(3.15).as_f64(), Some(3.15));
assert_eq!(RawColumn::Real(3.15).as_i64(), None);
assert_eq!(RawColumn::Text("x").as_f64(), None);
assert_eq!(RawColumn::Null.as_i64(), None);
}
#[test]
fn decode_pk_integer_roundtrip() {
for v in [0i64, 1, -1, 42, -1000, i64::MIN, i64::MAX] {
let encoded = encode_key_value(&Value::Integer(v));
let decoded = decode_pk_integer(&encoded).unwrap();
assert_eq!(decoded, v);
}
}
#[test]
fn decode_pk_integer_rejects_non_integer() {
let encoded = encode_key_value(&Value::Text("hello".into()));
assert!(decode_pk_integer(&encoded).is_err());
}
#[test]
fn raw_column_blob() {
let values = vec![Value::Blob(vec![0xDE, 0xAD])];
let encoded = encode_row(&values);
let raw = decode_column_raw(&encoded, 0).unwrap();
match raw {
RawColumn::Blob(b) => assert_eq!(b, &[0xDE, 0xAD]),
other => panic!("expected Blob, got {other:?}"),
}
}
#[test]
fn raw_column_matches_full_decode() {
let values = vec![
Value::Integer(-100),
Value::Real(3.15),
Value::Text("hello world".into()),
Value::Blob(vec![0xDE, 0xAD]),
Value::Boolean(false),
Value::Null,
];
let encoded = encode_row(&values);
let full = decode_row(&encoded).unwrap();
for (i, expected) in full.iter().enumerate() {
let raw = decode_column_raw(&encoded, i).unwrap();
assert_eq!(raw.to_value(), *expected, "mismatch at column {i}");
}
}
#[test]
fn int_row_template_matches_generic_encoder() {
let mut state: u64 = 0x00C1_ADE7_BABE_u64.wrapping_mul(0x9E37_79B9_7F4A_7C15);
let mut next = || {
state = state
.wrapping_mul(6364136223846793005)
.wrapping_add(1442695040888963407);
state as i64
};
for trial in 0..2_000 {
let phys_count = (trial % 6) + 1;
let virtual_mask = trial % (1 << phys_count);
let mut null_slots: Vec<usize> = Vec::new();
let mut values: Vec<Value> = Vec::with_capacity(phys_count);
for slot in 0..phys_count {
if (virtual_mask >> slot) & 1 == 1 {
null_slots.push(slot);
values.push(Value::Null);
} else {
values.push(Value::Integer(next()));
}
}
let mut generic = Vec::new();
encode_row_into(&values, &mut generic);
let tmpl = build_int_row_template(phys_count, &null_slots);
let mut specialized = Vec::new();
encode_int_row_with_template(&tmpl, &values, &mut specialized).unwrap();
assert_eq!(
specialized, generic,
"trial {trial}: phys_count={phys_count} null_slots={null_slots:?} \
values={values:?}",
);
}
}