Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 3.11.x will end 14 Nov 2022 (12 months plus 6 months extension).
  • Bug fixes for security issues in 3.11.x will end 13 Nov 2023 (18 months plus 12 months extension).
  • PHP version: minimum PHP 7.3.0 Note: minimum PHP version has increased since Moodle 3.10. PHP 7.4.x is supported too.

Differences Between: [Versions 310 and 311] [Versions 39 and 311]

   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 XML-RPC web service.
  19   *
  20   * @package    webservice_xmlrpc
  21   * @category   test
  22   * @copyright  2015 Jun Pataleta <jun@moodle.com>
  23   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  24   */
  25  namespace webservice_xmlrpc;
  26  
  27  defined('MOODLE_INTERNAL') || die();
  28  
  29  global $CFG;
  30  require_once($CFG->dirroot . '/webservice/xmlrpc/lib.php');
  31  
  32  /**
  33   * Unit tests for the XML-RPC web service.
  34   *
  35   * @package    webservice_xmlrpc
  36   * @category   test
  37   * @copyright  2015 Jun Pataleta <jun@moodle.com>
  38   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  39   */
  40  class lib_test extends \advanced_testcase {
  41  
  42      /**
  43       * Setup.
  44       */
  45      public function setUp(): void {
  46          $this->resetAfterTest();
  47  
  48          // All tests require xmlrpc. Skip tests, if xmlrpc is not installed.
  49          if (!function_exists('xmlrpc_decode')) {
  50              $this->markTestSkipped('XMLRPC is not installed.');
  51          }
  52      }
  53  
  54      /**
  55       * Test for array response.
  56       */
  57      public function test_client_with_array_response() {
  58          global $CFG;
  59  
  60          $client = new webservice_xmlrpc_client_mock('/webservice/xmlrpc/server.php', 'anytoken');
  61          $mockresponse = file_get_contents($CFG->dirroot . '/webservice/xmlrpc/tests/fixtures/array_response.xml');
  62          $client->set_mock_response($mockresponse);
  63          $result = $client->call('testfunction');
  64          $this->assertEquals(xmlrpc_decode($mockresponse), $result);
  65      }
  66  
  67      /**
  68       * Test for value response.
  69       */
  70      public function test_client_with_value_response() {
  71          global $CFG;
  72  
  73          $client = new webservice_xmlrpc_client_mock('/webservice/xmlrpc/server.php', 'anytoken');
  74          $mockresponse = file_get_contents($CFG->dirroot . '/webservice/xmlrpc/tests/fixtures/value_response.xml');
  75          $client->set_mock_response($mockresponse);
  76          $result = $client->call('testfunction');
  77          $this->assertEquals(xmlrpc_decode($mockresponse), $result);
  78      }
  79  
  80      /**
  81       * Test for fault response.
  82       */
  83      public function test_client_with_fault_response() {
  84          global $CFG;
  85  
  86          $client = new webservice_xmlrpc_client_mock('/webservice/xmlrpc/server.php', 'anytoken');
  87          $mockresponse = file_get_contents($CFG->dirroot . '/webservice/xmlrpc/tests/fixtures/fault_response.xml');
  88          $client->set_mock_response($mockresponse);
  89          $this->expectException('\moodle_exception');
  90          $client->call('testfunction');
  91      }
  92  
  93      /**
  94       * Test the XML-RPC request encoding.
  95       */
  96      public function test_encode_request() {
  97  
  98          $client = new webservice_xmlrpc_client_mock('/webservice/xmlrpc/server.php', 'anytoken');
  99  
 100          // Encode the request with the proper encoding and escaping options.
 101          $xml = $client->encode_request('do_it', ['foo' => '<bar>ŠČŘŽÝÁÍÉ</bar>']);
 102  
 103          // Assert that decoding with explicit encoding will work. This appeared
 104          // to fail if the markup escaping was not set.
 105          $this->assertEquals(['<bar>ŠČŘŽÝÁÍÉ</bar>'], xmlrpc_decode($xml, 'UTF-8'));
 106  
 107          // Decoding also works with our wrapper method.
 108          $this->assertEquals(['<bar>ŠČŘŽÝÁÍÉ</bar>'], $client->decode_response($xml));
 109  
 110          // Our experiments show that even with default/implicit encoding,
 111          // requests encoded with markup escaping set are also decoded
 112          // correctly. This is known to be used in some servers so we test it
 113          // here, too.
 114          // However, this does not work for all strings, see next test.
 115          $this->assertEquals(['<bar>ŠČŘŽÝÁÍÉ</bar>'], xmlrpc_decode($xml));
 116      }
 117  
 118      /**
 119       * Test the XML-RPC response decoding
 120       */
 121      public function test_decode_response() {
 122          $client = new webservice_xmlrpc_client_mock('/webservice/xmlrpc/server.php', 'anytoken');
 123  
 124          $teststring = '<bar>Recherche thématique:Villes & Développement durable</bar>';
 125  
 126          // Encode the string with the proper encoding and escaping options. Assert that decoding will work.
 127          $xml = $client->encode_request('do_it', [$teststring]);
 128          $this->assertEquals([$teststring], $client->decode_response($xml));
 129          // For this particular string bare decoding function does not work.
 130          // It can't really be explained why it works for the string 'ŠČŘŽÝÁÍÉ' in the previous test but not this one.
 131          // Symbol é comes as chr(233) . It looks like '<bar>Recherche th�matique:Villes & D�veloppement durable</bar>'.
 132          $this->assertEquals([preg_replace('/é/', chr(233), $teststring)], xmlrpc_decode($xml));
 133  
 134          // Encode the string without any options (default encoding "iso-8859-1" is used). Assert that decoding will work.
 135          $xml = xmlrpc_encode_request('do_it', [$teststring]);
 136          $this->assertEquals([$teststring], $client->decode_response($xml));
 137          $this->assertEquals([$teststring], xmlrpc_decode($xml));
 138  
 139          // Another example of the string where bare xmlrpc_decode() does not work but our wrapper does.
 140          $teststring = 'Formación Docente';
 141  
 142          $xml = $client->encode_request('do_it', [$teststring]);
 143          $this->assertEquals([$teststring], $client->decode_response($xml));
 144          // Bare decoding function xmlrpc_decode() does not work.
 145          // Symbol ó comes as chr(243), it looks like 'Formaci�n Docente'.
 146          $this->assertEquals([preg_replace('/ó/', chr(243), $teststring)], xmlrpc_decode($xml));
 147      }
 148  }
 149  
 150  /**
 151   * Class webservice_xmlrpc_client_mock.
 152   *
 153   * Mock class that returns the processed XML-RPC response.
 154   *
 155   * @package    webservice_xmlrpc
 156   * @category   test
 157   * @copyright  2015 Jun Pataleta <jun@moodle.com>
 158   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 159   */
 160  class webservice_xmlrpc_client_mock extends \webservice_xmlrpc_client {
 161  
 162      /** @var string The mock XML-RPC response string.  */
 163      private $mockresponse;
 164  
 165      /**
 166       * XML-RPC mock response setter.
 167       *
 168       * @param string $mockresponse
 169       */
 170      public function set_mock_response($mockresponse) {
 171          $this->mockresponse = $mockresponse;
 172      }
 173  
 174      /**
 175       * Since the call method uses download_file_content and it is hard to make an actual call to a web service,
 176       * we'll just have to simulate the receipt of the response from the server using the mock response so we
 177       * can test the processing result of this method.
 178       *
 179       * @param string $functionname the function name
 180       * @param array $params the parameters of the function
 181       * @return mixed The decoded XML RPC response.
 182       * @throws \moodle_exception
 183       */
 184      public function call($functionname, $params = array()) {
 185          // Get the response.
 186          $response = $this->mockresponse;
 187  
 188          // This is the part of the code in webservice_xmlrpc_client::call() what we would like to test.
 189          // Decode the response.
 190          $result = xmlrpc_decode($response);
 191          if (is_array($result) && xmlrpc_is_fault($result)) {
 192              throw new \moodle_exception($result['faultString']);
 193          }
 194  
 195          return $result;
 196      }
 197  
 198      /**
 199       * Allows to test the request encoding.
 200       *
 201       * @param string $functionname Name of the method to call.
 202       * @param mixed $params Method parameters compatible with the method signature.
 203       * @return string
 204       */
 205      public function encode_request($functionname, $params) {
 206          return parent::encode_request($functionname, $params);
 207      }
 208  
 209      /**
 210       * Allows to test the response decoding.
 211       *
 212       * @param string $response
 213       * @return array
 214       */
 215      public function decode_response($response) {
 216          return parent::decode_response($response);
 217      }
 218  }