use ferro_lumberjack::frame::{Frame, FrameDecoder};
const BEATS_WINDOW: &[u8] = include_bytes!("fixtures/beats_filebeat_window_v2.bin");
const LOGSTASH_ACK: &[u8] = include_bytes!("fixtures/logstash_ack_v2.bin");
#[test]
fn upstream_beats_window_decodes_to_window_plus_two_json_frames() {
let mut d = FrameDecoder::new();
d.feed(BEATS_WINDOW);
let f1 = d.next_frame().expect("decode 1").expect("frame ready");
let Frame::Window { count } = f1 else {
panic!("expected Window, got {f1:?}");
};
assert_eq!(count, 2);
let f2 = d.next_frame().expect("decode 2").expect("frame ready");
let Frame::Json { seq, payload } = f2 else {
panic!("expected Json, got {f2:?}");
};
assert_eq!(seq, 1);
let utf = std::str::from_utf8(&payload).expect("Filebeat payloads are UTF-8");
assert!(
utf.contains(r#""@timestamp":"2024-09-15T12:00:00.000Z""#),
"expected real Filebeat-style @timestamp field",
);
assert!(utf.contains(r#""beat":"filebeat""#));
assert!(utf.contains(r#""path":"/var/log/syslog""#));
let f3 = d.next_frame().expect("decode 3").expect("frame ready");
let Frame::Json { seq, payload } = f3 else {
panic!("expected Json, got {f3:?}");
};
assert_eq!(seq, 2);
let utf = std::str::from_utf8(&payload).expect("UTF-8");
assert!(
utf.contains(r#""offset":1156"#),
"expected the second event's log.offset progression",
);
assert!(d.next_frame().expect("decode 4").is_none());
}
#[test]
fn upstream_logstash_ack_decodes_with_correct_seq() {
let mut d = FrameDecoder::new();
d.feed(LOGSTASH_ACK);
let f = d.next_frame().expect("decode").expect("ready");
let Frame::Ack { seq } = f else {
panic!("expected Ack, got {f:?}");
};
assert_eq!(seq, 2);
assert!(d.next_frame().expect("decode 2").is_none());
}
#[test]
fn upstream_beats_window_split_feed_chunks_decode_identically() {
let mut single = FrameDecoder::new();
single.feed(BEATS_WINDOW);
let mut single_frames = Vec::new();
while let Some(f) = single.next_frame().expect("decode") {
single_frames.push(f);
}
let mut chunked = FrameDecoder::new();
let mut chunked_frames = Vec::new();
for byte in BEATS_WINDOW {
chunked.feed(std::slice::from_ref(byte));
while let Some(f) = chunked.next_frame().expect("decode") {
chunked_frames.push(f);
}
}
assert_eq!(single_frames, chunked_frames);
}
#[test]
fn upstream_fixture_frame_header_bytes_match_lumberjack_v2_spec() {
assert_eq!(BEATS_WINDOW[0], b'2', "version byte");
assert_eq!(BEATS_WINDOW[1], b'W', "window frame type byte");
let count = u32::from_be_bytes([
BEATS_WINDOW[2],
BEATS_WINDOW[3],
BEATS_WINDOW[4],
BEATS_WINDOW[5],
]);
assert_eq!(count, 2);
assert_eq!(LOGSTASH_ACK.len(), 6, "ACK frame is exactly 6 bytes");
assert_eq!(LOGSTASH_ACK[0], b'2');
assert_eq!(LOGSTASH_ACK[1], b'A');
}