Passed
Pull Request — 4 (#10028)
by Steve
09:01
created

TransactionTest   A

Complexity

Total Complexity 19

Size/Duplication

Total Lines 189
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 108
dl 0
loc 189
rs 10
c 0
b 0
f 0
wmc 19
1
<?php
2
3
namespace SilverStripe\ORM\Tests;
4
5
use SilverStripe\ORM\DB;
6
use SilverStripe\ORM\DataObject;
7
use SilverStripe\Dev\SapphireTest;
8
use SilverStripe\Dev\Deprecation;
9
use SilverStripe\ORM\Tests\TransactionTest\TestObject;
10
11
class TransactionTest extends SapphireTest
12
{
13
    protected $usesDatabase = true;
14
15
    protected $usesTransactions = false;
16
17
    protected static $extra_dataobjects = [
18
        TransactionTest\TestObject::class,
19
    ];
20
21
    private static $originalVersionInfo;
22
23
    protected function setUp(): void
24
    {
25
        parent::setUp();
26
        self::$originalVersionInfo = Deprecation::dump_settings();
27
    }
28
29
    protected function tearDown(): void
30
    {
31
        Deprecation::restore_settings(self::$originalVersionInfo);
32
        parent::tearDown();
33
    }
34
35
    public static function setUpBeforeClass(): void
36
    {
37
        parent::setUpBeforeClass();
38
        if (!DB::get_conn()->supportsTransactions()) {
39
            static::markTestSkipped('Current database does not support transactions');
40
        }
41
    }
42
43
    public function testTransactions()
44
    {
45
        $conn = DB::get_conn();
46
        if (!$conn->supportsTransactions()) {
47
            $this->markTestSkipped("DB Doesn't support transactions");
48
            return;
49
        }
50
51
        // Test that successful transactions are comitted
52
        $obj = new TestObject();
53
        $failed = false;
54
        $conn->withTransaction(
55
            function () use (&$obj) {
56
                $obj->Title = 'Save 1';
57
                $obj->write();
58
            },
59
            function () use (&$failed) {
60
                $failed = true;
61
            }
62
        );
63
        $this->assertEquals('Save 1', TestObject::get()->first()->Title);
64
        $this->assertFalse($failed);
65
66
        // Test failed transactions are rolled back
67
        $ex = null;
68
        $failed = false;
69
        try {
70
            $conn->withTransaction(
71
                function () use (&$obj) {
72
                    $obj->Title = 'Save 2';
73
                    $obj->write();
74
                    throw new \Exception("error");
75
                },
76
                function () use (&$failed) {
77
                    $failed = true;
78
                }
79
            );
80
        } catch (\Exception $ex) {
81
        }
82
        $this->assertTrue($failed);
83
        $this->assertEquals('Save 1', TestObject::get()->first()->Title);
84
        $this->assertInstanceOf('Exception', $ex);
85
        $this->assertEquals('error', $ex->getMessage());
86
    }
87
88
    public function testNestedTransaction()
89
    {
90
        if (!DB::get_conn()->supportsSavepoints()) {
91
            static::markTestSkipped('Current database does not support savepoints');
92
        }
93
94
        $this->assertCount(0, TestObject::get());
95
        try {
96
            DB::get_conn()->withTransaction(function () {
97
                $obj = TransactionTest\TestObject::create();
98
                $obj->Title = 'Test';
99
                $obj->write();
100
101
                $this->assertCount(1, TestObject::get());
102
103
                DB::get_conn()->withTransaction(function () {
104
                    $obj = TransactionTest\TestObject::create();
105
                    $obj->Title = 'Test2';
106
                    $obj->write();
107
                    $this->assertCount(2, TestObject::get());
108
                });
109
110
                throw new \Exception('roll back transaction');
111
            });
112
        } catch (\Exception $e) {
113
            $this->assertEquals('roll back transaction', $e->getMessage());
114
        }
115
        $this->assertCount(0, TestObject::get());
116
    }
117
118
    public function testCreateWithTransaction()
119
    {
120
        // First/Second in a successful transaction
121
        DB::get_conn()->transactionStart();
122
        $obj = new TransactionTest\TestObject();
123
        $obj->Title = 'First page';
124
        $obj->write();
125
126
        $obj = new TransactionTest\TestObject();
127
        $obj->Title = 'Second page';
128
        $obj->write();
129
        DB::get_conn()->transactionEnd();
130
131
        // Third/Fourth in a rolled back transaction
132
        DB::get_conn()->transactionStart();
133
        $obj = new TransactionTest\TestObject();
134
        $obj->Title = 'Third page';
135
        $obj->write();
136
137
        $obj = new TransactionTest\TestObject();
138
        $obj->Title = 'Fourth page';
139
        $obj->write();
140
        DB::get_conn()->transactionRollback();
141
142
143
        $first = DataObject::get(TransactionTest\TestObject::class, "\"Title\"='First page'");
144
        $second = DataObject::get(TransactionTest\TestObject::class, "\"Title\"='Second page'");
145
        $third = DataObject::get(TransactionTest\TestObject::class, "\"Title\"='Third page'");
146
        $fourth = DataObject::get(TransactionTest\TestObject::class, "\"Title\"='Fourth page'");
147
148
        //These pages should be in the system
149
        $this->assertTrue(is_object($first) && $first->exists());
150
        $this->assertTrue(is_object($second) && $second->exists());
151
152
        //These pages should NOT exist, we rolled back
153
        $this->assertFalse(is_object($third) && $third->exists());
154
        $this->assertFalse(is_object($fourth) && $fourth->exists());
155
    }
156
157
    public function testReadOnlyTransaction()
158
    {
159
        if (!DB::get_conn()->supportsTransactions()) {
160
            $this->markTestSkipped('Current database doesn\'t support transactions');
161
            return;
162
        }
163
164
        if (!DB::get_conn()->supportsTransactionMode('READ ONLY')) {
165
            $this->markTestSkipped('Current database doesn\'t support READ ONLY transactions');
166
            return;
167
        }
168
169
        // This feature is deprecated in 4.4, but we're still testing it.
170
        Deprecation::notification_version('4.3.0');
171
172
        $page = new TestObject();
173
        $page->Title = 'Read only success';
174
        $page->write();
175
176
        DB::get_conn()->transactionStart('READ ONLY');
177
178
        try {
179
            $page = new TestObject();
180
            $page->Title = 'Read only page failed';
181
            $page->write();
182
            DB::get_conn()->transactionEnd();
183
        } catch (\Exception $e) {
184
            //could not write this record
185
            //We need to do a rollback or a commit otherwise we'll get error messages
186
            DB::get_conn()->transactionRollback();
187
        }
188
189
        DataObject::flush_and_destroy_cache();
190
191
        $success = DataObject::get_one(TestObject::class, "\"Title\"='Read only success'");
192
        $fail = DataObject::get_one(TestObject::class, "\"Title\"='Read only page failed'");
193
194
        //This page should be in the system
195
        $this->assertInternalType('object', ($success);
0 ignored issues
show
Bug introduced by
A parse error occurred: Syntax error, unexpected ';', expecting ',' or ')' on line 195 at column 54
Loading history...
196
        $this->assertTrue($success->exists());
197
198
        //This page should NOT exist, we had 'read only' permissions
199
        $this->assertNull($fail);
200
    }
201
}
202