Merge "Update README from mediawiki.org upstream"
[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 $context = $this->apiContext->newTestContext( $request, null );
101 $api = new ApiMain( $context );
102 $priv = TestingAccessWrapper::newFromObject( $api );
103 $priv->mInternalMode = false;
104
105 $module = $this->getMockBuilder( 'ApiBase' )
106 ->setConstructorArgs( array( $api, 'mock' ) )
107 ->setMethods( array( 'getConditionalRequestData' ) )
108 ->getMockForAbstractClass();
109 $module->expects( $this->any() )
110 ->method( 'getConditionalRequestData' )
111 ->will( $this->returnCallback( function ( $condition ) use ( $conditions ) {
112 return isset( $conditions[$condition] ) ? $conditions[$condition] : null;
113 } ) );
114
115 $ret = $priv->checkConditionalRequestHeaders( $module );
116
117 $this->assertSame( $status, $request->response()->getStatusCode() );
118 $this->assertSame( $status === 200, $ret );
119 }
120
121 public static function provideCheckConditionalRequestHeaders() {
122 $now = time();
123
124 return array(
125 // Non-existing from module is ignored
126 array( array( 'If-None-Match' => '"foo", "bar"' ), array(), 200 ),
127 array( array( 'If-Modified-Since' => 'Tue, 18 Aug 2015 00:00:00 GMT' ), array(), 200 ),
128
129 // No headers
130 array(
131 array(),
132 array(
133 'etag' => '""',
134 'last-modified' => '20150815000000',
135 ),
136 200
137 ),
138
139 // Basic If-None-Match
140 array( array( 'If-None-Match' => '"foo", "bar"' ), array( 'etag' => '"bar"' ), 304 ),
141 array( array( 'If-None-Match' => '"foo", "bar"' ), array( 'etag' => '"baz"' ), 200 ),
142 array( array( 'If-None-Match' => '"foo"' ), array( 'etag' => 'W/"foo"' ), 304 ),
143 array( array( 'If-None-Match' => 'W/"foo"' ), array( 'etag' => '"foo"' ), 304 ),
144 array( array( 'If-None-Match' => 'W/"foo"' ), array( 'etag' => 'W/"foo"' ), 304 ),
145
146 // Pointless, but supported
147 array( array( 'If-None-Match' => '*' ), array(), 304 ),
148
149 // Basic If-Modified-Since
150 array( array( 'If-Modified-Since' => wfTimestamp( TS_RFC2822, $now ) ),
151 array( 'last-modified' => wfTimestamp( TS_MW, $now - 1 ) ), 304 ),
152 array( array( 'If-Modified-Since' => wfTimestamp( TS_RFC2822, $now ) ),
153 array( 'last-modified' => wfTimestamp( TS_MW, $now ) ), 304 ),
154 array( array( 'If-Modified-Since' => wfTimestamp( TS_RFC2822, $now ) ),
155 array( 'last-modified' => wfTimestamp( TS_MW, $now + 1 ) ), 200 ),
156
157 // If-Modified-Since ignored when If-None-Match is given too
158 array( array( 'If-None-Match' => '""', 'If-Modified-Since' => wfTimestamp( TS_RFC2822, $now ) ),
159 array( 'etag' => '"x"', 'last-modified' => wfTimestamp( TS_MW, $now - 1 ) ), 200 ),
160 array( array( 'If-None-Match' => '""', 'If-Modified-Since' => wfTimestamp( TS_RFC2822, $now ) ),
161 array( 'last-modified' => wfTimestamp( TS_MW, $now - 1 ) ), 304 ),
162
163 // Ignored for POST
164 array( array( 'If-None-Match' => '"foo", "bar"' ), array( 'etag' => '"bar"' ), 200, true ),
165 array( array( 'If-Modified-Since' => wfTimestamp( TS_RFC2822, $now ) ),
166 array( 'last-modified' => wfTimestamp( TS_MW, $now - 1 ) ), 200, true ),
167
168 // Other date formats allowed by the RFC
169 array( array( 'If-Modified-Since' => gmdate( 'l, d-M-y H:i:s', $now ) . ' GMT' ),
170 array( 'last-modified' => wfTimestamp( TS_MW, $now - 1 ) ), 304 ),
171 array( array( 'If-Modified-Since' => gmdate( 'D M j H:i:s Y', $now ) ),
172 array( 'last-modified' => wfTimestamp( TS_MW, $now - 1 ) ), 304 ),
173
174 // Old browser extension to HTTP/1.0
175 array( array( 'If-Modified-Since' => wfTimestamp( TS_RFC2822, $now ) . '; length=123' ),
176 array( 'last-modified' => wfTimestamp( TS_MW, $now - 1 ) ), 304 ),
177
178 // Invalid date formats should be ignored
179 array( array( 'If-Modified-Since' => gmdate( 'Y-m-d H:i:s', $now ) . ' GMT' ),
180 array( 'last-modified' => wfTimestamp( TS_MW, $now - 1 ) ), 200 ),
181 );
182 }
183
184 /**
185 * Test conditional headers output
186 * @dataProvider provideConditionalRequestHeadersOutput
187 * @param array $conditions Return data for ApiBase::getConditionalRequestData
188 * @param array $headers Expected output headers
189 * @param bool $isError $isError flag
190 * @param bool $post Request is a POST
191 */
192 public function testConditionalRequestHeadersOutput(
193 $conditions, $headers, $isError = false, $post = false
194 ) {
195 $request = new FauxRequest( array( 'action' => 'query', 'meta' => 'siteinfo' ), $post );
196 $response = $request->response();
197
198 $api = new ApiMain( $request );
199 $priv = TestingAccessWrapper::newFromObject( $api );
200 $priv->mInternalMode = false;
201
202 $module = $this->getMockBuilder( 'ApiBase' )
203 ->setConstructorArgs( array( $api, 'mock' ) )
204 ->setMethods( array( 'getConditionalRequestData' ) )
205 ->getMockForAbstractClass();
206 $module->expects( $this->any() )
207 ->method( 'getConditionalRequestData' )
208 ->will( $this->returnCallback( function ( $condition ) use ( $conditions ) {
209 return isset( $conditions[$condition] ) ? $conditions[$condition] : null;
210 } ) );
211 $priv->mModule = $module;
212
213 $priv->sendCacheHeaders( $isError );
214
215 foreach ( array( 'Last-Modified', 'ETag' ) as $header ) {
216 $this->assertEquals(
217 isset( $headers[$header] ) ? $headers[$header] : null,
218 $response->getHeader( $header ),
219 $header
220 );
221 }
222 }
223
224 public static function provideConditionalRequestHeadersOutput() {
225 return array(
226 array(
227 array(),
228 array()
229 ),
230 array(
231 array( 'etag' => '"foo"' ),
232 array( 'ETag' => '"foo"' )
233 ),
234 array(
235 array( 'last-modified' => '20150818000102' ),
236 array( 'Last-Modified' => 'Tue, 18 Aug 2015 00:01:02 GMT' )
237 ),
238 array(
239 array( 'etag' => '"foo"', 'last-modified' => '20150818000102' ),
240 array( 'ETag' => '"foo"', 'Last-Modified' => 'Tue, 18 Aug 2015 00:01:02 GMT' )
241 ),
242 array(
243 array( 'etag' => '"foo"', 'last-modified' => '20150818000102' ),
244 array(),
245 true,
246 ),
247 array(
248 array( 'etag' => '"foo"', 'last-modified' => '20150818000102' ),
249 array(),
250 false,
251 true,
252 ),
253 );
254 }
255
256 }