diff --git a/.cfformat.json b/.cfformat.json index 4125a2b..443ef67 100644 --- a/.cfformat.json +++ b/.cfformat.json @@ -1,13 +1,14 @@ { "array.empty_padding": false, "array.padding": true, - "array.multiline.min_length": 40, + "array.multiline.min_length": 50, "array.multiline.element_count": 2, "array.multiline.leading_comma.padding": true, "array.multiline.leading_comma": false, "alignment.consecutive.assignments": true, "alignment.consecutive.properties": true, "alignment.consecutive.params": true, + "alignment.doc_comments" : true, "brackets.padding": true, "comment.asterisks": "align", "binary_operators.padding": true, @@ -17,22 +18,22 @@ "function_call.multiline.leading_comma.padding": true, "function_call.casing.builtin": "cfdocs", "function_call.casing.userdefined": "camel", - "function_call.multiline.element_count": 2, + "function_call.multiline.element_count": 3, "function_call.multiline.leading_comma": false, - "function_call.multiline.min_length": 40, + "function_call.multiline.min_length": 50, "function_declaration.padding": true, "function_declaration.empty_padding": false, "function_declaration.multiline.leading_comma": false, "function_declaration.multiline.leading_comma.padding": true, - "function_declaration.multiline.element_count": 2, - "function_declaration.multiline.min_length": 40, + "function_declaration.multiline.element_count": 3, + "function_declaration.multiline.min_length": 50, "function_declaration.group_to_block_spacing": "compact", "function_anonymous.empty_padding": false, "function_anonymous.group_to_block_spacing": "compact", - "function_anonymous.multiline.element_count": 2, + "function_anonymous.multiline.element_count": 3, "function_anonymous.multiline.leading_comma": false, "function_anonymous.multiline.leading_comma.padding": true, - "function_anonymous.multiline.min_length": 40, + "function_anonymous.multiline.min_length": 50, "function_anonymous.padding": true, "indent_size": 4, "keywords.block_to_keyword_spacing": "spaced", @@ -41,13 +42,13 @@ "keywords.spacing_to_block": "spaced", "keywords.spacing_to_group": true, "keywords.empty_group_spacing": false, - "max_columns": 120, + "max_columns": 115, "metadata.multiline.element_count": 3, - "metadata.multiline.min_length": 40, - "method_call.chain.multiline": 3, - "newline": "\n", + "metadata.multiline.min_length": 50, + "method_call.chain.multiline" : 3, + "newline":"\n", "property.multiline.element_count": 3, - "property.multiline.min_length": 40, + "property.multiline.min_length": 30, "parentheses.padding": true, "strings.quote": "double", "strings.attributes.quote": "double", @@ -57,6 +58,6 @@ "struct.multiline.leading_comma": false, "struct.multiline.leading_comma.padding": true, "struct.multiline.element_count": 2, - "struct.multiline.min_length": 40, + "struct.multiline.min_length": 60, "tab_indent": true -} \ No newline at end of file +} diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..8d2322c --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,229 @@ +name: mementifier CI + +# Only on Development we build snapshots +on: + push: + branches: + - development + - master + +env: + MODULE_ID: mementifier + +jobs: + ############################################# + # Tests First baby! We fail, no build :( + ############################################# + tests: + name: Tests + runs-on: ubuntu-20.04 + env: + DB_USER: root + DB_PASSWORD: root + strategy: + fail-fast: false + matrix: + cfengine: [ "lucee@5", "adobe@2016", "adobe@2018", "adobe@2021" ] + steps: + - name: Checkout Repository + uses: actions/checkout@v2 + + - name: Setup Java + uses: actions/setup-java@v2 + with: + distribution: "adopt" + java-version: "11" + + - name: Setup Database and Fixtures + run: | + sudo systemctl start mysql.service + # Create Database + mysql -u${{ env.DB_USER }} -p${{ env.DB_PASSWORD }} -e 'CREATE DATABASE mementifier;' + + - name: Setup Environment For Testing Process + working-directory: ./test-harness + run: | + # Setup .env + touch .env + # ENV + printf "DB_HOST=localhost\n" >> .env + printf "DB_USER=${{ env.DB_USER }}\n" >> .env + printf "DB_PASSWORD=${{ env.DB_PASSWORD }}\n" >> .env + printf "DB_CLASS=com.mysql.cj.jdbc.Driver\n" >> .env + printf "DB_BUNDLEVERSION=8.0.19\n" >> .env + printf "DB_BUNDLENAME=com.mysql.cj\n" >> .env + + - name: Cache CommandBox Dependencies + uses: actions/cache@v1 + if: ${{ true }} + with: + path: ~/.CommandBox/artifacts + key: ${{ runner.OS }}-commandbox-cache-${{ hashFiles( 'box.json' ) }}-${{ hashFiles( 'test-harness/box.json' ) }} + restore-keys: | + ${{ runner.OS }}-commandbox-cache-${{ hashFiles( 'box.json' ) }}-${{ hashFiles( 'test-harness/box.json' ) }} + + - name: Setup CommandBox + uses: elpete/setup-commandbox@v1.0.0 + + - name: Install Test Harness Dependencies + working-directory: ./test-harness + run: | + box install + + - name: Start ${{ matrix.cfengine }} Server + working-directory: ./test-harness + run: | + box server start serverConfigFile="server-${{ matrix.cfengine }}.json" --noSaveSettings --debug + # Install Adobe 2021 cfpm modules + if [[ "${{ matrix.cfengine }}" == "adobe@2021" ]] ; then + box run-script install:2021 + fi + curl http://127.0.0.1:60299 + + - name: Run Tests + working-directory: ./test-harness + run: | + mkdir tests/results + box package set testbox.runner="http://localhost:60299/tests/runner.cfm" + box testbox run --verbose outputFile=tests/results/test-results outputFormats=json,antjunit + + - name: Publish Test Results + uses: EnricoMi/publish-unit-test-result-action@v1 + if: always() + with: + files: test-harness/tests/results/**/*.xml + check_name: "${{ matrix.cfengine }} Test Results" + + - name: Upload Test Results Artifacts + if: always() + uses: actions/upload-artifact@v2 + with: + name: test-results-${{ matrix.cfengine }} + path: | + test-harness/tests/results/**/* + + - name: Slack Notification + if: failure() + uses: rtCamp/action-slack-notify@v2 + env: + SLACK_CHANNEL: coding + SLACK_COLOR: ${{ job.status }} # or a specific color like 'green' or '#ff00ff' + SLACK_ICON_EMOJI: ":bell:" + SLACK_MESSAGE: '${{ env.MODULE_ID }} tests failed :cry:' + SLACK_TITLE: ${{ env.MODULE_ID }} Tests For ${{ matrix.cfengine }} failed + SLACK_USERNAME: CI + SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK_URL }} + + - name: Failure Debugging Info + if: ${{ failure() }} + working-directory: ./test-harness + run: | + box server log serverConfigFile="server-${{ matrix.cfengine }}.json" + + - name: Upload Debugging Info To Artifacts + if: ${{ failure() }} + uses: actions/upload-artifact@v2 + with: + name: Failure Debugging Info - ${{ matrix.cfengine }} + path: | + test-harness/.engine/**/logs/* + test-harness/.engine/**/WEB-INF/cfusion/logs/* + + ############################################# + # Build Module + ############################################# + build: + name: Build & Publish + needs: tests + runs-on: ubuntu-20.04 + steps: + - name: Checkout Repository + uses: actions/checkout@v2 + with: + fetch-depth: 0 + + - name: Setup Java + uses: actions/setup-java@v2 + with: + distribution: "adopt" + java-version: "11" + + - name: Cache CommandBox Dependencies + uses: actions/cache@v1 + if: ${{ true }} + with: + path: ~/.CommandBox/artifacts + key: ${{ runner.OS }}-commandbox-cache-${{ hashFiles( 'box.json' ) }}-${{ hashFiles( 'test-harness/box.json' ) }} + restore-keys: | + ${{ runner.OS }}-commandbox-cache-${{ hashFiles( 'box.json' ) }}-${{ hashFiles( 'test-harness/box.json' ) }} + + - name: Setup CommandBox + uses: elpete/setup-commandbox@v1.0.0 + with: + forgeboxAPIKey: ${{ secrets.FORGEBOX_TOKEN }} + + - name: Setup Environment Variables For Build Process + id: current_version + run: | + echo "VERSION=`cat box.json | jq '.version' -r`" >> $GITHUB_ENV + box package set version=@build.version@+@build.number@ + # master or snapshot + echo "Github Ref is $GITHUB_REF" + echo "BRANCH=master" >> $GITHUB_ENV + if [ $GITHUB_REF == 'refs/heads/development' ] + then + echo "BRANCH=development" >> $GITHUB_ENV + fi + + - name: Build ${{ env.MODULE_ID }} + run: | + box install commandbox-docbox + box task run taskfile=build/Build target=run :version=${{ env.VERSION }} :projectName=${{ env.MODULE_ID }} :buildID=${{ github.run_number }} :branch=${{ env.BRANCH }} + + - name: Upload Build Artifacts + if: success() + uses: actions/upload-artifact@v2 + with: + name: ${{ env.MODULE_ID }} + path: | + .artifacts/**/* + + - name: Upload Binaries to S3 + uses: jakejarvis/s3-sync-action@master + with: + args: --acl public-read + env: + AWS_S3_BUCKET: "downloads.ortussolutions.com" + AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_ACCESS_SECRET }} + SOURCE_DIR: ".artifacts/${{ env.MODULE_ID }}" + DEST_DIR: "ortussolutions/coldbox-modules/${{ env.MODULE_ID }}" + + - name: Upload API Docs to S3 + uses: jakejarvis/s3-sync-action@master + with: + args: --acl public-read + env: + AWS_S3_BUCKET: "apidocs.ortussolutions.com" + AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_ACCESS_SECRET }} + SOURCE_DIR: ".tmp/apidocs" + DEST_DIR: "coldbox-modules/${{ env.MODULE_ID }}/${{ env.VERSION }}" + + - name: Publish To ForgeBox + run: | + cd .tmp/${{ env.MODULE_ID }} + cat box.json + box forgebox publish + + - name: Inform Slack + if: ${{ always() }} + uses: rtCamp/action-slack-notify@v2 + env: + SLACK_CHANNEL: coding + SLACK_COLOR: ${{ job.status }} # or a specific color like 'green' or '#ff00ff' + SLACK_ICON_EMOJI: ":bell:" + SLACK_MESSAGE: '${{ env.MODULE_ID }} Built with ${{ job.status }}!' + SLACK_TITLE: "${{ env.MODULE_ID }} Build" + SLACK_USERNAME: CI + SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK_URL }} diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml new file mode 100644 index 0000000..8a63900 --- /dev/null +++ b/.github/workflows/pr.yml @@ -0,0 +1,118 @@ +name: Pull Requests + +on: + push: + branches-ignore: + - "main" + - "master" + - "development" + pull_request: + branches: + - development + +jobs: + tests: + name: Tests + runs-on: ubuntu-20.04 + env: + DB_USER: root + DB_PASSWORD: root + strategy: + fail-fast: true + matrix: + cfengine: [ "lucee@5", "adobe@2016", "adobe@2018", "adobe@2021" ] + steps: + - name: Checkout Repository + uses: actions/checkout@v2 + + - name: Setup Database and Fixtures + run: | + sudo systemctl start mysql.service + mysql -u${{ env.DB_USER }} -p${{ env.DB_PASSWORD }} -e 'CREATE DATABASE mementifier;' + + - name: Setup Java + uses: actions/setup-java@v2 + with: + distribution: "adopt" + java-version: "11" + + - name: Setup CommandBox + uses: elpete/setup-commandbox@v1.0.0 + + - name: Setup Environment For Testing Process + working-directory: ./test-harness + run: | + # Setup .env + touch .env + # ENV + printf "DB_HOST=localhost\n" >> .env + printf "DB_USER=${{ env.DB_USER }}\n" >> .env + printf "DB_PASSWORD=${{ env.DB_PASSWORD }}\n" >> .env + printf "DB_CLASS=com.mysql.cj.jdbc.Driver\n" >> .env + printf "DB_BUNDLEVERSION=8.0.19\n" >> .env + printf "DB_BUNDLENAME=com.mysql.cj\n" >> .env + + - name: Install Test Harness Dependencies + working-directory: ./test-harness + run: | + box install + + - name: Start ${{ matrix.cfengine }} Server + working-directory: ./test-harness + run: | + box server start serverConfigFile="server-${{ matrix.cfengine }}.json" --noSaveSettings --debug + # Install Adobe 2021 cfpm modules + if [[ "${{ matrix.cfengine }}" == "adobe@2021" ]] ; then + box run-script install:2021 + fi + curl http://127.0.0.1:60299 + + - name: Run Tests + working-directory: ./test-harness + run: | + mkdir tests/results + box package set testbox.runner="http://localhost:60299/tests/runner.cfm" + box testbox run --verbose outputFile=tests/results/test-results outputFormats=json,antjunit + + - name: Publish PR Test Reports + uses: mikepenz/action-junit-report@v2 + with: + report_paths: 'test-harness/tests/results/**/*.xml' + check_name: "${{ matrix.cfengine }} Test Results" + summary: true + + - name: Failure Debugging Info + if: ${{ failure() }} + working-directory: ./test-harness + run: | + box server log serverConfigFile="server-${{ matrix.cfengine }}.json" + if [[ "${{ matrix.cfengine }}" == "lucee-hibernate@5" ]] ; then + box cat "`box server info name=cborm-${{ matrix.cfengine }} property=serverHomeDirectory`/WEB-INF/lucee-server/context/logs/deploy.log" + fi + + format: + name: Format + runs-on: ubuntu-20.04 + steps: + - name: Checkout Repository + uses: actions/checkout@v2 + + - name: Setup Java + uses: actions/setup-java@v2 + with: + distribution: "adopt" + java-version: "11" + + - name: Set Up CommandBox + uses: elpete/setup-commandbox@v1.0.0 + + - name: Install CFFormat + run: box install commandbox-cfformat + + - name: Run CFFormat + run: box run-script format + + - name: Commit Format Changes + uses: stefanzweifel/git-auto-commit-action@v4 + with: + commit_message: Apply cfformat changes diff --git a/box.json b/box.json index 27107b9..75e93c7 100644 --- a/box.json +++ b/box.json @@ -1,6 +1,6 @@ { "name":"Mementifier : The State Maker!", - "version":"2.7.0", + "version":"2.8.0", "location":"https://downloads.ortussolutions.com/ortussolutions/coldbox-modules/mementifier/@build.version@/mementifier-@build.version@.zip", "author":"Ortus Solutions, Corp", "homepage":"https://github.com/coldbox-modules/mementifier", @@ -31,8 +31,8 @@ ], "scripts":{ "release":"recipe build/release.boxr", - "format":"cfformat run interceptors/,models/,test-harness/handlers,test-harness/tests/specs", - "format:watch":"cfformat watch interceptors/,models/,test-harness/handlers,test-harness/tests/specs ./.cfformat.json", - "format:check":"cfformat check interceptors/,models/,test-harness/handlers,test-harness/tests/specs" + "format":"cfformat run interceptors/,models/,test-harness/**/*.cfc ./.cfformat.json --overwrite", + "format:watch":"cfformat watch interceptors/,models/,test-harness/**/*.cfc ./.cfformat.json", + "format:check":"cfformat check interceptors/,models/,test-harness/**/*.cfc ./.cfformat.json" } -} \ No newline at end of file +} diff --git a/.travis.yml b/build/.travis.yml similarity index 100% rename from .travis.yml rename to build/.travis.yml diff --git a/build/Build.cfc b/build/Build.cfc index cf74dff..ae94650 100644 --- a/build/Build.cfc +++ b/build/Build.cfc @@ -2,202 +2,228 @@ * Build process for ColdBox Modules * Adapt to your needs. */ -component{ - - /** - * Constructor - */ - function init(){ - // Setup Pathing - variables.cwd = getCWD().reReplace( "\.$", "" ); - variables.artifactsDir = cwd & "/.artifacts"; - variables.buildDir = cwd & "/.tmp"; - variables.apiDocsURL = "http://localhost:60299/apidocs/"; - variables.testRunner = "http://localhost:60299/tests/runner.cfm"; - - // Source Excludes Not Added to final binary - variables.excludes = [ - ".gitignore", - ".travis.yml", - ".artifacts", - ".tmp", - "build", - "test-harness", - ".DS_Store", - ".git" - ]; - - // Cleanup + Init Build Directories - [ variables.buildDir, variables.artifactsDir ].each( function( item ){ - if( directoryExists( item ) ){ - directoryDelete( item, true ); - } - // Create directories - directoryCreate( item, true, true ); +component { + + /** + * Constructor + */ + function init(){ + // Setup Pathing + variables.cwd = getCWD().reReplace( "\.$", "" ); + variables.artifactsDir = cwd & "/.artifacts"; + variables.buildDir = cwd & "/.tmp"; + variables.apiDocsURL = "http://localhost:60299/apidocs/"; + variables.testRunner = "http://localhost:60299/tests/runner.cfm"; + + // Source Excludes Not Added to final binary + variables.excludes = [ + "build", + "node-modules", + "resources", + "test-harness", + "(package|package-lock).json", + "webpack.config.js", + "^\..*" + ]; + + // Cleanup + Init Build Directories + [ + variables.buildDir, + variables.artifactsDir + ].each( function( item ){ + if ( directoryExists( item ) ) { + directoryDelete( item, true ); + } + // Create directories + directoryCreate( item, true, true ); } ); // Create Mappings - fileSystemUtil.createMapping( "coldbox", variables.cwd & "test-harness/coldbox" ); - - return this; - } - - /** - * Run the build process: test, build source, docs, checksums - * - * @projectName The project name used for resources and slugs - * @version The version you are building - * @buldID The build identifier - * @branch The branch you are building - */ - function run( - required projectName, - version="1.0.0", - buildID=createUUID(), - branch="development" - ){ + fileSystemUtil.createMapping( + "coldbox", + variables.cwd & "test-harness/coldbox" + ); + + return this; + } + + /** + * Run the build process: test, build source, docs, checksums + * + * @projectName The project name used for resources and slugs + * @version The version you are building + * @buldID The build identifier + * @branch The branch you are building + */ + function run( + required projectName, + version = "1.0.0", + buildID = createUUID(), + branch = "development" + ){ // Create project mapping fileSystemUtil.createMapping( arguments.projectName, variables.cwd ); - // Run the tests - runTests(); + // Build the source + buildSource( argumentCollection = arguments ); - // Build the source - buildSource( argumentCollection=arguments ); + // Build Docs + arguments.outputDir = variables.buildDir & "/apidocs"; + docs( argumentCollection = arguments ); - // Build Docs - arguments.outputDir = variables.buildDir & "/apidocs"; - docs( argumentCollection=arguments ); - - // checksums + // checksums buildChecksums(); // Build latest changelog latestChangelog(); - // Finalize Message - print.line() - .boldMagentaLine( "Build Process is done! Enjoy your build!" ) - .toConsole(); - } - - /** - * Run the test suites - */ - function runTests(){ - // Tests First, if they fail then exit - print.blueLine( "Testing the package, please wait..." ).toConsole(); - - command( 'testbox run' ) - .params( - runner = variables.testRunner, - verbose = true, - outputFile = "build/results.json" - ) - .run(); - - // Check Exit Code? - if( shell.getExitCode() ){ - return error( "Cannot continue building, tests failed!" ); - } - } - - /** - * Build the source + // Finalize Message + print + .line() + .boldMagentaLine( "Build Process is done! Enjoy your build!" ) + .toConsole(); + } + + /** + * Run the test suites + */ + function runTests(){ + // Tests First, if they fail then exit + print.blueLine( "Testing the package, please wait..." ).toConsole(); + + command( "testbox run" ) + .params( + runner = variables.testRunner, + verbose = true, + outputFile = "#variables.cwd#/test-harness/results/test-results", + outputFormats="json,antjunit" + ) + .run(); + + // Check Exit Code? + if ( shell.getExitCode() ) { + return error( "Cannot continue building, tests failed!" ); + } + } + + /** + * Build the source * * @projectName The project name used for resources and slugs - * @version The version you are building - * @buldID The build identifier - * @branch The branch you are building - */ - function buildSource( - required projectName, - version="1.0.0", - buildID=createUUID(), - branch="development" - ){ - // Build Notice ID - print.line() - .boldMagentaLine( "Building #arguments.projectName# v#arguments.version#+#arguments.buildID# from #cwd# using the #arguments.branch# branch." ) - .toConsole(); - - // Prepare exports directory - variables.exportsDir = variables.artifactsDir & "/#projectName#/#arguments.version#"; - directoryCreate( variables.exportsDir, true, true ); - - // Project Build Dir - variables.projectBuildDir = variables.buildDir & "/#projectName#"; - directoryCreate( variables.projectBuildDir, true, true ); - - // Copy source - print.blueLine( "Copying source to build folder..." ).toConsole(); - copy( variables.cwd, variables.projectBuildDir ); - - // Create build ID - fileWrite( "#variables.projectBuildDir#/#projectName#-#version#+#buildID#", "Built with love on #dateTimeFormat( now(), "full")#" ); - - // Updating Placeholders - print.greenLine( "Updating version identifier to #arguments.version#" ).toConsole(); - command( 'tokenReplace' ) - .params( - path = "/#variables.projectBuildDir#/**", - token = "@build.version@", - replacement = arguments.version - ) - .run(); - - print.greenLine( "Updating build identifier to #arguments.buildID#" ).toConsole(); - command( 'tokenReplace' ) - .params( - path = "/#variables.projectBuildDir#/**", - token = ( arguments.branch == "master" ? "@build.number@" : "+@build.number@" ), - replacement = ( arguments.branch == "master" ? arguments.buildID : "-snapshot" ) - ) - .run(); - - // zip up source - var destination = "#variables.exportsDir#/#projectName#-#version#.zip"; - print.greenLine( "Zipping code to #destination#" ).toConsole(); - cfzip( - action="zip", - file="#destination#", - source="#variables.projectBuildDir#", - overwrite=true, - recurse=true - ); - - // Copy box.json for convenience - fileCopy( "#variables.projectBuildDir#/box.json", variables.exportsDir ); - } - - /** - * Produce the API Docs - */ - function docs( required projectName, version="1.0.0", outputDir=".tmp/apidocs" ){ - // Generate Docs - print.greenLine( "Generating API Docs, please wait..." ).toConsole(); - directoryCreate( arguments.outputDir, true, true ); - - command( 'docbox generate' ) - .params( - "source" = "models", - "mapping" = "models", - "strategy-projectTitle" = "#arguments.projectName# v#arguments.version#", - "strategy-outputDir" = arguments.outputDir - ) - .run(); - - print.greenLine( "API Docs produced at #arguments.outputDir#" ).toConsole(); - - var destination = "#variables.exportsDir#/#projectName#-docs-#version#.zip"; - print.greenLine( "Zipping apidocs to #destination#" ).toConsole(); - cfzip( - action="zip", - file="#destination#", - source="#arguments.outputDir#", - overwrite=true, - recurse=true - ); + * @version The version you are building + * @buldID The build identifier + * @branch The branch you are building + */ + function buildSource( + required projectName, + version = "1.0.0", + buildID = createUUID(), + branch = "development" + ){ + // Build Notice ID + print + .line() + .boldMagentaLine( + "Building #arguments.projectName# v#arguments.version#+#arguments.buildID# from #cwd# using the #arguments.branch# branch." + ) + .toConsole(); + + // Prepare exports directory + variables.exportsDir = variables.artifactsDir & "/#projectName#/#arguments.version#"; + directoryCreate( variables.exportsDir, true, true ); + + // Project Build Dir + variables.projectBuildDir = variables.buildDir & "/#projectName#"; + directoryCreate( + variables.projectBuildDir, + true, + true + ); + + // Copy source + print.blueLine( "Copying source to build folder..." ).toConsole(); + copy( + variables.cwd, + variables.projectBuildDir + ); + + // Create build ID + fileWrite( + "#variables.projectBuildDir#/#projectName#-#version#+#buildID#", + "Built with love on #dateTimeFormat( now(), "full" )#" + ); + + // Updating Placeholders + print.greenLine( "Updating version identifier to #arguments.version#" ).toConsole(); + command( "tokenReplace" ) + .params( + path = "/#variables.projectBuildDir#/**", + token = "@build.version@", + replacement = arguments.version + ) + .run(); + + print.greenLine( "Updating build identifier to #arguments.buildID#" ).toConsole(); + command( "tokenReplace" ) + .params( + path = "/#variables.projectBuildDir#/**", + token = ( arguments.branch == "master" ? "@build.number@" : "+@build.number@" ), + replacement = ( arguments.branch == "master" ? arguments.buildID : "-snapshot" ) + ) + .run(); + + // zip up source + var destination = "#variables.exportsDir#/#projectName#-#version#.zip"; + print.greenLine( "Zipping code to #destination#" ).toConsole(); + cfzip( + action = "zip", + file = "#destination#", + source = "#variables.projectBuildDir#", + overwrite = true, + recurse = true + ); + + // Copy box.json for convenience + fileCopy( + "#variables.projectBuildDir#/box.json", + variables.exportsDir + ); + } + + /** + * Produce the API Docs + */ + function docs( + required projectName, + version = "1.0.0", + outputDir = ".tmp/apidocs" + ){ + // 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( + "source" = "models", + "mapping" = "models", + "strategy-projectTitle" = "#arguments.projectName# v#arguments.version#", + "strategy-outputDir" = arguments.outputDir + ) + .run(); + + print.greenLine( "API Docs produced at #arguments.outputDir#" ).toConsole(); + + var destination = "#variables.exportsDir#/#projectName#-docs-#version#.zip"; + print.greenLine( "Zipping apidocs to #destination#" ).toConsole(); + cfzip( + action = "zip", + file = "#destination#", + source = "#arguments.outputDir#", + overwrite = true, + recurse = true + ); } /** @@ -206,13 +232,13 @@ component{ function latestChangelog(){ print.blueLine( "Building latest changelog..." ).toConsole(); - if( !fileExists( variables.cwd & "changelog.md" ) ){ + if ( !fileExists( variables.cwd & "changelog.md" ) ) { return error( "Cannot continue building, changelog.md file doesn't exist!" ); } fileWrite( variables.cwd & "changelog-latest.md", - fileRead( variables.cwd & 'changelog.md' ).split( '----' )[2].trim() & chr( 13 ) & chr( 10 ) + fileRead( variables.cwd & "changelog.md" ).split( "----" )[ 2 ].trim() & chr( 13 ) & chr( 10 ) ); print @@ -221,52 +247,70 @@ component{ .line( fileRead( variables.cwd & "changelog-latest.md" ) ); } - /********************************************* PRIVATE HELPERS *********************************************/ - - /** - * Build Checksums - */ - private function buildChecksums(){ - print.greenLine( "Building checksums" ).toConsole(); - command( 'checksum' ) - .params( path = '#variables.exportsDir#/*.zip', algorithm = 'SHA-512', extension="sha512", write=true ) - .run(); - command( 'checksum' ) - .params( path = '#variables.exportsDir#/*.zip', algorithm = 'md5', extension="md5", write=true ) - .run(); - } - - /** - * DirectoryCopy is broken in lucee - */ - private function copy( src, target, recurse=true ){ - // process paths with excludes - directoryList( src, false, "path", function( path ){ - var isExcluded = false; - variables.excludes.each( function( item ){ - if( path.replaceNoCase( variables.cwd, "", "all" ).findNoCase( item ) ){ - isExcluded = true; - } - } ); - return !isExcluded; - }).each( function( item ){ - // Copy to target - if( fileExists( item ) ){ - print.blueLine( "Copying #item#" ).toConsole(); - fileCopy( item, target ); - } else { - print.greenLine( "Copying directory #item#" ).toConsole(); - directoryCopy( item, target & "/" & item.replace( src, "" ), true ); - } - } ); - } - - /** + /********************************************* PRIVATE HELPERS *********************************************/ + + /** + * Build Checksums + */ + private function buildChecksums(){ + print.greenLine( "Building checksums" ).toConsole(); + command( "checksum" ) + .params( + path = "#variables.exportsDir#/*.zip", + algorithm = "SHA-512", + extension = "sha512", + write = true + ) + .run(); + command( "checksum" ) + .params( + path = "#variables.exportsDir#/*.zip", + algorithm = "md5", + extension = "md5", + write = true + ) + .run(); + } + + /** + * DirectoryCopy is broken in lucee + */ + private function copy( src, target, recurse = true ){ + // process paths with excludes + directoryList( + src, + false, + "path", + function( path ){ + var isExcluded = false; + variables.excludes.each( function( item ){ + if ( path.replaceNoCase( variables.cwd, "", "all" ).reFindNoCase( item ) ) { + isExcluded = true; + } + } ); + return !isExcluded; + } + ).each( function( item ){ + // Copy to target + if ( fileExists( item ) ) { + print.blueLine( "Copying #item#" ).toConsole(); + fileCopy( item, target ); + } else { + print.greenLine( "Copying directory #item#" ).toConsole(); + directoryCopy( + item, + target & "/" & item.replace( src, "" ), + true + ); + } + } ); + } + + /** * Gets the last Exit code to be used **/ - private function getExitCode() { - return (createObject( 'java', 'java.lang.System' ).getProperty( 'cfml.cli.exitCode' ) ?: 0); - + private function getExitCode(){ + return ( createObject( "java", "java.lang.System" ).getProperty( "cfml.cli.exitCode" ) ?: 0 ); } -} \ No newline at end of file +} diff --git a/build/release.boxr b/build/release.boxr index e216f22..d0ba4f7 100755 --- a/build/release.boxr +++ b/build/release.boxr @@ -22,4 +22,4 @@ # Bump to prepare for a new release, do minor, change if needed and don't tag bump --minor --!tagVersion !git commit -a -m "version bump" -!git push origin development \ No newline at end of file +!git push origin development diff --git a/changelog.md b/changelog.md index 3e37f7c..436a985 100644 --- a/changelog.md +++ b/changelog.md @@ -7,6 +7,20 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ---- +## [2.8.0] => 2022-JAN-11 + +### Added + +* Migration to github actions +* CFFormatting Rules + +### Fixed + +* Composite keys and no default includes fails (https://github.com/coldbox-modules/mementifier/pull/24) +* Fix mappers for keys not in memento (https://github.com/coldbox-modules/mementifier/pull/23) + +---- + ## [2.7.0] => 2021-JUN-07 ### Added @@ -59,7 +73,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added * Thanks to @elpete you can now add date/time formatting rules at the `getmemento()` level and the `this.memento` level. Please see the [readme](readme.md) for further information. - + ---- ## [2.2.1] => 2020-NOV-06 @@ -67,7 +81,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed * Reverted missing `nestedIncludes.len()` for ignore defaults on nested hierarchies. - + ---- ## [2.2.0] => 2020-NOV-05 @@ -186,4 +200,4 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [1.0.0] -* First iteration of this module \ No newline at end of file +* First iteration of this module diff --git a/interceptors/Mementifier.cfc b/interceptors/Mementifier.cfc index 0b8a435..953703f 100644 --- a/interceptors/Mementifier.cfc +++ b/interceptors/Mementifier.cfc @@ -76,26 +76,18 @@ component { } // Inject helper methods - arguments.entity.$injectMixin( - "$buildNestedMementoList", - variables.$buildNestedMementoList - ); - arguments.entity.$injectMixin( - "$buildNestedMementoStruct", - variables.$buildNestedMementoStruct - ); + arguments.entity.$injectMixin( "$buildNestedMementoList", variables.$buildNestedMementoList ); + arguments.entity.$injectMixin( "$buildNestedMementoStruct", variables.$buildNestedMementoStruct ); arguments.entity.$injectMixin( "$getDeepProperties", variables.$getDeepProperties ); // We do simple date formatters as they are faster than CFML methods var dateMask = isNull( this.memento.dateMask ) ? variables.settings.dateMask : this.memento.dateMask; var timeMask = isNull( this.memento.timeMask ) ? variables.settings.timeMask : this.memento.timeMask; - arguments.entity.$FORMATTER_ISO8601 = createObject( - "java", - "java.text.SimpleDateFormat" - ).init( "yyyy-MM-dd'T'HH:mm:ssXXX" ); - arguments.entity.$FORMATTER_CUSTOM = createObject( - "java", - "java.text.SimpleDateFormat" - ).init( "#dateMask# #timeMask#" ); + arguments.entity.$FORMATTER_ISO8601 = createObject( "java", "java.text.SimpleDateFormat" ).init( + "yyyy-MM-dd'T'HH:mm:ssXXX" + ); + arguments.entity.$FORMATTER_CUSTOM = createObject( "java", "java.text.SimpleDateFormat" ).init( + "#dateMask# #timeMask#" + ); // Do we set timezones? if ( len( variables.settings.convertToTimezone ) ) { var tz = createObject( "java", "java.util.TimeZone" ).getTimeZone( @@ -110,16 +102,16 @@ component { /** * Construct a memento representation from an entity according to includes and exclude lists * - * @includes The properties array or list to build the memento with alongside the default includes - * @excludes The properties array or list to exclude from the memento alongside the default excludes - * @mappers A struct of key-function pairs that will map properties to closures/lambadas to process the item value. The closure will transform the item value. - * @defaults A struct of key-value pairs that denotes the default values for properties if they are null, defaults for everything are a blank string. + * @includes The properties array or list to build the memento with alongside the default includes + * @excludes The properties array or list to exclude from the memento alongside the default excludes + * @mappers A struct of key-function pairs that will map properties to closures/lambadas to process the item value. The closure will transform the item value. + * @defaults A struct of key-value pairs that denotes the default values for properties if they are null, defaults for everything are a blank string. * @ignoreDefaults If set to true, default includes and excludes will be ignored and only the incoming `includes` and `excludes` list will be used. * @trustedGetters If set to true, getters will not be checked for in the `this` scope before trying to invoke them. - * @iso8601Format If set to true, will use the ISO 8601 standard for formatting dates - * @dateMask The date mask to use when formatting datetimes. Only used if iso8601Format is false. - * @timeMask The time mask to use when formatting datetimes. Only used if iso8601Format is false. - * @profile The profile to use instead of the defaults + * @iso8601Format If set to true, will use the ISO 8601 standard for formatting dates + * @dateMask The date mask to use when formatting datetimes. Only used if iso8601Format is false. + * @timeMask The time mask to use when formatting datetimes. Only used if iso8601Format is false. + * @profile The profile to use instead of the defaults */ struct function getMemento( includes = "", @@ -222,7 +214,7 @@ component { // Append primary keys if ( entityMd.hasIdentifierProperty() ) { arrayAppend( thisMemento.defaultIncludes, entityMd.getIdentifierPropertyName() ); - } else if ( thisMemento.defaultIncludes.getIdentifierType().isComponentType() ) { + } else if ( entityMd.getIdentifierType().isComponentType() ) { arrayAppend( thisMemento.defaultIncludes, listToArray( arrayToList( entityMd.getIdentifierType().getPropertyNames() ) ), @@ -309,16 +301,14 @@ component { } } // If the key doesn't exist and there is no mapper for the item, go to the next item. - } else if ( !structKeyExists( arguments.mappers, item ) ) { + } else if ( !structKeyExists( thisMemento.mappers, item ) ) { continue; } // Verify Nullness thisValue = isNull( thisValue ) ? ( arrayContainsNoCase( thisMemento.defaults.keyArray(), item ) ? ( - isNull( thisMemento.defaults[ item ] ) ? javacast( "null", "" ) : thisMemento.defaults[ - item - ] + isNull( thisMemento.defaults[ item ] ) ? javacast( "null", "" ) : thisMemento.defaults[ item ] ) : variables.$mementifierSettings.nullDefaultValue ) : thisValue; @@ -341,9 +331,7 @@ component { // Iso Date? if ( arguments.iso8601Format ) { // we need to convert trailing Zulu time designations offset or JS libs like Moment will not know how to parse it - result[ thisAlias ] = this.$FORMATTER_ISO8601 - .format( thisValue ) - .replace( "Z", "+00:00" ); + result[ thisAlias ] = this.$FORMATTER_ISO8601.format( thisValue ).replace( "Z", "+00:00" ); } else { result[ thisAlias ] = customDateFormatter.format( thisValue ); } @@ -436,10 +424,9 @@ component { result[ item ] = thisMapper( result[ item ], result ); } else { // Check for null values - result[ item ] = ( !result.keyExists( item ) || isNull( result[ item ] ) ) ? javacast( - "null", - "" - ) : result[ item ]; + result[ item ] = ( !result.keyExists( item ) || isNull( result[ item ] ) ) ? javacast( "null", "" ) : result[ + item + ]; } } @@ -458,10 +445,7 @@ component { function $buildNestedMementoList( required list, required root ){ return arguments.list .filter( function( target ){ - return listFirst( arguments.target, "." ) == root && listLen( - arguments.target, - "." - ) > 1; + return listFirst( arguments.target, "." ) == root && listLen( arguments.target, "." ) > 1; } ) .map( function( target ){ return listDeleteAt( arguments.target, 1, "." ); @@ -480,7 +464,7 @@ component { * Build a new memento mappers/defaults struct using the target list and a property root * * @struct The struct to use for construction - * @root The root to filter out + * @root The root to filter out * * @return A struct of the new hiearchy to use */ diff --git a/models/ResultsMapper.cfc b/models/ResultsMapper.cfc index c9162e8..9735000 100644 --- a/models/ResultsMapper.cfc +++ b/models/ResultsMapper.cfc @@ -4,10 +4,10 @@ * *
  * data = {
- * 	"results" = [],
- * 	"resultsMap" = {
+ * "results" = [],
+ * "resultsMap" = {
  *
- * 	}
+ * }
  * }
  * 
*/ @@ -24,12 +24,12 @@ component singleton { * Construct a memento representation using a results map. This process will iterate over the collection and create a * results array with all the identifiers and a struct keyed by identifier of the mememnto data. * - * @collection The target collection - * @id The identifier key, defaults to `id` for simplicity. - * @includes The properties array or list to build the memento with alongside the default includes - * @excludes The properties array or list to exclude from the memento alongside the default excludes - * @mappers A struct of key-function pairs that will map properties to closures/lambadas to process the item value. The closure will transform the item value. - * @defaults A struct of key-value pairs that denotes the default values for properties if they are null, defaults for everything are a blank string. + * @collection The target collection + * @id The identifier key, defaults to `id` for simplicity. + * @includes The properties array or list to build the memento with alongside the default includes + * @excludes The properties array or list to exclude from the memento alongside the default excludes + * @mappers A struct of key-function pairs that will map properties to closures/lambadas to process the item value. The closure will transform the item value. + * @defaults A struct of key-value pairs that denotes the default values for properties if they are null, defaults for everything are a blank string. * @ignoreDefaults If set to true, default includes and excludes will be ignored and only the incoming `includes` and `excludes` list will be used. * @trustedGetters If set to true, getters will not be checked for in the `this` scope before trying to invoke them. * diff --git a/test-harness/.cfconfig.json b/test-harness/.cfconfig.json index 131e366..da33319 100644 --- a/test-harness/.cfconfig.json +++ b/test-harness/.cfconfig.json @@ -12,19 +12,21 @@ "allowSelect":true, "allowUpdate":true, "blob":"false", - "class":"com.mysql.jdbc.Driver", + "bundleName": "${DB_BUNDLENAME}", + "bundleVersion": "${DB_BUNDLEVERSION}", + "class":"${DB_CLASS}", "clob":"false", "connectionTimeout":"1", - "custom":"useUnicode=true&characterEncoding=UTF-8&useLegacyDatetimeCode=true&useSSL=false", + "custom":"useUnicode=true&characterEncoding=UTF8&serverTimezone=America%2FChicago&useLegacyDatetimeCode=true&autoReconnect=true&useSSL=false&allowPublicKeyRetrieval=true", "database":"mementifier", "dbdriver":"MySQL", "dsn":"jdbc:mysql://{host}:{port}/{database}", - "host":"${DB_HOST}", + "host":"${DB_HOST:127.0.0.1}", "metaCacheTimeout":"60000", - "password":"${DB_PASSWORD}", - "port":"3306", + "password": "${DB_PASSWORD}", + "port": "${DB_PORT:3306}", "storage":"false", - "username":"${DB_USERNAME}", + "username": "${DB_USER:root}", "validate":"false" } }, @@ -35,4 +37,4 @@ "requestTimeout":"0,0,0,90", "robustExceptionEnabled":true, "whitespaceManagement":"white-space-pref" -} \ No newline at end of file +} diff --git a/test-harness/.cflintrc b/test-harness/.cflintrc deleted file mode 100644 index 9e26dfe..0000000 --- a/test-harness/.cflintrc +++ /dev/null @@ -1 +0,0 @@ -{} \ No newline at end of file diff --git a/test-harness/Application.cfc b/test-harness/Application.cfc index f433f56..7b33000 100644 --- a/test-harness/Application.cfc +++ b/test-harness/Application.cfc @@ -1,80 +1,102 @@ /** -******************************************************************************** -Copyright 2005-2007 ColdBox Framework by Luis Majano and Ortus Solutions, Corp -www.ortussolutions.com -******************************************************************************** -*/ -component{ + * ******************************************************************************* + * ******************************************************************************* + */ +component { // UPDATE THE NAME OF THE MODULE IN TESTING BELOW request.MODULE_NAME = "mementifier"; + request.MODULE_PATH = "mementifier"; // Application properties this.name = hash( getCurrentTemplatePath() ); this.sessionManagement = true; - this.sessionTimeout = createTimeSpan(0,0,15,0); - this.setClientCookies = true; + this.sessionTimeout = createTimespan( 0, 0, 15, 0 ); + this.setClientCookies = true; - /************************************** - LUCEE Specific Settings - **************************************/ + /************************************** + **************************************/ // buffer the output of a tag/function body to output in case of a exception - this.bufferOutput = true; + this.bufferOutput = true; // Activate Gzip Compression - this.compression = false; + this.compression = false; // Turn on/off white space managemetn - this.whiteSpaceManagement = "smart"; + this.whiteSpaceManagement = "smart"; // Turn on/off remote cfc content whitespace this.suppressRemoteComponentContent = false; // COLDBOX STATIC PROPERTY, DO NOT CHANGE UNLESS THIS IS NOT THE ROOT OF YOUR COLDBOX APP - COLDBOX_APP_ROOT_PATH = getDirectoryFromPath( getCurrentTemplatePath() ); + COLDBOX_APP_ROOT_PATH = getDirectoryFromPath( getCurrentTemplatePath() ); // The web server mapping to this application. Used for remote purposes or static purposes - COLDBOX_APP_MAPPING = ""; + COLDBOX_APP_MAPPING = ""; // COLDBOX PROPERTIES - COLDBOX_CONFIG_FILE = ""; + COLDBOX_CONFIG_FILE = ""; // COLDBOX APPLICATION KEY OVERRIDE - COLDBOX_APP_KEY = ""; + COLDBOX_APP_KEY = ""; - // Mappings - this.mappings[ "/root" ] = COLDBOX_APP_ROOT_PATH; + // Mappings + this.mappings[ "/root" ] = COLDBOX_APP_ROOT_PATH; this.mappings[ "/cborm" ] = COLDBOX_APP_ROOT_PATH & "/modules/cborm"; // Map back to its root - moduleRootPath = REReplaceNoCase( this.mappings[ "/root" ], "#request.MODULE_NAME#(\\|/)test-harness(\\|/)", "" ); - modulePath = REReplaceNoCase( this.mappings[ "/root" ], "test-harness(\\|/)", "" ); + moduleRootPath = reReplaceNoCase( + this.mappings[ "/root" ], + "#request.MODULE_NAME#(\\|/)test-harness(\\|/)", + "" + ); + modulePath = reReplaceNoCase( + this.mappings[ "/root" ], + "test-harness(\\|/)", + "" + ); // Module Root + Path Mappings - this.mappings[ "/moduleroot" ] = moduleRootPath; + this.mappings[ "/moduleroot" ] = moduleRootPath; this.mappings[ "/#request.MODULE_NAME#" ] = modulePath; // ORM definitions: ENABLE IF NEEDED this.datasource = "mementifier"; this.ormEnabled = "true"; + this.ormSettings = { - cfclocation = [ "models" ], - logSQL = true, - dbcreate = "dropcreate", - flushAtRequestEnd = false, - eventhandling = true, - eventHandler = "cborm.models.EventHandler", - skipcfcWithError = false + cfclocation : [ "models" ], + logSQL : true, + dbcreate : "update", + dialect : "org.hibernate.dialect.MySQL5InnoDBDialect", + secondarycacheenabled : false, + cacheProvider : "ehcache", + automanageSession : false, + flushAtRequestEnd : false, + eventhandling : true, + eventHandler : "cborm.models.EventHandler", + skipcfcWithError : false }; - //applicationstop();abort; - //ormReload(); + // applicationstop();abort; + // ormReload(); // application start public boolean function onApplicationStart(){ - application.cbBootstrap = new coldbox.system.Bootstrap( COLDBOX_CONFIG_FILE, COLDBOX_APP_ROOT_PATH, COLDBOX_APP_KEY, COLDBOX_APP_MAPPING ); + application.cbBootstrap = new coldbox.system.Bootstrap( + COLDBOX_CONFIG_FILE, + COLDBOX_APP_ROOT_PATH, + COLDBOX_APP_KEY, + COLDBOX_APP_MAPPING + ); application.cbBootstrap.loadColdbox(); return true; } // request start - public boolean function onRequestStart(String targetPage){ + public boolean function onRequestStart( String targetPage ){ + if ( !structKeyExists( application, "cbBootstrap" ) ) { + onApplicationStart(); + } - if( structKeyExists( url, "fwreinit" ) ){ + if ( url.keyExists( "fwreinit" ) ) { + if ( server.keyExists( "lucee" ) ) { + pagePoolClear(); + } ormReload(); } @@ -89,11 +111,11 @@ component{ } public void function onSessionEnd( struct sessionScope, struct appScope ){ - arguments.appScope.cbBootStrap.onSessionEnd( argumentCollection=arguments ); + arguments.appScope.cbBootStrap.onSessionEnd( argumentCollection = arguments ); } public boolean function onMissingTemplate( template ){ - return application.cbBootstrap.onMissingTemplate( argumentCollection=arguments ); + return application.cbBootstrap.onMissingTemplate( argumentCollection = arguments ); } -} \ No newline at end of file +} diff --git a/test-harness/box.json b/test-harness/box.json index 40587e0..8b6844d 100644 --- a/test-harness/box.json +++ b/test-harness/box.json @@ -9,8 +9,8 @@ "cborm":"^3.0.0" }, "devDependencies":{ - "testbox":"^4.0.0", - "mockdatacfc":"^3.1.0+13" + "testbox":"*", + "mockdatacfc":"*" }, "installPaths":{ "coldbox":"coldbox/", @@ -22,6 +22,10 @@ "runner":"http://localhost:60299/tests/runner.cfm" }, "scripts":{ - "cleanMementifier":"rm modules/cborm/modules/mementifier --force --recurse" + "postInstall":"run-script cleanMementifier && echo 'Mementifier Cleaned'", + "cleanMementifier":"rm modules/cborm/modules/mementifier --force --recurse", + "cfpm":"echo '\".engine/adobe2021/WEB-INF/cfusion/bin/cfpm.sh\"' | run", + "cfpm:install":"echo '\".engine/adobe2021/WEB-INF/cfusion/bin/cfpm.sh\" install ${1}' | run", + "install:2021":"run-script cfpm:install zip,debugger,orm,mysql,feed" } -} \ No newline at end of file +} diff --git a/test-harness/config/Coldbox.cfc b/test-harness/config/Coldbox.cfc index c24102e..061e363 100644 --- a/test-harness/config/Coldbox.cfc +++ b/test-harness/config/Coldbox.cfc @@ -56,11 +56,14 @@ logBox = { // Define Appenders appenders = { - files={class="coldbox.system.logging.appenders.RollingFileAppender", - properties = { - filename = "tester", filePath="/#appMapping#/logs" + files : { + class : "coldbox.system.logging.appenders.RollingFileAppender", + properties : { + filename : "tester", + filePath : "/#appMapping#/logs" } - } + }, + console : { class : "coldbox.system.logging.appenders.ConsoleAppender" } }, // Root Logger root = { levelmax="DEBUG", appenders="*" }, @@ -78,4 +81,4 @@ ); } -} \ No newline at end of file +} diff --git a/test-harness/config/Routes.cfm b/test-harness/config/Routes.cfm deleted file mode 100644 index a304d25..0000000 --- a/test-harness/config/Routes.cfm +++ /dev/null @@ -1,46 +0,0 @@ - - // Allow unique URL or combination of URLs, we recommend both enabled - setUniqueURLS(false); - // Auto reload configuration, true in dev makes sense to reload the routes on every request - //setAutoReload(false); - // Sets automatic route extension detection and places the extension in the rc.format variable - // setExtensionDetection(true); - // The valid extensions this interceptor will detect - // setValidExtensions('xml,json,jsont,rss,html,htm'); - // If enabled, the interceptor will throw a 406 exception that an invalid format was detected or just ignore it - // setThrowOnInvalidExtension(true); - - // Base URL - if( len(getSetting('AppMapping') ) lte 1){ - setBaseURL("http://#cgi.HTTP_HOST#/"); - } - else{ - setBaseURL("http://#cgi.HTTP_HOST#/#getSetting('AppMapping')#/"); - } - - // Your Application Routes - addRoute(pattern=":handler/:action?"); - - - /** Developers can modify the CGI.PATH_INFO value in advance of the SES - interceptor to do all sorts of manipulations in advance of route - detection. If provided, this function will be called by the SES - interceptor instead of referencing the value CGI.PATH_INFO. - - This is a great place to perform custom manipulations to fix systemic - URL issues your Web site may have or simplify routes for i18n sites. - - @Event The ColdBox RequestContext Object - **/ - function PathInfoProvider(Event){ - /* Example: - var URI = CGI.PATH_INFO; - if (URI eq "api/foo/bar") - { - Event.setProxyRequest(true); - return "some/other/value/for/your/routes"; - } - */ - return CGI.PATH_INFO; - } - \ No newline at end of file diff --git a/test-harness/handlers/Main.cfc b/test-harness/handlers/Main.cfc index b05bbba..94dfa12 100644 --- a/test-harness/handlers/Main.cfc +++ b/test-harness/handlers/Main.cfc @@ -1,126 +1,119 @@ -component{ +component { - property name="userService" inject="entityService:User"; - property name="roleService" inject="entityService:Role"; - property name="settingService" inject="entityService:Setting"; + property name="userService" inject="entityService:User"; + property name="roleService" inject="entityService:Role"; + property name="settingService" inject="entityService:Setting"; property name="permissionService" inject="entityService:Permission"; function index( event, rc, prc ){ - param rc.ignoreDefaults = false; param rc.ignoredRoots = ""; param rc.includes = ""; param rc.excludes = ""; var mockData = { - fname = "testuser", - lname = "testuser", - email = "testuser@testuser.com", - username = "testuser", - isConfirmed = true, - isActive = true, - otherURL = "www.luismajano.com" + fname : "testuser", + lname : "testuser", + email : "testuser@testuser.com", + username : "testuser", + isConfirmed : true, + isActive : true, + otherURL : "www.luismajano.com" }; - - var oUser = populateModel( - model = userService.new(), - memento = mockData, - composeRelationships= true + model = userService.new(), + memento = mockData, + composeRelationships = true ); - oUser.setRole( - roleService.new( { - role ="Admin", - description="Awesome Admin" - } ) - ); - oUser.getRole().setPermissions( [ - permissionService.new( { permission="READ", description="read" } ), - permissionService.new( { permission="WRITE", description="write" } ) - ] ); + oUser.setRole( roleService.new( { role : "Admin", description : "Awesome Admin" } ) ); + oUser + .getRole() + .setPermissions( [ + permissionService.new( { permission : "READ", description : "read" } ), + permissionService.new( { permission : "WRITE", description : "write" } ) + ] ); oUser.setPermissions( [ - permissionService.new( { permission="CUSTOM_READ", description="read" } ), - permissionService.new( { permission="CUSTOM_WRITE", description="write" } ) + permissionService.new( { permission : "CUSTOM_READ", description : "read" } ), + permissionService.new( { permission : "CUSTOM_WRITE", description : "write" } ) ] ); - oUser.setSettings( - [ getNewSetting(), getNewSetting(), getNewSetting(), getNewSetting(), getNewSetting() ] - ); + oUser.setSettings( [ + getNewSetting(), + getNewSetting(), + getNewSetting(), + getNewSetting(), + getNewSetting() + ] ); var result = oUser.getMemento( includes = rc.includes, excludes = rc.excludes, ignoreDefaults = rc.ignoreDefaults, ignoredRoots = rc.ignoredRoots, - trustedGetters = event.valueExists( "trustedGetters" ) ? - rc.trustedGetters : - javacast( "null", "" ), - mappers = { - lname = function( item ){ return item.ucase(); }, - "foo" = function( _, memento ) { - return memento.firstName & " " & memento.lastName; - } + trustedGetters = event.valueExists( "trustedGetters" ) ? rc.trustedGetters : javacast( "null", "" ), + mappers = { + lname : function( item ){ + return item.ucase(); + }, + "foo" : function( _, memento ){ + return memento.firstName & " " & memento.lastName; + } }, - iso8601Format = event.valueExists( "iso8601Format" ) ? - rc.iso8601Format : - javacast( "null", "" ), - dateMask = event.valueExists( "dateMask" ) ? - rc.dateMask : - javacast( "null", "" ), - timeMask = event.valueExists( "timeMask" ) ? - rc.timeMask : - javacast( "null", "" ) + iso8601Format = event.valueExists( "iso8601Format" ) ? rc.iso8601Format : javacast( "null", "" ), + dateMask = event.valueExists( "dateMask" ) ? rc.dateMask : javacast( "null", "" ), + timeMask = event.valueExists( "timeMask" ) ? rc.timeMask : javacast( "null", "" ) ); return result; } - function nested( event, rc, prc ) { + function nested( event, rc, prc ){ var mockData = { - fname = "testuser", - lname = "testuser", - email = "testuser@testuser.com", - username = "testuser", - isConfirmed = true, - isActive = true, - otherURL = "www.luismajano.com" + fname : "testuser", + lname : "testuser", + email : "testuser@testuser.com", + username : "testuser", + isConfirmed : true, + isActive : true, + otherURL : "www.luismajano.com" }; var oUser = populateModel( - model = userService.new(), - memento = mockData, - composeRelationships= true + model = userService.new(), + memento = mockData, + composeRelationships = true ); - oUser.setRole( - roleService.new( { - role ="Admin", - description="Awesome Admin" - } ) - ); - oUser.getRole().setPermissions( [ - permissionService.new( { permission="READ", description="read" } ), - permissionService.new( { permission="WRITE", description="write" } ) - ] ); + oUser.setRole( roleService.new( { role : "Admin", description : "Awesome Admin" } ) ); + oUser + .getRole() + .setPermissions( [ + permissionService.new( { permission : "READ", description : "read" } ), + permissionService.new( { permission : "WRITE", description : "write" } ) + ] ); oUser.setPermissions( [ - permissionService.new( { permission="CUSTOM_READ", description="read" } ), - permissionService.new( { permission="CUSTOM_WRITE", description="write" } ) + permissionService.new( { permission : "CUSTOM_READ", description : "read" } ), + permissionService.new( { permission : "CUSTOM_WRITE", description : "write" } ) ] ); - oUser.setSettings( - [ getNewSetting(), getNewSetting(), getNewSetting(), getNewSetting(), getNewSetting() ] - ); + oUser.setSettings( [ + getNewSetting(), + getNewSetting(), + getNewSetting(), + getNewSetting(), + getNewSetting() + ] ); var result = oUser.getMemento( - includes = [ "role.permissions", "role.userId" ], - mappers = { - "description": function( item, memento ) { - throw( "Should not be called" ); - }, - "role.permissions.description": function( item ) { - return uCase( item ); - } - } + includes = [ "role.permissions", "role.userId" ], + mappers = { + "description" : function( item, memento ){ + throw( "Should not be called" ); + }, + "role.permissions.description" : function( item ){ + return uCase( item ); + } + } ); return result; @@ -128,9 +121,9 @@ private function getNewSetting(){ return settingService.new( { - name : "setting-#createUUID()#", - description:"Hola!!!", - isConfirmed: randRange( 0, 1 ) + name : "setting-#createUUID()#", + description : "Hola!!!", + isConfirmed : randRange( 0, 1 ) } ); } @@ -150,14 +143,13 @@ // Build out objects .map( function( item ){ return populateModel( - model = userService.new(), - memento = item, - composeRelationships= true + model = userService.new(), + memento = item, + composeRelationships = true ); } ); - return getInstance( "ResultsMapper@mementifier" ) - .process( aObjects, "userId" ); + return getInstance( "ResultsMapper@mementifier" ).process( aObjects, "userId" ); } function alreadySerialized( event, rc, prc ){ @@ -166,27 +158,20 @@ param rc.excludes = ""; var mockData = { - fname = "testuser", - lname = "testuser", - email = "testuser@testuser.com", - username = "testuser", - isConfirmed = true, - isActive = true, - otherURL = "www.luismajano.com", - alreadySerialized = [ - { - 'foo' = 'bar' - }, - { - 'baz' = 'frobozz' - } - ] + fname : "testuser", + lname : "testuser", + email : "testuser@testuser.com", + username : "testuser", + isConfirmed : true, + isActive : true, + otherURL : "www.luismajano.com", + alreadySerialized : [ { "foo" : "bar" }, { "baz" : "frobozz" } ] }; var oUser = populateModel( - model = userService.new(), - memento = mockData, - composeRelationships= true + model = userService.new(), + memento = mockData, + composeRelationships = true ); return oUser.getMemento( diff --git a/test-harness/models/BaseEntity.cfc b/test-harness/models/BaseEntity.cfc index 34ce7f2..f7ba600 100644 --- a/test-harness/models/BaseEntity.cfc +++ b/test-harness/models/BaseEntity.cfc @@ -11,20 +11,17 @@ component mappedsuperclass="true" accessors="true"{ type="date" ormtype="timestamp" notnull="true" - update="false" - ndex="idx_createdDate"; + update="false"; property name="updatedDate" type="date" ormtype="timestamp" - notnull="true" - index="idx_updatedDate"; + notnull="true"; property name="isActive" ormtype="boolean" default="true" - notnull="true" - index="idx_active"; + notnull="true"; /* ********************************************************************* ** PUBLIC FUNCTIONS @@ -64,4 +61,4 @@ component mappedsuperclass="true" accessors="true"{ return ( isNull( variables[ this.pk ] ) OR !len( variables[ this.pk ] ) ? false : true ); } -} \ No newline at end of file +} diff --git a/test-harness/models/Permission.cfc b/test-harness/models/Permission.cfc index 70c97de..a714747 100755 --- a/test-harness/models/Permission.cfc +++ b/test-harness/models/Permission.cfc @@ -16,21 +16,24 @@ component persistent="true" column="permission_id" fieldtype="id" generator="uuid" + length ="36" ormtype="string" setter="false"; property name="permission" notnull="true" + ormtype="string" unique="true" length="255" - default="" - index="idx_permissionName"; + default=""; - property name="description" + property + name ="description" + column ="description" + ormtype="string" notnull="false" default="" - length="500" - db_html="textarea"; + length ="500"; /* ********************************************************************* ** CALCULATED FIELDS @@ -69,4 +72,4 @@ component persistent="true" return this; } -} \ No newline at end of file +} diff --git a/test-harness/models/Role.cfc b/test-harness/models/Role.cfc index e63a7ed..2dacfb6 100755 --- a/test-harness/models/Role.cfc +++ b/test-harness/models/Role.cfc @@ -17,21 +17,24 @@ component persistent="true" column="role_id" fieldtype="id" generator="uuid" + length ="36" ormtype="string" setter="false"; property name="role" notnull="true" unique="true" + ormtype="string" length="255" - default="" - index="idx_roleName"; + default=""; - property name="description" + property + name ="description" + column ="description" + ormtype="string" notnull="false" - db_html="textarea" default="" - length="500"; + length ="500"; /* ********************************************************************* ** RELATIONS @@ -42,9 +45,9 @@ component persistent="true" singularName="permission" fieldtype="many-to-many" type="array" - lazy="extra" + lazy="true" orderby="permission" - cascade="all" + cascade="save-update" cacheuse="read-write" cfc="Permission" fkcolumn="FK_roleID" @@ -152,4 +155,4 @@ component persistent="true" return this; } -} \ No newline at end of file +} diff --git a/test-harness/models/Setting.cfc b/test-harness/models/Setting.cfc index acd3fcb..eb6669d 100644 --- a/test-harness/models/Setting.cfc +++ b/test-harness/models/Setting.cfc @@ -13,6 +13,7 @@ component persistent="true" property name="settingId" fieldtype="id" generator="uuid" + length ="36" ormtype="string"; property name="name" @@ -60,4 +61,4 @@ component persistent="true" return this; } -} \ No newline at end of file +} diff --git a/test-harness/models/User.cfc b/test-harness/models/User.cfc index 4da7308..2d97f53 100755 --- a/test-harness/models/User.cfc +++ b/test-harness/models/User.cfc @@ -38,6 +38,7 @@ component persistent="true" column="user_id" fieldtype="id" generator="uuid" + length ="36" ormtype="string"; property name="fname" @@ -49,13 +50,11 @@ component persistent="true" property name="email" unique="true" notnull="true" - index="idx_email,idx_byEmail" db_display="false"; property name="username" unique="true" - notnull="true" - index="idx_credentials"; + notnull="true"; property name="password" notnull="true" @@ -64,8 +63,7 @@ component persistent="true" property name="isConfirmed" ormtype="boolean" default="false" - notnull="true" - index="idx_byEmail,idx_confirmed,idx_credentials"; + notnull="true"; property name="lastLogin" notnull="false" @@ -100,15 +98,13 @@ component persistent="true" notnull="false" length="255" unique="true" - index="idx_apitokens" default="" db_display="false"; property name="addToMailingList" ormtype="boolean" default="false" - notnull="true" - index="idx_mailingList"; + notnull="true"; /* ********************************************************************* ** RELATIONSHIPS diff --git a/test-harness/server-adobe@2021.json b/test-harness/server-adobe@2021.json new file mode 100644 index 0000000..713ddaf --- /dev/null +++ b/test-harness/server-adobe@2021.json @@ -0,0 +1,16 @@ +{ + "name":"mementifier-adobe@2021", + "app":{ + "serverHomeDirectory":".engine/adobe2021", + "cfengine":"adobe@2021" + }, + "web":{ + "http":{ + "port":"60299" + }, + "rewrites":{ + "enable":"true" + } + }, + "openBrowser":"false" +} diff --git a/test-harness/tests/Application.cfc b/test-harness/tests/Application.cfc index ac5ff94..777ce94 100644 --- a/test-harness/tests/Application.cfc +++ b/test-harness/tests/Application.cfc @@ -1,49 +1,54 @@ /** -******************************************************************************** -Copyright 2005-2007 ColdBox Framework by Luis Majano and Ortus Solutions, Corp -www.ortussolutions.com -******************************************************************************** -*/ -component{ + * ******************************************************************************* + * ******************************************************************************* + */ +component { - // structDelete( application, "cbController" ); - // applicationStop(); - // abort; + // structDelete( application, "cbController" ); + // applicationStop(); + // abort; // UPDATE THE NAME OF THE MODULE IN TESTING BELOW request.MODULE_NAME = "mementifier"; // APPLICATION CFC PROPERTIES - this.name = "ColdBoxTestingSuite" & hash(getCurrentTemplatePath()); - this.sessionManagement = true; - this.sessionTimeout = createTimeSpan( 0, 0, 15, 0 ); - this.applicationTimeout = createTimeSpan( 0, 0, 15, 0 ); - this.setClientCookies = true; + this.name = "ColdBoxTestingSuite" & hash( getCurrentTemplatePath() ); + this.sessionManagement = true; + this.sessionTimeout = createTimespan( 0, 0, 15, 0 ); + this.applicationTimeout = createTimespan( 0, 0, 15, 0 ); + this.setClientCookies = true; // Create testing mapping this.mappings[ "/tests" ] = getDirectoryFromPath( getCurrentTemplatePath() ); // The application root - rootPath = REReplaceNoCase( this.mappings[ "/tests" ], "tests(\\|/)", "" ); - this.mappings[ "/root" ] = rootPath; - this.mappings[ "/cborm" ] = rootPath & "/modules/cborm"; + rootPath = reReplaceNoCase( this.mappings[ "/tests" ], "tests(\\|/)", "" ); + this.mappings[ "/root" ] = rootPath; + this.mappings[ "/cborm" ] = rootPath & "/modules/cborm"; // The module root path - moduleRootPath = REReplaceNoCase( this.mappings[ "/root" ], "#request.module_name#(\\|/)test-harness(\\|/)", "" ); - this.mappings[ "/moduleroot" ] = moduleRootPath; + moduleRootPath = reReplaceNoCase( + this.mappings[ "/root" ], + "#request.module_name#(\\|/)test-harness(\\|/)", + "" + ); + this.mappings[ "/moduleroot" ] = moduleRootPath; this.mappings[ "/#request.MODULE_NAME#" ] = moduleRootPath & "#request.MODULE_NAME#"; // ORM definitions: ENABLE IF NEEDED - this.datasource = "mementifier"; - this.ormEnabled = "true"; + this.datasource = "mementifier"; + this.ormEnabled = "true"; this.ormSettings = { - cfclocation = [ "/root/models" ], - logSQL = true, - dbcreate = "dropcreate", - flushAtRequestEnd = false, - eventhandling = true, - eventHandler = "cborm.models.EventHandler", - skipcfcWithError = true + cfclocation : [ "/root/models" ], + logSQL : true, + dbcreate : "update", + dialect : "org.hibernate.dialect.MySQL5InnoDBDialect", + secondarycacheenabled : false, + cacheProvider : "ehcache", + flushAtRequestEnd : false, + eventhandling : true, + eventHandler : "cborm.models.EventHandler", + skipcfcWithError : false }; // request start @@ -67,4 +72,5 @@ component{ structDelete( application, "cbController" ); structDelete( application, "wirebox" ); } + } diff --git a/test-harness/tests/specs/MainTests.cfc b/test-harness/tests/specs/MainTests.cfc index 8d9f062..dc57bfa 100644 --- a/test-harness/tests/specs/MainTests.cfc +++ b/test-harness/tests/specs/MainTests.cfc @@ -1,13 +1,13 @@ component extends="coldbox.system.testing.BaseTestCase" { - function run() { - describe( "Mementifier", function() { - beforeEach( function( currentSpec ) { + function run(){ + describe( "Mementifier", function(){ + beforeEach( function( currentSpec ){ setup(); } ); - it( "can render base mementos", function() { - var event = this.request( route="/main/index", params={} ); + it( "can render base mementos", function(){ + var event = this.request( route = "/main/index", params = {} ); var memento = deserializeJSON( event.getRenderedContent() ); // Derfault INcludes + Excludes expect( memento ) @@ -18,112 +18,83 @@ expect( memento.lastName ).toBe( "TESTUSER" ); } ); - it( "can render mementos even if the object has already-serialized data", function() { - var event = this.request( route="/main/alreadySerialized", params={ - "includes": "alreadySerialized" - } ); + it( "can render mementos even if the object has already-serialized data", function(){ + var event = this.request( + route = "/main/alreadySerialized", + params = { "includes" : "alreadySerialized" } + ); var memento = deserializeJSON( event.getRenderedContent() ); // Derfault INcludes + Excludes - expect( memento[ "alreadySerialized" ] ) - .toBeArray() - .toHaveLength( 2 ); + expect( memento[ "alreadySerialized" ] ).toBeArray().toHaveLength( 2 ); - expect( memento [ "alreadySerialized" ][ 1 ] ) - .toBeStruct() - .toHaveKey( 'foo' ); + expect( memento[ "alreadySerialized" ][ 1 ] ).toBeStruct().toHaveKey( "foo" ); - expect( memento [ "alreadySerialized" ][ 2 ] ) - .toBeStruct() - .toHaveKey( 'baz' ); + expect( memento[ "alreadySerialized" ][ 2 ] ).toBeStruct().toHaveKey( "baz" ); - expect( memento[ "alreadySerialized" ][ 1 ][ 'foo' ] ) - .toBe( 'bar' ); + expect( memento[ "alreadySerialized" ][ 1 ][ "foo" ] ).toBe( "bar" ); } ); - it( "can process a resultsMap", function() { - var results = this.request( route="/main/resultMap" ) - .getValue( "cbox_handler_results" ); + it( "can process a resultsMap", function(){ + var results = this.request( route = "/main/resultMap" ).getValue( "cbox_handler_results" ); expect( results ).toHaveKey( "results" ).toHaveKey( "resultsMap" ); expect( results.resultsMap ).toHaveKey( results.results[ 1 ] ); - } ); + } ); - it( "can render inherited properties with wildcard default properties", function() { - var event = this.request( - route ="/main/index", - params={ - "includes": "createdDate,isActive" - } - ); + it( "can render inherited properties with wildcard default properties", function(){ + var event = this.request( route = "/main/index", params = { "includes" : "createdDate,isActive" } ); var memento = deserializeJSON( event.getRenderedContent() ); - // Expect inherited properties from the base class + // Expect inherited properties from the base class expect( memento ) .toBeStruct() .toHaveKey( "createdDate,isActive" ) .notToHaveKey( "modifiedDate" ); + } ); - } ); - - it( "can use a mapper for a property that does not exist", function() { - var event = this.request( - route ="/main/index", - params={ - "includes": "foo" - } - ); + it( "can use a mapper for a property that does not exist", function(){ + var event = this.request( route = "/main/index", params = { "includes" : "foo" } ); - var memento = deserializeJSON( event.getRenderedContent() ); + var memento = deserializeJSON( event.getRenderedContent() ); - // Expect inherited properties from the base class + // Expect inherited properties from the base class expect( memento ).toBeStruct(); expect( memento ).toHaveKey( "foo" ); expect( memento.foo ).toBe( memento.firstName & " " & memento.lastName ); - } ); + } ); - it( "skips properties that do not exist and do not have a mapper", function() { - var event = this.request( - route = "/main/index", - params = { - "includes": "doesntexist" - } - ); + it( "skips properties that do not exist and do not have a mapper", function(){ + var event = this.request( route = "/main/index", params = { "includes" : "doesntexist" } ); - var memento = deserializeJSON( event.getRenderedContent() ); + var memento = deserializeJSON( event.getRenderedContent() ); - // Expect inherited properties from the base class + // Expect inherited properties from the base class expect( memento ).toBeStruct(); expect( memento ).notToHaveKey( "doesntexist" ); - } ); + } ); - it( "tries to retrieve all properties when trustedGetters is on even if the method does not seem to exist", function() { - var event = this.request( - route = "/main/index", - params = { - "includes" : "doesntexist", - "trustedGetters": true - } - ); + it( "tries to retrieve all properties when trustedGetters is on even if the method does not seem to exist", function(){ + var event = this.request( + route = "/main/index", + params = { "includes" : "doesntexist", "trustedGetters" : true } + ); - var memento = deserializeJSON( event.getRenderedContent() ); + var memento = deserializeJSON( event.getRenderedContent() ); expect( memento ).toBeStruct(); expect( memento ).toHaveKey( "doesntexist" ); expect( memento.doesntexist ).toBe( "doesntexist" ); } ); - it( "can specify iso8601 in the getMemento call", function() { - var event = this.request( - route = "/main/index", - params = { - "includes": "createdDate", - "iso8601Format": true - } - ); + it( "can specify iso8601 in the getMemento call", function(){ + var event = this.request( + route = "/main/index", + params = { "includes" : "createdDate", "iso8601Format" : true } + ); var memento = deserializeJSON( event.getRenderedContent() ); expect( memento ).toBeStruct(); @@ -131,14 +102,11 @@ expect( memento.createdDate ).toInclude( "T" ); } ); - it( "can specify a date mask in the getMemento call", function() { - var event = this.request( - route = "/main/index", - params = { - "includes": "createdDate", - "dateMask": "MMM d YYYY" - } - ); + it( "can specify a date mask in the getMemento call", function(){ + var event = this.request( + route = "/main/index", + params = { "includes" : "createdDate", "dateMask" : "MMM d YYYY" } + ); var memento = deserializeJSON( event.getRenderedContent() ); expect( memento ).toBeStruct(); @@ -146,42 +114,33 @@ expect( listToArray( memento.createdDate, "" )[ 1 ] ).notToBeNumeric(); } ); - it( "can specify a time mask in the getMemento call", function() { - var event = this.request( - route = "/main/index", - params = { - "includes": "createdDate", - "timeMask": "H" - } - ); + it( "can specify a time mask in the getMemento call", function(){ + var event = this.request( + route = "/main/index", + params = { "includes" : "createdDate", "timeMask" : "H" } + ); var memento = deserializeJSON( event.getRenderedContent() ); expect( memento ).toBeStruct(); expect( memento ).toHaveKey( "createdDate" ); expect( find( ":", memento.createdDate ) > 0 ).toBeFalse( "Should not find a : character." ); - } ); + } ); - it( "correctly passes nested mappers", function() { - expect( function() { - var event = this.request( - route = "/main/nested", - params = {} - ); + it( "correctly passes nested mappers", function(){ + expect( function(){ + var event = this.request( route = "/main/nested", params = {} ); - var memento = deserializeJSON( event.getRenderedContent() ); - expect( memento.role.permissions[ 1 ].description ).toBeWithCase( "READ" ); - expect( memento.role.permissions[ 2 ].description ).toBeWithCase( "WRITE" ); - } ).notToThrow(); - } ); + var memento = deserializeJSON( event.getRenderedContent() ); + expect( memento.role.permissions[ 1 ].description ).toBeWithCase( "READ" ); + expect( memento.role.permissions[ 2 ].description ).toBeWithCase( "WRITE" ); + } ).notToThrow(); + } ); it( "can use property aliases for includes", function(){ - var event = this.request( - route = "/main/index", - params = { includes = "APIToken:token"} - ); + var event = this.request( route = "/main/index", params = { includes : "APIToken:token" } ); var memento = deserializeJSON( event.getRenderedContent() ); expect( memento ).toHaveKey( "token,firstName,lastName" ); - }); + } ); } ); }