aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMáté Szabó <mszabo@wikimedia.org>2025-04-04 11:47:44 +0200
committerMáté Szabó <mszabo@wikimedia.org>2025-04-04 15:46:44 +0200
commit394a18083405108b9ad2d48bd4cb5225333bd19a (patch)
tree49a3d89e60eb21ab55916536e744b1b451dd96f0
parent95485727e1d333be0995ff8b48b5f6e5593918b2 (diff)
downloadmediawikicore-394a18083405108b9ad2d48bd4cb5225333bd19a.tar.gz
mediawikicore-394a18083405108b9ad2d48bd4cb5225333bd19a.zip
storage: Assert CategoryMembershipChangeJob via trait
Why: - As suggested on I14944639e00407f59380c8787fd2810c7d24a5c9, ChangeTrackingUpdateSpyTrait is a better place to assert on operations performed by ChangeTrackingEventIngress than DerivedPageDataUpdaterTest. What: - Add an expected number of enqueued CategoryMembershipChangeJobs to ChangeTrackingUpdateSpyTrait. - Update tests using the trait to specify the expected number of jobs. - Clear hooks in relevant tests to avoid test failures when extensions are loaded that attempt to call methods on the mocks set by ChangeTrackingUpdateSpyTrait. These tests currently fail if e.g. PageTriage, Echo or EventBus are loaded. Bug: T390636 Change-Id: I6fe6cb87cd5a16b1ed39e0998c49713704633d71
-rw-r--r--tests/phpunit/includes/Storage/DerivedPageDataUpdaterTest.php25
-rw-r--r--tests/phpunit/includes/Storage/PageUpdaterTest.php21
-rw-r--r--tests/phpunit/includes/api/ApiImportTest.php9
-rw-r--r--tests/phpunit/includes/filerepo/file/LocalFileTest.php8
-rw-r--r--tests/phpunit/includes/import/ImportableOldRevisionImporterTest.php2
-rw-r--r--tests/phpunit/includes/page/MovePageTest.php10
-rw-r--r--tests/phpunit/includes/page/UndeletePageTest.php11
-rw-r--r--tests/phpunit/includes/page/WikiPageDbTest.php9
-rw-r--r--tests/phpunit/includes/recentchanges/ChangeTrackingUpdateSpyTrait.php28
-rw-r--r--tests/phpunit/integration/includes/page/DeletePageTest.php8
-rw-r--r--tests/phpunit/integration/includes/page/RollbackPageTest.php11
11 files changed, 108 insertions, 34 deletions
diff --git a/tests/phpunit/includes/Storage/DerivedPageDataUpdaterTest.php b/tests/phpunit/includes/Storage/DerivedPageDataUpdaterTest.php
index 6eb49eeeb117..91614f56649d 100644
--- a/tests/phpunit/includes/Storage/DerivedPageDataUpdaterTest.php
+++ b/tests/phpunit/includes/Storage/DerivedPageDataUpdaterTest.php
@@ -1129,16 +1129,11 @@ class DerivedPageDataUpdaterTest extends MediaWikiIntegrationTestCase {
* @covers \MediaWiki\Storage\DerivedPageDataUpdater::doUpdates()
* @covers \MediaWiki\Storage\DerivedPageDataUpdater::doSecondaryDataUpdates()
* @covers \MediaWiki\Storage\DerivedPageDataUpdater::doParserCacheUpdate()
- * @covers \MediaWiki\RecentChanges\ChangeTrackingEventIngress::handlePageRevisionUpdatedEvent()
- * @covers \MediaWiki\RecentChanges\ChangeTrackingEventIngress::anyChangedSlotSupportsCategories()
*/
public function testDoUpdates(
bool $simulateNullEdit,
- bool $simulatePageCreation,
- bool $rcWatchCategoryMembership
+ bool $simulatePageCreation
) {
- $this->overrideConfigValue( MainConfigNames::RCWatchCategoryMembership, $rcWatchCategoryMembership );
-
$page = $this->getPage( __METHOD__ );
$content = [ SlotRecord::MAIN => new WikitextContent( 'current [[main]]' ) ];
@@ -1257,15 +1252,6 @@ class DerivedPageDataUpdaterTest extends MediaWikiIntegrationTestCase {
$this->runDeferredUpdates();
$this->assertSame( 1, $listenerCalled, 'PageRevisionUpdatedEvent listener' );
- $this->assertSame(
- $rcWatchCategoryMembership && !$simulateNullEdit ? 1 : 0,
- $this->getServiceContainer()
- ->getJobQueueGroup()
- ->get( 'categoryMembershipChange' )
- ->getSize(),
- 'CategoryMembershipChangeJob should only be enqueued for non-null edits (T390636)'
- );
-
// TODO: MCR: test data updates for additional slots!
// TODO: test update for edit without page creation
// TODO: test message cache purge
@@ -1281,13 +1267,11 @@ class DerivedPageDataUpdaterTest extends MediaWikiIntegrationTestCase {
// null or non-null edit
[ true, false ],
// page creation
- [ true, false ],
- // category membership notifications enabled or disabled
[ true, false ]
);
foreach ( $testCases as $params ) {
- [ $simulateNullEdit, $simulatePageCreation, $rcWatchCategoryMembership ] = $params;
+ [ $simulateNullEdit, $simulatePageCreation ] = $params;
if ( $simulateNullEdit && $simulatePageCreation ) {
// Page creations cannot be null edits, so don't simulate an impossible scenario
@@ -1295,10 +1279,9 @@ class DerivedPageDataUpdaterTest extends MediaWikiIntegrationTestCase {
}
$description = sprintf(
- '%s edit, %s category membership notifications %s',
+ '%s edit%s',
$simulateNullEdit ? 'null' : 'non-null',
- $simulatePageCreation ? 'page creation, ' : '',
- $rcWatchCategoryMembership ? 'enabled' : 'disabled'
+ $simulatePageCreation ? ', page creation, ' : ''
);
yield $description => $params;
diff --git a/tests/phpunit/includes/Storage/PageUpdaterTest.php b/tests/phpunit/includes/Storage/PageUpdaterTest.php
index d0af2f7d9d83..904ac1069139 100644
--- a/tests/phpunit/includes/Storage/PageUpdaterTest.php
+++ b/tests/phpunit/includes/Storage/PageUpdaterTest.php
@@ -10,6 +10,7 @@ use MediaWiki\Content\TextContent;
use MediaWiki\Content\WikitextContent;
use MediaWiki\Deferred\DeferredUpdates;
use MediaWiki\Json\FormatJson;
+use MediaWiki\MainConfigNames;
use MediaWiki\Message\Message;
use MediaWiki\Page\Event\PageRevisionUpdatedEvent;
use MediaWiki\Page\PageIdentity;
@@ -49,6 +50,10 @@ class PageUpdaterTest extends MediaWikiIntegrationTestCase {
protected function setUp(): void {
parent::setUp();
+ // Force enable RC entry creation for category changes
+ // so that tests can verify whether CategoryMembershipChangeJobs get enqueued.
+ $this->overrideConfigValue( MainConfigNames::RCWatchCategoryMembership, true );
+
$slotRoleRegistry = $this->getServiceContainer()->getSlotRoleRegistry();
if ( !$slotRoleRegistry->isDefinedRole( 'aux' ) ) {
@@ -69,6 +74,13 @@ class PageUpdaterTest extends MediaWikiIntegrationTestCase {
// protect against service container resets
$this->setService( 'SlotRoleRegistry', $slotRoleRegistry );
+
+ // Clear some extension hook handlers that may interfere with mock object expectations.
+ $this->clearHooks( [
+ 'RevisionRecordInserted',
+ 'PageSaveComplete',
+ 'LinksUpdateComplete',
+ ] );
}
private function getDummyTitle( $method ) {
@@ -707,7 +719,8 @@ class PageUpdaterTest extends MediaWikiIntegrationTestCase {
$this->expectChangeTrackingUpdates(
1, 0, 1,
- $page->getNamespace() === NS_USER_TALK ? 1 : 0
+ $page->getNamespace() === NS_USER_TALK ? 1 : 0,
+ 1
);
$this->expectSearchUpdates( 1 );
@@ -751,7 +764,7 @@ class PageUpdaterTest extends MediaWikiIntegrationTestCase {
// Null edits should not go into recentchanges, should not
// increment counters, and should not trigger talk page notifications.
- $this->expectChangeTrackingUpdates( 0, 0, 0, 0 );
+ $this->expectChangeTrackingUpdates( 0, 0, 0, 0, 0 );
// Update derived data on null edits
$this->expectSearchUpdates( 1 );
@@ -796,7 +809,7 @@ class PageUpdaterTest extends MediaWikiIntegrationTestCase {
// Silent dummy revisions should not go into recentchanges,
// should not increment counters, and should not trigger talk page
// notifications.
- $this->expectChangeTrackingUpdates( 0, 0, 0, 0 );
+ $this->expectChangeTrackingUpdates( 0, 0, 0, 0, 0 );
// Do not update derived data on dummy revisions!
$this->expectSearchUpdates( 0 );
@@ -1262,7 +1275,7 @@ class PageUpdaterTest extends MediaWikiIntegrationTestCase {
// Clear pending jobs so the spies don't get confused
$this->runJobs();
- $this->expectChangeTrackingUpdates( 0, 0, 0, 0 );
+ $this->expectChangeTrackingUpdates( 0, 0, 0, 0, 0 );
$this->expectSearchUpdates( 0 );
$updater = $page->newPageUpdater( $user );
diff --git a/tests/phpunit/includes/api/ApiImportTest.php b/tests/phpunit/includes/api/ApiImportTest.php
index a3a3203f26d6..4c6c26e26a1a 100644
--- a/tests/phpunit/includes/api/ApiImportTest.php
+++ b/tests/phpunit/includes/api/ApiImportTest.php
@@ -30,6 +30,13 @@ class ApiImportTest extends ApiUploadTestCase {
}
public function testImport() {
+ // Clear some extension hook handlers that may interfere with mock object expectations.
+ $this->clearHooks( [
+ 'RevisionRecordInserted',
+ 'PageSaveComplete',
+ 'LinksUpdateComplete',
+ ] );
+
$title = $this->getNonexistingTestPage()->getTitle();
// We expect two PageRevisionUpdated events, one triggered by
@@ -89,7 +96,7 @@ class ApiImportTest extends ApiUploadTestCase {
// Expect only non-edit recent changes entry, but no edit count
// or user talk.
- $this->expectChangeTrackingUpdates( 0, 1, 0, 0 );
+ $this->expectChangeTrackingUpdates( 0, 1, 0, 0, 1 );
// Expect search updates to be triggered
$this->expectSearchUpdates( 1 );
diff --git a/tests/phpunit/includes/filerepo/file/LocalFileTest.php b/tests/phpunit/includes/filerepo/file/LocalFileTest.php
index 5a9b0a9ae327..8bba9ec1dfd2 100644
--- a/tests/phpunit/includes/filerepo/file/LocalFileTest.php
+++ b/tests/phpunit/includes/filerepo/file/LocalFileTest.php
@@ -896,8 +896,14 @@ class LocalFileTest extends MediaWikiIntegrationTestCase {
* @covers \LocalFile
*/
public function testUpload_updatePropagation() {
+ // Clear some extension hook handlers that may interfere with mock object expectations.
+ $this->clearHooks( [
+ 'PageSaveComplete',
+ 'RevisionRecordInserted',
+ ] );
+
// Expect two non-edit recent changes entries but only one edit count.
- $this->expectChangeTrackingUpdates( 0, 2, 1, 0 );
+ $this->expectChangeTrackingUpdates( 0, 2, 1, 0, 1 );
// Expect only one search update, the re-upload doesn't change the page.
$this->expectSearchUpdates( 1 );
diff --git a/tests/phpunit/includes/import/ImportableOldRevisionImporterTest.php b/tests/phpunit/includes/import/ImportableOldRevisionImporterTest.php
index f974eec26f7e..ed064e19ed99 100644
--- a/tests/phpunit/includes/import/ImportableOldRevisionImporterTest.php
+++ b/tests/phpunit/includes/import/ImportableOldRevisionImporterTest.php
@@ -190,7 +190,7 @@ class ImportableOldRevisionImporterTest extends MediaWikiIntegrationTestCase {
public function testUpdatePropagation( PageIdentity $title ) {
$revision = $this->getWikiRevision( Title::castFromPageIdentity( $title ) );
- $this->expectChangeTrackingUpdates( 0, 0, 0, 0 );
+ $this->expectChangeTrackingUpdates( 0, 0, 0, 0, 1 );
$this->expectSearchUpdates( 1 );
$this->expectLocalizationUpdate( $title->getNamespace() === NS_MEDIAWIKI ? 1 : 0 );
diff --git a/tests/phpunit/includes/page/MovePageTest.php b/tests/phpunit/includes/page/MovePageTest.php
index be3043578b90..07aaf63a09f9 100644
--- a/tests/phpunit/includes/page/MovePageTest.php
+++ b/tests/phpunit/includes/page/MovePageTest.php
@@ -741,6 +741,14 @@ class MovePageTest extends MediaWikiIntegrationTestCase {
* @dataProvider provideUpdatePropagation
*/
public function testUpdatePropagation( $old, $new, ?Content $content = null ) {
+ // Clear some extension hook handlers that may interfere with mock object expectations.
+ $this->clearHooks( [
+ 'RevisionRecordInserted',
+ 'PageSaveComplete',
+ 'PageMoveComplete',
+ 'LinksUpdateComplete',
+ ] );
+
$old = Title::newFromText( $old );
$new = Title::newFromText( $new );
@@ -754,7 +762,7 @@ class MovePageTest extends MediaWikiIntegrationTestCase {
// Should be counted as user contributions (T163966)
// Should generate an RC entry for the move log, but not for
// the dummy revision or redirect page.
- $this->expectChangeTrackingUpdates( 0, 1, 1, 0 );
+ $this->expectChangeTrackingUpdates( 0, 1, 1, 0, 1 );
// The moved page and the redirect should both get re-indexed.
$this->expectSearchUpdates( 2 );
diff --git a/tests/phpunit/includes/page/UndeletePageTest.php b/tests/phpunit/includes/page/UndeletePageTest.php
index a33201382924..7a471a748db5 100644
--- a/tests/phpunit/includes/page/UndeletePageTest.php
+++ b/tests/phpunit/includes/page/UndeletePageTest.php
@@ -76,6 +76,9 @@ class UndeletePageTest extends MediaWikiIntegrationTestCase {
$this->fail( $updater->getStatus()->getWikiText() );
}
+ // Run jobs that were enqueued by page creation now, since they might expect the page to exist.
+ $this->runJobs( [ 'minJobs' => 0 ] );
+
$this->pages[] = [ 'page' => $page, 'revId' => $revisionRecord->getId() ];
$this->deletePage( $page, '', $performer );
}
@@ -223,6 +226,12 @@ class UndeletePageTest extends MediaWikiIntegrationTestCase {
* @covers \MediaWiki\Page\UndeletePage::undeleteUnsafe
*/
public function testUpdatePropagation( ProperPageIdentity $page, ?Content $content = null ) {
+ // Clear some extension hook handlers that may interfere with mock object expectations.
+ $this->clearHooks( [
+ 'LinksUpdateComplete',
+ 'PageUndeleteComplete',
+ ] );
+
$content ??= new WikitextContent( 'hi' );
$this->setupPage( $page->getDBkey(), $page->getNamespace(), $content );
$this->runJobs();
@@ -231,7 +240,7 @@ class UndeletePageTest extends MediaWikiIntegrationTestCase {
// Should generate an RC entry for undeletion,
// but not a regular page edit.
- $this->expectChangeTrackingUpdates( 0, 1, 0, 0 );
+ $this->expectChangeTrackingUpdates( 0, 1, 0, 0, 0 );
$this->expectSearchUpdates( 1 );
$this->expectLocalizationUpdate( $page->getNamespace() === NS_MEDIAWIKI ? 1 : 0 );
diff --git a/tests/phpunit/includes/page/WikiPageDbTest.php b/tests/phpunit/includes/page/WikiPageDbTest.php
index 3efff6e309d0..d4e30e48fac2 100644
--- a/tests/phpunit/includes/page/WikiPageDbTest.php
+++ b/tests/phpunit/includes/page/WikiPageDbTest.php
@@ -1777,6 +1777,13 @@ more stuff
PageIdentity $title,
?Content $content = null
) {
+ // Clear some extension hook handlers that may interfere with mock object expectations.
+ $this->clearHooks( [
+ 'PageSaveComplete',
+ 'RevisionRecordInserted',
+ 'ArticleProtectComplete',
+ ] );
+
$content ??= new TextContent( 'Lorem Ipsum' );
$page = $this->createPage( $title, $content );
@@ -1784,7 +1791,7 @@ more stuff
$this->runJobs();
// Expect only non-edit recent changes entry
- $this->expectChangeTrackingUpdates( 0, 1, 0, 0 );
+ $this->expectChangeTrackingUpdates( 0, 1, 0, 0, 0 );
// Expect no resource module purges on protection
$this->expectResourceLoaderUpdates( 0 );
diff --git a/tests/phpunit/includes/recentchanges/ChangeTrackingUpdateSpyTrait.php b/tests/phpunit/includes/recentchanges/ChangeTrackingUpdateSpyTrait.php
index d7df06882f7a..dd31a56f75d5 100644
--- a/tests/phpunit/includes/recentchanges/ChangeTrackingUpdateSpyTrait.php
+++ b/tests/phpunit/includes/recentchanges/ChangeTrackingUpdateSpyTrait.php
@@ -2,6 +2,8 @@
namespace MediaWiki\Tests\recentchanges;
+use MediaWiki\JobQueue\JobQueueGroup;
+use MediaWiki\MainConfigNames;
use MediaWiki\RecentChanges\RecentChange;
use MediaWiki\User\TalkPageNotificationManager;
use MediaWiki\User\UserEditTracker;
@@ -25,8 +27,13 @@ trait ChangeTrackingUpdateSpyTrait {
int $rcEdit,
int $rcOther,
int $userEditCount,
- int $talkPageNotifications
+ int $talkPageNotifications,
+ int $categoryMembershipChangeJobs
) {
+ // Force enable RC entry creation for category changes
+ // to verify CategoryMembershipChangeJobs get enqueued irrespective of local configuration.
+ $this->overrideConfigValue( MainConfigNames::RCWatchCategoryMembership, true );
+
// Hack for spying on RC insertions
$rcEditStatus = $this->createMock( StatusValue::class );
$rcEditStatus->expects( $this->exactly( $rcEdit ) )
@@ -75,6 +82,25 @@ trait ChangeTrackingUpdateSpyTrait {
->method( 'removeUserHasNewMessages' );
$this->setService( 'TalkPageNotificationManager', $talkPageNotificationManager );
+
+ $categoryMembershipChangeJobStatus = $this->createMock( StatusValue::class );
+ $categoryMembershipChangeJobStatus->expects( $this->exactly( $categoryMembershipChangeJobs ) )
+ ->method( 'setOK' );
+
+ $jobQueueGroup = $this->createMock( JobQueueGroup::class );
+ $jobQueueGroup->method( $this->logicalOr( 'push', 'lazyPush' ) )
+ ->willReturnCallback(
+ static function ( $specs ) use ( $categoryMembershipChangeJobStatus ): void {
+ $specs = is_array( $specs ) ? $specs : [ $specs ];
+ foreach ( $specs as $spec ) {
+ if ( $spec->getType() === 'categoryMembershipChange' ) {
+ $categoryMembershipChangeJobStatus->setOK( true );
+ }
+ }
+ }
+ );
+
+ $this->setService( 'JobQueueGroup', $jobQueueGroup );
}
}
diff --git a/tests/phpunit/integration/includes/page/DeletePageTest.php b/tests/phpunit/integration/includes/page/DeletePageTest.php
index 78e35dcaead9..c6f7f46ec397 100644
--- a/tests/phpunit/integration/includes/page/DeletePageTest.php
+++ b/tests/phpunit/integration/includes/page/DeletePageTest.php
@@ -478,6 +478,11 @@ class DeletePageTest extends MediaWikiIntegrationTestCase {
* @covers \MediaWiki\Page\UndeletePage::undeleteUnsafe
*/
public function testUpdatePropagation( $name, ?Content $content = null ) {
+ // Clear some extension hook handlers that may interfere with mock object expectations.
+ $this->clearHooks( [
+ 'PageDeleteComplete',
+ ] );
+
$content ??= new WikitextContent( self::PAGE_TEXT );
$deleterUser = static::getTestSysop()->getUser();
$deleter = new UltimateAuthority( $deleterUser );
@@ -489,7 +494,8 @@ class DeletePageTest extends MediaWikiIntegrationTestCase {
// but not a regular page edit.
$this->expectChangeTrackingUpdates(
0, 1, 0,
- $page->getNamespace() === NS_USER_TALK ? -1 : 0
+ $page->getNamespace() === NS_USER_TALK ? -1 : 0,
+ 0
);
// TODO: Assert that the search index is updated after deletion.
diff --git a/tests/phpunit/integration/includes/page/RollbackPageTest.php b/tests/phpunit/integration/includes/page/RollbackPageTest.php
index 7ef37b73a795..973febea3eb3 100644
--- a/tests/phpunit/integration/includes/page/RollbackPageTest.php
+++ b/tests/phpunit/integration/includes/page/RollbackPageTest.php
@@ -588,6 +588,12 @@ class RollbackPageTest extends MediaWikiIntegrationTestCase {
?Content $content1 = null,
?Content $content2 = null
) {
+ // Clear some extension hook handlers that may interfere with mock object expectations.
+ $this->clearHooks( [
+ 'PageSaveComplete',
+ 'RevisionRecordInserted',
+ ] );
+
$page = $this->getServiceContainer()->getWikiPageFactory()
->newFromTitle( Title::newFromText( __METHOD__ ) );
@@ -602,7 +608,8 @@ class RollbackPageTest extends MediaWikiIntegrationTestCase {
// Should generate an RC entry for rollback
$this->expectChangeTrackingUpdates(
1, 0, 1,
- $page->getNamespace() === NS_USER_TALK ? 1 : 0
+ $page->getNamespace() === NS_USER_TALK ? 1 : 0,
+ 1
);
$this->expectSearchUpdates( 1 );
@@ -617,5 +624,7 @@ class RollbackPageTest extends MediaWikiIntegrationTestCase {
->newRollbackPage( $page, $admin, $user1 )
->rollbackIfAllowed();
$this->assertStatusGood( $rollbackResult );
+
+ $this->runDeferredUpdates();
}
}