MDL-67673 phpunit: Remove deprecated assertContains() uses on strings
[moodle.git] / mod / quiz / accessrule / seb / tests / rule_test.php
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/>.
17 /**
18  * PHPUnit tests for plugin rule class.
19  *
20  * @package    quizaccess_seb
21  * @author     Andrew Madden <andrewmadden@catalyst-au.net>
22  * @copyright  2019 Catalyst IT
23  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
24  */
26 use quizaccess_seb\quiz_settings;
27 use quizaccess_seb\settings_provider;
29 defined('MOODLE_INTERNAL') || die();
31 require_once(__DIR__ . '/test_helper_trait.php');
33 /**
34  * PHPUnit tests for plugin rule class.
35  *
36  * @copyright  2020 Catalyst IT
37  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
38  */
39 class quizaccess_seb_rule__testcase extends advanced_testcase {
40     use quizaccess_seb_test_helper_trait;
42     /**
43      * Called before every test.
44      */
45     public function setUp(): void {
46         parent::setUp();
48         $this->resetAfterTest();
49         $this->course = $this->getDataGenerator()->create_course();
50     }
53     /**
54      * Helper method to get SEB download link for testing.
55      *
56      * @return string
57      */
58     private function get_seb_download_link() {
59         return 'https://safeexambrowser.org/download_en.html';
60     }
62     /**
63      * Helper method to get SEB launch link for testing.
64      *
65      * @return string
66      */
67     private function get_seb_launch_link() {
68         return 'sebs://www.example.com/moodle/mod/quiz/accessrule/seb/config.php';
69     }
71     /**
72      * Helper method to get SEB config download link for testing.
73      *
74      * @return string
75      */
76     private function get_seb_config_download_link() {
77         return 'https://www.example.com/moodle/mod/quiz/accessrule/seb/config.php';
78     }
80     /**
81      * Provider to return valid form field data when saving settings.
82      *
83      * @return array
84      */
85     public function valid_form_data_provider() : array {
86         return [
87             'valid seb_requiresafeexambrowser' => ['seb_requiresafeexambrowser', '0'],
88             'valid seb_linkquitseb0' => ['seb_linkquitseb', 'http://safeexambrowser.org/macosx'],
89             'valid seb_linkquitseb1' => ['seb_linkquitseb', 'safeexambrowser.org/macosx'],
90             'valid seb_linkquitseb2' => ['seb_linkquitseb', 'www.safeexambrowser.org/macosx'],
91             'valid seb_linkquitseb3' => ['seb_linkquitseb', 'any.type.of.url.looking.thing'],
92             'valid seb_linkquitseb4' => ['seb_linkquitseb', 'http://any.type.of.url.looking.thing'],
93         ];
94     }
96     /**
97      * Provider to return invalid form field data when saving settings.
98      *
99      * @return array
100      */
101     public function invalid_form_data_provider() : array {
102         return [
103             'invalid seb_requiresafeexambrowser' => ['seb_requiresafeexambrowser', 'Uh oh!'],
104             'invalid seb_linkquitseb0' => ['seb_linkquitseb', '\0'],
105             'invalid seb_linkquitseb1' => ['seb_linkquitseb', 'invalid url'],
106             'invalid seb_linkquitseb2' => ['seb_linkquitseb', 'http]://safeexambrowser.org/macosx'],
107             'invalid seb_linkquitseb3' => ['seb_linkquitseb', '0'],
108             'invalid seb_linkquitseb4' => ['seb_linkquitseb', 'seb://any.type.of.url.looking.thing'],
109         ];
110     }
112     /**
113      * Test no errors are found with valid data.
114      *
115      * @param string $setting
116      * @param string $data
117      *
118      * @dataProvider valid_form_data_provider
119      */
120     public function test_validate_settings_with_valid_data(string $setting, string $data) {
121         $this->setAdminUser();
122         $this->quiz = $this->create_test_quiz($this->course, settings_provider::USE_SEB_CONFIG_MANUALLY);
124         $form = $this->createMock('mod_quiz_mod_form');
125         $form->method('get_context')->willReturn(context_module::instance($this->quiz->cmid));
127         // Validate settings with a dummy form.
128         $errors = quizaccess_seb::validate_settings_form_fields([], [
129             'instance' => $this->quiz->id,
130             'coursemodule' => $this->quiz->cmid,
131             $setting => $data
132         ], [], $form);
133         $this->assertEmpty($errors);
134     }
136     /**
137      * Test errors are found with invalid data.
138      *
139      * @param string $setting
140      * @param string $data
141      *
142      * @dataProvider invalid_form_data_provider
143      */
144     public function test_validate_settings_with_invalid_data(string $setting, string $data) {
145         $this->setAdminUser();
147         $this->quiz = $this->create_test_quiz($this->course, settings_provider::USE_SEB_CONFIG_MANUALLY);
148         $form = $this->createMock('mod_quiz_mod_form');
149         $form->method('get_context')->willReturn(context_module::instance($this->quiz->cmid));
151         // Validate settings with a dummy form and quiz instance.
152         $errors = quizaccess_seb::validate_settings_form_fields([], [
153             'instance' => $this->quiz->id,
154             'coursemodule' => $this->quiz->cmid,
155             $setting => $data
156         ], [], $form);
157         $this->assertEquals([$setting => 'Data submitted is invalid'], $errors);
158     }
160     /**
161      * Test settings validation is not run if settings are locked.
162      */
163     public function test_settings_validation_is_not_run_if_settings_are_locked() {
164         $user = $this->getDataGenerator()->create_user();
165         $this->quiz = $this->create_test_quiz($this->course);
166         $this->attempt_quiz($this->quiz, $user);
168         $this->setAdminUser();
170         $form = $this->createMock('mod_quiz_mod_form');
171         $form->method('get_context')->willReturn(context_module::instance($this->quiz->cmid));
173         // Validate settings with a dummy form and quiz instance.
174         $errors = quizaccess_seb::validate_settings_form_fields([], [
175             'instance' => $this->quiz->id,
176             'coursemodule' => $this->quiz->cmid, 'seb_requiresafeexambrowser' => 'Uh oh!'
177         ], [], $form);
178         $this->assertEmpty($errors);
179     }
181     /**
182      * Test settings validation is not run if settings are conflicting.
183      */
184     public function test_settings_validation_is_not_run_if_conflicting_permissions() {
185         $this->setAdminUser();
186         $this->quiz = $this->create_test_quiz($this->course, settings_provider::USE_SEB_CONFIG_MANUALLY);
188         $form = $this->createMock('mod_quiz_mod_form');
189         $form->method('get_context')->willReturn(context_module::instance($this->quiz->cmid));
191         $user = $this->getDataGenerator()->create_user();
192         $roleid = $this->getDataGenerator()->create_role();
193         $context = context_module::instance($this->quiz->cmid);
194         assign_capability('quizaccess/seb:manage_seb_requiresafeexambrowser', CAP_ALLOW, $roleid, $context->id);
195         $this->getDataGenerator()->role_assign($roleid, $user->id, $context->id);
197         // By default The user won't have permissions to configure manually.
198         $this->setUser($user);
200         // Validate settings with a dummy form and quiz instance.
201         $errors = quizaccess_seb::validate_settings_form_fields([], [
202             'instance' => $this->quiz->id,
203             'coursemodule' => $this->quiz->cmid,
204             'seb_requiresafeexambrowser' => 'Uh oh!'
205         ], [], $form);
206         $this->assertEmpty($errors);
207     }
209     /**
210      * Test bypassing validation if user don't have permissions to manage seb settings.
211      */
212     public function test_validate_settings_is_not_run_if_a_user_do_not_have_permissions_to_manage_seb_settings() {
213         // Set the user who can't change seb settings. So validation should be bypassed.
214         $user = $this->getDataGenerator()->create_user();
215         $this->setUser($user);
217         $this->quiz = $this->create_test_quiz($this->course, settings_provider::USE_SEB_CONFIG_MANUALLY);
218         $form = $this->createMock('mod_quiz_mod_form');
219         $form->method('get_context')->willReturn(context_module::instance($this->quiz->cmid));
221         // Validate settings with a dummy form and quiz instance.
222         $errors = quizaccess_seb::validate_settings_form_fields([], [
223             'instance' => $this->quiz->id,
224             'coursemodule' => $this->quiz->cmid, 'seb_requiresafeexambrowser' => 'Uh oh!'
225         ], [], $form);
226         $this->assertEmpty($errors);
227     }
229     /**
230      * Test settings are saved to DB.
231      */
232     public function test_create_settings_with_existing_quiz() {
233         global $DB;
235         $this->setAdminUser();
237         $this->quiz = $this->create_test_quiz($this->course, settings_provider::USE_SEB_NO);
238         $this->assertFalse($DB->record_exists('quizaccess_seb_quizsettings', ['quizid' => $this->quiz->id]));
240         $this->quiz->seb_requiresafeexambrowser = settings_provider::USE_SEB_CONFIG_MANUALLY;
241         quizaccess_seb::save_settings($this->quiz);
242         $this->assertNotFalse($DB->record_exists('quizaccess_seb_quizsettings', ['quizid' => $this->quiz->id]));
243     }
245     /**
246      * Test settings are not saved to DB if settings are locked.
247      */
248     public function test_settings_are_not_saved_if_settings_are_locked() {
249         global $DB;
251         $this->setAdminUser();
252         $this->quiz = $this->create_test_quiz($this->course);
254         $user = $this->getDataGenerator()->create_user();
255         $this->setUser($user);
256         $this->attempt_quiz($this->quiz, $user);
258         $this->setAdminUser();
259         $this->quiz->seb_requiresafeexambrowser = settings_provider::USE_SEB_CONFIG_MANUALLY;
260         quizaccess_seb::save_settings($this->quiz);
261         $this->assertFalse($DB->record_exists('quizaccess_seb_quizsettings', ['quizid' => $this->quiz->id]));
262     }
264     /**
265      * Test settings are not saved to DB if conflicting permissions.
266      */
267     public function test_settings_are_not_saved_if_conflicting_permissions() {
268         $this->setAdminUser();
269         $this->quiz = $this->create_test_quiz($this->course, settings_provider::USE_SEB_CONFIG_MANUALLY);
271         $user = $this->getDataGenerator()->create_user();
272         $roleid = $this->getDataGenerator()->create_role();
273         $context = context_module::instance($this->quiz->cmid);
274         assign_capability('quizaccess/seb:manage_seb_requiresafeexambrowser', CAP_ALLOW, $roleid, $context->id);
275         $this->getDataGenerator()->role_assign($roleid, $user->id, $context->id);
277         // By default The user won't have permissions to configure manually.
278         $this->setUser($user);
280         $this->quiz->seb_requiresafeexambrowser = settings_provider::USE_SEB_NO;
281         quizaccess_seb::save_settings($this->quiz);
283         $quizsettings = quiz_settings::get_record(['quizid' => $this->quiz->id]);
284         $this->assertEquals(settings_provider::USE_SEB_CONFIG_MANUALLY, $quizsettings->get('requiresafeexambrowser'));
285     }
287     /**
288      * Test exception thrown if cm could not be found while saving settings.
289      */
290     public function test_save_settings_throw_an_exception_if_cm_not_found() {
291         global $DB;
293         $this->expectException(dml_missing_record_exception::class);
294         $this->expectExceptionMessage('Can\'t find data record in database.');
296         $this->setAdminUser();
298         $this->quiz = $this->create_test_quiz($this->course, settings_provider::USE_SEB_CONFIG_MANUALLY);
299         $DB->delete_records('quiz', ['id' => $this->quiz->id]);
300         $this->quiz->seb_requiresafeexambrowser = settings_provider::USE_SEB_NO;
301         quizaccess_seb::save_settings($this->quiz);
302     }
304     /**
305      * Test nothing happens when deleted is called without settings saved.
306      */
307     public function test_delete_settings_without_existing_settings() {
308         global $DB;
309         $this->setAdminUser();
311         $quiz = new stdClass();
312         $quiz->id = 1;
313         quizaccess_seb::delete_settings($quiz);
314         $this->assertFalse($DB->record_exists('quizaccess_seb_quizsettings', ['quizid' => $quiz->id]));
315     }
317     /**
318      * Test settings are deleted from DB.
319      */
320     public function test_delete_settings_with_existing_settings() {
321         global $DB;
322         $this->setAdminUser();
324         $this->quiz = $this->create_test_quiz($this->course, settings_provider::USE_SEB_CONFIG_MANUALLY);
326         // Using a generator will create the quiz_settings record.
327         $this->assertNotFalse($DB->record_exists('quizaccess_seb_quizsettings', ['quizid' => $this->quiz->id]));
328         quizaccess_seb::delete_settings($this->quiz);
329         $this->assertFalse($DB->record_exists('quizaccess_seb_quizsettings', ['quizid' => $this->quiz->id]));
330     }
332     /**
333      * A helper method to check invalid config key.
334      */
335     protected function check_invalid_config_key() {
336         // Create an event sink, trigger event and retrieve event.
337         $sink = $this->redirectEvents();
339         // Check that correct error message is returned.
340         $errormsg = $this->make_rule()->prevent_access();
341         $this->assertNotEmpty($errormsg);
342         $this->assertStringContainsString("The config key or browser exam keys could not be validated. "
343             . "Please ensure you are using the Safe Exam Browser with correct configuration file.", $errormsg);
344         $this->assertStringContainsString($this->get_seb_download_link(), $errormsg);
345         $this->assertStringContainsString($this->get_seb_launch_link(), $errormsg);
346         $this->assertStringContainsString($this->get_seb_config_download_link(), $errormsg);
348         $events = $sink->get_events();
349         $this->assertEquals(1, count($events));
350         $event = reset($events);
352         // Test that the event data is as expected.
353         $this->assertInstanceOf('\quizaccess_seb\event\access_prevented', $event);
354         $this->assertEquals('Invalid SEB config key', $event->other['reason']);
355     }
357     /**
358      * Test access prevented if config key is invalid.
359      */
360     public function test_access_prevented_if_config_key_invalid() {
361         global $FULLME;
363         $this->setAdminUser();
364         $this->quiz = $this->create_test_quiz($this->course, settings_provider::USE_SEB_CONFIG_MANUALLY);
366         // Set up dummy request.
367         $FULLME = 'https://example.com/moodle/mod/quiz/attempt.php?attemptid=123&page=4';
368         $_SERVER['HTTP_X_SAFEEXAMBROWSER_CONFIGKEYHASH'] = 'Broken config key';
370         $user = $this->getDataGenerator()->create_user();
371         $this->setUser($user);
373         $this->check_invalid_config_key();
374     }
376     /**
377      * Test access prevented if config keys is invalid and using uploaded config.
378      */
379     public function test_access_prevented_if_config_key_invalid_uploaded_config() {
380         global $FULLME;
382         $this->setAdminUser();
383         $this->quiz = $this->create_test_quiz($this->course, settings_provider::USE_SEB_CONFIG_MANUALLY);
385         // Set quiz setting to require seb and save BEK.
386         $quizsettings = quiz_settings::get_record(['quizid' => $this->quiz->id]);
387         $quizsettings->set('requiresafeexambrowser', settings_provider::USE_SEB_UPLOAD_CONFIG);
388         $xml = file_get_contents(__DIR__ . '/fixtures/unencrypted.seb');
389         $this->create_module_test_file($xml, $this->quiz->cmid);
390         $quizsettings->save();
392         // Set up dummy request.
393         $FULLME = 'https://example.com/moodle/mod/quiz/attempt.php?attemptid=123&page=4';
394         $_SERVER['HTTP_X_SAFEEXAMBROWSER_CONFIGKEYHASH'] = 'Broken config key';
396         $user = $this->getDataGenerator()->create_user();
397         $this->setUser($user);
399         $this->check_invalid_config_key();
400     }
402     /**
403      * Test access prevented if config keys is invalid and using template.
404      */
405     public function test_access_prevented_if_config_key_invalid_uploaded_template() {
406         global $FULLME;
408         $this->setAdminUser();
409         $this->quiz = $this->create_test_quiz($this->course, settings_provider::USE_SEB_CONFIG_MANUALLY);
411         // Set quiz setting to require seb and save BEK.
412         $quizsettings = quiz_settings::get_record(['quizid' => $this->quiz->id]);
413         $quizsettings->set('requiresafeexambrowser', settings_provider::USE_SEB_TEMPLATE);
414         $quizsettings->set('templateid', $this->create_template()->get('id'));
415         $quizsettings->save();
417         // Set up dummy request.
418         $FULLME = 'https://example.com/moodle/mod/quiz/attempt.php?attemptid=123&page=4';
419         $_SERVER['HTTP_X_SAFEEXAMBROWSER_CONFIGKEYHASH'] = 'Broken config key';
421         $user = $this->getDataGenerator()->create_user();
422         $this->setUser($user);
424         $this->check_invalid_config_key();
425     }
427     /**
428      * Test access not prevented if config key matches header.
429      */
430     public function test_access_allowed_if_config_key_valid() {
431         global $FULLME;
433         $this->setAdminUser();
434         $this->quiz = $this->create_test_quiz($this->course, settings_provider::USE_SEB_CONFIG_MANUALLY);
436         $user = $this->getDataGenerator()->create_user();
437         $this->setUser($user);
439         // Set quiz setting to require seb.
440         $quizsettings = quiz_settings::get_record(['quizid' => $this->quiz->id]);
442         // Set up dummy request.
443         $FULLME = 'https://example.com/moodle/mod/quiz/attempt.php?attemptid=123&page=4';
444         $expectedhash = hash('sha256', $FULLME . $quizsettings->get_config_key());
445         $_SERVER['HTTP_X_SAFEEXAMBROWSER_CONFIGKEYHASH'] = $expectedhash;
447         // Check that correct error message is returned.
448         $this->assertFalse($this->make_rule()->prevent_access());
449     }
451     /**
452      * Test access not prevented if config key matches header.
453      */
454     public function test_access_allowed_if_config_key_valid_uploaded_config() {
455         global $FULLME;
457         $this->setAdminUser();
458         $this->quiz = $this->create_test_quiz($this->course, settings_provider::USE_SEB_CONFIG_MANUALLY);
460         // Set quiz setting to require seb and save BEK.
461         $quizsettings = quiz_settings::get_record(['quizid' => $this->quiz->id]);
462         $quizsettings->set('requiresafeexambrowser', settings_provider::USE_SEB_TEMPLATE);
463         $quizsettings->set('templateid', $this->create_template()->get('id'));
464         $quizsettings->save();
466         $user = $this->getDataGenerator()->create_user();
467         $this->setUser($user);
469         // Set up dummy request.
470         $FULLME = 'https://example.com/moodle/mod/quiz/attempt.php?attemptid=123&page=4';
471         $expectedhash = hash('sha256', $FULLME . $quizsettings->get_config_key());
472         $_SERVER['HTTP_X_SAFEEXAMBROWSER_CONFIGKEYHASH'] = $expectedhash;
474         // Check that correct error message is returned.
475         $this->assertFalse($this->make_rule()->prevent_access());
476     }
478     /**
479      * Test access not prevented if config key matches header.
480      */
481     public function test_access_allowed_if_config_key_valid_template() {
482         global $FULLME;
484         $this->setAdminUser();
485         $this->quiz = $this->create_test_quiz($this->course, settings_provider::USE_SEB_CONFIG_MANUALLY);
487         // Set quiz setting to require seb and save BEK.
488         $quizsettings = quiz_settings::get_record(['quizid' => $this->quiz->id]);
489         $quizsettings->set('requiresafeexambrowser', settings_provider::USE_SEB_UPLOAD_CONFIG);
490         $xml = file_get_contents(__DIR__ . '/fixtures/unencrypted.seb');
491         $this->create_module_test_file($xml, $this->quiz->cmid);
492         $quizsettings->save();
494         $user = $this->getDataGenerator()->create_user();
495         $this->setUser($user);
497         // Set up dummy request.
498         $FULLME = 'https://example.com/moodle/mod/quiz/attempt.php?attemptid=123&page=4';
499         $expectedhash = hash('sha256', $FULLME . $quizsettings->get_config_key());
500         $_SERVER['HTTP_X_SAFEEXAMBROWSER_CONFIGKEYHASH'] = $expectedhash;
502         // Check that correct error message is returned.
503         $this->assertFalse($this->make_rule()->prevent_access());
504     }
506     /**
507      * Test access not prevented if browser exam keys match headers.
508      */
509     public function test_access_allowed_if_browser_exam_keys_valid() {
510         global $FULLME;
512         $this->setAdminUser();
513         $this->quiz = $this->create_test_quiz($this->course, settings_provider::USE_SEB_CONFIG_MANUALLY);
515         $user = $this->getDataGenerator()->create_user();
516         $this->setUser($user);
518         // Set quiz setting to require seb and save BEK.
519         $browserexamkey = hash('sha256', 'testkey');
520         $quizsettings = quiz_settings::get_record(['quizid' => $this->quiz->id]);
521         $quizsettings->set('requiresafeexambrowser', settings_provider::USE_SEB_CLIENT_CONFIG); // Doesn't check config key.
522         $quizsettings->set('allowedbrowserexamkeys', $browserexamkey);
523         $quizsettings->save();
525         // Set up dummy request.
526         $FULLME = 'https://example.com/moodle/mod/quiz/attempt.php?attemptid=123&page=4';
527         $expectedhash = hash('sha256', $FULLME . $browserexamkey);
528         $_SERVER['HTTP_X_SAFEEXAMBROWSER_REQUESTHASH'] = $expectedhash;
529         $_SERVER['HTTP_USER_AGENT'] = 'SEB';
531         // Check that correct error message is returned.
532         $this->assertFalse($this->make_rule()->prevent_access());
533     }
535     /**
536      * Test access not prevented if browser exam keys match headers.
537      */
538     public function test_access_allowed_if_browser_exam_keys_valid_use_uploaded_file() {
539         global $FULLME;
541         $this->setAdminUser();
542         $this->quiz = $this->create_test_quiz($this->course, settings_provider::USE_SEB_CONFIG_MANUALLY);
544         // Set quiz setting to require seb and save BEK.
545         $browserexamkey = hash('sha256', 'testkey');
546         $quizsettings = quiz_settings::get_record(['quizid' => $this->quiz->id]);
547         $quizsettings->set('requiresafeexambrowser', settings_provider::USE_SEB_UPLOAD_CONFIG);
548         $quizsettings->set('allowedbrowserexamkeys', $browserexamkey);
549         $xml = file_get_contents(__DIR__ . '/fixtures/unencrypted.seb');
550         $this->create_module_test_file($xml, $this->quiz->cmid);
551         $quizsettings->save();
553         // Set up dummy request.
554         $FULLME = 'https://example.com/moodle/mod/quiz/attempt.php?attemptid=123&page=4';
555         $expectedbrowserkey = hash('sha256', $FULLME . $browserexamkey);
556         $_SERVER['HTTP_X_SAFEEXAMBROWSER_REQUESTHASH'] = $expectedbrowserkey;
557         $expectedconfigkey = hash('sha256', $FULLME . $quizsettings->get_config_key());
558         $_SERVER['HTTP_X_SAFEEXAMBROWSER_CONFIGKEYHASH'] = $expectedconfigkey;
560         $user = $this->getDataGenerator()->create_user();
561         $this->setUser($user);
563         // Check that correct error message is returned.
564         $this->assertFalse($this->make_rule()->prevent_access());
565     }
567     /**
568      * A helper method to check invalid browser key.
569      *
570      * @param bool $downloadseblink Make sure download SEB link is present.
571      * @param bool $launchlink Make sure launch SEB link is present.
572      * @param bool $downloadconfiglink Make download config link is present.
573      */
574     protected function check_invalid_browser_exam_key($downloadseblink = true, $launchlink = true, $downloadconfiglink = true) {
575         // Create an event sink, trigger event and retrieve event.
576         $sink = $this->redirectEvents();
578         // Check that correct error message is returned.
579         $errormsg = $this->make_rule()->prevent_access();
580         $this->assertNotEmpty($errormsg);
581         $this->assertStringContainsString("The config key or browser exam keys could not be validated. "
582             . "Please ensure you are using the Safe Exam Browser with correct configuration file.", $errormsg);
584         if ($downloadseblink) {
585             $this->assertStringContainsString($this->get_seb_download_link(), $errormsg);
586         } else {
587             $this->assertStringNotContainsString($this->get_seb_download_link(), $errormsg);
588         }
590         if ($launchlink) {
591             $this->assertStringContainsString($this->get_seb_launch_link(), $errormsg);
592         } else {
593             $this->assertStringNotContainsString($this->get_seb_launch_link(), $errormsg);
594         }
596         if ($downloadconfiglink) {
597             $this->assertStringContainsString($this->get_seb_config_download_link(), $errormsg);
598         } else {
599             $this->assertStringNotContainsString($this->get_seb_config_download_link(), $errormsg);
600         }
602         $events = $sink->get_events();
603         $this->assertEquals(1, count($events));
604         $event = reset($events);
606         // Test that the event data is as expected.
607         $this->assertInstanceOf('\quizaccess_seb\event\access_prevented', $event);
608         $this->assertEquals('Invalid SEB browser key', $event->other['reason']);
609     }
611     /**
612      * Test access prevented if browser exam keys do not match headers.
613      */
614     public function test_access_prevented_if_browser_exam_keys_are_invalid() {
615         $this->setAdminUser();
616         $this->quiz = $this->create_test_quiz($this->course, settings_provider::USE_SEB_CONFIG_MANUALLY);
618         $user = $this->getDataGenerator()->create_user();
619         $this->setUser($user);
621         // Set quiz setting to require seb and save BEK.
622         $browserexamkey = hash('sha256', 'testkey');
623         $quizsettings = quiz_settings::get_record(['quizid' => $this->quiz->id]);
624         $quizsettings->set('requiresafeexambrowser', settings_provider::USE_SEB_CLIENT_CONFIG); // Doesn't check config key.
625         $quizsettings->set('allowedbrowserexamkeys', $browserexamkey);
626         $quizsettings->save();
628         // Set up dummy request.
629         $_SERVER['HTTP_X_SAFEEXAMBROWSER_REQUESTHASH'] = 'Broken browser key';
630         $_SERVER['HTTP_USER_AGENT'] = 'SEB';
632         $this->check_invalid_browser_exam_key(true, false, false);
633     }
635     /**
636      * Test access prevented if browser exam keys do not match headers and using uploaded config.
637      */
638     public function test_access_prevented_if_browser_exam_keys_are_invalid_use_uploaded_file() {
639         global $FULLME;
641         $this->setAdminUser();
642         $this->quiz = $this->create_test_quiz($this->course, settings_provider::USE_SEB_CONFIG_MANUALLY);
644         // Set quiz setting to require seb and save BEK.
645         $browserexamkey = hash('sha256', 'testkey');
646         $quizsettings = quiz_settings::get_record(['quizid' => $this->quiz->id]);
647         $quizsettings->set('requiresafeexambrowser', settings_provider::USE_SEB_UPLOAD_CONFIG);
648         $quizsettings->set('allowedbrowserexamkeys', $browserexamkey);
649         $xml = file_get_contents(__DIR__ . '/fixtures/unencrypted.seb');
650         $this->create_module_test_file($xml, $this->quiz->cmid);
651         $quizsettings->save();
653         // Set up dummy request.
654         $FULLME = 'https://example.com/moodle/mod/quiz/attempt.php?attemptid=123&page=4';
655         $expectedhash = hash('sha256', $FULLME . $quizsettings->get_config_key());
656         $_SERVER['HTTP_X_SAFEEXAMBROWSER_CONFIGKEYHASH'] = $expectedhash;
658         // Set  up broken browser key.
659         $_SERVER['HTTP_X_SAFEEXAMBROWSER_REQUESTHASH'] = 'Broken browser key';
661         $user = $this->getDataGenerator()->create_user();
662         $this->setUser($user);
664         $this->check_invalid_browser_exam_key();
665     }
667     /**
668      * Test access not prevented if browser exam keys do not match headers and using template.
669      */
670     public function test_access_prevented_if_browser_exam_keys_are_invalid_use_template() {
671         global $FULLME;
673         $this->setAdminUser();
674         $this->quiz = $this->create_test_quiz($this->course, settings_provider::USE_SEB_CONFIG_MANUALLY);
676         // Set quiz setting to require seb and save BEK.
677         $browserexamkey = hash('sha256', 'testkey');
678         $quizsettings = quiz_settings::get_record(['quizid' => $this->quiz->id]);
679         $quizsettings->set('requiresafeexambrowser', settings_provider::USE_SEB_TEMPLATE);
680         $quizsettings->set('allowedbrowserexamkeys', $browserexamkey);
681         $quizsettings->set('templateid', $this->create_template()->get('id'));
682         $quizsettings->save();
684         // Set up dummy request.
685         $FULLME = 'https://example.com/moodle/mod/quiz/attempt.php?attemptid=123&page=4';
686         $expectedhash = hash('sha256', $FULLME . $quizsettings->get_config_key());
687         $_SERVER['HTTP_X_SAFEEXAMBROWSER_CONFIGKEYHASH'] = $expectedhash;
689         // Set  up broken browser key.
690         $_SERVER['HTTP_X_SAFEEXAMBROWSER_REQUESTHASH'] = 'Broken browser key';
692         $user = $this->getDataGenerator()->create_user();
693         $this->setUser($user);
695         // Check that correct error message is returned.
696         $this->assertFalse($this->make_rule()->prevent_access());
697     }
699     /**
700      * Test access allowed if using client configuration and SEB user agent header is valid.
701      */
702     public function test_access_allowed_if_using_client_config_basic_header_is_valid() {
703         $this->setAdminUser();
704         $this->quiz = $this->create_test_quiz($this->course, settings_provider::USE_SEB_CONFIG_MANUALLY);
706         $user = $this->getDataGenerator()->create_user();
707         $this->setUser($user);
709         // Set quiz setting to require seb.
710         $quizsettings = quiz_settings::get_record(['quizid' => $this->quiz->id]);
711         $quizsettings->set('requiresafeexambrowser', settings_provider::USE_SEB_CLIENT_CONFIG); // Doesn't check config key.
712         $quizsettings->save();
714         // Set up basic dummy request.
715         $_SERVER['HTTP_USER_AGENT'] = 'SEB_TEST_SITE';
717         // Check that correct error message is returned.
718         $this->assertFalse($this->make_rule()->prevent_access());
719     }
721     /**
722      * Test access prevented if using client configuration and SEB user agent header is invalid.
723      */
724     public function test_access_prevented_if_using_client_configuration_and_basic_head_is_invalid() {
725         $this->setAdminUser();
726         $this->quiz = $this->create_test_quiz($this->course, settings_provider::USE_SEB_CONFIG_MANUALLY);
728         $user = $this->getDataGenerator()->create_user();
729         $this->setUser($user);
731         // Set quiz setting to require seb.
732         $quizsettings = quiz_settings::get_record(['quizid' => $this->quiz->id]);
733         $quizsettings->set('requiresafeexambrowser', settings_provider::USE_SEB_CLIENT_CONFIG); // Doesn't check config key.
734         $quizsettings->save();
736         // Set up basic dummy request.
737         $_SERVER['HTTP_USER_AGENT'] = 'WRONG_TEST_SITE';
739         // Create an event sink, trigger event and retrieve event.
740         $sink = $this->redirectEvents();
742         // Check that correct error message is returned.
743         $this->assertStringContainsString(
744             'This quiz has been configured to use the Safe Exam Browser with client configuration.',
745             $this->make_rule()->prevent_access()
746         );
748         $events = $sink->get_events();
749         $this->assertEquals(1, count($events));
750         $event = reset($events);
752         // Test that the event data is as expected.
753         $this->assertInstanceOf('\quizaccess_seb\event\access_prevented', $event);
754         $this->assertEquals('No Safe Exam Browser is being used.', $event->other['reason']);
755     }
757     /**
758      * Test access allowed if using client configuration and SEB user agent header is invalid and use uploaded file.
759      */
760     public function test_access_allowed_if_using_client_configuration_and_basic_head_is_invalid_use_uploaded_config() {
761         global $FULLME;
763         $this->setAdminUser();
764         $this->quiz = $this->create_test_quiz($this->course, settings_provider::USE_SEB_CONFIG_MANUALLY);
766         // Set quiz setting to require seb.
767         $quizsettings = quiz_settings::get_record(['quizid' => $this->quiz->id]);
768         $quizsettings->set('requiresafeexambrowser', settings_provider::USE_SEB_UPLOAD_CONFIG); // Doesn't check basic header.
769         $xml = file_get_contents(__DIR__ . '/fixtures/unencrypted.seb');
770         $this->create_module_test_file($xml, $this->quiz->cmid);
771         $quizsettings->save();
773         // Set up dummy request.
774         $FULLME = 'https://example.com/moodle/mod/quiz/attempt.php?attemptid=123&page=4';
775         $expectedhash = hash('sha256', $FULLME . $quizsettings->get_config_key());
776         $_SERVER['HTTP_X_SAFEEXAMBROWSER_CONFIGKEYHASH'] = $expectedhash;
777         $_SERVER['HTTP_USER_AGENT'] = 'WRONG_TEST_SITE';
779         $user = $this->getDataGenerator()->create_user();
780         $this->setUser($user);
782         // Check that correct error message is returned.
783         $this->assertFalse($this->make_rule()->prevent_access());
784     }
786     /**
787      * Test access allowed if using client configuration and SEB user agent header is invalid and use template.
788      */
789     public function test_access_allowed_if_using_client_configuration_and_basic_head_is_invalid_use_template() {
790         global $FULLME;
792         $this->setAdminUser();
793         $this->quiz = $this->create_test_quiz($this->course, settings_provider::USE_SEB_CONFIG_MANUALLY);
795         // Set quiz setting to require seb.
796         $quizsettings = quiz_settings::get_record(['quizid' => $this->quiz->id]);
797         $quizsettings->set('requiresafeexambrowser', settings_provider::USE_SEB_TEMPLATE);
798         $quizsettings->set('templateid', $this->create_template()->get('id'));
799         $quizsettings->save();
801         // Set up dummy request.
802         $FULLME = 'https://example.com/moodle/mod/quiz/attempt.php?attemptid=123&page=4';
803         $expectedhash = hash('sha256', $FULLME . $quizsettings->get_config_key());
804         $_SERVER['HTTP_X_SAFEEXAMBROWSER_CONFIGKEYHASH'] = $expectedhash;
805         $_SERVER['HTTP_USER_AGENT'] = 'WRONG_TEST_SITE';
807         $user = $this->getDataGenerator()->create_user();
808         $this->setUser($user);
810         // Check that correct error message is returned.
811         $this->assertFalse($this->make_rule()->prevent_access());
812     }
814     /**
815      * Test access not prevented if SEB not required.
816      */
817     public function test_access_allowed_if_seb_not_required() {
818         $this->setAdminUser();
819         $this->quiz = $this->create_test_quiz($this->course, settings_provider::USE_SEB_CONFIG_MANUALLY);
821         $user = $this->getDataGenerator()->create_user();
822         $this->setUser($user);
824         // Set quiz setting to not require seb.
825         $quizsettings = quiz_settings::get_record(['quizid' => $this->quiz->id]);
826         $quizsettings->set('requiresafeexambrowser', settings_provider::USE_SEB_NO);
827         $quizsettings->save();
829         // The rule will not exist as the settings are not configured for SEB usage.
830         $this->assertNull($this->make_rule());
831     }
833     /**
834      * Test access not prevented if USER has bypass capability.
835      */
836     public function test_access_allowed_if_user_has_bypass_capability() {
837         $this->setAdminUser();
838         $this->quiz = $this->create_test_quiz($this->course, settings_provider::USE_SEB_CONFIG_MANUALLY);
840         $user = $this->getDataGenerator()->create_user();
841         $this->setUser($user);
843         // Set the bypass SEB check capability to $USER.
844         $this->assign_user_capability('quizaccess/seb:bypassseb', context_module::instance($this->quiz->cmid)->id);
846         // Check that correct error message is returned.
847         $this->assertFalse($this->make_rule()->prevent_access());
848     }
850     /**
851      * Test that quiz form cannot be saved if using template, but not actually pick one.
852      */
853     public function test_mod_quiz_form_cannot_be_saved_using_template_and_template_is_not_set() {
854         $this->setAdminUser();
855         $this->quiz = $this->create_test_quiz($this->course, settings_provider::USE_SEB_CONFIG_MANUALLY);
857         $form = $this->createMock('mod_quiz_mod_form');
858         $form->method('get_context')->willReturn(context_module::instance($this->quiz->cmid));
860         // Validate settings with a dummy form.
861         $errors = quizaccess_seb::validate_settings_form_fields([], [
862             'instance' => $this->quiz->id,
863             'coursemodule' => $this->quiz->cmid,
864             'seb_requiresafeexambrowser' => settings_provider::USE_SEB_TEMPLATE
865         ], [], $form);
867         $this->assertContains(get_string('invalidtemplate', 'quizaccess_seb'), $errors);
868     }
870     /**
871      * Test that quiz form cannot be saved if uploaded invalid file.
872      */
873     public function test_mod_quiz_form_cannot_be_saved_using_uploaded_file_and_file_is_not_valid() {
874         $this->setAdminUser();
875         $this->quiz = $this->create_test_quiz($this->course, settings_provider::USE_SEB_CONFIG_MANUALLY);
877         $form = $this->createMock('mod_quiz_mod_form');
878         $form->method('get_context')->willReturn(context_module::instance($this->quiz->cmid));
880         // Validate settings with a dummy form.
881         $errors = quizaccess_seb::validate_settings_form_fields([], [
882             'instance' => $this->quiz->id,
883             'coursemodule' => $this->quiz->cmid,
884             'seb_requiresafeexambrowser' => settings_provider::USE_SEB_UPLOAD_CONFIG,
885             'filemanager_sebconfigfile' => 0,
886         ], [], $form);
888         $this->assertContains(get_string('filenotpresent', 'quizaccess_seb'), $errors);
889     }
891     /**
892      * Test that quiz form cannot be saved if the global settings are set to require a password and no password is set.
893      */
894     public function test_mod_quiz_form_cannot_be_saved_if_global_settings_force_quiz_password_and_none_is_set() {
895         $this->setAdminUser();
896         // Set global settings to require quiz password but set password to be empty.
897         set_config('quizpasswordrequired', '1', 'quizaccess_seb');
898         $this->quiz = $this->create_test_quiz($this->course, settings_provider::USE_SEB_CONFIG_MANUALLY);
900         $form = $this->createMock('mod_quiz_mod_form');
901         $form->method('get_context')->willReturn(context_module::instance($this->quiz->cmid));
903         // Validate settings with a dummy form.
904         $errors = quizaccess_seb::validate_settings_form_fields([], [
905             'instance' => $this->quiz->id,
906             'coursemodule' => $this->quiz->cmid,
907             'seb_requiresafeexambrowser' => settings_provider::USE_SEB_CONFIG_MANUALLY,
908         ], [], $form);
910         $this->assertContains(get_string('passwordnotset', 'quizaccess_seb'), $errors);
911     }
913     /**
914      * Test that access to quiz is allowed if global setting is set to restrict quiz if no quiz password is set, and global quiz
915      * password is set.
916      */
917     public function test_mod_quiz_form_can_be_saved_if_global_settings_force_quiz_password_and_is_set() {
918         $this->setAdminUser();
919         // Set global settings to require quiz password but set password to be empty.
920         set_config('quizpasswordrequired', '1', 'quizaccess_seb');
922         $this->quiz = $this->create_test_quiz($this->course, settings_provider::USE_SEB_CONFIG_MANUALLY);
924         $form = $this->createMock('mod_quiz_mod_form');
925         $form->method('get_context')->willReturn(context_module::instance($this->quiz->cmid));
927         // Validate settings with a dummy form.
928         $errors = quizaccess_seb::validate_settings_form_fields([], [
929             'instance' => $this->quiz->id,
930             'coursemodule' => $this->quiz->cmid,
931             'quizpassword' => 'set',
932             'seb_requiresafeexambrowser' => settings_provider::USE_SEB_CONFIG_MANUALLY,
933         ], [], $form);
934         $this->assertNotContains(get_string('passwordnotset', 'quizaccess_seb'), $errors);
935     }
937     /**
938      * Test that quiz form can be saved if the global settings are set to require a password and no seb usage selected.
939      */
940     public function test_mod_quiz_form_can_be_saved_if_global_settings_force_quiz_password_and_none_no_seb() {
941         $this->setAdminUser();
942         // Set global settings to require quiz password but set password to be empty.
943         set_config('quizpasswordrequired', '1', 'quizaccess_seb');
944         $this->quiz = $this->create_test_quiz($this->course, settings_provider::USE_SEB_NO);
946         $form = $this->createMock('mod_quiz_mod_form');
947         $form->method('get_context')->willReturn(context_module::instance($this->quiz->cmid));
949         // Validate settings with a dummy form.
950         $errors = quizaccess_seb::validate_settings_form_fields([], [
951             'instance' => $this->quiz->id,
952             'coursemodule' => $this->quiz->cmid,
953             'seb_requiresafeexambrowser' => settings_provider::USE_SEB_NO,
954         ], [], $form);
956         $this->assertNotContains(get_string('passwordnotset', 'quizaccess_seb'), $errors);
957     }
959     /**
960      * Test get_download_seb_button, checks for empty config setting quizaccess_seb/downloadlink.
961      */
962     public function test_get_download_seb_button() {
963         $this->setAdminUser();
964         $this->quiz = $this->create_test_quiz($this->course, settings_provider::USE_SEB_CONFIG_MANUALLY);
966         $user = $this->getDataGenerator()->create_user();
967         $this->setUser($user);
969         $reflection = new \ReflectionClass('quizaccess_seb');
970         $method = $reflection->getMethod('get_download_seb_button');
971         $method->setAccessible(true);
973         // The current default contents.
974         $this->assertStringContainsString($this->get_seb_download_link(), $method->invoke($this->make_rule()));
976         set_config('downloadlink', '', 'quizaccess_seb');
978         // Will not return any button if the URL is empty.
979         $this->assertSame('', $method->invoke($this->make_rule()));
980     }
982     /**
983      * Test get_download_seb_button shows download SEB link when required,
984      */
985     public function test_get_get_action_buttons_shows_download_seb_link() {
986         $this->setAdminUser();
987         $this->quiz = $this->create_test_quiz($this->course, settings_provider::USE_SEB_CONFIG_MANUALLY);
989         $user = $this->getDataGenerator()->create_user();
990         $this->setUser($user);
992         $reflection = new \ReflectionClass('quizaccess_seb');
993         $method = $reflection->getMethod('get_action_buttons');
994         $method->setAccessible(true);
996         $this->assertStringContainsString($this->get_seb_download_link(), $method->invoke($this->make_rule()));
998         $this->quiz->seb_showsebdownloadlink = 0;
999         $this->assertStringNotContainsString($this->get_seb_download_link(), $method->invoke($this->make_rule()));
1000     }
1002     /**
1003      * Test get_download_seb_button shows SEB config related links when required.
1004      */
1005     public function test_get_get_action_buttons_shows_launch_and_download_config_links() {
1006         $this->setAdminUser();
1007         $this->quiz = $this->create_test_quiz($this->course, settings_provider::USE_SEB_CONFIG_MANUALLY);
1009         $user = $this->getDataGenerator()->create_user();
1010         $this->setUser($user);
1012         $reflection = new \ReflectionClass('quizaccess_seb');
1013         $method = $reflection->getMethod('get_action_buttons');
1014         $method->setAccessible(true);
1016         $quizsettings = quiz_settings::get_record(['quizid' => $this->quiz->id]);
1018         // Should see link when using manually.
1019         $this->assertStringContainsString($this->get_seb_launch_link(), $method->invoke($this->make_rule()));
1020         $this->assertStringContainsString($this->get_seb_config_download_link(), $method->invoke($this->make_rule()));
1022         // Should see links when using template.
1023         $quizsettings->set('requiresafeexambrowser', settings_provider::USE_SEB_TEMPLATE);
1024         $quizsettings->set('templateid', $this->create_template()->get('id'));
1025         $quizsettings->save();
1026         $this->assertStringContainsString($this->get_seb_launch_link(), $method->invoke($this->make_rule()));
1027         $this->assertStringContainsString($this->get_seb_config_download_link(), $method->invoke($this->make_rule()));
1029         // Should see links when using uploaded config.
1030         $quizsettings->set('requiresafeexambrowser', settings_provider::USE_SEB_UPLOAD_CONFIG);
1031         $xml = file_get_contents(__DIR__ . '/fixtures/unencrypted.seb');
1032         $this->create_module_test_file($xml, $this->quiz->cmid);
1033         $quizsettings->save();
1034         $this->assertStringContainsString($this->get_seb_launch_link(), $method->invoke($this->make_rule()));
1035         $this->assertStringContainsString($this->get_seb_config_download_link(), $method->invoke($this->make_rule()));
1037         // Shouldn't see links if using client config.
1038         $quizsettings->set('requiresafeexambrowser', settings_provider::USE_SEB_CLIENT_CONFIG);
1039         $quizsettings->save();
1040         $this->assertStringNotContainsString($this->get_seb_launch_link(), $method->invoke($this->make_rule()));
1041         $this->assertStringNotContainsString($this->get_seb_config_download_link(), $method->invoke($this->make_rule()));
1042     }
1044     /**
1045      * Test get_download_seb_button shows SEB config related links as configured in "showseblinks".
1046      */
1047     public function test_get_get_action_buttons_shows_launch_and_download_config_links_as_configured() {
1048         $this->setAdminUser();
1049         $this->quiz = $this->create_test_quiz($this->course, settings_provider::USE_SEB_CONFIG_MANUALLY);
1051         $user = $this->getDataGenerator()->create_user();
1052         $this->setUser($user);
1054         $reflection = new \ReflectionClass('quizaccess_seb');
1055         $method = $reflection->getMethod('get_action_buttons');
1056         $method->setAccessible(true);
1058         set_config('showseblinks', 'seb,http', 'quizaccess_seb');
1059         $this->assertStringContainsString($this->get_seb_launch_link(), $method->invoke($this->make_rule()));
1060         $this->assertStringContainsString($this->get_seb_config_download_link(), $method->invoke($this->make_rule()));
1062         set_config('showseblinks', 'http', 'quizaccess_seb');
1063         $this->assertStringNotContainsString($this->get_seb_launch_link(), $method->invoke($this->make_rule()));
1064         $this->assertStringContainsString($this->get_seb_config_download_link(), $method->invoke($this->make_rule()));
1066         set_config('showseblinks', 'seb', 'quizaccess_seb');
1067         $this->assertStringContainsString($this->get_seb_launch_link(), $method->invoke($this->make_rule()));
1068         $this->assertStringNotContainsString($this->get_seb_config_download_link(), $method->invoke($this->make_rule()));
1070         set_config('showseblinks', '', 'quizaccess_seb');
1071         $this->assertStringNotContainsString($this->get_seb_launch_link(), $method->invoke($this->make_rule()));
1072         $this->assertStringNotContainsString($this->get_seb_config_download_link(), $method->invoke($this->make_rule()));
1073     }
1075     /**
1076      * Test get_quit_button. If attempt count is greater than 0
1077      */
1078     public function test_get_quit_button() {
1079         $this->setAdminUser();
1080         $this->quiz = $this->create_test_quiz($this->course, settings_provider::USE_SEB_CLIENT_CONFIG);
1081         $this->quiz->seb_linkquitseb = "http://test.quit.link";
1083         $user = $this->getDataGenerator()->create_user();
1084         $this->attempt_quiz($this->quiz, $user);
1085         $this->setUser($user);
1087         // Set-up the button to be called.
1088         $reflection = new \ReflectionClass('quizaccess_seb');
1089         $method = $reflection->getMethod('get_quit_button');
1090         $method->setAccessible(true);
1092         $button = $method->invoke($this->make_rule());
1093         $this->assertStringContainsString("http://test.quit.link", $button);
1094     }
1096     /**
1097      * Test description, checks for a valid SEB session and attempt count .
1098      */
1099     public function test_description() {
1100         $this->setAdminUser();
1101         $this->quiz = $this->create_test_quiz($this->course, settings_provider::USE_SEB_CLIENT_CONFIG);
1103         $this->quiz->seb_linkquitseb = "http://test.quit.link";
1105         // Set up basic dummy request.
1106         $_SERVER['HTTP_USER_AGENT'] = 'SEB_TEST_SITE';
1108         $user = $this->getDataGenerator()->create_user();
1109         $this->attempt_quiz($this->quiz, $user);
1111         $description = $this->make_rule()->description();
1112         $this->assertCount(2, $description);
1113         $this->assertEquals($description[0], get_string('sebrequired', 'quizaccess_seb'));
1114         $this->assertEquals($description[1], '');
1116         // Set the user as display_quit_button() uses the global $USER.
1117         $this->setUser($user);
1118         $description = $this->make_rule()->description();
1119         $this->assertCount(2, $description);
1120         $this->assertEquals($description[0], get_string('sebrequired', 'quizaccess_seb'));
1122         // The button is contained in the description when a quiz attempt is finished.
1123         $this->assertStringContainsString("http://test.quit.link", $description[1]);
1124     }
1126     /**
1127      * Test description displays download SEB config button when required.
1128      */
1129     public function test_description_shows_download_config_link_when_required() {
1130         $this->setAdminUser();
1131         $this->quiz = $this->create_test_quiz($this->course, settings_provider::USE_SEB_CONFIG_MANUALLY);
1133         $quizsettings = quiz_settings::get_record(['quizid' => $this->quiz->id]);
1135         $user = $this->getDataGenerator()->create_user();
1136         $roleid = $this->getDataGenerator()->create_role();
1137         $context = context_module::instance($this->quiz->cmid);
1138         assign_capability('quizaccess/seb:bypassseb', CAP_ALLOW, $roleid, $context->id);
1140         $this->setUser($user);
1142         // Can see just basic description with standard perms.
1143         $description = $this->make_rule()->description();
1144         $this->assertCount(1, $description);
1145         $this->assertEquals($description[0], get_string('sebrequired', 'quizaccess_seb'));
1147         // Can see download config link as have bypass SEB permissions.
1148         $this->getDataGenerator()->role_assign($roleid, $user->id, $context->id);
1149         $description = $this->make_rule()->description();
1150         $this->assertCount(3, $description);
1151         $this->assertEquals($description[0], get_string('sebrequired', 'quizaccess_seb'));
1152         $this->assertStringContainsString($this->get_seb_config_download_link(), $description[1]);
1154         // Can't see download config link as usage method doesn't have SEB config to download.
1155         $quizsettings->set('requiresafeexambrowser', settings_provider::USE_SEB_CLIENT_CONFIG);
1156         $quizsettings->save();
1157         $description = $this->make_rule()->description();
1158         $this->assertCount(2, $description);
1159         $this->assertEquals($description[0], get_string('sebrequired', 'quizaccess_seb'));
1160     }
1162     /**
1163      * Test block display before a quiz started.
1164      */
1165     public function test_blocks_display_before_attempt_started() {
1166         global $PAGE;
1168         $this->setAdminUser();
1169         $this->quiz = $this->create_test_quiz($this->course, settings_provider::USE_SEB_CLIENT_CONFIG);
1171         $user = $this->getDataGenerator()->create_user();
1172         $this->setUser($user);
1174         // We will check if we show only fake blocks. Which means no other blocks on a page.
1175         $reflection = new \ReflectionClass('block_manager');
1176         $property = $reflection->getProperty('fakeblocksonly');
1177         $property->setAccessible(true);
1179         $this->assertFalse($property->getValue($PAGE->blocks));
1181         // Don't display blocks before start.
1182         set_config('displayblocksbeforestart', 0, 'quizaccess_seb');
1183         $this->set_up_quiz_view_page();
1184         $this->make_rule()->prevent_access();
1185         $this->assertEquals('secure', $PAGE->pagelayout);
1186         $this->assertTrue($property->getValue($PAGE->blocks));
1188         // Display blocks before start.
1189         set_config('displayblocksbeforestart', 1, 'quizaccess_seb');
1190         $this->set_up_quiz_view_page();
1191         $this->make_rule()->prevent_access();
1192         $this->assertEquals('secure', $PAGE->pagelayout);
1193         $this->assertFalse($property->getValue($PAGE->blocks));
1194     }
1196     /**
1197      * Test block display after a quiz completed.
1198      */
1199     public function test_blocks_display_after_attempt_finished() {
1200         global $PAGE;
1202         $this->setAdminUser();
1203         $this->quiz = $this->create_test_quiz($this->course, settings_provider::USE_SEB_CLIENT_CONFIG);
1205         // Finish the quiz.
1206         $user = $this->getDataGenerator()->create_user();
1207         $this->attempt_quiz($this->quiz, $user);
1208         $this->setUser($user);
1210         // We will check if we show only fake blocks. Which means no other blocks on a page.
1211         $reflection = new \ReflectionClass('block_manager');
1212         $property = $reflection->getProperty('fakeblocksonly');
1213         $property->setAccessible(true);
1215         $this->assertFalse($property->getValue($PAGE->blocks));
1217         // Don't display blocks after finish.
1218         set_config('displayblockswhenfinished', 0, 'quizaccess_seb');
1219         $this->set_up_quiz_view_page();
1220         $this->make_rule()->prevent_access();
1221         $this->assertEquals('secure', $PAGE->pagelayout);
1222         $this->assertTrue($property->getValue($PAGE->blocks));
1224         // Display blocks after finish.
1225         set_config('displayblockswhenfinished', 1, 'quizaccess_seb');
1226         $this->set_up_quiz_view_page();
1227         $this->make_rule()->prevent_access();
1228         $this->assertEquals('secure', $PAGE->pagelayout);
1229         $this->assertFalse($property->getValue($PAGE->blocks));
1230     }
1232     /**
1233      * Test we can decide if need to redirect to SEB config link.
1234      */
1235     public function test_should_redirect_to_seb_config_link() {
1236         $this->setAdminUser();
1237         $this->quiz = $this->create_test_quiz($this->course, settings_provider::USE_SEB_CONFIG_MANUALLY);
1239         $reflection = new \ReflectionClass('quizaccess_seb');
1240         $method = $reflection->getMethod('should_redirect_to_seb_config_link');
1241         $method->setAccessible(true);
1243         set_config('autoreconfigureseb', '0', 'quizaccess_seb');
1244         $_SERVER['HTTP_USER_AGENT'] = 'TEST';
1245         $this->assertFalse($method->invoke($this->make_rule()));
1247         set_config('autoreconfigureseb', '0', 'quizaccess_seb');
1248         $_SERVER['HTTP_USER_AGENT'] = 'SEB';
1249         $this->assertFalse($method->invoke($this->make_rule()));
1251         set_config('autoreconfigureseb', '1', 'quizaccess_seb');
1252         $_SERVER['HTTP_USER_AGENT'] = 'TEST';
1253         $this->assertFalse($method->invoke($this->make_rule()));
1255         set_config('autoreconfigureseb', '1', 'quizaccess_seb');
1256         $_SERVER['HTTP_USER_AGENT'] = 'SEB';
1257         $this->assertTrue($method->invoke($this->make_rule()));
1258     }