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' ] ) ->onlyMethods( [ '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' ] ) ->onlyMethods( [ 'addWarning' ] ) ->getMockForAbstractClass(); $module->method( 'addWarning' )->willReturnCallback( static function ( $msg, $code, $data ) use ( &$warnings ) { $warnings[] = [ $msg, $code, $data ]; } ); $query->getModuleManager()->addModule( 'test', 'meta', [ 'class' => get_class( $module ), 'factory' => static 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->assertSame( [], $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 ] ) ->onlyMethods( [ 'canApiHighLimits' ] ) ->getMock(); $main->method( 'canApiHighLimits' )->will( $this->onConsecutiveCalls( true, false ) ); $callbacks = new ApiParamValidatorCallbacks( $main ); $this->assertTrue( $callbacks->useHighLimits( [] ) ); $this->assertFalse( $callbacks->useHighLimits( [] ) ); } }