Search moodle.org's
Developer Documentation

  • 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.
  •    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  }