
LoggingTest.pudu
Pudu62 lines3.9 KB
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 Memory181920fn levels(memory: &Memory.Memory, template: Str) -> Array[Log.Level] {21 Memory.withTemplate(memory, template).map(|event: Log.Event| event.level)22}232425fn 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