
Stub.pudu
Pudu72 lines3.3 KB
1/** @HttpClient.Stub.Module — a primary handler answering from rules, for tests */2module PuduLangHttpClient.Stub34import Std.Bytes as ByteSeq5import Std.Concurrent.Cancel as Cancel6import Std.Http as Http7import PuduLangHttpClient.Content as Content8import PuduLangHttpClient.Domain.Uri as Uri9import PuduLangHttpClient.Handler as Handler10import PuduLangHttpClient as HttpClient11import PuduLangHttpClient.Request as Request12import PuduLangHttpClient.Response as Response13import PuduLangHttpClient.Utils.Shared as Shared1415/** @HttpClient.Stub.Rule — which requests a rule answers, and how */16export type Rule = { matches: fn(Request.Request) -> Bool, answer: fn(Request.Request) -> HttpClient.Outcome[Response.Response] }1718/** @HttpClient.Stub.Stub — the rules, tried in the order added, and every request received */19export type Stub = { rules: Shared.Shared[Array[Rule]], received: Shared.Shared[Array[Request.Request]] }202122export fn create() -> Stub { Stub{rules: Shared.shared([]), received: Shared.shared([])} }232425export fn when(stub: &Stub, matches: fn(Request.Request) -> Bool, answer: fn(Request.Request) -> HttpClient.Outcome[Response.Response]) -> () {26 Shared.update(&stub.rules, |rules: Array[Rule]| rules.push(Rule{matches: matches, answer: answer}))27}282930export fn on(stub: &Stub, method: Http.Method, path: Str, answer: fn(Request.Request) -> HttpClient.Outcome[Response.Response]) -> () {31 when(stub, |request: Request.Request| request.method == method && Uri.split(request.uri).path == path, answer)32}333435export fn respond(stub: &Stub, method: Http.Method, path: Str, code: Int, body: Str) -> () {36 on(stub, method, path, |request: Request.Request| Ok(Response.answer(&request, code).withText(body)))37}383940export fn send(stub: &Stub) -> Handler.Send {41 let held = *stub42 fn(request: Request.Request, token: Cancel.Token) -> HttpClient.Outcome[Response.Response] {43 if let Err(why) = Cancel.check(&token) {44 return match why {45 case Cancel.Requested(reason) => Err(HttpClient.Cancelled(reason))46 case Cancel.DeadlineExceeded => Err(HttpClient.TimedOut(0))47 }48 }49 Shared.update(&held.received, |held: Array[Request.Request]| held.push(request))50 for rule in Shared.current(&held.rules) {51 if (rule.matches)(request) { return delivered((rule.answer)(request)) }52 }53 Ok(Response.answer(&request, 404))54 }55}565758export fn received(stub: &Stub) -> Array[Request.Request] { Shared.current(&stub.received) }59606162fn delivered(outcome: HttpClient.Outcome[Response.Response]) -> HttpClient.Outcome[Response.Response] {63 let response = outcome ?64 match response.request.receiver {65 case Some(take) => {66 let _taken = take(response.content.payload)67 Ok(Response.Response{..response, content: Content.Content{..response.content, payload: ByteSeq.empty()}})68 }69 case None => Ok(response)70 }71}72