use ese_core::{coltyp, decode_record, fixed_col_size, ColumnDef, EseValue};
#[test]
fn fixed_col_size_long_is_4() {
assert_eq!(fixed_col_size(coltyp::LONG), Some(4));
}
#[test]
fn fixed_col_size_long_long_is_8() {
assert_eq!(fixed_col_size(coltyp::LONG_LONG), Some(8));
}
#[test]
fn fixed_col_size_text_is_none() {
assert_eq!(fixed_col_size(coltyp::TEXT), None);
}
#[test]
fn fixed_col_size_guid_is_16() {
assert_eq!(fixed_col_size(coltyp::GUID), Some(16));
}
fn make_fixed_record(columns: &[(u8, &[u8])]) -> Vec<u8> {
let last_fixed = columns.len() as u8;
let fixed_bytes: Vec<u8> = columns.iter().flat_map(|(_, data)| data.iter().copied()).collect();
let fixed_len = fixed_bytes.len();
let var_data_offset = (4 + fixed_len) as u16;
let mut rec = vec![last_fixed, 0u8];
rec.extend_from_slice(&var_data_offset.to_le_bytes());
rec.extend_from_slice(&fixed_bytes);
rec
}
#[test]
fn decode_record_single_i32_column() {
let value: i32 = 42;
let bytes = value.to_le_bytes();
let rec = make_fixed_record(&[(coltyp::LONG, bytes.as_slice())]);
let defs = vec![ColumnDef { column_id: 1, name: "auto_inc_id".into(), coltyp: coltyp::LONG }];
let result = decode_record(&rec, &defs).expect("decode");
assert_eq!(result.len(), 1);
assert_eq!(result[0].0, "auto_inc_id");
assert!(matches!(result[0].1, EseValue::I32(42)));
}
#[test]
fn decode_record_two_fixed_columns() {
let auto_inc: i32 = 7;
let bytes_sent: i64 = 1_234_567;
let mut fixed = auto_inc.to_le_bytes().to_vec();
fixed.extend_from_slice(&bytes_sent.to_le_bytes());
let var_offset = (4 + fixed.len()) as u16;
let mut rec = vec![2u8, 0u8]; rec.extend_from_slice(&var_offset.to_le_bytes());
rec.extend_from_slice(&fixed);
let defs = vec![
ColumnDef { column_id: 1, name: "auto_inc_id".into(), coltyp: coltyp::LONG },
ColumnDef { column_id: 2, name: "bytes_sent".into(), coltyp: coltyp::LONG_LONG },
];
let result = decode_record(&rec, &defs).expect("decode");
assert_eq!(result.len(), 2);
assert!(matches!(result[0].1, EseValue::I32(7)));
assert!(matches!(result[1].1, EseValue::I64(1_234_567)));
}
#[test]
fn decode_record_three_fixed_columns() {
let auto_inc: i32 = 1;
let app_id: i32 = 100;
let user_id: i32 = 200;
let mut fixed = auto_inc.to_le_bytes().to_vec();
fixed.extend_from_slice(&app_id.to_le_bytes());
fixed.extend_from_slice(&user_id.to_le_bytes());
let var_offset = (4 + fixed.len()) as u16;
let mut rec = vec![3u8, 0u8];
rec.extend_from_slice(&var_offset.to_le_bytes());
rec.extend_from_slice(&fixed);
let defs = vec![
ColumnDef { column_id: 1, name: "auto_inc_id".into(), coltyp: coltyp::LONG },
ColumnDef { column_id: 2, name: "app_id".into(), coltyp: coltyp::LONG },
ColumnDef { column_id: 3, name: "user_id".into(), coltyp: coltyp::LONG },
];
let result = decode_record(&rec, &defs).expect("decode");
assert_eq!(result.len(), 3);
assert!(matches!(result[0].1, EseValue::I32(1)));
assert!(matches!(result[1].1, EseValue::I32(100)));
assert!(matches!(result[2].1, EseValue::I32(200)));
}
#[test]
fn decode_record_columns_beyond_last_fixed_col_are_absent() {
let auto_inc: i32 = 5;
let var_offset = 4 + 4u16;
let mut rec = vec![1u8, 0u8];
rec.extend_from_slice(&var_offset.to_le_bytes());
rec.extend_from_slice(&auto_inc.to_le_bytes());
let defs = vec![
ColumnDef { column_id: 1, name: "auto_inc_id".into(), coltyp: coltyp::LONG },
ColumnDef { column_id: 2, name: "app_id".into(), coltyp: coltyp::LONG },
];
let result = decode_record(&rec, &defs).expect("decode");
assert_eq!(result.len(), 1);
assert_eq!(result[0].0, "auto_inc_id");
}
fn make_record_with_text(fixed_val: i32, text: &str) -> Vec<u8> {
let text_bytes = text.as_bytes();
let var_data_offset: u16 = 8; let end_offset = text_bytes.len() as u16;
let mut rec = vec![
1u8, 1u8, ];
rec.extend_from_slice(&var_data_offset.to_le_bytes()); rec.extend_from_slice(&fixed_val.to_le_bytes()); rec.extend_from_slice(&end_offset.to_le_bytes()); rec.extend_from_slice(text_bytes); rec
}
#[test]
fn decode_record_variable_text_column() {
let rec = make_record_with_text(99, "svchost.exe");
let defs = vec![
ColumnDef { column_id: 1, name: "auto_inc_id".into(), coltyp: coltyp::LONG },
ColumnDef { column_id: 129, name: "id_blob".into(), coltyp: coltyp::TEXT },
];
let result = decode_record(&rec, &defs).expect("decode");
let text_col = result.iter().find(|(n, _)| n == "id_blob");
assert!(text_col.is_some(), "variable text column must be present");
assert!(matches!(&text_col.unwrap().1, EseValue::Text(s) if s == "svchost.exe"));
}
#[test]
fn decode_record_empty_buffer_returns_empty() {
let defs = vec![ColumnDef { column_id: 1, name: "x".into(), coltyp: coltyp::LONG }];
let result = decode_record(&[], &defs).expect("decode");
assert!(result.is_empty());
}