
BehaviorTest.pudu
Pudu56 lines3.4 KB
1/** @Test.Validation.Behavior.Suite — checks cascade and boundary cases */2module PuduLangValidator.BehaviorTest34import Std.Test as Test5import PuduLangValidator.Result as Validation6import PuduLangValidator.Rule as Rule7import PuduLangValidator.Rules.Ordering as OrderingRule8import PuduLangValidator.Rules.Text as TextRule9import PuduLangValidator.Validator as Validator1011type Item = { name: Str, count: Int, enabled: Bool }1213fn chain() -> Rule.RuleBuilder[Item, Str] {14 let base = Rule.ruleFor("name", |item: Item| item.name)15 let first = Rule.withCode(TextRule.notEmpty(base), "MissingName")16 Rule.withState(Rule.withSeverity(Rule.withMessage(TextRule.minimumLength(first, 3),17 "\{PropertyName\} failed at \{PropertyPath\}."), Validation.Warning), "profile")18}1920fn main() -> Int {21 let empty = Item{name: "", count: 0, enabled: false}22 let stopped = Validator.validate(&Validator.add(Validator.create(), Rule.build(Rule.stopOnFirst(chain()))), empty)23 let continued = Validator.validate(&Validator.add(Validator.create(), Rule.build(chain())), empty)24 let whenAll = Validator.validate(&Validator.add(Validator.create(), Rule.build(Rule.when(chain(),25 |item: Item| item.enabled))), empty)26 let whenCurrent = Validator.validate(&Validator.add(Validator.create(), Rule.build(Rule.whenCurrent(chain(),27 |item: Item| item.enabled))), empty)28 let count = Rule.build(OrderingRule.greaterThan(Rule.ruleFor("count", |item: Item| item.count), 0))29 let classStopped = Validator.validate(&Validator.stopOnFirst(Validator.add(30 Validator.add(Validator.create(), Rule.build(chain())), count)), empty)31 let preflight = Validator.withPreValidation(Validator.add(Validator.create(), count),32 fn(_item: Item) -> Option[Array[Validation.Failure]] { Some([]) })33 let preflightResult = Validator.validate(&preflight, empty)34 let regexGood = TextRule.matches(Rule.ruleFor("name", |item: Item| item.name), "^a+$")35 let regexBad = TextRule.matches(Rule.ruleFor("name", |item: Item| item.name), "(")36 let checks = Test.suite("Behavior", &[37 Test.equals("rule cascade stops after first failure", &stopped.errors.length(), &1),38 Test.equals("rule cascade continues by default", &continued.errors.length(), &2),39 Test.equals("all-check condition skips every check", &whenAll.errors.length(), &0),40 Test.equals("current-check condition leaves earlier checks", &whenCurrent.errors.length(), &1),41 Test.equals("custom code is attached to the first failure", &continued.errors[0].errorCode, &"MissingName"),42 Test.equals("severity and state are attached to latest check", &continued.errors[1].severity, &Validation.Warning),43 Test.equals("state is kept", &continued.errors[1].customState, &"profile"),44 Test.equals("message placeholders render paths", &continued.errors[1].errorMessage, &"name failed at name."),45 Test.equals("validator cascade stops after first rule", &classStopped.errors.length(), &2),46 Test.equals("preflight may stop with a valid empty result", &preflightResult.isValid, &true),47 Test.that("valid regex compiles", match regexGood { case Ok(_) => true case Err(_) => false }),48 Test.that("invalid regex reports configuration error", match regexBad { case Ok(_) => false case Err(_) => true }),49 Test.that("invalid result becomes typed failure", match Validation.toResult(&continued, empty) {50 case Ok(_) => false51 case Err(errors) => errors.length() == 252 })53 ])54 Test.report(&Test.run(&checks))55}56