Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 3.11.x will end 14 Nov 2022 (12 months plus 6 months extension).
  • Bug fixes for security issues in 3.11.x will end 13 Nov 2023 (18 months plus 12 months extension).
  • PHP version: minimum PHP 7.3.0 Note: minimum PHP version has increased since Moodle 3.10. PHP 7.4.x is supported too.

Differences Between: [Versions 310 and 311] [Versions 311 and 400] [Versions 311 and 401] [Versions 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' =>