Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 4.3.x will end 7 October 2024 (12 months).
  • Bug fixes for security issues in 4.3.x will end 21 April 2025 (18 months).
  • PHP version: minimum PHP 8.0.0 Note: minimum PHP version has increased since Moodle 4.1. PHP 8.2.x is supported too.

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

   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   * Tests for core_message_inbound to test Variable Envelope Return Path functionality.
  19   *
  20   * @package    core_message
  21   * @copyright  2014 Andrew Nicols <andrew@nicols.co.uk>
  22   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  23   */
  24  
  25  namespace core_message;
  26  
  27  defined('MOODLE_INTERNAL') || die();
  28  require_once (__DIR__ . '/fixtures/inbound_fixtures.php');
  29  
  30  /**
  31   * Tests for core_message_inbound to test Variable Envelope Return Path functionality.
  32   *
  33   * @package    core_message
  34   * @copyright  2014 Andrew Nicols <andrew@nicols.co.uk>
  35   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  36   */
  37  class inbound_test extends \advanced_testcase {
  38  
  39      /**
  40       * Perform setup tasks generic to each test.
  41       * This includes:
  42       * * configuring the messageinbound_mailbox.
  43       */
  44      public function setUp(): void {
  45          global $CFG;
  46  
  47          $this->resetAfterTest(true);
  48  
  49          // Setup the default Inbound Message mailbox settings.
  50          $CFG->messageinbound_domain = 'example.com';
  51          $CFG->messageinbound_enabled = true;
  52  
  53          // Must be no longer than 15 characters.
  54          $CFG->messageinbound_mailbox = 'moodlemoodle123';
  55      }
  56  
  57      /**
  58       * Helper to create a new Inbound Message handler.
  59       *
  60       * @param $handlerclass The class of the handler to create
  61       * @param $enabled Whether the handler should be enabled
  62       * @param $component The component
  63       * @param $namepace The namepace
  64       */
  65      public function helper_create_handler($handlerclass, $enabled = true, $component = 'core_test', $namespace = '\\core\\test\\') {
  66          global $DB;
  67  
  68          $classname = $namespace . $handlerclass;
  69          $record = \core\message\inbound\manager::record_from_handler(new $classname());
  70          $record->component = $component;
  71          $record->enabled = $enabled;
  72          $record->id = $DB->insert_record('messageinbound_handlers', $record);
  73          $handler = core_message_inbound_test_manager::handler_from_record($record);
  74  
  75          return $handler;
  76      }
  77  
  78      /**
  79       * Test that the enabled check perform as expected.
  80       */
  81      public function test_is_enabled() {
  82          global $CFG;
  83  
  84          // First clear all of the settings set in the setUp.
  85          $CFG->messageinbound_domain = null;
  86          $CFG->messageinbound_enabled = null;
  87          $CFG->messageinbound_mailbox = null;
  88  
  89          $this->assertFalse(\core\message\inbound\manager::is_enabled());
  90  
  91          // Check whether only setting the enabled flag keeps it disabled.
  92          $CFG->messageinbound_enabled = true;
  93          $this->assertFalse(\core\message\inbound\manager::is_enabled());
  94  
  95          // Check that the mailbox entry on it's own does not enable Inbound Message handling.
  96          $CFG->messageinbound_mailbox = 'moodlemoodle123';
  97          $CFG->messageinbound_domain = null;
  98          $this->assertFalse(\core\message\inbound\manager::is_enabled());
  99  
 100          // And that the domain on it's own does not.
 101          $CFG->messageinbound_domain = 'example.com';
 102          $CFG->messageinbound_mailbox = null;
 103          $this->assertFalse(\core\message\inbound\manager::is_enabled());
 104  
 105          // And that an invalid mailbox does not.
 106          $CFG->messageinbound_mailbox = '';
 107          $CFG->messageinbound_domain = 'example.com';
 108          $this->assertFalse(\core\message\inbound\manager::is_enabled());
 109  
 110          // And that an invalid domain does not.
 111          $CFG->messageinbound_domain = '';
 112          $CFG->messageinbound_mailbox = 'moodlemoodle123';
 113          $this->assertFalse(\core\message\inbound\manager::is_enabled());
 114  
 115          // Finally a test that ensures that all settings correct enables the system.
 116          $CFG->messageinbound_mailbox = 'moodlemoodle123';
 117          $CFG->messageinbound_domain = 'example.com';
 118          $CFG->messageinbound_enabled = true;
 119  
 120          $this->assertTrue(\core\message\inbound\manager::is_enabled());
 121      }
 122  
 123      /**
 124       * Test that data items conform to RFCs 5231, and 5322 standards for
 125       * addressing, and to RFC 5233 for sub-addressing.
 126       */
 127      public function test_address_constraints() {
 128          $handler = $this->helper_create_handler('handler_one');
 129  
 130          // Using the handler created, generate an address for our data entry.
 131          $processor = new core_message_inbound_test_helper();
 132          $processor->set_handler($handler->classname);
 133  
 134          // Generate some IDs for the data and generate addresses for them.
 135          $dataids = array(
 136              -1,
 137              0,
 138              42,
 139              1073741823,
 140              2147483647,
 141          );
 142  
 143          $user = $this->getDataGenerator()->create_user();
 144          foreach ($dataids as $dataid) {
 145              $processor->set_data($dataid);
 146              $address = $processor->generate($user->id);
 147              $this->assertNotNull($address);
 148              $this->assertTrue(strlen($address) > 0, 'No address generated.');
 149              $this->assertTrue(strpos($address, '@') !== false, 'No domain found.');
 150              $this->assertTrue(strpos($address, '+') !== false, 'No subaddress found.');
 151  
 152              // The localpart must be less than 64 characters.
 153              list($localpart) = explode('@', $address);
 154              $this->assertTrue(strlen($localpart) <= 64, 'Localpart section of address too long');
 155  
 156              // And the data section should be no more than 48 characters.
 157              list(, $datasection) = explode('+', $localpart);
 158              $this->assertTrue(strlen($datasection) <= 48, 'Data section of address too long');
 159          }
 160      }
 161  
 162      /**
 163       * Test that the generated e-mail addresses are sufficiently random by
 164       * testing the multiple handlers, multiple users, and multiple data
 165       * items.
 166       */
 167      public function test_address_uniqueness() {
 168          // Generate a set of handlers. These are in two components, and each
 169          // component has two different generators.
 170          $handlers = array();
 171          $handlers[] = $this->helper_create_handler('handler_one', true, 'core_test');
 172          $handlers[] = $this->helper_create_handler('handler_two', true, 'core_test');
 173          $handlers[] = $this->helper_create_handler('handler_three', true, 'core_test_example');
 174          $handlers[] = $this->helper_create_handler('handler_four', true, 'core_test_example');
 175  
 176          // Generate some IDs for the data and generate addresses for them.
 177          $dataids = array(
 178              0,
 179              42,
 180              1073741823,
 181              2147483647,
 182          );
 183  
 184          $users = array();
 185          for ($i = 0; $i < 5; $i++) {
 186              $users[] = $this->getDataGenerator()->create_user();
 187          }
 188  
 189          // Store the addresses for later comparison.
 190          $addresses = array();
 191  
 192          foreach ($handlers as $handler) {
 193              $processor = new core_message_inbound_test_helper();
 194              $processor->set_handler($handler->classname);
 195  
 196              // Check each dataid.
 197              foreach ($dataids as $dataid) {
 198                  $processor->set_data($dataid);
 199  
 200                  // Check each user.
 201                  foreach ($users as $user) {
 202                      $address = $processor->generate($user->id);
 203                      $this->assertFalse(isset($addresses[$address]));
 204                      $addresses[$address] = true;
 205                  }
 206              }
 207          }
 208      }
 209  
 210      /**
 211       * Test address parsing of a generated address.
 212       */
 213      public function test_address_parsing() {
 214          $dataid = 42;
 215  
 216          // Generate a handler to use for this set of tests.
 217          $handler = $this->helper_create_handler('handler_one');
 218  
 219          // And a user.
 220          $user = $this->getDataGenerator()->create_user();
 221  
 222          // Using the handler created, generate an address for our data entry.
 223          $processor = new core_message_inbound_test_helper();
 224          $processor->set_handler($handler->classname);
 225          $processor->set_data($dataid);
 226          $address = $processor->generate($user->id);
 227  
 228          // We should be able to parse the address.
 229          $parser = new core_message_inbound_test_helper();
 230          $parser->process($address);
 231          $parsedresult = $parser->get_data();
 232          $this->assertEquals($user->id, $parsedresult->userid);
 233          $this->assertEquals($dataid, $parsedresult->datavalue);
 234          $this->assertEquals($dataid, $parsedresult->data->datavalue);
 235          $this->assertEquals($handler->id, $parsedresult->handlerid);
 236          $this->assertEquals($handler->id, $parsedresult->data->handler);
 237      }
 238  
 239      /**
 240       * Test address parsing of an address with an unrecognised format.
 241       */
 242      public function test_address_validation_invalid_format_failure() {
 243          // Create test data.
 244          $user = $this->getDataGenerator()->create_user();
 245          $handler = $this->helper_create_handler('handler_one');
 246          $dataid = 42;
 247  
 248          $parser = new core_message_inbound_test_helper();
 249  
 250          $generator = new core_message_inbound_test_helper();
 251          $generator->set_handler($handler->classname);
 252  
 253          // Check that validation fails when no address has been processed.
 254          $result = $parser->validate($user->email);
 255          $this->assertEquals(\core\message\inbound\address_manager::VALIDATION_INVALID_ADDRESS_FORMAT, $result);
 256  
 257          // Test that an address without data fails validation.
 258          $parser->process('bob@example.com');
 259          $result = $parser->validate($user->email);
 260          $this->assertEquals(\core\message\inbound\address_manager::VALIDATION_INVALID_ADDRESS_FORMAT, $result);
 261  
 262          // Test than address with a subaddress but invalid data fails with VALIDATION_UNKNOWN_DATAKEY.
 263          $parser->process('bob+nodata@example.com');
 264          $result = $parser->validate($user->email);
 265          $this->assertEquals(\core\message\inbound\address_manager::VALIDATION_INVALID_ADDRESS_FORMAT, $result);
 266      }
 267  
 268      /**
 269       * Test address parsing of an address with an unknown handler.
 270       */
 271      public function test_address_validation_unknown_handler() {
 272          global $DB;
 273  
 274          // Create test data.
 275          $user = $this->getDataGenerator()->create_user();
 276          $handler = $this->helper_create_handler('handler_one');
 277          $dataid = 42;
 278  
 279          $parser = new core_message_inbound_test_helper();
 280  
 281          $generator = new core_message_inbound_test_helper();
 282          $generator->set_handler($handler->classname);
 283          $generator->set_data($dataid);
 284          $address = $generator->generate($user->id);
 285  
 286          // Remove the handler record to invalidate it.
 287          $DB->delete_records('messageinbound_handlers', array(
 288              'id' => $handler->id,
 289          ));
 290  
 291          $parser->process($address);
 292          $result = $parser->validate($user->email);
 293          $expectedfail = \core\message\inbound\address_manager::VALIDATION_UNKNOWN_HANDLER;
 294          $this->assertEquals($expectedfail, $result & $expectedfail);
 295      }
 296  
 297      /**
 298       * Test address parsing of an address with a disabled handler.
 299       */
 300      public function test_address_validation_disabled_handler() {
 301          global $DB;
 302  
 303          // Create test data.
 304          $user = $this->getDataGenerator()->create_user();
 305          $handler = $this->helper_create_handler('handler_one');
 306          $dataid = 42;
 307  
 308          $parser = new core_message_inbound_test_helper();
 309  
 310          $generator = new core_message_inbound_test_helper();
 311          $generator->set_handler($handler->classname);
 312          $generator->set_data($dataid);
 313          $address = $generator->generate($user->id);
 314  
 315          // Disable the handler.
 316          $record = \core\message\inbound\manager::record_from_handler($handler);
 317          $record->enabled = false;
 318          $DB->update_record('messageinbound_handlers', $record);
 319  
 320          $parser->process($address);
 321          $result = $parser->validate($user->email);
 322          $expectedfail = \core\message\inbound\address_manager::VALIDATION_DISABLED_HANDLER;
 323          $this->assertEquals($expectedfail, $result & $expectedfail);
 324      }
 325  
 326      /**
 327       * Test address parsing of an address for an invalid user.
 328       */
 329      public function test_address_validation_invalid_user() {
 330          global $DB;
 331  
 332          // Create test data.
 333          $user = $this->getDataGenerator()->create_user();
 334          $handler = $this->helper_create_handler('handler_one');
 335          $dataid = 42;
 336  
 337          $parser = new core_message_inbound_test_helper();
 338  
 339          $generator = new core_message_inbound_test_helper();
 340          $generator->set_handler($handler->classname);
 341          $generator->set_data($dataid);
 342          $address = $generator->generate(-1);
 343  
 344          $parser->process($address);
 345          $result = $parser->validate($user->email);
 346          $expectedfail = \core\message\inbound\address_manager::VALIDATION_UNKNOWN_USER;
 347          $this->assertEquals($expectedfail, $result & $expectedfail);
 348      }
 349  
 350      /**
 351       * Test address parsing of an address for a disabled user.
 352       */
 353      public function test_address_validation_disabled_user() {
 354          global $DB;
 355  
 356          // Create test data.
 357          $user = $this->getDataGenerator()->create_user();
 358          $handler = $this->helper_create_handler('handler_one');
 359          $dataid = 42;
 360  
 361          $parser = new core_message_inbound_test_helper();
 362  
 363          $generator = new core_message_inbound_test_helper();
 364          $generator->set_handler($handler->classname);
 365          $generator->set_data($dataid);
 366          $address = $generator->generate($user->id);
 367  
 368          // Unconfirm the user.
 369          $user->confirmed = 0;
 370          $DB->update_record('user', $user);
 371  
 372          $parser->process($address);
 373          $result = $parser->validate($user->email);
 374          $expectedfail = \core\message\inbound\address_manager::VALIDATION_DISABLED_USER;
 375          $this->assertEquals($expectedfail, $result & $expectedfail);
 376      }
 377  
 378      /**
 379       * Test address parsing of an address for an invalid key.
 380       */
 381      public function test_address_validation_invalid_key() {
 382          global $DB;
 383  
 384          // Create test data.
 385          $user = $this->getDataGenerator()->create_user();
 386          $handler = $this->helper_create_handler('handler_one');
 387          $dataid = 42;
 388  
 389          $parser = new core_message_inbound_test_helper();
 390  
 391          $generator = new core_message_inbound_test_helper();
 392          $generator->set_handler($handler->classname);
 393          $generator->set_data($dataid);
 394          $address = $generator->generate($user->id);
 395  
 396          // Remove the data record to invalidate it.
 397          $DB->delete_records('messageinbound_datakeys', array(
 398              'handler' => $handler->id,
 399              'datavalue' => $dataid,
 400          ));
 401  
 402          $parser->process($address);
 403          $result = $parser->validate($user->email);
 404          $expectedfail = \core\message\inbound\address_manager::VALIDATION_UNKNOWN_DATAKEY;
 405          $this->assertEquals($expectedfail, $result & $expectedfail);
 406      }
 407  
 408      /**
 409       * Test address parsing of an address for an expired key.
 410       */
 411      public function test_address_validation_expired_key() {
 412          global $DB;
 413  
 414          // Create test data.
 415          $user = $this->getDataGenerator()->create_user();
 416          $handler = $this->helper_create_handler('handler_one');
 417          $dataid = 42;
 418  
 419          $parser = new core_message_inbound_test_helper();
 420  
 421          $generator = new core_message_inbound_test_helper();
 422          $generator->set_handler($handler->classname);
 423          $generator->set_data($dataid);
 424          $address = $generator->generate($user->id);
 425  
 426          // Expire the key by setting it's expiry time in the past.
 427          $key = $DB->get_record('messageinbound_datakeys', array(
 428              'handler' => $handler->id,
 429              'datavalue' => $dataid,
 430          ));
 431  
 432          $key->expires = time() - 3600;
 433          $DB->update_record('messageinbound_datakeys', $key);
 434  
 435          $parser->process($address);
 436          $result = $parser->validate($user->email);
 437          $expectedfail = \core\message\inbound\address_manager::VALIDATION_EXPIRED_DATAKEY;
 438          $this->assertEquals($expectedfail, $result & $expectedfail);
 439      }
 440  
 441      /**
 442       * Test address parsing of an address for an invalid hash.
 443       */
 444      public function test_address_validation_invalid_hash() {
 445          global $DB;
 446  
 447          // Create test data.
 448          $user = $this->getDataGenerator()->create_user();
 449          $handler = $this->helper_create_handler('handler_one');
 450          $dataid = 42;
 451  
 452          $parser = new core_message_inbound_test_helper();
 453  
 454          $generator = new core_message_inbound_test_helper();
 455          $generator->set_handler($handler->classname);
 456          $generator->set_data($dataid);
 457          $address = $generator->generate($user->id);
 458  
 459          // Expire the key by setting it's expiry time in the past.
 460          $key = $DB->get_record('messageinbound_datakeys', array(
 461              'handler' => $handler->id,
 462              'datavalue' => $dataid,
 463          ));
 464  
 465          $key->datakey = 'invalid value';
 466          $DB->update_record('messageinbound_datakeys', $key);
 467  
 468          $parser->process($address);
 469          $result = $parser->validate($user->email);
 470          $expectedfail = \core\message\inbound\address_manager::VALIDATION_INVALID_HASH;
 471          $this->assertEquals($expectedfail, $result & $expectedfail);
 472      }
 473  
 474      /**
 475       * Test address parsing of an address for an invalid sender.
 476       */
 477      public function test_address_validation_invalid_sender() {
 478          global $DB;
 479  
 480          // Create test data.
 481          $user = $this->getDataGenerator()->create_user();
 482          $handler = $this->helper_create_handler('handler_one');
 483          $dataid = 42;
 484  
 485          $parser = new core_message_inbound_test_helper();
 486  
 487          $generator = new core_message_inbound_test_helper();
 488          $generator->set_handler($handler->classname);
 489          $generator->set_data($dataid);
 490          $address = $generator->generate($user->id);
 491  
 492          $parser->process($address);
 493          $result = $parser->validate('incorrectuser@example.com');
 494          $expectedfail = \core\message\inbound\address_manager::VALIDATION_ADDRESS_MISMATCH;
 495          $this->assertEquals($expectedfail, $result & $expectedfail);
 496      }
 497  
 498      /**
 499       * Test address parsing of an address for an address which is correct.
 500       */
 501      public function test_address_validation_success() {
 502          global $DB;
 503  
 504          // Create test data.
 505          $user = $this->getDataGenerator()->create_user();
 506          $handler = $this->helper_create_handler('handler_one');
 507          $dataid = 42;
 508  
 509          $parser = new core_message_inbound_test_helper();
 510  
 511          $generator = new core_message_inbound_test_helper();
 512          $generator->set_handler($handler->classname);
 513          $generator->set_data($dataid);
 514          $address = $generator->generate($user->id);
 515  
 516          $parser->process($address);
 517          $result = $parser->validate($user->email);
 518          $this->assertEquals(\core\message\inbound\address_manager::VALIDATION_SUCCESS, $result);
 519  
 520      }
 521  
 522      /**
 523       * Test that a handler with no default expiration does not have an
 524       * expiration time applied.
 525       */
 526      public function test_default_hander_expiry_unlimited() {
 527          global $DB;
 528  
 529          // Set the default expiry of the handler to 0 - no expiration.
 530          $expiration = 0;
 531  
 532          // Create test data.
 533          $user = $this->getDataGenerator()->create_user();
 534          $handler = $this->helper_create_handler('handler_one');
 535  
 536          $record = \core\message\inbound\manager::record_from_handler($handler);
 537          $record->defaultexpiration = $expiration;
 538          $DB->update_record('messageinbound_handlers', $record);
 539  
 540          // Generate an address for the handler.
 541          $dataid = 42;
 542  
 543          $generator = new core_message_inbound_test_helper();
 544          $generator->set_handler($handler->classname);
 545          $generator->set_data($dataid);
 546          $address = $generator->generate($user->id);
 547  
 548          // Check that the datakey created matches the expirytime.
 549          $key = $DB->get_record('messageinbound_datakeys', array('handler' => $record->id, 'datavalue' => $dataid));
 550  
 551          $this->assertNull($key->expires);
 552      }
 553  
 554      /**
 555       * Test application of the default expiry on a handler.
 556       */
 557      public function test_default_hander_expiry_low() {
 558          global $DB;
 559  
 560          // Set the default expiry of the handler to 60 seconds.
 561          $expiration = 60;
 562  
 563          // Create test data.
 564          $user = $this->getDataGenerator()->create_user();
 565          $handler = $this->helper_create_handler('handler_one');
 566  
 567          $record = \core\message\inbound\manager::record_from_handler($handler);
 568          $record->defaultexpiration = $expiration;
 569          $DB->update_record('messageinbound_handlers', $record);
 570  
 571          // Generate an address for the handler.
 572          $dataid = 42;
 573  
 574          $generator = new core_message_inbound_test_helper();
 575          $generator->set_handler($handler->classname);
 576          $generator->set_data($dataid);
 577          $address = $generator->generate($user->id);
 578  
 579          // Check that the datakey created matches the expirytime.
 580          $key = $DB->get_record('messageinbound_datakeys', array('handler' => $record->id, 'datavalue' => $dataid));
 581  
 582          $this->assertEquals($key->timecreated + $expiration, $key->expires);
 583      }
 584  
 585      /**
 586       * Test application of the default expiry on a handler.
 587       */
 588      public function test_default_hander_expiry_medium() {
 589          global $DB;
 590  
 591          // Set the default expiry of the handler to 3600 seconds.
 592          $expiration = 3600;
 593  
 594          // Create test data.
 595          $user = $this->getDataGenerator()->create_user();
 596          $handler = $this->helper_create_handler('handler_one');
 597  
 598          $record = \core\message\inbound\manager::record_from_handler($handler);
 599          $record->defaultexpiration = $expiration;
 600          $DB->update_record('messageinbound_handlers', $record);
 601  
 602          // Generate an address for the handler.
 603          $dataid = 42;
 604  
 605          $generator = new core_message_inbound_test_helper();
 606          $generator->set_handler($handler->classname);
 607          $generator->set_data($dataid);
 608          $address = $generator->generate($user->id);
 609  
 610          // Check that the datakey created matches the expirytime.
 611          $key = $DB->get_record('messageinbound_datakeys', array('handler' => $record->id, 'datavalue' => $dataid));
 612  
 613          $this->assertEquals($key->timecreated + $expiration, $key->expires);
 614      }
 615  
 616  }
 617  
 618  /**
 619   * A helper function for unit testing to expose protected functions in the core_message_inbound API for testing.
 620   *
 621   * @copyright  2014 Andrew Nicols <andrew@nicols.co.uk>
 622   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 623   */
 624  class core_message_inbound_test_helper extends \core\message\inbound\address_manager {
 625      /**
 626       * The validate function.
 627       *
 628       * @param string $address
 629       * @return int
 630       */
 631      public function validate($address) {
 632          return parent::validate($address);
 633      }
 634  
 635      /**
 636       * The get_data function.
 637       *
 638       * @return stdClass
 639       */
 640      public function get_data() {
 641          return parent::get_data();
 642      }
 643  
 644      /**
 645       * The address processor function.
 646       *
 647       * @param string $address
 648       * @return void
 649       */
 650      public function process($address) {
 651          return parent::process($address);
 652      }
 653  }
 654  
 655  /**
 656   * A helper function for unit testing to expose protected functions in the core_message_inbound API for testing.
 657   *
 658   * @copyright  2014 Andrew Nicols <andrew@nicols.co.uk>
 659   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 660   */
 661  class core_message_inbound_test_manager extends \core\message\inbound\manager {
 662      /**
 663       * Helper to fetch make the handler_from_record public for unit testing.
 664       *
 665       * @param $record The handler record to fetch
 666       */
 667      public static function handler_from_record($record) {
 668          return parent::handler_from_record($record);
 669      }
 670  }