Passed
Push — int-types ( c34c73...b1a342 )
by Sam
10:49 queued 05:06
created

DatabaseTest::testHasTable()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 0
dl 0
loc 4
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace SilverStripe\ORM\Tests;
4
5
use SilverStripe\ORM\DB;
6
use SilverStripe\ORM\Connect\MySQLDatabase;
7
use SilverStripe\MSSQL\MSSQLDatabase;
0 ignored issues
show
Bug introduced by
The type SilverStripe\MSSQL\MSSQLDatabase was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
8
use SilverStripe\Dev\SapphireTest;
9
use Exception;
10
use SilverStripe\ORM\Tests\DatabaseTest\MyObject;
11
use SilverStripe\ORM\DataObject;
12
13
/**
14
 * @skipUpgrade
15
*/
16
class DatabaseTest extends SapphireTest
17
{
18
19
    protected static $extra_dataobjects = array(
20
        MyObject::class,
21
    );
22
23
    protected $usesDatabase = true;
24
25
    /**
26
     * Disable transactions so that we can test them
27
     */
28
    protected $usesTransactions = false;
29
30
    public function testDontRequireField()
31
    {
32
        $schema = DB::get_schema();
33
        $this->assertArrayHasKey(
34
            'MyField',
35
            $schema->fieldList('DatabaseTest_MyObject')
36
        );
37
38
        $schema->dontRequireField('DatabaseTest_MyObject', 'MyField');
39
40
        $this->assertArrayHasKey(
41
            '_obsolete_MyField',
42
            $schema->fieldList('DatabaseTest_MyObject'),
43
            'Field is renamed to _obsolete_<fieldname> through dontRequireField()'
44
        );
45
46
        static::resetDBSchema(true);
47
    }
48
49
    public function testRenameField()
50
    {
51
        $schema = DB::get_schema();
52
53
        $schema->clearCachedFieldlist();
54
55
        $schema->renameField('DatabaseTest_MyObject', 'MyField', 'MyRenamedField');
56
57
        $this->assertArrayHasKey(
58
            'MyRenamedField',
59
            $schema->fieldList('DatabaseTest_MyObject'),
60
            'New fieldname is set through renameField()'
61
        );
62
        $this->assertArrayNotHasKey(
63
            'MyField',
64
            $schema->fieldList('DatabaseTest_MyObject'),
65
            'Old fieldname isnt preserved through renameField()'
66
        );
67
68
        static::resetDBSchema(true);
69
    }
70
71
    public function testMySQLCreateTableOptions()
72
    {
73
        if (!(DB::get_conn() instanceof MySQLDatabase)) {
74
            $this->markTestSkipped('MySQL only');
75
        }
76
77
78
        $ret = DB::query(
79
            sprintf(
80
                'SHOW TABLE STATUS WHERE "Name" = \'%s\'',
81
                'DatabaseTest_MyObject'
82
            )
83
        )->first();
84
        $this->assertEquals(
85
            $ret['Engine'],
86
            'InnoDB',
87
            "MySQLDatabase tables can be changed to InnoDB through DataObject::\$create_table_options"
88
        );
89
    }
90
91
    function testIsSchemaUpdating()
92
    {
93
        $schema = DB::get_schema();
94
95
        $this->assertFalse($schema->isSchemaUpdating(), 'Before the transaction the flag is false.');
96
97
        // Test complete schema update
98
        $test = $this;
99
        $schema->schemaUpdate(
100
            function () use ($test, $schema) {
101
                $test->assertTrue($schema->isSchemaUpdating(), 'During the transaction the flag is true.');
102
            }
103
        );
104
        $this->assertFalse($schema->isSchemaUpdating(), 'After the transaction the flag is false.');
105
106
        // Test cancelled schema update
107
        $schema->schemaUpdate(
108
            function () use ($test, $schema) {
109
                $schema->cancelSchemaUpdate();
110
                $test->assertFalse($schema->doesSchemaNeedUpdating(), 'After cancelling the transaction the flag is false');
111
            }
112
        );
113
    }
114
115
    public function testSchemaUpdateChecking()
116
    {
117
        $schema = DB::get_schema();
118
119
        // Initially, no schema changes necessary
120
        $test = $this;
121
        $schema->schemaUpdate(
122
            function () use ($test, $schema) {
123
                $test->assertFalse($schema->doesSchemaNeedUpdating());
124
125
                // If we make a change, then the schema will need updating
126
                $schema->transCreateTable("TestTable");
127
                $test->assertTrue($schema->doesSchemaNeedUpdating());
128
129
                // If we make cancel the change, then schema updates are no longer necessary
130
                $schema->cancelSchemaUpdate();
131
                $test->assertFalse($schema->doesSchemaNeedUpdating());
132
            }
133
        );
134
    }
135
136
    public function testHasTable()
137
    {
138
        $this->assertTrue(DB::get_schema()->hasTable('DatabaseTest_MyObject'));
139
        $this->assertFalse(DB::get_schema()->hasTable('asdfasdfasdf'));
140
    }
141
142
    public function testGetAndReleaseLock()
143
    {
144
        $db = DB::get_conn();
145
146
        if (!$db->supportsLocks()) {
147
            return $this->markTestSkipped('Tested database doesn\'t support application locks');
148
        }
149
150
        $this->assertTrue(
151
            $db->getLock('DatabaseTest'),
152
            'Can aquire lock'
153
        );
154
        // $this->assertFalse($db->getLock('DatabaseTest'), 'Can\'t repeatedly aquire the same lock');
155
        $this->assertTrue(
156
            $db->getLock('DatabaseTest'),
157
            'The same lock can be aquired multiple times in the same connection'
158
        );
159
160
        $this->assertTrue(
161
            $db->getLock('DatabaseTestOtherLock'),
162
            'Can aquire different lock'
163
        );
164
        $db->releaseLock('DatabaseTestOtherLock');
165
166
        // Release potentially stacked locks from previous getLock() invocations
167
        $db->releaseLock('DatabaseTest');
168
        $db->releaseLock('DatabaseTest');
169
170
        $this->assertTrue(
171
            $db->getLock('DatabaseTest'),
172
            'Can aquire lock after releasing it'
173
        );
174
        $db->releaseLock('DatabaseTest');
175
    }
176
177
    public function testCanLock()
178
    {
179
        $db = DB::get_conn();
180
181
        if (!$db->supportsLocks()) {
182
            return $this->markTestSkipped('Database doesn\'t support locks');
183
        }
184
185
        if ($db instanceof MSSQLDatabase) {
186
            return $this->markTestSkipped('MSSQLDatabase doesn\'t support inspecting locks');
187
        }
188
189
        $this->assertTrue($db->canLock('DatabaseTest'), 'Can lock before first aquiring one');
190
        $db->getLock('DatabaseTest');
191
        $this->assertFalse($db->canLock('DatabaseTest'), 'Can\'t lock after aquiring one');
192
        $db->releaseLock('DatabaseTest');
193
        $this->assertTrue($db->canLock('DatabaseTest'), 'Can lock again after releasing it');
194
    }
195
196
    public function testTransactions()
197
    {
198
        $conn = DB::get_conn();
199
        if (!$conn->supportsTransactions()) {
200
            $this->markTestSkipped("DB Doesn't support transactions");
201
            return;
202
        }
203
204
        // Test that successful transactions are comitted
205
        $obj = new DatabaseTest\MyObject();
206
        $failed = false;
207
        $conn->withTransaction(
208
            function () use (&$obj) {
209
                $obj->MyField = 'Save 1';
210
                $obj->write();
211
            },
212
            function () use (&$failed) {
213
                $failed = true;
214
            }
215
        );
216
        $this->assertEquals('Save 1', DatabaseTest\MyObject::get()->first()->MyField);
0 ignored issues
show
Bug Best Practice introduced by
The property MyField does not exist on SilverStripe\ORM\DataObject. Since you implemented __get, consider adding a @property annotation.
Loading history...
217
        $this->assertFalse($failed);
218
219
        // Test failed transactions are rolled back
220
        $ex = null;
221
        $failed = false;
222
        try {
223
            $conn->withTransaction(
224
                function () use (&$obj) {
225
                    $obj->MyField = 'Save 2';
226
                    $obj->write();
227
                    throw new Exception("error");
228
                },
229
                function () use (&$failed) {
230
                    $failed = true;
231
                }
232
            );
233
        } catch (Exception $ex) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
234
        }
235
        $this->assertTrue($failed);
236
        $this->assertEquals('Save 1', DatabaseTest\MyObject::get()->first()->MyField);
237
        $this->assertInstanceOf('Exception', $ex);
238
        $this->assertEquals('error', $ex->getMessage());
0 ignored issues
show
Bug introduced by
The method getMessage() does not exist on null. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

238
        $this->assertEquals('error', $ex->/** @scrutinizer ignore-call */ getMessage());

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
239
    }
240
241
242
    public function testFieldTypes()
243
    {
244
        // Scaffold some data
245
        $obj = new MyObject();
246
        $obj->MyField = "value";
0 ignored issues
show
Bug Best Practice introduced by
The property MyField does not exist on SilverStripe\ORM\Tests\DatabaseTest\MyObject. Since you implemented __set, consider adding a @property annotation.
Loading history...
247
        $obj->MyInt = 5;
0 ignored issues
show
Bug Best Practice introduced by
The property MyInt does not exist on SilverStripe\ORM\Tests\DatabaseTest\MyObject. Since you implemented __set, consider adding a @property annotation.
Loading history...
248
        $obj->MyFloat = 6.0;
0 ignored issues
show
Bug Best Practice introduced by
The property MyFloat does not exist on SilverStripe\ORM\Tests\DatabaseTest\MyObject. Since you implemented __set, consider adding a @property annotation.
Loading history...
249
        $obj->MyBoolean = true;
0 ignored issues
show
Bug Best Practice introduced by
The property MyBoolean does not exist on SilverStripe\ORM\Tests\DatabaseTest\MyObject. Since you implemented __set, consider adding a @property annotation.
Loading history...
250
        $obj->write();
251
252
        $record = DB::prepared_query(
253
            'SELECT * FROM "DatabaseTest_MyObject" WHERE "ID" = ?',
254
            [ $obj->ID ]
255
        )->record();
256
257
        // IDs and ints are returned as ints
258
        $this->assertInternalType('int', $record['ID']);
259
        $this->assertInternalType('int', $record['MyInt']);
260
261
        $this->assertInternalType('float', $record['MyFloat']);
262
263
        // Booleans are returned as ints – we follow MySQL's lead
264
        $this->assertInternalType('int', $record['MyBoolean']);
265
266
        // Strings and enums are returned as strings
267
        $this->assertInternalType('string', $record['MyField']);
268
        $this->assertInternalType('string', $record['ClassName']);
269
270
        // Dates are returned as strings
271
        $this->assertInternalType('string', $record['Created']);
272
        $this->assertInternalType('string', $record['LastEdited']);
273
    }
274
}
275