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