From d4a233a1494e51e4a8cb2f6e3c4c96084b496485 Mon Sep 17 00:00:00 2001 From: Philipp Date: Wed, 25 Aug 2021 00:13:50 +0200 Subject: [PATCH] Extend test capability for HTTP Requests --- src/Factory/HTTPClientFactory.php | 11 +- ...Trait.php => DiceHttpMockHandlerTrait.php} | 36 +++-- tests/src/Network/ProbeTest.php | 133 +++++++++++++++++- tests/src/Util/ImagesTest.php | 16 +-- 4 files changed, 166 insertions(+), 30 deletions(-) rename tests/{DiceTestTrait.php => DiceHttpMockHandlerTrait.php} (57%) diff --git a/src/Factory/HTTPClientFactory.php b/src/Factory/HTTPClientFactory.php index c1cb47541..040ad7514 100644 --- a/src/Factory/HTTPClientFactory.php +++ b/src/Factory/HTTPClientFactory.php @@ -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(); diff --git a/tests/DiceTestTrait.php b/tests/DiceHttpMockHandlerTrait.php similarity index 57% rename from tests/DiceTestTrait.php rename to tests/DiceHttpMockHandlerTrait.php index 2426c20dc..969b76b5b 100644 --- a/tests/DiceTestTrait.php +++ b/tests/DiceHttpMockHandlerTrait.php @@ -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(); diff --git a/tests/src/Network/ProbeTest.php b/tests/src/Network/ProbeTest.php index 58711cb91..79c323adc 100644 --- a/tests/src/Network/ProbeTest.php +++ b/tests/src/Network/ProbeTest.php @@ -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 = ' @@ -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 + } + } + } } diff --git a/tests/src/Util/ImagesTest.php b/tests/src/Util/ImagesTest.php index a7cc682f9..ddadf9547 100644 --- a/tests/src/Util/ImagesTest.php +++ b/tests/src/Util/ImagesTest.php @@ -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), ]));