From 093231905d1105b5127e1eb6e50807a94aeb0b8d Mon Sep 17 00:00:00 2001 From: Aleksander Machniak Date: Sun, 6 Apr 2025 14:54:20 +0200 Subject: [PATCH] Tests: Cleanup/refactor around HTTP client mocking --- tests/Actions/Utils/ModcssTest.php | 9 +++--- tests/HttpClientMock.php | 51 ++++++++++++++++++++++++++++++ tests/Rcmail/OauthTest.php | 41 +++++++++--------------- tests/bootstrap.php | 44 ++++++++------------------ 4 files changed, 84 insertions(+), 61 deletions(-) create mode 100644 tests/HttpClientMock.php diff --git a/tests/Actions/Utils/ModcssTest.php b/tests/Actions/Utils/ModcssTest.php index e5e41d0d9..2f93d6036 100644 --- a/tests/Actions/Utils/ModcssTest.php +++ b/tests/Actions/Utils/ModcssTest.php @@ -3,10 +3,9 @@ namespace Roundcube\Tests\Actions\Utils; use Roundcube\Tests\ActionTestCase; +use Roundcube\Tests\HttpClientMock; use Roundcube\Tests\OutputHtmlMock; -use function Roundcube\Tests\setHttpClientMock; - /** * Test class to test rcmail_action_utils_modcss */ @@ -52,9 +51,9 @@ class ModcssTest extends ActionTestCase // Valid url pointing to non-existing resource $_SESSION['modcssurls'][$key] = $url; - setHttpClientMock([ - ['code' => 404], - ['code' => 200, 'headers' => ['Content-Type' => 'text/css'], 'response' => 'div.pre { display: none; }'], + HttpClientMock::setResponses([ + [404], + [200, ['Content-Type' => 'text/css'], 'div.pre { display: none; }'], ]); $this->runAndAssert($action, OutputHtmlMock::E_EXIT); diff --git a/tests/HttpClientMock.php b/tests/HttpClientMock.php new file mode 100644 index 000000000..92bcc4ec5 --- /dev/null +++ b/tests/HttpClientMock.php @@ -0,0 +1,51 @@ + | + +-----------------------------------------------------------------------+ +*/ + +/** + * A helper to mock Roundcube HTTP client + */ +class HttpClientMock +{ + public static function setResponses(array $responses) + { + foreach ($responses as $idx => $response) { + if ($response instanceof Response) { + $responses[$idx] = $response; + } elseif (is_array($response)) { + $responses[$idx] = new Response( + $response[0] ?? 200, + $response[1] ?? [], + $response[2] ?? '' + ); + } + } + + $mock = new MockHandler($responses); + $handler = HandlerStack::create($mock); + $rcube = \rcube::get_instance(); + + $rcube->config->set('http_client', ['handler' => $handler]); + } +} diff --git a/tests/Rcmail/OauthTest.php b/tests/Rcmail/OauthTest.php index 5f45ee1cb..89d54b768 100644 --- a/tests/Rcmail/OauthTest.php +++ b/tests/Rcmail/OauthTest.php @@ -2,11 +2,9 @@ namespace Roundcube\Tests\Rcmail; -use GuzzleHttp\Handler\MockHandler; -use GuzzleHttp\HandlerStack; -use GuzzleHttp\Psr7\Response; use Roundcube\Tests\ActionTestCase; use Roundcube\Tests\ExitException; +use Roundcube\Tests\HttpClientMock; use Roundcube\Tests\OutputHtmlMock; use Roundcube\Tests\StderrMock; @@ -151,17 +149,15 @@ class OauthTest extends ActionTestCase 'jwks_uri' => 'https://test/jwks', ]; - $mock = new MockHandler([ - new Response(200, ['Content-Type' => 'application/json'], json_encode($config_answer)), + HttpClientMock::setResponses([ + [200, ['Content-Type' => 'application/json'], json_encode($config_answer)], ]); - $handler = HandlerStack::create($mock); // provide only the config $oauth = new \rcmail_oauth([ 'provider' => 'example', 'config_uri' => 'https://test/config', 'client_id' => 'some-client', - 'http_options' => ['handler' => $handler], ]); $oauth->init(); @@ -249,13 +245,11 @@ class OauthTest extends ActionTestCase 'scope' => 'openid profile email', ]; - $mock = new MockHandler([ - new Response(200, ['Content-Type' => 'application/json'], json_encode($payload)), - ]); - $handler = HandlerStack::create($mock); - $oauth = new \rcmail_oauth((array) $this->config + [ - 'http_options' => ['handler' => $handler], + HttpClientMock::setResponses([ + [200, ['Content-Type' => 'application/json'], json_encode($payload)], ]); + + $oauth = new \rcmail_oauth((array) $this->config); $oauth->init(); $_SESSION['oauth_state'] = 'random-state'; // ensure state identiquals @@ -286,13 +280,11 @@ class OauthTest extends ActionTestCase 'scope' => 'openid profile email', ]; - $mock = new MockHandler([ - new Response(200, ['Content-Type' => 'application/json'], json_encode($payload)), - ]); - $handler = HandlerStack::create($mock); - $oauth = new \rcmail_oauth((array) $this->config + [ - 'http_options' => ['handler' => $handler], + HttpClientMock::setResponses([ + [200, ['Content-Type' => 'application/json'], json_encode($payload)], ]); + + $oauth = new \rcmail_oauth((array) $this->config); $oauth->init(); $_SESSION['oauth_state'] = 'random-state'; // ensure state identiquals @@ -326,15 +318,12 @@ class OauthTest extends ActionTestCase ]; // TODO should create a specific Mock to check request and validate it - $mock = new MockHandler([ - new Response(200, ['Content-Type' => 'application/json'], json_encode($payload)), // the request access - new Response(200, ['Content-Type' => 'application/json'], json_encode($this->identity)), // call to userinfo + HttpClientMock::setResponses([ + [200, ['Content-Type' => 'application/json'], json_encode($payload)], // the request access + [200, ['Content-Type' => 'application/json'], json_encode($this->identity)], // call to userinfo ]); - $handler = HandlerStack::create($mock); - $oauth = new \rcmail_oauth((array) $this->config + [ - 'http_options' => ['handler' => $handler], - ]); + $oauth = new \rcmail_oauth((array) $this->config); $oauth->init(); $_SESSION['oauth_state'] = 'random-state'; // ensure state identiquals diff --git a/tests/bootstrap.php b/tests/bootstrap.php index 685353fe8..a21d77972 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -2,9 +2,9 @@ namespace Roundcube\Tests; -use GuzzleHttp\Handler\MockHandler; -use GuzzleHttp\HandlerStack; -use GuzzleHttp\Psr7\Response; +use Dom\HTMLDocument; +use Dom\NodeList; +use Dom\XPath; use Masterminds\HTML5; /* @@ -116,36 +116,20 @@ function setProperty($object, $name, $value, $class = null): void * @param string $html HTML content * @param string $xpath_query XPath query * - * @return \DOMNodeList List of nodes found + * @return \DOMNodeList|NodeList List of nodes found */ function getHTMLNodes($html, $xpath_query) { - $html5 = new HTML5(['disable_html_ns' => true]); - $doc = $html5->loadHTML($html); - - $xpath = new \DOMXPath($doc); - - return $xpath->query($xpath_query); -} - -/** - * Mock Guzzle HTTP Client - */ -function setHttpClientMock(array $responses) -{ - foreach ($responses as $idx => $response) { - if (is_array($response)) { - $responses[$idx] = new Response( - $response['code'] ?? 200, - $response['headers'] ?? [], - $response['response'] ?? '' - ); - } + // Try HTML5 parser available in PHP >= 8.4 + if (class_exists(HTMLDocument::class)) { + $options = constant('Dom\HTML_NO_DEFAULT_NS') | \LIBXML_COMPACT | \LIBXML_NOERROR; + $doc = HTMLDocument::createFromString($html, $options, RCUBE_CHARSET); + $xpath = new XPath($doc); + } else { + $html5 = new HTML5(['disable_html_ns' => true]); + $doc = $html5->loadHTML($html); + $xpath = new \DOMXPath($doc); } - $mock = new MockHandler($responses); - $handler = HandlerStack::create($mock); - $rcube = \rcube::get_instance(); - - $rcube->config->set('http_client', ['handler' => $handler]); + return $xpath->query($xpath_query); }