Pudu programming language
Menu
Package

@chrismichaelps / pudu-lang-validator

Typed fluent validation rules for Pudu

0.1.1Apache-2.01

InstallClose

Testing.pudu

Pudu118 lines4.9 KB

GitHub ↗
1/** @Validation.Testing.Predicates — queries structured validation results */2module PuduLangValidator.Testing34import Std.List as List5import PuduLangValidator.Async as Async6import PuduLangValidator.Result as Validation7import PuduLangValidator.Validator as Validator89/** @Validation.Testing.FailureQuery — narrows failures while retaining the full result */10export type FailureQuery = {11  all: Array[Validation.Failure],12  matched: Array[Validation.Failure]13}1415/// Run a validator and inspect its ordinary result.16export fn testValidate[T](validator: &Validator.Validator[T], root: T) -> Validation.ValidationResult {17  Validator.validate(validator, root)18}1920/// Run selected rules and inspect their ordinary result.21export fn testValidateWith[T](22  validator: &Validator.Validator[T], root: T, options: Validator.Options23) -> Validation.ValidationResult {24  Validator.validateWith(validator, root, options)25}2627/// Await a validator and inspect its ordinary result.28export async fn testValidateAsync[T](29  validator: &Async.AsyncValidator[T], root: T30) -> Validation.ValidationResult {31  Async.validateAsync(validator, root).await32}3334/// Await selected rules and inspect their ordinary result.35export async fn testValidateAsyncWith[T](36  validator: &Async.AsyncValidator[T], root: T, options: Validator.Options37) -> Validation.ValidationResult {38  Async.validateAsyncWith(validator, root, options).await39}4041/// Select failures at one exact property path, including collection indices.42export fn forProperty(result: &Validation.ValidationResult, path: Str) -> FailureQuery {43  FailureQuery{all: result.errors,44    matched: result.errors.filter(|failure: Validation.Failure| failure.propertyName == path) }45}4647/// Retain failures whose full message matches the given text.48export fn withMessage(query: FailureQuery, message: Str) -> FailureQuery {49  FailureQuery{..query, matched: query.matched.filter(|failure: Validation.Failure| failure.errorMessage == message)}50}5152/// Retain failures whose message differs from the given text.53export fn withoutMessage(query: FailureQuery, message: Str) -> FailureQuery {54  FailureQuery{..query, matched: query.matched.filter(|failure: Validation.Failure| failure.errorMessage != message)}55}5657/// Retain failures carrying the given error code.58export fn withCode(query: FailureQuery, code: Str) -> FailureQuery {59  FailureQuery{..query, matched: query.matched.filter(|failure: Validation.Failure| failure.errorCode == code)}60}6162/// Retain failures carrying another error code.63export fn withoutCode(query: FailureQuery, code: Str) -> FailureQuery {64  FailureQuery{..query, matched: query.matched.filter(|failure: Validation.Failure| failure.errorCode != code)}65}6667/// Retain failures at the given severity.68export fn withSeverity(query: FailureQuery, severity: Validation.Severity) -> FailureQuery {69  FailureQuery{..query, matched: query.matched.filter(|failure: Validation.Failure| failure.severity == severity)}70}7172/// Retain failures at another severity.73export fn withoutSeverity(query: FailureQuery, severity: Validation.Severity) -> FailureQuery {74  FailureQuery{..query, matched: query.matched.filter(|failure: Validation.Failure| failure.severity != severity)}75}7677/// Retain failures with the given application state.78export fn withState(query: FailureQuery, state: Str) -> FailureQuery {79  FailureQuery{..query, matched: query.matched.filter(|failure: Validation.Failure| failure.customState == state)}80}8182/// Retain failures with another application state.83export fn withoutState(query: FailureQuery, state: Str) -> FailureQuery {84  FailureQuery{..query, matched: query.matched.filter(|failure: Validation.Failure| failure.customState != state)}85}8687/// Whether the query retains at least one failure.88export fn hasAny(query: &FailureQuery) -> Bool { !query.matched.isEmpty() }8990/// Whether the query retains no failures.91export fn hasNone(query: &FailureQuery) -> Bool { query.matched.isEmpty() }9293/// Whether the query retains every failure and at least one exists.94export fn only(query: &FailureQuery) -> Bool {95  !query.matched.isEmpty() && query.matched.length() == query.all.length()96}9798/// Whether a result reports a failure at this exact path.99export fn hasErrorFor(result: &Validation.ValidationResult, path: Str) -> Bool {100  List.any(&result.errors, |failure: Validation.Failure| failure.propertyName == path)101}102103/// Whether a result reports no failure at this exact path.104export fn hasNoErrorFor(result: &Validation.ValidationResult, path: Str) -> Bool {105  !hasErrorFor(result, path)106}107108/// Whether any failure has the stable error code.109export fn hasCode(result: &Validation.ValidationResult, code: Str) -> Bool {110  List.any(&result.errors, |failure: Validation.Failure| failure.errorCode == code)111}112113/// Whether every failure is confined to the named paths, with at least one failure.114export fn onlyErrorsFor(result: &Validation.ValidationResult, paths: Array[Str]) -> Bool {115  !result.errors.isEmpty() && List.all(&result.errors,116    |failure: Validation.Failure| paths.contains(failure.propertyName))117}118