Several classes have a "selectFields()" static method to tell callers which fields to select from the database. With the recent comment table change and the upcoming actor table change, this pattern has become too simplistic as a SELECT will need to join several tables to be able to retrieve all the needed fields. Thus, we deprecate the selectFields() methods in favor of getQueryInfo() methods that return tables and join conditions in addition to the fields. Change-Id: Idcfd15568489d9f03a7ba4460e96610d33bc4089
397 lines
12 KiB
PHP
397 lines
12 KiB
PHP
<?php
|
|
|
|
use Wikimedia\TestingAccessWrapper;
|
|
|
|
/**
|
|
* @group ContentHandler
|
|
*/
|
|
class RevisionUnitTest extends MediaWikiTestCase {
|
|
|
|
public function provideConstructFromArray() {
|
|
yield 'with text' => [
|
|
[
|
|
'text' => 'hello world.',
|
|
'content_model' => CONTENT_MODEL_JAVASCRIPT
|
|
],
|
|
];
|
|
yield 'with content' => [
|
|
[
|
|
'content' => new JavaScriptContent( 'hellow world.' )
|
|
],
|
|
];
|
|
}
|
|
|
|
/**
|
|
* @dataProvider provideConstructFromArray
|
|
* @covers Revision::__construct
|
|
* @covers Revision::constructFromRowArray
|
|
*/
|
|
public function testConstructFromArray( array $rowArray ) {
|
|
$rev = new Revision( $rowArray );
|
|
$this->assertNotNull( $rev->getContent(), 'no content object available' );
|
|
$this->assertEquals( CONTENT_MODEL_JAVASCRIPT, $rev->getContent()->getModel() );
|
|
$this->assertEquals( CONTENT_MODEL_JAVASCRIPT, $rev->getContentModel() );
|
|
}
|
|
|
|
public function provideConstructFromArrayThrowsExceptions() {
|
|
yield 'content and text_id both not empty' => [
|
|
[
|
|
'content' => new WikitextContent( 'GOAT' ),
|
|
'text_id' => 'someid',
|
|
],
|
|
new MWException( "Text already stored in external store (id someid), " .
|
|
"can't serialize content object" )
|
|
];
|
|
yield 'with bad content object (class)' => [
|
|
[ 'content' => new stdClass() ],
|
|
new MWException( '`content` field must contain a Content object.' )
|
|
];
|
|
yield 'with bad content object (string)' => [
|
|
[ 'content' => 'ImAGoat' ],
|
|
new MWException( '`content` field must contain a Content object.' )
|
|
];
|
|
yield 'bad row format' => [
|
|
'imastring, not a row',
|
|
new MWException( 'Revision constructor passed invalid row format.' )
|
|
];
|
|
}
|
|
|
|
/**
|
|
* @dataProvider provideConstructFromArrayThrowsExceptions
|
|
* @covers Revision::__construct
|
|
* @covers Revision::constructFromRowArray
|
|
*/
|
|
public function testConstructFromArrayThrowsExceptions( $rowArray, Exception $expectedException ) {
|
|
$this->setExpectedException(
|
|
get_class( $expectedException ),
|
|
$expectedException->getMessage(),
|
|
$expectedException->getCode()
|
|
);
|
|
new Revision( $rowArray );
|
|
}
|
|
|
|
public function provideConstructFromRow() {
|
|
yield 'Full construction' => [
|
|
[
|
|
'rev_id' => '2',
|
|
'rev_page' => '1',
|
|
'rev_text_id' => '2',
|
|
'rev_timestamp' => '20171017114835',
|
|
'rev_user_text' => '127.0.0.1',
|
|
'rev_user' => '0',
|
|
'rev_minor_edit' => '0',
|
|
'rev_deleted' => '0',
|
|
'rev_len' => '46',
|
|
'rev_parent_id' => '1',
|
|
'rev_sha1' => 'rdqbbzs3pkhihgbs8qf2q9jsvheag5z',
|
|
'rev_comment_text' => 'Goat Comment!',
|
|
'rev_comment_data' => null,
|
|
'rev_comment_cid' => null,
|
|
'rev_content_format' => 'GOATFORMAT',
|
|
'rev_content_model' => 'GOATMODEL',
|
|
],
|
|
function ( RevisionUnitTest $testCase, Revision $rev ) {
|
|
$testCase->assertSame( 2, $rev->getId() );
|
|
$testCase->assertSame( 1, $rev->getPage() );
|
|
$testCase->assertSame( 2, $rev->getTextId() );
|
|
$testCase->assertSame( '20171017114835', $rev->getTimestamp() );
|
|
$testCase->assertSame( '127.0.0.1', $rev->getUserText() );
|
|
$testCase->assertSame( 0, $rev->getUser() );
|
|
$testCase->assertSame( false, $rev->isMinor() );
|
|
$testCase->assertSame( false, $rev->isDeleted( Revision::DELETED_TEXT ) );
|
|
$testCase->assertSame( 46, $rev->getSize() );
|
|
$testCase->assertSame( 1, $rev->getParentId() );
|
|
$testCase->assertSame( 'rdqbbzs3pkhihgbs8qf2q9jsvheag5z', $rev->getSha1() );
|
|
$testCase->assertSame( 'Goat Comment!', $rev->getComment() );
|
|
$testCase->assertSame( 'GOATFORMAT', $rev->getContentFormat() );
|
|
$testCase->assertSame( 'GOATMODEL', $rev->getContentModel() );
|
|
}
|
|
];
|
|
yield 'null fields' => [
|
|
[
|
|
'rev_id' => '2',
|
|
'rev_page' => '1',
|
|
'rev_text_id' => '2',
|
|
'rev_timestamp' => '20171017114835',
|
|
'rev_user_text' => '127.0.0.1',
|
|
'rev_user' => '0',
|
|
'rev_minor_edit' => '0',
|
|
'rev_deleted' => '0',
|
|
'rev_comment_text' => 'Goat Comment!',
|
|
'rev_comment_data' => null,
|
|
'rev_comment_cid' => null,
|
|
],
|
|
function ( RevisionUnitTest $testCase, Revision $rev ) {
|
|
$testCase->assertNull( $rev->getSize() );
|
|
$testCase->assertNull( $rev->getParentId() );
|
|
$testCase->assertNull( $rev->getSha1() );
|
|
$testCase->assertSame( 'text/x-wiki', $rev->getContentFormat() );
|
|
$testCase->assertSame( 'wikitext', $rev->getContentModel() );
|
|
}
|
|
];
|
|
}
|
|
|
|
/**
|
|
* @dataProvider provideConstructFromRow
|
|
* @covers Revision::__construct
|
|
* @covers Revision::constructFromDbRowObject
|
|
*/
|
|
public function testConstructFromRow( array $arrayData, $assertions ) {
|
|
$row = (object)$arrayData;
|
|
$rev = new Revision( $row );
|
|
$assertions( $this, $rev );
|
|
}
|
|
|
|
public function provideGetRevisionText() {
|
|
yield 'Generic test' => [
|
|
'This is a goat of revision text.',
|
|
[
|
|
'old_flags' => '',
|
|
'old_text' => 'This is a goat of revision text.',
|
|
],
|
|
];
|
|
}
|
|
|
|
public function provideGetId() {
|
|
yield [
|
|
[],
|
|
null
|
|
];
|
|
yield [
|
|
[ 'id' => 998 ],
|
|
998
|
|
];
|
|
}
|
|
|
|
/**
|
|
* @dataProvider provideGetId
|
|
* @covers Revision::getId
|
|
*/
|
|
public function testGetId( $rowArray, $expectedId ) {
|
|
$rev = new Revision( $rowArray );
|
|
$this->assertEquals( $expectedId, $rev->getId() );
|
|
}
|
|
|
|
public function provideSetId() {
|
|
yield [ '123', 123 ];
|
|
yield [ 456, 456 ];
|
|
}
|
|
|
|
/**
|
|
* @dataProvider provideSetId
|
|
* @covers Revision::setId
|
|
*/
|
|
public function testSetId( $input, $expected ) {
|
|
$rev = new Revision( [] );
|
|
$rev->setId( $input );
|
|
$this->assertSame( $expected, $rev->getId() );
|
|
}
|
|
|
|
public function provideSetUserIdAndName() {
|
|
yield [ '123', 123, 'GOaT' ];
|
|
yield [ 456, 456, 'GOaT' ];
|
|
}
|
|
|
|
/**
|
|
* @dataProvider provideSetUserIdAndName
|
|
* @covers Revision::setUserIdAndName
|
|
*/
|
|
public function testSetUserIdAndName( $inputId, $expectedId, $name ) {
|
|
$rev = new Revision( [] );
|
|
$rev->setUserIdAndName( $inputId, $name );
|
|
$this->assertSame( $expectedId, $rev->getUser( Revision::RAW ) );
|
|
$this->assertEquals( $name, $rev->getUserText( Revision::RAW ) );
|
|
}
|
|
|
|
public function provideGetTextId() {
|
|
yield [ [], null ];
|
|
yield [ [ 'text_id' => '123' ], 123 ];
|
|
yield [ [ 'text_id' => 456 ], 456 ];
|
|
}
|
|
|
|
/**
|
|
* @dataProvider provideGetTextId
|
|
* @covers Revision::getTextId()
|
|
*/
|
|
public function testGetTextId( $rowArray, $expected ) {
|
|
$rev = new Revision( $rowArray );
|
|
$this->assertSame( $expected, $rev->getTextId() );
|
|
}
|
|
|
|
public function provideGetParentId() {
|
|
yield [ [], null ];
|
|
yield [ [ 'parent_id' => '123' ], 123 ];
|
|
yield [ [ 'parent_id' => 456 ], 456 ];
|
|
}
|
|
|
|
/**
|
|
* @dataProvider provideGetParentId
|
|
* @covers Revision::getParentId()
|
|
*/
|
|
public function testGetParentId( $rowArray, $expected ) {
|
|
$rev = new Revision( $rowArray );
|
|
$this->assertSame( $expected, $rev->getParentId() );
|
|
}
|
|
|
|
/**
|
|
* @covers Revision::getRevisionText
|
|
* @dataProvider provideGetRevisionText
|
|
*/
|
|
public function testGetRevisionText( $expected, $rowData, $prefix = 'old_', $wiki = false ) {
|
|
$this->assertEquals(
|
|
$expected,
|
|
Revision::getRevisionText( (object)$rowData, $prefix, $wiki ) );
|
|
}
|
|
|
|
public function provideGetRevisionTextWithZlibExtension() {
|
|
yield 'Generic gzip test' => [
|
|
'This is a small goat of revision text.',
|
|
[
|
|
'old_flags' => 'gzip',
|
|
'old_text' => gzdeflate( 'This is a small goat of revision text.' ),
|
|
],
|
|
];
|
|
}
|
|
|
|
/**
|
|
* @covers Revision::getRevisionText
|
|
* @dataProvider provideGetRevisionTextWithZlibExtension
|
|
*/
|
|
public function testGetRevisionWithZlibExtension( $expected, $rowData ) {
|
|
$this->checkPHPExtension( 'zlib' );
|
|
$this->testGetRevisionText( $expected, $rowData );
|
|
}
|
|
|
|
public function provideGetRevisionTextWithLegacyEncoding() {
|
|
yield 'Utf8Native' => [
|
|
"Wiki est l'\xc3\xa9cole superieur !",
|
|
'iso-8859-1',
|
|
[
|
|
'old_flags' => 'utf-8',
|
|
'old_text' => "Wiki est l'\xc3\xa9cole superieur !",
|
|
]
|
|
];
|
|
yield 'Utf8Legacy' => [
|
|
"Wiki est l'\xc3\xa9cole superieur !",
|
|
'iso-8859-1',
|
|
[
|
|
'old_flags' => '',
|
|
'old_text' => "Wiki est l'\xe9cole superieur !",
|
|
]
|
|
];
|
|
}
|
|
|
|
/**
|
|
* @covers Revision::getRevisionText
|
|
* @dataProvider provideGetRevisionTextWithLegacyEncoding
|
|
*/
|
|
public function testGetRevisionWithLegacyEncoding( $expected, $encoding, $rowData ) {
|
|
$this->setMwGlobals( 'wgLegacyEncoding', $encoding );
|
|
$this->testGetRevisionText( $expected, $rowData );
|
|
}
|
|
|
|
public function provideGetRevisionTextWithGzipAndLegacyEncoding() {
|
|
/**
|
|
* WARNING!
|
|
* Do not set the external flag!
|
|
* Otherwise, getRevisionText will hit the live database (if ExternalStore is enabled)!
|
|
*/
|
|
yield 'Utf8NativeGzip' => [
|
|
"Wiki est l'\xc3\xa9cole superieur !",
|
|
'iso-8859-1',
|
|
[
|
|
'old_flags' => 'gzip,utf-8',
|
|
'old_text' => gzdeflate( "Wiki est l'\xc3\xa9cole superieur !" ),
|
|
]
|
|
];
|
|
yield 'Utf8LegacyGzip' => [
|
|
"Wiki est l'\xc3\xa9cole superieur !",
|
|
'iso-8859-1',
|
|
[
|
|
'old_flags' => 'gzip',
|
|
'old_text' => gzdeflate( "Wiki est l'\xe9cole superieur !" ),
|
|
]
|
|
];
|
|
}
|
|
|
|
/**
|
|
* @covers Revision::getRevisionText
|
|
* @dataProvider provideGetRevisionTextWithGzipAndLegacyEncoding
|
|
*/
|
|
public function testGetRevisionWithGzipAndLegacyEncoding( $expected, $encoding, $rowData ) {
|
|
$this->checkPHPExtension( 'zlib' );
|
|
$this->setMwGlobals( 'wgLegacyEncoding', $encoding );
|
|
$this->testGetRevisionText( $expected, $rowData );
|
|
}
|
|
|
|
/**
|
|
* @covers Revision::compressRevisionText
|
|
*/
|
|
public function testCompressRevisionTextUtf8() {
|
|
$row = new stdClass;
|
|
$row->old_text = "Wiki est l'\xc3\xa9cole superieur !";
|
|
$row->old_flags = Revision::compressRevisionText( $row->old_text );
|
|
$this->assertTrue( false !== strpos( $row->old_flags, 'utf-8' ),
|
|
"Flags should contain 'utf-8'" );
|
|
$this->assertFalse( false !== strpos( $row->old_flags, 'gzip' ),
|
|
"Flags should not contain 'gzip'" );
|
|
$this->assertEquals( "Wiki est l'\xc3\xa9cole superieur !",
|
|
$row->old_text, "Direct check" );
|
|
$this->assertEquals( "Wiki est l'\xc3\xa9cole superieur !",
|
|
Revision::getRevisionText( $row ), "getRevisionText" );
|
|
}
|
|
|
|
/**
|
|
* @covers Revision::compressRevisionText
|
|
*/
|
|
public function testCompressRevisionTextUtf8Gzip() {
|
|
$this->checkPHPExtension( 'zlib' );
|
|
$this->setMwGlobals( 'wgCompressRevisions', true );
|
|
|
|
$row = new stdClass;
|
|
$row->old_text = "Wiki est l'\xc3\xa9cole superieur !";
|
|
$row->old_flags = Revision::compressRevisionText( $row->old_text );
|
|
$this->assertTrue( false !== strpos( $row->old_flags, 'utf-8' ),
|
|
"Flags should contain 'utf-8'" );
|
|
$this->assertTrue( false !== strpos( $row->old_flags, 'gzip' ),
|
|
"Flags should contain 'gzip'" );
|
|
$this->assertEquals( "Wiki est l'\xc3\xa9cole superieur !",
|
|
gzinflate( $row->old_text ), "Direct check" );
|
|
$this->assertEquals( "Wiki est l'\xc3\xa9cole superieur !",
|
|
Revision::getRevisionText( $row ), "getRevisionText" );
|
|
}
|
|
|
|
public function provideFetchFromConds() {
|
|
yield [ 0, [] ];
|
|
yield [ Revision::READ_LOCKING, [ 'FOR UPDATE' ] ];
|
|
}
|
|
|
|
/**
|
|
* @dataProvider provideFetchFromConds
|
|
* @covers Revision::fetchFromConds
|
|
*/
|
|
public function testFetchFromConds( $flags, array $options ) {
|
|
$conditions = [ 'conditionsArray' ];
|
|
|
|
$db = $this->getMock( IDatabase::class );
|
|
$db->expects( $this->once() )
|
|
->method( 'selectRow' )
|
|
->with(
|
|
$this->equalTo( [ 'revision', 'page', 'user' ] ),
|
|
// We don't really care about the fields are they come from the selectField methods
|
|
$this->isType( 'array' ),
|
|
$this->equalTo( $conditions ),
|
|
// Method name
|
|
$this->equalTo( 'Revision::fetchFromConds' ),
|
|
$this->equalTo( $options ),
|
|
// We don't really care about the join conds are they come from the joinCond methods
|
|
$this->isType( 'array' )
|
|
)
|
|
->willReturn( 'RETURNVALUE' );
|
|
|
|
$wrapper = TestingAccessWrapper::newFromClass( Revision::class );
|
|
$result = $wrapper->fetchFromConds( $db, $conditions, $flags );
|
|
|
|
$this->assertEquals( 'RETURNVALUE', $result );
|
|
}
|
|
}
|