[{"data":1,"prerenderedAt":3418},["ShallowReactive",2],{"navigation_docs":3,"-guide-testing":93,"-guide-testing-surround":3413},[4,71],{"title":5,"icon":6,"path":7,"stem":8,"children":9,"page":70},"Guide","i-lucide-book-open","\u002Fguide","1.guide",[10,14,18,22,26,30,34,38,42,46,50,54,58,62,66],{"title":11,"path":12,"stem":13},"Getting started","\u002Fguide\u002Fgetting-started","1.guide\u002F01.getting-started",{"title":15,"path":16,"stem":17},"Adoption paths","\u002Fguide\u002Fadoption","1.guide\u002F02.adoption",{"title":19,"path":20,"stem":21},"Routes and typed $fetch","\u002Fguide\u002Froutes","1.guide\u002F02.routes",{"title":23,"path":24,"stem":25},"Kyo handlers","\u002Fguide\u002Fkyo","1.guide\u002F03.kyo",{"title":27,"path":28,"stem":29},"ZIO handlers","\u002Fguide\u002Fzio","1.guide\u002F04.zio",{"title":31,"path":32,"stem":33},"Configuration and secrets","\u002Fguide\u002Fconfiguration","1.guide\u002F05.configuration",{"title":35,"path":36,"stem":37},"Application and request lifetimes","\u002Fguide\u002Flifetimes","1.guide\u002F06.lifetimes",{"title":39,"path":40,"stem":41},"Schemas","\u002Fguide\u002Fschema","1.guide\u002F07.schema",{"title":43,"path":44,"stem":45},"PostgreSQL","\u002Fguide\u002Fdatabase","1.guide\u002F08.database",{"title":47,"path":48,"stem":49},"Streaming and cancellation","\u002Fguide\u002Fstreaming","1.guide\u002F09.streaming",{"title":51,"path":52,"stem":53},"Testing","\u002Fguide\u002Ftesting","1.guide\u002F10.testing",{"title":55,"path":56,"stem":57},"Nitro storage and NuxtHub KV","\u002Fguide\u002Fstorage","1.guide\u002F11.storage",{"title":59,"path":60,"stem":61},"Debug Scala handlers","\u002Fguide\u002Fdebugging","1.guide\u002F12.debugging",{"title":63,"path":64,"stem":65},"npm build tooling","\u002Fguide\u002Fnpm-tooling","1.guide\u002F13.npm-tooling",{"title":67,"path":68,"stem":69},"Set up h34s with a coding assistant","\u002Fguide\u002Fagent-setup","1.guide\u002F14.agent-setup",false,{"title":72,"icon":73,"path":74,"stem":75,"children":76,"page":70},"Reference","i-lucide-library","\u002Freference","2.reference",[77,81,85,89],{"title":78,"path":79,"stem":80},"Architecture","\u002Freference\u002Farchitecture","2.reference\u002F1.architecture",{"title":82,"path":83,"stem":84},"Releases","\u002Freference\u002Freleases","2.reference\u002F2.releases",{"title":86,"path":87,"stem":88},"Compatibility","\u002Freference\u002Fcompatibility","2.reference\u002F3.compatibility",{"title":90,"path":91,"stem":92},"Migration guide","\u002Freference\u002Fmigration","2.reference\u002F4.migration",{"id":94,"title":51,"body":95,"description":3407,"extension":3408,"links":3409,"meta":3410,"navigation":343,"path":52,"seo":3411,"stem":53,"__hash__":3412},"docs\u002F1.guide\u002F10.testing.md",{"type":96,"value":97,"toc":3388},"minimark",[98,115,157,162,180,397,400,404,409,560,596,607,638,652,658,682,967,970,986,1111,1125,1131,1156,1362,1368,1695,1704,1711,1731,1739,1918,1938,2153,2176,2212,2216,2226,2229,2338,2341,2485,2492,2497,2520,2626,2628,2859,2881,2887,2988,3003,3007,3010,3086,3090,3100,3257,3261,3275,3279,3384],[99,100,101,102,106,107,110,111,114],"p",{},"A handler is a plain function from ",[103,104,105],"code",{},"Request"," to a program. Test it like any other function: build a fake request, run it with ",[103,108,109],{},"KHandler.test"," or ",[103,112,113],{},"ZHandler.test",", and check the head and the body. You need no h3, no Nitro and no server.",[99,116,117,121,122,125,126,129,130,132,133,132,135,138,139,142,143,146,147,138,150,153,154,156],{},[118,119,120],"strong",{},"Status."," ",[103,123,124],{},"Request.fake"," is new in ",[103,127,128],{},"0.2.0-alpha.1",". ",[103,131,109],{},", ",[103,134,113],{},[103,136,137],{},"TestResponse"," and ",[103,140,141],{},"Fetch.fake"," are new in ",[103,144,145],{},"0.3.0-alpha.1","; they replace the manual provisioning with ",[103,148,149],{},"Var.runTuple",[103,151,152],{},"ZIO.stateful"," that this guide showed for ",[103,155,128],{},".",[158,159,161],"h2",{"id":160},"keep-handlers-as-values","Keep handlers as values",[99,163,164,165,168,169,132,172,175,176,179],{},"Define the handler function as a value, and pass it to ",[103,166,167],{},"AppHandler.define"," (or ",[103,170,171],{},"KHandler.define",[103,173,174],{},"ZHandler.define",") separately. The demo's ZIO handlers do this in ",[103,177,178],{},"NotesHandler.scala",":",[181,182,187],"pre",{"className":183,"code":184,"language":185,"meta":186,"style":186},"language-scala shiki shiki-themes github-light github-light github-dark","object NotesHandler:\n    val create: Request => ZIO[HandlerEnv & Notes, Throwable, Note] = request =>\n      for\n        input \u003C- request.body.json[CreateNote]\n        note  \u003C- ZIO.serviceWithZIO[Notes](_.create(input.text))\n        _     \u003C- Response.status(Status.Created)\n      yield note\n\n@JSExportTopLevel(\"default\", \"notes-post\")\nval createNote: EventHandler = AppHandler.define(NotesHandler.create)\n","scala","",[103,188,189,205,261,267,285,304,329,338,345,368],{"__ignoreMap":186},[190,191,194,198,202],"span",{"class":192,"line":193},"line",1,[190,195,197],{"class":196},"s8jYJ","object",[190,199,201],{"class":200},"snPdu"," NotesHandler",[190,203,204],{"class":196},":\n",[190,206,208,211,215,217,220,223,226,230,233,236,239,241,244,246,249,252,255,258],{"class":192,"line":207},2,[190,209,210],{"class":196},"    val",[190,212,214],{"class":213},"sP4rz"," create",[190,216,179],{"class":196},[190,218,219],{"class":200}," Request",[190,221,222],{"class":196}," =>",[190,224,225],{"class":200}," ZIO",[190,227,229],{"class":228},"sxrX7","[",[190,231,232],{"class":200},"HandlerEnv",[190,234,235],{"class":196}," &",[190,237,238],{"class":200}," Notes",[190,240,132],{"class":228},[190,242,243],{"class":200},"Throwable",[190,245,132],{"class":228},[190,247,248],{"class":200},"Note",[190,250,251],{"class":228},"] ",[190,253,254],{"class":196},"=",[190,256,257],{"class":228}," request ",[190,259,260],{"class":196},"=>\n",[190,262,264],{"class":192,"line":263},3,[190,265,266],{"class":196},"      for\n",[190,268,270,273,276,279,282],{"class":192,"line":269},4,[190,271,272],{"class":228},"        input ",[190,274,275],{"class":196},"\u003C-",[190,277,278],{"class":228}," request.body.json[",[190,280,281],{"class":200},"CreateNote",[190,283,284],{"class":228},"]\n",[190,286,288,291,293,295,298,301],{"class":192,"line":287},5,[190,289,290],{"class":228},"        note  ",[190,292,275],{"class":196},[190,294,225],{"class":200},[190,296,297],{"class":228},".serviceWithZIO[",[190,299,300],{"class":200},"Notes",[190,302,303],{"class":228},"](_.create(input.text))\n",[190,305,307,310,312,315,318,321,323,326],{"class":192,"line":306},6,[190,308,309],{"class":228},"        _     ",[190,311,275],{"class":196},[190,313,314],{"class":200}," Response",[190,316,317],{"class":228},".status(",[190,319,320],{"class":200},"Status",[190,322,156],{"class":228},[190,324,325],{"class":200},"Created",[190,327,328],{"class":228},")\n",[190,330,332,335],{"class":192,"line":331},7,[190,333,334],{"class":196},"      yield",[190,336,337],{"class":228}," note\n",[190,339,341],{"class":192,"line":340},8,[190,342,344],{"emptyLinePlaceholder":343},true,"\n",[190,346,348,351,354,357,361,363,366],{"class":192,"line":347},9,[190,349,350],{"class":196},"@",[190,352,353],{"class":200},"JSExportTopLevel",[190,355,356],{"class":228},"(",[190,358,360],{"class":359},"sIIMD","\"default\"",[190,362,132],{"class":228},[190,364,365],{"class":359},"\"notes-post\"",[190,367,328],{"class":228},[190,369,371,374,377,379,382,385,388,391,394],{"class":192,"line":370},10,[190,372,373],{"class":196},"val",[190,375,376],{"class":213}," createNote",[190,378,179],{"class":196},[190,380,381],{"class":200}," EventHandler",[190,383,384],{"class":196}," =",[190,386,387],{"class":200}," AppHandler",[190,389,390],{"class":228},".define(",[190,392,393],{"class":200},"NotesHandler",[190,395,396],{"class":228},".create)\n",[99,398,399],{},"Put services behind traits, so a test can provide an in-memory implementation.",[158,401,403],{"id":402},"build-a-fake-request","Build a fake request",[99,405,406,408],{},[103,407,124],{}," takes named parameters, all optional:",[181,410,412],{"className":183,"code":411,"language":185,"meta":186,"style":186},"Request.fake(\n  method = \"POST\",\n  path = \"\u002Fapi\u002Fnotes\",\n  headers = Map(\"content-type\" -> \"application\u002Fjson\"),\n  query = Map(\"page\" -> List(\"2\")),\n  routerParams = Map(\"id\" -> \"7\"),\n  cookies = Map(\"session\" -> \"abc\"),\n  body = Some(\"\"\"{\"text\":\"hello\"}\"\"\"),\n)\n",[103,413,414,421,434,446,470,497,518,539,556],{"__ignoreMap":186},[190,415,416,418],{"class":192,"line":193},[190,417,105],{"class":200},[190,419,420],{"class":228},".fake(\n",[190,422,423,426,428,431],{"class":192,"line":207},[190,424,425],{"class":228},"  method ",[190,427,254],{"class":196},[190,429,430],{"class":359}," \"POST\"",[190,432,433],{"class":228},",\n",[190,435,436,439,441,444],{"class":192,"line":263},[190,437,438],{"class":228},"  path ",[190,440,254],{"class":196},[190,442,443],{"class":359}," \"\u002Fapi\u002Fnotes\"",[190,445,433],{"class":228},[190,447,448,451,453,456,458,461,464,467],{"class":192,"line":269},[190,449,450],{"class":228},"  headers ",[190,452,254],{"class":196},[190,454,455],{"class":200}," Map",[190,457,356],{"class":228},[190,459,460],{"class":359},"\"content-type\"",[190,462,463],{"class":196}," ->",[190,465,466],{"class":359}," \"application\u002Fjson\"",[190,468,469],{"class":228},"),\n",[190,471,472,475,477,479,481,484,486,489,491,494],{"class":192,"line":287},[190,473,474],{"class":228},"  query ",[190,476,254],{"class":196},[190,478,455],{"class":200},[190,480,356],{"class":228},[190,482,483],{"class":359},"\"page\"",[190,485,463],{"class":196},[190,487,488],{"class":200}," List",[190,490,356],{"class":228},[190,492,493],{"class":359},"\"2\"",[190,495,496],{"class":228},")),\n",[190,498,499,502,504,506,508,511,513,516],{"class":192,"line":306},[190,500,501],{"class":228},"  routerParams ",[190,503,254],{"class":196},[190,505,455],{"class":200},[190,507,356],{"class":228},[190,509,510],{"class":359},"\"id\"",[190,512,463],{"class":196},[190,514,515],{"class":359}," \"7\"",[190,517,469],{"class":228},[190,519,520,523,525,527,529,532,534,537],{"class":192,"line":331},[190,521,522],{"class":228},"  cookies ",[190,524,254],{"class":196},[190,526,455],{"class":200},[190,528,356],{"class":228},[190,530,531],{"class":359},"\"session\"",[190,533,463],{"class":196},[190,535,536],{"class":359}," \"abc\"",[190,538,469],{"class":228},[190,540,541,544,546,549,551,554],{"class":192,"line":340},[190,542,543],{"class":228},"  body ",[190,545,254],{"class":196},[190,547,548],{"class":200}," Some",[190,550,356],{"class":228},[190,552,553],{"class":359},"\"\"\"{\"text\":\"hello\"}\"\"\"",[190,555,469],{"class":228},[190,557,558],{"class":192,"line":347},[190,559,328],{"class":228},[99,561,562,132,565,132,568,132,571,138,574,577,578,132,581,583,584,587,588,591,592,595],{},[103,563,564],{},"development",[103,566,567],{},"debug",[103,569,570],{},"bodyLimit",[103,572,573],{},"requireJsonContentType",[103,575,576],{},"runtimeConfig"," are also available. Body reads follow the same rules as a real request: the content type, the limit, invalid JSON and schema validation behave the same. Like ",[103,579,580],{},"HandlerOptions",[103,582,124],{}," requires a JSON content type by default, so give a fake request with a body that you read with ",[103,585,586],{},"json[A]"," a ",[103,589,590],{},"content-type"," header. Pass ",[103,593,594],{},"requireJsonContentType = false"," to test a handler that turns the option off.",[99,597,598,599,602,603,606],{},"A handler that reads configuration per request with ",[103,600,601],{},"RuntimeConfig.get(request)"," (Kyo) or ",[103,604,605],{},"ZRuntimeConfig.get(request)"," (ZIO) needs the config in the fake request, as JSON text:",[181,608,610],{"className":183,"code":609,"language":185,"meta":186,"style":186},"val request = Request.fake(runtimeConfig = Some(\"\"\"{\"greeting\":{\"salutation\":\"Hi\"}}\"\"\"))\n",[103,611,612],{"__ignoreMap":186},[190,613,614,616,619,621,623,626,628,630,632,635],{"class":192,"line":193},[190,615,373],{"class":196},[190,617,618],{"class":213}," request",[190,620,384],{"class":196},[190,622,219],{"class":200},[190,624,625],{"class":228},".fake(runtimeConfig ",[190,627,254],{"class":196},[190,629,548],{"class":200},[190,631,356],{"class":228},[190,633,634],{"class":359},"\"\"\"{\"greeting\":{\"salutation\":\"Hi\"}}\"\"\"",[190,636,637],{"class":228},"))\n",[99,639,640,641,643,644,647,648,651],{},"Without ",[103,642,576],{},", those reads fail with ",[103,645,646],{},"ConfigError(\"h34s Nitro plugin is not installed\")",", as they do without the h34s module. Often it is simpler to read configuration in your ",[103,649,650],{},"AppHandler"," and pass values to handlers as services.",[158,653,655,656],{"id":654},"kyo-khandlertest","Kyo: ",[103,657,109],{},[99,659,660,661,664,665,667,668,671,672,675,676,678,679,179],{},"Call ",[103,662,663],{},"KHandler.test(handler)(request)",". It provides what ",[103,666,171],{}," provides for a request: a request scope, an empty response head, a transport for ",[103,669,670],{},"Response.sendStream"," and a server-side ",[103,673,674],{},"$fetch",". It gives a ",[103,677,137],{}," with the head that your program set and the body. This example is the \"guide's example\" test of ",[103,680,681],{},"HandlerTestSuite",[181,683,685],{"className":183,"code":684,"language":185,"meta":186,"style":186},"val handler: Request => Greeting \u003C HandlerEffects = request =>\n  for\n    input \u003C- request.body.json[Input]\n    _     \u003C- Response.status(Status.Created)\n  yield Greeting(s\"Hello, ${input.name}\")\n\nval request = Request.fake(method = \"POST\", headers = Map(\"content-type\" -> \"application\u002Fjson\"), body = Some(\"\"\"{\"name\":\"Scala\"}\"\"\"))\nval program = KHandler.test(handler)(request)\n\u002F\u002F program: TestResponse[Greeting] \u003C (Async & Abort[Throwable])\n\nimport AllowUnsafe.embrace.danger\nFiber.Unsafe.init(Abort.run[Throwable](program)).toFuture().map { result =>\n  val response = result.getOrThrow\n  assertEquals(response.status, Some(Status.Created))\n  assertEquals(response.body, TestResponse.Body.Value(Greeting(\"Hello, Scala\")))\n}\n",[103,686,687,715,720,734,753,776,780,826,841,847,851,870,897,911,930,961],{"__ignoreMap":186},[190,688,689,691,694,696,698,700,703,706,709,711,713],{"class":192,"line":193},[190,690,373],{"class":196},[190,692,693],{"class":213}," handler",[190,695,179],{"class":196},[190,697,219],{"class":200},[190,699,222],{"class":196},[190,701,702],{"class":200}," Greeting",[190,704,705],{"class":196}," \u003C",[190,707,708],{"class":200}," HandlerEffects",[190,710,384],{"class":196},[190,712,257],{"class":228},[190,714,260],{"class":196},[190,716,717],{"class":192,"line":207},[190,718,719],{"class":196},"  for\n",[190,721,722,725,727,729,732],{"class":192,"line":263},[190,723,724],{"class":228},"    input ",[190,726,275],{"class":196},[190,728,278],{"class":228},[190,730,731],{"class":200},"Input",[190,733,284],{"class":228},[190,735,736,739,741,743,745,747,749,751],{"class":192,"line":269},[190,737,738],{"class":228},"    _     ",[190,740,275],{"class":196},[190,742,314],{"class":200},[190,744,317],{"class":228},[190,746,320],{"class":200},[190,748,156],{"class":228},[190,750,325],{"class":200},[190,752,328],{"class":228},[190,754,755,758,760,762,765,768,771,774],{"class":192,"line":287},[190,756,757],{"class":196},"  yield",[190,759,702],{"class":200},[190,761,356],{"class":228},[190,763,764],{"class":196},"s",[190,766,767],{"class":359},"\"Hello, ",[190,769,770],{"class":228},"${input.name}",[190,772,773],{"class":359},"\"",[190,775,328],{"class":228},[190,777,778],{"class":192,"line":306},[190,779,344],{"emptyLinePlaceholder":343},[190,781,782,784,786,788,790,793,795,797,800,802,804,806,808,810,812,815,817,819,821,824],{"class":192,"line":331},[190,783,373],{"class":196},[190,785,618],{"class":213},[190,787,384],{"class":196},[190,789,219],{"class":200},[190,791,792],{"class":228},".fake(method ",[190,794,254],{"class":196},[190,796,430],{"class":359},[190,798,799],{"class":228},", headers ",[190,801,254],{"class":196},[190,803,455],{"class":200},[190,805,356],{"class":228},[190,807,460],{"class":359},[190,809,463],{"class":196},[190,811,466],{"class":359},[190,813,814],{"class":228},"), body ",[190,816,254],{"class":196},[190,818,548],{"class":200},[190,820,356],{"class":228},[190,822,823],{"class":359},"\"\"\"{\"name\":\"Scala\"}\"\"\"",[190,825,637],{"class":228},[190,827,828,830,833,835,838],{"class":192,"line":340},[190,829,373],{"class":196},[190,831,832],{"class":213}," program",[190,834,384],{"class":196},[190,836,837],{"class":200}," KHandler",[190,839,840],{"class":228},".test(handler)(request)\n",[190,842,843],{"class":192,"line":347},[190,844,846],{"class":845},"sCsY4","\u002F\u002F program: TestResponse[Greeting] \u003C (Async & Abort[Throwable])\n",[190,848,849],{"class":192,"line":370},[190,850,344],{"emptyLinePlaceholder":343},[190,852,854,857,860,862,865,867],{"class":192,"line":853},11,[190,855,856],{"class":196},"import",[190,858,859],{"class":200}," AllowUnsafe",[190,861,156],{"class":228},[190,863,864],{"class":200},"embrace",[190,866,156],{"class":228},[190,868,869],{"class":200},"danger\n",[190,871,873,876,878,881,884,887,890,892,895],{"class":192,"line":872},12,[190,874,875],{"class":200},"Fiber",[190,877,156],{"class":228},[190,879,880],{"class":200},"Unsafe",[190,882,883],{"class":228},".init(",[190,885,886],{"class":200},"Abort",[190,888,889],{"class":228},".run[",[190,891,243],{"class":200},[190,893,894],{"class":228},"](program)).toFuture().map { result ",[190,896,260],{"class":196},[190,898,900,903,906,908],{"class":192,"line":899},13,[190,901,902],{"class":196},"  val",[190,904,905],{"class":213}," response",[190,907,384],{"class":196},[190,909,910],{"class":228}," result.getOrThrow\n",[190,912,914,917,920,922,924,926,928],{"class":192,"line":913},14,[190,915,916],{"class":228},"  assertEquals(response.status, ",[190,918,919],{"class":200},"Some",[190,921,356],{"class":228},[190,923,320],{"class":200},[190,925,156],{"class":228},[190,927,325],{"class":200},[190,929,637],{"class":228},[190,931,933,936,938,940,943,945,948,950,953,955,958],{"class":192,"line":932},15,[190,934,935],{"class":228},"  assertEquals(response.body, ",[190,937,137],{"class":200},[190,939,156],{"class":228},[190,941,942],{"class":200},"Body",[190,944,156],{"class":228},[190,946,947],{"class":200},"Value",[190,949,356],{"class":228},[190,951,952],{"class":200},"Greeting",[190,954,356],{"class":228},[190,956,957],{"class":359},"\"Hello, Scala\"",[190,959,960],{"class":228},")))\n",[190,962,964],{"class":192,"line":963},16,[190,965,966],{"class":228},"}\n",[99,968,969],{},"The last part runs the program with munit, as the h34s suites do. Use your own Kyo test setup if you have one.",[99,971,972,973,976,977,129,979,982,983,179],{},"Provide services inside the function, with ",[103,974,975],{},"Env.run",", as you do for ",[103,978,171],{},[103,980,981],{},"fake"," is your in-memory ",[103,984,985],{},"Greetings",[181,987,989],{"className":183,"code":988,"language":185,"meta":186,"style":186},"val handler: Request => Greeting \u003C (HandlerEffects & Env[Greetings]) = request =>\n  Env.use[Greetings](greetings => Greeting(greetings.greet(request.query.getOrElse(\"name\", \"you\"))))\n\nval program = KHandler.test(request => Env.run(fake)(handler(request)))(Request.fake(query = Map(\"name\" -> List(\"Ada\"))))\n",[103,990,991,1031,1063,1067],{"__ignoreMap":186},[190,992,993,995,997,999,1001,1003,1005,1007,1010,1013,1015,1018,1020,1022,1025,1027,1029],{"class":192,"line":193},[190,994,373],{"class":196},[190,996,693],{"class":213},[190,998,179],{"class":196},[190,1000,219],{"class":200},[190,1002,222],{"class":196},[190,1004,702],{"class":200},[190,1006,705],{"class":196},[190,1008,1009],{"class":228}," (",[190,1011,1012],{"class":200},"HandlerEffects",[190,1014,235],{"class":196},[190,1016,1017],{"class":200}," Env",[190,1019,229],{"class":228},[190,1021,985],{"class":200},[190,1023,1024],{"class":228},"]) ",[190,1026,254],{"class":196},[190,1028,257],{"class":228},[190,1030,260],{"class":196},[190,1032,1033,1036,1039,1041,1044,1047,1049,1052,1055,1057,1060],{"class":192,"line":207},[190,1034,1035],{"class":200},"  Env",[190,1037,1038],{"class":228},".use[",[190,1040,985],{"class":200},[190,1042,1043],{"class":228},"](greetings ",[190,1045,1046],{"class":196},"=>",[190,1048,702],{"class":200},[190,1050,1051],{"class":228},"(greetings.greet(request.query.getOrElse(",[190,1053,1054],{"class":359},"\"name\"",[190,1056,132],{"class":228},[190,1058,1059],{"class":359},"\"you\"",[190,1061,1062],{"class":228},"))))\n",[190,1064,1065],{"class":192,"line":263},[190,1066,344],{"emptyLinePlaceholder":343},[190,1068,1069,1071,1073,1075,1077,1080,1082,1084,1087,1089,1092,1094,1096,1098,1100,1102,1104,1106,1109],{"class":192,"line":269},[190,1070,373],{"class":196},[190,1072,832],{"class":213},[190,1074,384],{"class":196},[190,1076,837],{"class":200},[190,1078,1079],{"class":228},".test(request ",[190,1081,1046],{"class":196},[190,1083,1017],{"class":200},[190,1085,1086],{"class":228},".run(fake)(handler(request)))(",[190,1088,105],{"class":200},[190,1090,1091],{"class":228},".fake(query ",[190,1093,254],{"class":196},[190,1095,455],{"class":200},[190,1097,356],{"class":228},[190,1099,1054],{"class":359},[190,1101,463],{"class":196},[190,1103,488],{"class":200},[190,1105,356],{"class":228},[190,1107,1108],{"class":359},"\"Ada\"",[190,1110,1062],{"class":228},[99,1112,1113,1114,1117,1118,1121,1122,156],{},"A failure of your program stays a failure of ",[103,1115,1116],{},"program",". Run it with ",[103,1119,1120],{},"Abort.run"," to check an error, for example ",[103,1123,1124],{},"Abort.run[HttpError](KHandler.test(handler)(request))",[158,1126,1128,1129],{"id":1127},"zio-zhandlertest","ZIO: ",[103,1130,113],{},[99,1132,660,1133,1136,1137,1140,1141,1144,1145,671,1147,1149,1150,1152,1153,179],{},[103,1134,1135],{},"ZHandler.test(handler)(request)",". It provides the ",[103,1138,1139],{},"ZState[ResponseHead]",", a request ",[103,1142,1143],{},"Scope",", the transport for ",[103,1146,670],{},[103,1148,674],{},", and gives a ",[103,1151,137],{},". Your program's typed error stays in the error channel. This example is the \"guide's example\" test of ",[103,1154,1155],{},"HandlerTestSpec",[181,1157,1159],{"className":183,"code":1158,"language":185,"meta":186,"style":186},"val handler: Request => ZIO[HandlerEnv, Throwable, Greeting] = request =>\n  for\n    input \u003C- request.body.json[Input]\n    _     \u003C- Response.status(Status.Created)\n  yield Greeting(s\"Hello, ${input.name}\")\n\nval request = Request.fake(method = \"POST\", headers = Map(\"content-type\" -> \"application\u002Fjson\"), body = Some(\"\"\"{\"name\":\"Scala\"}\"\"\"))\nfor response \u003C- ZHandler.test(handler)(request)\nyield assertTrue(\n  response.status.contains(Status.Created),\n  response.body == TestResponse.Body.Value(Greeting(\"Hello, Scala\")),\n)\n",[103,1160,1161,1195,1199,1211,1229,1247,1251,1293,1308,1316,1329,1358],{"__ignoreMap":186},[190,1162,1163,1165,1167,1169,1171,1173,1175,1177,1179,1181,1183,1185,1187,1189,1191,1193],{"class":192,"line":193},[190,1164,373],{"class":196},[190,1166,693],{"class":213},[190,1168,179],{"class":196},[190,1170,219],{"class":200},[190,1172,222],{"class":196},[190,1174,225],{"class":200},[190,1176,229],{"class":228},[190,1178,232],{"class":200},[190,1180,132],{"class":228},[190,1182,243],{"class":200},[190,1184,132],{"class":228},[190,1186,952],{"class":200},[190,1188,251],{"class":228},[190,1190,254],{"class":196},[190,1192,257],{"class":228},[190,1194,260],{"class":196},[190,1196,1197],{"class":192,"line":207},[190,1198,719],{"class":196},[190,1200,1201,1203,1205,1207,1209],{"class":192,"line":263},[190,1202,724],{"class":228},[190,1204,275],{"class":196},[190,1206,278],{"class":228},[190,1208,731],{"class":200},[190,1210,284],{"class":228},[190,1212,1213,1215,1217,1219,1221,1223,1225,1227],{"class":192,"line":269},[190,1214,738],{"class":228},[190,1216,275],{"class":196},[190,1218,314],{"class":200},[190,1220,317],{"class":228},[190,1222,320],{"class":200},[190,1224,156],{"class":228},[190,1226,325],{"class":200},[190,1228,328],{"class":228},[190,1230,1231,1233,1235,1237,1239,1241,1243,1245],{"class":192,"line":287},[190,1232,757],{"class":196},[190,1234,702],{"class":200},[190,1236,356],{"class":228},[190,1238,764],{"class":196},[190,1240,767],{"class":359},[190,1242,770],{"class":228},[190,1244,773],{"class":359},[190,1246,328],{"class":228},[190,1248,1249],{"class":192,"line":306},[190,1250,344],{"emptyLinePlaceholder":343},[190,1252,1253,1255,1257,1259,1261,1263,1265,1267,1269,1271,1273,1275,1277,1279,1281,1283,1285,1287,1289,1291],{"class":192,"line":331},[190,1254,373],{"class":196},[190,1256,618],{"class":213},[190,1258,384],{"class":196},[190,1260,219],{"class":200},[190,1262,792],{"class":228},[190,1264,254],{"class":196},[190,1266,430],{"class":359},[190,1268,799],{"class":228},[190,1270,254],{"class":196},[190,1272,455],{"class":200},[190,1274,356],{"class":228},[190,1276,460],{"class":359},[190,1278,463],{"class":196},[190,1280,466],{"class":359},[190,1282,814],{"class":228},[190,1284,254],{"class":196},[190,1286,548],{"class":200},[190,1288,356],{"class":228},[190,1290,823],{"class":359},[190,1292,637],{"class":228},[190,1294,1295,1298,1301,1303,1306],{"class":192,"line":340},[190,1296,1297],{"class":196},"for",[190,1299,1300],{"class":228}," response ",[190,1302,275],{"class":196},[190,1304,1305],{"class":200}," ZHandler",[190,1307,840],{"class":228},[190,1309,1310,1313],{"class":192,"line":347},[190,1311,1312],{"class":196},"yield",[190,1314,1315],{"class":228}," assertTrue(\n",[190,1317,1318,1321,1323,1325,1327],{"class":192,"line":370},[190,1319,1320],{"class":228},"  response.status.contains(",[190,1322,320],{"class":200},[190,1324,156],{"class":228},[190,1326,325],{"class":200},[190,1328,469],{"class":228},[190,1330,1331,1334,1337,1340,1342,1344,1346,1348,1350,1352,1354,1356],{"class":192,"line":853},[190,1332,1333],{"class":228},"  response.body ",[190,1335,1336],{"class":196},"==",[190,1338,1339],{"class":200}," TestResponse",[190,1341,156],{"class":228},[190,1343,942],{"class":200},[190,1345,156],{"class":228},[190,1347,947],{"class":200},[190,1349,356],{"class":228},[190,1351,952],{"class":200},[190,1353,356],{"class":228},[190,1355,957],{"class":359},[190,1357,496],{"class":228},[190,1359,1360],{"class":192,"line":872},[190,1361,328],{"class":228},[99,1363,1364,1365,1367],{},"Provide services inside the function, as you do for ",[103,1366,174],{},". A rejected request fails with the same error that a client would see as its status:",[181,1369,1371],{"className":183,"code":1370,"language":185,"meta":186,"style":186},"val handler: Request => ZIO[HandlerEnv & Greetings, HttpError, Greeting] = request =>\n  for\n    name     \u003C- ZIO.fromOption(request.query.get(\"name\")).orElseFail(HttpError(400, \"name is required\"))\n    greeting \u003C- ZIO.serviceWithZIO[Greetings](_.greet(name))\n  yield Greeting(greeting)\n\nval fake = new Greetings:\n    def greet(name: String): UIO[String] = ZIO.succeed(s\"Hi, $name\")\ndef run(request: Request) =\n  ZHandler.test(request => handler(request).provideSomeEnvironment[HandlerEnv](_.add[Greetings](fake)))(request)\n\nfor\n  greeted  \u003C- run(Request.fake(query = Map(\"name\" -> List(\"Ada\"))))\n  rejected \u003C- run(Request.fake()).either\nyield assertTrue(\n  greeted.body == TestResponse.Body.Value(Greeting(\"Hi, Ada\")),\n  rejected.left.toOption.map(_.status).contains(400),\n)\n",[103,1372,1373,1413,1417,1449,1465,1474,1478,1494,1546,1569,1591,1595,1600,1632,1646,1652,1680,1690],{"__ignoreMap":186},[190,1374,1375,1377,1379,1381,1383,1385,1387,1389,1391,1393,1396,1398,1401,1403,1405,1407,1409,1411],{"class":192,"line":193},[190,1376,373],{"class":196},[190,1378,693],{"class":213},[190,1380,179],{"class":196},[190,1382,219],{"class":200},[190,1384,222],{"class":196},[190,1386,225],{"class":200},[190,1388,229],{"class":228},[190,1390,232],{"class":200},[190,1392,235],{"class":196},[190,1394,1395],{"class":200}," Greetings",[190,1397,132],{"class":228},[190,1399,1400],{"class":200},"HttpError",[190,1402,132],{"class":228},[190,1404,952],{"class":200},[190,1406,251],{"class":228},[190,1408,254],{"class":196},[190,1410,257],{"class":228},[190,1412,260],{"class":196},[190,1414,1415],{"class":192,"line":207},[190,1416,719],{"class":196},[190,1418,1419,1422,1424,1426,1429,1431,1434,1436,1438,1442,1444,1447],{"class":192,"line":263},[190,1420,1421],{"class":228},"    name     ",[190,1423,275],{"class":196},[190,1425,225],{"class":200},[190,1427,1428],{"class":228},".fromOption(request.query.get(",[190,1430,1054],{"class":359},[190,1432,1433],{"class":228},")).orElseFail(",[190,1435,1400],{"class":200},[190,1437,356],{"class":228},[190,1439,1441],{"class":1440},"sBjJW","400",[190,1443,132],{"class":228},[190,1445,1446],{"class":359},"\"name is required\"",[190,1448,637],{"class":228},[190,1450,1451,1454,1456,1458,1460,1462],{"class":192,"line":269},[190,1452,1453],{"class":228},"    greeting ",[190,1455,275],{"class":196},[190,1457,225],{"class":200},[190,1459,297],{"class":228},[190,1461,985],{"class":200},[190,1463,1464],{"class":228},"](_.greet(name))\n",[190,1466,1467,1469,1471],{"class":192,"line":287},[190,1468,757],{"class":196},[190,1470,702],{"class":200},[190,1472,1473],{"class":228},"(greeting)\n",[190,1475,1476],{"class":192,"line":306},[190,1477,344],{"emptyLinePlaceholder":343},[190,1479,1480,1482,1485,1487,1490,1492],{"class":192,"line":331},[190,1481,373],{"class":196},[190,1483,1484],{"class":213}," fake",[190,1486,384],{"class":196},[190,1488,1489],{"class":196}," new",[190,1491,1395],{"class":200},[190,1493,204],{"class":196},[190,1495,1496,1499,1502,1504,1507,1510,1513,1516,1518,1521,1523,1525,1527,1529,1531,1534,1536,1539,1542,1544],{"class":192,"line":340},[190,1497,1498],{"class":196},"    def",[190,1500,1501],{"class":200}," greet",[190,1503,356],{"class":228},[190,1505,1506],{"class":213},"name",[190,1508,1509],{"class":228},": ",[190,1511,1512],{"class":200},"String",[190,1514,1515],{"class":228},")",[190,1517,179],{"class":196},[190,1519,1520],{"class":200}," UIO",[190,1522,229],{"class":228},[190,1524,1512],{"class":200},[190,1526,251],{"class":228},[190,1528,254],{"class":196},[190,1530,225],{"class":200},[190,1532,1533],{"class":228},".succeed(",[190,1535,764],{"class":196},[190,1537,1538],{"class":359},"\"Hi, ",[190,1540,1541],{"class":228},"$name",[190,1543,773],{"class":359},[190,1545,328],{"class":228},[190,1547,1548,1551,1554,1556,1559,1561,1563,1566],{"class":192,"line":347},[190,1549,1550],{"class":196},"def",[190,1552,1553],{"class":200}," run",[190,1555,356],{"class":228},[190,1557,1558],{"class":213},"request",[190,1560,1509],{"class":228},[190,1562,105],{"class":200},[190,1564,1565],{"class":228},") ",[190,1567,1568],{"class":196},"=\n",[190,1570,1571,1574,1576,1578,1581,1583,1586,1588],{"class":192,"line":370},[190,1572,1573],{"class":200},"  ZHandler",[190,1575,1079],{"class":228},[190,1577,1046],{"class":196},[190,1579,1580],{"class":228}," handler(request).provideSomeEnvironment[",[190,1582,232],{"class":200},[190,1584,1585],{"class":228},"](_.add[",[190,1587,985],{"class":200},[190,1589,1590],{"class":228},"](fake)))(request)\n",[190,1592,1593],{"class":192,"line":853},[190,1594,344],{"emptyLinePlaceholder":343},[190,1596,1597],{"class":192,"line":872},[190,1598,1599],{"class":196},"for\n",[190,1601,1602,1605,1607,1610,1612,1614,1616,1618,1620,1622,1624,1626,1628,1630],{"class":192,"line":899},[190,1603,1604],{"class":228},"  greeted  ",[190,1606,275],{"class":196},[190,1608,1609],{"class":228}," run(",[190,1611,105],{"class":200},[190,1613,1091],{"class":228},[190,1615,254],{"class":196},[190,1617,455],{"class":200},[190,1619,356],{"class":228},[190,1621,1054],{"class":359},[190,1623,463],{"class":196},[190,1625,488],{"class":200},[190,1627,356],{"class":228},[190,1629,1108],{"class":359},[190,1631,1062],{"class":228},[190,1633,1634,1637,1639,1641,1643],{"class":192,"line":913},[190,1635,1636],{"class":228},"  rejected ",[190,1638,275],{"class":196},[190,1640,1609],{"class":228},[190,1642,105],{"class":200},[190,1644,1645],{"class":228},".fake()).either\n",[190,1647,1648,1650],{"class":192,"line":932},[190,1649,1312],{"class":196},[190,1651,1315],{"class":228},[190,1653,1654,1657,1659,1661,1663,1665,1667,1669,1671,1673,1675,1678],{"class":192,"line":963},[190,1655,1656],{"class":228},"  greeted.body ",[190,1658,1336],{"class":196},[190,1660,1339],{"class":200},[190,1662,156],{"class":228},[190,1664,942],{"class":200},[190,1666,156],{"class":228},[190,1668,947],{"class":200},[190,1670,356],{"class":228},[190,1672,952],{"class":200},[190,1674,356],{"class":228},[190,1676,1677],{"class":359},"\"Hi, Ada\"",[190,1679,496],{"class":228},[190,1681,1683,1686,1688],{"class":192,"line":1682},17,[190,1684,1685],{"class":228},"  rejected.left.toOption.map(_.status).contains(",[190,1687,1441],{"class":1440},[190,1689,469],{"class":228},[190,1691,1693],{"class":192,"line":1692},18,[190,1694,328],{"class":228},[99,1696,1697,1698,1701,1702,156],{},"The demo's ",[103,1699,1700],{},"NotesHandlerSpec"," tests its real handlers this way, with an in-memory ",[103,1703,300],{},[158,1705,1707,1708,1710],{"id":1706},"answer-fetch-calls","Answer ",[103,1709,674],{}," calls",[99,1712,1713,138,1715,1717,1718,1721,1722,1724,1725,1727,1728,1730],{},[103,1714,109],{},[103,1716,113],{}," use ",[103,1719,1720],{},"Fetch.unavailable"," by default: a handler that calls ",[103,1723,674],{}," fails (Kyo) or dies (ZIO), and the message names ",[103,1726,141],{},". Pass ",[103,1729,141],{}," as the second argument to answer calls from a map of URL to response. It records every request, and a URL without an entry answers 404.",[99,1732,1733,1734,1736,1737,179],{},"Kyo, the ",[103,1735,141],{}," test of ",[103,1738,681],{},[181,1740,1742],{"className":183,"code":1741,"language":185,"meta":186,"style":186},"val fetch = Fetch.fake(Map(\n  \"\u002Fapi\u002Fgreeting\" -> FetchResponse.Plain(200, Map(\"content-type\" -> \"application\u002Fjson\"), \"\"\"{\"data\":{\"message\":\"hi\"}}\"\"\"),\n))\nval handler: Request => String \u003C HandlerEffects = _ =>\n  for\n    greeting \u003C- KHandler.$fetch.get[GreetingData](\"\u002Fapi\u002Fgreeting\")\n    missing  \u003C- KHandler.$fetch.raw(\"\u002Fapi\u002Fmissing\")\n  yield s\"${greeting.data.message} ${missing.status}\"\n\nval program = KHandler.test(handler)(Request.fake(), fetch)\n",[103,1743,1744,1765,1805,1809,1835,1839,1861,1878,1896,1900],{"__ignoreMap":186},[190,1745,1746,1748,1751,1753,1756,1759,1762],{"class":192,"line":193},[190,1747,373],{"class":196},[190,1749,1750],{"class":213}," fetch",[190,1752,384],{"class":196},[190,1754,1755],{"class":200}," Fetch",[190,1757,1758],{"class":228},".fake(",[190,1760,1761],{"class":200},"Map",[190,1763,1764],{"class":228},"(\n",[190,1766,1767,1770,1772,1775,1777,1780,1782,1785,1787,1789,1791,1793,1795,1797,1800,1803],{"class":192,"line":207},[190,1768,1769],{"class":359},"  \"\u002Fapi\u002Fgreeting\"",[190,1771,463],{"class":196},[190,1773,1774],{"class":200}," FetchResponse",[190,1776,156],{"class":228},[190,1778,1779],{"class":200},"Plain",[190,1781,356],{"class":228},[190,1783,1784],{"class":1440},"200",[190,1786,132],{"class":228},[190,1788,1761],{"class":200},[190,1790,356],{"class":228},[190,1792,460],{"class":359},[190,1794,463],{"class":196},[190,1796,466],{"class":359},[190,1798,1799],{"class":228},"), ",[190,1801,1802],{"class":359},"\"\"\"{\"data\":{\"message\":\"hi\"}}\"\"\"",[190,1804,469],{"class":228},[190,1806,1807],{"class":192,"line":263},[190,1808,637],{"class":228},[190,1810,1811,1813,1815,1817,1819,1821,1824,1826,1828,1830,1833],{"class":192,"line":269},[190,1812,373],{"class":196},[190,1814,693],{"class":213},[190,1816,179],{"class":196},[190,1818,219],{"class":200},[190,1820,222],{"class":196},[190,1822,1823],{"class":200}," String",[190,1825,705],{"class":196},[190,1827,708],{"class":200},[190,1829,384],{"class":196},[190,1831,1832],{"class":228}," _ ",[190,1834,260],{"class":196},[190,1836,1837],{"class":192,"line":287},[190,1838,719],{"class":196},[190,1840,1841,1843,1845,1847,1850,1853,1856,1859],{"class":192,"line":306},[190,1842,1453],{"class":228},[190,1844,275],{"class":196},[190,1846,837],{"class":200},[190,1848,1849],{"class":228},".$fetch.get[",[190,1851,1852],{"class":200},"GreetingData",[190,1854,1855],{"class":228},"](",[190,1857,1858],{"class":359},"\"\u002Fapi\u002Fgreeting\"",[190,1860,328],{"class":228},[190,1862,1863,1866,1868,1870,1873,1876],{"class":192,"line":331},[190,1864,1865],{"class":228},"    missing  ",[190,1867,275],{"class":196},[190,1869,837],{"class":200},[190,1871,1872],{"class":228},".$fetch.raw(",[190,1874,1875],{"class":359},"\"\u002Fapi\u002Fmissing\"",[190,1877,328],{"class":228},[190,1879,1880,1882,1885,1887,1890,1893],{"class":192,"line":340},[190,1881,757],{"class":196},[190,1883,1884],{"class":196}," s",[190,1886,773],{"class":359},[190,1888,1889],{"class":228},"${greeting.data.message}",[190,1891,1892],{"class":228}," ${missing.status}",[190,1894,1895],{"class":359},"\"\n",[190,1897,1898],{"class":192,"line":347},[190,1899,344],{"emptyLinePlaceholder":343},[190,1901,1902,1904,1906,1908,1910,1913,1915],{"class":192,"line":370},[190,1903,373],{"class":196},[190,1905,832],{"class":213},[190,1907,384],{"class":196},[190,1909,837],{"class":200},[190,1911,1912],{"class":228},".test(handler)(",[190,1914,105],{"class":200},[190,1916,1917],{"class":228},".fake(), fetch)\n",[99,1919,1920,1921,1736,1923,129,1925,132,1928,138,1931,1934,1935,179],{},"ZIO, the ",[103,1922,141],{},[103,1924,1155],{},[103,1926,1927],{},"Fetch",[103,1929,1930],{},"FetchResponse",[103,1932,1933],{},"FetchError"," come from ",[103,1936,1937],{},"dev.yuri4n.exts.zioh3",[181,1939,1941],{"className":183,"code":1940,"language":185,"meta":186,"style":186},"val fetch = Fetch.fake(Map(\n  \"\u002Fapi\u002Fgreeting\" -> FetchResponse.Plain(200, Map(\"content-type\" -> \"application\u002Fjson\"), \"\"\"{\"data\":{\"message\":\"hi\"}}\"\"\"),\n))\nval handler: Request => ZIO[HandlerEnv, FetchError, String] = _ =>\n  for\n    greeting \u003C- ZHandler.$fetch.get[GreetingData](\"\u002Fapi\u002Fgreeting\")\n    missing  \u003C- ZHandler.$fetch.raw(\"\u002Fapi\u002Fmissing\")\n  yield s\"${greeting.data.message} ${missing.status}\"\n\nfor response \u003C- ZHandler.test(handler)(Request.fake(), fetch)\nyield assertTrue(\n  response.body == TestResponse.Body.Value(\"hi 404\"),\n  fetch.requests.map(_.url) == List(\"\u002Fapi\u002Fgreeting\", \"\u002Fapi\u002Fmissing\"),\n)\n",[103,1942,1943,1959,1993,1997,2031,2035,2053,2067,2081,2085,2101,2107,2130,2149],{"__ignoreMap":186},[190,1944,1945,1947,1949,1951,1953,1955,1957],{"class":192,"line":193},[190,1946,373],{"class":196},[190,1948,1750],{"class":213},[190,1950,384],{"class":196},[190,1952,1755],{"class":200},[190,1954,1758],{"class":228},[190,1956,1761],{"class":200},[190,1958,1764],{"class":228},[190,1960,1961,1963,1965,1967,1969,1971,1973,1975,1977,1979,1981,1983,1985,1987,1989,1991],{"class":192,"line":207},[190,1962,1769],{"class":359},[190,1964,463],{"class":196},[190,1966,1774],{"class":200},[190,1968,156],{"class":228},[190,1970,1779],{"class":200},[190,1972,356],{"class":228},[190,1974,1784],{"class":1440},[190,1976,132],{"class":228},[190,1978,1761],{"class":200},[190,1980,356],{"class":228},[190,1982,460],{"class":359},[190,1984,463],{"class":196},[190,1986,466],{"class":359},[190,1988,1799],{"class":228},[190,1990,1802],{"class":359},[190,1992,469],{"class":228},[190,1994,1995],{"class":192,"line":263},[190,1996,637],{"class":228},[190,1998,1999,2001,2003,2005,2007,2009,2011,2013,2015,2017,2019,2021,2023,2025,2027,2029],{"class":192,"line":269},[190,2000,373],{"class":196},[190,2002,693],{"class":213},[190,2004,179],{"class":196},[190,2006,219],{"class":200},[190,2008,222],{"class":196},[190,2010,225],{"class":200},[190,2012,229],{"class":228},[190,2014,232],{"class":200},[190,2016,132],{"class":228},[190,2018,1933],{"class":200},[190,2020,132],{"class":228},[190,2022,1512],{"class":200},[190,2024,251],{"class":228},[190,2026,254],{"class":196},[190,2028,1832],{"class":228},[190,2030,260],{"class":196},[190,2032,2033],{"class":192,"line":287},[190,2034,719],{"class":196},[190,2036,2037,2039,2041,2043,2045,2047,2049,2051],{"class":192,"line":306},[190,2038,1453],{"class":228},[190,2040,275],{"class":196},[190,2042,1305],{"class":200},[190,2044,1849],{"class":228},[190,2046,1852],{"class":200},[190,2048,1855],{"class":228},[190,2050,1858],{"class":359},[190,2052,328],{"class":228},[190,2054,2055,2057,2059,2061,2063,2065],{"class":192,"line":331},[190,2056,1865],{"class":228},[190,2058,275],{"class":196},[190,2060,1305],{"class":200},[190,2062,1872],{"class":228},[190,2064,1875],{"class":359},[190,2066,328],{"class":228},[190,2068,2069,2071,2073,2075,2077,2079],{"class":192,"line":340},[190,2070,757],{"class":196},[190,2072,1884],{"class":196},[190,2074,773],{"class":359},[190,2076,1889],{"class":228},[190,2078,1892],{"class":228},[190,2080,1895],{"class":359},[190,2082,2083],{"class":192,"line":347},[190,2084,344],{"emptyLinePlaceholder":343},[190,2086,2087,2089,2091,2093,2095,2097,2099],{"class":192,"line":370},[190,2088,1297],{"class":196},[190,2090,1300],{"class":228},[190,2092,275],{"class":196},[190,2094,1305],{"class":200},[190,2096,1912],{"class":228},[190,2098,105],{"class":200},[190,2100,1917],{"class":228},[190,2102,2103,2105],{"class":192,"line":853},[190,2104,1312],{"class":196},[190,2106,1315],{"class":228},[190,2108,2109,2111,2113,2115,2117,2119,2121,2123,2125,2128],{"class":192,"line":872},[190,2110,1333],{"class":228},[190,2112,1336],{"class":196},[190,2114,1339],{"class":200},[190,2116,156],{"class":228},[190,2118,942],{"class":200},[190,2120,156],{"class":228},[190,2122,947],{"class":200},[190,2124,356],{"class":228},[190,2126,2127],{"class":359},"\"hi 404\"",[190,2129,469],{"class":228},[190,2131,2132,2135,2137,2139,2141,2143,2145,2147],{"class":192,"line":899},[190,2133,2134],{"class":228},"  fetch.requests.map(_.url) ",[190,2136,1336],{"class":196},[190,2138,488],{"class":200},[190,2140,356],{"class":228},[190,2142,1858],{"class":359},[190,2144,132],{"class":228},[190,2146,1875],{"class":359},[190,2148,469],{"class":228},[190,2150,2151],{"class":192,"line":913},[190,2152,328],{"class":228},[99,2154,2155,2156,2159,2160,2163,2164,2167,2168,2171,2172,2175],{},"In both, the body is ",[103,2157,2158],{},"TestResponse.Body.Value(\"hi 404\")",", and ",[103,2161,2162],{},"fetch.requests.map(_.url)"," is ",[103,2165,2166],{},"List(\"\u002Fapi\u002Fgreeting\", \"\u002Fapi\u002Fmissing\")",". A fake streamed response takes the stream of its runtime: ",[103,2169,2170],{},"FetchResponse.Streamed(200, headers, Stream.init(Seq(\"a\", \"b\")))"," in Kyo, ",[103,2173,2174],{},"FetchResponse.Streamed(200, headers, ZStream(\"a\", \"b\"))"," in ZIO.",[99,2177,2178,2180,2181,110,2183,2186,2187,2189,2190,2192,2193,2195,2196,2199,2200,2203,2204,2209,2210,156],{},[103,2179,141],{}," gives each response as you wrote it. It forwards no headers, adds no base URL, and does not apply the ",[103,2182,1779],{},[103,2184,2185],{},"Streamed"," rule of the real ",[103,2188,674],{},": the response in your map is what the handler gets. A fake ",[103,2191,2185],{}," body is your stream as you wrote it, too. The cancel of a body that the handler does not read to its end, and the wait for the called route's cleanup, belong to the real ",[103,2194,674],{},"; ",[103,2197,2198],{},"NitroLocalFetchSuite"," (Kyo) and ",[103,2201,2202],{},"NitroLocalFetchSpec"," (ZIO) test them. See ",[2205,2206,2208],"a",{"href":2207},"\u002Fguide\u002Fstreaming#in-process-calls","in-process calls"," for the rules of the real ",[103,2211,674],{},[158,2213,2215],{"id":2214},"test-a-streaming-handler","Test a streaming handler",[99,2217,2218,2219,2221,2222,2225],{},"The test helpers collect what your handler sends with ",[103,2220,670],{},". The body is then ",[103,2223,2224],{},"TestResponse.Body.Streamed(elements)",", the strings in order. The stream's resources are released when it ends, before the request scope closes, as in a real request.",[99,2227,2228],{},"Kyo:",[181,2230,2232],{"className":183,"code":2231,"language":185,"meta":186,"style":186},"val handler: Request => Sent \u003C HandlerEffects = _ =>\n  Response.header(\"content-type\", \"application\u002Fx-ndjson\").andThen(Response.sendStream(Stream.init(Seq(\"1\\n\", \"2\\n\"))))\n\nval program = KHandler.test(handler)(Request.fake())\n\u002F\u002F the body of the result: TestResponse.Body.Streamed(List(\"1\\n\", \"2\\n\"))\n",[103,2233,2234,2259,2312,2316,2333],{"__ignoreMap":186},[190,2235,2236,2238,2240,2242,2244,2246,2249,2251,2253,2255,2257],{"class":192,"line":193},[190,2237,373],{"class":196},[190,2239,693],{"class":213},[190,2241,179],{"class":196},[190,2243,219],{"class":200},[190,2245,222],{"class":196},[190,2247,2248],{"class":200}," Sent",[190,2250,705],{"class":196},[190,2252,708],{"class":200},[190,2254,384],{"class":196},[190,2256,1832],{"class":228},[190,2258,260],{"class":196},[190,2260,2261,2264,2267,2269,2271,2274,2277,2280,2283,2286,2288,2291,2293,2296,2299,2301,2303,2306,2308,2310],{"class":192,"line":207},[190,2262,2263],{"class":200},"  Response",[190,2265,2266],{"class":228},".header(",[190,2268,460],{"class":359},[190,2270,132],{"class":228},[190,2272,2273],{"class":359},"\"application\u002Fx-ndjson\"",[190,2275,2276],{"class":228},").andThen(",[190,2278,2279],{"class":200},"Response",[190,2281,2282],{"class":228},".sendStream(",[190,2284,2285],{"class":200},"Stream",[190,2287,883],{"class":228},[190,2289,2290],{"class":200},"Seq",[190,2292,356],{"class":228},[190,2294,2295],{"class":359},"\"1",[190,2297,2298],{"class":1440},"\\n",[190,2300,773],{"class":359},[190,2302,132],{"class":228},[190,2304,2305],{"class":359},"\"2",[190,2307,2298],{"class":1440},[190,2309,773],{"class":359},[190,2311,1062],{"class":228},[190,2313,2314],{"class":192,"line":263},[190,2315,344],{"emptyLinePlaceholder":343},[190,2317,2318,2320,2322,2324,2326,2328,2330],{"class":192,"line":269},[190,2319,373],{"class":196},[190,2321,832],{"class":213},[190,2323,384],{"class":196},[190,2325,837],{"class":200},[190,2327,1912],{"class":228},[190,2329,105],{"class":200},[190,2331,2332],{"class":228},".fake())\n",[190,2334,2335],{"class":192,"line":287},[190,2336,2337],{"class":845},"\u002F\u002F the body of the result: TestResponse.Body.Streamed(List(\"1\\n\", \"2\\n\"))\n",[99,2339,2340],{},"ZIO:",[181,2342,2344],{"className":183,"code":2343,"language":185,"meta":186,"style":186},"val handler: Request => ZIO[HandlerEnv, Throwable, Sent] = _ =>\n  Response.header(\"content-type\", \"application\u002Fx-ndjson\") *> Response.sendStream(ZStream(\"1\\n\", \"2\\n\"))\n\nfor response \u003C- ZHandler.test(handler)(Request.fake())\nyield assertTrue(response.body == TestResponse.Body.Streamed(List(\"1\\n\", \"2\\n\")))\n",[103,2345,2346,2381,2423,2427,2443],{"__ignoreMap":186},[190,2347,2348,2350,2352,2354,2356,2358,2360,2362,2364,2366,2368,2370,2373,2375,2377,2379],{"class":192,"line":193},[190,2349,373],{"class":196},[190,2351,693],{"class":213},[190,2353,179],{"class":196},[190,2355,219],{"class":200},[190,2357,222],{"class":196},[190,2359,225],{"class":200},[190,2361,229],{"class":228},[190,2363,232],{"class":200},[190,2365,132],{"class":228},[190,2367,243],{"class":200},[190,2369,132],{"class":228},[190,2371,2372],{"class":200},"Sent",[190,2374,251],{"class":228},[190,2376,254],{"class":196},[190,2378,1832],{"class":228},[190,2380,260],{"class":196},[190,2382,2383,2385,2387,2389,2391,2393,2395,2398,2400,2402,2405,2407,2409,2411,2413,2415,2417,2419,2421],{"class":192,"line":207},[190,2384,2263],{"class":200},[190,2386,2266],{"class":228},[190,2388,460],{"class":359},[190,2390,132],{"class":228},[190,2392,2273],{"class":359},[190,2394,1565],{"class":228},[190,2396,2397],{"class":196},"*>",[190,2399,314],{"class":200},[190,2401,2282],{"class":228},[190,2403,2404],{"class":200},"ZStream",[190,2406,356],{"class":228},[190,2408,2295],{"class":359},[190,2410,2298],{"class":1440},[190,2412,773],{"class":359},[190,2414,132],{"class":228},[190,2416,2305],{"class":359},[190,2418,2298],{"class":1440},[190,2420,773],{"class":359},[190,2422,637],{"class":228},[190,2424,2425],{"class":192,"line":263},[190,2426,344],{"emptyLinePlaceholder":343},[190,2428,2429,2431,2433,2435,2437,2439,2441],{"class":192,"line":269},[190,2430,1297],{"class":196},[190,2432,1300],{"class":228},[190,2434,275],{"class":196},[190,2436,1305],{"class":200},[190,2438,1912],{"class":228},[190,2440,105],{"class":200},[190,2442,2332],{"class":228},[190,2444,2445,2447,2450,2452,2454,2456,2458,2460,2462,2464,2467,2469,2471,2473,2475,2477,2479,2481,2483],{"class":192,"line":287},[190,2446,1312],{"class":196},[190,2448,2449],{"class":228}," assertTrue(response.body ",[190,2451,1336],{"class":196},[190,2453,1339],{"class":200},[190,2455,156],{"class":228},[190,2457,942],{"class":200},[190,2459,156],{"class":228},[190,2461,2185],{"class":200},[190,2463,356],{"class":228},[190,2465,2466],{"class":200},"List",[190,2468,356],{"class":228},[190,2470,2295],{"class":359},[190,2472,2298],{"class":1440},[190,2474,773],{"class":359},[190,2476,132],{"class":228},[190,2478,2305],{"class":359},[190,2480,2298],{"class":1440},[190,2482,773],{"class":359},[190,2484,960],{"class":228},[99,2486,2487,2488,2491],{},"A stream that fails makes the test program fail with the stream's error, as ",[103,2489,2490],{},"sendStream"," does in a real request.",[2493,2494,2496],"h3",{"id":2495},"test-a-handler-that-sends-events","Test a handler that sends events",[99,2498,2499,2163,2502,2504,2505,2508,2509,2512,2513,2515,2516,2519],{},[103,2500,2501],{},"Response.sendEvents",[103,2503,2490],{}," over frames, so the body is ",[103,2506,2507],{},"TestResponse.Body.Streamed"," with one frame per event, and the head has the event-stream headers that ",[103,2510,2511],{},"sendEvents"," sets. In these snippets, ",[103,2514,952],{}," is a model with a ",[103,2517,2518],{},"message"," field. Kyo:",[181,2521,2523],{"className":183,"code":2522,"language":185,"meta":186,"style":186},"val handler: Request => Sent \u003C HandlerEffects = _ =>\n  Response.sendEvents(Stream.init(Seq(ServerEvent.json(Greeting(\"hi\"), event = Some(\"greeting\")), ServerEvent(\"bye\"))))\n\nval program = KHandler.test(handler)(Request.fake())\n\u002F\u002F the body of the result: TestResponse.Body.Streamed(List(\"event: greeting\\ndata: {\\\"message\\\":\\\"hi\\\"}\\n\\n\", \"data: bye\\n\\n\"))\n",[103,2524,2525,2549,2601,2605,2621],{"__ignoreMap":186},[190,2526,2527,2529,2531,2533,2535,2537,2539,2541,2543,2545,2547],{"class":192,"line":193},[190,2528,373],{"class":196},[190,2530,693],{"class":213},[190,2532,179],{"class":196},[190,2534,219],{"class":200},[190,2536,222],{"class":196},[190,2538,2248],{"class":200},[190,2540,705],{"class":196},[190,2542,708],{"class":200},[190,2544,384],{"class":196},[190,2546,1832],{"class":228},[190,2548,260],{"class":196},[190,2550,2551,2553,2556,2558,2560,2562,2564,2567,2570,2572,2574,2577,2580,2582,2584,2586,2589,2592,2594,2596,2599],{"class":192,"line":207},[190,2552,2263],{"class":200},[190,2554,2555],{"class":228},".sendEvents(",[190,2557,2285],{"class":200},[190,2559,883],{"class":228},[190,2561,2290],{"class":200},[190,2563,356],{"class":228},[190,2565,2566],{"class":200},"ServerEvent",[190,2568,2569],{"class":228},".json(",[190,2571,952],{"class":200},[190,2573,356],{"class":228},[190,2575,2576],{"class":359},"\"hi\"",[190,2578,2579],{"class":228},"), event ",[190,2581,254],{"class":196},[190,2583,548],{"class":200},[190,2585,356],{"class":228},[190,2587,2588],{"class":359},"\"greeting\"",[190,2590,2591],{"class":228},")), ",[190,2593,2566],{"class":200},[190,2595,356],{"class":228},[190,2597,2598],{"class":359},"\"bye\"",[190,2600,1062],{"class":228},[190,2602,2603],{"class":192,"line":263},[190,2604,344],{"emptyLinePlaceholder":343},[190,2606,2607,2609,2611,2613,2615,2617,2619],{"class":192,"line":269},[190,2608,373],{"class":196},[190,2610,832],{"class":213},[190,2612,384],{"class":196},[190,2614,837],{"class":200},[190,2616,1912],{"class":228},[190,2618,105],{"class":200},[190,2620,2332],{"class":228},[190,2622,2623],{"class":192,"line":287},[190,2624,2625],{"class":845},"\u002F\u002F the body of the result: TestResponse.Body.Streamed(List(\"event: greeting\\ndata: {\\\"message\\\":\\\"hi\\\"}\\n\\n\", \"data: bye\\n\\n\"))\n",[99,2627,2340],{},[181,2629,2631],{"className":183,"code":2630,"language":185,"meta":186,"style":186},"val handler: Request => ZIO[HandlerEnv, Throwable, Sent] = _ =>\n  Response.sendEvents(ZStream(ServerEvent.json(Greeting(\"hi\"), event = Some(\"greeting\")), ServerEvent(\"bye\")))\n\nfor response \u003C- ZHandler.test(handler)(Request.fake())\nyield assertTrue(\n  response.body == TestResponse.Body.Streamed(List(\"event: greeting\\ndata: {\\\"message\\\":\\\"hi\\\"}\\n\\n\", \"data: bye\\n\\n\")),\n  response.header(\"content-type\").contains(\"text\u002Fevent-stream\"),\n  response.header(\"cache-control\").contains(\"private, no-cache, no-store, no-transform, must-revalidate, max-age=0\"),\n  response.header(\"x-accel-buffering\").contains(\"no\"),\n  response.header(\"connection\").contains(\"keep-alive\"),\n)\n",[103,2632,2633,2667,2707,2711,2727,2733,2798,2813,2827,2841,2855],{"__ignoreMap":186},[190,2634,2635,2637,2639,2641,2643,2645,2647,2649,2651,2653,2655,2657,2659,2661,2663,2665],{"class":192,"line":193},[190,2636,373],{"class":196},[190,2638,693],{"class":213},[190,2640,179],{"class":196},[190,2642,219],{"class":200},[190,2644,222],{"class":196},[190,2646,225],{"class":200},[190,2648,229],{"class":228},[190,2650,232],{"class":200},[190,2652,132],{"class":228},[190,2654,243],{"class":200},[190,2656,132],{"class":228},[190,2658,2372],{"class":200},[190,2660,251],{"class":228},[190,2662,254],{"class":196},[190,2664,1832],{"class":228},[190,2666,260],{"class":196},[190,2668,2669,2671,2673,2675,2677,2679,2681,2683,2685,2687,2689,2691,2693,2695,2697,2699,2701,2703,2705],{"class":192,"line":207},[190,2670,2263],{"class":200},[190,2672,2555],{"class":228},[190,2674,2404],{"class":200},[190,2676,356],{"class":228},[190,2678,2566],{"class":200},[190,2680,2569],{"class":228},[190,2682,952],{"class":200},[190,2684,356],{"class":228},[190,2686,2576],{"class":359},[190,2688,2579],{"class":228},[190,2690,254],{"class":196},[190,2692,548],{"class":200},[190,2694,356],{"class":228},[190,2696,2588],{"class":359},[190,2698,2591],{"class":228},[190,2700,2566],{"class":200},[190,2702,356],{"class":228},[190,2704,2598],{"class":359},[190,2706,960],{"class":228},[190,2708,2709],{"class":192,"line":263},[190,2710,344],{"emptyLinePlaceholder":343},[190,2712,2713,2715,2717,2719,2721,2723,2725],{"class":192,"line":269},[190,2714,1297],{"class":196},[190,2716,1300],{"class":228},[190,2718,275],{"class":196},[190,2720,1305],{"class":200},[190,2722,1912],{"class":228},[190,2724,105],{"class":200},[190,2726,2332],{"class":228},[190,2728,2729,2731],{"class":192,"line":287},[190,2730,1312],{"class":196},[190,2732,1315],{"class":228},[190,2734,2735,2737,2739,2741,2743,2745,2747,2749,2751,2753,2755,2758,2760,2763,2766,2768,2770,2772,2774,2777,2779,2782,2785,2787,2789,2792,2794,2796],{"class":192,"line":306},[190,2736,1333],{"class":228},[190,2738,1336],{"class":196},[190,2740,1339],{"class":200},[190,2742,156],{"class":228},[190,2744,942],{"class":200},[190,2746,156],{"class":228},[190,2748,2185],{"class":200},[190,2750,356],{"class":228},[190,2752,2466],{"class":200},[190,2754,356],{"class":228},[190,2756,2757],{"class":359},"\"event: greeting",[190,2759,2298],{"class":1440},[190,2761,2762],{"class":359},"data: {",[190,2764,2765],{"class":1440},"\\\"",[190,2767,2518],{"class":359},[190,2769,2765],{"class":1440},[190,2771,179],{"class":359},[190,2773,2765],{"class":1440},[190,2775,2776],{"class":359},"hi",[190,2778,2765],{"class":1440},[190,2780,2781],{"class":359},"}",[190,2783,2784],{"class":1440},"\\n\\n",[190,2786,773],{"class":359},[190,2788,132],{"class":228},[190,2790,2791],{"class":359},"\"data: bye",[190,2793,2784],{"class":1440},[190,2795,773],{"class":359},[190,2797,496],{"class":228},[190,2799,2800,2803,2805,2808,2811],{"class":192,"line":331},[190,2801,2802],{"class":228},"  response.header(",[190,2804,460],{"class":359},[190,2806,2807],{"class":228},").contains(",[190,2809,2810],{"class":359},"\"text\u002Fevent-stream\"",[190,2812,469],{"class":228},[190,2814,2815,2817,2820,2822,2825],{"class":192,"line":340},[190,2816,2802],{"class":228},[190,2818,2819],{"class":359},"\"cache-control\"",[190,2821,2807],{"class":228},[190,2823,2824],{"class":359},"\"private, no-cache, no-store, no-transform, must-revalidate, max-age=0\"",[190,2826,469],{"class":228},[190,2828,2829,2831,2834,2836,2839],{"class":192,"line":347},[190,2830,2802],{"class":228},[190,2832,2833],{"class":359},"\"x-accel-buffering\"",[190,2835,2807],{"class":228},[190,2837,2838],{"class":359},"\"no\"",[190,2840,469],{"class":228},[190,2842,2843,2845,2848,2850,2853],{"class":192,"line":370},[190,2844,2802],{"class":228},[190,2846,2847],{"class":359},"\"connection\"",[190,2849,2807],{"class":228},[190,2851,2852],{"class":359},"\"keep-alive\"",[190,2854,469],{"class":228},[190,2856,2857],{"class":192,"line":853},[190,2858,328],{"class":228},[99,2860,2861,2862,2864,2865,132,2868,110,2870,1509,2872,2875,2876,132,2878,2880],{},"To test the caller side, give ",[103,2863,141],{}," a response with the content type ",[103,2866,2867],{},"text\u002Fevent-stream",[103,2869,1779],{},[103,2871,2185],{},[103,2873,2874],{},"$fetch.events"," decodes its text. The test \"$fetch.events decodes a fake event stream, Plain or Streamed, by its content type and sends accept: text\u002Fevent-stream\" (",[103,2877,681],{},[103,2879,1155],{},") does this.",[158,2882,2884,2885],{"id":2883},"read-a-testresponse","Read a ",[103,2886,137],{},[2888,2889,2890,2902],"table",{},[2891,2892,2893],"thead",{},[2894,2895,2896,2900],"tr",{},[2897,2898,2899],"th",{},"Member",[2897,2901,947],{},[2903,2904,2905,2923,2939,2953,2963,2973],"tbody",{},[2894,2906,2907,2913],{},[2908,2909,2910],"td",{},[103,2911,2912],{},"head",[2908,2914,2915,2916,2919,2920,2922],{},"The ",[103,2917,2918],{},"ResponseHead"," that your program set; after ",[103,2921,2490],{},", the one at the call",[2894,2924,2925,2930],{},[2908,2926,2927],{},[103,2928,2929],{},"status",[2908,2931,2932,2195,2935,2938],{},[103,2933,2934],{},"head.status",[103,2936,2937],{},"None"," when your program set none",[2894,2940,2941,2946],{},[2908,2942,2943],{},[103,2944,2945],{},"header(name)",[2908,2947,2948,2949,2952],{},"The header's values after your sets and appends, joined with ",[103,2950,2951],{},"\", \"","; names ignore case",[2894,2954,2955,2960],{},[2908,2956,2957],{},[103,2958,2959],{},"headerValues(name)",[2908,2961,2962],{},"The same values as a list",[2894,2964,2965,2970],{},[2908,2966,2967],{},[103,2968,2969],{},"cookies",[2908,2971,2972],{},"The cookie entries that your program set",[2894,2974,2975,2980],{},[2908,2976,2977],{},[103,2978,2979],{},"body",[2908,2981,2982,2985,2986],{},[103,2983,2984],{},"TestResponse.Body.Value(value)",", or ",[103,2987,2224],{},[99,2989,2990,2992,2993,2995,2996,2998,2999,3002],{},[103,2991,2912],{}," is what your program set. For a handler that sends a stream, it is the head at the ",[103,2994,2490],{}," call, as on the wire: a change after the call does not reach the client, so it is not in ",[103,2997,2912],{}," either. Handler defaults (",[103,3000,3001],{},"HandlerOptions.defaults","), the default content type and the envelope are added by h34s when it writes a real response, so they are not in it. Check them end to end, as described below.",[158,3004,3006],{"id":3005},"test-the-wire-contract","Test the wire contract",[99,3008,3009],{},"Your clients depend on the JSON shapes, the statuses and the error entries. Pin them in tests:",[3011,3012,3013,3043,3053,3074],"ul",{},[3014,3015,3016,3019,3020,3023,3024,3027,3028,3031,3032,3038,3039,156],"li",{},[118,3017,3018],{},"Model shapes."," Convert a value and compare the JSON text with what your clients expect: ",[103,3021,3022],{},"note.toJson"," (from ",[103,3025,3026],{},"NativeConverter","). For a converter that describes its TypeScript type, also check ",[103,3029,3030],{},"converter.wireType.describes(js.JSON.parse(note.toJson))","; see ",[2205,3033,3035,3036],{"href":3034},"\u002Fguide\u002Froutes#where-the-types-come-from","routes and typed ",[103,3037,674],{},". A schema type gives the shapes listed in ",[2205,3040,3042],{"href":3041},"\u002Fguide\u002Fschema#kyo-wire-shapes","schemas",[3014,3044,3045,3048,3049,3052],{},[118,3046,3047],{},"Error entries."," Assert the exact ",[103,3050,3051],{},"InvalidInput"," entries, as above. They are your 422 contract.",[3014,3054,3055,3058,3059,132,3061,138,3064,3066,3067,3069,3070,3073],{},[118,3056,3057],{},"Statuses and headers."," Assert ",[103,3060,2929],{},[103,3062,3063],{},"header",[103,3065,2969],{}," of the ",[103,3068,137],{},". Remember that on failure the core keeps only ",[103,3071,3072],{},"OnFailure.Keep"," entries.",[3014,3075,3076,121,3079,132,3082,3085],{},[118,3077,3078],{},"The envelope.",[103,3080,3081],{},"{\"data\": ...}",[103,3083,3084],{},"{\"errors\": [...]}"," and 204 come from the core and are covered by h34s's own tests. Check them end to end with an HTTP request against a running server, for example in a smoke script.",[158,3087,3089],{"id":3088},"test-configuration-decoding","Test configuration decoding",[99,3091,3092,3093,3096,3097,179],{},"Decode JSON text with your config's converter, without Nitro and without ",[103,3094,3095],{},"scala.scalajs.js",". The demo's ",[103,3098,3099],{},"AppConfigSpec",[181,3101,3103],{"className":183,"code":3102,"language":185,"meta":186,"style":186},"val runtimeConfig =\n  \"{\\\"database\\\":{\\\"host\\\":\\\"127.0.0.1\\\",\\\"port\\\":5432,\\\"user\\\":\\\"h34s\\\",\\\"password\\\":\\\"hunter2\\\",\\\"name\\\":\\\"h34s\\\"}}\"\nval config = summon[NativeConverter[AppConfig]].fromJson(runtimeConfig)\nassertTrue(config.database.password.reveal == \"hunter2\", !config.toString.contains(\"hunter2\"))\n",[103,3104,3105,3115,3212,3234],{"__ignoreMap":186},[190,3106,3107,3109,3112],{"class":192,"line":193},[190,3108,373],{"class":196},[190,3110,3111],{"class":213}," runtimeConfig",[190,3113,3114],{"class":196}," =\n",[190,3116,3117,3120,3122,3125,3127,3130,3132,3135,3137,3139,3141,3144,3146,3149,3151,3154,3156,3159,3161,3164,3166,3168,3170,3173,3175,3177,3179,3182,3184,3186,3188,3191,3193,3195,3197,3199,3201,3203,3205,3207,3209],{"class":192,"line":207},[190,3118,3119],{"class":359},"  \"{",[190,3121,2765],{"class":1440},[190,3123,3124],{"class":359},"database",[190,3126,2765],{"class":1440},[190,3128,3129],{"class":359},":{",[190,3131,2765],{"class":1440},[190,3133,3134],{"class":359},"host",[190,3136,2765],{"class":1440},[190,3138,179],{"class":359},[190,3140,2765],{"class":1440},[190,3142,3143],{"class":359},"127.0.0.1",[190,3145,2765],{"class":1440},[190,3147,3148],{"class":359},",",[190,3150,2765],{"class":1440},[190,3152,3153],{"class":359},"port",[190,3155,2765],{"class":1440},[190,3157,3158],{"class":359},":5432,",[190,3160,2765],{"class":1440},[190,3162,3163],{"class":359},"user",[190,3165,2765],{"class":1440},[190,3167,179],{"class":359},[190,3169,2765],{"class":1440},[190,3171,3172],{"class":359},"h34s",[190,3174,2765],{"class":1440},[190,3176,3148],{"class":359},[190,3178,2765],{"class":1440},[190,3180,3181],{"class":359},"password",[190,3183,2765],{"class":1440},[190,3185,179],{"class":359},[190,3187,2765],{"class":1440},[190,3189,3190],{"class":359},"hunter2",[190,3192,2765],{"class":1440},[190,3194,3148],{"class":359},[190,3196,2765],{"class":1440},[190,3198,1506],{"class":359},[190,3200,2765],{"class":1440},[190,3202,179],{"class":359},[190,3204,2765],{"class":1440},[190,3206,3172],{"class":359},[190,3208,2765],{"class":1440},[190,3210,3211],{"class":359},"}}\"\n",[190,3213,3214,3216,3219,3221,3224,3226,3228,3231],{"class":192,"line":263},[190,3215,373],{"class":196},[190,3217,3218],{"class":213}," config",[190,3220,384],{"class":196},[190,3222,3223],{"class":228}," summon[",[190,3225,3026],{"class":200},[190,3227,229],{"class":228},[190,3229,3230],{"class":200},"AppConfig",[190,3232,3233],{"class":228},"]].fromJson(runtimeConfig)\n",[190,3235,3236,3239,3241,3244,3246,3249,3252,3255],{"class":192,"line":269},[190,3237,3238],{"class":228},"assertTrue(config.database.password.reveal ",[190,3240,1336],{"class":196},[190,3242,3243],{"class":359}," \"hunter2\"",[190,3245,132],{"class":228},[190,3247,3248],{"class":196},"!",[190,3250,3251],{"class":228},"config.toString.contains(",[190,3253,3254],{"class":359},"\"hunter2\"",[190,3256,637],{"class":228},[158,3258,3260],{"id":3259},"test-shared-resources","Test shared resources",[99,3262,3263,3264,138,3267,3270,3271,3274],{},"Without the Nitro plugin, ",[103,3265,3266],{},"Shared",[103,3268,3269],{},"ZShared"," register nothing. Call ",[103,3272,3273],{},"close"," at the end of the test to release the resource.",[158,3276,3278],{"id":3277},"what-h34s-tests-for-you","What h34s tests for you",[99,3280,3281,3282,3284,3285,3288,3289,3292,3293,3296,3297,3300,3301,3304,3305,3308,3309,3312,3313,3316,3317,3319,3320,3322,3323,3325,3326,3328,3329,3331,3332,3334,3335,3308,3337,3339,3340,3342,3343,3345,3346,3348,3349,3351,3352,3354,3355,3357,3358,3360,3361,3308,3363,3339,3365,3367,3368,3370,3371,3373,3374,3376,3377,3379,3380,3383],{},"The h34s suites pin the behavior your handlers rely on. For ",[103,3283,128],{},", on macOS x86_64 with Node 24.19.0: ",[103,3286,3287],{},"h3Ext"," 36 tests, ",[103,3290,3291],{},"kyoH3Ext"," 79, ",[103,3294,3295],{},"zioH3Ext"," 81, ",[103,3298,3299],{},"nativeConverterKyoSchemaExt"," 25, ",[103,3302,3303],{},"nativeConverterZIOSchemaExt"," 20, ",[103,3306,3307],{},"kyoNuxtHubExt"," 15, ",[103,3310,3311],{},"zioNuxtHubExt"," 14, the demo's ",[103,3314,3315],{},"nuxtsvr"," 6, and the Node tests of the npm package. After the ",[103,3318,2490],{}," change, on a Linux x86_64 cloud VM with Node 24.22.0: ",[103,3321,3287],{}," 49, ",[103,3324,3291],{}," 122, ",[103,3327,3295],{}," 104, ",[103,3330,3299],{}," 29, ",[103,3333,3303],{}," 22, ",[103,3336,3307],{},[103,3338,3311],{}," 14 and ",[103,3341,3315],{}," 7. After the server-side ",[103,3344,674],{}," of both runtimes, on the same kind of VM: ",[103,3347,3287],{}," 55, ",[103,3350,3291],{}," 131, ",[103,3353,3295],{}," 119, ",[103,3356,3299],{}," 30, ",[103,3359,3303],{}," 24, ",[103,3362,3307],{},[103,3364,3311],{},[103,3366,3315],{}," 7. They cover request snapshots and body limits, the wire format and envelopes, the failure rule, safe and opaque errors, diagnostics, request finalizers, client disconnects and uninterruptible steps, streaming, the server-side ",[103,3369,674],{},", the test helpers, ",[103,3372,3266],{},"\u002F",[103,3375,3269],{},", runtime config, storage and the schema bridges, through real h3 1.15.4 events with mocked Node responses, and (for streams and ",[103,3378,674],{},") a copy of Nitro 2.13.1's in-process local fetch. See ",[2205,3381,3382],{"href":87},"compatibility"," for the evidence.",[3385,3386,3387],"style",{},"html pre.shiki code .s8jYJ, html code.shiki .s8jYJ{--shiki-light:#D73A49;--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .snPdu, html code.shiki .snPdu{--shiki-light:#6F42C1;--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sP4rz, html code.shiki .sP4rz{--shiki-light:#E36209;--shiki-default:#E36209;--shiki-dark:#FFAB70}html pre.shiki code .sxrX7, html code.shiki .sxrX7{--shiki-light:#24292E;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sIIMD, html code.shiki .sIIMD{--shiki-light:#032F62;--shiki-default:#032F62;--shiki-dark:#9ECBFF}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sCsY4, html code.shiki .sCsY4{--shiki-light:#6A737D;--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .sBjJW, html code.shiki .sBjJW{--shiki-light:#005CC5;--shiki-default:#005CC5;--shiki-dark:#79B8FF}",{"title":186,"searchDepth":207,"depth":207,"links":3389},[3390,3391,3392,3394,3396,3398,3401,3403,3404,3405,3406],{"id":160,"depth":207,"text":161},{"id":402,"depth":207,"text":403},{"id":654,"depth":207,"text":3393},"Kyo: KHandler.test",{"id":1127,"depth":207,"text":3395},"ZIO: ZHandler.test",{"id":1706,"depth":207,"text":3397},"Answer $fetch calls",{"id":2214,"depth":207,"text":2215,"children":3399},[3400],{"id":2495,"depth":263,"text":2496},{"id":2883,"depth":207,"text":3402},"Read a TestResponse",{"id":3005,"depth":207,"text":3006},{"id":3088,"depth":207,"text":3089},{"id":3259,"depth":207,"text":3260},{"id":3277,"depth":207,"text":3278},"Test handler functions with Request.fake and KHandler.test or ZHandler.test, without h3, Nitro or a server.","md",null,{},{"title":51,"description":3407},"Fj2DAwkHOHZDB5d9UG9D35oH9iep3sWlNlK7yFgLR0k",[3414,3416],{"title":47,"path":48,"stem":49,"description":3415,"children":-1},"Send a Kyo Stream or a ZStream with Response.sendStream; h34s writes the head at once, keeps the request scope open and waits for cleanup when the client goes away.",{"title":55,"path":56,"stem":57,"description":3417,"children":-1},"Open a Nitro storage mount from Scala with KHubKv.mount or ZHubKv.mount, with no native objects.",1792533383165]