MWHttpRequestTestCase.php 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251
  1. <?php
  2. use Wikimedia\TestingAccessWrapper;
  3. abstract class MWHttpRequestTestCase extends PHPUnit\Framework\TestCase {
  4. protected static $httpEngine;
  5. protected $oldHttpEngine;
  6. public function setUp() {
  7. parent::setUp();
  8. $this->oldHttpEngine = Http::$httpEngine;
  9. Http::$httpEngine = static::$httpEngine;
  10. try {
  11. $request = MWHttpRequest::factory( 'null:' );
  12. } catch ( DomainException $e ) {
  13. $this->markTestSkipped( static::$httpEngine . ' engine not supported' );
  14. }
  15. if ( static::$httpEngine === 'php' ) {
  16. $this->assertInstanceOf( PhpHttpRequest::class, $request );
  17. } else {
  18. $this->assertInstanceOf( CurlHttpRequest::class, $request );
  19. }
  20. }
  21. public function tearDown() {
  22. parent::tearDown();
  23. Http::$httpEngine = $this->oldHttpEngine;
  24. }
  25. // --------------------
  26. public function testIsRedirect() {
  27. $request = MWHttpRequest::factory( 'http://httpbin.org/get' );
  28. $status = $request->execute();
  29. $this->assertTrue( $status->isGood() );
  30. $this->assertFalse( $request->isRedirect() );
  31. $request = MWHttpRequest::factory( 'http://httpbin.org/redirect/1' );
  32. $status = $request->execute();
  33. $this->assertTrue( $status->isGood() );
  34. $this->assertTrue( $request->isRedirect() );
  35. }
  36. public function testgetFinalUrl() {
  37. $request = MWHttpRequest::factory( 'http://httpbin.org/redirect/3' );
  38. if ( !$request->canFollowRedirects() ) {
  39. $this->markTestSkipped( 'cannot follow redirects' );
  40. }
  41. $status = $request->execute();
  42. $this->assertTrue( $status->isGood() );
  43. $this->assertNotSame( 'http://httpbin.org/get', $request->getFinalUrl() );
  44. $request = MWHttpRequest::factory( 'http://httpbin.org/redirect/3', [ 'followRedirects'
  45. => true ] );
  46. $status = $request->execute();
  47. $this->assertTrue( $status->isGood() );
  48. $this->assertSame( 'http://httpbin.org/get', $request->getFinalUrl() );
  49. $this->assertResponseFieldValue( 'url', 'http://httpbin.org/get', $request );
  50. $request = MWHttpRequest::factory( 'http://httpbin.org/redirect/3', [ 'followRedirects'
  51. => true ] );
  52. $status = $request->execute();
  53. $this->assertTrue( $status->isGood() );
  54. $this->assertSame( 'http://httpbin.org/get', $request->getFinalUrl() );
  55. $this->assertResponseFieldValue( 'url', 'http://httpbin.org/get', $request );
  56. if ( static::$httpEngine === 'curl' ) {
  57. $this->markTestIncomplete( 'maxRedirects seems to be ignored by CurlHttpRequest' );
  58. return;
  59. }
  60. $request = MWHttpRequest::factory( 'http://httpbin.org/redirect/3', [ 'followRedirects'
  61. => true, 'maxRedirects' => 1 ] );
  62. $status = $request->execute();
  63. $this->assertTrue( $status->isGood() );
  64. $this->assertNotSame( 'http://httpbin.org/get', $request->getFinalUrl() );
  65. }
  66. public function testSetCookie() {
  67. $request = MWHttpRequest::factory( 'http://httpbin.org/cookies' );
  68. $request->setCookie( 'foo', 'bar' );
  69. $request->setCookie( 'foo2', 'bar2', [ 'domain' => 'example.com' ] );
  70. $status = $request->execute();
  71. $this->assertTrue( $status->isGood() );
  72. $this->assertResponseFieldValue( 'cookies', [ 'foo' => 'bar' ], $request );
  73. }
  74. public function testSetCookieJar() {
  75. $request = MWHttpRequest::factory( 'http://httpbin.org/cookies' );
  76. $cookieJar = new CookieJar();
  77. $cookieJar->setCookie( 'foo', 'bar', [ 'domain' => 'httpbin.org' ] );
  78. $cookieJar->setCookie( 'foo2', 'bar2', [ 'domain' => 'example.com' ] );
  79. $request->setCookieJar( $cookieJar );
  80. $status = $request->execute();
  81. $this->assertTrue( $status->isGood() );
  82. $this->assertResponseFieldValue( 'cookies', [ 'foo' => 'bar' ], $request );
  83. $request = MWHttpRequest::factory( 'http://httpbin.org/cookies/set?foo=bar' );
  84. $cookieJar = new CookieJar();
  85. $request->setCookieJar( $cookieJar );
  86. $status = $request->execute();
  87. $this->assertTrue( $status->isGood() );
  88. $this->assertHasCookie( 'foo', 'bar', $request->getCookieJar() );
  89. $this->markTestIncomplete( 'CookieJar does not handle deletion' );
  90. // $request = MWHttpRequest::factory( 'http://httpbin.org/cookies/delete?foo' );
  91. // $cookieJar = new CookieJar();
  92. // $cookieJar->setCookie( 'foo', 'bar', [ 'domain' => 'httpbin.org' ] );
  93. // $cookieJar->setCookie( 'foo2', 'bar2', [ 'domain' => 'httpbin.org' ] );
  94. // $request->setCookieJar( $cookieJar );
  95. // $status = $request->execute();
  96. // $this->assertTrue( $status->isGood() );
  97. // $this->assertNotHasCookie( 'foo', $request->getCookieJar() );
  98. // $this->assertHasCookie( 'foo2', 'bar2', $request->getCookieJar() );
  99. }
  100. public function testGetResponseHeaders() {
  101. $request = MWHttpRequest::factory( 'http://httpbin.org/response-headers?Foo=bar' );
  102. $status = $request->execute();
  103. $this->assertTrue( $status->isGood() );
  104. $headers = array_change_key_case( $request->getResponseHeaders(), CASE_LOWER );
  105. $this->assertArrayHasKey( 'foo', $headers );
  106. $this->assertSame( $request->getResponseHeader( 'Foo' ), 'bar' );
  107. }
  108. public function testSetHeader() {
  109. $request = MWHttpRequest::factory( 'http://httpbin.org/headers' );
  110. $request->setHeader( 'Foo', 'bar' );
  111. $status = $request->execute();
  112. $this->assertTrue( $status->isGood() );
  113. $this->assertResponseFieldValue( [ 'headers', 'Foo' ], 'bar', $request );
  114. }
  115. public function testGetStatus() {
  116. $request = MWHttpRequest::factory( 'http://httpbin.org/status/418' );
  117. $status = $request->execute();
  118. $this->assertFalse( $status->isOK() );
  119. $this->assertSame( $request->getStatus(), 418 );
  120. }
  121. public function testSetUserAgent() {
  122. $request = MWHttpRequest::factory( 'http://httpbin.org/user-agent' );
  123. $request->setUserAgent( 'foo' );
  124. $status = $request->execute();
  125. $this->assertTrue( $status->isGood() );
  126. $this->assertResponseFieldValue( 'user-agent', 'foo', $request );
  127. }
  128. public function testSetData() {
  129. $request = MWHttpRequest::factory( 'http://httpbin.org/post', [ 'method' => 'POST' ] );
  130. $request->setData( [ 'foo' => 'bar', 'foo2' => 'bar2' ] );
  131. $status = $request->execute();
  132. $this->assertTrue( $status->isGood() );
  133. $this->assertResponseFieldValue( 'form', [ 'foo' => 'bar', 'foo2' => 'bar2' ], $request );
  134. }
  135. public function testSetCallback() {
  136. if ( static::$httpEngine === 'php' ) {
  137. $this->markTestIncomplete( 'PhpHttpRequest does not use setCallback()' );
  138. return;
  139. }
  140. $request = MWHttpRequest::factory( 'http://httpbin.org/ip' );
  141. $data = '';
  142. $request->setCallback( function ( $fh, $content ) use ( &$data ) {
  143. $data .= $content;
  144. return strlen( $content );
  145. } );
  146. $status = $request->execute();
  147. $this->assertTrue( $status->isGood() );
  148. $data = json_decode( $data, true );
  149. $this->assertInternalType( 'array', $data );
  150. $this->assertArrayHasKey( 'origin', $data );
  151. }
  152. public function testBasicAuthentication() {
  153. $request = MWHttpRequest::factory( 'http://httpbin.org/basic-auth/user/pass', [
  154. 'username' => 'user',
  155. 'password' => 'pass',
  156. ] );
  157. $status = $request->execute();
  158. $this->assertTrue( $status->isGood() );
  159. $this->assertResponseFieldValue( 'authenticated', true, $request );
  160. $request = MWHttpRequest::factory( 'http://httpbin.org/basic-auth/user/pass', [
  161. 'username' => 'user',
  162. 'password' => 'wrongpass',
  163. ] );
  164. $status = $request->execute();
  165. $this->assertFalse( $status->isOK() );
  166. $this->assertSame( 401, $request->getStatus() );
  167. }
  168. public function testFactoryDefaults() {
  169. $request = MWHttpRequest::factory( 'http://acme.test' );
  170. $this->assertInstanceOf( MWHttpRequest::class, $request );
  171. }
  172. // --------------------
  173. /**
  174. * Verifies that the request was successful, returned valid JSON and the given field of that
  175. * JSON data is as expected.
  176. * @param string|string[] $key Path to the data in the response object
  177. * @param mixed $expectedValue
  178. * @param MWHttpRequest $response
  179. */
  180. protected function assertResponseFieldValue( $key, $expectedValue, MWHttpRequest $response ) {
  181. $this->assertSame( 200, $response->getStatus(), 'response status is not 200' );
  182. $data = json_decode( $response->getContent(), true );
  183. $this->assertInternalType( 'array', $data, 'response is not JSON' );
  184. $keyPath = '';
  185. foreach ( (array)$key as $keySegment ) {
  186. $keyPath .= ( $keyPath ? '.' : '' ) . $keySegment;
  187. $this->assertArrayHasKey( $keySegment, $data, $keyPath . ' not found' );
  188. $data = $data[$keySegment];
  189. }
  190. $this->assertSame( $expectedValue, $data );
  191. }
  192. /**
  193. * Asserts that the cookie jar has the given cookie with the given value.
  194. * @param string $expectedName Cookie name
  195. * @param string $expectedValue Cookie value
  196. * @param CookieJar $cookieJar
  197. */
  198. protected function assertHasCookie( $expectedName, $expectedValue, CookieJar $cookieJar ) {
  199. $cookieJar = TestingAccessWrapper::newFromObject( $cookieJar );
  200. $cookies = array_change_key_case( $cookieJar->cookie, CASE_LOWER );
  201. $this->assertArrayHasKey( strtolower( $expectedName ), $cookies );
  202. $cookie = TestingAccessWrapper::newFromObject(
  203. $cookies[strtolower( $expectedName )] );
  204. $this->assertSame( $expectedValue, $cookie->value );
  205. }
  206. /**
  207. * Asserts that the cookie jar does not have the given cookie.
  208. * @param string $name Cookie name
  209. * @param CookieJar $cookieJar
  210. */
  211. protected function assertNotHasCookie( $name, CookieJar $cookieJar ) {
  212. $cookieJar = TestingAccessWrapper::newFromObject( $cookieJar );
  213. $this->assertArrayNotHasKey( strtolower( $name ),
  214. array_change_key_case( $cookieJar->cookie, CASE_LOWER ) );
  215. }
  216. }