Pudu programming language
Menu
Package

@chrismichaelps / pudu-lang-validator

Typed fluent validation rules for Pudu

0.1.1Apache-2.01

InstallClose

TestingQueryTest.pudu

Pudu78 lines5.1 KB

GitHub ↗
1/** @Test.Validation.TestingQuery.Suite — checks chained failure assertions */2module PuduLangValidator.TestingQueryTest34import Std.Test as Test5import PuduLangValidator.Async as Async6import PuduLangValidator.Result as Validation7import PuduLangValidator.Rule as Rule8import PuduLangValidator.Testing as Checking9import PuduLangValidator.Validator as Validator1011type Model = { name: Str, age: Int }1213async fn isReady(_root: Model, value: Str) -> Bool { value == "ready" }1415async fn main() -> Int {16  let errors = Validation.fromFailures([17      Validation.Failure{propertyName: "items[0].name", errorMessage: "required", errorCode: "Missing",18        severity: Validation.Error, customState: "first" },19      Validation.Failure{propertyName: "items[0].name", errorMessage: "too short", errorCode: "Length",20        severity: Validation.Warning, customState: "second" },21      Validation.Failure{propertyName: "age", errorMessage: "young", errorCode: "Range",22        severity: Validation.Info, customState: "third" }23    ])24  let item = Checking.forProperty(&errors, "items[0].name")25  let required = Checking.withState(Checking.withSeverity(26      Checking.withCode(Checking.withMessage(item, "required"), "Missing"), Validation.Error), "first")27  let inverse = Checking.withoutState(Checking.withoutSeverity(28      Checking.withoutCode(Checking.withoutMessage(item, "required"), "Missing"), Validation.Error), "first")29  let empty = Validation.fromFailures([])30  let one = Validation.fromFailures([errors.errors[0]])31  let samePath = Validation.fromFailures([errors.errors[0], errors.errors[1]])32  let built = Rule.build(Rule.withCode(Rule.must(33        Rule.ruleFor("name", |model: Model| model.name), |name: Str| !name.isEmpty()), "Missing"))34  let sync = Validator.add(Validator.create(), built)35  let namedSync = Validator.add(Validator.create(), Rule.build(Rule.inRuleSet(Rule.withCode(36          Rule.must(Rule.ruleFor("name", |model: Model| model.name), |name: Str| !name.isEmpty()), "Missing"), "named")))37  let bad = Model{name: "", age: 2}38  let syncResult = Checking.testValidate(&sync, bad)39  let selectedOut = Checking.testValidateWith(&sync, bad, Validator.includeProperties(Validator.defaults(), ["age"]))40  let namedSyncDefault = Checking.testValidate(&namedSync, bad)41  let namedSyncSelected = Checking.testValidateWith(&namedSync, bad, Validator.onlyRuleSets(["named"]))42  let awaited = Async.add(Async.create(), Async.mustAsync("name", |model: Model| model.name,43      isReady, "not ready", "NotReady"))44  let namedAsync = Async.add(Async.create(), Async.inRuleSet(Async.mustAsync("name",45        |model: Model| model.name, isReady, "not ready", "NotReady"), "named"))46  let asyncResult = Checking.testValidateAsync(&awaited, bad).await47  let asyncSelectedOut = Checking.testValidateAsyncWith(&awaited, bad,48    Validator.includeProperties(Validator.defaults(), ["age"])).await49  let namedAsyncDefault = Checking.testValidateAsync(&namedAsync, bad).await50  let namedAsyncSelected = Checking.testValidateAsyncWith(&namedAsync, bad,51    Validator.onlyRuleSets(["named"])).await52  let checks = Test.suite("TestingQuery", &[53      Test.that("exact indexed path retains both failures", item.matched.length() == 2),54      Test.that("metadata chain retains one matching failure", required.matched.length() == 1),55      Test.that("inverse chain retains the other failure", inverse.matched.length() == 1),56      Test.that("query recognizes matching failures", Checking.hasAny(&required)),57      Test.that("query recognizes absent path", Checking.hasNone(&Checking.forProperty(&errors, "items[1].name"))),58      Test.that("excluded metadata leaves no match", Checking.hasNone(&Checking.withoutCode(59            Checking.forProperty(&one, "items[0].name"), "Missing"))),60      Test.that("extra failure prevents only", !Checking.only(&item)),61      Test.that("filtered sibling failure prevents only", !Checking.only(&Checking.withMessage(62            Checking.forProperty(&samePath, "items[0].name"), "required"))),63      Test.that("unfiltered repeated path satisfies only", Checking.only(&Checking.forProperty(&samePath, "items[0].name"))),64      Test.that("all matched failures satisfy only", Checking.only(&Checking.forProperty(&one, "items[0].name"))),65      Test.that("empty result does not satisfy only", !Checking.only(&Checking.forProperty(&empty, "name"))),66      Test.that("empty result satisfies has none", Checking.hasNone(&Checking.forProperty(&empty, "name"))),67      Test.that("sync test entry runs validator", Checking.hasCode(&syncResult, "Missing")),68      Test.that("sync test entry honors selected paths", selectedOut.isValid),69      Test.that("sync default skips named rule", namedSyncDefault.isValid),70      Test.that("sync selected set includes named rule", Checking.hasCode(&namedSyncSelected, "Missing")),71      Test.that("async test entry awaits validator", Checking.hasCode(&asyncResult, "NotReady")),72      Test.that("async test entry honors selected paths", asyncSelectedOut.isValid),73      Test.that("async default skips named rule", namedAsyncDefault.isValid),74      Test.that("async selected set includes named rule", Checking.hasCode(&namedAsyncSelected, "NotReady"))75    ])76  Test.report(&Test.run(&checks))77}78