Extend test capability for HTTP Requests

This commit is contained in:
Philipp Holzer 2021-08-25 00:13:50 +02:00
parent a3d0404290
commit d4a233a149
No known key found for this signature in database
GPG Key ID: 9A28B7D4FF5667BD
4 changed files with 166 additions and 30 deletions

View File

@ -9,6 +9,7 @@ use Friendica\Network\HTTPClient;
use Friendica\Network\IHTTPClient;
use Friendica\Util\Profiler;
use GuzzleHttp\Client;
use GuzzleHttp\HandlerStack;
use GuzzleHttp\RequestOptions;
use mattwright\URLResolver;
use Psr\Http\Message\RequestInterface;
@ -33,7 +34,14 @@ class HTTPClientFactory extends BaseFactory
$this->baseUrl = $baseUrl;
}
public function createClient(): IHTTPClient
/**
* Creates a IHTTPClient for communications with HTTP endpoints
*
* @param HandlerStack|null $handlerStack (optional) A handler replacement (just usefull at test environments)
*
* @return IHTTPClient
*/
public function createClient(HandlerStack $handlerStack = null): IHTTPClient
{
$proxy = $this->config->get('system', 'proxy');
@ -84,6 +92,7 @@ class HTTPClientFactory extends BaseFactory
RequestOptions::HEADERS => [
'User-Agent' => $userAgent,
],
'handler' => $handlerStack ?? HandlerStack::create(),
]);
$resolver = new URLResolver();

View File

@ -21,47 +21,45 @@
namespace Friendica\Test;
use Dice\Dice;
use Friendica\DI;
use Friendica\Network\HTTPClient;
use Friendica\Factory\HTTPClientFactory;
use Friendica\Network\IHTTPClient;
use GuzzleHttp\Client;
use GuzzleHttp\HandlerStack;
use mattwright\URLResolver;
/**
* This class mocks some DICE dependencies because they're not direct usable for test environments
* (Like fetching data from external endpoints)
* This class injects a mockable handler into the IHTTPClient dependency per Dice
*/
trait DiceTestTrait
trait DiceHttpMockHandlerTrait
{
/**
* Handler for mocking requests anywhere for testing purpose
*
* @var HandlerStack
*/
protected static $httpRequestHandler;
protected $httpRequestHandler;
protected static function setUpDice(): void
protected function setupHttpMockHandler(): void
{
if (!empty(self::$httpRequestHandler) && self::$httpRequestHandler instanceof HandlerStack) {
if (!empty($this->httpRequestHandler) && $this->httpRequestHandler instanceof HandlerStack) {
return;
}
self::$httpRequestHandler = HandlerStack::create();
$this->httpRequestHandler = HandlerStack::create();
$client = new Client(['handler' => self::$httpRequestHandler]);
$dice = DI::getDice();
// addRule() clones the current instance and returns a new one, so no concurrency problems :-)
$newDice = $dice->addRule(IHTTPClient::class, [
'instanceOf' => HTTPClientFactory::class,
'call' => [
['createClient', [$this->httpRequestHandler], Dice::CHAIN_CALL],
],
]);
$resolver = \Mockery::mock(URLResolver::class);
$httpClient = new HTTPClient(DI::logger(), DI::profiler(), $client, $resolver);
$dice = DI::getDice();
$newDice = \Mockery::mock($dice)->makePartial();
$newDice->shouldReceive('create')->with(IHTTPClient::class)->andReturn($httpClient);
DI::init($newDice);
}
protected function tearDown() : void
protected function tearDown(): void
{
\Mockery::close();

View File

@ -3,10 +3,21 @@
namespace Friendica\Test\src\Network;
use Friendica\Network\Probe;
use PHPUnit\Framework\TestCase;
use Friendica\Test\DiceHttpMockHandlerTrait;
use Friendica\Test\FixtureTest;
use GuzzleHttp\Middleware;
class ProbeTest extends TestCase
class ProbeTest extends FixtureTest
{
use DiceHttpMockHandlerTrait;
protected function setUp(): void
{
parent::setUp();
$this->setupHttpMockHandler();
}
const TEMPLATENOBASE = '
<!DOCTYPE html>
<html lang="en-us">
@ -105,4 +116,122 @@ class ProbeTest extends TestCase
}
}
}
public function dataUri()
{
return [
'@-first' => [
'uri' => '@Artists4Future_Muenchen@climatejustice.global',
'assertUri' => 'Artists4Future_Muenchen@climatejustice.global',
'assertInfos' => [
'name' => 'Artists4Future München',
'nick' => 'Artists4Future_Muenchen',
'url' => 'https://climatejustice.global/users/Artists4Future_Muenchen',
'alias' => 'https://climatejustice.global/@Artists4Future_Muenchen',
'photo' => 'https://cdn.masto.host/climatejusticeglobal/accounts/avatars/000/021/220/original/05ee9e827a5b47fc.jpg',
'header' => 'https://cdn.masto.host/climatejusticeglobal/accounts/headers/000/021/220/original/9b98b75cf696cd11.jpg',
'account-type' => 0,
'about' => 'Wir sind Künstler oder einfach gerne kreativ tätig und setzen uns unabhängig von politischen Parteien für den Klimaschutz ein. Die Bedingungen zu schaffen, die die [url=https://climatejustice.global/tags/Klimakrise]#Klimakrise[/url] verhindern/eindämmen (gemäß den Forderungen der [url=https://climatejustice.global/tags/Fridays4Future]#Fridays4Future[/url]) ist Aufgabe der Politik, muss aber gesamtgesellschaftlich getragen werden. Mit unseren künstlerischen Aktionen wollen wir einen anderen Zugang anbieten für wissenschaftlich rationale Argumente, speziell zur Erderwärmung und ihre Konsequenzen.',
'hide' => 0,
'batch' => 'https://climatejustice.global/inbox',
'notify' => 'https://climatejustice.global/users/Artists4Future_Muenchen/inbox',
'poll' => 'https://climatejustice.global/users/Artists4Future_Muenchen/outbox',
'subscribe' => 'https://climatejustice.global/authorize_interaction?uri={uri}',
'following' => 'https://climatejustice.global/users/Artists4Future_Muenchen/following',
'followers' => 'https://climatejustice.global/users/Artists4Future_Muenchen/followers',
'inbox' => 'https://climatejustice.global/users/Artists4Future_Muenchen/inbox',
'outbox' => 'https://climatejustice.global/users/Artists4Future_Muenchen/outbox',
'sharedinbox' => 'https://climatejustice.global/inbox',
'priority' => 0,
'network' => 'apub',
'pubkey' => '-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA6pYKPuDKb+rmBB869uPV
uLYFPosGxMUfenWqfWmFKzEqJ87rAft0IQDAL6dCoYE55ov/lEDNROhasTZLirZf
M5b7/1JmwMrAfEiaciuYqDWT3/yDpnekOIdzP5iSClg4zt7e6HRFuClqo4+b6hIE
DTMV4ksItvq/92MIu62pZ2SZr5ADPPZ/914lJ86hIH5BanbE8ZFzDS9vJA7V74rt
Vvkr5c/OiUyuODNYApSl87Ez8cuj8Edt89YWkDCajQn3EkmXGeJY/VRjEDfcyk6r
AvdUa0ArjXud3y3NkakVFZ0d7tmB20Vn9s/CfYHU8FXzbI1kFkov2BX899VVP5Ay
xQIDAQAB
-----END PUBLIC KEY-----',
'manually-approve' => 0,
'baseurl' => 'https://climatejustice.global',
]
]
];
}
/**
* @dataProvider dataUri
*/
public function testCleanUri(string $uri, string $assertUri, array $assertInfos)
{
self::markTestIncomplete('hard work due mocking 19 different http-requests');
/**
* Requests:
*
* GET : https://climatejustice.global/.well-known/webfinger?resource=acct:Artists4Future_Muenchen%40climatejustice.global
* 200
* GET : http://localhost/.well-known/nodeinfo
* 200
* GET : http://localhost/statistics.json
* 404
* GET : http://localhost
* 200
* GET : http://localhost/friendica/json
* 404
* GET : http://localhost/friendika/json
* 404
* GET : http://localhost/poco
* 403
* GET : http://localhost/api/v1/directory?limit=1
* 200
* GET : http://localhost/.well-known/x-social-relay
* 200
* GET : http://localhost/friendica
* 404
* GET : https://climatejustice.global/users/Artists4Future_Muenchen
* 200
* GET : https://climatejustice.global/users/Artists4Future_Muenchen/following
* 200
* GET : https://climatejustice.global/users/Artists4Future_Muenchen/followers
* 200
* GET : https://climatejustice.global/users/Artists4Future_Muenchen/outbox
* 200
* GET : https://climatejustice.global/.well-known/nodeinfo
* 200
* GET : https://climatejustice.global/nodeinfo/2.0
* 200
* GET : https://climatejustice.global/poco
* 404
* GET : https://climatejustice.global/api/v1/directory?limit=1
* 200
* GET : https://climatejustice.global/.well-known/webfinger?resource=acct%3AArtists4Future_Muenchen%40climatejustice.global
* 200
*
*/
$container = [];
$history = Middleware::history($container);
$this->httpRequestHandler->push($history);
$cleaned = Probe::cleanURI($uri);
self::assertEquals($assertUri, $cleaned);
self::assertArraySubset($assertInfos, Probe::uri($cleaned, '', 0));
// Iterate over the requests and responses
foreach ($container as $transaction) {
echo $transaction['request']->getMethod() . " : " . $transaction['request']->getUri() . PHP_EOL;
//> GET, HEAD
if ($transaction['response']) {
echo $transaction['response']->getStatusCode() . PHP_EOL;
//> 200, 200
} elseif ($transaction['error']) {
echo $transaction['error'];
//> exception
}
}
}
}

View File

@ -2,7 +2,7 @@
namespace Friendica\Test\src\Util;
use Friendica\Test\DiceTestTrait;
use Friendica\Test\DiceHttpMockHandlerTrait;
use Friendica\Test\MockedTest;
use Friendica\Util\Images;
use GuzzleHttp\Handler\MockHandler;
@ -10,13 +10,13 @@ use GuzzleHttp\Psr7\Response;
class ImagesTest extends MockedTest
{
use DiceTestTrait;
use DiceHttpMockHandlerTrait;
public static function setUpBeforeClass(): void
protected function setUp(): void
{
parent::setUpBeforeClass();
parent::setUp();
self::setUpDice();
$this->setupHttpMockHandler();
}
public function dataImages()
@ -56,13 +56,13 @@ class ImagesTest extends MockedTest
}
/**
* Test the Images::getInfoFromURL() method
* Test the Images::getInfoFromURL() method (only remote images, not local/relative!)
*
* @dataProvider dataImages
*/
public function testGetInfoFromURL(string $url, array $headers, string $data, array $assertion)
public function testGetInfoFromRemotURL(string $url, array $headers, string $data, array $assertion)
{
self::$httpRequestHandler->setHandler(new MockHandler([
$this->httpRequestHandler->setHandler(new MockHandler([
new Response(200, $headers, $data),
]));