2 // This file is part of Moodle - https://moodle.org/
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.
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 <https://www.gnu.org/licenses/>.
20 * Unit tests for context helper class.
22 * NOTE: more tests are in lib/tests/accesslib_test.php
25 * @copyright Petr Skoda
26 * @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
27 * @coversDefaultClass \core\context_helper
29 class context_helper_test
extends \advanced_testcase
{
31 * Tests covered method.
32 * @covers ::parse_external_level
34 public function test_parse_external_level() {
35 $this->assertSame(context\system
::class, context_helper
::parse_external_level('system'));
36 $this->assertSame(context\system
::class, context_helper
::parse_external_level(CONTEXT_SYSTEM
));
37 $this->assertSame(context\system
::class, context_helper
::parse_external_level((string)CONTEXT_SYSTEM
));
39 $this->assertSame(context\user
::class, context_helper
::parse_external_level('user'));
40 $this->assertSame(context\user
::class, context_helper
::parse_external_level(CONTEXT_USER
));
41 $this->assertSame(context\user
::class, context_helper
::parse_external_level((string)CONTEXT_USER
));
43 $this->assertSame(context\coursecat
::class, context_helper
::parse_external_level('coursecat'));
44 $this->assertSame(context\coursecat
::class, context_helper
::parse_external_level(CONTEXT_COURSECAT
));
45 $this->assertSame(context\coursecat
::class, context_helper
::parse_external_level((string)CONTEXT_COURSECAT
));
47 $this->assertSame(context\course
::class, context_helper
::parse_external_level('course'));
48 $this->assertSame(context\course
::class, context_helper
::parse_external_level(CONTEXT_COURSE
));
49 $this->assertSame(context\course
::class, context_helper
::parse_external_level((string)CONTEXT_COURSE
));
51 $this->assertSame(context\module
::class, context_helper
::parse_external_level('module'));
52 $this->assertSame(context\module
::class, context_helper
::parse_external_level(CONTEXT_MODULE
));
53 $this->assertSame(context\module
::class, context_helper
::parse_external_level((string)CONTEXT_MODULE
));
55 $this->assertSame(context\block
::class, context_helper
::parse_external_level('block'));
56 $this->assertSame(context\block
::class, context_helper
::parse_external_level(CONTEXT_BLOCK
));
57 $this->assertSame(context\block
::class, context_helper
::parse_external_level((string)CONTEXT_BLOCK
));
59 $this->assertNull(context_helper
::parse_external_level('core_system'));
60 $this->assertNull(context_helper
::parse_external_level('xsystem'));
61 $this->assertNull(context_helper
::parse_external_level(1));
62 $this->assertNull(context_helper
::parse_external_level(''));
66 * Tests covered method.
67 * @covers ::resolve_behat_reference
69 public function test_resolve_behat_reference() {
70 $this->assertNull(context_helper
::resolve_behat_reference('blahbla', 'blahbla'));
71 $this->assertNull(context_helper
::resolve_behat_reference('', ''));
72 $this->assertNull(context_helper
::resolve_behat_reference('0', ''));
74 $syscontext = context\system
::instance();
75 $result = context_helper
::resolve_behat_reference('System', '');
76 $this->assertSame($syscontext->id
, $result->id
);
78 $syscontext = context\system
::instance();
79 $result = context_helper
::resolve_behat_reference('10', '');
80 $this->assertSame($syscontext->id
, $result->id
);
82 // The rest is tested in each context class test.
86 * Tests covered method.
87 * @covers ::get_class_for_level
89 public function test_get_class_for_level() {
90 $this->assertSame(context\system
::class, context_helper
::get_class_for_level(CONTEXT_SYSTEM
));
91 $this->assertSame(context\system
::class, context_helper
::get_class_for_level((string)CONTEXT_SYSTEM
));
93 $this->assertSame(context\user
::class, context_helper
::get_class_for_level(CONTEXT_USER
));
94 $this->assertSame(context\user
::class, context_helper
::get_class_for_level((string)CONTEXT_USER
));
96 $this->assertSame(context\coursecat
::class, context_helper
::get_class_for_level(CONTEXT_COURSECAT
));
97 $this->assertSame(context\coursecat
::class, context_helper
::get_class_for_level((string)CONTEXT_COURSECAT
));
99 $this->assertSame(context\course
::class, context_helper
::get_class_for_level(CONTEXT_COURSE
));
100 $this->assertSame(context\course
::class, context_helper
::get_class_for_level((string)CONTEXT_COURSE
));
102 $this->assertSame(context\module
::class, context_helper
::get_class_for_level(CONTEXT_MODULE
));
103 $this->assertSame(context\module
::class, context_helper
::get_class_for_level((string)CONTEXT_MODULE
));
105 $this->assertSame(context\block
::class, context_helper
::get_class_for_level(CONTEXT_BLOCK
));
106 $this->assertSame(context\block
::class, context_helper
::get_class_for_level((string)CONTEXT_BLOCK
));
109 context_helper
::get_class_for_level(1);
110 $this->fail('Exception expected if level does not exist');
111 } catch (\moodle_exception
$e) {
112 $this->assertInstanceOf(\coding_exception
::class, $e);
113 $this->assertSame('Coding error detected, it must be fixed by a programmer: Invalid context level specified',
119 * Tests covered method.
120 * @covers ::get_all_levels
122 public function test_get_all_levels() {
123 $levels = context_helper
::get_all_levels();
125 $this->assertArrayHasKey(CONTEXT_SYSTEM
, $levels);
126 $this->assertSame(context\system
::class, $levels[CONTEXT_SYSTEM
]);
128 $this->assertArrayHasKey(CONTEXT_USER
, $levels);
129 $this->assertSame(context\user
::class, $levels[CONTEXT_USER
]);
131 $this->assertArrayHasKey(CONTEXT_COURSECAT
, $levels);
132 $this->assertSame(context\coursecat
::class, $levels[CONTEXT_COURSECAT
]);
134 $this->assertArrayHasKey(CONTEXT_COURSE
, $levels);
135 $this->assertSame(context\course
::class, $levels[CONTEXT_COURSE
]);
137 $this->assertArrayHasKey(CONTEXT_MODULE
, $levels);
138 $this->assertSame(context\module
::class, $levels[CONTEXT_MODULE
]);
140 $this->assertArrayHasKey(CONTEXT_BLOCK
, $levels);
141 $this->assertSame(context\block
::class, $levels[CONTEXT_BLOCK
]);
144 ksort($sorted, SORT_NUMERIC
);
145 $block = $sorted[CONTEXT_BLOCK
];
146 unset($sorted[CONTEXT_BLOCK
]);
147 $sorted[CONTEXT_BLOCK
] = $block;
148 $this->assertSame(array_keys($sorted), array_keys($levels));
150 // Make sure level is set properly.
151 foreach ($levels as $level => $classname) {
152 $this->assertEquals($level, $classname::LEVEL
);
153 if ($level != CONTEXT_SYSTEM
) {
154 $this->assertGreaterThan(CONTEXT_SYSTEM
, $level);
160 * Tests covered method.
161 * @covers ::get_child_levels
163 public function test_get_child_levels() {
164 $alllevels = context_helper
::get_all_levels();
166 $childlevels = context_helper
::get_child_levels(CONTEXT_SYSTEM
);
167 $this->assertSame(count($alllevels) - 1, count($childlevels));
169 $childlevels = context_helper
::get_child_levels(CONTEXT_USER
);
170 $this->assertNotContains(CONTEXT_SYSTEM
, $childlevels);
171 $this->assertNotContains(CONTEXT_USER
, $childlevels);
172 $this->assertNotContains(CONTEXT_COURSECAT
, $childlevels);
173 $this->assertNotContains(CONTEXT_COURSE
, $childlevels);
174 $this->assertNotContains(CONTEXT_MODULE
, $childlevels);
175 $this->assertContains(CONTEXT_BLOCK
, $childlevels);
177 $childlevels = context_helper
::get_child_levels(CONTEXT_COURSECAT
);
178 $this->assertNotContains(CONTEXT_SYSTEM
, $childlevels);
179 $this->assertNotContains(CONTEXT_USER
, $childlevels);
180 $this->assertContains(CONTEXT_COURSECAT
, $childlevels);
181 $this->assertContains(CONTEXT_COURSE
, $childlevels);
182 $this->assertContains(CONTEXT_MODULE
, $childlevels);
183 $this->assertContains(CONTEXT_BLOCK
, $childlevels);
185 $childlevels = context_helper
::get_child_levels(CONTEXT_COURSE
);
186 $this->assertNotContains(CONTEXT_SYSTEM
, $childlevels);
187 $this->assertNotContains(CONTEXT_USER
, $childlevels);
188 $this->assertNotContains(CONTEXT_COURSECAT
, $childlevels);
189 $this->assertNotContains(CONTEXT_COURSE
, $childlevels);
190 $this->assertContains(CONTEXT_MODULE
, $childlevels);
191 $this->assertContains(CONTEXT_BLOCK
, $childlevels);
193 $childlevels = context_helper
::get_child_levels(CONTEXT_MODULE
);
194 $this->assertNotContains(CONTEXT_SYSTEM
, $childlevels);
195 $this->assertNotContains(CONTEXT_USER
, $childlevels);
196 $this->assertNotContains(CONTEXT_COURSECAT
, $childlevels);
197 $this->assertNotContains(CONTEXT_COURSE
, $childlevels);
198 $this->assertNotContains(CONTEXT_MODULE
, $childlevels);
199 $this->assertContains(CONTEXT_BLOCK
, $childlevels);
201 $childlevels = context_helper
::get_child_levels(CONTEXT_BLOCK
);
202 $this->assertCount(0, $childlevels);
206 * Tests covered method.
207 * @covers ::get_compatible_levels
209 public function test_get_compatible_levels() {
210 $levels = context_helper
::get_compatible_levels('manager');
211 $this->assertContains(CONTEXT_SYSTEM
, $levels);
212 $this->assertNotContains(CONTEXT_USER
, $levels);
213 $this->assertContains(CONTEXT_COURSECAT
, $levels);
214 $this->assertContains(CONTEXT_COURSE
, $levels);
215 $this->assertNotContains(CONTEXT_MODULE
, $levels);
216 $this->assertNotContains(CONTEXT_BLOCK
, $levels);
218 $levels = context_helper
::get_compatible_levels('coursecreator');
219 $this->assertContains(CONTEXT_SYSTEM
, $levels);
220 $this->assertNotContains(CONTEXT_USER
, $levels);
221 $this->assertContains(CONTEXT_COURSECAT
, $levels);
222 $this->assertNotContains(CONTEXT_COURSE
, $levels);
223 $this->assertNotContains(CONTEXT_MODULE
, $levels);
224 $this->assertNotContains(CONTEXT_BLOCK
, $levels);
226 $levels = context_helper
::get_compatible_levels('editingteacher');
227 $this->assertNotContains(CONTEXT_SYSTEM
, $levels);
228 $this->assertNotContains(CONTEXT_USER
, $levels);
229 $this->assertNotContains(CONTEXT_COURSECAT
, $levels);
230 $this->assertContains(CONTEXT_COURSE
, $levels);
231 $this->assertContains(CONTEXT_MODULE
, $levels);
232 $this->assertNotContains(CONTEXT_BLOCK
, $levels);
234 $levels = context_helper
::get_compatible_levels('teacher');
235 $this->assertNotContains(CONTEXT_SYSTEM
, $levels);
236 $this->assertNotContains(CONTEXT_USER
, $levels);
237 $this->assertNotContains(CONTEXT_COURSECAT
, $levels);
238 $this->assertContains(CONTEXT_COURSE
, $levels);
239 $this->assertContains(CONTEXT_MODULE
, $levels);
240 $this->assertNotContains(CONTEXT_BLOCK
, $levels);
242 $levels = context_helper
::get_compatible_levels('student');
243 $this->assertNotContains(CONTEXT_SYSTEM
, $levels);
244 $this->assertNotContains(CONTEXT_USER
, $levels);
245 $this->assertNotContains(CONTEXT_COURSECAT
, $levels);
246 $this->assertContains(CONTEXT_COURSE
, $levels);
247 $this->assertContains(CONTEXT_MODULE
, $levels);
248 $this->assertNotContains(CONTEXT_BLOCK
, $levels);
250 $levels = context_helper
::get_compatible_levels('user');
251 $this->assertCount(0, $levels);
253 $levels = context_helper
::get_compatible_levels('guest');
254 $this->assertCount(0, $levels);
256 $levels = context_helper
::get_compatible_levels('frontpage');
257 $this->assertCount(0, $levels);
261 * Tests covered method.
262 * @covers ::cleanup_instances
264 public function test_cleanup_instances() {
267 $this->resetAfterTest();
268 $this->preventResetByRollback();
270 $prevcount = $DB->count_records('context', []);
271 context_helper
::cleanup_instances();
272 $count = $DB->count_records('context', []);
273 $this->assertSame($prevcount, $count);
275 // Insert bogus records for each level and see if they get removed,
276 // more test should be in tests for each context level.
277 $alllevels = context_helper
::get_all_levels();
278 foreach ($alllevels as $classname) {
279 if ($classname::LEVEL
== CONTEXT_SYSTEM
) {
282 $record = new \
stdClass();
283 $record->contextlevel
= $classname::LEVEL
;
284 $record->instanceid
= 9999999999;
285 $record->path
= null;
286 $record->depth
= '2';
287 $record->id
= $DB->insert_record('context', $record);
288 $DB->set_field('context', 'path', SYSCONTEXTID
. '/' . $record->id
, ['id' => $record->id
]);
290 context_helper
::cleanup_instances();
291 $count = $DB->count_records('context', []);
292 $this->assertSame($prevcount, $count);
296 * Tests covered method.
297 * @covers ::create_instances
299 public function test_create_instances() {
302 $this->resetAfterTest();
303 $this->preventResetByRollback();
305 $user = $this->getDataGenerator()->create_user();
306 $usercontext = context\user
::instance($user->id
);
307 $category = $this->getDataGenerator()->create_category();
308 $categorycontext = context\coursecat
::instance($category->id
);
309 $course = $this->getDataGenerator()->create_course();
310 $coursecontext = context\course
::instance($course->id
);
311 $page = $this->getDataGenerator()->create_module('page', ['course' => $course->id
]);
312 $pagecontext = context\module
::instance($page->cmid
);
314 $prevcount = $DB->count_records('context', []);
315 $DB->delete_records('context', ['id' => $usercontext->id
]);
316 $DB->delete_records('context', ['id' => $categorycontext->id
]);
317 $DB->delete_records('context', ['id' => $coursecontext->id
]);
318 $DB->delete_records('context', ['id' => $pagecontext->id
]);
320 context_helper
::create_instances();
321 $count = $DB->count_records('context', []);
322 $this->assertSame($prevcount, $count);
326 * Tests covered method.
327 * @covers ::build_all_paths
329 public function test_build_all_paths() {
330 $this->resetAfterTest();
331 $this->preventResetByRollback();
333 // Just make sure there are no fatal errors for now.
334 context_helper
::build_all_paths(true);
335 context_helper
::build_all_paths();
339 * Tests covered method.
340 * @covers ::reset_caches
342 public function test_reset_caches() {
343 $this->resetAfterTest();
344 $this->preventResetByRollback();
346 // Just make sure there are no fatal errors for now.
347 context_helper
::reset_caches();
351 * Tests covered method.
352 * @covers ::get_preload_record_columns
354 public function test_get_preload_record_columns() {
356 'testalias.id' => 'ctxid',
357 'testalias.path' => 'ctxpath',
358 'testalias.depth' => 'ctxdepth',
359 'testalias.contextlevel' => 'ctxlevel',
360 'testalias.instanceid' => 'ctxinstance',
361 'testalias.locked' => 'ctxlocked',
363 $result = context_helper
::get_preload_record_columns('testalias');
364 $this->assertSame($expected, $result);
368 * Tests covered method.
369 * @covers ::get_preload_record_columns_sql
371 public function test_get_preload_record_columns_sql() {
374 $result = context_helper
::get_preload_record_columns_sql('testalias');
375 $expected = 'testalias.id AS ctxid, testalias.path AS ctxpath, testalias.depth AS ctxdepth,'
376 .' testalias.contextlevel AS ctxlevel, testalias.instanceid AS ctxinstance, testalias.locked AS ctxlocked';
377 $this->assertSame($expected, $result);
379 $sql = "SELECT id, $result
380 FROM {context} testalias";
381 $DB->get_records_sql($sql, []);
385 * Tests covered method.
386 * @covers ::preload_from_record
388 public function test_preload_from_record() {
391 $select = context_helper
::get_preload_record_columns_sql('testalias');
392 $sql = "SELECT id, $select
393 FROM {context} testalias";
394 $records = $DB->get_records_sql($sql, []);
395 foreach ($records as $record) {
396 $this->assertNull(context_helper
::preload_from_record($record));
399 $this->assertDebuggingNotCalled();
400 $record = reset($records);
401 unset($record->ctxlevel
);
402 $this->assertNull(context_helper
::preload_from_record($record));
406 * Tests covered method.
407 * @covers ::preload_contexts_by_id
409 public function test_preload_contexts_by_id() {
412 $contextids = $DB->get_fieldset_sql("SELECT id FROM {context}", []);
413 context_helper
::preload_contexts_by_id($contextids);
414 context_helper
::preload_contexts_by_id($contextids);
416 context_helper
::reset_caches();
417 context_helper
::preload_contexts_by_id($contextids);
421 * Tests covered method.
422 * @covers ::preload_course
424 public function test_preload_course() {
426 context_helper
::preload_course($SITE->id
);
430 * Tests covered method.
431 * @covers ::delete_instance
433 public function test_delete_instance() {
434 $this->resetAfterTest();
436 $user = $this->getDataGenerator()->create_user();
437 $category = $this->getDataGenerator()->create_category();
438 $course = $this->getDataGenerator()->create_course();
439 $page = $this->getDataGenerator()->create_module('page', array('course' => $course->id
));
441 // This is a bit silly test, it might start failing in the future
442 // because the instances are not deleted before deleting the contexts.
443 context_helper
::delete_instance(CONTEXT_USER
, $user->id
);
444 context_helper
::delete_instance(CONTEXT_COURSECAT
, $category->id
);
445 context_helper
::delete_instance(CONTEXT_COURSE
, $course->id
);
446 context_helper
::delete_instance(CONTEXT_MODULE
, $page->cmid
);
450 * Tests covered method.
451 * @covers ::get_level_name
453 public function test_get_level_name() {
454 $allevels = context_helper
::get_all_levels();
455 foreach ($allevels as $level => $classname) {
456 $name = context_helper
::get_level_name($level);
457 $this->assertIsString($name);
460 $this->assertSame('System', context_helper
::get_level_name(CONTEXT_SYSTEM
));
461 $this->assertSame('User', context_helper
::get_level_name(CONTEXT_USER
));
462 $this->assertSame('Category', context_helper
::get_level_name(CONTEXT_COURSECAT
));
463 $this->assertSame('Course', context_helper
::get_level_name(CONTEXT_COURSE
));
464 $this->assertSame('Activity module', context_helper
::get_level_name(CONTEXT_MODULE
));
465 $this->assertSame('Block', context_helper
::get_level_name(CONTEXT_BLOCK
));
469 * Tests covered method.
470 * @covers ::get_navigation_filter_context
472 public function test_get_navigation_filter_context() {
474 $this->resetAfterTest();
476 $user = $this->getDataGenerator()->create_user();
477 $usercontext = context\user
::instance($user->id
);
478 $category = $this->getDataGenerator()->create_category();
479 $categorycontext = context\coursecat
::instance($category->id
);
480 $course = $this->getDataGenerator()->create_course();
481 $coursecontext = context\course
::instance($course->id
);
482 $page = $this->getDataGenerator()->create_module('page', array('course' => $course->id
));
483 $pagecontext = context\module
::instance($page->cmid
);
484 $systemcontext = context\system
::instance();
487 $this->assertSame('0', $CFG->filternavigationwithsystemcontext
);
489 // First test passed values are returned if disabled.
490 set_config('filternavigationwithsystemcontext', '0');
492 $this->assertNull(context_helper
::get_navigation_filter_context(null));
494 $filtercontext = context_helper
::get_navigation_filter_context($systemcontext);
495 $this->assertSame($systemcontext, $filtercontext);
497 $filtercontext = context_helper
::get_navigation_filter_context($usercontext);
498 $this->assertSame($usercontext, $filtercontext);
500 $filtercontext = context_helper
::get_navigation_filter_context($categorycontext);
501 $this->assertSame($categorycontext, $filtercontext);
503 $filtercontext = context_helper
::get_navigation_filter_context($coursecontext);
504 $this->assertSame($coursecontext, $filtercontext);
506 $filtercontext = context_helper
::get_navigation_filter_context($pagecontext);
507 $this->assertSame($pagecontext, $filtercontext);
509 // Now test that any input returns system context if enabled.
510 set_config('filternavigationwithsystemcontext', '1');
512 $filtercontext = context_helper
::get_navigation_filter_context(null);
513 $this->assertSame($systemcontext->id
, $filtercontext->id
);
515 $filtercontext = context_helper
::get_navigation_filter_context($systemcontext);
516 $this->assertSame($systemcontext->id
, $filtercontext->id
);
518 $filtercontext = context_helper
::get_navigation_filter_context($usercontext);
519 $this->assertSame($systemcontext->id
, $filtercontext->id
);
521 $filtercontext = context_helper
::get_navigation_filter_context($categorycontext);
522 $this->assertSame($systemcontext->id
, $filtercontext->id
);
524 $filtercontext = context_helper
::get_navigation_filter_context($coursecontext);
525 $this->assertSame($systemcontext->id
, $filtercontext->id
);
527 $filtercontext = context_helper
::get_navigation_filter_context($pagecontext);
528 $this->assertSame($systemcontext->id
, $filtercontext->id
);