Skip to content

Commit

Permalink
Merge pull request #476 from alexislefebvre/2.x-add-tests-with-mysql-…
Browse files Browse the repository at this point in the history
…database-cache

2.x: Fix issues with MySQL database backup
  • Loading branch information
alexislefebvre authored Dec 27, 2018
2 parents 35cf946 + 92b616a commit c65cf21
Show file tree
Hide file tree
Showing 14 changed files with 233 additions and 75 deletions.
5 changes: 4 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,4 +41,7 @@ install:

# In phpunit.xml.dist, tests annotated with "@group mysql" are excluded,
# revert this.
script: php ./vendor/bin/phpunit --exclude-group ""
# Run tests twice to ensure that tests are idempotent even if database caching is enabled
script:
- php ./vendor/bin/phpunit --exclude-group ""
- php ./vendor/bin/phpunit --exclude-group ""
11 changes: 8 additions & 3 deletions src/Services/DatabaseBackup/AbstractDatabaseBackup.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,18 +33,23 @@ abstract class AbstractDatabaseBackup implements DatabaseBackupInterface
*
* @var array
*/
protected $classNames;
protected $classNames = [];

public function __construct(ContainerInterface $container, FixturesLoaderFactory $fixturesLoaderFactory)
{
$this->container = $container;
$this->fixturesLoaderFactory = $fixturesLoaderFactory;
}

public function init(array $metadatas, array $classNames): void
public function init(array $metadatas, array $classNames, bool $append = false): void
{
$this->metadatas = $metadatas;
$this->classNames = $classNames;

if ($append) {
$this->classNames = array_merge($this->classNames, $classNames);
} else {
$this->classNames = $classNames;
}
}

/**
Expand Down
4 changes: 2 additions & 2 deletions src/Services/DatabaseBackup/DatabaseBackupInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,13 @@
*/
interface DatabaseBackupInterface
{
public function init(array $metadatas, array $classNames): void;
public function init(array $metadatas, array $classNames, bool $append = false): void;

public function getBackupFilePath(): string;

public function isBackupActual(): bool;

public function backup(AbstractExecutor $executor): void;

public function restore(AbstractExecutor $executor): void;
public function restore(AbstractExecutor $executor, array $excludedTables = []): void;
}
8 changes: 1 addition & 7 deletions src/Services/DatabaseBackup/MongodbDatabaseBackup.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,6 @@ final class MongodbDatabaseBackup extends AbstractDatabaseBackup implements Data

protected static $databases;

public function init(array $metadatas, array $classNames): void
{
$this->metadatas = $metadatas;
$this->classNames = $classNames;
}

public function getBackupFilePath(): string
{
return $this->container->getParameter('kernel.cache_dir').'/test_mongodb_'.md5(serialize($this->metadatas).serialize($this->classNames));
Expand Down Expand Up @@ -90,7 +84,7 @@ public function backup(AbstractExecutor $executor): void
self::$metadata = $dm->getMetadataFactory()->getLoadedMetadata();
}

public function restore(AbstractExecutor $executor): void
public function restore(AbstractExecutor $executor, array $excludedTables = []): void
{
/** @var DocumentManager $dm */
$dm = $executor->getReferenceRepository()->getManager();
Expand Down
31 changes: 19 additions & 12 deletions src/Services/DatabaseBackup/MysqlDatabaseBackup.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,6 @@ final class MysqlDatabaseBackup extends AbstractDatabaseBackup implements Databa
{
protected static $referenceData;

protected static $sql;

protected static $metadata;

protected static $schemaUpdatedFlag = false;
Expand All @@ -40,11 +38,7 @@ public function getReferenceBackupFilePath(): string

protected function getBackup()
{
if (empty(self::$sql)) {
self::$sql = file_get_contents($this->getBackupFilePath());
}

return self::$sql;
return file_get_contents($this->getBackupFilePath());
}

protected function getReferenceBackup(): string
Expand Down Expand Up @@ -84,7 +78,7 @@ public function backup(AbstractExecutor $executor): void
$executor->getReferenceRepository()->save($this->getBackupFilePath());
self::$metadata = $em->getMetadataFactory()->getLoadedMetadata();

exec("mysqldump -h $dbHost -u $dbUser -p$dbPass --no-create-info --skip-triggers --no-create-db --no-tablespaces --compact $dbName > {$this->getBackupFilePath()}");
exec("MYSQL_PWD=$dbPass mysqldump --host $dbHost --port=$dbPort --user $dbUser --no-create-info --skip-triggers --no-create-db --no-tablespaces --compact $dbName > {$this->getBackupFilePath()}");
}

protected function updateSchemaIfNeed(EntityManager $em)
Expand All @@ -100,7 +94,7 @@ protected function updateSchemaIfNeed(EntityManager $em)
}
}

public function restore(AbstractExecutor $executor): void
public function restore(AbstractExecutor $executor, array $excludedTables = []): void
{
/** @var EntityManager $em */
$em = $executor->getReferenceRepository()->getManager();
Expand All @@ -110,10 +104,23 @@ public function restore(AbstractExecutor $executor): void
$this->updateSchemaIfNeed($em);
$truncateSql = [];
foreach ($this->metadatas as $classMetadata) {
$truncateSql[] = 'DELETE FROM '.$classMetadata->table['name']; // in small tables it's really faster than truncate
$tableName = $classMetadata->table['name'];

if (!in_array($tableName, $excludedTables)) {
$truncateSql[] = 'DELETE FROM '.$tableName; // in small tables it's really faster than truncate
}
}
$connection->query(implode(';', $truncateSql));
$connection->query($this->getBackup());
if (!empty($truncateSql)) {
$connection->query(implode(';', $truncateSql));
}

// Only run query if it exists, to avoid the following exception:
// SQLSTATE[42000]: Syntax error or access violation: 1065 Query was empty
$backup = $this->getBackup();
if (!empty($backup)) {
$connection->query($backup);
}

$connection->query('SET FOREIGN_KEY_CHECKS = 1;');

if (self::$metadata) {
Expand Down
2 changes: 1 addition & 1 deletion src/Services/DatabaseBackup/SqliteDatabaseBackup.php
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ public function backup(AbstractExecutor $executor): void
copy($this->getDatabaseName($connection), $this->getBackupFilePath());
}

public function restore(AbstractExecutor $executor): void
public function restore(AbstractExecutor $executor, array $excludedTables = []): void
{
/** @var EntityManager $em */
$em = $executor->getReferenceRepository()->getManager();
Expand Down
5 changes: 3 additions & 2 deletions src/Services/DatabaseTools/ORMDatabaseTool.php
Original file line number Diff line number Diff line change
Expand Up @@ -96,8 +96,9 @@ public function loadFixtures(array $classNames = [], bool $append = false): Abst
$this->createDatabaseIfNotExists();

$backupService = $this->getBackupService();

if ($backupService) {
$backupService->init($this->getMetadatas(), $classNames);
$backupService->init($this->getMetadatas(), $classNames, $append);

if ($backupService->isBackupActual()) {
if (null !== $this->connection) {
Expand All @@ -110,7 +111,7 @@ public function loadFixtures(array $classNames = [], bool $append = false): Abst
$this->webTestCase->preFixtureBackupRestore($this->om, $referenceRepository, $backupService->getBackupFilePath());
$executor = $this->getExecutor($this->getPurger());
$executor->setReferenceRepository($referenceRepository);
$backupService->restore($executor);
$backupService->restore($executor, $this->excludedDoctrineTables);
$this->webTestCase->postFixtureBackupRestore($backupService->getBackupFilePath());

return $executor;
Expand Down
9 changes: 0 additions & 9 deletions tests/AppConfigLeanFramework/AppConfigLeanFrameworkKernel.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,6 @@

namespace Liip\FunctionalTestBundle\Tests\AppConfigLeanFramework;

/*
* This file is part of the Liip/FunctionalTestBundle
*
* (c) Lukas Kahwe Smith <[email protected]>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/

use Liip\FunctionalTestBundle\Tests\App\AppKernel;
use Symfony\Component\Config\Loader\LoaderInterface;

Expand Down
9 changes: 0 additions & 9 deletions tests/AppConfigMysql/AppConfigMysqlKernel.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,6 @@

namespace Liip\FunctionalTestBundle\Tests\AppConfigMysql;

/*
* This file is part of the Liip/FunctionalTestBundle
*
* (c) Lukas Kahwe Smith <[email protected]>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/

use Liip\FunctionalTestBundle\Tests\App\AppKernel;
use Symfony\Component\Config\Loader\LoaderInterface;

Expand Down
32 changes: 32 additions & 0 deletions tests/AppConfigMysqlCacheDb/AppConfigMysqlKernelCacheDb.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<?php

declare(strict_types=1);

/*
* This file is part of the Liip/FunctionalTestBundle
*
* (c) Lukas Kahwe Smith <[email protected]>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/

namespace Liip\FunctionalTestBundle\Tests\AppConfigMysqlCacheDb;

use Liip\FunctionalTestBundle\Tests\AppConfigMysql\AppConfigMysqlKernel;
use Symfony\Component\Config\Loader\LoaderInterface;

class AppConfigMysqlKernelCacheDb extends AppConfigMysqlKernel
{
/**
* Load the config.yml from the current directory.
*/
public function registerContainerConfiguration(LoaderInterface $loader): void
{
// Load the default file.
parent::registerContainerConfiguration($loader);

// Load the file with MySQL configuration
$loader->load(__DIR__.'/config.yml');
}
}
5 changes: 5 additions & 0 deletions tests/AppConfigMysqlCacheDb/config.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# inherits configuration from ../AppConfigMysql/config.yml

liip_functional_test:
cache_db:
mysql: liip_functional_test.services_database_backup.mysql
9 changes: 0 additions & 9 deletions tests/AppConfigPhpcr/AppConfigPhpcrKernel.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,6 @@

namespace Liip\FunctionalTestBundle\Tests\AppConfigPhpcr;

/*
* This file is part of the Liip/FunctionalTestBundle
*
* (c) Lukas Kahwe Smith <[email protected]>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/

use Liip\FunctionalTestBundle\Tests\App\AppKernel;
use Symfony\Component\Config\Loader\LoaderInterface;

Expand Down
120 changes: 120 additions & 0 deletions tests/Test/WebTestCaseConfigMysqlCacheDbTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
<?php

declare(strict_types=1);

/*
* This file is part of the Liip/FunctionalTestBundle
*
* (c) Lukas Kahwe Smith <[email protected]>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/

namespace Liip\FunctionalTestBundle\Tests\Test;

use Liip\FunctionalTestBundle\Tests\AppConfigMysqlCacheDb\AppConfigMysqlKernelCacheDb;

/**
* Test MySQL database with database caching enabled.
*
* The following tests require a connection to a MySQL database,
* they are disabled by default (see phpunit.xml.dist).
*
* In order to run them, you have to set the MySQL connection
* parameters in the Tests/AppConfigMysql/config.yml file and
* add “--exclude-group ""” when running PHPUnit.
*
* Use Tests/AppConfigMysql/AppConfigMysqlKernelCacheDb.php instead of
* Tests/App/AppKernel.php.
* So it must be loaded in a separate process.
*
* @runTestsInSeparateProcesses
* @preserveGlobalState disabled
*/
class WebTestCaseConfigMysqlCacheDbTest extends WebTestCaseConfigMysqlTest
{
protected static function getKernelClass(): string
{
return AppConfigMysqlKernelCacheDb::class;
}

/**
* @group mysql
*/
public function testLoadFixturesAndCheckBackup(): void
{
$this->loadFixtures([
'Liip\FunctionalTestBundle\Tests\App\DataFixtures\ORM\LoadUserData',
]);

// Load data from database
$em = $this->getContainer()
->get('doctrine.orm.entity_manager');

$users = $em->getRepository('LiipFunctionalTestBundle:User')
->findAll();

// Check that all User have been saved to database
$this->assertCount(
2,
$users
);

/** @var \Liip\FunctionalTestBundle\Tests\App\Entity\User $user1 */
$user1 = $em->getRepository('LiipFunctionalTestBundle:User')
->findOneBy([
'id' => 1,
]);

$this->assertSame(
'[email protected]',
$user1->getEmail()
);

$this->assertTrue(
$user1->getEnabled()
);

// Store salt for later use
$salt = $user1->getSalt();

// Clean database
$this->loadFixtures();

$users = $em->getRepository('LiipFunctionalTestBundle:User')
->findAll();

// Check that all User have been removed from database
$this->assertCount(
0,
$users
);

// Load fixtures again
$this->loadFixtures([
'Liip\FunctionalTestBundle\Tests\App\DataFixtures\ORM\LoadUserData',
]);

$users = $em->getRepository('LiipFunctionalTestBundle:User')
->findAll();

// Check that all User have been loaded again in database
$this->assertCount(
2,
$users
);

$user1 = $em->getRepository('LiipFunctionalTestBundle:User')
->findOneBy([
'id' => 1,
]);

// Salt is a random string, if it's the same as before it means that the backup has been saved and loaded
// successfully
$this->assertSame(
$salt,
$user1->getSalt()
);
}
}
Loading

0 comments on commit c65cf21

Please sign in to comment.