Differences Between: [Versions 310 and 311] [Versions 310 and 400] [Versions 310 and 401] [Versions 310 and 402] [Versions 310 and 403] [Versions 39 and 310]
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/>. 16 17 /** 18 * Unit tests for the webservice component. 19 * 20 * @package core_webservice 21 * @category test 22 * @copyright 2016 Jun Pataleta <jun@moodle.com> 23 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 24 */ 25 defined('MOODLE_INTERNAL') || die(); 26 27 global $CFG; 28 require_once($CFG->dirroot . '/webservice/lib.php'); 29 30 /** 31 * Unit tests for the webservice component. 32 * 33 * @package core_webservice 34 * @category test 35 * @copyright 2016 Jun Pataleta <jun@moodle.com> 36 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 37 */ 38 class webservice_test extends advanced_testcase { 39 40 /** 41 * Setup. 42 */ 43 public function setUp(): void { 44 // Calling parent is good, always. 45 parent::setUp(); 46 47 // We always need enabled WS for this testcase. 48 set_config('enablewebservices', '1'); 49 } 50 51 /** 52 * Test init_service_class(). 53 */ 54 public function test_init_service_class() { 55 global $DB, $USER; 56 57 $this->resetAfterTest(true); 58 59 // Set current user. 60 $this->setAdminUser(); 61 62 // Add a web service. 63 $webservice = new stdClass(); 64 $webservice->name = 'Test web service'; 65 $webservice->enabled = true; 66 $webservice->restrictedusers = false; 67 $webservice->component = 'moodle'; 68 $webservice->timecreated = time(); 69 $webservice->downloadfiles = true; 70 $webservice->uploadfiles = true; 71 $externalserviceid = $DB->insert_record('external_services', $webservice); 72 73 // Add token. 74 $externaltoken = new stdClass(); 75 $externaltoken->token = 'testtoken'; 76 $externaltoken->tokentype = 0; 77 $externaltoken->userid = $USER->id; 78 $externaltoken->externalserviceid = $externalserviceid; 79 $externaltoken->contextid = 1; 80 $externaltoken->creatorid = $USER->id; 81 $externaltoken->timecreated = time(); 82 $DB->insert_record('external_tokens', $externaltoken); 83 84 // Add a function to the service. 85 $wsmethod = new stdClass(); 86 $wsmethod->externalserviceid = $externalserviceid; 87 $wsmethod->functionname = 'core_course_get_contents'; 88 $DB->insert_record('external_services_functions', $wsmethod); 89 90 // Initialise the dummy web service. 91 $dummy = new webservice_dummy(WEBSERVICE_AUTHMETHOD_PERMANENT_TOKEN); 92 // Set the token. 93 $dummy->set_token($externaltoken->token); 94 // Run the web service. 95 $dummy->run(); 96 // Get service methods and structs. 97 $servicemethods = $dummy->get_service_methods(); 98 $servicestructs = $dummy->get_service_structs(); 99 $this->assertNotEmpty($servicemethods); 100 // The function core_course_get_contents should be only the only web service function in the moment. 101 $this->assertEquals(1, count($servicemethods)); 102 // The function core_course_get_contents doesn't have a struct class, so the list of service structs should be empty. 103 $this->assertEmpty($servicestructs); 104 105 // Add other functions to the service. 106 // The function core_comment_get_comments has one struct class in its output. 107 $wsmethod->functionname = 'core_comment_get_comments'; 108 $DB->insert_record('external_services_functions', $wsmethod); 109 // The function core_grades_update_grades has one struct class in its input. 110 $wsmethod->functionname = 'core_grades_update_grades'; 111 $DB->insert_record('external_services_functions', $wsmethod); 112 113 // Run the web service again. 114 $dummy->run(); 115 // Get service methods and structs. 116 $servicemethods = $dummy->get_service_methods(); 117 $servicestructs = $dummy->get_service_structs(); 118 $this->assertEquals(3, count($servicemethods)); 119 $this->assertEquals(2, count($servicestructs)); 120 121 // Check the contents of service methods. 122 foreach ($servicemethods as $method) { 123 // Get the external function info. 124 $function = external_api::external_function_info($method->name); 125 126 // Check input params. 127 foreach ($function->parameters_desc->keys as $name => $keydesc) { 128 $this->check_params($method->inputparams[$name]['type'], $keydesc, $servicestructs); 129 } 130 131 // Check output params. 132 $this->check_params($method->outputparams['return']['type'], $function->returns_desc, $servicestructs); 133 134 // Check description. 135 $this->assertEquals($function->description, $method->description); 136 } 137 } 138 139 /** 140 * Tests update_token_lastaccess() function. 141 * 142 * @throws dml_exception 143 */ 144 public function test_update_token_lastaccess() { 145 global $DB; 146 147 $this->resetAfterTest(true); 148 149 // Set current user. 150 $this->setAdminUser(); 151 152 // Add a web service. 153 $webservice = new stdClass(); 154 $webservice->name = 'Test web service'; 155 $webservice->enabled = true; 156 $webservice->restrictedusers = false; 157 $webservice->component = 'moodle'; 158 $webservice->timecreated = time(); 159 $webservice->downloadfiles = true; 160 $webservice->uploadfiles = true; 161 $DB->insert_record('external_services', $webservice); 162 163 // Add token. 164 $tokenstr = external_create_service_token($webservice->name, context_system::instance()->id); 165 $token = $DB->get_record('external_tokens', ['token' => $tokenstr]); 166 167 // Trigger last access once (at current time). 168 webservice::update_token_lastaccess($token); 169 170 // Check last access. 171 $token = $DB->get_record('external_tokens', ['token' => $tokenstr]); 172 $this->assertLessThan(5, abs(time() - $token->lastaccess)); 173 174 // Try setting it to +1 second. This should not update yet. 175 $before = (int)$token->lastaccess; 176 webservice::update_token_lastaccess($token, $before + 1); 177 $token = $DB->get_record('external_tokens', ['token' => $tokenstr]); 178 $this->assertEquals($before, $token->lastaccess); 179 180 // To -1000 seconds. This should not update. 181 webservice::update_token_lastaccess($token, $before - 1000); 182 $token = $DB->get_record('external_tokens', ['token' => $tokenstr]); 183 $this->assertEquals($before, $token->lastaccess); 184 185 // To +59 seconds. This should also not quite update. 186 webservice::update_token_lastaccess($token, $before + 59); 187 $token = $DB->get_record('external_tokens', ['token' => $tokenstr]); 188 $this->assertEquals($before, $token->lastaccess); 189 190 // Finally to +60 seconds, where it should update. 191 webservice::update_token_lastaccess($token, $before + 60); 192 $token = $DB->get_record('external_tokens', ['token' => $tokenstr]); 193 $this->assertEquals($before + 60, $token->lastaccess); 194 } 195 196 /** 197 * Data provider for {@see test_get_active_tokens} 198 * 199 * @return array 200 */ 201 public function get_active_tokens_provider(): array { 202 return [ 203 'No expiration' => [0, true], 204 'Active' => [time() + DAYSECS, true], 205 'Expired' => [time() - DAYSECS, false], 206 ]; 207 } 208 209 /** 210 * Test getting active tokens for a user 211 * 212 * @param int $validuntil 213 * @param bool $expectedactive 214 * 215 * @dataProvider get_active_tokens_provider 216 */ 217 public function test_get_active_tokens(int $validuntil, bool $expectedactive): void { 218 global $DB; 219 220 $this->resetAfterTest(); 221 222 $user = $this->getDataGenerator()->create_user(); 223 224 $serviceid = $DB->get_field('external_services', 'id', ['shortname' => MOODLE_OFFICIAL_MOBILE_SERVICE], MUST_EXIST); 225 external_generate_token(EXTERNAL_TOKEN_PERMANENT, $serviceid, $user->id, context_system::instance(), $validuntil, ''); 226 227 $tokens = webservice::get_active_tokens($user->id); 228 if ($expectedactive) { 229 $this->assertCount(1, $tokens); 230 $this->assertEquals($serviceid, reset($tokens)->externalserviceid); 231 } else { 232 $this->assertEmpty($tokens); 233 } 234 } 235 236 /** 237 * Utility method that tests the parameter type of a method info's input/output parameter. 238 * 239 * @param string $type The parameter type that is being evaluated. 240 * @param mixed $methoddesc The method description of the WS function. 241 * @param array $servicestructs The list of generated service struct classes. 242 */ 243 private function check_params($type, $methoddesc, $servicestructs) { 244 if ($methoddesc instanceof external_value) { 245 // Test for simple types. 246 if (in_array($methoddesc->type, [PARAM_INT, PARAM_FLOAT, PARAM_BOOL])) { 247 $this->assertEquals($methoddesc->type, $type); 248 } else { 249 $this->assertEquals('string', $type); 250 } 251 } else if ($methoddesc instanceof external_single_structure) { 252 // Test that the class name of the struct class is in the array of service structs. 253 $structinfo = $this->get_struct_info($servicestructs, $type); 254 $this->assertNotNull($structinfo); 255 // Test that the properties of the struct info exist in the method description. 256 foreach ($structinfo->properties as $propname => $proptype) { 257 $this->assertTrue($this->in_keydesc($methoddesc, $propname)); 258 } 259 } else if ($methoddesc instanceof external_multiple_structure) { 260 // Test for array types. 261 $this->assertEquals('array', $type); 262 } 263 } 264 265 /** 266 * Gets the struct information from the list of struct classes based on the given struct class name. 267 * 268 * @param array $structarray The list of generated struct classes. 269 * @param string $structclass The name of the struct class. 270 * @return object|null The struct class info, or null if it's not found. 271 */ 272 private function get_struct_info($structarray, $structclass) { 273 foreach ($structarray as $struct) { 274 if ($struct->classname === $structclass) { 275 return $struct; 276 } 277 } 278 return null; 279 } 280 281 /** 282 * Searches the keys of the given external_single_structure object if it contains a certain property name. 283 * 284 * @param external_single_structure $keydesc 285 * @param string $propertyname The property name to be searched for. 286 * @return bool True if the property name is found in $keydesc. False, otherwise. 287 */ 288 private function in_keydesc(external_single_structure $keydesc, $propertyname) { 289 foreach ($keydesc->keys as $key => $desc) { 290 if ($key === $propertyname) { 291 return true; 292 } 293 } 294 return false; 295 } 296 } 297 298 /** 299 * Class webservice_dummy. 300 * 301 * Dummy webservice class for testing the webservice_base_server class and enable us to expose variables we want to test. 302 * 303 * @package core_webservice 304 * @category test 305 * @copyright 2016 Jun Pataleta <jun@moodle.com> 306 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 307 */ 308 class webservice_dummy extends webservice_base_server { 309 310 /** 311 * webservice_dummy constructor. 312 * 313 * @param int $authmethod The authentication method. 314 */ 315 public function __construct($authmethod) { 316 parent::__construct($authmethod); 317 318 // Arbitrarily naming this as REST in order not to have to register another WS protocol and set capabilities. 319 $this->wsname = 'rest'; 320 } 321 322 /** 323 * Token setter method. 324 * 325 * @param string $token The web service token. 326 */ 327 public function set_token($token) { 328 $this->token = $token; 329 } 330 331 /** 332 * This method parses the request input, it needs to get: 333 * 1/ user authentication - username+password or token 334 * 2/ function name 335 * 3/ function parameters 336 */ 337 protected function parse_request() { 338 // Just a method stub. No need to implement at the moment since it's not really being used for this test case for now. 339 } 340 341 /** 342 * Send the result of function call to the WS client. 343 */ 344 protected function send_response() { 345 // Just a method stub. No need to implement at the moment since it's not really being used for this test case for now. 346 } 347 348 /** 349 * Send the error information to the WS client. 350 * 351 * @param exception $ex 352 */ 353 protected function send_error($ex = null) { 354 // Just a method stub. No need to implement at the moment since it's not really being used for this test case for now. 355 } 356 357 /** 358 * run() method implementation. 359 */ 360 public function run() { 361 $this->authenticate_user(); 362 $this->init_service_class(); 363 } 364 365 /** 366 * Getter method of servicemethods array. 367 * 368 * @return array 369 */ 370 public function get_service_methods() { 371 return $this->servicemethods; 372 } 373 374 /** 375 * Getter method of servicestructs array. 376 * 377 * @return array 378 */ 379 public function get_service_structs() { 380 return $this->servicestructs; 381 } 382 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body