Differences Between: [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 * Forum search unit tests. 19 * 20 * @package mod_forum 21 * @category test 22 * @copyright 2015 David Monllao {@link http://www.davidmonllao.com} 23 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 24 */ 25 26 defined('MOODLE_INTERNAL') || die(); 27 28 global $CFG; 29 require_once($CFG->dirroot . '/search/tests/fixtures/testable_core_search.php'); 30 require_once($CFG->dirroot . '/mod/forum/tests/generator/lib.php'); 31 require_once($CFG->dirroot . '/mod/forum/lib.php'); 32 33 /** 34 * Provides the unit tests for forum search. 35 * 36 * @package mod_forum 37 * @category test 38 * @copyright 2015 David Monllao {@link http://www.davidmonllao.com} 39 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 40 */ 41 class mod_forum_search_testcase extends advanced_testcase { 42 43 /** 44 * @var string Area id 45 */ 46 protected $forumpostareaid = null; 47 48 public function setUp(): void { 49 $this->resetAfterTest(true); 50 set_config('enableglobalsearch', true); 51 52 $this->forumpostareaid = \core_search\manager::generate_areaid('mod_forum', 'post'); 53 54 // Set \core_search::instance to the mock_search_engine as we don't require the search engine to be working to test this. 55 $search = testable_core_search::instance(); 56 } 57 58 /** 59 * Availability. 60 * 61 * @return void 62 */ 63 public function test_search_enabled() { 64 65 $searcharea = \core_search\manager::get_search_area($this->forumpostareaid); 66 list($componentname, $varname) = $searcharea->get_config_var_name(); 67 68 // Enabled by default once global search is enabled. 69 $this->assertTrue($searcharea->is_enabled()); 70 71 set_config($varname . '_enabled', 0, $componentname); 72 $this->assertFalse($searcharea->is_enabled()); 73 74 set_config($varname . '_enabled', 1, $componentname); 75 $this->assertTrue($searcharea->is_enabled()); 76 } 77 78 /** 79 * Indexing mod forum contents. 80 * 81 * @return void 82 */ 83 public function test_posts_indexing() { 84 global $DB; 85 86 // Returns the instance as long as the area is supported. 87 $searcharea = \core_search\manager::get_search_area($this->forumpostareaid); 88 $this->assertInstanceOf('\mod_forum\search\post', $searcharea); 89 90 $user1 = self::getDataGenerator()->create_user(); 91 $user2 = self::getDataGenerator()->create_user(); 92 93 $course1 = self::getDataGenerator()->create_course(); 94 $course2 = self::getDataGenerator()->create_course(); 95 96 $this->getDataGenerator()->enrol_user($user1->id, $course1->id, 'student'); 97 $this->getDataGenerator()->enrol_user($user2->id, $course1->id, 'student'); 98 99 $record = new stdClass(); 100 $record->course = $course1->id; 101 102 // Available for both student and teacher. 103 $forum1 = self::getDataGenerator()->create_module('forum', $record); 104 105 // Create discussion1. 106 $record = new stdClass(); 107 $record->course = $course1->id; 108 $record->userid = $user1->id; 109 $record->forum = $forum1->id; 110 $record->message = 'discussion'; 111 $discussion1 = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_discussion($record); 112 113 // Create post1 in discussion1. 114 $record = new stdClass(); 115 $record->discussion = $discussion1->id; 116 $record->parent = $discussion1->firstpost; 117 $record->userid = $user2->id; 118 $record->message = 'post2'; 119 $discussion1reply1 = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_post($record); 120 121 // All records. 122 $recordset = $searcharea->get_recordset_by_timestamp(0); 123 $this->assertTrue($recordset->valid()); 124 $nrecords = 0; 125 foreach ($recordset as $record) { 126 $this->assertInstanceOf('stdClass', $record); 127 $doc = $searcharea->get_document($record); 128 $this->assertInstanceOf('\core_search\document', $doc); 129 130 // Static caches are working. 131 $dbreads = $DB->perf_get_reads(); 132 $doc = $searcharea->get_document($record); 133 $this->assertEquals($dbreads, $DB->perf_get_reads()); 134 $this->assertInstanceOf('\core_search\document', $doc); 135 $nrecords++; 136 } 137 // If there would be an error/failure in the foreach above the recordset would be closed on shutdown. 138 $recordset->close(); 139 $this->assertEquals(2, $nrecords); 140 141 // The +2 is to prevent race conditions. 142 $recordset = $searcharea->get_recordset_by_timestamp(time() + 2); 143 144 // No new records. 145 $this->assertFalse($recordset->valid()); 146 $recordset->close(); 147 148 // Context test: create another forum with 1 post. 149 $forum2 = self::getDataGenerator()->create_module('forum', ['course' => $course1->id]); 150 $record = new stdClass(); 151 $record->course = $course1->id; 152 $record->userid = $user1->id; 153 $record->forum = $forum2->id; 154 $record->message = 'discussion'; 155 self::getDataGenerator()->get_plugin_generator('mod_forum')->create_discussion($record); 156 157 // Test indexing with each forum then combined course context. 158 $rs = $searcharea->get_document_recordset(0, context_module::instance($forum1->cmid)); 159 $this->assertEquals(2, iterator_count($rs)); 160 $rs->close(); 161 $rs = $searcharea->get_document_recordset(0, context_module::instance($forum2->cmid)); 162 $this->assertEquals(1, iterator_count($rs)); 163 $rs->close(); 164 $rs = $searcharea->get_document_recordset(0, context_course::instance($course1->id)); 165 $this->assertEquals(3, iterator_count($rs)); 166 $rs->close(); 167 } 168 169 /** 170 * Document contents. 171 * 172 * @return void 173 */ 174 public function test_posts_document() { 175 global $DB; 176 177 // Returns the instance as long as the area is supported. 178 $searcharea = \core_search\manager::get_search_area($this->forumpostareaid); 179 $this->assertInstanceOf('\mod_forum\search\post', $searcharea); 180 181 $user = self::getDataGenerator()->create_user(); 182 $course1 = self::getDataGenerator()->create_course(); 183 $this->getDataGenerator()->enrol_user($user->id, $course1->id, 'teacher'); 184 185 $record = new stdClass(); 186 $record->course = $course1->id; 187 $forum1 = self::getDataGenerator()->create_module('forum', $record); 188 189 // Teacher only. 190 $forum2 = self::getDataGenerator()->create_module('forum', $record); 191 set_coursemodule_visible($forum2->cmid, 0); 192 193 // Create discussion1. 194 $record = new stdClass(); 195 $record->course = $course1->id; 196 $record->userid = $user->id; 197 $record->forum = $forum1->id; 198 $record->message = 'discussion'; 199 $record->groupid = 0; 200 $discussion1 = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_discussion($record); 201 202 // Create post1 in discussion1. 203 $record = new stdClass(); 204 $record->discussion = $discussion1->id; 205 $record->parent = $discussion1->firstpost; 206 $record->userid = $user->id; 207 $record->subject = 'subject1'; 208 $record->message = 'post1'; 209 $record->groupid = -1; 210 $discussion1reply1 = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_post($record); 211 212 $post1 = $DB->get_record('forum_posts', array('id' => $discussion1reply1->id)); 213 $post1->forumid = $forum1->id; 214 $post1->courseid = $forum1->course; 215 $post1->groupid = -1; 216 217 $doc = $searcharea->get_document($post1); 218 $this->assertInstanceOf('\core_search\document', $doc); 219 $this->assertEquals($discussion1reply1->id, $doc->get('itemid')); 220 $this->assertEquals($this->forumpostareaid . '-' . $discussion1reply1->id, $doc->get('id')); 221 $this->assertEquals($course1->id, $doc->get('courseid')); 222 $this->assertEquals($user->id, $doc->get('userid')); 223 $this->assertEquals($discussion1reply1->subject, $doc->get('title')); 224 $this->assertEquals($discussion1reply1->message, $doc->get('content')); 225 } 226 227 /** 228 * Group support for forum posts. 229 */ 230 public function test_posts_group_support() { 231 // Get the search area and test generators. 232 $searcharea = \core_search\manager::get_search_area($this->forumpostareaid); 233 $generator = $this->getDataGenerator(); 234 $forumgenerator = $generator->get_plugin_generator('mod_forum'); 235 236 // Create a course, a user, and two groups. 237 $course = $generator->create_course(); 238 $user = $generator->create_user(); 239 $generator->enrol_user($user->id, $course->id, 'teacher'); 240 $group1 = $generator->create_group(['courseid' => $course->id]); 241 $group2 = $generator->create_group(['courseid' => $course->id]); 242 243 // Separate groups forum. 244 $forum = self::getDataGenerator()->create_module('forum', ['course' => $course->id, 245 'groupmode' => SEPARATEGROUPS]); 246 247 // Create discussion with each group and one for all groups. One has a post in. 248 $discussion1 = $forumgenerator->create_discussion(['course' => $course->id, 249 'userid' => $user->id, 'forum' => $forum->id, 'message' => 'd1', 250 'groupid' => $group1->id]); 251 $forumgenerator->create_discussion(['course' => $course->id, 252 'userid' => $user->id, 'forum' => $forum->id, 'message' => 'd2', 253 'groupid' => $group2->id]); 254 $forumgenerator->create_discussion(['course' => $course->id, 255 'userid' => $user->id, 'forum' => $forum->id, 'message' => 'd3']); 256 257 // Create a reply in discussion1. 258 $forumgenerator->create_post(['discussion' => $discussion1->id, 'parent' => $discussion1->firstpost, 259 'userid' => $user->id, 'message' => 'p1']); 260 261 // Do the indexing of all 4 posts. 262 $rs = $searcharea->get_recordset_by_timestamp(0); 263 $results = []; 264 foreach ($rs as $rec) { 265 $results[$rec->message] = $rec; 266 } 267 $rs->close(); 268 $this->assertCount(4, $results); 269 270 // Check each document has the correct groupid. 271 $doc = $searcharea->get_document($results['d1']); 272 $this->assertTrue($doc->is_set('groupid')); 273 $this->assertEquals($group1->id, $doc->get('groupid')); 274 $doc = $searcharea->get_document($results['d2']); 275 $this->assertTrue($doc->is_set('groupid')); 276 $this->assertEquals($group2->id, $doc->get('groupid')); 277 $doc = $searcharea->get_document($results['d3']); 278 $this->assertFalse($doc->is_set('groupid')); 279 $doc = $searcharea->get_document($results['p1']); 280 $this->assertTrue($doc->is_set('groupid')); 281 $this->assertEquals($group1->id, $doc->get('groupid')); 282 283 // While we're here, also test that the search area requests restriction by group. 284 $modinfo = get_fast_modinfo($course); 285 $this->assertTrue($searcharea->restrict_cm_access_by_group($modinfo->get_cm($forum->cmid))); 286 287 // In visible groups mode, it won't request restriction by group. 288 set_coursemodule_groupmode($forum->cmid, VISIBLEGROUPS); 289 $modinfo = get_fast_modinfo($course); 290 $this->assertFalse($searcharea->restrict_cm_access_by_group($modinfo->get_cm($forum->cmid))); 291 } 292 293 /** 294 * Document accesses. 295 * 296 * @return void 297 */ 298 public function test_posts_access() { 299 global $DB; 300 301 // Returns the instance as long as the area is supported. 302 $searcharea = \core_search\manager::get_search_area($this->forumpostareaid); 303 304 $user1 = self::getDataGenerator()->create_user(); 305 $user2 = self::getDataGenerator()->create_user(); 306 307 $course1 = self::getDataGenerator()->create_course(); 308 $course2 = self::getDataGenerator()->create_course(); 309 310 $this->getDataGenerator()->enrol_user($user1->id, $course1->id, 'teacher'); 311 $this->getDataGenerator()->enrol_user($user2->id, $course1->id, 'student'); 312 313 $record = new stdClass(); 314 $record->course = $course1->id; 315 316 // Available for both student and teacher. 317 $forum1 = self::getDataGenerator()->create_module('forum', $record); 318 319 // Teacher only. 320 $forum2 = self::getDataGenerator()->create_module('forum', $record); 321 set_coursemodule_visible($forum2->cmid, 0); 322 323 // Create discussion1. 324 $record = new stdClass(); 325 $record->course = $course1->id; 326 $record->userid = $user1->id; 327 $record->forum = $forum1->id; 328 $record->message = 'discussion'; 329 $discussion1 = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_discussion($record); 330 331 // Create post1 in discussion1. 332 $record = new stdClass(); 333 $record->discussion = $discussion1->id; 334 $record->parent = $discussion1->firstpost; 335 $record->userid = $user2->id; 336 $record->message = 'post1'; 337 $discussion1reply1 = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_post($record); 338 339 // Create discussion2 only visible to teacher. 340 $record = new stdClass(); 341 $record->course = $course1->id; 342 $record->userid = $user1->id; 343 $record->forum = $forum2->id; 344 $record->message = 'discussion'; 345 $discussion2 = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_discussion($record); 346 347 // Create post2 in discussion2. 348 $record = new stdClass(); 349 $record->discussion = $discussion2->id; 350 $record->parent = $discussion2->firstpost; 351 $record->userid = $user1->id; 352 $record->message = 'post2'; 353 $discussion2reply1 = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_post($record); 354 355 $this->setUser($user2); 356 $this->assertEquals(\core_search\manager::ACCESS_GRANTED, $searcharea->check_access($discussion1reply1->id)); 357 $this->assertEquals(\core_search\manager::ACCESS_DENIED, $searcharea->check_access($discussion2reply1->id)); 358 } 359 360 /** 361 * Test for post attachments. 362 * 363 * @return void 364 */ 365 public function test_attach_files() { 366 global $DB; 367 368 $fs = get_file_storage(); 369 370 // Returns the instance as long as the area is supported. 371 $searcharea = \core_search\manager::get_search_area($this->forumpostareaid); 372 $this->assertInstanceOf('\mod_forum\search\post', $searcharea); 373 374 $user1 = self::getDataGenerator()->create_user(); 375 $user2 = self::getDataGenerator()->create_user(); 376 377 $course1 = self::getDataGenerator()->create_course(); 378 379 $this->getDataGenerator()->enrol_user($user1->id, $course1->id, 'student'); 380 $this->getDataGenerator()->enrol_user($user2->id, $course1->id, 'student'); 381 382 $record = new stdClass(); 383 $record->course = $course1->id; 384 385 $forum1 = self::getDataGenerator()->create_module('forum', $record); 386 387 // Create discussion1. 388 $record = new stdClass(); 389 $record->course = $course1->id; 390 $record->userid = $user1->id; 391 $record->forum = $forum1->id; 392 $record->message = 'discussion'; 393 $record->attachemt = 1; 394 $discussion1 = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_discussion($record); 395 396 // Attach 2 file to the discussion post. 397 $post = $DB->get_record('forum_posts', array('discussion' => $discussion1->id)); 398 $filerecord = array( 399 'contextid' => context_module::instance($forum1->cmid)->id, 400 'component' => 'mod_forum', 401 'filearea' => 'attachment', 402 'itemid' => $post->id, 403 'filepath' => '/', 404 'filename' => 'myfile1' 405 ); 406 $file1 = $fs->create_file_from_string($filerecord, 'Some contents 1'); 407 $filerecord['filename'] = 'myfile2'; 408 $file2 = $fs->create_file_from_string($filerecord, 'Some contents 2'); 409 410 // Create post1 in discussion1. 411 $record = new stdClass(); 412 $record->discussion = $discussion1->id; 413 $record->parent = $discussion1->firstpost; 414 $record->userid = $user2->id; 415 $record->message = 'post2'; 416 $record->attachemt = 1; 417 $discussion1reply1 = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_post($record); 418 419 $filerecord['itemid'] = $discussion1reply1->id; 420 $filerecord['filename'] = 'myfile3'; 421 $file3 = $fs->create_file_from_string($filerecord, 'Some contents 3'); 422 423 // Create post2 in discussion1. 424 $record = new stdClass(); 425 $record->discussion = $discussion1->id; 426 $record->parent = $discussion1->firstpost; 427 $record->userid = $user2->id; 428 $record->message = 'post3'; 429 $discussion1reply2 = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_post($record); 430 431 // Now get all the posts and see if they have the right files attached. 432 $searcharea = \core_search\manager::get_search_area($this->forumpostareaid); 433 $recordset = $searcharea->get_recordset_by_timestamp(0); 434 $nrecords = 0; 435 foreach ($recordset as $record) { 436 $doc = $searcharea->get_document($record); 437 $searcharea->attach_files($doc); 438 $files = $doc->get_files(); 439 // Now check that each doc has the right files on it. 440 switch ($doc->get('itemid')) { 441 case ($post->id): 442 $this->assertCount(2, $files); 443 $this->assertEquals($file1->get_id(), $files[$file1->get_id()]->get_id()); 444 $this->assertEquals($file2->get_id(), $files[$file2->get_id()]->get_id()); 445 break; 446 case ($discussion1reply1->id): 447 $this->assertCount(1, $files); 448 $this->assertEquals($file3->get_id(), $files[$file3->get_id()]->get_id()); 449 break; 450 case ($discussion1reply2->id): 451 $this->assertCount(0, $files); 452 break; 453 default: 454 $this->fail('Unexpected post returned'); 455 break; 456 } 457 $nrecords++; 458 } 459 $recordset->close(); 460 $this->assertEquals(3, $nrecords); 461 } 462 463 /** 464 * Tests that reindexing works in order starting from the forum with most recent discussion. 465 */ 466 public function test_posts_get_contexts_to_reindex() { 467 global $DB; 468 469 $generator = $this->getDataGenerator(); 470 $adminuser = get_admin(); 471 472 $course1 = $generator->create_course(); 473 $course2 = $generator->create_course(); 474 475 $time = time() - 1000; 476 477 // Create 3 forums (two in course 1, one in course 2 - doesn't make a difference). 478 $forum1 = $generator->create_module('forum', ['course' => $course1->id]); 479 $forum2 = $generator->create_module('forum', ['course' => $course1->id]); 480 $forum3 = $generator->create_module('forum', ['course' => $course2->id]); 481 $forum4 = $generator->create_module('forum', ['course' => $course2->id]); 482 483 // Hack added time for the course_modules entries. These should not be used (they would 484 // be used by the base class implementation). We are setting this so that the order would 485 // be 4, 3, 2, 1 if this ordering were used (newest first). 486 $DB->set_field('course_modules', 'added', $time + 100, ['id' => $forum1->cmid]); 487 $DB->set_field('course_modules', 'added', $time + 110, ['id' => $forum2->cmid]); 488 $DB->set_field('course_modules', 'added', $time + 120, ['id' => $forum3->cmid]); 489 $DB->set_field('course_modules', 'added', $time + 130, ['id' => $forum4->cmid]); 490 491 $forumgenerator = $generator->get_plugin_generator('mod_forum'); 492 493 // Create one discussion in forums 1 and 3, three in forum 2, and none in forum 4. 494 $forumgenerator->create_discussion(['course' => $course1->id, 495 'forum' => $forum1->id, 'userid' => $adminuser->id, 'timemodified' => $time + 20]); 496 497 $forumgenerator->create_discussion(['course' => $course1->id, 498 'forum' => $forum2->id, 'userid' => $adminuser->id, 'timemodified' => $time + 10]); 499 $forumgenerator->create_discussion(['course' => $course1->id, 500 'forum' => $forum2->id, 'userid' => $adminuser->id, 'timemodified' => $time + 30]); 501 $forumgenerator->create_discussion(['course' => $course1->id, 502 'forum' => $forum2->id, 'userid' => $adminuser->id, 'timemodified' => $time + 11]); 503 504 $forumgenerator->create_discussion(['course' => $course2->id, 505 'forum' => $forum3->id, 'userid' => $adminuser->id, 'timemodified' => $time + 25]); 506 507 // Get the contexts in reindex order. 508 $area = \core_search\manager::get_search_area($this->forumpostareaid); 509 $contexts = iterator_to_array($area->get_contexts_to_reindex(), false); 510 511 // We expect them in order of newest discussion. Forum 4 is not included at all (which is 512 // correct because it has no content). 513 $expected = [ 514 \context_module::instance($forum2->cmid), 515 \context_module::instance($forum3->cmid), 516 \context_module::instance($forum1->cmid) 517 ]; 518 $this->assertEquals($expected, $contexts); 519 } 520 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body