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   * Unit tests for (some of) mod/feedback/lib.php.
  18   *
  19   * @package    mod_feedback
  20   * @copyright  2016 Stephen Bourget
  21   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  22   */
  23  namespace mod_feedback;
  24  
  25  use mod_feedback_completion;
  26  
  27  defined('MOODLE_INTERNAL') || die();
  28  global $CFG;
  29  require_once($CFG->dirroot . '/mod/feedback/lib.php');
  30  
  31  /**
  32   * Unit tests for (some of) mod/feedback/lib.php.
  33   *
  34   * @copyright  2016 Stephen Bourget
  35   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  36   */
  37  class lib_test extends \advanced_testcase {
  38  
  39      public function test_feedback_initialise() {
  40          $this->resetAfterTest();
  41          $this->setAdminUser();
  42  
  43          $course = $this->getDataGenerator()->create_course();
  44          $params['course'] = $course->id;
  45          $params['timeopen'] = time() - 5 * MINSECS;
  46          $params['timeclose'] = time() + DAYSECS;
  47          $params['anonymous'] = 1;
  48          $params['intro'] = 'Some introduction text';
  49          $feedback = $this->getDataGenerator()->create_module('feedback', $params);
  50  
  51          // Test different ways to construct the structure object.
  52          $pseudocm = get_coursemodule_from_instance('feedback', $feedback->id); // Object similar to cm_info.
  53          $cm = get_fast_modinfo($course)->instances['feedback'][$feedback->id]; // Instance of cm_info.
  54  
  55          $constructorparams = [
  56              [$feedback, null],
  57              [null, $pseudocm],
  58              [null, $cm],
  59              [$feedback, $pseudocm],
  60              [$feedback, $cm],
  61          ];
  62  
  63          foreach ($constructorparams as $params) {
  64              $structure = new mod_feedback_completion($params[0], $params[1], 0);
  65              $this->assertTrue($structure->is_open());
  66              $this->assertTrue($structure->get_cm() instanceof \cm_info);
  67              $this->assertEquals($feedback->cmid, $structure->get_cm()->id);
  68              $this->assertEquals($feedback->intro, $structure->get_feedback()->intro);
  69          }
  70      }
  71  
  72      /**
  73       * Tests for mod_feedback_refresh_events.
  74       */
  75      public function test_feedback_refresh_events() {
  76          global $DB;
  77          $this->resetAfterTest();
  78          $this->setAdminUser();
  79  
  80          $timeopen = time();
  81          $timeclose = time() + 86400;
  82  
  83          $course = $this->getDataGenerator()->create_course();
  84          $generator = $this->getDataGenerator()->get_plugin_generator('mod_feedback');
  85          $params['course'] = $course->id;
  86          $params['timeopen'] = $timeopen;
  87          $params['timeclose'] = $timeclose;
  88          $feedback = $generator->create_instance($params);
  89          $cm = get_coursemodule_from_instance('feedback', $feedback->id);
  90          $context = \context_module::instance($cm->id);
  91  
  92          // Normal case, with existing course.
  93          $this->assertTrue(feedback_refresh_events($course->id));
  94          $eventparams = array('modulename' => 'feedback', 'instance' => $feedback->id, 'eventtype' => 'open');
  95          $openevent = $DB->get_record('event', $eventparams, '*', MUST_EXIST);
  96          $this->assertEquals($openevent->timestart, $timeopen);
  97  
  98          $eventparams = array('modulename' => 'feedback', 'instance' => $feedback->id, 'eventtype' => 'close');
  99          $closeevent = $DB->get_record('event', $eventparams, '*', MUST_EXIST);
 100          $this->assertEquals($closeevent->timestart, $timeclose);
 101          // In case the course ID is passed as a numeric string.
 102          $this->assertTrue(feedback_refresh_events('' . $course->id));
 103          // Course ID not provided.
 104          $this->assertTrue(feedback_refresh_events());
 105          $eventparams = array('modulename' => 'feedback');
 106          $events = $DB->get_records('event', $eventparams);
 107          foreach ($events as $event) {
 108              if ($event->modulename === 'feedback' && $event->instance === $feedback->id && $event->eventtype === 'open') {
 109                  $this->assertEquals($event->timestart, $timeopen);
 110              }
 111              if ($event->modulename === 'feedback' && $event->instance === $feedback->id && $event->eventtype === 'close') {
 112                  $this->assertEquals($event->timestart, $timeclose);
 113              }
 114          }
 115      }
 116  
 117      /**
 118       * Test check_updates_since callback.
 119       */
 120      public function test_check_updates_since() {
 121          global $DB;
 122  
 123          $this->resetAfterTest();
 124          $this->setAdminUser();
 125          $course = $this->getDataGenerator()->create_course();
 126  
 127          // Create user.
 128          $student = self::getDataGenerator()->create_user();
 129  
 130          // User enrolment.
 131          $studentrole = $DB->get_record('role', array('shortname' => 'student'));
 132          $this->getDataGenerator()->enrol_user($student->id, $course->id, $studentrole->id, 'manual');
 133  
 134          $this->setCurrentTimeStart();
 135          $record = array(
 136              'course' => $course->id,
 137              'custom' => 0,
 138              'feedback' => 1,
 139          );
 140          $feedback = $this->getDataGenerator()->create_module('feedback', $record);
 141          $cm = get_coursemodule_from_instance('feedback', $feedback->id, $course->id);
 142          $cm = \cm_info::create($cm);
 143  
 144          $this->setUser($student);
 145          // Check that upon creation, the updates are only about the new configuration created.
 146          $onehourago = time() - HOURSECS;
 147          $updates = feedback_check_updates_since($cm, $onehourago);
 148          foreach ($updates as $el => $val) {
 149              if ($el == 'configuration') {
 150                  $this->assertTrue($val->updated);
 151                  $this->assertTimeCurrent($val->timeupdated);
 152              } else {
 153                  $this->assertFalse($val->updated);
 154              }
 155          }
 156  
 157          $record = [
 158              'feedback' => $feedback->id,
 159              'userid' => $student->id,
 160              'timemodified' => time(),
 161              'random_response' => 0,
 162              'anonymous_response' => FEEDBACK_ANONYMOUS_NO,
 163              'courseid' => $course->id,
 164          ];
 165          $DB->insert_record('feedback_completed', (object)$record);
 166          $DB->insert_record('feedback_completedtmp', (object)$record);
 167  
 168          // Check now for finished and unfinished attempts.
 169          $updates = feedback_check_updates_since($cm, $onehourago);
 170          $this->assertTrue($updates->attemptsunfinished->updated);
 171          $this->assertCount(1, $updates->attemptsunfinished->itemids);
 172  
 173          $this->assertTrue($updates->attemptsfinished->updated);
 174          $this->assertCount(1, $updates->attemptsfinished->itemids);
 175      }
 176  
 177      /**
 178       * Test calendar event provide action open.
 179       */
 180      public function test_feedback_core_calendar_provide_event_action_open() {
 181          $this->resetAfterTest();
 182          $this->setAdminUser();
 183  
 184          $now = time();
 185          $course = $this->getDataGenerator()->create_course();
 186          $feedback = $this->getDataGenerator()->create_module('feedback', ['course' => $course->id,
 187                  'timeopen' => $now - DAYSECS, 'timeclose' => $now + DAYSECS]);
 188          $event = $this->create_action_event($course->id, $feedback->id, FEEDBACK_EVENT_TYPE_OPEN);
 189  
 190          $factory = new \core_calendar\action_factory();
 191          $actionevent = mod_feedback_core_calendar_provide_event_action($event, $factory);
 192  
 193          $this->assertInstanceOf('\core_calendar\local\event\value_objects\action', $actionevent);
 194          $this->assertEquals(get_string('answerquestions', 'feedback'), $actionevent->get_name());
 195          $this->assertInstanceOf('moodle_url', $actionevent->get_url());
 196          $this->assertEquals(1, $actionevent->get_item_count());
 197          $this->assertTrue($actionevent->is_actionable());
 198      }
 199  
 200      /**
 201       * Test calendar event provide action open, viewed by a different user.
 202       */
 203      public function test_feedback_core_calendar_provide_event_action_open_for_user() {
 204          global $DB;
 205  
 206          $this->resetAfterTest();
 207          $this->setAdminUser();
 208  
 209          $now = time();
 210          $user = $this->getDataGenerator()->create_user();
 211          $user2 = $this->getDataGenerator()->create_user();
 212          $studentrole = $DB->get_record('role', array('shortname' => 'student'));
 213          $course = $this->getDataGenerator()->create_course();
 214          $this->getDataGenerator()->enrol_user($user->id, $course->id, $studentrole->id, 'manual');
 215  
 216          $feedback = $this->getDataGenerator()->create_module('feedback', ['course' => $course->id,
 217              'timeopen' => $now - DAYSECS, 'timeclose' => $now + DAYSECS]);
 218          $event = $this->create_action_event($course->id, $feedback->id, FEEDBACK_EVENT_TYPE_OPEN);
 219          $factory = new \core_calendar\action_factory();
 220  
 221          $this->setUser($user2);
 222  
 223          // User2 checking their events.
 224          $actionevent = mod_feedback_core_calendar_provide_event_action($event, $factory, $user2->id);
 225          $this->assertNull($actionevent);
 226  
 227          // User2 checking $user's events.
 228          $actionevent = mod_feedback_core_calendar_provide_event_action($event, $factory, $user->id);
 229          $this->assertInstanceOf('\core_calendar\local\event\value_objects\action', $actionevent);
 230          $this->assertEquals(get_string('answerquestions', 'feedback'), $actionevent->get_name());
 231          $this->assertInstanceOf('moodle_url', $actionevent->get_url());
 232          $this->assertEquals(1, $actionevent->get_item_count());
 233          $this->assertTrue($actionevent->is_actionable());
 234      }
 235  
 236      /**
 237       * Test calendar event provide action closed.
 238       */
 239      public function test_feedback_core_calendar_provide_event_action_closed() {
 240          $this->resetAfterTest();
 241          $this->setAdminUser();
 242  
 243          $course = $this->getDataGenerator()->create_course();
 244          $feedback = $this->getDataGenerator()->create_module('feedback', array('course' => $course->id,
 245                  'timeclose' => time() - DAYSECS));
 246          $event = $this->create_action_event($course->id, $feedback->id, FEEDBACK_EVENT_TYPE_OPEN);
 247  
 248          $factory = new \core_calendar\action_factory();
 249          $actionevent = mod_feedback_core_calendar_provide_event_action($event, $factory);
 250  
 251          // No event on the dashboard if feedback is closed.
 252          $this->assertNull($actionevent);
 253      }
 254  
 255      /**
 256       * Test calendar event provide action closed, viewed by a different user.
 257       */
 258      public function test_feedback_core_calendar_provide_event_action_closed_for_user() {
 259          global $DB;
 260  
 261          $this->resetAfterTest();
 262          $this->setAdminUser();
 263  
 264          $course = $this->getDataGenerator()->create_course();
 265          $user = $this->getDataGenerator()->create_user();
 266          $user2 = $this->getDataGenerator()->create_user();
 267          $studentrole = $DB->get_record('role', array('shortname' => 'student'));
 268          $this->getDataGenerator()->enrol_user($user->id, $course->id, $studentrole->id, 'manual');
 269  
 270          $feedback = $this->getDataGenerator()->create_module('feedback', array('course' => $course->id,
 271              'timeclose' => time() - DAYSECS));
 272          $event = $this->create_action_event($course->id, $feedback->id, FEEDBACK_EVENT_TYPE_OPEN);
 273          $factory = new \core_calendar\action_factory();
 274          $this->setUser($user2);
 275  
 276          // User2 checking their events.
 277          $actionevent = mod_feedback_core_calendar_provide_event_action($event, $factory, $user2->id);
 278          $this->assertNull($actionevent);
 279  
 280          // User2 checking $user's events.
 281          $actionevent = mod_feedback_core_calendar_provide_event_action($event, $factory, $user->id);
 282  
 283          // No event on the dashboard if feedback is closed.
 284          $this->assertNull($actionevent);
 285      }
 286  
 287      /**
 288       * Test calendar event action open in future.
 289       *
 290       * @throws coding_exception
 291       */
 292      public function test_feedback_core_calendar_provide_event_action_open_in_future() {
 293          $this->resetAfterTest();
 294          $this->setAdminUser();
 295  
 296          $course = $this->getDataGenerator()->create_course();
 297          $feedback = $this->getDataGenerator()->create_module('feedback', ['course' => $course->id,
 298                  'timeopen' => time() + DAYSECS]);
 299          $event = $this->create_action_event($course->id, $feedback->id, FEEDBACK_EVENT_TYPE_OPEN);
 300  
 301          $factory = new \core_calendar\action_factory();
 302          $actionevent = mod_feedback_core_calendar_provide_event_action($event, $factory);
 303  
 304          $this->assertInstanceOf('\core_calendar\local\event\value_objects\action', $actionevent);
 305          $this->assertEquals(get_string('answerquestions', 'feedback'), $actionevent->get_name());
 306          $this->assertInstanceOf('moodle_url', $actionevent->get_url());
 307          $this->assertEquals(1, $actionevent->get_item_count());
 308          $this->assertFalse($actionevent->is_actionable());
 309      }
 310  
 311      /**
 312       * Test calendar event action open in future, viewed by a different user.
 313       *
 314       * @throws coding_exception
 315       */
 316      public function test_feedback_core_calendar_provide_event_action_open_in_future_for_user() {
 317          global $DB;
 318  
 319          $this->resetAfterTest();
 320          $this->setAdminUser();
 321  
 322          $course = $this->getDataGenerator()->create_course();
 323          $user = $this->getDataGenerator()->create_user();
 324          $user2 = $this->getDataGenerator()->create_user();
 325          $studentrole = $DB->get_record('role', array('shortname' => 'student'));
 326          $this->getDataGenerator()->enrol_user($user->id, $course->id, $studentrole->id, 'manual');
 327  
 328          $feedback = $this->getDataGenerator()->create_module('feedback', ['course' => $course->id,
 329              'timeopen' => time() + DAYSECS]);
 330          $event = $this->create_action_event($course->id, $feedback->id, FEEDBACK_EVENT_TYPE_OPEN);
 331  
 332          $factory = new \core_calendar\action_factory();
 333          $this->setUser($user2);
 334  
 335          // User2 checking their events.
 336          $actionevent = mod_feedback_core_calendar_provide_event_action($event, $factory, $user2->id);
 337          $this->assertNull($actionevent);
 338  
 339          // User2 checking $user's events.
 340          $actionevent = mod_feedback_core_calendar_provide_event_action($event, $factory, $user->id);
 341  
 342          $this->assertInstanceOf('\core_calendar\local\event\value_objects\action', $actionevent);
 343          $this->assertEquals(get_string('answerquestions', 'feedback'), $actionevent->get_name());
 344          $this->assertInstanceOf('moodle_url', $actionevent->get_url());
 345          $this->assertEquals(1, $actionevent->get_item_count());
 346          $this->assertFalse($actionevent->is_actionable());
 347      }
 348  
 349      /**
 350       * Test calendar event with no time specified.
 351       *
 352       * @throws coding_exception
 353       */
 354      public function test_feedback_core_calendar_provide_event_action_no_time_specified() {
 355          $this->resetAfterTest();
 356          $this->setAdminUser();
 357  
 358          $course = $this->getDataGenerator()->create_course();
 359          $feedback = $this->getDataGenerator()->create_module('feedback', ['course' => $course->id]);
 360          $event = $this->create_action_event($course->id, $feedback->id, FEEDBACK_EVENT_TYPE_OPEN);
 361  
 362          $factory = new \core_calendar\action_factory();
 363          $actionevent = mod_feedback_core_calendar_provide_event_action($event, $factory);
 364  
 365          $this->assertInstanceOf('\core_calendar\local\event\value_objects\action', $actionevent);
 366          $this->assertEquals(get_string('answerquestions', 'feedback'), $actionevent->get_name());
 367          $this->assertInstanceOf('moodle_url', $actionevent->get_url());
 368          $this->assertEquals(1, $actionevent->get_item_count());
 369          $this->assertTrue($actionevent->is_actionable());
 370      }
 371  
 372      /**
 373       * Test calendar event with no time specified, viewed by a different user.
 374       *
 375       * @throws coding_exception
 376       */
 377      public function test_feedback_core_calendar_provide_event_action_no_time_specified_for_user() {
 378          global $DB;
 379  
 380          $this->resetAfterTest();
 381          $this->setAdminUser();
 382  
 383          $course = $this->getDataGenerator()->create_course();
 384          $user = $this->getDataGenerator()->create_user();
 385          $user2 = $this->getDataGenerator()->create_user();
 386          $studentrole = $DB->get_record('role', array('shortname' => 'student'));
 387          $this->getDataGenerator()->enrol_user($user->id, $course->id, $studentrole->id, 'manual');
 388  
 389          $feedback = $this->getDataGenerator()->create_module('feedback', ['course' => $course->id]);
 390          $event = $this->create_action_event($course->id, $feedback->id, FEEDBACK_EVENT_TYPE_OPEN);
 391  
 392          $factory = new \core_calendar\action_factory();
 393          $this->setUser($user2);
 394  
 395          // User2 checking their events.
 396          $actionevent = mod_feedback_core_calendar_provide_event_action($event, $factory, $user2->id);
 397          $this->assertNull($actionevent);
 398  
 399          // User2 checking $user's events.
 400          $actionevent = mod_feedback_core_calendar_provide_event_action($event, $factory, $user->id);
 401  
 402          $this->assertInstanceOf('\core_calendar\local\event\value_objects\action', $actionevent);
 403          $this->assertEquals(get_string('answerquestions', 'feedback'), $actionevent->get_name());
 404          $this->assertInstanceOf('moodle_url', $actionevent->get_url());
 405          $this->assertEquals(1, $actionevent->get_item_count());
 406          $this->assertTrue($actionevent->is_actionable());
 407      }
 408  
 409      /**
 410       * A user that can not submit feedback should not have an action.
 411       */
 412      public function test_feedback_core_calendar_provide_event_action_can_not_submit() {
 413          global $DB;
 414  
 415          $this->resetAfterTest();
 416          $this->setAdminUser();
 417  
 418          $user = $this->getDataGenerator()->create_user();
 419          $studentrole = $DB->get_record('role', array('shortname' => 'student'));
 420          $course = $this->getDataGenerator()->create_course();
 421          $feedback = $this->getDataGenerator()->create_module('feedback', ['course' => $course->id]);
 422          $event = $this->create_action_event($course->id, $feedback->id, FEEDBACK_EVENT_TYPE_OPEN);
 423          $cm = get_coursemodule_from_instance('feedback', $feedback->id);
 424          $context = \context_module::instance($cm->id);
 425          $this->getDataGenerator()->enrol_user($user->id, $course->id, $studentrole->id, 'manual');
 426  
 427          $this->setUser($user);
 428          assign_capability('mod/feedback:complete', CAP_PROHIBIT, $studentrole->id, $context);
 429  
 430          $factory = new \core_calendar\action_factory();
 431          $action = mod_feedback_core_calendar_provide_event_action($event, $factory);
 432  
 433          $this->assertNull($action);
 434      }
 435  
 436      /**
 437       * A user that can not submit feedback should not have an action, viewed by a different user.
 438       */
 439      public function test_feedback_core_calendar_provide_event_action_can_not_submit_for_user() {
 440          global $DB;
 441  
 442          $this->resetAfterTest();
 443          $this->setAdminUser();
 444  
 445          $user = $this->getDataGenerator()->create_user();
 446          $user2 = $this->getDataGenerator()->create_user();
 447          $studentrole = $DB->get_record('role', array('shortname' => 'student'));
 448          $course = $this->getDataGenerator()->create_course();
 449          $feedback = $this->getDataGenerator()->create_module('feedback', ['course' => $course->id]);
 450          $event = $this->create_action_event($course->id, $feedback->id, FEEDBACK_EVENT_TYPE_OPEN);
 451          $cm = get_coursemodule_from_instance('feedback', $feedback->id);
 452          $context = \context_module::instance($cm->id);
 453          $this->getDataGenerator()->enrol_user($user->id, $course->id, $studentrole->id, 'manual');
 454  
 455          assign_capability('mod/feedback:complete', CAP_PROHIBIT, $studentrole->id, $context);
 456          $factory = new \core_calendar\action_factory();
 457          $this->setUser($user2);
 458  
 459          // User2 checking their events.
 460  
 461          $actionevent = mod_feedback_core_calendar_provide_event_action($event, $factory, $user2->id);
 462          $this->assertNull($actionevent);
 463  
 464          // User2 checking $user's events.
 465          $actionevent = mod_feedback_core_calendar_provide_event_action($event, $factory, $user->id);
 466  
 467          $this->assertNull($actionevent);
 468      }
 469  
 470      /**
 471       * A user that has already submitted feedback should not have an action.
 472       */
 473      public function test_feedback_core_calendar_provide_event_action_already_submitted() {
 474          global $DB;
 475  
 476          $this->resetAfterTest();
 477          $this->setAdminUser();
 478  
 479          $user = $this->getDataGenerator()->create_user();
 480          $studentrole = $DB->get_record('role', array('shortname' => 'student'));
 481          $course = $this->getDataGenerator()->create_course();
 482          $feedback = $this->getDataGenerator()->create_module('feedback', ['course' => $course->id]);
 483          $event = $this->create_action_event($course->id, $feedback->id, FEEDBACK_EVENT_TYPE_OPEN);
 484          $cm = get_coursemodule_from_instance('feedback', $feedback->id);
 485          $context = \context_module::instance($cm->id);
 486  
 487          $this->setUser($user);
 488  
 489          $record = [
 490              'feedback' => $feedback->id,
 491              'userid' => $user->id,
 492              'timemodified' => time(),
 493              'random_response' => 0,
 494              'anonymous_response' => FEEDBACK_ANONYMOUS_NO,
 495              'courseid' => 0,
 496          ];
 497          $DB->insert_record('feedback_completed', (object) $record);
 498  
 499          $factory = new \core_calendar\action_factory();
 500          $action = mod_feedback_core_calendar_provide_event_action($event, $factory);
 501  
 502          $this->assertNull($action);
 503      }
 504  
 505      /**
 506       * A user that has already submitted feedback should not have an action, viewed by a different user.
 507       */
 508      public function test_feedback_core_calendar_provide_event_action_already_submitted_for_user() {
 509          global $DB;
 510  
 511          $this->resetAfterTest();
 512          $this->setAdminUser();
 513  
 514          $user = $this->getDataGenerator()->create_user();
 515          $user2 = $this->getDataGenerator()->create_user();
 516          $studentrole = $DB->get_record('role', array('shortname' => 'student'));
 517          $course = $this->getDataGenerator()->create_course();
 518          $feedback = $this->getDataGenerator()->create_module('feedback', ['course' => $course->id]);
 519          $event = $this->create_action_event($course->id, $feedback->id, FEEDBACK_EVENT_TYPE_OPEN);
 520          $cm = get_coursemodule_from_instance('feedback', $feedback->id);
 521          $context = \context_module::instance($cm->id);
 522  
 523          $this->setUser($user);
 524  
 525          $record = [
 526              'feedback' => $feedback->id,
 527              'userid' => $user->id,
 528              'timemodified' => time(),
 529              'random_response' => 0,
 530              'anonymous_response' => FEEDBACK_ANONYMOUS_NO,
 531              'courseid' => 0,
 532          ];
 533          $DB->insert_record('feedback_completed', (object) $record);
 534  
 535          $factory = new \core_calendar\action_factory();
 536          $this->setUser($user2);
 537  
 538          // User2 checking their events.
 539          $actionevent = mod_feedback_core_calendar_provide_event_action($event, $factory, $user2->id);
 540          $this->assertNull($actionevent);
 541  
 542          // User2 checking $user's events.
 543          $actionevent = mod_feedback_core_calendar_provide_event_action($event, $factory, $user->id);
 544  
 545          $this->assertNull($actionevent);
 546      }
 547  
 548      public function test_feedback_core_calendar_provide_event_action_already_completed() {
 549          $this->resetAfterTest();
 550          set_config('enablecompletion', 1);
 551          $this->setAdminUser();
 552  
 553          // Create the activity.
 554          $course = $this->getDataGenerator()->create_course(array('enablecompletion' => 1));
 555          $feedback = $this->getDataGenerator()->create_module('feedback', array('course' => $course->id),
 556              array('completion' => 2, 'completionview' => 1, 'completionexpected' => time() + DAYSECS));
 557  
 558          // Get some additional data.
 559          $cm = get_coursemodule_from_instance('feedback', $feedback->id);
 560  
 561          // Create a calendar event.
 562          $event = $this->create_action_event($course->id, $feedback->id,
 563              \core_completion\api::COMPLETION_EVENT_TYPE_DATE_COMPLETION_EXPECTED);
 564  
 565          // Mark the activity as completed.
 566          $completion = new \completion_info($course);
 567          $completion->set_module_viewed($cm);
 568  
 569          // Create an action factory.
 570          $factory = new \core_calendar\action_factory();
 571  
 572          // Decorate action event.
 573          $actionevent = mod_feedback_core_calendar_provide_event_action($event, $factory);
 574  
 575          // Ensure result was null.
 576          $this->assertNull($actionevent);
 577      }
 578  
 579      public function test_feedback_core_calendar_provide_event_action_already_completed_for_user() {
 580          $this->resetAfterTest();
 581          set_config('enablecompletion', 1);
 582          $this->setAdminUser();
 583  
 584          // Create the activity.
 585          $course = $this->getDataGenerator()->create_course(array('enablecompletion' => 1));
 586          $feedback = $this->getDataGenerator()->create_module('feedback', array('course' => $course->id),
 587              array('completion' => 2, 'completionview' => 1, 'completionexpected' => time() + DAYSECS));
 588  
 589          // Enrol a student in the course.
 590          $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
 591  
 592          // Get some additional data.
 593          $cm = get_coursemodule_from_instance('feedback', $feedback->id);
 594  
 595          // Create a calendar event.
 596          $event = $this->create_action_event($course->id, $feedback->id,
 597              \core_completion\api::COMPLETION_EVENT_TYPE_DATE_COMPLETION_EXPECTED);
 598  
 599          // Mark the activity as completed for the student.
 600          $completion = new \completion_info($course);
 601          $completion->set_module_viewed($cm, $student->id);
 602  
 603          // Create an action factory.
 604          $factory = new \core_calendar\action_factory();
 605  
 606          // Decorate action event for the student.
 607          $actionevent = mod_feedback_core_calendar_provide_event_action($event, $factory, $student->id);
 608  
 609          // Ensure result was null.
 610          $this->assertNull($actionevent);
 611      }
 612  
 613      /**
 614       * Creates an action event.
 615       *
 616       * @param int $courseid The course id.
 617       * @param int $instanceid The feedback id.
 618       * @param string $eventtype The event type. eg. FEEDBACK_EVENT_TYPE_OPEN.
 619       * @return bool|calendar_event
 620       */
 621      private function create_action_event($courseid, $instanceid, $eventtype) {
 622          $event = new \stdClass();
 623          $event->name = 'Calendar event';
 624          $event->modulename = 'feedback';
 625          $event->courseid = $courseid;
 626          $event->instance = $instanceid;
 627          $event->type = CALENDAR_EVENT_TYPE_ACTION;
 628          $event->eventtype = $eventtype;
 629          $event->timestart = time();
 630  
 631          return \calendar_event::create($event);
 632      }
 633  
 634      /**
 635       * Test the callback responsible for returning the completion rule descriptions.
 636       * This function should work given either an instance of the module (cm_info), such as when checking the active rules,
 637       * or if passed a stdClass of similar structure, such as when checking the the default completion settings for a mod type.
 638       */
 639      public function test_mod_feedback_completion_get_active_rule_descriptions() {
 640          $this->resetAfterTest();
 641          $this->setAdminUser();
 642  
 643          // Two activities, both with automatic completion. One has the 'completionsubmit' rule, one doesn't.
 644          $course = $this->getDataGenerator()->create_course(['enablecompletion' => 2]);
 645          $feedback1 = $this->getDataGenerator()->create_module('feedback', [
 646              'course' => $course->id,
 647              'completion' => 2,
 648              'completionsubmit' => 1
 649          ]);
 650          $feedback2 = $this->getDataGenerator()->create_module('feedback', [
 651              'course' => $course->id,
 652              'completion' => 2,
 653              'completionsubmit' => 0
 654          ]);
 655          $cm1 = \cm_info::create(get_coursemodule_from_instance('feedback', $feedback1->id));
 656          $cm2 = \cm_info::create(get_coursemodule_from_instance('feedback', $feedback2->id));
 657  
 658          // Data for the stdClass input type.
 659          // This type of input would occur when checking the default completion rules for an activity type, where we don't have
 660          // any access to cm_info, rather the input is a stdClass containing completion and customdata attributes, just like cm_info.
 661          $moddefaults = new \stdClass();
 662          $moddefaults->customdata = ['customcompletionrules' => ['completionsubmit' => 1]];
 663          $moddefaults->completion = 2;
 664  
 665          $activeruledescriptions = [get_string('completionsubmit', 'feedback')];
 666          $this->assertEquals(mod_feedback_get_completion_active_rule_descriptions($cm1), $activeruledescriptions);
 667          $this->assertEquals(mod_feedback_get_completion_active_rule_descriptions($cm2), []);
 668          $this->assertEquals(mod_feedback_get_completion_active_rule_descriptions($moddefaults), $activeruledescriptions);
 669          $this->assertEquals(mod_feedback_get_completion_active_rule_descriptions(new \stdClass()), []);
 670      }
 671  
 672      /**
 673       * An unknown event should not have min or max restrictions.
 674       */
 675      public function test_get_valid_event_timestart_range_unknown_event() {
 676          global $CFG, $DB;
 677          require_once($CFG->dirroot . "/calendar/lib.php");
 678  
 679          $this->resetAfterTest(true);
 680          $this->setAdminUser();
 681          $generator = $this->getDataGenerator();
 682          $course = $generator->create_course();
 683          $feedbackgenerator = $generator->get_plugin_generator('mod_feedback');
 684          $timeopen = time();
 685          $timeclose = $timeopen + DAYSECS;
 686          $feedback = $feedbackgenerator->create_instance(['course' => $course->id]);
 687          $feedback->timeopen = $timeopen;
 688          $feedback->timeclose = $timeclose;
 689          $DB->update_record('feedback', $feedback);
 690  
 691          $event = new \calendar_event([
 692              'name' => 'Test event',
 693              'description' => '',
 694              'format' => 1,
 695              'courseid' => $course->id,
 696              'groupid' => 0,
 697              'userid' => 2,
 698              'modulename' => 'feedback',
 699              'instance' => $feedback->id,
 700              'eventtype' => 'SOME UNKNOWN EVENT',
 701              'timestart' => $timeopen,
 702              'timeduration' => 86400,
 703              'visible' => 1
 704          ]);
 705  
 706          list($min, $max) = mod_feedback_core_calendar_get_valid_event_timestart_range($event, $feedback);
 707          $this->assertNull($min);
 708          $this->assertNull($max);
 709      }
 710  
 711      /**
 712       * A FEEDBACK_EVENT_TYPE_OPEN should have a max timestart equal to the activity
 713       * close time.
 714       */
 715      public function test_get_valid_event_timestart_range_event_type_open() {
 716          global $CFG, $DB;
 717          require_once($CFG->dirroot . "/calendar/lib.php");
 718  
 719          $this->resetAfterTest(true);
 720          $this->setAdminUser();
 721          $generator = $this->getDataGenerator();
 722          $course = $generator->create_course();
 723          $feedbackgenerator = $generator->get_plugin_generator('mod_feedback');
 724          $timeopen = time();
 725          $timeclose = $timeopen + DAYSECS;
 726          $feedback = $feedbackgenerator->create_instance(['course' => $course->id]);
 727          $feedback->timeopen = $timeopen;
 728          $feedback->timeclose = $timeclose;
 729          $DB->update_record('feedback', $feedback);
 730  
 731          $event = new \calendar_event([
 732              'name' => 'Test event',
 733              'description' => '',
 734              'format' => 1,
 735              'courseid' => $course->id,
 736              'groupid' => 0,
 737              'userid' => 2,
 738              'modulename' => 'feedback',
 739              'instance' => $feedback->id,
 740              'eventtype' => FEEDBACK_EVENT_TYPE_OPEN,
 741              'timestart' => $timeopen,
 742              'timeduration' => 86400,
 743              'visible' => 1
 744          ]);
 745  
 746          list($min, $max) = mod_feedback_core_calendar_get_valid_event_timestart_range($event, $feedback);
 747          $this->assertNull($min);
 748          $this->assertEquals($timeclose, $max[0]);
 749          $this->assertNotEmpty($max[1]);
 750      }
 751  
 752      /**
 753       * A FEEDBACK_EVENT_TYPE_OPEN should not have a max timestamp if the activity
 754       * doesn't have a close date.
 755       */
 756      public function test_get_valid_event_timestart_range_event_type_open_no_close() {
 757          global $CFG, $DB;
 758          require_once($CFG->dirroot . "/calendar/lib.php");
 759  
 760          $this->resetAfterTest(true);
 761          $this->setAdminUser();
 762          $generator = $this->getDataGenerator();
 763          $course = $generator->create_course();
 764          $feedbackgenerator = $generator->get_plugin_generator('mod_feedback');
 765          $timeopen = time();
 766          $timeclose = $timeopen + DAYSECS;
 767          $feedback = $feedbackgenerator->create_instance(['course' => $course->id]);
 768          $feedback->timeopen = $timeopen;
 769          $feedback->timeclose = 0;
 770          $DB->update_record('feedback', $feedback);
 771  
 772          $event = new \calendar_event([
 773              'name' => 'Test event',
 774              'description' => '',
 775              'format' => 1,
 776              'courseid' => $course->id,
 777              'groupid' => 0,
 778              'userid' => 2,
 779              'modulename' => 'feedback',
 780              'instance' => $feedback->id,
 781              'eventtype' => FEEDBACK_EVENT_TYPE_OPEN,
 782              'timestart' => $timeopen,
 783              'timeduration' => 86400,
 784              'visible' => 1
 785          ]);
 786  
 787          list($min, $max) = mod_feedback_core_calendar_get_valid_event_timestart_range($event, $feedback);
 788          $this->assertNull($min);
 789          $this->assertNull($max);
 790      }
 791  
 792      /**
 793       * A FEEDBACK_EVENT_TYPE_CLOSE should have a min timestart equal to the activity
 794       * open time.
 795       */
 796      public function test_get_valid_event_timestart_range_event_type_close() {
 797          global $CFG, $DB;
 798          require_once($CFG->dirroot . "/calendar/lib.php");
 799  
 800          $this->resetAfterTest(true);
 801          $this->setAdminUser();
 802          $generator = $this->getDataGenerator();
 803          $course = $generator->create_course();
 804          $feedbackgenerator = $generator->get_plugin_generator('mod_feedback');
 805          $timeopen = time();
 806          $timeclose = $timeopen + DAYSECS;
 807          $feedback = $feedbackgenerator->create_instance(['course' => $course->id]);
 808          $feedback->timeopen = $timeopen;
 809          $feedback->timeclose = $timeclose;
 810          $DB->update_record('feedback', $feedback);
 811  
 812          $event = new \calendar_event([
 813              'name' => 'Test event',
 814              'description' => '',
 815              'format' => 1,
 816              'courseid' => $course->id,
 817              'groupid' => 0,
 818              'userid' => 2,
 819              'modulename' => 'feedback',
 820              'instance' => $feedback->id,
 821              'eventtype' => FEEDBACK_EVENT_TYPE_CLOSE,
 822              'timestart' => $timeopen,
 823              'timeduration' => 86400,
 824              'visible' => 1
 825          ]);
 826  
 827          list($min, $max) = mod_feedback_core_calendar_get_valid_event_timestart_range($event, $feedback);
 828          $this->assertEquals($timeopen, $min[0]);
 829          $this->assertNotEmpty($min[1]);
 830          $this->assertNull($max);
 831      }
 832  
 833      /**
 834       * A FEEDBACK_EVENT_TYPE_CLOSE should not have a minimum timestamp if the activity
 835       * doesn't have an open date.
 836       */
 837      public function test_get_valid_event_timestart_range_event_type_close_no_open() {
 838          global $CFG, $DB;
 839          require_once($CFG->dirroot . "/calendar/lib.php");
 840  
 841          $this->resetAfterTest(true);
 842          $this->setAdminUser();
 843          $generator = $this->getDataGenerator();
 844          $course = $generator->create_course();
 845          $feedbackgenerator = $generator->get_plugin_generator('mod_feedback');
 846          $timeopen = time();
 847          $timeclose = $timeopen + DAYSECS;
 848          $feedback = $feedbackgenerator->create_instance(['course' => $course->id]);
 849          $feedback->timeopen = 0;
 850          $feedback->timeclose = $timeclose;
 851          $DB->update_record('feedback', $feedback);
 852  
 853          $event = new \calendar_event([
 854              'name' => 'Test event',
 855              'description' => '',
 856              'format' => 1,
 857              'courseid' => $course->id,
 858              'groupid' => 0,
 859              'userid' => 2,
 860              'modulename' => 'feedback',
 861              'instance' => $feedback->id,
 862              'eventtype' => FEEDBACK_EVENT_TYPE_CLOSE,
 863              'timestart' => $timeopen,
 864              'timeduration' => 86400,
 865              'visible' => 1
 866          ]);
 867  
 868          list($min, $max) = mod_feedback_core_calendar_get_valid_event_timestart_range($event, $feedback);
 869          $this->assertNull($min);
 870          $this->assertNull($max);
 871      }
 872  
 873      /**
 874       * An unkown event type should not change the feedback instance.
 875       */
 876      public function test_mod_feedback_core_calendar_event_timestart_updated_unknown_event() {
 877          global $CFG, $DB;
 878          require_once($CFG->dirroot . "/calendar/lib.php");
 879  
 880          $this->resetAfterTest(true);
 881          $this->setAdminUser();
 882          $generator = $this->getDataGenerator();
 883          $course = $generator->create_course();
 884          $feedbackgenerator = $generator->get_plugin_generator('mod_feedback');
 885          $timeopen = time();
 886          $timeclose = $timeopen + DAYSECS;
 887          $feedback = $feedbackgenerator->create_instance(['course' => $course->id]);
 888          $feedback->timeopen = $timeopen;
 889          $feedback->timeclose = $timeclose;
 890          $DB->update_record('feedback', $feedback);
 891  
 892          // Create a valid event.
 893          $event = new \calendar_event([
 894              'name' => 'Test event',
 895              'description' => '',
 896              'format' => 1,
 897              'courseid' => $course->id,
 898              'groupid' => 0,
 899              'userid' => 2,
 900              'modulename' => 'feedback',
 901              'instance' => $feedback->id,
 902              'eventtype' => FEEDBACK_EVENT_TYPE_OPEN . "SOMETHING ELSE",
 903              'timestart' => 1,
 904              'timeduration' => 86400,
 905              'visible' => 1
 906          ]);
 907  
 908          mod_feedback_core_calendar_event_timestart_updated($event, $feedback);
 909  
 910          $feedback = $DB->get_record('feedback', ['id' => $feedback->id]);
 911          $this->assertEquals($timeopen, $feedback->timeopen);
 912          $this->assertEquals($timeclose, $feedback->timeclose);
 913      }
 914  
 915      /**
 916       * A FEEDBACK_EVENT_TYPE_OPEN event should update the timeopen property of
 917       * the feedback activity.
 918       */
 919      public function test_mod_feedback_core_calendar_event_timestart_updated_open_event() {
 920          global $CFG, $DB;
 921          require_once($CFG->dirroot . "/calendar/lib.php");
 922  
 923          $this->resetAfterTest(true);
 924          $this->setAdminUser();
 925          $generator = $this->getDataGenerator();
 926          $course = $generator->create_course();
 927          $feedbackgenerator = $generator->get_plugin_generator('mod_feedback');
 928          $timeopen = time();
 929          $timeclose = $timeopen + DAYSECS;
 930          $timemodified = 1;
 931          $newtimeopen = $timeopen - DAYSECS;
 932          $feedback = $feedbackgenerator->create_instance(['course' => $course->id]);
 933          $feedback->timeopen = $timeopen;
 934          $feedback->timeclose = $timeclose;
 935          $feedback->timemodified = $timemodified;
 936          $DB->update_record('feedback', $feedback);
 937  
 938          // Create a valid event.
 939          $event = new \calendar_event([
 940              'name' => 'Test event',
 941              'description' => '',
 942              'format' => 1,
 943              'courseid' => $course->id,
 944              'groupid' => 0,
 945              'userid' => 2,
 946              'modulename' => 'feedback',
 947              'instance' => $feedback->id,
 948              'eventtype' => FEEDBACK_EVENT_TYPE_OPEN,
 949              'timestart' => $newtimeopen,
 950              'timeduration' => 86400,
 951              'visible' => 1
 952          ]);
 953  
 954          mod_feedback_core_calendar_event_timestart_updated($event, $feedback);
 955  
 956          $feedback = $DB->get_record('feedback', ['id' => $feedback->id]);
 957          // Ensure the timeopen property matches the event timestart.
 958          $this->assertEquals($newtimeopen, $feedback->timeopen);
 959          // Ensure the timeclose isn't changed.
 960          $this->assertEquals($timeclose, $feedback->timeclose);
 961          // Ensure the timemodified property has been changed.
 962          $this->assertNotEquals($timemodified, $feedback->timemodified);
 963      }
 964  
 965      /**
 966       * A FEEDBACK_EVENT_TYPE_CLOSE event should update the timeclose property of
 967       * the feedback activity.
 968       */
 969      public function test_mod_feedback_core_calendar_event_timestart_updated_close_event() {
 970          global $CFG, $DB;
 971          require_once($CFG->dirroot . "/calendar/lib.php");
 972  
 973          $this->resetAfterTest(true);
 974          $this->setAdminUser();
 975          $generator = $this->getDataGenerator();
 976          $course = $generator->create_course();
 977          $feedbackgenerator = $generator->get_plugin_generator('mod_feedback');
 978          $timeopen = time();
 979          $timeclose = $timeopen + DAYSECS;
 980          $timemodified = 1;
 981          $newtimeclose = $timeclose + DAYSECS;
 982          $feedback = $feedbackgenerator->create_instance(['course' => $course->id]);
 983          $feedback->timeopen = $timeopen;
 984          $feedback->timeclose = $timeclose;
 985          $feedback->timemodified = $timemodified;
 986          $DB->update_record('feedback', $feedback);
 987  
 988          // Create a valid event.
 989          $event = new \calendar_event([
 990              'name' => 'Test event',
 991              'description' => '',
 992              'format' => 1,
 993              'courseid' => $course->id,
 994              'groupid' => 0,
 995              'userid' => 2,
 996              'modulename' => 'feedback',
 997              'instance' => $feedback->id,
 998              'eventtype' => FEEDBACK_EVENT_TYPE_CLOSE,
 999              'timestart' => $newtimeclose,
1000              'timeduration' => 86400,
1001              'visible' => 1
1002          ]);
1003  
1004          mod_feedback_core_calendar_event_timestart_updated($event, $feedback);
1005  
1006          $feedback = $DB->get_record('feedback', ['id' => $feedback->id]);
1007          // Ensure the timeclose property matches the event timestart.
1008          $this->assertEquals($newtimeclose, $feedback->timeclose);
1009          // Ensure the timeopen isn't changed.
1010          $this->assertEquals($timeopen, $feedback->timeopen);
1011          // Ensure the timemodified property has been changed.
1012          $this->assertNotEquals($timemodified, $feedback->timemodified);
1013      }
1014  
1015      /**
1016       * If a student somehow finds a way to update the calendar event
1017       * then the callback should not be executed to update the activity
1018       * properties as well because that would be a security issue.
1019       */
1020      public function test_student_role_cant_update_time_close_event() {
1021          global $CFG, $DB;
1022          require_once($CFG->dirroot . '/calendar/lib.php');
1023  
1024          $this->resetAfterTest();
1025          $this->setAdminUser();
1026  
1027          $generator = $this->getDataGenerator();
1028          $user = $generator->create_user();
1029          $course = $generator->create_course();
1030          $context = \context_course::instance($course->id);
1031          $roleid = $generator->create_role();
1032          $feedbackgenerator = $generator->get_plugin_generator('mod_feedback');
1033          $timeopen = time();
1034          $timeclose = $timeopen + DAYSECS;
1035          $timemodified = 1;
1036          $newtimeclose = $timeclose + DAYSECS;
1037          $feedback = $feedbackgenerator->create_instance(['course' => $course->id]);
1038          $feedback->timeopen = $timeopen;
1039          $feedback->timeclose = $timeclose;
1040          $feedback->timemodified = $timemodified;
1041          $DB->update_record('feedback', $feedback);
1042  
1043          $generator->enrol_user($user->id, $course->id, 'student');
1044          $generator->role_assign($roleid, $user->id, $context->id);
1045  
1046          // Create a valid event.
1047          $event = new \calendar_event([
1048              'name' => 'Test event',
1049              'description' => '',
1050              'format' => 1,
1051              'courseid' => $course->id,
1052              'groupid' => 0,
1053              'userid' => $user->id,
1054              'modulename' => 'feedback',
1055              'instance' => $feedback->id,
1056              'eventtype' => FEEDBACK_EVENT_TYPE_CLOSE,
1057              'timestart' => $newtimeclose,
1058              'timeduration' => 86400,
1059              'visible' => 1
1060          ]);
1061  
1062          assign_capability('moodle/calendar:manageentries', CAP_ALLOW, $roleid, $context, true);
1063          assign_capability('moodle/course:manageactivities', CAP_PROHIBIT, $roleid, $context, true);
1064  
1065          $this->setUser($user);
1066  
1067          mod_feedback_core_calendar_event_timestart_updated($event, $feedback);
1068  
1069          $newfeedback = $DB->get_record('feedback', ['id' => $feedback->id]);
1070          // The activity shouldn't have been updated because the user
1071          // doesn't have permissions to do it.
1072          $this->assertEquals($timeclose, $newfeedback->timeclose);
1073      }
1074  
1075      /**
1076       * The activity should update if a teacher modifies the calendar
1077       * event.
1078       */
1079      public function test_teacher_role_can_update_time_close_event() {
1080          global $CFG, $DB;
1081          require_once($CFG->dirroot . '/calendar/lib.php');
1082  
1083          $this->resetAfterTest();
1084          $this->setAdminUser();
1085  
1086          $generator = $this->getDataGenerator();
1087          $user = $generator->create_user();
1088          $course = $generator->create_course();
1089          $context = \context_course::instance($course->id);
1090          $roleid = $generator->create_role();
1091          $feedbackgenerator = $generator->get_plugin_generator('mod_feedback');
1092          $timeopen = time();
1093          $timeclose = $timeopen + DAYSECS;
1094          $timemodified = 1;
1095          $newtimeclose = $timeclose + DAYSECS;
1096          $feedback = $feedbackgenerator->create_instance(['course' => $course->id]);
1097          $feedback->timeopen = $timeopen;
1098          $feedback->timeclose = $timeclose;
1099          $feedback->timemodified = $timemodified;
1100          $DB->update_record('feedback', $feedback);
1101  
1102          $generator->enrol_user($user->id, $course->id, 'teacher');
1103          $generator->role_assign($roleid, $user->id, $context->id);
1104  
1105          // Create a valid event.
1106          $event = new \calendar_event([
1107              'name' => 'Test event',
1108              'description' => '',
1109              'format' => 1,
1110              'courseid' => $course->id,
1111              'groupid' => 0,
1112              'userid' => $user->id,
1113              'modulename' => 'feedback',
1114              'instance' => $feedback->id,
1115              'eventtype' => FEEDBACK_EVENT_TYPE_CLOSE,
1116              'timestart' => $newtimeclose,
1117              'timeduration' => 86400,
1118              'visible' => 1
1119          ]);
1120  
1121          assign_capability('moodle/calendar:manageentries', CAP_ALLOW, $roleid, $context, true);
1122          assign_capability('moodle/course:manageactivities', CAP_ALLOW, $roleid, $context, true);
1123  
1124          $this->setUser($user);
1125  
1126          $sink = $this->redirectEvents();
1127  
1128          mod_feedback_core_calendar_event_timestart_updated($event, $feedback);
1129  
1130          $triggeredevents = $sink->get_events();
1131          $moduleupdatedevents = array_filter($triggeredevents, function($e) {
1132              return is_a($e, 'core\event\course_module_updated');
1133          });
1134  
1135          $newfeedback = $DB->get_record('feedback', ['id' => $feedback->id]);
1136          // The activity should have been updated because the user
1137          // has permissions to do it.
1138          $this->assertEquals($newtimeclose, $newfeedback->timeclose);
1139          // A course_module_updated event should be fired if the module
1140          // was successfully modified.
1141          $this->assertNotEmpty($moduleupdatedevents);
1142      }
1143  
1144      /**
1145       * A user who does not have capabilities to add events to the calendar should be able to create an feedback.
1146       */
1147      public function test_creation_with_no_calendar_capabilities() {
1148          $this->resetAfterTest();
1149          $course = self::getDataGenerator()->create_course();
1150          $context = \context_course::instance($course->id);
1151          $user = self::getDataGenerator()->create_and_enrol($course, 'editingteacher');
1152          $roleid = self::getDataGenerator()->create_role();
1153          self::getDataGenerator()->role_assign($roleid, $user->id, $context->id);
1154          assign_capability('moodle/calendar:manageentries', CAP_PROHIBIT, $roleid, $context, true);
1155          $generator = self::getDataGenerator()->get_plugin_generator('mod_feedback');
1156          // Create an instance as a user without the calendar capabilities.
1157          $this->setUser($user);
1158          $time = time();
1159          $params = array(
1160              'course' => $course->id,
1161              'timeopen' => $time + 200,
1162              'timeclose' => $time + 2000,
1163          );
1164          $generator->create_instance($params);
1165      }
1166  }