Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 3.11.x will end 14 Nov 2022 (12 months plus 6 months extension).
  • Bug fixes for security issues in 3.11.x will end 13 Nov 2023 (18 months plus 12 months extension).
  • PHP version: minimum PHP 7.3.0 Note: minimum PHP version has increased since Moodle 3.10. PHP 7.4.x is supported too.

Differences Between: [Versions 311 and 400] [Versions 311 and 401] [Versions 311 and 402] [Versions 311 and 403]

   1  <?php
   2  // This file is part of Moodle - http://moodle.org/
   3  //
   4  // Moodle is free software: you can redistribute it and/or modify
   5  // it under the terms of the GNU General Public License as published by
   6  // the Free Software Foundation, either version 3 of the License, or
   7  // (at your option) any later version.
   8  //
   9  // Moodle is distributed in the hope that it will be useful,
  10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12  // GNU General Public License for more details.
  13  //
  14  // You should have received a copy of the GNU General Public License
  15  // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
  16  
  17  /**
  18   * Feedback module external functions tests
  19   *
  20   * @package    mod_feedback
  21   * @category   external
  22   * @copyright  2017 Juan Leyva <juan@moodle.com>
  23   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  24   * @since      Moodle 3.3
  25   */
  26  
  27  namespace mod_feedback\external;
  28  
  29  use externallib_advanced_testcase;
  30  use feedback_item_multichoice;
  31  use mod_feedback_external;
  32  
  33  defined('MOODLE_INTERNAL') || die();
  34  
  35  global $CFG;
  36  
  37  require_once($CFG->dirroot . '/webservice/tests/helpers.php');
  38  require_once($CFG->dirroot . '/mod/feedback/lib.php');
  39  
  40  /**
  41   * Feedback module external functions tests
  42   *
  43   * @package    mod_feedback
  44   * @category   external
  45   * @copyright  2017 Juan Leyva <juan@moodle.com>
  46   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  47   * @since      Moodle 3.3
  48   */
  49  class external_test extends externallib_advanced_testcase {
  50  
  51      /**
  52       * Set up for every test
  53       */
  54      public function setUp(): void {
  55          global $DB;
  56          $this->resetAfterTest();
  57          $this->setAdminUser();
  58  
  59          // Setup test data.
  60          $this->course = $this->getDataGenerator()->create_course();
  61          $this->feedback = $this->getDataGenerator()->create_module('feedback',
  62              array('course' => $this->course->id, 'email_notification' => 1));
  63          $this->context = \context_module::instance($this->feedback->cmid);
  64          $this->cm = get_coursemodule_from_instance('feedback', $this->feedback->id);
  65  
  66          // Create users.
  67          $this->student = self::getDataGenerator()->create_user();
  68          $this->teacher = self::getDataGenerator()->create_user();
  69  
  70          // Users enrolments.
  71          $this->studentrole = $DB->get_record('role', array('shortname' => 'student'));
  72          $this->teacherrole = $DB->get_record('role', array('shortname' => 'editingteacher'));
  73          $this->getDataGenerator()->enrol_user($this->student->id, $this->course->id, $this->studentrole->id, 'manual');
  74          $this->getDataGenerator()->enrol_user($this->teacher->id, $this->course->id, $this->teacherrole->id, 'manual');
  75      }
  76  
  77      /**
  78       * Helper method to add items to an existing feedback.
  79       *
  80       * @param \stdClass $feedback feedback instance
  81       * @param integer $pagescount the number of pages we want in the feedback
  82       * @return array list of items created
  83       */
  84      public function populate_feedback($feedback, $pagescount = 1) {
  85          $feedbackgenerator = $this->getDataGenerator()->get_plugin_generator('mod_feedback');
  86          $itemscreated = [];
  87  
  88          // Create at least one page.
  89          $itemscreated[] = $feedbackgenerator->create_item_label($feedback);
  90          $itemscreated[] = $feedbackgenerator->create_item_info($feedback);
  91          $itemscreated[] = $feedbackgenerator->create_item_numeric($feedback);
  92  
  93          // Check if we want more pages.
  94          for ($i = 1; $i < $pagescount; $i++) {
  95              $itemscreated[] = $feedbackgenerator->create_item_pagebreak($feedback);
  96              $itemscreated[] = $feedbackgenerator->create_item_multichoice($feedback);
  97              $itemscreated[] = $feedbackgenerator->create_item_multichoicerated($feedback);
  98              $itemscreated[] = $feedbackgenerator->create_item_textarea($feedback);
  99              $itemscreated[] = $feedbackgenerator->create_item_textfield($feedback);
 100              $itemscreated[] = $feedbackgenerator->create_item_numeric($feedback);
 101          }
 102          return $itemscreated;
 103      }
 104  
 105  
 106      /**
 107       * Test test_mod_feedback_get_feedbacks_by_courses
 108       */
 109      public function test_mod_feedback_get_feedbacks_by_courses() {
 110          global $DB;
 111  
 112          // Create additional course.
 113          $course2 = self::getDataGenerator()->create_course();
 114  
 115          // Second feedback.
 116          $record = new \stdClass();
 117          $record->course = $course2->id;
 118          $feedback2 = self::getDataGenerator()->create_module('feedback', $record);
 119  
 120          // Execute real Moodle enrolment as we'll call unenrol() method on the instance later.
 121          $enrol = enrol_get_plugin('manual');
 122          $enrolinstances = enrol_get_instances($course2->id, true);
 123          foreach ($enrolinstances as $courseenrolinstance) {
 124              if ($courseenrolinstance->enrol == "manual") {
 125                  $instance2 = $courseenrolinstance;
 126                  break;
 127              }
 128          }
 129          $enrol->enrol_user($instance2, $this->student->id, $this->studentrole->id);
 130  
 131          self::setUser($this->student);
 132  
 133          $returndescription = mod_feedback_external::get_feedbacks_by_courses_returns();
 134  
 135          // Create what we expect to be returned when querying the two courses.
 136          // First for the student user.
 137          $expectedfields = array('id', 'coursemodule', 'course', 'name', 'intro', 'introformat', 'introfiles', 'anonymous',
 138              'multiple_submit', 'autonumbering', 'page_after_submitformat', 'publish_stats', 'completionsubmit');
 139  
 140          $properties = feedback_summary_exporter::read_properties_definition();
 141  
 142          // Add expected coursemodule and data.
 143          $feedback1 = $this->feedback;
 144          $feedback1->coursemodule = $feedback1->cmid;
 145          $feedback1->introformat = 1;
 146          $feedback1->introfiles = [];
 147  
 148          $feedback2->coursemodule = $feedback2->cmid;
 149          $feedback2->introformat = 1;
 150          $feedback2->introfiles = [];
 151  
 152          foreach ($expectedfields as $field) {
 153              if (!empty($properties[$field]) && $properties[$field]['type'] == PARAM_BOOL) {
 154                  $feedback1->{$field} = (bool) $feedback1->{$field};
 155                  $feedback2->{$field} = (bool) $feedback2->{$field};
 156              }
 157              $expected1[$field] = $feedback1->{$field};
 158              $expected2[$field] = $feedback2->{$field};
 159          }
 160  
 161          $expectedfeedbacks = array($expected2, $expected1);
 162  
 163          // Call the external function passing course ids.
 164          $result = mod_feedback_external::get_feedbacks_by_courses(array($course2->id, $this->course->id));
 165          $result = \external_api::clean_returnvalue($returndescription, $result);
 166  
 167          $this->assertEquals($expectedfeedbacks, $result['feedbacks']);
 168          $this->assertCount(0, $result['warnings']);
 169  
 170          // Call the external function without passing course id.
 171          $result = mod_feedback_external::get_feedbacks_by_courses();
 172          $result = \external_api::clean_returnvalue($returndescription, $result);
 173          $this->assertEquals($expectedfeedbacks, $result['feedbacks']);
 174          $this->assertCount(0, $result['warnings']);
 175  
 176          // Unenrol user from second course and alter expected feedbacks.
 177          $enrol->unenrol_user($instance2, $this->student->id);
 178          array_shift($expectedfeedbacks);
 179  
 180          // Call the external function without passing course id.
 181          $result = mod_feedback_external::get_feedbacks_by_courses();
 182          $result = \external_api::clean_returnvalue($returndescription, $result);
 183          $this->assertEquals($expectedfeedbacks, $result['feedbacks']);
 184  
 185          // Call for the second course we unenrolled the user from, expected warning.
 186          $result = mod_feedback_external::get_feedbacks_by_courses(array($course2->id));
 187          $this->assertCount(1, $result['warnings']);
 188          $this->assertEquals('1', $result['warnings'][0]['warningcode']);
 189          $this->assertEquals($course2->id, $result['warnings'][0]['itemid']);
 190  
 191          // Now, try as a teacher for getting all the additional fields.
 192          self::setUser($this->teacher);
 193  
 194          $additionalfields = array('email_notification', 'site_after_submit', 'page_after_submit', 'timeopen', 'timeclose',
 195              'timemodified', 'pageaftersubmitfiles');
 196  
 197          $feedback1->pageaftersubmitfiles = [];
 198  
 199          foreach ($additionalfields as $field) {
 200              if (!empty($properties[$field]) && $properties[$field]['type'] == PARAM_BOOL) {
 201                  $feedback1->{$field} = (bool) $feedback1->{$field};
 202              }
 203              $expectedfeedbacks[0][$field] = $feedback1->{$field};
 204          }
 205          $expectedfeedbacks[0]['page_after_submitformat'] = 1;
 206  
 207          $result = mod_feedback_external::get_feedbacks_by_courses();
 208          $result = \external_api::clean_returnvalue($returndescription, $result);
 209          $this->assertEquals($expectedfeedbacks, $result['feedbacks']);
 210  
 211          // Admin also should get all the information.
 212          self::setAdminUser();
 213  
 214          $result = mod_feedback_external::get_feedbacks_by_courses(array($this->course->id));
 215          $result = \external_api::clean_returnvalue($returndescription, $result);
 216          $this->assertEquals($expectedfeedbacks, $result['feedbacks']);
 217      }
 218  
 219      /**
 220       * Test get_feedback_access_information function with basic defaults for student.
 221       */
 222      public function test_get_feedback_access_information_student() {
 223  
 224          self::setUser($this->student);
 225          $result = mod_feedback_external::get_feedback_access_information($this->feedback->id);
 226          $result = \external_api::clean_returnvalue(mod_feedback_external::get_feedback_access_information_returns(), $result);
 227  
 228          $this->assertFalse($result['canviewanalysis']);
 229          $this->assertFalse($result['candeletesubmissions']);
 230          $this->assertFalse($result['canviewreports']);
 231          $this->assertFalse($result['canedititems']);
 232          $this->assertTrue($result['cancomplete']);
 233          $this->assertTrue($result['cansubmit']);
 234          $this->assertTrue($result['isempty']);
 235          $this->assertTrue($result['isopen']);
 236          $this->assertTrue($result['isanonymous']);
 237          $this->assertFalse($result['isalreadysubmitted']);
 238      }
 239  
 240      /**
 241       * Test get_feedback_access_information function with basic defaults for teacher.
 242       */
 243      public function test_get_feedback_access_information_teacher() {
 244  
 245          self::setUser($this->teacher);
 246          $result = mod_feedback_external::get_feedback_access_information($this->feedback->id);
 247          $result = \external_api::clean_returnvalue(mod_feedback_external::get_feedback_access_information_returns(), $result);
 248  
 249          $this->assertTrue($result['canviewanalysis']);
 250          $this->assertTrue($result['canviewreports']);
 251          $this->assertTrue($result['canedititems']);
 252          $this->assertTrue($result['candeletesubmissions']);
 253          $this->assertFalse($result['cancomplete']);
 254          $this->assertTrue($result['cansubmit']);
 255          $this->assertTrue($result['isempty']);
 256          $this->assertTrue($result['isopen']);
 257          $this->assertTrue($result['isanonymous']);
 258          $this->assertFalse($result['isalreadysubmitted']);
 259  
 260          // Add some items to the feedback and check is not empty any more.
 261          self::populate_feedback($this->feedback);
 262          $result = mod_feedback_external::get_feedback_access_information($this->feedback->id);
 263          $result = \external_api::clean_returnvalue(mod_feedback_external::get_feedback_access_information_returns(), $result);
 264          $this->assertFalse($result['isempty']);
 265      }
 266  
 267      /**
 268       * Test view_feedback invalid id.
 269       */
 270      public function test_view_feedback_invalid_id() {
 271          // Test invalid instance id.
 272          $this->expectException('\moodle_exception');
 273          mod_feedback_external::view_feedback(0);
 274      }
 275      /**
 276       * Test view_feedback not enrolled user.
 277       */
 278      public function test_view_feedback_not_enrolled_user() {
 279          $usernotenrolled = self::getDataGenerator()->create_user();
 280          $this->setUser($usernotenrolled);
 281          $this->expectException('\moodle_exception');
 282          mod_feedback_external::view_feedback(0);
 283      }
 284      /**
 285       * Test view_feedback no capabilities.
 286       */
 287      public function test_view_feedback_no_capabilities() {
 288          // Test user with no capabilities.
 289          // We need a explicit prohibit since this capability is allowed for students by default.
 290          assign_capability('mod/feedback:view', CAP_PROHIBIT, $this->studentrole->id, $this->context->id);
 291          accesslib_clear_all_caches_for_unit_testing();
 292          $this->expectException('\moodle_exception');
 293          mod_feedback_external::view_feedback(0);
 294      }
 295      /**
 296       * Test view_feedback.
 297       */
 298      public function test_view_feedback() {
 299          // Test user with full capabilities.
 300          $this->setUser($this->student);
 301          // Trigger and capture the event.
 302          $sink = $this->redirectEvents();
 303          $result = mod_feedback_external::view_feedback($this->feedback->id);
 304          $result = \external_api::clean_returnvalue(mod_feedback_external::view_feedback_returns(), $result);
 305          $events = $sink->get_events();
 306          $this->assertCount(1, $events);
 307          $event = array_shift($events);
 308          // Checking that the event contains the expected values.
 309          $this->assertInstanceOf('\mod_feedback\event\course_module_viewed', $event);
 310          $this->assertEquals($this->context, $event->get_context());
 311          $moodledata = new \moodle_url('/mod/feedback/view.php', array('id' => $this->cm->id));
 312          $this->assertEquals($moodledata, $event->get_url());
 313          $this->assertEventContextNotUsed($event);
 314          $this->assertNotEmpty($event->get_name());
 315      }
 316  
 317      /**
 318       * Test get_current_completed_tmp.
 319       */
 320      public function test_get_current_completed_tmp() {
 321          global $DB;
 322  
 323          // Force non anonymous.
 324          $DB->set_field('feedback', 'anonymous', FEEDBACK_ANONYMOUS_NO, array('id' => $this->feedback->id));
 325          // Add a completed_tmp record.
 326          $record = [
 327              'feedback' => $this->feedback->id,
 328              'userid' => $this->student->id,
 329              'guestid' => '',
 330              'timemodified' => time() - DAYSECS,
 331              'random_response' => 0,
 332              'anonymous_response' => FEEDBACK_ANONYMOUS_NO,
 333              'courseid' => $this->course->id,
 334          ];
 335          $record['id'] = $DB->insert_record('feedback_completedtmp', (object) $record);
 336  
 337          // Test user with full capabilities.
 338          $this->setUser($this->student);
 339  
 340          $result = mod_feedback_external::get_current_completed_tmp($this->feedback->id);
 341          $result = \external_api::clean_returnvalue(mod_feedback_external::get_current_completed_tmp_returns(), $result);
 342          $this->assertEquals($record['id'], $result['feedback']['id']);
 343      }
 344  
 345      /**
 346       * Test get_items.
 347       */
 348      public function test_get_items() {
 349          // Test user with full capabilities.
 350          $this->setUser($this->student);
 351  
 352          // Add questions to the feedback, we are adding 2 pages of questions.
 353          $itemscreated = self::populate_feedback($this->feedback, 2);
 354  
 355          $result = mod_feedback_external::get_items($this->feedback->id);
 356          $result = \external_api::clean_returnvalue(mod_feedback_external::get_items_returns(), $result);
 357          $this->assertCount(count($itemscreated), $result['items']);
 358          $index = 1;
 359          foreach ($result['items'] as $key => $item) {
 360              if (is_numeric($itemscreated[$key])) {
 361                  continue; // Page break.
 362              }
 363              // Cannot compare directly the exporter and the db object (exporter have more fields).
 364              $this->assertEquals($itemscreated[$key]->id, $item['id']);
 365              $this->assertEquals($itemscreated[$key]->typ, $item['typ']);
 366              $this->assertEquals($itemscreated[$key]->name, $item['name']);
 367              $this->assertEquals($itemscreated[$key]->label, $item['label']);
 368  
 369              if ($item['hasvalue']) {
 370                  $this->assertEquals($index, $item['itemnumber']);
 371                  $index++;
 372              }
 373          }
 374      }
 375  
 376      /**
 377       * Test launch_feedback.
 378       */
 379      public function test_launch_feedback() {
 380          global $DB;
 381  
 382          // Test user with full capabilities.
 383          $this->setUser($this->student);
 384  
 385          // Add questions to the feedback, we are adding 2 pages of questions.
 386          $itemscreated = self::populate_feedback($this->feedback, 2);
 387  
 388          // First try a feedback we didn't attempt.
 389          $result = mod_feedback_external::launch_feedback($this->feedback->id);
 390          $result = \external_api::clean_returnvalue(mod_feedback_external::launch_feedback_returns(), $result);
 391          $this->assertEquals(0, $result['gopage']);
 392  
 393          // Now, try a feedback that we attempted.
 394          // Force non anonymous.
 395          $DB->set_field('feedback', 'anonymous', FEEDBACK_ANONYMOUS_NO, array('id' => $this->feedback->id));
 396          // Add a completed_tmp record.
 397          $record = [
 398              'feedback' => $this->feedback->id,
 399              'userid' => $this->student->id,
 400              'guestid' => '',
 401              'timemodified' => time() - DAYSECS,
 402              'random_response' => 0,
 403              'anonymous_response' => FEEDBACK_ANONYMOUS_NO,
 404              'courseid' => $this->course->id,
 405          ];
 406          $record['id'] = $DB->insert_record('feedback_completedtmp', (object) $record);
 407  
 408          // Add a response to the feedback for each question type with possible values.
 409          $response = [
 410              'course_id' => $this->course->id,
 411              'item' => $itemscreated[1]->id, // First item is the info question.
 412              'completed' => $record['id'],
 413              'tmp_completed' => $record['id'],
 414              'value' => 'A',
 415          ];
 416          $DB->insert_record('feedback_valuetmp', (object) $response);
 417          $response = [
 418              'course_id' => $this->course->id,
 419              'item' => $itemscreated[2]->id, // Second item is the numeric question.
 420              'completed' => $record['id'],
 421              'tmp_completed' => $record['id'],
 422              'value' => 5,
 423          ];
 424          $DB->insert_record('feedback_valuetmp', (object) $response);
 425  
 426          $result = mod_feedback_external::launch_feedback($this->feedback->id);
 427          $result = \external_api::clean_returnvalue(mod_feedback_external::launch_feedback_returns(), $result);
 428          $this->assertEquals(1, $result['gopage']);
 429      }
 430  
 431      /**
 432       * Test get_page_items.
 433       */
 434      public function test_get_page_items() {
 435          // Test user with full capabilities.
 436          $this->setUser($this->student);
 437  
 438          // Add questions to the feedback, we are adding 2 pages of questions.
 439          $itemscreated = self::populate_feedback($this->feedback, 2);
 440  
 441          // Retrieve first page.
 442          $result = mod_feedback_external::get_page_items($this->feedback->id, 0);
 443          $result = \external_api::clean_returnvalue(mod_feedback_external::get_page_items_returns(), $result);
 444          $this->assertCount(3, $result['items']);    // The first page has 3 items.
 445          $this->assertTrue($result['hasnextpage']);
 446          $this->assertFalse($result['hasprevpage']);
 447  
 448          // Retrieve second page.
 449          $result = mod_feedback_external::get_page_items($this->feedback->id, 1);
 450          $result = \external_api::clean_returnvalue(mod_feedback_external::get_page_items_returns(), $result);
 451          $this->assertCount(5, $result['items']);    // The second page has 5 items (page break doesn't count).
 452          $this->assertFalse($result['hasnextpage']);
 453          $this->assertTrue($result['hasprevpage']);
 454      }
 455  
 456      /**
 457       * Test process_page.
 458       */
 459      public function test_process_page() {
 460          global $DB;
 461  
 462          // Test user with full capabilities.
 463          $this->setUser($this->student);
 464          $pagecontents = 'You finished it!';
 465          $DB->set_field('feedback', 'page_after_submit', $pagecontents, array('id' => $this->feedback->id));
 466  
 467          // Add questions to the feedback, we are adding 2 pages of questions.
 468          $itemscreated = self::populate_feedback($this->feedback, 2);
 469  
 470          $data = [];
 471          foreach ($itemscreated as $item) {
 472  
 473              if (empty($item->hasvalue)) {
 474                  continue;
 475              }
 476  
 477              switch ($item->typ) {
 478                  case 'textarea':
 479                  case 'textfield':
 480                      $value = 'Lorem ipsum';
 481                      break;
 482                  case 'numeric':
 483                      $value = 5;
 484                      break;
 485                  case 'multichoice':
 486                      $value = '1';
 487                      break;
 488                  case 'multichoicerated':
 489                      $value = '1';
 490                      break;
 491                  case 'info':
 492                      $value = format_string($this->course->shortname, true, array('context' => $this->context));
 493                      break;
 494                  default:
 495                      $value = '';
 496              }
 497              $data[] = ['name' => $item->typ . '_' . $item->id, 'value' => $value];
 498          }
 499  
 500          // Process first page.
 501          $firstpagedata = [$data[0], $data[1]];
 502          $result = mod_feedback_external::process_page($this->feedback->id, 0, $firstpagedata);
 503          $result = \external_api::clean_returnvalue(mod_feedback_external::process_page_returns(), $result);
 504          $this->assertEquals(1, $result['jumpto']);
 505          $this->assertFalse($result['completed']);
 506  
 507          // Now, process the second page. But first we are going back to the first page.
 508          $secondpagedata = [$data[2], $data[3], $data[4], $data[5], $data[6]];
 509          $result = mod_feedback_external::process_page($this->feedback->id, 1, $secondpagedata, true);
 510          $result = \external_api::clean_returnvalue(mod_feedback_external::process_page_returns(), $result);
 511          $this->assertFalse($result['completed']);
 512          $this->assertEquals(0, $result['jumpto']);  // We jumped to the first page.
 513          // Check the values were correctly saved.
 514          $tmpitems = $DB->get_records('feedback_valuetmp');
 515          $this->assertCount(7, $tmpitems);   // 2 from the first page + 5 from the second page.
 516  
 517          // Go forward again (sending the same data).
 518          $result = mod_feedback_external::process_page($this->feedback->id, 0, $firstpagedata);
 519          $result = \external_api::clean_returnvalue(mod_feedback_external::process_page_returns(), $result);
 520          $this->assertEquals(1, $result['jumpto']);
 521          $this->assertFalse($result['completed']);
 522          $tmpitems = $DB->get_records('feedback_valuetmp');
 523          $this->assertCount(7, $tmpitems);   // 2 from the first page + 5 from the second page.
 524  
 525          // And finally, save everything! We are going to modify one previous recorded value.
 526          $messagessink = $this->redirectMessages();
 527          $data[2]['value'] = 2; // 2 is value of the option 'b'.
 528          $secondpagedata = [$data[2], $data[3], $data[4], $data[5], $data[6]];
 529          $result = mod_feedback_external::process_page($this->feedback->id, 1, $secondpagedata);
 530          $result = \external_api::clean_returnvalue(mod_feedback_external::process_page_returns(), $result);
 531          $this->assertTrue($result['completed']);
 532          $this->assertTrue(strpos($result['completionpagecontents'], $pagecontents) !== false);
 533          // Check all the items were saved.
 534          $items = $DB->get_records('feedback_value');
 535          $this->assertCount(7, $items);
 536          // Check if the one we modified was correctly saved.
 537          $itemid = $itemscreated[4]->id;
 538          $itemsaved = $DB->get_field('feedback_value', 'value', array('item' => $itemid));
 539          $mcitem = new feedback_item_multichoice();
 540          $itemval = $mcitem->get_printval($itemscreated[4], (object) ['value' => $itemsaved]);
 541          $this->assertEquals('b', $itemval);
 542  
 543          // Check that the answers are saved for course 0.
 544          foreach ($items as $item) {
 545              $this->assertEquals(0, $item->course_id);
 546          }
 547          $completed = $DB->get_record('feedback_completed', []);
 548          $this->assertEquals(0, $completed->courseid);
 549  
 550          // Test notifications sent.
 551          $messages = $messagessink->get_messages();
 552          $messagessink->close();
 553          // Test customdata.
 554          $customdata = json_decode($messages[0]->customdata);
 555          $this->assertEquals($this->feedback->id, $customdata->instance);
 556          $this->assertEquals($this->feedback->cmid, $customdata->cmid);
 557          $this->assertObjectHasAttribute('notificationiconurl', $customdata);
 558      }
 559  
 560      /**
 561       * Test process_page for a site feedback.
 562       */
 563      public function test_process_page_site_feedback() {
 564          global $DB;
 565          $pagecontents = 'You finished it!';
 566          $this->feedback = $this->getDataGenerator()->create_module('feedback',
 567              array('course' => SITEID, 'page_after_submit' => $pagecontents));
 568  
 569          // Test user with full capabilities.
 570          $this->setUser($this->student);
 571  
 572          // Add questions to the feedback, we are adding 2 pages of questions.
 573          $itemscreated = self::populate_feedback($this->feedback, 2);
 574  
 575          $data = [];
 576          foreach ($itemscreated as $item) {
 577  
 578              if (empty($item->hasvalue)) {
 579                  continue;
 580              }
 581  
 582              switch ($item->typ) {
 583                  case 'textarea':
 584                  case 'textfield':
 585                      $value = 'Lorem ipsum';
 586                      break;
 587                  case 'numeric':
 588                      $value = 5;
 589                      break;
 590                  case 'multichoice':
 591                      $value = '1';
 592                      break;
 593                  case 'multichoicerated':
 594                      $value = '1';
 595                      break;
 596                  case 'info':
 597                      $value = format_string($this->course->shortname, true, array('context' => $this->context));
 598                      break;
 599                  default:
 600                      $value = '';
 601              }
 602              $data[] = ['name' => $item->typ . '_' . $item->id, 'value' => $value];
 603          }
 604  
 605          // Process first page.
 606          $firstpagedata = [$data[0], $data[1]];
 607          $result = mod_feedback_external::process_page($this->feedback->id, 0, $firstpagedata, false, $this->course->id);
 608          $result = \external_api::clean_returnvalue(mod_feedback_external::process_page_returns(), $result);
 609          $this->assertEquals(1, $result['jumpto']);
 610          $this->assertFalse($result['completed']);
 611  
 612          // Process second page.
 613          $data[2]['value'] = 2; // 2 is value of the option 'b';
 614          $secondpagedata = [$data[2], $data[3], $data[4], $data[5], $data[6]];
 615          $result = mod_feedback_external::process_page($this->feedback->id, 1, $secondpagedata, false, $this->course->id);
 616          $result = \external_api::clean_returnvalue(mod_feedback_external::process_page_returns(), $result);
 617          $this->assertTrue($result['completed']);
 618          $this->assertTrue(strpos($result['completionpagecontents'], $pagecontents) !== false);
 619          // Check all the items were saved.
 620          $items = $DB->get_records('feedback_value');
 621          $this->assertCount(7, $items);
 622          // Check if the one we modified was correctly saved.
 623          $itemid = $itemscreated[4]->id;
 624          $itemsaved = $DB->get_field('feedback_value', 'value', array('item' => $itemid));
 625          $mcitem = new feedback_item_multichoice();
 626          $itemval = $mcitem->get_printval($itemscreated[4], (object) ['value' => $itemsaved]);
 627          $this->assertEquals('b', $itemval);
 628  
 629          // Check that the answers are saved for the correct course.
 630          foreach ($items as $item) {
 631              $this->assertEquals($this->course->id, $item->course_id);
 632          }
 633          $completed = $DB->get_record('feedback_completed', []);
 634          $this->assertEquals($this->course->id, $completed->courseid);
 635      }
 636  
 637      /**
 638       * Test get_analysis.
 639       */
 640      public function test_get_analysis() {
 641          // Test user with full capabilities.
 642          $this->setUser($this->student);
 643  
 644          // Create a very simple feedback.
 645          $feedbackgenerator = $this->getDataGenerator()->get_plugin_generator('mod_feedback');
 646          $numericitem = $feedbackgenerator->create_item_numeric($this->feedback);
 647          $textfielditem = $feedbackgenerator->create_item_textfield($this->feedback);
 648  
 649          $pagedata = [
 650              ['name' => $numericitem->typ .'_'. $numericitem->id, 'value' => 5],
 651              ['name' => $textfielditem->typ .'_'. $textfielditem->id, 'value' => 'abc'],
 652          ];
 653          // Process the feedback, there is only one page so the feedback will be completed.
 654          $result = mod_feedback_external::process_page($this->feedback->id, 0, $pagedata);
 655          $result = \external_api::clean_returnvalue(mod_feedback_external::process_page_returns(), $result);
 656          $this->assertTrue($result['completed']);
 657  
 658          // Retrieve analysis.
 659          $this->setUser($this->teacher);
 660          $result = mod_feedback_external::get_analysis($this->feedback->id);
 661          $result = \external_api::clean_returnvalue(mod_feedback_external::get_analysis_returns(), $result);
 662          $this->assertEquals(1, $result['completedcount']);  // 1 feedback completed.
 663          $this->assertEquals(2, $result['itemscount']);  // 2 items in the feedback.
 664          $this->assertCount(2, $result['itemsdata']);
 665          $this->assertCount(1, $result['itemsdata'][0]['data']); // There are 1 response per item.
 666          $this->assertCount(1, $result['itemsdata'][1]['data']);
 667          // Check we receive the info the students filled.
 668          foreach ($result['itemsdata'] as $data) {
 669              if ($data['item']['id'] == $numericitem->id) {
 670                  $this->assertEquals(5, $data['data'][0]);
 671              } else {
 672                  $this->assertEquals('abc', $data['data'][0]);
 673              }
 674          }
 675  
 676          // Create another user / response.
 677          $anotherstudent = self::getDataGenerator()->create_user();
 678          $this->getDataGenerator()->enrol_user($anotherstudent->id, $this->course->id, $this->studentrole->id, 'manual');
 679          $this->setUser($anotherstudent);
 680  
 681          // Process the feedback, there is only one page so the feedback will be completed.
 682          $result = mod_feedback_external::process_page($this->feedback->id, 0, $pagedata);
 683          $result = \external_api::clean_returnvalue(mod_feedback_external::process_page_returns(), $result);
 684          $this->assertTrue($result['completed']);
 685  
 686          // Retrieve analysis.
 687          $this->setUser($this->teacher);
 688          $result = mod_feedback_external::get_analysis($this->feedback->id);
 689          $result = \external_api::clean_returnvalue(mod_feedback_external::get_analysis_returns(), $result);
 690          $this->assertEquals(2, $result['completedcount']);  // 2 feedback completed.
 691          $this->assertEquals(2, $result['itemscount']);
 692          $this->assertCount(2, $result['itemsdata'][0]['data']); // There are 2 responses per item.
 693          $this->assertCount(2, $result['itemsdata'][1]['data']);
 694      }
 695  
 696      /**
 697       * Test get_unfinished_responses.
 698       */
 699      public function test_get_unfinished_responses() {
 700          // Test user with full capabilities.
 701          $this->setUser($this->student);
 702  
 703          // Create a very simple feedback.
 704          $feedbackgenerator = $this->getDataGenerator()->get_plugin_generator('mod_feedback');
 705          $numericitem = $feedbackgenerator->create_item_numeric($this->feedback);
 706          $textfielditem = $feedbackgenerator->create_item_textfield($this->feedback);
 707          $feedbackgenerator->create_item_pagebreak($this->feedback);
 708          $labelitem = $feedbackgenerator->create_item_label($this->feedback);
 709          $numericitem2 = $feedbackgenerator->create_item_numeric($this->feedback);
 710  
 711          $pagedata = [
 712              ['name' => $numericitem->typ .'_'. $numericitem->id, 'value' => 5],
 713              ['name' => $textfielditem->typ .'_'. $textfielditem->id, 'value' => 'abc'],
 714          ];
 715          // Process the feedback, there are two pages so the feedback will be unfinished yet.
 716          $result = mod_feedback_external::process_page($this->feedback->id, 0, $pagedata);
 717          $result = \external_api::clean_returnvalue(mod_feedback_external::process_page_returns(), $result);
 718          $this->assertFalse($result['completed']);
 719  
 720          // Retrieve the unfinished responses.
 721          $result = mod_feedback_external::get_unfinished_responses($this->feedback->id);
 722          $result = \external_api::clean_returnvalue(mod_feedback_external::get_unfinished_responses_returns(), $result);
 723          // Check that ids and responses match.
 724          foreach ($result['responses'] as $r) {
 725              if ($r['item'] == $numericitem->id) {
 726                  $this->assertEquals(5, $r['value']);
 727              } else {
 728                  $this->assertEquals($textfielditem->id, $r['item']);
 729                  $this->assertEquals('abc', $r['value']);
 730              }
 731          }
 732      }
 733  
 734      /**
 735       * Test get_finished_responses.
 736       */
 737      public function test_get_finished_responses() {
 738          // Test user with full capabilities.
 739          $this->setUser($this->student);
 740  
 741          // Create a very simple feedback.
 742          $feedbackgenerator = $this->getDataGenerator()->get_plugin_generator('mod_feedback');
 743          $numericitem = $feedbackgenerator->create_item_numeric($this->feedback);
 744          $textfielditem = $feedbackgenerator->create_item_textfield($this->feedback);
 745  
 746          $pagedata = [
 747              ['name' => $numericitem->typ .'_'. $numericitem->id, 'value' => 5],
 748              ['name' => $textfielditem->typ .'_'. $textfielditem->id, 'value' => 'abc'],
 749          ];
 750  
 751          // Process the feedback, there is only one page so the feedback will be completed.
 752          $result = mod_feedback_external::process_page($this->feedback->id, 0, $pagedata);
 753          $result = \external_api::clean_returnvalue(mod_feedback_external::process_page_returns(), $result);
 754          $this->assertTrue($result['completed']);
 755  
 756          // Retrieve the responses.
 757          $result = mod_feedback_external::get_finished_responses($this->feedback->id);
 758          $result = \external_api::clean_returnvalue(mod_feedback_external::get_finished_responses_returns(), $result);
 759          // Check that ids and responses match.
 760          foreach ($result['responses'] as $r) {
 761              if ($r['item'] == $numericitem->id) {
 762                  $this->assertEquals(5, $r['value']);
 763              } else {
 764                  $this->assertEquals($textfielditem->id, $r['item']);
 765                  $this->assertEquals('abc', $r['value']);
 766              }
 767          }
 768      }
 769  
 770      /**
 771       * Test get_non_respondents (student trying to get this information).
 772       */
 773      public function test_get_non_respondents_no_permissions() {
 774          $this->setUser($this->student);
 775          $this->expectException('\moodle_exception');
 776          mod_feedback_external::get_non_respondents($this->feedback->id);
 777      }
 778  
 779      /**
 780       * Test get_non_respondents from an anonymous feedback.
 781       */
 782      public function test_get_non_respondents_from_anonymous_feedback() {
 783          $this->setUser($this->student);
 784          $this->expectException('\moodle_exception');
 785          $this->expectExceptionMessage(get_string('anonymous', 'feedback'));
 786          mod_feedback_external::get_non_respondents($this->feedback->id);
 787      }
 788  
 789      /**
 790       * Test get_non_respondents.
 791       */
 792      public function test_get_non_respondents() {
 793          global $DB;
 794  
 795          // Force non anonymous.
 796          $DB->set_field('feedback', 'anonymous', FEEDBACK_ANONYMOUS_NO, array('id' => $this->feedback->id));
 797  
 798          // Create another student.
 799          $anotherstudent = self::getDataGenerator()->create_user();
 800          $this->getDataGenerator()->enrol_user($anotherstudent->id, $this->course->id, $this->studentrole->id, 'manual');
 801          $this->setUser($anotherstudent);
 802  
 803          // Test user with full capabilities.
 804          $this->setUser($this->student);
 805  
 806          // Create a very simple feedback.
 807          $feedbackgenerator = $this->getDataGenerator()->get_plugin_generator('mod_feedback');
 808          $numericitem = $feedbackgenerator->create_item_numeric($this->feedback);
 809  
 810          $pagedata = [
 811              ['name' => $numericitem->typ .'_'. $numericitem->id, 'value' => 5],
 812          ];
 813  
 814          // Process the feedback, there is only one page so the feedback will be completed.
 815          $result = mod_feedback_external::process_page($this->feedback->id, 0, $pagedata);
 816          $result = \external_api::clean_returnvalue(mod_feedback_external::process_page_returns(), $result);
 817          $this->assertTrue($result['completed']);
 818  
 819          // Retrieve the non-respondent users.
 820          $this->setUser($this->teacher);
 821          $result = mod_feedback_external::get_non_respondents($this->feedback->id);
 822          $result = \external_api::clean_returnvalue(mod_feedback_external::get_non_respondents_returns(), $result);
 823          $this->assertCount(0, $result['warnings']);
 824          $this->assertCount(1, $result['users']);
 825          $this->assertEquals($anotherstudent->id, $result['users'][0]['userid']);
 826  
 827          // Create another student.
 828          $anotherstudent2 = self::getDataGenerator()->create_user();
 829          $this->getDataGenerator()->enrol_user($anotherstudent2->id, $this->course->id, $this->studentrole->id, 'manual');
 830          $this->setUser($anotherstudent2);
 831          $this->setUser($this->teacher);
 832          $result = mod_feedback_external::get_non_respondents($this->feedback->id);
 833          $result = \external_api::clean_returnvalue(mod_feedback_external::get_non_respondents_returns(), $result);
 834          $this->assertCount(0, $result['warnings']);
 835          $this->assertCount(2, $result['users']);
 836  
 837          // Test pagination.
 838          $result = mod_feedback_external::get_non_respondents($this->feedback->id, 0, 'lastaccess', 0, 1);
 839          $result = \external_api::clean_returnvalue(mod_feedback_external::get_non_respondents_returns(), $result);
 840          $this->assertCount(0, $result['warnings']);
 841          $this->assertCount(1, $result['users']);
 842      }
 843  
 844      /**
 845       * Helper function that completes the feedback for two students.
 846       */
 847      protected function complete_basic_feedback() {
 848          global $DB;
 849  
 850          $generator = $this->getDataGenerator();
 851          // Create separated groups.
 852          $DB->set_field('course', 'groupmode', SEPARATEGROUPS);
 853          $DB->set_field('course', 'groupmodeforce', 1);
 854          assign_capability('moodle/site:accessallgroups', CAP_PROHIBIT, $this->teacherrole->id, $this->context);
 855          accesslib_clear_all_caches_for_unit_testing();
 856  
 857          $group1 = $generator->create_group(array('courseid' => $this->course->id));
 858          $group2 = $generator->create_group(array('courseid' => $this->course->id));
 859  
 860          // Create another students.
 861          $anotherstudent1 = self::getDataGenerator()->create_user();
 862          $anotherstudent2 = self::getDataGenerator()->create_user();
 863          $generator->enrol_user($anotherstudent1->id, $this->course->id, $this->studentrole->id, 'manual');
 864          $generator->enrol_user($anotherstudent2->id, $this->course->id, $this->studentrole->id, 'manual');
 865  
 866          $generator->create_group_member(array('groupid' => $group1->id, 'userid' => $this->student->id));
 867          $generator->create_group_member(array('groupid' => $group1->id, 'userid' => $this->teacher->id));
 868          $generator->create_group_member(array('groupid' => $group1->id, 'userid' => $anotherstudent1->id));
 869          $generator->create_group_member(array('groupid' => $group2->id, 'userid' => $anotherstudent2->id));
 870  
 871          // Test user with full capabilities.
 872          $this->setUser($this->student);
 873  
 874          // Create a very simple feedback.
 875          $feedbackgenerator = $generator->get_plugin_generator('mod_feedback');
 876          $numericitem = $feedbackgenerator->create_item_numeric($this->feedback);
 877          $textfielditem = $feedbackgenerator->create_item_textfield($this->feedback);
 878  
 879          $pagedata = [
 880              ['name' => $numericitem->typ .'_'. $numericitem->id, 'value' => 5],
 881              ['name' => $textfielditem->typ .'_'. $textfielditem->id, 'value' => 'abc'],
 882          ];
 883  
 884          // Process the feedback, there is only one page so the feedback will be completed.
 885          $result = mod_feedback_external::process_page($this->feedback->id, 0, $pagedata);
 886          $result = \external_api::clean_returnvalue(mod_feedback_external::process_page_returns(), $result);
 887          $this->assertTrue($result['completed']);
 888  
 889          $this->setUser($anotherstudent1);
 890  
 891          $pagedata = [
 892              ['name' => $numericitem->typ .'_'. $numericitem->id, 'value' => 10],
 893              ['name' => $textfielditem->typ .'_'. $textfielditem->id, 'value' => 'def'],
 894          ];
 895  
 896          $result = mod_feedback_external::process_page($this->feedback->id, 0, $pagedata);
 897          $result = \external_api::clean_returnvalue(mod_feedback_external::process_page_returns(), $result);
 898          $this->assertTrue($result['completed']);
 899  
 900          $this->setUser($anotherstudent2);
 901  
 902          $pagedata = [
 903              ['name' => $numericitem->typ .'_'. $numericitem->id, 'value' => 10],
 904              ['name' => $textfielditem->typ .'_'. $textfielditem->id, 'value' => 'def'],
 905          ];
 906  
 907          $result = mod_feedback_external::process_page($this->feedback->id, 0, $pagedata);
 908          $result = \external_api::clean_returnvalue(mod_feedback_external::process_page_returns(), $result);
 909          $this->assertTrue($result['completed']);
 910      }
 911  
 912      /**
 913       * Test get_responses_analysis for anonymous feedback.
 914       */
 915      public function test_get_responses_analysis_anonymous() {
 916          self::complete_basic_feedback();
 917  
 918          // Retrieve the responses analysis.
 919          $this->setUser($this->teacher);
 920          $result = mod_feedback_external::get_responses_analysis($this->feedback->id);
 921          $result = \external_api::clean_returnvalue(mod_feedback_external::get_responses_analysis_returns(), $result);
 922          $this->assertCount(0, $result['warnings']);
 923          $this->assertEquals(0, $result['totalattempts']);
 924          $this->assertEquals(2, $result['totalanonattempts']);   // Only see my groups.
 925  
 926          foreach ($result['attempts'] as $attempt) {
 927              $this->assertEmpty($attempt['userid']); // Is anonymous.
 928          }
 929      }
 930  
 931      /**
 932       * Test get_responses_analysis for non-anonymous feedback.
 933       */
 934      public function test_get_responses_analysis_non_anonymous() {
 935          global $DB;
 936  
 937          // Force non anonymous.
 938          $DB->set_field('feedback', 'anonymous', FEEDBACK_ANONYMOUS_NO, array('id' => $this->feedback->id));
 939  
 940          self::complete_basic_feedback();
 941          // Retrieve the responses analysis.
 942          $this->setUser($this->teacher);
 943          $result = mod_feedback_external::get_responses_analysis($this->feedback->id);
 944          $result = \external_api::clean_returnvalue(mod_feedback_external::get_responses_analysis_returns(), $result);
 945          $this->assertCount(0, $result['warnings']);
 946          $this->assertEquals(2, $result['totalattempts']);
 947          $this->assertEquals(0, $result['totalanonattempts']);   // Only see my groups.
 948  
 949          foreach ($result['attempts'] as $attempt) {
 950              $this->assertNotEmpty($attempt['userid']);  // Is not anonymous.
 951          }
 952      }
 953  
 954      /**
 955       * Test get_last_completed for feedback anonymous not completed.
 956       */
 957      public function test_get_last_completed_anonymous_not_completed() {
 958          global $DB;
 959  
 960          // Force anonymous.
 961          $DB->set_field('feedback', 'anonymous', FEEDBACK_ANONYMOUS_YES, array('id' => $this->feedback->id));
 962  
 963          // Test user with full capabilities that didn't complete the feedback.
 964          $this->setUser($this->student);
 965  
 966          $this->expectExceptionMessage(get_string('anonymous', 'feedback'));
 967          $this->expectException('\moodle_exception');
 968          mod_feedback_external::get_last_completed($this->feedback->id);
 969      }
 970  
 971      /**
 972       * Test get_last_completed for feedback anonymous and completed.
 973       */
 974      public function test_get_last_completed_anonymous_completed() {
 975          global $DB;
 976  
 977          // Force anonymous.
 978          $DB->set_field('feedback', 'anonymous', FEEDBACK_ANONYMOUS_YES, array('id' => $this->feedback->id));
 979          // Add one completion record..
 980          $record = [
 981              'feedback' => $this->feedback->id,
 982              'userid' => $this->student->id,
 983              'timemodified' => time() - DAYSECS,
 984              'random_response' => 0,
 985              'anonymous_response' => FEEDBACK_ANONYMOUS_YES,
 986              'courseid' => $this->course->id,
 987          ];
 988          $record['id'] = $DB->insert_record('feedback_completed', (object) $record);
 989  
 990          // Test user with full capabilities.
 991          $this->setUser($this->student);
 992  
 993          $this->expectExceptionMessage(get_string('anonymous', 'feedback'));
 994          $this->expectException('\moodle_exception');
 995          mod_feedback_external::get_last_completed($this->feedback->id);
 996      }
 997  
 998      /**
 999       * Test get_last_completed for feedback not anonymous and completed.
1000       */
1001      public function test_get_last_completed_not_anonymous_completed() {
1002          global $DB;
1003  
1004          // Force non anonymous.
1005          $DB->set_field('feedback', 'anonymous', FEEDBACK_ANONYMOUS_NO, array('id' => $this->feedback->id));
1006          // Add one completion record..
1007          $record = [
1008              'feedback' => $this->feedback->id,
1009              'userid' => $this->student->id,
1010              'timemodified' => time() - DAYSECS,
1011              'random_response' => 0,
1012              'anonymous_response' => FEEDBACK_ANONYMOUS_NO,
1013              'courseid' => $this->course->id,
1014          ];
1015          $record['id'] = $DB->insert_record('feedback_completed', (object) $record);
1016  
1017          // Test user with full capabilities.
1018          $this->setUser($this->student);
1019          $result = mod_feedback_external::get_last_completed($this->feedback->id);
1020          $result = \external_api::clean_returnvalue(mod_feedback_external::get_last_completed_returns(), $result);
1021          $this->assertEquals($record, $result['completed']);
1022      }
1023  
1024      /**
1025       * Test get_last_completed for feedback not anonymous and not completed.
1026       */
1027      public function test_get_last_completed_not_anonymous_not_completed() {
1028          global $DB;
1029  
1030          // Force anonymous.
1031          $DB->set_field('feedback', 'anonymous', FEEDBACK_ANONYMOUS_NO, array('id' => $this->feedback->id));
1032  
1033          // Test user with full capabilities that didn't complete the feedback.
1034          $this->setUser($this->student);
1035  
1036          $this->expectExceptionMessage(get_string('not_completed_yet', 'feedback'));
1037          $this->expectException('\moodle_exception');
1038          mod_feedback_external::get_last_completed($this->feedback->id);
1039      }
1040  
1041      /**
1042       * Test get_feedback_access_information for site feedback.
1043       */
1044      public function test_get_feedback_access_information_for_site_feedback() {
1045  
1046          $sitefeedback = $this->getDataGenerator()->create_module('feedback', array('course' => SITEID));
1047          $this->setUser($this->student);
1048          // Access the site feedback via the site activity.
1049          $result = mod_feedback_external::get_feedback_access_information($sitefeedback->id);
1050          $result = \external_api::clean_returnvalue(mod_feedback_external::get_feedback_access_information_returns(), $result);
1051          $this->assertTrue($result['cancomplete']);
1052          $this->assertTrue($result['cansubmit']);
1053  
1054          // Access the site feedback via course where I'm enrolled.
1055          $result = mod_feedback_external::get_feedback_access_information($sitefeedback->id, $this->course->id);
1056          $result = \external_api::clean_returnvalue(mod_feedback_external::get_feedback_access_information_returns(), $result);
1057          $this->assertTrue($result['cancomplete']);
1058          $this->assertTrue($result['cansubmit']);
1059  
1060          // Access the site feedback via course where I'm not enrolled.
1061          $othercourse = $this->getDataGenerator()->create_course();
1062  
1063          $this->expectException('\moodle_exception');
1064          mod_feedback_external::get_feedback_access_information($sitefeedback->id, $othercourse->id);
1065      }
1066  
1067      /**
1068       * Test get_feedback_access_information for site feedback mapped.
1069       */
1070      public function test_get_feedback_access_information_for_site_feedback_mapped() {
1071          global $DB;
1072  
1073          $sitefeedback = $this->getDataGenerator()->create_module('feedback', array('course' => SITEID));
1074          $this->setUser($this->student);
1075          $DB->insert_record('feedback_sitecourse_map', array('feedbackid' => $sitefeedback->id, 'courseid' => $this->course->id));
1076  
1077          // Access the site feedback via course where I'm enrolled and mapped.
1078          $result = mod_feedback_external::get_feedback_access_information($sitefeedback->id, $this->course->id);
1079          $result = \external_api::clean_returnvalue(mod_feedback_external::get_feedback_access_information_returns(), $result);
1080          $this->assertTrue($result['cancomplete']);
1081          $this->assertTrue($result['cansubmit']);
1082  
1083          // Access the site feedback via course where I'm enrolled but not mapped.
1084          $othercourse = $this->getDataGenerator()->create_course();
1085          $this->getDataGenerator()->enrol_user($this->student->id, $othercourse->id, $this->studentrole->id, 'manual');
1086  
1087          $this->expectException('\moodle_exception');
1088          $this->expectExceptionMessage(get_string('cannotaccess', 'mod_feedback'));
1089          mod_feedback_external::get_feedback_access_information($sitefeedback->id, $othercourse->id);
1090      }
1091  }