From 87c77ca570fded1a5043d904a402be60dcec9598 Mon Sep 17 00:00:00 2001 From: Luis Majano Date: Mon, 10 Oct 2022 21:02:40 +0200 Subject: [PATCH 1/8] version bump --- box.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/box.json b/box.json index 00f32da..9857bc4 100644 --- a/box.json +++ b/box.json @@ -1,7 +1,7 @@ { "name":"ColdBox Validation", "author":"Ortus Solutions ", - "version":"4.0.0", + "version":"4.1.0", "location":"https://downloads.ortussolutions.com/ortussolutions/coldbox-modules/cbvalidation/@build.version@/cbvalidation-@build.version@.zip", "slug":"cbvalidation", "type":"modules", From 834cd04268bba439431ee023aabab6f2fd55f3eb Mon Sep 17 00:00:00 2001 From: Luis Majano Date: Mon, 10 Oct 2022 21:07:42 +0200 Subject: [PATCH 2/8] fix duplicate changelog version --- changelog.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/changelog.md b/changelog.md index 66a64d3..9dba163 100644 --- a/changelog.md +++ b/changelog.md @@ -64,7 +64,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ---- -## [3.1.0] => 2021-MAY-17 +## [3.1.1] => 2021-MAY-17 ### Fixed From f8362f5b88464c6cb6674022695f6643371d29db Mon Sep 17 00:00:00 2001 From: Michael Born Date: Tue, 1 Nov 2022 10:35:52 -0400 Subject: [PATCH 3/8] =?UTF-8?q?=F0=9F=90=9B=20FIX:=20Correct=20build:docs?= =?UTF-8?q?=20script=20bugs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit An error when running box run-script build:docs because variables.exportsDir doesn't exist. The directoryCreate( outputDir ) is worse than useless, since DocBox creates this dir for you, and this code creates the wrong directory anyway! (it creates .tmp/apidocs/ inside the build/ directory, which is not where it should be.) --- build/Build.cfc | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/build/Build.cfc b/build/Build.cfc index 926cce1..8e6eb76 100644 --- a/build/Build.cfc +++ b/build/Build.cfc @@ -130,9 +130,7 @@ component { ) .toConsole(); - // Prepare exports directory - variables.exportsDir = variables.artifactsDir & "/#projectName#/#arguments.version#"; - directoryCreate( variables.exportsDir, true, true ); + ensureExportDir( argumentCollection = arguments ); // Project Build Dir variables.projectBuildDir = variables.buildDir & "/#projectName#"; @@ -200,11 +198,12 @@ component { version = "1.0.0", outputDir = ".tmp/apidocs" ){ + ensureExportDir( argumentCollection = arguments ); + // Create project mapping fileSystemUtil.createMapping( arguments.projectName, variables.cwd ); // Generate Docs print.greenLine( "Generating API Docs, please wait..." ).toConsole(); - directoryCreate( arguments.outputDir, true, true ); command( "docbox generate" ) .params( @@ -315,4 +314,18 @@ component { return ( createObject( "java", "java.lang.System" ).getProperty( "cfml.cli.exitCode" ) ?: 0 ); } + /** + * Ensure the export directory exists at artifacts/NAME/VERSION/ + */ + private function ensureExportDir( + required projectName, + version = "1.0.0" + ){ + if ( structKeyExists( variables, "exportsDir" ) && directoryExists( variables.exportsDir ) ){ + return; + } + // Prepare exports directory + variables.exportsDir = variables.artifactsDir & "/#projectName#/#arguments.version#"; + directoryCreate( variables.exportsDir, true, true ); + } } From 68a72f3f6adcb8d2e01d1f547912fd34b3d675a2 Mon Sep 17 00:00:00 2001 From: michaelborn Date: Tue, 1 Nov 2022 14:36:49 +0000 Subject: [PATCH 4/8] Apply cfformat changes --- test-harness/tests/Application.cfc | 46 +++++++++++++++--------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/test-harness/tests/Application.cfc b/test-harness/tests/Application.cfc index f452d23..de90772 100644 --- a/test-harness/tests/Application.cfc +++ b/test-harness/tests/Application.cfc @@ -1,8 +1,8 @@ /** -* Copyright 2005-2007 ColdBox Framework by Luis Majano and Ortus Solutions, Corp -* www.ortussolutions.com -* --- -*/ + * Copyright 2005-2007 ColdBox Framework by Luis Majano and Ortus Solutions, Corp + * www.ortussolutions.com + * --- + */ component { // UPDATE THE NAME OF THE MODULE IN TESTING BELOW @@ -10,21 +10,21 @@ component { request.MODULE_PATH = "cbvalidation"; // APPLICATION CFC PROPERTIES - this.name = "ColdBoxTestingSuite"; - this.sessionManagement = true; - this.setClientCookies = true; - this.sessionTimeout = createTimeSpan( 0, 0, 15, 0 ); - this.applicationTimeout = createTimeSpan( 0, 0, 15, 0 ); - // Turn on/off white space management + this.name = "ColdBoxTestingSuite"; + this.sessionManagement = true; + this.setClientCookies = true; + this.sessionTimeout = createTimespan( 0, 0, 15, 0 ); + this.applicationTimeout = createTimespan( 0, 0, 15, 0 ); + // Turn on/off white space management this.whiteSpaceManagement = "smart"; - this.enableNullSupport = shouldEnableFullNullSupport(); + this.enableNullSupport = shouldEnableFullNullSupport(); // Create testing mapping this.mappings[ "/tests" ] = getDirectoryFromPath( getCurrentTemplatePath() ); // The application root - rootPath = reReplaceNoCase( this.mappings[ "/tests" ], "tests(\\|/)", "" ); - this.mappings[ "/root" ] = rootPath; + rootPath = reReplaceNoCase( this.mappings[ "/tests" ], "tests(\\|/)", "" ); + this.mappings[ "/root" ] = rootPath; this.mappings[ "/cbi18n" ] = rootPath & "modules/cbi18n"; // The module root path @@ -39,9 +39,9 @@ component { // request start public boolean function onRequestStart( String targetPage ){ // Set a high timeout for long running tests - setting requestTimeout="9999"; + setting requestTimeout ="9999"; // New ColdBox Virtual Application Starter - request.coldBoxVirtualApp = new coldbox.system.testing.VirtualApp( appMapping = "/root" ); + request.coldBoxVirtualApp= new coldbox.system.testing.VirtualApp( appMapping = "/root" ); // If hitting the runner or specs, prep our virtual app if ( getBaseTemplatePath().replace( expandPath( "/tests" ), "" ).reFindNoCase( "(runner|specs)" ) ) { @@ -49,8 +49,8 @@ component { } // ORM Reload for fresh results - if( structKeyExists( url, "fwreinit" ) ){ - if( structKeyExists( server, "lucee" ) ){ + if ( structKeyExists( url, "fwreinit" ) ) { + if ( structKeyExists( server, "lucee" ) ) { pagePoolClear(); } // ormReload(); @@ -60,14 +60,14 @@ component { return true; } - public void function onRequestEnd( required targetPage ) { + public void function onRequestEnd( required targetPage ){ request.coldBoxVirtualApp.shutdown(); } - private boolean function shouldEnableFullNullSupport() { - var system = createObject( "java", "java.lang.System" ); - var value = system.getEnv( "FULL_NULL" ); - return isNull( value ) ? false : !!value; - } + private boolean function shouldEnableFullNullSupport(){ + var system = createObject( "java", "java.lang.System" ); + var value = system.getEnv( "FULL_NULL" ); + return isNull( value ) ? false : !!value; + } } From 320dac33a3d0d4cd47c75a2a25adf68de232a509 Mon Sep 17 00:00:00 2001 From: Luis Majano Date: Mon, 14 Nov 2022 19:19:29 +0100 Subject: [PATCH 5/8] * New validators: `notSameAs, notSameAsNoCase` --- .../validators/NotSameAsNoCaseValidator.cfc | 67 +++++++++++++++++++ models/validators/NotSameAsValidator.cfc | 61 +++++++++++++++++ .../NotSameAsNoCaseValidatorTest.cfc | 47 +++++++++++++ .../validators/NotSameAsValidatorTest.cfc | 45 +++++++++++++ 4 files changed, 220 insertions(+) create mode 100644 models/validators/NotSameAsNoCaseValidator.cfc create mode 100644 models/validators/NotSameAsValidator.cfc create mode 100644 test-harness/tests/specs/validators/NotSameAsNoCaseValidatorTest.cfc create mode 100644 test-harness/tests/specs/validators/NotSameAsValidatorTest.cfc diff --git a/models/validators/NotSameAsNoCaseValidator.cfc b/models/validators/NotSameAsNoCaseValidator.cfc new file mode 100644 index 0000000..81b3018 --- /dev/null +++ b/models/validators/NotSameAsNoCaseValidator.cfc @@ -0,0 +1,67 @@ +/** + * Copyright since 2020 by Ortus Solutions, Corp + * www.ortussolutions.com + * --- + * This validator validates if a field is NOT the same as another field with no case sensitivity + */ +component extends="BaseValidator" accessors="true" singleton { + + /** + * Constructor + */ + NotSameAsNoCaseValidator function init(){ + variables.name = "NotSameAsNoCase"; + return this; + } + + /** + * Will check if an incoming value validates + * + * @validationResult The result object of the validation + * @target The target object to validate on + * @field The field on the target object to validate on + * @targetValue The target value to validate + * @validationData The validation data the validator was created with + * @rules The rules imposed on the currently validating field + */ + boolean function validate( + required any validationResult, + required any target, + required string field, + any targetValue, + any validationData, + struct rules + ){ + // get secondary value from property + var compareValue = invoke( arguments.target, "get#arguments.validationData#" ); + + // Check if both null values + if ( isNull( arguments.targetValue ) && isNull( compareValue ) ) { + return true; + } + + // return true if no data to check, type needs a data element to be checked. + if ( isNull( arguments.targetValue ) || isNullOrEmpty( arguments.targetValue ) ) { + return true; + } + + // Evaluate now + if ( compareNoCase( arguments.targetValue, compareValue ) NEQ 0 ) { + return true; + } + + var args = { + message : "The '#arguments.field#' value is the same as #compareValue.toString()#", + field : arguments.field, + validationType : getName(), + rejectedValue : ( isSimpleValue( arguments.targetValue ) ? arguments.targetValue : "" ), + validationData : arguments.validationData + }; + var error = validationResult + .newError( argumentCollection = args ) + .setErrorMetadata( { "notsameas" : arguments.validationData } ); + validationResult.addError( error ); + return false; + } + +} diff --git a/models/validators/NotSameAsValidator.cfc b/models/validators/NotSameAsValidator.cfc new file mode 100644 index 0000000..c152298 --- /dev/null +++ b/models/validators/NotSameAsValidator.cfc @@ -0,0 +1,61 @@ +/** + * Copyright since 2020 by Ortus Solutions, Corp + * www.ortussolutions.com + * --- + * This validator validates if a field is NOT the same as another field with case sensitivity + */ +component extends="BaseValidator" accessors="true" singleton { + + /** + * Constructor + */ + NotSameAsValidator function init(){ + variables.name = "NotSameAs"; + return this; + } + + /** + * Will check if an incoming value validates + * + * @validationResult The result object of the validation + * @target The target object to validate on + * @field The field on the target object to validate on + * @targetValue The target value to validate + * @validationData The validation data the validator was created with + * @rules The rules imposed on the currently validating field + */ + boolean function validate( + required any validationResult, + required any target, + required string field, + any targetValue, + any validationData, + struct rules + ){ + // return true if no data to check, type needs a data element to be checked. + if ( isNull( arguments.targetValue ) || isNullOrEmpty( arguments.targetValue ) ) { + return true; + } + + // get secondary value from property + var compareValue = invoke( arguments.target, "get#arguments.validationData#" ); + + // Compare it now + if ( compare( arguments.targetValue, compareValue ) NEQ 0 ) { + return true; + } + var args = { + message : "The '#arguments.field#' value is the same as #compareValue.toString()#", + field : arguments.field, + validationType : getName(), + rejectedValue : ( isSimpleValue( arguments.targetValue ) ? arguments.targetValue : "" ), + validationData : arguments.validationData + }; + var error = validationResult + .newError( argumentCollection = args ) + .setErrorMetadata( { "notsameas" : arguments.validationData } ); + validationResult.addError( error ); + return false; + } + +} diff --git a/test-harness/tests/specs/validators/NotSameAsNoCaseValidatorTest.cfc b/test-harness/tests/specs/validators/NotSameAsNoCaseValidatorTest.cfc new file mode 100644 index 0000000..126cf04 --- /dev/null +++ b/test-harness/tests/specs/validators/NotSameAsNoCaseValidatorTest.cfc @@ -0,0 +1,47 @@ +/** + * ******************************************************************************* + * ******************************************************************************* + */ +component + extends="coldbox.system.testing.BaseModelTest" + model ="cbvalidation.models.validators.NotSameAsNoCaseValidator" +{ + + function setup(){ + super.setup(); + model.init(); + } + + function testValidate(){ + result = createMock( "cbvalidation.models.result.ValidationResult" ).init(); + + // luis not the same as data + r = model.validate( + validationResult: result, + target : this, + field : "test", + targetValue : "data", + validationData : "luis" + ); + expect( r ).toBeTrue(); + + // luis is the same as data + r = model.validate( + validationResult: result, + target : this, + field : "test", + targetValue : "luis", + validationData : "luis" + ); + expect( r ).toBeFalse(); + } + + function getLuis(){ + return "luis"; + } + + function getData(){ + return "data"; + } + +} diff --git a/test-harness/tests/specs/validators/NotSameAsValidatorTest.cfc b/test-harness/tests/specs/validators/NotSameAsValidatorTest.cfc new file mode 100644 index 0000000..4b78ba0 --- /dev/null +++ b/test-harness/tests/specs/validators/NotSameAsValidatorTest.cfc @@ -0,0 +1,45 @@ +/** + * ******************************************************************************* + * ******************************************************************************* + */ +component extends="coldbox.system.testing.BaseModelTest" model="cbvalidation.models.validators.NotSameAsValidator" { + + function setup(){ + super.setup(); + model.init(); + } + + function testValidate(){ + result = createMock( "cbvalidation.models.result.ValidationResult" ).init(); + + + // luis not the same as data + r = model.validate( + validationResult: result, + target : this, + field : "test", + targetValue : "luis", + validationData : "data" + ); + expect( r ).toBeTrue(); + + // luis is the same as data + r = model.validate( + validationResult: result, + target : this, + field : "test", + targetValue : "luis", + validationData : "luis" + ); + expect( r ).toBeFalse(); + } + + function getLuis(){ + return "luis"; + } + + function getData(){ + return "data"; + } + +} From bcc70e92e32b282fddb48ac2389442e84771185c Mon Sep 17 00:00:00 2001 From: Luis Majano Date: Mon, 14 Nov 2022 19:19:35 +0100 Subject: [PATCH 6/8] * New ColdBox 7 delegate: `Validatable@cbValidation` which can be used to make objects validatable --- changelog.md | 9 +++ models/delegates/Validatable.cfc | 131 +++++++++++++++++++++++++++++++ 2 files changed, 140 insertions(+) create mode 100644 models/delegates/Validatable.cfc diff --git a/changelog.md b/changelog.md index 9dba163..67a7ccd 100644 --- a/changelog.md +++ b/changelog.md @@ -7,6 +7,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ---- +## [4.1.0] => 2022-NOV-14 + +### Added + +* New ColdBox 7 delegate: `Validatable@cbValidation` which can be used to make objects validatable +* New validators: `notSameAs, notSameAsNoCase` + +---- + ## [4.0.0] => 2022-OCT-10 ### Added diff --git a/models/delegates/Validatable.cfc b/models/delegates/Validatable.cfc new file mode 100644 index 0000000..4a8b72f --- /dev/null +++ b/models/delegates/Validatable.cfc @@ -0,0 +1,131 @@ +/** + * Copyright since 2016 by Ortus Solutions, Corp + * www.ortussolutions.com + * --- + * This delegate helps models validate themselves + */ +component accessors="true" { + + // DI + property name="validationManager" inject="ValidationManager@cbvalidation"; + + // Properties + property name="validationResults"; + + /** + * Validate the $parent and stores a validation result in the delegate + * + * @fields One or more fields to validate on, by default it validates all fields in the constraints. This can be a simple list or an array. + * @constraints An optional shared constraints name or an actual structure of constraints to validate on. + * @locale An optional locale to use for i18n messages + * @excludeFields An optional list of fields to exclude from the validation. + * @IncludeFields An optional list of fields to include in the validation. + * @profiles If passed, a list of profile names to use for validation constraints + */ + boolean function isValid( + string fields = "*", + any constraints = "", + string locale = "", + string excludeFields = "", + string includeFields = "", + string profiles = "" + ){ + // validate and save results in private scope + variables.validationResults = validate( argumentCollection = arguments ); + + // return it + return ( !variables.validationResults.hasErrors() ); + } + + /** + * Get the validation results object. This will be an empty validation object if isValid() has not being called yet. + * + * @return cbvalidation.models.result.IValidationResult + */ + any function getValidationResults(){ + if ( !isNull( variables.validationResults ) && isObject( variables.validationResults ) ) { + return variables.validationResults; + } + return new cbvalidation.models.result.ValidationResult(); + } + + /** + * Validate the parent delegate + * + * @fields The fields to validate on the target. By default, it validates on all fields + * @constraints A structure of constraint rules or the name of the shared constraint rules to use for validation + * @locale The i18n locale to use for validation messages + * @excludeFields The fields to exclude from the validation + * @includeFields The fields to include in the validation + * @profiles If passed, a list of profile names to use for validation constraints + * + * @return cbvalidation.model.result.IValidationResult + */ + function validate(){ + arguments.target = $parent; + return variables.validationManager.validate( argumentCollection = arguments ); + } + + /** + * Validate an object or structure according to the constraints rules and throw an exception if the validation fails. + * The validation errors will be contained in the `extendedInfo` of the exception in JSON format + * + * @fields The fields to validate on the target. By default, it validates on all fields + * @constraints A structure of constraint rules or the name of the shared constraint rules to use for validation + * @locale The i18n locale to use for validation messages + * @excludeFields The fields to exclude from the validation + * @includeFields The fields to include in the validation + * @profiles If passed, a list of profile names to use for validation constraints + * + * @return The validated object or the structure fields that where validated + * + * @throws ValidationException + */ + function validateOrFail(){ + arguments.target = $parent; + return variables.validationManager.validateOrFail( argumentCollection = arguments ); + } + + /** + * Verify if the target value has a value + * Checks for nullness or for length if it's a simple value, array, query, struct or object. + */ + boolean function validateHasValue( any targetValue ){ + return variables.validationManager + .getValidator( "Required", {} ) + .hasValue( argumentCollection = arguments ); + } + + /** + * Check if a value is null or is a simple value and it's empty + * + * @targetValue the value to check for nullness/emptyness + */ + boolean function validateIsNullOrEmpty( any targetValue ){ + return variables.validationManager + .getValidator( "Required", {} ) + .hasValue( argumentCollection = arguments ); + } + + /** + * This method mimics the Java assert() function, where it evaluates the target to a boolean value and it must be true + * to pass and return a true to you, or throw an `AssertException` + * + * @target The tareget to evaluate for being true + * @message The message to send in the exception + * + * @return True, if the target is a non-null value. If false, then it will throw the `AssertError` exception + * + * @throws AssertException if the target is a false or null value + */ + boolean function assert( target, message = "" ){ + if ( !isNull( arguments.target ) && arguments.target ) { + return true; + } + throw( + type : "AssertException", + message: len( arguments.message ) ? arguments.message : "Assertion failed from #callStackGet()[ 2 ].toString()#" + ); + } + +} From b7e20ba0c22f11258fd90be6d3dee1b3c5a14d51 Mon Sep 17 00:00:00 2001 From: Luis Majano Date: Tue, 15 Nov 2022 12:13:29 +0100 Subject: [PATCH 7/8] * All date comparison validators now validate as `false` when the comparison target dates values are NOT dates instead of throwing an exception. --- changelog.md | 4 ++++ models/validators/AfterOrEqualValidator.cfc | 18 ++++++++++++++---- models/validators/AfterValidator.cfc | 18 ++++++++++++++---- models/validators/BeforeOrEqualValidator.cfc | 15 ++++++++++++--- models/validators/BeforeValidator.cfc | 15 ++++++++++++--- models/validators/DateEqualsValidator.cfc | 16 +++++++++++++--- 6 files changed, 69 insertions(+), 17 deletions(-) diff --git a/changelog.md b/changelog.md index 67a7ccd..1e0b957 100644 --- a/changelog.md +++ b/changelog.md @@ -14,6 +14,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * New ColdBox 7 delegate: `Validatable@cbValidation` which can be used to make objects validatable * New validators: `notSameAs, notSameAsNoCase` +### Changed + +* All date comparison validators now validate as `false` when the comparison target dates values are NOT dates instead of throwing an exception. + ---- ## [4.0.0] => 2022-OCT-10 diff --git a/models/validators/AfterOrEqualValidator.cfc b/models/validators/AfterOrEqualValidator.cfc index c00f105..395cef3 100644 --- a/models/validators/AfterOrEqualValidator.cfc +++ b/models/validators/AfterOrEqualValidator.cfc @@ -38,12 +38,22 @@ component extends="BaseValidator" accessors="true" singleton { if ( isNull( arguments.targetValue ) || isNullOrEmpty( arguments.targetValue ) ) { return true; } - // If not a date, throw it + + // If not a date, invalide it if ( !isDate( arguments.targetValue ) ) { - throw( - message = "The date you sent is an invalid date [#arguments.targetValue#]", - type = "InvalidValidationData" + validationResult.addError( + validationResult.newError( + argumentCollection = { + message : "The '#arguments.targetValue#' is not a valid date, so we cannot compare them.", + field : arguments.field, + validationType : getName(), + rejectedValue : ( arguments.targetValue ), + validationData : arguments.validationData, + errorMetadata : { "afterOrEqual" : arguments.targetValue } + } + ) ); + return false; } // The compare value is set or it can be another field diff --git a/models/validators/AfterValidator.cfc b/models/validators/AfterValidator.cfc index 3366971..a217672 100644 --- a/models/validators/AfterValidator.cfc +++ b/models/validators/AfterValidator.cfc @@ -38,12 +38,22 @@ component extends="BaseValidator" accessors="true" singleton { if ( isNull( arguments.targetValue ) || isNullOrEmpty( arguments.targetValue ) ) { return true; } - // If target is not a date, throw it + + // If target is not a date, invalidate it if ( !isDate( arguments.targetValue ) ) { - throw( - message = "The date you sent is an invalid date [#arguments.targetValue#]", - type = "InvalidValidationData" + validationResult.addError( + validationResult.newError( + argumentCollection = { + message : "The '#arguments.targetValue#' is not a valid date, so we cannot compare them.", + field : arguments.field, + validationType : getName(), + rejectedValue : ( arguments.targetValue ), + validationData : arguments.validationData, + errorMetadata : { "afterOrEqual" : arguments.targetValue } + } + ) ); + return false; } // The compare value is set or it can be another field diff --git a/models/validators/BeforeOrEqualValidator.cfc b/models/validators/BeforeOrEqualValidator.cfc index 52b74e1..fba4760 100644 --- a/models/validators/BeforeOrEqualValidator.cfc +++ b/models/validators/BeforeOrEqualValidator.cfc @@ -40,10 +40,19 @@ component extends="BaseValidator" accessors="true" singleton { } // If not a date, throw it if ( !isDate( arguments.targetValue ) ) { - throw( - message = "The date you sent is an invalid date [#arguments.targetValue#]", - type = "InvalidValidationData" + validationResult.addError( + validationResult.newError( + argumentCollection = { + message : "The '#arguments.targetValue#' is not a valid date, so we cannot compare them.", + field : arguments.field, + validationType : getName(), + rejectedValue : ( arguments.targetValue ), + validationData : arguments.validationData, + errorMetadata : { "afterOrEqual" : arguments.targetValue } + } + ) ); + return false; } // The compare value is set or it can be another field diff --git a/models/validators/BeforeValidator.cfc b/models/validators/BeforeValidator.cfc index 58c261f..27502a8 100644 --- a/models/validators/BeforeValidator.cfc +++ b/models/validators/BeforeValidator.cfc @@ -40,10 +40,19 @@ component extends="BaseValidator" accessors="true" singleton { } // If not a date, throw it if ( !isDate( arguments.targetValue ) ) { - throw( - message = "The date you sent is an invalid date [#arguments.targetValue#]", - type = "InvalidValidationData" + validationResult.addError( + validationResult.newError( + argumentCollection = { + message : "The '#arguments.targetValue#' is not a valid date, so we cannot compare them.", + field : arguments.field, + validationType : getName(), + rejectedValue : ( arguments.targetValue ), + validationData : arguments.validationData, + errorMetadata : { "afterOrEqual" : arguments.targetValue } + } + ) ); + return false; } // The compare value is set or it can be another field diff --git a/models/validators/DateEqualsValidator.cfc b/models/validators/DateEqualsValidator.cfc index e391e66..f740113 100644 --- a/models/validators/DateEqualsValidator.cfc +++ b/models/validators/DateEqualsValidator.cfc @@ -38,12 +38,22 @@ component extends="BaseValidator" accessors="true" singleton { if ( isNull( arguments.targetValue ) || isNullOrEmpty( arguments.targetValue ) ) { return true; } + // If not a date, throw it if ( !isDate( arguments.targetValue ) ) { - throw( - message = "The date you sent is an invalid date [#arguments.targetValue#]", - type = "InvalidValidationData" + validationResult.addError( + validationResult.newError( + argumentCollection = { + message : "The '#arguments.targetValue#' is not a valid date, so we cannot compare them.", + field : arguments.field, + validationType : getName(), + rejectedValue : ( arguments.targetValue ), + validationData : arguments.validationData, + errorMetadata : { "afterOrEqual" : arguments.targetValue } + } + ) ); + return false; } // The compare value is set or it can be another field From 391495f38cd2dd228523590535981a3309c42a8a Mon Sep 17 00:00:00 2001 From: Luis Majano Date: Tue, 15 Nov 2022 12:37:01 +0100 Subject: [PATCH 8/8] more more more tests baby!!! --- helpers/Mixins.cfm | 2 +- models/delegates/Validatable.cfc | 2 +- .../tests/specs/delegates/ValidatableSpec.cfc | 75 +++++++++++++++++++ .../validators/AfterOrEqualValidatorTest.cfc | 8 +- .../specs/validators/AfterValidatorTest.cfc | 8 +- .../validators/BeforeOrEqualValidatorTest.cfc | 8 +- .../specs/validators/BeforeValidatorTest.cfc | 8 +- .../validators/DateEqualsValidatorTest.cfc | 6 +- 8 files changed, 96 insertions(+), 21 deletions(-) create mode 100644 test-harness/tests/specs/delegates/ValidatableSpec.cfc diff --git a/helpers/Mixins.cfm b/helpers/Mixins.cfm index d85403c..29e2aa3 100644 --- a/helpers/Mixins.cfm +++ b/helpers/Mixins.cfm @@ -73,7 +73,7 @@ boolean function validateHasValue( any targetValue ){ * @targetValue the value to check for nullness/emptyness */ boolean function validateIsNullOrEmpty( any targetValue ){ - return getValidationManager().getValidator( "Required", {} ).hasValue( argumentCollection = arguments ); + return !getValidationManager().getValidator( "Required", {} ).hasValue( argumentCollection = arguments ); } /** diff --git a/models/delegates/Validatable.cfc b/models/delegates/Validatable.cfc index 4a8b72f..e1f7599 100644 --- a/models/delegates/Validatable.cfc +++ b/models/delegates/Validatable.cfc @@ -102,7 +102,7 @@ component accessors="true" { * @targetValue the value to check for nullness/emptyness */ boolean function validateIsNullOrEmpty( any targetValue ){ - return variables.validationManager + return !variables.validationManager .getValidator( "Required", {} ) .hasValue( argumentCollection = arguments ); } diff --git a/test-harness/tests/specs/delegates/ValidatableSpec.cfc b/test-harness/tests/specs/delegates/ValidatableSpec.cfc new file mode 100644 index 0000000..0de18c1 --- /dev/null +++ b/test-harness/tests/specs/delegates/ValidatableSpec.cfc @@ -0,0 +1,75 @@ +component extends="coldbox.system.testing.BaseTestCase" appMapping="/root" { + + this.unLoadColdBox = false; + + /*********************************** LIFE CYCLE Methods ***********************************/ + + function beforeAll(){ + super.beforeAll(); + // do your own stuff here + } + + function afterAll(){ + // do your own stuff here + super.afterAll(); + } + + /*********************************** BDD SUITES ***********************************/ + + function run(){ + describe( "Validatable Spec", function(){ + beforeEach( function( currentSpec ){ + // Setup as a new ColdBox request, VERY IMPORTANT. ELSE EVERYTHING LOOKS LIKE THE SAME REQUEST. + setup(); + delegate = getInstance( "Validatable@cbValidation" ); + delegate.injectPropertyMixin( "$parent", this ); + } ); + + it( "can be created", function(){ + expect( delegate ).toBeComponent() + } ); + + it( "can validate if a target has value", function(){ + expect( delegate.validateHasValue( "test" ) ).toBeTrue(); + expect( delegate.validateHasValue( "" ) ).toBeFalse(); + } ); + + it( "can validate if a target is null or empty", function(){ + expect( delegate.validateIsNullOrEmpty( "" ) ).toBeTrue(); + expect( delegate.validateIsNullOrEmpty( javacast( "null", "" ) ) ).toBeTrue(); + expect( delegate.validateIsNullOrEmpty( "test" ) ).toBeFalse(); + } ); + + it( "can validate if a target can be asserted", function(){ + expect( delegate.assert( true ) ).toBeTrue(); + expect( () => delegate.assert( javacast( "null", "" ) ) ).toThrow(); + expect( () => delegate.assert( false ) ).toThrow(); + } ); + + it( "can validate", function(){ + var results = delegate.validate(); + expect( results.hasErrors() ).toBeFalse(); + }); + + it( "can validate or fail", function(){ + expect( () => delegate.validateOrFail() ).notToThrow(); + expect( () => delegate.validateOrFail( + constraints = { + "data" : { required : true } + } + ) ).toThrow(); + }); + + it( "can self validate", function(){ + var results = delegate.isValid(); + expect( results ).toBeTrue(); + expect( delegate.getValidationResults().hasErrors() ).toBeFalse(); + }); + } ); + } + + function getData(){ + return ""; + } + +} diff --git a/test-harness/tests/specs/validators/AfterOrEqualValidatorTest.cfc b/test-harness/tests/specs/validators/AfterOrEqualValidatorTest.cfc index 3038894..6a47a00 100644 --- a/test-harness/tests/specs/validators/AfterOrEqualValidatorTest.cfc +++ b/test-harness/tests/specs/validators/AfterOrEqualValidatorTest.cfc @@ -24,8 +24,8 @@ component result = createMock( "cbvalidation.models.result.ValidationResult" ).init(); } ); - it( "can throw an exception if the target value is not a date", function(){ - expect( function(){ + it( "can invalidate if the target value is not a date", function(){ + expect( model.validate( validationResult: result, target : this, @@ -33,8 +33,8 @@ component targetValue : "I am not a date", validationData : now(), rules : {} - ); - } ).toThrow(); + ) + ).toBeFalse(); } ); it( "can validate true if the field under validation is after the target", function(){ diff --git a/test-harness/tests/specs/validators/AfterValidatorTest.cfc b/test-harness/tests/specs/validators/AfterValidatorTest.cfc index 5d079e1..ad5188e 100644 --- a/test-harness/tests/specs/validators/AfterValidatorTest.cfc +++ b/test-harness/tests/specs/validators/AfterValidatorTest.cfc @@ -21,8 +21,8 @@ component extends="coldbox.system.testing.BaseModelTest" model="cbvalidation.mod result = createMock( "cbvalidation.models.result.ValidationResult" ).init(); } ); - it( "can throw an exception if the target value is not a date", function(){ - expect( function(){ + it( "can invalidate if the target value is not a date", function(){ + expect( model.validate( validationResult: result, target : this, @@ -30,8 +30,8 @@ component extends="coldbox.system.testing.BaseModelTest" model="cbvalidation.mod targetValue : "I am not a date", validationData : now(), rules : {} - ); - } ).toThrow(); + ) + ).toBeFalse(); } ); it( "can validate true if the field under validation is after the target", function(){ diff --git a/test-harness/tests/specs/validators/BeforeOrEqualValidatorTest.cfc b/test-harness/tests/specs/validators/BeforeOrEqualValidatorTest.cfc index ea17c1b..1f395a4 100644 --- a/test-harness/tests/specs/validators/BeforeOrEqualValidatorTest.cfc +++ b/test-harness/tests/specs/validators/BeforeOrEqualValidatorTest.cfc @@ -24,8 +24,8 @@ component result = createMock( "cbvalidation.models.result.ValidationResult" ).init(); } ); - it( "can throw an exception if the target value is not a date", function(){ - expect( function(){ + it( "can invalidate if the target value is not a date", function(){ + expect( model.validate( validationResult: result, target : this, @@ -33,8 +33,8 @@ component targetValue : "I am not a date", validationData : now(), rules : {} - ); - } ).toThrow(); + ) + ).toBeFalse(); } ); it( "can validate true if the field under validation is before the target", function(){ diff --git a/test-harness/tests/specs/validators/BeforeValidatorTest.cfc b/test-harness/tests/specs/validators/BeforeValidatorTest.cfc index 1a66e6a..878cc48 100644 --- a/test-harness/tests/specs/validators/BeforeValidatorTest.cfc +++ b/test-harness/tests/specs/validators/BeforeValidatorTest.cfc @@ -21,8 +21,8 @@ component extends="coldbox.system.testing.BaseModelTest" model="cbvalidation.mod result = createMock( "cbvalidation.models.result.ValidationResult" ).init(); } ); - it( "can throw an exception if the target value is not a date", function(){ - expect( function(){ + it( "can invalidate if the target value is not a date", function(){ + expect( model.validate( validationResult: result, target : this, @@ -30,8 +30,8 @@ component extends="coldbox.system.testing.BaseModelTest" model="cbvalidation.mod targetValue : "I am not a date", validationData : now(), rules : {} - ); - } ).toThrow(); + ) + ).toBeFalse(); } ); it( "can validate true if the field under validation is before the target", function(){ diff --git a/test-harness/tests/specs/validators/DateEqualsValidatorTest.cfc b/test-harness/tests/specs/validators/DateEqualsValidatorTest.cfc index 18b4e86..5b2ddc8 100644 --- a/test-harness/tests/specs/validators/DateEqualsValidatorTest.cfc +++ b/test-harness/tests/specs/validators/DateEqualsValidatorTest.cfc @@ -22,7 +22,7 @@ component extends="coldbox.system.testing.BaseModelTest" model="cbvalidation.mod } ); it( "can throw an exception if the target value is not a date", function(){ - expect( function(){ + expect( model.validate( validationResult: result, target : this, @@ -30,8 +30,8 @@ component extends="coldbox.system.testing.BaseModelTest" model="cbvalidation.mod targetValue : "I am not a date", validationData : now(), rules : {} - ); - } ).toThrow(); + ) + ).toBeFalse(); } ); it( "can validate false if the field under validation is before the target", function(){