
MapTest.pudu
Pudu94 lines4.7 KB
1/** @Test.Sinks.Map.Suite — routing events to one sink per key */2module PuduLangLog.Sinks.MapTest34import Std.Io as Io5import Std.List as List6import Std.Sync as Sync7import Std.Test as Test8import PuduLangLog.Configuration as Configuration9import PuduLangLog as Log10import PuduLangLog.Logger as Logger11import PuduLangLog.Sink as Sink12import PuduLangLog.Sinks.Map as Map13import PuduLangLog.Value as Value141516fn journaled(journal: &Sync.Cell[Array[Str]], key: Str) -> Sink.Sink {17 let shared = *journal18 Sink.Sink {19 emit: fn(event: &Log.Event) -> Result[(), Str] {20 note(&shared, key + ":" + event.template.text)21 if key == "broken" { Err("refused") } else { Ok(()) }22 },23 flush: fn() -> () { note(&shared, key + ":flush") },24 close: fn() -> () { note(&shared, key + ":close") },25 attach: fn(_listener: Sink.Listener) -> () { note(&shared, key + ":attach") }26 }27}282930fn note(journal: &Sync.Cell[Array[Str]], line: Str) -> () {31 let current = match Sync.get(journal) {32 case Ok(lines) => lines33 case Err(_) => []34 }35 let _stored = Sync.set(journal, current.push(line))36}373839fn linesOf(journal: &Sync.Cell[Array[Str]]) -> Array[Str] {40 match Sync.get(journal) {41 case Ok(lines) => lines42 case Err(_) => []43 }44}454647fn main() -> Int {48 let none: Array[Str] = []49 let journal = Sync.cell(none)50 let routed = Map.sink(Map.defaults(Map.byProperty("Tenant", "shared"), fn(key: Str) -> Sink.Sink { journaled(&journal, key) }))51 let logger = Configuration.create().writeTo(routed).createLogger()52 logger.information("\{Tenant\} a", [Value.text("north")])53 logger.information("\{Tenant\} b", [Value.text("south")])54 logger.information("\{Tenant\} c", [Value.text("north")])55 logger.information("no tenant", [])56 logger.information("\{Tenant\} d", [Value.int(7)])57 let routedLines = linesOf(&journal)58 Logger.close(&logger)59 let closedLines = linesOf(&journal)60 let limited = Sync.cell(none)61 let bounded = Map.sink(Map.Options{..Map.defaults(Map.byProperty("Tenant", "shared"), fn(key: Str) -> Sink.Sink { journaled(&limited, key) }), limit: Some(2)})62 let boundedLogger = Configuration.create().writeTo(bounded).createLogger()63 for tenant in ["a", "b", "a", "c", "b"] { boundedLogger.information("\{Tenant\}", [Value.text(tenant)]) }64 let eager = Sync.cell(none)65 let single = Map.sink(Map.Options{..Map.defaults(Map.byProperty("Tenant", "shared"), fn(key: Str) -> Sink.Sink { journaled(&eager, key) }), limit: Some(0)})66 let _one = (single.emit)(&Log.Event{..sampleEvent(), properties: [Value.property("Tenant", Value.text("x"))]})67 let failing = Map.sink(Map.defaults(fn(_event: &Log.Event) -> Str { "broken" }, fn(key: Str) -> Sink.Sink { journaled(&journal, key) }))68 let refused = (failing.emit)(&sampleEvent())69 let after = Sync.cell(none)70 let closing = Map.sink(Map.defaults(fn(_event: &Log.Event) -> Str { "k" }, fn(key: Str) -> Sink.Sink { journaled(&after, key) }))71 (closing.attach)(fn(_report: &Sink.Report) -> () {})72 let _first = (closing.emit)(&sampleEvent())73 (closing.close)()74 let _late = (closing.emit)(&sampleEvent())75 let checks = Test.suite("Sinks.Map", &[76 Test.equals("events reach their key's sink, opened once", &routedLines, &["north:\{Tenant\} a", "south:\{Tenant\} b", "north:\{Tenant\} c", "shared:no tenant", "7:\{Tenant\} d"]),77 Test.equals("closing flushes, then closes every open sink, least recent first", &List.drop(&closedLines, routedLines.length()), &["south:flush", "north:flush", "shared:flush", "7:flush", "south:flush", "south:close", "north:flush", "north:close", "shared:flush", "shared:close", "7:flush", "7:close"]),78 Test.equals("the least recently used sink closes beyond the limit", &linesOf(&limited), &["a:\{Tenant\}", "b:\{Tenant\}", "a:\{Tenant\}", "c:\{Tenant\}", "b:flush", "b:close", "b:\{Tenant\}", "a:flush", "a:close"]),79 Test.equals("a limit of zero closes each sink after its event", &linesOf(&eager), &["x:msg", "x:flush", "x:close"]),80 Test.equals("a failing sink's error reaches the caller", &refused, &Err("refused")),81 Test.equals("listeners reach sinks opened later, and a closed map drops events", &linesOf(&after), &["k:attach", "k:msg", "k:flush", "k:close"])82 ])83 let ran = Test.run(&checks)84 for failure in Test.failuresOf(&ran) {85 let _reported = Io.writeErrorLine(failure)86 }87 Test.report(&ran)88}899091fn sampleEvent() -> Log.Event {92 Log.Event{timestamp: Log.Timestamp{millis: 0, offset: 0}, level: Log.Information, template: Log.Template{text: "msg", tokens: [Log.Literal("msg")], holes: [], binding: Log.Unbound}, properties: [], failure: None, traceId: None, spanId: None}93}94