1
0
قرینه از https://github.com/matomo-org/matomo.git synced 2025-08-24 16:07:37 +00:00
Files
matomo/tests/PHPUnit/Integration/Archive/ArchivePurgerTest.php
Nathan Gavin ff9e7ac212 Provide task to find/purge broken archives (#23421)
* Add core functionality to purge broken archives

* Add dummy archives to test broken archives purger

* Optimise SQL query

* Add cli for purging broken archives

* PHPCS fix

* PHPCS fix

* implement tests covering new cli

* Fill out cli tests

* PHPCS fix

* Update tests

* clean up some code, use array shorthand

* Use Month period rather than Dates

* Update tests to work with Month period

* clean up unused code

* remove unnecessary today variable for tests

* PHPCS fix

* Fix emergent issues from using Date:: in tests

* add early return

* simplify flow by removing unnecessary try catch

* Update cli command to use Months

* fix task test broken by new task

* fix phrasing on command messages

* Add check for month formatting on cli

* clean up

* PHPCs

* replace wrong variable

---------

Co-authored-by: Michal Kleiner <michal@innocraft.com>
2025-07-23 08:35:22 +12:00

231 خطوط
9.4 KiB
PHP

<?php
/**
* Matomo - free/libre analytics platform
*
* @link https://matomo.org
* @license https://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
*/
namespace Piwik\Tests\Integration\Archive;
use Piwik\Archive\ArchivePurger;
use Piwik\Config;
use Piwik\DataAccess\ArchiveTableCreator;
use Piwik\Date;
use Piwik\Db;
use Piwik\Period\Month;
use Piwik\Tests\Fixtures\RawArchiveDataWithTempAndInvalidated;
use Piwik\Tests\Framework\Fixture;
use Piwik\Tests\Framework\TestCase\IntegrationTestCase;
use Piwik\Segment;
/**
* @group ArchivePurgerTest
* @group Core
*/
class ArchivePurgerTest extends IntegrationTestCase
{
/**
* @var RawArchiveDataWithTempAndInvalidated
*/
public static $fixture;
/**
* @var ArchivePurger
*/
private $archivePurger;
/**
* @var Date
*/
private $january;
/**
* @var Date
*/
private $february;
public function setUp(): void
{
parent::setUp();
$this->january = self::$fixture->january;
$this->february = self::$fixture->february;
$this->archivePurger = new ArchivePurger();
$this->archivePurger->setTodayDate(Date::factory('2015-02-27'));
$this->archivePurger->setYesterdayDate(Date::factory('2015-02-26'));
$this->archivePurger->setNow(Date::factory('2015-02-27 08:00:00')->getTimestamp());
$this->configureCustomRangePurging();
// assert test data was added correctly
self::$fixture->assertInvalidatedArchivesNotPurged(self::$fixture->january);
self::$fixture->assertInvalidatedArchivesNotPurged(self::$fixture->february);
}
public function testPurgeOutdatedArchivesPurgesCorrectTemporaryArchivesWhileKeepingNewerTemporaryArchivesWithBrowserTriggeringEnabled()
{
$this->enableBrowserTriggeredArchiving();
$deletedRowCount = $this->archivePurger->purgeOutdatedArchives($this->february);
self::$fixture->assertTemporaryArchivesPurged($browserTriggeringEnabled = true, $this->february);
self::$fixture->assertErrorInProgressArchivesPurged($browserTriggeringEnabled = true, $this->february);
self::$fixture->assertCustomRangesNotPurged($this->february, $includeTemporary = false);
self::$fixture->assertErrorInProgressArchivedNotPurged($this->february, $includeRecentInProgress = false);
self::$fixture->assertTemporaryArchivesNotPurged($this->january);
self::$fixture->assertErrorInProgressArchivesNotPurged($this->january);
$this->assertEquals(11 * RawArchiveDataWithTempAndInvalidated::ROWS_PER_ARCHIVE, $deletedRowCount);
$this->checkNoDuplicateArchives();
}
public function testPurgeBrokenArchivesPurgesOnlyBrokenArchives()
{
$this->enableBrowserTriggeredArchiving();
$deletedRowCount = $this->archivePurger->purgeBrokenArchives(new Month($this->february));
self::$fixture->assertBrokenArchivesWithoutDoneFlagPurged($this->february);
self::$fixture->assertCustomRangesNotPurged($this->february, $includeTemporary = false);
self::$fixture->assertErrorInProgressArchivedNotPurged($this->february, $includeRecentInProgress = false);
self::$fixture->assertTemporaryArchivesNotPurged($this->january);
self::$fixture->assertErrorInProgressArchivesNotPurged($this->january);
$this->assertEquals(4 * RawArchiveDataWithTempAndInvalidated::ROWS_PER_ARCHIVE, $deletedRowCount);
$this->checkNoDuplicateArchives();
}
public function testPurgeOutdatedArchivesPurgesCorrectTemporaryArchivesWhileKeepingNewerTemporaryArchivesWithBrowserTriggeringDisabled()
{
$this->disableBrowserTriggeredArchiving();
$deletedRowCount = $this->archivePurger->purgeOutdatedArchives($this->february);
self::$fixture->assertTemporaryArchivesPurged($browserTriggeringEnabled = false, $this->february);
self::$fixture->assertErrorInProgressArchivesPurged($browserTriggeringEnabled = false, $this->february);
self::$fixture->assertCustomRangesNotPurged($this->february);
self::$fixture->assertErrorInProgressArchivedNotPurged($this->february);
self::$fixture->assertTemporaryArchivesNotPurged($this->january);
self::$fixture->assertErrorInProgressArchivesNotPurged($this->january);
$this->assertEquals(7 * RawArchiveDataWithTempAndInvalidated::ROWS_PER_ARCHIVE, $deletedRowCount);
$this->checkNoDuplicateArchives();
}
public function testPurgeInvalidatedArchivesFromPurgesAllInvalidatedArchivesAndMarksDatesAndSitesAsInvalidated()
{
$deletedRowCount = $this->archivePurger->purgeInvalidatedArchivesFrom($this->february);
self::$fixture->assertInvalidatedArchivesPurged($this->february);
self::$fixture->assertInvalidatedArchivesNotPurged($this->january);
self::$fixture->assertPartialArchivesPurged($this->february);
$this->assertEquals(10 * RawArchiveDataWithTempAndInvalidated::ROWS_PER_ARCHIVE, $deletedRowCount);
$this->checkNoDuplicateArchives();
}
public function testPurgeArchivesWithPeriodRangePurgesAllRangeArchives()
{
$deletedRowCount = $this->archivePurger->purgeArchivesWithPeriodRange($this->february);
self::$fixture->assertCustomRangesPurged($this->february);
self::$fixture->assertCustomRangesNotPurged($this->january);
$this->assertEquals(3 * RawArchiveDataWithTempAndInvalidated::ROWS_PER_ARCHIVE, $deletedRowCount);
}
public function testPurgeNoSiteArchivesPurgesAllNoSiteArchives()
{
//Create two websites (IDs #1 and #2). Existing rows for website #3 will be invalid.
Fixture::createWebsite($this->january);
Fixture::createWebsite($this->january);
//There are 5 rows for website #3 and 1. We leave the other two because they're before our purge threshold.
$deletedRowCount = $this->archivePurger->purgeDeletedSiteArchives($this->january);
$this->assertEquals(7 * RawArchiveDataWithTempAndInvalidated::ROWS_PER_ARCHIVE, $deletedRowCount);
self::$fixture->assertArchivesDoNotExist(array(3, 7, 10, 13, 19), $this->january);
}
public function testPurgeNoSegmentArchivesPurgesSegmentForAppropriateSitesOnly()
{
//Extra data set with segment and plugin archives
self::$fixture->insertSegmentArchives($this->january);
$segmentsToDelete = [
['definition' => '9876fedc5432abcd', 'enable_only_idsite' => 0, 'hash' => Segment::getSegmentHash('9876fedc5432abcd')],
['definition' => 'hash3', 'enable_only_idsite' => 0, 'hash' => Segment::getSegmentHash('hash3')],
// This segment also has archives for idsite = 1, which will be retained
['definition' => 'abcd1234abcd5678', 'enable_only_idsite' => 2, 'hash' => Segment::getSegmentHash('abcd1234abcd5678')]
];
//Archive #29 also has a deleted segment but it's before the purge threshold so it stays for now.
$deletedRowCount = $this->archivePurger->purgeDeletedSegmentArchives($this->january, $segmentsToDelete);
$this->assertEquals(4 * RawArchiveDataWithTempAndInvalidated::ROWS_PER_ARCHIVE, $deletedRowCount);
self::$fixture->assertArchivesDoNotExist(array(26, 27, 28, 32), $this->january);
}
public function testPurgeNoSegmentArchivesPreservesSingleSiteSegmentArchivesForDeletedAllSiteSegment()
{
// Extra data set with segment and plugin archives
self::$fixture->insertSegmentArchives($this->january);
$segmentsToDelete = [
// This segment also has archives for idsite = 1, which will be retained
['definition' => 'abcd1234abcd5678', 'enable_only_idsite' => 0, 'idsites_to_preserve' => [2], 'hash' => Segment::getSegmentHash('abcd1234abcd5678')]
];
// Archives for idsite=1 should be purged, but those for idsite=2 can stay
$deletedRowCount = $this->archivePurger->purgeDeletedSegmentArchives($this->january, $segmentsToDelete);
$this->assertEquals(2 * RawArchiveDataWithTempAndInvalidated::ROWS_PER_ARCHIVE, $deletedRowCount);
self::$fixture->assertArchivesDoNotExist(array(24, 25), $this->january);
}
public function testPurgeNoSegmentArchivesBlankSegmentName()
{
$segmentsToDelete = array(
array('definition' => '', 'enable_only_idsite' => 0)
);
// Should not purge all the "done%" archives!
$deletedRowCount = $this->archivePurger->purgeDeletedSegmentArchives($this->january, $segmentsToDelete);
$this->assertEquals(0, $deletedRowCount);
}
private function configureCustomRangePurging()
{
Config::getInstance()->General['purge_date_range_archives_after_X_days'] = 3;
}
private function enableBrowserTriggeredArchiving()
{
Config::getInstance()->General['enable_browser_archiving_triggering'] = 1;
}
private function disableBrowserTriggeredArchiving()
{
Config::getInstance()->General['enable_browser_archiving_triggering'] = 0;
}
private function checkNoDuplicateArchives()
{
$duplicateRows = Db::fetchAll("SELECT idsite, date1, date2, period, name, COUNT(*) AS count FROM "
. ArchiveTableCreator::getNumericTable(Date::factory($this->february))
. " WHERE name LIKE 'done%'
GROUP BY idarchive, idsite, date1, date2, period, name
HAVING count > 1");
$this->assertEmpty($duplicateRows);
}
}
ArchivePurgerTest::$fixture = new RawArchiveDataWithTempAndInvalidated();