Search moodle.org's
Developer Documentation

See Release Notes
Long Term Support Release

  • Bug fixes for general core bugs in 3.9.x will end* 10 May 2021 (12 months).
  • Bug fixes for security issues in 3.9.x will end* 8 May 2023 (36 months).
  • PHP version: minimum PHP 7.2.0 Note: minimum PHP version has increased since Moodle 3.8. PHP 7.3.x and 7.4.x are supported too.

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