MDL-51861 enrol: new unit test to verify all conditions and filters
authorEloy Lafuente (stronk7) <stronk7@moodle.org>
Fri, 6 Nov 2015 00:03:02 +0000 (01:03 +0100)
committerEloy Lafuente (stronk7) <stronk7@moodle.org>
Fri, 6 Nov 2015 01:26:02 +0000 (02:26 +0100)
This unit test does not verify contents, details, but only
that the returned users for every group mode, for every
filtering (active, groupid and withcapability) together
with permissions are correct.

enrol/tests/externallib_test.php

index 170d064..9f0ee98 100644 (file)
@@ -32,6 +32,326 @@ require_once($CFG->dirroot . '/enrol/externallib.php');
  */
 class core_enrol_externallib_testcase extends externallib_advanced_testcase {
 
+    /**
+     * dataProvider for test_get_enrolled_users_visibility().
+     */
+    public function get_enrolled_users_visibility_provider() {
+        return array(
+            'Course without groups, default behavior (not filtering by cap, group, active)' =>
+            array(
+                'settings' => array(
+                    'coursegroupmode' => NOGROUPS,
+                    'withcapability' => null,
+                    'groupid' => null,
+                    'onlyactive' => false,
+                    'allowedcaps' => array(),
+                ),
+                'results' => array( // Everybody can view everybody.
+                    'user0' => array('canview' => array('user0', 'user1', 'user2', 'user2su', 'user31', 'user32', 'userall')),
+                    'user1' => array('canview' => array('user0', 'user1', 'user2', 'user2su', 'user31', 'user32', 'userall')),
+                    'user2' => array('canview' => array('user0', 'user1', 'user2', 'user2su', 'user31', 'user32', 'userall')),
+                    'user31' => array('canview' => array('user0', 'user1', 'user2', 'user2su', 'user31', 'user32', 'userall')),
+                    'userall' => array('canview' => array('user0', 'user1', 'user2', 'user2su', 'user31', 'user32', 'userall')),
+                ),
+            ),
+
+            'Course with visible groups, default behavior (not filtering by cap, group, active)' =>
+            array(
+                'settings' => array(
+                    'coursegroupmode' => VISIBLEGROUPS,
+                    'withcapability' => null,
+                    'groupid' => null,
+                    'onlyactive' => false,
+                    'allowedcaps' => array(),
+                ),
+                'results' => array( // Everybody can view everybody.
+                    'user0' => array('canview' => array('user0', 'user1', 'user2', 'user2su', 'user31', 'user32', 'userall')),
+                    'user1' => array('canview' => array('user0', 'user1', 'user2', 'user2su', 'user31', 'user32', 'userall')),
+                    'user2' => array('canview' => array('user0', 'user1', 'user2', 'user2su', 'user31', 'user32', 'userall')),
+                    'user31' => array('canview' => array('user0', 'user1', 'user2', 'user2su', 'user31', 'user32', 'userall')),
+                    'userall' => array('canview' => array('user0', 'user1', 'user2', 'user2su', 'user31', 'user32', 'userall')),
+                ),
+            ),
+
+            'Course with separate groups, default behavior (not filtering by cap, group, active)' =>
+            array(
+                'settings' => array(
+                    'coursegroupmode' => SEPARATEGROUPS,
+                    'withcapability' => null,
+                    'groupid' => null,
+                    'onlyactive' => false,
+                    'allowedcaps' => array(),
+                ),
+                'results' => array( // Only users from own groups are visible.
+                    'user0' => array('canview' => array()), // Poor guy, cannot see anybody, himself included.
+                    'user1' => array('canview' => array('user1', 'userall')),
+                    'user2' => array('canview' => array('user2', 'user2su', 'userall')),
+                    'user31' => array('canview' => array('user31', 'user32', 'userall')),
+                    'userall' => array('canview' => array('user1', 'user2', 'user2su', 'user31', 'user32', 'userall')),
+                ),
+            ),
+
+            'Course with separate groups, default behavior (not filtering but having moodle/site:accessallgroups)' =>
+            array(
+                'settings' => array(
+                    'coursegroupmode' => VISIBLEGROUPS,
+                    'withcapability' => null,
+                    'groupid' => null,
+                    'onlyactive' => false,
+                    'allowedcaps' => array('moodle/site:accessallgroups'),
+                ),
+                'results' => array( // Everybody can view everybody.
+                    'user0' => array('canview' => array('user0', 'user1', 'user2', 'user2su', 'user31', 'user32', 'userall')),
+                    'user1' => array('canview' => array('user0', 'user1', 'user2', 'user2su', 'user31', 'user32', 'userall')),
+                    'user2' => array('canview' => array('user0', 'user1', 'user2', 'user2su', 'user31', 'user32', 'userall')),
+                    'user31' => array('canview' => array('user0', 'user1', 'user2', 'user2su', 'user31', 'user32', 'userall')),
+                    'userall' => array('canview' => array('user0', 'user1', 'user2', 'user2su', 'user31', 'user32', 'userall')),
+                ),
+            ),
+
+            'Course with separate groups, filtering onlyactive (missing moodle/course:enrolreview)' =>
+            array(
+                'settings' => array(
+                    'coursegroupmode' => SEPARATEGROUPS,
+                    'withcapability' => null,
+                    'groupid' => null,
+                    'onlyactive' => true,
+                    'allowedcaps' => array(),
+                ),
+                'results' => array( // returns exception, cannot view anybody without the cap.
+                    'user2' => array('exception' => array(
+                        'type' => 'required_capability_exception',
+                        'message' => 'Review course enrolments')),
+                    'userall' => array('exception' => array(
+                        'type' => 'required_capability_exception',
+                        'message' => 'Review course enrolments')),
+                ),
+            ),
+
+            'Course with separate groups, filtering onlyactive (having moodle/course:enrolreview)' =>
+            array(
+                'settings' => array(
+                    'coursegroupmode' => SEPARATEGROUPS,
+                    'withcapability' => null,
+                    'groupid' => null,
+                    'onlyactive' => true,
+                    'allowedcaps' => array('moodle/course:enrolreview'),
+                ),
+                'results' => array( // Suspended are not returned.
+                    'user2' => array('canview' => array('user2', 'userall')),
+                    'user31' => array('canview' => array('user31', 'user32', 'userall')),
+                    'userall' => array('canview' => array('user1', 'user2', 'user31', 'user32', 'userall')),
+                ),
+            ),
+
+            'Course with separate groups, filtering by groupid (not having moodle/site:accessallgroups)' =>
+            array(
+                'settings' => array(
+                    'coursegroupmode' => SEPARATEGROUPS,
+                    'withcapability' => null,
+                    'groupid' => 'group2',
+                    'onlyactive' => false,
+                    'allowedcaps' => array(),
+                ),
+                'results' => array( // Only group 2 members and only for members. Exception for non-members.
+                    'user0' => array('exception' => array(
+                        'type' => 'required_capability_exception',
+                        'message' => 'Access all groups')),
+                    'user1' => array('exception' => array(
+                        'type' => 'required_capability_exception',
+                        'message' => 'Access all groups')),
+                    'user2' => array('canview' => array('user2', 'user2su', 'userall')),
+                    'userall' => array('canview' => array('user2', 'user2su', 'userall')),
+                ),
+            ),
+
+            'Course with separate groups, filtering by groupid (having moodle/site:accessallgroups)' =>
+            array(
+                'settings' => array(
+                    'coursegroupmode' => SEPARATEGROUPS,
+                    'withcapability' => null,
+                    'groupid' => 'group2',
+                    'onlyactive' => false,
+                    'allowedcaps' => array('moodle/site:accessallgroups'),
+                ),
+                'results' => array( // All users with 'moodle/site:accessallgroups' can view group 2
+                    'user0' => array('canview' => array('user2', 'user2su', 'userall')),
+                    'user1' => array('canview' => array('user2', 'user2su', 'userall')),
+                    'user2' => array('canview' => array('user2', 'user2su', 'userall')),
+                    'userall' => array('canview' => array('user2', 'user2su', 'userall')),
+                ),
+            ),
+
+            'Course with separate groups, filtering by withcapability (not having moodle/role:review)' =>
+            array(
+                'settings' => array(
+                    'coursegroupmode' => SEPARATEGROUPS,
+                    'withcapability' => 'moodle/course:bulkmessaging',
+                    'groupid' => null,
+                    'onlyactive' => false,
+                    'allowedcaps' => array(),
+                ),
+                'results' => array( // No user has 'moodle/role:review' so exception.
+                    'user0' => array('exception' => array(
+                        'type' => 'required_capability_exception',
+                        'message' => 'Review permissions for others')),
+                    'user1' => array('exception' => array(
+                        'type' => 'required_capability_exception',
+                        'message' => 'Review permissions for others')),
+                    'user2' => array('exception' => array(
+                        'type' => 'required_capability_exception',
+                        'message' => 'Review permissions for others')),
+                    'userall' => array('exception' => array(
+                        'type' => 'required_capability_exception',
+                        'message' => 'Review permissions for others')),
+                ),
+            ),
+
+            'Course with separate groups, filtering by withcapability (having moodle/role:review)' =>
+            array(
+                'settings' => array(
+                    'coursegroupmode' => SEPARATEGROUPS,
+                    'withcapability' => 'moodle/course:bulkmessaging',
+                    'groupid' => null,
+                    'onlyactive' => false,
+                    'allowedcaps' => array('moodle/role:review'),
+                ),
+                'results' => array( // No user has withcapability, but all have 'moodle/role:review'. Empties.
+                    'user0' => array('canview' => array()),
+                    'user1' => array('canview' => array()),
+                    'user2' => array('canview' => array()),
+                    'userall' => array('canview' => array()),
+                ),
+            ),
+
+            'Course with separate groups, filtering by withcapability (having moodle/role:review)' =>
+            array(
+                'settings' => array(
+                    'coursegroupmode' => SEPARATEGROUPS,
+                    'withcapability' => 'moodle/course:bulkmessaging',
+                    'groupid' => null,
+                    'onlyactive' => false,
+                    'allowedcaps' => array('moodle/role:review', 'moodle/course:bulkmessaging'),
+                ),
+                'results' => array( // Users (previous) have withcapability, and all have 'moodle/role:review'.
+                    'user0' => array('canview' => array()),
+                    'user1' => array('canview' => array('user1')),
+                    'user2' => array('canview' => array('user2')),
+                    'userall' => array('canview' => array('user1', 'user2', 'userall')),
+                ),
+            ),
+        );
+    }
+
+
+    /**
+     * Verify get_enrolled_users() returned users are the expected in every situation.
+     *
+     * @dataProvider get_enrolled_users_visibility_provider
+     */
+    public function test_get_enrolled_users_visibility($settings, $results) {
+
+        global $USER;
+
+        $this->resetAfterTest();
+
+        // Create the course and the users.
+        $course = $this->getDataGenerator()->create_course(array('groupmode' => $settings['coursegroupmode']));
+        $coursecontext = context_course::instance($course->id);
+        $user0 = $this->getDataGenerator()->create_user(array('username' => 'user0'));     // A user without group.
+        $user1 = $this->getDataGenerator()->create_user(array('username' => 'user1'));     // User for group 1.
+        $user2 = $this->getDataGenerator()->create_user(array('username' => 'user2'));     // Two users for group 2.
+        $user2su = $this->getDataGenerator()->create_user(array('username' => 'user2su')); // (one suspended).
+        $user31 = $this->getDataGenerator()->create_user(array('username' => 'user31'));   // Two users for group 3.
+        $user32 = $this->getDataGenerator()->create_user(array('username' => 'user32'));   // (both enabled).
+        $userall = $this->getDataGenerator()->create_user(array('username' => 'userall')); // A user in all groups.
+
+        // Create utility array of created users, to produce better assertion messages.
+        $createdusers = array();
+        foreach (array($user0, $user1, $user2, $user2su, $user31, $user32, $userall) as $createduser) {
+            $createdusers[$createduser->id] = $createduser->username;
+        }
+
+        // Enrol the users in the course.
+        $this->getDataGenerator()->enrol_user($user0->id, $course->id);
+        $this->getDataGenerator()->enrol_user($user1->id, $course->id);
+        $this->getDataGenerator()->enrol_user($user2->id, $course->id);
+        $this->getDataGenerator()->enrol_user($user2su->id, $course->id, null, 'manual', 0, 0, ENROL_USER_SUSPENDED);
+        $this->getDataGenerator()->enrol_user($user31->id, $course->id);
+        $this->getDataGenerator()->enrol_user($user32->id, $course->id);
+        $this->getDataGenerator()->enrol_user($userall->id, $course->id);
+
+        // Create 3 groups.
+        $group1 = $this->getDataGenerator()->create_group(array('courseid' => $course->id));
+        $group2 = $this->getDataGenerator()->create_group(array('courseid' => $course->id));
+        $group3 = $this->getDataGenerator()->create_group(array('courseid' => $course->id));
+
+        // Add the users to the groups.
+        $this->getDataGenerator()->create_group_member(array('groupid' => $group1->id, 'userid' => $user1->id));
+        $this->getDataGenerator()->create_group_member(array('groupid' => $group2->id, 'userid' => $user2->id));
+        $this->getDataGenerator()->create_group_member(array('groupid' => $group2->id, 'userid' => $user2su->id));
+        $this->getDataGenerator()->create_group_member(array('groupid' => $group3->id, 'userid' => $user31->id));
+        $this->getDataGenerator()->create_group_member(array('groupid' => $group3->id, 'userid' => $user32->id));
+        $this->getDataGenerator()->create_group_member(array('groupid' => $group1->id, 'userid' => $userall->id));
+        $this->getDataGenerator()->create_group_member(array('groupid' => $group2->id, 'userid' => $userall->id));
+        $this->getDataGenerator()->create_group_member(array('groupid' => $group3->id, 'userid' => $userall->id));
+
+        // Create a role to add the allowedcaps. Users will have this role assigned.
+        $roleid = $this->getDataGenerator()->create_role();
+        // Allow the specified capabilities.
+        if (!empty($settings['allowedcaps'])) {
+            foreach ($settings['allowedcaps'] as $capability) {
+                assign_capability($capability, CAP_ALLOW, $roleid, $coursecontext);
+            }
+        }
+
+        // For each of the users, configure everything, perform the call, and assert results.
+        foreach ($results as $user => $expectations) {
+            // Convert canview expectations into a nice array of ids for easier handling.
+            $canview = array();
+            $exception = null;
+            // Analyse the expectations.
+            if (isset($expectations['canview'])) {
+                foreach ($expectations['canview'] as $canviewuser) {
+                    $canview[] = $createdusers[${$canviewuser}->id];
+                }
+            } else if (isset($expectations['exception'])) {
+                $exception = $expectations['exception'];
+                $this->setExpectedException($exception['type'], $exception['message']);
+            } else {
+                // Failed, only canview and exception are supported.
+                $this->markTestIncomplete('Incomplete, only canview and exception are supported');
+            }
+            // Switch to the user and assign the role.
+            $this->setUser(${$user});
+            role_assign($roleid, $USER->id, $coursecontext);
+
+            // Convert groupid to proper id.
+            $groupid = 0;
+            if (isset($settings['groupid'])) {
+                $groupid = ${$settings['groupid']}->id;
+            }
+
+            // Call to the function.
+            $options = array(
+                array('name' => 'withcapability', 'value' => $settings['withcapability']),
+                array('name' => 'groupid', 'value' => $groupid),
+                array('name' => 'onlyactive', 'value' => $settings['onlyactive']),
+                array('name' => 'userfields', 'value' => 'id')
+            );
+            $enrolledusers = core_enrol_external::get_enrolled_users($course->id, $options);
+
+            // We are only interested in ids to check visibility.
+            $viewed = array();
+            // Verify the user canview the expected users.
+            foreach ($enrolledusers as $enrolleduser) {
+                $viewed[] = $createdusers[$enrolleduser['id']];
+            }
+            // Verify viewed matches canview expectation (using canonicalize to ignore ordering).
+            $this->assertEquals($canview, $viewed, "Problem checking visible users for '{$createdusers[$USER->id]}'", 0, 1, true);
+        }
+    }
+
     /**
      * Test get_enrolled_users
      */