See Release Notes
Long Term Support Release
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 /** 18 * This file contains unit test related to xAPI library. 19 * 20 * @package core_xapi 21 * @copyright 2020 Ferran Recio 22 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 23 */ 24 25 namespace core_xapi\local; 26 27 use core_xapi\local\statement\item; 28 use core_xapi\local\statement\item_actor; 29 use core_xapi\local\statement\item_object; 30 use core_xapi\local\statement\item_activity; 31 use core_xapi\local\statement\item_verb; 32 use core_xapi\local\statement\item_agent; 33 use core_xapi\local\statement\item_group; 34 use core_xapi\local\statement\item_result; 35 use core_xapi\local\statement\item_attachment; 36 use core_xapi\local\statement\item_context; 37 use core_xapi\iri; 38 use core_xapi\xapi_exception; 39 use advanced_testcase; 40 use stdClass; 41 42 defined('MOODLE_INTERNAL') || die(); 43 44 /** 45 * Contains test cases for testing statement class. 46 * 47 * @package core_xapi 48 * @since Moodle 3.9 49 * @copyright 2020 Ferran Recio 50 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 51 */ 52 class statement_test extends advanced_testcase { 53 54 /** 55 * Returns a valid item for a specific attribute. 56 * 57 * @param string $itemname statement item name 58 * @return item the resulting item 59 */ 60 private function get_valid_item(string $itemname): item { 61 global $USER, $CFG; 62 switch ($itemname) { 63 case 'attachments': 64 case 'attachment': 65 $data = (object) [ 66 'usageType' => iri::generate('example', 'attachment'), 67 'display' => (object) [ 68 'en-US' => 'Example', 69 ], 70 'description' => (object) [ 71 'en-US' => 'Description example', 72 ], 73 "contentType" => "image/jpg", 74 "length" => 1234, 75 "sha2" => "b94c0f1cffb77475c6f1899111a0181efe1d6177" 76 ]; 77 return item_attachment::create_from_data($data); 78 case 'authority': 79 $data = (object) [ 80 'objectType' => 'Agent', 81 'account' => (object) [ 82 'homePage' => $CFG->wwwroot, 83 'name' => $USER->id, 84 ], 85 ]; 86 return item_agent::create_from_data($data); 87 } 88 // For now, the rest of the optional properties have no validation 89 // so we create a standard stdClass for all of them. 90 $data = (object)[ 91 'some' => 'data', 92 ]; 93 $classname = 'core_xapi\local\statement\item_'.$itemname; 94 if (class_exists($classname)) { 95 $item = $classname::create_from_data($data); 96 } else { 97 $item = item::create_from_data($data); 98 } 99 return $item; 100 } 101 102 /** 103 * Test statement creation. 104 * 105 * @dataProvider create_provider 106 * @param bool $useagent if use agent as actor (or group if false) 107 * @param array $extras extra item elements 108 * @param array $extravalues extra string values 109 */ 110 public function test_create(bool $useagent, array $extras, array $extravalues) { 111 112 $this->resetAfterTest(); 113 114 // Create one course with a group. 115 $course = $this->getDataGenerator()->create_course(); 116 $user = $this->getDataGenerator()->create_user(); 117 $this->getDataGenerator()->enrol_user($user->id, $course->id); 118 $group = $this->getDataGenerator()->create_group(array('courseid' => $course->id)); 119 $this->getDataGenerator()->create_group_member(array('groupid' => $group->id, 'userid' => $user->id)); 120 121 $this->setUser($user); 122 123 // Our statement. 124 $statement = new statement(); 125 126 // Populate statement. 127 if ($useagent) { 128 $statement->set_actor(item_agent::create_from_user($user)); 129 } else { 130 $statement->set_actor(item_group::create_from_group($group)); 131 } 132 $statement->set_verb(item_verb::create_from_id('cook')); 133 $statement->set_object(item_activity::create_from_id('paella')); 134 135 foreach ($extras as $extra) { 136 $method = 'set_'.$extra; 137 $item = $this->get_valid_item($extra); 138 $statement->$method($item); 139 } 140 141 // For now extra values have no validation. 142 foreach ($extravalues as $extra) { 143 $method = 'set_'.$extra; 144 $statement->$method('Example'); 145 } 146 147 // Check resulting statement. 148 if ($useagent) { 149 $stuser = $statement->get_user(); 150 $this->assertEquals($user->id, $stuser->id); 151 $stusers = $statement->get_all_users(); 152 $this->assertCount(1, $stusers); 153 } else { 154 $stgroup = $statement->get_group(); 155 $this->assertEquals($group->id, $stgroup->id); 156 $stusers = $statement->get_all_users(); 157 $this->assertCount(1, $stusers); 158 $stuser = array_shift($stusers); 159 $this->assertEquals($user->id, $stuser->id); 160 } 161 $this->assertEquals('cook', $statement->get_verb_id()); 162 $this->assertEquals('paella', $statement->get_activity_id()); 163 164 // Check resulting json (only first node structure, internal structure 165 // depends on every item json_encode test). 166 $data = json_decode(json_encode($statement)); 167 $this->assertNotEmpty($data->actor); 168 $this->assertNotEmpty($data->verb); 169 $this->assertNotEmpty($data->object); 170 $allextras = ['context', 'result', 'timestamp', 'stored', 'authority', 'version', 'attachments']; 171 $alldefined = array_merge($extras, $extravalues); 172 foreach ($allextras as $extra) { 173 if (in_array($extra, $alldefined)) { 174 $this->assertObjectHasAttribute($extra, $data); 175 $this->assertNotEmpty($data->$extra); 176 } else { 177 $this->assertObjectNotHasAttribute($extra, $data); 178 } 179 } 180 } 181 182 /** 183 * Data provider for the test_create and test_create_from_data tests. 184 * 185 * @return array 186 */ 187 public function create_provider() : array { 188 return [ 189 'Agent statement with no extras' => [ 190 true, [], [] 191 ], 192 'Agent statement with context' => [ 193 true, ['context'], [] 194 ], 195 'Agent statement with result' => [ 196 true, ['result'], [] 197 ], 198 'Agent statement with timestamp' => [ 199 true, [], ['timestamp'] 200 ], 201 'Agent statement with stored' => [ 202 true, [], ['stored'] 203 ], 204 'Agent statement with authority' => [ 205 true, ['authority'], [] 206 ], 207 'Agent statement with version' => [ 208 true, [], ['version'] 209 ], 210 'Group statement with no extras' => [ 211 false, [], [] 212 ], 213 'Group statement with context' => [ 214 false, ['context'], [] 215 ], 216 'Group statement with result' => [ 217 false, ['result'], [] 218 ], 219 'Group statement with timestamp' => [ 220 false, [], ['timestamp'] 221 ], 222 'Group statement with stored' => [ 223 false, [], ['stored'] 224 ], 225 'Group statement with authority' => [ 226 false, ['authority'], [] 227 ], 228 'Group statement with version' => [ 229 false, [], ['version'] 230 ], 231 ]; 232 } 233 234 /** 235 * Test statement creation from xAPI statement data. 236 * 237 * @dataProvider create_provider 238 * @param bool $useagent if use agent as actor (or group if false) 239 * @param array $extras extra item elements 240 * @param array $extravalues extra string values 241 */ 242 public function test_create_from_data(bool $useagent, array $extras, array $extravalues) { 243 $this->resetAfterTest(); 244 245 // Create one course with a group. 246 $course = $this->getDataGenerator()->create_course(); 247 $user = $this->getDataGenerator()->create_user(); 248 $this->getDataGenerator()->enrol_user($user->id, $course->id); 249 $group = $this->getDataGenerator()->create_group(array('courseid' => $course->id)); 250 $this->getDataGenerator()->create_group_member(array('groupid' => $group->id, 'userid' => $user->id)); 251 252 $this->setUser($user); 253 254 // Populate data. 255 if ($useagent) { 256 $actor = item_agent::create_from_user($user); 257 } else { 258 $actor = item_group::create_from_group($group); 259 } 260 $verb = item_verb::create_from_id('cook'); 261 $object = item_activity::create_from_id('paella'); 262 263 $data = (object) [ 264 'actor' => $actor->get_data(), 265 'verb' => $verb->get_data(), 266 'object' => $object->get_data(), 267 ]; 268 269 foreach ($extras as $extra) { 270 $item = $this->get_valid_item($extra); 271 $data->$extra = $item->get_data(); 272 } 273 274 // For now extra values have no validation. 275 foreach ($extravalues as $extra) { 276 $data->$extra = 'Example'; 277 } 278 279 $statement = statement::create_from_data($data); 280 281 // Check resulting statement. 282 if ($useagent) { 283 $stuser = $statement->get_user(); 284 $this->assertEquals($user->id, $stuser->id); 285 $stusers = $statement->get_all_users(); 286 $this->assertCount(1, $stusers); 287 } else { 288 $stgroup = $statement->get_group(); 289 $this->assertEquals($group->id, $stgroup->id); 290 $stusers = $statement->get_all_users(); 291 $this->assertCount(1, $stusers); 292 $stuser = array_shift($stusers); 293 $this->assertEquals($user->id, $stuser->id); 294 } 295 $this->assertEquals('cook', $statement->get_verb_id()); 296 $this->assertEquals('paella', $statement->get_activity_id()); 297 298 // Check resulting json (only first node structure, internal structure 299 // depends on every item json_encode test). 300 $data = json_decode(json_encode($statement)); 301 $this->assertNotEmpty($data->actor); 302 $this->assertNotEmpty($data->verb); 303 $this->assertNotEmpty($data->object); 304 $allextras = ['context', 'result', 'timestamp', 'stored', 'authority', 'version', 'attachments']; 305 $alldefined = array_merge($extras, $extravalues); 306 foreach ($allextras as $extra) { 307 if (in_array($extra, $alldefined)) { 308 $this->assertObjectHasAttribute($extra, $data); 309 $this->assertNotEmpty($data->object); 310 } else { 311 $this->assertObjectNotHasAttribute($extra, $data); 312 } 313 } 314 } 315 316 /** 317 * Test adding attachments to statement. 318 * 319 */ 320 public function test_add_attachment() { 321 322 // Our statement. 323 $statement = new statement(); 324 325 $attachments = $statement->get_attachments(); 326 $this->assertNull($attachments); 327 328 $item = $this->get_valid_item('attachment'); 329 $itemdata = $item->get_data(); 330 $statement->add_attachment($item); 331 332 $attachments = $statement->get_attachments(); 333 $this->assertNotNull($attachments); 334 $this->assertCount(1, $attachments); 335 336 $attachment = current($attachments); 337 $attachmentdata = $attachment->get_data(); 338 $this->assertEquals($itemdata->usageType, $attachmentdata->usageType); 339 $this->assertEquals($itemdata->length, $attachmentdata->length); 340 341 // Check resulting json. 342 $statementdata = json_decode(json_encode($statement)); 343 $this->assertObjectHasAttribute('attachments', $statementdata); 344 $this->assertNotEmpty($statementdata->attachments); 345 $this->assertCount(1, $statementdata->attachments); 346 } 347 348 /** 349 * Test adding attachments to statement. 350 * 351 */ 352 public function test_add_attachment_from_data() { 353 354 $this->resetAfterTest(); 355 356 $user = $this->getDataGenerator()->create_user(); 357 $this->setUser($user); 358 359 $actor = item_agent::create_from_user($user); 360 $verb = item_verb::create_from_id('cook'); 361 $object = item_activity::create_from_id('paella'); 362 363 $data = (object) [ 364 'actor' => $actor->get_data(), 365 'verb' => $verb->get_data(), 366 'object' => $object->get_data(), 367 ]; 368 369 $item = $this->get_valid_item('attachment'); 370 $itemdata = $item->get_data(); 371 $data->attachments = [$itemdata]; 372 373 $statement = statement::create_from_data($data); 374 375 $attachments = $statement->get_attachments(); 376 $this->assertNotNull($attachments); 377 $this->assertCount(1, $attachments); 378 379 $attachment = current($attachments); 380 $attachmentdata = $attachment->get_data(); 381 $this->assertEquals($itemdata->usageType, $attachmentdata->usageType); 382 $this->assertEquals($itemdata->length, $attachmentdata->length); 383 384 $statementdata = json_decode(json_encode($statement)); 385 $this->assertObjectHasAttribute('attachments', $statementdata); 386 $this->assertNotEmpty($statementdata->attachments); 387 $this->assertCount(1, $statementdata->attachments); 388 389 // Now try to send an invalid attachments. 390 $this->expectException(xapi_exception::class); 391 $data->attachments = 'Invalid data'; 392 $statement = statement::create_from_data($data); 393 } 394 395 /** 396 * Test all getters into a not set statement. 397 * 398 * @dataProvider invalid_gets_provider 399 * @param string $method the method to test 400 * @param bool $exception if an exception is expected 401 */ 402 public function test_invalid_gets(string $method, bool $exception) { 403 $statement = new statement(); 404 if ($exception) { 405 $this->expectException(xapi_exception::class); 406 } 407 $result = $statement->$method(); 408 $this->assertNull($result); 409 } 410 411 /** 412 * Data provider for the text_invalid_gets. 413 * 414 * @return array 415 */ 416 public function invalid_gets_provider() : array { 417 return [ 418 'Method get_user on empty statement' => ['get_user', true], 419 'Method get_all_users on empty statement' => ['get_all_users', true], 420 'Method get_group on empty statement' => ['get_group', true], 421 'Method get_verb_id on empty statement' => ['get_verb_id', true], 422 'Method get_activity_id on empty statement' => ['get_activity_id', true], 423 'Method get_actor on empty statement' => ['get_actor', false], 424 'Method get_verb on empty statement' => ['get_verb', false], 425 'Method get_object on empty statement' => ['get_object', false], 426 'Method get_context on empty statement' => ['get_context', false], 427 'Method get_result on empty statement' => ['get_result', false], 428 'Method get_timestamp on empty statement' => ['get_timestamp', false], 429 'Method get_stored on empty statement' => ['get_stored', false], 430 'Method get_authority on empty statement' => ['get_authority', false], 431 'Method get_version on empty statement' => ['get_version', false], 432 'Method get_attachments on empty statement' => ['get_attachments', false], 433 ]; 434 } 435 436 /** 437 * Try to get a user from a group statement. 438 */ 439 public function test_invalid_get_user() { 440 441 $this->resetAfterTest(); 442 443 // Create one course with a group. 444 $course = $this->getDataGenerator()->create_course(); 445 $user = $this->getDataGenerator()->create_user(); 446 $this->getDataGenerator()->enrol_user($user->id, $course->id); 447 $group = $this->getDataGenerator()->create_group(array('courseid' => $course->id)); 448 $this->getDataGenerator()->create_group_member(array('groupid' => $group->id, 'userid' => $user->id)); 449 450 // Our statement. 451 $statement = new statement(); 452 453 // Populate statement. 454 $statement->set_actor(item_group::create_from_group($group)); 455 $statement->set_verb(item_verb::create_from_id('cook')); 456 $statement->set_object(item_activity::create_from_id('paella')); 457 458 $this->expectException(xapi_exception::class); 459 $statement->get_user(); 460 } 461 462 /** 463 * Try to get a group from an agent statement. 464 */ 465 public function test_invalid_get_group() { 466 $this->resetAfterTest(); 467 468 $user = $this->getDataGenerator()->create_user(); 469 470 // Our statement. 471 $statement = new statement(); 472 473 // Populate statement. 474 $statement->set_actor(item_agent::create_from_user($user)); 475 $statement->set_verb(item_verb::create_from_id('cook')); 476 $statement->set_object(item_activity::create_from_id('paella')); 477 478 $this->expectException(xapi_exception::class); 479 $statement->get_group(); 480 } 481 482 /** 483 * Try to get activity Id from a statement with agent object. 484 */ 485 public function test_invalid_get_activity_id() { 486 $this->resetAfterTest(); 487 488 $user = $this->getDataGenerator()->create_user(); 489 490 // Our statement. 491 $statement = new statement(); 492 493 // Populate statement with and agent object. 494 $statement->set_actor(item_agent::create_from_user($user)); 495 $statement->set_verb(item_verb::create_from_id('cook')); 496 $statement->set_object(item_agent::create_from_user($user)); 497 498 $this->expectException(xapi_exception::class); 499 $statement->get_activity_id(); 500 } 501 502 /** 503 * Test for invalid structures. 504 * 505 * @dataProvider invalid_data_provider 506 * @param bool $useuser if use user into statement 507 * @param bool $userverb if use verb into statement 508 * @param bool $useobject if use object into statement 509 */ 510 public function test_invalid_data(bool $useuser, bool $userverb, bool $useobject): void { 511 512 $data = new stdClass(); 513 514 if ($useuser) { 515 $this->resetAfterTest(); 516 $user = $this->getDataGenerator()->create_user(); 517 $data->actor = item_agent::create_from_user($user); 518 } 519 if ($userverb) { 520 $data->verb = item_verb::create_from_id('cook'); 521 } 522 if ($useobject) { 523 $data->object = item_activity::create_from_id('paella'); 524 } 525 526 $this->expectException(xapi_exception::class); 527 $statement = statement::create_from_data($data); 528 } 529 530 /** 531 * Data provider for the test_invalid_data tests. 532 * 533 * @return array 534 */ 535 public function invalid_data_provider() : array { 536 return [ 537 'No actor, no verb, no object' => [false, false, false], 538 'No actor, verb, no object' => [false, true, false], 539 'No actor, no verb, object' => [false, false, true], 540 'No actor, verb, object' => [false, true, true], 541 'Actor, no verb, no object' => [true, false, false], 542 'Actor, verb, no object' => [true, true, false], 543 'Actor, no verb, object' => [true, false, true], 544 ]; 545 } 546 547 /** 548 * Test minify statement. 549 */ 550 public function test_minify() { 551 552 $this->resetAfterTest(); 553 554 $user = $this->getDataGenerator()->create_user(); 555 556 $this->setUser($user); 557 558 // Our statement. 559 $statement = new statement(); 560 561 // Populate statement. 562 $statement->set_actor(item_agent::create_from_user($user)); 563 $statement->set_verb(item_verb::create_from_id('cook')); 564 $statement->set_object(item_activity::create_from_id('paella')); 565 $statement->set_result($this->get_valid_item('result')); 566 $statement->set_context($this->get_valid_item('context')); 567 $statement->set_authority($this->get_valid_item('authority')); 568 $statement->add_attachment($this->get_valid_item('attachment')); 569 $statement->set_version('Example'); 570 $statement->set_timestamp('Example'); 571 $statement->set_stored('Example'); 572 573 $min = $statement->minify(); 574 575 // Check calculated fields. 576 $this->assertCount(6, $min); 577 $this->assertArrayNotHasKey('actor', $min); 578 $this->assertArrayHasKey('verb', $min); 579 $this->assertArrayHasKey('object', $min); 580 $this->assertArrayHasKey('context', $min); 581 $this->assertArrayHasKey('result', $min); 582 $this->assertArrayNotHasKey('timestamp', $min); 583 $this->assertArrayNotHasKey('stored', $min); 584 $this->assertArrayHasKey('authority', $min); 585 $this->assertArrayNotHasKey('version', $min); 586 $this->assertArrayHasKey('attachments', $min); 587 } 588 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body