diff options
Diffstat (limited to 'tests/unit/Module')
-rw-r--r-- | tests/unit/Module/HelpTest.php | 188 | ||||
-rw-r--r-- | tests/unit/Module/SetupTest.php | 74 | ||||
-rw-r--r-- | tests/unit/Module/TestCase.php | 74 |
3 files changed, 336 insertions, 0 deletions
diff --git a/tests/unit/Module/HelpTest.php b/tests/unit/Module/HelpTest.php new file mode 100644 index 000000000..0f1610db5 --- /dev/null +++ b/tests/unit/Module/HelpTest.php @@ -0,0 +1,188 @@ +<?php +/** + * SPDX-FileCopyrightText: 2024 Harald Eilertsen + * SPDX-FileCopyrightText: 2024 Hubzilla Community + * + * SPDX-License-Identifier: MIT + */ + +class HelpTest extends \Zotlabs\Tests\Unit\Module\TestCase { + + use \phpmock\phpunit\PHPMock; + + /** + * Define the stubs to make sure they work later in the test. + * + * @see https://php-mock.github.io/php-mock-phpunit/api/class-phpmock.phpunit.PHPMock.html#_defineFunctionMock + * + * @beforeClass + */ + public static function define_stubs(): void { + self::defineFunctionMock('Zotlabs\Lib\Traits', 'file_exists'); + self::defineFunctionMock('Zotlabs\Module', 'file_get_contents'); + } + + /** + * Test getting a help page when the underlying file exists. + * + * @testWith + * ["md"] + * ["bb"] + * ["html"] + */ + public function test_get_request_when_help_file_exists(string $ext): void { + // Stub file exists, to only retur true for the file with the current + // extension + $fe_stub = $this->getFunctionMock('Zotlabs\Lib\Traits', 'file_exists'); + $fe_stub + ->expects($this->any()) + ->willReturnCallback( + fn (string $path) => $path === "doc/en/about/help_topic.{$ext}" + ); + + // Use a value map to make the `file_get_contents` stub return the + // correct content for the file types. + $file_content_map = [ + [ 'doc/en/about/help_topic.md', "### Help heading\n\$Projectname help content" ], + [ 'doc/en/about/help_topic.bb', "[h3]Help heading[/h3]\n\n\$Projectname help content" ], + [ 'doc/en/about/help_topic.html', "<h3>Help heading</h3><p>\$Projectname help content</p>" ], + ]; + + // Stub `file_get_contents` to plant our own content. + $fgc_stub = $this->getFunctionMock('Zotlabs\Module', 'file_get_contents'); + $fgc_stub + ->expects($this->once()) + ->willReturn($this->returnValueMap($file_content_map)); + + + $this->get("help/about/help_topic"); + + // Check that markdown content was correctly rendered + $this->assertPageContains('<h3>Help heading</h3>'); + + // Check that `$Projectname` has been translated properly + $this->assertPageContains('Hubzilla help content'); + + // Check that heading has been set + $this->assertPageContains('Hubzilla Documentation: About'); + + // Check that page title has been set + $this->assertTrue(isset(\App::$page['title']), 'Page title not set'); + $this->assertStringContainsString('Help', \App::$page['title']); + $this->assertStringContainsStringIgnoringCase('Help Topic', \App::$page['title']); + + // Check that nav selection has been set + $this->assertTrue(isset(\App::$nav_sel['raw_name']), 'Nav selection raw name not set'); + $this->assertEquals('Help', \App::$nav_sel['raw_name']); + + $this->assertTrue(isset(\App::$nav_sel['name']), 'Navselection name not set'); + $this->assertEquals('Help', \App::$nav_sel['name']); + } + + public function test_get_request_should_return_404_when_help_file_does_not_exist(): void { + // Stub file exists, to only retur true for the file with the current + // extension + $fe_stub = $this->getFunctionMock('Zotlabs\Lib\Traits', 'file_exists'); + $fe_stub + ->expects($this->any()) + ->willReturn(false); + + // Make sure `file_get_contents` is never called by the code. + $fgc_stub = $this->getFunctionMock('Zotlabs\Module', 'file_get_contents'); + $fgc_stub->expects($this->never()); + + $this->get("help/this_topic_does_not_exist"); + + $this->assertPageContains('not found'); + } + + public function test_get_request_without_args_redirects_to_about_page(): void { + $this->stub_goaway(); + $this->expectException(\Zotlabs\Tests\Unit\Module\RedirectException::class); + $this->expectExceptionMessage('about/about'); + + $this->get('help'); + } + + public function test_find_help_file_returns_first_match(): void { + // Stub file exists, to always return true + $fe_stub = $this->getFunctionMock('Zotlabs\Lib\Traits', 'file_exists'); + $fe_stub + ->expects($this->once()) + ->willReturn(true); + + // Stub `file_get_contents` to plant our own content. + $fgc_stub = $this->getFunctionMock('Zotlabs\Module', 'file_get_contents'); + $fgc_stub + ->expects($this->once()) + ->with('doc/en/first.md') + ->willReturn('found'); + + $this->get('help/first'); + } + + public function test_includes(): void { + // Stub `file_get_contents` to plant our own content. + $fgc_stub = $this->getFunctionMock('Zotlabs\Module', 'file_get_contents'); + $fgc_stub + ->expects($this->any()) + ->willReturnCallback( + function (string $path): string { + if ($path === 'doc/en/sub.md') { + return "### This is the included file."; + } else { + return "### Main topic\n\n#include doc/en/sub.md;"; + } + } + ); + + // Stub file exists, to always return true + $fe_stub = $this->getFunctionMock('Zotlabs\Lib\Traits', 'file_exists'); + $fe_stub + ->expects($this->any()) + ->willReturn(true); + + $this->get('help/main'); + + $this->assertPageContains('<h3>This is the included file.</h3>'); + } + + public function test_include_file_of_different_type_than_main_file(): void { + // Stub `file_get_contents` to plant our own content. + $fgc_stub = $this->getFunctionMock('Zotlabs\Module', 'file_get_contents'); + $fgc_stub + ->expects($this->any()) + ->willReturnCallback( + function (string $path): string { + if ($path === 'doc/en/sub.md') { + return "### This is the included file."; + } else { + return "[h3]Main topic[/h3]\n\n#include doc/en/sub.md;"; + } + } + ); + + // Stub file exists, only return true for main.bb and sub.md + $fe_stub = $this->getFunctionMock('Zotlabs\Lib\Traits', 'file_exists'); + $fe_stub + ->expects($this->any()) + ->willReturnCallback( + fn (string $path) => ( + $path === 'doc/en/main.bb' || $path === 'doc/en/sub.md' + ) + ); + + $this->get('help/main'); + + $this->assertPageContains('<h3>This is the included file.</h3>'); + } + + /** + * Helper to simplify asserting contents in the rendered page. + * + * @param string $needle The expected string to find. + */ + private function assertPageContains(string $needle): void { + $this->assertStringContainsString($needle, \App::$page['content']); + } +} diff --git a/tests/unit/Module/SetupTest.php b/tests/unit/Module/SetupTest.php new file mode 100644 index 000000000..96a5ef932 --- /dev/null +++ b/tests/unit/Module/SetupTest.php @@ -0,0 +1,74 @@ +<?php +/** + * SPDX-FileCopyrightText: 2024 Harald Eilertsen + * SPDX-FileCopyrightText: 2024 Hubzilla Community + * + * SPDX-License-Identifier: MIT + */ + +/** + * SetupModuleTest + * + * The Setup module should only be available during site installation. This is + * determined by whether there are any accounts present in the database, or + * not. + * + * This is a complex module, so expect the tests to grow as more of it will be + * covered. + */ +class SetupModuleTest extends \Zotlabs\Tests\Unit\Module\TestCase { + + public function test_that_setup_is_available_if_no_accounts_in_db(): void { + $this->with_no_accounts_in_db(); + $this->get('setup'); + + $this->assertEquals('setup', \App::$page['page_title']); + + // Assert that result _don't_ match "Permission denied" + $this->assertThat( + \App::$page['content'], + $this->logicalNot( + $this->matchesRegularExpression('/Permission denied/') + ) + ); + } + + public function test_that_setup_is_not_available_if_accounts_in_db(): void { + // The fixtures loaded by default add a couple of accounts. + $this->get('setup'); + + $this->assertEquals('setup', \App::$page['page_title']); + $this->assertMatchesRegularExpression('/Permission denied/', \App::$page['content']); + } + + public function test_that_setup_testrewrite_returns_ok(): void { + // We need to stub the `killme` function, as it is called directly from + // the code under test. + $this->stub_killme(); + + $output = ''; + + // Turn on output buffering, as code under test echoes + // directly to the output + ob_start(); + try { + $this->get('setup/testrewrite'); + } catch (\Zotlabs\Tests\Unit\Module\KillmeException) { + $output = ob_get_contents(); + } + + $this->assertEquals('ok', $output); + + ob_end_clean(); + } + + /** + * Delete all accounts from the database. + * + * This is currently needed because we automatically import the database + * fixtures on test start, which contains a couple of accounts already. + */ + private function with_no_accounts_in_db(): void { + q('DELETE FROM account;'); + } +} diff --git a/tests/unit/Module/TestCase.php b/tests/unit/Module/TestCase.php new file mode 100644 index 000000000..aa09e0596 --- /dev/null +++ b/tests/unit/Module/TestCase.php @@ -0,0 +1,74 @@ +<?php + +namespace Zotlabs\Tests\Unit\Module; + +class TestCase extends \Zotlabs\Tests\Unit\UnitTestCase { + + // Import PHPMock methods into this class + use \phpmock\phpunit\PHPMock; + + /** + * Emulate a GET request. + * + * @param string $uri The URI to request. Typically this will be the module + * name, followed by any req args separated by slashes. + */ + protected function get(string $uri): void { + $_GET['q'] = $uri; + $_SERVER['REQUEST_METHOD'] = 'GET'; + + \App::init(); + \App::$page['content'] = ''; + + $router = new \Zotlabs\Web\Router(); + $router->Dispatch(); + } + + /** + * Stub out the `killme` function. + * + * Usefule for modules that call this function directly. + * + * Instead of calling exit, the stub will throw a `KillmeException`, + * that can be caught by the test code to regain control after request + * processing is terminated. + */ + protected function stub_killme(): void { + $killme_stub = $this->getFunctionMock('Zotlabs\Module', 'killme'); + $killme_stub + ->expects($this->once()) + ->willReturnCallback( + function () { + throw new KillmeException(); + } + ); + } + + protected function stub_goaway(): void { + $goaway_stub = $this->getFunctionMock('Zotlabs\Module', 'goaway'); + $goaway_stub + ->expects($this->once()) + ->willReturnCallback( + function (string $uri) { + throw new RedirectException($uri); + } + ); + } +} + +/** + * Exception class for killme stub + */ +class KillmeException extends \Exception {} + +/** + * Exception class for goaway stub. + * + * Takes the goaway uri as an arg, and makes it available as + * the public `$uri` member variable. + */ +class RedirectException extends \Exception { + function __construct(string $uri) { + parent::__construct($uri); + } +} |