2021-05-11 02:16:20 +00:00
|
|
|
<?php
|
|
|
|
|
|
2023-12-11 14:59:55 +00:00
|
|
|
use MediaWiki\CommentStore\CommentStoreComment;
|
2023-11-21 21:08:14 +00:00
|
|
|
use MediaWiki\Deferred\DeferredUpdates;
|
|
|
|
|
use MediaWiki\Deferred\UserEditCountUpdate;
|
2021-05-11 02:16:20 +00:00
|
|
|
use MediaWiki\Revision\MutableRevisionRecord;
|
|
|
|
|
use MediaWiki\Revision\SlotRecord;
|
2023-03-01 20:33:26 +00:00
|
|
|
use MediaWiki\Title\Title;
|
2021-05-11 02:16:20 +00:00
|
|
|
use MediaWiki\User\UserIdentity;
|
|
|
|
|
use MediaWiki\User\UserIdentityValue;
|
2022-03-16 04:08:00 +00:00
|
|
|
use MediaWiki\User\UserRigorOptions;
|
2021-05-11 02:16:20 +00:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @covers \MediaWiki\User\UserEditTracker
|
|
|
|
|
* @group Database
|
|
|
|
|
*/
|
|
|
|
|
class UserEditTrackerTest extends MediaWikiIntegrationTestCase {
|
Fix tests not properly cleaning up the DB
- UserEditTrackerTest: determine whether to create the page based on
WikiPage::exists() instead of a method parameter. The current
implementation only works because the page is not deleted between test
runs, and the creation query is ignored. However, if the first call to
`editTrackerDoEdit` doesn't have $create=true, tests would fail. For
instance, testGetEditTimestamp would fail when run on its own because
the revision cannot be inserted if the page doesn't exist. Just use
WikiPage::exists instead of forcing callers to handle this correctly.
- DatabaseBlockTest: use addDBDataOnce instead of a DIY implementation.
This also makes the tests more deterministic, because the records
needed by the test class are created immediately. Also avoid redundant
User::addToDatabase and ::saveSettings, these are already done by
TestUser.
- PageRestrictionTest: avoid Title::newFromID which is not guaranteed to
succeed if all pages have been deleted by that point. The second part
of the test was effectively doing the same thing as the first part, so
just remove it.
- WikiPageDbTest: avoid expensive page deletions in tearDown. These are
all unnecessary because the DB is cleaned up after each test (and
'page' is explicitly included in $tablesUsed). In fact, deleting test
pages as done there can be even worse than not doing anything, because
it creates log entries etc. Add page_restrictions to tablesUsed as
that's not truncated automatically.
- DBSiteStoreTest: testReset would fail when run on its own because it
depends on test sites being inserted in testGetSites. Make the test
add the sites it needs, so that they can safely be cleared between
test runs.
Change-Id: I1065fb3e8507b4b1a3bf185181f2f3059a97fd04
2023-08-11 17:50:20 +00:00
|
|
|
|
2021-05-11 02:16:20 +00:00
|
|
|
/**
|
|
|
|
|
* Do an edit
|
|
|
|
|
*
|
|
|
|
|
* @param UserIdentity $user
|
|
|
|
|
* @param string $timestamp
|
|
|
|
|
*/
|
Fix tests not properly cleaning up the DB
- UserEditTrackerTest: determine whether to create the page based on
WikiPage::exists() instead of a method parameter. The current
implementation only works because the page is not deleted between test
runs, and the creation query is ignored. However, if the first call to
`editTrackerDoEdit` doesn't have $create=true, tests would fail. For
instance, testGetEditTimestamp would fail when run on its own because
the revision cannot be inserted if the page doesn't exist. Just use
WikiPage::exists instead of forcing callers to handle this correctly.
- DatabaseBlockTest: use addDBDataOnce instead of a DIY implementation.
This also makes the tests more deterministic, because the records
needed by the test class are created immediately. Also avoid redundant
User::addToDatabase and ::saveSettings, these are already done by
TestUser.
- PageRestrictionTest: avoid Title::newFromID which is not guaranteed to
succeed if all pages have been deleted by that point. The second part
of the test was effectively doing the same thing as the first part, so
just remove it.
- WikiPageDbTest: avoid expensive page deletions in tearDown. These are
all unnecessary because the DB is cleaned up after each test (and
'page' is explicitly included in $tablesUsed). In fact, deleting test
pages as done there can be even worse than not doing anything, because
it creates log entries etc. Add page_restrictions to tablesUsed as
that's not truncated automatically.
- DBSiteStoreTest: testReset would fail when run on its own because it
depends on test sites being inserted in testGetSites. Make the test
add the sites it needs, so that they can safely be cleared between
test runs.
Change-Id: I1065fb3e8507b4b1a3bf185181f2f3059a97fd04
2023-08-11 17:50:20 +00:00
|
|
|
private function editTrackerDoEdit( $user, $timestamp ) {
|
2021-05-11 02:16:20 +00:00
|
|
|
$title = Title::newFromText( __FUNCTION__ );
|
|
|
|
|
$page = $this->getServiceContainer()->getWikiPageFactory()->newFromTitle( $title );
|
Fix tests not properly cleaning up the DB
- UserEditTrackerTest: determine whether to create the page based on
WikiPage::exists() instead of a method parameter. The current
implementation only works because the page is not deleted between test
runs, and the creation query is ignored. However, if the first call to
`editTrackerDoEdit` doesn't have $create=true, tests would fail. For
instance, testGetEditTimestamp would fail when run on its own because
the revision cannot be inserted if the page doesn't exist. Just use
WikiPage::exists instead of forcing callers to handle this correctly.
- DatabaseBlockTest: use addDBDataOnce instead of a DIY implementation.
This also makes the tests more deterministic, because the records
needed by the test class are created immediately. Also avoid redundant
User::addToDatabase and ::saveSettings, these are already done by
TestUser.
- PageRestrictionTest: avoid Title::newFromID which is not guaranteed to
succeed if all pages have been deleted by that point. The second part
of the test was effectively doing the same thing as the first part, so
just remove it.
- WikiPageDbTest: avoid expensive page deletions in tearDown. These are
all unnecessary because the DB is cleaned up after each test (and
'page' is explicitly included in $tablesUsed). In fact, deleting test
pages as done there can be even worse than not doing anything, because
it creates log entries etc. Add page_restrictions to tablesUsed as
that's not truncated automatically.
- DBSiteStoreTest: testReset would fail when run on its own because it
depends on test sites being inserted in testGetSites. Make the test
add the sites it needs, so that they can safely be cleared between
test runs.
Change-Id: I1065fb3e8507b4b1a3bf185181f2f3059a97fd04
2023-08-11 17:50:20 +00:00
|
|
|
if ( !$page->exists() ) {
|
2021-05-11 02:16:20 +00:00
|
|
|
$page->insertOn( $this->db );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$rev = new MutableRevisionRecord( $title );
|
|
|
|
|
$rev->setContent( SlotRecord::MAIN, new WikitextContent( $timestamp ) );
|
|
|
|
|
$rev->setComment( CommentStoreComment::newUnsavedComment( '' ) );
|
|
|
|
|
$rev->setTimestamp( $timestamp );
|
|
|
|
|
$rev->setUser( $user );
|
|
|
|
|
$rev->setPageId( $page->getId() );
|
|
|
|
|
$this->getServiceContainer()->getRevisionStore()->insertRevisionOn( $rev, $this->db );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Change the user_editcount field in the DB
|
|
|
|
|
*
|
|
|
|
|
* @param UserIdentity $user
|
|
|
|
|
* @param int|null $count
|
|
|
|
|
*/
|
|
|
|
|
private function setDbEditCount( $user, $count ) {
|
2024-04-14 18:39:18 +00:00
|
|
|
$this->db->newUpdateQueryBuilder()
|
|
|
|
|
->update( 'user' )
|
|
|
|
|
->set( [ 'user_editcount' => $count ] )
|
|
|
|
|
->where( [ 'user_id' => $user->getId() ] )
|
|
|
|
|
->caller( __METHOD__ )
|
|
|
|
|
->execute();
|
2021-05-11 02:16:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function testGetUserEditCount() {
|
|
|
|
|
// Set user_editcount to 5
|
|
|
|
|
$user = $this->getMutableTestUser()->getUser();
|
|
|
|
|
$update = new UserEditCountUpdate( $user, 5 );
|
|
|
|
|
$update->doUpdate();
|
|
|
|
|
|
|
|
|
|
$tracker = $this->getServiceContainer()->getUserEditTracker();
|
|
|
|
|
$this->assertSame( 5, $tracker->getUserEditCount( $user ) );
|
|
|
|
|
|
|
|
|
|
// Now fetch from cache
|
|
|
|
|
$this->assertSame( 5, $tracker->getUserEditCount( $user ) );
|
|
|
|
|
}
|
|
|
|
|
|
2021-06-15 08:08:33 +00:00
|
|
|
public function testGetUserEditCount_anon() {
|
|
|
|
|
// getUserEditCount returns null if the user is unregistered
|
|
|
|
|
$anon = UserIdentityValue::newAnonymous( '1.2.3.4' );
|
2021-05-11 02:16:20 +00:00
|
|
|
$tracker = $this->getServiceContainer()->getUserEditTracker();
|
2021-06-15 08:08:33 +00:00
|
|
|
$this->assertNull( $tracker->getUserEditCount( $anon ) );
|
2021-05-11 02:16:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function testGetUserEditCount_null() {
|
|
|
|
|
// getUserEditCount doesn't find a value in user_editcount and calls
|
|
|
|
|
// initializeUserEditCount
|
|
|
|
|
$user = $this->getMutableTestUser()->getUserIdentity();
|
|
|
|
|
$this->setDbEditCount( $user, null );
|
|
|
|
|
$tracker = $this->getServiceContainer()->getUserEditTracker();
|
|
|
|
|
$this->assertSame( 0, $tracker->getUserEditCount( $user ) );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function testInitializeUserEditCount() {
|
|
|
|
|
$user = $this->getMutableTestUser()->getUser();
|
Fix tests not properly cleaning up the DB
- UserEditTrackerTest: determine whether to create the page based on
WikiPage::exists() instead of a method parameter. The current
implementation only works because the page is not deleted between test
runs, and the creation query is ignored. However, if the first call to
`editTrackerDoEdit` doesn't have $create=true, tests would fail. For
instance, testGetEditTimestamp would fail when run on its own because
the revision cannot be inserted if the page doesn't exist. Just use
WikiPage::exists instead of forcing callers to handle this correctly.
- DatabaseBlockTest: use addDBDataOnce instead of a DIY implementation.
This also makes the tests more deterministic, because the records
needed by the test class are created immediately. Also avoid redundant
User::addToDatabase and ::saveSettings, these are already done by
TestUser.
- PageRestrictionTest: avoid Title::newFromID which is not guaranteed to
succeed if all pages have been deleted by that point. The second part
of the test was effectively doing the same thing as the first part, so
just remove it.
- WikiPageDbTest: avoid expensive page deletions in tearDown. These are
all unnecessary because the DB is cleaned up after each test (and
'page' is explicitly included in $tablesUsed). In fact, deleting test
pages as done there can be even worse than not doing anything, because
it creates log entries etc. Add page_restrictions to tablesUsed as
that's not truncated automatically.
- DBSiteStoreTest: testReset would fail when run on its own because it
depends on test sites being inserted in testGetSites. Make the test
add the sites it needs, so that they can safely be cleared between
test runs.
Change-Id: I1065fb3e8507b4b1a3bf185181f2f3059a97fd04
2023-08-11 17:50:20 +00:00
|
|
|
$this->editTrackerDoEdit( $user, '20200101000000' );
|
2021-05-11 02:16:20 +00:00
|
|
|
$tracker = $this->getServiceContainer()->getUserEditTracker();
|
|
|
|
|
$tracker->initializeUserEditCount( $user );
|
|
|
|
|
$this->runJobs();
|
|
|
|
|
$this->assertSame( 1, $tracker->getUserEditCount( $user ) );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function testGetEditTimestamp() {
|
|
|
|
|
$user = $this->getMutableTestUser()->getUser();
|
|
|
|
|
$tracker = $this->getServiceContainer()->getUserEditTracker();
|
|
|
|
|
$this->assertFalse( $tracker->getFirstEditTimestamp( $user ) );
|
|
|
|
|
$this->assertFalse( $tracker->getLatestEditTimestamp( $user ) );
|
|
|
|
|
|
|
|
|
|
$ts1 = '20010101000000';
|
|
|
|
|
$ts2 = '20020101000000';
|
|
|
|
|
$ts3 = '20030101000000';
|
Fix tests not properly cleaning up the DB
- UserEditTrackerTest: determine whether to create the page based on
WikiPage::exists() instead of a method parameter. The current
implementation only works because the page is not deleted between test
runs, and the creation query is ignored. However, if the first call to
`editTrackerDoEdit` doesn't have $create=true, tests would fail. For
instance, testGetEditTimestamp would fail when run on its own because
the revision cannot be inserted if the page doesn't exist. Just use
WikiPage::exists instead of forcing callers to handle this correctly.
- DatabaseBlockTest: use addDBDataOnce instead of a DIY implementation.
This also makes the tests more deterministic, because the records
needed by the test class are created immediately. Also avoid redundant
User::addToDatabase and ::saveSettings, these are already done by
TestUser.
- PageRestrictionTest: avoid Title::newFromID which is not guaranteed to
succeed if all pages have been deleted by that point. The second part
of the test was effectively doing the same thing as the first part, so
just remove it.
- WikiPageDbTest: avoid expensive page deletions in tearDown. These are
all unnecessary because the DB is cleaned up after each test (and
'page' is explicitly included in $tablesUsed). In fact, deleting test
pages as done there can be even worse than not doing anything, because
it creates log entries etc. Add page_restrictions to tablesUsed as
that's not truncated automatically.
- DBSiteStoreTest: testReset would fail when run on its own because it
depends on test sites being inserted in testGetSites. Make the test
add the sites it needs, so that they can safely be cleared between
test runs.
Change-Id: I1065fb3e8507b4b1a3bf185181f2f3059a97fd04
2023-08-11 17:50:20 +00:00
|
|
|
$this->editTrackerDoEdit( $user, $ts3 );
|
|
|
|
|
$this->editTrackerDoEdit( $user, $ts2 );
|
|
|
|
|
$this->editTrackerDoEdit( $user, $ts1 );
|
2021-05-11 02:16:20 +00:00
|
|
|
|
|
|
|
|
$this->assertSame( $ts1, $tracker->getFirstEditTimestamp( $user ) );
|
|
|
|
|
$this->assertSame( $ts3, $tracker->getLatestEditTimestamp( $user ) );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function testGetEditTimestamp_anon() {
|
|
|
|
|
$user = $this->getServiceContainer()->getUserFactory()
|
2022-03-16 04:08:00 +00:00
|
|
|
->newFromName( '127.0.0.1', UserRigorOptions::RIGOR_NONE );
|
2021-05-11 02:16:20 +00:00
|
|
|
$tracker = $this->getServiceContainer()->getUserEditTracker();
|
Fix tests not properly cleaning up the DB
- UserEditTrackerTest: determine whether to create the page based on
WikiPage::exists() instead of a method parameter. The current
implementation only works because the page is not deleted between test
runs, and the creation query is ignored. However, if the first call to
`editTrackerDoEdit` doesn't have $create=true, tests would fail. For
instance, testGetEditTimestamp would fail when run on its own because
the revision cannot be inserted if the page doesn't exist. Just use
WikiPage::exists instead of forcing callers to handle this correctly.
- DatabaseBlockTest: use addDBDataOnce instead of a DIY implementation.
This also makes the tests more deterministic, because the records
needed by the test class are created immediately. Also avoid redundant
User::addToDatabase and ::saveSettings, these are already done by
TestUser.
- PageRestrictionTest: avoid Title::newFromID which is not guaranteed to
succeed if all pages have been deleted by that point. The second part
of the test was effectively doing the same thing as the first part, so
just remove it.
- WikiPageDbTest: avoid expensive page deletions in tearDown. These are
all unnecessary because the DB is cleaned up after each test (and
'page' is explicitly included in $tablesUsed). In fact, deleting test
pages as done there can be even worse than not doing anything, because
it creates log entries etc. Add page_restrictions to tablesUsed as
that's not truncated automatically.
- DBSiteStoreTest: testReset would fail when run on its own because it
depends on test sites being inserted in testGetSites. Make the test
add the sites it needs, so that they can safely be cleared between
test runs.
Change-Id: I1065fb3e8507b4b1a3bf185181f2f3059a97fd04
2023-08-11 17:50:20 +00:00
|
|
|
$this->editTrackerDoEdit( $user, '20200101000000' );
|
2021-05-11 02:16:20 +00:00
|
|
|
$this->assertFalse( $tracker->getFirstEditTimestamp( $user ) );
|
|
|
|
|
$this->assertFalse( $tracker->getLatestEditTimestamp( $user ) );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function testClearUserEditCache() {
|
|
|
|
|
$user = $this->getMutableTestUser()->getUser();
|
|
|
|
|
$tracker = $this->getServiceContainer()->getUserEditTracker();
|
|
|
|
|
$this->assertSame( 0, $tracker->getUserEditCount( $user ) );
|
|
|
|
|
$this->setDbEditCount( $user, 1 );
|
|
|
|
|
$this->assertSame( 0, $tracker->getUserEditCount( $user ) );
|
|
|
|
|
$tracker->clearUserEditCache( $user );
|
|
|
|
|
$this->assertSame( 1, $tracker->getUserEditCount( $user ) );
|
|
|
|
|
}
|
|
|
|
|
|
2021-04-24 04:54:48 +00:00
|
|
|
public function testIncrementUserEditCount() {
|
|
|
|
|
$tracker = $this->getServiceContainer()->getUserEditTracker();
|
|
|
|
|
$user = $this->getMutableTestUser()->getUser();
|
|
|
|
|
|
|
|
|
|
$editCountStart = $tracker->getUserEditCount( $user );
|
|
|
|
|
|
|
|
|
|
$this->db->startAtomic( __METHOD__ ); // let deferred updates queue up
|
|
|
|
|
|
|
|
|
|
$tracker->incrementUserEditCount( $user );
|
|
|
|
|
$this->assertSame(
|
|
|
|
|
1,
|
|
|
|
|
DeferredUpdates::pendingUpdatesCount(),
|
|
|
|
|
'Update queued for registered user'
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
$tracker->incrementUserEditCount( UserIdentityValue::newAnonymous( '1.1.1.1' ) );
|
|
|
|
|
$this->assertSame(
|
|
|
|
|
1,
|
|
|
|
|
DeferredUpdates::pendingUpdatesCount(),
|
|
|
|
|
'No update queued for anonymous user'
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
$this->db->endAtomic( __METHOD__ ); // run deferred updates
|
|
|
|
|
$this->assertSame(
|
|
|
|
|
0,
|
|
|
|
|
DeferredUpdates::pendingUpdatesCount(),
|
2021-11-21 19:13:24 +00:00
|
|
|
'deferred updates ran'
|
2021-04-24 04:54:48 +00:00
|
|
|
);
|
|
|
|
|
|
|
|
|
|
$editCountEnd = $tracker->getUserEditCount( $user );
|
|
|
|
|
$this->assertSame(
|
|
|
|
|
1,
|
|
|
|
|
$editCountEnd - $editCountStart,
|
|
|
|
|
'Edit count was incremented'
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2021-06-15 08:08:33 +00:00
|
|
|
public function testManualCache() {
|
|
|
|
|
// Make sure manually setting the cached value overrides the database, in case
|
|
|
|
|
// User::loadFromRow() is called with a row containing user_editcount that is
|
|
|
|
|
// different from the actual database value, the row takes precedence
|
|
|
|
|
$user = new UserIdentityValue( 123, __METHOD__ );
|
|
|
|
|
$this->setDbEditCount( $user, 5 );
|
|
|
|
|
|
|
|
|
|
$tracker = $this->getServiceContainer()->getUserEditTracker();
|
|
|
|
|
$tracker->setCachedUserEditCount( $user, 10 );
|
|
|
|
|
$this->assertSame( 10, $tracker->getUserEditCount( $user ) );
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-11 02:16:20 +00:00
|
|
|
}
|