diff options
Diffstat (limited to 'tests/unit/Module')
-rw-r--r-- | tests/unit/Module/AdminAccountEditTest.php | 222 | ||||
-rw-r--r-- | tests/unit/Module/AdminAccountsTest.php | 173 | ||||
-rw-r--r-- | tests/unit/Module/HelpTest.php | 69 | ||||
-rw-r--r-- | tests/unit/Module/ItemTest.php | 56 | ||||
-rw-r--r-- | tests/unit/Module/MagicTest.php | 120 | ||||
-rw-r--r-- | tests/unit/Module/OwaTest.php | 64 | ||||
-rw-r--r-- | tests/unit/Module/TestCase.php | 64 |
7 files changed, 724 insertions, 44 deletions
diff --git a/tests/unit/Module/AdminAccountEditTest.php b/tests/unit/Module/AdminAccountEditTest.php new file mode 100644 index 000000000..818f30f26 --- /dev/null +++ b/tests/unit/Module/AdminAccountEditTest.php @@ -0,0 +1,222 @@ +<?php +/* Tests for Account_edit module + * + * SPDX-FileCopyrightText: 2024 Hubzilla Community + * SPDX-FileContributor: Harald Eilertsen + * + * SPDX-License-Identifier: MIT + */ + +namespace Zotlabs\Tests\Unit\Module; + +use DateTimeImmutable; +use PHPUnit\Framework\Attributes\{Before, After}; + +class AdminAccountEditTest extends TestCase { + + private $stub_is_site_admin; + private $stub_info; + private $stub_notice; + private $stub_check_security; + private $stub_get_form_security_token; + + private array $info; + private array $notice; + + #[Before] + public function setup_mocks(): void { + /* + * As we're testing pages that should only be reachable by the + * site admin, it makes no sense to have it return anything else + * than true. + */ + $this->stub_is_site_admin = + $this->getFunctionMock('Zotlabs\Module', 'is_site_admin') + ->expects($this->once()) + ->willReturn(true); + + $this->info = []; + $this->stub_info = + $this->getFunctionMock('Zotlabs\Module\Admin', 'info') + ->expects($this->any()) + ->willReturnCallback(function (string $arg) { + $this->info[] = $arg; + }); + + $this->notice = []; + $this->stub_notice = + $this->getFunctionMock('Zotlabs\Module\Admin', 'notice') + ->expects($this->any()) + ->willReturnCallback(function (string $arg) { + $this->notice[] = $arg; + }); + + } + + #[After] + public function tear_down_mocks(): void { + $this->stub_is_site_admin = null; + $this->stub_info = null; + $this->stub_notice = null; + $this->stub_check_security = null; + $this->stub_get_form_security_token = null; + } + + public function test_rendering_admin_account_edit_page(): void { + $this->stub_get_form_security_token = + $this->getFunctionMock('Zotlabs\Module\Admin', 'get_form_security_token') + ->expects($this->once()) + ->willReturn('the-csrf-token'); + + $account = $this->fixtures['account'][0]; + + $this->get("admin/account_edit/{$account['account_id']}"); + + $this->assertPageContains("<form action=\"admin/account_edit/{$account['account_id']}\" method=\"post\""); + $this->assertPageContains($account['account_email']); + + // Check that we generate a CSRF token for the form + $this->assertPageContains("<input type=\"hidden\" name=\"security\" value=\"the-csrf-token\""); + } + + public function test_rendering_admin_account_edit_page_fails_if_id_is_not_found(): void { + $this->get("admin/account_edit/666"); + + $this->assertEquals('', \App::$page['content']); + } + + public function test_rendering_admin_account_edit_page_fails_if_id_is_not_numeric(): void { + $this->get("admin/account_edit/66invalid"); + + $this->assertEquals('', \App::$page['content']); + } + + public function test_post_empty_form_does_not_modify_account(): void { + $this->stub_goaway(); + $this->stub_check_form_security(true); + + $account = get_account_by_id($this->fixtures['account'][0]['account_id']); + + try { + $this->post( + "admin/account_edit/{$account['account_id']}", + [], + [ + 'aid' => $account['account_id'], + 'pass1' => '', + 'pass2' => '', + 'service_class' => $account['account_service_class'], + 'account_language' => $account['account_language'], + 'security' => 'The security token', + ] + ); + } catch (RedirectException $ex) { + $this->assertEquals(z_root() . '/admin/accounts', $ex->getMessage()); + } + + $reloaded = get_account_by_id($account['account_id']); + + $this->assertEquals($account, $reloaded); + + // Not sure if this is expected behaviour, but this is how it is today. + $this->assertContains('Account settings updated.' . EOL, $this->info); + } + + public function test_post_form_changes_account(): void { + $this->stub_goaway(); + $this->stub_check_form_security(true); + + // clone account from fixture, to ensure it's not replaced with + // the reloaded one below. + $account = get_account_by_id($this->fixtures['account'][0]['account_id']); + + try { + $this->post( + "admin/account_edit/{$account['account_id']}", + [], + [ + 'aid' => $account['account_id'], + 'pass1' => 'hunter2', + 'pass2' => 'hunter2', + 'service_class' => 'Some other class', + 'account_language' => 'nn', + 'security' => 'The security token', + ] + ); + } catch (RedirectException $ex) { + $this->assertEquals(z_root() . '/admin/accounts', $ex->getMessage()); + } + + $reloaded = get_account_by_id($account['account_id']); + + $this->assertNotEquals($account, $reloaded); + $this->assertEquals('Some other class', $reloaded['account_service_class']); + $this->assertEquals('nn', $reloaded['account_language']); + + $now = new DateTimeImmutable('now'); + $this->assertEquals($now->format('Y-m-d H:i:s'), $reloaded['account_password_changed']); + + $this->assertContains('Account settings updated.' . EOL, $this->info); + $this->assertContains("Password changed for account {$account['account_id']}." . EOL, $this->info); + } + + public function test_form_with_missing_or_incalid_csrf_token_is_rejected(): void { + $this->expectException(KillmeException::class); + + // Emulate a failed CSRF check + $this->stub_check_form_security(false); + + $account_id = $this->fixtures['account'][0]['account_id']; + + $this->post( + "admin/account_edit/{$account_id}", + [], + [ + 'aid' => $account_id, + 'pass1' => 'hunter2', + 'pass2' => 'hunter2', + 'service_class' => 'Some other class', + 'account_language' => 'nn', + 'security' => 'Invalid security token', + ] + ); + } + + /* + * Override the stub_goaway method because we need the stub to live in the + * Admin namespace. + */ + protected function stub_goaway(): void { + $this->goaway_stub = $this->getFunctionMock('Zotlabs\Module\Admin', 'goaway') + ->expects($this->once()) + ->willReturnCallback( + function (string $uri) { + throw new RedirectException($uri); + } + ); + } + + /** + * Stub the check_form_security_token_ForbiddenOnErr. + * + * In these tests we're not really interested in _how_ the form security + * tokens work, but that the code under test perform the checks. This stub + * allows us to do that without having to worry if everything is set up so + * that the real function would work or not. + * + * @param bool $valid true if emulating a valid token, false otherwise. + */ + protected function stub_check_form_security(bool $valid): void { + $this->stub_check_security = + $this->getFunctionMock('Zotlabs\Module\Admin', 'check_form_security_token_ForbiddenOnErr') + ->expects($this->once()) + ->with( + $this->identicalTo('admin_account_edit'), + $this->identicalTo('security')) + ->willReturnCallback(function () use ($valid) { + if (! $valid) { + throw new KillmeException(); + } + }); + } +} diff --git a/tests/unit/Module/AdminAccountsTest.php b/tests/unit/Module/AdminAccountsTest.php new file mode 100644 index 000000000..2c76f2779 --- /dev/null +++ b/tests/unit/Module/AdminAccountsTest.php @@ -0,0 +1,173 @@ +<?php +/* + * SPDX-FileCopyrightText: 2024 Hubzilla Community + * SPDX-FileContributor: Harald Eilertsen + * + * SPDX-License-Identifier: MIT + */ + +namespace Zotlabs\Tests\Unit\Module; + +use PHPUnit\Framework\Attributes\Before; + +class AdminAccountsTest extends TestCase { + + protected $stub_check_security; + protected $stub_is_site_admin; + protected $stub_goaway; + protected $stub_notice; + + protected array $notice; + + /** + * Set up the stubs common for the tests. + */ + #[Before] + public function setup_stubs(): void { + $this->stub_check_form_security(); + $this->stub_is_site_admin(); + $this->stub_goaway(); + $this->stub_notice(); + } + + public function test_blocking_accounts_marks_selected_accounts_as_blocked(): void { + $params = [ + 'user' => [ 42 ], + 'blocked' => [ false ], + 'page_accounts_block' => true, + ]; + + try { + $this->post('admin/accounts', [], $params); + } catch (RedirectException $redirect) { + $this->assertEquals(z_root() . '/admin/accounts', $redirect->getMessage()); + } + + $account = get_account_by_id(42); + $this->assertEquals(ACCOUNT_BLOCKED, $account['account_flags'] & ACCOUNT_BLOCKED); + + $this->assertEquals('1 account blocked/unblocked', $this->notice[0]); + } + + public function test_unblocking_accounts_clears_the_blocked_flag(): void { + // Pass two users to the module, one that is not blocked, + // and one that is. + $params = [ + 'user' => [ 42, 44 ], + 'blocked' => [ false, true ], + 'page_accounts_block' => true, + ]; + + try { + $this->post('admin/accounts', [], $params); + } catch (RedirectException $redirect) { + $this->assertEquals(z_root() . '/admin/accounts', $redirect->getMessage()); + } + + // We expect the previously unblocked account to be blocked. + $account = get_account_by_id(42); + $this->assertEquals(ACCOUNT_BLOCKED, $account['account_flags'] & ACCOUNT_BLOCKED); + + // We expect the previously blocked account to be unblocked. + $blocked_account = get_account_by_id(44); + $this->assertEquals(0, $blocked_account['account_flags'] & ACCOUNT_BLOCKED); + + $this->assertEquals('2 account blocked/unblocked', $this->notice[0]); + } + + public function test_deleting_accouns_remove_them_from_db(): void { + $params = [ + 'user' => [ 42, 44 ], + 'page_accounts_delete' => true, + ]; + + try { + $this->post('admin/accounts', [], $params); + } catch (RedirectException $redirect) { + $this->assertEquals(z_root() . '/admin/accounts', $redirect->getMessage()); + } + + $this->assertEquals(null, get_account_by_id(42)); + $this->assertEquals(null, get_account_by_id(44)); + } + + public function test_approving_pending_accounts_clears_pending_flag(): void { + + // Catch calls to the php mail function + // + // This is just to get it out of the way, we don't care about + // how many times it's called, or with what args here. + $this->getFunctionMock('Zotlabs\Lib', 'mail') + ->expects($this->any()) + ->willReturn(true); + + $params = [ + 'pending' => [ + $this->fixtures['register'][0]['reg_hash'], + $this->fixtures['register'][1]['reg_hash'] + ], + 'page_accounts_approve' => true, + ]; + + try { + $this->post('admin/accounts', [], $params); + } catch (RedirectException $redirect) { + $this->assertEquals(z_root() . '/admin/accounts', $redirect->getMessage()); + } + + foreach ([45, 46] as $id) { + $account = get_account_by_id($id); + $this->assertEquals(0, $account['account_flags'] & ACCOUNT_PENDING); + } + } + + /** + * Stub the check_form_security_token_ForbiddenOnErr. + */ + protected function stub_check_form_security(): void { + $this->stub_check_security = + $this->getFunctionMock('Zotlabs\Module\Admin', 'check_form_security_token_redirectOnErr') + ->expects($this->once()) + ->with( + $this->identicalTo('/admin/accounts'), + $this->identicalTo('admin_accounts')) + ->willReturn(true); + } + + /** + * Stub the call to is_site_admin in the Admin main module. + */ + protected function stub_is_site_admin(): void { + $this->stub_is_site_admin = + $this->getFunctionMock('Zotlabs\Module', 'is_site_admin') + ->expects($this->once()) + ->willReturn(true); + } + + /** + * Stub the goaway function. + * + * Will throw an RedirectException with the URL being redirected to + * as the exception message. + * + * @throws RedirectException + */ + protected function stub_goaway(): void { + $this->stub_goaway = + $this->getFunctionMock('Zotlabs\Module\Admin', 'goaway') + ->expects($this->once()) + ->willReturnCallback(function (string $uri) { + throw new RedirectException($uri); + }); + } + + protected function stub_notice(): void { + $this->notice = []; + $this->stub_notice = + $this->getFunctionMock('Zotlabs\Module\Admin', 'notice') + ->expects($this->any()) + ->willReturnCallback(function (string $arg) { + $this->notice[] = $arg; + }); + } +} diff --git a/tests/unit/Module/HelpTest.php b/tests/unit/Module/HelpTest.php index c345d5e52..e1aea1a06 100644 --- a/tests/unit/Module/HelpTest.php +++ b/tests/unit/Module/HelpTest.php @@ -31,30 +31,7 @@ class HelpTest extends \Zotlabs\Tests\Unit\Module\TestCase { * ["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()) - ->willReturnMap($file_content_map); - - + $stubs = $this->prepare_stubs($ext); $this->get("help/about/help_topic"); // Check that markdown content was correctly rendered @@ -99,11 +76,16 @@ class HelpTest extends \Zotlabs\Tests\Unit\Module\TestCase { 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->expectExceptionMessage('about'); $this->get('help'); } + public function test_getting_locale_with_no_topic_should_redirect_to_about_page(): void { + $this->expectRedirectTo('help/about'); + $this->get('help/de'); + } + 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'); @@ -121,6 +103,16 @@ class HelpTest extends \Zotlabs\Tests\Unit\Module\TestCase { $this->get('help/first'); } + public function test_fall_back_to_english_if_localized_topic_dont_exist(): void { + \App::$language = 'nb'; + + $stubs = $this->prepare_stubs('bb'); + $this->get('help/about/help_topic'); + + $this->assertPageContains('Hubzilla Documentation: About'); + $this->assertPageContains('This page is not yet available in norsk bokmål'); + } + public function test_includes(): void { // Stub `file_get_contents` to plant our own content. $fgc_stub = $this->getFunctionMock('Zotlabs\Module', 'file_get_contents'); @@ -176,4 +168,31 @@ class HelpTest extends \Zotlabs\Tests\Unit\Module\TestCase { $this->assertPageContains('<h3>This is the included file.</h3>'); } + + private function prepare_stubs(string $ext): array { + // 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()) + ->willReturnMap($file_content_map); + + return [ 'file_exists' => $fe_stub, 'file_get_contents' => $fgc_stub ]; + } } diff --git a/tests/unit/Module/ItemTest.php b/tests/unit/Module/ItemTest.php new file mode 100644 index 000000000..b461a3685 --- /dev/null +++ b/tests/unit/Module/ItemTest.php @@ -0,0 +1,56 @@ +<?php +/* + * SPDX-FileCopyrightText: 2024 Hubzilla Community + * SPDX-FileContributor: Harald Eilertsen + * + * SPDX-License-Identifier: MIT + */ + +namespace Zotlabs\Tests\Unit\Module; + +use PHPUnit\Framework\Attributes\TestWith; + +class ItemTest extends TestCase { + + #[TestWith(['application/x-zot+json'])] + #[TestWith(['application/x-zot-activity+json'])] + public function test_request_with_no_args_return_404(string $type): void { + $this->expect_status(404, 'Not found'); + + $_SERVER['HTTP_ACCEPT'] = $type; + $this->get('item'); + } + + #[TestWith(['application/x-zot+json'])] + #[TestWith(['application/x-zot-activity+json'])] + public function test_request_with_non_exiting_idem_id(string $type): void { + $this->expect_status(404, 'Not found'); + + $_SERVER['HTTP_ACCEPT'] = $type; + $this->get('item/non-existing-id'); + } + + /** + * Helper function to mock the `http_status_exit` function. + * + * The request will be terminated by throwing an exception, which + * will also terminate the test case. Iow. control will not return + * to the test case after the request has been made. + * + * @param int $status The expected HTTP status code. + * @param string $description The expected HTTP status description + */ + private function expect_status(int $status, string $description): void { + $this->getFunctionMock('Zotlabs\Module', 'http_status_exit') + ->expects($this->once()) + ->with($this->identicalTo($status), $this->identicalTo($description)) + ->willReturnCallback( + function () { + throw new KillmeException(); + } + ); + + $this->expectException(KillmeException::class); + + } +} diff --git a/tests/unit/Module/MagicTest.php b/tests/unit/Module/MagicTest.php new file mode 100644 index 000000000..2c426bf76 --- /dev/null +++ b/tests/unit/Module/MagicTest.php @@ -0,0 +1,120 @@ +<?php +/** + * Tests for the Magic module + * + * SPDX-FileCopyrightText: 2024 Hubzilla Community + * SPDX-FileContributor: Harald Eilertsen + * + * SPDX-License-Identifier: MIT + */ + +namespace Zotlabs\Tests\Unit\Module; + +use PHPUnit\Framework\Attributes\BackupStaticProperties; +use Zotlabs\Module\Magic; +use App; + +class MagicTest extends TestCase { + + public function test_init_with_no_args(): void { + + // We expect the request to end with a status 400, as we do not + // pass any of the required params. + // + // To catch that, we have to mock the call to `http_status_exit` + // made by the code under test. + $this->getFunctionMock('Zotlabs\Module', 'http_status_exit') + ->expects($this->once()) // Expect the function to be called only once! + ->with( // ... with the following two arguments + $this->identicalTo(400), + $this->identicalTo('Bad Request') + ) + ->willReturnCallback(function () { // Run this function instead when called + throw new KillmeException; // Throw an exception to terminate processing + }); + + // Tell the test system we excpect this exception to be thrown + $this->expectException(KillmeException::class); + + $this->get('magic'); + } + + #[BackupStaticProperties(App::class)] + public function test_local_request_without_delegate(): void { + $baseurl = 'https://hubzilla.test'; + $dest_url = $baseurl . '/channel/testuser'; + + App::set_baseurl($baseurl); + + App::set_observer([ + 'xchan_hash' => 'the hash', + ]); + + // We pass a local URL, and have a valid observer, but as the + // delegate param is not passed, nothing will be done except + // redirecting to the passed dest url. + // + // This should probably return a 400 Invalid Request instead. + $this->expectRedirectTo($dest_url); + + $this->get('magic', [ 'bdest' => bin2hex($dest_url) ]); + } + + #[BackupStaticProperties(App::class)] + public function test_delegate_request_switches_channel_when_allowed(): void { + $baseurl = 'https://hubzilla.test'; + $dest_url = $baseurl . '/channel/testuser'; + + // Set the stage: + // Populate the global static App class with necessary values for the + // code under test + App::set_baseurl($baseurl); + App::$timezone = 'UTC'; + + // Simulate a foreign (to this hub) observer, + App::set_observer([ + 'xchan_hash' => 'foreign hash', + ]); + + // Create the channel the foreign observer wants to access + $result = create_identity([ + 'account_id' => $this->fixtures['account'][0]['account_id'], + 'nickname' => 'testuser', + 'name' => 'Trish Testuser', + ]); + + // Shortcut the permission checks, by saying this observer is allowed + // the delegate privilege over the target channel + insert_hook('perm_is_allowed', function (array &$perm) { + $perm['result'] = true; + }); + + // Add some dummy session data, so we can check that it's being + // pushed to the delegate session. + $original_session = [ + 'data' => 'Just some test session data', + ]; + + $_SESSION = $original_session; + + // Handle redirects manually, since we want to be able to check some + // assertions after the redirect is thrown. + $this->stub_goaway(); + + try { + // Send a request to get delegate privileges for the `testuser` channel + // on the local hub. + $this->get('magic', [ + 'bdest' => bin2hex($dest_url), + 'delegate' => 'testuser@hubzilla.test'] + ); + } catch (RedirectException $e) { + $this->assertEquals($dest_url, $e->getMessage()); + $this->assertEquals($result['channel']['channel_id'], App::$channel['channel_id']); + $this->assertEquals($original_session, $_SESSION['delegate_push']); + $this->assertEquals($result['channel']['channel_id'], $_SESSION['delegate_channel']); + $this->assertEquals('foreign hash', $_SESSION['delegate']); + $this->assertEquals($this->fixtures['account'][0]['account_id'], $_SESSION['account_id']); + } + } +} diff --git a/tests/unit/Module/OwaTest.php b/tests/unit/Module/OwaTest.php new file mode 100644 index 000000000..dbb25c0b5 --- /dev/null +++ b/tests/unit/Module/OwaTest.php @@ -0,0 +1,64 @@ +<?php +/* + * SPDX-FileCopyrightText: 2025 Hubzilla Community + * SPDX-FileContributor: Harald Eilertsen + * + * SPDX-License-Identifier: MIT + */ + +namespace Zotlabs\Tests\Unit\Module; + +class OwaTest extends TestCase +{ + public function testShouldReturnErrorIfNoAuthorizationHeader(): void + { + // Expect the call to return error + $this->expectJsonResponse([ + 'success' => false, + 'message' => 'Missing or invalid authorization header.' + ]); + + $this->get('owa'); + } + + public function testShouldReturnErrorIfWrongAuthorizationHeader(): void + { + // Expect the call to return error + $this->expectJsonResponse([ + 'success' => false, + 'message' => 'Missing or invalid authorization header.' + ]); + + $_SERVER['HTTP_AUTHORIZATION'] = 'Bearer kjkjhkjhkjh'; + $this->get('owa'); + } + + public function testShouldReturnErrorIfInvalidAuthorizationHeader(): void + { + // Expect the call to return error + $this->expectJsonResponse(['success' => false]); + + $_SERVER['HTTP_AUTHORIZATION'] = 'Signature kjkjhkjhkjh'; + $this->get('owa'); + } + + /** + * Expect the request to be terminated and return a json response. + */ + private function expectJsonResponse(array $data): void + { + $this->getFunctionMock('Zotlabs\Module', 'json_return_and_die') + ->expects($this->once()) + ->with( + $this->identicalTo($data), + $this->identicalTo('application/x-zot+json') + ) + ->willReturnCallback( + function() { + throw new KillmeException(); + } + ); + + $this->expectException(KillmeException::class); + } +} diff --git a/tests/unit/Module/TestCase.php b/tests/unit/Module/TestCase.php index e92bc7083..dd88a5a3b 100644 --- a/tests/unit/Module/TestCase.php +++ b/tests/unit/Module/TestCase.php @@ -10,6 +10,7 @@ namespace Zotlabs\Tests\Unit\Module; +use PHPUnit\Framework\Attributes\After; use Zotlabs\Tests\Unit\UnitTestCase; use App; @@ -25,26 +26,27 @@ class TestCase extends 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. - * @param array $query Assciative array of query args, with the parameters - * as keys. - */ - protected function get(string $uri, array $query = []): void { - $_GET['q'] = $uri; + protected $killme_stub; + protected $goaway_stub; + + #[After] + public function cleanup_stubs(): void { + $this->killme_stub = null; + $this->goaway_stub = null; + } - if (!empty($query)) { - $_GET = array_merge($_GET, $query); - } + protected function do_request(string $method, string $uri, array $query = [], array $params = []): void { + $_GET['q'] = $uri; + $_GET = array_merge($_GET, $query); + $_POST = $params; - $_SERVER['REQUEST_METHOD'] = 'GET'; + $_SERVER['REQUEST_METHOD'] = $method; $_SERVER['SERVER_PROTOCOL'] = 'HTTP/1.1'; $_SERVER['QUERY_STRING'] = "q={$uri}"; + $_SERVER['REQUEST_URI'] = $uri; + // phpcs:disable Generic.PHP.DisallowRequestSuperglobal.Found - $_REQUEST = $_GET; + $_REQUEST = array_merge($_GET, $_POST); // phpcs::enable \App::init(); @@ -55,6 +57,32 @@ class TestCase extends UnitTestCase { } /** + * 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. + * @param array $query Assciative array of query args, with the parameters + * as keys. + */ + protected function get(string $uri, array $query = []): void { + $this->do_request('GET', $uri, $query); + } + + /** + * Emulate a POST request. + * + * @param string $uri The URI to request. Typically this will be the module + * name, followed by any req args separated by slashes. + * @param array $query Associative array of query args, with the parameters + * as keys. + * @param array $params Associative array of POST params, with the param names + * as keys. + */ + protected function post(string $uri, array $query = [], array $params = []): void { + $this->do_request('POST', $uri, $query, $params); + } + + /** * Helper to simplify asserting contents in the rendered page. * * @param string $needle The expected string to find. @@ -100,8 +128,7 @@ class TestCase extends UnitTestCase { * @throws KillmeException */ protected function stub_killme(): void { - $killme_stub = $this->getFunctionMock('Zotlabs\Module', 'killme'); - $killme_stub + $this->killme_stub = $this->getFunctionMock('Zotlabs\Module', 'killme') ->expects($this->once()) ->willReturnCallback( function () { @@ -147,8 +174,7 @@ class TestCase extends UnitTestCase { * @throws RedirectException */ protected function stub_goaway(): void { - $goaway_stub = $this->getFunctionMock('Zotlabs\Module', 'goaway'); - $goaway_stub + $this->goaway_stub = $this->getFunctionMock('Zotlabs\Module', 'goaway') ->expects($this->once()) ->willReturnCallback( function (string $uri) { |