Completed
Push — develop ( 31199f...e56745 )
by Mathieu
01:56
created

DBObjectTest::testLoadOrFail()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 14
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 6
c 1
b 0
f 0
nc 1
nop 0
dl 0
loc 14
rs 10
1
<?php
2
require_once 'stubs/Category.php';
3
4
class DBObjectTest extends \PHPUnit\Framework\TestCase
5
{
6
    protected $tableName = 'users';
7
    public function testContructor()
8
    {
9
        $classname = '\Suricate\DBObject';
10
11
        // Get mock, without the constructor being called
12
        $mock = $this->getMockBuilder($classname)
13
            ->disableOriginalConstructor()
14
            ->setMethods(array('setRelations'))
15
            ->getMockForAbstractClass();
16
17
        // set expectations for constructor calls
18
        $mock->expects($this->once())
19
            ->method('setRelations');
20
21
        // now call the constructor
22
        $reflectedClass = new ReflectionClass($classname);
23
        $constructor = $reflectedClass->getConstructor();
24
        $constructor->invoke($mock);
25
    }
26
27
    public function testGetTableName()
28
    {
29
        $testName = 'my_sql_table';
30
31
        $testDBO = new \Suricate\DBObject();
32
        self::mockProperty($testDBO, 'tableName', $testName);
33
        $this->assertEquals($testName, $testDBO->getTableName());
34
    }
35
36
    public function testGetTableIndex()
37
    {
38
        $testIndex = 'id';
39
40
        $testDBO = new \Suricate\DBObject();
41
        self::mockProperty($testDBO, 'tableIndex', $testIndex);
42
        $this->assertEquals($testIndex, $testDBO->getTableIndex());
43
    }
44
45
    public function testGetDBConfig()
46
    {
47
        $testConfigName = 'my_config';
48
49
        $testDBO = new \Suricate\DBObject();
50
        self::mockProperty($testDBO, 'DBConfig', $testConfigName);
51
        $this->assertEquals($testConfigName, $testDBO->getDBConfig());
52
    }
53
54
    public function testUndefinedGet()
55
    {
56
        $testDBO = new \Suricate\DBObject();
57
        self::mockProperty($testDBO, 'dbVariables', ['id', 'name', 'last_update']);
58
        $this->expectException(\InvalidArgumentException::class);
59
        
60
        $testDBO->undefinedVar;
0 ignored issues
show
Bug Best Practice introduced by
The property undefinedVar does not exist on Suricate\DBObject. Since you implemented __get, consider adding a @property annotation.
Loading history...
61
    }
62
63
    public function testDBProperty()
64
    {
65
        $testDBO = new \Suricate\DBObject();
66
        $testDBO->regularProperty = 42;
0 ignored issues
show
Bug Best Practice introduced by
The property regularProperty does not exist on Suricate\DBObject. Since you implemented __set, consider adding a @property annotation.
Loading history...
67
        self::mockProperty($testDBO, 'dbVariables', ['id', 'name', 'not_loaded_var']);
68
        self::mockProperty($testDBO, 'dbValues', ['id' => 1, 'name' => 'test name']);
69
        $this->assertEquals($testDBO->id, 1);
0 ignored issues
show
Bug Best Practice introduced by
The property id does not exist on Suricate\DBObject. Since you implemented __get, consider adding a @property annotation.
Loading history...
70
        $this->assertNotEquals($testDBO->name, 'test name edited');
0 ignored issues
show
Bug Best Practice introduced by
The property name does not exist on Suricate\DBObject. Since you implemented __get, consider adding a @property annotation.
Loading history...
71
        $this->assertNull($testDBO->not_loaded_var);
0 ignored issues
show
Bug Best Practice introduced by
The property not_loaded_var does not exist on Suricate\DBObject. Since you implemented __get, consider adding a @property annotation.
Loading history...
72
73
        $this->assertTrue($testDBO->isDBVariable('id'));
74
        $this->assertFalse($testDBO->isDBVariable('regularProperty'));
75
76
        $this->assertTrue($testDBO->propertyExists('regularProperty'));
77
        $this->assertTrue($testDBO->propertyExists('id'));
78
        $this->assertFalse($testDBO->propertyExists('unknownProperty'));
79
    }
80
81
    public function testIsset()
82
    {
83
        $testDBO = new \Suricate\DBObject();
84
        self::mockProperty($testDBO, 'dbVariables', ['id', 'name', 'not_loaded_var']);
85
        self::mockProperty($testDBO, 'dbValues', ['id' => 1, 'name' => 'test name']);
86
87
        $this->assertTrue(isset($testDBO->id));
0 ignored issues
show
Bug Best Practice introduced by
The property id does not exist on Suricate\DBObject. Since you implemented __get, consider adding a @property annotation.
Loading history...
88
        $this->assertFalse(isset($testDBO->undefVar));
0 ignored issues
show
Bug Best Practice introduced by
The property undefVar does not exist on Suricate\DBObject. Since you implemented __get, consider adding a @property annotation.
Loading history...
89
    }
90
91
    public function testIsLoaded()
92
    {
93
        $testIndex = 'id';
94
95
        $testDBO = new \Suricate\DBObject();
96
        self::mockProperty($testDBO, 'tableIndex', $testIndex);
97
        self::mockProperty($testDBO, 'dbVariables', [$testIndex, 'name', 'not_loaded_var']);
98
        $this->assertFalse($testDBO->isLoaded());
99
100
        self::mockProperty($testDBO, 'dbValues', [$testIndex => 1, 'name' => 'test name']);
101
        $this->assertTrue($testDBO->isLoaded());
102
    }
103
104
    public function testInstanciate()
105
    {
106
        $testDBO = Category::instanciate([
107
            'id' => 1,
108
            'name' => 'test record',
109
        ]);
110
111
        $reflector = new ReflectionClass(Category::class);
112
        $property = $reflector->getProperty('dbValues');
113
        $property->setAccessible(true);
114
        $this->assertEquals([
115
            'id' => 1,
116
            'name' => 'test record',
117
        ], $property->getValue($testDBO));
118
    }
119
120
    public function testHydrate()
121
    {
122
        $testDBO = new \Suricate\DBObject();
123
        $testDBO->realProperty = '';
0 ignored issues
show
Bug Best Practice introduced by
The property realProperty does not exist on Suricate\DBObject. Since you implemented __set, consider adding a @property annotation.
Loading history...
124
125
        self::mockProperty($testDBO, 'dbVariables', ['id', 'name']);
126
        $testDBO->hydrate([
127
            'id' => 1,
128
            'name' => 'test record',
129
            'add_column' => 'test value',
130
            'realProperty' => 'my string',
131
        ]);
132
133
        $this->assertEquals($testDBO->realProperty, 'my string');
0 ignored issues
show
Bug Best Practice introduced by
The property realProperty does not exist on Suricate\DBObject. Since you implemented __get, consider adding a @property annotation.
Loading history...
134
135
        $reflector = new ReflectionClass(get_class($testDBO));
136
        $property = $reflector->getProperty('dbValues');
137
        $property->setAccessible(true);
138
        $this->assertEquals([
139
            'id' => 1,
140
            'name' => 'test record',
141
        ], $property->getValue($testDBO));
142
    }
143
144
    public function testWakeup()
145
    {
146
        $mock = $this->getMockBuilder(\Suricate\DBObject::class)
147
            ->setMethods(['setRelations'])
148
            ->getMock();
149
150
        $mock
151
            ->expects($this->once())
152
            ->method('setRelations');
153
        
154
        $mock->__wakeup();
0 ignored issues
show
Bug introduced by
The method __wakeup() does not exist on PHPUnit\Framework\MockObject\MockObject. ( Ignorable by Annotation )

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

154
        $mock->/** @scrutinizer ignore-call */ 
155
               __wakeup();

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...
155
    }
156
157
    public function testRelations()
158
    {
159
        $relations = [
160
            'category' => [
161
                'type' => \Suricate\DBObject::RELATION_ONE_ONE,
162
                'source' => 'category_id',
163
                'target' => 'Category'
164
            ]
165
        ];
166
        // Prepare database
167
        $this->setupData();
168
        $mock = $this->getMockBuilder(\Suricate\DBObject::class)
169
            ->setMethods(['setRelations', 'getRelation'])
170
            ->getMock();
171
172
        // Prepare setup DBObject
173
        $testDBO = $this->getDBOject();
174
        $reflector = new ReflectionClass($mock);
175
        $property = $reflector->getProperty('relations');
176
        $property->setAccessible(true);
177
        $property->setValue($testDBO, $relations);
178
179
        // get relation values
180
        $reflector = new ReflectionClass($testDBO);
181
        $relationValuesRef = $reflector->getProperty('relationValues');
182
        $relationValuesRef->setAccessible(true);
183
184
        $loadedRelationsRef = $reflector->getProperty('loadedRelations');
185
        $loadedRelationsRef->setAccessible(true);
186
187
        // Load
188
        $testDBO->load(1);
189
        $relationsValues = $relationValuesRef->getValue($testDBO);
190
        $loadedRelations = $loadedRelationsRef->getValue($testDBO);
191
192
        // No relation values at first
193
        $this->assertSame([], $relationsValues);
194
        $this->assertSame([], $loadedRelations);
195
        $this->assertEquals('Admin', $testDBO->category->name);
0 ignored issues
show
Bug Best Practice introduced by
The property category does not exist on Suricate\DBObject. Since you implemented __get, consider adding a @property annotation.
Loading history...
196
        $this->assertInstanceOf('\Suricate\DBObject', $testDBO->category);
197
198
199
        $relationsValues = $relationValuesRef->getValue($testDBO);
200
        $loadedRelations = $loadedRelationsRef->getValue($testDBO);
201
202
        // Check relation cache has been set
203
        $this->assertArrayHasKey('category', $relationsValues);
204
205
        // Check relation loaded flag has been set
206
        $this->assertArrayHasKey('category', $loadedRelations);
207
208
        // Check return type of relation
209
        $this->assertInstanceOf('\Suricate\DBObject', $relationsValues['category']);
210
211
        // Load new object
212
        $testDBO = $this->getDBOject();
213
        $reflector = new ReflectionClass($mock);
214
        $property = $reflector->getProperty('relations');
215
        $property->setAccessible(true);
216
        $property->setValue($testDBO, $relations);
217
        $testDBO->load(2);
218
        // get relation values
219
        $reflector = new ReflectionClass($testDBO);
220
        $relationValuesRef = $reflector->getProperty('relationValues');
221
        $relationValuesRef->setAccessible(true);
222
223
        $loadedRelationsRef = $reflector->getProperty('loadedRelations');
224
        $loadedRelationsRef->setAccessible(true);
225
226
        $relationsValues = $relationValuesRef->getValue($testDBO);
227
        $loadedRelations = $loadedRelationsRef->getValue($testDBO);
228
229
        // No relation values at first
230
        $this->assertSame([], $relationsValues);
231
        $this->assertSame([], $loadedRelations);
232
233
        // Isset implicit load relation, check that's been loaded
234
        $this->assertTrue(isset($testDBO->category));
235
    }
236
237
    public function testLoad()
238
    {
239
        // Prepare database
240
        $this->setupData();
241
242
        // Inject database handler
243
        $testDBO = $this->getDBOject();
244
245
        $this->assertFalse($testDBO->isLoaded());
246
        $retVal = $testDBO->load(1);
247
        $this->assertTrue($testDBO->isLoaded());
248
        $this->assertEquals(1, $testDBO->id);
0 ignored issues
show
Bug Best Practice introduced by
The property id does not exist on Suricate\DBObject. Since you implemented __get, consider adding a @property annotation.
Loading history...
249
250
        $this->assertEquals('John', $testDBO->name);
0 ignored issues
show
Bug Best Practice introduced by
The property name does not exist on Suricate\DBObject. Since you implemented __get, consider adding a @property annotation.
Loading history...
251
        
252
        $this->assertInstanceOf('\Suricate\DBObject', $retVal);
253
    }
254
255
    public function testLoadFromSQL()
256
    {
257
        // Prepare database
258
        $this->setupData();
259
260
        // Inject database handler
261
        $testDBO = $this->getDBOject();
262
263
        $sql = "SELECT * FROM `users` WHERE id=:id";
264
        $params = ['id' => 1];
265
        
266
        $retVal = $testDBO->loadFromSql($sql, $params);
267
        $this->assertInstanceOf('\Suricate\DBObject', $retVal);
268
269
        $params = ['id' => 100];
270
        $retVal = $testDBO->loadFromSql($sql, $params);
271
        $this->assertFalse($retVal);
272
    }
273
274
    public function testLoadOrFail()
275
    {
276
        // Prepare database
277
        $this->setupData();
278
279
        // Inject database handler
280
        $testDBO = $this->getDBOject();
281
282
        
283
        $retVal = $testDBO->loadOrFail(1);
284
        $this->assertInstanceOf('\Suricate\DBObject', $retVal);
285
286
        $this->expectException(\Suricate\Exception\ModelNotFoundException::class);
287
        $retVal = $testDBO->loadOrFail(100);
0 ignored issues
show
Unused Code introduced by
The assignment to $retVal is dead and can be removed.
Loading history...
288
    }
289
290
    public function testToArray()
291
    {
292
        // Prepare database
293
        $this->setupData();
294
295
        // Inject database handler
296
        $testDBO = $this->getDBOject();
297
        $testDBO->load(2);
298
        
299
        $this->assertSame([
300
            'id' => '2',
301
            'category_id' => '100',
302
            'name' => 'Paul',
303
            'date_added' => '2019-01-11 00:00:00',
304
            ],
305
            $testDBO->toArray()
306
        );
307
308
        $testDBO = $this->getDBOject();
309
        $testDBO->load(2);
310
        self::mockProperty($testDBO, 'exportedVariables', [
311
            'id' => 'id', 
312
            'category_id' => 'category_id,type:integer',
313
            'name' => ',omitempty',
314
            'date_added' => '-']
315
        );
316
        $testDBO->name = '';
0 ignored issues
show
Bug Best Practice introduced by
The property name does not exist on Suricate\DBObject. Since you implemented __set, consider adding a @property annotation.
Loading history...
317
318
        $this->assertSame([
319
            'id' => '2',
320
            'category_id' => 100,
321
            ],
322
            $testDBO->toArray()
323
        );
324
    }
325
326
    public function testToJson()
327
    {
328
        // Prepare database
329
        $this->setupData();
330
331
        // Inject database handler
332
        $testDBO = $this->getDBOject();
333
        $testDBO->load(2);
334
335
        $this->assertSame(
336
            '{"id":"2","category_id":"100","name":"Paul","date_added":"2019-01-11 00:00:00"}',
337
            $testDBO->toJson()
338
        );
339
    }
340
341
    public static function mockProperty($object, string $propertyName, $value)
342
    {
343
        $reflectionClass = new \ReflectionClass($object);
344
345
        $property = $reflectionClass->getProperty($propertyName);
346
        $property->setAccessible(true);
347
        $property->setValue($object, $value);
348
        $property->setAccessible(false);
349
    }
350
351
    protected function setupData()
352
    {
353
        $pdo = new PDO('sqlite:/tmp/test.db');
354
        $pdo->exec("DROP TABLE IF EXISTS `users`");
355
        $pdo->exec("DROP TABLE IF EXISTS `categories`");
356
        $pdo->exec("CREATE TABLE `users` (`id` INTEGER PRIMARY KEY,`category_id` INTEGER, `name` varchar(50) DEFAULT NULL,`date_added` datetime NOT NULL)");
357
        $pdo->exec("CREATE TABLE `categories` (`id` INTEGER PRIMARY KEY, `name` varchar(50) DEFAULT NULL)");
358
        
359
        $stmt = $pdo->prepare("INSERT INTO `users` (name, category_id, date_added) VALUES (:name, :categoryid, :date)");
360
        $values = [
361
            ['John', 100, '2019-01-10 00:00:00'],
362
            ['Paul', 100, '2019-01-11 00:00:00'],
363
            ['Robert', 101, '2019-01-12 00:00:00']
364
        ];
365
        foreach ($values as $value) {
366
            $stmt->execute(['name' => $value[0], 'categoryid' => $value[1], 'date' => $value[2]]);
367
        }
368
369
        $stmt = $pdo->prepare("INSERT INTO `categories` (id, name) VALUES (:id, :name)");
370
        $values = [
371
            [100, 'Admin'],
372
            [101, 'Employee']
373
        ];
374
        foreach ($values as $value) {
375
            $stmt->execute(['id' => $value[0], 'name' => $value[1]]);
376
        }
377
    }
378
379
    protected function getDatabase()
380
    {
381
        $database = new \Suricate\Database();
382
        $database->configure([
383
            'type' => 'sqlite',
384
            'file' => '/tmp/test.db',
385
        ]);
386
387
        return $database;
388
    }
389
390
    protected function getDBOject()
391
    {
392
        $dbLink = $this->getDatabase();
393
        // Inject database handler
394
        $testDBO = new \Suricate\DBObject();
395
396
397
        $reflector = new ReflectionClass(get_class($testDBO));
398
        $property = $reflector->getProperty('dbLink');
399
        $property->setAccessible(true);
400
        $property->setValue($testDBO, $dbLink);
401
402
        self::mockProperty($testDBO, 'tableName', $this->tableName);
403
        self::mockProperty($testDBO, 'tableIndex', 'id');
404
        self::mockProperty($testDBO, 'dbVariables', ['id', 'category_id', 'name', 'date_added']);
405
406
        return $testDBO;
407
    }
408
}
409