diff --git a/tests/TestApplication.php b/tests/TestApplication.php new file mode 100644 index 000000000..829f321d4 --- /dev/null +++ b/tests/TestApplication.php @@ -0,0 +1,63 @@ +removeDirectory($this->getRuntimePath()); + $this->removeDirectory($this->getAssetPath()); + } + + protected function removeDirectory($path) + { + if(is_dir($path) && ($folder=@opendir($path))!==false) + { + while($entry=@readdir($folder)) + { + if($entry[0]==='.') + continue; + $p=$path.DIRECTORY_SEPARATOR.$entry; + if(is_dir($p)) + $this->removeDirectory($p); + @unlink($p); + } + @closedir($folder); + } + } + + public function getAssetPath() + { + return dirname(__FILE__).DIRECTORY_SEPARATOR.'assets'; + } + + public function getRuntimePath() + { + return dirname(__FILE__).DIRECTORY_SEPARATOR.'runtime'; + } + + public function getBasePath() + { + return dirname(__FILE__); + } + + public function setBasePath($value) + { + } + + public function loadGlobalState() + { + parent::loadGlobalState(); + } + + public function saveGlobalState() + { + parent::saveGlobalState(); + } +} \ No newline at end of file diff --git a/tests/bootstrap.php b/tests/bootstrap.php new file mode 100644 index 000000000..05a81bcba --- /dev/null +++ b/tests/bootstrap.php @@ -0,0 +1,19 @@ +getMockForAbstractClass('CApplicationComponent',array('init','getIsInitialized'),'',NULL); + $c->expects($this->any()) + ->method('getIsInitialized') + ->will($this->returnValue(FALSE)); + $this->assertFalse($c->getIsInitialized()); + $c->init(); + $this->assertTrue($c->getIsInitialized()); + } +} diff --git a/tests/framework/base/CBehaviorTest.php b/tests/framework/base/CBehaviorTest.php new file mode 100644 index 000000000..ffe772d7e --- /dev/null +++ b/tests/framework/base/CBehaviorTest.php @@ -0,0 +1,51 @@ +attachBehavior('a',new NewBehavior); + $this->assertFalse($component->behaviorCalled); + $this->assertFalse(method_exists($component,'test')); + $this->assertEquals(2,$component->test()); + $this->assertTrue($component->behaviorCalled); + $this->setExpectedException('CException'); + $component->test2(); + } + + public function testDisableBehaviors(){ + $component=new NewComponent; + $component->attachBehavior('a',new NewBehavior); + $component->disableBehaviors(); + $this->setExpectedException('CException'); + // test should not be called since behavior is disabled + echo $component->test(); + } + + /** + * Since disableBehaviors() was called, validate() should not call beforeValidate() from behavior. + * @return void + */ + public function testDisableBehaviorsAndModels(){ + $model = new NewFormModel(); + $model->disableBehaviors(); + $model->validate(); + } + + /** + * enableBehaviors() should work after disableBehaviors(). + * @return void + */ + public function testDisableAndEnableBehaviorsAndModels(){ + $this->setExpectedException('NewBeforeValidateBehaviorException'); + $model = new NewFormModel(); + $model->disableBehaviors(); + $model->enableBehaviors(); + $model->validate(); + } +} \ No newline at end of file diff --git a/tests/framework/base/CComponentTest.php b/tests/framework/base/CComponentTest.php new file mode 100644 index 000000000..4ba0c4e86 --- /dev/null +++ b/tests/framework/base/CComponentTest.php @@ -0,0 +1,223 @@ +sender->eventHandled=true; +} + +function globalEventHandler2($event) +{ + $event->sender->eventHandled=true; + $event->handled=true; +} + +class CComponentTest extends CTestCase +{ + protected $component; + + public function setUp() + { + $this->component = new NewComponent(); + } + + public function tearDown() + { + $this->component = null; + } + + public function testHasProperty() + { + $this->assertTrue($this->component->hasProperty('Text'), "Component hasn't property Text"); + $this->assertTrue($this->component->hasProperty('text'), "Component hasn't property text"); + $this->assertFalse($this->component->hasProperty('Caption'), "Component as property Caption"); + } + + public function testCanGetProperty() + { + $this->assertTrue($this->component->canGetProperty('Text')); + $this->assertTrue($this->component->canGetProperty('text')); + $this->assertFalse($this->component->canGetProperty('Caption')); + } + + public function testCanSetProperty() + { + $this->assertTrue($this->component->canSetProperty('Text')); + $this->assertTrue($this->component->canSetProperty('text')); + $this->assertFalse($this->component->canSetProperty('Caption')); + } + + public function testGetProperty() + { + $this->assertTrue('default'===$this->component->Text); + $this->setExpectedException('CException'); + $value2=$this->component->Caption; + } + + public function testSetProperty() + { + $value='new value'; + $this->component->Text=$value; + $text=$this->component->Text; + $this->assertTrue($value===$this->component->Text); + $this->setExpectedException('CException'); + $this->component->NewMember=$value; + } + + public function testIsset() + { + $this->assertTrue(isset($this->component->Text)); + $this->assertTrue(!empty($this->component->Text)); + + unset($this->component->Text); + $this->assertFalse(isset($this->component->Text)); + $this->assertFalse(!empty($this->component->Text)); + + $this->component->Text=''; + $this->assertTrue(isset($this->component->Text)); + $this->assertTrue(empty($this->component->Text)); + } + + public function testHasEvent() + { + $this->assertTrue($this->component->hasEvent('OnMyEvent')); + $this->assertTrue($this->component->hasEvent('onmyevent')); + $this->assertFalse($this->component->hasEvent('onYourEvent')); + } + + public function testHasEventHandler() + { + $this->assertFalse($this->component->hasEventHandler('OnMyEvent')); + $this->component->attachEventHandler('OnMyEvent','foo'); + $this->assertTrue($this->component->hasEventHandler('OnMyEvent')); + } + + public function testGetEventHandlers() + { + $list=$this->component->getEventHandlers('OnMyEvent'); + $this->assertEquals($list->getCount(),0); + $this->component->attachEventHandler('OnMyEvent','foo'); + $this->assertEquals($list->getCount(),1); + $this->setExpectedException('CException'); + $list=$this->component->getEventHandlers('YourEvent'); + } + + public function testAttachEventHandler() + { + $this->component->attachEventHandler('OnMyEvent','foo'); + $this->assertTrue($this->component->getEventHandlers('OnMyEvent')->getCount()===1); + $this->setExpectedException('CException'); + $this->component->attachEventHandler('YourEvent','foo'); + } + + public function testDettachEventHandler() + { + $this->component->attachEventHandler('OnMyEvent','foo'); + $this->component->attachEventHandler('OnMyEvent',array($this->component,'myEventHandler')); + $this->assertEquals($this->component->getEventHandlers('OnMyEvent')->getCount(),2); + + $this->assertTrue($this->component->detachEventHandler('OnMyEvent','foo')); + $this->assertEquals($this->component->getEventHandlers('OnMyEvent')->getCount(),1); + + $this->assertFalse($this->component->detachEventHandler('OnMyEvent','foo')); + $this->assertEquals($this->component->getEventHandlers('OnMyEvent')->getCount(),1); + + $this->assertTrue($this->component->detachEventHandler('OnMyEvent',array($this->component,'myEventHandler'))); + $this->assertEquals($this->component->getEventHandlers('OnMyEvent')->getCount(),0); + + $this->assertFalse($this->component->detachEventHandler('OnMyEvent','foo')); + } + + public function testRaiseEvent() + { + $this->component->attachEventHandler('OnMyEvent',array($this->component,'myEventHandler')); + $this->assertFalse($this->component->eventHandled); + $this->component->raiseEvent('OnMyEvent',new CEvent($this)); + $this->assertTrue($this->component->eventHandled); + + //$this->setExpectedException('CException'); + //$this->component->raiseEvent('OnUnknown',new CEvent($this)); + } + + public function testEventAccessor() + { + $component=new NewComponent; + $this->assertEquals($component->onMyEvent->getCount(),0); + $component->onMyEvent='globalEventHandler'; + $component->onMyEvent=array($this->component,'myEventHandler'); + $this->assertEquals($component->onMyEvent->getCount(),2); + $this->assertFalse($component->eventHandled); + $this->assertFalse($this->component->eventHandled); + $component->onMyEvent(); + $this->assertTrue($component->eventHandled); + $this->assertTrue($this->component->eventHandled); + } + + public function testStopEvent() + { + $component=new NewComponent; + $component->onMyEvent='globalEventHandler2'; + $component->onMyEvent=array($this->component,'myEventHandler'); + $component->onMyEvent(); + $this->assertTrue($component->eventHandled); + $this->assertFalse($this->component->eventHandled); + } + + public function testInvalidHandler1() + { + $this->component->onMyEvent=array(1,2,3); + $this->setExpectedException('CException'); + $this->component->onMyEvent(); + } + + public function testInvalidHandler2() + { + $this->component->onMyEvent=array($this->component,'nullHandler'); + $this->setExpectedException('CException'); + $this->component->onMyEvent(); + } + public function testDetachBehavior() { + $component=new NewComponent; + $behavior = new NewBehavior; + $component->attachBehavior('a',$behavior); + $this->assertSame($behavior,$component->detachBehavior('a')); + } + public function testDetachingBehaviors() { + $component=new NewComponent; + $behavior = new NewBehavior; + $component->attachBehavior('a',$behavior); + $component->detachBehaviors(); + $this->setExpectedException('CException'); + $component->test(); + } + public function testEnablingBehavior() { + $component=new NewComponent; + $behavior = new NewBehavior; + $component->attachBehavior('a',$behavior); + $component->disableBehavior('a'); + $this->assertFalse($behavior->getEnabled()); + $component->enableBehavior('a'); + $this->assertTrue($behavior->getEnabled()); + } + public function testEnablingBehaviors() { + $component=new NewComponent; + $behavior = new NewBehavior; + $component->attachBehavior('a',$behavior); + $component->disableBehaviors(); + $this->assertFalse($behavior->getEnabled()); + $component->enableBehaviors(); + $this->assertTrue($behavior->getEnabled()); + } + public function testAsa() { + $component=new NewComponent; + $behavior = new NewBehavior; + $component->attachBehavior('a',$behavior); + $this->assertSame($behavior,$component->asa('a')); + } + public function testEvaluateExpression() { + $component = new NewComponent; + $this->assertEquals('Hello world',$component->evaluateExpression('"Hello $who"',array('who' => 'world'))); + $this->assertEquals('Hello world',$component->evaluateExpression(array($component,'exprEvaluator'),array('who' => 'world'))); + } +} diff --git a/tests/framework/base/CLogRouterTest.php b/tests/framework/base/CLogRouterTest.php new file mode 100644 index 000000000..048b08a32 --- /dev/null +++ b/tests/framework/base/CLogRouterTest.php @@ -0,0 +1,54 @@ +logCollected=true; + $this->property+=count($logs); + } +} + +class CLogRouterTest extends CTestCase +{ + public function testRoutes() + { + $app=new TestApplication; + $router=new CLogRouter; + + $this->assertEquals(count($router->routes),0); + $router->routes=array( + array( + 'class'=>'MyRoute', + 'property'=>2, + ), + array( + 'class'=>'MyRoute', + 'property'=>3, + ), + ); + $router->init($app); + $this->assertEquals(count($router->routes),2); + + $route1=$router->routes[0]; + $this->assertFalse($route1->logCollected); + $this->assertEquals($route1->property,2); + $route2=$router->routes[1]; + $this->assertFalse($route2->logCollected); + $this->assertEquals($route2->property,3); + + $logger=Yii::getLogger(); + $logger->log('message1','level1','category1'); + $logger->log('message2','level2','category2'); + $logger->log('message3','level3','category3'); + + $app->onEndRequest(new CEvent($this)); + $this->assertTrue($route1->logCollected); + $this->assertTrue($route1->property>2); + $this->assertTrue($route2->logCollected); + $this->assertTrue($route2->property>3); + } +} diff --git a/tests/framework/base/CLoggerTest.php b/tests/framework/base/CLoggerTest.php new file mode 100644 index 000000000..cfa7ca4f9 --- /dev/null +++ b/tests/framework/base/CLoggerTest.php @@ -0,0 +1,49 @@ +log('something','debug','application.test'); + } + + public function testGetLogs() + { + $logger=new CLogger(); + $logs=array( + array('message1','debug','application.pages'), + array('message2','info','application.config'), + array('message3','info','application.pages'), + ); + foreach($logs as $log) + $logger->log($log[0],$log[1],$log[2]); + + $l=$logger->getLogs('debug'); + $this->assertTrue($logs[0]===array_slice($l[0],0,3)); + + $l=$logger->getLogs('debug , Info'); + $this->assertTrue($logs[0]===array_slice($l[0],0,3)); + $this->assertTrue($logs[1]===array_slice($l[1],0,3)); + $this->assertTrue($logs[2]===array_slice($l[2],0,3)); + + $l=$logger->getLogs('','application.config'); + $this->assertTrue($logs[1]===array_slice($l[0],0,3)); + + $l=$logger->getLogs('','application.*'); + $this->assertTrue($logs[0]===array_slice($l[0],0,3)); + $this->assertTrue($logs[1]===array_slice($l[1],0,3)); + $this->assertTrue($logs[2]===array_slice($l[2],0,3)); + + $l=$logger->getLogs('','application.config , Application.pages'); + $this->assertTrue($logs[0]===array_slice($l[0],0,3)); + $this->assertTrue($logs[1]===array_slice($l[1],0,3)); + $this->assertTrue($logs[2]===array_slice($l[2],0,3)); + + $l=$logger->getLogs('info','application.config'); + $this->assertTrue($logs[1]===array_slice($l[0],0,3)); + + $l=$logger->getLogs('info,debug','application.config'); + $this->assertTrue($logs[1]===array_slice($l[0],0,3)); + } +} diff --git a/tests/framework/base/CModelTest.php b/tests/framework/base/CModelTest.php new file mode 100644 index 000000000..4073d5ace --- /dev/null +++ b/tests/framework/base/CModelTest.php @@ -0,0 +1,122 @@ +assertFalse($model->validate()); + $model->attr1=4; + $this->assertTrue($model->validate()); + $model->attr1=6; + $this->assertFalse($model->validate()); + $model->attr2=6; + $this->assertFalse($model->validate()); + $model->attr1=4; + $this->assertFalse($model->validate()); + $model->attr2=4; + $this->assertTrue($model->validate()); + } + + function testBeforeValidate(){ + $model=new NewModel; + $model->attr1=4; + $this->assertTrue($model->validate()); + + $model->onBeforeValidate = array($this, 'beforeValidate'); + $this->assertFalse($model->validate()); + } + + function beforeValidate($event){ + $event->isValid = false; + } + + function testIsAttributeRequired(){ + $model=new NewModel; + $this->assertTrue($model->isAttributeRequired('attr1')); + $this->assertFalse($model->isAttributeRequired('attr2')); + } + + function testIsAttributeSafe(){ + $model=new NewModel; + $this->assertTrue($model->isAttributeSafe('attr1')); + $this->assertFalse($model->isAttributeSafe('attr3')); + $this->assertFalse($model->isAttributeSafe('attr4')); + } + + function testGetSafeAttributeNames(){ + $model=new NewModel; + $safeAttributes = $model->getSafeAttributeNames(); + $this->assertContains('attr2', $safeAttributes); + $this->assertContains('attr1', $safeAttributes); + } + + public function testModifyValidators() + { + $model=new NewModel; + $model->attr1=2; + $model->attr2=2; + $this->assertTrue($model->validate()); + $model->validatorList->insertAt(0,CValidator::createValidator('numerical',$model,'attr1,attr2',array('min'=>3))); + $this->assertFalse($model->validate()); + $model->attr1=6; + $model->attr2=6; + $this->assertFalse($model->validate()); + $model->attr1=4; + $model->attr2=4; + $this->assertTrue($model->validate()); + $model=new NewModel; + $model->attr1=3; + $model->validatorList->add(CValidator::createValidator('required',$model,'attr2',array())); + $this->assertFalse($model->validate()); + $model->attr2=3; + $this->assertTrue($model->validate()); + } + + function testErrors(){ + $model=new NewModel; + $model->attr1=3; + $model->validatorList->add(CValidator::createValidator('required',$model,'attr2',array())); + $model->validatorList->add(CValidator::createValidator('required',$model,'attr4',array())); + $model->validate(); + + $this->assertTrue($model->hasErrors()); + $this->assertTrue($model->hasErrors('attr2')); + $this->assertFalse($model->hasErrors('attr1')); + + $model->clearErrors('attr2'); + $this->assertFalse($model->hasErrors('attr2')); + + $model->clearErrors(); + $this->assertFalse($model->hasErrors()); + } + + function testGetAttributes(){ + $model = new NewModel(); + $model->attr1 = 1; + $model->attr2 = 2; + + $attributes = $model->getAttributes(); + $this->assertEquals(1, $attributes['attr1']); + $this->assertEquals(2, $attributes['attr2']); + + $attributes = $model->getAttributes(array('attr1', 'non_existing')); + $this->assertEquals(1, $attributes['attr1']); + $this->assertEquals(null, $attributes['non_existing']); + } + + function testUnsetAttributes(){ + $model = new NewModel(); + $model->attr1 = 1; + $model->attr2 = 2; + + $model->unsetAttributes(array('attr1')); + $this->assertEquals(null, $model->attr1); + $this->assertEquals(2, $model->attr2); + + $model->unsetAttributes(); + $this->assertEquals(null, $model->attr1); + $this->assertEquals(null, $model->attr2); + } +} \ No newline at end of file diff --git a/tests/framework/base/CModuleTest.php b/tests/framework/base/CModuleTest.php new file mode 100644 index 000000000..18b3a7f68 --- /dev/null +++ b/tests/framework/base/CModuleTest.php @@ -0,0 +1,95 @@ +parent = new NewModule('root',NULL); + $this->mod = new NewModule('foo',$this->parent); + $this->d = dirname(__FILE__); + } + public function tearDown() { + unset($this->parent); + unset($this->mod); + } + public function testGetId() { + $this->assertEquals('foo',$this->mod->getId()); + } + public function testSetId() { + $this->mod->setId('bar'); + $this->assertEquals('bar',$this->mod->getId()); + } + public function testSetBasePath() { + $d = dirname($this->d.'/..'); + $this->mod->setBasePath($d); + $this->assertEquals($d,$this->mod->getBasePath()); + } + public function testGetBasePath() { + $this->assertEquals($this->d,$this->mod->getBasePath()); + } + public function testGetParams() { + $expected = new CAttributeCollection; + $expected->caseSensitive = TRUE; + $this->assertEquals($expected,$this->mod->getParams()); + } + public function testSetParams() { + $expected = array('foo' => 'bar'); + $this->mod->setParams($expected); + $this->assertEquals($expected,$this->mod->getParams()->toArray()); + } + public function testGetModulePath() { + $expected = $this->d.DIRECTORY_SEPARATOR.'modules'; + $this->assertEquals($expected,$this->mod->getModulePath()); + } + public function testSetModulePath() { + $this->mod->setModulePath($this->d); + $this->assertEquals($this->d,$this->mod->getModulePath()); + } + public function testGetParentModule() { + $this->assertSame($this->parent,$this->mod->getParentModule()); + } + /** + * @depends testGetId + */ + public function testGetModule() { + $p = $this->parent; + $p->setModulePath($this->d); + $p->setModules(array('foo' => array('class' => 'NewModule'))); + $this->assertEquals('root/foo',$p->getModule('foo')->getId()); + } + public function testGetModules() { + $p = $this->parent; + $p->setModulePath($this->d); + $expected = array('foo' => array('class' => 'NewModule'),'bar'); + $p->setModules($expected); + $expected['bar'] = array('class' => 'bar.BarModule'); + unset($expected[0]); + $this->assertEquals($expected,$p->getModules()); + } + public function testGetComponents() { + $c = new NewApplicationComponent; + $this->mod->setComponent('foo',$c); + $this->assertSame(array('foo' => $c),$this->mod->getComponents()); + } + public function testSetComponents() { + $expected = array('foo' => new NewApplicationComponent); + $this->mod->setComponents($expected); + $this->assertSame($expected,$this->mod->getComponents()); + } + public function testSetComponentsViaConfig() { + $this->mod = new NewModule('foo',$this->parent,array( + 'components' => array( + 'bar' => array('class' => 'NewApplicationComponent') + ) + )); + $this->assertEquals('hello world',$this->mod->bar->getText('hello world')); + } + public function testSetAliases() { + $this->mod->setAliases(array('modules' => $this->d)); + $this->assertEquals($this->d,Yii::getPathOfAlias('modules')); + } +} diff --git a/tests/framework/base/CPropertyValueTest.php b/tests/framework/base/CPropertyValueTest.php new file mode 100644 index 000000000..1a654019d --- /dev/null +++ b/tests/framework/base/CPropertyValueTest.php @@ -0,0 +1,138 @@ +$entry) + $this->assertTrue(CPropertyValue::ensureBoolean($entry[0])===$entry[1], + "Comparison $index: {$entry[0]}=={$entry[1]}"); + } + + public function testEnsureString() + { + $entries=array + ( + array('',''), + array('abc','abc'), + array(null,''), + array(0,'0'), + array(1,'1'), + array(-1.1,'-1.1'), + array(true,'true'), + array(false,'false'), + array(array(),'Array'), + array(array(0),'Array'), + ); + foreach($entries as $index=>$entry) + $this->assertTrue(CPropertyValue::ensureString($entry[0])===$entry[1], + "Comparison $index: {$entry[0]}=={$entry[1]}"); + } + + public function testEnsureInteger() + { + $entries=array + ( + array(123,123), + array(1.23,1), + array(null,0), + array('',0), + array('abc',0), + array('123',123), + array('1.23',1), + array(' 1.23',1), + array(' 1.23abc',1), + array('abc1.23abc',0), + array(true,1), + array(false,0), + array(array(),0), + array(array(0),1), + ); + foreach($entries as $index=>$entry) + $this->assertTrue(CPropertyValue::ensureInteger($entry[0])===$entry[1], + "Comparison $index: {$entry[0]}=={$entry[1]}"); + } + + public function testEnsureFloat() + { + $entries=array + ( + array(123,123.0), + array(1.23,1.23), + array(null,0.0), + array('',0.0), + array('abc',0.0), + array('123',123.0), + array('1.23',1.23), + array(' 1.23',1.23), + array(' 1.23abc',1.23), + array('abc1.23abc',0.0), + array(true,1.0), + array(false,0.0), + array(array(),0.0), + array(array(0),1.0), + ); + foreach($entries as $index=>$entry) + $this->assertTrue(CPropertyValue::ensureFloat($entry[0])===$entry[1], + "Comparison $index: {$entry[0]}=={$entry[1]}"); + } + + public function testEnsureArray() + { + $entries=array + ( + array(123,array(123)), + array(null,array()), + array('',array()), + array('abc',array('abc')), + array('(1,2)',array(1,2)), + array('("key"=>"value",2=>3)',array("key"=>"value",2=>3)), + array(true,array(true)), + array(array(),array()), + array(array(0),array(0)), + ); + foreach($entries as $index=>$entry) + $this->assertTrue(CPropertyValue::ensureArray($entry[0])===$entry[1], + "Comparison $index: {$entry[0]}=={$entry[1]}"); + } + + public function testEnsureObject() + { + $obj=new stdClass; + $this->assertTrue(CPropertyValue::ensureObject($obj)===$obj); + } + + public function testEnsureEnum() + { + $this->assertTrue(CPropertyValue::ensureEnum('Left','TextAlign')==='Left'); + $this->setExpectedException('CException'); + $this->assertTrue(CPropertyValue::ensureEnum('left','TextAlign')==='Left'); + } +} diff --git a/tests/framework/base/CSecurityManagerTest.php b/tests/framework/base/CSecurityManagerTest.php new file mode 100644 index 000000000..3179d5695 --- /dev/null +++ b/tests/framework/base/CSecurityManagerTest.php @@ -0,0 +1,79 @@ +reset(); + } + + public function testValidationKey() + { + $sm=new CSecurityManager; + $key='123456'; + $sm->validationKey=$key; + $this->assertEquals($key,$sm->validationKey); + + $app=new TestApplication; + $key=$app->securityManager->validationKey; + $app->saveGlobalState(); + $app2=new TestApplication; + $this->assertEquals($app2->securityManager->validationKey,$key); + } + + public function testEncryptionKey() + { + $sm=new CSecurityManager; + $key='123456'; + $sm->encryptionKey=$key; + $this->assertEquals($key,$sm->encryptionKey); + + $app=new TestApplication; + $key=$app->securityManager->encryptionKey; + $app->saveGlobalState(); + $app2=new TestApplication; + $this->assertEquals($app2->securityManager->encryptionKey,$key); + } + + public function testValidation() + { + $sm=new CSecurityManager; + $mode='SHA1'; + $sm->validation=$mode; + $this->assertEquals($mode,$sm->validation); + } + + public function testValidateData() + { + $sm=new CSecurityManager; + $sm->validationKey='123456'; + $sm->validation='SHA1'; + $data='this is raw data'; + $hashedData=$sm->hashData($data); + $this->assertEquals($data,$sm->validateData($hashedData)); + $hashedData[3]='c'; // tamper the data + $this->assertTrue($sm->validateData($hashedData)===false); + + $sm->validation='MD5'; + $data='this is raw data'; + $hashedData=$sm->hashData($data); + $this->assertEquals($data,$sm->validateData($hashedData)); + $hashedData[3]='c'; // tamper the data + $this->assertTrue($sm->validateData($hashedData)===false); + } + + public function testEncryptData() + { + if(!extension_loaded('mcrypt')) + $this->markTestSkipped('mcrypt extension is required to test encrypt feature.'); + $sm=new CSecurityManager; + $sm->encryptionKey='123456'; + $data='this is raw data'; + $encryptedData=$sm->encrypt($data); + $this->assertTrue($data!==$encryptedData); + $data2=$sm->decrypt($encryptedData); + $this->assertEquals($data,$data2); + } +} diff --git a/tests/framework/base/CStatePersisterTest.php b/tests/framework/base/CStatePersisterTest.php new file mode 100644 index 000000000..b7cf379fd --- /dev/null +++ b/tests/framework/base/CStatePersisterTest.php @@ -0,0 +1,33 @@ +reset(); + } + + public function testLoadSave() + { + $app=new TestApplication; + $sp=$app->statePersister; + $data=array('123','456','a'=>443); + $sp->save($data); + $this->assertEquals($sp->load(),$data); + // TODO: test with cache on + } + + public function testStateFile() + { + $sp=new CStatePersister; + $file=dirname(__FILE__).DIRECTORY_SEPARATOR.'state.bin'; + $sp->stateFile=$file; + $this->assertEquals($sp->stateFile,$file); + + $sp->stateFile=dirname(__FILE__).'/unknown/state.bin'; + $this->setExpectedException('CException'); + $sp->init(); + } +} diff --git a/tests/framework/base/NewApplicationComponent.php b/tests/framework/base/NewApplicationComponent.php new file mode 100644 index 000000000..ff548e6f5 --- /dev/null +++ b/tests/framework/base/NewApplicationComponent.php @@ -0,0 +1,13 @@ +_text; + } + return $text; + } + public function setText($val) { + $this->_text = $val; + } +} diff --git a/tests/framework/base/NewBeforeValidateBehavior.php b/tests/framework/base/NewBeforeValidateBehavior.php new file mode 100644 index 000000000..b90467d85 --- /dev/null +++ b/tests/framework/base/NewBeforeValidateBehavior.php @@ -0,0 +1,8 @@ +owner->behaviorCalled=true; + return 2; + } +} diff --git a/tests/framework/base/NewComponent.php b/tests/framework/base/NewComponent.php new file mode 100644 index 000000000..94a825c37 --- /dev/null +++ b/tests/framework/base/NewComponent.php @@ -0,0 +1,41 @@ +_text; + } + + public function setText($value) + { + $this->_text=$value; + } + + public function getObject() + { + if(!$this->_object) + { + $this->_object=new NewComponent; + $this->_object->_text='object text'; + } + return $this->_object; + } + + public function onMyEvent() + { + $this->raiseEvent('OnMyEvent',new CEvent($this)); + } + + public function myEventHandler($event) + { + $this->eventHandled=true; + } + public function exprEvaluator($p1,$comp) { + return "Hello $p1"; + } +} diff --git a/tests/framework/base/NewFormModel.php b/tests/framework/base/NewFormModel.php new file mode 100644 index 000000000..b1ee5dd6f --- /dev/null +++ b/tests/framework/base/NewFormModel.php @@ -0,0 +1,10 @@ + array( + 'class' => 'NewBeforeValidateBehavior', + ), + ); + } +} \ No newline at end of file diff --git a/tests/framework/base/NewModel.php b/tests/framework/base/NewModel.php new file mode 100644 index 000000000..e9a029352 --- /dev/null +++ b/tests/framework/base/NewModel.php @@ -0,0 +1,23 @@ +5), + array('attr1','required'), + array('attr3', 'unsafe'), + ); + } + + public function attributeNames() + { + return array('attr1','attr2'); + } +} \ No newline at end of file diff --git a/tests/framework/base/NewModule.php b/tests/framework/base/NewModule.php new file mode 100644 index 000000000..42ca8dfd3 --- /dev/null +++ b/tests/framework/base/NewModule.php @@ -0,0 +1,4 @@ +'testApp', + 'components'=>array( + 'cache'=>array( + 'class'=>'CDbCache', + ), + ), + ); + private $_config2=array( + 'id'=>'testApp', + 'components'=>array( + 'db'=>array( + 'connectionString'=>DBCACHE_TEST_DB, + ), + 'cache'=>array( + 'class'=>'system.caching.CDbCache', + 'connectionID'=>'db', + ), + ), + ); + + public function setUp() + { + if(!extension_loaded('pdo') || !extension_loaded('pdo_sqlite')) + $this->markTestSkipped('PDO and SQLite extensions are required.'); + } + + public function testKeyPrefix() + { + $cache=new CDbCache; + $this->assertEquals($cache->keyPrefix,''); + $cache->keyPrefix='key'; + $this->assertEquals($cache->keyPrefix,'key'); + + $app=new TestApplication($this->_config1); + $app->reset(); + $this->assertTrue($app->cache instanceof CDbCache); + $this->assertEquals($app->cache->keyPrefix,$app->id); + } + + public function testGetAndSet() + { + $app=new TestApplication($this->_config1); + $app->reset(); + $cache=$app->cache; + + $data=array('abc'=>1,2=>'def'); + $key='data1'; + + $this->assertFalse($cache->get($key)); + $cache->set($key,$data); + $this->assertTrue($cache->get($key)===$data); + + $app2=new TestApplication($this->_config1); + $this->assertTrue($app2->cache->get($key)===$data); + } + + public function testArrayAccess() + { + $app=new TestApplication($this->_config1); + $app->reset(); + $cache=$app->cache; + $data=array('abc'=>1,2=>'def'); + $key='data2'; + $cache[$key]=$data; + $this->assertTrue($cache->get($key)===$data); + $this->assertTrue($cache[$key]===$data); + unset($cache[$key]); + $this->assertFalse($cache[$key]); + } + + public function testExpire() + { + $app=new TestApplication($this->_config1); + $app->reset(); + $cache=$app->cache; + $data=array('abc'=>1,2=>'def'); + $key='data3'; + $cache->set($key,$data,2); + $this->assertTrue($cache->get($key)===$data); + sleep(4); + $app2=new TestApplication($this->_config1); + $this->assertFalse($app2->cache->get($key)); + } + + public function testUseDbConnection() + { + @unlink(DBCACHE_TEST_DBFILE); + $app=new TestApplication($this->_config2); + $cache=$app->cache; + + $data=array('abc'=>1,2=>'def'); + $key='data4'; + $this->assertFalse($cache->get($key)); + $cache->set($key,$data); + $this->assertTrue($cache->get($key)===$data); + + $app2=new TestApplication($this->_config2); + $this->assertTrue($app2->cache->get($key)===$data); + } + + public function testDependency() + { + @unlink(DBCACHE_TEST_DBFILE); + $app=new TestApplication($this->_config2); + $cache=$app->cache; + + $data=array('abc'=>1,2=>'def'); + $key='data5'; + + $cache->set($key,$data,0,new CFileCacheDependency(__FILE__)); + $this->assertTrue($cache->get($key)===$data); + $app=new TestApplication($this->_config2); + $this->assertTrue($app->cache->get($key)===$data); + + $key2='data6'; + $cache->set($key2,$data,0,new CFileCacheDependency(DBCACHE_TEST_DBFILE)); + sleep(2); // sleep to ensure timestamp is changed for the db file + $cache->set('data7',$data); + $app=new TestApplication($this->_config2); + $this->assertFalse($app->cache->get($key2)); + } + + public function testAdd() + { + @unlink(DBCACHE_TEST_DBFILE); + $app=new TestApplication($this->_config2); + $cache=$app->cache; + + $data=array('abc'=>1,2=>'def'); + $key='data8'; + + $cache->set($key,$data); + $this->assertTrue($cache->set($key,$data)); + $this->assertFalse($cache->add($key,$data)); + $this->assertTrue($cache->add('data9',$data)); + } + + public function testDelete() + { + @unlink(DBCACHE_TEST_DBFILE); + $app=new TestApplication($this->_config2); + $cache=$app->cache; + + $data=array('abc'=>1,2=>'def'); + $key='data10'; + $cache->set($key,$data); + $cache->delete($key); + $this->assertFalse($cache->get($key)); + } + + public function testFlush() + { + @unlink(DBCACHE_TEST_DBFILE); + $app=new TestApplication($this->_config2); + $cache=$app->cache; + + $data=array('abc'=>1,2=>'def'); + $key1='data11'; + $key2='data12'; + $cache->set($key1,$data); + $cache->set($key2,$data); + $cache->flush(); + $this->assertFalse($cache->get($key1)); + $this->assertFalse($cache->get($key2)); + } +} \ No newline at end of file diff --git a/tests/framework/caching/CDirectoryCacheDependencyTest.php b/tests/framework/caching/CDirectoryCacheDependencyTest.php new file mode 100644 index 000000000..85099f9c5 --- /dev/null +++ b/tests/framework/caching/CDirectoryCacheDependencyTest.php @@ -0,0 +1,61 @@ +assertEquals($dependency->directory,$directory); + + $this->setExpectedException('CException'); + $dependency=new CDirectoryCacheDependency(dirname(__FILE__).'/temp2'); + $dependency->evaluateDependency(); + } + + public function testRecursiveLevel() + { + $directory=realpath(dirname(__FILE__).'/temp'); + $dependency=new CDirectoryCacheDependency(dirname(__FILE__).'/temp'); + $this->assertEquals($dependency->recursiveLevel,-1); + $dependency->recursiveLevel=5; + $this->assertEquals($dependency->recursiveLevel,5); + } + + public function testHasChanged() + { + $tempFile=dirname(__FILE__).'/temp/foo.txt'; + @unlink($tempFile); + $fw=fopen($tempFile,"w"); + fwrite($fw,"test"); + fclose($fw); + clearstatcache(); + + $dependency=new CDirectoryCacheDependency(dirname($tempFile)); + $dependency->evaluateDependency(); + $str=serialize($dependency); + + // test directory not changed + sleep(2); + $dependency=unserialize($str); + $this->assertFalse($dependency->hasChanged); + + // change file + $fw=fopen($tempFile,"w"); + fwrite($fw,"test again"); + fclose($fw); + clearstatcache(); + + // test file changed + sleep(2); + $dependency->evaluateDependency(); + $dependency=unserialize($str); + $this->assertTrue($dependency->hasChanged); + + @unlink($tempFile); + } +} + +?> \ No newline at end of file diff --git a/tests/framework/caching/CFileCacheDependencyTest.php b/tests/framework/caching/CFileCacheDependencyTest.php new file mode 100644 index 000000000..085c2bfec --- /dev/null +++ b/tests/framework/caching/CFileCacheDependencyTest.php @@ -0,0 +1,53 @@ +assertEquals($dependency->fileName,__FILE__); + $dependency->evaluateDependency(); + $this->assertEquals($dependency->dependentData,filemtime(__FILE__)); + + $dependency=new CFileCacheDependency(dirname(__FILE__).'/foo.txt'); + $dependency->evaluateDependency(); + $this->assertFalse($dependency->dependentData); + } + + public function testHasChanged() + { + $tempFile=dirname(__FILE__).'/temp/foo.txt'; + @unlink($tempFile); + $fw=fopen($tempFile,"w"); + fwrite($fw,"test"); + fclose($fw); + clearstatcache(); + + $dependency=new CFileCacheDependency($tempFile); + $dependency->evaluateDependency(); + $str=serialize($dependency); + + // test file not changed + sleep(2); + $dependency=unserialize($str); + $this->assertFalse($dependency->hasChanged); + + // change file + $fw=fopen($tempFile,"w"); + fwrite($fw,"test again"); + fclose($fw); + clearstatcache(); + + // test file changed + sleep(2); + $dependency->evaluateDependency(); + $dependency=unserialize($str); + $this->assertTrue($dependency->hasChanged); + + @unlink($tempFile); + } +} + +?> \ No newline at end of file diff --git a/tests/framework/caching/CMemCacheTest.php b/tests/framework/caching/CMemCacheTest.php new file mode 100644 index 000000000..6c12b82ea --- /dev/null +++ b/tests/framework/caching/CMemCacheTest.php @@ -0,0 +1,141 @@ +'testApp', + 'components'=>array( + 'cache'=>array( + 'class'=>'CMemCache', + 'servers'=>array( + array('host'=>MEMCACHE_TEST_HOST, 'port'=>MEMCACHE_TEST_PORT, 'weight'=>100), + ), + ), + ), + + ); + + public function setUp() + { + if(!extension_loaded('memcache') && !extension_loaded('memcached')) + $this->markTestSkipped('Memcache or memcached extensions are required.'); + } + + public function testMget() + { + $app=new TestApplication($this->_config); + $app->reset(); + $cache=$app->cache; + + $data1=array('abc'=>1,2=>'def'); + $key1='data1'; + $data2=array('xyz'=>3,4=>'whn'); + $key2='data2'; + + $cache->delete($key1); + $cache->delete($key2); + + $this->assertFalse($cache->get($key1)); + $this->assertFalse($cache->get($key2)); + + $cache->set($key1,$data1); + $cache->set($key2,$data2); + $this->assertTrue($cache->get($key1)===$data1); + $this->assertTrue($cache->get($key2)===$data2); + + $mgetResult = $cache->mget(array($key1, $key2)); + $this->assertTrue(is_array($mgetResult)); + $this->assertEquals($mgetResult[$key1],$data1); + $this->assertEquals($mgetResult[$key2],$data2); + + $cache->delete($key2); + $mgetResult = $cache->mget(array($key1, $key2)); + $this->assertTrue(is_array($mgetResult)); + $this->assertEquals($mgetResult[$key1],$data1); + $this->assertFalse($mgetResult[$key2]); // data2 is removed from cache + + $cache->delete($key1); + $mgetResult = $cache->mget(array($key1, $key2)); + $this->assertTrue(is_array($mgetResult)); + $this->assertFalse($mgetResult[$key1]); // data1 is removed from cache + $this->assertFalse($mgetResult[$key2]); // data2 is removed from cache + } + + public function testKeyPrefix() + { + $cache=new CMemCache; + $this->assertEquals($cache->keyPrefix,''); + $cache->keyPrefix='key'; + $this->assertEquals($cache->keyPrefix,'key'); + + $app=new TestApplication($this->_config); + $app->reset(); + $this->assertTrue($app->cache instanceof CMemCache); + $this->assertEquals($app->cache->keyPrefix,$app->id); + } + + public function testGetAndSet() + { + $app=new TestApplication($this->_config); + $app->reset(); + $cache=$app->cache; + + $data=array('abc'=>1,2=>'def'); + $key='data1'; + $cache->delete($key); + + $this->assertFalse($cache->get($key)); + $cache->set($key,$data); + $this->assertTrue($cache->get($key)===$data); + + $app2=new TestApplication($this->_config); + $this->assertTrue($app2->cache->get($key)===$data); + } + + public function testArrayAccess() + { + $app=new TestApplication($this->_config); + $app->reset(); + $cache=$app->cache; + $data=array('abc'=>1,2=>'def'); + $key='data2'; + $cache[$key]=$data; + $this->assertTrue($cache->get($key)===$data); + $this->assertTrue($cache[$key]===$data); + unset($cache[$key]); + $this->assertFalse($cache[$key]); + } + + public function testExpire() + { + $app=new TestApplication($this->_config); + $app->reset(); + $cache=$app->cache; + $data=array('abc'=>1,2=>'def'); + $key='data3'; + $cache->set($key,$data,2); + $this->assertTrue($cache->get($key)===$data); + sleep(4); + $app2=new TestApplication($this->_config); + $this->assertFalse($app2->cache->get($key)); + } + + public function testDelete() + { + $app=new TestApplication($this->_config); + $cache=$app->cache; + + $data=array('abc'=>1,2=>'def'); + $key='data10'; + $cache->set($key,$data); + $cache->delete($key); + $this->assertFalse($cache->get($key)); + } +} \ No newline at end of file diff --git a/tests/framework/collections/CAttributeCollectionTest.php b/tests/framework/collections/CAttributeCollectionTest.php new file mode 100644 index 000000000..8dd99fe54 --- /dev/null +++ b/tests/framework/collections/CAttributeCollectionTest.php @@ -0,0 +1,106 @@ +Property = 'value'; + $this->assertEquals('value', $collection->Property); + $this->assertEquals(true, $collection->canGetProperty('Property')); + } + + public function testCanNotGetUndefinedProperty() + { + $collection = new CAttributeCollection(array(), true); + $this->assertEquals(false, $collection->canGetProperty('Property')); + $this->setExpectedException('CException'); + $value=$collection->Property; + } + + public function testCanSetProperty() + { + $collection = new CAttributeCollection(); + $collection->Property = 'value'; + $this->assertEquals('value', $collection->itemAt('Property')); + $this->assertEquals(true, $collection->canSetProperty('Property')); + } + + public function testCanNotSetPropertyIfReadOnly() + { + $collection = new CAttributeCollection(array(), true); + $this->setExpectedException('CException'); + $collection->Property = 'value'; + } + + public function testGetCaseSensitive() + { + $collection = new CAttributeCollection(); + $collection->caseSensitive=false; + $this->assertEquals(false, $collection->caseSensitive); + $collection->caseSensitive=true; + $this->assertEquals(true, $collection->caseSensitive); + } + + public function testSetCaseSensitive() + { + $collection = new CAttributeCollection(); + $collection->Property = 'value'; + $collection->caseSensitive=false; + $this->assertEquals('value', $collection->itemAt('property')); + } + + public function testItemAt() + { + $collection = new CAttributeCollection(); + $collection->Property = 'value'; + $this->assertEquals('value', $collection->itemAt('Property')); + } + + public function testAdd() + { + $collection = new CAttributeCollection(); + $collection->add('Property', 'value'); + $this->assertEquals('value', $collection->itemAt('Property')); + } + + public function testRemove() + { + $collection = new CAttributeCollection(); + $collection->add('Property', 'value'); + $collection->remove('Property'); + $this->assertEquals(0, count($collection)); + } + + public function testUnset(){ + $collection = new CAttributeCollection(); + $collection->add('Property', 'value'); + unset($collection->Property); + $this->assertEquals(0, count($collection)); + } + + public function testIsset(){ + $collection = new CAttributeCollection(); + $this->assertEquals(false, isset($collection->Property)); + $collection->Property = 'value'; + $this->assertEquals(true, isset($collection->Property)); + } + + public function testContains() + { + $collection = new CAttributeCollection(); + $this->assertEquals(false, $collection->contains('Property')); + $collection->Property = 'value'; + $this->assertEquals(true, $collection->contains('Property')); + } + + public function testHasProperty() + { + $collection = new CAttributeCollection(); + $this->assertEquals(false, $collection->hasProperty('Property')); + $collection->Property = 'value'; + $this->assertEquals(true, $collection->hasProperty('Property')); + } +} diff --git a/tests/framework/collections/CConfigurationTest.php b/tests/framework/collections/CConfigurationTest.php new file mode 100644 index 000000000..d61f988f6 --- /dev/null +++ b/tests/framework/collections/CConfigurationTest.php @@ -0,0 +1,97 @@ +_param2; + } + + public function setParam2($value) + { + $this->_param2=$value; + } + + public function getObject() + { + if($this->_object===null) + $this->_object=new MyClass; + return $this->_object; + } +} + +class CConfigurationTest extends CTestCase +{ + public $configFile; + + public function setUp() + { + $this->configFile=dirname(__FILE__).'/data/config.php'; + } + + public function tearDown() + { + } + + public function testLoadFromFile() + { + $config=new CConfiguration; + $this->assertTrue($config->toArray()===array()); + $config->loadFromFile($this->configFile); + $data=include($this->configFile); + $this->assertTrue($config->toArray()===$data); + } + + public function testSaveAsString() + { + $config=new CConfiguration($this->configFile); + $str=$config->saveAsString(); + eval("\$data=$str;"); + $this->assertTrue($config->toArray()===$data); + } + + public function testApplyTo() + { + $config=new CConfiguration($this->configFile); + $object=new MyClass; + $config->applyTo($object); + $this->assertTrue($object->param1==='value1'); + $this->assertTrue($object->param2===false); + $this->assertTrue($object->param3===123); + $this->assertTrue($object->backquote==="\\back'quote'"); + /* + $this->assertTrue($object->object->param1===null); + $this->assertTrue($object->object->param2==='123'); + $this->assertTrue($object->object->param3===array('param1'=>'kkk','ddd','')); + */ + } + + public function testException() + { + $config=new CConfiguration(array('invalid'=>'value')); + $object=new MyClass; + $this->setExpectedException('CException'); + $config->applyTo($object); + } + + public function testCreateComponent() + { + $obj=Yii::createComponent(array('class'=>'MyClass','param2'=>3)); + $this->assertEquals(get_class($obj),'MyClass'); + $this->assertEquals($obj->param2,3); + } + + public function testCreateObject(){ + $obj = CConfiguration::createObject(array('class'=>'MyClass','param2'=>3)); + $this->assertEquals(get_class($obj),'MyClass'); + $this->assertEquals($obj->param2,3); + } +} diff --git a/tests/framework/collections/CListTest.php b/tests/framework/collections/CListTest.php new file mode 100644 index 000000000..14ca1ca84 --- /dev/null +++ b/tests/framework/collections/CListTest.php @@ -0,0 +1,208 @@ +list=new CList; + $this->item1=new ListItem; + $this->item2=new ListItem; + $this->item3=new ListItem; + $this->list->add($this->item1); + $this->list->add($this->item2); + } + + public function tearDown() + { + $this->list=null; + $this->item1=null; + $this->item2=null; + $this->item3=null; + } + + public function testConstruct() + { + $a=array(1,2,3); + $list=new CList($a); + $this->assertEquals(3,$list->getCount()); + $list2=new CList($this->list); + $this->assertEquals(2,$list2->getCount()); + } + + public function testGetReadOnly() + { + $list = new CList(null, true); + $this->assertEquals(true, $list->getReadOnly(), 'List is not read-only'); + $list = new CList(null, false); + $this->assertEquals(false, $list->getReadOnly(), 'List is read-only'); + } + + public function testGetCount() + { + $this->assertEquals(2,$this->list->getCount()); + $this->assertEquals(2,$this->list->Count); + } + + public function testAdd() + { + $this->list->add(null); + $this->list->add($this->item3); + $this->assertEquals(4,$this->list->getCount()); + $this->assertEquals(3,$this->list->indexOf($this->item3)); + } + + public function testInsertAt() + { + $this->list->insertAt(0,$this->item3); + $this->assertEquals(3,$this->list->getCount()); + $this->assertEquals(2,$this->list->indexOf($this->item2)); + $this->assertEquals(0,$this->list->indexOf($this->item3)); + $this->assertEquals(1,$this->list->indexOf($this->item1)); + $this->setExpectedException('CException'); + $this->list->insertAt(4,$this->item3); + } + + public function testCanNotInsertWhenReadOnly() + { + $list = new CList(array(), true); + $this->setExpectedException('CException'); + $list->insertAt(1, 2); + } + + public function testRemove() + { + $this->list->remove($this->item1); + $this->assertEquals(1,$this->list->getCount()); + $this->assertEquals(-1,$this->list->indexOf($this->item1)); + $this->assertEquals(0,$this->list->indexOf($this->item2)); + + $this->assertEquals(false,$this->list->remove($this->item1)); + + } + + public function testRemoveAt() + { + $this->list->add($this->item3); + $this->list->removeAt(1); + $this->assertEquals(-1,$this->list->indexOf($this->item2)); + $this->assertEquals(1,$this->list->indexOf($this->item3)); + $this->assertEquals(0,$this->list->indexOf($this->item1)); + $this->setExpectedException('CException'); + $this->list->removeAt(2); + } + + public function testCanNotRemoveWhenReadOnly() + { + $list = new CList(array(1, 2, 3), true); + $this->setExpectedException('CException'); + $list->removeAt(2); + } + + public function testClear() + { + $this->list->clear(); + $this->assertEquals(0,$this->list->getCount()); + $this->assertEquals(-1,$this->list->indexOf($this->item1)); + $this->assertEquals(-1,$this->list->indexOf($this->item2)); + } + + public function testContains() + { + $this->assertTrue($this->list->contains($this->item1)); + $this->assertTrue($this->list->contains($this->item2)); + $this->assertFalse($this->list->contains($this->item3)); + } + + public function testIndexOf() + { + $this->assertEquals(0,$this->list->indexOf($this->item1)); + $this->assertEquals(1,$this->list->indexOf($this->item2)); + $this->assertEquals(-1,$this->list->indexOf($this->item3)); + } + + public function testCopyFrom() + { + $array=array($this->item3,$this->item1); + $this->list->copyFrom($array); + $this->assertTrue(count($array)==2 && $this->list[0]===$this->item3 && $this->list[1]===$this->item1); + $this->setExpectedException('CException'); + $this->list->copyFrom($this); + } + + public function testMergeWith() + { + $array=array($this->item3,$this->item1); + $this->list->mergeWith($array); + $this->assertTrue($this->list->getCount()==4 && $this->list[0]===$this->item1 && $this->list[3]===$this->item1); + $this->setExpectedException('CException'); + $this->list->mergeWith($this); + } + + public function testToArray() + { + $array=$this->list->toArray(); + $this->assertTrue(count($array)==2 && $array[0]===$this->item1 && $array[1]===$this->item2); + } + + public function testArrayRead() + { + $this->assertTrue($this->list[0]===$this->item1); + $this->assertTrue($this->list[1]===$this->item2); + $this->setExpectedException('CException'); + $a=$this->list[2]; + } + + public function testGetIterator() + { + $n=0; + $found=0; + foreach($this->list as $index=>$item) + { + foreach($this->list as $a=>$b); // test of iterator + $n++; + if($index===0 && $item===$this->item1) + $found++; + if($index===1 && $item===$this->item2) + $found++; + } + $this->assertTrue($n==2 && $found==2); + } + + public function testArrayMisc() + { + $this->assertEquals($this->list->Count,count($this->list)); + $this->assertTrue(isset($this->list[1])); + $this->assertFalse(isset($this->list[2])); + } + + public function testOffsetSetAdd() + { + $list = new CList(array(1, 2, 3)); + $list->offsetSet(null, 4); + $this->assertEquals(array(1, 2, 3, 4), $list->toArray()); + } + + public function testOffsetSetReplace() + { + $list = new CList(array(1, 2, 3)); + $list->offsetSet(1, 4); + $this->assertEquals(array(1, 4, 3), $list->toArray()); + } + + public function testOffsetUnset() + { + $list = new CList(array(1, 2, 3)); + $list->offsetUnset(1); + $this->assertEquals(array(1, 3), $list->toArray()); + } +} diff --git a/tests/framework/collections/CMapTest.php b/tests/framework/collections/CMapTest.php new file mode 100644 index 000000000..58686b742 --- /dev/null +++ b/tests/framework/collections/CMapTest.php @@ -0,0 +1,204 @@ +map=new CMap; + $this->item1=new MapItem; + $this->item2=new MapItem; + $this->item3=new MapItem; + $this->map->add('key1',$this->item1); + $this->map->add('key2',$this->item2); + } + + public function tearDown() + { + $this->map=null; + $this->item1=null; + $this->item2=null; + $this->item3=null; + } + + public function testConstruct() + { + $a=array(1,2,'key3'=>3); + $map=new CMap($a); + $this->assertEquals(3,$map->getCount()); + $map2=new CMap($this->map); + $this->assertEquals(2,$map2->getCount()); + } + + public function testGetReadOnly() + { + $map = new CMap(null, true); + self::assertEquals(true, $map->getReadOnly(), 'List is not read-only'); + } + + public function testGetCount() + { + $this->assertEquals(2,$this->map->getCount()); + } + + public function testGetKeys() + { + $keys=$this->map->getKeys(); + $this->assertEquals(2,count($keys)); + $this->assertEquals('key1',$keys[0]); + $this->assertEquals('key2',$keys[1]); + } + + public function testAdd() + { + $this->map->add('key3',$this->item3); + $this->assertEquals(3,$this->map->getCount()); + $this->assertTrue($this->map->contains('key3')); + } + + public function testCanNotAddWhenReadOnly() + { + $map = new CMap(array(), true); + $this->setExpectedException('CException'); + $map->add('key', 'value'); + } + + public function testRemove() + { + $this->map->remove('key1'); + $this->assertEquals(1,$this->map->getCount()); + $this->assertTrue(!$this->map->contains('key1')); + $this->assertTrue($this->map->remove('unknown key')===null); + } + + public function testCanNotRemoveWhenReadOnly() + { + $map = new CMap(array('key' => 'value'), true); + $this->setExpectedException('CException'); + $map->remove('key'); + } + + public function testClear() + { + $this->map->clear(); + $this->assertEquals(0,$this->map->getCount()); + $this->assertTrue(!$this->map->contains('key1') && !$this->map->contains('key2')); + } + + public function testContains() + { + $this->assertTrue($this->map->contains('key1')); + $this->assertTrue($this->map->contains('key2')); + $this->assertFalse($this->map->contains('key3')); + } + + public function testCopyFrom() + { + $array=array('key3'=>$this->item3,'key4'=>$this->item1); + $this->map->copyFrom($array); + + $this->assertEquals(2, $this->map->getCount()); + $this->assertEquals($this->item3, $this->map['key3']); + $this->assertEquals($this->item1, $this->map['key4']); + + $this->setExpectedException('CException'); + $this->map->copyFrom($this); + } + + public function testMergeWith() + { + $a=array('a'=>'v1','v2',array('2'),'c'=>array('3','c'=>'a')); + $b=array('v22','a'=>'v11',array('2'),'c'=>array('c'=>'3','a')); + $c=array('a'=>'v11','v2',array('2'),'c'=>array('3','c'=>'3','a'),'v22',array('2')); + $map=new CMap($a); + $map2=new CMap($b); + $map->mergeWith($map2); + $this->assertTrue($map->toArray()===$c); + + $array=array('key2'=>$this->item1,'key3'=>$this->item3); + $this->map->mergeWith($array,false); + $this->assertEquals(3,$this->map->getCount()); + $this->assertEquals($this->item1,$this->map['key2']); + $this->assertEquals($this->item3,$this->map['key3']); + $this->setExpectedException('CException'); + $this->map->mergeWith($this,false); + } + + public function testRecursiveMergeWithTraversable(){ + $map = new CMap(); + $obj = new ArrayObject(array( + 'k1' => $this->item1, + 'k2' => $this->item2, + 'k3' => new ArrayObject(array( + 'k4' => $this->item3, + )) + )); + $map->mergeWith($obj,true); + + $this->assertEquals(3, $map->getCount()); + $this->assertEquals($this->item1, $map['k1']); + $this->assertEquals($this->item2, $map['k2']); + $this->assertEquals($this->item3, $map['k3']['k4']); + } + + public function testArrayRead() + { + $this->assertEquals($this->item1,$this->map['key1']); + $this->assertEquals($this->item2,$this->map['key2']); + $this->assertEquals(null,$this->map['key3']); + } + + public function testArrayWrite() + { + $this->map['key3']=$this->item3; + $this->assertEquals(3,$this->map->getCount()); + $this->assertEquals($this->item3,$this->map['key3']); + + $this->map['key1']=$this->item3; + $this->assertEquals(3,$this->map->getCount()); + $this->assertEquals($this->item3,$this->map['key1']); + + unset($this->map['key2']); + $this->assertEquals(2,$this->map->getCount()); + $this->assertTrue(!$this->map->contains('key2')); + + unset($this->map['unknown key']); + } + + public function testArrayForeach() + { + $n=0; + $found=0; + foreach($this->map as $index=>$item) + { + $n++; + if($index==='key1' && $item===$this->item1) + $found++; + if($index==='key2' && $item===$this->item2) + $found++; + } + $this->assertTrue($n==2 && $found==2); + } + + public function testArrayMisc() + { + $this->assertEquals($this->map->Count,count($this->map)); + $this->assertTrue(isset($this->map['key1'])); + $this->assertFalse(isset($this->map['unknown key'])); + } + + public function testToArray() + { + $map = new CMap(array('key' => 'value')); + self::assertEquals(array('key' => 'value'), $map->toArray()); + } +} diff --git a/tests/framework/collections/CQueueTest.php b/tests/framework/collections/CQueueTest.php new file mode 100644 index 000000000..c69d8d182 --- /dev/null +++ b/tests/framework/collections/CQueueTest.php @@ -0,0 +1,119 @@ +assertEquals(array(), $queue->toArray()); + $queue = new CQueue(array(1, 2, 3)); + $this->assertEquals(array(1, 2, 3), $queue->toArray()); + } + + public function testToArray() + { + $queue = new CQueue(array(1, 2, 3)); + $this->assertEquals(array(1, 2, 3), $queue->toArray()); + } + + public function testCopyFrom() + { + $queue = new CQueue(array(1, 2, 3)); + $data = array(4, 5, 6); + $queue->copyFrom($data); + $this->assertEquals(array(4, 5, 6), $queue->toArray()); + } + + public function testCanNotCopyFromNonTraversableTypes() + { + $queue = new CQueue(); + $data = new stdClass(); + $this->setExpectedException('CException'); + $queue->copyFrom($data); + } + + public function testClear() + { + $queue = new CQueue(array(1, 2, 3)); + $queue->clear(); + $this->assertEquals(array(), $queue->toArray()); + } + + public function testContains() + { + $queue = new CQueue(array(1, 2, 3)); + $this->assertEquals(true, $queue->contains(2)); + $this->assertEquals(false, $queue->contains(4)); + } + + public function testPeek() + { + $queue = new CQueue(array(1)); + $this->assertEquals(1, $queue->peek()); + } + + public function testCanNotPeekAnEmptyQueue() + { + $queue = new CQueue(); + $this->setExpectedException('CException'); + $item = $queue->peek(); + } + + public function testDequeue() + { + $queue = new CQueue(array(1, 2, 3)); + $first = $queue->dequeue(); + $this->assertEquals(1, $first); + $this->assertEquals(array(2, 3), $queue->toArray()); + } + + public function testCanNotDequeueAnEmptyQueue() + { + $queue = new CQueue(); + $this->setExpectedException('CException'); + $item = $queue->dequeue(); + } + + public function testEnqueue() + { + $queue = new CQueue(); + $queue->enqueue(1); + $this->assertEquals(array(1), $queue->toArray()); + } + + public function testGetIterator() + { + $queue = new CQueue(array(1, 2)); + $this->assertInstanceOf('CQueueIterator', $queue->getIterator()); + $n = 0; + $found = 0; + foreach($queue as $index => $item) + { + foreach($queue as $a => $b); // test of iterator + $n++; + if($index === 0 && $item === 1) + $found++; + if($index === 1 && $item === 2) + $found++; + } + $this->assertTrue($n == 2 && $found == 2); + } + + public function testGetCount() + { + $queue = new CQueue(); + $this->assertEquals(0, $queue->getCount()); + $queue = new CQueue(array(1, 2, 3)); + $this->assertEquals(3, $queue->getCount()); + } + + public function testCountable() + { + $queue = new CQueue(); + $this->assertEquals(0, count($queue)); + $queue = new CQueue(array(1, 2, 3)); + $this->assertEquals(3, count($queue)); + } +} diff --git a/tests/framework/collections/CStackTest.php b/tests/framework/collections/CStackTest.php new file mode 100644 index 000000000..30175751e --- /dev/null +++ b/tests/framework/collections/CStackTest.php @@ -0,0 +1,119 @@ +assertEquals(array(), $stack->toArray()); + $stack = new CStack(array(1, 2, 3)); + $this->assertEquals(array(1, 2, 3), $stack->toArray()); + } + + public function testToArray() + { + $stack = new CStack(array(1, 2, 3)); + $this->assertEquals(array(1, 2, 3), $stack->toArray()); + } + + public function testCopyFrom() + { + $stack = new CStack(array(1, 2, 3)); + $data = array(4, 5, 6); + $stack->copyFrom($data); + $this->assertEquals(array(4, 5, 6), $stack->toArray()); + } + + public function testCanNotCopyFromNonTraversableTypes() + { + $stack = new CStack(); + $data = new stdClass(); + $this->setExpectedException('CException'); + $stack->copyFrom($data); + } + + public function testClear() + { + $stack = new CStack(array(1, 2, 3)); + $stack->clear(); + $this->assertEquals(array(), $stack->toArray()); + } + + public function testContains() + { + $stack = new CStack(array(1, 2, 3)); + $this->assertEquals(true, $stack->contains(2)); + $this->assertEquals(false, $stack->contains(4)); + } + + public function testPeek() + { + $stack = new CStack(array(1)); + $this->assertEquals(1, $stack->peek()); + } + + public function testCanNotPeekAnEmptyStack() + { + $stack = new CStack(); + $this->setExpectedException('CException'); + $item = $stack->peek(); + } + + public function testPop() + { + $stack = new CStack(array(1, 2, 3)); + $last = $stack->pop(); + $this->assertEquals(3, $last); + $this->assertEquals(array(1, 2), $stack->toArray()); + } + + public function testCanNotPopAnEmptyStack() + { + $stack = new CStack(); + $this->setExpectedException('CException'); + $item = $stack->pop(); + } + + public function testPush() + { + $stack = new CStack(); + $stack->push(1); + $this->assertEquals(array(1), $stack->toArray()); + } + + public function testGetIterator() + { + $stack = new CStack(array(1, 2)); + $this->assertInstanceOf('CStackIterator', $stack->getIterator()); + $n = 0; + $found = 0; + foreach($stack as $index => $item) + { + foreach($stack as $a => $b); // test of iterator + $n++; + if($index === 0 && $item === 1) + $found++; + if($index === 1 && $item === 2) + $found++; + } + $this->assertTrue($n == 2 && $found == 2); + } + + public function testGetCount() + { + $stack = new CStack(); + $this->assertEquals(0, $stack->getCount()); + $stack = new CStack(array(1, 2, 3)); + $this->assertEquals(3, $stack->getCount()); + } + + public function testCount() + { + $stack = new CStack(); + $this->assertEquals(0, count($stack)); + $stack = new CStack(array(1, 2, 3)); + $this->assertEquals(3, count($stack)); + } +} diff --git a/tests/framework/collections/CTypedListTest.php b/tests/framework/collections/CTypedListTest.php new file mode 100644 index 000000000..3c7e3fa3b --- /dev/null +++ b/tests/framework/collections/CTypedListTest.php @@ -0,0 +1,22 @@ +setExpectedException('CException'); + $list[]=new stdClass; + } + + public function testInterfaceType() + { + $list=new CTypedList('Traversable'); + $list[]=new CList; + $this->setExpectedException('CException'); + $list[]=new CComponent; + } +} diff --git a/tests/framework/collections/data/config.php b/tests/framework/collections/data/config.php new file mode 100644 index 000000000..4c759f0b2 --- /dev/null +++ b/tests/framework/collections/data/config.php @@ -0,0 +1,17 @@ +'value1', + 'param2'=>false, + 'param3'=>123, + "backquote"=>"\\back'quote'", +/* 'object'=>array( + 'param1'=>null, + 'param2'=>'123', + 'param3'=>array( + 'param1'=>'kkk', + 'ddd', + '', + ), + ),*/ +); \ No newline at end of file diff --git a/tests/framework/db/CDbCommand2Test.php b/tests/framework/db/CDbCommand2Test.php new file mode 100644 index 000000000..96e1c6fdc --- /dev/null +++ b/tests/framework/db/CDbCommand2Test.php @@ -0,0 +1,388 @@ +markTestSkipped('PDO and SQLite extensions are required.'); + + $this->_connection=new CDbConnection('sqlite::memory:'); + $this->_connection->active=true; + $this->_connection->pdoInstance->exec(file_get_contents(dirname(__FILE__).'/data/sqlite.sql')); + } + + public function tearDown() + { + $this->_connection->active=false; + } + + public function testSelect() + { + $command=$this->_connection->createCommand(); + + // default + $command->select(); + $this->assertEquals('*', $command->select); + + // string input + $command->select('id, username'); + $this->assertEquals('"id", "username"', $command->select); + + // string input with expression + $command->select('id, count(id) as num'); + $this->assertEquals('id, count(id) as num', $command->select); + + // array input + $command->select(array('id2', 'username2')); + $this->assertEquals('"id2", "username2"', $command->select); + + // table prefix and expression + $command->select(array('user.id', 'count(id) as num', 'profile.*')); + $this->assertEquals('\'user\'."id", count(id) as num, \'profile\'.*', $command->select); + + // alias + $command->select(array('id2 as id', 'profile.username2 AS username')); + $this->assertEquals('"id2" AS "id", \'profile\'."username2" AS "username"', $command->select); + + // getter and setter + $command->select=array('id2', 'username2'); + $this->assertEquals('"id2", "username2"', $command->select); + } + + public function testDistinct() + { + $command=$this->_connection->createCommand(); + + // default value + $this->assertEquals(false, $command->distinct); + + // select distinct + $command->selectDistinct('id, username'); + $this->assertEquals(true, $command->distinct); + $this->assertEquals('"id", "username"', $command->select); + + // getter and setter + $command->distinct=false; + $this->assertEquals(false, $command->distinct); + $command->distinct=true; + $this->assertEquals(true, $command->distinct); + } + + public function testFrom() + { + $command=$this->_connection->createCommand(); + + // default + $this->assertEquals('', $command->from); + + // string input + $command->from('user'); + $this->assertEquals('\'user\'', $command->from); + $command->from('user, profile'); + $this->assertEquals('\'user\', \'profile\'', $command->from); + + // string input with expression + $command->from('user, (select * from profile) p'); + $this->assertEquals('user, (select * from profile) p', $command->from); + + // array input + $command->from(array('user', 'profile')); + $this->assertEquals('\'user\', \'profile\'', $command->from); + + // table alias, expression, schema + $command->from(array('user u', '(select * from profile) p', 'public.post')); + $this->assertEquals('\'user\' \'u\', (select * from profile) p, \'public\'.\'post\'', $command->from); + + // getter and setter + $command->from=array('user', 'profile'); + $this->assertEquals('\'user\', \'profile\'', $command->from); + } + + public function testWhere() + { + $command=$this->_connection->createCommand(); + + // default + $this->assertEquals('', $command->where); + $this->assertEquals(array(), $command->params); + + // string input + $command->where('id=1 or id=:id2', array(':id2'=>2)); + $this->assertEquals('id=1 or id=:id2', $command->where); + $this->assertEquals(array(':id2'=>2), $command->params); + + // array input, and/or + $command->where(array('and', 'id=1', 'id=2')); + $this->assertEquals('(id=1) AND (id=2)', $command->where); + $command->where(array('and', 'id=1', array('or', 'id=3', 'id=4'), 'id=2')); + $this->assertEquals('(id=1) AND ((id=3) OR (id=4)) AND (id=2)', $command->where); + + // empty input + $command->where(array()); + $this->assertEquals('', $command->where); + + // in, empty + $command->where(array('in', 'id', array())); + $this->assertEquals('0=1', $command->where); + + // in + $command->where(array('in', 'id', array(1,'2',3))); + $this->assertEquals("\"id\" IN (1, '2', 3)", $command->where); + + // not in, empty + $command->where(array('not in', 'id', array())); + $this->assertEquals('', $command->where); + + // not in + $command->where(array('not in', 'id', array(1,'2',3))); + $this->assertEquals("\"id\" NOT IN (1, '2', 3)", $command->where); + + // like, string + $command->where(array('like', 'name', '%tester')); + $this->assertEquals('"name" LIKE \'%tester\'', $command->where); + + $command->where(array('like', 'name', array('%tester', '%tester2'))); + $this->assertEquals('"name" LIKE \'%tester\' AND "name" LIKE \'%tester2\'', $command->where); + + $command->where(array('not like', 'name', array('tester%', 'tester2%'))); + $this->assertEquals('"name" NOT LIKE \'tester%\' AND "name" NOT LIKE \'tester2%\'', $command->where); + + $command->where(array('or like', 'name', array('%tester', '%tester2'))); + $this->assertEquals('"name" LIKE \'%tester\' OR "name" LIKE \'%tester2\'', $command->where); + + $command->where(array('or not like', 'name', array('%tester', '%tester2'))); + $this->assertEquals('"name" NOT LIKE \'%tester\' OR "name" NOT LIKE \'%tester2\'', $command->where); + } + + public function testJoin() + { + $command=$this->_connection->createCommand(); + + // default + $this->assertEquals('', $command->join); + + // inner join + $command->join('user', 'user.id=t.id and id=:id', array(':id'=>1)); + $this->assertEquals(array('JOIN \'user\' ON user.id=t.id and id=:id'), $command->join); + $this->assertEquals(array(':id'=>1), $command->params); + + // left join + $join=$command->join; + $command->leftJoin('user', 'user.id=t.id and id=:id'); + $join[]='LEFT JOIN \'user\' ON user.id=t.id and id=:id'; + $this->assertEquals($join, $command->join); + + // right join + $command->rightJoin('user', 'user.id=t.id and id=:id'); + $join[]='RIGHT JOIN \'user\' ON user.id=t.id and id=:id'; + $this->assertEquals($join, $command->join); + + // cross join + $command->crossJoin('user'); + $join[]='CROSS JOIN \'user\''; + $this->assertEquals($join, $command->join); + + // natural join + $command->naturalJoin('user'); + $join[]='NATURAL JOIN \'user\''; + $this->assertEquals($join, $command->join); + } + + public function testGroup() + { + $command=$this->_connection->createCommand(); + + // default + $this->assertEquals('', $command->group); + + // string input + $command->group('id, username'); + $this->assertEquals('"id", "username"', $command->group); + + // string input with expression + $command->group('id, count(id)'); + $this->assertEquals('id, count(id)', $command->group); + + // array input + $command->group(array('id2', 'username2')); + $this->assertEquals('"id2", "username2"', $command->group); + + // table prefix and expression + $command->group(array('user.id', 'count(id)')); + $this->assertEquals('\'user\'."id", count(id)', $command->group); + + // getter and setter + $command->group=array('id2', 'username2'); + $this->assertEquals('"id2", "username2"', $command->group); + } + + public function testHaving() + { + $command=$this->_connection->createCommand(); + + // default + $this->assertEquals('', $command->having); + $this->assertEquals(array(), $command->params); + + // string input + $command->having('id=1 or id=:id2', array(':id2'=>2)); + $this->assertEquals('id=1 or id=:id2', $command->having); + $this->assertEquals(array(':id2'=>2), $command->params); + + // array input, and/or + $command->having(array('and', 'id=1', 'id=2')); + $this->assertEquals('(id=1) AND (id=2)', $command->having); + $command->having(array('and', 'id=1', array('or', 'id=3', 'id=4'), 'id=2')); + $this->assertEquals('(id=1) AND ((id=3) OR (id=4)) AND (id=2)', $command->having); + } + + public function testOrder() + { + $command=$this->_connection->createCommand(); + + // default + $this->assertEquals('', $command->order); + + // string input + $command->order('id, username desc'); + $this->assertEquals('"id", "username" DESC', $command->order); + + // string input with expression + $command->order('id, count(id) desc'); + $this->assertEquals('id, count(id) desc', $command->order); + + // array input + $command->order(array('id2 asc', 'username2 DESC')); + $this->assertEquals('"id2" ASC, "username2" DESC', $command->order); + + // table prefix and expression + $command->order(array('user.id asc', 'count(id)')); + $this->assertEquals('\'user\'."id" ASC, count(id)', $command->order); + + // getter and setter + $command->order=array('id2 asc', 'username2'); + $this->assertEquals('"id2" ASC, "username2"', $command->order); + } + + public function testLimit() + { + $command=$this->_connection->createCommand(); + + // default + $this->assertEquals(-1, $command->limit); + + $command->limit(10); + $this->assertEquals(10, $command->limit); + + $command->limit(20,30); + $this->assertEquals(20, $command->limit); + $this->assertEquals(30, $command->offset); + + // invalid string + $command->limit('abc'); + $this->assertEquals(0, $command->limit); + } + + public function testOffset() + { + $command=$this->_connection->createCommand(); + + // default + $this->assertEquals(-1, $command->offset); + + $command->offset(10); + $this->assertEquals(10, $command->offset); + + // invalid string + $command->offset('abc'); + $this->assertEquals(0, $command->offset); + } + + public function testUnion() + { + $command=$this->_connection->createCommand(); + + // default + $this->assertEquals('', $command->union); + + $command->union('select * from user'); + $this->assertEquals(array('select * from user'), $command->union); + + $command->union('select * from post'); + $this->assertEquals(array('select * from user', 'select * from post'), $command->union); + } + + /* + public function testInsert() + { + $command=$this->_connection->createCommand(); + + $command->insert('user', array('id'=>1, 'username'=>'tester')); + $this->assertEquals('INSERT INTO \'user\' ("id", "username") VALUES (:id, :username)', $command->text); + $this->assertEquals(array(':id'=>1, ':username'=>'tester'), $command->params); + } + + public function testUpdate() + { + $command=$this->_connection->createCommand(); + + $command->update('user', array('id'=>1, 'username'=>'tester'), 'status=:status', array(':status'=>2)); + $this->assertEquals('UPDATE \'user\' SET "id"=:id, "username"=:username WHERE status=:status', $command->text); + $this->assertEquals(array(':id'=>1, ':username'=>'tester', ':status'=>2), $command->params); + } + + public function testDelete() + { + $command=$this->_connection->createCommand(); + + $command->delete('user', 'status=:status', array(':status'=>2)); + $this->assertEquals('DELETE FROM \'user\' WHERE status=:status', $command->text); + $this->assertEquals(array(':status'=>2), $command->params); + } + */ + + public function testQuery() + { + // simple query + $command=$this->_connection->createCommand() + ->select('username, password') + ->from('users') + ->where('email=:email or email=:email2', array(':email'=>'email2', ':email2'=>'email4')) + ->order('username desc') + ->limit(2,1); + + $sql="SELECT \"username\", \"password\"\nFROM 'users'\nWHERE email=:email or email=:email2\nORDER BY \"username\" DESC LIMIT 2 OFFSET 1"; + $this->assertEquals($sql, $command->text); + + $rows=$command->queryAll(); + $this->assertEquals(1,count($rows)); + $this->assertEquals('user2',$rows[0]['username']); + $this->assertEquals('pass2',$rows[0]['password']); + } + + public function testArraySyntax() + { + $command=$this->_connection->createCommand(array( + 'select'=>'username, password', + 'from'=>'users', + 'where'=>'email=:email or email=:email2', + 'params'=>array(':email'=>'email2', ':email2'=>'email4'), + 'order'=>'username desc', + 'limit'=>2, + 'offset'=>1, + )); + + $sql="SELECT \"username\", \"password\"\nFROM 'users'\nWHERE email=:email or email=:email2\nORDER BY \"username\" DESC LIMIT 2 OFFSET 1"; + $this->assertEquals($sql, $command->text); + + $rows=$command->queryAll(); + $this->assertEquals(1,count($rows)); + $this->assertEquals('user2',$rows[0]['username']); + $this->assertEquals('pass2',$rows[0]['password']); + } +} \ No newline at end of file diff --git a/tests/framework/db/CDbCommandTest.php b/tests/framework/db/CDbCommandTest.php new file mode 100644 index 000000000..4ad1ae44d --- /dev/null +++ b/tests/framework/db/CDbCommandTest.php @@ -0,0 +1,239 @@ +markTestSkipped('PDO and SQLite extensions are required.'); + + $this->_connection=new CDbConnection('sqlite::memory:'); + $this->_connection->active=true; + $this->_connection->pdoInstance->exec(file_get_contents(dirname(__FILE__).'/data/sqlite.sql')); + } + + public function tearDown() + { + $this->_connection->active=false; + } + + public function testGetText() + { + $sql='SELECT * FROM posts'; + $command=$this->_connection->createCommand($sql); + $this->assertEquals($command->text,$sql); + } + + public function testSetText() + { + $sql='SELECT title FROM posts'; + $command=$this->_connection->createCommand($sql); + $this->assertEquals($command->queryScalar(),'post 1'); + + $newSql='SELECT id FROM posts'; + $command->text=$newSql; + $this->assertEquals($command->text,$newSql); + $this->assertEquals($command->queryScalar(),1); + } + + public function testConnection() + { + $sql='SELECT title FROM posts'; + $command=$this->_connection->createCommand($sql); + $this->assertEquals($command->connection,$this->_connection); + } + + public function testPrepare() + { + $sql='SELECT title FROM posts'; + $command=$this->_connection->createCommand($sql); + $this->assertEquals($command->pdoStatement,null); + $command->prepare(); + $this->assertTrue($command->pdoStatement instanceof PDOStatement); + $this->assertEquals($command->queryScalar(),'post 1'); + + $command->text='Bad SQL'; + $this->setExpectedException('CException'); + $command->prepare(); + } + + public function testCancel() + { + $sql='SELECT title FROM posts'; + $command=$this->_connection->createCommand($sql); + $command->prepare(); + $this->assertTrue($command->pdoStatement instanceof PDOStatement); + $command->cancel(); + $this->assertEquals($command->pdoStatement,null); + } + + public function testExecute() + { + $sql='INSERT INTO comments(content,post_id,author_id) VALUES (\'test comment\', 1, 1)'; + $command=$this->_connection->createCommand($sql); + $this->assertEquals($command->execute(),1); + $this->assertEquals($command->execute(),1); + $command=$this->_connection->createCommand('SELECT * FROM comments WHERE content=\'test comment\''); + $this->assertEquals($command->execute(),0); + $command=$this->_connection->createCommand('SELECT COUNT(*) FROM comments WHERE content=\'test comment\''); + $this->assertEquals($command->queryScalar(),2); + + $command=$this->_connection->createCommand('bad SQL'); + $this->setExpectedException('CException'); + $command->execute(); + } + + public function testQuery() + { + $sql='SELECT * FROM posts'; + $reader=$this->_connection->createCommand($sql)->query(); + $this->assertTrue($reader instanceof CDbDataReader); + + $sql='SELECT * FROM posts'; + $command=$this->_connection->createCommand($sql); + $command->prepare(); + $reader=$command->query(); + $this->assertTrue($reader instanceof CDbDataReader); + + $command=$this->_connection->createCommand('bad SQL'); + $this->setExpectedException('CException'); + $command->query(); + } + + public function testBindParam() + { + $sql='INSERT INTO posts(title,create_time,author_id) VALUES (:title, :create_time, 1)'; + $command=$this->_connection->createCommand($sql); + $title='test title'; + $createTime=time(); + $command->bindParam(':title',$title); + $command->bindParam(':create_time',$createTime); + $command->execute(); + + $sql='SELECT create_time FROM posts WHERE title=:title'; + $command=$this->_connection->createCommand($sql); + $command->bindParam(':title',$title); + $this->assertEquals($command->queryScalar(),$createTime); + + $sql='INSERT INTO types (int_col, char_col, float_col, blob_col, numeric_col, bool_col) VALUES (:int_col, :char_col, :float_col, :blob_col, :numeric_col, :bool_col)'; + $command=$this->_connection->createCommand($sql); + $intCol=123; + $charCol='abc'; + $floatCol=1.23; + $blobCol="\x10\x11\x12"; + $numericCol='1.23'; + $boolCol=false; + $command->bindParam(':int_col',$intCol); + $command->bindParam(':char_col',$charCol); + $command->bindParam(':float_col',$floatCol); + $command->bindParam(':blob_col',$blobCol); + $command->bindParam(':numeric_col',$numericCol); + $command->bindParam(':bool_col',$boolCol); + $this->assertEquals(1,$command->execute()); + + $sql='SELECT * FROM types'; + $row=$this->_connection->createCommand($sql)->queryRow(); + $this->assertEquals($row['int_col'],$intCol); + $this->assertEquals($row['char_col'],$charCol); + $this->assertEquals($row['float_col'],$floatCol); + $this->assertEquals($row['blob_col'],$blobCol); + $this->assertEquals($row['numeric_col'],$numericCol); + } + + public function testBindValue() + { + $sql='INSERT INTO comments(content,post_id,author_id) VALUES (:content, 1, 1)'; + $command=$this->_connection->createCommand($sql); + $command->bindValue(':content','test comment'); + $command->execute(); + + $sql='SELECT post_id FROM comments WHERE content=:content'; + $command=$this->_connection->createCommand($sql); + $command->bindValue(':content','test comment'); + $this->assertEquals($command->queryScalar(),1); + } + + public function testQueryAll() + { + $rows=$this->_connection->createCommand('SELECT * FROM posts')->queryAll(); + $this->assertEquals(count($rows),5); + $row=$rows[2]; + $this->assertEquals($row['id'],3); + $this->assertEquals($row['title'],'post 3'); + + $rows=$this->_connection->createCommand('SELECT * FROM posts WHERE id=10')->queryAll(); + $this->assertEquals($rows,array()); + } + + public function testQueryRow() + { + $sql='SELECT * FROM posts'; + $row=$this->_connection->createCommand($sql)->queryRow(); + $this->assertEquals($row['id'],1); + $this->assertEquals($row['title'],'post 1'); + + $sql='SELECT * FROM posts'; + $command=$this->_connection->createCommand($sql); + $command->prepare(); + $row=$command->queryRow(); + $this->assertEquals($row['id'],1); + $this->assertEquals($row['title'],'post 1'); + + $sql='SELECT * FROM posts WHERE id=10'; + $command=$this->_connection->createCommand($sql); + $this->assertFalse($command->queryRow()); + + $command=$this->_connection->createCommand('bad SQL'); + $this->setExpectedException('CException'); + $command->queryRow(); + } + + public function testQueryColumn() + { + $sql='SELECT * FROM posts'; + $column=$this->_connection->createCommand($sql)->queryColumn(); + $this->assertEquals($column,range(1,5)); + + $command=$this->_connection->createCommand('SELECT id FROM posts WHERE id=10'); + $this->assertEquals($command->queryColumn(),array()); + + $command=$this->_connection->createCommand('bad SQL'); + $this->setExpectedException('CException'); + $command->queryColumn(); + } + + public function testQueryScalar() + { + $sql='SELECT * FROM posts'; + $this->assertEquals($this->_connection->createCommand($sql)->queryScalar(),1); + + $sql='SELECT id FROM posts'; + $command=$this->_connection->createCommand($sql); + $command->prepare(); + $this->assertEquals($command->queryScalar(),1); + + $command=$this->_connection->createCommand('SELECT id FROM posts WHERE id=10'); + $this->assertFalse($command->queryScalar()); + + $command=$this->_connection->createCommand('bad SQL'); + $this->setExpectedException('CException'); + $command->queryScalar(); + } + + public function testFetchMode(){ + $sql='SELECT * FROM posts'; + $command=$this->_connection->createCommand($sql); + $result = $command->queryRow(); + $this->assertTrue(is_array($result)); + + $sql='SELECT * FROM posts'; + $command=$this->_connection->createCommand($sql); + $command->setFetchMode(PDO::FETCH_OBJ); + $result = $command->queryRow(); + $this->assertTrue(is_object($result)); + } +} \ No newline at end of file diff --git a/tests/framework/db/CDbConnectionTest.php b/tests/framework/db/CDbConnectionTest.php new file mode 100644 index 000000000..370a868ae --- /dev/null +++ b/tests/framework/db/CDbConnectionTest.php @@ -0,0 +1,116 @@ +markTestSkipped('PDO and SQLite extensions are required.'); + + $this->_connection=new CDbConnection('sqlite::memory:'); + } + + public function tearDown() + { + $this->_connection->active=false; + } + + public function testAutoConnect() + { + $db=new CDbConnection; + $db->connectionString='sqlite::memory:'; + $this->assertFalse($db->active); + $this->assertTrue($db->autoConnect); + $db->init(); + $this->assertTrue($db->active); + + $db2=new CDbConnection; + $db2->connectionString='sqlite::memory:'; + $db2->autoConnect=false; + $this->assertFalse($db2->autoConnect); + $this->assertFalse($db2->active); + $db->init(); + $this->assertFalse($db2->active); + } + + public function testInitialized() + { + $db=new CDbConnection; + $db->autoConnect=false; + $this->assertFalse($db->isInitialized); + $db->init(); + $this->assertTrue($db->isInitialized); + } + + public function testActive() + { + $this->assertFalse($this->_connection->active); + $this->_connection->active=true; + $this->assertTrue($this->_connection->active); + $pdo=$this->_connection->pdoInstance; + $this->assertTrue($pdo instanceof PDO); + + $this->_connection->active=true; + $this->assertEquals($pdo,$this->_connection->pdoInstance); + + $this->_connection->active=false; + $this->assertFalse($this->_connection->active); + $this->assertEquals($this->_connection->pdoInstance,null); + + $connection=new CDbConnection('unknown::memory:'); + $this->setExpectedException('CException'); + $connection->active=true; + } + + public function testCreateCommand() + { + $sql='SELECT * FROM posts'; + $this->_connection->active=true; + $this->_connection->pdoInstance->exec(file_get_contents(dirname(__FILE__).'/data/sqlite.sql')); + $command=$this->_connection->createCommand($sql); + $this->assertTrue($command instanceof CDbCommand); + + $this->_connection->active=false; + $this->setExpectedException('CException'); + $this->_connection->createCommand($sql); + } + + public function testLastInsertID() + { + $this->_connection->active=true; + $this->_connection->pdoInstance->exec(file_get_contents(dirname(__FILE__).'/data/sqlite.sql')); + $sql='INSERT INTO posts(title,create_time,author_id) VALUES(\'test post\',11000,1)'; + $this->_connection->createCommand($sql)->execute(); + $this->assertEquals($this->_connection->lastInsertID,6); + } + + public function testQuoteValue() + { + $this->_connection->active=true; + $this->_connection->pdoInstance->exec(file_get_contents(dirname(__FILE__).'/data/sqlite.sql')); + $str="this is 'my' name"; + $expectedStr="'this is ''my'' name'"; + $this->assertEquals($expectedStr,$this->_connection->quoteValue($str)); + } + + public function testColumnNameCase() + { + $this->_connection->active=true; + $this->_connection->pdoInstance->exec(file_get_contents(dirname(__FILE__).'/data/sqlite.sql')); + $this->assertEquals(PDO::CASE_NATURAL,$this->_connection->ColumnCase); + $this->_connection->columnCase=PDO::CASE_LOWER; + $this->assertEquals(PDO::CASE_LOWER,$this->_connection->ColumnCase); + } + + public function testNullConversion() + { + $this->_connection->active=true; + $this->_connection->pdoInstance->exec(file_get_contents(dirname(__FILE__).'/data/sqlite.sql')); + $this->assertEquals(PDO::NULL_NATURAL,$this->_connection->NullConversion); + $this->_connection->nullConversion=PDO::NULL_EMPTY_STRING; + $this->assertEquals(PDO::NULL_EMPTY_STRING,$this->_connection->NullConversion); + } +} diff --git a/tests/framework/db/CDbDataReaderTest.php b/tests/framework/db/CDbDataReaderTest.php new file mode 100644 index 000000000..f778ef1f7 --- /dev/null +++ b/tests/framework/db/CDbDataReaderTest.php @@ -0,0 +1,162 @@ +param1=$param1; + $this->param2=$param2; + } + + public function getTitle() + { + return $this->_title; + } + + public function setTitle($value) + { + $this->_title=$value; + } +} + +class CDbDataReaderTest extends CTestCase +{ + private $_connection; + + public function setUp() + { + if(!extension_loaded('pdo') || !extension_loaded('pdo_sqlite')) + $this->markTestSkipped('PDO and SQLite extensions are required.'); + + $this->_connection=new CDbConnection('sqlite::memory:'); + $this->_connection->active=true; + $this->_connection->pdoInstance->exec(file_get_contents(dirname(__FILE__).'/data/sqlite.sql')); + } + + public function tearDown() + { + $this->_connection->active=false; + } + + public function testRead() + { + $reader=$this->_connection->createCommand('SELECT * FROM posts')->query(); + for($i=1;$i<=5;++$i) + { + $row=$reader->read(); + $this->assertEquals($row['id'],$i); + } + $this->assertFalse($reader->read()); + } + + public function testReadColumn() + { + $reader=$this->_connection->createCommand('SELECT * FROM posts')->query(); + $this->assertEquals($reader->readColumn(0),1); + $this->assertEquals($reader->readColumn(1),'post 2'); + $reader->readColumn(0); + $reader->readColumn(0); + $this->assertEquals($reader->readColumn(0),5); + $this->assertFalse($reader->readColumn(0)); + } + + public function testReadObject() + { + $reader=$this->_connection->createCommand('SELECT * FROM posts')->query(); + $object=$reader->readObject('PostRecord',array(null,'v2')); + $this->assertEquals($object->id,1); + $this->assertEquals($object->title,'post 1'); + $this->assertEquals($object->param1,null); + $this->assertEquals($object->param2,'v2'); + } + + public function testReadAll() + { + $reader=$this->_connection->createCommand('SELECT * FROM posts')->query(); + $rows=$reader->readAll(); + $this->assertEquals(count($rows),5); + $row=$rows[2]; + $this->assertEquals($row['id'],3); + $this->assertEquals($row['title'],'post 3'); + + $reader=$this->_connection->createCommand('SELECT * FROM posts WHERE id=10')->query(); + $this->assertEquals($reader->readAll(),array()); + } + + public function testClose() + { + $reader=$this->_connection->createCommand('SELECT * FROM posts')->query(); + $row=$reader->read(); + $row=$reader->read(); + $this->assertFalse($reader->isClosed); + $reader->close(); + $this->assertTrue($reader->isClosed); + } + + public function testRowCount() + { + // unable to test because SQLite doesn't support row count + } + + public function testColumnCount() + { + $reader=$this->_connection->createCommand('SELECT * FROM posts')->query(); + $this->assertEquals($reader->columnCount,5); + + $reader=$this->_connection->createCommand('SELECT * FROM posts WHERE id=11')->query(); + $this->assertEquals($reader->ColumnCount,5); + } + + public function testForeach() + { + $ids=array(); + $reader=$this->_connection->createCommand('SELECT * FROM posts')->query(); + foreach($reader as $row) + $ids[]=$row['id']; + $this->assertEquals(count($ids),5); + $this->assertEquals($ids[3],4); + + $this->setExpectedException('CException'); + foreach($reader as $row) + $ids[]=$row['id']; + } + + public function testFetchMode() + { + $reader=$this->_connection->createCommand('SELECT * FROM posts')->query(); + + $reader->fetchMode=PDO::FETCH_NUM; + $row=$reader->read(); + $this->assertFalse(isset($row['id'])); + $this->assertTrue(isset($row[0])); + + $reader->fetchMode=PDO::FETCH_ASSOC; + $row=$reader->read(); + $this->assertTrue(isset($row['id'])); + $this->assertFalse(isset($row[0])); + } + + public function testBindColumn() + { + $reader=$this->_connection->createCommand('SELECT * FROM posts')->query(); + $reader->bindColumn(1,$id); + $reader->bindColumn(2,$title); + $reader->read(); + $this->assertEquals($id,1); + $this->assertEquals($title,'post 1'); + $reader->read(); + $this->assertEquals($id,2); + $this->assertEquals($title,'post 2'); + } +} + diff --git a/tests/framework/db/CDbTransactionTest.php b/tests/framework/db/CDbTransactionTest.php new file mode 100644 index 000000000..df5f9e1bc --- /dev/null +++ b/tests/framework/db/CDbTransactionTest.php @@ -0,0 +1,63 @@ +markTestSkipped('PDO and SQLite extensions are required.'); + + $this->_connection=new CDbConnection('sqlite::memory:'); + $this->_connection->active=true; + $this->_connection->pdoInstance->exec(file_get_contents(dirname(__FILE__).'/data/sqlite.sql')); + } + + public function tearDown() + { + $this->_connection->active=false; + } + + public function testBeginTransaction() + { + $sql='INSERT INTO posts(id,title,create_time,author_id) VALUES(10,\'test post\',11000,1)'; + $transaction=$this->_connection->beginTransaction(); + try + { + $this->_connection->createCommand($sql)->execute(); + $this->_connection->createCommand($sql)->execute(); + $this->fail('Expected exception not raised'); + $transaction->commit(); + } + catch(Exception $e) + { + $transaction->rollBack(); + $reader=$this->_connection->createCommand('SELECT * FROM posts WHERE id=10')->query(); + $this->assertFalse($reader->read()); + } + } + + public function testCommit() + { + $sql='INSERT INTO posts(id,title,create_time,author_id) VALUES(10,\'test post\',11000,1)'; + $transaction=$this->_connection->beginTransaction(); + try + { + $this->_connection->createCommand($sql)->execute(); + $this->assertTrue($transaction->active); + $transaction->commit(); + $this->assertFalse($transaction->active); + } + catch(Exception $e) + { + $transaction->rollBack(); + $this->fail('Unexpected exception'); + } + $n=$this->_connection->createCommand('SELECT COUNT(*) FROM posts WHERE id=10')->queryScalar(); + $this->assertEquals($n,1); + } +} + diff --git a/tests/framework/db/ar/CActiveRecord2Test.php b/tests/framework/db/ar/CActiveRecord2Test.php new file mode 100644 index 000000000..56e330c94 --- /dev/null +++ b/tests/framework/db/ar/CActiveRecord2Test.php @@ -0,0 +1,596 @@ +markTestSkipped('PDO and PostgreSQL extensions are required.'); + + $this->db=new CDbConnection('pgsql:host=127.0.0.1;dbname=yii','test','test'); + try + { + $this->db->active=true; + } + catch(Exception $e) + { + $schemaFile=realpath(dirname(__FILE__).'/../data/postgres.sql'); + $this->markTestSkipped("Please read $schemaFile for details on setting up the test environment for PostgreSQL test case."); + } + + try { $this->db->createCommand('DROP SCHEMA test CASCADE')->execute(); } catch(Exception $e) { } + try { $this->db->createCommand('DROP TABLE yii_types CASCADE')->execute(); } catch(Exception $e) { } + + $sqls=file_get_contents(dirname(__FILE__).'/../data/postgres.sql'); + foreach(explode(';',$sqls) as $sql) + { + if(trim($sql)!=='') + $this->db->createCommand($sql)->execute(); + } + $this->db->active=false; + + $config=array( + 'basePath'=>dirname(__FILE__), + 'components'=>array( + 'db'=>array( + 'class'=>'system.db.CDbConnection', + 'connectionString'=>'pgsql:host=127.0.0.1;dbname=yii', + 'username'=>'test', + 'password'=>'test', + ), + ), + ); + $app=new TestApplication($config); + $app->db->active=true; + CActiveRecord::$db=$this->db=$app->db; + } + + public function tearDown() + { + $this->db->active=false; + } + + public function testModel() + { + $model=Post2::model(); + $this->assertTrue($model instanceof Post2); + $this->assertTrue($model->dbConnection===$this->db); + $this->assertTrue($model->dbConnection->active); + $this->assertEquals('test.posts',$model->tableName()); + $this->assertEquals('id',$model->tableSchema->primaryKey); + $this->assertEquals('test.posts_id_seq',$model->tableSchema->sequenceName); + $this->assertEquals(array(),$model->attributeLabels()); + $this->assertEquals('Id',$model->getAttributeLabel('id')); + $this->assertEquals('Author Id',$model->getAttributeLabel('author_id')); + $this->assertTrue($model->getActiveRelation('author') instanceof CBelongsToRelation); + $this->assertTrue($model->tableSchema instanceof CDbTableSchema); + $this->assertTrue($model->commandBuilder instanceof CDbCommandBuilder); + $this->assertTrue($model->hasAttribute('id')); + $this->assertFalse($model->hasAttribute('comments')); + $this->assertFalse($model->hasAttribute('foo')); + $this->assertEquals(array(),$model->getAttributes(false)); + + $post=new Post2; + $this->assertNull($post->id); + $this->assertNull($post->title); + $post->setAttributes(array('id'=>3,'title'=>'test title')); + $this->assertNull($post->id); + $this->assertEquals('test title',$post->title); + } + + public function testFind() + { + // test find() with various parameters + $post=Post2::model()->find(); + $this->assertTrue($post instanceof Post2); + $this->assertEquals(1,$post->id); + + $post=Post2::model()->find('id=5'); + $this->assertTrue($post instanceof Post2); + $this->assertEquals(5,$post->id); + + $post=Post2::model()->find('id=:id',array(':id'=>2)); + $this->assertTrue($post instanceof Post2); + $this->assertEquals(2,$post->id); + + $post=Post2::model()->find(array('condition'=>'id=:id','params'=>array(':id'=>3))); + $this->assertTrue($post instanceof Post2); + $this->assertEquals(3,$post->id); + + // test find() without result + $post=Post2::model()->find('id=6'); + $this->assertNull($post); + + // test findAll() with various parameters + $posts=Post2::model()->findAll(); + $this->assertEquals(5,count($posts)); + $this->assertTrue($posts[3] instanceof Post2); + $this->assertEquals(4,$posts[3]->id); + + $posts=Post2::model()->findAll(new CDbCriteria(array('limit'=>3,'offset'=>1))); + $this->assertEquals(3,count($posts)); + $this->assertTrue($posts[2] instanceof Post2); + $this->assertEquals(4,$posts[2]->id); + + // test findAll() without result + $posts=Post2::model()->findAll('id=6'); + $this->assertTrue($posts===array()); + + // test findByPk + $post=Post2::model()->findByPk(2); + $this->assertEquals(2,$post->id); + + $post=Post2::model()->findByPk(array(3,2)); + $this->assertEquals(2,$post->id); + + $post=Post2::model()->findByPk(array()); + $this->assertNull($post); + + $post=Post2::model()->findByPk(6); + $this->assertNull($post); + + // test findAllByPk + $posts=Post2::model()->findAllByPk(2); + $this->assertEquals(1,count($posts)); + $this->assertEquals(2,$posts[0]->id); + + $posts=Post2::model()->findAllByPk(array(4,3,2),'id<4'); + $this->assertEquals(2,count($posts)); + $this->assertEquals(2,$posts[0]->id); + $this->assertEquals(3,$posts[1]->id); + + $posts=Post2::model()->findAllByPk(array()); + $this->assertTrue($posts===array()); + + // test findByAttributes + $post=Post2::model()->findByAttributes(array('author_id'=>2),array('order'=>'id DESC')); + $this->assertEquals(4,$post->id); + + // test findAllByAttributes + $posts=Post2::model()->findAllByAttributes(array('author_id'=>2)); + $this->assertEquals(3,count($posts)); + + // test findBySql + $post=Post2::model()->findBySql('select * from test.posts where id=:id',array(':id'=>2)); + $this->assertEquals(2,$post->id); + + // test findAllBySql + $posts=Post2::model()->findAllBySql('select * from test.posts where id>:id',array(':id'=>2)); + $this->assertEquals(3,count($posts)); + + // test count + $this->assertEquals(5,Post2::model()->count()); + $this->assertEquals(3,Post2::model()->count(array('condition'=>'id>2'))); + + // test countBySql + $this->assertEquals(1,Post2::model()->countBySql('select id from test.posts limit 1')); + + // test exists + $this->assertTrue(Post2::model()->exists('id=:id',array(':id'=>1))); + $this->assertFalse(Post2::model()->exists('id=:id',array(':id'=>6))); + } + + public function testInsert() + { + $post=new Post2; + $this->assertEquals(array(),$post->getAttributes(false)); + $post->title='test post 1'; + $post->create_time='2004-10-19 10:23:54'; + $post->author_id=1; + $post->content='test post content 1'; + $this->assertTrue($post->isNewRecord); + $this->assertNull($post->id); + $this->assertTrue($post->save()); + $this->assertEquals(array( + 'id'=>6, + 'title'=>'test post 1', + 'create_time'=>$post->create_time, + 'author_id'=>1, + 'content'=>'test post content 1'),$post->getAttributes()); + $this->assertFalse($post->isNewRecord); + $this->assertEquals($post->getAttributes(false),Post2::model()->findByPk($post->id)->getAttributes(false)); + } + + public function testUpdate() + { + // test save + $post=Post2::model()->findByPk(1); + $this->assertFalse($post->isNewRecord); + $this->assertEquals('post 1',$post->title); + $post->title='test post 1'; + $this->assertTrue($post->save()); + $this->assertFalse($post->isNewRecord); + $this->assertEquals('test post 1',$post->title); + $this->assertEquals('test post 1',Post2::model()->findByPk(1)->title); + + // test updateByPk + $this->assertEquals(2,Post2::model()->updateByPk(array(4,5),array('title'=>'test post'))); + $this->assertEquals('post 2',Post2::model()->findByPk(2)->title); + $this->assertEquals('test post',Post2::model()->findByPk(4)->title); + $this->assertEquals('test post',Post2::model()->findByPk(5)->title); + + // test updateAll + $this->assertEquals(1,Post2::model()->updateAll(array('title'=>'test post'),'id=1')); + $this->assertEquals('test post',Post2::model()->findByPk(1)->title); + + // test updateCounters + $this->assertEquals(2,Post2::model()->findByPk(2)->author_id); + $this->assertEquals(2,Post2::model()->findByPk(3)->author_id); + $this->assertEquals(2,Post2::model()->findByPk(4)->author_id); + $this->assertEquals(3,Post2::model()->updateCounters(array('author_id'=>-1),'id>2')); + $this->assertEquals(2,Post2::model()->findByPk(2)->author_id); + $this->assertEquals(1,Post2::model()->findByPk(3)->author_id); + $this->assertEquals(1,Post2::model()->findByPk(4)->author_id); + $this->assertEquals(2,Post2::model()->findByPk(5)->author_id); + } + + public function testDelete() + { + $post=Post2::model()->findByPk(1); + $this->assertTrue($post->delete()); + $this->assertNull(Post2::model()->findByPk(1)); + + $this->assertTrue(Post2::model()->findByPk(2) instanceof Post2); + $this->assertTrue(Post2::model()->findByPk(3) instanceof Post2); + $this->assertEquals(2,Post2::model()->deleteByPk(array(2,3))); + $this->assertNull(Post2::model()->findByPk(2)); + $this->assertNull(Post2::model()->findByPk(3)); + + $this->assertTrue(Post2::model()->findByPk(5) instanceof Post2); + $this->assertEquals(1,Post2::model()->deleteAll('id=5')); + $this->assertNull(Post2::model()->findByPk(5)); + } + + public function testRefresh() + { + $post=Post2::model()->findByPk(1); + $post2=Post2::model()->findByPk(1); + $post2->title='new post'; + $post2->save(); + $this->assertEquals('post 1',$post->title); + $this->assertTrue($post->refresh()); + $this->assertEquals('new post',$post->title); + } + + public function testEquals() + { + $post=Post2::model()->findByPk(1); + $post2=Post2::model()->findByPk(1); + $post3=Post2::model()->findByPk(3); + $this->assertEquals(1,$post->primaryKey); + $this->assertTrue($post->equals($post2)); + $this->assertTrue($post2->equals($post)); + $this->assertFalse($post->equals($post3)); + $this->assertFalse($post3->equals($post)); + } + + public function testValidation() + { + $user=new User2; + $user->password='passtest'; + $this->assertFalse($user->hasErrors()); + $this->assertEquals(array(),$user->errors); + $this->assertEquals(array(),$user->getErrors('username')); + $this->assertFalse($user->save()); + $this->assertNull($user->id); + $this->assertTrue($user->isNewRecord); + $this->assertTrue($user->hasErrors()); + $this->assertTrue($user->hasErrors('username')); + $this->assertTrue($user->hasErrors('email')); + $this->assertFalse($user->hasErrors('password')); + $this->assertEquals(1,count($user->getErrors('username'))); + $this->assertEquals(1,count($user->getErrors('email'))); + $this->assertEquals(2,count($user->errors)); + + $user->clearErrors(); + $this->assertFalse($user->hasErrors()); + $this->assertEquals(array(),$user->errors); + } + + public function testCompositeKey() + { + $order=new Order2; + $this->assertEquals(array('key1','key2'),$order->tableSchema->primaryKey); + $order=Order2::model()->findByPk(array('key1'=>2,'key2'=>1)); + $this->assertEquals('order 21',$order->name); + $orders=Order2::model()->findAllByPk(array(array('key1'=>2,'key2'=>1),array('key1'=>1,'key2'=>3))); + $this->assertEquals('order 13',$orders[0]->name); + $this->assertEquals('order 21',$orders[1]->name); + } + + public function testDefault() + { + $type=new ComplexType2; + $this->assertEquals(1,$type->int_col2); + $this->assertEquals('something',$type->char_col2); + $this->assertEquals(1.23,$type->real_col); + $this->assertEquals(null,$type->numeric_col); + $this->assertEquals(null,$type->time); + $this->assertEquals(null,$type->bool_col); + $this->assertEquals(true,$type->bool_col2); + } + + public function testPublicAttribute() + { + $post=new PostExt2; + $this->assertEquals(array('id'=>null,'title'=>'default title'),$post->getAttributes(false)); + $post=Post2::model()->findByPk(1); + $this->assertEquals(array( + 'id'=>1, + 'title'=>'post 1', + 'create_time'=>'2004-10-19 10:23:54', + 'author_id'=>1, + 'content'=>'content 1'),$post->getAttributes(false)); + + $post=new PostExt2; + $post->title='test post'; + $post->create_time='2004-10-19 10:23:53'; + $post->author_id=1; + $post->content='test'; + $post->save(); + $this->assertEquals(array( + 'id'=>6, + 'title'=>'test post', + 'create_time'=>'2004-10-19 10:23:53', + 'author_id'=>1, + 'content'=>'test'),$post->getAttributes(false)); + } + + public function testLazyRelation() + { + // test belongsTo + $post=Post2::model()->findByPk(2); + $this->assertTrue($post->author instanceof User2); + $this->assertEquals(array( + 'id'=>2, + 'username'=>'user2', + 'password'=>'pass2', + 'email'=>'email2'),$post->author->getAttributes(false)); + + // test hasOne + $post=Post2::model()->findByPk(2); + $this->assertTrue($post->firstComment instanceof Comment2); + $this->assertEquals(array( + 'id'=>4, + 'content'=>'comment 4', + 'post_id'=>2, + 'author_id'=>2),$post->firstComment->getAttributes(false)); + $post=Post2::model()->findByPk(4); + $this->assertNull($post->firstComment); + + // test hasMany + $post=Post2::model()->findByPk(2); + $this->assertEquals(2,count($post->comments)); + $this->assertEquals(array( + 'id'=>5, + 'content'=>'comment 5', + 'post_id'=>2, + 'author_id'=>2),$post->comments[0]->getAttributes(false)); + $this->assertEquals(array( + 'id'=>4, + 'content'=>'comment 4', + 'post_id'=>2, + 'author_id'=>2),$post->comments[1]->getAttributes(false)); + $post=Post2::model()->findByPk(4); + $this->assertEquals(array(),$post->comments); + + // test manyMany + $post=Post2::model()->findByPk(2); + $this->assertEquals(2,count($post->categories)); + + // TODO: when joining, need to replace both placeholders for the two joinin tables + $this->assertEquals(array( + 'id'=>4, + 'name'=>'cat 4', + 'parent_id'=>1),$post->categories[0]->getAttributes(false)); + $this->assertEquals(array( + 'id'=>1, + 'name'=>'cat 1', + 'parent_id'=>null),$post->categories[1]->getAttributes(false)); + + + $post=Post2::model()->findByPk(4); + $this->assertEquals(array(),$post->categories); + + // test self join + $category=Category2::model()->findByPk(5); + $this->assertEquals(array(),$category->posts); + $this->assertEquals(2,count($category->children)); + $this->assertEquals(array( + 'id'=>6, + 'name'=>'cat 6', + 'parent_id'=>5),$category->children[0]->getAttributes(false)); + $this->assertEquals(array( + 'id'=>7, + 'name'=>'cat 7', + 'parent_id'=>5),$category->children[1]->getAttributes(false)); + $this->assertTrue($category->parent instanceof Category2); + $this->assertEquals(array( + 'id'=>1, + 'name'=>'cat 1', + 'parent_id'=>null),$category->parent->getAttributes(false)); + + $category=Category2::model()->findByPk(2); + $this->assertEquals(1,count($category->posts)); + $this->assertEquals(array(),$category->children); + $this->assertNull($category->parent); + + // test composite key + $order=Order2::model()->findByPk(array('key1'=>1,'key2'=>2)); + $this->assertEquals(2,count($order->items)); + $order=Order2::model()->findByPk(array('key1'=>2,'key2'=>1)); + $this->assertEquals(0,count($order->items)); + $item=Item2::model()->findByPk(4); + $this->assertTrue($item->order instanceof Order2); + $this->assertEquals(array( + 'key1'=>2, + 'key2'=>2, + 'name'=>'order 22'),$item->order->getAttributes(false)); + } + + public function testEagerRelation() + { + $post=Post2::model()->with('author','firstComment','comments','categories')->findByPk(2); + $this->assertEquals(array( + 'id'=>2, + 'username'=>'user2', + 'password'=>'pass2', + 'email'=>'email2'),$post->author->getAttributes(false)); + $this->assertTrue($post->firstComment instanceof Comment2); + $this->assertEquals(array( + 'id'=>4, + 'content'=>'comment 4', + 'post_id'=>2, + 'author_id'=>2),$post->firstComment->getAttributes(false)); + $this->assertEquals(2,count($post->comments)); + $this->assertEquals(array( + 'id'=>5, + 'content'=>'comment 5', + 'post_id'=>2, + 'author_id'=>2),$post->comments[0]->getAttributes(false)); + $this->assertEquals(array( + 'id'=>4, + 'content'=>'comment 4', + 'post_id'=>2, + 'author_id'=>2),$post->comments[1]->getAttributes(false)); + $this->assertEquals(2,count($post->categories)); + + $this->assertEquals(array( + 'id'=>4, + 'name'=>'cat 4', + 'parent_id'=>1),$post->categories[0]->getAttributes(false)); + $this->assertEquals(array( + 'id'=>1, + 'name'=>'cat 1', + 'parent_id'=>null),$post->categories[1]->getAttributes(false)); + + $post=Post2::model()->with('author','firstComment','comments','categories')->findByPk(4); + $this->assertEquals(array( + 'id'=>2, + 'username'=>'user2', + 'password'=>'pass2', + 'email'=>'email2'),$post->author->getAttributes(false)); + $this->assertNull($post->firstComment); + $this->assertEquals(array(),$post->comments); + $this->assertEquals(array(),$post->categories); + } + + public function testLazyRecursiveRelation() + { + $post=PostExt2::model()->findByPk(2); + $this->assertEquals(2,count($post->comments)); + $this->assertTrue($post->comments[0]->post instanceof Post2); + $this->assertTrue($post->comments[1]->post instanceof Post2); + $this->assertTrue($post->comments[0]->author instanceof User2); + $this->assertTrue($post->comments[1]->author instanceof User2); + $this->assertEquals(3,count($post->comments[0]->author->posts)); + $this->assertEquals(3,count($post->comments[1]->author->posts)); + $this->assertTrue($post->comments[0]->author->posts[1]->author instanceof User2); + + // test self join + $category=Category2::model()->findByPk(1); + $this->assertEquals(2,count($category->nodes)); + $this->assertTrue($category->nodes[0]->parent instanceof Category2); + $this->assertTrue($category->nodes[1]->parent instanceof Category2); + $this->assertEquals(0,count($category->nodes[0]->children)); + $this->assertEquals(2,count($category->nodes[1]->children)); + } + + public function testEagerRecursiveRelation() + { + $post=Post2::model()->with(array('comments'=>'author','categories'))->findByPk(2); + $this->assertEquals(2,count($post->comments)); + $this->assertEquals(2,count($post->categories)); + } + + public function testRelationWithCondition() + { + $posts=Post2::model()->with('comments')->findAllByPk(array(2,3,4),array('order'=>'t.id')); + $this->assertEquals(3,count($posts)); + $this->assertEquals(2,count($posts[0]->comments)); + $this->assertEquals(4,count($posts[1]->comments)); + $this->assertEquals(0,count($posts[2]->comments)); + + $post=Post2::model()->with('comments')->findByAttributes(array('id'=>2)); + $this->assertTrue($post instanceof Post2); + $this->assertEquals(2,count($post->comments)); + $posts=Post2::model()->with('comments')->findAllByAttributes(array('id'=>2)); + $this->assertEquals(1,count($posts)); + + $post=Post2::model()->with('comments')->findBySql('select * from test.posts where id=:id',array(':id'=>2)); + $this->assertTrue($post instanceof Post2); + $posts=Post2::model()->with('comments')->findAllBySql('select * from test.posts where id=:id1 OR id=:id2',array(':id1'=>2,':id2'=>3)); + $this->assertEquals(2,count($posts)); + + $post=Post2::model()->with('comments','author')->find('t.id=:id',array(':id'=>2)); + $this->assertTrue($post instanceof Post2); + + $posts=Post2::model()->with('comments','author')->findAll(array( + 'select'=>'title', + 'condition'=>'t.id=:id', + 'limit'=>1, + 'offset'=>0, + 'order'=>'t.title', + 'params'=>array(':id'=>2))); + $this->assertTrue($posts[0] instanceof Post2); + + $posts=Post2::model()->with('comments','author')->findAll(array( + 'select'=>'title', + 'condition'=>'t.id=:id', + 'limit'=>1, + 'offset'=>2, + 'order'=>'t.title', + 'params'=>array(':id'=>2))); + $this->assertTrue($posts===array()); + } + + public function testSelfManyMany() + { + $user=User2::model()->findByPk(1); + $this->assertTrue($user instanceof User2); + $friends=$user->friends; + $this->assertEquals(count($friends),2); + $this->assertEquals($friends[0]->id,2); + $this->assertEquals($friends[1]->id,3); + + $user=User2::model()->with('friends')->findByPk(1); + $this->assertTrue($user instanceof User2); + $friends=$user->friends; + $this->assertEquals(count($friends),2); + $this->assertEquals($friends[0]->id,2); + $this->assertEquals($friends[1]->id,3); + } + + public function testRelationalCount() + { + $count=Post2::model()->with('author','firstComment','comments','categories')->count(); + $this->assertEquals(5,$count); + + $count=Post2::model()->with('author','firstComment','comments','categories')->count('t.id=4'); + $this->assertEquals(1,$count); + + $count=Post2::model()->with('author','firstComment','comments','categories')->count('t.id=14'); + $this->assertEquals(0,$count); + } + + public function testEmptyFinding() + { + $post=Post2::model()->with('author','firstComment','comments','categories')->find('t.id=100'); + $this->assertNull($post); + + $posts=Post2::model()->with('author','firstComment','comments','categories')->findAll('t.id=100'); + $this->assertTrue($posts===array()); + + $post=Post2::model()->with('author','firstComment','comments','categories')->findBySql('SELECT * FROM test.posts WHERE id=100'); + $this->assertNull($post); + + Post2::model()->with('author','firstComment','comments','categories')->findAllBySql('SELECT * FROM test.posts WHERE id=100'); + $this->assertTrue($posts===array()); + } +} \ No newline at end of file diff --git a/tests/framework/db/ar/CActiveRecordEventWrappersTest.php b/tests/framework/db/ar/CActiveRecordEventWrappersTest.php new file mode 100644 index 000000000..642e4b303 --- /dev/null +++ b/tests/framework/db/ar/CActiveRecordEventWrappersTest.php @@ -0,0 +1,156 @@ +markTestSkipped('PDO and SQLite extensions are required.'); + + $this->_connection=new CDbConnection('sqlite::memory:'); + $this->_connection->active=true; + $this->_connection->pdoInstance->exec(file_get_contents(dirname(__FILE__).'/../data/sqlite.sql')); + CActiveRecord::$db=$this->_connection; + + UserWithWrappers::clearCounters(); + PostWithWrappers::clearCounters(); + CommentWithWrappers::clearCounters(); + } + + public function tearDown() + { + $this->_connection->active=false; + } + + public function testBeforeFind() + { + UserWithWrappers::model()->find(); + $this->assertEquals(UserWithWrappers::getCounter('beforeFind'),1); + UserWithWrappers::model()->findByAttributes(array('username'=>'user1')); + $this->assertEquals(UserWithWrappers::getCounter('beforeFind'),1); + UserWithWrappers::model()->findByPk(1); + $this->assertEquals(UserWithWrappers::getCounter('beforeFind'),1); + UserWithWrappers::model()->findBySql('SELECT * FROM users'); + $this->assertEquals(UserWithWrappers::getCounter('beforeFind'),1); + UserWithWrappers::model()->findAll(); + $this->assertEquals(UserWithWrappers::getCounter('beforeFind'),1); + UserWithWrappers::model()->findAllByAttributes(array('username'=>'user1')); + $this->assertEquals(UserWithWrappers::getCounter('beforeFind'),1); + UserWithWrappers::model()->findAllByPk(1); + $this->assertEquals(UserWithWrappers::getCounter('beforeFind'),1); + UserWithWrappers::model()->findAllBySql('SELECT * FROM users'); + $this->assertEquals(UserWithWrappers::getCounter('beforeFind'),1); + } + + public function testBeforeFindRelationalEager() + { + UserWithWrappers::model()->with('posts.comments')->find(); + $this->assertEquals(UserWithWrappers::getCounter('beforeFind'),1); + $this->assertEquals(PostWithWrappers::getCounter('beforeFind'),1); + $this->assertEquals(CommentWithWrappers::getCounter('beforeFind'),1); + UserWithWrappers::model()->with('posts.comments')->findByAttributes(array('username'=>'user1')); + $this->assertEquals(UserWithWrappers::getCounter('beforeFind'),1); + $this->assertEquals(PostWithWrappers::getCounter('beforeFind'),1); + $this->assertEquals(CommentWithWrappers::getCounter('beforeFind'),1); + UserWithWrappers::model()->with('posts.comments')->findByPk(1); + $this->assertEquals(UserWithWrappers::getCounter('beforeFind'),1); + $this->assertEquals(PostWithWrappers::getCounter('beforeFind'),1); + $this->assertEquals(CommentWithWrappers::getCounter('beforeFind'),1); + UserWithWrappers::model()->with('posts.comments')->findBySql('SELECT * FROM users'); + $this->assertEquals(UserWithWrappers::getCounter('beforeFind'),1); + $this->assertEquals(PostWithWrappers::getCounter('beforeFind'),1); + $this->assertEquals(CommentWithWrappers::getCounter('beforeFind'),1); + UserWithWrappers::model()->with('posts.comments')->findAll(); + $this->assertEquals(UserWithWrappers::getCounter('beforeFind'),1); + $this->assertEquals(PostWithWrappers::getCounter('beforeFind'),1); + $this->assertEquals(CommentWithWrappers::getCounter('beforeFind'),1); + UserWithWrappers::model()->with('posts.comments')->findAllByAttributes(array('username'=>'user1')); + $this->assertEquals(UserWithWrappers::getCounter('beforeFind'),1); + $this->assertEquals(PostWithWrappers::getCounter('beforeFind'),1); + $this->assertEquals(CommentWithWrappers::getCounter('beforeFind'),1); + UserWithWrappers::model()->with('posts.comments')->findAllByPk(1); + $this->assertEquals(UserWithWrappers::getCounter('beforeFind'),1); + $this->assertEquals(PostWithWrappers::getCounter('beforeFind'),1); + $this->assertEquals(CommentWithWrappers::getCounter('beforeFind'),1); + UserWithWrappers::model()->with('posts.comments')->findAllBySql('SELECT * FROM users'); + $this->assertEquals(UserWithWrappers::getCounter('beforeFind'),1); + $this->assertEquals(PostWithWrappers::getCounter('beforeFind'),1); + $this->assertEquals(CommentWithWrappers::getCounter('beforeFind'),1); + } + + public function testBeforeFindRelationalLazy() + { + $user=UserWithWrappers::model()->find(); + $user->posts; + $this->assertEquals(UserWithWrappers::getCounter('beforeFind'),1); + $this->assertEquals(PostWithWrappers::getCounter('beforeFind'),1); + $user=UserWithWrappers::model()->find(); + $user->posts(array('with'=>'comments')); + $this->assertEquals(UserWithWrappers::getCounter('beforeFind'),1); + $this->assertEquals(PostWithWrappers::getCounter('beforeFind'),1); + $this->assertEquals(CommentWithWrappers::getCounter('beforeFind'),1); + } + + public function testAfterFind() + { + UserWithWrappers::model()->find(); + $this->assertEquals(UserWithWrappers::getCounter('afterFind'),1); + UserWithWrappers::model()->findByAttributes(array('username'=>'user1')); + $this->assertEquals(UserWithWrappers::getCounter('afterFind'),1); + UserWithWrappers::model()->findByPk(1); + $this->assertEquals(UserWithWrappers::getCounter('afterFind'),1); + UserWithWrappers::model()->findBySql('SELECT * FROM users'); + $this->assertEquals(UserWithWrappers::getCounter('afterFind'),1); + UserWithWrappers::model()->findAll(); + $this->assertEquals(UserWithWrappers::getCounter('afterFind'),4); + UserWithWrappers::model()->findAllByAttributes(array('username'=>'user1')); + $this->assertEquals(UserWithWrappers::getCounter('afterFind'),1); + UserWithWrappers::model()->findAllByPk(1); + $this->assertEquals(UserWithWrappers::getCounter('afterFind'),1); + UserWithWrappers::model()->findAllBySql('SELECT * FROM users'); + $this->assertEquals(UserWithWrappers::getCounter('afterFind'),4); + } + + public function testAfterFindRelational() + { + UserWithWrappers::model()->with('posts.comments')->find(); + $this->assertEquals(UserWithWrappers::getCounter('afterFind'),4); + $this->assertEquals(PostWithWrappers::getCounter('afterFind'),5); + $this->assertEquals(CommentWithWrappers::getCounter('afterFind'),10); + UserWithWrappers::model()->with('posts.comments')->findByAttributes(array('username'=>'user2')); + $this->assertEquals(UserWithWrappers::getCounter('afterFind'),1); + $this->assertEquals(PostWithWrappers::getCounter('afterFind'),3); + $this->assertEquals(CommentWithWrappers::getCounter('afterFind'),6); + UserWithWrappers::model()->with('posts.comments')->findByPk(2); + $this->assertEquals(UserWithWrappers::getCounter('afterFind'),1); + $this->assertEquals(PostWithWrappers::getCounter('afterFind'),3); + $this->assertEquals(CommentWithWrappers::getCounter('afterFind'),6); + UserWithWrappers::model()->with('posts.comments')->findBySql('SELECT * FROM users WHERE id=2'); + $this->assertEquals(UserWithWrappers::getCounter('afterFind'),1); + $this->assertEquals(PostWithWrappers::getCounter('afterFind'),3); + $this->assertEquals(CommentWithWrappers::getCounter('afterFind'),6); + UserWithWrappers::model()->with('posts.comments')->findAll(); + $this->assertEquals(UserWithWrappers::getCounter('afterFind'),4); + $this->assertEquals(PostWithWrappers::getCounter('afterFind'),5); + $this->assertEquals(CommentWithWrappers::getCounter('afterFind'),10); + UserWithWrappers::model()->with('posts.comments')->findAllByAttributes(array('username'=>'user2')); + $this->assertEquals(UserWithWrappers::getCounter('afterFind'),1); + $this->assertEquals(PostWithWrappers::getCounter('afterFind'),3); + $this->assertEquals(CommentWithWrappers::getCounter('afterFind'),6); + UserWithWrappers::model()->with('posts.comments')->findAllByPk(2); + $this->assertEquals(UserWithWrappers::getCounter('afterFind'),1); + $this->assertEquals(PostWithWrappers::getCounter('afterFind'),3); + $this->assertEquals(CommentWithWrappers::getCounter('afterFind'),6); + UserWithWrappers::model()->with('posts.comments')->findAllBySql('SELECT * FROM users'); + $this->assertEquals(UserWithWrappers::getCounter('afterFind'),4); + $this->assertEquals(PostWithWrappers::getCounter('afterFind'),5); + $this->assertEquals(CommentWithWrappers::getCounter('afterFind'),10); + } +} \ No newline at end of file diff --git a/tests/framework/db/ar/CActiveRecordHasManyThroughModels.php b/tests/framework/db/ar/CActiveRecordHasManyThroughModels.php new file mode 100644 index 000000000..17f8a80f3 --- /dev/null +++ b/tests/framework/db/ar/CActiveRecordHasManyThroughModels.php @@ -0,0 +1,54 @@ +array(self::HAS_MANY, 'UserGroup', 'user_id'), + 'groups'=>array(self::HAS_MANY, 'TestGroup', 'through'=>'usergroups'), + ); + } +} + +class TestGroup extends CActiveRecord { + public static function model($className=__CLASS__) { + return parent::model($className); + } + + public function tableName() { + return 'tbl_group'; + } + + public function relations() { + return array( + 'usergroups'=>array(self::HAS_MANY, 'TestUserGroup', 'group_id'), + 'users'=>array(self::HAS_MANY, 'TestUser', 'through'=>'usergroups'), + ); + } +} + +class TestUserGroup extends CActiveRecord { + public static function model($className=__CLASS__) { + return parent::model($className); + } + + public function tableName() { + return 'tbl_user_group'; + } + + public function relations() { + return array( + 'users'=>array(self::BELONGS_TO, 'TestUser', 'user_id'), + 'groups'=>array(self::BELONGS_TO, 'TestGroup', 'group_id'), + ); + } +} \ No newline at end of file diff --git a/tests/framework/db/ar/CActiveRecordHasManyThroughTest.php b/tests/framework/db/ar/CActiveRecordHasManyThroughTest.php new file mode 100644 index 000000000..138609371 --- /dev/null +++ b/tests/framework/db/ar/CActiveRecordHasManyThroughTest.php @@ -0,0 +1,77 @@ +markTestSkipped('PDO and SQLite extensions are required.'); + + // put db into runtime + $this->dbPath = dirname(dirname(dirname(dirname(__FILE__)))).'/assets/CActiveRecordHasManyThroughTest.sqlite'; + + $db = new SQLiteDatabase($this->dbPath); + $db->query(file_get_contents(dirname(__FILE__).'/CActiveRecordHasManyThroughTest.sql')); + unset($db); + + $config=array( + 'basePath'=>dirname(__FILE__), + 'components'=>array( + 'db'=>array( + 'class'=>'system.db.CDbConnection', + 'connectionString'=>'sqlite:'.$this->dbPath, + ), + ), + ); + $app=new TestApplication($config); + $app->db->active=true; + CActiveRecord::$db=$this->db=$app->db; + } + + public function tearDown(){ + if($this->db) + $this->db->active=false; + + // clean up db file + unlink($this->dbPath); + } + + public function testEager(){ + $user = User::model()->with('groups')->findByPk(1); + $result = array(); + foreach($user->groups as $group){ + foreach($group->usergroups as $usergroup){ + $result[] = array($user->usename, $group->name, $usergroup->role); + } + } + + $this->assertEquals(array( + array('Alexander', 'Yii', 'dev'), + array('Alexander', 'Zii', 'user'), + ), $result); + } + + public function testLazy(){ + $user = User::model()->findByPk(1); + + $result = array(); + foreach($user->groups as $group){ + foreach($group->usergroups as $usergroup){ + $result[] = array($user->usename, $group->name, $usergroup->role); + } + } + + $this->assertEquals(array( + array('Alexander', 'Yii', 'dev'), + array('Alexander', 'Zii', 'user'), + ), $result); + } +} diff --git a/tests/framework/db/ar/CActiveRecordHasManyThroughTest.sql b/tests/framework/db/ar/CActiveRecordHasManyThroughTest.sql new file mode 100644 index 000000000..9ce5a6096 --- /dev/null +++ b/tests/framework/db/ar/CActiveRecordHasManyThroughTest.sql @@ -0,0 +1,29 @@ +BEGIN; + +CREATE TABLE tbl_user ( + id INTEGER PRIMARY KEY, + username VARCHAR(255) +); + +CREATE TABLE tbl_group ( + id INTEGER PRIMARY KEY, + name VARCHAR(255) +); + +CREATE TABLE tbl_user_group ( + user_id INTEGER, + group_id INTEGER, + role VARCHAR(255) +); + +INSERT INTO tbl_user (id, username) VALUES(1, 'Alexander'); +INSERT INTO tbl_user (id, username) VALUES(2, 'Qiang'); + +INSERT INTO tbl_group (id, name) VALUES(1, 'Yii'); +INSERT INTO tbl_group (id, name) VALUES(2, 'Zii'); + +INSERT INTO tbl_user_group (user_id, group_id, role) VALUES(1, 1, 'dev'); +INSERT INTO tbl_user_group (user_id, group_id, role) VALUES(1, 2, 'user'); +INSERT INTO tbl_user_group (user_id, group_id, role) VALUES(2, 1, 'dev'); + +COMMIT; \ No newline at end of file diff --git a/tests/framework/db/ar/CActiveRecordTest.php b/tests/framework/db/ar/CActiveRecordTest.php new file mode 100644 index 000000000..9e9df8a61 --- /dev/null +++ b/tests/framework/db/ar/CActiveRecordTest.php @@ -0,0 +1,878 @@ +markTestSkipped('PDO and SQLite extensions are required.'); + + $this->_connection=new CDbConnection('sqlite::memory:'); + $this->_connection->active=true; + $this->_connection->pdoInstance->exec(file_get_contents(dirname(__FILE__).'/../data/sqlite.sql')); + CActiveRecord::$db=$this->_connection; + } + + protected function tearDown() + { + $this->_connection->active=false; + } + + public function testModel() + { + $model=Post::model(); + $this->assertTrue($model instanceof Post); + $this->assertTrue($model->dbConnection===$this->_connection); + $this->assertTrue($model->dbConnection->active); + $this->assertEquals('posts',$model->tableName()); + $this->assertEquals('id',$model->tableSchema->primaryKey); + $this->assertTrue($model->tableSchema->sequenceName===''); + $this->assertEquals(array(),$model->attributeLabels()); + $this->assertEquals('Id',$model->getAttributeLabel('id')); + $this->assertEquals('Author Id',$model->getAttributeLabel('author_id')); + $this->assertTrue($model->getActiveRelation('author') instanceof CBelongsToRelation); + $this->assertTrue($model->tableSchema instanceof CDbTableSchema); + $this->assertTrue($model->commandBuilder instanceof CDbCommandBuilder); + $this->assertTrue($model->hasAttribute('id')); + $this->assertFalse($model->hasAttribute('comments')); + $this->assertFalse($model->hasAttribute('foo')); + $this->assertEquals(array('id'=>null,'title'=>null,'create_time'=>null,'author_id'=>null,'content'=>null),$model->attributes); + + $post=new Post; + $this->assertNull($post->id); + $this->assertNull($post->title); + $post->setAttributes(array('id'=>3,'title'=>'test title')); + $this->assertNull($post->id); + $this->assertEquals('test title',$post->title); + } + + public function testFind() + { + // test find() with various parameters + $post=Post::model()->find(); + $this->assertTrue($post instanceof Post); + $this->assertEquals(1,$post->id); + + $post=Post::model()->find('id=5'); + $this->assertTrue($post instanceof Post); + $this->assertEquals(5,$post->id); + + $post=Post::model()->find('id=:id',array(':id'=>2)); + $this->assertTrue($post instanceof Post); + $this->assertEquals(2,$post->id); + + $post=Post::model()->find(array('condition'=>'id=:id','params'=>array(':id'=>3))); + $this->assertTrue($post instanceof Post); + $this->assertEquals(3,$post->id); + + // test find() without result + $post=Post::model()->find('id=6'); + $this->assertNull($post); + + // test findAll() with various parameters + $posts=Post::model()->findAll(); + $this->assertEquals(5,count($posts)); + $this->assertTrue($posts[3] instanceof Post); + $this->assertEquals(4,$posts[3]->id); + + $posts=Post::model()->findAll(new CDbCriteria(array('limit'=>3,'offset'=>1))); + $this->assertEquals(3,count($posts)); + $this->assertTrue($posts[2] instanceof Post); + $this->assertEquals(4,$posts[2]->id); + + // test findAll() without result + $posts=Post::model()->findAll('id=6'); + $this->assertTrue($posts===array()); + + // test findByPk + $post=Post::model()->findByPk(2); + $this->assertEquals(2,$post->id); + + $post=Post::model()->findByPk(array(3,2)); + $this->assertEquals(2,$post->id); + + $post=Post::model()->findByPk(array()); + $this->assertNull($post); + + $post=Post::model()->findByPk(6); + $this->assertNull($post); + + // test findAllByPk + $posts=Post::model()->findAllByPk(2); + $this->assertEquals(1,count($posts)); + $this->assertEquals(2,$posts[0]->id); + + $posts=Post::model()->findAllByPk(array(4,3,2),'id<4'); + $this->assertEquals(2,count($posts)); + $this->assertEquals(2,$posts[0]->id); + $this->assertEquals(3,$posts[1]->id); + + $posts=Post::model()->findAllByPk(array()); + $this->assertTrue($posts===array()); + + // test findByAttributes + $post=Post::model()->findByAttributes(array('author_id'=>2),array('order'=>'id DESC')); + $this->assertEquals(4,$post->id); + + // test findAllByAttributes + $posts=Post::model()->findAllByAttributes(array('author_id'=>2)); + $this->assertEquals(3,count($posts)); + + // test findBySql + $post=Post::model()->findBySql('select * from posts where id=:id',array(':id'=>2)); + $this->assertEquals(2,$post->id); + + // test findAllBySql + $posts=Post::model()->findAllBySql('select * from posts where id>:id',array(':id'=>2)); + $this->assertEquals(3,count($posts)); + + // test count + $this->assertEquals(5,Post::model()->count()); + $this->assertEquals(3,Post::model()->count(array('condition'=>'id>2'))); + + // test countBySql + $this->assertEquals(1,Post::model()->countBySql('select id from posts limit 1')); + + // test exists + $this->assertTrue(Post::model()->exists('id=:id',array(':id'=>1))); + $this->assertFalse(Post::model()->exists('id=:id',array(':id'=>6))); + } + + public function testInsert() + { + $post=new Post; + $this->assertEquals(array('id'=>null,'title'=>null,'create_time'=>null,'author_id'=>null,'content'=>null),$post->attributes); + $post->title='test post 1'; + $post->create_time=time(); + $post->author_id=1; + $post->content='test post content 1'; + $this->assertTrue($post->isNewRecord); + $this->assertNull($post->id); + $this->assertTrue($post->save()); + $this->assertEquals(array( + 'id'=>6, + 'title'=>'test post 1', + 'create_time'=>$post->create_time, + 'author_id'=>1, + 'content'=>'test post content 1'),$post->attributes); + $this->assertFalse($post->isNewRecord); + $this->assertEquals($post->attributes,Post::model()->findByPk($post->id)->attributes); + } + + public function testUpdate() + { + // test save + $post=Post::model()->findByPk(1); + $this->assertFalse($post->isNewRecord); + $this->assertEquals('post 1',$post->title); + $post->title='test post 1'; + $this->assertTrue($post->save()); + $this->assertFalse($post->isNewRecord); + $this->assertEquals('test post 1',$post->title); + $this->assertEquals('test post 1',Post::model()->findByPk(1)->title); + + // test updateByPk + $this->assertEquals(2,Post::model()->updateByPk(array(4,5),array('title'=>'test post'))); + $this->assertEquals('post 2',Post::model()->findByPk(2)->title); + $this->assertEquals('test post',Post::model()->findByPk(4)->title); + $this->assertEquals('test post',Post::model()->findByPk(5)->title); + + // test updateAll + $this->assertEquals(1,Post::model()->updateAll(array('title'=>'test post'),'id=1')); + $this->assertEquals('test post',Post::model()->findByPk(1)->title); + + // test updateCounters + $this->assertEquals(2,Post::model()->findByPk(2)->author_id); + $this->assertEquals(2,Post::model()->findByPk(3)->author_id); + $this->assertEquals(2,Post::model()->findByPk(4)->author_id); + $this->assertEquals(3,Post::model()->updateCounters(array('author_id'=>-1),'id>2')); + $this->assertEquals(2,Post::model()->findByPk(2)->author_id); + $this->assertEquals(1,Post::model()->findByPk(3)->author_id); + } + + public function testDelete() + { + $post=Post::model()->findByPk(1); + $this->assertTrue($post->delete()); + $this->assertNull(Post::model()->findByPk(1)); + + $this->assertTrue(Post::model()->findByPk(2) instanceof Post); + $this->assertTrue(Post::model()->findByPk(3) instanceof Post); + $this->assertEquals(2,Post::model()->deleteByPk(array(2,3))); + $this->assertNull(Post::model()->findByPk(2)); + $this->assertNull(Post::model()->findByPk(3)); + + $this->assertTrue(Post::model()->findByPk(5) instanceof Post); + $this->assertEquals(1,Post::model()->deleteAll('id=5')); + $this->assertNull(Post::model()->findByPk(5)); + } + + public function testRefresh() + { + $post=Post::model()->findByPk(1); + $post2=Post::model()->findByPk(1); + $post2->title='new post'; + $post2->save(); + $this->assertEquals('post 1',$post->title); + $this->assertTrue($post->refresh()); + $this->assertEquals('new post',$post->title); + } + + public function testEquals() + { + $post=Post::model()->findByPk(1); + $post2=Post::model()->findByPk(1); + $post3=Post::model()->findByPk(3); + $this->assertEquals(1,$post->primaryKey); + $this->assertTrue($post->equals($post2)); + $this->assertTrue($post2->equals($post)); + $this->assertFalse($post->equals($post3)); + $this->assertFalse($post3->equals($post)); + } + + public function testValidation() + { + $user=new User; + $user->password='passtest'; + $this->assertFalse($user->hasErrors()); + $this->assertEquals(array(),$user->errors); + $this->assertEquals(array(),$user->getErrors('username')); + $this->assertFalse($user->save()); + $this->assertNull($user->id); + $this->assertTrue($user->isNewRecord); + $this->assertTrue($user->hasErrors()); + $this->assertTrue($user->hasErrors('username')); + $this->assertTrue($user->hasErrors('email')); + $this->assertFalse($user->hasErrors('password')); + $this->assertEquals(1,count($user->getErrors('username'))); + $this->assertEquals(1,count($user->getErrors('email'))); + $this->assertEquals(2,count($user->errors)); + + $user->clearErrors(); + $this->assertFalse($user->hasErrors()); + $this->assertEquals(array(),$user->errors); + } + + public function testCompositeKey() + { + $order=new Order; + $this->assertEquals(array('key1','key2'),$order->tableSchema->primaryKey); + $order=Order::model()->findByPk(array('key1'=>2,'key2'=>1)); + $this->assertEquals('order 21',$order->name); + $orders=Order::model()->findAllByPk(array(array('key1'=>2,'key2'=>1),array('key1'=>1,'key2'=>3))); + $this->assertEquals('order 13',$orders[0]->name); + $this->assertEquals('order 21',$orders[1]->name); + } + + public function testDefault() + { + $type=new ComplexType; + $this->assertEquals(1,$type->int_col2); + $this->assertEquals('something',$type->char_col2); + $this->assertEquals(1.23,$type->float_col2); + $this->assertEquals(33.22,$type->numeric_col); + $this->assertEquals(123,$type->time); + $this->assertEquals(null,$type->bool_col); + $this->assertEquals(true,$type->bool_col2); + } + + public function testPublicAttribute() + { + $post=new PostExt; + $this->assertEquals(array('id'=>null,'title'=>'default title','create_time'=>null,'author_id'=>null,'content'=>null),$post->attributes); + $post=Post::model()->findByPk(1); + $this->assertEquals(array( + 'id'=>1, + 'title'=>'post 1', + 'create_time'=>100000, + 'author_id'=>1, + 'content'=>'content 1'),$post->attributes); + + $post=new PostExt; + $post->title='test post'; + $post->create_time=1000000; + $post->author_id=1; + $post->content='test'; + $post->save(); + $this->assertEquals(array( + 'id'=>6, + 'title'=>'test post', + 'create_time'=>1000000, + 'author_id'=>1, + 'content'=>'test'),$post->attributes); + } + + public function testLazyRelation() + { + // test belongsTo + $post=Post::model()->findByPk(2); + $this->assertTrue($post->author instanceof User); + $this->assertEquals(array( + 'id'=>2, + 'username'=>'user2', + 'password'=>'pass2', + 'email'=>'email2'),$post->author->attributes); + + // test hasOne + $post=Post::model()->findByPk(2); + $this->assertTrue($post->firstComment instanceof Comment); + $this->assertEquals(array( + 'id'=>4, + 'content'=>'comment 4', + 'post_id'=>2, + 'author_id'=>2),$post->firstComment->attributes); + $post=Post::model()->findByPk(4); + $this->assertNull($post->firstComment); + + // test hasMany + $post=Post::model()->findByPk(2); + $this->assertEquals(2,count($post->comments)); + $this->assertEquals(array( + 'id'=>5, + 'content'=>'comment 5', + 'post_id'=>2, + 'author_id'=>2),$post->comments[0]->attributes); + $this->assertEquals(array( + 'id'=>4, + 'content'=>'comment 4', + 'post_id'=>2, + 'author_id'=>2),$post->comments[1]->attributes); + $post=Post::model()->findByPk(4); + $this->assertEquals(array(),$post->comments); + + // test manyMany + $post=Post::model()->findByPk(2); + $this->assertEquals(2,count($post->categories)); + $this->assertEquals(array( + 'id'=>4, + 'name'=>'cat 4', + 'parent_id'=>1),$post->categories[0]->attributes); + $this->assertEquals(array( + 'id'=>1, + 'name'=>'cat 1', + 'parent_id'=>null),$post->categories[1]->attributes); + $post=Post::model()->findByPk(4); + $this->assertEquals(array(),$post->categories); + + // test self join + $category=Category::model()->findByPk(5); + $this->assertEquals(array(),$category->posts); + $this->assertEquals(2,count($category->children)); + $this->assertEquals(array( + 'id'=>6, + 'name'=>'cat 6', + 'parent_id'=>5),$category->children[0]->attributes); + $this->assertEquals(array( + 'id'=>7, + 'name'=>'cat 7', + 'parent_id'=>5),$category->children[1]->attributes); + $this->assertTrue($category->parent instanceof Category); + $this->assertEquals(array( + 'id'=>1, + 'name'=>'cat 1', + 'parent_id'=>null),$category->parent->attributes); + + $category=Category::model()->findByPk(2); + $this->assertEquals(1,count($category->posts)); + $this->assertEquals(array(),$category->children); + $this->assertNull($category->parent); + + // test composite key + $order=Order::model()->findByPk(array('key1'=>1,'key2'=>2)); + $this->assertEquals(2,count($order->items)); + $order=Order::model()->findByPk(array('key1'=>2,'key2'=>1)); + $this->assertEquals(0,count($order->items)); + $item=Item::model()->findByPk(4); + $this->assertTrue($item->order instanceof Order); + $this->assertEquals(array( + 'key1'=>2, + 'key2'=>2, + 'name'=>'order 22'),$item->order->attributes); + } + + public function testEagerRelation2() + { + $post=Post::model()->with('author','firstComment','comments','categories')->findByPk(2); + } + + private function checkEagerLoadedModel($post) + { + $this->assertEquals(array( + 'id'=>2, + 'username'=>'user2', + 'password'=>'pass2', + 'email'=>'email2'),$post->author->attributes); + $this->assertTrue($post->firstComment instanceof Comment); + $this->assertEquals(array( + 'id'=>4, + 'content'=>'comment 4', + 'post_id'=>2, + 'author_id'=>2),$post->firstComment->attributes); + $this->assertEquals(2,count($post->comments)); + $this->assertEquals(array( + 'id'=>5, + 'content'=>'comment 5', + 'post_id'=>2, + 'author_id'=>2),$post->comments[0]->attributes); + $this->assertEquals(array( + 'id'=>4, + 'content'=>'comment 4', + 'post_id'=>2, + 'author_id'=>2),$post->comments[1]->attributes); + $this->assertEquals(2,count($post->categories)); + $this->assertEquals(array( + 'id'=>4, + 'name'=>'cat 4', + 'parent_id'=>1),$post->categories[0]->attributes); + $this->assertEquals(array( + 'id'=>1, + 'name'=>'cat 1', + 'parent_id'=>null),$post->categories[1]->attributes); + } + + public function testEagerRelation() + { + $post=Post::model()->with('author','firstComment','comments','categories')->findByPk(2); + $this->checkEagerLoadedModel($post); + $post=Post::model()->findByPk(2,array( + 'with'=>array('author','firstComment','comments','categories'), + )); + $this->checkEagerLoadedModel($post); + + $post=Post::model()->with('author','firstComment','comments','categories')->findByPk(4); + $this->assertEquals(array( + 'id'=>2, + 'username'=>'user2', + 'password'=>'pass2', + 'email'=>'email2'),$post->author->attributes); + $this->assertNull($post->firstComment); + $this->assertEquals(array(),$post->comments); + $this->assertEquals(array(),$post->categories); + } + + public function testLazyRecursiveRelation() + { + $post=PostExt::model()->findByPk(2); + $this->assertEquals(2,count($post->comments)); + $this->assertTrue($post->comments[0]->post instanceof Post); + $this->assertTrue($post->comments[1]->post instanceof Post); + $this->assertTrue($post->comments[0]->author instanceof User); + $this->assertTrue($post->comments[1]->author instanceof User); + $this->assertEquals(3,count($post->comments[0]->author->posts)); + $this->assertEquals(3,count($post->comments[1]->author->posts)); + $this->assertTrue($post->comments[0]->author->posts[1]->author instanceof User); + + // test self join + $category=Category::model()->findByPk(1); + $this->assertEquals(2,count($category->nodes)); + $this->assertTrue($category->nodes[0]->parent instanceof Category); + $this->assertTrue($category->nodes[1]->parent instanceof Category); + $this->assertEquals(0,count($category->nodes[0]->children)); + $this->assertEquals(2,count($category->nodes[1]->children)); + } + + public function testEagerRecursiveRelation() + { + //$post=Post::model()->with(array('comments'=>'author','categories'))->findByPk(2); + $post=Post::model()->with('comments.author','categories')->findByPk(2); + $this->assertEquals(2,count($post->comments)); + $this->assertEquals(2,count($post->categories)); + + $posts=PostExt::model()->with('comments')->findAll(); + $this->assertEquals(5,count($posts)); + } + + public function testRelationWithCondition() + { + $posts=Post::model()->with('comments')->findAllByPk(array(2,3,4),array('order'=>'t.id')); + $this->assertEquals(3,count($posts)); + $this->assertEquals(2,count($posts[0]->comments)); + $this->assertEquals(4,count($posts[1]->comments)); + $this->assertEquals(0,count($posts[2]->comments)); + + $post=Post::model()->with('comments')->findByAttributes(array('id'=>2)); + $this->assertTrue($post instanceof Post); + $this->assertEquals(2,count($post->comments)); + $posts=Post::model()->with('comments')->findAllByAttributes(array('id'=>2)); + $this->assertEquals(1,count($posts)); + + $post=Post::model()->with('comments')->findBySql('select * from posts where id=:id',array(':id'=>2)); + $this->assertTrue($post instanceof Post); + $posts=Post::model()->with('comments')->findAllBySql('select * from posts where id=:id1 OR id=:id2',array(':id1'=>2,':id2'=>3)); + $this->assertEquals(2,count($posts)); + + $post=Post::model()->with('comments','author')->find('t.id=:id',array(':id'=>2)); + $this->assertTrue($post instanceof Post); + + $posts=Post::model()->with('comments','author')->findAll(array( + 'select'=>'title', + 'condition'=>'t.id=:id', + 'limit'=>1, + 'offset'=>0, + 'order'=>'t.title', + 'group'=>'t.id', + 'params'=>array(':id'=>2))); + $this->assertTrue($posts[0] instanceof Post); + + $posts=Post::model()->with('comments','author')->findAll(array( + 'select'=>'title', + 'condition'=>'t.id=:id', + 'limit'=>1, + 'offset'=>2, + 'order'=>'t.title', + 'params'=>array(':id'=>2))); + $this->assertTrue($posts===array()); + } + + public function testRelationWithColumnAlias() + { + $users=User::model()->with('posts')->findAll(array( + 'select'=>'id, username AS username2', + 'order'=>'username2', + )); + + $this->assertEquals(4,count($users)); + $this->assertEquals($users[1]->username,null); + $this->assertEquals($users[1]->username2,'user2'); + } + + public function testRelationalWithoutFK() + { + $users=UserNoFk::model()->with('posts')->findAll(); + $this->assertEquals(4,count($users)); + $this->assertEquals(3,count($users[1]->posts)); + + $posts=PostNoFk::model()->with('author')->findAll(); + $this->assertEquals(5,count($posts)); + $this->assertTrue($posts[2]->author instanceof UserNoFk); + } + + public function testRelationWithNewRecord() + { + $user=new User; + $posts=$user->posts; + $this->assertTrue(is_array($posts) && empty($posts)); + + $post=new Post; + $author=$post->author; + $this->assertNull($author); + } + + public function testRelationWithDynamicCondition() + { + $user=User::model()->with('posts')->findByPk(2); + $this->assertEquals($user->posts[0]->id,2); + $this->assertEquals($user->posts[1]->id,3); + $this->assertEquals($user->posts[2]->id,4); + $user=User::model()->with(array('posts'=>array('order'=>'posts.id DESC')))->findByPk(2); + $this->assertEquals($user->posts[0]->id,4); + $this->assertEquals($user->posts[1]->id,3); + $this->assertEquals($user->posts[2]->id,2); + } + + public function testEagerTogetherRelation() + { + $post=Post::model()->with('author','firstComment','comments','categories')->findByPk(2); + $comments=$post->comments; + $this->assertEquals(array( + 'id'=>2, + 'username'=>'user2', + 'password'=>'pass2', + 'email'=>'email2'),$post->author->attributes); + $this->assertTrue($post->firstComment instanceof Comment); + $this->assertEquals(array( + 'id'=>4, + 'content'=>'comment 4', + 'post_id'=>2, + 'author_id'=>2),$post->firstComment->attributes); + $this->assertEquals(2,count($post->comments)); + $this->assertEquals(array( + 'id'=>5, + 'content'=>'comment 5', + 'post_id'=>2, + 'author_id'=>2),$post->comments[0]->attributes); + $this->assertEquals(array( + 'id'=>4, + 'content'=>'comment 4', + 'post_id'=>2, + 'author_id'=>2),$post->comments[1]->attributes); + $this->assertEquals(2,count($post->categories)); + $this->assertEquals(array( + 'id'=>4, + 'name'=>'cat 4', + 'parent_id'=>1),$post->categories[0]->attributes); + $this->assertEquals(array( + 'id'=>1, + 'name'=>'cat 1', + 'parent_id'=>null),$post->categories[1]->attributes); + + $post=Post::model()->with('author','firstComment','comments','categories')->findByPk(4); + $this->assertEquals(array( + 'id'=>2, + 'username'=>'user2', + 'password'=>'pass2', + 'email'=>'email2'),$post->author->attributes); + $this->assertNull($post->firstComment); + $this->assertEquals(array(),$post->comments); + $this->assertEquals(array(),$post->categories); + } + + public function testRelationalCount() + { + $count=Post::model()->with('author','firstComment','comments','categories')->count(); + $this->assertEquals(5,$count); + + $count=Post::model()->count(array('with'=>array('author','firstComment','comments','categories'))); + $this->assertEquals(5,$count); + + $count=Post::model()->with('author','firstComment','comments','categories')->count('t.id=4'); + $this->assertEquals(1,$count); + + $count=Post::model()->with('author','firstComment','comments','categories')->count('t.id=14'); + $this->assertEquals(0,$count); + } + + public function testRelationalStat() + { + $users=User::model()->with('postCount')->findAll(); + $this->assertEquals(4,count($users)); + $this->assertEquals(1,$users[0]->postCount); + $this->assertEquals(3,$users[1]->postCount); + $this->assertEquals(1,$users[2]->postCount); + + $users=User::model()->findAll(); + $this->assertEquals(4,count($users)); + $this->assertEquals(1,$users[0]->postCount); + $this->assertEquals(3,$users[1]->postCount); + $this->assertEquals(1,$users[2]->postCount); + + $orders=Order::model()->with('itemCount')->findAll(); + $this->assertEquals(4,count($orders)); + $this->assertEquals(2,$orders[0]->itemCount); + $this->assertEquals(1,$orders[1]->itemCount); + $this->assertEquals(0,$orders[2]->itemCount); + $this->assertEquals(2,$orders[3]->itemCount); + + $orders=Order::model()->findAll(); + $this->assertEquals(4,count($orders)); + $this->assertEquals(2,$orders[0]->itemCount); + $this->assertEquals(1,$orders[1]->itemCount); + $this->assertEquals(0,$orders[2]->itemCount); + $this->assertEquals(2,$orders[3]->itemCount); + + $categories=Category::model()->with('postCount')->findAll(); + $this->assertEquals(7,count($categories)); + $this->assertEquals(3,$categories[0]->postCount); + $this->assertEquals(1,$categories[1]->postCount); + $this->assertEquals(1,$categories[2]->postCount); + $this->assertEquals(1,$categories[3]->postCount); + $this->assertEquals(0,$categories[4]->postCount); + $this->assertEquals(0,$categories[5]->postCount); + $this->assertEquals(0,$categories[6]->postCount); + + $categories=Category::model()->findAll(); + $this->assertEquals(7,count($categories)); + $this->assertEquals(3,$categories[0]->postCount); + $this->assertEquals(1,$categories[1]->postCount); + $this->assertEquals(1,$categories[2]->postCount); + $this->assertEquals(1,$categories[3]->postCount); + $this->assertEquals(0,$categories[4]->postCount); + $this->assertEquals(0,$categories[5]->postCount); + $this->assertEquals(0,$categories[6]->postCount); + + $users=User::model()->with('postCount','posts.commentCount')->findAll(); + $this->assertEquals(4,count($users)); + } + + public function testScopes() + { + $posts=Post::model()->post23()->findAll(); + $this->assertEquals(2,count($posts)); + $this->assertEquals(2,$posts[0]->id); + $this->assertEquals(3,$posts[1]->id); + + $post=Post::model()->post23()->find(); + $this->assertEquals(2,$post->id); + + $posts=Post::model()->post23()->post3()->findAll(); + $this->assertEquals(1,count($posts)); + $this->assertEquals(3,$posts[0]->id); + + $post=Post::model()->post23()->find(); + $this->assertTrue($post instanceof Post); + $this->assertEquals(2,$post->id); + + $posts=Post::model()->post23()->findAll('id=3'); + $this->assertEquals(1,count($posts)); + $this->assertEquals(3,$posts[0]->id); + + $posts=Post::model()->recent()->with('author')->findAll(); + $this->assertEquals(5,count($posts)); + $this->assertEquals(5,$posts[0]->id); + $this->assertEquals(4,$posts[1]->id); + + $posts=Post::model()->recent(3)->findAll(); + $this->assertEquals(3,count($posts)); + $this->assertEquals(5,$posts[0]->id); + $this->assertEquals(4,$posts[1]->id); + + $posts=PostSpecial::model()->findAll(); + $this->assertEquals(2,count($posts)); + $this->assertEquals(2,$posts[0]->id); + $this->assertEquals(3,$posts[1]->id); + + $posts=PostSpecial::model()->desc()->findAll(); + $this->assertEquals(2,count($posts)); + $this->assertEquals(3,$posts[0]->id); + $this->assertEquals(2,$posts[1]->id); + } + + public function testResetScope(){ + // resetting named scope + $posts=Post::model()->post23()->resetScope()->findAll(); + $this->assertEquals(5,count($posts)); + + // resetting default scope + $posts=PostSpecial::model()->resetScope()->findAll(); + $this->assertEquals(5,count($posts)); + } + + public function testLazyLoadingWithConditions() + { + $user=User::model()->findByPk(2); + $posts=$user->posts; + $this->assertEquals(3,count($posts)); + $posts=$user->posts(array('condition'=>'posts.id>=3', 'alias'=>'posts')); + $this->assertEquals(2,count($posts)); + } + + public function testScopeWithRelations() + { + $user=User::model()->with('posts:post23')->findByPk(2); + $this->assertEquals(2,count($user->posts)); + $this->assertEquals(2,$user->posts[0]->id); + $this->assertEquals(3,$user->posts[1]->id); + + $user=UserSpecial::model()->findByPk(2); + $posts=$user->posts; + $this->assertEquals(2,count($posts)); + $this->assertEquals(2,$posts[0]->id); + $this->assertEquals(3,$posts[1]->id); + + $user=UserSpecial::model()->findByPk(2); + $posts=$user->posts(array('params'=>array(':id1'=>4),'order'=>'posts.id DESC')); + $this->assertEquals(2,count($posts)); + $this->assertEquals(4,$posts[0]->id); + $this->assertEquals(3,$posts[1]->id); + } + + public function testDuplicateLazyLoadingBug() + { + $user=User::model()->with(array( + 'posts'=>array('on'=>'posts.id=-1') + ))->findByPk(1); + // with the bug, an eager loading for 'posts' would be trigger in the following + // and result with non-empty posts + $this->assertTrue($user->posts===array()); + } + + public function testTogether() + { + // test without together + $users=UserNoTogether::model()->with('posts.comments')->findAll(); + $postCount=0; + $commentCount=0; + foreach($users as $user) + { + $postCount+=count($user->posts); + foreach($posts=$user->posts as $post) + $commentCount+=count($post->comments); + } + $this->assertEquals(4,count($users)); + $this->assertEquals(5,$postCount); + $this->assertEquals(10,$commentCount); + + // test with together + $users=UserNoTogether::model()->with('posts.comments')->together()->findAll(); + $postCount=0; + $commentCount=0; + foreach($users as $user) + { + $postCount+=count($user->posts); + foreach($posts=$user->posts as $post) + $commentCount+=count($post->comments); + } + $this->assertEquals(3,count($users)); + $this->assertEquals(4,$postCount); + $this->assertEquals(10,$commentCount); + } + + public function testTogetherWithOption() + { + // test with together off option + $users=User::model()->with(array( + 'posts'=>array( + 'with'=>array( + 'comments'=>array( + 'joinType'=>'INNER JOIN', + 'together'=>false, + ), + ), + 'joinType'=>'INNER JOIN', + 'together'=>false, + ), + ))->findAll(); + + $postCount=0; + $commentCount=0; + foreach($users as $user) + { + $postCount+=count($user->posts); + foreach($posts=$user->posts as $post) + $commentCount+=count($post->comments); + } + $this->assertEquals(4,count($users)); + $this->assertEquals(5,$postCount); + $this->assertEquals(10,$commentCount); + + // test with together on option + $users=User::model()->with(array( + 'posts'=>array( + 'with'=>array( + 'comments'=>array( + 'joinType'=>'INNER JOIN', + 'together'=>true, + ), + ), + 'joinType'=>'INNER JOIN', + 'together'=>true, + ), + ))->findAll(); + + $postCount=0; + $commentCount=0; + foreach($users as $user) + { + $postCount+=count($user->posts); + foreach($posts=$user->posts as $post) + $commentCount+=count($post->comments); + } + $this->assertEquals(3,count($users)); + $this->assertEquals(4,$postCount); + $this->assertEquals(10,$commentCount); + } + + public function testCountByAttributes() + { + $n=Post::model()->countByAttributes(array('author_id'=>2)); + $this->assertEquals(3,$n); + + } +} diff --git a/tests/framework/db/data/models.php b/tests/framework/db/data/models.php new file mode 100644 index 000000000..10c2a3f6e --- /dev/null +++ b/tests/framework/db/data/models.php @@ -0,0 +1,596 @@ +'/^[\d\w_]+$/'), + array('email', 'email'), + array('username', 'length', 'min'=>3, 'max'=>32), + array('password', 'length', 'min'=>6, 'max'=>32), + ); + } + + public function relations() + { + return array( + 'posts'=>array(self::HAS_MANY,'Post','author_id'), + 'postCount'=>array(self::STAT,'Post','author_id'), + ); + } + + public function tableName() + { + return 'users'; + } +} + +class Post extends CActiveRecord +{ + public static function model($class=__CLASS__) + { + return parent::model($class); + } + + public function relations() + { + return array( + 'author'=>array(self::BELONGS_TO,'User','author_id'), + 'firstComment'=>array(self::HAS_ONE,'Comment','post_id','order'=>'firstComment.content'), + 'comments'=>array(self::HAS_MANY,'Comment','post_id','order'=>'comments.content DESC'), + 'commentCount'=>array(self::STAT,'Comment','post_id'), + 'categories'=>array(self::MANY_MANY,'Category','post_category(post_id,category_id)','order'=>'categories.id DESC'), + ); + } + + public function tableName() + { + return 'posts'; + } + + public function scopes() + { + return array( + 'post23'=>array('condition'=>'posts.id=2 OR posts.id=3', 'alias'=>'posts', 'order'=>'posts.id'), + 'post3'=>array('condition'=>'id=3'), + 'postX'=>array('condition'=>'id=:id1 OR id=:id2', 'params'=>array(':id1'=>2, ':id2'=>3)), + ); + } + + public function rules() + { + return array( + array('title', 'required'), + ); + } + + public function recent($limit=5) + { + $this->getDbCriteria()->mergeWith(array( + 'order'=>'create_time DESC', + 'limit'=>$limit, + )); + return $this; + } +} + +class PostSpecial extends CActiveRecord +{ + public static function model($class=__CLASS__) + { + return parent::model($class); + } + + public function tableName() + { + return 'posts'; + } + + public function defaultScope() + { + return array( + 'condition'=>'posts.id=:id1 OR posts.id=:id2', + 'params'=>array(':id1'=>2, ':id2'=>3), + 'alias'=>'posts', + ); + } + + public function scopes() + { + return array( + 'desc'=>array('order'=>'id DESC'), + ); + } +} + +class UserSpecial extends CActiveRecord +{ + public static function model($class=__CLASS__) + { + return parent::model($class); + } + + public function relations() + { + return array( + 'posts'=>array(self::HAS_MANY,'PostSpecial','author_id'), + ); + } + + public function tableName() + { + return 'users'; + } +} + +class PostExt extends CActiveRecord +{ + public $title='default title'; + public $id; + + public static function model($class=__CLASS__) + { + return parent::model($class); + } + + public function tableName() + { + return 'posts'; + } + + public function relations() + { + return array( + 'comments'=>array(self::HAS_MANY,'Comment','post_id','order'=>'comments.content DESC','with'=>array('post'=>array('alias'=>'post'), 'author')), + ); + } +} + +class Comment extends CActiveRecord +{ + public static function model($class=__CLASS__) + { + return parent::model($class); + } + + public function relations() + { + return array( + 'post'=>array(self::BELONGS_TO,'Post','post_id'), + 'author'=>array(self::BELONGS_TO,'User','author_id'), + ); + } + + public function tableName() + { + return 'comments'; + } +} + + +class Category extends CActiveRecord +{ + public static function model($class=__CLASS__) + { + return parent::model($class); + } + + public function tableName() + { + return 'categories'; + } + + public function relations() + { + return array( + 'posts'=>array(self::MANY_MANY, 'Post', 'post_category(post_id,category_id)'), + 'parent'=>array(self::BELONGS_TO,'Category','parent_id'), + 'children'=>array(self::HAS_MANY,'Category','parent_id'), + 'nodes'=>array(self::HAS_MANY,'Category','parent_id','with'=>array('parent','children')), + 'postCount'=>array(self::STAT, 'Post', 'post_category(post_id,category_id)'), + ); + } +} + + +class Order extends CActiveRecord +{ + public static function model($class=__CLASS__) + { + return parent::model($class); + } + + public function relations() + { + return array( + 'items'=>array(self::HAS_MANY,'Item','col1, col2'), + 'itemCount'=>array(self::STAT,'Item','col1, col2'), + ); + } + + public function tableName() + { + return 'orders'; + } +} + +class Item extends CActiveRecord +{ + public static function model($class=__CLASS__) + { + return parent::model($class); + } + + public function relations() + { + return array( + 'order'=>array(self::BELONGS_TO,'Order','col1, col2','alias'=>'_order'), + ); + } + + public function tableName() + { + return 'items'; + } +} + +class ComplexType extends CActiveRecord +{ + public static function model($class=__CLASS__) + { + return parent::model($class); + } + + public function tableName() + { + return 'types'; + } +} + +class Content extends CActiveRecord +{ + public static function model($class=__CLASS__) + { + return parent::model($class); + } + + public function tableName() + { + return 'Content'; + } + + public function relations() + { + return array( + 'parent'=>array(self::BELONGS_TO,'Content','parentID'), + 'children'=>array(self::HAS_MANY,'Content','parentID'), + 'owner'=>array(self::BELONGS_TO,'User','ownerID'), + ); + } +} + +class Article extends Content +{ + public static function model($class=__CLASS__) + { + return parent::model($class); + } + + public function tableName() + { + return 'Article'; + } + + public function relations() + { + return array( + 'author'=>array(self::BELONGS_TO,'User','authorID'), + 'comments'=>array(self::HAS_MANY,'ArticleComment','parentID'), + ); + } +} + +class ArticleComment extends Content +{ + public static function model($class=__CLASS__) + { + return parent::model($class); + } + + public function tableName() + { + return 'Comment'; + } + + public function relations() + { + return array( + 'author'=>array(self::BELONGS_TO,'User','authorID'), + 'article'=>array(self::BELONGS_TO,'Article','parentID'), + ); + } +} + + +class UserNoFk extends CActiveRecord +{ + public static function model($class=__CLASS__) + { + return parent::model($class); + } + + public function relations() + { + return array( + 'posts'=>array(self::HAS_MANY,'PostNoFk','author_id'), + ); + } + + public function tableName() + { + return 'users'; + } +} + +class PostNoFk extends CActiveRecord +{ + public static function model($class=__CLASS__) + { + return parent::model($class); + } + + public function relations() + { + return array( + 'author'=>array(self::BELONGS_TO,'UserNoFk','author_id'), + ); + } + + public function tableName() + { + return 'posts_nofk'; + } +} + +class UserNoTogether extends CActiveRecord +{ + public static function model($class=__CLASS__) + { + return parent::model($class); + } + + public function relations() + { + return array( + 'posts'=>array(self::HAS_MANY,'PostNoTogether','author_id','together'=>false,'joinType'=>'INNER JOIN'), + ); + } + + public function tableName() + { + return 'users'; + } +} + +class PostNoTogether extends CActiveRecord +{ + public static function model($class=__CLASS__) + { + return parent::model($class); + } + + public function relations() + { + return array( + 'comments'=>array(self::HAS_MANY,'Comment','post_id','together'=>false,'joinType'=>'INNER JOIN'), + ); + } + + public function tableName() + { + return 'posts'; + } +} + +class UserWithWrappers extends CActiveRecord +{ + private static $_counters=array(); + + public static function model($class=__CLASS__) + { + return parent::model($class); + } + + public function relations() + { + return array( + 'posts'=>array(self::HAS_MANY,'PostWithWrappers','author_id'), + 'postCount'=>array(self::STAT,'PostWithWrappers','author_id'), + ); + } + + public function tableName() + { + return 'users'; + } + + protected function beforeFind() + { + parent::beforeFind(); + $this->incrementCounter(__FUNCTION__); + } + + protected function afterFind() + { + parent::afterFind(); + $this->incrementCounter(__FUNCTION__); + } + + protected function incrementCounter($wrapper) + { + if(isset(self::$_counters[$wrapper])) + self::$_counters[$wrapper]++; + else + self::$_counters[$wrapper]=1; + } + + public static function getCounter($wrapper) + { + if(isset(self::$_counters[$wrapper])) + { + $result=self::$_counters[$wrapper]; + } + else + $result=0; + + self::clearCounters(); + + return $result; + } + + public static function clearCounters() + { + self::$_counters=array(); + } +} + +class PostWithWrappers extends CActiveRecord +{ + private static $_counters=array(); + + public static function model($class=__CLASS__) + { + return parent::model($class); + } + + public function relations() + { + return array( + 'author'=>array(self::BELONGS_TO,'UserWithWrappers','author_id'), + 'comments'=>array(self::HAS_MANY,'CommentWithWrappers','post_id','order'=>'comments.content DESC'), + 'commentCount'=>array(self::STAT,'CommentWithWrappers','post_id'), + ); + } + + public function tableName() + { + return 'posts'; + } + + public function rules() + { + return array( + array('title', 'required'), + ); + } + + protected function beforeFind() + { + parent::beforeFind(); + $this->incrementCounter(__FUNCTION__); + } + + protected function afterFind() + { + parent::afterFind(); + $this->incrementCounter(__FUNCTION__); + } + + protected function incrementCounter($wrapper) + { + if(isset(self::$_counters[$wrapper])) + self::$_counters[$wrapper]++; + else + self::$_counters[$wrapper]=1; + } + + public static function getCounter($wrapper) + { + if(isset(self::$_counters[$wrapper])) + { + $result=self::$_counters[$wrapper]; + } + else + $result=0; + + self::clearCounters(); + + return $result; + } + + public static function clearCounters() + { + self::$_counters=array(); + } +} + +class CommentWithWrappers extends CActiveRecord +{ + private static $_counters=array(); + + public static function model($class=__CLASS__) + { + return parent::model($class); + } + + public function relations() + { + return array( + 'post'=>array(self::BELONGS_TO,'PostWithWrappers','post_id'), + 'author'=>array(self::BELONGS_TO,'UserWithWrappers','author_id'), + ); + } + + public function tableName() + { + return 'comments'; + } + + protected function beforeFind() + { + parent::beforeFind(); + $this->incrementCounter(__FUNCTION__); + } + + protected function afterFind() + { + parent::afterFind(); + $this->incrementCounter(__FUNCTION__); + } + + protected function incrementCounter($wrapper) + { + if(isset(self::$_counters[$wrapper])) + self::$_counters[$wrapper]++; + else + self::$_counters[$wrapper]=1; + } + + public static function getCounter($wrapper) + { + if(isset(self::$_counters[$wrapper])) + { + $result=self::$_counters[$wrapper]; + } + else + $result=0; + + self::clearCounters(); + + return $result; + } + + public static function clearCounters() + { + self::$_counters=array(); + } +} \ No newline at end of file diff --git a/tests/framework/db/data/models2.php b/tests/framework/db/data/models2.php new file mode 100644 index 000000000..53d90b633 --- /dev/null +++ b/tests/framework/db/data/models2.php @@ -0,0 +1,185 @@ +'/^[\d\w_]+$/'), + array('email', 'email'), + array('username', 'length', 'min'=>3, 'max'=>32), + array('password', 'length', 'min'=>6, 'max'=>32), + ); + } + + public function relations() + { + return array( + 'posts'=>array(self::HAS_MANY,'Post2','author_id'), + 'friends'=>array(self::MANY_MANY,'User2','test.user_friends(id,friend)'), + ); + } + + public function tableName() + { + return 'test.users'; + } +} + +class Post2 extends CActiveRecord +{ + public static function model($class=__CLASS__) + { + return parent::model($class); + } + + public function relations() + { + return array( + 'author'=>array(self::BELONGS_TO,'User2','author_id'), + 'firstComment'=>array(self::HAS_ONE,'Comment2','post_id','order'=>'"firstComment".content'), + 'comments'=>array(self::HAS_MANY,'Comment2','post_id','order'=>'comments.content DESC'), + 'categories'=>array(self::MANY_MANY,'Category2','test.post_category(post_id,category_id)','order'=>'categories.id DESC'), + ); + } + + public function rules() + { + return array( + array('title', 'required'), + ); + } + + public function tableName() + { + return 'test.posts'; + } +} + +class PostExt2 extends CActiveRecord +{ + public $title='default title'; + public $id; + + public static function model($class=__CLASS__) + { + return parent::model($class); + } + + public function tableName() + { + return 'test.posts'; + } + + public function relations() + { + return array( + 'comments'=>array(self::HAS_MANY,'Comment2','post_id','order'=>'comments.content DESC','with'=>array('post'=>array('alias'=>'post'), 'author')), + ); + } +} + +class Comment2 extends CActiveRecord +{ + public static function model($class=__CLASS__) + { + return parent::model($class); + } + + public function relations() + { + return array( + 'post'=>array(self::BELONGS_TO,'Post2','post_id'), + 'author'=>array(self::BELONGS_TO,'User2','author_id'), + ); + } + + public function tableName() + { + return 'test.comments'; + } +} + + +class Category2 extends CActiveRecord +{ + public static function model($class=__CLASS__) + { + return parent::model($class); + } + + public function tableName() + { + return 'test.categories'; + } + + public function relations() + { + return array( + 'posts'=>array(self::MANY_MANY, 'Post2', 'test.post_category(post_id,category_id)'), + 'parent'=>array(self::BELONGS_TO,'Category2','parent_id'), + 'children'=>array(self::HAS_MANY,'Category2','parent_id'), + 'nodes'=>array(self::HAS_MANY,'Category2','parent_id','with'=>array('parent','children')), + ); + } +} + + +class Order2 extends CActiveRecord +{ + public static function model($class=__CLASS__) + { + return parent::model($class); + } + + public function relations() + { + return array( + 'items'=>array(self::HAS_MANY,'Item2','col1, col2'), + ); + } + + public function tableName() + { + return 'test.orders'; + } +} + +class Item2 extends CActiveRecord +{ + public static function model($class=__CLASS__) + { + return parent::model($class); + } + + public function relations() + { + return array( + 'order'=>array(self::BELONGS_TO,'Order2','col1, col2','alias'=>'_order'), + ); + } + + public function tableName() + { + return 'test.items'; + } +} + +class ComplexType2 extends CActiveRecord +{ + public static function model($class=__CLASS__) + { + return parent::model($class); + } + + public function tableName() + { + return 'yii_types'; + } +} \ No newline at end of file diff --git a/tests/framework/db/data/mssql.sql b/tests/framework/db/data/mssql.sql new file mode 100644 index 000000000..38967b241 --- /dev/null +++ b/tests/framework/db/data/mssql.sql @@ -0,0 +1,306 @@ +SET ANSI_NULLS ON +GO +SET QUOTED_IDENTIFIER ON +GO +IF NOT EXISTS (SELECT * FROM dbo.sysobjects WHERE id = OBJECT_ID(N'[dbo].[categories]') AND OBJECTPROPERTY(id, N'IsUserTable') = 1) +BEGIN +CREATE TABLE [dbo].[categories]( + [id] [int] IDENTITY(1,1) NOT NULL, + [name] [varchar](128) NOT NULL, + [parent_id] [int] NULL, + CONSTRAINT [PK_categories] PRIMARY KEY CLUSTERED +( + [id] ASC +) ON [PRIMARY] +) ON [PRIMARY] +END +GO +SET ANSI_NULLS ON +GO +SET QUOTED_IDENTIFIER ON +GO +IF NOT EXISTS (SELECT * FROM dbo.sysobjects WHERE id = OBJECT_ID(N'[dbo].[orders]') AND OBJECTPROPERTY(id, N'IsUserTable') = 1) +BEGIN +CREATE TABLE [dbo].[orders]( + [key1] [int] NOT NULL, + [key2] [int] NOT NULL, + [name] [varchar](128) NOT NULL, + CONSTRAINT [PK_orders] PRIMARY KEY CLUSTERED +( + [key1] ASC, + [key2] ASC +) ON [PRIMARY] +) ON [PRIMARY] +END +GO +SET ANSI_NULLS ON +GO +SET QUOTED_IDENTIFIER ON +GO +IF NOT EXISTS (SELECT * FROM dbo.sysobjects WHERE id = OBJECT_ID(N'[dbo].[types]') AND OBJECTPROPERTY(id, N'IsUserTable') = 1) +BEGIN +CREATE TABLE [dbo].[types]( + [int_col] [int] NOT NULL, + [int_col2] [int] NULL CONSTRAINT [DF_types_int_col2] DEFAULT (1), + [char_col] [char](100) NOT NULL, + [char_col2] [varchar](100) NULL CONSTRAINT [DF_types_char_col2] DEFAULT ('something'), + [char_col3] [text] NULL, + [float_col] [real] NOT NULL, + [float_col2] [float] NULL CONSTRAINT [DF_types_float_col2] DEFAULT (1.23), + [blob_col] [image] NULL, + [numeric_col] [numeric](5, 2) NULL CONSTRAINT [DF_types_numeric_col] DEFAULT (33.22), + [time] [datetime] NULL CONSTRAINT [DF_types_time] DEFAULT ('2002-01-01 00:00:00'), + [bool_col] [bit] NOT NULL, + [bool_col2] [bit] NOT NULL CONSTRAINT [DF_types_bool_col2] DEFAULT (1) +) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY] +END +GO +SET ANSI_NULLS ON +GO +SET QUOTED_IDENTIFIER ON +GO +IF NOT EXISTS (SELECT * FROM dbo.sysobjects WHERE id = OBJECT_ID(N'[dbo].[users]') AND OBJECTPROPERTY(id, N'IsUserTable') = 1) +BEGIN +CREATE TABLE [dbo].[users]( + [id] [int] IDENTITY(1,1) NOT NULL, + [username] [varchar](128) NOT NULL, + [password] [varchar](128) NOT NULL, + [email] [varchar](128) NOT NULL, + CONSTRAINT [PK_users] PRIMARY KEY CLUSTERED +( + [id] ASC +) ON [PRIMARY] +) ON [PRIMARY] +END +GO +SET ANSI_NULLS ON +GO +SET QUOTED_IDENTIFIER ON +GO +IF NOT EXISTS (SELECT * FROM dbo.sysobjects WHERE id = OBJECT_ID(N'[dbo].[post_category]') AND OBJECTPROPERTY(id, N'IsUserTable') = 1) +BEGIN +CREATE TABLE [dbo].[post_category]( + [category_id] [int] NOT NULL, + [post_id] [int] NOT NULL, + CONSTRAINT [PK_post_category] PRIMARY KEY CLUSTERED +( + [category_id] ASC, + [post_id] ASC +) ON [PRIMARY] +) ON [PRIMARY] +END +GO +SET ANSI_NULLS ON +GO +SET QUOTED_IDENTIFIER ON +GO +IF NOT EXISTS (SELECT * FROM dbo.sysobjects WHERE id = OBJECT_ID(N'[dbo].[items]') AND OBJECTPROPERTY(id, N'IsUserTable') = 1) +BEGIN +CREATE TABLE [dbo].[items]( + [id] [int] IDENTITY(1,1) NOT NULL, + [name] [varchar](128) NULL, + [col1] [int] NOT NULL, + [col2] [int] NOT NULL, + CONSTRAINT [PK_items] PRIMARY KEY CLUSTERED +( + [id] ASC +) ON [PRIMARY] +) ON [PRIMARY] +END +GO +SET ANSI_NULLS ON +GO +SET QUOTED_IDENTIFIER ON +GO +IF NOT EXISTS (SELECT * FROM dbo.sysobjects WHERE id = OBJECT_ID(N'[dbo].[comments]') AND OBJECTPROPERTY(id, N'IsUserTable') = 1) +BEGIN +CREATE TABLE [dbo].[comments]( + [id] [int] IDENTITY(1,1) NOT NULL, + [content] [text] NOT NULL, + [post_id] [int] NOT NULL, + [author_id] [int] NOT NULL, + CONSTRAINT [PK_comments] PRIMARY KEY CLUSTERED +( + [id] ASC +) ON [PRIMARY] +) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY] +END +GO +SET ANSI_NULLS ON +GO +SET QUOTED_IDENTIFIER ON +GO +IF NOT EXISTS (SELECT * FROM dbo.sysobjects WHERE id = OBJECT_ID(N'[dbo].[posts]') AND OBJECTPROPERTY(id, N'IsUserTable') = 1) +BEGIN +CREATE TABLE [dbo].[posts]( + [id] [int] IDENTITY(1,1) NOT NULL, + [title] [varchar](128) NOT NULL, + [create_time] [datetime] NOT NULL, + [author_id] [int] NOT NULL, + [content] [text] NULL, + CONSTRAINT [PK_posts] PRIMARY KEY CLUSTERED +( + [id] ASC +) ON [PRIMARY] +) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY] +END +GO +SET ANSI_NULLS ON +GO +SET QUOTED_IDENTIFIER ON +GO +IF NOT EXISTS (SELECT * FROM dbo.sysobjects WHERE id = OBJECT_ID(N'[dbo].[profiles]') AND OBJECTPROPERTY(id, N'IsUserTable') = 1) +BEGIN +CREATE TABLE [dbo].[profiles]( + [id] [int] IDENTITY(1,1) NOT NULL, + [first_name] [varchar](128) NOT NULL, + [last_name] [varchar](128) NOT NULL, + [user_id] [int] NOT NULL, + CONSTRAINT [PK_profiles] PRIMARY KEY CLUSTERED +( + [id] ASC +) ON [PRIMARY] +) ON [PRIMARY] +END +GO +IF NOT EXISTS (SELECT * FROM dbo.sysobjects WHERE id = OBJECT_ID(N'[dbo].[FK_categories_categories]') AND type = 'F') +ALTER TABLE [dbo].[categories] WITH CHECK ADD CONSTRAINT [FK_categories_categories] FOREIGN KEY([parent_id]) +REFERENCES [dbo].[categories] ([id]) +GO +IF NOT EXISTS (SELECT * FROM dbo.sysobjects WHERE id = OBJECT_ID(N'[dbo].[FK_post_category_categories]') AND type = 'F') +ALTER TABLE [dbo].[post_category] WITH CHECK ADD CONSTRAINT [FK_post_category_categories] FOREIGN KEY([category_id]) +REFERENCES [dbo].[categories] ([id]) +ON DELETE CASCADE +GO +IF NOT EXISTS (SELECT * FROM dbo.sysobjects WHERE id = OBJECT_ID(N'[dbo].[FK_post_category_posts]') AND type = 'F') +ALTER TABLE [dbo].[post_category] WITH NOCHECK ADD CONSTRAINT [FK_post_category_posts] FOREIGN KEY([post_id]) +REFERENCES [dbo].[posts] ([id]) +ON DELETE CASCADE +GO +ALTER TABLE [dbo].[post_category] CHECK CONSTRAINT [FK_post_category_posts] +GO +IF NOT EXISTS (SELECT * FROM dbo.sysobjects WHERE id = OBJECT_ID(N'[dbo].[FK_items_orders]') AND type = 'F') +ALTER TABLE [dbo].[items] WITH CHECK ADD CONSTRAINT [FK_items_orders] FOREIGN KEY([col1], [col2]) +REFERENCES [dbo].[orders] ([key1], [key2]) +ON DELETE CASCADE +GO +IF NOT EXISTS (SELECT * FROM dbo.sysobjects WHERE id = OBJECT_ID(N'[dbo].[FK_comments_users]') AND type = 'F') +ALTER TABLE [dbo].[comments] WITH NOCHECK ADD CONSTRAINT [FK_comments_users] FOREIGN KEY([author_id]) +REFERENCES [dbo].[users] ([id]) +ON DELETE CASCADE +GO +ALTER TABLE [dbo].[comments] CHECK CONSTRAINT [FK_comments_users] +GO +IF NOT EXISTS (SELECT * FROM dbo.sysobjects WHERE id = OBJECT_ID(N'[dbo].[FK_post_comment]') AND type = 'F') +ALTER TABLE [dbo].[comments] WITH NOCHECK ADD CONSTRAINT [FK_post_comment] FOREIGN KEY([post_id]) +REFERENCES [dbo].[posts] ([id]) +ON DELETE CASCADE +GO +ALTER TABLE [dbo].[comments] CHECK CONSTRAINT [FK_post_comment] +GO +IF NOT EXISTS (SELECT * FROM dbo.sysobjects WHERE id = OBJECT_ID(N'[dbo].[FK_posts_users]') AND type = 'F') +ALTER TABLE [dbo].[posts] WITH NOCHECK ADD CONSTRAINT [FK_posts_users] FOREIGN KEY([author_id]) +REFERENCES [dbo].[users] ([id]) +GO +ALTER TABLE [dbo].[posts] CHECK CONSTRAINT [FK_posts_users] +GO +IF NOT EXISTS (SELECT * FROM dbo.sysobjects WHERE id = OBJECT_ID(N'[dbo].[FK_profile_user]') AND type = 'F') +ALTER TABLE [dbo].[profiles] WITH NOCHECK ADD CONSTRAINT [FK_profile_user] FOREIGN KEY([user_id]) +REFERENCES [dbo].[users] ([id]) +ON DELETE CASCADE +GO +ALTER TABLE [dbo].[profiles] CHECK CONSTRAINT [FK_profile_user] + +INSERT INTO users (username, password, email) VALUES ('user1','pass1','email1') +GO +INSERT INTO users (username, password, email) VALUES ('user2','pass2','email2') +GO +INSERT INTO users (username, password, email) VALUES ('user3','pass3','email3') +GO + +INSERT INTO profiles (first_name, last_name, user_id) VALUES ('first 1','last 1',1) +GO +INSERT INTO profiles (first_name, last_name, user_id) VALUES ('first 2','last 2',2) +GO + +INSERT INTO posts (title, create_time, author_id, content) VALUES ('post 1','2000-01-01',1,'content 1') +GO +INSERT INTO posts (title, create_time, author_id, content) VALUES ('post 2','2000-01-02',2,'content 2') +GO +INSERT INTO posts (title, create_time, author_id, content) VALUES ('post 3','2000-01-03',2,'content 3') +GO +INSERT INTO posts (title, create_time, author_id, content) VALUES ('post 4','2000-01-04',2,'content 4') +GO +INSERT INTO posts (title, create_time, author_id, content) VALUES ('post 5','2000-01-05',3,'content 5') +GO + +INSERT INTO comments (content, post_id, author_id) VALUES ('comment 1',1, 2) +GO +INSERT INTO comments (content, post_id, author_id) VALUES ('comment 2',1, 2) +GO +INSERT INTO comments (content, post_id, author_id) VALUES ('comment 3',1, 2) +GO +INSERT INTO comments (content, post_id, author_id) VALUES ('comment 4',2, 2) +GO +INSERT INTO comments (content, post_id, author_id) VALUES ('comment 5',2, 2) +GO +INSERT INTO comments (content, post_id, author_id) VALUES ('comment 6',3, 2) +GO +INSERT INTO comments (content, post_id, author_id) VALUES ('comment 7',3, 2) +GO +INSERT INTO comments (content, post_id, author_id) VALUES ('comment 8',3, 2) +GO +INSERT INTO comments (content, post_id, author_id) VALUES ('comment 9',3, 2) +GO +INSERT INTO comments (content, post_id, author_id) VALUES ('comment 10',5, 3) +GO + +INSERT INTO categories (name, parent_id) VALUES ('cat 1',NULL) +GO +INSERT INTO categories (name, parent_id) VALUES ('cat 2',NULL) +GO +INSERT INTO categories (name, parent_id) VALUES ('cat 3',NULL) +GO +INSERT INTO categories (name, parent_id) VALUES ('cat 4',1) +GO +INSERT INTO categories (name, parent_id) VALUES ('cat 5',1) +GO +INSERT INTO categories (name, parent_id) VALUES ('cat 6',5) +GO +INSERT INTO categories (name, parent_id) VALUES ('cat 7',5) +GO + +INSERT INTO post_category (category_id, post_id) VALUES (1,1) +GO +INSERT INTO post_category (category_id, post_id) VALUES (2,1) +GO +INSERT INTO post_category (category_id, post_id) VALUES (3,1) +GO +INSERT INTO post_category (category_id, post_id) VALUES (4,2) +GO +INSERT INTO post_category (category_id, post_id) VALUES (1,2) +GO +INSERT INTO post_category (category_id, post_id) VALUES (1,3) +GO + + +INSERT INTO orders (key1,key2,name) VALUES (1,2,'order 12') +GO +INSERT INTO orders (key1,key2,name) VALUES (1,3,'order 13') +GO +INSERT INTO orders (key1,key2,name) VALUES (2,1,'order 21') +GO +INSERT INTO orders (key1,key2,name) VALUES (2,2,'order 22') +GO + + +INSERT INTO items (name,col1,col2) VALUES ('item 1',1,2) +GO +INSERT INTO items (name,col1,col2) VALUES ('item 2',1,2) +GO +INSERT INTO items (name,col1,col2) VALUES ('item 3',1,3) +GO +INSERT INTO items (name,col1,col2) VALUES ('item 4',2,2) +GO +INSERT INTO items (name,col1,col2) VALUES ('item 5',2,2) +GO diff --git a/tests/framework/db/data/mysql.sql b/tests/framework/db/data/mysql.sql new file mode 100644 index 000000000..057b1ad46 --- /dev/null +++ b/tests/framework/db/data/mysql.sql @@ -0,0 +1,150 @@ +/** + * This is the database schema for testing MySQL support of yii Active Record. + * To test this feature, you need to create a database named 'yii' on 'localhost' + * and create an account 'test/test' which owns this test database. + */ + +CREATE TABLE users +( + id INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT, + username VARCHAR(128) NOT NULL, + password VARCHAR(128) NOT NULL, + email VARCHAR(128) NOT NULL +) TYPE=INNODB; + +INSERT INTO users (username, password, email) VALUES ('user1','pass1','email1'); +INSERT INTO users (username, password, email) VALUES ('user2','pass2','email2'); +INSERT INTO users (username, password, email) VALUES ('user3','pass3','email3'); + +CREATE TABLE profiles +( + id INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT, + first_name VARCHAR(128) NOT NULL, + last_name VARCHAR(128) NOT NULL, + user_id INTEGER NOT NULL, + CONSTRAINT FK_profile_user FOREIGN KEY (user_id) + REFERENCES users (id) ON DELETE CASCADE ON UPDATE RESTRICT +) TYPE=INNODB; + +INSERT INTO profiles (first_name, last_name, user_id) VALUES ('first 1','last 1',1); +INSERT INTO profiles (first_name, last_name, user_id) VALUES ('first 2','last 2',2); + +CREATE TABLE posts +( + id INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT, + title VARCHAR(128) NOT NULL, + create_time TIMESTAMP NOT NULL, + author_id INTEGER NOT NULL, + content TEXT, + CONSTRAINT FK_post_author FOREIGN KEY (author_id) + REFERENCES users (id) ON DELETE CASCADE ON UPDATE RESTRICT +) TYPE=INNODB; + +INSERT INTO posts (title, create_time, author_id, content) VALUES ('post 1','2000-01-01',1,'content 1'); +INSERT INTO posts (title, create_time, author_id, content) VALUES ('post 2','2000-01-02',2,'content 2'); +INSERT INTO posts (title, create_time, author_id, content) VALUES ('post 3','2000-01-03',2,'content 3'); +INSERT INTO posts (title, create_time, author_id, content) VALUES ('post 4','2000-01-04',2,'content 4'); +INSERT INTO posts (title, create_time, author_id, content) VALUES ('post 5','2000-01-05',3,'content 5'); + +CREATE TABLE comments +( + id INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT, + content TEXT NOT NULL, + post_id INTEGER NOT NULL, + author_id INTEGER NOT NULL, + CONSTRAINT FK_post_comment FOREIGN KEY (post_id) + REFERENCES posts (id) ON DELETE CASCADE ON UPDATE RESTRICT, + CONSTRAINT FK_user_comment FOREIGN KEY (author_id) + REFERENCES users (id) ON DELETE CASCADE ON UPDATE RESTRICT +) TYPE=INNODB; + +INSERT INTO comments (content, post_id, author_id) VALUES ('comment 1',1, 2); +INSERT INTO comments (content, post_id, author_id) VALUES ('comment 2',1, 2); +INSERT INTO comments (content, post_id, author_id) VALUES ('comment 3',1, 2); +INSERT INTO comments (content, post_id, author_id) VALUES ('comment 4',2, 2); +INSERT INTO comments (content, post_id, author_id) VALUES ('comment 5',2, 2); +INSERT INTO comments (content, post_id, author_id) VALUES ('comment 6',3, 2); +INSERT INTO comments (content, post_id, author_id) VALUES ('comment 7',3, 2); +INSERT INTO comments (content, post_id, author_id) VALUES ('comment 8',3, 2); +INSERT INTO comments (content, post_id, author_id) VALUES ('comment 9',3, 2); +INSERT INTO comments (content, post_id, author_id) VALUES ('comment 10',5, 3); + +CREATE TABLE categories +( + id INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT, + name VARCHAR(128) NOT NULL, + parent_id INTEGER, + CONSTRAINT FK_category_category FOREIGN KEY (parent_id) + REFERENCES categories (id) ON DELETE CASCADE ON UPDATE RESTRICT +) TYPE=INNODB; + +INSERT INTO categories (name, parent_id) VALUES ('cat 1',NULL); +INSERT INTO categories (name, parent_id) VALUES ('cat 2',NULL); +INSERT INTO categories (name, parent_id) VALUES ('cat 3',NULL); +INSERT INTO categories (name, parent_id) VALUES ('cat 4',1); +INSERT INTO categories (name, parent_id) VALUES ('cat 5',1); +INSERT INTO categories (name, parent_id) VALUES ('cat 6',5); +INSERT INTO categories (name, parent_id) VALUES ('cat 7',5); + +CREATE TABLE post_category +( + category_id INTEGER NOT NULL, + post_id INTEGER NOT NULL, + PRIMARY KEY (category_id, post_id), + CONSTRAINT FK_post_category_post FOREIGN KEY (post_id) + REFERENCES posts (id) ON DELETE CASCADE ON UPDATE RESTRICT, + CONSTRAINT FK_post_category_category FOREIGN KEY (category_id) + REFERENCES categories (id) ON DELETE CASCADE ON UPDATE RESTRICT +) TYPE=INNODB; + +INSERT INTO post_category (category_id, post_id) VALUES (1,1); +INSERT INTO post_category (category_id, post_id) VALUES (2,1); +INSERT INTO post_category (category_id, post_id) VALUES (3,1); +INSERT INTO post_category (category_id, post_id) VALUES (4,2); +INSERT INTO post_category (category_id, post_id) VALUES (1,2); +INSERT INTO post_category (category_id, post_id) VALUES (1,3); + +CREATE TABLE orders +( + key1 INTEGER NOT NULL, + key2 INTEGER NOT NULL, + name VARCHAR(128), + PRIMARY KEY (key1, key2) +) TYPE=INNODB; + +INSERT INTO orders (key1,key2,name) VALUES (1,2,'order 12'); +INSERT INTO orders (key1,key2,name) VALUES (1,3,'order 13'); +INSERT INTO orders (key1,key2,name) VALUES (2,1,'order 21'); +INSERT INTO orders (key1,key2,name) VALUES (2,2,'order 22'); + +CREATE TABLE items +( + id INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT, + name VARCHAR(128), + col1 INTEGER NOT NULL, + col2 INTEGER NOT NULL, + CONSTRAINT FK_order_item FOREIGN KEY (col1,col2) + REFERENCES orders (key1,key2) ON DELETE CASCADE ON UPDATE RESTRICT +) TYPE=INNODB; + +INSERT INTO items (name,col1,col2) VALUES ('item 1',1,2); +INSERT INTO items (name,col1,col2) VALUES ('item 2',1,2); +INSERT INTO items (name,col1,col2) VALUES ('item 3',1,3); +INSERT INTO items (name,col1,col2) VALUES ('item 4',2,2); +INSERT INTO items (name,col1,col2) VALUES ('item 5',2,2); + +CREATE TABLE types +( + int_col INT NOT NULL, + int_col2 INTEGER DEFAULT 1, + char_col CHAR(100) NOT NULL, + char_col2 VARCHAR(100) DEFAULT 'something', + char_col3 TEXT, + float_col REAL(4,3) NOT NULL, + float_col2 DOUBLE DEFAULT 1.23, + blob_col BLOB, + numeric_col NUMERIC(5,2) DEFAULT 33.22, + time TIMESTAMP DEFAULT '2002-01-01', + bool_col BOOL NOT NULL, + bool_col2 BOOLEAN DEFAULT 1 +) TYPE=INNODB; \ No newline at end of file diff --git a/tests/framework/db/data/postgres.sql b/tests/framework/db/data/postgres.sql new file mode 100644 index 000000000..e46b2843f --- /dev/null +++ b/tests/framework/db/data/postgres.sql @@ -0,0 +1,165 @@ +/** + * This is the database schema for testing PostgreSQL support of yii Active Record. + * To test this feature, you need to create a database named 'yii' on 'localhost' + * and create an account 'test/test' which owns this test database. + */ +CREATE SCHEMA test; + +CREATE TABLE test.users +( + id SERIAL NOT NULL PRIMARY KEY, + username VARCHAR(128) NOT NULL, + password VARCHAR(128) NOT NULL, + email VARCHAR(128) NOT NULL +); + +INSERT INTO test.users (username, password, email) VALUES ('user1','pass1','email1'); +INSERT INTO test.users (username, password, email) VALUES ('user2','pass2','email2'); +INSERT INTO test.users (username, password, email) VALUES ('user3','pass3','email3'); + +CREATE TABLE test.user_friends +( + id INTEGER NOT NULL, + friend INTEGER NOT NULL, + PRIMARY KEY (id, friend), + CONSTRAINT FK_user_id FOREIGN KEY (id) + REFERENCES test.users (id) ON DELETE CASCADE ON UPDATE RESTRICT, + CONSTRAINT FK_friend_id FOREIGN KEY (friend) + REFERENCES test.users (id) ON DELETE CASCADE ON UPDATE RESTRICT +); + +INSERT INTO test.user_friends VALUES (1,2); +INSERT INTO test.user_friends VALUES (1,3); +INSERT INTO test.user_friends VALUES (2,3); + +CREATE TABLE test.profiles +( + id SERIAL NOT NULL PRIMARY KEY, + first_name VARCHAR(128) NOT NULL, + last_name VARCHAR(128) NOT NULL, + user_id INTEGER NOT NULL, + CONSTRAINT FK_profile_user FOREIGN KEY (user_id) + REFERENCES test.users (id) ON DELETE CASCADE ON UPDATE RESTRICT +); + +INSERT INTO test.profiles (first_name, last_name, user_id) VALUES ('first 1','last 1',1); +INSERT INTO test.profiles (first_name, last_name, user_id) VALUES ('first 2','last 2',2); + +CREATE TABLE test.posts +( + id SERIAL NOT NULL PRIMARY KEY, + title VARCHAR(128) NOT NULL, + create_time TIMESTAMP NOT NULL, + author_id INTEGER NOT NULL, + content TEXT, + CONSTRAINT FK_post_author FOREIGN KEY (author_id) + REFERENCES test.users (id) ON DELETE CASCADE ON UPDATE RESTRICT +); + +INSERT INTO test.posts (title, create_time, author_id, content) VALUES ('post 1',TIMESTAMP '2004-10-19 10:23:54',1,'content 1'); +INSERT INTO test.posts (title, create_time, author_id, content) VALUES ('post 2',TIMESTAMP '2004-10-19 10:23:54',2,'content 2'); +INSERT INTO test.posts (title, create_time, author_id, content) VALUES ('post 3',TIMESTAMP '2004-10-19 10:23:54',2,'content 3'); +INSERT INTO test.posts (title, create_time, author_id, content) VALUES ('post 4',TIMESTAMP '2004-10-19 10:23:54',2,'content 4'); +INSERT INTO test.posts (title, create_time, author_id, content) VALUES ('post 5',TIMESTAMP '2004-10-19 10:23:54',3,'content 5'); + +CREATE TABLE test.comments +( + id SERIAL NOT NULL PRIMARY KEY, + content TEXT NOT NULL, + post_id INTEGER NOT NULL, + author_id INTEGER NOT NULL, + CONSTRAINT FK_post_comment FOREIGN KEY (post_id) + REFERENCES test.posts (id) ON DELETE CASCADE ON UPDATE RESTRICT, + CONSTRAINT FK_user_comment FOREIGN KEY (author_id) + REFERENCES test.users (id) ON DELETE CASCADE ON UPDATE RESTRICT +); + +INSERT INTO test.comments (content, post_id, author_id) VALUES ('comment 1',1, 2); +INSERT INTO test.comments (content, post_id, author_id) VALUES ('comment 2',1, 2); +INSERT INTO test.comments (content, post_id, author_id) VALUES ('comment 3',1, 2); +INSERT INTO test.comments (content, post_id, author_id) VALUES ('comment 4',2, 2); +INSERT INTO test.comments (content, post_id, author_id) VALUES ('comment 5',2, 2); +INSERT INTO test.comments (content, post_id, author_id) VALUES ('comment 6',3, 2); +INSERT INTO test.comments (content, post_id, author_id) VALUES ('comment 7',3, 2); +INSERT INTO test.comments (content, post_id, author_id) VALUES ('comment 8',3, 2); +INSERT INTO test.comments (content, post_id, author_id) VALUES ('comment 9',3, 2); +INSERT INTO test.comments (content, post_id, author_id) VALUES ('comment 10',5, 3); + +CREATE TABLE test.categories +( + id SERIAL NOT NULL PRIMARY KEY, + name VARCHAR(128) NOT NULL, + parent_id INTEGER, + CONSTRAINT FK_category_category FOREIGN KEY (parent_id) + REFERENCES test.categories (id) ON DELETE CASCADE ON UPDATE RESTRICT +); + +INSERT INTO test.categories (name, parent_id) VALUES ('cat 1',NULL); +INSERT INTO test.categories (name, parent_id) VALUES ('cat 2',NULL); +INSERT INTO test.categories (name, parent_id) VALUES ('cat 3',NULL); +INSERT INTO test.categories (name, parent_id) VALUES ('cat 4',1); +INSERT INTO test.categories (name, parent_id) VALUES ('cat 5',1); +INSERT INTO test.categories (name, parent_id) VALUES ('cat 6',5); +INSERT INTO test.categories (name, parent_id) VALUES ('cat 7',5); + +CREATE TABLE test.post_category +( + category_id INTEGER NOT NULL, + post_id INTEGER NOT NULL, + PRIMARY KEY (category_id, post_id), + CONSTRAINT FK_post_category_post FOREIGN KEY (post_id) + REFERENCES test.posts (id) ON DELETE CASCADE ON UPDATE RESTRICT, + CONSTRAINT FK_post_category_category FOREIGN KEY (category_id) + REFERENCES test.categories (id) ON DELETE CASCADE ON UPDATE RESTRICT +); + +INSERT INTO test.post_category (category_id, post_id) VALUES (1,1); +INSERT INTO test.post_category (category_id, post_id) VALUES (2,1); +INSERT INTO test.post_category (category_id, post_id) VALUES (3,1); +INSERT INTO test.post_category (category_id, post_id) VALUES (4,2); +INSERT INTO test.post_category (category_id, post_id) VALUES (1,2); +INSERT INTO test.post_category (category_id, post_id) VALUES (1,3); + +CREATE TABLE test.orders +( + key1 INTEGER NOT NULL, + key2 INTEGER NOT NULL, + name VARCHAR(128), + PRIMARY KEY (key1, key2) +); + +INSERT INTO test.orders (key1,key2,name) VALUES (1,2,'order 12'); +INSERT INTO test.orders (key1,key2,name) VALUES (1,3,'order 13'); +INSERT INTO test.orders (key1,key2,name) VALUES (2,1,'order 21'); +INSERT INTO test.orders (key1,key2,name) VALUES (2,2,'order 22'); + +CREATE TABLE test.items +( + id SERIAL NOT NULL PRIMARY KEY, + name VARCHAR(128), + col1 INTEGER NOT NULL, + col2 INTEGER NOT NULL, + CONSTRAINT FK_order_item FOREIGN KEY (col1,col2) + REFERENCES test.orders (key1,key2) ON DELETE CASCADE ON UPDATE RESTRICT +); + +INSERT INTO test.items (name,col1,col2) VALUES ('item 1',1,2); +INSERT INTO test.items (name,col1,col2) VALUES ('item 2',1,2); +INSERT INTO test.items (name,col1,col2) VALUES ('item 3',1,3); +INSERT INTO test.items (name,col1,col2) VALUES ('item 4',2,2); +INSERT INTO test.items (name,col1,col2) VALUES ('item 5',2,2); + +CREATE TABLE public.yii_types +( + int_col INT NOT NULL, + int_col2 INTEGER DEFAULT 1, + char_col CHAR(100) NOT NULL, + char_col2 VARCHAR(100) DEFAULT 'something', + char_col3 TEXT, + numeric_col NUMERIC(4,3) NOT NULL, + real_col REAL DEFAULT 1.23, + blob_col BYTEA, + time TIMESTAMP, + bool_col BOOL NOT NULL, + bool_col2 BOOLEAN DEFAULT TRUE +); \ No newline at end of file diff --git a/tests/framework/db/data/sqlite.sql b/tests/framework/db/data/sqlite.sql new file mode 100644 index 000000000..4df58b0c1 --- /dev/null +++ b/tests/framework/db/data/sqlite.sql @@ -0,0 +1,215 @@ + +CREATE TABLE users +( + id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, + username VARCHAR(128) NOT NULL, + password VARCHAR(128) NOT NULL, + email VARCHAR(128) NOT NULL +); + +INSERT INTO users (username, password, email) VALUES ('user1','pass1','email1'); +INSERT INTO users (username, password, email) VALUES ('user2','pass2','email2'); +INSERT INTO users (username, password, email) VALUES ('user3','pass3','email3'); +INSERT INTO users (username, password, email) VALUES ('user4','pass4','email4'); + +CREATE TABLE profiles +( + id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, + first_name VARCHAR(128) NOT NULL, + last_name VARCHAR(128) NOT NULL, + user_id INTEGER NOT NULL, + CONSTRAINT FK_profile_user FOREIGN KEY (user_id) + REFERENCES users (id) ON DELETE CASCADE ON UPDATE RESTRICT +); + +INSERT INTO profiles (first_name, last_name, user_id) VALUES ('first 1','last 1',1); +INSERT INTO profiles (first_name, last_name, user_id) VALUES ('first 2','last 2',2); + +CREATE TABLE posts +( + id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, + title VARCHAR(128) NOT NULL, + create_time TIMESTAMP NOT NULL, + author_id INTEGER NOT NULL, + content TEXT, + CONSTRAINT FK_post_author FOREIGN KEY (author_id) + REFERENCES users (id) ON DELETE CASCADE ON UPDATE RESTRICT +); + +INSERT INTO posts (title, create_time, author_id, content) VALUES ('post 1',100000,1,'content 1'); +INSERT INTO posts (title, create_time, author_id, content) VALUES ('post 2',100001,2,'content 2'); +INSERT INTO posts (title, create_time, author_id, content) VALUES ('post 3',100002,2,'content 3'); +INSERT INTO posts (title, create_time, author_id, content) VALUES ('post 4',100003,2,'content 4'); +INSERT INTO posts (title, create_time, author_id, content) VALUES ('post 5',100004,3,'content 5'); + + +CREATE TABLE posts_nofk +( + id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, + title VARCHAR(128) NOT NULL, + create_time TIMESTAMP NOT NULL, + author_id INTEGER NOT NULL, + content TEXT +); + +INSERT INTO posts_nofk (title, create_time, author_id, content) VALUES ('post 1',100000,1,'content 1'); +INSERT INTO posts_nofk (title, create_time, author_id, content) VALUES ('post 2',100001,2,'content 2'); +INSERT INTO posts_nofk (title, create_time, author_id, content) VALUES ('post 3',100002,2,'content 3'); +INSERT INTO posts_nofk (title, create_time, author_id, content) VALUES ('post 4',100003,2,'content 4'); +INSERT INTO posts_nofk (title, create_time, author_id, content) VALUES ('post 5',100004,3,'content 5'); + + +CREATE TABLE comments +( + id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, + content TEXT NOT NULL, + post_id INTEGER NOT NULL, + author_id INTEGER NOT NULL, + CONSTRAINT FK_post_comment FOREIGN KEY (post_id) + REFERENCES posts (id) ON DELETE CASCADE ON UPDATE RESTRICT, + CONSTRAINT FK_user_comment FOREIGN KEY (author_id) + REFERENCES users (id) ON DELETE CASCADE ON UPDATE RESTRICT +); + +INSERT INTO comments (content, post_id, author_id) VALUES ('comment 1',1, 2); +INSERT INTO comments (content, post_id, author_id) VALUES ('comment 2',1, 2); +INSERT INTO comments (content, post_id, author_id) VALUES ('comment 3',1, 2); +INSERT INTO comments (content, post_id, author_id) VALUES ('comment 4',2, 2); +INSERT INTO comments (content, post_id, author_id) VALUES ('comment 5',2, 2); +INSERT INTO comments (content, post_id, author_id) VALUES ('comment 6',3, 2); +INSERT INTO comments (content, post_id, author_id) VALUES ('comment 7',3, 2); +INSERT INTO comments (content, post_id, author_id) VALUES ('comment 8',3, 2); +INSERT INTO comments (content, post_id, author_id) VALUES ('comment 9',3, 2); +INSERT INTO comments (content, post_id, author_id) VALUES ('comment 10',5, 3); + +CREATE TABLE categories +( + id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, + name VARCHAR(128) NOT NULL, + parent_id INTEGER, + CONSTRAINT FK_category_category FOREIGN KEY (parent_id) + REFERENCES categories (id) ON DELETE CASCADE ON UPDATE RESTRICT +); + +INSERT INTO categories (name, parent_id) VALUES ('cat 1',NULL); +INSERT INTO categories (name, parent_id) VALUES ('cat 2',NULL); +INSERT INTO categories (name, parent_id) VALUES ('cat 3',NULL); +INSERT INTO categories (name, parent_id) VALUES ('cat 4',1); +INSERT INTO categories (name, parent_id) VALUES ('cat 5',1); +INSERT INTO categories (name, parent_id) VALUES ('cat 6',5); +INSERT INTO categories (name, parent_id) VALUES ('cat 7',5); + +CREATE TABLE post_category +( + category_id INTEGER NOT NULL, + post_id INTEGER NOT NULL, + PRIMARY KEY (category_id, post_id), + CONSTRAINT FK_post_category_post FOREIGN KEY (post_id) + REFERENCES posts (id) ON DELETE CASCADE ON UPDATE RESTRICT, + CONSTRAINT FK_post_category_category FOREIGN KEY (category_id) + REFERENCES categories (id) ON DELETE CASCADE ON UPDATE RESTRICT +); + +INSERT INTO post_category (category_id, post_id) VALUES (1,1); +INSERT INTO post_category (category_id, post_id) VALUES (2,1); +INSERT INTO post_category (category_id, post_id) VALUES (3,1); +INSERT INTO post_category (category_id, post_id) VALUES (4,2); +INSERT INTO post_category (category_id, post_id) VALUES (1,2); +INSERT INTO post_category (category_id, post_id) VALUES (1,3); + +CREATE TABLE orders +( + key1 INTEGER NOT NULL, + key2 INTEGER NOT NULL, + name VARCHAR(128), + PRIMARY KEY (key1, key2) +); + +INSERT INTO orders (key1,key2,name) VALUES (1,2,'order 12'); +INSERT INTO orders (key1,key2,name) VALUES (1,3,'order 13'); +INSERT INTO orders (key1,key2,name) VALUES (2,1,'order 21'); +INSERT INTO orders (key1,key2,name) VALUES (2,2,'order 22'); + +CREATE TABLE items +( + id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, + name VARCHAR(128), + col1 INTEGER NOT NULL, + col2 INTEGER NOT NULL, + CONSTRAINT FK_order_item FOREIGN KEY (col1,col2) + REFERENCES orders (key1,key2) ON DELETE CASCADE ON UPDATE RESTRICT +); + +INSERT INTO items (name,col1,col2) VALUES ('item 1',1,2); +INSERT INTO items (name,col1,col2) VALUES ('item 2',1,2); +INSERT INTO items (name,col1,col2) VALUES ('item 3',1,3); +INSERT INTO items (name,col1,col2) VALUES ('item 4',2,2); +INSERT INTO items (name,col1,col2) VALUES ('item 5',2,2); + +CREATE TABLE types +( + int_col INT NOT NULL, + int_col2 INTEGER DEFAULT 1, + char_col CHAR(100) NOT NULL, + char_col2 VARCHAR(100) DEFAULT 'something', + char_col3 TEXT, + float_col REAL(4,3) NOT NULL, + float_col2 DOUBLE DEFAULT 1.23, + blob_col BLOB, + numeric_col NUMERIC(5,2) DEFAULT 33.22, + time TIMESTAMP DEFAULT 123, + bool_col BOOL NOT NULL, + bool_col2 BOOLEAN DEFAULT 1, + null_col INTEGER DEFAULT NULL +); + +CREATE TABLE Content +( + id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, + class VARCHAR(128), + parentID INTEGER NOT NULL, + ownerID INTEGER NOT NULL, + title VARCHAR(100), + CONSTRAINT FK_content_user FOREIGN KEY (ownerID) + REFERENCES users (id) ON DELETE CASCADE ON UPDATE RESTRICT + CONSTRAINT FK_content_parent FOREIGN KEY (parentID) + REFERENCES Content (id) ON DELETE CASCADE ON UPDATE RESTRICT +); + +INSERT INTO Content (class,parentID,ownerID,title) VALUES ('Article',-1,1,'article 1'); +INSERT INTO Content (class,parentID,ownerID,title) VALUES ('Article',-1,2,'article 2'); +INSERT INTO Content (class,parentID,ownerID,title) VALUES ('Comment',1,1,'comment 1'); +INSERT INTO Content (class,parentID,ownerID,title) VALUES ('Article',-1,2,'article 3'); +INSERT INTO Content (class,parentID,ownerID,title) VALUES ('Comment',4,2,'comment 2'); +INSERT INTO Content (class,parentID,ownerID,title) VALUES ('Comment',4,1,'comment 3'); + +CREATE TABLE Article +( + id INTEGER NOT NULL PRIMARY KEY, + authorID INTEGER NOT NULL, + body TEXT, + CONSTRAINT FK_article_content FOREIGN KEY (id) + REFERENCES Content (id) ON DELETE CASCADE ON UPDATE RESTRICT + CONSTRAINT FK_article_author FOREIGN KEY (authorID) + REFERENCES users (id) ON DELETE CASCADE ON UPDATE RESTRICT +); + +INSERT INTO Article (id,authorID,body) VALUES (1,1,'content for article 1'); +INSERT INTO Article (id,authorID,body) VALUES (2,2,'content for article 2'); +INSERT INTO Article (id,authorID,body) VALUES (4,1,'content for article 3'); + +CREATE TABLE Comment +( + id INTEGER NOT NULL PRIMARY KEY, + authorID INTEGER NOT NULL, + body TEXT, + CONSTRAINT FK_comment_content FOREIGN KEY (id) + REFERENCES Content (id) ON DELETE CASCADE ON UPDATE RESTRICT + CONSTRAINT FK_article_author FOREIGN KEY (authorID) + REFERENCES users (id) ON DELETE CASCADE ON UPDATE RESTRICT +); + +INSERT INTO Comment (id,authorID,body) VALUES (3,1,'content for comment 1'); +INSERT INTO Comment (id,authorID,body) VALUES (5,1,'content for comment 2'); +INSERT INTO Comment (id,authorID,body) VALUES (6,1,'content for comment 3'); + diff --git a/tests/framework/db/schema/CDbCriteriaTest.php b/tests/framework/db/schema/CDbCriteriaTest.php new file mode 100644 index 000000000..af6670182 --- /dev/null +++ b/tests/framework/db/schema/CDbCriteriaTest.php @@ -0,0 +1,507 @@ +addCondition('A'); + $this->assertEquals('A', $criteria->condition); + + //adding multiple conditions + $criteria = new CDbCriteria(); + $criteria->addCondition('A'); + $criteria->addCondition('B'); + $criteria->addCondition('C', 'OR'); + $this->assertEquals('((A) AND (B)) OR (C)', $criteria->condition); + + //adding empty array as condition + $criteria = new CDbCriteria(); + $criteria->addCondition('A'); + $criteria->addCondition(array()); + $this->assertEquals('A', $criteria->condition); + + //adding array as condition + $criteria = new CDbCriteria(); + $criteria->addCondition(array('A', 'B')); + $this->assertEquals('(A) AND (B)', $criteria->condition); + } + + /** + * @depends testAddCondition + * @covers CDbCriteria::addInCondition + */ + function testAddInCondition() { + CDbCriteria::$paramCount=0; + $criteria = new CDbCriteria(); + + $criteria->addInCondition('A', array()); + $this->assertEquals('0=1', $criteria->condition); + $this->assertTrue(empty($criteria->params)); + + // IN with one parameter should transform to = + $criteria = new CDbCriteria(); + + $criteria->addInCondition('A', array(1)); + $this->assertEquals('A=:ycp0', $criteria->condition); + $this->assertEquals(1, $criteria->params[':ycp0']); + + // IN with null should transform to IS NULL + $criteria = new CDbCriteria(); + + $criteria->addInCondition('A', array(null)); + $this->assertEquals('A IS NULL', $criteria->condition); + $this->assertTrue(empty($criteria->params)); + + // IN with many parameters + $criteria = new CDbCriteria(); + + $criteria->addInCondition('B', array(1, 2, '3')); + $this->assertEquals('B IN (:ycp1, :ycp2, :ycp3)', $criteria->condition); + $this->assertEquals(1, $criteria->params[':ycp1']); + $this->assertEquals(2, $criteria->params[':ycp2']); + $this->assertEquals('3', $criteria->params[':ycp3']); + } + + /** + * @depends testAddCondition + * @covers CDbCriteria::addNotInCondition + */ + function testAddNotInCondition() { + // NOT IN with empty array should not change anything + CDbCriteria::$paramCount=0; + $criteria = new CDbCriteria(); + + $criteria->addNotInCondition('A', array()); + $this->assertEquals('', $criteria->condition); + $this->assertTrue(empty($criteria->params)); + + // NOT IN with one parameter should transform to != + $criteria = new CDbCriteria(); + + $criteria->addNotInCondition('A', array(1)); + $this->assertEquals('A!=:ycp0', $criteria->condition); + $this->assertEquals(1, $criteria->params[':ycp0']); + + // NOT IN with null should transform to IS NOT NULL + $criteria = new CDbCriteria(); + + $criteria->addNotInCondition('A', array(null)); + $this->assertEquals('A IS NOT NULL', $criteria->condition); + $this->assertTrue(empty($criteria->params)); + + // NOT IN with many parameters + $criteria = new CDbCriteria(); + + $criteria->addNotInCondition('B', array(1, 2, '3')); + $this->assertEquals('B NOT IN (:ycp1, :ycp2, :ycp3)', $criteria->condition); + $this->assertEquals(1, $criteria->params[':ycp1']); + $this->assertEquals(2, $criteria->params[':ycp2']); + $this->assertEquals('3', $criteria->params[':ycp3']); + } + + /** + * @depends testAddCondition + * @covers CDbCriteria::addSearchCondition + */ + function testAddSearchCondition() { + // string escaping + CDbCriteria::$paramCount=0; + $criteria = new CDbCriteria(); + $criteria->addSearchCondition('A', 'key_word%'); + + $this->assertEquals('A LIKE :ycp0', $criteria->condition); + $this->assertEquals('%key\_word\%%', $criteria->params[':ycp0']); + + // no escaping + $criteria = new CDbCriteria(); + $criteria->addSearchCondition('A', 'key_word%', false); + + $this->assertEquals('A LIKE :ycp1', $criteria->condition); + $this->assertEquals('key_word%', $criteria->params[':ycp1']); + } + + /** + * @depends testAddCondition + * @covers CDbCriteria::addColumnCondition + */ + function testAddColumnCondition() { + CDbCriteria::$paramCount=0; + $criteria = new CDbCriteria(); + $criteria->addColumnCondition(array('A' => 1, 'B' => null, 'C' => '2')); + + $this->assertEquals('A=:ycp0 AND B IS NULL AND C=:ycp1', $criteria->condition); + $this->assertEquals(1, $criteria->params[':ycp0']); + $this->assertEquals('2', $criteria->params[':ycp1']); + } + + /** + * @depends testAddCondition + * @covers CDbCriteria::compare + */ + function testCompare(){ + CDbCriteria::$paramCount=0; + $criteria = new CDbCriteria(); + $criteria->compare('A', ''); + $this->assertEquals('', $criteria->condition); + + $criteria = new CDbCriteria(); + $criteria->compare('A', 1); + $this->assertEquals('A=:ycp0', $criteria->condition); + $this->assertEquals(1, $criteria->params[':ycp0']); + + $criteria = new CDbCriteria(); + $criteria->compare('A', '>1'); + $this->assertEquals('A>:ycp1', $criteria->condition); + $this->assertEquals(1, $criteria->params[':ycp1']); + + $criteria = new CDbCriteria(); + $criteria->compare('A', '<1'); + $this->assertEquals('A<:ycp2', $criteria->condition); + $this->assertEquals(1, $criteria->params[':ycp2']); + + $criteria = new CDbCriteria(); + $criteria->compare('A', '<=1'); + $this->assertEquals('A<=:ycp3', $criteria->condition); + $this->assertEquals(1, $criteria->params[':ycp3']); + + $criteria = new CDbCriteria(); + $criteria->compare('A', '>=1'); + $this->assertEquals('A>=:ycp4', $criteria->condition); + $this->assertEquals(1, $criteria->params[':ycp4']); + + $criteria = new CDbCriteria(); + $criteria->compare('A', '<>1'); + $this->assertEquals('A<>:ycp5', $criteria->condition); + $this->assertEquals(1, $criteria->params[':ycp5']); + + $criteria = new CDbCriteria(); + $criteria->compare('A', '=1'); + $this->assertEquals('A=:ycp6', $criteria->condition); + $this->assertEquals(1, $criteria->params[':ycp6']); + + $criteria = new CDbCriteria(); + $criteria->compare('A', '1', true); + $this->assertEquals('A LIKE :ycp7', $criteria->condition); + $this->assertEquals('%1%', $criteria->params[':ycp7']); + + $criteria = new CDbCriteria(); + $criteria->compare('A', '=1', true); + $this->assertEquals('A=:ycp8', $criteria->condition); + $this->assertEquals('1', $criteria->params[':ycp8']); + + $criteria = new CDbCriteria(); + $criteria->compare('A', '<>1', true); + $this->assertEquals('A NOT LIKE :ycp9', $criteria->condition); + $this->assertEquals('%1%', $criteria->params[':ycp9']); + + $criteria = new CDbCriteria(); + $criteria->compare('A', ' value_with_spaces '); + $this->assertEquals('A=:ycp10', $criteria->condition); + $this->assertEquals(' value_with_spaces ', $criteria->params[':ycp10']); + + $criteria = new CDbCriteria(); + $criteria->compare('A', array()); + $this->assertEquals('', $criteria->condition); + + $criteria = new CDbCriteria(); + $criteria->compare('A', array(1, '2')); + $this->assertEquals('A IN (:ycp11, :ycp12)', $criteria->condition); + $this->assertEquals(1, $criteria->params[':ycp11']); + $this->assertEquals('2', $criteria->params[':ycp12']); + } + + /** + * @depends testCompare + * @covers CDbCriteria::mergeWith + */ + function testMergeWith() { + // merging select + + // * should be replaced + CDbCriteria::$paramCount=0; + $criteria1 = new CDbCriteria; + $criteria1->select = '*'; + + $criteria2 = new CDbCriteria; + $criteria2->select = 'a'; + + $criteria1->mergeWith($criteria2); + + $this->assertEquals('a', $criteria1->select); + + // equal selects should be left as is + $criteria1 = new CDbCriteria; + $criteria1->select = 'a'; + + $criteria2 = new CDbCriteria; + $criteria2->select = 'a'; + + $criteria1->mergeWith($criteria2); + + $this->assertEquals('a', $criteria1->select); + + // not equal selects are being merged + $criteria1 = new CDbCriteria; + $criteria1->select = 'a, b, c, d'; + + $criteria2 = new CDbCriteria; + $criteria2->select = 'a, c, e, f'; + + $criteria1->mergeWith($criteria2); + + $this->assertEquals(array('a', 'b', 'c', 'd', 'e', 'f'), $criteria1->select); + + // conditions + + // equal conditions are not merged + $criteria1 = new CDbCriteria; + $criteria1->condition = 'a'; + + $criteria2 = new CDbCriteria; + $criteria2->condition = 'a'; + + $criteria1->mergeWith($criteria2); + + $this->assertEquals('a', $criteria1->condition); + + // empty condition is being replaced + $criteria1 = new CDbCriteria; + $criteria1->condition = ''; + + $criteria2 = new CDbCriteria; + $criteria2->condition = 'a'; + + $criteria1->mergeWith($criteria2); + + $this->assertEquals('a', $criteria1->condition); + + // not empty conditions are merged + $criteria1 = new CDbCriteria; + $criteria1->condition = 'a'; + + $criteria2 = new CDbCriteria; + $criteria2->condition = 'b'; + + $criteria1->mergeWith($criteria2); + + $this->assertEquals('(a) AND (b)', $criteria1->condition); + + // limit, offset, distinct and alias are being replaced + $criteria1 = new CDbCriteria; + $criteria1->limit = 10; + $criteria1->offset = 5; + $criteria1->alias = 'alias1'; + $criteria1->distinct = true; + + $criteria2 = new CDbCriteria; + $criteria2->limit = 20; + $criteria2->offset = 6; + $criteria2->alias = 'alias2'; + $criteria1->distinct = false; + + $criteria1->mergeWith($criteria2); + + $this->assertEquals(20, $criteria1->limit); + $this->assertEquals(6, $criteria1->offset); + $this->assertEquals('alias2', $criteria1->alias); + $this->assertEquals(false, $criteria1->distinct); + + + // empty order, group, join, having are being replaced + $criteria1 = new CDbCriteria; + $criteria1->order = ''; + $criteria1->group = ''; + $criteria1->join = ''; + $criteria1->having = ''; + + $criteria2 = new CDbCriteria; + $criteria2->order = 'a'; + $criteria1->group = 'a'; + $criteria1->join = 'a'; + $criteria2->having = 'a'; + + $criteria1->mergeWith($criteria2); + + $this->assertEquals('a', $criteria1->order); + $this->assertEquals('a', $criteria1->group); + $this->assertEquals('a', $criteria1->join); + $this->assertEquals('a', $criteria1->having); + + // merging with empty order, group, join ignored + $criteria1 = new CDbCriteria; + $criteria1->order = 'a'; + $criteria1->group = 'a'; + $criteria1->join = 'a'; + $criteria1->having = 'a'; + + $criteria2 = new CDbCriteria; + $criteria2->order = ''; + $criteria2->group = ''; + $criteria2->join = ''; + $criteria2->having = ''; + + $criteria1->mergeWith($criteria2); + + $this->assertEquals('a', $criteria1->order); + $this->assertEquals('a', $criteria1->group); + $this->assertEquals('a', $criteria1->join); + $this->assertEquals('a', $criteria1->having); + + // not empty order, group, join are being merged + $criteria1 = new CDbCriteria; + $criteria1->order = 'a'; + $criteria1->group = 'a'; + $criteria1->join = 'a'; + $criteria1->having = 'a'; + + $criteria2 = new CDbCriteria; + $criteria2->order = 'b'; + $criteria2->group = 'b'; + $criteria2->join = 'b'; + $criteria2->having = 'b'; + + $criteria1->mergeWith($criteria2); + + $this->assertEquals('b, a', $criteria1->order); + $this->assertEquals('a, b', $criteria1->group); + $this->assertEquals('a b', $criteria1->join); + $this->assertEquals('(a) AND (b)', $criteria1->having); + + // empty with is replaced + $criteria1 = new CDbCriteria; + $criteria1->with = ''; + + $criteria2 = new CDbCriteria; + $criteria2->with = 'a'; + + $criteria1->mergeWith($criteria2); + + $this->assertEquals('a', $criteria1->with); + + // not empty with are merged + $criteria1 = new CDbCriteria; + $criteria1->with = 'a'; + + $criteria2 = new CDbCriteria; + $criteria2->with = 'b'; + + $criteria1->mergeWith($criteria2); + + $this->assertEquals(array('a', 'b'), $criteria1->with); + + // not empty with are merged (more complex test) + $criteria1 = new CDbCriteria; + $criteria1->with = array('a', 'b'); + + $criteria2 = new CDbCriteria; + $criteria2->with = array('a', 'c'); + + $criteria1->mergeWith($criteria2); + + $this->assertEquals(array('a', 'b', 'a', 'c'), $criteria1->with); + + // merging two criteria with parameters + $criteria1 = new CDbCriteria; + $criteria1->compare('A1', 1); + $criteria1->compare('A2', 2); + $criteria1->compare('A3', 3); + $criteria1->compare('A4', 4); + $criteria1->compare('A5', 5); + $criteria1->compare('A6', 6); + + $criteria2 = new CDbCriteria; + $criteria2->compare('B1', 7); + $criteria2->compare('B2', 8); + $criteria2->compare('B3', 9); + $criteria2->compare('B4', 10); + $criteria2->compare('B5', 11); + $criteria2->compare('B6', 12); + + $criteria1->mergeWith($criteria2); + + $this->assertEquals('((((((A1=:ycp0) AND (A2=:ycp1)) AND (A3=:ycp2)) AND (A4=:ycp3)) AND (A5=:ycp4)) AND (A6=:ycp5)) AND ((((((B1=:ycp6) AND (B2=:ycp7)) AND (B3=:ycp8)) AND (B4=:ycp9)) AND (B5=:ycp10)) AND (B6=:ycp11))', $criteria1->condition); + $this->assertEquals(1, $criteria1->params[':ycp0']); + $this->assertEquals(2, $criteria1->params[':ycp1']); + $this->assertEquals(3, $criteria1->params[':ycp2']); + $this->assertEquals(4, $criteria1->params[':ycp3']); + $this->assertEquals(5, $criteria1->params[':ycp4']); + $this->assertEquals(6, $criteria1->params[':ycp5']); + $this->assertEquals(7, $criteria1->params[':ycp6']); + $this->assertEquals(8, $criteria1->params[':ycp7']); + $this->assertEquals(9, $criteria1->params[':ycp8']); + $this->assertEquals(10, $criteria1->params[':ycp9']); + $this->assertEquals(11, $criteria1->params[':ycp10']); + $this->assertEquals(12, $criteria1->params[':ycp11']); + } + + /** + * Merging criterias with positioned and non positioned parameters. + * + * @depends testCompare + * @covers CDbCriteria::mergeWith + */ + function testMergeWithPositionalPlaceholders(){ + CDbCriteria::$paramCount=0; + $criteria1 = new CDbCriteria(); + $criteria1->condition = 'A=? AND B=?'; + $criteria1->params = array(0 => 10, 1 => 20); + + $criteria2 = new CDbCriteria(); + $criteria2->compare('C', 30); + $criteria2->compare('D', 40); + + $criteria2->mergeWith($criteria1); + + $this->assertEquals('((C=:ycp0) AND (D=:ycp1)) AND (A=? AND B=?)', $criteria2->condition); + + $this->assertEquals(10, $criteria2->params[0]); + $this->assertEquals(20, $criteria2->params[1]); + $this->assertEquals(30, $criteria2->params[':ycp0']); + $this->assertEquals(40, $criteria2->params[':ycp1']); + + // and vice versa + + $criteria1 = new CDbCriteria(); + $criteria1->condition = 'A=? AND B=?'; + $criteria1->params = array(0 => 10, 1 => 20); + + $criteria2 = new CDbCriteria(); + $criteria2->compare('C', 30); + $criteria2->compare('D', 40); + + $criteria1->mergeWith($criteria2); + + $this->assertEquals('(A=? AND B=?) AND ((C=:ycp2) AND (D=:ycp3))', $criteria1->condition); + $this->assertEquals(10, $criteria1->params[0]); + $this->assertEquals(20, $criteria1->params[1]); + $this->assertEquals(30, $criteria1->params[':ycp2']); + $this->assertEquals(40, $criteria1->params[':ycp3']); + } + + /** + * @covers CDbCriteria::addBetweenCondition + */ + function testAddBetweenCondition(){ + CDbCriteria::$paramCount=0; + $criteria = new CDbCriteria(); + + $criteria->addBetweenCondition('A', 1, 2); + $this->assertEquals('A BETWEEN :ycp0 AND :ycp1', $criteria->condition); + $this->assertEquals(1, $criteria->params[':ycp0']); + $this->assertEquals(2, $criteria->params[':ycp1']); + } + + function testToArray(){ + $keys = array('select', 'condition', 'params', 'limit', 'offset', 'order', 'group', 'join', 'having', 'distinct', 'with', 'alias', 'index', 'together'); + $criteria = new CDbCriteria(); + $this->assertEquals($keys, array_keys($criteria->toArray())); + } +} diff --git a/tests/framework/db/schema/CMssqlTest.php b/tests/framework/db/schema/CMssqlTest.php new file mode 100644 index 000000000..e43f25a4a --- /dev/null +++ b/tests/framework/db/schema/CMssqlTest.php @@ -0,0 +1,310 @@ +markTestSkipped('PDO and MSSQL extensions are required.'); + $dsn=self::DB_DSN_PREFIX.':host='.self::DB_HOST.';dbname='.self::DB_NAME; + $this->db=new CDbConnection($dsn,self::DB_USER,self::DB_PASS);; + try + { + $this->db->active=true; + } + catch(Exception $e) + { + $schemaFile=realpath(dirname(__FILE__).'/../data/mssql.sql'); + $this->markTestSkipped("Please read $schemaFile for details on setting up the test environment for MySQL test case."); + } + + $tables=array('comments','post_category','posts','categories','profiles','users','items','orders','types'); + foreach($tables as $table) + { + $sql=<<db->createCommand($sql)->execute(); + } + + $sqls=file_get_contents(dirname(__FILE__).'/../data/mssql.sql'); + foreach(explode('GO',$sqls) as $sql) + { + if(trim($sql)!=='') + $this->db->createCommand($sql)->execute(); + } + + } + + public function tearDown() + { + $this->db->active=false; + } + + public function testSchema() + { + $schema=$this->db->schema; + $this->assertTrue($schema instanceof CDbSchema); + $this->assertEquals($schema->dbConnection,$this->db); + $this->assertTrue($schema->commandBuilder instanceof CDbCommandBuilder); + $this->assertEquals('[posts]',$schema->quoteTableName('posts')); + $this->assertEquals('[dbo].[posts]',$schema->quoteTableName('dbo.posts')); + $this->assertEquals('[id]',$schema->quoteColumnName('id')); + $this->assertTrue($schema->getTable('posts') instanceof CDbTableSchema); + $this->assertNull($schema->getTable('foo')); + } + + public function testTable() + { + $table=$this->db->schema->getTable('posts'); + $this->assertTrue($table instanceof CDbTableSchema); + $this->assertEquals('posts',$table->name); + $this->assertEquals('dbo',$table->schemaName); + $this->assertEquals('[dbo].[posts]',$table->rawName); + $this->assertEquals('id',$table->primaryKey); + $this->assertEquals(array('author_id'=>array('users','id')),$table->foreignKeys); + $this->assertEquals('',$table->sequenceName); + $this->assertEquals(5,count($table->columns)); + + $this->assertTrue($table->getColumn('id') instanceof CDbColumnSchema); + $this->assertTrue($table->getColumn('foo')===null); + $this->assertEquals(array('id','title','create_time','author_id','content'),$table->columnNames); + + $table=$this->db->schema->getTable('orders'); + $this->assertEquals(array('key1','key2'),$table->primaryKey); + + $table=$this->db->schema->getTable('items'); + $this->assertEquals('id',$table->primaryKey); + $this->assertEquals(array('col1'=>array('orders','key1'),'col2'=>array('orders','key2')),$table->foreignKeys); + + $table=$this->db->schema->getTable('types'); + $this->assertTrue($table instanceof CDbTableSchema); + $this->assertEquals('types',$table->name); + $this->assertEquals('[dbo].[types]',$table->rawName); + $this->assertTrue($table->primaryKey===null); + $this->assertTrue($table->foreignKeys===array()); + $this->assertTrue($table->sequenceName===null); + + $table=$this->db->schema->getTable('invalid'); + $this->assertNull($table); + } + + public function testColumn() + { + $values=array + ( + 'name'=>array('id', 'title', 'create_time', 'author_id', 'content'), + 'rawName'=>array('[id]', '[title]', '[create_time]', '[author_id]', '[content]'), + 'defaultValue'=>array(null, null, null, null, null), + 'size'=>array(10, 128, null, 10, null), + 'precision'=>array(10, 128, null, 10, null), + 'scale'=>array(0, null, null, 0, null), + 'dbType'=>array('int','varchar','datetime','int','text'), + 'type'=>array('integer','string','string','integer','string'), + 'isPrimaryKey'=>array(true,false,false,false,false), + 'isForeignKey'=>array(false,false,false,true,false), + ); + $this->checkColumns('posts',$values); + $values=array + ( + 'name'=>array('int_col', 'int_col2', 'char_col', 'char_col2', 'char_col3', 'float_col', 'float_col2', 'blob_col', 'numeric_col', 'time', 'bool_col', 'bool_col2'), + 'rawName'=>array('[int_col]', '[int_col2]', '[char_col]', '[char_col2]', '[char_col3]', '[float_col]', '[float_col2]', '[blob_col]', '[numeric_col]', '[time]', '[bool_col]', '[bool_col2]'), + 'defaultValue'=>array(null, 1, null, "something", null, null, '1.23', null, '33.22', '2002-01-01 00:00:00', null, true), + 'size'=>array(10, 10, 100, 100, null, 24, 53, null, 5, null, null, null), + 'precision'=>array(10, 10, 100, 100, null, 24, 53, null, 5, null, null, null), + 'scale'=>array(0, 0, null, null, null, null, null, null, 2, null, null, null), + 'dbType'=>array('int','int','char','varchar','text','real','float','image','numeric','datetime','bit','bit'), + 'type'=>array('integer','integer','string','string','string','double','double','string','string','string','boolean','boolean'), + 'isPrimaryKey'=>array(false,false,false,false,false,false,false,false,false,false,false,false), + 'isForeignKey'=>array(false,false,false,false,false,false,false,false,false,false,false,false), + ); + $this->checkColumns('types',$values); + } + + protected function checkColumns($tableName,$values) + { + $table=$this->db->schema->getTable($tableName); + foreach($values as $name=>$value) + { + foreach(array_values($table->columns) as $i=>$column) + { + $type1=gettype($column->$name); + $type2=gettype($value[$i]); + $this->assertTrue($column->$name===$value[$i], "$tableName.{$column->name}.$name is {$column->$name} ($type1), different from the expected {$value[$i]} ($type2)."); + } + } + } + + public function testCommandBuilder() + { + $schema=$this->db->schema; + $builder=$schema->commandBuilder; + $this->assertTrue($builder instanceof CDbCommandBuilder); + $table=$schema->getTable('posts'); + + $c=$builder->createInsertCommand($table,array('title'=>'test post','create_time'=>'2000-01-01','author_id'=>1,'content'=>'test content')); + $this->assertEquals('INSERT INTO [dbo].[posts] ([title], [create_time], [author_id], [content]) VALUES (:title, :create_time, :author_id, :content)',$c->text); + $c->execute(); + $this->assertEquals(6,$builder->getLastInsertId($table)); + $this->assertEquals(6, $this->db->getLastInsertID()); + + $c=$builder->createCountCommand($table,new CDbCriteria); + $this->assertEquals('SELECT COUNT(*) FROM [dbo].[posts] [t]',$c->text); + $this->assertEquals(6,$c->queryScalar()); + + $c=$builder->createDeleteCommand($table,new CDbCriteria(array( + 'condition'=>'id=:id', + 'params'=>array('id'=>6)))); + $this->assertEquals('DELETE FROM [dbo].[posts] WHERE id=:id',$c->text); + $c->execute(); + $c=$builder->createCountCommand($table,new CDbCriteria); + $this->assertEquals(5,$c->queryScalar()); + + $c=$builder->createFindCommand($table,new CDbCriteria(array( + 'select'=>'id, title', + 'condition'=>'id=:id', + 'params'=>array('id'=>5), + 'order'=>'title', + 'limit'=>2, + 'offset'=>0))); + $this->assertEquals('SELECT TOP 2 id, title FROM [dbo].[posts] [t] WHERE id=:id ORDER BY title',$c->text); + $rows=$c->query()->readAll(); + $this->assertEquals(1,count($rows)); + $this->assertEquals('post 5',$rows[0]['title']); + + $c=$builder->createFindCommand($table,new CDbCriteria(array( + 'select'=>'id, title', + 'order'=>'title', + 'limit'=>2, + 'offset'=>3))); + $this->assertEquals('SELECT * FROM (SELECT TOP 2 * FROM (SELECT TOP 5 id, title FROM [dbo].[posts] ORDER BY title) as [__inner top table__] ORDER BY title DESC) as [__outer top table__] ORDER BY title ASC',$c->text); + $rows=$c->query()->readAll(); + $this->assertEquals(2,count($rows)); + $this->assertEquals('post 4',$rows[0]['title']); + + + $c=$builder->createUpdateCommand($table,array('title'=>'new post 5'),new CDbCriteria(array( + 'condition'=>'id=:id', + 'params'=>array('id'=>5)))); + $c->execute(); + $c=$builder->createFindCommand($table,new CDbCriteria(array( + 'select'=>'title', + 'condition'=>'id=:id', + 'params'=>array('id'=>5)))); + $this->assertEquals('new post 5',$c->queryScalar()); + + $c=$builder->createSqlCommand('SELECT title FROM posts WHERE id=:id',array(':id'=>3)); + $this->assertEquals('post 3',$c->queryScalar()); + + $c=$builder->createUpdateCounterCommand($table,array('author_id'=>-2),new CDbCriteria(array('condition'=>'id=5'))); + $this->assertEquals('UPDATE [dbo].[posts] SET [author_id]=[author_id]-2 WHERE id=5',$c->text); + $c->execute(); + $c=$builder->createSqlCommand('SELECT author_id FROM posts WHERE id=5'); + $this->assertEquals(1,$c->queryScalar()); + + // test bind by position + $c=$builder->createFindCommand($table,new CDbCriteria(array( + 'select'=>'title', + 'condition'=>'id=?', + 'params'=>array(4)))); + $this->assertEquals('SELECT title FROM [dbo].[posts] WHERE id=?',$c->text); + $this->assertEquals('post 4',$c->queryScalar()); + + // another bind by position + $c=$builder->createUpdateCommand($table,array('title'=>'new post 4'),new CDbCriteria(array( + 'condition'=>'id=?', + 'params'=>array(4)))); + $c->execute(); + $c=$builder->createSqlCommand('SELECT title FROM posts WHERE id=4'); + $this->assertEquals('new post 4',$c->queryScalar()); + + // testCreateCriteria + $c=$builder->createCriteria('column=:value',array(':value'=>'value')); + $this->assertEquals('column=:value',$c->condition); + $this->assertEquals(array(':value'=>'value'),$c->params); + + $c=$builder->createCriteria(array('condition'=>'column=:value','params'=>array(':value'=>'value'))); + $this->assertEquals('column=:value',$c->condition); + $this->assertEquals(array(':value'=>'value'),$c->params); + + $c2=$builder->createCriteria($c); + $this->assertTrue($c2!==$c); + $this->assertEquals('column=:value',$c2->condition); + $this->assertEquals(array(':value'=>'value'),$c2->params); + + // testCreatePkCriteria + $c=$builder->createPkCriteria($table,1,'author_id>1'); + $this->assertEquals('[dbo].[posts].[id]=1 AND (author_id>1)',$c->condition); + + $c=$builder->createPkCriteria($table,array(1,2)); + $this->assertEquals('[dbo].[posts].[id] IN (1, 2)',$c->condition); + + $c=$builder->createPkCriteria($table,array()); + $this->assertEquals('0=1',$c->condition); + + $table2=$schema->getTable('orders'); + $c=$builder->createPkCriteria($table2,array('key1'=>1,'key2'=>2),'name=\'\''); + $this->assertEquals('[dbo].[orders].[key1]=1 AND [dbo].[orders].[key2]=2 AND (name=\'\')',$c->condition); + + $c=$builder->createPkCriteria($table2,array(array('key1'=>1,'key2'=>2),array('key1'=>3,'key2'=>4))); + $this->assertEquals('([dbo].[orders].[key1], [dbo].[orders].[key2]) IN ((1, 2), (3, 4))',$c->condition); + + // createColumnCriteria + $c=$builder->createColumnCriteria($table,array('id'=>1,'author_id'=>2),'title=\'\''); + $this->assertEquals('[dbo].[posts].[id]=:id AND [dbo].[posts].[author_id]=:author_id AND (title=\'\')',$c->condition); + + $c=$builder->createPkCriteria($table2,array()); + $this->assertEquals('0=1',$c->condition); + } + + public function testTransactions() + { + $transaction=$this->db->beginTransaction(); + $schema=$this->db->schema; + $builder=$schema->commandBuilder; + $table=$schema->getTable('posts'); + // Working transaction + try + { + $builder->createInsertCommand($table, array('title'=>'working transaction test post 1','create_time'=>'2009-01-01','author_id'=>1,'content'=>'test content'))->execute(); + $builder->createInsertCommand($table, array('title'=>'working transaction test post 2','create_time'=>'2009-01-01','author_id'=>1,'content'=>'test content'))->execute(); + $transaction->commit(); + } + catch (Exception $e) + { + $transaction->rollBack(); + } + $n=$builder->createCountCommand($table, new CDbCriteria(array('condition' => "title LIKE 'working transaction%'")))->queryScalar(); + $this->assertEquals(2, $n); + + + // Failing Transaction + $transaction=$this->db->beginTransaction(); + try + { + $builder->createInsertCommand($table, array('title'=>'failed transaction test post 1','create_time'=>'2009-01-01','author_id'=>1,'content'=>'test content'))->execute(); + $builder->createInsertCommand($table, array('id' => 1, 'title'=>'failed transaction test post 2','create_time'=>'2009-01-01','author_id'=>1,'content'=>'test content'))->execute(); + $transaction->commit(); + } + catch (Exception $e) + { + $transaction->rollBack(); + } + $n=$builder->createCountCommand($table, new CDbCriteria(array('condition' => "title LIKE 'failed transaction%'")))->queryScalar(); + $this->assertEquals(0, $n); + + } +} \ No newline at end of file diff --git a/tests/framework/db/schema/CMysql2Test.php b/tests/framework/db/schema/CMysql2Test.php new file mode 100644 index 000000000..fe7905162 --- /dev/null +++ b/tests/framework/db/schema/CMysql2Test.php @@ -0,0 +1,128 @@ +markTestSkipped('PDO and MySQL extensions are required.'); + + $this->db=new CDbConnection('mysql:host=127.0.0.1;dbname=yii','test','test'); + $this->db->charset='UTF8'; + try + { + $this->db->active=true; + } + catch(Exception $e) + { + $schemaFile=realpath(dirname(__FILE__).'/../data/mysql.sql'); + $this->markTestSkipped("Please read $schemaFile for details on setting up the test environment for MySQL test case."); + } + } + + public function tearDown() + { + $this->db->active=false; + } + + public function testCreateTable() + { + $sql=$this->db->schema->createTable('test',array( + 'id'=>'pk', + 'name'=>'string not null', + 'desc'=>'text', + 'primary key (id, name)', + ),'Engine=InnoDB'); + $expect="CREATE TABLE `test` (\n" + . "\t`id` int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY,\n" + . "\t`name` varchar(255) not null,\n" + . "\t`desc` text,\n" + . "\tprimary key (id, name)\n" + . ") Engine=InnoDB"; + $this->assertEquals($expect, $sql); + } + + public function testRenameTable() + { + $sql=$this->db->schema->renameTable('test', 'test2'); + $expect='RENAME TABLE `test` TO `test2`'; + $this->assertEquals($expect, $sql); + } + + public function testDropTable() + { + $sql=$this->db->schema->dropTable('test'); + $expect='DROP TABLE `test`'; + $this->assertEquals($expect, $sql); + } + + public function testAddColumn() + { + $sql=$this->db->schema->addColumn('test', 'id', 'integer'); + $expect='ALTER TABLE `test` ADD `id` int(11)'; + $this->assertEquals($expect, $sql); + } + + public function testAlterColumn() + { + $sql=$this->db->schema->alterColumn('test', 'id', 'boolean'); + $expect='ALTER TABLE `test` CHANGE `id` `id` tinyint(1)'; + $this->assertEquals($expect, $sql); + } + + public function testRenameColumn() + { + $sql=$this->db->schema->renameColumn('users', 'username', 'name'); + $expect='ALTER TABLE `users` CHANGE `username` `name` varchar(128) NOT NULL'; + $this->assertEquals($expect, $sql); + } + + public function testDropColumn() + { + $sql=$this->db->schema->dropColumn('test', 'id'); + $expect='ALTER TABLE `test` DROP COLUMN `id`'; + $this->assertEquals($expect, $sql); + } + + public function testAddForeignKey() + { + $sql=$this->db->schema->addForeignKey('fk_test', 'profile', 'user_id', 'users', 'id'); + $expect='ALTER TABLE `profile` ADD CONSTRAINT `fk_test` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`)'; + $this->assertEquals($expect, $sql); + + $sql=$this->db->schema->addForeignKey('fk_test', 'profile', 'user_id', 'users', 'id','CASCADE','RESTRICTED'); + $expect='ALTER TABLE `profile` ADD CONSTRAINT `fk_test` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE ON UPDATE RESTRICTED'; + $this->assertEquals($expect, $sql); + } + + public function testDropForeignKey() + { + $sql=$this->db->schema->dropForeignKey('fk_test', 'profile'); + $expect='ALTER TABLE `profile` DROP FOREIGN KEY `fk_test`'; + $this->assertEquals($expect, $sql); + } + + public function testCreateIndex() + { + $sql=$this->db->schema->createIndex('id_pk','test','id'); + $expect='CREATE INDEX `id_pk` ON `test` (`id`)'; + $this->assertEquals($expect, $sql); + + $sql=$this->db->schema->createIndex('id_pk','test','id1,id2',true); + $expect='CREATE UNIQUE INDEX `id_pk` ON `test` (`id1`, `id2`)'; + $this->assertEquals($expect, $sql); + } + + public function testDropIndex() + { + $sql=$this->db->schema->dropIndex('id_pk','test'); + $expect='DROP INDEX `id_pk` ON `test`'; + $this->assertEquals($expect, $sql); + } +} \ No newline at end of file diff --git a/tests/framework/db/schema/CMysqlTest.php b/tests/framework/db/schema/CMysqlTest.php new file mode 100644 index 000000000..86bca8462 --- /dev/null +++ b/tests/framework/db/schema/CMysqlTest.php @@ -0,0 +1,275 @@ +markTestSkipped('PDO and MySQL extensions are required.'); + + $this->db=new CDbConnection('mysql:host=127.0.0.1;dbname=yii','test','test'); + $this->db->charset='UTF8'; + try + { + $this->db->active=true; + } + catch(Exception $e) + { + $schemaFile=realpath(dirname(__FILE__).'/../data/mysql.sql'); + $this->markTestSkipped("Please read $schemaFile for details on setting up the test environment for MySQL test case."); + } + + $tables=array('comments','post_category','posts','categories','profiles','users','items','orders','types'); + foreach($tables as $table) + $this->db->createCommand("DROP TABLE IF EXISTS $table CASCADE")->execute(); + + $sqls=file_get_contents(dirname(__FILE__).'/../data/mysql.sql'); + foreach(explode(';',$sqls) as $sql) + { + if(trim($sql)!=='') + $this->db->createCommand($sql)->execute(); + } + } + + public function tearDown() + { + $this->db->active=false; + } + + public function testSchema() + { + $schema=$this->db->schema; + $this->assertTrue($schema instanceof CDbSchema); + $this->assertEquals($schema->dbConnection,$this->db); + $this->assertTrue($schema->commandBuilder instanceof CDbCommandBuilder); + $this->assertEquals('`posts`',$schema->quoteTableName('posts')); + $this->assertEquals('`id`',$schema->quoteColumnName('id')); + $this->assertTrue($schema->getTable('posts') instanceof CDbTableSchema); + $this->assertNull($schema->getTable('foo')); + } + + public function testTable() + { + $table=$this->db->schema->getTable('posts'); + $this->assertTrue($table instanceof CDbTableSchema); + $this->assertEquals('posts',$table->name); + $this->assertEquals('`posts`',$table->rawName); + $this->assertEquals('id',$table->primaryKey); + $this->assertEquals(array('author_id'=>array('users','id')),$table->foreignKeys); + $this->assertEquals('',$table->sequenceName); + $this->assertEquals(5,count($table->columns)); + + $this->assertTrue($table->getColumn('id') instanceof CDbColumnSchema); + $this->assertTrue($table->getColumn('foo')===null); + $this->assertEquals(array('id','title','create_time','author_id','content'),$table->columnNames); + + $table=$this->db->schema->getTable('orders'); + $this->assertEquals(array('key1','key2'),$table->primaryKey); + + $table=$this->db->schema->getTable('items'); + $this->assertEquals('id',$table->primaryKey); + $this->assertEquals(array('col1'=>array('orders','key1'),'col2'=>array('orders','key2')),$table->foreignKeys); + + $table=$this->db->schema->getTable('types'); + $this->assertTrue($table instanceof CDbTableSchema); + $this->assertEquals('types',$table->name); + $this->assertEquals('`types`',$table->rawName); + $this->assertTrue($table->primaryKey===null); + $this->assertTrue($table->foreignKeys===array()); + $this->assertTrue($table->sequenceName===null); + + $table=$this->db->schema->getTable('invalid'); + $this->assertNull($table); + } + + public function testColumn() + { + $values=array + ( + 'name'=>array('id', 'title', 'create_time', 'author_id', 'content'), + 'rawName'=>array('`id`', '`title`', '`create_time`', '`author_id`', '`content`'), + 'defaultValue'=>array(null, null, null, null, null), + 'size'=>array(11, 128, null, 11, null), + 'precision'=>array(11, 128, null, 11, null), + 'scale'=>array(null, null, null, null, null), + 'dbType'=>array('int(11)','varchar(128)','timestamp','int(11)','text'), + 'type'=>array('integer','string','string','integer','string'), + 'isPrimaryKey'=>array(true,false,false,false,false), + 'isForeignKey'=>array(false,false,false,true,false), + ); + $this->checkColumns('posts',$values); + $values=array + ( + 'name'=>array('int_col', 'int_col2', 'char_col', 'char_col2', 'char_col3', 'float_col', 'float_col2', 'blob_col', 'numeric_col', 'time', 'bool_col', 'bool_col2'), + 'rawName'=>array('`int_col`', '`int_col2`', '`char_col`', '`char_col2`', '`char_col3`', '`float_col`', '`float_col2`', '`blob_col`', '`numeric_col`', '`time`', '`bool_col`', '`bool_col2`'), + 'defaultValue'=>array(null, 1, null, 'something', null, null, '1.23', null, '33.22', '2002-01-01 00:00:00', null, 1), + 'size'=>array(11, 11, 100, 100, null, 4, null, null, 5, null, 1, 1), + 'precision'=>array(11, 11, 100, 100, null, 4, null, null, 5, null, 1, 1), + 'scale'=>array(null, null, null, null, null, 3, null, null, 2, null, null, null), + 'dbType'=>array('int(11)','int(11)','char(100)','varchar(100)','text','double(4,3)','double','blob','decimal(5,2)','timestamp','tinyint(1)','tinyint(1)'), + 'type'=>array('integer','integer','string','string','string','double','double','string','string','string','integer','integer'), + 'isPrimaryKey'=>array(false,false,false,false,false,false,false,false,false,false,false,false), + 'isForeignKey'=>array(false,false,false,false,false,false,false,false,false,false,false,false), + ); + $this->checkColumns('types',$values); + } + + protected function checkColumns($tableName,$values) + { + $table=$this->db->schema->getTable($tableName); + foreach($values as $name=>$value) + { + foreach(array_values($table->columns) as $i=>$column) + { + $type1=gettype($column->$name); + $type2=gettype($value[$i]); + $this->assertTrue($column->$name===$value[$i], "$tableName.{$column->name}.$name is {$column->$name} ($type1), different from the expected {$value[$i]} ($type2)."); + } + } + } + + public function testCommandBuilder() + { + $schema=$this->db->schema; + $builder=$schema->commandBuilder; + $this->assertTrue($builder instanceof CDbCommandBuilder); + $table=$schema->getTable('posts'); + + $c=$builder->createInsertCommand($table,array('title'=>'test post','create_time'=>'2000-01-01','author_id'=>1,'content'=>'test content')); + $this->assertEquals('INSERT INTO `posts` (`title`, `create_time`, `author_id`, `content`) VALUES (:yp0, :yp1, :yp2, :yp3)',$c->text); + $c->execute(); + $this->assertEquals(6,$builder->getLastInsertId($table)); + + $c=$builder->createCountCommand($table,new CDbCriteria); + $this->assertEquals('SELECT COUNT(*) FROM `posts` `t`',$c->text); + $this->assertEquals(6,$c->queryScalar()); + + $c=$builder->createDeleteCommand($table,new CDbCriteria(array( + 'condition'=>'id=:id', + 'params'=>array('id'=>6)))); + $this->assertEquals('DELETE FROM `posts` WHERE id=:id',$c->text); + $c->execute(); + $c=$builder->createCountCommand($table,new CDbCriteria); + $this->assertEquals(5,$c->queryScalar()); + + $c=$builder->createFindCommand($table,new CDbCriteria(array( + 'select'=>'id, title', + 'condition'=>'id=:id', + 'params'=>array('id'=>5), + 'order'=>'title', + 'limit'=>2, + 'offset'=>0))); + $this->assertEquals('SELECT id, title FROM `posts` `t` WHERE id=:id ORDER BY title LIMIT 2',$c->text); + $rows=$c->query()->readAll(); + $this->assertEquals(1,count($rows)); + $this->assertEquals('post 5',$rows[0]['title']); + + $c=$builder->createUpdateCommand($table,array('title'=>'new post 5'),new CDbCriteria(array( + 'condition'=>'id=:id', + 'params'=>array('id'=>5)))); + $c->execute(); + $c=$builder->createFindCommand($table,new CDbCriteria(array( + 'select'=>'title', + 'condition'=>'id=:id', + 'params'=>array('id'=>5)))); + $this->assertEquals('new post 5',$c->queryScalar()); + + $c=$builder->createSqlCommand('SELECT title FROM posts WHERE id=:id',array(':id'=>3)); + $this->assertEquals('post 3',$c->queryScalar()); + + $c=$builder->createUpdateCounterCommand($table,array('author_id'=>-2),new CDbCriteria(array('condition'=>'id=5'))); + $this->assertEquals('UPDATE `posts` SET `author_id`=`author_id`-2 WHERE id=5',$c->text); + $c->execute(); + $c=$builder->createSqlCommand('SELECT author_id FROM posts WHERE id=5'); + $this->assertEquals(1,$c->queryScalar()); + + // test bind by position + $c=$builder->createFindCommand($table,new CDbCriteria(array( + 'select'=>'title', + 'condition'=>'id=?', + 'params'=>array(4)))); + $this->assertEquals('SELECT title FROM `posts` `t` WHERE id=?',$c->text); + $this->assertEquals('post 4',$c->queryScalar()); + + // another bind by position + $c=$builder->createUpdateCommand($table,array('title'=>'new post 4'),new CDbCriteria(array( + 'condition'=>'id=?', + 'params'=>array(4)))); + $c->execute(); + $c=$builder->createSqlCommand('SELECT title FROM posts WHERE id=4'); + $this->assertEquals('new post 4',$c->queryScalar()); + + // testCreateCriteria + $c=$builder->createCriteria('column=:value',array(':value'=>'value')); + $this->assertEquals('column=:value',$c->condition); + $this->assertEquals(array(':value'=>'value'),$c->params); + + $c=$builder->createCriteria(array('condition'=>'column=:value','params'=>array(':value'=>'value'))); + $this->assertEquals('column=:value',$c->condition); + $this->assertEquals(array(':value'=>'value'),$c->params); + + $c2=$builder->createCriteria($c); + $this->assertTrue($c2!==$c); + $this->assertEquals('column=:value',$c2->condition); + $this->assertEquals(array(':value'=>'value'),$c2->params); + + // testCreatePkCriteria + $c=$builder->createPkCriteria($table,1,'author_id>1'); + $this->assertEquals('`posts`.`id`=1 AND (author_id>1)',$c->condition); + + $c=$builder->createPkCriteria($table,array(1,2)); + $this->assertEquals('`posts`.`id` IN (1, 2)',$c->condition); + + $c=$builder->createPkCriteria($table,array()); + $this->assertEquals('0=1',$c->condition); + + $table2=$schema->getTable('orders'); + $c=$builder->createPkCriteria($table2,array('key1'=>1,'key2'=>2),'name=``'); + $this->assertEquals('`orders`.`key1`=1 AND `orders`.`key2`=2 AND (name=``)',$c->condition); + + $c=$builder->createPkCriteria($table2,array(array('key1'=>1,'key2'=>2),array('key1'=>3,'key2'=>4))); + $this->assertEquals('(`orders`.`key1`, `orders`.`key2`) IN ((1, 2), (3, 4))',$c->condition); + + // createColumnCriteria + $c=$builder->createColumnCriteria($table,array('id'=>1,'author_id'=>2),'title=``'); + $this->assertEquals('`posts`.`id`=:yp0 AND `posts`.`author_id`=:yp1 AND (title=``)',$c->condition); + + $c=$builder->createPkCriteria($table2,array()); + $this->assertEquals('0=1',$c->condition); + } + + public function testResetSequence() + { + $max=$this->db->createCommand("SELECT MAX(id) FROM users")->queryScalar(); + $this->db->createCommand("DELETE FROM users")->execute(); + $this->db->createCommand("INSERT INTO users (username, password, email) VALUES ('user4','pass4','email4')")->execute(); + $max2=$this->db->createCommand("SELECT MAX(id) FROM users")->queryScalar(); + $this->assertEquals($max+1,$max2); + + $userTable=$this->db->schema->getTable('users'); + + $this->db->createCommand("DELETE FROM users")->execute(); + $this->db->schema->resetSequence($userTable);return; + $this->db->createCommand("INSERT INTO users (username, password, email) VALUES ('user4','pass4','email4')")->execute(); + $max=$this->db->createCommand("SELECT MAX(id) FROM users")->queryScalar(); + $this->assertEquals(1,$max); + $this->db->createCommand("INSERT INTO users (username, password, email) VALUES ('user4','pass4','email4')")->execute(); + $max=$this->db->createCommand("SELECT MAX(id) FROM users")->queryScalar(); + $this->assertEquals(2,$max); + + $this->db->createCommand("DELETE FROM users")->execute(); + $this->db->schema->resetSequence($userTable,10); + $this->db->createCommand("INSERT INTO users (username, password, email) VALUES ('user4','pass4','email4')")->execute(); + $max=$this->db->createCommand("SELECT MAX(id) FROM users")->queryScalar(); + $this->assertEquals(10,$max); + $this->db->createCommand("INSERT INTO users (username, password, email) VALUES ('user4','pass4','email4')")->execute(); + $max=$this->db->createCommand("SELECT MAX(id) FROM users")->queryScalar(); + $this->assertEquals(11,$max); + } +} \ No newline at end of file diff --git a/tests/framework/db/schema/CPostgres2Test.php b/tests/framework/db/schema/CPostgres2Test.php new file mode 100644 index 000000000..0d515bc15 --- /dev/null +++ b/tests/framework/db/schema/CPostgres2Test.php @@ -0,0 +1,126 @@ +markTestSkipped('PDO and PostgreSQL extensions are required.'); + + $this->db=new CDbConnection('pgsql:host=127.0.0.1;dbname=yii','test','test'); + $this->db->charset='UTF8'; + try + { + $this->db->active=true; + } + catch(Exception $e) + { + $schemaFile=realpath(dirname(__FILE__).'/../data/postgres.sql'); + $this->markTestSkipped("Please read $schemaFile for details on setting up the test environment for PostgreSQL test case."); + } + } + + public function tearDown() + { + $this->db->active=false; + } + + public function testCreateTable() + { + $sql=$this->db->schema->createTable('test',array( + 'id'=>'pk', + 'name'=>'string not null', + 'desc'=>'text', + 'primary key (id, name)', + )); + $expect="CREATE TABLE \"test\" (\n" + . "\t\"id\" serial NOT NULL PRIMARY KEY,\n" + . "\t\"name\" character varying (255) not null,\n" + . "\t\"desc\" text,\n" + . "\tprimary key (id, name)\n" + . ")"; + $this->assertEquals($expect, $sql); + } + + public function testRenameTable() + { + $sql=$this->db->schema->renameTable('test', 'test2'); + $expect='ALTER TABLE "test" RENAME TO "test2"'; + $this->assertEquals($expect, $sql); + } + + public function testDropTable() + { + $sql=$this->db->schema->dropTable('test'); + $expect='DROP TABLE "test"'; + $this->assertEquals($expect, $sql); + } + + public function testAddColumn() + { + $sql=$this->db->schema->addColumn('test', 'id', 'integer'); + $expect='ALTER TABLE "test" ADD COLUMN "id" integer'; + $this->assertEquals($expect, $sql); + } + + public function testAlterColumn() + { + $sql=$this->db->schema->alterColumn('test', 'id', 'boolean'); + $expect='ALTER TABLE "test" ALTER COLUMN "id" TYPE boolean'; + $this->assertEquals($expect, $sql); + } + + public function testRenameColumn() + { + $sql=$this->db->schema->renameColumn('users', 'username', 'name'); + $expect='ALTER TABLE "users" RENAME COLUMN "username" TO "name"'; + $this->assertEquals($expect, $sql); + } + + public function testDropColumn() + { + $sql=$this->db->schema->dropColumn('test', 'id'); + $expect='ALTER TABLE "test" DROP COLUMN "id"'; + $this->assertEquals($expect, $sql); + } + + public function testAddForeignKey() + { + $sql=$this->db->schema->addForeignKey('fk_test', 'profile', 'user_id', 'users', 'id'); + $expect='ALTER TABLE "profile" ADD CONSTRAINT "fk_test" FOREIGN KEY ("user_id") REFERENCES "users" ("id")'; + $this->assertEquals($expect, $sql); + + $sql=$this->db->schema->addForeignKey('fk_test', 'profile', 'user_id', 'users', 'id','CASCADE','RESTRICTED'); + $expect='ALTER TABLE "profile" ADD CONSTRAINT "fk_test" FOREIGN KEY ("user_id") REFERENCES "users" ("id") ON DELETE CASCADE ON UPDATE RESTRICTED'; + $this->assertEquals($expect, $sql); + } + + public function testDropForeignKey() + { + $sql=$this->db->schema->dropForeignKey('fk_test', 'profile'); + $expect='ALTER TABLE "profile" DROP CONSTRAINT "fk_test"'; + $this->assertEquals($expect, $sql); + } + + public function testCreateIndex() + { + $sql=$this->db->schema->createIndex('id_pk','test','id'); + $expect='CREATE INDEX "id_pk" ON "test" ("id")'; + $this->assertEquals($expect, $sql); + + $sql=$this->db->schema->createIndex('id_pk','test','id1,id2',true); + $expect='CREATE UNIQUE INDEX "id_pk" ON "test" ("id1", "id2")'; + $this->assertEquals($expect, $sql); + } + + public function testDropIndex() + { + $sql=$this->db->schema->dropIndex('id_pk','test'); + $expect='DROP INDEX "id_pk"'; + $this->assertEquals($expect, $sql); + } +} \ No newline at end of file diff --git a/tests/framework/db/schema/CPostgresTest.php b/tests/framework/db/schema/CPostgresTest.php new file mode 100644 index 000000000..7d78de50f --- /dev/null +++ b/tests/framework/db/schema/CPostgresTest.php @@ -0,0 +1,271 @@ +markTestSkipped('PDO and PostgreSQL extensions are required.'); + + $this->db=new CDbConnection('pgsql:host=127.0.0.1;dbname=yii','test','test'); + $this->db->charset='UTF8'; + try + { + $this->db->active=true; + } + catch(Exception $e) + { + $schemaFile=realpath(dirname(__FILE__).'/../data/postgres.sql'); + $this->markTestSkipped("Please read $schemaFile for details on setting up the test environment for PostgreSQL test case."); + } + + try { $this->db->createCommand('DROP SCHEMA test CASCADE')->execute(); } catch(Exception $e) { } + try { $this->db->createCommand('DROP TABLE yii_types CASCADE')->execute(); } catch(Exception $e) { } + + $sqls=file_get_contents(dirname(__FILE__).'/../data/postgres.sql'); + foreach(explode(';',$sqls) as $sql) + { + if(trim($sql)!=='') + $this->db->createCommand($sql)->execute(); + } + } + + public function tearDown() + { + $this->db->active=false; + } + + public function testSchema() + { + $schema=$this->db->schema; + $this->assertTrue($schema instanceof CDbSchema); + $this->assertEquals($schema->dbConnection,$this->db); + $this->assertTrue($schema->commandBuilder instanceof CDbCommandBuilder); + $this->assertEquals('"posts"',$schema->quoteTableName('posts')); + $this->assertEquals('"id"',$schema->quoteColumnName('id')); + $this->assertTrue($schema->getTable('test.posts') instanceof CDbTableSchema); + $this->assertTrue($schema->getTable('foo')===null); + } + + public function testTable() + { + $table=$this->db->schema->getTable('test.posts'); + $this->assertTrue($table instanceof CDbTableSchema); + $this->assertEquals('posts',$table->name); + $this->assertEquals('"test"."posts"',$table->rawName); + $this->assertEquals('id',$table->primaryKey); + $this->assertEquals(array('author_id'=>array('users','id')),$table->foreignKeys); + $this->assertEquals('test.posts_id_seq',$table->sequenceName); + $this->assertEquals(5,count($table->columns)); + + $this->assertTrue($table->getColumn('id') instanceof CDbColumnSchema); + $this->assertTrue($table->getColumn('foo')===null); + $this->assertEquals(array('id','title','create_time','author_id','content'),$table->columnNames); + + $table=$this->db->schema->getTable('test.orders'); + $this->assertEquals(array('key1','key2'),$table->primaryKey); + + $table=$this->db->schema->getTable('test.items'); + $this->assertEquals('id',$table->primaryKey); + $this->assertEquals(array('col1'=>array('orders','key1'),'col2'=>array('orders','key2')),$table->foreignKeys); + + $table=$this->db->schema->getTable('yii_types'); + $this->assertTrue($table instanceof CDbTableSchema); + $this->assertEquals('yii_types',$table->name); + $this->assertEquals('"yii_types"',$table->rawName); + $this->assertTrue($table->primaryKey===null); + $this->assertTrue($table->foreignKeys===array()); + $this->assertTrue($table->sequenceName===null); + + $table=$this->db->schema->getTable('invalid'); + $this->assertNull($table); + } + + public function testColumn() + { + $values=array + ( + 'name'=>array('id', 'title', 'create_time', 'author_id', 'content'), + 'rawName'=>array('"id"', '"title"', '"create_time"', '"author_id"', '"content"'), + 'defaultValue'=>array(null, null, null, null, null), + 'size'=>array(null, 128, null, null, null), + 'precision'=>array(null, 128, null, null, null), + 'scale'=>array(null, null, null, null, null), + 'type'=>array('integer','string','string','integer','string'), + 'isPrimaryKey'=>array(true,false,false,false,false), + 'isForeignKey'=>array(false,false,false,true,false), + ); + $this->checkColumns('test.posts',$values); + $values=array + ( + 'name'=>array('int_col', 'int_col2', 'char_col', 'char_col2', 'char_col3', 'numeric_col', 'real_col', 'blob_col', 'time', 'bool_col', 'bool_col2'), + 'rawName'=>array('"int_col"', '"int_col2"', '"char_col"', '"char_col2"', '"char_col3"', '"numeric_col"', '"real_col"', '"blob_col"', '"time"', '"bool_col"', '"bool_col2"'), + 'defaultValue'=>array(null, 1, null, 'something', null, null, '1.23', null, null, null, true), + 'size'=>array(null, null, 100, 100, null, 4, null, null, null, null, null), + 'precision'=>array(null, null, 100, 100, null, 4, null, null, null, null, null), + 'scale'=>array(null, null, null, null, null, 3, null, null, null, null, null), + 'type'=>array('integer','integer','string','string','string','string','double','string','string','boolean','boolean'), + 'isPrimaryKey'=>array(false,false,false,false,false,false,false,false,false,false,false), + 'isForeignKey'=>array(false,false,false,false,false,false,false,false,false,false,false), + ); + $this->checkColumns('yii_types',$values); + } + + protected function checkColumns($tableName,$values) + { + $table=$this->db->schema->getTable($tableName); + foreach($values as $name=>$value) + { + foreach(array_values($table->columns) as $i=>$column) + { + $type1=gettype($column->$name); + $type2=gettype($value[$i]); + $this->assertTrue($column->$name===$value[$i], "$tableName.{$column->name}.$name is {$column->$name} ($type1), different from the expected {$value[$i]} ($type2)."); + } + } + } + + public function testCommandBuilder() + { + $schema=$this->db->schema; + $builder=$schema->commandBuilder; + $this->assertTrue($builder instanceof CDbCommandBuilder); + $table=$schema->getTable('test.posts'); + + $c=$builder->createInsertCommand($table,array('title'=>'test post','create_time'=>'2004-10-19 10:23:54','author_id'=>1,'content'=>'test content')); + $this->assertEquals('INSERT INTO "test"."posts" ("title", "create_time", "author_id", "content") VALUES (:yp0, :yp1, :yp2, :yp3)',$c->text); + $c->execute(); + $this->assertEquals(6,$builder->getLastInsertId($table)); + + $c=$builder->createCountCommand($table,new CDbCriteria); + $this->assertEquals('SELECT COUNT(*) FROM "test"."posts" "t"',$c->text); + $this->assertEquals(6,$c->queryScalar()); + + $c=$builder->createDeleteCommand($table,new CDbCriteria(array( + 'condition'=>'id=:id', + 'params'=>array('id'=>6)))); + $this->assertEquals('DELETE FROM "test"."posts" WHERE id=:id',$c->text); + $c->execute(); + $c=$builder->createCountCommand($table,new CDbCriteria); + $this->assertEquals(5,$c->queryScalar()); + + $c=$builder->createFindCommand($table,new CDbCriteria(array( + 'select'=>'id, title', + 'condition'=>'id=:id', + 'params'=>array('id'=>5), + 'order'=>'title', + 'limit'=>2, + 'offset'=>0))); + $this->assertEquals('SELECT id, title FROM "test"."posts" "t" WHERE id=:id ORDER BY title LIMIT 2',$c->text); + $rows=$c->query()->readAll(); + $this->assertEquals(1,count($rows)); + $this->assertEquals('post 5',$rows[0]['title']); + + $c=$builder->createUpdateCommand($table,array('title'=>'new post 5'),new CDbCriteria(array( + 'condition'=>'id=:id', + 'params'=>array('id'=>5)))); + $c->execute(); + $c=$builder->createFindCommand($table,new CDbCriteria(array( + 'select'=>'title', + 'condition'=>'id=:id', + 'params'=>array('id'=>5)))); + $this->assertEquals('new post 5',$c->queryScalar()); + + $c=$builder->createSqlCommand('SELECT title FROM test.posts WHERE id=:id',array(':id'=>3)); + $this->assertEquals('post 3',$c->queryScalar()); + + $c=$builder->createUpdateCounterCommand($table,array('author_id'=>-2),new CDbCriteria(array('condition'=>'id=5'))); + $this->assertEquals('UPDATE "test"."posts" SET "author_id"="author_id"-2 WHERE id=5',$c->text); + $c->execute(); + $c=$builder->createSqlCommand('SELECT author_id FROM posts WHERE id=5'); + $this->assertEquals(1,$c->queryScalar()); + + // test bind by position + $c=$builder->createFindCommand($table,new CDbCriteria(array( + 'select'=>'title', + 'condition'=>'id=?', + 'params'=>array(4)))); + $this->assertEquals('SELECT title FROM "test"."posts" "t" WHERE id=?',$c->text); + $this->assertEquals('post 4',$c->queryScalar()); + + // another bind by position + $c=$builder->createUpdateCommand($table,array('title'=>'new post 4'),new CDbCriteria(array( + 'condition'=>'id=?', + 'params'=>array(4)))); + $c->execute(); + $c=$builder->createSqlCommand('SELECT title FROM test.posts WHERE id=4'); + $this->assertEquals('new post 4',$c->queryScalar()); + + // testCreateCriteria + $c=$builder->createCriteria('column=:value',array(':value'=>'value')); + $this->assertEquals('column=:value',$c->condition); + $this->assertEquals(array(':value'=>'value'),$c->params); + + $c=$builder->createCriteria(array('condition'=>'column=:value','params'=>array(':value'=>'value'))); + $this->assertEquals('column=:value',$c->condition); + $this->assertEquals(array(':value'=>'value'),$c->params); + + $c2=$builder->createCriteria($c); + $this->assertTrue($c2!==$c); + $this->assertEquals('column=:value',$c2->condition); + $this->assertEquals(array(':value'=>'value'),$c2->params); + + // testCreatePkCriteria + $c=$builder->createPkCriteria($table,1,'author_id>1'); + $this->assertEquals('"test"."posts"."id"=1 AND (author_id>1)',$c->condition); + + $c=$builder->createPkCriteria($table,array(1,2)); + $this->assertEquals('"test"."posts"."id" IN (1, 2)',$c->condition); + + $table2=$schema->getTable('test.orders'); + $c=$builder->createPkCriteria($table2,array('key1'=>1,'key2'=>2),'name=\'\''); + $this->assertEquals('"test"."orders"."key1"=1 AND "test"."orders"."key2"=2 AND (name=\'\')',$c->condition); + + $c=$builder->createPkCriteria($table2,array(array('key1'=>1,'key2'=>2),array('key1'=>3,'key2'=>4))); + $this->assertEquals('("test"."orders"."key1", "test"."orders"."key2") IN ((1, 2), (3, 4))',$c->condition); + + // createColumnCriteria + $c=$builder->createColumnCriteria($table,array('id'=>1,'author_id'=>2),'title=\'\''); + $this->assertEquals('"test"."posts"."id"=:yp0 AND "test"."posts"."author_id"=:yp1 AND (title=\'\')',$c->condition); + } + + public function testResetSequence() + { + $max=$this->db->createCommand("SELECT MAX(id) FROM test.users")->queryScalar(); + $this->db->createCommand("DELETE FROM test.users")->execute(); + $this->db->createCommand("INSERT INTO test.users (username, password, email) VALUES ('user4','pass4','email4')")->execute(); + $max2=$this->db->createCommand("SELECT MAX(id) FROM test.users")->queryScalar(); + $this->assertEquals($max+1,$max2); + + $userTable=$this->db->schema->getTable('test.users'); + + $this->db->createCommand("DELETE FROM test.users")->execute(); + $this->db->schema->resetSequence($userTable); + $this->db->createCommand("INSERT INTO test.users (username, password, email) VALUES ('user4','pass4','email4')")->execute(); + $max=$this->db->createCommand("SELECT MAX(id) FROM test.users")->queryScalar(); + $this->assertEquals(1,$max); + $this->db->createCommand("INSERT INTO test.users (username, password, email) VALUES ('user4','pass4','email4')")->execute(); + $max=$this->db->createCommand("SELECT MAX(id) FROM test.users")->queryScalar(); + $this->assertEquals(2,$max); + + $this->db->createCommand("DELETE FROM test.users")->execute(); + $this->db->schema->resetSequence($userTable,10); + $this->db->createCommand("INSERT INTO test.users (username, password, email) VALUES ('user4','pass4','email4')")->execute(); + $max=$this->db->createCommand("SELECT MAX(id) FROM test.users")->queryScalar(); + $this->assertEquals(10,$max); + $this->db->createCommand("INSERT INTO test.users (username, password, email) VALUES ('user4','pass4','email4')")->execute(); + $max=$this->db->createCommand("SELECT MAX(id) FROM test.users")->queryScalar(); + $this->assertEquals(11,$max); + } + + public function testCheckIntegrity() + { + $this->db->schema->checkIntegrity(false,'test'); + $this->db->createCommand("INSERT INTO test.profiles (first_name, last_name, user_id) VALUES ('first 1','last 1',1000)")->execute(); + $this->db->schema->checkIntegrity(true,'test'); + } +} \ No newline at end of file diff --git a/tests/framework/db/schema/CSqliteTest.php b/tests/framework/db/schema/CSqliteTest.php new file mode 100644 index 000000000..ec84b194a --- /dev/null +++ b/tests/framework/db/schema/CSqliteTest.php @@ -0,0 +1,258 @@ +markTestSkipped('PDO and SQLite extensions are required.'); + + $this->db=new CDbConnection('sqlite::memory:'); + $this->db->active=true; + $this->db->pdoInstance->exec(file_get_contents(dirname(__FILE__).'/../data/sqlite.sql')); + } + + public function tearDown() + { + $this->db->active=false; + } + + public function testSchema() + { + $schema=$this->db->schema; + $this->assertTrue($schema instanceof CDbSchema); + $this->assertEquals($schema->dbConnection,$this->db); + $this->assertTrue($schema->commandBuilder instanceof CDbCommandBuilder); + $this->assertEquals('\'posts\'',$schema->quoteTableName('posts')); + $this->assertEquals('"id"',$schema->quoteColumnName('id')); + $this->assertTrue($schema->getTable('posts') instanceof CDbTableSchema); + $this->assertTrue($schema->getTable('foo')===null); + } + + public function testTable() + { + $table=$this->db->schema->getTable('posts'); + $this->assertTrue($table instanceof CDbTableSchema); + $this->assertEquals('posts',$table->name); + $this->assertEquals('\'posts\'',$table->rawName); + $this->assertEquals('id',$table->primaryKey); + $this->assertEquals(array('author_id'=>array('users','id')),$table->foreignKeys); + $this->assertTrue($table->sequenceName===''); + $this->assertEquals(5,count($table->columns)); + + $this->assertTrue($table->getColumn('id') instanceof CDbColumnSchema); + $this->assertTrue($table->getColumn('foo')===null); + $this->assertEquals(array('id','title','create_time','author_id','content'),$table->columnNames); + + $table=$this->db->schema->getTable('orders'); + $this->assertEquals(array('key1','key2'),$table->primaryKey); + + $table=$this->db->schema->getTable('items'); + $this->assertEquals('id',$table->primaryKey); + $this->assertEquals(array('col1'=>array('orders','key1'),'col2'=>array('orders','key2')),$table->foreignKeys); + + $table=$this->db->schema->getTable('types'); + $this->assertTrue($table->primaryKey===null); + $this->assertTrue($table->foreignKeys===array()); + $this->assertTrue($table->sequenceName===null); + + $table=$this->db->schema->getTable('invalid'); + $this->assertNull($table); + } + + public function testColumn() + { + $values=array + ( + 'name'=>array('id', 'title', 'create_time', 'author_id', 'content'), + 'rawName'=>array('"id"', '"title"', '"create_time"', '"author_id"', '"content"'), + 'defaultValue'=>array(null, null, null, null, null), + 'size'=>array(null, 128, null, null, null), + 'precision'=>array(null, 128, null, null, null), + 'scale'=>array(null, null, null, null, null), + 'dbType'=>array('integer','varchar(128)','timestamp','integer','text'), + 'type'=>array('integer','string','string','integer','string'), + 'isPrimaryKey'=>array(true,false,false,false,false), + 'isForeignKey'=>array(false,false,false,true,false), + ); + $this->checkColumns('posts',$values); + $values=array + ( + 'name'=>array('int_col', 'int_col2', 'char_col', 'char_col2', 'char_col3', 'float_col', 'float_col2', 'blob_col', 'numeric_col', 'time', 'bool_col', 'bool_col2', 'null_col'), + 'rawName'=>array('"int_col"', '"int_col2"', '"char_col"', '"char_col2"', '"char_col3"', '"float_col"', '"float_col2"', '"blob_col"', '"numeric_col"', '"time"', '"bool_col"', '"bool_col2"', '"null_col"'), + 'defaultValue'=>array(null, 1, null, 'something', null, null, '1.23', null, '33.22', '123', null, true, null), + 'size'=>array(null, null, 100, 100, null, 4, null, null, 5, null, null, null, null), + 'precision'=>array(null, null, 100, 100, null, 4, null, null, 5, null, null, null, null), + 'scale'=>array(null, null, null, null, null, 3, null, null, 2, null, null, null, null), + 'dbType'=>array('int','integer','char(100)','varchar(100)','text','real(4,3)','double','blob','numeric(5,2)','timestamp','bool','boolean','integer'), + 'type'=>array('integer','integer','string','string','string','double','double','string','string','string','boolean','boolean','integer'), + 'isPrimaryKey'=>array(false,false,false,false,false,false,false,false,false,false,false,false,false), + 'isForeignKey'=>array(false,false,false,false,false,false,false,false,false,false,false,false,false), + ); + $this->checkColumns('types',$values); + } + + protected function checkColumns($tableName,$values) + { + $table=$this->db->schema->getTable($tableName); + foreach($values as $name=>$value) + { + foreach(array_values($table->columns) as $i=>$column) + { + $type1=gettype($column->$name); + $type2=gettype($value[$i]); + $this->assertTrue($column->$name===$value[$i], "$tableName.{$column->name}.$name is {$column->$name} ($type1), different from the expected {$value[$i]} ($type2)."); + } + } + } + + public function testCommandBuilder() + { + $schema=$this->db->schema; + $builder=$schema->commandBuilder; + $this->assertTrue($builder instanceof CDbCommandBuilder); + $table=$schema->getTable('posts'); + + $c=$builder->createInsertCommand($table,array('title'=>'test post','create_time'=>time(),'author_id'=>1,'content'=>'test content')); + $this->assertEquals('INSERT INTO \'posts\' ("title", "create_time", "author_id", "content") VALUES (:yp0, :yp1, :yp2, :yp3)',$c->text); + $c->execute(); + $this->assertEquals(6,$builder->getLastInsertId($table)); + + $c=$builder->createCountCommand($table,new CDbCriteria); + $this->assertEquals('SELECT COUNT(*) FROM \'posts\' \'t\'',$c->text); + $this->assertEquals(6,$c->queryScalar()); + + $c=$builder->createDeleteCommand($table,new CDbCriteria(array( + 'condition'=>'id=:id', + 'params'=>array('id'=>6)))); + $this->assertEquals('DELETE FROM \'posts\' WHERE id=:id',$c->text); + $c->execute(); + $c=$builder->createCountCommand($table,new CDbCriteria); + $this->assertEquals(5,$c->queryScalar()); + + $c=$builder->createFindCommand($table,new CDbCriteria(array( + 'select'=>'id, title', + 'condition'=>'id=:id', + 'params'=>array('id'=>5), + 'order'=>'title', + 'limit'=>2, + 'offset'=>0))); + $this->assertEquals('SELECT id, title FROM \'posts\' \'t\' WHERE id=:id ORDER BY title LIMIT 2',$c->text); + $rows=$c->query()->readAll(); + $this->assertEquals(1,count($rows)); + $this->assertEquals('post 5',$rows[0]['title']); + + $c=$builder->createUpdateCommand($table,array('title'=>'new post 5'),new CDbCriteria(array( + 'condition'=>'id=:id', + 'params'=>array('id'=>5)))); + $c->execute(); + $c=$builder->createFindCommand($table,new CDbCriteria(array( + 'select'=>'title', + 'condition'=>'id=:id', + 'params'=>array('id'=>5)))); + $this->assertEquals('new post 5',$c->queryScalar()); + + $c=$builder->createSqlCommand('SELECT title FROM posts \'t\' WHERE id=:id',array(':id'=>3)); + $this->assertEquals('post 3',$c->queryScalar()); + + $c=$builder->createUpdateCounterCommand($table,array('author_id'=>-2),new CDbCriteria(array('condition'=>'id=5'))); + $this->assertEquals('UPDATE \'posts\' SET "author_id"="author_id"-2 WHERE id=5',$c->text); + $c->execute(); + $c=$builder->createSqlCommand('SELECT author_id FROM posts WHERE id=5'); + $this->assertEquals(1,$c->queryScalar()); + + // test bind by position + $c=$builder->createFindCommand($table,new CDbCriteria(array( + 'select'=>'title', + 'condition'=>'id=?', + 'params'=>array(4)))); + $this->assertEquals('SELECT title FROM \'posts\' \'t\' WHERE id=?',$c->text); + $this->assertEquals('post 4',$c->queryScalar()); + + // another bind by position + $c=$builder->createUpdateCommand($table,array('title'=>'new post 4'),new CDbCriteria(array( + 'condition'=>'id=?', + 'params'=>array(4)))); + $c->execute(); + $c=$builder->createSqlCommand('SELECT title FROM posts WHERE id=4'); + $this->assertEquals('new post 4',$c->queryScalar()); + + // testCreateCriteria + $c=$builder->createCriteria('column=:value',array(':value'=>'value')); + $this->assertEquals('column=:value',$c->condition); + $this->assertEquals(array(':value'=>'value'),$c->params); + + $c=$builder->createCriteria(array('condition'=>'column=:value','params'=>array(':value'=>'value'))); + $this->assertEquals('column=:value',$c->condition); + $this->assertEquals(array(':value'=>'value'),$c->params); + + $c2=$builder->createCriteria($c); + $this->assertTrue($c2!==$c); + $this->assertEquals('column=:value',$c2->condition); + $this->assertEquals(array(':value'=>'value'),$c2->params); + + // testCreatePkCriteria + $c=$builder->createPkCriteria($table,1,'author_id>1'); + $this->assertEquals('\'posts\'."id"=1 AND (author_id>1)',$c->condition); + + $c=$builder->createPkCriteria($table,array(1,2)); + $this->assertEquals('\'posts\'."id" IN (1, 2)',$c->condition); + + $c=$builder->createPkCriteria($table,array()); + $this->assertEquals('0=1',$c->condition); + + $table2=$schema->getTable('orders'); + $c=$builder->createPkCriteria($table2,array('key1'=>1,'key2'=>2),'name=\'\''); + $this->assertEquals('\'orders\'."key1"=1 AND \'orders\'."key2"=2 AND (name=\'\')',$c->condition); + + $c=$builder->createPkCriteria($table2,array(array('key1'=>1,'key2'=>2),array('key1'=>3,'key2'=>4))); + $this->assertEquals('\'orders\'."key1"||\',\'||\'orders\'."key2" IN (1||\',\'||2, 3||\',\'||4)',$c->condition); + + // createColumnCriteria + $c=$builder->createColumnCriteria($table,array('id'=>1,'author_id'=>2),'title=\'\''); + $this->assertEquals('\'posts\'."id"=:yp0 AND \'posts\'."author_id"=:yp1 AND (title=\'\')',$c->condition); + + $c=$builder->createPkCriteria($table2,array()); + $this->assertEquals('0=1',$c->condition); + } + + public function testResetSequence() + { + $max=$this->db->createCommand("SELECT MAX(id) FROM users")->queryScalar(); + $this->db->createCommand("DELETE FROM users")->execute(); + $this->db->createCommand("INSERT INTO users (username, password, email) VALUES ('user4','pass4','email4')")->execute(); + $max2=$this->db->createCommand("SELECT MAX(id) FROM users")->queryScalar(); + $this->assertEquals($max+1,$max2); + + $userTable=$this->db->schema->getTable('users'); + + $this->db->createCommand("DELETE FROM users")->execute(); + $this->db->schema->resetSequence($userTable); + $this->db->createCommand("INSERT INTO users (username, password, email) VALUES ('user4','pass4','email4')")->execute(); + $max=$this->db->createCommand("SELECT MAX(id) FROM users")->queryScalar(); + $this->assertEquals(1,$max); + $this->db->createCommand("INSERT INTO users (username, password, email) VALUES ('user4','pass4','email4')")->execute(); + $max=$this->db->createCommand("SELECT MAX(id) FROM users")->queryScalar(); + $this->assertEquals(2,$max); + + $this->db->createCommand("DELETE FROM users")->execute(); + $this->db->schema->resetSequence($userTable,10); + $this->db->createCommand("INSERT INTO users (username, password, email) VALUES ('user4','pass4','email4')")->execute(); + $max=$this->db->createCommand("SELECT MAX(id) FROM users")->queryScalar(); + $this->assertEquals(10,$max); + $this->db->createCommand("INSERT INTO users (username, password, email) VALUES ('user4','pass4','email4')")->execute(); + $max=$this->db->createCommand("SELECT MAX(id) FROM users")->queryScalar(); + $this->assertEquals(11,$max); + } + + public function testCheckIntegrity() + { + $this->db->schema->checkIntegrity(false); + $this->db->schema->checkIntegrity(true); + } +} \ No newline at end of file diff --git a/tests/framework/db/schema/sqlite.sql b/tests/framework/db/schema/sqlite.sql new file mode 100644 index 000000000..9160b0289 --- /dev/null +++ b/tests/framework/db/schema/sqlite.sql @@ -0,0 +1,49 @@ +CREATE TABLE teams +( + id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, + name VARCHAR(128) NOT NULL, + create_time TIMESTAMP NOT NULL, + description TEXT DEFAULT 'not set' +); + +CREATE TABLE players +( + id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, + name VARCHAR(128) NOT NULL, + team_id INTEGER NOT NULL, + CONSTRAINT FK_team_player FOREIGN KEY (team_id) + REFERENCES teams (id) ON DELETE CASCADE ON UPDATE RESTRICT +); + +CREATE TABLE skills +( + id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, + name VARCHAR(128) NOT NULL +); + +CREATE TABLE player_skill +( + player_id INTEGER NOT NULL, + skill_id INTEGER NOT NULL, + level REAL (3,4), + PRIMARY KEY (player_id, skill_id), + CONSTRAINT FK_player_skill_player FOREIGN KEY (player_id) + REFERENCES players (id) ON DELETE CASCADE ON UPDATE RESTRICT, + CONSTRAINT FK_player_skill_skill FOREIGN KEY (skill_id) + REFERENCES skills (id) ON DELETE CASCADE ON UPDATE RESTRICT +); + +CREATE TABLE categories +( + id INTEGER NOT NULL, + sub_id INTEGER NOT NULL +); + +CREATE TABLE products +( + id INTEGER NOT NULL, + cat_id INTEGER NOT NULL, + subcat_id INTEGER NOT NULL, + CONSTRAINT FK_product_categories FOREIGN KEY (cat_id,subcat_id) + REFERENCES categories (id,sub_id) ON DELETE CASCADE ON UPDATE RESTRICT +); \ No newline at end of file diff --git a/tests/framework/i18n/CLocaleTest.php b/tests/framework/i18n/CLocaleTest.php new file mode 100644 index 000000000..6873f014d --- /dev/null +++ b/tests/framework/i18n/CLocaleTest.php @@ -0,0 +1,98 @@ + 'en', + ); + + new TestApplication($config); + Yii::app()->configure($config); + } + + function testRequiredDataExistence(){ + foreach($this->criticalLocaleCodes as $localeCode){ + $locale = Yii::app()->getLocale($localeCode); + // AM/PM + $this->assertNotNull($locale->getAMName(), "$localeCode: getAMName failed."); + $this->assertNotNull($locale->getPMName(), "$localeCode: getPMName failed."); + + // currency + $this->assertNotNull($locale->getCurrencySymbol("USD"), "$localeCode: getCurrencySymbol USD failed."); + $this->assertNotNull($locale->getCurrencySymbol("EUR"), "$localeCode: getCurrencySymbol EUR failed."); + + // numbers + $this->assertNotNull($locale->getNumberSymbol('decimal'), "$localeCode: getNumberSymbol failed."); + $this->assertNotNull($locale->getDecimalFormat(), "$localeCode: getDecimalFormat failed."); + $this->assertNotNull($locale->getCurrencyFormat(), "$localeCode: getCurrencyFormat failed."); + $this->assertNotNull($locale->getPercentFormat(), "$localeCode: getPercentFormat failed."); + $this->assertNotNull($locale->getScientificFormat(), "$localeCode: getScientificFormat failed."); + + // date and time formats + $this->assertNotNull($locale->getMonthName(1), "$localeCode: getMonthName 1 failed."); + $this->assertNotNull($locale->getMonthName(12, 'abbreviated'), "$localeCode: getMonthName 12 abbreviated failed."); + $this->assertNotNull($locale->getMonthName(1, 'narrow', true), "$localeCode: getMonthName 1 narrow standalone failed."); + $this->assertEquals(12, count($locale->getMonthNames()), "$localeCode: getMonthNames failed."); + $this->assertNotNull($locale->getWeekDayName(0), "$localeCode: getWeekDayName failed."); + $this->assertNotNull($locale->getWeekDayNames(), "$localeCode: getWeekDayNames failed."); + $this->assertNotNull($locale->getEraName(1), "$localeCode: getEraName failed."); + $this->assertNotNull($locale->getDateFormat(), "$localeCode: getDateFormat failed."); + $this->assertNotNull($locale->getTimeFormat(), "$localeCode: getTimeFormat failed."); + $this->assertNotNull($locale->getDateTimeFormat(), "$localeCode: getDateTimeFormat failed."); + + // ORIENTATION + $this->assertTrue(in_array($locale->getOrientation(), array('ltr', 'rtl')), "$localeCode: getOrientation failed."); + + // plurals + $l = explode('_', $localeCode); + if(!in_array($l[0], $this->noPlurals)){ + $pluralRules = $locale->getPluralRules(); + $this->assertNotEmpty($pluralRules, $localeCode.": no plural rules"); + } + } + } +} diff --git a/tests/framework/i18n/CNumberFormatterTest.php b/tests/framework/i18n/CNumberFormatterTest.php new file mode 100644 index 000000000..ff5cf49fb --- /dev/null +++ b/tests/framework/i18n/CNumberFormatterTest.php @@ -0,0 +1,70 @@ +usFormatter=new CNumberFormatter('en_us'); + $this->deFormatter=new CNumberFormatter('de'); + } + + public function testFormatCurrency() + { + $numbers=array( + array(0, '$0.00', '0,00 $'), + array(100, '$100.00', '100,00 $'), + array(-100, '($100.00)', '-100,00 $'), + array(100.123, '$100.12', '100,12 $'), + array(100.1, '$100.10', '100,10 $'), + array(100.126, '$100.13', '100,13 $'), + array(1000.126, '$1,000.13', '1.000,13 $'), + array(1000000.123, '$1,000,000.12', '1.000.000,12 $'), + ); + + foreach($numbers as $number) + { + $this->assertEquals($number[1],$this->usFormatter->formatCurrency($number[0],'USD')); + $this->assertEquals($number[2],$this->deFormatter->formatCurrency($number[0],'USD')); + } + } + + public function testFormatDecimal() + { + $numbers=array( + array(0, '0', '0'), + array(100, '100', '100'), + array(-100, '-100', '-100'), + array(100.123, '100.123', '100,123'), + array(100.1, '100.1', '100,1'), + array(100.1206, '100.121', '100,121'), + array(1000.1206, '1,000.121', '1.000,121'), + array(1000000.123, '1,000,000.123', '1.000.000,123'), + ); + + foreach($numbers as $number) + { + $this->assertEquals($number[1],$this->usFormatter->formatDecimal($number[0])); + $this->assertEquals($number[2],$this->deFormatter->formatDecimal($number[0])); + } + } + + public function testFormatPercentage() + { + $numbers=array( + array(0, '0%', '0 %'), + array(0.123, '12%', '12 %'), + array(-0.123, '-12%', '-12 %'), + array(10.12, '1,012%', '1.012 %'), + array(10000.1, '1,000,010%', '1.000.010 %'), + ); + + foreach($numbers as $number) + { + $this->assertEquals($number[1],$this->usFormatter->formatPercentage($number[0])); + $this->assertEquals($number[2],$this->deFormatter->formatPercentage($number[0])); + } + } +} diff --git a/tests/framework/i18n/YiiTTest.php b/tests/framework/i18n/YiiTTest.php new file mode 100644 index 000000000..5d20d4c12 --- /dev/null +++ b/tests/framework/i18n/YiiTTest.php @@ -0,0 +1,186 @@ + 'es', + 'components' => array( + 'messages' => array( + 'class' => 'CPhpMessageSource', + 'basePath' => dirname(__FILE__).'/data', + //'forceTranslation' => true, + ), + ), + ); + + new TestApplication($config); + Yii::app()->configure($config); + } + + // Simple: 'msg' + function testSimple(){ + Yii::app()->setLanguage('ru'); + $this->assertEquals('апельсины', Yii::t('test', 'oranges')); + } + + function testSimpleSameLanguage(){ + Yii::app()->setLanguage('es'); + $this->assertEquals('no_changes', Yii::t('test', 'no_changes')); + } + + function testSimplePlaceholders(){ + Yii::app()->setLanguage('ru'); + $this->assertEquals('сумочки caviar', Yii::t('test', '{brand} bags', array('{brand}' => 'caviar'))); + $this->assertEquals('в корзине: 10', Yii::t('test', 'in the cart: {n}', 10)); + } + + function testSimplePlaceholdersSameLanguage(){ + Yii::app()->setLanguage('es'); + $this->assertEquals('10 changes', Yii::t('test', '{n} changes', 10)); + } + + // Plural: 'msg1|msg2|msg3' + function testPlural(){ + // CLDR + Yii::app()->setLanguage('ru'); + + // array notation + $this->assertEquals('огурец', Yii::t('test', 'cucumber|cucumbers', array(1))); + + //ru + $this->assertEquals('огурец', Yii::t('test', 'cucumber|cucumbers', 1)); + $this->assertEquals('огурец', Yii::t('test', 'cucumber|cucumbers', 101)); + $this->assertEquals('огурец', Yii::t('test', 'cucumber|cucumbers', 51)); + $this->assertEquals('огурца', Yii::t('test', 'cucumber|cucumbers', 2)); + $this->assertEquals('огурца', Yii::t('test', 'cucumber|cucumbers', 62)); + $this->assertEquals('огурца', Yii::t('test', 'cucumber|cucumbers', 104)); + $this->assertEquals('огурцов', Yii::t('test', 'cucumber|cucumbers', 5)); + $this->assertEquals('огурцов', Yii::t('test', 'cucumber|cucumbers', 78)); + $this->assertEquals('огурцов', Yii::t('test', 'cucumber|cucumbers', 320)); + $this->assertEquals('огурцов', Yii::t('test', 'cucumber|cucumbers', 0)); + + // fractions (you should specify fourh variant to use these in Russian) + $this->assertEquals('огурца', Yii::t('test', 'cucumber|cucumbers', 1.5)); + + // en + Yii::app()->setLanguage('en'); + + $this->assertEquals('cucumber', Yii::t('test', 'cucumber|cucumbers', 1)); + $this->assertEquals('cucumbers', Yii::t('test', 'cucumber|cucumbers', 2)); + $this->assertEquals('cucumbers', Yii::t('test', 'cucumber|cucumbers', 0)); + + // short forms + Yii::app()->setLanguage('ru'); + + $this->assertEquals('огурец', Yii::t('test', 'cucumber|cucumbers', 1)); + + // explicit params + $this->assertEquals('огурец', Yii::t('test', 'cucumber|cucumbers', array(0 => 1))); + } + + function testPluralPlaceholders(){ + Yii::app()->setLanguage('ru'); + + $this->assertEquals('1 огурец', Yii::t('test', '{n} cucumber|{n} cucumbers', 1)); + $this->assertEquals('2 огурца', Yii::t('test', '{n} cucumber|{n} cucumbers', 2)); + $this->assertEquals('5 огурцов', Yii::t('test', '{n} cucumber|{n} cucumbers', 5)); + + // more placeholders + $this->assertEquals('+ 5 огурцов', Yii::t('test', '{sign} {n} cucumber|{sign} {n} cucumbers', array(5, '{sign}' => '+'))); + + // placeholder swapping + $this->assertEquals('один огурец', Yii::t('test', '{n} cucumber|{n} cucumbers', array(1, '{n}' => 'один'))); + } + + /** + * If there are useless params in translation just ignore them. + */ + function testPluralMoreVariants(){ + Yii::app()->setLanguage('ru'); + $this->assertEquals('шляпы', Yii::t('test', 'hat|hats', array(2))); + } + + /** + * If there are less variants in translation like + * 'zombie|zombies' => 'зомби' (CLDR requires 3 variants for Russian + * but zombie is too special to be plural) + * + * Same for Chinese but there are no plurals at all. + */ + function testPluralLessVariants(){ + // three variants are required and only one specified (still valid for + // Russian in some special cases) + Yii::app()->setLanguage('ru'); + $this->assertEquals('зомби', Yii::t('test', 'zombie|zombies', 10)); + $this->assertEquals('зомби', Yii::t('test', 'zombie|zombies', 1)); + + // language with no plurals + Yii::app()->setLanguage('zh_cn'); + $this->assertEquals('k-s', Yii::t('test', 'kiss|kisses', 1)); + + // 3 variants are required while only 2 specified + // this one is synthetic but still good to know it at least does not + // produce error + Yii::app()->setLanguage('ru'); + $this->assertEquals('син1', Yii::t('test', 'syn1|syn2|syn3', 1)); + $this->assertEquals('син2', Yii::t('test', 'syn1|syn2|syn3', 2)); + $this->assertEquals('син2', Yii::t('test', 'syn1|syn2|syn3', 5)); + } + + function pluralLessVariantsInSource(){ + // new doesn't have two forms in English + Yii::app()->setLanguage('ru'); + $this->assertEquals('новости', Yii::t('test', 'news', 2)); + } + + function testPluralSameLanguage(){ + Yii::app()->setLanguage('es'); + + $this->assertEquals('cucumbez', Yii::t('test', 'cucumbez|cucumberz', 1)); + $this->assertEquals('cucumberz', Yii::t('test', 'cucumbez|cucumberz', 2)); + $this->assertEquals('cucumberz', Yii::t('test', 'cucumbez|cucumberz', 0)); + } + + function testPluralPlaceholdersSameLanguage(){ + Yii::app()->setLanguage('es'); + + $this->assertEquals('1 cucumbez', Yii::t('test', '{n} cucumbez|{n} cucumberz', 1)); + $this->assertEquals('2 cucumberz', Yii::t('test', '{n} cucumbez|{n} cucumberz', 2)); + $this->assertEquals('5 cucumberz', Yii::t('test', '{n} cucumbez|{n} cucumberz', 5)); + } + + // Choice: 'expr1#msg1|expr2#msg2|expr3#msg3' + function testChoice(){ + Yii::app()->setLanguage('ru'); + + // simple choices + $this->assertEquals('одна книга', Yii::t('test', 'n==1#one book|n>1#many books', 1)); + $this->assertEquals('много книг', Yii::t('test', 'n==1#one book|n>1#many books', 10)); + $this->assertEquals('одна книга', Yii::t('test', '1#one book|n>1#many books', 1)); + $this->assertEquals('много книг', Yii::t('test', '1#one book|n>1#many books', 10)); + } + + function testChoiceSameLanguage(){ + Yii::app()->setLanguage('es'); + + $this->assertEquals('one book', Yii::t('test', 'n==1#one book|n>1#many books', 1)); + $this->assertEquals('many books', Yii::t('test', 'n==1#one book|n>1#many books', 10)); + } + + function testChoicePlaceholders(){ + //$this->assertEquals('51 apples', Yii::t('app', '1#1apple|n>1|{n} apples', array(51, 'n'=>51))); + } + + function testChoicePlaceholdersSameLanguage(){ + + } +} diff --git a/tests/framework/i18n/data/en/test.php b/tests/framework/i18n/data/en/test.php new file mode 100644 index 000000000..deb8c7d68 --- /dev/null +++ b/tests/framework/i18n/data/en/test.php @@ -0,0 +1,4 @@ + 'cucumber|cucumbers', +); \ No newline at end of file diff --git a/tests/framework/i18n/data/ru/test.php b/tests/framework/i18n/data/ru/test.php new file mode 100644 index 000000000..c4a10063b --- /dev/null +++ b/tests/framework/i18n/data/ru/test.php @@ -0,0 +1,15 @@ + 'апельсины', + '{brand} bags' => 'сумочки {brand}', + 'in the cart: {n}' => 'в корзине: {n}', + 'cucumber|cucumbers' => 'огурец|огурца|огурцов|огурца', + '{n} cucumber|{n} cucumbers' => '{n} огурец|{n} огурца|{n} огурцов', + '{sign} {n} cucumber|{sign} {n} cucumbers' => '{sign} {n} огурец|{sign} {n} огурца|{sign} {n} огурцов', + 'zombie|zombies' => 'зомби', + 'hat|hats' => 'шляпа|шляпы|шляп|useless1|useless2', + 'news' => 'новость|новости|новостей', + 'syn1|syn2|syn3' => 'син1|син2', + 'n==1#one book|n>1#many books' => 'n==1#одна книга|n>1#много книг', + '1#one book|n>1#many books' => '1#одна книга|n>1#много книг', +); \ No newline at end of file diff --git a/tests/framework/i18n/data/zh_cn/test.php b/tests/framework/i18n/data/zh_cn/test.php new file mode 100644 index 000000000..d8c67d635 --- /dev/null +++ b/tests/framework/i18n/data/zh_cn/test.php @@ -0,0 +1,4 @@ + 'k-s', +); \ No newline at end of file diff --git a/tests/framework/utils/CDateTimeParserTest.php b/tests/framework/utils/CDateTimeParserTest.php new file mode 100644 index 000000000..011965c38 --- /dev/null +++ b/tests/framework/utils/CDateTimeParserTest.php @@ -0,0 +1,11 @@ +assertEquals( + '31-12-2011 23:59:59', + date('d-m-Y H:i:s', CDateTimeParser::parse('2011-12-31', 'yyyy-MM-dd', array('hour' => 23, 'minute' => 59, 'second' => 59))) + ); + } +} diff --git a/tests/framework/validators/CEmailValidatorTest.php b/tests/framework/validators/CEmailValidatorTest.php new file mode 100644 index 000000000..b0750c7b3 --- /dev/null +++ b/tests/framework/validators/CEmailValidatorTest.php @@ -0,0 +1,12 @@ +validate(array('email')); + $this->assertArrayHasKey('email', $model->getErrors()); + } +} \ No newline at end of file diff --git a/tests/framework/validators/ValidatorTestModel.php b/tests/framework/validators/ValidatorTestModel.php new file mode 100644 index 000000000..e47912ba3 --- /dev/null +++ b/tests/framework/validators/ValidatorTestModel.php @@ -0,0 +1,12 @@ + false), + ); + } +} diff --git a/tests/framework/web/CAssetManagerTest.php b/tests/framework/web/CAssetManagerTest.php new file mode 100644 index 000000000..1d82a1904 --- /dev/null +++ b/tests/framework/web/CAssetManagerTest.php @@ -0,0 +1,42 @@ +reset(); + $am2->init($app); + $this->assertEquals($am2->basePath,$app->basePath.DIRECTORY_SEPARATOR.'assets'); + } + + public function testBaseUrl() + { + $app=new TestApplication; + $app->request->baseUrl='/test'; + $am=new CAssetManager; + $am->init($app); + $this->assertEquals($am->baseUrl,'/test/assets'); + } + + public function testPublishFile() + { + $app=new TestApplication; + $app->reset(); + $am=new CAssetManager; + $am->init($app); + $path1=$am->getPublishedPath(__FILE__); + clearstatcache(); + $this->assertFalse(is_file($path1)); + $url=$am->publish(__FILE__); + $path2=$am->getPublishedPath(__FILE__); + $this->assertEquals($path1,$path2); + clearstatcache(); + $this->assertTrue(is_file($path1)); + $this->assertEquals(basename($path1),basename(__FILE__)); + $this->assertEquals($url,$am->baseUrl.'/'.basename(dirname($path2)).'/'.basename($path2)); + } +} diff --git a/tests/framework/web/CControllerTest.php b/tests/framework/web/CControllerTest.php new file mode 100644 index 000000000..c31f25c56 --- /dev/null +++ b/tests/framework/web/CControllerTest.php @@ -0,0 +1,187 @@ +300, + ), + 'filter3 - internal', + ); + } + + public function actions() + { + return array( + 'external'=>'TestAction', + ); + } + + public function missingAction($actionName) + { + throw new CException('test missing'); + } + + public function filterFilter1($chain) + { + $chain->run(); + $this->internalFilter1++; + } + + public function filterFilter2($chain) + { + $this->internalFilter2++; + $chain->run(); + } + + public function filterFilter3($chain) + { + $this->internalFilter3++; + $chain->run(); + } + + public function actionInternal() + { + $this->internal++; + } + + public $a; + public $b; + public $c; + public $d; + + public function actionCreate($a,$b,$c=3,$d=4) + { + $this->a=$a; + $this->b=$b; + $this->c=$c; + $this->d=$d; + } +} + +class TestFilter extends CFilter +{ + public $expire=0; + public function filter($chain) + { + if($chain->controller->externalFilter<=1) + { + $chain->controller->externalFilter++; + $chain->run(); + } + } +} + +class TestAction extends CAction +{ + public function run() + { + $this->controller->external++; + } +} + +class CControllerTest extends CTestCase +{ + public function testDefaultProperties() + { + $app=new TestApplication; + $_SERVER['REQUEST_METHOD']='GET'; + $c=new CController('test/subtest'); + $this->assertEquals($c->id,'test/subtest'); + $this->assertEquals($c->filters(),array()); + $this->assertEquals($c->actions(),array()); + $this->assertNull($c->action); + $this->assertEquals($c->defaultAction,'index'); + $this->assertEquals($c->viewPath,$app->viewPath.'/test/subtest'); + $this->setExpectedException('CHttpException'); + $c->missingAction('index'); + } + + public function testRunAction() + { + $app=new TestApplication; + $c=new TestController('test'); + $this->assertEquals($c->internal,0); + $this->assertEquals($c->external,0); + $this->assertEquals($c->internalFilter1,0); + $this->assertEquals($c->internalFilter2,0); + $this->assertEquals($c->internalFilter3,0); + $this->assertEquals($c->externalFilter,0); + + $c->run(''); + $this->assertEquals($c->internal,0); + $this->assertEquals($c->external,1); + $this->assertEquals($c->internalFilter1,1); + $this->assertEquals($c->internalFilter2,0); + $this->assertEquals($c->internalFilter3,1); + $this->assertEquals($c->externalFilter,1); + + $c->run('internal'); + $this->assertEquals($c->internal,1); + $this->assertEquals($c->external,1); + $this->assertEquals($c->internalFilter1,2); + $this->assertEquals($c->internalFilter2,1); + $this->assertEquals($c->internalFilter3,1); + $this->assertEquals($c->externalFilter,2); + + $c->run('external'); + $this->assertEquals($c->internal,1); + $this->assertEquals($c->external,1); + $this->assertEquals($c->internalFilter1,3); + $this->assertEquals($c->internalFilter2,1); + $this->assertEquals($c->internalFilter3,1); + $this->assertEquals($c->externalFilter,2); + + $this->setExpectedException('CException'); + $c->run('unknown'); + } + + public function testActionParams() + { + $app=new TestApplication; + $c=new TestController('test'); + + $_GET['a']=1; + $_GET['b']='2'; + $c->run('create'); + $this->assertTrue($c->a===1); + $this->assertTrue($c->b==='2'); + $this->assertTrue($c->c===3); + $this->assertTrue($c->d===4); + + $_GET['a']=11; + $_GET['b']='22'; + $_GET['d']='44'; + $c->run('create'); + $this->assertTrue($c->a===11); + $this->assertTrue($c->b==='22'); + $this->assertTrue($c->c===3); + $this->assertTrue($c->d==='44'); + } + + public function testActionParamsInvalid() + { + $app=new TestApplication; + $c=new TestController('test'); + $_GET=array('a'=>1); + $this->setExpectedException('CException'); + $c->run('create'); + } +} diff --git a/tests/framework/web/CSortTest.php b/tests/framework/web/CSortTest.php new file mode 100644 index 000000000..044cfd29a --- /dev/null +++ b/tests/framework/web/CSortTest.php @@ -0,0 +1,84 @@ +markTestSkipped('PDO and SQLite extensions are required.'); + + $this->db=new CDbConnection('sqlite::memory:'); + $this->db->active=true; + $this->db->pdoInstance->exec(file_get_contents(dirname(__FILE__).'/CSortTest.sql')); + CActiveRecord::$db=$this->db; + } + + public function tearDown(){ + $this->db->active=false; + } + + /** + * Tests for acceptance of fields with dots in + * CSort::attributes. + * + * @return void + */ + function testGetDirectionsWithDots(){ + $_GET['sort'] = 'comments.id'; + + $criteria = new CDbCriteria(); + $criteria->with = 'comments'; + + $sort = new CSort('Post'); + $sort->attributes = array( + 'id', + 'comments.id' => array( + 'asc'=>'comments.id', + 'desc'=>'comments.id desc', + ), + ); + $sort->applyOrder($criteria); + $directions = $sort->getDirections(); + + $this->assertTrue(isset($directions['comments.id'])); + } +} + + +class TestPost extends CActiveRecord { + public static function model($className=__CLASS__) { + return parent::model($className); + } + + public function tableName() { + return 'post'; + } + + public function relations() { + return array( + 'comments'=>array(self::HAS_MANY, 'TestComment', 'post_id'), + ); + } +} + +class TestComment extends CActiveRecord { + public static function model($className=__CLASS__) { + return parent::model($className); + } + + public function tableName() { + return 'comment'; + } + + public function relations() { + return array( + 'post'=>array(self::BELONGS_TO, 'TestPost', 'post_id'), + ); + } +} \ No newline at end of file diff --git a/tests/framework/web/CSortTest.sql b/tests/framework/web/CSortTest.sql new file mode 100644 index 000000000..7e168b8fd --- /dev/null +++ b/tests/framework/web/CSortTest.sql @@ -0,0 +1,12 @@ +CREATE TABLE comment ( + id INTEGER PRIMARY KEY, + post_id INTEGER, + text text, + created_at INTEGER +); + +CREATE TABLE post ( + id INTEGER PRIMARY KEY, + created_at INTEGER, + title VARCHAR(255) +); \ No newline at end of file diff --git a/tests/framework/web/CUrlManagerTest.php b/tests/framework/web/CUrlManagerTest.php new file mode 100644 index 000000000..42f8cb3a4 --- /dev/null +++ b/tests/framework/web/CUrlManagerTest.php @@ -0,0 +1,559 @@ +myScriptUrl; + } + + public function setScriptUrl($value) + { + $this->myScriptUrl=$value; + } + + public function getPathInfo() + { + return $this->myPathInfo; + } + + public function setPathInfo($value) + { + $this->myPathInfo=$value; + } +} + + +class CUrlManagerTest extends CTestCase +{ + public function testParseUrlWithPathFormat() + { + $rules=array( + 'article/'=>'article/read', + 'article///*'=>'article/read', + 'a/<_a>/*'=>'article', + 'register/*'=>'user', + 'home/*'=>'', + 'ad/*'=>'admin/index/list', + '<c:(post|comment)>/<id:\d+>/<a:(create|update|delete)>'=>'<c>/<a>', + '<c:(post|comment)>/<id:\d+>'=>'<c>/view', + '<c:(post|comment)>s/*'=>'<c>/list', + 'http://<user:\w+>.example.com/<lang:\w+>/profile'=>'user/profile', + ); + $entries=array( + array( + 'pathInfo'=>'article/123', + 'route'=>'article/read', + 'params'=>array('id'=>'123'), + ), + array( + 'pathInfo'=>'article/123/name/value', + 'route'=>'article/123/name/value', + 'params'=>array(), + ), + array( + 'pathInfo'=>'article/2000/title goes here', + 'route'=>'article/read', + 'params'=>array('year'=>'2000','title'=>'title goes here'), + ), + array( + 'pathInfo'=>'article/2000/title goes here/name/value', + 'route'=>'article/read', + 'params'=>array('year'=>'2000','title'=>'title goes here','name'=>'value'), + ), + array( + 'pathInfo'=>'register/username/admin', + 'route'=>'user', + 'params'=>array('username'=>'admin'), + ), + array( + 'pathInfo'=>'home/name/value/name1/value1', + 'route'=>'', + 'params'=>array('name'=>'value','name1'=>'value1'), + ), + array( + 'pathInfo'=>'home2/name/value/name1/value1', + 'route'=>'home2/name/value/name1/value1', + 'params'=>array(), + ), + array( + 'pathInfo'=>'post', + 'route'=>'post', + 'params'=>array(), + ), + array( + 'pathInfo'=>'post/read', + 'route'=>'post/read', + 'params'=>array(), + ), + array( + 'pathInfo'=>'post/read/id/100', + 'route'=>'post/read/id/100', + 'params'=>array(), + ), + array( + 'pathInfo'=>'', + 'route'=>'', + 'params'=>array(), + ), + array( + 'pathInfo'=>'ad/name/value', + 'route'=>'admin/index/list', + 'params'=>array('name'=>'value'), + ), + array( + 'pathInfo'=>'admin/name/value', + 'route'=>'admin/name/value', + 'params'=>array(), + ), + array( + 'pathInfo'=>'posts', + 'route'=>'post/list', + 'params'=>array(), + ), + array( + 'pathInfo'=>'posts/page/3', + 'route'=>'post/list', + 'params'=>array('page'=>3), + ), + array( + 'pathInfo'=>'post/3', + 'route'=>'post/view', + 'params'=>array('id'=>3), + ), + array( + 'pathInfo'=>'post/3/delete', + 'route'=>'post/delete', + 'params'=>array('id'=>3), + ), + array( + 'pathInfo'=>'post/3/delete/a', + 'route'=>'post/3/delete/a', + 'params'=>array(), + ), + array( + 'pathInfo'=>'en/profile', + 'route'=>'user/profile', + 'params'=>array('user'=>'admin','lang'=>'en'), + ), + ); + $config=array( + 'basePath'=>dirname(__FILE__), + 'components'=>array( + 'request'=>array( + 'class'=>'TestHttpRequest', + 'scriptUrl'=>'/app/index.php', + ), + ), + ); + $app=new TestApplication($config); + $app->controllerPath=dirname(__FILE__).DIRECTORY_SEPARATOR.'controllers'; + $request=$app->request; + $_SERVER['HTTP_HOST']='admin.example.com'; + $um=new CUrlManager; + $um->urlSuffix='.html'; + $um->urlFormat='path'; + $um->rules=$rules; + $um->init($app); + foreach($entries as $entry) + { + $request->pathInfo=$entry['pathInfo']; + $_GET=array(); + $route=$um->parseUrl($request); + $this->assertEquals($entry['route'],$route); + $this->assertEquals($entry['params'],$_GET); + // test the .html version + $request->pathInfo=$entry['pathInfo'].'.html'; + $_GET=array(); + $route=$um->parseUrl($request); + $this->assertEquals($entry['route'],$route); + $this->assertEquals($entry['params'],$_GET); + } + } + + public function testcreateUrlWithPathFormat() + { + $rules=array( + 'article/<id:\d+>'=>'article/read', + 'article/<year:\d{4}>/<title>/*'=>'article/read', + 'a/<_a>/*'=>'article', + 'register/*'=>'user', + 'home/*'=>'', + '<c:(post|comment)>/<id:\d+>/<a:(create|update|delete)>'=>'<c>/<a>', + '<c:(post|comment)>/<id:\d+>'=>'<c>/view', + '<c:(post|comment)>s/*'=>'<c>/list', + 'http://<user:\w+>.example.com/<lang:\w+>/profile'=>'user/profile', + ); + $config=array( + 'basePath'=>dirname(__FILE__), + 'components'=>array( + 'request'=>array( + 'class'=>'TestHttpRequest', + ), + ), + ); + $_SERVER['HTTP_HOST']='user.example.com'; + $app=new TestApplication($config); + $entries=array( + array( + 'scriptUrl'=>'/apps/index.php', + 'url'=>'/apps/index.php/post/123?name1=value1', + 'url2'=>'/apps/post/123?name1=value1', + 'url3'=>'/apps/post/123.html?name1=value1', + 'route'=>'post/view', + 'params'=>array( + 'id'=>'123', + 'name1'=>'value1', + ), + ), + array( + 'scriptUrl'=>'/apps/index.php', + 'url'=>'/apps/index.php/post/123/update?name1=value1', + 'url2'=>'/apps/post/123/update?name1=value1', + 'url3'=>'/apps/post/123/update.html?name1=value1', + 'route'=>'post/update', + 'params'=>array( + 'id'=>'123', + 'name1'=>'value1', + ), + ), + array( + 'scriptUrl'=>'/apps/index.php', + 'url'=>'/apps/index.php/posts/page/123', + 'url2'=>'/apps/posts/page/123', + 'url3'=>'/apps/posts/page/123.html', + 'route'=>'post/list', + 'params'=>array( + 'page'=>'123', + ), + ), + array( + 'scriptUrl'=>'/apps/index.php', + 'url'=>'/apps/index.php/article/123?name1=value1', + 'url2'=>'/apps/article/123?name1=value1', + 'url3'=>'/apps/article/123.html?name1=value1', + 'route'=>'article/read', + 'params'=>array( + 'id'=>'123', + 'name1'=>'value1', + ), + ), + array( + 'scriptUrl'=>'/index.php', + 'url'=>'/index.php/article/123?name1=value1', + 'url2'=>'/article/123?name1=value1', + 'url3'=>'/article/123.html?name1=value1', + 'route'=>'article/read', + 'params'=>array( + 'id'=>'123', + 'name1'=>'value1', + ), + ), + array( + 'scriptUrl'=>'/apps/index.php', + 'url'=>'/apps/index.php/article/2000/the_title/name1/value1', + 'url2'=>'/apps/article/2000/the_title/name1/value1', + 'url3'=>'/apps/article/2000/the_title/name1/value1.html', + 'route'=>'article/read', + 'params'=>array( + 'year'=>'2000', + 'title'=>'the_title', + 'name1'=>'value1', + ), + ), + array( + 'scriptUrl'=>'/index.php', + 'url'=>'/index.php/article/2000/the_title/name1/value1', + 'url2'=>'/article/2000/the_title/name1/value1', + 'url3'=>'/article/2000/the_title/name1/value1.html', + 'route'=>'article/read', + 'params'=>array( + 'year'=>'2000', + 'title'=>'the_title', + 'name1'=>'value1', + ), + ), + array( + 'scriptUrl'=>'/apps/index.php', + 'url'=>'/apps/index.php/post/edit/id/123/name1/value1', + 'url2'=>'/apps/post/edit/id/123/name1/value1', + 'url3'=>'/apps/post/edit/id/123/name1/value1.html', + 'route'=>'post/edit', + 'params'=>array( + 'id'=>'123', + 'name1'=>'value1', + ), + ), + array( + 'scriptUrl'=>'/index.php', + 'url'=>'http://admin.example.com/en/profile', + 'url2'=>'http://admin.example.com/en/profile', + 'url3'=>'http://admin.example.com/en/profile.html', + 'route'=>'user/profile', + 'params'=>array( + 'user'=>'admin', + 'lang'=>'en', + ), + ), + array( + 'scriptUrl'=>'/index.php', + 'url'=>'/en/profile', + 'url2'=>'/en/profile', + 'url3'=>'/en/profile.html', + 'route'=>'user/profile', + 'params'=>array( + 'user'=>'user', + 'lang'=>'en', + ), + ), + ); + foreach($entries as $entry) + { + $app->request->baseUrl=null; // reset so that it can be determined based on scriptUrl + $app->request->scriptUrl=$entry['scriptUrl']; + $um=new CUrlManager; + $um->urlFormat='path'; + $um->rules=$rules; + $um->init($app); + $url=$um->createUrl($entry['route'],$entry['params']); + $this->assertEquals($entry['url'],$url); + + $um=new CUrlManager; + $um->urlFormat='path'; + $um->rules=$rules; + $um->init($app); + $um->showScriptName=false; + $url=$um->createUrl($entry['route'],$entry['params']); + $this->assertEquals($entry['url2'],$url); + + $um->urlSuffix='.html'; + $url=$um->createUrl($entry['route'],$entry['params']); + $this->assertEquals($entry['url3'],$url); + } + } + + public function testParseUrlWithGetFormat() + { + $config=array( + 'basePath'=>dirname(__FILE__), + 'components'=>array( + 'request'=>array( + 'class'=>'TestHttpRequest', + 'scriptUrl'=>'/app/index.php', + ), + ), + ); + $entries=array( + array( + 'route'=>'article/read', + 'name'=>'value', + ), + ); + $app=new TestApplication($config); + $request=$app->request; + $um=new CUrlManager; + $um->urlFormat='get'; + $um->routeVar='route'; + $um->init($app); + foreach($entries as $entry) + { + $_GET=$entry; + $route=$um->parseUrl($request); + $this->assertEquals($entry['route'],$route); + $this->assertEquals($_GET,$entry); + } + } + + public function testCreateUrlWithGetFormat() + { + $config=array( + 'basePath'=>dirname(__FILE__), + 'components'=>array( + 'request'=>array( + 'class'=>'TestHttpRequest', + ), + ), + ); + $app=new TestApplication($config); + $entries=array( + array( + 'scriptUrl'=>'/apps/index.php', + 'url'=>'/apps/index.php?route=article/read&name=value&name1=value1', + 'url2'=>'/apps/?route=article/read&name=value&name1=value1', + 'route'=>'article/read', + 'params'=>array( + 'name'=>'value', + 'name1'=>'value1', + ), + ), + array( + 'scriptUrl'=>'/index.php', + 'url'=>'/index.php?route=article/read&name=value&name1=value1', + 'url2'=>'/?route=article/read&name=value&name1=value1', + 'route'=>'article/read', + 'params'=>array( + 'name'=>'value', + 'name1'=>'value1', + ), + ), + ); + foreach($entries as $entry) + { + $app->request->baseUrl=null; + $app->request->scriptUrl=$entry['scriptUrl']; + $um=new CUrlManager; + $um->urlFormat='get'; + $um->routeVar='route'; + $um->init($app); + $url=$um->createUrl($entry['route'],$entry['params'],'&'); + $this->assertEquals($url,$entry['url']); + + $um=new CUrlManager; + $um->urlFormat='get'; + $um->routeVar='route'; + $um->showScriptName=false; + $um->init($app); + $url=$um->createUrl($entry['route'],$entry['params'],'&'); + $this->assertEquals($url,$entry['url2']); + } + } + + public function testDefaultParams() + { + $config=array( + 'basePath'=>dirname(__FILE__), + 'components'=>array( + 'request'=>array( + 'class'=>'TestHttpRequest', + ), + ), + ); + $app=new TestApplication($config); + + $app->request->baseUrl=null; // reset so that it can be determined based on scriptUrl + $app->request->scriptUrl='/apps/index.php'; + $um=new CUrlManager; + $um->urlFormat='path'; + $um->rules=array( + ''=>array('site/page', 'defaultParams'=>array('view'=>'about')), + 'posts'=>array('post/index', 'defaultParams'=>array('page'=>1)), + '<slug:[0-9a-z-]+>' => array('news/list', 'defaultParams' => array('page' => 1)), + ); + $um->init($app); + + $url=$um->createUrl('site/page',array('view'=>'about')); + $this->assertEquals('/apps/index.php/',$url); + $app->request->pathInfo=''; + $_GET=array(); + $route=$um->parseUrl($app->request); + $this->assertEquals('site/page',$route); + $this->assertEquals(array('view'=>'about'),$_GET); + + $url=$um->createUrl('post/index',array('page'=>1)); + $this->assertEquals('/apps/index.php/posts',$url); + $app->request->pathInfo='posts'; + $_GET=array(); + $route=$um->parseUrl($app->request); + $this->assertEquals('post/index',$route); + $this->assertEquals(array('page'=>'1'),$_GET); + + $url=$um->createUrl('news/list', array('slug' => 'example', 'page' => 1)); + $this->assertEquals('/apps/index.php/example',$url); + $app->request->pathInfo='example'; + $_GET=array(); + $route=$um->parseUrl($app->request); + $this->assertEquals('news/list',$route); + $this->assertEquals(array('slug'=>'example', 'page'=>'1'),$_GET); + } + + public function testVerb() + { + $config=array( + 'basePath'=>dirname(__FILE__), + 'components'=>array( + 'request'=>array( + 'class'=>'TestHttpRequest', + ), + ), + ); + $rules=array( + 'article/<id:\d+>'=>array('article/read', 'verb'=>'GET'), + 'article/update/<id:\d+>'=>array('article/update', 'verb'=>'POST'), + 'article/update/*'=>'article/admin', + ); + + $entries=array( + array( + 'scriptUrl'=>'/apps/index.php', + 'url'=>'article/123', + 'verb'=>'GET', + 'route'=>'article/read', + ), + array( + 'scriptUrl'=>'/apps/index.php', + 'url'=>'article/update/123', + 'verb'=>'POST', + 'route'=>'article/update', + ), + array( + 'scriptUrl'=>'/apps/index.php', + 'url'=>'article/update/123', + 'verb'=>'GET', + 'route'=>'article/admin', + ), + ); + + foreach($entries as $entry) + { + $_SERVER['REQUEST_METHOD']=$entry['verb']; + $app=new TestApplication($config); + $app->request->baseUrl=null; // reset so that it can be determined based on scriptUrl + $app->request->scriptUrl=$entry['scriptUrl']; + $app->request->pathInfo=$entry['url']; + $um=new CUrlManager; + $um->urlFormat='path'; + $um->rules=$rules; + $um->init($app); + $route=$um->parseUrl($app->request); + $this->assertEquals($entry['route'],$route); + } + } + + public function testParsingOnly() + { + $config=array( + 'basePath'=>dirname(__FILE__), + 'components'=>array( + 'request'=>array( + 'class'=>'TestHttpRequest', + ), + ), + ); + $rules=array( + '(articles|article)/<id:\d+>'=>array('article/read', 'parsingOnly'=>true), + 'article/<id:\d+>'=>array('article/read', 'verb'=>'GET'), + ); + + $_SERVER['REQUEST_METHOD']='GET'; + $app=new TestApplication($config); + $app->request->baseUrl=null; // reset so that it can be determined based on scriptUrl + $app->request->scriptUrl='/apps/index.php'; + $app->request->pathInfo='articles/123'; + $um=new CUrlManager; + $um->urlFormat='path'; + $um->rules=$rules; + $um->init($app); + + $route=$um->parseUrl($app->request); + $this->assertEquals('article/read',$route); + + $url=$um->createUrl('article/read', array('id'=>345)); + $this->assertEquals('/apps/index.php/article/345',$url); + } +} diff --git a/tests/framework/web/auth/AuthManagerTestBase.php b/tests/framework/web/auth/AuthManagerTestBase.php new file mode 100644 index 000000000..e11686090 --- /dev/null +++ b/tests/framework/web/auth/AuthManagerTestBase.php @@ -0,0 +1,243 @@ +<?php + +abstract class AuthManagerTestBase extends CTestCase +{ + protected $auth; + + public function testcreateAuthItem() + { + $type=CAuthItem::TYPE_TASK; + $name='editUser'; + $description='edit a user'; + $bizRule='checkUserIdentity()'; + $data=array(1,2,3); + $item=$this->auth->createAuthItem($name,$type,$description,$bizRule,$data); + $this->assertTrue($item instanceof CAuthItem); + $this->assertEquals($item->type,$type); + $this->assertEquals($item->name,$name); + $this->assertEquals($item->description,$description); + $this->assertEquals($item->bizRule,$bizRule); + $this->assertEquals($item->data,$data); + + // test shortcut + $name2='createUser'; + $item2=$this->auth->createRole($name2,$description,$bizRule,$data); + $this->assertEquals($item2->type,CAuthItem::TYPE_ROLE); + + // test adding an item with the same name + $this->setExpectedException('CException'); + $this->auth->createAuthItem($name,$type,$description,$bizRule,$data); + } + + public function testGetAuthItem() + { + $this->assertTrue($this->auth->getAuthItem('readPost') instanceof CAuthItem); + $this->assertTrue($this->auth->getAuthItem('reader') instanceof CAuthItem); + $this->assertNull($this->auth->getAuthItem('unknown')); + } + + public function testRemoveAuthItem() + { + $this->assertTrue($this->auth->getAuthItem('updatePost') instanceof CAuthItem); + $this->assertTrue($this->auth->removeAuthItem('updatePost')); + $this->assertNull($this->auth->getAuthItem('updatePost')); + $this->assertFalse($this->auth->removeAuthItem('updatePost')); + } + + public function testChangeItemName() + { + $item=$this->auth->getAuthItem('readPost'); + $this->assertTrue($item instanceof CAuthItem); + $this->assertTrue($this->auth->hasItemChild('reader','readPost')); + $item->name='readPost2'; + $this->assertNull($this->auth->getAuthItem('readPost')); + $this->assertEquals($this->auth->getAuthItem('readPost2'),$item); + $this->assertFalse($this->auth->hasItemChild('reader','readPost')); + $this->assertTrue($this->auth->hasItemChild('reader','readPost2')); + } + + public function testAddItemChild() + { + $this->auth->addItemChild('createPost','updatePost'); + + // test adding upper level item to lower one + $this->setExpectedException('CException'); + $this->auth->addItemChild('readPost','reader'); + } + + public function testAddItemChild2() + { + // test adding inexistent items + $this->setExpectedException('CException'); + $this->assertFalse($this->auth->addItemChild('createPost2','updatePost')); + } + + public function testRemoveItemChild() + { + $this->assertTrue($this->auth->hasItemChild('reader','readPost')); + $this->assertTrue($this->auth->removeItemChild('reader','readPost')); + $this->assertFalse($this->auth->hasItemChild('reader','readPost')); + $this->assertFalse($this->auth->removeItemChild('reader','readPost')); + } + + public function testGetItemChildren() + { + $this->assertEquals(array(),$this->auth->getItemChildren('readPost')); + $children=$this->auth->getItemChildren('author'); + $this->assertEquals(3,count($children)); + $this->assertTrue(reset($children) instanceof CAuthItem); + } + + public function testAssign() + { + $auth=$this->auth->assign('createPost','new user','rule','data'); + $this->assertTrue($auth instanceof CAuthAssignment); + $this->assertEquals($auth->userId,'new user'); + $this->assertEquals($auth->itemName,'createPost'); + $this->assertEquals($auth->bizRule,'rule'); + $this->assertEquals($auth->data,'data'); + + $this->setExpectedException('CException'); + $this->auth->assign('createPost2','new user','rule','data'); + } + + public function testRevoke() + { + $this->assertTrue($this->auth->isAssigned('author','author B')); + $auth=$this->auth->getAuthAssignment('author','author B'); + $this->assertTrue($auth instanceof CAuthAssignment); + $this->assertTrue($this->auth->revoke('author','author B')); + $this->assertFalse($this->auth->isAssigned('author','author B')); + $this->assertFalse($this->auth->revoke('author','author B')); + } + + public function testGetAuthAssignments() + { + $this->auth->assign('deletePost','author B'); + $auths=$this->auth->getAuthAssignments('author B'); + $this->assertEquals(2,count($auths)); + $this->assertTrue(reset($auths) instanceof CAuthAssignment); + } + + public function testGetAuthItems() + { + $this->assertEquals(count($this->auth->getRoles()),4); + $this->assertEquals(count($this->auth->getOperations()),4); + $this->assertEquals(count($this->auth->getTasks()),1); + $this->assertEquals(count($this->auth->getAuthItems()),9); + + $this->assertEquals(count($this->auth->getAuthItems(null,'author B')),1); + $this->assertEquals(count($this->auth->getAuthItems(null,'author C')),0); + $this->assertEquals(count($this->auth->getAuthItems(CAuthItem::TYPE_ROLE,'author B')),1); + $this->assertEquals(count($this->auth->getAuthItems(CAuthItem::TYPE_OPERATION,'author B')),0); + } + + public function testClearAll() + { + $this->auth->clearAll(); + $this->assertEquals(count($this->auth->getRoles()),0); + $this->assertEquals(count($this->auth->getOperations()),0); + $this->assertEquals(count($this->auth->getTasks()),0); + $this->assertEquals(count($this->auth->getAuthItems()),0); + $this->assertEquals(count($this->auth->getAuthAssignments('author B')),0); + } + + public function testClearAuthAssignments() + { + $this->auth->clearAuthAssignments(); + $this->assertEquals(count($this->auth->getAuthAssignments('author B')),0); + } + + public function testDetectLoop() + { + $this->setExpectedException('CException'); + $this->auth->addItemChild('readPost','readPost'); + } + + public function testExecuteBizRule() + { + $this->assertTrue($this->auth->executeBizRule(null,array(),null)); + $this->assertTrue($this->auth->executeBizRule('return 1==true;',array(),null)); + $this->assertTrue($this->auth->executeBizRule('return $params[0]==$params[1];',array(1,'1'),null)); + $this->assertFalse($this->auth->executeBizRule('invalid',array(),null)); + } + + public function testCheckAccess() + { + $results=array( + 'reader A'=>array( + 'createPost'=>false, + 'readPost'=>true, + 'updatePost'=>false, + 'updateOwnPost'=>false, + 'deletePost'=>false, + ), + 'author B'=>array( + 'createPost'=>true, + 'readPost'=>true, + 'updatePost'=>true, + 'updateOwnPost'=>true, + 'deletePost'=>false, + ), + 'editor C'=>array( + 'createPost'=>false, + 'readPost'=>true, + 'updatePost'=>true, + 'updateOwnPost'=>false, + 'deletePost'=>false, + ), + 'admin D'=>array( + 'createPost'=>true, + 'readPost'=>true, + 'updatePost'=>true, + 'updateOwnPost'=>false, + 'deletePost'=>true, + ), + ); + + $params=array('authorID'=>'author B'); + + foreach(array('reader A','author B','editor C','admin D') as $user) + { + $params['userID']=$user; + foreach(array('createPost','readPost','updatePost','updateOwnPost','deletePost') as $operation) + { + $result=$this->auth->checkAccess($operation,$user,$params); + $this->assertEquals($results[$user][$operation],$result); + } + } + } + + protected function prepareData() + { + $this->auth->createOperation('createPost','create a post'); + $this->auth->createOperation('readPost','read a post'); + $this->auth->createOperation('updatePost','update a post'); + $this->auth->createOperation('deletePost','delete a post'); + + $task=$this->auth->createTask('updateOwnPost','update a post by author himself','return $params["authorID"]==$params["userID"];'); + $task->addChild('updatePost'); + + $role=$this->auth->createRole('reader'); + $role->addChild('readPost'); + + $role=$this->auth->createRole('author'); + $role->addChild('reader'); + $role->addChild('createPost'); + $role->addChild('updateOwnPost'); + + $role=$this->auth->createRole('editor'); + $role->addChild('reader'); + $role->addChild('updatePost'); + + $role=$this->auth->createRole('admin'); + $role->addChild('editor'); + $role->addChild('author'); + $role->addChild('deletePost'); + + $this->auth->assign('reader','reader A'); + $this->auth->assign('author','author B'); + $this->auth->assign('editor','editor C'); + $this->auth->assign('admin','admin D'); + } +} diff --git a/tests/framework/web/auth/CDbAuthManagerTest.php b/tests/framework/web/auth/CDbAuthManagerTest.php new file mode 100644 index 000000000..71a90ad10 --- /dev/null +++ b/tests/framework/web/auth/CDbAuthManagerTest.php @@ -0,0 +1,45 @@ +<?php + +require_once(dirname(__FILE__).'/AuthManagerTestBase.php'); + +class CDbAuthManagerTest extends AuthManagerTestBase +{ + private $db; + + public function setUp() + { + if(!extension_loaded('pdo') || !extension_loaded('pdo_pgsql')) + $this->markTestSkipped('PDO and PostgreSQL extensions are required.'); + + $schemaFile=realpath(dirname(__FILE__).'/schema.sql'); + + $this->db=new CDbConnection('pgsql:host=localhost;dbname=yii','test','test'); + try + { + $this->db->active=true; + } + catch(Exception $e) + { + $this->markTestSkipped("Please read $schemaFile for details on setting up the test environment for PostgreSQL test case."); + } + + $sqls=file_get_contents($schemaFile); + foreach(explode(';',$sqls) as $sql) + { + if(trim($sql)!=='') + $this->db->createCommand($sql)->execute(); + } + $this->db->active=false; + + $this->auth=new CDbAuthManager; + $this->auth->db=$this->db; + $this->auth->init(); + $this->prepareData(); + } + + public function tearDown() + { + if($this->db) + $this->db->active=false; + } +} diff --git a/tests/framework/web/auth/CPhpAuthManagerTest.php b/tests/framework/web/auth/CPhpAuthManagerTest.php new file mode 100644 index 000000000..fdcb8ab8c --- /dev/null +++ b/tests/framework/web/auth/CPhpAuthManagerTest.php @@ -0,0 +1,29 @@ +<?php + +require_once(dirname(__FILE__).'/AuthManagerTestBase.php'); + +class CPhpAuthManagerTest extends AuthManagerTestBase +{ + public function setUp() + { + $authFile=dirname(__FILE__).'/data/auth.php'; + @unlink($authFile); + $this->auth=new CPhpAuthManager; + $this->auth->authFile=$authFile; + $this->auth->init(); + $this->prepareData(); + } + + public function tearDown() + { + @unlink($this->auth->authFile); + } + + public function testSaveLoad() + { + $this->auth->save(); + $this->auth->clearAll(); + $this->auth->load(); + $this->testCheckAccess(); + } +} diff --git a/tests/framework/web/auth/schema.sql b/tests/framework/web/auth/schema.sql new file mode 100644 index 000000000..f7f60bc2d --- /dev/null +++ b/tests/framework/web/auth/schema.sql @@ -0,0 +1,32 @@ +drop table if exists authassignment cascade; +drop table if exists authitemchild cascade; +drop table if exists authitem cascade; + +create table authitem +( + name varchar(64) not null, + type integer not null, + description text, + bizrule text, + data text, + primary key (name) +); + +create table authitemchild +( + parent varchar(64) not null, + child varchar(64) not null, + primary key (parent,child), + foreign key (parent) references authitem (name) on delete cascade on update cascade, + foreign key (child) references authitem (name) on delete cascade on update cascade +); + +create table authassignment +( + itemname varchar(64) not null, + userid varchar(64) not null, + bizrule text, + data text, + primary key (itemname,userid), + foreign key (itemname) references authitem (name) on delete cascade on update cascade +); diff --git a/tests/framework/web/controllers/admin/index.php b/tests/framework/web/controllers/admin/index.php new file mode 100644 index 000000000..e69de29bb diff --git a/tests/framework/web/controllers/article.php b/tests/framework/web/controllers/article.php new file mode 100644 index 000000000..e69de29bb diff --git a/tests/framework/web/controllers/home2.php b/tests/framework/web/controllers/home2.php new file mode 100644 index 000000000..e69de29bb diff --git a/tests/framework/web/controllers/post.php b/tests/framework/web/controllers/post.php new file mode 100644 index 000000000..e69de29bb diff --git a/tests/framework/web/helpers/CJSONTest.php b/tests/framework/web/helpers/CJSONTest.php new file mode 100644 index 000000000..eb341f845 --- /dev/null +++ b/tests/framework/web/helpers/CJSONTest.php @@ -0,0 +1,79 @@ +<?php +Yii::import('system.db.CDbConnection'); +Yii::import('system.db.ar.CActiveRecord'); + +require_once(dirname(__FILE__).'/../../db/data/models.php'); + +/** + * CJSON Test + */ +class CJSONTest extends CTestCase { + private $db; + + protected function setUp() + { + if(!extension_loaded('pdo') || !extension_loaded('pdo_sqlite')) + $this->markTestSkipped('PDO and SQLite extensions are required.'); + + $this->db=new CDbConnection('sqlite::memory:'); + $this->db->active=true; + $this->db->pdoInstance->exec(file_get_contents(dirname(__FILE__).'/../../db/data/sqlite.sql')); + CActiveRecord::$db=$this->db; + } + + protected function tearDown() + { + $this->db->active=false; + } + + /** + * native json_encode can't do it + * @return void + */ + public function testEncodeSingleAR(){ + $post = Post::model()->findByPk(1); + $this->assertEquals( + '{"id":"1","title":"post 1","create_time":"100000","author_id":"1","content":"content 1"}', + CJSON::encode($post) + ); + } + + /** + * native json_encode can't do it + * @return void + */ + public function testEncodeMultipleARs(){ + $posts=Post::model()->findAllByPk(array(1, 2)); + $this->assertEquals( + '[{"id":"1","title":"post 1","create_time":"100000","author_id":"1","content":"content 1"},{"id":"2","title":"post 2","create_time":"100001","author_id":"2","content":"content 2"}]', + CJSON::encode($posts) + ); + } + + public function testEncodeSimple(){ + $this->assertEquals("true", CJSON::encode(true)); + $this->assertEquals("false", CJSON::encode(false)); + $this->assertEquals("null", CJSON::encode(null)); + $this->assertEquals("123", CJSON::encode(123)); + $this->assertEquals("123.12", CJSON::encode(123.12)); + $this->assertEquals('"test\\\\me"', CJSON::encode('test\me')); + } + + public function testEncodeArray(){ + $objArr = array('a' => 'b'); + $arrArr = array('a', 'b'); + $mixedArr = array('c', 'a' => 'b'); + $nestedArr = array('a', 'b' => array('a', 'b' => 'c')); + + $this->assertEquals('{"a":"b"}', CJSON::encode($objArr)); + $this->assertEquals('["a","b"]', CJSON::encode($arrArr)); + $this->assertEquals('{"0":"c","a":"b"}', CJSON::encode($mixedArr)); + $this->assertEquals('{"0":"a","b":{"0":"a","b":"c"}}', CJSON::encode($nestedArr)); + } + + public function testDecode(){ + $this->assertEquals(array('c', 'a' => 'b'), CJSON::decode('{"0":"c","a":"b"}')); + $this->assertEquals(array('a', 'b'), CJSON::decode('["a","b"]')); + $this->assertEquals(array('a', 'b' => array('a', 'b' => 'c')), CJSON::decode('{"0":"a","b":{"0":"a","b":"c"}}')); + } +} diff --git a/tests/phpunit.xml b/tests/phpunit.xml new file mode 100644 index 000000000..17db94ef2 --- /dev/null +++ b/tests/phpunit.xml @@ -0,0 +1,7 @@ +<phpunit bootstrap="bootstrap.php" + colors="false" + convertErrorsToExceptions="true" + convertNoticesToExceptions="true" + convertWarningsToExceptions="true" + stopOnFailure="false"> +</phpunit> \ No newline at end of file diff --git a/tests/readme.txt b/tests/readme.txt new file mode 100644 index 000000000..998430419 --- /dev/null +++ b/tests/readme.txt @@ -0,0 +1,13 @@ +This folder contains unit tests of Yii framework. +PHPUnit 3.3+ and xdebug (for code coverage report) are required. + +To run a single unit test, use: +>> phpunit path/to/test.php + +To run all tests under a directory, use: +>> phpunit --verbose path/to/tests/directory + +To generate code coverage report: +>> phpunit --coverage-html reports path/to/tests/directory/or/test/class + +Please use 'phpunit --help' to see more command line options. \ No newline at end of file diff --git a/tests/rununit.bat b/tests/rununit.bat new file mode 100644 index 000000000..859e511d9 --- /dev/null +++ b/tests/rununit.bat @@ -0,0 +1 @@ +phpunit --verbose framework \ No newline at end of file