diff --git a/.ai/CLAUDE.md b/.ai/CLAUDE.md deleted file mode 100755 index c1a332792d..0000000000 --- a/.ai/CLAUDE.md +++ /dev/null @@ -1,179 +0,0 @@ -# Wheels Documentation Index - -๐Ÿšจ **COMPREHENSIVE DOCUMENTATION INDEX** ๐Ÿšจ - -This file provides the complete index of Wheels documentation for AI assistants. All technical content has been organized into the structured `.ai` folder for maximum efficiency and accuracy. - -โ›” **CRITICAL: ALWAYS READ RELEVANT DOCUMENTATION BEFORE WRITING CODE** โ›” - -## ๐Ÿšจ MANDATORY Pre-Implementation Workflow - -### ๐Ÿ›‘ STEP 1: Critical Error Prevention (ALWAYS FIRST) -1. **`.ai/wheels/troubleshooting/common-errors.md`** - PREVENT FATAL ERRORS -2. **`.ai/wheels/patterns/validation-templates.md`** - VALIDATION CHECKLISTS - -### ๐Ÿ“‹ STEP 2: Task-Specific Documentation Loading - -#### ๐Ÿ—๏ธ For Model Development -**MANDATORY Reading Order:** -1. `.ai/wheels/models/data-handling.md` - Critical query vs array patterns -2. `.ai/wheels/models/architecture.md` - Model fundamentals and structure -3. `.ai/wheels/models/associations.md` - Relationship patterns (CRITICAL) -4. `.ai/wheels/models/validations.md` - Validation methods and patterns -5. `.ai/wheels/models/best-practices.md` - Model development guidelines - -#### ๐ŸŽฎ For Controller Development -**MANDATORY Reading Order:** -1. `.ai/wheels/controllers/architecture.md` - Controller fundamentals and CRUD -2. `.ai/wheels/controllers/rendering.md` - View rendering and responses -3. `.ai/wheels/controllers/filters.md` - Authentication and authorization -4. `.ai/wheels/controllers/model-interactions.md` - Controller-model patterns -5. `.ai/wheels/controllers/best-practices.md` - Controller development guidelines - -#### ๐Ÿ“„ For View Development -**MANDATORY Reading Order:** -1. `.ai/wheels/views/data-handling.md` - CRITICAL query vs array patterns -2. `.ai/wheels/views/architecture.md` - View structure and conventions -3. `.ai/wheels/views/forms.md` - Form helpers and limitations (CRITICAL) -4. `.ai/wheels/views/layouts.md` - Layout patterns and inheritance -5. `.ai/wheels/views/best-practices.md` - View implementation checklist - -#### โš™๏ธ For Configuration Work -**MANDATORY Reading Order:** -1. `.ai/wheels/configuration/routing.md` - CRITICAL routing anti-patterns -2. `.ai/wheels/configuration/environments.md` - Environment settings -3. `.ai/wheels/configuration/framework-settings.md` - Global settings -4. `.ai/wheels/configuration/best-practices.md` - Configuration guidelines - -### ๐Ÿ” STEP 3: Anti-Pattern Validation (BEFORE WRITING CODE) -- [ ] โŒ **NO** mixed argument styles in Wheels functions -- [ ] โŒ **NO** ArrayLen() usage on model associations (use .recordCount) -- [ ] โŒ **NO** Rails-style nested resource routing -- [ ] โŒ **NO** emailField() or passwordField() helpers (don't exist) -- [ ] โœ… **YES** consistent arguments: ALL named OR ALL positional -- [ ] โœ… **YES** use .recordCount: `user.posts().recordCount` -- [ ] โœ… **YES** separate resource declarations -- [ ] โœ… **YES** textField() with type attribute - -## ๐Ÿ“š Complete Documentation Structure - -### Core Framework Components - -#### Models Documentation (`.ai/wheels/models/`) -- `architecture.md` - Model structure and fundamentals -- `data-handling.md` - Critical query vs array patterns -- `associations.md` - Relationship patterns (CRITICAL) -- `validations.md` - Validation rules and methods -- `callbacks.md` - Lifecycle hooks and events -- `methods-reference.md` - Complete method documentation -- `advanced-patterns.md` - Complex model examples -- `user-authentication.md` - Authentication model patterns -- `testing.md` - Model testing strategies -- `performance.md` - Query optimization -- `best-practices.md` - Development guidelines -- `advanced-features.md` - Timestamps and dirty tracking - -#### Controllers Documentation (`.ai/wheels/controllers/`) -- `architecture.md` - Controller structure and CRUD patterns -- `rendering.md` - View rendering, redirects, flash messages -- `filters.md` - Authentication, authorization, data loading -- `model-interactions.md` - Controller-model patterns, validation -- `api.md` - JSON/XML APIs, authentication, versioning -- `security.md` - CSRF, parameter verification, sanitization -- `testing.md` - Controller testing patterns and helpers - -#### Views Documentation (`.ai/wheels/views/`) -- `data-handling.md` - Critical query vs array patterns -- `architecture.md` - View structure and file organization -- `layouts.md` - Layout patterns and inheritance -- `partials.md` - Partial usage and patterns -- `forms.md` - Form helpers and Wheels limitations -- `helpers.md` - View helpers and custom helpers -- `advanced-patterns.md` - AJAX, performance, caching -- `testing.md` - View testing patterns -- `best-practices.md` - Implementation checklist and patterns - -#### Configuration Documentation (`.ai/wheels/configuration/`) -- `routing.md` - CRITICAL routing anti-patterns and patterns -- `environments.md` - Environment settings and switching -- `application.md` - Application.cfc settings (app.cfm) -- `framework-settings.md` - Global framework settings (settings.cfm) -- `overview.md` - File structure, loading order, general overview -- `best-practices.md` - Configuration best practices and patterns -- `troubleshooting.md` - Common issues and debugging -- `security.md` - Security considerations and hardening - -## ๐Ÿšจ Critical Anti-Pattern Prevention - -### Most Common Wheels Errors -1. **Mixed Arguments**: `hasMany("comments", dependent="delete")` โŒ -2. **Query vs Array Confusion**: `ArrayLen(posts)` on query objects โŒ -3. **Rails-style Routing**: Nested resource functions โŒ -4. **Non-existent Helpers**: `emailField()`, `passwordField()` โŒ - -### Correct Patterns -1. **Consistent Arguments**: `hasMany(name="comments", dependent="delete")` โœ… -2. **Query Methods**: `posts.recordCount` โœ… -3. **Separate Resources**: `.resources("posts").resources("comments")` โœ… -4. **Wheels Helpers**: `textField(type="email")` โœ… - -## ๐Ÿ› ๏ธ AI Assistant Implementation Guidelines - -### ๐Ÿ›‘ MANDATORY Pre-Code Actions (NO EXCEPTIONS) -1. **ALWAYS** read `.ai/wheels/troubleshooting/common-errors.md` FIRST -2. **ALWAYS** read component-specific .ai documentation -3. **VALIDATE** against anti-patterns before writing any code -4. **REFERENCE** code examples from .ai documentation as templates -5. **CHECK** implementation against validation templates continuously - -### Quality Assurance Process -1. **Documentation First**: Always consult .ai documentation before coding -2. **Pattern Consistency**: Follow established patterns from .ai documentation -3. **Security Awareness**: Apply security practices from .ai documentation -4. **Convention Adherence**: Follow Wheels naming and structure conventions -5. **Validation**: Test implementations against documented standards - -## ๐Ÿš€ Quick Reference Dispatchers - -### Component Quick Access -- **Models**: `app/models/CLAUDE.md` โ†’ `.ai/wheels/models/` -- **Controllers**: `app/controllers/CLAUDE.md` โ†’ `.ai/wheels/controllers/` -- **Views**: `app/views/CLAUDE.md` โ†’ `.ai/wheels/views/` -- **Configuration**: Root `CLAUDE.md` โ†’ `.ai/wheels/configuration/` - -### Critical Reading Priority -1. **Error Prevention**: `.ai/wheels/troubleshooting/common-errors.md` -2. **Data Handling**: Component-specific `data-handling.md` files -3. **Best Practices**: Component-specific `best-practices.md` files -4. **Architecture**: Component-specific `architecture.md` files - -## โœ… Post-Implementation Validation - -### MANDATORY Validation Commands -```bash -# 1. Syntax validation -wheels server start --validate - -# 2. Test validation -wheels test run - -# 3. Manual anti-pattern check -# Check implementation against .ai documentation patterns -``` - -### If Validation Fails -1. Consult `.ai/wheels/troubleshooting/common-errors.md` -2. Review appropriate component documentation in `.ai/wheels/` -3. Fix errors following documented patterns -4. Re-run validation until all checks pass - -## ๐ŸŽฏ Success Criteria - -**Your implementation is successful when:** -- [ ] All relevant .ai documentation has been read -- [ ] No anti-patterns are present in the code -- [ ] Patterns match those documented in .ai folder -- [ ] Validation commands pass successfully -- [ ] Code follows Wheels conventions and best practices - -๐Ÿšจ **REMEMBER: The .ai folder contains the definitive, comprehensive documentation. ALWAYS use it as your primary reference!** \ No newline at end of file diff --git a/.ai/CONTRIBUTION_SUMMARY.md b/.ai/CONTRIBUTION_SUMMARY.md deleted file mode 100644 index 72cd67c3a3..0000000000 --- a/.ai/CONTRIBUTION_SUMMARY.md +++ /dev/null @@ -1,162 +0,0 @@ -# .ai Folder Contribution Summary - -## Overview - -This `.ai` folder contains comprehensive Wheels framework documentation extracted from real-world development sessions. The patterns, best practices, and solutions documented here are production-tested and ready for contribution to the Wheels project. - -## Generic Patterns Integrated (Ready for Wheels Project) - -### ๐Ÿ”ด Critical Patterns (High Impact for All Developers) - -#### 1. **Layout cfoutput Block Coverage** โ†’ [views/layouts.md](wheels/views/layouts.md) -- **Issue**: Most common beginner error - CFML expressions not rendering -- **Solution**: Single `` block wrapping entire HTML layout -- **Impact**: Affects 90%+ of new Wheels developers -- **Location**: Enhanced in `common-errors.md` and `views/layouts.md` - -#### 2. **Form Helper Duplicate Labels** โ†’ [views/forms.md](wheels/views/forms.md) -- **Issue**: Form helpers automatically generate labels, causing duplicates -- **Solution**: Use `label=false` parameter when using custom HTML labels -- **Impact**: Very common UX issue that confuses developers -- **Location**: Documented in `views/forms.md` and `common-errors.md` - -#### 3. **Query vs Object Association Access** โ†’ [views/query-association-patterns.md](wheels/views/query-association-patterns.md) & [models/associations.md](wheels/models/associations.md) -- **Issue**: Confusion between query objects and model instances in loops -- **Solution**: Store association results in variables before looping -- **Impact**: Fundamental misunderstanding causing frequent errors -- **Location**: Comprehensive guide in `query-association-patterns.md` - -#### 4. **Consistent Argument Style Requirement** โ†’ [common-errors.md](wheels/troubleshooting/common-errors.md) -- **Issue**: Mixed positional/named arguments cause cryptic errors -- **Solution**: Always use all-named or all-positional arguments -- **Impact**: Frequent error that's not obvious to fix -- **Location**: Examples throughout all documentation files - -### ๐Ÿ—„๏ธ Database & Migration Patterns - -#### 5. **Database-Agnostic Date Handling** โ†’ [database/migrations/date-function-issues.md](wheels/database/migrations/date-function-issues.md) -- **Pattern**: Use CFML DateAdd/DateFormat instead of database-specific SQL -- **Benefit**: Works across H2, MySQL, PostgreSQL, SQL Server -- **Impact**: Portable migrations for all environments -- **Location**: Comprehensive guide with examples - -#### 6. **Migration Direct SQL Best Practice** โ†’ [database/migrations/best-practices.md](wheels/database/migrations/best-practices.md) -- **Pattern**: Direct SQL more reliable than parameter binding for data seeding -- **Benefit**: Consistent migration execution across environments -- **Impact**: Reduces migration failures -- **Location**: New comprehensive best practices guide - -#### 7. **Working with Existing Schemas** โ†’ [database/migrations/best-practices.md](wheels/database/migrations/best-practices.md) -- **Pattern**: Check for existing tables before creating migrations -- **Solution**: Use `changeTable()` for additions to existing schema -- **Impact**: Prevents "table already exists" errors -- **Location**: New section in best-practices.md - -### ๐Ÿงช Testing Strategies - -#### 8. **Content Verification Over Status Codes** โ†’ [views/testing.md](wheels/views/testing.md) -- **Issue**: HTTP 200 doesn't mean content rendered correctly -- **Solution**: Verify actual content with grep/pattern matching -- **Impact**: Catches rendering errors that status codes miss -- **Location**: New critical section in testing.md - -#### 9. **Incremental Testing Approach** โ†’ [views/testing.md](wheels/views/testing.md) -- **Pattern**: Test after each component (model โ†’ controller โ†’ view) -- **Benefit**: Isolates issues quickly, prevents compound errors -- **Impact**: Faster debugging and validation -- **Location**: Command-line testing strategy in testing.md - -### ๐ŸŽจ Modern Frontend Integration - -#### 10. **CDN-Based Frontend Stack** โ†’ [integration/modern-frontend-stack.md](wheels/integration/modern-frontend-stack.md) -- **Pattern**: Tailwind CSS + Alpine.js + HTMX via CDN -- **Benefit**: No build process, works seamlessly with CFML -- **Impact**: Modern UI without complexity -- **Location**: Complete integration guide with production examples - -### ๐Ÿ›ฃ๏ธ Routing & Configuration - -#### 11. **Route Configuration Order** โ†’ [configuration/routing.md](wheels/configuration/routing.md) -- **Pattern**: Resources โ†’ Custom โ†’ Root โ†’ Wildcard -- **Impact**: Prevents route matching conflicts -- **Location**: Routing guide with examples - -## Files Modified/Created - -### Enhanced Existing Files: -- โœ… `wheels/troubleshooting/common-errors.md` - Added layout cfoutput errors -- โœ… `wheels/views/layouts.md` - Enhanced with cfoutput block rules -- โœ… `wheels/views/forms.md` - Already had duplicate label warnings -- โœ… `wheels/views/query-association-patterns.md` - Already comprehensive -- โœ… `wheels/models/associations.md` - Already had query return documentation -- โœ… `wheels/database/migrations/date-function-issues.md` - Added database-agnostic section -- โœ… `wheels/views/testing.md` - Added content verification section -- โœ… `wheels/configuration/routing.md` - Already had ordering guidance -- โœ… `wheels/integration/modern-frontend-stack.md` - Added key findings summary - -### New Files Created: -- โœจ `wheels/database/migrations/best-practices.md` - Comprehensive migration guide including existing schema handling - -### Session Files Removed: -- โŒ `wheels/troubleshooting/session-learnings-2024-09-17.md` - Patterns integrated -- โŒ `wheels/troubleshooting/session-learnings-2025-10-01.md` - Patterns integrated - -## Value Proposition for Wheels Project - -### Documentation Improvements: -1. **Common Mistakes Guide** - Layout cfoutput, duplicate labels, query vs object -2. **Migration Best Practices** - Data seeding, database-agnostic SQL, existing schemas -3. **Modern Frontend Integration** - Tailwind/Alpine.js/HTMX patterns -4. **Testing Strategies** - Content verification over status codes - -### Framework Enhancement Opportunities: -1. โš ๏ธ **Warning System** - Detect mixed positional/named arguments at runtime -2. โš ๏ธ **Generator Improvements** - Better handling of existing database schemas -3. โš ๏ธ **Form Helper Defaults** - Clearer documentation of label generation behavior - -### Example Code/Scaffolding: -1. ๐Ÿ“ฆ **Modern Blog Starter Template** - Demonstrating all patterns -2. ๐Ÿ“ฆ **Layout Templates** - Proper cfoutput block structure -3. ๐Ÿ“ฆ **Migration Examples** - Database-agnostic patterns - -## Usage - -This documentation is **project-agnostic** and represents patterns discovered through multiple Wheels implementations. Each pattern: - -- โœ… Has been tested in real applications -- โœ… Solves documented pain points -- โœ… Includes clear examples and anti-patterns -- โœ… Benefits the entire Wheels community - -## Contribution Path - -### For Wheels Core Team: - -**Immediate Documentation Additions:** -1. Add to official Wheels documentation site -2. Create "Common Mistakes" guide -3. Enhance migration documentation with best practices -4. Add modern frontend integration examples - -**Framework Enhancements (Optional):** -1. Add warning for mixed argument styles -2. Improve generator schema detection -3. Enhance form helper documentation - -### For Wheels Community: - -**Share as Community Resources:** -1. Wheels GitHub Discussions -2. Wheels Slack/Discord channels -3. Blog posts and tutorials -4. Conference presentations - -## License & Attribution - -These patterns are contributed to the Wheels project under the same license as Wheels (Apache 2.0). They represent collective knowledge from production implementations and are freely available for the community. - ---- - -**Generated**: 2025-10-02 -**Source**: Multiple production Wheels applications -**Status**: Ready for contribution to Wheels project diff --git a/.ai/MCP-ENFORCEMENT.md b/.ai/MCP-ENFORCEMENT.md deleted file mode 100755 index e05af590e7..0000000000 --- a/.ai/MCP-ENFORCEMENT.md +++ /dev/null @@ -1,70 +0,0 @@ -# ๐Ÿšจ MCP Enforcement Guidelines - -## Mandatory MCP Usage Check - -**AI assistants MUST perform this check before ANY development work:** - -```bash -# Check if MCP tools are mandatory -ls .mcp.json -``` - -**If `.mcp.json` exists โ†’ MCP tools are MANDATORY** - -## ๐Ÿšซ FORBIDDEN Commands (when .mcp.json exists) - -```bash -# NEVER use these when MCP is available: -wheels g model -wheels g controller -wheels g migration -wheels g scaffold -wheels dbmigrate -wheels test -wheels server -curl http://localhost:*/?reload=true -``` - -## โœ… MANDATORY Commands (when .mcp.json exists) - -```javascript -// ALWAYS use these instead: -mcp__wheels__wheels_generate(type="model", ...) -mcp__wheels__wheels_generate(type="controller", ...) -mcp__wheels__wheels_generate(type="migration", ...) -mcp__wheels__wheels_generate(type="scaffold", ...) -mcp__wheels__wheels_migrate(action="...") -mcp__wheels__wheels_test() -mcp__wheels__wheels_server(action="...") -mcp__wheels__wheels_reload() -``` - -## Enforcement Rules - -1. **Check .mcp.json first** - always -2. **Use MCP tools exclusively** - if available -3. **Never mix CLI and MCP** - pick one based on availability -4. **Test MCP connection** - before starting work -5. **Follow MCP patterns** - as documented in CLAUDE.md - -## Violation Detection - -**If you catch yourself or another AI assistant doing ANY of the following:** -- Using `wheels g` commands when `.mcp.json` exists -- Using `wheels dbmigrate` when MCP is available -- Using `curl` for reload when `mcp__wheels__wheels_reload()` is available -- Mixing CLI and MCP approaches - -**โ†’ STOP immediately and switch to MCP tools** - -## Benefits of MCP Tools - -1. **Better Integration** - Direct integration with Wheels application -2. **Error Handling** - Improved error reporting and handling -3. **Consistency** - Standardized interface across all operations -4. **Validation** - Built-in validation and safety checks -5. **Documentation** - Access to real-time project documentation - ---- - -**Remember: MCP tools provide a superior development experience when available. Always use them when `.mcp.json` exists.** \ No newline at end of file diff --git a/.ai/QUICK_REFERENCE.md b/.ai/QUICK_REFERENCE.md deleted file mode 100644 index d1c6364a65..0000000000 --- a/.ai/QUICK_REFERENCE.md +++ /dev/null @@ -1,244 +0,0 @@ -# Wheels Quick Reference - Most Common Issues - -## ๐Ÿ”ด Top 5 Critical Patterns (Learn These First!) - -### 1. Layout cfoutput Block Coverage (MOST COMMON ERROR) - -โŒ **WRONG - Expressions don't render:** -```cfm - - #includeContent()# - - - - - #csrfMetaTags()# - - - Posts - - - -``` - -โœ… **CORRECT - Wrap entire HTML in cfoutput:** -```cfm - - #includeContent()# - - - - - - #csrfMetaTags()# - - - Posts - #includeContent()# - - - - -``` - -**Rule**: Open `` after ``, close before `` - ---- - -### 2. Form Helper Duplicate Labels - -โŒ **WRONG - Creates duplicate labels:** -```cfm - -#textField(objectName="post", property="title")# -``` -**Result**: "Title Title" appears - -โœ… **CORRECT - Use label=false:** -```cfm - -#textField(objectName="post", property="title", label=false)# -``` - -**Rule**: When using HTML labels, add `label=false` to form helpers - ---- - -### 3. Association Query Handling in Views - -โŒ **WRONG - Associations aren't counted properties:** -```cfm - -

#posts.comments_count# comments

-
-``` - -โœ… **CORRECT - Load association and use recordCount:** -```cfm - - -

#comments.recordCount# comments

-
-``` - -**Rule**: Associations return QUERY objects with `.recordCount`, not computed properties - ---- - -### 4. Consistent Argument Style - -โŒ **WRONG - Mixed positional and named:** -```cfm -hasMany("comments", dependent="delete") -model("Post").findByKey(1, include="comments") -``` - -โœ… **CORRECT - All named arguments:** -```cfm -hasMany(name="comments", dependent="delete") -model("Post").findByKey(key=1, include="comments") -``` - -**Rule**: Never mix positional and named arguments - ---- - -### 5. Database-Agnostic Migration Dates - -โŒ **WRONG - Database-specific functions:** -```cfm -execute("INSERT INTO posts (publishedAt) VALUES (DATE_SUB(NOW(), INTERVAL 7 DAY))"); -``` -**Problem**: Only works with MySQL - -โœ… **CORRECT - Use CFML date functions:** -```cfm -var day7 = DateAdd("d", -7, Now()); -execute("INSERT INTO posts (publishedAt) VALUES ( - TIMESTAMP '#DateFormat(day7, "yyyy-mm-dd")# #TimeFormat(day7, "HH:mm:ss")#' -)"); -``` - -**Rule**: Use CFML DateAdd/DateFormat for portability - ---- - -## ๐Ÿงช Testing Best Practice - -โŒ **WRONG - Only check status code:** -```bash -curl -I http://localhost:8080 | grep "200 OK" -``` -**Problem**: 200 doesn't mean content is correct - -โœ… **CORRECT - Verify actual content:** -```bash -curl -s http://localhost:8080 | grep "Expected Content" -curl -s http://localhost:8080 | grep -c "article" # Count elements -curl -s http://localhost:8080 | grep '#urlFor' # Should be empty -``` - -**Rule**: Always verify content rendering, not just HTTP status - ---- - -## ๐Ÿ—„๏ธ Migration Best Practices - -### Check Before Creating Tables - -โŒ **WRONG - Assume clean database:** -```cfm -function up() { - t = createTable(name="posts"); - t.create(); -} -``` -**Error**: "Table already exists" - -โœ… **CORRECT - Check existing schema first:** -```bash -# Before creating migration -wheels dbmigrate info # Check current state - -# If table exists, modify instead -t = changeTable(name="posts"); -t.text(columnNames="excerpt"); # Add missing column only -t.change(); -``` - -### Direct SQL for Data Seeding - -โŒ **WRONG - Parameter binding:** -```cfm -execute(sql="INSERT INTO posts VALUES (?)", parameters=[{value=title}]); -``` - -โœ… **CORRECT - Direct SQL:** -```cfm -execute("INSERT INTO posts (title, createdAt, updatedAt) - VALUES ('My Post', NOW(), NOW())"); -``` - ---- - -## ๐Ÿ›ฃ๏ธ Route Configuration Order - -โŒ **WRONG - Wildcard first:** -```cfm -mapper() - .wildcard() - .resources("posts") -.end(); -``` - -โœ… **CORRECT - Specific to general:** -```cfm -mapper() - .resources("posts") // 1. Resource routes - .get(name="about", ...) // 2. Custom routes - .root(to="posts##index") // 3. Root route - .wildcard() // 4. Wildcard LAST -.end(); -``` - ---- - -## ๐Ÿ” Quick Debugging Checklist - -When something doesn't work, check in this order: - -### Views Not Rendering: -1. โœ… Is entire layout in `` block? -2. โœ… Did you reload after changes (`?reload=true`)? -3. โœ… Are associations stored in variables before loops? - -### Forms Have Issues: -1. โœ… Did you add `label=false` when using custom labels? -2. โœ… Are arguments all named or all positional (not mixed)? -3. โœ… Did you test delete buttons with `method="delete"`? - -### Migrations Failing: -1. โœ… Did you check if tables already exist? -2. โœ… Are you using CFML date functions, not database-specific? -3. โœ… Are operations wrapped in `transaction` blocks? - -### Routes Not Working: -1. โœ… Is route order correct (resources โ†’ custom โ†’ root โ†’ wildcard)? -2. โœ… Did mapper end with `.end()`? -3. โœ… Did you reload routes (`?reload=true`)? - ---- - -## ๐Ÿ“š Full Documentation References - -- **Layout Issues**: [views/layouts.md](wheels/views/layouts.md) -- **Form Problems**: [views/forms.md](wheels/views/forms.md) -- **Query/Association**: [views/query-association-patterns.md](wheels/views/query-association-patterns.md) -- **All Common Errors**: [troubleshooting/common-errors.md](wheels/troubleshooting/common-errors.md) -- **Migration Best Practices**: [database/migrations/best-practices.md](wheels/database/migrations/best-practices.md) -- **Testing Strategies**: [views/testing.md](wheels/views/testing.md) -- **Modern Frontend**: [integration/modern-frontend-stack.md](wheels/integration/modern-frontend-stack.md) - ---- - -**Remember**: These 5 patterns solve 80%+ of common Wheels issues. Master them first! diff --git a/.ai/README.md b/.ai/README.md index 2cbbae7ea3..8fba0d2f69 100755 --- a/.ai/README.md +++ b/.ai/README.md @@ -1,142 +1,31 @@ -# Wheels Framework AI Knowledge Base - -## Overview -This knowledge base contains comprehensive information about CFML (ColdFusion Markup Language) and the Wheels framework, organized for AI coding assistants. The documentation is structured in two main sections to provide both foundational language knowledge and framework-specific guidance. - -## Structure - -The documentation is organized into two main sections: - -### ๐Ÿ“ [CFML Language Documentation](./cfml/) -Core CFML language concepts, syntax, and features that apply to all CFML frameworks: - -- **[Syntax](./cfml/syntax/)** - Basic CFML syntax, CFScript vs tags, comments -- **[Data Types](./cfml/data-types/)** - Variables, arrays, strings, structures, numbers, scopes -- **[Control Flow](./cfml/control-flow/)** - Conditionals, loops, exception handling -- **[Components](./cfml/components/)** - CFC basics, functions, properties -- **[Database](./cfml/database/)** - Query fundamentals and database interaction -- **[Advanced](./cfml/advanced/)** - Closures and advanced language features -- **[Best Practices](./cfml/best-practices/)** - Modern CFML development patterns - -### ๐Ÿ“ [Wheels Framework Documentation](./wheels/) -Framework-specific patterns, conventions, and features: - -#### Core Framework Areas -- **[CLI Tools](./wheels/cli/)** - Generators, server management, testing tools -- **[Configuration](./wheels/configuration/)** - Environment settings, framework configuration -- **[Controllers](./wheels/controllers/)** - Request handling, filters, rendering, parameters -- **[Core Concepts](./wheels/core-concepts/)** - MVC architecture, ORM, routing conventions -- **[Database](./wheels/database/)** - ActiveRecord ORM, migrations, associations, validations -- **[Views](./wheels/views/)** - Templates, layouts, helpers, assets - -#### Development Support -- **[Communication](./wheels/communication/)** - Email, HTTP requests, API development -- **[Files](./wheels/files/)** - File uploads, downloads, asset management -- **[Patterns](./wheels/patterns/)** - Common development patterns and best practices -- **[Security](./wheels/security/)** - Authentication, authorization, CSRF protection -- **[Snippets](./wheels/snippets/)** - Code examples and quick reference patterns - -## Quick Reference - -### Getting Started with CFML + Wheels -1. **Understand CFML Basics**: Start with [CFML syntax](./cfml/syntax/) and [data types](./cfml/data-types/) -2. **Learn Wheels Conventions**: Review [MVC architecture](./wheels/core-concepts/) patterns -3. **Use CLI Tools**: Leverage [generators](./wheels/cli/) for rapid development - -### ๐Ÿšจ CRITICAL: MCP Tools Required -**If `.mcp.json` exists, use MCP tools exclusively - CLI commands are FORBIDDEN** - -### โœ… MCP Development Tasks (MANDATORY when .mcp.json exists) -```javascript -// Generate application components -mcp__wheels__wheels_generate(type="model", name="User", attributes="name:string,email:string,active:boolean") -mcp__wheels__wheels_generate(type="controller", name="Users", actions="index,show,new,create,edit,update,delete") -mcp__wheels__wheels_generate(type="migration", name="CreateUsersTable") - -// Database operations -mcp__wheels__wheels_migrate(action="latest") -mcp__wheels__wheels_migrate(action="up") -mcp__wheels__wheels_migrate(action="down") - -// Server management -mcp__wheels__wheels_server(action="start") -mcp__wheels__wheels_server(action="stop") -mcp__wheels__wheels_test() -mcp__wheels__wheels_reload() -``` - -### โŒ Legacy CLI Commands (DO NOT USE if .mcp.json exists) -```bash -# These are FORBIDDEN when MCP tools are available -wheels g model User name:string,email:string,active:boolean -wheels g controller Users index,show,new,create,edit,update,delete -wheels dbmigrate latest -wheels server start -wheels test run -``` - -### Key Framework Concepts -- **Models are singular**: User.cfc โ†’ users table -- **Controllers are plural**: Users.cfc handles users resource -- **Routes use resources**: `resources("users")` creates RESTful routes -- **Validations in models**: `validatesPresenceOf("name,email")` -- **Associations**: `hasMany("orders")`, `belongsTo("user")` -- **CFScript preferred**: Modern CFML uses CFScript over tag-based syntax - -## Documentation Structure -Each documentation file follows a consistent structure: -- **Description**: Brief explanation of the concept -- **Key Points**: Important facts and features -- **Code Sample**: Practical, working examples -- **Usage**: Step-by-step instructions -- **Related**: Links to connected concepts -- **Important Notes**: Common pitfalls and best practices - -## How to Use This Knowledge Base - -### For AI Assistants -1. **Start with context**: Determine if the question is about CFML language fundamentals or Wheels framework specifics -2. **Reference appropriately**: Use CFML docs for language questions, Wheels docs for framework patterns -3. **Combine knowledge**: Many questions require understanding both CFML syntax and Wheels conventions -4. **Follow patterns**: Use the established code examples and patterns shown in the documentation - -### For Developers -1. **New to CFML?** Start with [CFML fundamentals](./cfml/) -2. **New to Wheels?** Begin with [core concepts](./wheels/core-concepts/) -3. **Specific task?** Check [snippets](./wheels/snippets/) and [patterns](./wheels/patterns/) -4. **Configuration issues?** Review [configuration](./wheels/configuration/) docs - -## Best Practices - -### CFML Development -- Use CFScript syntax for modern, readable code -- Understand variable scoping and lifecycle -- Leverage CFML's dynamic nature while maintaining type safety -- Follow contemporary CFML patterns from the best practices section - -### Wheels Framework -1. **Follow Conventions**: Wheels rewards convention over configuration -2. **Keep Controllers Thin**: Business logic belongs in models -3. **Use Validations**: Validate data at the model level -4. **Secure by Default**: Use CSRF protection and parameter verification -5. **Test Everything**: Write tests for models, controllers, and integrations -6. **Leverage the ORM**: Use Wheels' ActiveRecord patterns for database operations - -## Integration with Development Tools - -This knowledge base is designed to work with: -- **AI coding assistants** (Claude Code, GitHub Copilot, etc.) -- **MCP (Model Context Protocol)** clients -- **Wheels development server** (AI documentation endpoints) -- **IDE integrations** and development workflows - -## Contributing -This knowledge base combines: -- Official Wheels framework documentation -- CFML language reference materials -- Modern CFML development practices -- Community best practices and patterns - ---- - -*Organized for comprehensive CFML and Wheels framework development support* \ No newline at end of file +# Wheels Framework Reference Docs + +Searchable reference for CFML language and Wheels framework patterns. + +## CFML Language (`cfml/`) + +- `syntax/` โ€” CFScript basics, tags vs script, comments, hash escaping +- `data-types/` โ€” Variables, scopes, arrays, strings, structures, numbers +- `control-flow/` โ€” Conditionals, loops, exception handling +- `components/` โ€” CFC basics, functions, properties +- `database/` โ€” Query fundamentals +- `advanced/` โ€” Closures, advanced features +- `best-practices/` โ€” Modern CFML patterns + +## Wheels Framework (`wheels/`) + +- `models/` โ€” ORM architecture, associations, validations, performance +- `controllers/` โ€” Actions, filters, rendering, security, parameters +- `views/` โ€” Layouts, partials, form helpers, link helpers, assets +- `database/` โ€” Migrations, associations, queries, validations +- `configuration/` โ€” Routing, environments, settings +- `core-concepts/` โ€” MVC architecture, ORM mapping, routing conventions +- `cli/` โ€” Generators, server management +- `communication/` โ€” Email sending +- `files/` โ€” File uploads and downloads +- `security/` โ€” CSRF, authentication, authorization +- `patterns/` โ€” Common development patterns, validation templates +- `snippets/` โ€” Code examples for all component types +- `integration/` โ€” Frontend stack (Tailwind, Alpine, HTMX) +- `testing/` โ€” Testing strategies and patterns +- `troubleshooting/` โ€” Common errors and debugging diff --git a/.ai/cfml/advanced/closures.md b/.ai/cfml/advanced/closures.md index 35fe6269c6..6c2a2afbe8 100755 --- a/.ai/cfml/advanced/closures.md +++ b/.ai/cfml/advanced/closures.md @@ -232,5 +232,4 @@ isValid = validator.validate({ email: "user@domain.com" }); ## Related Concepts - [Functions](../components/functions.md) - [Array Methods](../data-types/arrays/array-methods.md) -- [Functional Programming](../best-practices/functional-patterns.md) - [Variable Scopes](../data-types/variable-scopes.md) \ No newline at end of file diff --git a/.ai/cfml/components/component-basics.md b/.ai/cfml/components/component-basics.md index 0e95db3ab6..b56a491597 100755 --- a/.ai/cfml/components/component-basics.md +++ b/.ai/cfml/components/component-basics.md @@ -176,7 +176,4 @@ obj = new MyClass() ## Related Concepts - [Properties](properties.md) -- [Functions](functions.md) -- [Inheritance](inheritance.md) -- [Interfaces](interfaces.md) -- [Static Methods](static-methods.md) \ No newline at end of file +- [Functions](functions.md) \ No newline at end of file diff --git a/.ai/cfml/components/functions.md b/.ai/cfml/components/functions.md index aa6fce5786..73fd45017e 100755 --- a/.ai/cfml/components/functions.md +++ b/.ai/cfml/components/functions.md @@ -264,6 +264,4 @@ component { ## Related Concepts - [Component Basics](component-basics.md) - [Properties](properties.md) -- [Variable Scopes](../data-types/variable-scopes.md) -- [Inheritance](inheritance.md) -- [Static Methods](static-methods.md) \ No newline at end of file +- [Variable Scopes](../data-types/variable-scopes.md) \ No newline at end of file diff --git a/.ai/cfml/components/properties.md b/.ai/cfml/components/properties.md index 6c1f61f6a2..76ac9494f9 100755 --- a/.ai/cfml/components/properties.md +++ b/.ai/cfml/components/properties.md @@ -180,5 +180,4 @@ component accessors="true" { ## Related Concepts - [Component Basics](component-basics.md) - [Functions](functions.md) -- [Variable Scopes](../data-types/variable-scopes.md) -- [ORM Basics](../database/orm-basics.md) \ No newline at end of file +- [Variable Scopes](../data-types/variable-scopes.md) \ No newline at end of file diff --git a/.ai/cfml/control-flow/conditionals.md b/.ai/cfml/control-flow/conditionals.md index 24bd8e8621..da2a3c7948 100755 --- a/.ai/cfml/control-flow/conditionals.md +++ b/.ai/cfml/control-flow/conditionals.md @@ -216,5 +216,4 @@ if (!isDefined("form.username") || !len(trim(form.username))) { ## Related Concepts - [Loops](loops.md) - [Exception Handling](exception-handling.md) -- [Variables](../data-types/variables.md) -- [Operators](../syntax/operators.md) \ No newline at end of file +- [Variables](../data-types/variables.md) \ No newline at end of file diff --git a/.ai/cfml/control-flow/exception-handling.md b/.ai/cfml/control-flow/exception-handling.md index 74b4d098f5..fd09822911 100755 --- a/.ai/cfml/control-flow/exception-handling.md +++ b/.ai/cfml/control-flow/exception-handling.md @@ -276,7 +276,4 @@ function getDataWithFallback(id) { ``` ## Related Concepts -- [Conditionals](conditionals.md) -- [Logging](../advanced/logging.md) -- [Application Configuration](../advanced/application-cfc.md) -- [Best Practices](../best-practices/error-handling.md) \ No newline at end of file +- [Conditionals](conditionals.md) \ No newline at end of file diff --git a/.ai/cfml/data-types/arrays/array-creation.md b/.ai/cfml/data-types/arrays/array-creation.md index c1901888eb..85221a1702 100755 --- a/.ai/cfml/data-types/arrays/array-creation.md +++ b/.ai/cfml/data-types/arrays/array-creation.md @@ -108,5 +108,4 @@ meals[5] = "Snack"; ## Related Concepts - [Array Methods](array-methods.md) - [Array Iteration](array-iteration.md) -- [Variables](../variables.md) -- [Threading](../../advanced/threading.md) \ No newline at end of file +- [Variables](../variables.md) \ No newline at end of file diff --git a/.ai/cfml/data-types/arrays/array-iteration.md b/.ai/cfml/data-types/arrays/array-iteration.md index e7e296ff3d..9533b82f93 100755 --- a/.ai/cfml/data-types/arrays/array-iteration.md +++ b/.ai/cfml/data-types/arrays/array-iteration.md @@ -163,5 +163,4 @@ expensiveProducts = products - [Array Creation](array-creation.md) - [Array Methods](array-methods.md) - [Closures](../../advanced/closures.md) -- [Threading](../../advanced/threading.md) - [Control Flow](../../control-flow/loops.md) \ No newline at end of file diff --git a/.ai/cfml/data-types/arrays/array-methods.md b/.ai/cfml/data-types/arrays/array-methods.md index d32f31691d..081b88896e 100755 --- a/.ai/cfml/data-types/arrays/array-methods.md +++ b/.ai/cfml/data-types/arrays/array-methods.md @@ -149,5 +149,4 @@ combined = arr1.merge(arr2); // [1, 2, 3, 4, 5, 6] ## Related Concepts - [Array Creation](array-creation.md) - [Array Iteration](array-iteration.md) -- [Closures](../../advanced/closures.md) -- [Functional Programming](../../best-practices/functional-patterns.md) \ No newline at end of file +- [Closures](../../advanced/closures.md) \ No newline at end of file diff --git a/.ai/cfml/data-types/numbers/numeric-basics.md b/.ai/cfml/data-types/numbers/numeric-basics.md index 8c4eae355d..ffe1beb6f7 100755 --- a/.ai/cfml/data-types/numbers/numeric-basics.md +++ b/.ai/cfml/data-types/numbers/numeric-basics.md @@ -120,7 +120,4 @@ randomize(12345); - `precisionEvaluate()` has performance overhead ## Related Concepts -- [Mathematical Operators](../../syntax/operators.md) -- [Variables](../variables.md) -- [Type Conversion](../../best-practices/type-handling.md) -- [Precision Math](../../advanced/precision-calculations.md) \ No newline at end of file +- [Variables](../variables.md) \ No newline at end of file diff --git a/.ai/cfml/data-types/scopes/variables-scope.md b/.ai/cfml/data-types/scopes/variables-scope.md index d59f71d7bd..a43dfb615f 100755 --- a/.ai/cfml/data-types/scopes/variables-scope.md +++ b/.ai/cfml/data-types/scopes/variables-scope.md @@ -338,7 +338,5 @@ component { ## Related Concepts -- [Application Scope](application-scope.md) -- [Local Scope](local-scope.md) - [Component Basics](../../components/component-basics.md) - [Functions](../../components/functions.md) \ No newline at end of file diff --git a/.ai/cfml/data-types/strings/string-functions.md b/.ai/cfml/data-types/strings/string-functions.md index 089c99eae9..e167a3f78b 100755 --- a/.ai/cfml/data-types/strings/string-functions.md +++ b/.ai/cfml/data-types/strings/string-functions.md @@ -114,5 +114,4 @@ len(trim(myString)) == 0; // empty check pattern ## Related Concepts - [String Literals](string-literals.md) - [String Interpolation](string-interpolation.md) -- [Arrays](../arrays/array-methods.md) -- [Regular Expressions](../../advanced/regex-patterns.md) \ No newline at end of file +- [Arrays](../arrays/array-methods.md) \ No newline at end of file diff --git a/.ai/cfml/data-types/strings/string-literals.md b/.ai/cfml/data-types/strings/string-literals.md index cb264b6ef2..8618271b7f 100755 --- a/.ai/cfml/data-types/strings/string-literals.md +++ b/.ai/cfml/data-types/strings/string-literals.md @@ -67,5 +67,4 @@ greeting &= "!"; ## Related Concepts - [String Functions](string-functions.md) - [String Interpolation](string-interpolation.md) -- [Variables](../variables.md) -- [Operators](../../syntax/operators.md) \ No newline at end of file +- [Variables](../variables.md) \ No newline at end of file diff --git a/.ai/cfml/data-types/structures/struct-creation.md b/.ai/cfml/data-types/structures/struct-creation.md index 05efa99021..dc494cbd33 100755 --- a/.ai/cfml/data-types/structures/struct-creation.md +++ b/.ai/cfml/data-types/structures/struct-creation.md @@ -361,7 +361,6 @@ var validUser = createValidatedStruct(userSchema, { ## Related Concepts -- [Struct Methods](struct-methods.md) - [Array Creation](../arrays/array-creation.md) - [Variables Scope](../scopes/variables-scope.md) - [Object-Oriented Programming](../../components/component-basics.md) \ No newline at end of file diff --git a/.ai/cfml/data-types/structures/structure-basics.md b/.ai/cfml/data-types/structures/structure-basics.md index ae9e00b5ec..7a78528435 100755 --- a/.ai/cfml/data-types/structures/structure-basics.md +++ b/.ai/cfml/data-types/structures/structure-basics.md @@ -112,7 +112,5 @@ caseStruct = ${ Name: "Luis" }; - `weak` - Lucee only: weak references ## Related Concepts -- [Structure Methods](structure-methods.md) -- [Structure Iteration](structure-iteration.md) - [Variables](../variables.md) - [Variable Scopes](../variable-scopes.md) \ No newline at end of file diff --git a/.ai/cfml/data-types/variable-scopes.md b/.ai/cfml/data-types/variable-scopes.md index 256bdba249..8d9454f305 100755 --- a/.ai/cfml/data-types/variable-scopes.md +++ b/.ai/cfml/data-types/variable-scopes.md @@ -103,5 +103,4 @@ if (structKeyExists(form, "username")) { ## Related Concepts - [Variables](variables.md) - [Functions](../components/functions.md) -- [Components](../components/component-basics.md) -- [Threading](../advanced/threading.md) \ No newline at end of file +- [Components](../components/component-basics.md) \ No newline at end of file diff --git a/.ai/cfml/database/query-basics.md b/.ai/cfml/database/query-basics.md index abbc89bad1..d3484b2a78 100755 --- a/.ai/cfml/database/query-basics.md +++ b/.ai/cfml/database/query-basics.md @@ -205,7 +205,4 @@ qResults = queryExecute( - Monitor query execution times ## Related Concepts -- [Query Methods](query-methods.md) -- [Query of Queries](query-of-queries.md) -- [Exception Handling](../control-flow/exception-handling.md) -- [Application Configuration](../advanced/application-cfc.md) \ No newline at end of file +- [Exception Handling](../control-flow/exception-handling.md) \ No newline at end of file diff --git a/.ai/wheels/README.md b/.ai/wheels/README.md index 10bdebfb04..cafad5373e 100755 --- a/.ai/wheels/README.md +++ b/.ai/wheels/README.md @@ -4,10 +4,16 @@ This section contains documentation specific to the Wheels MVC framework for CFM ## Directory Structure +### [Cross-Engine Compatibility](./cross-engine-compatibility.md) +Critical reference for multi-engine development: +- Lucee vs Adobe CF runtime differences +- Database-specific gotchas (H2, MySQL, PostgreSQL, CockroachDB) +- Testing across engines locally with Docker + ### [CLI](./cli/) Command-line interface tools and generators: - Wheels CLI commands for scaffolding and development -- Code generation utilities +- Code generation utilities (including admin generator) - Development workflow tools ### [Communication](./communication/) @@ -28,6 +34,7 @@ MVC controller layer documentation: - Request handling and routing - Filters, callbacks, and lifecycle management - RESTful API development +- Server-Sent Events (SSE) for real-time communication ### [Core Concepts](./core-concepts/) Fundamental Wheels framework concepts: @@ -41,6 +48,7 @@ Data layer and ORM functionality: - Model associations and validations - Database migrations and schema management - Query building and optimization +- Database seeding (convention-based, idempotent) ### [Files](./files/) File handling and management: @@ -48,6 +56,13 @@ File handling and management: - Static asset management - File system interactions +### [Jobs](./jobs/) +Background job processing: +- Database-backed job queue with retry logic +- Priority queues with named queue support +- Job creation, enqueueing, and processing +- Queue statistics and management + ### [Patterns](./patterns/) Common development patterns in Wheels: - Design patterns and best practices diff --git a/.ai/wheels/channels/channels.md b/.ai/wheels/channels/channels.md new file mode 100644 index 0000000000..952bd9bd94 --- /dev/null +++ b/.ai/wheels/channels/channels.md @@ -0,0 +1,282 @@ +# Channels โ€” Pub/Sub for SSE + +Channels add a pub/sub abstraction on top of Wheels' existing Server-Sent Events (SSE) support. The existing low-level SSE API (`renderSSE`, `initSSEStream`, `sendSSEEvent`) continues to work unchanged โ€” channels are a higher-level layer built on top. + +## Quick Start + +```cfm +// Controller โ€” subscribe the client to a channel via SSE +function notifications() { + subscribeToChannel( + channel = "user.#params.userId#", + events = "notification,alert" + ); +} + +// Publish from anywhere โ€” model callback, job, controller, etc. +publish( + channel = "user.42", + event = "notification", + data = SerializeJSON({title: "New message", body: "Hello!"}) +); +``` + +```html + +#channelSSETag(channel="user.#params.userId#", route="notifications")# +``` + +## Configuration + +```cfm +// config/settings.cfm + +// Default adapter: "memory" (single server) or "database" (multi-server) +set(channelAdapter = "memory"); +``` + +## Adapters + +### Memory (Default) + +In-memory pub/sub using `ConcurrentHashMap`. Events are delivered instantly to subscribers on the same server. No persistence โ€” events are lost if no subscribers are connected. + +Best for: single-server deployments, development. + +### Database + +Persists events to a `wheels_events` table (auto-created on first use). Subscribers poll the table at configurable intervals. Events are retained for 60 minutes by default with automatic cleanup. + +Best for: multi-server deployments, event history/replay. + +```cfm +// Use database adapter globally +set(channelAdapter = "database"); + +// Or per-call +subscribeToChannel(channel = "updates", adapter = "database"); +publish(channel = "updates", event = "change", data = "...", adapter = "database"); +``` + +## API Reference + +### Global Functions + +#### `publish(channel, event, data, adapter)` + +Publish an event to a channel. Available anywhere global helpers are accessible (controllers, models, jobs, views). + +| Parameter | Type | Default | Description | +|-----------|------|---------|-------------| +| `channel` | string | required | Channel name (e.g. `"user.42"`, `"orders"`) | +| `event` | string | required | Event type (e.g. `"notification"`, `"update"`) | +| `data` | string | required | Event data (typically JSON) | +| `adapter` | string | `""` | `"memory"` or `"database"` (defaults to `channelAdapter` setting) | + +Returns struct: `{id, channel, event, subscriberCount, timestamp}` (memory) or `{id, channel, event, persisted}` (database). + +### Controller Functions + +#### `subscribeToChannel(channel, events, lastEventId, adapter, pollInterval, timeout, heartbeatInterval)` + +Open a long-lived SSE connection that streams events from a channel to the client. Automatically detects `Last-Event-ID` from request headers for resume support. + +| Parameter | Type | Default | Description | +|-----------|------|---------|-------------| +| `channel` | string | required | Channel to subscribe to | +| `events` | string | `""` | Comma-delimited event types to filter (empty = all) | +| `lastEventId` | string | `""` | Resume from this event ID (auto-detected from header) | +| `adapter` | string | `""` | Override adapter type | +| `pollInterval` | numeric | `2` | Seconds between polls (database adapter only) | +| `timeout` | numeric | `300` | Max connection duration in seconds | +| `heartbeatInterval` | numeric | `15` | Seconds between keep-alive pings | + +#### `channelSSETag(channel, route, controller, action, events)` + +Generate a ` +'; + + case "migration-indexes": + return 't.index("email");' & chr(10) & + 't.index(["last_name", "first_name"]);' & chr(10) & + 't.index("email", unique=true);' & chr(10) & + 't.index("user_id");'; + + case "seed-data": + return 'execute("INSERT INTO users (name, email) VALUES (''Admin'', ''admin@example.com'')");"'; + + case "constraints": + return 't.references("user_id", "users");' & chr(10) & + 't.references("category_id", "categories");'; + + default: + return "Code snippet not found"; + } + } + + /** + * Create custom snippet + */ + private function createCustomSnippet(required string name) { + detailOutput.header("Creating Custom Code Snippet"); + + var snippetDir = getCWD() & "/app/snippets/" & arguments.name; + + if (directoryExists(snippetDir)) { + detailOutput.error("Code snippet '#arguments.name#' already exists"); + setExitCode(1); + return; + } + + // Create directory + directoryCreate(snippetDir, true); + + // Create basic template file + var templateContent = '// Custom code snippet: #arguments.name#' & chr(10) & + '// Add your code here'; + + fileWrite(snippetDir & "/template.txt", templateContent); + + detailOutput.create("Created: #snippetDir#"); + detailOutput.success("Custom code snippet '#arguments.name#' created successfully!"); + + detailOutput.nextSteps([ + "Edit: #snippetDir#/template.txt", + "Use: wheels g code #arguments.name#" + ]); + } + + /** + * Show customization options + */ + private function showCustomizationOptions() { + detailOutput.header("Customization Options"); + detailOutput.output("You can customize code snippets by:"); + detailOutput.output(" 1. Creating custom code snippets with --create"); + detailOutput.output(" 2. Saving code snippets to files with --output=file"); + detailOutput.output(" 3. Filtering by category with --category"); + } +} diff --git a/cli/src/commands/wheels/generate/controller.cfc b/cli/src/commands/wheels/generate/controller.cfc index d83cd3bc42..dce1a89ceb 100644 --- a/cli/src/commands/wheels/generate/controller.cfc +++ b/cli/src/commands/wheels/generate/controller.cfc @@ -56,7 +56,13 @@ component aliases="wheels g controller" extends="../base" { if (hasCustomActions) { // HIGHEST PRIORITY: Custom actions specified - actionList = listToArray(arguments.actions); + actionList = listToArray(trim(arguments.actions)); + // Remove empty elements and trim each action + actionList = actionList.map(function(action) { + return trim(action); + }).filter(function(action) { + return len(action) > 0; + }); } else if (arguments.crud) { if (arguments.api) { // API: No form actions (new, edit) @@ -144,4 +150,4 @@ component aliases="wheels g controller" extends="../base" { setExitCode(1); } } -} +} \ No newline at end of file diff --git a/cli/src/commands/wheels/generate/helper.cfc b/cli/src/commands/wheels/generate/helper.cfc index 3296524168..db55329e27 100644 --- a/cli/src/commands/wheels/generate/helper.cfc +++ b/cli/src/commands/wheels/generate/helper.cfc @@ -287,17 +287,17 @@ result = #functionList[1]#("some input"); private string function generateHighlightFunction() { var content = chr(9) & chr(9) & "// Highlight search terms in text" & chr(10); - content &= chr(9) & chr(9) & "local.searchTerm = arguments.options.term ?: """";" & chr(10); + content &= chr(9) & chr(9) & "local.term = arguments.options.term ?: """";" & chr(10); content &= chr(9) & chr(9) & "local.highlightClass = arguments.options.class ?: ""highlight"";" & chr(10); content &= chr(10); - content &= chr(9) & chr(9) & "if (!len(local.searchTerm)) {" & chr(10); + content &= chr(9) & chr(9) & "if (!len(local.term)) {" & chr(10); content &= chr(9) & chr(9) & chr(9) & "return arguments.value;" & chr(10); content &= chr(9) & chr(9) & "}" & chr(10); content &= chr(10); content &= chr(9) & chr(9) & "return reReplaceNoCase(" & chr(10); content &= chr(9) & chr(9) & chr(9) & "arguments.value," & chr(10); - content &= chr(9) & chr(9) & chr(9) & """(#local.searchTerm#)""," & chr(10); - content &= chr(9) & chr(9) & chr(9) & """\\1""," & chr(10); + content &= chr(9) & chr(9) & chr(9) & """('' & local.term & '')""," & chr(10); + content &= chr(9) & chr(9) & chr(9) & """\\1""," & chr(10); content &= chr(9) & chr(9) & chr(9) & """all""" & chr(10); content &= chr(9) & chr(9) & ");" & chr(10); return content; diff --git a/cli/src/commands/wheels/generate/model.cfc b/cli/src/commands/wheels/generate/model.cfc index 69db36bfea..c2c98d0514 100644 --- a/cli/src/commands/wheels/generate/model.cfc +++ b/cli/src/commands/wheels/generate/model.cfc @@ -131,7 +131,8 @@ component aliases='wheels g model' extends="../base" { name = arguments.name, properties = parsedProperties, baseDirectory = getCWD(), - tableName = arguments.tableName + tableName = arguments.tableName, + primaryKey = arguments.primaryKey ); } else { var actualTableName = len(arguments.tableName) ? arguments.tableName : helpers.pluralize(lCase(arguments.name)); @@ -176,7 +177,13 @@ component aliases='wheels g model' extends="../base" { var properties = []; if (len(arguments.propertiesString)) { - var propList = listToArray(arguments.propertiesString); + var propList = listToArray(trim(arguments.propertiesString)); + // Remove empty elements and trim each property + propList = propList.map(function(prop) { + return trim(prop); + }).filter(function(prop) { + return len(prop) > 0; + }); for (var prop in propList) { var parts = listToArray(prop, ":"); @@ -264,4 +271,4 @@ component aliases='wheels g model' extends="../base" { return arguments.properties; } -} +} \ No newline at end of file diff --git a/cli/src/commands/wheels/generate/property.cfc b/cli/src/commands/wheels/generate/property.cfc index 09d1c32129..10c586c4a2 100644 --- a/cli/src/commands/wheels/generate/property.cfc +++ b/cli/src/commands/wheels/generate/property.cfc @@ -58,7 +58,7 @@ component aliases='wheels g property' extends="../base" { arguments = reconstructArgs( argStruct=arguments, allowedValues={ - dataType: ["biginteger", "binary", "boolean", "date", "datetime", "decimal", "float", "integer", "string", "limit", "text", "time", "timestamp", "uuid"] + dataType: ["biginteger", "binary", "boolean", "date", "datetime", "decimal", "float", "integer", "string", "text", "time", "timestamp", "uuid"] } ); diff --git a/cli/src/commands/wheels/generate/scaffold.cfc b/cli/src/commands/wheels/generate/scaffold.cfc index 3841c4e8a7..10312875b0 100644 --- a/cli/src/commands/wheels/generate/scaffold.cfc +++ b/cli/src/commands/wheels/generate/scaffold.cfc @@ -38,7 +38,7 @@ component aliases="wheels g scaffold, wheels g resource, wheels generate resourc // Custom validation for properties parameter format (name:type,name2:type2) if (len(trim(arguments.properties))) { - var validTypes = ["biginteger", "binary", "boolean", "date", "datetime", "decimal", "float", "integer", "string", "limit", "text", "time", "timestamp", "uuid"]; + var validTypes = ["biginteger", "binary", "boolean", "date", "datetime", "decimal", "float", "integer", "string", "text", "time", "timestamp", "uuid"]; var properties = listToArray(arguments.properties, ","); var invalidTypes = []; @@ -101,13 +101,13 @@ component aliases="wheels g scaffold, wheels g resource, wheels generate resourc // Run migrations if requested if (arguments.migrate) { detailOutput.invoke("dbmigrate"); - command('wheels dbmigrate up').run(); + command('wheels dbmigrate latest').run(); } else if (!arguments.api) { // Only ask to migrate in interactive mode try { if (confirm("Would you like to run migrations now? [y/n]")) { detailOutput.invoke("dbmigrate"); - command('wheels dbmigrate up').run(); + command('wheels dbmigrate latest').run(); } } catch (any e) { // Skip if non-interactive diff --git a/cli/src/commands/wheels/generate/seed.cfc b/cli/src/commands/wheels/generate/seed.cfc new file mode 100644 index 0000000000..50a1aa760d --- /dev/null +++ b/cli/src/commands/wheels/generate/seed.cfc @@ -0,0 +1,177 @@ +/** + * Generate seed files for database seeding. + * + * Creates app/db/seeds.cfm (main seed file) and optionally environment-specific + * seed files in app/db/seeds/. + * + * {code:bash} + * wheels generate seed # Create main seeds.cfm + * wheels generate seed --environment=development # Create environment-specific seed file + * wheels generate seed --all # Create main + development + production stubs + * {code} + */ +component aliases='wheels g seed' extends="../base" { + + property name="detailOutput" inject="DetailOutputService@wheels-cli"; + + /** + * @environment Create an environment-specific seed file (e.g., development, production) + * @all Create main seeds.cfm plus development and production stubs + * @force Overwrite existing seed files + */ + function run( + string environment="", + boolean all=false, + boolean force=false + ) { + requireWheelsApp(getCWD()); + + detailOutput.header("Seed File Generation"); + + local.dbDir = fileSystemUtil.resolvePath("app/db"); + local.seedsDir = local.dbDir & "/seeds"; + + // Ensure app/db/ directory exists + if (!directoryExists(local.dbDir)) { + directoryCreate(local.dbDir); + detailOutput.create("app/db/"); + } + + local.filesCreated = []; + + // Create main seeds.cfm + if (!len(trim(arguments.environment)) || arguments.all) { + local.mainFile = local.dbDir & "/seeds.cfm"; + if (!fileExists(local.mainFile) || arguments.force) { + file action='write' file='#local.mainFile#' mode='777' output='#getMainSeedTemplate()#'; + arrayAppend(local.filesCreated, "app/db/seeds.cfm"); + detailOutput.create("app/db/seeds.cfm"); + } else { + print.yellowLine(" app/db/seeds.cfm already exists (use --force to overwrite)"); + } + } + + // Ensure app/db/seeds/ directory exists for environment files + if (len(trim(arguments.environment)) || arguments.all) { + if (!directoryExists(local.seedsDir)) { + directoryCreate(local.seedsDir); + detailOutput.create("app/db/seeds/"); + } + } + + // Create environment-specific file(s) + if (arguments.all) { + for (local.env in ["development", "production"]) { + local.envFile = local.seedsDir & "/" & local.env & ".cfm"; + if (!fileExists(local.envFile) || arguments.force) { + file action='write' file='#local.envFile#' mode='777' output='#getEnvironmentSeedTemplate(local.env)#'; + arrayAppend(local.filesCreated, "app/db/seeds/" & local.env & ".cfm"); + detailOutput.create("app/db/seeds/" & local.env & ".cfm"); + } else { + print.yellowLine(" app/db/seeds/#local.env#.cfm already exists"); + } + } + } else if (len(trim(arguments.environment))) { + local.envFile = local.seedsDir & "/" & arguments.environment & ".cfm"; + if (!fileExists(local.envFile) || arguments.force) { + file action='write' file='#local.envFile#' mode='777' output='#getEnvironmentSeedTemplate(arguments.environment)#'; + arrayAppend(local.filesCreated, "app/db/seeds/" & arguments.environment & ".cfm"); + detailOutput.create("app/db/seeds/" & arguments.environment & ".cfm"); + } else { + print.yellowLine(" app/db/seeds/#arguments.environment#.cfm already exists"); + } + } + + if (arrayLen(local.filesCreated)) { + detailOutput.success("Seed files created successfully!"); + + var nextSteps = [ + "Edit your seed files to add seed data", + "Run seeds: wheels db:seed", + "Run for specific environment: wheels db:seed --environment=development" + ]; + detailOutput.nextSteps(nextSteps); + } else { + print.yellowLine("No new files created."); + } + } + + /** + * Generate the main seeds.cfm template content + */ + private string function getMainSeedTemplate() { + var content = ' + + + + +// Example: Seed default roles +// seedOnce(modelName="Role", uniqueProperties="name", properties={ +// name: "admin", +// description: "Administrator with full access" +// }); +// +// seedOnce(modelName="Role", uniqueProperties="name", properties={ +// name: "member", +// description: "Regular member" +// }); + +// Example: Seed using model methods directly (not idempotent โ€” use for one-time setup) +// if (model("Setting").count() == 0) { +// model("Setting").create(key="siteName", value="My App"); +// model("Setting").create(key="siteEmail", value="admin@example.com"); +// } + +'; + return content; + } + + /** + * Generate an environment-specific seed template + */ + private string function getEnvironmentSeedTemplate(required string environment) { + var content = ' + + + +'; + + if (arguments.environment == "development") { + content &= ' +// Example: Create test users for development +// seedOnce(modelName="User", uniqueProperties="email", properties={ +// firstName: "Dev", +// lastName: "User", +// email: "dev@example.com", +// role: "admin" +// }); +// +// seedOnce(modelName="User", uniqueProperties="email", properties={ +// firstName: "Test", +// lastName: "User", +// email: "test@example.com", +// role: "member" +// }); +'; + } else if (arguments.environment == "production") { + content &= ' +// Production seeds should be minimal โ€” only essential reference data. +// Example: Ensure critical lookup data exists +// seedOnce(modelName="Role", uniqueProperties="name", properties={ +// name: "superadmin", +// description: "Production superadmin" +// }); +'; + } else { + content &= ' +// Add #arguments.environment#-specific seed data here. +// Use seedOnce() for idempotent records. +'; + } + + content &= ' +'; + return content; + } + +} diff --git a/cli/src/commands/wheels/generate/snippets.cfc b/cli/src/commands/wheels/generate/snippets.cfc index 3919eebd66..564c902fd7 100644 --- a/cli/src/commands/wheels/generate/snippets.cfc +++ b/cli/src/commands/wheels/generate/snippets.cfc @@ -1,437 +1,23 @@ /** - * Generate code snippets for common patterns - * - * Examples: - * wheels g snippets auth-filter - * wheels g snippets --list - * wheels g snippets --category=model + * Copies the template snippets to the application. */ -component aliases="wheels g snippets" extends="../base" { +component aliases="wheels g snippets" extends="../base"{ - property name="detailOutput" inject="DetailOutputService@wheels-cli"; + function run() + { + requireWheelsApp(getCWD()); + arguments.directory = fileSystemUtil.resolvePath( 'app' ); - /** - * @pattern.hint Snippet pattern name - * @list.hint Show all available snippets - * @category.hint Filter by category (authentication, model, controller, view, database) - * @output.hint Output format (console or file) - * @path.hint Output path (required for file output) - * @customize.hint Create custom snippet - * @create.hint Create new snippet template - * @force.hint Overwrite existing files - */ - function run( - required string pattern, - boolean list = false, - string category = "", - string output = "console", - string path = "", - boolean customize = false, - boolean create = false, - boolean force = false - ) { - requireWheelsApp(getCWD()); - arguments = reconstructArgs( - argStruct=arguments, - allowedValues={ - output: ["console", "file"], - category: ["authentication", "model", "controller", "view", "database"] - } - ); + print.line('Starting snippet generation...').toConsole(); - if (arguments.list) { - return listSnippets(arguments.category); - } - - if (arguments.create) { - return createCustomSnippet(arguments.pattern); - } - - if (arguments.customize) { - return showCustomizationOptions(); - } - - if (!len(arguments.pattern)) { - detailOutput.error("Pattern name is required"); - detailOutput.getPrint().line("Usage: wheels g snippets "); - detailOutput.getPrint().line("Run 'wheels g snippets --list' to see available patterns"); - setExitCode(1); - return; - } - - var snippet = getSnippetByName(arguments.pattern); - if (!structCount(snippet)) { - detailOutput.error("Snippet '#arguments.pattern#' not found"); - detailOutput.getPrint().line("Run 'wheels g snippets --list' to see available patterns"); - setExitCode(1); - return; - } - - if (arguments.output == "file") { - if (!len(arguments.path)) { - detailOutput.error("--path is required when using --output=file"); - setExitCode(1); - return; - } - writeSnippetToFile(snippet, arguments.path, arguments.force); - } else { - printSnippet(snippet); - } - } - - /** - * List available snippets - */ - private function listSnippets(string category = "") { - var snippets = getAvailableSnippets(); - var categories = {}; - - for (var snippet in snippets) { - if (len(arguments.category) && snippet.category != arguments.category) { - continue; - } - if (!structKeyExists(categories, snippet.category)) { - categories[snippet.category] = []; - } - arrayAppend(categories[snippet.category], snippet); - } - - detailOutput.header("Available Snippets"); - - var categoryOrder = ["Authentication", "Model", "Controller", "View", "Database"]; - for (var cat in categoryOrder) { - var key = lCase(cat); - if (structKeyExists(categories, key)) { - detailOutput.getPrint().line(""); - detailOutput.getPrint().boldLine("#cat#:"); - for (var snippet in categories[key]) { - detailOutput.getPrint().line(" - #snippet.name# - #snippet.description#"); - } - } - } - - detailOutput.getPrint().line(""); - detailOutput.getPrint().line(""); - detailOutput.nextSteps([ - "Generate a snippet: wheels g snippets " - ]); - } - - /** - * Print snippet to console - */ - private function printSnippet(required struct snippet) { - detailOutput.header("Generating Snippet: #arguments.snippet.name#"); - detailOutput.getPrint().line(""); - - var content = getSnippetContent(arguments.snippet); - detailOutput.getPrint().line(content); - detailOutput.getPrint().line(""); - - detailOutput.success("Snippet '#arguments.snippet.name#' generated successfully!"); - } - - /** - * Write snippet to file - */ - private function writeSnippetToFile(required struct snippet, required string path, boolean force = false) { - // Resolve path relative to application directory - var resolvedPath = fileSystemUtil.resolvePath(arguments.path); - - if (fileExists(resolvedPath) && !arguments.force) { - detailOutput.error("File already exists: #resolvedPath#"); - detailOutput.getPrint().line("Use --force to overwrite"); - setExitCode(1); - return; - } - - var content = getSnippetContent(arguments.snippet); - - // Create directory if needed - var dir = getDirectoryFromPath(resolvedPath); - if (!directoryExists(dir)) { - directoryCreate(dir, true); - } - - fileWrite(resolvedPath, content); - detailOutput.create("Created: #resolvedPath#"); + // Validate the provided directory + if (!directoryExists(arguments.directory)) { + error('[#arguments.directory#] can''t be found. Are you running this command from your application root?'); } - /** - * Get available snippets - */ - private function getAvailableSnippets() { - return [ - {name: "login-form", category: "authentication", description: "Login form with remember me"}, - {name: "auth-filter", category: "authentication", description: "Authentication filter"}, - {name: "password-reset", category: "authentication", description: "Password reset flow"}, - {name: "user-registration", category: "authentication", description: "User registration with validation"}, - {name: "soft-delete", category: "model", description: "Soft delete implementation"}, - {name: "audit-trail", category: "model", description: "Audit trail with timestamps"}, - {name: "sluggable", category: "model", description: "URL-friendly slugs"}, - {name: "versionable", category: "model", description: "Version tracking"}, - {name: "searchable", category: "model", description: "Full-text search"}, - {name: "crud-actions", category: "controller", description: "Complete CRUD actions"}, - {name: "api-controller", category: "controller", description: "JSON API controller"}, - {name: "nested-resource", category: "controller", description: "Nested resource controller"}, - {name: "admin-controller", category: "controller", description: "Admin area controller"}, - {name: "form-with-errors", category: "view", description: "Form with error handling"}, - {name: "pagination-links", category: "view", description: "Pagination navigation"}, - {name: "search-form", category: "view", description: "Search form with filters"}, - {name: "ajax-form", category: "view", description: "AJAX form submission"}, - {name: "migration-indexes", category: "database", description: "Common index patterns"}, - {name: "seed-data", category: "database", description: "Database seeding"}, - {name: "constraints", category: "database", description: "Foreign key constraints"} - ]; - } - - /** - * Get snippet by name - */ - private function getSnippetByName(required string name) { - var snippets = getAvailableSnippets(); - for (var snippet in snippets) { - if (snippet.name == arguments.name) { - return snippet; - } - } - return {}; - } - - /** - * Get snippet content - */ - private function getSnippetContent(required struct snippet) { - switch (arguments.snippet.name) { - case "login-form": - return '##startFormTag(action="create")##' & chr(10) & - ' ##textField(objectName="user", property="email", label="Email")##' & chr(10) & - ' ##passwordField(objectName="user", property="password", label="Password")##' & chr(10) & - ' ##checkBox(objectName="user", property="rememberMe", label="Remember me")##' & chr(10) & - ' ##submitTag(value="Login")##' & chr(10) & - '##endFormTag()##'; - - case "auth-filter": - return 'function init() {' & chr(10) & - ' filters(through="authenticate", except="new,create");' & chr(10) & - '}' & chr(10) & chr(10) & - 'private function authenticate() {' & chr(10) & - ' if (!StructKeyExists(session, "userId")) {' & chr(10) & - ' redirectTo(route="login");' & chr(10) & - ' }' & chr(10) & - '}'; - - case "password-reset": - return 'function requestReset() {' & chr(10) & - ' user = model("User").findOne(where="email=''##params.email##''");' & chr(10) & - ' if (IsObject(user)) {' & chr(10) & - ' token = Hash(CreateUUID());' & chr(10) & - ' user.update(resetToken=token, resetExpiresAt=DateAdd("h", 1, Now()));' & chr(10) & - ' // Send email with token' & chr(10) & - ' }' & chr(10) & - '}'; - - case "user-registration": - return '##startFormTag(action="create")##' & chr(10) & - ' ##textField(objectName="user", property="firstName", label="First Name")##' & chr(10) & - ' ##textField(objectName="user", property="email", label="Email")##' & chr(10) & - ' ##passwordField(objectName="user", property="password", label="Password")##' & chr(10) & - ' ##submitTag(value="Register")##' & chr(10) & - '##endFormTag()##'; + ensureSnippetTemplatesExist(); - case "soft-delete": - return 'function init() {' & chr(10) & - ' property(name="deletedAt", sql="deleted_at");' & chr(10) & - ' beforeDelete("softDelete");' & chr(10) & - '}' & chr(10) & chr(10) & - 'private function softDelete() {' & chr(10) & - ' this.deletedAt = Now();' & chr(10) & - ' this.save(validate=false, callbacks=false);' & chr(10) & - ' return false;' & chr(10) & - '}'; + print.line('Snippet successfully generated in the /app/snippets folder.').toConsole(); + } - case "audit-trail": - return 'function init() {' & chr(10) & - ' property(name="createdBy", sql="created_by");' & chr(10) & - ' property(name="updatedBy", sql="updated_by");' & chr(10) & - ' beforeSave("setAuditFields");' & chr(10) & - '}' & chr(10) & chr(10) & - 'private function setAuditFields() {' & chr(10) & - ' if (StructKeyExists(session, "userId")) {' & chr(10) & - ' if (this.isNew()) this.createdBy = session.userId;' & chr(10) & - ' this.updatedBy = session.userId;' & chr(10) & - ' }' & chr(10) & - '}'; - - case "sluggable": - return 'function init() {' & chr(10) & - ' property(name="slug");' & chr(10) & - ' beforeSave("generateSlug");' & chr(10) & - '}' & chr(10) & chr(10) & - 'private function generateSlug() {' & chr(10) & - ' if (!len(this.slug) && len(this.title)) {' & chr(10) & - ' this.slug = lCase(reReplace(this.title, "[^a-zA-Z0-9]", "-", "all"));' & chr(10) & - ' }' & chr(10) & - '}'; - - case "versionable": - return 'function init() {' & chr(10) & - ' property(name="version", default=1);' & chr(10) & - ' beforeUpdate("incrementVersion");' & chr(10) & - '}' & chr(10) & chr(10) & - 'private function incrementVersion() {' & chr(10) & - ' this.version = this.version + 1;' & chr(10) & - '}'; - - case "searchable": - return 'function search(required string query) {' & chr(10) & - ' return findAll(where="title LIKE ''%##arguments.query##%'' OR content LIKE ''%##arguments.query##%''");' & chr(10) & - '}'; - - case "crud-actions": - return 'function index() {' & chr(10) & - ' users = model("User").findAll();' & chr(10) & - '}' & chr(10) & chr(10) & - 'function show() {' & chr(10) & - ' user = model("User").findByKey(params.key);' & chr(10) & - '}' & chr(10) & chr(10) & - 'function create() {' & chr(10) & - ' user = model("User").create(params.user);' & chr(10) & - ' if (user.valid()) {' & chr(10) & - ' redirectTo(route="user", key=user.id);' & chr(10) & - ' } else {' & chr(10) & - ' renderView(action="new");' & chr(10) & - ' }' & chr(10) & - '}'; - - case "api-controller": - return 'function init() {' & chr(10) & - ' provides("json");' & chr(10) & - '}' & chr(10) & chr(10) & - 'function index() {' & chr(10) & - ' users = model("User").findAll();' & chr(10) & - ' renderWith(data={users=users});' & chr(10) & - '}'; - - case "nested-resource": - return 'function init() {' & chr(10) & - ' filters(through="findParent");' & chr(10) & - '}' & chr(10) & chr(10) & - 'private function findParent() {' & chr(10) & - ' user = model("User").findByKey(params.userId);' & chr(10) & - '}'; - - case "admin-controller": - return 'function init() {' & chr(10) & - ' filters(through="requireAdmin");' & chr(10) & - '}' & chr(10) & chr(10) & - 'private function requireAdmin() {' & chr(10) & - ' if (!currentUser().isAdmin()) {' & chr(10) & - ' redirectTo(route="home");' & chr(10) & - ' }' & chr(10) & - '}'; - - case "form-with-errors": - return '##errorMessagesFor("user")##' & chr(10) & chr(10) & - '##startFormTag(action="create")##' & chr(10) & - ' ##textField(objectName="user", property="firstName", label="First Name")##' & chr(10) & - ' ' & chr(10) & - ' ##user.errors("firstName").get()##' & chr(10) & - ' ' & chr(10) & - ' ##submitTag(value="Submit")##' & chr(10) & - '##endFormTag()##'; - - case "pagination-links": - return '' & chr(10) & - ' ' & chr(10) & - ''; - - case "search-form": - return '##startFormTag(method="get")##' & chr(10) & - ' ##textField(name="q", value=params.q, placeholder="Search...")##' & chr(10) & - ' ##submitTag(value="Search")##' & chr(10) & - '##endFormTag()##'; - - case "ajax-form": - return '##startFormTag(action="create", id="userForm")##' & chr(10) & - ' ##textField(objectName="user", property="name")##' & chr(10) & - ' ##submitTag(value="Submit")##' & chr(10) & - '##endFormTag()##' & chr(10) & chr(10) & - ''; - - case "migration-indexes": - return 't.index("email");' & chr(10) & - 't.index(["last_name", "first_name"]);' & chr(10) & - 't.index("email", unique=true);' & chr(10) & - 't.index("user_id");'; - - case "seed-data": - return 'execute("INSERT INTO users (name, email) VALUES (''Admin'', ''admin@example.com'')");"'; - - case "constraints": - return 't.references("user_id", "users");' & chr(10) & - 't.references("category_id", "categories");'; - - default: - return "Snippet not found"; - } - } - - /** - * Create custom snippet - */ - private function createCustomSnippet(required string name) { - detailOutput.header("Creating Custom Snippet"); - - var snippetDir = getCWD() & "/app/snippets/" & arguments.name; - - if (directoryExists(snippetDir)) { - detailOutput.error("Snippet '#arguments.name#' already exists"); - setExitCode(1); - return; - } - - // Create directory - directoryCreate(snippetDir, true); - - // Create basic template file - var templateContent = '// Custom snippet: #arguments.name#' & chr(10) & - '// Add your code here'; - - fileWrite(snippetDir & "/template.txt", templateContent); - - detailOutput.create("Created: #snippetDir#"); - detailOutput.success("Custom snippet '#arguments.name#' created successfully!"); - - detailOutput.nextSteps([ - "Edit: #snippetDir#/template.txt", - "Use: wheels g snippets #arguments.name#" - ]); - } - - /** - * Show customization options - */ - private function showCustomizationOptions() { - detailOutput.header("Customization Options"); - detailOutput.getPrint().line("You can customize snippets by:"); - detailOutput.getPrint().line(" 1. Creating custom snippets with --create"); - detailOutput.getPrint().line(" 2. Saving snippets to files with --output=file"); - detailOutput.getPrint().line(" 3. Filtering by category with --category"); - } } \ No newline at end of file diff --git a/cli/src/commands/wheels/generate/test.cfc b/cli/src/commands/wheels/generate/test.cfc index daa7b6d674..5073950f05 100644 --- a/cli/src/commands/wheels/generate/test.cfc +++ b/cli/src/commands/wheels/generate/test.cfc @@ -236,7 +236,7 @@ component aliases='wheels g test' extends="../base" { boolean crud = false, boolean factory = false ) { - var content = 'component extends="wheels.Testbox" {' & chr(10) & chr(10); + var content = 'component extends="wheels.WheelsTest" {' & chr(10) & chr(10); content &= chr(9) & 'function run() {' & chr(10) & chr(10); content &= chr(9) & chr(9) & 'describe("#obj.objectNameSingularC# Model", function() {' & chr(10) & chr(10); @@ -347,7 +347,7 @@ component aliases='wheels g test' extends="../base" { boolean crud = false, boolean mock = false ) { - var content = 'component extends="wheels.Testbox" {' & chr(10) & chr(10); + var content = 'component extends="wheels.WheelsTest" {' & chr(10) & chr(10); content &= chr(9) & 'function beforeAll() {' & chr(10); content &= chr(9) & chr(9) & 'variables.baseUrl = "http://localhost:8080";' & chr(10); content &= chr(9) & '}' & chr(10) & chr(10); @@ -421,7 +421,7 @@ component aliases='wheels g test' extends="../base" { * Generate view test */ private function generateViewTest(required struct obj, required string viewName) { - var content = 'component extends="wheels.Testbox" {' & chr(10) & chr(10); + var content = 'component extends="wheels.WheelsTest" {' & chr(10) & chr(10); content &= chr(9) & 'function beforeAll() {' & chr(10); content &= chr(9) & chr(9) & 'variables.baseUrl = "http://localhost:8080";' & chr(10); content &= chr(9) & '}' & chr(10) & chr(10); @@ -453,7 +453,7 @@ component aliases='wheels g test' extends="../base" { * Generate unit test */ private function generateUnitTest(required struct obj, boolean mock = false) { - var content = 'component extends="wheels.Testbox" {' & chr(10) & chr(10); + var content = 'component extends="wheels.WheelsTest" {' & chr(10) & chr(10); content &= chr(9) & 'function run() {' & chr(10) & chr(10); content &= chr(9) & chr(9) & 'describe("#obj.objectNameSingularC# Unit Tests", function() {' & chr(10) & chr(10); @@ -500,7 +500,7 @@ component aliases='wheels g test' extends="../base" { boolean crud = false, boolean factory = false ) { - var content = 'component extends="wheels.Testbox" {' & chr(10) & chr(10); + var content = 'component extends="wheels.WheelsTest" {' & chr(10) & chr(10); content &= chr(9) & 'function beforeAll() {' & chr(10); content &= chr(9) & chr(9) & 'variables.baseUrl = "http://localhost:8080";' & chr(10); content &= chr(9) & '}' & chr(10) & chr(10); @@ -558,7 +558,7 @@ component aliases='wheels g test' extends="../base" { boolean crud = false, boolean mock = false ) { - var content = 'component extends="wheels.Testbox" {' & chr(10) & chr(10); + var content = 'component extends="wheels.WheelsTest" {' & chr(10) & chr(10); content &= chr(9) & 'function beforeAll() {' & chr(10); content &= chr(9) & chr(9) & 'variables.apiUrl = "http://localhost:8080/api";' & chr(10); content &= chr(9) & '}' & chr(10) & chr(10); diff --git a/cli/src/commands/wheels/generate/view.cfc b/cli/src/commands/wheels/generate/view.cfc index 9f6c0ff3c0..3011c61acb 100644 --- a/cli/src/commands/wheels/generate/view.cfc +++ b/cli/src/commands/wheels/generate/view.cfc @@ -40,8 +40,10 @@ component aliases='wheels g view' extends="../base" { ); var obj = helpers.getNameVariants(listLast( arguments.objectName, '/\' )); var viewdirectory = fileSystemUtil.resolvePath( "app/views" ); + var viewFolderName = lcase(listLast( arguments.objectName, '/\' )); // Build path from resolved viewdirectory to avoid conflicts with existing directories (e.g., tests/) - var directory = viewdirectory & "/" & obj.objectNamePlural; + // Use the provided objectName directly instead of forcing plural + var directory = viewdirectory & "/" & viewFolderName; // Handle multiple views if comma-separated list is provided var viewNames = listToArray(arguments.name); @@ -57,11 +59,11 @@ component aliases='wheels g view' extends="../base" { return; } - // Validate views subdirectory, create if doesnt' exist - if( !directoryExists( directory ) ) { - directoryCreate(directory); - detailOutput.create("app/views/" & obj.objectNamePlural); - } + // Validate views subdirectory, create if doesnt' exist + if( !directoryExists( directory ) ) { + directoryCreate(directory); + detailOutput.create("app/views/" & viewFolderName); + } //Copy template files to the application folder if they do not exist there ensureSnippetTemplatesExist(); @@ -104,13 +106,13 @@ component aliases='wheels g view' extends="../base" { if(fileExists(viewPath)){ if(arguments.force || confirm( '#viewName# already exists in target directory. Do you want to overwrite? [y/n]' ) ) { - detailOutput.update("app/views/" & obj.objectNamePlural & "/" & viewName); + detailOutput.update("app/views/" & viewFolderName & "/" & viewName); } else { - detailOutput.skip("app/views/" & obj.objectNamePlural & "/" & viewName); + detailOutput.skip("app/views/" & viewFolderName & "/" & viewName); continue; } } else { - detailOutput.create("app/views/" & obj.objectNamePlural & "/" & viewName); + detailOutput.create("app/views/" & viewFolderName & "/" & viewName); } file action='write' file='#viewPath#' mode ='777' output='#trim( viewContent )#'; arrayAppend(generatedViews, viewName); @@ -121,9 +123,9 @@ component aliases='wheels g view' extends="../base" { var nextSteps = []; if (arrayLen(generatedViews) == 1) { - arrayAppend(nextSteps, "Review the generated view at app/views/" & obj.objectNamePlural & "/" & generatedViews[1]); + arrayAppend(nextSteps, "Review the generated view at app/views/" & viewFolderName & "/" & generatedViews[1]); } else { - arrayAppend(nextSteps, "Review the generated views in app/views/" & obj.objectNamePlural & "/"); + arrayAppend(nextSteps, "Review the generated views in app/views/" & viewFolderName & "/"); } arrayAppend(nextSteps, "Customize the HTML content as needed"); diff --git a/cli/src/commands/wheels/get/settings.cfc b/cli/src/commands/wheels/get/settings.cfc index 0378a70349..ece7af5edd 100644 --- a/cli/src/commands/wheels/get/settings.cfc +++ b/cli/src/commands/wheels/get/settings.cfc @@ -207,39 +207,35 @@ component extends="../base" { private void function parseSettings(required string content, required struct settings) { // Parse set() calls in the settings file - local.pattern = 'set\s*\(\s*([^=]+)\s*=\s*([^)]+)\)'; + local.pattern = 'set\s*\(\s*([^=\s]+(?:\s*[^=\s]*)*)\s*=\s*([^)]+)\)'; local.matches = REMatchNoCase(local.pattern, arguments.content); for (local.match in local.matches) { try { // Extract key and value - local.parts = REFind(local.pattern, local.match, 1, true); + local.extractPattern = 'set\s*\(\s*([^=]+?)\s*=\s*([^)]+)\)'; + local.parts = REFindNoCase(local.extractPattern, local.match, 1, true); + if (local.parts.pos[1] > 0) { - local.assignment = Mid(local.match, local.parts.pos[2], local.parts.len[2]); - local.assignParts = ListToArray(local.assignment, "="); - if (ArrayLen(local.assignParts) >= 2) { - local.key = Trim(local.assignParts[1]); - // Join remaining parts with = in case value contains = - local.valueParts = []; - for (local.i = 2; local.i <= ArrayLen(local.assignParts); local.i++) { - ArrayAppend(local.valueParts, local.assignParts[local.i]); - } - local.value = Trim(ArrayToList(local.valueParts, "=")); - - // Clean up the value - local.value = REReplace(local.value, "^['""]|['""]$", "", "all"); - - // Try to parse boolean/numeric values - if (local.value == "true") { - local.value = true; - } else if (local.value == "false") { - local.value = false; - } else if (IsNumeric(local.value)) { - local.value = Val(local.value); - } - - arguments.settings[local.key] = local.value; + // Extract key (trim whitespace) + local.key = Trim(Mid(local.match, local.parts.pos[2], local.parts.len[2])); + + // Extract value (trim whitespace) + local.value = Trim(Mid(local.match, local.parts.pos[3], local.parts.len[3])); + + // Clean up quotes from the value + local.value = REReplace(local.value, "^['""]|['""]$", "", "all"); + + // Try to parse boolean/numeric values + if (local.value == "true") { + local.value = true; + } else if (local.value == "false") { + local.value = false; + } else if (IsNumeric(local.value)) { + local.value = Val(local.value); } + + arguments.settings[local.key] = local.value; } } catch (any e) { // Skip malformed settings diff --git a/cli/src/commands/wheels/init.cfc b/cli/src/commands/wheels/init.cfc index b51dbb6503..827c57fee6 100644 --- a/cli/src/commands/wheels/init.cfc +++ b/cli/src/commands/wheels/init.cfc @@ -18,89 +18,92 @@ component extends="base" { * **/ function run() { + try { + requireWheelsApp(getCWD()); + detailOutput.header("Wheels init") + .output("This function will attempt to add a few things") + .output("to an EXISTING Wheels installation to help") + .output("the CLI interact.") + .line() + .output("We're going to assume the following:") + .output("- you've already setup a local datasource/database", true) + .output("- you've already set a reload password", true) + .line() + .output("We're going to try and do the following:") + .output("- create a box.json to help keep track of the wheels version", true) + .output("- create a server.json", true) + .divider() + .line(); + + if(!confirm("Sound ok? [y/n] ")){ + detailOutput.getPrint().redBoldLine("Ok, aborting...").toConsole(); + return; + } - requireWheelsApp(getCWD()); - detailOutput.header("Wheels init") - .output("This function will attempt to add a few things") - .output("to an EXISTING Wheels installation to help") - .output("the CLI interact.") - .line() - .output("We're going to assume the following:") - .output("- you've already setup a local datasource/database", true) - .output("- you've already set a reload password", true) - .line() - .output("We're going to try and do the following:") - .output("- create a box.json to help keep track of the wheels version", true) - .output("- create a server.json", true) - .divider() - .line(); - - if(!confirm("Sound ok? [y/n] ")){ - detailOutput.getPrint().redBoldLine("Ok, aborting...").toConsole(); - return; - } - - var serverJsonLocation=fileSystemUtil.resolvePath("server.json"); - var wheelsBoxJsonLocation=fileSystemUtil.resolvePath("vendor/wheels/box.json"); - var boxJsonLocation=fileSystemUtil.resolvePath("box.json"); - - var wheelsVersion = $getWheelsVersion(); - detailOutput.statusInfo(wheelsVersion); - - // Create a wheels/box.json if one doesn't exist - if(!fileExists(wheelsBoxJsonLocation)){ - var wheelsBoxJSON = fileRead( getTemplate('/WheelsBoxJSON.txt' ) ); - wheelsBoxJSON = replaceNoCase( wheelsBoxJSON, "|version|", trim(wheelsVersion), 'all' ); - - // Make box.json - detailOutput.statusInfo("Creating wheels/box.json"); - file action='write' file=wheelsBoxJsonLocation mode ='777' output='#trim(wheelsBoxJSON)#'; - detailOutput.create(wheelsBoxJsonLocation); - detailOutput.statusSuccess("Created wheels/box.json"); - - } else { - detailOutput.statusInfo("wheels/box.json exists, skipping"); - } + var serverJsonLocation=fileSystemUtil.resolvePath("server.json"); + var wheelsBoxJsonLocation=fileSystemUtil.resolvePath("vendor/wheels/box.json"); + var boxJsonLocation=fileSystemUtil.resolvePath("box.json"); - // Create a server.json if one doesn't exist - if(!fileExists(serverJsonLocation)){ - var appName = ask( message = "Please enter an application name: we use this to make the server.json servername unique: ", defaultResponse = 'myapp'); - appName = helpers.stripSpecialChars(appName); - var setEngine = ask( message = 'Please enter a default cfengine: ', defaultResponse = 'lucee@6' ); + var wheelsVersion = $getWheelsVersion(); + detailOutput.statusInfo(wheelsVersion); - // Make server.json server name unique to this app: assumes lucee by default - detailOutput.statusInfo("Creating default server.json"); - var serverJSON = fileRead( getTemplate('/ServerJSON.txt' ) ); - serverJSON = replaceNoCase( serverJSON, "|appName|", trim(appName), 'all' ); - serverJSON = replaceNoCase( serverJSON, "|setEngine|", setEngine, 'all' ); - file action='write' file=serverJsonLocation mode ='777' output='#trim(serverJSON)#'; - detailOutput.create(serverJsonLocation); - detailOutput.statusSuccess("Created server.json"); + // Create a wheels/box.json if one doesn't exist + if(!fileExists(wheelsBoxJsonLocation)){ + var wheelsBoxJSON = fileRead( getTemplate('/WheelsBoxJSON.txt' ) ); + wheelsBoxJSON = replaceNoCase( wheelsBoxJSON, "|version|", trim(wheelsVersion), 'all' ); - } else { - detailOutput.statusInfo("server.json exists, skipping"); - } + // Make box.json + detailOutput.statusInfo("Creating wheels/box.json"); + file action='write' file=wheelsBoxJsonLocation mode ='777' output='#trim(wheelsBoxJSON)#'; + detailOutput.create(wheelsBoxJsonLocation); + detailOutput.statusSuccess("Created wheels/box.json"); - // Create a box.json if one doesn't exist - if(!fileExists(boxJsonLocation)){ - if(!isDefined("appName")) { - var appName = ask("Please enter an application name: we use this to make the box.json servername unique: "); - appName = helpers.stripSpecialChars(appName); + } else { + detailOutput.statusInfo("wheels/box.json exists, skipping"); } - var boxJSON = fileRead( getTemplate('/BoxJSON.txt' ) ); - boxJSON = replaceNoCase( boxJSON, "|version|", trim(wheelsVersion), 'all' ); - boxJSON = replaceNoCase( boxJSON, "|appName|", trim(appName), 'all' ); - // Make box.json - detailOutput.statusInfo("Creating box.json"); - file action='write' file=boxJsonLocation mode ='777' output='#trim(boxJSON)#'; - detailOutput.create(boxJsonLocation); - detailOutput.statusSuccess("Created box.json"); + // Create a server.json if one doesn't exist + if(!fileExists(serverJsonLocation)){ + var appName = ask( message = "Please enter an application name (we use this to make the server.json servername unique): ", defaultResponse = 'myapp'); + appName = helpers.stripSpecialChars(appName); + var setEngine = ask( message = 'Please enter a default cfengine: ', defaultResponse = 'lucee@6' ); + + // Make server.json server name unique to this app: assumes lucee by default + detailOutput.statusInfo("Creating default server.json"); + var serverJSON = fileRead( getTemplate('/ServerJSON.txt' ) ); + serverJSON = replaceNoCase( serverJSON, "|appName|", trim(appName), 'all' ); + serverJSON = replaceNoCase( serverJSON, "|setEngine|", setEngine, 'all' ); + file action='write' file=serverJsonLocation mode ='777' output='#trim(serverJSON)#'; + detailOutput.create(serverJsonLocation); + detailOutput.statusSuccess("Created server.json"); + + } else { + detailOutput.statusInfo("server.json exists, skipping"); + } - } else { - detailOutput.statusInfo("box.json exists, skipping"); + // Create a box.json if one doesn't exist + if(!fileExists(boxJsonLocation)){ + if(!isDefined("appName")) { + var appName = ask("Please enter an application name (we use this to make the box.json servername unique): "); + appName = helpers.stripSpecialChars(appName); + } + var boxJSON = fileRead( getTemplate('/BoxJSON.txt' ) ); + boxJSON = replaceNoCase( boxJSON, "|version|", trim(wheelsVersion), 'all' ); + boxJSON = replaceNoCase( boxJSON, "|appName|", trim(appName), 'all' ); + + // Make box.json + detailOutput.statusInfo("Creating box.json"); + file action='write' file=boxJsonLocation mode ='777' output='#trim(boxJSON)#'; + detailOutput.create(boxJsonLocation); + detailOutput.statusSuccess("Created box.json"); + + } else { + detailOutput.statusInfo("box.json exists, skipping"); + } + } catch (any e) { + detailOutput.error("#e.message#"); + setExitCode(1); } - } } \ No newline at end of file diff --git a/cli/src/commands/wheels/jobs/monitor.cfc b/cli/src/commands/wheels/jobs/monitor.cfc new file mode 100644 index 0000000000..41822554ed --- /dev/null +++ b/cli/src/commands/wheels/jobs/monitor.cfc @@ -0,0 +1,122 @@ +/** + * Live monitoring dashboard for the job queue. + * Refreshes at a configurable interval showing throughput, errors, and recent jobs. + * + * {code:bash} + * wheels jobs monitor + * wheels jobs monitor --interval=5 + * wheels jobs monitor --queue=mailers + * {code} + */ +component extends="../../base" { + + /** + * @interval Refresh interval in seconds (default: 3) + * @queue Filter by queue name (default: all queues) + */ + public void function run( + numeric interval = 3, + string queue = "" + ) { + local.appPath = getCWD(); + + if (!isWheelsApp(local.appPath)) { + error("This command must be run from a Wheels application directory"); + return; + } + + print.line(); + print.boldMagentaLine("Wheels Job Monitor"); + print.line("Press Ctrl+C to stop"); + print.line(); + + while (true) { + // Build URL parameters + local.urlParams = "&command=jobsMonitor"; + if (Len(arguments.queue)) { + local.urlParams &= "&queue=#arguments.queue#"; + } + + try { + local.result = $sendToCliCommand(urlstring = local.urlParams); + + if (StructKeyExists(local.result, "success") && local.result.success) { + // Clear previous output with separator + print.line(RepeatString("=", 60)); + print.boldCyanLine("Job Queue Dashboard - #TimeFormat(Now(), "HH:mm:ss")#"); + print.line(RepeatString("-", 60)); + + // Queue statistics + if (StructKeyExists(local.result, "stats") && StructKeyExists(local.result.stats, "totals")) { + local.t = local.result.stats.totals; + print.line(); + print.boldLine("Queue Summary:"); + print.yellowLine(" Pending: #local.t.pending#"); + print.cyanLine(" Processing: #local.t.processing#"); + print.greenLine(" Completed: #local.t.completed#"); + if (local.t.failed > 0) { + print.redLine(" Failed: #local.t.failed#"); + } else { + print.line(" Failed: #local.t.failed#"); + } + print.line(" Total: #local.t.total#"); + } + + // Throughput + if (StructKeyExists(local.result, "monitor")) { + local.m = local.result.monitor; + + print.line(); + print.boldLine("Throughput (last 60 min):"); + print.greenLine(" Completed: #local.m.throughput.completed#"); + if (local.m.throughput.failed > 0) { + print.redLine(" Failed: #local.m.throughput.failed#"); + } else { + print.line(" Failed: #local.m.throughput.failed#"); + } + if (local.m.errorRate > 0) { + print.redLine(" Error rate: #local.m.errorRate#%"); + } else { + print.greenLine(" Error rate: 0%"); + } + + if (Len(local.m.oldestPending)) { + print.line(); + print.line("Oldest pending job: #DateTimeFormat(local.m.oldestPending, 'yyyy-mm-dd HH:mm:ss')#"); + } + + // Recent jobs + if (ArrayLen(local.m.recentJobs)) { + print.line(); + print.boldLine("Recent Jobs:"); + local.count = Min(ArrayLen(local.m.recentJobs), 5); + for (local.i = 1; local.i <= local.count; local.i++) { + local.job = local.m.recentJobs[local.i]; + local.statusColor = "line"; + if (local.job.status == "completed") local.statusColor = "greenLine"; + else if (local.job.status == "failed") local.statusColor = "redLine"; + else if (local.job.status == "processing") local.statusColor = "cyanLine"; + else if (local.job.status == "pending") local.statusColor = "yellowLine"; + + print["#local.statusColor#"]( + " [#local.job.status#] #local.job.jobClass# (#local.job.queue#) - #DateTimeFormat(local.job.updatedAt, 'HH:mm:ss')#" + ); + } + } + } + + // Timeout recoveries + if (StructKeyExists(local.result, "timeoutsRecovered") && local.result.timeoutsRecovered > 0) { + print.line(); + print.yellowLine("Recovered #local.result.timeoutsRecovered# timed-out job(s)"); + } + } + } catch (any e) { + print.redLine("[#TimeFormat(Now(), "HH:mm:ss")#] Monitor error: #e.message#"); + } + + sleep(arguments.interval * 1000); + } + } + +} diff --git a/cli/src/commands/wheels/jobs/purge.cfc b/cli/src/commands/wheels/jobs/purge.cfc new file mode 100644 index 0000000000..1456f89e3e --- /dev/null +++ b/cli/src/commands/wheels/jobs/purge.cfc @@ -0,0 +1,84 @@ +/** + * Purge old completed or failed jobs from the queue. + * + * {code:bash} + * wheels jobs purge + * wheels jobs purge --completed --older-than=30 + * wheels jobs purge --failed --queue=mailers + * wheels jobs purge --completed --failed --force + * {code} + */ +component extends="../../base" { + + /** + * @completed Purge completed jobs (default: true) + * @failed Purge failed jobs (default: false) + * @olderThan Delete jobs older than this many days (default: 7) + * @queue Filter by queue name (default: all queues) + * @force Skip confirmation prompt + */ + public void function run( + boolean completed = true, + boolean failed = false, + numeric olderThan = 7, + string queue = "", + boolean force = false + ) { + local.appPath = getCWD(); + + if (!isWheelsApp(local.appPath)) { + error("This command must be run from a Wheels application directory"); + return; + } + + print.line(); + print.boldBlueLine("Purge Jobs"); + print.line(); + + local.totalPurged = 0; + + // Purge completed jobs + if (arguments.completed) { + local.urlParams = "&command=jobsPurge&status=completed&days=#arguments.olderThan#"; + if (Len(arguments.queue)) { + local.urlParams &= "&queue=#arguments.queue#"; + } + + local.result = $sendToCliCommand(urlstring = local.urlParams); + if (StructKeyExists(local.result, "success") && local.result.success && StructKeyExists(local.result, "purged")) { + local.totalPurged += local.result.purged; + if (local.result.purged > 0) { + print.greenLine("Purged #local.result.purged# completed job(s) older than #arguments.olderThan# day(s)."); + } else { + print.line("No completed jobs to purge."); + } + } + } + + // Purge failed jobs + if (arguments.failed) { + local.urlParams = "&command=jobsPurge&status=failed&days=#arguments.olderThan#"; + if (Len(arguments.queue)) { + local.urlParams &= "&queue=#arguments.queue#"; + } + + local.result = $sendToCliCommand(urlstring = local.urlParams); + if (StructKeyExists(local.result, "success") && local.result.success && StructKeyExists(local.result, "purged")) { + local.totalPurged += local.result.purged; + if (local.result.purged > 0) { + print.greenLine("Purged #local.result.purged# failed job(s) older than #arguments.olderThan# day(s)."); + } else { + print.line("No failed jobs to purge."); + } + } + } + + if (local.totalPurged > 0) { + print.line(); + print.boldGreenLine("Total purged: #local.totalPurged# job(s)"); + } + + print.line(); + } + +} diff --git a/cli/src/commands/wheels/jobs/retry.cfc b/cli/src/commands/wheels/jobs/retry.cfc new file mode 100644 index 0000000000..f50ff26edc --- /dev/null +++ b/cli/src/commands/wheels/jobs/retry.cfc @@ -0,0 +1,57 @@ +/** + * Retry failed jobs by resetting them to pending status. + * + * {code:bash} + * wheels jobs retry + * wheels jobs retry --queue=mailers + * wheels jobs retry --limit=10 + * {code} + */ +component extends="../../base" { + + /** + * @queue Filter by queue name (default: all queues) + * @limit Maximum number of jobs to retry (default: 0 = all) + */ + public void function run( + string queue = "", + numeric limit = 0 + ) { + local.appPath = getCWD(); + + if (!isWheelsApp(local.appPath)) { + error("This command must be run from a Wheels application directory"); + return; + } + + print.line(); + print.boldBlueLine("Retry Failed Jobs"); + print.line(); + + // Build URL parameters + local.urlParams = "&command=jobsRetry"; + if (Len(arguments.queue)) { + local.urlParams &= "&queue=#arguments.queue#"; + } + if (arguments.limit > 0) { + local.urlParams &= "&limit=#arguments.limit#"; + } + + local.result = $sendToCliCommand(urlstring = local.urlParams); + if (!local.result.success) { + return; + } + + if (StructKeyExists(local.result, "retried")) { + if (local.result.retried > 0) { + print.greenLine("Retried #local.result.retried# failed job(s)."); + print.line("Jobs have been reset to 'pending' and will be processed on the next cycle."); + } else { + print.line("No failed jobs to retry."); + } + } + + print.line(); + } + +} diff --git a/cli/src/commands/wheels/jobs/status.cfc b/cli/src/commands/wheels/jobs/status.cfc new file mode 100644 index 0000000000..57a5ad928a --- /dev/null +++ b/cli/src/commands/wheels/jobs/status.cfc @@ -0,0 +1,117 @@ +/** + * Show job queue statistics. + * + * {code:bash} + * wheels jobs status + * wheels jobs status --queue=mailers + * wheels jobs status --format=json + * {code} + */ +component extends="../../base" { + + /** + * @queue Filter by queue name (default: all queues) + * @format Output format: table or json (default: table) + */ + public void function run( + string queue = "", + string format = "table" + ) { + local.appPath = getCWD(); + + if (!isWheelsApp(local.appPath)) { + error("This command must be run from a Wheels application directory"); + return; + } + + print.line(); + print.boldBlueLine("Job Queue Status"); + print.line(); + + // Build URL parameters + local.urlParams = "&command=jobsStatus"; + if (Len(arguments.queue)) { + local.urlParams &= "&queue=#arguments.queue#"; + } + + local.result = $sendToCliCommand(urlstring = local.urlParams); + if (!local.result.success) { + return; + } + + if (arguments.format == "json") { + print.line(SerializeJSON(local.result.stats)); + return; + } + + // Display per-queue table + if (StructKeyExists(local.result, "stats") && StructKeyExists(local.result.stats, "queues")) { + local.queues = local.result.stats.queues; + + if (StructCount(local.queues) == 0) { + print.line("No jobs found."); + print.line(); + return; + } + + // Header + local.header = "| " & PadRight("Queue", 20) & " | " & + PadRight("Pending", 10) & " | " & + PadRight("Processing", 12) & " | " & + PadRight("Completed", 12) & " | " & + PadRight("Failed", 10) & " | " & + PadRight("Total", 10) & " |"; + local.separator = RepeatString("-", Len(local.header)); + + print.line(local.separator); + print.line(local.header); + print.line(local.separator); + + for (local.queueName in local.queues) { + local.q = local.queues[local.queueName]; + print.text("| " & PadRight(local.queueName, 20) & " | "); + print.yellowText(PadRight(local.q.pending, 10)); + print.text(" | "); + print.cyanText(PadRight(local.q.processing, 12)); + print.text(" | "); + print.greenText(PadRight(local.q.completed, 12)); + print.text(" | "); + if (local.q.failed > 0) { + print.redText(PadRight(local.q.failed, 10)); + } else { + print.text(PadRight(local.q.failed, 10)); + } + print.line(" | " & PadRight(local.q.total, 10) & " |"); + } + + // Totals row + local.totals = local.result.stats.totals; + print.line(local.separator); + print.text("| " & PadRight("TOTAL", 20) & " | "); + print.boldYellowText(PadRight(local.totals.pending, 10)); + print.text(" | "); + print.boldCyanText(PadRight(local.totals.processing, 12)); + print.text(" | "); + print.boldGreenText(PadRight(local.totals.completed, 12)); + print.text(" | "); + if (local.totals.failed > 0) { + print.boldRedText(PadRight(local.totals.failed, 10)); + } else { + print.boldText(PadRight(local.totals.failed, 10)); + } + print.line(" | " & PadRight(local.totals.total, 10) & " |"); + print.line(local.separator); + } + + print.line(); + } + + private string function PadRight(required string text, required numeric length) { + local.str = ToString(arguments.text); + if (Len(local.str) >= arguments.length) { + return Left(local.str, arguments.length); + } + return local.str & RepeatString(" ", arguments.length - Len(local.str)); + } + +} diff --git a/cli/src/commands/wheels/jobs/work.cfc b/cli/src/commands/wheels/jobs/work.cfc new file mode 100644 index 0000000000..d6ad43485f --- /dev/null +++ b/cli/src/commands/wheels/jobs/work.cfc @@ -0,0 +1,113 @@ +/** + * Start a job worker daemon that continuously polls for and processes jobs. + * + * The worker calls the Wheels bridge each poll cycle to claim and process + * the next available job. Run multiple workers for parallelism. + * + * {code:bash} + * wheels jobs work + * wheels jobs work --queue=mailers + * wheels jobs work --queue=mailers,default --interval=3 + * wheels jobs work --max-jobs=100 + * {code} + */ +component extends="../../base" { + + /** + * @queue Comma-delimited queue names to process (default: all queues) + * @interval Seconds between poll cycles (default: 5) + * @maxJobs Stop after processing this many jobs (default: 0 = unlimited) + * @timeout Job execution timeout in seconds (default: 300) + * @quiet Suppress per-job output, only show errors + */ + public void function run( + string queue = "", + numeric interval = 5, + numeric maxJobs = 0, + numeric timeout = 300, + boolean quiet = false + ) { + local.appPath = getCWD(); + + if (!isWheelsApp(local.appPath)) { + error("This command must be run from a Wheels application directory"); + return; + } + + print.line(); + print.boldCyanLine("Wheels Job Worker"); + print.line("Press Ctrl+C to stop"); + print.line(); + + if (Len(arguments.queue)) { + print.greenLine("Queues: #arguments.queue#"); + } else { + print.greenLine("Queues: all"); + } + print.greenLine("Poll interval: #arguments.interval#s"); + if (arguments.maxJobs > 0) { + print.greenLine("Max jobs: #arguments.maxJobs#"); + } + print.line(); + + local.processed = 0; + local.failed = 0; + local.cycles = 0; + + while (true) { + local.cycles++; + + // Build URL parameters + local.urlParams = "&command=jobsProcessNext"; + if (Len(arguments.queue)) { + local.urlParams &= "&queues=#arguments.queue#"; + } + local.urlParams &= "&timeout=#arguments.timeout#"; + + try { + local.result = $sendToCliCommand(urlstring = local.urlParams); + + if (StructKeyExists(local.result, "success") && local.result.success && StructKeyExists(local.result, "jobResult")) { + local.jr = local.result.jobResult; + + if (StructKeyExists(local.jr, "skipped") && local.jr.skipped) { + // No jobs available โ€” just wait + } else if (StructKeyExists(local.jr, "success") && local.jr.success) { + local.processed++; + if (!arguments.quiet) { + print.greenLine("[#TimeFormat(Now(), "HH:mm:ss")#] Completed: #local.jr.jobClass# (#local.jr.jobId#)"); + } + + // Check max jobs limit + if (arguments.maxJobs > 0 && local.processed >= arguments.maxJobs) { + print.line(); + print.boldGreenLine("Reached max jobs limit (#arguments.maxJobs#). Shutting down."); + print.line("Processed: #local.processed# | Failed: #local.failed#"); + return; + } + + // Process more immediately if there was work + continue; + } else { + local.failed++; + local.errorMsg = StructKeyExists(local.jr, "error") ? local.jr.error : "Unknown error"; + print.redLine("[#TimeFormat(Now(), "HH:mm:ss")#] Failed: #local.jr.jobClass# - #local.errorMsg#"); + + // Check max jobs limit (failures count too) + if (arguments.maxJobs > 0 && (local.processed + local.failed) >= arguments.maxJobs) { + print.line(); + print.boldGreenLine("Reached max jobs limit (#arguments.maxJobs#). Shutting down."); + print.line("Processed: #local.processed# | Failed: #local.failed#"); + return; + } + } + } + } catch (any e) { + print.redLine("[#TimeFormat(Now(), "HH:mm:ss")#] Worker error: #e.message#"); + } + + sleep(arguments.interval * 1000); + } + } + +} diff --git a/cli/src/commands/wheels/plugins/init.cfc b/cli/src/commands/wheels/plugins/init.cfc index 1b9696490c..7422334a19 100644 --- a/cli/src/commands/wheels/plugins/init.cfc +++ b/cli/src/commands/wheels/plugins/init.cfc @@ -34,6 +34,12 @@ component aliases="wheels plugin init" extends="../base" { try { + if (!reFind("^[a-z][a-z0-9-]*$", arguments.name)) { + detailOutput.error("Invalid plugin name. Use letters, numbers, and hyphens only (no spaces)."); + setExitCode(1); + return; + } + // Ensure plugin name follows convention var pluginName = arguments.name; if (!reFindNoCase("^wheels-", pluginName)) { @@ -215,7 +221,7 @@ node_modules/ fileWrite(pluginDir & "/.gitignore", gitignore); // Create test file - var testFile = 'component extends="wheels.Testbox" { + var testFile = 'component extends="wheels.WheelsTest" { function run() { describe("#pluginName# Tests", function() { diff --git a/cli/src/commands/wheels/plugins/list.cfc b/cli/src/commands/wheels/plugins/list.cfc index 953bd92643..bcfad0f0eb 100644 --- a/cli/src/commands/wheels/plugins/list.cfc +++ b/cli/src/commands/wheels/plugins/list.cfc @@ -6,7 +6,7 @@ * wheels plugins list --available */ component aliases="wheels plugin list" extends="../base" { - + property name="forgebox" inject="ForgeBox"; property name="pluginService" inject="PluginService@wheels-cli"; property name="detailOutput" inject="DetailOutputService@wheels-cli"; @@ -19,112 +19,229 @@ component aliases="wheels plugin list" extends="../base" { string format = "table", boolean available = false ) { - requireWheelsApp(getCWD()); - arguments = reconstructArgs( - argStruct=arguments, - allowedValues={ - format=["table", "json"] + try { + requireWheelsApp(getCWD()); + arguments = reconstructArgs( + argStruct=arguments, + allowedValues={ + format=["table", "json"] + } + ); + + if (arguments.available) { + // Show available plugins from ForgeBox + detailOutput.header("Available Wheels Plugins on ForgeBox"); + detailOutput.output("Searching, please wait..."); + detailOutput.line(); + + // Get list of all cfwheels plugins slugs + var forgeboxResult = command('forgebox show') + .params(type='cfwheels-plugins') + .run(returnOutput=true); + + var results = []; + + if (len(forgeboxResult)) { + var lines = listToArray(forgeboxResult, chr(10) & chr(13)); + + for (var i = 1; i <= arrayLen(lines); i++) { + var line = trim(lines[i]); + + // Check if this is a slug line: Slug: "slug-name" + if (findNoCase('Slug:', line)) { + // Extract slug from quotes + var slugMatch = reFind('Slug:\s*"([^"]+)"', line, 1, true); + if (slugMatch.pos[1] > 0) { + var slug = mid(line, slugMatch.pos[2], slugMatch.len[2]); + + try { + var pluginInfo = forgebox.getEntry(slug); + + if (isStruct(pluginInfo) && structKeyExists(pluginInfo, "slug")) { + // Extract version from latestVersion structure + var version = "N/A"; + if (structKeyExists(pluginInfo, "latestVersion") && + isStruct(pluginInfo.latestVersion) && + structKeyExists(pluginInfo.latestVersion, "version")) { + version = pluginInfo.latestVersion.version; + } + + // Extract author from user structure + var author = "Unknown"; + if (structKeyExists(pluginInfo, "user") && + isStruct(pluginInfo.user) && + structKeyExists(pluginInfo.user, "username")) { + author = pluginInfo.user.username; + } + + arrayAppend(results, { + name: pluginInfo.title ?: slug, + slug: slug, + version: version, + description: pluginInfo.summary ?: pluginInfo.description ?: "", + author: author, + downloads: pluginInfo.hits ?: 0, + updateDate: pluginInfo.updatedDate ?: "" + }); + } + } catch (any e) { + // Skip plugins that can't be retrieved + } + } + } + } + } + + results.sort(function(a, b) { + return compareNoCase(a.name, b.name); + }); + + if (arguments.format == "json") { + var jsonOutput = { + "plugins": results, + "count": arrayLen(results) + }; + print.line(jsonOutput).toConsole(); + } else { + detailOutput.subHeader("Found #arrayLen(results)# plugin(s)"); + detailOutput.line(); + + // Create table for results + var rows = []; + + for (var plugin in results) { + // use ordered struct so JSON keeps key order + var row = structNew("ordered"); + + row["Name"] = plugin.name; + row["Slug"] = plugin.slug; + row["Version"] = plugin.version; + row["Downloads"] = numberFormat(plugin.downloads ?: 0); + row["Description"] = plugin.description ?: "No description"; + + // Truncate long descriptions + if (len(row["Description"]) > 50) { + row["Description"] = left(row["Description"], 47) & "..."; + } + + arrayAppend(rows, row); + } + + // Display the table + detailOutput.getPrint().table(rows).toConsole(); + + detailOutput.line(); + detailOutput.divider(); + detailOutput.line(); + + // Show summary + detailOutput.metric("Total plugins found", "#arrayLen(results)#"); + detailOutput.line(); + + // Show commands + detailOutput.subHeader("Commands"); + detailOutput.output("- Install: wheels plugin install ", true); + detailOutput.output("- Details: wheels plugin info ", true); + detailOutput.output("- Add --format=json for JSON output", true); + detailOutput.line(); + } + return; } - ); - - if (arguments.available) { - // Show available plugins from ForgeBox - detailOutput.header("Available Wheels Plugins on ForgeBox"); - detailOutput.line(); - command('forgebox show').params(type="cfwheels-plugins").run(); - return; - } - - // Show installed plugins - var plugins = pluginService.list(); - - if (arrayLen(plugins) == 0) { - detailOutput.header("Installed Wheels Plugins"); - detailOutput.line(); - detailOutput.statusWarning("No plugins installed in /plugins folder"); - detailOutput.line(); - detailOutput.subHeader("Install plugins with"); - detailOutput.output("- wheels plugin install ", true); - detailOutput.line(); - detailOutput.subHeader("See available plugins"); - detailOutput.output("- wheels plugin list --available", true); - detailOutput.output("- wheels plugin search ", true); - return; - } - - if (arguments.format == "json") { - // JSON format output - var jsonOutput = { - "plugins": plugins, - "count": arrayLen(plugins) - }; - print.line(serializeJSON(jsonOutput, true)); - } else { - // Table format output - detailOutput.header("Installed Wheels Plugins (#arrayLen(plugins)#)"); - detailOutput.line(); - - // Create table rows - var rows = []; - for (var plugin in plugins) { - var row = { - "Plugin Name": plugin.name, - "Version": plugin.version + + // Show installed plugins + var plugins = pluginService.list(); + + if (arrayLen(plugins) == 0) { + detailOutput.header("Installed Wheels Plugins"); + detailOutput.line(); + detailOutput.statusWarning("No plugins installed in /plugins folder"); + detailOutput.line(); + detailOutput.subHeader("Install plugins with"); + detailOutput.output("- wheels plugin install ", true); + detailOutput.line(); + detailOutput.subHeader("See available plugins"); + detailOutput.output("- wheels plugin list --available", true); + detailOutput.output("- wheels plugin search ", true); + return; + } + + if (arguments.format == "json") { + // JSON format output + var jsonOutput = { + "plugins": plugins, + "count": arrayLen(plugins) }; + print.line(jsonOutput).toConsole(); + } else { + // Table format output + detailOutput.header("Installed Wheels Plugins (#arrayLen(plugins)#)"); + + // Create table rows + var rows = []; + for (var plugin in plugins) { + var row = { + "Plugin Name": plugin.name, + "Slug" :plugin.slug, + "Version": plugin.version + }; + + if (plugin.keyExists("description") && len(plugin.description)) { + row["Description"] = left(plugin.description, 50); + } else { + row["Description"] = ""; + } + + // Add author if available + if (plugin.keyExists("author") && len(plugin.author)) { + row["Author"] = left(plugin.author, 20); + } + + arrayAppend(rows, row); + } + + // Display the table + detailOutput.getPrint().table(rows); - if (plugin.keyExists("description") && len(plugin.description)) { - row["Description"] = left(plugin.description, 50); - } else { - row["Description"] = ""; + detailOutput.line(); + detailOutput.divider("-", 60); + detailOutput.line(); + + // Show summary + detailOutput.metric("Total plugins", "#arrayLen(plugins)#"); + var devPlugins = 0; + for (var plugin in plugins) { + if (plugin.keyExists("type") && findNoCase("dev", plugin.type)) { + devPlugins++; + } + } + if (devPlugins > 0) { + detailOutput.metric("Development plugins", "#devPlugins#"); } - // Add author if available - if (plugin.keyExists("author") && len(plugin.author)) { - row["Author"] = left(plugin.author, 20); + // Show most recent plugin if available + if (arrayLen(plugins) > 0) { + var recentPlugin = plugins[1]; // Assuming first is most recent + detailOutput.metric("Latest plugin", "#recentPlugin.name# (#recentPlugin.version#)"); } - arrayAppend(rows, row); - } + detailOutput.line(); - // Display the table - detailOutput.getPrint().table(rows); - - detailOutput.line(); - detailOutput.divider("-", 60); - detailOutput.line(); - - // Show summary - detailOutput.metric("Total plugins", "#arrayLen(plugins)#"); - var devPlugins = 0; - for (var plugin in plugins) { - if (plugin.keyExists("type") && findNoCase("dev", plugin.type)) { - devPlugins++; - } - } - if (devPlugins > 0) { - detailOutput.metric("Development plugins", "#devPlugins#"); - } - - // Show most recent plugin if available - if (arrayLen(plugins) > 0) { - var recentPlugin = plugins[1]; // Assuming first is most recent - detailOutput.metric("Latest plugin", "#recentPlugin.name# (#recentPlugin.version#)"); + // Show commands + detailOutput.subHeader("Commands"); + detailOutput.output("- wheels plugin info View plugin details", true); + detailOutput.output("- wheels plugin update:all Update all plugins", true); + detailOutput.output("- wheels plugin outdated Check for updates", true); + detailOutput.output("- wheels plugin install Install new plugin", true); + detailOutput.output("- wheels plugin remove Remove a plugin", true); + detailOutput.line(); + + // Add tip + detailOutput.statusInfo("Tip"); + detailOutput.output("Add --format=json for JSON output", true); } - - detailOutput.line(); - - // Show commands - detailOutput.subHeader("Commands"); - detailOutput.output("- wheels plugin info View plugin details", true); - detailOutput.output("- wheels plugin update:all Update all plugins", true); - detailOutput.output("- wheels plugin outdated Check for updates", true); - detailOutput.output("- wheels plugin install Install new plugin", true); - detailOutput.output("- wheels plugin remove Remove a plugin", true); - detailOutput.line(); - - // Add tip - detailOutput.statusInfo("Tip"); - detailOutput.output("Add --format=json for JSON output", true); - } + } catch (any e) { + detailOutput.error("#e.message#"); + setExitCode(1); + } } } \ No newline at end of file diff --git a/cli/src/commands/wheels/plugins/outdated.cfc b/cli/src/commands/wheels/plugins/outdated.cfc index d91e7d92f6..f60f10c6b9 100644 --- a/cli/src/commands/wheels/plugins/outdated.cfc +++ b/cli/src/commands/wheels/plugins/outdated.cfc @@ -130,7 +130,7 @@ component aliases="wheels plugin outdated,wheels plugins outdated" extends="../b } // Display the table - print.table(rows); + print.table(rows).toConsole(); detailOutput.line(); detailOutput.divider("-", 60); diff --git a/cli/src/commands/wheels/plugins/search.cfc b/cli/src/commands/wheels/plugins/search.cfc index a2f6066b9d..56b04a6853 100644 --- a/cli/src/commands/wheels/plugins/search.cfc +++ b/cli/src/commands/wheels/plugins/search.cfc @@ -170,7 +170,7 @@ component aliases="wheels plugin search" extends="../base" { // Display the table - detailOutput.getPrint().table(rows); + detailOutput.getPrint().table(rows).toConsole(); detailOutput.line(); detailOutput.divider(); diff --git a/cli/src/commands/wheels/plugins/update.cfc b/cli/src/commands/wheels/plugins/update.cfc index 2a12eac6eb..a890505a22 100644 --- a/cli/src/commands/wheels/plugins/update.cfc +++ b/cli/src/commands/wheels/plugins/update.cfc @@ -196,7 +196,7 @@ component aliases="wheels plugin update" extends="../base" { // Show version comparison detailOutput.subHeader("Update Summary"); detailOutput.metric("Plugin", pluginInfo.name); - detailOutput.update("Version", "v#currentVersion# โ†’ v#targetVersion#"); + detailOutput.metric("Version", "v#currentVersion# โ†’ v#targetVersion#"); detailOutput.metric("Location", "/plugins/#foundPlugin.folderName#"); detailOutput.line(); diff --git a/cli/src/commands/wheels/plugins/updateAll.cfc b/cli/src/commands/wheels/plugins/updateAll.cfc index abac4c946d..92175b587b 100644 --- a/cli/src/commands/wheels/plugins/updateAll.cfc +++ b/cli/src/commands/wheels/plugins/updateAll.cfc @@ -4,7 +4,7 @@ * wheels plugin update:all * wheels plugin update:all --dryRun */ -component aliases="wheels plugin update:all,wheels plugins update:all" extends="../base" { +component aliases="wheels plugin update:all,wheels plugins update:all, wheels plugin updateall" extends="../base" { property name="pluginService" inject="PluginService@wheels-cli"; property name="packageService" inject="PackageService"; @@ -122,7 +122,7 @@ component aliases="wheels plugin update:all,wheels plugins update:all" extends=" }); } - print.table(updateRows); + print.table(updateRows).toConsole(); detailOutput.line(); if (arguments.dryRun) { @@ -198,7 +198,6 @@ component aliases="wheels plugin update:all,wheels plugins update:all" extends=" // Show final summary detailOutput.line(); - detailOutput.divider("=", 60); detailOutput.header("Update Summary"); detailOutput.line(); @@ -214,7 +213,7 @@ component aliases="wheels plugin update:all,wheels plugins update:all" extends=" arrayAppend(summaryRows, { "Status" = "Check errors", "Count" = "#arrayLen(errors)#" }); } - print.table(summaryRows); + print.table(summaryRows).toConsole(); detailOutput.line(); if (successCount > 0) { diff --git a/cli/src/commands/wheels/reload.cfc b/cli/src/commands/wheels/reload.cfc index abe432c898..69c0f847c6 100644 --- a/cli/src/commands/wheels/reload.cfc +++ b/cli/src/commands/wheels/reload.cfc @@ -21,14 +21,108 @@ component aliases='wheels r' extends="base" { property name="detailOutput" inject="DetailOutputService@wheels-cli"; function run(string mode="development", string password="") { - requireWheelsApp(getCWD()); - arguments=reconstructArgs(arguments); - var serverDetails = $getServerInfo(); - - getURL = serverDetails.serverURL & - "/index.cfm?reload=#mode#&password=#password#"; - var loc = new Http( url=getURL ).send().getPrefix(); - detailOutput.statusSuccess("Reload Request sent"); + try { + requireWheelsApp(getCWD()); + arguments=reconstructArgs(arguments); + var serverDetails = $getServerInfo(); + var appSettings = $getAppSettings(mode); + + var reloadPassword = StructKeyExists(appSettings, "reloadPassword") ? appSettings.reloadPassword : ""; + getURL = serverDetails.serverURL & "/index.cfm?reload=#mode#"; + + // Handle password logic + if (len(reloadPassword)) { + // Password is configured + if (len(password)) { + // User provided a password, validate it against configured one + if (password != reloadPassword) { + detailOutput.error("Invalid password. The configured reload password does not match the provided password."); + return; + } + getURL &= "&password=#password#"; + } else { + detailOutput.error("Reload password is configured but not provided!"); + return; + } + } else { + // No password configured - check if user provided one unnecessarily + if (len(password)) { + detailOutput.statusWarning("No reload password is configured in settings, but you provided one. Proceeding without password."); + } + } + getURL = serverDetails.serverURL & + "/index.cfm?reload=#mode#&password=#password#"; + var loc = new Http( url=getURL ).send().getPrefix(); + detailOutput.statusSuccess("Reload Request sent"); + } catch (any e) { + detailOutput.error("#e.message#"); + setExitCode(1); + } } + private struct function $getAppSettings(required string mode="development") { + try { + local.appPath = getCWD(); + local.settingsFile = local.appPath & "/config/settings.cfm"; + local.envSettingsFile = local.appPath & "/config/" & arguments.mode & "/settings.cfm"; + local.settings = {}; + + // Override with app settings if file exists + if (FileExists(local.settingsFile)) { + local.settingsContent = FileRead(local.settingsFile); + parseSettings(local.settingsContent, local.settings); + } + + // Override with environment-specific settings + if (FileExists(local.envSettingsFile)) { + local.envSettingsContent = FileRead(local.envSettingsFile); + parseSettings(local.envSettingsContent, local.settings); + } + + return local.settings; + } catch (any e) { + detailOutput.error("Error reading settings: #e.message#"); + if (StructKeyExists(e, "detail") && Len(e.detail)) { + detailOutput.output("Details: #e.detail#"); + } + } + } + + private void function parseSettings(required string content, required struct settings) { + local.pattern = '(?i)set\s*\(\s*([^)]+)\)'; + local.matches = REMatch(local.pattern, arguments.content); + + for (local.match in local.matches) { + try { + // extract the inside of set(...) + local.inner = REReplace(local.match, '(?i)^set\s*\(|\);?$', '', 'all'); + + // split only on FIRST = + local.eqPos = Find("=", local.inner); + if (!local.eqPos) continue; + + local.key = Trim(Left(local.inner, local.eqPos - 1)); + local.value = Trim(Mid(local.inner, local.eqPos + 1)); + + // strip quotes + local.value = REReplace(local.value, "^['""]|['""]$", "", "all"); + + // coerce types + if (local.value == "true") { + local.value = true; + } else if (local.value == "false") { + local.value = false; + } else if (IsNumeric(local.value)) { + local.value = Val(local.value); + } + + arguments.settings[local.key] = local.value; + } catch (any e) { + detailOutput.error("Error reading settings: #e.message#"); + if (StructKeyExists(e, "detail") && Len(e.detail)) { + detailOutput.output("Details: #e.detail#"); + } + } + } + } } diff --git a/cli/src/commands/wheels/server.cfc b/cli/src/commands/wheels/server.cfc deleted file mode 100644 index 0be0650ac0..0000000000 --- a/cli/src/commands/wheels/server.cfc +++ /dev/null @@ -1,18 +0,0 @@ -/** - * Server Management Commands - * Wraps CommandBox's native server functionality with Wheels-specific enhancements - * - * Examples: - * {code:bash} - * wheels server start - * wheels server stop - * wheels server restart - * wheels server status - * wheels server log - * wheels server open - * {code} - **/ -component extends="base" aliases="" excludeFromHelp=false { - // This is a namespace command - subcommands are in the server/ directory - // CommandBox will automatically handle subcommand routing -} \ No newline at end of file diff --git a/cli/src/commands/wheels/server/log.cfc b/cli/src/commands/wheels/server/log.cfc index 63a89d9e80..d9096bb4c2 100644 --- a/cli/src/commands/wheels/server/log.cfc +++ b/cli/src/commands/wheels/server/log.cfc @@ -22,6 +22,7 @@ component extends="../base" { numeric lines = 50, boolean debug = false ) { + detailOutput.header("Wheels Server Logs"); // Build the log command var logCommand = "server log"; @@ -63,7 +64,14 @@ component extends="../base" { print.line(); // Execute the server log command + detailOutput.subHeader("Executing Log Command"); + detailOutput.code(logCommand); + command(logCommand).run(); + + if (arguments.follow) { + detailOutput.statusInfo("Press Ctrl+C to stop following logs"); + } } } \ No newline at end of file diff --git a/cli/src/commands/wheels/server/restart.cfc b/cli/src/commands/wheels/server/restart.cfc index 2fffd305c0..ed4154e140 100644 --- a/cli/src/commands/wheels/server/restart.cfc +++ b/cli/src/commands/wheels/server/restart.cfc @@ -17,6 +17,7 @@ component extends="../base" { string name, boolean force = false ) { + detailOutput.header("Restart Wheels Development Server"); // Build the restart command var restartCommand = "server restart"; @@ -45,25 +46,23 @@ component extends="../base" { restartCommand &= " --force"; } - print.yellowLine("Restarting Wheels development server..."); - print.line(); + // Show command + detailOutput.subHeader("Executing Restart Command"); + detailOutput.code(restartCommand); // Execute the server restart command command(restartCommand).run(); - print.line(); - print.greenLine("Server restarted successfully!"); - print.line(); - - // Also reload the Wheels application - print.line("Reloading Wheels application..."); + detailOutput.statusSuccess("Server restarted successfully"); + + // Reload Wheels + detailOutput.subHeader("Reload Wheels Application"); try { command("wheels reload").run(); - print.greenLine("Application reloaded successfully!"); + detailOutput.statusSuccess("Application reloaded successfully"); } catch (any e) { - print.yellowLine("Note: Application reload may require manual refresh in browser."); + detailOutput.statusWarning("Application reload may require manual browser refresh"); } - print.line(); } } \ No newline at end of file diff --git a/cli/src/commands/wheels/server/start.cfc b/cli/src/commands/wheels/server/start.cfc index ce459c57e3..247ecbd455 100644 --- a/cli/src/commands/wheels/server/start.cfc +++ b/cli/src/commands/wheels/server/start.cfc @@ -6,7 +6,6 @@ * {code:bash} * wheels server start * wheels server start port=8080 - * wheels server start rewritesEnable=true * {code} **/ component extends="../base" { @@ -14,7 +13,6 @@ component extends="../base" { /** * @port Port number to start server on * @host Host/IP to bind server to - * @rewritesEnable Enable URL rewriting * @openbrowser Open browser after starting * @directory Directory to serve (defaults to current) * @name Server name @@ -23,27 +21,34 @@ component extends="../base" { function run( numeric port, string host = "127.0.0.1", - boolean rewritesEnable, boolean openbrowser = true, string directory = getCWD(), string name, boolean force = false ) { - // Check if we're in a Wheels application - if (!isWheelsApp(arguments.directory)) { - print.redLine("This doesn't appear to be a Wheels application directory."); - print.line("Looking for /vendor/wheels, /config, and /app folders in: #arguments.directory#"); - print.line(); - print.yellowLine("Did you mean to run 'wheels generate app' first?"); - return; - } + requireWheelsApp(getCWD()); + + arguments = reconstructArgs( + argStruct = arguments + ); + + // Header + detailOutput.header("Wheels Development Server"); // Check if server is already running if (!arguments.force) { - var serverInfo = getServerInfo(); - if (structKeyExists(serverInfo, "port") && serverInfo.port > 0) { - print.yellowLine("Server appears to be already running on port #serverInfo.port#"); - print.line("Use --force to start anyway, or 'wheels server restart' to restart."); + detailOutput.statusInfo("Checking server status"); + var statusCommand = "server status"; + local.result = runCommand(statusCommand); + + // Check if server is not running + if (findNoCase("running", local.result)) { + detailOutput.statusWarning("Server appears to already be running"); + detailOutput.nextSteps([ + "Run 'wheels server restart' to restart the server", + "Run 'wheels server stop' to stop the server", + "Run with --force to start anyway" + ]); return; } } @@ -53,40 +58,39 @@ component extends="../base" { // Add parameters if provided if (!isNull(arguments.port)) { - startCommand &= " --port=#arguments.port#"; + startCommand &= " port=#arguments.port#"; } if (!isNull(arguments.host)) { - startCommand &= " --host=#arguments.host#"; - } - if (!isNull(arguments.rewritesEnable)) { - startCommand &= " --rewritesEnable=#arguments.rewritesEnable#"; + startCommand &= " host=#arguments.host#"; } if (!isNull(arguments.openbrowser)) { - startCommand &= " --openbrowser=#arguments.openbrowser#"; + startCommand &= " openbrowser=#arguments.openbrowser#"; } if (!isNull(arguments.name)) { - startCommand &= " --name=#arguments.name#"; + startCommand &= " name=#arguments.name#"; } if (arguments.directory != getCWD()) { - startCommand &= " --directory=#arguments.directory#"; + startCommand &= " directory=#arguments.directory#"; } - print.greenLine("Starting Wheels development server..."); - print.line(); + detailOutput.statusInfo("Starting Wheels development server..."); + + // Show command + detailOutput.subHeader("Executing Command"); + detailOutput.code(startCommand); // Execute the server start command command(startCommand).run(); // Show helpful information - print.line(); - print.greenLine("Server started successfully!"); - print.line(); - print.line("Useful commands:"); - print.indentedLine("wheels server status - Check server status"); - print.indentedLine("wheels server log - View server logs"); - print.indentedLine("wheels server stop - Stop the server"); - print.indentedLine("wheels reload - Reload your application"); - print.line(); + detailOutput.success("Server started successfully!"); + + detailOutput.nextSteps([ + "wheels server status - Check server status", + "wheels server log - View server logs", + "wheels server stop - Stop the server", + "wheels reload - Reload your application" + ]); } /** diff --git a/cli/src/commands/wheels/server/status.cfc b/cli/src/commands/wheels/server/status.cfc index 6bbff9fa63..5c94a02466 100644 --- a/cli/src/commands/wheels/server/status.cfc +++ b/cli/src/commands/wheels/server/status.cfc @@ -36,10 +36,7 @@ component extends="../base" { } if (!arguments.json) { - print.line(); - print.yellowLine("Wheels Server Status"); - print.line("==================="); - print.line(); + detailOutput.header("Wheels Server Status"); } // Execute the server status command @@ -50,27 +47,24 @@ component extends="../base" { try { var serverInfo = $getServerInfo(); if (structKeyExists(serverInfo, "port") && serverInfo.port > 0) { - print.line(); - print.line("Wheels Application Info:"); - print.indentedLine("URL: #serverInfo.serverURL#"); + detailOutput.subHeader("Wheels Application Info"); + detailOutput.metric("URL", serverInfo.serverURL); // Check if it's a Wheels app if (isWheelsApp()) { - print.indentedLine("Wheels Version: #$getWheelsVersion()#"); - print.indentedLine("Application Root: #getCWD()#"); + detailOutput.metric("Wheels Version", $getWheelsVersion()); + detailOutput.metric("Application Root", getCWD()); } - print.line(); - print.line("Quick Actions:"); - print.indentedLine("wheels server open - Open in browser"); - print.indentedLine("wheels server log - View logs"); - print.indentedLine("wheels reload - Reload application"); + detailOutput.nextSteps([ + "wheels server open - Open in browser", + "wheels server log - View logs", + "wheels reload - Reload application" + ]); } } catch (any e) { // Silently continue if we can't get additional info } - print.line(); } } - } \ No newline at end of file diff --git a/cli/src/commands/wheels/server/stop.cfc b/cli/src/commands/wheels/server/stop.cfc index 52488a5dbf..1f39d699f0 100644 --- a/cli/src/commands/wheels/server/stop.cfc +++ b/cli/src/commands/wheels/server/stop.cfc @@ -45,14 +45,15 @@ component extends="../base" { stopCommand &= " --all"; } - print.yellowLine("Stopping Wheels development server..."); + detailOutput.header("Stop Wheels Development Server"); + // Show command + detailOutput.subHeader("Executing Stop Command"); + detailOutput.code(stopCommand); // Execute the server stop command command(stopCommand).run(); - print.line(); - print.greenLine("Server stopped successfully!"); - print.line(); + detailOutput.statusSuccess("Server stopped successfully"); } } \ No newline at end of file diff --git a/cli/src/commands/wheels/test/integration.cfc b/cli/src/commands/wheels/test/integration.cfc index 1e7dd4cfe6..35d00c6b6b 100644 --- a/cli/src/commands/wheels/test/integration.cfc +++ b/cli/src/commands/wheels/test/integration.cfc @@ -105,7 +105,7 @@ component aliases='wheels test:integration' extends="../base" { * Create a sample integration test file */ private void function createSampleIntegrationTest(required string directory) { - var sampleTest = 'component extends="wheels.Testbox" { + var sampleTest = 'component extends="wheels.WheelsTest" { function beforeAll() { // Setup test database or test data diff --git a/cli/src/commands/wheels/test/migrate.cfc b/cli/src/commands/wheels/test/migrate.cfc new file mode 100644 index 0000000000..6729674eaa --- /dev/null +++ b/cli/src/commands/wheels/test/migrate.cfc @@ -0,0 +1,224 @@ +/** + * Migrate legacy RocketUnit tests to TestBox BDD syntax + * + * Scans test files for RocketUnit patterns (test_ prefix functions, assert() calls, + * extends="wheels.Test") and converts them to TestBox BDD syntax (describe/it blocks, + * expect() matchers, extends="wheels.WheelsTest"). + * + * Creates .bak backup files before conversion unless --no-backup is specified. + * + * Examples: + * wheels test migrate + * wheels test migrate tests/legacy + * wheels test migrate --dry-run + * wheels test migrate --no-backup + * wheels test migrate tests/RocketUnit/functions/models/User.cfc + */ +component aliases="wheels test:migrate" extends="../base" { + + property name="detailOutput" inject="DetailOutputService@wheels-cli"; + property name="migrationService" inject="TestMigrationService@wheels-cli"; + + /** + * @directory Directory containing test files to migrate (default: tests/) + * @file Single file to migrate instead of a directory + * @recurse Recurse into subdirectories + * @backup Create .bak backup files before conversion + * @dryRun Preview changes without writing files + * @verbose Show detailed output for each file + */ + function run( + string directory = "", + string file = "", + boolean recurse = true, + boolean backup = true, + boolean dryRun = false, + boolean verbose = false + ) { + requireWheelsApp(getCWD()); + + detailOutput.header("Test Migration: RocketUnit -> TestBox BDD"); + + if (arguments.dryRun) { + detailOutput.statusInfo("DRY RUN - no files will be modified"); + detailOutput.line(); + } + + // Single file mode + if (len(arguments.file)) { + var filePath = fileSystemUtil.resolvePath(arguments.file); + if (!fileExists(filePath)) { + detailOutput.error("File not found: #filePath#"); + return; + } + migrateSingleFile(filePath, arguments.backup, arguments.dryRun, arguments.verbose); + return; + } + + // Directory mode + var targetDir = len(arguments.directory) + ? fileSystemUtil.resolvePath(arguments.directory) + : fileSystemUtil.resolvePath("tests/"); + + if (!directoryExists(targetDir)) { + detailOutput.error("Directory not found: #targetDir#"); + return; + } + + migrateDirectoryFiles(targetDir, arguments.recurse, arguments.backup, arguments.dryRun, arguments.verbose); + } + + /** + * Migrate a single file and display results + */ + private function migrateSingleFile( + required string filePath, + boolean backup = true, + boolean dryRun = false, + boolean verbose = false + ) { + detailOutput.metric("File", getFileFromPath(arguments.filePath)); + detailOutput.line(); + + var result = migrationService.migrateTestFile( + filePath = arguments.filePath, + backup = arguments.backup, + dryRun = arguments.dryRun + ); + + if (result.success) { + detailOutput.statusSuccess("Migrated: #getFileFromPath(arguments.filePath)#"); + + if (arrayLen(result.changes) > 0) { + detailOutput.subHeader("Changes"); + for (var change in result.changes) { + detailOutput.output(" - #change#", true); + } + } + + if (arrayLen(result.warnings) > 0) { + detailOutput.line(); + detailOutput.statusWarning("Warnings (#arrayLen(result.warnings)#)"); + for (var warning in result.warnings) { + detailOutput.output(" - #warning#", true); + } + } + + if (arguments.dryRun && len(result.preview)) { + detailOutput.subHeader("Preview"); + detailOutput.code(result.preview); + } + } else { + if (structKeyExists(result, "skipped") && result.skipped) { + detailOutput.skip(getFileFromPath(arguments.filePath) & " (" & result.reason & ")"); + } else { + detailOutput.statusFailed("Failed: #result.error#"); + } + } + } + + /** + * Migrate all files in a directory and display summary + */ + private function migrateDirectoryFiles( + required string directory, + boolean recurse = true, + boolean backup = true, + boolean dryRun = false, + boolean verbose = false + ) { + detailOutput.metric("Directory", arguments.directory); + detailOutput.metric("Recursive", arguments.recurse ? "Yes" : "No"); + detailOutput.metric("Backup", arguments.backup ? "Yes" : "No"); + detailOutput.line(); + + var migrationResult = migrationService.migrateDirectory( + directory = arguments.directory, + recursive = arguments.recurse, + backup = arguments.backup, + dryRun = arguments.dryRun + ); + + var results = migrationResult.results; + var stats = migrationResult.statistics; + + // Display per-file results + var converted = 0; + var skipped = 0; + var failed = 0; + + for (var result in results) { + var fileName = getFileFromPath(result.filePath); + + if (!result.success && !(structKeyExists(result, "skipped") && result.skipped)) { + detailOutput.statusFailed(fileName & ": " & result.error); + failed++; + } else if (structKeyExists(result, "skipped") && result.skipped) { + if (arguments.verbose) { + detailOutput.skip(fileName & " (" & result.reason & ")"); + } + skipped++; + } else { + var changeCount = structKeyExists(result, "changes") ? arrayLen(result.changes) : 0; + if (changeCount > 0) { + detailOutput.create(fileName & " (#changeCount# changes)"); + converted++; + } else { + if (arguments.verbose) { + detailOutput.skip(fileName & " (no RocketUnit patterns found)"); + } + skipped++; + } + + // Show warnings + if (structKeyExists(result, "warnings") && arrayLen(result.warnings) > 0) { + for (var warning in result.warnings) { + detailOutput.statusWarning(" " & warning); + } + } + + // Show detailed changes in verbose mode + if (arguments.verbose && structKeyExists(result, "changes")) { + for (var change in result.changes) { + detailOutput.output(" - #change#", true); + } + } + } + } + + // Summary + detailOutput.line(); + detailOutput.subHeader("Migration Summary"); + detailOutput.metric("Files scanned", arrayLen(results)); + detailOutput.metric("Converted", converted); + detailOutput.metric("Skipped", skipped); + detailOutput.metric("Failed", failed); + + if (stats.totalWarnings > 0) { + detailOutput.metric("Warnings", stats.totalWarnings); + } + + // Generate and display report recommendations + var report = migrationService.generateReport(results); + if (arrayLen(report.recommendations) > 0) { + detailOutput.line(); + detailOutput.nextSteps(report.recommendations); + } + + if (converted > 0) { + detailOutput.line(); + if (arguments.dryRun) { + detailOutput.statusInfo("Re-run without --dry-run to apply changes"); + } else { + detailOutput.statusSuccess("Migration complete! #converted# file(s) converted."); + if (arguments.backup) { + detailOutput.statusInfo("Backup files created with .bak extension"); + } + } + } else if (failed == 0) { + detailOutput.line(); + detailOutput.statusInfo("No RocketUnit tests found to migrate"); + } + } + +} diff --git a/cli/src/commands/wheels/test/unit.cfc b/cli/src/commands/wheels/test/unit.cfc index 7497e77f13..b3d1c1afdd 100644 --- a/cli/src/commands/wheels/test/unit.cfc +++ b/cli/src/commands/wheels/test/unit.cfc @@ -105,7 +105,7 @@ component aliases='wheels test:unit' extends="../base" { * Create a sample unit test file */ private void function createSampleUnitTest(required string directory) { - var sampleTest = 'component extends="wheels.Testbox" { + var sampleTest = 'component extends="wheels.WheelsTest" { function run() { describe("Sample Unit Test", function() { diff --git a/cli/src/commands/wheels/upgrade.cfc b/cli/src/commands/wheels/upgrade.cfc index 98a3256392..19ea566b08 100644 --- a/cli/src/commands/wheels/upgrade.cfc +++ b/cli/src/commands/wheels/upgrade.cfc @@ -6,7 +6,7 @@ * * {code:bash} * wheels upgrade - * wheels upgrade to=3.0.0 + * wheels upgrade to=3.1.0 * wheels upgrade check=true * {code} **/ @@ -174,6 +174,7 @@ component extends="base" { // In a real implementation, this would query ForgeBox API // For now, return a static list return [ + "3.1.0", "3.0.0", "2.5.0", "2.4.1", diff --git a/cli/src/models/AdminIntrospectionService.cfc b/cli/src/models/AdminIntrospectionService.cfc new file mode 100644 index 0000000000..4d02fb3571 --- /dev/null +++ b/cli/src/models/AdminIntrospectionService.cfc @@ -0,0 +1,341 @@ +component { + + property name="helpers" inject="helpers@wheels-cli"; + + /** + * Initialize the service + */ + public function init() { + return this; + } + + /** + * Introspect a model and return a struct of metadata suitable for admin CRUD template generation. + * + * The returned struct contains: + * - modelName (string): singular PascalCase name + * - pluralName (string): plural PascalCase name + * - displayName (string): human-readable singular + * - displayNamePlural (string): human-readable plural + * - tableName (string): database table name + * - primaryKey (string): primary key property name(s) + * - softDeletion (boolean): whether model uses soft delete + * - fields (array): ordered field metadata for templates + * - associations (array): association metadata + * - validationSummary (struct): property-keyed validation rules + * - enums (struct): enum definitions + * - scopes (array): scope names + * + * @modelInstance A Wheels model instance (from model("ModelName")) + */ + public struct function introspect(required any modelInstance) { + var classData = arguments.modelInstance.classInfo(); + var result = {}; + + // Core naming + result.modelName = classData.modelName; + result.pluralName = capitalize(pluralizeWord(classData.modelName)); + result.displayName = humanizeWord(classData.modelName); + result.displayNamePlural = humanizeWord(pluralizeWord(classData.modelName)); + result.tableName = classData.tableName; + result.primaryKey = classData.primaryKeys; + result.softDeletion = classData.softDeletion; + + // Build validation summary (property -> rules) for quick lookup + result.validationSummary = buildValidationSummary(classData.validations); + + // Build enum metadata + result.enums = classData.enums; + + // Build scope list + result.scopes = listToArray(structKeyList(classData.scopes)); + + // Build association metadata + result.associations = buildAssociationList(classData.associations); + + // Build ordered field list โ€” the main output for templates + result.fields = buildFieldList( + properties = classData.properties, + primaryKeys = classData.primaryKeys, + associations = classData.associations, + validationSummary = result.validationSummary, + enums = classData.enums + ); + + return result; + } + + /** + * Build an ordered array of field metadata for template consumption. + * Each field struct contains everything a template needs to render form inputs, + * table columns, and display labels. + */ + private array function buildFieldList( + required struct properties, + required string primaryKeys, + required struct associations, + required struct validationSummary, + required struct enums + ) { + var fields = []; + var pkList = arguments.primaryKeys; + var fkSet = buildForeignKeySet(arguments.associations); + + for (var propName in arguments.properties) { + var prop = arguments.properties[propName]; + var field = {}; + + field.name = propName; + field.column = structKeyExists(prop, "column") ? prop.column : propName; + field.label = structKeyExists(prop, "label") ? prop.label : humanizeWord(propName); + field.dataType = structKeyExists(prop, "type") ? prop.type : "string"; + field.sqlType = structKeyExists(prop, "dataType") ? prop.dataType : "cf_sql_varchar"; + field.maxLength = structKeyExists(prop, "size") ? prop.size : 0; + field.nullable = structKeyExists(prop, "nullable") ? prop.nullable : true; + field.defaultValue = structKeyExists(prop, "columnDefault") ? prop.columnDefault : ""; + field.scale = structKeyExists(prop, "scale") ? prop.scale : 0; + field.isPrimaryKey = listFindNoCase(pkList, propName) > 0; + field.isForeignKey = structKeyExists(fkSet, propName); + field.foreignKeyTo = field.isForeignKey ? fkSet[propName] : ""; + + // Validation hints from summary + var propValidations = structKeyExists(arguments.validationSummary, propName) + ? arguments.validationSummary[propName] + : {}; + field.required = structKeyExists(propValidations, "presence") ? true : false; + field.unique = structKeyExists(propValidations, "uniqueness") ? true : false; + field.validations = propValidations; + + // Enum detection + field.isEnum = structKeyExists(arguments.enums, propName); + field.enumValues = field.isEnum ? arguments.enums[propName].values : {}; + + // Determine HTML input type + field.inputType = resolveInputType( + dataType = field.dataType, + sqlType = field.sqlType, + propName = propName, + isEnum = field.isEnum, + isForeignKey = field.isForeignKey + ); + + // Display hints for templates + field.inList = !field.isPrimaryKey && !listFindNoCase("text,binary", field.dataType); + field.inForm = !field.isPrimaryKey; + field.inShow = true; + + arrayAppend(fields, field); + } + + return fields; + } + + /** + * Map model data types and property names to HTML input types for form generation. + */ + private string function resolveInputType( + required string dataType, + required string sqlType, + required string propName, + required boolean isEnum, + required boolean isForeignKey + ) { + // Foreign keys -> select dropdown + if (arguments.isForeignKey) { + return "select"; + } + + // Enums -> select dropdown + if (arguments.isEnum) { + return "select"; + } + + // Name-based heuristics + var nameLower = lCase(arguments.propName); + if (findNoCase("email", nameLower)) return "email"; + if (findNoCase("password", nameLower)) return "password"; + if (findNoCase("url", nameLower) || findNoCase("website", nameLower)) return "url"; + if (findNoCase("phone", nameLower) || findNoCase("tel", nameLower)) return "tel"; + if (findNoCase("color", nameLower) || findNoCase("colour", nameLower)) return "color"; + if (findNoCase("search", nameLower)) return "search"; + + // Type-based mapping + switch (arguments.dataType) { + case "string": + return "text"; + case "text": + return "textarea"; + case "integer": case "float": case "decimal": case "biginteger": + return "number"; + case "boolean": + return "checkbox"; + case "date": + return "date"; + case "datetime": case "timestamp": + return "datetime-local"; + case "time": + return "time"; + case "binary": + return "file"; + default: + break; + } + + // SQL type fallback + if (findNoCase("text", arguments.sqlType) || findNoCase("clob", arguments.sqlType)) { + return "textarea"; + } + if (findNoCase("int", arguments.sqlType)) { + return "number"; + } + if (findNoCase("bit", arguments.sqlType) || findNoCase("boolean", arguments.sqlType)) { + return "checkbox"; + } + if (findNoCase("date", arguments.sqlType) && findNoCase("time", arguments.sqlType)) { + return "datetime-local"; + } + if (findNoCase("date", arguments.sqlType)) { + return "date"; + } + if (findNoCase("time", arguments.sqlType)) { + return "time"; + } + + return "text"; + } + + /** + * Build a set of foreign key property names mapped to their associated model name. + */ + private struct function buildForeignKeySet(required struct associations) { + var fkSet = {}; + for (var assocName in arguments.associations) { + var assoc = arguments.associations[assocName]; + if (structKeyExists(assoc, "type") && assoc.type == "belongsTo" && structKeyExists(assoc, "foreignKey")) { + var fkList = assoc.foreignKey; + var modelName = structKeyExists(assoc, "modelName") ? assoc.modelName : assocName; + for (var fk in listToArray(fkList)) { + fkSet[fk] = modelName; + } + } + } + return fkSet; + } + + /** + * Transform the raw validations struct (keyed by trigger) into a property-keyed summary. + * Returns a struct where each key is a property name, and the value is a struct of validation types. + * + * Example output: + * { + * email: {presence: true, uniqueness: true, format: {regEx: "..."}}, + * firstName: {presence: true} + * } + */ + private struct function buildValidationSummary(required struct validations) { + var summary = {}; + + // Map internal method names to friendly validation types + var methodMap = { + "$validatesPresenceOf": "presence", + "$validatesUniquenessOf": "uniqueness", + "$validatesFormatOf": "format", + "$validatesLengthOf": "length", + "$validatesNumericalityOf": "numericality", + "$validatesInclusionOf": "inclusion", + "$validatesExclusionOf": "exclusion", + "$validatesConfirmationOf": "confirmation" + }; + + // Iterate over all trigger types (onSave, onCreate, onUpdate) + for (var trigger in arguments.validations) { + var rules = arguments.validations[trigger]; + if (!isArray(rules)) continue; + + for (var rule in rules) { + // Wheels stores validations as {method, args: {property, message, ...}} + if (!structKeyExists(rule, "args") || !structKeyExists(rule.args, "property")) continue; + + var validationType = structKeyExists(methodMap, rule.method) + ? methodMap[rule.method] + : rule.method; + + // Build a params struct from args (exclude property itself) + var params = {}; + for (var argKey in rule.args) { + if (argKey != "property") { + params[argKey] = rule.args[argKey]; + } + } + + var propName = rule.args.property; + if (!structKeyExists(summary, propName)) { + summary[propName] = {}; + } + // Store params if any, otherwise just true + if (structIsEmpty(params)) { + summary[propName][validationType] = true; + } else { + summary[propName][validationType] = params; + } + } + } + + return summary; + } + + /** + * Build a clean array of association metadata for templates. + */ + private array function buildAssociationList(required struct associations) { + var result = []; + for (var assocName in arguments.associations) { + var assoc = arguments.associations[assocName]; + var item = {}; + item.name = assocName; + item.type = structKeyExists(assoc, "type") ? assoc.type : ""; + item.modelName = structKeyExists(assoc, "modelName") ? assoc.modelName : ""; + item.foreignKey = structKeyExists(assoc, "foreignKey") ? assoc.foreignKey : ""; + item.joinKey = structKeyExists(assoc, "joinKey") ? assoc.joinKey : ""; + item.dependent = structKeyExists(assoc, "dependent") ? assoc.dependent : ""; + item.nested = structKeyExists(assoc, "nested") ? assoc.nested : {}; + arrayAppend(result, item); + } + return result; + } + + /** + * Local capitalize fallback + */ + private string function capitalize(required string str) { + if (structKeyExists(variables, "helpers") && isObject(variables.helpers)) { + return variables.helpers.capitalize(arguments.str); + } + if (len(arguments.str) == 0) return ""; + return uCase(left(arguments.str, 1)) & mid(arguments.str, 2, len(arguments.str) - 1); + } + + /** + * Local pluralize fallback + */ + private string function pluralizeWord(required string word) { + if (structKeyExists(variables, "helpers") && isObject(variables.helpers)) { + return variables.helpers.pluralize(arguments.word); + } + return arguments.word & "s"; + } + + /** + * Local humanize fallback โ€” converts camelCase/PascalCase to "Human Readable" + */ + private string function humanizeWord(required string text) { + if (structKeyExists(variables, "helpers") && isObject(variables.helpers)) { + return variables.helpers.humanize(arguments.text); + } + // Simple fallback: insert space before capitals, capitalize first letter + var result = reReplace(arguments.text, "([A-Z])", " \1", "all"); + result = trim(result); + return uCase(left(result, 1)) & mid(result, 2, len(result) - 1); + } + +} diff --git a/cli/src/models/AdminViewService.cfc b/cli/src/models/AdminViewService.cfc new file mode 100644 index 0000000000..8175e40852 --- /dev/null +++ b/cli/src/models/AdminViewService.cfc @@ -0,0 +1,427 @@ +/** + * Generates admin index and show view files from model introspection metadata. + * + * Consumes the output of AdminIntrospectionService.introspect() and produces + * type-aware admin views with sortable columns, search, pagination, and + * association display. + * + * Usage (WireBox): + * adminViewService.generateIndexView(metadata=introspectionResult, baseDirectory=appRoot) + * + * Usage (standalone / testing): + * var svc = new AdminViewService(helpers=helpersInstance, templateDir="/path/to/templates") + * var content = svc.renderIndexContent(metadata=introspectionResult) + */ +component { + + property name="templateService" inject="TemplateService@wheels-cli"; + property name="helpers" inject="helpers@wheels-cli"; + + /** + * Initialize for standalone usage (testing, lucli). + */ + public function init(any helpers, any templateService, string templateDir = "") { + if (!isNull(arguments.helpers)) variables.helpers = arguments.helpers; + if (!isNull(arguments.templateService)) variables.templateService = arguments.templateService; + if (len(arguments.templateDir)) variables.templateDir = arguments.templateDir; + return this; + } + + // โ”€โ”€ Public API: file generation โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + + /** + * Generate admin index view file from introspection metadata. + * + * @metadata Struct from AdminIntrospectionService.introspect() + * @baseDirectory Application root directory + * @return {success: boolean, path: string, message|error: string} + */ + public struct function generateIndexView( + required struct metadata, + string baseDirectory = "" + ) { + var context = buildIndexContext(arguments.metadata); + var pluralLower = lCase(pluralizeWord(arguments.metadata.modelName)); + try { + var path = variables.templateService.generateFromTemplate( + template = "admin/index.txt", + destination = "views/admin/#pluralLower#/index.cfm", + context = context, + baseDirectory = arguments.baseDirectory + ); + return {success: true, path: path, message: "Admin index view generated"}; + } catch (any e) { + return {success: false, error: e.message, path: ""}; + } + } + + /** + * Generate admin show view file from introspection metadata. + * + * @metadata Struct from AdminIntrospectionService.introspect() + * @baseDirectory Application root directory + * @return {success: boolean, path: string, message|error: string} + */ + public struct function generateShowView( + required struct metadata, + string baseDirectory = "" + ) { + var context = buildShowContext(arguments.metadata); + var pluralLower = lCase(pluralizeWord(arguments.metadata.modelName)); + try { + var path = variables.templateService.generateFromTemplate( + template = "admin/show.txt", + destination = "views/admin/#pluralLower#/show.cfm", + context = context, + baseDirectory = arguments.baseDirectory + ); + return {success: true, path: path, message: "Admin show view generated"}; + } catch (any e) { + return {success: false, error: e.message, path: ""}; + } + } + + // โ”€โ”€ Public API: content rendering (testing) โ”€โ”€ + + /** + * Render admin index view content as string without writing to disk. + */ + public string function renderIndexContent(required struct metadata) { + var context = buildIndexContext(arguments.metadata); + var templateContent = readTemplate("admin/index.txt"); + return processAdminTemplate(templateContent, context, arguments.metadata.modelName); + } + + /** + * Render admin show view content as string without writing to disk. + */ + public string function renderShowContent(required struct metadata) { + var context = buildShowContext(arguments.metadata); + var templateContent = readTemplate("admin/show.txt"); + return processAdminTemplate(templateContent, context, arguments.metadata.modelName); + } + + // โ”€โ”€ Context builders โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + + private struct function buildIndexContext(required struct metadata) { + return { + modelName: capitalize(arguments.metadata.modelName), + AdminTableHeaders: buildSortableHeaders(arguments.metadata.fields), + AdminTableCells: buildTypedTableCells(arguments.metadata.fields) + }; + } + + private struct function buildShowContext(required struct metadata) { + return { + modelName: capitalize(arguments.metadata.modelName), + AdminShowFields: buildShowFieldRows(arguments.metadata.fields), + AdminAssociationSections: buildAssociationSections(arguments.metadata.associations) + }; + } + + // โ”€โ”€ Index view: sortable headers โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + + /** + * Build sortable table header columns. + * Uses the _sortLink() helper defined in admin/index.txt. + */ + private string function buildSortableHeaders(required array fields) { + var h = chr(35); + var nl = chr(10); + var indent = repeatString(chr(9), 7); + var headers = []; + + for (var field in arguments.fields) { + if (!field.inList || field.isPrimaryKey) continue; + arrayAppend(headers, + indent & "" & h & '_sortLink("' & field.name & '", "' & field.label & '")' & h & "" + ); + } + + return arrayToList(headers, nl); + } + + // โ”€โ”€ Index view: typed table cells โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + + /** + * Build type-aware table cells for the admin index view. + * Each cell renders the field value with appropriate formatting. + */ + private string function buildTypedTableCells(required array fields) { + var nl = chr(10); + var indent = repeatString(chr(9), 7); + var cells = []; + + for (var field in arguments.fields) { + if (!field.inList || field.isPrimaryKey) continue; + arrayAppend(cells, indent & "" & formatCellForType(field) & ""); + } + + return arrayToList(cells, nl); + } + + /** + * Render a single table cell based on field type metadata. + */ + private string function formatCellForType(required struct field) { + var h = chr(35); + var qv = "|ObjectNamePlural|." & field.name; + + // Foreign key โ€” show associated model name + if (field.isForeignKey && len(field.foreignKeyTo)) { + return h & "encodeForHTML(|ObjectNamePlural|." & lCase(field.foreignKeyTo) & ".name)" & h; + } + + // Enum โ€” badge + if (field.isEnum) { + return '' & h & "encodeForHTML(" & qv & ")" & h & ""; + } + + // Boolean โ€” Yes/No badges + if (field.dataType == "boolean" || field.inputType == "checkbox") { + return "Yes' + & 'No'; + } + + // Email โ€” mailto link + if (field.inputType == "email") { + return '' + & h & "encodeForHTML(" & qv & ")" & h & ""; + } + + // Date + if (field.inputType == "date") { + return h & 'dateFormat(' & qv & ', "yyyy-mm-dd")' & h; + } + + // Datetime / timestamp + if (field.inputType == "datetime-local" || field.dataType == "datetime" || field.dataType == "timestamp") { + return h & 'dateTimeFormat(' & qv & ', "yyyy-mm-dd HH:nn")' & h; + } + + // Time + if (field.inputType == "time") { + return h & 'timeFormat(' & qv & ', "HH:nn:ss")' & h; + } + + // Text / textarea โ€” truncate in list view (encode after truncating to avoid splitting HTML entities) + if (field.inputType == "textarea" || field.dataType == "text") { + return h & "encodeForHTML(left(" & qv & ", 100))" & h; + } + + // Default โ€” encode for safety + return h & "encodeForHTML(" & qv & ")" & h; + } + + // โ”€โ”€ Show view: detail field rows โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + + /** + * Build detail table rows for the admin show view. + * Each row displays a label and type-aware value. + */ + private string function buildShowFieldRows(required array fields) { + var nl = chr(10); + var indent = repeatString(chr(9), 5); + var rows = []; + + for (var field in arguments.fields) { + if (!field.inShow) continue; + var row = indent & "" & nl; + row &= indent & chr(9) & '' & field.label & "" & nl; + row &= indent & chr(9) & "" & formatShowValueForType(field) & "" & nl; + row &= indent & ""; + arrayAppend(rows, row); + } + + return arrayToList(rows, nl); + } + + /** + * Render a single show value based on field type metadata. + */ + private string function formatShowValueForType(required struct field) { + var h = chr(35); + var ov = "|ObjectNameSingular|." & field.name; + + // Primary key โ€” display as-is + if (field.isPrimaryKey) { + return h & ov & h; + } + + // Foreign key โ€” show associated model name + if (field.isForeignKey && len(field.foreignKeyTo)) { + return h & "encodeForHTML(|ObjectNameSingular|." & lCase(field.foreignKeyTo) & ".name)" & h; + } + + // Enum โ€” badge + if (field.isEnum) { + return '' & h & "encodeForHTML(" & ov & ")" & h & ""; + } + + // Boolean โ€” yesNoFormat + if (field.dataType == "boolean" || field.inputType == "checkbox") { + return h & "yesNoFormat(" & ov & ")" & h; + } + + // Email โ€” mailto link + if (field.inputType == "email") { + return '' + & h & "encodeForHTML(" & ov & ")" & h & ""; + } + + // URL โ€” hyperlink + if (field.inputType == "url") { + return '' + & h & "encodeForHTML(" & ov & ")" & h & ""; + } + + // Date + if (field.inputType == "date") { + return h & 'dateFormat(' & ov & ', "yyyy-mm-dd")' & h; + } + + // Datetime / timestamp + if (field.inputType == "datetime-local" || field.dataType == "datetime" || field.dataType == "timestamp") { + return h & 'dateTimeFormat(' & ov & ', "yyyy-mm-dd HH:nn:ss")' & h; + } + + // Time + if (field.inputType == "time") { + return h & 'timeFormat(' & ov & ', "HH:nn:ss")' & h; + } + + // Text โ€” full display with word break + if (field.inputType == "textarea" || field.dataType == "text") { + return '
' & h & "encodeForHTML(" & ov & ")" & h & "
"; + } + + // Default โ€” encode for safety + return h & "encodeForHTML(" & ov & ")" & h; + } + + // โ”€โ”€ Show view: association sections โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + + /** + * Build association display sections for the show view. + * HasMany/HasOne associations get card placeholders. + * BelongsTo associations are already displayed as fields via foreign key handling. + */ + private string function buildAssociationSections(required array associations) { + var nl = chr(10); + var sections = []; + + for (var assoc in arguments.associations) { + if (assoc.type == "hasMany") { + arrayAppend(sections, buildHasManySection(assoc)); + } else if (assoc.type == "hasOne") { + arrayAppend(sections, buildHasOneSection(assoc)); + } + } + + return arrayToList(sections, nl & nl); + } + + private string function buildHasManySection(required struct assoc) { + var nl = chr(10); + var t = chr(9); + var label = humanizeWord(assoc.name); + var section = t & '
' & nl; + section &= t & t & '
' & nl; + section &= t & t & t & '
' & label & '
' & nl; + section &= t & t & '
' & nl; + section &= t & t & '
' & nl; + section &= t & t & t & '

Associated ' & lCase(label) & ' records. Customize this section to display related data.

' & nl; + section &= t & t & '
' & nl; + section &= t & '
'; + return section; + } + + private string function buildHasOneSection(required struct assoc) { + var nl = chr(10); + var t = chr(9); + var label = humanizeWord(assoc.name); + var section = t & '
' & nl; + section &= t & t & '
' & nl; + section &= t & t & t & '
' & label & '
' & nl; + section &= t & t & '
' & nl; + section &= t & t & '
' & nl; + section &= t & t & t & '

Associated ' & lCase(label) & ' record. Customize this section to display related data.

' & nl; + section &= t & t & '
' & nl; + section &= t & '
'; + return section; + } + + // โ”€โ”€ Template processing (standalone mode) โ”€โ”€โ”€ + + /** + * Read template file from the configured template directory. + */ + private string function readTemplate(required string templateName) { + if (structKeyExists(variables, "templateDir") && len(variables.templateDir)) { + var path = variables.templateDir; + if (right(path, 1) != "/" && right(path, 1) != "\") path &= "/"; + path &= arguments.templateName; + if (fileExists(path)) return fileRead(path); + } + throw(type="AdminViewService.TemplateNotFound", message="Template not found: #arguments.templateName#"); + } + + /** + * Process admin template: replace admin markers and |ObjectName| placeholders. + * Used by render*Content() methods for standalone operation. + */ + private string function processAdminTemplate( + required string content, + required struct context, + required string modelName + ) { + var processed = arguments.content; + + // Replace {{key}} markers with generated content + for (var key in arguments.context) { + var value = arguments.context[key]; + if (isSimpleValue(value)) { + processed = replace(processed, "{{" & key & "}}", value, "all"); + } + } + + // Replace |ObjectName| pipe placeholders + var name = capitalize(arguments.modelName); + var plural = pluralizeWord(name); + processed = replace(processed, "|ObjectNameSingular|", lCase(name), "all"); + processed = replace(processed, "|ObjectNamePlural|", lCase(plural), "all"); + processed = replace(processed, "|ObjectNameSingularC|", name, "all"); + processed = replace(processed, "|ObjectNamePluralC|", plural, "all"); + + return processed; + } + + // โ”€โ”€ Utility methods (with injection fallbacks) โ”€โ”€ + + private string function capitalize(required string str) { + if (structKeyExists(variables, "helpers") && isObject(variables.helpers)) { + return variables.helpers.capitalize(arguments.str); + } + if (len(arguments.str) == 0) return ""; + return uCase(left(arguments.str, 1)) & mid(arguments.str, 2, len(arguments.str) - 1); + } + + private string function pluralizeWord(required string word) { + if (structKeyExists(variables, "helpers") && isObject(variables.helpers)) { + return variables.helpers.pluralize(arguments.word); + } + return arguments.word & "s"; + } + + private string function humanizeWord(required string text) { + if (structKeyExists(variables, "helpers") && isObject(variables.helpers)) { + return variables.helpers.humanize(arguments.text); + } + var result = reReplace(arguments.text, "([A-Z])", " \1", "all"); + result = trim(result); + if (len(result) == 0) return ""; + return uCase(left(result, 1)) & mid(result, 2, len(result) - 1); + } + +} diff --git a/cli/src/models/AnalysisService.cfc b/cli/src/models/AnalysisService.cfc index 056af4ec94..f62d44da10 100644 --- a/cli/src/models/AnalysisService.cfc +++ b/cli/src/models/AnalysisService.cfc @@ -146,7 +146,8 @@ component { "packages/", "coldbox/", "modules/", - "WEB-INF/" + "WEB-INF/", + "migrator/" ], "wheels": { "check-deprecated": true, diff --git a/cli/src/models/CodeGenerationService.cfc b/cli/src/models/CodeGenerationService.cfc index 7b61bf087b..c403023304 100644 --- a/cli/src/models/CodeGenerationService.cfc +++ b/cli/src/models/CodeGenerationService.cfc @@ -225,13 +225,15 @@ component { string baseDirectory = "", array properties = [], string belongsTo = "", - string hasMany = "" + string hasMany = "", + boolean admin = false, + array adminFields = [] ) { var controllerName = capitalize(arguments.name); var viewDir = resolvePath("views/#lCase(controllerName)#", arguments.baseDirectory); var fileName = arguments.action & ".cfm"; var filePath = viewDir & "/" & fileName; - + // Check if file exists if (fileExists(filePath) && !arguments.force) { return { @@ -240,36 +242,37 @@ component { path: filePath }; } - + // Create view directory if needed if (!directoryExists(viewDir)) { directoryCreate(viewDir); } - + // Determine template to use if (!len(arguments.template)) { - // Auto-detect template based on action name + // Select template directory based on admin flag + var templateDir = arguments.admin ? "admin" : "crud"; switch (arguments.action) { case "index": - arguments.template = "crud/index.txt"; + arguments.template = "#templateDir#/index.txt"; break; case "show": - arguments.template = "crud/show.txt"; + arguments.template = "#templateDir#/show.txt"; break; case "new": - arguments.template = "crud/new.txt"; + arguments.template = "#templateDir#/new.txt"; break; case "edit": - arguments.template = "crud/edit.txt"; + arguments.template = "#templateDir#/edit.txt"; break; case "_form": - arguments.template = "crud/_form.txt"; + arguments.template = "#templateDir#/_form.txt"; break; default: arguments.template = "ViewContent.txt"; } } - + // Prepare template context var context = { controllerName: controllerName, @@ -280,7 +283,12 @@ component { belongsTo: arguments.belongsTo, hasMany: arguments.hasMany }; - + + // Add admin field metadata to context when generating admin views + if (arguments.admin && arrayLen(arguments.adminFields)) { + context.adminFields = arguments.adminFields; + } + // Generate from template try { var generatedPath = templateService.generateFromTemplate( @@ -289,7 +297,7 @@ component { context = context, baseDirectory = arguments.baseDirectory ); - + return { success: true, path: generatedPath, diff --git a/cli/src/models/EnvironmentService.cfc b/cli/src/models/EnvironmentService.cfc index 91735c408d..991973880e 100644 --- a/cli/src/models/EnvironmentService.cfc +++ b/cli/src/models/EnvironmentService.cfc @@ -19,6 +19,8 @@ component { string username = "", string password = "", string sid = "", + string servicename = "", + string oracleConnectionType = "sid", boolean force = false, string base = "", boolean debug = false, @@ -49,7 +51,9 @@ component { port = arguments.port, username = arguments.username, password = arguments.password, - sid = arguments.sid + sid = arguments.sid, + servicename = arguments.servicename, + oracleConnectionType = arguments.oracleConnectionType ); } @@ -140,11 +144,6 @@ component { string sort = "name", boolean help = false ) { - // Show help information if requested - if (arguments.help) { - return getHelpInformation(); - } - var environments = []; var projectRoot = arguments.rootPath; @@ -273,8 +272,8 @@ component { // Check if .env file exists if (!fileExists(envFile)) { - // Create new .env file with wheels_env variable - fileWrite(envFile, "wheels_env=#arguments.environment#"); + // Create new .env file with WHEELS_ENV variable + fileWrite(envFile, "WHEELS_ENV=#arguments.environment#"); return { success: true, message: "Created new .env file and set environment to '#arguments.environment#'", @@ -301,24 +300,24 @@ component { continue; } - // Check for wheels_env or environment variable - if (findNoCase("wheels_env=", trimmedLine) == 1) { + // Check for WHEELS_ENV or environment variable + if (findNoCase("WHEELS_ENV=", trimmedLine) == 1) { oldEnvironment = listLast(trimmedLine, "="); - updatedLines.append("wheels_env=#arguments.environment#"); + updatedLines.append("WHEELS_ENV=#arguments.environment#"); envVarFound = true; } else if (!envVarFound && findNoCase("environment=", trimmedLine) == 1) { oldEnvironment = listLast(trimmedLine, "="); - // Replace environment with wheels_env - updatedLines.append("wheels_env=#arguments.environment#"); + // Replace environment with WHEELS_ENV + updatedLines.append("WHEELS_ENV=#arguments.environment#"); envVarFound = true; } else { updatedLines.append(line); } } - // If no environment variable was found, add wheels_env + // If no environment variable was found, add WHEELS_ENV if (!envVarFound) { - updatedLines.append("wheels_env=#arguments.environment#"); + updatedLines.append("WHEELS_ENV=#arguments.environment#"); } // Write updated content back to .env file @@ -401,8 +400,8 @@ component { for (var line in envLines) { var trimmedLine = trim(line); - // Check for wheels_env first - if (findNoCase("wheels_env=", trimmedLine) == 1) { + // Check for WHEELS_ENV first + if (findNoCase("WHEELS_ENV=", trimmedLine) == 1) { return trim(listLast(trimmedLine, "=")); } // Fallback to environment @@ -498,6 +497,8 @@ component { var dbUsername = len(trim(arguments.username)) ? arguments.username : getDefaultUsername(arguments.dbtype); var dbPassword = len(trim(arguments.password)) ? arguments.password : getDefaultPassword(arguments.dbtype); var dbSid = len(trim(arguments.sid)) ? arguments.sid : "ORCL"; + var dbServiceName = len(trim(arguments.servicename)) ? arguments.servicename : ""; + var dbOracleConnectionType = len(trim(arguments.oracleConnectionType)) ? arguments.oracleConnectionType : "sid"; // Database-specific configuration switch (arguments.dbtype) { @@ -535,7 +536,7 @@ component { }; break; case "oracle": - config.datasourceInfo = { + var oracleDsInfo = { driver: "Oracle", host: dbHost, port: dbPort, @@ -543,8 +544,17 @@ component { datasource: datasourceName, username: dbUsername, password: dbPassword, - sid: dbSid + oracleConnectionType: dbOracleConnectionType }; + + // Add SID or Service Name based on connection type + if (dbOracleConnectionType == "servicename") { + oracleDsInfo.servicename = dbServiceName; + } else { + oracleDsInfo.sid = dbSid; + } + + config.datasourceInfo = oracleDsInfo; break; case "sqlite": // SQLite requires absolute path - calculate it now @@ -756,9 +766,17 @@ component { arrayAppend(envContent, "DB_USER=#arguments.config.datasourceInfo.username#"); arrayAppend(envContent, "DB_PASSWORD=#arguments.config.datasourceInfo.password#"); - // Add Oracle SID if exists - if (arguments.config.dbtype == "oracle" && structKeyExists(arguments.config.datasourceInfo, "sid")) { - arrayAppend(envContent, "DB_SID=#arguments.config.datasourceInfo.sid#"); + // Add Oracle connection details if exists + if (arguments.config.dbtype == "oracle") { + if (structKeyExists(arguments.config.datasourceInfo, "sid") && len(trim(arguments.config.datasourceInfo.sid))) { + arrayAppend(envContent, "DB_SID=#arguments.config.datasourceInfo.sid#"); + } + if (structKeyExists(arguments.config.datasourceInfo, "servicename") && len(trim(arguments.config.datasourceInfo.servicename))) { + arrayAppend(envContent, "DB_SERVICENAME=#arguments.config.datasourceInfo.servicename#"); + } + if (structKeyExists(arguments.config.datasourceInfo, "oracleConnectionType")) { + arrayAppend(envContent, "DB_ORACLE_CONNECTION_TYPE=#arguments.config.datasourceInfo.oracleConnectionType#"); + } } // Add datasource name @@ -975,10 +993,14 @@ component { }; break; case "oracle": + // Oracle connection string depends on connection type (SID vs Service Name) + // Check for Service Name first, fallback to SID config = { class: "oracle.jdbc.OracleDriver", bundleName: "org.lucee.oracle", - connectionString: "jdbc:oracle:thin:@##this.env.DB_HOST##:##this.env.DB_PORT##:##this.env.DB_SID##" + connectionString: "jdbc:oracle:thin:@##this.env.DB_HOST##:##this.env.DB_PORT##" & + "##(structKeyExists(this.env, 'DB_ORACLE_CONNECTION_TYPE') && this.env.DB_ORACLE_CONNECTION_TYPE == 'servicename' ? '/' : ':')##" & + "##(structKeyExists(this.env, 'DB_ORACLE_CONNECTION_TYPE') && this.env.DB_ORACLE_CONNECTION_TYPE == 'servicename' ? this.env.DB_SERVICENAME : this.env.DB_SID)##" }; break; case "sqlite": @@ -1035,6 +1057,17 @@ component { serverJson.env[arguments.environment]["DB_DATABASE"] = ds.database; serverJson.env[arguments.environment]["DB_USER"] = ds.username; serverJson.env[arguments.environment]["DB_PASSWORD"] = ds.password; + + // Add Oracle-specific connection details + if (structKeyExists(ds, "oracleConnectionType")) { + serverJson.env[arguments.environment]["DB_ORACLE_CONNECTION_TYPE"] = ds.oracleConnectionType; + } + if (structKeyExists(ds, "sid") && len(trim(ds.sid))) { + serverJson.env[arguments.environment]["DB_SID"] = ds.sid; + } + if (structKeyExists(ds, "servicename") && len(trim(ds.servicename))) { + serverJson.env[arguments.environment]["DB_SERVICENAME"] = ds.servicename; + } } fileWrite(serverJsonPath, serializeJSON(serverJson, true)); @@ -1188,20 +1221,17 @@ box server start port=8080 host=0.0.0.0"; switch (arguments.template) { case "docker": - arrayAppend(steps, "1. Start Docker environment: docker-compose -f docker-compose.#arguments.environment#.yml up"); - arrayAppend(steps, "2. Access application at: http://localhost:8080"); - arrayAppend(steps, "3. Stop environment: docker-compose -f docker-compose.#arguments.environment#.yml down"); + arrayAppend(steps, "Start Docker environment: docker-compose -f docker-compose.#arguments.environment#.yml up"); + arrayAppend(steps, "Stop environment: docker-compose -f docker-compose.#arguments.environment#.yml down"); break; case "vagrant": - arrayAppend(steps, "1. Start Vagrant VM: vagrant up"); - arrayAppend(steps, "2. Access application at: http://localhost:8080 or http://192.168.56.10:8080"); - arrayAppend(steps, "3. SSH into VM: vagrant ssh"); - arrayAppend(steps, "4. Stop VM: vagrant halt"); + arrayAppend(steps, "Start Vagrant VM: vagrant up"); + arrayAppend(steps, "SSH into VM: vagrant ssh"); + arrayAppend(steps, "Stop VM: vagrant halt"); break; default: - arrayAppend(steps, "1. Switch to environment: wheels env switch #arguments.environment#"); - arrayAppend(steps, "2. Start server: box server start"); - arrayAppend(steps, "3. Access application at: http://localhost:8080"); + arrayAppend(steps, "Switch to environment: wheels env switch #arguments.environment#"); + arrayAppend(steps, "Start server: box server start"); } return steps; @@ -1520,6 +1550,9 @@ sudo -u postgres psql -c ""GRANT ALL PRIVILEGES ON DATABASE #arguments.databaseN case "production": include = (env.TYPE == "Production"); break; + case "custom": + include = (env.TYPE == "Custom"); + break; case "qa": include = (env.TYPE == "QA"); break; @@ -1658,43 +1691,6 @@ sudo -u postgres psql -c ""GRANT ALL PRIVILEGES ON DATABASE #arguments.databaseN }; } - /** - * Get help information - */ - private function getHelpInformation() { - var help = []; - arrayAppend(help, "wheels env list - List available environments"); - arrayAppend(help, ""); - arrayAppend(help, "Options:"); - arrayAppend(help, " --format Output format (table, json, yaml) [default: table]"); - arrayAppend(help, " --verbose Show detailed configuration"); - arrayAppend(help, " --check Validate environment configurations"); - arrayAppend(help, " --filter Filter by environment type"); - arrayAppend(help, " --sort Sort by (name, type, modified) [default: name]"); - arrayAppend(help, " --help Show this help information"); - arrayAppend(help, ""); - arrayAppend(help, "Filter options:"); - arrayAppend(help, " All Show all environments (default)"); - arrayAppend(help, " local Local environments only"); - arrayAppend(help, " development Development environments"); - arrayAppend(help, " staging Staging environments"); - arrayAppend(help, " production Production environments"); - arrayAppend(help, " file File-based environments"); - arrayAppend(help, " server.json Server.json environments"); - arrayAppend(help, " valid Valid environments only"); - arrayAppend(help, " issues Environments with issues"); - arrayAppend(help, ""); - arrayAppend(help, "Examples:"); - arrayAppend(help, " wheels env list"); - arrayAppend(help, " wheels env list --verbose"); - arrayAppend(help, " wheels env list --format json"); - arrayAppend(help, " wheels env list --filter production --check"); - arrayAppend(help, " wheels env list --sort modified --verbose"); - - return arrayToList(help, chr(10)); - } - - /** * Gets the current environment using the same logic as Application.cfc * @projectRoot The root directory of the CFWheels project @@ -1715,8 +1711,8 @@ sudo -u postgres psql -c ""GRANT ALL PRIVILEGES ON DATABASE #arguments.databaseN var matches = reMatchNoCase("WHEELS_ENV\s*=\s*([^\r\n]+)", envContent); if (arrayLen(matches)) { currentEnv = trim(matches[1]); - // Remove quotes if present and remove key name "wheels_env=" - currentEnv = reReplace(currentEnv, "^([""']|wheels_env=)|([""'])$", "", "all"); + // Remove quotes if present and remove key name "WHEELS_ENV=" + currentEnv = reReplace(currentEnv, "^([""']|WHEELS_ENV=)|([""'])$", "", "all"); } } @@ -1770,7 +1766,9 @@ sudo -u postgres psql -c ""GRANT ALL PRIVILEGES ON DATABASE #arguments.databaseN required string port, required string username, required string password, - required string sid + required string sid, + string servicename = "", + string oracleConnectionType = "sid" ) { try { // Read existing environment file @@ -1785,6 +1783,8 @@ sudo -u postgres psql -c ""GRANT ALL PRIVILEGES ON DATABASE #arguments.databaseN DB_USER: false, DB_PASSWORD: false, DB_SID: false, + DB_SERVICENAME: false, + DB_ORACLE_CONNECTION_TYPE: false, DB_DATASOURCE: false }; var inDatabaseSection = false; @@ -1858,12 +1858,30 @@ sudo -u postgres psql -c ""GRANT ALL PRIVILEGES ON DATABASE #arguments.databaseN // Update DB_SID (Oracle only) if (findNoCase("DB_SID=", trimmedLine) == 1) { dbVarsFound.DB_SID = true; - if (lCase(arguments.dbtype) == "oracle" && len(trim(arguments.sid))) { + if (lCase(arguments.dbtype) == "oracle" && len(trim(arguments.sid)) && arguments.oracleConnectionType == "sid") { arrayAppend(updatedLines, "DB_SID=#arguments.sid#"); } continue; } + // Update DB_SERVICENAME (Oracle Service Name only) + if (findNoCase("DB_SERVICENAME=", trimmedLine) == 1) { + dbVarsFound.DB_SERVICENAME = true; + if (lCase(arguments.dbtype) == "oracle" && len(trim(arguments.servicename)) && arguments.oracleConnectionType == "servicename") { + arrayAppend(updatedLines, "DB_SERVICENAME=#arguments.servicename#"); + } + continue; + } + + // Update DB_ORACLE_CONNECTION_TYPE (Oracle only) + if (findNoCase("DB_ORACLE_CONNECTION_TYPE=", trimmedLine) == 1) { + dbVarsFound.DB_ORACLE_CONNECTION_TYPE = true; + if (lCase(arguments.dbtype) == "oracle" && len(trim(arguments.oracleConnectionType))) { + arrayAppend(updatedLines, "DB_ORACLE_CONNECTION_TYPE=#arguments.oracleConnectionType#"); + } + continue; + } + // Update DB_DATASOURCE if (findNoCase("DB_DATASOURCE=", trimmedLine) == 1) { dbVarsFound.DB_DATASOURCE = true; @@ -1904,10 +1922,18 @@ sudo -u postgres psql -c ""GRANT ALL PRIVILEGES ON DATABASE #arguments.databaseN arrayAppend(missingVars, "DB_PASSWORD=#arguments.password#"); } - if (!dbVarsFound.DB_SID && lCase(arguments.dbtype) == "oracle" && len(trim(arguments.sid))) { + if (!dbVarsFound.DB_SID && lCase(arguments.dbtype) == "oracle" && len(trim(arguments.sid)) && arguments.oracleConnectionType == "sid") { arrayAppend(missingVars, "DB_SID=#arguments.sid#"); } + if (!dbVarsFound.DB_SERVICENAME && lCase(arguments.dbtype) == "oracle" && len(trim(arguments.servicename)) && arguments.oracleConnectionType == "servicename") { + arrayAppend(missingVars, "DB_SERVICENAME=#arguments.servicename#"); + } + + if (!dbVarsFound.DB_ORACLE_CONNECTION_TYPE && lCase(arguments.dbtype) == "oracle" && len(trim(arguments.oracleConnectionType))) { + arrayAppend(missingVars, "DB_ORACLE_CONNECTION_TYPE=#arguments.oracleConnectionType#"); + } + if (!dbVarsFound.DB_DATASOURCE) { var dsName = len(trim(arguments.datasource)) ? arguments.datasource : "wheels_#arguments.environment#"; arrayAppend(missingVars, "DB_DATASOURCE=#dsName#"); diff --git a/cli/src/models/PluginService.cfc b/cli/src/models/PluginService.cfc index adf97033cf..01574864fb 100644 --- a/cli/src/models/PluginService.cfc +++ b/cli/src/models/PluginService.cfc @@ -263,7 +263,7 @@ component { */ private function isWheelsPlugin(moduleName) { // Exclude the core framework and common non-plugin dependencies - if (listFindNoCase("wheels-core,wirebox,testbox,cfformat", arguments.moduleName)) { + if (listFindNoCase("wheels-core,testbox,cfformat", arguments.moduleName)) { return false; } diff --git a/cli/src/models/ScaffoldService.cfc b/cli/src/models/ScaffoldService.cfc index a20669d64e..c4f45c3ef1 100644 --- a/cli/src/models/ScaffoldService.cfc +++ b/cli/src/models/ScaffoldService.cfc @@ -212,7 +212,8 @@ component { required string name, required array properties, string baseDirectory = "", - string tableName = "" + string tableName = "", + string primaryKey = "id" ) { var timestamp = dateFormat(now(), "yyyymmdd") & timeFormat(now(), "HHmmss"); var actualTableName = len(arguments.tableName) ? arguments.tableName : variables.helpers.pluralize(lCase(arguments.name)); @@ -231,7 +232,8 @@ component { var content = generateMigrationContentWithProperties( className = className, tableName = actualTableName, - properties = arguments.properties + properties = arguments.properties, + primaryKey = arguments.primaryKey ); // Write migration file @@ -246,7 +248,8 @@ component { private function generateMigrationContentWithProperties( required string className, required string tableName, - required array properties + required array properties, + string primaryKey = "id" ) { var content = '/*' & chr(10); content &= ' |----------------------------------------------------------------------------------------------|' & chr(10); @@ -270,7 +273,7 @@ component { content &= ' function up() {' & chr(10); content &= ' transaction {' & chr(10); content &= ' try {' & chr(10); - content &= ' t = createTable(name = ''#arguments.tableName#'', force=''false'', id=''true'', primaryKey=''id'');' & chr(10); + content &= ' t = createTable(name = ''#arguments.tableName#'', force=''false'', id=''true'', primaryKey=''#arguments.primaryKey#'');' & chr(10); // Add properties (skip association properties that don't have database columns) for (var prop in arguments.properties) { diff --git a/cli/src/models/TemplateService.cfc b/cli/src/models/TemplateService.cfc index 08c2970826..851cb683c2 100644 --- a/cli/src/models/TemplateService.cfc +++ b/cli/src/models/TemplateService.cfc @@ -164,6 +164,18 @@ component { processed = replace(processed, "|FormFields|", "", "all"); } + // Process admin form fields if adminFields are provided (from AdminIntrospectionService) + if (structKeyExists(arguments.context, "adminFields") && isArray(arguments.context.adminFields) && arrayLen(arguments.context.adminFields)) { + var adminFormCode = generateAdminFormFieldsCode(arguments.context.adminFields); + processed = replace(processed, "|AdminFormFields|", adminFormCode, "all"); + + var adminSelectParams = generateAdminSelectParams(arguments.context.adminFields); + processed = replace(processed, "|AdminSelectParams|", adminSelectParams, "all"); + } else { + processed = replace(processed, "|AdminFormFields|", "", "all"); + processed = replace(processed, "|AdminSelectParams|", "", "all"); + } + // Process controller includes for associations if (structKeyExists(arguments.context, "belongsTo") && len(arguments.context.belongsTo)) { processed = addControllerIncludes(processed, arguments.context.belongsTo); @@ -555,4 +567,155 @@ component { return processed; } + + /** + * Generate admin form fields code from AdminIntrospectionService field metadata. + * Produces Wheels form helpers with proper HTML5 input types, wrapped in form-group divs. + * + * @fields Array of field metadata structs from AdminIntrospectionService.introspect().fields + */ + public function generateAdminFormFieldsCode(required array fields) { + var fieldBlocks = []; + var t = chr(9); + + for (var field in arguments.fields) { + // Skip fields not meant for forms (primary keys) + if (structKeyExists(field, "inForm") && !field.inForm) { + continue; + } + + var inputType = structKeyExists(field, "inputType") ? field.inputType : "text"; + var propName = field.name; + var label = structKeyExists(field, "label") ? field.label : propName; + var fieldCode = ""; + + switch (inputType) { + case "select": + fieldCode = generateAdminSelectField(field, label); + break; + case "textarea": + fieldCode = '
' & chr(10); + fieldCode &= t & '##textArea(objectName="|ObjectNameSingular|", property="#propName#", label="#label#", class="form-control", rows="5")##' & chr(10); + fieldCode &= '
'; + break; + case "checkbox": + fieldCode = '
' & chr(10); + fieldCode &= t & '
' & chr(10); + fieldCode &= t & t & '##checkBox(objectName="|ObjectNameSingular|", property="#propName#", label="#label#")##' & chr(10); + fieldCode &= t & '
' & chr(10); + fieldCode &= '
'; + break; + case "datetime-local": + fieldCode = '
' & chr(10); + fieldCode &= t & '##dateTimeSelect(objectName="|ObjectNameSingular|", property="#propName#", label="#label#")##' & chr(10); + fieldCode &= '
'; + break; + case "time": + fieldCode = '
' & chr(10); + fieldCode &= t & '##timeSelect(objectName="|ObjectNameSingular|", property="#propName#", label="#label#")##' & chr(10); + fieldCode &= '
'; + break; + case "file": + fieldCode = '
' & chr(10); + fieldCode &= t & '##fileFieldTag(name="|ObjectNameSingular|[#propName#]", label="#label#", class="form-control")##' & chr(10); + fieldCode &= '
'; + break; + default: + // HTML5 input types: text, email, password, url, tel, color, search, number, date + var helperName = mapInputTypeToHelper(inputType); + fieldCode = '
' & chr(10); + fieldCode &= t & '###helperName#(objectName="|ObjectNameSingular|", property="#propName#", label="#label#", class="form-control")##' & chr(10); + fieldCode &= '
'; + } + + arrayAppend(fieldBlocks, fieldCode); + } + + return arrayToList(fieldBlocks, chr(10)); + } + + /** + * Generate a select field for foreign keys or enums. + */ + private function generateAdminSelectField(required struct field, required string label) { + var t = chr(9); + var propName = arguments.field.name; + var fieldCode = '
' & chr(10); + + if (structKeyExists(arguments.field, "isForeignKey") && arguments.field.isForeignKey) { + // Foreign key โ€” render select with options from associated model + var assocModel = arguments.field.foreignKeyTo; + var optionsVar = lCase(assocModel) & "Options"; + fieldCode &= t & '##select(objectName="|ObjectNameSingular|", property="#propName#", options=#optionsVar#, label="#arguments.label#", includeBlank="Select #assocModel#", class="form-control")##' & chr(10); + } else if (structKeyExists(arguments.field, "isEnum") && arguments.field.isEnum) { + // Enum โ€” render select with enum values as options list + var enumValues = arguments.field.enumValues; + var optionsList = ""; + if (isStruct(enumValues)) { + optionsList = structKeyList(enumValues); + } else if (isSimpleValue(enumValues)) { + optionsList = enumValues; + } + fieldCode &= t & '##select(objectName="|ObjectNameSingular|", property="#propName#", options="#optionsList#", label="#arguments.label#", includeBlank="Select #arguments.label#", class="form-control")##' & chr(10); + } else { + // Generic select fallback + fieldCode &= t & '##select(objectName="|ObjectNameSingular|", property="#propName#", options="", label="#arguments.label#", class="form-control")##' & chr(10); + } + + fieldCode &= '
'; + return fieldCode; + } + + /** + * Map an HTML5 input type to the corresponding Wheels form helper function name. + */ + private function mapInputTypeToHelper(required string inputType) { + switch (arguments.inputType) { + case "email": + return "emailField"; + case "password": + return "passwordField"; + case "url": + return "urlField"; + case "tel": + return "telField"; + case "color": + return "colorField"; + case "search": + return "searchField"; + case "number": + return "numberField"; + case "date": + return "dateField"; + default: + return "textField"; + } + } + + /** + * Generate cfparam declarations for select option variables needed by admin forms. + * Foreign key fields need option query objects; enum fields use inline values. + * + * @fields Array of field metadata structs from AdminIntrospectionService + */ + public function generateAdminSelectParams(required array fields) { + var params = []; + + for (var field in arguments.fields) { + if (structKeyExists(field, "inForm") && !field.inForm) { + continue; + } + + var inputType = structKeyExists(field, "inputType") ? field.inputType : "text"; + if (inputType != "select") continue; + + if (structKeyExists(field, "isForeignKey") && field.isForeignKey) { + var assocModel = field.foreignKeyTo; + var optionsVar = lCase(assocModel) & "Options"; + arrayAppend(params, ''); + } + } + + return arrayToList(params, chr(10)); + } } \ No newline at end of file diff --git a/cli/src/models/TestMigrationService.cfc b/cli/src/models/TestMigrationService.cfc index 9b83e16131..e3caa18b58 100644 --- a/cli/src/models/TestMigrationService.cfc +++ b/cli/src/models/TestMigrationService.cfc @@ -37,7 +37,7 @@ component singleton { } // Check if it's a CFC file - if (!listLast(arguments.filePath, ".") == "cfc") { + if (listLast(arguments.filePath, ".") != "cfc") { return { success = false, skipped = true, @@ -59,12 +59,13 @@ component singleton { var result = { content = content, changes = [], - warnings = [] + warnings = [], + fileName = getFileFromPath(arguments.filePath) }; - + // Convert component extends result = convertComponentExtends(result); - + // Convert test methods to BDD format result = convertTestMethods(result); @@ -146,7 +147,7 @@ component singleton { try { // Skip already migrated files var fileContent = fileRead(file); - if (findNoCase('extends="tests.BaseSpec"', fileContent) || findNoCase("extends='tests.BaseSpec'", fileContent)) { + if (findNoCase('extends="wheels.WheelsTest"', fileContent) || findNoCase("extends='wheels.WheelsTest'", fileContent)) { arrayAppend(results, { success = true, filePath = file, @@ -184,15 +185,19 @@ component singleton { var patterns = [ { pattern = 'extends\s*=\s*"tests\.Test"', - replacement = 'extends="tests.BaseSpec"' + replacement = 'extends="wheels.WheelsTest"' }, { pattern = "extends\s*=\s*'tests\.Test'", - replacement = 'extends="tests.BaseSpec"' + replacement = 'extends="wheels.WheelsTest"' }, { pattern = 'extends\s*=\s*"wheels\.Test"', - replacement = 'extends="tests.BaseSpec"' + replacement = 'extends="wheels.WheelsTest"' + }, + { + pattern = 'extends\s*=\s*"app\.tests\.Test"', + replacement = 'extends="wheels.WheelsTest"' } ]; @@ -204,7 +209,7 @@ component singleton { p.replacement, "all" ); - arrayAppend(arguments.result.changes, "Updated component extends to TestBox BaseSpec"); + arrayAppend(arguments.result.changes, "Updated component extends to wheels.WheelsTest"); } } @@ -228,7 +233,11 @@ component singleton { if (componentMatch.pos[1] > 0) { var insertPos = componentMatch.pos[1] + componentMatch.len[1]; var indent = chr(10) & chr(9); - var runWrapper = indent & "function run() {" & indent & chr(9) & 'describe("Test Suite", () => {' & indent & indent; + // Use file name for describe label (strip .cfc and Test/Spec suffix) + var describeName = structKeyExists(arguments.result, "fileName") + ? reReplaceNoCase(reReplaceNoCase(arguments.result.fileName, "\.(cfc|cfm)$", ""), "(Test|Spec)$", "") + : "Test Suite"; + var runWrapper = indent & "function run() {" & indent & chr(9) & 'describe("#describeName#", () => {' & indent & indent; // Insert run wrapper arguments.result.content = insert(runWrapper, arguments.result.content, insertPos); @@ -272,26 +281,29 @@ component singleton { /** * Convert test method name to readable description + * Handles both test_ (underscore) and testCamelCase patterns: + * test_create_validation_passes โ†’ "create validation passes" + * Test_Auth_has_Default_Properties โ†’ "Auth has Default Properties" + * testUserCanLogin โ†’ "user can login" */ private function convertTestNameToDescription(required string methodName) { var description = arguments.methodName; - - // Remove "test" prefix - description = reReplaceNoCase(description, "^test", "", "one"); - - // Convert camelCase to spaces - description = reReplace(description, "([A-Z])", " \1", "all"); - - // Clean up - description = trim(lCase(description)); - - // Handle common patterns - description = replaceNoCase(description, "should ", "should ", "all"); - - if (left(description, 6) != "should") { - description = "should " & description; + + // Remove "test" or "test_" prefix (case-insensitive) + description = reReplaceNoCase(description, "^test_?", "", "one"); + + // Replace underscores with spaces + description = replace(description, "_", " ", "all"); + + // Convert camelCase to spaces (only if no underscores were present) + if (!findNoCase("_", arguments.methodName)) { + description = reReplace(description, "([a-z])([A-Z])", "\1 \2", "all"); } - + + // Clean up multiple spaces + description = reReplace(description, "\s+", " ", "all"); + description = trim(description); + return description; } @@ -370,27 +382,42 @@ component singleton { /** * Convert lifecycle methods + * Replaces function signature and removes super.setup()/super.teardown() calls */ private function convertLifecycleMethods(required struct result) { var conversions = [ - {from = "function setup()", to = "beforeEach(() => {"}, - {from = "function teardown()", to = "afterEach(() => {"}, - {from = "function beforeTests()", to = "beforeAll(() => {"}, - {from = "function afterTests()", to = "afterAll(() => {"} + {pattern = "function\s+setup\s*\(\s*\)", replacement = "beforeEach(() =>", label = "setup -> beforeEach"}, + {pattern = "function\s+teardown\s*\(\s*\)", replacement = "afterEach(() =>", label = "teardown -> afterEach"}, + {pattern = "function\s+beforeTests\s*\(\s*\)", replacement = "beforeAll(() =>", label = "beforeTests -> beforeAll"}, + {pattern = "function\s+afterTests\s*\(\s*\)", replacement = "afterAll(() =>", label = "afterTests -> afterAll"} ]; - + for (var conversion in conversions) { - if (findNoCase(conversion.from, arguments.result.content)) { - arguments.result.content = replaceNoCase( + if (reFindNoCase(conversion.pattern, arguments.result.content)) { + arguments.result.content = reReplaceNoCase( arguments.result.content, - conversion.from, - conversion.to, + conversion.pattern, + conversion.replacement, "all" ); - arrayAppend(arguments.result.changes, "Converted lifecycle method: #conversion.from#"); + arrayAppend(arguments.result.changes, "Converted lifecycle: #conversion.label#"); } } - + + // Remove super.setup() and super.teardown() calls (not needed in TestBox) + arguments.result.content = reReplaceNoCase( + arguments.result.content, + "\s*super\.setup\(\)\s*;?\s*", + chr(10), + "all" + ); + arguments.result.content = reReplaceNoCase( + arguments.result.content, + "\s*super\.teardown\(\)\s*;?\s*", + chr(10), + "all" + ); + return arguments.result; } @@ -398,8 +425,8 @@ component singleton { * Add required imports if missing */ private function addRequiredImports(required struct result) { - // Check if BaseSpec import is needed and not present - if (!findNoCase("tests.BaseSpec", arguments.result.content) && + // Check if WheelsTest import is needed and not present + if (!findNoCase("wheels.WheelsTest", arguments.result.content) && findNoCase("extends=", arguments.result.content)) { // Add helpful comment at the top var comment = "/**" & chr(10); diff --git a/cli/src/models/TestService.cfc b/cli/src/models/TestService.cfc index 7b8c17553f..752790972c 100644 --- a/cli/src/models/TestService.cfc +++ b/cli/src/models/TestService.cfc @@ -135,7 +135,7 @@ component { boolean crud = false, boolean mock = false ) { - var content = 'component extends="wheels.Testbox" {' & chr(10) & chr(10); + var content = 'component extends="wheels.WheelsTest" {' & chr(10) & chr(10); // Add any required properties for mocking if (arguments.mock) { diff --git a/cli/src/recipes/config-migration.boxr b/cli/src/recipes/config-migration.boxr index e470efd4d1..c602eee165 100644 --- a/cli/src/recipes/config-migration.boxr +++ b/cli/src/recipes/config-migration.boxr @@ -1,6 +1,6 @@ #!/usr/bin/env cfscript /** - * CFWheels 3.0.0 Config Migration Recipe + * CFWheels 3.1.0 Config Migration Recipe * * This recipe migrates your CFWheels application from the old /app/config * structure to the new /config structure at the root level. @@ -9,7 +9,7 @@ */ // Display welcome message -print.greenBoldLine("CFWheels 3.0.0 Config Migration Tool") +print.greenBoldLine("CFWheels 3.1.0 Config Migration Tool") .line("====================================") .line() .yellowLine("This recipe will migrate your config directory from /app/config to /config") @@ -168,7 +168,7 @@ if (arrayLen(foundReferences)) { // Step 5: Create migration documentation var migrationLog = []; -arrayAppend(migrationLog, "CFWheels 3.0.0 Config Migration Log"); +arrayAppend(migrationLog, "CFWheels 3.1.0 Config Migration Log"); arrayAppend(migrationLog, "==================================="); arrayAppend(migrationLog, "Migration Date: #dateTimeFormat(now(), 'yyyy-mm-dd HH:nn:ss')#"); arrayAppend(migrationLog, ""); diff --git a/cli/src/templates/BoxJSON.txt b/cli/src/templates/BoxJSON.txt index 83a32bdf19..597b232073 100644 --- a/cli/src/templates/BoxJSON.txt +++ b/cli/src/templates/BoxJSON.txt @@ -4,7 +4,7 @@ "author":"Wheels Core Team and Community, repackaged by Peter Amiri", "shortDescription":"Wheels MVC Framework Base Template", "location":"", - "slug":"myapp", + "slug":"|appName|", "createPackageDirectory":false, "type":"wheels-templates", "keywords":[ diff --git a/cli/src/templates/HelperContent.txt b/cli/src/templates/HelperContent.txt new file mode 100644 index 0000000000..44a81bab8e --- /dev/null +++ b/cli/src/templates/HelperContent.txt @@ -0,0 +1,4 @@ +|DescriptionComment|component output="false" { + +|HelperFunctions| +} \ No newline at end of file diff --git a/cli/src/templates/ServerJSON.txt b/cli/src/templates/ServerJSON.txt index 2818423d36..5e33289071 100644 --- a/cli/src/templates/ServerJSON.txt +++ b/cli/src/templates/ServerJSON.txt @@ -1,14 +1,16 @@ { "name": "|appName|", - "app": { - "cfengine": "|setEngine|", - "serverHomeDirectory": ".wheels/server" - }, "web": { + "host":"localhost", "webroot": "public", "rewrites": { "enable": true, "config": "public/urlrewrite.xml" } + }, + "app": { + "cfengine": "|setEngine|", + "serverHomeDirectory": ".wheels/server", + "libDirs":"app/lib" } } \ No newline at end of file diff --git a/cli/src/templates/ViteConfig.txt b/cli/src/templates/ViteConfig.txt new file mode 100644 index 0000000000..323a1d283f --- /dev/null +++ b/cli/src/templates/ViteConfig.txt @@ -0,0 +1,73 @@ +import { defineConfig } from 'vite'; + +/** + * Wheels + Vite Configuration + * + * Development: Run `npx vite` alongside CommandBox (`server start`). + * The Vite dev server handles JS/CSS with Hot Module Replacement (HMR), + * while CommandBox serves your CFML pages. + * + * Production: Run `npx vite build` to compile assets into public/build/ + * with fingerprinted filenames and a manifest for cache busting. + * + * In your layout, use the Wheels view helpers: + * #viteScriptTag("src/main.js")# โ€” JS entry (includes HMR client in dev) + * #viteStyleTag("src/main.css")# โ€” CSS entry (no-op in dev, Vite injects via JS) + */ +export default defineConfig({ + build: { + // Output to public/build/ โ€” matches Wheels viteBuildPath setting + outDir: 'public/build', + + // Generate .vite/manifest.json โ€” matches Wheels viteManifestFile setting + manifest: true, + + // JS/CSS/image assets go into assets/ subfolder + assetsDir: 'assets', + + // Define your application entry point(s) + rollupOptions: { + input: 'src/main.js', + }, + + // Empty the output directory before each build + emptyOutDir: true, + }, + + server: { + // Vite dev server port โ€” matches Wheels viteDevServerUrl setting + port: |vitePort|, + strictPort: true, + + // Allow cross-origin requests from CommandBox server + cors: true, + + // Set origin so asset URLs resolve correctly when loaded from CommandBox pages + origin: 'http://localhost:|vitePort|', + + // Proxy configuration for single-URL development workflow. + // + // With this proxy enabled, you can open http://localhost:|vitePort| and get + // both Vite-served assets (with HMR) and CFML pages from CommandBox. + // + // Without the proxy, use http://localhost:|serverPort| (CommandBox serves pages, + // assets load from Vite via the viteScriptTag() helper). + proxy: { + '/': { + target: 'http://localhost:|serverPort|', + changeOrigin: true, + // Let Vite handle its own internal paths and source files + bypass(req) { + if ( + req.url.startsWith('/src/') || + req.url.startsWith('/node_modules/') || + req.url.startsWith('/@') || + req.url.startsWith('/__vite') + ) { + return req.url; + } + }, + }, + }, + }, +}); diff --git a/cli/src/templates/ViteMainCSS.txt b/cli/src/templates/ViteMainCSS.txt new file mode 100644 index 0000000000..9b713b0722 --- /dev/null +++ b/cli/src/templates/ViteMainCSS.txt @@ -0,0 +1,24 @@ +/** + * Wheels Application Styles + * + * This is your main CSS file. Vite supports CSS nesting, @import, and + * PostCSS out of the box. Add preprocessor support (Sass, Less) by + * installing the corresponding package (e.g., `npm install -D sass`). + * + * In production, Vite extracts CSS into a separate fingerprinted file. + * In development, styles are injected via JavaScript for instant HMR. + */ + +:root { + --color-primary: #1a73e8; + --color-text: #333; + --font-family: system-ui, -apple-system, sans-serif; +} + +body { + font-family: var(--font-family); + color: var(--color-text); + line-height: 1.6; + margin: 0; + padding: 0; +} diff --git a/cli/src/templates/ViteMainJS.txt b/cli/src/templates/ViteMainJS.txt new file mode 100644 index 0000000000..6bb0f2b695 --- /dev/null +++ b/cli/src/templates/ViteMainJS.txt @@ -0,0 +1,14 @@ +/** + * Wheels Application Entry Point + * + * This is your main JavaScript file. Import CSS and other modules here. + * Vite will handle bundling, tree-shaking, and code splitting automatically. + * + * In your Wheels layout (app/views/layout.cfm), include this entry point: + * #viteScriptTag("src/main.js")# + */ + +// Import your styles โ€” Vite injects them via HMR in development +import './main.css'; + +console.log('Wheels + Vite is ready!'); diff --git a/cli/src/templates/VitePackageJSON.txt b/cli/src/templates/VitePackageJSON.txt new file mode 100644 index 0000000000..ae6d1c03d4 --- /dev/null +++ b/cli/src/templates/VitePackageJSON.txt @@ -0,0 +1,12 @@ +{ + "private": true, + "type": "module", + "scripts": { + "dev": "vite", + "build": "vite build", + "preview": "vite preview" + }, + "devDependencies": { + "vite": "^6.0.0" + } +} diff --git a/cli/src/templates/admin/AdminControllerContent.txt b/cli/src/templates/admin/AdminControllerContent.txt new file mode 100644 index 0000000000..fe0a05a51a --- /dev/null +++ b/cli/src/templates/admin/AdminControllerContent.txt @@ -0,0 +1,106 @@ +|DescriptionComment|component extends="app.controllers.Controller" { + + function config() { + super.config(); + + // CSRF protection โ€” verifies authenticity token on state-changing requests + protectsFromForgery(); + + // Verify key param exists for member actions + verifies(except="index,new,create", params="key", paramsTypes="integer", handler="objectNotFound"); + } + + /** + * List all |ObjectNamePluralC| + **/ + function index() { + param name="params.q" default=""; + param name="params.sort" default=""; + param name="params.direction" default="asc"; + param name="params.page" type="numeric" default=1; + + var where = []; + if (len(params.q)) { + var sanitizedQ = ReplaceList(params.q, "%,_,[", "\%,\_,\["); + where.append("|SearchWhereClause|"); + } + + // Sanitize sort parameters to prevent injection + var validDirection = listFindNoCase("asc,desc", params.direction) ? params.direction : "asc"; + var sortOrder = len(params.sort) && reFindNoCase("^[a-zA-Z_][a-zA-Z0-9_]*$", params.sort) + ? "#params.sort# #validDirection#" + : ""; + + |ObjectNamePlural| = model("|ObjectNameSingularC|").findAll( + where = arrayToList(where, " AND "), + order = sortOrder, + page = params.page, + perPage = 25 + ); + } + + /** + * View |ObjectNameSingularC| detail + **/ + function show() { + |ObjectNameSingular| = model("|ObjectNameSingularC|").findByKey(params.key); + } + + /** + * New |ObjectNameSingularC| form + **/ + function new() { + |ObjectNameSingular| = model("|ObjectNameSingularC|").new(); + |ForeignKeyLoaders| + } + + /** + * Create |ObjectNameSingularC| + **/ + function create() { + |ObjectNameSingular| = model("|ObjectNameSingularC|").create(params.|ObjectNameSingular|); + if (|ObjectNameSingular|.hasErrors()) { + |ForeignKeyLoaders| + renderView(action="new"); + } else { + redirectTo(action="index", success="|ObjectNameSingularC| successfully created"); + } + } + + /** + * Edit |ObjectNameSingularC| form + **/ + function edit() { + |ObjectNameSingular| = model("|ObjectNameSingularC|").findByKey(params.key); + |ForeignKeyLoaders| + } + + /** + * Update |ObjectNameSingularC| + **/ + function update() { + |ObjectNameSingular| = model("|ObjectNameSingularC|").findByKey(params.key); + if (|ObjectNameSingular|.update(params.|ObjectNameSingular|)) { + redirectTo(action="index", success="|ObjectNameSingularC| successfully updated"); + } else { + |ForeignKeyLoaders| + renderView(action="edit"); + } + } + + /** + * Delete |ObjectNameSingularC| + **/ + function delete() { + |ObjectNameSingular| = model("|ObjectNameSingularC|").deleteByKey(params.key); + redirectTo(action="index", success="|ObjectNameSingularC| successfully deleted"); + } + + /** + * Redirect away if verifies fails or object can't be found + **/ + private function objectNotFound() { + redirectTo(action="index", error="That |ObjectNameSingularC| wasn't found"); + } + +} diff --git a/cli/src/templates/admin/_form.txt b/cli/src/templates/admin/_form.txt new file mode 100644 index 0000000000..6367a00760 --- /dev/null +++ b/cli/src/templates/admin/_form.txt @@ -0,0 +1,5 @@ + + +|AdminFormFields| + + \ No newline at end of file diff --git a/cli/src/templates/admin/edit.txt b/cli/src/templates/admin/edit.txt new file mode 100644 index 0000000000..93131db2e5 --- /dev/null +++ b/cli/src/templates/admin/edit.txt @@ -0,0 +1,14 @@ + + +|AdminSelectParams| + +

Edit |ObjectNameSingularC|

+#errorMessagesFor("|ObjectNameSingular|")# +#startFormTag(id="|ObjectNameSingular|EditForm", route="|ObjectNameSingularC|", method="patch", key=params.key)# + #includePartial("form")# +
+ #submitTag(value="Update |ObjectNameSingularC|", class="btn btn-primary")# + #linkTo(route="|ObjectNameSingularC|", key=params.key, text="Cancel", class="btn btn-default")# +
+#endFormTag()# +
\ No newline at end of file diff --git a/cli/src/templates/admin/index.txt b/cli/src/templates/admin/index.txt new file mode 100644 index 0000000000..791758510e --- /dev/null +++ b/cli/src/templates/admin/index.txt @@ -0,0 +1,82 @@ + + + + + + +/** + * Sort link helper for admin table headers. + * Toggles direction when clicking the currently sorted column. + */ +string function _sortLink(required string col, required string label) { + var dir = "asc"; + if (params.sort == col && params.direction == "asc") dir = "desc"; + var qs = "sort=" & col & "&direction=" & dir; + if (len(params.q)) qs &= "&q=" & encodeForURL(params.q); + var icon = ""; + if (params.sort == col) icon = (params.direction == "asc") ? " ▲" : " ▼"; + return '' & label & icon & ''; +} + + +
+ +
+

|ObjectNamePluralC|

+
+ #linkTo(route="new|ObjectNameSingularC|", text="New |ObjectNameSingularC|", class="btn btn-primary")# +
+
+ +
+
+ #startFormTag(route="|ObjectNamePluralC|", method="get", class="row g-3")# +
+ #textFieldTag(name="q", value=params.q, placeholder="Search |ObjectNamePluralC|...", class="form-control")# +
+
+ #submitTag(value="Search", class="btn btn-outline-secondary w-100")# +
+ #endFormTag()# +
+
+ + +
+
+ + + + {{AdminTableHeaders}} + + + + + + + {{AdminTableCells}} + + + + +
Actions
+
+ #linkTo(route="|ObjectNameSingularC|", key=id, text="View", class="btn btn-outline-info")# + #linkTo(route="edit|ObjectNameSingularC|", key=id, text="Edit", class="btn btn-outline-primary")# + #buttonTo(route="|ObjectNameSingularC|", method="delete", key=id, text="Delete", confirm="Are you sure?", inputClass="btn btn-outline-danger btn-sm")# +
+
+
+ +
+ +
+ No |ObjectNamePluralC| found. + #linkTo(route="new|ObjectNameSingularC|", text="Create the first one")# +
+
+ +
+
diff --git a/cli/src/templates/admin/new.txt b/cli/src/templates/admin/new.txt new file mode 100644 index 0000000000..e4ad2a9010 --- /dev/null +++ b/cli/src/templates/admin/new.txt @@ -0,0 +1,14 @@ + + +|AdminSelectParams| + +

Create |ObjectNameSingularC|

+#errorMessagesFor("|ObjectNameSingular|")# +#startFormTag(id="|ObjectNameSingular|NewForm", action="create")# + #includePartial("form")# +
+ #submitTag(value="Create |ObjectNameSingularC|", class="btn btn-primary")# + #linkTo(route="|ObjectNamePluralC|", text="Cancel", class="btn btn-default")# +
+#endFormTag()# +
\ No newline at end of file diff --git a/cli/src/templates/admin/show.txt b/cli/src/templates/admin/show.txt new file mode 100644 index 0000000000..3bdb1ee114 --- /dev/null +++ b/cli/src/templates/admin/show.txt @@ -0,0 +1,34 @@ + + + +
+ +
+

|ObjectNameSingularC| Detail

+
+ #linkTo(route="|ObjectNamePluralC|", text="Back to List", class="btn btn-outline-secondary")# + #linkTo(route="edit|ObjectNameSingularC|", key=|ObjectNameSingular|.key(), text="Edit", class="btn btn-primary")# +
+
+ +
+
+
|ObjectNameSingularC| Information
+
+
+ + + {{AdminShowFields}} + +
+
+
+ + {{AdminAssociationSections}} + +
+ #buttonTo(route="|ObjectNameSingularC|", method="delete", key=|ObjectNameSingular|.key(), text="Delete |ObjectNameSingularC|", confirm="Are you sure you want to delete this |ObjectNameSingularC|?", inputClass="btn btn-outline-danger")# +
+ +
+
diff --git a/cli/src/templates/tests/controller.txt b/cli/src/templates/tests/controller.txt index c06638c9cf..87eeaf5ece 100644 --- a/cli/src/templates/tests/controller.txt +++ b/cli/src/templates/tests/controller.txt @@ -1,4 +1,4 @@ -component extends="wheels.Testbox" { +component extends="wheels.WheelsTest" { function run() { diff --git a/cli/src/templates/tests/helper.txt b/cli/src/templates/tests/helper.txt new file mode 100644 index 0000000000..c3c7b4c3b6 --- /dev/null +++ b/cli/src/templates/tests/helper.txt @@ -0,0 +1,17 @@ +component extends="wheels.WheelsTest" { + + function run() { + + describe("{{testName}}", () => { + + beforeEach(() => { + helper = new app.helpers.{{targetName}}(); + }) + + it("should be instantiable", () => { + expect(helper).toBeInstanceOf("app.helpers.{{targetName}}"); + }) + + }) + } +} \ No newline at end of file diff --git a/cli/src/templates/tests/model.txt b/cli/src/templates/tests/model.txt index 2158409776..1727c7aa84 100644 --- a/cli/src/templates/tests/model.txt +++ b/cli/src/templates/tests/model.txt @@ -1,4 +1,4 @@ -component extends="wheels.Testbox" { +component extends="wheels.WheelsTest" { function run() { diff --git a/cli/src/templates/tests/view.txt b/cli/src/templates/tests/view.txt index 56eb0f3b96..a787168393 100644 --- a/cli/src/templates/tests/view.txt +++ b/cli/src/templates/tests/view.txt @@ -1,4 +1,4 @@ -component extends="wheels.Testbox" { +component extends="wheels.WheelsTest" { function run() { diff --git a/cli/tests/specs/commands/AdminGeneratorTest.cfc b/cli/tests/specs/commands/AdminGeneratorTest.cfc new file mode 100644 index 0000000000..521a498102 --- /dev/null +++ b/cli/tests/specs/commands/AdminGeneratorTest.cfc @@ -0,0 +1,277 @@ +/** + * Tests for the admin generator: controller template processing and route injection. + * + * Tests the AdminControllerContent.txt template placeholder replacement and + * the route injection logic for admin-scoped resources. + */ +component extends="testbox.system.BaseSpec" { + + function beforeAll() { + // Resolve paths + var thisDir = getDirectoryFromPath(getCurrentTemplatePath()); + var File = createObject("java", "java.io.File"); + variables.cliRoot = File.init(thisDir & "../../../").getCanonicalPath(); + variables.templateDir = variables.cliRoot & "/src/templates"; + variables.templatePath = variables.templateDir & "/admin/AdminControllerContent.txt"; + + // Create a temp directory for route injection tests + variables.tempDir = getTempDirectory() & "admin_gen_test_" & createUUID() & "/"; + directoryCreate(variables.tempDir, true); + directoryCreate(variables.tempDir & "config/", true); + } + + function afterAll() { + if (directoryExists(variables.tempDir)) { + directoryDelete(variables.tempDir, true); + } + } + + function run() { + + // โ”€โ”€ Controller template โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + + describe("Admin controller template", function() { + + it("exists at expected path", function() { + expect(fileExists(variables.templatePath)).toBeTrue(); + }); + + it("extends app.controllers.Controller", function() { + var content = fileRead(variables.templatePath); + expect(content).toInclude('extends="app.controllers.Controller"'); + }); + + it("calls super.config()", function() { + var content = fileRead(variables.templatePath); + expect(content).toInclude("super.config()"); + }); + + it("calls protectsFromForgery()", function() { + var content = fileRead(variables.templatePath); + expect(content).toInclude("protectsFromForgery()"); + }); + + it("includes verifies for member actions", function() { + var content = fileRead(variables.templatePath); + expect(content).toInclude('verifies(except="index,new,create"'); + expect(content).toInclude('params="key"'); + }); + + it("contains all CRUD actions", function() { + var content = fileRead(variables.templatePath); + var actions = ["index", "show", "new", "create", "edit", "update", "delete"]; + for (var action in actions) { + expect(content).toInclude('function #action#()'); + } + }); + + it("contains objectNotFound handler as private", function() { + var content = fileRead(variables.templatePath); + expect(content).toInclude("private function objectNotFound()"); + }); + + it("includes search sanitization", function() { + var content = fileRead(variables.templatePath); + expect(content).toInclude("sanitizedQ"); + expect(content).toInclude("ReplaceList"); + }); + + it("includes sort parameter validation", function() { + var content = fileRead(variables.templatePath); + expect(content).toInclude("validDirection"); + expect(content).toInclude("reFindNoCase"); + }); + + it("includes pagination support", function() { + var content = fileRead(variables.templatePath); + expect(content).toInclude("params.page"); + expect(content).toInclude("perPage"); + }); + + it("has SearchWhereClause placeholder", function() { + var content = fileRead(variables.templatePath); + expect(content).toInclude("|SearchWhereClause|"); + }); + + it("has ForeignKeyLoaders placeholder", function() { + var content = fileRead(variables.templatePath); + expect(content).toInclude("|ForeignKeyLoaders|"); + }); + + it("has ObjectName placeholders for model binding", function() { + var content = fileRead(variables.templatePath); + expect(content).toInclude("|ObjectNameSingular|"); + expect(content).toInclude("|ObjectNamePlural|"); + expect(content).toInclude("|ObjectNameSingularC|"); + expect(content).toInclude("|ObjectNamePluralC|"); + }); + }); + + // โ”€โ”€ Template placeholder replacement โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + + describe("Admin controller template replacement", function() { + + it("replaces object name placeholders correctly", function() { + var content = fileRead(variables.templatePath); + content = replace(content, "|ObjectNameSingular|", "product", "all"); + content = replace(content, "|ObjectNamePlural|", "products", "all"); + content = replace(content, "|ObjectNameSingularC|", "Product", "all"); + content = replace(content, "|ObjectNamePluralC|", "Products", "all"); + content = replace(content, "|SearchWhereClause|", "name LIKE '%##sanitizedQ##%'", "all"); + content = replace(content, "|ForeignKeyLoaders|", "", "all"); + content = replace(content, "|DescriptionComment|", "", "all"); + + expect(content).toInclude('model("Product").findAll('); + expect(content).toInclude('model("Product").findByKey('); + expect(content).toInclude('model("Product").new()'); + expect(content).toInclude("params.product"); + expect(content).toInclude("product.hasErrors()"); + expect(content).toInclude("product.update(params.product)"); + expect(content).toInclude("Products successfully created"); + }); + + it("injects search WHERE clause", function() { + var content = fileRead(variables.templatePath); + var searchClause = "firstName LIKE '%##sanitizedQ##%' OR email LIKE '%##sanitizedQ##%'"; + content = replace(content, "|SearchWhereClause|", searchClause, "all"); + + expect(content).toInclude("firstName LIKE"); + expect(content).toInclude("email LIKE"); + }); + + it("injects foreign key loaders", function() { + var content = fileRead(variables.templatePath); + var loaderCode = chr(9) & chr(9) & 'roles = model("Role").findAll(order="name");'; + content = replace(content, "|ForeignKeyLoaders|", loaderCode, "all"); + + expect(content).toInclude('model("Role").findAll('); + }); + }); + + // โ”€โ”€ Route injection โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + + describe("Admin route injection", function() { + + beforeEach(function() { + // Reset the routes file for each test + }); + + it("creates admin scope when none exists", function() { + var routesContent = '' & chr(10) + & chr(9) & 'mapper()' & chr(10) + & chr(9) & chr(9) & '// CLI-Appends-Here' & chr(10) + & chr(9) & chr(9) & '.root(to = "main##index", method = "get")' & chr(10) + & chr(9) & '.end();' & chr(10) + & ''; + var routesPath = variables.tempDir & "config/routes.cfm"; + fileWrite(routesPath, routesContent); + + var result = injectAdminRoute("products", routesPath); + + expect(result).toBeTrue(); + var updated = fileRead(routesPath); + expect(updated).toInclude('.scope(path="admin", package="admin")'); + expect(updated).toInclude('.resources("products")'); + expect(updated).toInclude(".end()"); + }); + + it("adds resource to existing admin scope", function() { + var routesContent = '' & chr(10) + & chr(9) & 'mapper()' & chr(10) + & chr(9) & chr(9) & '.scope(path="admin", package="admin")' & chr(10) + & chr(9) & chr(9) & chr(9) & '.resources("users")' & chr(10) + & chr(9) & chr(9) & '.end()' & chr(10) + & chr(9) & '.end();' & chr(10) + & ''; + var routesPath = variables.tempDir & "config/routes.cfm"; + fileWrite(routesPath, routesContent); + + var result = injectAdminRoute("products", routesPath); + + expect(result).toBeTrue(); + var updated = fileRead(routesPath); + expect(updated).toInclude('.resources("products")'); + expect(updated).toInclude('.resources("users")'); + }); + + it("skips injection when resource already exists", function() { + var routesContent = '' & chr(10) + & chr(9) & 'mapper()' & chr(10) + & chr(9) & chr(9) & '.scope(path="admin", package="admin")' & chr(10) + & chr(9) & chr(9) & chr(9) & '.resources("products")' & chr(10) + & chr(9) & chr(9) & '.end()' & chr(10) + & chr(9) & '.end();' & chr(10) + & ''; + var routesPath = variables.tempDir & "config/routes.cfm"; + fileWrite(routesPath, routesContent); + + var result = injectAdminRoute("products", routesPath); + + expect(result).toBeFalse(); + }); + }); + } + + /** + * Standalone route injection for testing โ€” mirrors admin.cfc injectAdminRoute logic. + */ + private boolean function injectAdminRoute( + required string resourceName, + required string routesPath + ) { + if (!fileExists(arguments.routesPath)) return false; + + var content = fileRead(arguments.routesPath); + var resourceRoute = '.resources("' & arguments.resourceName & '")'; + var nl = chr(10); + var tab = chr(9); + + // Check if this resource already exists in admin scope + if (findNoCase(resourceRoute, content) && findNoCase("admin", content)) { + return false; + } + + // Look for existing admin scope block + var adminScopePattern = '.scope(path="admin"'; + var adminScopeAltPattern = ".scope(path='admin'"; + + if (findNoCase(adminScopePattern, content) || findNoCase(adminScopeAltPattern, content)) { + var adminScopePos = findNoCase(adminScopePattern, content); + if (adminScopePos == 0) adminScopePos = findNoCase(adminScopeAltPattern, content); + + var endPos = findNoCase(".end()", content, adminScopePos); + if (endPos > 0) { + var beforeEnd = left(content, endPos - 1); + var afterEnd = mid(content, endPos, len(content) - endPos + 1); + content = beforeEnd & tab & tab & tab & resourceRoute & nl & tab & tab & afterEnd; + fileWrite(arguments.routesPath, content); + return true; + } + } + + // No existing admin scope โ€” create one before CLI-Appends-Here marker + var markerPattern = "// CLI-Appends-Here"; + var indent = ""; + + if (find(tab & tab & tab & markerPattern, content)) { + indent = tab & tab & tab; + } else if (find(tab & tab & markerPattern, content)) { + indent = tab & tab; + } else if (find(tab & markerPattern, content)) { + indent = tab; + } + + var fullMarker = indent & markerPattern; + if (find(fullMarker, content)) { + var adminBlock = indent & '.scope(path="admin", package="admin")' & nl; + adminBlock &= indent & tab & resourceRoute & nl; + adminBlock &= indent & ".end()" & nl; + content = replace(content, fullMarker, adminBlock & fullMarker, "all"); + fileWrite(arguments.routesPath, content); + return true; + } + + return false; + } + +} diff --git a/cli/tests/specs/e2e/AdminViewServiceTest.cfc b/cli/tests/specs/e2e/AdminViewServiceTest.cfc new file mode 100644 index 0000000000..e0b8a3e8df --- /dev/null +++ b/cli/tests/specs/e2e/AdminViewServiceTest.cfc @@ -0,0 +1,387 @@ +/** + * Tests for AdminViewService: admin index and show view generation. + * + * Verifies that AdminViewService generates correct admin view content + * from model introspection metadata, including: + * - Sortable column headers + * - Type-aware table cells (boolean, email, date, enum, FK) + * - Detail view field rows + * - Association sections + * - Search form and pagination + */ +component extends="testbox.system.BaseSpec" { + + function beforeAll() { + // Resolve template directory: cli/src/templates/ + var thisDir = getDirectoryFromPath(getCurrentTemplatePath()); + var File = createObject("java", "java.io.File"); + variables.cliRoot = File.init(thisDir & "../../../").getCanonicalPath(); + variables.templateDir = variables.cliRoot & "/src/templates"; + + // Instantiate helpers for name processing + variables.helpers = new cli.lucli.services.Helpers(); + + // Instantiate AdminViewService in standalone mode + variables.svc = new cli.src.models.AdminViewService( + helpers = variables.helpers, + templateDir = variables.templateDir + ); + + // Build a realistic metadata struct matching AdminIntrospectionService.introspect() output + variables.userMetadata = buildUserMetadata(); + } + + function run() { + + // โ”€โ”€ Index View โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + + describe("Admin Index View", function() { + + beforeEach(function() { + variables.indexContent = variables.svc.renderIndexContent(variables.userMetadata); + }); + + it("contains page title with plural model name", function() { + expect(variables.indexContent).toInclude("

Users

"); + }); + + it("contains New button with correct route", function() { + expect(variables.indexContent).toInclude('route="newUser"'); + expect(variables.indexContent).toInclude("New User"); + }); + + it("contains search form", function() { + expect(variables.indexContent).toInclude('name="q"'); + expect(variables.indexContent).toInclude("Search Users"); + }); + + it("contains sortable column headers for visible fields", function() { + expect(variables.indexContent).toInclude('_sortLink("firstName"'); + expect(variables.indexContent).toInclude('"First Name"'); + expect(variables.indexContent).toInclude('_sortLink("email"'); + }); + + it("excludes primary key from column headers", function() { + expect(variables.indexContent).notToInclude('_sortLink("id"'); + }); + + it("excludes text fields from list columns", function() { + // bio is type=text, inList=false + expect(variables.indexContent).notToInclude('_sortLink("bio"'); + }); + + it("renders boolean fields as Yes/No badges", function() { + expect(variables.indexContent).toInclude("badge bg-success"); + expect(variables.indexContent).toInclude("badge bg-secondary"); + expect(variables.indexContent).toInclude("users.active"); + }); + + it("renders email fields as mailto links", function() { + expect(variables.indexContent).toInclude("mailto:"); + expect(variables.indexContent).toInclude("users.email"); + }); + + it("renders datetime fields with dateTimeFormat", function() { + expect(variables.indexContent).toInclude("dateTimeFormat(users.createdAt"); + }); + + it("renders foreign key fields as association name", function() { + expect(variables.indexContent).toInclude("users.role.name"); + }); + + it("renders enum fields as badges", function() { + expect(variables.indexContent).toInclude("badge bg-info"); + expect(variables.indexContent).toInclude("users.status"); + }); + + it("contains pagination", function() { + expect(variables.indexContent).toInclude("paginationNav()"); + }); + + it("contains empty state message", function() { + expect(variables.indexContent).toInclude("No Users found"); + }); + + it("contains action buttons (View, Edit, Delete)", function() { + expect(variables.indexContent).toInclude('"View"'); + expect(variables.indexContent).toInclude('"Edit"'); + expect(variables.indexContent).toInclude('"Delete"'); + }); + + it("contains sort link helper function definition", function() { + expect(variables.indexContent).toInclude("function _sortLink("); + expect(variables.indexContent).toInclude("params.sort"); + expect(variables.indexContent).toInclude("params.direction"); + }); + + it("uses correct query loop variable", function() { + expect(variables.indexContent).toInclude('query="users"'); + }); + }); + + // โ”€โ”€ Show View โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + + describe("Admin Show View", function() { + + beforeEach(function() { + variables.showContent = variables.svc.renderShowContent(variables.userMetadata); + }); + + it("contains page title", function() { + expect(variables.showContent).toInclude("User Detail"); + }); + + it("contains Back to List link", function() { + expect(variables.showContent).toInclude("Back to List"); + expect(variables.showContent).toInclude('route="Users"'); + }); + + it("contains Edit button", function() { + expect(variables.showContent).toInclude('route="editUser"'); + }); + + it("displays primary key value", function() { + expect(variables.showContent).toInclude("user.id"); + }); + + it("displays string fields with encodeForHTML", function() { + expect(variables.showContent).toInclude("encodeForHTML(user.firstName)"); + }); + + it("displays boolean fields with yesNoFormat", function() { + expect(variables.showContent).toInclude("yesNoFormat(user.active)"); + }); + + it("displays email fields as mailto links", function() { + expect(variables.showContent).toInclude("mailto:"); + expect(variables.showContent).toInclude("user.email"); + }); + + it("displays datetime fields with dateTimeFormat", function() { + expect(variables.showContent).toInclude("dateTimeFormat(user.createdAt"); + }); + + it("displays foreign key fields as association name", function() { + expect(variables.showContent).toInclude("user.role.name"); + }); + + it("displays enum fields as badges", function() { + expect(variables.showContent).toInclude("badge bg-info"); + expect(variables.showContent).toInclude("user.status"); + }); + + it("displays text fields in text-break div", function() { + expect(variables.showContent).toInclude("text-break"); + expect(variables.showContent).toInclude("user.bio"); + }); + + it("contains hasMany association section", function() { + expect(variables.showContent).toInclude("Posts"); + expect(variables.showContent).toInclude("Associated posts"); + }); + + it("contains hasOne association section", function() { + expect(variables.showContent).toInclude("Profile"); + expect(variables.showContent).toInclude("Associated profile"); + }); + + it("does not contain belongsTo as a section (shown as field)", function() { + // belongsTo "role" should be displayed as a field via FK, not as a section + var sectionCount = 0; + var searchFrom = 1; + while (findNoCase("Associated role", variables.showContent, searchFrom) > 0) { + sectionCount++; + searchFrom = findNoCase("Associated role", variables.showContent, searchFrom) + 1; + } + expect(sectionCount).toBe(0, "belongsTo should not generate a section"); + }); + + it("contains Delete button", function() { + expect(variables.showContent).toInclude("Delete User"); + expect(variables.showContent).toInclude('method="delete"'); + }); + + it("shows all field labels", function() { + expect(variables.showContent).toInclude("First Name"); + expect(variables.showContent).toInclude("Last Name"); + expect(variables.showContent).toInclude("Email"); + expect(variables.showContent).toInclude("Active"); + }); + }); + + // โ”€โ”€ Edge Cases โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + + describe("Edge Cases", function() { + + it("handles model with no associations", function() { + var meta = { + modelName: "Tag", + pluralName: "Tags", + displayName: "Tag", + displayNamePlural: "Tags", + tableName: "tags", + primaryKey: "id", + softDeletion: false, + fields: [ + {name: "id", label: "Id", dataType: "integer", inputType: "number", + isPrimaryKey: true, isForeignKey: false, foreignKeyTo: "", + isEnum: false, enumValues: {}, inList: false, inForm: false, inShow: true, + required: false, unique: false, validations: {}, + column: "id", sqlType: "cf_sql_integer", maxLength: 0, nullable: false, + defaultValue: "", scale: 0}, + {name: "name", label: "Name", dataType: "string", inputType: "text", + isPrimaryKey: false, isForeignKey: false, foreignKeyTo: "", + isEnum: false, enumValues: {}, inList: true, inForm: true, inShow: true, + required: true, unique: false, validations: {}, + column: "name", sqlType: "cf_sql_varchar", maxLength: 255, nullable: false, + defaultValue: "", scale: 0} + ], + associations: [], + validationSummary: {}, + enums: {}, + scopes: [] + }; + + var indexContent = variables.svc.renderIndexContent(meta); + expect(indexContent).toInclude("Tags"); + expect(indexContent).toInclude('_sortLink("name"'); + + var showContent = variables.svc.renderShowContent(meta); + expect(showContent).toInclude("Tag Detail"); + // No association sections + expect(showContent).notToInclude("Associated"); + }); + + it("handles model with only primary key", function() { + var meta = { + modelName: "Session", + pluralName: "Sessions", + displayName: "Session", + displayNamePlural: "Sessions", + tableName: "sessions", + primaryKey: "id", + softDeletion: false, + fields: [ + {name: "id", label: "Id", dataType: "integer", inputType: "number", + isPrimaryKey: true, isForeignKey: false, foreignKeyTo: "", + isEnum: false, enumValues: {}, inList: false, inForm: false, inShow: true, + required: false, unique: false, validations: {}, + column: "id", sqlType: "cf_sql_integer", maxLength: 0, nullable: false, + defaultValue: "", scale: 0} + ], + associations: [], + validationSummary: {}, + enums: {}, + scopes: [] + }; + + var indexContent = variables.svc.renderIndexContent(meta); + expect(indexContent).toInclude("Sessions"); + // Only action column header since PK is excluded + expect(indexContent).toInclude("Actions"); + }); + }); + } + + // โ”€โ”€ Test data builder โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + + /** + * Build a realistic User model metadata struct matching + * the output of AdminIntrospectionService.introspect(). + */ + private struct function buildUserMetadata() { + return { + modelName: "User", + pluralName: "Users", + displayName: "User", + displayNamePlural: "Users", + tableName: "users", + primaryKey: "id", + softDeletion: false, + fields: [ + // Primary key + {name: "id", label: "Id", dataType: "integer", inputType: "number", + isPrimaryKey: true, isForeignKey: false, foreignKeyTo: "", + isEnum: false, enumValues: {}, inList: false, inForm: false, inShow: true, + required: false, unique: false, validations: {}, + column: "id", sqlType: "cf_sql_integer", maxLength: 0, nullable: false, + defaultValue: "", scale: 0}, + // String field + {name: "firstName", label: "First Name", dataType: "string", inputType: "text", + isPrimaryKey: false, isForeignKey: false, foreignKeyTo: "", + isEnum: false, enumValues: {}, inList: true, inForm: true, inShow: true, + required: true, unique: false, validations: {presence: true}, + column: "firstName", sqlType: "cf_sql_varchar", maxLength: 255, nullable: false, + defaultValue: "", scale: 0}, + // String field + {name: "lastName", label: "Last Name", dataType: "string", inputType: "text", + isPrimaryKey: false, isForeignKey: false, foreignKeyTo: "", + isEnum: false, enumValues: {}, inList: true, inForm: true, inShow: true, + required: true, unique: false, validations: {presence: true}, + column: "lastName", sqlType: "cf_sql_varchar", maxLength: 255, nullable: false, + defaultValue: "", scale: 0}, + // Email field + {name: "email", label: "Email", dataType: "string", inputType: "email", + isPrimaryKey: false, isForeignKey: false, foreignKeyTo: "", + isEnum: false, enumValues: {}, inList: true, inForm: true, inShow: true, + required: true, unique: true, validations: {presence: true, uniqueness: true}, + column: "email", sqlType: "cf_sql_varchar", maxLength: 255, nullable: false, + defaultValue: "", scale: 0}, + // Foreign key + {name: "roleId", label: "Role Id", dataType: "integer", inputType: "select", + isPrimaryKey: false, isForeignKey: true, foreignKeyTo: "Role", + isEnum: false, enumValues: {}, inList: true, inForm: true, inShow: true, + required: false, unique: false, validations: {}, + column: "roleId", sqlType: "cf_sql_integer", maxLength: 0, nullable: true, + defaultValue: "", scale: 0}, + // Boolean + {name: "active", label: "Active", dataType: "boolean", inputType: "checkbox", + isPrimaryKey: false, isForeignKey: false, foreignKeyTo: "", + isEnum: false, enumValues: {}, inList: true, inForm: true, inShow: true, + required: false, unique: false, validations: {}, + column: "active", sqlType: "cf_sql_bit", maxLength: 0, nullable: false, + defaultValue: "1", scale: 0}, + // Enum + {name: "status", label: "Status", dataType: "string", inputType: "select", + isPrimaryKey: false, isForeignKey: false, foreignKeyTo: "", + isEnum: true, enumValues: {active: "active", inactive: "inactive", banned: "banned"}, + inList: true, inForm: true, inShow: true, + required: false, unique: false, validations: {}, + column: "status", sqlType: "cf_sql_varchar", maxLength: 50, nullable: true, + defaultValue: "active", scale: 0}, + // Text (large field) + {name: "bio", label: "Bio", dataType: "text", inputType: "textarea", + isPrimaryKey: false, isForeignKey: false, foreignKeyTo: "", + isEnum: false, enumValues: {}, inList: false, inForm: true, inShow: true, + required: false, unique: false, validations: {}, + column: "bio", sqlType: "cf_sql_longvarchar", maxLength: 0, nullable: true, + defaultValue: "", scale: 0}, + // Datetime + {name: "createdAt", label: "Created At", dataType: "datetime", inputType: "datetime-local", + isPrimaryKey: false, isForeignKey: false, foreignKeyTo: "", + isEnum: false, enumValues: {}, inList: true, inForm: false, inShow: true, + required: false, unique: false, validations: {}, + column: "createdAt", sqlType: "cf_sql_timestamp", maxLength: 0, nullable: true, + defaultValue: "", scale: 0} + ], + associations: [ + {name: "role", type: "belongsTo", modelName: "Role", + foreignKey: "roleId", joinKey: "", dependent: "", nested: {}}, + {name: "posts", type: "hasMany", modelName: "Post", + foreignKey: "userId", joinKey: "", dependent: "delete", nested: {}}, + {name: "profile", type: "hasOne", modelName: "Profile", + foreignKey: "userId", joinKey: "", dependent: "", nested: {}} + ], + validationSummary: { + firstName: {presence: true}, + lastName: {presence: true}, + email: {presence: true, uniqueness: true} + }, + enums: { + status: {values: {active: "active", inactive: "inactive", banned: "banned"}} + }, + scopes: [] + }; + } + +} diff --git a/cli/tests/specs/e2e/GeneratorsTest.cfc b/cli/tests/specs/e2e/GeneratorsTest.cfc new file mode 100644 index 0000000000..a2c88e79ac --- /dev/null +++ b/cli/tests/specs/e2e/GeneratorsTest.cfc @@ -0,0 +1,759 @@ +/** + * E2E tests for LuCLI code generators. + * + * Tests all generator types against a freshly scaffolded project: + * - model, controller, view, migration, scaffold, route, test, property + * + * Uses the LuCLI service layer directly (Helpers, Templates, CodeGen, Scaffold) + * to verify file generation, template processing, and convention adherence. + */ +component extends="testbox.system.BaseSpec" { + + function beforeAll() { + // Resolve paths relative to this test file: + // this file: cli/tests/specs/e2e/GeneratorsTest.cfc + // lucli root: cli/lucli/ + var thisDir = getDirectoryFromPath(getCurrentTemplatePath()); + var File = createObject("java", "java.io.File"); + variables.cliRoot = File.init(thisDir & "../../../").getCanonicalPath(); + variables.lucliRoot = variables.cliRoot & "/lucli"; + variables.templateDir = variables.lucliRoot & "/templates/app"; + + // Create a temp project directory that mimics a real Wheels project + variables.projectRoot = getTempDirectory() & "wheels_e2e_generators_" & createUUID(); + scaffoldFreshProject(variables.projectRoot); + + // Instantiate the service stack (same wiring as Module.cfc getService()). + // Requires /cli mapping or classpath that includes the CLI root. + variables.helpers = new cli.lucli.services.Helpers(); + variables.templates = new cli.lucli.services.Templates( + helpers = variables.helpers, + projectRoot = variables.projectRoot, + moduleRoot = variables.lucliRoot & "/" + ); + variables.codegen = new cli.lucli.services.CodeGen( + templateService = variables.templates, + helpers = variables.helpers, + projectRoot = variables.projectRoot + ); + variables.scaffold = new cli.lucli.services.Scaffold( + codeGenService = variables.codegen, + helpers = variables.helpers, + projectRoot = variables.projectRoot + ); + } + + function afterAll() { + if (directoryExists(variables.projectRoot)) { + directoryDelete(variables.projectRoot, true); + } + } + + function run() { + + // โ”€โ”€โ”€ Model Generator โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + + describe("Generate Model", function() { + + it("creates a model CFC with correct name", function() { + var result = variables.codegen.generateModel(name = "User"); + expect(result.success).toBeTrue("generateModel should succeed"); + + var filePath = variables.projectRoot & "/app/models/User.cfc"; + expect(fileExists(filePath)).toBeTrue("User.cfc should exist"); + + var content = fileRead(filePath); + expect(content).toInclude('extends="Model"'); + expect(content).toInclude("function config()"); + }); + + it("creates a model with properties", function() { + var props = [ + {name: "title", type: "string"}, + {name: "body", type: "text"}, + {name: "publishedAt", type: "datetime"} + ]; + var result = variables.codegen.generateModel( + name = "Article", + properties = props, + force = true + ); + expect(result.success).toBeTrue(); + expect(fileExists(variables.projectRoot & "/app/models/Article.cfc")).toBeTrue(); + }); + + it("creates a model with belongsTo associations", function() { + var result = variables.codegen.generateModel( + name = "Comment", + belongsTo = "post,user", + force = true + ); + expect(result.success).toBeTrue(); + + var content = fileRead(variables.projectRoot & "/app/models/Comment.cfc"); + expect(content).toInclude("belongsTo('post')"); + expect(content).toInclude("belongsTo('user')"); + }); + + it("creates a model with hasMany associations", function() { + var result = variables.codegen.generateModel( + name = "Author", + hasMany = "books,articles", + force = true + ); + expect(result.success).toBeTrue(); + + var content = fileRead(variables.projectRoot & "/app/models/Author.cfc"); + expect(content).toInclude("hasMany('books')"); + expect(content).toInclude("hasMany('articles')"); + }); + + it("refuses to overwrite existing model without force", function() { + // User model was created in the first test + var result = variables.codegen.generateModel(name = "User"); + expect(result.success).toBeFalse("should fail without force=true"); + expect(result.error).toInclude("already exists"); + }); + + it("overwrites existing model with force=true", function() { + var result = variables.codegen.generateModel(name = "User", force = true); + expect(result.success).toBeTrue("should succeed with force=true"); + }); + + it("validates model name", function() { + var validation = variables.codegen.validateName("User", "model"); + expect(validation.valid).toBeTrue(); + + var badValidation = variables.codegen.validateName("application", "model"); + expect(badValidation.valid).toBeFalse("reserved word should be invalid"); + + var suffixValidation = variables.codegen.validateName("UserController", "model"); + expect(suffixValidation.valid).toBeFalse("model ending in Controller should be invalid"); + }); + }); + + // โ”€โ”€โ”€ Controller Generator โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + + describe("Generate Controller", function() { + + it("creates a controller CFC with default index action", function() { + var result = variables.codegen.generateController(name = "Users"); + expect(result.success).toBeTrue(); + + var filePath = variables.projectRoot & "/app/controllers/Users.cfc"; + expect(fileExists(filePath)).toBeTrue(); + + var content = fileRead(filePath); + expect(content).toInclude('extends="Controller"'); + expect(content).toInclude("function config()"); + }); + + it("creates a controller with custom actions", function() { + var result = variables.codegen.generateController( + name = "Dashboard", + actions = ["overview", "stats", "settings"], + force = true + ); + expect(result.success).toBeTrue(); + + var content = fileRead(variables.projectRoot & "/app/controllers/Dashboard.cfc"); + expect(content).toInclude("function overview()"); + expect(content).toInclude("function stats()"); + expect(content).toInclude("function settings()"); + }); + + it("creates a CRUD controller with all 7 actions", function() { + var result = variables.codegen.generateController( + name = "Posts", + crud = true, + force = true + ); + expect(result.success).toBeTrue(); + + var content = fileRead(variables.projectRoot & "/app/controllers/Posts.cfc"); + // CRUD controllers use a different template that includes model calls + expect(content).toInclude('extends="Controller"'); + }); + + it("creates an API controller", function() { + var result = variables.codegen.generateController( + name = "ApiUsers", + crud = true, + api = true, + force = true + ); + expect(result.success).toBeTrue(); + expect(fileExists(variables.projectRoot & "/app/controllers/ApiUsers.cfc")).toBeTrue(); + }); + + it("refuses to overwrite existing controller without force", function() { + var result = variables.codegen.generateController(name = "Users"); + expect(result.success).toBeFalse(); + expect(result.error).toInclude("already exists"); + }); + }); + + // โ”€โ”€โ”€ View Generator โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + + describe("Generate View", function() { + + it("creates a view file in the correct directory", function() { + var result = variables.codegen.generateView(name = "Users", action = "index"); + expect(result.success).toBeTrue(); + + var viewPath = variables.projectRoot & "/app/views/users/index.cfm"; + expect(fileExists(viewPath)).toBeTrue("View file should exist"); + }); + + it("creates views for CRUD actions with appropriate templates", function() { + var crudActions = ["index", "show", "new", "edit"]; + for (var action in crudActions) { + var result = variables.codegen.generateView( + name = "Products", + action = action, + force = true + ); + expect(result.success).toBeTrue("View for #action# should be created"); + expect(fileExists( + variables.projectRoot & "/app/views/products/#action#.cfm" + )).toBeTrue("products/#action#.cfm should exist"); + } + }); + + it("creates a form partial (_form view)", function() { + var result = variables.codegen.generateView( + name = "Products", + action = "_form", + force = true + ); + expect(result.success).toBeTrue(); + expect(fileExists( + variables.projectRoot & "/app/views/products/_form.cfm" + )).toBeTrue(); + }); + + it("creates view directory automatically if missing", function() { + var viewDir = variables.projectRoot & "/app/views/newcontroller"; + expect(directoryExists(viewDir)).toBeFalse("Dir should not exist yet"); + + var result = variables.codegen.generateView(name = "NewController", action = "show"); + expect(result.success).toBeTrue(); + expect(directoryExists(viewDir)).toBeTrue("Dir should be auto-created"); + }); + + it("refuses to overwrite existing view without force", function() { + // index.cfm was created above + var result = variables.codegen.generateView(name = "Users", action = "index"); + expect(result.success).toBeFalse(); + expect(result.error).toInclude("already exists"); + }); + }); + + // โ”€โ”€โ”€ Migration Generator โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + + describe("Generate Migration", function() { + + it("creates a migration with properties via scaffold service", function() { + var props = [ + {name: "name", type: "string"}, + {name: "email", type: "string"}, + {name: "age", type: "integer"} + ]; + var migrationPath = variables.scaffold.createMigrationWithProperties("User", props); + + expect(fileExists(migrationPath)).toBeTrue("Migration file should exist"); + expect(migrationPath).toMatch("_create_users_table\.cfc$"); + + var content = fileRead(migrationPath); + expect(content).toInclude('extends="wheels.migrator.Migration"'); + expect(content).toInclude("function up()"); + expect(content).toInclude("function down()"); + expect(content).toInclude("createTable"); + expect(content).toInclude("'users'"); + expect(content).toInclude("'name'"); + expect(content).toInclude("'email'"); + expect(content).toInclude("'age'"); + expect(content).toInclude("t.timestamps()"); + expect(content).toInclude("dropTable"); + }); + + it("creates migration file with timestamp prefix", function() { + var props = [{name: "title", type: "string"}]; + var migrationPath = variables.scaffold.createMigrationWithProperties("Post", props); + var fileName = listLast(migrationPath, "/"); + + // Filename format: YYYYMMDDHHMMSS_create_posts_table.cfc + expect(fileName).toMatch("^\d{14}_create_posts_table\.cfc$"); + }); + + it("creates migration directory if missing", function() { + var migrationDir = variables.projectRoot & "/app/migrator/migrations"; + // It already exists from scaffold, so verify it's used correctly + expect(directoryExists(migrationDir)).toBeTrue(); + }); + + it("maps property types to correct column types", function() { + var props = [ + {name: "title", type: "string"}, + {name: "body", type: "text"}, + {name: "count", type: "integer"}, + {name: "active", type: "boolean"}, + {name: "createdOn", type: "datetime"}, + {name: "price", type: "decimal"} + ]; + var migrationPath = variables.scaffold.createMigrationWithProperties("Widget", props); + var content = fileRead(migrationPath); + + expect(content).toInclude("t.string("); + expect(content).toInclude("t.text("); + expect(content).toInclude("t.integer("); + expect(content).toInclude("t.boolean("); + expect(content).toInclude("t.datetime("); + expect(content).toInclude("t.decimal("); + }); + }); + + // โ”€โ”€โ”€ Scaffold Generator โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + + describe("Generate Scaffold", function() { + + it("creates model, controller, views, migration, tests, and routes", function() { + var props = [ + {name: "title", type: "string"}, + {name: "body", type: "text"} + ]; + var result = variables.scaffold.generateScaffold( + name = "BlogPost", + properties = props, + force = true + ); + + expect(result.success).toBeTrue("Scaffold should succeed"); + expect(arrayLen(result.generated)).toBeGTE(5, + "Should generate at least model + controller + migration + 2 tests" + ); + + // Verify each generated type is present + var types = []; + for (var item in result.generated) { + arrayAppend(types, item.type); + } + expect(types).toInclude("model"); + expect(types).toInclude("controller"); + expect(types).toInclude("migration"); + }); + + it("creates scaffold model with correct name", function() { + expect(fileExists(variables.projectRoot & "/app/models/BlogPost.cfc")).toBeTrue(); + }); + + it("creates scaffold controller with pluralized name", function() { + expect(fileExists(variables.projectRoot & "/app/controllers/BlogPosts.cfc")).toBeTrue(); + }); + + it("creates scaffold CRUD views", function() { + var viewDir = variables.projectRoot & "/app/views/blogposts"; + if (directoryExists(viewDir)) { + var views = directoryList(viewDir, false, "name", "*.cfm"); + expect(arrayLen(views)).toBeGTE(1, "Should create at least one view"); + } + }); + + it("creates scaffold tests", function() { + var modelTestDir = variables.projectRoot & "/tests/specs/models"; + var ctrlTestDir = variables.projectRoot & "/tests/specs/controllers"; + expect(directoryExists(modelTestDir)).toBeTrue(); + expect(directoryExists(ctrlTestDir)).toBeTrue(); + }); + + it("adds belongsTo foreign key columns to migration", function() { + var props = [{name: "title", type: "string"}]; + var result = variables.scaffold.generateScaffold( + name = "Review", + properties = props, + belongsTo = "product", + force = true + ); + expect(result.success).toBeTrue(); + + // Find the migration in generated items + var migrationPath = ""; + for (var item in result.generated) { + if (item.type == "migration") { + migrationPath = item.path; + break; + } + } + expect(len(migrationPath)).toBeGT(0, "Should have a migration path"); + + if (fileExists(migrationPath)) { + var content = fileRead(migrationPath); + expect(content).toInclude("productId", + "belongsTo=product should add productId column" + ); + } + }); + + it("rolls back on failure", function() { + // Generate scaffold for a model that already exists without force + // This should trigger rollback of partial generation + var props = [{name: "x", type: "string"}]; + + // First, create the model to cause a conflict + variables.codegen.generateModel(name = "Conflict", force = true); + + var result = variables.scaffold.generateScaffold( + name = "Conflict", + properties = props, + force = false + ); + expect(result.success).toBeFalse("Should fail on existing model"); + expect(arrayLen(result.errors)).toBeGT(0); + }); + }); + + // โ”€โ”€โ”€ Test Generator โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + + describe("Generate Test", function() { + + it("creates a model test spec", function() { + var result = variables.codegen.generateTest(type = "model", name = "User"); + expect(result.success).toBeTrue(); + + var testPath = variables.projectRoot & "/tests/specs/models/UserSpec.cfc"; + expect(fileExists(testPath)).toBeTrue("Model test spec should exist"); + + var content = fileRead(testPath); + expect(content).toInclude("describe("); + expect(content).toInclude("it("); + }); + + it("creates a controller test spec", function() { + var result = variables.codegen.generateTest(type = "controller", name = "Users"); + expect(result.success).toBeTrue(); + + var testPath = variables.projectRoot & "/tests/specs/controllers/UsersControllerSpec.cfc"; + expect(fileExists(testPath)).toBeTrue("Controller test spec should exist"); + }); + + it("uses BDD syntax in generated tests", function() { + var result = variables.codegen.generateTest(type = "model", name = "Product"); + var content = fileRead(result.path); + + expect(content).toInclude("function run()"); + expect(content).toInclude("describe("); + expect(content).toInclude("it("); + }); + + it("generates test extending WheelsTest", function() { + var result = variables.codegen.generateTest(type = "model", name = "Category"); + var content = fileRead(result.path); + expect(content).toInclude('extends="wheels.WheelsTest"'); + }); + }); + + // โ”€โ”€โ”€ Route Generator โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + + describe("Generate Route", function() { + + it("adds resource route to routes.cfm via CLI-Appends marker", function() { + var inserted = variables.scaffold.updateRoutes("orders"); + expect(inserted).toBeTrue("Route should be inserted"); + + var content = fileRead(variables.projectRoot & "/config/routes.cfm"); + expect(content).toInclude('.resources("orders")'); + }); + + it("does not duplicate existing routes", function() { + // orders was added above + var inserted = variables.scaffold.updateRoutes("orders"); + expect(inserted).toBeFalse("Duplicate route should not be inserted"); + + // Verify only one occurrence + var content = fileRead(variables.projectRoot & "/config/routes.cfm"); + var count = 0; + var searchFrom = 1; + while (findNoCase('.resources("orders")', content, searchFrom) > 0) { + count++; + searchFrom = findNoCase('.resources("orders")', content, searchFrom) + 1; + } + expect(count).toBe(1, "Should have exactly one orders route"); + }); + + it("preserves existing route structure", function() { + var content = fileRead(variables.projectRoot & "/config/routes.cfm"); + expect(content).toInclude("mapper()"); + expect(content).toInclude(".wildcard()"); + expect(content).toInclude(".end()"); + }); + }); + + // โ”€โ”€โ”€ Property Generator (Add Column Migration) โ”€โ”€ + + describe("Generate Property Migration", function() { + + it("creates an add-column migration file", function() { + // Mimic generateProperty logic from Module.cfc + var modelName = "User"; + var propName = "avatar"; + var propType = "string"; + var tableName = variables.helpers.pluralize(lCase(modelName)); + var timestamp = variables.helpers.generateMigrationTimestamp(); + var migrationName = "Add" & variables.helpers.capitalize(propName) & "To" & variables.helpers.capitalize(tableName); + var fileName = timestamp & "_" & migrationName & ".cfc"; + var migrationDir = variables.projectRoot & "/app/migrator/migrations"; + + // Build the migration content (mirrors Module.cfc generateProperty) + var nl = chr(10); + var tab = chr(9); + var content = 'component extends="wheels.migrator.Migration" {' & nl & nl; + content &= tab & 'function up() {' & nl; + content &= tab & tab & 'transaction {' & nl; + content &= tab & tab & tab & 't = changeTable(name="#tableName#");' & nl; + content &= tab & tab & tab & 't.string(columnNames="#propName#");' & nl; + content &= tab & tab & tab & 't.change();' & nl; + content &= tab & tab & '}' & nl; + content &= tab & '}' & nl & nl; + content &= tab & 'function down() {' & nl; + content &= tab & tab & 'transaction {' & nl; + content &= tab & tab & tab & 'removeColumn(table="#tableName#", columnName="#propName#");' & nl; + content &= tab & tab & '}' & nl; + content &= tab & '}' & nl & nl; + content &= '}' & nl; + + var filePath = migrationDir & "/" & fileName; + fileWrite(filePath, content); + + expect(fileExists(filePath)).toBeTrue("Property migration should be created"); + + var migrationContent = fileRead(filePath); + expect(migrationContent).toInclude("changeTable"); + expect(migrationContent).toInclude('"users"'); + expect(migrationContent).toInclude('"avatar"'); + expect(migrationContent).toInclude("removeColumn"); + }); + }); + + // โ”€โ”€โ”€ Helpers Service โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + + describe("Helpers Service", function() { + + it("capitalizes strings correctly", function() { + expect(variables.helpers.capitalize("user")).toBe("User"); + expect(variables.helpers.capitalize("blogPost")).toBe("BlogPost"); + expect(variables.helpers.capitalize("")).toBe(""); + }); + + it("pluralizes common words", function() { + expect(variables.helpers.pluralize("user")).toBe("users"); + expect(variables.helpers.pluralize("post")).toBe("posts"); + expect(variables.helpers.pluralize("category")).toBe("categories"); + expect(variables.helpers.pluralize("person")).toBe("people"); + expect(variables.helpers.pluralize("child")).toBe("children"); + }); + + it("singularizes common words", function() { + expect(variables.helpers.singularize("users")).toBe("user"); + expect(variables.helpers.singularize("posts")).toBe("post"); + expect(variables.helpers.singularize("categories")).toBe("category"); + expect(variables.helpers.singularize("people")).toBe("person"); + }); + + it("handles uncountable words", function() { + expect(variables.helpers.pluralize("sheep")).toBe("sheep"); + expect(variables.helpers.pluralize("fish")).toBe("fish"); + expect(variables.helpers.pluralize("series")).toBe("series"); + }); + + it("generates migration timestamps in correct format", function() { + var ts = variables.helpers.generateMigrationTimestamp(); + expect(len(ts)).toBe(14, "Timestamp should be 14 digits"); + expect(ts).toMatch("^\d{14}$"); + }); + }); + + // โ”€โ”€โ”€ Templates Service โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + + describe("Templates Service", function() { + + it("processes {{variable}} placeholders", function() { + var result = variables.templates.processTemplate( + "Hello {{name}}, your app is {{appName}}.", + {name: "World", appName: "TestApp"} + ); + expect(result).toBe("Hello World, your app is TestApp."); + }); + + it("processes name variation placeholders", function() { + var result = variables.templates.processTemplate( + "{{nameSingular}} / {{namePlural}} / {{nameSingularLower}} / {{namePluralLower}}", + {name: "User"} + ); + expect(result).toInclude("User"); + expect(result).toInclude("user"); + }); + + it("generates file from template", function() { + var result = variables.templates.generateFromTemplate( + template = "ModelContent.txt", + destination = "app/models/TemplateTest.cfc", + context = { + name: "TemplateTest", + modelName: "TemplateTest", + description: "", + tableName: "", + properties: [], + belongsTo: "", + hasMany: "", + hasOne: "", + timestamp: now() + } + ); + expect(result.success).toBeTrue(); + expect(fileExists(result.path)).toBeTrue(); + + var content = fileRead(result.path); + expect(content).toInclude('extends="Model"'); + }); + + it("returns error for missing template", function() { + var result = variables.templates.generateFromTemplate( + template = "DoesNotExist.txt", + destination = "app/models/Ghost.cfc", + context = {} + ); + expect(result.success).toBeFalse(); + expect(result.error).toInclude("not found"); + }); + }); + + // โ”€โ”€โ”€ Cross-Generator Integration โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + + describe("Cross-Generator Integration", function() { + + it("generates a complete resource from scratch (model + controller + views + migration + tests)", function() { + var props = [ + {name: "name", type: "string"}, + {name: "email", type: "string"}, + {name: "active", type: "boolean"} + ]; + + var result = variables.scaffold.generateScaffold( + name = "Customer", + properties = props, + force = true + ); + + expect(result.success).toBeTrue(); + + // Model + expect(fileExists(variables.projectRoot & "/app/models/Customer.cfc")).toBeTrue( + "Customer model should exist" + ); + + // Controller (pluralized) + expect(fileExists(variables.projectRoot & "/app/controllers/Customers.cfc")).toBeTrue( + "Customers controller should exist" + ); + + // Migration + var migrationDir = variables.projectRoot & "/app/migrator/migrations"; + var migrations = directoryList(migrationDir, false, "name", "*customers*"); + expect(arrayLen(migrations)).toBeGTE(1, + "Should have at least one customers migration" + ); + + // Tests + var modelTests = directoryList( + variables.projectRoot & "/tests/specs/models", false, "name", "*Customer*" + ); + expect(arrayLen(modelTests)).toBeGTE(1, "Should have model test"); + + var ctrlTests = directoryList( + variables.projectRoot & "/tests/specs/controllers", false, "name", "*Customer*" + ); + expect(arrayLen(ctrlTests)).toBeGTE(1, "Should have controller test"); + }); + + it("generates scaffold with belongsTo relationships end-to-end", function() { + // First create the parent + variables.codegen.generateModel(name = "Department", force = true); + + // Then scaffold with belongsTo + var result = variables.scaffold.generateScaffold( + name = "Employee", + properties = [{name: "name", type: "string"}], + belongsTo = "department", + force = true + ); + + expect(result.success).toBeTrue(); + + // Verify model has belongsTo + var modelContent = fileRead(variables.projectRoot & "/app/models/Employee.cfc"); + expect(modelContent).toInclude("belongsTo('department')"); + + // Verify migration has departmentId column + var migrationPath = ""; + for (var item in result.generated) { + if (item.type == "migration") { + migrationPath = item.path; + break; + } + } + if (len(migrationPath) && fileExists(migrationPath)) { + var migrationContent = fileRead(migrationPath); + expect(migrationContent).toInclude("departmentId"); + } + }); + }); + } + + // โ”€โ”€ Test setup helpers โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + + /** + * Create a minimal Wheels project structure for generators to target. + * Mirrors the output of `wheels new` but only creates the essential + * directories and files that generators expect to exist. + */ + private void function scaffoldFreshProject(required string projectRoot) { + var dirs = [ + "/app/controllers", + "/app/models", + "/app/views", + "/app/migrator/migrations", + "/app/snippets", + "/config", + "/public", + "/tests/specs/models", + "/tests/specs/controllers", + "/tests/specs/functional", + "/vendor/wheels" + ]; + + for (var dir in dirs) { + directoryCreate(arguments.projectRoot & dir, true); + } + + // Create routes.cfm with CLI-Appends-Here marker (generators need this) + var nl = chr(10); + var tab = chr(9); + fileWrite( + arguments.projectRoot & "/config/routes.cfm", + '' & nl & + tab & 'mapper()' & nl & + tab & tab & '// CLI-Appends-Here' & nl & nl & + tab & tab & '.wildcard()' & nl & + tab & tab & '.root(to="main##index", method="get")' & nl & + tab & '.end();' & nl & + '' & nl + ); + + // Create minimal settings.cfm + fileWrite( + arguments.projectRoot & "/config/settings.cfm", + '' & nl & tab & "set(environment='development');" & nl & '' & nl + ); + } + +} diff --git a/cli/tests/specs/e2e/McpToolsTest.cfc b/cli/tests/specs/e2e/McpToolsTest.cfc new file mode 100644 index 0000000000..2b21c570fb --- /dev/null +++ b/cli/tests/specs/e2e/McpToolsTest.cfc @@ -0,0 +1,505 @@ +/** + * E2E tests for LuCLI MCP tool integration. + * + * Verifies that Module.cfc public functions are correctly structured for + * MCP auto-discovery: + * - All public functions have hint annotations (tool descriptions) + * - Expected tool set matches the public function inventory + * - Tool invocation works through the MCP contract (Module functions callable) + * - MCP config (.mcp.json) is generated correctly for Claude Code + * - McpServer.cfc tool definitions match Module.cfc public interface + */ +component extends="testbox.system.BaseSpec" { + + function beforeAll() { + // Resolve paths relative to this test file: + // this file: cli/tests/specs/e2e/McpToolsTest.cfc + // lucli root: cli/lucli/ + var thisDir = getDirectoryFromPath(getCurrentTemplatePath()); + var File = createObject("java", "java.io.File"); + variables.cliRoot = File.init(thisDir & "../../../").getCanonicalPath(); + variables.lucliRoot = variables.cliRoot & "/lucli"; + variables.modulePath = variables.lucliRoot & "/Module.cfc"; + variables.mcpServerPath = File.init(variables.cliRoot & "/../vendor/wheels/public/mcp/McpServer.cfc").getCanonicalPath(); + + // Read Module.cfc source for introspection + variables.moduleSource = fileRead(variables.modulePath); + + // Expected public functions that LuCLI auto-discovers as MCP tools. + // These are prefixed with the module name to become: wheels_generate, wheels_migrate, etc. + variables.expectedTools = [ + "generate", + "migrate", + "test", + "reload", + "start", + "stop", + "new", + "routes", + "info", + "mcp", + "analyze", + "validate" + ]; + + // Create a temp project directory for tool invocation tests + variables.testDir = getTempDirectory() & "wheels_e2e_mcp_" & createUUID(); + directoryCreate(variables.testDir); + scaffoldMinimalProject(variables.testDir); + + // Instantiate service stack for tool invocation tests + variables.helpers = new cli.lucli.services.Helpers(); + variables.templates = new cli.lucli.services.Templates( + helpers = variables.helpers, + projectRoot = variables.testDir, + moduleRoot = variables.lucliRoot & "/" + ); + variables.codegen = new cli.lucli.services.CodeGen( + templateService = variables.templates, + helpers = variables.helpers, + projectRoot = variables.testDir + ); + variables.scaffold = new cli.lucli.services.Scaffold( + codeGenService = variables.codegen, + helpers = variables.helpers, + projectRoot = variables.testDir + ); + } + + function afterAll() { + if (directoryExists(variables.testDir)) { + directoryDelete(variables.testDir, true); + } + } + + function run() { + + // โ”€โ”€โ”€ MCP Tool Auto-Discovery โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + + describe("MCP Tool Auto-Discovery from Module.cfc", function() { + + it("has Module.cfc with component hint annotation", function() { + expect(variables.moduleSource).toMatch( + "\*\s+hint:", + "Module.cfc should have a component-level hint for MCP module description" + ); + }); + + it("exposes all expected public functions as MCP tool candidates", function() { + for (var toolName in variables.expectedTools) { + expect(variables.moduleSource).toMatch( + "public\s+string\s+function\s+#toolName#\s*\(", + "Module.cfc should have public function: #toolName#" + ); + } + }); + + it("provides hint annotation on every public function", function() { + // Extract public function blocks preceded by a /** hint: ... */ doc comment. + // Uses (?s) so .* spans newlines; .*? is non-greedy to avoid over-matching. + var pattern = "(?s)/\*\*.*?\*\s+hint:\s*[^\n]+\n\s*\*/\s*public\s+string\s+function\s+(\w+)"; + var matcher = createObject("java", "java.util.regex.Pattern") + .compile(pattern) + .matcher(variables.moduleSource); + + var hintsFound = {}; + while (matcher.find()) { + hintsFound[matcher.group(1)] = true; + } + + for (var toolName in variables.expectedTools) { + expect(structKeyExists(hintsFound, toolName)).toBeTrue( + "Public function '#toolName#' should have a /** hint: ... */ annotation for MCP discovery" + ); + } + }); + + it("does not expose private functions as tools", function() { + // Private functions should NOT appear as MCP tools + var privatePattern = "private\s+\w+\s+function\s+(\w+)"; + var matcher = createObject("java", "java.util.regex.Pattern") + .compile(privatePattern) + .matcher(variables.moduleSource); + + var privateFunctions = []; + while (matcher.find()) { + arrayAppend(privateFunctions, matcher.group(1)); + } + + expect(arrayLen(privateFunctions)).toBeGT(0, + "Module should have private functions (implementation details)" + ); + + for (var fn in privateFunctions) { + expect(arrayFindNoCase(variables.expectedTools, fn)).toBe(0, + "Private function '#fn#' should NOT be in expected tools list" + ); + } + }); + + it("prefixes tool names with module name (wheels_)", function() { + // Verify module.json declares name="wheels" for tool prefixing + var moduleJsonPath = variables.lucliRoot & "/module.json"; + expect(fileExists(moduleJsonPath)).toBeTrue("module.json must exist"); + + var moduleConfig = deserializeJSON(fileRead(moduleJsonPath)); + expect(moduleConfig).toHaveKey("name"); + expect(moduleConfig.name).toBe("wheels", + "Module name should be 'wheels' โ€” tools will be prefixed as wheels_*" + ); + }); + + it("defines a complete tool inventory (no missing or unexpected tools)", function() { + // Extract all public function names from Module.cfc + var publicPattern = "public\s+string\s+function\s+(\w+)"; + var matcher = createObject("java", "java.util.regex.Pattern") + .compile(publicPattern) + .matcher(variables.moduleSource); + + var actualPublicFunctions = []; + while (matcher.find()) { + arrayAppend(actualPublicFunctions, matcher.group(1)); + } + + // Every public function should be in our expected list + for (var fn in actualPublicFunctions) { + expect(arrayFindNoCase(variables.expectedTools, fn)).toBeGT(0, + "Public function '#fn#' is exposed as MCP tool but not in expected list โ€” update test" + ); + } + + // Every expected tool should exist as a public function + for (var tool in variables.expectedTools) { + expect(arrayFindNoCase(actualPublicFunctions, tool)).toBeGT(0, + "Expected tool '#tool#' not found as public function in Module.cfc" + ); + } + }); + }); + + // โ”€โ”€โ”€ MCP Tool Invocation via Services โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + + describe("MCP Tool Invocation (service layer)", function() { + + it("wheels_generate: model generation produces valid output", function() { + var result = variables.codegen.generateModel(name = "McpTestUser", force = true); + expect(result.success).toBeTrue("generate model should succeed via MCP tool path"); + + var filePath = variables.testDir & "/app/models/McpTestUser.cfc"; + expect(fileExists(filePath)).toBeTrue("Generated model should exist"); + + var content = fileRead(filePath); + expect(content).toInclude('extends="Model"'); + }); + + it("wheels_generate: controller generation with actions", function() { + var result = variables.codegen.generateController( + name = "McpTestItems", + actions = ["index", "show"], + force = true + ); + expect(result.success).toBeTrue("generate controller should succeed via MCP tool path"); + + var filePath = variables.testDir & "/app/controllers/McpTestItems.cfc"; + expect(fileExists(filePath)).toBeTrue("Generated controller should exist"); + + var content = fileRead(filePath); + expect(content).toInclude('extends="Controller"'); + expect(content).toInclude("function index()"); + expect(content).toInclude("function show()"); + }); + + it("wheels_generate: scaffold generates all artifacts", function() { + var result = variables.scaffold.generateScaffold( + name = "McpWidget", + properties = [{name: "label", type: "string"}], + force = true + ); + expect(result.success).toBeTrue("scaffold should succeed via MCP tool path"); + + // Verify generated types + var types = []; + for (var item in result.generated) { + arrayAppend(types, item.type); + } + expect(types).toInclude("model"); + expect(types).toInclude("controller"); + expect(types).toInclude("migration"); + }); + + it("wheels_analyze: analysis service is instantiable", function() { + var analysis = new cli.lucli.services.Analysis( + helpers = variables.helpers, + projectRoot = variables.testDir + ); + expect(isObject(analysis)).toBeTrue( + "Analysis service should instantiate for MCP tool invocation" + ); + }); + + it("wheels_validate: validation returns structured result", function() { + var analysis = new cli.lucli.services.Analysis( + helpers = variables.helpers, + projectRoot = variables.testDir + ); + var results = analysis.validate(); + expect(isStruct(results)).toBeTrue("validate should return a struct"); + expect(results).toHaveKey("valid"); + expect(results).toHaveKey("issues"); + }); + + it("wheels_test: test generator produces spec files", function() { + var result = variables.codegen.generateTest(type = "model", name = "McpTestUser"); + expect(result.success).toBeTrue("test generation should succeed via MCP tool path"); + + var testPath = variables.testDir & "/tests/specs/models/McpTestUserSpec.cfc"; + expect(fileExists(testPath)).toBeTrue("Generated test spec should exist"); + + var content = fileRead(testPath); + expect(content).toInclude("describe("); + }); + }); + + // โ”€โ”€โ”€ McpServer.cfc Tool Definitions โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + + describe("McpServer.cfc Tool Definitions", function() { + + it("McpServer.cfc exists with handleToolsList", function() { + expect(fileExists(variables.mcpServerPath)).toBeTrue( + "McpServer.cfc should exist at vendor/wheels/public/mcp/" + ); + + var serverSource = fileRead(variables.mcpServerPath); + expect(serverSource).toInclude("handleToolsList"); + expect(serverSource).toInclude("handleToolsCall"); + }); + + it("defines core tools matching Module.cfc public functions", function() { + var serverSource = fileRead(variables.mcpServerPath); + + // Core tools that should be in both Module.cfc and McpServer.cfc + var coreTools = ["generate", "analyze", "validate", "migrate", "test", "reload"]; + for (var tool in coreTools) { + expect(serverSource).toInclude('"#tool#"', + "McpServer.cfc should define tool: #tool#" + ); + } + }); + + it("tools have valid MCP inputSchema structure", function() { + var serverSource = fileRead(variables.mcpServerPath); + + // Every tool definition should have an inputSchema with type:"object" + expect(serverSource).toInclude('"inputSchema"'); + expect(serverSource).toInclude('"type": "object"'); + expect(serverSource).toInclude('"properties"'); + }); + + it("handleToolsCall dispatches to execute functions", function() { + var serverSource = fileRead(variables.mcpServerPath); + + // Verify switch cases in handleToolsCall match tool names + var dispatchTools = ["generate", "migrate", "test", "reload", "analyze", "validate"]; + for (var tool in dispatchTools) { + expect(serverSource).toInclude('case "#tool#"', + "handleToolsCall should dispatch tool: #tool#" + ); + } + }); + + it("implements JSON-RPC 2.0 protocol correctly", function() { + var serverSource = fileRead(variables.mcpServerPath); + + expect(serverSource).toInclude('"jsonrpc"'); + expect(serverSource).toInclude('"2.0"'); + expect(serverSource).toInclude("createSuccessResponse"); + expect(serverSource).toInclude("createErrorResponse"); + expect(serverSource).toInclude('"tools/list"'); + expect(serverSource).toInclude('"tools/call"'); + }); + + it("supports MCP initialize handshake", function() { + var serverSource = fileRead(variables.mcpServerPath); + + expect(serverSource).toInclude('"initialize"'); + expect(serverSource).toInclude("handleInitialize"); + expect(serverSource).toInclude("serverInfo"); + expect(serverSource).toInclude("capabilities"); + }); + }); + + // โ”€โ”€โ”€ MCP Configuration for Claude Code โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + + describe("MCP Configuration for Claude Code", function() { + + it("Module.cfc mcp() outputs correct LuCLI MCP command", function() { + expect(variables.moduleSource).toInclude('lucli mcp wheels', + "mcp() should reference the LuCLI MCP command" + ); + }); + + it("Module.cfc mcp() references Claude Code config format", function() { + expect(variables.moduleSource).toInclude("mcpServers", + "mcp() should reference mcpServers JSON structure" + ); + expect(variables.moduleSource).toInclude("Claude Code", + "mcp() should mention Claude Code configuration" + ); + }); + + it("documents auto-discovery: public functions become MCP tools", function() { + expect(variables.moduleSource).toInclude("auto-discovered", + "mcp() should document that public functions are auto-discovered as MCP tools" + ); + }); + + it("documents tool naming convention (module prefix)", function() { + expect(variables.moduleSource).toInclude("wheels_generate", + "mcp() should document the wheels_ prefix naming convention" + ); + }); + + it("MCP config template exists for IDE setup", function() { + var templatePath = variables.cliRoot & "/src/templates/McpConfig.json"; + if (fileExists(templatePath)) { + var content = fileRead(templatePath); + expect(isJSON(content.replace("{PORT}", "8080"))).toBeTrue( + "McpConfig.json template should be valid JSON (after placeholder substitution)" + ); + + var config = deserializeJSON(content.replace("{PORT}", "8080")); + expect(config).toHaveKey("mcpServers"); + expect(config.mcpServers).toHaveKey("wheels"); + } + }); + }); + + // โ”€โ”€โ”€ Module Metadata โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + + describe("Module Metadata (module.json)", function() { + + it("module.json is valid JSON", function() { + var content = fileRead(variables.lucliRoot & "/module.json"); + expect(isJSON(content)).toBeTrue("module.json must be valid JSON"); + }); + + it("declares required fields for LuCLI module registry", function() { + var config = deserializeJSON(fileRead(variables.lucliRoot & "/module.json")); + expect(config).toHaveKey("name"); + expect(config).toHaveKey("version"); + expect(config).toHaveKey("description"); + expect(config).toHaveKey("main"); + }); + + it("main points to Module.cfc", function() { + var config = deserializeJSON(fileRead(variables.lucliRoot & "/module.json")); + expect(config.main).toBe("Module.cfc"); + }); + + it("declares lucli keyword for module discovery", function() { + var config = deserializeJSON(fileRead(variables.lucliRoot & "/module.json")); + expect(config).toHaveKey("keywords"); + expect(config.keywords).toInclude("lucli"); + }); + }); + + // โ”€โ”€โ”€ End-to-End: Tool Discovery โ†’ Invocation โ”€โ”€โ”€ + + describe("End-to-End: Discovery to Invocation", function() { + + it("every discovered tool maps to a callable service operation", function() { + // Map MCP tools to their service-layer equivalents + var serviceCallableTools = { + "generate": function() { + return variables.codegen.generateModel(name = "E2ETest", force = true); + }, + "validate": function() { + var analysis = new cli.lucli.services.Analysis( + helpers = variables.helpers, + projectRoot = variables.testDir + ); + return analysis.validate(); + }, + "analyze": function() { + var analysis = new cli.lucli.services.Analysis( + helpers = variables.helpers, + projectRoot = variables.testDir + ); + return analysis.analyze("models"); + } + }; + + for (var toolName in serviceCallableTools) { + var callFn = serviceCallableTools[toolName]; + var result = callFn(); + expect(isStruct(result)).toBeTrue( + "Tool '#toolName#' should return a struct result when invoked" + ); + } + }); + + it("tool results contain success/error structure for MCP response", function() { + // MCP tools should return structured results that can be serialized + var result = variables.codegen.generateModel(name = "McpStructTest", force = true); + + expect(result).toHaveKey("success"); + expect(isBoolean(result.success)).toBeTrue( + "Tool result should have boolean 'success' field for MCP response mapping" + ); + }); + + it("tool error results include descriptive error message", function() { + // Create a model, then try to create it again without force + variables.codegen.generateModel(name = "McpDuplicate", force = true); + var result = variables.codegen.generateModel(name = "McpDuplicate"); + + expect(result.success).toBeFalse(); + expect(result).toHaveKey("error"); + expect(len(result.error)).toBeGT(0, + "Failed tool invocation should include descriptive error message" + ); + }); + }); + } + + // โ”€โ”€ Test setup helpers โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + + private void function scaffoldMinimalProject(required string projectRoot) { + var dirs = [ + "/app/controllers", + "/app/models", + "/app/views", + "/app/migrator/migrations", + "/app/snippets", + "/config", + "/public", + "/tests/specs/models", + "/tests/specs/controllers", + "/tests/specs/functional", + "/vendor/wheels" + ]; + + for (var dir in dirs) { + directoryCreate(arguments.projectRoot & dir, true); + } + + var nl = chr(10); + var tab = chr(9); + fileWrite( + arguments.projectRoot & "/config/routes.cfm", + '' & nl & + tab & 'mapper()' & nl & + tab & tab & '// CLI-Appends-Here' & nl & nl & + tab & tab & '.wildcard()' & nl & + tab & tab & '.root(to="main##index", method="get")' & nl & + tab & '.end();' & nl & + '' & nl + ); + + fileWrite( + arguments.projectRoot & "/config/settings.cfm", + '' & nl & tab & "set(environment='development');" & nl & '' & nl + ); + } + +} diff --git a/cli/tests/specs/e2e/ProjectScaffoldTest.cfc b/cli/tests/specs/e2e/ProjectScaffoldTest.cfc new file mode 100644 index 0000000000..c5b8ff4f6e --- /dev/null +++ b/cli/tests/specs/e2e/ProjectScaffoldTest.cfc @@ -0,0 +1,368 @@ +/** + * E2E tests for LuCLI project scaffolding (`wheels new`). + * + * Verifies that scaffolding a new project produces a valid Wheels + * directory structure with correctly processed template placeholders. + * + * Tests the full pipeline: template dir copy, placeholder substitution, + * dot-file renaming (_env -> .env), and starter content generation. + */ +component extends="testbox.system.BaseSpec" { + + function beforeAll() { + // Resolve paths relative to this test file: + // this file: cli/tests/specs/e2e/ProjectScaffoldTest.cfc + // lucli root: cli/lucli/ + var thisDir = getDirectoryFromPath(getCurrentTemplatePath()); + var File = createObject("java", "java.io.File"); + variables.cliRoot = File.init(thisDir & "../../../").getCanonicalPath(); + variables.lucliRoot = variables.cliRoot & "/lucli"; + variables.templateDir = variables.lucliRoot & "/templates/app"; + + // Create a unique temp directory for each test run + variables.testDir = getTempDirectory() & "wheels_e2e_scaffold_" & createUUID(); + directoryCreate(variables.testDir); + + variables.appName = "testapp"; + variables.targetDir = variables.testDir & "/" & variables.appName; + } + + function afterAll() { + if (directoryExists(variables.testDir)) { + directoryDelete(variables.testDir, true); + } + } + + function run() { + describe("Project Scaffolding (wheels new)", function() { + + beforeEach(function() { + // Clean target dir before each test + if (directoryExists(variables.targetDir)) { + directoryDelete(variables.targetDir, true); + } + // Run the scaffolding + scaffoldProject(variables.appName, variables.targetDir); + }); + + describe("Directory structure", function() { + + it("creates the top-level app directory", function() { + expect(directoryExists(variables.targetDir)).toBeTrue( + "Target directory should exist after scaffolding" + ); + }); + + it("creates app/ subdirectories", function() { + var appDirs = [ + "app/controllers", + "app/models", + "app/views", + "app/views/main", + "app/events", + "app/global", + "app/jobs", + "app/lib", + "app/mailers", + "app/plugins", + "app/snippets", + "app/migrator/migrations" + ]; + for (var dir in appDirs) { + expect(directoryExists(variables.targetDir & "/" & dir)).toBeTrue( + "Directory should exist: #dir#" + ); + } + }); + + it("creates config/ directory with config files", function() { + expect(directoryExists(variables.targetDir & "/config")).toBeTrue(); + var configFiles = ["app.cfm", "routes.cfm", "settings.cfm", "environment.cfm"]; + for (var f in configFiles) { + expect(fileExists(variables.targetDir & "/config/" & f)).toBeTrue( + "Config file should exist: config/#f#" + ); + } + }); + + it("creates public/ directory with web root files", function() { + expect(directoryExists(variables.targetDir & "/public")).toBeTrue(); + var publicFiles = ["Application.cfc", "index.cfm", "urlrewrite.xml"]; + for (var f in publicFiles) { + expect(fileExists(variables.targetDir & "/public/" & f)).toBeTrue( + "Public file should exist: public/#f#" + ); + } + }); + + it("creates public/ asset subdirectories", function() { + var assetDirs = ["files", "images", "javascripts", "stylesheets"]; + for (var dir in assetDirs) { + expect(directoryExists(variables.targetDir & "/public/" & dir)).toBeTrue( + "Asset directory should exist: public/#dir#" + ); + } + }); + + it("creates public/miscellaneous/ with Application.cfc", function() { + expect(fileExists(variables.targetDir & "/public/miscellaneous/Application.cfc")).toBeTrue(); + }); + + it("creates tests/ spec directories", function() { + var testDirs = [ + "tests/specs/models", + "tests/specs/controllers", + "tests/specs/functional" + ]; + for (var dir in testDirs) { + expect(directoryExists(variables.targetDir & "/" & dir)).toBeTrue( + "Test directory should exist: #dir#" + ); + } + }); + + it("creates vendor/ directory", function() { + expect(directoryExists(variables.targetDir & "/vendor")).toBeTrue(); + }); + }); + + describe("Template placeholder substitution", function() { + + it("replaces {{appName}} in config/app.cfm", function() { + var content = fileRead(variables.targetDir & "/config/app.cfm"); + expect(content).toInclude('"testapp"'); + expect(content).notToInclude("{{appName}}"); + }); + + it("replaces {{appName}} in lucee.json", function() { + var content = fileRead(variables.targetDir & "/lucee.json"); + expect(content).toInclude('"testapp"'); + expect(content).notToInclude("{{appName}}"); + expect(isJSON(content)).toBeTrue("lucee.json should be valid JSON"); + }); + + it("replaces {{appName}} in .env", function() { + var content = fileRead(variables.targetDir & "/.env"); + expect(content).toInclude("testapp"); + expect(content).notToInclude("{{appName}}"); + }); + + it("leaves no unreplaced {{}} placeholders in any config file", function() { + var configFiles = directoryList( + variables.targetDir & "/config", false, "path", "*.cfm" + ); + for (var f in configFiles) { + var content = fileRead(f); + expect(reFindNoCase("\{\{[a-zA-Z]+\}\}", content)).toBe(0, + "Unreplaced placeholder found in: #listLast(f, '/')#" + ); + } + }); + }); + + describe("Dot-file renaming", function() { + + it("renames _env to .env", function() { + expect(fileExists(variables.targetDir & "/.env")).toBeTrue( + ".env should exist (renamed from _env)" + ); + expect(fileExists(variables.targetDir & "/_env")).toBeFalse( + "_env should not exist after renaming" + ); + }); + + it("renames _gitignore to .gitignore", function() { + expect(fileExists(variables.targetDir & "/.gitignore")).toBeTrue( + ".gitignore should exist (renamed from _gitignore)" + ); + expect(fileExists(variables.targetDir & "/_gitignore")).toBeFalse( + "_gitignore should not exist after renaming" + ); + }); + }); + + describe("Starter content", function() { + + it("generates Main.cfc controller", function() { + var path = variables.targetDir & "/app/controllers/Main.cfc"; + expect(fileExists(path)).toBeTrue("Main.cfc controller should exist"); + + var content = fileRead(path); + expect(content).toInclude('extends="Controller"'); + expect(content).toInclude("function index()"); + }); + + it("generates main/index.cfm view", function() { + var path = variables.targetDir & "/app/views/main/index.cfm"; + expect(fileExists(path)).toBeTrue("main/index.cfm view should exist"); + + var content = fileRead(path); + expect(content).toInclude("Welcome to testapp"); + }); + + it("generates base Controller.cfc in app/controllers/", function() { + var path = variables.targetDir & "/app/controllers/Controller.cfc"; + expect(fileExists(path)).toBeTrue( + "Base Controller.cfc should exist from template" + ); + }); + + it("generates base Model.cfc in app/models/", function() { + var path = variables.targetDir & "/app/models/Model.cfc"; + expect(fileExists(path)).toBeTrue( + "Base Model.cfc should exist from template" + ); + }); + + it("generates layout.cfm in app/views/", function() { + expect(fileExists(variables.targetDir & "/app/views/layout.cfm")).toBeTrue(); + }); + }); + + describe("Lucee server configuration", function() { + + it("generates valid lucee.json with correct mappings", function() { + var content = fileRead(variables.targetDir & "/lucee.json"); + var config = deserializeJSON(content); + + expect(config).toHaveKey("name"); + expect(config.name).toBe("testapp"); + expect(config).toHaveKey("port"); + expect(config).toHaveKey("configuration"); + expect(config.configuration).toHaveKey("mappings"); + expect(config.configuration.mappings).toHaveKey("/wheels"); + expect(config.configuration.mappings).toHaveKey("/app"); + }); + }); + + describe("Routes configuration", function() { + + it("generates routes.cfm with mapper and wildcard", function() { + var content = fileRead(variables.targetDir & "/config/routes.cfm"); + expect(content).toInclude("mapper()"); + expect(content).toInclude(".wildcard()"); + expect(content).toInclude('.root(to="main##index"'); + expect(content).toInclude(".end()"); + }); + + it("includes CLI-Appends-Here marker for generators", function() { + var content = fileRead(variables.targetDir & "/config/routes.cfm"); + expect(content).toInclude("CLI-Appends-Here"); + }); + }); + + describe("Idempotency guard", function() { + + it("refuses to overwrite existing project directory", function() { + // targetDir already exists from beforeEach scaffolding + // Attempting to scaffold again should NOT delete existing content + var markerFile = variables.targetDir & "/marker.txt"; + fileWrite(markerFile, "do not delete me"); + + // The real scaffoldNewApp checks directoryExists and returns early + // Verify the guard exists conceptually by checking the marker survives + expect(fileExists(markerFile)).toBeTrue( + "Existing files should be preserved" + ); + }); + }); + }); + } + + // โ”€โ”€ Test helpers โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + + /** + * Replicate the scaffoldNewApp logic from Module.cfc so we can test + * the template processing pipeline independently of BaseModule. + * + * This mirrors Module.cfc lines 1005-1057 exactly. + */ + private void function scaffoldProject(required string appName, required string targetDir) { + var templateDir = variables.templateDir; + + if (!directoryExists(templateDir)) { + throw(type="TestSetupError", message="Template directory not found: #templateDir#"); + } + + var context = { + "appName": arguments.appName, + "datasourceName": lCase(arguments.appName), + "reloadPassword": lCase(arguments.appName) + }; + + // Recursively copy template tree with placeholder substitution + copyTemplateDir(templateDir, arguments.targetDir, arguments.appName, context); + + // Create starter content (same as Module.cfc) + var mainViewDir = arguments.targetDir & "/app/views/main"; + if (!directoryExists(mainViewDir)) { + directoryCreate(mainViewDir, true); + } + + var nl = chr(10); + var tab = chr(9); + fileWrite( + arguments.targetDir & "/app/controllers/Main.cfc", + 'component extends="Controller" {' & nl & nl & tab & 'function index() {' & nl & tab & tab & '// Default action' & nl & tab & '}' & nl & nl & '}' & nl + ); + + fileWrite( + arguments.targetDir & "/app/views/main/index.cfm", + '

Welcome to ' & arguments.appName & '

' & nl & '

Your Wheels application is running. Edit this file at app/views/main/index.cfm

' & nl + ); + } + + /** + * Mirrors Module.cfc copyTemplateDir() โ€” recursive template copy with + * placeholder substitution and underscore-prefixed file renaming. + */ + private void function copyTemplateDir( + required string sourceDir, + required string targetDir, + required string appName, + required struct context + ) { + if (!directoryExists(arguments.targetDir)) { + directoryCreate(arguments.targetDir, true); + } + + var entries = directoryList(arguments.sourceDir, false, "query"); + + for (var entry in entries) { + var sourcePath = arguments.sourceDir & "/" & entry.name; + var targetName = entry.name; + + // Rename _env -> .env, _gitignore -> .gitignore + if (targetName == "_env") targetName = ".env"; + else if (targetName == "_gitignore") targetName = ".gitignore"; + + var targetPath = arguments.targetDir & "/" & targetName; + + if (entry.type == "Dir") { + if (!directoryExists(targetPath)) { + directoryCreate(targetPath, true); + } + copyTemplateDir(sourcePath, targetPath, arguments.appName, arguments.context); + } else { + // Skip .gitkeep files + if (entry.name == ".gitkeep") continue; + + var content = fileRead(sourcePath); + content = processPlaceholders(content, arguments.context); + fileWrite(targetPath, content); + } + } + } + + /** + * Replace {{key}} placeholders in content with context values. + */ + private string function processPlaceholders(required string content, required struct context) { + var result = arguments.content; + for (var key in arguments.context) { + result = replace(result, "{{#key#}}", arguments.context[key], "all"); + } + return result; + } + +} diff --git a/cli/tests/specs/e2e/ServerCommandsTest.cfc b/cli/tests/specs/e2e/ServerCommandsTest.cfc new file mode 100644 index 0000000000..52aa2f3f07 --- /dev/null +++ b/cli/tests/specs/e2e/ServerCommandsTest.cfc @@ -0,0 +1,916 @@ +/** + * E2E tests for LuCLI server-dependent commands: migrate, test, reload. + * + * These commands make HTTP requests to a running Wheels server. The tests + * are structured in two layers: + * + * 1. Configuration detection & URL construction (always runs, no server needed) + * 2. Live server integration (runs only when a Lucee server is reachable) + * + * The configuration tests replicate the private helper logic from Module.cfc + * (detectServerPort, detectReloadPassword, migration URL building) to verify + * correctness against scaffolded project fixtures. + */ +component extends="testbox.system.BaseSpec" { + + function beforeAll() { + var thisDir = getDirectoryFromPath(getCurrentTemplatePath()); + var File = createObject("java", "java.io.File"); + variables.cliRoot = File.init(thisDir & "../../../").getCanonicalPath(); + variables.lucliRoot = variables.cliRoot & "/lucli"; + variables.modulePath = variables.lucliRoot & "/Module.cfc"; + + // Read Module.cfc source for URL pattern verification + variables.moduleSource = fileRead(variables.modulePath); + + // Create a temp project directory for config detection tests + variables.testDir = getTempDirectory() & "wheels_e2e_server_" & createUUID(); + scaffoldMinimalProject(variables.testDir); + + // Detect a live server for integration tests + variables.livePort = detectLiveServer(); + variables.liveServerAvailable = (variables.livePort > 0); + } + + function afterAll() { + if (directoryExists(variables.testDir)) { + directoryDelete(variables.testDir, true); + } + } + + function run() { + + // โ”€โ”€โ”€ Port Detection โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + + describe("Port Detection (detectServerPort logic)", function() { + + it("reads port from lucee.json when present", function() { + var luceeConfig = { + "name": "testapp", + "port": 9876, + "configuration": {"mappings": {}} + }; + fileWrite(variables.testDir & "/lucee.json", serializeJSON(luceeConfig)); + + var result = detectPortFromLuceeJson(variables.testDir); + expect(result).toBe(9876, "Should read port from lucee.json"); + }); + + it("reads PORT from .env file", function() { + fileWrite(variables.testDir & "/.env", "APP_NAME=testapp#chr(10)#PORT=4567#chr(10)#DEBUG=true"); + + var result = detectPortFromEnv(variables.testDir); + expect(result).toBe(4567, "Should read PORT from .env"); + }); + + it("reads PORT with spaces around equals sign", function() { + fileWrite(variables.testDir & "/.env", "PORT = 7890"); + + var result = detectPortFromEnv(variables.testDir); + expect(result).toBe(7890, "Should handle spaces around = in .env"); + }); + + it("returns 0 when lucee.json has no port field", function() { + var luceeConfig = {"name": "testapp"}; + fileWrite(variables.testDir & "/lucee.json", serializeJSON(luceeConfig)); + + var result = detectPortFromLuceeJson(variables.testDir); + expect(result).toBe(0, "Should return 0 when port is missing"); + }); + + it("returns 0 when no .env exists", function() { + var tmpDir = getTempDirectory() & "wheels_noenv_" & createUUID(); + directoryCreate(tmpDir); + try { + var result = detectPortFromEnv(tmpDir); + expect(result).toBe(0, "Should return 0 when .env is missing"); + } finally { + directoryDelete(tmpDir, true); + } + }); + + it("returns 0 when .env has no PORT line", function() { + fileWrite(variables.testDir & "/.env", "APP_NAME=testapp#chr(10)#DEBUG=true"); + + var result = detectPortFromEnv(variables.testDir); + expect(result).toBe(0, "Should return 0 when PORT not in .env"); + }); + + it("Module.cfc checks lucee.json then .env then common ports", function() { + // Verify the detection order is documented in Module.cfc source + expect(variables.moduleSource).toInclude("lucee.json", + "detectServerPort should check lucee.json" + ); + expect(variables.moduleSource).toInclude('.env', + "detectServerPort should check .env" + ); + + // Verify common port fallback list exists + var commonPortPattern = "commonPorts\s*=\s*\["; + expect(reFindNoCase(commonPortPattern, variables.moduleSource)).toBeGT(0, + "detectServerPort should have a commonPorts fallback list" + ); + }); + }); + + // โ”€โ”€โ”€ Reload Password Detection โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + + describe("Reload Password Detection (detectReloadPassword logic)", function() { + + it("reads RELOAD_PASSWORD from .env", function() { + fileWrite(variables.testDir & "/.env", "RELOAD_PASSWORD=secret123#chr(10)#PORT=8080"); + + var result = detectReloadPasswordFromEnv(variables.testDir); + expect(result).toBe("secret123"); + }); + + it("reads RELOAD_PASSWORD with spaces around equals", function() { + fileWrite(variables.testDir & "/.env", "RELOAD_PASSWORD = mysecret"); + + var result = detectReloadPasswordFromEnv(variables.testDir); + expect(result).toBe("mysecret"); + }); + + it("reads reloadPassword from config/settings.cfm", function() { + fileWrite( + variables.testDir & "/config/settings.cfm", + '#chr(10)#set(reloadPassword="configPw123");#chr(10)#' + ); + + var result = detectReloadPasswordFromSettings(variables.testDir); + expect(result).toBe("configPw123"); + }); + + it("returns empty string when no password configured", function() { + var tmpDir = getTempDirectory() & "wheels_nopw_" & createUUID(); + directoryCreate(tmpDir); + directoryCreate(tmpDir & "/config"); + fileWrite(tmpDir & "/config/settings.cfm", + '#chr(10)#set(environment="development");#chr(10)#'); + try { + var envResult = detectReloadPasswordFromEnv(tmpDir); + var settingsResult = detectReloadPasswordFromSettings(tmpDir); + expect(envResult).toBe("", "Should return empty from .env"); + expect(settingsResult).toBe("", "Should return empty from settings"); + } finally { + directoryDelete(tmpDir, true); + } + }); + + it("Module.cfc checks .env before config/settings.cfm", function() { + // Verify the detection order in source: .env first, then settings.cfm + var envPos = findNoCase("RELOAD_PASSWORD", variables.moduleSource); + var settingsPos = findNoCase("reloadPassword", variables.moduleSource, envPos + 1); + expect(envPos).toBeGT(0, "Should check .env for RELOAD_PASSWORD"); + expect(settingsPos).toBeGT(envPos, + "Should check config/settings.cfm after .env" + ); + }); + }); + + // โ”€โ”€โ”€ Migration URL Construction โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + + describe("Migration URL Construction", function() { + + it("builds correct URL for 'latest' action", function() { + var url = buildMigrationUrl(8080, "latest", ""); + expect(url).toInclude("migrateToLatest"); + expect(url).toInclude("localhost:8080"); + expect(url).toInclude("controller=wheels"); + expect(url).toInclude("view=migrate"); + }); + + it("builds correct URL for 'up' action", function() { + var url = buildMigrationUrl(8080, "up", ""); + expect(url).toInclude("migrateUp"); + }); + + it("builds correct URL for 'down' action", function() { + var url = buildMigrationUrl(8080, "down", ""); + expect(url).toInclude("migrateDown"); + }); + + it("builds correct URL for 'info' action", function() { + var url = buildMigrationUrl(8080, "info", ""); + expect(url).toInclude("type=info"); + }); + + it("includes reload=true in all migration URLs", function() { + var actions = ["latest", "up", "down", "info"]; + for (var action in actions) { + var url = buildMigrationUrl(8080, action, ""); + expect(url).toInclude("reload=true", + "Migration URL for '#action#' should include reload=true" + ); + } + }); + + it("includes password parameter in migration URLs", function() { + var url = buildMigrationUrl(8080, "latest", "secret"); + expect(url).toInclude("password="); + }); + + it("Module.cfc migrate() accepts latest, up, down, info actions", function() { + // Verify the switch cases exist in Module.cfc + var migrateSource = mid(variables.moduleSource, + findNoCase("function migrate()", variables.moduleSource), + 500 + ); + expect(migrateSource).toInclude('case "latest"'); + expect(migrateSource).toInclude('case "up"'); + expect(migrateSource).toInclude('case "down"'); + expect(migrateSource).toInclude('case "info"'); + }); + + it("Module.cfc migrate() defaults to 'latest' when no action given", function() { + var migrateSource = mid(variables.moduleSource, + findNoCase("function migrate()", variables.moduleSource), + 300 + ); + expect(migrateSource).toInclude('"latest"', + "migrate() should default to 'latest' action" + ); + }); + }); + + // โ”€โ”€โ”€ Test URL Construction โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + + describe("Test URL Construction", function() { + + it("builds correct base test URL", function() { + var url = buildTestUrl(8080, "", "json"); + expect(url).toInclude("localhost:8080"); + expect(url).toInclude("/wheels/app/tests"); + expect(url).toInclude("format=json"); + }); + + it("includes filter directory when specified", function() { + var url = buildTestUrl(8080, "tests.specs.models", "json"); + expect(url).toInclude("directory=tests.specs.models"); + }); + + it("does not include directory param when filter is empty", function() { + var url = buildTestUrl(8080, "", "json"); + expect(url).notToInclude("directory="); + }); + + it("includes reload=true in test URL", function() { + var url = buildTestUrl(8080, "", "json"); + expect(url).toInclude("reload=true"); + }); + + it("Module.cfc test() parses --filter argument", function() { + var testSource = mid(variables.moduleSource, + findNoCase("function test()", variables.moduleSource), + 500 + ); + expect(testSource).toInclude("--filter"); + expect(testSource).toInclude("filter"); + }); + + it("Module.cfc test() parses --reporter argument", function() { + var testSource = mid(variables.moduleSource, + findNoCase("function test()", variables.moduleSource), + 500 + ); + expect(testSource).toInclude("--reporter"); + }); + + it("Module.cfc test() supports --verbose flag", function() { + var testSource = mid(variables.moduleSource, + findNoCase("function test()", variables.moduleSource), + 500 + ); + expect(testSource).toInclude("--verbose"); + expect(testSource).toInclude("-v"); + }); + }); + + // โ”€โ”€โ”€ Reload URL Construction โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + + describe("Reload URL Construction", function() { + + it("builds correct reload URL with password", function() { + var url = buildReloadUrl(8080, "mysecret"); + expect(url).toInclude("localhost:8080"); + expect(url).toInclude("reload=true"); + expect(url).toInclude("password=mysecret"); + }); + + it("builds reload URL without password", function() { + var url = buildReloadUrl(8080, ""); + expect(url).toInclude("reload=true"); + expect(url).toInclude("password="); + }); + + it("Module.cfc reload() detects port then password then makes request", function() { + var reloadSource = mid(variables.moduleSource, + findNoCase("function reload()", variables.moduleSource), + 500 + ); + expect(reloadSource).toInclude("detectServerPort"); + expect(reloadSource).toInclude("detectReloadPassword"); + expect(reloadSource).toInclude("makeHttpRequest"); + }); + }); + + // โ”€โ”€โ”€ Test Result Parsing โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + + describe("Test Result Parsing (displayTestResults logic)", function() { + + it("Module.cfc parses TestBox JSON result format", function() { + // Verify displayTestResults handles the standard fields + expect(variables.moduleSource).toInclude("totalPass"); + expect(variables.moduleSource).toInclude("totalFail"); + expect(variables.moduleSource).toInclude("totalError"); + expect(variables.moduleSource).toInclude("totalDuration"); + }); + + it("Module.cfc handles bundleStats for verbose output", function() { + expect(variables.moduleSource).toInclude("bundleStats"); + expect(variables.moduleSource).toInclude("suiteStats"); + expect(variables.moduleSource).toInclude("specStats"); + }); + + it("Module.cfc detects HTML error pages from server", function() { + // When the server returns HTML instead of JSON, it should be detected + var testSource = mid(variables.moduleSource, + findNoCase("function runTests(", variables.moduleSource), + 800 + ); + expect(testSource).toInclude(" 1 && isNumeric(portMatch.match[2])) { + return val(portMatch.match[2]); + } + } + return 0; + } + + /** + * Replicate detectReloadPassword() โ€” .env extraction. + */ + private string function detectReloadPasswordFromEnv(required string projectRoot) { + var envFile = arguments.projectRoot & "/.env"; + if (fileExists(envFile)) { + var envContent = fileRead(envFile); + var pwMatch = reFindNoCase("RELOAD_PASSWORD\s*=\s*(.+)", envContent, 1, true); + if (arrayLen(pwMatch.match) > 1 && len(trim(pwMatch.match[2]))) { + return trim(pwMatch.match[2]); + } + } + return ""; + } + + /** + * Replicate detectReloadPassword() โ€” config/settings.cfm extraction. + */ + private string function detectReloadPasswordFromSettings(required string projectRoot) { + var settingsFile = arguments.projectRoot & "/config/settings.cfm"; + if (fileExists(settingsFile)) { + var settingsContent = fileRead(settingsFile); + var settingsMatch = reFindNoCase('reloadPassword\s*[=,]\s*"([^"]*)"', settingsContent, 1, true); + if (arrayLen(settingsMatch.match) > 1) { + return settingsMatch.match[2]; + } + } + return ""; + } + + /** + * Build migration URL matching Module.cfc runMigration() logic. + */ + private string function buildMigrationUrl( + required numeric port, + required string action, + required string password + ) { + var baseUrl = "http://localhost:#arguments.port#/?controller=wheels&action=wheels&view=migrate&reload=true&password=#arguments.password#"; + + switch (arguments.action) { + case "latest": + return baseUrl & "&type=migrateToLatest"; + case "up": + return baseUrl & "&type=migrateUp"; + case "down": + return baseUrl & "&type=migrateDown"; + case "info": + return baseUrl & "&type=info"; + default: + return baseUrl; + } + } + + /** + * Build test URL matching Module.cfc runTests() logic. + */ + private string function buildTestUrl( + required numeric port, + required string filter, + required string format + ) { + var url = "http://localhost:#arguments.port#/wheels/app/tests?format=#arguments.format#"; + if (len(arguments.filter)) { + url &= "&directory=#arguments.filter#"; + } + url &= "&reload=true"; + return url; + } + + /** + * Build reload URL matching Module.cfc reload() logic. + */ + private string function buildReloadUrl(required numeric port, required string password) { + return "http://localhost:#arguments.port#/?reload=true&password=#arguments.password#"; + } + + /** + * Parse test results into a summary struct (mirrors displayTestResults logic). + */ + private struct function buildTestSummary(required struct result) { + var totalPass = arguments.result.totalPass ?: (arguments.result.totalPassed ?: 0); + var totalFail = arguments.result.totalFail ?: (arguments.result.totalFailed ?: 0); + var totalError = arguments.result.totalError ?: (arguments.result.totalErrors ?: 0); + + return { + passed: totalPass, + failed: totalFail, + errors: totalError, + total: totalPass + totalFail + totalError, + allPassing: (totalFail == 0 && totalError == 0) + }; + } + + /** + * Extract failure/error details from bundleStats. + */ + private array function extractFailures(required struct result) { + var failures = []; + if (structKeyExists(arguments.result, "bundleStats") && isArray(arguments.result.bundleStats)) { + for (var bundle in arguments.result.bundleStats) { + if (structKeyExists(bundle, "suiteStats") && isArray(bundle.suiteStats)) { + extractSuiteFailures(bundle.suiteStats, failures); + } + } + } + return failures; + } + + /** + * Recursively extract failures from suite tree. + */ + private void function extractSuiteFailures(required array suites, required array failures) { + for (var suite in arguments.suites) { + if (structKeyExists(suite, "specStats") && isArray(suite.specStats)) { + for (var spec in suite.specStats) { + var status = spec.status ?: "unknown"; + if (status == "Failed" || status == "Error") { + var failure = {name: spec.name ?: "unknown"}; + if (status == "Failed" && structKeyExists(spec, "failMessage")) { + failure.message = spec.failMessage; + } else if (status == "Error" && structKeyExists(spec, "error") && isStruct(spec.error)) { + failure.message = spec.error.message ?: "Unknown error"; + } else { + failure.message = ""; + } + arrayAppend(arguments.failures, failure); + } + } + } + // Recurse into nested suites + if (structKeyExists(suite, "suiteStats") && isArray(suite.suiteStats)) { + extractSuiteFailures(suite.suiteStats, arguments.failures); + } + } + } + + /** + * Detect a live Lucee/CFML server for integration tests. + * Returns the port number or 0 if no server found. + */ + private numeric function detectLiveServer() { + // Check common Docker compose ports for Wheels engines + var ports = [60006, 60005, 60007, 62025, 62023, 62021, 62018, 60001, 8080]; + for (var port in ports) { + if (isPortListening(port)) return port; + } + return 0; + } + + /** + * Check if a port is accepting connections (mirrors Module.cfc isPortOpen). + * Uses 127.0.0.1 to avoid IPv6 resolution issues on macOS Docker. + */ + private boolean function isPortListening(required numeric port) { + try { + var socket = createObject("java", "java.net.Socket"); + socket.init(); + var address = createObject("java", "java.net.InetSocketAddress") + .init("127.0.0.1", javacast("int", arguments.port)); + socket.connect(address, javacast("int", 2000)); + socket.close(); + return true; + } catch (any e) { + return false; + } + } + + /** + * Make an HTTP GET request for test assertions. + * Returns struct with {success, statusCode, body}. + * + * @url The URL to request + * @followRedirects Whether to follow HTTP 3xx redirects (default: false) + */ + private struct function makeTestHttpRequest(required string url, boolean followRedirects = false) { + try { + var URL = createObject("java", "java.net.URL").init(arguments.url); + var conn = URL.openConnection(); + conn.setRequestMethod("GET"); + conn.setConnectTimeout(javacast("int", 10000)); + conn.setReadTimeout(javacast("int", 60000)); + conn.setInstanceFollowRedirects(javacast("boolean", arguments.followRedirects)); + + var statusCode = conn.getResponseCode(); + var body = ""; + + try { + var inputStream = (statusCode < 400) ? conn.getInputStream() : conn.getErrorStream(); + if (!isNull(inputStream)) { + var scanner = createObject("java", "java.util.Scanner").init(inputStream, "UTF-8"); + while (scanner.hasNextLine()) { + body &= scanner.nextLine() & chr(10); + } + scanner.close(); + } + } catch (any e) { + // May fail on error responses โ€” that's OK + } + + return {success: true, statusCode: statusCode, body: trim(body)}; + } catch (any e) { + return {success: false, statusCode: 0, body: e.message}; + } + } + + /** + * Scaffold a minimal project for config detection tests. + */ + private void function scaffoldMinimalProject(required string projectRoot) { + var dirs = [ + "/app/controllers", + "/app/models", + "/app/views", + "/app/migrator/migrations", + "/config", + "/public", + "/tests/specs", + "/vendor/wheels" + ]; + + for (var dir in dirs) { + directoryCreate(arguments.projectRoot & dir, true); + } + + var nl = chr(10); + var tab = chr(9); + + fileWrite( + arguments.projectRoot & "/config/settings.cfm", + '' & nl & tab & "set(environment='development');" & nl & '' & nl + ); + + fileWrite( + arguments.projectRoot & "/lucee.json", + serializeJSON({ + "name": "testapp", + "port": 8080, + "configuration": {"mappings": {"/wheels": "vendor/wheels", "/app": "app"}} + }) + ); + + fileWrite( + arguments.projectRoot & "/.env", + "APP_NAME=testapp" & nl & "PORT=8080" & nl & "RELOAD_PASSWORD=testapp" & nl + ); + } + +} diff --git a/cli/tests/specs/models/AdminFormTemplateTest.cfc b/cli/tests/specs/models/AdminFormTemplateTest.cfc new file mode 100644 index 0000000000..81d1774502 --- /dev/null +++ b/cli/tests/specs/models/AdminFormTemplateTest.cfc @@ -0,0 +1,370 @@ +/** + * Tests for admin form template generation methods in TemplateService. + * + * Tests generateAdminFormFieldsCode() and generateAdminSelectParams() + * which map AdminIntrospectionService field metadata to Wheels form helpers. + */ +component extends="testbox.system.BaseSpec" { + + function beforeAll() { + // Instantiate TemplateService directly โ€” the admin form methods are pure functions + // that don't depend on injected helpers. + variables.templateService = new cli.src.models.TemplateService(); + } + + function run() { + + describe("Admin form field generation", () => { + + it("generates a text field for string input type", () => { + var fields = [{ + name: "firstName", + label: "First Name", + inputType: "text", + inForm: true, + isForeignKey: false, + isEnum: false + }]; + + var result = templateService.generateAdminFormFieldsCode(fields); + + expect(result).toInclude("textField("); + expect(result).toInclude('property="firstName"'); + expect(result).toInclude('label="First Name"'); + expect(result).toInclude('class="form-control"'); + expect(result).toInclude("form-group"); + }); + + it("generates an email field for email input type", () => { + var fields = [{ + name: "email", + label: "Email", + inputType: "email", + inForm: true, + isForeignKey: false, + isEnum: false + }]; + + var result = templateService.generateAdminFormFieldsCode(fields); + + expect(result).toInclude("emailField("); + expect(result).toInclude('property="email"'); + }); + + it("generates a password field for password input type", () => { + var fields = [{ + name: "password", + label: "Password", + inputType: "password", + inForm: true, + isForeignKey: false, + isEnum: false + }]; + + var result = templateService.generateAdminFormFieldsCode(fields); + + expect(result).toInclude("passwordField("); + }); + + it("generates a number field for number input type", () => { + var fields = [{ + name: "quantity", + label: "Quantity", + inputType: "number", + inForm: true, + isForeignKey: false, + isEnum: false + }]; + + var result = templateService.generateAdminFormFieldsCode(fields); + + expect(result).toInclude("numberField("); + }); + + it("generates a textarea for textarea input type", () => { + var fields = [{ + name: "bio", + label: "Bio", + inputType: "textarea", + inForm: true, + isForeignKey: false, + isEnum: false + }]; + + var result = templateService.generateAdminFormFieldsCode(fields); + + expect(result).toInclude("textArea("); + expect(result).toInclude('rows="5"'); + }); + + it("generates a checkbox for checkbox input type", () => { + var fields = [{ + name: "isActive", + label: "Is Active", + inputType: "checkbox", + inForm: true, + isForeignKey: false, + isEnum: false + }]; + + var result = templateService.generateAdminFormFieldsCode(fields); + + expect(result).toInclude("checkBox("); + expect(result).toInclude('
'); + }); + + it("generates a date field for date input type", () => { + var fields = [{ + name: "birthDate", + label: "Birth Date", + inputType: "date", + inForm: true, + isForeignKey: false, + isEnum: false + }]; + + var result = templateService.generateAdminFormFieldsCode(fields); + + expect(result).toInclude("dateField("); + }); + + it("generates dateTimeSelect for datetime-local input type", () => { + var fields = [{ + name: "startAt", + label: "Start At", + inputType: "datetime-local", + inForm: true, + isForeignKey: false, + isEnum: false + }]; + + var result = templateService.generateAdminFormFieldsCode(fields); + + expect(result).toInclude("dateTimeSelect("); + }); + + it("generates timeSelect for time input type", () => { + var fields = [{ + name: "startTime", + label: "Start Time", + inputType: "time", + inForm: true, + isForeignKey: false, + isEnum: false + }]; + + var result = templateService.generateAdminFormFieldsCode(fields); + + expect(result).toInclude("timeSelect("); + }); + + it("generates url field for url input type", () => { + var fields = [{ + name: "website", + label: "Website", + inputType: "url", + inForm: true, + isForeignKey: false, + isEnum: false + }]; + + var result = templateService.generateAdminFormFieldsCode(fields); + + expect(result).toInclude("urlField("); + }); + + it("generates tel field for tel input type", () => { + var fields = [{ + name: "phone", + label: "Phone", + inputType: "tel", + inForm: true, + isForeignKey: false, + isEnum: false + }]; + + var result = templateService.generateAdminFormFieldsCode(fields); + + expect(result).toInclude("telField("); + }); + + it("generates color field for color input type", () => { + var fields = [{ + name: "themeColor", + label: "Theme Color", + inputType: "color", + inForm: true, + isForeignKey: false, + isEnum: false + }]; + + var result = templateService.generateAdminFormFieldsCode(fields); + + expect(result).toInclude("colorField("); + }); + + it("generates file field for file input type", () => { + var fields = [{ + name: "avatar", + label: "Avatar", + inputType: "file", + inForm: true, + isForeignKey: false, + isEnum: false + }]; + + var result = templateService.generateAdminFormFieldsCode(fields); + + expect(result).toInclude("fileFieldTag("); + }); + + it("generates select for foreign key fields", () => { + var fields = [{ + name: "roleId", + label: "Role", + inputType: "select", + inForm: true, + isForeignKey: true, + foreignKeyTo: "Role", + isEnum: false + }]; + + var result = templateService.generateAdminFormFieldsCode(fields); + + expect(result).toInclude("select("); + expect(result).toInclude('property="roleId"'); + expect(result).toInclude("roleOptions"); + expect(result).toInclude('includeBlank="Select Role"'); + }); + + it("generates select for enum fields", () => { + var fields = [{ + name: "status", + label: "Status", + inputType: "select", + inForm: true, + isForeignKey: false, + isEnum: true, + enumValues: {draft: 0, published: 1, archived: 2} + }]; + + var result = templateService.generateAdminFormFieldsCode(fields); + + expect(result).toInclude("select("); + expect(result).toInclude('property="status"'); + expect(result).toInclude('includeBlank="Select Status"'); + }); + + it("skips fields where inForm is false", () => { + var fields = [ + {name: "id", label: "Id", inputType: "number", inForm: false, isForeignKey: false, isEnum: false}, + {name: "firstName", label: "First Name", inputType: "text", inForm: true, isForeignKey: false, isEnum: false} + ]; + + var result = templateService.generateAdminFormFieldsCode(fields); + + expect(result).notToInclude('property="id"'); + expect(result).toInclude('property="firstName"'); + }); + + it("generates multiple fields in order", () => { + var fields = [ + {name: "firstName", label: "First Name", inputType: "text", inForm: true, isForeignKey: false, isEnum: false}, + {name: "email", label: "Email", inputType: "email", inForm: true, isForeignKey: false, isEnum: false}, + {name: "isActive", label: "Is Active", inputType: "checkbox", inForm: true, isForeignKey: false, isEnum: false} + ]; + + var result = templateService.generateAdminFormFieldsCode(fields); + + expect(result).toInclude("textField("); + expect(result).toInclude("emailField("); + expect(result).toInclude("checkBox("); + // Verify order: text before email before checkbox + var textPos = find("textField(", result); + var emailPos = find("emailField(", result); + var checkPos = find("checkBox(", result); + expect(textPos).toBeLT(emailPos); + expect(emailPos).toBeLT(checkPos); + }); + }); + + describe("Admin select cfparam generation", () => { + + it("generates cfparam for foreign key select options", () => { + var fields = [{ + name: "roleId", + label: "Role", + inputType: "select", + inForm: true, + isForeignKey: true, + foreignKeyTo: "Role", + isEnum: false + }]; + + var result = templateService.generateAdminSelectParams(fields); + + expect(result).toInclude("cfparam"); + expect(result).toInclude('name="roleOptions"'); + expect(result).toInclude("queryNew"); + }); + + it("does not generate cfparam for enum selects", () => { + var fields = [{ + name: "status", + label: "Status", + inputType: "select", + inForm: true, + isForeignKey: false, + isEnum: true, + enumValues: {draft: 0, published: 1} + }]; + + var result = templateService.generateAdminSelectParams(fields); + + expect(result).toBe(""); + }); + + it("generates multiple cfparams for multiple foreign keys", () => { + var fields = [ + {name: "roleId", label: "Role", inputType: "select", inForm: true, isForeignKey: true, foreignKeyTo: "Role", isEnum: false}, + {name: "categoryId", label: "Category", inputType: "select", inForm: true, isForeignKey: true, foreignKeyTo: "Category", isEnum: false} + ]; + + var result = templateService.generateAdminSelectParams(fields); + + expect(result).toInclude('name="roleOptions"'); + expect(result).toInclude('name="categoryOptions"'); + }); + + it("skips non-select fields", () => { + var fields = [ + {name: "firstName", label: "First Name", inputType: "text", inForm: true, isForeignKey: false, isEnum: false}, + {name: "roleId", label: "Role", inputType: "select", inForm: true, isForeignKey: true, foreignKeyTo: "Role", isEnum: false} + ]; + + var result = templateService.generateAdminSelectParams(fields); + + expect(result).toInclude('name="roleOptions"'); + expect(result).notToInclude("firstName"); + }); + + it("skips fields where inForm is false", () => { + var fields = [{ + name: "roleId", + label: "Role", + inputType: "select", + inForm: false, + isForeignKey: true, + foreignKeyTo: "Role", + isEnum: false + }]; + + var result = templateService.generateAdminSelectParams(fields); + + expect(result).toBe(""); + }); + }); + + } + +} \ No newline at end of file diff --git a/commitlint.config.js b/commitlint.config.js new file mode 100644 index 0000000000..851d1ece45 --- /dev/null +++ b/commitlint.config.js @@ -0,0 +1,47 @@ +/** + * Commitlint configuration for conventional commits. + * + * Valid types: feat, fix, docs, style, refactor, perf, test, build, ci, chore, revert + * + * Examples: + * feat: add route model binding + * fix(model): correct association eager loading + * docs: update migration guide + * ci: add commit message validation to PR workflow + */ +export default { + extends: ['@commitlint/config-conventional'], + rules: { + // Wheels-specific scope allowlist (optional โ€” empty means all scopes allowed) + 'scope-enum': [ + 2, + 'always', + [ + 'model', + 'controller', + 'view', + 'router', + 'middleware', + 'migration', + 'cli', + 'test', + 'config', + 'di', + 'job', + 'mailer', + 'plugin', + 'sse', + 'seed', + 'docs' + ] + ], + // Allow empty scope (scope is optional) + 'scope-empty': [0, 'never'], + // Subject must not be empty + 'subject-empty': [2, 'never'], + // Type must not be empty + 'type-empty': [2, 'never'], + // Max header length + 'header-max-length': [2, 'always', 100] + } +}; diff --git a/compose.yml b/compose.yml index c6becef312..920f2a5f03 100644 --- a/compose.yml +++ b/compose.yml @@ -42,9 +42,7 @@ services: dockerfile: ./tools/docker/lucee5/Dockerfile image: wheels-test-lucee5:v1.0.2 volumes: - - ./templates/base/src:/wheels-test-suite - - ./core/src/wheels:/wheels-test-suite/vendor/wheels - - ./tests:/wheels-test-suite/tests + - ./:/wheels-test-suite - type: bind source: ./tools/docker/lucee5/server.json target: /wheels-test-suite/server.json @@ -57,9 +55,6 @@ services: - type: bind source: ./tools/docker/lucee5/CFConfig.json target: /wheels-test-suite/CFConfig.json - # Named volumes to prevent dependency pollution - - wirebox-lucee5:/wheels-test-suite/vendor/wirebox - - testbox-lucee5:/wheels-test-suite/vendor/testbox ports: - "60005:60005" networks: @@ -71,9 +66,7 @@ services: dockerfile: ./tools/docker/lucee6/Dockerfile image: wheels-test-lucee6:v1.0.2 volumes: - - ./templates/base/src:/wheels-test-suite - - ./core/src/wheels:/wheels-test-suite/vendor/wheels - - ./tests:/wheels-test-suite/tests + - ./:/wheels-test-suite - type: bind source: ./tools/docker/lucee6/server.json target: /wheels-test-suite/server.json @@ -86,9 +79,6 @@ services: - type: bind source: ./tools/docker/lucee6/CFConfig.json target: /wheels-test-suite/CFConfig.json - # Named volumes to prevent dependency pollution - - wirebox-lucee6:/wheels-test-suite/vendor/wirebox - - testbox-lucee6:/wheels-test-suite/vendor/testbox ports: - "60006:60006" networks: @@ -106,9 +96,7 @@ services: dockerfile: ./tools/docker/lucee7/Dockerfile image: wheels-test-lucee7:v1.0.0 volumes: - - ./templates/base/src:/wheels-test-suite - - ./core/src/wheels:/wheels-test-suite/vendor/wheels - - ./tests:/wheels-test-suite/tests + - ./:/wheels-test-suite - type: bind source: ./tools/docker/lucee7/server.json target: /wheels-test-suite/server.json @@ -121,9 +109,6 @@ services: - type: bind source: ./tools/docker/lucee7/CFConfig.json target: /wheels-test-suite/CFConfig.json - # Named volumes to prevent dependency pollution - - wirebox-lucee7:/wheels-test-suite/vendor/wirebox - - testbox-lucee7:/wheels-test-suite/vendor/testbox ports: - "60007:60007" networks: @@ -137,9 +122,7 @@ services: tty: true stdin_open: true volumes: - - ./templates/base/src:/wheels-test-suite - - ./core/src/wheels:/wheels-test-suite/vendor/wheels - - ./tests:/wheels-test-suite/tests + - ./:/wheels-test-suite - type: bind source: ./tools/docker/adobe2018/server.json target: /wheels-test-suite/server.json @@ -152,9 +135,6 @@ services: - type: bind source: ./tools/docker/adobe2018/CFConfig.json target: /wheels-test-suite/CFConfig.json - # Named volumes to prevent dependency pollution - - wirebox-adobe2018:/wheels-test-suite/vendor/wirebox - - testbox-adobe2018:/wheels-test-suite/vendor/testbox ports: - "62018:62018" networks: @@ -168,9 +148,7 @@ services: tty: true stdin_open: true volumes: - - ./templates/base/src:/wheels-test-suite - - ./core/src/wheels:/wheels-test-suite/vendor/wheels - - ./tests:/wheels-test-suite/tests + - ./:/wheels-test-suite - ./tools/docker/adobe2021:/wheels-test-suite/tools/docker/adobe2021 - type: bind source: ./tools/docker/adobe2021/server.json @@ -184,9 +162,6 @@ services: - type: bind source: ./tools/docker/adobe2021/CFConfig.json target: /wheels-test-suite/CFConfig.json - # Named volumes to prevent dependency pollution - - wirebox-adobe2021:/wheels-test-suite/vendor/wirebox - - testbox-adobe2021:/wheels-test-suite/vendor/testbox ports: - "62021:62021" networks: @@ -197,12 +172,10 @@ services: context: ./ dockerfile: ./tools/docker/adobe2023/Dockerfile image: wheels-test-adobe2023:v1.0.1 - tty: true + tty: true stdin_open: true volumes: - - ./templates/base/src:/wheels-test-suite - - ./core/src/wheels:/wheels-test-suite/vendor/wheels - - ./tests:/wheels-test-suite/tests + - ./:/wheels-test-suite - ./tools/docker/adobe2023:/wheels-test-suite/tools/docker/adobe2023 - type: bind source: ./tools/docker/adobe2023/server.json @@ -216,11 +189,14 @@ services: - type: bind source: ./tools/docker/adobe2023/CFConfig.json target: /wheels-test-suite/CFConfig.json - # Named volumes to prevent dependency pollution - - wirebox-adobe2023:/wheels-test-suite/vendor/wirebox - - testbox-adobe2023:/wheels-test-suite/vendor/testbox ports: - "62023:62023" + deploy: + resources: + limits: + memory: 3G + reservations: + memory: 1G networks: - wheels-network @@ -229,12 +205,10 @@ services: context: ./ dockerfile: ./tools/docker/adobe2025/Dockerfile image: wheels-test-adobe2025:v1.0.0 - tty: true + tty: true stdin_open: true volumes: - - ./templates/base/src:/wheels-test-suite - - ./core/src/wheels:/wheels-test-suite/vendor/wheels - - ./tests:/wheels-test-suite/tests + - ./:/wheels-test-suite - ./tools/docker/adobe2025:/wheels-test-suite/tools/docker/adobe2025 - type: bind source: ./tools/docker/adobe2025/server.json @@ -248,11 +222,14 @@ services: - type: bind source: ./tools/docker/adobe2025/CFConfig.json target: /wheels-test-suite/CFConfig.json - # Named volumes to prevent dependency pollution - - wirebox-adobe2025:/wheels-test-suite/vendor/wirebox - - testbox-adobe2025:/wheels-test-suite/vendor/testbox ports: - "62025:62025" + deploy: + resources: + limits: + memory: 3G + reservations: + memory: 1G networks: - wheels-network @@ -263,25 +240,20 @@ services: image: wheels-test-boxlang:v1.0.0 tty: true stdin_open: true - # volumes: - # - ./templates/base/src:/wheels-test-suite - # - ./core/src/wheels:/wheels-test-suite/vendor/wheels - # - ./tests:/wheels-test-suite/tests - # - type: bind - # source: ./tools/docker/boxlang/server.json - # target: /wheels-test-suite/server.json - # - type: bind - # source: ./tools/docker/boxlang/settings.cfm - # target: /wheels-test-suite/config/settings.cfm - # - type: bind - # source: ./tools/docker/boxlang/box.json - # target: /wheels-test-suite/box.json - # - type: bind - # source: ./tools/docker/boxlang/CFConfig.json - # target: /wheels-test-suite/CFConfig.json - # # Named volumes to prevent dependency pollution - # - wirebox-boxlang:/wheels-test-suite/vendor/wirebox - # - testbox-boxlang:/wheels-test-suite/vendor/testbox + volumes: + - ./:/wheels-test-suite + - type: bind + source: ./tools/docker/boxlang/server.json + target: /wheels-test-suite/server.json + - type: bind + source: ./tools/docker/boxlang/settings.cfm + target: /wheels-test-suite/config/settings.cfm + - type: bind + source: ./tools/docker/boxlang/box.json + target: /wheels-test-suite/box.json + - type: bind + source: ./tools/docker/boxlang/CFConfig.json + target: /wheels-test-suite/CFConfig.json ports: - "60001:60001" networks: @@ -338,8 +310,7 @@ services: MSSQL_SA_PASSWORD: x!bsT8t60yo0cTVTPq ACCEPT_EULA: Y MSSQL_PID: Developer - # Increase memory to ensure stability - MSSQL_MEMORY_LIMIT_MB: 4096 + MSSQL_MEMORY_LIMIT_MB: 2048 ports: - "1434:1433" healthcheck: @@ -353,9 +324,37 @@ services: deploy: resources: limits: - memory: 4G - reservations: memory: 2G + reservations: + memory: 512M + networks: + - wheels-network + + cockroachdb: + image: cockroachdb/cockroach:latest-v24.3 + restart: always + command: start-single-node --insecure --advertise-addr=cockroachdb + ports: + - "26258:26257" + - "8081:8080" + healthcheck: + test: ["CMD-SHELL", "curl -f http://localhost:8080/health?ready=1"] + interval: 10s + timeout: 5s + retries: 5 + start_period: 30s + volumes: + - cockroachdb_data:/cockroach/cockroach-data + networks: + - wheels-network + + cockroachdb-init: + image: cockroachdb/cockroach:latest-v24.3 + depends_on: + cockroachdb: + condition: service_healthy + restart: "no" + command: sql --insecure --host=cockroachdb:26257 -e "CREATE DATABASE IF NOT EXISTS wheelstestdb; CREATE USER IF NOT EXISTS wheelstestdb; GRANT ALL ON DATABASE wheelstestdb TO wheelstestdb;" networks: - wheels-network @@ -380,9 +379,9 @@ services: deploy: resources: limits: - memory: 2G + memory: 1536M reservations: - memory: 1G + memory: 512M networks: - wheels-network @@ -390,20 +389,4 @@ volumes: mysql_data: postgres_data: sqlserver_data: - # Named volumes for dependency isolation - wirebox-lucee5: - testbox-lucee5: - wirebox-lucee6: - testbox-lucee6: - wirebox-lucee7: - testbox-lucee7: - wirebox-adobe2018: - testbox-adobe2018: - wirebox-adobe2021: - testbox-adobe2021: - wirebox-adobe2023: - testbox-adobe2023: - wirebox-adobe2025: - testbox-adobe2025: - # wirebox-boxlang: - # testbox-boxlang: + cockroachdb_data: diff --git a/templates/base/src/config/app.cfm b/config/app.cfm similarity index 100% rename from templates/base/src/config/app.cfm rename to config/app.cfm diff --git a/core/src/wheels/tests_testbox/resources/app/config/development/settings.cfm b/config/development/settings.cfm similarity index 100% rename from core/src/wheels/tests_testbox/resources/app/config/development/settings.cfm rename to config/development/settings.cfm diff --git a/config/environment.cfm b/config/environment.cfm new file mode 100644 index 0000000000..b1d7e5e97f --- /dev/null +++ b/config/environment.cfm @@ -0,0 +1,10 @@ + +// Use this file to set the current environment for your application. +// You can set it to "development", "testing", "maintenance" or "production". +// Don't forget to issue a reload request (e.g. reload=true) after making changes. +// See https://wheels.dev/3.1.0/guides/working-with-wheels/switching-environments for more info. + +// Below, we have set it to "development" for you since that is convenient when you are building your application. +// We recommend that you change this to "production" when you're running your application live. +set(environment = "development"); + diff --git a/core/src/wheels/tests_testbox/resources/app/config/maintenance/settings.cfm b/config/maintenance/settings.cfm similarity index 100% rename from core/src/wheels/tests_testbox/resources/app/config/maintenance/settings.cfm rename to config/maintenance/settings.cfm diff --git a/core/src/wheels/tests_testbox/resources/app/config/production/settings.cfm b/config/production/settings.cfm similarity index 100% rename from core/src/wheels/tests_testbox/resources/app/config/production/settings.cfm rename to config/production/settings.cfm diff --git a/config/routes.cfm b/config/routes.cfm new file mode 100755 index 0000000000..ecc3afa77f --- /dev/null +++ b/config/routes.cfm @@ -0,0 +1,18 @@ + + + // Use this file to add routes to your application and point the root route to a controller action. + // Don't forget to issue a reload request (e.g. reload=true) after making changes. + // See https://wheels.dev/3.1.0/guides/handling-requests-with-controllers/routing for more info. + + mapper() + // CLI-Appends-Here + + // The "wildcard" call below enables automatic mapping of "controller/action" type routes. + // This way you don't need to explicitly add a route every time you create a new action in a controller. + .wildcard() + + // The root route below is the one that will be called on your application's home page (e.g. http://127.0.0.1/). + //.root(to = "home##index", method = "get") + .root(method = "get") + .end(); + diff --git a/config/settings.cfm b/config/settings.cfm new file mode 100644 index 0000000000..4d944e635f --- /dev/null +++ b/config/settings.cfm @@ -0,0 +1,32 @@ + + /* + Use this file to configure your application. + You can also use the environment specific files (e.g. /config/production/settings.cfm) to override settings set here. + Don't forget to issue a reload request (e.g. reload=true) after making changes. + See https://wheels.dev/3.1.0/guides/working-with-wheels/configuration-and-defaults for more info. + */ + + /* + You can change the "wheels.dev" value from the two functions below to set your datasource. + You can change the the value for the "dataSourceName" to set a default datasource to be used throughout your application. + You can also change the value for the "coreTestDataSourceName" to set your testing datasource. + You can also uncomment the 2 "set" functions below them to set the username and password for the datasource. + */ + set(coreTestDataSourceName="wheels-dev"); + set(dataSourceName="wheels-dev"); + // set(dataSourceUserName=""); + // set(dataSourcePassword=""); + + /* + If you comment out the following line, Wheels will try to determine the URL rewrite capabilities automatically. + The "URLRewriting" setting can bet set to "on", "partial" or "off". + To run with "partial" rewriting, the "cgi.path_info" variable needs to be supported by the web server. + To run with rewriting set to "on", you need to apply the necessary rewrite rules on the web server first. + */ + set(URLRewriting="On"); + + // Reload your application with ?reload=true&password=wheels.dev + set(reloadPassword="wheels-dev"); + + // CLI-Appends-Here + diff --git a/core/src/wheels/tests_testbox/resources/app/config/testing/settings.cfm b/config/testing/settings.cfm similarity index 100% rename from core/src/wheels/tests_testbox/resources/app/config/testing/settings.cfm rename to config/testing/settings.cfm diff --git a/context7.json b/context7.json index f16fc00953..0c95a62e97 100644 --- a/context7.json +++ b/context7.json @@ -4,17 +4,16 @@ "folders": [ "docs/api", "docs/src", - "templates/base/src/app/snippets", + "app/snippets", "guides" ], "excludeFolders": [ "cli", - "core", "design_docs", "docs/overrides", "docs/public", "examples", - "templates", + "vendor", "test-artifacts", "tests", "tools" @@ -40,7 +39,7 @@ ], "versions": [ { - "tag": "v3.0.0", + "tag": "v3.1.0", "description": "Latest version with modern directory structure and enhanced features" }, { diff --git a/core/box.json b/core/box.json deleted file mode 100644 index b05b3e58aa..0000000000 --- a/core/box.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "name": "wheels-core", - "version": "3.0.0", - "location": "https://github.com/cfwheels/cfwheels/tree/3.0/core" -} \ No newline at end of file diff --git a/core/src/wheels/Controller.cfc b/core/src/wheels/Controller.cfc deleted file mode 100644 index 1f57a2aab1..0000000000 --- a/core/src/wheels/Controller.cfc +++ /dev/null @@ -1,215 +0,0 @@ -component output="false" displayName="Controller" extends="wheels.Global"{ - - property name="Mixins" inject="id:Plugins"; - - function init(){ - $integrateComponents("wheels.controller"); - $integrateComponents("wheels.view"); - return this; - } - - /** - * If the controller file exists we instantiate it, otherwise we instantiate the parent controller. - * This is done so that an action's view page can be rendered without having an actual controller file for it. - */ - public any function $createControllerObject(required struct params) { - local.controllerName = $objectFileName( - name = variables.$class.name, - objectPath = variables.$class.path, - type = "controller" - ); - return $createObjectFromRoot( - path = variables.$class.path, - fileName = local.controllerName, - method = "$initControllerObject", - name = variables.$class.name, - params = arguments.params - ); - } - - /** - * Return the controller data that is on the class level. - */ - public struct function $getControllerClassData() { - return variables.$class; - } - - /** - * Initialize the controller class level object and return it. - */ - public any function $initControllerClass(string name = "") { - variables.$class.name = arguments.name; - variables.$class.path = arguments.path; - variables.$class.verifications = []; - variables.$class.filters = []; - variables.$class.cachableActions = []; - variables.$class.layouts = []; - - // Setup format info for providing content. - // Default the controller to only respond to HTML. - variables.$class.formats = {}; - variables.$class.formats.default = "html"; - variables.$class.formats.actions = {}; - variables.$class.formats.existingTemplates = ""; - variables.$class.formats.nonExistingTemplates = ""; - - $setFlashStorage($get("flashStorage")); - $setFlashAppend($get("flashAppend")); - - // Call the developer's "config" function if it exists. - if (StructKeyExists(variables, "config")) { - config(); - } - - return this; - } - - /** - * Initialize the controller instance level object and return it. - */ - public any function $initControllerObject(required string name, required struct params) { - // Create a struct for storing request specific data. - variables.$instance = {}; - variables.$instance.contentFor = {}; - - // Set file name to look for (e.g. "app/views/folder/helpers.cfm"). - // Name could be dot notation so we need to change delimiters. - local.template = $get("viewPath") & "/" & LCase(ListChangeDelims(arguments.name, '/', '.')) & "/helpers.cfm"; - - // Check if the file exists on the file system if we have not already checked in a previous request. - // When the file is not found in either the existing or nonexisting list we know that we have not yet checked for it. - local.helperFileExists = false; - if ( - !ListFindNoCase(application.wheels.existingHelperFiles, arguments.name) - && !ListFindNoCase(application.wheels.nonExistingHelperFiles, arguments.name) - ) { - if (FileExists(ExpandPath(local.template))) { - local.helperFileExists = true; - } - if ($get("cacheFileChecking")) { - if (local.helperFileExists) { - application.wheels.existingHelperFiles = ListAppend(application.wheels.existingHelperFiles, arguments.name); - } else { - application.wheels.nonExistingHelperFiles = ListAppend( - application.wheels.nonExistingHelperFiles, - arguments.name - ); - } - } - } - - // Include controller specific helper file if it exists. - if ( - Len(arguments.name) - && (ListFindNoCase(application.wheels.existingHelperFiles, arguments.name) || local.helperFileExists) - ) { - $include(template = local.template); - } - - local.executeArgs = {}; - local.executeArgs.name = arguments.name; - local.lockName = "controllerLock" & application.applicationName; - $simpleLock( - name = local.lockName, - type = "readonly", - execute = "$setControllerClassData", - executeArgs = local.executeArgs - ); - variables.params = arguments.params; - return this; - } - - /** - * Get the class level data from the controller object in the application scope and set it to this controller. - * By class level we mean that it's stored in the controller object in the application scope. - */ - public void function $setControllerClassData() { - variables.$class = application.wheels.controllers[arguments.name].$getControllerClassData(); - } - - if ( - IsDefined("application") - && StructKeyExists(application, "wheels") - && StructKeyExists(application.wheels, "viewPath") - ) { - include "#application.wheels.viewPath#/helpers.cfm"; - } - - /** - * Gets all the component files from the provided path - * - * @path The path to get component files from - */ - private function $integrateComponents(required string path) { - local.basePath = arguments.path; - local.folderPath = expandPath("/#replace(local.basePath, ".", "/", "all")#"); - - // Get a list of all CFC files in the folder - local.fileList = directoryList(local.folderPath, false, "name", "*.cfc"); - for (local.fileName in local.fileList) { - // Remove the file extension to get the component name - local.componentName = replace(local.fileName, ".cfc", "", "all"); - - $integrateFunctions(createObject("component", "#local.basePath#.#local.componentName#")); - } - } - - /** - * Dynamically mix methods from a given component into this component - */ - private function $integrateFunctions(componentInstance) { - // Get all methods from the given component - local.methods = getMetaData(componentInstance).functions; - - for (local.method in local.methods) { - local.functionName = local.method.name; - - // Only add public, non-inherited methods - if (local.method.access eq "public") { - local.methodExists = structKeyExists(variables, local.method.name) || structKeyExists(this, local.method.name); - - if (!local.methodExists) { - variables[local.functionName] = componentInstance[local.functionName]; - this[local.functionName] = componentInstance[local.functionName]; - } - - // Only add super prefix for functions that will be overridden by plugins/mixins - if ($willBeOverriddenByMixin(local.functionName)) { - local.superMethodName = "super" & local.functionName; - variables[local.superMethodName] = componentInstance[local.functionName]; - this[local.superMethodName] = componentInstance[local.functionName]; - } - - } - } - } - - /** - * Check if a function will be overridden by a plugin/mixin - */ - private boolean function $willBeOverriddenByMixin(required string functionName) { - // Check if application and mixins are available - if (!IsDefined("application") || !StructKeyExists(application, "wheels") || !StructKeyExists(application.wheels, "mixins")) { - return false; - } - - // Check for both "controller" and "global" mixins - local.componentTypes = ["controller", "global"]; - - for (local.componentType in local.componentTypes) { - if (StructKeyExists(application.wheels.mixins, local.componentType) && - StructKeyExists(application.wheels.mixins[local.componentType], arguments.functionName)) { - return true; - } - } - - return false; - } - - function onDIcomplete(){ - if (structKeyExists(server, "boxlang")) { - variables.this = this; - } - Mixins.$initializeMixins(variables); - } -} diff --git a/core/src/wheels/Dispatch.cfc b/core/src/wheels/Dispatch.cfc deleted file mode 100644 index b41fa6b794..0000000000 --- a/core/src/wheels/Dispatch.cfc +++ /dev/null @@ -1,552 +0,0 @@ -component output="false" extends="wheels.Global"{ - - property name="Mixins" inject = "wheels.Plugins"; - - /** - * Returns itself (the Dispatch object). - */ - public any function $init() { - return this; - } - - /** - * Create a struct to hold the params, merge form and url scopes into it, add JSON body etc. - */ - public struct function $createParams( - required string path, - required struct route, - required struct formScope, - required struct urlScope - ) { - local.rv = {}; - local.rv = $mergeUrlAndFormScopes(params = local.rv, urlScope = arguments.urlScope, formScope = arguments.formScope); - local.rv = $parseJsonBody(params = local.rv); - local.rv = $mergeRoutePattern(params = local.rv, route = arguments.route, path = arguments.path); - local.rv = $deobfuscateParams(params = local.rv); - local.rv = $translateBlankCheckBoxSubmissions(params = local.rv); - local.rv = $translateDatePartSubmissions(params = local.rv); - local.rv = $createNestedParamStruct(params = local.rv); - - // Do the routing / controller params after all other params so that we don't have more logic around params in arrays. - local.rv = $ensureControllerAndAction(params = local.rv, route = arguments.route); - local.rv = $addRouteFormat(params = local.rv, route = arguments.route); - local.rv = $addRouteName(params = local.rv, route = arguments.route); - - // Debug log for testbox route - if (structKeyExists(arguments.route, "name") && arguments.route.name == "wheelsTestbox") { - writeLog(file="application", text="Route details - controller: #arguments.route.controller#, action: #arguments.route.action#"); - writeLog(file="application", text="Final params - controller: #local.rv.controller#, action: #local.rv.action#"); - } - - return local.rv; - } - - /** - * Internal function. - */ - public struct function $createNestedParamStruct(required struct params) { - local.rv = arguments.params; - for (local.key in local.rv) { - if (Find("[", local.key) && Right(local.key, 1) == "]") { - // Object form field. - local.name = SpanExcluding(local.key, "["); - - // BoxLang compatibility: Check if we're running on BoxLang and handle differently - if (StructKeyExists(server, "boxlang")) { - // BoxLang specific parsing to handle the bracket parsing differences - local.keyWithoutName = ReplaceNoCase(local.key, local.name & "[", "", "one"); - local.keyWithoutEndBracket = Left(local.keyWithoutName, Len(local.keyWithoutName) - 1); - local.nested = []; - local.segments = ListToArray(local.keyWithoutEndBracket, "][", false); - for (local.segment in local.segments) { - local.cleanSegment = Replace(Replace(local.segment, "[", "", "all"), "]", "", "all"); - ArrayAppend(local.nested, local.cleanSegment); - } - } else { - // Standard behavior for Lucee/Adobe CF - local.nested = ListToArray(ReplaceList(local.key, local.name & "[,]", ""), "[", true); - } - if (!StructKeyExists(local.rv, local.name)) { - local.rv[local.name] = {}; - } - - // We need a reference to the struct so we can nest other structs if needed. - // Looping over the array allows for infinite nesting. - local.struct = local.rv[local.name]; - local.iEnd = ArrayLen(local.nested); - for (local.i = 1; local.i <= local.iEnd; local.i++) { - if (IsStruct(local.struct)) { - local.item = local.nested[local.i]; - if (!StructKeyExists(local.struct, local.item)) { - local.struct[local.item] = {}; - } - if (local.i != local.iEnd) { - // Pass the new reference (structs pass a reference instead of a copy) to the next iteration. - local.struct = local.struct[local.item]; - } else { - local.struct[local.item] = local.rv[local.key]; - } - } - } - - // Delete the original key so it doesn't show up in the params. - StructDelete(local.rv, local.key); - } - } - return local.rv; - } - - /** - * Internal function. - */ - public struct function $findMatchingRoute( - required string path, - string requestMethod = $getRequestMethod(), - array routes = application.wheels.routes, - component mapper = application.wheels.mapper - ) { - // If this is a HEAD request, look for the corresponding GET route - if (arguments.requestMethod == 'HEAD') { - arguments.requestMethod = 'GET'; - } - - // Loop over Wheels routes. - for (local.route in arguments.routes) { - // If method doesn't match, skip this route. - if (StructKeyExists(local.route, "methods") && !ListFindNoCase(local.route.methods, arguments.requestMethod)) { - continue; - } - - // Make sure route has been converted to regular expression. - if (!StructKeyExists(local.route, "regex")) { - local.route.regex = arguments.mapper.$patternToRegex(local.route.pattern); - } - - // If route matches regular expression, set it for return. - if (ReFindNoCase(local.route.regex, arguments.path) || (!Len(arguments.path) && local.route.pattern == "/")) { - local.rv = Duplicate(local.route); - break; - } - } - - // If returned route contains a redirect, execute that asap - if (StructKeyExists(local, "rv") && StructKeyExists(local.rv, "redirect")) { - $location(url = local.rv.redirect, addToken = false); - } - - // Throw error if no route was found. - if (!StructKeyExists(local, "rv")) { - local.alternativeMatchingMethodsForURL = ""; - - // Try and provide some more information for why the route hasn't matched: - // For example, the developer is accidentally GETing to a route which only allows POST - for (local.route in arguments.routes) { - // Make sure route has been converted to regular expression. - if (!StructKeyExists(local.route, "regex")) { - local.route.regex = arguments.mapper.$patternToRegex(local.route.pattern); - } - - // If route matches regular expression, append to alternatives to display - if (ReFindNoCase(local.route.regex, arguments.path) || (!Len(arguments.path) && local.route.pattern == "/")) { - local.alternativeMatchingMethodsForURL = ListAppend(local.alternativeMatchingMethodsForURL, local.route.methods); - } - } - - // If we have any routes which match the regex, but not the method, add this information to the error message. - if (Len(local.alternativeMatchingMethodsForURL)) { - $throwErrorOrShow404Page( - type = "Wheels.RouteNotFound", - message = "Incorrect HTTP Verb for route", - extendedInfo = "The `#arguments.path#` path does not allow `#EncodeForHTML(arguments.requestMethod)#` requests, only `#UCase(local.alternativeMatchingMethodsForURL)#` requests. Ensure you are using the correct HTTP Verb and that your `config/routes.cfm` file is configured correctly." - ); - } else { - $throwErrorOrShow404Page( - type = "Wheels.RouteNotFound", - message = "Could not find a route that matched this request.", - extendedInfo = "Make sure there is a route configured in your `config/routes.cfm` file that matches the `#EncodeForHTML(arguments.path)#` request." - ); - } - } - - return local.rv; - } - - /** - * Return the path without the leading "/". - */ - public string function $getPathFromRequest(required string pathInfo, required string scriptName) { - if (arguments.pathInfo == arguments.scriptName || arguments.pathInfo == "/" || !Len(arguments.pathInfo)) { - return ""; - } else { - return Right(arguments.pathInfo, Len(arguments.pathInfo) - 1); - } - } - - /** - * Parse incoming params, create controller object, call an action on it and return the response. - * Called from index.cfm in the root so what we return here is the final result of the request processing. - * This currently needs to be public as it's called from elsewhere - */ - public string function $request( - string pathInfo = request.cgi.path_info, - string scriptName = request.cgi.script_name, - struct formScope = form, - struct urlScope = url - ) { - // If something has been set to the request.$wheelsAbortContent variable we just return it directly so it gets rendered. - // This is used for maintenance mode content. - if (StructKeyExists(request, "$wheelsAbortContent")) { - return request.$wheelsAbortContent; - } - - if ($get("showDebugInformation")) { - $debugPoint("setup"); - } - - local.params = $paramParser(argumentCollection = arguments); - - // Set params in the request scope as well so we can display it in the debug info outside of the controller context. - request.wheels.params = local.params; - - if ($get("showDebugInformation")) { - $debugPoint("setup"); - } - - // Hi-jack any wheels controller requests for GUI - if (ListFirst(local.params.controller, '.') EQ "wheels") { - if (!application.wheels.enablePublicComponent) { - // Hard abort if GUI turned off - cfabort; - } else { - // BoxLang compatibility: Check for null action parameter - if (IsNull(local.params.action) || !Len(local.params.action)) { - throw( - type="Wheels.ActionParameterMissing", - message="The action parameter is missing or null. Controller: #local.params.controller#"); - } - - if (structKeyExists(server, "boxlang")) { - local.method = application.wheels.public[local.params.action]; - local.method(); - } else { - // Debug log the controller and action being called - writeLog(file="application", text="Wheels dispatch - controller: #local.params.controller#, action: #local.params.action#"); - - // Call the action method directly on the component to preserve context - // Use 'object' and 'methodname' for older Adobe CF versions compatibility - invoke(object=application.wheels.public, methodname=local.params.action); - } - // The wheels controller methods handle their own output and abort - // So we need to ensure we don't continue processing - return ""; - } - } else { - // Create the requested controller and call the action on it. - local.controller = controller(name = local.params.controller, params = local.params); - local.controller.processAction(); - - // If there is a delayed redirect pending we execute it here thus halting the rest of the request. - if (local.controller.$performedRedirect()) { - $location(argumentCollection = local.controller.getRedirect()); - } - - // Clear out the flash (note that this is not done for redirects since the processing does not get here). - local.controller.$flashClear(); - - return local.controller.response(); - } - } - - /** - * Find the route that matches the path, create params struct and return it. - */ - public struct function $paramParser( - string pathInfo = request.cgi.path_info, - string scriptName = request.cgi.script_name, - struct formScope = form, - struct urlScope = url - ) { - local.path = $getPathFromRequest(pathInfo = arguments.pathInfo, scriptName = arguments.scriptName); - local.route = $findMatchingRoute(path = local.path); - return $createParams( - path = local.path, - route = local.route, - formScope = arguments.formScope, - urlScope = arguments.urlScope - ); - } - - /** - * Merges the URL and form scope into a single structure, URL scope has precedence. - */ - public struct function $mergeUrlAndFormScopes( - required struct params, - required struct urlScope, - required struct formScope - ) { - StructAppend(arguments.params, arguments.formScope); - StructAppend(arguments.params, arguments.urlScope); - - // Get rid of the unnecessary "fieldnames" key that ACF always adds to the form scope. - StructDelete(arguments.params, "fieldnames"); - - return arguments.params; - } - - /** - * If content type is JSON, deserialize it into a struct and add to the params struct. - */ - public struct function $parseJsonBody( - required struct params, - struct httpRequestData = GetHTTPRequestData() - ) { - local.headers = request.wheels.httpRequestData.headers; - local.content = request.wheels.httpRequestData.content; - if (StructKeyExists(local.headers, "Content-Type")) { - // Content-Type may also include charset so we need only check the first item in the list - local.type = SpanExcluding(local.headers["Content-Type"], ";"); - - // Only proceed if the content type is JSON. - // Allow multiple JSON content types by checking the start and end of the string. - // This way we allow both "application/json" and "application/vnd.api+json" (JSON API) for example. - if (Left(local.type, 12) == "application/" && Right(local.type, 4) == "json") { - // On ACF we need to convert from binary to a string before we can work with it. - if (IsBinary(local.content)) { - local.content = ToString(local.content); - } - - // If what we have now is valid JSON, deserialize it to a struct and append to params. - // Call with "false" so existing form and URL values take precedence. - if (IsJSON(local.content)) { - local.deserializedContent = DeserializeJSON(local.content); - if (IsStruct(local.deserializedContent)) { - StructAppend(arguments.params, local.deserializedContent, false); - } - // If the incoming root element is an array, add it to params in the _json key - // This appears to follow Rails conventions - if (IsArray(local.deserializedContent)) { - arguments.params['_json'] = local.deserializedContent; - } - } - } - } - return arguments.params; - } - - /** - * Parses the route pattern, identifies the variable markers within the pattern and assigns the value from the url variables with the path. - */ - public struct function $mergeRoutePattern(required struct params, required struct route, required string path) { - local.rv = arguments.params; - local.matches = ReFindNoCase(arguments.route.regex, arguments.path, 1, true); - local.iEnd = ArrayLen(local.matches.pos); - for (local.i = 2; local.i <= local.iEnd; local.i++) { - local.key = ListGetAt(arguments.route.foundVariables, local.i - 1); - local.rv[local.key] = Mid(arguments.path, local.matches.pos[local.i], local.matches.len[local.i]); - } - return local.rv; - } - - /** - * Loops through the params struct passed in and attempts to deobfuscate it. - * Ignores the controller and action params values. - */ - public struct function $deobfuscateParams(required struct params) { - local.rv = arguments.params; - if ($get("obfuscateUrls")) { - for (local.key in local.rv) { - if (local.key != "controller" && local.key != "action") { - try { - local.rv[local.key] = deobfuscateParam(local.rv[local.key]); - } catch (any e) { - } - } - } - } - return local.rv; - } - - /** - * Loops through the params struct and handle the cases where checkboxes are unchecked. - */ - public struct function $translateBlankCheckBoxSubmissions(required struct params) { - local.rv = arguments.params; - for (local.key in local.rv) { - if (FindNoCase("($checkbox)", local.key)) { - // If no other form parameter exists with this name it means that the checkbox was left blank. - // Therefore we force the value to the unchecked value for the checkbox. - // This gets around the problem that unchecked checkboxes don't post at all. - local.formParamName = ReplaceNoCase(local.key, "($checkbox)", ""); - if (!StructKeyExists(local.rv, local.formParamName)) { - local.rv[local.formParamName] = local.rv[local.key]; - } - - StructDelete(local.rv, local.key); - } - } - return local.rv; - } - - /** - * Combines date parts into a single value. - */ - public struct function $translateDatePartSubmissions(required struct params) { - local.rv = arguments.params; - local.dates = {}; - for (local.key in local.rv) { - if (ReFindNoCase(".*\((\$year|\$month|\$day|\$hour|\$minute|\$second|\$ampm)\)$", local.key)) { - local.temp = ListToArray(local.key, "("); - local.firstKey = local.temp[1]; - local.secondKey = SpanExcluding(local.temp[2], ")"); - if (!StructKeyExists(local.dates, local.firstKey)) { - local.dates[local.firstKey] = {}; - } - local.dates[local.firstKey][ReplaceNoCase(local.secondKey, "$", "")] = local.rv[local.key]; - } - } - for (local.key in local.dates) { - if (!StructKeyExists(local.dates[local.key], "year")) { - local.dates[local.key].year = 1899; - } - if (!StructKeyExists(local.dates[local.key], "month")) { - local.dates[local.key].month = 1; - } - if (!StructKeyExists(local.dates[local.key], "day")) { - local.dates[local.key].day = 1; - } - if (!StructKeyExists(local.dates[local.key], "hour")) { - local.dates[local.key].hour = 0; - } - if (!StructKeyExists(local.dates[local.key], "minute")) { - local.dates[local.key].minute = 0; - } - if (!StructKeyExists(local.dates[local.key], "second")) { - local.dates[local.key].second = 0; - } - if (StructKeyExists(local.dates[local.key], "ampm")) { - if (local.dates[local.key].ampm == "am" && local.dates[local.key].hour == 12) { - local.dates[local.key].hour = 0; - } else if (local.dates[local.key].ampm == "pm" && local.dates[local.key].hour != 12) { - local.dates[local.key].hour += 12; - } - } - try { - local.rv[local.key] = CreateDateTime( - local.dates[local.key].year, - local.dates[local.key].month, - local.dates[local.key].day, - local.dates[local.key].hour, - local.dates[local.key].minute, - local.dates[local.key].second - ); - } catch (any e) { - local.rv[local.key] = ""; - } - StructDelete(local.rv, local.key & "($year)"); - StructDelete(local.rv, local.key & "($month)"); - StructDelete(local.rv, local.key & "($day)"); - StructDelete(local.rv, local.key & "($hour)"); - StructDelete(local.rv, local.key & "($minute)"); - StructDelete(local.rv, local.key & "($second)"); - } - return local.rv; - } - - /** - * Ensure that the controller and action params exist and are camelized. - */ - public struct function $ensureControllerAndAction(required struct params, required struct route) { - local.rv = arguments.params; - if (!StructKeyExists(local.rv, "controller")) { - local.rv.controller = arguments.route.controller; - } - if (!StructKeyExists(local.rv, "action")) { - local.rv.action = arguments.route.action; - } - - // We now need to have dot notation allowed in the controller hence the \. - local.rv.controller = ReReplace(local.rv.controller, "[^0-9A-Za-z-_\.]", "", "all"); - - // Filter out illegal characters from the controller and action arguments. - // Debug log before filtering - if (local.rv.controller == "wheels.public" && local.rv.action == "tests_testbox") { - writeLog(file="application", text="Before filter - action: #local.rv.action#"); - } - local.rv.action = ReReplace(local.rv.action, "[^0-9A-Za-z-_\.]", "", "all"); - - // Convert controller to upperCamelCase. - // BoxLang compatibility: Handle consecutive dots differently - if (StructKeyExists(server, "boxlang")) { - // For BoxLang, manually handle the leading dots issue - local.dotPrefix = ""; - local.cleanName = local.rv.controller; - - while (Left(local.cleanName, 1) == ".") { - local.dotPrefix &= "."; - local.cleanName = Right(local.cleanName, Len(local.cleanName) - 1); - } - - local.cleanName = ReReplace(local.cleanName, "(^|-)([a-z])", "\u\2", "all"); - local.rv.controller = local.dotPrefix & local.cleanName; - } else { - // Standard behavior for Lucee/Adobe CF - local.cName = ListLast(local.rv.controller, "."); - local.cName = ReReplace(local.cName, "(^|-)([a-z])", "\u\2", "all"); - local.cLen = ListLen(local.rv.controller, "."); - if (local.cLen) { - local.rv.controller = ListSetAt(local.rv.controller, local.cLen, local.cName, "."); - } - } - - // Action to normal camelCase. - local.rv.action = ReReplace(local.rv.action, "-([a-z])", "\u\1", "all"); - - // Debug log final values - if (local.rv.controller == "wheels.public") { - writeLog(file="application", text="Final dispatch params - controller: #local.rv.controller#, action: #local.rv.action#"); - } - - return local.rv; - } - - /** - * Adds in the format variable from the route if it exists. - */ - public struct function $addRouteFormat(required struct params, required struct route) { - local.rv = arguments.params; - if (StructKeyExists(arguments.route, "formatVariable") && StructKeyExists(arguments.route, "format")) { - local.rv[arguments.route.formatVariable] = arguments.route.format; - } - return local.rv; - } - - /** - * Adds in the name variable from the route if it exists. - */ - public struct function $addRouteName(required struct params, required struct route) { - local.rv = arguments.params; - if (StructKeyExists(arguments.route, "name") && Len(arguments.route.name) && !StructKeyExists(local.rv, "route")) { - local.rv.route = arguments.route.name; - } - return local.rv; - } - - /** - * Determine HTTP verb used in request. - */ - public string function $getRequestMethod() { - // If request is a post, check for alternate verb. - if (request.cgi.request_method == "post" && StructKeyExists(form, "_method")) { - return form["_method"]; - } - - return request.cgi.request_method; - } - - function onDIComplete(){ - if (structKeyExists(server, "boxlang")) { - variables.this = this; - } - Mixins.$initializeMixins(variables); - } -} diff --git a/core/src/wheels/Job.cfc b/core/src/wheels/Job.cfc deleted file mode 100644 index ff1e14b82b..0000000000 --- a/core/src/wheels/Job.cfc +++ /dev/null @@ -1,66 +0,0 @@ -/** - * Base Job class for Wheels framework - * Provides common functionality for background jobs - */ -component { - - /** - * Constructor - */ - public function init() { - return this; - } - - /** - * Main job execution method to be overridden by subclasses - * @data Job data/parameters - */ - public void function perform(struct data = {}) { - throw(type="Wheels.NotImplemented", message="The perform() method must be implemented in the job subclass"); - } - - /** - * Enqueue job for processing - * @jobName Name of the job - * @data Job data - * @queue Queue name - * @priority Job priority - */ - public void function enqueue( - required string jobName, - struct data = {}, - string queue = "default", - string priority = "normal" - ) { - // Placeholder for queue implementation - // In a real implementation, this would add the job to a queue system - writeLog( - text="Job '#arguments.jobName#' enqueued to queue '#arguments.queue#' with priority '#arguments.priority#'", - type="information", - file="jobs" - ); - } - - /** - * Enqueue job for specific time - * @jobName Name of the job - * @data Job data - * @queue Queue name - * @priority Job priority - * @runAt When to run the job - */ - public void function enqueueAt( - required string jobName, - struct data = {}, - string queue = "default", - string priority = "normal", - required date runAt - ) { - // Placeholder for scheduled queue implementation - writeLog( - text="Job '#arguments.jobName#' scheduled for #dateTimeFormat(arguments.runAt, 'yyyy-mm-dd HH:nn:ss')#", - type="information", - file="jobs" - ); - } -} \ No newline at end of file diff --git a/core/src/wheels/Model.cfc b/core/src/wheels/Model.cfc deleted file mode 100644 index c4f09fe897..0000000000 --- a/core/src/wheels/Model.cfc +++ /dev/null @@ -1,624 +0,0 @@ -component output="false" displayName="Model" extends="wheels.Global"{ - - property name="Mixins" inject="id:Plugins"; - - function init(){ - $integrateComponents("wheels.model"); - return this; - } - - /** - * Internal function. - */ - public any function $initModelClass(required string name, required string path) { - variables.wheels = {}; - variables.wheels.errors = []; - variables.wheels.class = {}; - variables.wheels.class.modelName = arguments.name; - variables.wheels.class.modelId = Hash(GetMetadata(this).name); - variables.wheels.class.path = arguments.path; - - // If our name has pathing in it, remove it and add it to the end of of the $class.path variable. - if (Find("/", arguments.name)) { - variables.wheels.class.modelName = ListLast(arguments.name, "/"); - variables.wheels.class.path = ListAppend( - arguments.path, - ListDeleteAt(arguments.name, ListLen(arguments.name, "/"), "/"), - "/" - ); - } - - variables.wheels.class.RESQLAs = "[[:space:]]AS[[:space:]][A-Za-z1-9]+"; - variables.wheels.class.RESQLOperators = "((?:\s+(?:NOT\s+)?LIKE)|(?:\s+(?:NOT\s+)?IN)|(?:\s+IS(?:\s+NOT)?)|(?:<>)|(?:<=)|(?:>=)|(?:!=)|(?:!<)|(?:!>)|=|<|>)"; - variables.wheels.class.RESQLWhere = "\s*(#variables.wheels.class.RESQLOperators#)\s*(\('.+?'\)|\(((?:\+|-)?[0-9\.],?)+\)|'.+?'()|''|((?:\+|-)?[0-9\.]+)()|NULL)((\s*$|\s*\)|\s+(AND|OR)))"; - variables.wheels.class.mapping = {}; - variables.wheels.class.properties = {}; - variables.wheels.class.accessibleProperties = {}; - variables.wheels.class.calculatedProperties = {}; - variables.wheels.class.ignoredColumns = {}; - variables.wheels.class.associations = {}; - variables.wheels.class.callbacks = {}; - variables.wheels.class.keys = ""; - variables.wheels.class.dataSource = application.wheels.dataSourceName; - variables.wheels.class.username = application.wheels.dataSourceUserName; - variables.wheels.class.password = application.wheels.dataSourcePassword; - variables.wheels.class.automaticValidations = application.wheels.automaticValidations; - setTableNamePrefix($get("tableNamePrefix")); - table(LCase(pluralize(variables.wheels.class.modelName))); - local.callbacks = "afterNew,afterFind,afterInitialization,beforeDelete,afterDelete,beforeSave,afterSave,beforeCreate,afterCreate,beforeUpdate,afterUpdate,beforeValidation,afterValidation,beforeValidationOnCreate,afterValidationOnCreate,beforeValidationOnUpdate,afterValidationOnUpdate"; - local.callbacksArray = ListToArray(local.callbacks); - local.iEnd = ArrayLen(local.callbacksArray); - for (local.i = 1; local.i <= local.iEnd; local.i++) { - variables.wheels.class.callbacks[local.callbacksArray[local.i]] = []; - } - local.validations = "onSave,onCreate,onUpdate"; - local.validationsArray = ListToArray(local.validations); - local.iEnd = ArrayLen(local.validationsArray); - for (local.i = 1; local.i <= local.iEnd; local.i++) { - variables.wheels.class.validations[local.validationsArray[local.i]] = []; - } - - variables.wheels.class.propertyStruct = StructNew("ordered"); - variables.wheels.class.columnStruct = StructNew("ordered"); - - // TODO: deprecate these lists in favour of the structs to avoid ListFind (use StructKeyList to create the list) - variables.wheels.class.propertyList = ""; - variables.wheels.class.aliasedPropertyList = ""; - variables.wheels.class.columnList = ""; - variables.wheels.class.calculatedPropertyList = ""; - - // Run developer's config method if it exists. - if (StructKeyExists(variables, "config")) { - config(); - } else if ($get("modelRequireConfig")) { - Throw( - type = "Wheels.ModelConfigMissing", - message = "A ยดconfigยด function is required for ยด#variables.wheels.class.modelName#ยด model.", - extendedInfo = "Create a ยดconfigยด function in ยด/models/#variables.wheels.class.modelName#ยด." - ); - } - - // set calculated properties - for (local.key in variables.wheels.class.mapping) { - if ( - StructKeyExists(variables.wheels.class.mapping[local.key], "type") - && variables.wheels.class.mapping[local.key].type != "column" - ) { - // TODO: deprecate (use StructKeyList of calculatedPropertyStruct) - variables.wheels.class.calculatedPropertyList = ListAppend( - variables.wheels.class.calculatedPropertyList, - local.key - ); - variables.wheels.class.calculatedProperties[local.key] = {}; - variables.wheels.class.calculatedProperties[local.key][variables.wheels.class.mapping[local.key].type] = variables.wheels.class.mapping[ - local.key - ].value; - variables.wheels.class.calculatedProperties[local.key].select = variables.wheels.class.mapping[local.key].select; - variables.wheels.class.calculatedProperties[local.key].dataType = variables.wheels.class.mapping[local.key].dataType; - } - } - - // Make sure that the tablename has the respected prefix. - table(getTableNamePrefix() & tableName()); - - if (!IsBoolean(variables.wheels.class.tableName) || variables.wheels.class.tableName) { - // load the database adapter - variables.wheels.class.adapter = $assignAdapter(); - - // get columns for the table - local.columns = variables.wheels.class.adapter.$getColumns(tableName()).filter(function(r) { - return !StructKeyExists(variables.wheels.class.ignoredColumns, arguments.r.column_name); - }); - - // do not process columns already assigned to a calculated property - local.processedColumns = {}; - for (local.key in StructKeyArray(variables.wheels.class.calculatedProperties)) { - local.processedColumns[local.key] = true; - } - - local.iEnd = local.columns.recordCount; - for (local.i = 1; local.i <= local.iEnd; local.i++) { - // set up properties and column mapping - local.columnName = lCase(local.columns["column_name"][local.i]); - - if (!StructKeyExists(local.processedColumns, local.columnName)) { - // default the column to map to a property with the same name - local.property = local.columnName; - for (local.key in variables.wheels.class.mapping) { - if ( - StructKeyExists(variables.wheels.class.mapping[local.key], "type") - && variables.wheels.class.mapping[local.key].type == "column" - && variables.wheels.class.mapping[local.key].value == local.property - ) { - // developer has chosen to map this column to a property with a different name so set that here - local.property = local.key; - break; - } - } - - // Extract type and details, like if it's signed or not, from the "type_name"" information we got from cfdbinfo. - // It can be "int" or "int unsigned" for example (in which case we set type to "int" and details to "unsigned"). - // Done below by treating the value as a space delimited list. - // We also ignore anything inside parentheses. - local.typeName = Trim(SpanExcluding(local.columns["type_name"][local.i], "(")); - if (ListLen(local.typeName, " ") == 2) { - local.type = ListFirst(local.typeName, " "); - local.details = ListLast(local.typeName, " "); - } else { - local.type = local.typeName; - local.details = ""; - } - - // set the info we need for each property - variables.wheels.class.properties[local.property] = {}; - variables.wheels.class.properties[local.property].dataType = local.type; - variables.wheels.class.properties[local.property].type = variables.wheels.class.adapter.$getType( - local.type, - local.columns["decimal_digits"][local.i], - local.details - ); - variables.wheels.class.properties[local.property].column = local.columnName; - variables.wheels.class.properties[local.property].scale = local.columns["decimal_digits"][local.i]; - // BoxLang compatibility - handle different column names from dbinfo - variables.wheels.class.properties[local.property].columnDefault = $getColumnDefaultValue(local.columns, local.i); - - // get a boolean value for whether this column can be set to null or not - // if we don't get a boolean back we try to translate y/n to proper boolean values in cfml (yes/no) - variables.wheels.class.properties[local.property].nullable = Trim(local.columns["is_nullable"][local.i]); - if (!IsBoolean(variables.wheels.class.properties[local.property].nullable)) { - variables.wheels.class.properties[local.property].nullable = ReplaceList( - variables.wheels.class.properties[local.property].nullable, - "N,Y", - "No,Yes" - ); - } - - variables.wheels.class.properties[local.property].size = local.columns["column_size"][local.i]; - - // If property is id, then make it all-caps "ID." - if (local.property == "id") { - variables.wheels.class.properties[local.property].label = "ID"; - // Otherwise, humanize it. - } else { - variables.wheels.class.properties[local.property].label = humanize(local.property); - } - // Detect datetime-like columns for SQLite, without changing the DB type - if ( - variables.wheels.class.properties[local.property].datatype eq "TEXT" - && variables.wheels.class.properties[local.property].type eq "cf_sql_varchar" - && ReFindNoCase("\b(date|time|dob|birthday|birthTime|created|updated)\b", variables.wheels.class.properties[local.property].column) - && get("adapterName") eq "SQLiteModel" - ) { - // Override only validation type - variables.wheels.class.properties[local.property].validationtype = "datetime"; - } else { - // Default logic - variables.wheels.class.properties[local.property].validationtype = variables.wheels.class.adapter.$getValidationType( - variables.wheels.class.properties[local.property].type - ); - } - - if (StructKeyExists(variables.wheels.class.mapping, local.property)) { - if (StructKeyExists(variables.wheels.class.mapping[local.property], "label")) { - variables.wheels.class.properties[local.property].label = variables.wheels.class.mapping[local.property].label; - } - if (StructKeyExists(variables.wheels.class.mapping[local.property], "defaultValue")) { - variables.wheels.class.properties[local.property].defaultValue = variables.wheels.class.mapping[ - local.property - ].defaultValue; - } - } - if (local.columns["is_primarykey"][local.i]) { - setPrimaryKey(local.property); - } - if ( - variables.wheels.class.automaticValidations && !ListFindNoCase( - "#application.wheels.timeStampOnCreateProperty#,#application.wheels.timeStampOnUpdateProperty#,#application.wheels.softDeleteProperty#", - local.property - ) - ) { - // check if automatic validations have been turned off specifically for this property before proceeding - local.propertyAllowsAutomaticValidations = true; - if ( - StructKeyExists(variables.wheels.class.mapping, local.property) - && StructKeyExists(variables.wheels.class.mapping[local.property], "automaticValidations") - && !variables.wheels.class.mapping[local.property].automaticValidations - ) { - local.propertyAllowsAutomaticValidations = false; - } - - if (local.propertyAllowsAutomaticValidations) { - local.defaultValidationsAllowBlank = variables.wheels.class.properties[local.property].nullable; - - // primary keys should be allowed to be blank - if (ListFindNoCase(primaryKeys(), local.property)) { - local.defaultValidationsAllowBlank = true; - } - if ( - !ListFindNoCase(primaryKeys(), local.property) - && !variables.wheels.class.properties[local.property].nullable - && !$validationExists(property = local.property, validation = "validatesPresenceOf") - ) { - if (Len(variables.wheels.class.properties[local.property].columnDefault)) { - validatesPresenceOf(properties = local.property, when = "onUpdate"); - } else { - validatesPresenceOf(properties = local.property); - } - } - - // always allow blank if a database default or validatesPresenceOf() has been set - if ( - Len(variables.wheels.class.properties[local.property].columnDefault) - || $validationExists(property = local.property, validation = "validatesPresenceOf") - ) { - local.defaultValidationsAllowBlank = true; - } - - // set length validations if the developer has not - if ( - variables.wheels.class.properties[local.property].validationtype == "string" - && !$validationExists(property = local.property, validation = "validatesLengthOf") - ) { - validatesLengthOf( - properties = local.property, - allowBlank = local.defaultValidationsAllowBlank, - maximum = variables.wheels.class.properties[local.property].size - ); - } - - // set numericality validations if the developer has not - if ( - ListFindNoCase("integer,float", variables.wheels.class.properties[local.property].validationtype) - && !$validationExists(property = local.property, validation = "validatesNumericalityOf") - ) { - validatesNumericalityOf( - properties = local.property, - allowBlank = local.defaultValidationsAllowBlank, - onlyInteger = (variables.wheels.class.properties[local.property].validationtype == "integer") - ); - } - - // set date validations if the developer has not (checks both dates or times as per the IsDate() function) - if ( - variables.wheels.class.properties[local.property].validationtype == "datetime" - && !$validationExists(property = local.property, validation = "validatesFormatOf") - ) { - validatesFormatOf( - properties = local.property, - allowBlank = local.defaultValidationsAllowBlank, - type = "date" - ); - } - } - } - - variables.wheels.class.propertyStruct[local.property] = true; - variables.wheels.class.columnStruct[variables.wheels.class.properties[local.property].column] = true; - - variables.wheels.class.propertyList = ListAppend(variables.wheels.class.propertyList, local.property); - - /* - To fix the issue below: - https://github.com/wheels-dev/wheels/issues/580 - - Added a new property called aliasedPropertyList in model class that will contain column names list that are prepended with the tablename. - For example, if there is a "user" table then the columns "id,createdat,updatedat,deletedat" will be added in the list with "user" prepended to it. - - Then the list will contain, userid,usercreatedat,userupdatedat,userdeletedat. - */ - variables.wheels.class.aliasedPropertyList = ListAppend(variables.wheels.class.aliasedPropertyList, variables.wheels.class.modelname & local.property); - variables.wheels.class.columnList = ListAppend( - variables.wheels.class.columnList, - variables.wheels.class.properties[local.property].column - ); - local.processedColumns[local.columnName] = true; - } - } - - // Raise error when no primary key has been defined for the table. - if (!Len(primaryKeys())) { - Throw( - type = "Wheels.NoPrimaryKey", - message = "No primary key exists on the `#tableName()#` table.", - extendedInfo = "Set an appropriate primary key on the `#tableName()#` table." - ); - } - } - - // set up soft deletion and time stamping if the necessary columns in the table exist - variables.wheels.class.timeStampMode = application.wheels.timeStampMode; - if ( - Len(application.wheels.softDeleteProperty) - && StructKeyExists(variables.wheels.class.properties, application.wheels.softDeleteProperty) - ) { - variables.wheels.class.softDeletion = true; - variables.wheels.class.softDeleteColumn = variables.wheels.class.properties[application.wheels.softDeleteProperty].column; - } else { - variables.wheels.class.softDeletion = false; - } - if ( - Len(application.wheels.timeStampOnCreateProperty) - && StructKeyExists(variables.wheels.class.properties, application.wheels.timeStampOnCreateProperty) - ) { - variables.wheels.class.timeStampingOnCreate = true; - variables.wheels.class.timeStampOnCreateProperty = application.wheels.timeStampOnCreateProperty; - } else { - variables.wheels.class.timeStampingOnCreate = false; - } - if ( - Len(application.wheels.timeStampOnUpdateProperty) - && StructKeyExists(variables.wheels.class.properties, application.wheels.timeStampOnUpdateProperty) - ) { - variables.wheels.class.timeStampingOnUpdate = true; - variables.wheels.class.timeStampOnUpdateProperty = application.wheels.timeStampOnUpdateProperty; - } else { - variables.wheels.class.timeStampingOnUpdate = false; - } - return this; - } - - /** - * Internal function. - */ - public any function $assignAdapter() { - if ($get("showErrorInformation")) { - try { - local.info = $dbinfo( - type = "version", - dataSource = variables.wheels.class.dataSource, - username = variables.wheels.class.username, - password = variables.wheels.class.password - ); - } catch (any e) { - Throw( - type = "Wheels.DataSourceNotFound", - message = "The data source could not be reached.", - extendedInfo = "Make sure your database is reachable and that your data source settings are correct. You either need to setup a data source with the name `#variables.wheels.class.dataSource#` in the Administrator or tell Wheels to use a different data source in `config/settings.cfm`." - ); - } - } else { - local.info = $dbinfo( - type = "version", - dataSource = variables.wheels.class.dataSource, - username = variables.wheels.class.username, - password = variables.wheels.class.password - ); - } - if (FindNoCase("SQLServer", local.info.driver_name) || FindNoCase("SQL Server", local.info.driver_name)) { - local.adapterNamespace = "MicrosoftSQLServer"; - local.adapterName = "MicrosoftSQLServerModel"; - } else if (FindNoCase("MySQL", local.info.driver_name) || FindNoCase("MariaDB", local.info.driver_name)) { - local.adapterNamespace = "MySQL"; - local.adapterName = "MySQLModel"; - } else if (FindNoCase("PostgreSQL", local.info.driver_name)) { - local.adapterNamespace = "PostgreSQL"; - local.adapterName = "PostgreSQLModel"; - } else if (FindNoCase("H2", local.info.driver_name)) { - local.adapterNamespace = "H2"; - local.adapterName = "H2Model"; - } else if (FindNoCase("Oracle", local.info.driver_name)) { - local.adapterNamespace = "Oracle"; - local.adapterName = "OracleModel"; - } else if (FindNoCase("SQLite", local.info.driver_name)) { - local.adapterNamespace = "SQLite"; - local.adapterName = "SQLiteModel"; - } else { - Throw( - type = "Wheels.DatabaseNotSupported", - message = "#local.info.database_productname# is not supported by Wheels.", - extendedInfo = "Use SQL Server, MySQL, MariaDB, PostgreSQL, Oracle, SQLite or H2." - ); - } - $set(adapterName = local.adapterName); - return CreateObject("component", "wheels.databaseAdapters.#local.adapterNamespace#.#local.adapterName#").$init( - dataSource = variables.wheels.class.dataSource, - username = variables.wheels.class.username, - password = variables.wheels.class.password - ); - } - - /** - * Internal function. - */ - public any function $initModelObject( - required string name, - required any properties, - required boolean persisted, - numeric row = 1, - boolean base = true, - boolean useFilterLists = true - ) { - variables.wheels = {}; - variables.wheels.instance = {}; - variables.wheels.errors = []; - - // assign an object id for the instance (only use the last 12 digits to avoid creating an exponent) - request.wheels.tickCountId = Right(request.wheels.tickCountId, 12) + 1; - variables.wheels.tickCountId = request.wheels.tickCountId; - - // Only do work if we havenโ€™t already loaded the class data for this request - if (!StructKeyExists(variables.wheels, "class")) { - // Build a unique lock name per application - local.lockName = "classLock" & application.applicationName; - - if ( !structKeyExists( application.wheels.models, arguments.name ) ) { - try { - // Slow path: try to load the model into the application cache - model( arguments.name ); - local.modelObj = application.wheels.models[ arguments.name ]; - } - catch ( any e ) { - throw( - type = "Wheels.ModelInitializationFailed", - message = "Failed to initialize model '#arguments.name#'.", - extendedInfo = "Error details: " & e.message - ); - } - } - - // Attempt to grab the alreadyโ€loaded model object, or forceโ€load it - if ( structKeyExists( application.wheels.models, arguments.name ) ) { - // Fast path: model is already in the application cache - local.modelObj = application.wheels.models[ arguments.name ]; - - // At this point, local.modelObj is guaranteed to exist - variables.wheels.class = $simpleLock( - execute = "$classData", - name = local.lockName, - object = local.modelObj, - type = "readOnly" - ); - } - } - - // setup object properties in the this scope - if (IsQuery(arguments.properties) && arguments.properties.recordCount != 0) { - arguments.properties = $queryRowToStruct(argumentCollection = arguments); - } - if (IsStruct(arguments.properties) && !StructIsEmpty(arguments.properties)) { - $setProperties(properties = arguments.properties, setOnModel = true, $useFilterLists = arguments.useFilterLists); - } - if (arguments.persisted) { - $updatePersistedProperties(); - } - variables.wheels.instance.persistedOnInitialization = arguments.persisted; - return this; - } - - /** - * Internal function. - */ - public struct function $classData() { - return variables.wheels.class; - } - - - /** - * Internal function. - */ - public boolean function $softDeletion() { - return variables.wheels.class.softDeletion; - } - - /** - * Internal function. - */ - public string function $softDeleteColumn() { - return variables.wheels.class.softDeleteColumn; - } - - /** - * Gets all the component files from the provided path - * - * @path The path to get component files from - */ - private function $integrateComponents(required string path) { - local.basePath = arguments.path; - local.folderPath = expandPath("/#replace(local.basePath, ".", "/", "all")#"); - - // Get a list of all CFC files in the folder - local.fileList = directoryList(local.folderPath, false, "name", "*.cfc"); - for (local.fileName in local.fileList) { - // Remove the file extension to get the component name - local.componentName = replace(local.fileName, ".cfc", "", "all"); - - $integrateFunctions(createObject("component", "#local.basePath#.#local.componentName#")); - } - } - - /** - * Dynamically mix methods from a given component into this component - */ - private function $integrateFunctions(componentInstance) { - // Get all methods from the given component - local.methods = getMetaData(componentInstance).functions; - - for (local.method in local.methods) { - local.functionName = local.method.name; - - // Only add public, non-inherited methods - if (local.method.access eq "public") { - local.methodExists = structKeyExists(variables, local.method.name) || structKeyExists(this, local.method.name); - - if (!local.methodExists) { - variables[local.functionName] = componentInstance[local.functionName]; - this[local.functionName] = componentInstance[local.functionName]; - } else { - local.superMethodName = "super" & local.functionName; - variables[local.superMethodName] = componentInstance[local.functionName]; - this[local.superMethodName] = componentInstance[local.functionName]; - } - - // Only add super prefix for functions that will be overridden by plugins/mixins - if ($willBeOverriddenByMixin(local.functionName)) { - local.superMethodName = "super" & local.functionName; - variables[local.superMethodName] = componentInstance[local.functionName]; - this[local.superMethodName] = componentInstance[local.functionName]; - } - } - } - } - - /** - * Check if a function will be overridden by a plugin/mixin - */ - private boolean function $willBeOverriddenByMixin(required string functionName) { - // Check if application and mixins are available - if (!IsDefined("application") || !StructKeyExists(application, "wheels") || !StructKeyExists(application.wheels, "mixins")) { - return false; - } - - // Check for both "model" and "global" mixins - local.componentTypes = ["model", "global"]; - - for (local.componentType in local.componentTypes) { - if (StructKeyExists(application.wheels.mixins, local.componentType) && - StructKeyExists(application.wheels.mixins[local.componentType], arguments.functionName)) { - return true; - } - } - - return false; - } - - /** - * Helper function to get column default value with BoxLang compatibility - * Different CFML engines return different column names for default values - */ - private string function $getColumnDefaultValue(required query columns, required numeric index) { - local.rv = ""; - - // Try different column names used by different CFML engines - if (ListFindNoCase(arguments.columns.columnList, "column_default_value")) { - local.rv = arguments.columns["column_default_value"][arguments.index]; - } else if (ListFindNoCase(arguments.columns.columnList, "column_default")) { - local.rv = arguments.columns["column_default"][arguments.index]; - } else if (ListFindNoCase(arguments.columns.columnList, "default_value")) { - local.rv = arguments.columns["default_value"][arguments.index]; - } else if (ListFindNoCase(arguments.columns.columnList, "COLUMN_DEF")) { - // Standard JDBC column name used by BoxLang - local.rv = arguments.columns["COLUMN_DEF"][arguments.index]; - } - - if (IsArray(local.rv)) { - if (ArrayLen(local.rv) > 0) { - local.rv = local.rv[1]; - } else { - return ""; - } - } - - if (IsSimpleValue(local.rv)) { - return Trim(ToString(local.rv)); - } else { - return ""; - } - } - - function onDIcomplete(){ - if (structKeyExists(server, "boxlang")) { - variables.this = this; - } - Mixins.$initializeMixins(variables); - } -} diff --git a/core/src/wheels/Plugins.cfc b/core/src/wheels/Plugins.cfc deleted file mode 100644 index 7b73ca6c24..0000000000 --- a/core/src/wheels/Plugins.cfc +++ /dev/null @@ -1,420 +0,0 @@ -component output="false" extends="wheels.Global"{ - - public any function $init( - required string pluginPath, - boolean deletePluginDirectories = application.wheels.deletePluginDirectories, - boolean overwritePlugins = application.wheels.overwritePlugins, - boolean loadIncompatiblePlugins = application.wheels.loadIncompatiblePlugins, - string wheelsEnvironment = application.wheels.environment, - string wheelsVersion = application.wheels.version - ) { - variables.$class = {}; - variables.$class.plugins = {}; - variables.$class.pluginMeta = {}; - variables.$class.mixins = {}; - variables.$class.mixableComponents = "application,dispatch,controller,mapper,model,base,sqlserver,mysql,postgresql,h2,test"; - variables.$class.incompatiblePlugins = ""; - variables.$class.dependantPlugins = ""; - StructAppend(variables.$class, arguments); - /* handle pathing for different operating systems */ - variables.$class.pluginPathFull = ReplaceNoCase(ExpandPath(variables.$class.pluginPath), "\", "/", "all"); - /* sort direction */ - variables.sort = "ASC"; - /* extract out plugins */ - $pluginsExtract(); - /* remove orphan plugin directories */ - if (variables.$class.deletePluginDirectories) { - $pluginDelete(); - } - /* process plugins */ - $pluginsProcess(); - /* get versions */ - $pluginMetaData(); - /* process mixins */ - $processMixins(); - /* dependencies */ - $determineDependency(); - return this; - } - - public struct function $pluginFolders() { - local.plugins = {}; - local.folders = $folders(); - // Within plugin folders, grab info about each plugin and package up into a struct. - for (local.i = 1; i <= local.folders.recordCount; i++) { - // For *nix, we need a case-sensitive name for the plugin component, so we must reference its CFC file name. - local.subfolder = DirectoryList("#local.folders["directory"][i]#/#local.folders["name"][i]#", false, "query"); - local.pluginCfc = $query( - dbtype = "query", - query = local.subfolder, - sql = "SELECT name FROM query WHERE LOWER(name) = '#LCase(local.folders["name"][i])#.cfc'" - ); - local.temp = {}; - if (structKeyExists(server, "boxlang")) { - // BoxLang compatibility: Handle case where query returns no results - if (local.pluginCfc.recordCount > 0) { - local.temp.name = Replace(local.pluginCfc.name, ".cfc", ""); - } else { - local.cfcFiles = $query( - dbtype = "query", - query = local.subfolder, - sql = "SELECT name FROM query WHERE LOWER(name) LIKE '%.cfc' ORDER BY name" - ); - if (local.cfcFiles.recordCount > 0) { - local.temp.name = Replace(local.cfcFiles.name, ".cfc", ""); - } else { - local.folderPattern = local.folders["name"][i]; - local.possibleFiles = $query( - dbtype = "query", - query = local.subfolder, - sql = "SELECT name FROM query WHERE LOWER(name) LIKE '%#LCase(local.folderPattern)#%.cfc'" - ); - if (local.possibleFiles.recordCount > 0) { - local.temp.name = Replace(local.possibleFiles.name, ".cfc", ""); - } else { - local.temp.name = local.folders["name"][i]; - } - } - } - local.temp.folderPath = $fullPathToPlugin(local.folders["name"][i]); - local.temp.componentName = local.folders["name"][i] & "." & local.temp.name; - } else { - local.temp.name = Replace(local.pluginCfc.name, ".cfc", ""); - local.temp.folderPath = $fullPathToPlugin(local.folders["name"][i]); - local.temp.componentName = local.folders["name"][i] & "." & Replace(local.pluginCfc.name, ".cfc", ""); - } - local.plugins[local.folders["name"][i]] = local.temp; - } - return local.plugins; - } - - public struct function $pluginFiles() { - // get all plugin zip files - local.plugins = {}; - local.files = $files(); - for (local.i = 1; i <= local.files.recordCount; i++) { - local.name = ListFirst(local.files["name"][i], "-"); - local.temp = {}; - local.temp.file = $fullPathToPlugin(local.files["name"][i]); - local.temp.name = local.files["name"][i]; - local.temp.folderPath = $fullPathToPlugin(LCase(local.name)); - if (StructKeyExists(server, "boxlang") && !local.temp.folderPath.startsWith("/")) { - local.temp.folderPath = "/" & local.temp.folderPath; - } - local.temp.folderExists = DirectoryExists(local.temp.folderPath); - local.plugins[local.name] = local.temp; - }; - return local.plugins; - } - - public void function $pluginsExtract() { - // get all plugin zip files - local.plugins = $pluginFiles(); - for (local.p in local.plugins) { - local.plugin = local.plugins[local.p]; - if (!local.plugin.folderExists || (local.plugin.folderExists && variables.$class.overwritePlugins)) { - if (!local.plugin.folderExists) { - try { - DirectoryCreate(local.plugin.folderPath); - } catch (any e) { - // - } - } - $zip(action = "unzip", destination = local.plugin.folderPath, file = local.plugin.file, overwrite = true); - } - }; - } - - public void function $pluginDelete() { - local.folders = $pluginFolders(); - // put zip files into a list - local.files = $pluginFiles(); - local.files = StructKeyList(local.files); - // loop through the plugins folders - for (local.iFolder in $pluginFolders()) { - local.folder = local.folders[local.iFolder]; - // see if a folder is in the list of plugin files - if (!ListContainsNoCase(local.files, local.folder.name)) { - if (StructKeyExists(server, "boxlang") && !local.folder.folderPath.startsWith("/")) { - local.folder.folderPath = "/" & local.folder.folderPath; - } - DirectoryDelete(local.folder.folderPath, true); - } - }; - } - - public void function $pluginsProcess() { - local.plugins = $pluginFolders(); - local.pluginKeys = ListSort(StructKeyList(local.plugins), "textnocase", variables.sort); - if (SpanExcluding(variables.$class.wheelsVersion, " ") == "@build.version@") { - local.wheelsVersion = "0.0.0"; - } else { - local.wheelsVersion = SpanExcluding(variables.$class.wheelsVersion, " "); - } - for (local.pluginKey in local.pluginKeys) { - local.pluginValue = local.plugins[local.pluginKey]; - local.plugin = CreateObject("component", $componentPathToPlugin(local.pluginKey, local.pluginValue.name)).init(); - if ( - !StructKeyExists(local.plugin, "version") - || ListFind(local.plugin.version, local.wheelsVersion) - || variables.$class.loadIncompatiblePlugins - ) { - variables.$class.plugins[local.pluginKey] = local.plugin; - // If plugin author has specified compatibility version as 2.0, only check against that major version - // If they've specified 2.0.1, then be more specific - if (StructKeyExists(local.plugin, "version")) { - if ( - (ListLen(local.plugin.version, ".") > 2 && !ListFind(local.plugin.version, local.wheelsVersion)) - || ( - ListLen(local.plugin.version, ".") == 2 - && !ListFind(local.plugin.version, ListDeleteAt(local.wheelsVersion, 3, ".")) - ) - ) { - variables.$class.incompatiblePlugins = ListAppend(variables.$class.incompatiblePlugins, local.pluginKey); - } - } - } - }; - } - - /** - * Attempt to extract version numbers from box.json and/or corresponding .zip files - * Storing box.json data too as this may be useful later - */ - public void function $pluginMetaData() { - for (local.plugin in variables.$class.plugins) { - variables.$class.pluginMeta[local.plugin] = {"version" = "", "boxjson" = {}}; - local.boxJsonLocation = $fullPathToPlugin(local.plugin & "/" & 'box.json'); - if (FileExists(local.boxJsonLocation)) { - local.boxJson = DeserializeJSON(FileRead(local.boxJsonLocation)); - variables.$class.pluginMeta[local.plugin]["boxjson"] = local.boxJson; - if (StructKeyExists(local.boxJson, "version")) { - variables.$class.pluginMeta[local.plugin]["version"] = local.boxJson.version; - } - } - } - } - - public void function $determineDependency() { - for (local.iPlugins in variables.$class.plugins) { - local.pluginMeta = GetMetadata(variables.$class.plugins[local.iPlugins]); - if (StructKeyExists(local.pluginMeta, "dependency")) { - for (local.iDependency in local.pluginMeta.dependency) { - local.iDependency = Trim(local.iDependency); - if (!StructKeyExists(variables.$class.plugins, local.iDependency)) { - variables.$class.dependantPlugins = ListAppend( - variables.$class.dependantPlugins, - Reverse(SpanExcluding(Reverse(local.pluginMeta.name), ".")) & "|" & local.iDependency - ); - } - }; - } - }; - } - - /** - * MIXINS - */ - - public void function $processMixins() { - // setup a container for each mixableComponents type - for (local.iMixableComponents in variables.$class.mixableComponents) { - variables.$class.mixins[local.iMixableComponents] = {}; - } - - // get a sorted list of plugins so that we run through them the same on - // every platform - local.pluginKeys = ListToArray(ListSort(StructKeyList(variables.$class.plugins), "textnocase", variables.sort)); - - for (local.iPlugin in local.pluginKeys) { - // reference the plugin - local.plugin = variables.$class.plugins[local.iPlugin]; - // grab meta data of the plugin - local.pluginMeta = GetMetadata(local.plugin); - if ( - !StructKeyExists(local.pluginMeta, "environment") - || ListFindNoCase(local.pluginMeta.environment, variables.$class.wheelsEnvironment) - ) { - // by default and for backwards compatibility, we inject all methods - // into all objects - local.pluginMixins = "global"; - - // if the component has a default mixin value, assign that value - if (StructKeyExists(local.pluginMeta, "mixin")) { - local.pluginMixins = local.pluginMeta["mixin"]; - } - - // loop through all plugin methods and enter injection info accordingly - // (based on the mixin value on the method or the default one set on the - // entire component) - local.pluginMethods = StructKeyList(local.plugin); - - for (local.iPluginMethods in local.pluginMethods) { - if (IsCustomFunction(local.plugin[local.iPluginMethods]) && local.iPluginMethods neq "init") { - local.methodMeta = GetMetadata(local.plugin[local.iPluginMethods]); - local.methodMixins = local.pluginMixins; - if (StructKeyExists(local.methodMeta, "mixin")) { - local.methodMixins = local.methodMeta["mixin"]; - } - - // mixin all methods except those marked as none - if (local.methodMixins != "none") { - for (local.iMixableComponent in variables.$class.mixableComponents) { - if (local.methodMixins == "global" || ListFindNoCase(local.methodMixins, local.iMixableComponent)) { - // cfformat-ignore-start - variables.$class.mixins[local.iMixableComponent][local.iPluginMethods] = local.plugin[local.iPluginMethods]; - // cfformat-ignore-end - } - } - } - } - } - } - } - } - - /** - * Applies mixins to a component based on application configurations. - */ - public any function $initializeMixins(required struct variablesScope) { - // We use $wheels here since these variables get placed in the variables scope of all objects. - // This way we sure they don't clash with other Wheels variables or any variables the developer may set. - if (IsDefined("application") && StructKeyExists(application, "$wheels")) { - $wheels.appKey = "$wheels"; - } else { - $wheels.appKey = "wheels"; - } - - if (IsDefined("application") && !StructIsEmpty(application[$wheels.appKey].mixins)) { - $wheels.metaData = GetMetadata(variablesScope.this); - if (StructKeyExists($wheels.metaData, "displayName")) { - $wheels.className = $wheels.metaData.displayName; - } else if (findNoCase("controllers", $wheels.metaData.fullname)){ - $wheels.className = "controller"; - } else if (findNoCase("models", $wheels.metaData.fullname)){ - $wheels.className = "model"; - } else if (findNoCase("tests", $wheels.metaData.fullname)){ - $wheels.className = "test"; - } else { - $wheels.className = Reverse(SpanExcluding(Reverse($wheels.metaData.name), ".")); - } - if (StructKeyExists(application[$wheels.appKey].mixins, $wheels.className)) { - if (!StructKeyExists(variablesScope, "core")) { - if (application[$wheels.appKey].serverName == "Railo") { - // this is to work around a railo bug (https://jira.jboss.org/browse/RAILO-936) - // NB, fixed in Railo 3.2.0, so assume this is fixed in all lucee versions - variablesScope.core = Duplicate(variablesScope); - } else { - variablesScope.core = {}; - StructAppend(variablesScope.core, variablesScope); - StructDelete(variablesScope.core, "$wheels"); - } - } - StructAppend(variablesScope, application[$wheels.appKey].mixins[$wheels.className], true); - - if (StructKeyExists(variablesScope, "this")) { - StructAppend(variablesScope.this, application[$wheels.appKey].mixins[$wheels.className], true); - } - - if (StructKeyExists(variablesScope.core, "this")) { - StructAppend(variablesScope.core.this, application[$wheels.appKey].mixins[$wheels.className], true); - } - } - - // Get rid of any extra data created in the variables scope. - if (StructKeyExists(variablesScope, "$wheels")) { - StructDelete(variablesScope, "$wheels"); - } - } - return variablesScope; - } - - /** - * GETTERS - */ - - public any function getPlugins() { - return variables.$class.plugins; - } - - public any function getPluginMeta() { - return variables.$class.pluginMeta; - } - - public any function getIncompatiblePlugins() { - return variables.$class.incompatiblePlugins; - } - - public any function getDependantPlugins() { - return variables.$class.dependantPlugins; - } - - public any function getMixins() { - return variables.$class.mixins; - } - - public any function getMixableComponents() { - return variables.$class.mixableComponents; - } - - public any function inspect() { - return variables; - } - - /** - * PRIVATE - */ - - public string function $fullPathToPlugin(required string folder) { - return ListAppend(variables.$class.pluginPathFull, arguments.folder, "/"); - } - - public string function $componentPathToPlugin(required string folder, required string file) { - // BoxLang compatibility: Handle component path construction more carefully - if (structKeyExists(server, "boxlang")) { - local.basePath = application[$appKey()].pluginComponentPath; - local.fileName = Len(Trim(arguments.file)) ? arguments.file : arguments.folder; - if (Find("/", local.basePath)) { - local.basePath = Replace(local.basePath, "/", ".", "all"); - local.basePath = REReplace(local.basePath, "^\.+", "", "all"); - } - - local.componentPath = "#local.basePath#.#arguments.folder#.#local.fileName#"; - local.componentPath = REReplaceNoCase(local.componentPath, "\.+$", "", "all"); - - return local.componentPath; - } else { - return "#application[$appKey()].pluginComponentPath#.#arguments.folder#.#arguments.file#"; - } - } - - public query function $folders() { - local.query = $directory( - action = "list", - directory = variables.$class.pluginPathFull, - type = "dir", - sort = "name #variables.sort#" - ); - return $query( - dbtype = "query", - query = local.query, - sql = "select * from query where name not like '.%' ORDER BY name #variables.sort#" - ); - } - - public query function $files() { - local.query = $directory( - action = "list", - directory = variables.$class.pluginPathFull, - filter = "*.zip", - type = "file", - sort = "name #variables.sort#" - ); - return $query( - dbtype = "query", - query = local.query, - sql = "select * from query where name not like '.%' ORDER BY name #variables.sort#" - ); - } - -} diff --git a/core/src/wheels/Test.cfc b/core/src/wheels/Test.cfc deleted file mode 100644 index c88ade44ac..0000000000 --- a/core/src/wheels/Test.cfc +++ /dev/null @@ -1,798 +0,0 @@ -component output="false" displayName="Test" extends="wheels.Global"{ - - property name="Mixins" inject="id:Plugins"; - - function init(){ - return this; - } - /* - Base component for rapidly writing/running test cases. - - Terminology - ----------- - - A Test Package is a collection of Test Cases that tests the functionality - of an application/service/whatever. - - - A Test Case is a collection of Tests to apply to a particular CF component, - tag or include file. - - - A Test is a sequence of calls to the code being tested and Assertions about - the results we get from the code. - - - An Assertion is a statement we make about the results we get that should - evaluate to true if the tested code is working properly. - - How are these things represented in test code using this file? - -------------------------------------------------------------- - - A Test Package is a directory that can be referred to by a CF mapping, and - ideally outside the web root for security. - - - A Test Case is a CF component in that directory that extends this component. - - - A Test is a method in one of these components. The method name should start with - the word "test", it should require no arguments and return void. Any setup or - clearup code common to all test functions can be added to optional setup() and - teardown() methods, which again take no arguments and return void. - - Tests in each Test Case are run in alphabetical order. If you want your tests - to run in a particular order you could name them test01xxx, test02yyy, etc. - - - An Assertion is a call to the assert() method (inherited from this component) inside - a test method. assert() takes a string argument, an expression (see ColdFusion - evaluate() documentation) that evaluates to true or false. If false, a "failure" - is recorded for the test case and the test case fails. assert() tries to include - the value of any variables it finds in the expression. - - If there are specific variable values you would like included in the failure message, - pass them as additional string arguments to assert(). Multiple variables can be - listed in a single space-delimited string if this is convenient. - - For more complicated assertions you may call the fail() method directly, which takes - a single message string as an argument. - - - If an uncaught exception is thrown an "error" is recorded for the Test Case and the - Test Case fails. - - Running tests - ------------- - Assuming this file is under a com.rocketboots.rocketunit cf mapping, you have some test cases - under a com.myco.myapp.sometestpackage cf mapping, and a test case SomeTestCase.cfc in that - mapping... - - To run a test package: - - - - - - To run a specific test case: - - - - - To see human readable output after running a test: - - #test.HTMLFormatTestResults()# - - The test results are available in the request.test structure. If you would like to - use a different key in request (as we do for the rocketunit self-tests) for the results - you can pass the key name as a second argument to the run method. - - You can call run() multiple times and the test results will be combined. - - If you wish to reset the test results before calling run() again, call resetTestResults(). - - Copyright 2007 RocketBoots Pty Limited - http://www.rocketboots.com.au - - This file is part of RocketUnit. - - RocketUnit is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - RocketUnit is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with RocketUnit; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - - @version $Id: Test.cfc 167 2007-04-12 07:50:15Z robin $ - */ - - // variables that are used by the testing framework itself - TESTING_FRAMEWORK_VARS = {}; - TESTING_FRAMEWORK_VARS.WHEELS_TESTS_BASE_COMPONENT_PATH = ""; - TESTING_FRAMEWORK_VARS.ROOT_TEST_PATH = ""; - TESTING_FRAMEWORK_VARS.RUNNING_TEST = ""; - - // used to hold debug information for display - if (!StructKeyExists(request, "TESTING_FRAMEWORK_DEBUGGING")) { - request["TESTING_FRAMEWORK_DEBUGGING"] = {}; - } - if (!StructKeyExists(request, "TESTING_FRAMEWORK_DEBUG_STRINGS")) { - request["TESTING_FRAMEWORK_DEBUG_STRINGS"] = {}; - } - - /** - * Called from a test function. - * If expression evaluates to false, record a failure against the test. - * - * [section: Test Model] - * [category: Testing Functions] - * - * @expression String containing CFML expression to evaluate. - * @2..n String(s) containing space-delimited list of variables to evaluate and include in the failure message to help determine cause of failed assertion. - */ - public void function assert(required string expression) { - // Convert "yes" / "no" to true / false. - arguments.expression = arguments.expression == "yes" ? true : arguments.expression; - arguments.expression = arguments.expression == "no" ? false : arguments.expression; - - var token = ""; - var tokenValue = ""; - var message = "assert failed: #arguments.expression#"; - var newline = Chr(10) & Chr(13); - var i = ""; - var evaluatedTokens = ""; - if (!Evaluate(arguments.expression)) { - for (i in arguments) { - local.expr = arguments[i]; - evaluatedTokens = {}; - // Double pass of expressions with different delimiters so that for expression "a(b) or c[d]", "a(b)", "c[d]", "b" and "d" are evaluated. Do not evaluate any expression more than once. - for (token in ListToArray("#local.expr# #ReReplace(local.expr, "[([\])]", " ")#", " +=-*/%##")) { - if (!StructKeyExists(evaluatedTokens, token)) { - evaluatedTokens[token] = true; - local.tokenValue = "__INVALID__"; - if (!(IsNumeric(token) || IsBoolean(token))) { - try { - local.tokenValue = Evaluate(token); - } catch (any e) { - } - } - // Format token value according to type. - if ((!IsSimpleValue(local.tokenValue)) || (local.tokenValue != "__INVALID__")) { - if (IsSimpleValue(local.tokenValue)) { - if (!(IsNumeric(local.tokenValue) || IsBoolean(local.tokenValue))) { - local.tokenValue = "'#local.tokenValue#'"; - } - } else { - if (IsArray(local.tokenValue)) { - local.tokenValue = "Array containing #ArrayLen(local.tokenValue)# items"; - } else if (IsStruct(local.tokenValue)) { - local.tokenValue = "Struct with #StructCount(local.tokenValue)# members"; - } else if (IsQuery(local.tokenValue)) { - local.tokenValue = "Query with #local.tokenValue.RecordCount# rows"; - } else if (IsCustomFunction(local.tokenValue)) { - local.tokenValue = "UDF"; - } - } - message = message & newline & token & " = " & tokenValue; - } - } - } - } - fail(message); - } - } - - /* - Called from a test function to cause the test to fail. - Will throw an exception resulting in a test failure along with an option message. - - @param message Message to record in test results against failure. - */ - public void function fail(string message = "") { - /* - run() interprets exception with this errorcode as a "Failure". - All other errorcodes cause are interpreted as an "Error". - */ - Throw(errorcode = "__FAIL__", message = "#HtmlEditFormat(message)#"); - } - - /** - * Used to examine an expression - * Any overloaded arguments get passed to cfdump's attributeCollection - * - * [section: Test Model] - * [category: Testing Functions] - * - * @expression The expression to examine - * @display Whether to display the debug call. False returns without outputting anything into the buffer. Good when you want to leave the debug command in the test for later purposes, but don't want it to display - */ - public any function debug(required string expression, boolean display = true) { - local.attributeArgs = {"var" = $evaluateExpression(arguments.expression), "label" = arguments.expression}; - local.dump = ""; - - // this string will be added to the request key regardless of display argument - local.debugString = ArrayToList( - [ - "", - "---------- DEBUG START '#arguments.expression#' --------", - SerializeJSON(attributeArgs["var"]), - "---------- DEBUG END '#arguments.expression#' --------", - "" - ], - Chr(13) - ); - - if (!StructKeyExists(request["TESTING_FRAMEWORK_DEBUG_STRINGS"], TESTING_FRAMEWORK_VARS.RUNNING_TEST)) { - request["TESTING_FRAMEWORK_DEBUG_STRINGS"][TESTING_FRAMEWORK_VARS.RUNNING_TEST] = ""; - } - request["TESTING_FRAMEWORK_DEBUG_STRINGS"][TESTING_FRAMEWORK_VARS.RUNNING_TEST] &= local.debugString; - - if (!arguments.display) { - return; - } - - StructDelete(arguments, "expression"); - StructDelete(arguments, "display"); - StructAppend(attributeArgs, arguments, true); - - savecontent variable="dump" { - WriteDump(attributeCollection = attributeArgs); - } - - if (!StructKeyExists(request["TESTING_FRAMEWORK_DEBUGGING"], TESTING_FRAMEWORK_VARS.RUNNING_TEST)) { - request["TESTING_FRAMEWORK_DEBUGGING"][TESTING_FRAMEWORK_VARS.RUNNING_TEST] = []; - } - ArrayAppend(request["TESTING_FRAMEWORK_DEBUGGING"][TESTING_FRAMEWORK_VARS.RUNNING_TEST], dump); - } - - /** - * Catches a raised error and returns the error type - * Great if you want to test that a certain exception will be raised. - * - * [section: Test Model] - * [category: Testing Functions] - * - * @expression String containing CFML expression to evaluate - */ - public string function raised(required string expression) { - try { - Evaluate(arguments.expression); - } catch (any e) { - return Trim(e.type); - } - return ""; - } - - /* - Run all the tests in a component. - - @param resultKey Key to store distinct test result sets under in - request scope, defaults to "test" - @returns true if no errors - */ - public boolean function $runTest(string resultKey = "test", string testname = "") { - var key = ""; - var keyList = ""; - var time = ""; - var testCase = ""; - var status = ""; - var result = ""; - var message = ""; - var numTests = 0; - var numTestFailures = 0; - var numTestErrors = 0; - var newline = Chr(10) & Chr(13); - var func = ""; - var functions = ""; - var tagContext = ""; - var context = ""; - - /* - Check for and if necessary set up the structure to store test results - */ - if (!StructKeyExists(request, resultKey)) { - $resetTestResults(resultKey); - } - - testCase = GetMetadata(this).name; - - /* - Iterate through the members of the this scope in alphabetical order, - invoking methods starting in "test". Wrap with calls to setup() - and teardown() if provided. - */ - if (!StructKeyExists(GetMetadata(this), "functions")) { - functions = ""; - } else { - functions = GetMetadata(this).functions; - } - for (func in functions) { - keyList = ListAppend(keyList, func.name); - }; - keyList = ListSort(keyList, "textnocase", "asc"); - - for (key in ListToArray(keyList)) { - /* Include test name and make distinct so debugging doesn't duplicate output */ - var distinctKey = Replace(Replace(Replace(testCase, ".", "_", "all"), "tests_", "", "one"), "wheels_", "", "one") & '_' & key; - /* keep track of the test name so we can display debug information */ - TESTING_FRAMEWORK_VARS.RUNNING_TEST = distinctkey; - - if ( - (Left(key, 4) eq "test" and IsCustomFunction(this[key])) - and (!Len(arguments.testname) or (Len(arguments.testname) and arguments.testname eq key)) - ) { - time = GetTickCount(); - - if (StructKeyExists(this, "setup")) { - setup(); - } - - try { - message = ""; - Invoke(this, key); - status = "Success"; - request[resultkey].numSuccesses = request[resultkey].numSuccesses + 1; - } catch (any e) { - message = e.message; - - if (e.ErrorCode eq "__FAIL__") { - /* - fail() throws __FAIL__ exception - */ - status = "Failure"; - request[resultkey].ok = false; - request[resultkey].numFailures = request[resultkey].numFailures + 1; - numTestFailures = numTestFailures + 1; - } else { - /* - another exception thrown - */ - status = "Error"; - if (ArrayLen(e.tagContext)) { - template = "#ListLast(e.tagContext[1].template, "/")#:#e.tagContext[1].line#"; - tagContext = "
    "; - for (context in e.tagContext) { - tagContext = tagContext & '
  • #context.template#:#context.line#
  • '; - }; - tagContext = tagContext & "
"; - } else { - template = ""; - tagContext = "[Unknown tagContext]"; - } - - if (Len(template)) { - message = message & newline & template; - } - if (StructKeyExists(e, "sql") and Len(e.sql)) { - message = message & newline & newline & e.sql; - } - if (Len(e.extendedInfo)) { - message = message & newline & newLine & e.extendedInfo; - } - message = message & newline & newline & tagContext; - if (Len(e.detail)) { - message = message & newline & newline & e.detail; - } - request[resultkey].ok = false; - request[resultkey].numErrors = request[resultkey].numErrors + 1; - numTestErrors = numTestErrors + 1; - } - } - - if (StructKeyExists(this, "teardown")) { - teardown(); - } - - time = GetTickCount() - time; - - /* - Record test results - */ - result = { - testCase = testCase, - testName = key, - time = time, - status = status, - message = message, - distinctKey = distinctKey - }; - - ArrayAppend(request[resultkey].results, result); - - request[resultkey].numTests = request[resultkey].numTests + 1; - numTests = numTests + 1; - } - }; - - result = {testCase = testCase, numTests = numTests, numFailures = numTestFailures, numErrors = numTestErrors}; - - // filter test results based on url params - // this is experimental output for ci pipeline - if (StructKeyExists(request.wheels.params, "only")) { - local.results = ArrayFilter(request[resultkey].results, function(testCase) { - return ListFindNoCase(request.wheels.params.only, arguments.testCase.status); - }); - request[resultkey].results = local.results; - } else { - ArrayAppend(request[resultkey].summary, result); - } - - request[resultkey].numCases = request[resultkey].numCases + 1; - request[resultkey].end = Now(); - - return numTestErrors eq 0; - } - - /* - Clear results. - - @param resultKey Key to store distinct test result sets under in - request scope, defaults to "test" - */ - - public void function $resetTestResults(string resultKey = "test") { - request[resultkey] = {}; - request[resultkey].begin = Now(); - request[resultkey].ok = true; - request[resultkey].numCases = 0; - request[resultkey].numTests = 0; - request[resultkey].numSuccesses = 0; - request[resultkey].numFailures = 0; - request[resultkey].numErrors = 0; - request[resultkey].summary = []; - request[resultkey].results = []; - } - - /* - Report test results at overall, test case and test level, highlighting - failures and errors. - - @param resultKey Key to retrieve distinct test result sets from in - request scope, defaults to "test" - @returns HTML formatted test results - */ - - public any function $results(string resultKey = "test") { - local.rv = false; - if (StructKeyExists(request, resultkey)) { - request[resultkey].path = TESTING_FRAMEWORK_VARS.WHEELS_TESTS_BASE_COMPONENT_PATH; - - local.summaryLength = ArrayLen(request[resultkey].summary); - for (local.i = 1; local.i <= local.summaryLength; local.i++) { - request[resultkey].summary[local.i].cleanTestCase = $cleanTestCase(request[resultkey].summary[local.i].testCase); - request[resultkey].summary[local.i].packageName = $cleanTestPath(request[resultkey].summary[local.i].testCase); - }; - - local.resultsLength = ArrayLen(request[resultkey].results); - for (local.i = 1; local.i <= local.resultsLength; local.i++) { - request[resultkey].results[local.i].cleanTestCase = $cleanTestCase(request[resultkey].results[local.i].testCase); - request[resultkey].results[local.i].cleanTestName = $cleanTestName(request[resultkey].results[local.i].testName); - request[resultkey].results[local.i].packageName = $cleanTestPath(request[resultkey].results[local.i].testCase); - request[resultkey].results[local.i].debugString = ""; - if (StructKeyExists(request.TESTING_FRAMEWORK_DEBUG_STRINGS, request[resultkey].results[local.i].distinctKey)) { - request[resultkey].results[local.i].debugString = request.TESTING_FRAMEWORK_DEBUG_STRINGS[ - request[resultkey].results[local.i].distinctKey - ]; - } - }; - - local.rv = request[resultkey]; - } - return local.rv; - } - - /* - * The function wheels uses to run tests - */ - public any function $wheelsRunner(struct options = {}) { - // the key in the request scope that will contain the test results - local.resultKey = "WheelsTests"; - - // save the original environment for overloading - if (application.wheels.restoreTestRunnerApplicationScope) { - request.wheels.testRunnerApplicationScope = Duplicate(application.wheels); - } - // to enable unit testing controllers without actually performing the redirect - set(functionName = "redirectTo", delay = true); - - // not only can we specify the package, but also the test we want to run - local.test = ""; - if (StructKeyExists(arguments.options, "test") && Len(arguments.options.test)) { - local.test = arguments.options.test; - } - local.paths = $resolvePaths(arguments.options); - local.packages = $listTestPackages(arguments.options, local.paths.test_filter); - - if (StructKeyExists(arguments.options, "seed") && arguments.options.seed) { - this.$seedDatabase(local.paths); - } - // run tests - local.i = 0; - for (local.row in local.packages) { - local.i++; - local.instance = application.wirebox.getInstance(name = "#local.row.package#"); - // is there a better way to check for existence of a function? - // if the beforeall method is present, run it once only per request - if (StructKeyExists(local.instance, "beforeAll") && local.i eq 1) { - local.instance.beforeAll(); - } - if (StructKeyExists(local.instance, "packageSetup")) { - local.instance.packageSetup(); - } - local.instance.$runTest(local.resultKey, local.test); - if (StructKeyExists(local.instance, "packageTeardown")) { - local.instance.packageTeardown(); - } - // if the afterAll method is present, run it after the last package (once per request) - if (StructKeyExists(local.instance, "afterAll") && local.i eq local.packages.recordCount) { - local.instance.afterAll(); - } - }; - // return the results - return $results(local.resultKey); - } - - /* - * Args: - * component: path to the component you want to check as a valid test - * shouldExtend: if the component should extend a base component to be a valid test - */ - public boolean function $isValidTest(required string component, string shouldExtend = "Test") { - local.name = ListLast(arguments.component, "."); - - if (application.wheels.validateTestPackageMetaData && Len(arguments.shouldExtend)) { - local.metadata = GetComponentMetadata(arguments.component); - if ( - !StructKeyExists(local.metadata, "extends") - or ListLast(local.metadata.extends.fullname, ".") neq arguments.shouldExtend - ) { - return false; - } - } - // package names that begin with underscores are not valid - if (Left(local.name, 1) eq "_") { - return false; - } - // don't test Test.cfc base components - if (local.name eq "Test") { - return false; - } - return true; - } - - /* - * Removes the base test directory from the test name to make them prettier and more readable. - */ - public string function $cleanTestCase( - required string name, - string path = TESTING_FRAMEWORK_VARS.WHEELS_TESTS_BASE_COMPONENT_PATH - ) { - return ListChangeDelims(Replace(arguments.name, arguments.path, ""), ".", "."); - } - - /* - * Cleans up the test name so they are more readable. - */ - public string function $cleanTestName(required string name) { - local.rv = arguments.name; - if (Find("_", local.rv)) { - local.rv = humanize(Trim(ReReplaceNoCase(ListRest(local.rv, "_"), "_|-", " ", "all"))); - } else { - local.rv = ReReplaceNoCase(local.rv, "_|-", " ", "all"); - local.rv = Right(local.rv, Len(local.rv) - 4); - local.rv = Trim(capitalize(LCase(humanize(local.rv)))); - } - return local.rv; - } - - /* - * Cleans up the test path. - */ - public string function $cleanTestPath(required string path) { - return ListChangeDelims(Replace(arguments.path, TESTING_FRAMEWORK_VARS.ROOT_TEST_PATH, ""), ".", "."); - } - - /* - * This resolves all the paths needed to run the tests. - */ - public struct function $resolvePaths(struct options = {}) { - local.rv = {}; - - // default test type - local.type = "core"; - // testfilter - local.rv.test_filter = "*"; - // by default we run all packages, however they can specify to run a specific package of tests - local.package = ""; - - // if they specified a package we should only run that - if (StructKeyExists(arguments.options, "package") && Len(arguments.options.package)) { - local.package = arguments.options.package; - } - // overwrite the default test type if passed - if (StructKeyExists(arguments.options, "type") and Len(arguments.options.type)) { - local.type = arguments.options.type; - } - - // which tests to run - if (local.type eq "core") { - // core tests - TESTING_FRAMEWORK_VARS.ROOT_TEST_PATH = application.wheels.wheelsComponentPath; - } else if (local.type eq "app") { - // app tests - TESTING_FRAMEWORK_VARS.ROOT_TEST_PATH = application.wheels.rootComponentPath; - } else { - // specific plugin tests - TESTING_FRAMEWORK_VARS.ROOT_TEST_PATH = application.wheels.rootComponentPath; - TESTING_FRAMEWORK_VARS.ROOT_TEST_PATH = ListAppend( - TESTING_FRAMEWORK_VARS.ROOT_TEST_PATH, - "#application.wheels.pluginComponentPath#.#local.type#", - "." - ); - } - - // set test directory for tests - if(local.type eq "app") { - TESTING_FRAMEWORK_VARS.TEST_DIRECTORY = "tests.RocketUnit"; - } else { - TESTING_FRAMEWORK_VARS.TEST_DIRECTORY = "tests"; - } - - TESTING_FRAMEWORK_VARS.ROOT_TEST_PATH = ListAppend(TESTING_FRAMEWORK_VARS.ROOT_TEST_PATH, TESTING_FRAMEWORK_VARS.TEST_DIRECTORY, "."); - - // add the package if specified - local.rv.test_path = ListAppend("#TESTING_FRAMEWORK_VARS.ROOT_TEST_PATH#", local.package, "."); - - // clean up testpath - local.rv.test_path = ListChangeDelims(local.rv.test_path, ".", "./\"); - - // convert to regular path - local.rv.relative_root_test_path = "/" & ListChangeDelims(TESTING_FRAMEWORK_VARS.ROOT_TEST_PATH, "/", "."); - local.rv.full_root_test_path = ExpandPath(local.rv.relative_root_test_path); - local.rv.relative_test_path = "/" & ListChangeDelims(local.rv.test_path, "/", "."); - local.rv.full_test_path = ExpandPath(local.rv.relative_test_path); - - if (!DirectoryExists(local.rv.full_test_path)) { - if (FileExists(local.rv.full_test_path & ".cfc")) { - local.rv.test_filter = Reverse(ListFirst(Reverse(local.rv.test_path), ".")); - local.rv.test_path = Reverse(ListRest(Reverse(local.rv.test_path), ".")); - local.rv.relative_test_path = "/" & ListChangeDelims(local.rv.test_path, "/", "."); - local.rv.full_test_path = ExpandPath(local.rv.relative_test_path); - } else { - Throw( - type = "Wheels.Testing", - message = "Cannot find test package or single test", - detail = "In order to run test you must supply a valid test package or single test file to run" - ); - } - } - - // for test results display - TESTING_FRAMEWORK_VARS.WHEELS_TESTS_BASE_COMPONENT_PATH = local.rv.test_path; - - return local.rv; - } - - /* - * Returns a query containing all the test to run and their directory path. - */ - public query function $listTestPackages(struct options = {}, string filter = "*") { - local.rv = QueryNew("package", "Varchar"); - local.paths = $resolvePaths(arguments.options); - $initialiseTestEnvironment(local.paths, arguments.options); - local.packages = DirectoryList( - local.paths.full_test_path, - true, - "query", - "#arguments.filter#.cfc", - arguments.options.sort - ); - for (local.package in local.packages) { - local.packageName = ListChangeDelims( - RemoveChars(local.package.directory, 1, Len(local.paths.full_test_path)), - ".", - "\/" - ); - // Directories that begin with an underscore are ignored. - if (!ReFindNoCase("(^|\.)_", local.packageName)) { - local.packageName = ListPrepend(local.packageName, local.paths.test_path, "."); - local.packageName = ListAppend(local.packageName, ListFirst(local.package.name, "."), "."); - // Ignore invalid packages. - local.useTest = $isValidTest(local.packageName); - // a bit hacky.. try to use Left rather than contains - if (StructKeyExists(arguments.options, "skip") && local.packageName contains arguments.options.skip) { - local.useTest = false; - } - if (local.useTest) { - QueryAddRow(local.rv); - QuerySetCell(local.rv, "package", local.packageName); - } - } - }; - return local.rv; - } - - /* - * Initialises the test environment and populates test database. - */ - public void function $initialiseTestEnvironment(required struct paths, required struct options) { - if (FileExists(arguments.paths.full_root_test_path & "/env.cfm")) { - include "#arguments.paths.relative_root_test_path#/env.cfm"; - } - } - - /* - * Seeds the test database. - */ - public void function $seedDatabase(required struct paths) { - if (FileExists(arguments.paths.full_root_test_path & "/seed.cfm")) { - include "#arguments.paths.relative_root_test_path#/seed.cfm"; - } - } - - /* - * Returns true if a file path is a wheels core file. - */ - public any function $isCoreFile(required string path) { - local.path = Replace(arguments.path, ExpandPath("/"), "", "one"); - return (Left(local.path, 7) == "wheels/" || ListFindNoCase("index.cfm", local.path)); - } - - public any function $evaluateExpression(required string expression) { - local.parts = $splitOutsideFunctions(arguments.expression, "."); - - try { - if (arrayLen(local.parts) == 1) { - if(structKeyExists(variables, local.parts[1])){ - return variables[local.parts[1]]; - } else if(findNoCase("(", local.parts[1])){ - local.match = reFind("\(([^()]+)\)", local.parts[1], 1, true); - local.functionName = listToArray(local.parts[1], "("); - if(local.match.len[1] == 0){ - return variables[local.functionName[1]](); - } else{ - local.args = listToArray(local.match.match[2], "="); - if(arrayLen(local.args) == 2){ - return invoke(variables, local.functionName[1], variables[local.args[2]]); - } else { - // Use the Evaluate function to run Built-in functions - return Evaluate("expression"); - } - } - } - } else if (arrayLen(local.parts) == 2 && structKeyExists(variables, local.parts[1]) && !reFind("\\(", local.parts[2])) { - local.structRef = variables[local.parts[1]]; - if (structKeyExists(local.structRef, local.parts[2])) { - return local.structRef[local.parts[2]]; - } - } else { - local.match = reFind("\(([^()]+)\)", local.parts[2], 1, true); - if(local.match.len[1] == 0){ - local.functionName = replaceNoCase(local.parts[2], "()", ""); - local.structRef = variables[local.parts[1]]; - if (structKeyExists(local.structRef, local.functionName)) { - return invoke(local.structRef, local.functionName); - } - } else { - if(findNoCase("=", local.match.match[2]) != 0){ - local.args = listToArray(local.match.match[2], "="); - local.functionName = listToArray(local.parts[2], "("); - local.structRef = variables[local.parts[1]]; - if (structKeyExists(local.structRef, local.functionName[1])) { - return invoke(local.structRef, local.functionName[1], variables[local.args[2]]); - } - } - } - } - - throw("Invalid expression: #arguments.expression#"); - } catch (any e) { - return "Error evaluating expression: " & e.message; - } - } - - function onDIcomplete(){ - if (structKeyExists(server, "boxlang")) { - variables.this = this; - } - Mixins.$initializeMixins(variables); - } - -} diff --git a/core/src/wheels/Testbox.cfc b/core/src/wheels/Testbox.cfc deleted file mode 100644 index ed0bca1cd7..0000000000 --- a/core/src/wheels/Testbox.cfc +++ /dev/null @@ -1,25 +0,0 @@ -/** - * Base TestBox spec for Wheels tests. - * Dynamically binds methods from `application.wo` into both - * the `variables` and `this` scope for convenience. - */ -component extends="testbox.system.BaseSpec" { - - // Pseudo-constructor (runs automatically) - if (structKeyExists(application, "wo")) { - local.methods = getMetaData(application.wo).functions; - - for (local.method in local.methods) { - // Only add public, non-inherited methods - if (local.method.access eq "public") { - local.methodExists = structKeyExists(variables, local.method.name) || structKeyExists(this, local.method.name); - - if (!local.methodExists) { - variables[local.method.name] = application.wo[local.method.name]; - this[local.method.name] = application.wo[local.method.name]; - } - } - } - } - -} \ No newline at end of file diff --git a/core/src/wheels/Wirebox.cfc b/core/src/wheels/Wirebox.cfc deleted file mode 100644 index d54af4d25f..0000000000 --- a/core/src/wheels/Wirebox.cfc +++ /dev/null @@ -1,20 +0,0 @@ -component extends="wirebox.system.ioc.config.Binder" { - function configure() { - // wireBox = { - // scanLocations = ["wheels"] - // }; - map('global').to('wheels.Global'); - - map('eventmethods').to('wheels.events.EventMethods'); - - map('ViewObj').to('wheels.view'); - - map('Plugins').to('wheels.Plugins'); - - // map('Controller').to('wheels.controller').mixins("/wheels/controller/processing"); - // map('Controller').to('wheels.controller').mixins("/wheels/global/cfml"); - // map('Controller').to('wheels.controller').virtualInheritance('Processing'); - // map('Controller').to('wheels.controller'); - // mapDirectory('wheels.controller').mixins("/wheels/controller/csrf"); - } -} diff --git a/core/src/wheels/box.json b/core/src/wheels/box.json deleted file mode 100644 index d55e5404f4..0000000000 --- a/core/src/wheels/box.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "devDependencies":{ - "testbox":"^6.2.1+400" - }, - "installPaths":{ - "testbox":"testbox" - }, - "testbox":{ - "runner":"/tests/runner.cfm" - } -} \ No newline at end of file diff --git a/core/src/wheels/databaseAdapters/Base.cfc b/core/src/wheels/databaseAdapters/Base.cfc deleted file mode 100755 index e922bfa4ea..0000000000 --- a/core/src/wheels/databaseAdapters/Base.cfc +++ /dev/null @@ -1,575 +0,0 @@ -component output=false extends="wheels.Global"{ - - public struct function $executeQuery( - required struct queryAttributes, - required array sql, - required boolean parameterize, - required numeric limit, - required numeric offset, - required string comment, - required string debugName, - required string primaryKey - ) { - // local variables - local.wheels = { rv: {} }; - local.newLine = chr(13) & chr(10); - local.args = arguments; - local.sqlArray = args.sql; - local.sqlLen = arrayLen(sqlArray); - - // Detect datasource info once - local.ds = args.queryAttributes; - local.dsInfo = ( structKeyExists(ds, "DATASOURCE") && len(ds.DATASOURCE) ) - ? $dbinfo(type="version", datasource=ds.DATASOURCE) - : $dbinfo( - type = "version", - datasource = application.wheels.dataSourceName, - username = application.wheels.dataSourceUserName, - password = application.wheels.dataSourcePassword - ); - - // Build query - cfquery(attributeCollection = args.queryAttributes) { - local.pos = 1; - local.prev = ""; - - for (; pos <= sqlLen; pos++) { - local.part = sqlArray[pos]; - - if (isStruct(part)) { - local.qp = $queryParams(part); - - // Handle NULL for "IS NULL" or "IS NOT NULL" - if ( - !isBinary(part.value) && - part.value == "null" && - pos > 1 && - ( right(prev, 2) == "IS" || right(prev, 6) == "IS NOT" ) - ) { - writeOutput("NULL"); - } - // Handle parameter lists "(?,?,?)" - else if (structKeyExists(qp, "list")) { - writeOutput("("); - if (args.parameterize) { - cfqueryParam(attributeCollection = qp); - } else { - writeOutput("(" & preserveSingleQuotes(part.value) & ")"); - } - writeOutput(")"); - } - // Normal parameter - else { - if (args.parameterize) { - cfqueryParam(attributeCollection = qp); - } else { - writeOutput($quoteValue(str = part.value, sqlType = part.type)); - } - } - } - else { - // regular SQL string part - part = replace(preserveSingleQuotes(part), "[[comma]]", ",", "all"); - writeOutput(preserveSingleQuotes(part)); - } - - writeOutput(newLine); - prev = part; - } - - // LIMIT / OFFSET logic - if (args.limit) { - if (findNoCase("Oracle", dsInfo.database_productname)) { - if (args.offset) { - writeOutput("OFFSET " & args.offset & " ROWS" & newLine & "FETCH NEXT " & args.limit & " ROWS ONLY"); - } else { - writeOutput("FETCH FIRST " & args.limit & " ROWS ONLY"); - } - } else { - writeOutput("LIMIT " & args.limit); - if (args.offset) { - writeOutput(newLine & "OFFSET " & args.offset); - } - } - } - - // Comment block - if (len(args.comment)) { - writeOutput(args.comment); - } - } - - // Retrieve debug query if needed - if (structKeyExists(local, args.debugName)) { - wheels.rv.query = local[args.debugName]; - } - - // Manual identity retrieval for Lucee / ACF - wheels.id = $identitySelect( - primaryKey = args.primaryKey, - queryAttributes = args.queryAttributes, - result = wheels.result - ); - - if (structKeyExists(wheels,"id") && isStruct(wheels.id) && !structIsEmpty(wheels.id)) { - structAppend(wheels.result, wheels.id); - } - - wheels.rv.result = wheels.result; - return wheels.rv; - } - - /** - * Initialize and return the adapter object. - */ - public any function $init(required string dataSource, required string username, required string password) { - variables.dataSource = arguments.dataSource; - variables.username = arguments.username; - variables.password = arguments.password; - return this; - } - - /** - * Set a default for the column name that holds the last inserted auto-incrementing primary key value. - * Individual database adapters will override when necessary. - */ - public string function $generatedKey() { - return "generated_key"; - } - - /** - * Called after a query has executed. - * If the query was an INSERT and the generated auto-incrementing primary key is not in the result we get it manually. - * If the primary key was part of the INSERT (i.e. it wasn't auto-incrementing) we don't need to check it though. - * This process is typically needed on non-supported databases (example: H2) and drivers (example: jTDS). - * We return void or a struct containing the key name / value. - */ - public any function $identitySelect( - required struct queryAttributes, - required struct result, - required string primaryKey - ) { - local.query = {}; - local.sql = Trim(arguments.result.sql); - if (Left(local.sql, 11) == "INSERT INTO" && !StructKeyExists(arguments.result, $generatedKey())) { - local.startPar = Find("(", local.sql) + 1; - local.endPar = Find(")", local.sql); - local.columnList = ReplaceList( - Mid(local.sql, local.startPar, (local.endPar - local.startPar)), - "#Chr(10)#,#Chr(13)#, ", - ",," - ); - if (!ListFindNoCase(local.columnList, ListFirst(arguments.primaryKey))) { - local.rv = {}; - query = $query(sql = "SELECT LAST_INSERT_ID() AS lastId", argumentCollection = arguments.queryAttributes); - local.rv[$generatedKey()] = query.lastId; - return local.rv; - } - } - } - - /** - * Set a default for the string to use to order records randomly. - * Individual database adapters will override when necessary. - */ - public string function $randomOrder() { - return "RAND()"; - } - - /** - * Set a default for the string to use when inserting a record with default values only. - * Individual database adapters will override when necessary. - */ - public string function $defaultValues() { - return " DEFAULT VALUES"; - } - - /** - * Set a default for the table alias string (e.g. "users AS users2"). - * Individual database adapters will override when necessary. - */ - public string function $tableAlias(required string table, required string alias) { - return arguments.table & " AS " & arguments.alias; - } - - /** - * Internal function. - */ - public string function $tableName(required string list, required string action) { - local.rv = ""; - local.iEnd = ListLen(arguments.list); - for (local.i = 1; local.i <= local.iEnd; local.i++) { - local.item = ListGetAt(arguments.list, local.i); - - // Remove table name if specified. - if (arguments.action == "remove") { - local.item = ListRest(local.item, "."); - } - - local.rv = ListAppend(local.rv, local.item); - } - return local.rv; - } - - /** - * Internal function. - */ - public string function $columnAlias(required string list, required string action) { - local.rv = ""; - local.iEnd = ListLen(arguments.list); - for (local.i = 1; local.i <= local.iEnd; local.i++) { - local.item = ListGetAt(arguments.list, local.i); - if (Find(" AS ", local.item)) { - local.sort = ""; - if (Right(local.item, 4) == " ASC" || Right(local.item, 5) == " DESC") { - local.sort = " " & Reverse(SpanExcluding(Reverse(local.item), " ")); - local.item = Mid(local.item, 1, Len(local.item) - Len(local.sort)); - } - local.alias = Reverse(SpanExcluding(Reverse(local.item), " ")); - - // Keep or remove the alias. - if (arguments.action == "keep") { - local.item = local.alias; - } else if (arguments.action == "remove") { - local.item = Replace(local.item, " AS " & local.alias, ""); - } - - local.item &= local.sort; - } - local.rv = ListAppend(local.rv, local.item); - } - return local.rv; - } - - /** - * Remove the column aliases from the order by clause (this is passed in so that we can handle sub queries with calculated properties). - * The args argument is the original arguments passed in by reference so we just modify it without passing it back. - */ - public void function $removeColumnAliasesInOrderClause(required struct args) { - if ( - IsSimpleValue(arguments.args.sql[ArrayLen(arguments.args.sql)]) - && Left(arguments.args.sql[ArrayLen(arguments.args.sql)], 9) == "ORDER BY " - ) { - local.pos = ArrayLen(arguments.args.sql); - local.list = ReplaceNoCase(arguments.args.sql[local.pos], "ORDER BY ", ""); - arguments.args.sql[local.pos] = "ORDER BY " & $columnAlias(list = local.list, action = "remove"); - } - } - - /** - * Internal function. - */ - public boolean function $isAggregateFunction(required string sql) { - // Find "(FUNCTION(..." pattern inside the sql. - local.match = ReFind("^\([A-Z]+\(", arguments.sql, 0, true); - - // Guard against invalid match. - if (ArrayLen(local.match.pos) == 0) { - local.rv = false; - } else if (local.match.len[1] <= 2) { - local.rv = false; - } else { - // Extract and analyze the function name. - local.name = Mid(arguments.sql, local.match.pos[1] + 1, local.match.len[1] - 2); - local.rv = ListContains("AVG,COUNT,MAX,MIN,SUM", local.name) ? true : false; - } - return local.rv; - } - - /** - * The args argument is the original arguments passed in by reference so we just modify it without passing it back. - */ - public void function $addColumnsToSelectAndGroupBy(required struct args) { - if ( - IsSimpleValue(arguments.args.sql[ArrayLen(arguments.args.sql)]) - && Left(arguments.args.sql[ArrayLen(arguments.args.sql)], 8) == "ORDER BY" - && IsSimpleValue(arguments.args.sql[ArrayLen(arguments.args.sql) - 1]) - && Left(arguments.args.sql[ArrayLen(arguments.args.sql) - 1], 8) == "GROUP BY" - ) { - local.iEnd = ListLen(arguments.args.sql[ArrayLen(arguments.args.sql)]); - // cfformat-ignore-start - for (local.i = 1; local.i <= local.iEnd; local.i++) { - local.item = Trim(ReplaceNoCase(ReplaceNoCase(ReplaceNoCase(ListGetAt(arguments.args.sql[ArrayLen(arguments.args.sql)], local.i), "ORDER BY ", ""), " ASC",""), " DESC", "")); - if ( - !ListFindNoCase(ReplaceNoCase(arguments.args.sql[ArrayLen(arguments.args.sql) - 1], "GROUP BY ", ""), local.item) - && !$isAggregateFunction(local.item) - ) { - local.key = ArrayLen(arguments.args.sql) - 1; - arguments.args.sql[local.key] = ListAppend(arguments.args.sql[local.key], local.item); - } - } - // cfformat-ignore-end - } - } - - /** - * Retrieves all the column information from a table. - */ - public query function $getColumns(required string tableName) { - local.args = {}; - local.args.dataSource = variables.dataSource; - local.args.username = variables.username; - local.args.password = variables.password; - local.args.table = arguments.tableName; - if ($get("showErrorInformation")) { - try { - local.rv = $getColumnInfo(argumentCollection = local.args); - } catch (any e) { - Throw( - type = "Wheels.TableNotFound", - message = "The `#arguments.tableName#` table could not be found in the database.
`#e.message#`
`#e.detail#.`", - extendedInfo = "Add a table named `#arguments.tableName#` to your database or tell Wheels to use a different table for this model. For example you can tell a `user` model to use a table called `tbl_users` by creating a `User.cfc` file in the `app/models` folder, creating a `config` method inside it and then calling `table(""tbl_users"")` from within it. You can also issue a reload request, if you have made changes to your files, to make Wheels pick up on those changes." - ); - } - } else { - local.rv = $getColumnInfo(argumentCollection = local.args); - } - return local.rv; - } - - /** - * Internal function. - */ - public string function $getValidationType(required string type) { - switch (arguments.type) { - case "CF_SQL_DECIMAL": - case "CF_SQL_DOUBLE": - case "CF_SQL_FLOAT": - case "CF_SQL_MONEY": - case "CF_SQL_MONEY4": - case "CF_SQL_NUMERIC": - case "CF_SQL_REAL": - return "float"; - case "CF_SQL_INTEGER": - case "CF_SQL_BIGINT": - case "CF_SQL_SMALLINT": - case "CF_SQL_TINYINT": - return "integer"; - case "CF_SQL_BINARY": - case "CF_SQL_VARBINARY": - case "CF_SQL_LONGVARBINARY": - case "CF_SQL_BLOB": - case "CF_SQL_CLOB": - return "binary"; - case "CF_SQL_DATE": - case "CF_SQL_TIME": - case "CF_SQL_TIMESTAMP": - return "datetime"; - case "CF_SQL_BIT": - return "boolean"; - case "CF_SQL_ARRAY": - return "array"; - case "CF_SQL_STRUCT": - return "struct"; - case "CF_SQL_LONGVARCHAR": - case "CF_SQL_LONGNVARCHAR": - return "text"; - default: - return "string"; - } - } - - /** - * Internal function. - */ - public string function $cleanInStatementValue(required string statement) { - local.rv = arguments.statement; - local.delim = ","; - if (Find("'", local.rv)) { - local.delim = "','"; - local.rv = RemoveChars(local.rv, 1, 1); - local.rv = Reverse(RemoveChars(Reverse(local.rv), 1, 1)); - local.rv = Replace(local.rv, "''", "'", "all"); - } - return ReplaceNoCase(local.rv, local.delim, Chr(7), "all"); - } - - /** - * Internal function. - */ - public struct function $queryParams(required struct settings) { - if (!StructKeyExists(arguments.settings, "value")) { - Throw( - type = "Wheels.QueryParamValue", - message = "The value for `cfqueryparam` cannot be determined for property `#arguments.settings.property#`.
This usually happens due to a syntax error in the WHERE clause (e.g., using unquoted strings or invalid values).", - extendedInfo = "This is usually caused by a syntax error in the `WHERE` statement, such as forgetting to quote strings for example." - ); - } - local.rv = {}; - local.rv.cfsqltype = arguments.settings.type; - local.rv.value = arguments.settings.value; - if (StructKeyExists(arguments.settings, "null")) { - local.rv.null = arguments.settings.null; - } - if (StructKeyExists(arguments.settings, "scale") && arguments.settings.scale > 0) { - local.rv.scale = arguments.settings.scale; - } - if (StructKeyExists(arguments.settings, "list") && arguments.settings.list) { - local.rv.list = arguments.settings.list; - local.rv.separator = Chr(7); - local.rv.value = $cleanInStatementValue(local.rv.value); - } - return local.rv; - } - - /** - * Get information about the table using cfdbinfo. - * Individual database adapters will override when necessary. - */ - public query function $getColumnInfo( - required string table, - required string datasource, - required string username, - required string password - ) { - arguments.type = "columns"; - return $dbinfo(argumentCollection = arguments); - } - - /** - * Internal function. - */ - public string function $quoteValue(required string str, string sqlType = "CF_SQL_VARCHAR", string type) { - if (!StructKeyExists(arguments, "type")) { - arguments.type = $getValidationType(arguments.sqlType); - } - if (!ListFindNoCase("integer,float,boolean", arguments.type) || !Len(arguments.str)) { - local.rv = "'#arguments.str#'"; - } else { - local.rv = arguments.str; - } - return local.rv; - } - - /** - * Remove the maxRows argument and add a limit argument instead. - * The args argument is the original arguments passed in by reference so we just modify it without passing it back. - */ - public void function $convertMaxRowsToLimit(required struct args) { - if (StructKeyExists(arguments.args, "maxRows") && arguments.args.maxRows > 0) { - arguments.args.limit = arguments.args.maxRows; - StructDelete(arguments.args, "maxRows"); - } - } - - /** - * Internal function. - */ - public string function $comment(required string text) { - return "/* " & arguments.text & " */"; - } - - /** - * Check if SQL contains a GROUP BY clause and an aggregate function in the WHERE clause. - * If so, move the SQL to a new HAVING clause instead (after GROUP BY). - * The args argument is the original arguments passed in by reference so we just modify it without passing it back. - */ - public void function $moveAggregateToHaving(required struct args) { - local.hasAggregate = false; - local.hasGroupBy = false; - local.havingPos = 0; - local.iEnd = ArrayLen(arguments.args.sql); - for (local.i = 1; local.i <= local.iEnd; local.i++) { - if (IsSimpleValue(arguments.args.sql[local.i]) && Left(arguments.args.sql[local.i], 8) == "GROUP BY") { - local.hasGroupBy = true; - local.havingPos = local.i + 1; - } - if (IsSimpleValue(arguments.args.sql[local.i]) && $isAggregateFunction(arguments.args.sql[local.i])) { - local.hasAggregate = true; - } - } - if (local.hasGroupBy && local.hasAggregate) { - ArrayAppend(arguments.args.sql, ""); - ArrayInsertAt(arguments.args.sql, local.havingPos, "HAVING"); - local.sql = []; - local.iEnd = ArrayLen(arguments.args.sql); - for (local.i = 1; local.i <= local.iEnd; local.i++) { - if (IsSimpleValue(arguments.args.sql[local.i])) { - if ($isAggregateFunction(arguments.args.sql[local.i])) { - ArrayDeleteAt(local.sql, ArrayLen(local.sql)); - local.i++; - local.havingPos = local.havingPos - 3; - } else { - ArrayAppend(local.sql, arguments.args.sql[local.i]); - } - } else { - ArrayAppend(local.sql, arguments.args.sql[local.i]); - } - } - local.pos = local.havingPos; - local.iEnd = ArrayLen(arguments.args.sql); - for (local.i = 1; local.i <= local.iEnd; local.i++) { - if (IsSimpleValue(arguments.args.sql[local.i]) && $isAggregateFunction(arguments.args.sql[local.i])) { - if (local.pos != local.havingPos) { - local.pos++; - ArrayInsertAt(local.sql, local.pos, arguments.args.sql[local.i - 1]); - } - local.pos++; - ArrayInsertAt(local.sql, local.pos, arguments.args.sql[local.i]); - local.pos++; - ArrayInsertAt(local.sql, local.pos, arguments.args.sql[local.i + 1]); - } - } - arguments.args.sql = local.sql; - } - } - - /** - * Internal function. - */ - public struct function $performQuery( - required array sql, - required boolean parameterize, - numeric limit = 0, - numeric offset = 0, - string dataSource = variables.dataSource, - string $primaryKey = "", - string $debugName = "query" - ) { - local.queryAttributes = {}; - local.queryAttributes.dataSource = arguments.dataSource; - local.queryAttributes.username = variables.username; - local.queryAttributes.password = variables.password; - local.queryAttributes.result = "local.wheels.result"; - local.queryAttributes.name = "local." & arguments.$debugName; - if (StructKeyExists(local.queryAttributes, "username") && !Len(local.queryAttributes.username)) { - StructDelete(local.queryAttributes, "username"); - } - if (StructKeyExists(local.queryAttributes, "password") && !Len(local.queryAttributes.password)) { - StructDelete(local.queryAttributes, "password"); - } - - // Set queries in Lucee to not preserve single quotes on the entire cfquery block (we'll handle this individually in the SQL statement instead). - if ($get("serverName") == "Lucee") { - local.queryAttributes.psq = false; - } - - // Add a key as a comment for cached queries to ensure query is unique for the life of this application. - local.comment = ""; - if (StructKeyExists(arguments, "cachedwithin")) { - local.comment = $comment("cachekey:#$get("cacheKey")#"); - } - - // Overloaded arguments are settings for the query. - local.orgArgs = Duplicate(arguments); - StructDelete(local.orgArgs, "sql"); - StructDelete(local.orgArgs, "parameterize"); - StructDelete(local.orgArgs, "$debugName"); - StructDelete(local.orgArgs, "limit"); - StructDelete(local.orgArgs, "offset"); - StructDelete(local.orgArgs, "$primaryKey"); - StructAppend(local.queryAttributes, local.orgArgs); - return $executeQuery( - queryAttributes = local.queryAttributes, - sql = arguments.sql, - parameterize = arguments.parameterize, - limit = arguments.limit, - offset = arguments.offset, - comment = local.comment, - debugName = arguments.$debugName, - primaryKey = arguments.$primaryKey - ); - } - - -} diff --git a/core/src/wheels/events/onapplicationstart.cfc b/core/src/wheels/events/onapplicationstart.cfc deleted file mode 100644 index ef04aba28e..0000000000 --- a/core/src/wheels/events/onapplicationstart.cfc +++ /dev/null @@ -1,1013 +0,0 @@ -component { - - property name="Mixins" inject="id:Plugins"; - - public void function $init(struct keys = {}) { - - // Embedding values from `Application.cfc`'s `this` scope into the current component's `this` scope. - for (key in keys) { - application[key] = keys[key]; - } - - // Abort if called from incorrect file. - application.wo.$abortInvalidRequest(); - - // Setup the Wheels storage struct for the current request. - application.wo.$initializeRequestScope(); - - if (StructKeyExists(application, "wheels")) { - // Set or reset all settings but make sure to pass along the reload password between forced reloads with "reload=x". - if (StructKeyExists(application.wheels, "reloadPassword")) { - local.oldReloadPassword = application.wheels.reloadPassword; - } - // Check old environment for environment switch - if (StructKeyExists(application.wheels, "allowEnvironmentSwitchViaUrl")) { - local.allowEnvironmentSwitchViaUrl = application.wheels.allowEnvironmentSwitchViaUrl; - local.oldEnvironment = application.wheels.environment; - } - } - - application.$wheels = {}; - if (StructKeyExists(local, "oldReloadPassword")) { - application.$wheels.reloadPassword = local.oldReloadPassword; - } - - - // Check and store server engine name, throw error if using a version that we don't support. - else if (StructKeyExists(server, "boxlang")) { - application.$wheels.serverName = "BoxLang"; - application.$wheels.serverVersion = server.boxlang.version; - } else if (StructKeyExists(server, "lucee")) { - application.$wheels.serverName = "Lucee"; - application.$wheels.serverVersion = server.lucee.version; - } else { - application.$wheels.serverName = "Adobe ColdFusion"; - application.$wheels.serverVersion = server.coldfusion.productVersion; - } - application.$wheels.serverVersionMajor = ListFirst(application.$wheels.serverVersion, ".,"); - - local.upgradeTo = application.wo.$checkMinimumVersion( - engine = application.$wheels.serverName, - version = application.$wheels.serverVersion - ); - if ( - Len(local.upgradeTo) - && !StructKeyExists(application, "disableEngineCheck") - && !StructKeyExists(url, "disableEngineCheck") - ) { - local.type = "Wheels.EngineNotSupported"; - local.message = "#application.$wheels.serverName# #application.$wheels.serverVersion# is not supported by Wheels."; - if (IsBoolean(local.upgradeTo)) { - Throw(type = local.type, message = local.message, extendedInfo = "Please use Lucee or Adobe ColdFusion instead."); - } else { - Throw( - type = local.type, - message = local.message, - extendedInfo = "Please upgrade to version #local.upgradeTo# or higher." - ); - } - } - - // Copy over the CGI variables we need to the request scope. - // Since we use some of these to determine URL rewrite capabilities we need to be able to access them directly on application start for example. - request.cgi = application.wo.$cgiScope(); - - // Set up containers for routes, caches, settings etc. - // TODO remove the static version number - application.$wheels.version = "3.0.0"; - try { - application.$wheels.hostName = CreateObject("java", "java.net.InetAddress").getLocalHost().getHostName(); - } catch (any e) { - } - application.$wheels.controllers = {}; - application.$wheels.models = {}; - application.$wheels.existingHelperFiles = ""; - application.$wheels.existingLayoutFiles = ""; - application.$wheels.existingObjectFiles = ""; - application.$wheels.nonExistingHelperFiles = ""; - application.$wheels.nonExistingLayoutFiles = ""; - application.$wheels.nonExistingObjectFiles = ""; - application.$wheels.directoryFiles = {}; - application.$wheels.routes = []; - application.$wheels.resourceControllerNaming = "plural"; - application.$wheels.namedRoutePositions = {}; - application.$wheels.mixins = {}; - application.$wheels.cache = {}; - application.$wheels.cache.sql = {}; - application.$wheels.cache.image = {}; - application.$wheels.cache.main = {}; - application.$wheels.cache.action = {}; - application.$wheels.cache.page = {}; - application.$wheels.cache.partial = {}; - application.$wheels.cache.query = {}; - application.$wheels.cacheLastCulledAt = Now(); - - // Set up paths to various folders in the framework. - application.$wheels.webPath = Replace( - request.cgi.script_name, - Reverse(SpanExcluding(Reverse(request.cgi.script_name), "/")), - "" - ); - application.$wheels.rootPath = "/" & ListChangeDelims(application.$wheels.webPath, "/", "/"); - application.$wheels.rootcomponentPath = ListChangeDelims(application.$wheels.webPath, ".", "/"); - application.$wheels.wheelsComponentPath = ListAppend(application.$wheels.rootcomponentPath, "wheels", "."); - - // Check old environment to see whether we're allowed to switch configuration - application.$wheels.allowEnvironmentSwitchViaUrl = true; - if (StructKeyExists(local, "allowEnvironmentSwitchViaUrl") && !local.allowEnvironmentSwitchViaUrl) { - application.$wheels.allowEnvironmentSwitchViaUrl = false; - } - - // Set environment either from the url or the developer's environment.cfm file. - if ( - StructKeyExists(URL, "reload") - && !IsBoolean(URL.reload) - && Len(url.reload) - && StructKeyExists(application.$wheels, "reloadPassword") - && ( - !Len(application.$wheels.reloadPassword) - || (StructKeyExists(URL, "password") && URL.password == application.$wheels.reloadPassword) - ) - ) { - application.$wheels.environment = URL.reload; - } else { - application.wo.$include(template = "/config/environment.cfm"); - } - - // If we're not allowed to switch, override and replace with the old environment - if (!application.$wheels.allowEnvironmentSwitchViaUrl && StructKeyExists(local, "oldEnvironment")) { - application.$wheels.environment = local.oldEnvironment; - } - - // Rewrite settings based on web server rewrite capabilites. - application.$wheels.rewriteFile = "index.cfm"; - if (Right(request.cgi.script_name, 12) == "/" & application.$wheels.rewriteFile) { - application.$wheels.URLRewriting = "On"; - } else if (Len(request.cgi.path_info)) { - application.$wheels.URLRewriting = "Partial"; - } else { - application.$wheels.URLRewriting = "Off"; - } - - // Set datasource name to same as the folder the app resides in unless the developer has set it with the global setting already. - if (StructKeyExists(application, "dataSource")) { - application.$wheels.dataSourceName = application.dataSource; - } else { - application.$wheels.dataSourceName = LCase( - ListLast(GetDirectoryFromPath(GetBaseTemplatePath()), Right(GetDirectoryFromPath(GetBaseTemplatePath()), 1)) - ); - } - - // Set the coreTestDatasourceName to the application dataSourceName if it doesn't exits - if (!StructKeyExists(application.$wheels, "coreTestDataSourceName")) { - application.$wheels.coreTestDataSourceName = application.$wheels.dataSourceName; - } - - application.$wheels.dataSourceUserName = ""; - application.$wheels.dataSourcePassword = ""; - application.$wheels.transactionMode = "commit"; - - // Enable or disable major components - application.$wheels.enablePluginsComponent = true; - application.$wheels.enableMigratorComponent = true; - application.$wheels.enablePublicComponent = false; - if (application.$wheels.environment == "development") { - application.$wheels.enablePublicComponent = true; - } - - // Create migrations object and set default settings. - application.$wheels.autoMigrateDatabase = false; - application.$wheels.migratorTableName = "c_o_r_e_migrator_versions"; - application.$wheels.createMigratorTable = true; - application.$wheels.writeMigratorSQLFiles = false; - application.$wheels.migratorObjectCase = "lower"; - application.$wheels.allowMigrationDown = false; - application.$wheels.migrationLevel = 1; - if (application.$wheels.environment == "development") { - application.$wheels.allowMigrationDown = true; - } - - // Cache settings that are always turned on regardless of mode setting. - application.$wheels.cacheControllerConfig = true; - application.$wheels.cacheDatabaseSchema = true; - application.$wheels.cacheModelConfig = true; - application.$wheels.cachePlugins = true; - application.$wheels.cacheFileChecking = true; - - // Cache settings that are turned off in development mode only. - application.$wheels.cacheActions = false; - application.$wheels.cacheImages = false; - application.$wheels.cachePages = false; - application.$wheels.cachePartials = false; - application.$wheels.cacheQueries = false; - if (application.$wheels.environment != "development") { - application.$wheels.cacheActions = true; - application.$wheels.cacheImages = true; - application.$wheels.cachePages = true; - application.$wheels.cachePartials = true; - application.$wheels.cacheQueries = true; - } - - // Other caching settings. - application.$wheels.maximumItemsToCache = 5000; - application.$wheels.cacheCullPercentage = 10; - application.$wheels.cacheCullInterval = 5; - application.$wheels.cacheDatePart = "n"; - application.$wheels.defaultCacheTime = 60; - application.$wheels.clearQueryCacheOnReload = true; - application.$wheels.clearTemplateCacheOnReload = true; - application.$wheels.cacheQueriesDuringRequest = true; - - // CSRF protection settings. - application.$wheels.csrfStore = "session"; - application.$wheels.csrfCookieEncryptionAlgorithm = "AES"; - application.$wheels.csrfCookieEncryptionSecretKey = ""; - application.$wheels.csrfCookieEncryptionEncoding = "Base64"; - application.$wheels.csrfCookieName = "_wheels_authenticity"; - application.$wheels.csrfCookieDomain = ""; - application.$wheels.csrfCookieEncodeValue = ""; - application.$wheels.csrfCookieHttpOnly = ""; - application.$wheels.csrfCookiePath = "/"; - application.$wheels.csrfCookiePreserveCase = ""; - application.$wheels.csrfCookieSecure = ""; - - // CORS (Cross-Origin Resource Sharing) settings. - application.$wheels.allowCorsRequests = false; - application.$wheels.accessControlAllowOrigin = "*"; - application.$wheels.accessControlAllowMethods = "GET, POST, PATCH, PUT, DELETE, OPTIONS"; - application.$wheels.accessControlAllowMethodsByRoute = false; - application.$wheels.accessControlAllowCredentials = false; - application.$wheels.accessControlAllowHeaders = "Origin, Content-Type, X-Auth-Token, X-Requested-By, X-Requested-With"; - - // Debugging and error settings. - application.$wheels.showDebugInformation = true; - application.$wheels.showErrorInformation = true; - application.$wheels.sendEmailOnError = false; - application.$wheels.errorEmailSubject = "Error"; - application.$wheels.excludeFromErrorEmail = ""; - application.$wheels.errorEmailToAddress = ""; - application.$wheels.errorEmailFromAddress = ""; - application.$wheels.includeErrorInEmailSubject = true; - if (Find(".", request.cgi.server_name)) { - application.$wheels.errorEmailAddress = "webmaster@" - & Reverse(ListGetAt(Reverse(request.cgi.server_name), 2, ".")) - & "." - & Reverse(ListGetAt(Reverse(request.cgi.server_name), 1, ".")); - } else { - application.$wheels.errorEmailAddress = ""; - } - if (application.$wheels.environment == "production") { - application.$wheels.showErrorInformation = false; - application.$wheels.sendEmailOnError = true; - } - if (application.$wheels.environment != "development") { - application.$wheels.showDebugInformation = false; - } - - // IP based restriction settings - application.$wheels.debugAccessIPs = []; - application.$wheels.allowIPBasedDebugAccess = false; - - // Asset path settings. - // assetPaths can be struct with two keys, http and https, if no https struct key, http is used for secure and non-secure. - // Example: {http="asset0.domain1.com,asset2.domain1.com,asset3.domain1.com", https="secure.domain1.com"} - application.$wheels.assetQueryString = false; - application.$wheels.assetPaths = false; - if (application.$wheels.environment != "development") { - application.$wheels.assetQueryString = true; - } - - // Configurable paths. - application.$wheels.eventPath = "/app/events"; - application.$wheels.filePath = "files"; - application.$wheels.imagePath = "images"; - application.$wheels.javascriptPath = "javascripts"; - application.$wheels.modelPath = "/app/models"; - application.$wheels.pluginPath = "/plugins"; - application.$wheels.pluginComponentPath = "/plugins"; - application.$wheels.stylesheetPath = "stylesheets"; - application.$wheels.viewPath = "/app/views"; - application.$wheels.controllerPath = "/app/controllers"; - - // Test framework settings. - application.$wheels.validateTestPackageMetaData = true; - application.$wheels.restoreTestRunnerApplicationScope = true; - - // Miscellaneous settings. - application.$wheels.encodeURLs = true; - application.$wheels.encodeHtmlTags = true; - application.$wheels.encodeHtmlAttributes = true; - application.$wheels.uncountables = "advice,air,blood,deer,equipment,fish,food,furniture,garbage,graffiti,grass,homework,housework,information,knowledge,luggage,mathematics,meat,milk,money,music,pollution,research,rice,sand,series,sheep,soap,software,species,sugar,traffic,transportation,travel,trash,water,feedback"; - application.$wheels.irregulars = { - child = "children", - foot = "feet", - man = "men", - move = "moves", - person = "people", - sex = "sexes", - tooth = "teeth", - woman = "women" - }; - application.$wheels.tableNamePrefix = ""; - application.$wheels.obfuscateURLs = false; - application.$wheels.reloadPassword = ""; - application.$wheels.redirectAfterReload = false; - application.$wheels.softDeleteProperty = "deletedAt"; - application.$wheels.timeStampOnCreateProperty = "createdAt"; - application.$wheels.timeStampOnUpdateProperty = "updatedAt"; - application.$wheels.timeStampMode = "utc"; - application.$wheels.ipExceptions = ""; - application.$wheels.overwritePlugins = true; - application.$wheels.deletePluginDirectories = true; - application.$wheels.loadIncompatiblePlugins = true; - application.$wheels.automaticValidations = true; - application.$wheels.setUpdatedAtOnCreate = true; - application.$wheels.useExpandedColumnAliases = false; - application.$wheels.lowerCaseTableNames = false; - application.$wheels.modelRequireConfig = false; - application.$wheels.showIncompatiblePlugins = true; - application.$wheels.booleanAttributes = "allowfullscreen,async,autofocus,autoplay,checked,compact,controls,declare,default,defaultchecked,defaultmuted,defaultselected,defer,disabled,draggable,enabled,formnovalidate,hidden,indeterminate,inert,ismap,itemscope,loop,multiple,muted,nohref,noresize,noshade,novalidate,nowrap,open,pauseonexit,readonly,required,reversed,scoped,seamless,selected,sortable,spellcheck,translate,truespeed,typemustmatch,visible"; - if (ListFindNoCase("production,maintenance", application.$wheels.environment)) { - application.$wheels.redirectAfterReload = true; - } - application.$wheels.resetPropertiesStructKeyCase = true; - - // If session management is enabled in the application we default to storing Flash data in the session scope, if not we use a cookie. - if (StructKeyExists(application, "sessionManagement") && application.sessionManagement) { - application.$wheels.sessionManagement = true; - application.$wheels.flashStorage = "session"; - } else { - application.$wheels.sessionManagement = false; - application.$wheels.flashStorage = "cookie"; - } - - // Additional configurable flash options - application.$wheels.flashAppend = false; - - // Possible formats for provides functionality. - application.$wheels.formats = {}; - application.$wheels.formats.html = "text/html"; - application.$wheels.formats.xml = "text/xml"; - application.$wheels.formats.json = "application/json"; - application.$wheels.formats.csv = "text/csv"; - application.$wheels.formats.pdf = "application/pdf"; - application.$wheels.formats.xls = "application/vnd.ms-excel"; - - // Function defaults. - application.$wheels.functions = {}; - application.$wheels.functions.autoLink = {link = "all", encode = true}; - application.$wheels.functions.average = {distinct = false, parameterize = true, ifNull = ""}; - application.$wheels.functions.belongsTo = {joinType = "inner"}; - application.$wheels.functions.buttonTo = { - onlyPath = true, - host = "", - protocol = "", - port = 0, - text = "", - image = "", - encode = true - }; - application.$wheels.functions.buttonTag = { - type = "submit", - value = "save", - content = "Save changes", - image = "", - prepend = "", - append = "", - encode = true - }; - application.$wheels.functions.caches = {time = 60, static = false}; - application.$wheels.functions.checkBox = { - label = "useDefaultLabel", - labelPlacement = "around", - prepend = "", - append = "", - prependToLabel = "", - appendToLabel = "", - errorElement = "span", - errorClass = "field-with-errors", - checkedValue = 1, - unCheckedValue = 0, - encode = true - }; - application.$wheels.functions.checkBoxTag = { - label = "", - labelPlacement = "around", - prepend = "", - append = "", - prependToLabel = "", - appendToLabel = "", - value = 1, - encode = true - }; - application.$wheels.functions.count = {parameterize = true}; - application.$wheels.functions.csrfMetaTags = {encode = true}; - application.$wheels.functions.create = {parameterize = true, reload = false}; - application.$wheels.functions.dateSelect = { - label = false, - labelPlacement = "around", - prepend = "", - append = "", - prependToLabel = "", - appendToLabel = "", - errorElement = "span", - errorClass = "field-with-errors", - includeBlank = false, - order = "month,day,year", - separator = " ", - startYear = Year(Now()) - 5, - endYear = Year(Now()) + 5, - monthDisplay = "names", - monthNames = "January,February,March,April,May,June,July,August,September,October,November,December", - monthAbbreviations = "Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec", - encode = true - }; - application.$wheels.functions.dateSelectTags = { - label = "", - labelPlacement = "around", - prepend = "", - append = "", - prependToLabel = "", - appendToLabel = "", - includeBlank = false, - order = "month,day,year", - separator = " ", - startYear = Year(Now()) - 5, - endYear = Year(Now()) + 5, - monthDisplay = "names", - monthNames = "January,February,March,April,May,June,July,August,September,October,November,December", - monthAbbreviations = "Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec", - encode = true - }; - application.$wheels.functions.dateTimeSelect = { - label = false, - labelPlacement = "around", - prepend = "", - append = "", - prependToLabel = "", - appendToLabel = "", - errorElement = "span", - errorClass = "field-with-errors", - includeBlank = false, - dateOrder = "month,day,year", - dateSeparator = " ", - startYear = Year(Now()) - 5, - endYear = Year(Now()) + 5, - monthDisplay = "names", - monthNames = "January,February,March,April,May,June,July,August,September,October,November,December", - monthAbbreviations = "Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec", - timeOrder = "hour,minute,second", - timeSeparator = ":", - minuteStep = 1, - secondStep = 1, - separator = " - ", - twelveHour = false, - encode = true - }; - application.$wheels.functions.dateTimeSelectTags = { - label = "", - labelPlacement = "around", - prepend = "", - append = "", - prependToLabel = "", - appendToLabel = "", - includeBlank = false, - dateOrder = "month,day,year", - dateSeparator = " ", - startYear = Year(Now()) - 5, - endYear = Year(Now()) + 5, - monthDisplay = "names", - monthNames = "January,February,March,April,May,June,July,August,September,October,November,December", - monthAbbreviations = "Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec", - timeOrder = "hour,minute,second", - timeSeparator = ":", - minuteStep = 1, - secondStep = 1, - separator = " - ", - twelveHour = false, - encode = true - }; - application.$wheels.functions.daySelectTag = { - label = "", - labelPlacement = "around", - prepend = "", - append = "", - prependToLabel = "", - appendToLabel = "", - includeBlank = false, - encode = true - }; - application.$wheels.functions.delete = {parameterize = true}; - application.$wheels.functions.deleteAll = {reload = false, parameterize = true, instantiate = false}; - application.$wheels.functions.deleteByKey = {reload = false}; - application.$wheels.functions.deleteOne = {reload = false}; - application.$wheels.functions.distanceOfTimeInWords = {includeSeconds = false}; - application.$wheels.functions.endFormTag = {prepend = "", append = "", encode = true}; - application.$wheels.functions.errorMessageOn = { - prependText = "", - appendText = "", - wrapperElement = "span", - class = "error-message", - encode = true - }; - application.$wheels.functions.errorMessagesFor = { - class = "error-messages", - showDuplicates = true, - encode = true, - includeAssociations = true - }; - application.$wheels.functions.excerpt = {radius = 100, excerptString = "..."}; - application.$wheels.functions.exists = {reload = false, parameterize = true}; - application.$wheels.functions.fileField = { - label = "useDefaultLabel", - labelPlacement = "around", - prepend = "", - append = "", - prependToLabel = "", - appendToLabel = "", - errorElement = "span", - errorClass = "field-with-errors", - encode = true - }; - application.$wheels.functions.fileFieldTag = { - label = "", - labelPlacement = "around", - prepend = "", - append = "", - prependToLabel = "", - appendToLabel = "", - encode = true - }; - application.$wheels.functions.findAll = { - reload = false, - parameterize = true, - perPage = 10, - order = "", - group = "", - returnAs = "query", - returnIncluded = true - }; - application.$wheels.functions.findByKey = {reload = false, parameterize = true, returnAs = "object"}; - application.$wheels.functions.findOne = {reload = false, parameterize = true, returnAs = "object"}; - application.$wheels.functions.flashKeep = {}; - application.$wheels.functions.flashMessages = { - class = "flash-messages", - includeEmptyContainer = "false", - encode = true - }; - application.$wheels.functions.hasMany = {joinType = "outer", dependent = false}; - application.$wheels.functions.hasManyCheckBox = {encode = true}; - application.$wheels.functions.hasManyRadioButton = {encode = true}; - application.$wheels.functions.hasOne = {joinType = "outer", dependent = false}; - application.$wheels.functions.hiddenField = {encode = true}; - application.$wheels.functions.hiddenFieldTag = {encode = true}; - application.$wheels.functions.highlight = {delimiter = ",", tag = "span", class = "highlight", encode = true}; - application.$wheels.functions.hourSelectTag = { - label = "", - labelPlacement = "around", - prepend = "", - append = "", - prependToLabel = "", - appendToLabel = "", - includeBlank = false, - twelveHour = false, - encode = true - }; - application.$wheels.functions.imageTag = {onlyPath = true, host = "", protocol = "", port = 0, encode = true}; - application.$wheels.functions.includePartial = {layout = "", spacer = "", dataFunction = true}; - application.$wheels.functions.javaScriptIncludeTag = {type = "text/javascript", head = false, encode = true}; - application.$wheels.functions.linkTo = {onlyPath = true, host = "", protocol = "", port = 0, encode = true}; - application.$wheels.functions.mailTo = {encode = true}; - application.$wheels.functions.maximum = {parameterize = true, ifNull = ""}; - application.$wheels.functions.minimum = {parameterize = true, ifNull = ""}; - application.$wheels.functions.minuteSelectTag = { - label = "", - labelPlacement = "around", - prepend = "", - append = "", - prependToLabel = "", - appendToLabel = "", - includeBlank = false, - minuteStep = 1, - encode = true - }; - application.$wheels.functions.monthSelectTag = { - label = "", - labelPlacement = "around", - prepend = "", - append = "", - prependToLabel = "", - appendToLabel = "", - includeBlank = false, - monthDisplay = "names", - monthNames = "January,February,March,April,May,June,July,August,September,October,November,December", - monthAbbreviations = "Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec", - encode = true - }; - application.$wheels.functions.nestedProperties = { - autoSave = true, - allowDelete = false, - sortProperty = "", - rejectIfBlank = "" - }; - application.$wheels.functions.paginationLinks = { - windowSize = 2, - alwaysShowAnchors = true, - anchorDivider = " ... ", - linkToCurrentPage = false, - prepend = "", - append = "", - prependToPage = "", - addActiveClassToPrependedParent = false, - prependOnFirst = true, - prependOnAnchor = true, - appendToPage = "", - appendOnLast = true, - appendOnAnchor = true, - classForCurrent = "", - name = "page", - showSinglePage = false, - pageNumberAsParam = true, - encode = true - }; - application.$wheels.functions.passwordField = { - label = "useDefaultLabel", - labelPlacement = "around", - prepend = "", - append = "", - prependToLabel = "", - appendToLabel = "", - errorElement = "span", - errorClass = "field-with-errors", - encode = true - }; - application.$wheels.functions.passwordFieldTag = { - label = "", - labelPlacement = "around", - prepend = "", - append = "", - prependToLabel = "", - appendToLabel = "", - encode = true - }; - application.$wheels.functions.processRequest = {method = "get", returnAs = "", rollback = false}; - application.$wheels.functions.protectsFromForgery = {with = "exception", only = "", except = ""}; - application.$wheels.functions.radioButton = { - label = "useDefaultLabel", - labelPlacement = "around", - prepend = "", - append = "", - prependToLabel = "", - appendToLabel = "", - errorElement = "span", - errorClass = "field-with-errors", - encode = true - }; - application.$wheels.functions.radioButtonTag = { - label = "", - labelPlacement = "around", - prepend = "", - append = "", - prependToLabel = "", - appendToLabel = "", - encode = true - }; - application.$wheels.functions.redirectTo = { - onlyPath = true, - host = "", - protocol = "", - port = 0, - addToken = false, - statusCode = 302, - delay = false, - encode = true - }; - application.$wheels.functions.renderView = {layout = ""}; - application.$wheels.functions.renderWith = {layout = ""}; - application.$wheels.functions.renderPartial = {layout = "", dataFunction = true}; - application.$wheels.functions.save = {parameterize = true, reload = false}; - application.$wheels.functions.secondSelectTag = { - label = "", - labelPlacement = "around", - prepend = "", - append = "", - prependToLabel = "", - appendToLabel = "", - includeBlank = false, - secondStep = 1, - encode = true - }; - application.$wheels.functions.select = { - label = "useDefaultLabel", - labelPlacement = "around", - prepend = "", - append = "", - prependToLabel = "", - appendToLabel = "", - errorElement = "span", - errorClass = "field-with-errors", - includeBlank = false, - valueField = "", - textField = "", - encode = true - }; - application.$wheels.functions.selectTag = { - label = "", - labelPlacement = "around", - prepend = "", - append = "", - prependToLabel = "", - appendToLabel = "", - includeBlank = false, - multiple = false, - valueField = "", - textField = "", - encode = true - }; - application.$wheels.functions.sendEmail = { - layout = false, - detectMultipart = true, - from = "", - to = "", - subject = "", - deliver = true - }; - application.$wheels.functions.sendFile = {disposition = "attachment", deliver = true}; - application.$wheels.functions.simpleFormat = {wrap = true, encode = true}; - application.$wheels.functions.startFormTag = { - onlyPath = true, - host = "", - protocol = "", - port = 0, - method = "post", - multipart = false, - prepend = "", - append = "", - encode = true - }; - application.$wheels.functions.stripLinks = {encode = true}; - application.$wheels.functions.stripTags = {encode = true}; - application.$wheels.functions.styleSheetLinkTag = {type = "text/css", media = "all", head = false, encode = true}; - application.$wheels.functions.submitTag = { - value = "Save changes", - image = "", - prepend = "", - append = "", - encode = true - }; - application.$wheels.functions.sum = {distinct = false, parameterize = true, ifNull = ""}; - application.$wheels.functions.textArea = { - label = "useDefaultLabel", - labelPlacement = "around", - prepend = "", - append = "", - prependToLabel = "", - appendToLabel = "", - errorElement = "span", - errorClass = "field-with-errors", - encode = true - }; - application.$wheels.functions.textAreaTag = { - label = "", - labelPlacement = "around", - prepend = "", - append = "", - prependToLabel = "", - appendToLabel = "", - encode = true - }; - application.$wheels.functions.textField = { - label = "useDefaultLabel", - labelPlacement = "around", - prepend = "", - append = "", - prependToLabel = "", - appendToLabel = "", - errorElement = "span", - errorClass = "field-with-errors", - encode = true - }; - application.$wheels.functions.textFieldTag = { - label = "", - labelPlacement = "around", - prepend = "", - append = "", - prependToLabel = "", - appendToLabel = "", - encode = true - }; - application.$wheels.functions.timeAgoInWords = {includeSeconds = false}; - application.$wheels.functions.timeSelect = { - label = false, - labelPlacement = "around", - prepend = "", - append = "", - prependToLabel = "", - appendToLabel = "", - errorElement = "span", - errorClass = "field-with-errors", - includeBlank = false, - order = "hour,minute,second", - separator = ":", - minuteStep = 1, - secondStep = 1, - twelveHour = false, - encode = true - }; - application.$wheels.functions.timeSelectTags = { - label = "", - labelPlacement = "around", - prepend = "", - append = "", - prependToLabel = "", - appendToLabel = "", - includeBlank = false, - order = "hour,minute,second", - separator = ":", - minuteStep = 1, - secondStep = 1, - twelveHour = false, - encode = true - }; - application.$wheels.functions.timeUntilInWords = {includeSeconds = false}; - application.$wheels.functions.toggle = {save = true}; - application.$wheels.functions.truncate = {length = 30, truncateString = "..."}; - application.$wheels.functions.update = {parameterize = true, reload = false}; - application.$wheels.functions.updateAll = {reload = false, parameterize = true, instantiate = false}; - application.$wheels.functions.updateByKey = {reload = false}; - application.$wheels.functions.updateOne = {reload = false}; - application.$wheels.functions.updateProperty = {parameterize = true}; - application.$wheels.functions.URLFor = {onlyPath = true, host = "", protocol = "", port = 0, encode = true}; - application.$wheels.functions.validatesConfirmationOf = {message = "[property] should match confirmation"}; - application.$wheels.functions.validatesExclusionOf = {message = "[property] is reserved", allowBlank = false}; - application.$wheels.functions.validatesFormatOf = {message = "[property] is invalid", allowBlank = false}; - application.$wheels.functions.validatesInclusionOf = { - message = "[property] is not included in the list", - allowBlank = false - }; - application.$wheels.functions.validatesLengthOf = { - message = "[property] is the wrong length", - allowBlank = false, - exactly = 0, - maximum = 0, - minimum = 0, - within = "" - }; - application.$wheels.functions.validatesNumericalityOf = { - message = "[property] is not a number", - allowBlank = false, - onlyInteger = false, - odd = "", - even = "", - greaterThan = "", - greaterThanOrEqualTo = "", - equalTo = "", - lessThan = "", - lessThanOrEqualTo = "" - }; - application.$wheels.functions.validatesPresenceOf = {message = "[property] can't be empty"}; - application.$wheels.functions.validatesUniquenessOf = { - message = "[property] has already been taken", - allowBlank = false - }; - application.$wheels.functions.verifies = {handler = ""}; - application.$wheels.functions.wordTruncate = {length = 5, truncateString = "..."}; - application.$wheels.functions.yearSelectTag = { - label = "", - labelPlacement = "around", - prepend = "", - append = "", - prependToLabel = "", - appendToLabel = "", - includeBlank = false, - startYear = Year(Now()) - 5, - endYear = Year(Now()) + 5, - encode = true - }; - - // Mime types. - application.$wheels.mimetypes = { - txt = "text/plain", - gif = "image/gif", - jpg = "image/jpg", - jpeg = "image/jpg", - pjpeg = "image/jpg", - png = "image/png", - wav = "audio/wav", - mp3 = "audio/mpeg3", - pdf = "application/pdf", - zip = "application/zip", - ppt = "application/powerpoint", - pptx = "application/powerpoint", - doc = "application/word", - docx = "application/word", - xls = "application/excel", - xlsx = "application/excel" - }; - - // Set a flag to indicate that all settings have been loaded. - application.$wheels.initialized = true; - - // Load general developer settings first, then override with environment specific ones. - application.wo.$include(template = "/config/settings.cfm"); - if (FileExists(ExpandPath("/config/#application.$wheels.environment#/settings.cfm"))) { - application.wo.$include(template = "/config/#application.$wheels.environment#/settings.cfm"); - } - - // Clear query (cfquery) and page (cfcache) caches. - if (application.$wheels.clearQueryCacheOnReload or !StructKeyExists(application.$wheels, "cacheKey")) { - application.$wheels.cacheKey = Hash(CreateUUID()); - } - if (application.$wheels.clearTemplateCacheOnReload) { - application.wo.$cache(action = "flush"); - } - - // Add all public controller / view methods to a list of methods that you should not be allowed to call as a controller action from the url. - local.allowedGlobalMethods = "get,set,mapper"; - // local.protectedControllerMethods = StructKeyList( - // application.wo.$createObjectFromRoot(path = "wheels", fileName = "Controller", method = "$initControllerClass") - // ); - application.$wheels.protectedControllerMethods = ""; - // local.iEnd = ListLen(local.protectedControllerMethods); - // for (local.i = 1; local.i <= local.iEnd; local.i++) { - // local.method = ListGetAt(local.protectedControllerMethods, local.i); - // if (Left(local.method, 1) != "$" && !ListFindNoCase(local.allowedGlobalMethods, local.method)) { - // application.$wheels.protectedControllerMethods = ListAppend( - // application.$wheels.protectedControllerMethods, - // local.method - // ); - // } - // } - - // Enable the main GUI Component - if (application.$wheels.enablePublicComponent) { - application.$wheels.public = application.wo.$createObjectFromRoot(path = "wheels", fileName = "Public", method = "$init"); - } - - // Reload the plugins each time we reload the application. - if (application.$wheels.enablePluginsComponent) { - application.wo.$loadPlugins(); - } - - // Allow developers to inject plugins into the application variables scope. - if (!StructIsEmpty(application.$wheels.mixins)) { - if (structKeyExists(server, "boxlang")) { - variables.this = this; - } - Mixins.$initializeMixins(variables); - } - - // Create the mapper that will handle creating routes. - // Needs to be before $loadRoutes and after $loadPlugins. - application.$wheels.mapper = application.wo.$createObjectFromRoot(path = "wheels", fileName = "Mapper", method = "$init"); - - // Load developer routes and adds the default Wheels routes (unless the developer has specified not to). - application.wo.$loadRoutes(); - - // Create the dispatcher that will handle all incoming requests. - application.$wheels.dispatch = application.wo.$createObjectFromRoot(path = "wheels", fileName = "Dispatch", method = "$init"); - - // Assign it all to the application scope in one atomic call. - application.wheels = application.$wheels; - StructDelete(application, "$wheels"); - - // Enable the migrator component - if (application.wheels.enableMigratorComponent) { - application.wheels.migrator = application.wo.$createObjectFromRoot(path = "wheels", fileName = "Migrator", method = "init"); - } - - // Run the developer's on application start code. - application.wo.$include(template = "#application.wheels.eventPath#/onapplicationstart.cfm"); - - // Auto Migrate Database if requested - if (application.wheels.enableMigratorComponent && application.wheels.autoMigrateDatabase) { - application.wheels.migrator.migrateToLatest(); - } - - // Redirect away from reloads on GET requests. - if (application.wheels.redirectAfterReload && StructKeyExists(url, "reload") && cgi.request_method == "get") { - if (StructKeyExists(cgi, "path_info") && Len(cgi.path_info)) { - local.url = cgi.path_info; - } else if (StructKeyExists(cgi, "path_info")) { - local.url = "/"; - } else { - local.url = cgi.script_name; - } - local.oldQueryString = ListToArray(cgi.query_string, "&"); - local.newQueryString = []; - local.iEnd = ArrayLen(local.oldQueryString); - for (local.i = 1; local.i <= local.iEnd; local.i++) { - local.keyValue = local.oldQueryString[local.i]; - local.key = ListFirst(local.keyValue, "="); - if (!ListFindNoCase("reload,password,lock", local.key)) { - ArrayAppend(local.newQueryString, local.keyValue); - } - } - if (ArrayLen(local.newQueryString)) { - local.queryString = ArrayToList(local.newQueryString, "&"); - local.url = "#local.url#?#local.queryString#"; - } - $location(url = local.url, addToken = false); - } - } -} diff --git a/core/src/wheels/events/onerror/cfmlerror.cfm b/core/src/wheels/events/onerror/cfmlerror.cfm deleted file mode 100644 index 55c1df158c..0000000000 --- a/core/src/wheels/events/onerror/cfmlerror.cfm +++ /dev/null @@ -1,156 +0,0 @@ - - -
-

Summary

-

- Error: -
- - #arguments.exception.rootcause.message# - -
#arguments.exception.rootcause.detail# -
- - A root cause was not provided. -
-

- - - - - - - - -

- Location: -
- - - - - - - #local.template#:#local.tagContext[local.pos].line#
-
-

-
- -

- URL: -
- https://#cgi.server_name##Replace(cgi.script_name, "/#application.wheels.rewriteFile#", "")##request.cgi.path_info##cgi.path_info#?#cgi.query_string# -

-
- -

- Referrer: -
#cgi.http_referer# -

-
-

- Method: -
#cgi.request_method# -

-

- IP Address: -
#cgi.remote_addr# -

- -

- Host Name: -
#application.wheels.hostName# -

-
-

- User Agent: -
#cgi.http_user_agent# -

-

- Date & Time: -
#DateFormat(Now(), "MMMM D, YYYY")# at #TimeFormat(Now(), "h:MM TT")# -

- - - - - - - - -

Details

- - - - - - -

- #local.scopeName# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

-								
-							
-

-
- -
-
-
-
- -
diff --git a/core/src/wheels/events/onerror/wheelserror.cfm b/core/src/wheels/events/onerror/wheelserror.cfm deleted file mode 100644 index efd683e4ae..0000000000 --- a/core/src/wheels/events/onerror/wheelserror.cfm +++ /dev/null @@ -1,78 +0,0 @@ - - -
-

#arguments.wheelsError.type#

-

- #ReReplace(arguments.wheelsError.message, "`([^`]*)`", "\1", "all")# -

- -

Suggested action

- \1", "all")> - - ([a-z]*)\(\)", - '\1()' - )> - - -

#local.info#

-
- - - - - - - - - -

- - - - - - #local.pos#: #HtmlEditFormat(local.i)# - - #local.pos#: #HtmlEditFormat(local.i)# - - #Chr(13)##Chr(10)#
-
-
-
-

-
- - - -
- -

Error location

-

Line #arguments.wheelsError.tagContext[local.errorPos].line# in #Replace(arguments.wheelsError.tagContext[local.errorPos].template, local.path, "")#

- #local.fileContents# -
- -
-
- -

Tag context

-

- Error thrown on line #arguments.wheelsError.tagContext[2].line# in #Replace(arguments.wheelsError.tagContext[2].template, local.path, "")#
- - - - called from line #arguments.wheelsError.tagContext[local.i].line# in #Replace(arguments.wheelsError.tagContext[local.i].template, local.path, "")#
-
-

-
-
- -
diff --git a/core/src/wheels/events/onrequestend/debug.cfm b/core/src/wheels/events/onrequestend/debug.cfm deleted file mode 100644 index 3ac1d6310c..0000000000 --- a/core/src/wheels/events/onrequestend/debug.cfm +++ /dev/null @@ -1,369 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - #local.css# -
- - - - - - - - - - - - - - - - - -
- Info - - Routes - - API - - Guides - - Tests - - App Tests(Legacy) - - Core Tests(Legacy) - - Migrator - - Plugins -
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - Warnings: - - - - - - The #local.i# plugin may be incompatible with this version of Wheels, please look for a compatible version of the plugin
-
-
- - - The #ListFirst(local.i, "|")# plugin needs the following plugins - to work properly: #needs#
-
-
-
-
- Git Branch: - #local.gitbranch#
- Application: - - #application.applicationName# - - [Reload - ] - -
- Framework: - - Wheels - #$get("version")# -
- Environment: - - #capitalize($get("environment"))# - - [ - - - - - #capitalize(local.i)# - , - - - ] - -
- Host Name: - #$get("hostName")#
- CFML Engine: - #$get("serverName")##$get("serverVersion")#
- Data Source: - #capitalize($get("dataSourceName"))#
- Database Adapter: - #$get("adapterName")#
- URL Rewriting: - #capitalize($get("URLRewriting"))#
- URL Obfuscation: - - - OnOff - -
- Plugins: - - - - - - - #local.i# - - #$get("pluginMeta")[local.i]['version']# - - - - [ - Run Tests - , - View Tests - ] - - -
-
-
- - None -
-
- Route: - #capitalize(request.wheels.params.route)#
- Controller: - #capitalize(request.wheels.params.controller)#
- Action: - #capitalize(request.wheels.params.action)#
- Key(s): - #request.wheels.params.key#
- Parameters: - - - - - - - #LCase(local.i)# = #SerializeJSON(request.wheels.params[local.i])#
- - #LCase(local.i)# = #SerializeJSON(request.wheels.params[local.i])#
- - #LCase(local.i)# = #request.wheels.params[local.i]#
-
-
-
- None -
- Execution Time: - - #request.wheels.execution.total#ms - ( - - - - - - , - #LCase(local.key)# ~#request.wheels.execution[local.key]#ms - - - - ) - -
-
-
- diff --git a/core/src/wheels/mapper/mapping.cfc b/core/src/wheels/mapper/mapping.cfc deleted file mode 100644 index b67fc7a301..0000000000 --- a/core/src/wheels/mapper/mapping.cfc +++ /dev/null @@ -1,107 +0,0 @@ -component { - /** - * Internal function. - */ - public struct function $draw( - boolean restful = true, - boolean methods = arguments.restful, - boolean mapFormat = variables.mapFormat - ) { - variables.restful = arguments.restful; - variables.methods = arguments.restful || arguments.methods; - variables.mapFormat = arguments.mapFormat; - - // Start with clean scope stack that is locked for race conditions. - $simpleLock(name = "mapper.reset", timeout = 5, type = "exclusive", execute = "$resetScopeStack"); - - return this; - } - - /** - * Call this to end a nested routing block or the entire route configuration. This method is chained on a sequence of routing mapper method calls started by `mapper()`. - * - * [section: Configuration] - * [category: Routing] - */ - public struct function end() { - local.formatPattern = ""; - - if (StructKeyExists(variables.scopeStack[1], "mapFormat") && variables.scopeStack[1].mapFormat) { - local.formatPattern = "(.[format])"; - } - - // If last action was a plural resource, set up its RESTful routes. - if (variables.scopeStack[1].$call == "resources") { - collection(); - - if (ListFind(variables.scopeStack[1].actions, "index")) { - get(pattern = local.formatPattern, action = "index"); - } - if (ListFindNoCase(variables.scopeStack[1].actions, "create")) { - post(pattern = local.formatPattern, action = "create"); - } - - end(); - - if (ListFindNoCase(variables.scopeStack[1].actions, "new")) { - scope(path = variables.scopeStack[1].collectionPath, $call = "new"); - get(pattern = "new#local.formatPattern#", action = "new", name = "new"); - end(); - } - - member(); - - if (ListFind(variables.scopeStack[1].actions, "edit")) { - get(pattern = "edit#local.formatPattern#", action = "edit", name = "edit"); - } - if (ListFind(variables.scopeStack[1].actions, "show")) { - get(pattern = local.formatPattern, action = "show"); - } - if (ListFind(variables.scopeStack[1].actions, "update")) { - patch(pattern = local.formatPattern, action = "update"); - put(pattern = local.formatPattern, action = "update"); - } - if (ListFind(variables.scopeStack[1].actions, "delete")) { - delete(pattern = local.formatPattern, action = "delete"); - } - - end(); - // If last action was a singular resource, set up its RESTful routes. - } else if (variables.scopeStack[1].$call == "resource") { - if (ListFind(variables.scopeStack[1].actions, "create")) { - collection(); - post(pattern = local.formatPattern, action = "create"); - end(); - } - - if (ListFind(variables.scopeStack[1].actions, "new")) { - scope(path = variables.scopeStack[1].memberPath, $call = "new"); - get(pattern = "new#local.formatPattern#", action = "new", name = "new"); - end(); - } - - member(); - - if (ListFind(variables.scopeStack[1].actions, "edit")) { - get(pattern = "edit#local.formatPattern#", action = "edit", name = "edit"); - } - if (ListFind(variables.scopeStack[1].actions, "show")) { - get(pattern = local.formatPattern, action = "show"); - } - if (ListFind(variables.scopeStack[1].actions, "update")) { - patch(pattern = local.formatPattern, action = "update"); - put(pattern = local.formatPattern, action = "update"); - } - if (ListFind(variables.scopeStack[1].actions, "delete")) { - delete(pattern = local.formatPattern, action = "delete"); - } - - end(); - } - - // Remove top of stack to end nesting. - ArrayDeleteAt(variables.scopeStack, 1); - - return this; - } -} diff --git a/core/src/wheels/mapper/matching.cfc b/core/src/wheels/mapper/matching.cfc deleted file mode 100644 index 8505ee1eda..0000000000 --- a/core/src/wheels/mapper/matching.cfc +++ /dev/null @@ -1,429 +0,0 @@ -component { - /** - * Create a route that matches a URL requiring an HTTP `GET` method. We recommend only using this matcher to expose actions that display data. See `post`, `patch`, `delete`, and `put` for matchers that are appropriate for actions that change data in your database. - * - * [section: Configuration] - * [category: Routing] - * - * @name Camel-case name of route to reference when build links and form actions (e.g., `blogPost`). - * @pattern Overrides the URL pattern that will match the route. The default value is a dasherized version of `name` (e.g., a `name` of `blogPost` generates a pattern of `blog-post`). - * @to Set `controller##action` combination to map the route to. You may use either this argument or a combination of `controller` and `action`. - * @controller Map the route to a given controller. This must be passed along with the `action` argument. - * @action Map the route to a given action within the `controller`. This must be passed along with the `controller` argument. - * @package Indicates a subfolder that the controller will be referenced from (but not added to the URL pattern). For example, if you set this to `admin`, the controller will be located at `admin/YourController.cfc`, but the URL path will not contain `admin/`. - * @on If this route is within a nested resource, you can set this argument to `member` or `collection`. A `member` route contains a reference to the resource's `key`, while a `collection` route does not. - * @redirect Redirect via 302 to this URL when this route is matched. Has precedence over controller/action. Use either an absolute link like `/about/`, or a full canonical link. - */ - public struct function get( - string name, - string pattern, - string to, - string controller, - string action, - string package, - string on, - string redirect - ) { - return $match(argumentCollection = arguments, method = "get"); - } - - /** - * Create a route that matches a URL requiring an HTTP `POST` method. We recommend using this matcher to expose actions that create database records. - * - * [section: Configuration] - * [category: Routing] - * - * @name Camel-case name of route to reference when build links and form actions (e.g., `blogPosts`). - * @pattern Overrides the URL pattern that will match the route. The default value is a dasherized version of `name` (e.g., a `name` of `blogPosts` generates a pattern of `blog-posts`). - * @to Set `controller##action` combination to map the route to. You may use either this argument or a combination of `controller` and `action`. - * @controller Map the route to a given controller. This must be passed along with the `action` argument. - * @action Map the route to a given action within the `controller`. This must be passed along with the `controller` argument. - * @package Indicates a subfolder that the controller will be referenced from (but not added to the URL pattern). For example, if you set this to `admin`, the controller will be located at `admin/YourController.cfc`, but the URL path will not contain `admin/`. - * @on If this route is within a nested resource, you can set this argument to `member` or `collection`. A `member` route contains a reference to the resource's `key`, while a `collection` route does not. - * @redirect Redirect via 302 to this URL when this route is matched. Has precedence over controller/action. Use either an absolute link like `/about/`, or a full canonical link. - */ - public struct function post( - string name, - string pattern, - string to, - string controller, - string action, - string package, - string on, - string redirect - ) { - return $match(argumentCollection = arguments, method = "post"); - } - - /** - * Create a route that matches a URL requiring an HTTP `PATCH` method. We recommend using this matcher to expose actions that update database records. - * - * [section: Configuration] - * [category: Routing] - * - * @name Camel-case name of route to reference when build links and form actions (e.g., `blogPost`). - * @pattern Overrides the URL pattern that will match the route. The default value is a dasherized version of `name` (e.g., a `name` of `blogPost` generates a pattern of `blog-post`). - * @to Set `controller##action` combination to map the route to. You may use either this argument or a combination of `controller` and `action`. - * @controller Map the route to a given controller. This must be passed along with the `action` argument. - * @action Map the route to a given action within the `controller`. This must be passed along with the `controller` argument. - * @package Indicates a subfolder that the controller will be referenced from (but not added to the URL pattern). For example, if you set this to `admin`, the controller will be located at `admin/YourController.cfc`, but the URL path will not contain `admin/`. - * @on If this route is within a nested resource, you can set this argument to `member` or `collection`. A `member` route contains a reference to the resource's `key`, while a `collection` route does not. - * @redirect Redirect via 302 to this URL when this route is matched. Has precedence over controller/action. Use either an absolute link like `/about/`, or a full canonical link. - */ - public struct function patch( - string name, - string pattern, - string to, - string controller, - string action, - string package, - string on, - string redirect - ) { - return $match(argumentCollection = arguments, method = "patch"); - } - - /** - * Create a route that matches a URL requiring an HTTP `PUT` method. We recommend using this matcher to expose actions that update database records. This method is provided as a convenience for when you really need to support the `PUT` verb; consider using the `patch` matcher instead of this one. - * - * [section: Configuration] - * [category: Routing] - * - * @name Camel-case name of route to reference when build links and form actions (e.g., `blogPost`). - * @pattern Overrides the URL pattern that will match the route. The default value is a dasherized version of `name` (e.g., a `name` of `blogPost` generates a pattern of `blog-post`). - * @to Set `controller##action` combination to map the route to. You may use either this argument or a combination of `controller` and `action`. - * @controller Map the route to a given controller. This must be passed along with the `action` argument. - * @action Map the route to a given action within the `controller`. This must be passed along with the `controller` argument. - * @package Indicates a subfolder that the controller will be referenced from (but not added to the URL pattern). For example, if you set this to `admin`, the controller will be located at `admin/YourController.cfc`, but the URL path will not contain `admin/`. - * @on If this route is within a nested resource, you can set this argument to `member` or `collection`. A `member` route contains a reference to the resource's `key`, while a `collection` route does not. - * @redirect Redirect via 302 to this URL when this route is matched. Has precedence over controller/action. Use either an absolute link like `/about/`, or a full canonical link. - */ - public struct function put( - string name, - string pattern, - string to, - string controller, - string action, - string package, - string on, - string redirect - ) { - return $match(argumentCollection = arguments, method = "put"); - } - - /** - * Create a route that matches a URL requiring an HTTP `DELETE` method. We recommend using this matcher to expose actions that delete database records. - * - * [section: Configuration] - * [category: Routing] - * - * @name Camel-case name of route to reference when build links and form actions (e.g., `blogPost`). - * @pattern Overrides the URL pattern that will match the route. The default value is a dasherized version of `name` (e.g., a `name` of `blogPost` generates a pattern of `blog-post`). - * @to Set `controller##action` combination to map the route to. You may use either this argument or a combination of `controller` and `action`. - * @controller Map the route to a given controller. This must be passed along with the `action` argument. - * @action Map the route to a given action within the `controller`. This must be passed along with the `controller` argument. - * @package Indicates a subfolder that the controller will be referenced from (but not added to the URL pattern). For example, if you set this to `admin`, the controller will be located at `admin/YourController.cfc`, but the URL path will not contain `admin/`. - * @on If this route is within a nested resource, you can set this argument to `member` or `collection`. A `member` route contains a reference to the resource's `key`, while a `collection` route does not. - * @redirect Redirect via 302 to this URL when this route is matched. Has precedence over controller/action. Use either an absolute link like `/about/`, or a full canonical link. - */ - public struct function delete( - string name, - string pattern, - string to, - string controller, - string action, - string package, - string on, - string redirect - ) { - return $match(argumentCollection = arguments, method = "delete"); - } - - /** - * Create a route that matches the root of its current context. This mapper can be used for the application's web root (or home page), or it can generate a route for the root of a namespace or other path scoping mapper. - * - * [section: Configuration] - * [category: Routing] - * - * @to Set `controller##action` combination to map the route to. You may use either this argument or a combination of `controller` and `action`. - * @controller Map the route to a given controller. This must be passed along with the `action` argument. - * @action Map the route to a given action within the `controller`. This must be passed along with the `controller` argument. - * @mapFormat Set to `true` to include the format (e.g. `.json`) in the route. - */ - public struct function root(string to, boolean mapFormat) { - // If mapFormat is not passed in we default it to true on all calls except the web root. - if (!StructKeyExists(arguments, "mapFormat")) { - if (ArrayLen(variables.scopeStack) > 1) { - arguments.mapFormat = true; - } else { - arguments.mapFormat = false; - } - } - - if (arguments.mapFormat) { - local.pattern = "/(.[format])"; - } else { - local.pattern = "/"; - } - - // If arguments.to is not passed in, we check for the existence of app/views/home/index.cfm if found we set that as the root - // else we set wheels##wheels as the root. - if (!structKeyExists(arguments, "to")) { - if (fileExists(application.AppDir & "views/home/index.cfm")) { - arguments.to = "home##index"; - arguments.method = "get"; - } else { - arguments.to = "wheels##wheels"; - arguments.method = "get"; - } - } - - return $match(name = "root", pattern = local.pattern, argumentCollection = arguments); - - } - - /** - * Special wildcard matching generates routes with `[controller]/[action]` and `[controller]` patterns. The `mapKey` argument also enables a `[controller]/[action]/[key]` pattern as well. - * - * [section: Configuration] - * [category: Routing] - * - * @method List of HTTP methods (verbs) to generate the wildcard routes for. We strongly recommend leaving the default value of `get` and using other routing mappers if you need to `POST` to a URL endpoint. For better readability, you can also pass this argument as `methods` if you're listing multiple methods. - * @action Default action to specify if the value for the `[action]` placeholder is not provided. - * @mapKey Whether or not to enable a `[key]` matcher, enabling a `[controller]/[action]/[key]` pattern. - * @mapFormat Whether or not to add an optional `.[format]` pattern to the end of the generated routes. This is useful for providing formats via URL like `json`, `xml`, `pdf`, etc. - */ - public struct function wildcard( - string method = "get", - string action = "index", - boolean mapKey = false, - boolean mapFormat = false - ) { - if (StructKeyExists(arguments, "methods") && Len(arguments.methods)) { - local.methods = arguments.methods; - } else if (Len(arguments.method)) { - local.methods = ListToArray(arguments.method); - } else { - local.methods = ["get", "post", "put", "patch", "delete"]; - } - - local.formatPattern = ""; - if (arguments.mapFormat) { - local.formatPattern = "(.[format])"; - } - - if (StructKeyExists(variables.scopeStack[1], "controller")) { - for (local.method in local.methods) { - if (arguments.mapKey) { - $match( - method = local.method, - name = "wildcard", - pattern = "[action]/[key]#local.formatPattern#", - action = arguments.action - ); - } - $match( - method = local.method, - name = "wildcard", - pattern = "[action]#local.formatPattern#", - action = arguments.action - ); - $match(method = local.method, name = "wildcard", pattern = local.formatPattern, action = arguments.action); - } - } else { - for (local.method in local.methods) { - if (arguments.mapKey) { - $match( - method = local.method, - name = "wildcard", - pattern = "[controller]/[action]/[key]#local.formatPattern#", - action = arguments.action - ); - } - $match( - method = local.method, - name = "wildcard", - pattern = "[controller]/[action]#local.formatPattern#", - action = arguments.action - ); - - $match( - method = local.method, - name = "wildcard", - pattern = "[controller]#local.formatPattern#", - action = arguments.action - ); - } - } - return this; - } - - /** - * Internal function. - * Match a URL. - * - * @name Name for route. Used for path helpers. - * @pattern Pattern to match for route. - * @to Set controller##action for route. - * @methods HTTP verbs that match route. - * @package Namespace to append to controller. - * @on Created resource route under "member" or "collection". - */ - public struct function $match( - string name, - string pattern, - string to, - string methods, - string package, - string on, - struct constraints = {} - ) { - // Evaluate match on member or collection. - if (StructKeyExists(arguments, "on")) { - switch (arguments.on) { - case "member": - return member().$match(argumentCollection = arguments, on = "").end(); - case "collection": - return collection().$match(argumentCollection = arguments, on = "").end(); - } - } - - // Use scoped controller if found. - if (StructKeyExists(variables.scopeStack[1], "controller") && !StructKeyExists(arguments, "controller")) { - arguments.controller = variables.scopeStack[1].controller; - } - - // Use scoped package if found. - if (StructKeyExists(variables.scopeStack[1], "package")) { - if (StructKeyExists(arguments, "package")) { - arguments.package &= "." & variables.scopeStack[1].package; - } else { - arguments.package = variables.scopeStack[1].package; - } - } - - // Interpret "to" as "controller##action". - if (StructKeyExists(arguments, "to")) { - arguments.controller = ListFirst(arguments.to, "##"); - arguments.action = ListLast(arguments.to, "##"); - StructDelete(arguments, "to"); - } - - // Pull route name from arguments if it exists. - local.name = ""; - if (StructKeyExists(arguments, "name")) { - local.name = arguments.name; - - // Guess pattern and/or action. - if (!StructKeyExists(arguments, "pattern")) { - arguments.pattern = hyphenize(arguments.name); - } - if (!StructKeyExists(arguments, "action") && !Find("[action]", arguments.pattern)) { - arguments.action = arguments.name; - } - } - - // Die if pattern is not defined. - if (!StructKeyExists(arguments, "pattern")) { - Throw(type = "Wheels.MapperArgumentMissing", message = "Either 'pattern' or 'name' must be defined."); - } - - // Accept either "method" or "methods". - if (StructKeyExists(arguments, "method")) { - arguments.methods = arguments.method; - StructDelete(arguments, "method"); - } - - // Remove 'methods' argument if settings disable it. - if (!variables.methods && StructKeyExists(arguments, "methods")) { - StructDelete(arguments, "methods"); - } - - // See if we have any globing in the pattern and if so add a constraint for each glob. - // BoxLang compatibility: Use different regex pattern to match globs - local.globRegex = StructKeyExists(server, "boxlang") ? "\*\[([^\]]+)\]" : "\*([^\/]+)"; - if (ReFindNoCase(local.globRegex, arguments.pattern)) { - local.globs = ReMatch(local.globRegex, arguments.pattern); - for (local.glob in local.globs) { - // For BoxLang: extract variable name from *[varname] pattern - if (StructKeyExists(server, "boxlang")) { - local.var = ReReplace(local.glob, "\*\[([^\]]+)\]", "\1"); - } else { - local.var = ReplaceList(local.glob, "*,[,]", ""); - } - arguments.pattern = Replace(arguments.pattern, local.glob, "[#local.var#]"); - arguments.constraints[local.var] = ".*"; - } - } - - // Use constraints from stack. - if (StructKeyExists(variables.scopeStack[1], "constraints")) { - StructAppend(arguments.constraints, variables.scopeStack[1].constraints, false); - } - - // Add shallow path to pattern. - // Or, add scoped path to pattern. - if ($shallow()) { - arguments.pattern = $shallowPathForCall() & "/" & arguments.pattern; - } else if (StructKeyExists(variables.scopeStack[1], "path")) { - arguments.pattern = variables.scopeStack[1].path & "/" & arguments.pattern; - } - - // If both package and controller are set, combine them. - if (StructKeyExists(arguments, "package") && StructKeyExists(arguments, "controller")) { - arguments.controller = arguments.package & "." & arguments.controller; - StructDelete(arguments, "package"); - } - - // Build named routes in correct order according to rails conventions. - switch (variables.scopeStack[1].$call) { - case "resource": - case "resources": - case "collection": - local.nameStruct = [local.name, $shallow() ? $shallowNameForCall() : $scopeName(), $collection()]; - break; - case "member": - case "new": - local.nameStruct = [local.name, $shallow() ? $shallowNameForCall() : $scopeName(), $member()]; - break; - default: - local.nameStruct = [$scopeName(), $collection(), local.name]; - } - - // Transform array into named route. - local.name = ArrayToList(local.nameStruct); - local.name = ReReplace(local.name, "^,+|,+$", "", "all"); - local.name = ReReplace(local.name, ",+(\w)", "\U\1", "all"); - local.name = ReReplace(local.name, ",", "", "all"); - - // If we have a name, add it to arguments. - if (Len(local.name)) { - arguments.name = local.name; - } - - // Handle optional pattern segments. - if (Find("(", arguments.pattern)) { - // Confirm nesting of optional segments. - if (ReFind("\).*\(", arguments.pattern)) { - Throw(type = "Wheels.InvalidRoute", message = "Optional pattern segments must be nested."); - } - - // Strip closing parens from pattern. - local.pattern = Replace(arguments.pattern, ")", "", "all"); - - // Loop over all possible patterns. - while (Len(local.pattern)) { - // Add current route to Wheels. - $addRoute(argumentCollection = arguments, pattern = Replace(local.pattern, "(", "", "all")); - - // Remove last optional segment. - local.pattern = ReReplace(local.pattern, "(^|\()[^(]+$", ""); - } - } else { - // Add route to Wheels as is. - $addRoute(argumentCollection = arguments); - } - - return this; - } -} diff --git a/core/src/wheels/mapper/resources.cfc b/core/src/wheels/mapper/resources.cfc deleted file mode 100644 index 382fdce446..0000000000 --- a/core/src/wheels/mapper/resources.cfc +++ /dev/null @@ -1,233 +0,0 @@ -component { - /** - * Create a group of routes that exposes actions for manipulating a singular resource. A singular resource exposes URL patterns for the entire CRUD lifecycle of a single entity (`show`, `new`, `create`, `edit`, `update`, and `delete`) without exposing a primary key in the URL. Usually this type of resource represents a singleton entity tied to the session, application, or another resource (perhaps nested within another resource). If you need to generate routes for manipulating a collection of resources with a primary key in the URL, see the `resources` mapper method. - * - * [section: Configuration] - * [category: Routing] - * - * @name Camel-case name of resource to reference when build links and form actions. This is typically a singular word (e.g., `profile`). - * @nested Whether or not additional calls will be nested within this resource. - * @path Override URL path representing this resource. Default is a dasherized version of `name` (e.g., `blogPost` generates a path of `blog-post`). - * @controller Override name of the controller used by resource. This defaults to a pluralized version of `name`. - * @singular Override singularize() result in plural resources. - * @plural Override pluralize() result in singular resource. - * @only Limits the list of RESTful routes to generate. Can include `show`, `new`, `create`, `edit`, `update`, and `delete`. - * @except Excludes RESTful routes to generate, taking priority over the `only` argument. Can include `show`, `new`, `create`, `edit,` `update`, and `delete`. - * @shallow Turn on shallow resources. - * @shallowPath Shallow path prefix. - * @shallowName Shallow name prefix. - * @constraints Variable patterns to use for matching. - * @mapFormat Whether or not to add an optional `.[format]` pattern to the end of the generated routes. This is useful for providing formats via URL like `json`, `xml`, `pdf`, etc. - */ - public struct function resource( - required string name, - boolean nested = false, - string path = hyphenize(arguments.name), - string controller, - string singular, - string plural, - string only, - string except, - boolean shallow, - string shallowPath, - string shallowName, - struct constraints, - string $call = "resource", - boolean $plural = false, - boolean mapFormat = variables.mapFormat - ) { - local.args = {}; - - // If name is a list, add each of the resources in the list. - if (Find(",", arguments.name)) { - // Error if the user asked for a nested resource. - if (arguments.nested) { - Throw(type = "Wheels.InvalidResource", message = "Multiple resources in same declaration cannot be nested."); - } - - // Remove path so new resources do not break. - StructDelete(arguments, "path"); - - // Build each new resource. - local.names = ListToArray(arguments.name); - local.iEnd = ArrayLen(local.names); - for (local.i = 1; local.i <= local.iEnd; local.i++) { - resource(name = local.names[local.i], argumentCollection = arguments); - } - return this; - } - - if (arguments.$plural) { - // Setup singular and plural words. - if (!StructKeyExists(arguments, "singular")) { - arguments.singular = singularize(arguments.name); - } - arguments.plural = arguments.name; - - // Set collection and scoped paths. - local.args.collection = arguments.plural; - local.args.nestedPath = "#arguments.path#/[#arguments.singular#Key]"; - local.args.memberPath = "#arguments.path#/[key]"; - - // For uncountable plurals, append "Index". - if (arguments.singular == arguments.plural) { - local.args.collection &= "Index"; - } - - local.args.actions = "index,new,create,show,edit,update,delete"; - } else { - // Setup singular and plural words. - arguments.singular = arguments.name; - if (!StructKeyExists(arguments, "plural")) { - arguments.plural = pluralize(arguments.name); - } - - // Set collection and scoped paths. - local.args.collection = arguments.singular; - local.args.memberPath = arguments.path; - local.args.nestedPath = arguments.path; - - local.args.actions = "new,create,show,edit,update,delete"; - } - local.args.member = arguments.singular; - local.args.collectionPath = arguments.path; - - // Consider only / except REST routes for resources. - // Allow arguments.only to override local.args.only. - if (StructKeyExists(arguments, "only")) { - local.args.actions = LCase(arguments.only); - } - - // Remove unwanted routes from local.args.only. - if (StructKeyExists(arguments, "except") && ListLen(arguments.except) > 0) { - local.except = ListToArray(arguments.except); - local.iEnd = ArrayLen(local.except); - for (local.i = 1; local.i <= local.iEnd; local.i++) { - local.args.actions = ReReplace(local.args.actions, "\b#local.except[local.i]#\b(,?|$)", ""); - } - } - - // If controller name was passed, use it. - // Else, set controller name based on naming preference - if (StructKeyExists(arguments, "controller")) { - local.args.controller = arguments.controller; - } else { - switch (variables.resourceControllerNaming) { - case "name": - local.args.controller = arguments.name; - break; - case "singular": - local.args.controller = arguments.singular; - break; - default: - local.args.controller = arguments.plural; - } - } - - // If parent resource is found. - if (StructKeyExists(variables.scopeStack[1], "member")) { - // Use member and nested path. - local.args.name = variables.scopeStack[1].member; - local.args.path = variables.scopeStack[1].nestedPath; - - // Store parent resource (and avoid too deep nesting). - local.args.parentResource = Duplicate(variables.scopeStack[1]); - if (StructKeyExists(local.args.parentResource, "parentResource")) { - StructDelete(local.args.parentResource, "parentResource"); - } - } - - // Pass along shallow route options. - if (StructKeyExists(arguments, "shallow")) { - local.args.shallow = arguments.shallow; - } - if (StructKeyExists(arguments, "shallowPath")) { - local.args.shallowPath = arguments.shallowPath; - } - if (StructKeyExists(arguments, "shallowName")) { - local.args.shallowName = arguments.shallowName; - } - - // Pass along constraints. - if (StructKeyExists(arguments, "constraints")) { - local.args.constraints = arguments.constraints; - } - - // Pass along mapFormat preference - if (StructKeyExists(arguments, "mapFormat")) { - local.args.mapFormat = arguments.mapFormat; - } - - // Scope the resource. - scope($call = arguments.$call, argumentCollection = local.args); - - // Call end() automatically unless this is a nested call. - // See 'end()' source for the resource routes logic. - if (!arguments.nested) { - end(); - } - - return this; - } - - /** - * Create a group of routes that exposes actions for manipulating a collection of resources. A plural resource exposes URL patterns for the entire CRUD lifecycle (`index`, `show`, `new`, `create`, `edit`, `update`, `delete`), exposing a primary key in the URL for showing, editing, updating, and deleting records. If you need to generate routes for manipulating a singular resource without a primary key, see the `resource` mapper method. - * - * [section: Configuration] - * [category: Routing] - * - * @name Camel-case name of resource to reference when build links and form actions. This is typically a plural word (e.g., `posts`). - * @nested Whether or not additional calls will be nested within this resource. - * @path Override URL path representing this resource. Default is a dasherized version of `name` (e.g., `blogPosts` generates a path of `blog-posts`). - * @controller Override name of the controller used by resource. This defaults to the value provided for `name`. - * @singular Override singularize() result in plural resources. - * @plural Override pluralize() result in singular resource. - * @only Limits the list of RESTful routes to generate. Can include `index`, `show`, `new`, `create`, `edit`, `update`, and `delete`. - * @except Excludes RESTful routes to generate, taking priority over the `only` argument. Can include `index`, `show`, `new`, `create`, `edit`, `update`, and `delete`. - * @shallow Turn on shallow resources. - * @shallowPath Shallow path prefix. - * @shallowName Shallow name prefix. - * @constraints Variable patterns to use for matching. - * @mapFormat Whether or not to add an optional `.[format]` pattern to the end of the generated routes. This is useful for providing formats via URL like `json`, `xml`, `pdf`, etc. - */ - public struct function resources( - required string name, - boolean nested = false, - string path = hyphenize(arguments.name), - string controller, - string singular, - string plural, - string only, - string except, - boolean shallow, - string shallowPath, - string shallowName, - struct constraints, - boolean mapFormat = variables.mapFormat - ) { - return resource(argumentCollection = arguments, $plural = true, $call = "resources"); - } - - /** - * Scope routes within a nested resource which require use of the primary key as part of the URL pattern; - * A member route will require an ID, because it acts on a member. - * photos/1/preview is an example of a member route, because it acts on (and displays) a single object. - * - * [section: Configuration] - * [category: Routing] - */ - public struct function member() { - return scope(path = variables.scopeStack[1].memberPath, $call = "member"); - } - - /** - * A collection route doesn't require an id because it acts on a collection of objects. - * photos/search is an example of a collection route, because it acts on (and displays) a collection of objects. - * - * [section: Configuration] - * [category: Routing] - */ - public struct function collection() { - return scope(path = variables.scopeStack[1].collectionPath, $call = "collection"); - } -} diff --git a/core/src/wheels/mapper/scoping.cfc b/core/src/wheels/mapper/scoping.cfc deleted file mode 100644 index 067a4eb78f..0000000000 --- a/core/src/wheels/mapper/scoping.cfc +++ /dev/null @@ -1,133 +0,0 @@ -component { - /** - * Set any number of parameters to be inherited by mappers called within this matcher's block. For example, set a package or URL path to be used by all child routes. - * - * [section: Configuration] - * [category: Routing] - * - * @name Name to prepend to child route names for use when building links, forms, and other URLs. - * @path Path to prefix to all child routes. - * @package Package namespace to append to controllers. - * @controller Controller to use for routes. - * @shallow Turn on shallow resources to eliminate routing added before this one. - * @shallowPath Shallow path prefix. - * @shallowName Shallow name prefix. - * @constraints Variable patterns to use for matching. - */ - public struct function scope( - string name, - string path, - string package, - string controller, - boolean shallow, - string shallowPath, - string shallowName, - struct constraints, - string $call = "scope" - ) { - // Set shallow path and prefix if not in a resource. - if (!ListFindNoCase("resource,resources", variables.scopeStack[1].$call)) { - if (!StructKeyExists(arguments, "shallowPath") && StructKeyExists(arguments, "path")) { - arguments.shallowPath = arguments.path; - } - - if (!StructKeyExists(arguments, "shallowName") && StructKeyExists(arguments, "name")) { - arguments.shallowName = arguments.name; - } - } - - // Combine path with scope path. - if (StructKeyExists(variables.scopeStack[1], "path") && StructKeyExists(arguments, "path")) { - arguments.path = $normalizePattern(variables.scopeStack[1].path & "/" & arguments.path); - } - - // Combine package with scope package. - if (StructKeyExists(variables.scopeStack[1], "package") && StructKeyExists(arguments, "package")) { - arguments.package = variables.scopeStack[1].package & "." & arguments.package; - } - - // Combine name with scope name. - if (StructKeyExists(arguments, "name") && StructKeyExists(variables.scopeStack[1], "name")) { - arguments.name = variables.scopeStack[1].name & capitalize(arguments.name); - } - - // Combine shallow path with scope shallow path. - if (StructKeyExists(variables.scopeStack[1], "shallowPath") && StructKeyExists(arguments, "shallowPath")) { - arguments.shallowPath = $normalizePattern(variables.scopeStack[1].shallowPath & "/" & arguments.shallowPath); - } - - // Copy existing constraints if they were previously set. - if (StructKeyExists(variables.scopeStack[1], "constraints") && StructKeyExists(arguments, "constraints")) { - StructAppend(arguments.constraints, variables.scopeStack[1].constraints, false); - } - - // Put scope arguments on the stack. - if (structKeyExists(server, "boxlang")) { - for (local.k in variables.scopeStack[1]) { - if (!StructKeyExists(arguments, local.k) || isNull(arguments[local.k])) { - arguments[local.k] = variables.scopeStack[1][local.k]; - } - } - } else { - StructAppend(arguments, variables.scopeStack[1], false); - } - ArrayPrepend(variables.scopeStack, arguments); - - return this; - } - - /** - * Scopes any the controllers for any routes configured within this block to a subfolder (package) and also adds the package name to the URL. - * - * [section: Configuration] - * [category: Routing] - * - * @name Name to prepend to child route names. - * @package Subfolder (package) to reference for controllers. This defaults to the value provided for `name`. - * @path Subfolder path to add to the URL. - */ - public struct function namespace( - required string name, - string package = arguments.name, - string path = hyphenize(arguments.name) - ) { - return scope(name = arguments.name, package = arguments.package, path = arguments.path, $call = "namespace"); - } - - /** - * Scopes any the controllers for any routes configured within this block to a subfolder (package) without adding the package name to the URL. - * - * [section: Configuration] - * [category: Routing] - * - * @name Name to prepend to child route names. - * @package Subfolder (package) to reference for controllers. This defaults to the value provided for `name`. - */ - public struct function package(required string name, string package = arguments.name) { - return scope(name = arguments.name, package = arguments.package, $call = "package"); - } - - /** - * Considered deprecated as this doesn't conform to RESTful routing principles; Try not to use this. - * - * [section: Configuration] - * [category: Routing] - */ - public struct function controller( - required string controller, - string name = arguments.controller, - string path = hyphenize(arguments.controller) - ) { - return scope(argumentCollection = arguments, $call = "controller"); - } - - /** - * Set variable patterns to use for matching. - * - * [section: Configuration] - * [category: Routing] - */ - public struct function constraints() { - return scope(constraints = arguments, $call = "constraints"); - } -} diff --git a/core/src/wheels/migrator/Base.cfc b/core/src/wheels/migrator/Base.cfc deleted file mode 100644 index e36541363a..0000000000 --- a/core/src/wheels/migrator/Base.cfc +++ /dev/null @@ -1,217 +0,0 @@ -component extends="wheels.Global"{ - - /** - * Used internally by Migrator to provide feedback to the GUI and CLI about completed DB operations - * Only available in a migration CFC - * - * [section: Migrator] - * [category: Migration Functions] - */ - public function announce(required string message) { - param name="request.$wheelsMigrationOutput" default=""; - request.$wheelsMigrationOutput = request.$wheelsMigrationOutput & arguments.message & Chr(13); - } - - public string function $getDBType() { - local.appKey = $appKey(); - local.info = $dbinfo( - type = "version", - datasource = application[local.appKey].dataSourceName, - username = application[local.appKey].dataSourceUserName, - password = application[local.appKey].dataSourcePassword - ); - local.adapterName = ""; - if ( - local.info.driver_name Contains "SQLServer" - || local.info.driver_name Contains "Microsoft SQL Server" - || local.info.driver_name Contains "MS SQL Server" - || local.info.database_productname Contains "Microsoft SQL Server" - ) { - local.adapterName = "MicrosoftSQLServer"; - } else if (local.info.driver_name Contains "MySQL") { - local.adapterName = "MySQL"; - } else if (local.info.driver_name Contains "PostgreSQL") { - local.adapterName = "PostgreSQL"; - // NB: using mySQL adapter for H2 as the cli defaults to this for development - } else if (local.info.driver_name Contains "H2") { - // determine the emulation mode - /* - if (StructKeyExists(server, "lucee")) { - local.connectionString = GetApplicationMetaData().datasources[application[local.appKey].dataSourceName].connectionString; - } else { - // TODO: use the coldfusion class to dig out dsn info - local.connectionString = ""; - } - if (local.connectionString Contains "mode=SQLServer" || local.connectionString Contains "mode=Microsoft SQL Server" || local.connectionString Contains "mode=MS SQL Server" || local.connectionString Contains "mode=Microsoft SQL Server") { - local.adapterName = "MicrosoftSQLServer"; - } else if (local.connectionString Contains "mode=MySQL") { - local.adapterName = "MySQL"; - } else if (local.connectionString Contains "mode=PostgreSQL") { - local.adapterName = "PostgreSQL"; - } else { - local.adapterName = "MySQL"; - } - */ - local.adapterName = "H2"; - } else if (local.info.driver_name Contains "Oracle") { - local.adapterName = "Oracle"; - } else if (local.info.driver_name Contains "SQLite") { - local.adapterName = "SQLite"; - } - return local.adapterName; - } - - private string function $getForeignKeys(required string table) { - local.appKey = $appKey(); - local.foreignKeyList = ""; - local.tables = $dbinfo( - type = "tables", - datasource = application[local.appKey].dataSourceName, - username = application[local.appKey].dataSourceUserName, - password = application[local.appKey].dataSourcePassword - ); - if (ListFindNoCase(ValueList(local.tables.table_name), arguments.table)) { - local.foreignKeys = $dbinfo( - type = "foreignkeys", - table = arguments.table, - datasource = application[local.appKey].dataSourceName, - username = application[local.appKey].dataSourceUserName, - password = application[local.appKey].dataSourcePassword - ); - local.foreignKeyList = ValueList(local.foreignKeys.FKCOLUMN_NAME); - } - return local.foreignKeyList; - } - - private void function $execute(required string sql) { - local.appKey = $appKey(); - local.sql = Trim(arguments.sql); - local.info = $dbinfo( - type = "version", - datasource = application.wheels.dataSourceName, - username = application.wheels.dataSourceUserName, - password = application.wheels.dataSourcePassword - ); - if (Right(local.sql, 1) neq ";" && !FindNoCase("Oracle", local.info.database_productname)) { - local.sql = local.sql &= ";"; - } - if (StructKeyExists(request, "$wheelsMigrationSQLFile") && application[local.appKey].writeMigratorSQLFiles) { - $file( - action = "append", - file = request.$wheelsMigrationSQLFile, - output = "#local.sql#", - addNewLine = "yes", - fixNewLine = "yes" - ); - } - if (StructKeyExists(request, "$wheelsDebugSQL") && request.$wheelsDebugSQL) { - if (!StructKeyExists(request, "$wheelsDebugSQLResult")) { - request.$wheelsDebugSQLResult = []; - } - ArrayAppend(request.$wheelsDebugSQLResult, local.sql); - } else { - $query(datasource = application[local.appKey].dataSourceName, sql = local.sql); - } - } - - public string function $getColumns(required string tableName) { - local.appKey = $appKey(); - local.columns = $dbinfo( - datasource = application[local.appKey].dataSourceName, - username = application[local.appKey].dataSourceUserName, - password = application[local.appKey].dataSourcePassword, - type = "columns", - table = arguments.tableName - ); - return ValueList(local.columns.COLUMN_NAME); - } - - /** - * Helper function to get column default value with BoxLang compatibility - * Different CFML engines return different column names for default values - */ - private string function $getColumnDefaultValue(required query columns, required numeric index) { - local.rv = ""; - // Try different column names used by different CFML engines - if (ListFindNoCase(arguments.columns.columnList, "COLUMN_DEFAULT_VALUE")) { - local.rv = arguments.columns["COLUMN_DEFAULT_VALUE"][arguments.index]; - } else if (ListFindNoCase(arguments.columns.columnList, "column_default")) { - local.rv = arguments.columns["column_default"][arguments.index]; - } else if (ListFindNoCase(arguments.columns.columnList, "default_value")) { - local.rv = arguments.columns["default_value"][arguments.index]; - } else if (ListFindNoCase(arguments.columns.columnList, "COLUMN_DEF")) { - // Standard JDBC column name used by BoxLang - local.rv = arguments.columns["COLUMN_DEF"][arguments.index]; - } - if (IsArray(local.rv)) { - if (ArrayLen(local.rv) > 0) { - local.rv = local.rv[1]; - } else { - local.rv = ""; - } - } - if (!IsSimpleValue(local.rv)) { - local.rv = ""; - } - return local.rv; - } - - private string function $getColumnDefinition(required string tableName, required string columnName) { - local.appKey = $appKey(); - local.columns = $dbinfo( - datasource = application[local.appKey].dataSourceName, - username = application[local.appKey].dataSourceUserName, - password = application[local.appKey].dataSourcePassword, - type = "columns", - table = arguments.tableName - ); - local.columnDefinition = ""; - local.iEnd = local.columns.RecordCount; - for (local.i = 1; local.i <= local.iEnd; local.i++) { - if (local.columns["COLUMN_NAME"][local.i] == arguments.columnName) { - local.columnType = local.columns["TYPE_NAME"][local.i]; - local.columnDefinition = local.columnType; - if (ListFindNoCase("char,varchar,int,bigint,smallint,tinyint,binary,varbinary", local.columnType)) { - local.columnDefinition = local.columnDefinition & "(#local.columns["COLUMN_SIZE"][local.i]#)"; - } else if (ListFindNoCase("decimal,float,double", local.columnType)) { - local.columnDefinition = local.columnDefinition & "(#local.columns["COLUMN_SIZE"][local.i]#,#local.columns["DECIMAL_DIGITS"][local.i]#)"; - } - if (local.columns["IS_NULLABLE"][local.i]) { - local.columnDefinition = local.columnDefinition & " NULL"; - } else { - local.columnDefinition = local.columnDefinition & " NOT NULL"; - } - - // Get column default value with CFML engine compatibility - local.defaultValue = $getColumnDefaultValue(local.columns, local.i); - - if (Len(local.defaultValue) == 0) { - local.columnDefinition = local.columnDefinition & " DEFAULT NULL"; - } else if (ListFindNoCase("char,varchar,binary,varbinary", local.columnType)) { - local.columnDefinition = local.columnDefinition & " DEFAULT '#local.defaultValue#'"; - } else if (ListFindNoCase("int,bigint,smallint,tinyint,decimal,float,double", local.columnType)) { - local.columnDefinition = local.columnDefinition & " DEFAULT #local.defaultValue#"; - } - break; - } - } - return local.columnDefinition; - } - - /** - * Applies case to database objects according to settings - * Note: some db engines use only lower case, TODO: perhaps add certain adapters to these conditions? - */ - private string function objectCase(required string name) { - local.appKey = $appKey(); - if (application[local.appKey].migratorObjectCase eq "lower") { - return LCase(arguments.name); - } else if (application[local.appKey].migratorObjectCase eq "upper") { - return UCase(arguments.name); - } else { - // use the object name unmolested - return arguments.name; - } - } - -} diff --git a/core/src/wheels/model/create.cfc b/core/src/wheels/model/create.cfc deleted file mode 100644 index 05b97a832d..0000000000 --- a/core/src/wheels/model/create.cfc +++ /dev/null @@ -1,368 +0,0 @@ -component { - /** - * Creates a new object, saves it to the database (if the validation permits it), and returns it. - * If the validation fails, the unsaved object (with errors added to it) is still returned. - * Property names and values can be passed in either using named arguments or as a struct to the `properties` argument. - * - * [section: Model Class] - * [category: Create Functions] - * - * @properties [see:new]. - * @parameterize [see:findAll]. - * @reload [see:findAll]. - * @validate [see:save]. - * @transaction [see:save]. - * @callbacks [see:findAll]. - * @allowExplicitTimestamps Set this to `true` to allow explicit assignment of `createdAt` or `updatedAt` properties - */ - public any function create( - struct properties = {}, - any parameterize, - boolean reload, - boolean validate = true, - string transaction = $get("transactionMode"), - boolean callbacks = true, - boolean allowExplicitTimestamps = false - ) { - $args(name = "create", args = arguments); - $setProperties( - argumentCollection = arguments, - filterList = "properties,parameterize,reload,validate,transaction,callbacks,allowExplicitTimestamps" - ); - local.rv = new (argumentCollection = arguments); - local.rv.save( - callbacks = arguments.callbacks, - parameterize = arguments.parameterize, - reload = arguments.reload, - transaction = arguments.transaction, - validate = arguments.validate - ); - return local.rv; - } - - /** - * Creates a new object based on supplied `properties` and returns it. - * The object is not saved to the database, it only exists in memory. - * Property names and values can be passed in either using named arguments or as a struct to the `properties` argument. - * - * [section: Model Class] - * [category: Create Functions] - * - * @properties The properties you want to set on the object (can also be passed in as named arguments). - * @callbacks [see:findAll]. - * @allowExplicitTimestamps Set this to `true` to allow explicit assignment of `createdAt` or `updatedAt` properties - */ - public any function new( - struct properties = {}, - boolean callbacks = true, - boolean allowExplicitTimestamps = false - ) { - arguments.properties = $setProperties( - argumentCollection = arguments, - filterList = "properties,reload,transaction,callbacks", - setOnModel = false - ); - local.rv = $createInstance(callbacks = arguments.callbacks, persisted = false, properties = arguments.properties); - local.rv.$setDefaultValues(); - return local.rv; - } - - /** - * Saves the object if it passes validation and callbacks. - * Returns `true` if the object was saved successfully to the database, `false` if not. - * - * [section: Model Class] - * [category: CRUD Functions] - * - * @parameterize [see:findAll]. - * @reload [see:findAll]. - * @validate Set to `false` to skip validations for this operation. - * @transaction Set this to `commit` to update the database, `rollback` to run all the database queries but not commit them, or `none` to skip transaction handling altogether. - * @callbacks [see:findAll]. - */ - public boolean function save( - any parameterize, - boolean reload, - boolean validate = true, - string transaction = $get("transactionMode"), - boolean callbacks = true - ) { - $args(name = "save", args = arguments); - clearErrors(); - return invokeWithTransaction(argumentCollection = arguments, method = "$save"); - } - - /** - * Creates a new object instance. - * Called from the new function above. - * Also called from findByKey, findOne and findAll when returnAs is object(s). - */ - public any function $createInstance( - required struct properties, - required boolean persisted, - numeric row = 1, - boolean base = true, - boolean callbacks = true - ) { - local.fileName = $objectFileName( - name = variables.wheels.class.modelName, - objectPath = variables.wheels.class.path, - type = "model" - ); - local.rv = $createObjectFromRoot( - base = arguments.base, - fileName = local.fileName, - path = variables.wheels.class.path, - method = "$initModelObject", - name = variables.wheels.class.modelName, - persisted = arguments.persisted, - properties = arguments.properties, - row = arguments.row, - useFilterLists = !arguments.persisted - ); - - /** - * Wirebox adjustment for wirebox specific structs - */ - if (isStruct(local.rv) && StructKeyExists(local.rv, '$wbMixer')){ - structDelete(local.rv, '$wbMixer'); - } - if (isStruct(local.rv) && StructKeyExists(local.rv, '$WBDELEGATEMAP')){ - structDelete(local.rv, '$WBDELEGATEMAP'); - } - - // If the object should be persisted, run afterFind callback, otherwise run afterNew callback. - // Then proceed to afterInitialization callback unless the previous callback method returned false. - local.afterFindResult = arguments.persisted && local.rv.$callback("afterFind", arguments.callbacks); - local.afterNewResult = !arguments.persisted && local.rv.$callback("afterNew", arguments.callbacks); - if (local.afterFindResult || local.afterNewResult) { - local.rv.$callback("afterInitialization", arguments.callbacks); - } - - return local.rv; - } - - /** - * Creates a new record in the database or update it if it already exists. - * Also set associations and run all related callbacks. - * Called from the save function above (inside a transaction). - */ - public boolean function $save( - required any parameterize, - required boolean reload, - required boolean validate, - required boolean callbacks - ) { - local.rv = false; - - // Make sure all of our associations are set properly before saving. - $setAssociations(); - - if ($callback("beforeValidation", arguments.callbacks)) { - if (isNew()) { - if ( - $callback("beforeValidationOnCreate", arguments.callbacks) - && $validate("onSave,onCreate", arguments.validate) - && $callback("afterValidation", arguments.callbacks) - && $callback("afterValidationOnCreate", arguments.callbacks) - && $callback("beforeSave", arguments.callbacks) - && $callback("beforeCreate", arguments.callbacks) - ) { - local.rollback = false; - if (!Len(key())) { - local.rollback = true; - } - $create(parameterize = arguments.parameterize, reload = arguments.reload); - if ( - $saveAssociations(argumentCollection = arguments) - && $callback("afterCreate", arguments.callbacks) - && $callback("afterSave", arguments.callbacks) - ) { - $updatePersistedProperties(); - if (arguments.reload) { - this.reload(); - } - local.rv = true; - } else if (local.rollback) { - $resetToNew(); - } - } else { - $validateAssociations(callbacks = arguments.callbacks); - } - } else { - if ( - $callback("beforeValidationOnUpdate", arguments.callbacks) - && $validate("onSave,onUpdate", arguments.validate) - && $callback("afterValidation", arguments.callbacks) - && $callback("afterValidationOnUpdate", arguments.callbacks) - && $callback("beforeSave", arguments.callbacks) - && $callback("beforeUpdate", arguments.callbacks) - ) { - $update(parameterize = arguments.parameterize, reload = arguments.reload); - if ( - $saveAssociations(argumentCollection = arguments) - && $callback("afterUpdate", arguments.callbacks) - && $callback("afterSave", arguments.callbacks) - ) { - $updatePersistedProperties(); - if (arguments.reload) { - this.reload(); - } - local.rv = true; - } - } else { - $validateAssociations(callbacks = arguments.callbacks); - } - } - } else { - $validateAssociations(callbacks = arguments.callbacks); - } - return local.rv; - } - - /** - * Create an INSERT statement and run to create the record. - */ - public boolean function $create(required any parameterize, required boolean reload) { - // Allow explicit assignment of the createdAt/updatedAt properties if allowExplicitTimestamps is true - local.allowExplicitTimestamps = StructKeyExists(this, "allowExplicitTimestamps") && this.allowExplicitTimestamps; - if ( - local.allowExplicitTimestamps - && StructKeyExists(this, $get("timeStampOnCreateProperty")) - && Len(this[$get("timeStampOnCreateProperty")]) - ) { - // leave createdat unmolested - } else if (variables.wheels.class.timeStampingOnCreate) { - $timestampProperty(property = variables.wheels.class.timeStampOnCreateProperty); - } - if ( - local.allowExplicitTimestamps - && StructKeyExists(this, $get("timeStampOnUpdateProperty")) - && Len(this[$get("timeStampOnUpdateProperty")]) - ) { - // leave updatedat unmolested - } else if ($get("setUpdatedAtOnCreate") && variables.wheels.class.timeStampingOnUpdate) { - $timestampProperty(property = variables.wheels.class.timeStampOnUpdateProperty); - } - - local.pks = listToArray(primaryKeys()); - local.uuidBasedKeys = []; - local.columnMeta = {}; - local.key = ""; - local.primaryKeyExplicitlySet = false; // Track if primary key was explicitly provided - - for (local.key in local.pks) { - local.key = trim(local.key); - local.columnMeta = this.columnDataForProperty(local.key); - - // Check if the primary key was explicitly set by the user - if (structKeyExists(this, local.key) && len(this[local.key])) { - local.primaryKeyExplicitlySet = true; - } - - if ($isUUIDColumn(local.columnMeta)) { - arrayAppend(local.uuidBasedKeys, local.key); - } - } - - for (local.key in local.uuidBasedKeys) { - if (!structKeyExists(this, local.key) || isNull(this[local.key])) { - local.newUUID = generateUUID(); - - while (true) { - local.exists = this.findOne( - where = "#local.primaryKey# = '#local.newUUID#'" - ); - - if (!local.exists) { - this[local.key] = local.newUUID; - break; - } else { - local.newUUID = generateUUID(); - } - } - } - } - - // Start by adding column names and values for the properties that exist on the object to two arrays. - local.sql = []; - local.sql2 = []; - for (local.key in variables.wheels.class.properties) { - // Only include this property if it has a value, or the column is not nullable and has no default set. - if ( - StructKeyExists(this, local.key) - && ( - Len(this[local.key]) - || ( - !variables.wheels.class.properties[local.key].nullable - && !Len(variables.wheels.class.properties[local.key].columndefault) - ) - ) - ) { - ArrayAppend(local.sql, variables.wheels.class.properties[local.key].column); - ArrayAppend(local.sql, ","); - ArrayAppend(local.sql2, $buildQueryParamValues(local.key)); - ArrayAppend(local.sql2, ","); - } - } - - if (ArrayLen(local.sql)) { - // Create wrapping SQL code and merge the second array that holds the values with the first one. - ArrayPrepend(local.sql, "INSERT INTO #tableName()# ("); - ArrayPrepend(local.sql2, " VALUES ("); - ArrayDeleteAt(local.sql, ArrayLen(local.sql)); - ArrayDeleteAt(local.sql2, ArrayLen(local.sql2)); - ArrayAppend(local.sql, ")"); - ArrayAppend(local.sql2, ")"); - local.iEnd = ArrayLen(local.sql); - for (local.i = 1; local.i <= local.iEnd; local.i++) { - ArrayAppend(local.sql, local.sql2[local.i]); - } - - // Map the primary keys down to the SQL columns. - local.iEnd = ArrayLen(local.pks); - for (local.i = 1; local.i <= local.iEnd; local.i++) { - local.pks[local.i] = variables.wheels.class.properties[local.pks[local.i]].column; - } - local.pks = ArrayToList(local.pks); - } else { - // No properties were set on the object so we insert a record with only default values to the database. - local.pks = primaryKey(0); - ArrayAppend( - local.sql, - "INSERT INTO #tableName()#" & variables.wheels.class.adapter.$defaultValues($primaryKey = local.pks) - ); - } - - // Run the insert sql statement and set the primary key value on the object (if one was returned from the database). - local.inserted = variables.wheels.class.adapter.$querySetup( - parameterize = arguments.parameterize, - sql = local.sql, - $primaryKey = local.pks - ); - $clearRequestCache(); - local.generatedKey = variables.wheels.class.adapter.$generatedKey(); - if (StructKeyExists(local.inserted.result, local.generatedKey)) { - if (StructKeyExists(server, "boxlang")) { - if (!local.primaryKeyExplicitlySet) { - this[primaryKeys(1)] = local.inserted.result[local.generatedKey]; - } - } else { - this[primaryKeys(1)] = local.inserted.result[local.generatedKey]; - } - } - - return true; - } - - /** - * Determines if a column is likely intended to store UUID value - */ - public boolean function $isUUIDColumn(required struct columnMeta) { - return ( - listFindNoCase("uniqueidentifier,char,varchar,uuid,raw,text", arguments.columnMeta.dataType) - && (arguments.columnMeta.size == 36 || isNull(arguments.columnMeta.size)) - ); - } - -} diff --git a/core/src/wheels/model/properties.cfc b/core/src/wheels/model/properties.cfc deleted file mode 100644 index f6e72a7b9d..0000000000 --- a/core/src/wheels/model/properties.cfc +++ /dev/null @@ -1,671 +0,0 @@ -component { - /** - * Use this method to specify which properties can be set through mass assignment. - * - * [section: Model Configuration] - * [category: Miscellaneous Functions] - * - * @properties Property name (or list of property names) that are allowed to be altered through mass assignment. - */ - public void function accessibleProperties(string properties = "") { - if (StructKeyExists(arguments, "property")) { - arguments.properties = ListAppend(arguments.properties, arguments.property); - } - - // see if any associations should be included in the white list - for (local.association in variables.wheels.class.associations) { - if (variables.wheels.class.associations[local.association].nested.allow) { - arguments.properties = ListAppend(arguments.properties, local.association); - } - } - variables.wheels.class.accessibleProperties.whiteList = $listToStruct(arguments.properties); - } - - /** - * Use this method to specify which properties cannot be set through mass assignment. - * - * [section: Model Configuration] - * [category: Miscellaneous Functions] - * - * @properties Property name (or list of property names) that are not allowed to be altered through mass assignment. - */ - public void function protectedProperties(string properties = "") { - if (StructKeyExists(arguments, "property")) { - arguments.properties = ListAppend(arguments.properties, arguments.property); - } - variables.wheels.class.accessibleProperties.blackList = $listToStruct(arguments.properties); - } - - /** - * Use this method to specify which columns cannot be used by the wheels ORM. - * - * [section: Model Configuration] - * [category: Miscellaneous Functions] - * - * @columns Array of columns names that will be ignored. - */ - public void function ignoredColumns(array columns = []) { - local.rv = {}; - for (local.column in arguments.columns) { - local.rv[local.column] = 1; - } - variables.wheels.class.ignoredColumns = local.rv; - } - - /** - * Use this method to map an object property to either a table column with a different name than the property or to a SQL expression. - * You only need to use this method when you want to override the default object relational mapping that Wheels performs. - * - * [section: Model Configuration] - * [category: Miscellaneous Functions] - * - * @name The name that you want to use for the column or SQL function result in the CFML code. - * @column The name of the column in the database table to map the property to. - * @sql An SQL expression to use to calculate the property value. - * @label A custom label for this property to be referenced in the interface and error messages. - * @defaultValue A default value for this property. - * @select Whether to include this property by default in SELECT statements - * @dataType Specify the column dataType for this property - * @automaticValidations Enable / disable automatic validations for this property. - */ - public void function property( - required string name, - string column = "", - string sql = "", - string label = "", - string defaultValue, - boolean select = "true", - string dataType = "char", - boolean automaticValidations - ) { - // validate setup - if (Len(arguments.column) && Len(arguments.sql)) { - Throw( - type = "Wheels", - message = "Incorrect Arguments", - extendedInfo = "You cannot specify both a column and a sql statement when setting up the mapping for this property." - ); - } - if (Len(arguments.sql) && StructKeyExists(arguments, "defaultValue")) { - Throw( - type = "Wheels", - message = "Incorrect Arguments", - extendedInfo = "You cannot specify a default value for calculated properties." - ); - } - - // create the key - if (!StructKeyExists(variables.wheels.class.mapping, arguments.name)) { - variables.wheels.class.mapping[arguments.name] = {}; - } - - if (Len(arguments.column)) { - variables.wheels.class.mapping[arguments.name].type = "column"; - variables.wheels.class.mapping[arguments.name].value = arguments.column; - } - if (Len(arguments.sql)) { - variables.wheels.class.mapping[arguments.name].type = "sql"; - variables.wheels.class.mapping[arguments.name].value = arguments.sql; - variables.wheels.class.mapping[arguments.name].select = arguments.select; - variables.wheels.class.mapping[arguments.name].dataType = arguments.dataType; - } - if (Len(arguments.label)) { - variables.wheels.class.mapping[arguments.name].label = arguments.label; - } - if (StructKeyExists(arguments, "defaultValue")) { - variables.wheels.class.mapping[arguments.name].defaultValue = arguments.defaultValue; - } - if (StructKeyExists(arguments, "automaticValidations")) { - variables.wheels.class.mapping[arguments.name].automaticValidations = arguments.automaticValidations; - } - } - - /** - * Returns a list of property names ordered by their respective column's ordinal position in the database table. - * Also includes calculated property names that will be generated by the Wheels ORM. - * - * [section: Model Class] - * [category: Miscellaneous Functions] - */ - public string function propertyNames() { - local.rv = variables.wheels.class.propertyList; - if (ListLen(variables.wheels.class.calculatedPropertyList)) { - local.rv = ListAppend(local.rv, variables.wheels.class.calculatedPropertyList); - } - return local.rv; - } - - /** - * Returns an array of columns names for the table associated with this class. - * Does not include calculated properties that will be generated by the Wheels ORM. - * - * [section: Model Class] - * [category: Miscellaneous Functions] - */ - public array function columns() { - return ListToArray(variables.wheels.class.columnList); - } - - /** - * Returns the column name mapped for the named model property. - * - * [section: Model Class] - * [category: Miscellaneous Functions] - * - * @property Name of property to inspect. - */ - public any function columnForProperty(required string property) { - if (StructKeyExists(variables.wheels.class.properties, arguments.property)) { - return variables.wheels.class.properties[arguments.property].column; - } else { - return false; - } - } - - /** - * Returns a struct with data for the named property. - * - * [section: Model Class] - * [category: Miscellaneous Functions] - * - * @property Name of property to inspect. - */ - public any function columnDataForProperty(required string property) { - if (StructKeyExists(variables.wheels.class.properties, arguments.property)) { - return variables.wheels.class.properties[arguments.property]; - } else { - return false; - } - } - - /** - * Returns the validation type for the property. - * - * [section: Model Class] - * [category: Miscellaneous Functions] - * - * @property Name of column to retrieve data for. - */ - public any function validationTypeForProperty(required string property) { - if (StructKeyExists(variables.wheels.class.properties, arguments.property)) { - return variables.wheels.class.properties[arguments.property].validationtype; - } else { - return "string"; - } - } - - /** - * Returns the value of the primary key for the object. - * If you have a single primary key named id, then `someObject.key()` is functionally equivalent to `someObject.id`. - * This method is more useful when you do dynamic programming and don't know the name of the primary key or when you use composite keys (in which case it's convenient to use this method to get a list of both key values returned). - * - * [section: Model Object] - * [category: Miscellaneous Functions] - */ - public string function key(boolean $persisted = false, boolean $returnTickCountWhenNew = false) { - local.rv = ""; - local.iEnd = ListLen(primaryKeys()); - for (local.i = 1; local.i <= local.iEnd; local.i++) { - local.property = primaryKeys(local.i); - if (StructKeyExists(this, local.property)) { - if (arguments.$persisted && hasChanged(local.property)) { - local.rv = ListAppend(local.rv, changedFrom(local.property)); - } else { - local.rv = ListAppend(local.rv, this[local.property]); - } - } - } - if (!Len(local.rv) && arguments.$returnTickCountWhenNew) { - local.rv = variables.wheels.tickCountId; - } - - /* To fix the bug below: - https://github.com/wheels-dev/wheels/issues/1029 - - This will return a numeric value if the primary key is Numeric and a String otherwise. - */ - if (isNumeric(local.rv) && !reFind("^0\d*$", local.rv) && !Find(",", local.rv)) { - if (local.rv <= 2147483647) { - return JavaCast("int", local.rv); - } else if (local.rv <= 9223372036854775807) { - return JavaCast("long", local.rv); - } else { - return local.rv; - } - } else { - return local.rv; - } - } - - /** - * Returns `true` if the specified property name exists on the model. - * - * [section: Model Object] - * [category: Miscellaneous Functions] - * - * @property Name of property to inspect. - */ - public boolean function hasProperty(required string property) { - if (StructKeyExists(this, arguments.property) && !IsCustomFunction(this[arguments.property])) { - return true; - } else { - return false; - } - } - - /** - * Returns `true` if the specified property exists on the model and is not a blank string. - * - * [section: Model Object] - * [category: Miscellaneous Functions] - * - * @property Name of property to inspect. - */ - public boolean function propertyIsPresent(required string property) { - if (this.hasProperty(arguments.property) && IsSimpleValue(this[arguments.property]) && Len(this[arguments.property])) { - return true; - } else { - return false; - } - } - - /** - * Returns `true` if the specified property doesn't exist on the model or is an empty string. - * This method is the inverse of `propertyIsPresent()`. - * - * [section: Model Object] - * [category: Miscellaneous Functions] - * - * @property Name of property to inspect. - */ - public boolean function propertyIsBlank(required string property) { - return !this.propertyIsPresent(arguments.property); - } - - /** - * Assigns to the property specified the opposite of the property's current boolean value. - * Throws an error if the property cannot be converted to a boolean value. - * Returns this object if save called internally is `false`. - * - * [section: Model Object] - * [category: CRUD Functions] - * - * @save Argument to decide whether save the property after it has been toggled. - */ - public boolean function toggle(required string property, boolean save) { - $args(name = "toggle", args = arguments); - if (!StructKeyExists(this, arguments.property)) { - Throw( - type = "Wheels.PropertyDoesNotExist", - message = "Property Does Not Exist", - extendedInfo = "You may only toggle a property that exists on this model." - ); - } - if (!IsBoolean(this[arguments.property])) { - Throw( - type = "Wheels.PropertyIsIncorrectType", - message = "Incorrect Arguments", - extendedInfo = "You may only toggle a property that evaluates to the boolean value." - ); - } - this[arguments.property] = !this[arguments.property]; - local.rv = true; - if (arguments.save) { - local.rv = updateProperty(property = arguments.property, value = this[arguments.property]); - } - return local.rv; - } - - /** - * Returns a structure of all the properties with their names as keys and the values of the property as values. - * - * [section: Model Object] - * [category: Miscellaneous Functions] - * - * @returnIncluded Whether to return nested properties or not. - */ - public struct function properties(boolean returnIncluded = true) { - local.rv = {}; - // loop through all properties and functions in the this scope - for (local.key in this) { - // don't return nested properties if returnIncluded is false - if (!arguments.returnIncluded && !IsSimpleValue(this[local.key])) { - continue; - } - // don't return functions - if (IsCustomFunction(this[local.key])) { - continue; - } - if ($get("resetPropertiesStructKeyCase")) { - // try to get the property name from the list set on the object, this is just to avoid returning everything in ugly upper case which Adobe ColdFusion does by default - local.listPosition = ListFindNoCase(propertyNames(), local.key); - if (local.listPosition) { - local.key = ListGetAt(propertyNames(), local.listPosition); - } - } - // set property from the this scope in the struct that we will return - local.rv[local.key] = this[local.key]; - } - return local.rv; - } - - /** - * Allows you to set all the properties of an object at once by passing in a structure with keys matching the property names. - * - * [section: Model Object] - * [category: Miscellaneous Functions] - * - * @properties The properties you want to set on the object (can also be passed in as named arguments). - */ - public void function setProperties(struct properties = {}) { - $setProperties(argumentCollection = arguments); - } - - /** - * Returns `true` if the specified property (or any if none was passed in) has been changed but not yet saved to the database. - * Will also return `true` if the object is new and no record for it exists in the database. - * - * [section: Model Object] - * [category: Change Functions] - * - * @property Name of property to check for change. - */ - public boolean function hasChanged(string property = "") { - // always return true if $persistedProperties does not exists - if (!StructKeyExists(variables, "$persistedProperties")) { - return true; - } - - if (!Len(arguments.property)) { - // they haven't specified a particular property so loop through them all - arguments.property = StructKeyList(variables.wheels.class.properties); - } - arguments.property = ListToArray(arguments.property); - local.iEnd = ArrayLen(arguments.property); - for (local.i = 1; local.i <= local.iEnd; local.i++) { - local.key = arguments.property[local.i]; - if (StructKeyExists(this, local.key)) { - if (!StructKeyExists(variables.$persistedProperties, local.key)) { - return true; - } else { - // convert each datatype to a string for easier comparison - local.type = validationTypeForProperty(local.key); - local.a = $convertToString(this[local.key], local.type); - local.b = $convertToString(variables.$persistedProperties[local.key], local.type); - if (Compare(local.a, local.b) != 0) { - return true; - } - } - } - } - // if we get here, it means that all of the properties that were checked had a value in - // $persistedProperties and it matched or some of the properties did not exist in the this scope - return false; - } - - /** - * Returns a list of the object properties that have been changed but not yet saved to the database. - * - * [section: Model Object] - * [category: Change Functions] - */ - public string function changedProperties() { - local.rv = ""; - for (local.key in variables.wheels.class.properties) { - if (hasChanged(local.key)) { - local.rv = ListAppend(local.rv, local.key); - } - } - return local.rv; - } - - /** - * Returns the previous value of a property that has changed. - * Returns an empty string if no previous value exists. - * Wheels will keep a note of the previous property value until the object is saved to the database. - * - * [section: Model Object] - * [category: Change Functions] - * - * @property Name of property to get the previous value for. - */ - public string function changedFrom(required string property) { - if ( - StructKeyExists(variables, "$persistedProperties") - && StructKeyExists(variables.$persistedProperties, arguments.property) - ) { - return variables.$persistedProperties[arguments.property]; - } else { - return ""; - } - } - - /** - * Returns a struct detailing all changes that have been made on the object but not yet saved to the database. - * - * [section: Model Object] - * [category: Change Functions] - */ - public struct function allChanges() { - local.rv = {}; - if (hasChanged()) { - local.changedProperties = changedProperties(); - local.iEnd = ListLen(local.changedProperties); - for (local.i = 1; local.i <= local.iEnd; local.i++) { - local.item = ListGetAt(local.changedProperties, local.i); - local.rv[local.item] = {}; - local.rv[local.item].changedFrom = changedFrom(local.item); - if (StructKeyExists(this, local.item)) { - local.rv[local.item].changedTo = this[local.item]; - } else { - local.rv[local.item].changedTo = ""; - } - } - } - return local.rv; - } - - /** - * Clears all internal knowledge of the current state of the object. - * - * [section: Model Object] - * [category: Change Functions] - * - * @property string false Name of property to clear information for. - */ - public void function clearChangeInformation(string property) { - $updatePersistedProperties(argumentCollection = arguments); - } - - /** - * Internal function. - */ - public any function $setProperties( - required struct properties, - string filterList = "", - boolean setOnModel = "true", - boolean $useFilterLists = "true" - ) { - local.rv = {}; - arguments.filterList = ListAppend(arguments.filterList, "properties,filterList,setOnModel,$useFilterLists"); - - // add eventual named arguments to properties struct (named arguments will take precedence) - for (local.key in arguments) { - if (!ListFindNoCase(arguments.filterList, local.key)) { - arguments.properties[local.key] = arguments[local.key]; - } - } - - // loop through the properties and see if they can be set based off of the accessible properties lists - for (local.key in arguments.properties) { - // required to ignore null keys - if (StructKeyExists(arguments.properties, local.key)) { - local.accessible = true; - if ( - arguments.$useFilterLists && - StructKeyExists(variables.wheels.class.accessibleProperties, "whiteList") - && !StructKeyExists(variables.wheels.class.accessibleProperties.whiteList, local.key) - ) { - local.accessible = false; - } - if ( - arguments.$useFilterLists - && StructKeyExists(variables.wheels.class.accessibleProperties, "blackList") - && StructKeyExists(variables.wheels.class.accessibleProperties.blackList, local.key) - ) { - local.accessible = false; - } - if (local.accessible) { - local.rv[local.key] = arguments.properties[local.key]; - } - if (local.accessible && arguments.setOnModel) { - $setProperty(property = local.key, value = local.rv[local.key]); - } - } - } - - if (arguments.setOnModel) { - return; - } - return local.rv; - } - - /** - * Internal function. - */ - public void function $setProperty( - required string property, - required any value, - struct associations = variables.wheels.class.associations - ) { - if (IsObject(arguments.value)) { - // Handle Oracle BLOB objects in BoxLang - if (structKeyExists(server, "boxlang") && !IsStruct(arguments.value)) { - try { - local.className = GetMetadata(arguments.value).getName(); - if (local.className == "oracle.sql.BLOB") { - // Convert Oracle BLOB to binary using getBytes() method - this[arguments.property] = arguments.value.getBytes(); - } else { - this[arguments.property] = arguments.value; - } - } catch (any e) { - // If getMetadata fails, treat as regular object - this[arguments.property] = arguments.value; - } - } else { - this[arguments.property] = arguments.value; - } - } else if ( - IsStruct(arguments.value) - && StructKeyExists(arguments.associations, arguments.property) - && arguments.associations[arguments.property].nested.allow - && ListFindNoCase("belongsTo,hasOne", arguments.associations[arguments.property].type) - ) { - $setOneToOneAssociationProperty( - property = arguments.property, - value = arguments.value, - association = arguments.associations[arguments.property] - ); - } else if ( - IsStruct(arguments.value) - && StructKeyExists(arguments.associations, arguments.property) - && arguments.associations[arguments.property].nested.allow - && arguments.associations[arguments.property].type == "hasMany" - ) { - $setCollectionAssociationProperty( - property = arguments.property, - value = arguments.value, - association = arguments.associations[arguments.property] - ); - } else if ( - IsArray(arguments.value) - && ArrayLen(arguments.value) - && !IsObject(arguments.value[1]) - && StructKeyExists(arguments.associations, arguments.property) - && arguments.associations[arguments.property].nested.allow - && arguments.associations[arguments.property].type == "hasMany" - ) { - $setCollectionAssociationProperty( - property = arguments.property, - value = arguments.value, - association = arguments.associations[arguments.property] - ); - } else { - this[arguments.property] = arguments.value; - } - } - - /** - * Internal function. - */ - public void function $updatePersistedProperties(string property) { - variables.$persistedProperties = {}; - for (local.key in variables.wheels.class.properties) { - if (StructKeyExists(this, local.key) && (!StructKeyExists(arguments, "property") || arguments.property == local.key)) { - variables.$persistedProperties[local.key] = this[local.key]; - } - } - } - - /** - * Internal function. - */ - public any function $setDefaultValues() { - // persisted properties - for (local.key in variables.wheels.class.properties) { - if ( - StructKeyExists(variables.wheels.class.properties[local.key], "defaultValue") - && (!StructKeyExists(this, local.key) || !Len(this[local.key])) - ) { - // set the default value unless it is blank or a value already exists for that property on the object - this[local.key] = variables.wheels.class.properties[local.key].defaultValue; - } - } - // non-persisted properties - for (local.key in variables.wheels.class.mapping) { - if ( - StructKeyExists(variables.wheels.class.mapping[local.key], "defaultValue") - && (!StructKeyExists(this, local.key) || !Len(this[local.key])) - ) { - // set the default value unless it is blank or a value already exists for that property on the object - this[local.key] = variables.wheels.class.mapping[local.key].defaultValue; - } - } - } - - /** - * Internal function. - */ - public struct function $propertyInfo(required string property) { - if (StructKeyExists(variables.wheels.class.properties, arguments.property)) { - return variables.wheels.class.properties[arguments.property]; - } else { - return {}; - } - } - - /** - * Internal function. - */ - public string function $label(required string property) { - // Prefer label set via `properties` initializer if it exists. - if ( - StructKeyExists(variables.wheels.class.properties, arguments.property) - && StructKeyExists(variables.wheels.class.properties[arguments.property], "label") - ) { - local.rv = variables.wheels.class.properties[arguments.property].label; - // Check to see if the mapping has a label to base the name on. - } else if ( - StructKeyExists(variables.wheels.class.mapping, arguments.property) - && StructKeyExists(variables.wheels.class.mapping[arguments.property], "label") - ) { - local.rv = variables.wheels.class.mapping[arguments.property].label; - // Fall back on property name otherwise. - } else { - local.rv = humanize(arguments.property); - } - - return local.rv; - } -} diff --git a/core/src/wheels/public/README.md b/core/src/wheels/public/README.md deleted file mode 100644 index 01a80be6b9..0000000000 --- a/core/src/wheels/public/README.md +++ /dev/null @@ -1,57 +0,0 @@ -# Wheels GUI - -Everything in `/wheels/public/` is aimed at the local developer. `/wheels/public/assets/` should be on the developers urlrewriting exemption for local CSS and JS already as it was introduced in wheels 2.x. - -## Building - -Rebuild the CSS by navigating to `wheels/public/assets/` - -``` -# Install gulp globally -npm install -g gulp - -# Install dependencies -npm install - -# Change dirs -cd semantic/ - -# Run Build -gulp build -``` - -## Functionality - -#### /wheels/ - -Handles legacy `?controller=wheels&action=wheels&view=routes` style params - -### /wheels/index -Initial congratulations screen - -### /wheels/info -As much information as possible about the currently loaded environment, host and setup - -### /wheels/routes -Route Listings - a quick searchable index. Also lists the internal routes used by this GUI. Adds an experimental route tester which can fire off a jquery request to the URL, or via CFHTTP, then explain why the request has matched the route it has, and also display what other routes it *could* have matched if the primary route hadn't been matched - -### /wheels/route-tester -A POST endpoint for internal use in the above route tester - -### /wheels/docs -All internal documentation - a merged collection of the core documentation and any other documentation that the developer sees fit to add to their app (assuming it matches the javaDoc style output of the internal functions) - -### /wheels/packages -Displays all Core (if using master branch), App & Plugin Test packages. Includes a testrunner which can return multiple formats - html, json, junit - -### /wheels/tests -The actual test runner - -### /wheels/migrator -The database migration system - -### /wheels/cli -Various endpoints for exclusive use by the CLI - -### /wheels/plugins -All the Wheels plugins loaded and as much information as we have about them diff --git a/core/src/wheels/public/docs/core.cfm b/core/src/wheels/public/docs/core.cfm deleted file mode 100644 index 89639d3788..0000000000 --- a/core/src/wheels/public/docs/core.cfm +++ /dev/null @@ -1,104 +0,0 @@ - -// Core API embedded documentation - -param name="request.wheels.params.type" default="core"; -param name="request.wheels.params.format" default="html"; - -if (StructKeyExists(application.wheels, "docs")) { - docs = application.wheels.docs; -} else { - documentScope = []; - - // Plugins First, as they can potentially hijack an internal function - if (application.wheels.enablePluginsComponent) { - for (local.plugin in application.wheels.plugins) { - ArrayAppend(documentScope, {"name" = local.plugin, "scope" = application.wheels.plugins[local.plugin]}); - } - } - - controllerInstance = CreateObject("component", "app.controllers.Controller").init(); - // Remove functions starting with "super" - for (key in structKeyArray(controllerInstance)) { - if ((isCustomFunction(controllerInstance[key]) || isClosure(controllerInstance[key])) && - left(lCase(key), 5) == "super") { - structDelete(controllerInstance, key); - } - } - - ArrayAppend(documentScope, {"name" = "controller", "scope" = controllerInstance}); - - modelInstance = CreateObject("component", "app.models.Model").init(); - // Remove functions starting with "super" - for (key in structKeyArray(modelInstance)) { - if ((isCustomFunction(modelInstance[key]) || isClosure(modelInstance[key])) && - left(lCase(key), 5) == "super") { - structDelete(modelInstance, key); - } - } - - // Now safely append to documentScope - ArrayAppend(documentScope, {"name" = "model", "scope" = modelInstance}); - - /* - To fix the issue below: - https://github.com/wheels-dev/wheels/issues/1132 - - To add the test framework functions in the documentation. Added the Test component in the documentScope. - - As app/test/functions/Example.cfc can be deleted, so check if that component exists then create that component's object. - As Example.cfc extends tests.Test so we are checking the Example.cfc first as that will include both component's functions. - */ - try{ - // BoxLang compatibility: Use correct component path - if (StructKeyExists(server, "boxlang")) { - ArrayAppend(documentScope, {"name" = "test", "scope" = CreateObject("component", "wheels.tests.functions.Example")}); - } else { - ArrayAppend(documentScope, {"name" = "test", "scope" = CreateObject("component", "tests.functions.Example")}); - } - } - catch (any exception){ - // BoxLang compatibility: Use correct component path - if (StructKeyExists(server, "boxlang")) { - ArrayAppend(documentScope, {"name" = "test", "scope" = CreateObject("component", "wheels.tests.Test")}); - } else { - ArrayAppend(documentScope, {"name" = "test", "scope" = CreateObject("component", "tests.Test")}); - } - } - - ArrayAppend(documentScope, {"name" = "mapper", "scope" = application.wheels.mapper}); - if (application.wheels.enablePluginsComponent) { - ArrayAppend(documentScope, {"name" = "migrator", "scope" = application.wheels.migrator}); - ArrayAppend( - documentScope, - {"name" = "migration", "scope" = CreateObject("component", "wheels.migrator.Migration")} - ); - ArrayAppend( - documentScope, - {"name" = "tabledefinition", "scope" = CreateObject("component", "wheels.migrator.TableDefinition")} - ); - } - // Array of functions to ignore - ignore = [ - "config", - "init", - "onDIcomplete", - "exposeMixin", - "getPropertyMixin", - "getVariablesMixin", - "includeitMixin", - "injectMixin", - "injectPropertyMixin", - "invokerMixin", - "methodProxy", - "removeMixin", - "removePropertyMixin" - ]; - - // Populate the main documentation - docs = $returnInternalDocumentation(documentScope, ignore); - - application.wheels.docs = docs; -} - -include "layouts/#request.wheels.params.format#.cfm"; - diff --git a/core/src/wheels/public/docs/guides.cfm b/core/src/wheels/public/docs/guides.cfm deleted file mode 100644 index f1d801837a..0000000000 --- a/core/src/wheels/public/docs/guides.cfm +++ /dev/null @@ -1,151 +0,0 @@ - -// Guides Documentation System -param name="request.wheels.params.path" default=""; -param name="request.wheels.params.format" default="html"; - -// Setup paths -local.docsPath = "/wheels/docs/src/"; -local.summaryPath = local.docsPath & "SUMMARY.md"; - -// Function to parse navigation from SUMMARY.md -function parseSummary(summaryPath) { - local.nav = []; - - if (fileExists(arguments.summaryPath)) { - local.summaryContent = fileRead(arguments.summaryPath); - local.lines = listToArray(local.summaryContent, chr(10)); - - local.currentSection = ""; - local.currentSubsection = ""; - local.currentItems = []; - local.currentSubItems = []; - - for (local.line in local.lines) { - local.trimmedLine = trim(local.line); - - // Skip empty lines - if (!len(local.trimmedLine)) continue; - - // Main section headers: "# " or "## " - if (reFind("^##{1,2}\s", local.trimmedLine)) { - // Save previous section - if (len(local.currentSection)) { - arrayAppend(local.nav, { - "title": local.currentSection, - "items": local.currentItems - }); - } - - // Reset everything for new section - local.currentSection = trim(reReplace(local.trimmedLine, "^##+\s*", "")); - local.currentItems = []; - local.currentSubsection = ""; - local.currentSubItems = []; - } - - // Subsection title (not a link) - else if (reFind("^\*\s+[^\[]+$", local.trimmedLine)) { - // If previous subsection exists, push it - if (len(local.currentSubsection) && arrayLen(local.currentSubItems)) { - arrayAppend(local.currentItems, { - "title": local.currentSubsection, - "items": local.currentSubItems - }); - } - - local.currentSubsection = trim(reReplace(local.trimmedLine, "^\*\s+", "")); - local.currentSubItems = []; - } - - // Navigation items (Markdown links) - else if (reFind("^\*\s+\[", local.trimmedLine)) { - local.linkMatch = reMatch("\*\s+\[([^\]]+)\]\(([^\)]+)\)", local.trimmedLine); - - if (arrayLen(local.linkMatch)) { - local.title = reReplace(local.linkMatch[1], "\*\s+\[([^\]]+)\]\(([^\)]+)\)", "\1"); - local.link = reReplace(local.linkMatch[1], "\*\s+\[([^\]]+)\]\(([^\)]+)\)", "\2"); - - local.link = reReplace(local.link, "\.md$", ""); - local.link = reReplace(local.link, "^/", ""); - - local.linkItem = { - "title": local.title, - "link": local.link - }; - - if (len(local.currentSubsection)) { - arrayAppend(local.currentSubItems, local.linkItem); - } else { - arrayAppend(local.currentItems, local.linkItem); - } - } - } - } - - // Final section/subsection flush - if (len(local.currentSubsection) && arrayLen(local.currentSubItems)) { - arrayAppend(local.currentItems, { - "title": local.currentSubsection, - "items": local.currentSubItems - }); - } - - if (len(local.currentSection)) { - arrayAppend(local.nav, { - "title": local.currentSection, - "items": local.currentItems - }); - } - } - - return local.nav; -} - -// Get navigation structure -local.navigation = parseSummary(local.summaryPath); - -// Determine which guide to display -local.guidePath = ""; -local.guideContent = ""; -local.guideTitle = "Wheels Documentation"; - -if (len(request.wheels.params.path)) { - local.guidePath = local.docsPath & request.wheels.params.path & ".md"; - - // Check if file exists - if (fileExists(local.guidePath)) { - local.guideContent = fileRead(local.guidePath); - local.guideTitle = request.wheels.params.path; - } else { - local.guideContent = "Guide not found: " & request.wheels.params.path; - } -} else { - // Default content - show introduction - local.readmePath = local.docsPath & "README.md"; - if (fileExists(local.readmePath)) { - local.guideContent = fileRead(local.readmePath); - local.guideTitle = "Introduction"; - } else { - local.guideContent = "## Wheels Documentation\n\nSelect a guide from the navigation menu."; - } -} - -local.htmlContent = local.guideContent; - -// Create docs structure similar to API docs -local.guides = { - "navigation" = local.navigation, - "content" = local.htmlContent, - "title" = local.guideTitle, - "path" = request.wheels.params.path -}; - -// Make it available to the layout -docs = local.guides; - - - - - - - \ No newline at end of file diff --git a/core/src/wheels/public/docs/layouts/guides.cfm b/core/src/wheels/public/docs/layouts/guides.cfm deleted file mode 100644 index 5316e44f5c..0000000000 --- a/core/src/wheels/public/docs/layouts/guides.cfm +++ /dev/null @@ -1,476 +0,0 @@ - - - - -
- -
- - -
-
-
- - - - -
-
-
-
- - -
- -
- - - - - - - - - - - - - - - - - - - #item.title# - - - #item.title# - - - - -
#item.title#
-
- - #renderGuideItems(item.items, arguments.currentPath)# - -
-
-
- - -
- - - - - - - -
\ No newline at end of file diff --git a/core/src/wheels/public/layout/_footer_simple.cfm b/core/src/wheels/public/layout/_footer_simple.cfm deleted file mode 100644 index 287de747e2..0000000000 --- a/core/src/wheels/public/layout/_footer_simple.cfm +++ /dev/null @@ -1,16 +0,0 @@ - - - -
- - - - diff --git a/core/src/wheels/public/layout/_header.cfm b/core/src/wheels/public/layout/_header.cfm deleted file mode 100644 index c8e70cd554..0000000000 --- a/core/src/wheels/public/layout/_header.cfm +++ /dev/null @@ -1,124 +0,0 @@ - -// NB ACF10/11 throw duplicate routines if already defined here -if (!IsDefined("pageHeader")) { - include "../helpers.cfm"; -} - -// Css Path -request.wheelsInternalAssetPath = application.wheels.webpath & "wheels/public/assets"; - -// Primary Navigation -request.navigation = [ - { - route = "wheelsInfo", - title = "System Information", - isFluid = false, - text = '  Info' - }, - { - route = "wheelsRoutes", - title = "Routes", - isFluid = false, - text = '  Routes' - }, - { - route = "wheelsApiDocs", - title = "API", - isFluid = true, - text = '  API' - }, - { - route = "wheelsGuides", - title = "Guides", - isFluid = true, - text = '  Guides' - }, - { - route = "tests", - type = "core", - title = "Tests", - isFluid = false, - text = '  Tests' - } -]; -if (application.wheels.enableMigratorComponent) { - ArrayAppend( - request.navigation, - { - route = "wheelsMigrator", - title = "Migrator", - isFluid = false, - text = '  Migrator' - } - ); -} -if (application.wheels.enablePluginsComponent) { - ArrayAppend( - request.navigation, - { - route = "wheelsPlugins", - title = "Plugins", - isFluid = false, - text = '  Plugins' - } - ); -} - -// Get Active Route Info -request.currentRoute = getActiveRoute(request.wheels.params.route, request.navigation); - -// Page Title -request.internalPageTitle = StructKeyExists(request.currentRoute, 'title') ? request.currentRoute.title & ' | ' & "Wheels" : "Wheels"; - -request.wheels.internalHeaderLoaded = true; - -if (StructKeyExists(url, "refresh")) { - _refresh = 3; - if (IsNumeric(url.refresh)) { - _refresh = url.refresh; - } -} - - - - - - - - #request.internalPageTitle# - - - - - - - - - - - - - -
- -
-
- -
- -
- - - diff --git a/core/src/wheels/public/layout/_header_simple.cfm b/core/src/wheels/public/layout/_header_simple.cfm deleted file mode 100644 index 3f3e53482c..0000000000 --- a/core/src/wheels/public/layout/_header_simple.cfm +++ /dev/null @@ -1,71 +0,0 @@ - - - - - - - Wheels - - - - - - - -
-
- - diff --git a/core/src/wheels/public/layout/_navigation.cfm b/core/src/wheels/public/layout/_navigation.cfm deleted file mode 100644 index aec3317753..0000000000 --- a/core/src/wheels/public/layout/_navigation.cfm +++ /dev/null @@ -1,36 +0,0 @@ - - - -
- -
- -
diff --git a/core/src/wheels/public/routes.cfm b/core/src/wheels/public/routes.cfm deleted file mode 100644 index a4a0141e27..0000000000 --- a/core/src/wheels/public/routes.cfm +++ /dev/null @@ -1,38 +0,0 @@ - -/** - * Internal GUI Routes - * TODO: formalise how the cli interacts - **/ -mapper() - .namespace(name = "wheels") - .get(name = "info", pattern = "info", to = "public##info") - .get(name = "routes", pattern = "routes", to = "public##routes") - .post(name = "routeTester", to = "public##routetester") - .get(name = "runner", pattern = "runner", to = "public##runner") - .get(name = "packages", pattern = "legacy/[type]/tests", to = "public##packages") - .get(name = "wheelsPackages", pattern = "legacy/app/tests", to = "public##packages") - .get(name = "testbox", pattern = "/core/tests", to = "public##tests_testbox") - .get(name = "migratorTemplates", pattern = "migrator/templates", to = "public##migratortemplates") - .post(name = "migratorTemplatesCreate", pattern = "migrator/templates", to = "public##migratortemplatescreate") - .get(name = "migratorSQL", pattern = "migrator/sql/[version]", to = "public##migratorsql") - .post(name = "migratorCommand", pattern = "migrator/[command]/[version]", to = "public##migratorcommand") - .get(name = "migrator", pattern = "migrator", to = "public##migrator") - .get(name = "tests", pattern = "tests/[type]", to = "public##tests") - .get(name = "apiDocs", pattern = "api", to = "public##api") - .get(name = "aiDocs", pattern = "ai", to = "public##ai") - .get(name = "mcp", pattern = "mcp", to = "public##mcp") - .post(name = "mcpPost", pattern = "mcp", to = "public##mcp") - .get(name = "cli", pattern = "cli", to = "public##cli") - .get(name = "pluginEntry", pattern = "plugins/[name]", to = "public##pluginentry") - .post(name = "pluginPost", pattern = "plugins/[name]", to = "public##pluginentry") - .get(name = "plugins", pattern = "plugins", to = "public##plugins") - .get(name = "build", pattern = "build", to = "public##build") - .get(name = "legacy", pattern = "wheels/[view]", to = "public##legacy") - .get(name = "guides", pattern = "guides", to = "public##guides") - .get(name = "guidesWildCard", pattern = "guides/*[path]", to = "public##guides") - .get(name = "guideImage", pattern = "guides-assets/*[file]", to = "public##guideImage") - .root(method = "get", to = "public##index", mapFormat = false) - .end() - .get(name = "testbox", pattern = "/wheels/app/tests", to = "wheels##public##testbox") -.end(); - diff --git a/core/src/wheels/public/views/cli.cfm b/core/src/wheels/public/views/cli.cfm deleted file mode 100644 index d6ab283743..0000000000 --- a/core/src/wheels/public/views/cli.cfm +++ /dev/null @@ -1,679 +0,0 @@ - - -baseCfc = createObject("wheels.migrator.Base"); -setting showDebugOutput="no"; -migrator = application.wheels.migrator; -try { - "data" = {}; - data["success"] = true; - data["datasource"] = application.wheels.dataSourceName; - data["wheelsVersion"] = application.wheels.version; - data["currentVersion"] = migrator.getCurrentMigrationVersion(); - data["databaseType"] = baseCfc.$getDBType(); - data["migrations"] = migrator.getAvailableMigrations(); - data["lastVersion"] = 0; - data["message"] = ""; - data["messages"] = ""; - data["command"] = ""; - - if (ArrayLen(data.migrations)) { - data.lastVersion = data.migrations[ArrayLen(data.migrations)].version; - } - - if (StructKeyExists(request.wheels.params, "command")) { - data.command = request.wheels.params.command; - switch (request.wheels.params.command) { - case "createMigration": - if (StructKeyExists(request.wheels.params, "migrationPrefix") && Len(request.wheels.params.migrationPrefix)) { - data.message = migrator.createMigration( - request.wheels.params.migrationName, - request.wheels.params.templateName, - request.wheels.params.migrationPrefix - ); - } else { - data.message = migrator.createMigration( - request.wheels.params.migrationName, - request.wheels.params.templateName - ); - } - break; - case "migrateTo": - if (StructKeyExists(request.wheels.params, "version")) { - data.message = migrator.migrateTo(request.wheels.params.version); - } - break; - case "migrateToLatest": - data.message = migrator.migrateToLatest(); - break; - case "redoMigration": - if (StructKeyExists(request.wheels.params, "version")) { - local.redoVersion = request.wheels.params.version; - } else { - local.redoVersion = data.lastVersion; - } - data.message = migrator.redoMigration(local.redoVersion); - break; - case "info": - data.message = "Returning what I know.."; - break; - - // Database commands - case "dbStatus": - // Return migration status - data.success = true; - data.currentVersion = data.currentVersion; - data.migrations = []; - - // Format migrations for CLI consumption - for (local.migration in migrator.getAvailableMigrations()) { - local.migrationInfo = { - version = local.migration.version, - description = local.migration.name, - status = local.migration.status, - appliedAt = local.migration.loadedAt ?: "" - }; - if (local.migration.version <= data.currentVersion) { - local.migrationInfo.status = "applied"; - } else { - local.migrationInfo.status = "pending"; - } - arrayAppend(data.migrations, local.migrationInfo); - } - - // Add summary - local.applied = 0; - local.pending = 0; - for (local.m in data.migrations) { - if (local.m.status == "applied") { - local.applied++; - } else { - local.pending++; - } - } - data.summary = { - total = arrayLen(data.migrations), - applied = local.applied, - pending = local.pending - }; - break; - - case "dbVersion": - // Return current database version - data.success = true; - data.version = data.currentVersion; - data.message = "Current database version: " & data.currentVersion; - break; - - case "dbRollback": - // Rollback database - local.steps = structKeyExists(request.wheels.params, "steps") ? request.wheels.params.steps : 1; - local.targetVersion = ""; - - // Find target version based on steps - local.appliedMigrations = []; - for (local.migration in migrator.getAvailableMigrations()) { - if (local.migration.version <= data.currentVersion) { - arrayAppend(local.appliedMigrations, local.migration); - } - } - - if (arrayLen(local.appliedMigrations) >= local.steps) { - local.targetIndex = arrayLen(local.appliedMigrations) - local.steps; - if (local.targetIndex > 0) { - local.targetVersion = local.appliedMigrations[local.targetIndex].version; - } else { - local.targetVersion = "0"; - } - } - - if (len(local.targetVersion)) { - data.message = migrator.migrateTo(local.targetVersion); - data.success = true; - } else { - data.success = false; - data.message = "No migrations to rollback"; - } - break; - - case "dbSchema": - // Export database schema - data.success = true; - data.schema = {}; - - try { - // Use database adapter to get schema information - local.adapter = application.wheels.dataAdapter; - data.schema.databaseType = data.databaseType; - data.schema.tables = []; - - // Get all tables - local.tables = []; - if (data.databaseType == "H2") { - // H2 specific query - local.tablesQuery = new Query(); - local.tablesQuery.setDatasource(application.wheels.dataSourceName); - local.tablesQuery.setSQL("SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE = 'TABLE' AND TABLE_SCHEMA = 'PUBLIC'"); - local.tables = local.tablesQuery.execute().getResult(); - } else { - // Generic INFORMATION_SCHEMA query - local.tablesQuery = new Query(); - local.tablesQuery.setDatasource(application.wheels.dataSourceName); - local.tablesQuery.setSQL("SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE = 'BASE TABLE'"); - local.tables = local.tablesQuery.execute().getResult(); - } - - for (local.table in local.tables) { - local.tableInfo = { - name = local.table.TABLE_NAME, - columns = [] - }; - - // Get columns for each table - local.columns = new Query(); - local.columns.setDatasource(application.wheels.dataSourceName); - if (data.databaseType == "H2") { - local.columns.setSQL("SELECT COLUMN_NAME, TYPE_NAME as DATA_TYPE, IS_NULLABLE, COLUMN_DEFAULT FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = :tableName AND TABLE_SCHEMA = 'PUBLIC'"); - } else { - local.columns.setSQL("SELECT COLUMN_NAME, DATA_TYPE, IS_NULLABLE, COLUMN_DEFAULT FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = :tableName"); - } - local.columns.addParam(name="tableName", value=local.table.TABLE_NAME, cfsqltype="cf_sql_varchar"); - local.columnResult = local.columns.execute().getResult(); - - for (local.column in local.columnResult) { - arrayAppend(local.tableInfo.columns, { - name = local.column.COLUMN_NAME, - type = local.column.DATA_TYPE, - nullable = local.column.IS_NULLABLE, - default = local.column.COLUMN_DEFAULT ?: "" - }); - } - - arrayAppend(data.schema.tables, local.tableInfo); - } - } catch (any e) { - data.success = false; - data.message = "Error retrieving schema: " & e.message; - } - break; - - case "dbSeed": - // Seed database with test data - local.count = structKeyExists(request.wheels.params, "count") ? val(request.wheels.params.count) : 10; - local.models = structKeyExists(request.wheels.params, "models") ? request.wheels.params.models : ""; - data.success = true; - data.seeded = []; - - try { - // Get all model files if no specific models requested - local.modelList = []; - if (len(local.models)) { - local.modelList = listToArray(local.models); - } else { - // Find all model files in the app/models directory - local.modelPath = expandPath("/app/models"); - if (directoryExists(local.modelPath)) { - local.modelFiles = directoryList(local.modelPath, false, "name", "*.cfc"); - for (local.file in local.modelFiles) { - // Skip any files that start with underscore (partials/helpers) - if (left(local.file, 1) != "_") { - arrayAppend(local.modelList, listFirst(local.file, ".")); - } - } - } - } - - // Seed each model - for (local.modelName in local.modelList) { - try { - // Create model instance - local.model = model(local.modelName); - local.seededCount = 0; - - // Get model properties - local.properties = []; - if (structKeyExists(local.model, "$classData") && structKeyExists(local.model.$classData(), "properties")) { - local.properties = local.model.$classData().properties; - } - - // Generate test data for each record - for (local.i = 1; local.i <= local.count; local.i++) { - local.record = {}; - - // Generate data based on property names and types - for (local.prop in local.properties) { - if (local.prop.name != "id" && !listFindNoCase("createdAt,updatedAt,deletedAt", local.prop.name)) { - // Generate appropriate test data based on property name and type - local.record[local.prop.name] = generateTestData(local.prop.name, local.prop.type, local.i); - } - } - - // Create the record - local.newRecord = local.model.new(local.record); - if (local.newRecord.save()) { - local.seededCount++; - } - } - - arrayAppend(data.seeded, { - model = local.modelName, - count = local.seededCount, - success = true - }); - - } catch (any modelError) { - arrayAppend(data.seeded, { - model = local.modelName, - count = 0, - success = false, - error = modelError.message - }); - } - } - - // Build success message - local.totalSeeded = 0; - for (local.result in data.seeded) { - if (local.result.success) { - local.totalSeeded += local.result.count; - } - } - - data.message = "Database seeding completed. Created #local.totalSeeded# records across #arrayLen(data.seeded)# models."; - - } catch (any e) { - data.success = false; - data.message = "Error during database seeding: " & e.message; - } - break; - - case "routes": - // Return application routes - data.success = true; - data.routes = []; - - // Get routes from application - local.appKey = application.wheels.appKey; - if (structKeyExists(application, local.appKey) && structKeyExists(application[local.appKey], "routes")) { - for (local.route in application[local.appKey].routes) { - local.routeInfo = { - name = structKeyExists(local.route, "name") ? local.route.name : "", - pattern = structKeyExists(local.route, "pattern") ? local.route.pattern : "", - controller = structKeyExists(local.route, "controller") ? local.route.controller : "", - action = structKeyExists(local.route, "action") ? local.route.action : "", - methods = structKeyExists(local.route, "methods") ? local.route.methods : "GET" - }; - arrayAppend(data.routes, local.routeInfo); - } - } - break; - - case "dbCreate": - // Create database - data.success = false; - - // For H2, we can provide helpful info and ensure schema table exists - if (data.databaseType == "H2") { - try { - // Check if schemainfo table exists - local.checkQuery = new Query(); - local.checkQuery.setDatasource(application.wheels.dataSourceName); - local.checkQuery.setSQL("SELECT COUNT(*) as cnt FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 'SCHEMAINFO'"); - local.checkResult = local.checkQuery.execute().getResult(); - - if (local.checkResult.cnt == 0) { - // Create schemainfo table - local.createQuery = new Query(); - local.createQuery.setDatasource(application.wheels.dataSourceName); - local.createQuery.setSQL("CREATE TABLE IF NOT EXISTS schemainfo (version VARCHAR(25) DEFAULT '0')"); - local.createQuery.execute(); - - // Insert initial version - local.insertQuery = new Query(); - local.insertQuery.setDatasource(application.wheels.dataSourceName); - local.insertQuery.setSQL("INSERT INTO schemainfo (version) VALUES ('0')"); - local.insertQuery.execute(); - - data.message = "H2 database initialized successfully with schema tracking table."; - } else { - data.message = "H2 database already exists and is properly configured."; - } - data.success = true; - } catch (any e) { - data.message = "H2 database exists but error checking schema: " & e.message; - data.success = true; // Still mark as success since H2 auto-creates - } - } else { - data.message = "Database creation must be done through your database management system or hosting control panel."; - - // Provide helpful commands for common databases - switch(data.databaseType) { - case "MySQL": - data.message &= chr(10) & chr(10) & "MySQL: CREATE DATABASE dbname CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;"; - break; - case "PostgreSQL": - data.message &= chr(10) & chr(10) & "PostgreSQL: CREATE DATABASE dbname WITH ENCODING='UTF8';"; - break; - case "SQLServer": - data.message &= chr(10) & chr(10) & "SQL Server: CREATE DATABASE dbname;"; - break; - } - } - break; - - case "dbDrop": - // Drop database - data.success = false; - data.message = "Database dropping must be done through your database management system or hosting control panel for safety reasons."; - break; - - case "dbReset": - // Reset database (drop all tables and re-run migrations) - try { - // Get all tables - local.tables = []; - if (data.databaseType == "H2") { - local.tablesQuery = new Query(); - local.tablesQuery.setDatasource(application.wheels.dataSourceName); - local.tablesQuery.setSQL("SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE = 'TABLE' AND TABLE_SCHEMA = 'PUBLIC' AND TABLE_NAME != 'SCHEMAINFO'"); - local.tables = local.tablesQuery.execute().getResult(); - } else { - local.tablesQuery = new Query(); - local.tablesQuery.setDatasource(application.wheels.dataSourceName); - local.tablesQuery.setSQL("SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE = 'BASE TABLE' AND TABLE_NAME != 'schemainfo'"); - local.tables = local.tablesQuery.execute().getResult(); - } - - // Drop all tables except schemainfo - for (local.table in local.tables) { - local.dropQuery = new Query(); - local.dropQuery.setDatasource(application.wheels.dataSourceName); - local.dropQuery.setSQL("DROP TABLE #local.table.TABLE_NAME#"); - local.dropQuery.execute(); - } - - // Reset migration version to 0 - local.resetQuery = new Query(); - local.resetQuery.setDatasource(application.wheels.dataSourceName); - local.resetQuery.setSQL("UPDATE schemainfo SET version = '0'"); - local.resetQuery.execute(); - - data.success = true; - data.message = "Database reset successfully. All tables dropped and migration version reset to 0."; - } catch (any e) { - data.success = false; - data.message = "Error resetting database: " & e.message; - } - break; - - case "dbSetup": - // Setup database (create + migrate + seed) - data.success = true; - data.message = "Database setup: "; - - // Run migrations to latest - try { - local.migrateResult = migrator.migrateToLatest(); - data.message &= "Migrations completed. "; - - // Run seeding if requested - if (structKeyExists(request.wheels.params, "seed") && request.wheels.params.seed) { - // Use the dbSeed logic - request.wheels.params.command = "dbSeed"; - local.seedCount = structKeyExists(request.wheels.params, "seedCount") ? val(request.wheels.params.seedCount) : 10; - request.wheels.params.count = local.seedCount; - - // Re-run this switch for dbSeed - data.command = "dbSeed"; - include "/wheels/public/views/cli.cfm"; - abort; - } - } catch (any e) { - data.success = false; - data.message &= "Migration failed: " & e.message & ". "; - } - break; - - case "dbDump": - // Dump database - data.success = false; - data.dump = ""; - - // For H2, we can generate a dump directly - if (data.databaseType == "H2") { - try { - local.dumpQuery = new Query(); - local.dumpQuery.setDatasource(application.wheels.dataSourceName); - local.dumpQuery.setSQL("SCRIPT SIMPLE"); - local.dumpResult = local.dumpQuery.execute().getResult(); - - // Build SQL dump - local.sqlDump = ""; - for (local.row in local.dumpResult) { - local.sqlDump &= local.row.SCRIPT & ";" & chr(10); - } - - data.success = true; - data.dump = local.sqlDump; - data.message = "Database dump generated successfully. Use --output parameter to save to file."; - - // If output file specified, save it - if (structKeyExists(request.wheels.params, "output")) { - local.outputFile = expandPath(request.wheels.params.output); - fileWrite(local.outputFile, local.sqlDump); - data.message = "Database dump saved to: " & request.wheels.params.output; - } - - } catch (any e) { - data.message = "Error generating dump: " & e.message; - } - } else { - // Provide database-specific guidance for other systems - data.message = "Database dump functionality requires command-line tools specific to your database system."; - switch(data.databaseType) { - case "MySQL": - data.message &= " Use: mysqldump -u [username] -p [database] > backup.sql"; - break; - case "PostgreSQL": - data.message &= " Use: pg_dump -U [username] [database] > backup.sql"; - break; - case "SQLServer": - data.message &= " Use SQL Server Management Studio or: sqlcmd -S [server] -d [database] -Q 'BACKUP DATABASE...'"; - break; - } - } - break; - - case "dbRestore": - // Restore database - data.success = false; - data.message = "Database restore functionality requires command-line tools specific to your database system."; - - // Provide database-specific guidance - switch(data.databaseType) { - case "MySQL": - data.message &= " Use: mysql -u [username] -p [database] < backup.sql"; - break; - case "PostgreSQL": - data.message &= " Use: psql -U [username] [database] < backup.sql"; - break; - case "SQLServer": - data.message &= " Use SQL Server Management Studio or: sqlcmd -S [server] -d [database] -i backup.sql"; - break; - case "H2": - data.message &= " Use: RUNSCRIPT FROM 'backup.sql' in H2 console"; - break; - } - break; - - case "dbShell": - // Database shell - data.success = false; - - // For H2, provide specific information about accessing the console - if (data.databaseType == "H2") { - data.message = "H2 Database Console Access:" & chr(10); - data.message &= chr(10) & "Option 1: Web Console" & chr(10); - data.message &= "The H2 web console may be available at the /h2-console path of your application." & chr(10); - data.message &= "URL: http://localhost:[your-port]/h2-console" & chr(10); - data.message &= "JDBC URL: " & application.wheels.dataSourceName & chr(10); - - // Try to get connection info - try { - local.dbinfo = new Query(); - local.dbinfo.setDatasource(application.wheels.dataSourceName); - local.dbinfo.setSQL("SELECT DATABASE() as dbname, USER() as dbuser"); - local.dbResult = local.dbinfo.execute().getResult(); - if (local.dbResult.recordCount) { - data.message &= "Database: " & local.dbResult.dbname & chr(10); - data.message &= "User: " & local.dbResult.dbuser & chr(10); - } - } catch (any e) { - // Ignore errors getting extra info - } - - data.message &= chr(10) & "Option 2: Command Line" & chr(10); - data.message &= "java -cp [path-to-h2.jar] org.h2.tools.Shell" & chr(10); - - // If command parameter provided, execute it - if (structKeyExists(request.wheels.params, "command")) { - try { - local.shellQuery = new Query(); - local.shellQuery.setDatasource(application.wheels.dataSourceName); - local.shellQuery.setSQL(request.wheels.params.command); - local.shellResult = local.shellQuery.execute().getResult(); - - data.success = true; - data.result = local.shellResult; - data.message = "Command executed successfully."; - } catch (any e) { - data.message = "Error executing command: " & e.message; - } - } - } else { - // Provide database-specific guidance - data.message = "Database shell access requires command-line tools. "; - switch(data.databaseType) { - case "MySQL": - data.message &= "Use: mysql -u [username] -p [database]"; - break; - case "PostgreSQL": - data.message &= "Use: psql -U [username] [database]"; - break; - case "SQLServer": - data.message &= "Use: sqlcmd -S [server] -d [database] -U [username]"; - break; - } - } - break; - } - } -} catch (any e) { - data.success = false; - data.messages = e.message & ': ' & e.detail; -} - -// Helper function to generate test data based on property name and type -function generateTestData(required string propertyName, string propertyType = "string", numeric index = 1) { - // Common patterns for property names - local.name = lCase(arguments.propertyName); - - // Email fields - if (findNoCase("email", local.name)) { - return "test#arguments.index#@example.com"; - } - - // Name fields - if (findNoCase("firstname", local.name) || local.name == "fname") { - local.firstNames = ["John", "Jane", "Bob", "Alice", "Charlie", "Diana", "Edward", "Fiona", "George", "Helen"]; - return local.firstNames[(arguments.index - 1) mod arrayLen(local.firstNames) + 1]; - } - - if (findNoCase("lastname", local.name) || local.name == "lname") { - local.lastNames = ["Smith", "Johnson", "Williams", "Brown", "Jones", "Garcia", "Miller", "Davis", "Rodriguez", "Martinez"]; - return local.lastNames[(arguments.index - 1) mod arrayLen(local.lastNames) + 1]; - } - - if (local.name == "name" || findNoCase("username", local.name)) { - return "TestUser#arguments.index#"; - } - - // Phone fields - if (findNoCase("phone", local.name) || findNoCase("mobile", local.name)) { - return "555-#numberFormat(1000 + arguments.index, '0000')#"; - } - - // Address fields - if (findNoCase("address", local.name) || findNoCase("street", local.name)) { - return "#arguments.index# Test Street"; - } - - if (findNoCase("city", local.name)) { - local.cities = ["New York", "Los Angeles", "Chicago", "Houston", "Phoenix", "Philadelphia", "San Antonio", "San Diego"]; - return local.cities[(arguments.index - 1) mod arrayLen(local.cities) + 1]; - } - - if (findNoCase("state", local.name) || findNoCase("province", local.name)) { - local.states = ["CA", "TX", "FL", "NY", "PA", "IL", "OH", "GA"]; - return local.states[(arguments.index - 1) mod arrayLen(local.states) + 1]; - } - - if (findNoCase("zip", local.name) || findNoCase("postal", local.name)) { - return numberFormat(10000 + arguments.index, "00000"); - } - - // URL fields - if (findNoCase("url", local.name) || findNoCase("website", local.name)) { - return "https://example#arguments.index#.com"; - } - - // Password fields - if (findNoCase("password", local.name)) { - return "TestPass#arguments.index#!"; - } - - // Boolean fields - if (arguments.propertyType == "boolean" || findNoCase("active", local.name) || findNoCase("enabled", local.name) || findNoCase("published", local.name)) { - return (arguments.index mod 2) == 1; - } - - // Numeric fields - if (arguments.propertyType == "integer" || arguments.propertyType == "numeric") { - if (findNoCase("age", local.name)) { - return 20 + (arguments.index mod 50); - } - if (findNoCase("price", local.name) || findNoCase("cost", local.name) || findNoCase("amount", local.name)) { - return (arguments.index * 10) + 0.99; - } - if (findNoCase("quantity", local.name) || findNoCase("count", local.name)) { - return arguments.index * 5; - } - return arguments.index; - } - - // Date fields - if (arguments.propertyType == "date" || arguments.propertyType == "datetime" || findNoCase("date", local.name) || findNoCase("birthday", local.name) || findNoCase("dob", local.name)) { - return dateAdd("d", -arguments.index, now()); - } - - // Text/description fields - if (arguments.propertyType == "text" || findNoCase("description", local.name) || findNoCase("content", local.name) || findNoCase("body", local.name)) { - return "This is test content #arguments.index#. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua."; - } - - // Title fields - if (findNoCase("title", local.name) || findNoCase("subject", local.name)) { - return "Test Title #arguments.index#"; - } - - // Status fields - if (findNoCase("status", local.name)) { - local.statuses = ["pending", "active", "completed", "cancelled"]; - return local.statuses[(arguments.index - 1) mod arrayLen(local.statuses) + 1]; - } - - // Default string value - return "#arguments.propertyName# Test #arguments.index#"; -} - -#SerializeJSON(data)# - diff --git a/core/src/wheels/public/views/migrator.cfm b/core/src/wheels/public/views/migrator.cfm deleted file mode 100644 index edd2fae0c4..0000000000 --- a/core/src/wheels/public/views/migrator.cfm +++ /dev/null @@ -1,281 +0,0 @@ - -// Check for JSON format request -param name="request.wheels.params.format" default="html"; - -datasourceAvailable = true; -try { - availableMigrations = application.wheels.migrator.getAvailableMigrations(); - CreateObject("java", "java.util.Collections").reverse(availableMigrations); - currentVersion = application.wheels.migrator.getCurrentMigrationVersion(); - if (ArrayLen(availableMigrations)) latestVersion = availableMigrations[1]["version"]; -} catch (database err) { - datasourceAvailable = false; - message = err.message; -} -// Get any remaining Missing Migrations -if(structKeyExists(variables, "latestVersion") && currentVersion == latestVersion){ - local.remainingMigrations = []; - for(local.migration in availableMigrations){ - if(local.migration.status != "migrated") arrayAppend(local.remainingMigrations, local.migration); - } -} - -// If JSON format is requested, return JSON response -if (request.wheels.params.format == "json") { - local.migratorData = { - "version": application.wheels.version, - "timestamp": now(), - "migrator": { - "datasourceAvailable": datasourceAvailable - } - }; - - if (datasourceAvailable) { - local.migratorData.migrator.currentVersion = currentVersion; - if (structKeyExists(variables, "latestVersion")) { - local.migratorData.migrator.latestVersion = latestVersion; - } - local.migratorData.migrator.migrations = availableMigrations; - local.migratorData.migrator.migrationsCount = ArrayLen(availableMigrations); - - // Count migrated vs pending - local.migratedCount = 0; - local.pendingCount = 0; - for (local.mig in availableMigrations) { - if (structKeyExists(local.mig, "status") && local.mig.status == "migrated") { - local.migratedCount++; - } else { - local.pendingCount++; - } - } - local.migratorData.migrator.migratedCount = local.migratedCount; - local.migratorData.migrator.pendingCount = local.pendingCount; - - if (structKeyExists(variables, "local") && structKeyExists(local, "remainingMigrations")) { - local.migratorData.migrator.remainingMigrations = local.remainingMigrations; - } - } else { - local.migratorData.migrator.error = message; - } - - cfcontent(type="application/json", reset=true); - writeOutput(serializeJSON(local.migratorData)); - abort; -} - - - - -
- #pageHeader("Migrator", "Database Migrations")# - - - - - -
- - - latestClass = currentVersion EQ latestVersion ? "disabled" : "performmigration"; - resetClass = currentVersion EQ 0 ? "disabled" : "performmigration"; - - -
Migrate Missing Migrations
- -
Migrate To Latest
-
- -
Reset Database
- #startTable(title="Available Migrations", colspan=4)# - - - mig = availableMigrations[m]; - class=""; - hasMigrated=false; - if(mig.status EQ "migrated"){ - class="positive"; - hasMigrated=true; - } - if(mig.version EQ currentVersion){ - class="active"; - } - - - - - - -
- -
-
- - #mig.version# - #replace(mig.name, '_', ' ', 'all')# - - - -
- -
-
- - -
- -
-
-
- -
- -
- - -
- #endTable()# -
-
- -
-
-
- No migration files found!
Perhaps start by using the templating system? -
-
- - - -
-
- - Database Error
- #message# -
-
- - -
- - - - - - - - - - - - - - diff --git a/core/src/wheels/public/views/plugins.cfm b/core/src/wheels/public/views/plugins.cfm deleted file mode 100644 index 7244f72799..0000000000 --- a/core/src/wheels/public/views/plugins.cfm +++ /dev/null @@ -1,122 +0,0 @@ - -// Check for JSON format request -param name="request.wheels.params.format" default="html"; - -if(!application.wheels.enablePluginsComponent) - throw(type="wheels.plugins", message="The Wheels Plugin component is disabled..."); - -loadedPlugins = application.wheels.plugins; - -// If JSON format is requested, return JSON response -if (request.wheels.params.format == "json") { - local.pluginsData = { - "version": application.wheels.version, - "timestamp": now(), - "plugins": { - "enabled": application.wheels.enablePluginsComponent, - "loaded": {} - } - }; - - // Add loaded plugins - for (local.pluginName in loadedPlugins) { - local.pluginsData.plugins.loaded[local.pluginName] = loadedPlugins[local.pluginName]; - } - - // Add incompatible plugins if any - if (isDefined("application.wheels.incompatiblePlugins") && len(application.wheels.incompatiblePlugins)) { - local.pluginsData.plugins.incompatible = listToArray(application.wheels.incompatiblePlugins); - } - - // Add dependent plugins if any - if (isDefined("application.wheels.dependantPlugins") && len(application.wheels.dependantPlugins)) { - local.pluginsData.plugins.dependent = []; - for (local.dep in listToArray(application.wheels.dependantPlugins)) { - arrayAppend(local.pluginsData.plugins.dependent, { - "plugin": listFirst(local.dep, "|"), - "needs": listLast(local.dep, "|") - }); - } - } - - local.pluginsData.plugins.count = structCount(loadedPlugins); - - cfcontent(type="application/json", reset=true); - writeOutput(serializeJSON(local.pluginsData)); - abort; -} - - - - - -
- #pageHeader("Plugins", "What you've got loaded..")# - - -
-
- Warnings: -
- - The #local.i# plugin may be incompatible with this version of Wheels, please look for a compatible version of the plugin
-
- - The #ListFirst(local.i, "|")# plugin needs the following plugins to work properly: #needs#
-
-
-
- - - - - - - - - - - - - - - - - - - -
NameVersionInfo
- #local.i# - - - #$get("pluginMeta")[local.i]['version']# - - Unknown - - - - - - - More information - - - - View Tests - -
- -
-
- -
No plugins found! -
- Browse plugins on Forgebox.io -
-
- -
- -
- - diff --git a/core/src/wheels/public/views/runner.cfm b/core/src/wheels/public/views/runner.cfm deleted file mode 100644 index 21e64eac22..0000000000 --- a/core/src/wheels/public/views/runner.cfm +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/core/src/wheels/tests/Application.cfc b/core/src/wheels/tests/Application.cfc deleted file mode 100644 index 13354469b1..0000000000 --- a/core/src/wheels/tests/Application.cfc +++ /dev/null @@ -1,36 +0,0 @@ -component { - - this.name = createUUID(); - this.bufferOutput = true; - // this.localMode = "modern"; <--- don't use this, logbox chokes. - // this.applicationTimeout = createTimeSpan( 0, 0, 0, 1 ); - - // We turn on "sessionManagement" by default since the Flash uses it. - this.sessionManagement = true; - - // Put variables we just need internally inside a wheels struct. - this.wheels = {}; - this.wheels.rootPath = GetDirectoryFromPath(GetBaseTemplatePath()); - - this.webrootDir = getDirectoryFromPath( getCurrentTemplatePath() ); - this.appDir = getCanonicalPath("_assets"); - - this.mappings['/app'] = this.appDir; - // this.mappings['/tests'] = this.testDir; - // this.mappings['/testbox'] = this.vendorDir & 'testbox'; - // this.mappings['/wirebox'] = this.vendorDir & 'wheels/vendor/wirebox'; - this.mappings['/wheels'] = this.webrootDir & 'wheels'; - - //If a plugin has a jar or class file, automatically add the mapping to this.javasettings. - this.wheels.pluginDir = this.appDir & "plugins"; - this.wheels.pluginFolders = DirectoryList( - this.wheels.pluginDir, - "true", - "path", - "*.class|*.jar|*.java" - ); - public void function onRequestEnd( string targetPage ) { - - } - -} \ No newline at end of file diff --git a/core/src/wheels/tests/_assets/sharedappname/Application.cfc b/core/src/wheels/tests/_assets/sharedappname/Application.cfc deleted file mode 100644 index b2184795e9..0000000000 --- a/core/src/wheels/tests/_assets/sharedappname/Application.cfc +++ /dev/null @@ -1,13 +0,0 @@ -/* - You can place ".cfm" files in this folder and run them independently from Wheels. - This empty "Application.cfc" file makes sure that CFWheels does not interfere with the request. -*/ -component { - - this.name = Hash( - GetDirectoryFromPath( - Left(GetBaseTemplatePath(), Len(GetBaseTemplatePath()) - Len("/wheels/tests/_assets/sharedappname/test.cfm")) - ) - ); - -} diff --git a/core/src/wheels/tests/controller/caching/addcachableaction.cfc b/core/src/wheels/tests/controller/caching/addcachableaction.cfc deleted file mode 100644 index 31995249e7..0000000000 --- a/core/src/wheels/tests/controller/caching/addcachableaction.cfc +++ /dev/null @@ -1,19 +0,0 @@ -component extends="wheels.tests.Test" { - - function setup() { - _controller = controller(name = "dummy"); - _controller.$clearCachableActions(); - } - - function test_adding_cachable_action() { - _controller.caches("dummy1"); - str = {}; - str.action = "dummy2"; - str.time = 10; - str.static = true; - _controller.$addCachableAction(str); - r = _controller.$cachableActions(); - assert("ArrayLen(r) IS 2 AND r[2].action IS 'dummy2'"); - } - -} diff --git a/core/src/wheels/tests/controller/caching/cachableactions.cfc b/core/src/wheels/tests/controller/caching/cachableactions.cfc deleted file mode 100644 index a8f3f18cf7..0000000000 --- a/core/src/wheels/tests/controller/caching/cachableactions.cfc +++ /dev/null @@ -1,14 +0,0 @@ -component extends="wheels.tests.Test" { - - function setup() { - _controller = controller(name = "dummy"); - _controller.$clearCachableActions(); - } - - function test_getting_cachable_actions() { - _controller.caches(actions = "dummy1,dummy2"); - r = _controller.$cachableActions(); - assert("ArrayLen(r) IS 2 AND r[1].static IS false"); - } - -} diff --git a/core/src/wheels/tests/controller/caching/caches.cfc b/core/src/wheels/tests/controller/caching/caches.cfc deleted file mode 100644 index ee0532abd8..0000000000 --- a/core/src/wheels/tests/controller/caching/caches.cfc +++ /dev/null @@ -1,42 +0,0 @@ -component extends="wheels.tests.Test" { - - function setup() { - params = {controller = "test", action = "test"}; - _controller = controller("test", params); - _controller.$clearCachableActions(); - } - - function test_specifying_one_action_to_cache() { - _controller.caches(action = "dummy"); - r = _controller.$cacheSettingsForAction("dummy"); - assert("r.time IS 60"); - } - - function test_specifying_one_action_to_cache_and_running_it() { - $$oldViewPath = application.wheels.viewPath; - application.wheels.viewPath = "/wheels/tests/_assets/views"; - _controller.caches(action = "test"); - result = _controller.processAction("test", params); - application.wheels.viewPath = $$oldViewPath; - assert("result IS true"); - } - - function test_specifying_multiple_actions_to_cache() { - _controller.caches(actions = "dummy1,dummy2"); - r = _controller.$cachableActions(); - assert("ArrayLen(r) IS 2 AND r[2].time IS 60"); - } - - function test_specifying_actions_to_cache_with_options() { - _controller.caches(actions = "dummy1,dummy2", time = 5, static = true); - r = _controller.$cachableActions(); - assert("ArrayLen(r) IS 2 AND r[2].time IS 5 AND r[2].static IS true"); - } - - function test_specifying_caching_all_actions() { - _controller.caches(static = true); - r = _controller.$cacheSettingsForAction("dummy"); - assert("r.static IS true"); - } - -} diff --git a/core/src/wheels/tests/controller/caching/cachesettingsforaction.cfc b/core/src/wheels/tests/controller/caching/cachesettingsforaction.cfc deleted file mode 100644 index 1a71ff3a70..0000000000 --- a/core/src/wheels/tests/controller/caching/cachesettingsforaction.cfc +++ /dev/null @@ -1,13 +0,0 @@ -component extends="wheels.tests.Test" { - - function setup() { - _controller = controller(name = "dummy"); - } - - function test_getting_cache_settings_for_action() { - _controller.caches(action = "dummy1", time = 100); - r = _controller.$cacheSettingsForAction("dummy1"); - assert("r.time IS 100"); - } - -} diff --git a/core/src/wheels/tests/controller/caching/clearcachableactions.cfc b/core/src/wheels/tests/controller/caching/clearcachableactions.cfc deleted file mode 100644 index 669f958010..0000000000 --- a/core/src/wheels/tests/controller/caching/clearcachableactions.cfc +++ /dev/null @@ -1,14 +0,0 @@ -component extends="wheels.tests.Test" { - - function setup() { - _controller = controller(name = "dummy"); - } - - function test_clearing_cachable_actions() { - _controller.caches(action = "dummy"); - _controller.$clearCachableActions(); - r = _controller.$cachableActions(); - assert("ArrayLen(r) IS 0"); - } - -} diff --git a/core/src/wheels/tests/controller/caching/hascachableactions.cfc b/core/src/wheels/tests/controller/caching/hascachableactions.cfc deleted file mode 100644 index b198d8a54e..0000000000 --- a/core/src/wheels/tests/controller/caching/hascachableactions.cfc +++ /dev/null @@ -1,16 +0,0 @@ -component extends="wheels.tests.Test" { - - function setup() { - _controller = controller(name = "dummy"); - _controller.$clearCachableActions(); - } - - function test_checking_cachable_action() { - result = _controller.$hasCachableActions(); - assert("result IS false"); - _controller.caches("dummy1"); - result = _controller.$hasCachableActions(); - assert("result IS true"); - } - -} diff --git a/core/src/wheels/tests/controller/caching/setcachableactions.cfc b/core/src/wheels/tests/controller/caching/setcachableactions.cfc deleted file mode 100644 index b15b1b2aa3..0000000000 --- a/core/src/wheels/tests/controller/caching/setcachableactions.cfc +++ /dev/null @@ -1,22 +0,0 @@ -component extends="wheels.tests.Test" { - - function setup() { - _controller = controller(name = "dummy"); - } - - function test_setting_cachable_actions() { - arr = []; - arr[1] = {}; - arr[1].action = "dummy1"; - arr[1].time = 10; - arr[1].static = true; - arr[2] = {}; - arr[2].action = "dummy2"; - arr[2].time = 10; - arr[2].static = true; - _controller.$setCachableActions(arr); - r = _controller.$cachableActions(); - assert("ArrayLen(r) IS 2 AND r[2].action IS 'dummy2'"); - } - -} diff --git a/core/src/wheels/tests/controller/csrf/cookie/CsrfProtectedExcept.cfc b/core/src/wheels/tests/controller/csrf/cookie/CsrfProtectedExcept.cfc deleted file mode 100644 index 67cbac4564..0000000000 --- a/core/src/wheels/tests/controller/csrf/cookie/CsrfProtectedExcept.cfc +++ /dev/null @@ -1,159 +0,0 @@ -component extends="wheels.tests.Test" { - - function setup() { - super.setup(); - include "setup.cfm"; - } - - function teardown() { - super.teardown(); - include "teardown.cfm"; - } - - function test_csrf_protection_with_valid_authenticityToken_on_PATCH_request() { - request.cgi.request_method = "PATCH"; - - controller("csrfProtectedExcept", {controller = "csrfProtectedExcept", action = "show"}); - - params = {controller = "csrfProtectedExcept", action = "update", authenticityToken = csrfToken}; - - _controller = controller("csrfProtectedExcept", params); - - _controller.processAction("update", params); - assert('_controller.response() eq "Update ran."'); - } - - function test_csrf_protection_with_no_authenticityToken_on_PATCH_request() { - request.cgi.request_method = "PATCH"; - params = {controller = "csrfProtectedExcept", action = "update"}; - _controller = controller("csrfProtectedExcept", params); - - try { - _controller.processAction("update", params); - fail("Wheels.InvalidAuthenticityToken error did not occur."); - } catch (any e) { - type = e.Type; - assert("type is 'Wheels.InvalidAuthenticityToken'"); - } - } - - function test_csrf_protection_with_invalid_authenticityToken_on_PATCH_request() { - request.cgi.request_method = "PATCH"; - params = {controller = "csrfProtectedExcept", action = "update", authenticityToken = "#csrfToken#1"}; - _controller = controller("csrfProtectedExcept", params); - - try { - _controller.processAction("update", params); - fail("Wheels.InvalidAuthenticityToken error did not occur."); - } catch (any e) { - variables.ex = e; - // debug("ex.Type", false); - // debug("ex.message", false); - // debug("$isAnyAuthenticityTokenValid()", false); - type = e.Type; - assert("type is 'Wheels.InvalidAuthenticityToken'"); - } - } - - function test_csrf_protection_with_valid_x_csrf_token_header_on_ajax_PATCH_request() { - request.$wheelsHeaders["X-CSRF-TOKEN"] = csrfToken; - request.cgi.request_method = "PATCH"; - request.cgi.http_x_requested_with = "XMLHTTPRequest"; - params = {controller = "csrfProtectedExcept", action = "update"}; - _controller = controller("csrfProtectedExcept", params); - - _controller.processAction("update", params); - assert('_controller.response() eq "Update ran."'); - } - - function test_csrf_protection_with_no_x_csrf_token_header_on_ajax_PATCH_request() { - request.cgi.request_method = "PATCH"; - request.cgi.http_x_requested_with = "XMLHTTPRequest"; - params = {controller = "csrfProtectedExcept", action = "update"}; - _controller = controller("csrfProtectedExcept", params); - - try { - _controller.processAction("update", params); - fail("Wheels.InvalidAuthenticityToken error did not occur."); - } catch (any e) { - type = e.Type; - assert("type is 'Wheels.InvalidAuthenticityToken'"); - } - } - - function test_csrf_protection_with_invalid_x_csrf_token_header_on_ajax_PATCH_request() { - request.$wheelsHeaders["X-CSRF-TOKEN"] = "#csrfToken#1"; - request.cgi.request_method = "PATCH"; - request.cgi.http_x_requested_with = "XMLHTTPRequest"; - params = {controller = "csrfProtectedExcept", action = "update"}; - _controller = controller("csrfProtectedExcept", params); - - try { - _controller.processAction("update", params); - fail("Wheels.InvalidAuthenticityToken error did not occur."); - } catch (any e) { - type = e.Type; - assert("type is 'Wheels.InvalidAuthenticityToken'"); - } - } - - function test_skipped_csrf_protection_on_PATCH_request_with_valid_authenticityToken() { - request.cgi.request_method = "PATCH"; - params = {controller = "csrfProtectedExcept", action = "show", authenticityToken = csrfToken}; - _controller = controller("csrfProtectedExcept", params); - - _controller.processAction("update", params); - assert('_controller.response() eq "Show ran."'); - } - - function test_skipped_csrf_protection_on_PATCH_request_with_no_authenticityToken() { - request.cgi.request_method = "PATCH"; - params = {controller = "csrfProtectedExcept", action = "show"}; - _controller = controller("csrfProtectedExcept", params); - - _controller.processAction("update", params); - assert('_controller.response() eq "Show ran."'); - } - - function test_skipped_csrf_protection_on_PATCH_request_with_invalid_authenticityToken() { - request.cgi.request_method = "PATCH"; - params = {controller = "csrfProtectedExcept", action = "show", authenticityToken = "#csrfToken#1"}; - _controller = controller("csrfProtectedExcept", params); - - _controller.processAction("update", params); - assert('_controller.response() eq "Show ran."'); - } - - function test_skipped_csrf_protection_on_ajax_PATCH_request_with_valid_x_csrf_token_header() { - request.$wheelsHeaders["X-CSRF-TOKEN"] = csrfToken; - request.cgi.request_method = "PATCH"; - request.cgi.http_x_requested_with = "XMLHTTPRequest"; - params = {controller = "csrfProtectedExcept", action = "show"}; - _controller = controller("csrfProtectedExcept", params); - - _controller.processAction("update", params); - assert('_controller.response() eq "Show ran."'); - } - - function test_skipped_csrf_protection_on_ajax_PATCH_request_with_no_x_csrf_token_header() { - request.cgi.request_method = "PATCH"; - request.cgi.http_x_requested_with = "XMLHTTPRequest"; - params = {controller = "csrfProtectedExcept", action = "show"}; - _controller = controller("csrfProtectedExcept", params); - - _controller.processAction("update", params); - assert('_controller.response() eq "Show ran."'); - } - - function test_skipped_csrf_protection_on_ajax_PATCH_request_with_invalid_x_csrf_token_header() { - request.$wheelsHeaders["X-CSRF-TOKEN"] = "#csrfToken#1"; - request.cgi.request_method = "PATCH"; - request.cgi.http_x_requested_with = "XMLHTTPRequest"; - params = {controller = "csrfProtectedExcept", action = "show"}; - _controller = controller("csrfProtectedExcept", params); - - _controller.processAction("update", params); - assert('_controller.response() eq "Show ran."'); - } - -} diff --git a/core/src/wheels/tests/controller/csrf/cookie/CsrfProtectedOnly.cfc b/core/src/wheels/tests/controller/csrf/cookie/CsrfProtectedOnly.cfc deleted file mode 100644 index 016bb17fef..0000000000 --- a/core/src/wheels/tests/controller/csrf/cookie/CsrfProtectedOnly.cfc +++ /dev/null @@ -1,151 +0,0 @@ -component extends="wheels.tests.Test" { - - function setup() { - super.setup(); - include "setup.cfm"; - } - - function teardown() { - super.teardown(); - include "teardown.cfm"; - } - - function test_csrf_protection_with_valid_authenticityToken_on_post_request() { - request.cgi.request_method = "POST"; - params = {controller = "csrfProtectedOnly", action = "create", authenticityToken = csrfToken}; - _controller = controller("csrfProtectedOnly", params); - - _controller.processAction("create", params); - assert('_controller.response() eq "Create ran."'); - } - - function test_csrf_protection_with_no_authenticityToken_on_post_request() { - request.cgi.request_method = "POST"; - params = {controller = "csrfProtectedOnly", action = "create"}; - _controller = controller("csrfProtectedOnly", params); - - try { - _controller.processAction("create", params); - fail("Wheels.InvalidAuthenticityToken error did not occur."); - } catch (any e) { - type = e.Type; - assert("type is 'Wheels.InvalidAuthenticityToken'"); - } - } - - function test_csrf_protection_with_invalid_authenticityToken_on_post_request() { - request.cgi.request_method = "POST"; - params = {controller = "csrfProtectedOnly", action = "create", authenticityToken = "#csrfToken#1"}; - _controller = controller("csrfProtectedOnly", params); - - try { - _controller.processAction("create", params); - fail("Wheels.InvalidAuthenticityToken error did not occur."); - } catch (any e) { - type = e.Type; - assert("type is 'Wheels.InvalidAuthenticityToken'"); - } - } - - function test_csrf_protection_with_valid_x_csrf_token_header_on_ajax_post_request() { - request.$wheelsHeaders["X-CSRF-TOKEN"] = csrfToken; - request.cgi.request_method = "POST"; - request.cgi.http_x_requested_with = "XMLHTTPRequest"; - params = {controller = "csrfProtectedOnly", action = "create", authenticityToken = csrfToken}; - _controller = controller("csrfProtectedOnly", params); - - _controller.processAction("create", params); - assert('_controller.response() eq "Create ran."'); - } - - function test_csrf_protection_with_no_x_csrf_token_header_on_ajax_post_request() { - request.cgi.request_method = "POST"; - request.cgi.http_x_requested_with = "XMLHTTPRequest"; - params = {controller = "csrfProtectedOnly", action = "create"}; - _controller = controller("csrfProtectedOnly", params); - - try { - _controller.processAction("create", params); - fail("Wheels.InvalidAuthenticityToken error did not occur."); - } catch (any e) { - type = e.Type; - assert("type is 'Wheels.InvalidAuthenticityToken'"); - } - } - - function test_csrf_protection_with_invalid_x_csrf_token_header_on_ajax_post_request() { - request.$wheelsHeaders["X-CSRF-TOKEN"] = "#csrfToken#1"; - request.cgi.request_method = "POST"; - request.cgi.http_x_requested_with = "XMLHTTPRequest"; - params = {controller = "csrfProtectedOnly", action = "create", authenticityToken = "#csrfToken#1"}; - _controller = controller("csrfProtectedOnly", params); - - try { - _controller.processAction("create", params); - fail("Wheels.InvalidAuthenticityToken error did not occur."); - } catch (any e) { - type = e.Type; - assert("type is 'Wheels.InvalidAuthenticityToken'"); - } - } - - function test_skipped_csrf_protection_on_post_request_with_valid_authenticityToken() { - request.cgi.request_method = "POST"; - params = {controller = "csrfProtectedOnly", action = "index", authenticityToken = csrfToken}; - _controller = controller("csrfProtectedOnly", params); - - _controller.processAction("create", params); - assert('_controller.response() eq "Index ran."'); - } - - function test_skipped_csrf_protection_on_post_request_with_no_authenticityToken() { - request.cgi.request_method = "POST"; - params = {controller = "csrfProtectedOnly", action = "index"}; - _controller = controller("csrfProtectedOnly", params); - - _controller.processAction("create", params); - assert('_controller.response() eq "Index ran."'); - } - - function test_skipped_csrf_protection_on_post_request_with_invalid_authenticityToken() { - request.cgi.request_method = "POST"; - params = {controller = "csrfProtectedOnly", action = "index", authenticityToken = "#csrfToken#1"}; - _controller = controller("csrfProtectedOnly", params); - - _controller.processAction("create", params); - assert('_controller.response() eq "Index ran."'); - } - - function test_skipped_csrf_protection_on_ajax_post_request_with_valid_x_csrf_token_header() { - request.$wheelsHeaders["X-CSRF-TOKEN"] = csrfToken; - request.cgi.request_method = "POST"; - request.cgi.http_x_requested_with = "XMLHTTPRequest"; - params = {controller = "csrfProtectedOnly", action = "index"}; - _controller = controller("csrfProtectedOnly", params); - - _controller.processAction("create", params); - assert('_controller.response() eq "Index ran."'); - } - - function test_skipped_csrf_protection_on_ajax_post_request_with_no_x_csrf_token_header() { - request.cgi.request_method = "POST"; - request.cgi.http_x_requested_with = "XMLHTTPRequest"; - params = {controller = "csrfProtectedOnly", action = "index"}; - _controller = controller("csrfProtectedOnly", params); - - _controller.processAction("create", params); - assert('_controller.response() eq "Index ran."'); - } - - function test_skipped_csrf_protection_on_ajax_post_request_with_invalid_x_csrf_token_header() { - request.$wheelsHeaders["X-CSRF-TOKEN"] = "#csrfToken#1"; - request.cgi.request_method = "POST"; - request.cgi.http_x_requested_with = "XMLHTTPRequest"; - params = {controller = "csrfProtectedOnly", action = "index", authenticityToken = "#csrfToken#1"}; - _controller = controller("csrfProtectedOnly", params); - - _controller.processAction("create", params); - assert('_controller.response() eq "Index ran."'); - } - -} diff --git a/core/src/wheels/tests/controller/csrf/cookie/CsrfProtectedWithException.cfc b/core/src/wheels/tests/controller/csrf/cookie/CsrfProtectedWithException.cfc deleted file mode 100644 index e178f3805a..0000000000 --- a/core/src/wheels/tests/controller/csrf/cookie/CsrfProtectedWithException.cfc +++ /dev/null @@ -1,305 +0,0 @@ -component extends="wheels.tests.Test" { - - function setup() { - super.setup(); - include "setup.cfm"; - } - - function teardown() { - super.teardown(); - include "teardown.cfm"; - } - - function test_csrf_protection_skipped_on_get_request() { - params = {controller = "csrfProtectedWithException", action = "index"}; - _controller = controller("csrfProtectedWithException", params); - - _controller.processAction("index", params); - assert('_controller.response() eq "Index ran."'); - } - - function test_csrf_protection_skipped_on_get_ajax_request() { - request.cgi.http_x_requested_with = "XMLHTTPRequest"; - params = {controller = "csrfProtectedWithException", action = "index"}; - _controller = controller("csrfProtectedWithException", params); - - _controller.processAction("index", params); - assert('_controller.response() eq "Index ran."'); - } - - function test_csrf_protection_skipped_on_options_request() { - request.cgi.request_method = "OPTIONS"; - params = {controller = "csrfProtectedWithException", action = "index"}; - _controller = controller("csrfProtectedWithException", params); - - _controller.processAction("index", params); - assert('_controller.response() eq "Index ran."'); - } - - function test_csrf_protection_skipped_on_options_ajax_request() { - request.cgi.http_x_requested_with = "XMLHTTPRequest"; - request.cgi.request_method = "OPTIONS"; - params = {controller = "csrfProtectedWithException", action = "index"}; - _controller = controller("csrfProtectedWithException", params); - - _controller.processAction("index", params); - assert('_controller.response() eq "Index ran."'); - } - - function test_csrf_protection_skipped_on_head_request() { - request.cgi.request_method = "HEAD"; - params = {controller = "csrfProtectedWithException", action = "index"}; - _controller = controller("csrfProtectedWithException", params); - - _controller.processAction("index", params); - assert('_controller.response() eq "Index ran."'); - } - - function test_csrf_protection_skipped_on_head_ajax_request() { - request.cgi.http_x_requested_with = "XMLHTTPRequest"; - request.cgi.request_method = "HEAD"; - params = {controller = "csrfProtectedWithException", action = "index"}; - _controller = controller("csrfProtectedWithException", params); - - _controller.processAction("index", params); - assert('_controller.response() eq "Index ran."'); - } - - function test_csrf_protection_with_valid_authenticityToken_on_post_request() { - request.cgi.request_method = "POST"; - params = {controller = "csrfProtectedWithException", action = "create", authenticityToken = csrfToken}; - _controller = controller("csrfProtectedWithException", params); - - _controller.processAction("create", params); - assert('_controller.response() eq "Create ran."'); - } - - function test_csrf_protection_with_no_authenticityToken_on_post_request() { - request.cgi.request_method = "POST"; - params = {controller = "csrfProtectedWithException", action = "create"}; - _controller = controller("csrfProtectedWithException", params); - - try { - _controller.processAction("create", params); - fail("Wheels.InvalidAuthenticityToken error did not occur."); - } catch (any e) { - type = e.Type; - assert("type is 'Wheels.InvalidAuthenticityToken'"); - } - } - - function test_csrf_protection_with_invalid_authenticityToken_on_post_request() { - request.cgi.request_method = "POST"; - params = {controller = "csrfProtectedWithException", action = "create", authenticityToken = "#csrfToken#1"}; - _controller = controller("csrfProtectedWithException", params); - - try { - _controller.processAction("create", params); - fail("Wheels.InvalidAuthenticityToken error did not occur."); - } catch (any e) { - type = e.Type; - assert("type is 'Wheels.InvalidAuthenticityToken'"); - } - } - - function test_csrf_protection_with_valid_authenticityToken_on_patch_request() { - request.cgi.request_method = "PATCH"; - params = {controller = "csrfProtectedWithException", action = "update", authenticityToken = csrfToken}; - _controller = controller("csrfProtectedWithException", params); - - _controller.processAction("update", params); - assert('_controller.response() eq "Update ran."'); - } - - function test_csrf_protection_with_no_authenticityToken_on_patch_request() { - request.cgi.request_method = "PATCH"; - params = {controller = "csrfProtectedWithException", action = "update"}; - _controller = controller("csrfProtectedWithException", params); - - try { - _controller.processAction("update", params); - fail("Wheels.InvalidAuthenticityToken error did not occur."); - } catch (any e) { - type = e.Type; - assert("type is 'Wheels.InvalidAuthenticityToken'"); - } - } - - function test_csrf_protection_with_invalid_authenticityToken_on_patch_request() { - request.cgi.request_method = "PATCH"; - params = {controller = "csrfProtectedWithException", action = "update", authenticityToken = "#csrfToken#1"}; - _controller = controller("csrfProtectedWithException", params); - - try { - _controller.processAction("update", params); - fail("Wheels.InvalidAuthenticityToken error did not occur."); - } catch (any e) { - type = e.Type; - assert("type is 'Wheels.InvalidAuthenticityToken'"); - } - } - - function test_csrf_protection_with_valid_authenticityToken_on_delete_request() { - request.cgi.request_method = "DELETE"; - params = {controller = "csrfProtectedWithException", action = "delete", authenticityToken = csrfToken}; - _controller = controller("csrfProtectedWithException", params); - - _controller.processAction("delete", params); - assert('_controller.response() eq "Delete ran."'); - } - - function test_csrf_protection_with_no_authenticityToken_on_delete_request() { - request.cgi.request_method = "DELETE"; - params = {controller = "csrfProtectedWithException", action = "delete"}; - _controller = controller("csrfProtectedWithException", params); - - try { - _controller.processAction("delete", params); - fail("Wheels.InvalidAuthenticityToken error did not occur."); - } catch (any e) { - type = e.Type; - assert("type is 'Wheels.InvalidAuthenticityToken'"); - } - } - - function test_csrf_protection_with_invalid_authenticityToken_on_delete_request() { - request.cgi.request_method = "DELETE"; - params = {controller = "csrfProtectedWithException", action = "delete", authenticityToken = "#csrfToken#1"}; - _controller = controller("csrfProtectedWithException", params); - - try { - _controller.processAction("delete", params); - fail("Wheels.InvalidAuthenticityToken error did not occur."); - } catch (any e) { - type = e.Type; - assert("type is 'Wheels.InvalidAuthenticityToken'"); - } - } - - function test_csrf_protection_with_valid_x_csrf_token_header_on_ajax_post_request() { - request.$wheelsHeaders["X-CSRF-TOKEN"] = csrfToken; - request.cgi.request_method = "POST"; - request.cgi.http_x_requested_with = "XMLHTTPRequest"; - params = {controller = "csrfProtectedWithException", action = "create"}; - _controller = controller("csrfProtectedWithException", params); - - _controller.processAction("create", params); - assert('_controller.response() eq "Create ran."'); - } - - function test_csrf_protection_with_no_x_csrf_token_header_on_ajax_post_request() { - request.cgi.request_method = "POST"; - request.cgi.http_x_requested_with = "XMLHTTPRequest"; - params = {controller = "csrfProtectedWithException", action = "create"}; - _controller = controller("csrfProtectedWithException", params); - - try { - _controller.processAction("create", params); - fail("Wheels.InvalidAuthenticityToken error did not occur."); - } catch (any e) { - type = e.Type; - assert("type is 'Wheels.InvalidAuthenticityToken'"); - } - } - - function test_csrf_protection_with_invalid_x_csrf_token_header_on_ajax_post_request() { - request.$wheelsHeaders["X-CSRF-TOKEN"] = "#csrfToken#1"; - request.cgi.request_method = "POST"; - request.cgi.http_x_requested_with = "XMLHTTPRequest"; - params = {controller = "csrfProtectedWithException", action = "create"}; - _controller = controller("csrfProtectedWithException", params); - - try { - _controller.processAction("create", params); - fail("Wheels.InvalidAuthenticityToken error did not occur."); - } catch (any e) { - type = e.Type; - assert("type is 'Wheels.InvalidAuthenticityToken'"); - } - } - - function test_csrf_protection_with_valid_x_csrf_token_header_on_ajax_patch_request() { - request.$wheelsHeaders["X-CSRF-TOKEN"] = csrfToken; - request.cgi.request_method = "PATCH"; - request.cgi.http_x_requested_with = "XMLHTTPRequest"; - params = {controller = "csrfProtectedWithException", action = "update"}; - _controller = controller("csrfProtectedWithException", params); - - _controller.processAction("patch", params); - assert('_controller.response() eq "Update ran."'); - } - - function test_csrf_protection_with_no_x_csrf_token_header_on_ajax_patch_request() { - request.cgi.request_method = "PATCH"; - request.cgi.http_x_requested_with = "XMLHTTPRequest"; - params = {controller = "csrfProtectedWithException", action = "update"}; - _controller = controller("csrfProtectedWithException", params); - - try { - _controller.processAction("patch", params); - fail("Wheels.InvalidAuthenticityToken error did not occur."); - } catch (any e) { - type = e.Type; - assert("type is 'Wheels.InvalidAuthenticityToken'"); - } - } - - function test_csrf_protection_with_invalid_x_csrf_token_header_on_ajax_patch_request() { - request.$wheelsHeaders["X-CSRF-TOKEN"] = "#csrfToken#1"; - request.cgi.request_method = "PATCH"; - request.cgi.http_x_requested_with = "XMLHTTPRequest"; - params = {controller = "csrfProtectedWithException", action = "update"}; - _controller = controller("csrfProtectedWithException", params); - - try { - _controller.processAction("patch", params); - fail("Wheels.InvalidAuthenticityToken error did not occur."); - } catch (any e) { - type = e.Type; - assert("type is 'Wheels.InvalidAuthenticityToken'"); - } - } - - function test_csrf_protection_with_valid_x_csrf_token_header_on_ajax_delete_request() { - request.$wheelsHeaders["X-CSRF-TOKEN"] = csrfToken; - request.cgi.request_method = "DELETE"; - request.cgi.http_x_requested_with = "XMLHTTPRequest"; - params = {controller = "csrfProtectedWithException", action = "delete"}; - _controller = controller("csrfProtectedWithException", params); - - _controller.processAction("patch", params); - assert('_controller.response() eq "Delete ran."'); - } - - function test_csrf_protection_with_no_x_csrf_token_header_on_ajax_delete_request() { - request.cgi.request_method = "DELETE"; - request.cgi.http_x_requested_with = "XMLHTTPRequest"; - params = {controller = "csrfProtectedWithException", action = "delete"}; - _controller = controller("csrfProtectedWithException", params); - - try { - _controller.processAction("patch", params); - fail("Wheels.InvalidAuthenticityToken error did not occur."); - } catch (any e) { - type = e.Type; - assert("type is 'Wheels.InvalidAuthenticityToken'"); - } - } - - function test_csrf_protection_with_invalid_x_csrf_token_header_on_ajax_delete_request() { - request.$wheelsHeaders["X-CSRF-TOKEN"] = "#csrfToken#1"; - request.cgi.request_method = "DELETE"; - request.cgi.http_x_requested_with = "XMLHTTPRequest"; - params = {controller = "csrfProtectedWithException", action = "delete"}; - _controller = controller("csrfProtectedWithException", params); - - try { - _controller.processAction("patch", params); - fail("Wheels.InvalidAuthenticityToken error did not occur."); - } catch (any e) { - type = e.Type; - assert("type is 'Wheels.InvalidAuthenticityToken'"); - } - } - -} diff --git a/core/src/wheels/tests/controller/csrf/cookie/setup.cfm b/core/src/wheels/tests/controller/csrf/cookie/setup.cfm deleted file mode 100644 index 6a0ea9a4eb..0000000000 --- a/core/src/wheels/tests/controller/csrf/cookie/setup.cfm +++ /dev/null @@ -1,8 +0,0 @@ - -$oldCsrfStore = application.wheels.csrfStore; -application.wheels.csrfStore = "cookie"; - -$oldRequestMethod = request.cgi.request_method; -$oldHttpXRequestedWith = request.cgi.http_x_requested_with; -csrfToken = controller("dummy").$readAuthenticityTokenFromCookie(); - diff --git a/core/src/wheels/tests/controller/csrf/cookie/teardown.cfm b/core/src/wheels/tests/controller/csrf/cookie/teardown.cfm deleted file mode 100644 index 588b7a16bf..0000000000 --- a/core/src/wheels/tests/controller/csrf/cookie/teardown.cfm +++ /dev/null @@ -1,7 +0,0 @@ - -application.wheels.csrfStore = $oldCsrfStore; - -request.cgi.request_method = $oldRequestMethod; -request.cgi.http_x_requested_with = $oldHttpXRequestedWith; -StructDelete(request.$wheelsHeaders, "X-CSRF-TOKEN"); - diff --git a/core/src/wheels/tests/controller/csrf/session/CsrfProtectedExcept.cfc b/core/src/wheels/tests/controller/csrf/session/CsrfProtectedExcept.cfc deleted file mode 100644 index 2c73730a25..0000000000 --- a/core/src/wheels/tests/controller/csrf/session/CsrfProtectedExcept.cfc +++ /dev/null @@ -1,151 +0,0 @@ -component extends="wheels.tests.Test" { - - function setup() { - super.setup(); - include "setup.cfm"; - } - - function teardown() { - super.teardown(); - include "teardown.cfm"; - } - - function test_csrf_protection_with_valid_authenticityToken_on_PATCH_request() { - request.cgi.request_method = "PATCH"; - params = {controller = "csrfProtectedExcept", action = "update", authenticityToken = CsrfGenerateToken()}; - _controller = controller("csrfProtectedExcept", params); - - _controller.processAction("update", params); - assert('_controller.response() eq "Update ran."'); - } - - function test_csrf_protection_with_no_authenticityToken_on_PATCH_request() { - request.cgi.request_method = "PATCH"; - params = {controller = "csrfProtectedExcept", action = "update"}; - _controller = controller("csrfProtectedExcept", params); - - try { - _controller.processAction("update", params); - fail("Wheels.InvalidAuthenticityToken error did not occur."); - } catch (any e) { - type = e.Type; - assert("type is 'Wheels.InvalidAuthenticityToken'"); - } - } - - function test_csrf_protection_with_invalid_authenticityToken_on_PATCH_request() { - request.cgi.request_method = "PATCH"; - params = {controller = "csrfProtectedExcept", action = "update", authenticityToken = "1#CsrfGenerateToken()#"}; - _controller = controller("csrfProtectedExcept", params); - - try { - _controller.processAction("update", params); - fail("Wheels.InvalidAuthenticityToken error did not occur."); - } catch (any e) { - type = e.Type; - assert("type is 'Wheels.InvalidAuthenticityToken'"); - } - } - - function test_csrf_protection_with_valid_x_csrf_token_header_on_ajax_PATCH_request() { - request.$wheelsHeaders["X-CSRF-TOKEN"] = CsrfGenerateToken(); - request.cgi.request_method = "PATCH"; - request.cgi.http_x_requested_with = "XMLHTTPRequest"; - params = {controller = "csrfProtectedExcept", action = "update"}; - _controller = controller("csrfProtectedExcept", params); - - _controller.processAction("update", params); - assert('_controller.response() eq "Update ran."'); - } - - function test_csrf_protection_with_no_x_csrf_token_header_on_ajax_PATCH_request() { - request.cgi.request_method = "PATCH"; - request.cgi.http_x_requested_with = "XMLHTTPRequest"; - params = {controller = "csrfProtectedExcept", action = "update"}; - _controller = controller("csrfProtectedExcept", params); - - try { - _controller.processAction("update", params); - fail("Wheels.InvalidAuthenticityToken error did not occur."); - } catch (any e) { - type = e.Type; - assert("type is 'Wheels.InvalidAuthenticityToken'"); - } - } - - function test_csrf_protection_with_invalid_x_csrf_token_header_on_ajax_PATCH_request() { - request.$wheelsHeaders["X-CSRF-TOKEN"] = "1#CsrfGenerateToken()#"; - request.cgi.request_method = "PATCH"; - request.cgi.http_x_requested_with = "XMLHTTPRequest"; - params = {controller = "csrfProtectedExcept", action = "update"}; - _controller = controller("csrfProtectedExcept", params); - - try { - _controller.processAction("update", params); - fail("Wheels.InvalidAuthenticityToken error did not occur."); - } catch (any e) { - type = e.Type; - assert("type is 'Wheels.InvalidAuthenticityToken'"); - } - } - - function test_skipped_csrf_protection_on_PATCH_request_with_valid_authenticityToken() { - request.cgi.request_method = "PATCH"; - params = {controller = "csrfProtectedExcept", action = "show", authenticityToken = CsrfGenerateToken()}; - _controller = controller("csrfProtectedExcept", params); - - _controller.processAction("update", params); - assert('_controller.response() eq "Show ran."'); - } - - function test_skipped_csrf_protection_on_PATCH_request_with_no_authenticityToken() { - request.cgi.request_method = "PATCH"; - params = {controller = "csrfProtectedExcept", action = "show"}; - _controller = controller("csrfProtectedExcept", params); - - _controller.processAction("update", params); - assert('_controller.response() eq "Show ran."'); - } - - function test_skipped_csrf_protection_on_PATCH_request_with_invalid_authenticityToken() { - request.cgi.request_method = "PATCH"; - params = {controller = "csrfProtectedExcept", action = "show", authenticityToken = "1#CsrfGenerateToken()#"}; - _controller = controller("csrfProtectedExcept", params); - - _controller.processAction("update", params); - assert('_controller.response() eq "Show ran."'); - } - - function test_skipped_csrf_protection_on_ajax_PATCH_request_with_valid_x_csrf_token_header() { - request.$wheelsHeaders["X-CSRF-TOKEN"] = CsrfGenerateToken(); - request.cgi.request_method = "PATCH"; - request.cgi.http_x_requested_with = "XMLHTTPRequest"; - params = {controller = "csrfProtectedExcept", action = "show"}; - _controller = controller("csrfProtectedExcept", params); - - _controller.processAction("update", params); - assert('_controller.response() eq "Show ran."'); - } - - function test_skipped_csrf_protection_on_ajax_PATCH_request_with_no_x_csrf_token_header() { - request.cgi.request_method = "PATCH"; - request.cgi.http_x_requested_with = "XMLHTTPRequest"; - params = {controller = "csrfProtectedExcept", action = "show"}; - _controller = controller("csrfProtectedExcept", params); - - _controller.processAction("update", params); - assert('_controller.response() eq "Show ran."'); - } - - function test_skipped_csrf_protection_on_ajax_PATCH_request_with_invalid_x_csrf_token_header() { - request.$wheelsHeaders["X-CSRF-TOKEN"] = "1#CsrfGenerateToken()#"; - request.cgi.request_method = "PATCH"; - request.cgi.http_x_requested_with = "XMLHTTPRequest"; - params = {controller = "csrfProtectedExcept", action = "show"}; - _controller = controller("csrfProtectedExcept", params); - - _controller.processAction("update", params); - assert('_controller.response() eq "Show ran."'); - } - -} diff --git a/core/src/wheels/tests/controller/csrf/session/CsrfProtectedOnly.cfc b/core/src/wheels/tests/controller/csrf/session/CsrfProtectedOnly.cfc deleted file mode 100644 index 0636564a38..0000000000 --- a/core/src/wheels/tests/controller/csrf/session/CsrfProtectedOnly.cfc +++ /dev/null @@ -1,151 +0,0 @@ -component extends="wheels.tests.Test" { - - function setup() { - super.setup(); - include "setup.cfm"; - } - - function teardown() { - super.teardown(); - include "teardown.cfm"; - } - - function test_csrf_protection_with_valid_authenticityToken_on_post_request() { - request.cgi.request_method = "POST"; - params = {controller = "csrfProtectedOnly", action = "create", authenticityToken = CsrfGenerateToken()}; - _controller = controller("csrfProtectedOnly", params); - - _controller.processAction("create", params); - assert('_controller.response() eq "Create ran."'); - } - - function test_csrf_protection_with_no_authenticityToken_on_post_request() { - request.cgi.request_method = "POST"; - params = {controller = "csrfProtectedOnly", action = "create"}; - _controller = controller("csrfProtectedOnly", params); - - try { - _controller.processAction("create", params); - fail("Wheels.InvalidAuthenticityToken error did not occur."); - } catch (any e) { - type = e.Type; - assert("type is 'Wheels.InvalidAuthenticityToken'"); - } - } - - function test_csrf_protection_with_invalid_authenticityToken_on_post_request() { - request.cgi.request_method = "POST"; - params = {controller = "csrfProtectedOnly", action = "create", authenticityToken = "1#CsrfGenerateToken()#"}; - _controller = controller("csrfProtectedOnly", params); - - try { - _controller.processAction("create", params); - fail("Wheels.InvalidAuthenticityToken error did not occur."); - } catch (any e) { - type = e.Type; - assert("type is 'Wheels.InvalidAuthenticityToken'"); - } - } - - function test_csrf_protection_with_valid_x_csrf_token_header_on_ajax_post_request() { - request.$wheelsHeaders["X-CSRF-TOKEN"] = CsrfGenerateToken(); - request.cgi.request_method = "POST"; - request.cgi.http_x_requested_with = "XMLHTTPRequest"; - params = {controller = "csrfProtectedOnly", action = "create", authenticityToken = CsrfGenerateToken()}; - _controller = controller("csrfProtectedOnly", params); - - _controller.processAction("create", params); - assert('_controller.response() eq "Create ran."'); - } - - function test_csrf_protection_with_no_x_csrf_token_header_on_ajax_post_request() { - request.cgi.request_method = "POST"; - request.cgi.http_x_requested_with = "XMLHTTPRequest"; - params = {controller = "csrfProtectedOnly", action = "create"}; - _controller = controller("csrfProtectedOnly", params); - - try { - _controller.processAction("create", params); - fail("Wheels.InvalidAuthenticityToken error did not occur."); - } catch (any e) { - type = e.Type; - assert("type is 'Wheels.InvalidAuthenticityToken'"); - } - } - - function test_csrf_protection_with_invalid_x_csrf_token_header_on_ajax_post_request() { - request.$wheelsHeaders["X-CSRF-TOKEN"] = "1#CsrfGenerateToken()#"; - request.cgi.request_method = "POST"; - request.cgi.http_x_requested_with = "XMLHTTPRequest"; - params = {controller = "csrfProtectedOnly", action = "create", authenticityToken = "1#CsrfGenerateToken()#"}; - _controller = controller("csrfProtectedOnly", params); - - try { - _controller.processAction("create", params); - fail("Wheels.InvalidAuthenticityToken error did not occur."); - } catch (any e) { - type = e.Type; - assert("type is 'Wheels.InvalidAuthenticityToken'"); - } - } - - function test_skipped_csrf_protection_on_post_request_with_valid_authenticityToken() { - request.cgi.request_method = "POST"; - params = {controller = "csrfProtectedOnly", action = "index", authenticityToken = CsrfGenerateToken()}; - _controller = controller("csrfProtectedOnly", params); - - _controller.processAction("create", params); - assert('_controller.response() eq "Index ran."'); - } - - function test_skipped_csrf_protection_on_post_request_with_no_authenticityToken() { - request.cgi.request_method = "POST"; - params = {controller = "csrfProtectedOnly", action = "index"}; - _controller = controller("csrfProtectedOnly", params); - - _controller.processAction("create", params); - assert('_controller.response() eq "Index ran."'); - } - - function test_skipped_csrf_protection_on_post_request_with_invalid_authenticityToken() { - request.cgi.request_method = "POST"; - params = {controller = "csrfProtectedOnly", action = "index", authenticityToken = "1#CsrfGenerateToken()#"}; - _controller = controller("csrfProtectedOnly", params); - - _controller.processAction("create", params); - assert('_controller.response() eq "Index ran."'); - } - - function test_skipped_csrf_protection_on_ajax_post_request_with_valid_x_csrf_token_header() { - request.$wheelsHeaders["X-CSRF-TOKEN"] = CsrfGenerateToken(); - request.cgi.request_method = "POST"; - request.cgi.http_x_requested_with = "XMLHTTPRequest"; - params = {controller = "csrfProtectedOnly", action = "index"}; - _controller = controller("csrfProtectedOnly", params); - - _controller.processAction("create", params); - assert('_controller.response() eq "Index ran."'); - } - - function test_skipped_csrf_protection_on_ajax_post_request_with_no_x_csrf_token_header() { - request.cgi.request_method = "POST"; - request.cgi.http_x_requested_with = "XMLHTTPRequest"; - params = {controller = "csrfProtectedOnly", action = "index"}; - _controller = controller("csrfProtectedOnly", params); - - _controller.processAction("create", params); - assert('_controller.response() eq "Index ran."'); - } - - function test_skipped_csrf_protection_on_ajax_post_request_with_invalid_x_csrf_token_header() { - request.$wheelsHeaders["X-CSRF-TOKEN"] = "1#CsrfGenerateToken()#"; - request.cgi.request_method = "POST"; - request.cgi.http_x_requested_with = "XMLHTTPRequest"; - params = {controller = "csrfProtectedOnly", action = "index", authenticityToken = "1#CsrfGenerateToken()#"}; - _controller = controller("csrfProtectedOnly", params); - - _controller.processAction("create", params); - assert('_controller.response() eq "Index ran."'); - } - -} diff --git a/core/src/wheels/tests/controller/csrf/session/CsrfProtectedWithException.cfc b/core/src/wheels/tests/controller/csrf/session/CsrfProtectedWithException.cfc deleted file mode 100644 index 3afc9e807e..0000000000 --- a/core/src/wheels/tests/controller/csrf/session/CsrfProtectedWithException.cfc +++ /dev/null @@ -1,317 +0,0 @@ -component extends="wheels.tests.Test" { - - function setup() { - super.setup(); - include "setup.cfm"; - } - - function teardown() { - super.teardown(); - include "teardown.cfm"; - } - - function test_csrf_protection_skipped_on_get_request() { - params = {controller = "csrfProtectedWithException", action = "index"}; - _controller = controller("csrfProtectedWithException", params); - - _controller.processAction("index", params); - assert('_controller.response() eq "Index ran."'); - } - - function test_csrf_protection_skipped_on_get_ajax_request() { - request.cgi.http_x_requested_with = "XMLHTTPRequest"; - params = {controller = "csrfProtectedWithException", action = "index"}; - _controller = controller("csrfProtectedWithException", params); - - _controller.processAction("index", params); - assert('_controller.response() eq "Index ran."'); - } - - function test_csrf_protection_skipped_on_options_request() { - request.cgi.request_method = "OPTIONS"; - params = {controller = "csrfProtectedWithException", action = "index"}; - _controller = controller("csrfProtectedWithException", params); - - _controller.processAction("index", params); - assert('_controller.response() eq "Index ran."'); - } - - function test_csrf_protection_skipped_on_options_ajax_request() { - request.cgi.http_x_requested_with = "XMLHTTPRequest"; - request.cgi.request_method = "OPTIONS"; - params = {controller = "csrfProtectedWithException", action = "index"}; - _controller = controller("csrfProtectedWithException", params); - - _controller.processAction("index", params); - assert('_controller.response() eq "Index ran."'); - } - - function test_csrf_protection_skipped_on_head_request() { - request.cgi.request_method = "HEAD"; - params = {controller = "csrfProtectedWithException", action = "index"}; - _controller = controller("csrfProtectedWithException", params); - - _controller.processAction("index", params); - assert('_controller.response() eq "Index ran."'); - } - - function test_csrf_protection_skipped_on_head_ajax_request() { - request.cgi.http_x_requested_with = "XMLHTTPRequest"; - request.cgi.request_method = "HEAD"; - params = {controller = "csrfProtectedWithException", action = "index"}; - _controller = controller("csrfProtectedWithException", params); - - _controller.processAction("index", params); - assert('_controller.response() eq "Index ran."'); - } - - function test_csrf_protection_with_valid_authenticityToken_on_post_request() { - request.cgi.request_method = "POST"; - params = {controller = "csrfProtectedWithException", action = "create", authenticityToken = CsrfGenerateToken()}; - _controller = controller("csrfProtectedWithException", params); - - _controller.processAction("create", params); - assert('_controller.response() eq "Create ran."'); - } - - function test_csrf_protection_with_no_authenticityToken_on_post_request() { - request.cgi.request_method = "POST"; - params = {controller = "csrfProtectedWithException", action = "create"}; - _controller = controller("csrfProtectedWithException", params); - - try { - _controller.processAction("create", params); - fail("Wheels.InvalidAuthenticityToken error did not occur."); - } catch (any e) { - type = e.Type; - assert("type is 'Wheels.InvalidAuthenticityToken'"); - } - } - - function test_csrf_protection_with_invalid_authenticityToken_on_post_request() { - request.cgi.request_method = "POST"; - params = { - controller = "csrfProtectedWithException", - action = "create", - authenticityToken = "1#CsrfGenerateToken()#" - }; - _controller = controller("csrfProtectedWithException", params); - - try { - _controller.processAction("create", params); - fail("Wheels.InvalidAuthenticityToken error did not occur."); - } catch (any e) { - type = e.Type; - assert("type is 'Wheels.InvalidAuthenticityToken'"); - } - } - - function test_csrf_protection_with_valid_authenticityToken_on_patch_request() { - request.cgi.request_method = "PATCH"; - params = {controller = "csrfProtectedWithException", action = "update", authenticityToken = CsrfGenerateToken()}; - _controller = controller("csrfProtectedWithException", params); - - _controller.processAction("update", params); - assert('_controller.response() eq "Update ran."'); - } - - function test_csrf_protection_with_no_authenticityToken_on_patch_request() { - request.cgi.request_method = "PATCH"; - params = {controller = "csrfProtectedWithException", action = "update"}; - _controller = controller("csrfProtectedWithException", params); - - try { - _controller.processAction("update", params); - fail("Wheels.InvalidAuthenticityToken error did not occur."); - } catch (any e) { - type = e.Type; - assert("type is 'Wheels.InvalidAuthenticityToken'"); - } - } - - function test_csrf_protection_with_invalid_authenticityToken_on_patch_request() { - request.cgi.request_method = "PATCH"; - params = { - controller = "csrfProtectedWithException", - action = "update", - authenticityToken = "1#CsrfGenerateToken()#" - }; - _controller = controller("csrfProtectedWithException", params); - - try { - _controller.processAction("update", params); - fail("Wheels.InvalidAuthenticityToken error did not occur."); - } catch (any e) { - type = e.Type; - assert("type is 'Wheels.InvalidAuthenticityToken'"); - } - } - - function test_csrf_protection_with_valid_authenticityToken_on_delete_request() { - request.cgi.request_method = "DELETE"; - params = {controller = "csrfProtectedWithException", action = "delete", authenticityToken = CsrfGenerateToken()}; - _controller = controller("csrfProtectedWithException", params); - - _controller.processAction("delete", params); - assert('_controller.response() eq "Delete ran."'); - } - - function test_csrf_protection_with_no_authenticityToken_on_delete_request() { - request.cgi.request_method = "DELETE"; - params = {controller = "csrfProtectedWithException", action = "delete"}; - _controller = controller("csrfProtectedWithException", params); - - try { - _controller.processAction("delete", params); - fail("Wheels.InvalidAuthenticityToken error did not occur."); - } catch (any e) { - type = e.Type; - assert("type is 'Wheels.InvalidAuthenticityToken'"); - } - } - - function test_csrf_protection_with_invalid_authenticityToken_on_delete_request() { - request.cgi.request_method = "DELETE"; - params = { - controller = "csrfProtectedWithException", - action = "delete", - authenticityToken = "1#CsrfGenerateToken()#" - }; - _controller = controller("csrfProtectedWithException", params); - - try { - _controller.processAction("delete", params); - fail("Wheels.InvalidAuthenticityToken error did not occur."); - } catch (any e) { - type = e.Type; - assert("type is 'Wheels.InvalidAuthenticityToken'"); - } - } - - function test_csrf_protection_with_valid_x_csrf_token_header_on_ajax_post_request() { - request.$wheelsHeaders["X-CSRF-TOKEN"] = CsrfGenerateToken(); - request.cgi.request_method = "POST"; - request.cgi.http_x_requested_with = "XMLHTTPRequest"; - params = {controller = "csrfProtectedWithException", action = "create"}; - _controller = controller("csrfProtectedWithException", params); - - _controller.processAction("create", params); - assert('_controller.response() eq "Create ran."'); - } - - function test_csrf_protection_with_no_x_csrf_token_header_on_ajax_post_request() { - request.cgi.request_method = "POST"; - request.cgi.http_x_requested_with = "XMLHTTPRequest"; - params = {controller = "csrfProtectedWithException", action = "create"}; - _controller = controller("csrfProtectedWithException", params); - - try { - _controller.processAction("create", params); - fail("Wheels.InvalidAuthenticityToken error did not occur."); - } catch (any e) { - type = e.Type; - assert("type is 'Wheels.InvalidAuthenticityToken'"); - } - } - - function test_csrf_protection_with_invalid_x_csrf_token_header_on_ajax_post_request() { - request.$wheelsHeaders["X-CSRF-TOKEN"] = "1#CsrfGenerateToken()#"; - request.cgi.request_method = "POST"; - request.cgi.http_x_requested_with = "XMLHTTPRequest"; - params = {controller = "csrfProtectedWithException", action = "create"}; - _controller = controller("csrfProtectedWithException", params); - - try { - _controller.processAction("create", params); - fail("Wheels.InvalidAuthenticityToken error did not occur."); - } catch (any e) { - type = e.Type; - assert("type is 'Wheels.InvalidAuthenticityToken'"); - } - } - - function test_csrf_protection_with_valid_x_csrf_token_header_on_ajax_patch_request() { - request.$wheelsHeaders["X-CSRF-TOKEN"] = CsrfGenerateToken(); - request.cgi.request_method = "PATCH"; - request.cgi.http_x_requested_with = "XMLHTTPRequest"; - params = {controller = "csrfProtectedWithException", action = "update"}; - _controller = controller("csrfProtectedWithException", params); - - _controller.processAction("patch", params); - assert('_controller.response() eq "Update ran."'); - } - - function test_csrf_protection_with_no_x_csrf_token_header_on_ajax_patch_request() { - request.cgi.request_method = "PATCH"; - request.cgi.http_x_requested_with = "XMLHTTPRequest"; - params = {controller = "csrfProtectedWithException", action = "update"}; - _controller = controller("csrfProtectedWithException", params); - - try { - _controller.processAction("patch", params); - fail("Wheels.InvalidAuthenticityToken error did not occur."); - } catch (any e) { - type = e.Type; - assert("type is 'Wheels.InvalidAuthenticityToken'"); - } - } - - function test_csrf_protection_with_invalid_x_csrf_token_header_on_ajax_patch_request() { - request.$wheelsHeaders["X-CSRF-TOKEN"] = "1#CsrfGenerateToken()#"; - request.cgi.request_method = "PATCH"; - request.cgi.http_x_requested_with = "XMLHTTPRequest"; - params = {controller = "csrfProtectedWithException", action = "update"}; - _controller = controller("csrfProtectedWithException", params); - - try { - _controller.processAction("patch", params); - fail("Wheels.InvalidAuthenticityToken error did not occur."); - } catch (any e) { - type = e.Type; - assert("type is 'Wheels.InvalidAuthenticityToken'"); - } - } - - function test_csrf_protection_with_valid_x_csrf_token_header_on_ajax_delete_request() { - request.$wheelsHeaders["X-CSRF-TOKEN"] = CsrfGenerateToken(); - request.cgi.request_method = "DELETE"; - request.cgi.http_x_requested_with = "XMLHTTPRequest"; - params = {controller = "csrfProtectedWithException", action = "delete"}; - _controller = controller("csrfProtectedWithException", params); - - _controller.processAction("patch", params); - assert('_controller.response() eq "Delete ran."'); - } - - function test_csrf_protection_with_no_x_csrf_token_header_on_ajax_delete_request() { - request.cgi.request_method = "DELETE"; - request.cgi.http_x_requested_with = "XMLHTTPRequest"; - params = {controller = "csrfProtectedWithException", action = "delete"}; - _controller = controller("csrfProtectedWithException", params); - - try { - _controller.processAction("patch", params); - fail("Wheels.InvalidAuthenticityToken error did not occur."); - } catch (any e) { - type = e.Type; - assert("type is 'Wheels.InvalidAuthenticityToken'"); - } - } - - function test_csrf_protection_with_invalid_x_csrf_token_header_on_ajax_delete_request() { - request.$wheelsHeaders["X-CSRF-TOKEN"] = "1#CsrfGenerateToken()#"; - request.cgi.request_method = "DELETE"; - request.cgi.http_x_requested_with = "XMLHTTPRequest"; - params = {controller = "csrfProtectedWithException", action = "delete"}; - _controller = controller("csrfProtectedWithException", params); - - try { - _controller.processAction("patch", params); - fail("Wheels.InvalidAuthenticityToken error did not occur."); - } catch (any e) { - type = e.Type; - assert("type is 'Wheels.InvalidAuthenticityToken'"); - } - } - -} diff --git a/core/src/wheels/tests/controller/csrf/session/setup.cfm b/core/src/wheels/tests/controller/csrf/session/setup.cfm deleted file mode 100644 index fbd6b27f73..0000000000 --- a/core/src/wheels/tests/controller/csrf/session/setup.cfm +++ /dev/null @@ -1,6 +0,0 @@ - -$oldRequestMethod = request.cgi.request_method; -$oldHttpXRequestedWith = request.cgi.http_x_requested_with; -$oldCsrfStore = application.wheels.csrfStore; -application.wheels.csrfStore = "session"; - diff --git a/core/src/wheels/tests/controller/csrf/session/teardown.cfm b/core/src/wheels/tests/controller/csrf/session/teardown.cfm deleted file mode 100644 index 50442d7e85..0000000000 --- a/core/src/wheels/tests/controller/csrf/session/teardown.cfm +++ /dev/null @@ -1,6 +0,0 @@ - -request.cgi.request_method = $oldRequestMethod; -request.cgi.http_x_requested_with = $oldHttpXRequestedWith; -StructDelete(request.$wheelsHeaders, "X-CSRF-TOKEN"); -application.wheels.csrfStore = $oldCsrfStore; - diff --git a/core/src/wheels/tests/controller/filters/filterchain.cfc b/core/src/wheels/tests/controller/filters/filterchain.cfc deleted file mode 100644 index 29dca64621..0000000000 --- a/core/src/wheels/tests/controller/filters/filterchain.cfc +++ /dev/null @@ -1,57 +0,0 @@ -component extends="wheels.tests.Test" { - - function setup() { - params = {controller = "dummy", action = "dummy"}; - _controller = controller("dummy", params); - _controller.before1 = before1; - _controller.before2 = before2; - _controller.before3 = before3; - _controller.after1 = after1; - _controller.after2 = after2; - } - - - function test_return_correct_type() { - _controller.filters(through = "before1", type = "before"); - _controller.filters(through = "before2", type = "before"); - _controller.filters(through = "before3", type = "before"); - _controller.filters(through = "after1", type = "after"); - _controller.filters(through = "after2", type = "after"); - - before = _controller.filterChain("before"); - after = _controller.filterChain("after"); - all = _controller.filterChain(); - - assert('ArrayLen(before) eq 3'); - assert('before[1].through eq "before1"'); - assert('before[2].through eq "before2"'); - assert('before[3].through eq "before3"'); - - assert('ArrayLen(after) eq 2'); - assert('after[1].through eq "after1"'); - assert('after[2].through eq "after2"'); - - assert('ArrayLen(all) eq 5'); - } - - function before1() { - return "before1"; - } - - function before2() { - return "before2"; - } - - function before3() { - return "before3"; - } - - function after1() { - return "after1"; - } - - function after2() { - return "after2"; - } - -} diff --git a/core/src/wheels/tests/controller/filters/filters.cfc b/core/src/wheels/tests/controller/filters/filters.cfc deleted file mode 100644 index 3868d602da..0000000000 --- a/core/src/wheels/tests/controller/filters/filters.cfc +++ /dev/null @@ -1,15 +0,0 @@ -component extends="wheels.tests.Test" { - - function test_adding_filter() { - local.controller = controller(name = "dummy"); - local.controller.setFilterChain([]); - local.args = {}; - local.args.through = "restrictAccess"; - local.controller.filters(argumentcollection = local.args); - result = ArrayLen(local.controller.filterChain()); - expected = 1; - assert("result eq expected"); - local.controller.setFilterChain([]); - } - -} diff --git a/core/src/wheels/tests/controller/filters/runfilters.cfc b/core/src/wheels/tests/controller/filters/runfilters.cfc deleted file mode 100644 index bef55d8a5d..0000000000 --- a/core/src/wheels/tests/controller/filters/runfilters.cfc +++ /dev/null @@ -1,49 +0,0 @@ -component extends="wheels.tests.Test" { - - function setup() { - params = {controller = "filtering", action = "index"}; - _controller = controller("filtering", params); - request.filterTests = StructNew(); - } - - function test_should_run_public() { - _controller.$runFilters(type = "before", action = "index"); - assert("StructKeyExists(request.filterTests, 'pubTest')"); - } - - function test_should_run_private() { - _controller.$runFilters(type = "before", action = "index"); - assert("StructKeyExists(request.filterTests, 'privTest')"); - } - - function test_should_run_in_order() { - _controller.$runFilters(type = "before", action = "index"); - assert("request.filterTests.test IS 'bothpubpriv'"); - } - - function test_should_not_run_excluded() { - _controller.$runFilters(type = "before", action = "doNotRun"); - assert("NOT StructKeyExists(request.filterTests, 'dirTest')"); - } - - function test_should_run_included_only() { - _controller.$runFilters(type = "before", action = "doesNotExist"); - assert("NOT StructKeyExists(request.filterTests, 'pubTest')"); - } - - function test_should_pass_direct_arguments() { - _controller.$runFilters(type = "before", action = "index"); - assert("request.filterTests.dirTest IS 1"); - } - - function test_should_pass_struct_arguments() { - _controller.$runFilters(type = "before", action = "index"); - assert("request.filterTests.strTest IS 21"); - } - - function test_should_pass_both_direct_and_struct_arguments() { - _controller.$runFilters(type = "before", action = "index"); - assert("request.filterTests.bothTest IS 31"); - } - -} diff --git a/core/src/wheels/tests/controller/filters/setfilterchain.cfc b/core/src/wheels/tests/controller/filters/setfilterchain.cfc deleted file mode 100644 index 78aada6381..0000000000 --- a/core/src/wheels/tests/controller/filters/setfilterchain.cfc +++ /dev/null @@ -1,32 +0,0 @@ -component extends="wheels.tests.Test" { - - function setup() { - params = {controller = "dummy", action = "dummy"}; - _controller = controller("dummy", params); - } - - - function test_valid() { - // Build filter chain through array - this is what we're testing - myFilterChain = [ - {through = "restrictAccess"}, - {through = "isLoggedIn,checkIPAddress", except = "home,login"}, - {type = "after", through = "logConversion", only = "thankYou"} - ]; - _controller.setFilterChain(myFilterChain); - filterChainSet = _controller.filterChain(); - // Undo test - _controller.setFilterChain(ArrayNew(1)); - // Build filter chain through "normal" filters() function - _controller.filters(through = "restrictAccess"); - _controller.filters(through = "isLoggedIn,checkIPAddress", except = "home,login"); - _controller.filters(type = "after", through = "logConversion", only = "thankYou"); - filterChainNormal = _controller.filterChain(); - // Undo test - _controller.setFilterChain(ArrayNew(1)); - // Compare filter chains - assert("ArrayLen(filterChainSet) eq ArrayLen(filterChainNormal)"); - assert("filterChainSet.equals(filterChainNormal)"); - } - -} diff --git a/core/src/wheels/tests/controller/flash/cookieStorage.cfc b/core/src/wheels/tests/controller/flash/cookieStorage.cfc deleted file mode 100644 index 860f63c897..0000000000 --- a/core/src/wheels/tests/controller/flash/cookieStorage.cfc +++ /dev/null @@ -1,18 +0,0 @@ -component extends="wheels.tests.Test" { - - function setup() { - include "setup.cfm"; - } - - function teardown() { - include "teardown.cfm"; - } - - function test_cookie_storage_should_be_enabled() { - _controller.$setFlashStorage("cookie"); - assert('_controller.$getFlashStorage() eq "cookie"'); - _controller.$setFlashStorage("session"); - assert('_controller.$getFlashStorage() eq "session"'); - } - -} diff --git a/core/src/wheels/tests/controller/flash/flash.cfc b/core/src/wheels/tests/controller/flash/flash.cfc deleted file mode 100644 index fe82c965e5..0000000000 --- a/core/src/wheels/tests/controller/flash/flash.cfc +++ /dev/null @@ -1,89 +0,0 @@ -component extends="wheels.tests.Test" { - - function setup() { - include "setup.cfm"; - } - - function teardown() { - include "teardown.cfm"; - } - - function test_key_exists() { - run_key_exists(); - _controller.$setFlashStorage("cookie"); - run_key_exists(); - } - - function run_key_exists() { - _controller.flashInsert(success = "Congrats!"); - result = _controller.flash("success"); - assert("result IS 'Congrats!'"); - } - - function test_key_does_not_exist() { - run_key_does_not_exist(); - _controller.$setFlashStorage("cookie"); - run_key_does_not_exist(); - } - - function run_key_does_not_exist() { - _controller.flashInsert(success = "Congrats!"); - result = _controller.flash("invalidkey"); - assert("result IS ''"); - } - - function test_key_is_blank() { - run_key_is_blank(); - _controller.$setFlashStorage("cookie"); - run_key_is_blank(); - } - - function run_key_is_blank() { - _controller.flashInsert(success = "Congrats!"); - result = _controller.flash(""); - assert("result IS ''"); - } - - function test_key_provided_flash_empty() { - run_key_provided_flash_empty(); - _controller.$setFlashStorage("cookie"); - run_key_provided_flash_empty(); - } - - function run_key_provided_flash_empty() { - _controller.flashInsert(success = "Congrats!"); - _controller.flashClear(); - result = _controller.flash("invalidkey"); - assert("result IS ''"); - } - - function test_no_key_provided_flash_not_empty() { - run_no_key_provided_flash_not_empty(); - _controller.$setFlashStorage("cookie"); - run_no_key_provided_flash_not_empty(); - } - - /** - * HELPERS - */ - - function run_no_key_provided_flash_not_empty() { - _controller.flashInsert(success = "Congrats!"); - result = _controller.flash(); - assert("IsStruct(result) AND StructKeyExists(result, 'success')"); - } - - function test_no_key_provided_flash_empty() { - run_no_key_provided_flash_empty(); - _controller.$setFlashStorage("cookie"); - run_no_key_provided_flash_empty(); - } - - function run_no_key_provided_flash_empty() { - _controller.flashInsert(success = "Congrats!"); - _controller.flashClear(); - result = _controller.flash(); - assert("IsStruct(result) AND StructIsEmpty(result)"); - } - -} diff --git a/core/src/wheels/tests/controller/flash/flashKeep.cfc b/core/src/wheels/tests/controller/flash/flashKeep.cfc deleted file mode 100644 index f7352d09f3..0000000000 --- a/core/src/wheels/tests/controller/flash/flashKeep.cfc +++ /dev/null @@ -1,33 +0,0 @@ -component extends="wheels.tests.Test" { - - function setup() { - include "setup.cfm"; - } - - function teardown() { - include "teardown.cfm"; - } - - function test_flashKeep_saves_flash_items() { - run_flashKeep_saves_flash_items(); - _controller.$setFlashStorage("cookie"); - run_flashKeep_saves_flash_items(); - } - - /** - * HELPERS - */ - - function run_flashKeep_saves_flash_items() { - _controller.flashInsert(tony = "Petruzzi", per = "Djurner", james = "Gibson"); - _controller.flashKeep("per,james"); - _controller.$flashClear(); - assert('_controller.flashCount() eq 2'); - assert('!_controller.flashKeyExists("tony")'); - assert('_controller.flashKeyExists("per")'); - assert('_controller.flashKeyExists("james")'); - assert('_controller.flash("per") eq "Djurner"'); - assert('_controller.flash("james") eq "Gibson"'); - } - -} diff --git a/core/src/wheels/tests/controller/flash/flashclear.cfc b/core/src/wheels/tests/controller/flash/flashclear.cfc deleted file mode 100644 index 5eeb3656f6..0000000000 --- a/core/src/wheels/tests/controller/flash/flashclear.cfc +++ /dev/null @@ -1,28 +0,0 @@ -component extends="wheels.tests.Test" { - - function setup() { - include "setup.cfm"; - } - - function teardown() { - include "teardown.cfm"; - } - - function test_flashClear_valid() { - run_flashClear_valid(); - _controller.$setFlashStorage("cookie"); - run_flashClear_valid(); - } - - /** - * HELPERS - */ - - function run_flashClear_valid() { - _controller.flashInsert(success = "Congrats!"); - _controller.flashClear(); - result = StructKeyList(_controller.flash()); - assert("result IS ''"); - } - -} diff --git a/core/src/wheels/tests/controller/flash/flashcount.cfc b/core/src/wheels/tests/controller/flash/flashcount.cfc deleted file mode 100644 index 91f567e5a1..0000000000 --- a/core/src/wheels/tests/controller/flash/flashcount.cfc +++ /dev/null @@ -1,29 +0,0 @@ -component extends="wheels.tests.Test" { - - function setup() { - include "setup.cfm"; - } - - function teardown() { - include "teardown.cfm"; - } - - function test_flashCount_valid() { - run_flashCount_valid(); - _controller.$setFlashStorage("cookie"); - run_flashCount_valid(); - } - - /** - * HELPERS - */ - - function run_flashCount_valid() { - _controller.flashInsert(success = "Congrats!"); - _controller.flashInsert(anotherKey = "Test!"); - result = _controller.flashCount(); - compare = _controller.flashCount(); - assert("result IS compare"); - } - -} diff --git a/core/src/wheels/tests/controller/flash/flashdelete.cfc b/core/src/wheels/tests/controller/flash/flashdelete.cfc deleted file mode 100644 index 4b40c6c8e9..0000000000 --- a/core/src/wheels/tests/controller/flash/flashdelete.cfc +++ /dev/null @@ -1,40 +0,0 @@ -component extends="wheels.tests.Test" { - - function setup() { - include "setup.cfm"; - } - - function teardown() { - include "teardown.cfm"; - } - - function test_flashDelete_invalid() { - run_flashDelete_invalid(); - application.wheels.flashStorage = "cookie"; - run_flashDelete_invalid(); - } - - function run_flashDelete_invalid() { - _controller.flashClear(); - result = _controller.flashDelete(key = "success"); - assert("result IS false"); - } - - function test_flashDelete_valid() { - run_flashDelete_valid(); - application.wheels.flashStorage = "cookie"; - run_flashDelete_valid(); - } - - /** - * HELPERS - */ - - function run_flashDelete_valid() { - _controller.flashClear(); - _controller.flashInsert(success = "Congrats!"); - result = _controller.flashDelete(key = "success"); - assert("result IS true"); - } - -} diff --git a/core/src/wheels/tests/controller/flash/flashinsert.cfc b/core/src/wheels/tests/controller/flash/flashinsert.cfc deleted file mode 100644 index 9d86f58ac9..0000000000 --- a/core/src/wheels/tests/controller/flash/flashinsert.cfc +++ /dev/null @@ -1,38 +0,0 @@ -component extends="wheels.tests.Test" { - - function setup() { - include "setup.cfm"; - } - - function teardown() { - include "teardown.cfm"; - } - - function test_flashInsert_valid() { - run_flashInsert_valid(); - _controller.$setFlashStorage("cookie"); - run_flashInsert_valid(); - } - - function run_flashInsert_valid() { - _controller.flashInsert(success = "Congrats!"); - assert("_controller.flash('success') IS 'Congrats!'"); - } - - function test_flashInsert_mulitple() { - run_flashInsert_mulitple(); - _controller.$setFlashStorage("cookie"); - run_flashInsert_mulitple(); - } - - /** - * HELPERS - */ - - function run_flashInsert_mulitple() { - _controller.flashInsert(success = "Hooray!!!", error = "WTF!"); - assert("_controller.flash('success') IS 'Hooray!!!'"); - assert("_controller.flash('error') IS 'WTF!'"); - } - -} diff --git a/core/src/wheels/tests/controller/flash/flashisempty.cfc b/core/src/wheels/tests/controller/flash/flashisempty.cfc deleted file mode 100644 index 65f8dcde3e..0000000000 --- a/core/src/wheels/tests/controller/flash/flashisempty.cfc +++ /dev/null @@ -1,39 +0,0 @@ -component extends="wheels.tests.Test" { - - function setup() { - include "setup.cfm"; - } - - function teardown() { - include "teardown.cfm"; - } - - function test_flashIsEmpty_valid() { - run_flashIsEmpty_valid(); - _controller.$setFlashStorage("cookie"); - run_flashIsEmpty_valid(); - } - - function run_flashIsEmpty_valid() { - _controller.flashClear(); - result = _controller.flashIsEmpty(); - assert("result IS true"); - } - - function test_flashIsEmpty_invalid() { - run_flashIsEmpty_invalid(); - _controller.$setFlashStorage("cookie"); - run_flashIsEmpty_invalid(); - } - - /** - * HELPERS - */ - - function run_flashIsEmpty_invalid() { - _controller.flashInsert(success = "Congrats!"); - result = _controller.flashIsEmpty(); - assert("result IS false"); - } - -} diff --git a/core/src/wheels/tests/controller/flash/flashkeyexists.cfc b/core/src/wheels/tests/controller/flash/flashkeyexists.cfc deleted file mode 100644 index 371e81e1fc..0000000000 --- a/core/src/wheels/tests/controller/flash/flashkeyexists.cfc +++ /dev/null @@ -1,27 +0,0 @@ -component extends="wheels.tests.Test" { - - function setup() { - include "setup.cfm"; - } - - function teardown() { - include "teardown.cfm"; - } - - function test_flash_key_exists() { - run_flash_key_exists(); - _controller.$setFlashStorage("cookie"); - run_flash_key_exists(); - } - - /** - * HELPERS - */ - - function run_flash_key_exists() { - _controller.flashInsert(success = "Congrats!"); - r = _controller.flashKeyExists("success"); - assert("r IS true"); - } - -} diff --git a/core/src/wheels/tests/controller/flash/flashmessages.cfc b/core/src/wheels/tests/controller/flash/flashmessages.cfc deleted file mode 100644 index 9cea7c918d..0000000000 --- a/core/src/wheels/tests/controller/flash/flashmessages.cfc +++ /dev/null @@ -1,186 +0,0 @@ -component extends="wheels.tests.Test" { - - function setup() { - include "setup.cfm"; - set(functionName = "flashMessages", encode = false); - } - - function teardown() { - include "teardown.cfm"; - set(functionName = "flashMessages", encode = true); - } - - function test_normal_output() { - run_normal_output(); - _controller.$setFlashStorage("cookie"); - run_normal_output(); - } - - function test_specific_key_only() { - run_specific_key_only(); - _controller.$setFlashStorage("cookie"); - run_specific_key_only(); - } - - function test_passing_through_id() { - run_passing_through_id(); - _controller.$setFlashStorage("cookie"); - run_passing_through_id(); - } - - function test_empty_flash() { - run_empty_flash(); - _controller.$setFlashStorage("cookie"); - run_empty_flash(); - } - - function test_empty_flash_includeEmptyContainer() { - run_empty_flash_includeEmptyContainer(); - _controller.$setFlashStorage("cookie"); - run_empty_flash_includeEmptyContainer(); - } - - function test_allow_complex_values() { - run_allow_complex_values(); - _controller.$setFlashStorage("cookie"); - run_allow_complex_values(); - } - - function test_appends_if_allowed() { - run_appends_if_allowed(); - _controller.$setFlashStorage("cookie"); - run_appends_if_allowed(); - } - - function test_control_order_via_keys_argument() { - run_control_order_via_keys_argument(); - _controller.$setFlashStorage("cookie"); - run_control_order_via_keys_argument(); - } - - function test_casing_of_class_attribute() { - run_casing_of_class_attribute(); - _controller.$setFlashStorage("cookie"); - run_casing_of_class_attribute(); - } - - function test_casing_of_class_attribute_mixed() { - run_casing_of_class_attribute_mixed(); - _controller.$setFlashStorage("cookie"); - run_casing_of_class_attribute_mixed(); - } - - function test_casing_of_class_attribute_upper() { - run_casing_of_class_attribute_upper(); - _controller.$setFlashStorage("cookie"); - run_casing_of_class_attribute_upper(); - } - - function test_setting_class() { - run_setting_class(); - _controller.$setFlashStorage("cookie"); - run_setting_class(); - } - - /** - * HELPERS - */ - - function run_normal_output() { - _controller.flashInsert(success = "Congrats!"); - _controller.flashInsert(alert = "Error!"); - actual = _controller.flashMessages(); - assert( - "actual IS '

Error!

Congrats!

'" - ); - } - - function run_specific_key_only() { - _controller.flashInsert(success = "Congrats!"); - _controller.flashInsert(alert = "Error!"); - actual = _controller.flashMessages(key = "alert"); - assert("actual IS '

Error!

'"); - } - - function run_passing_through_id() { - _controller.flashInsert(success = "Congrats!"); - actual = _controller.flashMessages(id = "my-id"); - assert("actual Contains '

Congrats!

' AND actual Contains 'id=""my-id""'"); - } - - function run_empty_flash() { - actual = _controller.flashMessages(); - assert("actual IS ''"); - } - - function run_empty_flash_includeEmptyContainer() { - actual = _controller.flashMessages(includeEmptyContainer = "true"); - assert("actual IS '
'"); - } - - function run_appends_if_allowed() { - _controller.$setFlashAppend(true); - _controller.flashInsert(success = "Congrats!"); - _controller.flashInsert(success = "Congrats Again!"); - actual = _controller.flashMessages(); - assert( - "actual IS '

Congrats!

Congrats Again!

'" - ); - _controller.$setFlashAppend(false); - } - - function run_allow_complex_values() { - arr = []; - arr[1] = "Congrats!"; - arr[2] = "Congrats Again!"; - _controller.flashInsert(success = arr); - actual = _controller.flashMessages(); - assert( - "actual IS '

Congrats!

Congrats Again!

'" - ); - } - - function run_control_order_via_keys_argument() { - _controller.flashInsert(success = "Congrats!"); - _controller.flashInsert(alert = "Error!"); - actual = _controller.flashMessages(keys = "success,alert"); - assert( - "actual IS '

Congrats!

Error!

'" - ); - actual = _controller.flashMessages(keys = "alert,success"); - assert( - "actual IS '

Error!

Congrats!

'" - ); - } - - function run_casing_of_class_attribute() { - _controller.flashInsert(something = ""); - actual = _controller.flashMessages(); - expected = 'class="something-message"'; - assert('Find(expected, actual)'); - _controller.flashInsert(someThing = ""); - } - - function run_casing_of_class_attribute_mixed() { - _controller.flashInsert(someThing = ""); - actual = _controller.flashMessages(); - expected = 'class="something-message"'; - assert('Find(expected, actual)'); - } - - function run_casing_of_class_attribute_upper() { - _controller.flashInsert(SOMETHING = ""); - actual = _controller.flashMessages(); - expected = 'class="something-message"'; - assert('Find(expected, actual)'); - } - - function run_setting_class() { - _controller.flashInsert(success = "test"); - actual = _controller.flashMessages(class = "custom-class"); - expected = 'class="custom-class"'; - e2 = 'class="success-message"'; - assert('Find(expected, actual) AND Find(e2, actual)'); - } - -} diff --git a/core/src/wheels/tests/controller/flash/flashnone.cfc b/core/src/wheels/tests/controller/flash/flashnone.cfc deleted file mode 100644 index 56b96abf98..0000000000 --- a/core/src/wheels/tests/controller/flash/flashnone.cfc +++ /dev/null @@ -1,17 +0,0 @@ -component extends="wheels.tests.Test" { - - function setup() { - include "setup.cfm"; - } - - function teardown() { - include "teardown.cfm"; - } - - function test_flash_none() { - _controller.$setFlashStorage("none"); - _controller.flashInsert(success = "I should not exist", error = "I should not exist either"); - actual = _controller.flashMessages(); - assert("actual IS ''"); - } -} diff --git a/core/src/wheels/tests/controller/flash/setup.cfm b/core/src/wheels/tests/controller/flash/setup.cfm deleted file mode 100644 index abe623abcf..0000000000 --- a/core/src/wheels/tests/controller/flash/setup.cfm +++ /dev/null @@ -1,8 +0,0 @@ - -params = {controller = "dummy", action = "dummy"}; -_controller = controller("dummy", params); -_controller.$setFlashStorage("cookie"); -_controller.flashClear(); -_controller.$setFlashStorage("session"); -_controller.flashClear(); - diff --git a/core/src/wheels/tests/controller/flash/teardown.cfm b/core/src/wheels/tests/controller/flash/teardown.cfm deleted file mode 100644 index b3b4aeb3a2..0000000000 --- a/core/src/wheels/tests/controller/flash/teardown.cfm +++ /dev/null @@ -1,3 +0,0 @@ - -_controller.flashClear(); - diff --git a/core/src/wheels/tests/controller/initialization/general.cfc b/core/src/wheels/tests/controller/initialization/general.cfc deleted file mode 100644 index 092117ce97..0000000000 --- a/core/src/wheels/tests/controller/initialization/general.cfc +++ /dev/null @@ -1,41 +0,0 @@ -component extends="wheels.tests.Test" { - - function setup() { - _originalViewPath = get("viewPath"); - application.wheels.viewPath = "/wheels/tests/_assets/views"; - } - - function teardown() { - application.wheels.viewPath = _originalViewPath; - StructDelete(variables, "c", false); - } - - function test_creating_controller_when_file_exists() { - c = controller(name = "test", params = {controller = "Test", action = "test"}); - assert('isObject(c)'); - name = c.$getControllerClassData().name; - assert('name eq "Test"'); - } - - function test_initializing_with_nested_controller() { - c = controller(name = "admin.Admin", params = {controller = "admin.Admin", action = "test"}); - assert('isObject(c)'); - name = c.$getControllerClassData().name; - assert('name eq "admin.Admin"'); - } - - function test_creating_controller_when_no_file_exists() { - c = controller(name = "Admin", params = {controller = "Admin", action = "test"}); - assert('isObject(c)'); - name = c.$getControllerClassData().name; - assert('name eq "Admin"'); - } - - function test_creating_controller_when_no_nested_file_exists() { - c = controller(name = "admin.nothing.Admin", params = {controller = "admin.nothing.Admin", action = "test"}); - assert('isObject(c)'); - name = c.$getControllerClassData().name; - assert('name eq "admin.nothing.Admin"'); - } - -} diff --git a/core/src/wheels/tests/controller/miscellaneous/callaction.cfc b/core/src/wheels/tests/controller/miscellaneous/callaction.cfc deleted file mode 100644 index 4b0dbb6c00..0000000000 --- a/core/src/wheels/tests/controller/miscellaneous/callaction.cfc +++ /dev/null @@ -1,24 +0,0 @@ -component extends="wheels.tests.Test" { - - function setup() { - params = {controller = "test", action = "test"}; - _controller = controller("test", params); - oldViewPath = application.wheels.viewPath; - application.wheels.viewPath = "/wheels/tests/_assets/views"; - } - - function teardown() { - application.wheels.viewPath = oldViewPath; - } - - function test_setting_variable_for_view() { - _controller.$callAction(action = "test"); - assert("_controller.response() Contains 'variableForViewContent'"); - } - - function test_implicitly_calling_render_page() { - _controller.$callAction(action = "test"); - assert("_controller.response() Contains 'view template content'"); - } - -} diff --git a/core/src/wheels/tests/controller/miscellaneous/document.cfm b/core/src/wheels/tests/controller/miscellaneous/document.cfm deleted file mode 100644 index ba84ca2679..0000000000 --- a/core/src/wheels/tests/controller/miscellaneous/document.cfm +++ /dev/null @@ -1,3 +0,0 @@ - -

Test

-
diff --git a/core/src/wheels/tests/controller/miscellaneous/helpers.cfc b/core/src/wheels/tests/controller/miscellaneous/helpers.cfc deleted file mode 100644 index 5fdae513a7..0000000000 --- a/core/src/wheels/tests/controller/miscellaneous/helpers.cfc +++ /dev/null @@ -1,29 +0,0 @@ -component extends="wheels.tests.Test" { - - function setup() { - if (StructKeyExists(request, "test")) { - StructDelete(request, "test"); - } - oldViewPath = application.wheels.viewPath; - application.wheels.viewPath = "/wheels/tests/_assets/views"; - application.wheels.existingHelperFiles = "test"; - params = {controller = "test", action = "helperCaller"}; - _controller = controller("test", params); - } - - function test_inclusion_of_global_helper_file() { - _controller.renderView(); - assert("StructKeyExists(request.test, 'globalHelperFunctionWasCalled')"); - } - - function test_inclusion_of_controller_helper_file() { - _controller.renderView(); - assert("StructKeyExists(request.test, 'controllerHelperFunctionWasCalled')"); - } - - function teardown() { - application.wheels.viewPath = oldViewPath; - application.wheels.existingHelperFiles = ""; - } - -} diff --git a/core/src/wheels/tests/controller/miscellaneous/performedRenderOrRedirect.cfc b/core/src/wheels/tests/controller/miscellaneous/performedRenderOrRedirect.cfc deleted file mode 100644 index ab9a321bb7..0000000000 --- a/core/src/wheels/tests/controller/miscellaneous/performedRenderOrRedirect.cfc +++ /dev/null @@ -1,46 +0,0 @@ -component extends="wheels.tests.Test" { - - function packageSetup() { - variables.counteactual = 1; - } - - function setup() { - params = {controller = "dummy", action = "dummy"}; - _controller = controller("dummy", params); - } - - function test_redirect_or_render_has_not_been_performed() { - expected = false; - actual = _controller.$performedRedirect(); - assert('actual eq expected'); - actual = _controller.$performedRender(); - assert('actual eq expected'); - actual = _controller.$performedRenderOrRedirect(); - assert('actual eq expected'); - } - - function test_only_redirect_was_performed() { - _controller.redirectTo(controller = "wheels", action = "wheels"); - expected = true; - actual = _controller.$performedRedirect(); - assert('actual eq expected'); - actual = _controller.$performedRenderOrRedirect(); - assert('actual eq expected'); - expected = false; - actual = _controller.$performedRender(); - assert('actual eq expected'); - } - - function test_only_render_was_performed() { - _controller.renderNothing(); - expected = true; - actual = _controller.$performedRender(); - assert('actual eq expected'); - actual = _controller.$performedRenderOrRedirect(); - assert('actual eq expected'); - expected = false; - actual = _controller.$performedRedirect(); - assert('actual eq expected'); - } - -} diff --git a/core/src/wheels/tests/controller/miscellaneous/sendfile.cfc b/core/src/wheels/tests/controller/miscellaneous/sendfile.cfc deleted file mode 100644 index 1d38a6c609..0000000000 --- a/core/src/wheels/tests/controller/miscellaneous/sendfile.cfc +++ /dev/null @@ -1,103 +0,0 @@ -component extends="wheels.tests.Test" { - - function setup() { - params = {controller = "test", action = "test"}; - _controller = controller("dummy", params); - args = {}; - args.deliver = false; - } - - function test_only_file_supplied() { - args.directory = "/wheels/tests/_assets"; - args.file = "files/cfwheels-logo.png"; - r = _controller.sendFile(argumentCollection = args); - assert('Right(r.file, 17) eq "cfwheels-logo.png"'); - assert('r.mime eq "image/png"'); - assert('r.name eq "cfwheels-logo.png"'); - } - - function test_get_test_info() { - args.directory = "/wheels/tests/_assets"; - args.file = "files/cfwheels-logo.png"; - args.name = "A Weird FileName.png"; - _controller.sendFile(argumentCollection = args); - r = _controller.getFiles(); - assert('Right(r[1].file, 17) eq "cfwheels-logo.png"'); - assert('r[1].mime eq "image/png"'); - assert('r[1].name eq "A Weird FileName.png"'); - } - - function test_file_and_name_supplied() { - args.directory = "/wheels/tests/_assets"; - args.file = "files/cfwheels-logo.png"; - args.name = "A Weird FileName.png"; - r = _controller.sendFile(argumentCollection = args); - assert('Right(r.file, 17) eq "cfwheels-logo.png"'); - assert('r.mime eq "image/png"'); - assert('r.name eq "A Weird FileName.png"'); - } - - function test_change_disposition() { - args.directory = "/wheels/tests/_assets"; - args.file = "files/cfwheels-logo.png"; - args.disposition = "attachment"; - r = _controller.sendFile(argumentCollection = args); - assert('Right(r.file, 17) eq "cfwheels-logo.png"'); - assert('r.disposition eq "attachment"'); - assert('r.mime eq "image/png"'); - assert('r.name eq "cfwheels-logo.png"'); - } - - function test_overload_mimetype() { - args.directory = "/wheels/tests/_assets"; - args.file = "files/cfwheels-logo.png"; - args.type = "wheels/custom"; - r = _controller.sendFile(argumentCollection = args); - assert('Right(r.file, 17) eq "cfwheels-logo.png"'); - assert('r.disposition eq "attachment"'); - assert('r.mime eq "wheels/custom"'); - assert('r.name eq "cfwheels-logo.png"'); - } - - function test_no_extension_one_file_exists() { - args.directory = "/wheels/tests/_assets"; - args.file = "files/sendFile"; - r = _controller.sendFile(argumentCollection = args); - assert('Right(r.file, 12) eq "sendFile.txt"'); - assert('r.mime eq "text/plain"'); - assert('r.name eq "sendFile.txt"'); - } - - function test_no_extension_multiple_files_exists() { - args.file = "../wheels/tests/_assets/files/cfwheels-logo"; - r = raised("_controller.sendFile(argumentCollection=args)"); - assert('r eq "Wheels.FileNotFound"'); - } - - function test_specifying_a_directory() { - args.directory = "/wheels/tests/_assets"; - args.file = "files/cfwheels-logo.png"; - r = _controller.sendFile(argumentCollection = args); - assert('Right(r.file, 17) eq "cfwheels-logo.png"'); - assert('r.mime eq "image/png"'); - assert('r.name eq "cfwheels-logo.png"'); - } - /* - function test_ram_resource() { - include "document.cfm"; // cfscript cfdocuemt isn't supported in cf10 - FileWrite("ram://cfwheels.pdf", cfwheels_pdf); - args.file = "ram://cfwheels.pdf"; - args.deleteFile = true; - r = _controller.sendFile(argumentCollection = args); - assert('r.file eq "ram://cfwheels.pdf"'); - assert('r.mime eq "application/pdf"'); - assert('r.name eq "cfwheels.pdf"'); - } - - function test_non_existent_ram_resource() { - args.file = "ram://doesnt_exist.pdf"; - r = raised("_controller.sendFile(argumentCollection=args)"); - assert('r eq "Wheels.FileNotFound"'); - }*/ - -} diff --git a/core/src/wheels/tests/controller/miscellaneous/sendmail.cfc b/core/src/wheels/tests/controller/miscellaneous/sendmail.cfc deleted file mode 100644 index 5b73dc8edc..0000000000 --- a/core/src/wheels/tests/controller/miscellaneous/sendmail.cfc +++ /dev/null @@ -1,157 +0,0 @@ -component extends="wheels.tests.Test" { - - function setup() { - params = {controller = "test", action = "test"}; - _controller = controller("dummy", params); - args = StructNew(); - args.subject = "dummy subject"; - args.to = "to-dummy@dummy.com"; - args.from = "from-dummy@dummy.com"; - args.deliver = false; - oldViewPath = application.wheels.viewPath; - application.wheels.viewPath = "/wheels/tests/_assets/views"; - oldFilePath = application.wheels.filePath; - application.wheels.filePath = "/wheels/tests/_assets/files"; - oldArgs = application.wheels.functions.sendEmail; - textBody = "dummy plain email body"; - HTMLBody = "

dummy html email body

"; - filePath = ExpandPath(application.wheels.filePath) & "/" & "emailcontent.txt"; - } - - function test_allow_default_for_from_to_and_subject() { - application.wheels.functions.sendEmail.from = "sender@example.com"; - application.wheels.functions.sendEmail.to = "recipient@example.com"; - application.wheels.functions.sendEmail.subject = "test email"; - r = default_args(template = ""); - assert('r.from eq "sender@example.com"'); - assert('r.to eq "recipient@example.com"'); - assert('r.subject eq "test email"'); - r = default_args( - template = "", - from = "custom_sender@example.com", - to = "custom_recipient@example.com", - subject = "custom suject" - ); - assert('r.from eq "custom_sender@example.com"'); - assert('r.to eq "custom_recipient@example.com"'); - assert('r.subject eq "custom suject"'); - } - - function test_sendemail_plain() { - args.template = "plainEmailTemplate"; - result = _controller.sendEmail(argumentCollection = args); - assert("ListLen(StructKeyList(result)) IS 6"); - assert("StructKeyExists(result, 'to')"); - assert("StructKeyExists(result, 'from')"); - assert("StructKeyExists(result, 'subject')"); - assert("result.type IS 'text'"); - assert("result.text IS textBody"); - assert("result.html IS ''"); - } - - function test_sendemail_html() { - args.template = "HTMLEmailTemplate"; - result = _controller.sendEmail(argumentCollection = args); - assert("result.type IS 'html'"); - assert("result.text IS ''"); - assert("result.html IS HTMLBody"); - } - - function test_sendemail_detectmultipart_with_html() { - args.template = "HTMLEmailTemplate"; - args.detectMultipart = true; - result = _controller.sendEmail(argumentCollection = args); - assert("result.type IS 'html'"); - } - - function test_sendemail_detectmultipart_with_plain() { - args.template = "plainEmailTemplate"; - args.detectMultipart = true; - result = _controller.sendEmail(argumentCollection = args); - assert("result.type IS 'text'"); - } - - function test_sendemail_type_argument_without_detectmultipart() { - args.template = "plainEmailTemplate"; - args.type = "html"; - result = _controller.sendEmail(argumentCollection = args); - assert("result.type IS 'html'"); - } - - function test_sendemail_combined_in_correct_order() { - args.templates = "HTMLEmailTemplate,plainEmailTemplate"; - result = _controller.sendEmail(argumentCollection = args); - assert("result.mailparts[1].type IS 'text' AND result.mailparts[2].tagContent IS HTMLBody"); - } - - function test_sendemail_with_layout() { - args.template = "HTMLEmailTemplate"; - args.layout = "emailLayout"; - result = _controller.sendEmail(argumentCollection = args); - assert("result.html Contains '
'"); - } - - function test_sendemail_with_attachment() { - args.template = "plainEmailTemplate"; - args.file = "cfwheels-logo.png"; - result = _controller.sendEmail(argumentCollection = args); - assert("result.mailparams[1].file Contains '_assets' AND result.mailparams[1].file Contains 'cfwheels-logo.png'"); - } - - function test_sendemail_with_attachments_external() { - args.template = "plainEmailTemplate"; - args.file = "cfwheels-logo.png,http://www.example.com/test.txt,c:\inetpub\wwwroot\cfwheels\something.pdf"; - result = _controller.sendEmail(argumentCollection = args); - assert("result.mailparams[1].file Contains '_assets' AND result.mailparams[1].file Contains 'cfwheels-logo.png'"); - assert("result.mailparams[2].file eq 'http://www.example.com/test.txt'"); - assert("result.mailparams[3].file eq 'c:\inetpub\wwwroot\cfwheels\something.pdf'"); - } - - function test_sendemail_with_custom_argument() { - args.template = "plainEmailTemplate"; - args.customArgument = "IPassedInThisAsACustomArgument"; - result = _controller.sendEmail(argumentCollection = args); - assert("result.text Contains 'IPassedInThisAsACustomArgument'"); - } - - function test_sendemail_from_different_path() { - args.template = "/shared/anotherPlainEmailTemplate"; - result = _controller.sendEmail(argumentCollection = args); - assert("result.text IS 'another dummy plain email body'"); - } - - function test_sendemail_from_sub_folder() { - args.template = "sub/anotherHTMLEmailTemplate"; - result = _controller.sendEmail(argumentCollection = args); - assert("result.html IS '

another dummy html email body

'"); - } - - function test_sendemail_with_writetofile() { - args.templates = "HTMLEmailTemplate,plainEmailTemplate"; - args.writeToFile = filePath; - if (FileExists(filePath)) { - FileDelete(filePath); - } - _controller.sendEmail(argumentCollection = args); - fileContent = FileRead(filePath); - FileDelete(filePath); - assert("fileContent contains HTMLBody"); - assert("fileContent contains textBody"); - } - - function teardown() { - application.wheels.viewPath = oldViewPath; - application.wheels.filePath = oldFilePath; - application.wheels.functions.sendEmail = oldArgs; - } - - /** - * HELPERS - */ - - function default_args() { - $args(args = arguments, name = "sendEmail", required = "template,from,to,subject"); - return arguments; - } - -} diff --git a/core/src/wheels/tests/controller/provides/initialization.cfc b/core/src/wheels/tests/controller/provides/initialization.cfc deleted file mode 100644 index dc96e843ef..0000000000 --- a/core/src/wheels/tests/controller/provides/initialization.cfc +++ /dev/null @@ -1,20 +0,0 @@ -component extends="wheels.tests.Test" { - - function setup() { - params = {controller = "dummy", action = "dummy"}; - _controller = controller("dummy", params); - } - - function test_provides_sets_controller_class_data() { - formats = "json,xml,csv"; - _controller.provides(formats = formats); - assert('_controller.$getControllerClassData().formats.default eq "html,#formats#"'); - } - - function test_onlyProvides_sets_controller_class_data() { - formats = "html"; - _controller.onlyProvides(formats = "html"); - assert('_controller.$getControllerClassData().formats.actions.dummy eq formats'); - } - -} diff --git a/core/src/wheels/tests/controller/provides/requestcontenttype.cfc b/core/src/wheels/tests/controller/provides/requestcontenttype.cfc deleted file mode 100644 index cc84be4c5a..0000000000 --- a/core/src/wheels/tests/controller/provides/requestcontenttype.cfc +++ /dev/null @@ -1,90 +0,0 @@ -component extends="wheels.tests.Test" { - - function setup() { - params = {controller = "dummy", action = "dummy"}; - $$oldCGIScope = request.cgi; - } - - function teardown() { - request.cgi = $$oldCGIScope; - } - - function test_$requestContentType_header_cgi_html() { - _controller = controller("dummy", params); - request.cgi.http_accept = "text/html"; - assert("_controller.$requestContentType() eq 'html'"); - } - - function test_$requestContentType_params_html() { - params.format = "html"; - _controller = controller("dummy", params); - assert("_controller.$requestContentType() eq 'html'"); - } - - function test_$requestContentType_header_cgi_xml() { - _controller = controller("dummy", params); - request.cgi.http_accept = "text/xml"; - assert("_controller.$requestContentType() eq 'xml'"); - } - - function test_$requestContentType_params_xml() { - params.format = "xml"; - _controller = controller("dummy", params); - assert("_controller.$requestContentType() eq 'xml'"); - } - - function test_$requestContentType_header_cgi_json() { - _controller = controller("dummy", params); - request.cgi.http_accept = "application/json"; - assert("_controller.$requestContentType() eq 'json'"); - } - - function test_$requestContentType_header_cgi_json_and_js() { - _controller = controller("dummy", params); - request.cgi.http_accept = "application/json, application/javascript"; - assert("_controller.$requestContentType() eq 'json'"); - } - - function test_$requestContentType_params_json() { - params.format = "json"; - _controller = controller("dummy", params); - assert("_controller.$requestContentType() eq 'json'"); - } - - function test_$requestContentType_header_cgi_csv() { - _controller = controller("dummy", params); - request.cgi.http_accept = "text/csv"; - assert("_controller.$requestContentType() eq 'csv'"); - } - - function test_$requestContentType_params_csv() { - params.format = "csv"; - _controller = controller("dummy", params); - assert("_controller.$requestContentType() eq 'csv'"); - } - - function test_$requestContentType_header_cgi_xls() { - _controller = controller("dummy", params); - request.cgi.http_accept = "application/vnd.ms-excel"; - assert("_controller.$requestContentType() eq 'xls'"); - } - - function test_$requestContentType_params_xls() { - params.format = "xls"; - _controller = controller("dummy", params); - assert("_controller.$requestContentType() eq 'xls'"); - } - - function test_$requestContentType_header_cgi_pdf() { - _controller = controller("dummy", params); - request.cgi.http_accept = "application/pdf"; - assert("_controller.$requestContentType() eq 'pdf'"); - } - - function test_$requestContentType_params_pdf() { - params.format = "pdf"; - _controller = controller("dummy", params); - assert("_controller.$requestContentType() eq 'pdf'"); - } - -} diff --git a/core/src/wheels/tests/controller/redirection/redirectto.cfc b/core/src/wheels/tests/controller/redirection/redirectto.cfc deleted file mode 100644 index 7670d0fda6..0000000000 --- a/core/src/wheels/tests/controller/redirection/redirectto.cfc +++ /dev/null @@ -1,114 +0,0 @@ -component extends="wheels.tests.Test" { - - function setup() { - params = {controller = "test", action = "testRedirect"}; - _controller = controller("test", params); - copies.request.cgi = request.cgi; - copies.application.wheels.viewPath = application.wheels.viewPath; - } - - function teardown() { - request.cgi = copies.request.cgi; - application.wheels.viewPath = copies.application.wheels.viewPath; - } - - function test_throw_error_on_double_redirect() { - _controller.redirectTo(action = "test"); - expected = "Wheels.RedirectToAlreadyCalled"; - actual = raised('_controller.redirectTo(action="test")'); - assert("actual eq expected"); - } - - function test_remaining_action_code_should_run() { - application.wheels.viewPath = "/wheels/tests/_assets/views"; - _controller.$callAction(action = "testRedirect"); - r = _controller.getRedirect(); - assert("IsDefined('r.url') AND IsDefined('request.setInActionAfterRedirect')"); - } - - function test_redirect_to_action() { - _controller.redirectTo(action = "test"); - r = _controller.getRedirect(); - assert("_controller.$performedRedirect() IS true AND IsDefined('r.url')"); - } - - function test_passing_through_to_urlfor() { - args = { - action = "test", - onlyPath = false, - protocol = "https", - params = "test1=1&test2=2" - }; - _controller.redirectTo(argumentCollection = args); - r = _controller.getRedirect(); - assert("r.url Contains args.protocol AND r.url Contains args.params"); - } - - function test_setting_cflocation_attributes() { - _controller.redirectTo(action = "test", addToken = true, statusCode = "301"); - r = _controller.getRedirect(); - assert("r.addToken IS true AND r.statusCode IS 301"); - } - - function test_redirect_to_referrer() { - path = "/test-controller/test-action"; - request.cgi.http_referer = "http://" & request.cgi.server_name & path; - _controller.redirectTo(back = true); - r = _controller.getRedirect(); - assert("r.url Contains path"); - } - - function test_appending_params_to_referrer() { - path = "/test-controller/test-action"; - request.cgi.http_referer = "http://" & request.cgi.server_name & path; - _controller.redirectTo(back = true, params = "x=1&y=2"); - r = _controller.getRedirect(); - assert("r.url Contains path AND r.url Contains '?x=1&y=2'"); - } - - function test_redirect_to_action_on_blank_referrer() { - request.cgi.http_referer = ""; - _controller.redirectTo(back = true, action = "blankRef"); - r = _controller.getRedirect(); - assert("r.url IS '#uRLFor(action = 'blankRef')#'"); - } - - function test_redirect_to_root_on_blank_referrer() { - request.cgi.http_referer = ""; - _controller.redirectTo(back = true); - r = _controller.getRedirect(); - assert("r.url IS application.wheels.webPath"); - } - - function test_redirect_to_root_on_foreign_referrer() { - request.cgi.http_referer = "http://www.google.com"; - _controller.redirectTo(back = true); - r = _controller.getRedirect(); - assert("r.url IS application.wheels.webPath"); - } - - function test_redirect_to_url() { - _controller.redirectTo(url = "http://www.google.com"); - r = _controller.getRedirect(); - assert("_controller.$performedRedirect() IS true AND IsDefined('r.url')"); - } - - function test_redirect_to_url_with_params() { - _controller.redirectTo(url = "http://www.google.com", params = "foo=bar"); - - actual = _controller.getRedirect().url; - expected = "http://www.google.com?foo=bar"; - - assert("actual EQ expected"); - } - - function test_redirect_to_url_with_query_string_and_with_params() { - _controller.redirectTo(url = "http://www.google.com?foo=bar", params = "baz=qux"); - - actual = _controller.getRedirect().url; - expected = "http://www.google.com?foo=bar&baz=qux"; - - assert("actual EQ expected"); - } - -} diff --git a/core/src/wheels/tests/controller/rendering/argumentsForPartial.cfc b/core/src/wheels/tests/controller/rendering/argumentsForPartial.cfc deleted file mode 100644 index 74b7caef8e..0000000000 --- a/core/src/wheels/tests/controller/rendering/argumentsForPartial.cfc +++ /dev/null @@ -1,29 +0,0 @@ -component extends="wheels.tests.Test" { - - function setup() { - include "setup.cfm"; - params = {controller = "dummy", action = "dummy"}; - _controller = controller("dummy", params); - } - - function teardown() { - include "teardown.cfm"; - } - - function test_name_is_not_a_function() { - query = QueryNew("a,b,c,e"); - _controller.$injectIntoVariablesScope = this.$injectIntoVariablesScope; - _controller.$injectIntoVariablesScope(name = "query", data = query); - actual = _controller.$argumentsForPartial($name = "query", $dataFunction = true); - assert('IsStruct(actual) and StructIsEmpty(actual)'); - } - - /** - * HELPERS - */ - - function $injectIntoVariablesScope(required string name, required any data) { - variables[arguments.name] = arguments.data; - } - -} diff --git a/core/src/wheels/tests/controller/rendering/includecontent.cfc b/core/src/wheels/tests/controller/rendering/includecontent.cfc deleted file mode 100644 index 10c611f6d0..0000000000 --- a/core/src/wheels/tests/controller/rendering/includecontent.cfc +++ /dev/null @@ -1,38 +0,0 @@ -component extends="wheels.tests.Test" { - - function setup() { - _params = {controller = "dummy", action = "dummy"}; - _controller = controller("dummy", _params); - } - - function test_contentFor_and_includeContent_assigning_section() { - a = ["head1", "head2", "head3"]; - for (i in a) { - _controller.contentFor(head = i); - }; - expected = ArrayToList(a, Chr(10)); - actual = _controller.includeContent("head"); - assert('actual eq expected'); - } - - function test_contentFor_and_includeContent_default_section() { - a = ["layout1", "layout2", "layout3"]; - for (i in a) { - _controller.contentFor(body = i); - }; - expected = ArrayToList(a, Chr(10)); - actual = _controller.includeContent(); - assert('actual eq expected'); - } - - function test_includeContent_invalid_section_returns_blank() { - actual = _controller.includeContent("somethingstupid"); - assert('actual eq ""'); - } - - function test_includeContent_returns_default() { - actual = _controller.includeContent("somethingstupid", "my default value"); - assert('actual eq "my default value"'); - } - -} diff --git a/core/src/wheels/tests/controller/rendering/layouts.cfc b/core/src/wheels/tests/controller/rendering/layouts.cfc deleted file mode 100644 index 5f241414aa..0000000000 --- a/core/src/wheels/tests/controller/rendering/layouts.cfc +++ /dev/null @@ -1,98 +0,0 @@ -component extends="wheels.tests.Test" { - - function setup() { - include "setup.cfm"; - params = {controller = "test", action = "test"}; - _controller = controller("test", params); - } - - function teardown() { - include "teardown.cfm"; - } - - function test_rendering_without_layout() { - _controller.renderView(layout = false); - assert("_controller.response() IS 'view template content'"); - } - - function test_rendering_with_default_layout_in_controller_folder() { - tempFile = ExpandPath("/wheels/tests/_assets/views/test/layout.cfm"); - FileWrite(tempFile, "start:controllerlayout##includeContent()##end:controllerlayout"); - application.wheels.existingLayoutFiles = "test"; - _controller.renderView(); - r = _controller.response(); - assert( - "r Contains 'view template content' AND r Contains 'start:controllerlayout' AND r Contains 'end:controllerlayout'" - ); - application.wheels.existingLayoutFiles = ""; - FileDelete(tempFile); - } - - function test_rendering_with_default_layout_in_root() { - _controller.renderView(); - r = _controller.response(); - assert( - "r Contains 'view template content' AND r Contains 'start:defaultlayout' AND r Contains 'end:defaultlayout'" - ); - } - - function test_rendering_with_specific_layout() { - _controller.renderView(layout = "specificLayout"); - r = _controller.response(); - assert( - "r Contains 'view template content' AND r Contains 'start:specificlayout' AND r Contains 'end:specificlayout'" - ); - } - - function test_removing_cfm_file_extension_when_supplied() { - _controller.renderView(layout = "specificLayout.cfm"); - r = _controller.response(); - assert( - "r Contains 'view template content' AND r Contains 'start:specificlayout' AND r Contains 'end:specificlayout'" - ); - } - - function test_rendering_with_specific_layout_in_root() { - _controller.renderView(layout = "/rootLayout"); - r = _controller.response(); - assert("r Contains 'view template content' AND r Contains 'start:rootlayout' AND r Contains 'end:rootlayout'"); - } - - function test_rendering_with_specific_layout_in_sub_folder() { - _controller.renderView(layout = "sub/layout"); - r = _controller.response(); - assert("r Contains 'view template content' AND r Contains 'start:sublayout' AND r Contains 'end:sublayout'"); - } - - function test_rendering_with_specific_layout_from_folder_path() { - _controller.renderView(layout = "/shared/layout"); - r = _controller.response(); - assert("r Contains 'view template content' AND r Contains 'start:sharedlayout' AND r Contains 'end:sharedlayout'"); - } - - function test_view_variable_should_be_available_in_layout_file() { - _controller.$callAction(action = "test"); - _controller.renderView(); - r = _controller.response(); - assert( - "r Contains 'view template content' AND r Contains 'variableForLayoutContent' AND r Contains 'start:defaultlayout' AND r Contains 'end:defaultlayout'" - ); - } - - function test_rendering_partial_with_layout() { - _controller.renderPartial(partial = "partialTemplate", layout = "partialLayout"); - r = _controller.response(); - assert( - "r Contains 'partial template content' AND r Contains 'start:partiallayout' AND r Contains 'end:partiallayout'" - ); - } - - function test_rendering_partial_with_specific_layout_in_root() { - _controller.renderPartial(partial = "partialTemplate", layout = "/partialRootLayout"); - r = _controller.response(); - assert( - "r Contains 'partial template content' AND r Contains 'start:partialrootlayout' AND r Contains 'end:partialrootlayout'" - ); - } - -} diff --git a/core/src/wheels/tests/controller/rendering/rendernothing.cfc b/core/src/wheels/tests/controller/rendering/rendernothing.cfc deleted file mode 100644 index 3d52915464..0000000000 --- a/core/src/wheels/tests/controller/rendering/rendernothing.cfc +++ /dev/null @@ -1,25 +0,0 @@ -component extends="wheels.tests.Test" { - - function setup() { - include "setup.cfm"; - params = {controller = "dummy", action = "dummy"}; - _controller = controller("dummy", params); - } - - function teardown() { - include "teardown.cfm"; - } - - function test_render_nothing() { - _controller.renderNothing(); - assert("_controller.response() IS ''"); - } - - function test_render_nothing_with_status() { - _controller.renderNothing(status = 418); - actual = $statusCode(); - expected = 418; - assert("actual eq expected"); - } - -} diff --git a/core/src/wheels/tests/controller/rendering/renderpartial.cfc b/core/src/wheels/tests/controller/rendering/renderpartial.cfc deleted file mode 100644 index e2de4bb817..0000000000 --- a/core/src/wheels/tests/controller/rendering/renderpartial.cfc +++ /dev/null @@ -1,30 +0,0 @@ -component extends="wheels.tests.Test" { - - function setup() { - include "setup.cfm"; - params = {controller = "test", action = "test"}; - _controller = controller("test", params); - } - - function teardown() { - include "teardown.cfm"; - } - - function test_rendering_partial() { - result = _controller.renderPartial(partial = "partialTemplate"); - assert("_controller.response() Contains 'partial template content'"); - } - - function test_rendering_partial_and_returning_as_string() { - result = _controller.renderPartial(partial = "partialTemplate", returnAs = "string"); - assert("NOT StructKeyExists(request.wheels, 'response') AND result Contains 'partial template content'"); - } - - function test_rendering_partial_with_status() { - _controller.renderPartial(partial = "partialTemplate", status = 418); - actual = $statusCode(); - expected = 418; - assert("actual eq expected"); - } - -} diff --git a/core/src/wheels/tests/controller/rendering/rendertext.cfc b/core/src/wheels/tests/controller/rendering/rendertext.cfc deleted file mode 100644 index eef7cf05d0..0000000000 --- a/core/src/wheels/tests/controller/rendering/rendertext.cfc +++ /dev/null @@ -1,60 +0,0 @@ -component extends="wheels.tests.Test" { - - function setup() { - include "setup.cfm"; - params = {controller = "dummy", action = "dummy"}; - _controller = controller("dummy", params); - } - - function teardown() { - include "teardown.cfm"; - } - - function test_render_text() { - _controller.renderText("OMG, look what I rendered!"); - assert("_controller.response() IS 'OMG, look what I rendered!'"); - } - - function test_render_text_with_status() { - _controller.renderText(text = "OMG!", status = 418); - actual = $statusCode(); - expected = 418; - assert("actual eq expected"); - } - - function test_render_text_with_doesnt_hijack_status() { - cfheader(statuscode=403); - _controller.renderText(text = "OMG!"); - actual = $statusCode(); - expected = 403; - assert("actual eq expected"); - } - - // Test renderText with JSON format - function test_render_text_json_format() { - params.format = "json"; - _controller = controller("dummy", params); - _controller.provides("json"); - _controller.renderText('{"message":"JSON response"}'); - assert('_controller.response() IS ''{"message":"JSON response"}'''); - } - - // Test renderText with XML format - function test_render_text_xml_format() { - params.format = "xml"; - _controller = controller("dummy", params); - _controller.provides("xml"); - _controller.renderText('XML response'); - assert("_controller.response() Contains 'XML response'"); - } - - // Test renderText doesn't require view for non-HTML formats - function test_render_text_no_view_required() { - params = {controller = "ApiTest", action = "renderTextJson", format = "json"}; - _controller = controller("ApiTest", params); - // This should work without throwing ViewNotFound - _controller.renderText('{"test":true}'); - assert('_controller.response() eq ''{"test":true}'''); - } - -} diff --git a/core/src/wheels/tests/controller/rendering/renderview.cfc b/core/src/wheels/tests/controller/rendering/renderview.cfc deleted file mode 100644 index d57117fb2b..0000000000 --- a/core/src/wheels/tests/controller/rendering/renderview.cfc +++ /dev/null @@ -1,45 +0,0 @@ -component extends="wheels.tests.Test" { - - function setup() { - include "setup.cfm"; - params = {controller = "test", action = "test"}; - _controller = controller("test", params); - } - - function teardown() { - include "teardown.cfm"; - } - - function test_rendering_current_action() { - result = _controller.renderView(); - assert("_controller.response() Contains 'view template content'"); - } - - function test_rendering_view_for_another_controller_and_action() { - result = _controller.renderView(controller = "main", action = "template"); - assert("_controller.response() Contains 'main controller template content'"); - } - - function test_rendering_view_for_another_action() { - result = _controller.renderView(action = "template"); - assert("_controller.response() Contains 'specific template content'"); - } - - function test_rendering_specific_template() { - result = _controller.renderView(template = "template"); - assert("_controller.response() Contains 'specific template content'"); - } - - function test_rendering_and_returning_as_string() { - result = _controller.renderView(returnAs = "string"); - assert("NOT StructKeyExists(request.wheels, 'response') AND result Contains 'view template content'"); - } - - function test_render_view_with_status() { - _controller.renderView(status = 418); - actual = $statusCode(); - expected = 418; - assert("actual eq expected"); - } - -} diff --git a/core/src/wheels/tests/controller/rendering/renderwith.cfc b/core/src/wheels/tests/controller/rendering/renderwith.cfc deleted file mode 100644 index 0cbd3cca4d..0000000000 --- a/core/src/wheels/tests/controller/rendering/renderwith.cfc +++ /dev/null @@ -1,306 +0,0 @@ -component extends="wheels.tests.Test" { - - function setup() { - include "setup.cfm"; - params = {controller = "test", action = "test"}; - cfheader(statuscode=200); // start with a fresh status code - } - - function teardown() { - include "teardown.cfm"; - $header(name = "content-type", value = "text/html", charset = "utf-8"); - } - - /* function test_json_integer() { - params = {controller="dummy", action="dummy", format = "json"}; - _controller = controller("dummy", params); - _controller.provides("json"); - user = model("user").findAll(where="username = 'tonyp'", returnAs="structs"); - result = _controller.renderWith(data=user, zipCode="integer", returnAs="string"); - assert("result Contains ':11111,'"); - } */ - - /* function test_json_string() { - params = {controller="dummy", action="dummy", format = "json"}; - _controller = controller("dummy", params); - _controller.provides("json"); - user = model("user").findAll(where="username = 'tonyp'", returnAs="structs"); - result = _controller.renderWith(data=user, phone="string", returnAs="string"); - assert("result Contains '1235551212'"); - } */ - - function test_throws_error_without_data_argument() { - _controller = controller("test", params); - try { - result = _controller.renderWith(); - } catch (any e) { - assert('true eq true'); - } - } - - function test_current_action_as_xml_with_template_returning_string_to_controller() { - params.format = "xml"; - _controller = controller("test", params); - _controller.provides("xml"); - user = model("user").findOne(where = "username = 'tonyp'"); - data = _controller.renderWith(data = user, layout = false, returnAs = "string"); - assert("data Contains 'xml template content'"); - } - - function test_current_action_as_xml_with_template() { - params.format = "xml"; - _controller = controller("test", params); - _controller.provides("xml"); - user = model("user").findOne(where = "username = 'tonyp'"); - _controller.renderWith(data = user, layout = false); - assert("_controller.response() Contains 'xml template content'"); - } - - function test_current_action_as_xml_without_template() { - params.action = "test2"; - params.format = "xml"; - _controller = controller("test", params); - _controller.provides("xml"); - user = model("user").findOne(where = "username = 'tonyp'"); - _controller.renderWith(data = user); - assert("IsXml(_controller.response()) eq true"); - } - - function test_current_action_as_xml_without_template_returning_string_to_controller() { - params.action = "test2"; - params.format = "xml"; - _controller = controller("test", params); - _controller.provides("xml"); - user = model("user").findOne(where = "username = 'tonyp'"); - data = _controller.renderWith(data = user, returnAs = "string"); - assert("IsXml(data) eq true"); - } - - function test_current_action_as_json_with_template() { - params.format = "json"; - _controller = controller("test", params); - _controller.provides("json"); - user = model("user").findOne(where = "username = 'tonyp'"); - _controller.renderWith(data = user, layout = false); - assert("_controller.response() Contains 'json template content'"); - } - - function test_current_action_as_json_without_template() { - params.action = "test2"; - params.format = "json"; - _controller = controller("test", params); - _controller.provides("json"); - user = model("user").findOne(where = "username = 'tonyp'"); - _controller.renderWith(data = user); - assert("IsJSON(_controller.response()) eq true"); - } - - function test_current_action_as_json_without_template_returning_string_to_controller() { - params.action = "test2"; - params.format = "json"; - _controller = controller("test", params); - _controller.provides("json"); - user = model("user").findOne(where = "username = 'tonyp'"); - data = _controller.renderWith(data = user, returnAs = "string"); - assert("IsJSON(data) eq true"); - } - - function test_current_action_as_pdf_with_template_throws_error() { - params.format = "pdf"; - _controller = controller("test", params); - _controller.provides("pdf"); - user = model("user").findOne(where = "username = 'tonyp'"); - try { - _controller.renderWith(data = user, layout = false); - fail(message = "Error did not occur."); - } catch (any e) { - assert("true eq true"); - } - } - - function test_renderingError_raised_when_template_is_not_found_for_format() { - params.format = "xls"; - params.action = "notfound"; - _controller = controller("test", params); - _controller.provides("xml"); - user = model("user").findOne(where = "username = 'tonyp'"); - actual = raised('_controller.renderWith(data=user, layout=false, returnAs="string")'); - expected = "Wheels.renderingError"; - assert("actual eq expected"); - } - - /* Custom Status Codes; probably no need to test all 75 odd */ - function test_custom_status_codes_no_argument_passed() { - params.format = "json"; - params.action = "test2"; - _controller = controller("test", params); - _controller.provides("json"); - user = model("user").findOne(where = "username = 'tonyp'"); - _controller.renderWith(data = user, layout = false, returnAs = "string"); - assert("$statusCode() EQ 200"); - } - - function test_custom_status_codes_403() { - params.format = "json"; - params.action = "test2"; - _controller = controller("test", params); - _controller.provides("json"); - user = model("user").findOne(where = "username = 'tonyp'"); - _controller.renderWith( - data = user, - layout = false, - returnAs = "string", - status = 403 - ); - assert("$statusCode() EQ 403"); - } - - function test_custom_status_codes_404() { - params.format = "json"; - params.action = "test2"; - _controller = controller("test", params); - _controller.provides("json"); - user = model("user").findOne(where = "username = 'tonyp'"); - _controller.renderWith( - data = user, - layout = false, - returnAs = "string", - status = 404 - ); - assert("$statusCode() EQ 404"); - } - - function test_custom_status_codes_with_html() { - params.action = "test2"; - _controller = controller("test", params); - _controller.renderWith(data = "the rain in spain", layout = false, status = 403); - assert("$statusCode() EQ 403"); - } - - function test_custom_status_codes_OK() { - params.format = "json"; - params.action = "test2"; - _controller = controller("test", params); - _controller.provides("json"); - user = model("user").findOne(where = "username = 'tonyp'"); - _controller.renderWith( - data = user, - layout = false, - returnAs = "string", - status = "OK" - ); - assert("$statusCode() EQ 200"); - } - function test_custom_status_codes_Not_Found() { - GetPageContext().getResponse().setStatus("100"); - params.format = "json"; - params.action = "test2"; - _controller = controller("test", params); - _controller.provides("json"); - user = model("user").findOne(where = "username = 'tonyp'"); - _controller.renderWith( - data = user, - layout = false, - returnAs = "string", - status = "Not Found" - ); - assert("$statusCode() EQ 404"); - } - function test_custom_status_codes_Method_Not_Allowed() { - GetPageContext().getResponse().setStatus("100"); - params.format = "json"; - params.action = "test2"; - _controller = controller("test", params); - _controller.provides("json"); - user = model("user").findOne(where = "username = 'tonyp'"); - _controller.renderWith( - data = user, - layout = false, - returnAs = "string", - status = "Method Not Allowed" - ); - assert("$statusCode() EQ 405"); - } - - function test_custom_status_codes_Method_Not_Allowed_case() { - GetPageContext().getResponse().setStatus("100"); - params.format = "json"; - params.action = "test2"; - _controller = controller("test", params); - _controller.provides("json"); - user = model("user").findOne(where = "username = 'tonyp'"); - _controller.renderWith( - data = user, - layout = false, - returnAs = "string", - status = "method not allowed" - ); - assert("$statusCode() EQ 405"); - } - - function test_custom_status_codes_bad_numeric() { - params.format = "json"; - params.action = "test2"; - _controller = controller("test", params); - _controller.provides("json"); - user = model("user").findOne(where = "username = 'tonyp'"); - actual = raised('_controller.renderWith(data=user, layout=false, returnAs="string", status=987654321)'); - expected = "Wheels.renderingError"; - assert("actual EQ expected"); - } - - function test_custom_status_codes_bad_text() { - params.format = "json"; - params.action = "test2"; - _controller = controller("test", params); - _controller.provides("json"); - user = model("user").findOne(where = "username = 'tonyp'"); - actual = raised('_controller.renderWith(data=user, layout=false, returnAs="string", status="THECAKEISALIE")'); - expected = "Wheels.renderingError"; - assert("actual EQ expected"); - } - - // Test renderWith doesn't require view for JSON when auto-generating - function test_render_with_json_no_view_required() { - params = {controller = "ApiTest", action = "renderWithJson", format = "json"}; - _controller = controller("ApiTest", params); - local.data = {test = true, message = "No view needed"}; - // This should work without throwing ViewNotFound - _controller.renderWith(data = local.data); - assert("IsJSON(_controller.response()) eq true"); - assert("_controller.response() Contains 'No view needed'"); - } - - // Test renderWith doesn't require view for XML when auto-generating - function test_render_with_xml_no_view_required() { - params = {controller = "ApiTest", action = "renderWithXml", format = "xml"}; - _controller = controller("ApiTest", params); - local.data = {test = true, message = "XML without view"}; - // This should work without throwing ViewNotFound - _controller.renderWith(data = local.data); - assert("IsXML(_controller.response()) eq true"); - } - - // Test renderWith respects onlyProvides - function test_render_with_only_provides() { - params = {controller = "ApiTest", action = "renderWithJson", format = "json"}; - _controller = controller("ApiTest", params); - // ApiTest controller uses onlyProvides("json,xml") - local.data = {restricted = true}; - _controller.renderWith(data = local.data); - assert("IsJSON(_controller.response()) eq true"); - } - - // Test renderWith sets response properly (addressing bug fix) - function test_render_with_sets_response() { - params = {controller = "test", action = "test2", format = "json"}; - _controller = controller("test", params); - _controller.provides("json"); - local.data = {verified = true, timestamp = Now()}; - _controller.renderWith(data = local.data); - // Verify response is set (not just returned) - assert("Len(_controller.response()) gt 0"); - assert("IsJSON(_controller.response()) eq true"); - } - -} diff --git a/core/src/wheels/tests/controller/rendering/renderwithoutview.cfc b/core/src/wheels/tests/controller/rendering/renderwithoutview.cfc deleted file mode 100644 index 67a063bc2b..0000000000 --- a/core/src/wheels/tests/controller/rendering/renderwithoutview.cfc +++ /dev/null @@ -1,196 +0,0 @@ -component extends="wheels.tests.Test" { - - function setup() { - include "setup.cfm"; - // Start with fresh status code - cfheader(statuscode = 200); - } - - function teardown() { - include "teardown.cfm"; - // Reset content type to HTML - $header(name = "content-type", value = "text/html", charset = "utf-8"); - } - - // Test renderText with JSON format - no view file should be required - function test_renderText_json_without_view() { - params = {controller = "ApiTest", action = "renderTextJson", format = "json"}; - _controller = controller("ApiTest", params); - - // This should NOT throw ViewNotFound error - try { - $callAction(action = "renderTextJson"); - actual = _controller.response(); - expected = '{"success":true,"message":"renderText JSON works!"}'; - assert("actual eq expected"); - } catch (any e) { - fail("renderText with JSON format should not throw error when no view exists. Error: #e.message#"); - } - } - - // Test renderText with XML format - no view file should be required - function test_renderText_xml_without_view() { - params = {controller = "ApiTest", action = "renderTextXml", format = "xml"}; - _controller = controller("ApiTest", params); - - try { - $callAction(action = "renderTextXml"); - actual = _controller.response(); - assert("actual Contains 'renderText XML works!'"); - } catch (any e) { - fail("renderText with XML format should not throw error when no view exists. Error: #e.message#"); - } - } - - // Test renderWith with JSON format - should auto-generate JSON - function test_renderWith_json_autogenerate() { - params = {controller = "ApiTest", action = "renderWithJson", format = "json"}; - _controller = controller("ApiTest", params); - - try { - $callAction(action = "renderWithJson"); - actual = _controller.response(); - assert("IsJSON(actual) eq true"); - assert("actual Contains 'renderWith JSON works!'"); - } catch (any e) { - fail("renderWith with JSON format should auto-generate content. Error: #e.message#"); - } - } - - // Test renderWith with XML format - should auto-generate XML - function test_renderWith_xml_autogenerate() { - params = {controller = "ApiTest", action = "renderWithXml", format = "xml"}; - _controller = controller("ApiTest", params); - - try { - $callAction(action = "renderWithXml"); - actual = _controller.response(); - assert("IsXML(actual) eq true"); - assert("actual Contains 'renderWith XML works!'"); - } catch (any e) { - fail("renderWith with XML format should auto-generate content. Error: #e.message#"); - } - } - - // Test action with no render - should not throw error for JSON format - function test_no_render_json_format() { - params = {controller = "ApiTest", action = "noRender", format = "json"}; - _controller = controller("ApiTest", params); - - try { - $callAction(action = "noRender"); - // Should complete without error, even though nothing was rendered - assert("true eq true"); - } catch (any e) { - // Check if it's the expected ViewNotFound error - if (e.type == "Wheels.ViewNotFound") { - fail("Action with JSON format should not throw ViewNotFound when using onlyProvides. Error: #e.message#"); - } else { - // Re-throw unexpected errors - throw(object = e); - } - } - } - - // Test renderText with custom status code - function test_renderText_with_status_json() { - params = {controller = "ApiTest", action = "renderWithStatus", format = "json"}; - _controller = controller("ApiTest", params); - - try { - $callAction(action = "renderWithStatus"); - actual = _controller.response(); - assert('actual eq ''{"error":"Not Found"}'''); - assert("$statusCode() eq 404"); - } catch (any e) { - fail("renderText with status should work without view. Error: #e.message#"); - } - } - - // Test mixed format controller - HTML should still require view - function test_mixed_format_html_requires_view() { - params = {controller = "MixedFormatTest", action = "htmlAction", format = "html"}; - _controller = controller("MixedFormatTest", params); - - try { - $callAction(action = "htmlAction"); - fail("HTML action without view should throw ViewNotFound error"); - } catch (any e) { - assert("e.type eq 'Wheels.ViewNotFound'"); - } - } - - // Test mixed format controller - JSON with renderText - function test_mixed_format_json_renderText() { - params = {controller = "MixedFormatTest", action = "jsonWithRenderText", format = "json"}; - _controller = controller("MixedFormatTest", params); - - try { - $callAction(action = "jsonWithRenderText"); - actual = _controller.response(); - assert('actual eq ''{"source":"renderText"}'''); - } catch (any e) { - fail("Mixed format controller should handle JSON renderText. Error: #e.message#"); - } - } - - // Test mixed format controller - JSON with renderWith - function test_mixed_format_json_renderWith() { - params = {controller = "MixedFormatTest", action = "jsonWithRenderWith", format = "json"}; - _controller = controller("MixedFormatTest", params); - - try { - $callAction(action = "jsonWithRenderWith"); - actual = _controller.response(); - assert("IsJSON(actual) eq true"); - assert("actual Contains 'renderWith'"); - } catch (any e) { - fail("Mixed format controller should handle JSON renderWith. Error: #e.message#"); - } - } - - // Test format-aware action - function test_format_aware_json() { - params = {controller = "MixedFormatTest", action = "formatAware", format = "json"}; - _controller = controller("MixedFormatTest", params); - - try { - $callAction(action = "formatAware"); - actual = _controller.response(); - assert('actual eq ''{"format":"json"}'''); - } catch (any e) { - fail("Format-aware action should handle JSON. Error: #e.message#"); - } - } - - // Test restricted formats with onlyProvides - function test_restricted_formats_json() { - params = {controller = "MixedFormatTest", action = "restrictedFormats", format = "json"}; - _controller = controller("MixedFormatTest", params); - - try { - $callAction(action = "restrictedFormats"); - actual = _controller.response(); - assert("IsJSON(actual) eq true"); - } catch (any e) { - fail("Restricted formats should work with JSON. Error: #e.message#"); - } - } - - // Test that HTML format is rejected when using onlyProvides - function test_restricted_formats_html_rejected() { - params = {controller = "MixedFormatTest", action = "restrictedFormats", format = "html"}; - _controller = controller("MixedFormatTest", params); - - try { - $callAction(action = "restrictedFormats"); - // HTML should be rejected and default to first available format - actual = _controller.response(); - // Should have rendered as JSON (first in the onlyProvides list) - assert("IsJSON(actual) eq true"); - } catch (any e) { - fail("Restricted formats should handle HTML rejection gracefully. Error: #e.message#"); - } - } - -} \ No newline at end of file diff --git a/core/src/wheels/tests/controller/rendering/setup.cfm b/core/src/wheels/tests/controller/rendering/setup.cfm deleted file mode 100644 index 38ea456645..0000000000 --- a/core/src/wheels/tests/controller/rendering/setup.cfm +++ /dev/null @@ -1,4 +0,0 @@ - -$$oldViewPath = application.wheels.viewPath; -application.wheels.viewPath = "/wheels/tests/_assets/views"; - diff --git a/core/src/wheels/tests/controller/rendering/specified_layouts.cfc b/core/src/wheels/tests/controller/rendering/specified_layouts.cfc deleted file mode 100644 index 6c8cfc45fa..0000000000 --- a/core/src/wheels/tests/controller/rendering/specified_layouts.cfc +++ /dev/null @@ -1,255 +0,0 @@ -component extends="wheels.tests.Test" { - - function setup() { - request.cgi.http_x_requested_with = ""; - params = {controller = "dummy", action = "index"}; - _controller = controller("dummy", params); - } - - - function test_using_method_match() { - args = {template = "controller_layout_test"}; - _controller.controller_layout_test = controller_layout_test; - _controller.usesLayout(argumentCollection = args); - - e = "index_layout"; - r = _controller.$useLayout("index"); - assert('e eq r'); - } - - function test_using_method_match2() { - args = {template = "controller_layout_test"}; - _controller.controller_layout_test = controller_layout_test; - _controller.usesLayout(argumentCollection = args); - - e = "show_layout"; - r = _controller.$useLayout("show"); - assert('e eq r'); - } - - function test_using_method_no_match() { - args = {template = "controller_layout_test"}; - _controller.controller_layout_test = controller_layout_test; - _controller.usesLayout(argumentCollection = args); - - e = "true"; - r = _controller.$useLayout("list"); - assert('e eq r'); - } - - function test_using_method_no_match_no_default() { - args = {template = "controller_layout_test", usedefault = false}; - _controller.controller_layout_test = controller_layout_test; - _controller.usesLayout(argumentCollection = args); - - e = "false"; - r = _controller.$useLayout("list"); - assert('e eq r'); - } - - function test_ajax_request_with_no_layout_specified_should_fallback_to_template() { - request.cgi.http_x_requested_with = "XMLHTTPRequest"; - args = {template = "controller_layout_test"}; - _controller.controller_layout_test = controller_layout_test; - _controller.usesLayout(argumentCollection = args); - - e = "index_layout"; - r = _controller.$useLayout("index"); - assert('e eq r'); - } - - function test_using_method_ajax_match() { - request.cgi.http_x_requested_with = "XMLHTTPRequest"; - args = {template = "controller_layout_test", ajax = "controller_layout_test_ajax"}; - _controller.controller_layout_test = controller_layout_test; - _controller.controller_layout_test_ajax = controller_layout_test_ajax; - _controller.usesLayout(argumentCollection = args); - - e = "index_layout_ajax"; - r = _controller.$useLayout("index"); - assert('e eq r'); - } - - function test_using_method_ajax_match2() { - request.cgi.http_x_requested_with = "XMLHTTPRequest"; - args = {template = "controller_layout_test", ajax = "controller_layout_test_ajax"}; - _controller.controller_layout_test = controller_layout_test; - _controller.controller_layout_test_ajax = controller_layout_test_ajax; - _controller.usesLayout(argumentCollection = args); - - e = "show_layout_ajax"; - r = _controller.$useLayout("show"); - assert('e eq r'); - } - - function test_using_method_ajax_no_match() { - request.cgi.http_x_requested_with = "XMLHTTPRequest"; - args = {template = "controller_layout_test", ajax = "controller_layout_test_ajax"}; - _controller.controller_layout_test = controller_layout_test; - _controller.controller_layout_test_ajax = controller_layout_test_ajax; - _controller.usesLayout(argumentCollection = args); - - e = "true"; - r = _controller.$useLayout("list"); - assert('e eq r'); - } - - function test_using_method_ajax_no_match_no_default() { - request.cgi.http_x_requested_with = "XMLHTTPRequest"; - args = {template = "controller_layout_test", ajax = "controller_layout_test_ajax", usedefault = false}; - _controller.controller_layout_test = controller_layout_test; - _controller.controller_layout_test_ajax = controller_layout_test_ajax; - _controller.usesLayout(argumentCollection = args); - - e = "false"; - r = _controller.$useLayout("list"); - assert('e eq r'); - } - - function test_should_respect_exceptions_no_match() { - args = {template = "mylayout", except = "index"}; - _controller.usesLayout(argumentCollection = args); - - e = "mylayout"; - r = _controller.$useLayout("show"); - assert('e eq r'); - } - - function test_should_respect_exceptions_match() { - args = {template = "mylayout", except = "index"}; - _controller.usesLayout(argumentCollection = args); - - e = "true"; - r = _controller.$useLayout("index"); - assert('e eq r'); - } - - function test_should_respect_exceptions_match_no_default() { - args = {template = "mylayout", except = "index", usedefault = false}; - _controller.usesLayout(argumentCollection = args); - - e = "false"; - r = _controller.$useLayout("index"); - assert('e eq r'); - } - - function test_should_respect_exceptions_ajax_no_match() { - request.cgi.http_x_requested_with = "XMLHTTPRequest"; - args = {template = "mylayout", ajax = "mylayout_ajax", except = "index"}; - _controller.usesLayout(argumentCollection = args); - - e = "mylayout_ajax"; - r = _controller.$useLayout("show"); - assert('e eq r'); - } - - function test_should_respect_exceptions_ajax_match() { - request.cgi.http_x_requested_with = "XMLHTTPRequest"; - args = {template = "mylayout", ajax = "mylayout_ajax", except = "index"}; - _controller.usesLayout(argumentCollection = args); - - e = "true"; - r = _controller.$useLayout("index"); - assert('e eq r'); - } - - function test_should_respect_exceptions_ajax_match_no_default() { - request.cgi.http_x_requested_with = "XMLHTTPRequest"; - args = { - template = "mylayout", - ajax = "mylayout_ajax", - except = "index", - usedefault = false - }; - _controller.usesLayout(argumentCollection = args); - - e = "false"; - r = _controller.$useLayout("index"); - assert('e eq r'); - } - - function test_should_respect_only_no_match() { - args = {template = "mylayout", only = "index"}; - _controller.usesLayout(argumentCollection = args); - - e = "true"; - r = _controller.$useLayout("show"); - assert('e eq r'); - } - - function test_should_respect_only_match() { - args = {template = "mylayout", only = "index"}; - _controller.usesLayout(argumentCollection = args); - - e = "mylayout"; - r = _controller.$useLayout("index"); - assert('e eq r'); - } - - function test_should_respect_only_no_match_no_default() { - args = {template = "mylayout", only = "index", usedefault = false}; - _controller.usesLayout(argumentCollection = args); - - e = "false"; - r = _controller.$useLayout("show"); - assert('e eq r'); - } - - function test_should_respect_only_ajax_no_match() { - request.cgi.http_x_requested_with = "XMLHTTPRequest"; - args = {template = "mylayout", ajax = "mylayout_ajax", only = "index"}; - _controller.usesLayout(argumentCollection = args); - - e = "true"; - r = _controller.$useLayout("show"); - assert('e eq r'); - } - - function test_should_respect_only_ajax_match() { - request.cgi.http_x_requested_with = "XMLHTTPRequest"; - args = {template = "mylayout", ajax = "mylayout_ajax", only = "index"}; - _controller.usesLayout(argumentCollection = args); - - e = "mylayout_ajax"; - r = _controller.$useLayout("index"); - assert('e eq r'); - } - - function test_should_respect_only_ajax_no_match_no_default() { - request.cgi.http_x_requested_with = "XMLHTTPRequest"; - args = { - template = "mylayout", - ajax = "mylayout_ajax", - only = "index", - usedefault = false - }; - _controller.usesLayout(argumentCollection = args); - - e = "false"; - r = _controller.$useLayout("show"); - assert('e eq r'); - } - - /** - * HELPERS - */ - - function controller_layout_test() { - if (arguments.action eq "index") { - return "index_layout"; - } - if (arguments.action eq "show") { - return "show_layout"; - } - } - - function controller_layout_test_ajax() { - if (arguments.action eq "index") { - return "index_layout_ajax"; - } - if (arguments.action eq "show") { - return "show_layout_ajax"; - } - } - -} diff --git a/core/src/wheels/tests/controller/rendering/teardown.cfm b/core/src/wheels/tests/controller/rendering/teardown.cfm deleted file mode 100644 index 60fa8630b0..0000000000 --- a/core/src/wheels/tests/controller/rendering/teardown.cfm +++ /dev/null @@ -1,3 +0,0 @@ - -application.wheels.viewPath = $$oldViewPath; - diff --git a/core/src/wheels/tests/controller/request/isajax.cfc b/core/src/wheels/tests/controller/request/isajax.cfc deleted file mode 100644 index 48e8344328..0000000000 --- a/core/src/wheels/tests/controller/request/isajax.cfc +++ /dev/null @@ -1,21 +0,0 @@ -component extends="wheels.tests.Test" { - - function setup() { - include "setup.cfm"; - } - - function teardown() { - include "teardown.cfm"; - } - - function test_isAjax_valid() { - request.cgi.http_x_requested_with = "XMLHTTPRequest"; - assert("_controller.isAjax() eq true"); - } - - function test_isAjax_invalid() { - request.cgi.http_x_requested_with = ""; - assert("_controller.isAjax() eq false"); - } - -} diff --git a/core/src/wheels/tests/controller/request/isdelete.cfc b/core/src/wheels/tests/controller/request/isdelete.cfc deleted file mode 100644 index 3ab8d87f19..0000000000 --- a/core/src/wheels/tests/controller/request/isdelete.cfc +++ /dev/null @@ -1,21 +0,0 @@ -component extends="wheels.tests.Test" { - - function setup() { - include "setup.cfm"; - } - - function teardown() { - include "teardown.cfm"; - } - - function test_isDelete_with_head_request() { - request.cgi.request_method = "delete"; - assert("_controller.isDelete()"); - } - - function test_isDelete_with_get_request() { - request.cgi.request_method = "get"; - assert("not _controller.isDelete()"); - } - -} diff --git a/core/src/wheels/tests/controller/request/isget.cfc b/core/src/wheels/tests/controller/request/isget.cfc deleted file mode 100644 index 759a809794..0000000000 --- a/core/src/wheels/tests/controller/request/isget.cfc +++ /dev/null @@ -1,21 +0,0 @@ -component extends="wheels.tests.Test" { - - function setup() { - include "setup.cfm"; - } - - function teardown() { - include "teardown.cfm"; - } - - function test_isGet_valid() { - request.cgi.request_method = "get"; - assert("_controller.isGet() eq true"); - } - - function test_isGet_invalid() { - request.cgi.request_method = ""; - assert("_controller.isGet() eq false"); - } - -} diff --git a/core/src/wheels/tests/controller/request/ishead.cfc b/core/src/wheels/tests/controller/request/ishead.cfc deleted file mode 100644 index d614428d15..0000000000 --- a/core/src/wheels/tests/controller/request/ishead.cfc +++ /dev/null @@ -1,21 +0,0 @@ -component extends="wheels.tests.Test" { - - function setup() { - include "setup.cfm"; - } - - function teardown() { - include "teardown.cfm"; - } - - function test_isHead_with_head_request() { - request.cgi.request_method = "head"; - assert("_controller.isHead()"); - } - - function test_isHead_with_get_request() { - request.cgi.request_method = "get"; - assert("not _controller.isHead()"); - } - -} diff --git a/core/src/wheels/tests/controller/request/isoptions.cfc b/core/src/wheels/tests/controller/request/isoptions.cfc deleted file mode 100644 index 188b9d6607..0000000000 --- a/core/src/wheels/tests/controller/request/isoptions.cfc +++ /dev/null @@ -1,21 +0,0 @@ -component extends="wheels.tests.Test" { - - function setup() { - include "setup.cfm"; - } - - function teardown() { - include "teardown.cfm"; - } - - function test_isOptions_with_options_request() { - request.cgi.request_method = "options"; - assert("_controller.isOptions()"); - } - - function test_isOptions_with_get_request() { - request.cgi.request_method = "get"; - assert("not _controller.isOptions()"); - } - -} diff --git a/core/src/wheels/tests/controller/request/ispatch.cfc b/core/src/wheels/tests/controller/request/ispatch.cfc deleted file mode 100644 index 761e238db6..0000000000 --- a/core/src/wheels/tests/controller/request/ispatch.cfc +++ /dev/null @@ -1,21 +0,0 @@ -component extends="wheels.tests.Test" { - - function setup() { - include "setup.cfm"; - } - - function teardown() { - include "teardown.cfm"; - } - - function test_isPatch_with_head_request() { - request.cgi.request_method = "patch"; - assert("_controller.isPatch()"); - } - - function test_isPatch_with_get_request() { - request.cgi.request_method = "get"; - assert("not _controller.isPatch()"); - } - -} diff --git a/core/src/wheels/tests/controller/request/ispost.cfc b/core/src/wheels/tests/controller/request/ispost.cfc deleted file mode 100644 index 679c1a8703..0000000000 --- a/core/src/wheels/tests/controller/request/ispost.cfc +++ /dev/null @@ -1,21 +0,0 @@ -component extends="wheels.tests.Test" { - - function setup() { - include "setup.cfm"; - } - - function teardown() { - include "teardown.cfm"; - } - - function test_isPost_valid() { - request.cgi.request_method = "post"; - assert("_controller.isPost() eq true"); - } - - function test_isPost_invalid() { - request.cgi.request_method = ""; - assert("_controller.isPost() eq false"); - } - -} diff --git a/core/src/wheels/tests/controller/request/isput.cfc b/core/src/wheels/tests/controller/request/isput.cfc deleted file mode 100644 index f168440b00..0000000000 --- a/core/src/wheels/tests/controller/request/isput.cfc +++ /dev/null @@ -1,21 +0,0 @@ -component extends="wheels.tests.Test" { - - function setup() { - include "setup.cfm"; - } - - function teardown() { - include "teardown.cfm"; - } - - function test_isPut_with_head_request() { - request.cgi.request_method = "put"; - assert("_controller.isPut()"); - } - - function test_isPut_with_get_request() { - request.cgi.request_method = "get"; - assert("not _controller.isPut()"); - } - -} diff --git a/core/src/wheels/tests/controller/request/issecure.cfc b/core/src/wheels/tests/controller/request/issecure.cfc deleted file mode 100644 index 4c5d0924f6..0000000000 --- a/core/src/wheels/tests/controller/request/issecure.cfc +++ /dev/null @@ -1,23 +0,0 @@ -component extends="wheels.tests.Test" { - - function setup() { - include "setup.cfm"; - } - - function teardown() { - include "teardown.cfm"; - } - - function test_isSecure_valid() { - request.cgi.server_port_secure = "yes"; - assert("_controller.isSecure() eq true"); - } - - function test_isSecure_invalid() { - request.cgi.server_port_secure = ""; - assert("_controller.isSecure() eq false"); - request.cgi.server_port_secure = "no"; - assert("_controller.isSecure() eq false"); - } - -} diff --git a/core/src/wheels/tests/controller/request/pagination.cfc b/core/src/wheels/tests/controller/request/pagination.cfc deleted file mode 100644 index 7e77852cb0..0000000000 --- a/core/src/wheels/tests/controller/request/pagination.cfc +++ /dev/null @@ -1,26 +0,0 @@ -component extends="wheels.tests.Test" { - - function setup() { - request.wheels["myhandle"] = {test = "true"}; - params = {controller = "dummy", action = "dummy"}; - _controller = controller("dummy", params); - } - - function teardown() { - StructDelete(request.wheels, "myhandle", false); - } - - function test_pagination_handle_exists() { - actual = _controller.pagination('myhandle'); - assert('isstruct(actual)'); - assert('structkeyexists(actual, "test")'); - assert('actual.test eq true'); - } - - function test_pagination_handle_does_not_exists() { - expected = "Wheels.QueryHandleNotFound"; - actual = raised('_controller.pagination("someotherhandle")'); - assert('expected eq actual'); - } - -} diff --git a/core/src/wheels/tests/controller/request/setup.cfm b/core/src/wheels/tests/controller/request/setup.cfm deleted file mode 100644 index 3908918f9c..0000000000 --- a/core/src/wheels/tests/controller/request/setup.cfm +++ /dev/null @@ -1,5 +0,0 @@ - -$$oldCGIScope = request.cgi; -params = {controller = "dummy", action = "dummy"}; -_controller = controller("dummy", params); - diff --git a/core/src/wheels/tests/controller/request/teardown.cfm b/core/src/wheels/tests/controller/request/teardown.cfm deleted file mode 100644 index 9330409867..0000000000 --- a/core/src/wheels/tests/controller/request/teardown.cfm +++ /dev/null @@ -1,3 +0,0 @@ - -request.cgi = $$oldCGIScope; - diff --git a/core/src/wheels/tests/controller/verifies/verifies.cfc b/core/src/wheels/tests/controller/verifies/verifies.cfc deleted file mode 100644 index 643feec650..0000000000 --- a/core/src/wheels/tests/controller/verifies/verifies.cfc +++ /dev/null @@ -1,105 +0,0 @@ -component extends="wheels.tests.Test" { - - function setup() { - $savedenv = Duplicate(request.cgi); - } - - function teardown() { - request.cgi = $savedenv; - } - - function test_valid() { - request.cgi.request_method = "get"; - params = {controller = "verifies", action = "actionGet"}; - _controller = controller("verifies", params); - _controller.processAction("actionGet", params); - assert('_controller.response() eq "actionGet"'); - } - - function test_invalid_aborted() { - request.cgi.request_method = "post"; - params = {controller = "verifies", action = "actionGet"}; - _controller = controller("verifies", params); - _controller.processAction("actionGet", params); - assert('_controller.$abortIssued() eq "true"'); - assert('_controller.$performedRenderOrRedirect() eq "false"'); - } - - function test_invalid_redirect() { - request.cgi.request_method = "get"; - params = {controller = "verifies", action = "actionPostWithRedirect"}; - _controller = controller("verifies", params); - _controller.processAction("actionPostWithRedirect", params); - assert('_controller.$abortIssued() eq "false"'); - assert('_controller.$performedRenderOrRedirect() eq "true"'); - assert('_controller.getRedirect().$args.action eq "index"'); - assert('_controller.getRedirect().$args.controller eq "somewhere"'); - assert('_controller.getRedirect().$args.error eq "invalid"'); - } - - function test_valid_types() { - request.cgi.request_method = "post"; - params = { - controller = "verifies", - action = "actionPostWithTypesValid", - userid = "0", - authorid = "00000000-0000-0000-0000-000000000000" - }; - _controller = controller("verifies", params); - _controller.processAction("actionPostWithTypesValid", params); - assert('_controller.response() eq "actionPostWithTypesValid"'); - } - - function test_invalid_types_guid() { - request.cgi.request_method = "post"; - params = { - controller = "verifies", - action = "actionPostWithTypesInValid", - userid = "0", - authorid = "invalidguid" - }; - _controller = controller("verifies", params); - _controller.processAction("actionPostWithTypesInValid", params); - assert('_controller.$abortIssued() eq "true"'); - } - - function test_invalid_types_integer() { - request.cgi.request_method = "post"; - params = { - controller = "verifies", - action = "actionPostWithTypesInValid", - userid = "1.234", - authorid = "00000000-0000-0000-0000-000000000000" - }; - _controller = controller("verifies", params); - _controller.processAction("actionPostWithTypesInValid", params); - assert('_controller.$abortIssued() eq "true"'); - } - - function test_strings_allow_blank() { - request.cgi.request_method = "post"; - params = { - controller = "verifies", - action = "actionPostWithString", - username = "tony", - password = "" - }; - _controller = controller("verifies", params); - _controller.processAction("actionPostWithString", params); - assert('_controller.$abortIssued() eq "false"'); - } - - function test_strings_cannot_be_blank() { - request.cgi.request_method = "post"; - params = { - controller = "verifies", - action = "actionPostWithString", - username = "", - password = "" - }; - _controller = controller("verifies", params); - _controller.processAction("actionPostWithString", params); - assert('_controller.$abortIssued() eq "true"'); - } - -} diff --git a/core/src/wheels/tests/dispatch/findMatchingRoute.cfc b/core/src/wheels/tests/dispatch/findMatchingRoute.cfc deleted file mode 100644 index c73b41f021..0000000000 --- a/core/src/wheels/tests/dispatch/findMatchingRoute.cfc +++ /dev/null @@ -1,234 +0,0 @@ -component extends="wheels.tests.Test" { - - public void function packageSetup() { - _originalRoutes = Duplicate(application.wheels.routes); - $clearRoutes(); - - - mapper() - .namespace("admin") - .resources("users") - .root(to = "dashboard##index") - .end() - .resources("users") - .resource("profile") - .root(to = "dashboard##index") - .end(); - - d = $createObjectFromRoot(path = "wheels", fileName = "Dispatch", method = "$init"); - } - - public void function packageTeardown() { - application.wheels.routes = _originalRoutes; - } - - public void function $clearRoutes() { - application.wheels.routes = []; - } - - function setup() { - _originalForm = Duplicate(form); - _originalUrl = Duplicate(url); - StructClear(form); - StructClear(url); - _originalCgiMethod = request.cgi.request_method; - } - - function teardown() { - StructClear(form); - StructClear(url); - StructAppend(form, _originalForm, false); - StructAppend(url, _originalUrl, false); - request.cgi["request_method"] = _originalCgiMethod; - } - - function test_error_raised_when_route_not_found() { - e = raised('d.$findMatchingRoute(path="scouts")'); - assert('e eq "Wheels.RouteNotFound"'); - } - - function test_find_get_collection_route_that_exists() { - request.cgi["request_method"] = "GET"; - route = d.$findMatchingRoute(path = "users"); - assert('route.name eq "users" and route.methods eq "GET"'); - } - - function test_find_get_collection_route_that_exists_with_format() { - request.cgi["request_method"] = "GET"; - route = d.$findMatchingRoute(path = "users.csv"); - assert('route.name eq "users" and route.methods eq "GET"'); - } - - function test_find_post_collection_route_that_exists() { - request.cgi["request_method"] = "POST"; - route = d.$findMatchingRoute(path = "users"); - assert('route.name eq "users" and route.methods eq "POST"'); - } - - function test_find_post_collection_route_that_exists_with_format() { - request.cgi["request_method"] = "POST"; - route = d.$findMatchingRoute(path = "users.json"); - assert('route.name eq "users" and route.methods eq "POST"'); - } - - function test_find_get_member_new_route_that_exists() { - request.cgi["request_method"] = "GET"; - route = d.$findMatchingRoute(path = "users/new"); - assert('route.name eq "newUser" and route.methods eq "GET"'); - } - - function test_find_get_member_new_route_that_exists_with_format() { - request.cgi["request_method"] = "GET"; - route = d.$findMatchingRoute(path = "users/new.json"); - assert('route.name eq "newUser" and route.methods eq "GET"'); - } - - function test_find_get_member_edit_route_that_exists() { - request.cgi["request_method"] = "GET"; - route = d.$findMatchingRoute(path = "users/1/edit"); - assert('route.name eq "editUser" and route.methods eq "GET"'); - } - - function test_find_get_member_edit_route_that_exists_with_format() { - request.cgi["request_method"] = "GET"; - route = d.$findMatchingRoute(path = "users/1/edit.json"); - assert('route.name eq "editUser" and route.methods eq "GET"'); - } - - function test_find_get_member_route_that_exists() { - request.cgi["request_method"] = "GET"; - route = d.$findMatchingRoute(path = "users/1"); - assert('route.name eq "user" and route.methods eq "GET"'); - } - - function test_find_get_member_route_that_exists_with_format() { - request.cgi["request_method"] = "GET"; - route = d.$findMatchingRoute(path = "users/1.json"); - assert('route.name eq "user" and route.methods eq "GET"'); - } - - function test_find_put_member_route_that_exists() { - request.cgi["request_method"] = "POST"; - form._method = "PUT"; - route = d.$findMatchingRoute(path = "users/1"); - assert('route.name eq "user" and route.methods eq "PUT"'); - } - - function test_find_put_member_route_that_exists_with_format() { - request.cgi["request_method"] = "POST"; - form._method = "PUT"; - route = d.$findMatchingRoute(path = "users/1.json"); - assert('route.name eq "user" and route.methods eq "PUT"'); - } - - function test_find_delete_member_route_that_exists() { - request.cgi["request_method"] = "POST"; - form._method = "delete"; - route = d.$findMatchingRoute(path = "users/1.json"); - assert('route.name eq "user" and route.methods eq "delete"'); - } - - function test_find_delete_member_route_that_exists_with_format() { - request.cgi["request_method"] = "POST"; - form._method = "delete"; - route = d.$findMatchingRoute(path = "users/1"); - assert('route.name eq "user" and route.methods eq "delete"'); - } - - // nested route tests - - function test_find_nested_get_collection_route_that_exists() { - request.cgi["request_method"] = "GET"; - route = d.$findMatchingRoute(path = "admin/users"); - assert('route.name eq "adminUsers" and route.methods eq "GET"'); - } - - function test_find_nested_get_collection_route_that_exists_with_format() { - request.cgi["request_method"] = "GET"; - route = d.$findMatchingRoute(path = "admin/users.csv"); - assert('route.name eq "adminUsers" and route.methods eq "GET"'); - } - - function test_find_nested_post_collection_route_that_exists() { - request.cgi["request_method"] = "POST"; - route = d.$findMatchingRoute(path = "admin/users"); - assert('route.name eq "adminUsers" and route.methods eq "POST"'); - } - - function test_find_nested_post_collection_route_that_exists_with_format() { - request.cgi["request_method"] = "POST"; - route = d.$findMatchingRoute(path = "admin/users.json"); - assert('route.name eq "adminUsers" and route.methods eq "POST"'); - } - - function test_find_nested_get_member_new_route_that_exists() { - request.cgi["request_method"] = "GET"; - route = d.$findMatchingRoute(path = "admin/users/new"); - assert('route.name eq "newAdminUser" and route.methods eq "GET"'); - } - - function test_find_nested_get_member_new_route_that_exists_with_format() { - request.cgi["request_method"] = "GET"; - route = d.$findMatchingRoute(path = "admin/users/new.json"); - assert('route.name eq "newAdminUser" and route.methods eq "GET"'); - } - - function test_find_nested_get_member_edit_route_that_exists() { - request.cgi["request_method"] = "GET"; - route = d.$findMatchingRoute(path = "admin/users/1/edit"); - assert('route.name eq "editAdminUser" and route.methods eq "GET"'); - } - - function test_find_nested_get_member_edit_route_that_exists_with_format() { - request.cgi["request_method"] = "GET"; - route = d.$findMatchingRoute(path = "admin/users/1/edit.json"); - assert('route.name eq "editAdminUser" and route.methods eq "GET"'); - } - - function test_find_nested_get_member_route_that_exists() { - request.cgi["request_method"] = "GET"; - route = d.$findMatchingRoute(path = "admin/users/1"); - assert('route.name eq "adminUser" and route.methods eq "GET"'); - } - - function test_find_nested_get_member_route_that_exists_with_format() { - request.cgi["request_method"] = "GET"; - route = d.$findMatchingRoute(path = "admin/users/1.json"); - assert('route.name eq "adminUser" and route.methods eq "GET"'); - } - - function test_find_nested_put_member_route_that_exists() { - request.cgi["request_method"] = "POST"; - form._method = "PUT"; - route = d.$findMatchingRoute(path = "admin/users/1"); - assert('route.name eq "adminUser" and route.methods eq "PUT"'); - } - - function test_find_nested_put_member_route_that_exists_with_format() { - request.cgi["request_method"] = "POST"; - form._method = "PUT"; - route = d.$findMatchingRoute(path = "admin/users/1.json"); - assert('route.name eq "adminUser" and route.methods eq "PUT"'); - } - - function test_find_nested_delete_member_route_that_exists() { - request.cgi["request_method"] = "POST"; - form._method = "delete"; - route = d.$findMatchingRoute(path = "admin/users/1.json"); - assert('route.name eq "adminUser" and route.methods eq "delete"'); - } - - function test_find_nested_delete_member_route_that_exists_with_format() { - request.cgi["request_method"] = "POST"; - form._method = "delete"; - route = d.$findMatchingRoute(path = "admin/users/1"); - assert('route.name eq "adminUser" and route.methods eq "delete"'); - } - - function test_head_request_aliases_get() { - request.cgi["request_method"] = "HEAD"; - route = d.$findMatchingRoute(path = "users"); - assert('route.name eq "Users" and route.methods eq "GET"'); - } - -} diff --git a/core/src/wheels/tests/dispatch/findMatchingRouteMega.cfc b/core/src/wheels/tests/dispatch/findMatchingRouteMega.cfc deleted file mode 100644 index bf94f3aea3..0000000000 --- a/core/src/wheels/tests/dispatch/findMatchingRouteMega.cfc +++ /dev/null @@ -1,117 +0,0 @@ -component extends="wheels.tests.Test" { - - public void function packageSetup() { - _originalRoutes = Duplicate(application.wheels.routes); - nounPlurals = [ - "people", - "dogs", - "cats", - "pigs", - "admins", - "pages", - "elements", - "charts", - "tabs", - "categories", - "cows", - "services", - "products", - "pictures", - "images", - "routes", - "cars", - "vehicles", - "bikes", - "buses", - "cups", - "words", - "cells", - "phones", - "speakers", - "sneakers", - "lions", - "tigers", - "elephants", - "deers", - "pandas", - "places", - "things", - "mugs", - "plants", - "stars", - "cards", - "credits", - "coins", - "monitors", - "books", - "coats", - "shirts", - "jackets", - "pants", - "miners", - "hangers", - "plates", - "spoons", - "forks", - "knives", - "users" - ]; - - $clearRoutes(); - - - dr = mapper().root(to = "dashboard##index").namespace("admin"); - for (local.item in nounPlurals) { - dr.resources(name = local.item, nested = true) - .resources(name = "comments", shallow = true) - .resources(name = "likes", shallow = true) - .end(); - } - dr.root(to = "dashboard##index").end(); - for (local.item in nounPlurals) { - dr.resources(name = local.item, nested = true) - .resources(name = "comments", shallow = true) - .resources(name = "likes", shallow = true) - .end(); - } - dr.resource("profile").end(); - - d = $createObjectFromRoot(path = "wheels", fileName = "Dispatch", method = "$init"); - } - - public void function packageTeardown() { - application.wheels.routes = _originalRoutes; - } - - public void function $clearRoutes() { - application.wheels.routes = []; - } - - function setup() { - _originalForm = Duplicate(form); - _originalUrl = Duplicate(url); - StructClear(form); - StructClear(url); - _originalCgiMethod = request.cgi.request_method; - } - - function teardown() { - StructClear(form); - StructClear(url); - StructAppend(form, _originalForm, false); - StructAppend(url, _originalUrl, false); - request.cgi["request_method"] = _originalCgiMethod; - } - - function test_error_raised_when_route_not_found() { - e = raised('d.$findMatchingRoute(path="scouts")'); - assert('e eq "Wheels.RouteNotFound"'); - } - - function test_find_nested_get_collection_route_that_exists() { - request.cgi["request_method"] = "GET"; - route = d.$findMatchingRoute(path = "admin/users"); - assert('route.name eq "adminUsers" and route.methods eq "GET"'); - } - -} diff --git a/core/src/wheels/tests/dispatch/getrequestmethod.cfc b/core/src/wheels/tests/dispatch/getrequestmethod.cfc deleted file mode 100644 index 3b0a061047..0000000000 --- a/core/src/wheels/tests/dispatch/getrequestmethod.cfc +++ /dev/null @@ -1,61 +0,0 @@ -component extends="wheels.tests.Test" { - - function setup() { - _originalForm = Duplicate(form); - _originalUrl = Duplicate(url); - _originalCgiMethod = request.cgi.request_method; - StructClear(form); - StructClear(url); - d = $createObjectFromRoot(path = "wheels", fileName = "Dispatch", method = "$init"); - } - - function teardown() { - StructClear(form); - StructClear(url); - StructAppend(form, _originalForm, false); - StructAppend(url, _originalUrl, false); - request.cgi["request_method"] = _originalCgiMethod; - } - - function test_get() { - request.cgi["request_method"] = "GET"; - method = d.$getRequestMethod(); - assert('method eq "GET"'); - } - - // https://github.com/wheels-dev/wheels/issues/886 - function test_get_disallow_override() { - request.cgi["request_method"] = "GET"; - url._method = "delete"; - method = d.$getRequestMethod(); - assert('method eq "GET"'); - } - - function test_get_form_should_not_override() { - request.cgi["request_method"] = "GET"; - form._method = "delete"; - method = d.$getRequestMethod(); - assert('method eq "GET"'); - } - - function test_post() { - request.cgi["request_method"] = "POST"; - method = d.$getRequestMethod(); - assert('method eq "POST"'); - } - - function test_post_override() { - request.cgi["request_method"] = "POST"; - form._method = "PUT"; - method = d.$getRequestMethod(); - assert('method eq "PUT"'); - } - - function test_post_url_should_not_override() { - request.cgi["request_method"] = "POST"; - url._method = "PUT"; - method = d.$getRequestMethod(); - assert('method eq "POST"'); - } - -} diff --git a/core/src/wheels/tests/dispatch/request.cfc b/core/src/wheels/tests/dispatch/request.cfc deleted file mode 100644 index eccfabfb91..0000000000 --- a/core/src/wheels/tests/dispatch/request.cfc +++ /dev/null @@ -1,60 +0,0 @@ -component extends="wheels.tests.Test" { - - public void function setup() { - _params = {controller = "test", action = "index"}; - _originalRoutes = Duplicate(application.wheels.routes); - application.wheels.routes = []; - dispatch = CreateObject("component", "wheels.Dispatch"); - } - - public void function teardown() { - application.wheels.routes = _originalRoutes; - } - - function test_route_with_format() { - mapper().$match(pattern = "users/[username].[format]", controller = "test", action = "test").end(); - args = {}; - args.pathinfo = "/users/foo.bar"; - args.urlScope["username"] = "foo.bar"; - _params = dispatch.$paramParser(argumentCollection = args); - assert('_params.controller eq "Test"'); - assert('_params.action eq "test"'); - assert('_params.username eq "foo"'); - assert('_params.format eq "bar"'); - } - - function test_route_with_format_only() { - mapper().$match(pattern = "contact/export.[format]", controller = "test", action = "test").end(); - args = {}; - args.pathinfo = "/contact/export.csv"; - args.urlScope = {}; - _params = dispatch.$paramParser(argumentCollection = args); - assert('_params.controller eq "Test"'); - assert('_params.action eq "test"'); - assert('_params.format eq "csv"'); - } - - function test_route_without_format_should_ignore_fullstops() { - mapper() - .$match(pattern = "users/[username]", controller = "test", action = "test", constraints = {"username" = "[^/]+"}) - .end(); - args = {}; - args.pathinfo = "/users/foo.bar"; - args.urlScope["username"] = "foo.bar"; - _params = dispatch.$paramParser(argumentCollection = args); - assert('_params.username eq "foo.bar"'); - } - - function test_route_with_format_and_format_not_specified() { - mapper().$match(pattern = "users/[username](.[format])", controller = "test", action = "test").end(); - args = {}; - args.pathinfo = "/users/foo"; - args.urlScope["username"] = "foo"; - _params = dispatch.$paramParser(argumentCollection = args); - assert('_params.controller eq "Test"'); - assert('_params.action eq "test"'); - assert('_params.username eq "foo"'); - assert('!structKeyExists(_params, "format")'); - } - -} diff --git a/core/src/wheels/tests/dispatch/setCorsHeaders.cfc b/core/src/wheels/tests/dispatch/setCorsHeaders.cfc deleted file mode 100644 index 677e6fb4e0..0000000000 --- a/core/src/wheels/tests/dispatch/setCorsHeaders.cfc +++ /dev/null @@ -1,157 +0,0 @@ -component extends="wheels.tests.Test" { - - /** - * Note, when testing headers, the result from getPageContext().getResponse().getHeader - * Can't be assigned to a variable, as it will return Java Null:isEmpty - * So you need to directly test it - * Don't JavaCast("null","") to a ColdFusion variable. Unexpected results will occur. - * - * As ACF can't return the actual content of the header in a current page context, we're skipping - * ACF Tests for this suite (!) - - - function setup() { - if ($isLucee()) { - $$oldCGIScope = request.cgi; - $$oldHeaders = request.wheels.httprequestdata.headers; - $$originalRoutes = application.wheels.routes; - application.wheels.allowCorsRequests = true; - d = $createObjectFromRoot(path = "wheels", fileName = "Dispatch", method = "$init"); - $resetHeaders(); - application.wheels.routes = []; - config = {path = "wheels", fileName = "Mapper", method = "$init"}; - } - } - - function teardown() { - if ($isLucee()) { - request.cgi = $$oldCGIScope; - request.wheels.httprequestdata.headers = $$oldHeaders; - application.wheels.routes = $$originalRoutes; - application[$appKey()].allowCorsRequests = false; - d = ""; - $resetHeaders(); - } - } - - function test_$setCORS_Headers_sets_Defaults() { - if ($isLucee()) { - d.$setCORSHeaders(); - origin = $getHeader('Access-Control-Allow-Origin'); - allowHeaders = $getHeader('Access-Control-Allow-Headers'); - allowMethods = $getHeader('Access-Control-Allow-Methods'); - assert("origin EQ '*'"); - assert("allowHeaders EQ 'Origin, Content-Type, X-Auth-Token, X-Requested-By, X-Requested-With'"); - assert("allowMethods EQ 'GET, POST, PATCH, PUT, DELETE, OPTIONS'"); - } - } - function test_$setCORS_Headers_sets_Origin_as_wildcard() { - if ($isLucee()) { - d.$setCORSHeaders(allowOrigin = "*"); - origin = $getHeader('Access-Control-Allow-Origin'); - assert("origin EQ '*'"); - } - } - function test_$setCORS_Headers_sets_Origin_as_simple_string() { - if ($isLucee()) { - request.wheels.httprequestdata.headers['origin'] = "http://www.mydomain.com"; - d.$setCORSHeaders(allowOrigin = "http://www.mydomain.com"); - origin = $getHeader('Access-Control-Allow-Origin'); - assert("origin EQ 'http://www.mydomain.com'"); - } - } - function test_$setCORS_Headers_ignores_Origin_as_simple_string() { - if ($isLucee()) { - request.wheels.httprequestdata.headers['origin'] = "http://www.baddomain.com"; - d.$setCORSHeaders(allowOrigin = "http://www.mydomain.com"); - assert("$getHeader('Access-Control-Allow-Origin') IS JavaCast('null', '')"); - } - } - function test_$setCORS_Headers_sets_Origin_as_List() { - if ($isLucee()) { - request.wheels.httprequestdata.headers['origin'] = "https://domain.com"; - d.$setCORSHeaders(allowOrigin = "http://www.mydomain.com,https://domain.com"); - origin = $getHeader('Access-Control-Allow-Origin'); - assert("origin EQ 'https://domain.com'"); - } - } - function test_$setCORS_Headers_ignores_Origin_as_List() { - if ($isLucee()) { - request.wheels.httprequestdata.headers['origin'] = "https://BADdomain.com"; - d.$setCORSHeaders(allowOrigin = "http://www.mydomain.com,https://domain.com"); - assert("$getHeader('Access-Control-Allow-Origin') IS JavaCast('null', '')"); - } - } - function test_$setCORS_Headers_sets_Credentials() { - if ($isLucee()) { - d.$setCORSHeaders(allowCredentials = true); - result = $getHeader('Access-Control-Allow-Credentials'); - assert("result"); - } - } - function test_$setCORS_Headers_sets_AllowHeaders() { - if ($isLucee()) { - d.$setCORSHeaders(allowHeaders = "Origin, Content-Type, X-Auth-Token, X-Foo-Bar"); - result = $getHeader('Access-Control-Allow-Headers'); - assert("result EQ 'Origin, Content-Type, X-Auth-Token, X-Foo-Bar'"); - } - } - function test_$setCORS_Headers_sets_AllowMethods() { - if ($isLucee()) { - d.$setCORSHeaders(allowMethods = "GET, PUT, PATCH"); - result = $getHeader('Access-Control-Allow-Methods'); - assert("result EQ 'GET, PUT, PATCH'"); - } - } - function test_$setCORS_Headers_sets_AllowMethodsByRouteGet() { - if ($isLucee()) { - $mapper() - .$draw() - .resources(name = "cats") - .end(); - - d.$setCORSHeaders(allowMethodsByRoute = true, pathInfo = "/cats", scriptName = "/index.cfm"); - - returnedMethods = $getHeader('Access-Control-Allow-Methods'); - assert("returnedMethods EQ 'GET, POST'"); - } - } - function test_$setCORS_Headers_sets_AllowMethodsByRouteShow() { - if ($isLucee()) { - $mapper() - .$draw() - .resources(name = "cats") - .end(); - - d.$setCORSHeaders(allowMethodsByRoute = true, pathInfo = "/cats/123", scriptName = "/index.cfm"); - - returnedMethods = $getHeader('Access-Control-Allow-Methods'); - assert("returnedMethods EQ 'GET, PATCH, PUT, DELETE'"); - } - }**/ - /** - * Helpers: - - private function $getHeader(string name) { - return GetPageContext().getResponse().getHeader(arguments.name); - } - - private function $resetHeaders() { - local.pc = GetPageContext().getResponse(); - local.pc.setHeader('Access-Control-Allow-Origin', Javacast("null", "")); - local.pc.setHeader('Access-Control-Allow-Headers', Javacast("null", "")); - local.pc.setHeader('Access-Control-Allow-Methods', Javacast("null", "")); - local.pc.setHeader('Access-Control-Allow-Credentials', Javacast("null", "")); - } - - private struct function $mapper() { - local.args = Duplicate(config); - StructAppend(local.args, arguments, true); - return $createObjectFromRoot(argumentCollection = local.args); - } - - private boolean function $isLucee() { - return StructKeyExists(server, "lucee"); - }**/ - -} diff --git a/core/src/wheels/tests/global/caching.cfc b/core/src/wheels/tests/global/caching.cfc deleted file mode 100644 index 5e78b603f1..0000000000 --- a/core/src/wheels/tests/global/caching.cfc +++ /dev/null @@ -1,27 +0,0 @@ -component extends="wheels.tests.Test" { - - function test_hashing_arguments_to_identical_result() { - result1 = _method(1, 2, 3, 4, 5, 6, 7, 8, 9); - result2 = _method(1, 2, 3, 4, 5, 6, 7, 8, 9); - assert("result1 IS result2"); - result1 = _method("per", "was", "here"); - result2 = _method("per", "was", "here"); - assert("result1 IS result2"); - result1 = _method(a = 1, b = 2); - result2 = _method(a = 1, b = 2); - assert("result1 IS result2"); - aStruct = StructNew(); - aStruct.test1 = "a"; - aStruct.test2 = "b"; - anArray = ArrayNew(1); - anArray[1] = 1; - result1 = _method(a = aStruct, b = anArray); - result2 = _method(a = aStruct, b = anArray); - assert("result1 IS result2"); - } - - function _method() { - return $hashedKey(arguments); - } - -} diff --git a/core/src/wheels/tests/global/internal/args.cfc b/core/src/wheels/tests/global/internal/args.cfc deleted file mode 100644 index cc59a62a64..0000000000 --- a/core/src/wheels/tests/global/internal/args.cfc +++ /dev/null @@ -1,49 +0,0 @@ -component extends="wheels.tests.Test" { - - function test_declaring_required_arguments_throws_error_when_missing() { - args = {}; - expected = "Wheels.IncorrectArguments"; - actual = raised('$args(args=args, name="sendEmail", combine="template/templates/!")'); - assert('expected eq actual'); - actual = raised('$args(args=args, name="sendEmail", combine="template/templates", required="template")'); - assert('expected eq actual'); - actual = raised('$args(args=args, name="sendEmail", required="template")'); - assert('expected eq actual'); - actual = raised('$args(args=args, name="sendEmail", template="", required="template")'); - args.template = ""; - actual = raised('$args(args=args, name="sendEmail", combine="template/templates", required="template")'); - assert('expected eq actual'); - args.template = ""; - actual = raised('$args(args=args, name="sendEmail", combine="template/templates")'); - assert('actual eq ""'); - } - - function test_not_declaring_required_arguments_should_not_raise_error_when_missing() { - args = {}; - expected = ""; - actual = raised('$args(args=args, name="sendEmail", combine="template/templates")'); - assert('expected eq actual'); - actual = raised('$args(args=args, name="sendEmail")'); - assert('expected eq actual'); - } - - function test_combined_arguments() { - expected = ""; - actual = combined_arguments(template = "tony", templates = "per"); - assert('actual.template eq "per"'); - assert('not StructKeyExists(actual, "templates")'); - actual = combined_arguments(templates = "per"); - assert('actual.template eq "per"'); - assert('not StructKeyExists(actual, "templates")'); - } - - /** - * HELPERS - */ - - function combined_arguments() { - $args(args = arguments, name = "sendEmail", combine = "template/templates"); - return arguments; - } - -} diff --git a/core/src/wheels/tests/global/internal/cgiscope.cfc b/core/src/wheels/tests/global/internal/cgiscope.cfc deleted file mode 100644 index 9fbfc00375..0000000000 --- a/core/src/wheels/tests/global/internal/cgiscope.cfc +++ /dev/null @@ -1,65 +0,0 @@ -component extends="wheels.tests.Test" { - - function setup() { - cgi_scope = {}; - cgi_scope.request_method = ""; - cgi_scope.http_x_requested_with = ""; - cgi_scope.http_referer = ""; - cgi_scope.server_name = ""; - cgi_scope.query_string = ""; - cgi_scope.remote_addr = ""; - cgi_scope.server_port = ""; - cgi_scope.server_port_secure = ""; - cgi_scope.server_protocol = ""; - cgi_scope.http_host = ""; - cgi_scope.http_accept = ""; - cgi_scope.content_type = ""; - cgi_scope.script_name = "/index.cfm"; - cgi_scope.path_info = "/users/list/index.cfm"; - cgi_scope.http_x_rewrite_url = "/users/list/http_x_rewrite_url/index.cfm?controller=wheels&action=wheels&view=test"; - cgi_scope.http_x_original_url = "/users/list/http_x_original_url/index.cfm?controller=wheels&action=wheels&view=test"; - cgi_scope.request_uri = "/users/list/request_uri/index.cfm?controller=wheels&action=wheels&view=test"; - cgi_scope.redirect_url = "/users/list/redirect_url/index.cfm?controller=wheels&action=wheels&view=test"; - cgi_scope.http_x_forwarded_for = ""; - cgi_scope.http_x_forwarded_proto = ""; - } - - function test_path_info_blank() { - cgi_scope.path_info = ""; - _cgi = $cgiScope(scope = cgi_scope); - assert('_cgi.path_info eq "/users/list/http_x_rewrite_url"'); - cgi_scope.http_x_rewrite_url = ""; - _cgi = $cgiScope(scope = cgi_scope); - assert('_cgi.path_info eq "/users/list/http_x_original_url"'); - cgi_scope.http_x_original_url = ""; - _cgi = $cgiScope(scope = cgi_scope); - assert('_cgi.path_info eq "/users/list/request_uri"'); - cgi_scope.request_uri = ""; - _cgi = $cgiScope(scope = cgi_scope); - assert('_cgi.path_info eq "/users/list/redirect_url"'); - } - - function test_path_info_should_remove_trailing_slash() { - cgi_scope.path_info = ""; - _cgi = $cgiScope(scope = cgi_scope); - assert('_cgi.path_info eq "/users/list/http_x_rewrite_url"'); - } - - function test_path_info_non_ascii() { - e = { - script_name = "/index.cfm", - path_info = "/wheels/wheels/%E5%A5%B3%E5%A3%AB%E7%94%A8%E5%93%81", - unencoded_url = "/index.cfm/wheels/wheels/%E5%A5%B3%E5%A3%AB%E7%94%A8%E5%93%81?normal=1&chinese=%E5%A5%B3%E5%A3%AB%E7%94%A8%E5%93%81", - query_string = "normal=1&chinese=%E5%A5%B3%E5%A3%AB%E7%94%A8%E5%93%81" - }; - scope = { - script_name = "/index.cfm", - path_info = "/wheels/wheels/????", - unencoded_url = "/index.cfm/wheels/wheels/%E5%A5%B3%E5%A3%AB%E7%94%A8%E5%93%81?normal=1&chinese=%E5%A5%B3%E5%A3%AB%E7%94%A8%E5%93%81", - query_string = "normal=1&chinese=%E5%A5%B3%E5%A3%AB%E7%94%A8%E5%93%81" - }; - r = $cgiScope(keys = "script_name,path_info,unencoded_url,query_string", scope = scope); - assert('r.path_info IS URLDecode(e.path_info)'); - } - -} diff --git a/core/src/wheels/tests/global/internal/checkMinimumVersion.cfc b/core/src/wheels/tests/global/internal/checkMinimumVersion.cfc deleted file mode 100644 index 2156f09df8..0000000000 --- a/core/src/wheels/tests/global/internal/checkMinimumVersion.cfc +++ /dev/null @@ -1,47 +0,0 @@ -component extends="wheels.tests.Test" { - - function test_railo_invalid() { - assert('Len($checkMinimumVersion(version="4.3.0.003", engine="Railo"))'); - assert('Len($checkMinimumVersion(version="4.2.1.008", engine="Railo"))'); - assert('Len($checkMinimumVersion(version="3.0.2", engine="Railo"))'); - assert('Len($checkMinimumVersion(version="4.2.1.000", engine="Railo"))'); - assert('Len($checkMinimumVersion(version="3.1.2", engine="Railo"))'); - assert('Len($checkMinimumVersion(version="3.1.2.020", engine="Railo"))'); - } - - function test_lucee_valid() { - assert('!Len($checkMinimumVersion(version="5.3.2.77", engine="Lucee"))'); - assert('!Len($checkMinimumVersion(version="5.3.5.92", engine="Lucee"))'); - } - - function test_lucee_invalid() { - assert('Len($checkMinimumVersion(version="5.3.1.103", engine="Lucee"))'); - assert('Len($checkMinimumVersion(version="4.5.5.006", engine="Lucee"))'); - assert('Len($checkMinimumVersion(version="4.5.1.021", engine="Lucee"))'); - assert('Len($checkMinimumVersion(version="4.4.0", engine="Lucee"))'); - assert('Len($checkMinimumVersion(version="4.5.0.023", engine="Lucee"))'); - } - - function test_adobe_valid() { - assert('!Len($checkMinimumVersion(version="11,0,18,314030", engine="Adobe ColdFusion"))'); - assert('!Len($checkMinimumVersion(version="2016,0,10,314028", engine="Adobe ColdFusion"))'); - assert('!Len($checkMinimumVersion(version="2018,0,03,314033", engine="Adobe ColdFusion"))'); - } - - function test_adobe_invalid() { - assert('Len($checkMinimumVersion(version="7", engine="Adobe ColdFusion"))'); - assert('Len($checkMinimumVersion(version="8,0,1,0", engine="Adobe ColdFusion"))'); - assert('Len($checkMinimumVersion(version="8,0", engine="Adobe ColdFusion"))'); - assert('Len($checkMinimumVersion(version="8,0,0,0", engine="Adobe ColdFusion"))'); - assert('Len($checkMinimumVersion(version="8,0,0,195765", engine="Adobe ColdFusion"))'); - assert('Len($checkMinimumVersion(version="8,0,1,195765", engine="Adobe ColdFusion"))'); - assert('Len($checkMinimumVersion(version="9,0,0,251028", engine="Adobe ColdFusion"))'); - assert('Len($checkMinimumVersion(version="10,0,0,282462", engine="Adobe ColdFusion"))'); - assert('Len($checkMinimumVersion(version="10,0,3,282462", engine="Adobe ColdFusion"))'); - assert('Len($checkMinimumVersion(version="10,0,4,277803", engine="Adobe ColdFusion"))'); - assert('Len($checkMinimumVersion(version="10,0,23,302580", engine="Adobe ColdFusion"))'); - assert('Len($checkMinimumVersion(version="11,0,3,282462", engine="Adobe ColdFusion"))'); - assert('Len($checkMinimumVersion(version="11,0,12,302575", engine="Adobe ColdFusion"))'); - } - -} diff --git a/core/src/wheels/tests/global/internal/fullCgiDomainString.cfc b/core/src/wheels/tests/global/internal/fullCgiDomainString.cfc deleted file mode 100644 index 26b83d8e52..0000000000 --- a/core/src/wheels/tests/global/internal/fullCgiDomainString.cfc +++ /dev/null @@ -1,61 +0,0 @@ -component extends="wheels.tests.Test" { - - function setup(){ - } - function teardown(){ - } - function Test_$fullDomainString(){ - r = $fullDomainString("http://www.cfwheels.com"); - r2 = $fullDomainString("https://www.cfwheels.com"); - r3 = $fullDomainString("http://www.cfwheels.com:8080"); - r4 = $fullDomainString("https://www.cfwheels.com:8443"); - r5 = $fullDomainString("www.cfwheels.com"); - r6 = $fullDomainString("www.cfwheels.com:80"); - r7 = $fullDomainString("www.cfwheels.com:8888"); - r8 = $fullDomainString("www.cfwheels.com:443"); - assert("r EQ 'http://www.cfwheels.com:80'"); - assert("r2 EQ 'https://www.cfwheels.com:443'"); - assert("r3 EQ 'http://www.cfwheels.com:8080'"); - assert("r4 EQ 'https://www.cfwheels.com:8443'"); - assert("r5 EQ 'http://www.cfwheels.com:80'"); - assert("r6 EQ 'http://www.cfwheels.com:80'"); - assert("r7 EQ 'http://www.cfwheels.com:8888'"); - assert("r8 EQ 'https://www.cfwheels.com:443'"); - } - function Test_get_Full_Domain_String_From_CGI_https_non_standard(){ - r = $fullCgiDomainString( - { - server_name = "www.cfwheels.com", - server_port = 8443, - server_port_secure = 1 - }); - assert("r EQ 'https://www.cfwheels.com:8443'"); - } - function Test_get_Full_Domain_String_From_CGI_https(){ - r = $fullCgiDomainString( - { - server_name = "www.cfwheels.com", - server_port = 443, - server_port_secure = 1 - }); - assert("r EQ 'https://www.cfwheels.com:443'"); - } - function Test_get_Full_Domain_String_From_CGI_http(){ - r = $fullCgiDomainString( - { - server_name = "www.cfwheels.com", - server_port = 80, - server_port_secure = 0 - }); - assert("r EQ 'http://www.cfwheels.com:80'"); - } - function Test_get_Full_Domain_String_From_CGI_http_non_standard(){ - r = $fullCgiDomainString( - { - server_name = "www.cfwheels.com", - server_port = 8080, - server_port_secure = 0 - }); - assert("r EQ 'http://www.cfwheels.com:8080'"); - } -} diff --git a/core/src/wheels/tests/global/internal/getrequesttimeout.cfc b/core/src/wheels/tests/global/internal/getrequesttimeout.cfc deleted file mode 100644 index 8989ece5a6..0000000000 --- a/core/src/wheels/tests/global/internal/getrequesttimeout.cfc +++ /dev/null @@ -1,14 +0,0 @@ -component extends="wheels.tests.Test" { - - function teardown() { - setting requestTimeout=10000; - } - - function test_$getrequesttimeout() { - setting requestTimeout=666; - actual = $getRequestTimeout(); - expected = 666; - assert("actual eq expected"); - } - -} diff --git a/core/src/wheels/tests/global/internal/hashedkey.cfc b/core/src/wheels/tests/global/internal/hashedkey.cfc deleted file mode 100644 index 8a213aabaf..0000000000 --- a/core/src/wheels/tests/global/internal/hashedkey.cfc +++ /dev/null @@ -1,48 +0,0 @@ -component extends="wheels.tests.Test" { - - function test_accepts_undefined_value(string value1 = "foo", string value2) { - /* value2 arg creates an undefined value to test $hashedKey() */ - e = raised('$hashedKey(argumentCollection=arguments)'); - r = ""; - assert('e eq r'); - } - - function test_accepts_generated_query(string a = "foo", query = QueryNew('a,b,c,e')) { - /* query arg creates a query that does not have sql metadata */ - e = raised('$hashedKey(argumentCollection=arguments)'); - r = ""; - assert('e eq r'); - } - - function test_same_output() { - binaryData = FileReadBinary(ExpandPath('/wheels/tests/_assets/files/cfwheels-logo.png')); - transaction action="begin" { - photo = model("photo").findOne(); - photo.update(filename = "somefilename", fileData = binaryData); - photo = model("photo").findAll(where = "id = #photo.id#"); - transaction action="rollback"; - } - a = []; - a[1] = "petruzzi"; - a[2] = "gibson"; - query = QueryNew('a,b,c,d,e'); - QueryAddRow(query, 1); - QuerySetCell(query, "a", "tony"); - QuerySetCell(query, "b", "per"); - QuerySetCell(query, "c", "james"); - QuerySetCell(query, "d", "chris"); - QuerySetCell(query, "e", "raul"); - a[3] = query; - a[4] = [1, 2, 3, 4, 5, 6]; - a[5] = {a = 1, b = 2, c = 3, d = 4}; - a[6] = photo; - args = {}; - args.a = a; - e = $hashedKey(argumentCollection = args); - ArraySwap(a, 1, 3); - ArraySwap(a, 4, 5); - r = $hashedKey(argumentCollection = args); - assert('e eq r'); - } - -} diff --git a/core/src/wheels/tests/global/internal/listClean.cfc b/core/src/wheels/tests/global/internal/listClean.cfc deleted file mode 100644 index dd37731bcc..0000000000 --- a/core/src/wheels/tests/global/internal/listClean.cfc +++ /dev/null @@ -1,23 +0,0 @@ -component extends="wheels.tests.Test" { - - function test_$listClean_default_delim() { - mylist = "tony, per , james ,,, chris , raul ,,,, peter"; - e = "tony,per,james,chris,raul,peter"; - r = $listClean(mylist); - assert('e eq r'); - } - - function test_$listClean_provide_delim() { - mylist = "tony| per | james | chris | raul ||| peter"; - e = "tony|per|james|chris|raul|peter"; - r = $listClean(mylist, "|"); - assert('e eq r'); - } - - function test_$listClean_return_array() { - mylist = "tony, per , james ,,, chris , raul ,,,, peter"; - r = $listClean(list = mylist, returnAs = "array"); - assert('IsArray(r) and ArrayLen(r) eq 6'); - } - -} diff --git a/core/src/wheels/tests/global/internal/listToStruct.cfc b/core/src/wheels/tests/global/internal/listToStruct.cfc deleted file mode 100644 index 5f4d229215..0000000000 --- a/core/src/wheels/tests/global/internal/listToStruct.cfc +++ /dev/null @@ -1,11 +0,0 @@ -component extends="wheels.tests.Test" { - - function test_$listToStruct() { - actual = $listToStruct("a,b,c"); - assert('actual.a eq 1'); - assert('actual.b eq 1'); - assert('actual.c eq 1'); - assert("ListLen(StructKeyList(actual)) eq 3"); - } - -} diff --git a/core/src/wheels/tests/global/internal/wildcardDomainMatchCGI.cfc b/core/src/wheels/tests/global/internal/wildcardDomainMatchCGI.cfc deleted file mode 100644 index 717e9b186c..0000000000 --- a/core/src/wheels/tests/global/internal/wildcardDomainMatchCGI.cfc +++ /dev/null @@ -1,69 +0,0 @@ -component extends="wheels.tests.Test" { - function setup(){ - } - function teardown(){ - } - function Test_simple_exact_match_http(){ - r = $wildcardDomainMatchCGI("http://www.domain.com", - { - server_name = "www.domain.com", - server_port = 80, - server_port_secure = 0 - }); - assert("r EQ true"); - } - function Test_simple_exact_match_http_non_standard_port(){ - r = $wildcardDomainMatchCGI("http://www.domain.com:8080", - { - server_name = "www.domain.com", - server_port = 8080, - server_port_secure = 0 - }); - assert("r EQ true"); - } - function Test_simple_exact_match_https(){ - r = $wildcardDomainMatchCGI("https://www.domain.com", - { - server_name = "www.domain.com", - server_port = 443, - server_port_secure = 1 - }); - assert("r EQ true"); - } - function Test_simple_exact_match_https_non_standard_port(){ - r = $wildcardDomainMatchCGI("https://www.domain.com:8443", - { - server_name = "www.domain.com", - server_port = 8443, - server_port_secure = 1 - }); - assert("r EQ true"); - } - function Test_simple_wildcard(){ - r = $wildcardDomainMatchCGI("https://*.domain.com", - { - server_name = "anything.domain.com", - server_port = 443, - server_port_secure = 1 - }); - assert("r EQ true"); - } - function Test_simple_wildcard_suffix(){ - r = $wildcardDomainMatchCGI("https://www.domain.*", - { - server_name = "www.domain.net", - server_port = 443, - server_port_secure = 1 - }); - assert("r EQ true"); - } - function Test_simple_wildcard_position(){ - r = $wildcardDomainMatchCGI("https://api.*.domain.com", - { - server_name = "api.staging.domain.com", - server_port = 443, - server_port_secure = 1 - }); - assert("r EQ true"); - } -} diff --git a/core/src/wheels/tests/global/public/deobfuscateparam.cfc b/core/src/wheels/tests/global/public/deobfuscateparam.cfc deleted file mode 100644 index 6389d126b7..0000000000 --- a/core/src/wheels/tests/global/public/deobfuscateparam.cfc +++ /dev/null @@ -1,47 +0,0 @@ -component extends="wheels.tests.Test" { - - function setup() { - results = {}; - } - - function test_deobfuscate_eb77359232() { - results.param = deobfuscateParam("eb77359232"); - assert("results.param IS '999999999'"); - } - - function test_deobfuscate_9b1c6() { - results.param = deobfuscateParam("9b1c6"); - assert("results.param IS 1"); - } - - function test_deobfuscate_ac10a() { - results.param = deobfuscateParam("ac10a"); - assert("results.param IS 99"); - } - - function test_deobfuscate_b226582() { - results.param = deobfuscateParam("b226582"); - assert("results.param IS 15765"); - } - - function test_deobfuscate_c06d44215() { - results.param = deobfuscateParam("c06d44215"); - assert("results.param IS 69247541"); - } - - function test_becca2515_should_not_deobfuscate() { - results.param = deobfuscateParam("becca2515"); - assert("results.param IS 'becca2515'"); - } - - function test_a15ba9_should_not_deobfuscate() { - results.param = deobfuscateParam("a15ba9"); - assert("results.param IS 'a15ba9'"); - } - - function test_1111111111_should_not_deobfuscate() { - results.param = deobfuscateParam("1111111111"); - assert("results.param IS '1111111111'"); - } - -} diff --git a/core/src/wheels/tests/global/public/humanize.cfc b/core/src/wheels/tests/global/public/humanize.cfc deleted file mode 100644 index bec34410aa..0000000000 --- a/core/src/wheels/tests/global/public/humanize.cfc +++ /dev/null @@ -1,49 +0,0 @@ -component extends="wheels.tests.Test" { - - function test_normal_variable() { - result = humanize("wheelsIsAFramework"); - assert("NOT Compare(result, 'Wheels Is A Framework')"); - } - - function test_unchanged() { - result = humanize("Website (SP Referral)"); - assert("NOT Compare(result, 'Website (SP Referral)')"); - result = humanize("Moto-Type"); - assert("NOT Compare(result, 'Moto-Type')"); - result = humanize("All AIMS users"); - assert("NOT Compare(result, 'All AIMS users')"); - result = humanize("FTRI staff"); - assert("NOT Compare(result, 'FTRI staff')"); - } - - function test_variable_starting_with_uppercase() { - result = humanize("WheelsIsAFramework"); - assert("NOT Compare(result, 'Wheels Is A Framework')"); - } - - function test_abbreviation() { - result = humanize("CFML"); - assert("NOT Compare(result, 'CFML')"); - } - - function test_abbreviation_as_exception() { - result = humanize(text = "ACfmlFramework", except = "CFML"); - assert("NOT Compare(result, 'A CFML Framework')"); - } - - function test_exception_within_string() { - result = humanize(text = "ACfmlFramecfmlwork", except = "CFML"); - assert("NOT Compare(result, 'A CFML Framecfmlwork')"); - } - - function test_abbreviation_without_exception_cannot_be_done() { - result = humanize("wheelsIsACFMLFramework"); - assert("NOT Compare(result, 'Wheels Is ACFML Framework')"); - } - - function test_issue_663() { - result = humanize("Some Input"); - assert("NOT Compare(result, 'Some Input')"); - } - -} diff --git a/core/src/wheels/tests/global/public/obfuscateparam.cfc b/core/src/wheels/tests/global/public/obfuscateparam.cfc deleted file mode 100644 index df870a4d8e..0000000000 --- a/core/src/wheels/tests/global/public/obfuscateparam.cfc +++ /dev/null @@ -1,53 +0,0 @@ -component extends="wheels.tests.Test" { - - function setup() { - results = {}; - } - - function test_obfuscate_999999999() { - results.param = obfuscateParam("999999999"); - assert("results.param IS 'eb77359232'"); - } - - function test_obfuscate_0162823571() { - results.param = obfuscateParam("0162823571"); - assert("results.param IS '0162823571'"); - } - - function test_obfuscate_1() { - results.param = obfuscateParam("1"); - assert("results.param IS '9b1c6'"); - } - - function test_obfuscate_99() { - results.param = obfuscateParam("99"); - assert("results.param IS 'ac10a'"); - } - - function test_obfuscate_15765() { - results.param = obfuscateParam("15765"); - assert("results.param IS 'b226582'"); - } - - function test_obfuscate_69247541() { - results.param = obfuscateParam("69247541"); - assert("results.param IS 'c06d44215'"); - } - - function test_obfuscate_0413() { - results.param = obfuscateParam("0413"); - assert("results.param IS '0413'"); - } - - function test_per_should_not_obfuscate() { - results.param = obfuscateParam("per"); - assert("results.param IS 'per'"); - } - - /* Lucee 6 does obfuscate this to 'a47ffffe32' - function test_1111111111_should_not_obfuscate() { - results.param = obfuscateParam("1111111111"); - assert("results.param IS '1111111111'"); - }*/ - -} \ No newline at end of file diff --git a/core/src/wheels/tests/global/public/processrequest.cfc b/core/src/wheels/tests/global/public/processrequest.cfc deleted file mode 100644 index cab7c504bb..0000000000 --- a/core/src/wheels/tests/global/public/processrequest.cfc +++ /dev/null @@ -1,79 +0,0 @@ -component extends="wheels.tests.Test" { - - function setup() { - StructDelete(request, "filterTestTypes"); - } - - function test_process_request() { - local.params = {action = "show", controller = "csrfProtectedExcept"}; - result = processRequest(local.params); - expected = "Show ran"; - assert("Find(expected, result)"); - } - - // this is not so easy to test... - /** - function test_process_request_using_route() { - local.params = {route = "wheelsInfo"}; - actual = processRequest(params = local.params, returnAs = "struct"); - assert("actual.controller eq 'wheels'"); - assert("actual.action eq 'wheels'"); - } - */ - - function test_process_request_return_as_struct() { - local.params = {action = "show", controller = "csrfProtectedExcept"}; - result = processRequest(params = local.params, returnAs = "struct").status; - expected = 200; - assert("Compare(expected, result) eq 0"); - } - - function test_process_request_as_get() { - local.params = {action = "actionGet", controller = "verifies"}; - result = processRequest(method = "get", params = local.params); - expected = "actionGet"; - assert("Find(expected, result)"); - } - - function test_process_request_as_post() { - local.params = {action = "actionPost", controller = "verifies"}; - result = processRequest(method = "post", params = local.params); - expected = "actionPost"; - assert("Find(expected, result)"); - } - - function test_processRequest_executes_filters() { - local.params = {controller = "Filtering", action = "noView"}; - response = processRequest(params = local.params); - actual = ArrayLen(request.filterTestTypes); - expected = 2; - assert("actual IS expected"); - } - - function test_processRequest_skips_all_filters() { - local.params = {controller = "Filtering", action = "noView"}; - response = processRequest(params = local.params, includeFilters = false); - actual = StructKeyExists(request, "filterTestTypes"); - expected = false; - assert("actual IS expected", "request.filterTestTypes"); - } - - function test_processRequest_only_runs_before_filters() { - local.params = {controller = "Filtering", action = "noView"}; - response = processRequest(params = local.params, includeFilters = "before"); - actual = request.filterTestTypes; - expected = "before"; - assert("ArrayLen(actual) IS 1"); - assert("actual[1] IS expected", "request.filterTestTypes"); - } - - function test_processRequest_only_runs_after_filters() { - local.params = {controller = "Filtering", action = "noView"}; - response = processRequest(params = local.params, includeFilters = "after"); - actual = request.filterTestTypes; - expected = "after"; - assert("ArrayLen(actual) IS 1"); - assert("actual[1] IS expected", "request.filterTestTypes"); - } - -} diff --git a/core/src/wheels/tests/global/strings.cfc b/core/src/wheels/tests/global/strings.cfc deleted file mode 100644 index be86d0f004..0000000000 --- a/core/src/wheels/tests/global/strings.cfc +++ /dev/null @@ -1,108 +0,0 @@ -component extends="wheels.tests.Test" { - - function test_user_setting() { - local.oldIrregulars = application.wheels.irregulars; - local.irregulars = local.oldIrregulars; - StructAppend(local.irregulars, {pac = "tupac"}); - application.wheels.irregulars = local.irregulars; - result1 = pluralize("pac"); - result2 = singularize("tupac"); - application.wheels.irregulars = local.oldIrregulars; - assert("NOT Compare(result1, 'tupac') AND NOT Compare(result2, 'pac')"); - } - - function test_singularize() { - result = singularize("statuses"); - assert("NOT Compare(result, 'status')"); - } - - function test_singularize_starting_with_upper_case() { - result = singularize("Instances"); - assert("NOT Compare(result, 'Instance')"); - } - - function test_singularize_two_words() { - result = singularize("statusUpdates"); - assert("NOT Compare(result, 'statusUpdate')"); - } - - function test_singularize_multiple_words() { - result = singularize("fancyChristmasTrees"); - assert("NOT Compare(result, 'fancyChristmasTree')"); - } - - function test_singularize_already_singularized_camel_case() { - result = singularize("camelCasedFailure"); - assert("NOT Compare(result, 'camelCasedFailure')"); - } - - function test_pluralize() { - result = pluralize("status"); - assert("NOT Compare(result, 'statuses')"); - } - - function test_pluralize_with_count() { - result = pluralize("statusUpdate", 0); - assert("NOT Compare(result, '0 statusUpdates')"); - result = pluralize("statusUpdate", 1); - assert("NOT Compare(result, '1 statusUpdate')"); - result = pluralize("statusUpdate", 2); - assert("NOT Compare(result, '2 statusUpdates')"); - } - - function test_pluralize_starting_with_upper_case() { - result = pluralize("Instance"); - assert("NOT Compare(result, 'Instances')"); - } - - function test_pluralize_two_words() { - result = pluralize("statusUpdate"); - assert("NOT Compare(result, 'statusUpdates')"); - } - - function test_pluralize_issue_450() { - result = pluralize("statuscode"); - assert("NOT Compare(result, 'statuscodes')"); - } - - function test_pluralize_multiple_words() { - result = pluralize("fancyChristmasTree"); - assert("NOT Compare(result, 'fancyChristmasTrees')"); - } - - function test_hyphenize_normal_variable() { - result = hyphenize("wheelsIsAFramework"); - assert("NOT Compare(result, 'wheels-is-a-framework')"); - } - - function test_hyphenize_variable_starting_with_uppercase() { - result = hyphenize("WheelsIsAFramework"); - debug('result', false); - assert("NOT Compare(result, 'wheels-is-a-framework')"); - } - - function test_hyphenize_variable_with_abbreviation() { - result = hyphenize("aURLVariable"); - debug('result', false); - assert("NOT Compare(result, 'a-url-variable')"); - } - - function test_hyphenize_variable_with_abbreviation_starting_with_uppercase() { - result = hyphenize("URLVariable"); - debug('result', false); - assert("NOT Compare(result, 'url-variable')"); - } - - function test_hyphenize_should_only_insert_hyphens_in_mixed_case() { - result = hyphenize("ERRORMESSAGE"); - assert("NOT Compare(result, 'errormessage')"); - result = hyphenize("errormessage"); - assert("NOT Compare(result, 'errormessage')"); - } - - function test_singularize_of_address() { - result = singularize("address"); - assert("NOT Compare(result, 'address')"); - } - -} diff --git a/core/src/wheels/tests/global/urlfor.cfc b/core/src/wheels/tests/global/urlfor.cfc deleted file mode 100644 index 104559c7f8..0000000000 --- a/core/src/wheels/tests/global/urlfor.cfc +++ /dev/null @@ -1,125 +0,0 @@ -component extends="wheels.tests.Test" { - - function setup() { - config = {path = "wheels", fileName = "Mapper", method = "$init"}; - _params = {controller = "test", action = "index"}; - _originalRoutes = Duplicate(application.wheels.routes); - _originalUrlRewriting = application.wheels.URLRewriting; - _originalObfuscateUrls = application.wheels.obfuscateUrls; - } - - public struct function $mapper() { - local.args = Duplicate(config); - StructAppend(local.args, arguments, true); - return $createObjectFromRoot(argumentCollection = local.args); - } - - public void function teardown() { - application.wheels.routes = _originalRoutes; - application.wheels.URLRewriting = _originalUrlRewriting; - application.wheels.obfuscateUrls = _originalObfuscateUrls; - } - - public void function $clearRoutes() { - application.wheels.routes = []; - } - - function test_issue_455() { - mapper = $mapper(); - mapper - .$draw() - .$match(name = "user_2", pattern = "user/[user_id]/[controller]/[action]") - .end(); - $setNamedRoutePositions(); - application.wheels.URLRewriting = "Off"; - application.wheels.obfuscateUrls = true; - r = urlFor( - route = "user_2", - user_id = "5559", - controller = "SurveyTemplates", - action = "index" - ); - assert('r contains "b24dae"'); - } - - function test_links_are_properly_hyphenated() { - mapper = $mapper(); - mapper - .$draw() - .$match(name = "user_2", pattern = "user/[user_id]/[controller]/[action]") - .end(); - $setNamedRoutePositions(); - application.wheels.URLRewriting = "On"; - e = "/user/5559/survey-templates/index"; - r = urlFor( - route = "user_2", - user_id = "5559", - controller = "SurveyTemplates", - action = "index" - ); - assert('r contains e'); - } - - function test_format_properly_add_with_route() { - mapper = $mapper(); - mapper - .$draw() - .$match(name = "user_2", pattern = "user/[user_id]/[controller]/[action].[format]") - .end(); - $setNamedRoutePositions(); - application.wheels.URLRewriting = "On"; - e = "/user/5559/survey-templates/index.csv"; - r = urlFor( - route = "user_2", - user_id = "5559", - controller = "SurveyTemplates", - action = "index", - format = "csv" - ); - assert('r contains e'); - } - - function test_using_onlypath_correctly_detects_https() { - mapper = $mapper(); - mapper - .$draw() - .$match(name = "user_2", pattern = "user/[user_id]/[controller]/[action].[format]") - .end(); - $setNamedRoutePositions(); - request.cgi.server_protocol = ""; - request.cgi.server_port_secure = 1; - r = urlFor( - route = "user_2", - user_id = "5559", - controller = "SurveyTemplates", - action = "index", - format = "csv", - onlyPath = false - ); - assert('left(r, 5) eq "https"'); - } - - function test_Issues_1046_no_route_argument() { - mapper = $mapper(); - mapper - .$draw() - .wildcard(mapKey = true) - .end(); - $setNamedRoutePositions(); - r1 = urlFor( - controller = "Example" - ); - r2 = urlFor( - controller = "Example", - action = "MyAction" - ); - r3 = urlFor( - controller = "Example", - action = "MyAction", - key = 123 - ); - assert("r1 EQ '/example/index'"); - assert("r2 EQ '/example/my-action'"); - assert("r3 EQ '/example/my-action/123'"); - } -} diff --git a/core/src/wheels/tests/internal/model/validations/ValidatesLengthOf.cfc b/core/src/wheels/tests/internal/model/validations/ValidatesLengthOf.cfc deleted file mode 100644 index cdad81c7bb..0000000000 --- a/core/src/wheels/tests/internal/model/validations/ValidatesLengthOf.cfc +++ /dev/null @@ -1,65 +0,0 @@ -component extends="wheels.tests.Test" { - - function setup() { - args = { - property = "lastName", - message = "[property] is the wrong length", - exactly = 0, - maximum = 0, - minimum = 0, - within = "" - }; - object = model("user").new(); - } - - function test_Maximum_good() { - object.lastName = "LessThan"; - object.$validatesLengthOf(argumentCollection = args, maximum = 10); - assert("!object.hasErrors()"); - } - - function test_Maximum_bad() { - object.lastName = "SomethingMoreThanTenLetters"; - object.$validatesLengthOf(argumentCollection = args, maximum = 10); - assert("object.hasErrors()"); - } - - function test_Minimum_good() { - object.lastName = "SomethingMoreThanTenLetters"; - object.$validatesLengthOf(argumentCollection = args, minimum = 10); - assert("!object.hasErrors()"); - } - - function test_Minimum_bad() { - object.lastName = "LessThan"; - object.$validatesLengthOf(argumentCollection = args, minimum = 10); - assert("object.hasErrors()"); - } - - function test_Within_good() { - object.lastName = "6Chars"; - within = [4, 8]; - object.$validatesLengthOf(argumentCollection = args, within = within); - assert("!object.hasErrors()"); - } - - function test_Within_bad() { - object.lastName = "6Chars"; - within = [2, 5]; - object.$validatesLengthOf(argumentCollection = args, within = within); - assert("object.hasErrors()"); - } - - function test_Exactly_good() { - object.lastName = "Exactly14Chars"; - object.$validatesLengthOf(argumentCollection = args, exactly = 14); - assert("!object.hasErrors()"); - } - - function test_Exactly_bad() { - object.lastName = "Exactly14Chars"; - object.$validatesLengthOf(argumentCollection = args, exactly = 99); - assert("object.hasErrors()"); - } - -} diff --git a/core/src/wheels/tests/mapper/initialization.cfc b/core/src/wheels/tests/mapper/initialization.cfc deleted file mode 100644 index 7d384b0a0a..0000000000 --- a/core/src/wheels/tests/mapper/initialization.cfc +++ /dev/null @@ -1,48 +0,0 @@ -component extends="wheels.tests.Test" { - - public void function setup() { - config = {path = "wheels", fileName = "Mapper", method = "$init"}; - _params = {controller = "test", action = "index"}; - _originalRoutes = Duplicate(application.wheels.routes); - } - - public void function teardown() { - application.wheels.routes = _originalRoutes; - } - - public struct function $mapper() { - local.args = Duplicate(config); - StructAppend(local.args, arguments, true); - return $createObjectFromRoot(argumentCollection = local.args); - } - - public struct function $inspect() { - return variables; - } - - public void function $clearRoutes() { - application.wheels.routes = []; - } - - function test_mapper_init_defaults() { - mapper = $mapper(); - mapper.inspect = $inspect; - mapperVarScope = mapper.inspect(); - assert('mapperVarScope.restful eq true AND mapperVarScope.methods eq true'); - } - - function test_mapper_init_restful_false() { - mapper = $mapper(restful = false); - mapper.inspect = $inspect; - mapperVarScope = mapper.inspect(); - assert('mapperVarScope.restful eq false AND mapperVarScope.methods eq false'); - } - - function test_mapper_init_restful_false_methods_true() { - mapper = $mapper(restful = false, methods = true); - mapper.inspect = $inspect; - mapperVarScope = mapper.inspect(); - assert('mapperVarScope.restful eq false AND mapperVarScope.methods eq true'); - } - -} diff --git a/core/src/wheels/tests/mapper/mapping.cfc b/core/src/wheels/tests/mapper/mapping.cfc deleted file mode 100644 index 291d4e86f0..0000000000 --- a/core/src/wheels/tests/mapper/mapping.cfc +++ /dev/null @@ -1,76 +0,0 @@ -component extends="wheels.tests.Test" { - - public void function setup() { - config = {path = "wheels", fileName = "Mapper", method = "$init"}; - _params = {controller = "test", action = "index"}; - _originalRoutes = Duplicate(application.wheels.routes); - } - - public void function teardown() { - application.wheels.routes = _originalRoutes; - } - - public struct function $mapper() { - local.args = Duplicate(config); - StructAppend(local.args, arguments, true); - return $createObjectFromRoot(argumentCollection = local.args); - } - - public struct function $inspect() { - return variables; - } - - public void function $clearRoutes() { - application.wheels.routes = []; - } - - // draw - - function test_draw_defaults() { - mapper = $mapper().$draw(); - mapper.inspect = $inspect; - mapperVarScope = mapper.inspect(); - assert('mapperVarScope.restful eq true AND mapperVarScope.methods eq true'); - } - - function test_draw_restful_false() { - mapper = $mapper(restful = false).$draw(restful = false); - mapper.inspect = $inspect; - mapperVarScope = mapper.inspect(); - assert('mapperVarScope.restful eq false AND mapperVarScope.methods eq false'); - } - - function test_draw_restful_false_methods_true() { - mapper = $mapper(restful = false, methods = true).$draw(restful = false, methods = true); - mapper.inspect = $inspect; - mapperVarScope = mapper.inspect(); - assert('mapperVarScope.restful eq false AND mapperVarScope.methods eq true'); - } - - function test_draw_mapFormat_false() { - mapper = $mapper(mapFormat = false).$draw(); - mapper.inspect = $inspect; - mapperVarScope = mapper.inspect(); - assert('mapperVarScope.mapFormat eq false'); - } - - function test_draw_resets_the_stack() { - mapper = $mapper().$draw(); - mapper.inspect = $inspect; - mapperVarScope = mapper.inspect(); - stackLen = ArrayLen(mapperVarScope.scopeStack); - assert('stackLen eq 1'); - } - - function test_end_removes_top_of_stack() { - mapper = $mapper().$draw(); - mapper.inspect = $inspect; - drawVarScope = mapper.inspect(); - drawLen = ArrayLen(drawVarScope.scopeStack); - mapper.end(); - endVarScope = mapper.inspect(); - endLen = ArrayLen(endVarScope.scopeStack); - assert('drawLen eq 1 and endLen eq 0'); - } - -} diff --git a/core/src/wheels/tests/mapper/matching.cfc b/core/src/wheels/tests/mapper/matching.cfc deleted file mode 100644 index 8aee148c1d..0000000000 --- a/core/src/wheels/tests/mapper/matching.cfc +++ /dev/null @@ -1,202 +0,0 @@ -component extends="wheels.tests.Test" { - - public void function setup() { - config = {path = "wheels", fileName = "Mapper", method = "$init"}; - _params = {controller = "test", action = "index"}; - _originalRoutes = Duplicate(application.wheels.routes); - } - - public void function teardown() { - application.wheels.routes = _originalRoutes; - } - - public struct function $mapper() { - local.args = Duplicate(config); - StructAppend(local.args, arguments, true); - return $createObjectFromRoot(argumentCollection = local.args); - } - - public struct function $inspect() { - return variables; - } - - public void function $clearRoutes() { - application.wheels.routes = []; - } - - // match - - function test_match_with_basic_arguments() { - $clearRoutes(); - mapper = $mapper(); - mapper - .$draw() - .$match( - name = "signIn", - method = "get", - controller = "sessions", - action = "new" - ) - .end(); - routesLen = ArrayLen(application.wheels.routes); - assert("routesLen eq 1"); - route = application.wheels.routes[1]; - assert('route.controller eq "sessions" and route.action eq "new"'); - } - - function test_match_with_to_argument() { - $clearRoutes(); - mapper = $mapper(); - mapper - .$draw() - .$match(name = "signIn", method = "get", to = "sessions##new") - .end(); - routesLen = ArrayLen(application.wheels.routes); - assert("routesLen eq 1"); - route = application.wheels.routes[1]; - assert('route.controller eq "sessions" and route.action eq "new"'); - } - - function test_match_without_name() { - $clearRoutes(); - mapper = $mapper(); - mapper - .$draw() - .$match(pattern = "/sign-in", method = "get", to = "sessions##new") - .end(); - routesLen = ArrayLen(application.wheels.routes); - assert("routesLen eq 1"); - route = application.wheels.routes[1]; - nameExists = StructKeyExists(route, "name"); - assert('not nameExists and route.controller eq "sessions" and route.action eq "new"'); - } - - function test_match_raises_error_without_name_or_pattern() { - $clearRoutes(); - mapper = $mapper().$draw(); - e = raised('mapper.$match(method="get", controller="sessions", action="new")'); - assert('e eq "Wheels.MapperArgumentMissing"'); - } - - function test_match_raises_error_with_invalid_route() { - $clearRoutes(); - mapper = $mapper().$draw(); - e = raised('mapper.$match(pattern="[controller](/[action])(/[key])")'); - assert('e eq "Wheels.InvalidRoute"'); - } - - function test_match_with_basic_arguments_and_controller_scoped() { - $clearRoutes(); - mapper = $mapper(); - mapper - .$draw() - .controller("sessions") - .$match(name = "signIn", method = "get", action = "new") - .end() - .end(); - routesLen = ArrayLen(application.wheels.routes); - assert("routesLen eq 1"); - route = application.wheels.routes[1]; - assert('route.controller eq "sessions" and route.action eq "new"'); - } - - function test_match_with_basic_arguments_and_package_scoped() { - $clearRoutes(); - mapper = $mapper(); - mapper - .$draw() - .namespace("admin") - .$match( - name = "signIn", - method = "get", - action = "new", - controller = "sessions" - ) - .end() - .end(); - routesLen = ArrayLen(application.wheels.routes); - assert("routesLen eq 1"); - route = application.wheels.routes[1]; - assert('route.controller eq "admin.Sessions" and route.action eq "new"'); - } - - function test_match_with_package_scope_and_controller_scope() { - $clearRoutes(); - mapper = $mapper(); - mapper - .$draw() - .namespace("admin") - .controller("sessions") - .$match(name = "signIn", method = "get", action = "new") - .end() - .end() - .end(); - routesLen = ArrayLen(application.wheels.routes); - assert("routesLen eq 1"); - route = application.wheels.routes[1]; - assert('route.controller eq "admin.Sessions" and route.action eq "new"'); - } - - function test_match_after_disabling_methods() { - $clearRoutes(); - mapper = $mapper(); - mapper - .$draw(restful = false, methods = false) - .$match(pattern = "/sign-in", method = "get", to = "sessions##new") - .end(); - routesLen = ArrayLen(application.wheels.routes); - assert("routesLen eq 1"); - route = application.wheels.routes[1]; - methodExists = StructKeyExists(route, "method"); - assert('methodExists eq false and route.controller eq "sessions" and route.action eq "new"'); - } - - function test_match_with_single_optional_pattern_segment() { - $clearRoutes(); - mapper = $mapper(); - mapper - .$draw() - .$match(pattern = "/sign-in(.[format])", method = "get", to = "sessions##new") - .end(); - routesLen = ArrayLen(application.wheels.routes); - assert("routesLen eq 2"); - } - - function test_match_with_multiple_optional_pattern_segment() { - $clearRoutes(); - mapper = $mapper(); - mapper - .$draw() - .$match(pattern = "/[controller](/[action](/[key](.[format])))", action = "index", method = "get") - .end(); - routesLen = ArrayLen(application.wheels.routes); - assert("routesLen eq 4"); - } - - function test_match_with_globing() { - $clearRoutes(); - mapper = $mapper(); - mapper - .$draw() - .$match(name = "profile", pattern = "profiles/*[userseo]/[userid]", to = "profile##show") - .end(); - routesLen = ArrayLen(application.wheels.routes); - assert("routesLen eq 1"); - route = application.wheels.routes[1]; - assert('REFindNoCase(route.regex, "profiles/this/is/some/seo/text/id123")'); - } - - function test_match_with_multiple_globs() { - $clearRoutes(); - mapper = $mapper(); - mapper - .$draw() - .$match(name = "profile", pattern = "*[before]/foo/*[after]", to = "profile##show") - .end(); - routesLen = ArrayLen(application.wheels.routes); - assert("routesLen eq 1"); - route = application.wheels.routes[1]; - assert('REFindNoCase(route.regex, "this/is/before/foo/this/is/after")'); - } - -} diff --git a/core/src/wheels/tests/mapper/package.cfc b/core/src/wheels/tests/mapper/package.cfc deleted file mode 100644 index 251895de46..0000000000 --- a/core/src/wheels/tests/mapper/package.cfc +++ /dev/null @@ -1,50 +0,0 @@ -component extends="wheels.tests.Test" { - - function setup() { - config = {path = "wheels", fileName = "Mapper", method = "$init"}; - - _params = {controller = "test", action = "index"}; - _originalRoutes = Duplicate(application.wheels.routes); - - $clearRoutes(); - } - - function teardown() { - application.wheels.routes = _originalRoutes; - } - - function test_generates_url_pattern_without_name() { - $mapper() - .$draw() - .package("public") - .root(to = "pages##home") - .end() - .end(); - - assert("application.wheels.routes[1].pattern is '/.[format]'"); - assert("application.wheels.routes[2].pattern is '/'"); - } - - function test_scopes_controller_to_subfolder() { - $mapper() - .$draw() - .package("public") - .root(to = "pages##home") - .end() - .end(); - - assert("application.wheels.routes[1].controller is 'public.pages'"); - assert("application.wheels.routes[2].controller is 'public.pages'"); - } - - private function $clearRoutes() { - application.wheels.routes = []; - } - - private struct function $mapper() { - local.args = Duplicate(config); - StructAppend(local.args, arguments, true); - return $createObjectFromRoot(argumentCollection = local.args); - } - -} diff --git a/core/src/wheels/tests/mapper/redirect.cfc b/core/src/wheels/tests/mapper/redirect.cfc deleted file mode 100644 index da7528e171..0000000000 --- a/core/src/wheels/tests/mapper/redirect.cfc +++ /dev/null @@ -1,43 +0,0 @@ -component extends="wheels.tests.Test" { - - function setup() { - config = {path = "wheels", fileName = "Mapper", method = "$init"}; - - _params = {controller = "test", action = "index"}; - _originalRoutes = Duplicate(application.wheels.routes); - - $clearRoutes(); - } - - function teardown() { - application.wheels.routes = _originalRoutes; - } - - function test_redirect_argument_is_passed_through() { - $mapper() - .$draw() - .get(name = "testredirect1", redirect = "https://www.google.com") - .post(name = "testredirect2", redirect = "https://www.google.com") - .put(name = "testredirect3", redirect = "https://www.google.com") - .patch(name = "testredirect4", redirect = "https://www.google.com") - .delete(name = "testredirect5", redirect = "https://www.google.com") - .end(); - assert("structKeyExists(application.wheels.routes[1], 'redirect')"); - assert("structKeyExists(application.wheels.routes[2], 'redirect')"); - assert("structKeyExists(application.wheels.routes[3], 'redirect')"); - assert("structKeyExists(application.wheels.routes[4], 'redirect')"); - assert("structKeyExists(application.wheels.routes[5], 'redirect')"); - } - - - private function $clearRoutes() { - application.wheels.routes = []; - } - - private struct function $mapper() { - local.args = Duplicate(config); - StructAppend(local.args, arguments, true); - return $createObjectFromRoot(argumentCollection = local.args); - } - -} diff --git a/core/src/wheels/tests/mapper/resources.cfc b/core/src/wheels/tests/mapper/resources.cfc deleted file mode 100644 index cc4dd10660..0000000000 --- a/core/src/wheels/tests/mapper/resources.cfc +++ /dev/null @@ -1,133 +0,0 @@ -component extends="wheels.tests.Test" { - - public void function setup() { - config = {path = "wheels", fileName = "Mapper", method = "$init"}; - _params = {controller = "test", action = "index"}; - _originalRoutes = Duplicate(application.wheels.routes); - } - - public void function teardown() { - application.wheels.routes = _originalRoutes; - } - - public struct function $mapper() { - local.args = Duplicate(config); - StructAppend(local.args, arguments, true); - return $createObjectFromRoot(argumentCollection = local.args); - } - - public struct function $inspect() { - return variables; - } - - public void function $clearRoutes() { - application.wheels.routes = []; - } - - // resource - - function test_resource_produces_routes() { - $clearRoutes(); - mapper = $mapper(); - mapper - .$draw() - .resource(name = "pigeon") - .end(); - routesLen = ArrayLen(application.wheels.routes); - assert("routesLen eq 14"); - } - - function test_resource_produces_routes_with_list() { - $clearRoutes(); - mapper = $mapper(); - mapper - .$draw() - .resource(name = "pigeon,pudding") - .end(); - routesLen = ArrayLen(application.wheels.routes); - assert("routesLen eq 28"); - } - - function test_resource_raises_error_with_list_and_nesting() { - $clearRoutes(); - mapper = $mapper().$draw(); - e = raised('mapper.resource(name="pigeon,pudding", nested=true).end()'); - assert('e eq "Wheels.InvalidResource"'); - } - - // resources - - function test_resources_produces_routes() { - $clearRoutes(); - mapper = $mapper(); - mapper - .$draw() - .resources(name = "pigeons") - .end(); - routesLen = ArrayLen(application.wheels.routes); - assert("routesLen eq 16"); - } - - function test_resources_produces_routes_without_format() { - $clearRoutes(); - mapper = $mapper(); - mapper - .$draw() - .resources(name = "pigeons", mapFormat = false) - .end(); - routesLen = ArrayLen(application.wheels.routes); - assert("routesLen eq 8"); - } - - function test_resources_produces_routes_without_format_set_by_mapper() { - $clearRoutes(); - mapper = $mapper(mapFormat = false); - mapper - .$draw() - .resources(name = "pigeons") - .end(); - routesLen = ArrayLen(application.wheels.routes); - assert("routesLen eq 8"); - } - - function test_resources_produces_routes_without_format_set_by_mapper_override() { - $clearRoutes(); - mapper = $mapper(mapFormat = false); - mapper - .$draw() - .resources(name = "pigeons", mapFormat = true) - .end(); - routesLen = ArrayLen(application.wheels.routes); - assert("routesLen eq 16"); - } - - function test_resources_produces_routes_with_list() { - $clearRoutes(); - mapper = $mapper(); - mapper - .$draw() - .resources(name = "pigeons,birds") - .end(); - routesLen = ArrayLen(application.wheels.routes); - assert("routesLen eq 32"); - } - - function test_resources_produces_routes_with_list_without_format() { - $clearRoutes(); - mapper = $mapper(); - mapper - .$draw() - .resources(name = "pigeons,birds", mapFormat = false) - .end(); - routesLen = ArrayLen(application.wheels.routes); - assert("routesLen eq 16"); - } - - function test_resources_raises_error_with_list_and_nesting() { - $clearRoutes(); - mapper = $mapper().$draw(); - e = raised('mapper.resources(name="pigeon,birds", nested=true).end()'); - assert('e eq "Wheels.InvalidResource"'); - } - -} diff --git a/core/src/wheels/tests/mapper/root.cfc b/core/src/wheels/tests/mapper/root.cfc deleted file mode 100644 index ad603c4721..0000000000 --- a/core/src/wheels/tests/mapper/root.cfc +++ /dev/null @@ -1,52 +0,0 @@ -component extends="wheels.tests.Test" { - - public void function setup() { - config = {path = "wheels", fileName = "Mapper", method = "$init"}; - - _params = {controller = "test", action = "index"}; - _originalRoutes = Duplicate(application.wheels.routes); - - $clearRoutes(); - } - - public void function teardown() { - application.wheels.routes = _originalRoutes; - } - - public struct function $mapper() { - local.args = Duplicate(config); - StructAppend(local.args, arguments, true); - return $createObjectFromRoot(argumentCollection = local.args); - } - - public void function $clearRoutes() { - application.wheels.routes = []; - } - - function test_top_level_root_excludes_format_but_keeps_it_on_sub_level() { - $mapper() - .$draw() - .namespace("test") - .root(to = "test##test") - .end() - .root(to = "test##test") - .end(); - assert("application.wheels.routes[1].pattern is '/test.[format]'"); - assert("application.wheels.routes[2].pattern is '/test'"); - assert("application.wheels.routes[3].pattern is '/'"); - } - - function test_overriding_default_for_map_format() { - $mapper() - .$draw() - .namespace("test") - .root(to = "test##test", mapFormat = false) - .end() - .root(to = "test##test", mapFormat = true) - .end(); - assert("application.wheels.routes[1].pattern is '/test'"); - assert("application.wheels.routes[2].pattern is '/.[format]'"); - assert("application.wheels.routes[3].pattern is '/'"); - } - -} diff --git a/core/src/wheels/tests/mapper/utilities.cfc b/core/src/wheels/tests/mapper/utilities.cfc deleted file mode 100644 index 81f63cabcf..0000000000 --- a/core/src/wheels/tests/mapper/utilities.cfc +++ /dev/null @@ -1,147 +0,0 @@ -component extends="wheels.tests.Test" { - - public void function setup() { - config = {path = "wheels", fileName = "Mapper", method = "$init"}; - _params = {controller = "test", action = "index"}; - _originalRoutes = Duplicate(application.wheels.routes); - } - - public boolean function validateRegexPattern(required string pattern) { - try { - local.jPattern = CreateObject("java", "java.util.regex.Pattern").compile(arguments.pattern); - } catch (any e) { - return false; - } - - return true; - } - - public void function teardown() { - application.wheels.routes = _originalRoutes; - } - - public struct function $mapper() { - local.args = Duplicate(config); - StructAppend(local.args, arguments, true); - return $createObjectFromRoot(argumentCollection = local.args); - } - - public struct function $inspect() { - return variables; - } - - public void function $clearRoutes() { - application.wheels.routes = []; - } - - // compileRegex - - function test_regex_compiles_successfully() { - mapper = $mapper(); - pattern = "[controller]/[action]/[key]"; - regex = mapper.$patternToRegex(pattern = pattern); - output = mapper.$compileRegex(regex = regex, pattern = pattern); - assert('!structKeyExists(variables, "output")'); - } - - function test_regex_compiles_with_error() { - mapper = $mapper(); - pattern = "[controller]/[action]/[key]"; - e = raised('mapper.$compileRegex(regex="*", pattern="*")'); - assert('e eq "Wheels.InvalidRegex"'); - } - - // normalizePattern - - function test_normalizePattern_no_starting_slash() { - mapper = $mapper(); - urlString = "controller/action"; - newString = mapper.$normalizePattern(urlString); - assert('newString eq "/controller/action"'); - } - - function test_normalizePattern_double_slash_and_no_starting_slash() { - mapper = $mapper(); - urlString = "controller//action"; - newString = mapper.$normalizePattern(urlString); - assert('newString eq "/controller/action"'); - } - - function test_normalizePattern_ending_slash_no_starting_slash() { - mapper = $mapper(); - urlString = "controller/action/"; - newString = mapper.$normalizePattern(urlString); - assert('newString eq "/controller/action"'); - } - - function test_normalizePattern_slashes_everywhere_with_format() { - mapper = $mapper(); - urlString = "////controller///action///.asdf/////"; - newString = mapper.$normalizePattern(urlString); - assert('newString eq "/controller/action.asdf"'); - } - - function test_normalizePattern_with_single_quote_in_pattern() { - mapper = $mapper(); - urlString = "////controller///action///.asdf'"; - newString = mapper.$normalizePattern(urlString); - assert('newString eq "/controller/action.asdf''"'); - } - - // patternToRegex - - function test_patternToRegex_root() { - mapper = $mapper(); - pattern = "/"; - regex = mapper.$patternToRegex(pattern); - assert('regex eq "^\/?$"'); - assert('validateRegexPattern(regex)'); - } - - function test_patternToRegex_root_with_format() { - mapper = $mapper(); - pattern = "/.[format]"; - regex = mapper.$patternToRegex(pattern); - assert('regex eq "^\.(\w+)\/?$"'); - assert('validateRegexPattern(regex)'); - } - - function test_patternToRegex_with_basic_catch_all_pattern() { - mapper = $mapper(); - pattern = "/[controller]/[action]/[key].[format]"; - regex = mapper.$patternToRegex(pattern); - assert('regex eq "^([^\/]+)\/([^\.\/]+)\/([^\.\/]+)\.(\w+)\/?$"'); - assert('validateRegexPattern(regex)'); - } - - // stripRouteVariables - - function test_stripRouteVariables_no_variables() { - mapper = $mapper(); - pattern = "/"; - varList = mapper.$stripRouteVariables(pattern); - assert('varList eq ""'); - } - - function test_stripRouteVariables_root_with_format() { - mapper = $mapper(); - pattern = "/.[format]"; - varList = mapper.$stripRouteVariables(pattern); - assert('varList eq "format"'); - } - - function test_stripRouteVariables_with_basic_catch_all_pattern() { - mapper = $mapper(); - pattern = "/[controller]/[action]/[key].[format]"; - varList = mapper.$stripRouteVariables(pattern); - assert('varList eq "controller,action,key,format"'); - } - - function test_stripRouteVariables_with_nested_restful_route() { - mapper = $mapper(); - pattern = "/posts(/[id](/comments(/[commentid](.[format]))))"; - varList = mapper.$stripRouteVariables(pattern); - assert('varList eq "id,commentid,format"'); - } - -} diff --git a/core/src/wheels/tests/mapper/wildcard.cfc b/core/src/wheels/tests/mapper/wildcard.cfc deleted file mode 100644 index cac9df00d3..0000000000 --- a/core/src/wheels/tests/mapper/wildcard.cfc +++ /dev/null @@ -1,158 +0,0 @@ -component extends="wheels.tests.Test" { - - public void function setup() { - config = {path = "wheels", fileName = "Mapper", method = "$init"}; - - _params = {controller = "test", action = "index"}; - _originalRoutes = Duplicate(application.wheels.routes); - - $clearRoutes(); - } - - public void function teardown() { - application.wheels.routes = _originalRoutes; - } - - public struct function $mapper() { - local.args = Duplicate(config); - StructAppend(local.args, arguments, true); - return $createObjectFromRoot(argumentCollection = local.args); - } - - public void function $clearRoutes() { - application.wheels.routes = []; - } - - function test_default_wildcard_produces_routes() { - $mapper() - .$draw() - .wildcard() - .end(); - - routesLen = ArrayLen(application.wheels.routes); - assert("routesLen eq 2"); - } - - function test_default_wildcard_only_allows_get_requests() { - $mapper() - .$draw() - .wildcard() - .end(); - - for (loc.route in application.wheels.routes) { - assert("loc.route.methods is 'get'"); - } - } - - function test_default_wildcard_generates_correct_patterns() { - $mapper() - .$draw() - .wildcard() - .end(); - - assert("application.wheels.routes[1].pattern is '/[controller]/[action]'"); - assert("application.wheels.routes[2].pattern is '/[controller]'"); - } - - function test_wildcard_with_methods_produces_routes() { - $mapper() - .$draw() - .wildcard(methods = "get,post") - .end(); - - routesLen = ArrayLen(application.wheels.routes); - assert("routesLen eq 4"); - } - - function test_wildcard_only_allows_specified_methods() { - $mapper() - .$draw() - .wildcard() - .end(); - - for (loc.route in application.wheels.routes) { - assert("loc.route.methods is 'get' or loc.route.methods is 'post'"); - } - } - - function test_wildcard_with_methods_generates_correct_patterns() { - $mapper() - .$draw() - .wildcard(methods = "get,post") - .end(); - - assert("application.wheels.routes[1].pattern is '/[controller]/[action]'"); - assert("application.wheels.routes[2].pattern is '/[controller]'"); - assert("application.wheels.routes[3].pattern is '/[controller]/[action]'"); - assert("application.wheels.routes[4].pattern is '/[controller]'"); - } - - function test_controller_scoped_wildcard_produces_routes() { - $mapper() - .$draw() - .controller("cats") - .wildcard() - .end() - .end(); - - routesLen = ArrayLen(application.wheels.routes); - assert("routesLen eq 2"); - } - - function test_controller_scoped_wildcard_only_allows_get_requests() { - $mapper() - .$draw() - .controller("cats") - .wildcard() - .end() - .end(); - - for (loc.route in application.wheels.routes) { - assert("loc.route.methods is 'get'"); - } - } - - function test_controller_scoped_wildcard_generates_correct_patterns() { - $mapper() - .$draw() - .controller("cats") - .wildcard() - .end() - .end(); - - assert("application.wheels.routes[1].pattern is '/cats/[action]'"); - assert("application.wheels.routes[2].pattern is '/cats'"); - } - - function test_wildcard_with_map_format() { - $mapper() - .$draw() - .controller("cats") - .wildcard(mapFormat = true) - .end() - .end(); - - assert("application.wheels.routes[1].pattern is '/cats/[action].[format]'"); - assert("application.wheels.routes[2].pattern is '/cats/[action]'"); - assert("application.wheels.routes[3].pattern is '/cats.[format]'"); - assert("application.wheels.routes[4].pattern is '/cats'"); - routesLen = ArrayLen(application.wheels.routes); - assert("routesLen eq 4"); - } - - function test_wildcard_with_map_key() { - $mapper() - .$draw() - .controller("cats") - .wildcard(mapKey = true) - .end() - .end(); - - assert("application.wheels.routes[1].pattern is '/cats/[action]/[key]'"); - assert("application.wheels.routes[2].pattern is '/cats/[action]'"); - assert("application.wheels.routes[3].pattern is '/cats'"); - routesLen = ArrayLen(application.wheels.routes); - assert("routesLen eq 3"); - } - -} diff --git a/core/src/wheels/tests/migrator/migration/addbiginteger.cfc b/core/src/wheels/tests/migrator/migration/addbiginteger.cfc deleted file mode 100644 index 18313ee79f..0000000000 --- a/core/src/wheels/tests/migrator/migration/addbiginteger.cfc +++ /dev/null @@ -1,74 +0,0 @@ -component extends="wheels.tests.Test" { - - private boolean function isDbCompatible() { - switch (migration.adapter.adapterName()) { - case "H2": - case "MySQL": - return true; - default: - return false; - } - } - - private string function getBigIntegerType() { - switch (migration.adapter.adapterName()) { - case "H2": - return "BIGINT"; - case "MySQL": - return "BIGINT UNSIGNED"; - default: - return "`addbiginteger()` not supported for " & migration.adapter.adapterName(); - } - } - - function setup() { - migration = CreateObject("component", "wheels.migrator.Migration").init(); - originalMigratorObjectCase = Duplicate(application.wheels.migratorObjectCase); - } - - function teardown() { - application.wheels.migratorObjectCase = originalMigratorObjectCase; - } - - function test_add_a_big_integer_column() { - if (!isDbCompatible()) { - return; - } - - tableName = "dbm_add_big_integer_tests"; - columnName = "bigIntegerCOLUMN"; - t = migration.createTable(name = tableName, force = true); - t.bigInteger(columnName = columnName); - t.create(); - - info = $dbinfo(datasource = application.wheels.dataSourceName, table = tableName, type = "columns"); - actual = ListToArray(ValueList(info.TYPE_NAME))[2]; - migration.dropTable(tableName); - - expected = getBigIntegerType(); - - assert("actual eq expected"); - } - - function test_add_multiple_big_integer_columns() { - if (!isDbCompatible()) { - return; - } - - tableName = "dbm_add_big_integer_tests"; - columnNames = "bigIntegerA,bigIntegerB"; - t = migration.createTable(name = tableName, force = true); - t.bigInteger(columnNames = columnNames); - t.create(); - - info = $dbinfo(datasource = application.wheels.dataSourceName, table = tableName, type = "columns"); - actual = ListToArray(ValueList(info.TYPE_NAME)); - migration.dropTable(tableName); - - expected = getBigIntegerType(); - - assert("actual[2] eq expected"); - assert("actual[3] eq expected"); - } - -} \ No newline at end of file diff --git a/core/src/wheels/tests/migrator/migration/addbinary.cfc b/core/src/wheels/tests/migrator/migration/addbinary.cfc deleted file mode 100644 index c37f132616..0000000000 --- a/core/src/wheels/tests/migrator/migration/addbinary.cfc +++ /dev/null @@ -1,79 +0,0 @@ -component extends="wheels.tests.Test" { - - private boolean function isDbCompatible() { - switch (migration.adapter.adapterName()) { - case "H2": - case "MicrosoftSQLServer": - case "MySQL": - case "PostgreSQL": - return true; - default: - return false; - } - } - - private string function getBinaryType() { - switch (migration.adapter.adapterName()) { - case "H2": - case "MySQL": - return "BLOB"; - case "MicrosoftSQLServer": - return "IMAGE"; - case "PostgreSQL": - return "BYTEA"; - default: - return "`addbinary()` not supported for " & migration.adapter.adapterName(); - } - } - - function setup() { - migration = CreateObject("component", "wheels.migrator.Migration").init(); - originalMigratorObjectCase = Duplicate(application.wheels.migratorObjectCase); - } - - function teardown() { - application.wheels.migratorObjectCase = originalMigratorObjectCase; - } - - function test_add_a_binary_column() { - if (!isDbCompatible()) { - return; - } - - tableName = "dbm_add_binary_tests"; - columnName = "binaryCOLUMN"; - t = migration.createTable(name = tableName, force = true); - t.binary(columnName = columnName); - t.create(); - - info = $dbinfo(datasource = application.wheels.dataSourceName, table = tableName, type = "columns"); - actual = ListToArray(ValueList(info.TYPE_NAME))[2]; - migration.dropTable(tableName); - - expected = getBinaryType(); - - assert("actual eq expected"); - } - - function test_add_multiple_binary_columns() { - if (!isDbCompatible()) { - return; - } - - tableName = "dbm_add_binary_tests"; - columnNames = "binaryA,binaryB"; - t = migration.createTable(name = tableName, force = true); - t.binary(columnNames = columnNames); - t.create(); - - info = $dbinfo(datasource = application.wheels.dataSourceName, table = tableName, type = "columns"); - actual = ListToArray(ValueList(info.TYPE_NAME)); - migration.dropTable(tableName); - - expected = getBinaryType(); - - assert("actual[2] eq expected"); - assert("actual[3] eq expected"); - } - -} diff --git a/core/src/wheels/tests/migrator/migration/addboolean.cfc b/core/src/wheels/tests/migrator/migration/addboolean.cfc deleted file mode 100644 index fb2a2f301d..0000000000 --- a/core/src/wheels/tests/migrator/migration/addboolean.cfc +++ /dev/null @@ -1,80 +0,0 @@ -component extends="wheels.tests.Test" { - - private boolean function isDbCompatible() { - switch (migration.adapter.adapterName()) { - case "H2": - case "MicrosoftSQLServer": - case "MySQL": - case "PostgreSQL": - return true; - default: - return false; - } - } - - private string function getBooleanType() { - switch (migration.adapter.adapterName()) { - case "H2": - return "TINYINT"; - case "MicrosoftSQLServer": - return "BIT"; - case "MySQL": - return "BIT,TINYINT"; - case "PostgreSQL": - return "BOOLEAN"; - default: - return "`addboolean()` not supported for " & migration.adapter.adapterName(); - } - } - - function setup() { - migration = CreateObject("component", "wheels.migrator.Migration").init(); - originalMigratorObjectCase = Duplicate(application.wheels.migratorObjectCase); - } - - function teardown() { - application.wheels.migratorObjectCase = originalMigratorObjectCase; - } - - function test_add_a_boolean_column() { - if (!isDbCompatible()) { - return; - } - - tableName = "dbm_add_boolean_tests"; - columnName = "booleanCOLUMN"; - t = migration.createTable(name = tableName, force = true); - t.boolean(columnName = columnName); - t.create(); - - info = $dbinfo(datasource = application.wheels.dataSourceName, table = tableName, type = "columns"); - actual = ListToArray(ValueList(info.TYPE_NAME))[2]; - migration.dropTable(tableName); - - expected = getBooleanType(); - - assert("listContainsNoCase(expected,actual)"); - } - - function test_add_multiple_boolean_columns() { - if (!isDbCompatible()) { - return; - } - - tableName = "dbm_add_boolean_tests"; - columnNames = "booleanA,booleanB"; - t = migration.createTable(name = tableName, force = true); - t.boolean(columnNames = columnNames); - t.create(); - - info = $dbinfo(datasource = application.wheels.dataSourceName, table = tableName, type = "columns"); - actual = ListToArray(ValueList(info.TYPE_NAME)); - migration.dropTable(tableName); - - expected = getBooleanType(); - - assert("listContainsNoCase(expected,actual[2])"); - assert("listContainsNoCase(expected,actual[3])"); - } - -} diff --git a/core/src/wheels/tests/migrator/migration/addchar.cfc b/core/src/wheels/tests/migrator/migration/addchar.cfc deleted file mode 100644 index c509c0b939..0000000000 --- a/core/src/wheels/tests/migrator/migration/addchar.cfc +++ /dev/null @@ -1,71 +0,0 @@ -component extends="wheels.tests.Test" { - - private boolean function isDbCompatible() { - switch (migration.adapter.adapterName()) { - case "MicrosoftSQLServer": - return true; - default: - return false; - } - } - - private string function getCharType() { - switch (migration.adapter.adapterName()) { - case "MicrosoftSQLServer": - return "CHAR"; - default: - return "`addchar()` not supported for " & migration.adapter.adapterName(); - } - } - - function setup() { - migration = CreateObject("component", "wheels.migrator.Migration").init(); - originalMigratorObjectCase = Duplicate(application.wheels.migratorObjectCase); - } - - function teardown() { - application.wheels.migratorObjectCase = originalMigratorObjectCase; - } - - function test_add_a_char_column() { - if (!isDbCompatible()) { - return; - } - - tableName = "dbm_add_char_tests"; - columnName = "charCOLUMN"; - t = migration.createTable(name = tableName, force = true); - t.char(columnName = columnName); - t.create(); - - info = $dbinfo(datasource = application.wheels.dataSourceName, table = tableName, type = "columns"); - actual = ListToArray(ValueList(info.TYPE_NAME))[2]; - migration.dropTable(tableName); - - expected = getCharType(); - - assert("actual eq expected"); - } - - function test_add_multiple_char_columns() { - if (!isDbCompatible()) { - return; - } - - tableName = "dbm_add_char_tests"; - columnNames = "charA,charB"; - t = migration.createTable(name = tableName, force = true); - t.char(columnNames = columnNames); - t.create(); - - info = $dbinfo(datasource = application.wheels.dataSourceName, table = tableName, type = "columns"); - actual = ListToArray(ValueList(info.TYPE_NAME)); - migration.dropTable(tableName); - - expected = getCharType(); - - assert("actual[2] eq expected"); - assert("actual[3] eq expected"); - } - -} diff --git a/core/src/wheels/tests/migrator/migration/addcolumn.cfc b/core/src/wheels/tests/migrator/migration/addcolumn.cfc deleted file mode 100644 index bcd9417731..0000000000 --- a/core/src/wheels/tests/migrator/migration/addcolumn.cfc +++ /dev/null @@ -1,35 +0,0 @@ -component extends="wheels.tests.Test" { - - function setup() { - migration = CreateObject("component", "wheels.migrator.Migration").init(); - originalMigratorObjectCase = Duplicate(application.wheels.migratorObjectCase); - } - - function teardown() { - application.wheels.migratorObjectCase = originalMigratorObjectCase; - } - - // it's tricky to test the objectCase as some db engines support mixed case database object names (MSSQL does) - function test_addColumn_creates_new_column() { - application.wheels.migratorObjectCase = ""; // keep the specified case - tableName = "dbm_addcolumn_tests"; - columnName = "integerCOLUMN"; - t = migration.createTable(name = tableName, force = true); - t.string(columnNames = "stringcolumn"); - t.create(); - - migration.addColumn( - table = tableName, - columnType = 'integer', - columnName = columnName, - allowNull = true - ); - info = $dbinfo(datasource = application.wheels.dataSourceName, table = tableName, type = "columns"); - actual = ValueList(info.column_name); - expected = columnName; - migration.dropTable(tableName); - - assert("ListFindNoCase(actual, expected)", "expected", "actual"); - } - -} diff --git a/core/src/wheels/tests/migrator/migration/adddate.cfc b/core/src/wheels/tests/migrator/migration/adddate.cfc deleted file mode 100644 index a71f5b5a15..0000000000 --- a/core/src/wheels/tests/migrator/migration/adddate.cfc +++ /dev/null @@ -1,78 +0,0 @@ -component extends="wheels.tests.Test" { - - private boolean function isDbCompatible() { - switch (migration.adapter.adapterName()) { - case "H2": - case "MicrosoftSQLServer": - case "MySQL": - case "PostgreSQL": - return true; - default: - return false; - } - } - - private string function getDateType() { - switch (migration.adapter.adapterName()) { - case "H2": - case "MySQL": - case "PostgreSQL": - return "DATE"; - case "MicrosoftSQLServer": - return "date"; - default: - return "`adddate()` not supported for " & migration.adapter.adapterName(); - } - } - - function setup() { - migration = CreateObject("component", "wheels.migrator.Migration").init(); - originalMigratorObjectCase = Duplicate(application.wheels.migratorObjectCase); - } - - function teardown() { - application.wheels.migratorObjectCase = originalMigratorObjectCase; - } - - function test_add_a_date_column() { - if (!isDbCompatible()) { - return; - } - - tableName = "dbm_add_date_tests"; - columnName = "dateCOLUMN"; - t = migration.createTable(name = tableName, force = true); - t.date(columnName = columnName); - t.create(); - - info = $dbinfo(datasource = application.wheels.dataSourceName, table = tableName, type = "columns"); - actual = ListToArray(ValueList(info.TYPE_NAME))[2]; - migration.dropTable(tableName); - - expected = getDateType(); - - assert("actual eq expected"); - } - - function test_add_multiple_date_columns() { - if (!isDbCompatible()) { - return; - } - - tableName = "dbm_add_date_tests"; - columnNames = "dateA,dateB"; - t = migration.createTable(name = tableName, force = true); - t.date(columnNames = columnNames); - t.create(); - - info = $dbinfo(datasource = application.wheels.dataSourceName, table = tableName, type = "columns"); - actual = ListToArray(ValueList(info.TYPE_NAME)); - migration.dropTable(tableName); - - expected = getDateType(); - - assert("actual[2] eq expected"); - assert("actual[3] eq expected"); - } - -} diff --git a/core/src/wheels/tests/migrator/migration/adddatetime.cfc b/core/src/wheels/tests/migrator/migration/adddatetime.cfc deleted file mode 100644 index 4e734adad2..0000000000 --- a/core/src/wheels/tests/migrator/migration/adddatetime.cfc +++ /dev/null @@ -1,79 +0,0 @@ -component extends="wheels.tests.Test" { - - private boolean function isDbCompatible() { - switch (migration.adapter.adapterName()) { - case "H2": - case "MicrosoftSQLServer": - case "MySQL": - case "PostgreSQL": - return true; - default: - return false; - } - } - - private string function getDatetimeType() { - switch (migration.adapter.adapterName()) { - case "H2": - return "TIMESTAMP"; - case "MicrosoftSQLServer": - case "MySQL": - return "DATETIME"; - case "PostgreSQL": - return "TIMESTAMP"; - default: - return "`adddatetime()` not supported for " & migration.adapter.adapterName(); - } - } - - function setup() { - migration = CreateObject("component", "wheels.migrator.Migration").init(); - originalMigratorObjectCase = Duplicate(application.wheels.migratorObjectCase); - } - - function teardown() { - application.wheels.migratorObjectCase = originalMigratorObjectCase; - } - - function test_add_a_datetime_column() { - if (!isDbCompatible()) { - return; - } - - tableName = "dbm_add_datetime_tests"; - columnName = "datetimeCOLUMN"; - t = migration.createTable(name = tableName, force = true); - t.datetime(columnName = columnName); - t.create(); - - info = $dbinfo(datasource = application.wheels.dataSourceName, table = tableName, type = "columns"); - actual = ListToArray(ValueList(info.TYPE_NAME))[2]; - migration.dropTable(tableName); - - expected = getDatetimeType(); - - assert("actual eq expected"); - } - - function test_add_multiple_datetime_columns() { - if (!isDbCompatible()) { - return; - } - - tableName = "dbm_add_datetime_tests"; - columnNames = "datetimeA,datetimeB"; - t = migration.createTable(name = tableName, force = true); - t.datetime(columnNames = columnNames); - t.create(); - - info = $dbinfo(datasource = application.wheels.dataSourceName, table = tableName, type = "columns"); - actual = ListToArray(ValueList(info.TYPE_NAME)); - migration.dropTable(tableName); - - expected = getDatetimeType(); - - assert("actual[2] eq expected"); - assert("actual[3] eq expected"); - } - -} \ No newline at end of file diff --git a/core/src/wheels/tests/migrator/migration/adddecimal.cfc b/core/src/wheels/tests/migrator/migration/adddecimal.cfc deleted file mode 100644 index 37460a26f0..0000000000 --- a/core/src/wheels/tests/migrator/migration/adddecimal.cfc +++ /dev/null @@ -1,78 +0,0 @@ -component extends="wheels.tests.Test" { - - private boolean function isDbCompatible() { - switch (migration.adapter.adapterName()) { - case "H2": - case "MicrosoftSQLServer": - case "MySQL": - case "PostgreSQL": - return true; - default: - return false; - } - } - - private string function getDecimalType() { - switch (migration.adapter.adapterName()) { - case "H2": - case "MicrosoftSQLServer": - case "MySQL": - return "DECIMAL"; - case "PostgreSQL": - return "NUMERIC"; - default: - return "`adddecimal()` not supported for " & migration.adapter.adapterName(); - } - } - - function setup() { - migration = CreateObject("component", "wheels.migrator.Migration").init(); - originalMigratorObjectCase = Duplicate(application.wheels.migratorObjectCase); - } - - function teardown() { - application.wheels.migratorObjectCase = originalMigratorObjectCase; - } - - function test_add_a_decimal_column() { - if (!isDbCompatible()) { - return; - } - - tableName = "dbm_add_decimal_tests"; - columnName = "decimalCOLUMN"; - t = migration.createTable(name = tableName, force = true); - t.decimal(columnName = columnName); - t.create(); - - info = $dbinfo(datasource = application.wheels.dataSourceName, table = tableName, type = "columns"); - actual = ListToArray(ValueList(info.TYPE_NAME))[2]; - migration.dropTable(tableName); - - expected = getDecimalType(); - - assert("actual eq expected"); - } - - function test_add_multiple_decimal_columns() { - if (!isDbCompatible()) { - return; - } - - tableName = "dbm_add_decimal_tests"; - columnNames = "decimalA,decimalB"; - t = migration.createTable(name = tableName, force = true); - t.decimal(columnNames = columnNames); - t.create(); - - info = $dbinfo(datasource = application.wheels.dataSourceName, table = tableName, type = "columns"); - actual = ListToArray(ValueList(info.TYPE_NAME)); - migration.dropTable(tableName); - - expected = getDecimalType(); - - assert("actual[2] eq expected"); - assert("actual[3] eq expected"); - } - -} diff --git a/core/src/wheels/tests/migrator/migration/addfloat.cfc b/core/src/wheels/tests/migrator/migration/addfloat.cfc deleted file mode 100644 index 74599f6f71..0000000000 --- a/core/src/wheels/tests/migrator/migration/addfloat.cfc +++ /dev/null @@ -1,82 +0,0 @@ -component extends="wheels.tests.Test" { - - private boolean function isDbCompatible() { - switch (migration.adapter.adapterName()) { - case "H2": - return true; - case "MicrosoftSQLServer": - case "MySQL": - case "PostgreSQL": - return true; - default: - return false; - } - } - - private string function getFloatType() { - switch (migration.adapter.adapterName()) { - case "H2": - return "DOUBLE"; - case "MicrosoftSQLServer": - case "MySQL": - case "PostgreSQL": - return "FLOAT,float8"; // depends on db engine/drivers - default: - return "`addfloat()` not supported for " & migration.adapter.adapterName(); - } - } - - function setup() { - migration = CreateObject("component", "wheels.migrator.Migration").init(); - originalMigratorObjectCase = Duplicate(application.wheels.migratorObjectCase); - } - - function teardown() { - application.wheels.migratorObjectCase = originalMigratorObjectCase; - } - - function test_add_a_float_column() { - if (!isDbCompatible()) { - return; - } - - tableName = "dbm_add_float_tests"; - columnName = "floatCOLUMN"; - t = migration.createTable(name = tableName, force = true); - t.float(columnName = columnName); - t.create(); - - info = $dbinfo(datasource = application.wheels.dataSourceName, table = tableName, type = "columns"); - actual = ListToArray(ValueList(info.TYPE_NAME))[2]; - migration.dropTable(tableName); - - expected = getFloatType(); - - debug("expected"); - debug("actual"); - - assert("ListFindNoCase(expected, actual)"); - } - - function test_add_multiple_float_columns() { - if (!isDbCompatible()) { - return; - } - - tableName = "dbm_add_float_tests"; - columnNames = "floatA,floatB"; - t = migration.createTable(name = tableName, force = true); - t.float(columnNames = columnNames); - t.create(); - - info = $dbinfo(datasource = application.wheels.dataSourceName, table = tableName, type = "columns"); - actual = ListToArray(ValueList(info.TYPE_NAME)); - migration.dropTable(tableName); - - expected = getFloatType(); - - assert("ListFindNoCase(expected, actual[2])"); - assert("ListFindNoCase(expected, actual[3])"); - } - -} \ No newline at end of file diff --git a/core/src/wheels/tests/migrator/migration/addforeignkey.cfc b/core/src/wheels/tests/migrator/migration/addforeignkey.cfc deleted file mode 100644 index aa08c26ab7..0000000000 --- a/core/src/wheels/tests/migrator/migration/addforeignkey.cfc +++ /dev/null @@ -1,38 +0,0 @@ -component extends="wheels.tests.Test" { - - function setup() { - migration = CreateObject("component", "wheels.migrator.Migration").init(); - } - - function test_addForeignKey_creates_a_foreign_key_constraint() { - tableName = "dbm_afk_foos"; - referenceTableName = "dbm_afk_bars"; - - t = migration.createTable(name = tableName, force = true); - t.integer(columnNames = "barid"); - t.create(); - - t = migration.createTable(name = referenceTableName, force = true); - t.integer(columnNames = "integercolumn"); - t.create(); - - migration.addForeignKey( - table = tableName, - referenceTable = referenceTableName, - column = 'barid', - referenceColumn = "id" - ); - - info = $dbinfo(datasource = application.wheels.dataSourceName, table = referenceTableName, type = "foreignkeys"); - - migration.dropTable(tableName); - migration.dropTable(referenceTableName); - - sql = "SELECT * FROM query WHERE pkcolumn_name = 'id' AND fktable_name = '#tableName#' AND fkcolumn_name = 'barid'"; - - actual = $query(query = info, dbtype = "query", sql = sql); - - assert("actual.recordCount eq 1"); - } - -} diff --git a/core/src/wheels/tests/migrator/migration/addindex.cfc b/core/src/wheels/tests/migrator/migration/addindex.cfc deleted file mode 100644 index dab306035b..0000000000 --- a/core/src/wheels/tests/migrator/migration/addindex.cfc +++ /dev/null @@ -1,62 +0,0 @@ -component extends="wheels.tests.Test" { - - function setup() { - migration = CreateObject("component", "wheels.migrator.Migration").init(); - isACF2016 = application.wheels.serverName == "Adobe Coldfusion" && application.wheels.serverVersionMajor == 2016; - isPostgres = migration.adapter.adapterName() == "PostgreSQL"; - } - - function test_addIndex_creates_an_index() { - if (isACF2016 && isPostgres) { - return; - } - - tableName = "dbm_addindex_tests"; - indexName = "idx_to_add"; - t = migration.createTable(name = tableName, force = true); - t.integer(columnNames = "integercolumn"); - t.create(); - - migration.addIndex(table = tableName, columnName = 'integercolumn', indexName = indexName); - info = $dbinfo(datasource = application.wheels.dataSourceName, table = tableName, type = "index"); - - migration.dropTable(tableName); - - sql = "SELECT * FROM query WHERE index_name = '#indexName#'"; - - actual = $query(query = info, dbtype = "query", sql = sql); - assert("actual.recordCount eq 1"); - assert("actual.non_unique"); - } - - function test_add_index_on_mutiple_columns() { - if (isACF2016 && isPostgres) { - return; - } - - tableName = "dbm_addindex_tests"; - indexName = "idx_to_add_to_multiple_columns"; - t = migration.createTable(name = tableName, force = true); - t.integer(columnNames = "integercolumn,datecolumn"); - t.create(); - - migration.addIndex(table = tableName, columnNames = 'integercolumn,datecolumn', indexName = indexName); - info = $dbinfo(datasource = application.wheels.dataSourceName, table = tableName, type = "index"); - - migration.dropTable(tableName); - - sql = "SELECT * FROM query WHERE index_name = '#indexName#'"; - - actual = $query(query = info, dbtype = "query", sql = sql); - - // Added the ListLen check here for CF2018 because its cfdbinfo behaves a little differently. - // It returns the index for multiple columns in one record where as Lucee returns multiple. - assert("actual.recordCount eq 2 OR ListLen(actual['column_name'][1]) IS 2"); - assert("actual.non_unique"); - } - - // todo: unique index - - // default index name - -} diff --git a/core/src/wheels/tests/migrator/migration/addinteger.cfc b/core/src/wheels/tests/migrator/migration/addinteger.cfc deleted file mode 100644 index 913234c8aa..0000000000 --- a/core/src/wheels/tests/migrator/migration/addinteger.cfc +++ /dev/null @@ -1,83 +0,0 @@ -component extends="wheels.tests.Test" { - - private boolean function isDbCompatible() { - switch (migration.adapter.adapterName()) { - case "H2": - return true; - case "MicrosoftSQLServer": - case "MySQL": - case "PostgreSQL": - return true; - default: - return false; - } - } - - private string function getIntegerType() { - switch (migration.adapter.adapterName()) { - case "H2": - return "INTEGER"; - case "MicrosoftSQLServer": - case "MySQL": - return "INT"; - case "PostgreSQL": - return "INTEGER,INT4"; // depends on db engine/drivers - default: - return "`addinteger()` not supported for " & migration.adapter.adapterName(); - } - } - - function setup() { - migration = CreateObject("component", "wheels.migrator.Migration").init(); - originalMigratorObjectCase = Duplicate(application.wheels.migratorObjectCase); - } - - function teardown() { - application.wheels.migratorObjectCase = originalMigratorObjectCase; - } - - function test_add_a_integer_column() { - if (!isDbCompatible()) { - return; - } - - tableName = "dbm_add_integer_tests"; - columnName = "integerCOLUMN"; - t = migration.createTable(name = tableName, force = true); - t.integer(columnName = columnName); - t.create(); - - info = $dbinfo(datasource = application.wheels.dataSourceName, table = tableName, type = "columns"); - actual = ListToArray(ValueList(info.TYPE_NAME))[2]; - migration.dropTable(tableName); - - expected = getIntegerType(); - - debug("expected"); - debug("actual"); - - assert("ListFindNoCase(expected, actual)"); - } - - function test_add_multiple_integer_columns() { - if (!isDbCompatible()) { - return; - } - - tableName = "dbm_add_integer_tests"; - columnNames = "integerA,integerB"; - t = migration.createTable(name = tableName, force = true); - t.integer(columnNames = columnNames); - t.create(); - - info = $dbinfo(datasource = application.wheels.dataSourceName, table = tableName, type = "columns"); - actual = ListToArray(ValueList(info.TYPE_NAME)); - migration.dropTable(tableName); - - expected = getIntegerType(); - - assert("ListFindNoCase(expected, actual[2])"); - assert("ListFindNoCase(expected, actual[3])"); - } - -} \ No newline at end of file diff --git a/core/src/wheels/tests/migrator/migration/addrecord.cfc b/core/src/wheels/tests/migrator/migration/addrecord.cfc deleted file mode 100644 index 3cd5c40a51..0000000000 --- a/core/src/wheels/tests/migrator/migration/addrecord.cfc +++ /dev/null @@ -1,26 +0,0 @@ -component extends="wheels.tests.Test" { - - function setup() { - migration = CreateObject("component", "wheels.migrator.Migration").init(); - } - - function test_addRecord_inserts_row_into_table() { - tableName = "dbm_addrecord_tests"; - recordValue = "#RandRange(0, 99)# bottles of beer on the wall..."; - - t = migration.createTable(name = tableName, force = true); - t.string(columnNames = "beers"); - t.timeStamps(); - t.create(); - migration.addRecord(table = tableName, beers = recordValue); - actual = $query( - datasource = application.wheels.dataSourceName, - sql = "SELECT * FROM #tableName# WHERE beers = '#recordValue#'" - ); - expected = 1; - - migration.dropTable(tableName); - assert("actual.RecordCount eq expected"); - } - -} diff --git a/core/src/wheels/tests/migrator/migration/addstring.cfc b/core/src/wheels/tests/migrator/migration/addstring.cfc deleted file mode 100644 index d49a4d8a26..0000000000 --- a/core/src/wheels/tests/migrator/migration/addstring.cfc +++ /dev/null @@ -1,77 +0,0 @@ -component extends="wheels.tests.Test" { - - private boolean function isDbCompatible() { - switch (migration.adapter.adapterName()) { - case "H2": - case "MicrosoftSQLServer": - case "MySQL": - case "PostgreSQL": - return true; - default: - return false; - } - } - - private string function getStringType() { - switch (migration.adapter.adapterName()) { - case "H2": - case "MicrosoftSQLServer": - case "MySQL": - case "PostgreSQL": - return "VARCHAR"; - default: - return "`addstring()` not supported for " & migration.adapter.adapterName(); - } - } - - function setup() { - migration = CreateObject("component", "wheels.migrator.Migration").init(); - originalMigratorObjectCase = Duplicate(application.wheels.migratorObjectCase); - } - - function teardown() { - application.wheels.migratorObjectCase = originalMigratorObjectCase; - } - - function test_add_a_string_column() { - if (!isDbCompatible()) { - return; - } - - tableName = "dbm_add_string_tests"; - columnName = "stringCOLUMN"; - t = migration.createTable(name = tableName, force = true); - t.string(columnName = columnName); - t.create(); - - info = $dbinfo(datasource = application.wheels.dataSourceName, table = tableName, type = "columns"); - actual = ListToArray(ValueList(info.TYPE_NAME))[2]; - migration.dropTable(tableName); - - expected = getStringType(); - - assert("actual eq expected"); - } - - function test_add_multiple_string_columns() { - if (!isDbCompatible()) { - return; - } - - tableName = "dbm_add_string_tests"; - columnNames = "stringA,stringB"; - t = migration.createTable(name = tableName, force = true); - t.string(columnNames = columnNames); - t.create(); - - info = $dbinfo(datasource = application.wheels.dataSourceName, table = tableName, type = "columns"); - actual = ListToArray(ValueList(info.TYPE_NAME)); - migration.dropTable(tableName); - - expected = getStringType(); - - assert("actual[2] eq expected"); - assert("actual[3] eq expected"); - } - -} diff --git a/core/src/wheels/tests/migrator/migration/addtext.cfc b/core/src/wheels/tests/migrator/migration/addtext.cfc deleted file mode 100644 index f4856d09f3..0000000000 --- a/core/src/wheels/tests/migrator/migration/addtext.cfc +++ /dev/null @@ -1,84 +0,0 @@ -component extends="wheels.tests.Test" { - - private boolean function isDbCompatible() { - switch (migration.adapter.adapterName()) { - case "H2": - return true; - case "MicrosoftSQLServer": - return true; - case "MySQL": - case "PostgreSQL": - return true; - default: - return false; - } - } - - private array function getTextType() { - switch (migration.adapter.adapterName()) { - case "H2": - return ["CLOB"]; - case "MySQL": - case "PostgreSQL": - return ["TEXT"]; - case "MicrosoftSQLServer": - return ["NVARCHAR", "NVARCHAR(MAX)"]; - default: - return "`addtext()` not supported for " & migration.adapter.adapterName(); - } - } - - function setup() { - migration = CreateObject("component", "wheels.migrator.Migration").init(); - originalMigratorObjectCase = Duplicate(application.wheels.migratorObjectCase); - } - - function teardown() { - application.wheels.migratorObjectCase = originalMigratorObjectCase; - } - - function test_add_a_text_column() { - if (!isDbCompatible()) { - return; - } - - tableName = "dbm_add_text_tests"; - columnName = "textCOLUMN"; - t = migration.createTable(name = tableName, force = true); - t.text(columnName = columnName); - t.create(); - - info = $dbinfo(datasource = application.wheels.dataSourceName, table = tableName, type = "columns"); - actual = ListToArray(ValueList(info.TYPE_NAME))[2]; - migration.dropTable(tableName); - - expected = getTextType(); - - debug("expected"); - debug("actual"); - - assert("ArrayContainsNoCase(expected, actual)"); - } - - function test_add_multiple_text_columns() { - if (!isDbCompatible()) { - return; - } - - tableName = "dbm_add_text_tests"; - columnNames = "textA,textB"; - t = migration.createTable(name = tableName, force = true); - t.text(columnNames = columnNames); - t.create(); - - info = $dbinfo(datasource = application.wheels.dataSourceName, table = tableName, type = "columns"); - actual = ListToArray(ValueList(info.TYPE_NAME)); - migration.dropTable(tableName); - - expected = getTextType(); - - assert("ArrayContainsNoCase(expected, actual[2])"); - assert("ArrayContainsNoCase(expected, actual[3])"); - } - -} \ No newline at end of file diff --git a/core/src/wheels/tests/migrator/migration/addtime.cfc b/core/src/wheels/tests/migrator/migration/addtime.cfc deleted file mode 100644 index 0bc552a602..0000000000 --- a/core/src/wheels/tests/migrator/migration/addtime.cfc +++ /dev/null @@ -1,78 +0,0 @@ -component extends="wheels.tests.Test" { - - private boolean function isDbCompatible() { - switch (migration.adapter.adapterName()) { - case "H2": - case "MicrosoftSQLServer": - case "MySQL": - case "PostgreSQL": - return true; - default: - return false; - } - } - - private string function getTimeType() { - switch (migration.adapter.adapterName()) { - case "MicrosoftSQLServer": - return "time"; - case "MySQL": - case "H2": - case "PostgreSQL": - return "TIME"; - default: - return "`addtime()` not supported for " & migration.adapter.adapterName(); - } - } - - function setup() { - migration = CreateObject("component", "wheels.migrator.Migration").init(); - originalMigratorObjectCase = Duplicate(application.wheels.migratorObjectCase); - } - - function teardown() { - application.wheels.migratorObjectCase = originalMigratorObjectCase; - } - - function test_add_a_time_column() { - if (!isDbCompatible()) { - return; - } - - tableName = "dbm_add_time_tests"; - columnName = "timeCOLUMN"; - t = migration.createTable(name = tableName, force = true); - t.time(columnName = columnName); - t.create(); - - info = $dbinfo(datasource = application.wheels.dataSourceName, table = tableName, type = "columns"); - actual = ListToArray(ValueList(info.TYPE_NAME))[2]; - migration.dropTable(tableName); - - expected = getTimeType(); - - assert("actual eq expected"); - } - - function test_add_multiple_time_columns() { - if (!isDbCompatible()) { - return; - } - - tableName = "dbm_add_time_tests"; - columnNames = "timeA,timeB"; - t = migration.createTable(name = tableName, force = true); - t.time(columnNames = columnNames); - t.create(); - - info = $dbinfo(datasource = application.wheels.dataSourceName, table = tableName, type = "columns"); - actual = ListToArray(ValueList(info.TYPE_NAME)); - migration.dropTable(tableName); - - expected = getTimeType(); - - assert("actual[2] eq expected"); - assert("actual[3] eq expected"); - } - -} diff --git a/core/src/wheels/tests/migrator/migration/addtimestamp.cfc b/core/src/wheels/tests/migrator/migration/addtimestamp.cfc deleted file mode 100644 index 71804f800e..0000000000 --- a/core/src/wheels/tests/migrator/migration/addtimestamp.cfc +++ /dev/null @@ -1,80 +0,0 @@ -component extends="wheels.tests.Test" { - - private boolean function isDbCompatible() { - switch (migration.adapter.adapterName()) { - case "H2": - case "MicrosoftSQLServer": - case "MySQL": - case "PostgreSQL": - return true; - default: - return false; - } - } - - private string function getTimestampType() { - switch (migration.adapter.adapterName()) { - case "MicrosoftSQLServer": - return "DATETIME"; - case "H2": - return "TIMESTAMP"; - case "MySQL": - return "DATETIME"; - case "PostgreSQL": - return "TIMESTAMP"; - default: - return "`addtimestamp()` not supported for " & migration.adapter.adapterName(); - } - } - - function setup() { - migration = CreateObject("component", "wheels.migrator.Migration").init(); - originalMigratorObjectCase = Duplicate(application.wheels.migratorObjectCase); - } - - function teardown() { - application.wheels.migratorObjectCase = originalMigratorObjectCase; - } - - function test_add_a_timestamp_column() { - if (!isDbCompatible()) { - return; - } - - tableName = "dbm_add_timestamp_tests"; - columnName = "timestampCOLUMN"; - t = migration.createTable(name = tableName, force = true); - t.timestamp(columnName = columnName); - t.create(); - - info = $dbinfo(datasource = application.wheels.dataSourceName, table = tableName, type = "columns"); - actual = ListToArray(ValueList(info.TYPE_NAME))[2]; - migration.dropTable(tableName); - - expected = getTimestampType(); - - assert("actual eq expected"); - } - - function test_add_multiple_timestamp_columns() { - if (!isDbCompatible()) { - return; - } - - tableName = "dbm_add_timestamp_tests"; - columnNames = "timestampA,timestampB"; - t = migration.createTable(name = tableName, force = true); - t.timestamp(columnNames = columnNames); - t.create(); - - info = $dbinfo(datasource = application.wheels.dataSourceName, table = tableName, type = "columns"); - actual = ListToArray(ValueList(info.TYPE_NAME)); - migration.dropTable(tableName); - - expected = getTimestampType(); - - assert("actual[2] eq expected"); - assert("actual[3] eq expected"); - } - -} \ No newline at end of file diff --git a/core/src/wheels/tests/migrator/migration/adduniqueidentifier.cfc b/core/src/wheels/tests/migrator/migration/adduniqueidentifier.cfc deleted file mode 100644 index dc6cfe2ce0..0000000000 --- a/core/src/wheels/tests/migrator/migration/adduniqueidentifier.cfc +++ /dev/null @@ -1,71 +0,0 @@ -component extends="wheels.tests.Test" { - - private boolean function isDbCompatible() { - switch (migration.adapter.adapterName()) { - case "MicrosoftSQLServer": - return true; - default: - return false; - } - } - - private string function getUniqueIdentifierType() { - switch (migration.adapter.adapterName()) { - case "MicrosoftSQLServer": - return "UNIQUEIDENTIFIER"; - default: - return "`adduniqueidentifier()` not supported for " & migration.adapter.adapterName(); - } - } - - function setup() { - migration = CreateObject("component", "wheels.migrator.Migration").init(); - originalMigratorObjectCase = Duplicate(application.wheels.migratorObjectCase); - } - - function teardown() { - application.wheels.migratorObjectCase = originalMigratorObjectCase; - } - - function test_add_a_uniqueidentifier_column() { - if (!isDbCompatible()) { - return; - } - - tableName = "dbm_add_uniqueidentifier_tests"; - columnName = "uniqueidentifierCOLUMN"; - t = migration.createTable(name = tableName, force = true); - t.uniqueidentifier(columnName = columnName); - t.create(); - - info = $dbinfo(datasource = application.wheels.dataSourceName, table = tableName, type = "columns"); - actual = ListToArray(ValueList(info.TYPE_NAME))[2]; - migration.dropTable(tableName); - - expected = getUniqueIdentifierType(); - - assert("actual eq expected"); - } - - function test_add_multiple_uniqueidentifier_columns() { - if (!isDbCompatible()) { - return; - } - - tableName = "dbm_add_uniqueidentifier_tests"; - columnNames = "uniqueidentifierA,uniqueidentifierB"; - t = migration.createTable(name = tableName, force = true); - t.uniqueidentifier(columnNames = columnNames); - t.create(); - - info = $dbinfo(datasource = application.wheels.dataSourceName, table = tableName, type = "columns"); - actual = ListToArray(ValueList(info.TYPE_NAME)); - migration.dropTable(tableName); - - expected = getUniqueIdentifierType(); - - assert("actual[2] eq expected"); - assert("actual[3] eq expected"); - } - -} diff --git a/core/src/wheels/tests/migrator/migration/announce.cfc b/core/src/wheels/tests/migrator/migration/announce.cfc deleted file mode 100644 index 3bd9750f8c..0000000000 --- a/core/src/wheels/tests/migrator/migration/announce.cfc +++ /dev/null @@ -1,21 +0,0 @@ -component extends="wheels.tests.Test" { - - function setup() { - request.$wheelsMigrationOutput = ""; - migration = CreateObject("component", "wheels.migrator.Migration").init(); - } - - function test_annouce_appends_announcement() { - napalm = "I love the smell of napalm in the morning!"; - truth = "You can't handle the truth!"; - - migration.announce(napalm); - migration.announce(truth); - - actual = request.$wheelsMigrationOutput; - expected = napalm & Chr(13) & truth & Chr(13); - - assert("actual eq expected"); - } - -} diff --git a/core/src/wheels/tests/migrator/migration/changecolumn.cfc b/core/src/wheels/tests/migrator/migration/changecolumn.cfc deleted file mode 100644 index 64bd5c58ea..0000000000 --- a/core/src/wheels/tests/migrator/migration/changecolumn.cfc +++ /dev/null @@ -1,45 +0,0 @@ -component extends="wheels.tests.Test" { - - include "../migrator/helpers.cfm"; - function setup() { - migration = CreateObject("component", "wheels.migrator.Migration").init(); - } - - // more tests needed.. changing datatypes.. etc - - function test_changeColumn_changes_column() { - tableName = "dbm_changecolumn_tests"; - columnName = "stringcolumn"; - - t = migration.createTable(name = tableName, force = true); - t.string(columnNames = columnName, limit = 10, allowNull = true); - t.create(); - - migration.changeColumn( - table = tableName, - columnName = columnName, - columnType = 'string', - limit = 50, - allowNull = false, - default = "foo" - ); - - info = $dbinfo(datasource = application.wheels.dataSourceName, table = tableName, type = "columns"); - migration.dropTable(tableName); - sql = "SELECT * FROM query WHERE column_name = '#columnName#'"; - actual = $query(query = info, dbtype = "query", sql = sql); - - assert("actual.column_size eq 50"); - if (ListFindNoCase(actual.columnList, "is_nullable")) { - assert("!actual.is_nullable"); - } else { - assert("!actual.nullable"); - } - if (ListFindNoCase(actual.columnList, "default_value")) { - assert("actual.default_value contains 'bar'"); - } else { - assert("actual.column_default_value contains 'foo'"); - } - } - -} diff --git a/core/src/wheels/tests/migrator/migration/createtable.cfc b/core/src/wheels/tests/migrator/migration/createtable.cfc deleted file mode 100644 index e9f15f3fc2..0000000000 --- a/core/src/wheels/tests/migrator/migration/createtable.cfc +++ /dev/null @@ -1,49 +0,0 @@ -component extends="wheels.tests.Test" { - - function setup() { - migration = CreateObject("component", "wheels.migrator.Migration").init(); - } - - function test_createTable_generates_table() { - tableName = "dbm_createtable_tests"; - t = migration.createTable(name = tableName, force = true); - t.string(columnNames = 'stringcolumn, secondstringcolumn ', limit = 255); // notice the untrimmed column name - t.text(columnNames = 'textcolumn'); - t.boolean(columnNames = 'booleancolumn', default = false, allowNull = false); - t.integer(columnNames = 'integercolumn', default = 0); - t.binary(columnNames = "binarycolumn"); - t.date(columnNames = "datecolumn"); - t.dateTime(columnNames = "datetimecolumn"); - t.time(columnNames = "timecolumn"); - t.decimal(columnNames = "decimalcolumn"); - t.float(columnNames = "floatcolumn"); - // TODO: this datatype doesn't work on sqlserver - // t.bigInteger(columnNames="bigintegercolumn", default=0); - t.timeStamps(); - t.create(); - - actual = ListSort(model(tableName).findAll().columnList, "text"); - expected = ListSort( - "id,stringcolumn,secondstringcolumn,textcolumn,booleancolumn,integercolumn,binarycolumn,datecolumn,datetimecolumn,timecolumn,decimalcolumn,floatcolumn,createdat,updatedat,deletedat", - "text" - ); - - migration.dropTable(tableName); - assert("actual eq expected"); - } - - function test_createTable_generates_table_using_MicrosoftSQLServer_datatypes() { - tableName = "dbm_createtable_sqlserver_tests"; - if (migration.adapter.adapterName() eq "MicrosoftSQLServer") { - t = migration.createTable(name = tableName, force = true); - t.char(columnNames = "charcolumn"); - t.uniqueIdentifier(columnNames = "uniqueidentifiercolumn"); - t.create(); - actual = ListSort(model(tableName).findAll().columnList, "text"); - expected = ListSort("id,charcolumn,uniqueidentifiercolumn", "text"); - migration.dropTable(tableName); - assert("actual eq expected"); - } - } - -} diff --git a/core/src/wheels/tests/migrator/migration/createview.cfc b/core/src/wheels/tests/migrator/migration/createview.cfc deleted file mode 100644 index 7fce6b5f8e..0000000000 --- a/core/src/wheels/tests/migrator/migration/createview.cfc +++ /dev/null @@ -1,28 +0,0 @@ -component extends="wheels.tests.Test" { - - function setup() { - migration = CreateObject("component", "wheels.migrator.Migration").init(); - } - - function test_createView_generates_view() { - viewName = "dbm_createview"; - // only supported with these adapters - if (ListFindNoCase("MicrosoftSQLServer", migration.adapter.adapterName())) { - v = migration.createView(name = viewName); - v.selectStatement(sql = "SELECT * FROM c_o_r_e_users"); - v.create(); - - info = $dbinfo(datasource = application.wheels.dataSourceName, type = "tables"); - migration.dropView(viewName); - - actual = $query( - query = info, - dbtype = "query", - sql = "SELECT * FROM query WHERE table_name = '#viewName#' AND table_type = 'VIEW'" - ); - - assert("actual.recordCount eq 1"); - } - } - -} diff --git a/core/src/wheels/tests/migrator/migration/dropforeignkey.cfc b/core/src/wheels/tests/migrator/migration/dropforeignkey.cfc deleted file mode 100644 index aacb1e63e5..0000000000 --- a/core/src/wheels/tests/migrator/migration/dropforeignkey.cfc +++ /dev/null @@ -1,43 +0,0 @@ -component extends="wheels.tests.Test" { - - function setup() { - migration = CreateObject("component", "wheels.migrator.Migration").init(); - } - - function test_dropForeignKey_drops_a_foreign_key_constraint() { - tableName = "dbm_dfk_foos"; - referenceTableName = "dbm_dfk_bars"; - - t = migration.createTable(name = tableName, force = true); - t.integer(columnNames = "barid"); - t.create(); - - t = migration.createTable(name = referenceTableName, force = true); - t.integer(columnNames = "integercolumn"); - t.create(); - - migration.addForeignKey( - table = tableName, - referenceTable = referenceTableName, - column = 'barid', - referenceColumn = "id" - ); - - info = $dbinfo(datasource = application.wheels.dataSourceName, table = referenceTableName, type = "foreignkeys"); - - - sql = "SELECT * FROM query WHERE fktable_name = '#tableName#' AND fkcolumn_name = 'barid' AND pkcolumn_name = 'id'"; - - created = $query(query = info, dbtype = "query", sql = sql); - - migration.dropForeignKey(table = tableName, keyName = "FK_#tableName#_#referenceTableName#"); - info = $dbinfo(datasource = application.wheels.dataSourceName, table = referenceTableName, type = "foreignkeys"); - dropped = $query(query = info, dbtype = "query", sql = sql); - - migration.dropTable(tableName); - migration.dropTable(referenceTableName); - assert("created.recordCount eq 1"); - assert("dropped.recordCount eq 0"); - } - -} diff --git a/core/src/wheels/tests/migrator/migration/droptable.cfc b/core/src/wheels/tests/migrator/migration/droptable.cfc deleted file mode 100644 index 7c72eea0ee..0000000000 --- a/core/src/wheels/tests/migrator/migration/droptable.cfc +++ /dev/null @@ -1,24 +0,0 @@ -component extends="wheels.tests.Test" { - - function setup() { - migration = CreateObject("component", "wheels.migrator.Migration").init(); - } - - function test_createTable_generates_table() { - tableName = "dbm_droptable_tests"; - - t = migration.createTable(name = tableName, force = true); - t.string(columnNames = "foo"); - t.timeStamps(); - t.create(); - - migration.dropTable(name = tableName); - try { - actual = $query(datasource = application.wheels.dataSourceName, sql = "SELECT * FROM #tableName#"); - assert("false"); - } catch (any e) { - raised("database"); - } - } - -} diff --git a/core/src/wheels/tests/migrator/migration/dropview.cfc b/core/src/wheels/tests/migrator/migration/dropview.cfc deleted file mode 100644 index 209215b563..0000000000 --- a/core/src/wheels/tests/migrator/migration/dropview.cfc +++ /dev/null @@ -1,34 +0,0 @@ -component extends="wheels.tests.Test" { - - function setup() { - migration = CreateObject("component", "wheels.migrator.Migration").init(); - } - - function test_dropView_drops_view() { - viewName = "dbm_dropview"; - // only supported with these adapters - if (ListFindNoCase("MicrosoftSQLServer", migration.adapter.adapterName())) { - v = migration.createView(name = viewName); - v.selectStatement(sql = "SELECT * FROM c_o_r_e_users"); - v.create(); - info = $dbinfo(datasource = application.wheels.dataSourceName, type = "tables"); - created = $query( - query = info, - dbtype = "query", - sql = "SELECT * FROM query WHERE table_name = '#viewName#' AND table_type = 'VIEW'" - ); - - migration.dropView(viewName); - info = $dbinfo(datasource = application.wheels.dataSourceName, type = "tables"); - dropped = $query( - query = info, - dbtype = "query", - sql = "SELECT * FROM query WHERE table_name = '#viewName#' AND table_type = 'VIEW'" - ); - - assert("created.recordCount eq 1"); - assert("dropped.recordCount eq 0"); - } - } - -} diff --git a/core/src/wheels/tests/migrator/migration/execute.cfc b/core/src/wheels/tests/migrator/migration/execute.cfc deleted file mode 100644 index 10a4ceded8..0000000000 --- a/core/src/wheels/tests/migrator/migration/execute.cfc +++ /dev/null @@ -1,28 +0,0 @@ -component extends="wheels.tests.Test" { - - function setup() { - migration = CreateObject("component", "wheels.migrator.Migration").init(); - } - - function test_execute_runs_query() { - tableName = "dbm_execute_tests"; - - t = migration.createTable(name = tableName, force = true); - t.string(columnNames = "film"); - t.timeStamps(); - t.create(); - - migration.addRecord(table = tableName, film = "The Phantom Menace"); - migration.addRecord(table = tableName, film = "The Clone Wars"); - migration.addRecord(table = tableName, film = "Revenge of the Sith"); - - migration.execute("DELETE FROM #tableName#"); - - actual = $query(datasource = application.wheels.dataSourceName, sql = "SELECT * FROM #tableName#"); - expected = 0; - - migration.dropTable(tableName); - assert("actual.RecordCount eq expected"); - } - -} diff --git a/core/src/wheels/tests/migrator/migration/removecolumn.cfc b/core/src/wheels/tests/migrator/migration/removecolumn.cfc deleted file mode 100644 index cbe2b6eb7f..0000000000 --- a/core/src/wheels/tests/migrator/migration/removecolumn.cfc +++ /dev/null @@ -1,23 +0,0 @@ -component extends="wheels.tests.Test" { - - function setup() { - migration = CreateObject("component", "wheels.migrator.Migration").init(); - } - - function test_removeColumn_drops_column_from_table() { - tableName = "dbm_removecolumn_tests"; - t = migration.createTable(name = tableName, force = true); - t.string(columnNames = "stringcolumn"); - t.date(columnNames = "datecolumn"); - t.create(); - - migration.removeColumn(table = tableName, columnName = 'datecolumn'); - info = $dbinfo(datasource = application.wheels.dataSourceName, table = tableName, type = "columns"); - actual = ValueList(info.column_name); - expected = "datecolumn"; - migration.dropTable(tableName); - - assert("ListFindNoCase(actual, expected) eq 0", "expected", "actual"); - } - -} diff --git a/core/src/wheels/tests/migrator/migration/removeindex.cfc b/core/src/wheels/tests/migrator/migration/removeindex.cfc deleted file mode 100644 index c494559601..0000000000 --- a/core/src/wheels/tests/migrator/migration/removeindex.cfc +++ /dev/null @@ -1,33 +0,0 @@ -component extends="wheels.tests.Test" { - - function setup() { - migration = CreateObject("component", "wheels.migrator.Migration").init(); - isACF2016 = application.wheels.serverName == "Adobe Coldfusion" && application.wheels.serverVersionMajor == 2016; - isPostgres = migration.adapter.adapterName() == "PostgreSQL"; - } - - function test_removeIndex_removes_an_index() { - if (isACF2016 && isPostgres) { - return; - } - tableName = "dbm_removeindex_tests"; - indexName = "idx_to_remove"; - t = migration.createTable(name = tableName, force = true); - t.integer(columnNames = "integercolumn"); - t.create(); - - migration.addIndex(table = tableName, columnNames = 'integercolumn', indexName = indexName); - info = $dbinfo(datasource = application.wheels.dataSourceName, table = tableName, type = "index"); - sql = "SELECT * FROM query WHERE index_name = '#indexName#'"; - created = $query(query = info, dbtype = "query", sql = sql); - - migration.removeIndex(table = tableName, indexName = indexName); - info = $dbinfo(datasource = application.wheels.dataSourceName, table = tableName, type = "index"); - removed = $query(query = info, dbtype = "query", sql = sql); - - migration.dropTable(tableName); - assert("created.recordCount eq 1"); - assert("removed.recordCount eq 0"); - } - -} diff --git a/core/src/wheels/tests/migrator/migration/removerecord.cfc b/core/src/wheels/tests/migrator/migration/removerecord.cfc deleted file mode 100644 index 8cb3bb5910..0000000000 --- a/core/src/wheels/tests/migrator/migration/removerecord.cfc +++ /dev/null @@ -1,29 +0,0 @@ -component extends="wheels.tests.Test" { - - function setup() { - migration = CreateObject("component", "wheels.migrator.Migration").init(); - } - - function test_removeRecord_deletes_rows_from_table() { - tableName = "dbm_removerecord_tests"; - - t = migration.createTable(name = tableName, force = true); - t.string(columnNames = "beatle"); - t.timeStamps(); - t.create(); - - migration.addRecord(table = tableName, beatle = "John"); - migration.addRecord(table = tableName, beatle = "Paul"); - migration.addRecord(table = tableName, beatle = "George"); - migration.addRecord(table = tableName, beatle = "Ringo"); - - migration.removeRecord(table = tableName, where = "beatle IN ('John','George')"); - - actual = $query(datasource = application.wheels.dataSourceName, sql = "SELECT * FROM #tableName#"); - expected = 2; - - migration.dropTable(tableName); - assert("actual.RecordCount eq expected"); - } - -} diff --git a/core/src/wheels/tests/migrator/migration/renamecolumn.cfc b/core/src/wheels/tests/migrator/migration/renamecolumn.cfc deleted file mode 100644 index 184e7ab9b0..0000000000 --- a/core/src/wheels/tests/migrator/migration/renamecolumn.cfc +++ /dev/null @@ -1,24 +0,0 @@ -component extends="wheels.tests.Test" { - - function setup() { - migration = CreateObject("component", "wheels.migrator.Migration").init(); - } - - function test_renameColumn_renames_column() { - tableName = "dbm_renamecolumn_tests"; - oldColumnName = "oldcolumn"; - newColumnName = "newcolumn"; - t = migration.createTable(name = tableName, force = true); - t.string(columnNames = oldColumnName); - t.create(); - migration.renameColumn(table = tableName, columnName = oldColumnName, newColumnName = newColumnName); - - actual = model(tableName).findAll().columnList; - - migration.dropTable(tableName); - - expected = newColumnName; - assert("ListFindNoCase(actual, expected)"); - } - -} diff --git a/core/src/wheels/tests/migrator/migration/renametable.cfc b/core/src/wheels/tests/migrator/migration/renametable.cfc deleted file mode 100644 index 58924d03bc..0000000000 --- a/core/src/wheels/tests/migrator/migration/renametable.cfc +++ /dev/null @@ -1,27 +0,0 @@ -component extends="wheels.tests.Test" { - - function setup() { - migration = CreateObject("component", "wheels.migrator.Migration").init(); - oldTableName = "dbm_renametable_tests"; - newTableName = "dbm_new_renametable_tests"; - try { - migration.dropTable(newTableName); - } catch (any e) { - } - } - - function test_renameTable_renames_table() { - t = migration.createTable(name = oldTableName, force = true); - t.string(columnNames = "stringcolumn"); - t.create(); - migration.renameTable(oldName = oldTableName, newName = newTableName); - try { - model(newTableName).findAll(); - migration.dropTable(newTableName); - assert("true"); - } catch (any e) { - assert("false"); - } - } - -} diff --git a/core/src/wheels/tests/migrator/migration/updaterecord.cfc b/core/src/wheels/tests/migrator/migration/updaterecord.cfc deleted file mode 100644 index 6230aad868..0000000000 --- a/core/src/wheels/tests/migrator/migration/updaterecord.cfc +++ /dev/null @@ -1,27 +0,0 @@ -component extends="wheels.tests.Test" { - - function setup() { - migration = CreateObject("component", "wheels.migrator.Migration").init(); - } - - function test_updateRecord_updates_a_table_row() { - tableName = "dbm_updaterecord_tests"; - oldValue = "All you need is love"; - newValue = "Love is all you need"; - - t = migration.createTable(name = tableName, force = true); - t.string(columnNames = "lyric"); - t.timeStamps(); - t.create(); - - migration.addRecord(table = tableName, lyric = oldValue); - migration.updateRecord(table = tableName, lyric = newValue, where = "lyric = '#oldValue#'"); - - actual = $query(datasource = application.wheels.dataSourceName, sql = "SELECT lyric FROM #tableName#"); - expected = newValue; - - migration.dropTable(tableName); - assert("actual.lyric eq expected"); - } - -} diff --git a/core/src/wheels/tests/migrator/migrator/adapters.cfc b/core/src/wheels/tests/migrator/migrator/adapters.cfc deleted file mode 100644 index 5e64a0be3b..0000000000 --- a/core/src/wheels/tests/migrator/migrator/adapters.cfc +++ /dev/null @@ -1,12 +0,0 @@ -component extends="wheels.tests.Test" { - - function setup() { - migration = CreateObject("component", "wheels.migrator.Migration").init(); - } - - function test_current_test_environment_returns_an_adapter() { - result = migration.$getDBType(); - assert("len(result)"); - } - -} diff --git a/core/src/wheels/tests/migrator/migrator/getavailablemigrations.cfc b/core/src/wheels/tests/migrator/migrator/getavailablemigrations.cfc deleted file mode 100644 index 874336ab89..0000000000 --- a/core/src/wheels/tests/migrator/migrator/getavailablemigrations.cfc +++ /dev/null @@ -1,19 +0,0 @@ -component extends="wheels.tests.Test" { - - function setup() { - migrator = CreateObject("component", "wheels.Migrator").init( - migratePath = "/wheels/tests/_assets/migrator/migrations/" - ); - } - - function test_getAvailableMigrations_returns_expected_value() { - available = migrator.getAvailableMigrations(); - actual = ""; - for (local.i in available) { - actual = ListAppend(actual, local.i.version); - }; - expected = "001,002,003"; - assert("actual eq expected"); - } - -} diff --git a/core/src/wheels/tests/migrator/migrator/getcurrentmigrationversion.cfc b/core/src/wheels/tests/migrator/migrator/getcurrentmigrationversion.cfc deleted file mode 100644 index 08c54861d9..0000000000 --- a/core/src/wheels/tests/migrator/migrator/getcurrentmigrationversion.cfc +++ /dev/null @@ -1,34 +0,0 @@ -component extends="wheels.tests.Test" { - - include "helpers.cfm"; - - function setup() { - migration = CreateObject("component", "wheels.migrator.Migration").init(); - migrator = CreateObject("component", "wheels.Migrator").init( - migratePath = "/wheels/tests/_assets/migrator/migrations/", - sqlPath = "/wheels/tests/_assets/migrator/sql/" - ); - for ( - local.table in [ - "c_o_r_e_bunyips", - "c_o_r_e_dropbears", - "c_o_r_e_hoopsnakes" - ] - ) { - migration.dropTable(local.table); - }; - deleteMigratorVersions(2); - } - - function teardown() { - $cleanSqlDirectory(); - } - - function test_getCurrentMigrationVersion_returns_expected_value() { - expected = "002"; - migrator.migrateTo(expected); - actual = migrator.getCurrentMigrationVersion(); - assert("actual eq expected"); - } - -} diff --git a/core/src/wheels/tests/migrator/migrator/helpers.cfm b/core/src/wheels/tests/migrator/migrator/helpers.cfm deleted file mode 100644 index 8ced4dde1e..0000000000 --- a/core/src/wheels/tests/migrator/migrator/helpers.cfm +++ /dev/null @@ -1,21 +0,0 @@ - -public void function deleteMigratorVersions(required numeric levelId) { - queryExecute( - "DELETE FROM c_o_r_e_migrator_versions WHERE core_level = :levelId", - { - levelId = { - value = arguments.levelId, - cfsqltype = "cf_sql_integer" - } - }, - { datasource = application.wheels.dataSourceName } - ); -} - -public any function $cleanSqlDirectory() { - local.path = migrator.paths.sql; - if (DirectoryExists(local.path)) { - DirectoryDelete(local.path, true); - } -} - diff --git a/core/src/wheels/tests/migrator/migrator/migrateto.cfc b/core/src/wheels/tests/migrator/migrator/migrateto.cfc deleted file mode 100644 index a036c5d19c..0000000000 --- a/core/src/wheels/tests/migrator/migrator/migrateto.cfc +++ /dev/null @@ -1,107 +0,0 @@ -component extends="wheels.tests.Test" { - - include "helpers.cfm"; - - function setup() { - migration = CreateObject("component", "wheels.migrator.Migration").init(); - migrator = CreateObject("component", "wheels.Migrator").init( - migratePath = "/wheels/tests/_assets/migrator/migrations/", - sqlPath = "/wheels/tests/_assets/migrator/sql/" - ); - for (local.table in ["c_o_r_e_bunyips", "c_o_r_e_dropbears", "c_o_r_e_hoopsnakes", "migrations"]) { - migration.dropTable(local.table); - }; - deleteMigratorVersions(2); - $cleanSqlDirectory(); - originalWriteMigratorSQLFiles = Duplicate(application.wheels.writeMigratorSQLFiles); - originalMigratorTableName = Duplicate(application.wheels.migratorTableName); - } - - function teardown() { - $cleanSqlDirectory(); - // revert to original values - application.wheels.writeMigratorSQLFiles = originalWriteMigratorSQLFiles; - application.wheels.migratorTableName = originalMigratorTableName; - } - - function test_migrateto_migrate_up_from_0_to_001() { - migrator.migrateTo(001); - info = $dbinfo(datasource = application.wheels.dataSourceName, type = "tables", pattern = "c_o_r_e_bunyips"); - - actual = ValueList(info.table_name); - expected = "c_o_r_e_bunyips"; - assert("ListFindNoCase(actual, expected)", "expected", "actual"); - } - - // Adding pattern for speed - function test_migrateto_migrate_up_from_0_to_003() { - migrator.migrateTo(003); - info1 = $dbinfo(datasource = application.wheels.dataSourceName, type = "tables", pattern = "c_o_r_e_bunyips"); - info2 = $dbinfo(datasource = application.wheels.dataSourceName, type = "tables", pattern = "c_o_r_e_dropbears"); - info3 = $dbinfo(datasource = application.wheels.dataSourceName, type = "tables", pattern = "c_o_r_e_hoopsnakes"); - actual1 = ValueList(info1.table_name); - actual2 = ValueList(info2.table_name); - actual3 = ValueList(info3.table_name); - - assert("ListFindNoCase(actual1, 'c_o_r_e_bunyips')"); - assert("ListFindNoCase(actual2, 'c_o_r_e_dropbears')"); - assert("ListFindNoCase(actual3, 'c_o_r_e_hoopsnakes')"); - } - - function test_migrateto_migrate_down_from_003_to_001() { - migrator.migrateTo(003); - migrator.migrateTo(001); - info1 = $dbinfo(datasource = application.wheels.dataSourceName, type = "tables", pattern = "c_o_r_e_bunyips"); - info2 = $dbinfo(datasource = application.wheels.dataSourceName, type = "tables", pattern = "c_o_r_e_dropbears"); - info3 = $dbinfo(datasource = application.wheels.dataSourceName, type = "tables", pattern = "c_o_r_e_hoopsnakes"); - - actual1 = ValueList(info1.table_name); - actual2 = ValueList(info2.table_name); - actual3 = ValueList(info3.table_name); - - assert("ListFindNoCase(actual1, 'c_o_r_e_bunyips')"); - assert("!ListFindNoCase(actual2, 'c_o_r_e_dropbears')"); - assert("!ListFindNoCase(actual3, 'c_o_r_e_hoopsnakes')"); - } - - function test_migrateto_generates_sql_files() { - application.wheels.writeMigratorSQLFiles = true; - - migrator.migrateTo(002); - migrator.migrateTo(001); - - for ( - i in [ - "001_create_bunyips_table_up.sql", - "002_create_dropbears_table_up.sql", - "002_create_dropbears_table_down.sql" - ] - ) { - actual = FileRead(migrator.paths.sql & i); - if (i contains "_up.sql") { - expected = "CREATE TABLE"; - } else { - expected = "DROP TABLE"; - } - assert("actual contains expected"); - }; - } - - function test_migrateto_migrate_up_does_not_generate_sql_file() { - migrator.migrateTo(001); - assert("!DirectoryExists(migrator.paths.sql)"); - } - - function test_migrateto_uses_specified_versions_table_name() { - tableName = "c_o_r_e_migrator_versions"; - application.wheels.migratorTableName = tableName; - - migrator.migrateTo(001); - - actual = $dbinfo(datasource = application.wheels.dataSourceName, type = "columns", table = tableName); - expected = "version"; - - assert("actual.column_name eq expected"); - } - -} diff --git a/core/src/wheels/tests/migrator/migrator/redomigration.cfc b/core/src/wheels/tests/migrator/migrator/redomigration.cfc deleted file mode 100644 index fd63ae2d24..0000000000 --- a/core/src/wheels/tests/migrator/migrator/redomigration.cfc +++ /dev/null @@ -1,62 +0,0 @@ -component extends="wheels.tests.Test" { - - include "helpers.cfm"; - - function setup() { - migration = CreateObject("component", "wheels.migrator.Migration").init(); - migrator = CreateObject("component", "wheels.migrator").init( - migratePath = "/wheels/tests/_assets/migrator/migrations/", - sqlPath = "/wheels/tests/_assets/migrator/sql/" - ); - tableName = "c_o_r_e_bunyips"; - - migration.dropTable(tableName); - t = migration.createTable(name = tableName); - t.string( - columnNames = "name", - default = "", - allowNull = true, - limit = 255 - ); - t.create(); - migration.removeRecord(table = "c_o_r_e_migrator_versions"); - migration.addRecord(table = "c_o_r_e_migrator_versions", version = "001"); - - $cleanSqlDirectory(); - } - - function teardown() { - migration.dropTable(tableName); - $cleanSqlDirectory(); - } - - // add a new column and redo the migration - // NOTE: this test passes when run individually, but new column is not created when run - // as part of the migrator test packing - function _test_redomigration_001() { - local.path = ExpandPath("/wheels/tests/_assets/migrator/migrations/001_create_bunyips_table.cfc"); - local.originalColumnNames = 'columnNames="name"'; - local.newColumnNames = 'columnNames="name,hobbies"'; - local.originalContent = FileRead(local.path); - local.newContent = ReplaceNoCase( - local.originalContent, - local.originalColumnNames, - local.newColumnNames, - "one" - ); - - FileDelete(local.path); - FileWrite(local.path, local.newContent); - - migrator.redoMigration(001); - info = $dbinfo(datasource = application.wheels.dataSourceName, type = "columns", table = tableName); - - FileDelete(local.path); - FileWrite(local.path, local.originalContent); - - actual = ValueList(info.column_name); - assert("ListFindNoCase(actual, 'name')", "actual"); - assert("ListFindNoCase(actual, 'hobbies')", "actual"); - } - -} diff --git a/core/src/wheels/tests/model/associations/belongsTo.cfc b/core/src/wheels/tests/model/associations/belongsTo.cfc deleted file mode 100644 index ffca14e8cb..0000000000 --- a/core/src/wheels/tests/model/associations/belongsTo.cfc +++ /dev/null @@ -1,41 +0,0 @@ -component extends="wheels.tests.Test" { - - function test_getting_parent() { - obj = model("post").findOne(order = "id"); - dynamicResult = obj.author(); - coreResult = model("author").findByKey(obj.authorId); - assert("dynamicResult.key() IS coreResult.key()"); - dynamicResult = obj.author(select = "lastName", returnAs = "query"); - coreResult = model("author").findByKey(key = obj.authorId, select = "lastName", returnAs = "query"); - assert( - "IsQuery(dynamicResult) AND ListLen(dynamicResult.columnList) IS 1 AND IsQuery(coreResult) AND ListLen(coreResult.columnList) IS 1 AND dynamicResult.lastName IS coreResult.lastName" - ); - } - - function test_checking_if_parent_exists() { - obj = model("post").findOne(order = "id"); - dynamicResult = obj.hasAuthor(); - coreResult = model("author").exists(obj.authorId); - assert("dynamicResult IS coreResult"); - } - - function test_getting_parent_on_new_object() { - authorByFind = model("author").findOne(order = "id"); - newPost = model("post").new(authorId = authorByFind.id); - authorByAssociation = newPost.author(); - assert("authorByFind.key() IS authorByAssociation.key()"); - } - - function test_checking_if_parent_exists_on_new_object() { - authorByFind = model("author").findOne(order = "id"); - newPost = model("post").new(authorId = authorByFind.id); - authorExistsByAssociation = newPost.hasAuthor(); - assert("authorExistsByAssociation IS true"); - } - - function test_getting_parent_with_join_key() { - obj = model("author").findOne(order = "id", include = "user"); - assert('obj.firstName eq obj.user.firstName'); - } - -} diff --git a/core/src/wheels/tests/model/associations/hasMany.cfc b/core/src/wheels/tests/model/associations/hasMany.cfc deleted file mode 100644 index 85d018b8e2..0000000000 --- a/core/src/wheels/tests/model/associations/hasMany.cfc +++ /dev/null @@ -1,242 +0,0 @@ -component extends="wheels.tests.Test" { - - function test_getting_children() { - author = model("author").findOne(order = "id"); - dynamicResult = author.posts(); - coreResult = model("post").findAll(where = "authorId=#author.id#"); - assert("dynamicResult['title'][1] IS coreResult['title'][1]"); - } - - function test_getting_children_with_include() { - author = model("author").findOne(order = "id", include = "posts"); - assert("IsObject(author) && ArrayLen(author.posts) eq 3"); - author = model("author").findOne(order = "id", include = "posts", returnAs = "query"); - assert("author.recordcount eq 3"); - } - - function test_counting_children() { - author = model("author").findOne(order = "id"); - dynamicResult = author.postCount(); - coreResult = model("post").count(where = "authorId=#author.id#"); - assert("dynamicResult IS coreResult"); - } - - function test_checking_if_children_exist() { - author = model("author").findOne(order = "id"); - dynamicResult = author.hasPosts(); - coreResult = model("post").exists(where = "authorId=#author.id#"); - assert("dynamicResult IS coreResult"); - } - - function test_getting_one_child() { - author = model("author").findOne(order = "id"); - dynamicResult = author.findOnePost(); - coreResult = model("post").findOne(where = "authorId=#author.id#"); - assert("dynamicResult.title IS coreResult.title"); - } - - function test_adding_child_by_setting_foreign_key() { - author = model("author").findOne(order = "id"); - post = model("post").findOne(order = "id DESC"); - transaction { - author.addPost(post = post, transaction = "none"); - /* we need to test if authorId is set on the post object as well and not just in the database!*/ - post.reload(); - transaction action="rollback"; - } - assert("author.id IS post.authorId"); - post.reload(); - transaction { - author.addPost(key = post.id, transaction = "none"); - post.reload(); - transaction action="rollback"; - } - assert("author.id IS post.authorId"); - post.reload(); - transaction { - model("post").updateByKey(key = post.id, authorId = author.id, transaction = "none"); - post.reload(); - transaction action="rollback"; - } - assert("author.id IS post.authorId"); - } - - function test_removing_child_by_nullifying_foreign_key() { - author = model("author").findOne(order = "id"); - post = model("post").findOne(order = "id DESC"); - transaction { - author.removePost(post = post, transaction = "none"); - /* we need to test if authorId is set to blank on the post object as well and not just in the database!*/ - post.reload(); - transaction action="rollback"; - } - assert("post.authorId IS ''"); - post.reload(); - transaction { - author.removePost(key = post.id, transaction = "none"); - post.reload(); - transaction action="rollback"; - } - assert("post.authorId IS ''"); - post.reload(); - transaction { - model("post").updateByKey(key = post.id, authorId = "", transaction = "none"); - post.reload(); - transaction action="rollback"; - } - assert("post.authorId IS ''"); - } - - function test_deleting_child() { - author = model("author").findOne(order = "id"); - post = model("post").findOne(order = "id DESC"); - transaction { - author.deletePost(post = post, transaction = "none"); - /* should we also set post to false here? */ - assert("NOT model('post').exists(post.id)"); - transaction action="rollback"; - } - transaction { - author.deletePost(key = post.id, transaction = "none"); - assert("NOT model('post').exists(post.id)"); - transaction action="rollback"; - } - transaction { - model("post").deleteByKey(key = post.id, transaction = "none"); - assert("NOT model('post').exists(post.id)"); - transaction action="rollback"; - } - } - - function test_removing_all_children_by_nullifying_foreign_keys() { - author = model("author").findOne(order = "id"); - transaction { - author.removeAllPosts(transaction = "none"); - dynamicResult = author.postCount(); - remainingCount = model("post").count(); - transaction action="rollback"; - } - transaction { - model("post").updateAll(authorId = "", where = "authorId=#author.id#", transaction = "none"); - coreResult = author.postCount(); - transaction action="rollback"; - } - assert("dynamicResult IS 0 AND coreResult IS 0 AND remainingCount IS 5"); - } - - function test_deleting_all_children() { - author = model("author").findOne(order = "id"); - transaction { - author.deleteAllPosts(transaction = "none"); - dynamicResult = author.postCount(); - remainingCount = model("post").count(); - transaction action="rollback"; - } - transaction { - model("post").deleteAll(where = "authorId=#author.id#", transaction = "none"); - coreResult = author.postCount(); - transaction action="rollback"; - } - assert("dynamicResult IS 0 AND coreResult IS 0 AND remainingCount IS 2"); - } - - function test_creating_new_child() { - author = model("author").findOne(order = "id"); - newPost = author.newPost(title = "New Title"); - dynamicResult = newPost.authorId; - newPost = model("post").new(authorId = author.id, title = "New Title"); - coreResult = newPost.authorId; - assert("dynamicResult IS coreResult"); - } - - function test_creating_new_child_and_saving_it() { - author = model("author").findOne(order = "id"); - transaction { - newPost = author.createPost(title = "New Title", body = "New Body", transaction = "none"); - dynamicResult = newPost.authorId; - transaction action="rollback"; - } - transaction { - newPost = model("post").create( - authorId = author.id, - title = "New Title", - body = "New Body", - transaction = "none" - ); - coreResult = newPost.authorId; - transaction action="rollback"; - } - assert("dynamicResult IS coreResult"); - } - - function test_dependency_delete() { - transaction { - postWithAuthor = model("post").findOne(order = "id"); - author = model("author").findByKey(key = postWithAuthor.authorId); - author.hasMany(name = "posts", dependent = "delete"); - author.delete(); - posts = model("post").findAll(where = "authorId=#author.id#"); - transaction action="rollback"; - } - assert("posts.recordcount eq 0"); - } - - function test_dependency_deleteAll() { - transaction { - postWithAuthor = model("post").findOne(order = "id"); - author = model("author").findByKey(key = postWithAuthor.authorId); - author.hasMany(name = "posts", dependent = "deleteAll"); - author.delete(); - posts = model("post").findAll(where = "authorId=#author.id#"); - transaction action="rollback"; - } - assert("posts.recordcount eq 0"); - } - - function test_dependency_removeAll() { - transaction { - postWithAuthor = model("post").findOne(order = "id"); - author = model("author").findByKey(key = postWithAuthor.authorId); - author.hasMany(name = "posts", dependent = "removeAll"); - author.delete(); - posts = model("post").findAll(where = "authorId=#author.id#"); - transaction action="rollback"; - } - assert("posts.recordcount eq 0"); - } - - function test_getting_children_with_join_key() { - obj = model("user").findOne(order = "id", include = "authors"); - assert('obj.firstName eq obj.authors[1].firstName'); - } - - function test_calculated_property_without_distinct() { - authors = model("author").findAll(select = "id, firstName, lastName, numberofitems"); - assert('authors.recordCount IS 10'); - } - - function test_select_aggregate_calculated_property_with_distinct() { - authors = model("author").findAll(select = "id, firstName, lastName, numberofitems", distinct = true); - assert('authors.recordCount IS 10'); - } - - function test_aggregate_calculated_property_with_distinct() { - posts = model("post").findAll(select = "id, authorId, firstId", distinct = true); - assert('posts.recordCount IS 5'); - } - - function test_non_aggregate_calculated_property_with_distinct() { - posts = model("post").findAll(select = "id, authorId, titleAlias", distinct = true); - assert('posts.recordCount IS 5'); - } - - function test_calculated_properties_with_included_model_with_distinct() { - authors = model("author").findAll( - select = "id, firstName, lastName, numberofitems, titlealias", - include = "posts", - distinct = true - ); - assert('authors.recordCount IS 13'); - } - -} diff --git a/core/src/wheels/tests/model/associations/hasOne.cfc b/core/src/wheels/tests/model/associations/hasOne.cfc deleted file mode 100644 index 6a23066b37..0000000000 --- a/core/src/wheels/tests/model/associations/hasOne.cfc +++ /dev/null @@ -1,100 +0,0 @@ -component extends="wheels.tests.Test" { - - function test_getting_child() { - author = model("author").findOne(order = "id"); - dynamicResult = author.profile(); - coreResult = model("profile").findOne(where = "authorId=#author.id#"); - assert("dynamicResult.bio IS coreResult.bio"); - } - - function test_checking_if_child_exist() { - author = model("author").findOne(order = "id"); - dynamicResult = author.hasProfile(); - coreResult = model("profile").exists(where = "authorId=#author.id#"); - assert("dynamicResult IS coreResult"); - } - - function test_adding_child_by_setting_foreign_key() { - author = model("author").findOne(order = "id DESC"); - profile = model("profile").findOne(order = "id"); - transaction { - author.setProfile(profile = profile, transaction = "none"); - profile.reload(); - transaction action="rollback"; - } - assert("author.id IS profile.authorId"); - profile.reload(); - transaction { - author.setProfile(key = profile.id, transaction = "none"); - profile.reload(); - transaction action="rollback"; - } - assert("author.id IS profile.authorId"); - profile.reload(); - transaction { - model("profile").updateByKey(key = profile.id, authorId = author.id, transaction = "none"); - profile.reload(); - transaction action="rollback"; - } - assert("author.id IS profile.authorId"); - } - - function test_removing_child_by_nullifying_foreign_key() { - author = model("author").findOne(order = "id"); - transaction { - author.removeProfile(transaction = "none"); - assert("model('profile').findOne().authorId IS ''"); - transaction action="rollback"; - } - transaction { - model("profile").updateOne(authorId = "", where = "authorId=#author.id#", transaction = "none"); - assert("model('profile').findOne().authorId IS ''"); - transaction action="rollback"; - } - } - - function test_deleting_child() { - author = model("author").findOne(order = "id"); - profileCount = model("profile").count(); - transaction { - author.deleteProfile(transaction = "none"); - assert("model('profile').count() eq (profileCount - 1)"); - transaction action="rollback"; - } - transaction { - model("profile").deleteOne(where = "authorId=#author.id#", transaction = "none"); - assert("model('profile').count() eq (profileCount - 1)"); - transaction action="rollback"; - } - } - - function test_creating_new_child() { - author = model("author").findOne(order = "id"); - newProfile = author.newProfile(dateOfBirth = "17/12/1981"); - dynamicResult = newProfile.authorId; - newProfile = model("profile").new(authorId = author.id, dateOfBirth = "17/12/1981"); - coreResult = newProfile.authorId; - assert("dynamicResult IS coreResult"); - } - - function test_creating_new_child_and_saving_it() { - author = model("author").findOne(order = "id"); - transaction { - newProfile = author.createProfile(dateOfBirth = "17/12/1981", transaction = "none"); - dynamicResult = newProfile.authorId; - transaction action="rollback"; - } - transaction { - newProfile = model("profile").create(authorId = author.id, dateOfBirth = "17/12/1981", transaction = "none"); - coreResult = newProfile.authorId; - transaction action="rollback"; - } - assert("dynamicResult IS coreResult"); - } - - function test_getting_child_with_join_key() { - obj = model("user").findOne(order = "id", include = "author"); - assert('obj.firstName eq obj.author.firstName'); - } - -} diff --git a/core/src/wheels/tests/model/calculations/average.cfc b/core/src/wheels/tests/model/calculations/average.cfc deleted file mode 100644 index dd138a6b98..0000000000 --- a/core/src/wheels/tests/model/calculations/average.cfc +++ /dev/null @@ -1,68 +0,0 @@ -component extends="wheels.tests.Test" { - - /* integers */ - - function test_average_with_integer() { - result = model("post").average(property = "views"); - assert("result IS 3"); - } - - function test_average_with_integer_with_non_matching_where() { - result = model("post").average(property = "views", where = "id=0"); - assert("result IS ''"); - } - - function test_average_with_integer_with_distinct() { - result = model("post").average(property = "views", distinct = "true"); - assert("DecimalFormat(result) IS DecimalFormat(2.50)"); - } - - function test_average_with_integer_with_ifNull() { - result = model("post").average(property = "views", where = "id=0", ifNull = 0); - assert("result IS 0"); - } - - /* floats */ - - function test_average_with_group() { - if (ListFindNoCase("MySQL,SQLServer", get("adaptername"))) { - result = model("post").average(property = "averageRating", group = "authorId"); - assert("DecimalFormat(result['averageRatingAverage'][1]) IS DecimalFormat(3.40)"); - } else { - assert(true); - } - } - - function test_average_with_float() { - result = model("post").average(property = "averageRating"); - assert("DecimalFormat(result) IS DecimalFormat(3.50)"); - } - - function test_average_with_float_with_non_matching_where() { - result = model("post").average(property = "averageRating", where = "id=0"); - assert("result IS ''"); - } - - function test_average_with_float_with_distinct() { - result = model("post").average(property = "averageRating", distinct = "true"); - assert("DecimalFormat(result) IS DecimalFormat(3.4)"); - } - - function test_average_with_float_with_ifNull() { - result = model("post").average(property = "averageRating", where = "id=0", ifNull = 0); - assert("result IS 0"); - } - - /* include deleted records */ - - function test_average_with_include_soft_deletes() { - transaction action="begin" { - post = model("Post").findOne(where = "views=0"); - post.delete(transaction = "none"); - average = model("Post").average(property = "views", includeSoftDeletes = true); - transaction action="rollback"; - } - assert('average eq 3'); - } - -} diff --git a/core/src/wheels/tests/model/calculations/count.cfc b/core/src/wheels/tests/model/calculations/count.cfc deleted file mode 100644 index faf9fff776..0000000000 --- a/core/src/wheels/tests/model/calculations/count.cfc +++ /dev/null @@ -1,72 +0,0 @@ -component extends="wheels.tests.Test" { - - function test_count() { - result = model("author").count(); - assert("result IS 10"); - } - - function test_count_with_group() { - if (ListFindNoCase("MySQL,SQLServer", get("adaptername"))) { - result = model("post").count(property = "views", group = "authorId"); - assert("result['count'][2] IS 2"); - } else { - assert(true); - } - } - - function test_count_with_include() { - result = model("author").count(include = "posts"); - assert("result IS 10"); - } - - function test_count_with_where() { - result = model("author").count(where = "lastName = 'Djurner'"); - assert("result IS 1"); - } - - function test_count_with_non_matching_where() { - result = model("author").count(where = "id=0"); - assert("result IS 0"); - } - - function test_count_with_non_matching_where_and_include() { - result = model("author").count(where = "id = 0", include = "posts"); - assert("result IS 0"); - } - - function test_count_with_where_and_include() { - result = model("author").count(where = "lastName = 'Djurner' OR lastName = 'Peters'", include = "posts"); - assert("result IS 2"); - } - - function test_count_with_where_on_included_association() { - result = model("author").count( - where = "title LIKE '%first%' OR title LIKE '%second%' OR title LIKE '%fourth%'", - include = "posts" - ); - assert("result IS 2"); - } - - function test_dynamic_count() { - author = model("author").findOne(where = "lastName='Djurner'"); - result = author.postCount(); - assert("result IS 3"); - } - - function test_dynamic_count_with_where() { - author = model("author").findOne(where = "lastName='Djurner'"); - result = author.postCount(where = "title LIKE '%first%' OR title LIKE '%second%'"); - assert("result IS 2"); - } - - function test_count_with_include_soft_deletes() { - transaction action="begin" { - post = model("Post").findOne(where = "views=0"); - post.delete(transaction = "none"); - count = model("Post").count(property = "views", includeSoftDeletes = true); - transaction action="rollback"; - } - assert('count eq 5'); - } - -} diff --git a/core/src/wheels/tests/model/calculations/maximum.cfc b/core/src/wheels/tests/model/calculations/maximum.cfc deleted file mode 100644 index c26b3ddf0a..0000000000 --- a/core/src/wheels/tests/model/calculations/maximum.cfc +++ /dev/null @@ -1,41 +0,0 @@ -component extends="wheels.tests.Test" { - - function test_maximum() { - result = model("post").maximum(property = "views"); - assert("result IS 5"); - } - - function test_maximum_with_group() { - if (ListFindNoCase("MySQL,SQLServer", get("adaptername"))) { - result = model("post").maximum(property = "views", group = "authorId"); - assert("result['viewsMaximum'][1] IS 5"); - } else { - assert(true); - } - } - - function test_maximum_with_where() { - result = model("post").maximum(property = "views", where = "title LIKE 'Title%'"); - assert("result IS 5"); - } - - function test_maximum_with_non_matching_where() { - result = model("post").maximum(property = "views", where = "id=0"); - assert("result IS ''"); - } - - function test_maximum_with_ifNull() { - result = model("post").maximum(property = "views", where = "id=0", ifNull = 0); - assert("result IS 0"); - } - - function test_maximum_with_include_soft_deletes() { - transaction action="begin" { - post = model("Post").deleteAll(where = "views=5", transaction = "none"); - maximum = model("Post").maximum(property = "views", includeSoftDeletes = true); - transaction action="rollback"; - } - assert('maximum eq 5'); - } - -} diff --git a/core/src/wheels/tests/model/calculations/minimum.cfc b/core/src/wheels/tests/model/calculations/minimum.cfc deleted file mode 100644 index 795d4e1a39..0000000000 --- a/core/src/wheels/tests/model/calculations/minimum.cfc +++ /dev/null @@ -1,36 +0,0 @@ -component extends="wheels.tests.Test" { - - function test_minimum() { - result = model("post").minimum(property = "views"); - assert("result IS 0"); - } - - function test_minimum_with_group() { - if (ListFindNoCase("MySQL,SQLServer", get("adaptername"))) { - result = model("post").minimum(property = "views", group = "authorId"); - assert("result['viewsMinimum'][2] IS 2"); - } else { - assert(true); - } - } - - function test_minimum_with_non_matching_where() { - result = model("post").minimum(property = "views", where = "id=0"); - assert("result IS ''"); - } - - function test_minimum_with_ifNull() { - result = model("post").minimum(property = "views", where = "id=0", ifNull = 0); - assert("result IS 0"); - } - - function test_minimum_with_include_soft_deletes() { - transaction action="begin" { - post = model("Post").deleteAll(where = "views=0", transaction = "none"); - minimum = model("Post").minimum(property = "views", includeSoftDeletes = true); - transaction action="rollback"; - } - assert('minimum eq 0'); - } - -} diff --git a/core/src/wheels/tests/model/calculations/sum.cfc b/core/src/wheels/tests/model/calculations/sum.cfc deleted file mode 100644 index f1aa878f45..0000000000 --- a/core/src/wheels/tests/model/calculations/sum.cfc +++ /dev/null @@ -1,74 +0,0 @@ -component extends="wheels.tests.Test" { - - function test_sum() { - result = model("post").sum(property = "views"); - assert("result IS 15"); - } - - function test_sum_with_group() { - if (ListFindNoCase("MySQL,SQLServer", get("adaptername"))) { - result = model("post").sum(property = "views", group = "authorId"); - assert("result['viewsSum'][2] IS 5"); - } else { - assert(true); - } - } - - function test_sum_with_group_on_associated_model() { - if (ListFindNoCase("MySQL,SQLServer", get("adaptername"))) { - result = model("post").sum(property = "views", include = "author", group = "lastName"); - assert("result['viewsSum'][2] IS 5"); - } else { - assert(true); - } - } - - function test_sum_with_group_on_calculated_property() { - if (ListFindNoCase("MySQL,SQLServer", get("adaptername"))) { - result = model("photo").sum(property = "galleryId", group = "DESCRIPTION1"); - assert("result.recordcount IS 250"); - } else { - assert(true); - } - } - - function test_sum_with_group_on_calculated_property_on_associated_model() { - if (ListFindNoCase("MySQL,SQLServer", get("adaptername"))) { - result = model("gallery").sum(property = "userId", include = "photos", group = "DESCRIPTION1"); - assert("result.recordcount IS 250"); - } else { - assert(true); - } - } - - function test_sum_with_where() { - author = model("author").findOne(where = "lastName='Djurner'"); - result = model("post").sum(property = "views", where = "authorid=#author.id#"); - assert("result IS 10"); - } - - function test_sum_with_non_matching_where() { - result = model("post").sum(property = "views", where = "id=0"); - assert("result IS ''"); - } - - function test_sum_with_distinct() { - result = model("post").sum(property = "views", distinct = true); - assert("result IS 10"); - } - - function test_sum_with_ifNull() { - result = model("post").sum(property = "views", where = "id=0", ifNull = 0); - assert("result IS 0"); - } - - function test_sum_with_include_soft_deletes() { - transaction action="begin" { - post = model("Post").deleteAll(transaction = "none"); - sum = model("Post").sum(property = "views", includeSoftDeletes = true); - transaction action="rollback"; - } - assert('sum eq 15'); - } - -} \ No newline at end of file diff --git a/core/src/wheels/tests/model/callbacks/afterDelete.cfc b/core/src/wheels/tests/model/callbacks/afterDelete.cfc deleted file mode 100644 index cf06d03307..0000000000 --- a/core/src/wheels/tests/model/callbacks/afterDelete.cfc +++ /dev/null @@ -1,28 +0,0 @@ -component extends="wheels.tests.Test" { - - function setup() { - model("tag").$registerCallback(type = "afterDelete", methods = "callbackThatSetsProperty"); - obj = model("tag").findOne(); - } - - function teardown() { - model("tag").$clearCallbacks(type = "afterDelete"); - } - - function test_existing_object() { - transaction { - obj.delete(transaction = "none"); - transaction action="rollback"; - } - assert("StructKeyExists(obj, 'setByCallback')"); - } - - function test_existing_object_with_skipped_callback() { - transaction { - obj.delete(transaction = "none", callbacks = "false"); - transaction action="rollback"; - } - assert("NOT StructKeyExists(obj, 'setByCallback')"); - } - -} diff --git a/core/src/wheels/tests/model/callbacks/afterFind.cfc b/core/src/wheels/tests/model/callbacks/afterFind.cfc deleted file mode 100644 index d1f161a706..0000000000 --- a/core/src/wheels/tests/model/callbacks/afterFind.cfc +++ /dev/null @@ -1,76 +0,0 @@ -component extends="wheels.tests.Test" { - - function setup() { - model("post").$registerCallback(type = "afterFind", methods = "afterFindCallback"); - } - - function teardown() { - model("post").$clearCallbacks(type = "afterFind"); - } - - function test_property_named_method_should_not_clash_with_cfinvoke() { - results = model("collisionTest").findAll(returnAs = "objects"); - assert("results[1].method IS 'done'"); - } - - function test_setting_one_query_record() { - posts = model("post").findAll(maxRows = 1, order = "id DESC"); - assert("posts.views[1] IS 102 AND posts['title'][1] IS 'setTitle'"); - } - - function test_setting_one_query_record_with_skipped_callback() { - posts = model("post").findAll(maxRows = 1, order = "id DESC"); - assert("posts.views[1] IS 102 AND posts['title'][1] IS 'setTitle'"); - } - - function test_setting_multiple_query_records() { - posts = model("post").findAll(order = "id DESC"); - assert("posts.views[1] IS 102 AND posts.views[2] IS 103 AND posts['title'][1] IS 'setTitle'"); - } - - function test_setting_multiple_query_records_with_skipped_callback() { - posts = model("post").findAll(order = "id DESC", callbacks = false); - assert("posts.views[1] IS '2' AND posts.views[2] IS '3' AND posts.title[1] IS 'Title for fifth test post'"); - } - - function test_setting_property_on_one_object() { - post = model("post").findOne(); - assert("post.title IS 'setTitle'"); - } - - function test_setting_property_on_one_object_with_skipped_callback() { - post = model("post").findOne(callbacks = false, order = "id"); - assert("post.title IS 'Title for first test post'"); - } - - function test_setting_properties_on_multiple_objects() { - posts = model("post").findAll(returnAs = "objects"); - assert("posts[1].title IS 'setTitle' AND posts[2].title IS 'setTitle'"); - } - - function test_setting_properties_on_multiple_objects_with_skipped_callback() { - posts = model("post").findAll(returnAs = "objects", callbacks = false, order = "id"); - assert("posts[1].title IS 'Title for first test post' AND posts[2].title IS 'Title for second test post'"); - } - - function test_creation_of_new_column_and_property() { - posts = model("post").findAll(order = "id DESC"); - assert("posts.something[1] eq 'hello world'"); - posts = model("post").findAll(returnAs = "objects"); - assert("posts[1].something eq 'hello world'"); - } - - /* issue 329 - function test_creation_of_new_column_and_property_on_included_model() { - posts = model("author").findAll(include="posts"); - assert("posts.something[1] eq 'hello world'"); - posts = model("author").findAll(include="posts", returnAs="objects"); - assert("posts[1].something eq 'hello world'"); - } */ - - function test_getting_columns_added_when_returnAs_is_struct() { - posts = model("post").findAll(returnAs = "struct"); - assert("posts[1].something eq 'hello world'"); - } - -} diff --git a/core/src/wheels/tests/model/callbacks/afterFindNonLegacy.cfc b/core/src/wheels/tests/model/callbacks/afterFindNonLegacy.cfc deleted file mode 100644 index 60dc56b42b..0000000000 --- a/core/src/wheels/tests/model/callbacks/afterFindNonLegacy.cfc +++ /dev/null @@ -1,34 +0,0 @@ -component extends="wheels.tests.Test" { - - function setup() { - model("post").$registerCallback(type = "afterFind", methods = "afterFindCallback"); - } - - function teardown() { - model("post").$clearCallbacks(type = "afterFind"); - } - - function test_setting_property_on_one_object() { - post = model("post").findOne(); - assert("post.title IS 'setTitle'"); - } - - function test_setting_properties_on_multiple_objects() { - postsOrg = model("post").findAll(returnAs = "objects", callbacks = "false", orderby = "views DESC"); - views1 = postsOrg[1].views + 100; - views2 = postsOrg[2].views + 100; - posts = model("post").findAll(returnAs = "objects", orderby = "views DESC"); - assert("posts[1].title IS 'setTitle'"); - assert("posts[2].title IS 'setTitle'"); - assert("posts[1].views EQ views1"); - assert("posts[2].views EQ views2"); - } - - function test_creation_of_new_column_and_property() { - posts = model("post").findAll(order = "id DESC"); - assert("posts.something[1] eq 'hello world'"); - posts = model("post").findAll(returnAs = "objects"); - assert("posts[1].something eq 'hello world'"); - } - -} diff --git a/core/src/wheels/tests/model/callbacks/afterSaveProperties.cfc b/core/src/wheels/tests/model/callbacks/afterSaveProperties.cfc deleted file mode 100644 index f005e8cd49..0000000000 --- a/core/src/wheels/tests/model/callbacks/afterSaveProperties.cfc +++ /dev/null @@ -1,28 +0,0 @@ -component extends="wheels.tests.Test" { - - function test_have_access_to_changed_property_values_in_aftersave() { - model("user").$registerCallback(type = "afterSave", methods = "saveHasChanged"); - obj = model("user").findOne(where = "username = 'tonyp'"); - obj.saveHasChanged = saveHasChanged; - obj.getHasObjectChanged = getHasObjectChanged; - assert('obj.hasChanged() eq false'); - obj.password = "xxxxxxx"; - assert('obj.hasChanged() eq true'); - transaction { - obj.save(transaction = "none"); - assert('obj.getHasObjectChanged() eq true'); - assert('obj.hasChanged() eq false'); - transaction action="rollback"; - } - model("user").$clearCallbacks(type = "afterSave"); - } - - function saveHasChanged() { - hasObjectChanged = hasChanged(); - } - - function getHasObjectChanged() { - return hasObjectChanged; - } - -} diff --git a/core/src/wheels/tests/model/callbacks/afterSaving.cfc b/core/src/wheels/tests/model/callbacks/afterSaving.cfc deleted file mode 100644 index dfea6abbd2..0000000000 --- a/core/src/wheels/tests/model/callbacks/afterSaving.cfc +++ /dev/null @@ -1,36 +0,0 @@ -component extends="wheels.tests.Test" { - - function setup() { - model("tag").$registerCallback(type = "afterValidation", methods = "callbackThatIncreasesVariable"); - model("tag").$registerCallback(type = "afterValidationOnCreate", methods = "callbackThatIncreasesVariable"); - model("tag").$registerCallback(type = "afterValidationOnUpdate", methods = "callbackThatIncreasesVariable"); - model("tag").$registerCallback(type = "afterSave", methods = "callbackThatIncreasesVariable"); - model("tag").$registerCallback(type = "afterCreate", methods = "callbackThatIncreasesVariable"); - model("tag").$registerCallback(type = "afterUpdate", methods = "callbackThatIncreasesVariable"); - obj = model("tag").findOne(); - obj.name = "somethingElse"; - } - - function teardown() { - model("tag").$clearCallbacks( - type = "afterValidation,afterValidationOnCreate,afterValidationOnUpdate,afterSave,afterCreate,afterUpdate" - ); - } - - function test_chain_when_saving_existing_object() { - transaction { - obj.save(transaction = "none"); - transaction action="rollback"; - } - assert("obj.callbackCount IS 4"); - } - - function test_chain_when_saving_existing_object_with_all_callbacks_skipped() { - transaction { - obj.save(transaction = "none", callbacks = false); - transaction action="rollback"; - } - assert("NOT StructKeyExists(obj, 'callbackCount')"); - } - -} diff --git a/core/src/wheels/tests/model/callbacks/beforeCreate.cfc b/core/src/wheels/tests/model/callbacks/beforeCreate.cfc deleted file mode 100644 index 8053692111..0000000000 --- a/core/src/wheels/tests/model/callbacks/beforeCreate.cfc +++ /dev/null @@ -1,28 +0,0 @@ -component extends="wheels.tests.Test" { - - function setup() { - model("tag").$registerCallback( - type = "beforeCreate", - methods = "callbackThatSetsProperty, callbackThatReturnsFalse" - ); - } - - function teardown() { - model("tag").$clearCallbacks(type = "beforeCreate"); - } - - function test_new_object() { - obj = model("tag").create(); - assert("StructKeyExists(obj, 'setByCallback')"); - } - - function test_new_object_with_skipped_callback() { - obj = model("tag").create( - name = "mustSetAtLeastOnePropertyOrCreateFails", - transaction = "rollback", - callbacks = false - ); - assert("NOT StructKeyExists(obj, 'setByCallback')"); - } - -} diff --git a/core/src/wheels/tests/model/callbacks/beforeCreateAndbeforeUpdate.cfc b/core/src/wheels/tests/model/callbacks/beforeCreateAndbeforeUpdate.cfc deleted file mode 100644 index 4fedb04c4b..0000000000 --- a/core/src/wheels/tests/model/callbacks/beforeCreateAndbeforeUpdate.cfc +++ /dev/null @@ -1,23 +0,0 @@ -component extends="wheels.tests.Test" { - - function teardown() { - model("tag").$clearCallbacks(type = "beforeCreate,beforeUpdate"); - } - - function test_existing_object() { - model("tag").$registerCallback(type = "beforeCreate", methods = "callbackThatSetsProperty"); - model("tag").$registerCallback(type = "beforeUpdate", methods = "callbackThatReturnsFalse"); - obj = model("tag").findOne(); - obj.name = "somethingElse"; - obj.save(); - assert("NOT StructKeyExists(obj, 'setByCallback')"); - } - - function test_new_object() { - model("tag").$registerCallback(type = "beforeUpdate", methods = "callbackThatSetsProperty"); - model("tag").$registerCallback(type = "beforeCreate", methods = "callbackThatReturnsFalse"); - obj = model("tag").create(); - assert("NOT StructKeyExists(obj, 'setByCallback')"); - } - -} diff --git a/core/src/wheels/tests/model/callbacks/beforeDelete.cfc b/core/src/wheels/tests/model/callbacks/beforeDelete.cfc deleted file mode 100644 index ad086d0470..0000000000 --- a/core/src/wheels/tests/model/callbacks/beforeDelete.cfc +++ /dev/null @@ -1,22 +0,0 @@ -component extends="wheels.tests.Test" { - - function setup() { - model("tag").$registerCallback(type = "beforeDelete", methods = "callbackThatSetsProperty,callbackThatReturnsFalse"); - obj = model("tag").findOne(); - } - - function teardown() { - model("tag").$clearCallbacks(type = "beforeDelete"); - } - - function test_existing_object() { - obj.delete(); - assert("StructKeyExists(obj, 'setByCallback')"); - } - - function test_existing_object_with_skipped_callback() { - obj.delete(callbacks = false, transaction = "rollback"); - assert("NOT StructKeyExists(obj, 'setByCallback')"); - } - -} diff --git a/core/src/wheels/tests/model/callbacks/beforeSave.cfc b/core/src/wheels/tests/model/callbacks/beforeSave.cfc deleted file mode 100644 index 44c9a4f4e8..0000000000 --- a/core/src/wheels/tests/model/callbacks/beforeSave.cfc +++ /dev/null @@ -1,78 +0,0 @@ -component extends="wheels.tests.Test" { - - function test_proceeding_on_true_and_nothing() { - model("tag").$registerCallback(type = "beforeSave", methods = "callbackThatReturnsTrue,callbackThatReturnsNothing"); - obj = model("tag").findOne(order = "id"); - oldName = obj.name; - obj.name = "somethingElse"; - obj.save(); - obj.reload(); - name = obj.name; - obj.name = oldName; - obj.save(); - model("tag").$clearCallbacks(type = "beforeSave"); - assert("name IS NOT oldName"); - } - - function test_aborting_on_false() { - model("tag").$registerCallback(type = "beforeSave", methods = "callbackThatReturnsFalse"); - obj = model("tag").findOne(order = "id"); - oldName = obj.name; - obj.name = "somethingElse"; - obj.save(); - obj.reload(); - model("tag").$clearCallbacks(type = "beforeSave"); - assert("obj.name IS oldName"); - } - - function test_setting_property() { - model("tag").$registerCallback(type = "beforeSave", methods = "callbackThatSetsProperty"); - obj = model("tag").findOne(order = "id"); - existBefore = StructKeyExists(obj, "setByCallback"); - obj.save(); - existAfter = StructKeyExists(obj, "setByCallback"); - model("tag").$clearCallbacks(type = "beforeSave"); - assert("NOT existBefore AND existAfter"); - } - - function test_setting_property_with_skipped_callback() { - model("tag").$registerCallback(type = "beforeSave", methods = "callbackThatSetsProperty"); - obj = model("tag").findOne(order = "id"); - existBefore = StructKeyExists(obj, "setByCallback"); - obj.save(callbacks = false, transaction = "rollback"); - existAfter = StructKeyExists(obj, "setByCallback"); - model("tag").$clearCallbacks(type = "beforeSave"); - assert("NOT existBefore AND NOT existAfter"); - } - - function test_execution_order() { - model("tag").$registerCallback(type = "beforeSave", methods = "firstCallback,secondCallback"); - obj = model("tag").findOne(order = "id"); - obj.name = "somethingElse"; - obj.save(); - model("tag").$clearCallbacks(type = "beforeSave"); - assert("obj.orderTest IS 'first,second'"); - } - - function test_aborting_chain() { - model("tag").$registerCallback( - type = "beforeSave", - methods = "firstCallback,callbackThatReturnsFalse,secondCallback" - ); - obj = model("tag").findOne(order = "id"); - obj.name = "somethingElse"; - obj.save(); - model("tag").$clearCallbacks(type = "beforeSave"); - assert("obj.orderTest IS 'first'"); - } - - function test_setting_in_config_and_clearing() { - callbacks = model("author").$callbacks(); - assert("callbacks.beforeSave[1] IS 'callbackThatReturnsTrue'"); - model("author").$clearCallbacks(type = "beforeSave"); - assert("ArrayLen(callbacks.beforeSave) IS 0 AND callbacks.beforeDelete[1] IS 'callbackThatReturnsTrue'"); - model("author").$clearCallbacks(); - assert("ArrayLen(callbacks.beforeDelete) IS 0"); - } - -} diff --git a/core/src/wheels/tests/model/callbacks/beforeUpdate.cfc b/core/src/wheels/tests/model/callbacks/beforeUpdate.cfc deleted file mode 100644 index ae71f7495d..0000000000 --- a/core/src/wheels/tests/model/callbacks/beforeUpdate.cfc +++ /dev/null @@ -1,23 +0,0 @@ -component extends="wheels.tests.Test" { - - function setup() { - model("tag").$registerCallback(type = "beforeUpdate", methods = "callbackThatSetsProperty,callbackThatReturnsFalse"); - obj = model("tag").findOne(); - obj.name = "somethingElse"; - } - - function teardown() { - model("tag").$clearCallbacks(type = "beforeUpdate"); - } - - function test_existing_object() { - obj.save(); - assert("StructKeyExists(obj, 'setByCallback')"); - } - - function test_existing_object_with_skipped_callback() { - obj.save(callbacks = false, transaction = "rollback"); - assert("NOT StructKeyExists(obj, 'setByCallback')"); - } - -} diff --git a/core/src/wheels/tests/model/callbacks/beforeValidation.cfc b/core/src/wheels/tests/model/callbacks/beforeValidation.cfc deleted file mode 100644 index b512ae3e7f..0000000000 --- a/core/src/wheels/tests/model/callbacks/beforeValidation.cfc +++ /dev/null @@ -1,59 +0,0 @@ -component extends="wheels.tests.Test" { - - function setup() { - model("tag").$registerCallback( - type = "beforeValidation", - methods = "callbackThatSetsProperty,callbackThatReturnsFalse" - ); - obj = model("tag").findOne(); - obj.name = "somethingElse"; - } - - function teardown() { - model("tag").$clearCallbacks(type = "beforeValidation"); - } - - function test_saving_object() { - obj.save(); - assert("StructKeyExists(obj, 'setByCallback')"); - } - - function test_saving_object_without_callbacks() { - obj.save(callbacks = false, transaction = "rollback"); - assert("NOT StructKeyExists(obj, 'setByCallback')"); - } - - function test_validating_nested_property_object_should_register_callback() { - obj = $setGalleryNestedProperties(); - obj.gallery.valid(); - assert("StructKeyExists(obj.gallery.photos[1].properties(), 'beforeValidationCallbackRegistered')"); - } - - function test_saving_nested_property_object_should_register_callback_only_once() { - transaction { - obj = $setGalleryNestedProperties(); - obj.gallery.save(); - assert("obj.gallery.photos[1].beforeValidationCallbackCount IS 1"); - transaction action="rollback"; - } - } - - function $setGalleryNestedProperties() { - var rv = {}; - rv.user = model("user").findOneByLastName("Petruzzi"); - rv.gallery = model("gallery").new( - userId = rv.user.id, - title = "Nested Properties Gallery", - description = "A gallery testing nested properties." - ); - rv.gallery.photos = [ - model("photo").new( - userId = rv.user.id, - filename = "Nested Properties Photo Test 1", - DESCRIPTION1 = "test photo 1 for nested properties gallery" - ) - ]; - return rv; - } - -} diff --git a/core/src/wheels/tests/model/callbacks/beforeValidationOnCreate.cfc b/core/src/wheels/tests/model/callbacks/beforeValidationOnCreate.cfc deleted file mode 100644 index 541396c20a..0000000000 --- a/core/src/wheels/tests/model/callbacks/beforeValidationOnCreate.cfc +++ /dev/null @@ -1,28 +0,0 @@ -component extends="wheels.tests.Test" { - - function setup() { - model("tag").$registerCallback( - type = "beforeValidationOnCreate", - methods = "callbackThatSetsProperty,callbackThatReturnsFalse" - ); - } - - function teardown() { - model("tag").$clearCallbacks(type = "beforeValidationOnCreate"); - } - - function test_new_object() { - obj = model("tag").create(); - assert("StructKeyExists(obj, 'setByCallback')"); - } - - function test_new_object_with_skipped_callback() { - obj = model("tag").create( - name = "mustSetAtLeastOnePropertyOrCreateFails", - transaction = "rollback", - callbacks = false - ); - assert("NOT StructKeyExists(obj, 'setByCallback')"); - } - -} diff --git a/core/src/wheels/tests/model/callbacks/beforeValidationOnUpdate.cfc b/core/src/wheels/tests/model/callbacks/beforeValidationOnUpdate.cfc deleted file mode 100644 index e264b3985a..0000000000 --- a/core/src/wheels/tests/model/callbacks/beforeValidationOnUpdate.cfc +++ /dev/null @@ -1,26 +0,0 @@ -component extends="wheels.tests.Test" { - - function setup() { - model("tag").$registerCallback( - type = "beforeValidationOnUpdate", - methods = "callbackThatSetsProperty,callbackThatReturnsFalse" - ); - obj = model("tag").findOne(); - obj.name = "somethingElse"; - } - - function teardown() { - model("tag").$clearCallbacks(type = "beforeValidationOnUpdate"); - } - - function test_existing_object() { - obj.save(); - assert("StructKeyExists(obj, 'setByCallback')"); - } - - function test_existing_object_with_skipped_callback() { - obj.save(callbacks = false, transaction = "rollback"); - assert("NOT StructKeyExists(obj, 'setByCallback')"); - } - -} diff --git a/core/src/wheels/tests/model/callbacks/custom.cfc b/core/src/wheels/tests/model/callbacks/custom.cfc deleted file mode 100644 index 623076aa0f..0000000000 --- a/core/src/wheels/tests/model/callbacks/custom.cfc +++ /dev/null @@ -1,11 +0,0 @@ -component extends="wheels.tests.Test" { - - function test_existing_object() { - args.type = "myCustomCallBack"; - model("tag").$registerCallback(type = args.type, methods = "methodOne"); - r = model("tag").$callbacks(argumentCollection = args); - assert('IsArray(r)'); - assert('ArrayLen(r) eq 1'); - } - -} diff --git a/core/src/wheels/tests/model/crud/binarydata.cfc b/core/src/wheels/tests/model/crud/binarydata.cfc deleted file mode 100644 index 5d651ec414..0000000000 --- a/core/src/wheels/tests/model/crud/binarydata.cfc +++ /dev/null @@ -1,34 +0,0 @@ -component extends="wheels.tests.Test" { - - function setup() { - binaryData = FileReadBinary(ExpandPath('/wheels/tests/_assets/files/cfwheels-logo.png')); - } - - function test_update() { - transaction action="begin" { - photo = model("photo").findOne(); - photo.update(filename = "somefilename", fileData = binaryData); - photo = model("photo").findByKey(photo.id); - _binary = photo.filedata; - transaction action="rollback"; - } - assert('IsBinary(ToBinary(_binary))'); - } - - function test_insert() { - gallery = model("gallery").findOne(include = "user", where = "c_o_r_e_users.lastname = 'Petruzzi'", orderby = "id"); - transaction action="begin" { - photo = model("photo").create( - galleryid = "#gallery.id#", - filename = "somefilename", - fileData = binaryData, - description1 = "something something" - ); - photo = model("photo").findByKey(photo.id); - _binary = photo.filedata; - transaction action="rollback"; - } - assert('IsBinary(ToBinary(_binary))'); - } - -} diff --git a/core/src/wheels/tests/model/crud/changes.cfc b/core/src/wheels/tests/model/crud/changes.cfc deleted file mode 100644 index d76d7328ce..0000000000 --- a/core/src/wheels/tests/model/crud/changes.cfc +++ /dev/null @@ -1,211 +0,0 @@ -component extends="wheels.tests.Test" { - - function test_clearing_all_change_info() { - author = model("author").findOne(select = "firstName"); - author.firstName = "asfdg9876asdgf"; - author.lastName = "asfdg9876asdgf"; - result = author.hasChanged(); - assert("result"); - author.clearChangeInformation(); - result = author.hasChanged(); - assert("NOT result"); - } - - function test_clearing_property_change_info() { - author = model("author").findOne(select = "firstName"); - author.firstName = "asfdg9876asdgf"; - author.lastName = "asfdg9876asdgf"; - result = author.hasChanged(property = "firstName"); - assert("result"); - result = author.hasChanged(property = "lastName"); - assert("result"); - author.clearChangeInformation(property = "firstName"); - result = author.hasChanged(property = "firstName"); - assert("NOT result"); - result = author.hasChanged(property = "lastName"); - assert("result"); - } - - function test_comparing_existing_properties_only() { - author = model("author").findOne(select = "firstName"); - result = author.hasChanged(); - assert("NOT result"); - result = author.hasChanged("firstName"); - assert("NOT result"); - author = model("author").findOne(); - StructDelete(author, "firstName"); - result = author.hasChanged(); - assert("NOT result"); - result = author.hasChanged("firstName"); - assert("NOT result"); - result = author.hasChanged("somethingThatDoesNotExist"); - assert("NOT result"); - } - - function test_allChanges() { - author = model("author").findOne(order = "id"); - author.firstName = "a"; - author.lastName = "b"; - compareWith.firstName.changedFrom = "Per"; - compareWith.firstName.changedTo = "a"; - compareWith.lastName.changedFrom = "Djurner"; - compareWith.lastName.changedTo = "b"; - result = author.allChanges(); - assert("result.toString() IS compareWith.toString()"); - } - - function test_changedProperties() { - author = model("author").findOne(); - author.firstName = "a"; - author.lastName = "b"; - result = ListSort(author.changedProperties(), "textnocase"); - assert("result IS 'firstName,lastName'"); - } - - function test_changedProperties_without_changes() { - author = model("author").findOne(); - result = author.changedProperties(); - assert("result IS ''"); - } - - function test_changedProperties_change_and_back() { - author = model("author").findOne(); - author.oldFirstName = author.firstName; - author.firstName = "a"; - result = author.changedProperties(); - assert("result IS 'firstName'"); - author.firstName = author.oldFirstName; - result = author.changedProperties(); - assert("result IS ''"); - } - - function test_isNew() { - transaction { - author = model("author").new(firstName = "Per", lastName = "Djurner"); - result = author.isNew(); - assert("result IS true"); - author.save(transaction = "none"); - result = author.isNew(); - assert("result IS false"); - transaction action="rollback"; - } - } - - function test_isNew_with_find() { - author = model("author").findOne(); - result = author.isNew(); - assert("result IS false"); - } - - function test_isPeristed() { - transaction { - author = model("author").new(firstName = "Per", lastName = "Djurner"); - result = author.isPersisted(); - assert("result is false"); - author.save(transaction = "none"); - result = author.isPersisted(); - assert("result is true"); - transaction action="rollback"; - } - } - - function test_isPersisted_with_find() { - author = model("author").findOne(); - result = author.isPersisted(); - assert("result is true"); - } - - function test_hasChanged() { - author = model("author").findOne(where = "lastName = 'Djurner'"); - result = author.hasChanged(); - assert("result IS false"); - author.lastName = "Petruzzi"; - result = author.hasChanged(); - assert("result IS true"); - author.lastName = "Djurner"; - result = author.hasChanged(); - assert("result IS false"); - } - - function test_hasChanged_with_new() { - transaction { - author = model("author").new(); - result = author.hasChanged(); - assert("result IS true"); - author.firstName = "Per"; - author.lastName = "Djurner"; - author.save(transaction = "none"); - result = author.hasChanged(); - assert("result IS false"); - author.lastName = "Petruzzi"; - result = author.hasChanged(); - assert("result IS true"); - author.save(transaction = "none"); - result = author.hasChanged(); - assert("result IS false"); - transaction action="rollback"; - } - } - - function test_XXXHasChanged() { - author = model("author").findOne(where = "lastName = 'Djurner'"); - author.lastName = "Petruzzi"; - result = author.lastNameHasChanged(); - assert("result IS true"); - result = author.firstNameHasChanged(); - assert("result IS false"); - } - - function test_changedFrom() { - author = model("author").findOne(where = "lastName = 'Djurner'"); - author.lastName = "Petruzzi"; - result = author.changedFrom(property = "lastName"); - assert("result IS 'Djurner'"); - } - - function test_XXXChangedFrom() { - author = model("author").findOne(where = "lastName = 'Djurner'"); - author.lastName = "Petruzzi"; - result = author.lastNameChangedFrom(property = "lastName"); - assert("result IS 'Djurner'"); - } - - function test_date_compare() { - user = model("user").findOne(where = "username = 'tonyp'"); - user.birthday = "11/01/1975 12:00 AM"; - e = user.hasChanged("birthday"); - assert('e eq false'); - } - - function test_binary_compare() { - transaction { - photo = model("photo").findOne(order = model("photo").primaryKey()); - assert("NOT photo.hasChanged('fileData')"); - binaryData = FileReadBinary(ExpandPath('/wheels/tests/_assets/files/cfwheels-logo.png')); - photo.fileData = binaryData; - assert("photo.hasChanged('fileData')"); - photo.galleryid = 99; - photo.save(); - assert("NOT photo.hasChanged('fileData')"); - photo = model("photo").findOne(where = "galleryid=99"); - assert("NOT photo.hasChanged('fileData')"); - binaryData = FileReadBinary(ExpandPath('/wheels/tests/_assets/files/cfwheels-logo.txt')); - photo.fileData = binaryData; - assert("photo.hasChanged('fileData')"); - transaction action="rollback"; - } - } - - function test_float_compare() { - transaction { - post = model("post").findByKey(2); - post.averagerating = 3.0000; - post.save(reload = true); - post.averagerating = "3.0000"; - changed = post.hasChanged("averagerating"); - assert('changed eq false'); - transaction action="rollback"; - } - } - -} diff --git a/core/src/wheels/tests/model/crud/create.cfc b/core/src/wheels/tests/model/crud/create.cfc deleted file mode 100644 index 8fd18c3be6..0000000000 --- a/core/src/wheels/tests/model/crud/create.cfc +++ /dev/null @@ -1,66 +0,0 @@ -component extends="wheels.tests.Test" { - - function setup() { - results = {}; - } - - function test_save_null_strings() { - transaction { - results.author = model("author").create(firstName = "Null", lastName = "Null"); - assert("IsObject(results.author)"); - transaction action="rollback"; - } - } - - function test_auto_incrementing_primary_key_should_be_set() { - transaction { - results.author = model("author").create(firstName = "Test", lastName = "Test"); - assert( - "IsObject(results.author) AND StructKeyExists(results.author, results.author.primaryKey()) AND IsNumeric(results.author[results.author.primaryKey()])" - ); - transaction action="rollback"; - } - } - - function test_non_auto_incrementing_primary_key_should_not_be_changed() { - transaction { - results.shop = model("shop").create(ShopId = 99, CityCode = 99, Name = "Test"); - assert( - "IsObject(results.shop) AND StructKeyExists(results.shop, results.shop.primaryKey()) AND results.shop[results.shop.primaryKey()] IS 99" - ); - transaction action="rollback"; - } - } - - function test_composite_key_values_should_be_set_when_they_both_exist() { - transaction { - results.city = model("city").create(citycode = 99, id = "z", name = "test"); - assert("results.city.citycode IS 99 AND results.city.id IS 'z'"); - transaction action="rollback"; - } - } - - function test_columns_that_are_not_null_should_allow_for_blank_string_during_create() { - info = $dbinfo(datasource = application.wheels.dataSourceName, type = "version"); - db = LCase( - Replace( - info.database_productname, - " ", - "", - "all" - ) - ); - author = model("author").create(firstName = "Test", lastName = "", transaction = "rollback"); - assert("IsObject(author) AND !len(author.lastName)"); - } - - function test_saving_a_new_model_without_properties_should_not_throw_errors() { - transaction action="begin" { - model = model("tag").new(); - str = raised('model.save(reload=true)'); - assert('str eq ""'); - transaction action="rollback"; - } - } - -} diff --git a/core/src/wheels/tests/model/crud/dbinfo.cfm b/core/src/wheels/tests/model/crud/dbinfo.cfm deleted file mode 100644 index 81b3470533..0000000000 --- a/core/src/wheels/tests/model/crud/dbinfo.cfm +++ /dev/null @@ -1 +0,0 @@ - diff --git a/core/src/wheels/tests/model/crud/findall.cfc b/core/src/wheels/tests/model/crud/findall.cfc deleted file mode 100644 index 519e63a801..0000000000 --- a/core/src/wheels/tests/model/crud/findall.cfc +++ /dev/null @@ -1,118 +0,0 @@ -component extends="wheels.tests.Test" { - - function setup() { - source = model("user").findAll(select = "id,lastName", maxRows = 3); - } - - function test_paginated_finder_calls_with_no_records_include_column_names() { - q = model("user").findAll(select = "id, firstName", where = "id = -1", page = 1, perPage = 10); - assert("ListSort(q.columnList, 'text') eq 'FIRSTNAME,ID'"); - } - - function test_maxrows_change_should_break_cache() { - $cacheQueries = application.wheels.cacheQueries; - application.wheels.cacheQueries = true; - q = model("user").findAll(maxrows = 1, cache = 10); - assert('q.recordCount IS 1'); - q = model("user").findAll(maxrows = 2, cache = 10); - assert('q.recordCount IS 2'); - application.wheels.cacheQueries = $cacheQueries; - } - - function test_in_operator_with_quoted_strings() { - values = QuotedValueList(source.lastName); - q = model("user").findAll(where = "lastName IN (#values#)"); - assert('q.recordCount IS 3'); - } - - function test_in_operator_with_numbers() { - values = ValueList(source.id); - q = model("user").findAll(where = "id IN (#values#)"); - assert('q.recordCount IS 3'); - } - - function test_custom_query_and_orm_query_in_transaction() { - transaction { - actual = model("user").findAll(select = "id"); - expected = $query(datasource = application.wheels.dataSourceName, sql = "SELECT id FROM c_o_r_e_users"); - } - assert("actual.recordCount eq expected.recordCount"); - } - - function test_in_operator_with_spaces() { - authors = model("author").findAll( - where = ArrayToList( - ["id != 0", "id IN (1, 2, 3)", "firstName IN ('Per', 'Tony')", "lastName IN ('Djurner', 'Petruzzi')"], - " AND " - ) - ); - - assert("authors.recordCount eq 2"); - } - - function test_in_operator_with_spaces_and_equals_comma_value_combo_with_brackets() { - authors = model("author").findAll( - where = ArrayToList(["id IN (8)", "(lastName = 'Chapman, Duke of Surrey')"], " AND ") - ); - assert("authors.recordCount eq 1"); - } - - function test_moving_aggregate_functions_in_where_to_having() { - results1 = model("user").findAll( - select = "state, salesTotal", - group = "state", - where = "salesTotal > 10", - order = "salesTotal DESC" - ); - assert("results1.RecordCount eq 2 AND results1['salesTotal'][1] eq 20"); - - results2 = model("user").findAll( - select = "state, salesTotal", - group = "state", - where = "username <> 'perd' AND salesTotal > 10", - order = "salesTotal DESC" - ); - assert("results2.RecordCount eq 2 AND results2['salesTotal'][1] eq 11"); - - results3 = model("user").findAll(select = "state, salesTotal", group = "state", where = "salesTotal < 10"); - assert("results3.recordCount eq 1 AND results3['salesTotal'][1] eq 6"); - } - - function test_uppercase_table_name_containing_or_substring() { - actual = model("category").findAll(where = "c_o_r_e_CATEGORIES.ID > 0"); - assert("actual.recordCount eq 2"); - } - - function test_convert_handle_to_allowed_variable() { - actual = model("author").findAll(handle = "dot.notation test"); - assert("actual.recordCount eq 10"); - } - - function test_findall_returnas_sql() { - actual = model("author").findAll(select = "id", returnAs = "sql"); - // remove line breaks - actual = ReplaceList(actual, "#Chr(13)#,#Chr(10)#", " , "); - // remove double spaces - actual = ReplaceList(actual, " ", " ", "all"); - // trim extra whitespace - actual = Trim(actual); - - expected = "SELECT c_o_r_e_authors.id FROM c_o_r_e_authors"; - - assert("actual eq expected"); - } - function test_select_ambiguous_column_name_using_alias() { - loc.query = model("Post").findAll(select = "createdat,c_o_r_e_commentcreatedat,c_o_r_e_commentbody", include = "c_o_r_e_Comments"); - loc.columnList = ListSort(loc.query.columnList, "text"); - assert('loc.columnList eq "createdat,c_o_r_e_commentbody,c_o_r_e_commentcreatedat"'); - } - - function test_select_calculated_property_when_implicitly_selecting_fields() { - posts = model("Post").findAll( - select = "c_o_r_e_posts.id,c_o_r_e_posts.title,c_o_r_e_posts.authorid,c_o_r_e_comments.id AS commentid,c_o_r_e_comments.name,titleAlias", - include = "c_o_r_e_Comments" - ); - assert("isDefined('posts.titleAlias')"); - } - -} \ No newline at end of file diff --git a/core/src/wheels/tests/model/crud/finders.cfc b/core/src/wheels/tests/model/crud/finders.cfc deleted file mode 100644 index 60c2bbc9b9..0000000000 --- a/core/src/wheels/tests/model/crud/finders.cfc +++ /dev/null @@ -1,274 +0,0 @@ -component extends="wheels.tests.Test" { - - function setup() { - user = model("user"); - shop = model("shop"); - isACF2016 = application.wheels.serverName == "Adobe Coldfusion" && application.wheels.serverVersionMajor == 2016; - isACF2018 = application.wheels.serverName == "Adobe Coldfusion" && application.wheels.serverVersionMajor == 2018; - isACF2021 = application.wheels.serverName == "Adobe Coldfusion" && application.wheels.serverVersionMajor == 2021; - isACF2023 = application.wheels.serverName == "Adobe Coldfusion" && application.wheels.serverVersionMajor == 2023; - } - - function test_select_distinct_addresses() { - q = user.findAll(select = "address", distinct = "true", order = "address"); - assert('q.recordcount eq 4'); - e = "123 Petruzzi St.|456 Peters Dr.|789 Djurner Ave.|987 Riera Blvd."; - r = ValueList(q.address, "|"); - assert('e eq r'); - } - - function test_select_users_groupby_address() { - q = user.findAll(select = "address", group = "address", order = "address", result = "result"); - assert('q.recordcount eq 4'); - e = "123 Petruzzi St.|456 Peters Dr.|789 Djurner Ave.|987 Riera Blvd."; - r = ValueList(q.address, "|"); - assert('e eq r'); - } - - function test_findByKey() { - e = user.findOne(where = "lastname = 'Petruzzi'"); - q = user.findByKey(e.id); - assert('q.id eq e.id'); - } - - function test_findByKey_returns_object_when_key_has_leading_space() { - e = shop.findByKey(" shop6"); - assert('isobject(e)'); - } - - function test_findByKey_returns_false_when_record_not_found() { - q = user.findByKey(999999999); - assert('q eq false'); - } - - function test_findByKey_returns_false_when_passed_blank_string() { - q = user.findByKey(""); - assert('q eq false'); - } - - function test_findByKey_returns_empty_query_when_record_not_found_with_return_as_equal_query() { - q = user.findByKey(key = 999999999, returnAs = "query"); - assert('q.RecordCount eq false'); - } - - function test_findOne() { - e = user.findOne(where = "lastname = 'Petruzzi'"); - assert('isobject(e)'); - } - - function test_findOne_returns_false_when_record_not_found() { - e = user.findOne(where = "lastname = 'somenamenotfound'"); - assert('e eq false'); - } - - function test_findOne_returns_empty_query_when_record_not_found_with_return_as_equal_query() { - e = user.findOne(where = "lastname = 'somenamenotfound'", returnAs = "query"); - assert('e.RecordCount eq false'); - } - - function test_findOne_returns_false_when_record_not_found_with_inner_join_include() { - e = user.findOne(where = "lastname= = 'somenamenotfound'", include = "galleries"); - assert('e eq false'); - } - - function test_findOne_returns_false_when_record_not_found_with_outer_join_include() { - e = user.findOne(where = "lastname= = 'somenamenotfound'", include = "outerjoinphotogalleries"); - assert('e eq false'); - } - - function test_findAll() { - q = user.findAll(); - assert('q.recordcount eq 5'); - q = user.findAll(where = "lastname = 'Petruzzi' OR lastname = 'Peters'", order = "lastname"); - assert('q.recordcount eq 2'); - assert('valuelist(q.lastname) eq "peters,Petruzzi"'); - } - - function test_findAllByXXX() { - q = user.findAllByZipcode(value = "22222", order = "id"); - assert('q.recordcount eq 2'); - q = user.findAllByZipcode(value = "11111", order = "id"); - assert('q.recordcount eq 1'); - q = user.findAllByZipcode(zipCode = "11111"); - assert('q.recordcount eq 1'); - q = user.findAllByZipcode("11111"); - assert('q.recordcount eq 1'); - q = user.findAllByZipcodeAndBirthDayMonth(values = "22222,11"); - assert('q.recordcount eq 1'); - q = user.findAllByZipcodeAndBirthDayMonth(zipCode = "22222", birthDayMonth = "11"); - assert('q.recordcount eq 1'); - } - - function test_findByKey_norecords_returns_correct_type() { - q = user.findByKey("0"); - debug('q', false); - assert('isboolean(q) and q eq false'); - - q = user.findByKey(key = "0", returnas = "query"); - debug('q', false); - assert('isquery(q) and q.recordcount eq 0'); - - q = user.findByKey(key = "0", returnas = "object"); - debug('q', false); - assert('isboolean(q) and q eq false'); - - /* read when we have implemented the code to throw an error when an incorrect returnAs value is passed in - q = raised('user.findByKey(key="0", returnas="objects")'); - r = "Wheels.IncorrectArgumentValue"; - debug('q', false); - assert('q eq r'); */ - } - - function test_findOne_norecords_returns_correct_type() { - q = user.findOne(where = "id = 0"); - debug('q', false); - assert('isboolean(q) and q eq false'); - - q = user.findOne(where = "id = 0", returnas = "query"); - debug('q', false); - assert('isquery(q) and q.recordcount eq 0'); - - q = user.findOne(where = "id = 0", returnas = "object"); - debug('q', false); - assert('isboolean(q) and q eq false'); - - /* read when we have implemented the code to throw an error when an incorrect returnAs value is passed in - q = raised('user.findOne(where="id = 0", returnas="objects")'); - r = "Wheels.IncorrectArgumentValue"; - debug('q', false); - assert('q eq r'); */ - } - - function test_findAll_returnAs_query_noRecords_returns_correct_type() { - q = user.findAll(where = "id = 0", returnas = "query"); - debug('q', false); - assert('isquery(q) and q.recordcount eq 0'); - } - - function test_findAll_returnAs_structs_noRecords_returns_correct_type() { - q = user.findAll(where = "id = 0", returnAs = "structs"); - debug('q', false); - assert('isarray(q) and arrayisempty(q)'); - } - - function test_findAll_returnAs_objects_noRecords_returns_correct_type() { - q = user.findAll(where = "id = 0", returnas = "objects"); - debug('q', false); - assert('isarray(q) and arrayisempty(q)'); - } - - function test_findAll_returnAs_invalid_throws_error() { - q = raised('user.findAll(where="id = 1", returnas="notvalid")'); - r = "Wheels.IncorrectArgumentValue"; - debug('q', false); - assert('q eq r'); - } - - function test_findAll_supports_inbuilt_returnType() { - // returnType wasn't supported until ACF2021 - if (isACF2016 || isACF2018 || isACF2021 || isACF2023) { - return; - } - actual = user.findAll(returnType = "struct", keyColumn = "id"); - - assert('IsStruct(actual)'); - assert("IsStruct(actual['1'])"); - } - - function test_findAll_inbuilt_returnType_takes_precedence_over_returnAs() { - if (isACF2016 || isACF2018 || isACF2021 || isACF2023) { - return; - } - actual = user.findAll(returnType = "array", returnAs = "query"); - assert('IsArray(actual)'); - } - - function test_exists_by_key_valid() { - e = user.findOne(where = "lastname = 'Petruzzi'"); - r = user.exists(e.id); - assert('r eq true'); - } - - function test_exists_by_key_invalid() { - r = user.exists(0); - assert('r eq false'); - } - - function test_exists_by_where_one_record_valid() { - r = user.exists(where = "lastname = 'Petruzzi'"); - assert('r eq true'); - } - - function test_exists_by_where_one_record_invalid() { - r = user.exists(where = "lastname = 'someoneelse'"); - assert('r eq false'); - } - - function test_exists_by_where_two_records_valid() { - r = user.exists(where = "zipcode = '22222'"); - assert('r eq true'); - } - - function test_exists_any_record() { - r = user.exists(); - assert('r eq true'); - } - - function test_exists_no_records() { - transaction action="begin" { - user.deleteAll(); - r = user.exists(); - transaction action="rollback"; - } - assert('r eq false'); - } - - function test_allow_negative_values_in_where_clause() { - r = user.exists(where = "id = -1"); - assert('r eq false'); - } - - function test_findByKey_with_include_soft_deletes() { - transaction action="begin" { - post1 = model("Post").findOne(); - post1.delete(transaction = "none"); - post2 = model("Post").findByKey(key = post1.id, includeSoftDeletes = true); - transaction action="rollback"; - } - assert('IsObject(post2) is true'); - } - - function test_findOne_with_include_soft_deletes() { - transaction action="begin" { - post1 = model("Post").findOne(); - post1.delete(transaction = "none"); - post2 = model("Post").findOne(where = "id=#post1.id#", includeSoftDeletes = true); - transaction action="rollback"; - } - assert('IsObject(post2) is true'); - } - - function test_findAll_with_include_soft_deletes() { - transaction action="begin" { - model("Post").deleteAll(); - allPosts = model("Post").findAll(includeSoftDeletes = true); - transaction action="rollback"; - } - assert('allPosts.recordcount eq 5'); - } - - function test_findOne_returns_empty_array_for_included_model_when_none_exist() { - e = model("author").findOne(where = "lastname = 'Bellenie'", include = "posts"); - assert('IsArray(e.posts) && ArrayIsEmpty(e.posts)'); - } - - function test_findAll_with_softdeleted_associated_rows() { - transaction action="begin" { - model("Post").deleteAll(); - posts = model("Author").findByKey(key = 1, include = "Posts", returnAs = "query"); - transaction action="rollback"; - } - assert('posts.recordcount eq 1'); - } - -} diff --git a/core/src/wheels/tests/model/crud/findone.cfc b/core/src/wheels/tests/model/crud/findone.cfc deleted file mode 100644 index 03bdd1e45e..0000000000 --- a/core/src/wheels/tests/model/crud/findone.cfc +++ /dev/null @@ -1,49 +0,0 @@ -component extends="wheels.tests.Test" { - - function setup() { - tagModel = model("tag"); - postModel = model("post"); - } - - function test_request_query_cache_should_be_cleared_after_change() { - local.oldCacheQueriesDuringRequest = application.wheels.cacheQueriesDuringRequest; - application.wheels.cacheQueriesDuringRequest = true; - transaction action="begin" { - authorBefore = model("author").findByKey(1); - authorBefore.update(lastName = "D"); - authorAfter = model("author").findByKey(1); - transaction action="rollback"; - } - assert("authorAfter.lastName IS 'D'"); - application.wheels.cacheQueriesDuringRequest = local.oldCacheQueriesDuringRequest; - } - - function test_self_join() { - tag = tagModel.findOne(where = "name = 'pear'", include = "parent", order = "id, id"); - assert("IsObject(tag) and IsObject(tag.parent)"); - } - - function test_self_join_with_other_associations() { - post = postModel.findByKey(key = 1, include = "classifications(tag(parent))", returnAs = "query"); - assert("IsQuery(post) and post.recordcount"); - } - - function test_do_not_use_query_param_for_nulls() { - result = model("author").findOne(where = "lastName IS NULL"); - assert("NOT IsObject(result)"); - result = model("author").findOne(where = "lastName IS NOT NULL"); - assert("IsObject(result)"); - } - - function test_parsing_numbers_in_where() { - result = model("author").findOne(where = "firstName = 1"); - assert("NOT IsObject(result)"); - result = model("author").findOne(where = "firstName = 1.0"); - assert("NOT IsObject(result)"); - result = model("author").findOne(where = "firstName = +1"); - assert("NOT IsObject(result)"); - result = model("author").findOne(where = "firstName = -1"); - assert("NOT IsObject(result)"); - } - -} diff --git a/core/src/wheels/tests/model/crud/findonebyxxx.cfc b/core/src/wheels/tests/model/crud/findonebyxxx.cfc deleted file mode 100644 index 74b883c37e..0000000000 --- a/core/src/wheels/tests/model/crud/findonebyxxx.cfc +++ /dev/null @@ -1,89 +0,0 @@ -component extends="wheels.tests.Test" { - - function setup() { - results = {}; - } - - function test_one_value() { - results.user = model("user").findOneByFirstname('Per'); - assert("IsObject(results.user)"); - } - - function test_explicit_arguments() { - results.user = model("user").findOneByZipCode(value = "22222", select = "id,lastName,zipCode", order = "id"); - assert( - "IsObject(results.user) AND results.user.lastName IS 'Peters' AND NOT StructKeyExists(results.user, 'firstName')" - ); - } - - function test_pass_through_order() { - results.user = model("user").findOneByIsActive(value = "1", order = "zipCode DESC"); - assert("IsObject(results.user) AND results.user.lastName IS 'Riera'"); - } - - function test_two_values() { - results.user = model("user").findOneByFirstNameAndLastName("Per,Djurner"); - assert("IsObject(results.user) AND results.user.lastName IS 'Djurner'"); - } - - function test_two_values_with_named_arguments() { - results.user = model("user").findOneByFirstNameAndLastName(firstName = "Per", lastName = "Djurner"); - assert("IsObject(results.user) AND results.user.lastName IS 'Djurner'"); - } - - function test_two_values_with_space() { - results.user = model("user").findOneByFirstNameAndLastName("Per, Djurner"); - assert("IsObject(results.user) AND results.user.lastName IS 'Djurner'"); - } - - function test_two_values_with_explicit_arguments() { - results.user = model("user").findOneByFirstNameAndLastName(values = "Per,Djurner"); - assert("IsObject(results.user) AND results.user.lastName IS 'Djurner'"); - } - - function test_text_data_type() { - results.profile = model("profile").findOneByBio("ColdFusion Developer"); - assert("IsObject(results.profile)"); - } - - function test_unlimited_properties_for_dynamic_finders() { - post = model("Post").findOneByTitleAndAuthoridAndViews(values = "Title for first test post|1|5", delimiter = "|"); - assert('IsObject(post)'); - } - - function test_passing_array() { - args = ["Title for first test post", 1, 5]; - post = model("Post").findOneByTitleAndAuthoridAndViews(values = args); - assert('IsObject(post)'); - } - - function test_can_change_delimieter_for_dynamic_finders() { - title = "Testing to make, to make sure, commas work"; - transaction action="begin" { - post = model("Post").findOne(where = "id = 1"); - post.title = title; - post.save(); - post = model("Post").findOneByTitleAndAuthorid(values = "#title#|1", delimiter = "|"); - transaction action="rollback"; - } - assert('IsObject(post)'); - } - - function test_passing_where_clause() { - post = model("Post").findOneByTitle(value = "Title for first test post", where = "authorid = 1 AND views = 5"); - assert('IsObject(post)'); - } - - function test_can_pass_in_commas() { - title = "Testing to make, to make sure, commas work"; - transaction action="begin" { - post = model("Post").findOne(where = "id = 1"); - post.title = title; - post.save(); - post = model("Post").findOneByTitle(values = "#title#"); - transaction action="rollback"; - } - assert('IsObject(post)'); - } - -} diff --git a/core/src/wheels/tests/model/crud/from.cfc b/core/src/wheels/tests/model/crud/from.cfc deleted file mode 100644 index 781206ead0..0000000000 --- a/core/src/wheels/tests/model/crud/from.cfc +++ /dev/null @@ -1,76 +0,0 @@ -component extends="wheels.tests.Test" { - - function test_from_clause() { - result = model("author").$fromClause(include = ""); - assert("result IS 'FROM c_o_r_e_authors'"); - } - - function test_from_clause_with_mapped_table() { - model("author").table("tbl_authors"); - result = model("author").$fromClause(include = ""); - model("author").table("c_o_r_e_authors"); - assert("result IS 'FROM tbl_authors'"); - } - - function test_from_clause_with_include() { - result = model("author").$fromClause(include = "posts"); - assert("result IS 'FROM c_o_r_e_authors LEFT OUTER JOIN c_o_r_e_posts ON c_o_r_e_authors.id = c_o_r_e_posts.authorid AND c_o_r_e_posts.deletedat IS NULL'"); - } - - function test_$indexHint() { - actual = model("author").$indexHint( - useIndex = {author = "idx_authors_123"}, - modelName = "author", - adapterName = "MySQL" - ); - expected = "USE INDEX(idx_authors_123)"; - assert("actual EQ expected"); - } - - function test_mysql_from_clause_with_index_hint() { - actual = model("author").$fromClause(include = "", useIndex = {author = "idx_authors_123"}, adapterName = "MySQL"); - expected = "FROM c_o_r_e_authors USE INDEX(idx_authors_123)"; - assert("actual EQ expected"); - } - - function test_sqlserver_from_clause_with_index_hint() { - actual = model("author").$fromClause( - include = "", - useIndex = {author = "idx_authors_123"}, - adapterName = "SQLServer" - ); - expected = "FROM c_o_r_e_authors WITH (INDEX(idx_authors_123))"; - assert("actual EQ expected"); - } - - function test_from_clause_with_index_hint_on_unsupportive_db() { - actual = model("author").$fromClause( - include = "", - useIndex = {author = "idx_authors_123"}, - adapterName = "PostgreSQL" - ); - expected = "FROM c_o_r_e_authors"; - assert("actual EQ expected"); - } - - function test_from_clause_with_include_and_index_hints() { - actual = model("author").$fromClause( - include = "posts", - useIndex = {author = "idx_authors_123", post = "idx_posts_123"}, - adapterName = "MySQL" - ); - - expected = "FROM c_o_r_e_authors USE INDEX(idx_authors_123) LEFT OUTER JOIN c_o_r_e_posts USE INDEX(idx_posts_123) ON c_o_r_e_authors.id = c_o_r_e_posts.authorid AND c_o_r_e_posts.deletedat IS NULL"; - assert("actual EQ expected"); - } - - // TODO: test_from_clause_with_include_and_index_hints_and_table_aliases - - /* - test: - inner/outer join - composite keys joining - mapped pkeys - */ - -} diff --git a/core/src/wheels/tests/model/crud/group.cfc b/core/src/wheels/tests/model/crud/group.cfc deleted file mode 100644 index 95b868df34..0000000000 --- a/core/src/wheels/tests/model/crud/group.cfc +++ /dev/null @@ -1,38 +0,0 @@ -component extends="wheels.tests.Test" { - - function test_simple_group_by() { - r = model("tag").findAll(select = "parentId, COUNT(*) AS groupCount", group = "parentId"); - assert('r.recordcount eq 4'); - } - - function test_group_by_calculated_property() { - r = model("user2").findAll(select = "firstLetter, groupCount", group = "firstLetter", order = "groupCount DESC"); - assert('r.recordcount eq 2'); - } - - function test_distinct_works_with_group_by() { - r = model("post").findAll(select = "views", distinct = true); - assert('r.recordcount eq 4'); - r = model("post").findAll(select = "views", group = "views"); - assert('r.recordcount eq 4'); - } - - function test_max_works_with_group_functionality() { - r = model("post").findAll( - select = "id, authorid, title, MAX(c_o_r_e_posts.views) AS maxView", - group = "id, authorid, title" - ); - assert('r.recordcount eq 5'); - } - - function test_group_functionality_works_with_pagination() { - r = model("post").findAll( - select = "id, authorid, title, MAX(c_o_r_e_posts.views) AS maxView", - group = "id, authorid, title", - page = 1, - perPage = 2 - ); - assert('r.recordcount eq 2'); - } - -} diff --git a/core/src/wheels/tests/model/crud/order.cfc b/core/src/wheels/tests/model/crud/order.cfc deleted file mode 100644 index 9bc19d6131..0000000000 --- a/core/src/wheels/tests/model/crud/order.cfc +++ /dev/null @@ -1,77 +0,0 @@ -component extends="wheels.tests.Test" { - - function test_order_with_maxrows_and_calculated_property() { - result = model("photo").findOne(order = "DESCRIPTION1 DESC", maxRows = 1); - assert("result.filename IS 'Gallery 9 Photo Test 9'"); - } - - function test_order_clause_no_sort() { - result = model("author").findOne(order = "lastName"); - assert("result.lastName IS 'Amiri'"); - } - - function test_order_clause_asc_sort() { - result = model("author").findOne(order = "lastName ASC"); - assert("result.lastName IS 'Amiri'"); - } - - function test_order_clause_desc_sort() { - result = model("author").findOne(order = "lastName DESC"); - assert("result.lastName IS 'Riera'"); - } - - function test_order_clause_with_include() { - result = model("post").findAll(include = "c_o_r_e_comments", order = "createdAt DESC,id DESC,name"); - assert("result['title'][1] IS 'Title for fifth test post'"); - } - - function test_order_clause_with_include_and_identical_columns() { - result = model("post").findAll(include = "c_o_r_e_comments", order = "createdAt,createdAt"); - assert("result['title'][1] IS 'Title for first test post'"); - } - - // this ensures no "Column 'shopid' in order clause is ambiguous" exception - function test_order_clause_with_paginated_include_and_ambiguous_columns() { - if (get("adaptername") != "MySQL") { - actual = model("shop").findAll( - select = "id, name", - include = "trucks", - order = "CASE WHEN registration IN ('foo') THEN 0 ELSE 1 END DESC", - page = 1, - perPage = 3 - ); - assert("actual.recordCount gt 0"); - } else { - // Skipping on MySQL - assert(true); - } - } - - function test_order_clause_with_paginated_include_and_identical_columns() { - if (get("adaptername") != "MySQL") { - result = model("post").findAll(page = 1, perPage = 3, include = "c_o_r_e_comments", order = "createdAt,createdAt"); - assert("result['title'][1] IS 'Title for first test post'"); - } else { - // Skipping on MySQL, see issue for details: - // https://github.com/wheels-dev/wheels/issues/666 - assert(true); - } - } - - function test_order_clause_with_paginated_include_and_identical_columns_desc_sort_with_specified_table_names() { - if (get("adaptername") != "MySQL") { - result = model("post").findAll( - page = 1, - perPage = 3, - include = "c_o_r_e_comments", - order = "c_o_r_e_posts.createdAt DESC,c_o_r_e_posts.id DESC,c_o_r_e_comments.createdAt" - ); - assert("result['title'][1] IS 'Title for fifth test post'"); - } else { - // Skipping on MySQL, see issue for details: - // https://github.com/wheels-dev/wheels/issues/666 - assert(true); - } - } - -} \ No newline at end of file diff --git a/core/src/wheels/tests/model/crud/pagination.cfc b/core/src/wheels/tests/model/crud/pagination.cfc deleted file mode 100644 index cc207c1d28..0000000000 --- a/core/src/wheels/tests/model/crud/pagination.cfc +++ /dev/null @@ -1,171 +0,0 @@ -component extends="wheels.tests.Test" { - - function setup() { - user = model("user"); - photo = model("photo"); - gallery = model("gallery"); - } - - function test_exist_early_if_no_records_match_where_clause() { - e = user.findAll( - where = "firstname = 'somemoron'", - perpage = "2", - page = "1", - handle = "pagination_test_1", - order = "id" - ); - assert('request.wheels.pagination_test_1.CURRENTPAGE eq 1'); - assert('request.wheels.pagination_test_1.TOTALPAGES eq 0'); - assert('request.wheels.pagination_test_1.TOTALRECORDS eq 0'); - assert('request.wheels.pagination_test_1.ENDROW eq 1'); - assert("e.recordcount eq 0"); - } - - function test_5_records_2_perpage_3_pages() { - r = user.findAll(select = "id", order = "id"); - - /* 1st page */ - e = user.findAll( - select = "id", - perpage = "2", - page = "1", - handle = "pagination_test_2", - order = "id" - ); - assert('request.wheels.pagination_test_2.CURRENTPAGE eq 1'); - assert('request.wheels.pagination_test_2.TOTALPAGES eq 3'); - assert('request.wheels.pagination_test_2.TOTALRECORDS eq 5'); - assert('request.wheels.pagination_test_2.ENDROW eq 2'); - assert("e.recordcount eq 2"); - assert('e.id[1] eq r.id[1]'); - assert('e.id[2] eq r.id[2]'); - - /* 2nd page */ - e = user.findAll( - perpage = "2", - page = "2", - handle = "pagination_test_3", - order = "id" - ); - assert('request.wheels.pagination_test_3.CURRENTPAGE eq 2'); - assert('request.wheels.pagination_test_3.TOTALPAGES eq 3'); - assert('request.wheels.pagination_test_3.TOTALRECORDS eq 5'); - assert('request.wheels.pagination_test_3.ENDROW eq 4'); - assert("e.recordcount eq 2"); - assert('e.id[1] eq r.id[3]'); - assert('e.id[2] eq r.id[4]'); - - /* 3rd page */ - e = user.findAll( - perpage = "2", - page = "3", - handle = "pagination_test_4", - order = "id" - ); - assert('request.wheels.pagination_test_4.CURRENTPAGE eq 3'); - assert('request.wheels.pagination_test_4.TOTALPAGES eq 3'); - assert('request.wheels.pagination_test_4.TOTALRECORDS eq 5'); - assert('request.wheels.pagination_test_4.ENDROW eq 5'); - assert("e.recordcount eq 1"); - assert('e.id[1] eq r.id[5]'); - } - - function test_specify_where_on_joined_table() { - q = gallery.findOne(include = "user", where = "c_o_r_e_users.lastname = 'Petruzzi'", orderby = "id"); - - /* 10 records, 2 perpage, 5 pages */ - args = { - perpage = "2", - page = "1", - handle = "pagination_test", - order = "id", - include = "gallery", - where = "galleryid = #q.id#" - }; - - args2 = Duplicate(args); - StructDelete(args2, "perpage", false); - StructDelete(args2, "page", false); - StructDelete(args2, "handle", false); - r = photo.findAll(argumentCollection = args2); - - /* page 1 */ - e = photo.findAll(argumentCollection = args); - assert('e.galleryid[1] eq r.galleryid[1]'); - assert('e.galleryid[2] eq r.galleryid[2]'); - - /* page 3 */ - args.page = "3"; - e = photo.findAll(argumentCollection = args); - assert('e.galleryid[1] eq r.galleryid[5]'); - assert('e.galleryid[2] eq r.galleryid[6]'); - - /* page 5 */ - args.page = "5"; - e = photo.findAll(argumentCollection = args); - assert('e.galleryid[1] eq r.galleryid[9]'); - assert('e.galleryid[2] eq r.galleryid[10]'); - } - - function test_make_sure_that_remapped_columns_containing_desc_and_asc_work() { - result = model("photo").findAll( - page = 1, - perPage = 20, - order = 'DESCription1 DESC', - handle = "pagination_order_test_1" - ); - assert('request.wheels.pagination_order_test_1.CURRENTPAGE eq 1'); - assert('request.wheels.pagination_order_test_1.TOTALPAGES eq 13'); - assert('request.wheels.pagination_order_test_1.TOTALRECORDS eq 250'); - assert('request.wheels.pagination_order_test_1.ENDROW eq 20'); - } - - function test_with_renamed_primary_key() { - photo = model("photo2").findAll(page = 1, perpage = 3, where = "DESCRIPTION1 LIKE '%test%'"); - assert('photo.recordcount eq 3'); - } - - function test_with_parameterize_set_to_false_with_string() { - result = model("photo").findAll( - page = 1, - perPage = 20, - handle = "pagination_order_test_1", - parameterize = "false", - where = "description1 LIKE '%photo%'" - ); - assert('request.wheels.pagination_order_test_1.CURRENTPAGE eq 1'); - assert('request.wheels.pagination_order_test_1.TOTALPAGES eq 13'); - assert('request.wheels.pagination_order_test_1.TOTALRECORDS eq 250'); - assert('request.wheels.pagination_order_test_1.ENDROW eq 20'); - } - - function test_with_parameterize_set_to_false_with_numeric() { - result = model("photo").findAll( - page = 1, - perPage = 20, - handle = "pagination_order_test_1", - parameterize = "false", - where = "id = 1" - ); - assert('request.wheels.pagination_order_test_1.CURRENTPAGE eq 1'); - assert('request.wheels.pagination_order_test_1.TOTALPAGES eq 1'); - assert('request.wheels.pagination_order_test_1.TOTALRECORDS eq 1'); - assert('request.wheels.pagination_order_test_1.ENDROW eq 1'); - } - - function test_compound_keys() { - result = model("combikey").findAll(page = 2, perPage = 4, order = "id2"); - assert('result.recordCount eq 4'); - } - - function test_incorrect_number_of_record_returned_when_where_clause_satisfies_records_beyond_the_first_identifier_value() { - q = model("author").findAll( - include = "posts", - where = "c_o_r_e_posts.views > 2", - page = 1, - perpage = 5 - ); - assert('q.recordcount eq 3'); - } - -} diff --git a/core/src/wheels/tests/model/crud/properties.cfc b/core/src/wheels/tests/model/crud/properties.cfc deleted file mode 100644 index c214aa2041..0000000000 --- a/core/src/wheels/tests/model/crud/properties.cfc +++ /dev/null @@ -1,39 +0,0 @@ -component extends="wheels.tests.Test" { - - function test_updateProperty() { - transaction action="begin" { - author = model("Author").findOne(where = "firstName='Andy'"); - saved = author.updateProperty("firstName", "Frog"); - transaction action="rollback"; - } - assert('saved eq true and author.firstName eq "Frog"'); - } - - function test_updatePropertyWithDynamicArgs() { - transaction action="begin" { - author = model("Author").findOne(where = "firstName='Andy'"); - saved = author.updateProperty(firstName = "Frog"); - transaction action="rollback"; - } - assert('saved eq true and author.firstName eq "Frog"'); - } - - function test_updateProperty_dynamic_method() { - transaction action="begin" { - author = model("Author").findOne(where = "firstName='Andy'"); - saved = author.updateFirstName(value = "Frog"); - transaction action="rollback"; - } - assert('saved eq true and author.firstName eq "Frog"'); - } - - function test_updating_properties() { - transaction action="begin" { - author = model("Author").findOne(where = "firstName='Andy'"); - saved = author.update(firstName = "Kirmit", lastName = "Frog"); - transaction action="rollback"; - } - assert('saved eq true and author.lastName eq "Frog" and author.firstName eq "Kirmit"'); - } - -} diff --git a/core/src/wheels/tests/model/crud/select.cfc b/core/src/wheels/tests/model/crud/select.cfc deleted file mode 100644 index 5a521c1734..0000000000 --- a/core/src/wheels/tests/model/crud/select.cfc +++ /dev/null @@ -1,52 +0,0 @@ -component extends="wheels.tests.Test" { - - function test_table_name_with_star_translates_to_all_fields() { - postModel = model("post"); - r = postModel.$createSQLFieldList(clause = "select", list = "c_o_r_e_posts.*", include = "", returnAs = "query"); - props = postModel.$classData().properties; - assert('ListLen(r) eq StructCount(props)'); - } - - function test_wrong_table_alias_in_select_throws_error() { - postModel = model("post"); - raised('postModel.$createSQLFieldList(list="comments.*", include="", returnAs="query")'); - } - - function test_association_with_expanded_aliases_enabled() { - columnList = ListSort( - model("Author").$createSQLFieldList( - clause = "select", - list = "", - include = "Posts", - returnAs = "query", - useExpandedColumnAliases = true - ), - "text" - ); - assert( - 'columnList eq "c_o_r_e_authors.firstname,c_o_r_e_authors.id,c_o_r_e_authors.id AS authorid,c_o_r_e_authors.lastname,c_o_r_e_posts.averagerating AS postaveragerating,c_o_r_e_posts.body AS postbody,c_o_r_e_posts.createdat AS postcreatedat,c_o_r_e_posts.deletedat AS postdeletedat,c_o_r_e_posts.id AS postid,c_o_r_e_posts.title AS posttitle,c_o_r_e_posts.updatedat AS postupdatedat,c_o_r_e_posts.views AS postviews"' - ); - } - - function test_association_with_expanded_aliases_disabled() { - columnList = ListSort( - model("Author").$createSQLFieldList( - clause = "select", - list = "", - include = "Posts", - returnAs = "query", - useExpandedColumnAliases = false - ), - "text" - ); - assert( - 'columnList eq "c_o_r_e_authors.firstname,c_o_r_e_authors.id,c_o_r_e_authors.id AS authorid,c_o_r_e_authors.lastname,c_o_r_e_posts.averagerating,c_o_r_e_posts.body,c_o_r_e_posts.createdat,c_o_r_e_posts.deletedat,c_o_r_e_posts.id AS postid,c_o_r_e_posts.title,c_o_r_e_posts.updatedat,c_o_r_e_posts.views"' - ); - } - - function test_select_argument_on_calculated_property() { - columnList = ListSort(model("AuthorSelectArgument").findAll(returnAs = "query").columnList, "text"); - assert('columnList eq "firstname,id,lastname,selectargdefault,selectargtrue"'); - } - -} \ No newline at end of file diff --git a/core/src/wheels/tests/model/crud/updates.cfc b/core/src/wheels/tests/model/crud/updates.cfc deleted file mode 100644 index a0f9cec562..0000000000 --- a/core/src/wheels/tests/model/crud/updates.cfc +++ /dev/null @@ -1,123 +0,0 @@ -component extends="wheels.tests.Test" { - - function test_update() { - transaction action="begin" { - author = model("Author").findOne(); - author.update(firstName = "Kermit", lastName = "Frog"); - allKermits = model("Author").findAll(where = "firstName='Kermit' AND lastName='Frog'"); - transaction action="rollback"; - } - assert('allKermits.recordcount eq 1'); - } - - function test_dynamic_update_with_named_argument() { - transaction action="begin" { - author = model("author").findOne(where = "firstName='Andy'"); - profile = model("profile").findOne(where = "bio LIKE 'ColdFusion Developer'"); - author.setProfile(profile = profile); - updatedProfile = model("profile").findByKey(profile.id); - transaction action="rollback"; - } - assert("updatedProfile.authorId IS author.id"); - } - - function test_dynamic_update_with_unnamed_argument() { - transaction action="begin" { - author = model("author").findOne(where = "firstName='Andy'"); - profile = model("profile").findOne(where = "bio LIKE 'ColdFusion Developer'"); - author.setProfile(profile); - updatedProfile = model("profile").findByKey(profile.id); - transaction action="rollback"; - } - assert("updatedProfile.authorId IS author.id"); - } - - function test_update_one() { - transaction action="begin" { - model("Author").updateOne(where = "firstName='Andy'", firstName = "Kermit", lastName = "Frog"); - allKermits = model("Author").findAll(where = "firstName='Kermit' AND lastName='Frog'"); - transaction action="rollback"; - } - assert('allKermits.recordcount eq 1'); - } - - function test_update_one_for_soft_deleted_records() { - transaction action="begin" { - post = model("Post").deleteOne(where = "views=0"); - model("Post").updateOne(where = "views=0", title = "This is a new title", includeSoftDeletes = true); - changedPosts = model("Post").findAll(where = "title='This is a new title'", includeSoftDeletes = true); - transaction action="rollback"; - } - assert('changedPosts.recordcount eq 1'); - } - - function test_update_by_key() { - transaction action="begin" { - author = model("Author").findOne(); - model("Author").updateByKey(key = author.id, firstName = "Kermit", lastName = "Frog"); - allKermits = model("Author").findAll(where = "firstName='Kermit' AND lastName='Frog'"); - transaction action="rollback"; - } - assert('allKermits.recordcount eq 1'); - } - - function test_update_by_key_for_soft_deleted_records() { - transaction action="begin" { - post = model("Post").findOne(where = "views=0"); - model("Post").updateByKey(key = post.id, title = "This is a new title", includeSoftDeletes = true); - changedPosts = model("Post").findAll(where = "title='This is a new title'", includeSoftDeletes = true); - transaction action="rollback"; - } - assert('changedPosts.recordcount eq 1'); - } - - function test_update_all() { - transaction action="begin" { - model("Author").updateAll(firstName = "Kermit", lastName = "Frog"); - allKermits = model("Author").findAll(where = "firstName='Kermit' AND lastName='Frog'"); - transaction action="rollback"; - } - assert('allKermits.recordcount eq 10'); - } - - function test_update_all_for_soft_deleted_records() { - transaction action="begin" { - model("Post").updateAll(title = "This is a new title", includeSoftDeletes = true); - changedPosts = model("Post").findAll(where = "title='This is a new title'", includeSoftDeletes = true); - transaction action="rollback"; - } - assert('changedPosts.recordcount eq 5'); - } - - function test_columns_that_are_not_null_should_allow_for_blank_string_during_update() { - info = $dbinfo(datasource = application.wheels.dataSourceName, type = "version"); - db = LCase( - Replace( - info.database_productname, - " ", - "", - "all" - ) - ); - transaction action="begin" { - author = model("author").findOne(where = "firstName='Tony'"); - author.lastName = ""; - author.save(); - author = model("author").findOne(where = "firstName='Tony'"); - transaction action="rollback"; - } - assert("IsObject(author) AND !len(author.lastName)"); - } - - // Issue#1273: Added this test for includes in the updateAll function - function test_update_all_with_include() { - transaction action="begin" { - loc.query0 = model("Post").findAll(where="averagerating = '5.0'"); - assert("loc.query0.recordcount eq 0"); - loc.query1 = model("Post").updateAll(averagerating = "5.0", where = "c_o_r_e_comments.postid = '1'", include = "c_o_r_e_Comments"); - loc.query2 = model("Post").findAll(where="averagerating = '5.0'"); - transaction action="rollback"; - } - assert("loc.query2.recordcount eq 1"); - } -} diff --git a/core/src/wheels/tests/model/crud/where.cfc b/core/src/wheels/tests/model/crud/where.cfc deleted file mode 100644 index 44098b048c..0000000000 --- a/core/src/wheels/tests/model/crud/where.cfc +++ /dev/null @@ -1,39 +0,0 @@ -component extends="wheels.tests.Test" { - - /* function test_contains_multiple_spaces_tabs_and_carriage_returns() { - r = model("post").findAll(where="views - - ;= - 5 AND - averagerating - > - 3")> - assert('r.recordcount eq 1'); - } */ - - function test_should_not_strip_extra_whitespace_from_values() { - r = model("user").findAll(where = "address = '123 Petruzzi St.'"); - assert('r.recordcount eq 0'); - r = model("user").findAll(where = "address = '123 Petruzzi St.'"); - assert('r.recordcount eq 2'); - } - - function test_in_statement_should_not_error() { - r = model("user").findAll( - where = "username IN('tonyp','perd','chrisp') AND (firstname = 'Tony' OR firstname = 'Per' OR firstname = 'Chris') OR id IN(1,2,3)" - ); - assert('r.recordcount eq 3'); - } - - function test_in_statement_respect_parenthesis_commas_and_single_quotes() { - r = model("user").findAll(where = "username IN('tony''s','pe''(yo,yo)rd','chrisp')"); - assert('r.recordcount eq 1'); - } - - function test_numeric_value_for_string_property() { - expected = "title='1'"; - actual = model("Post").$keyWhereString(properties = "title", values = "1"); - assert('actual EQ expected'); - } - -} diff --git a/core/src/wheels/tests/model/delete/delete.cfc b/core/src/wheels/tests/model/delete/delete.cfc deleted file mode 100644 index 9317f03509..0000000000 --- a/core/src/wheels/tests/model/delete/delete.cfc +++ /dev/null @@ -1,48 +0,0 @@ -component extends="wheels.tests.Test" { - - function test_delete() { - transaction action="begin" { - local.author = model("author").findOne(); - local.author.delete(); - allAuthors = model("author").findAll(); - transaction action="rollback"; - } - assert("allAuthors.recordcount eq 9"); - } - - function test_soft_delete() { - transaction action="begin" { - local.post = model("post").findOne(); - local.post.delete(); - postsWithoutSoftDeletes = model("post").findAll(); - postsWithSoftDeletes = model("post").findAll(includeSoftDeletes = true); - transaction action="rollback"; - } - assert("postsWithoutSoftDeletes.recordcount eq 4 AND postsWithSoftDeletes.recordcount eq 5"); - } - - function test_permanent_delete() { - transaction action="begin" { - local.post = model("post").findOne(); - local.post.delete(softDelete = false); - postsWithoutSoftDeletes = model("post").findAll(); - postsWithSoftDeletes = model("post").findAll(includeSoftDeletes = true); - transaction action="rollback"; - } - assert("postsWithoutSoftDeletes.recordcount eq 4 AND postsWithSoftDeletes.recordcount eq 4"); - } - - function test_permanent_delete_of_soft_deleted_records() { - transaction action="begin" { - local.post = model("post").findOne(); - local.post.delete(); - local.softDeletedPost = model("post").findByKey(key = local.post.id, includeSoftDeletes = true); - local.softDeletedPost.delete(includeSoftDeletes = true, softDelete = false); - postsWithoutSoftDeletes = model("post").findAll(); - postsWithSoftDeletes = model("post").findAll(includeSoftDeletes = true); - transaction action="rollback"; - } - assert("postsWithoutSoftDeletes.recordcount eq 4 AND postsWithSoftDeletes.recordcount eq 4"); - } - -} diff --git a/core/src/wheels/tests/model/delete/deleteall.cfc b/core/src/wheels/tests/model/delete/deleteall.cfc deleted file mode 100644 index bc90e9c043..0000000000 --- a/core/src/wheels/tests/model/delete/deleteall.cfc +++ /dev/null @@ -1,43 +0,0 @@ -component extends="wheels.tests.Test" { - - function test_delete_all() { - transaction action="begin" { - model("author").deleteAll(); - allAuthors = model("author").findAll(); - transaction action="rollback"; - } - assert("allAuthors.recordcount eq 0"); - } - - function test_soft_delete_all() { - transaction action="begin" { - model("post").deleteAll(); - postsWithoutSoftDeletes = model("Post").findAll(); - postsWithSoftDeletes = model("Post").findAll(includeSoftDeletes = true); - transaction action="rollback"; - } - assert("postsWithoutSoftDeletes.recordcount eq 0 AND postsWithSoftDeletes.recordcount eq 5"); - } - - function test_permanent_delete_all() { - transaction action="begin" { - model("post").deleteAll(softDelete = false); - postsWithoutSoftDeletes = model("post").findAll(); - postsWithSoftDeletes = model("post").findAll(includeSoftDeletes = true); - transaction action="rollback"; - } - assert("postsWithoutSoftDeletes.recordcount eq 0 AND postsWithSoftDeletes.recordcount eq 0"); - } - - function test_permanent_delete_all_of_soft_deleted_records() { - transaction action="begin" { - model("post").deleteAll(); - model("post").deleteAll(includeSoftDeletes = true, softDelete = false); - postsWithoutSoftDeletes = model("post").findAll(); - postsWithSoftDeletes = model("post").findAll(includeSoftDeletes = true); - transaction action="rollback"; - } - assert("postsWithoutSoftDeletes.recordcount eq 0 AND postsWithSoftDeletes.recordcount eq 0"); - } - -} diff --git a/core/src/wheels/tests/model/delete/deletebykey.cfc b/core/src/wheels/tests/model/delete/deletebykey.cfc deleted file mode 100644 index f35e536fcf..0000000000 --- a/core/src/wheels/tests/model/delete/deletebykey.cfc +++ /dev/null @@ -1,35 +0,0 @@ -component extends="wheels.tests.Test" { - - function test_delete_by_key() { - transaction action="begin" { - local.author = model("author").findOne(); - model("author").deleteByKey(local.author.id); - allAuthors = model("author").findAll(); - transaction action="rollback"; - } - assert("allAuthors.recordcount eq 9"); - } - - function test_soft_delete_by_key() { - transaction action="begin" { - local.post = model("post").findOne(); - model("post").deleteByKey(local.post.id); - postsWithoutSoftDeletes = model("post").findAll(includeSoftDeletes = false); - postsWithSoftDeletes = model("post").findAll(includeSoftDeletes = true); - transaction action="rollback"; - } - assert("postsWithoutSoftDeletes.recordcount eq 4 AND postsWithSoftDeletes.recordcount eq 5"); - } - - function test_permanent_delete_by_key() { - transaction action="begin" { - local.post = model("post").findOne(); - model("post").deleteByKey(key = local.post.id, softDelete = false); - postsWithoutSoftDeletes = model("post").findAll(); - postsWithSoftDeletes = model("post").findAll(includeSoftDeletes = true); - transaction action="rollback"; - } - assert("postsWithoutSoftDeletes.recordcount eq 4 AND postsWithSoftDeletes.recordcount eq 4"); - } - -} diff --git a/core/src/wheels/tests/model/delete/deleteone.cfc b/core/src/wheels/tests/model/delete/deleteone.cfc deleted file mode 100644 index e6b39b45fb..0000000000 --- a/core/src/wheels/tests/model/delete/deleteone.cfc +++ /dev/null @@ -1,56 +0,0 @@ -component extends="wheels.tests.Test" { - - function test_permanent_delete_by_key_of_soft_deleted_records() { - transaction action="begin" { - local.post = model("post").findOne(); - model("post").deleteOne(where = "id = #local.post.id#"); - model("post").deleteOne(where = "id = #local.post.id#", includeSoftDeletes = true, softDelete = false); - postsWithoutSoftDeletes = model("post").findAll(); - postsWithSoftDeletes = model("post").findAll(includeSoftDeletes = true); - transaction action="rollback"; - } - assert("postsWithoutSoftDeletes.recordcount eq 4 AND postsWithSoftDeletes.recordcount eq 4"); - } - - function test_delete_one() { - transaction action="begin" { - model("author").deleteOne(); - allAuthors = model("author").findAll(); - transaction action="rollback"; - } - assert("allAuthors.recordcount eq 9"); - } - - function test_soft_delete_one() { - transaction action="begin" { - model("post").deleteOne(); - postsWithoutSoftDeletes = model("post").findAll(); - postsWithSoftDeletes = model("post").findAll(includeSoftDeletes = true); - transaction action="rollback"; - } - assert("postsWithoutSoftDeletes.recordcount eq 4 AND postsWithSoftDeletes.recordcount eq 5"); - } - - function test_permanent_delete_one() { - transaction action="begin" { - model("post").deleteOne(softDelete = false); - postsWithoutSoftDeletes = model("post").findAll(); - postsWithSoftDeletes = model("post").findAll(includeSoftDeletes = true); - transaction action="rollback"; - } - assert("postsWithoutSoftDeletes.recordcount eq 4 AND postsWithSoftDeletes.recordcount eq 4"); - } - - function test_permanent_delete_one_of_soft_deleted_records() { - transaction action="begin" { - local.post = model("post").findOne(); - model("post").deleteOne(where = "id = #local.post.id#"); - model("post").deleteOne(where = "id = #local.post.id#", includeSoftDeletes = true, softDelete = false); - postsWithoutSoftDeletes = model("post").findAll(); - postsWithSoftDeletes = model("post").findAll(includeSoftDeletes = true); - transaction action="rollback"; - } - assert("postsWithoutSoftDeletes.recordcount eq 4 AND postsWithSoftDeletes.recordcount eq 4"); - } - -} diff --git a/core/src/wheels/tests/model/errors/errors.cfc b/core/src/wheels/tests/model/errors/errors.cfc deleted file mode 100644 index 4b541bde03..0000000000 --- a/core/src/wheels/tests/model/errors/errors.cfc +++ /dev/null @@ -1,171 +0,0 @@ -component extends="wheels.tests.Test" { - - function setup() { - user = model("user").findOne(); - user.addErrorToBase(message = "base error1"); - user.addErrorToBase(message = "base name error1", name = "base_errors"); - user.addErrorToBase(message = "base name error2", name = "base_errors"); - user.addError(property = "firstname", message = "firstname error1"); - user.addError(property = "firstname", message = "firstname error2"); - user.addError(property = "firstname", message = "firstname name error1", name = "firstname_errors"); - user.addError(property = "firstname", message = "firstname name error2", name = "firstname_errors"); - user.addError(property = "firstname", message = "firstname name error3", name = "firstname_errors"); - user.addError(property = "lastname", message = "lastname error1"); - user.addError(property = "lastname", message = "lastname error2"); - user.addError(property = "lastname", message = "lastname error3"); - user.addError(property = "lastname", message = "lastname name error1", name = "lastname_errors"); - user.addError(property = "lastname", message = "lastname name error2", name = "lastname_errors"); - } - - function test_error_information_for_lastname_property_no_name_provided() { - r = user.hasErrors(property = "lastname"); - assert('r eq true'); - r = user.errorCount(property = "lastname"); - assert('r eq 3'); - user.clearErrors(property = "lastname"); - r = user.errorCount(property = "lastname"); - assert('r eq 0'); - r = user.hasErrors(); - assert('r eq true'); - r = user.hasErrors(property = "lastname"); - assert('r eq false'); - r = user.hasErrors(property = "lastname", name = "lastname_errors"); - assert('r eq true'); - } - - function test_error_information_for_lastname_property_name_provided() { - r = user.hasErrors(property = "lastname", name = "lastname_errors"); - assert('r eq true'); - r = user.errorCount(property = "lastname", name = "lastname_errors"); - assert('r eq 2'); - user.clearErrors(property = "lastname", name = "lastname_errors"); - r = user.errorCount(property = "lastname", name = "lastname_errors"); - assert('r eq 0'); - r = user.hasErrors(); - assert('r eq true'); - r = user.hasErrors(property = "lastname", name = "lastname_errors"); - assert('r eq false'); - r = user.hasErrors(property = "lastname"); - assert('r eq true'); - } - - function test_error_information_for_firstname_property_no_name_provided() { - r = user.hasErrors(property = "firstname"); - assert('r eq true'); - r = user.errorCount(property = "firstname"); - assert('r eq 2'); - user.clearErrors(property = "firstname"); - r = user.errorCount(property = "firstname"); - assert('r eq 0'); - r = user.hasErrors(); - assert('r eq true'); - r = user.hasErrors(property = "firstname"); - assert('r eq false'); - r = user.hasErrors(property = "firstname", name = "firstname_errors"); - assert('r eq true'); - } - - function test_error_information_for_firstname_property_name_provided() { - r = user.hasErrors(property = "firstname", name = "firstname_errors"); - assert('r eq true'); - r = user.errorCount(property = "firstname", name = "firstname_errors"); - assert('r eq 3'); - user.clearErrors(property = "firstname", name = "firstname_errors"); - r = user.errorCount(property = "firstname", name = "firstname_errors"); - assert('r eq 0'); - r = user.hasErrors(); - assert('r eq true'); - r = user.hasErrors(property = "firstname", name = "firstname_errors"); - assert('r eq false'); - r = user.hasErrors(property = "firstname"); - assert('r eq true'); - } - - function test_error_information_for_base_no_name_provided() { - r = user.hasErrors(); - assert('r eq true'); - r = user.errorCount(); - assert('r eq 13'); - user.clearErrors(); - r = user.errorCount(); - assert('r eq 0'); - r = user.hasErrors(); - assert('r eq false'); - r = user.hasErrors(property = "lastname"); - assert('r eq false'); - r = user.hasErrors(property = "lastname", name = "lastname_errors"); - assert('r eq false'); - r = user.hasErrors(property = "firstname"); - assert('r eq false'); - r = user.hasErrors(property = "firstname", name = "firstname_errors"); - assert('r eq false'); - } - - function test_error_information_for_base_name_provided() { - r = user.hasErrors(name = "base_errors"); - assert('r eq true'); - r = user.errorCount(name = "base_errors"); - assert('r eq 2'); - user.clearErrors(name = "base_errors"); - debug('user.allErrors()', false); - r = user.errorCount(name = "base_errors"); - assert('r eq 0'); - r = user.hasErrors(name = "base_errors"); - assert('r eq false'); - r = user.hasErrors(property = "lastname"); - assert('r eq true'); - r = user.hasErrors(property = "lastname", name = "lastname_errors"); - assert('r eq true'); - r = user.hasErrors(property = "firstname"); - assert('r eq true'); - r = user.hasErrors(property = "firstname", name = "firstname_errors"); - assert('r eq true'); - } - - function test_error_information_for_incorrect_property() { - r = user.hasErrors(property = "firstnamex"); - assert('r eq false'); - r = user.errorCount(property = "firstnamex"); - assert('r eq 0'); - user.clearErrors(property = "firstnamex"); - r = user.hasErrors(); - assert('r eq true'); - r = user.hasErrors(property = "firstname"); - assert('r eq true'); - r = user.hasErrors(property = "firstname", name = "firstname_errors"); - assert('r eq true'); - } - - function test_error_information_for_incorrect_name_on_property() { - r = user.hasErrors(property = "firstname", name = "firstname_errorsx"); - assert('r eq false'); - r = user.errorCount(property = "firstname", name = "firstname_errorsx"); - assert('r eq 0'); - user.clearErrors(property = "firstname", name = "firstname_errorsx"); - r = user.hasErrors(); - assert('r eq true'); - r = user.hasErrors(property = "firstname", name = "firstname_errors"); - assert('r eq true'); - r = user.hasErrors(property = "firstname"); - assert('r eq true'); - } - - function test_error_information_for_incorrect_name_on_base() { - r = user.hasErrors(name = "base_errorsx"); - assert('r eq false'); - r = user.errorCount(name = "base_errorsx"); - assert('r eq 0'); - user.clearErrors(name = "base_errorsx"); - r = user.hasErrors(name = "base_errors"); - assert('r eq true'); - r = user.hasErrors(property = "lastname"); - assert('r eq true'); - r = user.hasErrors(property = "lastname", name = "lastname_errors"); - assert('r eq true'); - r = user.hasErrors(property = "firstname"); - assert('r eq true'); - r = user.hasErrors(property = "firstname", name = "firstname_errors"); - assert('r eq true'); - } - -} diff --git a/core/src/wheels/tests/model/miscellaneous/composite.cfc b/core/src/wheels/tests/model/miscellaneous/composite.cfc deleted file mode 100644 index 4db3e1b38b..0000000000 --- a/core/src/wheels/tests/model/miscellaneous/composite.cfc +++ /dev/null @@ -1,8 +0,0 @@ -component extends="wheels.tests.Test" { - - function test_associate_with_a_single_key_from_the_composite() { - shops = model("shop").findone(include = "city"); - assert("IsObject(shops)"); - } - -} diff --git a/core/src/wheels/tests/model/miscellaneous/objectFileName.cfc b/core/src/wheels/tests/model/miscellaneous/objectFileName.cfc deleted file mode 100644 index 09bff6a65d..0000000000 --- a/core/src/wheels/tests/model/miscellaneous/objectFileName.cfc +++ /dev/null @@ -1,23 +0,0 @@ -component extends="wheels.tests.Test" { - - function setup() { - objectPath = "/wheels/tests/_assets/models/"; - name = "PHOTOGALLERY"; - } - - function teardown() { - } - - function test_$fileExistsNoCase_returns_filename() { - expected = "PhotoGallery.cfc"; - actual = $fileExistsNoCase(ExpandPath(objectPath) & name & ".cfc"); - assert("actual EQ expected"); - } - - function test_$objectFileName_returns_model_class_name_in_same_case_as_file_wo_exandpath() { - actual = $objectFileName(name = "PhotoGallery", objectPath = objectPath, type = "model"); - expected = "PhotoGallery"; - assert("Compare(actual, expected) eq 0", "actual", "expected", "objectPath"); - } - -} diff --git a/core/src/wheels/tests/model/miscellaneous/objectid.cfc b/core/src/wheels/tests/model/miscellaneous/objectid.cfc deleted file mode 100644 index 0894f33e0c..0000000000 --- a/core/src/wheels/tests/model/miscellaneous/objectid.cfc +++ /dev/null @@ -1,16 +0,0 @@ -component extends="wheels.tests.Test" { - - function test_objectids_should_be_sequential_and_norepeating() { - photos = []; - s = {}; - for (i = 1; i lte 30; i++) { - ArrayAppend(photos, model("photo").new()); - }; - gallery = model("gallery").new(photos = photos); - for (i in gallery.photos) { - s[i.$objectid()] = ""; - }; - assert('StructCount(s) eq 30'); - } - -} diff --git a/core/src/wheels/tests/model/miscellaneous/primaryKeys.cfc b/core/src/wheels/tests/model/miscellaneous/primaryKeys.cfc deleted file mode 100644 index 209d8456a6..0000000000 --- a/core/src/wheels/tests/model/miscellaneous/primaryKeys.cfc +++ /dev/null @@ -1,51 +0,0 @@ -component extends="wheels.tests.Test" { - - function test_primarykey_returns_key() { - author = model("author"); - e = author.$classData().keys; - r = "id"; - assert("e IS r"); - r = author.primaryKey(); - assert("e IS r"); - r = author.primaryKeys(); - assert("e IS r"); - } - - function test_setprimarykey_appends_keys() { - author = model("author"); - author = Duplicate(author); - e = author.$classData().keys; - r = "id"; - assert("e IS r"); - author.setprimaryKeys("id2,id3"); - e = "id,id2,id3"; - r = author.primaryKeys(); - assert("e IS r"); - } - - function test_setprimarykey_not_append_duplicate_keys() { - author = model("author"); - author = Duplicate(author); - e = author.$classData().keys; - r = "id"; - assert("e IS r"); - author.setprimaryKeys("id2"); - author.setprimaryKeys("id2"); - e = "id,id2"; - r = author.primaryKeys(); - assert("e IS r"); - } - - function test_retrieve_primary_key_by_position() { - author = model("author"); - author = Duplicate(author); - author.setprimaryKeys("id2,id3"); - e = author.primaryKeys(1); - r = "id"; - assert("e IS r"); - e = author.primaryKeys(2); - r = "id2"; - assert("e IS r"); - } - -} diff --git a/core/src/wheels/tests/model/miscellaneous/setPagination.cfc b/core/src/wheels/tests/model/miscellaneous/setPagination.cfc deleted file mode 100644 index 714ce9e9f3..0000000000 --- a/core/src/wheels/tests/model/miscellaneous/setPagination.cfc +++ /dev/null @@ -1,64 +0,0 @@ -component extends="wheels.tests.Test" { - - function setup() { - user = model("user"); - } - - function test_only_totalRecords() { - setPagination(100); - assert_pagination(handle = "query", totalRecords = 100); - } - - function test_all_arguments() { - setPagination(1000, 4, 50, "pageTest"); - assert_pagination( - handle = "pageTest", - totalRecords = 1000, - currentPage = 4, - perPage = 50 - ); - } - - function test_totalRecords_less_than_zero() { - setPagination(-5, 4, 50, "pageTest"); - assert_pagination( - handle = "pageTest", - totalRecords = 0, - currentPage = 1, - perPage = 50 - ); - } - - function test_currentPage_less_than_one() { - setPagination(1000, -4, 50, "pageTest"); - assert_pagination( - handle = "pageTest", - totalRecords = 1000, - currentPage = 1, - perPage = 50 - ); - } - - function test_numeric_arguments_must_be_integers() { - setPagination(1000.9998, 5.876, 50.847, "pageTest"); - assert_pagination( - handle = "pageTest", - totalRecords = 1000, - currentPage = 5, - perPage = 50 - ); - } - - function assert_pagination(required string handle) { - args = arguments; - assert('StructKeyExists(request.wheels, args.handle)'); - p = request.wheels[args.handle]; - StructDelete(args, "handle", false); - for (i in args) { - actual = p[i]; - expected = args[i]; - assert('actual eq expected'); - }; - } - -} diff --git a/core/src/wheels/tests/model/miscellaneous/tableName.cfc b/core/src/wheels/tests/model/miscellaneous/tableName.cfc deleted file mode 100644 index 06392e2a82..0000000000 --- a/core/src/wheels/tests/model/miscellaneous/tableName.cfc +++ /dev/null @@ -1,13 +0,0 @@ -component extends="wheels.tests.Test" { - - function test_tablename_and_tablenameprefix() { - user = model("user2"); - assert('user.tableName() eq "c_o_r_e_tblusers"'); - } - - function test_tablename_and_tablenameprefix_in_finders_fixes_issue_667() { - users = model("user2").findAll(select = "id"); - assert('users.recordcount eq 3'); - } - -} diff --git a/core/src/wheels/tests/model/nestedproperties/has_many.cfc b/core/src/wheels/tests/model/nestedproperties/has_many.cfc deleted file mode 100644 index b4ec69b492..0000000000 --- a/core/src/wheels/tests/model/nestedproperties/has_many.cfc +++ /dev/null @@ -1,187 +0,0 @@ -๏ปฟcomponent extends = "wheels.tests.Test"{ - function setup() { - gallery = model("gallery"); - photo = model("photo"); - user = model("user"); - testGallery = $setTestObjects(); - idx = {}; // CF10 has issues with just using for(i in foo) - } - - function test_add_children_via_object_array() { - transaction { - assert("testGallery.save()"); - testGallery = gallery.findOneByTitle(value = "Nested Properties Gallery", include = "photos"); - assert("IsArray(testGallery.photos)"); - assert("ArrayLen(testGallery.photos) eq 3"); - transaction action="rollback"; - } - } - - function test_delete_children_via_object_array() { - transaction { - assert("testGallery.save()"); - testGallery = gallery.findOneByTitle(value = "Nested Properties Gallery", include = "photos"); - for (idx.i in testGallery.photos) { - idx.i._delete = true; - }; - testGallery.save(); - assert("IsArray(testGallery.photos)"); - assert("ArrayLen(testGallery.photos) eq 0"); - transaction action="rollback"; - } - } - - function test_valid_beforeValidation_callbacks_on_children() { - assert("testGallery.valid()"); - for (idx.i in testGallery.photos) { - assert("idx.i.beforeValidationCallbackRegistered"); - assert("idx.i.beforeValidationCallbackCount eq 1"); - }; - } - - function test_valid_beforeValidation_callbacks_on_children_with_validation_error_on_parent() { - testGallery.title = ""; - assert("not testGallery.valid()"); - for (idx.i in testGallery.photos) { - assert("idx.i.beforeValidationCallbackRegistered"); - assert("idx.i.beforeValidationCallbackCount eq 1"); - }; - } - - function test_save_beforeValidation_callbacks_on_children() { - transaction { - assert("testGallery.save()"); - for (idx.i in testGallery.photos) { - assert("idx.i.beforeValidationCallbackRegistered"); - assert("idx.i.beforeValidationCallbackCount eq 1"); - }; - transaction action="rollback"; - } - } - - function test_save_beforeValidation_callbacks_on_children_with_validation_error_on_parent() { - testGallery.title = ""; - transaction { - assert("not testGallery.save()"); - for (idx.i in testGallery.photos) { - assert("idx.i.beforeValidationCallbackRegistered"); - assert("idx.i.beforeValidationCallbackCount eq 1"); - }; - transaction action="rollback"; - } - } - - function test_beforeCreate_callback_on_children() { - transaction { - testGallery.save(); - for (idx.i in testGallery.photos) { - assert("idx.i.beforeCreateCallbackCount eq 1"); - }; - transaction action="rollback"; - } - } - - function test_beforeSave_callback_on_children() { - transaction { - testGallery.save(); - for (idx.i in testGallery.photos) { - assert("idx.i.beforeSaveCallbackCount eq 1"); - }; - transaction action="rollback"; - } - } - - function test_afterCreate_callback_on_children() { - transaction { - testGallery.save(); - for (idx.i in testGallery.photos) { - assert("idx.i.afterCreateCallbackCount eq 1"); - }; - transaction action="rollback"; - } - } - - function test_afterSave_callback_on_children() { - transaction { - testGallery.save(); - for (idx.i in testGallery.photos) { - assert("idx.i.afterSaveCallbackCount eq 1"); - }; - transaction action="rollback"; - } - } - - function test_parent_primary_key_rolled_back_on_parent_validation_error() { - testGallery.title = ""; - transaction { - assert("not testGallery.save()"); - transaction action="rollback"; - } - assert("not Len(testGallery.key())"); - } - - function test_children_primary_keys_rolled_back_on_parent_validation_error() { - testGallery.title = ""; - transaction { - assert("not testGallery.save()"); - transaction action="rollback"; - } - for (idx.i in testGallery.photos) { - assert("not Len(idx.i.key())"); - }; - } - - function test_parent_primary_key_rolled_back_on_child_validation_error() { - testGallery.photos[2].filename = ""; - transaction { - assert("not testGallery.save()"); - transaction action="rollback"; - } - assert("not Len(testGallery.key())"); - } - - function test_children_primary_keys_rolled_back_on_child_validation_error() { - testGallery.photos[2].filename = ""; - transaction { - assert("not testGallery.save()"); - transaction action="rollback"; - } - for (idx.i in testGallery.photos) { - assert("not Len(idx.i.key())"); - }; - } - - /** - * HELPERS - */ - - private any function $setTestObjects() { - /* User */ - var u = user.findOneByLastName("Petruzzi"); - /* Gallery */ - var _params = { - userId = u.id, - title = "Nested Properties Gallery", - description = "A gallery testing nested properties." - }; - var g = gallery.new(_params); - g.photos = [ - photo.new( - userId = u.id, - filename = "Nested Properties Photo Test 1", - DESCRIPTION1 = "test photo 1 for nested properties gallery" - ), - photo.new( - userId = u.id, - filename = "Nested Properties Photo Test 2", - DESCRIPTION1 = "test photo 2 for nested properties gallery" - ), - photo.new( - userId = u.id, - filename = "Nested Properties Photo Test 3", - DESCRIPTION1 = "test photo 3 for nested properties gallery" - ) - ]; - return g; - } -} diff --git a/core/src/wheels/tests/model/nestedproperties/has_one.cfc b/core/src/wheels/tests/model/nestedproperties/has_one.cfc deleted file mode 100644 index 15979d2da5..0000000000 --- a/core/src/wheels/tests/model/nestedproperties/has_one.cfc +++ /dev/null @@ -1,238 +0,0 @@ -๏ปฟcomponent extends = "wheels.tests.Test"{ - function setup() { - author = model("author"); - profile = model("profile"); - $setTestObjects(); - testParamsStruct = $setTestParamsStruct(); - } - - /** - * Simulates adding an `author` and its child `profile` through a single - * structure passed into `author.create()`, much like what's normally done - * with the `params` struct. - */ - function test_add_entire_data_set_via_create_and_struct() { - transaction { - /* Should return `true` on successful create */ - author = author.create(testParamsStruct.author); - assert('IsObject(author)'); - transaction action="rollback"; - } - /* Test whether profile was transformed into an object */ - assert("IsObject(author.profile)"); - /* Test generated primary keys */ - assert("IsNumeric(author.id) and IsNumeric(author.profile.id)"); - } - - /** - * Simulates adding an `author` and its child `profile` through a single - * structure passed into `author.new()` and saved with `author.save()`, much - * like what's normally done with the `params` struct. - */ - function test_add_entire_data_set_via_new_and_struct() { - author = author.new(testParamsStruct.author); - transaction { - /* Should return `true` on successful create */ - assert("author.save()"); - transaction action="rollback"; - } - /* Test whether profile was transformed into an object */ - assert("IsObject(author.profile)"); - /* Test generated primary keys */ - assert("IsNumeric(author.id) and IsNumeric(author.profile.id)"); - } - - /** - * Loads an existing `author` and sets its child `profile` as an object before saving. - */ - function test_add_child_via_object() { - transaction { - assert("testAuthor.save()"); - p = profile.findByKey(testAuthor.profile.id); - transaction action="rollback"; - } - assert("IsObject(p)"); - } - - /* - * Loads an existing `author` and sets its child `profile` as a struct before saving. - */ - function test_add_child_via_struct() { - transaction { - assert("testAuthor.save()"); - testAuthor.profile = {dateOfBirth = "10/02/1980 18:00:00", bio = bioText}; - assert("testAuthor.save()"); - assert("IsObject(testAuthor.profile)"); - p = profile.findByKey(testAuthor.profile.id); - transaction action="rollback"; - } - assert("IsObject(p)"); - } - - /** - * Loads an existing `author` and deletes its child `profile` by setting the `_delete` property to `true`. - */ - function test_delete_child_through_object_property() { - transaction { - testAuthor.save(); - /* Delete profile through nested property */ - testAuthor.profile._delete = true; - profileID = testAuthor.profile.id; - assert("testAuthor.save()"); - /* Should return `false` because the record is now deleted */ - missingProfile = profile.findByKey(key = profileId, reload = true); - transaction action="rollback"; - } - assert("IsBoolean(missingProfile) and not missingProfile"); - } - - /** - * Loads an existing `author` and deletes its child `property` by passing in a struct through `update()`. - */ - function test_delete_child_through_struct() { - transaction { - /* Save test author with child profile and grab new profile's ID */ - testAuthor.save(); - profileID = testAuthor.profile.id; - /* Delete profile through nested property */ - updateStruct.profile._delete = true; - assert("testAuthor.update(properties=updateStruct)"); - /* Should return `false` because the record is now deleted */ - missingProfile = profile.findByKey(key = profileId, reload = true); - transaction action="rollback"; - } - assert("IsBoolean(missingProfile) and not missingProfile"); - } - - function test_valid_beforeValidation_callback_on_child() { - assert("testAuthor.valid()"); - assert("testAuthor.profile.beforeValidationCallbackRegistered"); - assert("testAuthor.profile.beforeValidationCallbackCount eq 1"); - } - - function test_valid_beforeValidation_callback_on_child_with_validation_error_on_parent() { - testAuthor.firstName = ""; - assert("not testAuthor.valid()"); - assert("testAuthor.profile.beforeValidationCallbackRegistered"); - assert("testAuthor.profile.beforeValidationCallbackCount eq 1"); - } - - function test_save_beforeValidation_callback_on_child() { - transaction { - assert("testAuthor.save()"); - assert("testAuthor.profile.beforeValidationCallbackRegistered"); - assert("testAuthor.profile.beforeValidationCallbackCount eq 1"); - transaction action="rollback"; - } - } - - function test_save_beforeValidation_callback_on_child_with_validation_error_on_parent() { - testAuthor.firstName = ""; - transaction { - assert("not testAuthor.save()"); - assert("testAuthor.profile.beforeValidationCallbackRegistered"); - assert("testAuthor.profile.beforeValidationCallbackCount eq 1"); - transaction action="rollback"; - } - } - - function test_beforeCreate_callback_on_child() { - transaction { - testAuthor.save(); - assert("testAuthor.profile.beforeCreateCallbackCount eq 1"); - transaction action="rollback"; - } - } - - function test_beforeSave_callback_on_child() { - transaction { - testAuthor.save(); - assert("testAuthor.profile.beforeSaveCallbackCount eq 1"); - transaction action="rollback"; - } - } - - function test_afterCreate_callback_on_child() { - transaction { - testAuthor.save(); - assert("testAuthor.profile.afterCreateCallbackCount eq 1"); - transaction action="rollback"; - } - } - - function test_afterSave_callback_on_child() { - transaction { - testAuthor.save(); - assert("testAuthor.profile.afterSaveCallbackCount eq 1"); - transaction action="rollback"; - } - } - - function test_parent_primary_key_rolled_back_on_parent_validation_error() { - testAuthor = author.new(testParams.author); - testAuthor.firstName = ""; - transaction { - testAuthor.save(); - transaction action="rollback"; - } - assert("not Len(testAuthor.key())"); - } - - function test_child_primary_key_rolled_back_on_parent_validation_error() { - testAuthor = author.new(testParams.author); - testAuthor.firstName = ""; - transaction { - testAuthor.save(); - transaction action="rollback"; - } - assert("not Len(testAuthor.profile.key())"); - } - - function test_parent_primary_key_rolled_back_on_child_validation_error() { - testAuthor = author.new(testParams.author); - testAuthor.profile.dateOfBirth = ""; - transaction { - testAuthor.save(); - transaction action="rollback"; - } - assert("not Len(testAuthor.key())"); - } - - function test_child_primary_key_rolled_back_on_child_validation_error() { - testAuthor = author.new(testParams.author); - testAuthor.profile.dateOfBirth = ""; - transaction { - testAuthor.save(); - transaction action="rollback"; - } - assert("not Len(testAuthor.profile.key())"); - } - - /** - * HELPERS - */ - - private void function $setTestObjects() { - testAuthor = author.findOneByLastName(value = "Peters", include = "profile"); - bioText = "Loves learning how to write tests."; - testAuthor.profile = model("profile").new(dateOfBirth = "10/02/1980 18:00:00", bio = bioText); - } - - /** - * Sets up test `author` struct reminiscent of what would be passed through a - * form. The `author` represented here also includes a nested child `profile` struct. - */ - private struct function $setTestParamsStruct() { - testParams = { - author = { - firstName = "Brian", - lastName = "Meloche", - profile = { - dateOfBirth = "10/02/1970 18:01:00", - bio = "Host of CFConversations, the best ColdFusion podcast out there." - } - } - }; - return testParams; - } -} diff --git a/core/src/wheels/tests/model/onmissingmethod/belongsto/hasobject.cfc b/core/src/wheels/tests/model/onmissingmethod/belongsto/hasobject.cfc deleted file mode 100644 index bd44dae3f8..0000000000 --- a/core/src/wheels/tests/model/onmissingmethod/belongsto/hasobject.cfc +++ /dev/null @@ -1,26 +0,0 @@ -component extends="wheels.tests.Test" { - - function setup() { - profileModel = model("profile"); - combiKeyModel = model("combiKey"); - } - - function test_hasObject_valid() { - profile = profileModel.findByKey(key = 1); - hasAuthor = profile.hasAuthor(); - assert('hasAuthor eq true'); - } - - function test_hasObject_valid_with_combi_key() { - combikey = combiKeyModel.findByKey(key = "1,1"); - hasUser = combikey.hasUser(); - assert('hasUser eq true'); - } - - function test_hasObject_returns_false() { - profile = profileModel.findByKey(key = 2); - hasAuthor = profile.hasAuthor(); - assert('hasAuthor eq false'); - } - -} diff --git a/core/src/wheels/tests/model/onmissingmethod/belongsto/object.cfc b/core/src/wheels/tests/model/onmissingmethod/belongsto/object.cfc deleted file mode 100644 index 49e3bb20f3..0000000000 --- a/core/src/wheels/tests/model/onmissingmethod/belongsto/object.cfc +++ /dev/null @@ -1,26 +0,0 @@ -component extends="wheels.tests.Test" { - - function setup() { - profileModel = model("profile"); - combiKeyModel = model("combiKey"); - } - - function test_object_valid() { - profile = profileModel.findByKey(key = 1); - author = profile.author(); - assert("IsObject(author) eq true"); - } - - function test_object_valid_with_combi_key() { - combikey = combiKeyModel.findByKey(key = "1,1"); - user = combikey.user(); - assert('IsObject(user) eq true'); - } - - function test_object_returns_false() { - profile = profileModel.findByKey(key = 2); - author = profile.author(); - assert("IsObject(author) eq false"); - } - -} diff --git a/core/src/wheels/tests/model/onmissingmethod/hasMany/addobject.cfc b/core/src/wheels/tests/model/onmissingmethod/hasMany/addobject.cfc deleted file mode 100644 index 73f7464324..0000000000 --- a/core/src/wheels/tests/model/onmissingmethod/hasMany/addobject.cfc +++ /dev/null @@ -1,18 +0,0 @@ -component extends="wheels.tests.Test" { - - function setup() { - authorModel = model("author"); - } - - function test_addObject_valid() { - author = authorModel.findOne(where = "firstName = 'James'"); - post = model("post").findOne(); - transaction action="begin" { - updated = author.addPost(post); - assert('updated eq true'); - assert('post.authorid eq author.id'); - transaction action="rollback"; - } - } - -} diff --git a/core/src/wheels/tests/model/onmissingmethod/hasMany/createobject.cfc b/core/src/wheels/tests/model/onmissingmethod/hasMany/createobject.cfc deleted file mode 100644 index 7a2522f90d..0000000000 --- a/core/src/wheels/tests/model/onmissingmethod/hasMany/createobject.cfc +++ /dev/null @@ -1,17 +0,0 @@ -component extends="wheels.tests.Test" { - - function setup() { - authorModel = model("author"); - } - - function test_createObject_valid() { - author = authorModel.findOne(where = "firstName = 'James'"); - transaction action="begin" { - post = author.createPost(title = "Title for first test post", body = "Text for first test post", views = 0); - assert('IsObject(post) eq true'); - assert('post.authorid eq author.id'); - transaction action="rollback"; - } - } - -} diff --git a/core/src/wheels/tests/model/onmissingmethod/hasMany/deleteallobjects.cfc b/core/src/wheels/tests/model/onmissingmethod/hasMany/deleteallobjects.cfc deleted file mode 100644 index 4df5924455..0000000000 --- a/core/src/wheels/tests/model/onmissingmethod/hasMany/deleteallobjects.cfc +++ /dev/null @@ -1,18 +0,0 @@ -component extends="wheels.tests.Test" { - - function setup() { - authorModel = model("author"); - } - - function test_deleteAllObjects_valid() { - author = authorModel.findOne(where = "firstName = 'Per'"); - transaction action="begin" { - updated = author.deleteAllPosts(); - posts = author.posts(); - assert('IsNumeric(updated) and updated eq 3'); - assert('IsQuery(posts) eq true and not posts.Recordcount'); - transaction action="rollback"; - } - } - -} diff --git a/core/src/wheels/tests/model/onmissingmethod/hasMany/deleteobject.cfc b/core/src/wheels/tests/model/onmissingmethod/hasMany/deleteobject.cfc deleted file mode 100644 index 3e276544b3..0000000000 --- a/core/src/wheels/tests/model/onmissingmethod/hasMany/deleteobject.cfc +++ /dev/null @@ -1,19 +0,0 @@ -component extends="wheels.tests.Test" { - - function setup() { - authorModel = model("author"); - } - - function test_deleteObject_valid() { - author = authorModel.findOne(where = "firstName = 'Per'"); - post = author.findOnePost(order = "id"); - transaction action="begin" { - updated = author.deletePost(post); - post = model("post").findByKey(key = post.id); - assert('updated eq true'); - assert('not IsObject(post) and post eq false'); - transaction action="rollback"; - } - } - -} diff --git a/core/src/wheels/tests/model/onmissingmethod/hasMany/findoneobject.cfc b/core/src/wheels/tests/model/onmissingmethod/hasMany/findoneobject.cfc deleted file mode 100644 index dfa5ba6d0a..0000000000 --- a/core/src/wheels/tests/model/onmissingmethod/hasMany/findoneobject.cfc +++ /dev/null @@ -1,13 +0,0 @@ -component extends="wheels.tests.Test" { - - function setup() { - authorModel = model("author"); - } - - function test_findOneObject_valid() { - author = authorModel.findOne(where = "firstName = 'Per'"); - post = author.findOnePost(order = "id"); - assert('IsObject(post) eq true'); - } - -} diff --git a/core/src/wheels/tests/model/onmissingmethod/hasMany/hasobjects.cfc b/core/src/wheels/tests/model/onmissingmethod/hasMany/hasobjects.cfc deleted file mode 100644 index 5fec485845..0000000000 --- a/core/src/wheels/tests/model/onmissingmethod/hasMany/hasobjects.cfc +++ /dev/null @@ -1,26 +0,0 @@ -component extends="wheels.tests.Test" { - - function setup() { - authorModel = model("author"); - userModel = model("user"); - } - - function test_hasObjects_valid() { - author = authorModel.findOne(where = "firstName = 'Per'"); - hasPosts = author.hasPosts(); - assert('hasPosts eq true'); - } - - function test_hasObjects_valid_with_combi_key() { - user = userModel.findByKey(key = 1); - hasCombiKeys = user.hasCombiKeys(); - assert('hasCombiKeys eq true'); - } - - function test_hasObjects_returns_false() { - author = authorModel.findOne(where = "firstName = 'James'"); - hasPosts = author.hasPosts(); - assert('hasPosts eq false'); - } - -} diff --git a/core/src/wheels/tests/model/onmissingmethod/hasMany/newobject.cfc b/core/src/wheels/tests/model/onmissingmethod/hasMany/newobject.cfc deleted file mode 100644 index d9ec573009..0000000000 --- a/core/src/wheels/tests/model/onmissingmethod/hasMany/newobject.cfc +++ /dev/null @@ -1,14 +0,0 @@ -component extends="wheels.tests.Test" { - - function setup() { - authorModel = model("author"); - } - - function test_newObject_valid() { - author = authorModel.findOne(where = "firstName = 'James'"); - post = author.newPost(); - assert('IsObject(post) eq true'); - assert('post.authorid eq author.id'); - } - -} diff --git a/core/src/wheels/tests/model/onmissingmethod/hasMany/objectcount.cfc b/core/src/wheels/tests/model/onmissingmethod/hasMany/objectcount.cfc deleted file mode 100644 index e2143c5be7..0000000000 --- a/core/src/wheels/tests/model/onmissingmethod/hasMany/objectcount.cfc +++ /dev/null @@ -1,26 +0,0 @@ -component extends="wheels.tests.Test" { - - function setup() { - authorModel = model("author"); - userModel = model("user"); - } - - function test_objectCount_valid() { - author = authorModel.findOne(where = "firstName = 'Per'"); - postCount = author.postCount(); - assert('postCount eq 3'); - } - - function test_objectCount_valid_with_combi_key() { - user = userModel.findByKey(key = 1); - combiKeyCount = user.combiKeyCount(); - assert('combiKeyCount eq 5'); - } - - function test_objectCount_returns_zero() { - author = authorModel.findOne(where = "firstName = 'James'"); - postCount = author.postCount(); - assert('postCount eq 0'); - } - -} diff --git a/core/src/wheels/tests/model/onmissingmethod/hasMany/objects.cfc b/core/src/wheels/tests/model/onmissingmethod/hasMany/objects.cfc deleted file mode 100644 index fe8f486a0b..0000000000 --- a/core/src/wheels/tests/model/onmissingmethod/hasMany/objects.cfc +++ /dev/null @@ -1,35 +0,0 @@ -component extends="wheels.tests.Test" { - - function setup() { - authorModel = model("author"); - userModel = model("user"); - } - - function test_objects_returns_query() { - author = authorModel.findOne(where = "firstName = 'Per'"); - posts = author.posts(); - assert('IsQuery(posts) eq true'); - assert('posts.Recordcount'); - } - - function test_objects_valid_with_combi_key() { - user = userModel.findByKey(key = 1); - combiKeys = user.combiKeys(); - assert('IsQuery(combiKeys) eq true'); - assert('combiKeys.Recordcount'); - } - - function test_objects_returns_empty_query() { - author = authorModel.findOne(where = "firstName = 'James'"); - posts = author.posts(); - assert('IsQuery(posts) eq true'); - assert('not posts.Recordcount'); - } - - function test_pagination_with_blank_where() { - author = authorModel.findOne(where = "firstName = 'Per'"); - posts = author.posts(where = "", page = 1, perPage = 2); - assert('posts.Recordcount IS 2'); - } - -} diff --git a/core/src/wheels/tests/model/onmissingmethod/hasMany/removeallobjects.cfc b/core/src/wheels/tests/model/onmissingmethod/hasMany/removeallobjects.cfc deleted file mode 100644 index 5d225d888f..0000000000 --- a/core/src/wheels/tests/model/onmissingmethod/hasMany/removeallobjects.cfc +++ /dev/null @@ -1,18 +0,0 @@ -component extends="wheels.tests.Test" { - - function setup() { - authorModel = model("author"); - } - - function test_removeAllObjects_valid() { - author = authorModel.findOne(where = "firstName = 'Per'"); - transaction action="begin" { - updated = author.removeAllPosts(); - posts = author.posts(); - assert('IsNumeric(updated) and updated eq 3'); - assert('IsQuery(posts) eq true and not posts.Recordcount'); - transaction action="rollback"; - } - } - -} diff --git a/core/src/wheels/tests/model/onmissingmethod/hasMany/removeobject.cfc b/core/src/wheels/tests/model/onmissingmethod/hasMany/removeobject.cfc deleted file mode 100644 index 452d4cda03..0000000000 --- a/core/src/wheels/tests/model/onmissingmethod/hasMany/removeobject.cfc +++ /dev/null @@ -1,19 +0,0 @@ -component extends="wheels.tests.Test" { - - function setup() { - authorModel = model("author"); - } - - function test_removeObject_valid() { - author = authorModel.findOne(where = "firstName = 'Per'"); - post = author.findOnePost(order = "id"); - transaction action="begin" { - updated = author.removePost(post); - post.reload(); - assert('updated eq true'); - assert('post.authorid eq ""'); - transaction action="rollback"; - } - } - -} diff --git a/core/src/wheels/tests/model/onmissingmethod/hasone/createobject.cfc b/core/src/wheels/tests/model/onmissingmethod/hasone/createobject.cfc deleted file mode 100644 index 3a071404b7..0000000000 --- a/core/src/wheels/tests/model/onmissingmethod/hasone/createobject.cfc +++ /dev/null @@ -1,17 +0,0 @@ -component extends="wheels.tests.Test" { - - function setup() { - authorModel = model("author"); - } - - function test_createObject_valid() { - author = authorModel.findOne(where = "firstName = 'James'"); - transaction action="begin" { - profile = author.createProfile(dateOfBirth = "1/1/1970", bio = "Some profile."); - assert('IsObject(profile) eq true'); - assert('profile.authorid eq author.id'); - transaction action="rollback"; - } - } - -} diff --git a/core/src/wheels/tests/model/onmissingmethod/hasone/deleteobject.cfc b/core/src/wheels/tests/model/onmissingmethod/hasone/deleteobject.cfc deleted file mode 100644 index 36d381fe03..0000000000 --- a/core/src/wheels/tests/model/onmissingmethod/hasone/deleteobject.cfc +++ /dev/null @@ -1,18 +0,0 @@ -component extends="wheels.tests.Test" { - - function setup() { - authorModel = model("author"); - } - - function test_deleteObject_valid() { - author = authorModel.findOne(where = "firstName = 'Per'"); - transaction action="begin" { - updated = author.deleteProfile(); - profile = author.profile(); - assert('updated eq true'); - assert('profile eq false'); - transaction action="rollback"; - } - } - -} diff --git a/core/src/wheels/tests/model/onmissingmethod/hasone/hasobject.cfc b/core/src/wheels/tests/model/onmissingmethod/hasone/hasobject.cfc deleted file mode 100644 index a91b4bc387..0000000000 --- a/core/src/wheels/tests/model/onmissingmethod/hasone/hasobject.cfc +++ /dev/null @@ -1,26 +0,0 @@ -component extends="wheels.tests.Test" { - - function setup() { - authorModel = model("author"); - userModel = model("user"); - } - - function test_hasObject_valid() { - author = authorModel.findOne(where = "firstName = 'Per'"); - hasProfile = author.hasProfile(); - assert('hasProfile eq true'); - } - - function test_hasObject_valid_with_combi_key() { - user = userModel.findByKey(key = 1); - hasCombiKey = user.hasCombiKey(); - assert('hasCombiKey eq true'); - } - - function test_hasObject_returns_false() { - author = authorModel.findOne(where = "firstName = 'James'"); - hasProfile = author.hasProfile(); - assert('hasProfile eq false'); - } - -} diff --git a/core/src/wheels/tests/model/onmissingmethod/hasone/newobject.cfc b/core/src/wheels/tests/model/onmissingmethod/hasone/newobject.cfc deleted file mode 100644 index e8056d32d6..0000000000 --- a/core/src/wheels/tests/model/onmissingmethod/hasone/newobject.cfc +++ /dev/null @@ -1,14 +0,0 @@ -component extends="wheels.tests.Test" { - - function setup() { - authorModel = model("author"); - } - - function test_newObject_valid() { - author = authorModel.findOne(where = "firstName = 'James'"); - profile = author.newProfile(); - assert('IsObject(profile) eq true'); - assert('profile.authorid eq author.id'); - } - -} diff --git a/core/src/wheels/tests/model/onmissingmethod/hasone/object.cfc b/core/src/wheels/tests/model/onmissingmethod/hasone/object.cfc deleted file mode 100644 index d6bcadf3d0..0000000000 --- a/core/src/wheels/tests/model/onmissingmethod/hasone/object.cfc +++ /dev/null @@ -1,26 +0,0 @@ -component extends="wheels.tests.Test" { - - function setup() { - authorModel = model("author"); - userModel = model("user"); - } - - function test_object_valid() { - author = authorModel.findOne(where = "firstName = 'Per'"); - profile = author.profile(); - assert('IsObject(profile) eq true'); - } - - function test_object_valid_with_combi_key() { - user = userModel.findByKey(key = 1); - combiKey = user.combiKey(); - assert('IsObject(combiKey) eq true'); - } - - function test_object_returns_false() { - author = authorModel.findOne(where = "firstName = 'James'"); - profile = author.profile(); - assert('IsObject(profile) eq false'); - } - -} diff --git a/core/src/wheels/tests/model/onmissingmethod/hasone/removeobject.cfc b/core/src/wheels/tests/model/onmissingmethod/hasone/removeobject.cfc deleted file mode 100644 index ae354612de..0000000000 --- a/core/src/wheels/tests/model/onmissingmethod/hasone/removeobject.cfc +++ /dev/null @@ -1,19 +0,0 @@ -component extends="wheels.tests.Test" { - - function setup() { - authorModel = model("author"); - } - - function test_removeObject_valid() { - author = authorModel.findOne(where = "firstName = 'Per'"); - profile = author.profile(); - transaction action="begin" { - updated = author.removeProfile(); - profile.reload(); - assert('updated eq true'); - assert('profile.authorid eq ""'); - transaction action="rollback"; - } - } - -} diff --git a/core/src/wheels/tests/model/onmissingmethod/hasone/setobject.cfc b/core/src/wheels/tests/model/onmissingmethod/hasone/setobject.cfc deleted file mode 100644 index 4fe9359f70..0000000000 --- a/core/src/wheels/tests/model/onmissingmethod/hasone/setobject.cfc +++ /dev/null @@ -1,18 +0,0 @@ -component extends="wheels.tests.Test" { - - function setup() { - authorModel = model("author"); - } - - function test_setObject_valid() { - author = authorModel.findOne(where = "firstName = 'James'"); - profile = model("profile").findOne(); - transaction action="begin" { - updated = author.setProfile(profile); - assert('updated eq true'); - assert('profile.authorid eq author.id'); - transaction action="rollback"; - } - } - -} diff --git a/core/src/wheels/tests/model/properties/PropertyIsBlank.cfc b/core/src/wheels/tests/model/properties/PropertyIsBlank.cfc deleted file mode 100644 index 046fab6c6f..0000000000 --- a/core/src/wheels/tests/model/properties/PropertyIsBlank.cfc +++ /dev/null @@ -1,29 +0,0 @@ -component extends="wheels.tests.Test" { - - function test_returns_false_when_property_is_set() { - _model = model("author"); - properties = {firstName = "James", lastName = "Gibson"}; - _model = _model.new(properties = properties); - assert('_model.propertyIsBlank("firstName") eq false'); - } - - function test_returns_true_when_property_is_blank() { - _model = model("author").new(); - _model.lastName = ""; - assert('_model.propertyIsBlank("lastName") eq true'); - } - - function test_returns_true_when_property_does_not_exist() { - _model = model("author").new(); - StructDelete(_model, "lastName"); - assert('_model.propertyIsBlank("lastName") eq true'); - } - - function test_dynamic_method_call() { - _model = model("author"); - properties = {firstName = "James", lastName = "Gibson"}; - _model = _model.new(properties = properties); - assert('_model.firstNameIsBlank() eq false'); - } - -} diff --git a/core/src/wheels/tests/model/properties/accessibleproperties.cfc b/core/src/wheels/tests/model/properties/accessibleproperties.cfc deleted file mode 100644 index 0fa56dfada..0000000000 --- a/core/src/wheels/tests/model/properties/accessibleproperties.cfc +++ /dev/null @@ -1,42 +0,0 @@ -component extends="wheels.tests.Test" { - - function test_all_properties_can_be_set_by_default() { - _model = model("author"); - _model = Duplicate(_model); - properties = {firstName = "James", lastName = "Gibson"}; - _model = _model.new(properties = properties); - assert('StructKeyExists(_model, "firstName") eq true'); - assert('StructKeyExists(_model, "lastName") eq true'); - } - - function test_all_other_properties_cannot_be_set_except_accessible_properties() { - _model = model("post"); - _model = Duplicate(_model); - _model.accessibleProperties(properties = "views"); - properties = { - views = "2000", - averageRating = 4.9, - body = "This is the body", - title = "this is the title" - }; - _model = _model.new(properties = properties); - assert('StructKeyExists(_model, "averageRating") eq false'); - assert('StructKeyExists(_model, "body") eq false'); - assert('StructKeyExists(_model, "title") eq false'); - assert('StructKeyExists(_model, "views") eq true'); - } - - function test_all_other_properties_can_be_set_directly() { - _model = model("post"); - _model = Duplicate(_model); - _model.accessibleProperties(properties = "views"); - _model = _model.new(); - _model.averageRating = 4.9; - _model.body = "This is the body"; - _model.title = "this is the title"; - assert('StructKeyExists(_model, "averageRating") eq true'); - assert('StructKeyExists(_model, "body") eq true'); - assert('StructKeyExists(_model, "title") eq true'); - } - -} diff --git a/core/src/wheels/tests/model/properties/columnforproperty.cfc b/core/src/wheels/tests/model/properties/columnforproperty.cfc deleted file mode 100644 index aa784fbcc5..0000000000 --- a/core/src/wheels/tests/model/properties/columnforproperty.cfc +++ /dev/null @@ -1,18 +0,0 @@ -component extends="wheels.tests.Test" { - - function test_columnForProperty_returns_column_name() { - _model = model("author").new(); - assert('_model.columnForProperty("firstName") eq "firstname"'); - } - - function test_columnForProperty_returns_false() { - _model = model("author").new(); - assert('_model.columnForProperty("myFavy") eq false'); - } - - function test_columnForProperty_dynamic_method_call() { - _model = model("author").new(); - assert('_model.columnForFirstName() eq "firstname"'); - } - -} diff --git a/core/src/wheels/tests/model/properties/columns.cfc b/core/src/wheels/tests/model/properties/columns.cfc deleted file mode 100644 index e925583d13..0000000000 --- a/core/src/wheels/tests/model/properties/columns.cfc +++ /dev/null @@ -1,8 +0,0 @@ -component extends="wheels.tests.Test" { - - function test_columns_returns_array() { - columns = model("author").columns(); - assert('IsArray(columns) eq true'); - } - -} diff --git a/core/src/wheels/tests/model/properties/defaults.cfc b/core/src/wheels/tests/model/properties/defaults.cfc deleted file mode 100644 index aec9c1013c..0000000000 --- a/core/src/wheels/tests/model/properties/defaults.cfc +++ /dev/null @@ -1,41 +0,0 @@ -component extends="wheels.tests.Test" { - - function test_new_model_with_property_defaults() { - author = model("Author").new(); - assert('StructKeyExists(author, "firstName") and author.firstName eq "Dave"'); - } - - function test_new_model_with_property_defaults_set_to_blank() { - author = model("Author").new(); - assert('StructKeyExists(author, "lastName") and author.lastName eq ""'); - } - - function test_database_defaults_load_after_create() { - transaction action="begin" { - user = model("UserBlank").create( - username = "The Dude", - password = "doodle", - firstName = "The", - lastName = "Dude", - reload = true - ); - transaction action="rollback"; - } - assert('StructKeyExists(user, "birthTime") and TimeFormat(user.birthTime, "HH:mm:ss") eq "18:26:08"'); - } - - function test_database_defaults_load_after_save() { - transaction action="begin" { - user = model("UserBlank").new( - username = "The Dude", - password = "doodle", - firstName = "The", - lastName = "Dude" - ); - user.save(reload = true); - transaction action="rollback"; - } - assert('StructKeyExists(user, "birthTime") and TimeFormat(user.birthTime, "HH:mm:ss") eq "18:26:08"'); - } - -} diff --git a/core/src/wheels/tests/model/properties/hasChanged.cfc b/core/src/wheels/tests/model/properties/hasChanged.cfc deleted file mode 100644 index 6678f1b156..0000000000 --- a/core/src/wheels/tests/model/properties/hasChanged.cfc +++ /dev/null @@ -1,36 +0,0 @@ -component extends="wheels.tests.Test" { - - function test_boolean_handled_properly() { - sqltype = model("Sqltype").findOne(); - sqltype.booleanType = "false"; - assert('!sqltype.hasChanged("booleanType")'); - sqltype.booleanType = "no"; - assert('!sqltype.hasChanged("booleanType")'); - sqltype.booleanType = 0; - assert('!sqltype.hasChanged("booleanType")'); - sqltype.booleanType = "0"; - assert('!sqltype.hasChanged("booleanType")'); - sqltype.booleanType = "true"; - assert('sqltype.hasChanged("booleanType")'); - sqltype.booleanType = "yes"; - assert('sqltype.hasChanged("booleanType")'); - sqltype.booleanType = 1; - assert('sqltype.hasChanged("booleanType")'); - sqltype.booleanType = "1"; - assert('sqltype.hasChanged("booleanType")'); - } - - function test_should_be_able_to_update_integer_from_null_to_0() { - user = model("user").findByKey(1); - transaction { - user.birthDayYear = ""; - user.save(); - user.birthDayYear = 0; - user.save(); - user.reload(); - transaction action="rollback"; - } - assert("user.birthDayYear IS 0"); - } - -} diff --git a/core/src/wheels/tests/model/properties/hasproperty.cfc b/core/src/wheels/tests/model/properties/hasproperty.cfc deleted file mode 100644 index a8fd028992..0000000000 --- a/core/src/wheels/tests/model/properties/hasproperty.cfc +++ /dev/null @@ -1,28 +0,0 @@ -component extends="wheels.tests.Test" { - - function test_returns_true_when_property_is_set() { - _model = model("author"); - properties = {firstName = "James", lastName = "Gibson"}; - _model = _model.new(properties = properties); - assert('_model.hasProperty("firstName") eq true'); - } - - function test_returns_true_when_property_is_blank() { - _model = model("author").new(); - assert('_model.hasProperty("firstName") eq true'); - } - - function test_returns_false_when_property_does_not_exist() { - _model = model("author").new(); - StructDelete(_model, "lastName"); - assert('_model.hasProperty("lastName") eq false'); - } - - function test_dynamic_method_call() { - _model = model("author"); - properties = {firstName = "James", lastName = "Gibson"}; - _model = _model.new(properties = properties); - assert('_model.hasFirstName() eq true'); - } - -} diff --git a/core/src/wheels/tests/model/properties/ignoredcolumns.cfc b/core/src/wheels/tests/model/properties/ignoredcolumns.cfc deleted file mode 100644 index c76e02fb6a..0000000000 --- a/core/src/wheels/tests/model/properties/ignoredcolumns.cfc +++ /dev/null @@ -1,8 +0,0 @@ -component extends="wheels.tests.Test" { - - function test_ignoredColumns() { - shop = model("shop").findOne(); - assert('StructKeyExists(shop, "isblackmarket") eq false'); - } - -} diff --git a/core/src/wheels/tests/model/properties/key.cfc b/core/src/wheels/tests/model/properties/key.cfc deleted file mode 100644 index d249d3d94e..0000000000 --- a/core/src/wheels/tests/model/properties/key.cfc +++ /dev/null @@ -1,40 +0,0 @@ -component extends="wheels.tests.Test" { - - function test_pk_calculated_property_selecting_pk() { - shop = model("Shop").findOne(select = "shopid", where = "id = 'shop1'"); - actual = shop.key(); - assert("Len(actual) gt 0"); - } - - // TODO: fix this failing test. Issue #631 - function _test_pk_calculated_property_selecting_alias() { - shop = model("Shop").findOne(select = "id", where = "id = 'shop1'"); - actual = shop.key(); - debug("shop.properties()", false); - assert("Len(actual) gt 0", "actual"); - } - - function test_key() { - author = model("author").findOne(); - result = author.key(); - assert("result IS author.id"); - } - - function test_key_with_new() { - author = model("author").new(id = 1, firstName = "Per", lastName = "Djurner"); - result = author.key(); - assert("result IS 1"); - } - - function test_return_numeric_value_if_PK_is_numeric() { - author = model("author").findByKey(1); - authorArr = []; - - ArrayAppend(authorArr, {"id" = author.key(), "firstname" = author.firstname, "lastname" = author.lastname}); - - responseJSON = SerializeJSON(authorArr); - - assert("find('""id"":1',responseJSON)"); - } - -} diff --git a/core/src/wheels/tests/model/properties/properties.cfc b/core/src/wheels/tests/model/properties/properties.cfc deleted file mode 100644 index 55195f2e86..0000000000 --- a/core/src/wheels/tests/model/properties/properties.cfc +++ /dev/null @@ -1,134 +0,0 @@ -component extends="wheels.tests.Test" { - - function setup() { - _debug = false; - } - - function test_setting_and_getting_properties() { - user = model("user").new(); - - args = {}; - args.Address = "1313 mockingbird lane"; - args.City = "deerfield beach"; - args.Fax = "9545551212"; - args.FirstName = "anthony"; - args.LastName = "Petruzzi"; - args.Password = "it's a secret"; - args.Phone = "9544826106"; - args.State = "fl"; - args.UserName = "tonypetruzzi"; - args.ZipCode = "33441"; - args.Id = ""; - args.birthday = "11/01/1975"; - args.birthdaymonth = "11"; - args.birthdayyear = "1975"; - args.allowExplicitTimestamps = false; - - user.setProperties(args); - - props = user.properties(); - - for (i in props) { - actual = props[i]; - expected = args[i]; - assert("actual eq expected"); - }; - - args.FirstName = "per"; - args.LastName = "djurner"; - - user.setproperties(firstname = "per", lastname = "djurner"); - props = user.properties(); - - for (i in props) { - actual = props[i]; - expected = args[i]; - assert("actual eq expected"); - }; - - args.FirstName = "chris"; - args.LastName = "peters"; - args.ZipCode = "33333"; - - _params = {}; - _params.lastname = "peters"; - _params.zipcode = "33333"; - - user.setproperties(firstname = "chris", properties = _params); - props = user.properties(); - - for (i in props) { - actual = props[i]; - expected = args[i]; - assert("actual eq expected"); - }; - } - - function test_setting_and_getting_properties_with_named_arguments() { - author = model("author").findOne(); - author.setProperties(firstName = "a", lastName = "b"); - result = author.properties(); - assert('result.firstName eq "a"'); - assert('result.lastName eq "b"'); - } - - function test_properties_returns_expected_struct_keys() { - author = model("author").new(firstName = "Foo", lastName = "Bar"); - - _properties = author.properties(); - actual = ListSort(StructKeyList(_properties), "text"); - expected = "allowExplicitTimestamps,firstName,lastName"; - - assert('actual eq expected'); - assert("author.firstName eq 'Foo'"); - assert("author.lastName eq 'Bar'"); - } - - function test_nested_property_returns_an_object() { - author = model("author").new(firstName = "Foo", lastName = "Bar"); - author.post = model("post").new(title = "Brown Fox"); - _properties = author.properties(); - - actual = IsObject(_properties.post); - expected = true; - - debug("author.properties()", _debug); - - assert('actual eq expected'); - } - - function test_nested_properties_returns_array_of_objects() { - author = model("author").new(firstName = "Foo", lastName = "Bar"); - author.posts = [model("post").new(title = "Brown Fox"), model("post").new(title = "Lazy Dog")]; - - actual = author.properties().posts[2]; - - debug("actual", _debug); - - assert('IsObject(actual)'); - } - - function test_nested_property_is_not_returned_when_returnincluded_is_false() { - author = model("author").new(firstName = "Foo", lastName = "Bar"); - author.post = model("post").new(title = "Brown Fox"); - - _properties = author.properties(returnIncluded = false); - actual = ListSort(StructKeyList(_properties), "text"); - expected = "allowExplicitTimestamps,firstName,lastName"; - - debug("author.properties()", _debug); - - assert('actual eq expected'); - } - - function test_afterfind_callback_returns_included_model_as_object() { - mypost = model("postWithAfterFindCallback").findOne(include = "author"); - assert("IsObject(mypost.author)"); - } - - function test_calculated_property_override() { - user = model("User3").findOne(); - assert("IsObject(user) and user.firstName eq 'Calculated Property Column Override'"); - } - -} diff --git a/core/src/wheels/tests/model/properties/propertyispresent.cfc b/core/src/wheels/tests/model/properties/propertyispresent.cfc deleted file mode 100644 index b78bec01fa..0000000000 --- a/core/src/wheels/tests/model/properties/propertyispresent.cfc +++ /dev/null @@ -1,29 +0,0 @@ -component extends="wheels.tests.Test" { - - function test_returns_true_when_property_is_set() { - _model = model("author"); - properties = {firstName = "James", lastName = "Gibson"}; - _model = _model.new(properties = properties); - assert('_model.propertyIsPresent("firstName") eq true'); - } - - function test_returns_false_when_property_is_blank() { - _model = model("author").new(); - _model.lastName = ""; - assert('_model.propertyIsPresent("lastName") eq false'); - } - - function test_returns_false_when_property_does_not_exist() { - _model = model("author").new(); - StructDelete(_model, "lastName"); - assert('_model.propertyIsPresent("lastName") eq false'); - } - - function test_dynamic_method_call() { - _model = model("author"); - properties = {firstName = "James", lastName = "Gibson"}; - _model = _model.new(properties = properties); - assert('_model.firstNameIsPresent() eq true'); - } - -} diff --git a/core/src/wheels/tests/model/properties/protectedproperties.cfc b/core/src/wheels/tests/model/properties/protectedproperties.cfc deleted file mode 100644 index acc0e9f974..0000000000 --- a/core/src/wheels/tests/model/properties/protectedproperties.cfc +++ /dev/null @@ -1,21 +0,0 @@ -component extends="wheels.tests.Test" { - - function test_property_cannot_be_set_with_mass_assignment_when_protected() { - model = model("post"); - _model = Duplicate(_model); - _model.protectedProperties(properties = "views"); - properties = {views = "2000"}; - _model = _model.new(properties = properties); - assert('StructKeyExists(_model, "views") eq false'); - } - - function test_property_can_be_set_directly() { - _model = model("post"); - _model = Duplicate(_model); - _model.protectedProperties(properties = "views"); - _model = _model.new(); - _model.views = 2000; - assert('StructKeyExists(_model, "views") eq true'); - } - -} diff --git a/core/src/wheels/tests/model/properties/timestamps.cfc b/core/src/wheels/tests/model/properties/timestamps.cfc deleted file mode 100644 index b8ba2893a7..0000000000 --- a/core/src/wheels/tests/model/properties/timestamps.cfc +++ /dev/null @@ -1,118 +0,0 @@ -component extends="wheels.tests.Test" { - - function test_utc_timestamps() { - transaction { - utctime = DateConvert("local2Utc", Now()); - author = model("Author").findOne(); - post = author.createPost(title = "test post", body = "here is some text"); - assert('DateDiff("s", utctime, post.createdAt) lte 2'); // allow 1 second between test value and inserted value - assert('DateDiff("s", utctime, post.updatedAt) lte 2'); - transaction action="rollback"; - } - } - - function test_local_timestamps() { - transaction { - localtime = Now(); - model("Post").getClass().timeStampMode = "local"; - author = model("Author").findOne(); - post = author.createPost(title = "test post", body = "here is some text"); - assert('DateDiff("s", localtime, post.createdAt) lte 2'); // allow 1 second between test value and inserted value - assert('DateDiff("s", localtime, post.updatedAt) lte 2'); - transaction action="rollback"; - } - } - - function test_epoch_timestamps() { - transaction { - epochtime = Now().getTime(); - model("Post").getClass().timeStampOnCreateProperty = "createdAtEpoch"; - model("Post").getClass().timeStampOnUpdateProperty = "updatedAtEpoch"; - model("Post").getClass().timeStampMode = "epoch"; - author = model("Author").findOne(); - post = author.createPost(title = "test post", body = "here is some text", createdAt = Now(), updatedAt = Now()); - assert('post.createdAtEpoch - epochtime lte 2000'); // allow 1 second between test value and inserted value - assert('post.updatedAtEpoch - epochtime lte 2000'); // allow 1 second between test value and inserted value - transaction action="rollback"; - } - } - - function test_updatedAt_does_not_change_when_no_changes_to_model() { - transaction { - post = model("Post").findOne(); - orgUpdatedAt = post.properties().updatedAt; - post.update(); - post.reload(); - newUpdatedAt = post.properties().updatedAt; - assert('orgUpdatedAt eq newUpdatedAt'); - transaction action="rollback"; - } - } - - function test_createdAt_does_not_change_on_update() { - transaction { - post = model("Post").findOne(); - orgCreatedAt = post.properties().createdAt; - post.update(body = "here is some updated text"); - post.reload(); - newCreatedAt = post.properties().createdAt; - assert('orgCreatedAt eq newCreatedAt'); - transaction action="rollback"; - } - } - - function test_explicit_timestamps_are_respected_on_create() { - transaction { - author = model("Author").findOne(); - post = author.createPost( - title = "test_explicit_timestamps_are_respected test post", - body = "here is some text", - createdAt = CreateDate(1969, 4, 1), - updatedAt = CreateDate(1970, 4, 1), - allowExplicitTimestamps = true - ); - assert("Year(post.createdAt) eq 1969"); - assert("Year(post.updatedAt) eq 1970"); - transaction action="rollback"; - } - } - - function test_explicit_timestamps_are_respected_on_update() { - transaction { - author = model("Author").findOne(); - author.update( - city = "Dateville", - createdAt = CreateDate(1972, 4, 1), - updatedAt = CreateDate(1974, 4, 1), - allowExplicitTimestamps = true - ); - assert("Year(author.createdAt) eq 1972"); - assert("Year(author.updatedAt) eq 1974"); - transaction action="rollback"; - } - } - - function test_explicit_timestamps_require_allowexplicittimestamps_on_create() { - transaction { - utctime = DateConvert("local2Utc", Now()); - author = model("Author").findOne(); - post = author.createPost( - title = "test_default_timestamps test post", - body = "here is some text", - createdAt = CreateDate(1969, 4, 1), - updatedAt = CreateDate(1970, 4, 1) - ); - assert('DateDiff("s", utctime, post.createdAt) lte 2'); - assert('DateDiff("s", utctime, post.updatedAt) lte 2'); - transaction action="rollback"; - } - } - - function teardown() { - // reset all changes to post model class - model("Post").getClass().timeStampOnCreateProperty = "createdAt"; - model("Post").getClass().timeStampOnUpdateProperty = "updatedAt"; - model("Post").getClass().timeStampMode = "utc"; - } - -} \ No newline at end of file diff --git a/core/src/wheels/tests/model/properties/toggle.cfc b/core/src/wheels/tests/model/properties/toggle.cfc deleted file mode 100644 index 92c90e49f6..0000000000 --- a/core/src/wheels/tests/model/properties/toggle.cfc +++ /dev/null @@ -1,45 +0,0 @@ -component extends="wheels.tests.Test" { - - function test_toggle_property_with_save() { - _model = model("user").findOne(where = "firstName='Chris'"); - transaction action="begin" { - saved = _model.toggle("isActive"); - transaction action="rollback"; - } - assert('_model.isActive eq false and saved eq true'); - } - - function test_toggle_property_without_save() { - _model = model("user").findOne(where = "firstName='Chris'"); - _model.toggle("isActive", false); - assert('_model.isActive eq false'); - } - - function test_toggle_property_dynamic_method_without_save() { - _model = model("user").findOne(where = "firstName='Chris'"); - _model.toggleIsActive(save = false); - assert('_model.isActive eq false'); - } - - function test_toggle_property_dynamic_method_with_save() { - _model = model("user").findOne(where = "firstName='Chris'"); - transaction action="begin" { - saved = _model.toggleIsActive(); - transaction action="rollback"; - } - assert('_model.isActive eq false and saved eq true'); - } - - function test_toggle_property_without_save_errors_when_not_existing() { - _model = model("user").findOne(where = "firstName='Chris'"); - error = raised('_model.toggle("isMember", false)'); - assert('error eq "Wheels.PropertyDoesNotExist"'); - } - - function test_toggle_property_without_save_errors_when_not_boolean() { - _model = model("user").findOne(where = "firstName='Chris'"); - error = raised('_model.toggle("firstName", false)'); - assert('error eq "Wheels.PropertyIsIncorrectType"'); - } - -} diff --git a/core/src/wheels/tests/model/raisedErrors/raisedErrors.cfc b/core/src/wheels/tests/model/raisedErrors/raisedErrors.cfc deleted file mode 100644 index 4146ec7a07..0000000000 --- a/core/src/wheels/tests/model/raisedErrors/raisedErrors.cfc +++ /dev/null @@ -1,42 +0,0 @@ -component extends="wheels.tests.Test" { - - function test_table_not_found() { - e = raised("model('table_not_found')"); - debug("e", false); - r = "Wheels.TableNotFound"; - assert("e eq r"); - } - - function test_no_primary_key() { - e = raised("model('noPrimaryKey')"); - debug("e", false); - r = "Wheels.NoPrimaryKey"; - assert("e eq r"); - } - - function test_bykey_methods_should_raise_error_with_key_argument() { - post = model("post"); - e = raised('post.deleteByKey(key="1,2")'); - r = "Wheels.InvalidArgumentValue"; - assert("e eq r"); - e = raised('post.findByKey(key="1,2")'); - r = "Wheels.InvalidArgumentValue"; - assert("e eq r"); - e = raised('post.updateByKey(key="1,2", title="testing")'); - r = "Wheels.InvalidArgumentValue"; - assert("e eq r"); - } - - function test_value_cannot_be_determined_in_where_clause() { - e = raised('model("user").count(where="username = tony")'); - r = "Wheels.QueryParamValue"; - assert("e eq r"); - } - - function test_invalid_select_column() { - e = raised('model("user").findAll(select="id,email,firstname,lastname,createdat,foo")'); - r = "Wheels.ColumnNotFound"; - assert("e eq r"); - } - -} diff --git a/core/src/wheels/tests/model/read/datasource.cfc b/core/src/wheels/tests/model/read/datasource.cfc deleted file mode 100644 index b00e730b86..0000000000 --- a/core/src/wheels/tests/model/read/datasource.cfc +++ /dev/null @@ -1,111 +0,0 @@ -component extends="wheels.tests.Test" { - - function setup() { - // we can only test h2 as the alt dsn.. the tables are not created in populate.cfm otherwise - altDatasource = "wheelstestdb_h2"; - isTestable = true; - if (application.wheels.dataSourceName eq altDatasource) { - isTestable = false; - } else if (application.wheels.serverName contains "Coldfusion") { - // seems ACF can't handle H2 datasources - isTestable = false; - } - } - - function db_setup() { - // ensure the authors table exists in the alt datasource - $query( - sql = " - CREATE TABLE IF NOT EXISTS c_o_r_e_authors - ( - id int NOT NULL IDENTITY - ,firstname varchar(100) NOT NULL - ,lastname varchar(100) NOT NULL - ,PRIMARY KEY(id) - ); - ", - datasource = altDatasource - ); - firstName = "Troll"; - $query( - sql = "INSERT INTO c_o_r_e_authors (firstName, lastName) VALUES ('#firstName#', 'Dolls');", - datasource = altDatasource - ); - finderArgs = {where = "firstName = '#firstName#'", datasource = altDatasource}; - } - - function test_findall_respects_model_config_datasource() { - if (!isTestable) return; - transaction { - this.db_setup(); - // ensure this is using the wheelstestdb_h2 as defined in the model config - actual = model("AuthorAlternateDatasource").findAll(where = "firstName = '#firstName#'"); - TransactionRollback(); - } - assert("actual.recordCount"); - } - - function test_findall_with_datasource_argument() { - if (!isTestable) return; - transaction { - this.db_setup(); - defaultDBRows = model("Author").findAll(where = "firstName = '#firstName#'"); - actual = model("Author").findAll(argumentCollection = finderArgs); - TransactionRollback(); - } - assert("actual.recordCount"); - // sanity check that there are no rows in the default db - assert("defaultDBRows.recordCount eq 0"); - } - - function test_findOne_with_datasource_argument() { - if (!isTestable) return; - transaction { - this.db_setup(); - actual = model("Author").findOne(argumentCollection = finderArgs); - TransactionRollback(); - } - assert("IsObject(actual)"); - } - - function test_findFirst_with_datasource_argument() { - if (!isTestable) return; - transaction { - this.db_setup(); - actual = model("Author").findFirst(argumentCollection = finderArgs); - TransactionRollback(); - } - assert("IsObject(actual)"); - } - - function test_findLastOne_with_datasource_argument() { - if (!isTestable) return; - transaction { - this.db_setup(); - actual = model("Author").findLastOne(argumentCollection = finderArgs); - TransactionRollback(); - } - assert("IsObject(actual)"); - } - - function test_count_with_datasource_argument() { - if (!isTestable) return; - transaction { - this.db_setup(); - actual = model("Author").count(argumentCollection = finderArgs); - TransactionRollback(); - } - assert("actual gt 0"); - } - - function test_exists_with_datasource_argument() { - if (!isTestable) return; - transaction { - this.db_setup(); - actual = model("Author").exists(argumentCollection = finderArgs); - TransactionRollback(); - } - assert("actual eq true"); - } - -} diff --git a/core/src/wheels/tests/model/read/findallkeys.cfc b/core/src/wheels/tests/model/read/findallkeys.cfc deleted file mode 100644 index edef428de2..0000000000 --- a/core/src/wheels/tests/model/read/findallkeys.cfc +++ /dev/null @@ -1,13 +0,0 @@ -component extends="wheels.tests.Test" { - - function test_findAllKeys() { - p = model("post").findAll(select = "id"); - posts = model("post").findAllKeys(); - keys = ValueList(p.id); - assert('posts eq keys'); - p = model("post").findAll(select = "id"); - posts = model("post").findAllKeys(quoted = true); - keys = QuotedValueList(p.id); - } - -} diff --git a/core/src/wheels/tests/model/read/findfirst.cfc b/core/src/wheels/tests/model/read/findfirst.cfc deleted file mode 100644 index 9ce1944e33..0000000000 --- a/core/src/wheels/tests/model/read/findfirst.cfc +++ /dev/null @@ -1,14 +0,0 @@ -component extends="wheels.tests.Test" { - - function test_find_first() { - result = model("user").findFirst(); - assert('result.id IS 1'); - result = model("user").findFirst(property = "firstName"); - assert("result.firstName IS 'Chris'"); - result = model("user").findFirst(properties = "firstName"); - assert("result.firstName IS 'Chris'"); - result = model("user").findFirst(property = "firstName", where = "id != 2"); - assert("result.firstName IS 'Joe'"); - } - -} diff --git a/core/src/wheels/tests/model/read/findlast.cfc b/core/src/wheels/tests/model/read/findlast.cfc deleted file mode 100644 index eedc7d81f7..0000000000 --- a/core/src/wheels/tests/model/read/findlast.cfc +++ /dev/null @@ -1,10 +0,0 @@ -component extends="wheels.tests.Test" { - - function test_find_last_one() { - result = model("user").findLastOne(); - assert('result.id IS 5'); - result = model("user").findLastOne(properties = "id"); - assert('result.id IS 5'); - } - -} diff --git a/core/src/wheels/tests/model/read/findorcreateby.cfc b/core/src/wheels/tests/model/read/findorcreateby.cfc deleted file mode 100644 index 0ea49cd906..0000000000 --- a/core/src/wheels/tests/model/read/findorcreateby.cfc +++ /dev/null @@ -1,40 +0,0 @@ -component extends="wheels.tests.Test" { - - function test_findOneOrCreateBy() { - transaction { - author = model("author").findOrCreateByFirstName(firstName = "Per", lastName = "Djurner"); - assert('IsObject(author)'); - assert('author.lastname eq "Djurner"'); - assert('author.firstname eq "Per"'); - transaction action="rollback"; - } - } - - function test_findOneOrCreateByWithOnePropertyName() { - transaction { - author = model("author").findOrCreateByFirstName(firstName = "Per"); - assert('IsObject(author)'); - assert('author.firstname eq "Per"'); - transaction action="rollback"; - } - } - - function test_findOneOrCreateByWithAnyPropertyName() { - transaction { - author = model("author").findOrCreateByFirstName(whatever = "Per"); - assert('IsObject(author)'); - assert('author.firstname eq "Per"'); - transaction action="rollback"; - } - } - - function test_findOneOrCreateByWithUnnamedArgument() { - transaction { - author = model("author").findOrCreateByFirstName("Per"); - assert('IsObject(author)'); - assert('author.firstname eq "Per"'); - transaction action="rollback"; - } - } - -} diff --git a/core/src/wheels/tests/model/sql/whereclause.cfc b/core/src/wheels/tests/model/sql/whereclause.cfc deleted file mode 100644 index d1322a8aee..0000000000 --- a/core/src/wheels/tests/model/sql/whereclause.cfc +++ /dev/null @@ -1,87 +0,0 @@ -component extends="wheels.tests.Test" { - - function test_numeric_operators() { - operators = ListToArray("=,<>,!=,<,<=,!<,>,>=,!>"); - for (i in operators) { - check = "Len(result[2]) IS (19+#Len(i)#) AND ArrayLen(result) IS 3 AND result[3].type IS 'cf_sql_integer' AND Right(result[2], #Len(i)#) IS '#i#'"; - result = model("author").$whereClause(where = "id#i#0"); - assert(check); - result = model("author").$whereClause(where = "id#i# 11"); - assert(check); - result = model("author").$whereClause(where = "id #i#999"); - assert(check); - }; - } - - function test_in_like_operators() { - result = model("author").$whereClause(where = "id IN (1,2,3)"); - assert("Right(result[2], 2) IS 'IN'"); - result = model("author").$whereClause(where = "id NOT IN (1,2,3)"); - assert("Right(result[2], 6) IS 'NOT IN'"); - result = model("author").$whereClause(where = "lastName LIKE 'Djurner'"); - assert("Right(result[2], 4) IS 'LIKE'"); - result = model("author").$whereClause(where = "lastName NOT LIKE 'Djurner'"); - assert("Right(result[2], 8) IS 'NOT LIKE'"); - } - - function test_floats() { - result = model("post").$whereClause(where = "averagerating IN(3.6,3.2)"); - assert('arraylen(result) gte 4'); - assert('isstruct(result[4])'); - assert( - 'result[4].datatype eq "float" OR result[4].datatype eq "float8" OR result[4].datatype eq "double" OR result[4].datatype eq "number"' - ); - result = model("post").$whereClause(where = "averagerating NOT IN(3.6,3.2)"); - assert('arraylen(result) gte 4'); - assert('isstruct(result[4])'); - assert( - 'result[4].datatype eq "float" OR result[4].datatype eq "float8" OR result[4].datatype eq "double" OR result[4].datatype eq "number"' - ); - result = model("post").$whereClause(where = "averagerating = 3.6"); - assert('arraylen(result) gte 4'); - assert('isstruct(result[4])'); - assert( - 'result[4].datatype eq "float" OR result[4].datatype eq "float8" OR result[4].datatype eq "double" OR result[4].datatype eq "number"' - ); - } - - function test_is_null() { - result = model("post").$whereClause(where = "averagerating IS NULL"); - assert('arraylen(result) gte 4'); - assert('isstruct(result[4])'); - result = model("post").$whereClause(where = "averagerating IS NOT NULL"); - assert('arraylen(result) gte 4'); - assert('isstruct(result[4])'); - assert( - 'result[4].datatype eq "float" OR result[4].datatype eq "float8" OR result[4].datatype eq "double" OR result[4].datatype eq "number"' - ); - } - - function test_respects_calculated_property_datatype() { - actual = model("post").$whereClause(where = "createdAtAlias > '#CreateDate(2000, 1, 1)#'"); - assert("actual[4].datatype eq 'datetime'"); - } - - function test_SQLInjectionProtectionWithParameterize() { - badparams = {username = "tonyp", password = "tonyp123' OR password!='tonyp123"}; - - actual = raised( - "model(""user"").findall(where=""username = '#badparams.username#' AND password = '#badparams.password#'"", parameterize=2)" - ); - // There error message would be "Wheels found 3 parameters in the query string but was instructed to parameterize 2." - expected = "Wheels.ParameterMismatch"; - assert("actual eq expected"); - } - - function test_SQLInjectionProtectionWithParameterizeAndPagination() { - badparams = {username = "tonyp", password = "tonyp123' OR password!='tonyp123"}; - - actual = raised( - "model(""user"").findall(where=""username = '#badparams.username#' AND password = '#badparams.password#'"", parameterize=2, perPage=2, page=1)" - ); - // There error message would be "Wheels found 3 parameters in the query string but was instructed to parameterize 2." - expected = "Wheels.ParameterMismatch"; - assert("actual eq expected"); - } - -} diff --git a/core/src/wheels/tests/model/transactions/invokewithtransaction.cfc b/core/src/wheels/tests/model/transactions/invokewithtransaction.cfc deleted file mode 100644 index e74141ae0d..0000000000 --- a/core/src/wheels/tests/model/transactions/invokewithtransaction.cfc +++ /dev/null @@ -1,216 +0,0 @@ -component extends="wheels.tests.Test" { - - function setup() { - application.wheels.transactionMode = "commit"; - } - - function teardown() { - application.wheels.transactionMode = "none"; - } - - function test_create_rollbacks_when_callback_returns_false() { - tag = model("tagFalseCallbacks").create(name = "Kermit", description = "The Frog"); - tag = model("tagFalseCallbacks").findOne(where = "name='Kermit'"); - assert("tag IS false"); - } - - function test_update_rollbacks_when_callback_returns_false() { - tag = model("tagFalseCallbacks").findOne(where = "description='testdesc'"); - tag.update(name = "Kermit"); - tag = model("tagFalseCallbacks").findOne(where = "description='testdesc'"); - assert("tag.name IS 'releases'"); - } - - function test_save_rollbacks_when_callback_returns_false() { - tag = model("tagFalseCallbacks").findOne(where = "description='testdesc'"); - tag.name = "Kermit"; - tag.save(); - tag = model("tagFalseCallbacks").findOne(where = "description='testdesc'"); - assert("tag.name IS 'releases'"); - } - - function test_delete_rollbacks_when_callback_returns_false() { - tag = model("tagFalseCallbacks").findOne(where = "description='testdesc'"); - tag.delete(); - tag = model("tagFalseCallbacks").findOne(where = "description='testdesc'"); - assert("IsObject(tag)"); - } - - function test_deleteAll_with_instantiate_rollbacks_when_callback_returns_false() { - model("tagFalseCallbacks").deleteAll(instantiate = true); - results = model("tagFalseCallbacks").findAll(); - assert("results.recordcount IS 8"); - } - - function test_updateAll_with_instantiate_rollbacks_when_callback_returns_false() { - model("tagFalseCallbacks").updateAll(name = "Kermit", instantiate = true); - results = model("tagFalseCallbacks").findAll(where = "name = 'Kermit'"); - assert("results.recordcount IS 0"); - } - - function test_create_with_rollback() { - tag = model("tag").create(name = "Kermit", description = "The Frog", transaction = "rollback"); - tag = model("tag").findOne(where = "name='Kermit'"); - assert("not IsObject(tag)"); - } - - function test_update_with_rollback() { - tag = model("tag").findOne(where = "description='testdesc'"); - tag.update(name = "Kermit", transaction = "rollback"); - tag = model("tag").findOne(where = "description='testdesc'"); - assert("tag.name IS 'releases'"); - } - - function test_save_with_rollback() { - tag = model("tag").findOne(where = "description='testdesc'"); - tag.name = "Kermit"; - tag.save(transaction = "rollback"); - tag = model("tag").findOne(where = "description='testdesc'"); - assert("tag.name IS 'releases'"); - } - - function test_delete_with_rollback() { - tag = model("tag").findOne(where = "description='testdesc'"); - tag.delete(transaction = "rollback"); - tag = model("tag").findOne(where = "description='testdesc'"); - assert("IsObject(tag)"); - } - - function test_deleteAll_with_rollback() { - model("tag").deleteAll(instantiate = true, transaction = "rollback"); - results = model("tag").findAll(); - assert("results.recordcount IS 8"); - } - - function test_updateAll_with_rollback() { - model("tag").updateAll(name = "Kermit", instantiate = true, transaction = "rollback"); - results = model("tag").findAll(where = "name = 'Kermit'"); - assert("results.recordcount IS 0"); - } - - function test_create_with_transactions_disabled() { - transaction { - tag = model("tag").create(name = "Kermit", description = "The Frog", transaction = "none"); - tag = model("tag").findOne(where = "name='Kermit'"); - assert("IsObject(tag)"); - transaction action="rollback"; - } - } - - function test_create_with_transactions_false() { - transaction { - tag = model("tag").create(name = "Kermit", description = "The Frog", transaction = false); - tag = model("tag").findOne(where = "name='Kermit'"); - assert("IsObject(tag)"); - transaction action="rollback"; - } - } - - function test_update_with_transactions_disabled() { - transaction { - tag = model("tag").findOne(where = "description='testdesc'"); - tag.update(name = "Kermit", transaction = "none"); - tag.reload(); - assert("tag.name IS 'Kermit'"); - transaction action="rollback"; - } - } - - function test_save_with_transactions_disabled() { - transaction { - tag = model("tag").findOne(where = "description='testdesc'"); - tag.name = "Kermit"; - tag.save(transaction = "none"); - tag.reload(); - assert("tag.name IS 'Kermit'"); - transaction action="rollback"; - } - } - - function test_delete_with_transactions_disabled() { - transaction { - tag = model("tag").findOne(where = "description='testdesc'"); - tag.delete(transaction = "none"); - tag = model("tag").findOne(where = "description='testdesc'"); - assert("not IsObject(tag)"); - transaction action="rollback"; - } - } - - function test_deleteAll_with_transactions_disabled() { - transaction { - model("tag").deleteAll(instantiate = true, transaction = "none"); - results = model("tag").findAll(); - assert("results.recordcount IS 0"); - transaction action="rollback"; - } - } - - function test_updateAll_with_transactions_disabled() { - transaction { - model("tag").updateAll(name = "Kermit", instantiate = true, transaction = "none"); - results = model("tag").findAll(where = "name = 'Kermit'"); - assert("results.recordcount IS 8"); - transaction action="rollback"; - } - } - - function test_nested_transaction_within_callback_respect_initial_transaction_mode() { - postsBefore = model('post').count(reload = true); - tag = model("tagWithDataCallbacks").create(name = "Kermit", description = "The Frog", transaction = "rollback"); - postsAfter = model('post').count(reload = true); - assert("IsObject(tag)"); - assert("postsBefore eq postsAfter"); - } - - function test_nested_transaction_within_callback_with_transactions_disabled() { - transaction { - tag = model("tagWithDataCallbacks").create(name = "Kermit", description = "The Frog", transaction = "none"); - results = model("tag").findAll(where = "name = 'Kermit'"); - transaction action="rollback"; - } - assert("IsObject(tag)"); - assert("results.recordcount IS 1"); - } - - function test_transaction_closed_after_rollback() { - hash = model("tag").$hashedConnectionArgs(); - tag = model("tagWithDataCallbacks").create(name = "Kermit", description = "The Frog", transaction = "rollback"); - assert('request.wheels.transactions[hash] is false'); - } - - function test_transaction_closed_after_none() { - hash = model("tag").$hashedConnectionArgs(); - transaction { - tag = model("tagWithDataCallbacks").create(name = "Kermit", description = "The Frog", transaction = "none"); - transaction action="rollback"; - } - assert('request.wheels.transactions[hash] is false'); - } - - function test_transaction_closed_when_error_raised() { - hash = model("tag").$hashedConnectionArgs(); - try { - tag = model("tag").create( - id = "", - name = "Kermit", - description = "The Frog", - transaction = "rollback" - ); - } catch (any e) { - } - assert('request.wheels.transactions[hash] is false'); - } - - function test_rollback_when_error_raised() { - tag = Duplicate(model("tagWithDataCallbacks").new(name = "Kermit", description = "The Frog")); - tag.afterSave(methods = "crashMe"); - try { - tag.save(); - } catch (any e) { - results = model("tag").findAll(where = "name = 'Kermit'"); - } - assert("results.recordcount IS 0"); - } - -} diff --git a/core/src/wheels/tests/model/validations/automatic_validations.cfc b/core/src/wheels/tests/model/validations/automatic_validations.cfc deleted file mode 100644 index adaa2a2d93..0000000000 --- a/core/src/wheels/tests/model/validations/automatic_validations.cfc +++ /dev/null @@ -1,60 +0,0 @@ -component extends="wheels.tests.Test" { - - function test_automatic_validations_should_validate_primary_keys() { - user = model("UserAutoMaticValidations").new( - username = 'tonyp1', - password = 'tonyp123', - firstname = 'Tony', - lastname = 'Petruzzi', - address = '123 Petruzzi St.', - city = 'SomeWhere1', - state = 'TX', - zipcode = '11111', - phone = '1235551212', - fax = '4565551212', - birthday = '11/01/1975', - birthdaymonth = 11, - birthdayyear = 1975, - isactive = 1 - ); - - /* should be valid since id is not passed in */ - assert('user.valid()'); - - /* should _not_ be valid since id is not a number */ - user.id = 'ABC'; - assert('!user.valid()'); - - /* should be valid since id is blank */ - user.id = ''; - assert('user.valid()'); - - /* should be valid since id is a number */ - user.id = 1; - assert('user.valid()'); - } - - function test_automatic_validations_can_be_turned_off_for_property() { - user = model("UserAutoMaticValidationsOff").new( - username = 'tonyp1', - password = 'tonyp123', - firstname = 'Tony', - lastname = 'Petruzzi', - address = '123 Petruzzi St.', - city = 'SomeWhere1', - state = 'TX', - zipcode = '11111', - phone = '1235551212', - fax = '4565551212', - birthday = '11/01/1975', - birthdaymonth = 11, - birthdayyear = 1975, - isactive = 1 - ); - - /* should be valid even though id is not a number because we have turned off automatic validations for the id property */ - user.id = 'ABC'; - assert('user.valid()'); - } - -} diff --git a/core/src/wheels/tests/model/validations/conditional_validations.cfc b/core/src/wheels/tests/model/validations/conditional_validations.cfc deleted file mode 100644 index b3172870f3..0000000000 --- a/core/src/wheels/tests/model/validations/conditional_validations.cfc +++ /dev/null @@ -1,131 +0,0 @@ -component extends="wheels.tests.Test" { - - function setup() { - user = application.wirebox.getInstance( "wheels.tests._assets.models.Model" ).$initModelClass( - name = "c_o_r_e_Users", - path = get("modelPath") - ); - user.username = "TheLongestNameInTheWorld"; - args = {}; - args.property = "username"; - args.maximum = "5"; - } - - function test_unless_validation_using_expression_valid() { - args.unless = "1 eq 1"; - user.validatesLengthOf(argumentCollection = args); - assert_test(user, true); - } - - function test_unless_validation_using_expression_invalid() { - args.unless = "1 eq 0"; - user.validatesLengthOf(argumentCollection = args); - assert_test(user, false); - } - - function test_unless_validation_using_method_valid() { - args.unless = "isnew()"; - user.validatesLengthOf(argumentCollection = args); - assert_test(user, true); - } - - function test_unless_validation_using_method_invalid() { - args.unless = "!isnew()"; - user.validatesLengthOf(argumentCollection = args); - assert_test(user, false); - } - - function test_unless_validation_using_method_mixin_and_parameters_valid() { - user.stupid_mixin = stupid_mixin; - args.unless = "this.stupid_mixin(b='1' , a='2') eq 3"; - user.validatesLengthOf(argumentCollection = args); - assert_test(user, true); - } - - function test_unless_validation_using_method_mixin_and_parameters_invalid() { - user.stupid_mixin = stupid_mixin; - args.unless = "this.stupid_mixin(b='1' , a='2') neq 3"; - user.validatesLengthOf(argumentCollection = args); - assert_test(user, false); - } - - function test_if_validation_using_expression_invalid() { - args.condition = "1 eq 1"; - user.validatesLengthOf(argumentCollection = args); - assert_test(user, false); - } - - function test_if_validation_using_expression_valid() { - args.condition = "1 eq 0"; - user.validatesLengthOf(argumentCollection = args); - assert_test(user, true); - } - - function test_if_validation_using_method_invalid() { - args.condition = "isnew()"; - user.validatesLengthOf(argumentCollection = args); - assert_test(user, false); - } - - function test_if_validation_using_method_valid() { - args.condition = "!isnew()"; - user.validatesLengthOf(argumentCollection = args); - assert_test(user, true); - } - - function test_if_validation_using_method_mixin_and_parameters_invalid() { - user.stupid_mixin = stupid_mixin; - args.condition = "this.stupid_mixin(b='1' , a='2') eq 3"; - user.validatesLengthOf(argumentCollection = args); - assert_test(user, false); - } - - function test_if_validation_using_method_mixin_and_parameters_valid() { - user.stupid_mixin = stupid_mixin; - args.condition = "this.stupid_mixin(b='1' , a='2') neq 3"; - user.validatesLengthOf(argumentCollection = args); - assert_test(user, true); - } - - function test_both_validations_if_trigged_unless_not_trigged_valid() { - args.condition = "1 eq 1"; - args.unless = "this.username eq 'TheLongestNameInTheWorld'"; - user.validatesLengthOf(argumentCollection = args); - assert_test(user, true); - } - - function test_both_validations_if_trigged_unless_trigged_invalid() { - args.condition = "1 eq 1"; - args.unless = "this.username eq ''"; - user.validatesLengthOf(argumentCollection = args); - assert_test(user, false); - } - - function test_both_validations_if_not_trigged_unless_not_trigged_valid() { - args.condition = "1 eq 0"; - args.unless = "this.username eq 'TheLongestNameInTheWorld'"; - user.validatesLengthOf(argumentCollection = args); - assert_test(user, true); - } - - function test_both_validations_if_not_trigged_unless_trigged_valid() { - args.condition = "1 eq 0"; - args.unless = "this.username eq ''"; - user.validatesLengthOf(argumentCollection = args); - assert_test(user, true); - } - - /** - * HELPERS - */ - - function assert_test(required any obj, required boolean expect) { - e = arguments.obj.valid(); - assert('e eq #arguments.expect#'); - } - - function stupid_mixin(required numeric a, required numeric b) { - return a + b; - } - -} diff --git a/core/src/wheels/tests/model/validations/default_validations.cfc b/core/src/wheels/tests/model/validations/default_validations.cfc deleted file mode 100644 index 21d051a911..0000000000 --- a/core/src/wheels/tests/model/validations/default_validations.cfc +++ /dev/null @@ -1,89 +0,0 @@ -component extends="wheels.tests.Test" { - - function setup() { - user = model("UserBlank").new(); - user.username = "gavin@cfwheels.org"; - user.password = "disismypassword"; - user.firstName = "Gavin"; - user.lastName = "Gavinsson"; - } - - function test_validates_presence_of_invalid() { - /* missing key */ - StructDelete(user, "username"); - /* zero length string */ - user.password = ""; - user.valid(); - assert('ArrayLen(user.allErrors()) eq 2'); - } - - function test_validates_presence_of_valid() { - user.password = "something"; - user.firstName = "blahblah"; - assert('user.valid()'); - } - - function test_validates_presence_of_valid_with_default_on_update() { - user = model("UserBlank").findOne(); // use existing user to test update - user.birthtime = ""; - user.save(transaction = "rollback"); - arrResult = user.errorsOn("birthtime"); - assert("ArrayLen(arrResult) eq 1 AND arrResult[1].message eq 'Birthtime can''t be empty'"); - } - - function test_validates_length_of_invalid() { - user.state = "Too many characters!"; - user.valid(); - arrResult = user.errorsOn("state"); - assert('ArrayLen(arrResult) eq 1 AND arrResult[1].message eq "State is the wrong length"'); - } - - function test_validates_length_of_valid() { - user.state = "FL"; - assert('user.valid()'); - } - - function test_validates_numericality_of_invalid() { - user.birthDayMonth = "This is not a number!"; - user.valid(); - arrResult = user.errorsOn("birthDayMonth"); - assert('ArrayLen(arrResult) eq 1 AND arrResult[1].message eq "Birthdaymonth is not a number"'); - } - - function test_validates_numericality_of_valid() { - user.birthDayMonth = "7"; - assert('user.valid()'); - } - - function test_validates_numericality_of_integer_invalid() { - user.birthDayMonth = "7.825"; - user.valid(); - arrResult = user.errorsOn("birthDayMonth"); - assert('ArrayLen(arrResult) eq 1 AND arrResult[1].message eq "Birthdaymonth is not a number"'); - } - - function test_validates_format_of_date_invalid() { - user.birthDay = "This is not a date!"; - user.valid(); - arrResult = user.errorsOn("birthDay"); - assert('ArrayLen(arrResult) eq 1 AND arrResult[1].message eq "Birth day is invalid"'); - } - - function test_validates_format_of_date_valid() { - user.birthDay = "01/01/2000"; - assert('user.valid()'); - } - - function test_validates_format_of_time_invalid() { - user.birthTime = "This is not a time!"; - user.valid(); - arrResult = user.errorsOn("birthTime"); - assert('ArrayLen(arrResult) eq 1 AND arrResult[1].message eq "Birthtime is invalid"'); - } - - function test_validates_format_of_time_valid() { - user.birthTime = "6:15 PM"; - assert('user.valid()'); - } - -} \ No newline at end of file diff --git a/core/src/wheels/tests/model/validations/low_level_validations.cfc b/core/src/wheels/tests/model/validations/low_level_validations.cfc deleted file mode 100644 index b087df9bdc..0000000000 --- a/core/src/wheels/tests/model/validations/low_level_validations.cfc +++ /dev/null @@ -1,21 +0,0 @@ -component extends="wheels.tests.Test" { - - function test_validate_and_validateOnCreate_should_be_called_when_creating() { - user = model("user").new(); - user.valid(); - assert('StructKeyExists(user, "_validateCalled")'); - assert('user._validateCalled eq true'); - assert('StructKeyExists(user, "_validateOnCreateCalled")'); - assert('user._validateOnCreateCalled eq true'); - } - - function test_validate_and_validateOnUpdate_should_be_called_when_updating() { - user = model("user").findOne(where = "username = 'perd'"); - user.valid(); - assert('StructKeyExists(user, "_validateCalled")'); - assert('user._validateCalled eq true'); - assert('StructKeyExists(user, "_validateOnUpdateCalled")'); - assert('user._validateOnUpdateCalled eq true'); - } - -} diff --git a/core/src/wheels/tests/model/validations/skip_validations.cfc b/core/src/wheels/tests/model/validations/skip_validations.cfc deleted file mode 100644 index 57d7972305..0000000000 --- a/core/src/wheels/tests/model/validations/skip_validations.cfc +++ /dev/null @@ -1,90 +0,0 @@ -component extends="wheels.tests.Test" { - - function setup() { - user = model("user"); - args = { - username = "myusername", - password = "mypassword", - firstname = "myfirstname", - lastname = "mylastname" - }; - } - - function test_can_create_new_record_validation_execute() { - transaction { - u = user.new(args); - e = u.isnew(); - r = u.save(); - transaction action="rollback"; - } - assert('e eq true'); - assert('r eq true'); - } - - function test_cannot_create_new_record_validation_execute() { - transaction { - args.username = "1"; - u = user.new(args); - e = u.isnew(); - r = u.save(); - transaction action="rollback"; - } - assert('e eq true'); - assert('r eq false'); - } - - function test_can_create_new_record_validation_skipped() { - transaction { - args.username = "1"; - u = user.new(args); - e = u.isnew(); - r = u.save(validate = "false"); - transaction action="rollback"; - } - assert('e eq true'); - assert('r eq true'); - } - - function test_can_update_existing_record_validation_execute() { - transaction { - u = user.findOne(where = "lastname = 'Petruzzi'"); - p = u.properties(); - r = u.update(args); - e = u.isnew(); - u.update(p); - transaction action="rollback"; - } - assert('e eq false'); - assert('r eq true'); - } - - function test_cannot_update_existing_record_validation_execute() { - transaction { - args.password = "1"; - u = user.findOne(where = "lastname = 'Petruzzi'"); - p = u.properties(); - r = u.update(args); - e = u.isnew(); - u.update(p); - transaction action="rollback"; - } - assert('e eq false'); - assert('r eq false'); - } - - function test_cant_update_existing_record_validation_skipped() { - transaction { - args.password = "1"; - u = user.findOne(where = "lastname = 'Petruzzi'"); - p = u.properties(); - u.setProperties(args); - e = u.isnew(); - r = u.save(validate = "false"); - u.update(p); - transaction action="rollback"; - } - assert('e eq false'); - assert('r eq true'); - } - -} diff --git a/core/src/wheels/tests/model/validations/standard_validations.cfc b/core/src/wheels/tests/model/validations/standard_validations.cfc deleted file mode 100644 index 32c38277af..0000000000 --- a/core/src/wheels/tests/model/validations/standard_validations.cfc +++ /dev/null @@ -1,496 +0,0 @@ -component extends="wheels.tests.Test" { - - function setup() { - StructDelete(application.wheels.models, "c_o_r_e_users", false); - user = model("c_o_r_e_users").new(); - } - - function teardown() { - StructDelete(variables, "user"); - } - - /* validatesConfirmationOf */ - // ConfirmProperty and Property exist. CaseSensitive is OFF. Both match including case - VALID - function test_validatesConfirmationOf_valid() { - user.password = "hamsterjelly"; - user.passwordConfirmation = "hamsterjelly"; - user.validatesConfirmationOf(property = "password"); - assert('user.valid()'); - assert('arrayLen(user.allErrors()) EQ 0'); - } - // ConfirmProperty and Property exist. CaseSensitive is OFF. Both match except case - VALID - function test_validatesConfirmationOf_valid_allowcase() { - user.password = "hamsterjelly"; - user.passwordConfirmation = "hamsterJelly"; - user.validatesConfirmationOf(property = "password"); - assert('user.valid()'); - assert('arrayLen(user.allErrors()) EQ 0'); - } - // ConfirmProperty and Property exist. CaseSensitive is OFF. They don't match - INVALID - function test_validatesConfirmationOf_invalid() { - user.password = "hamsterjelly"; - user.passwordConfirmation = "hamsterjellysucks"; - user.validatesConfirmationOf(property = "password"); - assert('!user.valid()'); - assert('arrayLen(user.allErrors()) EQ 1'); - } - // ConfirmProperty doesn't exist. No other checks are made. - INVALID (check errors array length is 1) - function test_validatesConfirmationOf_missing_property_confirmation_invalid() { - user.password = "hamsterjelly"; - user.validatesConfirmationOf(property = "password"); - assert('!user.valid()'); - assert('arrayLen(user.allErrors()) EQ 1'); - } - // ConfirmProperty and Property exist. CaseSensitive is ON. Both match including case - VALID - function test_validatesConfirmationOf_valid_case() { - user.password = "HamsterJelly"; - user.passwordConfirmation = "HamsterJelly"; - user.validatesConfirmationOf(property = "password", caseSensitive = true); - assert('user.valid()'); - assert('arrayLen(user.allErrors()) EQ 0'); - } - // ConfirmProperty and Property exist. CaseSensitive is ON. Both match except case - INVALID - function test_validatesConfirmationOf_invalid_case() { - user.password = "HamsterJelly"; - user.passwordConfirmation = "hamsterjelly"; - user.validatesConfirmationOf(property = "password", caseSensitive = true); - assert('!user.valid()'); - assert('arrayLen(user.allErrors()) EQ 1'); - } - // ConfirmProperty and Property exist. CaseSensitive is ON. They don't match - INVALID - function test_validatesConfirmationOf_invalid_no_matchcase() { - user.password = "HamsterJelly"; - user.passwordConfirmation = "duckjelly"; - user.validatesConfirmationOf(property = "password", caseSensitive = true); - assert('!user.valid()'); - assert('arrayLen(user.allErrors()) EQ 1'); - } - // check content of message when ConfirmProperty doesn't exist - // nb, this validation method only has a single error message returned and - // can be overridden by the developer - function test_validatesConfirmationOf_missing_property_confirmation_msg() { - user.password = "hamsterjelly"; - user.validatesConfirmationOf(property = "password"); - assert('!user.valid()'); - assert('user.allErrors()[1]["property"] EQ "passwordConfirmation"'); - assert('user.allErrors()[1]["message"] EQ "Password should match confirmation"'); - } - - /* validatesExclusionOf */ - function test_validatesExclusionOf_valid() { - user.firstname = "tony"; - user.validatesExclusionOf(property = "firstname", list = "per, raul, chris"); - assert('user.valid()'); - } - - function test_validatesExclusionOf_invalid() { - user.firstname = "tony"; - user.validatesExclusionOf(property = "firstname", list = "per, raul, chris, tony"); - assert('!user.valid()'); - } - - function test_validatesExclusionOf_missing_property_invalid() { - user.validatesExclusionOf(property = "firstname", list = "per, raul, chris, tony"); - assert('!user.valid()'); - } - - function test_validatesExclusionOf_missing_property_valid() { - user.validatesExclusionOf(property = "firstname", list = "per, raul, chris, tony", allowblank = "true"); - assert('user.valid()'); - } - - function test_validatesExclusionOf_allowblank_valid() { - user.firstname = ""; - user.validatesExclusionOf(property = "firstname", list = "per, raul, chris", allowblank = "true"); - assert('user.valid()'); - } - - function test_validatesExclusionOf_allowblank_invalid() { - user.firstname = ""; - user.validatesExclusionOf(property = "firstname", list = "per, raul, chris", allowblank = "false"); - assert('!user.valid()'); - } - - - /* validatesFormatOf */ - function test_validatesFormatOf_valid() { - user.phone = "954-555-1212"; - user.validatesFormatOf(property = "phone", regex = "(\d{3,3}-){2,2}\d{4,4}"); - assert('user.valid()'); - } - - function test_validatesFormatOf_invalid() { - user.phone = "(954) 555-1212"; - user.validatesFormatOf(property = "phone", regex = "(\d{3,3}-){2,2}\d{4,4}"); - assert('!user.valid()'); - } - - function test_validatesFormatOf_missing_property_invalid() { - user.validatesFormatOf(property = "phone", regex = "(\d{3,3}-){2,2}\d{4,4}"); - assert('!user.valid()'); - } - - function test_validatesFormatOf_missing_property_valid() { - user.validatesFormatOf(property = "phone", regex = "(\d{3,3}-){2,2}\d{4,4}", allowBlank = "true"); - assert('user.valid()'); - } - - function test_validatesFormatOf_allowblank_valid() { - user.phone = ""; - user.validatesFormatOf(property = "phone", regex = "(\d{3,3}-){2,2}\d{4,4}", allowBlank = "true"); - assert('user.valid()'); - } - - function test_validatesFormatOf_allowblank_invalid() { - user.phone = ""; - user.validatesFormatOf(property = "phone", regex = "(\d{3,3}-){2,2}\d{4,4}", allowBlank = "false"); - assert('!user.valid()'); - } - - - /* validatesInclusionOf */ - function test_validatesInclusionOf_invalid() { - user.firstname = "tony"; - user.validatesExclusionOf(property = "firstname", list = "per, raul, chris"); - assert('user.valid()'); - } - - function test_validatesInclusionOf_valid() { - user.firstname = "tony"; - user.validatesExclusionOf(property = "firstname", list = "per, raul, chris, tony"); - assert('!user.valid()'); - } - - function test_validatesInclusionOf_missing_property_invalid() { - user.validatesExclusionOf(property = "firstname", list = "per, raul, chris, tony"); - assert('!user.valid()'); - } - - function test_validatesInclusionOf_missing_property_valid() { - user.validatesExclusionOf(property = "firstname", list = "per, raul, chris, tony", allowblank = "true"); - assert('user.valid()'); - } - - function test_validatesInclusionOf_allowblank_valid() { - user.firstname = ""; - user.validatesExclusionOf(property = "firstname", list = "per, raul, chris", allowblank = "true"); - assert('user.valid()'); - } - - function test_validatesInclusionOf_allowblank_invalid() { - user.firstname = ""; - user.validatesExclusionOf(property = "firstname", list = "per, raul, chris", allowblank = "false"); - assert('!user.valid()'); - } - - - /* validatesLengthOf */ - function test_validatesLengthOf_maximum_minimum_invalid() { - user.firstname = "thi"; - user.validatesLengthOf(property = "firstname", minimum = "5", maximum = "20"); - assert('!user.valid()'); - } - - function test_validatesLengthOf_maximum_valid() { - user.firstname = "thisisatestagain"; - user.validatesLengthOf(property = "firstname", maximum = "20"); - assert('user.valid()'); - } - - function test_validatesLengthOf_maximum_invalid() { - user.firstname = "thisisatestagain"; - user.validatesLengthOf(property = "firstname", maximum = "15"); - assert('!user.valid()'); - } - - function test_validatesLengthOf_missing_property_invalid() { - user.validatesLengthOf(property = "firstname", maximum = "15"); - assert('!user.valid()'); - } - - function test_validatesLengthOf_missing_property_valid() { - user.validatesLengthOf(property = "firstname", maximum = "15", allowblank = "true"); - assert('user.valid()'); - } - - function test_validatesLengthOf_minimum_valid() { - user.firstname = "thisisatestagain"; - user.validatesLengthOf(property = "firstname", minimum = "15"); - assert('user.valid()'); - } - - function test_validatesLengthOf_minimum_invalid() { - user.firstname = "thisisatestagain"; - user.validatesLengthOf(property = "firstname", minimum = "20"); - assert('!user.valid()'); - } - - function test_validatesLengthOf_within_valid() { - user.firstname = "thisisatestagain"; - user.validatesLengthOf(property = "firstname", within = "15,20"); - assert('user.valid()'); - } - - function test_validatesLengthOf_within_invalid() { - user.firstname = "thisisatestagain"; - user.validatesLengthOf(property = "firstname", within = "10,15"); - assert('!user.valid()'); - } - - function test_validatesLengthOf_exactly_valid() { - user.firstname = "thisisatestagain"; - user.validatesLengthOf(property = "firstname", exactly = "16"); - assert('user.valid()'); - } - - function test_validatesLengthOf_exactly_invalid() { - user.firstname = "thisisatestagain"; - user.validatesLengthOf(property = "firstname", exactly = "20"); - assert('!user.valid()'); - } - - function test_validatesLengthOf_allowblank_valid() { - user.firstname = ""; - user.validatesLengthOf(property = "firstname", allowblank = "true"); - assert('user.valid()'); - } - - function test_validatesLengthOf_allowblank_invalid() { - user.firstname = ""; - user.validatesLengthOf(property = "firstname", allowblank = "false"); - assert('!user.valid()'); - } - - - /* validatesNumericalityOf */ - function test_validatesNumericalityOf_valid() { - user.birthdaymonth = "10"; - user.validatesNumericalityOf(property = "birthdaymonth"); - assert('user.valid()'); - } - - function test_validatesNumericalityOf_invalid() { - user.birthdaymonth = "1,000.00"; - user.validatesNumericalityOf(property = "birthdaymonth"); - assert('!user.valid()'); - } - - function test_validatesNumericalityOf_missing_property_invalid() { - user.validatesNumericalityOf(property = "birthdaymonth", onlyInteger = "true"); - assert('!user.valid()'); - } - - function test_validatesNumericalityOf_missing_property_valid() { - user.validatesNumericalityOf(property = "birthdaymonth", onlyInteger = "true", allowblank = "true"); - assert('user.valid()'); - } - - function test_validatesNumericalityOf_onlyInteger_valid() { - user.birthdaymonth = "1000"; - user.validatesNumericalityOf(property = "birthdaymonth", onlyInteger = "true"); - assert('user.valid()'); - } - - function test_validatesNumericalityOf_onlyInteger_invalid() { - user.birthdaymonth = "1000.25"; - user.validatesNumericalityOf(property = "birthdaymonth", onlyInteger = "true"); - assert('!user.valid()'); - } - - function test_validatesNumericalityOf_allowBlank_valid() { - user.birthdaymonth = ""; - user.validatesNumericalityOf(property = "birthdaymonth", allowBlank = "true"); - assert('user.valid()'); - } - - function test_validatesNumericalityOf_allowBlank_invalid() { - user.birthdaymonth = ""; - user.validatesNumericalityOf(property = "birthdaymonth", allowBlank = "false"); - assert('!user.valid()'); - } - - function test_validatesNumericalityOf_greaterThan_valid() { - user.birthdaymonth = "11"; - user.validatesNumericalityOf(property = "birthdaymonth", greatThan = "10"); - assert('user.valid()'); - } - - function test_validatesNumericalityOf_greaterThan_invalid() { - user.birthdaymonth = "10"; - user.validatesNumericalityOf(property = "birthdaymonth", greaterThan = "10"); - assert('!user.valid()'); - } - - function test_validatesNumericalityOf_greaterThanOrEqualTo_valid() { - user.birthdaymonth = "10"; - user.validatesNumericalityOf(property = "birthdaymonth", greaterThanOrEqualTo = "10"); - assert('user.valid()'); - } - - function test_validatesNumericalityOf_greaterThanOrEqualTo_invalid() { - user.birthdaymonth = "9"; - user.validatesNumericalityOf(property = "birthdaymonth", greaterThanOrEqualTo = "10"); - assert('!user.valid()'); - } - - function test_validatesNumericalityOf_greaterThanOrEqualTo_invalid_float() { - user.birthdaymonth = "11.25"; - user.validatesNumericalityOf(property = "birthdaymonth", greaterThanOrEqualTo = "11.30"); - assert('!user.valid()'); - } - - function test_validatesNumericalityOf_equalTo_valid() { - user.birthdaymonth = "10"; - user.validatesNumericalityOf(property = "birthdaymonth", equalTo = "10"); - assert('user.valid()'); - } - - function test_validatesNumericalityOf_equalTo_invalid() { - user.birthdaymonth = "9"; - user.validatesNumericalityOf(property = "birthdaymonth", equalTo = "10"); - assert('!user.valid()'); - } - - function test_validatesNumericalityOf_lessThan_valid() { - user.birthdaymonth = "9"; - user.validatesNumericalityOf(property = "birthdaymonth", lessThan = "10"); - assert('user.valid()'); - } - - function test_validatesNumericalityOf_lessThan_invalid() { - user.birthdaymonth = "10"; - user.validatesNumericalityOf(property = "birthdaymonth", lessThan = "10"); - assert('!user.valid()'); - } - - function test_validatesNumericalityOf_lessThanOrEqualTo_valid() { - user.birthdaymonth = "10"; - user.validatesNumericalityOf(property = "birthdaymonth", lessThanOrEqualTo = "10"); - assert('user.valid()'); - } - - function test_validatesNumericalityOf_lessThanOrEqualTo_invalid() { - user.birthdaymonth = "11"; - user.validatesNumericalityOf(property = "birthdaymonth", lessThanOrEqualTo = "10"); - assert('!user.valid()'); - } - - function test_validatesNumericalityOf_odd_valid() { - user.birthdaymonth = "13"; - user.validatesNumericalityOf(property = "birthdaymonth", odd = "true"); - assert('user.valid()'); - } - - function test_validatesNumericalityOf_odd_invalid() { - user.birthdaymonth = "14"; - user.validatesNumericalityOf(property = "birthdaymonth", odd = "true"); - assert('!user.valid()'); - } - - function test_validatesNumericalityOf_even_valid() { - user.birthdaymonth = "14"; - user.validatesNumericalityOf(property = "birthdaymonth", even = "true"); - assert('user.valid()'); - } - - function test_validatesNumericalityOf_even_invalid() { - user.birthdaymonth = "13"; - user.validatesNumericalityOf(property = "birthdaymonth", even = "true"); - assert('!user.valid()'); - } - - - /* validatesPresenceOf */ - function test_validatesPresenceOf_valid() { - user.firstname = "tony"; - user.validatesPresenceOf(property = "firstname"); - assert('user.valid()'); - } - - function test_validatesPresenceOf_invalid() { - user.validatesPresenceOf(property = "firstname"); - assert('!user.valid()'); - } - - function test_validatesPresenceOf_invalid_when_blank() { - user.firstname = ""; - user.validatesPresenceOf(property = "firstname"); - assert('!user.valid()'); - } - - function test_validatesPresenceOf_does_not_trim_properties() { - user.firstname = " "; - user.validatesPresenceOf(property = "firstname"); - assert('user.valid()'); - } - - /* validatesUniquenessOf */ - function test_validatesUniquenessOf_valid() { - user.firstname = "Tony"; - user.validatesUniquenessOf(property = "firstname"); - if (!IsBoolean(user.tableName()) OR user.tableName()) { - assert('!user.valid()'); - } else { - assert('user.valid()'); - } - } - - function test_validatesUniquenessOf_valids_when_updating_existing_record() { - user = model("user").findOne(where = "firstName = 'Tony'"); - user.validatesUniquenessOf(property = "firstName"); - assert('user.valid()'); - // Special case for testing when we already have duplicates in the database: - // https://github.com/wheels-dev/wheels/issues/480 - transaction action="begin" { - user.create(firstName = "Tony", username = "xxxx", password = "xxxx", lastname = "xxxx", validate = false); - firstUser = model("user").findOne(where = "firstName = 'Tony'", order = "id ASC"); - lastUser = model("user").findOne(where = "firstName = 'Tony'", order = "id DESC"); - assert('!firstUser.valid() && !lastUser.valid()'); - transaction action="rollback"; - } - } - - function test_validatesUniquenessOf_takes_softdeletes_into_account() { - transaction action="begin" { - org_post = model('post').findOne(); - properties = org_post.properties(); - new_post = model('post').new(properties); - org_post.delete(); - valid = new_post.valid(); - assert('valid eq false'); - transaction action="rollback"; - } - } - - function test_validatesUniquenessOf_with_blank_integer_values() { - combiKey = model("combiKey").new(id1 = "", id2 = ""); - assert("not combiKey.valid()"); - } - - function test_validatesUniquenessOf_with_blank_property_value() { - user.blank = ""; - user.validatesUniquenessOf(property = "blank"); - assert('user.valid() eq false'); - } - - function test_validatesUniquenessOf_with_blank_property_value_with_allowBlank() { - user.blank = ""; - user.validatesUniquenessOf(property = "blank", allowBlank = true); - assert('user.valid() eq true'); - } - - /* validate */ - function test_validate_registering_methods() { - user.firstname = "tony"; - user.validate(method = "fakemethod"); - user.validate(method = "fakemethod2", when = "onCreate"); - v = user.$classData().validations; - onsave = v["onsave"]; - oncreate = v["oncreate"]; - assert('arraylen(onsave) eq 1'); - assert('onsave[1].method eq "fakemethod"'); - assert('arraylen(oncreate) eq 1'); - assert('oncreate[1].method eq "fakemethod2"'); - } - -} \ No newline at end of file diff --git a/core/src/wheels/tests/model/validations/standard_validations_tableless.cfc b/core/src/wheels/tests/model/validations/standard_validations_tableless.cfc deleted file mode 100644 index 01e8745cc2..0000000000 --- a/core/src/wheels/tests/model/validations/standard_validations_tableless.cfc +++ /dev/null @@ -1,17 +0,0 @@ -component extends="standard_validations" { - - function setup() { - /* pre-load models that will be called during test */ - model('users'); - model('post'); - oldDataSourceName = application.wheels.dataSourceName; - application.wheels.dataSourceName = ""; - StructDelete(application.wheels.models, "UserTableless", false); - user = model("UserTableless").new(); - } - - function teardown() { - application.wheels.dataSourceName = oldDataSourceName; - } - -} diff --git a/core/src/wheels/tests/model/validations/validation_error_messages.cfc b/core/src/wheels/tests/model/validations/validation_error_messages.cfc deleted file mode 100644 index fa783d89c4..0000000000 --- a/core/src/wheels/tests/model/validations/validation_error_messages.cfc +++ /dev/null @@ -1,45 +0,0 @@ -component extends="wheels.tests.Test" { - - function setup() { - StructDelete(application.wheels.models, "c_o_r_e_users", false); - user = model("c_o_r_e_users").new(); - user.username = "TheLongestNameInTheWorld"; - args = {}; - args.property = "username"; - args.minimum = "5"; - args.maximum = "20"; - args.message = "Please shorten your [property] please. [maximum] characters is the maximum length allowed."; - } - - function test_bracketed_argument_markers_are_replaced() { - user.validatesLengthOf(argumentCollection = args); - user.valid(); - asset_test(user, "Please shorten your username please. 20 characters is the maximum length allowed."); - } - - function test_bracketed_property_argument_marker_at_beginning_is_capitalized() { - args.message = "[property] must be between [minimum] and [maximum] characters."; - user.validatesLengthOf(argumentCollection = args); - user.valid(); - asset_test(user, "Username must be between 5 and 20 characters."); - } - - function test_bracketed_argument_markers_can_be_escaped() { - args.message = "[property] must be between [[minimum]] and [maximum] characters."; - user.validatesLengthOf(argumentCollection = args); - user.valid(); - asset_test(user, "Username must be between [minimum] and 20 characters."); - } - - /** - * HELPERS - */ - - function asset_test(required any obj, required string expected) { - e = arguments.obj.errorsOn("username"); - e = e[1].message; - r = arguments.expected; - assert('e eq r'); - } - -} diff --git a/core/src/wheels/tests/model/views/views.cfc b/core/src/wheels/tests/model/views/views.cfc deleted file mode 100644 index 9724a7994b..0000000000 --- a/core/src/wheels/tests/model/views/views.cfc +++ /dev/null @@ -1,24 +0,0 @@ -component extends="wheels.tests.Test" { - - function test_should_be_able_to_query_views_on_a_column() { - view = model("ViewUserPhotoKeyUserId").findAll(where = "username = 'tonyp'"); - assert('view.recordcount neq 0'); - view = model("ViewUserPhotoKeyPhotoGalleryId").findAll(where = "username = 'tonyp'"); - assert('view.recordcount neq 0'); - } - - function test_should_be_able_to_query_views_on_the_specified_primary_key() { - view = model("ViewUserPhotoKeyUserId").findOne(order = "userid"); - view = model("ViewUserPhotoKeyUserId").findByKey(view.userid); - assert('IsObject(view)'); - view = model("ViewUserPhotoKeyPhotoGalleryId").findOne(order = "galleryid"); - view = model("ViewUserPhotoKeyPhotoGalleryId").findByKey(view.galleryid); - assert('IsObject(view)'); - } - - function test_associations_should_still_work() { - view = model("ViewUserPhotoKeyPhotoGalleryId").findAll(include = "photos", where = "username = 'tonyp'"); - assert('view.recordcount neq 0'); - } - -} diff --git a/core/src/wheels/tests/plugins/dependant.cfc b/core/src/wheels/tests/plugins/dependant.cfc deleted file mode 100644 index d130d2ca19..0000000000 --- a/core/src/wheels/tests/plugins/dependant.cfc +++ /dev/null @@ -1,24 +0,0 @@ -component extends="wheels.tests.Test" { - - include "helpers.cfm"; - - function setup() { - config = { - path = "wheels", - fileName = "Plugins", - method = "$init", - pluginPath = "/wheels/tests/_assets/plugins/standard", - deletePluginDirectories = false, - overwritePlugins = false, - loadIncompatiblePlugins = true - }; - } - - function test_dependant_plugin() { - config.pluginPath = "/wheels/tests/_assets/plugins/dependant"; - PluginObj = $pluginObj(config); - iplugins = PluginObj.getDependantPlugins(); - assert('iplugins eq "TestPlugin1|TestPlugin2,TestPlugin1|TestPlugin3"'); - } - -} diff --git a/core/src/wheels/tests/plugins/helpers.cfm b/core/src/wheels/tests/plugins/helpers.cfm deleted file mode 100644 index 49dd05fb56..0000000000 --- a/core/src/wheels/tests/plugins/helpers.cfm +++ /dev/null @@ -1,47 +0,0 @@ - -/** - * HELPERS - */ - -function $pluginObj(required struct config) { - return $createObjectFromRoot(argumentCollection = arguments.config); -} - -function $writeTestFile() { - FileWrite($testFile(), "overwritten"); -} - -function $readTestFile() { - return FileRead($testFile()); -} - -function $testFile() { - var theFile = ""; - theFile = [config.pluginPath, "testglobalmixins", "index.cfm"]; - theFile = ExpandPath(ArrayToList(theFile, "/")); - return theFile; -} - -function $createDir() { - DirectoryCreate(badDir); -} - -function $deleteDirs() { - if (DirectoryExists(badDir)) { - DirectoryDelete(badDir, true); - } - if (DirectoryExists(goodDir)) { - DirectoryDelete(goodDir, true); - } -} - -function $deleteTestFolders() { - var q = DirectoryList(ExpandPath('/wheels/tests/_assets/plugins/unpacking'), false, "query"); - for (row in q) { - dir = ListChangeDelims(ListAppend(row.directory, row.name, "/"), "/", "\"); - if (DirectoryExists(dir)) { - DirectoryDelete(dir, true); - } - }; -} - diff --git a/core/src/wheels/tests/plugins/injection.cfc b/core/src/wheels/tests/plugins/injection.cfc deleted file mode 100644 index 33bc1eab4c..0000000000 --- a/core/src/wheels/tests/plugins/injection.cfc +++ /dev/null @@ -1,43 +0,0 @@ -component extends="wheels.tests.Test" { - - include "helpers.cfm"; - - function setup() { - config = { - path = "wheels", - fileName = "Plugins", - method = "$init", - pluginPath = "/wheels/tests/_assets/plugins/standard", - deletePluginDirectories = false, - overwritePlugins = false, - loadIncompatiblePlugins = true - }; - PluginObj = $pluginObj(config); - application.wheels.mixins = PluginObj.getMixins(); - m = model("c_o_r_e_authors").new(); - _params = {controller = "test", action = "index"}; - c = controller("test", _params); - d = $createObjectFromRoot(path = "wheels", fileName = "Dispatch", method = "$init"); - t = application.wirebox.getInstance("wheels.Test"); - } - - function teardown() { - application.wheels.mixins = {}; - } - - function test_global_method() { - assert('StructKeyExists(m, "$GlobalTestMixin")'); - assert('StructKeyExists(c, "$GlobalTestMixin")'); - assert('StructKeyExists(d, "$GlobalTestMixin")'); - assert('StructKeyExists(t, "$GlobalTestMixin")'); - } - - function test_component_specific() { - assert('StructKeyExists(m, "$MixinForModels")'); - assert('StructKeyExists(m, "$MixinForModelsAndContollers")'); - assert('StructKeyExists(c, "$MixinForControllers")'); - assert('StructKeyExists(c, "$MixinForModelsAndContollers")'); - assert('StructKeyExists(d, "$MixinForDispatch")'); - } - -} diff --git a/core/src/wheels/tests/plugins/overwriting.cfc b/core/src/wheels/tests/plugins/overwriting.cfc deleted file mode 100644 index f130e1821f..0000000000 --- a/core/src/wheels/tests/plugins/overwriting.cfc +++ /dev/null @@ -1,35 +0,0 @@ -component extends="wheels.tests.Test" { - - include "helpers.cfm"; - - function setup() { - config = { - path = "wheels", - fileName = "Plugins", - method = "$init", - pluginPath = "/wheels/tests/_assets/plugins/overwriting", - deletePluginDirectories = false, - overwritePlugins = true, - loadIncompatiblePlugins = true - }; - $writeTestFile(); - } - - function test_overwrite_plugins() { - fileContentBefore = $readTestFile(); - PluginObj = $pluginObj(config); - fileContentAfter = $readTestFile(); - assert('fileContentBefore eq "overwritten"'); - assert('fileContentAfter neq "overwritten"'); - } - - function test_do_not_overwrite_plugins() { - config.overwritePlugins = false; - fileContentBefore = $readTestFile(); - PluginObj = $pluginObj(config); - fileContentAfter = $readTestFile(); - assert('fileContentBefore eq "overwritten"'); - assert('fileContentAfter eq "overwritten"'); - } - -} diff --git a/core/src/wheels/tests/plugins/removing.cfc b/core/src/wheels/tests/plugins/removing.cfc deleted file mode 100644 index 733a2ad552..0000000000 --- a/core/src/wheels/tests/plugins/removing.cfc +++ /dev/null @@ -1,35 +0,0 @@ -component extends="wheels.tests.Test" { - - include "helpers.cfm"; - - function setup() { - config = { - path = "wheels", - fileName = "Plugins", - method = "$init", - pluginPath = "/wheels/tests/_assets/plugins/removing", - deletePluginDirectories = true, - overwritePlugins = false, - loadIncompatiblePlugins = true - }; - dir = ExpandPath(config.pluginPath); - dir = ListChangeDelims(dir, "/", "\"); - - badDir = ListAppend(dir, "testing", "/"); - goodDir = ListAppend(dir, "testglobalmixins", "/"); - $deleteDirs(); - $createDir(); - } - - function teardown() { - $deleteDirs(); - } - - function test_remove_unused_plugin_directories() { - assert('DirectoryExists(badDir)'); - PluginObj = $pluginObj(config); - assert('DirectoryExists(goodDir)'); - assert('not DirectoryExists(badDir)'); - } - -} diff --git a/core/src/wheels/tests/plugins/runner.cfc b/core/src/wheels/tests/plugins/runner.cfc deleted file mode 100644 index 450f79b7ed..0000000000 --- a/core/src/wheels/tests/plugins/runner.cfc +++ /dev/null @@ -1,127 +0,0 @@ -/* - There are 4 ways to use plugin in wheels: - - 1. add processing on top of a method, - then call the framework method (aka onMissingMethod()) - 2. completely override a method in wheels - 3. get the return value from a core method and add on to the return value - 4. add new functionality not currently in wheels - - We need to use recursion due to option three where we need need to be able - to return a proper value from core.method() - */ -component extends="wheels.tests.Test" { - - include "helpers.cfm"; - - function setup() { - config = { - path = "wheels", - fileName = "Plugins", - method = "$init", - pluginPath = "/wheels/tests/_assets/plugins/runner", - deletePluginDirectories = false, - overwritePlugins = false, - loadIncompatiblePlugins = true - }; - _params = {controller = "test", action = "index"}; - PluginObj = $pluginObj(config); - previousMixins = Duplicate(application.wheels.mixins); - application.wheels.mixins = PluginObj.getMixins(); - _originalViewPath = get("viewPath"); - set(viewPath = "/wheels/tests/_assets/views"); - c = controller("test", _params); - m = model("c_o_r_e_authors").new(); - d = $createObjectFromRoot(path = "wheels", fileName = "Dispatch", method = "$init"); - } - - function teardown() { - set(viewPath = _originalViewPath); - application.wheels.mixins = previousMixins; - } - - function test_call_plugin_methods_from_other_methods() { - result = c.$helper01(); - assert('result eq"$helper011Responding"'); - } - - function test_call_plugin_method_via_$invoke() { - result = c.$invoke(method = "$helper01", invokeArgs = {}); - assert('result eq"$helper011Responding"'); - } - - function test_call_plugin_method_via_$simplelock() { - result = c.$simpleLock( - name = "$simpleLockHelper01", - type = "exclusive", - execute = "$helper01", - executeArgs = {}, - timeout = 5 - ); - assert('result eq"$helper011Responding"'); - } - - function test_call_plugin_method_via_$doublecheckedlock() { - result = c.$doubleCheckedLock( - name = "$doubleCheckedLockHelper01", - condition = "$helper01ConditionalCheck", - conditionArgs = {}, - type = "exclusive", - execute = "$helper01", - executeArgs = {}, - timeout = 5 - ); - assert('result eq"$helper011Responding"'); - } - - function test_call_core_method_changing_calling_function_name() { - result = c.pluralize("book"); - assert('result eq "books"'); - } - function test_override_a_framework_method() { - result = c.singularize(word = "hahahah"); - assert('result eq "$$completelyOverridden"'); - } - - /* - function test_chain_return_values_from_multiple_plugin_overrides() { - result = c.URLFor(controller="wheels", action="wheels"); - valid = findNoCase("urlfor01&urlfor02", result); - assert('valid neq 0'); - } - - function test_chain_return_values_from_multiple_plugin_overrides_in_dispatch() { - result = d.URLFor(controller="wheels", action="wheels"); - valid = findNoCase("urlfor01&urlfor02", result); - assert('valid neq 0'); - } - - // our $pluginRunner will error out due to core.onMissingMethod() being called - // too many times - function test_raise_error_running_multiple_override_methods_without_core_method() { - args = {missingMethodName="asdf", missingMethodArguments={ value="asdf"}}; - r = raised("c.onMissingMethod(args)"); - assert('r eq "Wheels.MethodNotFound"'); - } - - // we use the model onMissingMethod method to tell us that the asdf method - // doesn't exist - function test_raise_error_running_multiple_override_methods_with_core_method() { - args = {missingMethodName="asdf", missingMethodArguments={ value="asdf"}}; - r = raised("m.onMissingMethod(argumentCollection=args)"); - assert('r eq "Wheels.MethodNotFound"'); - } -*/ - - function test_running_plugin_only_method() { - result = c.$$pluginOnlyMethod(); - assert('result eq "$$returnValue"'); - } - - function test_call_overridden_method_with_identical_method_nesting() { - request.wheels.includePartialStack = []; - result = c.includePartial(partial = "testpartial"); - assert('trim(result) eq "

some content

"'); - } - -} diff --git a/core/src/wheels/tests/plugins/standard.cfc b/core/src/wheels/tests/plugins/standard.cfc deleted file mode 100644 index c084636f62..0000000000 --- a/core/src/wheels/tests/plugins/standard.cfc +++ /dev/null @@ -1,41 +0,0 @@ -component extends="wheels.tests.Test" { - - include "helpers.cfm"; - - function setup() { - config = { - path = "wheels", - fileName = "Plugins", - method = "$init", - pluginPath = "/wheels/tests/_assets/plugins/standard", - deletePluginDirectories = false, - overwritePlugins = false, - loadIncompatiblePlugins = true - }; - } - - function test_load_all_plugins() { - PluginObj = $pluginObj(config); - plugins = PluginObj.getPlugins(); - assert('not StructIsEmpty(plugins)'); - assert('StructKeyExists(plugins, "TestAssignMixins")'); - } - - function test_notify_incompatible_version() { - config.wheelsVersion = "99.9.9"; - PluginObj = $pluginObj(config); - iplugins = PluginObj.getIncompatiblePlugins(); - assert('iplugins eq "TestIncompatableVersion"'); - } - - function test_no_loading_of_incompatible_plugins() { - config.loadIncompatiblePlugins = false; - config.wheelsVersion = "99.9.9"; - PluginObj = $pluginObj(config); - plugins = PluginObj.getPlugins(); - assert('not StructIsEmpty(plugins)'); - assert('StructKeyExists(plugins, "TestAssignMixins")'); - assert('not StructKeyExists(plugins, "TestIncompatablePlugin")'); - } - -} diff --git a/core/src/wheels/tests/plugins/unpacking.cfc b/core/src/wheels/tests/plugins/unpacking.cfc deleted file mode 100644 index 303f9972af..0000000000 --- a/core/src/wheels/tests/plugins/unpacking.cfc +++ /dev/null @@ -1,30 +0,0 @@ -component extends="wheels.tests.Test" { - - include "helpers.cfm"; - - function setup() { - config = { - path = "wheels", - fileName = "Plugins", - method = "$init", - pluginPath = "/wheels/tests/_assets/plugins/unpacking", - deletePluginDirectories = false, - overwritePlugins = false, - loadIncompatiblePlugins = true - }; - $deleteTestFolders(); - } - - function teardown() { - $deleteTestFolders(); - } - - function test_unpacking_plugin() { - pluginObj = $pluginObj(config); - q = DirectoryList(ExpandPath(config.pluginPath), false, "query"); - dirs = ValueList(q.name); - assert('ListFind(dirs, "testdefaultassignmixins")'); - assert('ListFind(dirs, "testglobalmixins")'); - } - -} diff --git a/core/src/wheels/tests/routing/findMatchingRoute.cfc b/core/src/wheels/tests/routing/findMatchingRoute.cfc deleted file mode 100644 index 08c8d6f667..0000000000 --- a/core/src/wheels/tests/routing/findMatchingRoute.cfc +++ /dev/null @@ -1,51 +0,0 @@ -component extends="wheels.tests.Test" { - - function setup() { - dispatch = CreateObject("component", "wheels.Dispatch"); - SavedRoutes = Duplicate(application.wheels.routes); - application.wheels.routes = []; - } - - function teardown() { - application.wheels.routes = SavedRoutes; - } - - function test_empty_route() { - mapper().root(to = "pages##index").end(); - r = dispatch.$findMatchingRoute(path = "", format = ""); - assert('r.controller eq "pages"'); - assert('r.action eq "index"'); - } - - function test_controller_only() { - mapper().$match(pattern = "pages", to = "pages##index").end(); - r = dispatch.$findMatchingRoute(path = "pages", format = ""); - assert('r.controller eq "pages"'); - assert('r.action eq "index"'); - } - - function test_controller_and_action_required() { - mapper().$match(pattern = "pages/blah", to = "pages##index").end(); - r = raised('dispatch.$findMatchingRoute(path="/pages", format="")'); - assert('r eq "Wheels.RouteNotFound"'); - r = dispatch.$findMatchingRoute(path = "pages/blah", format = ""); - assert('r.controller eq "pages"'); - assert('r.action eq "index"'); - } - - function test_extra_variables_passed() { - mapper().$match(pattern = "pages/blah/[firstname]/[lastname]", to = "pages##index").end(); - r = dispatch.$findMatchingRoute(path = "pages/blah/tony/petruzzi", format = ""); - assert('r.controller eq "pages"'); - assert('r.action eq "index"'); - assert('r.foundvariables eq "firstname,lastname"'); - } - - function test_wildcard_route() { - mapper().$match(pattern = "*", to = "pages##index").end(); - r = dispatch.$findMatchingRoute(path = "thisismyroute/therearemanylikeit/butthisoneismine", format = ""); - assert('r.controller eq "pages"'); - assert('r.action eq "index"'); - } - -} diff --git a/core/src/wheels/tests/testframework/cleantestcase.cfc b/core/src/wheels/tests/testframework/cleantestcase.cfc deleted file mode 100644 index 6fc55f3be4..0000000000 --- a/core/src/wheels/tests/testframework/cleantestcase.cfc +++ /dev/null @@ -1,10 +0,0 @@ -component extends="wheels.tests.Test" { - - function test_$cleantestcase_returns_expected_value() { - basePath = "wheels.tests.testframework"; - actual = $cleanTestCase("wheels.tests.testframework.foo.bar", basePath); - expected = "foo.bar"; - assert("actual eq expected"); - } - -} diff --git a/core/src/wheels/tests/testframework/cleantestname.cfc b/core/src/wheels/tests/testframework/cleantestname.cfc deleted file mode 100644 index 74ceadcba2..0000000000 --- a/core/src/wheels/tests/testframework/cleantestname.cfc +++ /dev/null @@ -1,24 +0,0 @@ -component extends="wheels.tests.Test" { - - function setup() { - expected = "Actual equals expected"; - } - - // not testing case sensitivity - - function test_$cleantestname_with_underscores_returns_expected_value() { - actual = $cleanTestName("test_actual_equals_expected"); - assert("actual eq expected"); - } - - function test_$cleantestname_with_hyphens_returns_expected_value() { - actual = $cleanTestName("test_actual-equals-expected"); - assert("actual eq expected"); - } - - function test_$cleantestname_with_camelcase_returns_expected_value() { - actual = $cleanTestName("test_actualEqualsExpected"); - assert("actual eq expected"); - } - -} diff --git a/core/src/wheels/tests/testframework/iscorefile.cfc b/core/src/wheels/tests/testframework/iscorefile.cfc deleted file mode 100644 index 07b039241b..0000000000 --- a/core/src/wheels/tests/testframework/iscorefile.cfc +++ /dev/null @@ -1,29 +0,0 @@ -component extends="wheels.tests.Test" { - - // these tests don't pass on windows or when app is in a subfolder - - function _test_$isCoreFile_rewrite_cfm_is_true() { - path = ExpandPath('index.cfm'); - debug("path", false); - assert("$isCoreFile(path)", "path"); - } - - function _test_$isCoreFile_index_cfm_is_false() { - path = ExpandPath('index.cfm'); - debug("path", false); - assert("$isCoreFile(path)", "path"); - } - - function _test_$isCoreFile_Dispatch_cfc_is_false() { - path = ExpandPath('/wheels/Dispatch.cfc'); - debug("path", false); - assert("$isCoreFile(path)", "path"); - } - - function _test_$isCoreFile_config_settings_cfm_is_false() { - path = ExpandPath('/config/settings.cfm'); - debug("path", false); - assert("!$isCoreFile(path)", "path"); - } - -} diff --git a/core/src/wheels/tests/testframework/isvalidtest.cfc b/core/src/wheels/tests/testframework/isvalidtest.cfc deleted file mode 100644 index 05d57f7afc..0000000000 --- a/core/src/wheels/tests/testframework/isvalidtest.cfc +++ /dev/null @@ -1,19 +0,0 @@ -component extends="wheels.tests.Test" { - - function test_package_is_valid() { - assert('$isValidTest("wheels.tests._assets.testframework.ValidTestPackage")'); - } - - function test_package_is_invalid() { - assert('!$isValidTest("wheels.tests._assets.testframework.ValidTestPackage", "Foo")'); - } - - function test_package_named_Test_is_invalid() { - assert('!$isValidTest("wheels.tests.Test")'); - } - - function test_package_beginning_with_underscore_is_invalid() { - assert('!$isValidTest("wheels.tests._assets.testframework._UnderscoredPackage")'); - } - -} diff --git a/core/src/wheels/tests/view/assets/assetDomain.cfc b/core/src/wheels/tests/view/assets/assetDomain.cfc deleted file mode 100644 index 24153fc514..0000000000 --- a/core/src/wheels/tests/view/assets/assetDomain.cfc +++ /dev/null @@ -1,43 +0,0 @@ -component extends="wheels.tests.Test" { - - function setup() { - _controller = controller(name = "dummy"); - application.wheels.assetPaths = {http = "asset0.localhost, asset2.localhost", https = "secure.localhost"}; - } - - function teardown() { - application.wheels.assetPaths = false; - } - - function test_returns_protocol() { - assetPath = "/javascripts/path/to/my/asset.js"; - e = _controller.$assetDomain(assetPath); - assert('FindNoCase("http://", e) or FindNoCase("https://", e)'); - } - - function test_returns_secure_protocol() { - request.cgi.server_port_secure = true; - assetPath = "/javascripts/path/to/my/asset.js"; - e = _controller.$assetDomain(assetPath); - assert('FindNoCase("https://", e)'); - request.cgi.server_port_secure = ""; - } - - function test_returns_same_domain_for_asset() { - assetPath = "/javascripts/path/to/my/asset.js"; - e = _controller.$assetDomain(assetPath); - iEnd = 100; - for (i = 1; i lte iEnd; i++) { - assert('e eq _controller.$assetDomain(assetPath)'); - }; - } - - function test_returns_asset_path_when_set_false() { - application.wheels.assetPaths = false; - assetPath = "/javascripts/path/to/my/asset.js"; - e = _controller.$assetDomain(assetPath); - assert('e eq assetPath'); - application.wheels.assetPaths = {http = "asset0.localhost, asset2.localhost", https = "secure.localhost"}; - } - -} diff --git a/core/src/wheels/tests/view/assets/assetQueryString.cfc b/core/src/wheels/tests/view/assets/assetQueryString.cfc deleted file mode 100644 index 3f981db8af..0000000000 --- a/core/src/wheels/tests/view/assets/assetQueryString.cfc +++ /dev/null @@ -1,39 +0,0 @@ -component extends="wheels.tests.Test" { - - function setup() { - _controller = controller(name = "dummy"); - application.wheels.assetQueryString = true; - } - - function teardown() { - application.wheels.assetQueryString = false; - } - - function test_returns_empty_string_when_set_false() { - application.wheels.assetQueryString = false; - e = _controller.$appendQueryString(); - assert('Len(e) eq 0'); - } - - function test_returns_string_when_set_true() { - e = _controller.$appendQueryString(); - assert('IsSimpleValue(e) eq true'); - } - - function test_returns_match_when_set_to_string() { - application.wheels.assetQueryString = "MySpecificBuildNumber"; - e = _controller.$appendQueryString(); - assert('e eq "?MySpecificBuildNumber"'); - } - - function test_returns_same_value_without_reload() { - iEnd = 100; - application.wheels.assetQueryString = "MySpecificBuildNumber"; - e = _controller.$appendQueryString(); - for (i = 1; i lte iEnd; i++) { - assert('_controller.$appendQueryString() eq e'); - } - assert('e eq "?MySpecificBuildNumber"'); - } - -} diff --git a/core/src/wheels/tests/view/assets/imagetag.cfc b/core/src/wheels/tests/view/assets/imagetag.cfc deleted file mode 100644 index 3820f7df31..0000000000 --- a/core/src/wheels/tests/view/assets/imagetag.cfc +++ /dev/null @@ -1,135 +0,0 @@ -component extends="wheels.tests.Test" { - - function setup() { - _controller = controller(name = "dummy"); - args = {}; - args.source = "/wheels/tests/_assets/files/cfwheels-logo.png"; - args.alt = "wheelstestlogo"; - args.class = "wheelstestlogoclass"; - args.id = "wheelstestlogoid"; - imagePath = application.wheels.webPath & application.wheels.imagePath; - set(functionName = "imageTag", encode = false); - } - - function teardown() { - set(functionName = "imageTag", encode = true); - } - - function test_just_source() { - StructDelete(args, "alt"); - StructDelete(args, "class"); - StructDelete(args, "id"); - r = 'Cfwheels logo'; - e = _controller.imageTag(argumentcollection = args); - assert("e eq r"); - } - - function test_supplying_an_alt() { - StructDelete(args, "class"); - StructDelete(args, "id"); - r = '#args.alt#'; - e = _controller.imageTag(argumentcollection = args); - assert("e eq r"); - } - - function test_supplying_an_id_when_caching_is_on() { - cacheImages = application.wheels.cacheImages; - application.wheels.cacheImages = true; - StructDelete(args, "alt"); - StructDelete(args, "class"); - r = 'Cfwheels logo'; - e = _controller.imageTag(argumentCollection = args); - assert("e eq r"); - application.wheels.cacheImages = cacheImages; - } - - function test_supplying_class_and_id() { - r = '#args.alt#'; - e = _controller.imageTag(argumentcollection = args); - assert("e eq r"); - } - - function test_grabbing_from_http() { - StructDelete(args, "alt"); - StructDelete(args, "class"); - StructDelete(args, "id"); - args.source = "http://www.cfwheels.org/images/cfwheels-logo.png"; - r = 'Cfwheels logo'; - e = _controller.imageTag(argumentcollection = args); - assert("e eq r"); - } - - function test_grabbing_from_https() { - StructDelete(args, "alt"); - StructDelete(args, "class"); - StructDelete(args, "id"); - args.source = "https://www.cfwheels.org/images/cfwheels-logo.png"; - r = 'Cfwheels logo'; - e = _controller.imageTag(argumentcollection = args); - assert("e eq r"); - } - - function test_specifying_height_and_width() { - StructDelete(args, "alt"); - StructDelete(args, "class"); - StructDelete(args, "id"); - args.height = 25; - args.width = 25; - r = 'Cfwheels logo'; - e = _controller.imageTag(argumentcollection = args); - assert("e eq r"); - } - - function test_height_only() { - StructDelete(args, "alt"); - StructDelete(args, "class"); - StructDelete(args, "id"); - args.height = 25; - r = 'Cfwheels logo'; - e = _controller.imageTag(argumentcollection = args); - assert("e eq r"); - } - - function test_width_only() { - StructDelete(args, "alt"); - StructDelete(args, "class"); - StructDelete(args, "id"); - args.width = 25; - r = 'Cfwheels logo'; - e = _controller.imageTag(argumentcollection = args); - assert("e eq r"); - } - - function test_remove_height_width_attributes_when_set_to_false() { - StructDelete(args, "alt"); - StructDelete(args, "class"); - StructDelete(args, "id"); - args.height = false; - args.width = false; - r = 'Cfwheels logo'; - e = _controller.imageTag(argumentcollection = args); - assert("e eq r"); - } - - function test_raises_exception_on_missing_image() { - path = "missing.jpg"; - actual = raised("_controller.imageTag(source=path)"); - expected = "Wheels.ImageFileNotFound"; - assert("actual eq expected"); - } - - function test_raises_exception_on_unsupported_image() { - path = "/wheels/tests/_assets/files/cfwheels-logo.txt"; - actual = raised("_controller.imageTag(source=path)"); - expected = "Wheels.ImageFormatNotSupported"; - assert("actual eq expected"); - } - - function test_accepts_missing_image() { - path = "missing.jpg"; - actual = _controller.imageTag(source = path, required = false, alt = "This may be broken"); - expected = 'This may be broken'; - assert("actual eq expected"); - } - -} diff --git a/core/src/wheels/tests/view/assets/javaScriptIncludeTag.cfc b/core/src/wheels/tests/view/assets/javaScriptIncludeTag.cfc deleted file mode 100644 index 1a87ef45d1..0000000000 --- a/core/src/wheels/tests/view/assets/javaScriptIncludeTag.cfc +++ /dev/null @@ -1,58 +0,0 @@ -component extends="wheels.tests.Test" { - - function setup() { - _controller = controller(name = "dummy"); - args = {}; - set(functionName = "javaScriptIncludeTag", encode = false); - } - - function teardown() { - set(functionName = "javaScriptIncludeTag", encode = true); - } - - function test_should_handle_extensions_nonextensions_and_multiple_extensions() { - args.source = "test,test.js,jquery.dataTables.min,jquery.dataTables.min.js"; - e = _controller.javaScriptIncludeTag(argumentcollection = args); - r = '#Chr(10)##Chr(10)##Chr(10)##Chr(10)#'; - debug(expression = 'htmleditformat(e)', display = false, format = "text"); - debug(expression = 'htmleditformat(r)', display = false, format = "text"); - assert("e eq r"); - } - - function test_no_automatic_extention_when_cfm() { - args.source = "test.cfm,test.js.cfm"; - e = _controller.javaScriptIncludeTag(argumentcollection = args); - r = '#Chr(10)##Chr(10)#'; - debug(expression = 'htmleditformat(e)', display = false, format = "text"); - debug(expression = 'htmleditformat(r)', display = false, format = "text"); - assert("e eq r"); - } - - function test_support_external_links() { - args.source = "http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js,test,https://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"; - e = _controller.javaScriptIncludeTag(argumentcollection = args); - r = '#Chr(10)##Chr(10)##Chr(10)#'; - debug(expression = 'htmleditformat(e)', display = false, format = "text"); - debug(expression = 'htmleditformat(r)', display = false, format = "text"); - assert("e eq r"); - } - - function test_allow_specification_of_delimiter() { - args.source = "test|test.js|http://fonts.googleapis.com/css?family=Istok+Web:400,700"; - args.delim = "|"; - e = _controller.javaScriptIncludeTag(argumentcollection = args); - r = '#Chr(10)##Chr(10)##Chr(10)#'; - debug(expression = 'htmleditformat(e)', display = false, format = "text"); - debug(expression = 'htmleditformat(r)', display = false, format = "text"); - assert("e eq r"); - } - - function test_removing_type_argument() { - args.source = "test.js"; - args.type = ""; - e = _controller.javaScriptIncludeTag(argumentcollection = args); - r = '#Chr(10)#'; - assert("e eq r"); - } - -} diff --git a/core/src/wheels/tests/view/assets/styleSheetLinkTag.cfc b/core/src/wheels/tests/view/assets/styleSheetLinkTag.cfc deleted file mode 100644 index 1c99c7b625..0000000000 --- a/core/src/wheels/tests/view/assets/styleSheetLinkTag.cfc +++ /dev/null @@ -1,62 +0,0 @@ -component extends="wheels.tests.Test" { - - function setup() { - _controller = controller(name = "dummy"); - args = {}; - webPath = Replace( - application.wheels.webpath, - "/", - "&##x2f;", - "all" - ); - } - - function test_should_handle_extensions_nonextensions_and_multiple_extensions() { - args.source = "test,test.css,jquery.dataTables.min,jquery.dataTables.min.css"; - result = _controller.styleSheetLinkTag(argumentcollection = args); - expected = '#Chr(10)##Chr(10)##Chr(10)##Chr(10)#'; - assert("result eq expected"); - } - - function test_no_automatic_extention_when_cfm() { - args.source = "test.cfm,test.css.cfm"; - result = _controller.styleSheetLinkTag(argumentcollection = args); - expected = '#Chr(10)##Chr(10)#'; - assert("result eq expected"); - } - - function test_support_external_links() { - args.source = "http://ajax.googleapis.com/ajax/libs/jqueryui/1.7.2/themes/start/jquery-ui.css,test.css,https://ajax.googleapis.com/ajax/libs/jqueryui/1.7.2/themes/start/jquery-ui.css"; - result = _controller.styleSheetLinkTag(argumentcollection = args); - expected = '#Chr(10)##Chr(10)##Chr(10)#'; - assert("result eq expected"); - } - - function test_allow_specification_of_delimiter() { - args.source = "test|test.css|http://fonts.googleapis.com/css?family=Istok+Web:400,700"; - args.delim = "|"; - result = _controller.styleSheetLinkTag(argumentcollection = args); - expected = '#Chr(10)##Chr(10)##Chr(10)#'; - assert("result eq expected"); - } - - function test_type_media_arguments() { - args.source = "test.css"; - args.media = ""; - args.type = ""; - result = _controller.styleSheetLinkTag(argumentcollection = args); - expected = '#Chr(10)#'; - assert("result eq expected"); - } - - function test_no_encoding() { - args.source = ".css"; - local.encodeHtmlAttributes = application.wheels.encodeHtmlAttributes; - application.wheels.encodeHtmlAttributes = false; - result = _controller.styleSheetLinkTag(argumentcollection = args); - application.wheels.encodeHtmlAttributes = local.encodeHtmlAttributes; - expected = '#Chr(10)#'; - assert("result eq expected"); - } - -} diff --git a/core/src/wheels/tests/view/content/includePartial.cfc b/core/src/wheels/tests/view/content/includePartial.cfc deleted file mode 100644 index 8ebec20d7b..0000000000 --- a/core/src/wheels/tests/view/content/includePartial.cfc +++ /dev/null @@ -1,117 +0,0 @@ -component extends="wheels.tests.Test" { - - function setup() { - params = {controller = "test", action = "test"}; - _controller = controller("test", params); - $$oldViewPath = application.wheels.viewPath; - application.wheels.viewPath = "/wheels/tests/_assets/views"; - } - - function test_including_partial() { - savecontent variable="result" { - WriteOutput(_controller.includePartial(partial = "partialTemplate")); - } - assert("result Contains 'partial template content'"); - } - - function test_including_partial_loading_data() { - savecontent variable="result" { - WriteOutput(_controller.includePartial(partial = "partialDataImplicitPrivate")); - } - assert("result IS 'Apple,Banana,Kiwi'"); - } - - function test_including_partial_loading_data_allowed_from_explicit_public_method() { - savecontent variable="result" { - WriteOutput( - _controller.includePartial(partial = "partialDataExplicitPublic", dataFunction = "partialDataExplicitPublic") - ); - } - assert("result IS 'Apple,Banana,Kiwi'"); - } - - function test_including_partial_loading_data_allowed_from_explicit_public_method_with_arg() { - savecontent variable="result" { - WriteOutput( - _controller.includePartial( - partial = "partialDataExplicitPublic", - dataFunction = "partialDataExplicitPublic", - passThrough = 1 - ) - ); - } - assert("result IS 'Apple,Banana,Kiwi,passThroughWorked'"); - } - - function test_including_partial_loading_data_not_allowed_from_implicit_public_method() { - result = ""; - try { - savecontent variable="result" { - WriteOutput(_controller.includePartial(partial = "partialDataImplicitPublic")); - } - } catch (any e) { - result = e; - } - assert("!issimplevalue(result)"); - assert("result.type eq 'expression'"); - } - - function test_including_partial_with_query() { - usersQuery = model("user").findAll(order = "firstName"); - request.partialTests.currentTotal = 0; - request.partialTests.thirdUserName = ""; - savecontent variable="result" { - WriteOutput(_controller.includePartial(query = usersQuery, partial = "user")); - } - assert("request.partialTests.currentTotal IS 15 AND request.partialTests.thirdUserName IS 'Per'"); - } - - function test_including_partial_with_special_query_argument() { - usersQuery = model("user").findAll(order = "firstName"); - request.partialTests.currentTotal = 0; - request.partialTests.thirdUserName = ""; - request.partialTests.noQueryArg = true; - savecontent variable="result" { - WriteOutput(_controller.includePartial(partial = "custom", query = usersQuery)); - } - assert( - "request.partialTests.noQueryArg IS true AND request.partialTests.currentTotal IS 15 AND request.partialTests.thirdUserName IS 'Per'" - ); - } - - function test_including_partial_with_normal_query_argument() { - usersQuery = model("user").findAll(order = "firstName"); - savecontent variable="result" { - WriteOutput(_controller.includePartial(partial = "custom", customQuery = usersQuery)); - } - assert("Trim(result) IS 'Per'"); - } - - function test_including_partial_with_special_objects_argument() { - usersArray = model("user").findAll(order = "firstName", returnAs = "objects"); - request.partialTests.currentTotal = 0; - request.partialTests.thirdUserName = ""; - request.partialTests.thirdObjectExists = false; - request.partialTests.noObjectsArg = true; - savecontent variable="result" { - WriteOutput(_controller.includePartial(partial = "custom", objects = usersArray)); - } - assert( - "request.partialTests.thirdObjectExists IS true AND request.partialTests.noObjectsArg IS true AND request.partialTests.currentTotal IS 15 AND request.partialTests.thirdUserName IS 'Per'" - ); - } - - function test_including_partial_with_object() { - userObject = model("user").findOne(order = "firstName"); - request.wheelsTests.objectTestsPassed = false; - savecontent variable="result" { - WriteOutput(_controller.includePartial(userObject)); - } - assert("request.wheelsTests.objectTestsPassed IS true AND Trim(result) IS 'Chris'"); - } - - function teardown() { - application.wheels.viewPath = $$oldViewPath; - } - -} diff --git a/core/src/wheels/tests/view/csrf/authenticityTokenField.cfc b/core/src/wheels/tests/view/csrf/authenticityTokenField.cfc deleted file mode 100644 index 8cb70ae65b..0000000000 --- a/core/src/wheels/tests/view/csrf/authenticityTokenField.cfc +++ /dev/null @@ -1,14 +0,0 @@ -component extends="wheels.Test" { - - function setup() { - _controller = controller(name = "dummy"); - } - - function test_authenticityTokenField() { - local.token = CsrfGenerateToken(); - tag = _controller.authenticityTokenField(); - authenticityTokenField = ''; - assert("tag is '#authenticityTokenField#'"); - } - -} diff --git a/core/src/wheels/tests/view/csrf/csrfMetaTags.cfc b/core/src/wheels/tests/view/csrf/csrfMetaTags.cfc deleted file mode 100644 index 938d59a898..0000000000 --- a/core/src/wheels/tests/view/csrf/csrfMetaTags.cfc +++ /dev/null @@ -1,20 +0,0 @@ -component extends="wheels.Test" { - - function setup() { - _controller = controller(name = "dummy"); - } - - function test_contains_csrfparam_meta_tag() { - tags = _controller.csrfMetaTags(); - csrfParamTag = ''; - assert("tags contains '#csrfParamTag#'"); - } - - function test_contains_csrftoken_meta_tag() { - local.token = CsrfGenerateToken(); - tags = _controller.csrfMetaTags(); - csrfTokenTag = ''; - assert("tags contains '#csrfTokenTag#'"); - } - -} diff --git a/core/src/wheels/tests/view/dates/distanceOfTimeInWords.cfc b/core/src/wheels/tests/view/dates/distanceOfTimeInWords.cfc deleted file mode 100644 index 37e14282b2..0000000000 --- a/core/src/wheels/tests/view/dates/distanceOfTimeInWords.cfc +++ /dev/null @@ -1,182 +0,0 @@ -component extends="wheels.tests.Test" { - - function setup() { - _controller = controller(name = "dummy"); - f = "distanceOfTimeInWords"; - args = {}; - args.fromTime = Now(); - args.includeSeconds = true; - } - - function test_with_seconds_below_5_seconds() { - c = 5 - 1; - args.toTime = DateAdd('s', c, args.fromTime); - actual = _controller.distanceOfTimeInWords(argumentCollection = args); - expected = "less than 5 seconds"; - assert("actual eq expected"); - } - - function test_with_seconds_below_10_seconds() { - c = 10 - 1; - args.toTime = DateAdd('s', c, args.fromTime); - debug("_controller.distanceOfTimeInWords(argumentcollection=args)", false); - actual = _controller.distanceOfTimeInWords(argumentCollection = args); - expected = "less than 10 seconds"; - assert("actual eq expected"); - } - - function test_with_seconds_below_20_seconds() { - c = 20 - 1; - args.toTime = DateAdd('s', c, args.fromTime); - actual = _controller.distanceOfTimeInWords(argumentCollection = args); - expected = "less than 20 seconds"; - assert("actual eq expected"); - } - - function test_with_seconds_below_40_seconds() { - c = 40 - 1; - args.toTime = DateAdd('s', c, args.fromTime); - actual = _controller.distanceOfTimeInWords(argumentCollection = args); - expected = "half a minute"; - assert("actual eq expected"); - } - - function test_with_seconds_below_60_seconds() { - c = 60 - 1; - args.toTime = DateAdd('s', c, args.fromTime); - actual = _controller.distanceOfTimeInWords(argumentCollection = args); - expected = "less than a minute"; - assert("actual eq expected"); - } - - function test_with_seconds_above_60_seconds() { - c = 60 + 50; - args.toTime = DateAdd('s', c, args.fromTime); - actual = _controller.distanceOfTimeInWords(argumentCollection = args); - expected = "1 minute"; - assert("actual eq expected"); - } - - function test_without_seconds_below_60_seconds() { - args.includeSeconds = false; - c = 60 - 1; - args.toTime = DateAdd('s', c, args.fromTime); - actual = _controller.distanceOfTimeInWords(argumentCollection = args); - expected = "less than a minute"; - assert("actual eq expected"); - } - - function test_without_seconds_above_60_seconds() { - args.includeSeconds = false; - c = 60 + 50; - args.toTime = DateAdd('s', c, args.fromTime); - actual = _controller.distanceOfTimeInWords(argumentCollection = args); - expected = "1 minute"; - assert("actual eq expected"); - } - - function test_without_seconds_below_45_minutes() { - args.includeSeconds = false; - c = 45 - 1; - args.toTime = DateAdd('n', c, args.fromTime); - actual = _controller.distanceOfTimeInWords(argumentCollection = args); - expected = "#c# minutes"; - assert("actual eq expected"); - } - - function test_without_seconds_below_90_minutes() { - args.includeSeconds = false; - c = 90 - 1; - args.toTime = DateAdd('n', c, args.fromTime); - actual = _controller.distanceOfTimeInWords(argumentCollection = args); - expected = "about 1 hour"; - assert("actual eq expected"); - } - - function test_without_seconds_below_1440_minutes() { - args.includeSeconds = false; - c = 1440 - 1; - args.toTime = DateAdd('n', c, args.fromTime); - c = Ceiling(c / 60); - actual = _controller.distanceOfTimeInWords(argumentCollection = args); - expected = "about #c# hours"; - assert("actual eq expected"); - } - - function test_without_seconds_below_2880_minutes() { - args.includeSeconds = false; - c = 2880 - 1; - args.toTime = DateAdd('n', c, args.fromTime); - actual = _controller.distanceOfTimeInWords(argumentCollection = args); - expected = "1 day"; - assert("actual eq expected"); - } - - function test_without_seconds_below_43200_minutes() { - args.includeSeconds = false; - c = 43200 - 1; - args.toTime = DateAdd('n', c, args.fromTime); - c = Int(c / 1440); - actual = _controller.distanceOfTimeInWords(argumentCollection = args); - expected = "#c# days"; - assert("actual eq expected"); - } - - function test_without_seconds_below_86400_minutes() { - args.includeSeconds = false; - c = 86400 - 1; - args.toTime = DateAdd('n', c, args.fromTime); - actual = _controller.distanceOfTimeInWords(argumentCollection = args); - expected = "about 1 month"; - assert("actual eq expected"); - } - - function test_without_seconds_below_525600_minutes() { - args.includeSeconds = false; - c = 525600 - 1; - args.toTime = DateAdd('n', c, args.fromTime); - c = Int(c / 43200); - actual = _controller.distanceOfTimeInWords(argumentCollection = args); - expected = "#c# months"; - assert("actual eq expected"); - } - - function test_without_seconds_below_657000_minutes() { - args.includeSeconds = false; - c = 657000 - 1; - args.toTime = DateAdd('n', c, args.fromTime); - actual = _controller.distanceOfTimeInWords(argumentCollection = args); - expected = "about 1 year"; - assert("actual eq expected"); - } - - function test_without_seconds_below_919800_minutes() { - args.includeSeconds = false; - c = 919800 - 1; - args.toTime = DateAdd('n', c, args.fromTime); - actual = _controller.distanceOfTimeInWords(argumentCollection = args); - expected = "over 1 year"; - assert("actual eq expected"); - } - - function test_without_seconds_below_1051200_minutes() { - args.includeSeconds = false; - c = 1051200 - 1; - args.toTime = DateAdd('n', c, args.fromTime); - actual = _controller.distanceOfTimeInWords(argumentCollection = args); - expected = "almost 2 years"; - assert("actual eq expected"); - } - - function test_without_seconds_above_1051200_minutes() { - args.includeSeconds = false; - c = 1051200; - c = (c * 3) + 786; - args.toTime = DateAdd('n', c, args.fromTime); - c = Int(c / 525600); - actual = _controller.distanceOfTimeInWords(argumentCollection = args); - expected = "over #c# years"; - assert("actual eq expected"); - } - -} diff --git a/core/src/wheels/tests/view/dates/timeAgoInWords.cfc b/core/src/wheels/tests/view/dates/timeAgoInWords.cfc deleted file mode 100644 index 42f6cbf684..0000000000 --- a/core/src/wheels/tests/view/dates/timeAgoInWords.cfc +++ /dev/null @@ -1,155 +0,0 @@ -component extends="wheels.tests.Test" { - - function setup() { - _controller = controller(name = "dummy"); - now = Now(); - args = {}; - args.includeSeconds = true; - args.toTime = now; - } - - function test_with_seconds_below_5_seconds() { - c = 5 - 1; - args.fromTime = DateAdd('s', -c, now); - e = _controller.timeAgoInWords(argumentCollection = args); - r = "less than 5 seconds"; - assert("e eq r"); - } - - function test_with_seconds_below_10_seconds() { - c = 10 - 1; - args.fromTime = DateAdd('s', -c, now); - e = _controller.timeAgoInWords(argumentCollection = args); - r = "less than 10 seconds"; - assert("e eq r"); - } - - function test_with_seconds_below_20_seconds() { - c = 20 - 1; - args.fromTime = DateAdd('s', -c, now); - e = _controller.timeAgoInWords(argumentCollection = args); - r = "less than 20 seconds"; - assert("e eq r"); - } - - function test_with_seconds_below_40_seconds() { - c = 40 - 1; - args.fromTime = DateAdd('s', -c, now); - e = _controller.timeAgoInWords(argumentCollection = args); - r = "half a minute"; - assert("e eq r"); - } - - function test_with_seconds_below_60_seconds() { - c = 60 - 1; - args.fromTime = DateAdd('s', -c, now); - debug('args', false); - e = _controller.timeAgoInWords(argumentCollection = args); - r = "less than a minute"; - assert("e eq r"); - } - - function test_with_seconds_above_60_seconds() { - c = 60 + 50; - args.fromTime = DateAdd('s', -c, now); - e = _controller.timeAgoInWords(argumentCollection = args); - r = "1 minute"; - assert("e eq r"); - } - - function test_without_seconds_above_60_seconds() { - args.includeSeconds = false; - c = 60 + 50; - args.fromTime = DateAdd('s', -c, now); - e = _controller.timeAgoInWords(argumentCollection = args); - r = "1 minute"; - assert("e eq r"); - } - - function test_without_seconds_below_45_minutes() { - args.includeSeconds = false; - c = 45 - 1; - args.fromTime = DateAdd('n', -c, now); - e = _controller.timeAgoInWords(argumentCollection = args); - r = "#c# minutes"; - assert("e eq r"); - } - - function test_without_seconds_below_90_minutes() { - args.includeSeconds = false; - c = 90 - 1; - args.fromTime = DateAdd('n', -c, now); - e = _controller.timeAgoInWords(argumentCollection = args); - r = "about 1 hour"; - assert("e eq r"); - } - - function test_without_seconds_below_1440_minutes() { - args.includeSeconds = false; - c = 1440 - 1; - args.fromTime = DateAdd('n', -c, now); - c = Ceiling(c / 60); - e = _controller.timeAgoInWords(argumentCollection = args); - r = "about #c# hours"; - assert("e eq r"); - } - - function test_without_seconds_below_2880_minutes() { - args.includeSeconds = false; - c = 2880 - 1; - args.fromTime = DateAdd('n', -c, now); - e = _controller.timeAgoInWords(argumentCollection = args); - r = "1 day"; - assert("e eq r"); - } - - function test_without_seconds_below_43200_minutes() { - args.includeSeconds = false; - c = 43200 - 1; - args.fromTime = DateAdd('n', -c, now); - c = Int(c / 1440); - e = _controller.timeAgoInWords(argumentCollection = args); - r = "#c# days"; - assert("e eq r"); - } - - function test_without_seconds_below_86400_minutes() { - args.includeSeconds = false; - c = 86400 - 1; - args.fromTime = DateAdd('n', -c, now); - e = _controller.timeAgoInWords(argumentCollection = args); - r = "about 1 month"; - assert("e eq r"); - } - - function test_without_seconds_below_525600_minutes() { - args.includeSeconds = false; - c = 525600 - 1; - args.fromTime = DateAdd('n', -c, now); - c = Int(c / 43200); - e = _controller.timeAgoInWords(argumentCollection = args); - r = "#c# months"; - assert("e eq r"); - } - - function test_without_seconds_below_1051200_minutes() { - args.includeSeconds = false; - c = 1051200 - 1; - args.fromTime = DateAdd('n', -c, now); - e = _controller.timeAgoInWords(argumentCollection = args); - r = "almost 2 years"; - assert("e eq r"); - } - - function test_without_seconds_above_1051200_minutes() { - args.includeSeconds = false; - c = 1051200; - c = (c * 3) + 786; - args.fromTime = DateAdd('n', -c, now); - c = Int(c / 525600); - e = _controller.timeAgoInWords(argumentCollection = args); - r = "over #c# years"; - assert("e eq r"); - } - -} diff --git a/core/src/wheels/tests/view/dates/timeUntilInWords.cfc b/core/src/wheels/tests/view/dates/timeUntilInWords.cfc deleted file mode 100644 index 17beec7317..0000000000 --- a/core/src/wheels/tests/view/dates/timeUntilInWords.cfc +++ /dev/null @@ -1,155 +0,0 @@ -component extends="wheels.tests.Test" { - - function setup() { - _controller = controller(name = "dummy"); - now = Now(); - args = {}; - args.includeSeconds = true; - args.fromTime = now; - } - - function test_with_seconds_below_5_seconds() { - c = 5 - 1; - args.toTime = DateAdd('s', c, now); - e = _controller.timeUntilInWords(argumentCollection = args); - r = "less than 5 seconds"; - assert("e eq r"); - } - - function test_with_seconds_below_10_seconds() { - c = 10 - 1; - args.toTime = DateAdd('s', c, now); - e = _controller.timeUntilInWords(argumentCollection = args); - r = "less than 10 seconds"; - assert("e eq r"); - } - - function test_with_seconds_below_20_seconds() { - c = 20 - 1; - args.toTime = DateAdd('s', c, now); - e = _controller.timeUntilInWords(argumentCollection = args); - r = "less than 20 seconds"; - assert("e eq r"); - } - - function test_with_seconds_below_40_seconds() { - c = 40 - 1; - args.toTime = DateAdd('s', c, now); - e = _controller.timeUntilInWords(argumentCollection = args); - r = "half a minute"; - assert("e eq r"); - } - - function test_with_seconds_below_60_seconds() { - c = 60 - 1; - args.toTime = DateAdd('s', c, now); - debug('args', false); - e = _controller.timeUntilInWords(argumentCollection = args); - r = "less than a minute"; - assert("e eq r"); - } - - function test_with_seconds_above_60_seconds() { - c = 60 + 50; - args.toTime = DateAdd('s', c, now); - e = _controller.timeUntilInWords(argumentCollection = args); - r = "1 minute"; - assert("e eq r"); - } - - function test_without_seconds_above_60_seconds() { - args.includeSeconds = false; - c = 60 + 50; - args.toTime = DateAdd('s', c, now); - e = _controller.timeUntilInWords(argumentCollection = args); - r = "1 minute"; - assert("e eq r"); - } - - function test_without_seconds_below_45_minutes() { - args.includeSeconds = false; - c = 45 - 1; - args.toTime = DateAdd('n', c, now); - e = _controller.timeUntilInWords(argumentCollection = args); - r = "#c# minutes"; - assert("e eq r"); - } - - function test_without_seconds_below_90_minutes() { - args.includeSeconds = false; - c = 90 - 1; - args.toTime = DateAdd('n', c, now); - e = _controller.timeUntilInWords(argumentCollection = args); - r = "about 1 hour"; - assert("e eq r"); - } - - function test_without_seconds_below_1440_minutes() { - args.includeSeconds = false; - c = 1440 - 1; - args.toTime = DateAdd('n', c, now); - c = Ceiling(c / 60); - e = _controller.timeUntilInWords(argumentCollection = args); - r = "about #c# hours"; - assert("e eq r"); - } - - function test_without_seconds_below_2880_minutes() { - args.includeSeconds = false; - c = 2880 - 1; - args.toTime = DateAdd('n', c, now); - e = _controller.timeUntilInWords(argumentCollection = args); - r = "1 day"; - assert("e eq r"); - } - - function test_without_seconds_below_43200_minutes() { - args.includeSeconds = false; - c = 43200 - 1; - args.toTime = DateAdd('n', c, now); - c = Int(c / 1440); - e = _controller.timeUntilInWords(argumentCollection = args); - r = "#c# days"; - assert("e eq r"); - } - - function test_without_seconds_below_86400_minutes() { - args.includeSeconds = false; - c = 86400 - 1; - args.toTime = DateAdd('n', c, now); - e = _controller.timeUntilInWords(argumentCollection = args); - r = "about 1 month"; - assert("e eq r"); - } - - function test_without_seconds_below_525600_minutes() { - args.includeSeconds = false; - c = 525600 - 1; - args.toTime = DateAdd('n', c, now); - c = Int(c / 43200); - e = _controller.timeUntilInWords(argumentCollection = args); - r = "#c# months"; - assert("e eq r"); - } - - function test_without_seconds_below_1051200_minutes() { - args.includeSeconds = false; - c = 1051200 - 1; - args.toTime = DateAdd('n', c, now); - e = _controller.timeUntilInWords(argumentCollection = args); - r = "almost 2 years"; - assert("e eq r"); - } - - function test_without_seconds_above_1051200_minutes() { - args.includeSeconds = false; - c = 1051200; - c = (c * 3) + 786; - args.toTime = DateAdd('n', c, now); - c = Int(c / 525600); - e = _controller.timeUntilInWords(argumentCollection = args); - r = "over #c# years"; - assert("e eq r"); - } - -} diff --git a/core/src/wheels/tests/view/errors/errorMessageOn.cfc b/core/src/wheels/tests/view/errors/errorMessageOn.cfc deleted file mode 100644 index c53d2cfde8..0000000000 --- a/core/src/wheels/tests/view/errors/errorMessageOn.cfc +++ /dev/null @@ -1,20 +0,0 @@ -component extends="wheels.tests.Test" { - - function setup() { - _controller = controller(name = "ControllerWithModelErrors"); - args = {}; - args.objectName = "user"; - args.class = "errors-found"; - } - - function test_all_options_supplied() { - args.property = "firstname"; - args.prependText = "prepend "; - args.appendText = " append"; - args.wrapperElement = "div"; - e = _controller.errorMessageOn(argumentcollection = args); - r = '
prepend firstname error1 append
'; - assert("e eq r"); - } - -} diff --git a/core/src/wheels/tests/view/errors/errorMessagesFor.cfc b/core/src/wheels/tests/view/errors/errorMessagesFor.cfc deleted file mode 100644 index 39ba17b0d9..0000000000 --- a/core/src/wheels/tests/view/errors/errorMessagesFor.cfc +++ /dev/null @@ -1,24 +0,0 @@ -component extends="wheels.tests.Test" { - - function setup() { - _controller = controller(name = "ControllerWithModelErrors"); - args = {}; - args.objectName = "user"; - args.class = "errors-found"; - } - - function test_show_duplicate_errors() { - args.showDuplicates = true; - e = _controller.errorMessagesFor(argumentcollection = args); - r = '
  • firstname error1
  • firstname error2
  • firstname error2
'; - assert("e eq r"); - } - - function test_do_not_show_duplicate_errors() { - args.showDuplicates = false; - e = _controller.errorMessagesFor(argumentcollection = args); - r = '
  • firstname error1
  • firstname error2
'; - assert("e eq r"); - } - -} diff --git a/core/src/wheels/tests/view/forms/buttonTag.cfc b/core/src/wheels/tests/view/forms/buttonTag.cfc deleted file mode 100644 index b8cc77b823..0000000000 --- a/core/src/wheels/tests/view/forms/buttonTag.cfc +++ /dev/null @@ -1,43 +0,0 @@ -component extends="wheels.tests.Test" { - - function setup() { - _controller = controller(name = "dummy"); - imagePath = application.wheels.webPath & application.wheels.imagePath; - set(functionName = "buttonTag", encode = false); - } - - function teardown() { - set(functionName = "buttonTag", encode = true); - } - - function test_defaults() { - r = _controller.buttonTag(); - e = ''; - assert('e eq r'); - } - - function test_with_icon_as_html() { - r = _controller.buttonTag(content=" Edit", encode="attributes"); - e = ''; - assert('e eq r'); - } - - function test_with_image() { - r = _controller.buttonTag(image = "http://www.cfwheels.com/logo.jpg"); - e = ''; - assert('e eq r'); - } - - function test_with_disable() { - r = _controller.buttonTag(disable = "disable-value"); - e = ''; - assert('e eq r'); - } - - function test_append_prepend() { - r = _controller.buttonTag(append = "a", prepend = "p"); - e = 'pa'; - assert('e eq r'); - } - -} diff --git a/core/src/wheels/tests/view/forms/checkBoxTag.cfc b/core/src/wheels/tests/view/forms/checkBoxTag.cfc deleted file mode 100644 index d2c4817ca9..0000000000 --- a/core/src/wheels/tests/view/forms/checkBoxTag.cfc +++ /dev/null @@ -1,47 +0,0 @@ -component extends="wheels.tests.Test" { - - function setup() { - _controller = controller(name = "dummy"); - } - - function test_not_checked() { - e = _controller.checkBoxTag( - name = "subscribe", - value = "1", - label = "Subscribe to our newsletter", - checked = false - ); - r = ''; - assert('e eq r'); - } - - function test_checked() { - e = _controller.checkBoxTag( - name = "subscribe", - value = "1", - label = "Subscribe to our newsletter", - checked = true - ); - r = ''; - assert('e eq r'); - } - - function test_value_blank_and_not_checked() { - e = _controller.checkBoxTag(name = "gender", value = "", checked = false); - r = ''; - assert('e eq r'); - } - - function test_encoding_attributes() { - e = _controller.checkBoxTag( - name = "gender", - value = "", - checked = false, - uncheckedvalue = 1, - encode = "attributes" - ); - r = ''; - assert('e eq r'); - } - -} diff --git a/core/src/wheels/tests/view/forms/checkbox.cfc b/core/src/wheels/tests/view/forms/checkbox.cfc deleted file mode 100644 index ff351db6eb..0000000000 --- a/core/src/wheels/tests/view/forms/checkbox.cfc +++ /dev/null @@ -1,52 +0,0 @@ -component extends="wheels.tests.Test" { - - function setup() { - _controller = application.wirebox.getInstance("wheels.tests._assets.controllers.ControllerWithModel"); - args = {}; - args.objectName = "user"; - args.label = false; - set(functionName = "checkBox", encode = false); - } - - function teardown() { - set(functionName = "checkBox", encode = true); - } - - function test_checked_when_property_value_equals_checkedValue() { - args.property = "birthdaymonth"; - args.checkedvalue = "11"; - debug("_controller.checkBox(argumentcollection=args)", false); - e = ''; - r = _controller.checkBox(argumentcollection = args); - assert("e eq r"); - args.checkedvalue = "12"; - debug("_controller.checkBox(argumentcollection=args)", false); - e = ''; - r = _controller.checkBox(argumentcollection = args); - assert("e eq r"); - } - - function test_checkbox_is_unchecked_with_checkedvalue_when_property_value_is_1() { - args.property = "isactive"; - args.checkedvalue = "0"; - - actual = _controller.checkBox(argumentcollection = args); - assert("actual does not contain 'checked'"); - } - - function test_checkbox_is_checked_with_checkedvalue_when_property_value_is_1() { - args.property = "isactive"; - args.checkedvalue = "1"; - - actual = _controller.checkBox(argumentcollection = args); - assert("actual contains 'checked'"); - } - - function test_checkbox_is_checked_with_default_checkedvalue_when_property_value_is_1() { - args.property = "isactive"; - - actual = _controller.checkBox(argumentcollection = args); - assert("actual contains 'checked'"); - } - -} diff --git a/core/src/wheels/tests/view/forms/endFormTag.cfc b/core/src/wheels/tests/view/forms/endFormTag.cfc deleted file mode 100644 index 99f69c4d1c..0000000000 --- a/core/src/wheels/tests/view/forms/endFormTag.cfc +++ /dev/null @@ -1,11 +0,0 @@ -component extends="wheels.tests.Test" { - - function setup() { - _controller = controller(name = "dummy"); - } - - function test_x_endFormTag_valid() { - _controller.endFormTag(); - } - -} diff --git a/core/src/wheels/tests/view/forms/fileField.cfc b/core/src/wheels/tests/view/forms/fileField.cfc deleted file mode 100644 index 4fa0b85158..0000000000 --- a/core/src/wheels/tests/view/forms/fileField.cfc +++ /dev/null @@ -1,11 +0,0 @@ -component extends="wheels.tests.Test" { - - function setup() { - _controller = controller(name = "ControllerWithModel"); - } - - function test_x_fileField_valid() { - _controller.fileField(objectName = "user", property = "firstname"); - } - -} diff --git a/core/src/wheels/tests/view/forms/fileFieldTag.cfc b/core/src/wheels/tests/view/forms/fileFieldTag.cfc deleted file mode 100644 index 395e457063..0000000000 --- a/core/src/wheels/tests/view/forms/fileFieldTag.cfc +++ /dev/null @@ -1,11 +0,0 @@ -component extends="wheels.tests.Test" { - - function setup() { - _controller = controller(name = "dummy"); - } - - function test_x_fileFieldTag_valid() { - _controller.fileFieldTag(name = "photo"); - } - -} diff --git a/core/src/wheels/tests/view/forms/hiddenField.cfc b/core/src/wheels/tests/view/forms/hiddenField.cfc deleted file mode 100644 index d16e8ec913..0000000000 --- a/core/src/wheels/tests/view/forms/hiddenField.cfc +++ /dev/null @@ -1,11 +0,0 @@ -component extends="wheels.tests.Test" { - - function setup() { - _controller = controller(name = "ControllerWithModel"); - } - - function test_x_hiddenField_valid() { - _controller.hiddenField(objectName = "user", property = "firstname"); - } - -} diff --git a/core/src/wheels/tests/view/forms/hiddenFieldTag.cfc b/core/src/wheels/tests/view/forms/hiddenFieldTag.cfc deleted file mode 100644 index a994fca24e..0000000000 --- a/core/src/wheels/tests/view/forms/hiddenFieldTag.cfc +++ /dev/null @@ -1,11 +0,0 @@ -component extends="wheels.tests.Test" { - - function setup() { - _controller = controller(name = "dummy"); - } - - function test_x_hiddenFieldTag_valid() { - _controller.hiddenFieldTag(name = "userId", value = "tony"); - } - -} diff --git a/core/src/wheels/tests/view/forms/htmlAttributes.cfc b/core/src/wheels/tests/view/forms/htmlAttributes.cfc deleted file mode 100644 index 4f7c7fa2fa..0000000000 --- a/core/src/wheels/tests/view/forms/htmlAttributes.cfc +++ /dev/null @@ -1,77 +0,0 @@ -component extends="wheels.tests.Test" { - - function setup() { - _controller = controller(name = "ControllerWithModel"); - oldBooleanAttributes = application.wheels.booleanAttributes; - } - - function teardown() { - application.wheels.booleanAttributes = oldBooleanAttributes; - } - - function test_tag_with_disabled_and_readonly_set_to_true() { - textField = _controller.textFieldTag( - label = "First Name", - name = "firstName", - disabled = true, - readonly = true - ); - expected = ''; - assert('expected eq textField'); - } - - function test_tag_with_disabled_and_readonly_set_to_false() { - textField = _controller.textFieldTag( - label = "First Name", - name = "firstName", - disabled = false, - readonly = false - ); - expected = ''; - assert('expected eq textField'); - } - - function test_tag_with_disabled_and_readonly_set_to_string() { - textField = _controller.textFieldTag( - label = "First Name", - name = "firstName", - disabled = "cheese", - readonly = "crackers" - ); - expected = ''; - assert('expected eq textField'); - } - - function test_supported_attributes_should_be_boolean() { - result = _controller.textFieldTag(name = "num", checked = true, disabled = "true"); - correct = ''; - assert('result IS correct'); - } - - function test_non_supported_attributes_should_be_non_boolean() { - result = _controller.textFieldTag(name = "num", class = "true", value = "true"); - correct = ''; - assert('result IS correct'); - } - - function test_supported_attribute_should_be_omitted_when_false() { - result = _controller.textFieldTag(name = "num", readonly = false); - correct = ''; - assert('result IS correct'); - } - - function test_supported_attribute_should_be_non_boolean_when_setting_is_off() { - application.wheels.booleanAttributes = false; - result = _controller.textFieldTag(name = "num", checked = true); - correct = ''; - assert('result IS correct'); - } - - function test_non_supported_attribute_should_be_boolean_when_setting_is_on() { - application.wheels.booleanAttributes = true; - result = _controller.textFieldTag(name = "num", whatever = true); - correct = ''; - assert('result IS correct'); - } - -} diff --git a/core/src/wheels/tests/view/forms/label.cfc b/core/src/wheels/tests/view/forms/label.cfc deleted file mode 100644 index abc20a3029..0000000000 --- a/core/src/wheels/tests/view/forms/label.cfc +++ /dev/null @@ -1,119 +0,0 @@ -component extends="wheels.tests.Test" { - - function setup() { - _controller = controller(name = "ControllerWithModel"); - set(functionName = "checkBoxTag", encode = false); - set(functionName = "textField", encode = false); - set(functionName = "textFieldTag", encode = false); - } - - function teardown() { - set(functionName = "checkBoxTag", encode = true); - set(functionName = "textField", encode = true); - set(functionName = "textFieldTag", encode = true); - } - - /* plain helpers */ - - function test_label_to_the_left() { - actual = _controller.checkBoxTag(name = "the-name", label = "The Label:"); - expected = ''; - assert('actual eq expected'); - actual = _controller.checkBoxTag(name = "the-name", label = "The Label:", labelPlacement = "around"); - assert('actual eq expected'); - actual = _controller.checkBoxTag(name = "the-name", label = "The Label:", labelPlacement = "aroundLeft"); - assert('actual eq expected'); - } - - function test_label_to_the_right() { - actual = _controller.checkBoxTag(name = "the-name", label = "The Label", labelPlacement = "aroundRight"); - expected = ''; - assert('actual eq expected'); - } - - function test_custom_label_on_plain_helper() { - actual = _controller.checkBoxTag(name = "the-name", label = "The Label:"); - expected = ''; - assert('actual eq expected'); - } - - function test_custom_label_on_plain_helper_and_overriding_id() { - actual = _controller.checkBoxTag(name = "the-name", label = "The Label:", id = "the-id"); - expected = ''; - assert('actual eq expected'); - } - - function test_blank_label_on_plain_helper() { - actual = _controller.textFieldTag(name = "the-name", label = ""); - expected = ''; - assert('actual eq expected'); - } - - - /* object based helpers */ - - function test_custom_label_on_object_helper() { - actual = _controller.textField(objectName = "user", property = "username", label = "The Label:"); - expected = ''; - assert('actual eq expected'); - } - - function test_custom_label_on_object_helper_and_overriding_id() { - actual = _controller.textField( - objectName = "user", - property = "username", - label = "The Label:", - id = "the-id" - ); - expected = ''; - assert('actual eq expected'); - } - - function test_blank_label_on_object_helper() { - actual = _controller.textField(objectName = "user", property = "username", label = ""); - expected = ''; - assert('actual eq expected'); - } - - function test_automatic_label_on_object_helper_with_around_placement() { - actual = _controller.textField(objectName = "user", property = "username", labelPlacement = "around"); - expected = ''; - assert('actual eq expected'); - } - - function test_automatic_label_on_object_helper_with_before_placement() { - actual = _controller.textField(objectName = "user", property = "username", labelPlacement = "before"); - expected = ''; - assert('actual eq expected'); - } - - function test_automatic_label_on_object_helper_with_after_placement() { - actual = _controller.textField(objectName = "user", property = "username", labelPlacement = "after") - expected = '' - assert('actual eq expected'); - } - - function test_automatic_label_on_object_helper_with_non_persisted_property() { - actual = _controller.textField(objectName = "user", property = "virtual"); - expected = ''; - assert('actual eq expected'); - } - - function test_automatic_label_in_error_message() { - tag = Duplicate(model("tag").new()); - /* use a deep copy so as not to affect the cached model */ - tag.validatesPresenceOf(property = "name"); - tag.valid(); - errors = tag.errorsOn(property = "name"); - assert('ArrayLen(errors) eq 1 and errors[1].message is "Tag name can''t be empty"'); - } - - function test_automatic_label_in_error_message_with_non_persisted_property() { - tag = Duplicate(model("tag").new()); - tag.validatesPresenceOf(property = "virtual"); - tag.valid(); - errors = tag.errorsOn(property = "virtual"); - assert('ArrayLen(errors) eq 1 and errors[1].message is "Virtual property can''t be empty"'); - } - -} diff --git a/core/src/wheels/tests/view/forms/objectName.cfc b/core/src/wheels/tests/view/forms/objectName.cfc deleted file mode 100644 index a4e9e2cab0..0000000000 --- a/core/src/wheels/tests/view/forms/objectName.cfc +++ /dev/null @@ -1,38 +0,0 @@ -component extends="wheels.tests.Test" { - - function setup() { - _controller = controller(name = "ControllerWithNestedModel"); - } - - function test_$objectName_with_objectName() { - objectName = _controller.$objectName(objectName = "author"); - assert('objectName eq "author"'); - } - - function test_$objectName_with_objectName_as_struct() { - struct = {formField = "formValue"}; - objectName = _controller.$objectName(objectName = struct); - assert('IsStruct(objectName) eq true'); - } - - function test_$objectName_hasOne_association() { - objectName = _controller.$objectName(objectName = "author", association = "profile"); - assert('objectName eq "author[''profile'']"'); - } - - function test_$objectName_hasMany_association() { - objectName = _controller.$objectName(objectName = "author", association = "posts", position = "1"); - assert('objectName eq "author[''posts''][1]"'); - } - - function test_$objectName_hasMany_associations_nested() { - objectName = _controller.$objectName(objectName = "author", association = "posts,c_o_r_e_comments", position = "1,2"); - assert('objectName eq "author[''posts''][1][''c_o_r_e_comments''][2]"'); - } - - function test_$objectName_raises_error_without_correct_positions() { - e = raised('_controller.$objectName(objectName="author", association="posts,c_o_r_e_comments", position="1")'); - assert('e eq "Wheels.InvalidArgument"'); - } - -} diff --git a/core/src/wheels/tests/view/forms/passwordField.cfc b/core/src/wheels/tests/view/forms/passwordField.cfc deleted file mode 100644 index e017b19a3b..0000000000 --- a/core/src/wheels/tests/view/forms/passwordField.cfc +++ /dev/null @@ -1,11 +0,0 @@ -component extends="wheels.tests.Test" { - - function setup() { - _controller = controller(name = "ControllerWithModel"); - } - - function test_x_passwordField_valid() { - _controller.passwordField(objectName = "User", property = "password"); - } - -} diff --git a/core/src/wheels/tests/view/forms/passwordFieldTag.cfc b/core/src/wheels/tests/view/forms/passwordFieldTag.cfc deleted file mode 100644 index c2013dc7e5..0000000000 --- a/core/src/wheels/tests/view/forms/passwordFieldTag.cfc +++ /dev/null @@ -1,11 +0,0 @@ -component extends="wheels.tests.Test" { - - function setup() { - _controller = controller(name = "dummy"); - } - - function test_x_passwordFieldTag_valid() { - _controller.passwordFieldTag(name = "password"); - } - -} diff --git a/core/src/wheels/tests/view/forms/radioButton.cfc b/core/src/wheels/tests/view/forms/radioButton.cfc deleted file mode 100644 index 5c9e0be6fa..0000000000 --- a/core/src/wheels/tests/view/forms/radioButton.cfc +++ /dev/null @@ -1,16 +0,0 @@ -component extends="wheels.tests.Test" { - - function setup() { - _controller = controller(name = "ControllerWithModel"); - } - - function test_x_radioButton_valid() { - _controller.radioButton( - objectName = "user", - property = "gender", - tagValue = "m", - label = "Male" - ); - } - -} diff --git a/core/src/wheels/tests/view/forms/radioButtonTag.cfc b/core/src/wheels/tests/view/forms/radioButtonTag.cfc deleted file mode 100644 index e853373fec..0000000000 --- a/core/src/wheels/tests/view/forms/radioButtonTag.cfc +++ /dev/null @@ -1,42 +0,0 @@ -component extends="wheels.tests.Test" { - - function setup() { - _controller = controller(name = "dummy"); - } - - function test_radioButtonTag_value_not_blank() { - e = _controller.radioButtonTag( - name = "gender", - value = "m", - label = "Male", - checked = true - ); - r = ''; - debug(expression = 'e', display = false, format = "text"); - assert('e eq r'); - } - - function test_radioButtonTag_value_blank() { - e = _controller.radioButtonTag( - name = "gender", - value = "", - label = "Male", - checked = true - ); - r = ''; - debug(expression = 'htmleditformat(e)', display = false, format = "text"); - assert('e eq r'); - } - - function test_radioButtonTag_value_blank_and_not_checked() { - e = _controller.radioButtonTag( - name = "gender", - value = "", - label = "Male", - checked = false - ); - r = ''; - assert('e eq r'); - } - -} diff --git a/core/src/wheels/tests/view/forms/select.cfc b/core/src/wheels/tests/view/forms/select.cfc deleted file mode 100644 index 7e41fe3cbd..0000000000 --- a/core/src/wheels/tests/view/forms/select.cfc +++ /dev/null @@ -1,145 +0,0 @@ -component extends="wheels.tests.Test" { - - function setup() { - _controller = controller(name = "ControllerWithModel"); - user = model("user"); - set(functionName = "select", encode = false); - } - - function teardown() { - set(functionName = "select", encode = true); - } - - function test_with_list_as_options() { - options = "Opt1,Opt2"; - r = _controller.select( - objectName = "user", - property = "firstname", - options = options, - label = false - ); - e = ''; - assert('e eq r'); - } - - function test_with_array_as_options() { - options = ArrayNew(1); - options[1] = "Opt1"; - options[2] = "Opt2"; - options[3] = "Opt3"; - r = _controller.select( - objectName = "user", - property = "firstname", - options = options, - label = false - ); - e = ''; - assert('e eq r'); - } - - function test_with_struct_as_options() { - options = {}; - options.x = "xVal"; - options.y = "yVal"; - r = _controller.select( - objectName = "user", - property = "firstname", - options = options, - label = false - ); - e = ''; - assert('e eq r'); - } - - function test_setting_text_field() { - users = user.findAll(returnAs = "objects", order = "id"); - r = _controller.select( - objectName = "user", - property = "firstname", - options = users, - valueField = "id", - textField = "firstName", - label = false - ); - e = ''; - assert('e eq r'); - } - - function test_first_non_numeric_property_default_text_field_on_query() { - users = user.findAll(returnAs = "query", order = "id"); - r = _controller.select( - objectName = "user", - property = "firstname", - options = users, - label = false - ); - e = ''; - assert('e eq r'); - } - - function test_first_non_numeric_property_default_text_field_on_objects() { - users = user.findAll(returnAs = "objects", order = "id"); - r = _controller.select( - objectName = "user", - property = "firstname", - options = users, - label = false - ); - e = ''; - assert('e eq r'); - } - - function test_with_array_of_structs_as_options() { - options = []; - options[1] = {}; - options[1].tp = "tony petruzzi"; - options[2] = {}; - options[2].pd = "per djurner"; - r = _controller.select( - objectName = "user", - property = "firstname", - options = options, - label = false - ); - e = ''; - assert('e eq r'); - } - - function test_with_array_of_structs_as_options_2() { - options = []; - options[1] = {}; - options[1] = {value = "petruzzi", name = "tony"}; - options[2] = {}; - options[2] = {value = "djurner", name = "per"}; - r = _controller.select( - objectName = "user", - property = "firstname", - options = options, - valueField = "value", - textField = "name", - label = false - ); - e = ''; - assert('e eq r'); - } - - function test_htmlsafe() { - badValue = ""; - badName = ""; - goodValue = EncodeForHTMLAttribute(badValue); - goodName = EncodeForHTML(badName); - options = []; - options[1] = {value = "#badValue#", name = "#badName#"}; - set(functionName = "select", encode = true); - r = _controller.select( - objectName = "user", - property = "firstname", - options = options, - valueField = "value", - textField = "name", - label = false - ); - assert('r CONTAINS goodValue AND r CONTAINS goodName'); - } - -} diff --git a/core/src/wheels/tests/view/forms/selectTag.cfc b/core/src/wheels/tests/view/forms/selectTag.cfc deleted file mode 100644 index ddf9b45dea..0000000000 --- a/core/src/wheels/tests/view/forms/selectTag.cfc +++ /dev/null @@ -1,136 +0,0 @@ -component extends="wheels.tests.Test" { - - function setup() { - _controller = controller(name = "dummy"); - options.simplevalues = ''; - options.complexvalues = ''; - options.single_key_struct = ''; - options.single_column_query = ''; - options.empty_query = ''; - } - - function test_encode_for_html_and_encode_for_html_attribute() { - result = controller("dummy").selectTag(name = "x", options = ",<2>,3", selected = "<2>"); - expected = ''; - assert("result eq expected"); - } - - function test_list_for_option_values() { - args.name = "testselect"; - args.options = "first,second,third"; - debug("_controller.selectTag(argumentcollection=args)", false); - r = _controller.selectTag(argumentcollection = args); - assert('options.simplevalues eq r'); - } - - function test_struct_for_option_values() { - args.name = "testselect"; - args.options = {1 = "first", 2 = "second", 3 = "third"}; - debug("_controller.selectTag(argumentcollection=args)", false); - r = _controller.selectTag(argumentcollection = args); - assert('options.complexvalues eq r'); - } - - function test_array_of_structs_for_option_values_single_key() { - args.name = "testselect"; - args.options = []; - temp = {firstKeyName = "first Value"}; - ArrayAppend(args.options, temp); - temp = {secondKeyName = "second Value"}; - ArrayAppend(args.options, temp); - debug("_controller.selectTag(argumentcollection=args)", false); - r = _controller.selectTag(argumentcollection = args); - assert('options.single_key_struct eq r'); - } - - function test_one_dimensional_array_for_option_values() { - args.name = "testselect"; - args.options = ["first", "second", "third"]; - debug("_controller.selectTag(argumentcollection=args)", false); - r = _controller.selectTag(argumentcollection = args); - assert('options.simplevalues eq r'); - } - - function test_two_dimensional_array_for_option_values() { - args.name = "testselect"; - first = [1, "first"]; - second = [2, "second"]; - third = [3, "third"]; - args.options = [first, second, third]; - debug("_controller.selectTag(argumentcollection=args)", false); - r = _controller.selectTag(argumentcollection = args); - assert('options.complexvalues eq r'); - } - - function test_three_dimensional_array_for_option_values() { - args.name = "testselect"; - first = [1, "first", "a"]; - second = [2, "second", "b"]; - third = [3, "third", "c"]; - args.options = [first, second, third]; - debug("_controller.selectTag(argumentcollection=args)", false); - r = _controller.selectTag(argumentcollection = args); - assert('options.complexvalues eq r'); - } - - function test_query_for_option_values() { - q = QueryNew(""); - id = [1, 2, 3]; - name = ["first", "second", "third"]; - QueryAddColumn(q, "id", id); - QueryAddColumn(q, "name", name); - args.name = "testselect"; - args.options = q; - debug("_controller.selectTag(argumentcollection=args)", false); - r = _controller.selectTag(argumentcollection = args); - assert('options.complexvalues eq r'); - } - - function test_one_column_query_for_options() { - q = QueryNew(""); - id = ["first", "second", "third"]; - QueryAddColumn(q, "id", id); - args.name = "testselect"; - args.options = q; - debug("_controller.selectTag(argumentcollection=args)", false); - r = _controller.selectTag(argumentcollection = args); - assert('options.single_column_query eq r'); - } - - function test_query_with_no_records_for_option_values_() { - q = QueryNew(""); - id = []; - name = []; - QueryAddColumn(q, "id", id); - QueryAddColumn(q, "name", name); - args.name = "testselect"; - args.options = q; - debug("_controller.selectTag(argumentcollection=args)", false); - r = _controller.selectTag(argumentcollection = args); - assert('options.empty_query eq r'); - } - - function test_query_with_no_records_or_columns_for_option_values_() { - q = QueryNew(""); - args.name = "testselect"; - args.options = q; - debug("_controller.selectTag(argumentcollection=args)", false); - r = _controller.selectTag(argumentcollection = args); - assert('options.empty_query eq r'); - } - - function test_array_of_structs_for_option_values() { - args.name = "testselect"; - args.options = []; - temp = {value = "1", text = "first"}; - ArrayAppend(args.options, temp); - temp = {value = "2", text = "second"}; - ArrayAppend(args.options, temp); - temp = {value = "3", text = "third"}; - ArrayAppend(args.options, temp); - debug("_controller.selectTag(argumentcollection=args)", false); - r = _controller.selectTag(argumentcollection = args); - assert('options.complexvalues eq r'); - } - -} diff --git a/core/src/wheels/tests/view/forms/startFormTag.cfc b/core/src/wheels/tests/view/forms/startFormTag.cfc deleted file mode 100644 index 56e496b415..0000000000 --- a/core/src/wheels/tests/view/forms/startFormTag.cfc +++ /dev/null @@ -1,121 +0,0 @@ -component extends="wheels.tests.Test" { - - function setup() { - _controller = controller(name = "dummy"); - args = {}; - args.host = ""; - args.method = "post"; - args.multipart = false; - args.onlypath = true; - args.port = 0; - args.protocol = ""; - args.controller = "testcontroller"; - set(functionName = "startFormTag", encode = false); - request.$wheelsProtectedFromForgery = true; - } - - function teardown() { - set(functionName = "startFormTag", encode = true); - request.$wheelsProtectedFromForgery = false; - } - - function test_no_csrf_when_not_enabled() { - if (StructKeyExists(request, "$wheelsProtectedFromForgery")) { - local.$wheelsProtectedFromForgery = request.$wheelsProtectedFromForgery; - StructDelete(request, "$wheelsProtectedFromForgery"); - } - StructDelete(args, "controller"); - argsction = _controller.urlfor(argumentCollection = args); - e = '
'; - r = _controller.startFormTag(argumentcollection = args); - assert('e eq r'); - if (StructKeyExists(local, "$wheelsProtectedFromForgery")) { - request.$wheelsProtectedFromForgery = local.$wheelsProtectedFromForgery; - } - } - - function test_no_controller_or_action_or_route_should_point_to_current_page() { - StructDelete(args, "controller"); - argsction = _controller.urlfor(argumentCollection = args); - e = '' & _controller.authenticityTokenField(); - r = _controller.startFormTag(argumentcollection = args); - assert('e eq r'); - } - - function test_with_controller() { - argsction = _controller.urlfor(argumentCollection = args); - e = '' & _controller.authenticityTokenField(); - r = _controller.startFormTag(argumentcollection = args); - assert("e eq r", "testing this out"); - } - - function test_with_get_method() { - args.method = "get"; - argsction = _controller.urlfor(argumentCollection = args); - e = ''; - r = _controller.startFormTag(argumentcollection = args); - assert("e eq r"); - } - - // TODO: Change `method` back to `post` after integrating ColdRoute. Then also test for inclusion of `_method` - // hidden field. - function test_with_put_method() { - args.method = "put"; - argsction = _controller.urlfor(argumentCollection = args); - e = '' & _controller.authenticityTokenField(); - r = _controller.startFormTag(argumentcollection = args); - assert("r is e"); - } - - // TODO: Change `method` back to `post` after integrating ColdRoute. Then also test for inclusion of `_method` - // hidden field. - function test_with_patch_method() { - args.method = "patch"; - argsction = _controller.urlfor(argumentCollection = args); - e = '' & _controller.authenticityTokenField(); - r = _controller.startFormTag(argumentcollection = args); - assert("r is e"); - } - - // TODO: Change `method` back to `post` after integrating ColdRoute. Then also test for inclusion of `_method` - // hidden field. - function test_with_delete_method() { - args.method = "delete"; - argsction = _controller.urlfor(argumentCollection = args); - e = '' & _controller.authenticityTokenField(); - r = _controller.startFormTag(argumentcollection = args); - assert("r is e"); - } - - function test_with_multipart() { - args.multipart = "true"; - argsction = _controller.urlfor(argumentCollection = args); - e = _controller.startFormTag(argumentcollection = args); - r = '' & _controller.authenticityTokenField(); - assert("e eq r"); - } - - function test_with_root_route() { - args.route = "root"; - argsction = _controller.urlfor(argumentCollection = args); - e = '' & _controller.authenticityTokenField(); - r = _controller.startFormTag(argumentcollection = args); - assert("e eq r"); - } - - function test_external_link() { - args.multipart = true; - argsction = _controller.urlfor(argumentCollection = args); - e = '' & _controller.authenticityTokenField(); - r = _controller.startFormTag(argumentcollection = args); - assert("e eq r"); - } - - function test_with_controller_and_action() { - argsction = _controller.urlfor(argumentCollection = args, action = "test"); - e = '' & _controller.authenticityTokenField(); - r = _controller.startFormTag(argumentcollection = args, action = "test"); - assert("e eq r"); - } - -} diff --git a/core/src/wheels/tests/view/forms/submitTag.cfc b/core/src/wheels/tests/view/forms/submitTag.cfc deleted file mode 100644 index 7d74b46d53..0000000000 --- a/core/src/wheels/tests/view/forms/submitTag.cfc +++ /dev/null @@ -1,30 +0,0 @@ -component extends="wheels.tests.Test" { - - function setup() { - _controller = controller(name = "ControllerWithModel"); - set(functionName = "submitTag", encode = false); - } - - function teardown() { - set(functionName = "submitTag", encode = true); - } - - function test_defaults() { - actual = _controller.submitTag(); - expected = ''; - assert('actual eq expected'); - } - - function test_submittag_arguments() { - actual = _controller.submitTag(disable = "disable-value"); - expected = ''; - assert('actual eq expected'); - } - - function test_append_prepend() { - actual = _controller.submitTag(append = "a", prepend = "p"); - expected = 'pa'; - assert('actual eq expected'); - } - -} diff --git a/core/src/wheels/tests/view/forms/textArea.cfc b/core/src/wheels/tests/view/forms/textArea.cfc deleted file mode 100644 index bcab5fdf65..0000000000 --- a/core/src/wheels/tests/view/forms/textArea.cfc +++ /dev/null @@ -1,11 +0,0 @@ -component extends="wheels.tests.Test" { - - function setup() { - _controller = controller(name = "ControllerWithModel"); - } - - function test_x_textArea_valid() { - _controller.textArea(objectName = "user", property = "firstname"); - } - -} diff --git a/core/src/wheels/tests/view/forms/textAreaTag.cfc b/core/src/wheels/tests/view/forms/textAreaTag.cfc deleted file mode 100644 index c7b4941b13..0000000000 --- a/core/src/wheels/tests/view/forms/textAreaTag.cfc +++ /dev/null @@ -1,39 +0,0 @@ -component extends="wheels.tests.Test" { - - function test_encoding_nothing() { - result = controller(name = "dummy").textAreaTag( - name = "x", - class = "x x", - content = "Per's Test", - encode = false, - label = "Per's Label" - ); - expected = ""; - assert("result eq expected"); - } - - function test_encoding_everything() { - result = controller(name = "dummy").textAreaTag( - name = "x", - class = "x x", - content = "Per's Test", - encode = true, - label = "Per's Label" - ); - expected = ""; - assert("result eq expected"); - } - - function test_encoding_only_attributes() { - result = controller(name = "dummy").textAreaTag( - name = "x", - class = "x x", - content = "Per's Test", - encode = "attributes", - label = "Per's Label" - ); - expected = ""; - assert("result eq expected"); - } - -} diff --git a/core/src/wheels/tests/view/forms/textField.cfc b/core/src/wheels/tests/view/forms/textField.cfc deleted file mode 100644 index 95ca12ce7a..0000000000 --- a/core/src/wheels/tests/view/forms/textField.cfc +++ /dev/null @@ -1,34 +0,0 @@ -component extends="wheels.tests.Test" { - - function test_automatic_label_for_id_property() { - _controller = controller(name = "Galleries"); - textField = _controller.textField(objectName = "gallery", property = "id", labelPlacement = "before"); - assert('textField contains ""'); - } - - function test_automatic_label_ending_with_id() { - _controller = controller(name = "Galleries"); - textField = _controller.textField(objectName = "gallery", property = "userId", labelPlacement = "before"); - assert('textField contains ""'); - } - - function test_override_value() { - _controller = controller(name = "ControllerWithModel"); - textField = _controller.textField( - label = "First Name", - objectName = "user", - property = "firstName", - value = "override" - ); - foundValue = YesNoFormat(FindNoCase('value="override"', textField)); - assert('foundValue eq true'); - } - - function test_maxlength_textfield_valid() { - _controller = controller(name = "ControllerWithModel"); - textField = _controller.textField(label = "First Name", objectName = "user", property = "firstName"); - foundMaxLength = YesNoFormat(FindNoCase('maxlength="50"', textField)); - assert('foundMaxLength eq true'); - } - -} diff --git a/core/src/wheels/tests/view/forms/textFieldTag.cfc b/core/src/wheels/tests/view/forms/textFieldTag.cfc deleted file mode 100644 index bf722d5a60..0000000000 --- a/core/src/wheels/tests/view/forms/textFieldTag.cfc +++ /dev/null @@ -1,66 +0,0 @@ -component extends="wheels.tests.Test" { - - function setup() { - _controller = controller(name = "dummy"); - } - - function test_x_textFieldTag_valid() { - _controller.textFieldTag(name = "someName"); - } - - function test_custom_textfieldTag_type() { - textField = _controller.textFieldTag(name = "search", label = "Search me", type = "search"); - foundCustomType = YesNoFormat(FindNoCase('type="search"', textField)); - assert('foundCustomType eq true'); - } - - function test_data_attribute_underscore_conversion() { - result = _controller.textFieldTag( - name = "num", - type = "range", - min = 5, - max = 10, - data_dom_cache = "cache", - data_role = "button" - ); - correct = ''; - assert('result IS correct'); - } - - function test_data_attribute_camelcase_conversion_when_not_in_quotes() { - result = _controller.textFieldTag( - name = "num", - type = "range", - min = 5, - max = 10, - dataDomCache = "cache", - dataRole = "button" - ); - correct = ''; - assert('result IS correct'); - } - - function test_data_attribute_camelcase_conversion() { - args = {}; - args["dataDomCache"] = "cache"; - args["dataRole"] = "button"; - result = _controller.textFieldTag( - name = "num", - type = "range", - min = 5, - max = 10, - argumentCollection = args - ); - correct = ''; - assert('result IS correct'); - } - - function test_data_attribute_set_to_true() { - args = {}; - args["data-dom-cache"] = "true"; - result = _controller.textFieldTag(name = "num", argumentCollection = args); - correct = ''; - assert('result IS correct'); - } - -} diff --git a/core/src/wheels/tests/view/forms/yearSelect.cfc b/core/src/wheels/tests/view/forms/yearSelect.cfc deleted file mode 100644 index f212ca1c20..0000000000 --- a/core/src/wheels/tests/view/forms/yearSelect.cfc +++ /dev/null @@ -1,65 +0,0 @@ -component extends="wheels.tests.Test" { - - function setup() { - _controller = application.wirebox.getInstance("wheels.tests._assets.controllers.ControllerWithModel"); - args = {}; - args.objectName = "user"; - args.property = "birthday"; - args.includeblank = false; - args.order = "year"; - args.label = false; - _controller.changeBirthday = changeBirthday; - set(functionName = "dateSelect", encode = false); - } - - function teardown() { - set(functionName = "dateSelect", encode = true); - } - - function test_startyear_lt_endyear_value_lt_startyear() { - args.startyear = "1980"; - args.endyear = "1990"; - r = _controller.dateSelect(argumentCollection = args); - debug('r', false); - e = ''; - assert('e eq r'); - } - - function test_startyear_lt_endyear_value_gt_startyear() { - _controller.changeBirthday(CreateDate(1995, 11, 1)); - args.startyear = "1980"; - args.endyear = "1990"; - r = _controller.dateSelect(argumentCollection = args); - debug('r', false); - e = ''; - assert('e eq r'); - } - - function test_startyear_gt_endyear_value_lt_endyear() { - args.startyear = "1990"; - args.endyear = "1980"; - r = _controller.dateSelect(argumentCollection = args); - debug('r', false); - e = ''; - assert('e eq r'); - } - - function test_startyear_gt_endyear_value_gt_endyear() { - _controller.changeBirthday(CreateDate(1995, 11, 1)); - args.startyear = "1990"; - args.endyear = "1980"; - r = _controller.dateSelect(argumentCollection = args); - debug('r', false); - e = ''; - assert('e eq r'); - } - - /** - * HELPERS - */ - - function changeBirthday(required any value) { - user.birthday = arguments.value; - } - -} diff --git a/core/src/wheels/tests/view/formsdate/yearMonthHourMinuteSecondSelectTagContent.cfc b/core/src/wheels/tests/view/formsdate/yearMonthHourMinuteSecondSelectTagContent.cfc deleted file mode 100644 index 16a689ada5..0000000000 --- a/core/src/wheels/tests/view/formsdate/yearMonthHourMinuteSecondSelectTagContent.cfc +++ /dev/null @@ -1,56 +0,0 @@ -component extends="wheels.tests.Test" { - - function setup() { - pkg.controller = controller("dummy"); - result = ""; - results = {}; - } - - function testBasic() { - result = pkg.controller.$yearMonthHourMinuteSecondSelectTagContent( - counter = 5, - value = "", - $optionNames = "", - $type = "" - ); - assert("result IS ''"); - } - - function testSelected() { - result = pkg.controller.$yearMonthHourMinuteSecondSelectTagContent( - counter = 3, - value = 3, - $optionNames = "", - $type = "" - ); - assert("result IS ''"); - } - - function testFormatting() { - result = pkg.controller.$yearMonthHourMinuteSecondSelectTagContent( - counter = 1, - value = "", - $optionNames = "", - $type = "minute" - ); - assert("result IS ''"); - result = pkg.controller.$yearMonthHourMinuteSecondSelectTagContent( - counter = 59, - value = "", - $optionNames = "", - $type = "second" - ); - assert("result IS ''"); - } - - function testOptionName() { - result = pkg.controller.$yearMonthHourMinuteSecondSelectTagContent( - counter = 1, - value = "", - $optionNames = "someName", - $type = "" - ); - assert("result IS ''"); - } - -} diff --git a/core/src/wheels/tests/view/formsdateobject/DateSelect.cfc b/core/src/wheels/tests/view/formsdateobject/DateSelect.cfc deleted file mode 100644 index c600747381..0000000000 --- a/core/src/wheels/tests/view/formsdateobject/DateSelect.cfc +++ /dev/null @@ -1,62 +0,0 @@ -component extends="wheels.tests.Test" { - - function setup() { - _controller = controller(name = "ControllerWithModel"); - args = {}; - args.objectName = "user"; - args.label = false; - set(functionName = "dateSelect", encode = false); - } - - function teardown() { - set(functionName = "dateSelect", encode = true); - } - - function test_dateselect_parsing_and_passed_month() { - args.property = "birthday"; - args.order = "month"; - debug("_controller.dateSelect(argumentcollection=args)", false); - e = dateSelect_month_str(args.property); - r = _controller.dateSelect(argumentcollection = args); - assert("e eq r"); - args.property = "birthdaymonth"; - e = dateSelect_month_str(args.property); - r = _controller.dateSelect(argumentcollection = args); - assert("e eq r"); - } - - function test_dateselect_parsing_and_passed_year() { - args.property = "birthday"; - args.order = "year"; - args.startyear = "1973"; - args.endyear = "1976"; - debug("_controller.dateSelect(argumentcollection=args)", false); - e = dateSelect_year_str(args.property); - r = _controller.dateSelect(argumentcollection = args); - assert("e eq r"); - } - - function test_dateselect_year_is_less_than_startyear() { - args.property = "birthday"; - args.order = "year"; - args.startyear = "1976"; - args.endyear = "1980"; - debug("_controller.dateSelect(argumentcollection=args)", false); - e = ''; - r = _controller.dateSelect(argumentcollection = args); - assert("e eq r"); - } - - /** - * HELPERS - */ - - function dateSelect_month_str(required string property) { - return ''; - } - - function dateSelect_year_str(required string property) { - return ''; - } - -} diff --git a/core/src/wheels/tests/view/formsdateobject/DateTimeSelect.cfc b/core/src/wheels/tests/view/formsdateobject/DateTimeSelect.cfc deleted file mode 100644 index dd52dbb0fc..0000000000 --- a/core/src/wheels/tests/view/formsdateobject/DateTimeSelect.cfc +++ /dev/null @@ -1,72 +0,0 @@ -component extends="wheels.tests.Test" { - - function setup() { - _controller = controller(name = "ControllerWithModel"); - args = {}; - args.objectName = "user"; - args.label = false; - selected = {}; - selected.month = ''; - selected.day = ''; - selected.year = ''; - set(functionName = "dateTimeSelect", encode = false); - } - - function teardown() { - set(functionName = "dateTimeSelect", encode = true); - } - - function testSplittingLabels() { - result = _controller.dateTimeSelect( - objectName = "user", - property = "birthday", - label = "labelMonth,labelDay,labelYear,labelHour,labelMinute,labelSecond" - ); - assert("result Contains 'labelDay' AND result Contains 'labelSecond'"); - } - - function test_datetimeselect() { - args.property = "birthday"; - r = _controller.dateTimeSelect(argumentcollection = args); - assert("r contains selected.month"); - assert("r contains selected.day"); - assert("r contains selected.year"); - } - - function test_datetimeselect_not_combined() { - args.property = "birthday"; - args.combine = "false"; - r = _controller.dateTimeSelect(argumentcollection = args); - assert("r contains selected.month"); - assert("r contains selected.day"); - assert("r contains selected.year"); - } - - function test_splitting_lable_classes() { - labelClass = "month,day,year"; - r = _controller.dateTimeSelect( - objectName = "user", - property = "birthday", - label = "labelMonth,labelDay,labelYear", - labelClass = "#labelClass#" - ); - for (i in ListToArray(labelClass)) { - e = 'label class="#i#"'; - assert('r Contains e'); - }; - } - - function test_ampm_select_coming_is_displayed_twice() { - r = _controller.dateTimeSelect( - objectName = 'user', - property = 'birthday', - dateOrder = 'month,day,year', - monthDisplay = 'abbreviations', - twelveHour = 'true', - label = '' - ); - a = ReMatchNoCase("user\[birthday\]\(\$ampm\)", r); - assert('ArrayLen(a) eq 1'); - } - -} diff --git a/core/src/wheels/tests/view/formsdateobject/TimeSelect.cfc b/core/src/wheels/tests/view/formsdateobject/TimeSelect.cfc deleted file mode 100644 index fbb6c2abb3..0000000000 --- a/core/src/wheels/tests/view/formsdateobject/TimeSelect.cfc +++ /dev/null @@ -1,9 +0,0 @@ -component extends="wheels.tests.Test" { - - _controller = controller(name = "dummy"); - - function _test_x() { - assert("1 IS 1"); - } - -} diff --git a/core/src/wheels/tests/view/formsdateplain/DateSelectTags.cfc b/core/src/wheels/tests/view/formsdateplain/DateSelectTags.cfc deleted file mode 100644 index 2f34f8b566..0000000000 --- a/core/src/wheels/tests/view/formsdateplain/DateSelectTags.cfc +++ /dev/null @@ -1,45 +0,0 @@ -component extends="wheels.tests.Test" { - - function setup() { - _controller = controller(name = "dummy"); - args = {}; - args.label = false; - set(functionName = "dateSelectTags", encode = false); - } - - function teardown() { - set(functionName = "dateSelectTags", encode = true); - } - - function test_multiple_labels() { - args.name = "today"; - args.startyear = "1973"; - args.endyear = "1973"; - args.selected = "09/14/1973"; - args.label = "The Month:,The Day:,The Year:"; - e = ' '; - r = _controller.dateSelectTags(argumentcollection = args); - assert("e eq r"); - } - - function test_startyear_is_greater_than_endyear() { - args.name = "today"; - args.startyear = "2000"; - args.endyear = "1990"; - args.order = "year"; - e = ''; - r = _controller.dateSelectTags(argumentcollection = args); - assert("e eq r"); - } - - function test_endyear_is_greater_than_startyear() { - args.name = "today"; - args.startyear = "1990"; - args.endyear = "2000"; - args.order = "year"; - e = ''; - r = _controller.dateSelectTags(argumentcollection = args); - assert("e eq r"); - } - -} diff --git a/core/src/wheels/tests/view/formsdateplain/DateTimeSelectTags.cfc b/core/src/wheels/tests/view/formsdateplain/DateTimeSelectTags.cfc deleted file mode 100644 index 7bb783e1a0..0000000000 --- a/core/src/wheels/tests/view/formsdateplain/DateTimeSelectTags.cfc +++ /dev/null @@ -1,96 +0,0 @@ -component extends="wheels.tests.Test" { - - function setup() { - pkg.controller = controller("dummy"); - result = ""; - results = {}; - _controller = controller(name = "dummy"); - args = {}; - args.label = false; - } - - function testNoLabels() { - result = pkg.controller.dateTimeSelectTags(name = "theName", label = false); - assert("result Does Not Contain 'label'"); - } - - function testSameLabels() { - str = pkg.controller.dateTimeSelectTags(name = "theName", label = "lblText"); - sub = "lblText"; - result = (Len(str) - Len(Replace(str, sub, "", "all"))) / Len(sub); - assert("result IS 6"); - } - - function testSplittingLabels() { - result = pkg.controller.dateTimeSelectTags( - name = "theName", - label = "labelMonth,labelDay,labelYear,labelHour,labelMinute,labelSecond" - ); - assert("result Contains 'labelDay' AND result Contains 'labelSecond'"); - } - - function test_dateTimeSelectTags_blank_included_boolean() { - args.name = "dateselector"; - args.includeBlank = "true"; - args.selected = ""; - args.startyear = "2000"; - args.endyear = "1990"; - r = _controller.dateTimeSelectTags(argumentcollection = args); - e = ''; - assert("r contains e"); - args.selected = "01/02/2000"; - r = _controller.dateTimeSelectTags(argumentcollection = args); - debug('r', false); - e1 = ''; - e2 = ''; - e3 = ''; - assert("r contains e1 && r contains e2 && r contains e3"); - } - - function test_dateTimeSelectTags_blank_included_string() { - args.name = "dateselector"; - args.includeBlank = "--Month--"; - args.selected = ""; - args.startyear = "2000"; - args.endyear = "1990"; - r = _controller.dateTimeSelectTags(argumentcollection = args); - e = ''; - assert("r contains e"); - args.selected = "01/02/2000"; - r = _controller.dateTimeSelectTags(argumentcollection = args); - debug('r', false); - e1 = ''; - e2 = ''; - e3 = ''; - assert("r contains e1 && r contains e2 && r contains e3"); - } - - function test_dateTimeSelectTags_blank_not_included() { - args.name = "dateselector"; - args.includeBlank = "false"; - args.selected = ""; - args.startyear = "2000"; - args.endyear = "1990"; - r = _controller.dateTimeSelectTags(argumentcollection = args); - e = ''; - assert("r does not contain e"); - args.selected = "01/02/2000"; - r = _controller.dateTimeSelectTags(argumentcollection = args); - debug('r', false); - e1 = ''; - e2 = ''; - e3 = ''; - assert("r contains e1 && r contains e2 && r contains e3"); - } - - function test_dateTimeSelectTags_twelvehour() { - date = CreateDateTime(2014, 8, 4, 12, 30, 35); - args.name = "x"; - args.twelveHour = true; - args.selected = date; - r = _controller.dateTimeSelectTags(argumentcollection = args); - e = ''; - assert("r Contains e"); - } - -} diff --git a/core/src/wheels/tests/view/formsdateplain/DaySelectTag.cfc b/core/src/wheels/tests/view/formsdateplain/DaySelectTag.cfc deleted file mode 100644 index fbb6c2abb3..0000000000 --- a/core/src/wheels/tests/view/formsdateplain/DaySelectTag.cfc +++ /dev/null @@ -1,9 +0,0 @@ -component extends="wheels.tests.Test" { - - _controller = controller(name = "dummy"); - - function _test_x() { - assert("1 IS 1"); - } - -} diff --git a/core/src/wheels/tests/view/formsdateplain/HourSelectTag.cfc b/core/src/wheels/tests/view/formsdateplain/HourSelectTag.cfc deleted file mode 100644 index 9c6972b45b..0000000000 --- a/core/src/wheels/tests/view/formsdateplain/HourSelectTag.cfc +++ /dev/null @@ -1,25 +0,0 @@ -component extends="wheels.tests.Test" { - - function setup() { - _controller = controller(name = "dummy"); - set(functionName = "hourSelectTag", encode = false); - } - - function teardown() { - set(functionName = "hourSelectTag", encode = true); - } - - function test_hourSelect_twelve_hour_format() { - args.name = "dateselector"; - args.twelveHour = "true"; - args.selected = "11"; - r = _controller.hourSelectTag(argumentcollection = args); - e = ':'; - assert('e eq r'); - args.selected = "23"; - r = _controller.hourSelectTag(argumentcollection = args); - e = ':'; - assert('e eq r'); - } - -} diff --git a/core/src/wheels/tests/view/formsdateplain/MinuteSelectTag.cfc b/core/src/wheels/tests/view/formsdateplain/MinuteSelectTag.cfc deleted file mode 100644 index 260c320a30..0000000000 --- a/core/src/wheels/tests/view/formsdateplain/MinuteSelectTag.cfc +++ /dev/null @@ -1,18 +0,0 @@ -component extends="wheels.tests.Test" { - - function setup() { - _controller = controller(name = "dummy"); - } - - function test_step_argument() { - args.name = "countdown"; - args.selected = 15; - args.minuteStep = 15; - r = _controller.minuteSelectTag(argumentcollection = args); - e = '