
ContentTest.pudu
Pudu52 lines4.0 KB
1/** @Test.Content.Suite — message bodies and the headers describing them */2module PuduLangHttpClient.ContentTest34import Std.Io as Io5import Std.Json as Json6import Std.Test as Test7import PuduLangHttpClient.Content as Content8import PuduLangHttpClient as HttpClient910/** @Test.Content.Order — a body written and read as JSON through derived implementations */11type Order = { id: Int, item: Str, gift: Option[Bool] } derives Json.Encode, Json.Decode121314fn main() -> Int {15 let order = Order{id: 7, item: "tea", gift: None}16 let multipart = Content.multipartWith("b0", &[Content.field("note", "hi"), Content.file("doc", "a\"b.txt", "text/plain", &"x".toBytes())])17 let random = Content.multipart(&[Content.field("a", "1")])18 let checks = Test.suite("Content", &[19 Test.equals("an empty body has no bytes and no headers", &Content.empty(), &Content.Content{payload: "".toBytes(), headers: [], stream: None}),20 Test.equals("plain text is UTF-8 text", &Content.plain("héllo"), &Content.Content{payload: "héllo".toBytes(), headers: [("content-type", "text/plain; charset=utf-8")], stream: None}),21 Test.equals("text gains a charset only when it names none", &(Content.text("<p/>", "text/html").headers, Content.text("x", "text/csv; charset=latin1").headers), &([("content-type", "text/html; charset=utf-8")], [("content-type", "text/csv; charset=latin1")])),22 Test.equals("bytes keep their media type", &(Content.bytes(&"ab".toBytes(), "image/png").headers, Content.octets(&"ab".toBytes()).headers), &([("content-type", "image/png")], [("content-type", "application/octet-stream")])),23 Test.equals("a derived value is written as JSON", &Content.readText(&Content.jsonOf(&order)), &Ok("\{\"id\":7,\"item\":\"tea\",\"gift\":null\}")),24 Test.equals("JSON reads back into a derived value", &Content.readAs(&Content.jsonOf(&order)), &Ok(order)),25 Test.equals("a form is url-encoded", &Content.readText(&Content.form(&[("q", "a b"), ("x", "1&2")])), &Ok("q=a+b&x=1%262")),26 Test.equals("a form reads back into fields", &Content.readForm(&Content.form(&[("q", "a b"), ("x", "1&2")])), &Ok([("q", "a b"), ("x", "1&2")])),27 Test.equals("a multipart body frames each part", &Content.readText(&multipart), &Ok("--b0\r\ncontent-disposition: form-data; name=\"note\"\r\n\r\nhi\r\n--b0\r\ncontent-disposition: form-data; name=\"doc\"; filename=\"a\\\"b.txt\"\r\ncontent-type: text/plain\r\n\r\nx\r\n--b0--\r\n")),28 Test.equals("a multipart body names its boundary", &Content.mediaTypeOf(&multipart), &Some("multipart/form-data")),29 Test.that("a random boundary is used when none is given", match random.headers {30 case [(_, value)] => value.startsWith("multipart/form-data; boundary=pudu-")31 case _ => false32 }),33 Test.equals("a header is set once", &Content.withHeader(&Content.plain("x"), "Content-Type", "text/csv").headers, &[("Content-Type", "text/csv")]),34 Test.that("a streamed body holds a producer and no bytes", Content.isStreamed(&Content.streamed("text/csv", || None)) && Content.streamed("text/csv", || None).payload.isEmpty() && !Content.isStreamed(&Content.plain("x"))),35 Test.equals("the length counts bytes", &Content.length(&Content.plain("é")), &2),36 Test.equals("bytes that are not text are unreadable as text", &Content.readText(&Content.octets(&"é".toBytes().take(1))), &Err(HttpClient.Unreadable("the body is not UTF-8 text"))),37 Test.that("text that is not JSON is unreadable as JSON", match Content.readJson(&Content.plain("\{nope")) {38 case Err(HttpClient.Unreadable(reason)) => reason.startsWith("the body is not JSON: ")39 case _ => false40 }),41 Test.that("JSON of another shape is unreadable as the type", match Content.readAs[Order](&Content.plain("\{\"id\":\"x\"\}")) {42 case Err(HttpClient.Unreadable(reason)) => reason.startsWith("the body does not decode: ")43 case _ => false44 })45 ])46 let ran = Test.run(&checks)47 for failure in Test.failuresOf(&ran) {48 let _reported = Io.writeErrorLine(failure)49 }50 Test.report(&ran)51}52