Merge branch 'MDL-50225_master' of git://github.com/markn86/moodle
[moodle.git] / search / tests / base_block_test.php
blob5a6d5c164a01bc398f9e36e4edf5c801a677efbd
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.
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/>.
17 /**
18 * Unit tests for the base_block class.
20 * @package core_search
21 * @copyright 2017 The Open University
22 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
25 defined('MOODLE_INTERNAL') || die();
27 require_once(__DIR__ . '/fixtures/testable_core_search.php');
28 require_once(__DIR__ . '/fixtures/mock_block_area.php');
30 /**
31 * Unit tests for the base_block class.
33 * @package core_search
34 * @copyright 2017 The Open University
35 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
37 class base_block_testcase extends advanced_testcase {
38 /**
39 * Tests getting the name out of the class name.
41 public function test_get_block_name() {
42 $area = new \block_mockblock\search\area();
43 $this->assertEquals('mockblock', $area->get_block_name());
46 /**
47 * Tests getting the recordset.
49 public function test_get_document_recordset() {
50 global $DB, $USER;
52 $this->resetAfterTest();
53 $this->setAdminUser();
55 // Create course and activity module.
56 $generator = $this->getDataGenerator();
57 $course = $generator->create_course();
58 $coursecontext = \context_course::instance($course->id);
59 $page = $generator->create_module('page', ['course' => $course->id]);
60 $pagecontext = \context_module::instance($page->cmid);
62 // Create another 2 courses (in same category and in a new category).
63 $cat1context = \context_coursecat::instance($course->category);
64 $course2 = $generator->create_course();
65 $course2context = \context_course::instance($course2->id);
66 $cat2 = $generator->create_category();
67 $cat2context = \context_coursecat::instance($cat2->id);
68 $course3 = $generator->create_course(['category' => $cat2->id]);
69 $course3context = \context_course::instance($course3->id);
71 // Add blocks by hacking table (because it's not a real block type).
73 // 1. Block on course page.
74 $configdata = base64_encode(serialize((object) ['example' => 'content']));
75 $instance = (object)['blockname' => 'mockblock', 'parentcontextid' => $coursecontext->id,
76 'showinsubcontexts' => 0, 'pagetypepattern' => 'course-view-*',
77 'defaultweight' => 0, 'timecreated' => 1, 'timemodified' => 1,
78 'configdata' => $configdata];
79 $block1id = $DB->insert_record('block_instances', $instance);
80 $block1context = \context_block::instance($block1id);
82 // 2. Block on activity page.
83 $instance->parentcontextid = $pagecontext->id;
84 $instance->pagetypepattern = 'mod-page-view';
85 $instance->timemodified = 2;
86 $block2id = $DB->insert_record('block_instances', $instance);
87 \context_block::instance($block2id);
89 // 3. Block on site context.
90 $sitecourse = get_site();
91 $sitecontext = \context_course::instance($sitecourse->id);
92 $instance->parentcontextid = $sitecontext->id;
93 $instance->pagetypepattern = 'site-index';
94 $instance->timemodified = 3;
95 $block3id = $DB->insert_record('block_instances', $instance);
96 $block3context = \context_block::instance($block3id);
98 // 4. Block on course page but no data.
99 $instance->parentcontextid = $coursecontext->id;
100 $instance->pagetypepattern = 'course-view-*';
101 unset($instance->configdata);
102 $instance->timemodified = 4;
103 $block4id = $DB->insert_record('block_instances', $instance);
104 \context_block::instance($block4id);
106 // 5. Block on course page but not this block.
107 $instance->blockname = 'mockotherblock';
108 $instance->configdata = $configdata;
109 $instance->timemodified = 5;
110 $block5id = $DB->insert_record('block_instances', $instance);
111 \context_block::instance($block5id);
113 // 6. Block on course page with '*' page type.
114 $instance->blockname = 'mockblock';
115 $instance->pagetypepattern = '*';
116 $instance->timemodified = 6;
117 $block6id = $DB->insert_record('block_instances', $instance);
118 \context_block::instance($block6id);
120 // 7. Block on course page with 'course-*' page type.
121 $instance->pagetypepattern = 'course-*';
122 $instance->timemodified = 7;
123 $block7id = $DB->insert_record('block_instances', $instance);
124 \context_block::instance($block7id);
126 // 8. Block on course 2.
127 $instance->parentcontextid = $course2context->id;
128 $instance->timemodified = 8;
129 $block8id = $DB->insert_record('block_instances', $instance);
130 \context_block::instance($block8id);
132 // 9. Block on course 3.
133 $instance->parentcontextid = $course3context->id;
134 $instance->timemodified = 9;
135 $block9id = $DB->insert_record('block_instances', $instance);
136 \context_block::instance($block9id);
138 // Get all the blocks.
139 $area = new block_mockblock\search\area();
140 $results = self::recordset_to_indexed_array($area->get_document_recordset());
142 // Only blocks 1, 3, 6, 7, 8, 9 should be returned. Check all the fields for the first two.
143 $this->assertCount(6, $results);
145 $this->assertEquals($block1id, $results[0]->id);
146 $this->assertEquals(1, $results[0]->timemodified);
147 $this->assertEquals(1, $results[0]->timecreated);
148 $this->assertEquals($configdata, $results[0]->configdata);
149 $this->assertEquals($course->id, $results[0]->courseid);
150 $this->assertEquals($block1context->id, $results[0]->contextid);
152 $this->assertEquals($block3id, $results[1]->id);
153 $this->assertEquals(3, $results[1]->timemodified);
154 $this->assertEquals(1, $results[1]->timecreated);
155 $this->assertEquals($configdata, $results[1]->configdata);
156 $this->assertEquals($sitecourse->id, $results[1]->courseid);
157 $this->assertEquals($block3context->id, $results[1]->contextid);
159 // For the later ones, just check it got the right ones!
160 $this->assertEquals($block6id, $results[2]->id);
161 $this->assertEquals($block7id, $results[3]->id);
162 $this->assertEquals($block8id, $results[4]->id);
163 $this->assertEquals($block9id, $results[5]->id);
165 // Repeat with a time restriction.
166 $results = self::recordset_to_indexed_array($area->get_document_recordset(2));
168 // Only block 3, 6, 7, 8, and 9 are returned.
169 $this->assertEquals([$block3id, $block6id, $block7id, $block8id, $block9id],
170 self::records_to_ids($results));
172 // Now use context restrictions. First, the whole site (no change).
173 $results = self::recordset_to_indexed_array($area->get_document_recordset(
174 0, context_system::instance()));
175 $this->assertEquals([$block1id, $block3id, $block6id, $block7id, $block8id, $block9id],
176 self::records_to_ids($results));
178 // Course page only (leave out the one on site page and other courses).
179 $results = self::recordset_to_indexed_array($area->get_document_recordset(
180 0, $coursecontext));
181 $this->assertEquals([$block1id, $block6id, $block7id],
182 self::records_to_ids($results));
184 // Other course page only.
185 $results = self::recordset_to_indexed_array($area->get_document_recordset(
186 0, $course2context));
187 $this->assertEquals([$block8id], self::records_to_ids($results));
189 // Activity module only (no results).
190 $results = self::recordset_to_indexed_array($area->get_document_recordset(
191 0, $pagecontext));
192 $this->assertCount(0, $results);
194 // Specific block context.
195 $results = self::recordset_to_indexed_array($area->get_document_recordset(
196 0, $block3context));
197 $this->assertEquals([$block3id], self::records_to_ids($results));
199 // User context (no results).
200 $usercontext = context_user::instance($USER->id);
201 $results = self::recordset_to_indexed_array($area->get_document_recordset(
202 0, $usercontext));
203 $this->assertCount(0, $results);
205 // Category 1 context (courses 1 and 2).
206 $results = self::recordset_to_indexed_array($area->get_document_recordset(
207 0, $cat1context));
208 $this->assertEquals([$block1id, $block6id, $block7id, $block8id],
209 self::records_to_ids($results));
211 // Category 2 context (course 3).
212 $results = self::recordset_to_indexed_array($area->get_document_recordset(
213 0, $cat2context));
214 $this->assertEquals([$block9id], self::records_to_ids($results));
216 // Combine context restriction (category 1) with timemodified.
217 $results = self::recordset_to_indexed_array($area->get_document_recordset(
218 7, $cat1context));
219 $this->assertEquals([$block7id, $block8id], self::records_to_ids($results));
223 * Utility function to convert recordset to array for testing.
225 * @param moodle_recordset $rs Recordset to convert
226 * @return array Array indexed by number (0, 1, 2, ...)
228 protected static function recordset_to_indexed_array(moodle_recordset $rs) {
229 $results = [];
230 foreach ($rs as $rec) {
231 $results[] = $rec;
233 $rs->close();
234 return $results;
238 * Utility function to convert records to array of IDs.
240 * @param array $recs Records which should have an 'id' field
241 * @return array Array of ids
243 protected static function records_to_ids(array $recs) {
244 $ids = [];
245 foreach ($recs as $rec) {
246 $ids[] = $rec->id;
248 return $ids;
252 * Tests the get_doc_url function.
254 public function test_get_doc_url() {
255 global $DB;
257 $this->resetAfterTest();
259 // Create course and activity module.
260 $generator = $this->getDataGenerator();
261 $course = $generator->create_course();
262 $coursecontext = \context_course::instance($course->id);
263 $page = $generator->create_module('page', ['course' => $course->id]);
264 $pagecontext = \context_module::instance($page->cmid);
266 // Create block on course page.
267 $configdata = base64_encode(serialize(new \stdClass()));
268 $instance = (object)['blockname' => 'mockblock', 'parentcontextid' => $coursecontext->id,
269 'showinsubcontexts' => 0, 'pagetypepattern' => 'course-view-*', 'defaultweight' => 0,
270 'timecreated' => 1, 'timemodified' => 1, 'configdata' => $configdata];
271 $blockid = $DB->insert_record('block_instances', $instance);
273 // Get document URL.
274 $area = new block_mockblock\search\area();
275 $doc = $this->get_doc($course->id, $blockid);
276 $expected = new moodle_url('/course/view.php', ['id' => $course->id], 'inst' . $blockid);
277 $this->assertEquals($expected, $area->get_doc_url($doc));
278 $this->assertEquals($expected, $area->get_context_url($doc));
280 // Repeat with block on site page.
281 $sitecourse = get_site();
282 $sitecontext = \context_course::instance($sitecourse->id);
283 $instance->pagetypepattern = 'site-index';
284 $instance->parentcontextid = $sitecontext->id;
285 $block2id = $DB->insert_record('block_instances', $instance);
287 // Get document URL.
288 $doc2 = $this->get_doc($course->id, $block2id);
289 $expected = new moodle_url('/', ['redirect' => 0], 'inst' . $block2id);
290 $this->assertEquals($expected, $area->get_doc_url($doc2));
291 $this->assertEquals($expected, $area->get_context_url($doc2));
293 // Repeat with block on module page (this cannot happen yet because the search query will
294 // only include course context blocks, but let's check it works for the future).
295 $instance->pagetypepattern = 'mod-page-view';
296 $instance->parentcontextid = $pagecontext->id;
297 $block3id = $DB->insert_record('block_instances', $instance);
299 // Get and check document URL, ignoring debugging message for unsupported page type.
300 $debugmessage = 'Unexpected module-level page type for block ' . $block3id .
301 ': mod-page-view';
302 $doc3 = $this->get_doc($course->id, $block3id);
303 $this->assertDebuggingCalledCount(2, [$debugmessage, $debugmessage]);
305 $expected = new moodle_url('/mod/page/view.php', ['id' => $page->cmid], 'inst' . $block3id);
306 $this->assertEquals($expected, $area->get_doc_url($doc3));
307 $this->assertDebuggingCalled($debugmessage);
308 $this->assertEquals($expected, $area->get_context_url($doc3));
309 $this->assertDebuggingCalled($debugmessage);
311 // Repeat with another block on course page but '*' pages.
312 $instance->pagetypepattern = '*';
313 $instance->parentcontextid = $coursecontext->id;
314 $block4id = $DB->insert_record('block_instances', $instance);
316 // Get document URL.
317 $doc = $this->get_doc($course->id, $block4id);
318 $expected = new moodle_url('/course/view.php', ['id' => $course->id], 'inst' . $block4id);
319 $this->assertEquals($expected, $area->get_doc_url($doc));
320 $this->assertEquals($expected, $area->get_context_url($doc));
322 // And same thing but 'course-*' pages.
323 $instance->pagetypepattern = 'course-*';
324 $block5id = $DB->insert_record('block_instances', $instance);
326 // Get document URL.
327 $doc = $this->get_doc($course->id, $block5id);
328 $expected = new moodle_url('/course/view.php', ['id' => $course->id], 'inst' . $block5id);
329 $this->assertEquals($expected, $area->get_doc_url($doc));
330 $this->assertEquals($expected, $area->get_context_url($doc));
334 * Tests the check_access function.
336 public function test_check_access() {
337 global $DB;
339 $this->resetAfterTest();
341 // Create course and activity module.
342 $generator = $this->getDataGenerator();
343 $course = $generator->create_course();
344 $coursecontext = \context_course::instance($course->id);
345 $page = $generator->create_module('page', ['course' => $course->id]);
346 $pagecontext = \context_module::instance($page->cmid);
348 // Create block on course page.
349 $configdata = base64_encode(serialize(new \stdClass()));
350 $instance = (object)['blockname' => 'mockblock', 'parentcontextid' => $coursecontext->id,
351 'showinsubcontexts' => 0, 'pagetypepattern' => 'course-view-*', 'defaultweight' => 0,
352 'timecreated' => 1, 'timemodified' => 1, 'configdata' => $configdata];
353 $blockid = $DB->insert_record('block_instances', $instance);
355 // Check access for block that exists.
356 $area = new block_mockblock\search\area();
357 $this->assertEquals(\core_search\manager::ACCESS_GRANTED, $area->check_access($blockid));
359 // Check access for nonexistent block.
360 $this->assertEquals(\core_search\manager::ACCESS_DELETED, $area->check_access($blockid + 1));
362 // Check if block is not in a course context any longer.
363 $DB->set_field('block_instances', 'parentcontextid', $pagecontext->id, ['id' => $blockid]);
364 \core_search\base_block::clear_static();
365 $this->assertEquals(\core_search\manager::ACCESS_DELETED, $area->check_access($blockid));
367 // Or what if it is in a course context but has supported vs. unsupported page type.
368 $DB->set_field('block_instances', 'parentcontextid', $coursecontext->id, ['id' => $blockid]);
370 $DB->set_field('block_instances', 'pagetypepattern', 'course-*', ['id' => $blockid]);
371 \core_search\base_block::clear_static();
372 $this->assertEquals(\core_search\manager::ACCESS_GRANTED, $area->check_access($blockid));
374 $DB->set_field('block_instances', 'pagetypepattern', '*', ['id' => $blockid]);
375 \core_search\base_block::clear_static();
376 $this->assertEquals(\core_search\manager::ACCESS_GRANTED, $area->check_access($blockid));
378 $DB->set_field('block_instances', 'pagetypepattern', 'course-view-frogs', ['id' => $blockid]);
379 \core_search\base_block::clear_static();
380 $this->assertEquals(\core_search\manager::ACCESS_GRANTED, $area->check_access($blockid));
382 $DB->set_field('block_instances', 'pagetypepattern', 'anythingelse', ['id' => $blockid]);
383 \core_search\base_block::clear_static();
384 $this->assertEquals(\core_search\manager::ACCESS_DELETED, $area->check_access($blockid));
388 * Tests the block version of get_contexts_to_reindex, which is supposed to return all the
389 * block contexts in order of date added.
391 public function test_get_contexts_to_reindex() {
392 global $DB;
394 $this->resetAfterTest();
396 // Create course and activity module.
397 $generator = $this->getDataGenerator();
398 $course = $generator->create_course();
399 $coursecontext = \context_course::instance($course->id);
400 $page = $generator->create_module('page', ['course' => $course->id]);
401 $pagecontext = \context_module::instance($page->cmid);
403 // Create blocks on course page, with time modified non-sequential.
404 $configdata = base64_encode(serialize(new \stdClass()));
405 $instance = (object)['blockname' => 'mockblock', 'parentcontextid' => $coursecontext->id,
406 'showinsubcontexts' => 0, 'pagetypepattern' => 'course-view-*', 'defaultweight' => 0,
407 'timecreated' => 1, 'timemodified' => 100, 'configdata' => $configdata];
408 $blockid1 = $DB->insert_record('block_instances', $instance);
409 $context1 = \context_block::instance($blockid1);
410 $instance->timemodified = 120;
411 $blockid2 = $DB->insert_record('block_instances', $instance);
412 $context2 = \context_block::instance($blockid2);
413 $instance->timemodified = 110;
414 $blockid3 = $DB->insert_record('block_instances', $instance);
415 $context3 = \context_block::instance($blockid3);
417 // Create another block on the activity page (not included).
418 $instance->parentcontextid = $pagecontext->id;
419 $blockid4 = $DB->insert_record('block_instances', $instance);
420 \context_block::instance($blockid4);
422 // Check list of contexts.
423 $area = new block_mockblock\search\area();
424 $contexts = iterator_to_array($area->get_contexts_to_reindex(), false);
425 $expected = [
426 $context2,
427 $context3,
428 $context1
430 $this->assertEquals($expected, $contexts);
434 * Gets a search document object from the fake search area.
436 * @param int $courseid Course id in document
437 * @param int $blockinstanceid Block instance id in document
438 * @return \core_search\document Document object
440 protected function get_doc($courseid, $blockinstanceid) {
441 $engine = testable_core_search::instance()->get_engine();
442 $area = new block_mockblock\search\area();
443 $docdata = ['id' => $blockinstanceid, 'courseid' => $courseid,
444 'areaid' => $area->get_area_id(), 'itemid' => 0];
445 return $engine->to_document($area, $docdata);