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