use super::*;
use std::io::Write as _;
const TERMINATED: &str = "event: message_start\ndata: {\"type\":\"message_start\"}\n\n\
event: message_stop\ndata: {\"type\":\"message_stop\"}\n\n";
fn gzip(plain: &[u8]) -> Vec<u8> {
let mut encoder = flate2::write::GzEncoder::new(Vec::new(), flate2::Compression::default());
encoder.write_all(plain).expect("gzip");
encoder.finish().expect("gzip")
}
fn brotli(plain: &[u8]) -> Vec<u8> {
let mut encoded = Vec::new();
let mut writer = brotli::CompressorWriter::new(&mut encoded, BROTLI_BUFFER, 5, 22);
writer.write_all(plain).expect("brotli");
drop(writer);
encoded
}
fn zstd(plain: &[u8]) -> Vec<u8> {
zstd::stream::encode_all(plain, 0).expect("zstd")
}
fn deflate(plain: &[u8]) -> Vec<u8> {
let mut encoder =
flate2::write::DeflateEncoder::new(Vec::new(), flate2::Compression::default());
encoder.write_all(plain).expect("deflate");
encoder.finish().expect("deflate")
}
#[test]
fn every_advertised_encoding_decodes_to_readable_sse() {
for (encoding, encode) in [
(Encoding::Gzip, gzip as fn(&[u8]) -> Vec<u8>),
(Encoding::Brotli, brotli),
(Encoding::Zstd, zstd),
(Encoding::Deflate, deflate),
] {
let decoded = decode(&encode(TERMINATED.as_bytes()), encoding)
.unwrap_or_else(|| panic!("{encoding:?} must decode"));
assert_eq!(decoded, TERMINATED, "{encoding:?} must round-trip exactly");
assert!(
decoded.contains("message_stop"),
"{encoding:?}: the terminator must be findable after decoding"
);
}
}
#[test]
fn a_truncated_stream_still_yields_the_frames_that_arrived() {
let framed_gzip = |frames: &[&str]| {
let mut encoder = flate2::write::GzEncoder::new(Vec::new(), flate2::Compression::default());
for frame in frames {
encoder.write_all(frame.as_bytes()).expect("gzip");
encoder.flush().expect("flush");
}
encoder.finish().expect("gzip")
};
let start = "event: message_start\ndata: {\"type\":\"message_start\"}\n\n";
let stop = "event: message_stop\ndata: {\"type\":\"message_stop\"}\n\n";
let complete = framed_gzip(&[start, stop]);
let cut_after_start = framed_gzip(&[start]);
let cut = &cut_after_start[..cut_after_start.len() - 8];
for (encoding, bytes, terminated) in [
(Encoding::Gzip, complete.as_slice(), true),
(Encoding::Gzip, cut, false),
] {
let decoded = decode(bytes, encoding).unwrap_or_else(|| {
panic!("{encoding:?}: a truncated stream must still be partly readable")
});
assert_eq!(
decoded.contains("message_stop"),
terminated,
"{encoding:?}: a stream cut before its terminator must not appear \
terminated: {decoded}"
);
assert!(
decoded.contains("message_start"),
"{encoding:?}: the frames that arrived must be readable: {decoded}"
);
}
}
#[test]
fn frames_are_decoded_as_one_stream() {
let complete = gzip(TERMINATED.as_bytes());
let (first, rest) = complete.split_at(complete.len() / 2);
assert!(
decode(first, Encoding::Gzip).is_none_or(|text| !text.contains("message_stop")),
"half a stream is not a whole one"
);
let mut joined = first.to_vec();
joined.extend_from_slice(rest);
assert_eq!(
decode(&joined, Encoding::Gzip).as_deref(),
Some(TERMINATED),
"the concatenation decodes even though its parts do not"
);
}
#[test]
fn an_unknown_encoding_is_not_claimed_as_readable() {
assert_eq!(Encoding::parse("gzip"), Some(Encoding::Gzip));
assert_eq!(Encoding::parse("GZIP"), Some(Encoding::Gzip));
assert_eq!(Encoding::parse("x-gzip"), Some(Encoding::Gzip));
assert_eq!(Encoding::parse("br"), Some(Encoding::Brotli));
assert_eq!(Encoding::parse("zstd"), Some(Encoding::Zstd));
assert_eq!(Encoding::parse("deflate"), Some(Encoding::Deflate));
assert_eq!(Encoding::parse(""), Some(Encoding::Identity));
assert_eq!(Encoding::parse("identity"), Some(Encoding::Identity));
assert_eq!(Encoding::parse("identity, gzip"), Some(Encoding::Gzip));
assert_eq!(Encoding::parse("compress"), None);
assert_eq!(Encoding::parse("exotic-2026"), None);
}
#[test]
fn bytes_that_do_not_match_the_header_decode_to_nothing() {
assert_eq!(decode(b"", Encoding::Gzip), None);
assert_eq!(decode(b"", Encoding::Identity), None);
assert_eq!(decode(b"plain text, not gzip", Encoding::Gzip), None);
assert_eq!(
decode(b"plain text", Encoding::Identity).as_deref(),
Some("plain text"),
"identity bytes are readable as they stand"
);
}