Commit | Line | Data |
---|---|---|
77043fd6 | 1 | <?php |
77043fd6 DM |
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 | * Unit tests for those parts of adminlib.php that implement the admin tree | |
19 | * functionality. | |
20 | * | |
21 | * @package core | |
05fb92e9 | 22 | * @category phpunit |
77043fd6 DM |
23 | * @copyright 2013 David Mudrak <david@moodle.com> |
24 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later | |
25 | */ | |
26 | ||
27 | defined('MOODLE_INTERNAL') || die(); | |
28 | ||
29 | global $CFG; | |
30 | require_once($CFG->libdir.'/adminlib.php'); | |
31 | ||
32 | /** | |
33 | * Provides the unit tests for admin tree functionality. | |
34 | */ | |
05fb92e9 | 35 | class core_admintree_testcase extends advanced_testcase { |
77043fd6 DM |
36 | |
37 | /** | |
05fb92e9 | 38 | * Adding nodes into the admin tree. |
77043fd6 DM |
39 | */ |
40 | public function test_add_nodes() { | |
41 | ||
42 | $tree = new admin_root(true); | |
43 | $tree->add('root', $one = new admin_category('one', 'One')); | |
44 | $tree->add('root', new admin_category('three', 'Three')); | |
45 | $tree->add('one', new admin_category('one-one', 'One-one')); | |
46 | $tree->add('one', new admin_category('one-three', 'One-three')); | |
47 | ||
48 | // Check the order of nodes in the root. | |
49 | $map = array(); | |
50 | foreach ($tree->children as $child) { | |
51 | $map[] = $child->name; | |
52 | } | |
53 | $this->assertEquals(array('one', 'three'), $map); | |
54 | ||
55 | // Insert a node into the middle. | |
56 | $tree->add('root', new admin_category('two', 'Two'), 'three'); | |
57 | $map = array(); | |
58 | foreach ($tree->children as $child) { | |
59 | $map[] = $child->name; | |
60 | } | |
61 | $this->assertEquals(array('one', 'two', 'three'), $map); | |
62 | ||
63 | // Non-existing sibling. | |
64 | $tree->add('root', new admin_category('four', 'Four'), 'five'); | |
65 | $this->assertDebuggingCalled('Sibling five not found', DEBUG_DEVELOPER); | |
66 | ||
67 | $tree->add('root', new admin_category('five', 'Five')); | |
68 | $map = array(); | |
69 | foreach ($tree->children as $child) { | |
70 | $map[] = $child->name; | |
71 | } | |
72 | $this->assertEquals(array('one', 'two', 'three', 'four', 'five'), $map); | |
73 | ||
05fb92e9 | 74 | // Insert a node into the middle of the subcategory. |
77043fd6 DM |
75 | $tree->add('one', new admin_category('one-two', 'One-two'), 'one-three'); |
76 | $map = array(); | |
77 | foreach ($one->children as $child) { | |
78 | $map[] = $child->name; | |
79 | } | |
80 | $this->assertEquals(array('one-one', 'one-two', 'one-three'), $map); | |
81 | ||
82 | // Check just siblings, not parents or children. | |
83 | $tree->add('one', new admin_category('one-four', 'One-four'), 'one'); | |
84 | $this->assertDebuggingCalled('Sibling one not found', DEBUG_DEVELOPER); | |
85 | ||
86 | $tree->add('root', new admin_category('six', 'Six'), 'one-two'); | |
87 | $this->assertDebuggingCalled('Sibling one-two not found', DEBUG_DEVELOPER); | |
88 | ||
89 | // Me! Me! I wanna be first! | |
90 | $tree->add('root', new admin_externalpage('zero', 'Zero', 'http://foo.bar'), 'one'); | |
91 | $map = array(); | |
92 | foreach ($tree->children as $child) { | |
93 | $map[] = $child->name; | |
94 | } | |
95 | $this->assertEquals(array('zero', 'one', 'two', 'three', 'four', 'five', 'six'), $map); | |
96 | } | |
97 | ||
98 | /** | |
99 | * @expectedException coding_exception | |
100 | */ | |
101 | public function test_add_nodes_before_invalid1() { | |
102 | $tree = new admin_root(true); | |
103 | $tree->add('root', new admin_externalpage('foo', 'Foo', 'http://foo.bar'), array('moodle:site/config')); | |
104 | } | |
105 | ||
106 | /** | |
107 | * @expectedException coding_exception | |
108 | */ | |
109 | public function test_add_nodes_before_invalid2() { | |
110 | $tree = new admin_root(true); | |
111 | $tree->add('root', new admin_category('bar', 'Bar'), ''); | |
112 | } | |
914499a3 | 113 | |
f28c0c72 DW |
114 | /** |
115 | * Test that changes to config trigger events. | |
116 | */ | |
117 | public function test_config_log_created_event() { | |
118 | global $DB; | |
119 | $this->resetAfterTest(); | |
120 | $this->setAdminUser(); | |
121 | ||
122 | $adminroot = new admin_root(true); | |
123 | $adminroot->add('root', $one = new admin_category('one', 'One')); | |
124 | $page = new admin_settingpage('page', 'Page'); | |
125 | $page->add(new admin_setting_configtext('text1', 'Text 1', '', '')); | |
126 | $page->add(new admin_setting_configpasswordunmask('pass1', 'Password 1', '', '')); | |
127 | $adminroot->add('one', $page); | |
128 | ||
129 | $sink = $this->redirectEvents(); | |
130 | $data = array('s__text1' => 'sometext', 's__pass1' => ''); | |
131 | $this->save_config_data($adminroot, $data); | |
132 | ||
133 | $events = $sink->get_events(); | |
134 | $sink->close(); | |
135 | $event = array_pop($events); | |
136 | $this->assertInstanceOf('\core\event\config_log_created', $event); | |
137 | ||
138 | $sink = $this->redirectEvents(); | |
139 | $data = array('s__text1'=>'other', 's__pass1'=>'nice password'); | |
140 | $count = $this->save_config_data($adminroot, $data); | |
141 | ||
142 | $events = $sink->get_events(); | |
143 | $sink->close(); | |
144 | $event = array_pop($events); | |
145 | $this->assertInstanceOf('\core\event\config_log_created', $event); | |
146 | // Verify password was nuked. | |
147 | $this->assertNotEquals($event->other['value'], 'nice password'); | |
148 | ||
149 | } | |
150 | ||
9cd7bb37 AN |
151 | /** |
152 | * Testing whether a configexecutable setting is executable. | |
153 | */ | |
154 | public function test_admin_setting_configexecutable() { | |
155 | global $CFG; | |
156 | $this->resetAfterTest(); | |
157 | ||
e00f1c66 | 158 | $CFG->theme = 'classic'; |
9cd7bb37 AN |
159 | $executable = new admin_setting_configexecutable('test1', 'Text 1', 'Help Path', ''); |
160 | ||
161 | // Check for an invalid path. | |
162 | $result = $executable->output_html($CFG->dirroot . '/lib/tests/other/file_does_not_exist'); | |
e00f1c66 | 163 | $this->assertRegexp('/class="text-danger"/', $result); |
9cd7bb37 AN |
164 | |
165 | // Check for a directory. | |
166 | $result = $executable->output_html($CFG->dirroot); | |
e00f1c66 | 167 | $this->assertRegexp('/class="text-danger"/', $result); |
9cd7bb37 AN |
168 | |
169 | // Check for a file which is not executable. | |
f500ff4e | 170 | $result = $executable->output_html($CFG->dirroot . '/filter/tex/readme_moodle.txt'); |
e00f1c66 | 171 | $this->assertRegexp('/class="text-danger"/', $result); |
9cd7bb37 AN |
172 | |
173 | // Check for an executable file. | |
f500ff4e EL |
174 | if ($CFG->ostype == 'WINDOWS') { |
175 | $filetocheck = 'mimetex.exe'; | |
176 | } else { | |
177 | $filetocheck = 'mimetex.darwin'; | |
178 | } | |
179 | $result = $executable->output_html($CFG->dirroot . '/filter/tex/' . $filetocheck); | |
e00f1c66 | 180 | $this->assertRegexp('/class="text-success"/', $result); |
9cd7bb37 AN |
181 | |
182 | // Check for no file specified. | |
183 | $result = $executable->output_html(''); | |
890c2603 DW |
184 | $this->assertRegexp('/name="s__test1"/', $result); |
185 | $this->assertRegexp('/value=""/', $result); | |
9cd7bb37 AN |
186 | } |
187 | ||
914499a3 PS |
188 | /** |
189 | * Saving of values. | |
190 | */ | |
191 | public function test_config_logging() { | |
192 | global $DB; | |
193 | $this->resetAfterTest(); | |
6e861be6 | 194 | $this->setAdminUser(); |
914499a3 PS |
195 | |
196 | $DB->delete_records('config_log', array()); | |
197 | ||
198 | $adminroot = new admin_root(true); | |
199 | $adminroot->add('root', $one = new admin_category('one', 'One')); | |
200 | $page = new admin_settingpage('page', 'Page'); | |
201 | $page->add(new admin_setting_configtext('text1', 'Text 1', '', '')); | |
202 | $page->add(new admin_setting_configpasswordunmask('pass1', 'Password 1', '', '')); | |
203 | $adminroot->add('one', $page); | |
204 | ||
205 | $this->assertEmpty($DB->get_records('config_log')); | |
206 | $data = array('s__text1'=>'sometext', 's__pass1'=>''); | |
207 | $count = $this->save_config_data($adminroot, $data); | |
208 | ||
209 | $this->assertEquals(2, $count); | |
210 | $records = $DB->get_records('config_log', array(), 'id asc'); | |
211 | $this->assertCount(2, $records); | |
212 | reset($records); | |
213 | $record = array_shift($records); | |
214 | $this->assertNull($record->plugin); | |
215 | $this->assertSame('text1', $record->name); | |
216 | $this->assertNull($record->oldvalue); | |
217 | $this->assertSame('sometext', $record->value); | |
218 | $record = array_shift($records); | |
219 | $this->assertNull($record->plugin); | |
220 | $this->assertSame('pass1', $record->name); | |
221 | $this->assertNull($record->oldvalue); | |
222 | $this->assertSame('', $record->value); | |
223 | ||
224 | $DB->delete_records('config_log', array()); | |
225 | $data = array('s__text1'=>'other', 's__pass1'=>'nice password'); | |
226 | $count = $this->save_config_data($adminroot, $data); | |
227 | ||
228 | $this->assertEquals(2, $count); | |
229 | $records = $DB->get_records('config_log', array(), 'id asc'); | |
230 | $this->assertCount(2, $records); | |
231 | reset($records); | |
232 | $record = array_shift($records); | |
233 | $this->assertNull($record->plugin); | |
234 | $this->assertSame('text1', $record->name); | |
235 | $this->assertSame('sometext', $record->oldvalue); | |
236 | $this->assertSame('other', $record->value); | |
237 | $record = array_shift($records); | |
238 | $this->assertNull($record->plugin); | |
239 | $this->assertSame('pass1', $record->name); | |
240 | $this->assertSame('', $record->oldvalue); | |
241 | $this->assertSame('********', $record->value); | |
242 | ||
243 | $DB->delete_records('config_log', array()); | |
244 | $data = array('s__text1'=>'', 's__pass1'=>''); | |
245 | $count = $this->save_config_data($adminroot, $data); | |
246 | ||
247 | $this->assertEquals(2, $count); | |
248 | $records = $DB->get_records('config_log', array(), 'id asc'); | |
249 | $this->assertCount(2, $records); | |
250 | reset($records); | |
251 | $record = array_shift($records); | |
252 | $this->assertNull($record->plugin); | |
253 | $this->assertSame('text1', $record->name); | |
254 | $this->assertSame('other', $record->oldvalue); | |
255 | $this->assertSame('', $record->value); | |
256 | $record = array_shift($records); | |
257 | $this->assertNull($record->plugin); | |
258 | $this->assertSame('pass1', $record->name); | |
259 | $this->assertSame('********', $record->oldvalue); | |
260 | $this->assertSame('', $record->value); | |
261 | } | |
262 | ||
263 | protected function save_config_data(admin_root $adminroot, array $data) { | |
264 | $adminroot->errors = array(); | |
265 | ||
266 | $settings = admin_find_write_settings($adminroot, $data); | |
267 | ||
268 | $count = 0; | |
269 | foreach ($settings as $fullname=>$setting) { | |
270 | /** @var $setting admin_setting */ | |
271 | $original = $setting->get_setting(); | |
272 | $error = $setting->write_setting($data[$fullname]); | |
273 | if ($error !== '') { | |
274 | $adminroot->errors[$fullname] = new stdClass(); | |
275 | $adminroot->errors[$fullname]->data = $data[$fullname]; | |
276 | $adminroot->errors[$fullname]->id = $setting->get_id(); | |
277 | $adminroot->errors[$fullname]->error = $error; | |
278 | } else { | |
279 | $setting->write_setting_flags($data); | |
280 | } | |
281 | if ($setting->post_write_settings($original)) { | |
282 | $count++; | |
283 | } | |
284 | } | |
285 | ||
286 | return $count; | |
287 | } | |
41186f4f PS |
288 | |
289 | public function test_preventexecpath() { | |
290 | $this->resetAfterTest(); | |
291 | ||
292 | set_config('preventexecpath', 0); | |
293 | set_config('execpath', null, 'abc_cde'); | |
294 | $this->assertFalse(get_config('abc_cde', 'execpath')); | |
295 | $setting = new admin_setting_configexecutable('abc_cde/execpath', 'some desc', '', '/xx/yy'); | |
296 | $setting->write_setting('/oo/pp'); | |
297 | $this->assertSame('/oo/pp', get_config('abc_cde', 'execpath')); | |
298 | ||
299 | // Prevent changes. | |
300 | set_config('preventexecpath', 1); | |
301 | $setting->write_setting('/mm/nn'); | |
302 | $this->assertSame('/oo/pp', get_config('abc_cde', 'execpath')); | |
303 | ||
304 | // Use default in install. | |
305 | set_config('execpath', null, 'abc_cde'); | |
306 | $setting->write_setting('/mm/nn'); | |
307 | $this->assertSame('/xx/yy', get_config('abc_cde', 'execpath')); | |
308 | ||
309 | // Use empty value if no default. | |
310 | $setting = new admin_setting_configexecutable('abc_cde/execpath', 'some desc', '', null); | |
311 | set_config('execpath', null, 'abc_cde'); | |
312 | $setting->write_setting('/mm/nn'); | |
313 | $this->assertSame('', get_config('abc_cde', 'execpath')); | |
314 | ||
5aaac8b2 | 315 | // This also affects admin_setting_configfile and admin_setting_configdirectory. |
41186f4f PS |
316 | |
317 | set_config('preventexecpath', 0); | |
318 | set_config('execpath', null, 'abc_cde'); | |
319 | $this->assertFalse(get_config('abc_cde', 'execpath')); | |
320 | $setting = new admin_setting_configfile('abc_cde/execpath', 'some desc', '', '/xx/yy'); | |
321 | $setting->write_setting('/oo/pp'); | |
322 | $this->assertSame('/oo/pp', get_config('abc_cde', 'execpath')); | |
323 | ||
324 | // Prevent changes. | |
325 | set_config('preventexecpath', 1); | |
326 | $setting->write_setting('/mm/nn'); | |
327 | $this->assertSame('/oo/pp', get_config('abc_cde', 'execpath')); | |
328 | ||
329 | // Use default in install. | |
330 | set_config('execpath', null, 'abc_cde'); | |
331 | $setting->write_setting('/mm/nn'); | |
332 | $this->assertSame('/xx/yy', get_config('abc_cde', 'execpath')); | |
333 | ||
334 | // Use empty value if no default. | |
335 | $setting = new admin_setting_configfile('abc_cde/execpath', 'some desc', '', null); | |
336 | set_config('execpath', null, 'abc_cde'); | |
337 | $setting->write_setting('/mm/nn'); | |
338 | $this->assertSame('', get_config('abc_cde', 'execpath')); | |
339 | ||
5aaac8b2 PS |
340 | set_config('preventexecpath', 0); |
341 | set_config('execpath', null, 'abc_cde'); | |
342 | $this->assertFalse(get_config('abc_cde', 'execpath')); | |
343 | $setting = new admin_setting_configdirectory('abc_cde/execpath', 'some desc', '', '/xx/yy'); | |
344 | $setting->write_setting('/oo/pp'); | |
345 | $this->assertSame('/oo/pp', get_config('abc_cde', 'execpath')); | |
346 | ||
347 | // Prevent changes. | |
348 | set_config('preventexecpath', 1); | |
349 | $setting->write_setting('/mm/nn'); | |
350 | $this->assertSame('/oo/pp', get_config('abc_cde', 'execpath')); | |
351 | ||
352 | // Use default in install. | |
353 | set_config('execpath', null, 'abc_cde'); | |
354 | $setting->write_setting('/mm/nn'); | |
355 | $this->assertSame('/xx/yy', get_config('abc_cde', 'execpath')); | |
356 | ||
357 | // Use empty value if no default. | |
358 | $setting = new admin_setting_configdirectory('abc_cde/execpath', 'some desc', '', null); | |
359 | set_config('execpath', null, 'abc_cde'); | |
360 | $setting->write_setting('/mm/nn'); | |
361 | $this->assertSame('', get_config('abc_cde', 'execpath')); | |
41186f4f | 362 | } |
bd39ef24 MG |
363 | |
364 | /** | |
365 | * Test setting for blocked hosts | |
366 | * | |
367 | * For testing the admin settings element only. Test for blocked hosts functionality can be found | |
368 | * in lib/tests/curl_security_helper_test.php | |
369 | */ | |
370 | public function test_mixedhostiplist() { | |
371 | $this->resetAfterTest(); | |
372 | ||
373 | $adminsetting = new admin_setting_configmixedhostiplist('abc_cde/hostiplist', 'some desc', '', ''); | |
374 | ||
375 | // Test valid settings. | |
376 | $validsimplesettings = [ | |
377 | 'localhost', | |
378 | "localhost\n127.0.0.1", | |
379 | '192.168.10.1', | |
380 | '0:0:0:0:0:0:0:1', | |
381 | '::1', | |
382 | 'fe80::', | |
383 | '231.54.211.0/20', | |
384 | 'fe80::/64', | |
385 | '231.3.56.10-20', | |
386 | 'fe80::1111-bbbb', | |
387 | '*.example.com', | |
388 | '*.sub.example.com', | |
389 | ]; | |
390 | ||
391 | foreach ($validsimplesettings as $setting) { | |
392 | $errormessage = $adminsetting->write_setting($setting); | |
393 | $this->assertEmpty($errormessage, $errormessage); | |
394 | $this->assertSame($setting, get_config('abc_cde', 'hostiplist')); | |
395 | $this->assertSame($setting, $adminsetting->get_setting()); | |
396 | } | |
397 | ||
398 | // Test valid international site names. | |
399 | $valididnsettings = [ | |
400 | 'правительство.рф' => 'xn--80aealotwbjpid2k.xn--p1ai', | |
401 | 'faß.de' => 'xn--fa-hia.de', | |
402 | 'ß.ß' => 'xn--zca.xn--zca', | |
403 | '*.tharkûn.com' => '*.xn--tharkn-0ya.com', | |
404 | ]; | |
405 | ||
406 | foreach ($valididnsettings as $setting => $encodedsetting) { | |
407 | $errormessage = $adminsetting->write_setting($setting); | |
408 | $this->assertEmpty($errormessage, $errormessage); | |
409 | $this->assertSame($encodedsetting, get_config('abc_cde', 'hostiplist')); | |
410 | $this->assertSame($setting, $adminsetting->get_setting()); | |
411 | } | |
412 | ||
413 | // Invalid settings. | |
414 | $this->assertEquals('These entries are invalid: nonvalid site name', $adminsetting->write_setting('nonvalid site name')); | |
415 | $this->assertEquals('Empty lines are not valid', $adminsetting->write_setting("localhost\n")); | |
416 | } | |
dbf66238 JD |
417 | |
418 | /** | |
419 | * Verifies the $ADMIN global (adminroot cache) is properly reset when changing users, which might occur naturally during cron. | |
420 | */ | |
421 | public function test_adminroot_cache_reset() { | |
422 | $this->resetAfterTest(); | |
423 | global $DB; | |
424 | // Current user is a manager at site context, which won't have access to the 'debugging' section of the admin tree. | |
425 | $manageruser = $this->getDataGenerator()->create_user(); | |
426 | $context = context_system::instance(); | |
427 | $managerrole = $DB->get_record('role', array('shortname' => 'manager')); | |
428 | role_assign($managerrole->id, $manageruser->id, $context->id); | |
429 | $this->setUser($manageruser); | |
430 | $adminroot = admin_get_root(); | |
431 | $section = $adminroot->locate('debugging'); | |
432 | $this->assertEmpty($section); | |
433 | ||
434 | // Now, change the user to an admin user and confirm we get a new copy of the admin tree when next we ask for it. | |
435 | $adminuser = get_admin(); | |
436 | $this->setUser($adminuser); | |
437 | $adminroot = admin_get_root(); | |
438 | $section = $adminroot->locate('debugging'); | |
439 | $this->assertInstanceOf('\admin_settingpage', $section); | |
440 | } | |
77043fd6 | 441 | } |