Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 3.10.x will end 8 November 2021 (12 months).
  • Bug fixes for security issues in 3.10.x will end 9 May 2022 (18 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 310]

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