php/tests/Feature/LifecycleEventProviderTest.php
Snider be304e7b1a
Some checks failed
CI / PHP 8.4 (pull_request) Failing after 2m3s
CI / PHP 8.3 (pull_request) Failing after 2m18s
CI / PHP 8.4 (push) Failing after 2m2s
CI / PHP 8.3 (push) Failing after 2m20s
Publish Composer Package / publish (push) Failing after 10s
fix(lifecycle): deduplicate route names from multi-domain registrations
When the same route file is registered on multiple domains (e.g.
core.test, hub.core.test, core.localhost), Laravel's route:cache
fails with "Another route has already been assigned name". Add
deduplicateRouteNames() to strip names from duplicate routes,
keeping only the first registration. Extract processViews(),
processLivewire(), and refreshRoutes() helpers to reduce
duplication across fire* methods.

Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-21 23:19:30 +00:00

159 lines
5.3 KiB
PHP

<?php
declare(strict_types=1);
namespace Core\Tests\Feature;
use Core\Events\ApiRoutesRegistering;
use Core\Events\FrameworkBooted;
use Core\Events\WebRoutesRegistering;
use Core\LifecycleEventProvider;
use Core\ModuleRegistry;
use Core\ModuleScanner;
use Core\Tests\TestCase;
use Illuminate\Support\Facades\Event;
use Illuminate\Support\Facades\Route;
class LifecycleEventProviderTest extends TestCase
{
public function test_provider_is_registered(): void
{
$this->assertInstanceOf(
LifecycleEventProvider::class,
$this->app->getProvider(LifecycleEventProvider::class)
);
}
public function test_provider_registers_module_scanner_as_singleton(): void
{
$scanner1 = $this->app->make(ModuleScanner::class);
$scanner2 = $this->app->make(ModuleScanner::class);
$this->assertSame($scanner1, $scanner2);
}
public function test_provider_registers_module_registry_as_singleton(): void
{
$registry1 = $this->app->make(ModuleRegistry::class);
$registry2 = $this->app->make(ModuleRegistry::class);
$this->assertSame($registry1, $registry2);
}
public function test_provider_fires_framework_booted_event(): void
{
$this->assertTrue(class_exists(FrameworkBooted::class));
}
public function test_provider_registers_modules(): void
{
$registry = new ModuleRegistry(new ModuleScanner);
$registry->register([$this->getFixturePath('Mod')]);
$this->assertTrue($registry->isRegistered());
$this->assertNotEmpty($registry->getMappings());
}
public function test_fire_web_routes_fires_event(): void
{
LifecycleEventProvider::fireWebRoutes();
$this->assertTrue(true);
}
public function test_fire_admin_booting_fires_event(): void
{
LifecycleEventProvider::fireAdminBooting();
$this->assertTrue(true);
}
public function test_fire_client_routes_fires_event(): void
{
LifecycleEventProvider::fireClientRoutes();
$this->assertTrue(true);
}
public function test_fire_api_routes_fires_event(): void
{
LifecycleEventProvider::fireApiRoutes();
$this->assertTrue(true);
}
public function test_fire_mcp_tools_returns_array(): void
{
$handlers = LifecycleEventProvider::fireMcpTools();
$this->assertIsArray($handlers);
}
public function test_fire_web_routes_deduplicates_route_names_across_domains(): void
{
// Register the same named route on two different domains
Event::listen(WebRoutesRegistering::class, function (WebRoutesRegistering $event) {
$event->routes(fn () => Route::domain('example.test')
->name('hub.')
->group(function () {
Route::get('/dashboard', fn () => 'ok')->name('dashboard');
}));
$event->routes(fn () => Route::domain('hub.example.test')
->name('hub.')
->group(function () {
Route::get('/dashboard', fn () => 'ok')->name('dashboard');
}));
});
LifecycleEventProvider::fireWebRoutes();
$routes = app('router')->getRoutes();
$named = collect($routes->getRoutes())
->filter(fn ($r) => $r->getName() === 'hub.dashboard');
$this->assertCount(1, $named, 'Only one route should keep the name "hub.dashboard"');
// Both routes should still exist (just one unnamed)
$allDashboard = collect($routes->getRoutes())
->filter(fn ($r) => $r->uri() === 'dashboard');
$this->assertCount(2, $allDashboard, 'Both domain routes should still be registered');
}
public function test_fire_api_routes_deduplicates_route_names_across_domains(): void
{
Event::listen(ApiRoutesRegistering::class, function (ApiRoutesRegistering $event) {
$event->routes(fn () => Route::domain('api.example.test')
->name('api.')
->group(function () {
Route::get('/users', fn () => 'ok')->name('users.index');
}));
$event->routes(fn () => Route::domain('api.hub.example.test')
->name('api.')
->group(function () {
Route::get('/users', fn () => 'ok')->name('users.index');
}));
});
LifecycleEventProvider::fireApiRoutes();
$routes = app('router')->getRoutes();
$named = collect($routes->getRoutes())
->filter(fn ($r) => $r->getName() === 'api.users.index');
$this->assertCount(1, $named, 'Only one route should keep the name "api.users.index"');
}
public function test_deduplication_preserves_unique_route_names(): void
{
Event::listen(WebRoutesRegistering::class, function (WebRoutesRegistering $event) {
$event->routes(fn () => Route::domain('example.test')
->group(function () {
Route::get('/home', fn () => 'ok')->name('home');
Route::get('/about', fn () => 'ok')->name('about');
}));
});
LifecycleEventProvider::fireWebRoutes();
$routes = app('router')->getRoutes();
$this->assertNotNull($routes->getByName('home'));
$this->assertNotNull($routes->getByName('about'));
}
}