Differences Between: [Versions 310 and 311] [Versions 311 and 400] [Versions 311 and 401] [Versions 38 and 311] [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 namespace core_competency; 18 19 /** 20 * API tests. 21 * 22 * @package core_competency 23 * @copyright 2015 Frédéric Massart - FMCorz.net 24 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 25 */ 26 class api_test extends \advanced_testcase { 27 28 public function test_get_framework_related_contexts() { 29 $this->resetAfterTest(true); 30 $dg = $this->getDataGenerator(); 31 $cat1 = $dg->create_category(); 32 $cat2 = $dg->create_category(array('parent' => $cat1->id)); 33 $cat3 = $dg->create_category(array('parent' => $cat2->id)); 34 $c1 = $dg->create_course(array('category' => $cat2->id)); // This context should not be returned. 35 36 $cat1ctx = \context_coursecat::instance($cat1->id); 37 $cat2ctx = \context_coursecat::instance($cat2->id); 38 $cat3ctx = \context_coursecat::instance($cat3->id); 39 $sysctx = \context_system::instance(); 40 41 $expected = array($cat1ctx->id => $cat1ctx); 42 $this->assertEquals($expected, api::get_related_contexts($cat1ctx, 'self')); 43 44 $expected = array($cat1ctx->id => $cat1ctx, $cat2ctx->id => $cat2ctx, $cat3ctx->id => $cat3ctx); 45 $this->assertEquals($expected, api::get_related_contexts($cat1ctx, 'children')); 46 47 $expected = array($sysctx->id => $sysctx, $cat1ctx->id => $cat1ctx, $cat2ctx->id => $cat2ctx); 48 $this->assertEquals($expected, api::get_related_contexts($cat2ctx, 'parents')); 49 } 50 51 public function test_get_framework_related_contexts_with_capabilities() { 52 $this->resetAfterTest(true); 53 $dg = $this->getDataGenerator(); 54 $user = $dg->create_user(); 55 $cat1 = $dg->create_category(); 56 $cat2 = $dg->create_category(array('parent' => $cat1->id)); 57 $cat3 = $dg->create_category(array('parent' => $cat2->id)); 58 $c1 = $dg->create_course(array('category' => $cat2->id)); // This context should not be returned. 59 60 $cat1ctx = \context_coursecat::instance($cat1->id); 61 $cat2ctx = \context_coursecat::instance($cat2->id); 62 $cat3ctx = \context_coursecat::instance($cat3->id); 63 $sysctx = \context_system::instance(); 64 65 $roleallow = create_role('Allow', 'allow', 'Allow read'); 66 assign_capability('moodle/competency:competencyview', CAP_ALLOW, $roleallow, $sysctx->id); 67 role_assign($roleallow, $user->id, $sysctx->id); 68 69 $roleprevent = create_role('Prevent', 'prevent', 'Prevent read'); 70 assign_capability('moodle/competency:competencyview', CAP_PROHIBIT, $roleprevent, $sysctx->id); 71 role_assign($roleprevent, $user->id, $cat2ctx->id); 72 73 accesslib_clear_all_caches_for_unit_testing(); 74 $this->setUser($user); 75 $this->assertFalse(has_capability('moodle/competency:competencyview', $cat2ctx)); 76 77 $requiredcap = array('moodle/competency:competencyview'); 78 79 $expected = array(); 80 $this->assertEquals($expected, api::get_related_contexts($cat2ctx, 'self', $requiredcap)); 81 82 $expected = array($cat1ctx->id => $cat1ctx); 83 $this->assertEquals($expected, api::get_related_contexts($cat1ctx, 'children', $requiredcap)); 84 85 $expected = array($sysctx->id => $sysctx, $cat1ctx->id => $cat1ctx); 86 $this->assertEquals($expected, api::get_related_contexts($cat2ctx, 'parents', $requiredcap)); 87 } 88 89 public function test_get_template_related_contexts() { 90 $this->resetAfterTest(true); 91 $dg = $this->getDataGenerator(); 92 $cat1 = $dg->create_category(); 93 $cat2 = $dg->create_category(array('parent' => $cat1->id)); 94 $cat3 = $dg->create_category(array('parent' => $cat2->id)); 95 $c1 = $dg->create_course(array('category' => $cat2->id)); // This context should not be returned. 96 97 $cat1ctx = \context_coursecat::instance($cat1->id); 98 $cat2ctx = \context_coursecat::instance($cat2->id); 99 $cat3ctx = \context_coursecat::instance($cat3->id); 100 $sysctx = \context_system::instance(); 101 102 $expected = array($cat1ctx->id => $cat1ctx); 103 $this->assertEquals($expected, api::get_related_contexts($cat1ctx, 'self')); 104 105 $expected = array($cat1ctx->id => $cat1ctx, $cat2ctx->id => $cat2ctx, $cat3ctx->id => $cat3ctx); 106 $this->assertEquals($expected, api::get_related_contexts($cat1ctx, 'children')); 107 108 $expected = array($sysctx->id => $sysctx, $cat1ctx->id => $cat1ctx, $cat2ctx->id => $cat2ctx); 109 $this->assertEquals($expected, api::get_related_contexts($cat2ctx, 'parents')); 110 } 111 112 public function test_get_template_related_contexts_with_capabilities() { 113 $this->resetAfterTest(true); 114 $dg = $this->getDataGenerator(); 115 $user = $dg->create_user(); 116 $cat1 = $dg->create_category(); 117 $cat2 = $dg->create_category(array('parent' => $cat1->id)); 118 $cat3 = $dg->create_category(array('parent' => $cat2->id)); 119 $c1 = $dg->create_course(array('category' => $cat2->id)); // This context should not be returned. 120 121 $cat1ctx = \context_coursecat::instance($cat1->id); 122 $cat2ctx = \context_coursecat::instance($cat2->id); 123 $cat3ctx = \context_coursecat::instance($cat3->id); 124 $sysctx = \context_system::instance(); 125 126 $roleallow = create_role('Allow', 'allow', 'Allow read'); 127 assign_capability('moodle/competency:templateview', CAP_ALLOW, $roleallow, $sysctx->id); 128 role_assign($roleallow, $user->id, $sysctx->id); 129 130 $roleprevent = create_role('Prevent', 'prevent', 'Prevent read'); 131 assign_capability('moodle/competency:templateview', CAP_PROHIBIT, $roleprevent, $sysctx->id); 132 role_assign($roleprevent, $user->id, $cat2ctx->id); 133 134 accesslib_clear_all_caches_for_unit_testing(); 135 $this->setUser($user); 136 $this->assertFalse(has_capability('moodle/competency:templateview', $cat2ctx)); 137 138 $requiredcap = array('moodle/competency:templateview'); 139 140 $expected = array(); 141 $this->assertEquals($expected, api::get_related_contexts($cat2ctx, 'self', $requiredcap)); 142 143 $expected = array($cat1ctx->id => $cat1ctx); 144 $this->assertEquals($expected, api::get_related_contexts($cat1ctx, 'children', $requiredcap)); 145 146 $expected = array($sysctx->id => $sysctx, $cat1ctx->id => $cat1ctx); 147 $this->assertEquals($expected, api::get_related_contexts($cat2ctx, 'parents', $requiredcap)); 148 } 149 150 /** 151 * Test updating a template. 152 */ 153 public function test_update_template() { 154 $cat = $this->getDataGenerator()->create_category(); 155 $this->resetAfterTest(true); 156 $this->setAdminUser(); 157 158 $syscontext = \context_system::instance(); 159 $template = api::create_template((object) array('shortname' => 'testing', 'contextid' => $syscontext->id)); 160 161 $this->assertEquals('testing', $template->get('shortname')); 162 $this->assertEquals($syscontext->id, $template->get('contextid')); 163 164 // Simple update. 165 api::update_template((object) array('id' => $template->get('id'), 'shortname' => 'success')); 166 $template = api::read_template($template->get('id')); 167 $this->assertEquals('success', $template->get('shortname')); 168 169 // Trying to change the context. 170 $this->expectException(\coding_exception::class); 171 api::update_template((object) array('id' => $template->get('id'), 'contextid' => \context_coursecat::instance($cat->id))); 172 } 173 174 /** 175 * Test listing framework with order param. 176 */ 177 public function test_list_frameworks() { 178 $this->resetAfterTest(true); 179 $this->setAdminUser(); 180 $lpg = $this->getDataGenerator()->get_plugin_generator('core_competency'); 181 182 // Create a list of frameworks. 183 $framework1 = $lpg->create_framework(array( 184 'shortname' => 'shortname_alpha', 185 'idnumber' => 'idnumber_cinnamon', 186 'description' => 'description', 187 'descriptionformat' => FORMAT_HTML, 188 'visible' => true, 189 'contextid' => \context_system::instance()->id 190 )); 191 192 $framework2 = $lpg->create_framework(array( 193 'shortname' => 'shortname_beetroot', 194 'idnumber' => 'idnumber_apple', 195 'description' => 'description', 196 'descriptionformat' => FORMAT_HTML, 197 'visible' => true, 198 'contextid' => \context_system::instance()->id 199 )); 200 201 $framework3 = $lpg->create_framework(array( 202 'shortname' => 'shortname_crisps', 203 'idnumber' => 'idnumber_beer', 204 'description' => 'description', 205 'descriptionformat' => FORMAT_HTML, 206 'visible' => false, 207 'contextid' => \context_system::instance()->id 208 )); 209 210 // Get frameworks list order by shortname desc. 211 $result = api::list_frameworks('shortname', 'DESC', null, 3, \context_system::instance()); 212 213 $f = (object) array_shift($result); 214 $this->assertEquals($framework3->get('id'), $f->get('id')); 215 $f = (object) array_shift($result); 216 $this->assertEquals($framework2->get('id'), $f->get('id')); 217 $f = (object) array_shift($result); 218 $this->assertEquals($framework1->get('id'), $f->get('id')); 219 220 // Get frameworks list order by idnumber asc. 221 $result = api::list_frameworks('idnumber', 'ASC', null, 3, \context_system::instance()); 222 223 $f = (object) array_shift($result); 224 $this->assertEquals($framework2->get('id'), $f->get('id')); 225 $f = (object) array_shift($result); 226 $this->assertEquals($framework3->get('id'), $f->get('id')); 227 $f = (object) array_shift($result); 228 $this->assertEquals($framework1->get('id'), $f->get('id')); 229 230 // Repeat excluding the non-visible ones. 231 $result = api::list_frameworks('idnumber', 'ASC', null, 3, \context_system::instance(), 'self', true); 232 $this->assertCount(2, $result); 233 $f = (object) array_shift($result); 234 $this->assertEquals($framework2->get('id'), $f->get('id')); 235 $f = (object) array_shift($result); 236 $this->assertEquals($framework1->get('id'), $f->get('id')); 237 238 // Search by query string, trying match on shortname. 239 $result = api::list_frameworks('idnumber', 'ASC', null, 3, \context_system::instance(), 'self', false, 'crisp'); 240 $this->assertCount(1, $result); 241 $f = (object) array_shift($result); 242 $this->assertEquals($framework3->get('id'), $f->get('id')); 243 244 // Search by query string, trying match on shortname, but hidden. 245 $result = api::list_frameworks('idnumber', 'ASC', null, 3, \context_system::instance(), 'self', true, 'crisp'); 246 $this->assertCount(0, $result); 247 248 // Search by query string, trying match on ID number. 249 $result = api::list_frameworks('idnumber', 'ASC', null, 3, \context_system::instance(), 'self', false, 'apple'); 250 $this->assertCount(1, $result); 251 $f = (object) array_shift($result); 252 $this->assertEquals($framework2->get('id'), $f->get('id')); 253 254 // Search by query string, trying match on both. 255 $result = api::list_frameworks('idnumber', 'ASC', null, 3, \context_system::instance(), 'self', false, 'bee'); 256 $this->assertCount(2, $result); 257 $f = (object) array_shift($result); 258 $this->assertEquals($framework2->get('id'), $f->get('id')); 259 $f = (object) array_shift($result); 260 $this->assertEquals($framework3->get('id'), $f->get('id')); 261 } 262 263 /** 264 * Test duplicate a framework. 265 */ 266 public function test_duplicate_framework() { 267 $lpg = $this->getDataGenerator()->get_plugin_generator('core_competency'); 268 $this->resetAfterTest(true); 269 $this->setAdminUser(); 270 271 $syscontext = \context_system::instance(); 272 $params = array( 273 'shortname' => 'shortname_a', 274 'idnumber' => 'idnumber_c', 275 'description' => 'description', 276 'descriptionformat' => FORMAT_HTML, 277 'visible' => true, 278 'contextid' => $syscontext->id 279 ); 280 $framework = $lpg->create_framework($params); 281 $competency1 = $lpg->create_competency(array('competencyframeworkid' => $framework->get('id'))); 282 $competency2 = $lpg->create_competency(array('competencyframeworkid' => $framework->get('id'))); 283 $competency3 = $lpg->create_competency(array('competencyframeworkid' => $framework->get('id'))); 284 $competency4 = $lpg->create_competency(array('competencyframeworkid' => $framework->get('id'))); 285 $competency41 = $lpg->create_competency(array('competencyframeworkid' => $framework->get('id'), 286 'parentid' => $competency4->get('id')) 287 ); 288 $competency42 = $lpg->create_competency(array('competencyframeworkid' => $framework->get('id'), 289 'parentid' => $competency4->get('id')) 290 ); 291 $competencyidnumbers = array($competency1->get('idnumber'), 292 $competency2->get('idnumber'), 293 $competency3->get('idnumber'), 294 $competency4->get('idnumber'), 295 $competency41->get('idnumber'), 296 $competency42->get('idnumber') 297 ); 298 299 $config = json_encode(array( 300 'base' => array('points' => 4), 301 'competencies' => array( 302 array('id' => $competency41->get('id'), 'points' => 3, 'required' => 0), 303 array('id' => $competency42->get('id'), 'points' => 2, 'required' => 1), 304 ) 305 )); 306 $competency4->set('ruletype', 'core_competency\competency_rule_points'); 307 $competency4->set('ruleoutcome', \core_competency\competency::OUTCOME_EVIDENCE); 308 $competency4->set('ruleconfig', $config); 309 $competency4->update(); 310 311 api::add_related_competency($competency1->get('id'), $competency2->get('id')); 312 api::add_related_competency($competency3->get('id'), $competency4->get('id')); 313 314 $frameworkduplicated1 = api::duplicate_framework($framework->get('id')); 315 $frameworkduplicated2 = api::duplicate_framework($framework->get('id')); 316 317 $this->assertEquals($framework->get('idnumber').'_1', $frameworkduplicated1->get('idnumber')); 318 $this->assertEquals($framework->get('idnumber').'_2', $frameworkduplicated2->get('idnumber')); 319 320 $competenciesfr1 = api::list_competencies(array('competencyframeworkid' => $frameworkduplicated1->get('id'))); 321 $competenciesfr2 = api::list_competencies(array('competencyframeworkid' => $frameworkduplicated2->get('id'))); 322 323 $competencyidsfr1 = array(); 324 $competencyidsfr2 = array(); 325 326 foreach ($competenciesfr1 as $cmp) { 327 $competencyidsfr1[] = $cmp->get('idnumber'); 328 } 329 foreach ($competenciesfr2 as $cmp) { 330 $competencyidsfr2[] = $cmp->get('idnumber'); 331 } 332 333 $this->assertEmpty(array_diff($competencyidsfr1, $competencyidnumbers)); 334 $this->assertEmpty(array_diff($competencyidsfr2, $competencyidnumbers)); 335 $this->assertCount(6, $competenciesfr1); 336 $this->assertCount(6, $competenciesfr2); 337 338 // Test the related competencies. 339 reset($competenciesfr1); 340 $compduplicated1 = current($competenciesfr1); 341 $relatedcompetencies = $compduplicated1->get_related_competencies(); 342 $comprelated = current($relatedcompetencies); 343 $this->assertEquals($comprelated->get('idnumber'), $competency2->get('idnumber')); 344 345 // Check if config rule have been ported correctly. 346 $competency4duplicated = competency::get_record(array( 347 'idnumber' => $competency4->get('idnumber'), 348 'competencyframeworkid' => $frameworkduplicated2->get('id') 349 )); 350 $configduplicated = json_decode($competency4duplicated->get('ruleconfig'), true); 351 $configorigin = json_decode($config, true); 352 // Check that the 2 config have the same base. 353 $this->assertEquals($configorigin['base'], $configduplicated['base']); 354 $this->assertEquals(count($configorigin['competencies']), count($configduplicated['competencies'])); 355 $competencyidsrules = array(); 356 foreach ($configduplicated['competencies'] as $key => $value) { 357 // Check that the only difference between the 2 config is id competency. 358 $this->assertEquals(1, count(array_diff($value, $configorigin['competencies'][$key]))); 359 $competencyidsrules[] = $value['id']; 360 } 361 $this->assertTrue($competency4duplicated->is_parent_of($competencyidsrules)); 362 363 // Test duplicate an empty framework. 364 $emptyfrm = $lpg->create_framework(); 365 $emptyfrmduplicated = api::duplicate_framework($emptyfrm->get('id')); 366 $this->assertEquals($emptyfrm->get('idnumber').'_1', $emptyfrmduplicated->get('idnumber')); 367 $nbcomp = api::count_competencies(array('competencyframeworkid' => $emptyfrmduplicated->get('id'))); 368 $this->assertEquals(0, $nbcomp); 369 370 } 371 372 /** 373 * Test update plan. 374 */ 375 public function test_update_plan() { 376 $this->resetAfterTest(true); 377 $dg = $this->getDataGenerator(); 378 $usermanageowndraft = $dg->create_user(); 379 $usermanageown = $dg->create_user(); 380 $usermanagedraft = $dg->create_user(); 381 $usermanage = $dg->create_user(); 382 383 $syscontext = \context_system::instance(); 384 385 // Creating specific roles. 386 $manageowndraftrole = $dg->create_role(array( 387 'name' => 'User manage own draft', 388 'shortname' => 'manage-own-draft' 389 )); 390 $manageownrole = $dg->create_role(array( 391 'name' => 'User manage own', 392 'shortname' => 'manage-own' 393 )); 394 $managedraftrole = $dg->create_role(array( 395 'name' => 'User manage draft', 396 'shortname' => 'manage-draft' 397 )); 398 $managerole = $dg->create_role(array( 399 'name' => 'User manage', 400 'shortname' => 'manage' 401 )); 402 403 assign_capability('moodle/competency:planmanageowndraft', CAP_ALLOW, $manageowndraftrole, $syscontext->id); 404 assign_capability('moodle/competency:planviewowndraft', CAP_ALLOW, $manageowndraftrole, $syscontext->id); 405 406 assign_capability('moodle/competency:planmanageown', CAP_ALLOW, $manageownrole, $syscontext->id); 407 assign_capability('moodle/competency:planviewown', CAP_ALLOW, $manageownrole, $syscontext->id); 408 409 assign_capability('moodle/competency:planmanagedraft', CAP_ALLOW, $managedraftrole, $syscontext->id); 410 assign_capability('moodle/competency:planviewdraft', CAP_ALLOW, $managedraftrole, $syscontext->id); 411 412 assign_capability('moodle/competency:planmanage', CAP_ALLOW, $managerole, $syscontext->id); 413 assign_capability('moodle/competency:planview', CAP_ALLOW, $managerole, $syscontext->id); 414 415 $dg->role_assign($manageowndraftrole, $usermanageowndraft->id, $syscontext->id); 416 $dg->role_assign($manageownrole, $usermanageown->id, $syscontext->id); 417 $dg->role_assign($managedraftrole, $usermanagedraft->id, $syscontext->id); 418 $dg->role_assign($managerole, $usermanage->id, $syscontext->id); 419 420 // Create first learning plan with user create draft. 421 $this->setUser($usermanageowndraft); 422 $plan = array ( 423 'name' => 'plan own draft', 424 'description' => 'plan own draft', 425 'userid' => $usermanageowndraft->id 426 ); 427 $plan = api::create_plan((object)$plan); 428 $record = $plan->to_record(); 429 $record->name = 'plan own draft modified'; 430 431 // Check if user create draft can edit the plan name. 432 $plan = api::update_plan($record); 433 $this->assertInstanceOf('\core_competency\plan', $plan); 434 435 // The status cannot be changed in this method. 436 $record->status = \core_competency\plan::STATUS_ACTIVE; 437 try { 438 $plan = api::update_plan($record); 439 $this->fail('Updating the status is not allowed.'); 440 } catch (\coding_exception $e) { 441 $this->assertMatchesRegularExpression('/To change the status of a plan use the appropriate methods./', 442 $e->getMessage()); 443 } 444 445 // Test when user with manage own plan capability try to edit other user plan. 446 $record->status = \core_competency\plan::STATUS_DRAFT; 447 $record->name = 'plan create draft modified 2'; 448 $this->setUser($usermanageown); 449 try { 450 $plan = api::update_plan($record); 451 $this->fail('User with manage own plan capability can only edit his own plan.'); 452 } catch (\required_capability_exception $e) { 453 $this->assertTrue(true); 454 } 455 456 // User with manage plan capability cannot edit the other user plans with status draft. 457 $this->setUser($usermanage); 458 $record->name = 'plan create draft modified 3'; 459 try { 460 $plan = api::update_plan($record); 461 $this->fail('User with manage plan capability cannot edit the other user plans with status draft'); 462 } catch (\required_capability_exception $e) { 463 $this->assertTrue(true); 464 } 465 466 // User with manage draft capability can edit other user's learning plan if the status is draft. 467 $this->setUser($usermanagedraft); 468 $record->status = \core_competency\plan::STATUS_DRAFT; 469 $record->name = 'plan manage draft modified 3'; 470 $plan = api::update_plan($record); 471 $this->assertInstanceOf('\core_competency\plan', $plan); 472 473 // User with manage plan capability can create/edit learning plan if status is active/complete. 474 $this->setUser($usermanage); 475 $plan = array ( 476 'name' => 'plan create', 477 'description' => 'plan create', 478 'userid' => $usermanage->id, 479 'status' => \core_competency\plan::STATUS_ACTIVE 480 ); 481 $plan = api::create_plan((object)$plan); 482 483 // Silently transition to complete status to avoid errors about transitioning to complete. 484 $plan->set('status', \core_competency\plan::STATUS_COMPLETE); 485 $plan->update(); 486 487 $record = $plan->to_record(); 488 $record->name = 'plan create own modified'; 489 try { 490 api::update_plan($record); 491 $this->fail('Completed plan can not be edited'); 492 } catch (\coding_exception $e) { 493 $this->assertTrue(true); 494 } 495 } 496 497 public function test_create_plan_from_template() { 498 $this->resetAfterTest(true); 499 $this->setAdminUser(); 500 501 $u1 = $this->getDataGenerator()->create_user(); 502 $tpl = $this->getDataGenerator()->get_plugin_generator('core_competency')->create_template(); 503 504 // Creating a new plan. 505 $plan = api::create_plan_from_template($tpl, $u1->id); 506 $record = $plan->to_record(); 507 $this->assertInstanceOf('\core_competency\plan', $plan); 508 $this->assertTrue(\core_competency\plan::record_exists($plan->get('id'))); 509 $this->assertEquals($tpl->get('id'), $plan->get('templateid')); 510 $this->assertEquals($u1->id, $plan->get('userid')); 511 $this->assertTrue($plan->is_based_on_template()); 512 513 // Creating a plan that already exists. 514 $plan = api::create_plan_from_template($tpl, $u1->id); 515 $this->assertFalse($plan); 516 517 // Check that api::create_plan cannot be used. 518 unset($record->id); 519 $this->expectException(\coding_exception::class); 520 $plan = api::create_plan($record); 521 } 522 523 public function test_update_plan_based_on_template() { 524 $this->resetAfterTest(true); 525 $dg = $this->getDataGenerator(); 526 $lpg = $dg->get_plugin_generator('core_competency'); 527 $u1 = $dg->create_user(); 528 $u2 = $dg->create_user(); 529 530 $this->setAdminUser(); 531 $tpl1 = $lpg->create_template(); 532 $tpl2 = $lpg->create_template(); 533 $up1 = $lpg->create_plan(array('userid' => $u1->id, 'templateid' => $tpl1->get('id'))); 534 $up2 = $lpg->create_plan(array('userid' => $u2->id, 'templateid' => null)); 535 536 try { 537 // Trying to remove the template dependency. 538 $record = $up1->to_record(); 539 $record->templateid = null; 540 api::update_plan($record); 541 $this->fail('A plan cannot be unlinked using api::update_plan()'); 542 } catch (\coding_exception $e) { 543 // All good. 544 } 545 546 try { 547 // Trying to switch to another template. 548 $record = $up1->to_record(); 549 $record->templateid = $tpl2->get('id'); 550 api::update_plan($record); 551 $this->fail('A plan cannot be moved to another template.'); 552 } catch (\coding_exception $e) { 553 // All good. 554 } 555 556 try { 557 // Trying to switch to using a template. 558 $record = $up2->to_record(); 559 $record->templateid = $tpl1->get('id'); 560 api::update_plan($record); 561 $this->fail('A plan cannot be update to use a template.'); 562 } catch (\coding_exception $e) { 563 // All good. 564 } 565 } 566 567 public function test_unlink_plan_from_template() { 568 $this->resetAfterTest(true); 569 $dg = $this->getDataGenerator(); 570 $lpg = $dg->get_plugin_generator('core_competency'); 571 $u1 = $dg->create_user(); 572 $u2 = $dg->create_user(); 573 574 $this->setAdminUser(); 575 $f1 = $lpg->create_framework(); 576 $f2 = $lpg->create_framework(); 577 $c1a = $lpg->create_competency(array('competencyframeworkid' => $f1->get('id'))); 578 $c2a = $lpg->create_competency(array('competencyframeworkid' => $f2->get('id'))); 579 $c1b = $lpg->create_competency(array('competencyframeworkid' => $f1->get('id'))); 580 581 $tpl1 = $lpg->create_template(); 582 $tpl2 = $lpg->create_template(); 583 584 $tplc1a = $lpg->create_template_competency(array('templateid' => $tpl1->get('id'), 'competencyid' => $c1a->get('id'), 585 'sortorder' => 9)); 586 $tplc1b = $lpg->create_template_competency(array('templateid' => $tpl1->get('id'), 'competencyid' => $c1b->get('id'), 587 'sortorder' => 8)); 588 $tplc2a = $lpg->create_template_competency(array('templateid' => $tpl2->get('id'), 'competencyid' => $c2a->get('id'))); 589 590 $plan1 = $lpg->create_plan(array('userid' => $u1->id, 'templateid' => $tpl1->get('id'), 'status' => plan::STATUS_ACTIVE)); 591 $plan2 = $lpg->create_plan(array('userid' => $u2->id, 'templateid' => $tpl2->get('id'))); 592 $plan3 = $lpg->create_plan(array('userid' => $u1->id, 'templateid' => $tpl1->get('id'), 'status' => plan::STATUS_COMPLETE)); 593 594 // Check that we have what we expect at this stage. 595 $this->assertEquals(2, \core_competency\template_competency::count_records(array('templateid' => $tpl1->get('id')))); 596 $this->assertEquals(1, \core_competency\template_competency::count_records(array('templateid' => $tpl2->get('id')))); 597 $this->assertEquals(0, \core_competency\plan_competency::count_records(array('planid' => $plan1->get('id')))); 598 $this->assertEquals(0, \core_competency\plan_competency::count_records(array('planid' => $plan2->get('id')))); 599 $this->assertTrue($plan1->is_based_on_template()); 600 $this->assertTrue($plan2->is_based_on_template()); 601 602 // Let's do this! 603 $tpl1comps = \core_competency\template_competency::list_competencies($tpl1->get('id'), true); 604 $tpl2comps = \core_competency\template_competency::list_competencies($tpl2->get('id'), true); 605 606 api::unlink_plan_from_template($plan1); 607 608 $plan1->read(); 609 $plan2->read(); 610 $this->assertCount(2, $tpl1comps); 611 $this->assertCount(1, $tpl2comps); 612 $this->assertEquals(2, \core_competency\template_competency::count_records(array('templateid' => $tpl1->get('id')))); 613 $this->assertEquals(1, \core_competency\template_competency::count_records(array('templateid' => $tpl2->get('id')))); 614 $this->assertEquals(2, \core_competency\plan_competency::count_records(array('planid' => $plan1->get('id')))); 615 $this->assertEquals(0, \core_competency\plan_competency::count_records(array('planid' => $plan2->get('id')))); 616 $this->assertFalse($plan1->is_based_on_template()); 617 $this->assertEquals($tpl1->get('id'), $plan1->get('origtemplateid')); 618 $this->assertTrue($plan2->is_based_on_template()); 619 $this->assertEquals(null, $plan2->get('origtemplateid')); 620 621 // Check we can unlink draft plan. 622 try { 623 api::unlink_plan_from_template($plan2); 624 } catch (\coding_exception $e) { 625 $this->fail('Fail to unlink draft plan.'); 626 } 627 628 // Check we can not unlink completed plan. 629 try { 630 api::unlink_plan_from_template($plan3); 631 $this->fail('We can not unlink completed plan.'); 632 } catch (\coding_exception $e) { 633 // All good. 634 } 635 636 // Even the order remains. 637 $plan1comps = \core_competency\plan_competency::list_competencies($plan1->get('id')); 638 $before = reset($tpl1comps); 639 $after = reset($plan1comps); 640 $this->assertEquals($before->get('id'), $after->get('id')); 641 $this->assertEquals($before->get('sortorder'), $after->get('sortorder')); 642 $before = next($tpl1comps); 643 $after = next($plan1comps); 644 $this->assertEquals($before->get('id'), $after->get('id')); 645 $this->assertEquals($before->get('sortorder'), $after->get('sortorder')); 646 } 647 648 public function test_update_template_updates_plans() { 649 $this->resetAfterTest(true); 650 $this->setAdminUser(); 651 652 $dg = $this->getDataGenerator(); 653 $u1 = $dg->create_user(); 654 $u2 = $dg->create_user(); 655 $lpg = $dg->get_plugin_generator('core_competency'); 656 $tpl1 = $lpg->create_template(); 657 $tpl2 = $lpg->create_template(); 658 659 // Create plans with data not matching templates. 660 $time = time(); 661 $plan1 = $lpg->create_plan(array('templateid' => $tpl1->get('id'), 'userid' => $u1->id, 662 'name' => 'Not good name', 'duedate' => $time + 3600, 'description' => 'Ahah', 'descriptionformat' => FORMAT_MARKDOWN)); 663 $plan2 = $lpg->create_plan(array('templateid' => $tpl1->get('id'), 'userid' => $u2->id, 664 'name' => 'Not right name', 'duedate' => $time + 3601, 'description' => 'Ahah', 'descriptionformat' => FORMAT_PLAIN)); 665 $plan3 = $lpg->create_plan(array('templateid' => $tpl2->get('id'), 'userid' => $u1->id, 666 'name' => 'Not sweet name', 'duedate' => $time + 3602, 'description' => 'Ahah', 'descriptionformat' => FORMAT_PLAIN)); 667 668 // Prepare our expectations. 669 $plan1->read(); 670 $plan2->read(); 671 $plan3->read(); 672 673 $this->assertEquals($tpl1->get('id'), $plan1->get('templateid')); 674 $this->assertEquals($tpl1->get('id'), $plan2->get('templateid')); 675 $this->assertEquals($tpl2->get('id'), $plan3->get('templateid')); 676 $this->assertNotEquals($tpl1->get('shortname'), $plan1->get('name')); 677 $this->assertNotEquals($tpl1->get('shortname'), $plan2->get('name')); 678 $this->assertNotEquals($tpl2->get('shortname'), $plan3->get('name')); 679 $this->assertNotEquals($tpl1->get('description'), $plan1->get('description')); 680 $this->assertNotEquals($tpl1->get('description'), $plan2->get('description')); 681 $this->assertNotEquals($tpl2->get('description'), $plan3->get('description')); 682 $this->assertNotEquals($tpl1->get('descriptionformat'), $plan1->get('descriptionformat')); 683 $this->assertNotEquals($tpl1->get('descriptionformat'), $plan2->get('descriptionformat')); 684 $this->assertNotEquals($tpl2->get('descriptionformat'), $plan3->get('descriptionformat')); 685 $this->assertNotEquals($tpl1->get('duedate'), $plan1->get('duedate')); 686 $this->assertNotEquals($tpl1->get('duedate'), $plan2->get('duedate')); 687 $this->assertNotEquals($tpl2->get('duedate'), $plan3->get('duedate')); 688 689 // Update the template without changing critical fields does not update the plans. 690 $data = $tpl1->to_record(); 691 $data->visible = 0; 692 api::update_template($data); 693 $this->assertNotEquals($tpl1->get('shortname'), $plan1->get('name')); 694 $this->assertNotEquals($tpl1->get('shortname'), $plan2->get('name')); 695 $this->assertNotEquals($tpl2->get('shortname'), $plan3->get('name')); 696 $this->assertNotEquals($tpl1->get('description'), $plan1->get('description')); 697 $this->assertNotEquals($tpl1->get('description'), $plan2->get('description')); 698 $this->assertNotEquals($tpl2->get('description'), $plan3->get('description')); 699 $this->assertNotEquals($tpl1->get('descriptionformat'), $plan1->get('descriptionformat')); 700 $this->assertNotEquals($tpl1->get('descriptionformat'), $plan2->get('descriptionformat')); 701 $this->assertNotEquals($tpl2->get('descriptionformat'), $plan3->get('descriptionformat')); 702 $this->assertNotEquals($tpl1->get('duedate'), $plan1->get('duedate')); 703 $this->assertNotEquals($tpl1->get('duedate'), $plan2->get('duedate')); 704 $this->assertNotEquals($tpl2->get('duedate'), $plan3->get('duedate')); 705 706 // Now really update the template. 707 $data = $tpl1->to_record(); 708 $data->shortname = 'Awesome!'; 709 $data->description = 'This is too awesome!'; 710 $data->descriptionformat = FORMAT_HTML; 711 $data->duedate = $time + 200; 712 api::update_template($data); 713 $tpl1->read(); 714 715 // Now confirm that the right plans were updated. 716 $plan1->read(); 717 $plan2->read(); 718 $plan3->read(); 719 720 $this->assertEquals($tpl1->get('id'), $plan1->get('templateid')); 721 $this->assertEquals($tpl1->get('id'), $plan2->get('templateid')); 722 $this->assertEquals($tpl2->get('id'), $plan3->get('templateid')); 723 724 $this->assertEquals($tpl1->get('shortname'), $plan1->get('name')); 725 $this->assertEquals($tpl1->get('shortname'), $plan2->get('name')); 726 $this->assertNotEquals($tpl2->get('shortname'), $plan3->get('name')); 727 $this->assertEquals($tpl1->get('description'), $plan1->get('description')); 728 $this->assertEquals($tpl1->get('description'), $plan2->get('description')); 729 $this->assertNotEquals($tpl2->get('description'), $plan3->get('description')); 730 $this->assertEquals($tpl1->get('descriptionformat'), $plan1->get('descriptionformat')); 731 $this->assertEquals($tpl1->get('descriptionformat'), $plan2->get('descriptionformat')); 732 $this->assertNotEquals($tpl2->get('descriptionformat'), $plan3->get('descriptionformat')); 733 $this->assertEquals($tpl1->get('duedate'), $plan1->get('duedate')); 734 $this->assertEquals($tpl1->get('duedate'), $plan2->get('duedate')); 735 $this->assertNotEquals($tpl2->get('duedate'), $plan3->get('duedate')); 736 } 737 738 /** 739 * Test that the method to complete a plan. 740 */ 741 public function test_complete_plan() { 742 global $DB; 743 744 $this->resetAfterTest(true); 745 $this->setAdminUser(); 746 $dg = $this->getDataGenerator(); 747 $lpg = $this->getDataGenerator()->get_plugin_generator('core_competency'); 748 $user = $dg->create_user(); 749 750 // Create a framework and assign competencies. 751 $framework = $lpg->create_framework(); 752 $c1 = $lpg->create_competency(array('competencyframeworkid' => $framework->get('id'))); 753 $c2 = $lpg->create_competency(array('competencyframeworkid' => $framework->get('id'))); 754 $c3 = $lpg->create_competency(array('competencyframeworkid' => $framework->get('id'))); 755 $c4 = $lpg->create_competency(array('competencyframeworkid' => $framework->get('id'))); 756 757 // Create two plans and assign competencies. 758 $plan = $lpg->create_plan(array('userid' => $user->id)); 759 $otherplan = $lpg->create_plan(array('userid' => $user->id)); 760 761 $lpg->create_plan_competency(array('planid' => $plan->get('id'), 'competencyid' => $c1->get('id'))); 762 $lpg->create_plan_competency(array('planid' => $plan->get('id'), 'competencyid' => $c2->get('id'))); 763 $lpg->create_plan_competency(array('planid' => $plan->get('id'), 'competencyid' => $c3->get('id'))); 764 $lpg->create_plan_competency(array('planid' => $otherplan->get('id'), 'competencyid' => $c1->get('id'))); 765 766 $uclist = array( 767 $lpg->create_user_competency(array('userid' => $user->id, 'competencyid' => $c1->get('id'), 768 'proficiency' => true, 'grade' => 1 )), 769 $lpg->create_user_competency(array('userid' => $user->id, 'competencyid' => $c2->get('id'), 770 'proficiency' => false, 'grade' => 2 )) 771 ); 772 773 $this->assertEquals(2, \core_competency\user_competency::count_records()); 774 $this->assertEquals(0, \core_competency\user_competency_plan::count_records()); 775 776 // Change status of the plan to complete. 777 api::complete_plan($plan); 778 779 // Check that user competencies are now in user_competency_plan objects and still in user_competency. 780 $this->assertEquals(2, \core_competency\user_competency::count_records()); 781 $this->assertEquals(3, \core_competency\user_competency_plan::count_records()); 782 783 $usercompetenciesplan = \core_competency\user_competency_plan::get_records(); 784 785 $this->assertEquals($uclist[0]->get('userid'), $usercompetenciesplan[0]->get('userid')); 786 $this->assertEquals($uclist[0]->get('competencyid'), $usercompetenciesplan[0]->get('competencyid')); 787 $this->assertEquals($uclist[0]->get('proficiency'), (bool) $usercompetenciesplan[0]->get('proficiency')); 788 $this->assertEquals($uclist[0]->get('grade'), $usercompetenciesplan[0]->get('grade')); 789 $this->assertEquals($plan->get('id'), $usercompetenciesplan[0]->get('planid')); 790 791 $this->assertEquals($uclist[1]->get('userid'), $usercompetenciesplan[1]->get('userid')); 792 $this->assertEquals($uclist[1]->get('competencyid'), $usercompetenciesplan[1]->get('competencyid')); 793 $this->assertEquals($uclist[1]->get('proficiency'), (bool) $usercompetenciesplan[1]->get('proficiency')); 794 $this->assertEquals($uclist[1]->get('grade'), $usercompetenciesplan[1]->get('grade')); 795 $this->assertEquals($plan->get('id'), $usercompetenciesplan[1]->get('planid')); 796 797 $this->assertEquals($user->id, $usercompetenciesplan[2]->get('userid')); 798 $this->assertEquals($c3->get('id'), $usercompetenciesplan[2]->get('competencyid')); 799 $this->assertNull($usercompetenciesplan[2]->get('proficiency')); 800 $this->assertNull($usercompetenciesplan[2]->get('grade')); 801 $this->assertEquals($plan->get('id'), $usercompetenciesplan[2]->get('planid')); 802 803 // Check we can not add competency to completed plan. 804 try { 805 api::add_competency_to_plan($plan->get('id'), $c4->get('id')); 806 $this->fail('We can not add competency to completed plan.'); 807 } catch (\coding_exception $e) { 808 // All good. 809 } 810 811 // Check we can not remove competency to completed plan. 812 try { 813 api::remove_competency_from_plan($plan->get('id'), $c3->get('id')); 814 $this->fail('We can not remove competency to completed plan.'); 815 } catch (\coding_exception $e) { 816 // All good. 817 } 818 819 // Completing a plan that is completed throws an exception. 820 $this->expectException(\coding_exception::class); 821 api::complete_plan($plan); 822 } 823 824 /** 825 * Set-up the workflow data (review, active, ...). 826 * 827 * @return array 828 */ 829 protected function setup_workflow_data() { 830 $this->resetAfterTest(); 831 832 $dg = $this->getDataGenerator(); 833 $user = $dg->create_user(); 834 $reviewer = $dg->create_user(); 835 $otheruser = $dg->create_user(); 836 837 $syscontext = \context_system::instance(); 838 $userrole = $dg->create_role(); 839 $reviewerrole = $dg->create_role(); 840 $otheruserrole = $dg->create_role(); 841 842 assign_capability('moodle/competency:planmanageowndraft', CAP_ALLOW, $userrole, $syscontext->id); 843 assign_capability('moodle/competency:planmanage', CAP_ALLOW, $reviewerrole, $syscontext->id); 844 assign_capability('moodle/competency:planviewdraft', CAP_ALLOW, $reviewerrole, $syscontext->id); 845 $dg->role_assign($userrole, $user->id, $syscontext->id); 846 $dg->role_assign($reviewerrole, $reviewer->id, $syscontext->id); 847 accesslib_clear_all_caches_for_unit_testing(); 848 849 $lpg = $dg->get_plugin_generator('core_competency'); 850 $tpl = $lpg->create_template(); 851 $plan = $lpg->create_plan(array('userid' => $user->id)); 852 $tplplan = $lpg->create_plan(array('userid' => $user->id, 'templateid' => $tpl->get('id'))); 853 854 return array( 855 'dg' => $dg, 856 'lpg' => $lpg, 857 'user' => $user, 858 'reviewer' => $reviewer, 859 'otheruser' => $otheruser, 860 'plan' => $plan, 861 'tplplan' => $tplplan, 862 ); 863 } 864 865 /** 866 * Testing requesting the review of a plan. 867 */ 868 public function test_plan_request_review() { 869 $data = $this->setup_workflow_data(); 870 $dg = $data['dg']; 871 $lpg = $data['lpg']; 872 $user = $data['user']; 873 $reviewer = $data['reviewer']; 874 $otheruser = $data['otheruser']; 875 $plan = $data['plan']; 876 $tplplan = $data['tplplan']; 877 878 $this->assertEquals(plan::STATUS_DRAFT, $plan->get('status')); 879 $this->assertEquals(plan::STATUS_DRAFT, $tplplan->get('status')); 880 881 // Foreign user cannot do anything. 882 $this->setUser($otheruser); 883 try { 884 api::plan_request_review($plan); 885 $this->fail('The user can not read the plan.'); 886 } catch (\required_capability_exception $e) { 887 $this->assertEquals('nopermissions', $e->errorcode); 888 } 889 890 // Can not change a plan based on a template. 891 $this->setUser($user); 892 try { 893 api::plan_request_review($tplplan); 894 $this->fail('The plan is based on a template.'); 895 } catch (\coding_exception $e) { 896 $this->assertMatchesRegularExpression('/Template plans cannot be reviewed./', $e->getMessage()); 897 } 898 899 // Can not send for review when not draft. 900 $this->setUser($user); 901 $plan->set('status', plan::STATUS_WAITING_FOR_REVIEW); 902 try { 903 api::plan_request_review($plan); 904 $this->fail('The plan cannot be sent for review at this stage.'); 905 } catch (\coding_exception $e) { 906 $this->assertMatchesRegularExpression('/The plan cannot be sent for review at this stage./', $e->getMessage()); 907 } 908 909 // Can not send for review when not draft. 910 $this->setUser($user); 911 $plan->set('status', plan::STATUS_ACTIVE); 912 try { 913 api::plan_request_review($plan); 914 $this->fail('The plan cannot be sent for review at this stage.'); 915 } catch (\coding_exception $e) { 916 $this->assertMatchesRegularExpression('/The plan cannot be sent for review at this stage./', $e->getMessage()); 917 } 918 919 // Can not send for review when not draft. 920 $this->setUser($user); 921 $plan->set('status', plan::STATUS_IN_REVIEW); 922 try { 923 api::plan_request_review($plan); 924 $this->fail('The plan cannot be sent for review at this stage.'); 925 } catch (\coding_exception $e) { 926 $this->assertMatchesRegularExpression('/The plan cannot be sent for review at this stage./', $e->getMessage()); 927 } 928 929 // Can not send for review when not draft. 930 $this->setUser($user); 931 $plan->set('status', plan::STATUS_COMPLETE); 932 try { 933 api::plan_request_review($plan); 934 $this->fail('The plan cannot be sent for review at this stage.'); 935 } catch (\coding_exception $e) { 936 $this->assertMatchesRegularExpression('/The plan cannot be sent for review at this stage./', $e->getMessage()); 937 } 938 939 // Sending for review as a reviewer. 940 $this->setUser($reviewer); 941 $plan->set('status', plan::STATUS_DRAFT); 942 try { 943 api::plan_request_review($plan); 944 $this->fail('The user can not request a review.'); 945 } catch (\required_capability_exception $e) { 946 $this->assertEquals('nopermissions', $e->errorcode); 947 } 948 949 // Sending for review. 950 $this->setUser($user); 951 api::plan_request_review($plan); 952 $plan->read(); 953 $this->assertEquals(plan::STATUS_WAITING_FOR_REVIEW, $plan->get('status')); 954 955 // Sending for review by ID. 956 $plan->set('status', plan::STATUS_DRAFT); 957 $plan->update(); 958 api::plan_request_review($plan->get('id')); 959 $plan->read(); 960 $this->assertEquals(plan::STATUS_WAITING_FOR_REVIEW, $plan->get('status')); 961 } 962 963 /** 964 * Testing cancelling the review request. 965 */ 966 public function test_plan_cancel_review_request() { 967 $data = $this->setup_workflow_data(); 968 $dg = $data['dg']; 969 $lpg = $data['lpg']; 970 $user = $data['user']; 971 $reviewer = $data['reviewer']; 972 $otheruser = $data['otheruser']; 973 $plan = $data['plan']; 974 $tplplan = $data['tplplan']; 975 976 // Set waiting for review. 977 $tplplan->set('status', plan::STATUS_WAITING_FOR_REVIEW); 978 $tplplan->update(); 979 $plan->set('status', plan::STATUS_WAITING_FOR_REVIEW); 980 $plan->update(); 981 982 // Foreign user cannot do anything. 983 $this->setUser($otheruser); 984 try { 985 api::plan_cancel_review_request($plan); 986 $this->fail('The user can not read the plan.'); 987 } catch (\required_capability_exception $e) { 988 $this->assertEquals('nopermissions', $e->errorcode); 989 } 990 991 // Can not change a plan based on a template. 992 $this->setUser($user); 993 try { 994 api::plan_cancel_review_request($tplplan); 995 $this->fail('The plan is based on a template.'); 996 } catch (\coding_exception $e) { 997 $this->assertMatchesRegularExpression('/Template plans cannot be reviewed./', $e->getMessage()); 998 } 999 1000 // Can not cancel review request when not waiting for review. 1001 $this->setUser($user); 1002 $plan->set('status', plan::STATUS_DRAFT); 1003 try { 1004 api::plan_cancel_review_request($plan); 1005 $this->fail('The plan cannot be sent for review at this stage.'); 1006 } catch (\coding_exception $e) { 1007 $this->assertMatchesRegularExpression('/The plan review cannot be cancelled at this stage./', $e->getMessage()); 1008 } 1009 1010 // Can not cancel review request when not waiting for review. 1011 $this->setUser($user); 1012 $plan->set('status', plan::STATUS_IN_REVIEW); 1013 try { 1014 api::plan_cancel_review_request($plan); 1015 $this->fail('The plan review cannot be cancelled at this stage.'); 1016 } catch (\coding_exception $e) { 1017 $this->assertMatchesRegularExpression('/The plan review cannot be cancelled at this stage./', $e->getMessage()); 1018 } 1019 1020 // Can not cancel review request when not waiting for review. 1021 $this->setUser($user); 1022 $plan->set('status', plan::STATUS_ACTIVE); 1023 try { 1024 api::plan_cancel_review_request($plan); 1025 $this->fail('The plan review cannot be cancelled at this stage.'); 1026 } catch (\coding_exception $e) { 1027 $this->assertMatchesRegularExpression('/The plan review cannot be cancelled at this stage./', $e->getMessage()); 1028 } 1029 1030 // Can not cancel review request when not waiting for review. 1031 $this->setUser($user); 1032 $plan->set('status', plan::STATUS_COMPLETE); 1033 try { 1034 api::plan_cancel_review_request($plan); 1035 $this->fail('The plan review cannot be cancelled at this stage.'); 1036 } catch (\coding_exception $e) { 1037 $this->assertMatchesRegularExpression('/The plan review cannot be cancelled at this stage./', $e->getMessage()); 1038 } 1039 1040 // Cancelling as a reviewer. 1041 $this->setUser($reviewer); 1042 $plan->set('status', plan::STATUS_WAITING_FOR_REVIEW); 1043 try { 1044 api::plan_cancel_review_request($plan); 1045 $this->fail('The user can not cancel a review request.'); 1046 } catch (\required_capability_exception $e) { 1047 $this->assertEquals('nopermissions', $e->errorcode); 1048 } 1049 1050 // Cancelling review request. 1051 $this->setUser($user); 1052 api::plan_cancel_review_request($plan); 1053 $plan->read(); 1054 $this->assertEquals(plan::STATUS_DRAFT, $plan->get('status')); 1055 1056 // Cancelling review request by ID. 1057 $plan->set('status', plan::STATUS_WAITING_FOR_REVIEW); 1058 $plan->update(); 1059 api::plan_cancel_review_request($plan->get('id')); 1060 $plan->read(); 1061 $this->assertEquals(plan::STATUS_DRAFT, $plan->get('status')); 1062 } 1063 1064 /** 1065 * Testing starting the review. 1066 */ 1067 public function test_plan_start_review() { 1068 $data = $this->setup_workflow_data(); 1069 $dg = $data['dg']; 1070 $lpg = $data['lpg']; 1071 $user = $data['user']; 1072 $reviewer = $data['reviewer']; 1073 $otheruser = $data['otheruser']; 1074 $plan = $data['plan']; 1075 $tplplan = $data['tplplan']; 1076 1077 // Set waiting for review. 1078 $tplplan->set('status', plan::STATUS_WAITING_FOR_REVIEW); 1079 $tplplan->update(); 1080 $plan->set('status', plan::STATUS_WAITING_FOR_REVIEW); 1081 $plan->update(); 1082 1083 // Foreign user cannot do anything. 1084 $this->setUser($otheruser); 1085 try { 1086 api::plan_start_review($plan); 1087 $this->fail('The user can not read the plan.'); 1088 } catch (\required_capability_exception $e) { 1089 $this->assertEquals('nopermissions', $e->errorcode); 1090 } 1091 1092 // Can not change a plan based on a template. 1093 $this->setUser($reviewer); 1094 try { 1095 api::plan_start_review($tplplan); 1096 $this->fail('The plan is based on a template.'); 1097 } catch (\coding_exception $e) { 1098 $this->assertMatchesRegularExpression('/Template plans cannot be reviewed./', $e->getMessage()); 1099 } 1100 1101 // Can not start a review when not waiting for review. 1102 $this->setUser($reviewer); 1103 $plan->set('status', plan::STATUS_DRAFT); 1104 try { 1105 api::plan_start_review($plan); 1106 $this->fail('The plan review cannot be started at this stage.'); 1107 } catch (\coding_exception $e) { 1108 $this->assertMatchesRegularExpression('/The plan review cannot be started at this stage./', $e->getMessage()); 1109 } 1110 1111 // Can not start a review when not waiting for review. 1112 $this->setUser($reviewer); 1113 $plan->set('status', plan::STATUS_IN_REVIEW); 1114 try { 1115 api::plan_start_review($plan); 1116 $this->fail('The plan review cannot be started at this stage.'); 1117 } catch (\coding_exception $e) { 1118 $this->assertMatchesRegularExpression('/The plan review cannot be started at this stage./', $e->getMessage()); 1119 } 1120 1121 // Can not start a review when not waiting for review. 1122 $this->setUser($reviewer); 1123 $plan->set('status', plan::STATUS_ACTIVE); 1124 try { 1125 api::plan_start_review($plan); 1126 $this->fail('The plan review cannot be started at this stage.'); 1127 } catch (\coding_exception $e) { 1128 $this->assertMatchesRegularExpression('/The plan review cannot be started at this stage./', $e->getMessage()); 1129 } 1130 1131 // Can not start a review when not waiting for review. 1132 $this->setUser($reviewer); 1133 $plan->set('status', plan::STATUS_COMPLETE); 1134 try { 1135 api::plan_start_review($plan); 1136 $this->fail('The plan review cannot be started at this stage.'); 1137 } catch (\coding_exception $e) { 1138 $this->assertMatchesRegularExpression('/The plan review cannot be started at this stage./', $e->getMessage()); 1139 } 1140 1141 // Starting as the owner. 1142 $this->setUser($user); 1143 $plan->set('status', plan::STATUS_WAITING_FOR_REVIEW); 1144 try { 1145 api::plan_start_review($plan); 1146 $this->fail('The user can not start a review.'); 1147 } catch (\required_capability_exception $e) { 1148 $this->assertEquals('nopermissions', $e->errorcode); 1149 } 1150 1151 // Starting review. 1152 $this->setUser($reviewer); 1153 api::plan_start_review($plan); 1154 $plan->read(); 1155 $this->assertEquals(plan::STATUS_IN_REVIEW, $plan->get('status')); 1156 $this->assertEquals($reviewer->id, $plan->get('reviewerid')); 1157 1158 // Starting review by ID. 1159 $plan->set('status', plan::STATUS_WAITING_FOR_REVIEW); 1160 $plan->set('reviewerid', null); 1161 $plan->update(); 1162 api::plan_start_review($plan->get('id')); 1163 $plan->read(); 1164 $this->assertEquals(plan::STATUS_IN_REVIEW, $plan->get('status')); 1165 $this->assertEquals($reviewer->id, $plan->get('reviewerid')); 1166 } 1167 1168 /** 1169 * Testing stopping the review. 1170 */ 1171 public function test_plan_stop_review() { 1172 $data = $this->setup_workflow_data(); 1173 $dg = $data['dg']; 1174 $lpg = $data['lpg']; 1175 $user = $data['user']; 1176 $reviewer = $data['reviewer']; 1177 $otheruser = $data['otheruser']; 1178 $plan = $data['plan']; 1179 $tplplan = $data['tplplan']; 1180 1181 // Set waiting for review. 1182 $tplplan->set('status', plan::STATUS_IN_REVIEW); 1183 $tplplan->update(); 1184 $plan->set('status', plan::STATUS_IN_REVIEW); 1185 $plan->update(); 1186 1187 // Foreign user cannot do anything. 1188 $this->setUser($otheruser); 1189 try { 1190 api::plan_stop_review($plan); 1191 $this->fail('The user can not read the plan.'); 1192 } catch (\required_capability_exception $e) { 1193 $this->assertEquals('nopermissions', $e->errorcode); 1194 } 1195 1196 // Can not change a plan based on a template. 1197 $this->setUser($reviewer); 1198 try { 1199 api::plan_stop_review($tplplan); 1200 $this->fail('The plan is based on a template.'); 1201 } catch (\coding_exception $e) { 1202 $this->assertMatchesRegularExpression('/Template plans cannot be reviewed./', $e->getMessage()); 1203 } 1204 1205 // Can not stop a review whe not in review. 1206 $this->setUser($reviewer); 1207 $plan->set('status', plan::STATUS_DRAFT); 1208 try { 1209 api::plan_stop_review($plan); 1210 $this->fail('The plan review cannot be stopped at this stage.'); 1211 } catch (\coding_exception $e) { 1212 $this->assertMatchesRegularExpression('/The plan review cannot be stopped at this stage./', $e->getMessage()); 1213 } 1214 1215 // Can not stop a review whe not in review. 1216 $this->setUser($reviewer); 1217 $plan->set('status', plan::STATUS_WAITING_FOR_REVIEW); 1218 try { 1219 api::plan_stop_review($plan); 1220 $this->fail('The plan review cannot be stopped at this stage.'); 1221 } catch (\coding_exception $e) { 1222 $this->assertMatchesRegularExpression('/The plan review cannot be stopped at this stage./', $e->getMessage()); 1223 } 1224 1225 // Can not stop a review whe not in review. 1226 $this->setUser($reviewer); 1227 $plan->set('status', plan::STATUS_ACTIVE); 1228 try { 1229 api::plan_stop_review($plan); 1230 $this->fail('The plan review cannot be stopped at this stage.'); 1231 } catch (\coding_exception $e) { 1232 $this->assertMatchesRegularExpression('/The plan review cannot be stopped at this stage./', $e->getMessage()); 1233 } 1234 1235 // Can not stop a review whe not in review. 1236 $this->setUser($reviewer); 1237 $plan->set('status', plan::STATUS_COMPLETE); 1238 try { 1239 api::plan_stop_review($plan); 1240 $this->fail('The plan review cannot be stopped at this stage.'); 1241 } catch (\coding_exception $e) { 1242 $this->assertMatchesRegularExpression('/The plan review cannot be stopped at this stage./', $e->getMessage()); 1243 } 1244 1245 // Stopping as the owner. 1246 $this->setUser($user); 1247 $plan->set('status', plan::STATUS_IN_REVIEW); 1248 try { 1249 api::plan_stop_review($plan); 1250 $this->fail('The user can not stop a review.'); 1251 } catch (\required_capability_exception $e) { 1252 $this->assertEquals('nopermissions', $e->errorcode); 1253 } 1254 1255 // Stopping review. 1256 $this->setUser($reviewer); 1257 api::plan_stop_review($plan); 1258 $plan->read(); 1259 $this->assertEquals(plan::STATUS_DRAFT, $plan->get('status')); 1260 1261 // Stopping review by ID. 1262 $plan->set('status', plan::STATUS_IN_REVIEW); 1263 $plan->update(); 1264 api::plan_stop_review($plan->get('id')); 1265 $plan->read(); 1266 $this->assertEquals(plan::STATUS_DRAFT, $plan->get('status')); 1267 } 1268 1269 /** 1270 * Testing approving the plan. 1271 */ 1272 public function test_approve_plan() { 1273 $data = $this->setup_workflow_data(); 1274 $dg = $data['dg']; 1275 $lpg = $data['lpg']; 1276 $user = $data['user']; 1277 $reviewer = $data['reviewer']; 1278 $otheruser = $data['otheruser']; 1279 $plan = $data['plan']; 1280 $tplplan = $data['tplplan']; 1281 1282 // Set waiting for review. 1283 $tplplan->set('status', plan::STATUS_IN_REVIEW); 1284 $tplplan->update(); 1285 $plan->set('status', plan::STATUS_IN_REVIEW); 1286 $plan->update(); 1287 1288 // Foreign user cannot do anything. 1289 $this->setUser($otheruser); 1290 try { 1291 api::approve_plan($plan); 1292 $this->fail('The user can not read the plan.'); 1293 } catch (\required_capability_exception $e) { 1294 $this->assertEquals('nopermissions', $e->errorcode); 1295 } 1296 1297 // Can not change a plan based on a template. 1298 $this->setUser($reviewer); 1299 try { 1300 api::approve_plan($tplplan); 1301 $this->fail('The plan is based on a template.'); 1302 } catch (\coding_exception $e) { 1303 $this->assertMatchesRegularExpression('/Template plans are already approved./', $e->getMessage()); 1304 } 1305 1306 // Can not approve a plan already approved. 1307 $this->setUser($reviewer); 1308 $plan->set('status', plan::STATUS_ACTIVE); 1309 try { 1310 api::approve_plan($plan); 1311 $this->fail('The plan cannot be approved at this stage.'); 1312 } catch (\coding_exception $e) { 1313 $this->assertMatchesRegularExpression('/The plan cannot be approved at this stage./', $e->getMessage()); 1314 } 1315 1316 // Can not approve a plan already approved. 1317 $this->setUser($reviewer); 1318 $plan->set('status', plan::STATUS_COMPLETE); 1319 try { 1320 api::approve_plan($plan); 1321 $this->fail('The plan cannot be approved at this stage.'); 1322 } catch (\coding_exception $e) { 1323 $this->assertMatchesRegularExpression('/The plan cannot be approved at this stage./', $e->getMessage()); 1324 } 1325 1326 // Approve as the owner. 1327 $this->setUser($user); 1328 $plan->set('status', plan::STATUS_IN_REVIEW); 1329 try { 1330 api::approve_plan($plan); 1331 $this->fail('The user can not approve the plan.'); 1332 } catch (\required_capability_exception $e) { 1333 $this->assertEquals('nopermissions', $e->errorcode); 1334 } 1335 1336 // Approve plan from in review. 1337 $this->setUser($reviewer); 1338 api::approve_plan($plan); 1339 $plan->read(); 1340 $this->assertEquals(plan::STATUS_ACTIVE, $plan->get('status')); 1341 1342 // Approve plan by ID. 1343 $plan->set('status', plan::STATUS_IN_REVIEW); 1344 $plan->update(); 1345 api::approve_plan($plan->get('id')); 1346 $plan->read(); 1347 $this->assertEquals(plan::STATUS_ACTIVE, $plan->get('status')); 1348 1349 // Approve plan from draft. 1350 $plan->set('status', plan::STATUS_DRAFT); 1351 $plan->update(); 1352 api::approve_plan($plan); 1353 $plan->read(); 1354 $this->assertEquals(plan::STATUS_ACTIVE, $plan->get('status')); 1355 1356 // Approve plan from waiting for review. 1357 $plan->set('status', plan::STATUS_WAITING_FOR_REVIEW); 1358 $plan->update(); 1359 api::approve_plan($plan); 1360 $plan->read(); 1361 $this->assertEquals(plan::STATUS_ACTIVE, $plan->get('status')); 1362 } 1363 1364 /** 1365 * Testing stopping the review. 1366 */ 1367 public function test_unapprove_plan() { 1368 $data = $this->setup_workflow_data(); 1369 $dg = $data['dg']; 1370 $lpg = $data['lpg']; 1371 $user = $data['user']; 1372 $reviewer = $data['reviewer']; 1373 $otheruser = $data['otheruser']; 1374 $plan = $data['plan']; 1375 $tplplan = $data['tplplan']; 1376 1377 // Set waiting for review. 1378 $tplplan->set('status', plan::STATUS_ACTIVE); 1379 $tplplan->update(); 1380 $plan->set('status', plan::STATUS_ACTIVE); 1381 $plan->update(); 1382 1383 // Foreign user cannot do anything. 1384 $this->setUser($otheruser); 1385 try { 1386 api::unapprove_plan($plan); 1387 $this->fail('The user can not read the plan.'); 1388 } catch (\required_capability_exception $e) { 1389 $this->assertEquals('nopermissions', $e->errorcode); 1390 } 1391 1392 // Can not change a plan based on a template. 1393 $this->setUser($reviewer); 1394 try { 1395 api::unapprove_plan($tplplan); 1396 $this->fail('The plan is based on a template.'); 1397 } catch (\coding_exception $e) { 1398 $this->assertMatchesRegularExpression('/Template plans are always approved./', $e->getMessage()); 1399 } 1400 1401 // Can not unapprove a non-draft plan. 1402 $this->setUser($reviewer); 1403 $plan->set('status', plan::STATUS_DRAFT); 1404 try { 1405 api::unapprove_plan($plan); 1406 $this->fail('The plan cannot be sent back to draft at this stage.'); 1407 } catch (\coding_exception $e) { 1408 $this->assertMatchesRegularExpression('/The plan cannot be sent back to draft at this stage./', $e->getMessage()); 1409 } 1410 1411 // Can not unapprove a non-draft plan. 1412 $this->setUser($reviewer); 1413 $plan->set('status', plan::STATUS_WAITING_FOR_REVIEW); 1414 try { 1415 api::unapprove_plan($plan); 1416 $this->fail('The plan cannot be sent back to draft at this stage.'); 1417 } catch (\coding_exception $e) { 1418 $this->assertMatchesRegularExpression('/The plan cannot be sent back to draft at this stage./', $e->getMessage()); 1419 } 1420 1421 // Can not unapprove a non-draft plan. 1422 $this->setUser($reviewer); 1423 $plan->set('status', plan::STATUS_IN_REVIEW); 1424 try { 1425 api::unapprove_plan($plan); 1426 $this->fail('The plan cannot be sent back to draft at this stage.'); 1427 } catch (\coding_exception $e) { 1428 $this->assertMatchesRegularExpression('/The plan cannot be sent back to draft at this stage./', $e->getMessage()); 1429 } 1430 1431 // Can not unapprove a non-draft plan. 1432 $this->setUser($reviewer); 1433 $plan->set('status', plan::STATUS_COMPLETE); 1434 try { 1435 api::unapprove_plan($plan); 1436 $this->fail('The plan cannot be sent back to draft at this stage.'); 1437 } catch (\coding_exception $e) { 1438 $this->assertMatchesRegularExpression('/The plan cannot be sent back to draft at this stage./', $e->getMessage()); 1439 } 1440 1441 // Unapprove as the owner. 1442 $this->setUser($user); 1443 $plan->set('status', plan::STATUS_ACTIVE); 1444 try { 1445 api::unapprove_plan($plan); 1446 $this->fail('The user can not unapprove the plan.'); 1447 } catch (\required_capability_exception $e) { 1448 $this->assertEquals('nopermissions', $e->errorcode); 1449 } 1450 1451 // Unapprove plan. 1452 $this->setUser($reviewer); 1453 api::unapprove_plan($plan); 1454 $plan->read(); 1455 $this->assertEquals(plan::STATUS_DRAFT, $plan->get('status')); 1456 1457 // Unapprove plan by ID. 1458 $plan->set('status', plan::STATUS_ACTIVE); 1459 $plan->update(); 1460 api::unapprove_plan($plan->get('id')); 1461 $plan->read(); 1462 $this->assertEquals(plan::STATUS_DRAFT, $plan->get('status')); 1463 } 1464 1465 /** 1466 * Test update plan and the managing of archived user competencies. 1467 */ 1468 public function test_update_plan_manage_archived_competencies() { 1469 global $DB; 1470 1471 $this->resetAfterTest(true); 1472 $dg = $this->getDataGenerator(); 1473 $lpg = $this->getDataGenerator()->get_plugin_generator('core_competency'); 1474 1475 $syscontext = \context_system::instance(); 1476 1477 // Create users and roles for the test. 1478 $user = $dg->create_user(); 1479 $manageownrole = $dg->create_role(array( 1480 'name' => 'User manage own', 1481 'shortname' => 'manageown' 1482 )); 1483 assign_capability('moodle/competency:planmanageowndraft', CAP_ALLOW, $manageownrole, $syscontext->id); 1484 assign_capability('moodle/competency:planviewowndraft', CAP_ALLOW, $manageownrole, $syscontext->id); 1485 assign_capability('moodle/competency:planmanageown', CAP_ALLOW, $manageownrole, $syscontext->id); 1486 assign_capability('moodle/competency:planviewown', CAP_ALLOW, $manageownrole, $syscontext->id); 1487 $dg->role_assign($manageownrole, $user->id, $syscontext->id); 1488 $this->setUser($user); 1489 1490 // Create a framework and assign competencies. 1491 $framework = $lpg->create_framework(); 1492 $c1 = $lpg->create_competency(array('competencyframeworkid' => $framework->get('id'))); 1493 $c2 = $lpg->create_competency(array('competencyframeworkid' => $framework->get('id'))); 1494 $c3 = $lpg->create_competency(array('competencyframeworkid' => $framework->get('id'))); 1495 1496 // Create two plans and assign competencies. 1497 $plan = $lpg->create_plan(array('userid' => $user->id)); 1498 $otherplan = $lpg->create_plan(array('userid' => $user->id)); 1499 1500 $lpg->create_plan_competency(array('planid' => $plan->get('id'), 'competencyid' => $c1->get('id'))); 1501 $lpg->create_plan_competency(array('planid' => $plan->get('id'), 'competencyid' => $c2->get('id'))); 1502 $lpg->create_plan_competency(array('planid' => $plan->get('id'), 'competencyid' => $c3->get('id'))); 1503 $lpg->create_plan_competency(array('planid' => $otherplan->get('id'), 'competencyid' => $c1->get('id'))); 1504 1505 $uclist = array( 1506 $lpg->create_user_competency(array( 1507 'userid' => $user->id, 1508 'competencyid' => $c1->get('id'), 1509 'proficiency' => true, 1510 'grade' => 1 1511 )), 1512 $lpg->create_user_competency(array( 1513 'userid' => $user->id, 1514 'competencyid' => $c2->get('id'), 1515 'proficiency' => false, 1516 'grade' => 2 1517 )) 1518 ); 1519 1520 // Change status of the plan to complete. 1521 $record = $plan->to_record(); 1522 $record->status = \core_competency\plan::STATUS_COMPLETE; 1523 1524 try { 1525 $plan = api::update_plan($record); 1526 $this->fail('We cannot complete a plan using api::update_plan().'); 1527 } catch (\coding_exception $e) { 1528 // All good. 1529 } 1530 api::complete_plan($plan); 1531 1532 // Check that user compretencies are now in user_competency_plan objects and still in user_competency. 1533 $this->assertEquals(2, \core_competency\user_competency::count_records()); 1534 $this->assertEquals(3, \core_competency\user_competency_plan::count_records()); 1535 1536 $usercompetenciesplan = \core_competency\user_competency_plan::get_records(); 1537 1538 $this->assertEquals($uclist[0]->get('userid'), $usercompetenciesplan[0]->get('userid')); 1539 $this->assertEquals($uclist[0]->get('competencyid'), $usercompetenciesplan[0]->get('competencyid')); 1540 $this->assertEquals($uclist[0]->get('proficiency'), (bool) $usercompetenciesplan[0]->get('proficiency')); 1541 $this->assertEquals($uclist[0]->get('grade'), $usercompetenciesplan[0]->get('grade')); 1542 $this->assertEquals($plan->get('id'), $usercompetenciesplan[0]->get('planid')); 1543 1544 $this->assertEquals($uclist[1]->get('userid'), $usercompetenciesplan[1]->get('userid')); 1545 $this->assertEquals($uclist[1]->get('competencyid'), $usercompetenciesplan[1]->get('competencyid')); 1546 $this->assertEquals($uclist[1]->get('proficiency'), (bool) $usercompetenciesplan[1]->get('proficiency')); 1547 $this->assertEquals($uclist[1]->get('grade'), $usercompetenciesplan[1]->get('grade')); 1548 $this->assertEquals($plan->get('id'), $usercompetenciesplan[1]->get('planid')); 1549 1550 $this->assertEquals($user->id, $usercompetenciesplan[2]->get('userid')); 1551 $this->assertEquals($c3->get('id'), $usercompetenciesplan[2]->get('competencyid')); 1552 $this->assertNull($usercompetenciesplan[2]->get('proficiency')); 1553 $this->assertNull($usercompetenciesplan[2]->get('grade')); 1554 $this->assertEquals($plan->get('id'), $usercompetenciesplan[2]->get('planid')); 1555 1556 // Change status of the plan to active. 1557 $record = $plan->to_record(); 1558 $record->status = \core_competency\plan::STATUS_ACTIVE; 1559 1560 try { 1561 api::update_plan($record); 1562 $this->fail('Completed plan can not be edited'); 1563 } catch (\coding_exception $e) { 1564 // All good. 1565 } 1566 1567 api::reopen_plan($record->id); 1568 // Check that user_competency_plan objects are deleted if the plan status is changed to another status. 1569 $this->assertEquals(2, \core_competency\user_competency::count_records()); 1570 $this->assertEquals(0, \core_competency\user_competency_plan::count_records()); 1571 } 1572 1573 /** 1574 * Test completing plan does not change the order of competencies. 1575 */ 1576 public function test_complete_plan_doesnot_change_order() { 1577 global $DB; 1578 1579 $this->resetAfterTest(true); 1580 $this->setAdminUser(); 1581 $dg = $this->getDataGenerator(); 1582 $lpg = $this->getDataGenerator()->get_plugin_generator('core_competency'); 1583 1584 $syscontext = \context_system::instance(); 1585 1586 // Create users and roles for the test. 1587 $user = $dg->create_user(); 1588 1589 // Create a framework and assign competencies. 1590 $framework = $lpg->create_framework(); 1591 $c1 = $lpg->create_competency(array('competencyframeworkid' => $framework->get('id'))); 1592 $c2 = $lpg->create_competency(array('competencyframeworkid' => $framework->get('id'))); 1593 $c3 = $lpg->create_competency(array('competencyframeworkid' => $framework->get('id'))); 1594 1595 // Create two plans and assign competencies. 1596 $plan = $lpg->create_plan(array('userid' => $user->id)); 1597 1598 $lpg->create_plan_competency(array('planid' => $plan->get('id'), 'competencyid' => $c1->get('id'))); 1599 $lpg->create_plan_competency(array('planid' => $plan->get('id'), 'competencyid' => $c2->get('id'))); 1600 $lpg->create_plan_competency(array('planid' => $plan->get('id'), 'competencyid' => $c3->get('id'))); 1601 1602 // Changing competencies order in plan competency. 1603 api::reorder_plan_competency($plan->get('id'), $c1->get('id'), $c3->get('id')); 1604 1605 $competencies = api::list_plan_competencies($plan); 1606 $this->assertEquals($c2->get('id'), $competencies[0]->competency->get('id')); 1607 $this->assertEquals($c3->get('id'), $competencies[1]->competency->get('id')); 1608 $this->assertEquals($c1->get('id'), $competencies[2]->competency->get('id')); 1609 1610 // Completing plan. 1611 api::complete_plan($plan); 1612 1613 $competencies = api::list_plan_competencies($plan); 1614 1615 // Completing plan does not change order. 1616 $this->assertEquals($c2->get('id'), $competencies[0]->competency->get('id')); 1617 $this->assertEquals($c3->get('id'), $competencies[1]->competency->get('id')); 1618 $this->assertEquals($c1->get('id'), $competencies[2]->competency->get('id')); 1619 1620 // Testing plan based on template. 1621 $template = $lpg->create_template(); 1622 $framework = $lpg->create_framework(); 1623 $c1 = $lpg->create_competency(array('competencyframeworkid' => $framework->get('id'))); 1624 $c2 = $lpg->create_competency(array('competencyframeworkid' => $framework->get('id'))); 1625 $c3 = $lpg->create_competency(array('competencyframeworkid' => $framework->get('id'))); 1626 1627 $lpg->create_template_competency(array( 1628 'templateid' => $template->get('id'), 1629 'competencyid' => $c1->get('id') 1630 )); 1631 $lpg->create_template_competency(array( 1632 'templateid' => $template->get('id'), 1633 'competencyid' => $c2->get('id') 1634 )); 1635 $lpg->create_template_competency(array( 1636 'templateid' => $template->get('id'), 1637 'competencyid' => $c3->get('id') 1638 )); 1639 // Reorder competencies in template. 1640 api::reorder_template_competency($template->get('id'), $c1->get('id'), $c3->get('id')); 1641 1642 // Create plan from template. 1643 $plan = api::create_plan_from_template($template->get('id'), $user->id); 1644 1645 $competencies = api::list_plan_competencies($plan); 1646 1647 // Completing plan does not change order. 1648 $this->assertEquals($c2->get('id'), $competencies[0]->competency->get('id')); 1649 $this->assertEquals($c3->get('id'), $competencies[1]->competency->get('id')); 1650 $this->assertEquals($c1->get('id'), $competencies[2]->competency->get('id')); 1651 1652 // Completing plan. 1653 api::complete_plan($plan); 1654 1655 $competencies = api::list_plan_competencies($plan); 1656 1657 // Completing plan does not change order. 1658 $this->assertEquals($c2->get('id'), $competencies[0]->competency->get('id')); 1659 $this->assertEquals($c3->get('id'), $competencies[1]->competency->get('id')); 1660 $this->assertEquals($c1->get('id'), $competencies[2]->competency->get('id')); 1661 } 1662 1663 /** 1664 * Test remove plan and the managing of archived user competencies. 1665 */ 1666 public function test_delete_plan_manage_archived_competencies() { 1667 $this->resetAfterTest(true); 1668 $dg = $this->getDataGenerator(); 1669 $lpg = $this->getDataGenerator()->get_plugin_generator('core_competency'); 1670 1671 $syscontext = \context_system::instance(); 1672 1673 // Create user and role for the test. 1674 $user = $dg->create_user(); 1675 $managerole = $dg->create_role(array( 1676 'name' => 'User manage own', 1677 'shortname' => 'manageown' 1678 )); 1679 assign_capability('moodle/competency:planmanageowndraft', CAP_ALLOW, $managerole, $syscontext->id); 1680 assign_capability('moodle/competency:planmanageown', CAP_ALLOW, $managerole, $syscontext->id); 1681 $dg->role_assign($managerole, $user->id, $syscontext->id); 1682 $this->setUser($user); 1683 1684 // Create a framework and assign competencies. 1685 $framework = $lpg->create_framework(); 1686 $c1 = $lpg->create_competency(array('competencyframeworkid' => $framework->get('id'))); 1687 $c2 = $lpg->create_competency(array('competencyframeworkid' => $framework->get('id'))); 1688 $c3 = $lpg->create_competency(array('competencyframeworkid' => $framework->get('id'))); 1689 1690 // Create completed plan with records in user_competency. 1691 $completedplan = $lpg->create_plan(array('userid' => $user->id, 'status' => \core_competency\plan::STATUS_COMPLETE)); 1692 1693 $lpg->create_plan_competency(array('planid' => $completedplan->get('id'), 'competencyid' => $c1->get('id'))); 1694 $lpg->create_plan_competency(array('planid' => $completedplan->get('id'), 'competencyid' => $c2->get('id'))); 1695 1696 $uc1 = $lpg->create_user_competency(array('userid' => $user->id, 'competencyid' => $c1->get('id'))); 1697 $uc2 = $lpg->create_user_competency(array('userid' => $user->id, 'competencyid' => $c2->get('id'))); 1698 1699 $ucp1 = $lpg->create_user_competency_plan(array('userid' => $user->id, 'competencyid' => $c1->get('id'), 1700 'planid' => $completedplan->get('id'))); 1701 $ucp2 = $lpg->create_user_competency_plan(array('userid' => $user->id, 'competencyid' => $c2->get('id'), 1702 'planid' => $completedplan->get('id'))); 1703 1704 api::delete_plan($completedplan->get('id')); 1705 1706 // Check that achived user competencies are deleted. 1707 $this->assertEquals(0, \core_competency\plan::count_records()); 1708 $this->assertEquals(2, \core_competency\user_competency::count_records()); 1709 $this->assertEquals(0, \core_competency\user_competency_plan::count_records()); 1710 } 1711 1712 /** 1713 * Test listing of plan competencies. 1714 */ 1715 public function test_list_plan_competencies_manage_archived_competencies() { 1716 $this->resetAfterTest(true); 1717 $dg = $this->getDataGenerator(); 1718 $lpg = $this->getDataGenerator()->get_plugin_generator('core_competency'); 1719 1720 $syscontext = \context_system::instance(); 1721 1722 // Create user and role for the test. 1723 $user = $dg->create_user(); 1724 $viewrole = $dg->create_role(array( 1725 'name' => 'User view', 1726 'shortname' => 'view' 1727 )); 1728 assign_capability('moodle/competency:planviewdraft', CAP_ALLOW, $viewrole, $syscontext->id); 1729 assign_capability('moodle/competency:planview', CAP_ALLOW, $viewrole, $syscontext->id); 1730 $dg->role_assign($viewrole, $user->id, $syscontext->id); 1731 $this->setUser($user); 1732 1733 // Create a framework and assign competencies. 1734 $framework = $lpg->create_framework(); 1735 $c1 = $lpg->create_competency(array('competencyframeworkid' => $framework->get('id'))); 1736 $c2 = $lpg->create_competency(array('competencyframeworkid' => $framework->get('id'))); 1737 $c3 = $lpg->create_competency(array('competencyframeworkid' => $framework->get('id'))); 1738 1739 // Create draft plan with records in user_competency. 1740 $draftplan = $lpg->create_plan(array('userid' => $user->id)); 1741 1742 $lpg->create_plan_competency(array('planid' => $draftplan->get('id'), 'competencyid' => $c1->get('id'))); 1743 $lpg->create_plan_competency(array('planid' => $draftplan->get('id'), 'competencyid' => $c2->get('id'))); 1744 $lpg->create_plan_competency(array('planid' => $draftplan->get('id'), 'competencyid' => $c3->get('id'))); 1745 1746 $uc1 = $lpg->create_user_competency(array('userid' => $user->id, 'competencyid' => $c1->get('id'))); 1747 $uc2 = $lpg->create_user_competency(array('userid' => $user->id, 'competencyid' => $c2->get('id'))); 1748 1749 // Check that user_competency objects are returned when plan status is not complete. 1750 $plancompetencies = api::list_plan_competencies($draftplan); 1751 1752 $this->assertCount(3, $plancompetencies); 1753 $this->assertInstanceOf('\core_competency\user_competency', $plancompetencies[0]->usercompetency); 1754 $this->assertEquals($uc1->get('id'), $plancompetencies[0]->usercompetency->get('id')); 1755 $this->assertNull($plancompetencies[0]->usercompetencyplan); 1756 1757 $this->assertInstanceOf('\core_competency\user_competency', $plancompetencies[1]->usercompetency); 1758 $this->assertEquals($uc2->get('id'), $plancompetencies[1]->usercompetency->get('id')); 1759 $this->assertNull($plancompetencies[1]->usercompetencyplan); 1760 1761 $this->assertInstanceOf('\core_competency\user_competency', $plancompetencies[2]->usercompetency); 1762 $this->assertEquals(0, $plancompetencies[2]->usercompetency->get('id')); 1763 $this->assertNull($plancompetencies[2]->usercompetencyplan); 1764 1765 // Create completed plan with records in user_competency_plan. 1766 $completedplan = $lpg->create_plan(array('userid' => $user->id, 'status' => \core_competency\plan::STATUS_COMPLETE)); 1767 1768 $pc1 = $lpg->create_plan_competency(array('planid' => $completedplan->get('id'), 'competencyid' => $c1->get('id'))); 1769 $pc2 = $lpg->create_plan_competency(array('planid' => $completedplan->get('id'), 'competencyid' => $c2->get('id'))); 1770 $pc3 = $lpg->create_plan_competency(array('planid' => $completedplan->get('id'), 'competencyid' => $c3->get('id'))); 1771 1772 $ucp1 = $lpg->create_user_competency_plan(array('userid' => $user->id, 'competencyid' => $c1->get('id'), 1773 'planid' => $completedplan->get('id'))); 1774 $ucp2 = $lpg->create_user_competency_plan(array('userid' => $user->id, 'competencyid' => $c2->get('id'), 1775 'planid' => $completedplan->get('id'))); 1776 $ucp3 = $lpg->create_user_competency_plan(array('userid' => $user->id, 'competencyid' => $c3->get('id'), 1777 'planid' => $completedplan->get('id'))); 1778 1779 // Check that user_competency_plan objects are returned when plan status is complete. 1780 $plancompetencies = api::list_plan_competencies($completedplan); 1781 1782 $this->assertCount(3, $plancompetencies); 1783 $this->assertInstanceOf('\core_competency\user_competency_plan', $plancompetencies[0]->usercompetencyplan); 1784 $this->assertEquals($ucp1->get('id'), $plancompetencies[0]->usercompetencyplan->get('id')); 1785 $this->assertNull($plancompetencies[0]->usercompetency); 1786 $this->assertInstanceOf('\core_competency\user_competency_plan', $plancompetencies[1]->usercompetencyplan); 1787 $this->assertEquals($ucp2->get('id'), $plancompetencies[1]->usercompetencyplan->get('id')); 1788 $this->assertNull($plancompetencies[1]->usercompetency); 1789 $this->assertInstanceOf('\core_competency\user_competency_plan', $plancompetencies[2]->usercompetencyplan); 1790 $this->assertEquals($ucp3->get('id'), $plancompetencies[2]->usercompetencyplan->get('id')); 1791 $this->assertNull($plancompetencies[2]->usercompetency); 1792 } 1793 1794 public function test_create_template_cohort() { 1795 $this->resetAfterTest(true); 1796 $this->setAdminUser(); 1797 1798 $dg = $this->getDataGenerator(); 1799 $lpg = $this->getDataGenerator()->get_plugin_generator('core_competency'); 1800 1801 $c1 = $dg->create_cohort(); 1802 $c2 = $dg->create_cohort(); 1803 $t1 = $lpg->create_template(); 1804 $t2 = $lpg->create_template(); 1805 1806 $this->assertEquals(0, \core_competency\template_cohort::count_records()); 1807 1808 // Create two relations with mixed parameters. 1809 $result = api::create_template_cohort($t1->get('id'), $c1->id); 1810 $result = api::create_template_cohort($t1, $c2); 1811 1812 $this->assertEquals(2, \core_competency\template_cohort::count_records()); 1813 $this->assertInstanceOf('core_competency\template_cohort', $result); 1814 $this->assertEquals($c2->id, $result->get('cohortid')); 1815 $this->assertEquals($t1->get('id'), $result->get('templateid')); 1816 $this->assertEquals(2, \core_competency\template_cohort::count_records_select('templateid = :id', 1817 array('id' => $t1->get('id')))); 1818 $this->assertEquals(0, \core_competency\template_cohort::count_records_select('templateid = :id', 1819 array('id' => $t2->get('id')))); 1820 } 1821 1822 public function test_create_template_cohort_permissions() { 1823 $this->resetAfterTest(true); 1824 1825 $dg = $this->getDataGenerator(); 1826 $lpg = $this->getDataGenerator()->get_plugin_generator('core_competency'); 1827 $cat = $dg->create_category(); 1828 $catcontext = \context_coursecat::instance($cat->id); 1829 $syscontext = \context_system::instance(); 1830 1831 $user = $dg->create_user(); 1832 $role = $dg->create_role(); 1833 assign_capability('moodle/competency:templatemanage', CAP_ALLOW, $role, $syscontext->id, true); 1834 $dg->role_assign($role, $user->id, $syscontext->id); 1835 1836 $cohortrole = $dg->create_role(); 1837 assign_capability('moodle/cohort:view', CAP_ALLOW, $cohortrole, $syscontext->id, true); 1838 1839 accesslib_clear_all_caches_for_unit_testing(); 1840 1841 $c1 = $dg->create_cohort(); 1842 $c2 = $dg->create_cohort(array('visible' => 0, 'contextid' => $catcontext->id)); 1843 $t1 = $lpg->create_template(); 1844 1845 $this->assertEquals(0, \core_competency\template_cohort::count_records()); 1846 1847 $this->setUser($user); 1848 $result = api::create_template_cohort($t1, $c1); 1849 $this->assertInstanceOf('core_competency\\template_cohort', $result); 1850 1851 try { 1852 $result = api::create_template_cohort($t1, $c2); 1853 $this->fail('Permission required.'); 1854 } catch (\required_capability_exception $e) { 1855 // That's what should happen. 1856 } 1857 1858 // Try again with the right permissions. 1859 $dg->role_assign($cohortrole, $user->id, $catcontext->id); 1860 accesslib_clear_all_caches_for_unit_testing(); 1861 1862 $result = api::create_template_cohort($t1, $c2); 1863 $this->assertInstanceOf('core_competency\\template_cohort', $result); 1864 } 1865 1866 public function test_reorder_template_competencies_permissions() { 1867 $this->resetAfterTest(true); 1868 1869 $dg = $this->getDataGenerator(); 1870 $lpg = $this->getDataGenerator()->get_plugin_generator('core_competency'); 1871 $cat = $dg->create_category(); 1872 $catcontext = \context_coursecat::instance($cat->id); 1873 $syscontext = \context_system::instance(); 1874 1875 $user = $dg->create_user(); 1876 $role = $dg->create_role(); 1877 assign_capability('moodle/competency:templatemanage', CAP_ALLOW, $role, $syscontext->id, true); 1878 $dg->role_assign($role, $user->id, $syscontext->id); 1879 1880 // Create a template. 1881 $template = $lpg->create_template(array('contextid' => $catcontext->id)); 1882 1883 // Create a competency framework. 1884 $framework = $lpg->create_framework(array('contextid' => $catcontext->id)); 1885 1886 // Create competencies. 1887 $competency1 = $lpg->create_competency(array('competencyframeworkid' => $framework->get('id'))); 1888 $competency2 = $lpg->create_competency(array('competencyframeworkid' => $framework->get('id'))); 1889 1890 // Add the competencies. 1891 $lpg->create_template_competency(array( 1892 'templateid' => $template->get('id'), 1893 'competencyid' => $competency1->get('id') 1894 )); 1895 $lpg->create_template_competency(array( 1896 'templateid' => $template->get('id'), 1897 'competencyid' => $competency2->get('id') 1898 )); 1899 $this->setUser($user); 1900 // Can reorder competencies with system context permissions in category context. 1901 $result = api::reorder_template_competency($template->get('id'), $competency2->get('id'), $competency1->get('id')); 1902 $this->assertTrue($result); 1903 unassign_capability('moodle/competency:templatemanage', $role, $syscontext->id); 1904 accesslib_clear_all_caches_for_unit_testing(); 1905 1906 try { 1907 api::reorder_template_competency($template->get('id'), $competency2->get('id'), $competency1->get('id')); 1908 $this->fail('Exception expected due to not permissions to manage template competencies'); 1909 } catch (\required_capability_exception $e) { 1910 $this->assertEquals('nopermissions', $e->errorcode); 1911 } 1912 1913 // Giving permissions in category context. 1914 assign_capability('moodle/competency:templatemanage', CAP_ALLOW, $role, $catcontext->id, true); 1915 $dg->role_assign($role, $user->id, $catcontext->id); 1916 // User with templatemanage capability in category context can reorder competencies in temple. 1917 $result = api::reorder_template_competency($template->get('id'), $competency1->get('id'), $competency2->get('id')); 1918 $this->assertTrue($result); 1919 // Removing templatemanage capability in category context. 1920 unassign_capability('moodle/competency:templatemanage', $role, $catcontext->id); 1921 accesslib_clear_all_caches_for_unit_testing(); 1922 1923 try { 1924 api::reorder_template_competency($template->get('id'), $competency2->get('id'), $competency1->get('id')); 1925 $this->fail('Exception expected due to not permissions to manage template competencies'); 1926 } catch (\required_capability_exception $e) { 1927 $this->assertEquals('nopermissions', $e->errorcode); 1928 } 1929 } 1930 1931 public function test_delete_template() { 1932 $this->resetAfterTest(true); 1933 $this->setAdminUser(); 1934 1935 $dg = $this->getDataGenerator(); 1936 $lpg = $this->getDataGenerator()->get_plugin_generator('core_competency'); 1937 1938 $c1 = $dg->create_cohort(); 1939 $c2 = $dg->create_cohort(); 1940 $template = $lpg->create_template(); 1941 $id = $template->get('id'); 1942 1943 // Create 2 template cohorts. 1944 $tc1 = $lpg->create_template_cohort(array('templateid' => $template->get('id'), 'cohortid' => $c1->id)); 1945 $tc1 = $lpg->create_template_cohort(array('templateid' => $template->get('id'), 'cohortid' => $c2->id)); 1946 1947 // Check pre-test. 1948 $this->assertTrue(\core_competency\template::record_exists($id)); 1949 $this->assertEquals(2, \core_competency\template_cohort::count_records(array('templateid' => $id))); 1950 1951 $result = api::delete_template($template->get('id')); 1952 $this->assertTrue($result); 1953 1954 // Check that the template deos not exist anymore. 1955 $this->assertFalse(\core_competency\template::record_exists($id)); 1956 1957 // Test if associated cohorts are also deleted. 1958 $this->assertEquals(0, \core_competency\template_cohort::count_records(array('templateid' => $id))); 1959 } 1960 1961 public function test_delete_template_cohort() { 1962 $this->resetAfterTest(true); 1963 $this->setAdminUser(); 1964 1965 $dg = $this->getDataGenerator(); 1966 $lpg = $this->getDataGenerator()->get_plugin_generator('core_competency'); 1967 1968 $c1 = $dg->create_cohort(); 1969 $c2 = $dg->create_cohort(); 1970 $t1 = $lpg->create_template(); 1971 $t2 = $lpg->create_template(); 1972 $tc1 = $lpg->create_template_cohort(array('templateid' => $t1->get('id'), 'cohortid' => $c1->id)); 1973 $tc1 = $lpg->create_template_cohort(array('templateid' => $t2->get('id'), 'cohortid' => $c2->id)); 1974 1975 $this->assertEquals(2, \core_competency\template_cohort::count_records()); 1976 $this->assertEquals(1, \core_competency\template_cohort::count_records_select('templateid = :id', 1977 array('id' => $t1->get('id')))); 1978 $this->assertEquals(1, \core_competency\template_cohort::count_records_select('templateid = :id', 1979 array('id' => $t2->get('id')))); 1980 1981 // Delete existing. 1982 $result = api::delete_template_cohort($t1->get('id'), $c1->id); 1983 $this->assertTrue($result); 1984 $this->assertEquals(1, \core_competency\template_cohort::count_records()); 1985 $this->assertEquals(0, \core_competency\template_cohort::count_records_select('templateid = :id', 1986 array('id' => $t1->get('id')))); 1987 $this->assertEquals(1, \core_competency\template_cohort::count_records_select('templateid = :id', 1988 array('id' => $t2->get('id')))); 1989 1990 // Delete non-existant. 1991 $result = api::delete_template_cohort($t1->get('id'), $c1->id); 1992 $this->assertTrue($result); 1993 $this->assertEquals(1, \core_competency\template_cohort::count_records()); 1994 $this->assertEquals(0, \core_competency\template_cohort::count_records_select('templateid = :id', 1995 array('id' => $t1->get('id')))); 1996 $this->assertEquals(1, \core_competency\template_cohort::count_records_select('templateid = :id', 1997 array('id' => $t2->get('id')))); 1998 } 1999 2000 public function test_add_evidence_log() { 2001 $this->resetAfterTest(true); 2002 $dg = $this->getDataGenerator(); 2003 $lpg = $dg->get_plugin_generator('core_competency'); 2004 2005 $u1 = $dg->create_user(); 2006 $u1ctx = \context_user::instance($u1->id); 2007 $f1 = $lpg->create_framework(); 2008 $c1 = $lpg->create_competency(array('competencyframeworkid' => $f1->get('id'))); 2009 $c2 = $lpg->create_competency(array('competencyframeworkid' => $f1->get('id'))); 2010 2011 // Creating a standard evidence with minimal information. 2012 $evidence = api::add_evidence($u1->id, $c1->get('id'), $u1ctx->id, \core_competency\evidence::ACTION_LOG, 2013 'invaliddata', 'error'); 2014 $evidence->read(); 2015 $uc = \core_competency\user_competency::get_record(array('userid' => $u1->id, 'competencyid' => $c1->get('id'))); 2016 $this->assertEquals(\core_competency\user_competency::STATUS_IDLE, $uc->get('status')); 2017 $this->assertSame(null, $uc->get('grade')); 2018 $this->assertSame(null, $uc->get('proficiency')); 2019 $this->assertEquals($uc->get('id'), $evidence->get('usercompetencyid')); 2020 $this->assertEquals($u1ctx->id, $evidence->get('contextid')); 2021 $this->assertEquals(\core_competency\evidence::ACTION_LOG, $evidence->get('action')); 2022 $this->assertEquals('invaliddata', $evidence->get('descidentifier')); 2023 $this->assertEquals('error', $evidence->get('desccomponent')); 2024 $this->assertSame(null, $evidence->get('desca')); 2025 $this->assertSame(null, $evidence->get('url')); 2026 $this->assertSame(null, $evidence->get('grade')); 2027 $this->assertSame(null, $evidence->get('actionuserid')); 2028 2029 // Creating a standard evidence with more information. 2030 $evidence = api::add_evidence($u1->id, $c1->get('id'), $u1ctx->id, \core_competency\evidence::ACTION_LOG, 'invaliddata', 2031 'error', '$a', false, 'http://moodle.org', null, 2, 'The evidence of prior learning were reviewed.'); 2032 $evidence->read(); 2033 $uc = \core_competency\user_competency::get_record(array('userid' => $u1->id, 'competencyid' => $c1->get('id'))); 2034 $this->assertEquals(\core_competency\user_competency::STATUS_IDLE, $uc->get('status')); 2035 $this->assertSame(null, $uc->get('grade')); 2036 $this->assertSame(null, $uc->get('proficiency')); 2037 $this->assertEquals($uc->get('id'), $evidence->get('usercompetencyid')); 2038 $this->assertEquals($u1ctx->id, $evidence->get('contextid')); 2039 $this->assertEquals(\core_competency\evidence::ACTION_LOG, $evidence->get('action')); 2040 $this->assertEquals('invaliddata', $evidence->get('descidentifier')); 2041 $this->assertEquals('error', $evidence->get('desccomponent')); 2042 $this->assertEquals('$a', $evidence->get('desca')); 2043 $this->assertEquals('http://moodle.org', $evidence->get('url')); 2044 $this->assertSame(null, $evidence->get('grade')); 2045 $this->assertEquals(2, $evidence->get('actionuserid')); 2046 $this->assertSame('The evidence of prior learning were reviewed.', $evidence->get('note')); 2047 2048 // Creating a standard evidence and send for review. 2049 $evidence = api::add_evidence($u1->id, $c2->get('id'), $u1ctx->id, \core_competency\evidence::ACTION_LOG, 'invaliddata', 2050 'error', null, true); 2051 $evidence->read(); 2052 $uc = \core_competency\user_competency::get_record(array('userid' => $u1->id, 'competencyid' => $c2->get('id'))); 2053 $this->assertEquals(\core_competency\user_competency::STATUS_WAITING_FOR_REVIEW, $uc->get('status')); 2054 2055 // Trying to pass a grade should fail. 2056 try { 2057 $evidence = api::add_evidence($u1->id, $c1->get('id'), $u1ctx->id, \core_competency\evidence::ACTION_LOG, 'invaliddata', 2058 'error', null, false, null, 1); 2059 $this->fail('A grade can not be set'); 2060 } catch (\coding_exception $e) { 2061 $this->assertMatchesRegularExpression('/grade MUST NOT be set/', $e->getMessage()); 2062 } 2063 } 2064 2065 public function test_add_evidence_complete() { 2066 $this->resetAfterTest(true); 2067 $dg = $this->getDataGenerator(); 2068 $lpg = $dg->get_plugin_generator('core_competency'); 2069 2070 $u1 = $dg->create_user(); 2071 $u1ctx = \context_user::instance($u1->id); 2072 $scale = $dg->create_scale(array('scale' => 'A,B,C,D')); 2073 $scaleconfig = array(array('scaleid' => $scale->id)); 2074 $scaleconfig[] = array('name' => 'B', 'id' => 2, 'scaledefault' => 1, 'proficient' => 0); 2075 $scaleconfig[] = array('name' => 'C', 'id' => 3, 'scaledefault' => 0, 'proficient' => 1); 2076 $scaleconfig[] = array('name' => 'D', 'id' => 4, 'scaledefault' => 0, 'proficient' => 1); 2077 $c2scaleconfig = array(array('scaleid' => $scale->id)); 2078 $c2scaleconfig[] = array('name' => 'B', 'id' => 2, 'scaledefault' => 0, 'proficient' => 1); 2079 $c2scaleconfig[] = array('name' => 'C', 'id' => 3, 'scaledefault' => 0, 'proficient' => 0); 2080 $c2scaleconfig[] = array('name' => 'D', 'id' => 4, 'scaledefault' => 1, 'proficient' => 1); 2081 $f1 = $lpg->create_framework(array('scaleid' => $scale->id, 'scaleconfiguration' => $scaleconfig)); 2082 $c1 = $lpg->create_competency(array('competencyframeworkid' => $f1->get('id'))); 2083 $c2 = $lpg->create_competency(array('competencyframeworkid' => $f1->get('id'), 'scaleid' => $scale->id, 2084 'scaleconfiguration' => $c2scaleconfig)); 2085 $c3 = $lpg->create_competency(array('competencyframeworkid' => $f1->get('id'))); 2086 2087 // Creating an evidence with minimal information. 2088 $evidence = api::add_evidence($u1->id, $c1->get('id'), $u1ctx->id, \core_competency\evidence::ACTION_COMPLETE, 2089 'invaliddata', 'error'); 2090 $evidence->read(); 2091 $uc = \core_competency\user_competency::get_record(array('userid' => $u1->id, 'competencyid' => $c1->get('id'))); 2092 $this->assertEquals(\core_competency\user_competency::STATUS_IDLE, $uc->get('status')); 2093 $this->assertEquals(2, $uc->get('grade')); // The grade has been set automatically to the framework default. 2094 $this->assertEquals(0, $uc->get('proficiency')); 2095 $this->assertEquals($uc->get('id'), $evidence->get('usercompetencyid')); 2096 $this->assertEquals($u1ctx->id, $evidence->get('contextid')); 2097 $this->assertEquals(\core_competency\evidence::ACTION_COMPLETE, $evidence->get('action')); 2098 $this->assertEquals('invaliddata', $evidence->get('descidentifier')); 2099 $this->assertEquals('error', $evidence->get('desccomponent')); 2100 $this->assertSame(null, $evidence->get('desca')); 2101 $this->assertSame(null, $evidence->get('url')); 2102 $this->assertEquals(2, $evidence->get('grade')); 2103 $this->assertSame(null, $evidence->get('actionuserid')); 2104 2105 // Creating an evidence complete on competency with custom scale. 2106 $evidence = api::add_evidence($u1->id, $c2->get('id'), $u1ctx->id, \core_competency\evidence::ACTION_COMPLETE, 2107 'invaliddata', 'error'); 2108 $evidence->read(); 2109 $uc = \core_competency\user_competency::get_record(array('userid' => $u1->id, 'competencyid' => $c2->get('id'))); 2110 $this->assertEquals(\core_competency\user_competency::STATUS_IDLE, $uc->get('status')); 2111 $this->assertEquals(4, $uc->get('grade')); // The grade has been set automatically to the competency default. 2112 $this->assertEquals(true, $uc->get('proficiency')); 2113 $this->assertEquals($uc->get('id'), $evidence->get('usercompetencyid')); 2114 $this->assertEquals($u1ctx->id, $evidence->get('contextid')); 2115 $this->assertEquals(\core_competency\evidence::ACTION_COMPLETE, $evidence->get('action')); 2116 $this->assertEquals('invaliddata', $evidence->get('descidentifier')); 2117 $this->assertEquals('error', $evidence->get('desccomponent')); 2118 $this->assertSame(null, $evidence->get('desca')); 2119 $this->assertSame(null, $evidence->get('url')); 2120 $this->assertEquals(4, $evidence->get('grade')); 2121 $this->assertSame(null, $evidence->get('actionuserid')); 2122 2123 // Creating an evidence complete on a user competency with an existing grade. 2124 $uc = $lpg->create_user_competency(array('userid' => $u1->id, 'competencyid' => $c3->get('id'), 'grade' => 1, 2125 'proficiency' => 0)); 2126 $this->assertEquals(1, $uc->get('grade')); 2127 $this->assertEquals(0, $uc->get('proficiency')); 2128 $evidence = api::add_evidence($u1->id, $c3->get('id'), $u1ctx->id, \core_competency\evidence::ACTION_COMPLETE, 2129 'invaliddata', 'error'); 2130 $evidence->read(); 2131 $uc->read(); 2132 $this->assertEquals(\core_competency\user_competency::STATUS_IDLE, $uc->get('status')); 2133 $this->assertEquals(1, $uc->get('grade')); // The grade has not been changed. 2134 $this->assertEquals(0, $uc->get('proficiency')); 2135 $this->assertEquals($uc->get('id'), $evidence->get('usercompetencyid')); 2136 $this->assertEquals($u1ctx->id, $evidence->get('contextid')); 2137 $this->assertEquals(\core_competency\evidence::ACTION_COMPLETE, $evidence->get('action')); 2138 $this->assertEquals('invaliddata', $evidence->get('descidentifier')); 2139 $this->assertEquals('error', $evidence->get('desccomponent')); 2140 $this->assertSame(null, $evidence->get('desca')); 2141 $this->assertSame(null, $evidence->get('url')); 2142 $this->assertEquals(2, $evidence->get('grade')); // The complete grade has been set. 2143 $this->assertSame(null, $evidence->get('actionuserid')); 2144 2145 // Creating a standard evidence and send for review. 2146 $evidence = api::add_evidence($u1->id, $c2->get('id'), $u1ctx->id, \core_competency\evidence::ACTION_COMPLETE, 2147 'invaliddata', 'error', null, true); 2148 $evidence->read(); 2149 $uc = \core_competency\user_competency::get_record(array('userid' => $u1->id, 'competencyid' => $c2->get('id'))); 2150 $this->assertEquals(\core_competency\user_competency::STATUS_WAITING_FOR_REVIEW, $uc->get('status')); 2151 2152 // Trying to pass a grade should throw an exception. 2153 try { 2154 api::add_evidence($u1->id, $c2->get('id'), $u1ctx->id, \core_competency\evidence::ACTION_COMPLETE, 'invaliddata', 2155 'error', null, false, null, 1); 2156 } catch (\coding_exception $e) { 2157 $this->assertMatchesRegularExpression('/grade MUST NOT be set/', $e->getMessage()); 2158 } 2159 } 2160 2161 public function test_add_evidence_override() { 2162 $this->resetAfterTest(true); 2163 $dg = $this->getDataGenerator(); 2164 $lpg = $dg->get_plugin_generator('core_competency'); 2165 2166 $u1 = $dg->create_user(); 2167 $u1ctx = \context_user::instance($u1->id); 2168 $f1 = $lpg->create_framework(); 2169 $c1 = $lpg->create_competency(array('competencyframeworkid' => $f1->get('id'))); 2170 2171 // Creating an evidence with minimal information. 2172 $evidence = api::add_evidence($u1->id, $c1->get('id'), $u1ctx->id, \core_competency\evidence::ACTION_OVERRIDE, 2173 'invaliddata', 'error'); 2174 $evidence->read(); 2175 $uc = \core_competency\user_competency::get_record(array('userid' => $u1->id, 'competencyid' => $c1->get('id'))); 2176 $this->assertEquals(\core_competency\user_competency::STATUS_IDLE, $uc->get('status')); 2177 $this->assertSame(null, $uc->get('grade')); // We overrode with 'null'. 2178 $this->assertSame(null, $uc->get('proficiency')); 2179 $this->assertEquals($uc->get('id'), $evidence->get('usercompetencyid')); 2180 $this->assertEquals($u1ctx->id, $evidence->get('contextid')); 2181 $this->assertEquals(\core_competency\evidence::ACTION_OVERRIDE, $evidence->get('action')); 2182 $this->assertEquals('invaliddata', $evidence->get('descidentifier')); 2183 $this->assertEquals('error', $evidence->get('desccomponent')); 2184 $this->assertSame(null, $evidence->get('desca')); 2185 $this->assertSame(null, $evidence->get('url')); 2186 $this->assertSame(null, $evidence->get('grade')); // We overrode with 'null'. 2187 $this->assertSame(null, $evidence->get('actionuserid')); 2188 2189 // Creating an evidence with a grade information. 2190 $evidence = api::add_evidence($u1->id, $c1->get('id'), $u1ctx->id, \core_competency\evidence::ACTION_OVERRIDE, 2191 'invaliddata', 'error', null, false, null, 3); 2192 $evidence->read(); 2193 $uc = \core_competency\user_competency::get_record(array('userid' => $u1->id, 'competencyid' => $c1->get('id'))); 2194 $this->assertEquals(\core_competency\user_competency::STATUS_IDLE, $uc->get('status')); 2195 $this->assertEquals(3, $uc->get('grade')); 2196 $this->assertEquals(true, $uc->get('proficiency')); 2197 $this->assertEquals($uc->get('id'), $evidence->get('usercompetencyid')); 2198 $this->assertEquals($u1ctx->id, $evidence->get('contextid')); 2199 $this->assertEquals(\core_competency\evidence::ACTION_OVERRIDE, $evidence->get('action')); 2200 $this->assertEquals('invaliddata', $evidence->get('descidentifier')); 2201 $this->assertEquals('error', $evidence->get('desccomponent')); 2202 $this->assertSame(null, $evidence->get('desca')); 2203 $this->assertSame(null, $evidence->get('url')); 2204 $this->assertEquals(3, $evidence->get('grade')); 2205 $this->assertSame(null, $evidence->get('actionuserid')); 2206 2207 // Creating an evidence with another grade information. 2208 $evidence = api::add_evidence($u1->id, $c1->get('id'), $u1ctx->id, \core_competency\evidence::ACTION_OVERRIDE, 2209 'invaliddata', 'error', null, false, null, 1); 2210 $evidence->read(); 2211 $uc = \core_competency\user_competency::get_record(array('userid' => $u1->id, 'competencyid' => $c1->get('id'))); 2212 $this->assertEquals(\core_competency\user_competency::STATUS_IDLE, $uc->get('status')); 2213 $this->assertEquals(1, $uc->get('grade')); 2214 $this->assertEquals(0, $uc->get('proficiency')); 2215 $this->assertEquals($uc->get('id'), $evidence->get('usercompetencyid')); 2216 $this->assertEquals($u1ctx->id, $evidence->get('contextid')); 2217 $this->assertEquals(\core_competency\evidence::ACTION_OVERRIDE, $evidence->get('action')); 2218 $this->assertEquals('invaliddata', $evidence->get('descidentifier')); 2219 $this->assertEquals('error', $evidence->get('desccomponent')); 2220 $this->assertSame(null, $evidence->get('desca')); 2221 $this->assertSame(null, $evidence->get('url')); 2222 $this->assertEquals(1, $evidence->get('grade')); 2223 $this->assertSame(null, $evidence->get('actionuserid')); 2224 2225 // Creating reverting the grade and send for review. 2226 $evidence = api::add_evidence($u1->id, $c1->get('id'), $u1ctx->id, \core_competency\evidence::ACTION_OVERRIDE, 2227 'invaliddata', 'error', null, true); 2228 $evidence->read(); 2229 $uc = \core_competency\user_competency::get_record(array('userid' => $u1->id, 'competencyid' => $c1->get('id'))); 2230 $this->assertSame(null, $uc->get('grade')); 2231 $this->assertSame(null, $uc->get('proficiency')); 2232 $this->assertEquals(\core_competency\user_competency::STATUS_WAITING_FOR_REVIEW, $uc->get('status')); 2233 $this->assertSame(null, $evidence->get('grade')); 2234 } 2235 2236 public function test_add_evidence_and_send_for_review() { 2237 $this->resetAfterTest(true); 2238 $dg = $this->getDataGenerator(); 2239 $lpg = $dg->get_plugin_generator('core_competency'); 2240 2241 $u1 = $dg->create_user(); 2242 $u1ctx = \context_user::instance($u1->id); 2243 $f1 = $lpg->create_framework(); 2244 $c1 = $lpg->create_competency(array('competencyframeworkid' => $f1->get('id'))); 2245 2246 // Non-existing user competencies are created up for review. 2247 $evidence = api::add_evidence($u1->id, $c1->get('id'), $u1ctx->id, \core_competency\evidence::ACTION_LOG, 'invaliddata', 2248 'error', null, true); 2249 $uc = \core_competency\user_competency::get_record(array('userid' => $u1->id, 'competencyid' => $c1->get('id'))); 2250 $this->assertEquals(\core_competency\user_competency::STATUS_WAITING_FOR_REVIEW, $uc->get('status')); 2251 2252 // Existing user competencies sent for review don't change. 2253 $evidence = api::add_evidence($u1->id, $c1->get('id'), $u1ctx->id, \core_competency\evidence::ACTION_LOG, 'invaliddata', 2254 'error', null, true); 2255 $uc->read(); 2256 $this->assertEquals(\core_competency\user_competency::STATUS_WAITING_FOR_REVIEW, $uc->get('status')); 2257 2258 // A user competency with a status non-idle won't change. 2259 $uc->set('status', \core_competency\user_competency::STATUS_IN_REVIEW); 2260 $uc->update(); 2261 $evidence = api::add_evidence($u1->id, $c1->get('id'), $u1ctx->id, \core_competency\evidence::ACTION_LOG, 'invaliddata', 2262 'error', null, true); 2263 $uc->read(); 2264 $this->assertEquals(\core_competency\user_competency::STATUS_IN_REVIEW, $uc->get('status')); 2265 } 2266 2267 /** 2268 * Test add evidence for existing user_competency. 2269 */ 2270 public function test_add_evidence_existing_user_competency() { 2271 $this->resetAfterTest(true); 2272 $dg = $this->getDataGenerator(); 2273 $lpg = $this->getDataGenerator()->get_plugin_generator('core_competency'); 2274 2275 $syscontext = \context_system::instance(); 2276 2277 // Create users. 2278 $user = $dg->create_user(); 2279 $this->setUser($user); 2280 2281 // Create a framework and assign competencies. 2282 $framework = $lpg->create_framework(); 2283 $c1 = $lpg->create_competency(array('competencyframeworkid' => $framework->get('id'))); 2284 $c2 = $lpg->create_competency(array('competencyframeworkid' => $framework->get('id'))); 2285 $c3 = $lpg->create_competency(array('competencyframeworkid' => $framework->get('id'))); 2286 $uc = $lpg->create_user_competency(array('userid' => $user->id, 'competencyid' => $c1->get('id'))); 2287 $this->assertSame(null, $uc->get('grade')); 2288 $this->assertSame(null, $uc->get('proficiency')); 2289 2290 // Create an evidence and check it was created with the right usercomptencyid and information. 2291 $evidence = api::add_evidence($user->id, $c1->get('id'), $syscontext->id, \core_competency\evidence::ACTION_OVERRIDE, 2292 'invalidevidencedesc', 'core_competency', array('a' => 'b'), false, 'http://moodle.org', 1, 2); 2293 $this->assertEquals(1, \core_competency\evidence::count_records()); 2294 2295 $evidence->read(); 2296 $uc->read(); 2297 $this->assertEquals($uc->get('id'), $evidence->get('usercompetencyid')); 2298 $this->assertEquals('invalidevidencedesc', $evidence->get('descidentifier')); 2299 $this->assertEquals('core_competency', $evidence->get('desccomponent')); 2300 $this->assertEquals((object) array('a' => 'b'), $evidence->get('desca')); 2301 $this->assertEquals('http://moodle.org', $evidence->get('url')); 2302 $this->assertEquals(\core_competency\evidence::ACTION_OVERRIDE, $evidence->get('action')); 2303 $this->assertEquals(2, $evidence->get('actionuserid')); 2304 $this->assertEquals(1, $evidence->get('grade')); 2305 $this->assertEquals(1, $uc->get('grade')); 2306 $this->assertEquals(0, $uc->get('proficiency')); 2307 } 2308 2309 /** 2310 * Test add evidence for non-existing user_competency. 2311 */ 2312 public function test_add_evidence_no_existing_user_competency() { 2313 $this->resetAfterTest(true); 2314 $dg = $this->getDataGenerator(); 2315 $lpg = $this->getDataGenerator()->get_plugin_generator('core_competency'); 2316 2317 $syscontext = \context_system::instance(); 2318 2319 // Create users. 2320 $user = $dg->create_user(); 2321 $this->setUser($user); 2322 2323 // Create a framework and assign competencies. 2324 $framework = $lpg->create_framework(); 2325 $c1 = $lpg->create_competency(array('competencyframeworkid' => $framework->get('id'))); 2326 $c2 = $lpg->create_competency(array('competencyframeworkid' => $framework->get('id'))); 2327 $this->assertEquals(0, \core_competency\user_competency::count_records()); 2328 2329 // Create an evidence without a user competency record. 2330 $evidence = api::add_evidence($user->id, $c1->get('id'), $syscontext->id, \core_competency\evidence::ACTION_OVERRIDE, 2331 'invalidevidencedesc', 'core_competency', 'Hello world!', false, 'http://moodle.org', 1, 2); 2332 $this->assertEquals(1, \core_competency\evidence::count_records()); 2333 $this->assertEquals(1, \core_competency\user_competency::count_records()); 2334 2335 $uc = \core_competency\user_competency::get_record(array('userid' => $user->id, 'competencyid' => $c1->get('id'))); 2336 $evidence->read(); 2337 $this->assertEquals($uc->get('id'), $evidence->get('usercompetencyid')); 2338 $this->assertEquals('invalidevidencedesc', $evidence->get('descidentifier')); 2339 $this->assertEquals('core_competency', $evidence->get('desccomponent')); 2340 $this->assertEquals('Hello world!', $evidence->get('desca')); 2341 $this->assertEquals('http://moodle.org', $evidence->get('url')); 2342 $this->assertEquals(\core_competency\evidence::ACTION_OVERRIDE, $evidence->get('action')); 2343 $this->assertEquals(2, $evidence->get('actionuserid')); 2344 $this->assertEquals(1, $evidence->get('grade')); 2345 $this->assertEquals(1, $uc->get('grade')); 2346 $this->assertEquals(0, $uc->get('proficiency')); 2347 } 2348 2349 public function test_add_evidence_applies_competency_rules() { 2350 $this->resetAfterTest(true); 2351 $dg = $this->getDataGenerator(); 2352 $lpg = $dg->get_plugin_generator('core_competency'); 2353 $syscontext = \context_system::instance(); 2354 $ctxid = $syscontext->id; 2355 2356 $u1 = $dg->create_user(); 2357 2358 // Setting up the framework. 2359 $f1 = $lpg->create_framework(); 2360 $c1 = $lpg->create_competency(array('competencyframeworkid' => $f1->get('id'))); 2361 $c1a = $lpg->create_competency(array('competencyframeworkid' => $f1->get('id'), 'parentid' => $c1->get('id'))); 2362 $c1b = $lpg->create_competency(array('competencyframeworkid' => $f1->get('id'), 'parentid' => $c1->get('id'))); 2363 $c2 = $lpg->create_competency(array('competencyframeworkid' => $f1->get('id'))); 2364 $c2a = $lpg->create_competency(array('competencyframeworkid' => $f1->get('id'), 'parentid' => $c2->get('id'))); 2365 $c3 = $lpg->create_competency(array('competencyframeworkid' => $f1->get('id'))); 2366 $c3a = $lpg->create_competency(array('competencyframeworkid' => $f1->get('id'), 'parentid' => $c3->get('id'))); 2367 $c4 = $lpg->create_competency(array('competencyframeworkid' => $f1->get('id'))); 2368 $c4a = $lpg->create_competency(array('competencyframeworkid' => $f1->get('id'), 'parentid' => $c4->get('id'))); 2369 $c5 = $lpg->create_competency(array('competencyframeworkid' => $f1->get('id'))); 2370 2371 // Setting up the rules. 2372 $c1->set('ruletype', 'core_competency\\competency_rule_all'); 2373 $c1->set('ruleoutcome', \core_competency\competency::OUTCOME_COMPLETE); 2374 $c1->update(); 2375 $c2->set('ruletype', 'core_competency\\competency_rule_all'); 2376 $c2->set('ruleoutcome', \core_competency\competency::OUTCOME_RECOMMEND); 2377 $c2->update(); 2378 $c3->set('ruletype', 'core_competency\\competency_rule_all'); 2379 $c3->set('ruleoutcome', \core_competency\competency::OUTCOME_EVIDENCE); 2380 $c3->update(); 2381 $c4->set('ruletype', 'core_competency\\competency_rule_all'); 2382 $c4->set('ruleoutcome', \core_competency\competency::OUTCOME_NONE); 2383 $c4->update(); 2384 2385 // Confirm the current data. 2386 $this->assertEquals(0, user_competency::count_records()); 2387 $this->assertEquals(0, evidence::count_records()); 2388 2389 // Let's do this! 2390 // First let's confirm that evidence not marking a completion have no impact. 2391 api::add_evidence($u1->id, $c1a, $ctxid, evidence::ACTION_LOG, 'commentincontext', 'core'); 2392 $uc1a = user_competency::get_record(array('userid' => $u1->id, 'competencyid' => $c1a->get('id'))); 2393 $this->assertSame(null, $uc1a->get('proficiency')); 2394 $this->assertFalse(user_competency::record_exists_select('userid = ? AND competencyid = ?', 2395 array($u1->id, $c1->get('id')))); 2396 2397 // Now let's try complete a competency but the rule won't match (not all children are complete). 2398 // The parent (the thing with the rule) will be created but won't have any evidence attached, and not 2399 // not be marked as completed. 2400 api::add_evidence($u1->id, $c1a, $ctxid, evidence::ACTION_COMPLETE, 'commentincontext', 'core'); 2401 $uc1a = user_competency::get_record(array('userid' => $u1->id, 'competencyid' => $c1a->get('id'))); 2402 $this->assertEquals(true, $uc1a->get('proficiency')); 2403 $uc1 = user_competency::get_record(array('userid' => $u1->id, 'competencyid' =>