use reifydb_codec::row::{
bytes::RowBuilder,
operator::{EncodedOperatorRow, OPERATOR_HEADER_SIZE},
shape::{RowFamily, RowShape, RowShapeField},
};
use reifydb_value::{
factory::time::at_nanos,
value::{Value, datetime::DateTime, value_type::ValueType},
};
fn timed_shape() -> RowShape {
RowShape::new(RowFamily::Operator, vec![RowShapeField::unconstrained("v", ValueType::Int4)])
}
#[test]
fn the_header_is_eight_bytes_and_carries_nothing_but_a_time() {
assert_eq!(OPERATOR_HEADER_SIZE, 8);
assert_eq!(RowFamily::Operator.header_size(), OPERATOR_HEADER_SIZE);
}
#[test]
fn set_time_preserves_body() {
let payload: Vec<u8> = vec![7, 8, 9, 10];
let mut row = EncodedOperatorRow::new(&payload, at_nanos(7));
assert_eq!(row.time(), at_nanos(7));
row.set_time(at_nanos(99));
assert_eq!(row.time(), at_nanos(99));
assert_eq!(row.body(), payload.as_slice(), "the body must stay untouched");
}
#[test]
fn body_mut_windows_the_same_bytes_as_body() {
let payload: Vec<u8> = vec![1, 2, 3];
let mut row = EncodedOperatorRow::new(&payload, DateTime::EPOCH);
assert_eq!(row.body_mut(), payload.as_slice());
assert_eq!(row.body(), payload.as_slice());
}
#[test]
fn a_row_round_trip_through_bytes_preserves_time() {
let row = EncodedOperatorRow::new(&[1, 2, 3], at_nanos(1234));
let reloaded = EncodedOperatorRow::from(row.clone().into_bytes());
assert_eq!(reloaded, row);
assert_eq!(reloaded.time(), at_nanos(1234));
assert_eq!(reloaded.body(), &[1, 2, 3]);
}
#[test]
fn timeless_rows_sort_above_every_cutoff() {
let row = EncodedOperatorRow::timeless(&[]);
assert_eq!(row.time(), DateTime::MAX);
assert!(row.time() > at_nanos(u64::MAX - 1));
assert!(row.body().is_empty());
}
#[test]
fn a_timeless_row_reads_back_as_absent_not_as_a_real_instant() {
assert_eq!(EncodedOperatorRow::timeless(&[1, 2]).row_time(), None);
assert_eq!(EncodedOperatorRow::new(&[1, 2], at_nanos(5)).row_time(), Some(at_nanos(5)));
}
#[test]
fn byte_size_covers_header_and_body() {
let row = EncodedOperatorRow::new(&[1, 2, 3, 4], DateTime::EPOCH);
assert_eq!(row.byte_size().as_bytes(), row.len() as u64);
assert_eq!(row.len(), OPERATOR_HEADER_SIZE + row.body().len());
}
#[test]
fn new_body_round_trips_exactly() {
let payload: Vec<u8> = (0..=255).collect();
let row = EncodedOperatorRow::new(&payload, at_nanos(7));
assert_eq!(row.body(), payload.as_slice());
assert_eq!(row.time(), at_nanos(7));
}
#[test]
fn a_freshly_allocated_operator_row_reads_as_timeless() {
let shape = timed_shape();
let encoded = shape.allocate_operator();
assert_eq!(encoded.row_time(), None);
assert_eq!(shape.time(&encoded), None);
}
#[test]
fn the_shape_lifts_a_time_written_through_the_builder() {
let shape = timed_shape();
let mut encoded = shape.allocate_operator();
shape.set_values(&mut encoded, &[Value::int4(42)]);
encoded.set_time(at_nanos(60));
let bytes = encoded.freeze_bytes();
assert_eq!(shape.time(&bytes), Some(at_nanos(60)));
assert_eq!(shape.get_value(&bytes, 0), Value::int4(42));
}
#[test]
fn an_operator_field_starts_after_the_time_header() {
let shape = timed_shape();
assert!(shape.fields()[0].offset as usize >= OPERATOR_HEADER_SIZE);
}
#[test]
#[should_panic(expected = "operator rows carry no created_at")]
fn reading_created_at_off_an_operator_row_panics() {
let shape = timed_shape();
let encoded = shape.allocate_operator();
let _ = shape.created_at(&encoded);
}
#[test]
#[should_panic(expected = "operator rows carry no updated_at")]
fn reading_updated_at_off_an_operator_row_panics() {
let shape = timed_shape();
let encoded = shape.allocate_operator();
let _ = shape.updated_at(&encoded);
}
#[test]
#[should_panic(expected = "allocate_operator on a shape of another family")]
fn allocating_an_operator_row_from_a_pod_shape_panics() {
let shape = RowShape::new(RowFamily::Pod, vec![RowShapeField::unconstrained("v", ValueType::Int4)]);
let _ = shape.allocate_operator();
}
#[test]
#[should_panic(expected = "allocate_operator on a shape of another family")]
fn test_a_shape_of_another_family_cannot_allocate_an_operator_row() {
RowShape::new(RowFamily::Table, vec![RowShapeField::unconstrained("state", ValueType::Blob)])
.allocate_operator();
}