Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 4.2.x will end 22 April 2024 (12 months).
  • Bug fixes for security issues in 4.2.x will end 7 October 2024 (18 months).
  • PHP version: minimum PHP 8.0.0 Note: minimum PHP version has increased since Moodle 4.1. PHP 8.1.x is supported too.

Differences Between: [Versions 310 and 402] [Versions 311 and 402] [Versions 39 and 402] [Versions 400 and 402] [Versions 401 and 402]

   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_favourites;
  18  
  19  use core_favourites\local\repository\favourite_repository;
  20  use core_favourites\local\entity\favourite;
  21  
  22  /**
  23   * Test class covering the favourite_repository.
  24   *
  25   * @package    core_favourites
  26   * @category   test
  27   * @copyright  2018 Jake Dallimore <jrhdallimore@gmail.com>
  28   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  29   */
  30  class repository_test extends \advanced_testcase {
  31  
  32      public function setUp(): void {
  33          $this->resetAfterTest();
  34      }
  35  
  36      // Basic setup stuff to be reused in most tests.
  37      protected function setup_users_and_courses() {
  38          $user1 = self::getDataGenerator()->create_user();
  39          $user1context = \context_user::instance($user1->id);
  40          $user2 = self::getDataGenerator()->create_user();
  41          $user2context = \context_user::instance($user2->id);
  42          $course1 = self::getDataGenerator()->create_course();
  43          $course2 = self::getDataGenerator()->create_course();
  44          $course1context = \context_course::instance($course1->id);
  45          $course2context = \context_course::instance($course2->id);
  46          return [$user1context, $user2context, $course1context, $course2context];
  47      }
  48  
  49      /**
  50       * Verify the basic create operation can create records, and is validated.
  51       */
  52      public function test_add() {
  53          list($user1context, $user2context, $course1context, $course2context) = $this->setup_users_and_courses();
  54  
  55          // Create a favourites repository and favourite a course.
  56          $favouritesrepo = new favourite_repository($user1context);
  57  
  58          $favcourse = new favourite(
  59              'core_course',
  60              'course',
  61              $course1context->instanceid,
  62              $course1context->id,
  63              $user1context->instanceid
  64          );
  65          $timenow = time(); // Reference only, to check that the created item has a time equal to or greater than this.
  66          $favourite = $favouritesrepo->add($favcourse);
  67  
  68          // Verify we get the record back.
  69          $this->assertInstanceOf(favourite::class, $favourite);
  70          $this->assertObjectHasAttribute('id', $favourite);
  71          $this->assertEquals('core_course', $favourite->component);
  72          $this->assertEquals('course', $favourite->itemtype);
  73  
  74          // Verify the returned object has additional properties, created as part of the add.
  75          $this->assertObjectHasAttribute('ordering', $favourite);
  76          $this->assertObjectHasAttribute('timecreated', $favourite);
  77          $this->assertGreaterThanOrEqual($timenow, $favourite->timecreated);
  78  
  79          // Try to save the same record again and confirm the store throws an exception.
  80          $this->expectException('dml_write_exception');
  81          $favouritesrepo->add($favcourse);
  82      }
  83  
  84      /**
  85       * Tests that incomplete favourites cannot be saved.
  86       */
  87      public function test_add_incomplete_favourite() {
  88          list($user1context, $user2context, $course1context, $course2context) = $this->setup_users_and_courses();
  89  
  90          // Create a favourites repository and try to favourite a course.
  91          $favouritesrepo = new favourite_repository($user1context);
  92  
  93          $favcourse = new favourite(
  94              'core_course',
  95              'course',
  96              $course1context->instanceid,
  97              $course1context->id,
  98              $user1context->instanceid
  99          );
 100          unset($favcourse->userid);
 101  
 102          $this->expectException('moodle_exception');
 103          $favouritesrepo->add($favcourse);
 104      }
 105  
 106      public function test_add_all_basic() {
 107          list($user1context, $user2context, $course1context, $course2context) = $this->setup_users_and_courses();
 108  
 109          // Create a favourites repository and favourite several courses.
 110          $favouritesrepo = new favourite_repository($user1context);
 111          $favcourses = [];
 112  
 113          $favcourses[] = new favourite(
 114              'core_course',
 115              'course',
 116              $course1context->instanceid,
 117              $course1context->id,
 118              $user1context->instanceid
 119          );
 120          $favcourses[] = new favourite(
 121              'core_course',
 122              'course',
 123              $course2context->instanceid,
 124              $course2context->id,
 125              $user1context->instanceid
 126          );
 127  
 128          $timenow = time(); // Reference only, to check that the created item has a time equal to or greater than this.
 129          $favourites = $favouritesrepo->add_all($favcourses);
 130  
 131          $this->assertIsArray($favourites);
 132          $this->assertCount(2, $favourites);
 133          foreach ($favourites as $favourite) {
 134              // Verify we get the favourite back.
 135              $this->assertInstanceOf(favourite::class, $favourite);
 136              $this->assertEquals('core_course', $favourite->component);
 137              $this->assertEquals('course', $favourite->itemtype);
 138  
 139              // Verify the returned object has additional properties, created as part of the add.
 140              $this->assertObjectHasAttribute('ordering', $favourite);
 141              $this->assertObjectHasAttribute('timecreated', $favourite);
 142              $this->assertGreaterThanOrEqual($timenow, $favourite->timecreated);
 143          }
 144  
 145          // Try to save the same record again and confirm the store throws an exception.
 146          $this->expectException('dml_write_exception');
 147          $favouritesrepo->add_all($favcourses);
 148      }
 149  
 150      /**
 151       * Tests reading from the repository by instance id.
 152       */
 153      public function test_find() {
 154          list($user1context, $user2context, $course1context, $course2context) = $this->setup_users_and_courses();
 155  
 156          // Create a favourites repository and favourite a course.
 157          $favouritesrepo = new favourite_repository($user1context);
 158          $favourite = new favourite(
 159              'core_course',
 160              'course',
 161              $course1context->instanceid,
 162              $course1context->id,
 163              $user1context->instanceid
 164          );
 165          $favourite = $favouritesrepo->add($favourite);
 166  
 167          // Now, from the repo, get the single favourite we just created, by id.
 168          $userfavourite = $favouritesrepo->find($favourite->id);
 169          $this->assertInstanceOf(favourite::class, $userfavourite);
 170          $this->assertObjectHasAttribute('timecreated', $userfavourite);
 171  
 172          // Try to get a favourite we know doesn't exist.
 173          // We expect an exception in this case.
 174          $this->expectException(\dml_exception::class);
 175          $favouritesrepo->find(0);
 176      }
 177  
 178      /**
 179       * Test verifying that find_all() returns all favourites, or an empty array.
 180       */
 181      public function test_find_all() {
 182          list($user1context, $user2context, $course1context, $course2context) = $this->setup_users_and_courses();
 183  
 184          $favouritesrepo = new favourite_repository($user1context);
 185  
 186          // Verify that only two self-conversations are found.
 187          $this->assertCount(2, $favouritesrepo->find_all());
 188  
 189          // Save a favourite for 2 courses, in different areas.
 190          $favourite = new favourite(
 191              'core_course',
 192              'course',
 193              $course1context->instanceid,
 194              $course1context->id,
 195              $user1context->instanceid
 196          );
 197          $favourite2 = new favourite(
 198              'core_course',
 199              'course',
 200              $course2context->instanceid,
 201              $course2context->id,
 202              $user1context->instanceid
 203          );
 204          $favouritesrepo->add($favourite);
 205          $favouritesrepo->add($favourite2);
 206  
 207          // Verify that find_all returns both of our favourites + two self-conversations.
 208          $favourites = $favouritesrepo->find_all();
 209          $this->assertCount(4, $favourites);
 210          foreach ($favourites as $fav) {
 211              $this->assertInstanceOf(favourite::class, $fav);
 212              $this->assertObjectHasAttribute('id', $fav);
 213              $this->assertObjectHasAttribute('timecreated', $fav);
 214          }
 215      }
 216  
 217      /**
 218       * Testing the pagination of the find_all method.
 219       */
 220      public function test_find_all_pagination() {
 221          list($user1context, $user2context, $course1context, $course2context) = $this->setup_users_and_courses();
 222  
 223          $favouritesrepo = new favourite_repository($user1context);
 224  
 225          // Verify that for an empty repository, find_all with any combination of page options returns only self-conversations.
 226          $this->assertCount(2, $favouritesrepo->find_all(0, 0));
 227          $this->assertCount(2, $favouritesrepo->find_all(0, 10));
 228          $this->assertCount(1, $favouritesrepo->find_all(1, 0));
 229          $this->assertCount(1, $favouritesrepo->find_all(1, 10));
 230  
 231          // Save 10 arbitrary favourites to the repo.
 232          foreach (range(1, 10) as $i) {
 233              $favourite = new favourite(
 234                  'core_course',
 235                  'course',
 236                  $i,
 237                  $course1context->id,
 238                  $user1context->instanceid
 239              );
 240              $favouritesrepo->add($favourite);
 241          }
 242  
 243          // Verify we have 10 favourites + 2 self-conversations.
 244          $this->assertEquals(12, $favouritesrepo->count());
 245  
 246          // Verify we can fetch the first page of 5 records+ 2 self-conversations.
 247          $favourites = $favouritesrepo->find_all(0, 6);
 248          $this->assertCount(6, $favourites);
 249  
 250          // Verify we can fetch the second page.
 251          $favourites = $favouritesrepo->find_all(6, 6);
 252          $this->assertCount(6, $favourites);
 253  
 254          // Verify the third page request ends with an empty array.
 255          $favourites = $favouritesrepo->find_all(12, 6);
 256          $this->assertCount(0, $favourites);
 257      }
 258  
 259      /**
 260       * Test retrieval of a user's favourites for a given criteria, in this case, area.
 261       */
 262      public function test_find_by() {
 263          list($user1context, $user2context, $course1context, $course2context) = $this->setup_users_and_courses();
 264  
 265          // Create a favourites repository and favourite a course.
 266          $favouritesrepo = new favourite_repository($user1context);
 267          $favourite = new favourite(
 268              'core_course',
 269              'course',
 270              $course1context->instanceid,
 271              $course1context->id,
 272              $user1context->instanceid
 273          );
 274          $favouritesrepo->add($favourite);
 275  
 276          // Add another favourite.
 277          $favourite = new favourite(
 278              'core_course',
 279              'course_item',
 280              $course1context->instanceid,
 281              $course1context->id,
 282              $user1context->instanceid
 283          );
 284          $favouritesrepo->add($favourite);
 285  
 286          // From the repo, get the list of favourites for the 'core_course/course' area.
 287          $userfavourites = $favouritesrepo->find_by(['component' => 'core_course', 'itemtype' => 'course']);
 288          $this->assertIsArray($userfavourites);
 289          $this->assertCount(1, $userfavourites);
 290  
 291          // Try to get a list of favourites for a non-existent area.
 292          $userfavourites = $favouritesrepo->find_by(['component' => 'core_cannibalism', 'itemtype' => 'course']);
 293          $this->assertIsArray($userfavourites);
 294          $this->assertCount(0, $userfavourites);
 295  
 296          // From the repo, get the list of favourites for the 'core_course/course' area when passed as an array.
 297          $userfavourites = $favouritesrepo->find_by(['component' => 'core_course', 'itemtype' => ['course']]);
 298          $this->assertIsArray($userfavourites);
 299          $this->assertCount(1, $userfavourites);
 300  
 301          // From the repo, get the list of favourites for the 'core_course' area given multiple item_types.
 302          $userfavourites = $favouritesrepo->find_by(['component' => 'core_course', 'itemtype' => ['course', 'course_item']]);
 303          $this->assertIsArray($userfavourites);
 304          $this->assertCount(2, $userfavourites);
 305      }
 306  
 307      /**
 308       * Testing the pagination of the find_by method.
 309       */
 310      public function test_find_by_pagination() {
 311          list($user1context, $user2context, $course1context, $course2context) = $this->setup_users_and_courses();
 312  
 313          $favouritesrepo = new favourite_repository($user1context);
 314  
 315          // Verify that by default, find_all with any combination of page options returns only self-conversations.
 316          $this->assertCount(2, $favouritesrepo->find_by([], 0, 0));
 317          $this->assertCount(2, $favouritesrepo->find_by([], 0, 10));
 318          $this->assertCount(1, $favouritesrepo->find_by([], 1, 0));
 319          $this->assertCount(1, $favouritesrepo->find_by([], 1, 10));
 320  
 321          // Save 10 arbitrary favourites to the repo.
 322          foreach (range(1, 10) as $i) {
 323              $favourite = new favourite(
 324                  'core_course',
 325                  'course',
 326                  $i,
 327                  $course1context->id,
 328                  $user1context->instanceid
 329              );
 330              $favouritesrepo->add($favourite);
 331          }
 332  
 333          // Verify we have 10 favourites + 2 self-conversations.
 334          $this->assertEquals(12, $favouritesrepo->count());
 335  
 336          // Verify a request for a page, when no criteria match, results in 2 self-conversations array.
 337          $favourites = $favouritesrepo->find_by(['component' => 'core_message'], 0, 5);
 338          $this->assertCount(2, $favourites);
 339  
 340          // Verify we can fetch a the first page of 5 records.
 341          $favourites = $favouritesrepo->find_by(['component' => 'core_course'], 0, 5);
 342          $this->assertCount(5, $favourites);
 343  
 344          // Verify we can fetch the second page.
 345          $favourites = $favouritesrepo->find_by(['component' => 'core_course'], 5, 5);
 346          $this->assertCount(5, $favourites);
 347  
 348          // Verify the third page request ends with an empty array.
 349          $favourites = $favouritesrepo->find_by(['component' => 'core_course'], 10, 5);
 350          $this->assertCount(0, $favourites);
 351      }
 352  
 353      /**
 354       * Test the count_by() method.
 355       */
 356      public function test_count_by() {
 357          list($user1context, $user2context, $course1context, $course2context) = $this->setup_users_and_courses();
 358  
 359          // Create a favourites repository and add 2 favourites in different areas.
 360          $favouritesrepo = new favourite_repository($user1context);
 361          $favourite = new favourite(
 362              'core_course',
 363              'course',
 364              $course1context->instanceid,
 365              $course1context->id,
 366              $user1context->instanceid
 367          );
 368          $favourite2 = new favourite(
 369              'core_course',
 370              'anothertype',
 371              $course2context->instanceid,
 372              $course2context->id,
 373              $user1context->instanceid
 374          );
 375          $favouritesrepo->add($favourite);
 376          $favouritesrepo->add($favourite2);
 377  
 378          // Verify counts can be restricted by criteria.
 379          $this->assertEquals(1, $favouritesrepo->count_by(['userid' => $user1context->instanceid, 'component' => 'core_course',
 380                  'itemtype' => 'course']));
 381          $this->assertEquals(1, $favouritesrepo->count_by(['userid' => $user1context->instanceid, 'component' => 'core_course',
 382              'itemtype' => 'anothertype']));
 383          $this->assertEquals(0, $favouritesrepo->count_by(['userid' => $user1context->instanceid, 'component' => 'core_course',
 384              'itemtype' => 'nonexistenttype']));
 385      }
 386  
 387      /**
 388       * Test the exists() function.
 389       */
 390      public function test_exists() {
 391          list($user1context, $user2context, $course1context, $course2context) = $this->setup_users_and_courses();
 392  
 393          // Create a favourites repository and favourite a course.
 394          $favouritesrepo = new favourite_repository($user1context);
 395          $favourite = new favourite(
 396              'core_course',
 397              'course',
 398              $course1context->instanceid,
 399              $course1context->id,
 400              $user1context->instanceid
 401          );
 402          $createdfavourite = $favouritesrepo->add($favourite);
 403  
 404          // Verify the existence of the favourite in the repo.
 405          $this->assertTrue($favouritesrepo->exists($createdfavourite->id));
 406  
 407          // Verify exists returns false for non-existent favourite.
 408          $this->assertFalse($favouritesrepo->exists(0));
 409      }
 410  
 411      /**
 412       * Test the exists_by() method.
 413       */
 414      public function test_exists_by() {
 415          list($user1context, $user2context, $course1context, $course2context) = $this->setup_users_and_courses();
 416  
 417          // Create a favourites repository and favourite two courses, in different areas.
 418          $favouritesrepo = new favourite_repository($user1context);
 419          $favourite = new favourite(
 420              'core_course',
 421              'course',
 422              $course1context->instanceid,
 423              $course1context->id,
 424              $user1context->instanceid
 425          );
 426          $favourite2 = new favourite(
 427              'core_course',
 428              'anothertype',
 429              $course2context->instanceid,
 430              $course2context->id,
 431              $user1context->instanceid
 432          );
 433          $favourite1 = $favouritesrepo->add($favourite);
 434          $favourite2 = $favouritesrepo->add($favourite2);
 435  
 436          // Verify the existence of the favourites.
 437          $this->assertTrue($favouritesrepo->exists_by(
 438              [
 439                  'userid' => $user1context->instanceid,
 440                  'component' => 'core_course',
 441                  'itemtype' => 'course',
 442                  'itemid' => $favourite1->itemid,
 443                  'contextid' => $favourite1->contextid
 444              ]
 445          ));
 446          $this->assertTrue($favouritesrepo->exists_by(
 447              [
 448                  'userid' => $user1context->instanceid,
 449                  'component' => 'core_course',
 450                  'itemtype' => 'anothertype',
 451                  'itemid' => $favourite2->itemid,
 452                  'contextid' => $favourite2->contextid
 453              ]
 454          ));
 455  
 456          // Verify that we can't find a favourite from one area, in another.
 457          $this->assertFalse($favouritesrepo->exists_by(
 458              [
 459                  'userid' => $user1context->instanceid,
 460                  'component' => 'core_course',
 461                  'itemtype' => 'anothertype',
 462                  'itemid' => $favourite1->itemid,
 463                  'contextid' => $favourite1->contextid
 464              ]
 465          ));
 466      }
 467  
 468      /**
 469       * Test the update() method, by simulating a user changing the ordering of a favourite.
 470       */
 471      public function test_update() {
 472          list($user1context, $user2context, $course1context, $course2context) = $this->setup_users_and_courses();
 473  
 474          // Create a favourites repository and favourite a course.
 475          $favouritesrepo = new favourite_repository($user1context);
 476          $favourite = new favourite(
 477              'core_course',
 478              'course',
 479              $course1context->instanceid,
 480              $course1context->id,
 481              $user1context->instanceid
 482          );
 483          $favourite1 = $favouritesrepo->add($favourite);
 484          $this->assertNull($favourite1->ordering);
 485  
 486          // Verify we can update the ordering for 2 favourites.
 487          $favourite1->ordering = 1;
 488          $favourite1 = $favouritesrepo->update($favourite1);
 489          $this->assertInstanceOf(favourite::class, $favourite1);
 490          $this->assertEquals('1', $favourite1->ordering);
 491      }
 492  
 493      public function test_delete() {
 494          list($user1context, $user2context, $course1context, $course2context) = $this->setup_users_and_courses();
 495  
 496          // Create a favourites repository and favourite a course.
 497          $favouritesrepo = new favourite_repository($user1context);
 498          $favourite = new favourite(
 499              'core_course',
 500              'course',
 501              $course1context->instanceid,
 502              $course1context->id,
 503              $user1context->instanceid
 504          );
 505          $favourite = $favouritesrepo->add($favourite);
 506  
 507          // Verify the existence of the favourite in the repo.
 508          $this->assertTrue($favouritesrepo->exists($favourite->id));
 509  
 510          // Now, delete the favourite and confirm it's not retrievable.
 511          $favouritesrepo->delete($favourite->id);
 512          $this->assertFalse($favouritesrepo->exists($favourite->id));
 513      }
 514  
 515      /**
 516       * Test the delete_by() method.
 517       */
 518      public function test_delete_by() {
 519          list($user1context, $user2context, $course1context, $course2context) = $this->setup_users_and_courses();
 520  
 521          // Create a favourites repository and favourite two courses, in different areas.
 522          $favouritesrepo = new favourite_repository($user1context);
 523          $favourite = new favourite(
 524              'core_course',
 525              'course',
 526              $course1context->instanceid,
 527              $course1context->id,
 528              $user1context->instanceid
 529          );
 530          $favourite2 = new favourite(
 531              'core_course',
 532              'anothertype',
 533              $course1context->instanceid,
 534              $course1context->id,
 535              $user1context->instanceid
 536          );
 537          $favourite1 = $favouritesrepo->add($favourite);
 538          $favourite2 = $favouritesrepo->add($favourite2);
 539  
 540          // Verify we have 2 items in the repo + 2 self-conversations.
 541          $this->assertEquals(4, $favouritesrepo->count());
 542  
 543          // Try to delete by a non-existent area, and confirm it doesn't remove anything.
 544          $favouritesrepo->delete_by(
 545              [
 546                  'userid' => $user1context->instanceid,
 547                  'component' => 'core_course',
 548                  'itemtype' => 'donaldduck'
 549              ]
 550          );
 551          $this->assertEquals(4, $favouritesrepo->count());
 552  
 553          // Try to delete by a non-existent area, and confirm it doesn't remove anything.
 554          $favouritesrepo->delete_by(
 555              [
 556                  'userid' => $user1context->instanceid,
 557                  'component' => 'core_course',
 558                  'itemtype' => 'cat'
 559              ]
 560          );
 561          $this->assertEquals(4, $favouritesrepo->count());
 562  
 563          // Delete by area, and confirm we have one record left, from the 'core_course/anothertype' area.
 564          $favouritesrepo->delete_by(
 565              [
 566                  'userid' => $user1context->instanceid,
 567                  'component' => 'core_course',
 568                  'itemtype' => 'course'
 569              ]
 570          );
 571          $this->assertEquals(3, $favouritesrepo->count());
 572          $this->assertFalse($favouritesrepo->exists($favourite1->id));
 573          $this->assertTrue($favouritesrepo->exists($favourite2->id));
 574      }
 575  
 576      /**
 577       * Test the find_favourite() method for an existing favourite.
 578       */
 579      public function test_find_favourite_basic() {
 580          list($user1context, $user2context, $course1context, $course2context) = $this->setup_users_and_courses();
 581  
 582          // Create a favourites repository and favourite two courses, in different areas.
 583          $favouritesrepo = new favourite_repository($user1context);
 584          $favourite = new favourite(
 585              'core_course',
 586              'course',
 587              $course1context->instanceid,
 588              $course1context->id,
 589              $user1context->instanceid
 590          );
 591          $favourite2 = new favourite(
 592              'core_course',
 593              'anothertype',
 594              $course1context->instanceid,
 595              $course1context->id,
 596              $user1context->instanceid
 597          );
 598          $favourite1 = $favouritesrepo->add($favourite);
 599          $favourite2 = $favouritesrepo->add($favourite2);
 600  
 601          $fav = $favouritesrepo->find_favourite($user1context->instanceid, 'core_course', 'course', $course1context->instanceid,
 602              $course1context->id);
 603          $this->assertInstanceOf(\core_favourites\local\entity\favourite::class, $fav);
 604      }
 605  
 606      /**
 607       * Test confirming the repository throws an exception in find_favourite if the favourite can't be found.
 608       */
 609      public function test_find_favourite_nonexistent_favourite() {
 610          list($user1context, $user2context, $course1context, $course2context) = $this->setup_users_and_courses();
 611  
 612          // Confirm we get an exception.
 613          $favouritesrepo = new favourite_repository($user1context);
 614          $this->expectException(\dml_exception::class);
 615          $favouritesrepo->find_favourite($user1context->instanceid, 'core_course', 'course', 0, $course1context->id);
 616      }
 617  }