Search moodle.org's
Developer Documentation

  • 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 37 and 311] [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  /**
      18   * IMS Enterprise enrolment tests.
      19   *
      20   * @package    enrol_imsenterprise
      21   * @category   test
      22   * @copyright  2012 David MonllaĆ³
      23   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
      24   */
      25  
      26  defined('MOODLE_INTERNAL') || die();
      27  
      28  global $CFG;
      29  require_once($CFG->dirroot . '/enrol/imsenterprise/locallib.php');
      30  require_once($CFG->dirroot . '/enrol/imsenterprise/lib.php');
      31  
      32  /**
      33   * IMS Enterprise test case
      34   *
      35   * @package    enrol_imsenterprise
      36   * @category   test
      37   * @copyright  2012 David MonllaĆ³
      38   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
      39   */
      40  class enrol_imsenterprise_testcase extends advanced_testcase {
      41  
      42      /**
      43       * @var $imsplugin enrol_imsenterprise_plugin IMS plugin instance.
      44       */
      45      public $imsplugin;
      46  
      47      /**
      48       * Setup required for all tests.
      49       */
      50      protected function setUp(): void {
      51          $this->resetAfterTest(true);
      52          $this->imsplugin = enrol_get_plugin('imsenterprise');
      53          $this->set_test_config();
      54      }
      55  
      56      /**
      57       * With an empty IMS enterprise file
      58       */
      59      public function test_emptyfile() {
      60          global $DB;
      61  
      62          $prevncourses = $DB->count_records('course');
      63          $prevnusers = $DB->count_records('user');
      64  
      65          $this->set_xml_file(false, false);
      66          $this->imsplugin->cron();
      67  
      68          $this->assertEquals($prevncourses, $DB->count_records('course'));
      69          $this->assertEquals($prevnusers, $DB->count_records('user'));
      70      }
      71  
      72      /**
      73       * Existing users are not created again
      74       */
      75      public function test_users_existing() {
      76          global $DB;
      77  
      78          $user1 = $this->getDataGenerator()->create_user();
      79          $user2 = $this->getDataGenerator()->create_user();
      80  
      81          $prevnusers = $DB->count_records('user');
      82  
      83          $users = array($user1, $user2);
      84          $this->set_xml_file($users);
      85          $this->imsplugin->cron();
      86  
      87          $this->assertEquals($prevnusers, $DB->count_records('user'));
      88      }
      89  
      90      /**
      91       * Add new users
      92       */
      93      public function test_users_add() {
      94          global $DB;
      95  
      96          $prevnusers = $DB->count_records('user');
      97  
      98          $user1 = new StdClass();
      99          $user1->recstatus = enrol_imsenterprise_plugin::IMSENTERPRISE_ADD;
     100          $user1->username = 'u1';
     101          $user1->email = 'u1@example.com';
     102          $user1->firstname = 'U';
     103          $user1->lastname = '1';
     104  
     105          $users = array($user1);
     106          $this->set_xml_file($users);
     107          $this->imsplugin->cron();
     108  
     109          $this->assertEquals(($prevnusers + 1), $DB->count_records('user'));
     110      }
     111  
     112      /**
     113       * Add new users and set an auth type
     114       */
     115      public function test_users_add_with_auth() {
     116          global $DB;
     117  
     118          $prevnusers = $DB->count_records('user');
     119  
     120          $user2 = new StdClass();
     121          $user2->recstatus = enrol_imsenterprise_plugin::IMSENTERPRISE_ADD;
     122          $user2->username = 'u2';
     123          $user2->auth = 'cas';
     124          $user2->email = 'u2@u2.org';
     125          $user2->firstname = 'U';
     126          $user2->lastname = '2';
     127  
     128          $users = array($user2);
     129          $this->set_xml_file($users);
     130          $this->imsplugin->cron();
     131  
     132          $dbuser = $DB->get_record('user', array('username' => $user2->username));
     133          // TODO: MDL-15863 this needs more work due to multiauth changes, use first auth for now.
     134          $dbauth = explode(',', $dbuser->auth);
     135          $dbauth = reset($dbauth);
     136  
     137          $this->assertEquals(($prevnusers + 1), $DB->count_records('user'));
     138          $this->assertEquals($dbauth, $user2->auth);
     139      }
     140  
     141  
     142      /**
     143       * Update user
     144       */
     145      public function test_user_update() {
     146          global $DB;
     147  
     148          $user = $this->getDataGenerator()->create_user(array('idnumber' => 'test-update-user'));
     149          $imsuser = new stdClass();
     150          $imsuser->recstatus = enrol_imsenterprise_plugin::IMSENTERPRISE_UPDATE;
     151          // THIS SHOULD WORK, surely?: $imsuser->username = $user->username;
     152          // But this is required...
     153          $imsuser->username = $user->idnumber;
     154          $imsuser->email = 'u3@u3.org';
     155          $imsuser->firstname = 'U';
     156          $imsuser->lastname = '3';
     157  
     158          $this->set_xml_file(array($imsuser));
     159          $this->imsplugin->cron();
     160          $dbuser = $DB->get_record('user', array('id' => $user->id), '*', MUST_EXIST);
     161          $this->assertEquals($imsuser->email, $dbuser->email);
     162          $this->assertEquals($imsuser->firstname, $dbuser->firstname);
     163          $this->assertEquals($imsuser->lastname, $dbuser->lastname);
     164      }
     165  
     166      public function test_user_update_disabled() {
     167          global $DB;
     168  
     169          $this->imsplugin->set_config('imsupdateusers', false);
     170  
     171          $user = $this->getDataGenerator()->create_user(array('idnumber' => 'test-update-user'));
     172          $imsuser = new stdClass();
     173          $imsuser->recstatus = enrol_imsenterprise_plugin::IMSENTERPRISE_UPDATE;
     174          // THIS SHOULD WORK, surely?: $imsuser->username = $user->username;
     175          // But this is required...
     176          $imsuser->username = $user->idnumber;
     177          $imsuser->email = 'u3@u3.org';
     178          $imsuser->firstname = 'U';
     179          $imsuser->lastname = '3';
     180  
     181          $this->set_xml_file(array($imsuser));
     182          $this->imsplugin->cron();
     183  
     184          // Verify no changes have been made.
     185          $dbuser = $DB->get_record('user', array('id' => $user->id), '*', MUST_EXIST);
     186          $this->assertEquals($user->email, $dbuser->email);
     187          $this->assertEquals($user->firstname, $dbuser->firstname);
     188          $this->assertEquals($user->lastname, $dbuser->lastname);
     189      }
     190  
     191      /**
     192       * Delete user
     193       */
     194      public function test_user_delete() {
     195          global $DB;
     196  
     197          $this->imsplugin->set_config('imsdeleteusers', true);
     198          $user = $this->getDataGenerator()->create_user(array('idnumber' => 'test-update-user'));
     199  
     200          $imsuser = new stdClass();
     201          $imsuser->recstatus = enrol_imsenterprise_plugin::IMSENTERPRISE_DELETE;
     202          $imsuser->username = $user->username;
     203          $imsuser->firstname = $user->firstname;
     204          $imsuser->lastname = $user->lastname;
     205          $imsuser->email = $user->email;
     206          $this->set_xml_file(array($imsuser));
     207  
     208          $this->imsplugin->cron();
     209          $this->assertEquals(1, $DB->get_field('user', 'deleted', array('id' => $user->id), MUST_EXIST));
     210      }
     211  
     212      /**
     213       * Delete user disabled
     214       */
     215      public function test_user_delete_disabled() {
     216          global $DB;
     217  
     218          $this->imsplugin->set_config('imsdeleteusers', false);
     219          $user = $this->getDataGenerator()->create_user(array('idnumber' => 'test-update-user'));
     220  
     221          $imsuser = new stdClass();
     222          $imsuser->recstatus = enrol_imsenterprise_plugin::IMSENTERPRISE_DELETE;
     223          $imsuser->username = $user->username;
     224          $imsuser->firstname = $user->firstname;
     225          $imsuser->lastname = $user->lastname;
     226          $imsuser->email = $user->email;
     227          $this->set_xml_file(array($imsuser));
     228  
     229          $this->imsplugin->cron();
     230          $this->assertEquals(0, $DB->get_field('user', 'deleted', array('id' => $user->id), MUST_EXIST));
     231      }
     232  
     233      /**
     234       * Existing courses are not created again
     235       */
     236      public function test_courses_existing() {
     237          global $DB;
     238  
     239          $course1 = $this->getDataGenerator()->create_course(array('idnumber' => 'id1'));
     240          $course2 = $this->getDataGenerator()->create_course(array('idnumber' => 'id2'));
     241  
     242          // Default mapping according to default course attributes - IMS description tags mapping.
     243          $course1->imsshort = $course1->fullname;
     244          $course2->imsshort = $course2->fullname;
     245          unset($course1->category);
     246          unset($course2->category);
     247  
     248          $prevncourses = $DB->count_records('course');
     249  
     250          $courses = array($course1, $course2);
     251          $this->set_xml_file(false, $courses);
     252          $this->imsplugin->cron();
     253  
     254          $this->assertEquals($prevncourses, $DB->count_records('course'));
     255      }
     256  
     257      /**
     258       * Add new courses
     259       */
     260      public function test_courses_add() {
     261          global $DB;
     262  
     263          $prevncourses = $DB->count_records('course');
     264  
     265          $course1 = new StdClass();
     266          $course1->recstatus = enrol_imsenterprise_plugin::IMSENTERPRISE_ADD;
     267          $course1->idnumber = 'id1';
     268          $course1->imsshort = 'id1';
     269          $course1->category[] = 'DEFAULT CATNAME';
     270  
     271          $course2 = new StdClass();
     272          $course2->recstatus = enrol_imsenterprise_plugin::IMSENTERPRISE_ADD;
     273          $course2->idnumber = 'id2';
     274          $course2->imsshort = 'id2';
     275          $course2->category[] = 'DEFAULT CATNAME';
     276  
     277          $courses = array($course1, $course2);
     278          $this->set_xml_file(false, $courses);
     279          $this->imsplugin->cron();
     280  
     281          $this->assertEquals(($prevncourses + 2), $DB->count_records('course'));
     282          $this->assertTrue($DB->record_exists('course', array('idnumber' => $course1->idnumber)));
     283          $this->assertTrue($DB->record_exists('course', array('idnumber' => $course2->idnumber)));
     284      }
     285  
     286      /**
     287       * Verify that courses are not created when createnewcourses
     288       * option is diabled.
     289       */
     290      public function test_courses_add_createnewcourses_disabled() {
     291          global $DB;
     292  
     293          $this->imsplugin->set_config('createnewcourses', false);
     294          $prevncourses = $DB->count_records('course');
     295  
     296          $course1 = new StdClass();
     297          $course1->recstatus = enrol_imsenterprise_plugin::IMSENTERPRISE_ADD;
     298          $course1->idnumber = 'id1';
     299          $course1->imsshort = 'id1';
     300          $course1->category[] = 'DEFAULT CATNAME';
     301  
     302          $course2 = new StdClass();
     303          $course2->recstatus = enrol_imsenterprise_plugin::IMSENTERPRISE_ADD;
     304          $course2->idnumber = 'id2';
     305          $course2->imsshort = 'id2';
     306          $course2->category[] = 'DEFAULT CATNAME';
     307  
     308          $courses = array($course1, $course2);
     309          $this->set_xml_file(false, $courses);
     310          $this->imsplugin->cron();
     311  
     312          $courses = array($course1, $course2);
     313          $this->set_xml_file(false, $courses);
     314          $this->imsplugin->cron();
     315  
     316          // Verify the courses have not ben creased.
     317          $this->assertEquals($prevncourses , $DB->count_records('course'));
     318          $this->assertFalse($DB->record_exists('course', array('idnumber' => $course1->idnumber)));
     319          $this->assertFalse($DB->record_exists('course', array('idnumber' => $course2->idnumber)));
     320      }
     321  
     322      /**
     323       * Test adding a course with no idnumber.
     324       */
     325      public function test_courses_no_idnumber() {
     326          global $DB;
     327  
     328          $prevncourses = $DB->count_records('course');
     329  
     330          $course1 = new StdClass();
     331          $course1->recstatus = enrol_imsenterprise_plugin::IMSENTERPRISE_ADD;
     332          $course1->idnumber = '';
     333          $course1->imsshort = 'id1';
     334          $course1->category[] = 'DEFAULT CATNAME';
     335  
     336          $this->set_xml_file(false, array($course1));
     337          $this->imsplugin->cron();
     338  
     339          // Verify no action.
     340          $this->assertEquals($prevncourses, $DB->count_records('course'));
     341      }
     342  
     343      /**
     344       * Add new course with the truncateidnumber setting.
     345       */
     346      public function test_courses_add_truncate_idnumber() {
     347          global $DB;
     348  
     349          $truncatelength = 4;
     350  
     351          $this->imsplugin->set_config('truncatecoursecodes', $truncatelength);
     352          $prevncourses = $DB->count_records('course');
     353  
     354          $course1 = new StdClass();
     355          $course1->recstatus = enrol_imsenterprise_plugin::IMSENTERPRISE_ADD;
     356          $course1->idnumber = '123456789';
     357          $course1->imsshort = 'id1';
     358          $course1->category[] = 'DEFAULT CATNAME';
     359  
     360          $this->set_xml_file(false, array($course1));
     361          $this->imsplugin->cron();
     362  
     363          // Verify the new course has been added.
     364          $this->assertEquals(($prevncourses + 1), $DB->count_records('course'));
     365  
     366          $truncatedidnumber = substr($course1->idnumber, 0, $truncatelength);
     367  
     368          $this->assertTrue($DB->record_exists('course', array('idnumber' => $truncatedidnumber)));
     369      }
     370  
     371      /**
     372       * Add new course without a category.
     373       */
     374      public function test_course_add_default_category() {
     375          global $DB;
     376  
     377          $this->imsplugin->set_config('createnewcategories', false);
     378  
     379          // Delete the default category, to ensure the plugin handles this gracefully.
     380          $defaultcat = core_course_category::get_default();
     381          $defaultcat->delete_full(false);
     382  
     383          // Create an course with the IMS plugin without a category.
     384          $course1 = new stdClass();
     385          $course1->idnumber = 'id1';
     386          $course1->imsshort = 'id1';
     387          $course1->category[] = '';
     388          $this->set_xml_file(false, array($course1));
     389          $this->imsplugin->cron();
     390  
     391          // Check the course has been created.
     392          $dbcourse = $DB->get_record('course', array('idnumber' => $course1->idnumber), '*', MUST_EXIST);
     393          // Check that it belongs to a category which exists.
     394          $this->assertTrue($DB->record_exists('course_categories', array('id' => $dbcourse->category)));
     395      }
     396  
     397      /**
     398       * Course attributes mapping to IMS enterprise group description tags
     399       */
     400      public function test_courses_attrmapping() {
     401          global $DB;
     402  
     403          // Setting a all = coursecode (idnumber) mapping.
     404          $this->imsplugin->set_config('imscoursemapshortname', 'coursecode');
     405          $this->imsplugin->set_config('imscoursemapfullname', 'coursecode');
     406          $this->imsplugin->set_config('imscoursemapsummary', 'coursecode');
     407  
     408          $course1 = new StdClass();
     409          $course1->recstatus = enrol_imsenterprise_plugin::IMSENTERPRISE_ADD;
     410          $course1->idnumber = 'id1';
     411          $course1->imsshort = 'description_short1';
     412          $course1->imslong = 'description_long';
     413          $course1->imsfull = 'description_full';
     414          $course1->category[] = 'DEFAULT CATNAME';
     415  
     416          $this->set_xml_file(false, array($course1));
     417          $this->imsplugin->cron();
     418  
     419          $dbcourse = $DB->get_record('course', array('idnumber' => $course1->idnumber));
     420          $this->assertFalse(!$dbcourse);
     421          $this->assertEquals($dbcourse->shortname, $course1->idnumber);
     422          $this->assertEquals($dbcourse->fullname, $course1->idnumber);
     423          $this->assertEquals($dbcourse->summary, $course1->idnumber);
     424  
     425          // Setting a mapping using all the description tags.
     426          $this->imsplugin->set_config('imscoursemapshortname', 'short');
     427          $this->imsplugin->set_config('imscoursemapfullname', 'long');
     428          $this->imsplugin->set_config('imscoursemapsummary', 'full');
     429  
     430          $course2 = new StdClass();
     431          $course2->recstatus = enrol_imsenterprise_plugin::IMSENTERPRISE_ADD;
     432          $course2->idnumber = 'id2';
     433          $course2->imsshort = 'description_short2';
     434          $course2->imslong = 'description_long';
     435          $course2->imsfull = 'description_full';
     436          $course2->category[] = 'DEFAULT CATNAME';
     437  
     438          $this->set_xml_file(false, array($course2));
     439          $this->imsplugin->cron();
     440  
     441          $dbcourse = $DB->get_record('course', array('idnumber' => $course2->idnumber));
     442          $this->assertFalse(!$dbcourse);
     443          $this->assertEquals($dbcourse->shortname, $course2->imsshort);
     444          $this->assertEquals($dbcourse->fullname, $course2->imslong);
     445          $this->assertEquals($dbcourse->summary, $course2->imsfull);
     446  
     447          // Setting a mapping where the specified description tags doesn't exist in the XML file (must delegate into idnumber).
     448          $this->imsplugin->set_config('imscoursemapshortname', 'short');
     449          $this->imsplugin->set_config('imscoursemapfullname', 'long');
     450          $this->imsplugin->set_config('imscoursemapsummary', 'full');
     451  
     452          $course3 = new StdClass();
     453          $course3->recstatus = enrol_imsenterprise_plugin::IMSENTERPRISE_ADD;
     454          $course3->idnumber = 'id3';
     455          $course3->imsshort = 'description_short3';
     456          $course3->category[] = 'DEFAULT CATNAME';
     457  
     458          $this->set_xml_file(false, array($course3));
     459          $this->imsplugin->cron();
     460  
     461          $dbcourse = $DB->get_record('course', array('idnumber' => $course3->idnumber));
     462          $this->assertFalse(!$dbcourse);
     463          $this->assertEquals($dbcourse->shortname, $course3->imsshort);
     464          $this->assertEquals($dbcourse->fullname, $course3->idnumber);
     465          $this->assertEquals($dbcourse->summary, $course3->idnumber);
     466  
     467      }
     468  
     469      /**
     470       * Course updates
     471       */
     472      public function test_course_update() {
     473          global $DB;
     474  
     475          $course4 = new StdClass();
     476          $course4->recstatus = enrol_imsenterprise_plugin::IMSENTERPRISE_ADD;
     477          $course4->idnumber = 'id4';
     478          $course4->imsshort = 'id4';
     479          $course4->imsfull = 'id4';
     480          $course4->category[] = 'DEFAULT CATNAME';
     481  
     482          $this->set_xml_file(false, array($course4));
     483          $this->imsplugin->cron();
     484  
     485          $course4u = $DB->get_record('course', array('idnumber' => $course4->idnumber));
     486  
     487          $course4u->recstatus = enrol_imsenterprise_plugin::IMSENTERPRISE_UPDATE;
     488          $course4u->imsshort = 'description_short_updated';
     489          $course4u->imsfull = 'description_full_updated';
     490          unset($course4u->category);
     491  
     492          $this->set_xml_file(false, array($course4u));
     493          $this->imsplugin->cron();
     494  
     495          $dbcourse = $DB->get_record('course', array('idnumber' => $course4->idnumber));
     496          $this->assertFalse(!$dbcourse);
     497          $this->assertEquals($dbcourse->shortname, $course4u->imsshort);
     498          $this->assertEquals($dbcourse->fullname, $course4u->imsfull);
     499      }
     500  
     501      /**
     502       * Course delete. Make it hidden.
     503       */
     504      public function test_course_delete() {
     505          global $DB;
     506  
     507          $course8 = new StdClass();
     508          $course8->recstatus = enrol_imsenterprise_plugin::IMSENTERPRISE_ADD;
     509          $course8->idnumber = 'id8';
     510          $course8->imsshort = 'id8';
     511          $course8->imsfull = 'id8';
     512          $course8->category[] = 'DEFAULT CATNAME';
     513  
     514          $this->set_xml_file(false, array($course8));
     515          $this->imsplugin->cron();
     516  
     517          $course8d = $DB->get_record('course', array('idnumber' => $course8->idnumber));
     518          $this->assertEquals($course8d->visible, 1);
     519  
     520          $course8d->recstatus = enrol_imsenterprise_plugin::IMSENTERPRISE_DELETE;
     521          unset($course8d->category);
     522  
     523          $this->set_xml_file(false, array($course8d));
     524          $this->imsplugin->cron();
     525  
     526          $dbcourse = $DB->get_record('course', array('idnumber' => $course8d->idnumber));
     527          $this->assertFalse(!$dbcourse);
     528          $this->assertEquals($dbcourse->visible, 0);
     529      }
     530  
     531  
     532      /**
     533       * Nested categories with name during course creation
     534       */
     535      public function test_nested_categories() {
     536          global $DB;
     537  
     538          $this->imsplugin->set_config('nestedcategories', true);
     539  
     540          $topcat = 'DEFAULT CATNAME';
     541          $subcat = 'DEFAULT SUB CATNAME';
     542  
     543          $course5 = new StdClass();
     544          $course5->recstatus = enrol_imsenterprise_plugin::IMSENTERPRISE_ADD;
     545          $course5->idnumber = 'id5';
     546          $course5->imsshort = 'description_short';
     547          $course5->imslong = 'description_long';
     548          $course5->imsfull = 'description_full';
     549          $course5->category = array();
     550          $course5->category[] = $topcat;
     551          $course5->category[] = $subcat;
     552  
     553          $this->set_xml_file(false, array($course5));
     554          $this->imsplugin->cron();
     555  
     556          $parentcatid = $DB->get_field('course_categories', 'id', array('name' => $topcat));
     557          $subcatid = $DB->get_field('course_categories', 'id', array('name' => $subcat, 'parent' => $parentcatid));
     558  
     559          $this->assertTrue(isset($subcatid));
     560          $this->assertTrue($subcatid > 0);
     561  
     562          $topcat = 'DEFAULT CATNAME';
     563          $subcat = 'DEFAULT SUB CATNAME TEST2';
     564  
     565          $course6 = new StdClass();
     566          $course6->recstatus = enrol_imsenterprise_plugin::IMSENTERPRISE_ADD;
     567          $course6->idnumber = 'id6';
     568          $course6->imsshort = 'description_short';
     569          $course6->imslong = 'description_long';
     570          $course6->imsfull = 'description_full';
     571          $course6->category = array();
     572          $course6->category[] = $topcat;
     573          $course6->category[] = $subcat;
     574  
     575          $this->set_xml_file(false, array($course6));
     576          $this->imsplugin->cron();
     577  
     578          $parentcatid = $DB->get_field('course_categories', 'id', array('name' => $topcat));
     579          $subcatid = $DB->get_field('course_categories', 'id', array('name' => $subcat, 'parent' => $parentcatid));
     580  
     581          $this->assertTrue(isset($subcatid));
     582          $this->assertTrue($subcatid > 0);
     583      }
     584  
     585  
     586      /**
     587       * Test that duplicate nested categories with name are not created
     588       */
     589      public function test_nested_categories_for_dups() {
     590          global $DB;
     591  
     592          $this->imsplugin->set_config('nestedcategories', true);
     593  
     594          $topcat = 'DEFAULT CATNAME';
     595          $subcat = 'DEFAULT SUB CATNAME DUPTEST';
     596  
     597          $course7 = new StdClass();
     598          $course7->recstatus = enrol_imsenterprise_plugin::IMSENTERPRISE_ADD;
     599          $course7->idnumber = 'id7';
     600          $course7->imsshort = 'description_short';
     601          $course7->imslong = 'description_long';
     602          $course7->imsfull = 'description_full';
     603          $course7->category[] = $topcat;
     604          $course7->category[] = $subcat;
     605  
     606          $this->set_xml_file(false, array($course7));
     607          $this->imsplugin->cron();
     608  
     609          $prevncategories = $DB->count_records('course_categories');
     610  
     611          $course8 = new StdClass();
     612          $course8->recstatus = enrol_imsenterprise_plugin::IMSENTERPRISE_ADD;
     613          $course8->idnumber = 'id8';
     614          $course8->imsshort = 'description_short';
     615          $course8->imslong = 'description_long';
     616          $course8->imsfull = 'description_full';
     617          $course8->category[] = $topcat;
     618          $course8->category[] = $subcat;
     619  
     620          $this->set_xml_file(false, array($course8));
     621          $this->imsplugin->cron();
     622  
     623          $this->assertEquals($prevncategories, $DB->count_records('course_categories'));
     624      }
     625  
     626      /**
     627       * Nested categories with idnumber during course creation
     628       */
     629      public function test_nested_categories_idnumber() {
     630          global $DB;
     631  
     632          $this->imsplugin->set_config('nestedcategories', true);
     633          $this->imsplugin->set_config('categoryidnumber', true);
     634          $this->imsplugin->set_config('categoryseparator', '|');
     635  
     636          $catsep = trim($this->imsplugin->get_config('categoryseparator'));
     637  
     638          $topcatname = 'DEFAULT CATNAME';
     639          $subcatname = 'DEFAULT SUB CATNAME';
     640          $topcatidnumber = '01';
     641          $subcatidnumber = '0101';
     642  
     643          $topcat = $topcatname.$catsep.$topcatidnumber;
     644          $subcat = $subcatname.$catsep.$subcatidnumber;
     645  
     646          $course1 = new StdClass();
     647          $course1->recstatus = enrol_imsenterprise_plugin::IMSENTERPRISE_ADD;
     648          $course1->idnumber = 'id5';
     649          $course1->imsshort = 'description_short';
     650          $course1->imslong = 'description_long';
     651          $course1->imsfull = 'description_full';
     652          $course1->category[] = $topcat;
     653          $course1->category[] = $subcat;
     654  
     655          $this->set_xml_file(false, array($course1));
     656          $this->imsplugin->cron();
     657  
     658          $parentcatid = $DB->get_field('course_categories', 'id', array('idnumber' => $topcatidnumber));
     659          $subcatid = $DB->get_field('course_categories', 'id', array('idnumber' => $subcatidnumber, 'parent' => $parentcatid));
     660  
     661          $this->assertTrue(isset($subcatid));
     662          $this->assertTrue($subcatid > 0);
     663  
     664          // Change the category separator character.
     665          $this->imsplugin->set_config('categoryseparator', ':');
     666  
     667          $catsep = trim($this->imsplugin->get_config('categoryseparator'));
     668  
     669          $topcatname = 'DEFAULT CATNAME';
     670          $subcatname = 'DEFAULT SUB CATNAME TEST2';
     671          $topcatidnumber = '01';
     672          $subcatidnumber = '0102';
     673  
     674          $topcat = $topcatname.$catsep.$topcatidnumber;
     675          $subcat = $subcatname.$catsep.$subcatidnumber;
     676  
     677          $course2 = new StdClass();
     678          $course2->recstatus = enrol_imsenterprise_plugin::IMSENTERPRISE_ADD;
     679          $course2->idnumber = 'id6';
     680          $course2->imsshort = 'description_short';
     681          $course2->imslong = 'description_long';
     682          $course2->imsfull = 'description_full';
     683          $course2->category[] = $topcat;
     684          $course2->category[] = $subcat;
     685  
     686          $this->set_xml_file(false, array($course2));
     687          $this->imsplugin->cron();
     688  
     689          $parentcatid = $DB->get_field('course_categories', 'id', array('idnumber' => $topcatidnumber));
     690          $subcatid = $DB->get_field('course_categories', 'id', array('idnumber' => $subcatidnumber, 'parent' => $parentcatid));
     691  
     692          $this->assertTrue(isset($subcatid));
     693          $this->assertTrue($subcatid > 0);
     694      }
     695  
     696      /**
     697       * Test that duplicate nested categories with idnumber are not created
     698       */
     699      public function test_nested_categories_idnumber_for_dups() {
     700          global $DB;
     701  
     702          $this->imsplugin->set_config('nestedcategories', true);
     703          $this->imsplugin->set_config('categoryidnumber', true);
     704          $this->imsplugin->set_config('categoryseparator', '|');
     705  
     706          $catsep = trim($this->imsplugin->get_config('categoryseparator'));
     707  
     708          $topcatname = 'DEFAULT CATNAME';
     709          $subcatname = 'DEFAULT SUB CATNAME';
     710          $topcatidnumber = '01';
     711          $subcatidnumber = '0101';
     712  
     713          $topcat = $topcatname.$catsep.$topcatidnumber;
     714          $subcat = $subcatname.$catsep.$subcatidnumber;
     715  
     716          $course1 = new StdClass();
     717          $course1->recstatus = enrol_imsenterprise_plugin::IMSENTERPRISE_ADD;
     718          $course1->idnumber = 'id1';
     719          $course1->imsshort = 'description_short';
     720          $course1->imslong = 'description_long';
     721          $course1->imsfull = 'description_full';
     722          $course1->category[] = $topcat;
     723          $course1->category[] = $subcat;
     724  
     725          $this->set_xml_file(false, array($course1));
     726          $this->imsplugin->cron();
     727  
     728          $prevncategories = $DB->count_records('course_categories');
     729  
     730          $course2 = new StdClass();
     731          $course2->recstatus = enrol_imsenterprise_plugin::IMSENTERPRISE_ADD;
     732          $course2->idnumber = 'id2';
     733          $course2->imsshort = 'description_short';
     734          $course2->imslong = 'description_long';
     735          $course2->imsfull = 'description_full';
     736          $course2->category[] = $topcat;
     737          $course2->category[] = $subcat;
     738  
     739          $this->set_xml_file(false, array($course2));
     740          $this->imsplugin->cron();
     741  
     742          $this->assertEquals($prevncategories, $DB->count_records('course_categories'));
     743      }
     744  
     745      /**
     746       * Test that nested categories with idnumber is not created if name is missing
     747       */
     748      public function test_categories_idnumber_missing_name() {
     749          global $DB, $CFG;
     750  
     751          $this->imsplugin->set_config('nestedcategories', true);
     752          $this->imsplugin->set_config('categoryidnumber', true);
     753          $this->imsplugin->set_config('categoryseparator', '|');
     754          $catsep = trim($this->imsplugin->get_config('categoryseparator'));
     755  
     756          $topcatname = 'DEFAULT CATNAME';
     757          $subcatname = '';
     758          $topcatidnumber = '01';
     759          $subcatidnumber = '0101';
     760  
     761          $topcat = $topcatname.$catsep.$topcatidnumber;
     762          $subcat = $subcatname.$catsep.$subcatidnumber;
     763  
     764          $course1 = new StdClass();
     765          $course1->recstatus = enrol_imsenterprise_plugin::IMSENTERPRISE_ADD;
     766          $course1->idnumber = 'id1';
     767          $course1->imsshort = 'description_short';
     768          $course1->imslong = 'description_long';
     769          $course1->imsfull = 'description_full';
     770          $course1->category[] = $topcat;
     771          $course1->category[] = $subcat;
     772  
     773          $this->set_xml_file(false, array($course1));
     774          $this->imsplugin->cron();
     775  
     776          // Check all categories except the last subcategory was created.
     777          $parentcatid = $DB->get_field('course_categories', 'id', array('idnumber' => $topcatidnumber));
     778          $this->assertTrue((boolean)$parentcatid);
     779          $subcatid = $DB->get_field('course_categories', 'id', array('idnumber' => $subcatidnumber, 'parent' => $parentcatid));
     780          $this->assertFalse((boolean)$subcatid);
     781  
     782          // Check course was put in default category.
     783          $defaultcat = core_course_category::get_default();
     784          $dbcourse = $DB->get_record('course', array('idnumber' => $course1->idnumber), '*', MUST_EXIST);
     785          $this->assertEquals($dbcourse->category, $defaultcat->id);
     786  
     787      }
     788  
     789      /**
     790       * Create category with name (nested categories not activated).
     791       */
     792      public function test_create_category_name_no_nested() {
     793          global $DB;
     794  
     795          $course = new StdClass();
     796          $course->recstatus = enrol_imsenterprise_plugin::IMSENTERPRISE_ADD;
     797          $course->idnumber = 'id';
     798          $course->imsshort = 'description_short';
     799          $course->imslong = 'description_long';
     800          $course->imsfull = 'description_full';
     801          $course->category[] = 'CATNAME';
     802  
     803          $this->set_xml_file(false, array($course));
     804          $this->imsplugin->cron();
     805  
     806          $dbcat = $DB->get_record('course_categories', array('name' => $course->category[0]));
     807          $this->assertFalse(!$dbcat);
     808          $this->assertEquals($dbcat->parent, 0);
     809  
     810          $dbcourse = $DB->get_record('course', array('idnumber' => $course->idnumber));
     811          $this->assertFalse(!$dbcourse);
     812          $this->assertEquals($dbcourse->category, $dbcat->id);
     813  
     814      }
     815  
     816      /**
     817       * Find a category with name (nested categories not activated).
     818       */
     819      public function test_find_category_name_no_nested() {
     820          global $DB;
     821  
     822          $cattop = $this->getDataGenerator()->create_category(array('name' => 'CAT-TOP'));
     823          $catsub = $this->getDataGenerator()->create_category(array('name' => 'CAT-SUB', 'parent' => $cattop->id));
     824          $prevcats = $DB->count_records('course_categories');
     825  
     826          $course = new StdClass();
     827          $course->recstatus = enrol_imsenterprise_plugin::IMSENTERPRISE_ADD;
     828          $course->idnumber = 'id';
     829          $course->imsshort = 'description_short';
     830          $course->imslong = 'description_long';
     831          $course->imsfull = 'description_full';
     832          $course->category[] = 'CAT-SUB';
     833  
     834          $this->set_xml_file(false, array($course));
     835          $this->imsplugin->cron();
     836  
     837          $newcats = $DB->count_records('course_categories');
     838  
     839          // Check that no new category was not created.
     840          $this->assertEquals($prevcats, $newcats);
     841  
     842          // Check course is associated to CAT-SUB.
     843          $dbcourse = $DB->get_record('course', array('idnumber' => $course->idnumber));
     844          $this->assertFalse(!$dbcourse);
     845          $this->assertEquals($dbcourse->category, $catsub->id);
     846  
     847      }
     848  
     849      /**
     850       * Create category with idnumber (nested categories not activated).
     851       */
     852      public function test_create_category_idnumber_no_nested() {
     853          global $DB;
     854  
     855          $this->imsplugin->set_config('categoryidnumber', true);
     856          $this->imsplugin->set_config('categoryseparator', '|');
     857          $catsep = trim($this->imsplugin->get_config('categoryseparator'));
     858  
     859          $course = new StdClass();
     860          $course->recstatus = enrol_imsenterprise_plugin::IMSENTERPRISE_ADD;
     861          $course->idnumber = 'id';
     862          $course->imsshort = 'description_short';
     863          $course->imslong = 'description_long';
     864          $course->imsfull = 'description_full';
     865          $course->category[] = 'CATNAME'. $catsep .  'CATIDNUMBER';
     866  
     867          $this->set_xml_file(false, array($course));
     868          $this->imsplugin->cron();
     869  
     870          $dbcat = $DB->get_record('course_categories', array('idnumber' => 'CATIDNUMBER'));
     871          $this->assertFalse(!$dbcat);
     872          $this->assertEquals($dbcat->parent, 0);
     873          $this->assertEquals($dbcat->name, 'CATNAME');
     874  
     875          $dbcourse = $DB->get_record('course', array('idnumber' => $course->idnumber));
     876          $this->assertFalse(!$dbcourse);
     877          $this->assertEquals($dbcourse->category, $dbcat->id);
     878  
     879      }
     880  
     881      /**
     882       * Find a category with idnumber (nested categories not activated).
     883       */
     884      public function test_find_category_idnumber_no_nested() {
     885          global $DB;
     886  
     887          $this->imsplugin->set_config('categoryidnumber', true);
     888          $this->imsplugin->set_config('categoryseparator', '|');
     889          $catsep = trim($this->imsplugin->get_config('categoryseparator'));
     890  
     891          $topcatname = 'CAT-TOP';
     892          $subcatname = 'CAT-SUB';
     893          $topcatidnumber = 'ID-TOP';
     894          $subcatidnumber = 'ID-SUB';
     895  
     896          $cattop = $this->getDataGenerator()->create_category(array('name' => $topcatname, 'idnumber' => $topcatidnumber));
     897          $catsub = $this->getDataGenerator()->create_category(array('name' => $subcatname, 'idnumber' => $subcatidnumber,
     898                  'parent' => $cattop->id));
     899          $prevcats = $DB->count_records('course_categories');
     900  
     901          $course = new StdClass();
     902          $course->recstatus = enrol_imsenterprise_plugin::IMSENTERPRISE_ADD;
     903          $course->idnumber = 'id';
     904          $course->imsshort = 'description_short';
     905          $course->imslong = 'description_long';
     906          $course->imsfull = 'description_full';
     907          $course->category[] = $subcatname . $catsep . $subcatidnumber;
     908  
     909          $this->set_xml_file(false, array($course));
     910          $this->imsplugin->cron();
     911  
     912          $newcats = $DB->count_records('course_categories');
     913  
     914          // Check that no new category was not created.
     915          $this->assertEquals($prevcats, $newcats);
     916  
     917          $dbcourse = $DB->get_record('course', array('idnumber' => $course->idnumber));
     918          $this->assertFalse(!$dbcourse);
     919          $this->assertEquals($dbcourse->category, $catsub->id);
     920  
     921      }
     922  
     923      /**
     924       * Test that category with idnumber is not created if name is missing (nested categories not activated).
     925       */
     926      public function test_category_idnumber_missing_name_no_nested() {
     927          global $DB;
     928  
     929          $this->imsplugin->set_config('categoryidnumber', true);
     930          $this->imsplugin->set_config('categoryseparator', '|');
     931          $catsep = trim($this->imsplugin->get_config('categoryseparator'));
     932  
     933          $catidnumber = '01';
     934  
     935          $course = new StdClass();
     936          $course->recstatus = enrol_imsenterprise_plugin::IMSENTERPRISE_ADD;
     937          $course->idnumber = 'id1';
     938          $course->imsshort = 'description_short';
     939          $course->imslong = 'description_long';
     940          $course->imsfull = 'description_full';
     941          $course->category[] = '' . $catsep . $catidnumber;
     942  
     943          $this->set_xml_file(false, array($course));
     944          $this->imsplugin->cron();
     945  
     946          // Check category was not created.
     947          $catid = $DB->get_record('course_categories', array('idnumber' => $catidnumber));
     948          $this->assertFalse($catid);
     949  
     950          // Check course was put in default category.
     951          $defaultcat = core_course_category::get_default();
     952          $dbcourse = $DB->get_record('course', array('idnumber' => $course->idnumber), '*', MUST_EXIST);
     953          $this->assertEquals($dbcourse->category, $defaultcat->id);
     954  
     955      }
     956  
     957      /**
     958       * Sets the plugin configuration for testing
     959       */
     960      public function set_test_config() {
     961          $this->imsplugin->set_config('mailadmins', false);
     962          $this->imsplugin->set_config('prev_path', '');
     963          $this->imsplugin->set_config('createnewusers', true);
     964          $this->imsplugin->set_config('imsupdateusers', true);
     965          $this->imsplugin->set_config('createnewcourses', true);
     966          $this->imsplugin->set_config('updatecourses', true);
     967          $this->imsplugin->set_config('createnewcategories', true);
     968          $this->imsplugin->set_config('categoryseparator', '');
     969          $this->imsplugin->set_config('categoryidnumber', false);
     970          $this->imsplugin->set_config('nestedcategories', false);
     971      }
     972  
     973      /**
     974       * Creates an IMS enterprise XML file and adds it's path to config settings.
     975       *
     976       * @param bool|array $users false or array of users StdClass
     977       * @param bool|array $courses false or of courses StdClass
     978       */
     979      public function set_xml_file($users = false, $courses = false) {
     980  
     981          $xmlcontent = '<enterprise>';
     982  
     983          // Users.
     984          if (!empty($users)) {
     985              foreach ($users as $user) {
     986                  $xmlcontent .= '
     987    <person';
     988  
     989                  // Optional recstatus (1=add, 2=update, 3=delete).
     990                  if (!empty($user->recstatus)) {
     991                      $xmlcontent .= ' recstatus="'.$user->recstatus.'"';
     992                  }
     993  
     994                  $xmlcontent .= '>
     995      <sourcedid>
     996        <source>TestSource</source>
     997        <id>'.$user->username.'</id>
     998      </sourcedid>
     999      <userid';
    1000  
    1001                  // Optional authentication type.
    1002                  if (!empty($user->auth)) {
    1003                      $xmlcontent .= ' authenticationtype="'.$user->auth.'"';
    1004                  }
    1005  
    1006                  $xmlcontent .= '>'.$user->username.'</userid>
    1007      <name>
    1008        <fn>'.$user->firstname.' '.$user->lastname.'</fn>
    1009        <n>
    1010          <family>'.$user->lastname.'</family>
    1011          <given>'.$user->firstname.'</given>
    1012        </n>
    1013      </name>
    1014      <email>'.$user->email.'</email>
    1015    </person>';
    1016              }
    1017          }
    1018  
    1019          // Courses.
    1020          // Mapping based on default course attributes - IMS group tags mapping.
    1021          if (!empty($courses)) {
    1022              foreach ($courses as $course) {
    1023  
    1024                  $xmlcontent .= '
    1025    <group';
    1026  
    1027                  // Optional recstatus (1=add, 2=update, 3=delete).
    1028                  if (!empty($course->recstatus)) {
    1029                      $xmlcontent .= ' recstatus="'.$course->recstatus.'"';
    1030                  }
    1031  
    1032                  $xmlcontent .= '>
    1033      <sourcedid>
    1034        <source>TestSource</source>
    1035        <id>'.$course->idnumber.'</id>
    1036      </sourcedid>
    1037      <description>';
    1038  
    1039                  // Optional to test course attributes mappings.
    1040                  if (!empty($course->imsshort)) {
    1041                      $xmlcontent .= '
    1042        <short>'.$course->imsshort.'</short>';
    1043                  }
    1044  
    1045                  // Optional to test course attributes mappings.
    1046                  if (!empty($course->imslong)) {
    1047                      $xmlcontent .= '
    1048        <long>'.$course->imslong.'</long>';
    1049                  }
    1050  
    1051                  // Optional to test course attributes mappings.
    1052                  if (!empty($course->imsfull)) {
    1053                      $xmlcontent .= '
    1054        <full>'.$course->imsfull.'</full>';
    1055                  }
    1056  
    1057                  // The orgunit tag value is used by moodle as category name.
    1058                  $xmlcontent .= '
    1059      </description>
    1060      <org>';
    1061                  // Optional category name.
    1062                  if (isset($course->category) && !empty($course->category)) {
    1063                      foreach ($course->category as $category) {
    1064                          $xmlcontent .= '
    1065        <orgunit>'.$category.'</orgunit>';
    1066                      }
    1067                  }
    1068  
    1069                  $xmlcontent .= '
    1070      </org>
    1071    </group>';
    1072              }
    1073          }
    1074  
    1075          $xmlcontent .= '
    1076  </enterprise>';
    1077  
    1078          // Creating the XML file.
    1079          $filename = 'ims_' . rand(1000, 9999) . '.xml';
    1080          $tmpdir = make_temp_directory('enrol_imsenterprise');
    1081          $xmlfilepath = $tmpdir . '/' . $filename;
    1082          file_put_contents($xmlfilepath, $xmlcontent);
    1083  
    1084          // Setting the file path in CFG.
    1085          $this->imsplugin->set_config('imsfilelocation', $xmlfilepath);
    1086      }
    1087  
    1088      /**
    1089       * IMS Enterprise enrolment task test.
    1090       */
    1091      public function test_imsenterprise_cron_task() {
    1092          global $DB;
    1093          $prevnusers = $DB->count_records('user');
    1094  
    1095          $user1 = new StdClass();
    1096          $user1->username = 'u1';
    1097          $user1->email = 'u1@example.com';
    1098          $user1->firstname = 'U';
    1099          $user1->lastname = '1';
    1100  
    1101          $users = array($user1);
    1102          $this->set_xml_file($users);
    1103  
    1104          $task = new enrol_imsenterprise\task\cron_task();
    1105          $task->execute();
    1106  
    1107          $this->assertEquals(($prevnusers + 1), $DB->count_records('user'));
    1108      }
    1109  }