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