diff --git a/.gitignore b/.gitignore index f726eeb..2e8382e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,30 +1,17 @@ -# ============================================ -# Environnement & Configuration -# ============================================ +# Environment .env -# ============================================ -# Dépendances Composer -# ============================================ +# Composer vendor/ -# ============================================ -# Base de données -# ============================================ -database/ - -# ============================================ -# Cache & Logs -# ============================================ +# Runtime caches and reports coverage/ var/ .php-cs-fixer.cache .phpstan/ .phpunit.result.cache -# ============================================ -# IDE & OS -# ============================================ +# IDE / OS .vscode/ .idea/ *.swp diff --git a/README.md b/README.md index 834d885..291773a 100644 --- a/README.md +++ b/README.md @@ -11,8 +11,6 @@ Ce dépôt est conçu pour être consommé par des projets applicatifs séparés ## Installation depuis le dépôt Git en HTTPS -### Pendant le développement du core - ```json { "repositories": [ @@ -22,30 +20,14 @@ Ce dépôt est conçu pour être consommé par des projets applicatifs séparés } ], "require": { - "netig/netslim-core": "^0.3@dev" - } -} -``` - -### Après la première release taguée - -```json -{ - "repositories": [ - { - "type": "vcs", - "url": "https://git.netig.net/netig/netslim-core.git" - } - ], - "require": { - "netig/netslim-core": "^0.1" + "netig/netslim-core": "dev-main" } } ``` ## Option locale pendant le développement -Pour développer le core et une application consommatrice côte à côte, un `path` repository local reste pratique, mais ce n'est pas le mode de consommation par défaut : +Pour développer le core et une application consommatrice côte à côte, un `path` repository local reste pratique : ```json { @@ -56,7 +38,7 @@ Pour développer le core et une application consommatrice côte à côte, un `pa } ], "require": { - "netig/netslim-core": "^0.3@dev" + "netig/netslim-core": "dev-main" } } ``` @@ -69,7 +51,7 @@ Le package ne porte pas d'application concrète. Un projet consommateur doit fou - ses templates applicatifs ; - son pipeline d'assets. -Si l'application active `Identity` et exécute le provisionnement initial, elle doit aussi définir `ADMIN_USERNAME`, `ADMIN_EMAIL` et `ADMIN_PASSWORD` dans son `.env`. Ces variables ne sont plus exigées par le bootstrap du noyau seul. +Si l'application active `Identity` et exécute le provisionnement initial, elle doit aussi définir `ADMIN_USERNAME`, `ADMIN_EMAIL` et `ADMIN_PASSWORD` dans son `.env`. Si l'application active `Notifications`, elle doit configurer `MAIL_HOST`, `MAIL_PORT`, `MAIL_USERNAME`, `MAIL_PASSWORD`, `MAIL_ENCRYPTION`, `MAIL_FROM` et `MAIL_FROM_NAME` pour permettre l'envoi effectif des emails transactionnels. @@ -83,12 +65,8 @@ Les templates du socle supposent en particulier : Le socle expose principalement : - `Netig\Netslim\Kernel\...` pour le runtime public ; -- les interfaces applicatives documentées des modules partagés (`Netig\Netslim\Identity\Application\*ServiceInterface`, `Netig\Netslim\Settings\Application\SettingsServiceInterface`, `Netig\Netslim\AuditLog\Application\AuditLogServiceInterface`, `Netig\Netslim\Notifications\Application\NotificationServiceInterface`, `Netig\Netslim\Taxonomy\Application\TaxonomyServiceInterface`, `Netig\Netslim\Media\Application\MediaServiceInterface`) ; -- `Netig\Netslim\Settings\Contracts\...` ; -- `Netig\Netslim\AuditLog\Contracts\...` ; -- `Netig\Netslim\Notifications\Contracts\...` ; -- `Netig\Netslim\Taxonomy\Contracts\...` ; -- `Netig\Netslim\Media\Contracts\...` ; +- les interfaces applicatives documentées des modules partagés (`Identity`, `Settings`, `AuditLog`, `Notifications`, `Taxonomy`, `Media`) ; +- les contrats publics sous `Netig\Netslim\*/Contracts/` ; - les classes `*Module` des modules partagés. La frontière détaillée entre API publique et API interne est documentée dans [`docs/PUBLIC_API.md`](docs/PUBLIC_API.md). @@ -100,8 +78,4 @@ composer install composer qa ``` -## Gouvernance du package - -- `docs/PUBLIC_API.md` définit la frontière supportée entre le package et les projets consommateurs ; - -> Quand `netslim-core` est installé via Composer, les chemins runtime détectent automatiquement la racine du projet consommateur pour les scripts CLI et les suites de tests qui n'appellent pas explicitement `Bootstrap::create()`. \ No newline at end of file +> Quand `netslim-core` est installé via Composer, les chemins runtime détectent automatiquement la racine du projet consommateur pour les scripts CLI et les suites de tests qui n'appellent pas explicitement `Bootstrap::create()`. diff --git a/composer.json b/composer.json index e6a4251..72c6a77 100644 --- a/composer.json +++ b/composer.json @@ -1,6 +1,6 @@ { "name": "netig/netslim-core", - "description": "Reusable kernel and shared modules for NETslim based applications.", + "description": "Reusable kernel and shared modules for Netslim-based applications.", "license": "MIT", "type": "library", "require": { diff --git a/config/modules.php b/config/modules.php deleted file mode 100644 index 382917d..0000000 --- a/config/modules.php +++ /dev/null @@ -1,5 +0,0 @@ - $value) { $this->envBackup[$key] = $_ENV[$key] ?? null; $_ENV[$key] = $value; @@ -66,8 +65,6 @@ final class ContainerWiringIntegrationTest extends TestCase protected function tearDown(): void { - RuntimePaths::resetApplicationRoot(); - RuntimePaths::resetProjectRoot(); ModuleRegistry::reset(); foreach (self::ENV_DEFAULTS as $key => $_) { $previous = $this->envBackup[$key] ?? null; diff --git a/tests/Kernel/DatabaseReadinessTest.php b/tests/Kernel/DatabaseReadinessTest.php index 1512d68..36f4227 100644 --- a/tests/Kernel/DatabaseReadinessTest.php +++ b/tests/Kernel/DatabaseReadinessTest.php @@ -6,14 +6,14 @@ namespace Tests\Kernel; use Netig\Netslim\Kernel\Persistence\Infrastructure\DatabaseNotProvisionedException; use Netig\Netslim\Kernel\Persistence\Infrastructure\DatabaseReadiness; -use PDO; use PHPUnit\Framework\TestCase; +use Tests\Support\TestDatabaseFactory; final class DatabaseReadinessTest extends TestCase { public function testAssertProvisionedFailsWhenModuleTablesAreMissing(): void { - $db = new PDO('sqlite::memory:'); + $db = TestDatabaseFactory::createInMemory(); $db->exec('CREATE TABLE migrations (id INTEGER PRIMARY KEY AUTOINCREMENT, version TEXT, run_at TEXT)'); $this->expectException(DatabaseNotProvisionedException::class); @@ -24,7 +24,7 @@ final class DatabaseReadinessTest extends TestCase public function testAssertProvisionedAcceptsCompleteCoreSchema(): void { - $db = new PDO('sqlite::memory:'); + $db = TestDatabaseFactory::createInMemory(); $db->exec('CREATE TABLE migrations (id INTEGER PRIMARY KEY AUTOINCREMENT, version TEXT, run_at TEXT)'); $db->exec('CREATE TABLE users (id INTEGER PRIMARY KEY, username TEXT, email TEXT, password_hash TEXT, role TEXT, session_version INTEGER, created_at TEXT)'); diff --git a/tests/Kernel/MigratorTest.php b/tests/Kernel/MigratorTest.php index 88ca514..809ccf1 100644 --- a/tests/Kernel/MigratorTest.php +++ b/tests/Kernel/MigratorTest.php @@ -5,8 +5,8 @@ declare(strict_types=1); namespace Tests\Kernel; use Netig\Netslim\Kernel\Persistence\Infrastructure\Migrator; -use PDO; use PHPUnit\Framework\TestCase; +use Tests\Support\TestDatabaseFactory; /** * Tests unitaires pour Migrator. @@ -18,14 +18,11 @@ use PHPUnit\Framework\TestCase; #[\PHPUnit\Framework\Attributes\AllowMockObjectsWithoutExpectations] final class MigratorTest extends TestCase { - private PDO $db; + private \PDO $db; protected function setUp(): void { - $this->db = new PDO('sqlite::memory:', options: [ - PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, - PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, - ]); + $this->db = TestDatabaseFactory::createInMemory(); } public function testRunCreatesMigrationsTable(): void diff --git a/tests/Kernel/ModuleRegistryTest.php b/tests/Kernel/ModuleRegistryTest.php index 66ab74b..8796e07 100644 --- a/tests/Kernel/ModuleRegistryTest.php +++ b/tests/Kernel/ModuleRegistryTest.php @@ -8,26 +8,23 @@ use Netig\Netslim\Kernel\Runtime\Module\ModuleInterface; use Netig\Netslim\Kernel\Runtime\Module\ModuleRegistry; use Netig\Netslim\Kernel\Runtime\RuntimePaths; use PHPUnit\Framework\TestCase; +use Tests\Support\TestRuntimeFactory; final class ModuleRegistryTest extends TestCase { protected function setUp(): void { - RuntimePaths::resetApplicationRoot(); - RuntimePaths::resetProjectRoot(); - ModuleRegistry::reset(); + TestRuntimeFactory::resetRuntime(); } protected function tearDown(): void { - RuntimePaths::resetApplicationRoot(); - RuntimePaths::resetProjectRoot(); - ModuleRegistry::reset(); + TestRuntimeFactory::resetRuntime(); } public function testModulesAreDeclaredInExpectedOrderForFixtureApplication(): void { - RuntimePaths::setApplicationRoot(dirname(__DIR__, 2) . '/tests/Fixtures/Application'); + TestRuntimeFactory::resetRuntime(); $moduleClasses = array_map( static fn (ModuleInterface $module): string => $module::class, @@ -47,7 +44,7 @@ final class ModuleRegistryTest extends TestCase public function testModuleClassNamesExposeTheActiveApplicationComposition(): void { - RuntimePaths::setApplicationRoot(dirname(__DIR__, 2) . '/tests/Fixtures/Application'); + TestRuntimeFactory::resetRuntime(); self::assertSame([ 'Netig\Netslim\Kernel\Runtime\KernelModule', @@ -62,14 +59,14 @@ final class ModuleRegistryTest extends TestCase public function testApplicationManifestIsResolvedFromTheActiveApplicationRoot(): void { - RuntimePaths::setApplicationRoot(dirname(__DIR__, 2) . '/tests/Fixtures/Application'); + TestRuntimeFactory::resetRuntime(); self::assertFileExists(RuntimePaths::getApplicationConfigPath('modules.php')); } public function testEveryModuleExposesResolvableMetadata(): void { - RuntimePaths::setApplicationRoot(dirname(__DIR__, 2) . '/tests/Fixtures/Application'); + TestRuntimeFactory::resetRuntime(); foreach (ModuleRegistry::modules() as $module) { self::assertNotSame([], $module->definitions(), $module::class . ' should expose DI definitions.'); diff --git a/tests/Kernel/ModuleSchemaTest.php b/tests/Kernel/ModuleSchemaTest.php index 4cff1db..cc02feb 100644 --- a/tests/Kernel/ModuleSchemaTest.php +++ b/tests/Kernel/ModuleSchemaTest.php @@ -6,21 +6,18 @@ namespace Tests\Kernel; use Netig\Netslim\Kernel\Runtime\Module\ModuleRegistry; use Netig\Netslim\Kernel\Runtime\Module\ProvidesSchemaInterface; -use Netig\Netslim\Kernel\Runtime\RuntimePaths; use PHPUnit\Framework\TestCase; +use Tests\Support\TestRuntimeFactory; final class ModuleSchemaTest extends TestCase { protected function setUp(): void { - RuntimePaths::setApplicationRoot(dirname(__DIR__, 2) . '/tests/Fixtures/Application'); - ModuleRegistry::reset(); + TestRuntimeFactory::resetRuntime(); } protected function tearDown(): void { - RuntimePaths::resetApplicationRoot(); - RuntimePaths::resetProjectRoot(); ModuleRegistry::reset(); } diff --git a/tests/Kernel/ProvisionerTest.php b/tests/Kernel/ProvisionerTest.php index ffc0494..ff38dd7 100644 --- a/tests/Kernel/ProvisionerTest.php +++ b/tests/Kernel/ProvisionerTest.php @@ -5,14 +5,15 @@ declare(strict_types=1); namespace Tests\Kernel; use Netig\Netslim\Kernel\Persistence\Infrastructure\Provisioner; -use PDO; use PHPUnit\Framework\TestCase; +use Tests\Support\TestDatabaseFactory; +use Tests\Support\TestRuntimeFactory; #[\PHPUnit\Framework\Attributes\AllowMockObjectsWithoutExpectations] final class ProvisionerTest extends TestCase { - private PDO $db; + private \PDO $db; private string $lockPath; @@ -20,13 +21,11 @@ final class ProvisionerTest extends TestCase protected function setUp(): void { - $this->db = new PDO('sqlite::memory:', options: [ - PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, - PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, - ]); + TestRuntimeFactory::resetRuntime(); + $this->db = TestDatabaseFactory::createInMemory(); $this->db->sqliteCreateFunction('strip_tags', 'strip_tags', 1); - $this->lockPath = dirname(__DIR__, 2) . '/database/.provision.lock'; + $this->lockPath = TestRuntimeFactory::path('database/.provision.lock'); @unlink($this->lockPath); $this->envBackup = [ diff --git a/tests/Kernel/RoutesTest.php b/tests/Kernel/RoutesTest.php index c8f6d5c..3f4dd16 100644 --- a/tests/Kernel/RoutesTest.php +++ b/tests/Kernel/RoutesTest.php @@ -6,23 +6,20 @@ namespace Tests\Kernel; use Netig\Netslim\Kernel\Runtime\Module\ModuleRegistry; use Netig\Netslim\Kernel\Runtime\Routing\Routes; -use Netig\Netslim\Kernel\Runtime\RuntimePaths; use PHPUnit\Framework\TestCase; use Slim\Factory\AppFactory; +use Tests\Support\TestRuntimeFactory; #[\PHPUnit\Framework\Attributes\AllowMockObjectsWithoutExpectations] final class RoutesTest extends TestCase { protected function setUp(): void { - RuntimePaths::setApplicationRoot(dirname(__DIR__, 2) . '/tests/Fixtures/Application'); - ModuleRegistry::reset(); + TestRuntimeFactory::resetRuntime(); } protected function tearDown(): void { - RuntimePaths::resetApplicationRoot(); - RuntimePaths::resetProjectRoot(); ModuleRegistry::reset(); } diff --git a/tests/Kernel/RuntimePathsTest.php b/tests/Kernel/RuntimePathsTest.php index 996a18e..80b338c 100644 --- a/tests/Kernel/RuntimePathsTest.php +++ b/tests/Kernel/RuntimePathsTest.php @@ -6,40 +6,40 @@ namespace Tests\Kernel; use Netig\Netslim\Kernel\Runtime\RuntimePaths; use PHPUnit\Framework\TestCase; +use Tests\Support\TestRuntimeFactory; #[\PHPUnit\Framework\Attributes\AllowMockObjectsWithoutExpectations] final class RuntimePathsTest extends TestCase { protected function tearDown(): void { - RuntimePaths::resetApplicationRoot(); - RuntimePaths::resetProjectRoot(); + TestRuntimeFactory::resetRuntime(); } - public function testGetProjectRootReturnsRepositoryRoot(): void + public function testGetProjectRootReturnsIsolatedTestProjectRoot(): void { - self::assertSame(dirname(__DIR__, 2), RuntimePaths::getProjectRoot()); + self::assertSame(TestRuntimeFactory::projectRoot(), RuntimePaths::getProjectRoot()); } public function testGetConfigPathReturnsRootConfigDirectoryAndFilePath(): void { - self::assertSame(dirname(__DIR__, 2) . '/config', RuntimePaths::getConfigPath()); - self::assertSame(dirname(__DIR__, 2) . '/config/modules.php', RuntimePaths::getConfigPath('modules.php')); + self::assertSame(TestRuntimeFactory::path('config'), RuntimePaths::getConfigPath()); + self::assertSame(TestRuntimeFactory::path('config/modules.php'), RuntimePaths::getConfigPath('modules.php')); } public function testApplicationRootDefaultsToProjectRoot(): void { - self::assertSame(dirname(__DIR__, 2), RuntimePaths::getApplicationRoot()); - self::assertSame(dirname(__DIR__, 2) . '/config', RuntimePaths::getApplicationConfigPath()); + self::assertSame(TestRuntimeFactory::applicationRoot(), RuntimePaths::getApplicationRoot()); + self::assertSame(TestRuntimeFactory::path('config'), RuntimePaths::getApplicationConfigPath()); } - public function testApplicationRootCanPointToFixtureApplication(): void + public function testApplicationRootCanPointToTheIsolatedFixtureApplication(): void { - RuntimePaths::setApplicationRoot(dirname(__DIR__, 2) . '/tests/Fixtures/Application'); + RuntimePaths::setApplicationRoot(TestRuntimeFactory::applicationRoot()); - self::assertSame(dirname(__DIR__, 2) . '/tests/Fixtures/Application', RuntimePaths::getApplicationRoot()); - self::assertSame(dirname(__DIR__, 2) . '/tests/Fixtures/Application/config/modules.php', RuntimePaths::getApplicationConfigPath('modules.php')); - self::assertSame(dirname(__DIR__, 2) . '/tests/Fixtures/Application/templates/Kernel', RuntimePaths::getApplicationPath('templates/Kernel')); + self::assertSame(TestRuntimeFactory::applicationRoot(), RuntimePaths::getApplicationRoot()); + self::assertSame(TestRuntimeFactory::path('config/modules.php'), RuntimePaths::getApplicationConfigPath('modules.php')); + self::assertSame(TestRuntimeFactory::path('templates/Kernel'), RuntimePaths::getApplicationPath('templates/Kernel')); } public function testGetTwigCacheReturnsFalseInDev(): void @@ -57,7 +57,7 @@ final class RuntimePathsTest extends TestCase public function testGetDatabasePathCreatesDatabaseFileWhenMissing(): void { - $dbFile = dirname(__DIR__, 2) . '/database/app.sqlite'; + $dbFile = TestRuntimeFactory::path('database/app.sqlite'); $dbDir = dirname($dbFile); $backup = $dbFile . '.bak-test'; diff --git a/tests/Media/MediaSchemaIntegrationTest.php b/tests/Media/MediaSchemaIntegrationTest.php index 1964307..e1fb647 100644 --- a/tests/Media/MediaSchemaIntegrationTest.php +++ b/tests/Media/MediaSchemaIntegrationTest.php @@ -5,20 +5,19 @@ declare(strict_types=1); namespace Tests\Media; use Netig\Netslim\Kernel\Persistence\Infrastructure\Migrator; -use PDO; use PDOException; use PHPUnit\Framework\TestCase; +use Tests\Support\TestDatabaseFactory; +use Tests\Support\TestRuntimeFactory; final class MediaSchemaIntegrationTest extends TestCase { - private PDO $db; + private \PDO $db; protected function setUp(): void { - $this->db = new PDO('sqlite::memory:', options: [ - PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, - PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, - ]); + TestRuntimeFactory::resetRuntime(); + $this->db = TestDatabaseFactory::createInMemory(); $this->db->sqliteCreateFunction('strip_tags', 'strip_tags', 1); Migrator::run($this->db); diff --git a/tests/README.md b/tests/README.md new file mode 100644 index 0000000..46deb87 --- /dev/null +++ b/tests/README.md @@ -0,0 +1,9 @@ +# Tests du core + +La suite de tests prépare une mini application consommatrice éphémère sous un répertoire temporaire. Les chemins runtime (`database/`, `var/`, `public/media/`) ne pointent donc ni vers le dépôt du core ni vers un projet client réel. + +Organisation : +- `Architecture/` : garde-fous de dépendances et de structure +- `Kernel/` : runtime, bootstrap, wiring, utilitaires transverses +- `Support/` : helpers de tests et fabrication du runtime de test +- `Fixtures/` : manifeste et templates minimaux de l'application de test diff --git a/tests/Support/TestDatabaseFactory.php b/tests/Support/TestDatabaseFactory.php new file mode 100644 index 0000000..8e4611c --- /dev/null +++ b/tests/Support/TestDatabaseFactory.php @@ -0,0 +1,34 @@ + PDO::ERRMODE_EXCEPTION, + PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, + ]); + } + + public static function createFileBacked(string $name): PDO + { + $path = TestRuntimeFactory::path('database/' . $name . '.sqlite'); + if (is_file($path)) { + @unlink($path); + } + + return new PDO('sqlite:' . $path, options: [ + PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, + PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, + ]); + } +} diff --git a/tests/Support/TestRuntimeFactory.php b/tests/Support/TestRuntimeFactory.php new file mode 100644 index 0000000..06b76f9 --- /dev/null +++ b/tests/Support/TestRuntimeFactory.php @@ -0,0 +1,163 @@ +