
ArgumentsTest.pudu
Pudu98 lines5.2 KB
1/** @Test.Validation.Arguments.Suite — checks failure message values */2module PuduLangValidator.ArgumentsTest34import Std.Test as Test5import PuduLangValidator.Rule as Rule6import PuduLangValidator.Rules.Collection as Collection7import PuduLangValidator.Rules.Comparison as Comparison8import PuduLangValidator.Rules.Decimal as DecimalRule9import PuduLangValidator.Rules.Number as Number10import PuduLangValidator.Rules.Ordering as Ordering11import PuduLangValidator.Rules.Text as Text12import PuduLangValidator.Validator as Validator1314type Sample = { text: Str, number: Int, lines: Array[Str], amount: Decimal }1516fn textRules() -> Validator.Validator[Sample] {17 let length = Text.length(Rule.ruleFor("text", |sample: Sample| sample.text), 2, 4)18 let named = Rule.withMessage(length, "\{PropertyName\}: \{MinLength\}-\{MaxLength\}; got \{TotalLength\}.")19 Validator.add(Validator.create(), Rule.build(named))20}2122fn numberRules() -> Validator.Validator[Sample] {23 let comparison = Rule.withMessage(Number.greaterThan(Rule.ruleFor("number", |sample: Sample| sample.number), 5),24 "Compare \{ComparisonValue\}.")25 let bounded = Number.inclusiveBetween(comparison, 2, 7)26 let first = Rule.withMessage(bounded, "Outside \{From\}..\{To\}.")27 Validator.add(Validator.create(), Rule.build(first))28}2930fn collectionRules() -> Validator.Validator[Sample] {31 let count = Collection.countBetween(Rule.ruleFor("lines", |sample: Sample| sample.lines), 1, 2)32 let named = Rule.withMessage(count, "\{TotalCount\} items; expected \{From\}..\{To\}.")33 Validator.add(Validator.create(), Rule.build(named))34}3536fn decimalRules() -> Validator.Validator[Sample] {37 let precision = DecimalRule.precisionScale(Rule.ruleFor("amount", |sample: Sample| sample.amount), 3, 1, false)38 let named = Rule.withMessage(precision, "\{Digits\}/\{ActualScale\} exceeds \{ExpectedPrecision\}/\{ExpectedScale\}.")39 Validator.add(Validator.create(), Rule.build(named))40}4142fn customRules() -> Validator.Validator[Sample] {43 let firstBase = Rule.withArgument(Rule.withMessage(Rule.must(44 Rule.ruleFor("number", |sample: Sample| sample.number), |_value: Int| false45 ), "First \{Tag\} \{PropertyName\}."), "Tag", "one")46 let first = Rule.withArgument(firstBase, "PropertyName", "ignored")47 let secondBase = Rule.must(first, |_value: Int| false)48 let secondMessage = Rule.withMessage(secondBase, "Second \{Tag\} \{Actual\}.")49 let secondTagged = Rule.withArgument(Rule.withArgument(secondMessage, "Tag", "old"), "Tag", "two")50 let second = Rule.withArgumentFrom(secondTagged, "Actual", fn(root: Sample, value: Int) -> Str {51 show(root.number + value)52 })53 Validator.add(Validator.create(), Rule.build(second))54}5556fn literalRules() -> Validator.Validator[Sample] {57 let initial = Rule.withMessage(Rule.must(Rule.ruleFor("text", |sample: Sample| sample.text),58 |_value: Str| false), "A: \{A\}; value: \{PropertyValue\}.")59 let first = Rule.withArgument(initial, "A", "\{B\}")60 let second = Rule.withArgument(first, "B", "secret")61 Validator.add(Validator.create(), Rule.build(second))62}6364fn genericRules() -> Validator.Validator[Sample] {65 let equal = Rule.build(Rule.withMessage(Comparison.equal(66 Rule.ruleFor("number", |sample: Sample| sample.number), 4),67 "Expected \{ComparisonValue\}."))68 let between = Rule.build(Rule.withMessage(Ordering.exclusiveBetween(69 Rule.ruleFor("number", |sample: Sample| sample.number), 1, 5),70 "Strictly \{From\}..\{To\}."))71 Validator.add(Validator.add(Validator.create(), equal), between)72}7374fn main() -> Int {75 let sample = Sample{text: "x", number: 9, lines: [], amount: 12.34d}76 let text = Validator.validate(&textRules(), sample)77 let numbers = Validator.validate(&numberRules(), sample)78 let compared = Validator.validate(&numberRules(), Sample{..sample, number: 4})79 let collection = Validator.validate(&collectionRules(), sample)80 let decimal = Validator.validate(&decimalRules(), sample)81 let custom = Validator.validate(&customRules(), sample)82 let literal = Validator.validate(&literalRules(), Sample{..sample, text: "\{B\}"})83 let generic = Validator.validate(&genericRules(), sample)84 let checks = Test.suite("Arguments", &[85 Test.equals("text bounds and measured length render", &text.errors[0].errorMessage, &"text: 2-4; got 1."),86 Test.equals("interval bounds render", &numbers.errors[0].errorMessage, &"Outside 2..7."),87 Test.equals("collection count and bounds render", &collection.errors[0].errorMessage, &"0 items; expected 1..2."),88 Test.equals("decimal shape and bounds render", &decimal.errors[0].errorMessage, &"4/2 exceeds 3/1."),89 Test.equals("arguments stay with their own checks", &custom.errors[0].errorMessage, &"First one number."),90 Test.equals("latest argument replaces earlier value", &custom.errors[1].errorMessage, &"Second two 18."),91 Test.equals("comparison argument is stored on first check", &compared.errors[0].errorMessage, &"Compare 5."),92 Test.equals("argument and property values are inserted literally", &literal.errors[0].errorMessage, &"A: \{B\}; value: \{B\}."),93 Test.equals("generic equality exposes its comparison value", &generic.errors[0].errorMessage, &"Expected 4."),94 Test.equals("generic ordering exposes interval bounds", &generic.errors[1].errorMessage, &"Strictly 1..5.")95 ])96 Test.report(&Test.run(&checks))97}98