<?php
/**
* This file contains the base class for module test cases.
*
* SPDX-FileCopyrightText: 2024 Hubzilla Community
* SPDX-FileContributor: Harald Eilertsen
*
* SPDX-License-Identifier: MIT
*/
namespace Zotlabs\Tests\Unit\Module;
use PHPUnit\Framework\Attributes\After;
use Zotlabs\Tests\Unit\UnitTestCase;
use App;
/**
* Base class for writing module tests.
*
* This test class adds a number of helper methods to the base from
* the UnitTestCase class, useful when testing Modules (Controllers)
* that handle incoming requests.
*/
class TestCase extends UnitTestCase {
// Import PHPMock methods into this class
use \phpmock\phpunit\PHPMock;
protected $killme_stub;
protected $goaway_stub;
#[After]
public function cleanup_stubs(): void {
$this->killme_stub = null;
$this->goaway_stub = null;
}
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'] = $method;
$_SERVER['SERVER_PROTOCOL'] = 'HTTP/1.1';
$_SERVER['QUERY_STRING'] = "q={$uri}";
// phpcs:disable Generic.PHP.DisallowRequestSuperglobal.Found
$_REQUEST = array_merge($_GET, $_POST);
// phpcs::enable
\App::init();
\App::$page['content'] = '';
$router = new \Zotlabs\Web\Router();
$router->Dispatch();
}
/**
* 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.
*/
protected function assertPageContains(string $needle): void {
$this->assertStringContainsString($needle, App::$page['content']);
}
/**
* Stub out the `killme` function.
*
* Useful for testing 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.
*
* **Example:**
*
* public function test_something(): void {
* $this->stub_killme();
*
* try {
* killme();
* } catch (KillmeException $e) {
* $this->assertSomething(...);
* }
* }
*
* It's also possible to use the builting PHPUnit expecations to verify
* that the function was called.
*
* public function test_something(): void {
* $this->stub_killme();
* $this->expectException(KillmeException::class);
*
* killme();
* }
*
* This is useful if you only want to check that processing was terminated
* with the `killme()` function.
*
* @throws KillmeException
*/
protected function stub_killme(): void {
$this->killme_stub = $this->getFunctionMock('Zotlabs\Module', 'killme')
->expects($this->once())
->willReturnCallback(
function () {
throw new KillmeException();
}
);
}
/**
* Stub out the `goaway` function.
*
* Useful for testing modules that calls this function directly.
*
* Instead of calling `killme()`, the stub will throw a RedirectException
* with the target URL as the exception message. This allows the test code
* to regain control after request processing is terminated.
*
* **Example:**
*
* public function test_redirect(): void {
* $this->stub_goaway();
*
* try {
* goaway('https://example.com/some_uri');
* } catch (RedirectException $e) {
* $this->assertEquals('https://example.com/some_uri', $e->getMessage());
* $this->assertSomethingElse(...);
* }
* }
* It's also possible to use the builting PHPUnit expecations to verify
* that the function was called.
*
* public function test_something(): void {
* $this->stub_goaway();
* $this->expectException(RedirectException::class);
* $this->expectExceptionMessage('https://example.com/some_uri');
*
* goaway('https://example.com/some_uri');
* }
*
* This is useful if you only want to check that the request was redirected.
*
* @throws RedirectException
*/
protected function stub_goaway(): void {
$this->goaway_stub = $this->getFunctionMock('Zotlabs\Module', 'goaway')
->expects($this->once())
->willReturnCallback(
function (string $uri) {
throw new RedirectException($uri);
}
);
}
/**
* Shorthand function to expect a redirect to a given URL.
*
* **Example:**
*
* public function test_redirect(): void {
* $this->expectRedirectTo('https://example.com/some_uri');
* goaway('https://example.com/some_uri');
* }
*/
protected function expectRedirectTo(string $destination): void {
$this->stub_goaway();
$this->expectException(RedirectException::class);
$this->expectExceptionMessage($destination);
}
}
/**
* 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 to the catch
* site via the `getMessage()` method.
*/
class RedirectException extends \Exception {
function __construct(string $uri) {
parent::__construct($uri);
}
}