diff --git a/lib/classes/task/delete_unconfirmed_users_task.php b/lib/classes/task/delete_unconfirmed_users_task.php index 40dfe8ef1ef8a..9cc4c416b0f8a 100644 --- a/lib/classes/task/delete_unconfirmed_users_task.php +++ b/lib/classes/task/delete_unconfirmed_users_task.php @@ -27,6 +27,7 @@ * Simple task to delete user accounts for users who have not confirmed in time. */ class delete_unconfirmed_users_task extends scheduled_task { + use stored_progress_task_trait; /** * Get a descriptive name for this task (shown to admins). @@ -48,14 +49,23 @@ public function execute() { // Delete users who haven't confirmed within required period. if (!empty($CFG->deleteunconfirmed)) { + $this->start_stored_progress(); $cuttime = $timenow - ($CFG->deleteunconfirmed * 3600); - $rs = $DB->get_recordset_sql ("SELECT * - FROM {user} - WHERE confirmed = 0 AND timecreated > 0 - AND timecreated < ? AND deleted = 0", array($cuttime)); + $selectcount = "SELECT COUNT(*)"; + $select = "SELECT *"; + $sql = " + FROM {user} + WHERE confirmed = 0 AND timecreated > 0 + AND timecreated < ? AND deleted = 0"; + $params = [$cuttime]; + $count = $DB->count_records_sql($selectcount . $sql, $params); + $rs = $DB->get_recordset_sql($select . $sql, $params); + $processed = 0; foreach ($rs as $user) { delete_user($user); - mtrace(" Deleted unconfirmed user ".fullname($user, true)." ($user->id)"); + $message = " Deleted unconfirmed user ".fullname($user, true)." ($user->id)"; + $processed++; + $this->progress->update($processed, $count, $message); } $rs->close(); } diff --git a/lib/classes/task/stored_progress_task_trait.php b/lib/classes/task/stored_progress_task_trait.php index 6cf2e32991629..eaf29742731cb 100644 --- a/lib/classes/task/stored_progress_task_trait.php +++ b/lib/classes/task/stored_progress_task_trait.php @@ -26,7 +26,7 @@ */ trait stored_progress_task_trait { - /** @var stored_progress_bar|null $progress */ + /** @var \core\stored_progress_bar|null $progress */ protected $progress = null; /** diff --git a/lib/testing/generator/data_generator.php b/lib/testing/generator/data_generator.php index 4b8c42ae8fd1f..766ecdc4cc66f 100644 --- a/lib/testing/generator/data_generator.php +++ b/lib/testing/generator/data_generator.php @@ -1530,6 +1530,41 @@ public function create_user_course_lastaccess(\stdClass $user, \stdClass $course return $DB->get_record('user_lastaccess', ['id' => $recordid], '*', MUST_EXIST); } + /** + * Generate a stored_progress record and return the ID. + * + * All fields are optional, required fields will be generated if not supplied. + * + * @param ?string $idnumber The unique ID Number for this stored progress. + * @param ?int $timestart The time progress was started, defaults to now. + * @param ?int $lastupdate The time the progress was last updated. + * @param ?float $percent The percentage progress so far. + * @param ?string $message An error message. + * @param ?bool $haserrored Whether the process has encountered an error. + * @return stdClass The record including the inserted id. + * @throws dml_exception + */ + public function create_stored_progress( + ?string $idnumber = null, + ?int $timestart = null, + ?int $lastupdate = null, + float $percent = 0.00, + ?string $message = null, + ?bool $haserrored = false, + ): stdClass { + global $DB; + $record = (object)[ + 'idnumber' => $idnumber ?? random_string(), + 'timestart' => $timestart ?? time(), + 'lastupdate' => $lastupdate, + 'percent' => $percent, + 'message' => $message, + 'haserrored' => $haserrored, + ]; + $record->id = $DB->insert_record('stored_progress', $record); + return $record; + } + /** * Gets a default generator for a given component. * diff --git a/lib/tests/stored_progress_bar_test.php b/lib/tests/stored_progress_bar_test.php new file mode 100644 index 0000000000000..8df720ede8ac0 --- /dev/null +++ b/lib/tests/stored_progress_bar_test.php @@ -0,0 +1,192 @@ +. +namespace core; + +/** + * Unit tests for \core\stored_progress_bar + * + * @package core + * @copyright 2024 onwards Catalyst IT EU {@link https://catalyst-eu.net} + * @author Mark Johnson + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + * @covers \core\stored_progress_bar + */ +class stored_progress_bar_test extends \advanced_testcase { + /** + * Test the progress bar initialisation. + * + * Creating a new stored progress bar object should set the idnumber, + * and not generate any output. + * + * @return void + */ + public function test_init(): void { + $idnumber = random_string(); + $progress = new stored_progress_bar($idnumber); + $this->assertEquals($idnumber, $progress->get_id()); + } + + /** + * Calling get_by_idnumber() fetches the correct record. + * + * @return void + * @throws \dml_exception + */ + public function test_get_by_idnumber(): void { + $this->resetAfterTest(); + $generator = $this->getDataGenerator(); + $progress1 = $generator->create_stored_progress(message: 'progress1'); + $progress2 = $generator->create_stored_progress(message: 'progress2'); + $progress3 = $generator->create_stored_progress(message: 'progress3'); + + $progressbar = stored_progress_bar::get_by_idnumber($progress2->idnumber); + $this->assertEquals('progress2', $progressbar->get_message()); + $progressbar = stored_progress_bar::get_by_idnumber($progress1->idnumber); + $this->assertEquals('progress1', $progressbar->get_message()); + $progressbar = stored_progress_bar::get_by_idnumber($progress3->idnumber); + $this->assertEquals('progress3', $progressbar->get_message()); + } + + /** + * Calling get_by_id() fetches the correct record. + * + * @return void + */ + public function test_get_by_id(): void { + $this->resetAfterTest(); + $generator = $this->getDataGenerator(); + $progress1 = $generator->create_stored_progress(); + $progress2 = $generator->create_stored_progress(); + $progress3 = $generator->create_stored_progress(); + + $progressbar = stored_progress_bar::get_by_id($progress2->id); + $this->assertEquals($progress2->idnumber, $progressbar->get_id()); + $progressbar = stored_progress_bar::get_by_id($progress1->id); + $this->assertEquals($progress1->idnumber, $progressbar->get_id()); + $progressbar = stored_progress_bar::get_by_id($progress3->id); + $this->assertEquals($progress3->idnumber, $progressbar->get_id()); + } + + /** + * Calling error() method updates the record with the new message and haserrored = true. + * + * @return void + */ + public function test_error(): void { + $this->resetAfterTest(); + $generator = $this->getDataGenerator(); + $progress = $generator->create_stored_progress(); + + $originalprogressbar = stored_progress_bar::get_by_id($progress->id); + $originalprogressbar->auto_update(false); + $this->assertEmpty($originalprogressbar->get_message()); + $this->assertFalse($originalprogressbar->get_haserrored()); + + $message = 'There was an error'; + $originalprogressbar->error($message); + + $updatedprogressbar = stored_progress_bar::get_by_id($progress->id); + $this->assertEquals($message, $updatedprogressbar->get_message()); + $this->assertTrue($updatedprogressbar->get_haserrored()); + } + + /** + * Calling start() replaces the existing record with a new one for the same idnumber. + */ + public function test_start(): void { + $this->resetAfterTest(); + $generator = $this->getDataGenerator(); + $originalprogress = $generator->create_stored_progress(); + + $progressbar = stored_progress_bar::get_by_id($originalprogress->id); + $this->assertNotNull($progressbar); + $this->assertEquals($originalprogress->idnumber, $progressbar->get_id()); + + $newid = $progressbar->start(); + + $oldprogressbar = stored_progress_bar::get_by_id($originalprogress->id); + $this->assertNull($oldprogressbar); + + $newprogressbar = stored_progress_bar::get_by_id($newid); + $this->assertNotNull($newprogressbar); + $this->assertEquals($originalprogress->idnumber, $newprogressbar->get_id()); + } + + /** + * Calling convert_to_idnumber() returns a valid idnumber. + * + * Leading backslashes are stripped from the class name, and any disallowed characters + * (any except lower-case letters, numbers and underscores) are replaced with underscores. + * The result is then concatenated with an underscore and the id argument. + * + * @return void + */ + public function test_convert_to_idnumber(): void { + $classname = '\\foo\\bar\\class-1_Name'; + $id = rand(1, 10); + + $idnumber = stored_progress_bar::convert_to_idnumber($classname, $id); + $this->assertEquals('foo_bar_class_1__ame_' . $id, $idnumber); + } + + /** + * Calling get_timeout() returns the global progresspollinterval setting, or 5 by default. + * + * @return void + */ + public function test_get_timeout(): void { + global $CFG; + $this->resetAfterTest(); + + $this->assertEquals(5, stored_progress_bar::get_timeout()); + $progresspollinterval = rand(10, 20); + $CFG->progresspollinterval = $progresspollinterval; + $this->assertEquals($progresspollinterval, stored_progress_bar::get_timeout()); + + } + + /** + * Calling export_for_template() returns the current values for rendering the progress bar. + */ + public function test_export_for_template(): void { + global $PAGE; + $this->resetAfterTest(); + $generator = $this->getDataGenerator(); + $timenow = time(); + $progress = $generator->create_stored_progress( + 'foo_bar_123', + $timenow - 10, + $timenow - 1, + 50.00, + 'error', + true + ); + + $progressbar = stored_progress_bar::get_by_id($progress->id); + + $templatecontext = $progressbar->export_for_template($PAGE->get_renderer('core')); + + $this->assertEquals([ + 'id' => $progress->id, + 'idnumber' => $progress->idnumber, + 'width' => 0, + 'class' => 'stored-progress-bar', + 'value' => $progress->percent, + 'message' => $progress->message, + 'error' => $progress->haserrored, + ], $templatecontext); + } +} diff --git a/lib/tests/task/stored_progress_bar_cleanup_task_test.php b/lib/tests/task/stored_progress_bar_cleanup_task_test.php new file mode 100644 index 0000000000000..4d134cc9dce90 --- /dev/null +++ b/lib/tests/task/stored_progress_bar_cleanup_task_test.php @@ -0,0 +1,50 @@ +. +namespace core\task; + +use core\stored_progress_bar; + +/** + * Unit tests for stored_progress_bar_cleanup + * + * @package core + * @copyright 2024 onwards Catalyst IT EU {@link https://catalyst-eu.net} + * @author Mark Johnson + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + * @covers core\task\stored_progress_bar_cleanup + */ +class stored_progress_bar_cleanup_task_test extends \advanced_testcase { + /** + * Clean up stored_progress records that were last updated over 24 hours ago. + */ + public function test_execute(): void { + $this->resetAfterTest(); + $generator = $this->getDataGenerator(); + $neverupdated = $generator->create_stored_progress(); + $updatednow = $generator->create_stored_progress(lastupdate: time()); + $updated23hours = $generator->create_stored_progress(lastupdate: time() - (HOURSECS * 23)); + $updated24hours = $generator->create_stored_progress(lastupdate: time() - DAYSECS - 1); + + $task = new stored_progress_bar_cleanup_task(); + $this->expectOutputString('Deleted old stored_progress records' . PHP_EOL); + $task->execute(); + + $this->assertNotNull(stored_progress_bar::get_by_id($neverupdated->id)); + $this->assertNotNull(stored_progress_bar::get_by_id($updatednow->id)); + $this->assertNotNull(stored_progress_bar::get_by_id($updated23hours->id)); + $this->assertNull(stored_progress_bar::get_by_id($updated24hours->id)); + } +}