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

TemplateTest.pudu

Pudu63 lines3.8 KB

GitHub ↗
1/** @Test.Expressions.Template.Suite — expression templates and their directives */2module PuduLangLog.Expressions.TemplateTest34import Std.Io as Io5import Std.Test as Test6import PuduLangLog.Event as Event7import PuduLangLog.Expressions as Expressions8import PuduLangLog.Failure as Failure9import PuduLangLog as Log10import PuduLangLog.Value as Value1112/// The event every template in this suite renders.13fn sample() -> Log.Event {14  Event.withFailure(&Event.create(Log.Timestamp{millis: 1790604309123, offset: 0}, Log.Warning, "Cart \{Cart\} for \{User\}", [15        Value.property("User", Value.text("Ada")),16        Value.property("Cart", Value.list([Value.text("tea"), Value.text("cup")])),17        Value.property("Total", Value.decimal(12.5d)),18        Value.property("Meta", Value.object([("region", Value.text("eu")), ("tier", Value.int(2))])),19        Value.property("Empty", Value.list([]))20      ]), Failure.of("IoError", "full"))21}2223/// The text a template renders for the sample event, or `?` and the reason it was refused.24fn rendered(template: Str) -> Str {25  match Expressions.template(template) {26    case Ok(formatter) => formatter(&sample())27    case Err(reason) => "? " + reason28  }29}3031/// Runs the suite.32fn main() -> Int {33  let checks = Test.suite("Expressions.Template", &[34      Test.equals("literal text and doubled braces", &rendered("plain \{\{x\}\} text"), &"plain \{x\} text"),35      Test.equals("holes with formats", &rendered("[\{@t:HH:mm:ss\} \{@l:u3\}] \{Total:0.00\} \{User\}"), &"[14:05:09 WRN] 12.50 Ada"),36      Test.equals("the message and the template", &rendered("\{@m\} | \{@mt\}"), &"Cart [\"tea\",\"cup\"] for Ada | Cart \{Cart\} for \{User\}"),37      Test.equals("collections render as JSON and undefined holes as nothing", &rendered("\{Meta\}|\{Missing\}|\{@x\}"), &"\{\"region\":\"eu\",\"tier\":2\}||IoError: full"),38      Test.equals("an if block", &rendered("\{#if Total > 10\}big\{#end\}"), &"big"),39      Test.equals("else if and else", &[rendered("\{#if Total > 100\}a\{#else if Total > 10\}b\{#else\}c\{#end\}"), rendered("\{#if Total > 100\}a\{#else if Total > 50\}b\{#else\}c\{#end\}"), rendered("\{#if Missing\}a\{#end\}")], &["b", "c", ""]),40      Test.equals("each over a sequence with a delimiter", &rendered("\{#each item, i in Cart\}\{i\}:\{item\}\{#delimit\}, \{#end\}"), &"0:tea, 1:cup"),41      Test.equals("each over members", &rendered("\{#each name, held in Meta\}\{name\}=\{held\}\{#delimit\};\{#end\}"), &"region=eu;tier=2"),42      Test.equals("each with an else for nothing", &[rendered("\{#each x in Empty\}\{x\}\{#else\}none\{#end\}"), rendered("\{#each x in Missing\}\{x\}\{#else\}none\{#end\}")], &["none", "none"]),43      Test.equals("nested blocks", &rendered("\{#each x in Cart\}\{#if x = 'cup'\}[\{x\}]\{#end\}\{#end\}"), &"[cup]"),44      Test.equals("rest leaves out names the template reads", &rendered("\{User\} \{Rest()\}"), &"Ada \{\"Cart\":[\"tea\",\"cup\"],\"Total\":12.5,\"Meta\":\{\"region\":\"eu\",\"tier\":2\},\"Empty\":[]\}"),45      Test.equals("deep rest also leaves out names the message uses", &rendered("\{Rest(true)\}"), &"\{\"Total\":12.5,\"Meta\":\{\"region\":\"eu\",\"tier\":2\},\"Empty\":[]\}"),46      Test.equals("refusals", &[rendered("\{User"), rendered("\{#if User\}x"), rendered("\{#end\}"), rendered("\{#frob\}"), rendered("\{#each x Cart\}\{#end\}"), rendered("\{#if a\}\{#else\}b\{#delimit\}"), rendered("\{User User\}"), rendered("\{Frob(User)\}")], &[47          "? a hole opened at 0 is not closed",48          "? `\{#if\}` is not closed",49          "? unexpected `\{#end\}`",50          "? unknown directive `\{#frob\}`",51          "? `\{#each\}` needs `in`",52          "? `\{#else\}` must be followed by `\{#end\}`",53          "? unexpected `User` in hole `User User`",54          "? unknown function `frob`"55        ])56    ])57  let ran = Test.run(&checks)58  for failure in Test.failuresOf(&ran) {59    let _reported = Io.writeErrorLine(failure)60  }61  Test.report(&ran)62}63