
TestingQueryTest.pudu
Pudu78 lines5.1 KB
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