tests/externallib_test.php
<?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/>.

namespace auth_userkey;
defined('MOODLE_INTERNAL') || die();

use advanced_testcase;
use webservice_access_exception;
use auth_userkey_external;
use core_external\external_api;
use invalid_parameter_exception;
use required_capability_exception;
use context_system;
global $CFG;
require_once($CFG->dirroot . '/webservice/lib.php');
require_once($CFG->dirroot . '/auth/userkey/externallib.php');

/**
 * Tests for externallib.php.
 *
 * @covers \auth_userkey_external
 *
 * @package    auth_userkey
 * @copyright  2016 Dmitrii Metelkin (dmitriim@catalyst-au.net)
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 */
final class externallib_test extends advanced_testcase {
    /**
     * User object.
     *
     * @var $user.
     */
    protected $user = [];

    /**
     * Initial set up.
     */
    public function setUp(): void {
        global $CFG;
        parent::setUp();

        $this->resetAfterTest();

        $user = [];
        $user['username'] = 'username';
        $user['email'] = 'exists@test.com';
        $user['idnumber'] = 'idnumber';
        $this->user = self::getDataGenerator()->create_user($user);
    }

    /**
     * Test call with incorrect required parameter.
     */
    public function test_throwing_plugin_disabled_exception(): void {
        $this->setAdminUser();

        $params = [
            'bla' => 'exists@test.com',
        ];

        $this->expectException(webservice_access_exception::class);
        $this->expectExceptionMessage('Access control exception (The userkey authentication plugin is disabled.)');

        // Simulate the web service server.
        $result = auth_userkey_external::request_login_url($params);
        $result = external_api::clean_returnvalue(auth_userkey_external::request_login_url_returns(), $result);
    }

    /**
     * Test successful web service calls.
     */
    public function test_successful_webservice_calls(): void {
        global $DB, $CFG;

        $CFG->auth = "userkey";
        $this->setAdminUser();

        // Email.
        $params = [
            'email' => 'exists@test.com',
        ];

        // Simulate the web service server.
        $result = auth_userkey_external::request_login_url($params);
        $result = external_api::clean_returnvalue(auth_userkey_external::request_login_url_returns(), $result);

        $actualkey = $DB->get_record('user_private_key', ['userid' => $this->user->id]);
        $expectedurl = $CFG->wwwroot . '/auth/userkey/login.php?key=' . $actualkey->value;

        $this->assertTrue(is_array($result));
        $this->assertTrue(key_exists('loginurl', $result));
        $this->assertEquals($expectedurl, $result['loginurl']);

        // Username.
        set_config('mappingfield', 'username', 'auth_userkey');
        $params = [
            'username' => 'username',
        ];

        // Simulate the web service server.
        $result = auth_userkey_external::request_login_url($params);
        $result = external_api::clean_returnvalue(auth_userkey_external::request_login_url_returns(), $result);

        $actualkey = $DB->get_record('user_private_key', ['userid' => $this->user->id]);
        $expectedurl = $CFG->wwwroot . '/auth/userkey/login.php?key=' . $actualkey->value;

        $this->assertTrue(is_array($result));
        $this->assertTrue(key_exists('loginurl', $result));
        $this->assertEquals($expectedurl, $result['loginurl']);

        // Idnumber.
        set_config('mappingfield', 'idnumber', 'auth_userkey');
        $params = [
            'idnumber' => 'idnumber',
        ];

        // Simulate the web service server.
        $result = auth_userkey_external::request_login_url($params);
        $result = external_api::clean_returnvalue(auth_userkey_external::request_login_url_returns(), $result);

        $actualkey = $DB->get_record('user_private_key', ['userid' => $this->user->id]);
        $expectedurl = $CFG->wwwroot . '/auth/userkey/login.php?key=' . $actualkey->value;

        $this->assertTrue(is_array($result));
        $this->assertTrue(key_exists('loginurl', $result));
        $this->assertEquals($expectedurl, $result['loginurl']);

        // IP restriction.
        set_config('iprestriction', true, 'auth_userkey');
        set_config('mappingfield', 'idnumber', 'auth_userkey');
        $params = [
            'idnumber' => 'idnumber',
            'ip' => '192.168.1.1',
        ];

        // Simulate the web service server.
        $result = auth_userkey_external::request_login_url($params);
        $result = external_api::clean_returnvalue(auth_userkey_external::request_login_url_returns(), $result);

        $actualkey = $DB->get_record('user_private_key', ['userid' => $this->user->id]);
        $expectedurl = $CFG->wwwroot . '/auth/userkey/login.php?key=' . $actualkey->value;

        $this->assertTrue(is_array($result));
        $this->assertTrue(key_exists('loginurl', $result));
        $this->assertEquals($expectedurl, $result['loginurl']);
    }

    /**
     * Test call with missing email required parameter.
     */
    public function test_exception_thrown_if_required_parameter_email_is_not_set(): void {
        global $CFG;

        $this->setAdminUser();
        $CFG->auth = "userkey";

        $params = [
            'bla' => 'exists@test.com',
        ];

        $this->expectException(invalid_parameter_exception::class);
        $this->expectExceptionMessage('Invalid parameter value detected (Required field "email" is not set or empty.)');

        auth_userkey_external::request_login_url($params);
    }

    /**
     * Test call with missing ip required parameter.
     */
    public function test_exception_thrown_if_required_parameter_op_is_not_set(): void {
        global $CFG;

        $this->setAdminUser();
        $CFG->auth = "userkey";

        set_config('iprestriction', true, 'auth_userkey');

        $params = [
            'email' => 'exists@test.com',
        ];

        $this->expectException(invalid_parameter_exception::class);
        $this->expectExceptionMessage('Invalid parameter value detected (Required parameter "ip" is not set.)');

        auth_userkey_external::request_login_url($params);
    }

    /**
     * Test request for a user who is not exist.
     */
    public function test_request_not_existing_user(): void {
        global $CFG;

        $this->setAdminUser();
        $CFG->auth = "userkey";

        $params = [
            'email' => 'notexists@test.com',
        ];

        $this->expectException(invalid_parameter_exception::class);
        $this->expectExceptionMessage('Invalid parameter value detected (User is not exist)');

        // Simulate the web service server.
        $result = auth_userkey_external::request_login_url($params);
        $result = external_api::clean_returnvalue(auth_userkey_external::request_login_url_returns(), $result);
    }

    /**
     * Test that permission exception gets thrown if user doesn't have required permissions.
     */
    public function test_throwing_of_permission_exception(): void {
        global $CFG;

        $this->setUser($this->user);
        $CFG->auth = "userkey";

        $params = [
            'email' => 'notexists@test.com',
        ];

        $this->expectException(required_capability_exception::class);
        $this->expectExceptionMessage('Sorry, but you do not currently have permissions to do that (Generate login user key)');

        // Simulate the web service server.
        $result = auth_userkey_external::request_login_url($params);
        $result = external_api::clean_returnvalue(auth_userkey_external::request_login_url_returns(), $result);
    }

    /**
     * Test request gets executed correctly if use has required permissions.
     */
    public function test_request_gets_executed_if_user_has_permission(): void {
        global $CFG, $DB;

        $this->setUser($this->user);
        $CFG->auth = "userkey";

        $context = context_system::instance();
        $studentrole = $DB->get_record('role', ['shortname' => 'student'], '*', MUST_EXIST);
        assign_capability('auth/userkey:generatekey', CAP_ALLOW, $studentrole->id, $context->id);
        role_assign($studentrole->id, $this->user->id, $context->id);

        $params = [
            'email' => 'exists@test.com',
        ];

        // Simulate the web service server.
        $result = auth_userkey_external::request_login_url($params);
        $result = external_api::clean_returnvalue(auth_userkey_external::request_login_url_returns(), $result);

        $actualkey = $DB->get_record('user_private_key', ['userid' => $this->user->id]);
        $expectedurl = $CFG->wwwroot . '/auth/userkey/login.php?key=' . $actualkey->value;

        $this->assertTrue(is_array($result));
        $this->assertTrue(key_exists('loginurl', $result));
        $this->assertEquals($expectedurl, $result['loginurl']);
    }
}
