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\Network\IHTTPClient;
use Friendica\Util\Profiler; use Friendica\Util\Profiler;
use GuzzleHttp\Client; use GuzzleHttp\Client;
use GuzzleHttp\HandlerStack;
use GuzzleHttp\RequestOptions; use GuzzleHttp\RequestOptions;
use mattwright\URLResolver; use mattwright\URLResolver;
use Psr\Http\Message\RequestInterface; use Psr\Http\Message\RequestInterface;
@ -33,7 +34,14 @@ class HTTPClientFactory extends BaseFactory
$this->baseUrl = $baseUrl; $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'); $proxy = $this->config->get('system', 'proxy');
@ -84,6 +92,7 @@ class HTTPClientFactory extends BaseFactory
RequestOptions::HEADERS => [ RequestOptions::HEADERS => [
'User-Agent' => $userAgent, 'User-Agent' => $userAgent,
], ],
'handler' => $handlerStack ?? HandlerStack::create(),
]); ]);
$resolver = new URLResolver(); $resolver = new URLResolver();

View file

@ -21,47 +21,45 @@
namespace Friendica\Test; namespace Friendica\Test;
use Dice\Dice;
use Friendica\DI; use Friendica\DI;
use Friendica\Network\HTTPClient; use Friendica\Factory\HTTPClientFactory;
use Friendica\Network\IHTTPClient; use Friendica\Network\IHTTPClient;
use GuzzleHttp\Client;
use GuzzleHttp\HandlerStack; use GuzzleHttp\HandlerStack;
use mattwright\URLResolver;
/** /**
* This class mocks some DICE dependencies because they're not direct usable for test environments * This class injects a mockable handler into the IHTTPClient dependency per Dice
* (Like fetching data from external endpoints)
*/ */
trait DiceTestTrait trait DiceHttpMockHandlerTrait
{ {
/** /**
* Handler for mocking requests anywhere for testing purpose * Handler for mocking requests anywhere for testing purpose
* *
* @var HandlerStack * @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; 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); DI::init($newDice);
} }
protected function tearDown() : void protected function tearDown(): void
{ {
\Mockery::close(); \Mockery::close();

View file

@ -3,10 +3,21 @@
namespace Friendica\Test\src\Network; namespace Friendica\Test\src\Network;
use Friendica\Network\Probe; 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 = ' const TEMPLATENOBASE = '
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en-us"> <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; namespace Friendica\Test\src\Util;
use Friendica\Test\DiceTestTrait; use Friendica\Test\DiceHttpMockHandlerTrait;
use Friendica\Test\MockedTest; use Friendica\Test\MockedTest;
use Friendica\Util\Images; use Friendica\Util\Images;
use GuzzleHttp\Handler\MockHandler; use GuzzleHttp\Handler\MockHandler;
@ -10,13 +10,13 @@ use GuzzleHttp\Psr7\Response;
class ImagesTest extends MockedTest 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() 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 * @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), new Response(200, $headers, $data),
])); ]));