
ChunkedTest.pudu
Pudu67 lines4.4 KB
1/** @Test.Domain.Chunked.Suite — chunked bodies decoded as their bytes arrive */2module PuduLangHttpClient.Domain.ChunkedTest34import Std.Io as Io5import Std.Test as Test6import PuduLangHttpClient.Domain.Chunked as Chunked789fn fed(pieces: &Array[Str], limit: Int) -> Result[(Chunked.Decoder, Str), Chunked.Problem] {10 var decoder = Chunked.decoder()11 var body = ""12 for piece in pieces {13 let (next, decoded) = Chunked.feed(&decoder, &piece.toBytes(), limit) ?14 decoder = next15 body = body + textOf(&decoded)16 }17 Ok((decoder, body))18}192021fn textOf(bytes: &Bytes) -> Str {22 match bytes.toText() {23 case Some(text) => text24 case None => ""25 }26}272829fn summary(pieces: &Array[Str], limit: Int) -> Result[(Str, Bool, Array[(Str, Str)], Str), Chunked.Problem] {30 let (decoder, body) = fed(pieces, limit) ?31 Ok((body, Chunked.isDone(&decoder), decoder.trailers, textOf(&Chunked.leftover(&decoder))))32}333435fn main() -> Int {36 let whole = "4\r\nWiki\r\n5\r\npedia\r\nE\r\n in\r\n\r\nchunks.\r\n0\r\n\r\n"37 let pieces = whole.chars().map(|c: Char| c.toText())38 let checks = Test.suite("Domain.Chunked", &[39 Test.equals("a whole body decodes", &summary(&[whole], 100), &Ok(("Wikipedia in\r\n\r\nchunks.", true, [], ""))),40 Test.equals("a body arriving one character at a time decodes the same", &summary(&pieces, 100), &Ok(("Wikipedia in\r\n\r\nchunks.", true, [], ""))),41 Test.equals("extensions are ignored and trailers kept", &summary(&["3;name=v\r\nabc\r\n0\r\nExpires: never\r\nX-A: b \r\n\r\n"], 100), &Ok(("abc", true, [("Expires", "never"), ("X-A", "b")], ""))),42 Test.equals("bytes after the body are left over", &summary(&["1\r\nx\r\n0\r\n\r\nHTTP"], 100), &Ok(("x", true, [], "HTTP"))),43 Test.equals("an unfinished body is not done", &summary(&["5\r\nab"], 100), &Ok(("ab", false, [], ""))),44 Test.equals("an empty body is done at once", &summary(&["0\r\n\r\n"], 0), &Ok(("", true, [], ""))),45 Test.equals("a body exactly at the limit decodes", &summary(&["3\r\nabc\r\n0\r\n\r\n"], 3), &Ok(("abc", true, [], ""))),46 Test.equals("a body one byte over the limit is exceeded", &summary(&["4\r\nabcd\r\n0\r\n\r\n"], 3), &Err(Chunked.Exceeded)),47 Test.equals("upper-case hexadecimal sizes are read", &summary(&["A\r\n0123456789\r\n0\r\n\r\n"], 100), &Ok(("0123456789", true, [], ""))),48 Test.equals("a size that is not hexadecimal is malformed", &summary(&["xyz\r\n"], 100), &Err(Chunked.Malformed("a chunk size is not hexadecimal: xyz"))),49 Test.equals("an empty size is malformed", &summary(&[";x\r\n"], 100), &Err(Chunked.Malformed("a chunk size is missing or too long"))),50 Test.equals("a size of sixteen digits is malformed", &summary(&["1000000000000000\r\n"], 100), &Err(Chunked.Malformed("a chunk size is missing or too long"))),51 Test.equals("a chunk not followed by a line break is malformed", &summary(&["1\r\nxyz"], 100), &Err(Chunked.Malformed("a chunk is not followed by a line break"))),52 Test.equals("a trailer without a name is malformed", &summary(&["0\r\n: v\r\n\r\n"], 100), &Err(Chunked.Malformed("a trailer field has no name"))),53 Test.equals("a framing line past the limit is malformed", &summary(&["1".repeat(8193)], 100), &Err(Chunked.Malformed("a framing line is longer than 8192 bytes"))),54 Test.equals("a long line with its break is malformed too", &summary(&["0;" + "x".repeat(8191) + "\r\n"], 100), &Err(Chunked.Malformed("a framing line is longer than 8192 bytes"))),55 Test.equals("a line at the limit is accepted", &summary(&["0;" + "x".repeat(8190) + "\r\n\r\n"], 100), &Ok(("", true, [], ""))),56 Test.equals("an unfinished line exactly at the limit waits for more", &summary(&["1".repeat(8192)], 100), &Ok(("", false, [], ""))),57 Test.equals("a size of fifteen digits is read", &summary(&["00000000000000A\r\n0123456789\r\n0\r\n\r\n"], 100), &Ok(("0123456789", true, [], ""))),58 Test.equals("a trailer name of one letter is a name", &summary(&["0\r\nX:1\r\n\r\n"], 100), &Ok(("", true, [("X", "1")], ""))),59 Test.equals("nothing is left over before the end", &textOf(&Chunked.leftover(&Chunked.decoder())), &"")60 ])61 let ran = Test.run(&checks)62 for failure in Test.failuresOf(&ran) {63 let _reported = Io.writeErrorLine(failure)64 }65 Test.report(&ran)66}67