assertInstanceOf(\Throwable::class, $error); $this->assertNotInstanceOf(\Exception::class, $error); } /** * catch (\Throwable)가 \Error를 잡는지 검증 */ public function test_catch_throwable_catches_error(): void { $caught = false; try { throw new \Error('Non-static method called statically'); } catch (\Throwable $e) { $caught = true; } $this->assertTrue($caught, 'catch (\\Throwable) should catch \\Error'); } /** * catch (\Exception)가 \Error를 잡지 못하는지 검증 (이전 버그 재현) */ public function test_catch_exception_does_not_catch_error(): void { $caught = false; $uncaught = false; try { try { throw new \Error('Non-static method called statically'); } catch (\Exception $e) { $caught = true; } } catch (\Error $e) { $uncaught = true; } $this->assertFalse($caught, 'catch (\\Exception) should NOT catch \\Error'); $this->assertTrue($uncaught, '\\Error should propagate past catch (\\Exception)'); } /** * ModuleManager::updateModule의 외부 catch가 \Throwable을 사용하는지 검증 */ public function test_module_manager_update_uses_throwable_catch(): void { $reflection = new \ReflectionMethod( \App\Extension\ModuleManager::class, 'updateModule' ); $source = file_get_contents($reflection->getFileName()); $startLine = $reflection->getStartLine(); $endLine = $reflection->getEndLine(); $methodLines = array_slice( explode("\n", $source), $startLine - 1, $endLine - $startLine + 1 ); $methodSource = implode("\n", $methodLines); // 외부 catch 블록이 \Throwable을 사용하는지 확인 $this->assertStringContainsString( 'catch (\Throwable $e)', $methodSource, 'ModuleManager::updateModule의 외부 catch 블록은 \\Throwable을 사용해야 합니다' ); } /** * PluginManager::updatePlugin의 외부 catch가 \Throwable을 사용하는지 검증 */ public function test_plugin_manager_update_uses_throwable_catch(): void { $reflection = new \ReflectionMethod( \App\Extension\PluginManager::class, 'updatePlugin' ); $source = file_get_contents($reflection->getFileName()); $startLine = $reflection->getStartLine(); $endLine = $reflection->getEndLine(); $methodLines = array_slice( explode("\n", $source), $startLine - 1, $endLine - $startLine + 1 ); $methodSource = implode("\n", $methodLines); $this->assertStringContainsString( 'catch (\Throwable $e)', $methodSource, 'PluginManager::updatePlugin의 외부 catch 블록은 \\Throwable을 사용해야 합니다' ); } /** * TemplateManager::updateTemplate의 외부 catch가 \Throwable을 사용하는지 검증 */ public function test_template_manager_update_uses_throwable_catch(): void { $reflection = new \ReflectionMethod( \App\Extension\TemplateManager::class, 'updateTemplate' ); $source = file_get_contents($reflection->getFileName()); $startLine = $reflection->getStartLine(); $endLine = $reflection->getEndLine(); $methodLines = array_slice( explode("\n", $source), $startLine - 1, $endLine - $startLine + 1 ); $methodSource = implode("\n", $methodLines); $this->assertStringContainsString( 'catch (\Throwable $e)', $methodSource, 'TemplateManager::updateTemplate의 외부 catch 블록은 \\Throwable을 사용해야 합니다' ); } }