Pudu programming language
Menu
Package

@chrismichaelps / pudu-lang-validator

Typed fluent validation rules for Pudu

0.1.1Apache-2.01

InstallClose

AsyncPreflightTest.pudu

Pudu84 lines4.6 KB

GitHub ↗
1/** @Test.Validation.AsyncPreflight.Suite — checks early awaited decisions */2module PuduLangValidator.AsyncPreflightTest34import Std.Json as Json5import Std.Map as Map6import Std.Test as Test7import PuduLangValidator.Async as Async8import PuduLangValidator.Result as Validation9import PuduLangValidator.Rule as Rule10import PuduLangValidator.Validator as Validator1112type Child = { code: Str }13type Model = { child: Child }14type CollectionModel = { children: Array[Child] }1516fn failure() -> Validation.Failure {17  Validation.Failure{propertyName: "code", errorMessage: "Rejected before rules.",18    errorCode: "Preflight", severity: Validation.Warning, customState: "early" }19}2021async fn shouldNotRun(_root: Child, _value: Str) -> Bool { panic("rule ran after prevalidation") }2223async fn rejectsFromContext(24  _root: Child, data: Rule.ContextData25) -> Option[Array[Validation.Failure]] {26  match Map.get(&data, "reject") {27    case Some(Json.Text("yes")) => Some([failure()])28    case _ => None29  }30}3132async fn main() -> Int {33  let child = Child{code: ""}34  let rule = Async.mustAsync("code", |value: Child| value.code, shouldNotRun, "Code failed.", "Code")35  let earlySuccess = Async.withPreValidation(Async.add(Async.create(), rule),36    fn(_root: Child) -> Option[Array[Validation.Failure]] { Some([]) })37  let success = Async.validateAsync(&earlySuccess, child).await38  let earlyFailure = Async.withPreValidation(Async.add(Async.create(), rule),39    fn(_root: Child) -> Option[Array[Validation.Failure]] { Some([failure()]) })40  let rejected = Async.validateAsync(&earlyFailure, child).await41  let ordinary = Async.mustAsync("code", |value: Child| value.code,42    async fn(_root: Child, _value: Str) -> Bool { false }, "Code failed.", "Code")43  let contextOnly = Async.withPreValidationAsync(Async.add(Async.create(), ordinary), rejectsFromContext)44  let data: Rule.ContextData = mapOf([("reject", Json.Text("yes"))])45  let contextRejected = Async.validateAsyncWithData(&contextOnly, child, Validator.defaults(), data).await46  let continued = Async.validateAsync(&contextOnly, child).await47  let replaced = Async.withPreValidationAsync(48    Async.withPreValidation(Async.create(), fn(_root: Child) -> Option[Array[Validation.Failure]] {49        panic("replaced preflight ran")50      }), rejectsFromContext)51  let replacedResult = Async.validateAsync(&replaced, child).await52  let parent = Async.add(Async.create(),53    Async.setValidatorAsync("child", |model: Model| model.child, contextOnly))54  let childResult = Async.validateAsyncWithData(&parent, Model{child: child}, Validator.defaults(), data).await55  let excludedChild = Async.validateAsyncWithData(&parent, Model{child: child},56    Validator.includeProperties(Validator.defaults(), ["other"]), data).await57  let excludedSet = Async.validateAsyncWithData(&parent, Model{child: child},58    Validator.onlyRuleSets(["other"]), data).await59  let collection = Async.add(Async.create(),60    Async.forEachValidatorAsync("children", |model: CollectionModel| model.children, contextOnly))61  let selectedIndex = Async.validateAsyncWithData(&collection,62    CollectionModel{children: [child, child]},63    Validator.includeProperties(Validator.defaults(), ["children[1].code"]), data).await64  let unselected = Async.validateAsyncWith(&earlyFailure, child,65    Validator.includeProperties(Validator.defaults(), ["other"])).await66  let checks = Test.suite("AsyncPreflight", &[67      Test.that("empty early result is valid", success.isValid),68      Test.equals("empty early result has no failures", &success.errors.length(), &0),69      Test.equals("early failure skips rule", &rejected.errors[0].errorCode, &"Preflight"),70      Test.equals("early metadata stays intact", &rejected.errors[0].customState, &"early"),71      Test.equals("awaited preflight reads root context", &contextRejected.errors[0].errorCode, &"Preflight"),72      Test.equals("none continues into ordinary rules", &continued.errors[0].errorCode, &"Code"),73      Test.that("latest preflight replaces earlier callback", replacedResult.isValid),74      Test.equals("preflight precedes property selection", &unselected.errors[0].errorCode, &"Preflight"),75      Test.equals("child preflight path is prefixed", &childResult.errors[0].propertyName, &"child.code"),76      Test.equals("child preflight severity is retained", &childResult.errors[0].severity, &Validation.Warning),77      Test.that("unselected child skips preflight", excludedChild.isValid),78      Test.that("excluded child rule set skips preflight", excludedSet.isValid),79      Test.equals("selected collection index runs one child preflight",80          &selectedIndex.errors.map(|error: Validation.Failure| error.propertyName), &["children[1].code"])81    ])82  Test.report(&Test.run(&checks))83}84