Pudu programming language
Menu
Package

@chrismichaelps / pudu-lang-mediator

In-process messaging for Pudu: requests, notifications, streams, pipeline behaviors, processors, and exception handling

0.1.0Apache-2.01

InstallClose

ResilientTest.pudu

Pudu72 lines3.6 KB

GitHub ↗
1/** @Test.Behaviors.Resilient.Suite — requests retried, timed out, and cancelled by a resilience pipeline */2module PuduLangMediator.Behaviors.ResilientTest34import Std.Io as Io5import Std.Test as Test6import PuduLangMediator.Behaviors.Resilient as Resilient7import PuduLangMediator.Context as Context8import PuduLangMediator.Mediator as Mediator9import PuduLangMediator as Messaging10import PuduLangMediator.Registration as Registration11import PuduLangMediator.Request as Request12import PuduLangMediator.Utils.Shared as Shared13import PuduLangResilience.Pipeline as Pipeline14import PuduLangResilience as Resilience15import PuduLangResilience.Retry as Retry16import PuduLangResilience.Strategy as Strategy17import PuduLangResilience.Timeout as Timeout1819/// A pipeline of the given strategies, or the reason it was refused.20fn guarded(strategies: Array[Strategy.Strategy[Int, Messaging.Failure[Str]]]) -> Pipeline.Pipeline[Int, Messaging.Failure[Str]] {21  match Pipeline.build(strategies) {22    case Ok(pipeline) => pipeline23    case Err(invalid) => panic(Pipeline.explain(&invalid))24  }25}2627/// Runs the suite.28fn main() -> Int {29  let flaky: Request.Kind[Int, Int, Str] = Request.kind("flaky")30  let slow: Request.Kind[Int, Int, Str] = Request.kind("slow")31  let attempts = Shared.shared(0)32  let retrying = guarded([Retry.strategy(Retry.Options{..Retry.defaults(), maxRetryAttempts: 3, delay: 0})])33  let timing = guarded([Timeout.strategy(Timeout.after(50))])34  let mediator = match Mediator.build([35      Resilient.register(&flaky, retrying),36      Registration.handler(&flaky, fn(failUntil: Int, _context: Context.Context) -> Messaging.Outcome[Int, Str] {37          let made = Shared.change(&attempts, fn(count: Int) -> (Int, Int) { (count + 1, count + 1) })38          if made < failUntil { Messaging.raise("attempt " + show(made)) } else { Ok(made) }39        }),40      Resilient.register(&slow, timing),41      Registration.handler(&slow, fn(millis: Int, context: Context.Context) -> Messaging.Outcome[Int, Str] {42          Context.pause(&context, millis) ?43          Ok(millis)44        })45    ]) {46    case Ok(found) => found47    case Err(invalid) => panic(Mediator.explain(&invalid))48  }49  let recovered = Mediator.send(&mediator, &flaky, 3)50  Shared.update(&attempts, |_count: Int| 0)51  let exhausted = Mediator.send(&mediator, &flaky, 10)52  let quick = Mediator.send(&mediator, &slow, 1)53  let late = Mediator.send(&mediator, &slow, 5000)54  let leaving = Context.create()55  Context.cancel(&leaving, "caller left")56  let cancelled = Mediator.sendWith(&mediator, &slow, 5000, leaving)57  let checks = Test.suite("Behaviors.Resilient", &[58      Test.equals("a failing handler is retried until it succeeds", &recovered, &Ok(3)),59      Test.equals("the handler's own failure survives exhausted retries", &(exhausted, Shared.current(&attempts)), &(Err(Messaging.Raised("attempt 4")), 4)),60      Test.equals("a quick handler is not timed out", &quick, &Ok(1)),61      Test.equals("a slow handler is refused with the timeout's description", &late, &Err(Messaging.Refused("The operation did not complete within 50 ms."))),62      Test.equals("a caller's cancellation stays a cancellation", &cancelled, &Err(Messaging.Cancelled("cancelled: caller left"))),63      Test.equals("a rejection becomes a refusal", &Resilient.translated(&Resilience.IsolatedCircuit), &Messaging.Refused("The circuit is isolated and admits no execution.")),64      Test.equals("a crash stays a crash", &Resilient.translated(&Resilience.Crashed("boom")), &Messaging.Crashed("boom"))65    ])66  let ran = Test.run(&checks)67  for failure in Test.failuresOf(&ran) {68    let _reported = Io.writeErrorLine(failure)69  }70  Test.report(&ran)71}72