diff options
Diffstat (limited to 'www/wiki/tests/phpunit/includes/logging')
16 files changed, 3573 insertions, 0 deletions
diff --git a/www/wiki/tests/phpunit/includes/logging/BlockLogFormatterTest.php b/www/wiki/tests/phpunit/includes/logging/BlockLogFormatterTest.php new file mode 100644 index 00000000..03671ac8 --- /dev/null +++ b/www/wiki/tests/phpunit/includes/logging/BlockLogFormatterTest.php @@ -0,0 +1,379 @@ +<?php + +/** + * @covers BlockLogFormatter + */ +class BlockLogFormatterTest extends LogFormatterTestCase { + + /** + * Provide different rows from the logging table to test + * for backward compatibility. + * Do not change the existing data, just add a new database row + */ + public static function provideBlockLogDatabaseRows() { + return [ + // Current log format + [ + [ + 'type' => 'block', + 'action' => 'block', + 'comment' => 'Block comment', + 'user' => 0, + 'user_text' => 'Sysop', + 'namespace' => NS_USER, + 'title' => 'Logtestuser', + 'params' => [ + '5::duration' => 'infinite', + '6::flags' => 'anononly', + ], + ], + [ + 'text' => 'Sysop blocked Logtestuser with an expiration time of indefinite' + . ' (anonymous users only)', + 'api' => [ + 'duration' => 'infinite', + 'flags' => [ 'anononly' ], + ], + ], + ], + + // Old legacy log + [ + [ + 'type' => 'block', + 'action' => 'block', + 'comment' => 'Block comment', + 'user' => 0, + 'user_text' => 'Sysop', + 'namespace' => NS_USER, + 'title' => 'Logtestuser', + 'params' => [ + 'infinite', + 'anononly', + ], + ], + [ + 'legacy' => true, + 'text' => 'Sysop blocked Logtestuser with an expiration time of indefinite' + . ' (anonymous users only)', + 'api' => [ + 'duration' => 'infinite', + 'flags' => [ 'anononly' ], + ], + ], + ], + + // Old legacy log without flag + [ + [ + 'type' => 'block', + 'action' => 'block', + 'comment' => 'Block comment', + 'user' => 0, + 'user_text' => 'Sysop', + 'namespace' => NS_USER, + 'title' => 'Logtestuser', + 'params' => [ + 'infinite', + ], + ], + [ + 'legacy' => true, + 'text' => 'Sysop blocked Logtestuser with an expiration time of indefinite', + 'api' => [ + 'duration' => 'infinite', + 'flags' => [], + ], + ], + ], + + // Very old legacy log without duration + [ + [ + 'type' => 'block', + 'action' => 'block', + 'comment' => 'Block comment', + 'user' => 0, + 'user_text' => 'Sysop', + 'namespace' => NS_USER, + 'title' => 'Logtestuser', + 'params' => [], + ], + [ + 'legacy' => true, + 'text' => 'Sysop blocked Logtestuser with an expiration time of indefinite', + 'api' => [ + 'duration' => 'infinite', + 'flags' => [], + ], + ], + ], + ]; + } + + /** + * @dataProvider provideBlockLogDatabaseRows + */ + public function testBlockLogDatabaseRows( $row, $extra ) { + $this->doTestLogFormatter( $row, $extra ); + } + + /** + * Provide different rows from the logging table to test + * for backward compatibility. + * Do not change the existing data, just add a new database row + */ + public static function provideReblockLogDatabaseRows() { + return [ + // Current log format + [ + [ + 'type' => 'block', + 'action' => 'reblock', + 'comment' => 'Block comment', + 'user' => 0, + 'user_text' => 'Sysop', + 'namespace' => NS_USER, + 'title' => 'Logtestuser', + 'params' => [ + '5::duration' => 'infinite', + '6::flags' => 'anononly', + ], + ], + [ + 'text' => 'Sysop changed block settings for Logtestuser with an expiration time of' + . ' indefinite (anonymous users only)', + 'api' => [ + 'duration' => 'infinite', + 'flags' => [ 'anononly' ], + ], + ], + ], + + // Old log + [ + [ + 'type' => 'block', + 'action' => 'reblock', + 'comment' => 'Block comment', + 'user' => 0, + 'user_text' => 'Sysop', + 'namespace' => NS_USER, + 'title' => 'Logtestuser', + 'params' => [ + 'infinite', + 'anononly', + ], + ], + [ + 'legacy' => true, + 'text' => 'Sysop changed block settings for Logtestuser with an expiration time of' + . ' indefinite (anonymous users only)', + 'api' => [ + 'duration' => 'infinite', + 'flags' => [ 'anononly' ], + ], + ], + ], + + // Older log without flag + [ + [ + 'type' => 'block', + 'action' => 'reblock', + 'comment' => 'Block comment', + 'user' => 0, + 'user_text' => 'Sysop', + 'namespace' => NS_USER, + 'title' => 'Logtestuser', + 'params' => [ + 'infinite', + ] + ], + [ + 'legacy' => true, + 'text' => 'Sysop changed block settings for Logtestuser with an expiration time of indefinite', + 'api' => [ + 'duration' => 'infinite', + 'flags' => [], + ], + ], + ], + ]; + } + + /** + * @dataProvider provideReblockLogDatabaseRows + */ + public function testReblockLogDatabaseRows( $row, $extra ) { + $this->doTestLogFormatter( $row, $extra ); + } + + /** + * Provide different rows from the logging table to test + * for backward compatibility. + * Do not change the existing data, just add a new database row + */ + public static function provideUnblockLogDatabaseRows() { + return [ + // Current log format + [ + [ + 'type' => 'block', + 'action' => 'unblock', + 'comment' => 'Block comment', + 'user' => 0, + 'user_text' => 'Sysop', + 'namespace' => NS_USER, + 'title' => 'Logtestuser', + 'params' => [], + ], + [ + 'text' => 'Sysop unblocked Logtestuser', + 'api' => [], + ], + ], + ]; + } + + /** + * @dataProvider provideUnblockLogDatabaseRows + */ + public function testUnblockLogDatabaseRows( $row, $extra ) { + $this->doTestLogFormatter( $row, $extra ); + } + + /** + * Provide different rows from the logging table to test + * for backward compatibility. + * Do not change the existing data, just add a new database row + */ + public static function provideSuppressBlockLogDatabaseRows() { + return [ + // Current log format + [ + [ + 'type' => 'suppress', + 'action' => 'block', + 'comment' => 'Block comment', + 'user' => 0, + 'user_text' => 'Sysop', + 'namespace' => NS_USER, + 'title' => 'Logtestuser', + 'params' => [ + '5::duration' => 'infinite', + '6::flags' => 'anononly', + ], + ], + [ + 'text' => 'Sysop blocked Logtestuser with an expiration time of indefinite' + . ' (anonymous users only)', + 'api' => [ + 'duration' => 'infinite', + 'flags' => [ 'anononly' ], + ], + ], + ], + + // legacy log + [ + [ + 'type' => 'suppress', + 'action' => 'block', + 'comment' => 'Block comment', + 'user' => 0, + 'user_text' => 'Sysop', + 'namespace' => NS_USER, + 'title' => 'Logtestuser', + 'params' => [ + 'infinite', + 'anononly', + ], + ], + [ + 'legacy' => true, + 'text' => 'Sysop blocked Logtestuser with an expiration time of indefinite' + . ' (anonymous users only)', + 'api' => [ + 'duration' => 'infinite', + 'flags' => [ 'anononly' ], + ], + ], + ], + ]; + } + + /** + * @dataProvider provideSuppressBlockLogDatabaseRows + */ + public function testSuppressBlockLogDatabaseRows( $row, $extra ) { + $this->doTestLogFormatter( $row, $extra ); + } + + /** + * Provide different rows from the logging table to test + * for backward compatibility. + * Do not change the existing data, just add a new database row + */ + public static function provideSuppressReblockLogDatabaseRows() { + return [ + // Current log format + [ + [ + 'type' => 'suppress', + 'action' => 'reblock', + 'comment' => 'Block comment', + 'user' => 0, + 'user_text' => 'Sysop', + 'namespace' => NS_USER, + 'title' => 'Logtestuser', + 'params' => [ + '5::duration' => 'infinite', + '6::flags' => 'anononly', + ], + ], + [ + 'text' => 'Sysop changed block settings for Logtestuser with an expiration time of' + . ' indefinite (anonymous users only)', + 'api' => [ + 'duration' => 'infinite', + 'flags' => [ 'anononly' ], + ], + ], + ], + + // Legacy format + [ + [ + 'type' => 'suppress', + 'action' => 'reblock', + 'comment' => 'Block comment', + 'user' => 0, + 'user_text' => 'Sysop', + 'namespace' => NS_USER, + 'title' => 'Logtestuser', + 'params' => [ + 'infinite', + 'anononly', + ], + ], + [ + 'legacy' => true, + 'text' => 'Sysop changed block settings for Logtestuser with an expiration time of' + . ' indefinite (anonymous users only)', + 'api' => [ + 'duration' => 'infinite', + 'flags' => [ 'anononly' ], + ], + ], + ], + ]; + } + + /** + * @dataProvider provideSuppressReblockLogDatabaseRows + */ + public function testSuppressReblockLogDatabaseRows( $row, $extra ) { + $this->doTestLogFormatter( $row, $extra ); + } +} diff --git a/www/wiki/tests/phpunit/includes/logging/ContentModelLogFormatterTest.php b/www/wiki/tests/phpunit/includes/logging/ContentModelLogFormatterTest.php new file mode 100644 index 00000000..17e54115 --- /dev/null +++ b/www/wiki/tests/phpunit/includes/logging/ContentModelLogFormatterTest.php @@ -0,0 +1,60 @@ +<?php + +/** + * @covers ContentModelLogFormatter + */ +class ContentModelLogFormatterTest extends LogFormatterTestCase { + public static function provideContentModelLogDatabaseRows() { + return [ + [ + [ + 'type' => 'contentmodel', + 'action' => 'new', + 'comment' => 'new content model comment', + 'namespace' => NS_MAIN, + 'title' => 'ContentModelPage', + 'params' => [ + '5::newModel' => 'testcontentmodel', + ], + ], + [ + 'text' => 'User created the page ContentModelPage ' . + 'using a non-default content model ' . + '"testcontentmodel"', + 'api' => [ + 'newModel' => 'testcontentmodel', + ], + ], + ], + [ + [ + 'type' => 'contentmodel', + 'action' => 'change', + 'comment' => 'change content model comment', + 'namespace' => NS_MAIN, + 'title' => 'ContentModelPage', + 'params' => [ + '4::oldmodel' => 'wikitext', + '5::newModel' => 'testcontentmodel', + ], + ], + [ + 'text' => 'User changed the content model of the page ' . + 'ContentModelPage from "wikitext" to ' . + '"testcontentmodel"', + 'api' => [ + 'oldmodel' => 'wikitext', + 'newModel' => 'testcontentmodel', + ], + ], + ], + ]; + } + + /** + * @dataProvider provideContentModelLogDatabaseRows + */ + public function testContentModelLogDatabaseRows( $row, $extra ) { + $this->doTestLogFormatter( $row, $extra ); + } +} diff --git a/www/wiki/tests/phpunit/includes/logging/DatabaseLogEntryTest.php b/www/wiki/tests/phpunit/includes/logging/DatabaseLogEntryTest.php new file mode 100644 index 00000000..4af1742e --- /dev/null +++ b/www/wiki/tests/phpunit/includes/logging/DatabaseLogEntryTest.php @@ -0,0 +1,162 @@ +<?php + +use MediaWiki\MediaWikiServices; +use Wikimedia\Rdbms\IDatabase; + +class DatabaseLogEntryTest extends MediaWikiTestCase { + public function setUp() { + parent::setUp(); + + // These services cache their joins + MediaWikiServices::getInstance()->resetServiceForTesting( 'CommentStore' ); + MediaWikiServices::getInstance()->resetServiceForTesting( 'ActorMigration' ); + } + + public function tearDown() { + parent::tearDown(); + + MediaWikiServices::getInstance()->resetServiceForTesting( 'CommentStore' ); + MediaWikiServices::getInstance()->resetServiceForTesting( 'ActorMigration' ); + } + + /** + * @covers DatabaseLogEntry::newFromId + * @covers DatabaseLogEntry::getSelectQueryData + * + * @dataProvider provideNewFromId + * + * @param int $id + * @param array $selectFields + * @param string[]|null $row + * @param string[]|null $expectedFields + * @param string $migration + */ + public function testNewFromId( $id, + array $selectFields, + array $row = null, + array $expectedFields = null, + $migration + ) { + $this->setMwGlobals( [ + 'wgCommentTableSchemaMigrationStage' => $migration, + 'wgActorTableSchemaMigrationStage' => $migration, + ] ); + + $row = $row ? (object)$row : null; + $db = $this->getMock( IDatabase::class ); + $db->expects( self::once() ) + ->method( 'selectRow' ) + ->with( $selectFields['tables'], + $selectFields['fields'], + $selectFields['conds'], + 'DatabaseLogEntry::newFromId', + $selectFields['options'], + $selectFields['join_conds'] + ) + ->will( self::returnValue( $row ) ); + + /** @var IDatabase $db */ + $logEntry = DatabaseLogEntry::newFromId( $id, $db ); + + if ( !$expectedFields ) { + self::assertNull( $logEntry, "Expected no log entry returned for id=$id" ); + } else { + self::assertEquals( $id, $logEntry->getId() ); + self::assertEquals( $expectedFields['type'], $logEntry->getType() ); + self::assertEquals( $expectedFields['comment'], $logEntry->getComment() ); + } + } + + public function provideNewFromId() { + $oldTables = [ + 'tables' => [ 'logging', 'user' ], + 'fields' => [ + 'log_id', + 'log_type', + 'log_action', + 'log_timestamp', + 'log_namespace', + 'log_title', + 'log_params', + 'log_deleted', + 'user_id', + 'user_name', + 'user_editcount', + 'log_comment_text' => 'log_comment', + 'log_comment_data' => 'NULL', + 'log_comment_cid' => 'NULL', + 'log_user' => 'log_user', + 'log_user_text' => 'log_user_text', + 'log_actor' => 'NULL', + ], + 'options' => [], + 'join_conds' => [ 'user' => [ 'LEFT JOIN', 'user_id=log_user' ] ], + ]; + $newTables = [ + 'tables' => [ + 'logging', + 'user', + 'comment_log_comment' => 'comment', + 'actor_log_user' => 'actor' + ], + 'fields' => [ + 'log_id', + 'log_type', + 'log_action', + 'log_timestamp', + 'log_namespace', + 'log_title', + 'log_params', + 'log_deleted', + 'user_id', + 'user_name', + 'user_editcount', + 'log_comment_text' => 'comment_log_comment.comment_text', + 'log_comment_data' => 'comment_log_comment.comment_data', + 'log_comment_cid' => 'comment_log_comment.comment_id', + 'log_user' => 'actor_log_user.actor_user', + 'log_user_text' => 'actor_log_user.actor_name', + 'log_actor' => 'log_actor', + ], + 'options' => [], + 'join_conds' => [ + 'user' => [ 'LEFT JOIN', 'user_id=actor_log_user.actor_user' ], + 'comment_log_comment' => [ 'JOIN', 'comment_log_comment.comment_id = log_comment_id' ], + 'actor_log_user' => [ 'JOIN', 'actor_log_user.actor_id = log_actor' ], + ], + ]; + return [ + [ + 0, + $oldTables + [ 'conds' => [ 'log_id' => 0 ] ], + null, + null, + MIGRATION_OLD, + ], + [ + 123, + $oldTables + [ 'conds' => [ 'log_id' => 123 ] ], + [ + 'log_id' => 123, + 'log_type' => 'foobarize', + 'log_comment_text' => 'test!', + 'log_comment_data' => null, + ], + [ 'type' => 'foobarize', 'comment' => 'test!' ], + MIGRATION_OLD, + ], + [ + 567, + $newTables + [ 'conds' => [ 'log_id' => 567 ] ], + [ + 'log_id' => 567, + 'log_type' => 'foobarize', + 'log_comment_text' => 'test!', + 'log_comment_data' => null, + ], + [ 'type' => 'foobarize', 'comment' => 'test!' ], + MIGRATION_NEW, + ], + ]; + } +} diff --git a/www/wiki/tests/phpunit/includes/logging/DeleteLogFormatterTest.php b/www/wiki/tests/phpunit/includes/logging/DeleteLogFormatterTest.php new file mode 100644 index 00000000..0e6855d9 --- /dev/null +++ b/www/wiki/tests/phpunit/includes/logging/DeleteLogFormatterTest.php @@ -0,0 +1,556 @@ +<?php + +/** + * @covers DeleteLogFormatter + */ +class DeleteLogFormatterTest extends LogFormatterTestCase { + + /** + * Provide different rows from the logging table to test + * for backward compatibility. + * Do not change the existing data, just add a new database row + */ + public static function provideDeleteLogDatabaseRows() { + return [ + // Current format + [ + [ + 'type' => 'delete', + 'action' => 'delete', + 'comment' => 'delete comment', + 'namespace' => NS_MAIN, + 'title' => 'Page', + 'params' => [], + ], + [ + 'text' => 'User deleted page Page', + 'api' => [], + ], + ], + + // Legacy format + [ + [ + 'type' => 'delete', + 'action' => 'delete', + 'comment' => 'delete comment', + 'namespace' => NS_MAIN, + 'title' => 'Page', + 'params' => [], + ], + [ + 'legacy' => true, + 'text' => 'User deleted page Page', + 'api' => [], + ], + ], + ]; + } + + /** + * @dataProvider provideDeleteLogDatabaseRows + */ + public function testDeleteLogDatabaseRows( $row, $extra ) { + $this->doTestLogFormatter( $row, $extra ); + } + + /** + * Provide different rows from the logging table to test + * for backward compatibility. + * Do not change the existing data, just add a new database row + */ + public static function provideRestoreLogDatabaseRows() { + return [ + // Current format + [ + [ + 'type' => 'delete', + 'action' => 'restore', + 'comment' => 'delete comment', + 'namespace' => NS_MAIN, + 'title' => 'Page', + 'params' => [ + ':assoc:count' => [ + 'revisions' => 2, + 'files' => 1, + ], + ], + ], + [ + 'text' => 'User restored page Page (2 revisions and 1 file)', + 'api' => [ + 'count' => [ + 'revisions' => 2, + 'files' => 1, + ], + ], + ], + ], + + // Legacy format without counts + [ + [ + 'type' => 'delete', + 'action' => 'restore', + 'comment' => 'delete comment', + 'namespace' => NS_MAIN, + 'title' => 'Page', + 'params' => [], + ], + [ + 'text' => 'User restored page Page', + 'api' => [], + ], + ], + + // Legacy format + [ + [ + 'type' => 'delete', + 'action' => 'restore', + 'comment' => 'delete comment', + 'namespace' => NS_MAIN, + 'title' => 'Page', + 'params' => [], + ], + [ + 'legacy' => true, + 'text' => 'User restored page Page', + 'api' => [], + ], + ], + ]; + } + + /** + * @dataProvider provideRestoreLogDatabaseRows + */ + public function testRestoreLogDatabaseRows( $row, $extra ) { + $this->doTestLogFormatter( $row, $extra ); + } + + /** + * Provide different rows from the logging table to test + * for backward compatibility. + * Do not change the existing data, just add a new database row + */ + public static function provideRevisionLogDatabaseRows() { + return [ + // Current format + [ + [ + 'type' => 'delete', + 'action' => 'revision', + 'comment' => 'delete comment', + 'namespace' => NS_MAIN, + 'title' => 'Page', + 'params' => [ + '4::type' => 'archive', + '5::ids' => [ '1', '3', '4' ], + '6::ofield' => '1', + '7::nfield' => '2', + ], + ], + [ + 'text' => 'User changed visibility of 3 revisions on page Page: edit summary ' + . 'hidden and content unhidden', + 'api' => [ + 'type' => 'archive', + 'ids' => [ '1', '3', '4' ], + 'old' => [ + 'bitmask' => 1, + 'content' => true, + 'comment' => false, + 'user' => false, + 'restricted' => false, + ], + 'new' => [ + 'bitmask' => 2, + 'content' => false, + 'comment' => true, + 'user' => false, + 'restricted' => false, + ], + ], + ], + ], + + // Legacy format + [ + [ + 'type' => 'delete', + 'action' => 'revision', + 'comment' => 'delete comment', + 'namespace' => NS_MAIN, + 'title' => 'Page', + 'params' => [ + 'archive', + '1,3,4', + 'ofield=1', + 'nfield=2', + ], + ], + [ + 'legacy' => true, + 'text' => 'User changed visibility of 3 revisions on page Page: edit summary ' + . 'hidden and content unhidden', + 'api' => [ + 'type' => 'archive', + 'ids' => [ '1', '3', '4' ], + 'old' => [ + 'bitmask' => 1, + 'content' => true, + 'comment' => false, + 'user' => false, + 'restricted' => false, + ], + 'new' => [ + 'bitmask' => 2, + 'content' => false, + 'comment' => true, + 'user' => false, + 'restricted' => false, + ], + ], + ], + ], + ]; + } + + /** + * @dataProvider provideRevisionLogDatabaseRows + */ + public function testRevisionLogDatabaseRows( $row, $extra ) { + $this->doTestLogFormatter( $row, $extra ); + } + + /** + * Provide different rows from the logging table to test + * for backward compatibility. + * Do not change the existing data, just add a new database row + */ + public static function provideEventLogDatabaseRows() { + return [ + // Current format + [ + [ + 'type' => 'delete', + 'action' => 'event', + 'comment' => 'delete comment', + 'namespace' => NS_MAIN, + 'title' => 'Page', + 'params' => [ + '4::ids' => [ '1', '3', '4' ], + '5::ofield' => '1', + '6::nfield' => '2', + ], + ], + [ + 'text' => 'User changed visibility of 3 log events on Page: edit summary hidden ' + . 'and content unhidden', + 'api' => [ + 'type' => 'logging', + 'ids' => [ '1', '3', '4' ], + 'old' => [ + 'bitmask' => 1, + 'content' => true, + 'comment' => false, + 'user' => false, + 'restricted' => false, + ], + 'new' => [ + 'bitmask' => 2, + 'content' => false, + 'comment' => true, + 'user' => false, + 'restricted' => false, + ], + ], + ], + ], + + // Legacy format + [ + [ + 'type' => 'delete', + 'action' => 'event', + 'comment' => 'delete comment', + 'namespace' => NS_MAIN, + 'title' => 'Page', + 'params' => [ + '1,3,4', + 'ofield=1', + 'nfield=2', + ], + ], + [ + 'legacy' => true, + 'text' => 'User changed visibility of 3 log events on Page: edit summary hidden ' + . 'and content unhidden', + 'api' => [ + 'type' => 'logging', + 'ids' => [ '1', '3', '4' ], + 'old' => [ + 'bitmask' => 1, + 'content' => true, + 'comment' => false, + 'user' => false, + 'restricted' => false, + ], + 'new' => [ + 'bitmask' => 2, + 'content' => false, + 'comment' => true, + 'user' => false, + 'restricted' => false, + ], + ], + ], + ], + ]; + } + + /** + * @dataProvider provideEventLogDatabaseRows + */ + public function testEventLogDatabaseRows( $row, $extra ) { + $this->doTestLogFormatter( $row, $extra ); + } + + /** + * Provide different rows from the logging table to test + * for backward compatibility. + * Do not change the existing data, just add a new database row + */ + public static function provideSuppressRevisionLogDatabaseRows() { + return [ + // Current format + [ + [ + 'type' => 'suppress', + 'action' => 'revision', + 'comment' => 'Suppress comment', + 'namespace' => NS_MAIN, + 'title' => 'Page', + 'params' => [ + '4::type' => 'archive', + '5::ids' => [ '1', '3', '4' ], + '6::ofield' => '1', + '7::nfield' => '10', + ], + ], + [ + 'text' => 'User secretly changed visibility of 3 revisions on page Page: edit ' + . 'summary hidden, content unhidden and applied restrictions to administrators', + 'api' => [ + 'type' => 'archive', + 'ids' => [ '1', '3', '4' ], + 'old' => [ + 'bitmask' => 1, + 'content' => true, + 'comment' => false, + 'user' => false, + 'restricted' => false, + ], + 'new' => [ + 'bitmask' => 10, + 'content' => false, + 'comment' => true, + 'user' => false, + 'restricted' => true, + ], + ], + ], + ], + + // Legacy format + [ + [ + 'type' => 'suppress', + 'action' => 'revision', + 'comment' => 'Suppress comment', + 'namespace' => NS_MAIN, + 'title' => 'Page', + 'params' => [ + 'archive', + '1,3,4', + 'ofield=1', + 'nfield=10', + ], + ], + [ + 'legacy' => true, + 'text' => 'User secretly changed visibility of 3 revisions on page Page: edit ' + . 'summary hidden, content unhidden and applied restrictions to administrators', + 'api' => [ + 'type' => 'archive', + 'ids' => [ '1', '3', '4' ], + 'old' => [ + 'bitmask' => 1, + 'content' => true, + 'comment' => false, + 'user' => false, + 'restricted' => false, + ], + 'new' => [ + 'bitmask' => 10, + 'content' => false, + 'comment' => true, + 'user' => false, + 'restricted' => true, + ], + ], + ], + ], + ]; + } + + /** + * @dataProvider provideSuppressRevisionLogDatabaseRows + */ + public function testSuppressRevisionLogDatabaseRows( $row, $extra ) { + $this->doTestLogFormatter( $row, $extra ); + } + + /** + * Provide different rows from the logging table to test + * for backward compatibility. + * Do not change the existing data, just add a new database row + */ + public static function provideSuppressEventLogDatabaseRows() { + return [ + // Current format + [ + [ + 'type' => 'suppress', + 'action' => 'event', + 'comment' => 'Suppress comment', + 'namespace' => NS_MAIN, + 'title' => 'Page', + 'params' => [ + '4::ids' => [ '1', '3', '4' ], + '5::ofield' => '1', + '6::nfield' => '10', + ], + ], + [ + 'text' => 'User secretly changed visibility of 3 log events on Page: edit ' + . 'summary hidden, content unhidden and applied restrictions to administrators', + 'api' => [ + 'type' => 'logging', + 'ids' => [ '1', '3', '4' ], + 'old' => [ + 'bitmask' => 1, + 'content' => true, + 'comment' => false, + 'user' => false, + 'restricted' => false, + ], + 'new' => [ + 'bitmask' => 10, + 'content' => false, + 'comment' => true, + 'user' => false, + 'restricted' => true, + ], + ], + ], + ], + + // Legacy format + [ + [ + 'type' => 'suppress', + 'action' => 'event', + 'comment' => 'Suppress comment', + 'namespace' => NS_MAIN, + 'title' => 'Page', + 'params' => [ + '1,3,4', + 'ofield=1', + 'nfield=10', + ], + ], + [ + 'legacy' => true, + 'text' => 'User secretly changed visibility of 3 log events on Page: edit ' + . 'summary hidden, content unhidden and applied restrictions to administrators', + 'api' => [ + 'type' => 'logging', + 'ids' => [ '1', '3', '4' ], + 'old' => [ + 'bitmask' => 1, + 'content' => true, + 'comment' => false, + 'user' => false, + 'restricted' => false, + ], + 'new' => [ + 'bitmask' => 10, + 'content' => false, + 'comment' => true, + 'user' => false, + 'restricted' => true, + ], + ], + ], + ], + ]; + } + + /** + * @dataProvider provideSuppressEventLogDatabaseRows + */ + public function testSuppressEventLogDatabaseRows( $row, $extra ) { + $this->doTestLogFormatter( $row, $extra ); + } + + /** + * Provide different rows from the logging table to test + * for backward compatibility. + * Do not change the existing data, just add a new database row + */ + public static function provideSuppressDeleteLogDatabaseRows() { + return [ + // Current format + [ + [ + 'type' => 'suppress', + 'action' => 'delete', + 'comment' => 'delete comment', + 'namespace' => NS_MAIN, + 'title' => 'Page', + 'params' => [], + ], + [ + 'text' => 'User suppressed page Page', + 'api' => [], + ], + ], + + // Legacy format + [ + [ + 'type' => 'suppress', + 'action' => 'delete', + 'comment' => 'delete comment', + 'namespace' => NS_MAIN, + 'title' => 'Page', + 'params' => [], + ], + [ + 'legacy' => true, + 'text' => 'User suppressed page Page', + 'api' => [], + ], + ], + ]; + } + + /** + * @dataProvider provideSuppressDeleteLogDatabaseRows + */ + public function testSuppressDeleteLogDatabaseRows( $row, $extra ) { + $this->doTestLogFormatter( $row, $extra ); + } +} diff --git a/www/wiki/tests/phpunit/includes/logging/ImportLogFormatterTest.php b/www/wiki/tests/phpunit/includes/logging/ImportLogFormatterTest.php new file mode 100644 index 00000000..80e4c0bc --- /dev/null +++ b/www/wiki/tests/phpunit/includes/logging/ImportLogFormatterTest.php @@ -0,0 +1,126 @@ +<?php + +/** + * @covers ImportLogFormatter + */ +class ImportLogFormatterTest extends LogFormatterTestCase { + + /** + * Provide different rows from the logging table to test + * for backward compatibility. + * Do not change the existing data, just add a new database row + */ + public static function provideUploadLogDatabaseRows() { + return [ + // Current format + [ + [ + 'type' => 'import', + 'action' => 'upload', + 'comment' => 'upload comment', + 'namespace' => NS_MAIN, + 'title' => 'ImportPage', + 'params' => [ + '4:number:count' => '1', + ], + ], + [ + 'text' => 'User imported ImportPage by file upload (1 revision)', + 'api' => [ + 'count' => 1, + ], + ], + ], + + // old format - without details + [ + [ + 'type' => 'import', + 'action' => 'upload', + 'comment' => '1 revision: import comment', + 'namespace' => NS_MAIN, + 'title' => 'ImportPage', + 'params' => [], + ], + [ + 'text' => 'User imported ImportPage by file upload', + 'api' => [], + ], + ], + ]; + } + + /** + * @dataProvider provideUploadLogDatabaseRows + */ + public function testUploadLogDatabaseRows( $row, $extra ) { + $this->doTestLogFormatter( $row, $extra ); + } + + /** + * Provide different rows from the logging table to test + * for backward compatibility. + * Do not change the existing data, just add a new database row + */ + public static function provideInterwikiLogDatabaseRows() { + return [ + // Current format + [ + [ + 'type' => 'import', + 'action' => 'interwiki', + 'comment' => 'interwiki comment', + 'namespace' => NS_MAIN, + 'title' => 'ImportPage', + 'params' => [ + '4:number:count' => '1', + '5:title-link:interwiki' => 'importiw:PageImport', + ], + ], + [ + 'text' => 'User imported ImportPage from importiw:PageImport (1 revision)', + 'api' => [ + 'count' => 1, + 'interwiki_ns' => 0, + 'interwiki_title' => 'importiw:PageImport', + ], + ], + ], + + // old format - without details + [ + [ + 'type' => 'import', + 'action' => 'interwiki', + 'comment' => '1 revision from importiw:PageImport: interwiki comment', + 'namespace' => NS_MAIN, + 'title' => 'ImportPage', + 'params' => [], + ], + [ + 'text' => 'User imported ImportPage from another wiki', + 'api' => [], + ], + ], + ]; + } + + /** + * @dataProvider provideInterwikiLogDatabaseRows + */ + public function testInterwikiLogDatabaseRows( $row, $extra ) { + // Setup importiw: as interwiki prefix + $this->setMwGlobals( 'wgHooks', [ + 'InterwikiLoadPrefix' => [ + function ( $prefix, &$data ) { + if ( $prefix == 'importiw' ) { + $data = [ 'iw_url' => 'wikipedia' ]; + } + return false; + } + ] + ] ); + + $this->doTestLogFormatter( $row, $extra ); + } +} diff --git a/www/wiki/tests/phpunit/includes/logging/LogFormatterTest.php b/www/wiki/tests/phpunit/includes/logging/LogFormatterTest.php new file mode 100644 index 00000000..e523a31e --- /dev/null +++ b/www/wiki/tests/phpunit/includes/logging/LogFormatterTest.php @@ -0,0 +1,664 @@ +<?php + +/** + * @group Database + */ +class LogFormatterTest extends MediaWikiLangTestCase { + private static $oldExtMsgFiles; + + /** + * @var User + */ + protected $user; + + /** + * @var Title + */ + protected $title; + + /** + * @var RequestContext + */ + protected $context; + + /** + * @var Title + */ + protected $target; + + /** + * @var string + */ + protected $user_comment; + + public static function setUpBeforeClass() { + parent::setUpBeforeClass(); + + global $wgExtensionMessagesFiles; + self::$oldExtMsgFiles = $wgExtensionMessagesFiles; + $wgExtensionMessagesFiles['LogTests'] = __DIR__ . '/LogTests.i18n.php'; + Language::getLocalisationCache()->recache( 'en' ); + } + + public static function tearDownAfterClass() { + global $wgExtensionMessagesFiles; + $wgExtensionMessagesFiles = self::$oldExtMsgFiles; + Language::getLocalisationCache()->recache( 'en' ); + + parent::tearDownAfterClass(); + } + + protected function setUp() { + parent::setUp(); + + $this->setMwGlobals( [ + 'wgLogTypes' => [ 'phpunit' ], + 'wgLogActionsHandlers' => [ 'phpunit/test' => LogFormatter::class, + 'phpunit/param' => LogFormatter::class ], + 'wgUser' => User::newFromName( 'Testuser' ), + ] ); + + $this->user = User::newFromName( 'Testuser' ); + $this->title = Title::newFromText( 'SomeTitle' ); + $this->target = Title::newFromText( 'TestTarget' ); + + $this->context = new RequestContext(); + $this->context->setUser( $this->user ); + $this->context->setTitle( $this->title ); + $this->context->setLanguage( RequestContext::getMain()->getLanguage() ); + + $this->user_comment = '<User comment about action>'; + } + + public function newLogEntry( $action, $params ) { + $logEntry = new ManualLogEntry( 'phpunit', $action ); + $logEntry->setPerformer( $this->user ); + $logEntry->setTarget( $this->title ); + $logEntry->setComment( 'A very good reason' ); + + $logEntry->setParameters( $params ); + + return $logEntry; + } + + /** + * @covers LogFormatter::newFromEntry + */ + public function testNormalLogParams() { + $entry = $this->newLogEntry( 'test', [] ); + $formatter = LogFormatter::newFromEntry( $entry ); + $formatter->setContext( $this->context ); + + $formatter->setShowUserToolLinks( false ); + $paramsWithoutTools = $formatter->getMessageParametersForTesting(); + unset( $formatter->parsedParameters ); + + $formatter->setShowUserToolLinks( true ); + $paramsWithTools = $formatter->getMessageParametersForTesting(); + + $userLink = Linker::userLink( + $this->user->getId(), + $this->user->getName() + ); + + $userTools = Linker::userToolLinksRedContribs( + $this->user->getId(), + $this->user->getName(), + $this->user->getEditCount() + ); + + $titleLink = Linker::link( $this->title, null, [], [] ); + + // $paramsWithoutTools and $paramsWithTools should be only different + // in index 0 + $this->assertEquals( $paramsWithoutTools[1], $paramsWithTools[1] ); + $this->assertEquals( $paramsWithoutTools[2], $paramsWithTools[2] ); + + $this->assertEquals( $userLink, $paramsWithoutTools[0]['raw'] ); + $this->assertEquals( $userLink . $userTools, $paramsWithTools[0]['raw'] ); + + $this->assertEquals( $this->user->getName(), $paramsWithoutTools[1] ); + + $this->assertEquals( $titleLink, $paramsWithoutTools[2]['raw'] ); + } + + /** + * @covers LogFormatter::newFromEntry + * @covers LogFormatter::getActionText + */ + public function testLogParamsTypeRaw() { + $params = [ '4:raw:raw' => Linker::link( $this->title, null, [], [] ) ]; + $expected = Linker::link( $this->title, null, [], [] ); + + $entry = $this->newLogEntry( 'param', $params ); + $formatter = LogFormatter::newFromEntry( $entry ); + $formatter->setContext( $this->context ); + + $logParam = $formatter->getActionText(); + + $this->assertEquals( $expected, $logParam ); + } + + /** + * @covers LogFormatter::newFromEntry + * @covers LogFormatter::getActionText + */ + public function testLogParamsTypeMsg() { + $params = [ '4:msg:msg' => 'log-description-phpunit' ]; + $expected = wfMessage( 'log-description-phpunit' )->text(); + + $entry = $this->newLogEntry( 'param', $params ); + $formatter = LogFormatter::newFromEntry( $entry ); + $formatter->setContext( $this->context ); + + $logParam = $formatter->getActionText(); + + $this->assertEquals( $expected, $logParam ); + } + + /** + * @covers LogFormatter::newFromEntry + * @covers LogFormatter::getActionText + */ + public function testLogParamsTypeMsgContent() { + $params = [ '4:msg-content:msgContent' => 'log-description-phpunit' ]; + $expected = wfMessage( 'log-description-phpunit' )->inContentLanguage()->text(); + + $entry = $this->newLogEntry( 'param', $params ); + $formatter = LogFormatter::newFromEntry( $entry ); + $formatter->setContext( $this->context ); + + $logParam = $formatter->getActionText(); + + $this->assertEquals( $expected, $logParam ); + } + + /** + * @covers LogFormatter::newFromEntry + * @covers LogFormatter::getActionText + */ + public function testLogParamsTypeNumber() { + global $wgLang; + + $params = [ '4:number:number' => 123456789 ]; + $expected = $wgLang->formatNum( 123456789 ); + + $entry = $this->newLogEntry( 'param', $params ); + $formatter = LogFormatter::newFromEntry( $entry ); + $formatter->setContext( $this->context ); + + $logParam = $formatter->getActionText(); + + $this->assertEquals( $expected, $logParam ); + } + + /** + * @covers LogFormatter::newFromEntry + * @covers LogFormatter::getActionText + */ + public function testLogParamsTypeUserLink() { + $params = [ '4:user-link:userLink' => $this->user->getName() ]; + $expected = Linker::userLink( + $this->user->getId(), + $this->user->getName() + ); + + $entry = $this->newLogEntry( 'param', $params ); + $formatter = LogFormatter::newFromEntry( $entry ); + $formatter->setContext( $this->context ); + + $logParam = $formatter->getActionText(); + + $this->assertEquals( $expected, $logParam ); + } + + /** + * @covers LogFormatter::newFromEntry + * @covers LogFormatter::getActionText + */ + public function testLogParamsTypeTitleLink() { + $params = [ '4:title-link:titleLink' => $this->title->getText() ]; + $expected = Linker::link( $this->title, null, [], [] ); + + $entry = $this->newLogEntry( 'param', $params ); + $formatter = LogFormatter::newFromEntry( $entry ); + $formatter->setContext( $this->context ); + + $logParam = $formatter->getActionText(); + + $this->assertEquals( $expected, $logParam ); + } + + /** + * @covers LogFormatter::newFromEntry + * @covers LogFormatter::getActionText + */ + public function testLogParamsTypePlain() { + $params = [ '4:plain:plain' => 'Some plain text' ]; + $expected = 'Some plain text'; + + $entry = $this->newLogEntry( 'param', $params ); + $formatter = LogFormatter::newFromEntry( $entry ); + $formatter->setContext( $this->context ); + + $logParam = $formatter->getActionText(); + + $this->assertEquals( $expected, $logParam ); + } + + /** + * @covers LogFormatter::newFromEntry + * @covers LogFormatter::getComment + */ + public function testLogComment() { + $entry = $this->newLogEntry( 'test', [] ); + $formatter = LogFormatter::newFromEntry( $entry ); + $formatter->setContext( $this->context ); + + $comment = ltrim( Linker::commentBlock( $entry->getComment() ) ); + + $this->assertEquals( $comment, $formatter->getComment() ); + } + + /** + * @dataProvider provideApiParamFormatting + * @covers LogFormatter::formatParametersForApi + * @covers LogFormatter::formatParameterValueForApi + */ + public function testApiParamFormatting( $key, $value, $expected ) { + $entry = $this->newLogEntry( 'param', [ $key => $value ] ); + $formatter = LogFormatter::newFromEntry( $entry ); + $formatter->setContext( $this->context ); + + ApiResult::setIndexedTagName( $expected, 'param' ); + ApiResult::setArrayType( $expected, 'assoc' ); + + $this->assertEquals( $expected, $formatter->formatParametersForApi() ); + } + + public static function provideApiParamFormatting() { + return [ + [ 0, 'value', [ 'value' ] ], + [ 'named', 'value', [ 'named' => 'value' ] ], + [ '::key', 'value', [ 'key' => 'value' ] ], + [ '4::key', 'value', [ 'key' => 'value' ] ], + [ '4:raw:key', 'value', [ 'key' => 'value' ] ], + [ '4:plain:key', 'value', [ 'key' => 'value' ] ], + [ '4:bool:key', '1', [ 'key' => true ] ], + [ '4:bool:key', '0', [ 'key' => false ] ], + [ '4:number:key', '123', [ 'key' => 123 ] ], + [ '4:number:key', '123.5', [ 'key' => 123.5 ] ], + [ '4:array:key', [], [ 'key' => [ ApiResult::META_TYPE => 'array' ] ] ], + [ '4:assoc:key', [], [ 'key' => [ ApiResult::META_TYPE => 'assoc' ] ] ], + [ '4:kvp:key', [], [ 'key' => [ ApiResult::META_TYPE => 'kvp' ] ] ], + [ '4:timestamp:key', '20150102030405', [ 'key' => '2015-01-02T03:04:05Z' ] ], + [ '4:msg:key', 'parentheses', [ + 'key_key' => 'parentheses', + 'key_text' => wfMessage( 'parentheses' )->text(), + ] ], + [ '4:msg-content:key', 'parentheses', [ + 'key_key' => 'parentheses', + 'key_text' => wfMessage( 'parentheses' )->inContentLanguage()->text(), + ] ], + [ '4:title:key', 'project:foo', [ + 'key_ns' => NS_PROJECT, + 'key_title' => Title::newFromText( 'project:foo' )->getFullText(), + ] ], + [ '4:title-link:key', 'project:foo', [ + 'key_ns' => NS_PROJECT, + 'key_title' => Title::newFromText( 'project:foo' )->getFullText(), + ] ], + [ '4:title-link:key', '<invalid>', [ + 'key_ns' => NS_SPECIAL, + 'key_title' => SpecialPage::getTitleFor( 'Badtitle', '<invalid>' )->getFullText(), + ] ], + [ '4:user:key', 'foo', [ 'key' => 'Foo' ] ], + [ '4:user-link:key', 'foo', [ 'key' => 'Foo' ] ], + ]; + } + + /** + * The testIrcMsgForAction* tests are supposed to cover the hacky + * LogFormatter::getIRCActionText / T36508 + * + * Third parties bots listen to those messages. They are clever enough + * to fetch the i18n messages from the wiki and then analyze the IRC feed + * to reverse engineer the $1, $2 messages. + * One thing bots can not detect is when MediaWiki change the meaning of + * a message like what happened when we deployed 1.19. $1 became the user + * performing the action which broke basically all bots around. + * + * Should cover the following log actions (which are most commonly used by bots): + * - block/block + * - block/unblock + * - block/reblock + * - delete/delete + * - delete/restore + * - newusers/create + * - newusers/create2 + * - newusers/autocreate + * - move/move + * - move/move_redir + * - protect/protect + * - protect/modifyprotect + * - protect/unprotect + * - protect/move_prot + * - upload/upload + * - merge/merge + * - import/upload + * - import/interwiki + * + * As well as the following Auto Edit Summaries: + * - blank + * - replace + * - rollback + * - undo + */ + + /** + * @covers LogFormatter::getIRCActionComment + * @covers LogFormatter::getIRCActionText + */ + public function testIrcMsgForLogTypeBlock() { + $sep = $this->context->msg( 'colon-separator' )->text(); + + # block/block + $this->assertIRCComment( + $this->context->msg( 'blocklogentry', 'SomeTitle', 'duration', '(flags)' )->plain() + . $sep . $this->user_comment, + 'block', 'block', + [ + '5::duration' => 'duration', + '6::flags' => 'flags', + ], + $this->user_comment + ); + # block/block - legacy + $this->assertIRCComment( + $this->context->msg( 'blocklogentry', 'SomeTitle', 'duration', '(flags)' )->plain() + . $sep . $this->user_comment, + 'block', 'block', + [ + 'duration', + 'flags', + ], + $this->user_comment, + '', + true + ); + # block/unblock + $this->assertIRCComment( + $this->context->msg( 'unblocklogentry', 'SomeTitle' )->plain() . $sep . $this->user_comment, + 'block', 'unblock', + [], + $this->user_comment + ); + # block/reblock + $this->assertIRCComment( + $this->context->msg( 'reblock-logentry', 'SomeTitle', 'duration', '(flags)' )->plain() + . $sep . $this->user_comment, + 'block', 'reblock', + [ + '5::duration' => 'duration', + '6::flags' => 'flags', + ], + $this->user_comment + ); + } + + /** + * @covers LogFormatter::getIRCActionComment + * @covers LogFormatter::getIRCActionText + */ + public function testIrcMsgForLogTypeDelete() { + $sep = $this->context->msg( 'colon-separator' )->text(); + + # delete/delete + $this->assertIRCComment( + $this->context->msg( 'deletedarticle', 'SomeTitle' )->plain() . $sep . $this->user_comment, + 'delete', 'delete', + [], + $this->user_comment + ); + + # delete/restore + $this->assertIRCComment( + $this->context->msg( 'undeletedarticle', 'SomeTitle' )->plain() . $sep . $this->user_comment, + 'delete', 'restore', + [], + $this->user_comment + ); + } + + /** + * @covers LogFormatter::getIRCActionComment + * @covers LogFormatter::getIRCActionText + */ + public function testIrcMsgForLogTypeNewusers() { + $this->assertIRCComment( + 'New user account', + 'newusers', 'newusers', + [] + ); + $this->assertIRCComment( + 'New user account', + 'newusers', 'create', + [] + ); + $this->assertIRCComment( + 'created new account SomeTitle', + 'newusers', 'create2', + [] + ); + $this->assertIRCComment( + 'Account created automatically', + 'newusers', 'autocreate', + [] + ); + } + + /** + * @covers LogFormatter::getIRCActionComment + * @covers LogFormatter::getIRCActionText + */ + public function testIrcMsgForLogTypeMove() { + $move_params = [ + '4::target' => $this->target->getPrefixedText(), + '5::noredir' => 0, + ]; + $sep = $this->context->msg( 'colon-separator' )->text(); + + # move/move + $this->assertIRCComment( + $this->context->msg( '1movedto2', 'SomeTitle', 'TestTarget' ) + ->plain() . $sep . $this->user_comment, + 'move', 'move', + $move_params, + $this->user_comment + ); + + # move/move_redir + $this->assertIRCComment( + $this->context->msg( '1movedto2_redir', 'SomeTitle', 'TestTarget' ) + ->plain() . $sep . $this->user_comment, + 'move', 'move_redir', + $move_params, + $this->user_comment + ); + } + + /** + * @covers LogFormatter::getIRCActionComment + * @covers LogFormatter::getIRCActionText + */ + public function testIrcMsgForLogTypePatrol() { + # patrol/patrol + $this->assertIRCComment( + $this->context->msg( 'patrol-log-line', 'revision 777', '[[SomeTitle]]', '' )->plain(), + 'patrol', 'patrol', + [ + '4::curid' => '777', + '5::previd' => '666', + '6::auto' => 0, + ] + ); + } + + /** + * @covers LogFormatter::getIRCActionComment + * @covers LogFormatter::getIRCActionText + */ + public function testIrcMsgForLogTypeProtect() { + $protectParams = [ + '4::description' => '[edit=sysop] (indefinite) ‎[move=sysop] (indefinite)' + ]; + $sep = $this->context->msg( 'colon-separator' )->text(); + + # protect/protect + $this->assertIRCComment( + $this->context->msg( 'protectedarticle', 'SomeTitle ' . $protectParams['4::description'] ) + ->plain() . $sep . $this->user_comment, + 'protect', 'protect', + $protectParams, + $this->user_comment + ); + + # protect/unprotect + $this->assertIRCComment( + $this->context->msg( 'unprotectedarticle', 'SomeTitle' )->plain() . $sep . $this->user_comment, + 'protect', 'unprotect', + [], + $this->user_comment + ); + + # protect/modify + $this->assertIRCComment( + $this->context->msg( + 'modifiedarticleprotection', + 'SomeTitle ' . $protectParams['4::description'] + )->plain() . $sep . $this->user_comment, + 'protect', 'modify', + $protectParams, + $this->user_comment + ); + + # protect/move_prot + $this->assertIRCComment( + $this->context->msg( 'movedarticleprotection', 'SomeTitle', 'OldTitle' ) + ->plain() . $sep . $this->user_comment, + 'protect', 'move_prot', + [ + '4::oldtitle' => 'OldTitle' + ], + $this->user_comment + ); + } + + /** + * @covers LogFormatter::getIRCActionComment + * @covers LogFormatter::getIRCActionText + */ + public function testIrcMsgForLogTypeUpload() { + $sep = $this->context->msg( 'colon-separator' )->text(); + + # upload/upload + $this->assertIRCComment( + $this->context->msg( 'uploadedimage', 'SomeTitle' )->plain() . $sep . $this->user_comment, + 'upload', 'upload', + [], + $this->user_comment + ); + + # upload/overwrite + $this->assertIRCComment( + $this->context->msg( 'overwroteimage', 'SomeTitle' )->plain() . $sep . $this->user_comment, + 'upload', 'overwrite', + [], + $this->user_comment + ); + } + + /** + * @covers LogFormatter::getIRCActionComment + * @covers LogFormatter::getIRCActionText + */ + public function testIrcMsgForLogTypeMerge() { + $sep = $this->context->msg( 'colon-separator' )->text(); + + # merge/merge + $this->assertIRCComment( + $this->context->msg( 'pagemerge-logentry', 'SomeTitle', 'Dest', 'timestamp' )->plain() + . $sep . $this->user_comment, + 'merge', 'merge', + [ + '4::dest' => 'Dest', + '5::mergepoint' => 'timestamp', + ], + $this->user_comment + ); + } + + /** + * @covers LogFormatter::getIRCActionComment + * @covers LogFormatter::getIRCActionText + */ + public function testIrcMsgForLogTypeImport() { + $sep = $this->context->msg( 'colon-separator' )->text(); + + # import/upload + $msg = $this->context->msg( 'import-logentry-upload', 'SomeTitle' )->plain() . + $sep . + $this->user_comment; + $this->assertIRCComment( + $msg, + 'import', 'upload', + [], + $this->user_comment + ); + + # import/interwiki + $msg = $this->context->msg( 'import-logentry-interwiki', 'SomeTitle' )->plain() . + $sep . + $this->user_comment; + $this->assertIRCComment( + $msg, + 'import', 'interwiki', + [], + $this->user_comment + ); + } + + /** + * @param string $expected Expected IRC text without colors codes + * @param string $type Log type (move, delete, suppress, patrol ...) + * @param string $action A log type action + * @param array $params + * @param string $comment (optional) A comment for the log action + * @param string $msg (optional) A message for PHPUnit :-) + */ + protected function assertIRCComment( $expected, $type, $action, $params, + $comment = null, $msg = '', $legacy = false + ) { + $logEntry = new ManualLogEntry( $type, $action ); + $logEntry->setPerformer( $this->user ); + $logEntry->setTarget( $this->title ); + if ( $comment !== null ) { + $logEntry->setComment( $comment ); + } + $logEntry->setParameters( $params ); + $logEntry->setLegacy( $legacy ); + + $formatter = LogFormatter::newFromEntry( $logEntry ); + $formatter->setContext( $this->context ); + + // Apply the same transformation as done in IRCColourfulRCFeedFormatter::getLine for rc_comment + $ircRcComment = IRCColourfulRCFeedFormatter::cleanupForIRC( $formatter->getIRCActionComment() ); + + $this->assertEquals( + $expected, + $ircRcComment, + $msg + ); + } + +} diff --git a/www/wiki/tests/phpunit/includes/logging/LogFormatterTestCase.php b/www/wiki/tests/phpunit/includes/logging/LogFormatterTestCase.php new file mode 100644 index 00000000..786d7619 --- /dev/null +++ b/www/wiki/tests/phpunit/includes/logging/LogFormatterTestCase.php @@ -0,0 +1,68 @@ +<?php + +/** + * @since 1.26 + */ +abstract class LogFormatterTestCase extends MediaWikiLangTestCase { + + public function doTestLogFormatter( $row, $extra ) { + RequestContext::resetMain(); + $row = $this->expandDatabaseRow( $row, $this->isLegacy( $extra ) ); + + $formatter = LogFormatter::newFromRow( $row ); + + $this->assertEquals( + $extra['text'], + self::removeSomeHtml( $formatter->getActionText() ), + 'Action text is equal to expected text' + ); + + $this->assertSame( // ensure types and array key order + $extra['api'], + self::removeApiMetaData( $formatter->formatParametersForApi() ), + 'Api log params is equal to expected array' + ); + } + + protected function isLegacy( $extra ) { + return isset( $extra['legacy'] ) && $extra['legacy']; + } + + protected function expandDatabaseRow( $data, $legacy ) { + return [ + // no log_id because no insert in database + 'log_type' => $data['type'], + 'log_action' => $data['action'], + 'log_timestamp' => isset( $data['timestamp'] ) ? $data['timestamp'] : wfTimestampNow(), + 'log_user' => isset( $data['user'] ) ? $data['user'] : 0, + 'log_user_text' => isset( $data['user_text'] ) ? $data['user_text'] : 'User', + 'log_actor' => isset( $data['actor'] ) ? $data['actor'] : 0, + 'log_namespace' => isset( $data['namespace'] ) ? $data['namespace'] : NS_MAIN, + 'log_title' => isset( $data['title'] ) ? $data['title'] : 'Main_Page', + 'log_page' => isset( $data['page'] ) ? $data['page'] : 0, + 'log_comment_text' => isset( $data['comment'] ) ? $data['comment'] : '', + 'log_comment_data' => null, + 'log_params' => $legacy + ? LogPage::makeParamBlob( $data['params'] ) + : LogEntryBase::makeParamBlob( $data['params'] ), + 'log_deleted' => isset( $data['deleted'] ) ? $data['deleted'] : 0, + ]; + } + + private static function removeSomeHtml( $html ) { + $html = str_replace( '"', '"', $html ); + $html = preg_replace( '/\xE2\x80[\x8E\x8F]/', '', $html ); // Strip lrm/rlm + return trim( strip_tags( $html ) ); + } + + private static function removeApiMetaData( $val ) { + if ( is_array( $val ) ) { + unset( $val['_element'] ); + unset( $val['_type'] ); + foreach ( $val as $key => $value ) { + $val[$key] = self::removeApiMetaData( $value ); + } + } + return $val; + } +} diff --git a/www/wiki/tests/phpunit/includes/logging/LogTests.i18n.php b/www/wiki/tests/phpunit/includes/logging/LogTests.i18n.php new file mode 100644 index 00000000..23e62b53 --- /dev/null +++ b/www/wiki/tests/phpunit/includes/logging/LogTests.i18n.php @@ -0,0 +1,15 @@ +<?php +/** + * Internationalisation file for log tests. + * + * @file + */ + +$messages = []; + +$messages['en'] = [ + 'log-name-phpunit' => 'PHPUnit-log', + 'log-description-phpunit' => 'Log for PHPUnit-tests', + 'logentry-phpunit-test' => '$1 {{GENDER:$2|tests}} with page $3', + 'logentry-phpunit-param' => '$4', +]; diff --git a/www/wiki/tests/phpunit/includes/logging/MergeLogFormatterTest.php b/www/wiki/tests/phpunit/includes/logging/MergeLogFormatterTest.php new file mode 100644 index 00000000..1978f1b5 --- /dev/null +++ b/www/wiki/tests/phpunit/includes/logging/MergeLogFormatterTest.php @@ -0,0 +1,70 @@ +<?php + +/** + * @covers MergeLogFormatter + */ +class MergeLogFormatterTest extends LogFormatterTestCase { + + /** + * Provide different rows from the logging table to test + * for backward compatibility. + * Do not change the existing data, just add a new database row + */ + public static function provideMergeLogDatabaseRows() { + return [ + // Current format + [ + [ + 'type' => 'merge', + 'action' => 'merge', + 'comment' => 'Merge comment', + 'namespace' => NS_MAIN, + 'title' => 'OldPage', + 'params' => [ + '4::dest' => 'NewPage', + '5::mergepoint' => '20140804160710', + ], + ], + [ + 'text' => 'User merged OldPage into NewPage (revisions up to 16:07, 4 August 2014)', + 'api' => [ + 'dest_ns' => 0, + 'dest_title' => 'NewPage', + 'mergepoint' => '2014-08-04T16:07:10Z', + ], + ], + ], + + // Legacy format + [ + [ + 'type' => 'merge', + 'action' => 'merge', + 'comment' => 'merge comment', + 'namespace' => NS_MAIN, + 'title' => 'OldPage', + 'params' => [ + 'NewPage', + '20140804160710', + ], + ], + [ + 'legacy' => true, + 'text' => 'User merged OldPage into NewPage (revisions up to 16:07, 4 August 2014)', + 'api' => [ + 'dest_ns' => 0, + 'dest_title' => 'NewPage', + 'mergepoint' => '2014-08-04T16:07:10Z', + ], + ], + ], + ]; + } + + /** + * @dataProvider provideMergeLogDatabaseRows + */ + public function testMergeLogDatabaseRows( $row, $extra ) { + $this->doTestLogFormatter( $row, $extra ); + } +} diff --git a/www/wiki/tests/phpunit/includes/logging/MoveLogFormatterTest.php b/www/wiki/tests/phpunit/includes/logging/MoveLogFormatterTest.php new file mode 100644 index 00000000..ebda46b2 --- /dev/null +++ b/www/wiki/tests/phpunit/includes/logging/MoveLogFormatterTest.php @@ -0,0 +1,273 @@ +<?php + +/** + * @covers MoveLogFormatter + */ +class MoveLogFormatterTest extends LogFormatterTestCase { + + /** + * Provide different rows from the logging table to test + * for backward compatibility. + * Do not change the existing data, just add a new database row + */ + public static function provideMoveLogDatabaseRows() { + return [ + // Current format - with redirect + [ + [ + 'type' => 'move', + 'action' => 'move', + 'comment' => 'move comment with redirect', + 'namespace' => NS_MAIN, + 'title' => 'OldPage', + 'params' => [ + '4::target' => 'NewPage', + '5::noredir' => '0', + ], + ], + [ + 'text' => 'User moved page OldPage to NewPage', + 'api' => [ + 'target_ns' => 0, + 'target_title' => 'NewPage', + 'suppressredirect' => false, + ], + ], + ], + + // Current format - without redirect + [ + [ + 'type' => 'move', + 'action' => 'move', + 'comment' => 'move comment', + 'namespace' => NS_MAIN, + 'title' => 'OldPage', + 'params' => [ + '4::target' => 'NewPage', + '5::noredir' => '1', + ], + ], + [ + 'text' => 'User moved page OldPage to NewPage without leaving a redirect', + 'api' => [ + 'target_ns' => 0, + 'target_title' => 'NewPage', + 'suppressredirect' => true, + ], + ], + ], + + // legacy format - with redirect + [ + [ + 'type' => 'move', + 'action' => 'move', + 'comment' => 'move comment', + 'namespace' => NS_MAIN, + 'title' => 'OldPage', + 'params' => [ + 'NewPage', + '', + ], + ], + [ + 'legacy' => true, + 'text' => 'User moved page OldPage to NewPage', + 'api' => [ + 'target_ns' => 0, + 'target_title' => 'NewPage', + 'suppressredirect' => false, + ], + ], + ], + + // legacy format - without redirect + [ + [ + 'type' => 'move', + 'action' => 'move', + 'comment' => 'move comment', + 'namespace' => NS_MAIN, + 'title' => 'OldPage', + 'params' => [ + 'NewPage', + '1', + ], + ], + [ + 'legacy' => true, + 'text' => 'User moved page OldPage to NewPage without leaving a redirect', + 'api' => [ + 'target_ns' => 0, + 'target_title' => 'NewPage', + 'suppressredirect' => true, + ], + ], + ], + + // old format without flag for redirect suppression + [ + [ + 'type' => 'move', + 'action' => 'move', + 'comment' => 'move comment', + 'namespace' => NS_MAIN, + 'title' => 'OldPage', + 'params' => [ + 'NewPage', + ], + ], + [ + 'legacy' => true, + 'text' => 'User moved page OldPage to NewPage', + 'api' => [ + 'target_ns' => 0, + 'target_title' => 'NewPage', + 'suppressredirect' => false, + ], + ], + ], + ]; + } + + /** + * @dataProvider provideMoveLogDatabaseRows + */ + public function testMoveLogDatabaseRows( $row, $extra ) { + $this->doTestLogFormatter( $row, $extra ); + } + + /** + * Provide different rows from the logging table to test + * for backward compatibility. + * Do not change the existing data, just add a new database row + */ + public static function provideMoveRedirLogDatabaseRows() { + return [ + // Current format - with redirect + [ + [ + 'type' => 'move', + 'action' => 'move_redir', + 'comment' => 'move comment with redirect', + 'namespace' => NS_MAIN, + 'title' => 'OldPage', + 'params' => [ + '4::target' => 'NewPage', + '5::noredir' => '0', + ], + ], + [ + 'text' => 'User moved page OldPage to NewPage over redirect', + 'api' => [ + 'target_ns' => 0, + 'target_title' => 'NewPage', + 'suppressredirect' => false, + ], + ], + ], + + // Current format - without redirect + [ + [ + 'type' => 'move', + 'action' => 'move_redir', + 'comment' => 'move comment', + 'namespace' => NS_MAIN, + 'title' => 'OldPage', + 'params' => [ + '4::target' => 'NewPage', + '5::noredir' => '1', + ], + ], + [ + 'text' => 'User moved page OldPage to NewPage over a redirect without leaving a redirect', + 'api' => [ + 'target_ns' => 0, + 'target_title' => 'NewPage', + 'suppressredirect' => true, + ], + ], + ], + + // legacy format - with redirect + [ + [ + 'type' => 'move', + 'action' => 'move_redir', + 'comment' => 'move comment', + 'namespace' => NS_MAIN, + 'title' => 'OldPage', + 'params' => [ + 'NewPage', + '', + ], + ], + [ + 'legacy' => true, + 'text' => 'User moved page OldPage to NewPage over redirect', + 'api' => [ + 'target_ns' => 0, + 'target_title' => 'NewPage', + 'suppressredirect' => false, + ], + ], + ], + + // legacy format - without redirect + [ + [ + 'type' => 'move', + 'action' => 'move_redir', + 'comment' => 'move comment', + 'namespace' => NS_MAIN, + 'title' => 'OldPage', + 'params' => [ + 'NewPage', + '1', + ], + ], + [ + 'legacy' => true, + 'text' => 'User moved page OldPage to NewPage over a redirect without leaving a redirect', + 'api' => [ + 'target_ns' => 0, + 'target_title' => 'NewPage', + 'suppressredirect' => true, + ], + ], + ], + + // old format without flag for redirect suppression + [ + [ + 'type' => 'move', + 'action' => 'move_redir', + 'comment' => 'move comment', + 'namespace' => NS_MAIN, + 'title' => 'OldPage', + 'params' => [ + 'NewPage', + ], + ], + [ + 'legacy' => true, + 'text' => 'User moved page OldPage to NewPage over redirect', + 'api' => [ + 'target_ns' => 0, + 'target_title' => 'NewPage', + 'suppressredirect' => false, + ], + ], + ], + ]; + } + + /** + * @dataProvider provideMoveRedirLogDatabaseRows + */ + public function testMoveRedirLogDatabaseRows( $row, $extra ) { + $this->doTestLogFormatter( $row, $extra ); + } +} diff --git a/www/wiki/tests/phpunit/includes/logging/NewUsersLogFormatterTest.php b/www/wiki/tests/phpunit/includes/logging/NewUsersLogFormatterTest.php new file mode 100644 index 00000000..eee2981c --- /dev/null +++ b/www/wiki/tests/phpunit/includes/logging/NewUsersLogFormatterTest.php @@ -0,0 +1,204 @@ +<?php + +/** + * @covers NewUsersLogFormatter + * @group Database + */ +class NewUsersLogFormatterTest extends LogFormatterTestCase { + + protected function setUp() { + parent::setUp(); + + // Register LogHandler, see $wgNewUserLog in Setup.php + $this->mergeMwGlobalArrayValue( 'wgLogActionsHandlers', [ + 'newusers/newusers' => NewUsersLogFormatter::class, + 'newusers/create' => NewUsersLogFormatter::class, + 'newusers/create2' => NewUsersLogFormatter::class, + 'newusers/byemail' => NewUsersLogFormatter::class, + 'newusers/autocreate' => NewUsersLogFormatter::class, + ] ); + } + + /** + * Provide different rows from the logging table to test + * for backward compatibility. + * Do not change the existing data, just add a new database row + */ + public static function provideNewUsersLogDatabaseRows() { + return [ + // Only old logs + [ + [ + 'type' => 'newusers', + 'action' => 'newusers', + 'comment' => 'newusers comment', + 'user' => 0, + 'user_text' => 'New user', + 'namespace' => NS_USER, + 'title' => 'New user', + 'params' => [], + ], + [ + 'legacy' => true, + 'text' => 'User account New user was created', + 'api' => [], + ], + ], + ]; + } + + /** + * @dataProvider provideNewUsersLogDatabaseRows + */ + public function testNewUsersLogDatabaseRows( $row, $extra ) { + $this->doTestLogFormatter( $row, $extra ); + } + + /** + * Provide different rows from the logging table to test + * for backward compatibility. + * Do not change the existing data, just add a new database row + */ + public static function provideCreateLogDatabaseRows() { + return [ + // Current format + [ + [ + 'type' => 'newusers', + 'action' => 'create', + 'comment' => 'newusers comment', + 'user' => 0, + 'user_text' => 'New user', + 'namespace' => NS_USER, + 'title' => 'New user', + 'params' => [ + '4::userid' => 1, + ], + ], + [ + 'text' => 'User account New user was created', + 'api' => [ + 'userid' => 1, + ], + ], + ], + ]; + } + + /** + * @dataProvider provideCreateLogDatabaseRows + */ + public function testCreateLogDatabaseRows( $row, $extra ) { + $this->doTestLogFormatter( $row, $extra ); + } + + /** + * Provide different rows from the logging table to test + * for backward compatibility. + * Do not change the existing data, just add a new database row + */ + public static function provideCreate2LogDatabaseRows() { + return [ + // Current format + [ + [ + 'type' => 'newusers', + 'action' => 'create2', + 'comment' => 'newusers comment', + 'user' => 0, + 'user_text' => 'User', + 'namespace' => NS_USER, + 'title' => 'UTSysop' + ], + [ + 'text' => 'User account UTSysop was created by User' + ], + ], + ]; + } + + /** + * @dataProvider provideCreate2LogDatabaseRows + */ + public function testCreate2LogDatabaseRows( $row, $extra ) { + // Make UTSysop user and use its user_id (sequence does not reset to 1 for postgres) + $user = static::getTestSysop()->getUser(); + $row['params']['4::userid'] = $user->getId(); + $extra['api']['userid'] = $user->getId(); + $this->doTestLogFormatter( $row, $extra ); + } + + /** + * Provide different rows from the logging table to test + * for backward compatibility. + * Do not change the existing data, just add a new database row + */ + public static function provideByemailLogDatabaseRows() { + return [ + // Current format + [ + [ + 'type' => 'newusers', + 'action' => 'byemail', + 'comment' => 'newusers comment', + 'user' => 0, + 'user_text' => 'Sysop', + 'namespace' => NS_USER, + 'title' => 'UTSysop' + ], + [ + 'text' => 'User account UTSysop was created by Sysop and password was sent by email' + ], + ], + ]; + } + + /** + * @dataProvider provideByemailLogDatabaseRows + */ + public function testByemailLogDatabaseRows( $row, $extra ) { + // Make UTSysop user and use its user_id (sequence does not reset to 1 for postgres) + $user = static::getTestSysop()->getUser(); + $row['params']['4::userid'] = $user->getId(); + $extra['api']['userid'] = $user->getId(); + $this->doTestLogFormatter( $row, $extra ); + } + + /** + * Provide different rows from the logging table to test + * for backward compatibility. + * Do not change the existing data, just add a new database row + */ + public static function provideAutocreateLogDatabaseRows() { + return [ + // Current format + [ + [ + 'type' => 'newusers', + 'action' => 'autocreate', + 'comment' => 'newusers comment', + 'user' => 0, + 'user_text' => 'New user', + 'namespace' => NS_USER, + 'title' => 'New user', + 'params' => [ + '4::userid' => 1, + ], + ], + [ + 'text' => 'User account New user was created automatically', + 'api' => [ + 'userid' => 1, + ], + ], + ], + ]; + } + + /** + * @dataProvider provideAutocreateLogDatabaseRows + */ + public function testAutocreateLogDatabaseRows( $row, $extra ) { + $this->doTestLogFormatter( $row, $extra ); + } +} diff --git a/www/wiki/tests/phpunit/includes/logging/PageLangLogFormatterTest.php b/www/wiki/tests/phpunit/includes/logging/PageLangLogFormatterTest.php new file mode 100644 index 00000000..33fd68f6 --- /dev/null +++ b/www/wiki/tests/phpunit/includes/logging/PageLangLogFormatterTest.php @@ -0,0 +1,56 @@ +<?php + +/** + * @covers PageLangLogFormatter + */ +class PageLangLogFormatterTest extends LogFormatterTestCase { + + protected function setUp() { + parent::setUp(); + + // Disable cldr extension + $this->setMwGlobals( 'wgHooks', [] ); + // Register LogHandler, see $wgPageLanguageUseDB in Setup.php + $this->mergeMwGlobalArrayValue( 'wgLogActionsHandlers', [ + 'pagelang/pagelang' => PageLangLogFormatter::class, + ] ); + } + + /** + * Provide different rows from the logging table to test + * for backward compatibility. + * Do not change the existing data, just add a new database row + */ + public static function providePageLangLogDatabaseRows() { + return [ + // Current format + [ + [ + 'type' => 'pagelang', + 'action' => 'pagelang', + 'comment' => 'page lang comment', + 'namespace' => NS_MAIN, + 'title' => 'Page', + 'params' => [ + '4::oldlanguage' => 'en', + '5::newlanguage' => 'de[def]', + ], + ], + [ + 'text' => 'User changed the language of Page from English (en) to Deutsch (de) [default]', + 'api' => [ + 'oldlanguage' => 'en', + 'newlanguage' => 'de[def]' + ], + ], + ], + ]; + } + + /** + * @dataProvider providePageLangLogDatabaseRows + */ + public function testPageLangLogDatabaseRows( $row, $extra ) { + $this->doTestLogFormatter( $row, $extra ); + } +} diff --git a/www/wiki/tests/phpunit/includes/logging/PatrolLogFormatterTest.php b/www/wiki/tests/phpunit/includes/logging/PatrolLogFormatterTest.php new file mode 100644 index 00000000..0d78ed9c --- /dev/null +++ b/www/wiki/tests/phpunit/includes/logging/PatrolLogFormatterTest.php @@ -0,0 +1,121 @@ +<?php + +/** + * @covers PatrolLogFormatter + */ +class PatrolLogFormatterTest extends LogFormatterTestCase { + + /** + * Provide different rows from the logging table to test + * for backward compatibility. + * Do not change the existing data, just add a new database row + */ + public static function providePatrolLogDatabaseRows() { + return [ + // Current format + [ + [ + 'type' => 'patrol', + 'action' => 'patrol', + 'comment' => 'patrol comment', + 'namespace' => NS_MAIN, + 'title' => 'Page', + 'params' => [ + '4::curid' => 2, + '5::previd' => 1, + '6::auto' => 0, + ], + ], + [ + 'text' => 'User marked revision 2 of page Page patrolled', + 'api' => [ + 'curid' => 2, + 'previd' => 1, + 'auto' => false, + ], + ], + ], + + // Current format - autopatrol + [ + [ + 'type' => 'patrol', + 'action' => 'patrol', + 'comment' => 'patrol comment', + 'namespace' => NS_MAIN, + 'title' => 'Page', + 'params' => [ + '4::curid' => 2, + '5::previd' => 1, + '6::auto' => 1, + ], + ], + [ + 'text' => 'User automatically marked revision 2 of page Page patrolled', + 'api' => [ + 'curid' => 2, + 'previd' => 1, + 'auto' => true, + ], + ], + ], + + // Legacy format + [ + [ + 'type' => 'patrol', + 'action' => 'patrol', + 'comment' => 'patrol comment', + 'namespace' => NS_MAIN, + 'title' => 'Page', + 'params' => [ + '2', + '1', + '0', + ], + ], + [ + 'legacy' => true, + 'text' => 'User marked revision 2 of page Page patrolled', + 'api' => [ + 'curid' => 2, + 'previd' => 1, + 'auto' => false, + ], + ], + ], + + // Legacy format - autopatrol + [ + [ + 'type' => 'patrol', + 'action' => 'patrol', + 'comment' => 'patrol comment', + 'namespace' => NS_MAIN, + 'title' => 'Page', + 'params' => [ + '2', + '1', + '1', + ], + ], + [ + 'legacy' => true, + 'text' => 'User automatically marked revision 2 of page Page patrolled', + 'api' => [ + 'curid' => 2, + 'previd' => 1, + 'auto' => true, + ], + ], + ], + ]; + } + + /** + * @dataProvider providePatrolLogDatabaseRows + */ + public function testPatrolLogDatabaseRows( $row, $extra ) { + $this->doTestLogFormatter( $row, $extra ); + } +} diff --git a/www/wiki/tests/phpunit/includes/logging/ProtectLogFormatterTest.php b/www/wiki/tests/phpunit/includes/logging/ProtectLogFormatterTest.php new file mode 100644 index 00000000..1c076cab --- /dev/null +++ b/www/wiki/tests/phpunit/includes/logging/ProtectLogFormatterTest.php @@ -0,0 +1,431 @@ +<?php + +/** + * @covers ProtectLogFormatter + */ +class ProtectLogFormatterTest extends LogFormatterTestCase { + + /** + * Provide different rows from the logging table to test + * for backward compatibility. + * Do not change the existing data, just add a new database row + */ + public static function provideProtectLogDatabaseRows() { + return [ + // Current format + [ + [ + 'type' => 'protect', + 'action' => 'protect', + 'comment' => 'protect comment', + 'namespace' => NS_MAIN, + 'title' => 'ProtectPage', + 'params' => [ + '4::description' => '[edit=sysop] (indefinite)[move=sysop] (indefinite)', + '5:bool:cascade' => false, + 'details' => [ + [ + 'type' => 'edit', + 'level' => 'sysop', + 'expiry' => 'infinity', + 'cascade' => false, + ], + [ + 'type' => 'move', + 'level' => 'sysop', + 'expiry' => 'infinity', + 'cascade' => false, + ], + ], + ], + ], + [ + 'text' => 'User protected ProtectPage [Edit=Allow only administrators] ' . + '(indefinite) [Move=Allow only administrators] (indefinite)', + 'api' => [ + 'description' => '[edit=sysop] (indefinite)[move=sysop] (indefinite)', + 'cascade' => false, + 'details' => [ + [ + 'type' => 'edit', + 'level' => 'sysop', + 'expiry' => 'infinite', + 'cascade' => false, + ], + [ + 'type' => 'move', + 'level' => 'sysop', + 'expiry' => 'infinite', + 'cascade' => false, + ], + ], + ], + ], + ], + + // Current format with cascade + [ + [ + 'type' => 'protect', + 'action' => 'protect', + 'comment' => 'protect comment', + 'namespace' => NS_MAIN, + 'title' => 'ProtectPage', + 'params' => [ + '4::description' => '[edit=sysop] (indefinite)[move=sysop] (indefinite)', + '5:bool:cascade' => true, + 'details' => [ + [ + 'type' => 'edit', + 'level' => 'sysop', + 'expiry' => 'infinity', + 'cascade' => true, + ], + [ + 'type' => 'move', + 'level' => 'sysop', + 'expiry' => 'infinity', + 'cascade' => false, + ], + ], + ], + ], + [ + 'text' => 'User protected ProtectPage [Edit=Allow only administrators] ' . + '(indefinite) [Move=Allow only administrators] (indefinite) [cascading]', + 'api' => [ + 'description' => '[edit=sysop] (indefinite)[move=sysop] (indefinite)', + 'cascade' => true, + 'details' => [ + [ + 'type' => 'edit', + 'level' => 'sysop', + 'expiry' => 'infinite', + 'cascade' => true, + ], + [ + 'type' => 'move', + 'level' => 'sysop', + 'expiry' => 'infinite', + 'cascade' => false, + ], + ], + ], + ], + ], + + // Legacy format + [ + [ + 'type' => 'protect', + 'action' => 'protect', + 'comment' => 'protect comment', + 'namespace' => NS_MAIN, + 'title' => 'ProtectPage', + 'params' => [ + '[edit=sysop] (indefinite)[move=sysop] (indefinite)', + '', + ], + ], + [ + 'legacy' => true, + 'text' => 'User protected ProtectPage [edit=sysop] (indefinite)[move=sysop] (indefinite)', + 'api' => [ + 'description' => '[edit=sysop] (indefinite)[move=sysop] (indefinite)', + 'cascade' => false, + ], + ], + ], + + // Legacy format with cascade + [ + [ + 'type' => 'protect', + 'action' => 'protect', + 'comment' => 'protect comment', + 'namespace' => NS_MAIN, + 'title' => 'ProtectPage', + 'params' => [ + '[edit=sysop] (indefinite)[move=sysop] (indefinite)', + 'cascade', + ], + ], + [ + 'legacy' => true, + 'text' => 'User protected ProtectPage [edit=sysop] ' . + '(indefinite)[move=sysop] (indefinite) [cascading]', + 'api' => [ + 'description' => '[edit=sysop] (indefinite)[move=sysop] (indefinite)', + 'cascade' => true, + ], + ], + ], + ]; + } + + /** + * @dataProvider provideProtectLogDatabaseRows + */ + public function testProtectLogDatabaseRows( $row, $extra ) { + $this->doTestLogFormatter( $row, $extra ); + } + + /** + * Provide different rows from the logging table to test + * for backward compatibility. + * Do not change the existing data, just add a new database row + */ + public static function provideModifyLogDatabaseRows() { + return [ + // Current format + [ + [ + 'type' => 'protect', + 'action' => 'modify', + 'comment' => 'protect comment', + 'namespace' => NS_MAIN, + 'title' => 'ProtectPage', + 'params' => [ + '4::description' => '[edit=sysop] (indefinite)[move=sysop] (indefinite)', + '5:bool:cascade' => false, + 'details' => [ + [ + 'type' => 'edit', + 'level' => 'sysop', + 'expiry' => 'infinity', + 'cascade' => false, + ], + [ + 'type' => 'move', + 'level' => 'sysop', + 'expiry' => 'infinity', + 'cascade' => false, + ], + ], + ], + ], + [ + 'text' => 'User changed protection level for ProtectPage ' . + '[Edit=Allow only administrators] ' . + '(indefinite) [Move=Allow only administrators] (indefinite)', + 'api' => [ + 'description' => '[edit=sysop] (indefinite)[move=sysop] (indefinite)', + 'cascade' => false, + 'details' => [ + [ + 'type' => 'edit', + 'level' => 'sysop', + 'expiry' => 'infinite', + 'cascade' => false, + ], + [ + 'type' => 'move', + 'level' => 'sysop', + 'expiry' => 'infinite', + 'cascade' => false, + ], + ], + ], + ], + ], + + // Current format with cascade + [ + [ + 'type' => 'protect', + 'action' => 'modify', + 'comment' => 'protect comment', + 'namespace' => NS_MAIN, + 'title' => 'ProtectPage', + 'params' => [ + '4::description' => '[edit=sysop] (indefinite)[move=sysop] (indefinite)', + '5:bool:cascade' => true, + 'details' => [ + [ + 'type' => 'edit', + 'level' => 'sysop', + 'expiry' => 'infinity', + 'cascade' => true, + ], + [ + 'type' => 'move', + 'level' => 'sysop', + 'expiry' => 'infinity', + 'cascade' => false, + ], + ], + ], + ], + [ + 'text' => 'User changed protection level for ProtectPage ' . + '[Edit=Allow only administrators] (indefinite) ' . + '[Move=Allow only administrators] (indefinite) [cascading]', + 'api' => [ + 'description' => '[edit=sysop] (indefinite)[move=sysop] (indefinite)', + 'cascade' => true, + 'details' => [ + [ + 'type' => 'edit', + 'level' => 'sysop', + 'expiry' => 'infinite', + 'cascade' => true, + ], + [ + 'type' => 'move', + 'level' => 'sysop', + 'expiry' => 'infinite', + 'cascade' => false, + ], + ], + ], + ], + ], + + // Legacy format + [ + [ + 'type' => 'protect', + 'action' => 'modify', + 'comment' => 'protect comment', + 'namespace' => NS_MAIN, + 'title' => 'ProtectPage', + 'params' => [ + '[edit=sysop] (indefinite)[move=sysop] (indefinite)', + '', + ], + ], + [ + 'legacy' => true, + 'text' => 'User changed protection level for ProtectPage ' . + '[edit=sysop] (indefinite)[move=sysop] (indefinite)', + 'api' => [ + 'description' => '[edit=sysop] (indefinite)[move=sysop] (indefinite)', + 'cascade' => false, + ], + ], + ], + + // Legacy format with cascade + [ + [ + 'type' => 'protect', + 'action' => 'modify', + 'comment' => 'protect comment', + 'namespace' => NS_MAIN, + 'title' => 'ProtectPage', + 'params' => [ + '[edit=sysop] (indefinite)[move=sysop] (indefinite)', + 'cascade', + ], + ], + [ + 'legacy' => true, + 'text' => 'User changed protection level for ProtectPage ' . + '[edit=sysop] (indefinite)[move=sysop] (indefinite) [cascading]', + 'api' => [ + 'description' => '[edit=sysop] (indefinite)[move=sysop] (indefinite)', + 'cascade' => true, + ], + ], + ], + ]; + } + + /** + * @dataProvider provideModifyLogDatabaseRows + */ + public function testModifyLogDatabaseRows( $row, $extra ) { + $this->doTestLogFormatter( $row, $extra ); + } + + /** + * Provide different rows from the logging table to test + * for backward compatibility. + * Do not change the existing data, just add a new database row + */ + public static function provideUnprotectLogDatabaseRows() { + return [ + // Current format + [ + [ + 'type' => 'protect', + 'action' => 'unprotect', + 'comment' => 'unprotect comment', + 'namespace' => NS_MAIN, + 'title' => 'ProtectPage', + 'params' => [], + ], + [ + 'text' => 'User removed protection from ProtectPage', + 'api' => [], + ], + ], + ]; + } + + /** + * @dataProvider provideUnprotectLogDatabaseRows + */ + public function testUnprotectLogDatabaseRows( $row, $extra ) { + $this->doTestLogFormatter( $row, $extra ); + } + + /** + * Provide different rows from the logging table to test + * for backward compatibility. + * Do not change the existing data, just add a new database row + */ + public static function provideMoveProtLogDatabaseRows() { + return [ + // Current format + [ + [ + 'type' => 'protect', + 'action' => 'move_prot', + 'comment' => 'Move comment', + 'namespace' => NS_MAIN, + 'title' => 'NewPage', + 'params' => [ + '4::oldtitle' => 'OldPage', + ], + ], + [ + 'text' => 'User moved protection settings from OldPage to NewPage', + 'api' => [ + 'oldtitle_ns' => 0, + 'oldtitle_title' => 'OldPage', + ], + ], + ], + + // Legacy format + [ + [ + 'type' => 'protect', + 'action' => 'move_prot', + 'comment' => 'Move comment', + 'namespace' => NS_MAIN, + 'title' => 'NewPage', + 'params' => [ + 'OldPage', + ], + ], + [ + 'legacy' => true, + 'text' => 'User moved protection settings from OldPage to NewPage', + 'api' => [ + 'oldtitle_ns' => 0, + 'oldtitle_title' => 'OldPage', + ], + ], + ], + ]; + } + + /** + * @dataProvider provideMoveProtLogDatabaseRows + */ + public function testMoveProtLogDatabaseRows( $row, $extra ) { + $this->doTestLogFormatter( $row, $extra ); + } +} diff --git a/www/wiki/tests/phpunit/includes/logging/RightsLogFormatterTest.php b/www/wiki/tests/phpunit/includes/logging/RightsLogFormatterTest.php new file mode 100644 index 00000000..d081c61b --- /dev/null +++ b/www/wiki/tests/phpunit/includes/logging/RightsLogFormatterTest.php @@ -0,0 +1,219 @@ +<?php + +/** + * @covers RightsLogFormatter + */ +class RightsLogFormatterTest extends LogFormatterTestCase { + + /** + * Provide different rows from the logging table to test + * for backward compatibility. + * Do not change the existing data, just add a new database row + */ + public static function provideRightsLogDatabaseRows() { + return [ + // Current format + [ + [ + 'type' => 'rights', + 'action' => 'rights', + 'comment' => 'rights comment', + 'user' => 0, + 'user_text' => 'Sysop', + 'namespace' => NS_USER, + 'title' => 'User', + 'params' => [ + '4::oldgroups' => [], + '5::newgroups' => [ 'sysop', 'bureaucrat' ], + 'oldmetadata' => [], + 'newmetadata' => [ + [ 'expiry' => null ], + [ 'expiry' => '20160101123456' ] + ], + ], + ], + [ + 'text' => 'Sysop changed group membership for User from (none) to ' + . 'bureaucrat (temporary, until 12:34, 1 January 2016) and administrator', + 'api' => [ + 'oldgroups' => [], + 'newgroups' => [ 'sysop', 'bureaucrat' ], + 'oldmetadata' => [], + 'newmetadata' => [ + [ 'group' => 'sysop', 'expiry' => 'infinity' ], + [ 'group' => 'bureaucrat', 'expiry' => '2016-01-01T12:34:56Z' ], + ], + ], + ], + ], + + // Previous format (oldgroups and newgroups as arrays, no metadata) + [ + [ + 'type' => 'rights', + 'action' => 'rights', + 'comment' => 'rights comment', + 'user' => 0, + 'user_text' => 'Sysop', + 'namespace' => NS_USER, + 'title' => 'User', + 'params' => [ + '4::oldgroups' => [], + '5::newgroups' => [ 'sysop', 'bureaucrat' ], + ], + ], + [ + 'text' => 'Sysop changed group membership for User from (none) to ' + . 'administrator and bureaucrat', + 'api' => [ + 'oldgroups' => [], + 'newgroups' => [ 'sysop', 'bureaucrat' ], + 'oldmetadata' => [], + 'newmetadata' => [ + [ 'group' => 'sysop', 'expiry' => 'infinity' ], + [ 'group' => 'bureaucrat', 'expiry' => 'infinity' ], + ], + ], + ], + ], + + // Legacy format (oldgroups and newgroups as numeric-keyed strings) + [ + [ + 'type' => 'rights', + 'action' => 'rights', + 'comment' => 'rights comment', + 'user' => 0, + 'user_text' => 'Sysop', + 'namespace' => NS_USER, + 'title' => 'User', + 'params' => [ + '', + 'sysop, bureaucrat', + ], + ], + [ + 'legacy' => true, + 'text' => 'Sysop changed group membership for User from (none) to ' + . 'administrator and bureaucrat', + 'api' => [ + 'oldgroups' => [], + 'newgroups' => [ 'sysop', 'bureaucrat' ], + 'oldmetadata' => [], + 'newmetadata' => [ + [ 'group' => 'sysop', 'expiry' => 'infinity' ], + [ 'group' => 'bureaucrat', 'expiry' => 'infinity' ], + ], + ], + ], + ], + + // Really old entry + [ + [ + 'type' => 'rights', + 'action' => 'rights', + 'comment' => 'rights comment', + 'user' => 0, + 'user_text' => 'Sysop', + 'namespace' => NS_USER, + 'title' => 'User', + 'params' => [], + ], + [ + 'legacy' => true, + 'text' => 'Sysop changed group membership for User', + 'api' => [], + ], + ], + ]; + } + + /** + * @dataProvider provideRightsLogDatabaseRows + */ + public function testRightsLogDatabaseRows( $row, $extra ) { + $this->doTestLogFormatter( $row, $extra ); + } + + /** + * Provide different rows from the logging table to test + * for backward compatibility. + * Do not change the existing data, just add a new database row + */ + public static function provideAutopromoteLogDatabaseRows() { + return [ + // Current format + [ + [ + 'type' => 'rights', + 'action' => 'autopromote', + 'comment' => 'rights comment', + 'user' => 0, + 'user_text' => 'Sysop', + 'namespace' => NS_USER, + 'title' => 'Sysop', + 'params' => [ + '4::oldgroups' => [ 'sysop' ], + '5::newgroups' => [ 'sysop', 'bureaucrat' ], + ], + ], + [ + 'text' => 'Sysop was automatically promoted from administrator to ' + . 'administrator and bureaucrat', + 'api' => [ + 'oldgroups' => [ 'sysop' ], + 'newgroups' => [ 'sysop', 'bureaucrat' ], + 'oldmetadata' => [ + [ 'group' => 'sysop', 'expiry' => 'infinity' ], + ], + 'newmetadata' => [ + [ 'group' => 'sysop', 'expiry' => 'infinity' ], + [ 'group' => 'bureaucrat', 'expiry' => 'infinity' ], + ], + ], + ], + ], + + // Legacy format + [ + [ + 'type' => 'rights', + 'action' => 'autopromote', + 'comment' => 'rights comment', + 'user' => 0, + 'user_text' => 'Sysop', + 'namespace' => NS_USER, + 'title' => 'Sysop', + 'params' => [ + 'sysop', + 'sysop, bureaucrat', + ], + ], + [ + 'legacy' => true, + 'text' => 'Sysop was automatically promoted from administrator to ' + . 'administrator and bureaucrat', + 'api' => [ + 'oldgroups' => [ 'sysop' ], + 'newgroups' => [ 'sysop', 'bureaucrat' ], + 'oldmetadata' => [ + [ 'group' => 'sysop', 'expiry' => 'infinity' ], + ], + 'newmetadata' => [ + [ 'group' => 'sysop', 'expiry' => 'infinity' ], + [ 'group' => 'bureaucrat', 'expiry' => 'infinity' ], + ], + ], + ], + ], + ]; + } + + /** + * @dataProvider provideAutopromoteLogDatabaseRows + */ + public function testAutopromoteLogDatabaseRows( $row, $extra ) { + $this->doTestLogFormatter( $row, $extra ); + } +} diff --git a/www/wiki/tests/phpunit/includes/logging/UploadLogFormatterTest.php b/www/wiki/tests/phpunit/includes/logging/UploadLogFormatterTest.php new file mode 100644 index 00000000..2b4067f1 --- /dev/null +++ b/www/wiki/tests/phpunit/includes/logging/UploadLogFormatterTest.php @@ -0,0 +1,169 @@ +<?php + +/** + * @covers UploadLogFormatter + */ +class UploadLogFormatterTest extends LogFormatterTestCase { + + /** + * Provide different rows from the logging table to test + * for backward compatibility. + * Do not change the existing data, just add a new database row + */ + public static function provideUploadLogDatabaseRows() { + return [ + // Current format + [ + [ + 'type' => 'upload', + 'action' => 'upload', + 'comment' => 'upload comment', + 'namespace' => NS_FILE, + 'title' => 'File.png', + 'params' => [ + 'img_sha1' => 'hash', + 'img_timestamp' => '20150101000000', + ], + ], + [ + 'text' => 'User uploaded File:File.png', + 'api' => [ + 'img_sha1' => 'hash', + 'img_timestamp' => '2015-01-01T00:00:00Z', + ], + ], + ], + + // Old format without params + [ + [ + 'type' => 'upload', + 'action' => 'upload', + 'comment' => 'upload comment', + 'namespace' => NS_FILE, + 'title' => 'File.png', + 'params' => [], + ], + [ + 'text' => 'User uploaded File:File.png', + 'api' => [], + ], + ], + ]; + } + + /** + * @dataProvider provideUploadLogDatabaseRows + */ + public function testUploadLogDatabaseRows( $row, $extra ) { + $this->doTestLogFormatter( $row, $extra ); + } + + /** + * Provide different rows from the logging table to test + * for backward compatibility. + * Do not change the existing data, just add a new database row + */ + public static function provideOverwriteLogDatabaseRows() { + return [ + // Current format + [ + [ + 'type' => 'upload', + 'action' => 'overwrite', + 'comment' => 'upload comment', + 'namespace' => NS_FILE, + 'title' => 'File.png', + 'params' => [ + 'img_sha1' => 'hash', + 'img_timestamp' => '20150101000000', + ], + ], + [ + 'text' => 'User uploaded a new version of File:File.png', + 'api' => [ + 'img_sha1' => 'hash', + 'img_timestamp' => '2015-01-01T00:00:00Z', + ], + ], + ], + + // Old format without params + [ + [ + 'type' => 'upload', + 'action' => 'overwrite', + 'comment' => 'upload comment', + 'namespace' => NS_FILE, + 'title' => 'File.png', + 'params' => [], + ], + [ + 'text' => 'User uploaded a new version of File:File.png', + 'api' => [], + ], + ], + ]; + } + + /** + * @dataProvider provideOverwriteLogDatabaseRows + */ + public function testOverwriteLogDatabaseRows( $row, $extra ) { + $this->doTestLogFormatter( $row, $extra ); + } + + /** + * Provide different rows from the logging table to test + * for backward compatibility. + * Do not change the existing data, just add a new database row + */ + public static function provideRevertLogDatabaseRows() { + return [ + // Current format + [ + [ + 'type' => 'upload', + 'action' => 'revert', + 'comment' => 'upload comment', + 'namespace' => NS_FILE, + 'title' => 'File.png', + 'params' => [ + 'img_sha1' => 'hash', + 'img_timestamp' => '20150101000000', + ], + ], + [ + 'text' => 'User uploaded File:File.png', + 'api' => [ + 'img_sha1' => 'hash', + 'img_timestamp' => '2015-01-01T00:00:00Z', + ], + ], + ], + + // Old format without params + [ + [ + 'type' => 'upload', + 'action' => 'revert', + 'comment' => 'upload comment', + 'namespace' => NS_FILE, + 'title' => 'File.png', + 'params' => [], + ], + [ + 'text' => 'User uploaded File:File.png', + 'api' => [], + ], + ], + ]; + } + + /** + * @dataProvider provideRevertLogDatabaseRows + */ + public function testRevertLogDatabaseRows( $row, $extra ) { + $this->doTestLogFormatter( $row, $extra ); + } +} |