wiki.techinc.nl/tests/phpunit/includes/api/Validator/ApiParamValidatorCallbacksTest.php
Brad Jorsch c2b1525908 API: Use ParamValidator library
This brings significant modularization to the Action API's parameter
validation, and allows the Action API and MW REST API to share
validation code.

Note there are several changes in this patch that may affect other code;
see the entries in RELEASE-NOTES-1.35 for details.

Bug: T142080
Bug: T232672
Bug: T21195
Bug: T34675
Bug: T154774
Change-Id: I1462edc1701278760fa695308007006868b249fc
Depends-On: I10011be060fe6d27c7527312ad41218786b3f40d
2020-02-04 13:36:14 -05:00

289 lines
9.2 KiB
PHP

<?php
namespace MediaWiki\Api\Validator;
use ApiBase;
use ApiMain;
use ApiMessage;
use ApiQueryBase;
use ApiUploadTestCase;
use FauxRequest;
use Wikimedia\Message\DataMessageValue;
use Wikimedia\TestingAccessWrapper;
/**
* @covers MediaWiki\Api\Validator\ApiParamValidatorCallbacks
* @group API
* @group medium
*/
class ApiParamValidatorCallbacksTest extends ApiUploadTestCase {
private function getCallbacks( FauxRequest $request ) : array {
$context = $this->apiContext->newTestContext( $request, $this->getTestUser()->getUser() );
$main = new ApiMain( $context );
return [ new ApiParamValidatorCallbacks( $main ), $main ];
}
private function filePath( $fileName ) {
return __DIR__ . '/../../../data/media/' . $fileName;
}
public function testHasParam() : void {
[ $callbacks, $main ] = $this->getCallbacks( new FauxRequest( [
'foo' => '1',
'bar' => '',
] ) );
$this->assertTrue( $callbacks->hasParam( 'foo', [] ) );
$this->assertTrue( $callbacks->hasParam( 'bar', [] ) );
$this->assertFalse( $callbacks->hasParam( 'baz', [] ) );
$this->assertSame(
[ 'foo', 'bar', 'baz' ],
TestingAccessWrapper::newFromObject( $main )->getParamsUsed()
);
}
/**
* @dataProvider provideGetValue
* @param string|null $data Value from request
* @param mixed $default For getValue()
* @param mixed $expect Expected return value
* @param bool $normalized Whether handleParamNormalization is called
*/
public function testGetValue( ?string $data, $default, $expect, bool $normalized = false ) : void {
[ $callbacks, $main ] = $this->getCallbacks( new FauxRequest( [ 'test' => $data ] ) );
$module = $this->getMockBuilder( ApiBase::class )
->setConstructorArgs( [ $main, 'testmodule' ] )
->setMethods( [ 'handleParamNormalization' ] )
->getMockForAbstractClass();
$options = [ 'module' => $module ];
if ( $normalized ) {
$module->expects( $this->once() )->method( 'handleParamNormalization' )
->with(
$this->identicalTo( 'test' ),
$this->identicalTo( $expect ),
$this->identicalTo( $data ?? $default )
);
} else {
$module->expects( $this->never() )->method( 'handleParamNormalization' );
}
$this->assertSame( $expect, $callbacks->getValue( 'test', $default, $options ) );
$this->assertSame( [ 'test' ], TestingAccessWrapper::newFromObject( $main )->getParamsUsed() );
}
public function provideGetValue() {
$obj = (object)[];
return [
'Basic test' => [ 'foo', 'bar', 'foo', false ],
'Default value' => [ null, 1234, 1234, false ],
'Default value (2)' => [ null, $obj, $obj, false ],
'No default value' => [ null, null, null, false ],
'Multi separator' => [ "\x1ffoo\x1fbar", 1234, "\x1ffoo\x1fbar", false ],
'Normalized' => [ "\x1ffoo\x1fba\u{0301}r", 1234, "\x1ffoo\x1fbár", true ],
];
}
private function setupUploads() : void {
$fileName = 'TestUploadStash.jpg';
$mimeType = 'image/jpeg';
$filePath = $this->filePath( 'yuv420.jpg' );
$this->fakeUploadFile( 'file', $fileName, $mimeType, $filePath );
$_FILES['file2'] = [
'name' => '',
'type' => '',
'tmp_name' => '',
'size' => 0,
'error' => UPLOAD_ERR_NO_FILE,
];
$_FILES['file3'] = [
'name' => 'xxx.png',
'type' => '',
'tmp_name' => '',
'size' => 0,
'error' => UPLOAD_ERR_INI_SIZE,
];
}
public function testHasUpload() : void {
$this->setupUploads();
[ $callbacks, $main ] = $this->getCallbacks( new FauxRequest( [
'foo' => '1',
'bar' => '',
] ) );
$this->assertFalse( $callbacks->hasUpload( 'foo', [] ) );
$this->assertFalse( $callbacks->hasUpload( 'bar', [] ) );
$this->assertFalse( $callbacks->hasUpload( 'baz', [] ) );
$this->assertTrue( $callbacks->hasUpload( 'file', [] ) );
$this->assertTrue( $callbacks->hasUpload( 'file2', [] ) );
$this->assertTrue( $callbacks->hasUpload( 'file3', [] ) );
$this->assertSame(
[ 'foo', 'bar', 'baz', 'file', 'file2', 'file3' ],
TestingAccessWrapper::newFromObject( $main )->getParamsUsed()
);
}
public function testGetUploadedFile() : void {
$this->setupUploads();
[ $callbacks, $main ] = $this->getCallbacks( new FauxRequest( [
'foo' => '1',
'bar' => '',
] ) );
$this->assertNull( $callbacks->getUploadedFile( 'foo', [] ) );
$this->assertNull( $callbacks->getUploadedFile( 'bar', [] ) );
$this->assertNull( $callbacks->getUploadedFile( 'baz', [] ) );
$file = $callbacks->getUploadedFile( 'file', [] );
$this->assertInstanceOf( \Psr\Http\Message\UploadedFileInterface::class, $file );
$this->assertSame( UPLOAD_ERR_OK, $file->getError() );
$this->assertSame( 'TestUploadStash.jpg', $file->getClientFilename() );
$file = $callbacks->getUploadedFile( 'file2', [] );
$this->assertInstanceOf( \Psr\Http\Message\UploadedFileInterface::class, $file );
$this->assertSame( UPLOAD_ERR_NO_FILE, $file->getError() );
$file = $callbacks->getUploadedFile( 'file3', [] );
$this->assertInstanceOf( \Psr\Http\Message\UploadedFileInterface::class, $file );
$this->assertSame( UPLOAD_ERR_INI_SIZE, $file->getError() );
}
/**
* @dataProvider provideRecordCondition
* @param DataMessageValue $message
* @param ApiMessage|null $expect
* @param bool $sensitive
*/
public function testRecordCondition(
DataMessageValue $message, ?ApiMessage $expect, bool $sensitive = false
) : void {
[ $callbacks, $main ] = $this->getCallbacks( new FauxRequest( [ 'testparam' => 'testvalue' ] ) );
$query = $main->getModuleFromPath( 'query' );
$warnings = [];
$module = $this->getMockBuilder( ApiQueryBase::class )
->setConstructorArgs( [ $query, 'test' ] )
->setMethods( [ 'addWarning' ] )
->getMockForAbstractClass();
$module->method( 'addWarning' )->willReturnCallback(
function ( $msg, $code, $data ) use ( &$warnings ) {
$warnings[] = [ $msg, $code, $data ];
}
);
$query->getModuleManager()->addModule( 'test', 'meta', [
'class' => get_class( $module ),
'factory' => function () use ( $module ) {
return $module;
}
] );
$callbacks->recordCondition( $message, 'testparam', 'testvalue', [], [ 'module' => $module ] );
if ( $expect ) {
$this->assertNotCount( 0, $warnings );
$this->assertSame(
$expect->inLanguage( 'qqx' )->plain(),
$warnings[0][0]->inLanguage( 'qqx' )->plain()
);
$this->assertSame( $expect->getApiCode(), $warnings[0][1] );
$this->assertSame( $expect->getApiData(), $warnings[0][2] );
} else {
$this->assertEmpty( $warnings );
}
$this->assertSame(
$sensitive ? [ 'testparam' ] : [],
TestingAccessWrapper::newFromObject( $main )->getSensitiveParams()
);
}
public function provideRecordCondition() : \Generator {
yield 'Deprecated param' => [
DataMessageValue::new(
'paramvalidator-param-deprecated', [],
'param-deprecated',
[ 'data' => true ]
)->plaintextParams( 'XXtestparam', 'XXtestvalue' ),
ApiMessage::create(
'paramvalidator-param-deprecated',
'deprecation',
[ 'data' => true, 'feature' => 'action=query&meta=test&testparam' ]
)->plaintextParams( 'XXtestparam', 'XXtestvalue' )
];
yield 'Deprecated value' => [
DataMessageValue::new(
'paramvalidator-deprecated-value', [],
'deprecated-value'
)->plaintextParams( 'XXtestparam', 'XXtestvalue' ),
ApiMessage::create(
'paramvalidator-deprecated-value',
'deprecation',
[ 'feature' => 'action=query&meta=test&testparam=testvalue' ]
)->plaintextParams( 'XXtestparam', 'XXtestvalue' )
];
yield 'Deprecated value with custom MessageValue' => [
DataMessageValue::new(
'some-custom-message-value', [],
'deprecated-value',
[ 'xyz' => 123 ]
)->plaintextParams( 'XXtestparam', 'XXtestvalue', 'foobar' ),
ApiMessage::create(
'some-custom-message-value',
'deprecation',
[ 'xyz' => 123, 'feature' => 'action=query&meta=test&testparam=testvalue' ]
)->plaintextParams( 'XXtestparam', 'XXtestvalue', 'foobar' )
];
// See ApiParamValidator::normalizeSettings()
yield 'Deprecated value with custom Message' => [
DataMessageValue::new(
'some-custom-message', [],
'deprecated-value',
[ '💩' => 'back-compat' ]
)->plaintextParams( 'XXtestparam', 'XXtestvalue', 'foobar' ),
ApiMessage::create(
'some-custom-message',
'deprecation',
[ 'feature' => 'action=query&meta=test&testparam=testvalue' ]
)->plaintextParams( 'foobar' )
];
yield 'Sensitive param' => [
DataMessageValue::new( 'paramvalidator-param-sensitive', [], 'param-sensitive' )
->plaintextParams( 'XXtestparam', 'XXtestvalue' ),
null,
true
];
yield 'Arbitrary warning' => [
DataMessageValue::new( 'some-warning', [], 'some-code', [ 'some-data' ] )
->plaintextParams( 'XXtestparam', 'XXtestvalue', 'foobar' ),
ApiMessage::create( 'some-warning', 'some-code', [ 'some-data' ] )
->plaintextParams( 'XXtestparam', 'XXtestvalue', 'foobar' ),
];
}
public function testUseHighLimits() : void {
$context = $this->apiContext->newTestContext( new FauxRequest, $this->getTestUser()->getUser() );
$main = $this->getMockBuilder( ApiMain::class )
->setConstructorArgs( [ $context ] )
->setMethods( [ 'canApiHighLimits' ] )
->getMock();
$main->method( 'canApiHighLimits' )->will( $this->onConsecutiveCalls( true, false ) );
$callbacks = new ApiParamValidatorCallbacks( $main );
$this->assertTrue( $callbacks->useHighLimits( [] ) );
$this->assertFalse( $callbacks->useHighLimits( [] ) );
}
}