| Current File : /home/jvzmxxx/wiki1/extensions/Wikibase/repo/tests/phpunit/includes/Api/ApiErrorReporterTest.php |
<?php
namespace Wikibase\Test\Repo\Api;
use ApiMain;
use DataValues\IllegalValueException;
use Language;
use Status;
use UsageException;
use ValueParsers\ParseException;
use Wikibase\Repo\Api\ApiErrorReporter;
use Wikibase\Repo\Localizer\DispatchingExceptionLocalizer;
use Wikibase\Repo\Localizer\ExceptionLocalizer;
use Wikibase\Repo\Localizer\ParseExceptionLocalizer;
/**
* @covers Wikibase\Repo\Api\ApiErrorReporter
*
* @group Wikibase
* @group WikibaseValidators
* @group WikibaseAPI
* @group Database
*
* @license GPL-2.0+
* @author Daniel Kinzler
*/
class ApiErrorReporterTest extends \MediaWikiTestCase {
protected function assertUsageException(
$info,
$code,
$httpStatusCode,
array $expectedDataFields,
UsageException $ex
) {
$messageArray = $ex->getMessageArray();
$this->assertArrayHasKey( 'code', $messageArray );
$this->assertArrayHasKey( 'info', $messageArray );
if ( $info !== null ) {
$this->assertRegExp( $info, $messageArray['info'] );
}
if ( $code !== null ) {
$this->assertEquals( $code, $messageArray['code'] );
}
if ( $httpStatusCode ) {
$this->assertEquals( $httpStatusCode, $ex->getCode() );
}
foreach ( $expectedDataFields as $path => $value ) {
$path = explode( '/', $path );
$this->assertValueAtPath( $value, $path, $messageArray );
}
}
protected function assertValueAtPath( $expected, $path, $data ) {
$name = '';
foreach ( $path as $step ) {
$this->assertArrayHasKey( $step, $data );
$data = $data[$step];
$name .= '/' . $step;
}
if ( preg_match( '/^([^\s\w\d]).*\1[a-zA-Z]*$/', $expected ) ) {
$this->assertInternalType( 'string', $data, $name );
$this->assertRegExp( $expected, $data, $name );
} else {
$this->assertEquals( $expected, $data, $name );
}
}
public function exceptionProvider() {
return array(
// Reporting an unknown / generic exception with an unknown error
// code should result in the error code and being used directly,
// and the error info being set to the exception message.
'IllegalValueException' => array(
'$exception' => new IllegalValueException( 'ugh!' ),
'$code' => 'errorreporter-test-ugh',
'$httpStatusCode' => 0,
'$extradata' => array(),
'$infoPattern' => '/ugh!/',
'$expectedData' => array(
'code' => 'errorreporter-test-ugh',
),
),
// Any extra data should be passed through.
// The HTTP status code should be used.
'extradata' => array(
'$exception' => new IllegalValueException( 'ugh!' ),
'$code' => 'errorreporter-test-ugh',
'$httpStatusCode' => 555,
'$extradata' => array( 'fruit' => 'Banana' ),
'$infoPattern' => '/ugh!/',
'$expectedData' => array(
'fruit' => 'Banana',
),
),
// Reporting an unknwon / generic exception along with a well known
// error code should result in that code being used find a message key
// and generate a localized message.
'known error code' => array(
'$exception' => new IllegalValueException( 'ugh!' ),
'$code' => 'no-such-sitelink',
'$httpStatusCode' => 0,
'$extradata' => array( 'fruit' => 'Banana' ),
'$infoPattern' => '/sitelink.*\(ugh!\)$/',
'$expectedData' => array(
'fruit' => 'Banana',
'messages/0/name' => 'wikibase-api-no-such-sitelink',
'messages/0/html' => '/gefunden/', // in German
),
),
// Reporting a well known exception should result in an appropriate
// localized message via the ExceptionLocalizer mechanism.
'known exception' => array(
'$exception' => new ParseException( 'arg!' ),
'$code' => 'errorreporter-test-bla',
'$httpStatusCode' => 0,
'$extradata' => null,
'$infoPattern' => '/^Malformed value\./',
'$expectedData' => array(
'messages/0/name' => 'wikibase-parse-error',
'messages/0/html' => '/Wert/', // in German
),
),
);
}
/**
* @dataProvider exceptionProvider
*/
public function testDieException(
$exception,
$code,
$httpStatusCode,
array $extradata = null,
$infoPattern,
array $expectedDataFields
) {
$api = new ApiMain();
$localizer = $this->getExceptionLocalizer();
$reporter = new ApiErrorReporter( $api, $localizer, Language::factory( 'de' ) );
try {
$reporter->dieException( $exception, $code, $httpStatusCode, $extradata );
$this->fail( 'UsageException was not thrown!' );
} catch ( UsageException $ex ) {
$this->assertUsageException( $infoPattern, $code, $httpStatusCode, $expectedDataFields, $ex );
}
}
public function messageProvider() {
$code = 'no-such-sitelink';
$param = 'Foo';
return array(
// The appropriate message should be included in the extra data.
// Most importantly, the info field should contain the message text in English,
// while the HTML should be in German. Any Message parameters must be present.
'known error code' => array(
'$code' => $code,
'$param' => $param,
'$infoPattern' => '/sitelink/',
'$expectedDataFields' => array(
'messages/0/name' => 'wikibase-api-no-such-sitelink',
'messages/0/html' => '/gefunden/', // in German
'messages/0/parameters/0' => '/Foo/',
),
)
);
}
/**
* @dataProvider messageProvider
*/
public function testDieMessage( $code, $param, $infoPattern, array $expectedDataFields ) {
$api = new ApiMain();
$localizer = $this->getExceptionLocalizer();
$reporter = new ApiErrorReporter( $api, $localizer, Language::factory( 'de' ) );
try {
$reporter->dieMessage( $code, $param );
$this->fail( 'UsageException was not thrown!' );
} catch ( UsageException $ex ) {
$this->assertUsageException( $infoPattern, $code, null, $expectedDataFields, $ex );
}
}
public function statusProvider() {
$status = Status::newFatal( 'wikibase-api-no-such-sitelink' );
$status->fatal( 'wikibase-noentity', 'Q123' );
return array(
// Using an (existing) message, the message should be included in the extra data.
// The code string should be unchanged.
// Most importantly, the info field should contain the message text in English,
// while the HTML should be in German.
// All error messages from the Status object should be present, all message
// parameters must be present.
'known error code' => array(
'$status' => $status,
'$code' => 'errorreporter-test-ugh',
'$httpStatusCode' => 0,
'$extradata' => null,
'$infoPattern' => '/sitelink/',
'$expectedData' => array(
'messages/0/name' => 'wikibase-api-no-such-sitelink',
'messages/0/html' => '/gefunden/', // in German
'messages/1/name' => 'wikibase-noentity',
'messages/1/parameters/0' => 'Q123',
'messages/1/html' => '/ist nicht vorhanden/', // in German
),
),
// Any extra data should be passed through.
// The HTTP status code should be used.
'extradata' => array(
'$status' => $status,
'$code' => 'errorreporter-test-ugh',
'$httpStatusCode' => 555,
'$extradata' => array( 'fruit' => 'Banana' ),
'$infoPattern' => null,
'$expectedData' => array(
'fruit' => 'Banana',
),
),
);
}
/**
* @dataProvider statusProvider
*/
public function testDieStatus(
Status $status,
$code,
$httpStatusCode,
array $extradata = null,
$infoPattern,
array $expectedDataFields
) {
$api = new ApiMain();
$localizer = $this->getExceptionLocalizer();
$reporter = new ApiErrorReporter( $api, $localizer, Language::factory( 'de' ) );
try {
$reporter->dieStatus( $status, $code, $httpStatusCode, $extradata );
$this->fail( 'UsageException was not thrown!' );
} catch ( UsageException $ex ) {
$this->assertUsageException( $infoPattern, $code, $httpStatusCode, $expectedDataFields, $ex );
}
}
public function errorProvider() {
return array(
// The provided description and code should be present
// in the result.
'IllegalValueException' => array(
'$description' => 'Ugh!',
'$code' => 'errorreporter-test-ugh',
'$httpStatusCode' => 0,
'$extradata' => array(),
'$infoPattern' => '/^Ugh!$/',
'$expectedData' => array(
'info' => 'Ugh!',
'code' => 'errorreporter-test-ugh',
),
),
// Any extra data should be passed through.
// The HTTP status code should be used.
'extradata' => array(
'$description' => 'Ugh!',
'$code' => 'errorreporter-test-ugh',
'$httpStatusCode' => 555,
'$extradata' => array( 'fruit' => 'Banana' ),
'$infoPattern' => '/^Ugh!$/',
'$expectedData' => array(
'fruit' => 'Banana',
),
),
);
}
/**
* @dataProvider errorProvider
*/
public function testDieError(
$description,
$code,
$httpStatusCode,
array $extradata,
$infoPattern,
array $expectedDataFields
) {
$api = new ApiMain();
$localizer = $this->getExceptionLocalizer();
$reporter = new ApiErrorReporter( $api, $localizer, Language::factory( 'de' ) );
try {
$reporter->dieError( $description, $code, $httpStatusCode, $extradata );
$this->fail( 'UsageException was not thrown!' );
} catch ( UsageException $ex ) {
$this->assertUsageException( $infoPattern, $code, $httpStatusCode, $expectedDataFields, $ex );
}
}
public function warningProvider() {
$status = Status::newGood();
$status->warning( 'wikibase-conflict-patched' );
$status->warning( 'undo-nochange' );
return array(
// Messages from the Status object should be added to the 'warnings' section.
'known error code' => array(
'$status' => $status,
'$expectedData' => array(
'warnings/main_int/messages/0/name' => 'wikibase-conflict-patched',
'warnings/main_int/messages/0/html' => '/Version/', // in German
'warnings/main_int/messages/1/name' => 'undo-nochange',
'warnings/main_int/messages/1/html' => '/Bearbeitung.*bereits/', // in German
),
),
);
}
/**
* @dataProvider warningProvider
*/
public function testReportStatusWarnings( Status $status, array $expectedDataFields ) {
$api = new ApiMain();
$localizer = $this->getExceptionLocalizer();
$reporter = new ApiErrorReporter( $api, $localizer, Language::factory( 'de' ) );
$reporter->reportStatusWarnings( $status );
$result = $api->getResult()->getResultData();
foreach ( $expectedDataFields as $path => $value ) {
$path = explode( '/', $path );
$this->assertValueAtPath( $value, $path, $result );
}
}
/**
* @return ExceptionLocalizer
*/
private function getExceptionLocalizer() {
$localizers = array(
new ParseExceptionLocalizer()
);
return new DispatchingExceptionLocalizer( $localizers );
}
}