From 5531baa87f2cbfae76b5733016f860917813eea5 Mon Sep 17 00:00:00 2001 From: Sam Minnee Date: Thu, 18 Oct 2018 20:19:39 +1300 Subject: [PATCH] FIX: Introduce readonly transaction test to all database. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This should work on MySQL and PDO; let’s test this. --- tests/php/ORM/DatabaseTest.php | 51 --------------- tests/php/ORM/TransactionTest.php | 104 ++++++++++++++++++++++++++++++ 2 files changed, 104 insertions(+), 51 deletions(-) diff --git a/tests/php/ORM/DatabaseTest.php b/tests/php/ORM/DatabaseTest.php index 247224ae5..0fd108120 100644 --- a/tests/php/ORM/DatabaseTest.php +++ b/tests/php/ORM/DatabaseTest.php @@ -21,11 +21,6 @@ class DatabaseTest extends SapphireTest protected $usesDatabase = true; - /** - * Disable transactions so that we can test them - */ - protected $usesTransactions = false; - public function testDontRequireField() { $schema = DB::get_schema(); @@ -192,52 +187,6 @@ class DatabaseTest extends SapphireTest $this->assertTrue($db->canLock('DatabaseTest'), 'Can lock again after releasing it'); } - public function testTransactions() - { - $conn = DB::get_conn(); - if (!$conn->supportsTransactions()) { - $this->markTestSkipped("DB Doesn't support transactions"); - return; - } - - // Test that successful transactions are comitted - $obj = new DatabaseTest\MyObject(); - $failed = false; - $conn->withTransaction( - function () use (&$obj) { - $obj->MyField = 'Save 1'; - $obj->write(); - }, - function () use (&$failed) { - $failed = true; - } - ); - $this->assertEquals('Save 1', DatabaseTest\MyObject::get()->first()->MyField); - $this->assertFalse($failed); - - // Test failed transactions are rolled back - $ex = null; - $failed = false; - try { - $conn->withTransaction( - function () use (&$obj) { - $obj->MyField = 'Save 2'; - $obj->write(); - throw new Exception("error"); - }, - function () use (&$failed) { - $failed = true; - } - ); - } catch (Exception $ex) { - } - $this->assertTrue($failed); - $this->assertEquals('Save 1', DatabaseTest\MyObject::get()->first()->MyField); - $this->assertInstanceOf('Exception', $ex); - $this->assertEquals('error', $ex->getMessage()); - } - - public function testFieldTypes() { // Scaffold some data diff --git a/tests/php/ORM/TransactionTest.php b/tests/php/ORM/TransactionTest.php index 88617f26c..c6e892f28 100644 --- a/tests/php/ORM/TransactionTest.php +++ b/tests/php/ORM/TransactionTest.php @@ -5,6 +5,7 @@ namespace SilverStripe\ORM\Tests; use SilverStripe\ORM\DB; use SilverStripe\ORM\DataObject; use SilverStripe\Dev\SapphireTest; +use SilverStripe\Dev\Deprecation; use SilverStripe\ORM\Tests\TransactionTest\TestObject; class TransactionTest extends SapphireTest @@ -17,6 +18,20 @@ class TransactionTest extends SapphireTest TransactionTest\TestObject::class, ]; + private static $originalVersionInfo; + + protected function setUp() + { + parent::setUp(); + self::$originalVersionInfo = Deprecation::dump_settings(); + } + + protected function tearDown() + { + Deprecation::restore_settings(self::$originalVersionInfo); + parent::tearDown(); + } + public static function setUpBeforeClass() { parent::setUpBeforeClass(); @@ -25,8 +40,57 @@ class TransactionTest extends SapphireTest } } + public function testTransactions() + { + $conn = DB::get_conn(); + if (!$conn->supportsTransactions()) { + $this->markTestSkipped("DB Doesn't support transactions"); + return; + } + + // Test that successful transactions are comitted + $obj = new TestObject(); + $failed = false; + $conn->withTransaction( + function () use (&$obj) { + $obj->Title = 'Save 1'; + $obj->write(); + }, + function () use (&$failed) { + $failed = true; + } + ); + $this->assertEquals('Save 1', TestObject::get()->first()->Title); + $this->assertFalse($failed); + + // Test failed transactions are rolled back + $ex = null; + $failed = false; + try { + $conn->withTransaction( + function () use (&$obj) { + $obj->Title = 'Save 2'; + $obj->write(); + throw new \Exception("error"); + }, + function () use (&$failed) { + $failed = true; + } + ); + } catch (\Exception $ex) { + } + $this->assertTrue($failed); + $this->assertEquals('Save 1', TestObject::get()->first()->Title); + $this->assertInstanceOf('Exception', $ex); + $this->assertEquals('error', $ex->getMessage()); + } + public function testNestedTransaction() { + if (!DB::get_conn()->supportsSavepoints()) { + static::markTestSkipped('Current database does not support savepoints'); + } + $this->assertCount(0, TestObject::get()); try { DB::get_conn()->withTransaction(function () { @@ -89,4 +153,44 @@ class TransactionTest extends SapphireTest $this->assertFalse(is_object($third) && $third->exists()); $this->assertFalse(is_object($fourth) && $fourth->exists()); } + + public function testReadOnlyTransaction() + { + if (!DB::get_conn()->supportsTransactions()) { + $this->markTestSkipped('Current database is doesn\'t support transactions'); + return; + } + + // This feature is deprecated in 4.4, but we're still testing it. + Deprecation::notification_version('4.3.0'); + + $page = new TestObject(); + $page->Title = 'Read only success'; + $page->write(); + + DB::get_conn()->transactionStart('READ ONLY'); + + try { + $page = new TestObject(); + $page->Title = 'Read only page failed'; + $page->write(); + DB::get_conn()->transactionEnd(); + } catch (\Exception $e) { + //could not write this record + //We need to do a rollback or a commit otherwise we'll get error messages + DB::get_conn()->transactionRollback(); + } + + DataObject::flush_and_destroy_cache(); + + $success = DataObject::get_one(TestObject::class, "\"Title\"='Read only success'"); + $fail = DataObject::get_one(TestObject::class, "\"Title\"='Read only page failed'"); + + //This page should be in the system + $this->assertInternalType('object', $success); + $this->assertTrue($success->exists()); + + //This page should NOT exist, we had 'read only' permissions + $this->assertNull($fail); + } }