Pudu programming language
Menu
Package

@chrismichaelps / pudu-lang-log

Structured event logging for Pudu: message templates, enrichment, filtering, formatting, and sinks

0.1.0Apache-2.01

InstallClose

MaskingTest.pudu

Pudu54 lines3.9 KB

GitHub ↗
1/** @Test.Enrichers.Masking.Suite — sensitive values hidden before sinks */2module PuduLangLog.Enrichers.MaskingTest34import Std.Io as Io5import Std.List as List6import Std.Option as Option7import Std.Result as Result8import Std.Test as Test9import PuduLangLog.Configuration as Configuration10import PuduLangLog.Enricher as Enricher11import PuduLangLog.Enrichers.Masking as Masking12import PuduLangLog.Failure as Failure13import PuduLangLog as Log14import PuduLangLog.Logger as Logger15import PuduLangLog.Sinks.Memory as Memory16import PuduLangLog.Value as Value1718/// The latest message and failure a configured logger wrote after logging one event.19fn written(configure: fn(Configuration.Configuration) -> Configuration.Configuration, template: Str, arguments: Array[Log.Value], failure: Option[Log.Failure]) -> (Str, Option[Log.Failure]) {20  let memory = Memory.create()21  let logger = configure(Configuration.create().writeTo(Memory.sink(&memory))).createLogger()22  let _written = logger.tryWrite(Log.Information, failure, template, arguments)23  let latest = Memory.latest(&memory)24  (Option.unwrapOr(List.first(&Memory.messages(&memory)), ""), Option.andThen(latest, |event: Log.Event| event.failure))25}2627/// Runs the suite.28fn main() -> Int {29  let byName = fn(configuration: Configuration.Configuration) -> Configuration.Configuration { configuration.enrichWith(Masking.properties(["Password", "token"], Masking.MASK)) }30  let sensitive = Result.unwrapOr(Masking.sensitive("[hidden]"), Enricher.all([]))31  let byPattern = fn(configuration: Configuration.Configuration) -> Configuration.Configuration { configuration.enrichWith(sensitive) }32  let refused = Masking.patterns(["(open"], "*")33  let ignoring = fn(configuration: Configuration.Configuration) -> Configuration.Configuration { configuration.destructureByIgnoring("User", ["Password"]) }34  let masking = fn(configuration: Configuration.Configuration) -> Configuration.Configuration { configuration.destructureByMasking("User", ["password"], "#") }35  let user = Value.structure("User", [("Name", Value.text("Ada")), ("Password", Value.text("s3cret"))])36  let listed = written(byName, "Login \{User\} with \{Password\} and \{@Session\}", [Value.text("ada"), Value.text("s3cret"), Value.object([("Token", Value.text("abc")), ("Id", Value.int(4))])], None)37  let contact = written(byPattern, "Mail \{To\} card \{Card\} iban \{Iban\} order \{Order\}", [Value.text("ada@example.org"), Value.text("4111 1111 1111 1111"), Value.text("GB82WEST12345698765432"), Value.int(1234567890123)], Some(Failure.causedBy(&Failure.of("Mail", "bounced for bob@example.com"), Failure.of("Smtp", "to eve@example.net"))))38  let checks = Test.suite("Enrichers.Masking", &[39      Test.equals("listed properties and members are hidden", &listed[0], &"Login \"ada\" with \"***\" and \{ Token: \"***\", Id: 4 \}"),40      Test.equals("patterns hide addresses, card numbers, and account numbers", &contact[0], &"Mail \"[hidden]\" card \"[hidden]\" iban \"[hidden]\" order 1234567890123"),41      Test.equals("patterns hide text in failures and their causes", &contact[1], &Some(Failure.causedBy(&Failure.of("Mail", "bounced for [hidden]"), Failure.of("Smtp", "to [hidden]")))),42      Test.equals("text without matches is kept", &written(byPattern, "\{Note\}", [Value.text("call at 12:30")], None)[0], &"\"call at 12:30\""),43      Test.that("a pattern that does not compile is refused", Result.isErr(&refused)),44      Test.equals("destructuring can leave members out", &written(ignoring, "\{@User\}", [user], None)[0], &"User \{ Name: \"Ada\" \}"),45      Test.equals("destructuring can mask members", &written(masking, "\{@User\}", [user], None)[0], &"User \{ Name: \"Ada\", Password: \"#\" \}"),46      Test.equals("rules apply only to their tag", &written(ignoring, "\{@Other\}", [Value.structure("Other", [("Password", Value.int(1))])], None)[0], &"Other \{ Password: 1 \}")47    ])48  let ran = Test.run(&checks)49  for failure in Test.failuresOf(&ran) {50    let _reported = Io.writeErrorLine(failure)51  }52  Test.report(&ran)53}54