Rev 1 | Ir a la última revisión | Autoría | Comparar con el anterior | Ultima modificación | Ver Log |
<?php// This file is part of Moodle - http://moodle.org///// Moodle is free software: you can redistribute it and/or modify// it under the terms of the GNU General Public License as published by// the Free Software Foundation, either version 3 of the License, or// (at your option) any later version.//// Moodle is distributed in the hope that it will be useful,// but WITHOUT ANY WARRANTY; without even the implied warranty of// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the// GNU General Public License for more details.//// You should have received a copy of the GNU General Public License// along with Moodle. If not, see <http://www.gnu.org/licenses/>.namespace core\task;/*** This file contains the unit tests for the database task logger.** @package core* @category test* @copyright 2018 Andrew Nicols <andrew@nicols.co.uk>* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later*/class database_logger_test extends \advanced_testcase {/*** @var \moodle_database The original database prior to mocking*/protected $DB;/*** Setup to backup the database before mocking.*/public function setUp(): void {global $DB;$this->DB = $DB;}/*** Tear down to unmock the database where it was mocked.*/public function tearDown(): void {global $DB;$DB = $this->DB;$this->DB = null;}/*** Ensure that store_log_for_task works with a passing scheduled task.*/public function test_store_log_for_task_scheduled(): void {global $DB;$this->resetAfterTest();$endtime = microtime(true);$starttime = $endtime - 4;$logdir = make_request_directory();$logpath = "{$logdir}/log.txt";file_put_contents($logpath, 'Example content');$task = new \core\task\cache_cron_task();database_logger::store_log_for_task($task, $logpath, false, 1, 2, $starttime, $endtime);$logs = $DB->get_records('task_log');$this->assertCount(1, $logs);$log = reset($logs);$this->assertEquals(file_get_contents($logpath), $log->output);$this->assertEquals(0, $log->result);$this->assertEquals(database_logger::TYPE_SCHEDULED, $log->type);$this->assertEquals('core\task\cache_cron_task', $log->classname);$this->assertEquals(0, $log->userid);}/*** Ensure that store_log_for_task works with a passing adhoc task.*/public function test_store_log_for_task_adhoc(): void {global $DB;$this->resetAfterTest();$endtime = microtime(true);$starttime = $endtime - 4;$logdir = make_request_directory();$logpath = "{$logdir}/log.txt";file_put_contents($logpath, 'Example content');$task = $this->getMockBuilder(\core\task\adhoc_task::class)->onlyMethods(['get_component', 'execute'])->getMock();$task->method('get_component')->willReturn('core_test');database_logger::store_log_for_task($task, $logpath, false, 1, 2, $starttime, $endtime);$logs = $DB->get_records('task_log');$this->assertCount(1, $logs);$log = reset($logs);$this->assertEquals(file_get_contents($logpath), $log->output);$this->assertEquals(0, $log->result);$this->assertEquals(database_logger::TYPE_ADHOC, $log->type);}/*** Ensure that store_log_for_task works with a failing scheduled task.*/public function test_store_log_for_task_failed_scheduled(): void {global $DB;$this->resetAfterTest();$endtime = microtime(true);$starttime = $endtime - 4;$logdir = make_request_directory();$logpath = "{$logdir}/log.txt";file_put_contents($logpath, 'Example content');$task = new \core\task\cache_cron_task();database_logger::store_log_for_task($task, $logpath, true, 1, 2, $starttime, $endtime);$logs = $DB->get_records('task_log');$this->assertCount(1, $logs);$log = reset($logs);$this->assertEquals(file_get_contents($logpath), $log->output);$this->assertEquals(1, $log->result);$this->assertEquals(database_logger::TYPE_SCHEDULED, $log->type);$this->assertEquals('core\task\cache_cron_task', $log->classname);$this->assertEquals(0, $log->userid);}/*** Ensure that store_log_for_task works with a failing adhoc task.*/public function test_store_log_for_task_failed_adhoc(): void {global $DB;$this->resetAfterTest();$endtime = microtime(true);$starttime = $endtime - 4;$logdir = make_request_directory();$logpath = "{$logdir}/log.txt";file_put_contents($logpath, 'Example content');$task = $this->getMockBuilder(\core\task\adhoc_task::class)->onlyMethods(['get_component', 'execute'])->getMock();$task->method('get_component')->willReturn('core_test');database_logger::store_log_for_task($task, $logpath, true, 1, 2, $starttime, $endtime);$logs = $DB->get_records('task_log');$this->assertCount(1, $logs);$log = reset($logs);$this->assertEquals(file_get_contents($logpath), $log->output);$this->assertEquals(1, $log->result);$this->assertEquals(database_logger::TYPE_ADHOC, $log->type);$this->assertEquals(0, $log->userid);}/*** Ensure that store_log_for_task works with a passing adhoc task run as a specific user.*/public function test_store_log_for_task_adhoc_userid(): void {global $DB;$this->resetAfterTest();$endtime = microtime(true);$starttime = $endtime - 4;$logdir = make_request_directory();$logpath = "{$logdir}/log.txt";file_put_contents($logpath, 'Example content');$task = $this->getMockBuilder(\core\task\adhoc_task::class)->onlyMethods(['get_component', 'execute', 'get_userid'])->getMock();$task->method('get_component')->willReturn('core_test');$task->method('get_userid')->willReturn(99);database_logger::store_log_for_task($task, $logpath, false, 1, 2, $starttime, $endtime);$logs = $DB->get_records('task_log');$this->assertCount(1, $logs);$log = reset($logs);$this->assertEquals(file_get_contents($logpath), $log->output);$this->assertEquals(0, $log->result);$this->assertEquals(database_logger::TYPE_ADHOC, $log->type);$this->assertEquals(99, $log->userid);}/*** Ensure that the delete_task_logs function performs necessary deletion tasks.** @dataProvider delete_task_logs_provider* @param mixed $ids*/public function test_delete_task_logs($ids): void {$DB = $this->mock_database();$DB->expects($this->once())->method('delete_records_list')->with($this->equalTo('task_log'),$this->equalTo('id'),$this->callback(function($deletedids) use ($ids) {sort($ids);$idvalues = array_values($deletedids);sort($idvalues);return $ids == $idvalues;}));database_logger::delete_task_logs($ids);}/*** Data provider for delete_task_logs tests.** @return array*/public function delete_task_logs_provider(): array {return [[[0],[1],[1, 2, 3, 4, 5],],];}/*** Ensure that the retention period applies correctly.*/public function test_cleanup_retention(): void {global $DB;$this->resetAfterTest();// Set a high value for task_logretainruns so that it does no interfere.set_config('task_logretainruns', 1000);// Create sample log data - 1 run per hour for 3 days - round down to the start of the hour to avoid time race conditions.$date = new \DateTime();$date->setTime($date->format('G'), 0);$baselogtime = $date->getTimestamp();for ($i = 0; $i < 3 * 24; $i++) {$task = new \core\task\cache_cron_task();$logpath = __FILE__;database_logger::store_log_for_task($task, $logpath, false, 1, 2, $date->getTimestamp(), $date->getTimestamp() + MINSECS);$date->sub(new \DateInterval('PT1H'));}// Initially there should be 72 runs.$this->assertCount(72, $DB->get_records('task_log'));// Note: We set the retention time to a period like DAYSECS minus an adjustment.// The adjustment is to account for the time taken during setup.// With a retention period of 2 * DAYSECS, there should only be 47-48 left.set_config('task_logretention', (2 * DAYSECS) - (time() - $baselogtime));\core\task\database_logger::cleanup();$this->assertGreaterThanOrEqual(47, $DB->count_records('task_log'));$this->assertLessThanOrEqual(48, $DB->count_records('task_log'));// The oldest should be no more than 48 hours old.$oldest = $DB->get_records('task_log', [], 'timestart DESC', 'timestart', 0, 1);$oldest = reset($oldest);$this->assertGreaterThan(time() - (48 * DAYSECS), $oldest->timestart);// With a retention period of DAYSECS, there should only be 23 left.set_config('task_logretention', DAYSECS - (time() - $baselogtime));\core\task\database_logger::cleanup();$this->assertGreaterThanOrEqual(23, $DB->count_records('task_log'));$this->assertLessThanOrEqual(24, $DB->count_records('task_log'));// The oldest should be no more than 24 hours old.$oldest = $DB->get_records('task_log', [], 'timestart DESC', 'timestart', 0, 1);$oldest = reset($oldest);$this->assertGreaterThan(time() - (24 * DAYSECS), $oldest->timestart);// With a retention period of 0.5 DAYSECS, there should only be 11 left.set_config('task_logretention', (DAYSECS / 2) - (time() - $baselogtime));\core\task\database_logger::cleanup();$this->assertGreaterThanOrEqual(11, $DB->count_records('task_log'));$this->assertLessThanOrEqual(12, $DB->count_records('task_log'));// The oldest should be no more than 12 hours old.$oldest = $DB->get_records('task_log', [], 'timestart DESC', 'timestart', 0, 1);$oldest = reset($oldest);$this->assertGreaterThan(time() - (12 * DAYSECS), $oldest->timestart);}/*** Ensure that the run-count retention applies.*/public function test_cleanup_retainruns(): void {global $DB;$this->resetAfterTest();// Set a high value for task_logretention so that it does not interfere.set_config('task_logretention', YEARSECS);// Create sample log data - 2 tasks, once per hour for 3 days.$date = new \DateTime();$date->setTime($date->format('G'), 0);$firstdate = $date->getTimestamp();for ($i = 0; $i < 3 * 24; $i++) {$task = new \core\task\cache_cron_task();$logpath = __FILE__;database_logger::store_log_for_task($task, $logpath, false, 1, 2, $date->getTimestamp(), $date->getTimestamp() + MINSECS);$task = new \core\task\badges_cron_task();$logpath = __FILE__;database_logger::store_log_for_task($task, $logpath, false, 1, 2, $date->getTimestamp(), $date->getTimestamp() + MINSECS);$date->sub(new \DateInterval('PT1H'));}$lastdate = $date->getTimestamp();// Initially there should be 144 runs - 72 for each task.$this->assertEquals(144, $DB->count_records('task_log'));$this->assertEquals(72, $DB->count_records('task_log', ['classname' => \core\task\cache_cron_task::class]));$this->assertEquals(72, $DB->count_records('task_log', ['classname' => \core\task\badges_cron_task::class]));// Grab the records for comparison.$cachecronrecords = array_values($DB->get_records('task_log', ['classname' => \core\task\cache_cron_task::class], 'timestart DESC'));$badgescronrecords = array_values($DB->get_records('task_log', ['classname' => \core\task\badges_cron_task::class], 'timestart DESC'));// Configured to retain 144 should have no effect.set_config('task_logretainruns', 144);\core\task\database_logger::cleanup();$this->assertEquals(144, $DB->count_records('task_log'));$this->assertEquals(72, $DB->count_records('task_log', ['classname' => \core\task\cache_cron_task::class]));$this->assertEquals(72, $DB->count_records('task_log', ['classname' => \core\task\badges_cron_task::class]));// The list of records should be identical.$this->assertEquals($cachecronrecords, array_values($DB->get_records('task_log', ['classname' => \core\task\cache_cron_task::class], 'timestart DESC')));$this->assertEquals($badgescronrecords, array_values($DB->get_records('task_log', ['classname' => \core\task\badges_cron_task::class], 'timestart DESC')));// Configured to retain 72 should have no effect either.set_config('task_logretainruns', 72);\core\task\database_logger::cleanup();$this->assertEquals(144, $DB->count_records('task_log'));$this->assertEquals(72, $DB->count_records('task_log', ['classname' => \core\task\cache_cron_task::class]));$this->assertEquals(72, $DB->count_records('task_log', ['classname' => \core\task\badges_cron_task::class]));// The list of records should now only contain the first 72 of each.$this->assertEquals(array_slice($cachecronrecords, 0, 72),array_values($DB->get_records('task_log', ['classname' => \core\task\cache_cron_task::class], 'timestart DESC')));$this->assertEquals(array_slice($badgescronrecords, 0, 72),array_values($DB->get_records('task_log', ['classname' => \core\task\badges_cron_task::class], 'timestart DESC')));// Configured to only retain 24 should bring that down to a total of 48, or 24 each.set_config('task_logretainruns', 24);\core\task\database_logger::cleanup();$this->assertEquals(48, $DB->count_records('task_log'));$this->assertEquals(24, $DB->count_records('task_log', ['classname' => \core\task\cache_cron_task::class]));$this->assertEquals(24, $DB->count_records('task_log', ['classname' => \core\task\badges_cron_task::class]));// The list of records should now only contain the first 24 of each.$this->assertEquals(array_slice($cachecronrecords, 0, 24),array_values($DB->get_records('task_log', ['classname' => \core\task\cache_cron_task::class], 'timestart DESC')));$this->assertEquals(array_slice($badgescronrecords, 0, 24),array_values($DB->get_records('task_log', ['classname' => \core\task\badges_cron_task::class], 'timestart DESC')));// Configured to only retain 5 should bring that down to a total of 10, or 5 each.set_config('task_logretainruns', 5);\core\task\database_logger::cleanup();$this->assertEquals(10, $DB->count_records('task_log'));$this->assertEquals(5, $DB->count_records('task_log', ['classname' => \core\task\cache_cron_task::class]));$this->assertEquals(5, $DB->count_records('task_log', ['classname' => \core\task\badges_cron_task::class]));// The list of records should now only contain the first 5 of each.$this->assertEquals(array_slice($cachecronrecords, 0, 5),array_values($DB->get_records('task_log', ['classname' => \core\task\cache_cron_task::class], 'timestart DESC')));$this->assertEquals(array_slice($badgescronrecords, 0, 5),array_values($DB->get_records('task_log', ['classname' => \core\task\badges_cron_task::class], 'timestart DESC')));// Configured to only retain 0 should bring that down to none.set_config('task_logretainruns', 0);\core\task\database_logger::cleanup();$this->assertEquals(0, $DB->count_records('task_log'));}/*** Ensure that the retention period applies correctly when combined with the run count retention.*/public function test_cleanup_combined(): void {global $DB;$this->resetAfterTest();// Calculate date to be used for logs, starting from current time rounded down to nearest hour.$date = new \DateTime();$date->setTime($date->format('G'), 0);$baselogtime = $date->getTimestamp();// Create sample log data - 2 tasks, once per hour for 3 days.for ($i = 0; $i < 3 * 24; $i++) {$task = new \core\task\cache_cron_task();$logpath = __FILE__;database_logger::store_log_for_task($task, $logpath, false, 1, 2, $date->getTimestamp(), $date->getTimestamp() + MINSECS);$task = new \core\task\badges_cron_task();$logpath = __FILE__;database_logger::store_log_for_task($task, $logpath, false, 1, 2, $date->getTimestamp(), $date->getTimestamp() + MINSECS);$date->sub(new \DateInterval('PT1H'));}// Initially there should be 144 runs - 72 for each task.$this->assertEquals(144, $DB->count_records('task_log'));$this->assertEquals(72, $DB->count_records('task_log', ['classname' => \core\task\cache_cron_task::class]));$this->assertEquals(72, $DB->count_records('task_log', ['classname' => \core\task\badges_cron_task::class]));// Note: We set the retention time to a period like DAYSECS minus an adjustment.// The adjustment is to account for the difference between current time and baselogtime.// With a retention period of 2 * DAYSECS, there should only be 96 left.// The run count is a higher number so it will have no effect.set_config('task_logretention', time() - ($baselogtime - (2 * DAYSECS)) - 1);set_config('task_logretainruns', 50);\core\task\database_logger::cleanup();$this->assertEquals(96, $DB->count_records('task_log'));$this->assertEquals(48, $DB->count_records('task_log', ['classname' => \core\task\cache_cron_task::class]));$this->assertEquals(48, $DB->count_records('task_log', ['classname' => \core\task\badges_cron_task::class]));// We should retain the most recent 48 of each task, so the oldest will be 47 hours old.$oldest = $DB->get_records('task_log', [], 'timestart ASC', 'timestart', 0, 1);$oldest = reset($oldest);$this->assertEquals($baselogtime - (47 * HOURSECS), $oldest->timestart);// Reducing the retain runs count to 10 should reduce the total logs to 20, overriding the time constraint.set_config('task_logretainruns', 10);\core\task\database_logger::cleanup();$this->assertEquals(20, $DB->count_records('task_log'));$this->assertEquals(10, $DB->count_records('task_log', ['classname' => \core\task\cache_cron_task::class]));$this->assertEquals(10, $DB->count_records('task_log', ['classname' => \core\task\badges_cron_task::class]));// We should retain the most recent 10 of each task, so the oldest will be 9 hours old.$oldest = $DB->get_records('task_log', [], 'timestart ASC', 'timestart', 0, 1);$oldest = reset($oldest);$this->assertEquals($baselogtime - (9 * HOURSECS), $oldest->timestart);}/*** Mock the database.*/protected function mock_database() {global $DB;$DB = $this->getMockBuilder(\moodle_database::class)->getMock();$DB->method('get_record')->willReturn((object) []);return $DB;}}