From cb376bf420a7414ef573d4d7c4ff7d23ef0a4514 Mon Sep 17 00:00:00 2001 From: chinpei215 Date: Sun, 3 Aug 2014 22:34:11 +0900 Subject: [PATCH] Add some more transaction tests And remove 2 else clauses. --- lib/Cake/Model/Model.php | 6 +- lib/Cake/Test/Case/Model/ModelWriteTest.php | 169 ++++++++++++++++++++ 2 files changed, 171 insertions(+), 4 deletions(-) diff --git a/lib/Cake/Model/Model.php b/lib/Cake/Model/Model.php index f04f1c27d..f5e8ed1d4 100644 --- a/lib/Cake/Model/Model.php +++ b/lib/Cake/Model/Model.php @@ -2243,11 +2243,10 @@ class Model extends Object implements CakeEventListener { $options['validate'] = false; } + $transactionBegun = false; if ($options['atomic']) { $db = $this->getDataSource(); $transactionBegun = $db->begin(); - } else { - $transactionBegun = false; } try { @@ -2378,11 +2377,10 @@ class Model extends Object implements CakeEventListener { $options['validate'] = false; } + $transactionBegun = false; if ($options['atomic']) { $db = $this->getDataSource(); $transactionBegun = $db->begin(); - } else { - $transactionBegun = false; } try { diff --git a/lib/Cake/Test/Case/Model/ModelWriteTest.php b/lib/Cake/Test/Case/Model/ModelWriteTest.php index 2c7be723d..6a523e5a1 100644 --- a/lib/Cake/Test/Case/Model/ModelWriteTest.php +++ b/lib/Cake/Test/Case/Model/ModelWriteTest.php @@ -36,6 +36,8 @@ class TestAuthor extends Author { protected $_dataSourceObject; + public $dataForAfterSave; + /** * Helper method to set a datasource object * @@ -74,6 +76,8 @@ class TestPost extends Post { protected $_dataSourceObject; + public $dataForAfterSave; + /** * Helper method to set a datasource object * @@ -7499,4 +7503,169 @@ class ModelWriteTest extends BaseModelTest { return $db; } + +/** + * Test that transactions behave correctly on nested saveMany calls. + * + * @return void + */ + public function testTransactionOnNestedSaveMany() { + $this->loadFixtures('Post'); + $Post = new TestPost(); + $Post->getEventManager()->attach(array($this, 'nestedSaveMany'), 'Model.afterSave'); + + // begin -> [ begin -> commit ] -> commit + $db = $this->_getMockDboSource(array('begin', 'commit', 'rollback')); + $db->expects($this->exactly(2))->method('begin')->will($this->returnValue(true)); + $db->expects($this->exactly(2))->method('commit'); + $db->expects($this->never())->method('rollback'); + $Post->setDataSourceObject($db); + + $data = array( + array('author_id' => 1, 'title' => 'Outer Post'), + ); + $Post->dataForAfterSave = array( + array('author_id' => 1, 'title' => 'Inner Post'), + ); + $this->assertTrue($Post->saveMany($data)); + + // begin -> [ begin(false) ] -> commit + $db = $this->_getMockDboSource(array('begin', 'commit', 'rollback')); + $db->expects($this->at(0))->method('begin')->will($this->returnValue(true)); + $db->expects($this->at(1))->method('begin')->will($this->returnValue(false)); + $db->expects($this->once())->method('commit'); + $db->expects($this->never())->method('rollback'); + $Post->setDataSourceObject($db); + + $data = array( + array('author_id' => 1, 'title' => 'Outer Post'), + ); + $Post->dataForAfterSave = array( + array('author_id' => 1, 'title' => 'Inner Post'), + ); + $this->assertTrue($Post->saveMany($data)); + + // begin -> [ begin -> rollback ] -> rollback + $db = $this->_getMockDboSource(array('begin', 'commit', 'rollback')); + $db->expects($this->exactly(2))->method('begin')->will($this->returnValue(true)); + $db->expects($this->never())->method('commit'); + $db->expects($this->exactly(2))->method('rollback'); + $Post->setDataSourceObject($db); + $data = array( + array('author_id' => 1, 'title' => 'Outer Post'), + ); + $Post->dataForAfterSave = array( + array('author_id' => 1, 'title' => 'Inner Post', 'body' => $db->expression('PDO_EXCEPTION()')), + ); + + try { + $Post->saveMany($data); + $this->fail('No exception thrown'); + } catch(Exception $e) { + } + } + +/** + * Test that transaction behaves correctly on nested saveAssociated calls. + * + * @return void + */ + public function testTransactionOnNestedSaveAssociated() { + $this->loadFixtures('Author', 'Post'); + + $Author = new TestAuthor(); + $Author->getEventManager()->attach(array($this, 'nestedSaveAssociated'), 'Model.afterSave'); + + // begin -> [ begin -> commit ] -> commit + $db = $this->_getMockDboSource(array('begin', 'commit', 'rollback')); + $db->expects($this->exactly(2))->method('begin')->will($this->returnValue(true)); + $db->expects($this->exactly(2))->method('commit'); + $db->expects($this->never())->method('rollback'); + $Author->setDataSourceObject($db); + $Author->Post->setDataSourceObject($db); + + $data = array( + 'Author' => array('user' => 'outer'), + 'Post' => array( + array('title' => 'Outer Post'), + ) + ); + $Author->dataForAfterSave = array( + 'Author' => array('user' => 'inner'), + 'Post' => array( + array('title' => 'Inner Post'), + ) + ); + $this->assertTrue($Author->saveAssociated($data)); + + // begin -> [ begin(false) ] -> commit + $db = $this->_getMockDboSource(array('begin', 'commit', 'rollback')); + $db->expects($this->at(0))->method('begin')->will($this->returnValue(true)); + $db->expects($this->at(1))->method('begin')->will($this->returnValue(false)); + $db->expects($this->once())->method('commit'); + $db->expects($this->never())->method('rollback'); + $Author->setDataSourceObject($db); + $Author->Post->setDataSourceObject($db); + $data = array( + 'Author' => array('user' => 'outer'), + 'Post' => array( + array('title' => 'Outer Post'), + ) + ); + $Author->dataForAfterSave = array( + 'Author' => array('user' => 'inner'), + 'Post' => array( + array('title' => 'Inner Post'), + ) + ); + $this->assertTrue($Author->saveAssociated($data)); + + // begin -> [ begin -> rollback ] -> rollback + $db = $this->_getMockDboSource(array('begin', 'commit', 'rollback')); + $db->expects($this->exactly(2))->method('begin')->will($this->returnValue(true)); + $db->expects($this->never())->method('commit'); + $db->expects($this->exactly(2))->method('rollback'); + $Author->setDataSourceObject($db); + $Author->Post->setDataSourceObject($db); + $data = array( + 'Author' => array('user' => 'outer'), + 'Post' => array( + array('title' => 'Outer Post'), + ) + ); + $Author->dataForAfterSave = array( + 'Author' => array('user' => 'inner', 'password' => $db->expression('PDO_EXCEPTION()')), + 'Post' => array( + array('title' => 'Inner Post'), + ) + ); + + try { + $Author->saveAssociated($data); + $this->fail('No exception thrown'); + } catch(Exception $e) { + } + } + +/** + * A callback for testing nested saveMany. + * + * @param CakeEvent $event containing the Model + * @return void + */ + public function nestedSaveMany($event) { + $Model = $event->subject; + $Model->saveMany($Model->dataForAfterSave, array('callbacks' => false)); + } + +/** + * A callback for testing nested saveAssociated. + * + * @param CakeEvent $event containing the Model + * @return void + */ + public function nestedSaveAssociated($event) { + $Model = $event->subject; + $Model->saveAssociated($Model->dataForAfterSave, array('callbacks' => false)); + } }