Passed
Push — master ( 679088...d84efc )
by
unknown
08:49
created

tests/TDBMDaoGeneratorTest.php (7 issues)

1
<?php
2
declare(strict_types=1);
3
4
/*
5
 Copyright (C) 2006-2018 David Négrier - THE CODING MACHINE
6
7
This program is free software; you can redistribute it and/or modify
8
it under the terms of the GNU General Public License as published by
9
the Free Software Foundation; either version 2 of the License, or
10
(at your option) any later version.
11
12
This program is distributed in the hope that it will be useful,
13
but WITHOUT ANY WARRANTY; without even the implied warranty of
14
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15
GNU General Public License for more details.
16
17
You should have received a copy of the GNU General Public License
18
along with this program; if not, write to the Free Software
19
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
20
*/
21
22
namespace TheCodingMachine\TDBM;
23
24
use Doctrine\Common\Cache\ArrayCache;
25
use Doctrine\DBAL\Exception\ForeignKeyConstraintViolationException;
26
use Doctrine\DBAL\Exception\UniqueConstraintViolationException;
27
use Doctrine\DBAL\Platforms\MySQL57Platform;
28
use Doctrine\DBAL\Platforms\MySqlPlatform;
29
use Doctrine\DBAL\Platforms\OraclePlatform;
30
use Mouf\Database\SchemaAnalyzer\SchemaAnalyzer;
31
use Ramsey\Uuid\Uuid;
32
use ReflectionClass;
33
use ReflectionMethod;
34
use ReflectionNamedType;
35
use TheCodingMachine\TDBM\Dao\TestAlbumDao;
36
use TheCodingMachine\TDBM\Dao\TestArticleDao;
37
use TheCodingMachine\TDBM\Dao\TestArticleSubQueryDao;
38
use TheCodingMachine\TDBM\Dao\TestCountryDao;
39
use TheCodingMachine\TDBM\Dao\TestPersonDao;
40
use TheCodingMachine\TDBM\Dao\TestRoleDao;
41
use TheCodingMachine\TDBM\Dao\TestUserDao;
42
use TheCodingMachine\TDBM\Fixtures\Interfaces\TestUserDaoInterface;
43
use TheCodingMachine\TDBM\Fixtures\Interfaces\TestUserInterface;
44
use TheCodingMachine\TDBM\Test\Dao\AlbumDao;
45
use TheCodingMachine\TDBM\Test\Dao\AllNullableDao;
46
use TheCodingMachine\TDBM\Test\Dao\AnimalDao;
47
use TheCodingMachine\TDBM\Test\Dao\ArtistDao;
48
use TheCodingMachine\TDBM\Test\Dao\BaseObjectDao;
49
use TheCodingMachine\TDBM\Test\Dao\Bean\AccountBean;
50
use TheCodingMachine\TDBM\Test\Dao\Bean\AlbumBean;
51
use TheCodingMachine\TDBM\Test\Dao\Bean\AllNullableBean;
52
use TheCodingMachine\TDBM\Test\Dao\Bean\AnimalBean;
53
use TheCodingMachine\TDBM\Test\Dao\Bean\Article2Bean;
54
use TheCodingMachine\TDBM\Test\Dao\Bean\ArticleBean;
55
use TheCodingMachine\TDBM\Test\Dao\Bean\ArtistBean;
56
use TheCodingMachine\TDBM\Test\Dao\Bean\BaseObjectBean;
57
use TheCodingMachine\TDBM\Test\Dao\Bean\BoatBean;
58
use TheCodingMachine\TDBM\Test\Dao\Bean\CatBean;
59
use TheCodingMachine\TDBM\Test\Dao\Bean\CategoryBean;
60
use TheCodingMachine\TDBM\Test\Dao\Bean\CountryBean;
61
use TheCodingMachine\TDBM\Test\Dao\Bean\DogBean;
62
use TheCodingMachine\TDBM\Test\Dao\Bean\FileBean;
63
use TheCodingMachine\TDBM\Test\Dao\Bean\Generated\ArticleBaseBean;
64
use TheCodingMachine\TDBM\Test\Dao\Bean\Generated\BoatBaseBean;
65
use TheCodingMachine\TDBM\Test\Dao\Bean\Generated\FileBaseBean;
66
use TheCodingMachine\TDBM\Test\Dao\Bean\Generated\UserBaseBean;
67
use TheCodingMachine\TDBM\Test\Dao\Bean\InheritedObjectBean;
68
use TheCodingMachine\TDBM\Test\Dao\Bean\NodeBean;
69
use TheCodingMachine\TDBM\Test\Dao\Bean\PersonBean;
70
use TheCodingMachine\TDBM\Test\Dao\Bean\PlayerBean;
71
use TheCodingMachine\TDBM\Test\Dao\Bean\RefNoPrimKeyBean;
72
use TheCodingMachine\TDBM\Test\Dao\Bean\RoleBean;
73
use TheCodingMachine\TDBM\Test\Dao\Bean\StateBean;
74
use TheCodingMachine\TDBM\Test\Dao\Bean\UserBean;
75
use TheCodingMachine\TDBM\Test\Dao\BoatDao;
76
use TheCodingMachine\TDBM\Test\Dao\CatDao;
77
use TheCodingMachine\TDBM\Test\Dao\CategoryDao;
78
use TheCodingMachine\TDBM\Test\Dao\CompositeFkSourceDao;
79
use TheCodingMachine\TDBM\Test\Dao\ContactDao;
80
use TheCodingMachine\TDBM\Test\Dao\CountryDao;
81
use TheCodingMachine\TDBM\Test\Dao\DogDao;
82
use TheCodingMachine\TDBM\Test\Dao\FileDao;
83
use TheCodingMachine\TDBM\Test\Dao\Generated\UserBaseDao;
84
use TheCodingMachine\TDBM\Test\Dao\InheritedObjectDao;
85
use TheCodingMachine\TDBM\Test\Dao\NodeDao;
86
use TheCodingMachine\TDBM\Test\Dao\PersonDao;
87
use TheCodingMachine\TDBM\Test\Dao\PlayerDao;
88
use TheCodingMachine\TDBM\Test\Dao\RefNoPrimKeyDao;
89
use TheCodingMachine\TDBM\Test\Dao\RoleDao;
90
use TheCodingMachine\TDBM\Test\Dao\StateDao;
91
use TheCodingMachine\TDBM\Test\Dao\UserDao;
92
use TheCodingMachine\TDBM\Utils\PathFinder\NoPathFoundException;
93
use TheCodingMachine\TDBM\Utils\PathFinder\PathFinder;
94
use TheCodingMachine\TDBM\Utils\TDBMDaoGenerator;
95
use Symfony\Component\Process\Process;
96
use function get_class;
97
98
class TDBMDaoGeneratorTest extends TDBMAbstractServiceTest
99
{
100
    /** @var TDBMDaoGenerator $tdbmDaoGenerator */
101
    protected $tdbmDaoGenerator;
102
103
    private $rootPath;
104
105
    protected function setUp(): void
106
    {
107
        parent::setUp();
108
        $schemaManager = $this->tdbmService->getConnection()->getSchemaManager();
109
        $schemaAnalyzer = new SchemaAnalyzer($schemaManager);
110
        $schemaLockFileDumper = new SchemaLockFileDumper($this->tdbmService->getConnection(), new ArrayCache(), Configuration::getDefaultLockFilePath());
0 ignored issues
show
Deprecated Code introduced by
The class Doctrine\Common\Cache\ArrayCache has been deprecated: Deprecated without replacement in doctrine/cache 1.11. This class will be dropped in 2.0 ( Ignorable by Annotation )

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

110
        $schemaLockFileDumper = new SchemaLockFileDumper($this->tdbmService->getConnection(), /** @scrutinizer ignore-deprecated */ new ArrayCache(), Configuration::getDefaultLockFilePath());
Loading history...
111
        $tdbmSchemaAnalyzer = new TDBMSchemaAnalyzer($this->tdbmService->getConnection(), new ArrayCache(), $schemaAnalyzer, $schemaLockFileDumper);
0 ignored issues
show
Deprecated Code introduced by
The class Doctrine\Common\Cache\ArrayCache has been deprecated: Deprecated without replacement in doctrine/cache 1.11. This class will be dropped in 2.0 ( Ignorable by Annotation )

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

111
        $tdbmSchemaAnalyzer = new TDBMSchemaAnalyzer($this->tdbmService->getConnection(), /** @scrutinizer ignore-deprecated */ new ArrayCache(), $schemaAnalyzer, $schemaLockFileDumper);
Loading history...
112
        $this->tdbmDaoGenerator = new TDBMDaoGenerator($this->getConfiguration(), $tdbmSchemaAnalyzer);
113
        $this->rootPath = __DIR__ . '/../';
114
        //$this->tdbmDaoGenerator->setComposerFile($this->rootPath.'composer.json');
115
    }
116
117
    public function testGetSchemaCrashWithoutLock()
118
    {
119
        //let's delete the lock file
120
        $schemaFilePath = Configuration::getDefaultLockFilePath();
121
        if (file_exists($schemaFilePath)) {
122
            unlink($schemaFilePath);
123
        }
124
        //let's check we cannot call get schema without a lock file
125
        $schemaAnalyzer = new SchemaAnalyzer(self::getConnection()->getSchemaManager(), new ArrayCache(), 'prefix_');
0 ignored issues
show
Deprecated Code introduced by
The class Doctrine\Common\Cache\ArrayCache has been deprecated: Deprecated without replacement in doctrine/cache 1.11. This class will be dropped in 2.0 ( Ignorable by Annotation )

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

125
        $schemaAnalyzer = new SchemaAnalyzer(self::getConnection()->getSchemaManager(), /** @scrutinizer ignore-deprecated */ new ArrayCache(), 'prefix_');
Loading history...
126
        $schemaLockFileDumper = new SchemaLockFileDumper(self::getConnection(), new ArrayCache(), Configuration::getDefaultLockFilePath());
0 ignored issues
show
Deprecated Code introduced by
The class Doctrine\Common\Cache\ArrayCache has been deprecated: Deprecated without replacement in doctrine/cache 1.11. This class will be dropped in 2.0 ( Ignorable by Annotation )

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

126
        $schemaLockFileDumper = new SchemaLockFileDumper(self::getConnection(), /** @scrutinizer ignore-deprecated */ new ArrayCache(), Configuration::getDefaultLockFilePath());
Loading history...
127
        $tdbmSchemaAnalyzer = new TDBMSchemaAnalyzer(self::getConnection(), new ArrayCache(), $schemaAnalyzer, $schemaLockFileDumper);
0 ignored issues
show
Deprecated Code introduced by
The class Doctrine\Common\Cache\ArrayCache has been deprecated: Deprecated without replacement in doctrine/cache 1.11. This class will be dropped in 2.0 ( Ignorable by Annotation )

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

127
        $tdbmSchemaAnalyzer = new TDBMSchemaAnalyzer(self::getConnection(), /** @scrutinizer ignore-deprecated */ new ArrayCache(), $schemaAnalyzer, $schemaLockFileDumper);
Loading history...
128
        $this->expectException('TheCodingMachine\TDBM\TDBMException');
129
        $schema1 = $tdbmSchemaAnalyzer->getSchema(true);
130
    }
131
132
    public function testDaoGeneration(): void
133
    {
134
        // Remove all previously generated files.
135
        $this->recursiveDelete($this->rootPath . 'src/Test/Dao/');
136
        mkdir($this->rootPath . 'src/Test/Dao/Generated', 0755, true);
137
        // Let's generate a dummy file to see it is indeed removed.
138
        $dummyFile = $this->rootPath . 'src/Test/Dao/Generated/foobar.php';
139
        touch($dummyFile);
140
        $this->assertFileExists($dummyFile);
141
142
        //let's delete the lock file
143
        $schemaFilePath = Configuration::getDefaultLockFilePath();
144
        if (file_exists($schemaFilePath)) {
145
            unlink($schemaFilePath);
146
        }
147
148
        $this->tdbmDaoGenerator->generateAllDaosAndBeans();
149
150
        $this->assertFileNotExists($dummyFile);
151
152
        //Check that the lock file was generated
153
        $this->assertFileExists($schemaFilePath);
154
155
        // Let's require all files to check they are valid PHP!
156
        // Test the daoFactory
157
        require_once $this->rootPath . 'src/Test/Dao/Generated/DaoFactory.php';
158
        // Test the others
159
160
        $beanDescriptors = $this->getDummyGeneratorListener()->getBeanDescriptors();
161
162
        foreach ($beanDescriptors as $beanDescriptor) {
163
            $daoName = $beanDescriptor->getDaoClassName();
164
            $daoBaseName = $beanDescriptor->getBaseDaoClassName();
165
            $beanName = $beanDescriptor->getBeanClassName();
166
            $baseBeanName = $beanDescriptor->getBaseBeanClassName();
167
            require_once $this->rootPath . 'src/Test/Dao/Bean/Generated/' . $baseBeanName . '.php';
168
            require_once $this->rootPath . 'src/Test/Dao/Bean/' . $beanName . '.php';
169
            require_once $this->rootPath . 'src/Test/Dao/Generated/' . $daoBaseName . '.php';
170
            require_once $this->rootPath . 'src/Test/Dao/' . $daoName . '.php';
171
        }
172
173
        // Check that pivot tables do not generate DAOs or beans.
174
        $this->assertFalse(class_exists('TheCodingMachine\\TDBM\\Test\\Dao\\RolesRightDao'));
175
    }
176
177
    public function testGenerationException(): void
178
    {
179
        $configuration = new Configuration('UnknownVendor\\Dao', 'UnknownVendor\\Bean', self::getConnection(), $this->getNamingStrategy());
180
181
        $schemaManager = $this->tdbmService->getConnection()->getSchemaManager();
182
        $schemaAnalyzer = new SchemaAnalyzer($schemaManager);
183
        $schemaLockFileDumper = new SchemaLockFileDumper($this->tdbmService->getConnection(), new ArrayCache(), Configuration::getDefaultLockFilePath());
0 ignored issues
show
Deprecated Code introduced by
The class Doctrine\Common\Cache\ArrayCache has been deprecated: Deprecated without replacement in doctrine/cache 1.11. This class will be dropped in 2.0 ( Ignorable by Annotation )

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

183
        $schemaLockFileDumper = new SchemaLockFileDumper($this->tdbmService->getConnection(), /** @scrutinizer ignore-deprecated */ new ArrayCache(), Configuration::getDefaultLockFilePath());
Loading history...
184
        $tdbmSchemaAnalyzer = new TDBMSchemaAnalyzer($this->tdbmService->getConnection(), new ArrayCache(), $schemaAnalyzer, $schemaLockFileDumper);
0 ignored issues
show
Deprecated Code introduced by
The class Doctrine\Common\Cache\ArrayCache has been deprecated: Deprecated without replacement in doctrine/cache 1.11. This class will be dropped in 2.0 ( Ignorable by Annotation )

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

184
        $tdbmSchemaAnalyzer = new TDBMSchemaAnalyzer($this->tdbmService->getConnection(), /** @scrutinizer ignore-deprecated */ new ArrayCache(), $schemaAnalyzer, $schemaLockFileDumper);
Loading history...
185
        $tdbmDaoGenerator = new TDBMDaoGenerator($configuration, $tdbmSchemaAnalyzer);
186
        $this->rootPath = __DIR__ . '/../../../../';
187
        //$tdbmDaoGenerator->setComposerFile($this->rootPath.'composer.json');
188
189
        $this->expectException(NoPathFoundException::class);
190
        $tdbmDaoGenerator->generateAllDaosAndBeans();
191
    }
192
193
    /**
194
     * Delete a file or recursively delete a directory.
195
     *
196
     * @param string $str Path to file or directory
197
     * @return bool
198
     */
199
    private function recursiveDelete(string $str): bool
200
    {
201
        if (is_file($str)) {
202
            return @unlink($str);
203
        } elseif (is_dir($str)) {
204
            $scan = glob(rtrim($str, '/') . '/*');
205
            foreach ($scan as $index => $path) {
206
                $this->recursiveDelete($path);
207
            }
208
209
            return @rmdir($str);
210
        }
211
        return false;
212
    }
213
214
    /**
215
     * @depends testDaoGeneration
216
     */
217
    public function testGetBeanClassName(): void
218
    {
219
        $this->assertEquals(UserBean::class, $this->tdbmService->getBeanClassName('users'));
220
221
        // Let's create another TDBMService to test the cache.
222
        $configuration = new Configuration('TheCodingMachine\\TDBM\\Test\\Dao\\Bean', 'TheCodingMachine\\TDBM\\Test\\Dao', self::getConnection(), $this->getNamingStrategy(), $this->getCache(), null, null, [$this->getDummyGeneratorListener()]);
223
        $configuration->setPathFinder(new PathFinder(null, dirname(__DIR__, 4)));
224
        $newTdbmService = new TDBMService($configuration);
225
        $this->assertEquals(UserBean::class, $newTdbmService->getBeanClassName('users'));
226
    }
227
228
    /**
229
     * @depends testDaoGeneration
230
     */
231
    public function testGeneratedGetById(): void
232
    {
233
        $contactDao = new ContactDao($this->tdbmService);
234
        $contactBean = $contactDao->getById(1);
235
        $this->assertEquals(1, $contactBean->getId());
236
        $this->assertInstanceOf('\\DateTimeInterface', $contactBean->getCreatedAt());
237
238
        // FIXME: Question: que faire du paramètre stockage "UTC"????
239
    }
240
241
    /**
242
     * @depends testDaoGeneration
243
     */
244
    public function testGeneratedGetByIdLazyLoaded(): void
245
    {
246
        $roleDao = new RoleDao($this->tdbmService);
247
        $roleBean = $roleDao->getById(1, true);
248
        $this->assertEquals(1, $roleBean->getId());
249
        $this->assertInstanceOf('\\DateTimeInterface', $roleBean->getCreatedAt());
250
251
        $roleBean2 = $roleDao->getById(1, true);
252
        $this->assertTrue($roleBean === $roleBean2);
253
    }
254
255
    /**
256
     * @depends testDaoGeneration
257
     */
258
    public function testDefaultValueOnNewBean(): void
259
    {
260
        $roleBean = new RoleBean('my_role');
261
        $this->assertEquals(1, $roleBean->getStatus());
262
    }
263
264
    /**
265
     * @depends testDaoGeneration
266
     */
267
    public function testForeignKeyInBean(): void
268
    {
269
        $userDao = new UserDao($this->tdbmService);
270
        $userBean = $userDao->getById(1);
271
        $country = $userBean->getCountry();
272
273
        $this->assertEquals('uk', $country->getLabel());
274
275
        $userBean2 = $userDao->getById(1);
276
        $this->assertTrue($userBean === $userBean2);
277
278
        $contactDao = new ContactDao($this->tdbmService);
279
        $contactBean = $contactDao->getById(1);
280
281
        $this->assertTrue($userBean === $contactBean);
282
    }
283
284
    /**
285
     * @depends testDaoGeneration
286
     */
287
    public function testNewBeans(): void
288
    {
289
        $countryDao = new CountryDao($this->tdbmService);
290
        $userDao = new UserDao($this->tdbmService);
291
        $userBean = new UserBean('John Doe', '[email protected]', $countryDao->getById(2), 'john.doe');
292
        $userBean->setOrder(1); // Let's set a "protected keyword" column.
293
294
        $userDao->save($userBean);
295
296
        $this->assertNull($userBean->getManager());
297
    }
298
299
    /**
300
     * @depends testDaoGeneration
301
     */
302
    public function testDateTimeImmutableGetter(): void
303
    {
304
        $userDao = new UserDao($this->tdbmService);
305
        $user = $userDao->getById(1);
306
307
        $this->assertInstanceOf('\DateTimeImmutable', $user->getCreatedAt());
308
    }
309
310
    /**
311
     * @depends testDaoGeneration
312
     */
313
    public function testAssigningNewBeans(): void
314
    {
315
        $userDao = new UserDao($this->tdbmService);
316
        $countryBean = new CountryBean('Mexico');
317
        $userBean = new UserBean('Speedy Gonzalez', '[email protected]', $countryBean, 'speedy.gonzalez');
318
        $this->assertEquals($countryBean, $userBean->getCountry());
319
320
        $userDao->save($userBean);
321
    }
322
323
    /**
324
     * @depends testAssigningNewBeans
325
     */
326
    public function testUpdatingProtectedColumn(): void
327
    {
328
        $userDao = new UserDao($this->tdbmService);
329
        $userBean = $userDao->findOneByLogin('speedy.gonzalez');
330
        $userBean->setOrder(2);
331
        $userDao->save($userBean);
332
        $this->assertSame(2, $userBean->getOrder());
333
    }
334
335
    /**
336
     * @depends testDaoGeneration
337
     */
338
    public function testAssigningExistingRelationship(): void
339
    {
340
        $userDao = new UserDao($this->tdbmService);
341
        $user = $userDao->getById(1);
342
        $countryDao = new CountryDao($this->tdbmService);
343
        $country = $countryDao->getById(2);
344
345
        $user->setCountry($country);
346
        $this->assertEquals(TDBMObjectStateEnum::STATE_DIRTY, $user->_getStatus());
347
    }
348
349
    /**
350
     * @depends testDaoGeneration
351
     */
352
    public function testDirectReversedRelationship(): void
353
    {
354
        $countryDao = new CountryDao($this->tdbmService);
355
        $country = $countryDao->getById(1);
356
        $users = $country->getUsers();
357
358
        $arr = $users->toArray();
359
360
        $this->assertCount(1, $arr);
361
        $this->assertInstanceOf('TheCodingMachine\\TDBM\\Test\\Dao\\Bean\\UserBean', $arr[0]);
362
        $this->assertEquals('jean.dupont', $arr[0]->getLogin());
363
364
        $newUser = new UserBean('Speedy Gonzalez', '[email protected]', $country, 'speedy.gonzalez');
365
        $users = $country->getUsers();
366
367
        $arr = $users->toArray();
368
369
        $this->assertCount(2, $arr);
370
        $this->assertInstanceOf('TheCodingMachine\\TDBM\\Test\\Dao\\Bean\\UserBean', $arr[1]);
371
        $this->assertEquals('speedy.gonzalez', $arr[1]->getLogin());
372
    }
373
374
    /**
375
     * @depends testDaoGeneration
376
     */
377
    public function testDeleteInDirectReversedRelationship(): void
378
    {
379
        $countryDao = new CountryDao($this->tdbmService);
380
        $country = $countryDao->getById(1);
381
382
        $userDao = new UserDao($this->tdbmService);
383
        $newUser = new UserBean('John Snow', '[email protected]', $country, 'john.snow');
384
        $userDao->save($newUser);
385
386
        $users = $country->getUsers();
387
388
        $arr = $users->toArray();
389
390
        $this->assertCount(2, $arr);
391
392
        $userDao->delete($arr[1]);
393
394
        $users = $country->getUsers();
395
        $arr = $users->toArray();
396
        $this->assertCount(1, $arr);
397
    }
398
399
    /**
400
     * @depends testDaoGeneration
401
     */
402
    public function testJointureGetters(): void
403
    {
404
        $roleDao = new RoleDao($this->tdbmService);
405
        $role = $roleDao->getById(1);
406
        $users = $role->getUsers();
407
408
        $this->assertCount(2, $users);
409
        $this->assertInstanceOf('TheCodingMachine\\TDBM\\Test\\Dao\\Bean\\UserBean', $users[0]);
410
411
        $rights = $role->getRights();
412
413
        $this->assertCount(2, $rights);
414
        $this->assertInstanceOf('TheCodingMachine\\TDBM\\Test\\Dao\\Bean\\RightBean', $rights[0]);
415
    }
416
417
    /**
418
     * @depends testDaoGeneration
419
     */
420
    public function testNestedIterationOnAlterableResultIterator(): void
421
    {
422
        $countryDao = new CountryDao($this->tdbmService);
423
        $country = $countryDao->getById(2);
424
425
        $count = 0;
426
        // Let's perform 2 nested calls to check that iterators do not melt.
427
        foreach ($country->getUsers() as $user) {
428
            foreach ($country->getUsers() as $user2) {
429
                $count++;
430
            }
431
        }
432
        // There are 3 users linked to country 2.
433
        $this->assertSame(9, $count);
434
    }
435
436
    /**
437
     * @depends testDaoGeneration
438
     */
439
    public function testNewBeanConstructor(): void
440
    {
441
        $role = new RoleBean('Newrole');
442
        $this->assertEquals(TDBMObjectStateEnum::STATE_DETACHED, $role->_getStatus());
443
    }
444
445
    /**
446
     * @depends testDaoGeneration
447
     */
448
    public function testJointureAdderOnNewBean(): void
449
    {
450
        $countryDao = new CountryDao($this->tdbmService);
451
        $country = $countryDao->getById(1);
452
        $user = new UserBean('Speedy Gonzalez', '[email protected]', $country, 'speedy.gonzalez');
453
        $role = new RoleBean('Mouse');
454
        $user->addRole($role);
455
        $roles = $user->getRoles();
456
        $this->assertCount(1, $roles);
457
        $role = $roles[0];
458
        $this->assertInstanceOf('TheCodingMachine\\TDBM\\Test\\Dao\\Bean\\RoleBean', $role);
459
        $users = $role->getUsers();
460
        $this->assertCount(1, $users);
461
        $this->assertEquals($user, $users[0]);
462
463
        $role->removeUser($user);
464
        $this->assertCount(0, $role->getUsers());
465
        $this->assertCount(0, $user->getRoles());
466
    }
467
468
    /**
469
     * @depends testDaoGeneration
470
     */
471
    public function testJointureDeleteBeforeGetters(): void
472
    {
473
        $roleDao = new RoleDao($this->tdbmService);
474
        $userDao = new UserDao($this->tdbmService);
475
        $role = $roleDao->getById(1);
476
        $user = $userDao->getById(1);
477
478
        // We call removeUser BEFORE calling getUsers
479
        // This should work as expected.
480
        $role->removeUser($user);
481
        $users = $role->getUsers();
482
483
        $this->assertCount(1, $users);
484
    }
485
486
    /**
487
     * @depends testDaoGeneration
488
     */
489
    public function testJointureMultiAdd(): void
490
    {
491
        $countryDao = new CountryDao($this->tdbmService);
492
        $country = $countryDao->getById(1);
493
        $user = new UserBean('Speedy Gonzalez', '[email protected]', $country, 'speedy.gonzalez');
494
        $role = new RoleBean('Mouse');
495
        $user->addRole($role);
496
        $role->addUser($user);
497
        $user->addRole($role);
498
499
        $this->assertCount(1, $user->getRoles());
500
    }
501
502
    /**
503
     * Step 1: we remove the role 1 from user 1 but save role 1.
504
     * Expected result: no save done.
505
     *
506
     * @depends testDaoGeneration
507
     */
508
    public function testJointureSave1(): void
509
    {
510
        $roleDao = new RoleDao($this->tdbmService);
511
        $role = $roleDao->getById(1);
512
        $userDao = new UserDao($this->tdbmService);
513
        $user = $userDao->getById(1);
514
515
        $this->assertTrue($user->hasRole($role));
516
        $this->assertTrue($role->hasUser($user));
517
        $user->removeRole($role);
518
        $this->assertFalse($user->hasRole($role));
519
        $this->assertFalse($role->hasUser($user));
520
        $roleDao->save($role);
521
522
        $this->assertEquals(TDBMObjectStateEnum::STATE_DIRTY, $user->_getStatus());
523
        $this->assertEquals(TDBMObjectStateEnum::STATE_LOADED, $role->_getStatus());
524
    }
525
526
    /**
527
     * Step 2: we check that nothing was saved
528
     * Expected result: no save done.
529
     *
530
     * @depends testJointureSave1
531
     */
532
    public function testJointureSave2(): void
533
    {
534
        $roleDao = new RoleDao($this->tdbmService);
535
        $role = $roleDao->getById(1);
536
        $this->assertCount(2, $role->getUsers());
537
    }
538
539
    /**
540
     * Step 3: we remove the role 1 from user 1 and save user 1.
541
     * Expected result: save done.
542
     *
543
     * @depends testJointureSave2
544
     */
545
    public function testJointureSave3(): void
546
    {
547
        $roleDao = new RoleDao($this->tdbmService);
548
        $role = $roleDao->getById(1);
549
        $userDao = new UserDao($this->tdbmService);
550
        $user = $userDao->getById(1);
551
552
        $this->assertCount(1, $user->getRoles());
553
        $user->removeRole($role);
554
        $this->assertCount(0, $user->getRoles());
555
        $userDao->save($user);
556
        $this->assertCount(0, $user->getRoles());
557
    }
558
559
    /**
560
     * Step 4: we check that save was done
561
     * Expected result: save done.
562
     *
563
     * @depends testJointureSave3
564
     */
565
    public function testJointureSave4(): void
566
    {
567
        $roleDao = new RoleDao($this->tdbmService);
568
        $role = $roleDao->getById(1);
569
        $this->assertCount(1, $role->getUsers());
570
        $userDao = new UserDao($this->tdbmService);
571
        $user = $userDao->getById(1);
572
        $this->assertCount(0, $user->getRoles());
573
    }
574
575
    /**
576
     * Step 5: we add the role 1 from user 1 and save user 1.
577
     * Expected result: save done.
578
     *
579
     * @depends testJointureSave4
580
     */
581
    public function testJointureSave5(): void
582
    {
583
        $roleDao = new RoleDao($this->tdbmService);
584
        $role = $roleDao->getById(1);
585
        $userDao = new UserDao($this->tdbmService);
586
        $user = $userDao->getById(1);
587
588
        $user->addRole($role);
589
        $this->assertCount(1, $user->getRoles());
590
        $userDao->save($user);
591
    }
592
593
    /**
594
     * Step 6: we check that save was done
595
     * Expected result: save done.
596
     *
597
     * @depends testJointureSave5
598
     */
599
    public function testJointureSave6(): void
600
    {
601
        $roleDao = new RoleDao($this->tdbmService);
602
        $role = $roleDao->getById(1);
603
        $this->assertCount(2, $role->getUsers());
604
        $userDao = new UserDao($this->tdbmService);
605
        $user = $userDao->getById(1);
606
        $this->assertCount(1, $user->getRoles());
607
    }
608
609
    /**
610
     * Step 7: we add a new role to user 1 and save user 1.
611
     * Expected result: save done, including the new role.
612
     *
613
     * @depends testJointureSave6
614
     */
615
    public function testJointureSave7(): void
616
    {
617
        $role = new RoleBean('my new role');
618
        $userDao = new UserDao($this->tdbmService);
619
        $user = $userDao->getById(1);
620
621
        $user->addRole($role);
622
        $userDao->save($user);
623
624
        $this->assertEquals(TDBMObjectStateEnum::STATE_LOADED, $role->_getStatus());
625
    }
626
627
    /**
628
     * Step 8: we check that save was done
629
     * Expected result: save done.
630
     *
631
     * @depends testJointureSave7
632
     */
633
    public function testJointureSave8(): void
634
    {
635
        $roleDao = new RoleDao($this->tdbmService);
636
        $userDao = new UserDao($this->tdbmService);
637
        $user = $userDao->getById(1);
638
639
        $roles = $user->getRoles();
640
        foreach ($roles as $role) {
641
            if ($role->getName() === 'my new role') {
642
                $selectedRole = $role;
643
                break;
644
            }
645
        }
646
        $this->assertNotNull($selectedRole);
647
648
        $this->assertCount(2, $user->getRoles());
649
650
        // Expected: relationship removed!
651
        $roleDao->delete($selectedRole);
652
653
        $this->assertCount(1, $user->getRoles());
654
    }
655
656
    /**
657
     * Step 9: Let's test the setXXX method.
658
     *
659
     * @depends testJointureSave8
660
     */
661
    public function testJointureSave9(): void
662
    {
663
        $roleDao = new RoleDao($this->tdbmService);
664
        $userDao = new UserDao($this->tdbmService);
665
        $user = $userDao->getById(1);
666
667
        // At this point, user 1 is linked to role 1.
668
        // Let's bind it to role 2.
669
        $user->setRoles([$roleDao->getById(2)]);
670
        $userDao->save($user);
671
        $this->assertTrue($user->hasRole($roleDao->getById(2)));
672
    }
673
674
    /**
675
     * Step 10: Let's check results of 9.
676
     *
677
     * @depends testJointureSave9
678
     */
679
    public function testJointureSave10(): void
680
    {
681
        $userDao = new UserDao($this->tdbmService);
682
        $user = $userDao->getById(1);
683
684
        $roles = $user->getRoles();
685
686
        $this->assertCount(1, $roles);
687
        $this->assertEquals(2, $roles[0]->getId());
688
    }
689
690
    /**
691
     * Test jointure in a parent table in an inheritance relationship
692
     *
693
     * @depends testDaoGeneration
694
     */
695
    public function testJointureInParentTable(): void
696
    {
697
        $userDao = new UserDao($this->tdbmService);
698
        $user = $userDao->getById(1);
699
700
        $boats = $user->getBoats();
701
702
        $this->assertCount(1, $boats);
703
        $this->assertEquals(1, $boats[0]->getId());
704
    }
705
706
    /**
707
     * @depends testDaoGeneration
708
     */
709
    public function testFindOrderBy(): void
710
    {
711
        $userDao = new TestUserDao($this->tdbmService);
712
        $users = $userDao->getUsersByAlphabeticalOrder();
713
714
        $this->assertCount(6, $users);
715
        $this->assertEquals('bill.shakespeare', $users[0]->getLogin());
716
        $this->assertEquals('jean.dupont', $users[1]->getLogin());
717
718
        $users = $userDao->getUsersByCountryOrder();
719
        $this->assertCount(6, $users);
720
        $countryName1 = $users[0]->getCountry()->getLabel();
721
        for ($i = 1; $i < 6; $i++) {
722
            $countryName2 = $users[$i]->getCountry()->getLabel();
723
            $this->assertLessThanOrEqual(0, strcmp($countryName1, $countryName2));
724
            $countryName1 = $countryName2;
725
        }
726
    }
727
728
    /**
729
     * @depends testDaoGeneration
730
     */
731
    public function testFindFromSqlOrderBy(): void
732
    {
733
        $userDao = new TestUserDao($this->tdbmService);
734
        $users = $userDao->getUsersFromSqlByAlphabeticalOrder();
735
736
        $this->assertCount(6, $users);
737
        $this->assertEquals('bill.shakespeare', $users[0]->getLogin());
738
        $this->assertEquals('jean.dupont', $users[1]->getLogin());
739
740
        $users = $userDao->getUsersFromSqlByCountryOrder();
741
        $this->assertCount(6, $users);
742
        $countryName1 = $users[0]->getCountry()->getLabel();
743
        for ($i = 1; $i < 6; $i++) {
744
            $countryName2 = $users[$i]->getCountry()->getLabel();
745
            $this->assertLessThanOrEqual(0, strcmp($countryName1, $countryName2));
746
            $countryName1 = $countryName2;
747
        }
748
    }
749
750
    /**
751
     * @depends testDaoGeneration
752
     */
753
    public function testFindFromSqlOrderByOnInheritedBean(): void
754
    {
755
        $articleDao = new TestArticleDao($this->tdbmService);
756
        $articles = $articleDao->getArticlesByUserLogin();
757
758
        foreach ($articles as $article) {
759
            var_dump($article);
760
        }
761
        $this->assertCount(0, $articles);
762
    }
763
764
765
    /**
766
     * @depends testDaoGeneration
767
     */
768
    public function testFindFromSqlOrderByJoinRole(): void
769
    {
770
        $roleDao = new TestRoleDao($this->tdbmService);
771
        $roles = $roleDao->getRolesByRightCanSing('roles.name DESC');
772
773
        $this->assertCount(2, $roles);
774
        $this->assertEquals('Singers', $roles[0]->getName());
775
        $this->assertEquals('Admins', $roles[1]->getName());
776
    }
777
778
    /**
779
     * @depends testDaoGeneration
780
     */
781
    public function testFindFromRawSqlOrderByUserCount(): void
782
    {
783
        $countryDao = new TestCountryDao($this->tdbmService);
784
        $countries = $countryDao->getCountriesByUserCount();
785
786
        $nbCountries = 4;
787
        $this->assertCount($nbCountries, $countries);
788
        for ($i = 1; $i < $nbCountries; $i++) {
789
            $this->assertLessThanOrEqual($countries[$i - 1]->getUsers()->count(), $countries[$i]->getUsers()->count());
790
        }
791
    }
792
793
    /**
794
     * @depends testDaoGeneration
795
     */
796
    public function testFindFromRawSqlWithUnion(): void
797
    {
798
        $countryDao = new TestCountryDao($this->tdbmService);
799
        $countries = $countryDao->getCountriesUsingUnion();
800
801
        $this->assertCount(2, $countries);
802
        $this->assertEquals(1, $countries[0]->getId());
803
    }
804
805
    /**
806
     * @depends testDaoGeneration
807
     */
808
    public function testFindFromRawSqlWithSimpleQuery(): void
809
    {
810
        $countryDao = new TestCountryDao($this->tdbmService);
811
        $countries = $countryDao->getCountriesUsingSimpleQuery();
812
813
        $this->assertCount(1, $countries);
814
        $this->assertEquals(1, $countries[0]->getId());
815
    }
816
817
    /**
818
     * @depends testDaoGeneration
819
     */
820
    public function testFindFromRawSqlWithDistinctQuery(): void
821
    {
822
        $countryDao = new TestCountryDao($this->tdbmService);
823
        $countries = $countryDao->getCountriesUsingDistinctQuery();
824
825
        $this->assertCount(1, $countries);
826
        $this->assertEquals(2, $countries[0]->getId());
827
    }
828
829
    /**
830
     * @depends testDaoGeneration
831
     */
832
    public function testFindFilters(): void
833
    {
834
        $userDao = new TestUserDao($this->tdbmService);
835
        $users = $userDao->getUsersByLoginStartingWith('bill');
836
837
        $this->assertCount(1, $users);
838
        $this->assertEquals('bill.shakespeare', $users[0]->getLogin());
839
    }
840
841
    /**
842
     * @depends testDaoGeneration
843
     */
844
    public function testFindMode(): void
845
    {
846
        $userDao = new TestUserDao($this->tdbmService);
847
        $users = $userDao->getUsersByLoginStartingWith('bill', TDBMService::MODE_CURSOR);
848
849
        $this->expectException('TheCodingMachine\TDBM\TDBMException');
850
        $users[0];
851
    }
852
853
    /**
854
     * @depends testDaoGeneration
855
     */
856
    public function testFindAll(): void
857
    {
858
        $userDao = new TestUserDao($this->tdbmService);
859
        $users = $userDao->findAll();
860
861
        $this->assertCount(6, $users);
862
    }
863
864
    /**
865
     * @depends testDaoGeneration
866
     */
867
    public function testFindOne(): void
868
    {
869
        $userDao = new TestUserDao($this->tdbmService);
870
        $user = $userDao->getUserByLogin('bill.shakespeare');
871
872
        $this->assertEquals('bill.shakespeare', $user->getLogin());
873
    }
874
875
    /**
876
     * @depends testDaoGeneration
877
     */
878
    public function testJsonEncodeBean(): void
879
    {
880
        $userDao = new TestUserDao($this->tdbmService);
881
        $user = $userDao->getUserByLogin('bill.shakespeare');
882
883
        $jsonEncoded = json_encode($user);
884
        $userDecoded = json_decode($jsonEncoded, true);
885
886
        $this->assertEquals('bill.shakespeare', $userDecoded['login']);
887
888
        // test serialization of dates.
889
        $this->assertTrue(is_string($userDecoded['createdAt']));
890
        $this->assertEquals('2015-10-24', (new \DateTimeImmutable($userDecoded['createdAt']))->format('Y-m-d'));
891
        $this->assertNull($userDecoded['modifiedAt']);
892
893
        // testing many to 1 relationships
894
        $this->assertEquals('uk', $userDecoded['country']['label']);
895
896
        // testing many to many relationships
897
        $this->assertCount(1, $userDecoded['roles']);
898
        $this->assertArrayNotHasKey('users', $userDecoded['roles'][0]);
899
        $this->assertArrayNotHasKey('rights', $userDecoded['roles'][0]);
900
    }
901
902
    /**
903
     * @depends testDaoGeneration
904
     */
905
    public function testNullableForeignKey(): void
906
    {
907
        $userDao = new TestUserDao($this->tdbmService);
908
        $user = $userDao->getUserByLogin('john.smith');
909
910
        $this->assertNull($user->getManager());
911
912
        $jsonEncoded = json_encode($user);
913
        $userDecoded = json_decode($jsonEncoded, true);
914
915
        $this->assertNull($userDecoded['manager']);
916
    }
917
918
    /**
919
     * Test that setting (and saving) objects' references (foreign keys relations) to null is working.
920
     *
921
     * @depends testDaoGeneration
922
     */
923
    public function testSetToNullForeignKey(): void
924
    {
925
        $userDao = new TestUserDao($this->tdbmService);
926
        $user = $userDao->getUserByLogin('john.smith');
927
        $manager = $userDao->getUserByLogin('jean.dupont');
928
929
        $user->setManager($manager);
930
        $userDao->save($user);
931
932
        $user->setManager(null);
933
        $userDao->save($user);
934
        $this->assertNull($user->getManager());
935
    }
936
937
    /**
938
     * @depends testDaoGeneration
939
     */
940
    public function testQueryOnWrongTableName(): void
941
    {
942
        $userDao = new TestUserDao($this->tdbmService);
943
        $users = $userDao->getUsersWrongTableName();
944
        $this->expectException('Mouf\Database\SchemaAnalyzer\SchemaAnalyzerTableNotFoundException');
945
        $this->expectExceptionMessage('Could not find table \'contacts\'. Did you mean \'contact\'?');
946
        $users->count();
947
    }
948
949
    /**
950
     * @depends testDaoGeneration
951
     */
952
    /*public function testQueryNullForeignKey(): void
953
    {
954
        $userDao = new TestUserDao($this->tdbmService);
955
        $users = $userDao->getUsersByManagerId(null);
956
        $this->assertCount(3, $users);
957
    }*/
958
959
    /**
960
     * @depends testDaoGeneration
961
     */
962
    public function testInnerJsonEncode(): void
963
    {
964
        $userDao = new TestUserDao($this->tdbmService);
965
        $user = $userDao->getUserByLogin('bill.shakespeare');
966
967
        $jsonEncoded = json_encode(['user' => $user]);
968
        $msgDecoded = json_decode($jsonEncoded, true);
969
970
        $this->assertEquals('bill.shakespeare', $msgDecoded['user']['login']);
971
    }
972
973
    /**
974
     * @depends testDaoGeneration
975
     */
976
    public function testArrayJsonEncode(): void
977
    {
978
        $userDao = new TestUserDao($this->tdbmService);
979
        $users = $userDao->getUsersByLoginStartingWith('bill');
980
981
        $jsonEncoded = json_encode($users);
982
        $msgDecoded = json_decode($jsonEncoded, true);
983
984
        $this->assertCount(1, $msgDecoded);
985
    }
986
987
    /**
988
     * @depends testDaoGeneration
989
     */
990
    public function testCursorJsonEncode(): void
991
    {
992
        $userDao = new TestUserDao($this->tdbmService);
993
        $users = $userDao->getUsersByLoginStartingWith('bill', TDBMService::MODE_CURSOR);
994
995
        $jsonEncoded = json_encode($users);
996
        $msgDecoded = json_decode($jsonEncoded, true);
997
998
        $this->assertCount(1, $msgDecoded);
999
    }
1000
1001
    /**
1002
     * @depends testDaoGeneration
1003
     */
1004
    public function testPageJsonEncode(): void
1005
    {
1006
        $userDao = new TestUserDao($this->tdbmService);
1007
        $users = $userDao->getUsersByLoginStartingWith('bill');
1008
1009
        $jsonEncoded = json_encode($users->take(0, 1));
1010
        $msgDecoded = json_decode($jsonEncoded, true);
1011
1012
        $this->assertCount(1, $msgDecoded);
1013
    }
1014
1015
    /**
1016
     * @depends testDaoGeneration
1017
     */
1018
    public function testInnerResultIteratorCountAfterFetch(): void
1019
    {
1020
        $userDao = new TestUserDao($this->tdbmService);
1021
        $users = $userDao->getUsersByLoginStartingWith('j')->take(0, 4);
1022
        $users->toArray(); // We force to fetch
1023
        $this->assertEquals(3, $users->count());
1024
    }
1025
1026
    /**
1027
     * @depends testDaoGeneration
1028
     */
1029
    public function testFirst(): void
1030
    {
1031
        $userDao = new TestUserDao($this->tdbmService);
1032
        $users = $userDao->getUsersByLoginStartingWith('bill');
1033
1034
        $bill = $users->first();
1035
        $this->assertEquals('bill.shakespeare', $bill->getLogin());
1036
    }
1037
1038
    /**
1039
     * @depends testDaoGeneration
1040
     */
1041
    public function testFirstNull(): void
1042
    {
1043
        $userDao = new TestUserDao($this->tdbmService);
1044
        $users = $userDao->getUsersByLoginStartingWith('mike');
1045
1046
        $user = $users->first();
1047
        $this->assertNull($user);
1048
    }
1049
1050
    /**
1051
     * @depends testDaoGeneration
1052
     */
1053
    public function testCloneBeanAttachedBean(): void
1054
    {
1055
        $userDao = new TestUserDao($this->tdbmService);
1056
        $user = $userDao->getUserByLogin('bill.shakespeare');
1057
        $this->assertEquals(4, $user->getId());
1058
        $user2 = clone $user;
1059
        $this->assertNull($user2->getId());
1060
        $this->assertEquals('bill.shakespeare', $user2->getLogin());
1061
        $this->assertEquals('Bill Shakespeare', $user2->getName());
1062
        $this->assertEquals('uk', $user2->getCountry()->getLabel());
1063
1064
        // MANY 2 MANY must be duplicated
1065
        $this->assertEquals('Writers', $user2->getRoles()[0]->getName());
1066
1067
        // Let's test saving this clone
1068
        $user2->setLogin('william.shakespeare');
1069
        $userDao->save($user2);
1070
1071
        $user3 = $userDao->getUserByLogin('william.shakespeare');
1072
        $this->assertTrue($user3 === $user2);
1073
        $userDao->delete($user3);
1074
1075
        // Finally, let's test the origin user still exists!
1076
        $user4 = $userDao->getUserByLogin('bill.shakespeare');
1077
        $this->assertEquals('bill.shakespeare', $user4->getLogin());
1078
    }
1079
1080
    /**
1081
     * @depends testDaoGeneration
1082
     */
1083
    public function testCloneNewBean(): void
1084
    {
1085
        $countryDao = new CountryDao($this->tdbmService);
1086
        $roleDao = new RoleDao($this->tdbmService);
1087
        $role = $roleDao->getById(1);
1088
1089
        $userBean = new UserBean('John Doe', '[email protected]', $countryDao->getById(2), 'john.doe');
1090
        $userBean->addRole($role);
1091
1092
        $user2 = clone $userBean;
1093
1094
        $this->assertNull($user2->getId());
1095
        $this->assertEquals('john.doe', $user2->getLogin());
1096
        $this->assertEquals('John Doe', $user2->getName());
1097
        $this->assertEquals('uk', $user2->getCountry()->getLabel());
1098
1099
        // MANY 2 MANY must be duplicated
1100
        $this->assertEquals($role->getName(), $user2->getRoles()[0]->getName());
1101
    }
1102
1103
    /**
1104
     * @depends testDaoGeneration
1105
     */
1106
    public function testCascadeDelete(): void
1107
    {
1108
        $userDao = new TestUserDao($this->tdbmService);
1109
        $countryDao = new CountryDao($this->tdbmService);
1110
1111
        $spain = new CountryBean('Spain');
1112
        $sanchez = new UserBean('Manuel Sanchez', '[email protected]', $spain, 'manuel.sanchez');
1113
1114
        $countryDao->save($spain);
1115
        $userDao->save($sanchez);
1116
1117
        $speedy2 = $userDao->getUserByLogin('manuel.sanchez');
1118
        $this->assertTrue($sanchez === $speedy2);
1119
1120
        $exceptionTriggered = false;
1121
        try {
1122
            $countryDao->delete($spain);
1123
        } catch (ForeignKeyConstraintViolationException $e) {
1124
            $exceptionTriggered = true;
1125
        }
1126
        $this->assertTrue($exceptionTriggered);
1127
1128
        $countryDao->delete($spain, true);
1129
1130
        // Let's check that speed gonzalez was removed.
1131
        $speedy3 = $userDao->getUserByLogin('manuel.sanchez');
1132
        $this->assertNull($speedy3);
1133
    }
1134
1135
    /**
1136
     * @depends testDaoGeneration
1137
     */
1138
    public function testDiscardChanges(): void
1139
    {
1140
        $contactDao = new ContactDao($this->tdbmService);
1141
        $contactBean = $contactDao->getById(1);
1142
1143
        $oldName = $contactBean->getName();
1144
1145
        $contactBean->setName('MyNewName');
1146
1147
        $contactBean->discardChanges();
1148
1149
        $this->assertEquals($oldName, $contactBean->getName());
1150
    }
1151
1152
    /**
1153
     * @depends testDaoGeneration
1154
     */
1155
    public function testDiscardChangesOnNewBeanFails(): void
1156
    {
1157
        $person = new PersonBean('John Foo', new \DateTimeImmutable());
1158
        $this->expectException('TheCodingMachine\TDBM\TDBMException');
1159
        $person->discardChanges();
1160
    }
1161
1162
    /**
1163
     * @depends testDaoGeneration
1164
     */
1165
    public function testDiscardChangesOnDeletedBeanFails(): void
1166
    {
1167
        $userDao = new TestUserDao($this->tdbmService);
1168
        $countryDao = new CountryDao($this->tdbmService);
1169
1170
        $sanchez = new UserBean('Manuel Sanchez', '[email protected]', $countryDao->getById(1), 'manuel.sanchez');
1171
1172
        $userDao->save($sanchez);
1173
1174
        $userDao->delete($sanchez);
1175
1176
        $this->expectException('TheCodingMachine\TDBM\TDBMException');
1177
        // Cannot discard changes on a bean that is already deleted.
1178
        $sanchez->discardChanges();
1179
    }
1180
1181
    /**
1182
     * @depends testDaoGeneration
1183
     */
1184
    public function testUniqueIndexBasedSearch(): void
1185
    {
1186
        $userDao = new UserDao($this->tdbmService);
1187
        $user = $userDao->findOneByLogin('bill.shakespeare');
1188
1189
        $this->assertEquals('bill.shakespeare', $user->getLogin());
1190
        $this->assertEquals('Bill Shakespeare', $user->getName());
1191
    }
1192
1193
    /**
1194
     * @depends testDaoGeneration
1195
     */
1196
    public function testFindOneByRetunsNull(): void
1197
    {
1198
        // Let's assert that the findOneBy... methods can return null.
1199
        $userDao = new UserDao($this->tdbmService);
1200
        $userBean = $userDao->findOneByLogin('not_exist');
1201
1202
        $this->assertNull($userBean);
1203
    }
1204
1205
    /**
1206
     * @depends testDaoGeneration
1207
     */
1208
    public function testMultiColumnsIndexBasedSearch(): void
1209
    {
1210
        $countryDao = new CountryDao($this->tdbmService);
1211
        $userDao = new UserDao($this->tdbmService);
1212
        $users = $userDao->findByStatusAndCountry('on', $countryDao->getById(1));
1213
1214
        $this->assertEquals('jean.dupont', $users[0]->getLogin());
1215
    }
1216
1217
    /**
1218
     * @depends testDaoGeneration
1219
     */
1220
    public function testPartialMultiColumnsIndexBasedSearch(): void
1221
    {
1222
        $userDao = new UserDao($this->tdbmService);
1223
        $users = $userDao->findByStatusAndCountry('on');
1224
1225
        $this->assertCount(2, $users);
1226
    }
1227
1228
    /**
1229
     * @depends testDaoGeneration
1230
     */
1231
    public function testCreationInNullableDate(): void
1232
    {
1233
        $roleDao = new RoleDao($this->tdbmService);
1234
1235
        $role = new RoleBean('newbee');
1236
        $roleDao->save($role);
1237
1238
        $this->assertNull($role->getCreatedAt());
1239
    }
1240
1241
    /**
1242
     * @depends testDaoGeneration
1243
     */
1244
    public function testUpdateInNullableDate(): void
1245
    {
1246
        $roleDao = new RoleDao($this->tdbmService);
1247
1248
        $role = new RoleBean('newbee');
1249
        $roleDao->save($role);
1250
1251
        $role->setCreatedAt(null);
1252
        $roleDao->save($role);
1253
        $this->assertNull($role->getCreatedAt());
1254
    }
1255
1256
    /**
1257
     * @depends testDaoGeneration
1258
     */
1259
    public function testFindFromSql(): void
1260
    {
1261
        $roleDao = new TestRoleDao($this->tdbmService);
1262
1263
        $roles = $roleDao->getRolesByRightCanSing();
1264
        $this->assertCount(2, $roles);
1265
        $this->assertInstanceOf(RoleBean::class, $roles[0]);
1266
    }
1267
1268
    /**
1269
     * @depends testDaoGeneration
1270
     */
1271
    public function testFindOneFromSql(): void
1272
    {
1273
        $roleDao = new TestRoleDao($this->tdbmService);
1274
1275
        $role = $roleDao->getRoleByRightCanSingAndNameSinger();
1276
        $this->assertInstanceOf(RoleBean::class, $role);
1277
    }
1278
1279
    /**
1280
     * @depends testDaoGeneration
1281
     */
1282
    public function testCreateEmptyExtendedBean(): void
1283
    {
1284
        // This test cases checks issue https://github.com/thecodingmachine/database.tdbm/issues/92
1285
1286
        $dogDao = new DogDao($this->tdbmService);
1287
1288
        // We are not filling no field that is part of dog table.
1289
        $dog = new DogBean('Youki');
1290
        $dog->setOrder(1);
1291
1292
        $dogDao->save($dog);
1293
        $this->assertNull($dog->getRace());
1294
    }
1295
1296
    /**
1297
     * @depends testCreateEmptyExtendedBean
1298
     */
1299
    public function testFetchEmptyExtendedBean(): void
1300
    {
1301
        // This test cases checks issue https://github.com/thecodingmachine/database.tdbm/issues/92
1302
1303
        $animalDao = new AnimalDao($this->tdbmService);
1304
1305
        // We are not filling no field that is part of dog table.
1306
        $animalBean = $animalDao->getById(1);
1307
1308
        $this->assertInstanceOf(DogBean::class, $animalBean);
1309
    }
1310
1311
    /**
1312
     * @depends testDaoGeneration
1313
     */
1314
    public function testTwoBranchesHierarchy(): void
1315
    {
1316
        // This test cases checks issue https://github.com/thecodingmachine/mouf/issues/131
1317
1318
        $catDao = new CatDao($this->tdbmService);
1319
1320
        // We are not filling no field that is part of dog table.
1321
        $cat = new CatBean('Mew');
1322
        $cat->setOrder(2);
1323
1324
        $catDao->save($cat);
1325
        $this->assertNotNull($cat->getId());
1326
    }
1327
1328
    /**
1329
     * @depends testTwoBranchesHierarchy
1330
     */
1331
    public function testFetchTwoBranchesHierarchy(): void
1332
    {
1333
        // This test cases checks issue https://github.com/thecodingmachine/mouf/issues/131
1334
1335
        $animalDao = new AnimalDao($this->tdbmService);
1336
1337
        $animalBean = $animalDao->getById(2);
1338
1339
        $this->assertInstanceOf(CatBean::class, $animalBean);
1340
        /* @var $animalBean CatBean */
1341
        $animalBean->setCutenessLevel(999);
1342
        $animalBean->setUppercaseColumn('foobar');
1343
1344
        $animalDao->save($animalBean);
1345
    }
1346
1347
    /**
1348
     * @depends testDaoGeneration
1349
     */
1350
    public function testExceptionOnGetById(): void
1351
    {
1352
        $countryDao = new CountryDao($this->tdbmService);
1353
        $this->expectException(\TypeError::class);
1354
        $countryDao->getById(null);
1355
    }
1356
1357
    /**
1358
     * @depends testDaoGeneration
1359
     */
1360
    public function testDisconnectedManyToOne(): void
1361
    {
1362
        // This test cases checks issue https://github.com/thecodingmachine/database.tdbm/issues/99
1363
1364
        $country = new CountryBean('Spain');
1365
1366
        $user = new UserBean('John Doe', '[email protected]', $country, 'john.doe');
1367
1368
        $this->assertCount(1, $country->getUsers());
1369
        $this->assertSame($user, $country->getUsers()[0]);
1370
    }
1371
1372
    /**
1373
     * @depends testDaoGeneration
1374
     */
1375
    public function testOrderByExternalCol(): void
1376
    {
1377
        // This test cases checks issue https://github.com/thecodingmachine/database.tdbm/issues/106
1378
1379
        $userDao = new TestUserDao($this->tdbmService);
1380
        $users = $userDao->getUsersByCountryName();
1381
1382
        $this->assertEquals('uk', $users[0]->getCountry()->getLabel());
1383
    }
1384
1385
    /**
1386
     * @depends testDaoGeneration
1387
     */
1388
    public function testResultIteratorSort(): void
1389
    {
1390
        $userDao = new UserDao($this->tdbmService);
1391
        $users = $userDao->findAll()->withOrder('country.label DESC');
1392
1393
        $this->assertEquals('uk', $users[0]->getCountry()->getLabel());
1394
1395
        $users = $users->withOrder('country.label ASC');
1396
        $this->assertEquals('France', $users[0]->getCountry()->getLabel());
1397
    }
1398
1399
    /**
1400
     * @depends testDaoGeneration
1401
     */
1402
    public function testResultIteratorWithParameters(): void
1403
    {
1404
        $userDao = new TestUserDao($this->tdbmService);
1405
        $users = $userDao->getUsersByLoginStartingWith()->withParameters(['login' => 'bill%']);
1406
        $this->assertEquals('bill.shakespeare', $users[0]->getLogin());
1407
1408
        $users = $users->withParameters(['login' => 'jean%']);
1409
        $this->assertEquals('jean.dupont', $users[0]->getLogin());
1410
    }
1411
1412
    /**
1413
     * @depends testDaoGeneration
1414
     */
1415
    public function testOrderByExpression(): void
1416
    {
1417
        $userDao = new TestUserDao($this->tdbmService);
1418
        $users = $userDao->getUsersByReversedCountryName();
1419
1420
        $this->assertEquals('Jamaica', $users[0]->getCountry()->getLabel());
1421
    }
1422
1423
    /**
1424
     * @depends testDaoGeneration
1425
     */
1426
    public function testOrderByException(): void
1427
    {
1428
        $userDao = new TestUserDao($this->tdbmService);
1429
        $users = $userDao->getUsersByInvalidOrderBy();
1430
        $this->expectException(TDBMInvalidArgumentException::class);
1431
        $user = $users[0];
1432
    }
1433
1434
    /**
1435
     * @depends testDaoGeneration
1436
     */
1437
    public function testOrderByProtectedColumn(): void
1438
    {
1439
        $animalDao = new AnimalDao($this->tdbmService);
1440
        $animals = $animalDao->findAll();
1441
        $animals = $animals->withOrder('`order` ASC');
1442
1443
        $this->assertInstanceOf(DogBean::class, $animals[0]);
1444
        $this->assertInstanceOf(CatBean::class, $animals[1]);
1445
1446
        $animals = $animals->withOrder('`order` DESC');
1447
1448
        $this->assertInstanceOf(CatBean::class, $animals[0]);
1449
        $this->assertInstanceOf(DogBean::class, $animals[1]);
1450
    }
1451
1452
    /**
1453
     * @depends testDaoGeneration
1454
     */
1455
    public function testGetOnAllNullableValues(): void
1456
    {
1457
        // Tests that a get performed on a column that has only nullable fields succeeds.
1458
        $allNullable = new AllNullableBean();
1459
        $this->assertNull($allNullable->getId());
1460
        $this->assertNull($allNullable->getLabel());
1461
        $this->assertNull($allNullable->getCountry());
1462
    }
1463
1464
    /**
1465
     * @depends testDaoGeneration
1466
     */
1467
    public function testExceptionOnMultipleInheritance(): void
1468
    {
1469
        // Because of the sequence on the PK, we cannot set the PK to 99 at all.
1470
        $this->skipOracle();
1471
1472
        $connection = self::getConnection();
1473
        self::insert($connection, 'animal', [
1474
            'id' => 99, 'name' => 'Snoofield',
1475
        ]);
1476
        self::insert($connection, 'dog', [
1477
            'id' => 99, 'race' => 'dog',
1478
        ]);
1479
        self::insert($connection, 'cat', [
1480
            'id' => 99, 'cuteness_level' => 0,
1481
        ]);
1482
1483
        $catched = false;
1484
        try {
1485
            $animalDao = new AnimalDao($this->tdbmService);
1486
            $animalDao->getById(99);
1487
        } catch (TDBMInheritanceException $e) {
1488
            $catched = true;
1489
        }
1490
        $this->assertTrue($catched, 'Exception TDBMInheritanceException was not caught');
1491
1492
        self::delete($connection, 'cat', ['id' => 99]);
1493
        self::delete($connection, 'dog', ['id' => 99]);
1494
        self::delete($connection, 'animal', ['id' => 99]);
1495
    }
1496
1497
    /**
1498
     * @depends testDaoGeneration
1499
     */
1500
    public function testReferenceNotSaved(): void
1501
    {
1502
        $boatDao = new BoatDao($this->tdbmService);
1503
1504
        $country = new CountryBean('Atlantis');
1505
        $boat = new BoatBean($country, 'Titanic');
1506
1507
        $boatDao->save($boat);
1508
        $this->assertNotNull($country->getId());
1509
    }
1510
1511
    /**
1512
     * @depends testReferenceNotSaved
1513
     */
1514
    public function testUniqueIndexOnForeignKeyThenScalar(): void
1515
    {
1516
        $boatDao = new BoatDao($this->tdbmService);
1517
        $countryDao = new CountryDao($this->tdbmService);
1518
1519
        $countryBean = $countryDao->findOneByLabel('Atlantis');
1520
        $boatBean = $boatDao->findOneByAnchorageCountryAndName($countryBean, 'Titanic');
1521
1522
        $this->assertNotNull($boatBean);
1523
    }
1524
1525
    /**
1526
     * @depends testDaoGeneration
1527
     */
1528
    public function testReferenceDeleted(): void
1529
    {
1530
        $countryDao = new CountryDao($this->tdbmService);
1531
        $boatDao = new BoatDao($this->tdbmService);
1532
1533
        $country = new CountryBean('Bikini Bottom');
1534
        $countryDao->save($country);
1535
1536
        $boat = new BoatBean($country, 'Squirrel boat');
1537
        $countryDao->delete($country);
1538
1539
        $this->expectException(TDBMMissingReferenceException::class);
1540
        $boatDao->save($boat);
1541
    }
1542
1543
    /**
1544
     * @depends testDaoGeneration
1545
     */
1546
    public function testCyclicReferenceWithInheritance(): void
1547
    {
1548
        $userDao = new UserDao($this->tdbmService);
1549
1550
        $country = new CountryBean('Norrisland');
1551
        $user = new UserBean('Chuck Norris', '[email protected]', $country, 'chuck.norris');
1552
1553
        $user->setManager($user);
1554
1555
        $this->expectException(TDBMCyclicReferenceException::class);
1556
        $userDao->save($user);
1557
    }
1558
1559
    /**
1560
     * @depends testDaoGeneration
1561
     */
1562
    public function testCyclicReference(): void
1563
    {
1564
        $categoryDao = new CategoryDao($this->tdbmService);
1565
1566
        $category = new CategoryBean('Root');
1567
1568
        $category->setParent($category);
1569
1570
        $this->expectException(TDBMCyclicReferenceException::class);
1571
        $categoryDao->save($category);
1572
    }
1573
1574
    /**
1575
     * @depends testDaoGeneration
1576
     */
1577
    public function testCorrectTypeForPrimaryKeyAfterSave(): void
1578
    {
1579
        // PosqtgreSQL does not particularly like empty inserts (i.e.: "INSERT INTO all_nullable () VALUES ()" )
1580
        $this->onlyMySql();
1581
1582
        $allNullableDao = new AllNullableDao($this->tdbmService);
1583
        $allNullable = new AllNullableBean();
1584
        $allNullableDao->save($allNullable);
1585
        $id = $allNullable->getId();
1586
1587
        $this->assertTrue(is_int($id));
1588
    }
1589
1590
    /**
1591
     * @depends testDaoGeneration
1592
     */
1593
    public function testPSR2Compliance(): void
1594
    {
1595
        $process = new Process(['vendor/bin/php-cs-fixer', 'fix', 'src/Test/', '--dry-run', '--diff', '--diff-format=udiff', '--rules=@PSR2']);
1596
        $process->run();
1597
1598
        // executes after the command finishes
1599
        if (!$process->isSuccessful()) {
1600
            echo $process->getOutput();
1601
            $this->fail('Generated code is not PSR-2 compliant');
1602
        }
1603
        $this->assertTrue($process->isSuccessful());
1604
    }
1605
1606
    /**
1607
     * @depends testDaoGeneration
1608
     */
1609
    public function testFindOneByGeneration(): void
1610
    {
1611
        $reflectionMethod = new \ReflectionMethod(UserBaseDao::class, 'findOneByLogin');
1612
        $parameters = $reflectionMethod->getParameters();
1613
1614
        $this->assertCount(2, $parameters);
1615
        $this->assertSame('login', $parameters[0]->getName());
1616
        $this->assertSame('additionalTablesFetch', $parameters[1]->getName());
1617
    }
1618
1619
    /**
1620
     * @depends testDaoGeneration
1621
     */
1622
    public function testUuid(): void
1623
    {
1624
        $article = new ArticleBean('content');
1625
        $this->assertSame('content', $article->getContent());
1626
        $this->assertNotEmpty($article->getId());
1627
        $uuid = Uuid::fromString($article->getId());
1628
        $this->assertSame(1, $uuid->getVersion());
1629
    }
1630
1631
    /**
1632
     * @depends testDaoGeneration
1633
     */
1634
    public function testUuidv4(): void
1635
    {
1636
        $article = new Article2Bean('content');
1637
        $this->assertSame('content', $article->getContent());
1638
        $this->assertNotEmpty($article->getId());
1639
        $uuid = Uuid::fromString($article->getId());
1640
        $this->assertSame(4, $uuid->getVersion());
1641
    }
1642
1643
    /**
1644
     * @depends testDaoGeneration
1645
     */
1646
    public function testTypeHintedConstructors(): void
1647
    {
1648
        $userBaseBeanReflectionConstructor = new \ReflectionMethod(UserBaseBean::class, '__construct');
1649
        /** @var ReflectionNamedType $nameParam */
1650
        $nameParam = $userBaseBeanReflectionConstructor->getParameters()[0];
1651
1652
        $this->assertSame('string', $nameParam->getType()->getName());
1653
    }
1654
1655
    /**
1656
     * @depends testDaoGeneration
1657
     */
1658
    public function testSaveTransaction(): void
1659
    {
1660
        $countryDao = new CountryDao($this->tdbmService);
1661
1662
        $boatDao = new BoatDao($this->tdbmService);
1663
        $boatBean = $boatDao->getById(1);
1664
        $boatBean->setName('Bismark');
1665
1666
        $boatBean->getCountry();
1667
1668
        // Let's insert a row without telling TDBM to trigger an error!
1669
        self::insert($this->getConnection(), 'sailed_countries', [
1670
            'boat_id' => 1,
1671
            'country_id' => 2
1672
        ]);
1673
1674
        $boatBean->addCountry($countryDao->getById(2));
1675
1676
        $this->expectException(UniqueConstraintViolationException::class);
1677
1678
        $boatDao->save($boatBean);
1679
    }
1680
1681
    /**
1682
     * @depends testSaveTransaction
1683
     */
1684
    public function testSaveTransaction2(): void
1685
    {
1686
        $boatDao = new BoatDao($this->tdbmService);
1687
        $boatBean = $boatDao->getById(1);
1688
1689
        // The name should not have been saved because the transaction of the previous test should have rollbacked.
1690
        $this->assertNotSame('Bismark', $boatBean->getName());
1691
    }
1692
1693
    /**
1694
     * @depends testDaoGeneration
1695
     */
1696
    public function testForeignKeyPointingToNonPrimaryKey(): void
1697
    {
1698
        $dao = new RefNoPrimKeyDao($this->tdbmService);
1699
        $bean = $dao->getById(1);
1700
1701
        $this->assertSame('foo', $bean->getFrom()->getTo());
1702
1703
        $newBean = new RefNoPrimKeyBean($bean, 'baz');
1704
        $dao->save($newBean);
1705
        $this->assertSame('foo', $newBean->getFrom()->getTo());
1706
1707
        $resultSet = $bean->getRefNoPrimKey();
1708
        $this->assertCount(2, $resultSet);
1709
    }
1710
1711
    /**
1712
     * @depends testDaoGeneration
1713
     */
1714
    public function testCloningUuidBean(): void
1715
    {
1716
        $article = new ArticleBean('content');
1717
        $this->assertNotEmpty($article->getId());
1718
        $article2 = clone $article;
1719
        $this->assertNotEmpty($article2->getId());
1720
        $this->assertNotSame($article->getId(), $article2->getId());
1721
    }
1722
1723
    /**
1724
     * @depends testDaoGeneration
1725
     */
1726
    public function testRecursiveSave(): void
1727
    {
1728
        $categoryDao = new CategoryDao($this->tdbmService);
1729
1730
        $root1 = new CategoryBean('Root1');
1731
        $categoryDao->save($root1);
1732
        // Root 2 is not saved yet.
1733
        $root2 = new CategoryBean('Root2');
1734
        $intermediate = new CategoryBean('Intermediate');
1735
        $categoryDao->save($intermediate);
1736
1737
        // Let's switch the parent to a bean in detached state.
1738
        $intermediate->setParent($root2);
1739
1740
        // Now, let's save a new category that references the leaf category.
1741
        $leaf = new CategoryBean('Leaf');
1742
        $leaf->setParent($intermediate);
1743
        $categoryDao->save($leaf);
1744
        $this->assertNull($root2->getId());
1745
    }
1746
1747
    /**
1748
     * @depends testDaoGeneration
1749
     */
1750
    public function testBlob(): void
1751
    {
1752
        // An issue in DBAL makes using BLOB type impossible with resources.
1753
        // See https://github.com/doctrine/dbal/issues/3290
1754
        $this->skipOracle();
1755
1756
        $fp = fopen(__FILE__, 'r');
1757
        $file = new FileBean($fp);
1758
1759
        $fileDao = new FileDao($this->tdbmService);
1760
1761
        $fileDao->save($file);
1762
1763
        $loadedFile = $fileDao->getById($file->getId());
1764
1765
        $resource = $loadedFile->getFile();
1766
        $result = fseek($resource, 0);
1767
        $this->assertSame(0, $result);
1768
        $this->assertIsResource($resource);
1769
        $firstLine = fgets($resource);
1770
        $this->assertSame("<?php\n", $firstLine);
1771
    }
1772
1773
    /**
1774
     * @depends testBlob
1775
     */
1776
    public function testReadBlob(): void
1777
    {
1778
        // An issue in DBAL makes using BLOB type impossible with resources.
1779
        // See https://github.com/doctrine/dbal/issues/3290
1780
        $this->skipOracle();
1781
1782
        $fileDao = new FileDao($this->tdbmService);
1783
        $loadedFile = $fileDao->getById(1);
1784
1785
        $resource = $loadedFile->getFile();
1786
        $this->assertIsResource($resource);
1787
        $firstLine = fgets($resource);
1788
        $this->assertSame("<?php\n", $firstLine);
1789
1790
        stream_get_contents($resource);
1791
1792
        $loadedFile->setId($loadedFile->getId());
1793
1794
        $fileDao->save($loadedFile);
1795
    }
1796
1797
    /**
1798
     * @depends testReadBlob
1799
     */
1800
    public function testReadAndSaveBlob(): void
1801
    {
1802
        // An issue in DBAL makes using BLOB type impossible with resources.
1803
        // See https://github.com/doctrine/dbal/issues/3290
1804
        $this->skipOracle();
1805
1806
        $fileDao = new FileDao($this->tdbmService);
1807
        $loadedFile = $fileDao->getById(1);
1808
1809
        $resource = $loadedFile->getFile();
1810
1811
        $firstLine = fgets($resource);
1812
        $this->assertSame("<?php\n", $firstLine);
1813
    }
1814
1815
    /**
1816
     * @depends testReadBlob
1817
     */
1818
    public function testProtectedGetterSetter(): void
1819
    {
1820
        // An issue in DBAL makes using BLOB type impossible with resources.
1821
        // See https://github.com/doctrine/dbal/issues/3290
1822
        $this->skipOracle();
1823
1824
        $md5Getter = new ReflectionMethod(FileBaseBean::class, 'getMd5');
1825
        $md5Setter = new ReflectionMethod(FileBaseBean::class, 'setMd5');
1826
1827
        $this->assertTrue($md5Getter->isProtected());
1828
        $this->assertTrue($md5Setter->isProtected());
1829
1830
        $md5Getter2 = new ReflectionMethod(FileBaseBean::class, 'getArticle');
1831
        $md5Setter2 = new ReflectionMethod(FileBaseBean::class, 'setArticle');
1832
1833
        $this->assertTrue($md5Getter2->isProtected());
1834
        $this->assertTrue($md5Setter2->isProtected());
1835
1836
        $oneToManyGetter = new ReflectionMethod(ArticleBaseBean::class, 'getFiles');
1837
        $this->assertTrue($oneToManyGetter->isProtected());
1838
1839
        $fileDao = new FileDao($this->tdbmService);
1840
        $loadedFile = $fileDao->getById(1);
1841
1842
        // The md5 and article columns are not JSON serialized
1843
        $this->assertSame([
1844
            'id' => 1,
1845
        ], $loadedFile->jsonSerialize());
1846
    }
1847
1848
    /**
1849
     * @depends testDaoGeneration
1850
     */
1851
    public function testBlobResourceException(): void
1852
    {
1853
        $this->expectException(TDBMInvalidArgumentException::class);
1854
        $this->expectExceptionMessage('Invalid argument passed to \'TheCodingMachine\\TDBM\\Test\\Dao\\Bean\\Generated\\FileBaseBean::setFile\'. Expecting a resource. Got a string.');
1855
        new FileBean('foobar');
1856
    }
1857
1858
    /**
1859
     * @depends testDaoGeneration
1860
     */
1861
    public function testFilterBag(): void
1862
    {
1863
        $userDao = new TestUserDao($this->tdbmService);
1864
        $countryDao = new CountryDao($this->tdbmService);
1865
1866
        $country = $countryDao->getById(2);
1867
1868
        // Let's test filter bags by bean and filter bag with many values.
1869
        $users = $userDao->getUsersByComplexFilterBag($country, ['John Doe', 'Jane Doe']);
1870
1871
        $this->assertCount(1, $users);
1872
        $this->assertSame('John Doe', $users[0]->getName());
1873
    }
1874
1875
    /**
1876
     * @depends testDaoGeneration
1877
     */
1878
    public function testDecimalIsMappedToString(): void
1879
    {
1880
        $reflectionClass = new \ReflectionClass(BoatBaseBean::class);
1881
        $this->assertSame('string', $reflectionClass->getMethod('getLength')->getReturnType()->getName());
1882
    }
1883
1884
    /**
1885
     * @depends testDaoGeneration
1886
     */
1887
    public function testInsertMultiPrimaryKeysBean(): void
1888
    {
1889
        $countryDao = new CountryDao($this->tdbmService);
1890
1891
        $country = $countryDao->getById(1);
1892
1893
        $stateDao = new StateDao($this->tdbmService);
1894
        $state = new StateBean($country, 'IDF', 'Ile de France');
1895
        $stateDao->save($state);
1896
1897
        $this->assertSame($state, $stateDao->findAll()[0]);
1898
    }
1899
1900
    /**
1901
     * @depends testInsertMultiPrimaryKeysBean
1902
     */
1903
    public function testDeleteMultiPrimaryKeysBean(): void
1904
    {
1905
        $stateDao = new StateDao($this->tdbmService);
1906
1907
        $state = $stateDao->findAll()[0];
1908
        $stateDao->delete($state);
1909
        $this->assertCount(0, $stateDao->findAll());
1910
    }
1911
1912
    /**
1913
     * @depends testDaoGeneration
1914
     */
1915
    public function testCompositePrimaryKeyGetter(): void
1916
    {
1917
        $stateDao = new StateDao($this->tdbmService);
1918
        $country = new CountryBean('USA');
1919
        $stateBean = new StateBean($country, 'CA', 'California');
1920
        $stateDao->save($stateBean);
1921
        $this->assertSame($stateBean, $stateDao->getById($country->getId(), 'CA'));
1922
    }
1923
1924
    /**
1925
     * @depends testDaoGeneration
1926
     */
1927
    public function testSortOnInheritedTable(): void
1928
    {
1929
        $animalDao = new AnimalDao($this->tdbmService);
1930
1931
        // Let's insert an animal that is nothing.
1932
        $animal = new AnimalBean('Mickey');
1933
        $animalDao->save($animal);
1934
1935
        $animals = $animalDao->findAll()->withOrder('dog.race ASC');
1936
1937
        $animalsArr = $animals->toArray();
1938
        $this->assertCount(3, $animalsArr);
1939
    }
1940
1941
    /**
1942
     * @depends testDaoGeneration
1943
     */
1944
    public function testJsonKey(): void
1945
    {
1946
        $node = new NodeBean('foo.html');
1947
        $json = $node->jsonSerialize();
1948
        self::assertTrue(isset($json['basename']));
1949
        self::assertEquals('foo.html', $json['basename']);
1950
    }
1951
1952
    /**
1953
     * @depends testDaoGeneration
1954
     */
1955
    public function testJsonIgnore(): void
1956
    {
1957
        $nodeDao = new NodeDao($this->tdbmService);
1958
        $index = $nodeDao->getById(6);
1959
        $json = $index->jsonSerialize();
1960
        // Ignored scalar 'id'
1961
        self::assertTrue(!isset($json['id']));
1962
        // Ignored object 'root'
1963
        self::assertTrue(!isset($json['root']));
1964
        self::assertTrue(isset($json['guests']));
1965
        self::assertTrue(!empty($json['guests']));
1966
        $account = $index->getAccounts()[0];
1967
        $json = $account->jsonSerialize();
1968
        // Ignored array 'nodes' (from nodes_users table)
1969
        self::assertTrue(!isset($json['nodes']));
1970
    }
1971
1972
    /**
1973
     * @depends testDaoGeneration
1974
     */
1975
    public function testJsonInclude(): void
1976
    {
1977
        $nodeDao = new NodeDao($this->tdbmService);
1978
        $index = $nodeDao->getById(6);
1979
        $json = $index->jsonSerialize();
1980
        // Whole chain of parents should be serialized
1981
        self::assertTrue(isset($json['parent']));
1982
        self::assertTrue(isset($json['parent']['parent']));
1983
        self::assertTrue(isset($json['parent']['parent']['parent']));
1984
        self::assertEquals('/', $json['parent']['parent']['parent']['basename']);
1985
    }
1986
1987
    /**
1988
     * @depends testDaoGeneration
1989
     */
1990
    public function testJsonRecursive(): void
1991
    {
1992
        $nodeDao = new NodeDao($this->tdbmService);
1993
        $index = $nodeDao->getById(8);
1994
        $json = $index->jsonSerialize();
1995
        // Original chain of aliases is recursively serialized, ...
1996
        self::assertTrue(isset($json['alias']));
1997
        self::assertTrue(isset($json['alias']['alias']));
1998
        self::assertEquals('index.html', $json['alias']['alias']['basename']);
1999
        // ... each alias even serializes its parents, ...
2000
        self::assertTrue(isset($json['alias']['alias']['parent']['parent']));
2001
        // ... however, parents aliases chains have just their foreign key (id), as parents are serialized with $stopRecursion=true
2002
        self::assertEquals(3, $json['alias']['alias']['parent']['parent']['alias']['id']);
2003
        self::assertCount(1, $json['alias']['alias']['parent']['parent']['alias']);
2004
    }
2005
2006
    /**
2007
     * @depends testDaoGeneration
2008
     */
2009
    public function testJsonFormat(): void
2010
    {
2011
        $nodeDao = new NodeDao($this->tdbmService);
2012
        $index = $nodeDao->getById(6);
2013
        $json = $index->jsonSerialize();
2014
        self::assertTrue(isset($json['size']));
2015
        self::assertEquals('512 o', $json['size']);
2016
        self::assertEquals('42.50g', $json['weight']);
2017
        self::assertEquals($index->getCreatedAt()->format('Y-m-d'), $json['createdAt']);
2018
        self::assertEquals($index->getOwner()->getName(), $json['owner']);
2019
        self::assertEquals($index->getAccounts()[1]->getName(), $json['guests'][1]);
2020
        self::assertTrue(isset($json['entries']));
2021
        self::assertEquals('Hello, World', $json['entries'][1]);
2022
        $www = $index->getParent();
2023
        $json = $www->jsonSerialize();
2024
        self::assertEquals('0 o', $json['size']);
2025
        self::assertNull($json['weight']);
2026
        self::assertNull($json['owner']);
2027
    }
2028
2029
    /**
2030
     * @depends testDaoGeneration
2031
     */
2032
    public function testJsonCollection(): void
2033
    {
2034
        // This test tries to perform a SELECT DISTINCT on a JSON column (which is represented as a CLOB column in Oracle)
2035
        // DISTINCT statements cannot be applied on CLOB columns. As a result, JSON columns are not supported in Oracle + TDBM 5 for now.
2036
        $this->skipOracle();
2037
2038
        $artists = new ArtistDao($this->tdbmService);
2039
        $pinkFloyd = $artists->getById(1);
2040
        $animals =  $pinkFloyd->getAlbums()[0];
2041
        $json = $pinkFloyd->jsonSerialize();
2042
        // Collection name properly handled ('discography' instead of default 'albums')
2043
        self::assertTrue(isset($json['discography']));
2044
        self::assertEquals($animals->getTitle(), $json['discography'][0]['title']);
2045
        // Make sure top object have just its primary key
2046
        self::assertEquals(1, $json['discography'][0]['artist']['id']);
2047
        self::assertCount(1, $json['discography'][0]['artist']);
2048
        $json = $animals->jsonSerialize();
2049
        // Nevertheless, artist should be serialized in album as top object...
2050
        self::assertTrue(isset($json['artist']));
2051
        // ... as should be tracks...
2052
        self::assertTrue(isset($json['tracks'][0]));
2053
        self::assertEquals('Pigs on the Wing 1', $json['tracks'][0]['title']);
2054
        // ... and, ultimately, list of featuring artists, since feat is included
2055
        self::assertTrue(isset($json['tracks'][0]['feat'][0]));
2056
        self::assertEquals('Roger Waters', $json['tracks'][0]['feat'][0]['name']);
2057
    }
2058
2059
    public function testFloydHasNoParent(): void
2060
    {
2061
        // This test tries to perform a SELECT DISTINCT on a JSON column (which is represented as a CLOB column in Oracle)
2062
        // DISTINCT statements cannot be applied on CLOB columns. As a result, JSON columns are not supported in Oracle + TDBM 5 for now.
2063
        $this->skipOracle();
2064
2065
        $artists = new ArtistDao($this->tdbmService);
2066
        $pinkFloyd = $artists->getById(1);
2067
        $parents = $pinkFloyd->getParents();
2068
2069
        $this->assertEquals([], $parents);
2070
    }
2071
2072
    public function testFloydHasOneChild(): void
2073
    {
2074
        // This test tries to perform a SELECT DISTINCT on a JSON column (which is represented as a CLOB column in Oracle)
2075
        // DISTINCT statements cannot be applied on CLOB columns. As a result, JSON columns are not supported in Oracle + TDBM 5 for now.
2076
        $this->skipOracle();
2077
2078
        $artists = new ArtistDao($this->tdbmService);
2079
        $pinkFloyd = $artists->getById(1);
2080
        $children = $pinkFloyd->getChildrenByArtistsRelations();
2081
2082
        $this->assertEquals(1, count($children));
2083
        $this->assertEquals(2, $children[0]->getId());
2084
    }
2085
2086
    /**
2087
     * @depends testDaoGeneration
2088
     */
2089
    public function testAddInterfaceAnnotation(): void
2090
    {
2091
        $refClass = new ReflectionClass(UserBaseBean::class);
2092
        $this->assertTrue($refClass->implementsInterface(TestUserInterface::class));
2093
    }
2094
2095
    /**
2096
     * @depends testDaoGeneration
2097
     */
2098
    public function testAddInterfaceOnDaoAnnotation(): void
2099
    {
2100
        $refClass = new ReflectionClass(UserBaseDao::class);
2101
        $this->assertTrue($refClass->implementsInterface(TestUserDaoInterface::class));
2102
    }
2103
2104
    /**
2105
     * @depends testDaoGeneration
2106
     */
2107
    public function testTrait(): void
2108
    {
2109
        $userDao = new UserDao($this->tdbmService);
2110
        $userBean = $userDao->getById(1);
2111
2112
        $this->assertSame('TestOtherUserTrait', $userBean->method1());
2113
        $this->assertSame('TestUserTrait', $userBean->method1renamed());
2114
2115
        $refClass = new ReflectionClass(UserBaseDao::class);
2116
        $this->assertTrue($refClass->hasMethod('findNothing'));
2117
    }
2118
2119
    /**
2120
     * @depends testDaoGeneration
2121
     */
2122
    public function testNonInstantiableAbstractDaosAndBeans(): void
2123
    {
2124
        $refClass = new ReflectionClass(UserBaseDao::class);
2125
        $this->assertFalse($refClass->isInstantiable());
2126
2127
        $refClass = new ReflectionClass(UserBaseBean::class);
2128
        $this->assertFalse($refClass->isInstantiable());
2129
    }
2130
2131
    /**
2132
     * @depends testDaoGeneration
2133
     */
2134
    public function testCanNullifyBlob(): void
2135
    {
2136
        $article = new ArticleBean('content');
2137
        $fp = fopen(__FILE__, 'r');
2138
        $article->setAttachment($fp);
2139
        $article->setAttachment(null);
2140
        $this->assertNull($article->getAttachment(null));
2141
        fclose($fp);
2142
    }
2143
2144
    /**
2145
     * @depends testDaoGeneration
2146
     */
2147
    public function testOptionnalParametersCanBeNullInFindOneBy()
2148
    {
2149
        $albumDao = new AlbumDao($this->tdbmService);
2150
        $artist = new ArtistBean('Marcel');
2151
2152
        $albumDao->findOneByArtistAndNode($artist, null);
2153
        $this->assertEquals(1, 1);
2154
    }
2155
2156
    /**
2157
     * @depends testDaoGeneration
2158
     */
2159
    public function testRequiredParametersCannotBeNullInFindOneBy()
2160
    {
2161
        $albumDao = new AlbumDao($this->tdbmService);
2162
        $artist = new ArtistBean('Marcel');
2163
        $account = new AccountBean('Jamie');
2164
2165
        $albumDao->findOneByArtistAndAccount($artist, $account);
2166
2167
        $this->expectException('TypeError');
2168
        $albumDao->findOneByArtistAndAccount($artist, null);
2169
    }
2170
2171
    /**
2172
     * @depends testDaoGeneration
2173
     */
2174
    public function testLazyLoad(): void
2175
    {
2176
        $roleDao = new RoleDao($this->tdbmService);
2177
        $roleBean = $roleDao->getById(1, true);
2178
2179
        $this->assertSame(TDBMObjectStateEnum::STATE_NOT_LOADED, $roleBean->_getDbRows()['roles']->_getStatus());
2180
        $roleBean->getId();
2181
        $this->assertSame(TDBMObjectStateEnum::STATE_NOT_LOADED, $roleBean->_getDbRows()['roles']->_getStatus());
2182
    }
2183
2184
    /**
2185
     * @depends testDaoGeneration
2186
     */
2187
    public function testOneToOneInverseRelationGetter(): void
2188
    {
2189
        $this->skipOracle();
2190
2191
        $objectBaseDao = new BaseObjectDao($this->tdbmService);
2192
        $objectInheritedDao = new InheritedObjectDao($this->tdbmService);
2193
        $objectBase = new BaseObjectBean('label');
2194
        $objectBaseDao->save($objectBase);
2195
        $this->assertNull($objectBase->getInheritedObject());
2196
        $objectInherited = new InheritedObjectBean($objectBase);
2197
        $objectInheritedDao->save($objectInherited);
2198
        $this->assertSame($objectInherited, $objectBase->getInheritedObject());
2199
        $this->assertEquals(1, $objectBase->jsonSerialize()['inheritedObject']['id']);
2200
    }
2201
2202
    public function testLazyStopRecursion(): void
2203
    {
2204
        $albumDao = new AlbumDao($this->tdbmService);
2205
        $albumBean = $albumDao->getById(1);
2206
        $json = $albumBean->jsonSerialize(true);
2207
        $this->assertArrayHasKey('id', $json['artist']);
2208
        $this->assertArrayNotHasKey('name', $json['artist']);
2209
    }
2210
2211
    public function testLazyStopRecursionOnCompositeForeignKey(): void
2212
    {
2213
        $compositeFkSourceDao = new CompositeFkSourceDao($this->tdbmService);
2214
        $compositeFkSourceBean = $compositeFkSourceDao->getById(1);
2215
        $json = $compositeFkSourceBean->jsonSerialize(true);
2216
        $this->assertEquals(1, $json['compositeFkTarget']['1']['id']);
2217
        $this->assertEquals(1, $json['compositeFkTarget']['id2']);
2218
    }
2219
2220
    public function testMethodNameConflictsBetweenRegularAndAutoPivotProperties(): void
2221
    {
2222
        $artist = new ArtistBean('Super');
2223
        $artist->getChildren(); // regular property
2224
        $artist->getChildrenByArtistId(); // one-to-may relationship
2225
        $artist->getChildrenByArtistsRelations(); // auto-pivot relationship
2226
        $this->assertEquals(1, 1);
2227
    }
2228
2229
    /**
2230
     * @depends testDaoGeneration
2231
     */
2232
    public function testSQLCountWithArray(): void
2233
    {
2234
        $userDao = new TestUserDao($this->tdbmService);
2235
        $countryDao = new CountryDao($this->tdbmService);
2236
2237
        $country = $countryDao->getById(2);
2238
2239
        // Let's test filter bags by bean and filter bag with many values.
2240
        $users = $userDao->getUsersByComplexFilterBag($country, ['John Doe', 'John Smith'])->take(0, 1);
2241
        $this->assertEquals(1, $users->count());
2242
    }
2243
2244
    /**
2245
     * @depends testDaoGeneration
2246
     */
2247
    public function testSubQueryWithFind(): void
2248
    {
2249
        $userDao = new TestUserDao($this->tdbmService);
2250
        $articleDao = new TestArticleSubQueryDao($this->tdbmService, $userDao);
2251
2252
        $bill = $userDao->getById(4);
2253
        $article = new ArticleBean('Foo');
2254
        $article->setAuthor($bill);
2255
        $articleDao->save($article);
2256
2257
        $results = $articleDao->getArticlesByUserLoginStartingWith('bill');
2258
2259
        $this->assertCount(1, $results);
2260
        $this->assertSame('Foo', $results[0]->getContent());
2261
    }
2262
2263
    public function testSubQueryExceptionOnPrimaryKeysWithMultipleColumns(): void
2264
    {
2265
        $stateDao = new StateDao($this->tdbmService);
2266
        $states = $stateDao->findAll();
2267
        $this->expectException(TDBMException::class);
2268
        $this->expectExceptionMessage('You cannot use in a sub-query a table that has a primary key on more that 1 column.');
2269
        $states->_getSubQuery();
2270
    }
2271
2272
    public function testFindByDateTime(): void
2273
    {
2274
        $personDao = new PersonDao($this->tdbmService);
2275
        $personDao->findByModifiedAt(new \DateTimeImmutable())->count();
2276
        $this->assertTrue(true);
2277
    }
2278
2279
    /**
2280
     * Bug: find from sql use a `COUNT(DISTINCT *)` which fails because of null values.
2281
     */
2282
    public function testFindFromRawSqlCount(): void
2283
    {
2284
        $dao = new TestAlbumDao($this->tdbmService);
2285
        $albums = $dao->findAllFromRawSql();
2286
2287
        $firstAlbum = $albums->first();
2288
        assert($firstAlbum instanceof AlbumBean);
2289
        $this->assertNull($firstAlbum->getNode()); // This null ensure reproducibility of the bug
2290
        $expectedCount = $dao->findAllFromRawSqlWithCount()->count();
2291
        $this->assertEquals($expectedCount, $albums->count());
2292
    }
2293
2294
    public function testFindFromRawSQLOnInheritance(): void
2295
    {
2296
        $dao = new TestPersonDao($this->tdbmService);
2297
        $objects = $dao->testFindFromRawSQLOnInherited();
2298
2299
        $this->assertNotNull($objects->first());
2300
        $this->assertNotEquals(0, $objects->count());
2301
    }
2302
2303
    public function testGeneratedColumnsAreNotPartOfTheConstructor(): void
2304
    {
2305
        if (!$this->tdbmService->getConnection()->getDatabasePlatform() instanceof MySqlPlatform || self::isMariaDb($this->tdbmService->getConnection())) {
2306
            $this->markTestSkipped('ReadOnly column is only tested with MySQL');
2307
        }
2308
2309
        $dao = new PlayerDao($this->tdbmService);
2310
2311
        $player = new PlayerBean([
2312
            'id' => 1,
2313
            'name' => 'Sally',
2314
            'games_played' =>
2315
                [
2316
                    'Battlefield' =>
2317
                        [
2318
                            'weapon' => 'sniper rifle',
2319
                            'rank' => 'Sergeant V',
2320
                            'level' => 20,
2321
                        ],
2322
                    'Crazy Tennis' =>
2323
                        [
2324
                            'won' => 4,
2325
                            'lost' => 1,
2326
                        ],
2327
                    'Puzzler' =>
2328
                        [
2329
                            'time' => 7,
2330
                        ],
2331
                ],
2332
        ]);
2333
2334
        $dao->save($player);
2335
2336
        $this->assertTrue(true);
2337
    }
2338
2339
    public function testCanReadVirtualColumn(): void
2340
    {
2341
        if (!$this->tdbmService->getConnection()->getDatabasePlatform() instanceof MySqlPlatform || self::isMariaDb($this->tdbmService->getConnection())) {
2342
            $this->markTestSkipped('ReadOnly column is only tested with MySQL');
2343
        }
2344
2345
        $dao = new PlayerDao($this->tdbmService);
2346
2347
        $player = $dao->getById(1);
2348
        $this->assertSame('Sally', $player->getNamesVirtual());
2349
    }
2350
2351
    private function skipOracle()
2352
    {
2353
        if (self::getConnection()->getDatabasePlatform() instanceof OraclePlatform) {
2354
            $this->markTestSkipped('Not supported in Oracle');
2355
        }
2356
    }
2357
}
2358