Merge "Permissions: Declare the $nsInfo property and document it in constructor"
authorjenkins-bot <jenkins-bot@gerrit.wikimedia.org>
Sun, 5 May 2019 19:35:38 +0000 (19:35 +0000)
committerGerrit Code Review <gerrit@wikimedia.org>
Sun, 5 May 2019 19:35:38 +0000 (19:35 +0000)
23 files changed:
RELEASE-NOTES-1.34
includes/Revision.php
includes/Revision/RevisionLookup.php
includes/Revision/RevisionStore.php
includes/Title.php
includes/api/ApiSetNotificationTimestamp.php
includes/jobqueue/jobs/ClearUserWatchlistJob.php
includes/user/User.php
includes/user/UserIdentity.php
includes/user/UserIdentityValue.php
includes/watcheditem/NoWriteWatchedItemStore.php
includes/watcheditem/WatchedItem.php
includes/watcheditem/WatchedItemQueryService.php
includes/watcheditem/WatchedItemQueryServiceExtension.php
includes/watcheditem/WatchedItemStore.php
includes/watcheditem/WatchedItemStoreInterface.php
tests/phpunit/MediaWikiTestCase.php
tests/phpunit/includes/Revision/RevisionStoreDbTestBase.php
tests/phpunit/includes/RevisionDbTestBase.php
tests/phpunit/includes/user/UserTest.php
tests/phpunit/includes/watcheditem/NoWriteWatchedItemStoreUnitTest.php
tests/phpunit/includes/watcheditem/WatchedItemQueryServiceUnitTest.php
tests/phpunit/includes/watcheditem/WatchedItemStoreUnitTest.php

index 5d46edd..1050c4d 100644 (file)
@@ -25,6 +25,7 @@ Some specific notes for MediaWiki 1.34 upgrades are below:
 For notes on 1.33.x and older releases, see HISTORY.
 
 === Configuration changes for system administrators in 1.34 ===
+
 ==== New configuration ====
 * …
 
@@ -41,6 +42,7 @@ For notes on 1.33.x and older releases, see HISTORY.
 * …
 
 === External library changes in 1.34 ===
+
 ==== New external libraries ====
 * …
 
@@ -126,6 +128,13 @@ because of Phabricator reports.
   instead.
 * The Config argument to ChangesListSpecialPage::checkStructuredFilterUiEnabled
   is deprecated. Pass only the User argument.
+* WatchedItem::getUser is deprecated. Use getUserIdentity.
+* Passing a Title as the first parameter to the getTimestampById method of
+  RevisionStore is deprecated. Omit it, passing only the remaining parameters.
+* Title::getPreviousRevisionId and Title::getNextRevisionId are deprecated. Use
+  RevisionLookup::getPreviousRevision and RevisionLookup::getNextRevision.
+* The Title parameter to RevisionLookup::getPreviousRevision and
+  RevisionLookup::getNextRevision is deprecated and should be omitted.
 
 === Other changes in 1.34 ===
 * …
index cbaff90..de3c299 100644 (file)
@@ -1008,9 +1008,8 @@ class Revision implements IDBAccessObject {
         * @return Revision|null
         */
        public function getPrevious() {
-               $title = $this->getTitle();
-               $rec = self::getRevisionLookup()->getPreviousRevision( $this->mRecord, $title );
-               return $rec ? new Revision( $rec, self::READ_NORMAL, $title ) : null;
+               $rec = self::getRevisionLookup()->getPreviousRevision( $this->mRecord );
+               return $rec ? new Revision( $rec, self::READ_NORMAL, $this->getTitle() ) : null;
        }
 
        /**
@@ -1019,9 +1018,8 @@ class Revision implements IDBAccessObject {
         * @return Revision|null
         */
        public function getNext() {
-               $title = $this->getTitle();
-               $rec = self::getRevisionLookup()->getNextRevision( $this->mRecord, $title );
-               return $rec ? new Revision( $rec, self::READ_NORMAL, $title ) : null;
+               $rec = self::getRevisionLookup()->getNextRevision( $this->mRecord );
+               return $rec ? new Revision( $rec, self::READ_NORMAL, $this->getTitle() ) : null;
        }
 
        /**
@@ -1256,13 +1254,13 @@ class Revision implements IDBAccessObject {
        /**
         * Get rev_timestamp from rev_id, without loading the rest of the row
         *
-        * @param Title $title
+        * @param Title $title (ignored since 1.34)
         * @param int $id
         * @param int $flags
         * @return string|bool False if not found
         */
        static function getTimestampFromId( $title, $id, $flags = 0 ) {
-               return self::getRevisionStore()->getTimestampFromId( $title, $id, $flags );
+               return self::getRevisionStore()->getTimestampFromId( $id, $flags );
        }
 
        /**
index db6c7c3..17cafc6 100644 (file)
@@ -85,11 +85,12 @@ interface RevisionLookup extends IDBAccessObject {
         * MCR migration note: this replaces Revision::getPrevious
         *
         * @param RevisionRecord $rev
-        * @param Title|null $title if known (optional)
+        * @param int $flags (optional) $flags include:
+        *      IDBAccessObject::READ_LATEST: Select the data from the master
         *
         * @return RevisionRecord|null
         */
-       public function getPreviousRevision( RevisionRecord $rev, Title $title = null );
+       public function getPreviousRevision( RevisionRecord $rev, $flags = 0 );
 
        /**
         * Get next revision for this title
@@ -97,11 +98,24 @@ interface RevisionLookup extends IDBAccessObject {
         * MCR migration note: this replaces Revision::getNext
         *
         * @param RevisionRecord $rev
-        * @param Title|null $title if known (optional)
+        * @param int $flags (optional) $flags include:
+        *      IDBAccessObject::READ_LATEST: Select the data from the master
         *
         * @return RevisionRecord|null
         */
-       public function getNextRevision( RevisionRecord $rev, Title $title = null );
+       public function getNextRevision( RevisionRecord $rev, $flags = 0 );
+
+       /**
+        * Get rev_timestamp from rev_id, without loading the rest of the row.
+        *
+        * MCR migration note: this replaces Revision::getTimestampFromId
+        *
+        * @param int $id
+        * @param int $flags
+        * @return string|bool False if not found
+        * @since 1.34 (present earlier in RevisionStore)
+        */
+       public function getTimestampFromId( $id, $flags = 0 );
 
        /**
         * Load a revision based on a known page ID and current revision ID from the DB
index 0329bd1..bdaac7f 100644 (file)
@@ -278,12 +278,13 @@ class RevisionStore
 
        /**
         * @param int $mode DB_MASTER or DB_REPLICA
+        * @param array $groups
         *
         * @return IDatabase
         */
-       private function getDBConnection( $mode ) {
+       private function getDBConnection( $mode, $groups = [] ) {
                $lb = $this->getDBLoadBalancer();
-               return $lb->getConnection( $mode, [], $this->wikiId );
+               return $lb->getConnection( $mode, $groups, $this->wikiId );
        }
 
        /**
@@ -2548,20 +2549,17 @@ class RevisionStore
        }
 
        /**
-        * Get the revision before $rev in the page's history, if any.
-        * Will return null for the first revision but also for deleted or unsaved revisions.
-        *
-        * MCR migration note: this replaces Revision::getPrevious
-        *
-        * @see Title::getPreviousRevisionID
-        * @see PageArchive::getPreviousRevision
+        * Implementation of getPreviousRevision and getNextRevision.
         *
         * @param RevisionRecord $rev
-        * @param Title|null $title if known (optional)
-        *
+        * @param int $flags
+        * @param string $dir 'next' or 'prev'
         * @return RevisionRecord|null
         */
-       public function getPreviousRevision( RevisionRecord $rev, Title $title = null ) {
+       private function getRelativeRevision( RevisionRecord $rev, $flags, $dir ) {
+               $op = $dir === 'next' ? '>' : '<';
+               $sort = $dir === 'next' ? 'ASC' : 'DESC';
+
                if ( !$rev->getId() || !$rev->getPageId() ) {
                        // revision is unsaved or otherwise incomplete
                        return null;
@@ -2572,54 +2570,86 @@ class RevisionStore
                        return null;
                }
 
-               if ( $title === null ) {
-                       // this would fail for deleted revisions
-                       $title = $this->getTitle( $rev->getPageId(), $rev->getId() );
+               list( $dbType, ) = DBAccessObjectUtils::getDBOptions( $flags );
+               $db = $this->getDBConnection( $dbType, [ 'contributions' ] );
+
+               $ts = $this->getTimestampFromId( $rev->getId(), $flags );
+               if ( $ts === false ) {
+                       // XXX Should this be moved into getTimestampFromId?
+                       $ts = $db->selectField( 'archive', 'ar_timestamp',
+                               [ 'ar_rev_id' => $rev->getId() ], __METHOD__ );
+                       if ( $ts === false ) {
+                               // XXX Is this reachable? How can we have a page id but no timestamp?
+                               return null;
+                       }
                }
+               $ts = $db->addQuotes( $db->timestamp( $ts ) );
+
+               $revId = $db->selectField( 'revision', 'rev_id',
+                       [
+                               'rev_page' => $rev->getPageId(),
+                               "rev_timestamp $op $ts OR (rev_timestamp = $ts AND rev_id $op {$rev->getId()})"
+                       ],
+                       __METHOD__,
+                       [
+                               'ORDER BY' => "rev_timestamp $sort, rev_id $sort",
+                               'IGNORE INDEX' => 'rev_timestamp', // Probably needed for T159319
+                       ]
+               );
 
-               $prev = $title->getPreviousRevisionID( $rev->getId() );
-               if ( !$prev ) {
+               if ( $revId === false ) {
                        return null;
                }
 
-               return $this->getRevisionByTitle( $title, $prev );
+               return $this->getRevisionById( intval( $revId ) );
        }
 
        /**
-        * Get the revision after $rev in the page's history, if any.
-        * Will return null for the latest revision but also for deleted or unsaved revisions.
+        * Get the revision before $rev in the page's history, if any.
+        * Will return null for the first revision but also for deleted or unsaved revisions.
         *
-        * MCR migration note: this replaces Revision::getNext
+        * MCR migration note: this replaces Revision::getPrevious
         *
-        * @see Title::getNextRevisionID
+        * @see Title::getPreviousRevisionID
+        * @see PageArchive::getPreviousRevision
         *
         * @param RevisionRecord $rev
-        * @param Title|null $title if known (optional)
+        * @param int $flags (optional) $flags include:
+        *      IDBAccessObject::READ_LATEST: Select the data from the master
         *
         * @return RevisionRecord|null
         */
-       public function getNextRevision( RevisionRecord $rev, Title $title = null ) {
-               if ( !$rev->getId() || !$rev->getPageId() ) {
-                       // revision is unsaved or otherwise incomplete
-                       return null;
-               }
-
-               if ( $rev instanceof RevisionArchiveRecord ) {
-                       // revision is deleted, so it's not part of the page history
-                       return null;
+       public function getPreviousRevision( RevisionRecord $rev, $flags = 0 ) {
+               if ( $flags instanceof Title ) {
+                       // Old calling convention, we don't use Title here anymore
+                       wfDeprecated( __METHOD__ . ' with Title', '1.34' );
+                       $flags = 0;
                }
 
-               if ( $title === null ) {
-                       // this would fail for deleted revisions
-                       $title = $this->getTitle( $rev->getPageId(), $rev->getId() );
-               }
+               return $this->getRelativeRevision( $rev, $flags, 'prev' );
+       }
 
-               $next = $title->getNextRevisionID( $rev->getId() );
-               if ( !$next ) {
-                       return null;
+       /**
+        * Get the revision after $rev in the page's history, if any.
+        * Will return null for the latest revision but also for deleted or unsaved revisions.
+        *
+        * MCR migration note: this replaces Revision::getNext
+        *
+        * @see Title::getNextRevisionID
+        *
+        * @param RevisionRecord $rev
+        * @param int $flags (optional) $flags include:
+        *      IDBAccessObject::READ_LATEST: Select the data from the master
+        * @return RevisionRecord|null
+        */
+       public function getNextRevision( RevisionRecord $rev, $flags = 0 ) {
+               if ( $flags instanceof Title ) {
+                       // Old calling convention, we don't use Title here anymore
+                       wfDeprecated( __METHOD__ . ' with Title', '1.34' );
+                       $flags = 0;
                }
 
-               return $this->getRevisionByTitle( $title, $next );
+               return $this->getRelativeRevision( $rev, $flags, 'next' );
        }
 
        /**
@@ -2658,21 +2688,27 @@ class RevisionStore
        }
 
        /**
-        * Get rev_timestamp from rev_id, without loading the rest of the row
+        * Get rev_timestamp from rev_id, without loading the rest of the row.
+        *
+        * Historically, there was an extra Title parameter that was passed before $id. This is no
+        * longer needed and is deprecated in 1.34.
         *
         * MCR migration note: this replaces Revision::getTimestampFromId
         *
-        * @param Title $title
         * @param int $id
         * @param int $flags
         * @return string|bool False if not found
         */
-       public function getTimestampFromId( $title, $id, $flags = 0 ) {
+       public function getTimestampFromId( $id, $flags = 0 ) {
+               if ( $id instanceof Title ) {
+                       // Old deprecated calling convention supported for backwards compatibility
+                       $id = $flags;
+                       $flags = func_num_args() > 2 ? func_get_arg( 2 ) : 0;
+               }
                $db = $this->getDBConnectionRefForQueryFlags( $flags );
 
-               $conds = [ 'rev_id' => $id ];
-               $conds['rev_page'] = $title->getArticleID();
-               $timestamp = $db->selectField( 'revision', 'rev_timestamp', $conds, __METHOD__ );
+               $timestamp =
+                       $db->selectField( 'revision', 'rev_timestamp', [ 'rev_id' => $id ], __METHOD__ );
 
                return ( $timestamp !== false ) ? wfTimestamp( TS_MW, $timestamp ) : false;
        }
index 27baeb2..2203ccb 100644 (file)
@@ -3730,57 +3730,25 @@ class Title implements LinkTarget, IDBAccessObject {
         * @return int|bool New revision ID, or false if none exists
         */
        private function getRelativeRevisionID( $revId, $flags, $dir ) {
-               $revId = (int)$revId;
-               if ( $dir === 'next' ) {
-                       $op = '>';
-                       $sort = 'ASC';
-               } elseif ( $dir === 'prev' ) {
-                       $op = '<';
-                       $sort = 'DESC';
-               } else {
-                       throw new InvalidArgumentException( '$dir must be "next" or "prev"' );
-               }
-
-               if ( $flags & self::GAID_FOR_UPDATE ) {
-                       $db = wfGetDB( DB_MASTER );
-               } else {
-                       $db = wfGetDB( DB_REPLICA, 'contributions' );
-               }
-
-               // Intentionally not caring if the specified revision belongs to this
-               // page. We only care about the timestamp.
-               $ts = $db->selectField( 'revision', 'rev_timestamp', [ 'rev_id' => $revId ], __METHOD__ );
-               if ( $ts === false ) {
-                       $ts = $db->selectField( 'archive', 'ar_timestamp', [ 'ar_rev_id' => $revId ], __METHOD__ );
-                       if ( $ts === false ) {
-                               // Or should this throw an InvalidArgumentException or something?
-                               return false;
-                       }
+               $rl = MediaWikiServices::getInstance()->getRevisionLookup();
+               $rlFlags = $flags === self::GAID_FOR_UPDATE ? IDBAccessObject::READ_LATEST : 0;
+               $rev = $rl->getRevisionById( $revId, $rlFlags );
+               if ( !$rev ) {
+                       return false;
                }
-               $ts = $db->addQuotes( $ts );
-
-               $revId = $db->selectField( 'revision', 'rev_id',
-                       [
-                               'rev_page' => $this->getArticleID( $flags ),
-                               "rev_timestamp $op $ts OR (rev_timestamp = $ts AND rev_id $op $revId)"
-                       ],
-                       __METHOD__,
-                       [
-                               'ORDER BY' => "rev_timestamp $sort, rev_id $sort",
-                               'IGNORE INDEX' => 'rev_timestamp', // Probably needed for T159319
-                       ]
-               );
-
-               if ( $revId === false ) {
+               $oldRev = $dir === 'next'
+                       ? $rl->getNextRevision( $rev, $rlFlags )
+                       : $rl->getPreviousRevision( $rev, $rlFlags );
+               if ( !$oldRev ) {
                        return false;
-               } else {
-                       return intval( $revId );
                }
+               return $oldRev->getId();
        }
 
        /**
         * Get the revision ID of the previous revision
         *
+        * @deprecated since 1.34, use RevisionLookup::getPreviousRevision
         * @param int $revId Revision ID. Get the revision that was before this one.
         * @param int $flags Title::GAID_FOR_UPDATE
         * @return int|bool Old revision ID, or false if none exists
@@ -3792,6 +3760,7 @@ class Title implements LinkTarget, IDBAccessObject {
        /**
         * Get the revision ID of the next revision
         *
+        * @deprecated since 1.34, use RevisionLookup::getNextRevision
         * @param int $revId Revision ID. Get the revision that was after this one.
         * @param int $flags Title::GAID_FOR_UPDATE
         * @return int|bool Next revision ID, or false if none exists
index ba4c6e8..d2bbe7b 100644 (file)
@@ -77,8 +77,9 @@ class ApiSetNotificationTimestamp extends ApiBase {
                        $titles = $pageSet->getGoodTitles();
                        $title = reset( $titles );
                        if ( $title ) {
+                               // XXX $title isn't actually used, can we just get rid of the previous six lines?
                                $timestamp = MediaWikiServices::getInstance()->getRevisionStore()
-                                       ->getTimestampFromId( $title, $params['torevid'], IDBAccessObject::READ_LATEST );
+                                       ->getTimestampFromId( $params['torevid'], IDBAccessObject::READ_LATEST );
                                if ( $timestamp ) {
                                        $timestamp = $dbw->timestamp( $timestamp );
                                } else {
index 01fa46c..0cb1a52 100644 (file)
@@ -1,6 +1,7 @@
 <?php
 
 use MediaWiki\MediaWikiServices;
+use MediaWiki\User\UserIdentity;
 
 /**
  * Job to clear a users watchlist in batches.
@@ -23,12 +24,12 @@ class ClearUserWatchlistJob extends Job implements GenericParameterJob {
        }
 
        /**
-        * @param User $user User to clear the watchlist for.
+        * @param UserIdentity $user User to clear the watchlist for.
         * @param int $maxWatchlistId The maximum wl_id at the time the job was first created.
         *
         * @return ClearUserWatchlistJob
         */
-       public static function newForUser( User $user, $maxWatchlistId ) {
+       public static function newForUser( UserIdentity $user, $maxWatchlistId ) {
                return new self( [ 'userId' => $user->getId(), 'maxWatchlistId' => $maxWatchlistId ] );
        }
 
index cdbbcc5..d6e1081 100644 (file)
@@ -3665,12 +3665,25 @@ class User implements IDBAccessObject, UserIdentity {
                return true;
        }
 
+       /**
+        * Alias of isLoggedIn() with a name that describes its actual functionality. UserIdentity has
+        * only this new name and not the old isLoggedIn() variant.
+        *
+        * @return bool True if user is registered on this wiki, i.e., has a user ID. False if user is
+        *   anonymous or has no local account (which can happen when importing). This is equivalent to
+        *   getId() != 0 and is provided for code readability.
+        * @since 1.34
+        */
+       public function isRegistered() {
+               return $this->getId() != 0;
+       }
+
        /**
         * Get whether the user is logged in
         * @return bool
         */
        public function isLoggedIn() {
-               return $this->getId() != 0;
+               return $this->isRegistered();
        }
 
        /**
@@ -3678,7 +3691,7 @@ class User implements IDBAccessObject, UserIdentity {
         * @return bool
         */
        public function isAnon() {
-               return !$this->isLoggedIn();
+               return !$this->isRegistered();
        }
 
        /**
index ac9bbec..64c61fe 100644 (file)
@@ -62,4 +62,12 @@ interface UserIdentity {
         */
        public function equals( UserIdentity $user );
 
+       /**
+        * @since 1.34
+        *
+        * @return bool True if user is registered on this wiki, i.e., has a user ID. False if user is
+        *   anonymous or has no local account (which can happen when importing). This must be
+        *   equivalent to getId() != 0 and is provided for code readability.
+        */
+       public function isRegistered();
 }
index d1fd19d..800ac76 100644 (file)
@@ -93,4 +93,14 @@ class UserIdentityValue implements UserIdentity {
                return $this->getName() === $user->getName();
        }
 
+       /**
+        * @since 1.34
+        *
+        * @return bool True if user is registered on this wiki, i.e., has a user ID. False if user is
+        *   anonymous or has no local account (which can happen when importing). This is equivalent to
+        *   getId() != 0 and is provided for code readability.
+        */
+       public function isRegistered() {
+               return $this->getId() != 0;
+       }
 }
index fc95ebc..d5d175c 100644 (file)
@@ -18,7 +18,9 @@
  * @file
  * @ingroup Watchlist
  */
+
 use MediaWiki\Linker\LinkTarget;
+use MediaWiki\User\UserIdentity;
 use Wikimedia\Rdbms\DBReadOnlyError;
 
 /**
@@ -42,7 +44,7 @@ class NoWriteWatchedItemStore implements WatchedItemStoreInterface {
                $this->actualStore = $actualStore;
        }
 
-       public function countWatchedItems( User $user ) {
+       public function countWatchedItems( UserIdentity $user ) {
                return $this->actualStore->countWatchedItems( $user );
        }
 
@@ -68,27 +70,27 @@ class NoWriteWatchedItemStore implements WatchedItemStoreInterface {
                );
        }
 
-       public function getWatchedItem( User $user, LinkTarget $target ) {
+       public function getWatchedItem( UserIdentity $user, LinkTarget $target ) {
                return $this->actualStore->getWatchedItem( $user, $target );
        }
 
-       public function loadWatchedItem( User $user, LinkTarget $target ) {
+       public function loadWatchedItem( UserIdentity $user, LinkTarget $target ) {
                return $this->actualStore->loadWatchedItem( $user, $target );
        }
 
-       public function getWatchedItemsForUser( User $user, array $options = [] ) {
+       public function getWatchedItemsForUser( UserIdentity $user, array $options = [] ) {
                return $this->actualStore->getWatchedItemsForUser( $user, $options );
        }
 
-       public function isWatched( User $user, LinkTarget $target ) {
+       public function isWatched( UserIdentity $user, LinkTarget $target ) {
                return $this->actualStore->isWatched( $user, $target );
        }
 
-       public function getNotificationTimestampsBatch( User $user, array $targets ) {
+       public function getNotificationTimestampsBatch( UserIdentity $user, array $targets ) {
                return $this->actualStore->getNotificationTimestampsBatch( $user, $targets );
        }
 
-       public function countUnreadNotifications( User $user, $unreadLimit = null ) {
+       public function countUnreadNotifications( UserIdentity $user, $unreadLimit = null ) {
                return $this->actualStore->countUnreadNotifications( $user, $unreadLimit );
        }
 
@@ -100,36 +102,38 @@ class NoWriteWatchedItemStore implements WatchedItemStoreInterface {
                throw new DBReadOnlyError( null, self::DB_READONLY_ERROR );
        }
 
-       public function addWatch( User $user, LinkTarget $target ) {
+       public function addWatch( UserIdentity $user, LinkTarget $target ) {
                throw new DBReadOnlyError( null, self::DB_READONLY_ERROR );
        }
 
-       public function addWatchBatchForUser( User $user, array $targets ) {
+       public function addWatchBatchForUser( UserIdentity $user, array $targets ) {
                throw new DBReadOnlyError( null, self::DB_READONLY_ERROR );
        }
 
-       public function removeWatch( User $user, LinkTarget $target ) {
+       public function removeWatch( UserIdentity $user, LinkTarget $target ) {
                throw new DBReadOnlyError( null, self::DB_READONLY_ERROR );
        }
 
        public function setNotificationTimestampsForUser(
-               User $user,
+               UserIdentity $user,
                $timestamp,
                array $targets = []
        ) {
                throw new DBReadOnlyError( null, self::DB_READONLY_ERROR );
        }
 
-       public function updateNotificationTimestamp( User $editor, LinkTarget $target, $timestamp ) {
+       public function updateNotificationTimestamp(
+               UserIdentity $editor, LinkTarget $target, $timestamp
+       ) {
                throw new DBReadOnlyError( null, self::DB_READONLY_ERROR );
        }
 
-       public function resetAllNotificationTimestampsForUser( User $user ) {
+       public function resetAllNotificationTimestampsForUser( UserIdentity $user ) {
                throw new DBReadOnlyError( null, self::DB_READONLY_ERROR );
        }
 
        public function resetNotificationTimestamp(
-               User $user,
+               UserIdentity $user,
                Title $title,
                $force = '',
                $oldid = 0
@@ -137,19 +141,21 @@ class NoWriteWatchedItemStore implements WatchedItemStoreInterface {
                throw new DBReadOnlyError( null, self::DB_READONLY_ERROR );
        }
 
-       public function clearUserWatchedItems( User $user ) {
+       public function clearUserWatchedItems( UserIdentity $user ) {
                throw new DBReadOnlyError( null, self::DB_READONLY_ERROR );
        }
 
-       public function clearUserWatchedItemsUsingJobQueue( User $user ) {
+       public function clearUserWatchedItemsUsingJobQueue( UserIdentity $user ) {
                throw new DBReadOnlyError( null, self::DB_READONLY_ERROR );
        }
 
-       public function removeWatchBatchForUser( User $user, array $titles ) {
+       public function removeWatchBatchForUser( UserIdentity $user, array $titles ) {
                throw new DBReadOnlyError( null, self::DB_READONLY_ERROR );
        }
 
-       public function getLatestNotificationTimestamp( $timestamp, User $user, LinkTarget $target ) {
+       public function getLatestNotificationTimestamp(
+               $timestamp, UserIdentity $user, LinkTarget $target
+       ) {
                return wfTimestampOrNull( TS_MW, $timestamp );
        }
 }
index 43a9c4e..4bf7f0c 100644 (file)
@@ -20,6 +20,7 @@
  */
 
 use MediaWiki\Linker\LinkTarget;
+use MediaWiki\User\UserIdentity;
 
 /**
  * Representation of a pair of user and title for watchlist entries.
@@ -36,7 +37,7 @@ class WatchedItem {
        private $linkTarget;
 
        /**
-        * @var User
+        * @var UserIdentity
         */
        private $user;
 
@@ -46,12 +47,12 @@ class WatchedItem {
        private $notificationTimestamp;
 
        /**
-        * @param User $user
+        * @param UserIdentity $user
         * @param LinkTarget $linkTarget
         * @param null|string $notificationTimestamp the value of the wl_notificationtimestamp field
         */
        public function __construct(
-               User $user,
+               UserIdentity $user,
                LinkTarget $linkTarget,
                $notificationTimestamp
        ) {
@@ -61,9 +62,17 @@ class WatchedItem {
        }
 
        /**
+        * @deprecated since 1.34, use getUserIdentity()
         * @return User
         */
        public function getUser() {
+               return User::newFromIdentity( $this->user );
+       }
+
+       /**
+        * @return UserIdentity
+        */
+       public function getUserIdentity() {
                return $this->user;
        }
 
index 6094f41..30e3cbe 100644 (file)
@@ -1,8 +1,9 @@
 <?php
 
-use Wikimedia\Rdbms\IDatabase;
 use MediaWiki\Linker\LinkTarget;
+use MediaWiki\User\UserIdentity;
 use Wikimedia\Assert\Assert;
+use Wikimedia\Rdbms\IDatabase;
 use Wikimedia\Rdbms\LoadBalancer;
 
 /**
@@ -121,8 +122,8 @@ class WatchedItemQueryService {
         *        'end'                 => string (format accepted by wfTimestamp) requires 'dir' option,
         *                                 timestamp to end enumerating
         *        'watchlistOwner'      => User user whose watchlist items should be listed if different
-        *                                 than the one specified with $user param,
-        *                                 requires 'watchlistOwnerToken' option
+        *                                 than the one specified with $user param, requires
+        *                                 'watchlistOwnerToken' option
         *        'watchlistOwnerToken' => string a watchlist token used to access another user's
         *                                 watchlist, used with 'watchlistOwnerToken' option
         *        'limit'               => int maximum numbers of items to return
@@ -256,7 +257,7 @@ class WatchedItemQueryService {
        /**
         * For simple listing of user's watchlist items, see WatchedItemStore::getWatchedItemsForUser
         *
-        * @param User $user
+        * @param UserIdentity $user
         * @param array $options Allowed keys:
         *        'sort'         => string optional sorting by namespace ID and title
         *                          one of the self::SORT_* constants
@@ -272,8 +273,8 @@ class WatchedItemQueryService {
         *                          specified using the form option
         * @return WatchedItem[]
         */
-       public function getWatchedItemsForUser( User $user, array $options = [] ) {
-               if ( $user->isAnon() ) {
+       public function getWatchedItemsForUser( UserIdentity $user, array $options = [] ) {
+               if ( !$user->isRegistered() ) {
                        // TODO: should this just return an empty array or rather complain loud at this point
                        // as e.g. ApiBase::getWatchlistUser does?
                        return [];
@@ -460,11 +461,12 @@ class WatchedItemQueryService {
                return $conds;
        }
 
-       private function getWatchlistOwnerId( User $user, array $options ) {
+       private function getWatchlistOwnerId( UserIdentity $user, array $options ) {
                if ( array_key_exists( 'watchlistOwner', $options ) ) {
                        /** @var User $watchlistOwner */
                        $watchlistOwner = $options['watchlistOwner'];
-                       $ownersToken = $watchlistOwner->getOption( 'watchlisttoken' );
+                       $ownersToken =
+                               $watchlistOwner->getOption( 'watchlisttoken' );
                        $token = $options['watchlistOwnerToken'];
                        if ( $ownersToken == '' || !hash_equals( $ownersToken, $token ) ) {
                                throw ApiUsageException::newWithMessage( null, 'apierror-bad-watchlist-token', 'bad_wltoken' );
@@ -613,7 +615,9 @@ class WatchedItemQueryService {
                );
        }
 
-       private function getWatchedItemsForUserQueryConds( IDatabase $db, User $user, array $options ) {
+       private function getWatchedItemsForUserQueryConds(
+               IDatabase $db, UserIdentity $user, array $options
+       ) {
                $conds = [ 'wl_user' => $user->getId() ];
                if ( $options['namespaceIds'] ) {
                        $conds['wl_namespace'] = array_map( 'intval', $options['namespaceIds'] );
index a0e64c5..00770ea 100644 (file)
@@ -1,5 +1,6 @@
 <?php
 
+use MediaWiki\User\UserIdentity;
 use Wikimedia\Rdbms\IResultWrapper;
 use Wikimedia\Rdbms\IDatabase;
 
@@ -21,7 +22,7 @@ interface WatchedItemQueryServiceExtension {
         *
         * @warning Any joins added *must* join on a unique key of the target table
         *  unless you really know what you're doing.
-        * @param User $user
+        * @param UserIdentity $user
         * @param array $options Options from
         *  WatchedItemQueryService::getWatchedItemsWithRecentChangeInfo()
         * @param IDatabase $db Database connection being used for the query
@@ -31,15 +32,16 @@ interface WatchedItemQueryServiceExtension {
         * @param array &$dbOptions Options for Database::select()
         * @param array &$joinConds Join conditions for Database::select()
         */
-       public function modifyWatchedItemsWithRCInfoQuery( User $user, array $options, IDatabase $db,
-               array &$tables, array &$fields, array &$conds, array &$dbOptions, array &$joinConds
+       public function modifyWatchedItemsWithRCInfoQuery( UserIdentity $user, array $options,
+               IDatabase $db, array &$tables, array &$fields, array &$conds, array &$dbOptions,
+               array &$joinConds
        );
 
        /**
         * Modify the results from WatchedItemQueryService::getWatchedItemsWithRecentChangeInfo()
         * before they're returned.
         *
-        * @param User $user
+        * @param UserIdentity $user
         * @param array $options Options from
         *  WatchedItemQueryService::getWatchedItemsWithRecentChangeInfo()
         * @param IDatabase $db Database connection being used for the query
@@ -50,7 +52,7 @@ interface WatchedItemQueryServiceExtension {
         *  [ $recentChangeInfo['rc_timestamp'], $recentChangeInfo['rc_id'] ] from the first item
         *  removed.
         */
-       public function modifyWatchedItemsWithRCInfo( User $user, array $options, IDatabase $db,
+       public function modifyWatchedItemsWithRCInfo( UserIdentity $user, array $options, IDatabase $db,
                array &$items, $res, &$startFrom
        );
 
index e287a35..b7e5559 100644 (file)
@@ -1,12 +1,13 @@
 <?php
 
-use Wikimedia\Rdbms\IDatabase;
 use Liuggio\StatsdClient\Factory\StatsdDataFactoryInterface;
 use MediaWiki\Linker\LinkTarget;
+use MediaWiki\User\UserIdentity;
 use Wikimedia\Assert\Assert;
-use Wikimedia\ScopedCallback;
+use Wikimedia\Rdbms\IDatabase;
 use Wikimedia\Rdbms\ILBFactory;
 use Wikimedia\Rdbms\LoadBalancer;
+use Wikimedia\ScopedCallback;
 
 /**
  * Storage layer class for WatchedItems.
@@ -167,7 +168,7 @@ class WatchedItemStore implements WatchedItemStoreInterface, StatsdAwareInterfac
                } );
        }
 
-       private function getCacheKey( User $user, LinkTarget $target ) {
+       private function getCacheKey( UserIdentity $user, LinkTarget $target ) {
                return $this->cache->makeKey(
                        (string)$target->getNamespace(),
                        $target->getDBkey(),
@@ -176,7 +177,7 @@ class WatchedItemStore implements WatchedItemStoreInterface, StatsdAwareInterfac
        }
 
        private function cache( WatchedItem $item ) {
-               $user = $item->getUser();
+               $user = $item->getUserIdentity();
                $target = $item->getLinkTarget();
                $key = $this->getCacheKey( $user, $target );
                $this->cache->set( $key, $item );
@@ -184,7 +185,7 @@ class WatchedItemStore implements WatchedItemStoreInterface, StatsdAwareInterfac
                $this->stats->increment( 'WatchedItemStore.cache' );
        }
 
-       private function uncache( User $user, LinkTarget $target ) {
+       private function uncache( UserIdentity $user, LinkTarget $target ) {
                $this->cache->delete( $this->getCacheKey( $user, $target ) );
                unset( $this->cacheIndex[$target->getNamespace()][$target->getDBkey()][$user->getId()] );
                $this->stats->increment( 'WatchedItemStore.uncache' );
@@ -201,7 +202,7 @@ class WatchedItemStore implements WatchedItemStoreInterface, StatsdAwareInterfac
                }
        }
 
-       private function uncacheUser( User $user ) {
+       private function uncacheUser( UserIdentity $user ) {
                $this->stats->increment( 'WatchedItemStore.uncacheUser' );
                foreach ( $this->cacheIndex as $ns => $dbKeyArray ) {
                        foreach ( $dbKeyArray as $dbKey => $userArray ) {
@@ -218,12 +219,12 @@ class WatchedItemStore implements WatchedItemStoreInterface, StatsdAwareInterfac
        }
 
        /**
-        * @param User $user
+        * @param UserIdentity $user
         * @param LinkTarget $target
         *
         * @return WatchedItem|false
         */
-       private function getCached( User $user, LinkTarget $target ) {
+       private function getCached( UserIdentity $user, LinkTarget $target ) {
                return $this->cache->get( $this->getCacheKey( $user, $target ) );
        }
 
@@ -231,12 +232,12 @@ class WatchedItemStore implements WatchedItemStoreInterface, StatsdAwareInterfac
         * Return an array of conditions to select or update the appropriate database
         * row.
         *
-        * @param User $user
+        * @param UserIdentity $user
         * @param LinkTarget $target
         *
         * @return array
         */
-       private function dbCond( User $user, LinkTarget $target ) {
+       private function dbCond( UserIdentity $user, LinkTarget $target ) {
                return [
                        'wl_user' => $user->getId(),
                        'wl_namespace' => $target->getNamespace(),
@@ -260,11 +261,11 @@ class WatchedItemStore implements WatchedItemStoreInterface, StatsdAwareInterfac
         *
         * @since 1.30
         *
-        * @param User $user
+        * @param UserIdentity $user
         *
         * @return bool true on success, false when too many items are watched
         */
-       public function clearUserWatchedItems( User $user ) {
+       public function clearUserWatchedItems( UserIdentity $user ) {
                if ( $this->countWatchedItems( $user ) > $this->updateRowsPerQuery ) {
                        return false;
                }
@@ -280,7 +281,7 @@ class WatchedItemStore implements WatchedItemStoreInterface, StatsdAwareInterfac
                return true;
        }
 
-       private function uncacheAllItemsForUser( User $user ) {
+       private function uncacheAllItemsForUser( UserIdentity $user ) {
                $userId = $user->getId();
                foreach ( $this->cacheIndex as $ns => $dbKeyIndex ) {
                        foreach ( $dbKeyIndex as $dbKey => $userIndex ) {
@@ -309,9 +310,9 @@ class WatchedItemStore implements WatchedItemStoreInterface, StatsdAwareInterfac
         *
         * @since 1.31
         *
-        * @param User $user
+        * @param UserIdentity $user
         */
-       public function clearUserWatchedItemsUsingJobQueue( User $user ) {
+       public function clearUserWatchedItemsUsingJobQueue( UserIdentity $user ) {
                $job = ClearUserWatchlistJob::newForUser( $user, $this->getMaxId() );
                $this->queueGroup->push( $job );
        }
@@ -332,10 +333,10 @@ class WatchedItemStore implements WatchedItemStoreInterface, StatsdAwareInterfac
 
        /**
         * @since 1.31
-        * @param User $user
+        * @param UserIdentity $user
         * @return int
         */
-       public function countWatchedItems( User $user ) {
+       public function countWatchedItems( UserIdentity $user ) {
                $dbr = $this->getConnectionRef( DB_REPLICA );
                $return = (int)$dbr->selectField(
                        'watchlist',
@@ -394,16 +395,16 @@ class WatchedItemStore implements WatchedItemStoreInterface, StatsdAwareInterfac
        }
 
        /**
-        * @param User $user
+        * @param UserIdentity $user
         * @param TitleValue[] $titles
         * @return bool
         * @throws MWException
         */
-       public function removeWatchBatchForUser( User $user, array $titles ) {
+       public function removeWatchBatchForUser( UserIdentity $user, array $titles ) {
                if ( $this->readOnlyMode->isReadOnly() ) {
                        return false;
                }
-               if ( $user->isAnon() ) {
+               if ( !$user->isRegistered() ) {
                        return false;
                }
                if ( !$titles ) {
@@ -563,12 +564,12 @@ class WatchedItemStore implements WatchedItemStoreInterface, StatsdAwareInterfac
 
        /**
         * @since 1.27
-        * @param User $user
+        * @param UserIdentity $user
         * @param LinkTarget $target
         * @return bool
         */
-       public function getWatchedItem( User $user, LinkTarget $target ) {
-               if ( $user->isAnon() ) {
+       public function getWatchedItem( UserIdentity $user, LinkTarget $target ) {
+               if ( !$user->isRegistered() ) {
                        return false;
                }
 
@@ -583,13 +584,13 @@ class WatchedItemStore implements WatchedItemStoreInterface, StatsdAwareInterfac
 
        /**
         * @since 1.27
-        * @param User $user
+        * @param UserIdentity $user
         * @param LinkTarget $target
         * @return WatchedItem|bool
         */
-       public function loadWatchedItem( User $user, LinkTarget $target ) {
-               // Only loggedin user can have a watchlist
-               if ( $user->isAnon() ) {
+       public function loadWatchedItem( UserIdentity $user, LinkTarget $target ) {
+               // Only registered user can have a watchlist
+               if ( !$user->isRegistered() ) {
                        return false;
                }
 
@@ -618,11 +619,11 @@ class WatchedItemStore implements WatchedItemStoreInterface, StatsdAwareInterfac
 
        /**
         * @since 1.27
-        * @param User $user
+        * @param UserIdentity $user
         * @param array $options
         * @return WatchedItem[]
         */
-       public function getWatchedItemsForUser( User $user, array $options = [] ) {
+       public function getWatchedItemsForUser( UserIdentity $user, array $options = [] ) {
                $options += [ 'forWrite' => false ];
 
                $dbOptions = [];
@@ -664,27 +665,27 @@ class WatchedItemStore implements WatchedItemStoreInterface, StatsdAwareInterfac
 
        /**
         * @since 1.27
-        * @param User $user
+        * @param UserIdentity $user
         * @param LinkTarget $target
         * @return bool
         */
-       public function isWatched( User $user, LinkTarget $target ) {
+       public function isWatched( UserIdentity $user, LinkTarget $target ) {
                return (bool)$this->getWatchedItem( $user, $target );
        }
 
        /**
         * @since 1.27
-        * @param User $user
+        * @param UserIdentity $user
         * @param LinkTarget[] $targets
         * @return array
         */
-       public function getNotificationTimestampsBatch( User $user, array $targets ) {
+       public function getNotificationTimestampsBatch( UserIdentity $user, array $targets ) {
                $timestamps = [];
                foreach ( $targets as $target ) {
                        $timestamps[$target->getNamespace()][$target->getDBkey()] = false;
                }
 
-               if ( $user->isAnon() ) {
+               if ( !$user->isRegistered() ) {
                        return $timestamps;
                }
 
@@ -728,27 +729,27 @@ class WatchedItemStore implements WatchedItemStoreInterface, StatsdAwareInterfac
 
        /**
         * @since 1.27
-        * @param User $user
+        * @param UserIdentity $user
         * @param LinkTarget $target
         * @throws MWException
         */
-       public function addWatch( User $user, LinkTarget $target ) {
+       public function addWatch( UserIdentity $user, LinkTarget $target ) {
                $this->addWatchBatchForUser( $user, [ $target ] );
        }
 
        /**
         * @since 1.27
-        * @param User $user
+        * @param UserIdentity $user
         * @param LinkTarget[] $targets
         * @return bool
         * @throws MWException
         */
-       public function addWatchBatchForUser( User $user, array $targets ) {
+       public function addWatchBatchForUser( UserIdentity $user, array $targets ) {
                if ( $this->readOnlyMode->isReadOnly() ) {
                        return false;
                }
-               // Only logged-in user can have a watchlist
-               if ( $user->isAnon() ) {
+               // Only registered user can have a watchlist
+               if ( !$user->isRegistered() ) {
                        return false;
                }
 
@@ -799,12 +800,12 @@ class WatchedItemStore implements WatchedItemStoreInterface, StatsdAwareInterfac
 
        /**
         * @since 1.27
-        * @param User $user
+        * @param UserIdentity $user
         * @param LinkTarget $target
         * @return bool
         * @throws MWException
         */
-       public function removeWatch( User $user, LinkTarget $target ) {
+       public function removeWatch( UserIdentity $user, LinkTarget $target ) {
                return $this->removeWatchBatchForUser( $user, [ $target ] );
        }
 
@@ -820,14 +821,16 @@ class WatchedItemStore implements WatchedItemStoreInterface, StatsdAwareInterfac
         * only the specified titles will be updated, and this will be done immediately (not deferred).
         *
         * @since 1.27
-        * @param User $user
+        * @param UserIdentity $user
         * @param string|int $timestamp Value to set the "last viewed" timestamp to (null to clear)
         * @param LinkTarget[] $targets Titles to set the timestamp for; [] means the entire watchlist
         * @return bool
         */
-       public function setNotificationTimestampsForUser( User $user, $timestamp, array $targets = [] ) {
-               // Only loggedin user can have a watchlist
-               if ( $user->isAnon() || $this->readOnlyMode->isReadOnly() ) {
+       public function setNotificationTimestampsForUser(
+               UserIdentity $user, $timestamp, array $targets = []
+       ) {
+               // Only registered user can have a watchlist
+               if ( !$user->isRegistered() || $this->readOnlyMode->isReadOnly() ) {
                        return false;
                }
 
@@ -873,7 +876,9 @@ class WatchedItemStore implements WatchedItemStoreInterface, StatsdAwareInterfac
                return true;
        }
 
-       public function getLatestNotificationTimestamp( $timestamp, User $user, LinkTarget $target ) {
+       public function getLatestNotificationTimestamp(
+               $timestamp, UserIdentity $user, LinkTarget $target
+       ) {
                $timestamp = wfTimestampOrNull( TS_MW, $timestamp );
                if ( $timestamp === null ) {
                        return null; // no notification
@@ -894,12 +899,12 @@ class WatchedItemStore implements WatchedItemStoreInterface, StatsdAwareInterfac
        /**
         * Schedule a DeferredUpdate that sets all of the "last viewed" timestamps for a given user
         * to the same value.
-        * @param User $user
+        * @param UserIdentity $user
         * @param string|int|null $timestamp Value to set all timestamps to, null to clear them
         */
-       public function resetAllNotificationTimestampsForUser( User $user, $timestamp = null ) {
-               // Only loggedin user can have a watchlist
-               if ( $user->isAnon() ) {
+       public function resetAllNotificationTimestampsForUser( UserIdentity $user, $timestamp = null ) {
+               // Only registered user can have a watchlist
+               if ( !$user->isRegistered() ) {
                        return;
                }
 
@@ -920,12 +925,14 @@ class WatchedItemStore implements WatchedItemStoreInterface, StatsdAwareInterfac
 
        /**
         * @since 1.27
-        * @param User $editor
+        * @param UserIdentity $editor
         * @param LinkTarget $target
         * @param string|int $timestamp
         * @return int[]
         */
-       public function updateNotificationTimestamp( User $editor, LinkTarget $target, $timestamp ) {
+       public function updateNotificationTimestamp(
+               UserIdentity $editor, LinkTarget $target, $timestamp
+       ) {
                $dbw = $this->getConnectionRef( DB_MASTER );
                $uids = $dbw->selectFieldValues(
                        'watchlist',
@@ -977,23 +984,32 @@ class WatchedItemStore implements WatchedItemStoreInterface, StatsdAwareInterfac
 
        /**
         * @since 1.27
-        * @param User $user
+        * @param UserIdentity $user
         * @param Title $title
         * @param string $force
         * @param int $oldid
         * @return bool
         */
-       public function resetNotificationTimestamp( User $user, Title $title, $force = '', $oldid = 0 ) {
+       public function resetNotificationTimestamp(
+               UserIdentity $user, Title $title, $force = '', $oldid = 0
+       ) {
                $time = time();
 
-               // Only loggedin user can have a watchlist
-               if ( $this->readOnlyMode->isReadOnly() || $user->isAnon() ) {
+               // Only registered user can have a watchlist
+               if ( $this->readOnlyMode->isReadOnly() || !$user->isRegistered() ) {
                        return false;
                }
 
-               if ( !Hooks::run( 'BeforeResetNotificationTimestamp', [ &$user, &$title, $force, &$oldid ] ) ) {
+               // Hook expects User, not UserIdentity
+               $userObj = User::newFromId( $user->getId() );
+               if ( !Hooks::run( 'BeforeResetNotificationTimestamp',
+                       [ &$userObj, &$title, $force, &$oldid ] )
+               ) {
                        return false;
                }
+               if ( !$userObj->equals( $user ) ) {
+                       $user = $userObj;
+               }
 
                $item = null;
                if ( $force != 'force' ) {
@@ -1053,10 +1069,10 @@ class WatchedItemStore implements WatchedItemStoreInterface, StatsdAwareInterfac
        }
 
        /**
-        * @param User $user
+        * @param UserIdentity $user
         * @return MapCacheLRU|null The map contains prefixed title keys and TS_MW values
         */
-       private function getPageSeenTimestamps( User $user ) {
+       private function getPageSeenTimestamps( UserIdentity $user ) {
                $key = $this->getPageSeenTimestampsKey( $user );
 
                return $this->latestUpdateCache->getWithSetCallback(
@@ -1069,10 +1085,10 @@ class WatchedItemStore implements WatchedItemStoreInterface, StatsdAwareInterfac
        }
 
        /**
-        * @param User $user
+        * @param UserIdentity $user
         * @return string
         */
-       private function getPageSeenTimestampsKey( User $user ) {
+       private function getPageSeenTimestampsKey( UserIdentity $user ) {
                return $this->stash->makeGlobalKey(
                        'watchlist-recent-updates',
                        $this->lbFactory->getLocalDomainID(),
@@ -1088,7 +1104,9 @@ class WatchedItemStore implements WatchedItemStoreInterface, StatsdAwareInterfac
                return "{$target->getNamespace()}:{$target->getDBkey()}";
        }
 
-       private function getNotificationTimestamp( User $user, Title $title, $item, $force, $oldid ) {
+       private function getNotificationTimestamp(
+               UserIdentity $user, Title $title, $item, $force, $oldid
+       ) {
                if ( !$oldid ) {
                        // No oldid given, assuming latest revision; clear the timestamp.
                        return null;
@@ -1137,11 +1155,11 @@ class WatchedItemStore implements WatchedItemStoreInterface, StatsdAwareInterfac
 
        /**
         * @since 1.27
-        * @param User $user
+        * @param UserIdentity $user
         * @param int|null $unreadLimit
         * @return int|bool
         */
-       public function countUnreadNotifications( User $user, $unreadLimit = null ) {
+       public function countUnreadNotifications( UserIdentity $user, $unreadLimit = null ) {
                $dbr = $this->getConnectionRef( DB_REPLICA );
 
                $queryOptions = [];
@@ -1241,10 +1259,10 @@ class WatchedItemStore implements WatchedItemStoreInterface, StatsdAwareInterfac
        }
 
        /**
-        * @param User $user
+        * @param UserIdentity $user
         * @param Title[] $titles
         */
-       private function uncacheTitlesForUser( User $user, array $titles ) {
+       private function uncacheTitlesForUser( UserIdentity $user, array $titles ) {
                foreach ( $titles as $title ) {
                        $this->uncache( $user, $title );
                }
index 349d98a..299c222 100644 (file)
@@ -18,7 +18,9 @@
  * @file
  * @ingroup Watchlist
  */
+
 use MediaWiki\Linker\LinkTarget;
+use MediaWiki\User\UserIdentity;
 use Wikimedia\Rdbms\DBUnexpectedError;
 
 /**
@@ -43,11 +45,11 @@ interface WatchedItemStoreInterface {
         *
         * @since 1.31
         *
-        * @param User $user
+        * @param UserIdentity $user
         *
         * @return int
         */
-       public function countWatchedItems( User $user );
+       public function countWatchedItems( UserIdentity $user );
 
        /**
         * @since 1.31
@@ -115,29 +117,29 @@ interface WatchedItemStoreInterface {
         *
         * @since 1.31
         *
-        * @param User $user
+        * @param UserIdentity $user
         * @param LinkTarget $target
         *
         * @return WatchedItem|false
         */
-       public function getWatchedItem( User $user, LinkTarget $target );
+       public function getWatchedItem( UserIdentity $user, LinkTarget $target );
 
        /**
         * Loads an item from the db
         *
         * @since 1.31
         *
-        * @param User $user
+        * @param UserIdentity $user
         * @param LinkTarget $target
         *
         * @return WatchedItem|false
         */
-       public function loadWatchedItem( User $user, LinkTarget $target );
+       public function loadWatchedItem( UserIdentity $user, LinkTarget $target );
 
        /**
         * @since 1.31
         *
-        * @param User $user
+        * @param UserIdentity $user
         * @param array $options Allowed keys:
         *        'forWrite' => bool defaults to false
         *        'sort' => string optional sorting by namespace ID and title
@@ -145,24 +147,24 @@ interface WatchedItemStoreInterface {
         *
         * @return WatchedItem[]
         */
-       public function getWatchedItemsForUser( User $user, array $options = [] );
+       public function getWatchedItemsForUser( UserIdentity $user, array $options = [] );
 
        /**
         * Must be called separately for Subject & Talk namespaces
         *
         * @since 1.31
         *
-        * @param User $user
+        * @param UserIdentity $user
         * @param LinkTarget $target
         *
         * @return bool
         */
-       public function isWatched( User $user, LinkTarget $target );
+       public function isWatched( UserIdentity $user, LinkTarget $target );
 
        /**
         * @since 1.31
         *
-        * @param User $user
+        * @param UserIdentity $user
         * @param LinkTarget[] $targets
         *
         * @return array multi-dimensional like $return[$namespaceId][$titleString] = $timestamp,
@@ -170,54 +172,54 @@ interface WatchedItemStoreInterface {
         *         - string|null value of wl_notificationtimestamp,
         *         - false if $target is not watched by $user.
         */
-       public function getNotificationTimestampsBatch( User $user, array $targets );
+       public function getNotificationTimestampsBatch( UserIdentity $user, array $targets );
 
        /**
         * Must be called separately for Subject & Talk namespaces
         *
         * @since 1.31
         *
-        * @param User $user
+        * @param UserIdentity $user
         * @param LinkTarget $target
         */
-       public function addWatch( User $user, LinkTarget $target );
+       public function addWatch( UserIdentity $user, LinkTarget $target );
 
        /**
         * @since 1.31
         *
-        * @param User $user
+        * @param UserIdentity $user
         * @param LinkTarget[] $targets
         *
         * @return bool success
         */
-       public function addWatchBatchForUser( User $user, array $targets );
+       public function addWatchBatchForUser( UserIdentity $user, array $targets );
 
        /**
-        * Removes an entry for the User watching the LinkTarget
+        * Removes an entry for the UserIdentity watching the LinkTarget
         * Must be called separately for Subject & Talk namespaces
         *
         * @since 1.31
         *
-        * @param User $user
+        * @param UserIdentity $user
         * @param LinkTarget $target
         *
         * @return bool success
         * @throws DBUnexpectedError
         * @throws MWException
         */
-       public function removeWatch( User $user, LinkTarget $target );
+       public function removeWatch( UserIdentity $user, LinkTarget $target );
 
        /**
         * @since 1.31
         *
-        * @param User $user The user to set the timestamps for
+        * @param UserIdentity $user The user to set the timestamps for
         * @param string|null $timestamp Set the update timestamp to this value
         * @param LinkTarget[] $targets List of targets to update. Default to all targets
         *
         * @return bool success
         */
        public function setNotificationTimestampsForUser(
-               User $user,
+               UserIdentity $user,
                $timestamp,
                array $targets = []
        );
@@ -227,28 +229,29 @@ interface WatchedItemStoreInterface {
         *
         * @since 1.31
         *
-        * @param User $user The user to reset the timestamps for
+        * @param UserIdentity $user The user to reset the timestamps for
         */
-       public function resetAllNotificationTimestampsForUser( User $user );
+       public function resetAllNotificationTimestampsForUser( UserIdentity $user );
 
        /**
         * @since 1.31
         *
-        * @param User $editor The editor that triggered the update. Their notification
+        * @param UserIdentity $editor The editor that triggered the update. Their notification
         *  timestamp will not be updated(they have already seen it)
         * @param LinkTarget $target The target to update timestamps for
         * @param string $timestamp Set the update timestamp to this value
         *
         * @return int[] Array of user IDs the timestamp has been updated for
         */
-       public function updateNotificationTimestamp( User $editor, LinkTarget $target, $timestamp );
+       public function updateNotificationTimestamp(
+               UserIdentity $editor, LinkTarget $target, $timestamp );
 
        /**
         * Reset the notification timestamp of this entry
         *
         * @since 1.31
         *
-        * @param User $user
+        * @param UserIdentity $user
         * @param Title $title
         * @param string $force Whether to force the write query to be executed even if the
         *    page is not watched or the notification timestamp is already NULL.
@@ -258,18 +261,19 @@ interface WatchedItemStoreInterface {
         *
         * @return bool success Whether a job was enqueued
         */
-       public function resetNotificationTimestamp( User $user, Title $title, $force = '', $oldid = 0 );
+       public function resetNotificationTimestamp(
+               UserIdentity $user, Title $title, $force = '', $oldid = 0 );
 
        /**
         * @since 1.31
         *
-        * @param User $user
+        * @param UserIdentity $user
         * @param int|null $unreadLimit
         *
         * @return int|bool The number of unread notifications
         *                  true if greater than or equal to $unreadLimit
         */
-       public function countUnreadNotifications( User $user, $unreadLimit = null );
+       public function countUnreadNotifications( UserIdentity $user, $unreadLimit = null );
 
        /**
         * Check if the given title already is watched by the user, and if so
@@ -303,28 +307,28 @@ interface WatchedItemStoreInterface {
         *
         * @since 1.31
         *
-        * @param User $user
+        * @param UserIdentity $user
         */
-       public function clearUserWatchedItems( User $user );
+       public function clearUserWatchedItems( UserIdentity $user );
 
        /**
         * Queues a job that will clear the users watchlist using the Job Queue.
         *
         * @since 1.31
         *
-        * @param User $user
+        * @param UserIdentity $user
         */
-       public function clearUserWatchedItemsUsingJobQueue( User $user );
+       public function clearUserWatchedItemsUsingJobQueue( UserIdentity $user );
 
        /**
         * @since 1.32
         *
-        * @param User $user
+        * @param UserIdentity $user
         * @param LinkTarget[] $targets
         *
         * @return bool success
         */
-       public function removeWatchBatchForUser( User $user, array $targets );
+       public function removeWatchBatchForUser( UserIdentity $user, array $targets );
 
        /**
         * Convert $timestamp to TS_MW or return null if the page was visited since then by $user
@@ -335,9 +339,10 @@ interface WatchedItemStoreInterface {
         * Usage of this method should be limited to WatchedItem* classes
         *
         * @param string|null $timestamp Value of wl_notificationtimestamp from the DB
-        * @param User $user
+        * @param UserIdentity $user
         * @param LinkTarget $target
         * @return string TS_MW timestamp or null
         */
-       public function getLatestNotificationTimestamp( $timestamp, User $user, LinkTarget $target );
+       public function getLatestNotificationTimestamp(
+               $timestamp, UserIdentity $user, LinkTarget $target );
 }
index ebc3b79..fa5832e 100644 (file)
@@ -2408,4 +2408,18 @@ abstract class MediaWikiTestCase extends PHPUnit\Framework\TestCase {
                        'comment' => $comment,
                ] );
        }
+
+       /**
+        * Returns a PHPUnit constraint that matches anything other than a fixed set of values. This can
+        * be used to whitelist values, e.g.
+        *   $mock->expects( $this->never() )->method( $this->anythingBut( 'foo', 'bar' ) );
+        * which will throw if any unexpected method is called.
+        *
+        * @param mixed ...$values Values that are not matched
+        */
+       protected function anythingBut( ...$values ) {
+               return $this->logicalNot( $this->logicalOr(
+                       ...array_map( [ $this, 'matches' ], $values )
+               ) );
+       }
 }
index b183fca..3467153 100644 (file)
@@ -1416,10 +1416,7 @@ abstract class RevisionStoreDbTestBase extends MediaWikiTestCase {
                        ->value['revision'];
 
                $store = MediaWikiServices::getInstance()->getRevisionStore();
-               $result = $store->getTimestampFromId(
-                       $page->getTitle(),
-                       $rev->getId()
-               );
+               $result = $store->getTimestampFromId( $rev->getId() );
 
                $this->assertSame( $rev->getTimestamp(), $result );
        }
@@ -1434,10 +1431,7 @@ abstract class RevisionStoreDbTestBase extends MediaWikiTestCase {
                        ->value['revision'];
 
                $store = MediaWikiServices::getInstance()->getRevisionStore();
-               $result = $store->getTimestampFromId(
-                       $page->getTitle(),
-                       $rev->getId() + 1
-               );
+               $result = $store->getTimestampFromId( $rev->getId() + 1 );
 
                $this->assertFalse( $result );
        }
index 13ddffa..96e2766 100644 (file)
@@ -625,6 +625,34 @@ abstract class RevisionDbTestBase extends MediaWikiTestCase {
                $this->assertEquals( $latestRevision, $newRevision->getPrevious()->getId() );
        }
 
+       /**
+        * @covers Title::getPreviousRevisionID
+        * @covers Title::getRelativeRevisionID
+        * @covers MediaWiki\Revision\RevisionStore::getPreviousRevision
+        * @covers MediaWiki\Revision\RevisionStore::getRelativeRevision
+        */
+       public function testTitleGetPreviousRevisionID() {
+               $oldestId = $this->testPage->getOldestRevision()->getId();
+               $latestId = $this->testPage->getLatest();
+
+               $title = $this->testPage->getTitle();
+
+               $this->assertFalse( $title->getPreviousRevisionID( $oldestId ) );
+
+               $this->testPage->doEditContent( new WikitextContent( __METHOD__ ), __METHOD__ );
+               $newId = $this->testPage->getRevision()->getId();
+
+               $this->assertEquals( $latestId, $title->getPreviousRevisionID( $newId ) );
+       }
+
+       /**
+        * @covers Title::getPreviousRevisionID
+        * @covers Title::getRelativeRevisionID
+        */
+       public function testTitleGetPreviousRevisionID_invalid() {
+               $this->assertFalse( $this->testPage->getTitle()->getPreviousRevisionID( 123456789 ) );
+       }
+
        /**
         * @covers Revision::getNext
         */
@@ -640,6 +668,33 @@ abstract class RevisionDbTestBase extends MediaWikiTestCase {
                $this->assertEquals( $rev2->getId(), $rev1->getNext()->getId() );
        }
 
+       /**
+        * @covers Title::getNextRevisionID
+        * @covers Title::getRelativeRevisionID
+        * @covers MediaWiki\Revision\RevisionStore::getNextRevision
+        * @covers MediaWiki\Revision\RevisionStore::getRelativeRevision
+        */
+       public function testTitleGetNextRevisionID() {
+               $title = $this->testPage->getTitle();
+
+               $origId = $this->testPage->getLatest();
+
+               $this->assertFalse( $title->getNextRevisionID( $origId ) );
+
+               $this->testPage->doEditContent( new WikitextContent( __METHOD__ ), __METHOD__ );
+               $newId = $this->testPage->getLatest();
+
+               $this->assertSame( $this->testPage->getLatest(), $title->getNextRevisionID( $origId ) );
+       }
+
+       /**
+        * @covers Title::getNextRevisionID
+        * @covers Title::getRelativeRevisionID
+        */
+       public function testTitleGetNextRevisionID_invalid() {
+               $this->assertFalse( $this->testPage->getTitle()->getNextRevisionID( 123456789 ) );
+       }
+
        /**
         * @covers Revision::newNullRevision
         */
index 481da75..02b2f3c 100644 (file)
@@ -504,20 +504,24 @@ class UserTest extends MediaWikiTestCase {
        }
 
        /**
+        * @covers User::isRegistered
         * @covers User::isLoggedIn
         * @covers User::isAnon
         */
        public function testLoggedIn() {
                $user = $this->getMutableTestUser()->getUser();
+               $this->assertTrue( $user->isRegistered() );
                $this->assertTrue( $user->isLoggedIn() );
                $this->assertFalse( $user->isAnon() );
 
                // Non-existent users are perceived as anonymous
                $user = User::newFromName( 'UTNonexistent' );
+               $this->assertFalse( $user->isRegistered() );
                $this->assertFalse( $user->isLoggedIn() );
                $this->assertTrue( $user->isAnon() );
 
                $user = new User;
+               $this->assertFalse( $user->isRegistered() );
                $this->assertFalse( $user->isLoggedIn() );
                $this->assertTrue( $user->isAnon() );
        }
index a8761e3..cc60899 100644 (file)
@@ -1,5 +1,7 @@
 <?php
 
+use MediaWiki\User\UserIdentityValue;
+
 /**
  * @author Addshore
  *
@@ -14,7 +16,8 @@ class NoWriteWatchedItemStoreUnitTest extends MediaWikiTestCase {
                $noWriteService = new NoWriteWatchedItemStore( $innerService );
 
                $this->setExpectedException( DBReadOnlyError::class );
-               $noWriteService->addWatch( $this->getTestSysop()->getUser(), new TitleValue( 0, 'Foo' ) );
+               $noWriteService->addWatch(
+                       new UserIdentityValue( 1, 'MockUser', 0 ), new TitleValue( 0, 'Foo' ) );
        }
 
        public function testAddWatchBatchForUser() {
@@ -24,7 +27,7 @@ class NoWriteWatchedItemStoreUnitTest extends MediaWikiTestCase {
                $noWriteService = new NoWriteWatchedItemStore( $innerService );
 
                $this->setExpectedException( DBReadOnlyError::class );
-               $noWriteService->addWatchBatchForUser( $this->getTestSysop()->getUser(), [] );
+               $noWriteService->addWatchBatchForUser( new UserIdentityValue( 1, 'MockUser', 0 ), [] );
        }
 
        public function testRemoveWatch() {
@@ -34,7 +37,8 @@ class NoWriteWatchedItemStoreUnitTest extends MediaWikiTestCase {
                $noWriteService = new NoWriteWatchedItemStore( $innerService );
 
                $this->setExpectedException( DBReadOnlyError::class );
-               $noWriteService->removeWatch( $this->getTestSysop()->getUser(), new TitleValue( 0, 'Foo' ) );
+               $noWriteService->removeWatch(
+                       new UserIdentityValue( 1, 'MockUser', 0 ), new TitleValue( 0, 'Foo' ) );
        }
 
        public function testSetNotificationTimestampsForUser() {
@@ -45,7 +49,7 @@ class NoWriteWatchedItemStoreUnitTest extends MediaWikiTestCase {
 
                $this->setExpectedException( DBReadOnlyError::class );
                $noWriteService->setNotificationTimestampsForUser(
-                       $this->getTestSysop()->getUser(),
+                       new UserIdentityValue( 1, 'MockUser', 0 ),
                        'timestamp',
                        []
                );
@@ -59,7 +63,7 @@ class NoWriteWatchedItemStoreUnitTest extends MediaWikiTestCase {
 
                $this->setExpectedException( DBReadOnlyError::class );
                $noWriteService->updateNotificationTimestamp(
-                       $this->getTestSysop()->getUser(),
+                       new UserIdentityValue( 1, 'MockUser', 0 ),
                        new TitleValue( 0, 'Foo' ),
                        'timestamp'
                );
@@ -73,7 +77,7 @@ class NoWriteWatchedItemStoreUnitTest extends MediaWikiTestCase {
 
                $this->setExpectedException( DBReadOnlyError::class );
                $noWriteService->resetNotificationTimestamp(
-                       $this->getTestSysop()->getUser(),
+                       new UserIdentityValue( 1, 'MockUser', 0 ),
                        Title::newFromText( 'Foo' )
                );
        }
@@ -85,7 +89,7 @@ class NoWriteWatchedItemStoreUnitTest extends MediaWikiTestCase {
                $noWriteService = new NoWriteWatchedItemStore( $innerService );
 
                $return = $noWriteService->countWatchedItems(
-                       $this->getTestSysop()->getUser()
+                       new UserIdentityValue( 1, 'MockUser', 0 )
                );
                $this->assertEquals( __METHOD__, $return );
        }
@@ -154,7 +158,7 @@ class NoWriteWatchedItemStoreUnitTest extends MediaWikiTestCase {
                $noWriteService = new NoWriteWatchedItemStore( $innerService );
 
                $return = $noWriteService->getWatchedItem(
-                       $this->getTestSysop()->getUser(),
+                       new UserIdentityValue( 1, 'MockUser', 0 ),
                        new TitleValue( 0, 'Foo' )
                );
                $this->assertEquals( __METHOD__, $return );
@@ -167,7 +171,7 @@ class NoWriteWatchedItemStoreUnitTest extends MediaWikiTestCase {
                $noWriteService = new NoWriteWatchedItemStore( $innerService );
 
                $return = $noWriteService->loadWatchedItem(
-                       $this->getTestSysop()->getUser(),
+                       new UserIdentityValue( 1, 'MockUser', 0 ),
                        new TitleValue( 0, 'Foo' )
                );
                $this->assertEquals( __METHOD__, $return );
@@ -182,7 +186,7 @@ class NoWriteWatchedItemStoreUnitTest extends MediaWikiTestCase {
                $noWriteService = new NoWriteWatchedItemStore( $innerService );
 
                $return = $noWriteService->getWatchedItemsForUser(
-                       $this->getTestSysop()->getUser(),
+                       new UserIdentityValue( 1, 'MockUser', 0 ),
                        []
                );
                $this->assertEquals( __METHOD__, $return );
@@ -195,7 +199,7 @@ class NoWriteWatchedItemStoreUnitTest extends MediaWikiTestCase {
                $noWriteService = new NoWriteWatchedItemStore( $innerService );
 
                $return = $noWriteService->isWatched(
-                       $this->getTestSysop()->getUser(),
+                       new UserIdentityValue( 1, 'MockUser', 0 ),
                        new TitleValue( 0, 'Foo' )
                );
                $this->assertEquals( __METHOD__, $return );
@@ -210,7 +214,7 @@ class NoWriteWatchedItemStoreUnitTest extends MediaWikiTestCase {
                $noWriteService = new NoWriteWatchedItemStore( $innerService );
 
                $return = $noWriteService->getNotificationTimestampsBatch(
-                       $this->getTestSysop()->getUser(),
+                       new UserIdentityValue( 1, 'MockUser', 0 ),
                        [ new TitleValue( 0, 'Foo' ) ]
                );
                $this->assertEquals( __METHOD__, $return );
@@ -225,7 +229,7 @@ class NoWriteWatchedItemStoreUnitTest extends MediaWikiTestCase {
                $noWriteService = new NoWriteWatchedItemStore( $innerService );
 
                $return = $noWriteService->countUnreadNotifications(
-                       $this->getTestSysop()->getUser(),
+                       new UserIdentityValue( 1, 'MockUser', 0 ),
                        88
                );
                $this->assertEquals( __METHOD__, $return );
index b22b7f8..3ba8773 100644 (file)
@@ -1,5 +1,6 @@
 <?php
 
+use MediaWiki\User\UserIdentityValue;
 use Wikimedia\Rdbms\IDatabase;
 use Wikimedia\Rdbms\LoadBalancer;
 use Wikimedia\TestingAccessWrapper;
@@ -156,34 +157,32 @@ class WatchedItemQueryServiceUnitTest extends MediaWikiTestCase {
 
        /**
         * @param int $id
+        * @param string[] $extraMethods Extra methods that are expected might be called
         * @return PHPUnit_Framework_MockObject_MockObject|User
         */
-       private function getMockNonAnonUserWithId( $id ) {
+       private function getMockNonAnonUserWithId( $id, array $extraMethods = [] ) {
                $mock = $this->getMockBuilder( User::class )->getMock();
-               $mock->expects( $this->any() )
-                       ->method( 'isAnon' )
-                       ->will( $this->returnValue( false ) );
-               $mock->expects( $this->any() )
-                       ->method( 'getId' )
-                       ->will( $this->returnValue( $id ) );
+               $mock->method( 'isRegistered' )->willReturn( true );
+               $mock->method( 'getId' )->willReturn( $id );
+               $methods = array_merge( [
+                       'isRegistered',
+                       'getId',
+               ], $extraMethods );
+               $mock->expects( $this->never() )->method( $this->anythingBut( ...$methods ) );
                return $mock;
        }
 
        /**
         * @param int $id
+        * @param string[] $extraMethods Extra methods that are expected might be called
         * @return PHPUnit_Framework_MockObject_MockObject|User
         */
-       private function getMockUnrestrictedNonAnonUserWithId( $id ) {
-               $mock = $this->getMockNonAnonUserWithId( $id );
-               $mock->expects( $this->any() )
-                       ->method( 'isAllowed' )
-                       ->will( $this->returnValue( true ) );
-               $mock->expects( $this->any() )
-                       ->method( 'isAllowedAny' )
-                       ->will( $this->returnValue( true ) );
-               $mock->expects( $this->any() )
-                       ->method( 'useRCPatrol' )
-                       ->will( $this->returnValue( true ) );
+       private function getMockUnrestrictedNonAnonUserWithId( $id, array $extraMethods = [] ) {
+               $mock = $this->getMockNonAnonUserWithId( $id,
+                       array_merge( [ 'isAllowed', 'isAllowedAny', 'useRCPatrol' ], $extraMethods ) );
+               $mock->method( 'isAllowed' )->willReturn( true );
+               $mock->method( 'isAllowedAny' )->willReturn( true );
+               $mock->method( 'useRCPatrol' )->willReturn( true );
                return $mock;
        }
 
@@ -193,18 +192,19 @@ class WatchedItemQueryServiceUnitTest extends MediaWikiTestCase {
         * @return PHPUnit_Framework_MockObject_MockObject|User
         */
        private function getMockNonAnonUserWithIdAndRestrictedPermissions( $id, $notAllowedAction ) {
-               $mock = $this->getMockNonAnonUserWithId( $id );
+               $mock = $this->getMockNonAnonUserWithId( $id,
+                       [ 'isAllowed', 'isAllowedAny', 'useRCPatrol', 'useNPPatrol' ] );
 
-               $mock->expects( $this->any() )
-                       ->method( 'isAllowed' )
+               $mock->method( 'isAllowed' )
                        ->will( $this->returnCallback( function ( $action ) use ( $notAllowedAction ) {
                                return $action !== $notAllowedAction;
                        } ) );
-               $mock->expects( $this->any() )
-                       ->method( 'isAllowedAny' )
+               $mock->method( 'isAllowedAny' )
                        ->will( $this->returnCallback( function ( ...$actions ) use ( $notAllowedAction ) {
                                return !in_array( $notAllowedAction, $actions );
                        } ) );
+               $mock->method( 'useRCPatrol' )->willReturn( false );
+               $mock->method( 'useNPPatrol' )->willReturn( false );
 
                return $mock;
        }
@@ -214,7 +214,8 @@ class WatchedItemQueryServiceUnitTest extends MediaWikiTestCase {
         * @return PHPUnit_Framework_MockObject_MockObject|User
         */
        private function getMockNonAnonUserWithIdAndNoPatrolRights( $id ) {
-               $mock = $this->getMockNonAnonUserWithId( $id );
+               $mock = $this->getMockNonAnonUserWithId( $id,
+                       [ 'isAllowed', 'isAllowedAny', 'useRCPatrol', 'useNPPatrol' ] );
 
                $mock->expects( $this->any() )
                        ->method( 'isAllowed' )
@@ -233,14 +234,6 @@ class WatchedItemQueryServiceUnitTest extends MediaWikiTestCase {
                return $mock;
        }
 
-       private function getMockAnonUser() {
-               $mock = $this->getMockBuilder( User::class )->getMock();
-               $mock->expects( $this->any() )
-                       ->method( 'isAnon' )
-                       ->will( $this->returnValue( true ) );
-               return $mock;
-       }
-
        private function getFakeRow( array $rowValues ) {
                $fakeRow = new stdClass();
                foreach ( $rowValues as $valueName => $value ) {
@@ -1382,7 +1375,7 @@ class WatchedItemQueryServiceUnitTest extends MediaWikiTestCase {
 
                $queryService = $this->newService( $mockDb );
                $user = $this->getMockUnrestrictedNonAnonUserWithId( 1 );
-               $otherUser = $this->getMockUnrestrictedNonAnonUserWithId( 2 );
+               $otherUser = $this->getMockUnrestrictedNonAnonUserWithId( 2, [ 'getOption' ] );
                $otherUser->expects( $this->once() )
                        ->method( 'getOption' )
                        ->with( 'watchlisttoken' )
@@ -1413,7 +1406,7 @@ class WatchedItemQueryServiceUnitTest extends MediaWikiTestCase {
 
                $queryService = $this->newService( $mockDb );
                $user = $this->getMockUnrestrictedNonAnonUserWithId( 1 );
-               $otherUser = $this->getMockUnrestrictedNonAnonUserWithId( 2 );
+               $otherUser = $this->getMockUnrestrictedNonAnonUserWithId( 2, [ 'getOption' ] );
                $otherUser->expects( $this->once() )
                        ->method( 'getOption' )
                        ->with( 'watchlisttoken' )
@@ -1713,7 +1706,8 @@ class WatchedItemQueryServiceUnitTest extends MediaWikiTestCase {
 
                $queryService = $this->newService( $mockDb );
 
-               $items = $queryService->getWatchedItemsForUser( $this->getMockAnonUser() );
+               $items = $queryService->getWatchedItemsForUser(
+                       new UserIdentityValue( 0, 'AnonUser', 0 ) );
                $this->assertEmpty( $items );
        }
 
index 2f95688..ca5ae3e 100644 (file)
@@ -1,5 +1,6 @@
 <?php
 use MediaWiki\Linker\LinkTarget;
+use MediaWiki\User\UserIdentityValue;
 use Wikimedia\Rdbms\LBFactory;
 use Wikimedia\Rdbms\LoadBalancer;
 use Wikimedia\ScopedCallback;
@@ -108,31 +109,6 @@ class WatchedItemStoreUnitTest extends MediaWikiTestCase {
                return $mock;
        }
 
-       /**
-        * @param int $id
-        * @return PHPUnit_Framework_MockObject_MockObject|User
-        */
-       private function getMockNonAnonUserWithId( $id ) {
-               $mock = $this->createMock( User::class );
-               $mock->expects( $this->any() )
-                       ->method( 'isAnon' )
-                       ->will( $this->returnValue( false ) );
-               $mock->expects( $this->any() )
-                       ->method( 'getId' )
-                       ->will( $this->returnValue( $id ) );
-               $mock->expects( $this->any() )
-                       ->method( 'getUserPage' )
-                       ->will( $this->returnValue( Title::makeTitle( NS_USER, 'MockUser' ) ) );
-               return $mock;
-       }
-
-       /**
-        * @return User
-        */
-       private function getAnonUser() {
-               return User::newFromName( 'Anon_User' );
-       }
-
        private function getFakeRow( array $rowValues ) {
                $fakeRow = new stdClass();
                foreach ( $rowValues as $valueName => $value ) {
@@ -158,7 +134,7 @@ class WatchedItemStoreUnitTest extends MediaWikiTestCase {
        }
 
        public function testClearWatchedItems() {
-               $user = $this->getMockNonAnonUserWithId( 7 );
+               $user = new UserIdentityValue( 7, 'MockUser', 0 );
 
                $mockDb = $this->getMockDb();
                $mockDb->expects( $this->once() )
@@ -200,7 +176,7 @@ class WatchedItemStoreUnitTest extends MediaWikiTestCase {
        }
 
        public function testClearWatchedItems_tooManyItemsWatched() {
-               $user = $this->getMockNonAnonUserWithId( 7 );
+               $user = new UserIdentityValue( 7, 'MockUser', 0 );
 
                $mockDb = $this->getMockDb();
                $mockDb->expects( $this->once() )
@@ -231,7 +207,7 @@ class WatchedItemStoreUnitTest extends MediaWikiTestCase {
        }
 
        public function testCountWatchedItems() {
-               $user = $this->getMockNonAnonUserWithId( 1 );
+               $user = new UserIdentityValue( 1, 'MockUser', 0 );
 
                $mockDb = $this->getMockDb();
                $mockDb->expects( $this->exactly( 1 ) )
@@ -716,7 +692,7 @@ class WatchedItemStoreUnitTest extends MediaWikiTestCase {
        }
 
        public function testCountUnreadNotifications() {
-               $user = $this->getMockNonAnonUserWithId( 1 );
+               $user = new UserIdentityValue( 1, 'MockUser', 0 );
 
                $mockDb = $this->getMockDb();
                $mockDb->expects( $this->exactly( 1 ) )
@@ -751,7 +727,7 @@ class WatchedItemStoreUnitTest extends MediaWikiTestCase {
         * @dataProvider provideIntWithDbUnsafeVersion
         */
        public function testCountUnreadNotifications_withUnreadLimit_overLimit( $limit ) {
-               $user = $this->getMockNonAnonUserWithId( 1 );
+               $user = new UserIdentityValue( 1, 'MockUser', 0 );
 
                $mockDb = $this->getMockDb();
                $mockDb->expects( $this->exactly( 1 ) )
@@ -790,7 +766,7 @@ class WatchedItemStoreUnitTest extends MediaWikiTestCase {
         * @dataProvider provideIntWithDbUnsafeVersion
         */
        public function testCountUnreadNotifications_withUnreadLimit_underLimit( $limit ) {
-               $user = $this->getMockNonAnonUserWithId( 1 );
+               $user = new UserIdentityValue( 1, 'MockUser', 0 );
 
                $mockDb = $this->getMockDb();
                $mockDb->expects( $this->exactly( 1 ) )
@@ -852,8 +828,8 @@ class WatchedItemStoreUnitTest extends MediaWikiTestCase {
                );
 
                $store->duplicateEntry(
-                       Title::newFromText( 'Old_Title' ),
-                       Title::newFromText( 'New_Title' )
+                       new TitleValue( 0, 'Old_Title' ),
+                       new TitleValue( 0, 'New_Title' )
                );
        }
 
@@ -912,8 +888,8 @@ class WatchedItemStoreUnitTest extends MediaWikiTestCase {
                );
 
                $store->duplicateEntry(
-                       Title::newFromText( 'Old_Title' ),
-                       Title::newFromText( 'New_Title' )
+                       new TitleValue( 0, 'Old_Title' ),
+                       new TitleValue( 0, 'New_Title' )
                );
        }
 
@@ -960,14 +936,14 @@ class WatchedItemStoreUnitTest extends MediaWikiTestCase {
                );
 
                $store->duplicateAllAssociatedEntries(
-                       Title::newFromText( 'Old_Title' ),
-                       Title::newFromText( 'New_Title' )
+                       new TitleValue( 0, 'Old_Title' ),
+                       new TitleValue( 0, 'New_Title' )
                );
        }
 
        public function provideLinkTargetPairs() {
                return [
-                       [ Title::newFromText( 'Old_Title' ), Title::newFromText( 'New_Title' ) ],
+                       [ new TitleValue( 0, 'Old_Title' ), new TitleValue( 0, 'New_Title' ) ],
                        [ new TitleValue( 0, 'Old_Title' ),  new TitleValue( 0, 'New_Title' ) ],
                ];
        }
@@ -1089,8 +1065,8 @@ class WatchedItemStoreUnitTest extends MediaWikiTestCase {
                );
 
                $store->addWatch(
-                       $this->getMockNonAnonUserWithId( 1 ),
-                       Title::newFromText( 'Some_Page' )
+                       new UserIdentityValue( 1, 'MockUser', 0 ),
+                       new TitleValue( 0, 'Some_Page' )
                );
        }
 
@@ -1111,8 +1087,8 @@ class WatchedItemStoreUnitTest extends MediaWikiTestCase {
                );
 
                $store->addWatch(
-                       $this->getAnonUser(),
-                       Title::newFromText( 'Some_Page' )
+                       new UserIdentityValue( 0, 'AnonUser', 0 ),
+                       new TitleValue( 0, 'Some_Page' )
                );
        }
 
@@ -1126,7 +1102,7 @@ class WatchedItemStoreUnitTest extends MediaWikiTestCase {
 
                $this->assertFalse(
                        $store->addWatchBatchForUser(
-                               $this->getMockNonAnonUserWithId( 1 ),
+                               new UserIdentityValue( 1, 'MockUser', 0 ),
                                [ new TitleValue( 0, 'Some_Page' ), new TitleValue( 1, 'Some_Page' ) ]
                        )
                );
@@ -1175,7 +1151,7 @@ class WatchedItemStoreUnitTest extends MediaWikiTestCase {
                        $this->getMockReadOnlyMode()
                );
 
-               $mockUser = $this->getMockNonAnonUserWithId( 1 );
+               $mockUser = new UserIdentityValue( 1, 'MockUser', 0 );
 
                $this->assertTrue(
                        $store->addWatchBatchForUser(
@@ -1203,14 +1179,14 @@ class WatchedItemStoreUnitTest extends MediaWikiTestCase {
 
                $this->assertFalse(
                        $store->addWatchBatchForUser(
-                               $this->getAnonUser(),
+                               new UserIdentityValue( 0, 'AnonUser', 0 ),
                                [ new TitleValue( 0, 'Other_Page' ) ]
                        )
                );
        }
 
        public function testAddWatchBatchReturnsTrue_whenGivenEmptyList() {
-               $user = $this->getMockNonAnonUserWithId( 1 );
+               $user = new UserIdentityValue( 1, 'MockUser', 0 );
                $mockDb = $this->getMockDb();
                $mockDb->expects( $this->never() )
                        ->method( 'insert' );
@@ -1263,7 +1239,7 @@ class WatchedItemStoreUnitTest extends MediaWikiTestCase {
                );
 
                $watchedItem = $store->loadWatchedItem(
-                       $this->getMockNonAnonUserWithId( 1 ),
+                       new UserIdentityValue( 1, 'MockUser', 0 ),
                        new TitleValue( 0, 'SomeDbKey' )
                );
                $this->assertInstanceOf( WatchedItem::class, $watchedItem );
@@ -1300,7 +1276,7 @@ class WatchedItemStoreUnitTest extends MediaWikiTestCase {
 
                $this->assertFalse(
                        $store->loadWatchedItem(
-                               $this->getMockNonAnonUserWithId( 1 ),
+                               new UserIdentityValue( 1, 'MockUser', 0 ),
                                new TitleValue( 0, 'SomeDbKey' )
                        )
                );
@@ -1324,7 +1300,7 @@ class WatchedItemStoreUnitTest extends MediaWikiTestCase {
 
                $this->assertFalse(
                        $store->loadWatchedItem(
-                               $this->getAnonUser(),
+                               new UserIdentityValue( 0, 'AnonUser', 0 ),
                                new TitleValue( 0, 'SomeDbKey' )
                        )
                );
@@ -1372,11 +1348,10 @@ class WatchedItemStoreUnitTest extends MediaWikiTestCase {
                        $this->getMockReadOnlyMode()
                );
 
-               $titleValue = new TitleValue( 0, 'SomeDbKey' );
                $this->assertTrue(
                        $store->removeWatch(
-                               $this->getMockNonAnonUserWithId( 1 ),
-                               Title::newFromTitleValue( $titleValue )
+                               new UserIdentityValue( 1, 'MockUser', 0 ),
+                               new TitleValue( 0, 'SomeDbKey' )
                        )
                );
        }
@@ -1424,11 +1399,10 @@ class WatchedItemStoreUnitTest extends MediaWikiTestCase {
                        $this->getMockReadOnlyMode()
                );
 
-               $titleValue = new TitleValue( 0, 'SomeDbKey' );
                $this->assertFalse(
                        $store->removeWatch(
-                               $this->getMockNonAnonUserWithId( 1 ),
-                               Title::newFromTitleValue( $titleValue )
+                               new UserIdentityValue( 1, 'MockUser', 0 ),
+                               new TitleValue( 0, 'SomeDbKey' )
                        )
                );
        }
@@ -1452,7 +1426,7 @@ class WatchedItemStoreUnitTest extends MediaWikiTestCase {
 
                $this->assertFalse(
                        $store->removeWatch(
-                               $this->getAnonUser(),
+                               new UserIdentityValue( 0, 'AnonUser', 0 ),
                                new TitleValue( 0, 'SomeDbKey' )
                        )
                );
@@ -1497,7 +1471,7 @@ class WatchedItemStoreUnitTest extends MediaWikiTestCase {
                );
 
                $watchedItem = $store->getWatchedItem(
-                       $this->getMockNonAnonUserWithId( 1 ),
+                       new UserIdentityValue( 1, 'MockUser', 0 ),
                        new TitleValue( 0, 'SomeDbKey' )
                );
                $this->assertInstanceOf( WatchedItem::class, $watchedItem );
@@ -1511,7 +1485,7 @@ class WatchedItemStoreUnitTest extends MediaWikiTestCase {
                $mockDb->expects( $this->never() )
                        ->method( 'selectRow' );
 
-               $mockUser = $this->getMockNonAnonUserWithId( 1 );
+               $mockUser = new UserIdentityValue( 1, 'MockUser', 0 );
                $linkTarget = new TitleValue( 0, 'SomeDbKey' );
                $cachedItem = new WatchedItem( $mockUser, $linkTarget, '20151212010101' );
 
@@ -1573,7 +1547,7 @@ class WatchedItemStoreUnitTest extends MediaWikiTestCase {
 
                $this->assertFalse(
                        $store->getWatchedItem(
-                               $this->getMockNonAnonUserWithId( 1 ),
+                               new UserIdentityValue( 1, 'MockUser', 0 ),
                                new TitleValue( 0, 'SomeDbKey' )
                        )
                );
@@ -1598,7 +1572,7 @@ class WatchedItemStoreUnitTest extends MediaWikiTestCase {
 
                $this->assertFalse(
                        $store->getWatchedItem(
-                               $this->getAnonUser(),
+                               new UserIdentityValue( 0, 'AnonUser', 0 ),
                                new TitleValue( 0, 'SomeDbKey' )
                        )
                );
@@ -1637,7 +1611,7 @@ class WatchedItemStoreUnitTest extends MediaWikiTestCase {
                        $mockCache,
                        $this->getMockReadOnlyMode()
                );
-               $user = $this->getMockNonAnonUserWithId( 1 );
+               $user = new UserIdentityValue( 1, 'MockUser', 0 );
 
                $watchedItems = $store->getWatchedItemsForUser( $user );
 
@@ -1670,7 +1644,7 @@ class WatchedItemStoreUnitTest extends MediaWikiTestCase {
                $mockDb = $this->getMockDb();
                $mockCache = $this->getMockCache();
                $mockLoadBalancer = $this->getMockLBFactory( $mockDb, $dbType );
-               $user = $this->getMockNonAnonUserWithId( 1 );
+               $user = new UserIdentityValue( 1, 'MockUser', 0 );
 
                $mockDb->expects( $this->once() )
                        ->method( 'select' )
@@ -1707,7 +1681,7 @@ class WatchedItemStoreUnitTest extends MediaWikiTestCase {
 
                $this->setExpectedException( InvalidArgumentException::class );
                $store->getWatchedItemsForUser(
-                       $this->getMockNonAnonUserWithId( 1 ),
+                       new UserIdentityValue( 1, 'MockUser', 0 ),
                        [ 'sort' => 'foo' ]
                );
        }
@@ -1750,7 +1724,7 @@ class WatchedItemStoreUnitTest extends MediaWikiTestCase {
 
                $this->assertTrue(
                        $store->isWatched(
-                               $this->getMockNonAnonUserWithId( 1 ),
+                               new UserIdentityValue( 1, 'MockUser', 0 ),
                                new TitleValue( 0, 'SomeDbKey' )
                        )
                );
@@ -1788,7 +1762,7 @@ class WatchedItemStoreUnitTest extends MediaWikiTestCase {
 
                $this->assertFalse(
                        $store->isWatched(
-                               $this->getMockNonAnonUserWithId( 1 ),
+                               new UserIdentityValue( 1, 'MockUser', 0 ),
                                new TitleValue( 0, 'SomeDbKey' )
                        )
                );
@@ -1813,7 +1787,7 @@ class WatchedItemStoreUnitTest extends MediaWikiTestCase {
 
                $this->assertFalse(
                        $store->isWatched(
-                               $this->getAnonUser(),
+                               new UserIdentityValue( 0, 'AnonUser', 0 ),
                                new TitleValue( 0, 'SomeDbKey' )
                        )
                );
@@ -1885,7 +1859,8 @@ class WatchedItemStoreUnitTest extends MediaWikiTestCase {
                                0 => [ 'SomeDbKey' => '20151212010101', ],
                                1 => [ 'AnotherDbKey' => null, ],
                        ],
-                       $store->getNotificationTimestampsBatch( $this->getMockNonAnonUserWithId( 1 ), $targets )
+                       $store->getNotificationTimestampsBatch(
+                               new UserIdentityValue( 1, 'MockUser', 0 ), $targets )
                );
        }
 
@@ -1936,7 +1911,8 @@ class WatchedItemStoreUnitTest extends MediaWikiTestCase {
                        [
                                0 => [ 'OtherDbKey' => false, ],
                        ],
-                       $store->getNotificationTimestampsBatch( $this->getMockNonAnonUserWithId( 1 ), $targets )
+                       $store->getNotificationTimestampsBatch(
+                               new UserIdentityValue( 1, 'MockUser', 0 ), $targets )
                );
        }
 
@@ -1946,7 +1922,7 @@ class WatchedItemStoreUnitTest extends MediaWikiTestCase {
                        new TitleValue( 1, 'AnotherDbKey' ),
                ];
 
-               $user = $this->getMockNonAnonUserWithId( 1 );
+               $user = new UserIdentityValue( 1, 'MockUser', 0 );
                $cachedItem = new WatchedItem( $user, $targets[0], '20151212010101' );
 
                $mockDb = $this->getMockDb();
@@ -2010,7 +1986,7 @@ class WatchedItemStoreUnitTest extends MediaWikiTestCase {
                        new TitleValue( 1, 'AnotherDbKey' ),
                ];
 
-               $user = $this->getMockNonAnonUserWithId( 1 );
+               $user = new UserIdentityValue( 1, 'MockUser', 0 );
                $cachedItems = [
                        new WatchedItem( $user, $targets[0], '20151212010101' ),
                        new WatchedItem( $user, $targets[1], null ),
@@ -2070,7 +2046,8 @@ class WatchedItemStoreUnitTest extends MediaWikiTestCase {
                                0 => [ 'SomeDbKey' => false, ],
                                1 => [ 'AnotherDbKey' => false, ],
                        ],
-                       $store->getNotificationTimestampsBatch( $this->getAnonUser(), $targets )
+                       $store->getNotificationTimestampsBatch(
+                               new UserIdentityValue( 0, 'AnonUser', 0 ), $targets )
                );
        }
 
@@ -2093,7 +2070,7 @@ class WatchedItemStoreUnitTest extends MediaWikiTestCase {
 
                $this->assertFalse(
                        $store->resetNotificationTimestamp(
-                               $this->getAnonUser(),
+                               new UserIdentityValue( 0, 'AnonUser', 0 ),
                                Title::newFromText( 'SomeDbKey' )
                        )
                );
@@ -2128,14 +2105,14 @@ class WatchedItemStoreUnitTest extends MediaWikiTestCase {
 
                $this->assertFalse(
                        $store->resetNotificationTimestamp(
-                               $this->getMockNonAnonUserWithId( 1 ),
+                               new UserIdentityValue( 1, 'MockUser', 0 ),
                                Title::newFromText( 'SomeDbKey' )
                        )
                );
        }
 
        public function testResetNotificationTimestamp_item() {
-               $user = $this->getMockNonAnonUserWithId( 1 );
+               $user = new UserIdentityValue( 1, 'MockUser', 0 );
                $title = Title::newFromText( 'SomeDbKey' );
 
                $mockDb = $this->getMockDb();
@@ -2189,7 +2166,7 @@ class WatchedItemStoreUnitTest extends MediaWikiTestCase {
        }
 
        public function testResetNotificationTimestamp_noItemForced() {
-               $user = $this->getMockNonAnonUserWithId( 1 );
+               $user = new UserIdentityValue( 1, 'MockUser', 0 );
                $title = Title::newFromText( 'SomeDbKey' );
 
                $mockDb = $this->getMockDb();
@@ -2265,7 +2242,7 @@ class WatchedItemStoreUnitTest extends MediaWikiTestCase {
        }
 
        public function testResetNotificationTimestamp_oldidSpecifiedLatestRevisionForced() {
-               $user = $this->getMockNonAnonUserWithId( 1 );
+               $user = new UserIdentityValue( 1, 'MockUser', 0 );
                $oldid = 22;
                $title = $this->getMockTitle( 'SomeTitle' );
                $title->expects( $this->once() )
@@ -2318,7 +2295,7 @@ class WatchedItemStoreUnitTest extends MediaWikiTestCase {
        }
 
        public function testResetNotificationTimestamp_oldidSpecifiedNotLatestRevisionForced() {
-               $user = $this->getMockNonAnonUserWithId( 1 );
+               $user = new UserIdentityValue( 1, 'MockUser', 0 );
                $oldid = 22;
                $title = $this->getMockTitle( 'SomeDbKey' );
                $title->expects( $this->once() )
@@ -2397,7 +2374,7 @@ class WatchedItemStoreUnitTest extends MediaWikiTestCase {
        }
 
        public function testResetNotificationTimestamp_notWatchedPageForced() {
-               $user = $this->getMockNonAnonUserWithId( 1 );
+               $user = new UserIdentityValue( 1, 'MockUser', 0 );
                $oldid = 22;
                $title = $this->getMockTitle( 'SomeDbKey' );
                $title->expects( $this->once() )
@@ -2460,7 +2437,7 @@ class WatchedItemStoreUnitTest extends MediaWikiTestCase {
        }
 
        public function testResetNotificationTimestamp_futureNotificationTimestampForced() {
-               $user = $this->getMockNonAnonUserWithId( 1 );
+               $user = new UserIdentityValue( 1, 'MockUser', 0 );
                $oldid = 22;
                $title = $this->getMockTitle( 'SomeDbKey' );
                $title->expects( $this->once() )
@@ -2539,7 +2516,7 @@ class WatchedItemStoreUnitTest extends MediaWikiTestCase {
        }
 
        public function testResetNotificationTimestamp_futureNotificationTimestampNotForced() {
-               $user = $this->getMockNonAnonUserWithId( 1 );
+               $user = new UserIdentityValue( 1, 'MockUser', 0 );
                $oldid = 22;
                $title = $this->getMockTitle( 'SomeDbKey' );
                $title->expects( $this->once() )
@@ -2624,11 +2601,12 @@ class WatchedItemStoreUnitTest extends MediaWikiTestCase {
                        $this->getMockCache(),
                        $this->getMockReadOnlyMode()
                );
-               $this->assertFalse( $store->setNotificationTimestampsForUser( $this->getAnonUser(), '' ) );
+               $this->assertFalse( $store->setNotificationTimestampsForUser(
+                       new UserIdentityValue( 0, 'AnonUser', 0 ), '' ) );
        }
 
        public function testSetNotificationTimestampsForUser_allRows() {
-               $user = $this->getMockNonAnonUserWithId( 1 );
+               $user = new UserIdentityValue( 1, 'MockUser', 0 );
                $timestamp = '20100101010101';
 
                $store = $this->newWatchedItemStore(
@@ -2653,7 +2631,7 @@ class WatchedItemStoreUnitTest extends MediaWikiTestCase {
        }
 
        public function testSetNotificationTimestampsForUser_nullTimestamp() {
-               $user = $this->getMockNonAnonUserWithId( 1 );
+               $user = new UserIdentityValue( 1, 'MockUser', 0 );
                $timestamp = null;
 
                $store = $this->newWatchedItemStore(
@@ -2677,7 +2655,7 @@ class WatchedItemStoreUnitTest extends MediaWikiTestCase {
        }
 
        public function testSetNotificationTimestampsForUser_specificTargets() {
-               $user = $this->getMockNonAnonUserWithId( 1 );
+               $user = new UserIdentityValue( 1, 'MockUser', 0 );
                $timestamp = '20100101010101';
                $targets = [ new TitleValue( 0, 'Foo' ), new TitleValue( 0, 'Bar' ) ];
 
@@ -2753,7 +2731,7 @@ class WatchedItemStoreUnitTest extends MediaWikiTestCase {
                $this->assertEquals(
                        [ 2, 3 ],
                        $store->updateNotificationTimestamp(
-                               $this->getMockNonAnonUserWithId( 1 ),
+                               new UserIdentityValue( 1, 'MockUser', 0 ),
                                new TitleValue( 0, 'SomeDbKey' ),
                                '20151212010101'
                        )
@@ -2793,7 +2771,7 @@ class WatchedItemStoreUnitTest extends MediaWikiTestCase {
                );
 
                $watchers = $store->updateNotificationTimestamp(
-                       $this->getMockNonAnonUserWithId( 1 ),
+                       new UserIdentityValue( 1, 'MockUser', 0 ),
                        new TitleValue( 0, 'SomeDbKey' ),
                        '20151212010101'
                );
@@ -2802,7 +2780,7 @@ class WatchedItemStoreUnitTest extends MediaWikiTestCase {
        }
 
        public function testUpdateNotificationTimestamp_clearsCachedItems() {
-               $user = $this->getMockNonAnonUserWithId( 1 );
+               $user = new UserIdentityValue( 1, 'MockUser', 0 );
                $titleValue = new TitleValue( 0, 'SomeDbKey' );
 
                $mockDb = $this->getMockDb();
@@ -2841,7 +2819,7 @@ class WatchedItemStoreUnitTest extends MediaWikiTestCase {
                $store->getWatchedItem( $user, $titleValue );
 
                $store->updateNotificationTimestamp(
-                       $this->getMockNonAnonUserWithId( 1 ),
+                       new UserIdentityValue( 1, 'MockUser', 0 ),
                        $titleValue,
                        '20151212010101'
                );