
AsyncPreflightTest.pudu
Pudu84 lines4.6 KB
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