mirror of
https://github.com/moodle/moodle.git
synced 2025-08-05 17:06:53 +02:00
Merge branch 'MDL-61407-master' of git://github.com/andrewnicols/moodle
This commit is contained in:
commit
209f6e1eda
123 changed files with 5000 additions and 33 deletions
164
mod/quiz/tests/privacy_legacy_quizaccess_polyfill_test.php
Normal file
164
mod/quiz/tests/privacy_legacy_quizaccess_polyfill_test.php
Normal file
|
@ -0,0 +1,164 @@
|
|||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Unit tests for the privacy legacy polyfill for quiz access rules.
|
||||
*
|
||||
* @package mod_quiz
|
||||
* @category test
|
||||
* @copyright 2018 Andrew Nicols <andrew@nicols.co.uk>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
global $CFG;
|
||||
require_once($CFG->dirroot . '/mod/quiz/attemptlib.php');
|
||||
|
||||
/**
|
||||
* Unit tests for the privacy legacy polyfill for quiz access rules.
|
||||
*
|
||||
* @copyright 2018 Andrew Nicols <andrew@nicols.co.uk>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class core_privacy_legacy_quizaccess_polyfill_test extends advanced_testcase {
|
||||
/**
|
||||
* Test that the core_quizaccess\privacy\legacy_polyfill works and that the static _export_quizaccess_user_data can
|
||||
* be called.
|
||||
*/
|
||||
public function test_export_quizaccess_user_data() {
|
||||
$quiz = $this->createMock(quiz::class);
|
||||
$user = (object) [];
|
||||
$returnvalue = (object) [];
|
||||
|
||||
$mock = $this->createMock(test_privacy_legacy_quizaccess_polyfill_mock_wrapper::class);
|
||||
$mock->expects($this->once())
|
||||
->method('get_return_value')
|
||||
->with('_export_quizaccess_user_data', [$quiz, $user])
|
||||
->willReturn($returnvalue);
|
||||
|
||||
test_privacy_legacy_quizaccess_polyfill_provider::$mock = $mock;
|
||||
$result = test_privacy_legacy_quizaccess_polyfill_provider::export_quizaccess_user_data($quiz, $user);
|
||||
$this->assertSame($returnvalue, $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the _delete_quizaccess_for_context shim.
|
||||
*/
|
||||
public function test_delete_quizaccess_for_context() {
|
||||
$context = context_system::instance();
|
||||
|
||||
$quiz = $this->createMock(quiz::class);
|
||||
|
||||
$mock = $this->createMock(test_privacy_legacy_quizaccess_polyfill_mock_wrapper::class);
|
||||
$mock->expects($this->once())
|
||||
->method('get_return_value')
|
||||
->with('_delete_quizaccess_data_for_all_users_in_context', [$quiz]);
|
||||
|
||||
test_privacy_legacy_quizaccess_polyfill_provider::$mock = $mock;
|
||||
test_privacy_legacy_quizaccess_polyfill_provider::delete_quizaccess_data_for_all_users_in_context($quiz);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the _delete_quizaccess_for_context shim.
|
||||
*/
|
||||
public function test_delete_quizaccess_for_user() {
|
||||
$context = context_system::instance();
|
||||
|
||||
$quiz = $this->createMock(quiz::class);
|
||||
$user = (object) [];
|
||||
|
||||
$mock = $this->createMock(test_privacy_legacy_quizaccess_polyfill_mock_wrapper::class);
|
||||
$mock->expects($this->once())
|
||||
->method('get_return_value')
|
||||
->with('_delete_quizaccess_data_for_user', [$quiz, $user]);
|
||||
|
||||
test_privacy_legacy_quizaccess_polyfill_provider::$mock = $mock;
|
||||
test_privacy_legacy_quizaccess_polyfill_provider::delete_quizaccess_data_for_user($quiz, $user);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Legacy polyfill test class for the quizaccess_provider.
|
||||
*
|
||||
* @copyright 2018 Andrew Nicols <andrew@nicols.co.uk>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class test_privacy_legacy_quizaccess_polyfill_provider implements
|
||||
\core_privacy\local\metadata\provider,
|
||||
\mod_quiz\privacy\quizaccess_provider {
|
||||
|
||||
use \mod_quiz\privacy\legacy_quizaccess_polyfill;
|
||||
use \core_privacy\local\legacy_polyfill;
|
||||
|
||||
/**
|
||||
* @var test_privacy_legacy_quizaccess_polyfill_provider $mock.
|
||||
*/
|
||||
public static $mock = null;
|
||||
|
||||
/**
|
||||
* Export all user data for the quizaccess plugin.
|
||||
*
|
||||
* @param \quiz $quiz
|
||||
* @param \stdClass $user
|
||||
*/
|
||||
protected static function _export_quizaccess_user_data($quiz, $user) {
|
||||
return static::$mock->get_return_value(__FUNCTION__, func_get_args());
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes all user data for the given context.
|
||||
*
|
||||
* @param \quiz $quiz
|
||||
*/
|
||||
protected static function _delete_quizaccess_data_for_all_users_in_context($quiz) {
|
||||
static::$mock->get_return_value(__FUNCTION__, func_get_args());
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete personal data for the given user and context.
|
||||
*
|
||||
* @param \quiz $quiz The quiz being deleted
|
||||
* @param \stdClass $user The user to export data for
|
||||
*/
|
||||
protected static function _delete_quizaccess_data_for_user($quiz, $user) {
|
||||
static::$mock->get_return_value(__FUNCTION__, func_get_args());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns metadata about this plugin.
|
||||
*
|
||||
* @param \core_privacy\local\metadata\collection $collection The initialised collection to add items to.
|
||||
* @return \core_privacy\local\metadata\collection A listing of user data stored through this system.
|
||||
*/
|
||||
protected static function _get_metadata(\core_privacy\local\metadata\collection $collection) {
|
||||
return $collection;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called inside the polyfill methods in the test polyfill provider, allowing us to ensure these are called with correct params.
|
||||
*
|
||||
* @copyright 2018 Andrew Nicols <andrew@nicols.co.uk>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class test_privacy_legacy_quizaccess_polyfill_mock_wrapper {
|
||||
/**
|
||||
* Get the return value for the specified item.
|
||||
*/
|
||||
public function get_return_value() {
|
||||
}
|
||||
}
|
279
mod/quiz/tests/privacy_provider_test.php
Normal file
279
mod/quiz/tests/privacy_provider_test.php
Normal file
|
@ -0,0 +1,279 @@
|
|||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Privacy provider tests.
|
||||
*
|
||||
* @package mod_quiz
|
||||
* @copyright 2018 Andrew Nicols <andrew@nicols.co.uk>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
use core_privacy\local\metadata\collection;
|
||||
use core_privacy\local\request\deletion_criteria;
|
||||
use core_privacy\local\request\writer;
|
||||
use mod_quiz\privacy\provider;
|
||||
use mod_quiz\privacy\helper;
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
global $CFG;
|
||||
require_once($CFG->dirroot . '/question/tests/privacy_helper.php');
|
||||
|
||||
/**
|
||||
* Privacy provider tests class.
|
||||
*
|
||||
* @package mod_quiz
|
||||
* @copyright 2018 Andrew Nicols <andrew@nicols.co.uk>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class mod_quiz_privacy_provider_testcase extends \core_privacy\tests\provider_testcase {
|
||||
|
||||
use core_question_privacy_helper;
|
||||
|
||||
/**
|
||||
* Test that a user who has no data gets no contexts
|
||||
*/
|
||||
public function test_get_contexts_for_userid_no_data() {
|
||||
global $USER;
|
||||
$this->resetAfterTest();
|
||||
$this->setAdminUser();
|
||||
|
||||
$contextlist = provider::get_contexts_for_userid($USER->id);
|
||||
$this->assertEmpty($contextlist);
|
||||
}
|
||||
|
||||
/**
|
||||
* The export function should handle an empty contextlist properly.
|
||||
*/
|
||||
public function test_export_user_data_no_data() {
|
||||
global $USER;
|
||||
$this->resetAfterTest();
|
||||
$this->setAdminUser();
|
||||
|
||||
$approvedcontextlist = new \core_privacy\tests\request\approved_contextlist(
|
||||
\core_user::get_user($USER->id),
|
||||
'mod_quiz',
|
||||
[]
|
||||
);
|
||||
|
||||
provider::export_user_data($approvedcontextlist);
|
||||
$this->assertDebuggingNotCalled();
|
||||
|
||||
// No data should have been exported.
|
||||
$writer = \core_privacy\local\request\writer::with_context(\context_system::instance());
|
||||
$this->assertFalse($writer->has_any_data_in_any_context());
|
||||
}
|
||||
|
||||
/**
|
||||
* The delete function should handle an empty contextlist properly.
|
||||
*/
|
||||
public function test_delete_data_for_user_no_data() {
|
||||
global $USER;
|
||||
$this->resetAfterTest();
|
||||
$this->setAdminUser();
|
||||
|
||||
$approvedcontextlist = new \core_privacy\tests\request\approved_contextlist(
|
||||
\core_user::get_user($USER->id),
|
||||
'mod_quiz',
|
||||
[]
|
||||
);
|
||||
|
||||
provider::delete_data_for_user($approvedcontextlist);
|
||||
$this->assertDebuggingNotCalled();
|
||||
}
|
||||
|
||||
/**
|
||||
* Export + Delete quiz data for a user who has made a single attempt.
|
||||
*/
|
||||
public function test_user_with_data() {
|
||||
global $DB;
|
||||
$this->resetAfterTest(true);
|
||||
|
||||
// Make a quiz.
|
||||
$course = $this->getDataGenerator()->create_course();
|
||||
$user = $this->getDataGenerator()->create_user();
|
||||
$quizgenerator = $this->getDataGenerator()->get_plugin_generator('mod_quiz');
|
||||
|
||||
$quiz = $quizgenerator->create_instance([
|
||||
'course' => $course->id,
|
||||
'questionsperpage' => 0,
|
||||
'grade' => 100.0,
|
||||
'sumgrades' => 2,
|
||||
]);
|
||||
|
||||
// Create a couple of questions.
|
||||
$questiongenerator = $this->getDataGenerator()->get_plugin_generator('core_question');
|
||||
$cat = $questiongenerator->create_question_category();
|
||||
|
||||
$saq = $questiongenerator->create_question('shortanswer', null, array('category' => $cat->id));
|
||||
quiz_add_quiz_question($saq->id, $quiz);
|
||||
$numq = $questiongenerator->create_question('numerical', null, array('category' => $cat->id));
|
||||
quiz_add_quiz_question($numq->id, $quiz);
|
||||
|
||||
// Run as the user and make an attempt on the quiz.
|
||||
$this->setUser($user);
|
||||
$starttime = time();
|
||||
$quizobj = quiz::create($quiz->id, $user->id);
|
||||
$context = $quizobj->get_context();
|
||||
|
||||
$quba = question_engine::make_questions_usage_by_activity('mod_quiz', $quizobj->get_context());
|
||||
$quba->set_preferred_behaviour($quizobj->get_quiz()->preferredbehaviour);
|
||||
|
||||
// Start the attempt.
|
||||
$attempt = quiz_create_attempt($quizobj, 1, false, $starttime, false, $user->id);
|
||||
quiz_start_new_attempt($quizobj, $quba, $attempt, 1, $starttime);
|
||||
quiz_attempt_save_started($quizobj, $quba, $attempt);
|
||||
|
||||
// Answer the questions.
|
||||
$attemptobj = quiz_attempt::create($attempt->id);
|
||||
|
||||
$tosubmit = [
|
||||
1 => ['answer' => 'frog'],
|
||||
2 => ['answer' => '3.14'],
|
||||
];
|
||||
|
||||
$attemptobj->process_submitted_actions($starttime, false, $tosubmit);
|
||||
|
||||
// Finish the attempt.
|
||||
$attemptobj = quiz_attempt::create($attempt->id);
|
||||
$this->assertTrue($attemptobj->has_response_to_at_least_one_graded_question());
|
||||
$attemptobj->process_finish($starttime, false);
|
||||
|
||||
// Fetch the contexts -only one context should be returned.
|
||||
$this->setUser();
|
||||
$contextlist = provider::get_contexts_for_userid($user->id);
|
||||
$this->assertCount(1, $contextlist);
|
||||
$this->assertEquals($context, $contextlist->current());
|
||||
|
||||
// Perform the export and check the data.
|
||||
$this->setUser($user);
|
||||
$approvedcontextlist = new \core_privacy\tests\request\approved_contextlist(
|
||||
\core_user::get_user($user->id),
|
||||
'mod_quiz',
|
||||
$contextlist->get_contextids()
|
||||
);
|
||||
provider::export_user_data($approvedcontextlist);
|
||||
|
||||
// Ensure that the quiz data was exported correctly.
|
||||
$writer = writer::with_context($context);
|
||||
$this->assertTrue($writer->has_any_data());
|
||||
|
||||
$quizdata = $writer->get_data([]);
|
||||
$this->assertEquals($quizobj->get_quiz_name(), $quizdata->name);
|
||||
|
||||
// Every module has an intro.
|
||||
$this->assertTrue(isset($quizdata->intro));
|
||||
|
||||
// Fetch the attempt data.
|
||||
$attemptsubcontext = [
|
||||
get_string('attempts', 'mod_quiz'),
|
||||
$attempt->attempt,
|
||||
];
|
||||
$attemptdata = writer::with_context($context)->get_data($attemptsubcontext);
|
||||
|
||||
$attempt = $attemptobj->get_attempt();
|
||||
$this->assertTrue(isset($attemptdata->state));
|
||||
$this->assertEquals(\quiz_attempt::state_name($attemptobj->get_state()), $attemptdata->state);
|
||||
$this->assertTrue(isset($attemptdata->timestart));
|
||||
$this->assertTrue(isset($attemptdata->timefinish));
|
||||
$this->assertTrue(isset($attemptdata->timemodified));
|
||||
$this->assertFalse(isset($attemptdata->timemodifiedoffline));
|
||||
$this->assertFalse(isset($attemptdata->timecheckstate));
|
||||
|
||||
$this->assertTrue(isset($attemptdata->grade));
|
||||
$this->assertEquals(100.00, $attemptdata->grade->grade);
|
||||
|
||||
// Check that the exported question attempts are correct.
|
||||
$attemptsubcontext = helper::get_quiz_attempt_subcontext($attemptobj->get_attempt(), $user);
|
||||
$this->assert_question_attempt_exported(
|
||||
$context,
|
||||
$attemptsubcontext,
|
||||
\question_engine::load_questions_usage_by_activity($attemptobj->get_uniqueid()),
|
||||
quiz_get_review_options($quiz, $attemptobj->get_attempt(), $context),
|
||||
$user
|
||||
);
|
||||
|
||||
// Delete the data and check it is removed.
|
||||
$this->setUser();
|
||||
provider::delete_data_for_user($approvedcontextlist);
|
||||
$this->expectException(\dml_missing_record_exception::class);
|
||||
\quiz_attempt::create($attemptobj->get_quizid());
|
||||
}
|
||||
|
||||
/**
|
||||
* Export + Delete quiz data for a user who has made a single attempt.
|
||||
*/
|
||||
public function test_user_with_preview() {
|
||||
global $DB;
|
||||
$this->resetAfterTest(true);
|
||||
|
||||
// Make a quiz.
|
||||
$course = $this->getDataGenerator()->create_course();
|
||||
$user = $this->getDataGenerator()->create_user();
|
||||
$quizgenerator = $this->getDataGenerator()->get_plugin_generator('mod_quiz');
|
||||
|
||||
$quiz = $quizgenerator->create_instance([
|
||||
'course' => $course->id,
|
||||
'questionsperpage' => 0,
|
||||
'grade' => 100.0,
|
||||
'sumgrades' => 2,
|
||||
]);
|
||||
|
||||
// Create a couple of questions.
|
||||
$questiongenerator = $this->getDataGenerator()->get_plugin_generator('core_question');
|
||||
$cat = $questiongenerator->create_question_category();
|
||||
|
||||
$saq = $questiongenerator->create_question('shortanswer', null, array('category' => $cat->id));
|
||||
quiz_add_quiz_question($saq->id, $quiz);
|
||||
$numq = $questiongenerator->create_question('numerical', null, array('category' => $cat->id));
|
||||
quiz_add_quiz_question($numq->id, $quiz);
|
||||
|
||||
// Run as the user and make an attempt on the quiz.
|
||||
$this->setUser($user);
|
||||
$starttime = time();
|
||||
$quizobj = quiz::create($quiz->id, $user->id);
|
||||
$context = $quizobj->get_context();
|
||||
|
||||
$quba = question_engine::make_questions_usage_by_activity('mod_quiz', $quizobj->get_context());
|
||||
$quba->set_preferred_behaviour($quizobj->get_quiz()->preferredbehaviour);
|
||||
|
||||
// Start the attempt.
|
||||
$attempt = quiz_create_attempt($quizobj, 1, false, $starttime, true, $user->id);
|
||||
quiz_start_new_attempt($quizobj, $quba, $attempt, 1, $starttime);
|
||||
quiz_attempt_save_started($quizobj, $quba, $attempt);
|
||||
|
||||
// Answer the questions.
|
||||
$attemptobj = quiz_attempt::create($attempt->id);
|
||||
|
||||
$tosubmit = [
|
||||
1 => ['answer' => 'frog'],
|
||||
2 => ['answer' => '3.14'],
|
||||
];
|
||||
|
||||
$attemptobj->process_submitted_actions($starttime, false, $tosubmit);
|
||||
|
||||
// Finish the attempt.
|
||||
$attemptobj = quiz_attempt::create($attempt->id);
|
||||
$this->assertTrue($attemptobj->has_response_to_at_least_one_graded_question());
|
||||
$attemptobj->process_finish($starttime, false);
|
||||
|
||||
// Fetch the contexts - no context should be returned.
|
||||
$this->setUser();
|
||||
$contextlist = provider::get_contexts_for_userid($user->id);
|
||||
$this->assertCount(0, $contextlist);
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue