Search moodle.org's
Developer Documentation

See Release Notes

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

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

   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 rating/lib.php
  19   *
  20   * @package    core_rating
  21   * @category   test
  22   * @copyright  2011 onwards Eloy Lafuente (stronk7) {@link http://stronk7.com}
  23   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  24   */
  25  
  26  namespace core_rating;
  27  
  28  use rating_manager;
  29  
  30  defined('MOODLE_INTERNAL') || die();
  31  
  32  // Include all the needed stuff.
  33  global $CFG;
  34  require_once($CFG->dirroot . '/rating/lib.php');
  35  
  36  
  37  /**
  38   * Unit test case for all the rating/lib.php requiring DB mockup & manipulation
  39   *
  40   * @package    core_rating
  41   * @category   test
  42   * @copyright  2011 onwards Eloy Lafuente (stronk7) {@link http://stronk7.com}
  43   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  44   */
  45  class rating_test extends \advanced_testcase {
  46  
  47      protected $syscontext;
  48      protected $neededcaps = array('view', 'viewall', 'viewany', 'rate');
  49      protected $originaldefaultfrontpageroleid;
  50  
  51      public function setUp(): void {
  52          global $CFG;
  53          parent::setUp();
  54  
  55          $this->resetAfterTest(true);
  56  
  57          $CFG->defaultfrontpageroleid = null;
  58      }
  59  
  60      /**
  61       * Test the current get_ratings method main sql
  62       */
  63      public function test_get_ratings_sql() {
  64          global $DB;
  65  
  66          // We load 3 items. Each is rated twice. For simplicity itemid == user id of the item owner.
  67          $ctxid = \context_system::instance()->id;
  68          $ratings = array(
  69              // User 1's items. Average == 2.
  70              array('contextid' => $ctxid,
  71                    'component' => 'mod_forum',
  72                    'ratingarea' => 'post',
  73                    'itemid' => 1,
  74                    'scaleid' => 10,
  75                    'rating' => 1,
  76                    'userid' => 2,
  77                    'timecreated' => 1,
  78                    'timemodified' => 1),
  79  
  80              array('contextid' => $ctxid,
  81                    'component' => 'mod_forum',
  82                    'ratingarea' => 'post',
  83                    'itemid' => 1,
  84                    'scaleid' => 10,
  85                    'rating' => 3,
  86                    'userid' => 3,
  87                    'timecreated' => 1,
  88                    'timemodified' => 1),
  89  
  90              // User 2's items. Average == 3.
  91              array('contextid' => $ctxid,
  92                    'component' => 'mod_forum',
  93                    'ratingarea' => 'post',
  94                    'itemid' => 2,
  95                    'scaleid' => 10,
  96                    'rating' => 1,
  97                    'userid' => 1,
  98                    'timecreated' => 1,
  99                    'timemodified' => 1),
 100  
 101              array('contextid' => $ctxid,
 102                    'component' => 'mod_forum',
 103                    'ratingarea' => 'post',
 104                    'itemid' => 2,
 105                    'scaleid' => 10,
 106                    'rating' => 5,
 107                    'userid' => 3,
 108                    'timecreated' => 1,
 109                    'timemodified' => 1),
 110  
 111              // User 3's items. Average == 4.
 112              array('contextid' => $ctxid,
 113                    'component' => 'mod_forum',
 114                    'ratingarea' => 'post',
 115                    'itemid' => 3,
 116                    'scaleid' => 10,
 117                    'rating' => 3,
 118                    'userid' => 1,
 119                    'timecreated' => 1,
 120                    'timemodified' => 1),
 121  
 122              array('contextid' => $ctxid,
 123                    'component' => 'mod_forum',
 124                    'ratingarea' => 'post',
 125                    'itemid' => 3,
 126                    'scaleid' => 10,
 127                    'rating' => 5,
 128                    'userid' => 2,
 129                    'timecreated' => 1,
 130                    'timemodified' => 1)
 131          );
 132          foreach ($ratings as $rating) {
 133              $DB->insert_record('rating', $rating);
 134          }
 135  
 136          // A post (item) by user 1 (rated above by user 2 and 3 with average = 2).
 137          $user1posts = array(
 138              (object)array('id' => 1, 'userid' => 1, 'message' => 'hello'));
 139          // A post (item) by user 2 (rated above by user 1 and 3 with average = 3).
 140          $user2posts = array(
 141              (object)array('id' => 2, 'userid' => 2, 'message' => 'world'));
 142          // A post (item) by user 3 (rated above by user 1 and 2 with average = 4).
 143          $user3posts = array(
 144              (object)array('id' => 3, 'userid' => 3, 'message' => 'moodle'));
 145  
 146          // Prepare the default options.
 147          $defaultoptions = array (
 148              'context'    => \context_system::instance(),
 149              'component'  => 'mod_forum',
 150              'ratingarea' => 'post',
 151              'scaleid'    => 10,
 152              'aggregate'  => RATING_AGGREGATE_AVERAGE);
 153  
 154          $rm = new mockup_rating_manager();
 155  
 156          // STEP 1: Retreive ratings using the current user.
 157  
 158          // Get results for user 1's item (expected average 1 + 3 / 2 = 2).
 159          $toptions = (object)array_merge($defaultoptions, array('items' => $user1posts));
 160          $result = $rm->get_ratings($toptions);
 161          $this->assertEquals(count($result), count($user1posts));
 162          $this->assertEquals($result[0]->id, $user1posts[0]->id);
 163          $this->assertEquals($result[0]->userid, $user1posts[0]->userid);
 164          $this->assertEquals($result[0]->message, $user1posts[0]->message);
 165          $this->assertEquals($result[0]->rating->count, 2);
 166          $this->assertEquals($result[0]->rating->aggregate, 2);
 167          // Note that $result[0]->rating->rating is somewhat random.
 168          // We didn't supply a user ID so $USER was used which will vary depending on who runs the tests.
 169  
 170          // Get results for items of user 2 (expected average 1 + 5 / 2 = 3).
 171          $toptions = (object)array_merge($defaultoptions, array('items' => $user2posts));
 172          $result = $rm->get_ratings($toptions);
 173          $this->assertEquals(count($result), count($user2posts));
 174          $this->assertEquals($result[0]->id, $user2posts[0]->id);
 175          $this->assertEquals($result[0]->userid, $user2posts[0]->userid);
 176          $this->assertEquals($result[0]->message, $user2posts[0]->message);
 177          $this->assertEquals($result[0]->rating->count, 2);
 178          $this->assertEquals($result[0]->rating->aggregate, 3);
 179          // Note that $result[0]->rating->rating is somewhat random.
 180          // We didn't supply a user ID so $USER was used which will vary depending on who runs the tests.
 181  
 182          // Get results for items of user 3 (expected average 3 + 5 / 2 = 4).
 183          $toptions = (object)array_merge($defaultoptions, array('items' => $user3posts));
 184          $result = $rm->get_ratings($toptions);
 185          $this->assertEquals(count($result), count($user3posts));
 186          $this->assertEquals($result[0]->id, $user3posts[0]->id);
 187          $this->assertEquals($result[0]->userid, $user3posts[0]->userid);
 188          $this->assertEquals($result[0]->message, $user3posts[0]->message);
 189          $this->assertEquals($result[0]->rating->count, 2);
 190          $this->assertEquals($result[0]->rating->aggregate, 4);
 191          // Note that $result[0]->rating->rating is somewhat random.
 192          // We didn't supply a user ID so $USER was used which will vary depending on who runs the tests.
 193  
 194          // Get results for items of user 1 & 2 together (expected averages are 2 and 3, as tested above).
 195          $posts = array_merge($user1posts, $user2posts);
 196          $toptions = (object)array_merge($defaultoptions, array('items' => $posts));
 197          $result = $rm->get_ratings($toptions);
 198          $this->assertEquals(count($result), count($posts));
 199          $this->assertEquals($result[0]->id, $posts[0]->id);
 200          $this->assertEquals($result[0]->userid, $posts[0]->userid);
 201          $this->assertEquals($result[0]->message, $posts[0]->message);
 202          $this->assertEquals($result[0]->rating->count, 2);
 203          $this->assertEquals($result[0]->rating->aggregate, 2);
 204          // Note that $result[0]->rating->rating is somewhat random.
 205          // We didn't supply a user ID so $USER was used which will vary depending on who runs the tests.
 206  
 207          $this->assertEquals($result[1]->id, $posts[1]->id);
 208          $this->assertEquals($result[1]->userid, $posts[1]->userid);
 209          $this->assertEquals($result[1]->message, $posts[1]->message);
 210          $this->assertEquals($result[1]->rating->count, 2);
 211          $this->assertEquals($result[1]->rating->aggregate, 3);
 212          // Note that $result[0]->rating->rating is somewhat random.
 213          // We didn't supply a user ID so $USER was used which will vary depending on who runs the tests.
 214  
 215          // STEP 2: Retrieve ratings by a specified user.
 216          //         We still expect complete aggregations and counts.
 217  
 218          // Get results for items of user 1 rated by user 2 (avg 2, rating 1).
 219          $toptions = (object)array_merge($defaultoptions, array('items' => $user1posts, 'userid' => 2));
 220          $result = $rm->get_ratings($toptions);
 221          $this->assertEquals(count($result), count($user1posts));
 222          $this->assertEquals($result[0]->id, $user1posts[0]->id);
 223          $this->assertEquals($result[0]->userid, $user1posts[0]->userid);
 224          $this->assertEquals($result[0]->message, $user1posts[0]->message);
 225          $this->assertEquals($result[0]->rating->count, 2);
 226          $this->assertEquals($result[0]->rating->aggregate, 2);
 227          $this->assertEquals($result[0]->rating->rating, 1); // User 2 rated user 1 "1".
 228          $this->assertEquals($result[0]->rating->userid, $toptions->userid); // Must be the passed userid.
 229  
 230          // Get results for items of user 1 rated by user 3.
 231          $toptions = (object)array_merge($defaultoptions, array('items' => $user1posts, 'userid' => 3));
 232          $result = $rm->get_ratings($toptions);
 233          $this->assertEquals(count($result), count($user1posts));
 234          $this->assertEquals($result[0]->id, $user1posts[0]->id);
 235          $this->assertEquals($result[0]->userid, $user1posts[0]->userid);
 236          $this->assertEquals($result[0]->message, $user1posts[0]->message);
 237          $this->assertEquals($result[0]->rating->count, 2);
 238          $this->assertEquals($result[0]->rating->aggregate, 2);
 239          $this->assertEquals($result[0]->rating->rating, 3); // User 3 rated user 1 "3".
 240          $this->assertEquals($result[0]->rating->userid, $toptions->userid); // Must be the passed userid.
 241  
 242          // Get results for items of user 1 & 2 together rated by user 3.
 243          $posts = array_merge($user1posts, $user2posts);
 244          $toptions = (object)array_merge($defaultoptions, array('items' => $posts, 'userid' => 3));
 245          $result = $rm->get_ratings($toptions);
 246          $this->assertEquals(count($result), count($posts));
 247          $this->assertEquals($result[0]->id, $posts[0]->id);
 248          $this->assertEquals($result[0]->userid, $posts[0]->userid);
 249          $this->assertEquals($result[0]->message, $posts[0]->message);
 250          $this->assertEquals($result[0]->rating->count, 2);
 251          $this->assertEquals($result[0]->rating->aggregate, 2);
 252          $this->assertEquals($result[0]->rating->rating, 3); // User 3 rated user 1 "3".
 253          $this->assertEquals($result[0]->rating->userid, $toptions->userid); // Must be the passed userid.
 254  
 255          $this->assertEquals($result[1]->id, $posts[1]->id);
 256          $this->assertEquals($result[1]->userid, $posts[1]->userid);
 257          $this->assertEquals($result[1]->message, $posts[1]->message);
 258          $this->assertEquals($result[1]->rating->count, 2);
 259          $this->assertEquals($result[1]->rating->aggregate, 3);
 260          $this->assertEquals($result[0]->rating->rating, 3); // User 3 rated user 2 "5".
 261          $this->assertEquals($result[1]->rating->userid, $toptions->userid); // Must be the passed userid.
 262  
 263          // STEP 3: Some special cases.
 264  
 265          // Get results for user 1's items (expected average 1 + 3 / 2 = 2).
 266          // Supplying a non-existent user id so no rating from that user should be found.
 267          $toptions = (object)array_merge($defaultoptions, array('items' => $user1posts));
 268          $toptions->userid = 123456; // Non-existent user.
 269          $result = $rm->get_ratings($toptions);
 270          $this->assertNull($result[0]->rating->userid);
 271          $this->assertNull($result[0]->rating->rating);
 272          $this->assertEquals($result[0]->rating->aggregate, 2); // Should still get the aggregate.
 273  
 274          // Get results for items of user 2 (expected average 1 + 5 / 2 = 3).
 275          // Supplying the user id of the user who owns the items so no rating should be found.
 276          $toptions = (object)array_merge($defaultoptions, array('items' => $user2posts));
 277          $toptions->userid = 2; // User 2 viewing the ratings of their own item.
 278          $result = $rm->get_ratings($toptions);
 279          // These should be null as the user is viewing their own item and thus cannot rate.
 280          $this->assertNull($result[0]->rating->userid);
 281          $this->assertNull($result[0]->rating->rating);
 282          $this->assertEquals($result[0]->rating->aggregate, 3); // Should still get the aggregate.
 283      }
 284  
 285      /**
 286       * Data provider for get_aggregate_string tests.
 287       *
 288       * @return array
 289       */
 290      public function get_aggregate_string_provider() {
 291          return [
 292              'Non-numeric aggregate produces empty string' => [
 293                  RATING_AGGREGATE_NONE,
 294                  'string',
 295                  null,
 296                  ['Foo', 'Bar'],
 297                  '',
 298              ],
 299              'Aggregate count produces empty string' => [
 300                  RATING_AGGREGATE_COUNT,
 301                  0,
 302                  null,
 303                  ['Foo', 'Bar'],
 304                  '',
 305              ],
 306              'Numeric SUM with non-numeric scale produces returns original value' => [
 307                  RATING_AGGREGATE_SUM,
 308                  10,
 309                  false,
 310                  ['Foo', 'Bar'],
 311                  '10',
 312              ],
 313              'Numeric SUM with non-numeric scale produces returns rounded value' => [
 314                  RATING_AGGREGATE_SUM,
 315                  10.45,
 316                  false,
 317                  ['Foo', 'Bar'],
 318                  '10.5',
 319              ],
 320              'Numeric SUM with numeric scale produces returns rounded value' => [
 321                  RATING_AGGREGATE_SUM,
 322                  10.45,
 323                  true,
 324                  ['Foo', 'Bar'],
 325                  '10.5',
 326              ],
 327              'Numeric AVERAGE with numeric scale produces returns rounded value' => [
 328                  RATING_AGGREGATE_AVERAGE,
 329                  10.45,
 330                  true,
 331                  ['Foo', 'Bar'],
 332                  '10.5',
 333              ],
 334              'Numeric AVERAGE with non-numeric scale produces returns indexed value (0)' => [
 335                  RATING_AGGREGATE_AVERAGE,
 336                  0,
 337                  false,
 338                  ['Foo', 'Bar'],
 339                  'Foo',
 340              ],
 341              'Numeric AVERAGE with non-numeric scale produces returns indexed value (1)' => [
 342                  RATING_AGGREGATE_AVERAGE,
 343                  1,
 344                  false,
 345                  ['Foo', 'Bar'],
 346                  'Bar',
 347              ],
 348          ];
 349      }
 350  
 351      /**
 352       * Test the value returned by get_aggregate_string().
 353       *
 354       * @dataProvider get_aggregate_string_provider
 355       */
 356      public function test_get_aggregate_string($method, $aggregate, $isnumeric, $scaleitems, $expectation) {
 357          $options = new \stdClass();
 358          $options->aggregate = $aggregate;
 359          $options->context = null;
 360          $options->component = null;
 361          $options->ratingarea = null;
 362          $options->itemid = null;
 363          $options->scaleid = null;
 364          $options->userid = null;
 365  
 366          $options->settings = new \stdClass();
 367          $options->settings->aggregationmethod = $method;
 368          $options->settings->scale = new \stdClass();
 369          $options->settings->scale->isnumeric = $isnumeric;
 370          $options->settings->scale->scaleitems = $scaleitems;
 371  
 372          $rating = new \rating($options);
 373          $this->assertEquals($expectation, $rating->get_aggregate_string());
 374      }
 375  }
 376  
 377  /**
 378   * rating_manager subclass for unit testing without requiring capabilities to be loaded
 379   */
 380  class mockup_rating_manager extends rating_manager {
 381  
 382      /**
 383       * Overwrite get_plugin_permissions_array() so it always return granted perms for unit testing
 384       */
 385      public function get_plugin_permissions_array($contextid, $component, $ratingarea) {
 386          return array(
 387              'rate' => true,
 388              'view' => true,
 389              'viewany' => true,
 390              'viewall' => true);
 391      }
 392  
 393  }