moodle/search/tests/base_block_test.php
Eloy Lafuente (stronk7) 01148a0816
MDL-81522 phpunit: Add missing void return type to all tests
While this change is not 100% required now, it's good habit
and we are checking for it since Moodle 4.4.

All the changes in this commit have been applied automatically
using the moodle.PHPUnit.TestReturnType sniff and are, exclusively
adding the ": void" return types when missing.
2024-06-11 11:55:07 +02:00

460 lines
20 KiB
PHP

<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
namespace core_search;
defined('MOODLE_INTERNAL') || die();
require_once(__DIR__ . '/fixtures/testable_core_search.php');
require_once(__DIR__ . '/fixtures/mock_block_area.php');
/**
* Unit tests for the base_block class.
*
* @package core_search
* @copyright 2017 The Open University
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class base_block_test extends \advanced_testcase {
/**
* Tests getting the name out of the class name.
*/
public function test_get_block_name(): void {
$area = new \block_mockblock\search\area();
$this->assertEquals('mockblock', $area->get_block_name());
}
/**
* Tests getting the recordset.
*/
public function test_get_document_recordset(): void {
global $DB, $USER;
$this->resetAfterTest();
$this->setAdminUser();
// Create course and activity module.
$generator = $this->getDataGenerator();
$course = $generator->create_course();
$coursecontext = \context_course::instance($course->id);
$page = $generator->create_module('page', ['course' => $course->id]);
$pagecontext = \context_module::instance($page->cmid);
// Create another 2 courses (in same category and in a new category).
$cat1context = \context_coursecat::instance($course->category);
$course2 = $generator->create_course();
$course2context = \context_course::instance($course2->id);
$cat2 = $generator->create_category();
$cat2context = \context_coursecat::instance($cat2->id);
$course3 = $generator->create_course(['category' => $cat2->id]);
$course3context = \context_course::instance($course3->id);
// Add blocks by hacking table (because it's not a real block type).
// 1. Block on course page.
$configdata = base64_encode(serialize((object) ['example' => 'content']));
$instance = (object)['blockname' => 'mockblock', 'parentcontextid' => $coursecontext->id,
'showinsubcontexts' => 0, 'pagetypepattern' => 'course-view-*',
'defaultweight' => 0, 'timecreated' => 1, 'timemodified' => 1,
'configdata' => $configdata];
$block1id = $DB->insert_record('block_instances', $instance);
$block1context = \context_block::instance($block1id);
// 2. Block on activity page.
$instance->parentcontextid = $pagecontext->id;
$instance->pagetypepattern = 'mod-page-view';
$instance->timemodified = 2;
$block2id = $DB->insert_record('block_instances', $instance);
\context_block::instance($block2id);
// 3. Block on site context.
$sitecourse = get_site();
$sitecontext = \context_course::instance($sitecourse->id);
$instance->parentcontextid = $sitecontext->id;
$instance->pagetypepattern = 'site-index';
$instance->timemodified = 3;
$block3id = $DB->insert_record('block_instances', $instance);
$block3context = \context_block::instance($block3id);
// 4. Block on course page but no data.
$instance->parentcontextid = $coursecontext->id;
$instance->pagetypepattern = 'course-view-*';
unset($instance->configdata);
$instance->timemodified = 4;
$block4id = $DB->insert_record('block_instances', $instance);
\context_block::instance($block4id);
// 5. Block on course page but not this block.
$instance->blockname = 'mockotherblock';
$instance->configdata = $configdata;
$instance->timemodified = 5;
$block5id = $DB->insert_record('block_instances', $instance);
\context_block::instance($block5id);
// 6. Block on course page with '*' page type.
$instance->blockname = 'mockblock';
$instance->pagetypepattern = '*';
$instance->timemodified = 6;
$block6id = $DB->insert_record('block_instances', $instance);
\context_block::instance($block6id);
// 7. Block on course page with 'course-*' page type.
$instance->pagetypepattern = 'course-*';
$instance->timemodified = 7;
$block7id = $DB->insert_record('block_instances', $instance);
\context_block::instance($block7id);
// 8. Block on course 2.
$instance->parentcontextid = $course2context->id;
$instance->timemodified = 8;
$block8id = $DB->insert_record('block_instances', $instance);
\context_block::instance($block8id);
// 9. Block on course 3.
$instance->parentcontextid = $course3context->id;
$instance->timemodified = 9;
$block9id = $DB->insert_record('block_instances', $instance);
\context_block::instance($block9id);
// Get all the blocks.
$area = new \block_mockblock\search\area();
$results = self::recordset_to_indexed_array($area->get_document_recordset());
// Only blocks 1, 3, 6, 7, 8, 9 should be returned. Check all the fields for the first two.
$this->assertCount(6, $results);
$this->assertEquals($block1id, $results[0]->id);
$this->assertEquals(1, $results[0]->timemodified);
$this->assertEquals(1, $results[0]->timecreated);
$this->assertEquals($configdata, $results[0]->configdata);
$this->assertEquals($course->id, $results[0]->courseid);
$this->assertEquals($block1context->id, $results[0]->contextid);
$this->assertEquals($block3id, $results[1]->id);
$this->assertEquals(3, $results[1]->timemodified);
$this->assertEquals(1, $results[1]->timecreated);
$this->assertEquals($configdata, $results[1]->configdata);
$this->assertEquals($sitecourse->id, $results[1]->courseid);
$this->assertEquals($block3context->id, $results[1]->contextid);
// For the later ones, just check it got the right ones!
$this->assertEquals($block6id, $results[2]->id);
$this->assertEquals($block7id, $results[3]->id);
$this->assertEquals($block8id, $results[4]->id);
$this->assertEquals($block9id, $results[5]->id);
// Repeat with a time restriction.
$results = self::recordset_to_indexed_array($area->get_document_recordset(2));
// Only block 3, 6, 7, 8, and 9 are returned.
$this->assertEquals([$block3id, $block6id, $block7id, $block8id, $block9id],
self::records_to_ids($results));
// Now use context restrictions. First, the whole site (no change).
$results = self::recordset_to_indexed_array($area->get_document_recordset(
0, \context_system::instance()));
$this->assertEquals([$block1id, $block3id, $block6id, $block7id, $block8id, $block9id],
self::records_to_ids($results));
// Course page only (leave out the one on site page and other courses).
$results = self::recordset_to_indexed_array($area->get_document_recordset(
0, $coursecontext));
$this->assertEquals([$block1id, $block6id, $block7id],
self::records_to_ids($results));
// Other course page only.
$results = self::recordset_to_indexed_array($area->get_document_recordset(
0, $course2context));
$this->assertEquals([$block8id], self::records_to_ids($results));
// Activity module only (no results).
$results = self::recordset_to_indexed_array($area->get_document_recordset(
0, $pagecontext));
$this->assertCount(0, $results);
// Specific block context.
$results = self::recordset_to_indexed_array($area->get_document_recordset(
0, $block3context));
$this->assertEquals([$block3id], self::records_to_ids($results));
// User context (no results).
$usercontext = \context_user::instance($USER->id);
$results = self::recordset_to_indexed_array($area->get_document_recordset(
0, $usercontext));
$this->assertCount(0, $results);
// Category 1 context (courses 1 and 2).
$results = self::recordset_to_indexed_array($area->get_document_recordset(
0, $cat1context));
$this->assertEquals([$block1id, $block6id, $block7id, $block8id],
self::records_to_ids($results));
// Category 2 context (course 3).
$results = self::recordset_to_indexed_array($area->get_document_recordset(
0, $cat2context));
$this->assertEquals([$block9id], self::records_to_ids($results));
// Combine context restriction (category 1) with timemodified.
$results = self::recordset_to_indexed_array($area->get_document_recordset(
7, $cat1context));
$this->assertEquals([$block7id, $block8id], self::records_to_ids($results));
}
/**
* Utility function to convert recordset to array for testing.
*
* @param \moodle_recordset $rs Recordset to convert
* @return array Array indexed by number (0, 1, 2, ...)
*/
protected static function recordset_to_indexed_array(\moodle_recordset $rs) {
$results = [];
foreach ($rs as $rec) {
$results[] = $rec;
}
$rs->close();
return $results;
}
/**
* Utility function to convert records to array of IDs.
*
* @param array $recs Records which should have an 'id' field
* @return array Array of ids
*/
protected static function records_to_ids(array $recs) {
$ids = [];
foreach ($recs as $rec) {
$ids[] = $rec->id;
}
return $ids;
}
/**
* Tests the get_doc_url function.
*/
public function test_get_doc_url(): void {
global $DB;
$this->resetAfterTest();
// Create course and activity module.
$generator = $this->getDataGenerator();
$course = $generator->create_course();
$coursecontext = \context_course::instance($course->id);
$page = $generator->create_module('page', ['course' => $course->id]);
$pagecontext = \context_module::instance($page->cmid);
// Create block on course page.
$configdata = base64_encode(serialize(new \stdClass()));
$instance = (object)['blockname' => 'mockblock', 'parentcontextid' => $coursecontext->id,
'showinsubcontexts' => 0, 'pagetypepattern' => 'course-view-*', 'defaultweight' => 0,
'timecreated' => 1, 'timemodified' => 1, 'configdata' => $configdata];
$blockid = $DB->insert_record('block_instances', $instance);
// Get document URL.
$area = new \block_mockblock\search\area();
$doc = $this->get_doc($course->id, $blockid);
$expected = new \moodle_url('/course/view.php', ['id' => $course->id], 'inst' . $blockid);
$this->assertEquals($expected, $area->get_doc_url($doc));
$this->assertEquals($expected, $area->get_context_url($doc));
// Repeat with block on site page.
$sitecourse = get_site();
$sitecontext = \context_course::instance($sitecourse->id);
$instance->pagetypepattern = 'site-index';
$instance->parentcontextid = $sitecontext->id;
$block2id = $DB->insert_record('block_instances', $instance);
// Get document URL.
$doc2 = $this->get_doc($course->id, $block2id);
$expected = new \moodle_url('/', ['redirect' => 0], 'inst' . $block2id);
$this->assertEquals($expected, $area->get_doc_url($doc2));
$this->assertEquals($expected, $area->get_context_url($doc2));
// Repeat with block on module page (this cannot happen yet because the search query will
// only include course context blocks, but let's check it works for the future).
$instance->pagetypepattern = 'mod-page-view';
$instance->parentcontextid = $pagecontext->id;
$block3id = $DB->insert_record('block_instances', $instance);
// Get and check document URL, ignoring debugging message for unsupported page type.
$debugmessage = 'Unexpected module-level page type for block ' . $block3id .
': mod-page-view';
$doc3 = $this->get_doc($course->id, $block3id);
$this->assertDebuggingCalledCount(2, [$debugmessage, $debugmessage]);
$expected = new \moodle_url('/mod/page/view.php', ['id' => $page->cmid], 'inst' . $block3id);
$this->assertEquals($expected, $area->get_doc_url($doc3));
$this->assertDebuggingCalled($debugmessage);
$this->assertEquals($expected, $area->get_context_url($doc3));
$this->assertDebuggingCalled($debugmessage);
// Repeat with another block on course page but '*' pages.
$instance->pagetypepattern = '*';
$instance->parentcontextid = $coursecontext->id;
$block4id = $DB->insert_record('block_instances', $instance);
// Get document URL.
$doc = $this->get_doc($course->id, $block4id);
$expected = new \moodle_url('/course/view.php', ['id' => $course->id], 'inst' . $block4id);
$this->assertEquals($expected, $area->get_doc_url($doc));
$this->assertEquals($expected, $area->get_context_url($doc));
// And same thing but 'course-*' pages.
$instance->pagetypepattern = 'course-*';
$block5id = $DB->insert_record('block_instances', $instance);
// Get document URL.
$doc = $this->get_doc($course->id, $block5id);
$expected = new \moodle_url('/course/view.php', ['id' => $course->id], 'inst' . $block5id);
$this->assertEquals($expected, $area->get_doc_url($doc));
$this->assertEquals($expected, $area->get_context_url($doc));
}
/**
* Tests the check_access function.
*/
public function test_check_access(): void {
global $DB;
$this->resetAfterTest();
// Create course and activity module.
$generator = $this->getDataGenerator();
$course = $generator->create_course();
$coursecontext = \context_course::instance($course->id);
$page = $generator->create_module('page', ['course' => $course->id]);
$pagecontext = \context_module::instance($page->cmid);
// Create block on course page.
$configdata = base64_encode(serialize(new \stdClass()));
$instance = (object)['blockname' => 'mockblock', 'parentcontextid' => $coursecontext->id,
'showinsubcontexts' => 0, 'pagetypepattern' => 'course-view-*', 'defaultweight' => 0,
'timecreated' => 1, 'timemodified' => 1, 'configdata' => $configdata];
$blockid = $DB->insert_record('block_instances', $instance);
// Check access for block that exists.
$area = new \block_mockblock\search\area();
$this->assertEquals(\core_search\manager::ACCESS_GRANTED, $area->check_access($blockid));
// Check access for nonexistent block.
$this->assertEquals(\core_search\manager::ACCESS_DELETED, $area->check_access($blockid + 1));
// Check if block is not in a course context any longer.
$DB->set_field('block_instances', 'parentcontextid', $pagecontext->id, ['id' => $blockid]);
\core_search\base_block::clear_static();
$this->assertEquals(\core_search\manager::ACCESS_DELETED, $area->check_access($blockid));
// Or what if it is in a course context but has supported vs. unsupported page type.
$DB->set_field('block_instances', 'parentcontextid', $coursecontext->id, ['id' => $blockid]);
$DB->set_field('block_instances', 'pagetypepattern', 'course-*', ['id' => $blockid]);
\core_search\base_block::clear_static();
$this->assertEquals(\core_search\manager::ACCESS_GRANTED, $area->check_access($blockid));
$DB->set_field('block_instances', 'pagetypepattern', '*', ['id' => $blockid]);
\core_search\base_block::clear_static();
$this->assertEquals(\core_search\manager::ACCESS_GRANTED, $area->check_access($blockid));
$DB->set_field('block_instances', 'pagetypepattern', 'course-view-frogs', ['id' => $blockid]);
\core_search\base_block::clear_static();
$this->assertEquals(\core_search\manager::ACCESS_GRANTED, $area->check_access($blockid));
$DB->set_field('block_instances', 'pagetypepattern', 'anythingelse', ['id' => $blockid]);
\core_search\base_block::clear_static();
$this->assertEquals(\core_search\manager::ACCESS_DELETED, $area->check_access($blockid));
}
/**
* Tests the block version of get_contexts_to_reindex, which is supposed to return all the
* block contexts in order of date added.
*/
public function test_get_contexts_to_reindex(): void {
global $DB;
$this->resetAfterTest();
// Create course and activity module.
$generator = $this->getDataGenerator();
$course = $generator->create_course();
$coursecontext = \context_course::instance($course->id);
$page = $generator->create_module('page', ['course' => $course->id]);
$pagecontext = \context_module::instance($page->cmid);
// Create blocks on course page, with time modified non-sequential.
$configdata = base64_encode(serialize(new \stdClass()));
$instance = (object)['blockname' => 'mockblock', 'parentcontextid' => $coursecontext->id,
'showinsubcontexts' => 0, 'pagetypepattern' => 'course-view-*', 'defaultweight' => 0,
'timecreated' => 1, 'timemodified' => 100, 'configdata' => $configdata];
$blockid1 = $DB->insert_record('block_instances', $instance);
$context1 = \context_block::instance($blockid1);
$instance->timemodified = 120;
$blockid2 = $DB->insert_record('block_instances', $instance);
$context2 = \context_block::instance($blockid2);
$instance->timemodified = 110;
$blockid3 = $DB->insert_record('block_instances', $instance);
$context3 = \context_block::instance($blockid3);
// Create another block on the activity page (not included).
$instance->parentcontextid = $pagecontext->id;
$blockid4 = $DB->insert_record('block_instances', $instance);
\context_block::instance($blockid4);
// Check list of contexts.
$area = new \block_mockblock\search\area();
$contexts = iterator_to_array($area->get_contexts_to_reindex(), false);
$expected = [
$context2,
$context3,
$context1
];
$this->assertEquals($expected, $contexts);
}
/**
* Gets a search document object from the fake search area.
*
* @param int $courseid Course id in document
* @param int $blockinstanceid Block instance id in document
* @return \core_search\document Document object
*/
protected function get_doc($courseid, $blockinstanceid) {
$engine = \testable_core_search::instance()->get_engine();
$area = new \block_mockblock\search\area();
$docdata = ['id' => $blockinstanceid, 'courseid' => $courseid,
'areaid' => $area->get_area_id(), 'itemid' => 0];
return $engine->to_document($area, $docdata);
}
/**
* Test document icon.
*/
public function test_get_doc_icon(): void {
$baseblock = $this->getMockBuilder('\core_search\base_block')
->disableOriginalConstructor()
->getMockForAbstractClass();
$document = $this->getMockBuilder('\core_search\document')
->disableOriginalConstructor()
->getMock();
$result = $baseblock->get_doc_icon($document);
$this->assertEquals('e/anchor', $result->get_name());
$this->assertEquals('moodle', $result->get_component());
}
}