Autoría | Ultima modificación | Ver Log |
<?php// This file is part of the Zoom plugin for 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 get_meeting_reports task class.** @package mod_zoom* @copyright 2019 UC Regents* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later*/namespace mod_zoom;use advanced_testcase;/*** PHPunit testcase class.* @covers \mod_zoom\webservice*/final class mod_zoom_webservice_test extends advanced_testcase {/*** @var object Anonymous class to mock \curl.*/private $notfoundmockcurl;/*** Setup to ensure that fixtures are loaded.*/public static function setUpBeforeClass(): void {global $CFG;require_once($CFG->dirroot . '/mod/zoom/locallib.php');}/*** Setup before every test.*/public function setUp(): void {$this->resetAfterTest();// Set fake values so we can test methods in class.set_config('clientid', 'test', 'zoom');set_config('clientsecret', 'test', 'zoom');set_config('accountid', 'test', 'zoom');// @codingStandardsIgnoreStart$this->notfoundmockcurl = new class {/*** Stub for curl setHeader().* @param string $unusedparam* @return void*/public function setHeader($unusedparam) {// @codingStandardsIgnoreEndreturn;}/*** Stub for curl get_errno().* @return boolean*/public function get_errno() {return false;}/*** Returns 404 error code.* @return array*/public function get_info() {return ['http_code' => 404];}};}/*** Tests that uuid are encoded properly for use in web service calls.*/public function test_encode_uuid(): void {$service = zoom_webservice();// If uuid includes / or // it needs to be double encoded.$uuid = $service->encode_uuid('/u2F0gUNSqqC7DT+08xKrw==');$this->assertEquals('%252Fu2F0gUNSqqC7DT%252B08xKrw%253D%253D', $uuid);$uuid = $service->encode_uuid('Ahqu+zVcQpO//RcAUUWkNA==');$this->assertEquals('Ahqu%252BzVcQpO%252F%252FRcAUUWkNA%253D%253D', $uuid);// If not, then it can be used as is.$uuid = $service->encode_uuid('M8TigfzxRTKJmhXnV7bNjw==');$this->assertEquals('M8TigfzxRTKJmhXnV7bNjw==', $uuid);}/*** Tests whether the meeting not found errors are properly parsed.*/public function test_meeting_not_found_exception(): void {$mockservice = $this->getMockBuilder('\mod_zoom\webservice')->setMethods(['make_curl_call', 'get_curl_object', 'get_access_token'])->getMock();$mockservice->expects($this->any())->method('make_curl_call')->willReturn('{"code":3001,"message":"réunion introuvable"}');$mockservice->expects($this->any())->method('get_curl_object')->willReturn($this->notfoundmockcurl);$mockservice->expects($this->any())->method('get_access_token')->willReturn('token123');$foundexception = false;try {$response = $mockservice->get_meeting_webinar_info('-1', false);} catch (webservice_exception $error) {$this->assertEquals(3001, $error->zoomerrorcode);$this->assertTrue(zoom_is_meeting_gone_error($error));$foundexception = true;}$this->assertTrue($foundexception);}/*** Tests whether user not found errors are properly parsed.*/public function test_user_not_found_exception(): void {$mockservice = $this->getMockBuilder('\mod_zoom\webservice')->setMethods(['make_curl_call', 'get_curl_object', 'get_access_token'])->getMock();$mockservice->expects($this->any())->method('make_curl_call')->willReturn('{"code":1001,"message":"n’existe pas ou n’appartient pas à ce compte"}');$mockservice->expects($this->any())->method('get_curl_object')->willReturn($this->notfoundmockcurl);$mockservice->expects($this->any())->method('get_access_token')->willReturn('token123');$foundexception = false;try {$founduser = $mockservice->get_user('-1');} catch (webservice_exception $error) {$this->assertEquals(1001, $error->zoomerrorcode);$this->assertTrue(zoom_is_meeting_gone_error($error));$this->assertTrue(zoom_is_user_not_found_error($error));$foundexception = true;}$this->assertTrue($foundexception || !$founduser);}/*** Tests whether invalid user errors are parsed properly*/public function test_invalid_user_exception(): void {// @codingStandardsIgnoreStart$invalidmockcurl = new class {/*** Stub for curl setHeader().* @param string $unusedparam* @return void*/public function setHeader($unusedparam) {// @codingStandardsIgnoreEndreturn;}/*** Stub for curl get_errno().* @return boolean*/public function get_errno() {return false;}/*** Returns 400 error code.* @return array*/public function get_info() {return ['http_code' => 400];}};$mockservice = $this->getMockBuilder('\mod_zoom\webservice')->setMethods(['make_curl_call', 'get_curl_object', 'get_access_token'])->getMock();$mockservice->expects($this->any())->method('make_curl_call')->willReturn('{"code":1120,"message":"utilisateur invalide"}');$mockservice->expects($this->any())->method('get_curl_object')->willReturn($invalidmockcurl);$mockservice->expects($this->any())->method('get_access_token')->willReturn('token123');$foundexception = false;try {$founduser = $mockservice->get_user('-1');} catch (webservice_exception $error) {$this->assertEquals(1120, $error->zoomerrorcode);$this->assertTrue(zoom_is_meeting_gone_error($error));$this->assertTrue(zoom_is_user_not_found_error($error));$foundexception = true;}$this->assertTrue($foundexception || !$founduser);}/*** Tests whether the retry on a 429 works properly when the Retry-After header* is in the curl response to specify the time that the retry should be sent.*/public function test_retry_with_header(): void {// @codingStandardsIgnoreStart$retrywithheadermockcurl = new class {public $numgetinfocalls = 0;/*** Stub for curl setHeader().* @param string $unusedparam* @return void*/public function setHeader($unusedparam) {// @codingStandardsIgnoreEndreturn;}/*** Stub for curl get_errno().* @return boolean*/public function get_errno() {return false;}/*** Returns 429 for first 3 calls, then 200.* @return array*/public function get_info() {$this->numgetinfocalls++;if ($this->numgetinfocalls <= 3) {return ['http_code' => 429];}return ['http_code' => 200];}// @codingStandardsIgnoreStart/*** Returns retry to be 1 second later.* @return array*/public function getResponse() {// @codingStandardsIgnoreEnd// Set retry time to be 1 second. Format is 2020-05-31T00:00:00Z.$retrytime = time() + 1;return ['X-RateLimit-Type' => 'Daily','X-RateLimit-Remaining' => 100,'Retry-After' => gmdate('Y-m-d\TH:i:s\Z', $retrytime),];}};$mockservice = $this->getMockBuilder('\mod_zoom\webservice')->setMethods(['make_curl_call', 'get_curl_object', 'get_access_token'])->getMock();$mockservice->expects($this->any())->method('make_curl_call')->willReturn('{"response":"success", "message": "", "code": 200}');// Class retrywithheadermockcurl will give 429 retry error 3 times// before giving a 200.$mockservice->expects($this->any())->method('get_curl_object')->willReturn($retrywithheadermockcurl);$mockservice->expects($this->any())->method('get_access_token')->willReturn('token123');$result = $mockservice->get_user("1");// Expect 3 debugging calls for each retry attempt.$this->assertDebuggingCalledCount($expectedcount = 3);// Expect 3 calls to get_info() for the retries and 1 for success.$this->assertEquals($retrywithheadermockcurl->numgetinfocalls, 4);$this->assertEquals($result->response, 'success');}/*** Tests whether the retry on a 429 response works when the Retry-After* header is not sent in the curl response.*/public function test_retry_without_header(): void {// @codingStandardsIgnoreStart$retrynoheadermockcurl = new class {public $numgetinfocalls = 0;/*** Stub for curl setHeader().* @param string $unusedparam* @return void*/public function setHeader($unusedparam) {// @codingStandardsIgnoreEndreturn;}/*** Stub for curl get_errno().* @return boolean*/public function get_errno() {return false;}/*** Returns 429 for first 3 calls, then 200.* @return array*/public function get_info() {$this->numgetinfocalls++;if ($this->numgetinfocalls <= 3) {return ['http_code' => 429];}return ['http_code' => 200];}// @codingStandardsIgnoreStart/*** Returns empty response.* @return array*/public function getResponse() {// @codingStandardsIgnoreEndreturn [];}};$mockservice = $this->getMockBuilder('\mod_zoom\webservice')->setMethods(['make_curl_call', 'get_curl_object', 'get_access_token'])->getMock();$mockservice->expects($this->any())->method('make_curl_call')->willReturn('{"response":"success"}');$mockservice->expects($this->any())->method('get_curl_object')->willReturn($retrynoheadermockcurl);$mockservice->expects($this->any())->method('get_access_token')->willReturn('token123');$result = $mockservice->get_user("1");$this->assertDebuggingCalledCount($expectedcount = 3);$this->assertEquals($retrynoheadermockcurl->numgetinfocalls, 4);$this->assertEquals($result->response, 'success');}/*** Tests that we throw error if we tried more than max retries.*/public function test_retry_exception(): void {// @codingStandardsIgnoreStart$retryfailuremockcurl = new class {public $urlpath = null;/*** Stub for curl setHeader().* @param string $unusedparam* @return void*/public function setHeader($unusedparam) {// @codingStandardsIgnoreendreturn;}/*** Stub for curl get_errno().* @return boolean*/public function get_errno() {return false;}/*** Returns 429.* @return array*/public function get_info() {return array('http_code' => 429);}/*** Returns error code and message.* @param string $url* @param array $data* @return string*/public function get($url, $data) {if ($this->urlpath === null) {$this->urlpath = $url;} else if ($this->urlpath !== $url) {// We should be getting the same path every time.return '{"code":-1, "message":"incorrect url"}';}return '{"code":-1, "message":"too many retries"}';}// @codingStandardsIgnoreStart/*** Returns retry to be 1 second later.* @return array*/public function getResponse() {// @codingStandardsIgnoreEnd// Set retry time after 1 second. Format is 2020-05-31T00:00:00Z.$retrytime = time() + 1;return ['X-RateLimit-Type' => 'Daily','X-RateLimit-Remaining' => 100,'Retry-After' => gmdate('Y-m-d\TH:i:s\Z', $retrytime),];}};$mockservice = $this->getMockBuilder('\mod_zoom\webservice')->setMethods(['get_curl_object', 'get_access_token'])->getMock();$mockservice->expects($this->any())->method('get_curl_object')->willReturn($retryfailuremockcurl);$mockservice->expects($this->any())->method('get_access_token')->willReturn('token123');$foundexception = false;try {$result = $mockservice->get_user("1");} catch (retry_failed_exception $error) {$foundexception = true;$this->assertEquals($error->response, 'too many retries');}$this->assertTrue($foundexception);// Check that we retried MAX_RETRIES times.$this->assertDebuggingCalledCount(webservice::MAX_RETRIES);}/*** Tests that we are waiting 1 minute for QPS rate limit types.*/public function test_retryqps_exception(): void {// @codingStandardsIgnoreStart$retryqpsmockcurl = new class {public $urlpath = null;/*** Stub for curl setHeader().* @param string $unusedparam* @return void*/public function setHeader($unusedparam) {// @codingStandardsIgnoreEndreturn;}/*** Stub for curl get_errno().* @return boolean*/public function get_errno() {return false;}/*** Returns 429.* @return array*/public function get_info() {return ['http_code' => 429];}/*** Returns error code and message.* @param string $url* @param array $data* @return string*/public function get($url, $data) {if ($this->urlpath === null) {$this->urlpath = $url;} else if ($this->urlpath !== $url) {// We should be getting the same path every time.return '{"code":-1, "message":"incorrect url"}';}return '{"code":-1, "message":"too many retries"}';}// @codingStandardsIgnoreStart/*** Returns retry to be 1 second later.* @return array*/public function getResponse() {// @codingStandardsIgnoreEnd// Signify that we reached max per second/minute rate limit.return ['X-RateLimit-Type' => 'QPS'];}};$mockservice = $this->getMockBuilder('\mod_zoom\webservice')->setMethods(['get_curl_object', 'get_access_token'])->getMock();$mockservice->expects($this->any())->method('get_curl_object')->willReturn($retryqpsmockcurl);$mockservice->expects($this->any())->method('get_access_token')->willReturn('token123');$foundexception = false;try {$result = $mockservice->get_meetings('2020-01-01', '2020-01-02');} catch (webservice_exception $error) {$foundexception = true;$this->assertEquals($error->response, 'too many retries');}$this->assertTrue($foundexception);// Check that we waited 1 minute.$debugging = $this->getDebuggingMessages();$debuggingmsg = array_pop($debugging);$this->assertEquals('Received 429 response, sleeping 60 seconds ' .'until next retry. Current retry: 5', $debuggingmsg->message);// Check that we retried MAX_RETRIES times.$this->assertDebuggingCalledCount(webservice::MAX_RETRIES);}}