
FallbackTest.pudu
Pudu61 lines3.5 KB
1/** @Test.Fallback.Suite — substitutes for handled outcomes */2module PuduLangResilience.FallbackTest34import Std.Io as Io5import Std.Result as Result6import Std.Sync as Sync7import Std.Test as Test8import PuduLangResilience.Context as Context9import PuduLangResilience.Fallback as Fallback10import PuduLangResilience.Pipeline as Pipeline11import PuduLangResilience.Predicate as Predicate12import PuduLangResilience as Resilience131415fn guarded(options: Fallback.Options[Int, Str]) -> Pipeline.Pipeline[Int, Str] {16 match Pipeline.build([Fallback.strategy(options)]) {17 case Ok(pipeline) => pipeline18 case Err(invalid) => panic(Pipeline.explain(&invalid))19 }20}212223fn answering(outcome: Resilience.Outcome[Int, Str]) -> fn(Context.Context) -> Resilience.Outcome[Int, Str] {24 fn(_context: Context.Context) -> Resilience.Outcome[Int, Str] { outcome }25}262728fn main() -> Int {29 let seen: Sync.Cell[Array[Str]] = Sync.cell([])30 let observed = Fallback.Options{..Fallback.withValue(0), onFallback: Some(fn(given: Predicate.Arguments[Int, Str]) -> () {31 let _kept = Sync.set(&seen, [Resilience.summarize(&given.outcome)])32 }) }33 let derived = Fallback.Options{..Fallback.defaults(), action: Some(fn(given: Predicate.Arguments[Int, Str]) -> Resilience.Outcome[Int, Str] {34 match given.outcome {35 case Err(Resilience.Raised(problem)) => Ok(problem.length())36 case _ => Ok(-1)37 }38 }) }39 let byResult = Fallback.Options{..Fallback.withValue(100), shouldHandle: Predicate.result(404)}40 let failing = Fallback.Options{..Fallback.defaults(), action: Some(fn(_given: Predicate.Arguments[Int, Str]) -> Resilience.Outcome[Int, Str] { Resilience.raise("no substitute") })}41 let missing = Pipeline.build([Fallback.strategy(Fallback.defaults())])42 let checks = Test.suite("Fallback", &[43 Test.equals("a handled failure is replaced by the substitute", &Pipeline.execute(&guarded(observed), answering(Resilience.raise("down"))), &Ok(0)),44 Test.equals("onFallback sees the handled outcome", &Sync.get(&seen), &Ok(["The operation failed: \"down\""])),45 Test.equals("a value passes through untouched", &Pipeline.execute(&guarded(observed), answering(Ok(5))), &Ok(5)),46 Test.equals("the substitute may depend on the outcome", &Pipeline.execute(&guarded(derived), answering(Resilience.raise("abc"))), &Ok(3)),47 Test.equals("a cancellation is not handled by default", &Pipeline.execute(&guarded(observed), answering(Err(Resilience.Cancelled("stop")))), &Err(Resilience.Cancelled("stop"))),48 Test.equals("a rejection by another strategy is handled by default", &Pipeline.execute(&guarded(observed), answering(Err(Resilience.TimedOut(10)))), &Ok(0)),49 Test.equals("a handled value is replaced", &Pipeline.execute(&guarded(byResult), answering(Ok(404))), &Ok(100)),50 Test.equals("an unhandled value is kept", &Pipeline.execute(&guarded(byResult), answering(Ok(200))), &Ok(200)),51 Test.equals("a failing substitute is answered as is", &Pipeline.execute(&guarded(failing), answering(Resilience.raise("down"))), &Resilience.raise("no substitute")),52 Test.equals("validation names the missing action", &Fallback.validate(&Fallback.defaults()), &["action is required"]),53 Test.equals("a fallback without an action is refused", &Result.err(missing), &Some(Pipeline.Invalid{problems: ["Fallback: action is required"]}))54 ])55 let ran = Test.run(&checks)56 for failure in Test.failuresOf(&ran) {57 let _reported = Io.writeErrorLine(failure)58 }59 Test.report(&ran)60}61