Proyectos de Subversion Moodle

Rev

Rev 1 | | Comparar con el anterior | Ultima modificación | Ver Log |

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