Search moodle.org's
Developer Documentation

See Release Notes
Long Term Support Release

  • Bug fixes for general core bugs in 3.9.x will end* 10 May 2021 (12 months).
  • Bug fixes for security issues in 3.9.x will end* 8 May 2023 (36 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 311] [Versions 39 and 400] [Versions 39 and 401] [Versions 39 and 402] [Versions 39 and 403]

   1  <?php
   2  // This file is part of Moodle - http://moodle.org/
   3  //
   4  // Moodle is free software: you can redistribute it and/or modify
   5  // it under the terms of the GNU General Public License as published by
   6  // the Free Software Foundation, either version 3 of the License, or
   7  // (at your option) any later version.
   8  //
   9  // Moodle is distributed in the hope that it will be useful,
  10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12  // GNU General Public License for more details.
  13  //
  14  // You should have received a copy of the GNU General Public License
  15  // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
  16  
  17  /**
  18   * Unit Tests for the Content Writer used for unit testing.
  19   *
  20   * @package     core_privacy
  21   * @category    test
  22   * @copyright   2018 Andrew Nicols <andrew@nicols.co.uk>
  23   * @license     http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  24   */
  25  
  26  defined('MOODLE_INTERNAL') || die();
  27  
  28  global $CFG;
  29  
  30  use \core_privacy\local\request\writer;
  31  use \core_privacy\tests\request\content_writer;
  32  
  33  /**
  34   * Unit Tests for the Content Writer used for unit testing.
  35   *
  36   * @copyright   2018 Andrew Nicols <andrew@nicols.co.uk>
  37   * @license     http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  38   */
  39  class tests_content_writer_test extends advanced_testcase {
  40  
  41      /**
  42       * It should be possible to store and retrieve data.
  43       */
  44      public function test_export_data() {
  45          $context = \context_system::instance();
  46          $writer = $this->get_writer_instance();
  47  
  48          $dataa = (object) [
  49              'example' => 'a',
  50          ];
  51          $datab = (object) [
  52              'example' => 'b',
  53          ];
  54  
  55          $writer->set_context($context)
  56              ->export_data(['data'], $dataa)
  57              ->export_data([], $datab);
  58  
  59          $data = $writer->get_data([]);
  60          $this->assertSame($datab, $data);
  61  
  62          $data = $writer->get_data(['data']);
  63          $this->assertSame($dataa, $data);
  64      }
  65  
  66      /**
  67       * It should be possible to store and retrieve data at the same point in different contexts.
  68       */
  69      public function test_export_data_no_context_clash() {
  70          $writer = $this->get_writer_instance();
  71  
  72          $context = \context_system::instance();
  73          $dataa = (object) [
  74              'example' => 'a',
  75          ];
  76          $writer->set_context($context)
  77              ->export_data(['data'], $dataa);
  78  
  79          $adminuser = \core_user::get_user_by_username('admin');
  80          $usercontext = \context_user::instance($adminuser->id);
  81          $datab = (object) [
  82              'example' => 'b',
  83          ];
  84          $writer->set_context($usercontext)
  85              ->export_data(['data'], $datab);
  86  
  87          $writer->set_context($context);
  88          $data = $writer->get_data(['data']);
  89          $this->assertSame($dataa, $data);
  90          $this->assertTrue($writer->has_any_data());
  91          $this->assertTrue($writer->has_any_data(['data']));
  92          $this->assertFalse($writer->has_any_data(['somepath']));
  93  
  94          $writer->set_context($usercontext);
  95          $data = $writer->get_data(['data']);
  96          $this->assertSame($datab, $data);
  97      }
  98  
  99      /**
 100       * Test export and recover with children.
 101       */
 102      public function test_get_data_with_children() {
 103          $writer = $this->get_writer_instance();
 104          $context = \context_system::instance();
 105  
 106          $writer->set_context($context)
 107              ->export_data(['a'], (object) ['parent' => true])
 108              ->export_data(['a', 'b'], (object) ['parent' => false]);
 109  
 110          $this->assertTrue($writer->get_data(['a'])->parent);
 111          $this->assertFalse($writer->get_data(['a', 'b'])->parent);
 112          $this->assertEquals([], $writer->get_data(['a', 'b', 'c']));
 113      }
 114  
 115      /**
 116       * It should be possible to store and retrieve metadata.
 117       */
 118      public function test_export_metadata() {
 119          $context = \context_system::instance();
 120          $writer = $this->get_writer_instance();
 121  
 122          $writer->set_context($context)
 123              ->export_metadata(['metadata'], 'somekey', 'value1', 'description1')
 124              ->export_metadata([], 'somekey', 'value2', 'description2');
 125  
 126          $allmetadata = $writer->get_all_metadata([]);
 127          $this->assertCount(1, $allmetadata);
 128          $this->assertArrayHasKey('somekey', $allmetadata);
 129          $this->assertEquals('value2', $allmetadata['somekey']->value);
 130          $this->assertEquals('description2', $allmetadata['somekey']->description);
 131  
 132          $metadata = $writer->get_metadata([], 'somekey', false);
 133          $this->assertEquals('value2', $metadata->value);
 134          $this->assertEquals('description2', $metadata->description);
 135          $this->assertEquals('value2', $writer->get_metadata([], 'somekey', true));
 136  
 137          $allmetadata = $writer->get_all_metadata(['metadata']);
 138          $this->assertCount(1, $allmetadata);
 139          $this->assertArrayHasKey('somekey', $allmetadata);
 140          $this->assertEquals('value1', $allmetadata['somekey']->value);
 141          $this->assertEquals('description1', $allmetadata['somekey']->description);
 142  
 143          $metadata = $writer->get_metadata(['metadata'], 'somekey', false);
 144          $this->assertEquals('value1', $metadata->value);
 145          $this->assertEquals('description1', $metadata->description);
 146          $this->assertEquals('value1', $writer->get_metadata(['metadata'], 'somekey', true));
 147      }
 148  
 149      /**
 150       * It should be possible to store and retrieve metadata at the same point in different contexts.
 151       */
 152      public function test_export_metadata_no_context_clash() {
 153          $writer = $this->get_writer_instance();
 154  
 155          $context = \context_system::instance();
 156          $writer->set_context($context)
 157              ->export_metadata(['metadata'], 'somekey', 'value1', 'description1');
 158  
 159          $adminuser = \core_user::get_user_by_username('admin');
 160          $usercontext = \context_user::instance($adminuser->id);
 161          $writer->set_context($usercontext)
 162              ->export_metadata(['metadata'], 'somekey', 'value2', 'description2');
 163  
 164          $writer->set_context($context);
 165          $allmetadata = $writer->get_all_metadata(['metadata']);
 166          $this->assertCount(1, $allmetadata);
 167          $this->assertArrayHasKey('somekey', $allmetadata);
 168          $this->assertEquals('value1', $allmetadata['somekey']->value);
 169          $this->assertEquals('description1', $allmetadata['somekey']->description);
 170  
 171          $metadata = $writer->get_metadata(['metadata'], 'somekey', false);
 172          $this->assertEquals('value1', $metadata->value);
 173          $this->assertEquals('description1', $metadata->description);
 174          $this->assertEquals('value1', $writer->get_metadata(['metadata'], 'somekey', true));
 175  
 176          $writer->set_context($usercontext);
 177          $allmetadata = $writer->get_all_metadata(['metadata']);
 178          $this->assertCount(1, $allmetadata);
 179          $this->assertArrayHasKey('somekey', $allmetadata);
 180          $this->assertEquals('value2', $allmetadata['somekey']->value);
 181          $this->assertEquals('description2', $allmetadata['somekey']->description);
 182  
 183          $metadata = $writer->get_metadata(['metadata'], 'somekey', false);
 184          $this->assertEquals('value2', $metadata->value);
 185          $this->assertEquals('description2', $metadata->description);
 186          $this->assertEquals('value2', $writer->get_metadata(['metadata'], 'somekey', true));
 187          $this->assertTrue($writer->has_any_data());
 188          $this->assertTrue($writer->has_any_data(['metadata']));
 189          $this->assertFalse($writer->has_any_data(['somepath']));
 190      }
 191  
 192      /**
 193       * It should be possible to store and retrieve user preferences.
 194       */
 195      public function test_export_user_preference() {
 196          $context = \context_system::instance();
 197          $adminuser = \core_user::get_user_by_username('admin');
 198          $usercontext = \context_user::instance($adminuser->id);
 199          $writer = $this->get_writer_instance();
 200  
 201          $writer->set_context($context)
 202              ->export_user_preference('core_privacy', 'somekey', 'value0', 'description0');
 203          $writer->set_context($usercontext)
 204              ->export_user_preference('core_tests', 'somekey', 'value1', 'description1')
 205              ->export_user_preference('core_privacy', 'somekey', 'value2', 'description2')
 206              ->export_user_preference('core_tests', 'someotherkey', 'value2', 'description2');
 207  
 208          $writer->set_context($usercontext);
 209  
 210          $someprefs = $writer->get_user_preferences('core_privacy');
 211          $this->assertCount(1, (array) $someprefs);
 212          $this->assertTrue(isset($someprefs->somekey));
 213          $this->assertEquals('value0', $someprefs->somekey->value);
 214          $this->assertEquals('description0', $someprefs->somekey->description);
 215  
 216          $someprefs = $writer->get_user_context_preferences('core_tests');
 217          $this->assertCount(2, (array) $someprefs);
 218          $this->assertTrue(isset($someprefs->somekey));
 219          $this->assertEquals('value1', $someprefs->somekey->value);
 220          $this->assertEquals('description1', $someprefs->somekey->description);
 221          $this->assertTrue(isset($someprefs->someotherkey));
 222          $this->assertEquals('value2', $someprefs->someotherkey->value);
 223          $this->assertEquals('description2', $someprefs->someotherkey->description);
 224  
 225          $someprefs = $writer->get_user_context_preferences('core_privacy');
 226          $this->assertCount(1, (array) $someprefs);
 227          $this->assertTrue(isset($someprefs->somekey));
 228          $this->assertEquals('value2', $someprefs->somekey->value);
 229          $this->assertEquals('description2', $someprefs->somekey->description);
 230      }
 231  
 232      /**
 233       * It should be possible to store and retrieve user preferences at the same point in different contexts.
 234       */
 235      public function test_export_user_preference_no_context_clash() {
 236          $writer = $this->get_writer_instance();
 237          $context = \context_system::instance();
 238          $coursecontext = \context_course::instance(SITEID);
 239          $adminuser = \core_user::get_user_by_username('admin');
 240          $usercontext = \context_user::instance($adminuser->id);
 241  
 242          $writer->set_context($context)
 243              ->export_user_preference('core_tests', 'somekey', 'value0', 'description0');
 244          $writer->set_context($coursecontext)
 245              ->export_user_preference('core_tests', 'somekey', 'value1', 'description1');
 246          $writer->set_context($usercontext)
 247              ->export_user_preference('core_tests', 'somekey', 'value2', 'description2');
 248  
 249          // Set the course context and fetch with get_user_preferences to get the global preference.
 250          $writer->set_context($coursecontext);
 251          $someprefs = $writer->get_user_preferences('core_tests');
 252          $this->assertCount(1, (array) $someprefs);
 253          $this->assertTrue(isset($someprefs->somekey));
 254          $this->assertEquals('value0', $someprefs->somekey->value);
 255          $this->assertEquals('description0', $someprefs->somekey->description);
 256  
 257          // Set the course context and fetch with get_user_context_preferences.
 258          $someprefs = $writer->get_user_context_preferences('core_tests');
 259          $this->assertCount(1, (array) $someprefs);
 260          $this->assertTrue(isset($someprefs->somekey));
 261          $this->assertEquals('value1', $someprefs->somekey->value);
 262          $this->assertEquals('description1', $someprefs->somekey->description);
 263  
 264          $writer->set_context($usercontext);
 265          $someprefs = $writer->get_user_context_preferences('core_tests');
 266          $this->assertCount(1, (array) $someprefs);
 267          $this->assertTrue(isset($someprefs->somekey));
 268          $this->assertEquals('value2', $someprefs->somekey->value);
 269          $this->assertEquals('description2', $someprefs->somekey->description);
 270      }
 271  
 272      /**
 273       * Test export and recover with children.
 274       */
 275      public function test_get_metadata_with_children() {
 276          $writer = $this->get_writer_instance();
 277          $context = \context_system::instance();
 278  
 279          $writer->set_context($context)
 280              ->export_metadata(['a'], 'abc', 'ABC', 'A, B, C')
 281              ->export_metadata(['a', 'b'], 'def', 'DEF', 'D, E, F');
 282  
 283          $this->assertEquals('ABC', $writer->get_metadata(['a'], 'abc'));
 284          $this->assertEquals('DEF', $writer->get_metadata(['a', 'b'], 'def'));
 285      }
 286  
 287      /**
 288       * It should be possible to export files in the files and children contexts.
 289       */
 290      public function test_export_file_special_folders() {
 291          $context = \context_system::instance();
 292  
 293          $filea = $this->get_stored_file('/', 'files');
 294          $fileb = $this->get_stored_file('/children/', 'foo.zip');
 295  
 296          $writer = $this->get_writer_instance()
 297              ->set_context($context)
 298              ->export_file([], $filea)
 299              ->export_file([], $fileb);
 300  
 301          $files = $writer->get_files([]);
 302  
 303          $this->assertCount(2, $files);
 304          $this->assertEquals($filea, $files['files']);
 305          $this->assertEquals($fileb, $files['children/foo.zip']);
 306      }
 307  
 308      /**
 309       * It should be possible to export mutliple files in the same subcontext/path space but different context and not
 310       * have them clash.
 311       */
 312      public function test_export_file_no_context_clash() {
 313          $writer = $this->get_writer_instance();
 314          $context = \context_system::instance();
 315          $filea = $this->get_stored_file('/foo/', 'foo.txt');
 316          $writer = $this->get_writer_instance()
 317              ->set_context($context)
 318              ->export_file([], $filea);
 319  
 320          $adminuser = \core_user::get_user_by_username('admin');
 321          $usercontext = \context_user::instance($adminuser->id);
 322          $fileb = $this->get_stored_file('/foo/', 'foo.txt');
 323          $writer->set_context($usercontext)
 324              ->export_file([], $fileb);
 325  
 326          $writer->set_context($context);
 327          $files = $writer->get_files([]);
 328          $this->assertCount(1, $files);
 329          $this->assertEquals($filea, $files['foo/foo.txt']);
 330  
 331          $writer->set_context($usercontext);
 332          $files = $writer->get_files([]);
 333          $this->assertCount(1, $files);
 334          $this->assertEquals($fileb, $files['foo/foo.txt']);
 335          $this->assertTrue($writer->has_any_data());
 336          $this->assertFalse($writer->has_any_data(['somepath']));
 337      }
 338  
 339      /**
 340       * Test export and recover with children.
 341       */
 342      public function test_get_file_with_children() {
 343          $writer = $this->get_writer_instance();
 344          $context = \context_system::instance();
 345  
 346          $filea = $this->get_stored_file('/foo/', 'foo.txt');
 347          $fileb = $this->get_stored_file('/foo/', 'foo.txt');
 348  
 349          $writer->set_context($context)
 350              ->export_file(['a'], $filea)
 351              ->export_file(['a', 'b'], $fileb);
 352  
 353          $files = $writer->get_files(['a']);
 354          $this->assertCount(1, $files);
 355          $this->assertEquals($filea, $files['foo/foo.txt']);
 356  
 357          $files = $writer->get_files(['a', 'b']);
 358          $this->assertCount(1, $files);
 359          $this->assertEquals($fileb, $files['foo/foo.txt']);
 360      }
 361  
 362      /**
 363       * It should be possible to export related data in the files and children contexts.
 364       */
 365      public function test_export_related_data() {
 366          $context = \context_system::instance();
 367  
 368          $writer = $this->get_writer_instance()
 369              ->set_context($context)
 370              ->export_related_data(['file', 'data'], 'file', 'data1')
 371              ->export_related_data([], 'file', 'data2');
 372  
 373          $data = $writer->get_related_data([]);
 374          $this->assertCount(1, $data);
 375          $this->assertEquals('data2', $data['file']);
 376  
 377          $data = $writer->get_related_data([], 'file');
 378          $this->assertEquals('data2', $data);
 379  
 380          $data = $writer->get_related_data(['file', 'data']);
 381          $this->assertCount(1, $data);
 382          $this->assertEquals('data1', $data['file']);
 383  
 384          $data = $writer->get_related_data(['file', 'data'], 'file');
 385          $this->assertEquals('data1', $data);
 386          $this->assertTrue($writer->has_any_data());
 387          $this->assertTrue($writer->has_any_data(['file']));
 388          $this->assertTrue($writer->has_any_data(['file', 'data']));
 389          $this->assertFalse($writer->has_any_data(['somepath']));
 390      }
 391  
 392      /**
 393       * It should be possible to export related data in the same location,but in a different context.
 394       */
 395      public function test_export_related_data_no_context_clash() {
 396          $writer = $this->get_writer_instance();
 397  
 398          $context = \context_system::instance();
 399          $writer->set_context($context)
 400              ->export_related_data(['file', 'data'], 'file', 'data1');
 401  
 402          $adminuser = \core_user::get_user_by_username('admin');
 403          $usercontext = \context_user::instance($adminuser->id);
 404          $writer->set_context($usercontext)
 405              ->export_related_data(['file', 'data'], 'file', 'data2');
 406  
 407          $writer->set_context($context);
 408          $data = $writer->get_related_data(['file', 'data']);
 409          $this->assertCount(1, $data);
 410          $this->assertEquals('data1', $data['file']);
 411  
 412          $writer->set_context($usercontext);
 413          $data = $writer->get_related_data(['file', 'data']);
 414          $this->assertCount(1, $data);
 415          $this->assertEquals('data2', $data['file']);
 416      }
 417  
 418      /**
 419       * Test export and recover with children.
 420       */
 421      public function test_get_related_data_with_children() {
 422          $writer = $this->get_writer_instance();
 423          $context = \context_system::instance();
 424  
 425          $writer->set_context($context)
 426              ->export_related_data(['a'], 'abc', 'ABC')
 427              ->export_related_data(['a', 'b'], 'def', 'DEF');
 428  
 429          $this->assertEquals('ABC', $writer->get_related_data(['a'], 'abc'));
 430          $this->assertEquals('DEF', $writer->get_related_data(['a', 'b'], 'def'));
 431      }
 432  
 433      /**
 434       * It should be possible to export related files in the files and children contexts.
 435       */
 436      public function test_export_custom_file() {
 437          $context = \context_system::instance();
 438  
 439          $writer = $this->get_writer_instance()
 440              ->set_context($context)
 441              ->export_custom_file(['file.txt'], 'file.txt', 'Content 1')
 442              ->export_custom_file([], 'file.txt', 'Content 2');
 443  
 444          $files = $writer->get_custom_file([]);
 445          $this->assertCount(1, $files);
 446          $this->assertEquals('Content 2', $files['file.txt']);
 447          $file = $writer->get_custom_file([], 'file.txt');
 448          $this->assertEquals('Content 2', $file);
 449  
 450          $files = $writer->get_custom_file(['file.txt']);
 451          $this->assertCount(1, $files);
 452          $this->assertEquals('Content 1', $files['file.txt']);
 453          $file = $writer->get_custom_file(['file.txt'], 'file.txt');
 454          $this->assertEquals('Content 1', $file);
 455          $this->assertTrue($writer->has_any_data());
 456          $this->assertTrue($writer->has_any_data(['file.txt']));
 457          $this->assertFalse($writer->has_any_data(['somepath']));
 458      }
 459  
 460      /**
 461       * It should be possible to export related files in the same location
 462       * in different contexts.
 463       */
 464      public function test_export_custom_file_no_context_clash() {
 465          $writer = $this->get_writer_instance();
 466          $context = \context_system::instance();
 467  
 468          $writer->set_context($context)
 469              ->export_custom_file(['file.txt'], 'file.txt', 'Content 1');
 470  
 471          $adminuser = \core_user::get_user_by_username('admin');
 472          $usercontext = \context_user::instance($adminuser->id);
 473          $writer->set_context($usercontext)
 474              ->export_custom_file(['file.txt'], 'file.txt', 'Content 2');
 475  
 476          $writer->set_context($context);
 477          $files = $writer->get_custom_file(['file.txt']);
 478          $this->assertCount(1, $files);
 479          $this->assertEquals('Content 1', $files['file.txt']);
 480  
 481          $writer->set_context($usercontext);
 482          $files = $writer->get_custom_file(['file.txt']);
 483          $this->assertCount(1, $files);
 484          $this->assertEquals('Content 2', $files['file.txt']);
 485      }
 486  
 487      /**
 488       * Test export and recover with children.
 489       */
 490      public function test_get_custom_file_with_children() {
 491          $writer = $this->get_writer_instance();
 492          $context = \context_system::instance();
 493  
 494          $writer->set_context($context)
 495              ->export_custom_file(['a'], 'file.txt', 'ABC')
 496              ->export_custom_file(['a', 'b'], 'file.txt', 'DEF');
 497  
 498          $this->assertEquals('ABC', $writer->get_custom_file(['a'], 'file.txt'));
 499          $this->assertEquals('DEF', $writer->get_custom_file(['a', 'b'], 'file.txt'));
 500      }
 501  
 502      /**
 503       * Get a fresh content writer.
 504       *
 505       * @return  moodle_content_writer
 506       */
 507      public function get_writer_instance() {
 508          $factory = $this->createMock(writer::class);
 509          return new content_writer($factory);
 510      }
 511  
 512      /**
 513       * Helper to create a stored file objectw with the given supplied content.
 514       *
 515       * @param   string  $filepath The file path to use in the stored_file
 516       * @param   string  $filename The file name to use in the stored_file
 517       * @return  stored_file
 518       */
 519      protected function get_stored_file($filepath, $filename) {
 520          static $counter = 0;
 521          $counter++;
 522          $filecontent = "Example content {$counter}";
 523          $contenthash = file_storage::hash_from_string($filecontent);
 524  
 525          $file = $this->getMockBuilder(stored_file::class)
 526              ->setMethods(null)
 527              ->setConstructorArgs([
 528                  get_file_storage(),
 529                  (object) [
 530                      'contenthash' => $contenthash,
 531                      'filesize' => strlen($filecontent),
 532                      'filepath' => $filepath,
 533                      'filename' => $filename,
 534                  ]
 535              ])
 536              ->getMock();
 537  
 538          return $file;
 539      }
 540  }