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