wiki.techinc.nl/tests/phpunit/unit/includes/Storage/EditResultBuilderTest.php
Ostrzyciel 3d73bdf568 PageUpdater: create EditResult class
EditResult is an immutable object created during a page update by
the PageUpdater using a builder object, the EditResultBuilder.

EditResult is a container for information on how the edit
interacted with the page and previous revisions. It also aims to
provide a standardized way of describing reverts and reverted
edits. It is as simple as possible, with no dependencies on the DB
or global variables. Most of the logic is encapsulated in the
builder.

PageUpdater::getEditResult() replaces the following methods:
* getOriginalRevisionId()
* getUndidRevisionId()
They are both available in the EditResult object.

PageUpdater::markAsRevert() replaces
PageUpdater::setUndidRevisionId()

Bug: T254074
Change-Id: Ie04c38043d9c295552e488109436ec1df20bb2ca
2020-06-23 11:49:02 +02:00

347 lines
12 KiB
PHP

<?php
namespace MediaWiki\Tests\Storage;
use MediaWiki\Revision\MutableRevisionRecord;
use MediaWiki\Revision\RevisionRecord;
use MediaWiki\Revision\RevisionStore;
use MediaWiki\Storage\EditResult;
use MediaWiki\Storage\EditResultBuilder;
use MediaWiki\Storage\PageUpdateException;
use MediaWikiUnitTestCase;
use Title;
/**
* @covers \MediaWiki\Storage\EditResultBuilder
* @covers \MediaWiki\Storage\EditResult
*/
class EditResultBuilderTest extends MediaWikiUnitTestCase {
/**
* @covers \MediaWiki\Storage\EditResultBuilder::buildEditResult
*/
public function testBuilderThrowsExceptionOnMissingRevision() {
$erb = $this->getNewEditResultBuilder();
$this->expectException( PageUpdateException::class );
$erb->buildEditResult();
}
/**
* @covers \MediaWiki\Storage\EditResultBuilder
*/
public function testIsNewUnset() {
$erb = $this->getNewEditResultBuilder();
$erb->setRevisionRecord( $this->getDummyRevision() );
$er = $erb->buildEditResult();
$this->assertFalse( $er->isNew(), 'EditResult::isNew()' );
}
public function provideSetIsNew() {
return [
'not a new page' => [ false ],
'a new page' => [ true ]
];
}
/**
* @dataProvider provideSetIsNew
* @covers \MediaWiki\Storage\EditResultBuilder
* @param bool $isNew
*/
public function testSetIsNew( bool $isNew ) {
$erb = $this->getNewEditResultBuilder();
$erb->setIsNew( $isNew );
$erb->setRevisionRecord( $this->getDummyRevision() );
$er = $erb->buildEditResult();
$this->assertSame( $isNew, $er->isNew(), 'EditResult::isNew()' );
}
/**
* Tests a normal edit to the page
* @covers \MediaWiki\Storage\EditResult
* @covers \MediaWiki\Storage\EditResultBuilder
*/
public function testEditNotARevert() {
$erb = $this->getNewEditResultBuilder();
$erb->setRevisionRecord( $this->getDummyRevision() );
$er = $erb->buildEditResult();
$this->assertFalse( $er->isNew(), 'EditResult::isNew()' );
$this->assertFalse( $er->isNullEdit(), 'EditResult::isNullEdit()' );
$this->assertFalse( $er->getOriginalRevisionId(),
'EditResult::getOriginalRevisionId()' );
$this->assertFalse( $er->isRevert(), 'EditResult::isRevert()' );
$this->assertFalse( $er->isExactRevert(), 'EditResult::isExactRevert()' );
$this->assertNull( $er->getRevertMethod(), 'EditResult::getRevertMethod()' );
$this->assertNull( $er->getOldestRevertedRevisionId(),
'EditResult::getOldestRevertedRevisionId()' );
$this->assertNull( $er->getNewestRevertedRevisionId(),
'EditResult::getNewestRevertedRevisionId()' );
$this->assertSame( 0, $er->getUndidRevId(), 'EditResult::getUndidRevId' );
$this->assertArrayEquals( [], $er->getRevertTags(), 'EditResult::getRevertTags' );
}
/**
* Tests the case when the new edit doesn't actually change anything on the page,
* i.e. is a null edit.
* @covers \MediaWiki\Storage\EditResult
* @covers \MediaWiki\Storage\EditResultBuilder
*/
public function testNullEdit() {
$originalRevision = $this->getExistingRevision();
$erb = $this->getNewEditResultBuilder( $originalRevision );
$newRevision = MutableRevisionRecord::newFromParentRevision( $originalRevision );
$erb->setOriginalRevisionId( $originalRevision->getId() );
$erb->setRevisionRecord( $newRevision );
$er = $erb->buildEditResult();
$this->assertFalse( $er->isNew(), 'EditResult::isNew()' );
$this->assertTrue( $er->isNullEdit(), 'EditResult::isNullEdit()' );
$this->assertSame( $originalRevision->getId(), $er->getOriginalRevisionId(),
'EditResult::getOriginalRevisionId()' );
$this->assertFalse( $er->isRevert(), 'EditResult::isRevert()' );
$this->assertFalse( $er->isExactRevert(), 'EditResult::isExactRevert()' );
$this->assertNull( $er->getRevertMethod(), 'EditResult::getRevertMethod()' );
$this->assertNull( $er->getOldestRevertedRevisionId(),
'EditResult::getOldestRevertedRevisionId()' );
$this->assertNull( $er->getNewestRevertedRevisionId(),
'EditResult::getNewestRevertedRevisionId()' );
$this->assertSame( 0, $er->getUndidRevId(), 'EditResult::getUndidRevId' );
$this->assertArrayEquals( [], $er->getRevertTags(), 'EditResult::getRevertTags' );
}
public function provideEnabledSoftwareTagsForRollback() : array {
return [
"all change tags enabled" => [
$this->getSoftwareTags(),
[ "mw-rollback" ]
],
"no change tags enabled" => [
[],
[]
]
];
}
/**
* Test the case where the edit restored the page exactly to a previous state.
*
* @covers \MediaWiki\Storage\EditResult
* @covers \MediaWiki\Storage\EditResultBuilder
* @dataProvider provideEnabledSoftwareTagsForRollback
*
* @param string[] $changeTags
* @param string[] $expectedRevertTags
*/
public function testRollback( array $changeTags, array $expectedRevertTags ) {
$originalRevision = $this->getExistingRevision();
$erb = $this->getNewEditResultBuilder( $originalRevision, $changeTags );
$newRevision = MutableRevisionRecord::newFromParentRevision( $originalRevision );
// We change the parent id to something different, so it's not treated as a null edit
$newRevision->setParentId( 125 );
$erb->setOriginalRevisionId( $originalRevision->getId() );
$erb->setRevisionRecord( $newRevision );
// We are bluffing here, those revision ids don't exist.
// EditResult is as dumb as possible, it doesn't check that.
$erb->markAsRevert( EditResult::REVERT_ROLLBACK, 123, 125 );
$er = $erb->buildEditResult();
$this->assertFalse( $er->isNew(), 'EditResult::isNew()' );
$this->assertFalse( $er->isNullEdit(), 'EditResult::isNullEdit()' );
$this->assertSame( $originalRevision->getId(), $er->getOriginalRevisionId(),
'EditResult::getOriginalRevisionId()' );
$this->assertTrue( $er->isRevert(), 'EditResult::isRevert()' );
$this->assertTrue( $er->isExactRevert(), 'EditResult::isExactRevert()' );
$this->assertSame( EditResult::REVERT_ROLLBACK, $er->getRevertMethod(),
'EditResult::getRevertMethod()' );
$this->assertSame( 123, $er->getOldestRevertedRevisionId(),
'EditResult::getOldestRevertedRevisionId()' );
$this->assertSame( 125, $er->getNewestRevertedRevisionId(),
'EditResult::getNewestRevertedRevisionId()' );
$this->assertSame( 0, $er->getUndidRevId(), 'EditResult::getUndidRevId' );
$this->assertArrayEquals( $expectedRevertTags, $er->getRevertTags(),
'EditResult::getRevertTags' );
}
public function provideEnabledSoftwareTagsForUndo() : array {
return [
"all change tags enabled" => [
$this->getSoftwareTags(),
[ "mw-undo" ]
],
"no change tags enabled" => [
[],
[]
]
];
}
/**
* Test the case where the edit was an undo
*
* @covers \MediaWiki\Storage\EditResult
* @covers \MediaWiki\Storage\EditResultBuilder
* @dataProvider provideEnabledSoftwareTagsForUndo
*
* @param string[] $changeTags
* @param string[] $expectedRevertTags
*/
public function testUndo( array $changeTags, array $expectedRevertTags ) {
$originalRevision = $this->getExistingRevision();
$erb = $this->getNewEditResultBuilder( $originalRevision, $changeTags );
$newRevision = MutableRevisionRecord::newFromParentRevision( $originalRevision );
// We change the parent id to something different, so it's not treated as a null edit
$newRevision->setParentId( 124 );
$erb->setOriginalRevisionId( $originalRevision->getId() );
$erb->setRevisionRecord( $newRevision );
$erb->markAsRevert( EditResult::REVERT_UNDO, 124 );
$er = $erb->buildEditResult();
$this->assertFalse( $er->isNew(), 'EditResult::isNew()' );
$this->assertFalse( $er->isNullEdit(), 'EditResult::isNullEdit()' );
$this->assertSame( $originalRevision->getId(), $er->getOriginalRevisionId(),
'EditResult::getOriginalRevisionId()' );
$this->assertTrue( $er->isRevert(), 'EditResult::isRevert()' );
$this->assertTrue( $er->isExactRevert(), 'EditResult::isExactRevert()' );
$this->assertSame( EditResult::REVERT_UNDO, $er->getRevertMethod(),
'EditResult::getRevertMethod()' );
$this->assertSame( 124, $er->getOldestRevertedRevisionId(),
'EditResult::getOldestRevertedRevisionId()' );
$this->assertSame( 124, $er->getNewestRevertedRevisionId(),
'EditResult::getNewestRevertedRevisionId()' );
$this->assertSame( 124, $er->getUndidRevId(), 'EditResult::getUndidRevId' );
$this->assertArrayEquals( $expectedRevertTags, $er->getRevertTags(),
'EditResult::getRevertTags' );
}
/**
* Test the case where setRevert() is called, but nothing was really reverted
*
* @covers \MediaWiki\Storage\EditResult
* @covers \MediaWiki\Storage\EditResultBuilder
*/
public function testIgnoreEmptyRevert() {
$erb = $this->getNewEditResultBuilder();
$newRevision = $this->getDummyRevision();
$erb->setRevisionRecord( $newRevision );
$erb->markAsRevert( EditResult::REVERT_UNDO, 0 );
$er = $erb->buildEditResult();
$this->assertFalse( $er->isNew(), 'EditResult::isNew()' );
$this->assertFalse( $er->isNullEdit(), 'EditResult::isNullEdit()' );
$this->assertFalse( $er->getOriginalRevisionId(), 'EditResult::getOriginalRevisionId()' );
$this->assertFalse( $er->isRevert(), 'EditResult::isRevert()' );
$this->assertFalse( $er->isExactRevert(), 'EditResult::isExactRevert()' );
$this->assertNull( $er->getRevertMethod(), 'EditResult::getRevertMethod()' );
$this->assertNull( $er->getOldestRevertedRevisionId(),
'EditResult::getOldestRevertedRevisionId()' );
$this->assertNull( $er->getNewestRevertedRevisionId(),
'EditResult::getNewestRevertedRevisionId()' );
$this->assertSame( 0, $er->getUndidRevId(), 'EditResult::getUndidRevId' );
$this->assertArrayEquals( [], $er->getRevertTags(), 'EditResult::getRevertTags' );
}
/**
* Test the case where setRevert() is properly called, but the original revision was not set
*
* @covers \MediaWiki\Storage\EditResult
* @covers \MediaWiki\Storage\EditResultBuilder
*/
public function testRevertWithoutOriginalRevision() {
$erb = $this->getNewEditResultBuilder(
null,
$this->getSoftwareTags()
);
$newRevision = $this->getDummyRevision();
$erb->setRevisionRecord( $newRevision );
$erb->markAsRevert( EditResult::REVERT_UNDO, 123 );
$er = $erb->buildEditResult();
$this->assertFalse( $er->isNew(), 'EditResult::isNew()' );
$this->assertFalse( $er->isNullEdit(), 'EditResult::isNullEdit()' );
$this->assertFalse( $er->getOriginalRevisionId(), 'EditResult::getOriginalRevisionId()' );
$this->assertTrue( $er->isRevert(), 'EditResult::isRevert()' );
$this->assertFalse( $er->isExactRevert(), 'EditResult::isExactRevert()' );
$this->assertSame( EditResult::REVERT_UNDO, $er->getRevertMethod(),
'EditResult::getRevertMethod()' );
$this->assertSame( 123, $er->getOldestRevertedRevisionId(),
'EditResult::getOldestRevertedRevisionId()' );
$this->assertSame( 123, $er->getNewestRevertedRevisionId(),
'EditResult::getNewestRevertedRevisionId()' );
$this->assertSame( 123, $er->getUndidRevId(), 'EditResult::getUndidRevId' );
$this->assertArrayEquals( [ 'mw-undo' ], $er->getRevertTags(),
'EditResult::getRevertTags' );
}
/**
* Returns an empty RevisionRecord
*
* @return MutableRevisionRecord
*/
private function getDummyRevision() : MutableRevisionRecord {
return new MutableRevisionRecord(
$this->createMock( Title::class )
);
}
/**
* Returns a RevisionRecord that pretends to have an ID and a page ID.
*
* @return MutableRevisionRecord
*/
private function getExistingRevision() : MutableRevisionRecord {
$revisionRecord = $this->getDummyRevision();
$revisionRecord->setId( 5 );
$revisionRecord->setPageId( 5 );
return $revisionRecord;
}
/**
* Convenience function for creating a new EditResultBuilder object.
*
* @param RevisionRecord|null $originalRevisionRecord RevisionRecord that should be returned
* by RevisionStore::getRevisionById.
* @param string[] $changeTags
*
* @return EditResultBuilder
*/
private function getNewEditResultBuilder(
?RevisionRecord $originalRevisionRecord = null,
array $changeTags = []
) {
$store = $this->createMock( RevisionStore::class );
$store->method( 'getRevisionById' )
->willReturn( $originalRevisionRecord );
return new EditResultBuilder(
$store,
$changeTags
);
}
/**
* Meant to reproduce the values provided by ChangeTags::getSoftwareTags.
*
* @return string[]
*/
private function getSoftwareTags() : array {
return [
"mw-contentmodelchange",
"mw-new-redirect",
"mw-removed-redirect",
"mw-changed-redirect-target",
"mw-blank",
"mw-replace",
"mw-rollback",
"mw-undo"
];
}
}