Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 3.10.x will end 8 November 2021 (12 months).
  • Bug fixes for security issues in 3.10.x will end 9 May 2022 (18 months).
  • PHP version: minimum PHP 7.2.0 Note: minimum PHP version has increased since Moodle 3.8. PHP 7.3.x and 7.4.x are supported too.

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