Merge "Revert "Remove old remapping hacks from Database::indexName()""
[lhc/web/wiklou.git] / tests / phpunit / includes / api / ApiErrorFormatterTest.php
1 <?php
2
3 /**
4 * @group API
5 */
6 class ApiErrorFormatterTest extends MediaWikiLangTestCase {
7
8 /**
9 * @covers ApiErrorFormatter
10 */
11 public function testErrorFormatterBasics() {
12 $result = new ApiResult( 8388608 );
13 $formatter = new ApiErrorFormatter( $result, Language::factory( 'de' ), 'wikitext', false );
14 $this->assertSame( 'de', $formatter->getLanguage()->getCode() );
15
16 $formatter->addMessagesFromStatus( null, Status::newGood() );
17 $this->assertSame(
18 [ ApiResult::META_TYPE => 'assoc' ],
19 $result->getResultData()
20 );
21
22 $this->assertSame( [], $formatter->arrayFromStatus( Status::newGood() ) );
23
24 $wrappedFormatter = TestingAccessWrapper::newFromObject( $formatter );
25 $this->assertSame(
26 'Blah "kbd" <X> 😊',
27 $wrappedFormatter->stripMarkup( 'Blah <kbd>kbd</kbd> <b>&lt;X&gt;</b> &#x1f60a;' ),
28 'stripMarkup'
29 );
30 }
31
32 /**
33 * @covers ApiErrorFormatter
34 * @dataProvider provideErrorFormatter
35 */
36 public function testErrorFormatter( $format, $lang, $useDB,
37 $expect1, $expect2, $expect3
38 ) {
39 $result = new ApiResult( 8388608 );
40 $formatter = new ApiErrorFormatter( $result, Language::factory( $lang ), $format, $useDB );
41
42 // Add default type
43 $expect1[ApiResult::META_TYPE] = 'assoc';
44 $expect2[ApiResult::META_TYPE] = 'assoc';
45 $expect3[ApiResult::META_TYPE] = 'assoc';
46
47 $formatter->addWarning( 'string', 'mainpage' );
48 $formatter->addError( 'err', 'mainpage' );
49 $this->assertEquals( $expect1, $result->getResultData(), 'Simple test' );
50
51 $result->reset();
52 $formatter->addWarning( 'foo', 'mainpage' );
53 $formatter->addWarning( 'foo', 'mainpage' );
54 $formatter->addWarning( 'foo', [ 'parentheses', 'foobar' ] );
55 $msg1 = wfMessage( 'mainpage' );
56 $formatter->addWarning( 'message', $msg1 );
57 $msg2 = new ApiMessage( 'mainpage', 'overriddenCode', [ 'overriddenData' => true ] );
58 $formatter->addWarning( 'messageWithData', $msg2 );
59 $formatter->addError( 'errWithData', $msg2 );
60 $this->assertSame( $expect2, $result->getResultData(), 'Complex test' );
61
62 $this->assertEquals(
63 $this->removeModuleTag( $expect2['warnings'][2] ),
64 $formatter->formatMessage( $msg1 ),
65 'formatMessage test 1'
66 );
67 $this->assertEquals(
68 $this->removeModuleTag( $expect2['warnings'][3] ),
69 $formatter->formatMessage( $msg2 ),
70 'formatMessage test 2'
71 );
72
73 $result->reset();
74 $status = Status::newGood();
75 $status->warning( 'mainpage' );
76 $status->warning( 'parentheses', 'foobar' );
77 $status->warning( $msg1 );
78 $status->warning( $msg2 );
79 $status->error( 'mainpage' );
80 $status->error( 'parentheses', 'foobar' );
81 $formatter->addMessagesFromStatus( 'status', $status );
82 $this->assertSame( $expect3, $result->getResultData(), 'Status test' );
83
84 $this->assertSame(
85 array_map( [ $this, 'removeModuleTag' ], $expect3['errors'] ),
86 $formatter->arrayFromStatus( $status, 'error' ),
87 'arrayFromStatus test for error'
88 );
89 $this->assertSame(
90 array_map( [ $this, 'removeModuleTag' ], $expect3['warnings'] ),
91 $formatter->arrayFromStatus( $status, 'warning' ),
92 'arrayFromStatus test for warning'
93 );
94 }
95
96 private function removeModuleTag( $s ) {
97 if ( is_array( $s ) ) {
98 unset( $s['module'] );
99 }
100 return $s;
101 }
102
103 public static function provideErrorFormatter() {
104 $mainpageText = wfMessage( 'mainpage' )->inLanguage( 'de' )->useDatabase( false )->text();
105 $parensText = wfMessage( 'parentheses', 'foobar' )->inLanguage( 'de' )
106 ->useDatabase( false )->text();
107 $mainpageHTML = wfMessage( 'mainpage' )->inLanguage( 'en' )->parse();
108 $parensHTML = wfMessage( 'parentheses', 'foobar' )->inLanguage( 'en' )->parse();
109 $C = ApiResult::META_CONTENT;
110 $I = ApiResult::META_INDEXED_TAG_NAME;
111 $overriddenData = [ 'overriddenData' => true, ApiResult::META_TYPE => 'assoc' ];
112
113 return [
114 $tmp = [ 'wikitext', 'de', false,
115 [
116 'errors' => [
117 [ 'code' => 'mainpage', 'text' => $mainpageText, 'module' => 'err', $C => 'text' ],
118 $I => 'error',
119 ],
120 'warnings' => [
121 [ 'code' => 'mainpage', 'text' => $mainpageText, 'module' => 'string', $C => 'text' ],
122 $I => 'warning',
123 ],
124 ],
125 [
126 'errors' => [
127 [ 'code' => 'overriddenCode', 'text' => $mainpageText,
128 'data' => $overriddenData, 'module' => 'errWithData', $C => 'text' ],
129 $I => 'error',
130 ],
131 'warnings' => [
132 [ 'code' => 'mainpage', 'text' => $mainpageText, 'module' => 'foo', $C => 'text' ],
133 [ 'code' => 'parentheses', 'text' => $parensText, 'module' => 'foo', $C => 'text' ],
134 [ 'code' => 'mainpage', 'text' => $mainpageText, 'module' => 'message', $C => 'text' ],
135 [ 'code' => 'overriddenCode', 'text' => $mainpageText,
136 'data' => $overriddenData, 'module' => 'messageWithData', $C => 'text' ],
137 $I => 'warning',
138 ],
139 ],
140 [
141 'errors' => [
142 [ 'code' => 'mainpage', 'text' => $mainpageText, 'module' => 'status', $C => 'text' ],
143 [ 'code' => 'parentheses', 'text' => $parensText, 'module' => 'status', $C => 'text' ],
144 $I => 'error',
145 ],
146 'warnings' => [
147 [ 'code' => 'mainpage', 'text' => $mainpageText, 'module' => 'status', $C => 'text' ],
148 [ 'code' => 'parentheses', 'text' => $parensText, 'module' => 'status', $C => 'text' ],
149 [ 'code' => 'overriddenCode', 'text' => $mainpageText,
150 'data' => $overriddenData, 'module' => 'status', $C => 'text' ],
151 $I => 'warning',
152 ],
153 ],
154 ],
155 [ 'plaintext' ] + $tmp, // For these messages, plaintext and wikitext are the same
156 [ 'html', 'en', true,
157 [
158 'errors' => [
159 [ 'code' => 'mainpage', 'html' => $mainpageHTML, 'module' => 'err', $C => 'html' ],
160 $I => 'error',
161 ],
162 'warnings' => [
163 [ 'code' => 'mainpage', 'html' => $mainpageHTML, 'module' => 'string', $C => 'html' ],
164 $I => 'warning',
165 ],
166 ],
167 [
168 'errors' => [
169 [ 'code' => 'overriddenCode', 'html' => $mainpageHTML,
170 'data' => $overriddenData, 'module' => 'errWithData', $C => 'html' ],
171 $I => 'error',
172 ],
173 'warnings' => [
174 [ 'code' => 'mainpage', 'html' => $mainpageHTML, 'module' => 'foo', $C => 'html' ],
175 [ 'code' => 'parentheses', 'html' => $parensHTML, 'module' => 'foo', $C => 'html' ],
176 [ 'code' => 'mainpage', 'html' => $mainpageHTML, 'module' => 'message', $C => 'html' ],
177 [ 'code' => 'overriddenCode', 'html' => $mainpageHTML,
178 'data' => $overriddenData, 'module' => 'messageWithData', $C => 'html' ],
179 $I => 'warning',
180 ],
181 ],
182 [
183 'errors' => [
184 [ 'code' => 'mainpage', 'html' => $mainpageHTML, 'module' => 'status', $C => 'html' ],
185 [ 'code' => 'parentheses', 'html' => $parensHTML, 'module' => 'status', $C => 'html' ],
186 $I => 'error',
187 ],
188 'warnings' => [
189 [ 'code' => 'mainpage', 'html' => $mainpageHTML, 'module' => 'status', $C => 'html' ],
190 [ 'code' => 'parentheses', 'html' => $parensHTML, 'module' => 'status', $C => 'html' ],
191 [ 'code' => 'overriddenCode', 'html' => $mainpageHTML,
192 'data' => $overriddenData, 'module' => 'status', $C => 'html' ],
193 $I => 'warning',
194 ],
195 ],
196 ],
197 [ 'raw', 'fr', true,
198 [
199 'errors' => [
200 [
201 'code' => 'mainpage',
202 'key' => 'mainpage',
203 'params' => [ $I => 'param' ],
204 'module' => 'err',
205 ],
206 $I => 'error',
207 ],
208 'warnings' => [
209 [
210 'code' => 'mainpage',
211 'key' => 'mainpage',
212 'params' => [ $I => 'param' ],
213 'module' => 'string',
214 ],
215 $I => 'warning',
216 ],
217 ],
218 [
219 'errors' => [
220 [
221 'code' => 'overriddenCode',
222 'key' => 'mainpage',
223 'params' => [ $I => 'param' ],
224 'data' => $overriddenData,
225 'module' => 'errWithData',
226 ],
227 $I => 'error',
228 ],
229 'warnings' => [
230 [
231 'code' => 'mainpage',
232 'key' => 'mainpage',
233 'params' => [ $I => 'param' ],
234 'module' => 'foo',
235 ],
236 [
237 'code' => 'parentheses',
238 'key' => 'parentheses',
239 'params' => [ 'foobar', $I => 'param' ],
240 'module' => 'foo',
241 ],
242 [
243 'code' => 'mainpage',
244 'key' => 'mainpage',
245 'params' => [ $I => 'param' ],
246 'module' => 'message',
247 ],
248 [
249 'code' => 'overriddenCode',
250 'key' => 'mainpage',
251 'params' => [ $I => 'param' ],
252 'data' => $overriddenData,
253 'module' => 'messageWithData',
254 ],
255 $I => 'warning',
256 ],
257 ],
258 [
259 'errors' => [
260 [
261 'code' => 'mainpage',
262 'key' => 'mainpage',
263 'params' => [ $I => 'param' ],
264 'module' => 'status',
265 ],
266 [
267 'code' => 'parentheses',
268 'key' => 'parentheses',
269 'params' => [ 'foobar', $I => 'param' ],
270 'module' => 'status',
271 ],
272 $I => 'error',
273 ],
274 'warnings' => [
275 [
276 'code' => 'mainpage',
277 'key' => 'mainpage',
278 'params' => [ $I => 'param' ],
279 'module' => 'status',
280 ],
281 [
282 'code' => 'parentheses',
283 'key' => 'parentheses',
284 'params' => [ 'foobar', $I => 'param' ],
285 'module' => 'status',
286 ],
287 [
288 'code' => 'overriddenCode',
289 'key' => 'mainpage',
290 'params' => [ $I => 'param' ],
291 'data' => $overriddenData,
292 'module' => 'status',
293 ],
294 $I => 'warning',
295 ],
296 ],
297 ],
298 [ 'none', 'fr', true,
299 [
300 'errors' => [
301 [ 'code' => 'mainpage', 'module' => 'err' ],
302 $I => 'error',
303 ],
304 'warnings' => [
305 [ 'code' => 'mainpage', 'module' => 'string' ],
306 $I => 'warning',
307 ],
308 ],
309 [
310 'errors' => [
311 [ 'code' => 'overriddenCode', 'data' => $overriddenData,
312 'module' => 'errWithData' ],
313 $I => 'error',
314 ],
315 'warnings' => [
316 [ 'code' => 'mainpage', 'module' => 'foo' ],
317 [ 'code' => 'parentheses', 'module' => 'foo' ],
318 [ 'code' => 'mainpage', 'module' => 'message' ],
319 [ 'code' => 'overriddenCode', 'data' => $overriddenData,
320 'module' => 'messageWithData' ],
321 $I => 'warning',
322 ],
323 ],
324 [
325 'errors' => [
326 [ 'code' => 'mainpage', 'module' => 'status' ],
327 [ 'code' => 'parentheses', 'module' => 'status' ],
328 $I => 'error',
329 ],
330 'warnings' => [
331 [ 'code' => 'mainpage', 'module' => 'status' ],
332 [ 'code' => 'parentheses', 'module' => 'status' ],
333 [ 'code' => 'overriddenCode', 'data' => $overriddenData, 'module' => 'status' ],
334 $I => 'warning',
335 ],
336 ],
337 ],
338 ];
339 }
340
341 /**
342 * @covers ApiErrorFormatter_BackCompat
343 */
344 public function testErrorFormatterBC() {
345 $mainpagePlain = wfMessage( 'mainpage' )->useDatabase( false )->plain();
346 $parensPlain = wfMessage( 'parentheses', 'foobar' )->useDatabase( false )->plain();
347
348 $result = new ApiResult( 8388608 );
349 $formatter = new ApiErrorFormatter_BackCompat( $result );
350
351 $this->assertSame( 'en', $formatter->getLanguage()->getCode() );
352
353 $this->assertSame( [], $formatter->arrayFromStatus( Status::newGood() ) );
354
355 $formatter->addWarning( 'string', 'mainpage' );
356 $formatter->addWarning( 'raw',
357 new RawMessage( 'Blah <kbd>kbd</kbd> <b>&lt;X&gt;</b> &#x1f61e;' )
358 );
359 $formatter->addError( 'err', 'mainpage' );
360 $this->assertSame( [
361 'error' => [
362 'code' => 'mainpage',
363 'info' => $mainpagePlain,
364 ],
365 'warnings' => [
366 'raw' => [
367 'warnings' => 'Blah "kbd" <X> 😞',
368 ApiResult::META_CONTENT => 'warnings',
369 ],
370 'string' => [
371 'warnings' => $mainpagePlain,
372 ApiResult::META_CONTENT => 'warnings',
373 ],
374 ],
375 ApiResult::META_TYPE => 'assoc',
376 ], $result->getResultData(), 'Simple test' );
377
378 $result->reset();
379 $formatter->addWarning( 'foo', 'mainpage' );
380 $formatter->addWarning( 'foo', 'mainpage' );
381 $formatter->addWarning( 'xxx+foo', [ 'parentheses', 'foobar' ] );
382 $msg1 = wfMessage( 'mainpage' );
383 $formatter->addWarning( 'message', $msg1 );
384 $msg2 = new ApiMessage( 'mainpage', 'overriddenCode', [ 'overriddenData' => true ] );
385 $formatter->addWarning( 'messageWithData', $msg2 );
386 $formatter->addError( 'errWithData', $msg2 );
387 $formatter->addWarning( null, 'mainpage' );
388 $this->assertSame( [
389 'error' => [
390 'code' => 'overriddenCode',
391 'info' => $mainpagePlain,
392 'overriddenData' => true,
393 ],
394 'warnings' => [
395 'unknown' => [
396 'warnings' => $mainpagePlain,
397 ApiResult::META_CONTENT => 'warnings',
398 ],
399 'messageWithData' => [
400 'warnings' => $mainpagePlain,
401 ApiResult::META_CONTENT => 'warnings',
402 ],
403 'message' => [
404 'warnings' => $mainpagePlain,
405 ApiResult::META_CONTENT => 'warnings',
406 ],
407 'foo' => [
408 'warnings' => "$mainpagePlain\n$parensPlain",
409 ApiResult::META_CONTENT => 'warnings',
410 ],
411 ],
412 ApiResult::META_TYPE => 'assoc',
413 ], $result->getResultData(), 'Complex test' );
414
415 $this->assertSame(
416 [
417 'code' => 'mainpage',
418 'info' => 'Main Page',
419 ],
420 $formatter->formatMessage( $msg1 )
421 );
422 $this->assertSame(
423 [
424 'code' => 'overriddenCode',
425 'info' => 'Main Page',
426 'overriddenData' => true,
427 ],
428 $formatter->formatMessage( $msg2 )
429 );
430
431 $result->reset();
432 $status = Status::newGood();
433 $status->warning( 'mainpage' );
434 $status->warning( 'parentheses', 'foobar' );
435 $status->warning( $msg1 );
436 $status->warning( $msg2 );
437 $status->error( 'mainpage' );
438 $status->error( 'parentheses', 'foobar' );
439 $formatter->addMessagesFromStatus( 'status', $status );
440 $this->assertSame( [
441 'error' => [
442 'code' => 'mainpage',
443 'info' => $mainpagePlain,
444 ],
445 'warnings' => [
446 'status' => [
447 'warnings' => "$mainpagePlain\n$parensPlain",
448 ApiResult::META_CONTENT => 'warnings',
449 ],
450 ],
451 ApiResult::META_TYPE => 'assoc',
452 ], $result->getResultData(), 'Status test' );
453
454 $I = ApiResult::META_INDEXED_TAG_NAME;
455 $this->assertSame(
456 [
457 [
458 'message' => 'mainpage',
459 'params' => [ $I => 'param' ],
460 'code' => 'mainpage',
461 'type' => 'error',
462 ],
463 [
464 'message' => 'parentheses',
465 'params' => [ 'foobar', $I => 'param' ],
466 'code' => 'parentheses',
467 'type' => 'error',
468 ],
469 $I => 'error',
470 ],
471 $formatter->arrayFromStatus( $status, 'error' ),
472 'arrayFromStatus test for error'
473 );
474 $this->assertSame(
475 [
476 [
477 'message' => 'mainpage',
478 'params' => [ $I => 'param' ],
479 'code' => 'mainpage',
480 'type' => 'warning',
481 ],
482 [
483 'message' => 'parentheses',
484 'params' => [ 'foobar', $I => 'param' ],
485 'code' => 'parentheses',
486 'type' => 'warning',
487 ],
488 [
489 'message' => 'mainpage',
490 'params' => [ $I => 'param' ],
491 'code' => 'mainpage',
492 'type' => 'warning',
493 ],
494 [
495 'message' => 'mainpage',
496 'params' => [ $I => 'param' ],
497 'code' => 'overriddenCode',
498 'type' => 'warning',
499 ],
500 $I => 'warning',
501 ],
502 $formatter->arrayFromStatus( $status, 'warning' ),
503 'arrayFromStatus test for warning'
504 );
505
506 $result->reset();
507 $result->addValue( null, 'error', [ 'bogus' ] );
508 $formatter->addError( 'err', 'mainpage' );
509 $this->assertSame( [
510 'error' => [
511 'code' => 'mainpage',
512 'info' => $mainpagePlain,
513 ],
514 ApiResult::META_TYPE => 'assoc',
515 ], $result->getResultData(), 'Overwrites bogus "error" value with real error' );
516 }
517
518 /**
519 * @dataProvider provideGetMessageFromException
520 * @covers ApiErrorFormatter::getMessageFromException
521 * @covers ApiErrorFormatter::formatException
522 * @param Exception $exception
523 * @param array $options
524 * @param array $expect
525 */
526 public function testGetMessageFromException( $exception, $options, $expect ) {
527 $result = new ApiResult( 8388608 );
528 $formatter = new ApiErrorFormatter( $result, Language::factory( 'en' ), 'html', false );
529
530 $msg = $formatter->getMessageFromException( $exception, $options );
531 $this->assertInstanceOf( Message::class, $msg );
532 $this->assertInstanceOf( IApiMessage::class, $msg );
533 $this->assertSame( $expect, [
534 'text' => $msg->parse(),
535 'code' => $msg->getApiCode(),
536 'data' => $msg->getApiData(),
537 ] );
538
539 $expectFormatted = $formatter->formatMessage( $msg );
540 $formatted = $formatter->formatException( $exception, $options );
541 $this->assertSame( $expectFormatted, $formatted );
542 }
543
544 /**
545 * @dataProvider provideGetMessageFromException
546 * @covers ApiErrorFormatter_BackCompat::formatException
547 * @param Exception $exception
548 * @param array $options
549 * @param array $expect
550 */
551 public function testGetMessageFromException_BC( $exception, $options, $expect ) {
552 $result = new ApiResult( 8388608 );
553 $formatter = new ApiErrorFormatter_BackCompat( $result );
554
555 $msg = $formatter->getMessageFromException( $exception, $options );
556 $this->assertInstanceOf( Message::class, $msg );
557 $this->assertInstanceOf( IApiMessage::class, $msg );
558 $this->assertSame( $expect, [
559 'text' => $msg->parse(),
560 'code' => $msg->getApiCode(),
561 'data' => $msg->getApiData(),
562 ] );
563
564 $expectFormatted = $formatter->formatMessage( $msg );
565 $formatted = $formatter->formatException( $exception, $options );
566 $this->assertSame( $expectFormatted, $formatted );
567 $formatted = $formatter->formatException( $exception, $options + [ 'bc' => true ] );
568 $this->assertSame( $expectFormatted['info'], $formatted );
569 }
570
571 public static function provideGetMessageFromException() {
572 return [
573 'Normal exception' => [
574 new RuntimeException( '<b>Something broke!</b>' ),
575 [],
576 [
577 'text' => '&#60;b&#62;Something broke!&#60;/b&#62;',
578 'code' => 'internal_api_error_RuntimeException',
579 'data' => [],
580 ]
581 ],
582 'Normal exception, wrapped' => [
583 new RuntimeException( '<b>Something broke!</b>' ),
584 [ 'wrap' => 'parentheses', 'code' => 'some-code', 'data' => [ 'foo' => 'bar', 'baz' => 42 ] ],
585 [
586 'text' => '(&#60;b&#62;Something broke!&#60;/b&#62;)',
587 'code' => 'some-code',
588 'data' => [ 'foo' => 'bar', 'baz' => 42 ],
589 ]
590 ],
591 'UsageException' => [
592 new UsageException( '<b>Something broke!</b>', 'ue-code', 0, [ 'xxx' => 'yyy', 'baz' => 23 ] ),
593 [],
594 [
595 'text' => '&#60;b&#62;Something broke!&#60;/b&#62;',
596 'code' => 'ue-code',
597 'data' => [ 'xxx' => 'yyy', 'baz' => 23 ],
598 ]
599 ],
600 'UsageException, wrapped' => [
601 new UsageException( '<b>Something broke!</b>', 'ue-code', 0, [ 'xxx' => 'yyy', 'baz' => 23 ] ),
602 [ 'wrap' => 'parentheses', 'code' => 'some-code', 'data' => [ 'foo' => 'bar', 'baz' => 42 ] ],
603 [
604 'text' => '(&#60;b&#62;Something broke!&#60;/b&#62;)',
605 'code' => 'some-code',
606 'data' => [ 'xxx' => 'yyy', 'baz' => 42, 'foo' => 'bar' ],
607 ]
608 ],
609 'LocalizedException' => [
610 new LocalizedException( [ 'returnto', '<b>FooBar</b>' ] ),
611 [],
612 [
613 'text' => 'Return to <b>FooBar</b>.',
614 'code' => 'returnto',
615 'data' => [],
616 ]
617 ],
618 'LocalizedException, wrapped' => [
619 new LocalizedException( [ 'returnto', '<b>FooBar</b>' ] ),
620 [ 'wrap' => 'parentheses', 'code' => 'some-code', 'data' => [ 'foo' => 'bar', 'baz' => 42 ] ],
621 [
622 'text' => 'Return to <b>FooBar</b>.',
623 'code' => 'some-code',
624 'data' => [ 'foo' => 'bar', 'baz' => 42 ],
625 ]
626 ],
627 ];
628 }
629
630 }