Merge "Update comment about enabled extensions"
[lhc/web/wiklou.git] / tests / phpunit / includes / api / ApiMainTest.php
1 <?php
2
3 /**
4 * @group API
5 * @group medium
6 *
7 * @covers ApiMain
8 */
9 class ApiMainTest extends ApiTestCase {
10
11 /**
12 * Test that the API will accept a FauxRequest and execute.
13 */
14 public function testApi() {
15 $api = new ApiMain(
16 new FauxRequest( array( 'action' => 'query', 'meta' => 'siteinfo' ) )
17 );
18 $api->execute();
19 $data = $api->getResult()->getResultData();
20 $this->assertInternalType( 'array', $data );
21 $this->assertArrayHasKey( 'query', $data );
22 }
23
24 public static function provideAssert() {
25 return array(
26 array( false, array(), 'user', 'assertuserfailed' ),
27 array( true, array(), 'user', false ),
28 array( true, array(), 'bot', 'assertbotfailed' ),
29 array( true, array( 'bot' ), 'user', false ),
30 array( true, array( 'bot' ), 'bot', false ),
31 );
32 }
33
34 /**
35 * Tests the assert={user|bot} functionality
36 *
37 * @covers ApiMain::checkAsserts
38 * @dataProvider provideAssert
39 * @param bool $registered
40 * @param array $rights
41 * @param string $assert
42 * @param string|bool $error False if no error expected
43 */
44 public function testAssert( $registered, $rights, $assert, $error ) {
45 $user = new User();
46 if ( $registered ) {
47 $user->setId( 1 );
48 }
49 $user->mRights = $rights;
50 try {
51 $this->doApiRequest( array(
52 'action' => 'query',
53 'assert' => $assert,
54 ), null, null, $user );
55 $this->assertFalse( $error ); // That no error was expected
56 } catch ( UsageException $e ) {
57 $this->assertEquals( $e->getCodeString(), $error );
58 }
59 }
60
61 /**
62 * Test if all classes in the main module manager exists
63 */
64 public function testClassNamesInModuleManager() {
65 global $wgAutoloadLocalClasses, $wgAutoloadClasses;
66
67 // wgAutoloadLocalClasses has precedence, just like in includes/AutoLoader.php
68 $classes = $wgAutoloadLocalClasses + $wgAutoloadClasses;
69
70 $api = new ApiMain(
71 new FauxRequest( array( 'action' => 'query', 'meta' => 'siteinfo' ) )
72 );
73 $modules = $api->getModuleManager()->getNamesWithClasses();
74 foreach ( $modules as $name => $class ) {
75 $this->assertArrayHasKey(
76 $class,
77 $classes,
78 'Class ' . $class . ' for api module ' . $name . ' not in autoloader (with exact case)'
79 );
80 }
81 }
82
83 /**
84 * Test HTTP precondition headers
85 *
86 * @covers ApiMain::checkConditionalRequestHeaders
87 * @dataProvider provideCheckConditionalRequestHeaders
88 * @param array $headers HTTP headers
89 * @param array $conditions Return data for ApiBase::getConditionalRequestData
90 * @param int $status Expected response status
91 * @param bool $post Request is a POST
92 */
93 public function testCheckConditionalRequestHeaders(
94 $headers, $conditions, $status, $post = false
95 ) {
96 $request = new FauxRequest( array( 'action' => 'query', 'meta' => 'siteinfo' ), $post );
97 $request->setHeaders( $headers );
98 $request->response()->statusHeader( 200 ); // Why doesn't it default?
99
100 $api = new ApiMain( $request );
101 $priv = TestingAccessWrapper::newFromObject( $api );
102 $priv->mInternalMode = false;
103
104 $module = $this->getMockBuilder( 'ApiBase' )
105 ->setConstructorArgs( array( $api, 'mock' ) )
106 ->setMethods( array( 'getConditionalRequestData' ) )
107 ->getMockForAbstractClass();
108 $module->expects( $this->any() )
109 ->method( 'getConditionalRequestData' )
110 ->will( $this->returnCallback( function ( $condition ) use ( $conditions ) {
111 return isset( $conditions[$condition] ) ? $conditions[$condition] : null;
112 } ) );
113
114 $ret = $priv->checkConditionalRequestHeaders( $module );
115
116 $this->assertSame( $status, $request->response()->getStatusCode() );
117 $this->assertSame( $status === 200, $ret );
118 }
119
120 public static function provideCheckConditionalRequestHeaders() {
121 $now = time();
122
123 return array(
124 // Non-existing from module is ignored
125 array( array( 'If-None-Match' => '"foo", "bar"' ), array(), 200 ),
126 array( array( 'If-Modified-Since' => 'Tue, 18 Aug 2015 00:00:00 GMT' ), array(), 200 ),
127
128 // No headers
129 array(
130 array(),
131 array(
132 'etag' => '""',
133 'last-modified' => '20150815000000',
134 ),
135 200
136 ),
137
138 // Basic If-None-Match
139 array( array( 'If-None-Match' => '"foo", "bar"' ), array( 'etag' => '"bar"' ), 304 ),
140 array( array( 'If-None-Match' => '"foo", "bar"' ), array( 'etag' => '"baz"' ), 200 ),
141 array( array( 'If-None-Match' => '"foo"' ), array( 'etag' => 'W/"foo"' ), 304 ),
142 array( array( 'If-None-Match' => 'W/"foo"' ), array( 'etag' => '"foo"' ), 304 ),
143 array( array( 'If-None-Match' => 'W/"foo"' ), array( 'etag' => 'W/"foo"' ), 304 ),
144
145 // Pointless, but supported
146 array( array( 'If-None-Match' => '*' ), array(), 304 ),
147
148 // Basic If-Modified-Since
149 array( array( 'If-Modified-Since' => wfTimestamp( TS_RFC2822, $now ) ),
150 array( 'last-modified' => wfTimestamp( TS_MW, $now - 1 ) ), 304 ),
151 array( array( 'If-Modified-Since' => wfTimestamp( TS_RFC2822, $now ) ),
152 array( 'last-modified' => wfTimestamp( TS_MW, $now ) ), 304 ),
153 array( array( 'If-Modified-Since' => wfTimestamp( TS_RFC2822, $now ) ),
154 array( 'last-modified' => wfTimestamp( TS_MW, $now + 1 ) ), 200 ),
155
156 // If-Modified-Since ignored when If-None-Match is given too
157 array( array( 'If-None-Match' => '""', 'If-Modified-Since' => wfTimestamp( TS_RFC2822, $now ) ),
158 array( 'etag' => '"x"', 'last-modified' => wfTimestamp( TS_MW, $now - 1 ) ), 200 ),
159 array( array( 'If-None-Match' => '""', 'If-Modified-Since' => wfTimestamp( TS_RFC2822, $now ) ),
160 array( 'last-modified' => wfTimestamp( TS_MW, $now - 1 ) ), 304 ),
161
162 // Ignored for POST
163 array( array( 'If-None-Match' => '"foo", "bar"' ), array( 'etag' => '"bar"' ), 200, true ),
164 array( array( 'If-Modified-Since' => wfTimestamp( TS_RFC2822, $now ) ),
165 array( 'last-modified' => wfTimestamp( TS_MW, $now - 1 ) ), 200, true ),
166
167 // Other date formats allowed by the RFC
168 array( array( 'If-Modified-Since' => gmdate( 'l, d-M-y H:i:s', $now ) . ' GMT' ),
169 array( 'last-modified' => wfTimestamp( TS_MW, $now - 1 ) ), 304 ),
170 array( array( 'If-Modified-Since' => gmdate( 'D M j H:i:s Y', $now ) ),
171 array( 'last-modified' => wfTimestamp( TS_MW, $now - 1 ) ), 304 ),
172
173 // Old browser extension to HTTP/1.0
174 array( array( 'If-Modified-Since' => wfTimestamp( TS_RFC2822, $now ) . '; length=123' ),
175 array( 'last-modified' => wfTimestamp( TS_MW, $now - 1 ) ), 304 ),
176
177 // Invalid date formats should be ignored
178 array( array( 'If-Modified-Since' => gmdate( 'Y-m-d H:i:s', $now ) . ' GMT' ),
179 array( 'last-modified' => wfTimestamp( TS_MW, $now - 1 ) ), 200 ),
180 );
181 }
182
183 /**
184 * Test conditional headers output
185 * @dataProvider provideConditionalRequestHeadersOutput
186 * @param array $conditions Return data for ApiBase::getConditionalRequestData
187 * @param array $headers Expected output headers
188 * @param bool $isError $isError flag
189 * @param bool $post Request is a POST
190 */
191 public function testConditionalRequestHeadersOutput(
192 $conditions, $headers, $isError = false, $post = false
193 ) {
194 $request = new FauxRequest( array( 'action' => 'query', 'meta' => 'siteinfo' ), $post );
195 $response = $request->response();
196
197 $api = new ApiMain( $request );
198 $priv = TestingAccessWrapper::newFromObject( $api );
199 $priv->mInternalMode = false;
200
201 $module = $this->getMockBuilder( 'ApiBase' )
202 ->setConstructorArgs( array( $api, 'mock' ) )
203 ->setMethods( array( 'getConditionalRequestData' ) )
204 ->getMockForAbstractClass();
205 $module->expects( $this->any() )
206 ->method( 'getConditionalRequestData' )
207 ->will( $this->returnCallback( function ( $condition ) use ( $conditions ) {
208 return isset( $conditions[$condition] ) ? $conditions[$condition] : null;
209 } ) );
210 $priv->mModule = $module;
211
212 $priv->sendCacheHeaders( $isError );
213
214 foreach ( array( 'Last-Modified', 'ETag' ) as $header ) {
215 $this->assertEquals(
216 isset( $headers[$header] ) ? $headers[$header] : null,
217 $response->getHeader( $header ),
218 $header
219 );
220 }
221 }
222
223 public static function provideConditionalRequestHeadersOutput() {
224 return array(
225 array(
226 array(),
227 array()
228 ),
229 array(
230 array( 'etag' => '"foo"' ),
231 array( 'ETag' => '"foo"' )
232 ),
233 array(
234 array( 'last-modified' => '20150818000102' ),
235 array( 'Last-Modified' => 'Tue, 18 Aug 2015 00:01:02 GMT' )
236 ),
237 array(
238 array( 'etag' => '"foo"', 'last-modified' => '20150818000102' ),
239 array( 'ETag' => '"foo"', 'Last-Modified' => 'Tue, 18 Aug 2015 00:01:02 GMT' )
240 ),
241 array(
242 array( 'etag' => '"foo"', 'last-modified' => '20150818000102' ),
243 array(),
244 true,
245 ),
246 array(
247 array( 'etag' => '"foo"', 'last-modified' => '20150818000102' ),
248 array(),
249 false,
250 true,
251 ),
252 );
253 }
254
255 }