3 use Wikimedia\TestingAccessWrapper
;
8 class ApiErrorFormatterTest
extends MediaWikiLangTestCase
{
11 * @covers ApiErrorFormatter
13 public function testErrorFormatterBasics() {
14 $result = new ApiResult( 8388608 );
15 $formatter = new ApiErrorFormatter( $result, Language
::factory( 'de' ), 'wikitext', false );
16 $this->assertSame( 'de', $formatter->getLanguage()->getCode() );
18 $formatter->addMessagesFromStatus( null, Status
::newGood() );
20 [ ApiResult
::META_TYPE
=> 'assoc' ],
21 $result->getResultData()
24 $this->assertSame( [], $formatter->arrayFromStatus( Status
::newGood() ) );
26 $wrappedFormatter = TestingAccessWrapper
::newFromObject( $formatter );
28 'Blah "kbd" <X> 😊',
29 $wrappedFormatter->stripMarkup( 'Blah <kbd>kbd</kbd> <b><X></b> 😊' ),
35 * @covers ApiErrorFormatter
36 * @dataProvider provideErrorFormatter
38 public function testErrorFormatter( $format, $lang, $useDB,
39 $expect1, $expect2, $expect3
41 $result = new ApiResult( 8388608 );
42 $formatter = new ApiErrorFormatter( $result, Language
::factory( $lang ), $format, $useDB );
45 $expect1[ApiResult
::META_TYPE
] = 'assoc';
46 $expect2[ApiResult
::META_TYPE
] = 'assoc';
47 $expect3[ApiResult
::META_TYPE
] = 'assoc';
49 $formatter->addWarning( 'string', 'mainpage' );
50 $formatter->addError( 'err', 'mainpage' );
51 $this->assertEquals( $expect1, $result->getResultData(), 'Simple test' );
54 $formatter->addWarning( 'foo', 'mainpage' );
55 $formatter->addWarning( 'foo', 'mainpage' );
56 $formatter->addWarning( 'foo', [ 'parentheses', 'foobar' ] );
57 $msg1 = wfMessage( 'mainpage' );
58 $formatter->addWarning( 'message', $msg1 );
59 $msg2 = new ApiMessage( 'mainpage', 'overriddenCode', [ 'overriddenData' => true ] );
60 $formatter->addWarning( 'messageWithData', $msg2 );
61 $formatter->addError( 'errWithData', $msg2 );
62 $this->assertSame( $expect2, $result->getResultData(), 'Complex test' );
65 $this->removeModuleTag( $expect2['warnings'][2] ),
66 $formatter->formatMessage( $msg1 ),
67 'formatMessage test 1'
70 $this->removeModuleTag( $expect2['warnings'][3] ),
71 $formatter->formatMessage( $msg2 ),
72 'formatMessage test 2'
76 $status = Status
::newGood();
77 $status->warning( 'mainpage' );
78 $status->warning( 'parentheses', 'foobar' );
79 $status->warning( $msg1 );
80 $status->warning( $msg2 );
81 $status->error( 'mainpage' );
82 $status->error( 'parentheses', 'foobar' );
83 $formatter->addMessagesFromStatus( 'status', $status );
84 $this->assertSame( $expect3, $result->getResultData(), 'Status test' );
87 array_map( [ $this, 'removeModuleTag' ], $expect3['errors'] ),
88 $formatter->arrayFromStatus( $status, 'error' ),
89 'arrayFromStatus test for error'
92 array_map( [ $this, 'removeModuleTag' ], $expect3['warnings'] ),
93 $formatter->arrayFromStatus( $status, 'warning' ),
94 'arrayFromStatus test for warning'
98 private function removeModuleTag( $s ) {
99 if ( is_array( $s ) ) {
100 unset( $s['module'] );
105 public static function provideErrorFormatter() {
106 $mainpageText = wfMessage( 'mainpage' )->inLanguage( 'de' )->useDatabase( false )->text();
107 $parensText = wfMessage( 'parentheses', 'foobar' )->inLanguage( 'de' )
108 ->useDatabase( false )->text();
109 $mainpageHTML = wfMessage( 'mainpage' )->inLanguage( 'en' )->parse();
110 $parensHTML = wfMessage( 'parentheses', 'foobar' )->inLanguage( 'en' )->parse();
111 $C = ApiResult
::META_CONTENT
;
112 $I = ApiResult
::META_INDEXED_TAG_NAME
;
113 $overriddenData = [ 'overriddenData' => true, ApiResult
::META_TYPE
=> 'assoc' ];
116 $tmp = [ 'wikitext', 'de', false,
119 [ 'code' => 'mainpage', 'text' => $mainpageText, 'module' => 'err', $C => 'text' ],
123 [ 'code' => 'mainpage', 'text' => $mainpageText, 'module' => 'string', $C => 'text' ],
129 [ 'code' => 'overriddenCode', 'text' => $mainpageText,
130 'data' => $overriddenData, 'module' => 'errWithData', $C => 'text' ],
134 [ 'code' => 'mainpage', 'text' => $mainpageText, 'module' => 'foo', $C => 'text' ],
135 [ 'code' => 'parentheses', 'text' => $parensText, 'module' => 'foo', $C => 'text' ],
136 [ 'code' => 'mainpage', 'text' => $mainpageText, 'module' => 'message', $C => 'text' ],
137 [ 'code' => 'overriddenCode', 'text' => $mainpageText,
138 'data' => $overriddenData, 'module' => 'messageWithData', $C => 'text' ],
144 [ 'code' => 'mainpage', 'text' => $mainpageText, 'module' => 'status', $C => 'text' ],
145 [ 'code' => 'parentheses', 'text' => $parensText, 'module' => 'status', $C => 'text' ],
149 [ 'code' => 'mainpage', 'text' => $mainpageText, 'module' => 'status', $C => 'text' ],
150 [ 'code' => 'parentheses', 'text' => $parensText, 'module' => 'status', $C => 'text' ],
151 [ 'code' => 'overriddenCode', 'text' => $mainpageText,
152 'data' => $overriddenData, 'module' => 'status', $C => 'text' ],
157 [ 'plaintext' ] +
$tmp, // For these messages, plaintext and wikitext are the same
158 [ 'html', 'en', true,
161 [ 'code' => 'mainpage', 'html' => $mainpageHTML, 'module' => 'err', $C => 'html' ],
165 [ 'code' => 'mainpage', 'html' => $mainpageHTML, 'module' => 'string', $C => 'html' ],
171 [ 'code' => 'overriddenCode', 'html' => $mainpageHTML,
172 'data' => $overriddenData, 'module' => 'errWithData', $C => 'html' ],
176 [ 'code' => 'mainpage', 'html' => $mainpageHTML, 'module' => 'foo', $C => 'html' ],
177 [ 'code' => 'parentheses', 'html' => $parensHTML, 'module' => 'foo', $C => 'html' ],
178 [ 'code' => 'mainpage', 'html' => $mainpageHTML, 'module' => 'message', $C => 'html' ],
179 [ 'code' => 'overriddenCode', 'html' => $mainpageHTML,
180 'data' => $overriddenData, 'module' => 'messageWithData', $C => 'html' ],
186 [ 'code' => 'mainpage', 'html' => $mainpageHTML, 'module' => 'status', $C => 'html' ],
187 [ 'code' => 'parentheses', 'html' => $parensHTML, 'module' => 'status', $C => 'html' ],
191 [ 'code' => 'mainpage', 'html' => $mainpageHTML, 'module' => 'status', $C => 'html' ],
192 [ 'code' => 'parentheses', 'html' => $parensHTML, 'module' => 'status', $C => 'html' ],
193 [ 'code' => 'overriddenCode', 'html' => $mainpageHTML,
194 'data' => $overriddenData, 'module' => 'status', $C => 'html' ],
203 'code' => 'mainpage',
205 'params' => [ $I => 'param' ],
212 'code' => 'mainpage',
214 'params' => [ $I => 'param' ],
215 'module' => 'string',
223 'code' => 'overriddenCode',
225 'params' => [ $I => 'param' ],
226 'data' => $overriddenData,
227 'module' => 'errWithData',
233 'code' => 'mainpage',
235 'params' => [ $I => 'param' ],
239 'code' => 'parentheses',
240 'key' => 'parentheses',
241 'params' => [ 'foobar', $I => 'param' ],
245 'code' => 'mainpage',
247 'params' => [ $I => 'param' ],
248 'module' => 'message',
251 'code' => 'overriddenCode',
253 'params' => [ $I => 'param' ],
254 'data' => $overriddenData,
255 'module' => 'messageWithData',
263 'code' => 'mainpage',
265 'params' => [ $I => 'param' ],
266 'module' => 'status',
269 'code' => 'parentheses',
270 'key' => 'parentheses',
271 'params' => [ 'foobar', $I => 'param' ],
272 'module' => 'status',
278 'code' => 'mainpage',
280 'params' => [ $I => 'param' ],
281 'module' => 'status',
284 'code' => 'parentheses',
285 'key' => 'parentheses',
286 'params' => [ 'foobar', $I => 'param' ],
287 'module' => 'status',
290 'code' => 'overriddenCode',
292 'params' => [ $I => 'param' ],
293 'data' => $overriddenData,
294 'module' => 'status',
300 [ 'none', 'fr', true,
303 [ 'code' => 'mainpage', 'module' => 'err' ],
307 [ 'code' => 'mainpage', 'module' => 'string' ],
313 [ 'code' => 'overriddenCode', 'data' => $overriddenData,
314 'module' => 'errWithData' ],
318 [ 'code' => 'mainpage', 'module' => 'foo' ],
319 [ 'code' => 'parentheses', 'module' => 'foo' ],
320 [ 'code' => 'mainpage', 'module' => 'message' ],
321 [ 'code' => 'overriddenCode', 'data' => $overriddenData,
322 'module' => 'messageWithData' ],
328 [ 'code' => 'mainpage', 'module' => 'status' ],
329 [ 'code' => 'parentheses', 'module' => 'status' ],
333 [ 'code' => 'mainpage', 'module' => 'status' ],
334 [ 'code' => 'parentheses', 'module' => 'status' ],
335 [ 'code' => 'overriddenCode', 'data' => $overriddenData, 'module' => 'status' ],
344 * @covers ApiErrorFormatter_BackCompat
346 public function testErrorFormatterBC() {
347 $mainpagePlain = wfMessage( 'mainpage' )->useDatabase( false )->plain();
348 $parensPlain = wfMessage( 'parentheses', 'foobar' )->useDatabase( false )->plain();
350 $result = new ApiResult( 8388608 );
351 $formatter = new ApiErrorFormatter_BackCompat( $result );
353 $this->assertSame( 'en', $formatter->getLanguage()->getCode() );
355 $this->assertSame( [], $formatter->arrayFromStatus( Status
::newGood() ) );
357 $formatter->addWarning( 'string', 'mainpage' );
358 $formatter->addWarning( 'raw',
359 new RawMessage( 'Blah <kbd>kbd</kbd> <b><X></b> 😞' )
361 $formatter->addError( 'err', 'mainpage' );
364 'code' => 'mainpage',
365 'info' => $mainpagePlain,
369 'warnings' => 'Blah "kbd" <X> 😞',
370 ApiResult
::META_CONTENT
=> 'warnings',
373 'warnings' => $mainpagePlain,
374 ApiResult
::META_CONTENT
=> 'warnings',
377 ApiResult
::META_TYPE
=> 'assoc',
378 ], $result->getResultData(), 'Simple test' );
381 $formatter->addWarning( 'foo', 'mainpage' );
382 $formatter->addWarning( 'foo', 'mainpage' );
383 $formatter->addWarning( 'xxx+foo', [ 'parentheses', 'foobar' ] );
384 $msg1 = wfMessage( 'mainpage' );
385 $formatter->addWarning( 'message', $msg1 );
386 $msg2 = new ApiMessage( 'mainpage', 'overriddenCode', [ 'overriddenData' => true ] );
387 $formatter->addWarning( 'messageWithData', $msg2 );
388 $formatter->addError( 'errWithData', $msg2 );
389 $formatter->addWarning( null, 'mainpage' );
392 'code' => 'overriddenCode',
393 'info' => $mainpagePlain,
394 'overriddenData' => true,
398 'warnings' => $mainpagePlain,
399 ApiResult
::META_CONTENT
=> 'warnings',
401 'messageWithData' => [
402 'warnings' => $mainpagePlain,
403 ApiResult
::META_CONTENT
=> 'warnings',
406 'warnings' => $mainpagePlain,
407 ApiResult
::META_CONTENT
=> 'warnings',
410 'warnings' => "$mainpagePlain\n$parensPlain",
411 ApiResult
::META_CONTENT
=> 'warnings',
414 ApiResult
::META_TYPE
=> 'assoc',
415 ], $result->getResultData(), 'Complex test' );
419 'code' => 'mainpage',
420 'info' => 'Main Page',
422 $formatter->formatMessage( $msg1 )
426 'code' => 'overriddenCode',
427 'info' => 'Main Page',
428 'overriddenData' => true,
430 $formatter->formatMessage( $msg2 )
434 $status = Status
::newGood();
435 $status->warning( 'mainpage' );
436 $status->warning( 'parentheses', 'foobar' );
437 $status->warning( $msg1 );
438 $status->warning( $msg2 );
439 $status->error( 'mainpage' );
440 $status->error( 'parentheses', 'foobar' );
441 $formatter->addMessagesFromStatus( 'status', $status );
444 'code' => 'mainpage',
445 'info' => $mainpagePlain,
449 'warnings' => "$mainpagePlain\n$parensPlain",
450 ApiResult
::META_CONTENT
=> 'warnings',
453 ApiResult
::META_TYPE
=> 'assoc',
454 ], $result->getResultData(), 'Status test' );
456 $I = ApiResult
::META_INDEXED_TAG_NAME
;
460 'message' => 'mainpage',
461 'params' => [ $I => 'param' ],
462 'code' => 'mainpage',
466 'message' => 'parentheses',
467 'params' => [ 'foobar', $I => 'param' ],
468 'code' => 'parentheses',
473 $formatter->arrayFromStatus( $status, 'error' ),
474 'arrayFromStatus test for error'
479 'message' => 'mainpage',
480 'params' => [ $I => 'param' ],
481 'code' => 'mainpage',
485 'message' => 'parentheses',
486 'params' => [ 'foobar', $I => 'param' ],
487 'code' => 'parentheses',
491 'message' => 'mainpage',
492 'params' => [ $I => 'param' ],
493 'code' => 'mainpage',
497 'message' => 'mainpage',
498 'params' => [ $I => 'param' ],
499 'code' => 'overriddenCode',
504 $formatter->arrayFromStatus( $status, 'warning' ),
505 'arrayFromStatus test for warning'
509 $result->addValue( null, 'error', [ 'bogus' ] );
510 $formatter->addError( 'err', 'mainpage' );
513 'code' => 'mainpage',
514 'info' => $mainpagePlain,
516 ApiResult
::META_TYPE
=> 'assoc',
517 ], $result->getResultData(), 'Overwrites bogus "error" value with real error' );
521 * @dataProvider provideGetMessageFromException
522 * @covers ApiErrorFormatter::getMessageFromException
523 * @covers ApiErrorFormatter::formatException
524 * @param Exception $exception
525 * @param array $options
526 * @param array $expect
528 public function testGetMessageFromException( $exception, $options, $expect ) {
529 if ( $exception instanceof UsageException
) {
530 $this->hideDeprecated( 'UsageException::getMessageArray' );
533 $result = new ApiResult( 8388608 );
534 $formatter = new ApiErrorFormatter( $result, Language
::factory( 'en' ), 'html', false );
536 $msg = $formatter->getMessageFromException( $exception, $options );
537 $this->assertInstanceOf( Message
::class, $msg );
538 $this->assertInstanceOf( IApiMessage
::class, $msg );
539 $this->assertSame( $expect, [
540 'text' => $msg->parse(),
541 'code' => $msg->getApiCode(),
542 'data' => $msg->getApiData(),
545 $expectFormatted = $formatter->formatMessage( $msg );
546 $formatted = $formatter->formatException( $exception, $options );
547 $this->assertSame( $expectFormatted, $formatted );
551 * @dataProvider provideGetMessageFromException
552 * @covers ApiErrorFormatter_BackCompat::formatException
553 * @param Exception $exception
554 * @param array $options
555 * @param array $expect
557 public function testGetMessageFromException_BC( $exception, $options, $expect ) {
558 $result = new ApiResult( 8388608 );
559 $formatter = new ApiErrorFormatter_BackCompat( $result );
561 $msg = $formatter->getMessageFromException( $exception, $options );
562 $this->assertInstanceOf( Message
::class, $msg );
563 $this->assertInstanceOf( IApiMessage
::class, $msg );
564 $this->assertSame( $expect, [
565 'text' => $msg->parse(),
566 'code' => $msg->getApiCode(),
567 'data' => $msg->getApiData(),
570 $expectFormatted = $formatter->formatMessage( $msg );
571 $formatted = $formatter->formatException( $exception, $options );
572 $this->assertSame( $expectFormatted, $formatted );
573 $formatted = $formatter->formatException( $exception, $options +
[ 'bc' => true ] );
574 $this->assertSame( $expectFormatted['info'], $formatted );
577 public static function provideGetMessageFromException() {
578 MediaWiki\
suppressWarnings();
579 $usageException = new UsageException(
580 '<b>Something broke!</b>', 'ue-code', 0, [ 'xxx' => 'yyy', 'baz' => 23 ]
582 MediaWiki\restoreWarnings
();
585 'Normal exception' => [
586 new RuntimeException( '<b>Something broke!</b>' ),
589 'text' => '<b>Something broke!</b>',
590 'code' => 'internal_api_error_RuntimeException',
594 'Normal exception, wrapped' => [
595 new RuntimeException( '<b>Something broke!</b>' ),
596 [ 'wrap' => 'parentheses', 'code' => 'some-code', 'data' => [ 'foo' => 'bar', 'baz' => 42 ] ],
598 'text' => '(<b>Something broke!</b>)',
599 'code' => 'some-code',
600 'data' => [ 'foo' => 'bar', 'baz' => 42 ],
603 'UsageException' => [
607 'text' => '<b>Something broke!</b>',
609 'data' => [ 'xxx' => 'yyy', 'baz' => 23 ],
612 'UsageException, wrapped' => [
614 [ 'wrap' => 'parentheses', 'code' => 'some-code', 'data' => [ 'foo' => 'bar', 'baz' => 42 ] ],
616 'text' => '(<b>Something broke!</b>)',
617 'code' => 'some-code',
618 'data' => [ 'xxx' => 'yyy', 'baz' => 42, 'foo' => 'bar' ],
621 'LocalizedException' => [
622 new LocalizedException( [ 'returnto', '<b>FooBar</b>' ] ),
625 'text' => 'Return to <b>FooBar</b>.',
626 'code' => 'returnto',
630 'LocalizedException, wrapped' => [
631 new LocalizedException( [ 'returnto', '<b>FooBar</b>' ] ),
632 [ 'wrap' => 'parentheses', 'code' => 'some-code', 'data' => [ 'foo' => 'bar', 'baz' => 42 ] ],
634 'text' => 'Return to <b>FooBar</b>.',
635 'code' => 'some-code',
636 'data' => [ 'foo' => 'bar', 'baz' => 42 ],