Split module structure contracts
This commit is contained in:
142
tests/Architecture/ModuleManifestContractTest.php
Normal file
142
tests/Architecture/ModuleManifestContractTest.php
Normal file
@@ -0,0 +1,142 @@
|
||||
<?php
|
||||
|
||||
namespace MintyPHP\Tests\Architecture;
|
||||
|
||||
final class ModuleManifestContractTest extends AbstractModuleStructureContractTestCase
|
||||
{
|
||||
public function testAtLeastOneModuleExists(): void
|
||||
{
|
||||
self::assertNotEmpty(self::$modules, 'Expected at least one module under modules/');
|
||||
}
|
||||
|
||||
public function testEveryModuleHasManifest(): void
|
||||
{
|
||||
$modulesDir = dirname(__DIR__, 2) . '/modules';
|
||||
if (!is_dir($modulesDir)) {
|
||||
self::markTestSkipped('No modules/ directory');
|
||||
}
|
||||
|
||||
$entries = scandir($modulesDir) ?: [];
|
||||
foreach ($entries as $entry) {
|
||||
if ($entry === '.' || $entry === '..' || !is_dir($modulesDir . '/' . $entry)) {
|
||||
continue;
|
||||
}
|
||||
self::assertFileExists(
|
||||
$modulesDir . '/' . $entry . '/module.php',
|
||||
"Module directory '{$entry}' is missing module.php manifest"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public function testManifestIdMatchesDirectoryName(): void
|
||||
{
|
||||
foreach (self::$modules as $module) {
|
||||
self::assertSame(
|
||||
$module['id'],
|
||||
$module['manifest']->id,
|
||||
"Module directory '{$module['id']}' has mismatched manifest id '{$module['manifest']->id}'"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public function testManifestHasVersion(): void
|
||||
{
|
||||
foreach (self::$modules as $module) {
|
||||
self::assertMatchesRegularExpression(
|
||||
'/^\d+\.\d+\.\d+$/',
|
||||
$module['manifest']->version,
|
||||
"Module '{$module['id']}' must have a semver version (got '{$module['manifest']->version}')"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public function testManifestHasAllRequiredKeys(): void
|
||||
{
|
||||
$requiredKeys = [
|
||||
'id', 'version', 'enabled_by_default', 'load_order',
|
||||
'routes', 'public_paths', 'container_registrars',
|
||||
'ui_slots', 'search_resources', 'asset_groups',
|
||||
'scheduler_jobs', 'layout_context_providers',
|
||||
'session_providers', 'permissions',
|
||||
];
|
||||
|
||||
foreach (self::$modules as $module) {
|
||||
foreach ($requiredKeys as $key) {
|
||||
self::assertArrayHasKey(
|
||||
$key,
|
||||
$module['raw'],
|
||||
"Module '{$module['id']}' manifest is missing required key '{$key}'"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function testRequiresFieldContainsOnlyValidModuleIds(): void
|
||||
{
|
||||
foreach (self::$modules as $module) {
|
||||
foreach ($module['manifest']->requires as $index => $requiredId) {
|
||||
self::assertIsString($requiredId);
|
||||
self::assertNotEmpty(
|
||||
trim($requiredId),
|
||||
"Module '{$module['id']}' requires[{$index}] must be a non-empty string"
|
||||
);
|
||||
self::assertMatchesRegularExpression(
|
||||
'/^[a-z][a-z0-9_-]*$/',
|
||||
$requiredId,
|
||||
"Module '{$module['id']}' requires[{$index}] has invalid module id format: '{$requiredId}'"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
self::addToAssertionCount(1);
|
||||
}
|
||||
|
||||
public function testModuleHasLibDirectory(): void
|
||||
{
|
||||
foreach (self::$modules as $module) {
|
||||
self::assertDirectoryExists(
|
||||
$module['path'] . '/lib',
|
||||
"Module '{$module['id']}' must have a lib/ directory for PHP classes"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public function testRoutesHavePathAndTarget(): void
|
||||
{
|
||||
foreach (self::$modules as $module) {
|
||||
foreach ($module['manifest']->routes as $idx => $route) {
|
||||
self::assertArrayHasKey(
|
||||
'path',
|
||||
$route,
|
||||
"Module '{$module['id']}' route[{$idx}] is missing 'path'"
|
||||
);
|
||||
self::assertArrayHasKey(
|
||||
'target',
|
||||
$route,
|
||||
"Module '{$module['id']}' route[{$idx}] is missing 'target'"
|
||||
);
|
||||
self::assertNotEmpty(
|
||||
trim((string) $route['path']),
|
||||
"Module '{$module['id']}' route[{$idx}] has empty 'path'"
|
||||
);
|
||||
self::assertNotEmpty(
|
||||
trim((string) $route['target']),
|
||||
"Module '{$module['id']}' route[{$idx}] has empty 'target'"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function testModulesWithRoutesHavePagesDirectory(): void
|
||||
{
|
||||
foreach (self::$modules as $module) {
|
||||
if (empty($module['manifest']->routes)) {
|
||||
continue;
|
||||
}
|
||||
self::assertDirectoryExists(
|
||||
$module['path'] . '/pages',
|
||||
"Module '{$module['id']}' declares routes but has no pages/ directory"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user