summaryrefslogtreecommitdiff
path: root/www/wiki/tests/phpunit/includes/logging
diff options
context:
space:
mode:
Diffstat (limited to 'www/wiki/tests/phpunit/includes/logging')
-rw-r--r--www/wiki/tests/phpunit/includes/logging/BlockLogFormatterTest.php379
-rw-r--r--www/wiki/tests/phpunit/includes/logging/ContentModelLogFormatterTest.php60
-rw-r--r--www/wiki/tests/phpunit/includes/logging/DatabaseLogEntryTest.php162
-rw-r--r--www/wiki/tests/phpunit/includes/logging/DeleteLogFormatterTest.php556
-rw-r--r--www/wiki/tests/phpunit/includes/logging/ImportLogFormatterTest.php126
-rw-r--r--www/wiki/tests/phpunit/includes/logging/LogFormatterTest.php664
-rw-r--r--www/wiki/tests/phpunit/includes/logging/LogFormatterTestCase.php68
-rw-r--r--www/wiki/tests/phpunit/includes/logging/LogTests.i18n.php15
-rw-r--r--www/wiki/tests/phpunit/includes/logging/MergeLogFormatterTest.php70
-rw-r--r--www/wiki/tests/phpunit/includes/logging/MoveLogFormatterTest.php273
-rw-r--r--www/wiki/tests/phpunit/includes/logging/NewUsersLogFormatterTest.php204
-rw-r--r--www/wiki/tests/phpunit/includes/logging/PageLangLogFormatterTest.php56
-rw-r--r--www/wiki/tests/phpunit/includes/logging/PatrolLogFormatterTest.php121
-rw-r--r--www/wiki/tests/phpunit/includes/logging/ProtectLogFormatterTest.php431
-rw-r--r--www/wiki/tests/phpunit/includes/logging/RightsLogFormatterTest.php219
-rw-r--r--www/wiki/tests/phpunit/includes/logging/UploadLogFormatterTest.php169
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( '&quot;', '"', $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 );
+ }
+}