From 65d2f11304f1d4ecce85cd0927ef7cec0e687c70 Mon Sep 17 00:00:00 2001 From: Andrew Nicols Date: Fri, 17 Apr 2020 11:22:04 +0800 Subject: [PATCH 1/2] MDL-68246 roles tests --- user/tests/participants_search_test.php | 594 ++++++++++++++++++++++++ 1 file changed, 594 insertions(+) create mode 100644 user/tests/participants_search_test.php diff --git a/user/tests/participants_search_test.php b/user/tests/participants_search_test.php new file mode 100644 index 00000000000..d6701515940 --- /dev/null +++ b/user/tests/participants_search_test.php @@ -0,0 +1,594 @@ +. + +/** + * Provides {@link core_user_selector_testcase} class. + * + * @package core_user + * @category test + * @copyright 2020 Andrew Nicols + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +declare(strict_types=1); + +namespace core_user; + +use advanced_testcase; +use context_course; +use context_coursecat; +use core_table\local\filter\filter; +use core_table\local\filter\integer_filter; +use core_user\table\participants_filterset; +use moodle_recordset; +use stdClass; + +/** + * Tests for the implementation of {@link user_selector_base} class. + * + * @copyright 2020 Andrew Nicols + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class participants_search_test extends advanced_testcase { + + /** + * Helper to convert a moodle_recordset to an array of records. + * + * @param moodle_recordset $recordset + * @return array + */ + protected function convert_recordset_to_array(moodle_recordset $recordset): array { + $records = []; + foreach ($recordset as $record) { + $records[$record->id] = $record; + } + $recordset->close(); + + return $records; + } + + /** + * Create and enrol a set of users into the specified course. + * + * @param stdClass $course + * @param int $count + * @param null|string $role + * @return array + */ + protected function create_and_enrol_users(stdClass $course, int $count, ?string $role = null): array { + $this->resetAfterTest(true); + $users = []; + + for ($i = 0; $i < $count; $i++) { + $user = $this->getDataGenerator()->create_user(); + $this->getDataGenerator()->enrol_user($user->id, $course->id, $role); + $users[] = $user; + } + + return $users; + } + + /** + * Create a new course with several types of user. + * + * @param int $editingteachers + * @param int $teachers + * @param int $students + * @param int $norole + * @return stdClass + */ + protected function create_course_with_users(int $editingteachers, int $teachers, int $students, int $norole): stdClass { + $data = (object) [ + 'course' => $this->getDataGenerator()->create_course(), + 'editingteachers' => [], + 'teachers' => [], + 'students' => [], + 'norole' => [], + ]; + + $data->context = context_course::instance($data->course->id); + + $data->editingteachers = $this->create_and_enrol_users($data->course, $editingteachers, 'editingteacher'); + $data->teachers = $this->create_and_enrol_users($data->course, $teachers, 'teacher'); + $data->students = $this->create_and_enrol_users($data->course, $students, 'student'); + $data->norole = $this->create_and_enrol_users($data->course, $students); + + return $data; + } + + /** + * Ensure that the roles filter works as expected with the provided test cases. + * + * @param array $usersdata The list of users and their roles to create + * @param array $testroles The list of roles to filter by + * @param int $jointype The join type to use when combining filter values + * @param int $count The expected count + * @param array $expectedusers + * @dataProvider role_provider + */ + public function test_roles_filter(array $usersdata, array $testroles, int $jointype, int $count, array $expectedusers): void { + global $DB; + + $roles = $DB->get_records_menu('role', [], '', 'shortname, id'); + + // Remove the default role. + set_config('roleid', 0, 'enrol_manual'); + + $course = $this->getDataGenerator()->create_course(); + $coursecontext = context_course::instance($course->id); + + $category = $DB->get_record('course_categories', ['id' => $course->category]); + $categorycontext = context_coursecat::instance($category->id); + + $users = []; + + foreach ($usersdata as $username => $userdata) { + $user = $this->getDataGenerator()->create_user(['username' => $username]); + + if (array_key_exists('courseroles', $userdata)) { + $this->getDataGenerator()->enrol_user($user->id, $course->id, null); + foreach ($userdata['courseroles'] as $rolename) { + $this->getDataGenerator()->role_assign($roles[$rolename], $user->id, $coursecontext->id); + } + } + + if (array_key_exists('categoryroles', $userdata)) { + foreach ($userdata['categoryroles'] as $rolename) { + $this->getDataGenerator()->role_assign($roles[$rolename], $user->id, $categorycontext->id); + } + } + $users[$username] = $user; + } + + // Create a secondary course with users. We should not see these users. + $this->create_course_with_users(1, 1, 1, 1); + + // Create the basic filter. + $filterset = new participants_filterset(); + $filterset->add_filter(new integer_filter('courseid', null, [(int) $course->id])); + + // Create the role filter. + $rolefilter = new integer_filter('roles'); + $filterset->add_filter($rolefilter); + + // Configure the filter. + foreach ($testroles as $rolename) { + $rolefilter->add_filter_value((int) $roles[$rolename]); + } + $rolefilter->set_join_type($jointype); + + // Run the search. + $search = new participants_search($filterset); + $rs = $search->get_participants(); + $this->assertInstanceOf(moodle_recordset::class, $rs); + $records = $this->convert_recordset_to_array($rs); + + $this->assertCount($count, $records); + $this->assertEquals($count, $search->get_total_participants_count()); + + foreach ($expectedusers as $expecteduser) { + $this->assertArrayHasKey($users[$expecteduser]->id, $records); + } + } + + /** + * Data provider for role tests. + * + * @return array + */ + public function role_provider(): array { + $tests = [ + // Users who only have one role each. + 'Users in each role' => (object) [ + 'users' => [ + 'a' => [ + 'courseroles' => [ + 'student', + ], + ], + 'b' => [ + 'courseroles' => [ + 'student', + ], + ], + 'c' => [ + 'courseroles' => [ + 'editingteacher', + ], + ], + 'd' => [ + 'courseroles' => [ + 'editingteacher', + ], + ], + 'e' => [ + 'courseroles' => [ + 'teacher', + ], + ], + 'f' => [ + 'courseroles' => [ + 'teacher', + ], + ], + // User is enrolled in the course without role. + 'g' => [ + 'courseroles' => [ + ], + ], + + // User is a category manager and also enrolled without role in the course. + 'h' => [ + 'courseroles' => [ + ], + 'categoryroles' => [ + 'manager', + ], + ], + + // User is a category manager and not enrolled in the course. + // This user should not show up in any filter. + 'i' => [ + 'categoryroles' => [ + 'manager', + ], + ], + ], + 'expect' => [ + 'ANY: No role' => (object) [ + 'roles' => [], + 'jointype' => filter::JOINTYPE_ANY, + 'count' => 8, + 'expectedusers' => [ + 'a', + 'b', + 'c', + 'd', + 'e', + 'f', + 'g', + 'h', + ], + ], + 'ANY: Filter on student' => (object) [ + 'roles' => ['student'], + 'jointype' => filter::JOINTYPE_ANY, + 'count' => 2, + 'expectedusers' => [ + 'a', + 'b', + ], + ], + 'ANY: Filter on student, teacher' => (object) [ + 'roles' => ['student', 'teacher'], + 'jointype' => filter::JOINTYPE_ANY, + 'count' => 4, + 'expectedusers' => [ + 'a', + 'b', + 'e', + 'f', + ], + ], + 'ANY: Filter on student, manager (category level role)' => (object) [ + 'roles' => ['student', 'manager'], + 'jointype' => filter::JOINTYPE_ANY, + 'count' => 3, + 'expectedusers' => [ + 'a', + 'b', + 'h', + ], + ], + 'ANY: Filter on student, coursecreator (not assigned)' => (object) [ + 'roles' => ['student', 'coursecreator'], + 'jointype' => filter::JOINTYPE_ANY, + 'count' => 2, + 'expectedusers' => [ + 'a', + 'b', + ], + ], + + 'ALL: No role' => (object) [ + 'roles' => [], + 'jointype' => filter::JOINTYPE_ALL, + 'count' => 8, + 'expectedusers' => [ + 'a', + 'b', + 'c', + 'd', + 'e', + 'f', + 'g', + 'h', + ], + ], + 'ALL: Filter on student' => (object) [ + 'roles' => ['student'], + 'jointype' => filter::JOINTYPE_ALL, + 'count' => 2, + 'expectedusers' => [ + 'a', + 'b', + ], + ], + /* + 'ALL: Filter on student, teacher' => (object) [ + 'roles' => ['student', 'teacher'], + 'jointype' => filter::JOINTYPE_ALL, + 'count' => 0, + 'expectedusers' => [], + ], + 'ALL: Filter on student, manager (category level role))' => (object) [ + 'roles' => ['student', 'manager'], + 'jointype' => filter::JOINTYPE_ALL, + 'count' => 0, + 'expectedusers' => [], + ], + 'ALL: Filter on student, coursecreator (not assigned))' => (object) [ + 'roles' => ['student', 'coursecreator'], + 'jointype' => filter::JOINTYPE_ALL, + 'count' => 0, + 'expectedusers' => [], + ], + */ + ], + ], + 'Users with multiple roles' => (object) [ + 'users' => [ + 'a' => [ + 'courseroles' => [ + 'student', + ], + ], + 'b' => [ + 'courseroles' => [ + 'student', + 'teacher', + ], + ], + 'c' => [ + 'courseroles' => [ + 'editingteacher', + ], + ], + 'd' => [ + 'courseroles' => [ + 'editingteacher', + ], + ], + 'e' => [ + 'courseroles' => [ + 'teacher', + 'editingteacher', + ], + ], + 'f' => [ + 'courseroles' => [ + 'teacher', + ], + ], + + // User is enrolled in the course without role. + 'g' => [ + 'courseroles' => [ + ], + ], + + // User is a category manager and also enrolled without role in the course. + 'h' => [ + 'courseroles' => [ + ], + 'categoryroles' => [ + 'manager', + ], + ], + + // User is a category manager and not enrolled in the course. + // This user should not show up in any filter. + 'i' => [ + 'categoryroles' => [ + 'manager', + ], + ], + ], + 'expect' => [ + 'ANY: No role' => (object) [ + 'roles' => [], + 'jointype' => filter::JOINTYPE_ANY, + 'count' => 8, + 'expectedusers' => [ + 'a', + 'b', + 'c', + 'd', + 'e', + 'f', + 'g', + 'h', + ], + ], + 'ANY: Filter on student' => (object) [ + 'roles' => ['student'], + 'jointype' => filter::JOINTYPE_ANY, + 'count' => 2, + 'expectedusers' => [ + 'a', + 'b', + ], + ], + 'ANY: Filter on teacher' => (object) [ + 'roles' => ['teacher'], + 'jointype' => filter::JOINTYPE_ANY, + 'count' => 3, + 'expectedusers' => [ + 'b', + 'e', + 'f', + ], + ], + 'ANY: Filter on editingteacher' => (object) [ + 'roles' => ['editingteacher'], + 'jointype' => filter::JOINTYPE_ANY, + 'count' => 3, + 'expectedusers' => [ + 'c', + 'd', + 'e', + ], + ], + 'ANY: Filter on student, teacher' => (object) [ + 'roles' => ['student', 'teacher'], + 'jointype' => filter::JOINTYPE_ANY, + 'count' => 4, + 'expectedusers' => [ + 'a', + 'b', + 'e', + 'f', + ], + ], + 'ANY: Filter on teacher, editingteacher' => (object) [ + 'roles' => ['teacher', 'editingteacher'], + 'jointype' => filter::JOINTYPE_ANY, + 'count' => 5, + 'expectedusers' => [ + 'b', + 'c', + 'd', + 'e', + 'f', + ], + ], + 'ANY: Filter on student, manager (category level role)' => (object) [ + 'roles' => ['student', 'manager'], + 'jointype' => filter::JOINTYPE_ANY, + 'count' => 3, + 'expectedusers' => [ + 'a', + 'b', + 'h', + ], + ], + 'ANY: Filter on student, coursecreator (not assigned)' => (object) [ + 'roles' => ['student', 'coursecreator'], + 'jointype' => filter::JOINTYPE_ANY, + 'count' => 2, + 'expectedusers' => [ + 'a', + 'b', + ], + ], + + + // Tests for jointype: ALL. + 'ALL: No role' => (object) [ + 'roles' => [], + 'jointype' => filter::JOINTYPE_ALL, + 'count' => 8, + 'expectedusers' => [ + 'a', + 'b', + 'c', + 'd', + 'e', + 'f', + 'g', + 'h', + ], + ], + 'ALL: Filter on student' => (object) [ + 'roles' => ['student'], + 'jointype' => filter::JOINTYPE_ALL, + 'count' => 2, + 'expectedusers' => [ + 'a', + 'b', + ], + ], + 'ALL: Filter on teacher' => (object) [ + 'roles' => ['teacher'], + 'jointype' => filter::JOINTYPE_ALL, + 'count' => 3, + 'expectedusers' => [ + 'b', + 'e', + 'f', + ], + ], + 'ALL: Filter on editingteacher' => (object) [ + 'roles' => ['editingteacher'], + 'jointype' => filter::JOINTYPE_ALL, + 'count' => 3, + 'expectedusers' => [ + 'c', + 'd', + 'e', + ], + ], + /* + 'ALL: Filter on student, teacher' => (object) [ + 'roles' => ['student', 'teacher'], + 'jointype' => filter::JOINTYPE_ALL, + 'count' => 1, + 'expectedusers' => [ + 'b', + ], + ], + 'ALL: Filter on teacher, editingteacher' => (object) [ + 'roles' => ['teacher', 'editingteacher'], + 'jointype' => filter::JOINTYPE_ALL, + 'count' => 1, + 'expectedusers' => [ + 'e', + ], + ], + 'ALL: Filter on student, manager (category level role)' => (object) [ + 'roles' => ['student', 'manager'], + 'jointype' => filter::JOINTYPE_ALL, + 'count' => 0, + 'expectedusers' => [], + ], + 'ALL: Filter on student, coursecreator (not assigned)' => (object) [ + 'roles' => ['student', 'coursecreator'], + 'jointype' => filter::JOINTYPE_ALL, + 'count' => 0, + 'expectedusers' => [], + ], + */ + ], + ], + ]; + + $finaltests = []; + foreach ($tests as $testname => $testdata) { + foreach ($testdata->expect as $expectname => $expectdata) { + $finaltests["$testname => {$expectname}"] = [ + 'users' => $testdata->users, + 'roles' => $expectdata->roles, + 'jointype' => $expectdata->jointype, + 'count' => $expectdata->count, + 'expectedusers' => $expectdata->expectedusers, + ]; + } + } + + return $finaltests; + } + +} -- 2.21.0