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