wiki.techinc.nl/tests/phpunit/includes/deferred/RefreshSecondaryDataUpdateTest.php
James D. Forrester ad06527fb4 Reorg: Namespace the Title class
This is moderately messy.

Process was principally:

* xargs rg --files-with-matches '^use Title;' | grep 'php$' | \
  xargs -P 1 -n 1 sed -i -z 's/use Title;/use MediaWiki\\Title\\Title;/1'
* rg --files-without-match 'MediaWiki\\Title\\Title;' . | grep 'php$' | \
  xargs rg --files-with-matches 'Title\b' | \
  xargs -P 1 -n 1 sed -i -z 's/\nuse /\nuse MediaWiki\\Title\\Title;\nuse /1'
* composer fix

Then manual fix-ups for a few files that don't have any use statements.

Bug: T166010
Follows-Up: Ia5d8cb759dc3bc9e9bbe217d0fb109e2f8c4101a
Change-Id: If8fc9d0d95fc1a114021e282a706fc3e7da3524b
2023-03-02 08:46:53 -05:00

222 lines
7.4 KiB
PHP

<?php
use MediaWiki\Revision\MutableRevisionRecord;
use MediaWiki\Storage\DerivedPageDataUpdater;
use MediaWiki\Title\Title;
use Psr\Log\NullLogger;
use Wikimedia\ScopedCallback;
/**
* @covers RefreshSecondaryDataUpdate
*/
class RefreshSecondaryDataUpdateTest extends MediaWikiIntegrationTestCase {
public function testSuccess() {
$services = $this->getServiceContainer();
$lbFactory = $services->getDBLoadBalancerFactory();
$queue = $services->getJobQueueGroup()->get( 'refreshLinksPrioritized' );
$user = $this->getTestUser()->getUser();
$goodCalls = 0;
$goodUpdate = $this->getMockBuilder( DataUpdate::class )
->onlyMethods( [ 'doUpdate', 'setTransactionTicket' ] )
->getMock();
$goodTrxFname = get_class( $goodUpdate ) . '::doUpdate';
$goodUpdate->method( 'doUpdate' )
->willReturnCallback( static function () use ( &$goodCalls, $lbFactory, $goodTrxFname ) {
// Update can commit since it owns the transaction
$lbFactory->commitPrimaryChanges( $goodTrxFname );
++$goodCalls;
} );
$goodUpdate->expects( $this->once() )
->method( 'setTransactionTicket' );
$updater = $this->getMockBuilder( DerivedPageDataUpdater::class )
->disableOriginalConstructor()
->onlyMethods( [ 'getSecondaryDataUpdates' ] )
->getMock();
$updater->method( 'getSecondaryDataUpdates' )
->willReturn( [ $goodUpdate ] );
$revision = $this->getMockBuilder( MutableRevisionRecord::class )
->disableOriginalConstructor()
->onlyMethods( [ 'getId' ] )
->getMock();
$revision->method( 'getId' )
->willReturn( 42 );
$dbw = wfGetDB( DB_PRIMARY );
$dbw->startAtomic( __METHOD__ );
$this->assertSame( 0, $queue->getSize() );
$this->assertSame( 0, DeferredUpdates::pendingUpdatesCount() );
$wikiPage = $services->getWikiPageFactory()->newFromTitle( Title::makeTitle( NS_MAIN, 'TestPage' ) );
DeferredUpdates::addUpdate( new RefreshSecondaryDataUpdate(
$lbFactory,
$user,
$wikiPage,
$revision,
$updater,
[]
) );
$this->assertSame( 1, DeferredUpdates::pendingUpdatesCount() );
$this->assertSame( 0, $queue->getSize() );
$dbw->endAtomic( __METHOD__ ); // run updates
$this->assertSame( 0, DeferredUpdates::pendingUpdatesCount() );
$queue->flushCaches();
$this->assertSame( 0, $queue->getSize(), "Nothing failed; no enqueue" );
$this->assertSame( 1, $goodCalls );
}
public function testEnqueueOnFailure() {
$services = $this->getServiceContainer();
$lbFactory = $services->getDBLoadBalancerFactory();
$queue = $services->getJobQueueGroup()->get( 'refreshLinksPrioritized' );
$user = $this->getTestUser()->getUser();
// T248189: DeferredUpdate will log the exception, don't fail because of that.
$this->setLogger( 'exception', new NullLogger() );
$goodCalls = 0;
$goodUpdate = $this->getMockBuilder( DataUpdate::class )
->onlyMethods( [ 'doUpdate', 'setTransactionTicket' ] )
->getMock();
$goodTrxFname = get_class( $goodUpdate ) . '::doUpdate';
$goodUpdate->method( 'doUpdate' )
->willReturnCallback( static function () use ( &$goodCalls, $lbFactory, $goodTrxFname ) {
// Update can commit since it owns the transaction
$lbFactory->commitPrimaryChanges( $goodTrxFname );
++$goodCalls;
} );
$goodUpdate->expects( $this->once() )
->method( 'setTransactionTicket' );
$badCalls = 0;
$badUpdate = $this->getMockBuilder( DataUpdate::class )
->onlyMethods( [ 'doUpdate', 'setTransactionTicket' ] )
->getMock();
$badTrxFname = get_class( $goodUpdate ) . '::doUpdate';
$badUpdate->expects( $this->once() )
->method( 'setTransactionTicket' );
$badUpdate->method( 'doUpdate' )
->willReturnCallback( static function () use ( &$badCalls, $lbFactory, $badTrxFname ) {
// Update can commit since it owns the transaction
$lbFactory->commitPrimaryChanges( $badTrxFname );
++$badCalls;
throw new LogicException( 'We have a problem' );
} );
$updater = $this->getMockBuilder( DerivedPageDataUpdater::class )
->disableOriginalConstructor()
->onlyMethods( [ 'getSecondaryDataUpdates' ] )
->getMock();
$updater->method( 'getSecondaryDataUpdates' )
->willReturn( [ $goodUpdate, $badUpdate ] );
$revision = $this->getMockBuilder( MutableRevisionRecord::class )
->disableOriginalConstructor()
->onlyMethods( [ 'getId' ] )
->getMock();
$revision->method( 'getId' )
->willReturn( 42 );
$dbw = wfGetDB( DB_PRIMARY );
$dbw->startAtomic( __METHOD__ );
$goodCalls = 0;
$this->assertSame( 0, $queue->getSize() );
$this->assertSame( 0, DeferredUpdates::pendingUpdatesCount() );
$wikiPage = $services->getWikiPageFactory()->newFromTitle( Title::makeTitle( NS_MAIN, 'TestPage' ) );
DeferredUpdates::addUpdate( new RefreshSecondaryDataUpdate(
$lbFactory,
$user,
$wikiPage,
$revision,
$updater,
[]
) );
$this->assertSame( 1, DeferredUpdates::pendingUpdatesCount() );
$this->assertSame( 0, $queue->getSize() );
try {
// Trigger deferred updates run to execute the update and secondary updates
$dbw->endAtomic( __METHOD__ );
// Callback rigged to fail
$this->fail( "Expected LogicException" );
} catch ( LogicException $e ) {
$this->assertSame( "We have a problem", $e->getMessage() );
}
$this->assertSame( 0, DeferredUpdates::pendingUpdatesCount() );
$queue->flushCaches();
$this->assertSame( 1, $queue->getSize(), "Update failed; job enqueued" );
$this->assertSame( 1, $goodCalls );
$this->assertSame( 1, $badCalls );
// Run the RefreshLinksJob
$this->runJobs( [ 'ignoreErrorsMatchingFormat' => 'Revision %d is not current' ] );
$queue->flushCaches();
$this->assertSame( 0, $queue->getSize() );
}
/**
* Attempted use of onTransactionResolution() to avoid an update running on
* rollback shouldn't cause DeferredUpdates to fail to get a ticket.
*/
public function testT248003() {
$services = $this->getServiceContainer();
$lbFactory = $services->getDBLoadBalancerFactory();
$user = $this->getTestUser()->getUser();
$fname = __METHOD__;
$dbw = $lbFactory->getMainLB()->getConnectionRef( DB_PRIMARY );
$dbw->setFlag( DBO_TRX, $dbw::REMEMBER_PRIOR ); // make queries trigger TRX
$reset = new ScopedCallback( [ $dbw, 'restoreFlags' ] );
$this->assertSame( 0, $dbw->trxLevel() );
$dbw->selectRow( 'page', '*', '', __METHOD__ );
if ( !$dbw->trxLevel() ) {
$this->markTestSkipped( 'No implicit transaction, cannot test for T248003' );
}
$dbw->commit( __METHOD__, $dbw::FLUSHING_INTERNAL );
$this->assertSame( 0, $dbw->trxLevel() );
$goodCalls = 0;
$goodUpdate = $this->getMockBuilder( DataUpdate::class )
->onlyMethods( [ 'doUpdate' ] )
->getMock();
$goodUpdate->method( 'doUpdate' )
->willReturnCallback( static function () use ( &$goodCalls ) {
++$goodCalls;
} );
$updater = $this->getMockBuilder( DerivedPageDataUpdater::class )
->disableOriginalConstructor()
->onlyMethods( [ 'getSecondaryDataUpdates' ] )
->getMock();
$updater->method( 'getSecondaryDataUpdates' )
->willReturnCallback( static function () use ( $dbw, $fname, $goodUpdate ) {
$dbw->selectRow( 'page', '*', '', $fname );
$dbw->onTransactionResolution( static function () {
}, $fname );
return [ $goodUpdate ];
} );
$wikiPage = $services->getWikiPageFactory()->newFromTitle( Title::makeTitle( NS_MAIN, 'UTPage' ) );
$update = new RefreshSecondaryDataUpdate(
$lbFactory,
$user,
$wikiPage,
$wikiPage->getRevisionRecord(),
$updater,
[]
);
$update->doUpdate();
$this->assertSame( 1, $goodCalls );
}
}