+<?php
+
+/**
+ * Tests for MultiHttpClient
+ *
+ * The urls herein are not actually called, because we mock the return results.
+ *
+ * @covers MultiHttpClient
+ */
+class MultiHttpClientTest extends MediaWikiTestCase {
+ protected $client;
+
+ protected function setUp() {
+ parent::setUp();
+ $client = $this->getMockBuilder( MultiHttpClient::class )
+ ->setConstructorArgs( [ [] ] )
+ ->setMethods( [ 'isCurlEnabled' ] )->getMock();
+ $client->method( 'isCurlEnabled' )->willReturn( false );
+ $this->client = $client;
+ }
+
+ private function getHttpRequest( $statusValue, $statusCode, $headers = [] ) {
+ $httpRequest = $this->getMockBuilder( PhpHttpRequest::class )
+ ->setConstructorArgs( [ '', [] ] )
+ ->getMock();
+ $httpRequest->expects( $this->any() )
+ ->method( 'execute' )
+ ->willReturn( Status::wrap( $statusValue ) );
+ $httpRequest->expects( $this->any() )
+ ->method( 'getResponseHeaders' )
+ ->willReturn( $headers );
+ $httpRequest->expects( $this->any() )
+ ->method( 'getStatus' )
+ ->willReturn( $statusCode );
+ return $httpRequest;
+ }
+
+ private function mockHttpRequestFactory( $httpRequest ) {
+ $factory = $this->getMockBuilder( MediaWiki\Http\HttpRequestFactory::class )
+ ->getMock();
+ $factory->expects( $this->any() )
+ ->method( 'create' )
+ ->willReturn( $httpRequest );
+ return $factory;
+ }
+
+ /**
+ * Test call of a single url that should succeed
+ */
+ public function testMultiHttpClientSingleSuccess() {
+ // Mock success
+ $httpRequest = $this->getHttpRequest( StatusValue::newGood( 200 ), 200 );
+ $this->setService( 'HttpRequestFactory', $this->mockHttpRequestFactory( $httpRequest ) );
+
+ list( $rcode, $rdesc, /* $rhdrs */, $rbody, $rerr ) = $this->client->run( [
+ 'method' => 'GET',
+ 'url' => "http://example.test",
+ ] );
+
+ $this->assertEquals( 200, $rcode );
+ }
+
+ /**
+ * Test call of a single url that should not exist, and therefore fail
+ */
+ public function testMultiHttpClientSingleFailure() {
+ // Mock an invalid tld
+ $httpRequest = $this->getHttpRequest(
+ StatusValue::newFatal( 'http-invalid-url', 'http://www.example.test' ), 0 );
+ $this->setService( 'HttpRequestFactory', $this->mockHttpRequestFactory( $httpRequest ) );
+
+ list( $rcode, $rdesc, /* $rhdrs */, $rbody, $rerr ) = $this->client->run( [
+ 'method' => 'GET',
+ 'url' => "http://www.example.test",
+ ] );
+
+ $failure = $rcode < 200 || $rcode >= 400;
+ $this->assertTrue( $failure );
+ }
+
+ /**
+ * Test call of multiple urls that should all succeed
+ */
+ public function testMultiHttpClientMultipleSuccess() {
+ // Mock success
+ $httpRequest = $this->getHttpRequest( StatusValue::newGood( 200 ), 200 );
+ $this->setService( 'HttpRequestFactory', $this->mockHttpRequestFactory( $httpRequest ) );
+
+ $reqs = [
+ [
+ 'method' => 'GET',
+ 'url' => 'http://example.test',
+ ],
+ [
+ 'method' => 'GET',
+ 'url' => 'https://get.test',
+ ],
+ ];
+ $responses = $this->client->runMulti( $reqs );
+ foreach ( $responses as $response ) {
+ list( $rcode, $rdesc, /* $rhdrs */, $rbody, $rerr ) = $response['response'];
+ $this->assertEquals( 200, $rcode );
+ }
+ }
+
+ /**
+ * Test call of multiple urls that should all fail
+ */
+ public function testMultiHttpClientMultipleFailure() {
+ // Mock page not found
+ $httpRequest = $this->getHttpRequest(
+ StatusValue::newFatal( "http-bad-status", 404, 'Not Found' ), 404 );
+ $this->setService( 'HttpRequestFactory', $this->mockHttpRequestFactory( $httpRequest ) );
+
+ $reqs = [
+ [
+ 'method' => 'GET',
+ 'url' => 'http://example.test/12345',
+ ],
+ [
+ 'method' => 'GET',
+ 'url' => 'http://example.test/67890' ,
+ ]
+ ];
+ $responses = $this->client->runMulti( $reqs );
+ foreach ( $responses as $response ) {
+ list( $rcode, $rdesc, /* $rhdrs */, $rbody, $rerr ) = $response['response'];
+ $failure = $rcode < 200 || $rcode >= 400;
+ $this->assertTrue( $failure );
+ }
+ }
+
+ /**
+ * Test of response header handling
+ */
+ public function testMultiHttpClientHeaders() {
+ // Represenative headers for typical requests, per MWHttpRequest::getResponseHeaders()
+ $headers = [
+ 'content-type' => [
+ 'text/html; charset=utf-8',
+ ],
+ 'date' => [
+ 'Wed, 18 Jul 2018 14:52:41 GMT',
+ ],
+ 'set-cookie' => [
+ 'COUNTRY=NAe6; expires=Wed, 25-Jul-2018 14:52:41 GMT; path=/; domain=.example.test',
+ 'LAST_NEWS=1531925562; expires=Thu, 18-Jul-2019 14:52:41 GMT; path=/; domain=.example.test',
+ ]
+ ];
+
+ // Mock success with specific headers
+ $httpRequest = $this->getHttpRequest( StatusValue::newGood( 200 ), 200, $headers );
+ $this->setService( 'HttpRequestFactory', $this->mockHttpRequestFactory( $httpRequest ) );
+
+ list( $rcode, $rdesc, $rhdrs, $rbody, $rerr ) = $this->client->run( [
+ 'method' => 'GET',
+ 'url' => 'http://example.test',
+ ] );
+
+ $this->assertEquals( 200, $rcode );
+ $this->assertEquals( count( $headers ), count( $rhdrs ) );
+ foreach ( $headers as $name => $values ) {
+ $value = implode( ', ', $values );
+ $this->assertArrayHasKey( $name, $rhdrs );
+ $this->assertEquals( $value, $rhdrs[$name] );
+ }
+ }
+}