Merge "Set l18n cache to array in DevelopmentSettings.php"
authorjenkins-bot <jenkins-bot@gerrit.wikimedia.org>
Wed, 18 Sep 2019 11:59:57 +0000 (11:59 +0000)
committerGerrit Code Review <gerrit@wikimedia.org>
Wed, 18 Sep 2019 11:59:57 +0000 (11:59 +0000)
19 files changed:
RELEASE-NOTES-1.34
includes/Rest/BasicAccess/MWBasicAuthorizer.php
includes/Rest/BasicAccess/MWBasicRequestAuthorizer.php
includes/Rest/EntryPoint.php
includes/Rest/Validator/ParamValidatorCallbacks.php
includes/Rest/Validator/Validator.php
includes/api/ApiQueryImageInfo.php
includes/filerepo/file/File.php
includes/filerepo/file/LocalFile.php
includes/libs/objectcache/wancache/WANObjectCache.php
includes/objectcache/SqlBagOStuff.php
maintenance/Doxyfile
maintenance/mwdocgen.php
tests/phpunit/includes/Rest/BasicAccess/MWBasicRequestAuthorizerTest.php
tests/phpunit/includes/Rest/EntryPointTest.php
tests/phpunit/includes/filerepo/file/LocalFileTest.php
tests/phpunit/includes/libs/objectcache/WANObjectCacheTest.php
tests/phpunit/unit/includes/Rest/Handler/HelloHandlerTest.php
tests/phpunit/unit/includes/Rest/RouterTest.php

index 09f5346..136ecc3 100644 (file)
@@ -164,6 +164,18 @@ $wgPasswordPolicy['policies']['default']['PasswordNotInLargeBlacklist'] = false;
   deprecated in 1.25, has been removed.
 * (T60993) action=query list=filearchive, list=alldeletedrevisions and
   prop=deletedrevisions no longer require the 'deletedhistory' user right.
+* In the response to queries that use 'prop=imageinfo', entries for
+  non-existing files (indicated by the 'filemissing' field) now omit the
+  following fields, since they are meaningless in this context:
+  'timestamp', 'userhidden', 'user', 'userid', 'anon', 'size', 'width',
+  'height', 'pagecount', 'duration', 'commenthidden', 'parsedcomment',
+  'comment', 'thumburl', 'thumbwidth', 'thumbheight', 'thumbmime',
+  'thumberror', 'url', 'sha1', 'metadata', 'extmetadata', 'commonmetadata',
+  'mime', 'mediadtype', 'bitdepth'.
+  Clients that process these fields should first check if 'filemissing' is
+  set. Fields that are supported even if the file is missing include:
+  'canonicaltitle', ''archivename' (deleted files only), 'descriptionurl',
+  'descriptionshorturl'.
 
 === Action API internal changes in 1.34 ===
 * The exception thrown in ApiModuleManager::getModule has been changed
index 43014f1..92529b3 100644 (file)
@@ -2,24 +2,24 @@
 
 namespace MediaWiki\Rest\BasicAccess;
 
-use User;
 use MediaWiki\Permissions\PermissionManager;
 use MediaWiki\Rest\Handler;
 use MediaWiki\Rest\RequestInterface;
+use MediaWiki\User\UserIdentity;
 
 /**
- * A factory for MWBasicRequestAuthorizer which passes through a User object
+ * A factory for MWBasicRequestAuthorizer which passes through a UserIdentity.
  *
  * @internal
  */
 class MWBasicAuthorizer extends BasicAuthorizerBase {
-       /** @var User */
+       /** @var UserIdentity */
        private $user;
 
        /** @var PermissionManager */
        private $permissionManager;
 
-       public function __construct( User $user, PermissionManager $permissionManager ) {
+       public function __construct( UserIdentity $user, PermissionManager $permissionManager ) {
                $this->user = $user;
                $this->permissionManager = $permissionManager;
        }
index 8c459c6..671488a 100644 (file)
@@ -2,7 +2,7 @@
 
 namespace MediaWiki\Rest\BasicAccess;
 
-use User;
+use MediaWiki\User\UserIdentity;
 use MediaWiki\Permissions\PermissionManager;
 use MediaWiki\Rest\Handler;
 use MediaWiki\Rest\RequestInterface;
@@ -13,14 +13,14 @@ use MediaWiki\Rest\RequestInterface;
  * @internal
  */
 class MWBasicRequestAuthorizer extends BasicRequestAuthorizer {
-       /** @var User */
+       /** @var UserIdentity */
        private $user;
 
        /** @var PermissionManager */
        private $permissionManager;
 
        public function __construct( RequestInterface $request, Handler $handler,
-               User $user, PermissionManager $permissionManager
+               UserIdentity $user, PermissionManager $permissionManager
        ) {
                parent::__construct( $request, $handler );
                $this->user = $user;
index 4fdd1f8..070451d 100644 (file)
@@ -57,7 +57,11 @@ class EntryPoint {
                        $services->getPermissionManager() );
 
                // @phan-suppress-next-line PhanAccessMethodInternal
-               $restValidator = new Validator( $objectFactory, $request, RequestContext::getMain()->getUser() );
+               $restValidator = new Validator( $objectFactory,
+                       $services->getPermissionManager(),
+                       $request,
+                       RequestContext::getMain()->getUser()
+               );
 
                global $IP;
                $router = new Router(
index 6c54a50..93de911 100644 (file)
@@ -3,21 +3,30 @@
 namespace MediaWiki\Rest\Validator;
 
 use InvalidArgumentException;
+use MediaWiki\Permissions\PermissionManager;
 use MediaWiki\Rest\RequestInterface;
+use MediaWiki\User\UserIdentity;
 use Psr\Http\Message\UploadedFileInterface;
-use User;
 use Wikimedia\ParamValidator\Callbacks;
 use Wikimedia\ParamValidator\ValidationException;
 
 class ParamValidatorCallbacks implements Callbacks {
 
+       /** @var PermissionManager */
+       private $permissionManager;
+
        /** @var RequestInterface */
        private $request;
 
-       /** @var User */
+       /** @var UserIdentity */
        private $user;
 
-       public function __construct( RequestInterface $request, User $user ) {
+       public function __construct(
+               PermissionManager $permissionManager,
+               RequestInterface $request,
+               UserIdentity $user
+       ) {
+               $this->permissionManager = $permissionManager;
                $this->request = $request;
                $this->user = $user;
        }
@@ -76,7 +85,7 @@ class ParamValidatorCallbacks implements Callbacks {
        }
 
        public function useHighLimits( array $options ) {
-               return $this->user->isAllowed( 'apihighlimits' );
+               return $this->permissionManager->userHasRight( $this->user, 'apihighlimits' );
        }
 
 }
index cee1cdb..be8d7a4 100644 (file)
@@ -2,10 +2,11 @@
 
 namespace MediaWiki\Rest\Validator;
 
+use MediaWiki\Permissions\PermissionManager;
 use MediaWiki\Rest\Handler;
 use MediaWiki\Rest\HttpException;
 use MediaWiki\Rest\RequestInterface;
-use User;
+use MediaWiki\User\UserIdentity;
 use Wikimedia\ObjectFactory;
 use Wikimedia\ParamValidator\ParamValidator;
 use Wikimedia\ParamValidator\TypeDef\BooleanDef;
@@ -62,16 +63,20 @@ class Validator {
        private $paramValidator;
 
        /**
-        * @internal
         * @param ObjectFactory $objectFactory
+        * @param PermissionManager $permissionManager
         * @param RequestInterface $request
-        * @param User $user
+        * @param UserIdentity $user
+        * @internal
         */
        public function __construct(
-               ObjectFactory $objectFactory, RequestInterface $request, User $user
+               ObjectFactory $objectFactory,
+               PermissionManager $permissionManager,
+               RequestInterface $request,
+               UserIdentity $user
        ) {
                $this->paramValidator = new ParamValidator(
-                       new ParamValidatorCallbacks( $request, $user ),
+                       new ParamValidatorCallbacks( $permissionManager, $request, $user ),
                        $objectFactory,
                        [
                                'typeDefs' => self::$typeDefs,
index 97a9b0a..285c0bf 100644 (file)
@@ -387,9 +387,13 @@ class ApiQueryImageInfo extends ApiQueryBase {
                $vals = [
                        ApiResult::META_TYPE => 'assoc',
                ];
+
+               // Some information will be unavailable if the file does not exist. T221812
+               $exists = $file->exists();
+
                // Timestamp is shown even if the file is revdelete'd in interface
                // so do same here.
-               if ( isset( $prop['timestamp'] ) ) {
+               if ( isset( $prop['timestamp'] ) && $exists ) {
                        $vals['timestamp'] = wfTimestamp( TS_ISO_8601, $file->getTimestamp() );
                }
 
@@ -408,7 +412,7 @@ class ApiQueryImageInfo extends ApiQueryBase {
                $user = isset( $prop['user'] );
                $userid = isset( $prop['userid'] );
 
-               if ( $user || $userid ) {
+               if ( ( $user || $userid ) && $exists ) {
                        if ( $file->isDeleted( File::DELETED_USER ) ) {
                                $vals['userhidden'] = true;
                                $anyHidden = true;
@@ -428,7 +432,7 @@ class ApiQueryImageInfo extends ApiQueryBase {
 
                // This is shown even if the file is revdelete'd in interface
                // so do same here.
-               if ( isset( $prop['size'] ) || isset( $prop['dimensions'] ) ) {
+               if ( ( isset( $prop['size'] ) || isset( $prop['dimensions'] ) ) && $exists ) {
                        $vals['size'] = (int)$file->getSize();
                        $vals['width'] = (int)$file->getWidth();
                        $vals['height'] = (int)$file->getHeight();
@@ -449,7 +453,7 @@ class ApiQueryImageInfo extends ApiQueryBase {
                $pcomment = isset( $prop['parsedcomment'] );
                $comment = isset( $prop['comment'] );
 
-               if ( $pcomment || $comment ) {
+               if ( ( $pcomment || $comment ) && $exists ) {
                        if ( $file->isDeleted( File::DELETED_COMMENT ) ) {
                                $vals['commenthidden'] = true;
                                $anyHidden = true;
@@ -500,7 +504,7 @@ class ApiQueryImageInfo extends ApiQueryBase {
                }
 
                if ( $url ) {
-                       if ( $file->exists() ) {
+                       if ( $exists ) {
                                if ( !is_null( $thumbParams ) ) {
                                        $mto = $file->transform( $thumbParams );
                                        self::$transformCount++;
@@ -529,8 +533,6 @@ class ApiQueryImageInfo extends ApiQueryBase {
                                        }
                                }
                                $vals['url'] = wfExpandUrl( $file->getFullUrl(), PROTO_CURRENT );
-                       } else {
-                               $vals['filemissing'] = true;
                        }
                        $vals['descriptionurl'] = wfExpandUrl( $file->getDescriptionUrl(), PROTO_CURRENT );
 
@@ -540,11 +542,15 @@ class ApiQueryImageInfo extends ApiQueryBase {
                        }
                }
 
-               if ( $sha1 ) {
+               if ( !$exists ) {
+                       $vals['filemissing'] = true;
+               }
+
+               if ( $sha1 && $exists ) {
                        $vals['sha1'] = Wikimedia\base_convert( $file->getSha1(), 36, 16, 40 );
                }
 
-               if ( $meta ) {
+               if ( $meta && $exists ) {
                        Wikimedia\suppressWarnings();
                        $metadata = unserialize( $file->getMetadata() );
                        Wikimedia\restoreWarnings();
@@ -553,12 +559,12 @@ class ApiQueryImageInfo extends ApiQueryBase {
                        }
                        $vals['metadata'] = $metadata ? static::processMetaData( $metadata, $result ) : null;
                }
-               if ( $commonmeta ) {
+               if ( $commonmeta && $exists ) {
                        $metaArray = $file->getCommonMetaArray();
                        $vals['commonmetadata'] = $metaArray ? static::processMetaData( $metaArray, $result ) : [];
                }
 
-               if ( $extmetadata ) {
+               if ( $extmetadata && $exists ) {
                        // Note, this should return an array where all the keys
                        // start with a letter, and all the values are strings.
                        // Thus there should be no issue with format=xml.
@@ -575,11 +581,11 @@ class ApiQueryImageInfo extends ApiQueryBase {
                        $vals['extmetadata'] = $extmetaArray;
                }
 
-               if ( $mime ) {
+               if ( $mime && $exists ) {
                        $vals['mime'] = $file->getMimeType();
                }
 
-               if ( $mediatype ) {
+               if ( $mediatype && $exists ) {
                        $vals['mediatype'] = $file->getMediaType();
                }
 
@@ -589,7 +595,7 @@ class ApiQueryImageInfo extends ApiQueryBase {
                        $vals['archivename'] = $file->getArchiveName();
                }
 
-               if ( $bitdepth ) {
+               if ( $bitdepth && $exists ) {
                        $vals['bitdepth'] = $file->getBitDepth();
                }
 
index 60d4e29..73b08e6 100644 (file)
@@ -2042,7 +2042,7 @@ abstract class File implements IDBAccessObject {
         * Get the URL of the image description page. May return false if it is
         * unknown or not applicable.
         *
-        * @return string
+        * @return string|bool
         */
        function getDescriptionUrl() {
                if ( $this->repo ) {
index ceb8dda..6d29433 100644 (file)
@@ -452,6 +452,10 @@ class LocalFile extends File {
         * This covers fields that are sometimes not cached.
         */
        protected function loadExtraFromDB() {
+               if ( !$this->title ) {
+                       return; // Avoid hard failure when the file does not exist. T221812
+               }
+
                $fname = static::class . '::' . __FUNCTION__;
 
                # Unconditionally set loaded=true, we don't want the accessors constantly rechecking
@@ -857,12 +861,24 @@ class LocalFile extends File {
        function getUser( $type = 'text' ) {
                $this->load();
 
-               if ( $type === 'object' ) {
-                       return $this->user;
-               } elseif ( $type === 'text' ) {
-                       return $this->user->getName();
-               } elseif ( $type === 'id' ) {
-                       return $this->user->getId();
+               if ( !$this->user ) {
+                       // If the file does not exist, $this->user will be null, see T221812.
+                       // Note: 'Unknown user' this is a reserved user name.
+                       if ( $type === 'object' ) {
+                               return User::newFromName( 'Unknown user', false );
+                       } elseif ( $type === 'text' ) {
+                               return 'Unknown user';
+                       } elseif ( $type === 'id' ) {
+                               return 0;
+                       }
+               } else {
+                       if ( $type === 'object' ) {
+                               return $this->user;
+                       } elseif ( $type === 'text' ) {
+                               return $this->user->getName();
+                       } elseif ( $type === 'id' ) {
+                               return $this->user->getId();
+                       }
                }
 
                throw new MWException( "Unknown type '$type'." );
@@ -876,9 +892,13 @@ class LocalFile extends File {
         * @since 1.27
         */
        public function getDescriptionShortUrl() {
+               if ( !$this->title ) {
+                       return null; // Avoid hard failure when the file does not exist. T221812
+               }
+
                $pageId = $this->title->getArticleID();
 
-               if ( $pageId !== null ) {
+               if ( $pageId ) {
                        $url = $this->repo->makeUrl( [ 'curid' => $pageId ] );
                        if ( $url !== false ) {
                                return $url;
@@ -1145,6 +1165,10 @@ class LocalFile extends File {
         * @return OldLocalFile[]
         */
        function getHistory( $limit = null, $start = null, $end = null, $inc = true ) {
+               if ( !$this->exists() ) {
+                       return []; // Avoid hard failure when the file does not exist. T221812
+               }
+
                $dbr = $this->repo->getReplicaDB();
                $oldFileQuery = OldLocalFile::getQueryInfo();
 
@@ -1198,9 +1222,13 @@ class LocalFile extends File {
         *  0      return line for current version
         *  1      query for old versions, return first one
         *  2, ... return next old version from above query
-        * @return bool
+        * @return stdClass|bool
         */
        public function nextHistoryLine() {
+               if ( !$this->exists() ) {
+                       return false; // Avoid hard failure when the file does not exist. T221812
+               }
+
                # Polymorphic function name to distinguish foreign and local fetches
                $fname = static::class . '::' . __FUNCTION__;
 
@@ -2026,9 +2054,13 @@ class LocalFile extends File {
 
        /**
         * Get the URL of the file description page.
-        * @return string
+        * @return string|bool
         */
        function getDescriptionUrl() {
+               if ( !$this->title ) {
+                       return false; // Avoid hard failure when the file does not exist. T221812
+               }
+
                return $this->title->getLocalURL();
        }
 
@@ -2041,6 +2073,10 @@ class LocalFile extends File {
         * @return string|false
         */
        function getDescriptionText( Language $lang = null ) {
+               if ( !$this->title ) {
+                       return false; // Avoid hard failure when the file does not exist. T221812
+               }
+
                $store = MediaWikiServices::getInstance()->getRevisionStore();
                $revision = $store->getRevisionByTitle( $this->title, 0, Revision::READ_NORMAL );
                if ( !$revision ) {
@@ -2090,6 +2126,10 @@ class LocalFile extends File {
         * @return bool|string
         */
        public function getDescriptionTouched() {
+               if ( !$this->exists() ) {
+                       return false; // Avoid hard failure when the file does not exist. T221812
+               }
+
                // The DB lookup might return false, e.g. if the file was just deleted, or the shared DB repo
                // itself gets it from elsewhere. To avoid repeating the DB lookups in such a case, we
                // need to differentiate between null (uninitialized) and false (failed to load).
index 629d2cd..70f3553 100644 (file)
@@ -2488,8 +2488,9 @@ class WANObjectCache implements IExpiringStore, IStoreKeyEncoder, LoggerAwareInt
         */
        private function determineKeyClassForStats( $key ) {
                $parts = explode( ':', $key, 3 );
-
-               return $parts[1] ?? $parts[0]; // sanity
+               // Sanity fallback in case the key was not made by makeKey.
+               // Replace dots because they are special in StatsD (T232907)
+               return strtr( $parts[1] ?? $parts[0], '.', '_' );
        }
 
        /**
index d713396..4a3445e 100644 (file)
@@ -194,6 +194,10 @@ class SqlBagOStuff extends MediumSpecificBagOStuff {
                                $conn = Database::factory( $type, $info );
                                $conn->clearFlag( DBO_TRX ); // auto-commit mode
                                $this->conns[$shardIndex] = $conn;
+                               // Automatically create the objectcache table for sqlite as needed
+                               if ( $conn->getType() === 'sqlite' ) {
+                                       $this->initSqliteDatabase( $conn );
+                               }
                        }
                        $conn = $this->conns[$shardIndex];
                } else {
@@ -206,10 +210,6 @@ class SqlBagOStuff extends MediumSpecificBagOStuff {
                        $attribs = $lb->getServerAttributes( $lb->getWriterIndex() );
                        $flags = $attribs[Database::ATTR_DB_LEVEL_LOCKING] ? 0 : $lb::CONN_TRX_AUTOCOMMIT;
                        $conn = $lb->getMaintenanceConnectionRef( $index, [], false, $flags );
-                       // Automatically create the objectcache table for sqlite as needed
-                       if ( $conn->getType() === 'sqlite' ) {
-                               $this->initSqliteDatabase( $conn );
-                       }
                }
 
                $this->logger->debug( sprintf( "Connection %s will be used for SqlBagOStuff", $conn ) );
index 6ea046e..0d1579d 100644 (file)
@@ -143,6 +143,7 @@ EXCLUDE_PATTERNS       = LocalSettings.php \
                          AdminSettings.php \
                          .svn \
                          */.git/* \
+                         */README.md \
                          {{EXCLUDE_PATTERNS}}
 EXCLUDE_SYMBOLS        =
 EXAMPLE_PATH           =
index 4401ea8..6b22097 100644 (file)
@@ -83,8 +83,10 @@ class MWDocGen extends Maintenance {
                $this->addOption( 'output',
                        'Path to write doc to',
                        false, true );
-               $this->addOption( 'no-extensions',
-                       'Ignore extensions' );
+               $this->addOption( 'extensions',
+                       'Process the extensions/ directory as well (ignored if --file is used)' );
+               $this->addOption( 'skins',
+                       'Process the skins/ directory as well (ignored if --file is used)' );
        }
 
        public function getDbType() {
@@ -115,22 +117,23 @@ class MWDocGen extends Maintenance {
 
                $this->template = $IP . '/maintenance/Doxyfile';
                $this->excludes = [
-                       'vendor',
-                       'node_modules',
-                       'resources/lib',
                        'images',
+                       'node_modules',
+                       'resources',
                        'static',
                        'tests',
-                       'includes/libs/Message/README.md',
-                       'includes/libs/objectcache/README.md',
-                       'includes/libs/ParamValidator/README.md',
-                       'maintenance/benchmarks/README.md',
-                       'resources/src/mediawiki.ui/styleguide.md',
+                       'vendor',
                ];
                $this->excludePatterns = [];
-               if ( $this->hasOption( 'no-extensions' ) ) {
-                       $this->excludePatterns[] = 'extensions';
-                       $this->excludePatterns[] = 'skins';
+               if ( $this->input === '' ) {
+                       // If no explicit --file filter is set, we're indexing all of $IP,
+                       // but any extension or skin submodules should be excluded by default.
+                       if ( !$this->hasOption( 'extensions' ) ) {
+                               $this->excludePatterns[] = 'extensions';
+                       }
+                       if ( !$this->hasOption( 'skins' ) ) {
+                               $this->excludePatterns[] = 'skins';
+                       }
                }
 
                $this->doDot = shell_exec( 'which dot' );
index a310242..d5bbb11 100644 (file)
@@ -3,7 +3,7 @@
 namespace MediaWiki\Tests\Rest\BasicAccess;
 
 use GuzzleHttp\Psr7\Uri;
-use MediaWiki\MediaWikiServices;
+use MediaWiki\Permissions\PermissionManager;
 use MediaWiki\Rest\BasicAccess\MWBasicAuthorizer;
 use MediaWiki\Rest\Handler;
 use MediaWiki\Rest\RequestData;
@@ -26,20 +26,18 @@ use Wikimedia\ObjectFactory;
 class MWBasicRequestAuthorizerTest extends MediaWikiTestCase {
        private function createRouter( $userRights, $request ) {
                $user = User::newFromName( 'Test user' );
-               // Don't allow the rights to everybody so that user rights kick in.
-               $this->mergeMwGlobalArrayValue( 'wgGroupPermissions', [ '*' => $userRights ] );
-               $this->overrideUserPermissions(
-                       $user,
-                       array_keys( array_filter( $userRights ), function ( $value ) {
-                               return $value === true;
-                       } )
-               );
-
-               global $IP;
-
                $objectFactory = new ObjectFactory(
                        $this->getMockForAbstractClass( ContainerInterface::class )
                );
+               $permissionManager = $this->createMock( PermissionManager::class );
+               // Don't allow the rights to everybody so that user rights kick in.
+               $permissionManager->method( 'isEveryoneAllowed' )->willReturn( false );
+               $permissionManager->method( 'userHasRight' )
+                       ->will( $this->returnCallback( function ( $user, $action ) use ( $userRights ) {
+                               return isset( $userRights[$action] ) && $userRights[$action];
+                       } ) );
+
+               global $IP;
 
                return new Router(
                        [ "$IP/tests/phpunit/unit/includes/Rest/testRoutes.json" ],
@@ -47,9 +45,9 @@ class MWBasicRequestAuthorizerTest extends MediaWikiTestCase {
                        '/rest',
                        new \EmptyBagOStuff(),
                        new ResponseFactory( [] ),
-                       new MWBasicAuthorizer( $user, MediaWikiServices::getInstance()->getPermissionManager() ),
+                       new MWBasicAuthorizer( $user, $permissionManager ),
                        $objectFactory,
-                       new Validator( $objectFactory, $request, $user )
+                       new Validator( $objectFactory, $permissionManager, $request, $user )
                );
        }
 
index 1c9bc41..d05c797 100644 (file)
@@ -5,6 +5,7 @@ namespace MediaWiki\Tests\Rest;
 use EmptyBagOStuff;
 use GuzzleHttp\Psr7\Uri;
 use GuzzleHttp\Psr7\Stream;
+use MediaWiki\Permissions\PermissionManager;
 use MediaWiki\Rest\BasicAccess\StaticBasicAuthorizer;
 use MediaWiki\Rest\Handler;
 use MediaWiki\Rest\EntryPoint;
@@ -32,6 +33,7 @@ class EntryPointTest extends \MediaWikiTestCase {
                $objectFactory = new ObjectFactory(
                        $this->getMockForAbstractClass( ContainerInterface::class )
                );
+               $permissionManager = $this->createMock( PermissionManager::class );
 
                return new Router(
                        [ "$IP/tests/phpunit/unit/includes/Rest/testRoutes.json" ],
@@ -41,7 +43,7 @@ class EntryPointTest extends \MediaWikiTestCase {
                        new ResponseFactory( [] ),
                        new StaticBasicAuthorizer(),
                        $objectFactory,
-                       new Validator( $objectFactory, $request, new User )
+                       new Validator( $objectFactory, $permissionManager, $request, new User )
                );
        }
 
index 7acd237..8f37805 100644 (file)
@@ -193,4 +193,26 @@ class LocalFileTest extends MediaWikiTestCase {
                        'wfLocalFile() returns LocalFile for valid Titles'
                );
        }
+
+       /**
+        * @covers File::getUser
+        */
+       public function testGetUserForNonExistingFile() {
+               $this->assertSame( 'Unknown user', $this->file_hl0->getUser() );
+               $this->assertSame( 0, $this->file_hl0->getUser( 'id' ) );
+       }
+
+       /**
+        * @covers File::getUser
+        */
+       public function testDescriptionShortUrlForNonExistingFile() {
+               $this->assertNull( $this->file_hl0->getDescriptionShortUrl() );
+       }
+
+       /**
+        * @covers File::getUser
+        */
+       public function testDescriptionTextForNonExistingFile() {
+               $this->assertFalse( $this->file_hl0->getDescriptionText() );
+       }
 }
index ac988e6..076bf52 100644 (file)
@@ -1981,6 +1981,8 @@ class WANObjectCacheTest extends PHPUnit\Framework\TestCase {
                        [ 'domain:page:5', 'page' ],
                        [ 'domain:main-key', 'main-key' ],
                        [ 'domain:page:history', 'page' ],
+                       // Regression test for T232907
+                       [ 'domain:foo-bar-1.2:abc:v2', 'foo-bar-1_2' ],
                        [ 'missingdomainkey', 'missingdomainkey' ]
                ];
        }
index 7d682fd..ca3cf10 100644 (file)
@@ -4,6 +4,7 @@ namespace MediaWiki\Tests\Rest\Handler;
 
 use EmptyBagOStuff;
 use GuzzleHttp\Psr7\Uri;
+use MediaWiki\Permissions\PermissionManager;
 use MediaWiki\Rest\BasicAccess\StaticBasicAuthorizer;
 use MediaWiki\Rest\RequestData;
 use MediaWiki\Rest\ResponseFactory;
@@ -55,7 +56,7 @@ class HelloHandlerTest extends \MediaWikiUnitTestCase {
                $objectFactory = new ObjectFactory(
                        $this->getMockForAbstractClass( ContainerInterface::class )
                );
-
+               $permissionManager = $this->createMock( PermissionManager::class );
                $request = new RequestData( $requestInfo );
                $router = new Router(
                        [ __DIR__ . '/../testRoutes.json' ],
@@ -65,7 +66,7 @@ class HelloHandlerTest extends \MediaWikiUnitTestCase {
                        new ResponseFactory( [] ),
                        new StaticBasicAuthorizer(),
                        $objectFactory,
-                       new Validator( $objectFactory, $request, new User )
+                       new Validator( $objectFactory, $permissionManager, $request, new User )
                );
                $response = $router->execute( $request );
                if ( isset( $responseInfo['statusCode'] ) ) {
index 58039ea..270bcfc 100644 (file)
@@ -3,6 +3,7 @@
 namespace MediaWiki\Tests\Rest;
 
 use GuzzleHttp\Psr7\Uri;
+use MediaWiki\Permissions\PermissionManager;
 use MediaWiki\Rest\BasicAccess\StaticBasicAuthorizer;
 use MediaWiki\Rest\Handler;
 use MediaWiki\Rest\HttpException;
@@ -24,6 +25,7 @@ class RouterTest extends \MediaWikiUnitTestCase {
                $objectFactory = new ObjectFactory(
                        $this->getMockForAbstractClass( ContainerInterface::class )
                );
+               $permissionManager = $this->createMock( PermissionManager::class );
                return new Router(
                        [ __DIR__ . '/testRoutes.json' ],
                        [],
@@ -32,7 +34,7 @@ class RouterTest extends \MediaWikiUnitTestCase {
                        new ResponseFactory( [] ),
                        new StaticBasicAuthorizer( $authError ),
                        $objectFactory,
-                       new Validator( $objectFactory, $request, new User )
+                       new Validator( $objectFactory, $permissionManager, $request, new User )
                );
        }