Search moodle.org's
Developer Documentation

See Release Notes
Long Term Support Release

  • Bug fixes for general core bugs in 4.1.x will end 13 November 2023 (12 months).
  • Bug fixes for security issues in 4.1.x will end 10 November 2025 (36 months).
  • PHP version: minimum PHP 7.4.0 Note: minimum PHP version has increased since Moodle 4.0. PHP 8.0.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  namespace core_courseformat\external;
  18  
  19  use stdClass;
  20  use moodle_exception;
  21  
  22  defined('MOODLE_INTERNAL') || die();
  23  
  24  global $CFG;
  25  require_once($CFG->dirroot . '/webservice/tests/helpers.php');
  26  
  27  /**
  28   * Tests for the update_course class.
  29   *
  30   * @package    core_course
  31   * @category   test
  32   * @copyright  2021 Sara Arjona (sara@moodle.com)
  33   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  34   * @coversDefaultClass \core_courseformat\external\update_course
  35   */
  36  class update_course_test extends \externallib_advanced_testcase {
  37  
  38      /**
  39       * Setup to ensure that fixtures are loaded.
  40       */
  41      public static function setupBeforeClass(): void {
  42          global $CFG;
  43  
  44          require_once($CFG->dirroot . '/course/format/tests/fixtures/format_theunittest.php');
  45          require_once($CFG->dirroot . '/course/format/tests/fixtures/format_theunittest_output_course_format_state.php');
  46          require_once($CFG->dirroot . '/course/format/tests/fixtures/format_theunittest_stateactions.php');
  47      }
  48  
  49      /**
  50       * Test the webservice can execute a core state action (cm_state).
  51       *
  52       * @dataProvider execute_course_state_provider
  53       * @covers ::execute
  54       *
  55       * @param string $format the course format
  56       * @param string $action the state action name
  57       * @param array $expected the expected results
  58       * @param bool $expectexception if an exception should happen.
  59       * @param bool $assertdebug if an debug message should happen.
  60       */
  61      public function test_execute_course_state(
  62          string $format,
  63          string $action,
  64          array $expected,
  65          bool $expectexception,
  66          bool $assertdebug
  67      ) {
  68  
  69          $this->resetAfterTest();
  70  
  71          // Create a course with two activities.
  72          $course = $this->getDataGenerator()->create_course(['format' => $format]);
  73          $activity = $this->getDataGenerator()->create_module('book', ['course' => $course->id]);
  74  
  75          $this->setAdminUser();
  76  
  77          // Expect exception.
  78          if ($expectexception) {
  79              $this->expectException(moodle_exception::class);
  80          }
  81  
  82          // Execute course action.
  83          $results = json_decode(update_course::execute($action, $course->id, [$activity->cmid]));
  84  
  85          if ($assertdebug) {
  86              // Some course formats hasn't the renderer file, so a debugging message will be displayed.
  87              $this->assertDebuggingCalled();
  88          }
  89  
  90          // Check result.
  91          $this->assertCount($expected['count'], $results);
  92  
  93          $update = $this->find_update($results, $expected['action'], 'cm', $activity->cmid);
  94          $this->assertNotEmpty($update);
  95          if ($expected['visible'] === null) {
  96              $this->assertObjectNotHasAttribute('visible', $update->fields);
  97          } else {
  98              $this->assertEquals($expected['visible'], $update->fields->visible);
  99          }
 100      }
 101  
 102      /**
 103       * Data provider for test_execute_course_state
 104       *
 105       * @return array of testing scenarios
 106       */
 107      public function execute_course_state_provider(): array {
 108          return [
 109              'Execute a core state action (cm_state)' => [
 110                  'format' => 'topics',
 111                  'action' => 'cm_state',
 112                  'expected' => [
 113                      'count' => 2,
 114                      'action' => 'put',
 115                      'visible' => 1,
 116                  ],
 117                  'expectexception' => false,
 118                  'assertdebug' => false,
 119              ],
 120              'Formats can override core state actions' => [
 121                  'format' => 'theunittest',
 122                  'action' => 'cm_state',
 123                  'expected' => [
 124                      'count' => 1,
 125                      'action' => 'create',
 126                      'visible' => 1,
 127                  ],
 128                  'expectexception' => false,
 129                  'assertdebug' => true,
 130              ],
 131              'Formats can create new state actions' => [
 132                  'format' => 'theunittest',
 133                  'action' => 'format_do_something',
 134                  'expected' => [
 135                      'count' => 1,
 136                      'action' => 'remove',
 137                      'visible' => null,
 138                  ],
 139                  'expectexception' => false,
 140                  'assertdebug' => true,
 141              ],
 142              'Innexisting state action' => [
 143                  'format' => 'topics',
 144                  'action' => 'Wrong_State_Action_Name',
 145                  'expected' => [],
 146                  'expectexception' => true,
 147                  'assertdebug' => false,
 148              ],
 149          ];
 150      }
 151  
 152      /**
 153       * Helper methods to find a specific update in the updadelist.
 154       *
 155       * @param array $updatelist the update list
 156       * @param string $action the action to find
 157       * @param string $name the element name to find
 158       * @param int $identifier the element id value
 159       * @return stdClass|null the object found, if any.
 160       */
 161      private function find_update(
 162          array $updatelist,
 163          string $action,
 164          string $name,
 165          int $identifier
 166      ): ?stdClass {
 167          foreach ($updatelist as $update) {
 168              if ($update->action != $action || $update->name != $name) {
 169                  continue;
 170              }
 171              if (!isset($update->fields->id)) {
 172                  continue;
 173              }
 174              if ($update->fields->id == $identifier) {
 175                  return $update;
 176              }
 177          }
 178          return null;
 179      }
 180  
 181      /**
 182       * Test a wrong course id.
 183       *
 184       * @covers ::execute
 185       *
 186       */
 187      public function test_execute_wrong_courseid() {
 188  
 189          $this->resetAfterTest();
 190  
 191          // Create a course with two activities.
 192          $course = $this->getDataGenerator()->create_course(['format' => 'topics']);
 193          $activity = $this->getDataGenerator()->create_module('book', ['course' => $course->id]);
 194  
 195          $this->setAdminUser();
 196  
 197          // Expect exception.
 198          $this->expectException(moodle_exception::class);
 199  
 200          // Execute course action.
 201          $results = json_decode(update_course::execute('cm_state', $course->id + 1, [$activity->cmid]));
 202      }
 203  
 204      /**
 205       * Test target params are passed to the state actions.
 206       *
 207       * @covers ::execute
 208       */
 209      public function test_execute_target_params() {
 210  
 211          $this->resetAfterTest();
 212  
 213          // Create a course with two activities.
 214          $course = $this->getDataGenerator()->create_course(['format' => 'theunittest', 'numsections' => 2]);
 215          $activity = $this->getDataGenerator()->create_module('book', ['course' => $course->id]);
 216  
 217          $modinfo = get_fast_modinfo($course);
 218          $section = $modinfo->get_section_info(1);
 219  
 220          $this->setAdminUser();
 221  
 222          // Execute action with targetsectionid.
 223          $results = json_decode(update_course::execute('targetsection_test', $course->id, [], $section->id));
 224          $this->assertDebuggingCalled();
 225          $this->assertCount(1, $results);
 226          $update = $this->find_update($results, 'put', 'section', $section->id);
 227          $this->assertNotEmpty($update);
 228  
 229          // Execute action with targetcmid.
 230          $results = json_decode(update_course::execute('targetcm_test', $course->id, [], null, $activity->cmid));
 231          $this->assertDebuggingCalled();
 232          $this->assertCount(1, $results);
 233          $update = $this->find_update($results, 'put', 'cm', $activity->cmid);
 234          $this->assertNotEmpty($update);
 235      }
 236  }