1153 lines
39 KiB
PHP
1153 lines
39 KiB
PHP
|
<?php /** @noinspection PhpMissingDocCommentInspection */
|
||
|
|
||
|
/*********************************************************************************
|
||
|
*
|
||
|
* TimeTrex is a Workforce Management program developed by
|
||
|
* TimeTrex Software Inc. Copyright (C) 2003 - 2021 TimeTrex Software Inc.
|
||
|
*
|
||
|
* This program is free software; you can redistribute it and/or modify it under
|
||
|
* the terms of the GNU Affero General Public License version 3 as published by
|
||
|
* the Free Software Foundation with the addition of the following permission
|
||
|
* added to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED
|
||
|
* WORK IN WHICH THE COPYRIGHT IS OWNED BY TIMETREX, TIMETREX DISCLAIMS THE
|
||
|
* WARRANTY OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
|
||
|
*
|
||
|
* This program 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 Affero General Public License for more
|
||
|
* details.
|
||
|
*
|
||
|
*
|
||
|
* You should have received a copy of the GNU Affero General Public License along
|
||
|
* with this program; if not, see http://www.gnu.org/licenses or write to the Free
|
||
|
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||
|
* 02110-1301 USA.
|
||
|
*
|
||
|
*
|
||
|
* You can contact TimeTrex headquarters at Unit 22 - 2475 Dobbin Rd. Suite
|
||
|
* #292 West Kelowna, BC V4T 2E9, Canada or at email address info@timetrex.com.
|
||
|
*
|
||
|
*
|
||
|
* The interactive user interfaces in modified source and object code versions
|
||
|
* of this program must display Appropriate Legal Notices, as required under
|
||
|
* Section 5 of the GNU Affero General Public License version 3.
|
||
|
*
|
||
|
*
|
||
|
* In accordance with Section 7(b) of the GNU Affero General Public License
|
||
|
* version 3, these Appropriate Legal Notices must retain the display of the
|
||
|
* "Powered by TimeTrex" logo. If the display of the logo is not reasonably
|
||
|
* feasible for technical reasons, the Appropriate Legal Notices must display
|
||
|
* the words "Powered by TimeTrex".
|
||
|
*
|
||
|
********************************************************************************/
|
||
|
|
||
|
class SQLTest extends PHPUnit\Framework\TestCase {
|
||
|
public function setUp(): void {
|
||
|
global $dd;
|
||
|
Debug::text( 'Running setUp(): ', __FILE__, __LINE__, __METHOD__, 10 );
|
||
|
|
||
|
$dd = new DemoData();
|
||
|
$dd->setEnableQuickPunch( false ); //Helps prevent duplicate punch IDs and validation failures.
|
||
|
$dd->setUserNamePostFix( '_' . uniqid( null, true ) ); //Needs to be super random to prevent conflicts and random failing tests.
|
||
|
$this->company_id = $dd->createCompany();
|
||
|
$this->legal_entity_id = $dd->createLegalEntity( $this->company_id, 10 );
|
||
|
Debug::text( 'Company ID: ' . $this->company_id, __FILE__, __LINE__, __METHOD__, 10 );
|
||
|
$this->assertTrue( TTUUID::isUUID( $this->company_id ) );
|
||
|
|
||
|
//$dd->createPermissionGroups( $this->company_id, 40 ); //Administrator only.
|
||
|
|
||
|
$dd->createCurrency( $this->company_id, 10 );
|
||
|
|
||
|
$this->branch_id = $dd->createBranch( $this->company_id, 10 ); //NY
|
||
|
|
||
|
//$dd->createPayStubAccount( $this->company_id );
|
||
|
//$dd->createPayStubAccountLink( $this->company_id );
|
||
|
|
||
|
$dd->createUserWageGroups( $this->company_id );
|
||
|
|
||
|
$this->user_id = $dd->createUser( $this->company_id, $this->legal_entity_id, 100 );
|
||
|
$this->assertTrue( TTUUID::isUUID( $this->user_id ) );
|
||
|
}
|
||
|
|
||
|
public function tearDown(): void {
|
||
|
Debug::text( 'Running tearDown(): ', __FILE__, __LINE__, __METHOD__, 10 );
|
||
|
}
|
||
|
|
||
|
function getListFactoryClassList( $equal_parts = 1 ) {
|
||
|
global $global_class_map;
|
||
|
|
||
|
$retarr = [];
|
||
|
|
||
|
//Get all ListFactory classes
|
||
|
foreach ( $global_class_map as $class_name => $class_file_name ) {
|
||
|
if ( strpos( $class_name, 'ListFactory' ) !== false ) {
|
||
|
$retarr[] = $class_name;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
$chunk_size = ceil( ( count( $retarr ) / $equal_parts ) );
|
||
|
|
||
|
return array_chunk( $retarr, $chunk_size );
|
||
|
}
|
||
|
|
||
|
/** @noinspection PhpMissingBreakStatementInspection */
|
||
|
function runSQLTestOnListFactory( $factory_name ) {
|
||
|
if ( class_exists( $factory_name ) ) {
|
||
|
$reflectionClass = new ReflectionClass( $factory_name );
|
||
|
$class_file_name = $reflectionClass->getFileName();
|
||
|
|
||
|
Debug::text( 'Checking Class: ' . $factory_name . ' File: ' . $class_file_name, __FILE__, __LINE__, __METHOD__, 10 );
|
||
|
|
||
|
$filter_data_types = [
|
||
|
'not_set',
|
||
|
'true',
|
||
|
'false',
|
||
|
'null',
|
||
|
'empty_string',
|
||
|
'negative_small_int',
|
||
|
'small_int',
|
||
|
'large_int',
|
||
|
'string',
|
||
|
'array',
|
||
|
];
|
||
|
|
||
|
//Parse filter array keys from class file so we can populate them with dummy data.
|
||
|
preg_match_all( '/\$filter_data\[\'([a-z0-9_]*)\'\]/i', file_get_contents( $class_file_name ), $filter_data_match );
|
||
|
if ( isset( $filter_data_match[1] ) ) {
|
||
|
//Debug::Arr($filter_data_match, 'Filter Data Match: ', __FILE__, __LINE__, __METHOD__, 10);
|
||
|
foreach ( $filter_data_types as $filter_data_type ) {
|
||
|
Debug::Text( 'Filter Data Type: ' . $filter_data_type, __FILE__, __LINE__, __METHOD__, 10 );
|
||
|
|
||
|
$filter_data = [];
|
||
|
|
||
|
$filter_data_match[1] = array_unique( $filter_data_match[1] );
|
||
|
foreach ( $filter_data_match[1] as $filter_data_key ) {
|
||
|
//Skip sort_column/sort_order
|
||
|
if ( in_array( $filter_data_key, [ 'sort_column', 'sort_order' ] ) ) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
//Test with:
|
||
|
// Small Integers
|
||
|
// Large Integers (64bit)
|
||
|
// Strings
|
||
|
// Arrays
|
||
|
switch ( $filter_data_type ) {
|
||
|
case 'true':
|
||
|
$filter_data[$filter_data_key] = true;
|
||
|
break;
|
||
|
case 'false':
|
||
|
$filter_data[$filter_data_key] = false;
|
||
|
break;
|
||
|
case 'null':
|
||
|
$filter_data[$filter_data_key] = null;
|
||
|
break;
|
||
|
case 'empty_string':
|
||
|
$filter_data[$filter_data_key] = '';
|
||
|
break;
|
||
|
case 'negative_small_int':
|
||
|
$filter_data[$filter_data_key] = ( rand( 0, 128 ) * -1 );
|
||
|
break;
|
||
|
case 'small_int':
|
||
|
$filter_data[$filter_data_key] = rand( 0, 128 );
|
||
|
break;
|
||
|
case 'large_int':
|
||
|
$filter_data[$filter_data_key] = rand( 2147483648, 21474836489 );
|
||
|
break;
|
||
|
case 'string':
|
||
|
$filter_data[$filter_data_key] = 'A' . substr( md5( microtime() ), rand( 0, 26 ), 10 );
|
||
|
break;
|
||
|
case 'array':
|
||
|
$filter_data[$filter_data_key] = [ rand( 0, 128 ), rand( 2147483648, 21474836489 ), 'A' . substr( md5( microtime() ), rand( 0, 26 ), 10 ) ];
|
||
|
break;
|
||
|
case 'not_set':
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
//Debug::Arr($filter_data, 'Filter Data: ', __FILE__, __LINE__, __METHOD__, 10);
|
||
|
|
||
|
$lf = TTNew( $factory_name );
|
||
|
switch ( $factory_name ) {
|
||
|
case 'ScheduleListFactory':
|
||
|
$retarr = $lf->getSearchByCompanyIdAndArrayCriteria( $this->company_id, $filter_data, 1, 1, null, null );
|
||
|
$this->assertNotEquals( false, $retarr );
|
||
|
$this->assertTrue( is_object( $retarr ), true );
|
||
|
|
||
|
$retarr = $lf->getAPISearchByCompanyIdAndArrayCriteria( $this->company_id, $filter_data, 1, 1, null, null );
|
||
|
$this->assertNotEquals( false, $retarr );
|
||
|
$this->assertTrue( is_object( $retarr ), true );
|
||
|
break;
|
||
|
case 'MessageControlListFactory':
|
||
|
case 'NotificationListFactory':
|
||
|
$filter_data['current_user_id'] = $this->user_id;
|
||
|
//break; //Intentional that break is not here so it spills into default?
|
||
|
default:
|
||
|
if ( method_exists( $lf, 'getAPISearchByCompanyIdAndArrayCriteria' ) ) {
|
||
|
//Make sure we test pagination, especially with subqueries and the need for _ADODB_COUNT workarounds, $limit = NULL, $page = NULL, $where = NULL, $order = NULL
|
||
|
$retarr = $lf->getAPISearchByCompanyIdAndArrayCriteria( $this->company_id, $filter_data, 1, 1, null, null );
|
||
|
$this->assertNotEquals( false, $retarr );
|
||
|
$this->assertTrue( is_object( $retarr ), true );
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
unset( $filter_data_match );
|
||
|
|
||
|
Debug::text( 'Done...', __FILE__, __LINE__, __METHOD__, 10 );
|
||
|
|
||
|
return true;
|
||
|
} else {
|
||
|
Debug::text( 'Class does not exist: ' . $factory_name, __FILE__, __LINE__, __METHOD__, 10 );
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
function runSQLTestOnListFactoryMethods( $factory_name ) {
|
||
|
if ( in_array( $factory_name, [
|
||
|
'HierarchyListFactory',
|
||
|
'PolicyGroupAccrualPolicyListFactory',
|
||
|
'PolicyGroupOverTimePolicyListFactory',
|
||
|
'PolicyGroupPremiumPolicyListFactory',
|
||
|
'PolicyGroupRoundIntervalPolicyListFactory',
|
||
|
'ProductTaxPolicyProductListFactory',
|
||
|
'RecurringScheduleUserListFactory',
|
||
|
]
|
||
|
)
|
||
|
) {
|
||
|
return true; //Deprecated classes.
|
||
|
}
|
||
|
|
||
|
if ( class_exists( $factory_name ) ) {
|
||
|
$reflectionClass = new ReflectionClass( $factory_name );
|
||
|
Debug::text( 'Checking Class: ' . $factory_name, __FILE__, __LINE__, __METHOD__, 10 );
|
||
|
|
||
|
$raw_methods = $reflectionClass->getMethods( ReflectionMethod::IS_PUBLIC );
|
||
|
if ( is_array( $raw_methods ) ) {
|
||
|
foreach ( $raw_methods as $raw_method ) {
|
||
|
if ( $factory_name == $raw_method->class
|
||
|
&& (
|
||
|
strpos( $raw_method->name, 'getAll' ) !== false
|
||
|
|| strpos( $raw_method->name, 'getBy' ) !== false
|
||
|
|| strpos( $raw_method->name, 'Report' ) !== false
|
||
|
)
|
||
|
&& (
|
||
|
//Skip getByCompanyIdArray() functions, but include getBy*AndArrayCriteria(). So just check if its ends with Array or not.
|
||
|
( substr( $raw_method->name, -5 ) !== 'Array' )
|
||
|
)
|
||
|
) {
|
||
|
Debug::text( 'Class: ' . $factory_name . ' Method: ' . $raw_method->name, __FILE__, __LINE__, __METHOD__, 10 );
|
||
|
|
||
|
$test_modes = [ 'default', 'fuzz' ];
|
||
|
foreach ( $test_modes as $test_mode ) {
|
||
|
Debug::text( ' Test Mode: ' . $test_mode, __FILE__, __LINE__, __METHOD__, 10 );
|
||
|
//Get method arguments.
|
||
|
$method_parameters = $raw_method->getParameters();
|
||
|
if ( is_array( $method_parameters ) ) {
|
||
|
$input_arguments = [];
|
||
|
foreach ( $method_parameters as $method_parameter ) {
|
||
|
Debug::text( ' Parameter: ' . $method_parameter->name, __FILE__, __LINE__, __METHOD__, 10 );
|
||
|
|
||
|
switch ( $factory_name ) {
|
||
|
case 'ClientContactListFactory':
|
||
|
switch ( $method_parameter->name ) {
|
||
|
case 'key':
|
||
|
$input_argument = '900d6136975e3a728051a62ed119191034568745';
|
||
|
break;
|
||
|
case 'name':
|
||
|
$input_argument = 'test';
|
||
|
break;
|
||
|
}
|
||
|
break;
|
||
|
case 'RoundIntervalPolicyListFactory':
|
||
|
switch ( $method_parameter->name ) {
|
||
|
case 'type_id':
|
||
|
$input_argument = 40;
|
||
|
break;
|
||
|
}
|
||
|
break;
|
||
|
case 'ScheduleListFactory':
|
||
|
switch ( $method_parameter->name ) {
|
||
|
case 'direction':
|
||
|
$input_argument = 'before';
|
||
|
break;
|
||
|
}
|
||
|
break;
|
||
|
case 'UserListFactory':
|
||
|
case 'UserContactListFactory':
|
||
|
case 'JobApplicantListFactory':
|
||
|
switch ( $method_parameter->name ) {
|
||
|
case 'email':
|
||
|
$input_argument = 'hi@hi.com';
|
||
|
break;
|
||
|
case 'key':
|
||
|
$input_argument = '900d6136975e3a728051a62ed119191034568745';
|
||
|
break;
|
||
|
}
|
||
|
break;
|
||
|
case 'PayPeriodTimeSheetVerifyListFactory':
|
||
|
case 'RequestListFactory':
|
||
|
switch ( $method_parameter->name ) {
|
||
|
case 'hierarchy_level_map':
|
||
|
$input_argument = [
|
||
|
[
|
||
|
'hierarchy_control_id' => 1,
|
||
|
'level' => 1,
|
||
|
'last_level' => 2,
|
||
|
'object_type_id' => 10,
|
||
|
],
|
||
|
];
|
||
|
break;
|
||
|
}
|
||
|
break;
|
||
|
case 'ExceptionListFactory':
|
||
|
switch ( $method_parameter->name ) {
|
||
|
case 'time_period':
|
||
|
$input_argument = 'week';
|
||
|
break;
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if ( $test_mode == 'fuzz' ) {
|
||
|
//If LIMIT argument is available always set it to 1 to reduce memory usage.
|
||
|
if ( in_array( $method_parameter->name, [ 'where', 'order', 'page' ] ) ) {
|
||
|
$input_argument = null;
|
||
|
} else if ( !isset( $input_argument ) && ( $method_parameter->name == 'id' || strpos( $method_parameter->name, '_id' ) !== false || $method_parameter->name == 'limit' ) ) { //Use integer as its a ID argument.
|
||
|
$input_argument = 'false'; //Try passing a string where ID is expected.
|
||
|
} else if ( !isset( $input_argument ) ) {
|
||
|
$input_argument = 2;
|
||
|
}
|
||
|
$input_arguments[] = $input_argument;
|
||
|
} else {
|
||
|
//If LIMIT argument is available always set it to 1 to reduce memory usage.
|
||
|
if ( in_array( $method_parameter->name, [ 'where', 'order', 'page' ] ) ) {
|
||
|
$input_argument = null;
|
||
|
} else if ( !isset( $input_argument ) && ( $method_parameter->name == 'id' || strpos( $method_parameter->name, '_id' ) !== false || $method_parameter->name == 'limit' ) ) { //Use integer as its a ID argument.
|
||
|
$input_argument = 1;
|
||
|
} else if ( !isset( $input_argument ) ) {
|
||
|
$input_argument = 2;
|
||
|
}
|
||
|
$input_arguments[] = $input_argument;
|
||
|
}
|
||
|
unset( $input_argument );
|
||
|
}
|
||
|
|
||
|
if ( isset( $input_arguments ) && is_array( $input_arguments ) ) {
|
||
|
Debug::Arr( $input_arguments, ' Calling Class: ' . $factory_name . ' Method: ' . $raw_method->name, __FILE__, __LINE__, __METHOD__, 10 );
|
||
|
$lf = TTNew( $factory_name );
|
||
|
switch ( $factory_name . '::' . $raw_method->name ) {
|
||
|
case 'StationListFactory::getByUserIdAndStatusAndType':
|
||
|
case 'PayStubEntryAccountListFactory::getByTypeArrayByCompanyIdAndStatusId':
|
||
|
//Skip due to failures.
|
||
|
break;
|
||
|
case 'CompanyListFactory::getByPhoneID':
|
||
|
$retarr = call_user_func_array( [ $lf, $raw_method->name ], $input_arguments );
|
||
|
if ( $test_mode == 'fuzz' ) {
|
||
|
$this->assertEquals( false, ( ( is_object( $retarr ) ) ? ( ( $retarr->getRecordCount() == 0 ) ? false : true ) : $retarr ) ); //This will be FALSE
|
||
|
} else {
|
||
|
$this->assertNotEquals( false, $retarr );
|
||
|
$this->assertTrue( is_object( $retarr ), true );
|
||
|
}
|
||
|
break;
|
||
|
case 'MessageControlListFactory::getByCompanyIdAndObjectTypeAndObjectAndNotUser':
|
||
|
$retarr = call_user_func_array( [ $lf, $raw_method->name ], $input_arguments );
|
||
|
$this->assertEquals( false, ( ( is_object( $retarr ) ) ? ( ( $retarr->getRecordCount() == 0 ) ? false : true ) : $retarr ) ); //This will be FALSE, but it still executes a query.
|
||
|
//$this->assertTrue( is_object($retarr), TRUE );
|
||
|
break;
|
||
|
case 'PayStubEntryListFactory::getByPayStubIdAndEntryNameId':
|
||
|
//FUZZ tests should return FALSE, otherwise they should be normal.
|
||
|
$retarr = call_user_func_array( [ $lf, $raw_method->name ], $input_arguments );
|
||
|
if ( $test_mode == 'fuzz' ) {
|
||
|
$this->assertEquals( false, ( ( is_object( $retarr ) ) ? ( ( $retarr->getRecordCount() == 0 ) ? false : true ) : $retarr ) ); //This will be FALSE
|
||
|
} else {
|
||
|
$this->assertNotEquals( false, $retarr );
|
||
|
$this->assertTrue( is_object( $retarr ), true );
|
||
|
}
|
||
|
break;
|
||
|
default:
|
||
|
$retarr = call_user_func_array( [ $lf, $raw_method->name ], $input_arguments );
|
||
|
//Debug::Arr($retarr, ' RetArr: ', __FILE__, __LINE__, __METHOD__, 10);
|
||
|
$this->assertNotEquals( false, $retarr );
|
||
|
$this->assertTrue( is_object( $retarr ), true );
|
||
|
break;
|
||
|
}
|
||
|
} else {
|
||
|
Debug::text( ' No INPUT arguments... Skipping Class: ' . $factory_name . ' Method: ' . $raw_method->name, __FILE__, __LINE__, __METHOD__, 10 );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
} else {
|
||
|
Debug::text( 'Skipping... Class: ' . $factory_name . ' Method: ' . $raw_method->name, __FILE__, __LINE__, __METHOD__, 10 );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
Debug::text( 'Done...', __FILE__, __LINE__, __METHOD__, 10 );
|
||
|
|
||
|
return true;
|
||
|
} else {
|
||
|
Debug::text( 'Class does not exist: ' . $factory_name, __FILE__, __LINE__, __METHOD__, 10 );
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
function runSQLSortTestOnListFactory( $factory_name ) {
|
||
|
if ( class_exists( $factory_name ) ) {
|
||
|
$reflectionClass = new ReflectionClass( $factory_name );
|
||
|
$class_file_name = $reflectionClass->getFileName();
|
||
|
|
||
|
Debug::text( 'Checking Class: ' . $factory_name . ' File: ' . $class_file_name, __FILE__, __LINE__, __METHOD__, 10 );
|
||
|
|
||
|
$lf = TTNew( $factory_name );
|
||
|
if ( method_exists( $lf, 'getOptions' ) ) {
|
||
|
if ( method_exists( $lf, 'getAPISearchByCompanyIdAndArrayCriteria' ) ) {
|
||
|
$tmp_columns = $lf->getOptions( 'columns' );
|
||
|
if ( is_array( $tmp_columns ) && count( $tmp_columns ) > 0 ) {
|
||
|
$columns = array_fill_keys( array_keys( array_flip( array_keys( Misc::trimSortPrefix( $lf->getOptions( 'columns' ) ) ) ) ), 'asc' ); //Set sort order to ASC for all columns.
|
||
|
unset( $columns['tag'] ); //Remove columns that we can never sort by.
|
||
|
if ( is_array( $columns ) ) {
|
||
|
try {
|
||
|
|
||
|
if ( !in_array( $factory_name, [ 'BankAccountListFactory' ] ) ) { //Skip legacy factories.
|
||
|
if ( $factory_name == 'MessageControlListFactory' ) {
|
||
|
$retarr = $lf->getAPISearchByCompanyIdAndArrayCriteria( $this->company_id, [ 'current_user_id' => TTUUID::getZeroID() ], 1, 1, null, $columns );
|
||
|
} else if ( $factory_name == 'NotificationListFactory' ) {
|
||
|
$retarr = $lf->getAPISearchByCompanyIdAndArrayCriteria( $this->company_id, [ 'current_user_id' => TTUUID::getZeroID() ], 1, 1, null, $columns );
|
||
|
} else {
|
||
|
//$retarr = $lf->getAPISearchByCompanyIdAndArrayCriteria( $this->company_id, array(), 1, 1, NULL, array('a.bogus' => 'asc') );
|
||
|
$retarr = $lf->getAPISearchByCompanyIdAndArrayCriteria( $this->company_id, [], 1, 1, null, $columns );
|
||
|
}
|
||
|
|
||
|
$this->assertNotEquals( false, $retarr );
|
||
|
$this->assertTrue( is_object( $retarr ), true );
|
||
|
}
|
||
|
} catch ( Exception $e ) {
|
||
|
Debug::Arr( $columns, 'Columns: ', __FILE__, __LINE__, __METHOD__, 10 );
|
||
|
|
||
|
$this->assertTrue( false, $factory_name . ': ' . $e->getMessage() );
|
||
|
}
|
||
|
} else {
|
||
|
Debug::text( 'getOptions(\'columns\') does not return any data, skipping... Factory: ' . $factory_name, __FILE__, __LINE__, __METHOD__, 10 );
|
||
|
}
|
||
|
}
|
||
|
unset( $tmp_columns );
|
||
|
}
|
||
|
} else {
|
||
|
Debug::text( 'getOptions() method does not exist, skipping...', __FILE__, __LINE__, __METHOD__, 10 );
|
||
|
}
|
||
|
|
||
|
Debug::text( 'Done...', __FILE__, __LINE__, __METHOD__, 10 );
|
||
|
|
||
|
return true;
|
||
|
} else {
|
||
|
Debug::text( 'Class does not exist: ' . $factory_name, __FILE__, __LINE__, __METHOD__, 10 );
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
function runSQLTestOnEdition( $product_edition = TT_PRODUCT_ENTERPRISE, $class_list ) {
|
||
|
global $TT_PRODUCT_EDITION, $db;
|
||
|
|
||
|
$original_product_edition = getTTProductEdition();
|
||
|
|
||
|
$this->assertTrue( true );
|
||
|
if ( $product_edition <= $original_product_edition ) {
|
||
|
$TT_PRODUCT_EDITION = $product_edition;
|
||
|
Debug::text( 'Checking against Edition: ' . getTTProductEditionName(), __FILE__, __LINE__, __METHOD__, 10 );
|
||
|
|
||
|
//Loop through all ListFactory classes testing SQL queries.
|
||
|
|
||
|
//Run tests with count rows enabled, then with it disabled as well.
|
||
|
$db->pageExecuteCountRows = false;
|
||
|
foreach ( $class_list as $class_name ) {
|
||
|
$this->runSQLSortTestOnListFactory( $class_name );
|
||
|
$this->runSQLTestOnListFactoryMethods( $class_name );
|
||
|
$this->runSQLTestOnListFactory( $class_name );
|
||
|
}
|
||
|
|
||
|
$db->pageExecuteCountRows = true;
|
||
|
foreach ( $class_list as $class_name ) {
|
||
|
$this->runSQLTestOnListFactoryMethods( $class_name );
|
||
|
$this->runSQLTestOnListFactory( $class_name );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @group SQL_CommunityA
|
||
|
*/
|
||
|
function testSQLCommunityA() {
|
||
|
$classes = $this->getListFactoryClassList( 4 );
|
||
|
$this->runSQLTestOnEdition( TT_PRODUCT_COMMUNITY, $classes[0] );
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @group SQL_CommunityB
|
||
|
*/
|
||
|
function testSQLCommunityB() {
|
||
|
$classes = $this->getListFactoryClassList( 4 );
|
||
|
$this->runSQLTestOnEdition( TT_PRODUCT_COMMUNITY, $classes[1] );
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @group SQL_CommunityC
|
||
|
*/
|
||
|
function testSQLCommunityC() {
|
||
|
$classes = $this->getListFactoryClassList( 4 );
|
||
|
$this->runSQLTestOnEdition( TT_PRODUCT_COMMUNITY, $classes[2] );
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @group SQL_CommunityD
|
||
|
*/
|
||
|
function testSQLCommunityD() {
|
||
|
$classes = $this->getListFactoryClassList( 4 );
|
||
|
$this->runSQLTestOnEdition( TT_PRODUCT_COMMUNITY, $classes[3] );
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* @group SQL_ProfessionalA
|
||
|
*/
|
||
|
function testSQLProfessionalA() {
|
||
|
$classes = $this->getListFactoryClassList( 4 );
|
||
|
$this->runSQLTestOnEdition( TT_PRODUCT_PROFESSIONAL, $classes[0] );
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @group SQL_ProfessionalB
|
||
|
*/
|
||
|
function testSQLProfessionalB() {
|
||
|
$classes = $this->getListFactoryClassList( 4 );
|
||
|
$this->runSQLTestOnEdition( TT_PRODUCT_PROFESSIONAL, $classes[1] );
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @group SQL_ProfessionalC
|
||
|
*/
|
||
|
function testSQLProfessionalC() {
|
||
|
$classes = $this->getListFactoryClassList( 4 );
|
||
|
$this->runSQLTestOnEdition( TT_PRODUCT_PROFESSIONAL, $classes[2] );
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @group SQL_ProfessionalD
|
||
|
*/
|
||
|
function testSQLProfessionalD() {
|
||
|
$classes = $this->getListFactoryClassList( 4 );
|
||
|
$this->runSQLTestOnEdition( TT_PRODUCT_PROFESSIONAL, $classes[3] );
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* @group SQL_CorporateA
|
||
|
*/
|
||
|
function testSQLCorporateA() {
|
||
|
$classes = $this->getListFactoryClassList( 4 );
|
||
|
$this->runSQLTestOnEdition( TT_PRODUCT_CORPORATE, $classes[0] );
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @group SQL_CorporateB
|
||
|
*/
|
||
|
function testSQLCorporateB() {
|
||
|
$classes = $this->getListFactoryClassList( 4 );
|
||
|
$this->runSQLTestOnEdition( TT_PRODUCT_CORPORATE, $classes[1] );
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @group SQL_CorporateC
|
||
|
*/
|
||
|
function testSQLCorporateC() {
|
||
|
$classes = $this->getListFactoryClassList( 4 );
|
||
|
$this->runSQLTestOnEdition( TT_PRODUCT_CORPORATE, $classes[2] );
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @group SQL_CorporateD
|
||
|
*/
|
||
|
function testSQLCorporateD() {
|
||
|
$classes = $this->getListFactoryClassList( 4 );
|
||
|
$this->runSQLTestOnEdition( TT_PRODUCT_CORPORATE, $classes[3] );
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* @group SQL_EnterpriseA
|
||
|
*/
|
||
|
function testSQLEnterpriseA() {
|
||
|
$classes = $this->getListFactoryClassList( 4 );
|
||
|
$this->runSQLTestOnEdition( TT_PRODUCT_ENTERPRISE, $classes[0] );
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @group SQL_EnterpriseB
|
||
|
*/
|
||
|
function testSQLEnterpriseB() {
|
||
|
$classes = $this->getListFactoryClassList( 4 );
|
||
|
$this->runSQLTestOnEdition( TT_PRODUCT_ENTERPRISE, $classes[1] );
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @group SQL_EnterpriseC
|
||
|
*/
|
||
|
function testSQLEnterpriseC() {
|
||
|
$classes = $this->getListFactoryClassList( 4 );
|
||
|
$this->runSQLTestOnEdition( TT_PRODUCT_ENTERPRISE, $classes[2] );
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @group SQL_EnterpriseD
|
||
|
*/
|
||
|
function testSQLEnterpriseD() {
|
||
|
$classes = $this->getListFactoryClassList( 4 );
|
||
|
$this->runSQLTestOnEdition( TT_PRODUCT_ENTERPRISE, $classes[3] );
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @group SQL_ADODBActiveRecordCount
|
||
|
*/
|
||
|
function testADODBActiveRecordCount() {
|
||
|
global $db;
|
||
|
|
||
|
//This will test the automatic functionality of ADODB to add count(*) in SQL queries.
|
||
|
|
||
|
//PageExecute($query, $limit, $page, $ph)
|
||
|
|
||
|
$db->pageExecuteCountRows = true;
|
||
|
|
||
|
try {
|
||
|
$query = 'SELECT id FROM currency';
|
||
|
$db->PageExecute( $query, 2, 2 );
|
||
|
$this->assertTrue( true );
|
||
|
} catch ( Exception $e ) {
|
||
|
$this->assertTrue( false );
|
||
|
}
|
||
|
|
||
|
try {
|
||
|
$query = 'SELECT id, status_id FROM currency';
|
||
|
$db->PageExecute( $query, 2, 2 );
|
||
|
$this->assertTrue( true );
|
||
|
} catch ( Exception $e ) {
|
||
|
$this->assertTrue( false );
|
||
|
}
|
||
|
|
||
|
try {
|
||
|
$query = 'SELECT a.id FROM currency AS a LEFT JOIN company as tmp ON a.id = tmp.id';
|
||
|
$db->PageExecute( $query, 2, 2 );
|
||
|
$this->assertTrue( true );
|
||
|
} catch ( Exception $e ) {
|
||
|
$this->assertTrue( false );
|
||
|
}
|
||
|
|
||
|
try {
|
||
|
$query = 'SELECT a.id FROM currency AS a LEFT JOIN ( SELECT id FROM company ) as tmp ON a.id = tmp.id';
|
||
|
$db->PageExecute( $query, 2, 2 );
|
||
|
$this->assertTrue( true );
|
||
|
} catch ( Exception $e ) {
|
||
|
$this->assertTrue( false );
|
||
|
}
|
||
|
|
||
|
try {
|
||
|
$query = 'SELECT a.id, a.status_id FROM currency AS a LEFT JOIN ( SELECT id, status_id FROM company ) as tmp ON a.id = tmp.id';
|
||
|
$db->PageExecute( $query, 2, 2 );
|
||
|
$this->assertTrue( true );
|
||
|
} catch ( Exception $e ) {
|
||
|
$this->assertTrue( false );
|
||
|
}
|
||
|
|
||
|
try {
|
||
|
$query = 'SELECT * FROM ( SELECT a.id FROM currency AS a LEFT JOIN company as tmp ON a.id = tmp.id ) as tmp2';
|
||
|
$db->PageExecute( $query, 2, 2 );
|
||
|
$this->assertTrue( true );
|
||
|
} catch ( Exception $e ) {
|
||
|
$this->assertTrue( false );
|
||
|
}
|
||
|
|
||
|
try {
|
||
|
$query = 'SELECT _ADODB_COUNT id, (SELECT 1 FROM currency LIMIT 1) _ADODB_COUNT AS tmp FROM users';
|
||
|
$db->PageExecute( $query, 2, 2 );
|
||
|
|
||
|
$this->assertTrue( true );
|
||
|
} catch ( Exception $e ) {
|
||
|
$this->assertTrue( false );
|
||
|
}
|
||
|
|
||
|
//This query should have the _ADODB_COUNT keyword above to make it work on all databases.
|
||
|
//It works on PGSQL because it can wrap it in a sub-query, ie: SELECT count(*) FROM ( $query )
|
||
|
try {
|
||
|
$query = 'SELECT id, (SELECT 1 FROM currency LIMIT 1) AS tmp FROM users';
|
||
|
$db->PageExecute( $query, 2, 2 );
|
||
|
|
||
|
$this->assertTrue( true ); //PGSQL
|
||
|
} catch ( Exception $e ) {
|
||
|
$this->assertTrue( false ); //PGSQL
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @group SQL_SQLInjectionA
|
||
|
*/
|
||
|
function testSQLInjectionA() {
|
||
|
//Test SQL injection with SORT SQL.
|
||
|
//Test SQL injection with WHERE SQL.
|
||
|
|
||
|
$utlf = new UserTitleListFactory();
|
||
|
|
||
|
//Test standard case that should work.
|
||
|
$sort_arr = [
|
||
|
'a.name' => 'asc',
|
||
|
];
|
||
|
$utlf->getAPISearchByCompanyIdAndArrayCriteria( 1, [], null, null, null, $sort_arr );
|
||
|
|
||
|
//var_dump( $utlf->rs->sql );
|
||
|
if ( stripos( $utlf->rs->sql, 'a.name' ) !== false ) {
|
||
|
$this->assertTrue( true );
|
||
|
} else {
|
||
|
$this->assertTrue( false );
|
||
|
}
|
||
|
|
||
|
|
||
|
//Test advanced case that should work.
|
||
|
$sort_arr = [
|
||
|
//'a.name = \'test\'' => 'asc'
|
||
|
'a.created_date = 0' => 'asc',
|
||
|
];
|
||
|
$utlf->getAPISearchByCompanyIdAndArrayCriteria( 1, [], null, null, null, $sort_arr );
|
||
|
|
||
|
//var_dump( $utlf->rs->sql );
|
||
|
if ( stripos( $utlf->rs->sql, 'a.created_date = 0' ) !== false ) {
|
||
|
$this->assertTrue( true );
|
||
|
} else {
|
||
|
$this->assertTrue( false );
|
||
|
}
|
||
|
|
||
|
|
||
|
//Test advanced case that does not work currently, but we may need to get to work.
|
||
|
try {
|
||
|
$pself = new PayStubEntryListFactory();
|
||
|
$sort_arr = [
|
||
|
'abs(a.created_date)' => 'asc',
|
||
|
];
|
||
|
|
||
|
$pself->getAPISearchByCompanyIdAndArrayCriteria( 1, [], null, null, null, $sort_arr );
|
||
|
//var_dump( $pself->rs->sql );
|
||
|
if ( stripos( $pself->rs->sql, 'abs(a.created_date)' ) !== false ) {
|
||
|
$this->assertTrue( false );
|
||
|
} else {
|
||
|
$this->assertTrue( true );
|
||
|
}
|
||
|
} catch ( Exception $e ) {
|
||
|
$this->assertTrue( true );
|
||
|
}
|
||
|
|
||
|
|
||
|
//Test SQL injection in the ORDER BY clause.
|
||
|
try {
|
||
|
$sort_arr = [
|
||
|
'created_by' => '(SELECT 1)-- .id.',
|
||
|
];
|
||
|
$utlf->getAPISearchByCompanyIdAndArrayCriteria( 1, [], null, null, null, $sort_arr );
|
||
|
|
||
|
//var_dump( $utlf->rs->sql );
|
||
|
if ( stripos( $utlf->rs->sql, '(SELECT 1)' ) !== false ) {
|
||
|
$this->assertTrue( false );
|
||
|
} else {
|
||
|
$this->assertTrue( true );
|
||
|
}
|
||
|
} catch ( Exception $e ) {
|
||
|
$this->assertTrue( true );
|
||
|
}
|
||
|
|
||
|
|
||
|
//Test SQL injection with brackets and "--"
|
||
|
try {
|
||
|
$sort_arr = [
|
||
|
'(SELECT 1)-- .id.' => 1,
|
||
|
];
|
||
|
$utlf->getAPISearchByCompanyIdAndArrayCriteria( 1, [], null, null, null, $sort_arr );
|
||
|
|
||
|
//var_dump( $utlf->rs->sql );
|
||
|
if ( stripos( $utlf->rs->sql, '(SELECT 1)' ) !== false ) {
|
||
|
$this->assertTrue( false );
|
||
|
} else {
|
||
|
$this->assertTrue( true );
|
||
|
}
|
||
|
} catch ( Exception $e ) {
|
||
|
$this->assertTrue( true );
|
||
|
}
|
||
|
|
||
|
|
||
|
//Test SQL injection with ";" and "--"
|
||
|
try {
|
||
|
$sort_arr = [
|
||
|
'; (SELECT 1)-- .id.' => 1,
|
||
|
];
|
||
|
$utlf->getAPISearchByCompanyIdAndArrayCriteria( 1, [], null, null, null, $sort_arr );
|
||
|
|
||
|
//var_dump( $utlf->rs->sql );
|
||
|
if ( stripos( $utlf->rs->sql, '(SELECT 1)' ) !== false ) {
|
||
|
$this->assertTrue( false );
|
||
|
} else {
|
||
|
$this->assertTrue( true );
|
||
|
}
|
||
|
} catch ( Exception $e ) {
|
||
|
$this->assertTrue( true );
|
||
|
}
|
||
|
|
||
|
|
||
|
//FIXME: Test around the WHERE clause, even though no user input should ever get to it.
|
||
|
// $pslf = TTnew('PayStubListFactory');
|
||
|
// //$pslf->getByCompanyId( 1, 1, NULL, ( array('a.start_date' => ">= '". $pslf->db->BindTimeStamp( TTDate::getBeginDayEpoch( time() - (86400 * 30) ) )."'") ) );
|
||
|
// $pslf->getByCompanyId( 1, 1, NULL, ( array('a.start_date >=' => $pslf->db->BindTimeStamp( TTDate::getBeginDayEpoch( time() - (86400 * 30) ) ) ) ) );
|
||
|
// //$pslf->getByCompanyId( 1, 1, NULL, ( array('a.created_date' => "=1-- (SELECT 1)--") ) );
|
||
|
// var_dump( $pslf->rs->sql );
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Test SQL Injection in the sort query.
|
||
|
* @group SQL_SQLInjectionA
|
||
|
*/
|
||
|
function testSortSQLInjectionA() {
|
||
|
$injected_payload = [ "(CASE WHEN (SELECT lo_import('/etc/hosts'))='1' THEN a.created_date ELSE a.updated_date END) ASC --" => 'ASC' ];
|
||
|
|
||
|
//Reflection class required to test as getSortSQL is a protected function.
|
||
|
$uf = new UserFactory();
|
||
|
$class = new ReflectionClass($uf);
|
||
|
$method = $class->getMethod('getSortSQL');
|
||
|
$method->setAccessible(true);
|
||
|
|
||
|
//SQL Payload completely rejected when strict is true.
|
||
|
try {
|
||
|
$method->invokeArgs($uf, [ $injected_payload, true ] );
|
||
|
$this->assertTrue( false );
|
||
|
} catch (Exception $e) {
|
||
|
$this->assertTrue( true );
|
||
|
}
|
||
|
|
||
|
//SQL Payload filtered when strict is false.
|
||
|
$this->assertTrue( $method->invokeArgs( $uf, [ $injected_payload, false ] ) === ' ORDER BY (CASE WHEN (SELECT lo_import(/etc/hosts))=1 THEN a.created_date ELSE a.updated_date END) ASC ASC' );
|
||
|
$this->assertTrue( $method->invokeArgs( $uf, [ [ 'safe"$$;-$-' . "'_characters" => 'ASC' ], false ] ) === ' ORDER BY safe_characters ASC' );
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Used to call protected methods in the Factory class.
|
||
|
* @param $name
|
||
|
* @return ReflectionMethod
|
||
|
*/
|
||
|
protected static function getMethod( $name ) {
|
||
|
$class = new ReflectionClass( 'UserListFactory' );
|
||
|
$method = $class->getMethod( $name );
|
||
|
$method->setAccessible( true );
|
||
|
|
||
|
return $method;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @group SQL_testWhereClauseSQL
|
||
|
*/
|
||
|
function testWhereClauseSQL() {
|
||
|
$method = self::getMethod( 'getWhereClauseSQL' );
|
||
|
$ulf = new UserListFactory();
|
||
|
|
||
|
|
||
|
//Boolean TRUE
|
||
|
$ph = [];
|
||
|
$retval = $method->invokeArgs( $ulf, [ 'a.private', (bool)true, 'boolean', &$ph ] );
|
||
|
$this->assertEquals( ' AND a.private = ? ', $retval );
|
||
|
$this->assertEquals( 1, $ph[0] );
|
||
|
|
||
|
$ph = [];
|
||
|
$retval = $method->invokeArgs( $ulf, [ 'a.private', (int)1, 'boolean', &$ph ] );
|
||
|
$this->assertEquals( ' AND a.private = ? ', $retval );
|
||
|
$this->assertEquals( 1, $ph[0] );
|
||
|
|
||
|
$ph = [];
|
||
|
$retval = $method->invokeArgs( $ulf, [ 'a.private', 1, 'boolean', &$ph ] );
|
||
|
$this->assertEquals( ' AND a.private = ? ', $retval );
|
||
|
$this->assertEquals( 1, $ph[0] );
|
||
|
|
||
|
$ph = [];
|
||
|
$retval = $method->invokeArgs( $ulf, [ 'a.private', 1.00, 'boolean', &$ph ] );
|
||
|
$this->assertEquals( ' AND a.private = ? ', $retval );
|
||
|
$this->assertEquals( 1, $ph[0] );
|
||
|
|
||
|
$ph = [];
|
||
|
$retval = $method->invokeArgs( $ulf, [ 'a.private', '1', 'boolean', &$ph ] );
|
||
|
$this->assertEquals( ' AND a.private = ? ', $retval );
|
||
|
$this->assertEquals( 1, $ph[0] );
|
||
|
|
||
|
$ph = [];
|
||
|
$retval = $method->invokeArgs( $ulf, [ 'a.private', 'TRUE', 'boolean', &$ph ] );
|
||
|
$this->assertEquals( ' AND a.private = ? ', $retval );
|
||
|
$this->assertEquals( 1, $ph[0] );
|
||
|
|
||
|
|
||
|
//Boolean FALSE
|
||
|
$ph = [];
|
||
|
$retval = $method->invokeArgs( $ulf, [ 'a.private', (bool)false, 'boolean', &$ph ] );
|
||
|
$this->assertEquals( ' AND a.private = ? ', $retval );
|
||
|
$this->assertEquals( 0, $ph[0] );
|
||
|
|
||
|
$ph = [];
|
||
|
$retval = $method->invokeArgs( $ulf, [ 'a.private', (int)0, 'boolean', &$ph ] );
|
||
|
$this->assertEquals( ' AND a.private = ? ', $retval );
|
||
|
$this->assertEquals( 0, $ph[0] );
|
||
|
|
||
|
$ph = [];
|
||
|
$retval = $method->invokeArgs( $ulf, [ 'a.private', 0, 'boolean', &$ph ] );
|
||
|
$this->assertEquals( ' AND a.private = ? ', $retval );
|
||
|
$this->assertEquals( 0, $ph[0] );
|
||
|
|
||
|
$ph = [];
|
||
|
$retval = $method->invokeArgs( $ulf, [ 'a.private', 0.00, 'boolean', &$ph ] );
|
||
|
$this->assertEquals( ' AND a.private = ? ', $retval );
|
||
|
$this->assertEquals( 0, $ph[0] );
|
||
|
|
||
|
$ph = [];
|
||
|
$retval = $method->invokeArgs( $ulf, [ 'a.private', '0', 'boolean', &$ph ] );
|
||
|
$this->assertEquals( ' AND a.private = ? ', $retval );
|
||
|
$this->assertEquals( 0, $ph[0] );
|
||
|
|
||
|
$ph = [];
|
||
|
$retval = $method->invokeArgs( $ulf, [ 'a.private', 'FALSE', 'boolean', &$ph ] );
|
||
|
$this->assertEquals( ' AND a.private = ? ', $retval );
|
||
|
$this->assertEquals( 0, $ph[0] );
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @group SQL_testTransactionNestingA
|
||
|
*/
|
||
|
function testTransactionNestingA() {
|
||
|
$uf = new UserFactory();
|
||
|
|
||
|
$this->assertEquals( 0, $uf->db->transCnt );
|
||
|
$this->assertEquals( 0, $uf->db->transOff );
|
||
|
$this->assertEquals( true, $uf->db->_transOK );
|
||
|
|
||
|
|
||
|
$uf->StartTransaction();
|
||
|
$this->assertEquals( 1, $uf->db->transCnt );
|
||
|
$this->assertEquals( 1, $uf->db->transOff );
|
||
|
|
||
|
$uf->StartTransaction();
|
||
|
$this->assertEquals( 1, $uf->db->transCnt );
|
||
|
$this->assertEquals( 2, $uf->db->transOff );
|
||
|
|
||
|
$uf->StartTransaction();
|
||
|
$this->assertEquals( 1, $uf->db->transCnt );
|
||
|
$this->assertEquals( 3, $uf->db->transOff );
|
||
|
|
||
|
//$uf->FailTransaction();
|
||
|
$uf->CommitTransaction();
|
||
|
$this->assertEquals( 1, $uf->db->transCnt );
|
||
|
$this->assertEquals( 2, $uf->db->transOff );
|
||
|
|
||
|
$uf->CommitTransaction();
|
||
|
$this->assertEquals( 1, $uf->db->transCnt );
|
||
|
$this->assertEquals( 1, $uf->db->transOff );
|
||
|
|
||
|
$uf->CommitTransaction();
|
||
|
$this->assertEquals( 0, $uf->db->transCnt );
|
||
|
$this->assertEquals( 0, $uf->db->transOff );
|
||
|
|
||
|
$this->assertEquals( 0, $uf->db->transCnt );
|
||
|
$this->assertEquals( 0, $uf->db->transOff );
|
||
|
$this->assertEquals( true, $uf->db->_transOK );
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @group SQL_testTransactionNestingB
|
||
|
*/
|
||
|
function testTransactionNestingB() {
|
||
|
$uf = new UserFactory();
|
||
|
|
||
|
$this->assertEquals( 0, $uf->db->transCnt );
|
||
|
$this->assertEquals( 0, $uf->db->transOff );
|
||
|
$this->assertEquals( true, $uf->db->_transOK );
|
||
|
|
||
|
|
||
|
$uf->StartTransaction();
|
||
|
$this->assertEquals( 1, $uf->db->transCnt );
|
||
|
$this->assertEquals( 1, $uf->db->transOff );
|
||
|
$this->assertEquals( true, $uf->db->_transOK );
|
||
|
|
||
|
$uf->StartTransaction();
|
||
|
$this->assertEquals( 1, $uf->db->transCnt );
|
||
|
$this->assertEquals( 2, $uf->db->transOff );
|
||
|
$this->assertEquals( true, $uf->db->_transOK );
|
||
|
|
||
|
$uf->StartTransaction();
|
||
|
$this->assertEquals( 1, $uf->db->transCnt );
|
||
|
$this->assertEquals( 3, $uf->db->transOff );
|
||
|
$this->assertEquals( true, $uf->db->_transOK );
|
||
|
|
||
|
$uf->FailTransaction();
|
||
|
$this->assertEquals( 1, $uf->db->transCnt );
|
||
|
$this->assertEquals( 3, $uf->db->transOff );
|
||
|
$this->assertEquals( false, $uf->db->_transOK );
|
||
|
|
||
|
$uf->CommitTransaction();
|
||
|
$this->assertEquals( 1, $uf->db->transCnt );
|
||
|
$this->assertEquals( 2, $uf->db->transOff );
|
||
|
$this->assertEquals( false, $uf->db->_transOK );
|
||
|
|
||
|
$uf->CommitTransaction();
|
||
|
$this->assertEquals( 1, $uf->db->transCnt );
|
||
|
$this->assertEquals( 1, $uf->db->transOff );
|
||
|
$this->assertEquals( false, $uf->db->_transOK );
|
||
|
|
||
|
$uf->CommitTransaction();
|
||
|
$this->assertEquals( 0, $uf->db->transCnt );
|
||
|
$this->assertEquals( 0, $uf->db->transOff );
|
||
|
$this->assertEquals( false, $uf->db->_transOK );
|
||
|
|
||
|
|
||
|
$this->assertEquals( 0, $uf->db->transCnt );
|
||
|
$this->assertEquals( 0, $uf->db->transOff );
|
||
|
$this->assertEquals( false, $uf->db->_transOK );
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @group SQL_testTransactionNestingC
|
||
|
*/
|
||
|
function testTransactionNestingC() {
|
||
|
$uf = new UserFactory();
|
||
|
|
||
|
$this->assertEquals( 0, $uf->db->transCnt );
|
||
|
$this->assertEquals( 0, $uf->db->transOff );
|
||
|
$this->assertEquals( true, $uf->db->_transOK );
|
||
|
|
||
|
|
||
|
$uf->StartTransaction();
|
||
|
$this->assertEquals( 1, $uf->db->transCnt );
|
||
|
$this->assertEquals( 1, $uf->db->transOff );
|
||
|
$this->assertEquals( true, $uf->db->_transOK );
|
||
|
|
||
|
$uf->StartTransaction();
|
||
|
$this->assertEquals( 1, $uf->db->transCnt );
|
||
|
$this->assertEquals( 2, $uf->db->transOff );
|
||
|
$this->assertEquals( true, $uf->db->_transOK );
|
||
|
|
||
|
$uf->StartTransaction();
|
||
|
$this->assertEquals( 1, $uf->db->transCnt );
|
||
|
$this->assertEquals( 3, $uf->db->transOff );
|
||
|
$this->assertEquals( true, $uf->db->_transOK );
|
||
|
|
||
|
$uf->FailTransaction();
|
||
|
$this->assertEquals( 1, $uf->db->transCnt );
|
||
|
$this->assertEquals( 3, $uf->db->transOff );
|
||
|
$this->assertEquals( false, $uf->db->_transOK );
|
||
|
|
||
|
$uf->CommitTransaction( true ); //Unest all transactions.
|
||
|
$this->assertEquals( 0, $uf->db->transCnt );
|
||
|
$this->assertEquals( 0, $uf->db->transOff );
|
||
|
$this->assertEquals( false, $uf->db->_transOK );
|
||
|
|
||
|
|
||
|
$this->assertEquals( 0, $uf->db->transCnt );
|
||
|
$this->assertEquals( 0, $uf->db->transOff );
|
||
|
$this->assertEquals( false, $uf->db->_transOK );
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @group SQL_testTransactionNestingC2
|
||
|
*/
|
||
|
function testTransactionNestingC2() {
|
||
|
$uf = new UserFactory();
|
||
|
|
||
|
$this->assertEquals( 0, $uf->db->transCnt );
|
||
|
$this->assertEquals( 0, $uf->db->transOff );
|
||
|
$this->assertEquals( true, $uf->db->_transOK );
|
||
|
|
||
|
|
||
|
$uf->StartTransaction();
|
||
|
$uf->StartTransaction();
|
||
|
$uf->StartTransaction();
|
||
|
$uf->StartTransaction();
|
||
|
$uf->StartTransaction();
|
||
|
$uf->StartTransaction();
|
||
|
$uf->StartTransaction();
|
||
|
$uf->StartTransaction();
|
||
|
$uf->StartTransaction();
|
||
|
$uf->FailTransaction();
|
||
|
|
||
|
$uf->CommitTransaction( true ); //Unest all transactions.
|
||
|
|
||
|
$this->assertEquals( 0, $uf->db->transCnt );
|
||
|
$this->assertEquals( 0, $uf->db->transOff );
|
||
|
$this->assertEquals( false, $uf->db->_transOK );
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @group SQL_testTransactionNestingC3
|
||
|
*/
|
||
|
function testTransactionNestingC3() {
|
||
|
$uf = new UserFactory();
|
||
|
|
||
|
$this->assertEquals( 0, $uf->db->transCnt );
|
||
|
$this->assertEquals( 0, $uf->db->transOff );
|
||
|
$this->assertEquals( true, $uf->db->_transOK );
|
||
|
|
||
|
|
||
|
$uf->StartTransaction();
|
||
|
$uf->StartTransaction();
|
||
|
$uf->StartTransaction();
|
||
|
$uf->StartTransaction();
|
||
|
$uf->StartTransaction();
|
||
|
$uf->StartTransaction();
|
||
|
$uf->StartTransaction();
|
||
|
$uf->StartTransaction();
|
||
|
$uf->StartTransaction();
|
||
|
$uf->FailTransaction();
|
||
|
$uf->FailTransaction();
|
||
|
$uf->FailTransaction();
|
||
|
|
||
|
$uf->CommitTransaction( true ); //Unest all transactions.
|
||
|
|
||
|
$this->assertEquals( 0, $uf->db->transCnt );
|
||
|
$this->assertEquals( 0, $uf->db->transOff );
|
||
|
$this->assertEquals( false, $uf->db->_transOK );
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @group SQL_testMaximumQueryLengthA
|
||
|
*/
|
||
|
function testMaximumQueryLengthA() {
|
||
|
$ulf = new UserListFactory();
|
||
|
|
||
|
//Build list of many UUIDs.
|
||
|
for( $i = 0; $i < 500000; $i++ ) { //65535 appears to be the max number of parameters for a WHERE IN clause in PostgreSQL.
|
||
|
$ids[] = TTUUID::generateUUID();
|
||
|
}
|
||
|
|
||
|
//This should fail with an exception.
|
||
|
try {
|
||
|
$ulf->getByIdAndCompanyId( $ids, TTUUID::getZeroID() );
|
||
|
//$this->assertTrue( false, $ulf->getRecordCount() ); //This would trigger an asserting exception that would be caught in the catch() below.
|
||
|
$this->assertEquals( 0, $ulf->getRecordCount() ); //Now that we automatically convert UUID arrays to strings without placeholders in getListSQL(), we can exceed the 65535 limit.
|
||
|
} catch ( Exception $e ) {
|
||
|
$this->assertTrue( true, $e->getMessage() );
|
||
|
$this->assertGreaterThan( 10, strlen( $e->getMessage() ) );
|
||
|
$this->assertEquals( -1, $e->getCode() );
|
||
|
}
|
||
|
|
||
|
//This should succeed with still a very high number.
|
||
|
$ids = array_slice( $ids, 0, 5000 );
|
||
|
try {
|
||
|
$ulf->getByIdAndCompanyId( $ids, TTUUID::getZeroID() );
|
||
|
$this->assertTrue( true, $ulf->getRecordCount() );
|
||
|
} catch ( Exception $e ) {
|
||
|
$this->assertTrue( false, $e->getMessage() );
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
}
|