3 namespace MediaWiki\Tests\Api\Validator
;
6 use MediaWiki\Api\ApiBase
;
7 use MediaWiki\Api\ApiMain
;
8 use MediaWiki\Api\ApiMessage
;
9 use MediaWiki\Api\ApiQueryBase
;
10 use MediaWiki\Api\Validator\ApiParamValidatorCallbacks
;
11 use MediaWiki\Request\FauxRequest
;
12 use MediaWiki\Tests\Api\ApiUploadTestCase
;
13 use MediaWiki\Tests\Unit\Permissions\MockAuthorityTrait
;
14 use Psr\Http\Message\UploadedFileInterface
;
15 use Wikimedia\Message\DataMessageValue
;
16 use Wikimedia\TestingAccessWrapper
;
19 * @covers \MediaWiki\Api\Validator\ApiParamValidatorCallbacks
23 class ApiParamValidatorCallbacksTest
extends ApiUploadTestCase
{
24 use MockAuthorityTrait
;
26 private function getCallbacks( FauxRequest
$request ): array {
27 $context = $this->apiContext
->newTestContext( $request, $this->mockRegisteredUltimateAuthority() );
28 $main = new ApiMain( $context );
29 return [ new ApiParamValidatorCallbacks( $main ), $main ];
32 private function filePath( $fileName ) {
33 return __DIR__
. '/../../../data/media/' . $fileName;
36 public function testHasParam(): void
{
37 [ $callbacks, $main ] = $this->getCallbacks( new FauxRequest( [
42 $this->assertTrue( $callbacks->hasParam( 'foo', [] ) );
43 $this->assertTrue( $callbacks->hasParam( 'bar', [] ) );
44 $this->assertFalse( $callbacks->hasParam( 'baz', [] ) );
47 [ 'foo', 'bar', 'baz' ],
48 TestingAccessWrapper
::newFromObject( $main )->getParamsUsed()
53 * @dataProvider provideGetValue
54 * @param string|null $data Value from request
55 * @param mixed $default For getValue()
56 * @param mixed $expect Expected return value
57 * @param bool $normalized Whether handleParamNormalization is called
59 public function testGetValue( ?
string $data, $default, $expect, bool $normalized = false ): void
{
60 [ $callbacks, $main ] = $this->getCallbacks( new FauxRequest( [ 'test' => $data ] ) );
62 $module = $this->getMockBuilder( ApiBase
::class )
63 ->setConstructorArgs( [ $main, 'testmodule' ] )
64 ->onlyMethods( [ 'handleParamNormalization' ] )
65 ->getMockForAbstractClass();
66 $options = [ 'module' => $module ];
68 $module->expects( $this->once() )->method( 'handleParamNormalization' )
70 $this->identicalTo( 'test' ),
71 $this->identicalTo( $expect ),
72 $this->identicalTo( $data ??
$default )
75 $module->expects( $this->never() )->method( 'handleParamNormalization' );
78 $this->assertSame( $expect, $callbacks->getValue( 'test', $default, $options ) );
79 $this->assertSame( [ 'test' ], TestingAccessWrapper
::newFromObject( $main )->getParamsUsed() );
82 public static function provideGetValue() {
85 'Basic test' => [ 'foo', 'bar', 'foo', false ],
86 'Default value' => [ null, 1234, 1234, false ],
87 'Default value (2)' => [ null, $obj, $obj, false ],
88 'No default value' => [ null, null, null, false ],
89 'Multi separator' => [ "\x1ffoo\x1fbar", 1234, "\x1ffoo\x1fbar", false ],
90 'Normalized' => [ "\x1ffoo\x1fba\u{0301}r", 1234, "\x1ffoo\x1fbár", true ],
94 private function setupUploads(): void
{
95 $fileName = 'TestUploadStash.jpg';
96 $mimeType = 'image/jpeg';
97 $filePath = $this->filePath( 'yuv420.jpg' );
98 $this->fakeUploadFile( 'file', $fileName, $mimeType, $filePath );
100 $this->requestDataFiles
['file2'] = [
105 'error' => UPLOAD_ERR_NO_FILE
,
108 $this->requestDataFiles
['file3'] = [
113 'error' => UPLOAD_ERR_INI_SIZE
,
117 public function testHasUpload(): void
{
118 $this->setupUploads();
120 $request = new FauxRequest( [
124 $request->setUploadData( $this->requestDataFiles
);
125 [ $callbacks, $main ] = $this->getCallbacks( $request );
127 $this->assertFalse( $callbacks->hasUpload( 'foo', [] ) );
128 $this->assertFalse( $callbacks->hasUpload( 'bar', [] ) );
129 $this->assertFalse( $callbacks->hasUpload( 'baz', [] ) );
130 $this->assertTrue( $callbacks->hasUpload( 'file', [] ) );
131 $this->assertTrue( $callbacks->hasUpload( 'file2', [] ) );
132 $this->assertTrue( $callbacks->hasUpload( 'file3', [] ) );
135 [ 'foo', 'bar', 'baz', 'file', 'file2', 'file3' ],
136 TestingAccessWrapper
::newFromObject( $main )->getParamsUsed()
140 public function testGetUploadedFile(): void
{
141 $this->setupUploads();
143 $request = new FauxRequest( [
147 $request->setUploadData( $this->requestDataFiles
);
148 [ $callbacks, $main ] = $this->getCallbacks( $request );
150 $this->assertNull( $callbacks->getUploadedFile( 'foo', [] ) );
151 $this->assertNull( $callbacks->getUploadedFile( 'bar', [] ) );
152 $this->assertNull( $callbacks->getUploadedFile( 'baz', [] ) );
154 $file = $callbacks->getUploadedFile( 'file', [] );
155 $this->assertInstanceOf( UploadedFileInterface
::class, $file );
156 $this->assertSame( UPLOAD_ERR_OK
, $file->getError() );
157 $this->assertSame( 'TestUploadStash.jpg', $file->getClientFilename() );
159 $file = $callbacks->getUploadedFile( 'file2', [] );
160 $this->assertInstanceOf( UploadedFileInterface
::class, $file );
161 $this->assertSame( UPLOAD_ERR_NO_FILE
, $file->getError() );
163 $file = $callbacks->getUploadedFile( 'file3', [] );
164 $this->assertInstanceOf( UploadedFileInterface
::class, $file );
165 $this->assertSame( UPLOAD_ERR_INI_SIZE
, $file->getError() );
169 * @dataProvider provideRecordCondition
170 * @param DataMessageValue $message
171 * @param ApiMessage|null $expect
172 * @param bool $sensitive
174 public function testRecordCondition(
175 DataMessageValue
$message, ?ApiMessage
$expect, bool $sensitive = false
177 [ $callbacks, $main ] = $this->getCallbacks( new FauxRequest( [ 'testparam' => 'testvalue' ] ) );
178 $query = $main->getModuleFromPath( 'query' );
181 $module = $this->getMockBuilder( ApiQueryBase
::class )
182 ->setConstructorArgs( [ $query, 'test' ] )
183 ->onlyMethods( [ 'addWarning' ] )
184 ->getMockForAbstractClass();
185 $module->method( 'addWarning' )->willReturnCallback(
186 static function ( $msg, $code, $data ) use ( &$warnings ) {
187 $warnings[] = [ $msg, $code, $data ];
190 $query->getModuleManager()->addModule( 'test', 'meta', [
191 'class' => get_class( $module ),
192 'factory' => static function () use ( $module ) {
197 $callbacks->recordCondition( $message, 'testparam', 'testvalue', [], [ 'module' => $module ] );
200 $this->assertNotCount( 0, $warnings );
202 $expect->inLanguage( 'qqx' )->plain(),
203 $warnings[0][0]->inLanguage( 'qqx' )->plain()
205 $this->assertSame( $expect->getApiCode(), $warnings[0][1] );
206 $this->assertSame( $expect->getApiData(), $warnings[0][2] );
208 $this->assertSame( [], $warnings );
212 $sensitive ?
[ 'testparam' ] : [],
213 TestingAccessWrapper
::newFromObject( $main )->getSensitiveParams()
217 public static function provideRecordCondition(): Generator
{
218 yield
'Deprecated param' => [
219 DataMessageValue
::new(
220 'paramvalidator-param-deprecated', [],
223 )->plaintextParams( 'XXtestparam', 'XXtestvalue' ),
225 'paramvalidator-param-deprecated',
227 [ 'data' => true, 'feature' => 'action=query&meta=test&testparam' ]
228 )->plaintextParams( 'XXtestparam', 'XXtestvalue' )
231 yield
'Deprecated value' => [
232 DataMessageValue
::new(
233 'paramvalidator-deprecated-value', [],
235 )->plaintextParams( 'XXtestparam', 'XXtestvalue' ),
237 'paramvalidator-deprecated-value',
239 [ 'feature' => 'action=query&meta=test&testparam=testvalue' ]
240 )->plaintextParams( 'XXtestparam', 'XXtestvalue' )
243 yield
'Deprecated value with custom MessageValue' => [
244 DataMessageValue
::new(
245 'some-custom-message-value', [],
248 )->plaintextParams( 'XXtestparam', 'XXtestvalue', 'foobar' ),
250 'some-custom-message-value',
252 [ 'xyz' => 123, 'feature' => 'action=query&meta=test&testparam=testvalue' ]
253 )->plaintextParams( 'XXtestparam', 'XXtestvalue', 'foobar' )
256 // See ApiParamValidator::normalizeSettings()
257 yield
'Deprecated value with custom Message' => [
258 DataMessageValue
::new(
259 'some-custom-message', [],
261 [ '💩' => 'back-compat' ]
262 )->plaintextParams( 'XXtestparam', 'XXtestvalue', 'foobar' ),
264 'some-custom-message',
266 [ 'feature' => 'action=query&meta=test&testparam=testvalue' ]
267 )->plaintextParams( 'foobar' )
270 yield
'Sensitive param' => [
271 DataMessageValue
::new( 'paramvalidator-param-sensitive', [], 'param-sensitive' )
272 ->plaintextParams( 'XXtestparam', 'XXtestvalue' ),
277 yield
'Arbitrary warning' => [
278 DataMessageValue
::new( 'some-warning', [], 'some-code', [ 'some-data' ] )
279 ->plaintextParams( 'XXtestparam', 'XXtestvalue', 'foobar' ),
280 ApiMessage
::create( 'some-warning', 'some-code', [ 'some-data' ] )
281 ->plaintextParams( 'XXtestparam', 'XXtestvalue', 'foobar' ),
285 public function testUseHighLimits(): void
{
286 $context = $this->apiContext
->newTestContext( new FauxRequest
, $this->mockRegisteredUltimateAuthority() );
287 $main = $this->getMockBuilder( ApiMain
::class )
288 ->setConstructorArgs( [ $context ] )
289 ->onlyMethods( [ 'canApiHighLimits' ] )
292 $main->method( 'canApiHighLimits' )->willReturnOnConsecutiveCalls( true, false );
294 $callbacks = new ApiParamValidatorCallbacks( $main );
295 $this->assertTrue( $callbacks->useHighLimits( [] ) );
296 $this->assertFalse( $callbacks->useHighLimits( [] ) );