See Release Notes
Long Term Support Release
Differences Between: [Versions 310 and 401] [Versions 311 and 401] [Versions 39 and 401] [Versions 401 and 402] [Versions 401 and 403]
1 <?php 2 // This file is part of Moodle - http://moodle.org/ 3 // 4 // Moodle is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // Moodle is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU General Public License for more details. 13 // 14 // You should have received a copy of the GNU General Public License 15 // along with Moodle. If not, see <http://www.gnu.org/licenses/>. 16 17 namespace core_search; 18 19 defined('MOODLE_INTERNAL') || die(); 20 21 global $CFG; 22 require_once (__DIR__ . '/fixtures/testable_core_search.php'); 23 require_once($CFG->dirroot . '/search/tests/fixtures/mock_search_area.php'); 24 25 /** 26 * Search engine base unit tests. 27 * 28 * @package core_search 29 * @copyright 2017 Matt Porritt <mattp@catalyst-au.net> 30 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 31 */ 32 class base_activity_test extends \advanced_testcase { 33 /** 34 * @var \core_search::manager 35 */ 36 protected $search = null; 37 38 /** 39 * @var Instace of core_search_generator. 40 */ 41 protected $generator = null; 42 43 /** 44 * @var Instace of testable_engine. 45 */ 46 protected $engine = null; 47 48 /** @var context[] Array of test contexts */ 49 protected $contexts; 50 51 /** @var stdClass[] Array of test forum objects */ 52 protected $forums; 53 54 public function setUp(): void { 55 global $DB; 56 $this->resetAfterTest(); 57 set_config('enableglobalsearch', true); 58 59 // Set \core_search::instance to the mock_search_engine as we don't require the search engine to be working to test this. 60 $search = \testable_core_search::instance(); 61 62 $this->generator = self::getDataGenerator()->get_plugin_generator('core_search'); 63 $this->generator->setup(); 64 65 $this->setAdminUser(); 66 67 // Create course and 2 forums. 68 $generator = $this->getDataGenerator(); 69 $course = $generator->create_course(); 70 $this->contexts['c1'] = \context_course::instance($course->id); 71 $this->forums[1] = $generator->create_module('forum', ['course' => $course->id, 'name' => 'Forum 1', 72 'intro' => '<p>Intro 1</p>', 'introformat' => FORMAT_HTML]); 73 $this->contexts['f1'] = \context_module::instance($this->forums[1]->cmid); 74 $this->forums[2] = $generator->create_module('forum', ['course' => $course->id, 'name' => 'Forum 2', 75 'intro' => '<p>Intro 2</p>', 'introformat' => FORMAT_HTML]); 76 $this->contexts['f2'] = \context_module::instance($this->forums[2]->cmid); 77 78 // Create another 2 courses (in same category and in a new category) with one forum each. 79 $this->contexts['cc1'] = \context_coursecat::instance($course->category); 80 $course2 = $generator->create_course(); 81 $this->contexts['c2'] = \context_course::instance($course2->id); 82 $this->forums[3] = $generator->create_module('forum', ['course' => $course2->id, 'name' => 'Forum 3', 83 'intro' => '<p>Intro 3</p>', 'introformat' => FORMAT_HTML]); 84 $this->contexts['f3'] = \context_module::instance($this->forums[3]->cmid); 85 $cat2 = $generator->create_category(); 86 $this->contexts['cc2'] = \context_coursecat::instance($cat2->id); 87 $course3 = $generator->create_course(['category' => $cat2->id]); 88 $this->contexts['c3'] = \context_course::instance($course3->id); 89 $this->forums[4] = $generator->create_module('forum', ['course' => $course3->id, 'name' => 'Forum 4', 90 'intro' => '<p>Intro 4</p>', 'introformat' => FORMAT_HTML]); 91 $this->contexts['f4'] = \context_module::instance($this->forums[4]->cmid); 92 93 // Hack about with the time modified values. 94 foreach ($this->forums as $index => $forum) { 95 $DB->set_field('forum', 'timemodified', $index, ['id' => $forum->id]); 96 } 97 } 98 99 public function tearDown(): void { 100 // For unit tests before PHP 7, teardown is called even on skip. So only do our teardown if we did setup. 101 if ($this->generator) { 102 // Moodle DML freaks out if we don't teardown the temp table after each run. 103 $this->generator->teardown(); 104 $this->generator = null; 105 } 106 } 107 108 /** 109 * Test base activity get search fileareas 110 */ 111 public function test_get_search_fileareas_base() { 112 113 $builder = $this->getMockBuilder('\core_search\base_activity'); 114 $builder->disableOriginalConstructor(); 115 $stub = $builder->getMockForAbstractClass(); 116 117 $result = $stub->get_search_fileareas(); 118 119 $this->assertEquals(array('intro'), $result); 120 } 121 122 /** 123 * Test base attach files 124 */ 125 public function test_attach_files_base() { 126 $filearea = 'intro'; 127 $component = 'mod_forum'; 128 $module = 'forum'; 129 130 $course = self::getDataGenerator()->create_course(); 131 $activity = self::getDataGenerator()->create_module('forum', array('course' => $course->id)); 132 $context = \context_module::instance($activity->cmid); 133 $contextid = $context->id; 134 135 // Create file to add. 136 $fs = get_file_storage(); 137 $filerecord = array( 138 'contextid' => $contextid, 139 'component' => $component, 140 'filearea' => $filearea, 141 'itemid' => 0, 142 'filepath' => '/', 143 'filename' => 'testfile.txt'); 144 $content = 'All the news that\'s fit to print'; 145 $file = $fs->create_file_from_string($filerecord, $content); 146 147 // Construct the search document. 148 $rec = new \stdClass(); 149 $rec->courseid = $course->id; 150 $area = new \core_mocksearch\search\mock_search_area(); 151 $record = $this->generator->create_record($rec); 152 153 $document = $area->get_document($record); 154 $document->set('itemid', $activity->id); 155 156 // Create a mock from the abstract class, 157 // with required methods stubbed. 158 $builder = $this->getMockBuilder('\core_search\base_activity'); 159 $builder->disableOriginalConstructor(); 160 $builder->onlyMethods(array('get_module_name', 'get_component_name')); 161 $stub = $builder->getMockForAbstractClass(); 162 $stub->method('get_module_name')->willReturn($module); 163 $stub->method('get_component_name')->willReturn($component); 164 165 // Attach file to our test document. 166 $stub->attach_files($document); 167 168 // Verify file is attached. 169 $files = $document->get_files(); 170 $file = array_values($files)[0]; 171 172 $this->assertEquals(1, count($files)); 173 $this->assertEquals($content, $file->get_content()); 174 } 175 176 /** 177 * Tests getting the recordset. 178 */ 179 public function test_get_document_recordset() { 180 global $USER, $DB; 181 182 // Get all the forums to index (no restriction). 183 $area = new \mod_forum\search\activity(); 184 $results = self::recordset_to_indexed_array($area->get_document_recordset()); 185 186 // Should return all forums. 187 $this->assertCount(4, $results); 188 189 // Each result should basically have the contents of the forum table. We'll just check 190 // the key fields for the first one and then the other ones by id only. 191 $this->assertEquals($this->forums[1]->id, $results[0]->id); 192 $this->assertEquals(1, $results[0]->timemodified); 193 $this->assertEquals($this->forums[1]->course, $results[0]->course); 194 $this->assertEquals('Forum 1', $results[0]->name); 195 $this->assertEquals('<p>Intro 1</p>', $results[0]->intro); 196 $this->assertEquals(FORMAT_HTML, $results[0]->introformat); 197 198 $allids = self::records_to_ids($this->forums); 199 $this->assertEquals($allids, self::records_to_ids($results)); 200 201 // Repeat with a time restriction. 202 $results = self::recordset_to_indexed_array($area->get_document_recordset(3)); 203 $this->assertEquals([$this->forums[3]->id, $this->forums[4]->id], 204 self::records_to_ids($results)); 205 206 // Now use context restrictions. First, the whole site (no change). 207 $results = self::recordset_to_indexed_array($area->get_document_recordset( 208 0, \context_system::instance())); 209 $this->assertEquals($allids, self::records_to_ids($results)); 210 211 // Course 1 only. 212 $results = self::recordset_to_indexed_array($area->get_document_recordset( 213 0, $this->contexts['c1'])); 214 $this->assertEquals([$this->forums[1]->id, $this->forums[2]->id], 215 self::records_to_ids($results)); 216 217 // Course 2 only. 218 $results = self::recordset_to_indexed_array($area->get_document_recordset( 219 0, $this->contexts['c2'])); 220 $this->assertEquals([$this->forums[3]->id], self::records_to_ids($results)); 221 222 // Specific forum only. 223 $results = self::recordset_to_indexed_array($area->get_document_recordset( 224 0, $this->contexts['f4'])); 225 $this->assertEquals([$this->forums[4]->id], self::records_to_ids($results)); 226 227 // Category 1 context (courses 1 and 2). 228 $results = self::recordset_to_indexed_array($area->get_document_recordset( 229 0, $this->contexts['cc1'])); 230 $this->assertEquals([$this->forums[1]->id, $this->forums[2]->id, $this->forums[3]->id], 231 self::records_to_ids($results)); 232 233 // Category 2 context (course 3). 234 $results = self::recordset_to_indexed_array($area->get_document_recordset( 235 0, $this->contexts['cc2'])); 236 $this->assertEquals([$this->forums[4]->id], self::records_to_ids($results)); 237 238 // Combine context restriction (category 1) with timemodified. 239 $results = self::recordset_to_indexed_array($area->get_document_recordset( 240 2, $this->contexts['cc1'])); 241 $this->assertEquals([$this->forums[2]->id, $this->forums[3]->id], 242 self::records_to_ids($results)); 243 244 // Find an arbitrary block on the system to get a block context. 245 $blockid = array_values($DB->get_records('block_instances', null, 'id', 'id', 0, 1))[0]->id; 246 $blockcontext = \context_block::instance($blockid); 247 248 // Block context (cannot return anything, so always null). 249 $this->assertNull($area->get_document_recordset(0, $blockcontext)); 250 251 // User context (cannot return anything, so always null). 252 $usercontext = \context_user::instance($USER->id); 253 $this->assertNull($area->get_document_recordset(0, $usercontext)); 254 } 255 256 /** 257 * Utility function to convert recordset to array for testing. 258 * 259 * @param moodle_recordset $rs Recordset to convert 260 * @return array Array indexed by number (0, 1, 2, ...) 261 */ 262 protected static function recordset_to_indexed_array(\moodle_recordset $rs) { 263 $results = []; 264 foreach ($rs as $rec) { 265 $results[] = $rec; 266 } 267 $rs->close(); 268 return $results; 269 } 270 271 /** 272 * Utility function to convert records to array of IDs. 273 * 274 * @param array $recs Records which should have an 'id' field 275 * @return array Array of ids 276 */ 277 protected static function records_to_ids(array $recs) { 278 $ids = []; 279 foreach ($recs as $rec) { 280 $ids[] = $rec->id; 281 } 282 return $ids; 283 } 284 285 /** 286 * Tests the get_doc_url function. 287 */ 288 public function test_get_doc_url() { 289 $area = new \mod_forum\search\activity(); 290 $results = self::recordset_to_indexed_array($area->get_document_recordset()); 291 292 for ($i = 0; $i < 4; $i++) { 293 $this->assertEquals(new \moodle_url('/mod/forum/view.php', 294 ['id' => $this->forums[$i + 1]->cmid]), 295 $area->get_doc_url($area->get_document($results[$i]))); 296 } 297 } 298 299 /** 300 * Tests the check_access function. 301 */ 302 public function test_check_access() { 303 global $CFG; 304 require_once($CFG->dirroot . '/course/lib.php'); 305 306 // Create a test user who can access courses 1 and 2 (everything except forum 4). 307 $generator = $this->getDataGenerator(); 308 $user = $generator->create_user(); 309 $generator->enrol_user($user->id, $this->forums[1]->course, 'student'); 310 $generator->enrol_user($user->id, $this->forums[3]->course, 'student'); 311 $this->setUser($user); 312 313 // Delete forum 2 and set forum 3 hidden. 314 course_delete_module($this->forums[2]->cmid); 315 set_coursemodule_visible($this->forums[3]->cmid, 0); 316 317 // Call check access on all the first three. 318 $area = new \mod_forum\search\activity(); 319 $this->assertEquals(\core_search\manager::ACCESS_GRANTED, $area->check_access( 320 $this->forums[1]->id)); 321 $this->assertEquals(\core_search\manager::ACCESS_DELETED, $area->check_access( 322 $this->forums[2]->id)); 323 $this->assertEquals(\core_search\manager::ACCESS_DENIED, $area->check_access( 324 $this->forums[3]->id)); 325 326 // Note: Do not check forum 4 which is in a course the user can't access; this will return 327 // ACCESS_GRANTED, but it does not matter because the search engine will not have included 328 // that context in the list to search. (This is because the $cm->uservisible access flag 329 // is only valid if the user is known to be able to access the course.) 330 } 331 332 /** 333 * Tests the module version of get_contexts_to_reindex, which is supposed to return all the 334 * activity contexts in order of date added. 335 */ 336 public function test_get_contexts_to_reindex() { 337 global $DB; 338 339 $this->resetAfterTest(); 340 341 // Set up a course with two URLs and a Page. 342 $generator = $this->getDataGenerator(); 343 $course = $generator->create_course(['fullname' => 'TCourse']); 344 $url1 = $generator->create_module('url', ['course' => $course->id, 'name' => 'TURL1']); 345 $url2 = $generator->create_module('url', ['course' => $course->id, 'name' => 'TURL2']); 346 $page = $generator->create_module('page', ['course' => $course->id, 'name' => 'TPage1']); 347 348 // Hack the items so they have different added times. 349 $now = time(); 350 $DB->set_field('course_modules', 'added', $now - 3, ['id' => $url2->cmid]); 351 $DB->set_field('course_modules', 'added', $now - 2, ['id' => $url1->cmid]); 352 $DB->set_field('course_modules', 'added', $now - 1, ['id' => $page->cmid]); 353 354 // Check the URL contexts are in date order. 355 $urlarea = new \mod_url\search\activity(); 356 $contexts = iterator_to_array($urlarea->get_contexts_to_reindex(), false); 357 $this->assertEquals([\context_module::instance($url1->cmid), 358 \context_module::instance($url2->cmid)], $contexts); 359 360 // Check the Page contexts. 361 $pagearea = new \mod_page\search\activity(); 362 $contexts = iterator_to_array($pagearea->get_contexts_to_reindex(), false); 363 $this->assertEquals([\context_module::instance($page->cmid)], $contexts); 364 365 // Check another module area that has no instances. 366 $glossaryarea = new \mod_glossary\search\activity(); 367 $contexts = iterator_to_array($glossaryarea->get_contexts_to_reindex(), false); 368 $this->assertEquals([], $contexts); 369 } 370 371 /** 372 * Test document icon. 373 */ 374 public function test_get_doc_icon() { 375 $baseactivity = $this->getMockBuilder('\core_search\base_activity') 376 ->disableOriginalConstructor() 377 ->onlyMethods(array('get_module_name')) 378 ->getMockForAbstractClass(); 379 380 $baseactivity->method('get_module_name')->willReturn('test_activity'); 381 382 $document = $this->getMockBuilder('\core_search\document') 383 ->disableOriginalConstructor() 384 ->getMock(); 385 386 $result = $baseactivity->get_doc_icon($document); 387 388 $this->assertEquals('monologo', $result->get_name()); 389 $this->assertEquals('test_activity', $result->get_component()); 390 } 391 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body