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.

Differences Between: [Versions 310 and 401] [Versions 39 and 401]

   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 mod_data;
  18  
  19  /**
  20   * Unit tests for import.php.
  21   *
  22   * @package    mod_data
  23   * @category   test
  24   * @copyright  2019 Tobias Reischmann
  25   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  26   */
  27  class import_test extends \advanced_testcase {
  28  
  29      /**
  30       * Set up function.
  31       */
  32      protected function setUp(): void {
  33          parent::setUp();
  34  
  35          global $CFG;
  36          require_once($CFG->dirroot . '/mod/data/lib.php');
  37          require_once($CFG->dirroot . '/lib/datalib.php');
  38          require_once($CFG->dirroot . '/lib/csvlib.class.php');
  39          require_once($CFG->dirroot . '/search/tests/fixtures/testable_core_search.php');
  40          require_once($CFG->dirroot . '/mod/data/tests/generator/lib.php');
  41      }
  42  
  43      /**
  44       * Get the test data.
  45       * In this instance we are setting up database records to be used in the unit tests.
  46       *
  47       * @return array
  48       */
  49      protected function get_test_data(): array {
  50          $this->resetAfterTest(true);
  51  
  52          $generator = $this->getDataGenerator()->get_plugin_generator('mod_data');
  53          $course = $this->getDataGenerator()->create_course();
  54          $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
  55          $this->setUser($teacher);
  56          $student = $this->getDataGenerator()->create_and_enrol($course, 'student', array('username' => 'student'));
  57  
  58          $data = $generator->create_instance(array('course' => $course->id));
  59          $cm = get_coursemodule_from_instance('data', $data->id);
  60  
  61          // Add fields.
  62          $fieldrecord = new \stdClass();
  63          $fieldrecord->name = 'ID'; // Identifier of the records for testing.
  64          $fieldrecord->type = 'number';
  65          $generator->create_field($fieldrecord, $data);
  66  
  67          $fieldrecord->name = 'Param2';
  68          $fieldrecord->type = 'text';
  69          $generator->create_field($fieldrecord, $data);
  70  
  71  
  72          return [
  73              'teacher' => $teacher,
  74              'student' => $student,
  75              'data' => $data,
  76              'cm' => $cm,
  77          ];
  78      }
  79  
  80      /**
  81       * Test uploading entries for a data instance without userdata.
  82       * @throws dml_exception
  83       */
  84      public function test_import(): void {
  85          [
  86              'data' => $data,
  87              'cm' => $cm,
  88              'teacher' => $teacher,
  89          ] = $this->get_test_data();
  90  
  91          $filecontent = file_get_contents(__DIR__ . '/fixtures/test_data_import.csv');
  92          ob_start();
  93          data_import_csv($cm, $data, $filecontent, 'UTF-8', 'comma');
  94          ob_end_clean();
  95  
  96          // No userdata is present in the file: Fallback is to assign the uploading user as author.
  97          $expecteduserids = array();
  98          $expecteduserids[1] = $teacher->id;
  99          $expecteduserids[2] = $teacher->id;
 100  
 101          $records = $this->get_data_records($data->id);
 102          $this->assertCount(2, $records);
 103          foreach ($records as $record) {
 104              $identifier = $record->items['ID']->content;
 105              $this->assertEquals($expecteduserids[$identifier], $record->userid);
 106          }
 107      }
 108  
 109      /**
 110       * Test uploading entries for a data instance with userdata.
 111       *
 112       * At least one entry has an identifiable user, which is assigned as author.
 113       * @throws dml_exception
 114       */
 115      public function test_import_with_userdata(): void {
 116          [
 117              'data' => $data,
 118              'cm' => $cm,
 119              'teacher' => $teacher,
 120              'student' => $student,
 121          ] = $this->get_test_data();
 122  
 123          $filecontent = file_get_contents(__DIR__ . '/fixtures/test_data_import_with_userdata.csv');
 124          ob_start();
 125          data_import_csv($cm, $data, $filecontent, 'UTF-8', 'comma');
 126          ob_end_clean();
 127  
 128          $expecteduserids = array();
 129          $expecteduserids[1] = $student->id; // User student exists and is assigned as author.
 130          $expecteduserids[2] = $teacher->id; // User student2 does not exist. Fallback is the uploading user.
 131  
 132          $records = $this->get_data_records($data->id);
 133          $this->assertCount(2, $records);
 134          foreach ($records as $record) {
 135              $identifier = $record->items['ID']->content;
 136              $this->assertEquals($expecteduserids[$identifier], $record->userid);
 137          }
 138      }
 139  
 140      /**
 141       * Test uploading entries for a data instance with userdata and a defined field 'Username'.
 142       *
 143       * This should test the corner case, in which a user has defined a data fields, which has the same name
 144       * as the current lang string for username. In that case, the first Username entry is used for the field.
 145       * The second one is used to identify the author.
 146       * @throws coding_exception
 147       * @throws dml_exception
 148       */
 149      public function test_import_with_field_username(): void {
 150          [
 151              'data' => $data,
 152              'cm' => $cm,
 153              'teacher' => $teacher,
 154              'student' => $student,
 155          ] = $this->get_test_data();
 156          $generator = $this->getDataGenerator()->get_plugin_generator('mod_data');
 157  
 158          // Add username field.
 159          $fieldrecord = new \stdClass();
 160          $fieldrecord->name = 'Username';
 161          $fieldrecord->type = 'text';
 162          $generator->create_field($fieldrecord, $data);
 163  
 164          $filecontent = file_get_contents(__DIR__ . '/fixtures/test_data_import_with_field_username.csv');
 165          ob_start();
 166          data_import_csv($cm, $data, $filecontent, 'UTF-8', 'comma');
 167          ob_end_clean();
 168  
 169          $expecteduserids = array();
 170          $expecteduserids[1] = $student->id; // User student exists and is assigned as author.
 171          $expecteduserids[2] = $teacher->id; // User student2 does not exist. Fallback is the uploading user.
 172          $expecteduserids[3] = $student->id; // User student exists and is assigned as author.
 173  
 174          $expectedcontent = array();
 175          $expectedcontent[1] = array(
 176              'Username' => 'otherusername1',
 177              'Param2' => 'My first entry',
 178          );
 179          $expectedcontent[2] = array(
 180              'Username' => 'otherusername2',
 181              'Param2' => 'My second entry',
 182          );
 183          $expectedcontent[3] = array(
 184              'Username' => 'otherusername3',
 185              'Param2' => 'My third entry',
 186          );
 187  
 188          $records = $this->get_data_records($data->id);
 189          $this->assertCount(3, $records);
 190          foreach ($records as $record) {
 191              $identifier = $record->items['ID']->content;
 192              $this->assertEquals($expecteduserids[$identifier], $record->userid);
 193  
 194              foreach ($expectedcontent[$identifier] as $field => $value) {
 195                  $this->assertEquals($value, $record->items[$field]->content,
 196                      "The value of field \"$field\" for the record at position \"$identifier\" ".
 197                      "which is \"{$record->items[$field]->content}\" does not match the expected value \"$value\".");
 198              }
 199          }
 200      }
 201  
 202      /**
 203       * Test uploading entries for a data instance with a field 'Username' but only one occurrence in the csv file.
 204       *
 205       * This should test the corner case, in which a user has defined a data fields, which has the same name
 206       * as the current lang string for username. In that case, the only Username entry is used for the field.
 207       * The author should not be set.
 208       * @throws coding_exception
 209       * @throws dml_exception
 210       */
 211      public function test_import_with_field_username_without_userdata(): void {
 212          [
 213              'data' => $data,
 214              'cm' => $cm,
 215              'teacher' => $teacher,
 216              'student' => $student,
 217          ] = $this->get_test_data();
 218          $generator = $this->getDataGenerator()->get_plugin_generator('mod_data');
 219  
 220          // Add username field.
 221          $fieldrecord = new \stdClass();
 222          $fieldrecord->name = 'Username';
 223          $fieldrecord->type = 'text';
 224          $generator->create_field($fieldrecord, $data);
 225  
 226          $filecontent = file_get_contents(__DIR__ . '/fixtures/test_data_import_with_userdata.csv');
 227          ob_start();
 228          data_import_csv($cm, $data, $filecontent, 'UTF-8', 'comma');
 229          ob_end_clean();
 230  
 231          // No userdata is present in the file: Fallback is to assign the uploading user as author.
 232          $expecteduserids = array();
 233          $expecteduserids[1] = $teacher->id;
 234          $expecteduserids[2] = $teacher->id;
 235  
 236          $expectedcontent = array();
 237          $expectedcontent[1] = array(
 238              'Username' => 'student',
 239              'Param2' => 'My first entry',
 240          );
 241          $expectedcontent[2] = array(
 242              'Username' => 'student2',
 243              'Param2' => 'My second entry',
 244          );
 245  
 246          $records = $this->get_data_records($data->id);
 247          $this->assertCount(2, $records);
 248          foreach ($records as $record) {
 249              $identifier = $record->items['ID']->content;
 250              $this->assertEquals($expecteduserids[$identifier], $record->userid);
 251  
 252              foreach ($expectedcontent[$identifier] as $field => $value) {
 253                  $this->assertEquals($value, $record->items[$field]->content,
 254                      "The value of field \"$field\" for the record at position \"$identifier\" ".
 255                      "which is \"{$record->items[$field]->content}\" does not match the expected value \"$value\".");
 256              }
 257          }
 258      }
 259  
 260      /**
 261       * Returns the records of the data instance.
 262       *
 263       * Each records has an item entry, which contains all fields associated with this item.
 264       * Each fields has the parameters name, type and content.
 265       * @param int $dataid Id of the data instance.
 266       * @return array The records of the data instance.
 267       * @throws dml_exception
 268       */
 269      private function get_data_records(int $dataid): array {
 270          global $DB;
 271  
 272          $records = $DB->get_records('data_records', ['dataid' => $dataid]);
 273          foreach ($records as $record) {
 274              $sql = 'SELECT f.name, f.type, con.content FROM
 275                  {data_content} con JOIN {data_fields} f ON con.fieldid = f.id
 276                  WHERE con.recordid = :recordid';
 277              $items = $DB->get_records_sql($sql, array('recordid' => $record->id));
 278              $record->items = $items;
 279          }
 280          return $records;
 281      }
 282  }