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() );
17 $this->assertSame( 'wikitext', $formatter->getFormat() );
19 $formatter->addMessagesFromStatus( null, Status
::newGood() );
21 [ ApiResult
::META_TYPE
=> 'assoc' ],
22 $result->getResultData()
25 $this->assertSame( [], $formatter->arrayFromStatus( Status
::newGood() ) );
27 $wrappedFormatter = TestingAccessWrapper
::newFromObject( $formatter );
29 'Blah "kbd" <X> 😊',
30 $wrappedFormatter->stripMarkup( 'Blah <kbd>kbd</kbd> <b><X></b> 😊' ),
36 * @covers ApiErrorFormatter
37 * @covers ApiErrorFormatter_BackCompat
39 public function testNewWithFormat() {
40 $result = new ApiResult( 8388608 );
41 $formatter = new ApiErrorFormatter( $result, Language
::factory( 'de' ), 'wikitext', false );
42 $formatter2 = $formatter->newWithFormat( 'html' );
44 $this->assertSame( $formatter->getLanguage(), $formatter2->getLanguage() );
45 $this->assertSame( 'html', $formatter2->getFormat() );
47 $formatter3 = new ApiErrorFormatter_BackCompat( $result );
48 $formatter4 = $formatter3->newWithFormat( 'html' );
49 $this->assertNotInstanceOf( ApiErrorFormatter_BackCompat
::class, $formatter4 );
50 $this->assertSame( $formatter3->getLanguage(), $formatter4->getLanguage() );
51 $this->assertSame( 'html', $formatter4->getFormat() );
55 * @covers ApiErrorFormatter
56 * @dataProvider provideErrorFormatter
58 public function testErrorFormatter( $format, $lang, $useDB,
59 $expect1, $expect2, $expect3
61 $result = new ApiResult( 8388608 );
62 $formatter = new ApiErrorFormatter( $result, Language
::factory( $lang ), $format, $useDB );
65 $expect1[ApiResult
::META_TYPE
] = 'assoc';
66 $expect2[ApiResult
::META_TYPE
] = 'assoc';
67 $expect3[ApiResult
::META_TYPE
] = 'assoc';
69 $formatter->addWarning( 'string', 'mainpage' );
70 $formatter->addError( 'err', 'mainpage' );
71 $this->assertEquals( $expect1, $result->getResultData(), 'Simple test' );
74 $formatter->addWarning( 'foo', 'mainpage' );
75 $formatter->addWarning( 'foo', 'mainpage' );
76 $formatter->addWarning( 'foo', [ 'parentheses', 'foobar' ] );
77 $msg1 = wfMessage( 'mainpage' );
78 $formatter->addWarning( 'message', $msg1 );
79 $msg2 = new ApiMessage( 'mainpage', 'overriddenCode', [ 'overriddenData' => true ] );
80 $formatter->addWarning( 'messageWithData', $msg2 );
81 $formatter->addError( 'errWithData', $msg2 );
82 $this->assertSame( $expect2, $result->getResultData(), 'Complex test' );
85 $this->removeModuleTag( $expect2['warnings'][2] ),
86 $formatter->formatMessage( $msg1 ),
87 'formatMessage test 1'
90 $this->removeModuleTag( $expect2['warnings'][3] ),
91 $formatter->formatMessage( $msg2 ),
92 'formatMessage test 2'
96 $status = Status
::newGood();
97 $status->warning( 'mainpage' );
98 $status->warning( 'parentheses', 'foobar' );
99 $status->warning( $msg1 );
100 $status->warning( $msg2 );
101 $status->error( 'mainpage' );
102 $status->error( 'parentheses', 'foobar' );
103 $formatter->addMessagesFromStatus( 'status', $status );
104 $this->assertSame( $expect3, $result->getResultData(), 'Status test' );
107 array_map( [ $this, 'removeModuleTag' ], $expect3['errors'] ),
108 $formatter->arrayFromStatus( $status, 'error' ),
109 'arrayFromStatus test for error'
112 array_map( [ $this, 'removeModuleTag' ], $expect3['warnings'] ),
113 $formatter->arrayFromStatus( $status, 'warning' ),
114 'arrayFromStatus test for warning'
118 private function removeModuleTag( $s ) {
119 if ( is_array( $s ) ) {
120 unset( $s['module'] );
125 public static function provideErrorFormatter() {
126 $mainpageText = wfMessage( 'mainpage' )->inLanguage( 'de' )->useDatabase( false )->text();
127 $parensText = wfMessage( 'parentheses', 'foobar' )->inLanguage( 'de' )
128 ->useDatabase( false )->text();
129 $mainpageHTML = wfMessage( 'mainpage' )->inLanguage( 'en' )->parse();
130 $parensHTML = wfMessage( 'parentheses', 'foobar' )->inLanguage( 'en' )->parse();
131 $C = ApiResult
::META_CONTENT
;
132 $I = ApiResult
::META_INDEXED_TAG_NAME
;
133 $overriddenData = [ 'overriddenData' => true, ApiResult
::META_TYPE
=> 'assoc' ];
136 $tmp = [ 'wikitext', 'de', false,
139 [ 'code' => 'mainpage', 'text' => $mainpageText, 'module' => 'err', $C => 'text' ],
143 [ 'code' => 'mainpage', 'text' => $mainpageText, 'module' => 'string', $C => 'text' ],
149 [ 'code' => 'overriddenCode', 'text' => $mainpageText,
150 'data' => $overriddenData, 'module' => 'errWithData', $C => 'text' ],
154 [ 'code' => 'mainpage', 'text' => $mainpageText, 'module' => 'foo', $C => 'text' ],
155 [ 'code' => 'parentheses', 'text' => $parensText, 'module' => 'foo', $C => 'text' ],
156 [ 'code' => 'mainpage', 'text' => $mainpageText, 'module' => 'message', $C => 'text' ],
157 [ 'code' => 'overriddenCode', 'text' => $mainpageText,
158 'data' => $overriddenData, 'module' => 'messageWithData', $C => 'text' ],
164 [ 'code' => 'mainpage', 'text' => $mainpageText, 'module' => 'status', $C => 'text' ],
165 [ 'code' => 'parentheses', 'text' => $parensText, 'module' => 'status', $C => 'text' ],
169 [ 'code' => 'mainpage', 'text' => $mainpageText, 'module' => 'status', $C => 'text' ],
170 [ 'code' => 'parentheses', 'text' => $parensText, 'module' => 'status', $C => 'text' ],
171 [ 'code' => 'overriddenCode', 'text' => $mainpageText,
172 'data' => $overriddenData, 'module' => 'status', $C => 'text' ],
177 [ 'plaintext' ] +
$tmp, // For these messages, plaintext and wikitext are the same
178 [ 'html', 'en', true,
181 [ 'code' => 'mainpage', 'html' => $mainpageHTML, 'module' => 'err', $C => 'html' ],
185 [ 'code' => 'mainpage', 'html' => $mainpageHTML, 'module' => 'string', $C => 'html' ],
191 [ 'code' => 'overriddenCode', 'html' => $mainpageHTML,
192 'data' => $overriddenData, 'module' => 'errWithData', $C => 'html' ],
196 [ 'code' => 'mainpage', 'html' => $mainpageHTML, 'module' => 'foo', $C => 'html' ],
197 [ 'code' => 'parentheses', 'html' => $parensHTML, 'module' => 'foo', $C => 'html' ],
198 [ 'code' => 'mainpage', 'html' => $mainpageHTML, 'module' => 'message', $C => 'html' ],
199 [ 'code' => 'overriddenCode', 'html' => $mainpageHTML,
200 'data' => $overriddenData, 'module' => 'messageWithData', $C => 'html' ],
206 [ 'code' => 'mainpage', 'html' => $mainpageHTML, 'module' => 'status', $C => 'html' ],
207 [ 'code' => 'parentheses', 'html' => $parensHTML, 'module' => 'status', $C => 'html' ],
211 [ 'code' => 'mainpage', 'html' => $mainpageHTML, 'module' => 'status', $C => 'html' ],
212 [ 'code' => 'parentheses', 'html' => $parensHTML, 'module' => 'status', $C => 'html' ],
213 [ 'code' => 'overriddenCode', 'html' => $mainpageHTML,
214 'data' => $overriddenData, 'module' => 'status', $C => 'html' ],
223 'code' => 'mainpage',
225 'params' => [ $I => 'param' ],
232 'code' => 'mainpage',
234 'params' => [ $I => 'param' ],
235 'module' => 'string',
243 'code' => 'overriddenCode',
245 'params' => [ $I => 'param' ],
246 'data' => $overriddenData,
247 'module' => 'errWithData',
253 'code' => 'mainpage',
255 'params' => [ $I => 'param' ],
259 'code' => 'parentheses',
260 'key' => 'parentheses',
261 'params' => [ 'foobar', $I => 'param' ],
265 'code' => 'mainpage',
267 'params' => [ $I => 'param' ],
268 'module' => 'message',
271 'code' => 'overriddenCode',
273 'params' => [ $I => 'param' ],
274 'data' => $overriddenData,
275 'module' => 'messageWithData',
283 'code' => 'mainpage',
285 'params' => [ $I => 'param' ],
286 'module' => 'status',
289 'code' => 'parentheses',
290 'key' => 'parentheses',
291 'params' => [ 'foobar', $I => 'param' ],
292 'module' => 'status',
298 'code' => 'mainpage',
300 'params' => [ $I => 'param' ],
301 'module' => 'status',
304 'code' => 'parentheses',
305 'key' => 'parentheses',
306 'params' => [ 'foobar', $I => 'param' ],
307 'module' => 'status',
310 'code' => 'overriddenCode',
312 'params' => [ $I => 'param' ],
313 'data' => $overriddenData,
314 'module' => 'status',
320 [ 'none', 'fr', true,
323 [ 'code' => 'mainpage', 'module' => 'err' ],
327 [ 'code' => 'mainpage', 'module' => 'string' ],
333 [ 'code' => 'overriddenCode', 'data' => $overriddenData,
334 'module' => 'errWithData' ],
338 [ 'code' => 'mainpage', 'module' => 'foo' ],
339 [ 'code' => 'parentheses', 'module' => 'foo' ],
340 [ 'code' => 'mainpage', 'module' => 'message' ],
341 [ 'code' => 'overriddenCode', 'data' => $overriddenData,
342 'module' => 'messageWithData' ],
348 [ 'code' => 'mainpage', 'module' => 'status' ],
349 [ 'code' => 'parentheses', 'module' => 'status' ],
353 [ 'code' => 'mainpage', 'module' => 'status' ],
354 [ 'code' => 'parentheses', 'module' => 'status' ],
355 [ 'code' => 'overriddenCode', 'data' => $overriddenData, 'module' => 'status' ],
364 * @covers ApiErrorFormatter_BackCompat
366 public function testErrorFormatterBC() {
367 $mainpagePlain = wfMessage( 'mainpage' )->useDatabase( false )->plain();
368 $parensPlain = wfMessage( 'parentheses', 'foobar' )->useDatabase( false )->plain();
370 $result = new ApiResult( 8388608 );
371 $formatter = new ApiErrorFormatter_BackCompat( $result );
373 $this->assertSame( 'en', $formatter->getLanguage()->getCode() );
374 $this->assertSame( 'bc', $formatter->getFormat() );
376 $this->assertSame( [], $formatter->arrayFromStatus( Status
::newGood() ) );
378 $formatter->addWarning( 'string', 'mainpage' );
379 $formatter->addWarning( 'raw',
380 new RawMessage( 'Blah <kbd>kbd</kbd> <b><X></b> 😞' )
382 $formatter->addError( 'err', 'mainpage' );
385 'code' => 'mainpage',
386 'info' => $mainpagePlain,
390 'warnings' => 'Blah "kbd" <X> 😞',
391 ApiResult
::META_CONTENT
=> 'warnings',
394 'warnings' => $mainpagePlain,
395 ApiResult
::META_CONTENT
=> 'warnings',
398 ApiResult
::META_TYPE
=> 'assoc',
399 ], $result->getResultData(), 'Simple test' );
402 $formatter->addWarning( 'foo', 'mainpage' );
403 $formatter->addWarning( 'foo', 'mainpage' );
404 $formatter->addWarning( 'xxx+foo', [ 'parentheses', 'foobar' ] );
405 $msg1 = wfMessage( 'mainpage' );
406 $formatter->addWarning( 'message', $msg1 );
407 $msg2 = new ApiMessage( 'mainpage', 'overriddenCode', [ 'overriddenData' => true ] );
408 $formatter->addWarning( 'messageWithData', $msg2 );
409 $formatter->addError( 'errWithData', $msg2 );
410 $formatter->addWarning( null, 'mainpage' );
413 'code' => 'overriddenCode',
414 'info' => $mainpagePlain,
415 'overriddenData' => true,
419 'warnings' => $mainpagePlain,
420 ApiResult
::META_CONTENT
=> 'warnings',
422 'messageWithData' => [
423 'warnings' => $mainpagePlain,
424 ApiResult
::META_CONTENT
=> 'warnings',
427 'warnings' => $mainpagePlain,
428 ApiResult
::META_CONTENT
=> 'warnings',
431 'warnings' => "$mainpagePlain\n$parensPlain",
432 ApiResult
::META_CONTENT
=> 'warnings',
435 ApiResult
::META_TYPE
=> 'assoc',
436 ], $result->getResultData(), 'Complex test' );
440 'code' => 'mainpage',
441 'info' => 'Main Page',
443 $formatter->formatMessage( $msg1 )
447 'code' => 'overriddenCode',
448 'info' => 'Main Page',
449 'overriddenData' => true,
451 $formatter->formatMessage( $msg2 )
455 $status = Status
::newGood();
456 $status->warning( 'mainpage' );
457 $status->warning( 'parentheses', 'foobar' );
458 $status->warning( $msg1 );
459 $status->warning( $msg2 );
460 $status->error( 'mainpage' );
461 $status->error( 'parentheses', 'foobar' );
462 $formatter->addMessagesFromStatus( 'status', $status );
465 'code' => 'mainpage',
466 'info' => $mainpagePlain,
470 'warnings' => "$mainpagePlain\n$parensPlain",
471 ApiResult
::META_CONTENT
=> 'warnings',
474 ApiResult
::META_TYPE
=> 'assoc',
475 ], $result->getResultData(), 'Status test' );
477 $I = ApiResult
::META_INDEXED_TAG_NAME
;
481 'message' => 'mainpage',
482 'params' => [ $I => 'param' ],
483 'code' => 'mainpage',
487 'message' => 'parentheses',
488 'params' => [ 'foobar', $I => 'param' ],
489 'code' => 'parentheses',
494 $formatter->arrayFromStatus( $status, 'error' ),
495 'arrayFromStatus test for error'
500 'message' => 'mainpage',
501 'params' => [ $I => 'param' ],
502 'code' => 'mainpage',
506 'message' => 'parentheses',
507 'params' => [ 'foobar', $I => 'param' ],
508 'code' => 'parentheses',
512 'message' => 'mainpage',
513 'params' => [ $I => 'param' ],
514 'code' => 'mainpage',
518 'message' => 'mainpage',
519 'params' => [ $I => 'param' ],
520 'code' => 'overriddenCode',
525 $formatter->arrayFromStatus( $status, 'warning' ),
526 'arrayFromStatus test for warning'
530 $result->addValue( null, 'error', [ 'bogus' ] );
531 $formatter->addError( 'err', 'mainpage' );
534 'code' => 'mainpage',
535 'info' => $mainpagePlain,
537 ApiResult
::META_TYPE
=> 'assoc',
538 ], $result->getResultData(), 'Overwrites bogus "error" value with real error' );
542 * @dataProvider provideGetMessageFromException
543 * @covers ApiErrorFormatter::getMessageFromException
544 * @covers ApiErrorFormatter::formatException
545 * @param Exception $exception
546 * @param array $options
547 * @param array $expect
549 public function testGetMessageFromException( $exception, $options, $expect ) {
550 $result = new ApiResult( 8388608 );
551 $formatter = new ApiErrorFormatter( $result, Language
::factory( 'en' ), 'html', false );
553 $msg = $formatter->getMessageFromException( $exception, $options );
554 $this->assertInstanceOf( Message
::class, $msg );
555 $this->assertInstanceOf( IApiMessage
::class, $msg );
556 $this->assertSame( $expect, [
557 'text' => $msg->parse(),
558 'code' => $msg->getApiCode(),
559 'data' => $msg->getApiData(),
562 $expectFormatted = $formatter->formatMessage( $msg );
563 $formatted = $formatter->formatException( $exception, $options );
564 $this->assertSame( $expectFormatted, $formatted );
568 * @dataProvider provideGetMessageFromException
569 * @covers ApiErrorFormatter_BackCompat::formatException
570 * @param Exception $exception
571 * @param array $options
572 * @param array $expect
574 public function testGetMessageFromException_BC( $exception, $options, $expect ) {
575 $result = new ApiResult( 8388608 );
576 $formatter = new ApiErrorFormatter_BackCompat( $result );
578 $msg = $formatter->getMessageFromException( $exception, $options );
579 $this->assertInstanceOf( Message
::class, $msg );
580 $this->assertInstanceOf( IApiMessage
::class, $msg );
581 $this->assertSame( $expect, [
582 'text' => $msg->parse(),
583 'code' => $msg->getApiCode(),
584 'data' => $msg->getApiData(),
587 $expectFormatted = $formatter->formatMessage( $msg );
588 $formatted = $formatter->formatException( $exception, $options );
589 $this->assertSame( $expectFormatted, $formatted );
590 $formatted = $formatter->formatException( $exception, $options +
[ 'bc' => true ] );
591 $this->assertSame( $expectFormatted['info'], $formatted );
594 public static function provideGetMessageFromException() {
596 'Normal exception' => [
597 new RuntimeException( '<b>Something broke!</b>' ),
600 'text' => '<b>Something broke!</b>',
601 'code' => 'internal_api_error_RuntimeException',
605 'Normal exception, wrapped' => [
606 new RuntimeException( '<b>Something broke!</b>' ),
607 [ 'wrap' => 'parentheses', 'code' => 'some-code', 'data' => [ 'foo' => 'bar', 'baz' => 42 ] ],
609 'text' => '(<b>Something broke!</b>)',
610 'code' => 'some-code',
611 'data' => [ 'foo' => 'bar', 'baz' => 42 ],
614 'LocalizedException' => [
615 new LocalizedException( [ 'returnto', '<b>FooBar</b>' ] ),
618 'text' => 'Return to <b>FooBar</b>.',
619 'code' => 'returnto',
623 'LocalizedException, wrapped' => [
624 new LocalizedException( [ 'returnto', '<b>FooBar</b>' ] ),
625 [ 'wrap' => 'parentheses', 'code' => 'some-code', 'data' => [ 'foo' => 'bar', 'baz' => 42 ] ],
627 'text' => 'Return to <b>FooBar</b>.',
628 'code' => 'some-code',
629 'data' => [ 'foo' => 'bar', 'baz' => 42 ],