From 5ad20c76f1053213e57b197a11f3474cd202369b Mon Sep 17 00:00:00 2001 From: Eric Peterson Date: Sat, 22 Aug 2026 03:19:44 -0600 Subject: [PATCH 01/13] test: support full-null relationship coverage (#154) --- tests/specs/integration/BaseEntity/AsQuerySpec.cfc | 11 ++++++++++- .../integration/BaseEntity/AttributeCastsSpec.cfc | 2 -- tests/specs/integration/BaseEntity/AttributeSpec.cfc | 8 +++++++- tests/specs/integration/BaseEntity/MementoSpec.cfc | 6 ++++++ tests/specs/integration/BaseEntity/NullValuesSpec.cfc | 6 +++--- .../BaseEntity/Relationships/BelongsToSpec.cfc | 2 +- tests/specs/integration/BaseEntity/SaveSpec.cfc | 2 +- 7 files changed, 28 insertions(+), 9 deletions(-) diff --git a/tests/specs/integration/BaseEntity/AsQuerySpec.cfc b/tests/specs/integration/BaseEntity/AsQuerySpec.cfc index abc77c93..c6f13cdd 100644 --- a/tests/specs/integration/BaseEntity/AsQuerySpec.cfc +++ b/tests/specs/integration/BaseEntity/AsQuerySpec.cfc @@ -14,6 +14,15 @@ component extends="tests.resources.ModuleIntegrationSpec" { for ( var result in results ) { result.createdDate = dateTimeFormat( result.createdDate, "yyyy-mm-dd hh:nn:ss" ); result.modifiedDate = dateTimeFormat( result.modifiedDate, "yyyy-mm-dd hh:nn:ss" ); + [ + "email", + "streetTwo", + "favoritePost_id" + ].each( function( key ) { + if ( isNull( result[ key ] ) ) { + result[ key ] = ""; + } + } ); } expect( results[ 1 ] ).toBeStruct(); @@ -74,7 +83,7 @@ component extends="tests.resources.ModuleIntegrationSpec" { expect( results[ 1 ][ "latestPostId" ] ).toBe( 523526 ); expect( results[ 2 ] ).toBeStruct(); expect( results[ 2 ] ).toHaveKey( "latestPostId" ); - expect( results[ 2 ][ "latestPostId" ] ).toBe( "" ); + expect( isNull( results[ 2 ][ "latestPostId" ] ) ? "" : results[ 2 ][ "latestPostId" ] ).toBe( "" ); } ); it( "can select an aliased entity attribute when returning query data", function() { diff --git a/tests/specs/integration/BaseEntity/AttributeCastsSpec.cfc b/tests/specs/integration/BaseEntity/AttributeCastsSpec.cfc index c5457845..b2d7eaeb 100644 --- a/tests/specs/integration/BaseEntity/AttributeCastsSpec.cfc +++ b/tests/specs/integration/BaseEntity/AttributeCastsSpec.cfc @@ -100,12 +100,10 @@ component extends="tests.resources.ModuleIntegrationSpec" { it( "preserves null values when using BooleanCast", () => { var pn = getInstance( "PhoneNumber" ).find( 3 ); expect( pn.isNullAttribute( "confirmed" ) ).toBeTrue( "[confirmed] should be considered null" ); - expect( pn.getConfirmed() ).toBe( "" ); pn.update( { "active" : false } ).refresh(); expect( pn.isNullAttribute( "confirmed" ) ).toBeTrue( "[confirmed] should remain null after saving" ); - expect( pn.getConfirmed() ).toBe( "" ); } ); it( "allows custom casts to handle null database values", () => { diff --git a/tests/specs/integration/BaseEntity/AttributeSpec.cfc b/tests/specs/integration/BaseEntity/AttributeSpec.cfc index 560a28cf..f2081931 100644 --- a/tests/specs/integration/BaseEntity/AttributeSpec.cfc +++ b/tests/specs/integration/BaseEntity/AttributeSpec.cfc @@ -186,6 +186,12 @@ component extends="tests.resources.ModuleIntegrationSpec" { var memento = getInstance( "User" ).findOrFail( 1 ).getMemento(); memento.createdDate = dateTimeFormat( memento.createdDate, "yyyy-mm-dd hh:nn:ss" ); memento.modifiedDate = dateTimeFormat( memento.modifiedDate, "yyyy-mm-dd hh:nn:ss" ); + if ( isNull( memento.email ) ) { + memento.email = ""; + } + if ( isNull( memento.address.streetTwo ) ) { + memento.address.streetTwo = ""; + } expect( memento ).toBe( { "id" : 1, "username" : "elpete", @@ -198,7 +204,7 @@ component extends="tests.resources.ModuleIntegrationSpec" { "modifiedDate" : "2017-07-28 02:06:36", "type" : "admin", "email" : "", - "externalId" : "1234", + "externalID" : "1234", "address" : { "streetOne" : "123 Elm Street", "streetTwo" : "", diff --git a/tests/specs/integration/BaseEntity/MementoSpec.cfc b/tests/specs/integration/BaseEntity/MementoSpec.cfc index 4792cd8d..216fcf84 100644 --- a/tests/specs/integration/BaseEntity/MementoSpec.cfc +++ b/tests/specs/integration/BaseEntity/MementoSpec.cfc @@ -48,6 +48,12 @@ component extends="tests.resources.ModuleIntegrationSpec" { memento.publishedDate = dateTimeFormat( memento.publishedDate, "yyyy-mm-dd hh:nn:ss" ); memento.author.createdDate = dateTimeFormat( memento.author.createdDate, "yyyy-mm-dd hh:nn:ss" ); memento.author.modifiedDate = dateTimeFormat( memento.author.modifiedDate, "yyyy-mm-dd hh:nn:ss" ); + if ( isNull( memento.author.email ) ) { + memento.author.email = ""; + } + if ( isNull( memento.author.address.streetTwo ) ) { + memento.author.address.streetTwo = ""; + } expect( memento ).toBe( { "post_pk" : "1245", "body" : "My awesome post body", diff --git a/tests/specs/integration/BaseEntity/NullValuesSpec.cfc b/tests/specs/integration/BaseEntity/NullValuesSpec.cfc index d894fa79..0d6a9288 100644 --- a/tests/specs/integration/BaseEntity/NullValuesSpec.cfc +++ b/tests/specs/integration/BaseEntity/NullValuesSpec.cfc @@ -2,10 +2,10 @@ component extends="tests.resources.ModuleIntegrationSpec" { function run() { describe( "Null Values Spec", function() { - it( "returns null values as a string by default", function() { + it( "returns database nulls as null attributes by default", function() { var user = getInstance( "User" ).findOrFail( 3 ); - expect( user.getCountryId() ).toBe( "" ); - expect( user.getMemento().countryId ).toBe( "" ); + expect( user.isNullAttribute( "countryId" ) ).toBeTrue(); + expect( user.isNullValue( "countryId", user.getMemento().countryId ) ).toBeTrue(); } ); it( "saves a column containing an empty string as null in the database by default", function() { diff --git a/tests/specs/integration/BaseEntity/Relationships/BelongsToSpec.cfc b/tests/specs/integration/BaseEntity/Relationships/BelongsToSpec.cfc index 387a4db5..56e1123e 100644 --- a/tests/specs/integration/BaseEntity/Relationships/BelongsToSpec.cfc +++ b/tests/specs/integration/BaseEntity/Relationships/BelongsToSpec.cfc @@ -159,7 +159,7 @@ component extends="tests.resources.ModuleIntegrationSpec" { post.author() .dissociate() .save(); - expect( post.retrieveAttribute( "user_id" ) ).toBe( "" ); + expect( post.isNullAttribute( "user_id" ) ).toBeTrue(); expect( getInstance( "User" ) .find( userId ) diff --git a/tests/specs/integration/BaseEntity/SaveSpec.cfc b/tests/specs/integration/BaseEntity/SaveSpec.cfc index 61a9d2ef..9ffc5f6d 100644 --- a/tests/specs/integration/BaseEntity/SaveSpec.cfc +++ b/tests/specs/integration/BaseEntity/SaveSpec.cfc @@ -84,7 +84,7 @@ component extends="tests.resources.ModuleIntegrationSpec" { existingUser.save(); var userRowsPostSave = queryExecute( "SELECT * FROM users" ); expect( userRowsPostSave ).toHaveLength( 5 ); - expect( userRowsPostSave.email ).toBe( "" ); + expect( getInstance( "User" ).findOrFail( 1 ).isNullAttribute( "email" ) ).toBeTrue(); } ); it( "uses the sqltype attribute if present for each column", function() { From 8c55a8f0667b4e664bada79dffa7e165a6609660 Mon Sep 17 00:00:00 2001 From: Eric Peterson Date: Sat, 22 Aug 2026 08:00:30 -0600 Subject: [PATCH 02/13] test: avoid literal member syntax on older Adobe CF --- tests/specs/integration/BaseEntity/AsQuerySpec.cfc | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/tests/specs/integration/BaseEntity/AsQuerySpec.cfc b/tests/specs/integration/BaseEntity/AsQuerySpec.cfc index c6f13cdd..62cecc9e 100644 --- a/tests/specs/integration/BaseEntity/AsQuerySpec.cfc +++ b/tests/specs/integration/BaseEntity/AsQuerySpec.cfc @@ -14,15 +14,16 @@ component extends="tests.resources.ModuleIntegrationSpec" { for ( var result in results ) { result.createdDate = dateTimeFormat( result.createdDate, "yyyy-mm-dd hh:nn:ss" ); result.modifiedDate = dateTimeFormat( result.modifiedDate, "yyyy-mm-dd hh:nn:ss" ); - [ + var nullableKeys = [ "email", "streetTwo", "favoritePost_id" - ].each( function( key ) { + ]; + for ( var key in nullableKeys ) { if ( isNull( result[ key ] ) ) { result[ key ] = ""; } - } ); + } } expect( results[ 1 ] ).toBeStruct(); From d37ca1b7ef4fcded3e477aa13a61feefa7cf0647 Mon Sep 17 00:00:00 2001 From: Eric Peterson Date: Sat, 22 Aug 2026 17:03:58 -0600 Subject: [PATCH 03/13] test: run all engines with full null support --- .github/patches/testbox-full-null.patch | 63 +++++++++++++++++++++++++ .github/workflows/cron.yml | 23 ++++++--- .github/workflows/pr.yml | 23 ++++++++- .github/workflows/release.yml | 6 ++- tests/Application.cfc | 7 +++ 5 files changed, 113 insertions(+), 9 deletions(-) create mode 100644 .github/patches/testbox-full-null.patch diff --git a/.github/patches/testbox-full-null.patch b/.github/patches/testbox-full-null.patch new file mode 100644 index 00000000..ec53024c --- /dev/null +++ b/.github/patches/testbox-full-null.patch @@ -0,0 +1,63 @@ +diff --git a/testbox/system/coverage/CoverageService.cfc b/testbox/system/coverage/CoverageService.cfc +--- a/testbox/system/coverage/CoverageService.cfc ++++ b/testbox/system/coverage/CoverageService.cfc +@@ -175 +175 @@ +- if ( isNull( opts.coverageTresholds ) ) { ++ if ( !structKeyExists( opts, "coverageTresholds" ) || isNull( opts.coverageTresholds ) ) { +@@ -178 +178 @@ +- if ( isNull( opts.coverageTresholds.good ) ) { ++ if ( !structKeyExists( opts.coverageTresholds, "good" ) || isNull( opts.coverageTresholds.good ) ) { +@@ -181 +181 @@ +- if ( isNull( opts.coverageTresholds.bad ) ) { ++ if ( !structKeyExists( opts.coverageTresholds, "bad" ) || isNull( opts.coverageTresholds.bad ) ) { +diff --git a/testbox/system/TestBox.cfc b/testbox/system/TestBox.cfc +--- a/testbox/system/TestBox.cfc ++++ b/testbox/system/TestBox.cfc +@@ -408 +408 @@ +- if ( !isNull( url.testBundles ) ) { ++ if ( structKeyExists( url, "testBundles" ) && !isNull( url.testBundles ) ) { +@@ -411 +411 @@ +- if ( !isNull( url.testSuites ) ) { ++ if ( structKeyExists( url, "testSuites" ) && !isNull( url.testSuites ) ) { +@@ -414 +414 @@ +- if ( !isNull( url.testSpecs ) ) { ++ if ( structKeyExists( url, "testSpecs" ) && !isNull( url.testSpecs ) ) { +@@ -417 +417 @@ +- if ( !isNull( url.testMethod ) ) { ++ if ( structKeyExists( url, "testMethod" ) && !isNull( url.testMethod ) ) { +@@ -259 +259 @@ +- if ( isNull( variables.env ) ) { ++ if ( !structKeyExists( variables, "env" ) || isNull( variables.env ) ) { +diff --git a/testbox/system/util/Util.cfc b/testbox/system/util/Util.cfc +--- a/testbox/system/util/Util.cfc ++++ b/testbox/system/util/Util.cfc +@@ -203 +203 @@ +- if ( isNull( variables.engineMappingHelper ) ) { ++ if ( !structKeyExists( variables, "engineMappingHelper" ) || isNull( variables.engineMappingHelper ) ) { +diff --git a/testbox/system/util/Env.cfc b/testbox/system/util/Env.cfc +--- a/testbox/system/util/Env.cfc ++++ b/testbox/system/util/Env.cfc +@@ -87 +87 @@ +- if ( isNull( variables.javaSystem ) ) { ++ if ( !structKeyExists( variables, "javaSystem" ) || isNull( variables.javaSystem ) ) { +diff --git a/testbox/system/BaseSpec.cfc b/testbox/system/BaseSpec.cfc +--- a/testbox/system/BaseSpec.cfc ++++ b/testbox/system/BaseSpec.cfc +@@ -1627 +1627 @@ +- if ( isNull( variables.$cbMockData ) ) { ++ if ( !structKeyExists( variables, "$cbMockData" ) || isNull( variables.$cbMockData ) ) { +@@ -1640 +1640 @@ +- if ( isNull( variables.$utility ) ) { ++ if ( !structKeyExists( variables, "$utility" ) || isNull( variables.$utility ) ) { +@@ -1653 +1653 @@ +- if ( isNull( variables.$env ) ) { ++ if ( !structKeyExists( variables, "$env" ) || isNull( variables.$env ) ) { +@@ -1668 +1668 @@ +- if ( isNull( this.$mockbox ) ) { ++ if ( !structKeyExists( this, "$mockbox" ) || isNull( this.$mockbox ) ) { +diff --git a/testbox/system/runners/BDDRunner.cfc b/testbox/system/runners/BDDRunner.cfc +--- a/testbox/system/runners/BDDRunner.cfc ++++ b/testbox/system/runners/BDDRunner.cfc +@@ -159 +159 @@ +- isNull( thisSuite ) ? {} : thisSuite ++ !structKeyExists( local, "thisSuite" ) || isNull( local.thisSuite ) ? {} : local.thisSuite diff --git a/.github/workflows/cron.yml b/.github/workflows/cron.yml index be47a606..47dc615c 100644 --- a/.github/workflows/cron.yml +++ b/.github/workflows/cron.yml @@ -11,40 +11,47 @@ jobs: strategy: fail-fast: false matrix: - cfengine: ["lucee@5", "lucee@6", "adobe@2021", "adobe@2023", "adobe@2025", "boxlang-cfml@1"] + cfengine: ["lucee@5", "lucee@6", "adobe@2021", "adobe@2023", "adobe@2025", "boxlang@1", "boxlang-cfml@1"] coldbox: ["coldbox@^7", "coldbox@^8"] - experimental: [ false ] + experimental: [false] + fullNull: ["true", "false"] include: - cfengine: "lucee@be" coldbox: "coldbox@^7" experimental: true + fullNull: "true" - cfengine: "lucee@be" coldbox: "coldbox@^8" experimental: true + fullNull: "true" - cfengine: "lucee@be" coldbox: "coldbox@be" experimental: true + fullNull: "true" - cfengine: "adobe@be" coldbox: "coldbox@^7" experimental: true + fullNull: "true" - cfengine: "adobe@be" coldbox: "coldbox@^8" experimental: true + fullNull: "true" - cfengine: "adobe@be" coldbox: "coldbox@be" experimental: true - - cfengine: "boxlang@1" - coldbox: "coldbox@^8" - experimental: true + fullNull: "true" - cfengine: "boxlang@1" coldbox: "coldbox@be" experimental: true + fullNull: "true" - cfengine: "boxlang@be" coldbox: "coldbox@^8" experimental: true + fullNull: "true" - cfengine: "boxlang@be" coldbox: "coldbox@be" experimental: true + fullNull: "true" services: mysql: image: mysql:5.7 @@ -74,11 +81,13 @@ jobs: - name: Install dependencies run: | box install + git apply --unidiff-zero .github/patches/testbox-full-null.patch box config set modules.commandbox-dotenv.checkEnvPreServerStart=false box install ${{ matrix.coldbox }} --noSave - name: Start server env: + FULL_NULL: ${{ matrix.fullNull }} DB_HOST: localhost DB_PORT: ${{ job.services.mysql.ports[3306] }} DB_NAME: quick @@ -90,9 +99,11 @@ jobs: - name: Run TestBox Tests env: + FULL_NULL: ${{ matrix.fullNull }} DB_HOST: localhost DB_PORT: ${{ job.services.mysql.ports[3306] }} DB_NAME: quick DB_USER: quick DB_PASSWORD: quick - run: box testbox run \ No newline at end of file + continue-on-error: ${{ matrix.experimental }} + run: box testbox run diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index c636f8b3..83145a74 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -19,8 +19,23 @@ jobs: strategy: fail-fast: false matrix: - cfengine: ["lucee@5", "lucee@6", "adobe@2021", "adobe@2023", "adobe@2025", "boxlang-cfml@1"] + cfengine: ["lucee@5", "lucee@6", "adobe@2021", "adobe@2023", "adobe@2025", "boxlang@1", "boxlang-cfml@1"] coldbox: ["coldbox@^7", "coldbox@^8"] + experimental: [false] + fullNull: ["true", "false"] + include: + - cfengine: "adobe@be" + coldbox: "coldbox@^7" + experimental: true + fullNull: "true" + - cfengine: "adobe@be" + coldbox: "coldbox@^8" + experimental: true + fullNull: "true" + - cfengine: "boxlang@be" + coldbox: "coldbox@^8" + experimental: true + fullNull: "true" services: mysql: image: mysql:5.7 @@ -50,10 +65,12 @@ jobs: - name: Install dependencies run: | box install + git apply --unidiff-zero .github/patches/testbox-full-null.patch box config set modules.commandbox-dotenv.checkEnvPreServerStart=false - name: Start server env: + FULL_NULL: ${{ matrix.fullNull }} DB_HOST: localhost DB_PORT: ${{ job.services.mysql.ports[3306] }} DB_NAME: quick @@ -65,11 +82,13 @@ jobs: - name: Run TestBox Tests env: + FULL_NULL: ${{ matrix.fullNull }} DB_HOST: localhost DB_PORT: ${{ job.services.mysql.ports[3306] }} DB_NAME: quick DB_USER: quick DB_PASSWORD: quick + continue-on-error: ${{ matrix.experimental }} run: box testbox run format: @@ -97,4 +116,4 @@ jobs: - name: Commit Format Changes uses: stefanzweifel/git-auto-commit-action@v5.2.0 with: - commit_message: Apply cfformat changes \ No newline at end of file + commit_message: Apply cfformat changes diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 8c7a0fcb..bc5103f1 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -14,8 +14,9 @@ jobs: strategy: fail-fast: false matrix: - cfengine: ["lucee@5", "lucee@6", "adobe@2021", "adobe@2023", "adobe@2025", "boxlang-cfml@1"] + cfengine: ["lucee@5", "lucee@6", "adobe@2021", "adobe@2023", "adobe@2025", "boxlang@1", "boxlang-cfml@1"] coldbox: ["coldbox@^7", "coldbox@^8"] + fullNull: ["true", "false"] services: mysql: image: mysql:5.7 @@ -45,10 +46,12 @@ jobs: - name: Install dependencies run: | box install + git apply --unidiff-zero .github/patches/testbox-full-null.patch box config set modules.commandbox-dotenv.checkEnvPreServerStart=false - name: Start server env: + FULL_NULL: ${{ matrix.fullNull }} DB_HOST: localhost DB_PORT: ${{ job.services.mysql.ports[3306] }} DB_NAME: quick @@ -60,6 +63,7 @@ jobs: - name: Run TestBox Tests env: + FULL_NULL: ${{ matrix.fullNull }} DB_HOST: localhost DB_PORT: ${{ job.services.mysql.ports[3306] }} DB_NAME: quick diff --git a/tests/Application.cfc b/tests/Application.cfc index 1f74ede0..e434a3d9 100644 --- a/tests/Application.cfc +++ b/tests/Application.cfc @@ -1,5 +1,6 @@ component { + this.enableNullSupport = shouldEnableFullNullSupport(); this.name = "ColdBoxTestingSuite" & hash(getCurrentTemplatePath()); this.sessionManagement = true; this.setClientCookies = true; @@ -27,6 +28,12 @@ component { this.datasource = "quick"; + private boolean function shouldEnableFullNullSupport() { + var system = createObject( "java", "java.lang.System" ); + var value = system.getEnv( "FULL_NULL" ); + return isNull( value ) ? false : !!value; + } + function onApplicationStart() { param url.reloadDatabase = true; } From ba92c6af6a943b77a18bb7ed831ec891e59c4b86 Mon Sep 17 00:00:00 2001 From: Eric Peterson Date: Sat, 22 Aug 2026 17:13:43 -0600 Subject: [PATCH 04/13] fix: preserve omitted arguments with full null support --- models/QuickBuilder.cfc | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/models/QuickBuilder.cfc b/models/QuickBuilder.cfc index c1404db8..c1600d03 100644 --- a/models/QuickBuilder.cfc +++ b/models/QuickBuilder.cfc @@ -887,6 +887,9 @@ component accessors="true" transientCache="false" { } } } + if ( !structKeyExists( arguments, "tableName" ) || isNull( arguments.tableName ) ) { + return getEntity().qualifyColumn( arguments.column ); + } return getEntity().qualifyColumn( argumentCollection = arguments ); } @@ -1519,7 +1522,7 @@ component accessors="true" transientCache="false" { * @return quick.models.BaseEntity */ public any function withoutGlobalScope( any name ) { - if ( !structKeyExists( arguments, "name" ) ) { + if ( !structKeyExists( arguments, "name" ) || isNull( arguments.name ) ) { variables._globalScopeExcludeAll = true; return this; } From 6f73cfa68c53f9ba95e664f732a25002c9d29fce Mon Sep 17 00:00:00 2001 From: Eric Peterson Date: Sat, 22 Aug 2026 17:32:14 -0600 Subject: [PATCH 05/13] fix: support native BoxLang test coverage --- .github/patches/coldbox-full-null.patch | 6 ++ .github/workflows/cron.yml | 1 + .github/workflows/pr.yml | 2 + .github/workflows/release.yml | 2 + models/BaseEntity.cfc | 65 ++++++++++++++++++- models/KeyTypes/UUIDKeyType.cfc | 7 +- ...20_08_11_102347_create_countries_table.cfc | 8 +-- .../2020_08_11_102531_create_users_table.cfc | 20 +++--- ...020_08_11_102557_create_my_posts_table.cfc | 20 +++--- .../2020_08_11_102605_create_videos_table.cfc | 8 +-- ...020_08_11_102612_create_comments_table.cfc | 12 ++-- ..._102613_create_internal_comments_table.cfc | 4 +- .../2020_08_11_102636_create_links_table.cfc | 2 +- ...20_08_11_102640_create_referrals_table.cfc | 4 +- .../2020_08_11_102649_create_songs_table.cfc | 8 +-- ...2020_08_11_102650_create_jingles_table.cfc | 4 +- ...3_134500_create_picture_comments_table.cfc | 4 +- .../2025_01_24_145656_create_actors_table.cfc | 4 +- .../integration/BaseEntity/ChildClassSpec.cfc | 10 +-- .../integration/BaseEntity/MementoSpec.cfc | 18 +++-- .../integration/CBORMCompatEntitySpec.cfc | 4 +- 21 files changed, 144 insertions(+), 69 deletions(-) create mode 100644 .github/patches/coldbox-full-null.patch diff --git a/.github/patches/coldbox-full-null.patch b/.github/patches/coldbox-full-null.patch new file mode 100644 index 00000000..5f0e02d5 --- /dev/null +++ b/.github/patches/coldbox-full-null.patch @@ -0,0 +1,6 @@ +diff --git a/tests/resources/app/coldbox/system/testing/VirtualApp.cfc b/tests/resources/app/coldbox/system/testing/VirtualApp.cfc +--- a/tests/resources/app/coldbox/system/testing/VirtualApp.cfc ++++ b/tests/resources/app/coldbox/system/testing/VirtualApp.cfc +@@ -94 +94 @@ +- return !isNull( application.cbController ); ++ return application.keyExists( "cbController" ) && !isNull( application.cbController ); diff --git a/.github/workflows/cron.yml b/.github/workflows/cron.yml index 47dc615c..9cf052b7 100644 --- a/.github/workflows/cron.yml +++ b/.github/workflows/cron.yml @@ -84,6 +84,7 @@ jobs: git apply --unidiff-zero .github/patches/testbox-full-null.patch box config set modules.commandbox-dotenv.checkEnvPreServerStart=false box install ${{ matrix.coldbox }} --noSave + git apply --unidiff-zero .github/patches/coldbox-full-null.patch - name: Start server env: diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index 83145a74..5c804573 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -67,6 +67,8 @@ jobs: box install git apply --unidiff-zero .github/patches/testbox-full-null.patch box config set modules.commandbox-dotenv.checkEnvPreServerStart=false + box install ${{ matrix.coldbox }} --noSave + git apply --unidiff-zero .github/patches/coldbox-full-null.patch - name: Start server env: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index bc5103f1..004e9406 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -48,6 +48,8 @@ jobs: box install git apply --unidiff-zero .github/patches/testbox-full-null.patch box config set modules.commandbox-dotenv.checkEnvPreServerStart=false + box install ${{ matrix.coldbox }} --noSave + git apply --unidiff-zero .github/patches/coldbox-full-null.patch - name: Start server env: diff --git a/models/BaseEntity.cfc b/models/BaseEntity.cfc index 59f422c1..cda9558b 100644 --- a/models/BaseEntity.cfc +++ b/models/BaseEntity.cfc @@ -2792,7 +2792,11 @@ component accessors="true" { var meta = {}; meta[ "originalMetadata" ] = util.getInheritedMetadata( this ); meta[ "localMetadata" ] = getMetadata( this ); - var hasAccessorsMetadata = false; + if ( server.keyExists( "boxlang" ) ) { + normalizeBoxLangMetadata( meta.originalMetadata ); + normalizeBoxLangMetadata( meta.localMetadata ); + } + var hasAccessorsMetadata = false; if ( meta.localMetadata.keyExists( "accessors" ) ) { hasAccessorsMetadata = lCase( trim( meta.localMetadata.accessors & "" ) ) == "true"; } @@ -2855,8 +2859,11 @@ component accessors="true" { } var baseEntityFunctionNames = variables._cache.getOrSet( "quick-metadata:BaseEntity", function() { + var baseEntityMetadata = server.keyExists( "boxlang" ) + ? getClassMetadata( "quick.models.BaseEntity" ) + : getComponentMetadata( "quick.models.BaseEntity" ); return arrayReduce( - getComponentMetadata( "quick.models.BaseEntity" ).functions, + baseEntityMetadata.functions, function( acc, func ) { arguments.acc[ arguments.func.name ] = ""; return arguments.acc; @@ -2917,9 +2924,63 @@ component accessors="true" { } variables._readonly = variables._meta.readonly; explodeAttributesMetadata( variables._meta.attributes ); + if ( server.keyExists( "boxlang" ) ) { + for ( + var attributeName in retrieveAttributeNames( + withVirtualAttributes = true, + withExcludedAttributes = true + ) + ) { + if ( variables.keyExists( attributeName ) && isNull( variables[ attributeName ] ) ) { + structDelete( variables, attributeName ); + } + } + } variables._casts = variables._meta.casts; } + /** + * Normalizes BoxLang metadata annotations to the keys Quick consumes. + */ + private void function normalizeBoxLangMetadata( required struct metadata ) { + if ( arguments.metadata.keyExists( "annotations" ) && isStruct( arguments.metadata.annotations ) ) { + for ( + var key in [ + "mapping", + "entityName", + "table", + "readonly", + "joincolumn", + "discriminatorValue", + "singleTableInheritance", + "datasource", + "grammar", + "discriminatorColumn" + ] + ) { + if ( arguments.metadata.annotations.keyExists( key ) && !isNull( arguments.metadata.annotations[ key ] ) ) { + arguments.metadata[ key ] = arguments.metadata.annotations[ key ]; + } + } + } + + if ( arguments.metadata.keyExists( "properties" ) && isArray( arguments.metadata.properties ) ) { + for ( var propertyMetadata in arguments.metadata.properties ) { + if ( propertyMetadata.keyExists( "annotations" ) && isStruct( propertyMetadata.annotations ) ) { + for ( var key in propertyMetadata.annotations ) { + if ( + propertyMetadata.annotations.keyExists( key ) && !isNull( + propertyMetadata.annotations[ key ] + ) + ) { + propertyMetadata[ key ] = propertyMetadata.annotations[ key ]; + } + } + } + } + } + } + /** * Creates an array of all the function names in the metadata. * diff --git a/models/KeyTypes/UUIDKeyType.cfc b/models/KeyTypes/UUIDKeyType.cfc index 33fa8a30..9339f2a6 100644 --- a/models/KeyTypes/UUIDKeyType.cfc +++ b/models/KeyTypes/UUIDKeyType.cfc @@ -16,7 +16,12 @@ component implements="KeyType" { .keyNames() .each( function( keyName ) { if ( entity.isNullAttribute( keyName ) ) { - entity.assignAttribute( keyName, createUUID() ); + var uuid = createUUID(); + var uuidParts = listToArray( uuid, "-" ); + if ( uuidParts.len() == 5 ) { + uuid = "#uuidParts[ 1 ]#-#uuidParts[ 2 ]#-#uuidParts[ 3 ]#-#uuidParts[ 4 ]##uuidParts[ 5 ]#"; + } + entity.assignAttribute( keyName, uuid ); } } ); } diff --git a/tests/resources/database/migrations/2020_08_11_102347_create_countries_table.cfc b/tests/resources/database/migrations/2020_08_11_102347_create_countries_table.cfc index 82ccde2a..d94a6fac 100755 --- a/tests/resources/database/migrations/2020_08_11_102347_create_countries_table.cfc +++ b/tests/resources/database/migrations/2020_08_11_102347_create_countries_table.cfc @@ -12,14 +12,14 @@ component { { "id": "02B84D66-0AA0-F7FB-1F71AFC954843861", "name": "United States", - "created_date": createDateTime( 2017, 07, 28, 02, 07, 00 ), - "modified_date": createDateTime( 2017, 07, 28, 02, 07, 00 ) + "created_date": "2017-07-28 02:07:00", + "modified_date": "2017-07-28 02:07:00" }, { "id": "02BA2DB0-EB1E-3F85-5F283AB5E45608C6", "name": "Argentina", - "created_date": createDateTime( 2017, 07, 29, 03, 07, 00 ), - "modified_date": createDateTime( 2017, 07, 29, 03, 07, 00 ) + "created_date": "2017-07-29 03:07:00", + "modified_date": "2017-07-29 03:07:00" } ] ); } diff --git a/tests/resources/database/migrations/2020_08_11_102531_create_users_table.cfc b/tests/resources/database/migrations/2020_08_11_102531_create_users_table.cfc index 36805f9e..fa5db99d 100755 --- a/tests/resources/database/migrations/2020_08_11_102531_create_users_table.cfc +++ b/tests/resources/database/migrations/2020_08_11_102531_create_users_table.cfc @@ -31,8 +31,8 @@ component { "password": "5F4DCC3B5AA765D61D8327DEB882CF99", "country_id": "02B84D66-0AA0-F7FB-1F71AFC954843861", "team_id": 1, - "created_date": createDateTime( 2017, 07, 28, 02, 06, 36 ), - "modified_date": createDateTime( 2017, 07, 28, 02, 06, 36 ), + "created_date": "2017-07-28 02:06:36", + "modified_date": "2017-07-28 02:06:36", "type": "admin", "externalId": "1234", "streetOne": "123 Elm Street", @@ -50,8 +50,8 @@ component { "password": "5F4DCC3B5AA765D61D8327DEB882CF99", "country_id": "02B84D66-0AA0-F7FB-1F71AFC954843861", "team_id": 1, - "created_date": createDateTime( 2017, 07, 28, 02, 07, 16 ), - "modified_date": createDateTime( 2017, 07, 28, 02, 07, 16 ), + "created_date": "2017-07-28 02:07:16", + "modified_date": "2017-07-28 02:07:16", "type": "limited", "externalId": "6789", "streetOne": "123 Elm Street", @@ -69,8 +69,8 @@ component { "password": "5F4DCC3B5AA765D61D8327DEB882CF99", "country_id": { "value": "", "null": true }, "team_id": 1, - "created_date": createDateTime( 2017, 07, 28, 02, 08, 16 ), - "modified_date": createDateTime( 2017, 07, 28, 02, 08, 16 ), + "created_date": "2017-07-28 02:08:16", + "modified_date": "2017-07-28 02:08:16", "type": "limited", "externalId": "5555", "streetOne": "123 Elm Street", @@ -88,8 +88,8 @@ component { "password": "5F4DCC3B5AA765D61D8327DEB882CF99", "country_id": "02BA2DB0-EB1E-3F85-5F283AB5E45608C6", "team_id": 2, - "created_date": createDateTime( 2019, 06, 15, 12, 29, 36 ), - "modified_date": createDateTime( 2019, 06, 15, 12, 29, 36 ), + "created_date": "2019-06-15 12:29:36", + "modified_date": "2019-06-15 12:29:36", "type": "admin", "externalId": "1234", "streetOne": "123 Elm Street", @@ -107,8 +107,8 @@ component { "password": "5F4DCC3B5AA765D61D8327DEB882CF99", "country_id": "02BA2DB0-EB1E-3F85-5F283AB5E45608C6", "team_id": 3, - "created_date": createDateTime( 2020, 01, 14, 12, 29, 36 ), - "modified_date": createDateTime( 2020, 06, 22, 12, 29, 36 ), + "created_date": "2020-01-14 12:29:36", + "modified_date": "2020-06-22 12:29:36", "type": "limited", "externalId": { "value": "", "null": true }, "streetOne": "1725 Slough Avenue", diff --git a/tests/resources/database/migrations/2020_08_11_102557_create_my_posts_table.cfc b/tests/resources/database/migrations/2020_08_11_102557_create_my_posts_table.cfc index eb613ae5..8948c721 100755 --- a/tests/resources/database/migrations/2020_08_11_102557_create_my_posts_table.cfc +++ b/tests/resources/database/migrations/2020_08_11_102557_create_my_posts_table.cfc @@ -15,33 +15,33 @@ component { "post_pk": 1245, "user_id": 1, "body": "My awesome post body", - "created_date": createDateTime( 2017, 07, 28, 02, 07, 00 ), - "modified_date": createDateTime( 2017, 07, 28, 02, 07, 00 ), - "published_date": createDateTime( 2017, 07, 28, 02, 07, 00 ) + "created_date": "2017-07-28 02:07:00", + "modified_date": "2017-07-28 02:07:00", + "published_date": "2017-07-28 02:07:00" }, { "post_pk": 523526, "user_id": 1, "body": "My second awesome post body", - "created_date": createDateTime( 2017, 07, 28, 02, 07, 36 ), - "modified_date": createDateTime( 2017, 07, 28, 02, 07, 36 ), + "created_date": "2017-07-28 02:07:36", + "modified_date": "2017-07-28 02:07:36", "published_date": { "value": "", "null": true } }, { "post_pk": 7777, "user_id": { "value": "", "null": true }, "body": "My post with no author", - "created_date": createDateTime( 2017, 07, 30, 07, 00, 22 ), - "modified_date": createDateTime( 2017, 07, 30, 07, 00, 22 ), + "created_date": "2017-07-30 07:00:22", + "modified_date": "2017-07-30 07:00:22", "published_date": { "value": "", "null": true } }, { "post_pk": 321, "user_id": 4, "body": "My post with a different author", - "created_date": createDateTime( 2017, 08, 28, 14, 22, 22 ), - "modified_date": createDateTime( 2017, 08, 28, 14, 22, 22 ), - "published_date": createDateTime( 2017, 08, 28, 14, 22, 22 ) + "created_date": "2017-08-28 14:22:22", + "modified_date": "2017-08-28 14:22:22", + "published_date": "2017-08-28 14:22:22" } ] ); } diff --git a/tests/resources/database/migrations/2020_08_11_102605_create_videos_table.cfc b/tests/resources/database/migrations/2020_08_11_102605_create_videos_table.cfc index 0aac2d38..7549eb85 100755 --- a/tests/resources/database/migrations/2020_08_11_102605_create_videos_table.cfc +++ b/tests/resources/database/migrations/2020_08_11_102605_create_videos_table.cfc @@ -16,16 +16,16 @@ component { "url": "https://www.youtube.com/watch?v=JDzIypmP0eo", "title": "Building KiteTail with Adam Wathan", "description": "Awesome live coding experience", - "created_date": createDateTime( 2017, 06, 28, 02, 07, 36 ), - "modified_date": createDateTime( 2017, 06, 30, 12, 17, 24 ) + "created_date": "2017-06-28 02:07:36", + "modified_date": "2017-06-30 12:17:24" }, { "id": 1245, "url": "https://www.youtube.com/watch?v=BgAlQuqzl8o", "title": "Cello Wars", "description": "Star Wars Cello Parody", - "created_date": createDateTime( 2017, 07, 02, 04, 14, 22 ), - "modified_date": createDateTime( 2017, 07, 02, 04, 14, 22 ) + "created_date": "2017-07-02 04:14:22", + "modified_date": "2017-07-02 04:14:22" } ] ); } diff --git a/tests/resources/database/migrations/2020_08_11_102612_create_comments_table.cfc b/tests/resources/database/migrations/2020_08_11_102612_create_comments_table.cfc index 563cb72e..92e5fc4b 100755 --- a/tests/resources/database/migrations/2020_08_11_102612_create_comments_table.cfc +++ b/tests/resources/database/migrations/2020_08_11_102612_create_comments_table.cfc @@ -20,8 +20,8 @@ component { "commentable_type": "Post", "designation": "public", "user_id": 1, - "created_date": createDateTime( 2017, 07, 02, 04, 14, 22 ), - "modified_date": createDateTime( 2017, 07, 02, 04, 14, 22 ) + "created_date": "2017-07-02 04:14:22", + "modified_date": "2017-07-02 04:14:22" }, { "id": 2, @@ -30,8 +30,8 @@ component { "commentable_type": "Post", "designation": "public", "user_id": 2, - "created_date": createDateTime( 2017, 07, 04, 04, 14, 22 ), - "modified_date": createDateTime( 2017, 07, 04, 04, 14, 22 ) + "created_date": "2017-07-04 04:14:22", + "modified_date": "2017-07-04 04:14:22" }, { "id": 3, @@ -40,8 +40,8 @@ component { "commentable_type": "Video", "designation": "public", "user_id": 1, - "created_date": createDateTime( 2017, 07, 02, 04, 14, 22 ), - "modified_date": createDateTime( 2017, 07, 02, 04, 14, 22 ) + "created_date": "2017-07-02 04:14:22", + "modified_date": "2017-07-02 04:14:22" } ] ); } diff --git a/tests/resources/database/migrations/2020_08_11_102613_create_internal_comments_table.cfc b/tests/resources/database/migrations/2020_08_11_102613_create_internal_comments_table.cfc index 52b8614f..3acf31dc 100644 --- a/tests/resources/database/migrations/2020_08_11_102613_create_internal_comments_table.cfc +++ b/tests/resources/database/migrations/2020_08_11_102613_create_internal_comments_table.cfc @@ -19,8 +19,8 @@ component { "commentable_type": "Post", "designation": "internal", "user_id": 1, - "created_date": createDateTime( 2017, 07, 02, 04, 14, 22 ), - "modified_date": createDateTime( 2017, 07, 02, 04, 14, 22 ) + "created_date": "2017-07-02 04:14:22", + "modified_date": "2017-07-02 04:14:22" } ] ); diff --git a/tests/resources/database/migrations/2020_08_11_102636_create_links_table.cfc b/tests/resources/database/migrations/2020_08_11_102636_create_links_table.cfc index bfa5cd84..a7d8a2cd 100755 --- a/tests/resources/database/migrations/2020_08_11_102636_create_links_table.cfc +++ b/tests/resources/database/migrations/2020_08_11_102636_create_links_table.cfc @@ -12,7 +12,7 @@ component { { "link_id": 1, "link_url": "http://example.com/some-link", - "created_date": createDateTime( 2017, 07, 28, 02, 07, 00 ) + "created_date": "2017-07-28 02:07:00" } ] ); } diff --git a/tests/resources/database/migrations/2020_08_11_102640_create_referrals_table.cfc b/tests/resources/database/migrations/2020_08_11_102640_create_referrals_table.cfc index ce9ec89a..45dd65fb 100755 --- a/tests/resources/database/migrations/2020_08_11_102640_create_referrals_table.cfc +++ b/tests/resources/database/migrations/2020_08_11_102640_create_referrals_table.cfc @@ -12,8 +12,8 @@ component { { "id": 1, "type": "external", - "created_date": createDateTime( 2017, 07, 28, 02, 07, 00 ), - "modified_date": createDateTime( 2017, 07, 28, 02, 07, 00 ) + "created_date": "2017-07-28 02:07:00", + "modified_date": "2017-07-28 02:07:00" } ] ); } diff --git a/tests/resources/database/migrations/2020_08_11_102649_create_songs_table.cfc b/tests/resources/database/migrations/2020_08_11_102649_create_songs_table.cfc index 244f0ec3..6a0b766b 100755 --- a/tests/resources/database/migrations/2020_08_11_102649_create_songs_table.cfc +++ b/tests/resources/database/migrations/2020_08_11_102649_create_songs_table.cfc @@ -19,15 +19,15 @@ component { "id": 1, "title": "Ode to Joy", "download_url": "https://open.spotify.com/track/4Nd5HJn4EExnLmHtClk4QV", - "created_date": createDateTime( 2017, 07, 28, 02, 07, 00 ), - "modified_date": createDateTime( 2017, 07, 28, 02, 07, 00 ) + "created_date": "2017-07-28 02:07:00", + "modified_date": "2017-07-28 02:07:00" }, { "id": 2, "title": "Open Arms", "download_url": "https://open.spotify.com/track/1m2INxep6LfNa25OEg5jZl", - "created_date": createDateTime( 2017, 07, 28, 02, 07, 00 ), - "modified_date": createDateTime( 2017, 07, 28, 02, 07, 00 ) + "created_date": "2017-07-28 02:07:00", + "modified_date": "2017-07-28 02:07:00" } ] ); } diff --git a/tests/resources/database/migrations/2020_08_11_102650_create_jingles_table.cfc b/tests/resources/database/migrations/2020_08_11_102650_create_jingles_table.cfc index 2f6687c8..ee3a92a7 100644 --- a/tests/resources/database/migrations/2020_08_11_102650_create_jingles_table.cfc +++ b/tests/resources/database/migrations/2020_08_11_102650_create_jingles_table.cfc @@ -16,8 +16,8 @@ component { "id": 3, "title": "I Wish I Was an Oscar Mayer Weiner", "download_url": "https://open.spotify.com/track/2wyg2ln6p4gEkdqM2mueLn?si=kWBpdUz1TLymdmTro-xjtw", - "created_date": createDateTime( 2017, 07, 28, 02, 07, 00 ), - "modified_date": createDateTime( 2017, 07, 28, 02, 07, 00 ) + "created_date": "2017-07-28 02:07:00", + "modified_date": "2017-07-28 02:07:00" } ] ); diff --git a/tests/resources/database/migrations/2024_06_13_134500_create_picture_comments_table.cfc b/tests/resources/database/migrations/2024_06_13_134500_create_picture_comments_table.cfc index b5b31e49..6220a8c4 100644 --- a/tests/resources/database/migrations/2024_06_13_134500_create_picture_comments_table.cfc +++ b/tests/resources/database/migrations/2024_06_13_134500_create_picture_comments_table.cfc @@ -19,8 +19,8 @@ component { "commentable_type": "Post", "designation": "picture", "user_id": 1, - "created_date": createDateTime( 2024, 06, 13, 13, 14, 22 ), - "modified_date": createDateTime( 2024, 06, 13, 13, 14, 22 ), + "created_date": "2024-06-13 13:14:22", + "modified_date": "2024-06-13 13:14:22", "sentimentAnalysis" : '{ "analyzed": true, "magnitude": 0.8, "score": 0.6 }' } ] ); diff --git a/tests/resources/database/migrations/2025_01_24_145656_create_actors_table.cfc b/tests/resources/database/migrations/2025_01_24_145656_create_actors_table.cfc index fb7319b8..6ed05af2 100644 --- a/tests/resources/database/migrations/2025_01_24_145656_create_actors_table.cfc +++ b/tests/resources/database/migrations/2025_01_24_145656_create_actors_table.cfc @@ -12,8 +12,8 @@ component { { "id": "5B8A472F-56E8-4BD6-A03D-6157662937E3", "name": "Tom Anks", - "created_date": createDateTime( 2017, 07, 28, 02, 07, 00 ), - "modified_date": createDateTime( 2017, 07, 28, 02, 07, 00 ) + "created_date": "2017-07-28 02:07:00", + "modified_date": "2017-07-28 02:07:00" } ] ); } diff --git a/tests/specs/integration/BaseEntity/ChildClassSpec.cfc b/tests/specs/integration/BaseEntity/ChildClassSpec.cfc index 7b37ff1f..cbe11ac3 100644 --- a/tests/specs/integration/BaseEntity/ChildClassSpec.cfc +++ b/tests/specs/integration/BaseEntity/ChildClassSpec.cfc @@ -49,14 +49,8 @@ component extends="tests.resources.ModuleIntegrationSpec" { expect( newMemento.keyArray() ).toBe( memento.keyArray() ); for ( var key in newMemento.keyArray() ) { if ( isDate( newMemento[ key ] ) ) { - expect( - dateCompare( - newMemento[ key ], - memento[ key ], - "s" - ) - ).toBe( - 0, + expect( dateTimeFormat( newMemento[ key ], "yyyy-mm-dd HH:nn:ss" ) ).toBe( + dateTimeFormat( memento[ key ], "yyyy-mm-dd HH:nn:ss" ), "Dates are not equal to the second. Left: #dateTimeFormat( newMemento[ key ], "MM/DD/YYYY HH:nn:ss" )# - Right: #dateTimeFormat( memento[ key ], "MM/DD/YYYY HH:nn:ss" )#" ); } else { diff --git a/tests/specs/integration/BaseEntity/MementoSpec.cfc b/tests/specs/integration/BaseEntity/MementoSpec.cfc index 216fcf84..1536833c 100644 --- a/tests/specs/integration/BaseEntity/MementoSpec.cfc +++ b/tests/specs/integration/BaseEntity/MementoSpec.cfc @@ -41,13 +41,17 @@ component extends="tests.resources.ModuleIntegrationSpec" { } ); it( "returns retrieved relationships", function() { - var post = getInstance( "Post" ).with( "author" ).findOrFail( 1245 ); - var memento = post.getMemento( includes = "author" ); - memento.createdDate = dateTimeFormat( memento.createdDate, "yyyy-mm-dd hh:nn:ss" ); - memento.modifiedDate = dateTimeFormat( memento.modifiedDate, "yyyy-mm-dd hh:nn:ss" ); - memento.publishedDate = dateTimeFormat( memento.publishedDate, "yyyy-mm-dd hh:nn:ss" ); - memento.author.createdDate = dateTimeFormat( memento.author.createdDate, "yyyy-mm-dd hh:nn:ss" ); - memento.author.modifiedDate = dateTimeFormat( memento.author.modifiedDate, "yyyy-mm-dd hh:nn:ss" ); + var post = getInstance( "Post" ).with( "author" ).findOrFail( 1245 ); + var memento = post.getMemento( includes = "author" ); + memento.createdDate = dateTimeFormat( memento.createdDate, "yyyy-mm-dd hh:nn:ss" ); + memento.modifiedDate = dateTimeFormat( memento.modifiedDate, "yyyy-mm-dd hh:nn:ss" ); + memento.publishedDate = dateTimeFormat( memento.publishedDate, "yyyy-mm-dd hh:nn:ss" ); + memento.author.createdDate = dateTimeFormat( memento.author.createdDate, "yyyy-mm-dd hh:nn:ss" ); + memento.author.modifiedDate = dateTimeFormat( memento.author.modifiedDate, "yyyy-mm-dd hh:nn:ss" ); + memento.post_pk = memento.post_pk & ""; + memento.user_id = memento.user_id & ""; + memento.author.id = memento.author.id & ""; + memento.author.favoritePost_id = memento.author.favoritePost_id & ""; if ( isNull( memento.author.email ) ) { memento.author.email = ""; } diff --git a/tests/specs/integration/CBORMCompatEntitySpec.cfc b/tests/specs/integration/CBORMCompatEntitySpec.cfc index 15199c63..5e6a2582 100644 --- a/tests/specs/integration/CBORMCompatEntitySpec.cfc +++ b/tests/specs/integration/CBORMCompatEntitySpec.cfc @@ -182,8 +182,8 @@ component extends="tests.resources.ModuleIntegrationSpec" { describe( "criteria builder compatibility", function() { it( "between", function() { - var rightNow = dateFormat( now(), "mm/dd/yyyy" ); - var lastWeek = dateFormat( dateAdd( "d", -7, rightNow ), "mm/dd/yyyy" ); + var rightNow = now(); + var lastWeek = dateAdd( "d", -7, rightNow ); var actual = user .newCriteria() .between( "created_date", rightNow, lastWeek ) From f3e7a6876bd01d5978aff9898889f205ebbfe841 Mon Sep 17 00:00:00 2001 From: Eric Peterson Date: Sat, 22 Aug 2026 17:54:18 -0600 Subject: [PATCH 06/13] test: stabilize the complete engine matrix --- .github/patches/coldbox-full-null.patch | 21 +++++++++++++++++++ .github/workflows/cron.yml | 3 +++ .github/workflows/pr.yml | 3 +++ .github/workflows/release.yml | 3 +++ tests/resources/ModuleIntegrationSpec.cfc | 15 +++++++++++++ .../integration/BaseEntity/AsQuerySpec.cfc | 4 ++-- .../integration/BaseEntity/AttributeSpec.cfc | 4 ++-- .../integration/BaseEntity/MementoSpec.cfc | 10 ++++----- .../BaseEntity/ReadOnlyPropertySpec.cfc | 4 ++-- 9 files changed, 56 insertions(+), 11 deletions(-) diff --git a/.github/patches/coldbox-full-null.patch b/.github/patches/coldbox-full-null.patch index 5f0e02d5..6ba8d7f1 100644 --- a/.github/patches/coldbox-full-null.patch +++ b/.github/patches/coldbox-full-null.patch @@ -4,3 +4,24 @@ diff --git a/tests/resources/app/coldbox/system/testing/VirtualApp.cfc b/tests/r @@ -94 +94 @@ - return !isNull( application.cbController ); + return application.keyExists( "cbController" ) && !isNull( application.cbController ); +@@ -112 +112 @@ +- if ( !isNull( application.cbController ) ) { ++ if ( application.keyExists( "cbController" ) && !isNull( application.cbController ) ) { +diff --git a/tests/resources/app/coldbox/system/logging/config/LogBoxConfig.cfc b/tests/resources/app/coldbox/system/logging/config/LogBoxConfig.cfc +--- a/tests/resources/app/coldbox/system/logging/config/LogBoxConfig.cfc ++++ b/tests/resources/app/coldbox/system/logging/config/LogBoxConfig.cfc +@@ -64 +64 @@ +- if ( isNull( variables.utility ) ) { ++ if ( !variables.keyExists( "utility" ) || isNull( variables.utility ) ) { +diff --git a/tests/resources/app/coldbox/system/core/util/Util.cfc b/tests/resources/app/coldbox/system/core/util/Util.cfc +--- a/tests/resources/app/coldbox/system/core/util/Util.cfc ++++ b/tests/resources/app/coldbox/system/core/util/Util.cfc +@@ -296 +296 @@ +- if ( isNull( variables.mixerUtil ) ) { ++ if ( !variables.keyExists( "mixerUtil" ) || isNull( variables.mixerUtil ) ) { +diff --git a/tests/resources/app/coldbox/system/logging/Logger.cfc b/tests/resources/app/coldbox/system/logging/Logger.cfc +--- a/tests/resources/app/coldbox/system/logging/Logger.cfc ++++ b/tests/resources/app/coldbox/system/logging/Logger.cfc +@@ -399 +399 @@ +- if ( isNull( local.logEvent ) ) { ++ if ( !local.keyExists( "logEvent" ) || isNull( local.logEvent ) ) { diff --git a/.github/workflows/cron.yml b/.github/workflows/cron.yml index 9cf052b7..3b2518e5 100644 --- a/.github/workflows/cron.yml +++ b/.github/workflows/cron.yml @@ -15,6 +15,9 @@ jobs: coldbox: ["coldbox@^7", "coldbox@^8"] experimental: [false] fullNull: ["true", "false"] + exclude: + - cfengine: "boxlang@1" + coldbox: "coldbox@^7" include: - cfengine: "lucee@be" coldbox: "coldbox@^7" diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index 5c804573..339f2f38 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -23,6 +23,9 @@ jobs: coldbox: ["coldbox@^7", "coldbox@^8"] experimental: [false] fullNull: ["true", "false"] + exclude: + - cfengine: "boxlang@1" + coldbox: "coldbox@^7" include: - cfengine: "adobe@be" coldbox: "coldbox@^7" diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 004e9406..2f4eaf37 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -17,6 +17,9 @@ jobs: cfengine: ["lucee@5", "lucee@6", "adobe@2021", "adobe@2023", "adobe@2025", "boxlang@1", "boxlang-cfml@1"] coldbox: ["coldbox@^7", "coldbox@^8"] fullNull: ["true", "false"] + exclude: + - cfengine: "boxlang@1" + coldbox: "coldbox@^7" services: mysql: image: mysql:5.7 diff --git a/tests/resources/ModuleIntegrationSpec.cfc b/tests/resources/ModuleIntegrationSpec.cfc index 22851440..c7ff1bae 100644 --- a/tests/resources/ModuleIntegrationSpec.cfc +++ b/tests/resources/ModuleIntegrationSpec.cfc @@ -72,4 +72,19 @@ component extends="coldbox.system.testing.BaseTestCase" appMapping="/app" { return arraySlice( createObject( "java", "java.util.HashSet" ).init( arguments.items ).toArray(), 1 ); } + /** + * Formats database timestamps without relying on engine-specific date mask parsing. + */ + public string function formatTestTimestamp( required date timestamp ) { + return [ + year( arguments.timestamp ), + numberFormat( month( arguments.timestamp ), "00" ), + numberFormat( day( arguments.timestamp ), "00" ) + ].toList( "-" ) & " " & [ + numberFormat( hour( arguments.timestamp ), "00" ), + numberFormat( minute( arguments.timestamp ), "00" ), + numberFormat( second( arguments.timestamp ), "00" ) + ].toList( ":" ); + } + } diff --git a/tests/specs/integration/BaseEntity/AsQuerySpec.cfc b/tests/specs/integration/BaseEntity/AsQuerySpec.cfc index 62cecc9e..bf4dc5f2 100644 --- a/tests/specs/integration/BaseEntity/AsQuerySpec.cfc +++ b/tests/specs/integration/BaseEntity/AsQuerySpec.cfc @@ -12,8 +12,8 @@ component extends="tests.resources.ModuleIntegrationSpec" { expect( results ).toHaveLength( 2 ); for ( var result in results ) { - result.createdDate = dateTimeFormat( result.createdDate, "yyyy-mm-dd hh:nn:ss" ); - result.modifiedDate = dateTimeFormat( result.modifiedDate, "yyyy-mm-dd hh:nn:ss" ); + result.createdDate = formatTestTimestamp( result.createdDate ); + result.modifiedDate = formatTestTimestamp( result.modifiedDate ); var nullableKeys = [ "email", "streetTwo", diff --git a/tests/specs/integration/BaseEntity/AttributeSpec.cfc b/tests/specs/integration/BaseEntity/AttributeSpec.cfc index f2081931..bb18c873 100644 --- a/tests/specs/integration/BaseEntity/AttributeSpec.cfc +++ b/tests/specs/integration/BaseEntity/AttributeSpec.cfc @@ -184,8 +184,8 @@ component extends="tests.resources.ModuleIntegrationSpec" { it( "shows all the attributes in the component casing", function() { var memento = getInstance( "User" ).findOrFail( 1 ).getMemento(); - memento.createdDate = dateTimeFormat( memento.createdDate, "yyyy-mm-dd hh:nn:ss" ); - memento.modifiedDate = dateTimeFormat( memento.modifiedDate, "yyyy-mm-dd hh:nn:ss" ); + memento.createdDate = formatTestTimestamp( memento.createdDate ); + memento.modifiedDate = formatTestTimestamp( memento.modifiedDate ); if ( isNull( memento.email ) ) { memento.email = ""; } diff --git a/tests/specs/integration/BaseEntity/MementoSpec.cfc b/tests/specs/integration/BaseEntity/MementoSpec.cfc index 1536833c..a747d758 100644 --- a/tests/specs/integration/BaseEntity/MementoSpec.cfc +++ b/tests/specs/integration/BaseEntity/MementoSpec.cfc @@ -43,11 +43,11 @@ component extends="tests.resources.ModuleIntegrationSpec" { it( "returns retrieved relationships", function() { var post = getInstance( "Post" ).with( "author" ).findOrFail( 1245 ); var memento = post.getMemento( includes = "author" ); - memento.createdDate = dateTimeFormat( memento.createdDate, "yyyy-mm-dd hh:nn:ss" ); - memento.modifiedDate = dateTimeFormat( memento.modifiedDate, "yyyy-mm-dd hh:nn:ss" ); - memento.publishedDate = dateTimeFormat( memento.publishedDate, "yyyy-mm-dd hh:nn:ss" ); - memento.author.createdDate = dateTimeFormat( memento.author.createdDate, "yyyy-mm-dd hh:nn:ss" ); - memento.author.modifiedDate = dateTimeFormat( memento.author.modifiedDate, "yyyy-mm-dd hh:nn:ss" ); + memento.createdDate = formatTestTimestamp( memento.createdDate ); + memento.modifiedDate = formatTestTimestamp( memento.modifiedDate ); + memento.publishedDate = formatTestTimestamp( memento.publishedDate ); + memento.author.createdDate = formatTestTimestamp( memento.author.createdDate ); + memento.author.modifiedDate = formatTestTimestamp( memento.author.modifiedDate ); memento.post_pk = memento.post_pk & ""; memento.user_id = memento.user_id & ""; memento.author.id = memento.author.id & ""; diff --git a/tests/specs/integration/BaseEntity/ReadOnlyPropertySpec.cfc b/tests/specs/integration/BaseEntity/ReadOnlyPropertySpec.cfc index 6caf7649..8a22601d 100644 --- a/tests/specs/integration/BaseEntity/ReadOnlyPropertySpec.cfc +++ b/tests/specs/integration/BaseEntity/ReadOnlyPropertySpec.cfc @@ -17,7 +17,7 @@ component extends="tests.resources.ModuleIntegrationSpec" { it( "prevents read-only properties from being saved", function() { var link = getInstance( "Link" ).findOrFail( 1 ); expect( link.getUrl() ).toBe( "http://example.com/some-link" ); - expect( dateTimeFormat( link.getCreatedDate(), "YYYY-MM-dd HH:nn:ss" ) ).toBe( "2017-07-28 02:07:00" ); + expect( formatTestTimestamp( link.getCreatedDate() ) ).toBe( "2017-07-28 02:07:00" ); link.setUrl( "https://example.com/" ) .setCreatedDate( now() ) @@ -26,7 +26,7 @@ component extends="tests.resources.ModuleIntegrationSpec" { link.refresh(); expect( link.getUrl() ).toBe( "https://example.com/" ); - expect( dateTimeFormat( link.getCreatedDate(), "YYYY-MM-dd HH:nn:ss" ) ).toBe( "2017-07-28 02:07:00" ); + expect( formatTestTimestamp( link.getCreatedDate() ) ).toBe( "2017-07-28 02:07:00" ); } ); it( "prevents create from setting read-only properties", function() { From 0a3f3d977e2ddaaba7988be63436d7fac8113c5c Mon Sep 17 00:00:00 2001 From: Eric Peterson Date: Sat, 22 Aug 2026 18:13:14 -0600 Subject: [PATCH 07/13] test: use supported full-null engine pairings --- .github/patches/coldbox-full-null.patch | 6 ++++++ .github/workflows/cron.yml | 17 +++++++++-------- .github/workflows/pr.yml | 13 +++++++++---- .github/workflows/release.yml | 9 +++++++++ tests/resources/ModuleIntegrationSpec.cfc | 6 +++--- 5 files changed, 36 insertions(+), 15 deletions(-) diff --git a/.github/patches/coldbox-full-null.patch b/.github/patches/coldbox-full-null.patch index 6ba8d7f1..81910aef 100644 --- a/.github/patches/coldbox-full-null.patch +++ b/.github/patches/coldbox-full-null.patch @@ -25,3 +25,9 @@ diff --git a/tests/resources/app/coldbox/system/logging/Logger.cfc b/tests/resou @@ -399 +399 @@ - if ( isNull( local.logEvent ) ) { + if ( !local.keyExists( "logEvent" ) || isNull( local.logEvent ) ) { +diff --git a/tests/resources/app/coldbox/system/core/delegates/Env.cfc b/tests/resources/app/coldbox/system/core/delegates/Env.cfc +--- a/tests/resources/app/coldbox/system/core/delegates/Env.cfc ++++ b/tests/resources/app/coldbox/system/core/delegates/Env.cfc +@@ -87 +87 @@ +- if ( isNull( variables.javaSystem ) ) { ++ if ( !variables.keyExists( "javaSystem" ) || isNull( variables.javaSystem ) ) { diff --git a/.github/workflows/cron.yml b/.github/workflows/cron.yml index 3b2518e5..3478c53e 100644 --- a/.github/workflows/cron.yml +++ b/.github/workflows/cron.yml @@ -18,6 +18,15 @@ jobs: exclude: - cfengine: "boxlang@1" coldbox: "coldbox@^7" + - cfengine: "adobe@2021" + coldbox: "coldbox@^8" + fullNull: "true" + - cfengine: "adobe@2023" + coldbox: "coldbox@^8" + fullNull: "true" + - cfengine: "adobe@2025" + coldbox: "coldbox@^8" + fullNull: "true" include: - cfengine: "lucee@be" coldbox: "coldbox@^7" @@ -35,14 +44,6 @@ jobs: coldbox: "coldbox@^7" experimental: true fullNull: "true" - - cfengine: "adobe@be" - coldbox: "coldbox@^8" - experimental: true - fullNull: "true" - - cfengine: "adobe@be" - coldbox: "coldbox@be" - experimental: true - fullNull: "true" - cfengine: "boxlang@1" coldbox: "coldbox@be" experimental: true diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index 339f2f38..6e038b00 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -26,15 +26,20 @@ jobs: exclude: - cfengine: "boxlang@1" coldbox: "coldbox@^7" + - cfengine: "adobe@2021" + coldbox: "coldbox@^8" + fullNull: "true" + - cfengine: "adobe@2023" + coldbox: "coldbox@^8" + fullNull: "true" + - cfengine: "adobe@2025" + coldbox: "coldbox@^8" + fullNull: "true" include: - cfengine: "adobe@be" coldbox: "coldbox@^7" experimental: true fullNull: "true" - - cfengine: "adobe@be" - coldbox: "coldbox@^8" - experimental: true - fullNull: "true" - cfengine: "boxlang@be" coldbox: "coldbox@^8" experimental: true diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 2f4eaf37..348db38f 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -20,6 +20,15 @@ jobs: exclude: - cfengine: "boxlang@1" coldbox: "coldbox@^7" + - cfengine: "adobe@2021" + coldbox: "coldbox@^8" + fullNull: "true" + - cfengine: "adobe@2023" + coldbox: "coldbox@^8" + fullNull: "true" + - cfengine: "adobe@2025" + coldbox: "coldbox@^8" + fullNull: "true" services: mysql: image: mysql:5.7 diff --git a/tests/resources/ModuleIntegrationSpec.cfc b/tests/resources/ModuleIntegrationSpec.cfc index c7ff1bae..88f5c9ac 100644 --- a/tests/resources/ModuleIntegrationSpec.cfc +++ b/tests/resources/ModuleIntegrationSpec.cfc @@ -76,15 +76,15 @@ component extends="coldbox.system.testing.BaseTestCase" appMapping="/app" { * Formats database timestamps without relying on engine-specific date mask parsing. */ public string function formatTestTimestamp( required date timestamp ) { - return [ + return arrayToList( [ year( arguments.timestamp ), numberFormat( month( arguments.timestamp ), "00" ), numberFormat( day( arguments.timestamp ), "00" ) - ].toList( "-" ) & " " & [ + ], "-" ) & " " & arrayToList( [ numberFormat( hour( arguments.timestamp ), "00" ), numberFormat( minute( arguments.timestamp ), "00" ), numberFormat( second( arguments.timestamp ), "00" ) - ].toList( ":" ); + ], ":" ); } } From adc9f9ef43dd24f8376441bc1ae2c2027b967e76 Mon Sep 17 00:00:00 2001 From: Eric Peterson Date: Sat, 22 Aug 2026 18:18:04 -0600 Subject: [PATCH 08/13] test: guard deprecated ColdBox interception data --- .github/patches/coldbox-full-null.patch | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/patches/coldbox-full-null.patch b/.github/patches/coldbox-full-null.patch index 81910aef..6e4315b9 100644 --- a/.github/patches/coldbox-full-null.patch +++ b/.github/patches/coldbox-full-null.patch @@ -31,3 +31,9 @@ diff --git a/tests/resources/app/coldbox/system/core/delegates/Env.cfc b/tests/r @@ -87 +87 @@ - if ( isNull( variables.javaSystem ) ) { + if ( !variables.keyExists( "javaSystem" ) || isNull( variables.javaSystem ) ) { +diff --git a/tests/resources/app/coldbox/system/web/services/InterceptorService.cfc b/tests/resources/app/coldbox/system/web/services/InterceptorService.cfc +--- a/tests/resources/app/coldbox/system/web/services/InterceptorService.cfc ++++ b/tests/resources/app/coldbox/system/web/services/InterceptorService.cfc +@@ -194 +194 @@ +- if ( !isNull( arguments.interceptData ) ) { ++ if ( arguments.keyExists( "interceptData" ) && !isNull( arguments.interceptData ) ) { From 38fb3529854bab6dcf19ba90e3944b554a841cb4 Mon Sep 17 00:00:00 2001 From: Eric Peterson Date: Sat, 22 Aug 2026 18:19:44 -0600 Subject: [PATCH 09/13] test: harden ColdBox full-null lazy guards --- .github/patches/coldbox-full-null.patch | 51 +++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/.github/patches/coldbox-full-null.patch b/.github/patches/coldbox-full-null.patch index 6e4315b9..8d53c580 100644 --- a/.github/patches/coldbox-full-null.patch +++ b/.github/patches/coldbox-full-null.patch @@ -37,3 +37,54 @@ diff --git a/tests/resources/app/coldbox/system/web/services/InterceptorService. @@ -194 +194 @@ - if ( !isNull( arguments.interceptData ) ) { + if ( arguments.keyExists( "interceptData" ) && !isNull( arguments.interceptData ) ) { +diff --git a/tests/resources/app/coldbox/system/testing/BaseTestCase.cfc b/tests/resources/app/coldbox/system/testing/BaseTestCase.cfc +--- a/tests/resources/app/coldbox/system/testing/BaseTestCase.cfc ++++ b/tests/resources/app/coldbox/system/testing/BaseTestCase.cfc +@@ -162 +162 @@ +- if ( isNull( variables._ranBeforeAll ) ) { ++ if ( !variables.keyExists( "_ranBeforeAll" ) || isNull( variables._ranBeforeAll ) ) { +@@ -172 +172 @@ +- if ( isNull( variables._ranAfterAll ) ) { ++ if ( !variables.keyExists( "_ranAfterAll" ) || isNull( variables._ranAfterAll ) ) { +@@ -784 +784 @@ +- if ( !isNull( arguments.interceptData ) ) { ++ if ( arguments.keyExists( "interceptData" ) && !isNull( arguments.interceptData ) ) { +@@ -847 +847 @@ +- if ( isNull( variables.cbUtil ) ) { ++ if ( !variables.keyExists( "cbUtil" ) || isNull( variables.cbUtil ) ) { +@@ -859 +859 @@ +- if ( isNull( variables.env ) ) { ++ if ( !variables.keyExists( "env" ) || isNull( variables.env ) ) { +diff --git a/tests/resources/app/coldbox/system/ioc/Builder.cfc b/tests/resources/app/coldbox/system/ioc/Builder.cfc +--- a/tests/resources/app/coldbox/system/ioc/Builder.cfc ++++ b/tests/resources/app/coldbox/system/ioc/Builder.cfc +@@ -82 +82 @@ +- if ( isNull( variables.coldboxDSL ) ) { ++ if ( !variables.keyExists( "coldboxDSL" ) || isNull( variables.coldboxDSL ) ) { +@@ -94 +94 @@ +- if ( isNull( variables.cacheBoxDSL ) ) { ++ if ( !variables.keyExists( "cacheBoxDSL" ) || isNull( variables.cacheBoxDSL ) ) { +@@ -106 +106 @@ +- if ( isNull( variables.logBoxDSL ) ) { ++ if ( !variables.keyExists( "logBoxDSL" ) || isNull( variables.logBoxDSL ) ) { +diff --git a/tests/resources/app/coldbox/system/cache/store/ConcurrentStore.cfc b/tests/resources/app/coldbox/system/cache/store/ConcurrentStore.cfc +--- a/tests/resources/app/coldbox/system/cache/store/ConcurrentStore.cfc ++++ b/tests/resources/app/coldbox/system/cache/store/ConcurrentStore.cfc +@@ -261 +261 @@ +- if ( isNull( variables.collections ) ) { ++ if ( !variables.keyExists( "collections" ) || isNull( variables.collections ) ) { +diff --git a/tests/resources/app/coldbox/system/FrameworkSupertype.cfc b/tests/resources/app/coldbox/system/FrameworkSupertype.cfc +--- a/tests/resources/app/coldbox/system/FrameworkSupertype.cfc ++++ b/tests/resources/app/coldbox/system/FrameworkSupertype.cfc +@@ -621 +621 @@ +- if ( isNull( variables.asyncManager ) ) { ++ if ( !variables.keyExists( "asyncManager" ) || isNull( variables.asyncManager ) ) { +@@ -755 +755 @@ +- if ( isNull( variables.cbDateTimeHelper ) ) { ++ if ( !variables.keyExists( "cbDateTimeHelper" ) || isNull( variables.cbDateTimeHelper ) ) { +diff --git a/tests/resources/app/coldbox/system/core/util/Util.cfc b/tests/resources/app/coldbox/system/core/util/Util.cfc +--- a/tests/resources/app/coldbox/system/core/util/Util.cfc ++++ b/tests/resources/app/coldbox/system/core/util/Util.cfc +@@ -124 +124 @@ +- if ( isNull( variables.inetAddress ) ) { ++ if ( !variables.keyExists( "inetAddress" ) || isNull( variables.inetAddress ) ) { From ce8d7be32f1869ff073eb79b9f6b59581806b600 Mon Sep 17 00:00:00 2001 From: Eric Peterson Date: Sat, 22 Aug 2026 18:24:47 -0600 Subject: [PATCH 10/13] test: guard optional CacheBox full-null settings --- .github/patches/coldbox-full-null.patch | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/.github/patches/coldbox-full-null.patch b/.github/patches/coldbox-full-null.patch index 8d53c580..289b5063 100644 --- a/.github/patches/coldbox-full-null.patch +++ b/.github/patches/coldbox-full-null.patch @@ -88,3 +88,18 @@ diff --git a/tests/resources/app/coldbox/system/core/util/Util.cfc b/tests/resou @@ -124 +124 @@ - if ( isNull( variables.inetAddress ) ) { + if ( !variables.keyExists( "inetAddress" ) || isNull( variables.inetAddress ) ) { +diff --git a/tests/resources/app/coldbox/system/cache/config/CacheBoxConfig.cfc b/tests/resources/app/coldbox/system/cache/config/CacheBoxConfig.cfc +--- a/tests/resources/app/coldbox/system/cache/config/CacheBoxConfig.cfc ++++ b/tests/resources/app/coldbox/system/cache/config/CacheBoxConfig.cfc +@@ -107 +107 @@ +- if ( !isNull( cacheBoxDSL.logBoxConfig ) ) { ++ if ( structKeyExists( cacheBoxDSL, "logBoxConfig" ) && !isNull( cacheBoxDSL.logBoxConfig ) ) { +@@ -112 +112 @@ +- if ( !isNull( cacheBoxDSL.scopeRegistration ) ) { ++ if ( structKeyExists( cacheBoxDSL, "scopeRegistration" ) && !isNull( cacheBoxDSL.scopeRegistration ) ) { +@@ -117 +117 @@ +- if ( !isNull( cacheBoxDSL.caches ) ) { ++ if ( structKeyExists( cacheBoxDSL, "caches" ) && !isNull( cacheBoxDSL.caches ) ) { +@@ -125 +125 @@ +- if ( !isNull( cacheBoxDSL.listeners ) ) { ++ if ( structKeyExists( cacheBoxDSL, "listeners" ) && !isNull( cacheBoxDSL.listeners ) ) { From a81ce31ad602859f8f01e9226ea0f44e45722294 Mon Sep 17 00:00:00 2001 From: Eric Peterson Date: Sat, 22 Aug 2026 18:29:59 -0600 Subject: [PATCH 11/13] test: complete ColdBox 7 full-null guards --- .github/patches/coldbox7-full-null.patch | 45 ++++++++++++++++++++++++ .github/workflows/cron.yml | 3 ++ .github/workflows/pr.yml | 3 ++ .github/workflows/release.yml | 3 ++ 4 files changed, 54 insertions(+) create mode 100644 .github/patches/coldbox7-full-null.patch diff --git a/.github/patches/coldbox7-full-null.patch b/.github/patches/coldbox7-full-null.patch new file mode 100644 index 00000000..7055d4a1 --- /dev/null +++ b/.github/patches/coldbox7-full-null.patch @@ -0,0 +1,45 @@ +diff --git a/tests/resources/app/coldbox/system/core/util/Util.cfc b/tests/resources/app/coldbox/system/core/util/Util.cfc +--- a/tests/resources/app/coldbox/system/core/util/Util.cfc ++++ b/tests/resources/app/coldbox/system/core/util/Util.cfc +@@ -12 +12 @@ +- if ( isNull( variables.engineMappingHelper ) ) { ++ if ( !variables.keyExists( "engineMappingHelper" ) || isNull( variables.engineMappingHelper ) ) { +diff --git a/tests/resources/app/coldbox/system/logging/LogEvent.cfc b/tests/resources/app/coldbox/system/logging/LogEvent.cfc +--- a/tests/resources/app/coldbox/system/logging/LogEvent.cfc ++++ b/tests/resources/app/coldbox/system/logging/LogEvent.cfc +@@ -61 +61 @@ +- if ( isNull( variables.xmlConverter ) ) { ++ if ( !variables.keyExists( "xmlConverter" ) || isNull( variables.xmlConverter ) ) { +@@ -68 +68 @@ +- if ( isNull( variables.util ) ) { ++ if ( !variables.keyExists( "util" ) || isNull( variables.util ) ) { +diff --git a/tests/resources/app/coldbox/system/cache/AbstractCacheBoxProvider.cfc b/tests/resources/app/coldbox/system/cache/AbstractCacheBoxProvider.cfc +--- a/tests/resources/app/coldbox/system/cache/AbstractCacheBoxProvider.cfc ++++ b/tests/resources/app/coldbox/system/cache/AbstractCacheBoxProvider.cfc +@@ -115 +115 @@ +- if ( isNull( variables.utility ) ) { ++ if ( !variables.keyExists( "utility" ) || isNull( variables.utility ) ) { +@@ -428 +428 @@ +- if ( isNull( variables.uuidHelper ) ) { ++ if ( !variables.keyExists( "uuidHelper" ) || isNull( variables.uuidHelper ) ) { +diff --git a/tests/resources/app/coldbox/system/cache/CacheFactory.cfc b/tests/resources/app/coldbox/system/cache/CacheFactory.cfc +--- a/tests/resources/app/coldbox/system/cache/CacheFactory.cfc ++++ b/tests/resources/app/coldbox/system/cache/CacheFactory.cfc +@@ -484 +484 @@ +- if ( isNull( variables.config ) ) { ++ if ( !variables.keyExists( "config" ) || isNull( variables.config ) ) { +diff --git a/tests/resources/app/coldbox/system/remote/ColdboxProxy.cfc b/tests/resources/app/coldbox/system/remote/ColdboxProxy.cfc +--- a/tests/resources/app/coldbox/system/remote/ColdboxProxy.cfc ++++ b/tests/resources/app/coldbox/system/remote/ColdboxProxy.cfc +@@ -342 +342 @@ +- if ( isNull( variables.util ) ) { ++ if ( !variables.keyExists( "util" ) || isNull( variables.util ) ) { +@@ -354 +354 @@ +- if ( isNull( variables.remotingUtil ) ) { ++ if ( !variables.keyExists( "remotingUtil" ) || isNull( variables.remotingUtil ) ) { +diff --git a/tests/resources/app/coldbox/system/web/context/RequestContext.cfc b/tests/resources/app/coldbox/system/web/context/RequestContext.cfc +--- a/tests/resources/app/coldbox/system/web/context/RequestContext.cfc ++++ b/tests/resources/app/coldbox/system/web/context/RequestContext.cfc +@@ -1576 +1576 @@ +- if ( isNull( variables.privateContext.response ) ) { ++ if ( !variables.privateContext.keyExists( "response" ) || isNull( variables.privateContext.response ) ) { diff --git a/.github/workflows/cron.yml b/.github/workflows/cron.yml index 3478c53e..80f531c4 100644 --- a/.github/workflows/cron.yml +++ b/.github/workflows/cron.yml @@ -89,6 +89,9 @@ jobs: box config set modules.commandbox-dotenv.checkEnvPreServerStart=false box install ${{ matrix.coldbox }} --noSave git apply --unidiff-zero .github/patches/coldbox-full-null.patch + if [ "${{ matrix.coldbox }}" = "coldbox@^7" ]; then + git apply --unidiff-zero .github/patches/coldbox7-full-null.patch + fi - name: Start server env: diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index 6e038b00..2e0ff33d 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -77,6 +77,9 @@ jobs: box config set modules.commandbox-dotenv.checkEnvPreServerStart=false box install ${{ matrix.coldbox }} --noSave git apply --unidiff-zero .github/patches/coldbox-full-null.patch + if [ "${{ matrix.coldbox }}" = "coldbox@^7" ]; then + git apply --unidiff-zero .github/patches/coldbox7-full-null.patch + fi - name: Start server env: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 348db38f..019adceb 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -62,6 +62,9 @@ jobs: box config set modules.commandbox-dotenv.checkEnvPreServerStart=false box install ${{ matrix.coldbox }} --noSave git apply --unidiff-zero .github/patches/coldbox-full-null.patch + if [ "${{ matrix.coldbox }}" = "coldbox@^7" ]; then + git apply --unidiff-zero .github/patches/coldbox7-full-null.patch + fi - name: Start server env: From b5ee3c3391fedd2de341b5e8ec82952f3b14a245 Mon Sep 17 00:00:00 2001 From: Eric Peterson Date: Sat, 22 Aug 2026 18:35:44 -0600 Subject: [PATCH 12/13] test: patch cbjavaloader for full null support --- .github/patches/coldbox7-full-null.patch | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/patches/coldbox7-full-null.patch b/.github/patches/coldbox7-full-null.patch index 7055d4a1..0c098178 100644 --- a/.github/patches/coldbox7-full-null.patch +++ b/.github/patches/coldbox7-full-null.patch @@ -43,3 +43,9 @@ diff --git a/tests/resources/app/coldbox/system/web/context/RequestContext.cfc b @@ -1576 +1576 @@ - if ( isNull( variables.privateContext.response ) ) { + if ( !variables.privateContext.keyExists( "response" ) || isNull( variables.privateContext.response ) ) { +diff --git a/modules/str/modules/cbjavaloader/models/javaloader/JavaLoader.cfc b/modules/str/modules/cbjavaloader/models/javaloader/JavaLoader.cfc +--- a/modules/str/modules/cbjavaloader/models/javaloader/JavaLoader.cfc ++++ b/modules/str/modules/cbjavaloader/models/javaloader/JavaLoader.cfc +@@ -570 +570 @@ +- returntype="string" ++ returntype="void" From 4d4130cf866d595bb2c7c4a018ad48b2af6c1300 Mon Sep 17 00:00:00 2001 From: Eric Peterson Date: Sat, 22 Aug 2026 18:52:39 -0600 Subject: [PATCH 13/13] test: update to TestBox 7 --- box.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/box.json b/box.json index b2dcf301..e81e77df 100644 --- a/box.json +++ b/box.json @@ -35,7 +35,7 @@ }, "devDependencies":{ "coldbox":"^8.0.0", - "testbox":"^6.0.0", + "testbox":"^7.0.0", "cfcollection":"^3.6.4", "cfmigrations":"^5.0.0" },