Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP: FEATURE: move FlowContextTrait into neos/flow #3181

Closed
wants to merge 1 commit into from
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
191 changes: 191 additions & 0 deletions Neos.Flow/Classes/TestSuite/Behavior/Behat/FlowBootstrapTrait.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
<?php
declare(strict_types=1);

namespace Neos\Flow\TestSuite\Behaviour\Behat;

/*
* This file is part of the Neos.Flow package.
*
* (c) Contributors of the Neos Project - www.neos.io
*
* This package is Open Source Software. For the full copyright and license
* information, please view the LICENSE file which was distributed with this
* source code.
*/

use Doctrine\DBAL\DBALException;
use Doctrine\ORM\EntityManagerInterface;
use Neos\Flow\Cli\CommandRequestHandler;
use Neos\Flow\Core\Booting\Scripts;
use Neos\Flow\Core\Bootstrap;
use Neos\Flow\Mvc\Routing\Router;
use Neos\Flow\ObjectManagement\ObjectManagerInterface;
use Neos\Flow\Persistence\Doctrine\Service;
use Neos\Flow\Persistence\PersistenceManagerInterface;
use Neos\Flow\Security\Policy\PolicyService;

trait FlowBootstrapTrait
{
/**
* @var Bootstrap
*/
static protected $bootstrap;

/**
* @var Router
*/
protected $router;

/**
* @var ObjectManagerInterface
*/
protected $objectManager;

/**
* @var \Doctrine\DBAL\Schema\Schema
*/
protected static $databaseSchema;

/**
* Create a flow bootstrap instance
*/
protected function initializeFlow(): Bootstrap
{
require_once(__DIR__ . '/../../../Core/Bootstrap.php');
if (!defined('FLOW_PATH_ROOT')) {
define('FLOW_PATH_ROOT', realpath(__DIR__ . '/../../../../..') . '/');
}
// The new classloader needs warnings converted to exceptions
if (!defined('BEHAT_ERROR_REPORTING')) {
define('BEHAT_ERROR_REPORTING', E_ALL);
}
$bootstrap = new Bootstrap('Testing/Behat');
Scripts::initializeClassLoader($bootstrap);
Scripts::initializeSignalSlot($bootstrap);
Scripts::initializePackageManagement($bootstrap);
// FIXME: We NEED to define a request due to return type declarations, and with the
// current state of the behat test (setup) we cannot use a Http\RequestHandler because
// some code would then try to access the httpRequest and Response which is not available,
// so we need to think if we "mock" the whole component chain and a Http\RequestHandler or
// live with having a CommandRequestHandler here. (A specialisted TestHandler for this case
// would probably be a good idea.
$bootstrap->setActiveRequestHandler(new CommandRequestHandler($bootstrap));
$bootstrap->buildRuntimeSequence()->invoke($bootstrap);

return $bootstrap;
}

public function getObjectManager(): ObjectManagerInterface
{
return $this->objectManager;
}

/**
* @AfterSuite
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we switch this to attributes in the future we would create a dependency on behat, therefore I think we want a separate package even if it's just this trait?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yesssesses it’s in neos/be hat already. This idea is overhauled and can be closed. We decided against this.

*/
public static function shutdownFlow(): void
{
if (self::$bootstrap !== null) {
self::$bootstrap->shutdown('Runtime');
}
}

/**
* @BeforeScenario @fixtures
*/
public function resetTestFixtures(): void
{
/** @var EntityManagerInterface $entityManager */
$entityManager = $this->objectManager->get(EntityManagerInterface::class);
$entityManager->clear();

if (self::$databaseSchema !== null) {
$this->truncateTables($entityManager);
} else {
try {
/** @var Service $doctrineService */
$doctrineService = $this->objectManager->get(Service::class);
$doctrineService->executeMigrations();
$needsTruncate = true;
} catch (DBALException $exception) {
// Do an initial teardown to drop the schema cleanly
$this->objectManager->get(PersistenceManagerInterface::class)->tearDown();

/** @var Service $doctrineService */
$doctrineService = $this->objectManager->get(Service::class);
$doctrineService->executeMigrations();
$needsTruncate = false;
} catch (\PDOException $exception) {
if ($exception->getMessage() !== 'There is no active transaction') {
throw $exception;
}
$needsTruncate = true;
}

$schema = $entityManager->getConnection()->getSchemaManager()->createSchema();
self::$databaseSchema = $schema;

if ($needsTruncate) {
$this->truncateTables($entityManager);
}

// FIXME Check if this is needed at all!
$proxyFactory = $entityManager->getProxyFactory();
$proxyFactory->generateProxyClasses($entityManager->getMetadataFactory()->getAllMetadata());
}

$this->resetPolicyService();
}

/**
* Truncate all known tables
*
* @param EntityManagerInterface $entityManager
* @return void
*/
private function truncateTables(EntityManagerInterface $entityManager): void
{
$connection = $entityManager->getConnection();

$tables = array_filter(self::$databaseSchema->getTables(), function ($table) {
return $table->getName() !== 'flow_doctrine_migrationstatus';
});
switch ($connection->getDatabasePlatform()->getName()) {
case 'mysql':
$sql = 'SET FOREIGN_KEY_CHECKS=0;';
foreach ($tables as $table) {
$sql .= 'TRUNCATE `' . $table->getName() . '`;';
}
$sql .= 'SET FOREIGN_KEY_CHECKS=1;';
$connection->executeQuery($sql);
break;
case 'sqlite':
$sql = 'PRAGMA foreign_keys = OFF;';
foreach ($tables as $table) {
$sql .= 'DELETE FROM `' . $table->getName() . '`;';
}
$sql .= 'PRAGMA foreign_keys = ON;';
$connection->executeQuery($sql);
break;
case 'postgresql':
default:
foreach ($tables as $table) {
$sql = 'TRUNCATE ' . $table->getName() . ' CASCADE;';
$connection->executeQuery($sql);
}
break;
}
}

/**
* Reset policy service
*
* This is needed to remove cached role entities after resetting the database.
*
* @return void
*/
private function resetPolicyService(): void
{
$this->objectManager->get(PolicyService::class)->reset();
}
}
Loading