tmpDir = sys_get_temp_dir() . '/corecore-module-assets-test-' . uniqid(); mkdir($this->tmpDir, 0777, true); } protected function tearDown(): void { $this->removePath($this->tmpDir); } public function testPublishSymlinkCreatesManagedModuleLinksAndRemovesStaleEntries(): void { if (!function_exists('symlink')) { self::markTestSkipped('Symlink support is required for this test.'); } $moduleA = $this->createModuleWithWebAssets('module-a', ['js/a.js' => 'console.log("a");']); $moduleB = $this->createModuleWithoutWebAssets('module-b'); $runtimeModulesDir = $this->tmpDir . '/web/modules'; mkdir($runtimeModulesDir, 0777, true); file_put_contents($runtimeModulesDir . '/stale.txt', 'old'); $publisher = new ModuleRuntimeAssetPublisher(); $result = $publisher->publish($runtimeModulesDir, [ 'module-a' => $moduleA, 'module-b' => $moduleB, ], 'symlink'); self::assertTrue(is_link($runtimeModulesDir . '/module-a')); self::assertSame( realpath($this->tmpDir . '/modules/module-a/web'), realpath($runtimeModulesDir . '/module-a') ); self::assertFileDoesNotExist($runtimeModulesDir . '/module-b'); self::assertFileDoesNotExist($runtimeModulesDir . '/stale.txt'); self::assertSame(1, $result['created']); self::assertSame(1, $result['removed']); } public function testPublishSymlinkUpdatesWrongExistingLink(): void { if (!function_exists('symlink')) { self::markTestSkipped('Symlink support is required for this test.'); } $moduleA = $this->createModuleWithWebAssets('module-a', ['css/a.css' => '.a{}']); $runtimeModulesDir = $this->tmpDir . '/web/modules'; mkdir($runtimeModulesDir, 0777, true); $wrongTarget = $this->tmpDir . '/wrong-target'; mkdir($wrongTarget, 0777, true); symlink($wrongTarget, $runtimeModulesDir . '/module-a'); $publisher = new ModuleRuntimeAssetPublisher(); $result = $publisher->publish($runtimeModulesDir, ['module-a' => $moduleA], 'symlink'); self::assertTrue(is_link($runtimeModulesDir . '/module-a')); self::assertSame( realpath($this->tmpDir . '/modules/module-a/web'), realpath($runtimeModulesDir . '/module-a') ); self::assertSame(1, $result['updated']); } public function testPublishCopyModeCopiesModuleAssets(): void { $moduleA = $this->createModuleWithWebAssets('module-a', [ 'js/app.js' => 'console.log("copy-mode");', 'css/app.css' => '.copy-mode{}', ]); $runtimeModulesDir = $this->tmpDir . '/web/modules'; $publisher = new ModuleRuntimeAssetPublisher(); $result = $publisher->publish($runtimeModulesDir, ['module-a' => $moduleA], 'copy'); self::assertDirectoryExists($runtimeModulesDir . '/module-a'); self::assertFalse(is_link($runtimeModulesDir . '/module-a')); self::assertFileExists($runtimeModulesDir . '/module-a/js/app.js'); self::assertFileExists($runtimeModulesDir . '/module-a/css/app.css'); self::assertSame(1, $result['created']); } private function createModuleWithWebAssets(string $id, array $files): ModuleManifest { $moduleDir = $this->tmpDir . '/modules/' . $id; mkdir($moduleDir . '/web', 0777, true); foreach ($files as $relativePath => $content) { $target = $moduleDir . '/web/' . ltrim($relativePath, '/'); $targetDir = dirname($target); if (!is_dir($targetDir)) { mkdir($targetDir, 0777, true); } file_put_contents($target, $content); } return ModuleManifest::fromArray(['id' => $id], $moduleDir); } private function createModuleWithoutWebAssets(string $id): ModuleManifest { $moduleDir = $this->tmpDir . '/modules/' . $id; mkdir($moduleDir, 0777, true); return ModuleManifest::fromArray(['id' => $id], $moduleDir); } private function removePath(string $path): void { if (!file_exists($path) && !is_link($path)) { return; } if (is_link($path) || is_file($path)) { @unlink($path); return; } if (!is_dir($path)) { return; } $entries = scandir($path); if ($entries !== false) { foreach ($entries as $entry) { if ($entry === '.' || $entry === '..') { continue; } $this->removePath($path . '/' . $entry); } } @rmdir($path); } }