2013-10-23 15:36:40 +00:00
|
|
|
|
<?php
|
|
|
|
|
|
|
2019-05-13 14:18:07 +00:00
|
|
|
|
use MediaWiki\Block\DatabaseBlock;
|
2018-08-05 17:58:51 +00:00
|
|
|
|
use MediaWiki\MediaWikiServices;
|
2017-04-19 19:37:35 +00:00
|
|
|
|
use Wikimedia\TestingAccessWrapper;
|
|
|
|
|
|
|
2013-10-23 15:36:40 +00:00
|
|
|
|
/**
|
|
|
|
|
|
* @group API
|
|
|
|
|
|
* @group Database
|
|
|
|
|
|
* @group medium
|
2018-03-20 17:34:03 +00:00
|
|
|
|
*
|
|
|
|
|
|
* @covers ApiBase
|
2013-10-23 15:36:40 +00:00
|
|
|
|
*/
|
|
|
|
|
|
class ApiBaseTest extends ApiTestCase {
|
|
|
|
|
|
/**
|
2018-03-20 17:34:03 +00:00
|
|
|
|
* This covers a variety of stub methods that return a fixed value.
|
|
|
|
|
|
*
|
|
|
|
|
|
* @param string|array $method Name of method, or [ name, params... ]
|
|
|
|
|
|
* @param string $value Expected value
|
|
|
|
|
|
*
|
|
|
|
|
|
* @dataProvider provideStubMethods
|
2013-10-23 15:36:40 +00:00
|
|
|
|
*/
|
2018-03-20 17:34:03 +00:00
|
|
|
|
public function testStubMethods( $expected, $method, $args = [] ) {
|
|
|
|
|
|
// Some of these are protected
|
|
|
|
|
|
$mock = TestingAccessWrapper::newFromObject( new MockApi() );
|
|
|
|
|
|
$result = call_user_func_array( [ $mock, $method ], $args );
|
|
|
|
|
|
$this->assertSame( $expected, $result );
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public function provideStubMethods() {
|
|
|
|
|
|
return [
|
|
|
|
|
|
[ null, 'getModuleManager' ],
|
|
|
|
|
|
[ null, 'getCustomPrinter' ],
|
|
|
|
|
|
[ [], 'getHelpUrls' ],
|
|
|
|
|
|
// @todo This is actually overriden by MockApi
|
|
|
|
|
|
// [ [], 'getAllowedParams' ],
|
|
|
|
|
|
[ true, 'shouldCheckMaxLag' ],
|
|
|
|
|
|
[ true, 'isReadMode' ],
|
|
|
|
|
|
[ false, 'isWriteMode' ],
|
|
|
|
|
|
[ false, 'mustBePosted' ],
|
|
|
|
|
|
[ false, 'isDeprecated' ],
|
|
|
|
|
|
[ false, 'isInternal' ],
|
|
|
|
|
|
[ false, 'needsToken' ],
|
|
|
|
|
|
[ null, 'getWebUITokenSalt', [ [] ] ],
|
|
|
|
|
|
[ null, 'getConditionalRequestData', [ 'etag' ] ],
|
|
|
|
|
|
[ null, 'dynamicParameterDocumentation' ],
|
|
|
|
|
|
];
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2013-10-23 15:36:40 +00:00
|
|
|
|
public function testRequireOnlyOneParameterDefault() {
|
|
|
|
|
|
$mock = new MockApi();
|
|
|
|
|
|
$mock->requireOnlyOneParameter(
|
2016-02-17 09:09:32 +00:00
|
|
|
|
[ "filename" => "foo.txt", "enablechunks" => false ],
|
2013-10-23 15:36:40 +00:00
|
|
|
|
"filename", "enablechunks"
|
|
|
|
|
|
);
|
|
|
|
|
|
$this->assertTrue( true );
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public function testRequireOnlyOneParameterZero() {
|
|
|
|
|
|
$mock = new MockApi();
|
2019-10-11 22:22:26 +00:00
|
|
|
|
$this->expectException( ApiUsageException::class );
|
2013-10-23 15:36:40 +00:00
|
|
|
|
$mock->requireOnlyOneParameter(
|
2016-02-17 09:09:32 +00:00
|
|
|
|
[ "filename" => "foo.txt", "enablechunks" => 0 ],
|
2013-10-23 15:36:40 +00:00
|
|
|
|
"filename", "enablechunks"
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public function testRequireOnlyOneParameterTrue() {
|
|
|
|
|
|
$mock = new MockApi();
|
2019-10-11 22:22:26 +00:00
|
|
|
|
$this->expectException( ApiUsageException::class );
|
2013-10-23 15:36:40 +00:00
|
|
|
|
$mock->requireOnlyOneParameter(
|
2016-02-17 09:09:32 +00:00
|
|
|
|
[ "filename" => "foo.txt", "enablechunks" => true ],
|
2013-10-23 15:36:40 +00:00
|
|
|
|
"filename", "enablechunks"
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2018-03-20 17:34:03 +00:00
|
|
|
|
public function testRequireOnlyOneParameterMissing() {
|
2019-10-05 15:42:53 +00:00
|
|
|
|
$this->expectException( ApiUsageException::class );
|
|
|
|
|
|
$this->expectExceptionMessage( 'One of the parameters "foo" and "bar" is required.' );
|
2018-03-20 17:34:03 +00:00
|
|
|
|
$mock = new MockApi();
|
|
|
|
|
|
$mock->requireOnlyOneParameter(
|
|
|
|
|
|
[ "filename" => "foo.txt", "enablechunks" => false ],
|
|
|
|
|
|
"foo", "bar" );
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public function testRequireMaxOneParameterZero() {
|
|
|
|
|
|
$mock = new MockApi();
|
|
|
|
|
|
$mock->requireMaxOneParameter(
|
|
|
|
|
|
[ 'foo' => 'bar', 'baz' => 'quz' ],
|
|
|
|
|
|
'squirrel' );
|
|
|
|
|
|
$this->assertTrue( true );
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public function testRequireMaxOneParameterOne() {
|
|
|
|
|
|
$mock = new MockApi();
|
|
|
|
|
|
$mock->requireMaxOneParameter(
|
|
|
|
|
|
[ 'foo' => 'bar', 'baz' => 'quz' ],
|
|
|
|
|
|
'foo', 'squirrel' );
|
|
|
|
|
|
$this->assertTrue( true );
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public function testRequireMaxOneParameterTwo() {
|
2019-10-05 15:42:53 +00:00
|
|
|
|
$this->expectException( ApiUsageException::class );
|
|
|
|
|
|
$this->expectExceptionMessage( 'The parameters "foo" and "baz" can not be used together.' );
|
2018-03-20 17:34:03 +00:00
|
|
|
|
$mock = new MockApi();
|
|
|
|
|
|
$mock->requireMaxOneParameter(
|
|
|
|
|
|
[ 'foo' => 'bar', 'baz' => 'quz' ],
|
|
|
|
|
|
'foo', 'baz' );
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public function testRequireAtLeastOneParameterZero() {
|
2019-10-05 15:42:53 +00:00
|
|
|
|
$this->expectException( ApiUsageException::class );
|
|
|
|
|
|
$this->expectExceptionMessage( 'At least one of the parameters "foo" and "bar" is required.' );
|
2018-03-20 17:34:03 +00:00
|
|
|
|
$mock = new MockApi();
|
|
|
|
|
|
$mock->requireAtLeastOneParameter(
|
|
|
|
|
|
[ 'a' => 'b', 'c' => 'd' ],
|
|
|
|
|
|
'foo', 'bar' );
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public function testRequireAtLeastOneParameterOne() {
|
|
|
|
|
|
$mock = new MockApi();
|
|
|
|
|
|
$mock->requireAtLeastOneParameter(
|
|
|
|
|
|
[ 'a' => 'b', 'c' => 'd' ],
|
|
|
|
|
|
'foo', 'a' );
|
|
|
|
|
|
$this->assertTrue( true );
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public function testRequireAtLeastOneParameterTwo() {
|
|
|
|
|
|
$mock = new MockApi();
|
|
|
|
|
|
$mock->requireAtLeastOneParameter(
|
|
|
|
|
|
[ 'a' => 'b', 'c' => 'd' ],
|
|
|
|
|
|
'a', 'c' );
|
|
|
|
|
|
$this->assertTrue( true );
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public function testGetTitleOrPageIdBadParams() {
|
2019-10-05 15:42:53 +00:00
|
|
|
|
$this->expectException( ApiUsageException::class );
|
|
|
|
|
|
$this->expectExceptionMessage( 'The parameters "title" and "pageid" can not be used together.' );
|
2018-03-20 17:34:03 +00:00
|
|
|
|
$mock = new MockApi();
|
|
|
|
|
|
$mock->getTitleOrPageId( [ 'title' => 'a', 'pageid' => 7 ] );
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public function testGetTitleOrPageIdTitle() {
|
|
|
|
|
|
$mock = new MockApi();
|
|
|
|
|
|
$result = $mock->getTitleOrPageId( [ 'title' => 'Foo' ] );
|
|
|
|
|
|
$this->assertInstanceOf( WikiPage::class, $result );
|
|
|
|
|
|
$this->assertSame( 'Foo', $result->getTitle()->getPrefixedText() );
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public function testGetTitleOrPageIdInvalidTitle() {
|
2019-10-05 15:42:53 +00:00
|
|
|
|
$this->expectException( ApiUsageException::class );
|
|
|
|
|
|
$this->expectExceptionMessage( 'Bad title "|".' );
|
2018-03-20 17:34:03 +00:00
|
|
|
|
$mock = new MockApi();
|
|
|
|
|
|
$mock->getTitleOrPageId( [ 'title' => '|' ] );
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public function testGetTitleOrPageIdSpecialTitle() {
|
2019-10-05 15:42:53 +00:00
|
|
|
|
$this->expectException( ApiUsageException::class );
|
|
|
|
|
|
$this->expectExceptionMessage( "Namespace doesn't allow actual pages." );
|
2018-03-20 17:34:03 +00:00
|
|
|
|
$mock = new MockApi();
|
|
|
|
|
|
$mock->getTitleOrPageId( [ 'title' => 'Special:RandomPage' ] );
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public function testGetTitleOrPageIdPageId() {
|
2018-06-12 16:36:34 +00:00
|
|
|
|
$page = $this->getExistingTestPage();
|
2018-03-20 17:34:03 +00:00
|
|
|
|
$result = ( new MockApi() )->getTitleOrPageId(
|
2018-06-12 16:36:34 +00:00
|
|
|
|
[ 'pageid' => $page->getId() ] );
|
2018-03-20 17:34:03 +00:00
|
|
|
|
$this->assertInstanceOf( WikiPage::class, $result );
|
2018-06-12 16:36:34 +00:00
|
|
|
|
$this->assertSame(
|
|
|
|
|
|
$page->getTitle()->getPrefixedText(),
|
|
|
|
|
|
$result->getTitle()->getPrefixedText()
|
|
|
|
|
|
);
|
2018-03-20 17:34:03 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public function testGetTitleOrPageIdInvalidPageId() {
|
2018-08-01 07:25:32 +00:00
|
|
|
|
// FIXME: fails under postgres
|
|
|
|
|
|
$this->markTestSkippedIfDbType( 'postgres' );
|
|
|
|
|
|
|
2019-10-05 15:42:53 +00:00
|
|
|
|
$this->expectException( ApiUsageException::class );
|
|
|
|
|
|
$this->expectExceptionMessage( 'There is no page with ID 2147483648.' );
|
2018-03-20 17:34:03 +00:00
|
|
|
|
$mock = new MockApi();
|
|
|
|
|
|
$mock->getTitleOrPageId( [ 'pageid' => 2147483648 ] );
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public function testGetTitleFromTitleOrPageIdBadParams() {
|
2019-10-05 15:42:53 +00:00
|
|
|
|
$this->expectException( ApiUsageException::class );
|
|
|
|
|
|
$this->expectExceptionMessage( 'The parameters "title" and "pageid" can not be used together.' );
|
2018-03-20 17:34:03 +00:00
|
|
|
|
$mock = new MockApi();
|
|
|
|
|
|
$mock->getTitleFromTitleOrPageId( [ 'title' => 'a', 'pageid' => 7 ] );
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public function testGetTitleFromTitleOrPageIdTitle() {
|
|
|
|
|
|
$mock = new MockApi();
|
|
|
|
|
|
$result = $mock->getTitleFromTitleOrPageId( [ 'title' => 'Foo' ] );
|
|
|
|
|
|
$this->assertInstanceOf( Title::class, $result );
|
|
|
|
|
|
$this->assertSame( 'Foo', $result->getPrefixedText() );
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public function testGetTitleFromTitleOrPageIdInvalidTitle() {
|
2019-10-05 15:42:53 +00:00
|
|
|
|
$this->expectException( ApiUsageException::class );
|
|
|
|
|
|
$this->expectExceptionMessage( 'Bad title "|".' );
|
2018-03-20 17:34:03 +00:00
|
|
|
|
$mock = new MockApi();
|
|
|
|
|
|
$mock->getTitleFromTitleOrPageId( [ 'title' => '|' ] );
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public function testGetTitleFromTitleOrPageIdPageId() {
|
2018-06-12 16:36:34 +00:00
|
|
|
|
$page = $this->getExistingTestPage();
|
2018-03-20 17:34:03 +00:00
|
|
|
|
$result = ( new MockApi() )->getTitleFromTitleOrPageId(
|
2018-06-12 16:36:34 +00:00
|
|
|
|
[ 'pageid' => $page->getId() ] );
|
2018-03-20 17:34:03 +00:00
|
|
|
|
$this->assertInstanceOf( Title::class, $result );
|
2018-06-12 16:36:34 +00:00
|
|
|
|
$this->assertSame( $page->getTitle()->getPrefixedText(), $result->getPrefixedText() );
|
2018-03-20 17:34:03 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public function testGetTitleFromTitleOrPageIdInvalidPageId() {
|
2019-10-05 15:42:53 +00:00
|
|
|
|
$this->expectException( ApiUsageException::class );
|
|
|
|
|
|
$this->expectExceptionMessage( 'There is no page with ID 298401643.' );
|
2018-03-20 17:34:03 +00:00
|
|
|
|
$mock = new MockApi();
|
|
|
|
|
|
$mock->getTitleFromTitleOrPageId( [ 'pageid' => 298401643 ] );
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2018-05-28 22:45:24 +00:00
|
|
|
|
public function testGetParameter() {
|
|
|
|
|
|
$mock = $this->getMockBuilder( MockApi::class )
|
|
|
|
|
|
->setMethods( [ 'getAllowedParams' ] )
|
|
|
|
|
|
->getMock();
|
|
|
|
|
|
$mock->method( 'getAllowedParams' )->willReturn( [
|
|
|
|
|
|
'foo' => [
|
|
|
|
|
|
ApiBase::PARAM_TYPE => [ 'value' ],
|
|
|
|
|
|
],
|
|
|
|
|
|
'bar' => [
|
|
|
|
|
|
ApiBase::PARAM_TYPE => [ 'value' ],
|
|
|
|
|
|
],
|
|
|
|
|
|
] );
|
|
|
|
|
|
$wrapper = TestingAccessWrapper::newFromObject( $mock );
|
|
|
|
|
|
|
|
|
|
|
|
$context = new DerivativeContext( $mock );
|
|
|
|
|
|
$context->setRequest( new FauxRequest( [ 'foo' => 'bad', 'bar' => 'value' ] ) );
|
|
|
|
|
|
$wrapper->mMainModule = new ApiMain( $context );
|
|
|
|
|
|
|
|
|
|
|
|
// Even though 'foo' is bad, getParameter( 'bar' ) must not fail
|
|
|
|
|
|
$this->assertSame( 'value', $wrapper->getParameter( 'bar' ) );
|
|
|
|
|
|
|
|
|
|
|
|
// But getParameter( 'foo' ) must throw.
|
|
|
|
|
|
try {
|
|
|
|
|
|
$wrapper->getParameter( 'foo' );
|
|
|
|
|
|
$this->fail( 'Expected exception not thrown' );
|
|
|
|
|
|
} catch ( ApiUsageException $ex ) {
|
|
|
|
|
|
$this->assertTrue( $this->apiExceptionHasCode( $ex, 'unknown_foo' ) );
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// And extractRequestParams() must throw too.
|
|
|
|
|
|
try {
|
|
|
|
|
|
$mock->extractRequestParams();
|
|
|
|
|
|
$this->fail( 'Expected exception not thrown' );
|
|
|
|
|
|
} catch ( ApiUsageException $ex ) {
|
|
|
|
|
|
$this->assertTrue( $this->apiExceptionHasCode( $ex, 'unknown_foo' ) );
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2016-08-16 20:36:27 +00:00
|
|
|
|
/**
|
|
|
|
|
|
* @param string|null $input
|
|
|
|
|
|
* @param array $paramSettings
|
|
|
|
|
|
* @param mixed $expected
|
2018-03-20 17:34:03 +00:00
|
|
|
|
* @param array $options Key-value pairs:
|
|
|
|
|
|
* 'parseLimits': true|false
|
|
|
|
|
|
* 'apihighlimits': true|false
|
|
|
|
|
|
* 'internalmode': true|false
|
2018-07-23 13:22:23 +00:00
|
|
|
|
* 'prefix': true|false
|
2016-08-16 20:36:27 +00:00
|
|
|
|
* @param string[] $warnings
|
|
|
|
|
|
*/
|
2018-07-23 13:22:23 +00:00
|
|
|
|
private function doGetParameterFromSettings(
|
2018-03-20 17:34:03 +00:00
|
|
|
|
$input, $paramSettings, $expected, $warnings, $options = []
|
|
|
|
|
|
) {
|
2016-08-16 20:36:27 +00:00
|
|
|
|
$mock = new MockApi();
|
|
|
|
|
|
$wrapper = TestingAccessWrapper::newFromObject( $mock );
|
2018-07-23 13:22:23 +00:00
|
|
|
|
if ( $options['prefix'] ) {
|
|
|
|
|
|
$wrapper->mModulePrefix = 'my';
|
|
|
|
|
|
$paramName = 'Param';
|
|
|
|
|
|
} else {
|
|
|
|
|
|
$paramName = 'myParam';
|
|
|
|
|
|
}
|
2016-08-16 20:36:27 +00:00
|
|
|
|
|
|
|
|
|
|
$context = new DerivativeContext( $mock );
|
2018-03-20 17:34:03 +00:00
|
|
|
|
$context->setRequest( new FauxRequest(
|
|
|
|
|
|
$input !== null ? [ 'myParam' => $input ] : [] ) );
|
2016-08-16 20:36:27 +00:00
|
|
|
|
$wrapper->mMainModule = new ApiMain( $context );
|
|
|
|
|
|
|
2017-10-06 22:17:58 +00:00
|
|
|
|
$parseLimits = $options['parseLimits'] ?? true;
|
2018-03-20 17:34:03 +00:00
|
|
|
|
|
|
|
|
|
|
if ( !empty( $options['apihighlimits'] ) ) {
|
|
|
|
|
|
$context->setUser( self::$users['sysop']->getUser() );
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if ( isset( $options['internalmode'] ) && !$options['internalmode'] ) {
|
|
|
|
|
|
$mainWrapper = TestingAccessWrapper::newFromObject( $wrapper->mMainModule );
|
|
|
|
|
|
$mainWrapper->mInternalMode = false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// If we're testing tags, set up some tags
|
|
|
|
|
|
if ( isset( $paramSettings[ApiBase::PARAM_TYPE] ) &&
|
|
|
|
|
|
$paramSettings[ApiBase::PARAM_TYPE] === 'tags'
|
|
|
|
|
|
) {
|
|
|
|
|
|
ChangeTags::defineTag( 'tag1' );
|
|
|
|
|
|
ChangeTags::defineTag( 'tag2' );
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if ( $expected instanceof Exception ) {
|
2016-08-16 20:36:27 +00:00
|
|
|
|
try {
|
2018-07-23 13:22:23 +00:00
|
|
|
|
$wrapper->getParameterFromSettings( $paramName, $paramSettings,
|
2018-03-20 17:34:03 +00:00
|
|
|
|
$parseLimits );
|
|
|
|
|
|
$this->fail( 'No exception thrown' );
|
|
|
|
|
|
} catch ( Exception $ex ) {
|
2016-08-16 20:36:27 +00:00
|
|
|
|
$this->assertEquals( $expected, $ex );
|
|
|
|
|
|
}
|
|
|
|
|
|
} else {
|
2018-07-23 13:22:23 +00:00
|
|
|
|
$result = $wrapper->getParameterFromSettings( $paramName,
|
2018-03-20 17:34:03 +00:00
|
|
|
|
$paramSettings, $parseLimits );
|
|
|
|
|
|
if ( isset( $paramSettings[ApiBase::PARAM_TYPE] ) &&
|
|
|
|
|
|
$paramSettings[ApiBase::PARAM_TYPE] === 'timestamp' &&
|
|
|
|
|
|
$expected === 'now'
|
|
|
|
|
|
) {
|
|
|
|
|
|
// Allow one second of fuzziness. Make sure the formats are
|
|
|
|
|
|
// correct!
|
|
|
|
|
|
$this->assertRegExp( '/^\d{14}$/', $result );
|
|
|
|
|
|
$this->assertLessThanOrEqual( 1,
|
|
|
|
|
|
abs( wfTimestamp( TS_UNIX, $result ) - time() ),
|
|
|
|
|
|
"Result $result differs from expected $expected by " .
|
|
|
|
|
|
'more than one second' );
|
|
|
|
|
|
} else {
|
|
|
|
|
|
$this->assertSame( $expected, $result );
|
|
|
|
|
|
}
|
|
|
|
|
|
$actualWarnings = array_map( function ( $warn ) {
|
|
|
|
|
|
return $warn instanceof Message
|
|
|
|
|
|
? array_merge( [ $warn->getKey() ], $warn->getParams() )
|
|
|
|
|
|
: $warn;
|
|
|
|
|
|
}, $mock->warnings );
|
|
|
|
|
|
$this->assertSame( $warnings, $actualWarnings );
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if ( !empty( $paramSettings[ApiBase::PARAM_SENSITIVE] ) ||
|
|
|
|
|
|
( isset( $paramSettings[ApiBase::PARAM_TYPE] ) &&
|
|
|
|
|
|
$paramSettings[ApiBase::PARAM_TYPE] === 'password' )
|
|
|
|
|
|
) {
|
|
|
|
|
|
$mainWrapper = TestingAccessWrapper::newFromObject( $wrapper->getMain() );
|
|
|
|
|
|
$this->assertSame( [ 'myParam' ],
|
|
|
|
|
|
$mainWrapper->getSensitiveParams() );
|
2016-08-16 20:36:27 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2018-07-23 13:22:23 +00:00
|
|
|
|
/**
|
|
|
|
|
|
* @dataProvider provideGetParameterFromSettings
|
|
|
|
|
|
* @see self::doGetParameterFromSettings()
|
|
|
|
|
|
*/
|
|
|
|
|
|
public function testGetParameterFromSettings_noprefix(
|
|
|
|
|
|
$input, $paramSettings, $expected, $warnings, $options = []
|
|
|
|
|
|
) {
|
|
|
|
|
|
$options['prefix'] = false;
|
|
|
|
|
|
$this->doGetParameterFromSettings( $input, $paramSettings, $expected, $warnings, $options );
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* @dataProvider provideGetParameterFromSettings
|
|
|
|
|
|
* @see self::doGetParameterFromSettings()
|
|
|
|
|
|
*/
|
|
|
|
|
|
public function testGetParameterFromSettings_prefix(
|
|
|
|
|
|
$input, $paramSettings, $expected, $warnings, $options = []
|
|
|
|
|
|
) {
|
|
|
|
|
|
$options['prefix'] = true;
|
|
|
|
|
|
$this->doGetParameterFromSettings( $input, $paramSettings, $expected, $warnings, $options );
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2016-08-16 20:36:27 +00:00
|
|
|
|
public static function provideGetParameterFromSettings() {
|
2016-08-24 18:07:43 +00:00
|
|
|
|
$warnings = [
|
2018-03-20 17:34:03 +00:00
|
|
|
|
[ 'apiwarn-badutf8', 'myParam' ],
|
2016-08-24 18:07:43 +00:00
|
|
|
|
];
|
|
|
|
|
|
|
2016-08-16 20:36:27 +00:00
|
|
|
|
$c0 = '';
|
|
|
|
|
|
$enc = '';
|
|
|
|
|
|
for ( $i = 0; $i < 32; $i++ ) {
|
|
|
|
|
|
$c0 .= chr( $i );
|
|
|
|
|
|
$enc .= ( $i === 9 || $i === 10 || $i === 13 )
|
|
|
|
|
|
? chr( $i )
|
|
|
|
|
|
: '<27>';
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2018-03-20 17:34:03 +00:00
|
|
|
|
$returnArray = [
|
2016-08-16 20:36:27 +00:00
|
|
|
|
'Basic param' => [ 'bar', null, 'bar', [] ],
|
2016-08-24 18:07:43 +00:00
|
|
|
|
'Basic param, C0 controls' => [ $c0, null, $enc, $warnings ],
|
2016-08-16 20:36:27 +00:00
|
|
|
|
'String param' => [ 'bar', '', 'bar', [] ],
|
|
|
|
|
|
'String param, defaulted' => [ null, '', '', [] ],
|
|
|
|
|
|
'String param, empty' => [ '', 'default', '', [] ],
|
|
|
|
|
|
'String param, required, empty' => [
|
|
|
|
|
|
'',
|
|
|
|
|
|
[ ApiBase::PARAM_DFLT => 'default', ApiBase::PARAM_REQUIRED => true ],
|
2018-03-20 17:34:03 +00:00
|
|
|
|
ApiUsageException::newWithMessage( null,
|
|
|
|
|
|
[ 'apierror-missingparam', 'myParam' ] ),
|
2016-08-16 20:36:27 +00:00
|
|
|
|
[]
|
|
|
|
|
|
],
|
|
|
|
|
|
'Multi-valued parameter' => [
|
|
|
|
|
|
'a|b|c',
|
|
|
|
|
|
[ ApiBase::PARAM_ISMULTI => true ],
|
|
|
|
|
|
[ 'a', 'b', 'c' ],
|
|
|
|
|
|
[]
|
|
|
|
|
|
],
|
|
|
|
|
|
'Multi-valued parameter, alternative separator' => [
|
|
|
|
|
|
"\x1fa|b\x1fc|d",
|
|
|
|
|
|
[ ApiBase::PARAM_ISMULTI => true ],
|
|
|
|
|
|
[ 'a|b', 'c|d' ],
|
|
|
|
|
|
[]
|
|
|
|
|
|
],
|
|
|
|
|
|
'Multi-valued parameter, other C0 controls' => [
|
|
|
|
|
|
$c0,
|
|
|
|
|
|
[ ApiBase::PARAM_ISMULTI => true ],
|
|
|
|
|
|
[ $enc ],
|
2016-08-24 18:07:43 +00:00
|
|
|
|
$warnings
|
2016-08-16 20:36:27 +00:00
|
|
|
|
],
|
|
|
|
|
|
'Multi-valued parameter, other C0 controls (2)' => [
|
|
|
|
|
|
"\x1f" . $c0,
|
|
|
|
|
|
[ ApiBase::PARAM_ISMULTI => true ],
|
|
|
|
|
|
[ substr( $enc, 0, -3 ), '' ],
|
2016-08-24 18:07:43 +00:00
|
|
|
|
$warnings
|
2016-08-16 20:36:27 +00:00
|
|
|
|
],
|
2018-03-20 17:34:03 +00:00
|
|
|
|
'Multi-valued parameter with limits' => [
|
|
|
|
|
|
'a|b|c',
|
|
|
|
|
|
[
|
|
|
|
|
|
ApiBase::PARAM_ISMULTI => true,
|
|
|
|
|
|
ApiBase::PARAM_ISMULTI_LIMIT1 => 3,
|
|
|
|
|
|
],
|
|
|
|
|
|
[ 'a', 'b', 'c' ],
|
|
|
|
|
|
[],
|
|
|
|
|
|
],
|
|
|
|
|
|
'Multi-valued parameter with exceeded limits' => [
|
|
|
|
|
|
'a|b|c',
|
|
|
|
|
|
[
|
|
|
|
|
|
ApiBase::PARAM_ISMULTI => true,
|
|
|
|
|
|
ApiBase::PARAM_ISMULTI_LIMIT1 => 2,
|
|
|
|
|
|
],
|
2018-05-18 15:22:44 +00:00
|
|
|
|
ApiUsageException::newWithMessage(
|
|
|
|
|
|
null, [ 'apierror-toomanyvalues', 'myParam', 2 ], 'too-many-myParam'
|
|
|
|
|
|
),
|
|
|
|
|
|
[]
|
2018-03-20 17:34:03 +00:00
|
|
|
|
],
|
|
|
|
|
|
'Multi-valued parameter with exceeded limits for non-bot' => [
|
|
|
|
|
|
'a|b|c',
|
|
|
|
|
|
[
|
|
|
|
|
|
ApiBase::PARAM_ISMULTI => true,
|
|
|
|
|
|
ApiBase::PARAM_ISMULTI_LIMIT1 => 2,
|
|
|
|
|
|
ApiBase::PARAM_ISMULTI_LIMIT2 => 3,
|
|
|
|
|
|
],
|
2018-05-18 15:22:44 +00:00
|
|
|
|
ApiUsageException::newWithMessage(
|
|
|
|
|
|
null, [ 'apierror-toomanyvalues', 'myParam', 2 ], 'too-many-myParam'
|
|
|
|
|
|
),
|
|
|
|
|
|
[]
|
2018-03-20 17:34:03 +00:00
|
|
|
|
],
|
|
|
|
|
|
'Multi-valued parameter with non-exceeded limits for bot' => [
|
|
|
|
|
|
'a|b|c',
|
|
|
|
|
|
[
|
|
|
|
|
|
ApiBase::PARAM_ISMULTI => true,
|
|
|
|
|
|
ApiBase::PARAM_ISMULTI_LIMIT1 => 2,
|
|
|
|
|
|
ApiBase::PARAM_ISMULTI_LIMIT2 => 3,
|
|
|
|
|
|
],
|
|
|
|
|
|
[ 'a', 'b', 'c' ],
|
|
|
|
|
|
[],
|
|
|
|
|
|
[ 'apihighlimits' => true ],
|
|
|
|
|
|
],
|
|
|
|
|
|
'Multi-valued parameter with prohibited duplicates' => [
|
|
|
|
|
|
'a|b|a|c',
|
|
|
|
|
|
[ ApiBase::PARAM_ISMULTI => true ],
|
|
|
|
|
|
// Note that the keys are not sequential! This matches
|
|
|
|
|
|
// array_unique, but might be unexpected.
|
|
|
|
|
|
[ 0 => 'a', 1 => 'b', 3 => 'c' ],
|
|
|
|
|
|
[],
|
|
|
|
|
|
],
|
|
|
|
|
|
'Multi-valued parameter with allowed duplicates' => [
|
|
|
|
|
|
'a|a',
|
|
|
|
|
|
[
|
|
|
|
|
|
ApiBase::PARAM_ISMULTI => true,
|
|
|
|
|
|
ApiBase::PARAM_ALLOW_DUPLICATES => true,
|
|
|
|
|
|
],
|
|
|
|
|
|
[ 'a', 'a' ],
|
|
|
|
|
|
[],
|
|
|
|
|
|
],
|
|
|
|
|
|
'Empty boolean param' => [
|
|
|
|
|
|
'',
|
|
|
|
|
|
[ ApiBase::PARAM_TYPE => 'boolean' ],
|
|
|
|
|
|
true,
|
|
|
|
|
|
[],
|
|
|
|
|
|
],
|
|
|
|
|
|
'Boolean param 0' => [
|
|
|
|
|
|
'0',
|
|
|
|
|
|
[ ApiBase::PARAM_TYPE => 'boolean' ],
|
|
|
|
|
|
true,
|
|
|
|
|
|
[],
|
|
|
|
|
|
],
|
|
|
|
|
|
'Boolean param false' => [
|
|
|
|
|
|
'false',
|
|
|
|
|
|
[ ApiBase::PARAM_TYPE => 'boolean' ],
|
|
|
|
|
|
true,
|
|
|
|
|
|
[],
|
|
|
|
|
|
],
|
|
|
|
|
|
'Boolean multi-param' => [
|
|
|
|
|
|
'true|false',
|
|
|
|
|
|
[
|
|
|
|
|
|
ApiBase::PARAM_TYPE => 'boolean',
|
|
|
|
|
|
ApiBase::PARAM_ISMULTI => true,
|
|
|
|
|
|
],
|
|
|
|
|
|
new MWException(
|
|
|
|
|
|
'Internal error in ApiBase::getParameterFromSettings: ' .
|
|
|
|
|
|
'Multi-values not supported for myParam'
|
|
|
|
|
|
),
|
|
|
|
|
|
[],
|
|
|
|
|
|
],
|
|
|
|
|
|
'Empty boolean param with non-false default' => [
|
|
|
|
|
|
'',
|
|
|
|
|
|
[
|
|
|
|
|
|
ApiBase::PARAM_TYPE => 'boolean',
|
|
|
|
|
|
ApiBase::PARAM_DFLT => true,
|
|
|
|
|
|
],
|
|
|
|
|
|
new MWException(
|
|
|
|
|
|
'Internal error in ApiBase::getParameterFromSettings: ' .
|
|
|
|
|
|
"Boolean param myParam's default is set to '1'. " .
|
|
|
|
|
|
'Boolean parameters must default to false.' ),
|
|
|
|
|
|
[],
|
|
|
|
|
|
],
|
|
|
|
|
|
'Deprecated parameter' => [
|
|
|
|
|
|
'foo',
|
|
|
|
|
|
[ ApiBase::PARAM_DEPRECATED => true ],
|
|
|
|
|
|
'foo',
|
|
|
|
|
|
[ [ 'apiwarn-deprecation-parameter', 'myParam' ] ],
|
|
|
|
|
|
],
|
2019-02-07 20:32:36 +00:00
|
|
|
|
'Deprecated parameter with default, unspecified' => [
|
|
|
|
|
|
null,
|
|
|
|
|
|
[ ApiBase::PARAM_DEPRECATED => true, ApiBase::PARAM_DFLT => 'foo' ],
|
|
|
|
|
|
'foo',
|
|
|
|
|
|
[],
|
|
|
|
|
|
],
|
|
|
|
|
|
'Deprecated parameter with default, specified' => [
|
|
|
|
|
|
'foo',
|
|
|
|
|
|
[ ApiBase::PARAM_DEPRECATED => true, ApiBase::PARAM_DFLT => 'foo' ],
|
|
|
|
|
|
'foo',
|
|
|
|
|
|
[ [ 'apiwarn-deprecation-parameter', 'myParam' ] ],
|
|
|
|
|
|
],
|
2018-03-20 17:34:03 +00:00
|
|
|
|
'Deprecated parameter value' => [
|
|
|
|
|
|
'a',
|
|
|
|
|
|
[ ApiBase::PARAM_DEPRECATED_VALUES => [ 'a' => true ] ],
|
|
|
|
|
|
'a',
|
|
|
|
|
|
[ [ 'apiwarn-deprecation-parameter', 'myParam=a' ] ],
|
|
|
|
|
|
],
|
2019-02-07 20:32:36 +00:00
|
|
|
|
'Deprecated parameter value as default, unspecified' => [
|
|
|
|
|
|
null,
|
|
|
|
|
|
[ ApiBase::PARAM_DEPRECATED_VALUES => [ 'a' => true ], ApiBase::PARAM_DFLT => 'a' ],
|
|
|
|
|
|
'a',
|
|
|
|
|
|
[],
|
|
|
|
|
|
],
|
|
|
|
|
|
'Deprecated parameter value as default, specified' => [
|
|
|
|
|
|
'a',
|
|
|
|
|
|
[ ApiBase::PARAM_DEPRECATED_VALUES => [ 'a' => true ], ApiBase::PARAM_DFLT => 'a' ],
|
|
|
|
|
|
'a',
|
|
|
|
|
|
[ [ 'apiwarn-deprecation-parameter', 'myParam=a' ] ],
|
|
|
|
|
|
],
|
2018-03-20 17:34:03 +00:00
|
|
|
|
'Multiple deprecated parameter values' => [
|
|
|
|
|
|
'a|b|c|d',
|
|
|
|
|
|
[ ApiBase::PARAM_DEPRECATED_VALUES =>
|
|
|
|
|
|
[ 'b' => true, 'd' => true ],
|
|
|
|
|
|
ApiBase::PARAM_ISMULTI => true ],
|
|
|
|
|
|
[ 'a', 'b', 'c', 'd' ],
|
|
|
|
|
|
[
|
|
|
|
|
|
[ 'apiwarn-deprecation-parameter', 'myParam=b' ],
|
|
|
|
|
|
[ 'apiwarn-deprecation-parameter', 'myParam=d' ],
|
|
|
|
|
|
],
|
|
|
|
|
|
],
|
|
|
|
|
|
'Deprecated parameter value with custom warning' => [
|
|
|
|
|
|
'a',
|
|
|
|
|
|
[ ApiBase::PARAM_DEPRECATED_VALUES => [ 'a' => 'my-msg' ] ],
|
|
|
|
|
|
'a',
|
|
|
|
|
|
[ 'my-msg' ],
|
|
|
|
|
|
],
|
|
|
|
|
|
'"*" when wildcard not allowed' => [
|
|
|
|
|
|
'*',
|
|
|
|
|
|
[ ApiBase::PARAM_ISMULTI => true,
|
|
|
|
|
|
ApiBase::PARAM_TYPE => [ 'a', 'b', 'c' ] ],
|
|
|
|
|
|
[],
|
|
|
|
|
|
[ [ 'apiwarn-unrecognizedvalues', 'myParam',
|
|
|
|
|
|
[ 'list' => [ '*' ], 'type' => 'comma' ], 1 ] ],
|
|
|
|
|
|
],
|
|
|
|
|
|
'Wildcard "*"' => [
|
|
|
|
|
|
'*',
|
|
|
|
|
|
[
|
|
|
|
|
|
ApiBase::PARAM_ISMULTI => true,
|
|
|
|
|
|
ApiBase::PARAM_TYPE => [ 'a', 'b', 'c' ],
|
|
|
|
|
|
ApiBase::PARAM_ALL => true,
|
|
|
|
|
|
],
|
|
|
|
|
|
[ 'a', 'b', 'c' ],
|
|
|
|
|
|
[],
|
|
|
|
|
|
],
|
|
|
|
|
|
'Wildcard "*" with multiples not allowed' => [
|
|
|
|
|
|
'*',
|
|
|
|
|
|
[
|
|
|
|
|
|
ApiBase::PARAM_TYPE => [ 'a', 'b', 'c' ],
|
|
|
|
|
|
ApiBase::PARAM_ALL => true,
|
|
|
|
|
|
],
|
|
|
|
|
|
ApiUsageException::newWithMessage( null,
|
|
|
|
|
|
[ 'apierror-unrecognizedvalue', 'myParam', '*' ],
|
|
|
|
|
|
'unknown_myParam' ),
|
|
|
|
|
|
[],
|
|
|
|
|
|
],
|
|
|
|
|
|
'Wildcard "*" with unrestricted type' => [
|
|
|
|
|
|
'*',
|
|
|
|
|
|
[
|
|
|
|
|
|
ApiBase::PARAM_ISMULTI => true,
|
|
|
|
|
|
ApiBase::PARAM_ALL => true,
|
|
|
|
|
|
],
|
|
|
|
|
|
[ '*' ],
|
|
|
|
|
|
[],
|
|
|
|
|
|
],
|
|
|
|
|
|
'Wildcard "x"' => [
|
|
|
|
|
|
'x',
|
|
|
|
|
|
[
|
|
|
|
|
|
ApiBase::PARAM_ISMULTI => true,
|
|
|
|
|
|
ApiBase::PARAM_TYPE => [ 'a', 'b', 'c' ],
|
|
|
|
|
|
ApiBase::PARAM_ALL => 'x',
|
|
|
|
|
|
],
|
|
|
|
|
|
[ 'a', 'b', 'c' ],
|
|
|
|
|
|
[],
|
|
|
|
|
|
],
|
|
|
|
|
|
'Wildcard conflicting with allowed value' => [
|
|
|
|
|
|
'a',
|
|
|
|
|
|
[
|
|
|
|
|
|
ApiBase::PARAM_ISMULTI => true,
|
|
|
|
|
|
ApiBase::PARAM_TYPE => [ 'a', 'b', 'c' ],
|
|
|
|
|
|
ApiBase::PARAM_ALL => 'a',
|
|
|
|
|
|
],
|
|
|
|
|
|
new MWException(
|
|
|
|
|
|
'Internal error in ApiBase::getParameterFromSettings: ' .
|
|
|
|
|
|
'For param myParam, PARAM_ALL collides with a possible ' .
|
|
|
|
|
|
'value' ),
|
|
|
|
|
|
[],
|
|
|
|
|
|
],
|
|
|
|
|
|
'Namespace with wildcard' => [
|
|
|
|
|
|
'*',
|
|
|
|
|
|
[
|
|
|
|
|
|
ApiBase::PARAM_ISMULTI => true,
|
|
|
|
|
|
ApiBase::PARAM_TYPE => 'namespace',
|
|
|
|
|
|
],
|
2018-08-05 17:58:51 +00:00
|
|
|
|
MediaWikiServices::getInstance()->getNamespaceInfo()->getValidNamespaces(),
|
2018-03-20 17:34:03 +00:00
|
|
|
|
[],
|
|
|
|
|
|
],
|
|
|
|
|
|
// PARAM_ALL is ignored with namespace types.
|
|
|
|
|
|
'Namespace with wildcard suppressed' => [
|
|
|
|
|
|
'*',
|
|
|
|
|
|
[
|
|
|
|
|
|
ApiBase::PARAM_ISMULTI => true,
|
|
|
|
|
|
ApiBase::PARAM_TYPE => 'namespace',
|
|
|
|
|
|
ApiBase::PARAM_ALL => false,
|
|
|
|
|
|
],
|
2018-08-05 17:58:51 +00:00
|
|
|
|
MediaWikiServices::getInstance()->getNamespaceInfo()->getValidNamespaces(),
|
2018-03-20 17:34:03 +00:00
|
|
|
|
[],
|
|
|
|
|
|
],
|
|
|
|
|
|
'Namespace with wildcard "x"' => [
|
|
|
|
|
|
'x',
|
|
|
|
|
|
[
|
|
|
|
|
|
ApiBase::PARAM_ISMULTI => true,
|
|
|
|
|
|
ApiBase::PARAM_TYPE => 'namespace',
|
|
|
|
|
|
ApiBase::PARAM_ALL => 'x',
|
|
|
|
|
|
],
|
|
|
|
|
|
[],
|
|
|
|
|
|
[ [ 'apiwarn-unrecognizedvalues', 'myParam',
|
|
|
|
|
|
[ 'list' => [ 'x' ], 'type' => 'comma' ], 1 ] ],
|
|
|
|
|
|
],
|
|
|
|
|
|
'Password' => [
|
|
|
|
|
|
'dDy+G?e?txnr.1:(@[Ru',
|
|
|
|
|
|
[ ApiBase::PARAM_TYPE => 'password' ],
|
|
|
|
|
|
'dDy+G?e?txnr.1:(@[Ru',
|
|
|
|
|
|
[],
|
|
|
|
|
|
],
|
|
|
|
|
|
'Sensitive field' => [
|
|
|
|
|
|
'I am fond of pineapples',
|
|
|
|
|
|
[ ApiBase::PARAM_SENSITIVE => true ],
|
|
|
|
|
|
'I am fond of pineapples',
|
|
|
|
|
|
[],
|
|
|
|
|
|
],
|
|
|
|
|
|
'Upload with default' => [
|
|
|
|
|
|
'',
|
|
|
|
|
|
[
|
|
|
|
|
|
ApiBase::PARAM_TYPE => 'upload',
|
|
|
|
|
|
ApiBase::PARAM_DFLT => '',
|
|
|
|
|
|
],
|
|
|
|
|
|
new MWException(
|
|
|
|
|
|
'Internal error in ApiBase::getParameterFromSettings: ' .
|
|
|
|
|
|
"File upload param myParam's default is set to ''. " .
|
|
|
|
|
|
'File upload parameters may not have a default.' ),
|
|
|
|
|
|
[],
|
|
|
|
|
|
],
|
|
|
|
|
|
'Multiple upload' => [
|
|
|
|
|
|
'',
|
|
|
|
|
|
[
|
|
|
|
|
|
ApiBase::PARAM_TYPE => 'upload',
|
|
|
|
|
|
ApiBase::PARAM_ISMULTI => true,
|
|
|
|
|
|
],
|
|
|
|
|
|
new MWException(
|
|
|
|
|
|
'Internal error in ApiBase::getParameterFromSettings: ' .
|
|
|
|
|
|
'Multi-values not supported for myParam' ),
|
|
|
|
|
|
[],
|
|
|
|
|
|
],
|
|
|
|
|
|
// @todo Test actual upload
|
|
|
|
|
|
'Namespace -1' => [
|
|
|
|
|
|
'-1',
|
|
|
|
|
|
[ ApiBase::PARAM_TYPE => 'namespace' ],
|
|
|
|
|
|
ApiUsageException::newWithMessage( null,
|
|
|
|
|
|
[ 'apierror-unrecognizedvalue', 'myParam', '-1' ],
|
|
|
|
|
|
'unknown_myParam' ),
|
|
|
|
|
|
[],
|
|
|
|
|
|
],
|
|
|
|
|
|
'Extra namespace -1' => [
|
|
|
|
|
|
'-1',
|
|
|
|
|
|
[
|
|
|
|
|
|
ApiBase::PARAM_TYPE => 'namespace',
|
|
|
|
|
|
ApiBase::PARAM_EXTRA_NAMESPACES => [ '-1' ],
|
|
|
|
|
|
],
|
|
|
|
|
|
'-1',
|
|
|
|
|
|
[],
|
|
|
|
|
|
],
|
|
|
|
|
|
// @todo Test with PARAM_SUBMODULE_MAP unset, need
|
|
|
|
|
|
// getModuleManager() to return something real
|
|
|
|
|
|
'Nonexistent module' => [
|
|
|
|
|
|
'not-a-module-name',
|
|
|
|
|
|
[
|
|
|
|
|
|
ApiBase::PARAM_TYPE => 'submodule',
|
|
|
|
|
|
ApiBase::PARAM_SUBMODULE_MAP =>
|
|
|
|
|
|
[ 'foo' => 'foo', 'bar' => 'foo+bar' ],
|
|
|
|
|
|
],
|
|
|
|
|
|
ApiUsageException::newWithMessage(
|
|
|
|
|
|
null,
|
|
|
|
|
|
[
|
|
|
|
|
|
'apierror-unrecognizedvalue',
|
|
|
|
|
|
'myParam',
|
|
|
|
|
|
'not-a-module-name',
|
|
|
|
|
|
],
|
|
|
|
|
|
'unknown_myParam'
|
|
|
|
|
|
),
|
|
|
|
|
|
[],
|
|
|
|
|
|
],
|
|
|
|
|
|
'\\x1f with multiples not allowed' => [
|
|
|
|
|
|
"\x1f",
|
|
|
|
|
|
[],
|
|
|
|
|
|
ApiUsageException::newWithMessage( null,
|
|
|
|
|
|
'apierror-badvalue-notmultivalue',
|
|
|
|
|
|
'badvalue_notmultivalue' ),
|
|
|
|
|
|
[],
|
|
|
|
|
|
],
|
|
|
|
|
|
'Integer with unenforced min' => [
|
|
|
|
|
|
'-2',
|
|
|
|
|
|
[
|
|
|
|
|
|
ApiBase::PARAM_TYPE => 'integer',
|
|
|
|
|
|
ApiBase::PARAM_MIN => -1,
|
|
|
|
|
|
],
|
|
|
|
|
|
-1,
|
|
|
|
|
|
[ [ 'apierror-integeroutofrange-belowminimum', 'myParam', -1,
|
|
|
|
|
|
-2 ] ],
|
|
|
|
|
|
],
|
|
|
|
|
|
'Integer with enforced min' => [
|
|
|
|
|
|
'-2',
|
|
|
|
|
|
[
|
|
|
|
|
|
ApiBase::PARAM_TYPE => 'integer',
|
|
|
|
|
|
ApiBase::PARAM_MIN => -1,
|
|
|
|
|
|
ApiBase::PARAM_RANGE_ENFORCE => true,
|
|
|
|
|
|
],
|
|
|
|
|
|
ApiUsageException::newWithMessage( null,
|
|
|
|
|
|
[ 'apierror-integeroutofrange-belowminimum', 'myParam',
|
|
|
|
|
|
'-1', '-2' ], 'integeroutofrange',
|
|
|
|
|
|
[ 'min' => -1, 'max' => null, 'botMax' => null ] ),
|
|
|
|
|
|
[],
|
|
|
|
|
|
],
|
|
|
|
|
|
'Integer with unenforced max (internal mode)' => [
|
|
|
|
|
|
'8',
|
|
|
|
|
|
[
|
|
|
|
|
|
ApiBase::PARAM_TYPE => 'integer',
|
|
|
|
|
|
ApiBase::PARAM_MAX => 7,
|
|
|
|
|
|
],
|
|
|
|
|
|
8,
|
|
|
|
|
|
[],
|
|
|
|
|
|
],
|
|
|
|
|
|
'Integer with enforced max (internal mode)' => [
|
|
|
|
|
|
'8',
|
|
|
|
|
|
[
|
|
|
|
|
|
ApiBase::PARAM_TYPE => 'integer',
|
|
|
|
|
|
ApiBase::PARAM_MAX => 7,
|
|
|
|
|
|
ApiBase::PARAM_RANGE_ENFORCE => true,
|
|
|
|
|
|
],
|
|
|
|
|
|
8,
|
|
|
|
|
|
[],
|
|
|
|
|
|
],
|
|
|
|
|
|
'Integer with unenforced max (non-internal mode)' => [
|
|
|
|
|
|
'8',
|
|
|
|
|
|
[
|
|
|
|
|
|
ApiBase::PARAM_TYPE => 'integer',
|
|
|
|
|
|
ApiBase::PARAM_MAX => 7,
|
|
|
|
|
|
],
|
|
|
|
|
|
7,
|
|
|
|
|
|
[ [ 'apierror-integeroutofrange-abovemax', 'myParam', 7, 8 ] ],
|
|
|
|
|
|
[ 'internalmode' => false ],
|
|
|
|
|
|
],
|
|
|
|
|
|
'Integer with enforced max (non-internal mode)' => [
|
|
|
|
|
|
'8',
|
|
|
|
|
|
[
|
|
|
|
|
|
ApiBase::PARAM_TYPE => 'integer',
|
|
|
|
|
|
ApiBase::PARAM_MAX => 7,
|
|
|
|
|
|
ApiBase::PARAM_RANGE_ENFORCE => true,
|
|
|
|
|
|
],
|
|
|
|
|
|
ApiUsageException::newWithMessage(
|
|
|
|
|
|
null,
|
|
|
|
|
|
[ 'apierror-integeroutofrange-abovemax', 'myParam', '7', '8' ],
|
|
|
|
|
|
'integeroutofrange',
|
|
|
|
|
|
[ 'min' => null, 'max' => 7, 'botMax' => 7 ]
|
|
|
|
|
|
),
|
|
|
|
|
|
[],
|
|
|
|
|
|
[ 'internalmode' => false ],
|
|
|
|
|
|
],
|
|
|
|
|
|
'Array of integers' => [
|
|
|
|
|
|
'3|12|966|-1',
|
|
|
|
|
|
[
|
|
|
|
|
|
ApiBase::PARAM_ISMULTI => true,
|
|
|
|
|
|
ApiBase::PARAM_TYPE => 'integer',
|
|
|
|
|
|
],
|
|
|
|
|
|
[ 3, 12, 966, -1 ],
|
|
|
|
|
|
[],
|
|
|
|
|
|
],
|
|
|
|
|
|
'Array of integers with unenforced min/max (internal mode)' => [
|
|
|
|
|
|
'3|12|966|-1',
|
|
|
|
|
|
[
|
|
|
|
|
|
ApiBase::PARAM_ISMULTI => true,
|
|
|
|
|
|
ApiBase::PARAM_TYPE => 'integer',
|
|
|
|
|
|
ApiBase::PARAM_MIN => 0,
|
|
|
|
|
|
ApiBase::PARAM_MAX => 100,
|
|
|
|
|
|
],
|
|
|
|
|
|
[ 3, 12, 966, 0 ],
|
|
|
|
|
|
[ [ 'apierror-integeroutofrange-belowminimum', 'myParam', 0, -1 ] ],
|
|
|
|
|
|
],
|
|
|
|
|
|
'Array of integers with enforced min/max (internal mode)' => [
|
|
|
|
|
|
'3|12|966|-1',
|
|
|
|
|
|
[
|
|
|
|
|
|
ApiBase::PARAM_ISMULTI => true,
|
|
|
|
|
|
ApiBase::PARAM_TYPE => 'integer',
|
|
|
|
|
|
ApiBase::PARAM_MIN => 0,
|
|
|
|
|
|
ApiBase::PARAM_MAX => 100,
|
|
|
|
|
|
ApiBase::PARAM_RANGE_ENFORCE => true,
|
|
|
|
|
|
],
|
|
|
|
|
|
ApiUsageException::newWithMessage(
|
|
|
|
|
|
null,
|
|
|
|
|
|
[ 'apierror-integeroutofrange-belowminimum', 'myParam', 0, -1 ],
|
|
|
|
|
|
'integeroutofrange',
|
|
|
|
|
|
[ 'min' => 0, 'max' => 100, 'botMax' => 100 ]
|
|
|
|
|
|
),
|
|
|
|
|
|
[],
|
|
|
|
|
|
],
|
|
|
|
|
|
'Array of integers with unenforced min/max (non-internal mode)' => [
|
|
|
|
|
|
'3|12|966|-1',
|
|
|
|
|
|
[
|
|
|
|
|
|
ApiBase::PARAM_ISMULTI => true,
|
|
|
|
|
|
ApiBase::PARAM_TYPE => 'integer',
|
|
|
|
|
|
ApiBase::PARAM_MIN => 0,
|
|
|
|
|
|
ApiBase::PARAM_MAX => 100,
|
|
|
|
|
|
],
|
|
|
|
|
|
[ 3, 12, 100, 0 ],
|
|
|
|
|
|
[
|
|
|
|
|
|
[ 'apierror-integeroutofrange-abovemax', 'myParam', 100, 966 ],
|
|
|
|
|
|
[ 'apierror-integeroutofrange-belowminimum', 'myParam', 0, -1 ]
|
|
|
|
|
|
],
|
|
|
|
|
|
[ 'internalmode' => false ],
|
|
|
|
|
|
],
|
|
|
|
|
|
'Array of integers with enforced min/max (non-internal mode)' => [
|
|
|
|
|
|
'3|12|966|-1',
|
|
|
|
|
|
[
|
|
|
|
|
|
ApiBase::PARAM_ISMULTI => true,
|
|
|
|
|
|
ApiBase::PARAM_TYPE => 'integer',
|
|
|
|
|
|
ApiBase::PARAM_MIN => 0,
|
|
|
|
|
|
ApiBase::PARAM_MAX => 100,
|
|
|
|
|
|
ApiBase::PARAM_RANGE_ENFORCE => true,
|
|
|
|
|
|
],
|
|
|
|
|
|
ApiUsageException::newWithMessage(
|
|
|
|
|
|
null,
|
|
|
|
|
|
[ 'apierror-integeroutofrange-abovemax', 'myParam', 100, 966 ],
|
|
|
|
|
|
'integeroutofrange',
|
|
|
|
|
|
[ 'min' => 0, 'max' => 100, 'botMax' => 100 ]
|
|
|
|
|
|
),
|
|
|
|
|
|
[],
|
|
|
|
|
|
[ 'internalmode' => false ],
|
|
|
|
|
|
],
|
2019-08-30 17:21:34 +00:00
|
|
|
|
'Limit with parseLimits false (numeric)' => [
|
2018-03-20 17:34:03 +00:00
|
|
|
|
'100',
|
|
|
|
|
|
[ ApiBase::PARAM_TYPE => 'limit' ],
|
2019-08-30 17:21:34 +00:00
|
|
|
|
100,
|
|
|
|
|
|
[],
|
|
|
|
|
|
[ 'parseLimits' => false ],
|
|
|
|
|
|
],
|
|
|
|
|
|
'Limit with parseLimits false (max)' => [
|
|
|
|
|
|
'max',
|
|
|
|
|
|
[ ApiBase::PARAM_TYPE => 'limit' ],
|
|
|
|
|
|
'max',
|
|
|
|
|
|
[],
|
|
|
|
|
|
[ 'parseLimits' => false ],
|
|
|
|
|
|
],
|
|
|
|
|
|
'Limit with parseLimits false (invalid)' => [
|
|
|
|
|
|
'kitten',
|
|
|
|
|
|
[ ApiBase::PARAM_TYPE => 'limit' ],
|
|
|
|
|
|
0,
|
2018-03-20 17:34:03 +00:00
|
|
|
|
[],
|
|
|
|
|
|
[ 'parseLimits' => false ],
|
|
|
|
|
|
],
|
|
|
|
|
|
'Limit with no max' => [
|
|
|
|
|
|
'100',
|
|
|
|
|
|
[
|
|
|
|
|
|
ApiBase::PARAM_TYPE => 'limit',
|
|
|
|
|
|
ApiBase::PARAM_MAX2 => 10,
|
|
|
|
|
|
],
|
|
|
|
|
|
new MWException(
|
|
|
|
|
|
'Internal error in ApiBase::getParameterFromSettings: ' .
|
|
|
|
|
|
'MAX1 or MAX2 are not defined for the limit myParam' ),
|
|
|
|
|
|
[],
|
|
|
|
|
|
],
|
|
|
|
|
|
'Limit with no max2' => [
|
|
|
|
|
|
'100',
|
|
|
|
|
|
[
|
|
|
|
|
|
ApiBase::PARAM_TYPE => 'limit',
|
|
|
|
|
|
ApiBase::PARAM_MAX => 10,
|
|
|
|
|
|
],
|
|
|
|
|
|
new MWException(
|
|
|
|
|
|
'Internal error in ApiBase::getParameterFromSettings: ' .
|
|
|
|
|
|
'MAX1 or MAX2 are not defined for the limit myParam' ),
|
|
|
|
|
|
[],
|
|
|
|
|
|
],
|
|
|
|
|
|
'Limit with multi-value' => [
|
|
|
|
|
|
'100',
|
|
|
|
|
|
[
|
|
|
|
|
|
ApiBase::PARAM_TYPE => 'limit',
|
|
|
|
|
|
ApiBase::PARAM_MAX => 10,
|
|
|
|
|
|
ApiBase::PARAM_MAX2 => 10,
|
|
|
|
|
|
ApiBase::PARAM_ISMULTI => true,
|
|
|
|
|
|
],
|
|
|
|
|
|
new MWException(
|
|
|
|
|
|
'Internal error in ApiBase::getParameterFromSettings: ' .
|
|
|
|
|
|
'Multi-values not supported for myParam' ),
|
|
|
|
|
|
[],
|
|
|
|
|
|
],
|
|
|
|
|
|
'Valid limit' => [
|
|
|
|
|
|
'100',
|
|
|
|
|
|
[
|
|
|
|
|
|
ApiBase::PARAM_TYPE => 'limit',
|
|
|
|
|
|
ApiBase::PARAM_MAX => 100,
|
|
|
|
|
|
ApiBase::PARAM_MAX2 => 100,
|
|
|
|
|
|
],
|
|
|
|
|
|
100,
|
|
|
|
|
|
[],
|
|
|
|
|
|
],
|
|
|
|
|
|
'Limit max' => [
|
|
|
|
|
|
'max',
|
|
|
|
|
|
[
|
|
|
|
|
|
ApiBase::PARAM_TYPE => 'limit',
|
|
|
|
|
|
ApiBase::PARAM_MAX => 100,
|
|
|
|
|
|
ApiBase::PARAM_MAX2 => 101,
|
|
|
|
|
|
],
|
|
|
|
|
|
100,
|
|
|
|
|
|
[],
|
|
|
|
|
|
],
|
|
|
|
|
|
'Limit max for apihighlimits' => [
|
|
|
|
|
|
'max',
|
|
|
|
|
|
[
|
|
|
|
|
|
ApiBase::PARAM_TYPE => 'limit',
|
|
|
|
|
|
ApiBase::PARAM_MAX => 100,
|
|
|
|
|
|
ApiBase::PARAM_MAX2 => 101,
|
|
|
|
|
|
],
|
|
|
|
|
|
101,
|
|
|
|
|
|
[],
|
|
|
|
|
|
[ 'apihighlimits' => true ],
|
|
|
|
|
|
],
|
|
|
|
|
|
'Limit too large (internal mode)' => [
|
|
|
|
|
|
'101',
|
|
|
|
|
|
[
|
|
|
|
|
|
ApiBase::PARAM_TYPE => 'limit',
|
|
|
|
|
|
ApiBase::PARAM_MAX => 100,
|
|
|
|
|
|
ApiBase::PARAM_MAX2 => 101,
|
|
|
|
|
|
],
|
|
|
|
|
|
101,
|
|
|
|
|
|
[],
|
|
|
|
|
|
],
|
|
|
|
|
|
'Limit okay for apihighlimits (internal mode)' => [
|
|
|
|
|
|
'101',
|
|
|
|
|
|
[
|
|
|
|
|
|
ApiBase::PARAM_TYPE => 'limit',
|
|
|
|
|
|
ApiBase::PARAM_MAX => 100,
|
|
|
|
|
|
ApiBase::PARAM_MAX2 => 101,
|
|
|
|
|
|
],
|
|
|
|
|
|
101,
|
|
|
|
|
|
[],
|
|
|
|
|
|
[ 'apihighlimits' => true ],
|
|
|
|
|
|
],
|
|
|
|
|
|
'Limit too large for apihighlimits (internal mode)' => [
|
|
|
|
|
|
'102',
|
|
|
|
|
|
[
|
|
|
|
|
|
ApiBase::PARAM_TYPE => 'limit',
|
|
|
|
|
|
ApiBase::PARAM_MAX => 100,
|
|
|
|
|
|
ApiBase::PARAM_MAX2 => 101,
|
|
|
|
|
|
],
|
|
|
|
|
|
102,
|
|
|
|
|
|
[],
|
|
|
|
|
|
[ 'apihighlimits' => true ],
|
|
|
|
|
|
],
|
|
|
|
|
|
'Limit too large (non-internal mode)' => [
|
|
|
|
|
|
'101',
|
|
|
|
|
|
[
|
|
|
|
|
|
ApiBase::PARAM_TYPE => 'limit',
|
|
|
|
|
|
ApiBase::PARAM_MAX => 100,
|
|
|
|
|
|
ApiBase::PARAM_MAX2 => 101,
|
|
|
|
|
|
],
|
|
|
|
|
|
100,
|
|
|
|
|
|
[ [ 'apierror-integeroutofrange-abovemax', 'myParam', 100, 101 ] ],
|
|
|
|
|
|
[ 'internalmode' => false ],
|
|
|
|
|
|
],
|
|
|
|
|
|
'Limit okay for apihighlimits (non-internal mode)' => [
|
|
|
|
|
|
'101',
|
|
|
|
|
|
[
|
|
|
|
|
|
ApiBase::PARAM_TYPE => 'limit',
|
|
|
|
|
|
ApiBase::PARAM_MAX => 100,
|
|
|
|
|
|
ApiBase::PARAM_MAX2 => 101,
|
|
|
|
|
|
],
|
|
|
|
|
|
101,
|
|
|
|
|
|
[],
|
|
|
|
|
|
[ 'internalmode' => false, 'apihighlimits' => true ],
|
|
|
|
|
|
],
|
|
|
|
|
|
'Limit too large for apihighlimits (non-internal mode)' => [
|
|
|
|
|
|
'102',
|
|
|
|
|
|
[
|
|
|
|
|
|
ApiBase::PARAM_TYPE => 'limit',
|
|
|
|
|
|
ApiBase::PARAM_MAX => 100,
|
|
|
|
|
|
ApiBase::PARAM_MAX2 => 101,
|
|
|
|
|
|
],
|
|
|
|
|
|
101,
|
|
|
|
|
|
[ [ 'apierror-integeroutofrange-abovebotmax', 'myParam', 101, 102 ] ],
|
|
|
|
|
|
[ 'internalmode' => false, 'apihighlimits' => true ],
|
|
|
|
|
|
],
|
|
|
|
|
|
'Limit too small' => [
|
|
|
|
|
|
'-2',
|
|
|
|
|
|
[
|
|
|
|
|
|
ApiBase::PARAM_TYPE => 'limit',
|
|
|
|
|
|
ApiBase::PARAM_MIN => -1,
|
|
|
|
|
|
ApiBase::PARAM_MAX => 100,
|
|
|
|
|
|
ApiBase::PARAM_MAX2 => 100,
|
|
|
|
|
|
],
|
|
|
|
|
|
-1,
|
|
|
|
|
|
[ [ 'apierror-integeroutofrange-belowminimum', 'myParam', -1,
|
|
|
|
|
|
-2 ] ],
|
|
|
|
|
|
],
|
|
|
|
|
|
'Timestamp' => [
|
|
|
|
|
|
wfTimestamp( TS_UNIX, '20211221122112' ),
|
|
|
|
|
|
[ ApiBase::PARAM_TYPE => 'timestamp' ],
|
|
|
|
|
|
'20211221122112',
|
|
|
|
|
|
[],
|
|
|
|
|
|
],
|
|
|
|
|
|
'Timestamp 0' => [
|
|
|
|
|
|
'0',
|
|
|
|
|
|
[ ApiBase::PARAM_TYPE => 'timestamp' ],
|
|
|
|
|
|
// Magic keyword
|
|
|
|
|
|
'now',
|
|
|
|
|
|
[ [ 'apiwarn-unclearnowtimestamp', 'myParam', '0' ] ],
|
|
|
|
|
|
],
|
|
|
|
|
|
'Timestamp empty' => [
|
|
|
|
|
|
'',
|
|
|
|
|
|
[ ApiBase::PARAM_TYPE => 'timestamp' ],
|
|
|
|
|
|
'now',
|
|
|
|
|
|
[ [ 'apiwarn-unclearnowtimestamp', 'myParam', '' ] ],
|
|
|
|
|
|
],
|
|
|
|
|
|
// wfTimestamp() interprets this as Unix time
|
|
|
|
|
|
'Timestamp 00' => [
|
|
|
|
|
|
'00',
|
|
|
|
|
|
[ ApiBase::PARAM_TYPE => 'timestamp' ],
|
|
|
|
|
|
'19700101000000',
|
|
|
|
|
|
[],
|
|
|
|
|
|
],
|
|
|
|
|
|
'Timestamp now' => [
|
|
|
|
|
|
'now',
|
|
|
|
|
|
[ ApiBase::PARAM_TYPE => 'timestamp' ],
|
|
|
|
|
|
'now',
|
|
|
|
|
|
[],
|
|
|
|
|
|
],
|
|
|
|
|
|
'Invalid timestamp' => [
|
|
|
|
|
|
'a potato',
|
|
|
|
|
|
[ ApiBase::PARAM_TYPE => 'timestamp' ],
|
|
|
|
|
|
ApiUsageException::newWithMessage(
|
|
|
|
|
|
null,
|
|
|
|
|
|
[ 'apierror-badtimestamp', 'myParam', 'a potato' ],
|
|
|
|
|
|
'badtimestamp_myParam'
|
|
|
|
|
|
),
|
|
|
|
|
|
[],
|
|
|
|
|
|
],
|
|
|
|
|
|
'Timestamp array' => [
|
|
|
|
|
|
'100|101',
|
|
|
|
|
|
[
|
|
|
|
|
|
ApiBase::PARAM_TYPE => 'timestamp',
|
|
|
|
|
|
ApiBase::PARAM_ISMULTI => 1,
|
|
|
|
|
|
],
|
|
|
|
|
|
[ wfTimestamp( TS_MW, 100 ), wfTimestamp( TS_MW, 101 ) ],
|
|
|
|
|
|
[],
|
|
|
|
|
|
],
|
|
|
|
|
|
'User' => [
|
|
|
|
|
|
'foo_bar',
|
|
|
|
|
|
[ ApiBase::PARAM_TYPE => 'user' ],
|
|
|
|
|
|
'Foo bar',
|
|
|
|
|
|
[],
|
|
|
|
|
|
],
|
2018-05-18 09:59:04 +00:00
|
|
|
|
'User prefixed with "User:"' => [
|
|
|
|
|
|
'User:foo_bar',
|
|
|
|
|
|
[ ApiBase::PARAM_TYPE => 'user' ],
|
|
|
|
|
|
'Foo bar',
|
|
|
|
|
|
[],
|
|
|
|
|
|
],
|
2018-03-20 17:34:03 +00:00
|
|
|
|
'Invalid username "|"' => [
|
|
|
|
|
|
'|',
|
|
|
|
|
|
[ ApiBase::PARAM_TYPE => 'user' ],
|
|
|
|
|
|
ApiUsageException::newWithMessage( null,
|
|
|
|
|
|
[ 'apierror-baduser', 'myParam', '|' ],
|
|
|
|
|
|
'baduser_myParam' ),
|
|
|
|
|
|
[],
|
|
|
|
|
|
],
|
|
|
|
|
|
'Invalid username "300.300.300.300"' => [
|
|
|
|
|
|
'300.300.300.300',
|
|
|
|
|
|
[ ApiBase::PARAM_TYPE => 'user' ],
|
|
|
|
|
|
ApiUsageException::newWithMessage( null,
|
|
|
|
|
|
[ 'apierror-baduser', 'myParam', '300.300.300.300' ],
|
|
|
|
|
|
'baduser_myParam' ),
|
|
|
|
|
|
[],
|
|
|
|
|
|
],
|
|
|
|
|
|
'IP range as username' => [
|
|
|
|
|
|
'10.0.0.0/8',
|
|
|
|
|
|
[ ApiBase::PARAM_TYPE => 'user' ],
|
|
|
|
|
|
'10.0.0.0/8',
|
|
|
|
|
|
[],
|
|
|
|
|
|
],
|
|
|
|
|
|
'IPv6 as username' => [
|
|
|
|
|
|
'::1',
|
|
|
|
|
|
[ ApiBase::PARAM_TYPE => 'user' ],
|
|
|
|
|
|
'0:0:0:0:0:0:0:1',
|
|
|
|
|
|
[],
|
|
|
|
|
|
],
|
|
|
|
|
|
'Obsolete cloaked usemod IP address as username' => [
|
|
|
|
|
|
'1.2.3.xxx',
|
|
|
|
|
|
[ ApiBase::PARAM_TYPE => 'user' ],
|
|
|
|
|
|
'1.2.3.xxx',
|
|
|
|
|
|
[],
|
|
|
|
|
|
],
|
|
|
|
|
|
'Invalid username containing IP address' => [
|
|
|
|
|
|
'This is [not] valid 1.2.3.xxx, ha!',
|
|
|
|
|
|
[ ApiBase::PARAM_TYPE => 'user' ],
|
|
|
|
|
|
ApiUsageException::newWithMessage(
|
|
|
|
|
|
null,
|
|
|
|
|
|
[ 'apierror-baduser', 'myParam', 'This is [not] valid 1.2.3.xxx, ha!' ],
|
|
|
|
|
|
'baduser_myParam'
|
|
|
|
|
|
),
|
|
|
|
|
|
[],
|
|
|
|
|
|
],
|
|
|
|
|
|
'External username' => [
|
|
|
|
|
|
'M>Foo bar',
|
|
|
|
|
|
[ ApiBase::PARAM_TYPE => 'user' ],
|
|
|
|
|
|
'M>Foo bar',
|
|
|
|
|
|
[],
|
|
|
|
|
|
],
|
|
|
|
|
|
'Array of usernames' => [
|
|
|
|
|
|
'foo|bar',
|
|
|
|
|
|
[
|
|
|
|
|
|
ApiBase::PARAM_TYPE => 'user',
|
|
|
|
|
|
ApiBase::PARAM_ISMULTI => true,
|
|
|
|
|
|
],
|
|
|
|
|
|
[ 'Foo', 'Bar' ],
|
|
|
|
|
|
[],
|
|
|
|
|
|
],
|
|
|
|
|
|
'tag' => [
|
|
|
|
|
|
'tag1',
|
|
|
|
|
|
[ ApiBase::PARAM_TYPE => 'tags' ],
|
|
|
|
|
|
[ 'tag1' ],
|
|
|
|
|
|
[],
|
|
|
|
|
|
],
|
|
|
|
|
|
'Array of one tag' => [
|
|
|
|
|
|
'tag1',
|
|
|
|
|
|
[
|
|
|
|
|
|
ApiBase::PARAM_TYPE => 'tags',
|
|
|
|
|
|
ApiBase::PARAM_ISMULTI => true,
|
|
|
|
|
|
],
|
|
|
|
|
|
[ 'tag1' ],
|
|
|
|
|
|
[],
|
|
|
|
|
|
],
|
|
|
|
|
|
'Array of tags' => [
|
|
|
|
|
|
'tag1|tag2',
|
|
|
|
|
|
[
|
|
|
|
|
|
ApiBase::PARAM_TYPE => 'tags',
|
|
|
|
|
|
ApiBase::PARAM_ISMULTI => true,
|
|
|
|
|
|
],
|
|
|
|
|
|
[ 'tag1', 'tag2' ],
|
|
|
|
|
|
[],
|
|
|
|
|
|
],
|
|
|
|
|
|
'Invalid tag' => [
|
|
|
|
|
|
'invalid tag',
|
|
|
|
|
|
[ ApiBase::PARAM_TYPE => 'tags' ],
|
|
|
|
|
|
new ApiUsageException( null,
|
|
|
|
|
|
Status::newFatal( 'tags-apply-not-allowed-one',
|
|
|
|
|
|
'invalid tag', 1 ) ),
|
|
|
|
|
|
[],
|
|
|
|
|
|
],
|
|
|
|
|
|
'Unrecognized type' => [
|
|
|
|
|
|
'foo',
|
|
|
|
|
|
[ ApiBase::PARAM_TYPE => 'nonexistenttype' ],
|
|
|
|
|
|
new MWException(
|
|
|
|
|
|
'Internal error in ApiBase::getParameterFromSettings: ' .
|
|
|
|
|
|
"Param myParam's type is unknown - nonexistenttype" ),
|
|
|
|
|
|
[],
|
|
|
|
|
|
],
|
|
|
|
|
|
'Too many bytes' => [
|
|
|
|
|
|
'1',
|
|
|
|
|
|
[
|
|
|
|
|
|
ApiBase::PARAM_MAX_BYTES => 0,
|
|
|
|
|
|
ApiBase::PARAM_MAX_CHARS => 0,
|
|
|
|
|
|
],
|
|
|
|
|
|
ApiUsageException::newWithMessage( null,
|
|
|
|
|
|
[ 'apierror-maxbytes', 'myParam', 0 ] ),
|
|
|
|
|
|
[],
|
|
|
|
|
|
],
|
|
|
|
|
|
'Too many chars' => [
|
|
|
|
|
|
'§§',
|
|
|
|
|
|
[
|
|
|
|
|
|
ApiBase::PARAM_MAX_BYTES => 4,
|
|
|
|
|
|
ApiBase::PARAM_MAX_CHARS => 1,
|
|
|
|
|
|
],
|
|
|
|
|
|
ApiUsageException::newWithMessage( null,
|
|
|
|
|
|
[ 'apierror-maxchars', 'myParam', 1 ] ),
|
|
|
|
|
|
[],
|
|
|
|
|
|
],
|
|
|
|
|
|
'Omitted required param' => [
|
|
|
|
|
|
null,
|
|
|
|
|
|
[ ApiBase::PARAM_REQUIRED => true ],
|
|
|
|
|
|
ApiUsageException::newWithMessage( null,
|
|
|
|
|
|
[ 'apierror-missingparam', 'myParam' ] ),
|
|
|
|
|
|
[],
|
|
|
|
|
|
],
|
|
|
|
|
|
'Empty multi-value' => [
|
|
|
|
|
|
'',
|
|
|
|
|
|
[ ApiBase::PARAM_ISMULTI => true ],
|
|
|
|
|
|
[],
|
|
|
|
|
|
[],
|
|
|
|
|
|
],
|
|
|
|
|
|
'Multi-value \x1f' => [
|
|
|
|
|
|
"\x1f",
|
|
|
|
|
|
[ ApiBase::PARAM_ISMULTI => true ],
|
|
|
|
|
|
[],
|
|
|
|
|
|
[],
|
|
|
|
|
|
],
|
|
|
|
|
|
'Allowed non-multi-value with "|"' => [
|
|
|
|
|
|
'a|b',
|
|
|
|
|
|
[ ApiBase::PARAM_TYPE => [ 'a|b' ] ],
|
|
|
|
|
|
'a|b',
|
|
|
|
|
|
[],
|
|
|
|
|
|
],
|
|
|
|
|
|
'Prohibited multi-value' => [
|
|
|
|
|
|
'a|b',
|
|
|
|
|
|
[ ApiBase::PARAM_TYPE => [ 'a', 'b' ] ],
|
|
|
|
|
|
ApiUsageException::newWithMessage( null,
|
|
|
|
|
|
[
|
|
|
|
|
|
'apierror-multival-only-one-of',
|
|
|
|
|
|
'myParam',
|
|
|
|
|
|
Message::listParam( [ '<kbd>a</kbd>', '<kbd>b</kbd>' ] ),
|
|
|
|
|
|
2
|
|
|
|
|
|
],
|
|
|
|
|
|
'multival_myParam'
|
|
|
|
|
|
),
|
|
|
|
|
|
[],
|
|
|
|
|
|
],
|
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
|
|
// The following really just test PHP's string-to-int conversion.
|
|
|
|
|
|
$integerTests = [
|
|
|
|
|
|
[ '+1', 1 ],
|
|
|
|
|
|
[ '-1', -1 ],
|
|
|
|
|
|
[ '1.5', 1 ],
|
|
|
|
|
|
[ '-1.5', -1 ],
|
|
|
|
|
|
[ '1abc', 1 ],
|
|
|
|
|
|
[ ' 1', 1 ],
|
|
|
|
|
|
[ "\t1", 1, '\t1' ],
|
|
|
|
|
|
[ "\r1", 1, '\r1' ],
|
|
|
|
|
|
[ "\f1", 0, '\f1', 'badutf-8' ],
|
|
|
|
|
|
[ "\n1", 1, '\n1' ],
|
|
|
|
|
|
[ "\v1", 0, '\v1', 'badutf-8' ],
|
|
|
|
|
|
[ "\e1", 0, '\e1', 'badutf-8' ],
|
|
|
|
|
|
[ "\x001", 0, '\x001', 'badutf-8' ],
|
2016-08-16 20:36:27 +00:00
|
|
|
|
];
|
2018-03-20 17:34:03 +00:00
|
|
|
|
|
|
|
|
|
|
foreach ( $integerTests as $test ) {
|
2017-10-06 22:17:58 +00:00
|
|
|
|
$desc = $test[2] ?? $test[0];
|
2018-03-20 17:34:03 +00:00
|
|
|
|
$warnings = isset( $test[3] ) ?
|
|
|
|
|
|
[ [ 'apiwarn-badutf8', 'myParam' ] ] : [];
|
|
|
|
|
|
$returnArray["\"$desc\" as integer"] = [
|
|
|
|
|
|
$test[0],
|
|
|
|
|
|
[ ApiBase::PARAM_TYPE => 'integer' ],
|
|
|
|
|
|
$test[1],
|
|
|
|
|
|
$warnings,
|
|
|
|
|
|
];
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return $returnArray;
|
2016-08-16 20:36:27 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2016-10-19 16:54:25 +00:00
|
|
|
|
public function testErrorArrayToStatus() {
|
|
|
|
|
|
$mock = new MockApi();
|
|
|
|
|
|
|
2019-03-01 14:49:05 +00:00
|
|
|
|
$msg = new Message( 'mainpage' );
|
|
|
|
|
|
|
2016-10-19 16:54:25 +00:00
|
|
|
|
// Sanity check empty array
|
|
|
|
|
|
$expect = Status::newGood();
|
|
|
|
|
|
$this->assertEquals( $expect, $mock->errorArrayToStatus( [] ) );
|
|
|
|
|
|
|
|
|
|
|
|
// No blocked $user, so no special block handling
|
|
|
|
|
|
$expect = Status::newGood();
|
|
|
|
|
|
$expect->fatal( 'blockedtext' );
|
|
|
|
|
|
$expect->fatal( 'autoblockedtext' );
|
2016-12-01 16:51:03 +00:00
|
|
|
|
$expect->fatal( 'systemblockedtext' );
|
2016-10-19 16:54:25 +00:00
|
|
|
|
$expect->fatal( 'mainpage' );
|
2019-03-01 14:49:05 +00:00
|
|
|
|
$expect->fatal( $msg );
|
|
|
|
|
|
$expect->fatal( $msg, 'foobar' );
|
2016-10-19 16:54:25 +00:00
|
|
|
|
$expect->fatal( 'parentheses', 'foobar' );
|
|
|
|
|
|
$this->assertEquals( $expect, $mock->errorArrayToStatus( [
|
|
|
|
|
|
[ 'blockedtext' ],
|
|
|
|
|
|
[ 'autoblockedtext' ],
|
2016-12-01 16:51:03 +00:00
|
|
|
|
[ 'systemblockedtext' ],
|
2016-10-19 16:54:25 +00:00
|
|
|
|
'mainpage',
|
2019-03-01 14:49:05 +00:00
|
|
|
|
$msg,
|
|
|
|
|
|
[ $msg, 'foobar' ],
|
2016-10-19 16:54:25 +00:00
|
|
|
|
[ 'parentheses', 'foobar' ],
|
|
|
|
|
|
] ) );
|
|
|
|
|
|
|
|
|
|
|
|
// Has a blocked $user, so special block handling
|
|
|
|
|
|
$user = $this->getMutableTestUser()->getUser();
|
2019-05-13 14:18:07 +00:00
|
|
|
|
$block = new DatabaseBlock( [
|
2016-10-19 16:54:25 +00:00
|
|
|
|
'address' => $user->getName(),
|
|
|
|
|
|
'user' => $user->getID(),
|
2017-09-12 17:12:29 +00:00
|
|
|
|
'by' => $this->getTestSysop()->getUser()->getId(),
|
2016-10-19 16:54:25 +00:00
|
|
|
|
'reason' => __METHOD__,
|
|
|
|
|
|
'expiry' => time() + 100500,
|
|
|
|
|
|
] );
|
|
|
|
|
|
$block->insert();
|
2019-10-20 00:04:00 +00:00
|
|
|
|
|
|
|
|
|
|
$mockTrait = $this->getMockForTrait( ApiBlockInfoTrait::class );
|
|
|
|
|
|
$mockTrait->method( 'getLanguage' )->willReturn( 'en' );
|
|
|
|
|
|
$userInfoTrait = TestingAccessWrapper::newFromObject( $mockTrait );
|
2019-05-09 10:13:30 +00:00
|
|
|
|
$blockinfo = [ 'blockinfo' => $userInfoTrait->getBlockDetails( $block ) ];
|
2016-10-19 16:54:25 +00:00
|
|
|
|
|
|
|
|
|
|
$expect = Status::newGood();
|
|
|
|
|
|
$expect->fatal( ApiMessage::create( 'apierror-blocked', 'blocked', $blockinfo ) );
|
|
|
|
|
|
$expect->fatal( ApiMessage::create( 'apierror-autoblocked', 'autoblocked', $blockinfo ) );
|
2016-12-01 16:51:03 +00:00
|
|
|
|
$expect->fatal( ApiMessage::create( 'apierror-systemblocked', 'blocked', $blockinfo ) );
|
2016-10-19 16:54:25 +00:00
|
|
|
|
$expect->fatal( 'mainpage' );
|
2019-03-01 14:49:05 +00:00
|
|
|
|
$expect->fatal( $msg );
|
|
|
|
|
|
$expect->fatal( $msg, 'foobar' );
|
2016-10-19 16:54:25 +00:00
|
|
|
|
$expect->fatal( 'parentheses', 'foobar' );
|
|
|
|
|
|
$this->assertEquals( $expect, $mock->errorArrayToStatus( [
|
|
|
|
|
|
[ 'blockedtext' ],
|
|
|
|
|
|
[ 'autoblockedtext' ],
|
2016-12-01 16:51:03 +00:00
|
|
|
|
[ 'systemblockedtext' ],
|
2016-10-19 16:54:25 +00:00
|
|
|
|
'mainpage',
|
2019-03-01 14:49:05 +00:00
|
|
|
|
$msg,
|
|
|
|
|
|
[ $msg, 'foobar' ],
|
2016-10-19 16:54:25 +00:00
|
|
|
|
[ 'parentheses', 'foobar' ],
|
|
|
|
|
|
], $user ) );
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2019-02-18 20:30:41 +00:00
|
|
|
|
public function testAddBlockInfoToStatus() {
|
|
|
|
|
|
$mock = new MockApi();
|
|
|
|
|
|
|
2019-03-01 14:49:05 +00:00
|
|
|
|
$msg = new Message( 'mainpage' );
|
|
|
|
|
|
|
2019-02-18 20:30:41 +00:00
|
|
|
|
// Sanity check empty array
|
|
|
|
|
|
$expect = Status::newGood();
|
|
|
|
|
|
$test = Status::newGood();
|
|
|
|
|
|
$mock->addBlockInfoToStatus( $test );
|
|
|
|
|
|
$this->assertEquals( $expect, $test );
|
|
|
|
|
|
|
|
|
|
|
|
// No blocked $user, so no special block handling
|
|
|
|
|
|
$expect = Status::newGood();
|
|
|
|
|
|
$expect->fatal( 'blockedtext' );
|
|
|
|
|
|
$expect->fatal( 'autoblockedtext' );
|
|
|
|
|
|
$expect->fatal( 'systemblockedtext' );
|
|
|
|
|
|
$expect->fatal( 'mainpage' );
|
2019-03-01 14:49:05 +00:00
|
|
|
|
$expect->fatal( $msg );
|
|
|
|
|
|
$expect->fatal( $msg, 'foobar' );
|
2019-02-18 20:30:41 +00:00
|
|
|
|
$expect->fatal( 'parentheses', 'foobar' );
|
|
|
|
|
|
$test = clone $expect;
|
|
|
|
|
|
$mock->addBlockInfoToStatus( $test );
|
|
|
|
|
|
$this->assertEquals( $expect, $test );
|
|
|
|
|
|
|
|
|
|
|
|
// Has a blocked $user, so special block handling
|
|
|
|
|
|
$user = $this->getMutableTestUser()->getUser();
|
2019-05-13 14:18:07 +00:00
|
|
|
|
$block = new DatabaseBlock( [
|
2019-02-18 20:30:41 +00:00
|
|
|
|
'address' => $user->getName(),
|
|
|
|
|
|
'user' => $user->getID(),
|
|
|
|
|
|
'by' => $this->getTestSysop()->getUser()->getId(),
|
|
|
|
|
|
'reason' => __METHOD__,
|
|
|
|
|
|
'expiry' => time() + 100500,
|
|
|
|
|
|
] );
|
|
|
|
|
|
$block->insert();
|
2019-10-20 00:04:00 +00:00
|
|
|
|
|
|
|
|
|
|
$mockTrait = $this->getMockForTrait( ApiBlockInfoTrait::class );
|
|
|
|
|
|
$mockTrait->method( 'getLanguage' )->willReturn( 'en' );
|
|
|
|
|
|
$userInfoTrait = TestingAccessWrapper::newFromObject( $mockTrait );
|
2019-05-09 10:13:30 +00:00
|
|
|
|
$blockinfo = [ 'blockinfo' => $userInfoTrait->getBlockDetails( $block ) ];
|
2019-02-18 20:30:41 +00:00
|
|
|
|
|
|
|
|
|
|
$expect = Status::newGood();
|
|
|
|
|
|
$expect->fatal( ApiMessage::create( 'apierror-blocked', 'blocked', $blockinfo ) );
|
|
|
|
|
|
$expect->fatal( ApiMessage::create( 'apierror-autoblocked', 'autoblocked', $blockinfo ) );
|
|
|
|
|
|
$expect->fatal( ApiMessage::create( 'apierror-systemblocked', 'blocked', $blockinfo ) );
|
|
|
|
|
|
$expect->fatal( 'mainpage' );
|
2019-03-01 14:49:05 +00:00
|
|
|
|
$expect->fatal( $msg );
|
|
|
|
|
|
$expect->fatal( $msg, 'foobar' );
|
2019-02-18 20:30:41 +00:00
|
|
|
|
$expect->fatal( 'parentheses', 'foobar' );
|
|
|
|
|
|
$test = Status::newGood();
|
|
|
|
|
|
$test->fatal( 'blockedtext' );
|
|
|
|
|
|
$test->fatal( 'autoblockedtext' );
|
|
|
|
|
|
$test->fatal( 'systemblockedtext' );
|
|
|
|
|
|
$test->fatal( 'mainpage' );
|
2019-03-01 14:49:05 +00:00
|
|
|
|
$test->fatal( $msg );
|
|
|
|
|
|
$test->fatal( $msg, 'foobar' );
|
2019-02-18 20:30:41 +00:00
|
|
|
|
$test->fatal( 'parentheses', 'foobar' );
|
|
|
|
|
|
$mock->addBlockInfoToStatus( $test, $user );
|
|
|
|
|
|
$this->assertEquals( $expect, $test );
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2017-06-12 16:54:24 +00:00
|
|
|
|
public function testDieStatus() {
|
|
|
|
|
|
$mock = new MockApi();
|
|
|
|
|
|
|
|
|
|
|
|
$status = StatusValue::newGood();
|
|
|
|
|
|
$status->error( 'foo' );
|
|
|
|
|
|
$status->warning( 'bar' );
|
|
|
|
|
|
try {
|
|
|
|
|
|
$mock->dieStatus( $status );
|
|
|
|
|
|
$this->fail( 'Expected exception not thrown' );
|
|
|
|
|
|
} catch ( ApiUsageException $ex ) {
|
|
|
|
|
|
$this->assertTrue( ApiTestCase::apiExceptionHasCode( $ex, 'foo' ), 'Exception has "foo"' );
|
|
|
|
|
|
$this->assertFalse( ApiTestCase::apiExceptionHasCode( $ex, 'bar' ), 'Exception has "bar"' );
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
$status = StatusValue::newGood();
|
|
|
|
|
|
$status->warning( 'foo' );
|
|
|
|
|
|
$status->warning( 'bar' );
|
|
|
|
|
|
try {
|
|
|
|
|
|
$mock->dieStatus( $status );
|
|
|
|
|
|
$this->fail( 'Expected exception not thrown' );
|
|
|
|
|
|
} catch ( ApiUsageException $ex ) {
|
|
|
|
|
|
$this->assertTrue( ApiTestCase::apiExceptionHasCode( $ex, 'foo' ), 'Exception has "foo"' );
|
|
|
|
|
|
$this->assertTrue( ApiTestCase::apiExceptionHasCode( $ex, 'bar' ), 'Exception has "bar"' );
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
$status = StatusValue::newGood();
|
2019-08-31 20:59:45 +00:00
|
|
|
|
$status->setOK( false );
|
2017-06-12 16:54:24 +00:00
|
|
|
|
try {
|
|
|
|
|
|
$mock->dieStatus( $status );
|
|
|
|
|
|
$this->fail( 'Expected exception not thrown' );
|
|
|
|
|
|
} catch ( ApiUsageException $ex ) {
|
|
|
|
|
|
$this->assertTrue( ApiTestCase::apiExceptionHasCode( $ex, 'unknownerror-nocode' ),
|
|
|
|
|
|
'Exception has "unknownerror-nocode"' );
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2018-04-04 20:22:01 +00:00
|
|
|
|
/**
|
|
|
|
|
|
* @covers ApiBase::extractRequestParams
|
|
|
|
|
|
*/
|
|
|
|
|
|
public function testExtractRequestParams() {
|
|
|
|
|
|
$request = new FauxRequest( [
|
|
|
|
|
|
'xxexists' => 'exists!',
|
|
|
|
|
|
'xxmulti' => 'a|b|c|d|{bad}',
|
|
|
|
|
|
'xxempty' => '',
|
|
|
|
|
|
'xxtemplate-a' => 'A!',
|
|
|
|
|
|
'xxtemplate-b' => 'B1|B2|B3',
|
|
|
|
|
|
'xxtemplate-c' => '',
|
|
|
|
|
|
'xxrecursivetemplate-b-B1' => 'X',
|
|
|
|
|
|
'xxrecursivetemplate-b-B3' => 'Y',
|
|
|
|
|
|
'xxrecursivetemplate-b-B4' => '?',
|
|
|
|
|
|
'xxemptytemplate-' => 'nope',
|
|
|
|
|
|
'foo' => 'a|b|c',
|
|
|
|
|
|
'xxfoo' => 'a|b|c',
|
|
|
|
|
|
'errorformat' => 'raw',
|
|
|
|
|
|
] );
|
|
|
|
|
|
$context = new DerivativeContext( RequestContext::getMain() );
|
|
|
|
|
|
$context->setRequest( $request );
|
|
|
|
|
|
$main = new ApiMain( $context );
|
|
|
|
|
|
|
|
|
|
|
|
$mock = $this->getMockBuilder( ApiBase::class )
|
|
|
|
|
|
->setConstructorArgs( [ $main, 'test', 'xx' ] )
|
|
|
|
|
|
->setMethods( [ 'getAllowedParams' ] )
|
|
|
|
|
|
->getMockForAbstractClass();
|
|
|
|
|
|
$mock->method( 'getAllowedParams' )->willReturn( [
|
|
|
|
|
|
'notexists' => null,
|
|
|
|
|
|
'exists' => null,
|
|
|
|
|
|
'multi' => [
|
|
|
|
|
|
ApiBase::PARAM_ISMULTI => true,
|
|
|
|
|
|
],
|
|
|
|
|
|
'empty' => [
|
|
|
|
|
|
ApiBase::PARAM_ISMULTI => true,
|
|
|
|
|
|
],
|
|
|
|
|
|
'template-{m}' => [
|
|
|
|
|
|
ApiBase::PARAM_ISMULTI => true,
|
|
|
|
|
|
ApiBase::PARAM_TEMPLATE_VARS => [ 'm' => 'multi' ],
|
|
|
|
|
|
],
|
|
|
|
|
|
'recursivetemplate-{m}-{t}' => [
|
|
|
|
|
|
ApiBase::PARAM_TEMPLATE_VARS => [ 't' => 'template-{m}', 'm' => 'multi' ],
|
|
|
|
|
|
],
|
|
|
|
|
|
'emptytemplate-{m}' => [
|
|
|
|
|
|
ApiBase::PARAM_ISMULTI => true,
|
|
|
|
|
|
ApiBase::PARAM_TEMPLATE_VARS => [ 'm' => 'empty' ],
|
|
|
|
|
|
],
|
|
|
|
|
|
'badtemplate-{e}' => [
|
|
|
|
|
|
ApiBase::PARAM_TEMPLATE_VARS => [ 'e' => 'exists' ],
|
|
|
|
|
|
],
|
|
|
|
|
|
'badtemplate2-{e}' => [
|
|
|
|
|
|
ApiBase::PARAM_TEMPLATE_VARS => [ 'e' => 'badtemplate2-{e}' ],
|
|
|
|
|
|
],
|
|
|
|
|
|
'badtemplate3-{x}' => [
|
|
|
|
|
|
ApiBase::PARAM_TEMPLATE_VARS => [ 'x' => 'foo' ],
|
|
|
|
|
|
],
|
|
|
|
|
|
] );
|
|
|
|
|
|
|
|
|
|
|
|
$this->assertEquals( [
|
|
|
|
|
|
'notexists' => null,
|
|
|
|
|
|
'exists' => 'exists!',
|
|
|
|
|
|
'multi' => [ 'a', 'b', 'c', 'd', '{bad}' ],
|
|
|
|
|
|
'empty' => [],
|
|
|
|
|
|
'template-a' => [ 'A!' ],
|
|
|
|
|
|
'template-b' => [ 'B1', 'B2', 'B3' ],
|
|
|
|
|
|
'template-c' => [],
|
|
|
|
|
|
'template-d' => null,
|
|
|
|
|
|
'recursivetemplate-a-A!' => null,
|
|
|
|
|
|
'recursivetemplate-b-B1' => 'X',
|
|
|
|
|
|
'recursivetemplate-b-B2' => null,
|
|
|
|
|
|
'recursivetemplate-b-B3' => 'Y',
|
|
|
|
|
|
], $mock->extractRequestParams() );
|
|
|
|
|
|
|
|
|
|
|
|
$used = TestingAccessWrapper::newFromObject( $main )->getParamsUsed();
|
|
|
|
|
|
sort( $used );
|
|
|
|
|
|
$this->assertEquals( [
|
|
|
|
|
|
'xxempty',
|
|
|
|
|
|
'xxexists',
|
|
|
|
|
|
'xxmulti',
|
|
|
|
|
|
'xxnotexists',
|
|
|
|
|
|
'xxrecursivetemplate-a-A!',
|
|
|
|
|
|
'xxrecursivetemplate-b-B1',
|
|
|
|
|
|
'xxrecursivetemplate-b-B2',
|
|
|
|
|
|
'xxrecursivetemplate-b-B3',
|
|
|
|
|
|
'xxtemplate-a',
|
|
|
|
|
|
'xxtemplate-b',
|
|
|
|
|
|
'xxtemplate-c',
|
|
|
|
|
|
'xxtemplate-d',
|
|
|
|
|
|
], $used );
|
|
|
|
|
|
|
|
|
|
|
|
$warnings = $mock->getResult()->getResultData( 'warnings', [ 'Strip' => 'all' ] );
|
|
|
|
|
|
$this->assertCount( 1, $warnings );
|
|
|
|
|
|
$this->assertSame( 'ignoring-invalid-templated-value', $warnings[0]['code'] );
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2013-10-23 15:36:40 +00:00
|
|
|
|
}
|