public function test_async_section_deletion_hook_implemented() { // Async section deletion (provided section contains modules), depends on the 'true' being returned by at least one plugin // implementing the 'course_module_adhoc_deletion_recommended' hook. In core, is implemented by the course recyclebin, // which will only return true if the plugin is enabled. To make sure async deletion occurs, this test enables recyclebin. global $DB, $USER; $this->resetAfterTest(true); $this->setAdminUser(); // Ensure recyclebin is enabled. set_config('coursebinenable', true, 'tool_recyclebin'); // Create course, module and context. $generator = $this->getDataGenerator(); $course = $generator->create_course(['numsections' => 4, 'format' => 'topics'], ['createsections' => true]); $assign0 = $generator->create_module('assign', ['course' => $course, 'section' => 2]); $assign1 = $generator->create_module('assign', ['course' => $course, 'section' => 2]); $assign2 = $generator->create_module('assign', ['course' => $course, 'section' => 2]); $assign3 = $generator->create_module('assign', ['course' => $course, 'section' => 0]); // Delete empty section. No difference from normal, synchronous behaviour. $this->assertTrue(course_delete_section($course, 4, false, true)); $this->assertEquals(3, course_get_format($course)->get_course()->numsections); // Delete a module in section 2 (using async). Need to verify this doesn't generate two tasks when we delete // the section in the next step. course_delete_module($assign2->cmid, true); // Confirm that the module is pending deletion in its current section. $section = $DB->get_record('course_sections', ['course' => $course->id, 'section' => '2']); // For event comparison. $this->assertEquals(true, $DB->record_exists('course_modules', ['id' => $assign2->cmid, 'deletioninprogress' => 1, 'section' => $section->id])); // Now, delete section 2. $this->assertFalse(course_delete_section($course, 2, false, true)); // Non-empty section, no forcedelete, so no change. $sink = $this->redirectEvents(); // To capture the event. $this->assertTrue(course_delete_section($course, 2, true, true)); // Now, confirm that: // a) the section's modules have been flagged for deletion and moved to section 0 and; // b) the section has been deleted and; // c) course_section_deleted event has been fired. The course_module_deleted events will only fire once they have been // removed from section 0 via the adhoc task. // Modules should have been flagged for deletion and moved to section 0. $sectionid = $DB->get_field('course_sections', 'id', ['course' => $course->id, 'section' => 0]); $this->assertEquals(3, $DB->count_records('course_modules', ['section' => $sectionid, 'deletioninprogress' => 1])); // Confirm the section has been deleted. $this->assertEquals(2, course_get_format($course)->get_course()->numsections); // Check event fired. $events = $sink->get_events(); $event = array_pop($events); $sink->close(); $this->assertInstanceOf('\\core\\event\\course_section_deleted', $event); $this->assertEquals($section->id, $event->objectid); $this->assertEquals($USER->id, $event->userid); $this->assertEquals('course_sections', $event->objecttable); $this->assertEquals(null, $event->get_url()); $this->assertEquals($section, $event->get_record_snapshot('course_sections', $section->id)); // Now, run the adhoc task to delete the modules from section 0. $sink = $this->redirectEvents(); // To capture the events. phpunit_util::run_all_adhoc_tasks(); // Confirm the modules have been deleted. list($insql, $assignids) = $DB->get_in_or_equal([$assign0->cmid, $assign1->cmid, $assign2->cmid]); $cmcount = $DB->count_records_select('course_modules', 'id ' . $insql, $assignids); $this->assertEmpty($cmcount); // Confirm other modules in section 0 still remain. $this->assertEquals(1, $DB->count_records('course_modules', ['id' => $assign3->cmid])); // Confirm that events were generated for all 3 of the modules. $events = $sink->get_events(); $sink->close(); $count = 0; while (!empty($events)) { $event = array_pop($events); if ($event instanceof \core\event\course_module_deleted && in_array($event->objectid, [$assign0->cmid, $assign1->cmid, $assign2->cmid])) { $count++; } } $this->assertEquals(3, $count); }
/** * Test the cleanup task. */ public function test_cleanup_task() { global $DB; set_config('coursebinexpiry', WEEKSECS, 'tool_recyclebin'); // Delete the quiz. course_delete_module($this->quiz->cmid); // Now, run the course module deletion adhoc task. phpunit_util::run_all_adhoc_tasks(); // Set deleted date to the distant past. $recyclebin = new \tool_recyclebin\course_bin($this->course->id); foreach ($recyclebin->get_items() as $item) { $item->timecreated = time() - WEEKSECS; $DB->update_record('tool_recyclebin_course', $item); } // Create another module we are going to delete, but not alter the time it was placed in the recycle bin. $book = $this->getDataGenerator()->get_plugin_generator('mod_book')->create_instance(array('course' => $this->course->id)); course_delete_module($book->cmid); // Now, run the course module deletion adhoc task. phpunit_util::run_all_adhoc_tasks(); // Should have 2 items now. $this->assertEquals(2, count($recyclebin->get_items())); // Execute cleanup task. $this->expectOutputRegex("/\\[tool_recyclebin\\] Deleting item '\\d+' from the course recycle bin/"); $task = new \tool_recyclebin\task\cleanup_course_bin(); $task->execute(); // Should only have the book as it was not due to be deleted. $items = $recyclebin->get_items(); $this->assertEquals(1, count($items)); $deletedbook = reset($items); $this->assertEquals($book->name, $deletedbook->name); }