use super::*; use bytes::{Bytes, BytesMut}; #[test] fn reading_body_pending_application_plaintext_is_preserved_on_into_inner() { let sample = b"coalesced-tail-after-mtproto"; let mut reader = FakeTlsReader::new(tokio::io::empty()); reader.state = TlsReaderState::ReadingBody { record_type: TLS_RECORD_APPLICATION, length: sample.len(), buffer: BytesMut::from(&sample[..]), }; let (_inner, pending) = reader.into_inner_with_pending_plaintext(); assert_eq!( pending, sample, "partial application-data body must survive into fallback path" ); } #[test] fn yielding_pending_plaintext_is_preserved_on_into_inner() { let sample = b"already-decoded-buffer"; let mut reader = FakeTlsReader::new(tokio::io::empty()); reader.state = TlsReaderState::Yielding { buffer: YieldBuffer::new(Bytes::copy_from_slice(sample)), }; let (_inner, pending) = reader.into_inner_with_pending_plaintext(); assert_eq!(pending, sample); } #[test] fn reading_body_non_application_record_does_not_produce_plaintext() { let sample = b"unexpected-handshake-fragment"; let mut reader = FakeTlsReader::new(tokio::io::empty()); reader.state = TlsReaderState::ReadingBody { record_type: TLS_RECORD_HANDSHAKE, length: sample.len(), buffer: BytesMut::from(&sample[..]), }; let (_inner, pending) = reader.into_inner_with_pending_plaintext(); assert!( pending.is_empty(), "non-application partial body must not be surfaced as plaintext" ); } #[test] fn partial_header_state_does_not_produce_plaintext() { let mut header = HeaderBuffer::::new(); let unfilled = header.unfilled_mut(); unfilled[0] = TLS_RECORD_APPLICATION; header.advance(1); let mut reader = FakeTlsReader::new(tokio::io::empty()); reader.state = TlsReaderState::ReadingHeader { header }; let (_inner, pending) = reader.into_inner_with_pending_plaintext(); assert!( pending.is_empty(), "partial header bytes are not plaintext payload" ); } #[test] fn edge_zero_length_application_fragment_remains_empty_without_panics() { let mut reader = FakeTlsReader::new(tokio::io::empty()); reader.state = TlsReaderState::ReadingBody { record_type: TLS_RECORD_APPLICATION, length: 0, buffer: BytesMut::new(), }; let (_inner, pending) = reader.into_inner_with_pending_plaintext(); assert!(pending.is_empty()); } #[test] fn adversarial_poisoned_state_never_leaks_pending_bytes() { let mut reader = FakeTlsReader::new(tokio::io::empty()); reader.state = TlsReaderState::Poisoned { error: Some(std::io::Error::other("poisoned by adversarial input")), }; let (_inner, pending) = reader.into_inner_with_pending_plaintext(); assert!( pending.is_empty(), "poisoned state must fail-closed for fallback payload" ); } #[test] fn stress_large_application_fragment_survives_state_extraction() { let mut payload = vec![0u8; 96 * 1024]; for (i, b) in payload.iter_mut().enumerate() { *b = (i as u8).wrapping_mul(17).wrapping_add(3); } let mut reader = FakeTlsReader::new(tokio::io::empty()); reader.state = TlsReaderState::ReadingBody { record_type: TLS_RECORD_APPLICATION, length: payload.len(), buffer: BytesMut::from(&payload[..]), }; let (_inner, pending) = reader.into_inner_with_pending_plaintext(); assert_eq!( pending, payload, "large pending application plaintext must be preserved exactly" ); } #[test] fn light_fuzz_state_matrix_preserves_pending_contract() { let mut seed = 0x9E37_79B9_7F4A_7C15u64; for _ in 0..4096 { seed ^= seed << 7; seed ^= seed >> 9; seed ^= seed << 8; let len = (seed & 0x1ff) as usize; let mut payload = vec![0u8; len]; for (idx, b) in payload.iter_mut().enumerate() { *b = (seed as u8).wrapping_add(idx as u8); } let record_type = match seed & 0x3 { 0 => TLS_RECORD_APPLICATION, 1 => TLS_RECORD_HANDSHAKE, 2 => TLS_RECORD_ALERT, _ => TLS_RECORD_CHANGE_CIPHER, }; let mut reader = FakeTlsReader::new(tokio::io::empty()); reader.state = TlsReaderState::ReadingBody { record_type, length: payload.len(), buffer: BytesMut::from(&payload[..]), }; let (_inner, pending) = reader.into_inner_with_pending_plaintext(); if record_type == TLS_RECORD_APPLICATION { assert_eq!(pending, payload); } else { assert!(pending.is_empty()); } } }