Search moodle.org's
Developer Documentation

See Release Notes

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

Differences Between: [Versions 310 and 403] [Versions 311 and 403] [Versions 39 and 403] [Versions 400 and 403] [Versions 401 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  namespace core_xapi;
  18  
  19  use core_xapi\xapi_exception;
  20  use core_xapi\local\statement;
  21  use core_xapi\local\statement\item_agent;
  22  use core_xapi\local\statement\item_verb;
  23  use core_xapi\local\statement\item_activity;
  24  use advanced_testcase;
  25  use core_xapi\local\state;
  26  use stdClass;
  27  
  28  /**
  29   * Contains test cases for testing xAPI handler base methods.
  30   *
  31   * @package    core_xapi
  32   * @since      Moodle 3.9
  33   * @covers     \core_xapi\handler
  34   * @copyright  2020 Ferran Recio
  35   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  36   */
  37  class handler_test extends advanced_testcase {
  38  
  39      /**
  40       * Setup to ensure that fixtures are loaded.
  41       */
  42      public static function setUpBeforeClass(): void {
  43          global $CFG;
  44          require_once($CFG->dirroot.'/lib/xapi/tests/helper.php');
  45      }
  46  
  47      /**
  48       * Test handler creation.
  49       */
  50      public function test_handler_create() {
  51          // Get an existent handler.
  52          $handler = handler::create('fake_component');
  53          $this->assertEquals(get_class($handler), 'fake_component\\xapi\\handler');
  54  
  55          // Get a non existent handler.
  56          $this->expectException(xapi_exception::class);
  57          $value = handler::create('potato_omelette');
  58      }
  59  
  60      /**
  61       * Test xAPI support.
  62       */
  63      public function test_supports_xapi() {
  64          // Get an existent handler.
  65          $result = handler::supports_xapi('fake_component');
  66          $this->assertTrue($result);
  67  
  68          // Get a non existent handler.
  69          $result = handler::supports_xapi('potato_omelette');
  70          $this->assertFalse($result);
  71      }
  72  
  73      /**
  74       * Test support group.
  75       */
  76      public function test_support_group_actor() {
  77          global $CFG;
  78          // Get an existent handler.
  79          $this->resetAfterTest();
  80          $handler = handler::create('fake_component');
  81          $this->assertEquals(get_class($handler), 'fake_component\\xapi\\handler');
  82          $CFG->xapitestforcegroupactors = false;
  83          $this->assertEquals(false, $handler->supports_group_actors());
  84      }
  85  
  86      /**
  87       * Test for process_statements method.
  88       */
  89      public function test_process_statements() {
  90  
  91          $this->resetAfterTest();
  92          $this->preventResetByRollback(); // Logging waits till the transaction gets committed.
  93  
  94          $user = $this->getDataGenerator()->create_user();
  95  
  96          $testhelper = new test_helper();
  97          $testhelper->init_log();
  98  
  99          // Generate a 2 statements array (one accepted one not).
 100          $statements = [];
 101  
 102          $statement = new statement();
 103          $statement->set_actor(item_agent::create_from_user($user));
 104          $statement->set_verb(item_verb::create_from_id('cook'));
 105          $statement->set_object(item_activity::create_from_id('paella'));
 106          $statements[] = $statement;
 107  
 108          $statement2 = new statement();
 109          $statement2->set_actor(item_agent::create_from_user($user));
 110          $statement2->set_verb(item_verb::create_from_id('invalid'));
 111          $statement2->set_object(item_activity::create_from_id('paella'));
 112          $statements[] = $statement2;
 113  
 114          $handler = handler::create('fake_component');
 115          $result = $handler->process_statements($statements);
 116  
 117          // Check results.
 118          $this->assertCount(2, $result);
 119          $this->assertEquals(true, $result[0]);
 120          $this->assertEquals(false, $result[1]);
 121  
 122          // Check log entries.
 123          /** @var \core_xapi\event\xapi_test_statement_post $log */
 124          $log = $testhelper->get_last_log_entry();
 125          $this->assertNotEmpty($log);
 126  
 127          // Validate statement information on log.
 128          $value = $log->get_name();
 129          $this->assertEquals($value, 'xAPI test statement');
 130          $value = $log->get_description();
 131          // Due to logstore limitation, event must use a real component (core_xapi).
 132          $this->assertEquals($value, 'User \''.$user->id.'\' send a statement to component \'core_xapi\'');
 133          $this->assertTrue($log->compare_statement($statement));
 134      }
 135  
 136      /**
 137       * Testing save_state method.
 138       */
 139      public function test_save_state(): void {
 140          global $DB;
 141  
 142          $this->resetAfterTest();
 143  
 144          // Scenario.
 145          $this->setAdminUser();
 146          $component = 'fake_component';
 147          $handler = handler::create($component);
 148  
 149          // Check the state has been added.
 150          $state = test_helper::create_state();
 151          $this->assertEquals(0, $DB->count_records('xapi_states'));
 152          $result = $handler->save_state($state);
 153          $this->assertTrue($result);
 154          $records = $DB->get_records('xapi_states');
 155          $this->assertCount(1, $records);
 156          $record = reset($records);
 157          $this->check_state($component, $state, $record);
 158  
 159          // Check the state has been updated.
 160          $statedata = '{"progress":0,"answers":[[["BB"],[""]],[{"answers":[]}]],"answered":[true,false]}';
 161          $state->set_state_data(json_decode($statedata));
 162          $result = $handler->save_state($state);
 163          $this->assertTrue($result);
 164          $records = $DB->get_records('xapi_states');
 165          $this->assertCount(1, $records);
 166          $record = reset($records);
 167          $this->check_state($component, $state, $record);
 168  
 169          // Check an exception is thrown when the state is not valid.
 170          $this->expectException(xapi_exception::class);
 171          $state = test_helper::create_state(['stateid' => 'paella']);
 172          $result = $handler->save_state($state);
 173      }
 174  
 175      /**
 176       * Testing load_state method.
 177       */
 178      public function test_load_state(): void {
 179          global $DB;
 180  
 181          $this->resetAfterTest();
 182  
 183          // Scenario.
 184          $this->setAdminUser();
 185          $component = 'fake_component';
 186          $handler = handler::create($component);
 187  
 188          // Check the state is not found (when there are no states).
 189          $state = test_helper::create_state();
 190          $state->set_state_data(null);
 191          $this->assertEquals(0, $DB->count_records('xapi_states'));
 192          $result = $handler->load_state($state);
 193          $this->assertEquals(0, $DB->count_records('xapi_states'));
 194          $this->assertNull($result);
 195  
 196          // Add, at least, one xAPI state record to database (with the default values).
 197          test_helper::create_state([], true);
 198  
 199          // Check the state is found when it exists.
 200          $result = $handler->load_state($state);
 201          $records = $DB->get_records('xapi_states');
 202          $this->assertCount(1, $records);
 203          $record = reset($records);
 204          $this->check_state($component, $state, $record);
 205          $this->assertEquals($state->jsonSerialize(), $result->jsonSerialize());
 206  
 207          // Check the state is not found when it doesn't exist.
 208          $state = test_helper::create_state(['activity' => item_activity::create_from_id('1')]);
 209          $state->set_state_data(null);
 210          $result = $handler->load_state($state);
 211          $records = $DB->get_records('xapi_states');
 212          $this->assertCount(1, $DB->get_records('xapi_states'));
 213          $this->assertNull($result);
 214  
 215          // Check an exception is thrown when the state is not valid.
 216          $this->expectException(xapi_exception::class);
 217          $state = test_helper::create_state(['stateid' => 'paella']);
 218          $result = $handler->load_state($state);
 219      }
 220  
 221      /**
 222       * Testing delete_state method.
 223       */
 224      public function test_delete_state(): void {
 225          global $DB;
 226  
 227          $this->resetAfterTest();
 228  
 229          // Scenario.
 230          $this->setAdminUser();
 231          $component = 'fake_component';
 232          $handler = handler::create($component);
 233  
 234          // Check the state is not deleted (when there are no states).
 235          $state = test_helper::create_state();
 236          $this->assertEquals(0, $DB->count_records('xapi_states'));
 237          $result = $handler->delete_state($state);
 238          $this->assertTrue($result);
 239          $this->assertEquals(0, $DB->count_records('xapi_states'));
 240  
 241          // Add, at least, one xAPI state record to database (with the default values).
 242          test_helper::create_state([], true);
 243  
 244          // Check the state is not deleted if the given state doesn't meet its values.
 245          $state2 = test_helper::create_state(['activity' => item_activity::create_from_id('1')]);
 246          $result = $handler->delete_state($state2);
 247          $this->assertTrue($result);
 248          $this->assertCount(1, $DB->get_records('xapi_states'));
 249  
 250          // Check the state is deleted if it exists.
 251          $result = $handler->delete_state($state);
 252          $this->assertTrue($result);
 253          $this->assertCount(0, $DB->get_records('xapi_states'));
 254  
 255          // Check an exception is thrown when the state is not valid.
 256          $this->expectException(xapi_exception::class);
 257          $state = test_helper::create_state(['stateid' => 'paella']);
 258          $result = $handler->delete_state($state);
 259      }
 260  
 261      /**
 262       * Testing reset_states method.
 263       */
 264      public function test_reset_states(): void {
 265          global $DB;
 266  
 267          $this->resetAfterTest();
 268  
 269          // Scenario.
 270          $this->setAdminUser();
 271          $component = 'fake_component';
 272          $handler = handler::create($component);
 273  
 274          // Check the state is not reset (when there are no states).
 275          $this->assertCount(0, $DB->get_records_select('xapi_states', 'statedata IS NULL'));
 276          $handler->reset_states();
 277          $this->assertCount(0, $DB->get_records_select('xapi_states', 'statedata IS NULL'));
 278  
 279          // Add, at least, one xAPI state record to database (with the default values).
 280          test_helper::create_state([], true);
 281  
 282          // Check the state is not reset if the given state doesn't meet its values.
 283          $handler->reset_states('1');
 284          $this->assertCount(1, $DB->get_records('xapi_states'));
 285          $this->assertCount(0, $DB->get_records_select('xapi_states', 'statedata IS NULL'));
 286  
 287          // Check the state is reset if it exists.
 288          $handler->reset_states();
 289          $this->assertCount(1, $DB->get_records('xapi_states'));
 290          $this->assertCount(1, $DB->get_records_select('xapi_states', 'statedata IS NULL'));
 291  
 292          // Check the state is reset too when using some of the given parameters.
 293          test_helper::create_state(['activity' => item_activity::create_from_id('1')], true);
 294          $handler->reset_states('1');
 295          $this->assertCount(2, $DB->get_records('xapi_states'));
 296          $this->assertCount(2, $DB->get_records_select('xapi_states', 'statedata IS NULL'));
 297      }
 298  
 299      /**
 300       * Testing wipe_states method.
 301       */
 302      public function test_wipe_states(): void {
 303          global $DB;
 304  
 305          $this->resetAfterTest();
 306  
 307          // Scenario.
 308          $this->setAdminUser();
 309          $component = 'fake_component';
 310          $handler = handler::create($component);
 311  
 312          // Check the state is not wiped (when there are no states).
 313          $this->assertCount(0, $DB->get_records('xapi_states'));
 314          $handler->wipe_states();
 315          $this->assertCount(0, $DB->get_records('xapi_states'));
 316  
 317          // Add, at least, one xAPI state record to database (with the default values).
 318          test_helper::create_state([], true);
 319  
 320          // Check the state is not wiped if the given state doesn't meet its values.
 321          $handler->wipe_states('1');
 322          $this->assertCount(1, $DB->get_records('xapi_states'));
 323  
 324          // Check the state is wiped if it exists.
 325          $handler->wipe_states();
 326          $this->assertCount(0, $DB->get_records('xapi_states'));
 327  
 328          // Check the state is wiped too when using some of the given parameters.
 329          test_helper::create_state(['activity' => item_activity::create_from_id('1')], true);
 330          $this->assertCount(1, $DB->get_records('xapi_states'));
 331          $handler->wipe_states('1');
 332          $this->assertCount(0, $DB->get_records('xapi_states'));
 333      }
 334  
 335      /**
 336       * Check if the given state and record are equals.
 337       *
 338       * @param string $component The component name in frankenstyle.
 339       * @param state $state The state to check.
 340       * @param stdClass $record The record to be compared with the state.
 341       */
 342      private function check_state(string $component, state $state, stdClass $record): void {
 343          $this->assertEquals($component, $record->component);
 344          $this->assertEquals($state->get_activity_id(), $record->itemid);
 345          $this->assertEquals($state->get_user()->id, $record->userid);
 346          $this->assertEquals(json_encode($state->jsonSerialize()), $record->statedata);
 347          $this->assertEquals($state->get_registration(), $record->registration);
 348      }
 349  
 350  }