Pudu programming language
Menu
Package

@chrismichaelps / pudu-lang-httpclient

Named HTTP clients for Pudu: a client factory, delegating handlers, pooled keep-alive connections, handler lifetimes, logging, and resilience

0.1.1Apache-2.01

InstallClose

BuilderTest.pudu

Pudu59 lines4.0 KB

GitHub ↗
1/** @Test.Factory.Builder.Suite — how one named client and its pipeline are configured */2module PuduLangHttpClient.Factory.BuilderTest34import Std.Http as Http5import Std.Io as Io6import Std.Test as Test7import PuduLangHttpClient.Client as Client8import PuduLangHttpClient.Constants.Defaults as Defaults9import PuduLangHttpClient.Domain.Version as Version10import PuduLangHttpClient.Factory.Builder as Builder11import PuduLangHttpClient.Handler as Handler12import PuduLangHttpClient.Stub as Stub13import PuduLangHttpClient.Transport as Transport1415/// An observer whose handlers pass every request on.16fn quiet() -> Builder.Observer { Builder.Observer{outer: |_seen: Builder.Observation| Handler.passThrough(), inner: |_seen: Builder.Observation| Handler.passThrough()} }1718/// The client a builder's actions make from a stub.19fn applied(builder: &Builder.Builder) -> Client.Client {20  var client = Client.create(Stub.send(&Stub.create()))21  for action in builder.clientActions { client = action(client) }22  client23}2425/// Runs the suite.26fn main() -> Int {27  let shared = Builder.defaults().withTimeout(5000).withHandler(Handler.passThrough()).observedBy(quiet()).redactingHeaders(&["X-Api-Key"]).withHandlerLifetime(1000)28  let own = Builder.named("api").withBaseAddress("https://api.test/").withDefaultHeader("accept", "*/*").withMaxResponseContentBufferSize(10).withDefaultVersion(Http.Http10, Version.Exact).withHandlerFactory(|| Handler.passThrough()).withTransport(Transport.defaults()).configureTransport(|options: Transport.Options| options.withDecompression())29  let combined = Builder.combine(&shared, &own)30  let cleared = Builder.combine(&shared, &Builder.named("bare").withoutObservers().observedBy(quiet()).withPrimary(|| Stub.send(&Stub.create())).redactingWhen(|name: Str| name.startsWith("x-secret")))31  let client = applied(&combined)32  let redact = Builder.redaction(&combined)33  let custom = Builder.redaction(&cleared)34  let checks = Test.suite("Factory.Builder", &[35      Test.equals("a named builder applies to its name only", &(own.name, own.everyClient, Builder.defaults().everyClient), &("api", false, true)),36      Test.equals("client settings run defaults first, then the name's", &(client.timeout, client.baseAddress, client.defaultHeaders, client.maxResponseContentBufferSize, client.defaultVersion, client.defaultVersionPolicy), &(5000, Some("https://api.test/"), [("accept", "*/*")], 10, Http.Http10, Version.Exact)),37      Test.equals("handlers and edits are joined in order", &(combined.handlers.length(), combined.transportEdits.length()), &(2, 1)),38      Test.that("a later primary handler replaces an earlier one", match cleared.primary {39          case Some(Builder.Custom(_)) => true40          case _ => false41        }),42      Test.that("an earlier primary handler is kept when a later builder names none", match Builder.combine(&own, &Builder.named("api")).primary {43          case Some(Builder.Pooled(_)) => true44          case _ => false45        }),46      Test.equals("the later lifetime wins and the default applies without one", &(Builder.lifetimeOf(&combined), Builder.lifetimeOf(&Builder.combine(&shared, &Builder.named("x").withHandlerLifetime(Defaults.INFINITE))), Builder.lifetimeOf(&Builder.named("y"))), &(1000, Defaults.INFINITE, Defaults.HANDLER_LIFETIME)),47      Test.equals("observers are joined unless a later builder clears them", &(combined.observers.length(), cleared.observers.length()), &(1, 1)),48      Test.that("sensitive and named headers are redacted without regard to case", redact("Authorization") && redact("cookie") && redact("set-cookie") && redact("proxy-authorization") && redact("x-api-key")),49      Test.not("other headers are not", redact("accept") || custom("x-api")),50      Test.that("a predicate redacts what it holds for", custom("X-Secret-Token") && custom("x-api-key")),51      Test.equals("a builder without a predicate has none", &(combined.redactWhen == None, cleared.redactWhen != None), &(true, true))52    ])53  let ran = Test.run(&checks)54  for failure in Test.failuresOf(&ran) {55    let _reported = Io.writeErrorLine(failure)56  }57  Test.report(&ran)58}59