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

LoggingTest.pudu

Pudu62 lines3.9 KB

GitHub ↗
1/** @Test.Handlers.Logging.Suite — structured events around a pipeline and its primary handler */2module PuduLangHttpClient.Handlers.LoggingTest34import Std.Http as Http5import Std.Io as Io6import Std.Test as Test7import PuduLangHttpClient.Client as Client8import PuduLangHttpClient.Constants.Events as Events9import PuduLangHttpClient.Factory.Builder as Builder10import PuduLangHttpClient.Factory as Factory11import PuduLangHttpClient.Handlers.Logging as Logging12import PuduLangHttpClient as HttpClient13import PuduLangHttpClient.Request as Request14import PuduLangHttpClient.Stub as Stub15import PuduLangLog.Configuration as Configuration16import PuduLangLog as Log17import PuduLangLog.Sinks.Memory as Memory1819/// The levels of the events written with a template.20fn levels(memory: &Memory.Memory, template: Str) -> Array[Log.Level] {21  Memory.withTemplate(memory, template).map(|event: Log.Event| event.level)22}2324/// Runs the suite.25fn main() -> Int {26  let memory = Memory.create()27  let logger = Configuration.create().minimumLevel(Log.Verbose).writeTo(Memory.sink(&memory)).createLogger()28  let stub = Stub.create()29  Stub.respond(&stub, Http.Get, "/ok", 200, "fine")30  Stub.when(&stub, |request: Request.Request| request.uri.endsWith("/down"), |_request: Request.Request| Err(HttpClient.Connection("refused")))31  let factory = match Factory.build([32      Builder.defaults().observedBy(Logging.observer(logger)),33      Builder.named("api").withBaseAddress("http://api.test/").withPrimary(|| Stub.send(&stub)).withDefaultHeader("authorization", "Bearer secret").withDefaultHeader("x-api-key", "k").withDefaultHeader("accept", "*/*").redactingHeaders(&["x-api-key"]),34      Builder.named("silent").withBaseAddress("http://api.test/").withPrimary(|| Stub.send(&stub)).withoutObservers()35    ]) {36    case Ok(found) => found37    case Err(invalid) => panic(Factory.explain(&invalid))38  }39  let client = Factory.createClient(&factory, "api")40  let _ok = Client.get(&client, "ok")41  let _down = Client.get(&client, "down")42  let afterApi = Memory.events(&memory).length()43  let _quiet = Client.get(&Factory.createClient(&factory, "silent"), "ok")44  let messages = Memory.messages(&memory)45  let checks = Test.suite("Handlers.Logging", &[46      Test.equals("a request entering and leaving the pipeline is information", &(levels(&memory, Events.PIPELINE_STARTED), levels(&memory, Events.PIPELINE_ENDED)), &([Log.Information, Log.Information], [Log.Information])),47      Test.equals("a request reaching the primary handler is information", &(levels(&memory, Events.REQUEST_SENDING), levels(&memory, Events.RESPONSE_RECEIVED)), &([Log.Information, Log.Information], [Log.Information])),48      Test.equals("a failure is a warning at both layers", &levels(&memory, Events.REQUEST_FAILED), &[Log.Warning, Log.Warning]),49      Test.equals("headers are listed at the verbose level", &(levels(&memory, Events.REQUEST_HEADERS).length(), levels(&memory, Events.RESPONSE_HEADERS).length()), &(4, 2)),50      Test.that("the start names the method and address", messages[0] == "Start processing HTTP request \"GET\" \"http://api.test/ok\""),51      Test.that("sensitive and named header values are redacted and others kept", messages[1].contains("authorization: *") && messages[1].contains("x-api-key: *") && messages[1].contains("accept: */*") && !messages[1].contains("secret")),52      Test.that("a failure names what went wrong", messages.filter(|line: Str| line.contains("failed after") && line.contains("The connection failed: refused")).length() == 2),53      Test.equals("a client without observers writes nothing", &Memory.events(&memory).length(), &afterApi),54      Test.equals("sources name the client and the layer", &(Logging.sourceOf("api", Events.LOGICAL_HANDLER), Logging.sourceOf("", Events.CLIENT_HANDLER)), &("PuduLangHttpClient.api.LogicalHandler", "PuduLangHttpClient.ClientHandler"))55    ])56  let ran = Test.run(&checks)57  for failure in Test.failuresOf(&ran) {58    let _reported = Io.writeErrorLine(failure)59  }60  Test.report(&ran)61}62