@@ -357,6 +357,81 @@ mod tests {
357357 assert_eq ! ( result. unwrap_err( ) . code, GRPC_STATUS_INVALID_ARGUMENT ) ;
358358 }
359359
360+ #[ test]
361+ fn test_encode_decode_roundtrip_varied_sizes ( ) {
362+ // Test encode/decode roundtrip with many payload sizes including edge cases
363+ let sizes = [ 0 , 1 , 2 , 4 , 5 , 127 , 128 , 255 , 256 , 1000 , 4096 , 65535 , 65536 ] ;
364+ for & size in & sizes {
365+ let payload: Vec < u8 > = ( 0 ..size) . map ( |i| ( i % 256 ) as u8 ) . collect ( ) ;
366+ let encoded = encode_grpc_frame ( & payload) ;
367+ assert_eq ! ( encoded. len( ) , 5 + size, "wrong frame size for payload len {size}" ) ;
368+ assert_eq ! ( encoded[ 0 ] , 0 , "compression flag should be 0" ) ;
369+ let decoded_len =
370+ u32:: from_be_bytes ( [ encoded[ 1 ] , encoded[ 2 ] , encoded[ 3 ] , encoded[ 4 ] ] ) as usize ;
371+ assert_eq ! ( decoded_len, size, "length header mismatch for payload len {size}" ) ;
372+ let decoded = decode_grpc_body ( & encoded) . unwrap ( ) ;
373+ assert_eq ! ( decoded, & payload[ ..] , "roundtrip failed for payload len {size}" ) ;
374+ }
375+ }
376+
377+ #[ test]
378+ fn test_decode_rejects_all_nonzero_compression_flags ( ) {
379+ for flag in 1 ..=255u8 {
380+ let data = vec ! [ flag, 0 , 0 , 0 , 1 , 42 ] ;
381+ let result = decode_grpc_body ( & data) ;
382+ assert ! ( result. is_err( ) , "should reject compression flag {flag}" ) ;
383+ assert_eq ! ( result. unwrap_err( ) . code, GRPC_STATUS_UNIMPLEMENTED ) ;
384+ }
385+ }
386+
387+ #[ test]
388+ fn test_decode_rejects_all_short_inputs ( ) {
389+ for len in 0 ..5 {
390+ let data = vec ! [ 0u8 ; len] ;
391+ assert ! ( decode_grpc_body( & data) . is_err( ) , "should reject {len}-byte input" ) ;
392+ }
393+ }
394+
395+ #[ test]
396+ fn test_percent_encode_ascii_range ( ) {
397+ // Verify every ASCII byte is either passed through or percent-encoded
398+ for b in 0u8 ..=127 {
399+ let s = String :: from ( b as char ) ;
400+ let encoded = percent_encode ( & s) ;
401+ let is_unreserved = b. is_ascii_alphanumeric ( )
402+ || b == b'-' || b == b'_'
403+ || b == b'.' || b == b'~'
404+ || b == b' ' ;
405+ if is_unreserved {
406+ assert_eq ! ( encoded, s, "byte {b:#04x} ({}) should pass through" , b as char ) ;
407+ } else {
408+ assert_eq ! ( encoded, format!( "%{b:02X}" ) , "byte {b:#04x} should be percent-encoded" ) ;
409+ }
410+ }
411+ }
412+
413+ #[ test]
414+ fn test_percent_encode_multibyte_utf8 ( ) {
415+ // Each byte of multi-byte UTF-8 chars should be individually encoded
416+ let encoded = percent_encode ( "café" ) ;
417+ assert_eq ! ( encoded, "caf%C3%A9" ) ;
418+ }
419+
420+ #[ test]
421+ fn test_parse_grpc_timeout_boundary_values ( ) {
422+ use std:: time:: Duration ;
423+ // Zero values
424+ assert_eq ! ( parse_grpc_timeout( "0S" ) , Some ( Duration :: from_secs( 0 ) ) ) ;
425+ assert_eq ! ( parse_grpc_timeout( "0m" ) , Some ( Duration :: from_millis( 0 ) ) ) ;
426+ // Large values
427+ assert_eq ! ( parse_grpc_timeout( "99999999S" ) , Some ( Duration :: from_secs( 99_999_999 ) ) ) ;
428+ // Various invalid formats
429+ assert_eq ! ( parse_grpc_timeout( "5" ) , None ) ; // no unit
430+ assert_eq ! ( parse_grpc_timeout( "abc" ) , None ) ; // non-numeric
431+ assert_eq ! ( parse_grpc_timeout( "5X" ) , None ) ; // unknown unit
432+ assert_eq ! ( parse_grpc_timeout( "5 S" ) , None ) ; // space before unit
433+ }
434+
360435 #[ test]
361436 fn test_parse_grpc_timeout ( ) {
362437 use std:: time:: Duration ;
0 commit comments