tempRoot = sys_get_temp_dir().DIRECTORY_SEPARATOR.'g7-redeclare-guard-'.bin2hex(random_bytes(6)); } protected function tearDown(): void { if (is_dir($this->tempRoot)) { File::deleteDirectory($this->tempRoot); } parent::tearDown(); } /** * 활성/번들 두 사본을 갖는 확장 픽스처를 만든다. * * 두 사본은 같은 FQN 을 쓰되 `origin()` 반환값이 달라, 어느 쪽을 읽었는지 구분된다. * * @param string $kind `modules` 또는 `plugins` * @param string $identifier 확장 디렉토리명 (vendor-name) * @param string $namespace 네임스페이스 조각 (Vendor\Name) * @return string 활성 사본 파일 경로 */ private function writeFixture(string $kind, string $identifier, string $namespace): string { $isModule = $kind === 'modules'; $rootNs = $isModule ? 'Modules' : 'Plugins'; $base = $isModule ? 'App\Extension\AbstractModule' : 'App\Extension\AbstractPlugin'; $file = $isModule ? 'module.php' : 'plugin.php'; $class = $isModule ? 'Module' : 'Plugin'; $activePath = $this->tempRoot."/{$kind}/{$identifier}/{$file}"; $bundledPath = $this->tempRoot."/{$kind}/_bundled/{$identifier}/{$file}"; foreach (['active' => $activePath, 'bundled' => $bundledPath] as $origin => $path) { File::ensureDirectoryExists(dirname($path)); File::put($path, <<getProperty($property); $prop->setAccessible(true); $prop->setValue($manager, $path); } public function test_module_bundled_instance_does_not_redeclare_active_copy(): void { $activePath = $this->writeFixture('modules', 'g7guard-module_fixture', 'G7guard\\ModuleFixture'); // 부팅이 활성 사본을 로드한 상태를 재현한다. require_once $activePath; $this->assertTrue(class_exists(Module::class, false)); $manager = app(ModuleManager::class); $this->repointRoot($manager, 'modulesPath', $this->tempRoot.'/modules'); $method = (new ReflectionClass($manager))->getMethod('tryLoadModuleInstance'); $method->setAccessible(true); // 가드가 없으면 여기서 프로세스가 fatal 로 종료된다 (Error 라 catch 로 못 막는다). $module = $method->invoke($manager, 'g7guard-module_fixture', false); $this->assertInstanceOf(ModuleInterface::class, $module, '번들 인스턴스를 얻지 못했습니다.'); $this->assertSame( 'bundled', $module->origin(), '활성 사본을 그대로 돌려줬습니다 — 번들 쪽 메타데이터를 읽어야 합니다.', ); } public function test_plugin_bundled_instance_does_not_redeclare_active_copy(): void { $activePath = $this->writeFixture('plugins', 'g7guard-plugin_fixture', 'G7guard\\PluginFixture'); require_once $activePath; $this->assertTrue(class_exists(Plugin::class, false)); $manager = app(PluginManager::class); $this->repointRoot($manager, 'pluginsPath', $this->tempRoot.'/plugins'); $method = (new ReflectionClass($manager))->getMethod('tryLoadPluginInstance'); $method->setAccessible(true); $plugin = $method->invoke($manager, 'g7guard-plugin_fixture', false); $this->assertInstanceOf(PluginInterface::class, $plugin, '번들 인스턴스를 얻지 못했습니다.'); $this->assertSame( 'bundled', $plugin->origin(), '활성 사본을 그대로 돌려줬습니다 — 번들 쪽 메타데이터를 읽어야 합니다.', ); } }