Passed
Push — develop ( 5cbd49...d5f7f5 )
by Mathieu
01:43
created

DBObjectTest   A

Complexity

Total Complexity 28

Size/Duplication

Total Lines 533
Duplicated Lines 0 %

Importance

Changes 18
Bugs 1 Features 0
Metric Value
eloc 300
dl 0
loc 533
rs 10
c 18
b 1
f 0
wmc 28

26 Methods

Rating   Name   Duplication   Size   Complexity  
A testInstanciate() 0 16 1
A getDatabase() 0 9 1
A testGetTableName() 0 7 1
A testContructor() 0 18 1
A testToJson() 0 12 1
A testToArray() 0 33 1
A testLoad() 0 16 1
A testHydrate() 0 24 1
A testGetDBConfig() 0 7 1
A getCategoryDBOject() 0 17 1
A testWakeup() 0 11 1
A mockProperty() 0 8 1
A testGetTableIndex() 0 7 1
A testLoadOrFail() 0 14 1
A testLoadFromSQL() 0 19 1
A testCreate() 0 15 1
A testProtected() 0 31 1
A setupData() 0 25 3
A testLoadOrInstanciate() 0 37 1
A testIsLoaded() 0 19 1
A testDBProperty() 0 16 1
A testUndefinedGet() 0 7 1
A getDBOject() 0 17 1
A testValidate() 0 4 1
A testIsset() 0 8 1
A testRelations() 0 78 1
1
<?php
2
require_once 'stubs/Category.php';
3
4
/**
5
 * @SuppressWarnings("StaticAccess")
6
 */
7
class DBObjectTest extends \PHPUnit\Framework\TestCase
8
{
9
    protected $tableName = 'users';
10
    public function testContructor()
11
    {
12
        $classname = '\Suricate\DBObject';
13
14
        // Get mock, without the constructor being called
15
        $mock = $this->getMockBuilder($classname)
16
            ->disableOriginalConstructor()
17
            ->setMethods(array('setRelations'))
18
            ->getMockForAbstractClass();
19
20
        // set expectations for constructor calls
21
        $mock->expects($this->once())
22
            ->method('setRelations');
23
24
        // now call the constructor
25
        $reflectedClass = new ReflectionClass($classname);
26
        $constructor = $reflectedClass->getConstructor();
27
        $constructor->invoke($mock);
28
    }
29
30
    public function testGetTableName()
31
    {
32
        $testName = 'my_sql_table';
33
34
        $testDBO = new \Suricate\DBObject();
35
        self::mockProperty($testDBO, 'tableName', $testName);
36
        $this->assertEquals($testName, $testDBO->getTableName());
37
    }
38
39
    public function testGetTableIndex()
40
    {
41
        $testIndex = 'id';
42
43
        $testDBO = new \Suricate\DBObject();
44
        self::mockProperty($testDBO, 'tableIndex', $testIndex);
45
        $this->assertEquals($testIndex, $testDBO->getTableIndex());
46
    }
47
48
    public function testGetDBConfig()
49
    {
50
        $testConfigName = 'my_config';
51
52
        $testDBO = new \Suricate\DBObject();
53
        self::mockProperty($testDBO, 'DBConfig', $testConfigName);
54
        $this->assertEquals($testConfigName, $testDBO->getDBConfig());
55
    }
56
57
    public function testUndefinedGet()
58
    {
59
        $testDBO = new \Suricate\DBObject();
60
        self::mockProperty($testDBO, 'dbVariables', ['id', 'name', 'last_update']);
61
        $this->expectException(\InvalidArgumentException::class);
62
        
63
        $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...
64
    }
65
66
    public function testDBProperty()
67
    {
68
        $testDBO = new \Suricate\DBObject();
69
        $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...
70
        self::mockProperty($testDBO, 'dbVariables', ['id', 'name', 'not_loaded_var']);
71
        self::mockProperty($testDBO, 'dbValues', ['id' => 1, 'name' => 'test name']);
72
        $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...
73
        $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...
74
        $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...
75
76
        $this->assertTrue($testDBO->isDBVariable('id'));
77
        $this->assertFalse($testDBO->isDBVariable('regularProperty'));
78
79
        $this->assertTrue($testDBO->propertyExists('regularProperty'));
80
        $this->assertTrue($testDBO->propertyExists('id'));
81
        $this->assertFalse($testDBO->propertyExists('unknownProperty'));
82
    }
83
84
    public function testIsset()
85
    {
86
        $testDBO = new \Suricate\DBObject();
87
        self::mockProperty($testDBO, 'dbVariables', ['id', 'name', 'not_loaded_var']);
88
        self::mockProperty($testDBO, 'dbValues', ['id' => 1, 'name' => 'test name']);
89
90
        $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...
91
        $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...
92
    }
93
94
    public function testIsLoaded()
95
    {
96
        $testIndex = 'id';
97
98
        $testDBO = new \Suricate\DBObject();
99
        self::mockProperty($testDBO, 'tableIndex', $testIndex);
100
        self::mockProperty($testDBO, 'dbVariables', [$testIndex, 'name', 'not_loaded_var']);
101
        $this->assertFalse($testDBO->isLoaded());
102
103
        self::mockProperty($testDBO, 'dbValues', [$testIndex => 1, 'name' => 'test name']);
104
        $this->assertFalse($testDBO->isLoaded());
105
106
        $this->setupData();
107
        $dbo = $this->getDBOject();
108
        $this->assertFalse($dbo->isLoaded());
109
        $dbo->load(1);
110
        $this->assertTrue($dbo->isLoaded());
111
        $dbo->load(999);
112
        $this->assertFalse($dbo->isLoaded());
113
    }
114
115
    public function testProtected()
116
    {
117
        $testDBO = Category::instanciate([
118
            'id' => 1,
119
            'name' => 'test record',
120
        ]);
121
        $reflector = new ReflectionClass(Category::class);
122
        $property = $reflector->getProperty('protectedValues');
123
        $property->setAccessible(true);
124
        $this->assertSame([], $property->getValue($testDBO));
125
        $this->assertNull($testDBO->unloadable);
0 ignored issues
show
Bug Best Practice introduced by
The property unloadable does not exist on Suricate\DBObject. Since you implemented __get, consider adding a @property annotation.
Loading history...
126
        
127
        $reflector = new ReflectionClass(Category::class);
128
        $property = $reflector->getProperty('protectedValues');
129
        $property->setAccessible(true);
130
        $this->assertSame([], $property->getValue($testDBO));
131
        
132
        $reflector = new ReflectionClass(Category::class);
133
        $property = $reflector->getProperty('loadedProtectedVariables');
134
        $property->setAccessible(true);
135
        $this->assertSame([], $property->getValue($testDBO));
136
137
        $this->assertSame(42, $testDBO->prot_var);
0 ignored issues
show
Bug Best Practice introduced by
The property prot_var does not exist on Suricate\DBObject. Since you implemented __get, consider adding a @property annotation.
Loading history...
138
        $property = $reflector->getProperty('protectedValues');
139
        $property->setAccessible(true);
140
        $this->assertSame(['prot_var' => 42], $property->getValue($testDBO));
141
142
        $reflector = new ReflectionClass(Category::class);
143
        $property = $reflector->getProperty('loadedProtectedVariables');
144
        $property->setAccessible(true);
145
        $this->assertSame(['prot_var' => true], $property->getValue($testDBO));
146
    }
147
    public function testInstanciate()
148
    {
149
        $testDBO = Category::instanciate([
150
            'id' => 1,
151
            'name' => 'test record',
152
        ]);
153
154
        $reflector = new ReflectionClass(Category::class);
155
        $property = $reflector->getProperty('dbValues');
156
        $property->setAccessible(true);
157
        $this->assertEquals([
158
            'id' => 1,
159
            'name' => 'test record',
160
        ], $property->getValue($testDBO));
161
162
        $this->assertFalse($testDBO->isLoaded());
163
    }
164
165
    public function testHydrate()
166
    {
167
        $testDBO = new \Suricate\DBObject();
168
        $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...
169
170
        self::mockProperty($testDBO, 'dbVariables', ['id', 'name']);
171
        $testDBO->hydrate([
172
            'id' => 1,
173
            'name' => 'test record',
174
            'add_column' => 'test value',
175
            'realProperty' => 'my string',
176
        ]);
177
178
        $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...
179
180
        $reflector = new ReflectionClass(get_class($testDBO));
181
        $property = $reflector->getProperty('dbValues');
182
        $property->setAccessible(true);
183
        $this->assertEquals([
184
            'id' => 1,
185
            'name' => 'test record',
186
        ], $property->getValue($testDBO));
187
188
        $this->assertFalse($testDBO->isLoaded());
189
    }
190
191
    public function testWakeup()
192
    {
193
        $mock = $this->getMockBuilder(\Suricate\DBObject::class)
194
            ->setMethods(['setRelations'])
195
            ->getMock();
196
197
        $mock
198
            ->expects($this->once())
199
            ->method('setRelations');
200
        
201
        $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

201
        $mock->/** @scrutinizer ignore-call */ 
202
               __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...
202
    }
203
204
    public function testRelations()
205
    {
206
        $relations = [
207
            'category' => [
208
                'type' => \Suricate\DBObject::RELATION_ONE_ONE,
209
                'source' => 'category_id',
210
                'target' => 'Category'
211
            ]
212
        ];
213
        // Prepare database
214
        $this->setupData();
215
        $mock = $this->getMockBuilder(\Suricate\DBObject::class)
216
            ->setMethods(['setRelations', 'getRelation'])
217
            ->getMock();
218
219
        // Prepare setup DBObject
220
        $testDBO = $this->getDBOject();
221
        $reflector = new ReflectionClass($mock);
222
        $property = $reflector->getProperty('relations');
223
        $property->setAccessible(true);
224
        $property->setValue($testDBO, $relations);
225
226
        // get relation values
227
        $reflector = new ReflectionClass($testDBO);
228
        $relationValuesRef = $reflector->getProperty('relationValues');
229
        $relationValuesRef->setAccessible(true);
230
231
        $loadedRelationsRef = $reflector->getProperty('loadedRelations');
232
        $loadedRelationsRef->setAccessible(true);
233
234
        // Load
235
        $testDBO->load(1);
236
        $relationsValues = $relationValuesRef->getValue($testDBO);
237
        $loadedRelations = $loadedRelationsRef->getValue($testDBO);
238
239
        // No relation values at first
240
        $this->assertSame([], $relationsValues);
241
        $this->assertSame([], $loadedRelations);
242
        $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...
243
        $this->assertInstanceOf('\Suricate\DBObject', $testDBO->category);
244
245
246
        $relationsValues = $relationValuesRef->getValue($testDBO);
247
        $loadedRelations = $loadedRelationsRef->getValue($testDBO);
248
249
        // Check relation cache has been set
250
        $this->assertArrayHasKey('category', $relationsValues);
251
252
        // Check relation loaded flag has been set
253
        $this->assertArrayHasKey('category', $loadedRelations);
254
255
        // Check return type of relation
256
        $this->assertInstanceOf('\Suricate\DBObject', $relationsValues['category']);
257
258
        // Load new object
259
        $testDBO = $this->getDBOject();
260
        $reflector = new ReflectionClass($mock);
261
        $property = $reflector->getProperty('relations');
262
        $property->setAccessible(true);
263
        $property->setValue($testDBO, $relations);
264
        $testDBO->load(2);
265
        // get relation values
266
        $reflector = new ReflectionClass($testDBO);
267
        $relationValuesRef = $reflector->getProperty('relationValues');
268
        $relationValuesRef->setAccessible(true);
269
270
        $loadedRelationsRef = $reflector->getProperty('loadedRelations');
271
        $loadedRelationsRef->setAccessible(true);
272
273
        $relationsValues = $relationValuesRef->getValue($testDBO);
274
        $loadedRelations = $loadedRelationsRef->getValue($testDBO);
275
276
        // No relation values at first
277
        $this->assertSame([], $relationsValues);
278
        $this->assertSame([], $loadedRelations);
279
280
        // Isset implicit load relation, check that's been loaded
281
        $this->assertTrue(isset($testDBO->category));
282
    }
283
284
    public function testLoad()
285
    {
286
        // Prepare database
287
        $this->setupData();
288
289
        // Inject database handler
290
        $testDBO = $this->getDBOject();
291
292
        $this->assertFalse($testDBO->isLoaded());
293
        $retVal = $testDBO->load(1);
294
        $this->assertTrue($testDBO->isLoaded());
295
        $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...
296
297
        $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...
298
        
299
        $this->assertInstanceOf('\Suricate\DBObject', $retVal);
300
    }
301
302
    public function testLoadFromSQL()
303
    {
304
        // Prepare database
305
        $this->setupData();
306
307
        // Inject database handler
308
        $testDBO = $this->getDBOject();
309
310
        $sql = "SELECT * FROM `users` WHERE id=:id";
311
        $params = ['id' => 1];
312
        
313
        $retVal = $testDBO->loadFromSql($sql, $params);
314
        $this->assertInstanceOf('\Suricate\DBObject', $retVal);
315
        $this->assertTrue($testDBO->isLoaded());
316
317
        $params = ['id' => 100];
318
        $retVal = $testDBO->loadFromSql($sql, $params);
319
        $this->assertFalse($retVal);
320
        $this->assertFalse($testDBO->isLoaded());
321
    }
322
323
    public function testLoadOrFail()
324
    {
325
        // Prepare database
326
        $this->setupData();
327
328
        // Inject database handler
329
        $testDBO = $this->getDBOject();
330
331
        
332
        $retVal = $testDBO->loadOrFail(1);
333
        $this->assertInstanceOf('\Suricate\DBObject', $retVal);
334
335
        $this->expectException(\Suricate\Exception\ModelNotFoundException::class);
336
        $testDBO->loadOrFail(100);
337
    }
338
339
    public function testLoadOrInstanciate()
340
    {
341
        // Prepare database
342
        $this->setupData();
343
344
        $testDBO = Category::loadOrInstanciate(100);
345
346
        $comparison = $this->getCategoryDBOject();
347
        $comparison->load(100);
348
349
        $this->assertInstanceOf('\Suricate\DBObject', $testDBO);
350
        $this->assertInstanceOf('Category', $testDBO);
351
352
        $this->assertSame($comparison->id, $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...
353
        $this->assertSame($comparison->name, $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...
354
        $this->assertTrue($testDBO->isLoaded());
355
356
        // non existing
357
        $testDBO = Category::loadOrInstanciate(102);
358
        $this->assertFalse($testDBO->isLoaded()); // has been instanciated, not loaded
359
        $this->assertSame($testDBO->id, "102");
360
        $this->assertSame($testDBO->name, null);
361
362
        $testDBO = Category::loadOrInstanciate(['id' => 102, 'name' => 'test name']);
363
        $this->assertFalse($testDBO->isLoaded());
364
        $this->assertSame($testDBO->id, "102");
365
        $this->assertSame($testDBO->name, 'test name');
366
367
        $testDBO = Category::loadOrInstanciate(['id' => 101, 'name' => 'test name']);
368
        $this->assertFalse($testDBO->isLoaded());
369
        $this->assertSame($testDBO->id, "101");
370
        $this->assertSame($testDBO->name, 'test name');
371
372
        $testDBO = Category::loadOrInstanciate(['id' => 101, 'name' => 'Employee']);
373
        $this->assertTrue($testDBO->isLoaded());
374
        $this->assertSame($testDBO->id, "101");
375
        $this->assertSame($testDBO->name, 'Employee');
376
    }
377
378
    public function testCreate()
379
    {
380
        // Prepare database
381
        $this->setupData();
382
        $comparison = $this->getCategoryDBOject();
383
384
        $testDBO = Category::create(['id' => 1020]);
385
386
        $this->assertInstanceOf('\Suricate\DBObject', $testDBO);
387
        $this->assertTrue($testDBO->isLoaded());
388
389
        $comparison->load(1020);
390
391
        $this->assertSame($comparison->id, $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...
392
        $this->assertSame($comparison->name, null);
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...
393
        
394
    }
395
396
397
    public function testToArray()
398
    {
399
        // Prepare database
400
        $this->setupData();
401
402
        // Inject database handler
403
        $testDBO = $this->getDBOject();
404
        $testDBO->load(2);
405
        
406
        $this->assertSame([
407
            'id' => '2',
408
            'category_id' => '100',
409
            'name' => 'Paul',
410
            'date_added' => '2019-01-11 00:00:00',
411
            ],
412
            $testDBO->toArray()
413
        );
414
415
        $testDBO = $this->getDBOject();
416
        $testDBO->load(2);
417
        self::mockProperty($testDBO, 'exportedVariables', [
418
            'id' => 'id', 
419
            'category_id' => 'category_id,type:integer',
420
            'name' => ',omitempty',
421
            'date_added' => '-']
422
        );
423
        $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...
424
425
        $this->assertSame([
426
            'id' => '2',
427
            'category_id' => 100,
428
            ],
429
            $testDBO->toArray()
430
        );
431
    }
432
433
    public function testToJson()
434
    {
435
        // Prepare database
436
        $this->setupData();
437
438
        // Inject database handler
439
        $testDBO = $this->getDBOject();
440
        $testDBO->load(2);
441
442
        $this->assertSame(
443
            '{"id":"2","category_id":"100","name":"Paul","date_added":"2019-01-11 00:00:00"}',
444
            $testDBO->toJson()
445
        );
446
    }
447
448
    public function testValidate()
449
    {
450
        $testDBO = $this->getDBOject();
451
        $this->assertTrue($testDBO->validate());
452
453
    }
454
455
    public static function mockProperty($object, string $propertyName, $value)
456
    {
457
        $reflectionClass = new \ReflectionClass($object);
458
459
        $property = $reflectionClass->getProperty($propertyName);
460
        $property->setAccessible(true);
461
        $property->setValue($object, $value);
462
        $property->setAccessible(false);
463
    }
464
465
    protected function setupData()
466
    {
467
        $pdo = new PDO('sqlite:/tmp/test.db');
468
        $pdo->exec("DROP TABLE IF EXISTS `users`");
469
        $pdo->exec("DROP TABLE IF EXISTS `categories`");
470
        $pdo->exec("CREATE TABLE `users` (`id` INTEGER PRIMARY KEY,`category_id` INTEGER, `name` varchar(50) DEFAULT NULL,`date_added` datetime NOT NULL)");
471
        $pdo->exec("CREATE TABLE `categories` (`id` INTEGER PRIMARY KEY, `name` varchar(50) DEFAULT NULL, `parent_id` INTEGER DEFAULT NULL)");
472
        
473
        $stmt = $pdo->prepare("INSERT INTO `users` (name, category_id, date_added) VALUES (:name, :categoryid, :date)");
474
        $values = [
475
            ['John', 100, '2019-01-10 00:00:00'],
476
            ['Paul', 100, '2019-01-11 00:00:00'],
477
            ['Robert', 101, '2019-01-12 00:00:00']
478
        ];
479
        foreach ($values as $value) {
480
            $stmt->execute(['name' => $value[0], 'categoryid' => $value[1], 'date' => $value[2]]);
481
        }
482
483
        $stmt = $pdo->prepare("INSERT INTO `categories` (id, name) VALUES (:id, :name)");
484
        $values = [
485
            [100, 'Admin'],
486
            [101, 'Employee']
487
        ];
488
        foreach ($values as $value) {
489
            $stmt->execute(['id' => $value[0], 'name' => $value[1]]);
490
        }
491
    }
492
493
    protected function getDatabase()
494
    {
495
        $database = new \Suricate\Database();
496
        $database->configure([
497
            'type' => 'sqlite',
498
            'file' => '/tmp/test.db',
499
        ]);
500
501
        return $database;
502
    }
503
504
    protected function getDBOject()
505
    {
506
        $dbLink = $this->getDatabase();
507
        // Inject database handler
508
        $testDBO = new \Suricate\DBObject();
509
510
511
        $reflector = new ReflectionClass(get_class($testDBO));
512
        $property = $reflector->getProperty('dbLink');
513
        $property->setAccessible(true);
514
        $property->setValue($testDBO, $dbLink);
515
516
        self::mockProperty($testDBO, 'tableName', $this->tableName);
517
        self::mockProperty($testDBO, 'tableIndex', 'id');
518
        self::mockProperty($testDBO, 'dbVariables', ['id', 'category_id', 'name', 'date_added']);
519
520
        return $testDBO;
521
    }
522
523
    protected function getCategoryDBOject()
524
    {
525
        $dbLink = $this->getDatabase();
526
        // Inject database handler
527
        $testDBO = new \Suricate\DBObject();
528
529
530
        $reflector = new ReflectionClass(get_class($testDBO));
531
        $property = $reflector->getProperty('dbLink');
532
        $property->setAccessible(true);
533
        $property->setValue($testDBO, $dbLink);
534
535
        self::mockProperty($testDBO, 'tableName', 'categories');
536
        self::mockProperty($testDBO, 'tableIndex', 'id');
537
        self::mockProperty($testDBO, 'dbVariables', ['id','name']);
538
539
        return $testDBO;
540
    }
541
}
542