From 7c173411785f959d250d3dfbd7d4cfcb0e20f0e0 Mon Sep 17 00:00:00 2001 From: Simon Rettberg Date: Wed, 26 Nov 2025 10:46:51 +0100 Subject: Add tests using PHPUnit Tests generated by Junie AI. Might not have the best possible quality but at least we got something, and if it turns out to be complete rubbish, we can just throw it out again without any issues, as this is independent of the actual code base. --- tests/Inc/ModuleTest.php | 188 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 188 insertions(+) create mode 100644 tests/Inc/ModuleTest.php (limited to 'tests/Inc/ModuleTest.php') diff --git a/tests/Inc/ModuleTest.php b/tests/Inc/ModuleTest.php new file mode 100644 index 00000000..05791022 --- /dev/null +++ b/tests/Inc/ModuleTest.php @@ -0,0 +1,188 @@ +baseDir = getcwd() . '/modules'; + // Ensure a clean environment: the Module registry initializes lazily in Module::init(), + // and we haven't referenced Module yet in this process. + $this->makeModule('TestDep', [ + 'dependencies' => [], + 'client-plugin' => false, + 'category' => 'utilities', + ]); + $this->writeFile('modules/TestDep/page.inc.php', + "makeModule('TestMain', [ + 'dependencies' => ['testdep'], // must reference the lowercase id key + 'client-plugin' => true, + 'category' => 'main', + 'css' => [], + 'scripts' => [], + 'collapse' => true, + ]); + $this->writeFile('modules/TestMain/page.inc.php', + "writeFile('modules/TestMain/inc/abchelper.inc.php', + "writeFile('modules/TestMain/style.css', '/* css */'); + $this->writeFile('modules/TestMain/clientscript.js', '// js'); + + // A module with a missing dependency to test failure path + $this->makeModule('BadMain', [ 'dependencies' => ['no_such_dep'] ]); + $this->writeFile('modules/BadMain/page.inc.php', + "created) as $path) { + if (is_file($path)) { + @unlink($path); + } elseif (is_dir($path)) { + @rmdir($path); + } + } + $this->created = []; + } + + private function makeModule(string $name, array $config): void + { + $dir = $this->baseDir . '/' . $name; + $inc = $dir . '/inc'; + if (!is_dir($dir)) { + mkdir($dir, 0777, true); + $this->created[] = $dir; + } + if (!is_dir($inc)) { + mkdir($inc, 0777, true); + $this->created[] = $inc; + } + $this->writeFile($dir . '/config.json', json_encode($config)); + } + + private function writeFile(string $path, string $content): void + { + file_put_contents($path, $content); + $this->created[] = $path; + } + + public function testInitAndGetAndActivationAndNewPage(): void + { + // Load module list from filesystem + require_once 'inc/module.inc.php'; + Module::init(); + + // Resolve/activate and check availability + $this->assertTrue(Module::isAvailable('testmain', true)); + $main = Module::get('testmain'); + $this->assertNotFalse($main); + $this->assertSame('TestMain', $main->getIdentifier()); + // getDisplayName falls back to !!name!! without dictionary entry + $this->assertSame('!!TestMain!!', $main->getDisplayName()); + // Category helpers + $this->assertSame('main', $main->getCategory()); + $this->assertSame('Cat:main', $main->getCategoryName()); + $this->assertTrue($main->doCollapse()); + $this->assertSame('modules/TestMain', $main->getDir()); + + // Test autoloader from module/inc + $this->assertTrue(class_exists('AbcHelper'), 'Autoloader should expose AbcHelper from module/inc'); + $this->assertSame(123, AbcHelper::id()); + + // Page loader + $page = $main->newPage(); + $this->assertInstanceOf(Page::class, $page); + $this->assertInstanceOf(Page_TestMain::class, $page); + + // Assets only reported when directly activated and client-plugin true + $css = $main->getCss(); + $js = $main->getScripts(); + $this->assertContains('style.css', $css); + $this->assertContains('clientscript.js', $js); + } + + public function testDependencyResolutionAndQueries(): void + { + require_once 'inc/module.inc.php'; + Module::init(); + + // BadMain is not available due to missing dep + // The Module loader triggers a user-level warning in this case; expect it so the test doesn't fail + set_error_handler(function ($errno, $errstr, $errfile, $errline) { + if ($errstr === 'Disabling module BadMain: Dependency no_such_dep failed.') + return; + throw new ErrorException($errstr, 0, $errno, $errfile, $errline); + }); + $this->assertFalse(Module::isAvailable('badmain')); + $this->assertFalse(Module::get('badmain')); + + // TestDep alone is available + $this->assertTrue(Module::isAvailable('testdep')); + $dep = Module::get('testdep'); + $this->assertNotFalse($dep); + + // Enabled list contains both TestDep and TestMain when we activate main + Module::isAvailable('testmain', true); + $enabled = Module::getEnabled(true); + $ids = array_map(function($m){ return $m->getIdentifier(); }, $enabled); + $this->assertContains('TestDep', $ids); + $this->assertContains('TestMain', $ids); + + // All includes even those with missing deps (after resolution attempt) + $all = Module::getAll(); + $allIds = array_map(function($m){ return $m->getIdentifier(); }, $all); + $this->assertContains('TestDep', $allIds); + $this->assertContains('TestMain', $allIds); + $this->assertContains('BadMain', $allIds); // present but unusable + + // Activated contains modules with an activated depth marker + $activated = Module::getActivated(); + $this->assertNotEmpty($activated); + $actVals = array_values($activated); + $this->assertContainsOnlyInstancesOf(Module::class, $actVals); + } + + public function testTransitiveDependenciesListed(): void + { + // Create Dep2 and make TestDep depend on it; then init fresh process registry + $this->makeModule('Dep2', [ 'dependencies' => [] ]); + $this->writeFile('modules/Dep2/page.inc.php', + "writeFile('modules/TestDep/config.json', json_encode(['dependencies' => ['dep2']])); + + require_once 'inc/module.inc.php'; + Module::init(); + // Activate main (which depends on testdep which depends on dep2) + Module::isAvailable('testmain', true); + $main = Module::get('testmain'); + $list = $main->getDependencies(); + // Should include transitive dependency + $this->assertContains('testdep', $list); + $this->assertContains('dep2', $list); + } +} -- cgit v1.2.3-55-g7522