Files
netslim-core/tests/Support/TestRuntimeFactory.php

164 lines
4.5 KiB
PHP

<?php
declare(strict_types=1);
namespace Tests\Support;
use Netig\Netslim\Kernel\Runtime\Module\ModuleRegistry;
use Netig\Netslim\Kernel\Runtime\RuntimePaths;
/**
* Prépare une mini application consommatrice éphémère pour les tests du core.
*
* Tous les chemins runtime persistants (database/, var/, public/media) sont
* créés sous un répertoire temporaire isolé afin que la suite de tests ne
* touche ni le dépôt courant ni un projet client réel.
*/
final class TestRuntimeFactory
{
private static ?string $projectRoot = null;
public static function boot(): void
{
if (self::$projectRoot !== null) {
self::applyRuntimeRoots();
return;
}
$baseRoot = rtrim(sys_get_temp_dir(), DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . 'netslim-core-tests';
if (!is_dir($baseRoot)) {
mkdir($baseRoot, 0777, true);
}
$projectRoot = $baseRoot . DIRECTORY_SEPARATOR . 'run-' . bin2hex(random_bytes(6));
mkdir($projectRoot, 0777, true);
self::copyDirectory(self::fixtureRoot() . '/config', $projectRoot . '/config');
self::copyDirectory(self::fixtureRoot() . '/templates', $projectRoot . '/templates');
foreach (['database', 'var/cache/twig', 'var/cache/htmlpurifier', 'var/logs', 'public/media'] as $directory) {
$path = $projectRoot . DIRECTORY_SEPARATOR . str_replace('/', DIRECTORY_SEPARATOR, $directory);
if (!is_dir($path)) {
mkdir($path, 0777, true);
}
}
self::$projectRoot = $projectRoot;
self::applyRuntimeRoots();
register_shutdown_function(static function (): void {
TestRuntimeFactory::cleanup();
});
}
public static function projectRoot(): string
{
self::boot();
return self::$projectRoot;
}
public static function applicationRoot(): string
{
return self::projectRoot();
}
public static function path(string $relativePath = ''): string
{
$root = self::projectRoot();
return $relativePath === ''
? $root
: $root . DIRECTORY_SEPARATOR . ltrim(str_replace('/', DIRECTORY_SEPARATOR, $relativePath), DIRECTORY_SEPARATOR);
}
public static function resetRuntime(): void
{
self::boot();
self::applyRuntimeRoots();
}
private static function applyRuntimeRoots(): void
{
if (self::$projectRoot === null) {
throw new \LogicException('Test runtime project root is not initialized.');
}
RuntimePaths::setProjectRoot(self::$projectRoot);
RuntimePaths::setApplicationRoot(self::$projectRoot);
ModuleRegistry::reset();
}
public static function cleanup(): void
{
if (self::$projectRoot === null || !is_dir(self::$projectRoot)) {
return;
}
self::deleteDirectory(self::$projectRoot);
self::$projectRoot = null;
RuntimePaths::resetProjectRoot();
RuntimePaths::resetApplicationRoot();
ModuleRegistry::reset();
}
private static function fixtureRoot(): string
{
return dirname(__DIR__) . '/Fixtures/Application';
}
private static function copyDirectory(string $source, string $destination): void
{
if (!is_dir($destination)) {
mkdir($destination, 0777, true);
}
$items = scandir($source);
if ($items === false) {
throw new \RuntimeException(sprintf('Unable to read fixture directory: %s', $source));
}
foreach ($items as $item) {
if ($item === '.' || $item === '..') {
continue;
}
$from = $source . DIRECTORY_SEPARATOR . $item;
$to = $destination . DIRECTORY_SEPARATOR . $item;
if (is_dir($from)) {
self::copyDirectory($from, $to);
continue;
}
copy($from, $to);
}
}
private static function deleteDirectory(string $path): void
{
$items = scandir($path);
if ($items === false) {
return;
}
foreach ($items as $item) {
if ($item === '.' || $item === '..') {
continue;
}
$current = $path . DIRECTORY_SEPARATOR . $item;
if (is_dir($current)) {
self::deleteDirectory($current);
continue;
}
@unlink($current);
}
@rmdir($path);
}
}