Search moodle.org's
Developer Documentation

See Release Notes
Long Term Support Release

  • Bug fixes for general core bugs in 3.9.x will end* 10 May 2021 (12 months).
  • Bug fixes for security issues in 3.9.x will end* 8 May 2023 (36 months).
  • PHP version: minimum PHP 7.2.0 Note: minimum PHP version has increased since Moodle 3.8. PHP 7.3.x and 7.4.x are supported too.

Differences Between: [Versions 39 and 310]

   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   * Unit tests for privacy.
  19   *
  20   * @package   search_simpledb
  21   * @copyright 2018 David MonllaĆ³ {@link http://www.davidmonllao.com}
  22   * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  23   */
  24  
  25  use \search_simpledb\privacy\provider;
  26  use core_privacy\local\request\transform;
  27  use core_privacy\local\request\writer;
  28  
  29  defined('MOODLE_INTERNAL') || die();
  30  
  31  global $CFG;
  32  require_once($CFG->dirroot . '/search/tests/fixtures/testable_core_search.php');
  33  require_once($CFG->dirroot . '/search/tests/fixtures/mock_search_area.php');
  34  
  35  /**
  36   * Unit tests for privacy.
  37   *
  38   * @package   search_simpledb
  39   * @copyright 2018 David MonllaĆ³ {@link http://www.davidmonllao.com}
  40   * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  41   */
  42  class privacy_model_testcase extends \core_privacy\tests\provider_testcase {
  43  
  44      public function setUp() {
  45          global $DB;
  46  
  47          if ($this->requires_manual_index_update()) {
  48              // We need to update fulltext index manually, which requires an alter table statement.
  49              $this->preventResetByRollback();
  50          }
  51  
  52          $this->resetAfterTest();
  53          set_config('enableglobalsearch', true);
  54  
  55          // Inject search_simpledb engine into the testable core search as we need to add the mock
  56          // search component to it.
  57  
  58          $this->engine = new \search_simpledb\engine();
  59          $this->search = testable_core_search::instance($this->engine);
  60          $areaid = \core_search\manager::generate_areaid('core_mocksearch', 'mock_search_area');
  61          $this->search->add_search_area($areaid, new core_mocksearch\search\mock_search_area());
  62  
  63          $this->generator = self::getDataGenerator()->get_plugin_generator('core_search');
  64          $this->generator->setup();
  65  
  66          $this->c1 = $this->getDataGenerator()->create_course();
  67          $this->c2 = $this->getDataGenerator()->create_course();
  68  
  69          $this->c1context = \context_course::instance($this->c1->id);
  70          $this->c2context = \context_course::instance($this->c2->id);
  71  
  72          $this->u1 = $this->getDataGenerator()->create_user();
  73          $this->u2 = $this->getDataGenerator()->create_user();
  74  
  75          $this->getDataGenerator()->enrol_user($this->u1->id, $this->c1->id, 'student');
  76          $this->getDataGenerator()->enrol_user($this->u1->id, $this->c2->id, 'student');
  77          $this->getDataGenerator()->enrol_user($this->u2->id, $this->c1->id, 'student');
  78          $this->getDataGenerator()->enrol_user($this->u2->id, $this->c2->id, 'student');
  79  
  80          $record = (object)[
  81              'userid' => $this->u1->id,
  82              'contextid' => $this->c1context->id,
  83              'title' => 'vi',
  84              'content' => 'va',
  85              'description1' => 'san',
  86              'description2' => 'jose'
  87          ];
  88          $this->generator->create_record($record);
  89          $this->generator->create_record((object)['userid' => $this->u1->id, 'contextid' => $this->c2context->id]);
  90          $this->generator->create_record((object)['userid' => $this->u2->id, 'contextid' => $this->c2context->id]);
  91          $this->generator->create_record((object)['userid' => $this->u2->id, 'contextid' => $this->c1context->id]);
  92          $this->generator->create_record((object)['owneruserid' => $this->u1->id, 'contextid' => $this->c1context->id]);
  93          $this->generator->create_record((object)['owneruserid' => $this->u1->id, 'contextid' => $this->c2context->id]);
  94          $this->generator->create_record((object)['owneruserid' => $this->u2->id, 'contextid' => $this->c1context->id]);
  95          $this->generator->create_record((object)['owneruserid' => $this->u2->id, 'contextid' => $this->c2context->id]);
  96          $this->search->index();
  97  
  98          $this->setAdminUser();
  99      }
 100  
 101      /**
 102       * tearDown
 103       *
 104       * @return void
 105       */
 106      public function tearDown() {
 107          // Call parent tearDown() first.
 108          parent::tearDown();
 109  
 110          // For unit tests before PHP 7, teardown is called even on skip. So only do our teardown if we did setup.
 111          if ($this->generator) {
 112              // Moodle DML freaks out if we don't teardown the temp table after each run.
 113              $this->generator->teardown();
 114              $this->generator = null;
 115          }
 116      }
 117  
 118      /**
 119       * Test fetching contexts for a given user ID.
 120       */
 121      public function test_get_contexts_for_userid() {
 122          // Ensure both contexts are found for both users.
 123          $expected = [$this->c1context->id, $this->c2context->id];
 124          sort($expected);
 125  
 126          // User 1.
 127          $contextlist = provider::get_contexts_for_userid($this->u1->id);
 128          $this->assertCount(2, $contextlist);
 129  
 130          $actual = $contextlist->get_contextids();
 131          sort($actual);
 132          $this->assertEquals($expected, $actual);
 133  
 134          // User 2.
 135          $contextlist = provider::get_contexts_for_userid($this->u2->id);
 136          $this->assertCount(2, $contextlist);
 137  
 138          $actual = $contextlist->get_contextids();
 139          sort($actual);
 140          $this->assertEquals($expected, $actual);
 141      }
 142  
 143      /**
 144       * Test fetching user IDs for a given context.
 145       */
 146      public function test_get_users_in_context() {
 147          $component = 'search_simpledb';
 148  
 149          // Ensure both users are found for both contexts.
 150          $expected = [$this->u1->id, $this->u2->id];
 151          sort($expected);
 152  
 153          // User 1.
 154          $userlist = new \core_privacy\local\request\userlist($this->c1context, $component);
 155          provider::get_users_in_context($userlist);
 156          $this->assertCount(2, $userlist);
 157  
 158          $actual = $userlist->get_userids();
 159          sort($actual);
 160          $this->assertEquals($expected, $actual);
 161  
 162          // User 2.
 163          $userlist = new \core_privacy\local\request\userlist($this->c2context, $component);
 164          provider::get_users_in_context($userlist);
 165          $this->assertCount(2, $userlist);
 166  
 167          $actual = $userlist->get_userids();
 168          sort($actual);
 169          $this->assertEquals($expected, $actual);
 170      }
 171  
 172      /**
 173       * Test export user data.
 174       *
 175       * @return null
 176       */
 177      public function test_export_user_data() {
 178          global $DB;
 179  
 180          $contextlist = new \core_privacy\local\request\approved_contextlist($this->u1, 'search_simpledb',
 181                                                                              [$this->c1context->id]);
 182          provider::export_user_data($contextlist);
 183          $writer = \core_privacy\local\request\writer::with_context($this->c1context);
 184          $this->assertTrue($writer->has_any_data());
 185          $u1c1 = $DB->get_record('search_simpledb_index', ['userid' => $this->u1->id, 'contextid' => $this->c1context->id]);
 186          $data = $writer->get_data([get_string('search', 'search'), $u1c1->docid]);
 187  
 188          $this->assertEquals($this->c1context->get_context_name(true, true), $data->context);
 189          $this->assertEquals('vi', $data->title);
 190          $this->assertEquals('va', $data->content);
 191          $this->assertEquals('san', $data->description1);
 192          $this->assertEquals('jose', $data->description2);
 193      }
 194  
 195      /**
 196       * Test delete search for context.
 197       *
 198       * @return null
 199       */
 200      public function test_delete_data_for_all_users() {
 201          global $DB;
 202  
 203          $this->assertEquals(8, $DB->count_records('search_simpledb_index'));
 204  
 205          provider::delete_data_for_all_users_in_context($this->c1context);
 206          $this->assertEquals(0, $DB->count_records('search_simpledb_index', ['contextid' => $this->c1context->id]));
 207          $this->assertEquals(4, $DB->count_records('search_simpledb_index'));
 208  
 209          $u2context = \context_user::instance($this->u2->id);
 210          provider::delete_data_for_all_users_in_context($u2context);
 211          $this->assertEquals(0, $DB->count_records('search_simpledb_index', ['contextid' => $u2context->id]));
 212          $this->assertEquals(2, $DB->count_records('search_simpledb_index'));
 213      }
 214  
 215      /**
 216       * Test delete search for user.
 217       *
 218       * @return null
 219       */
 220      public function test_delete_data_for_user() {
 221          global $DB;
 222  
 223          $contextlist = new \core_privacy\local\request\approved_contextlist($this->u1, 'search_simpledb',
 224                                                                              [$this->c1context->id]);
 225          provider::delete_data_for_user($contextlist);
 226          $select = 'contextid = :contextid AND (owneruserid = :owneruserid OR userid = :userid)';
 227          $params = ['contextid' => $this->c1context->id, 'owneruserid' => $this->u1->id, 'userid' => $this->u1->id];
 228          $this->assertEquals(0, $DB->count_records_select('search_simpledb_index', $select, $params));
 229          $this->assertEquals(2, $DB->count_records('search_simpledb_index', ['contextid' => $this->c1context->id]));
 230          $this->assertEquals(6, $DB->count_records('search_simpledb_index'));
 231  
 232          $contextlist = new \core_privacy\local\request\approved_contextlist($this->u2, 'search_simpledb',
 233                                                                              [$this->c2context->id]);
 234          provider::delete_data_for_user($contextlist);
 235          $select = 'contextid = :contextid AND (owneruserid = :owneruserid OR userid = :userid)';
 236          $params = ['contextid' => $this->c2context->id, 'owneruserid' => $this->u2->id, 'userid' => $this->u2->id];
 237          $this->assertEquals(0, $DB->count_records_select('search_simpledb_index', $select, $params));
 238          $this->assertEquals(2, $DB->count_records('search_simpledb_index', ['contextid' => $this->c2context->id]));
 239          $this->assertEquals(4, $DB->count_records('search_simpledb_index'));
 240      }
 241  
 242      /**
 243       * Test deleting data for an approved userlist.
 244       */
 245      public function test_delete_data_for_users() {
 246          global $DB;
 247          $component = 'search_simpledb';
 248          $select = 'contextid = :contextid AND (owneruserid = :owneruserid OR userid = :userid)';
 249  
 250          // Ensure expected amount of data for both users exists in each context.
 251          $this->assertEquals(4, $DB->count_records('search_simpledb_index', ['contextid' => $this->c1context->id]));
 252          $this->assertEquals(4, $DB->count_records('search_simpledb_index', ['contextid' => $this->c2context->id]));
 253  
 254          // Delete user 1's data in context 1.
 255          $approveduserids = [$this->u1->id];
 256          $approvedlist = new \core_privacy\local\request\approved_userlist($this->c1context, $component, $approveduserids);
 257          provider::delete_data_for_users($approvedlist);
 258  
 259          $params = ['contextid' => $this->c1context->id, 'owneruserid' => $this->u1->id, 'userid' => $this->u1->id];
 260          $this->assertEquals(0, $DB->count_records_select('search_simpledb_index', $select, $params));
 261  
 262          // Ensure user 2's data in context 1 is retained.
 263          $params = ['contextid' => $this->c1context->id, 'owneruserid' => $this->u2->id, 'userid' => $this->u2->id];
 264          $this->assertEquals(2, $DB->count_records_select('search_simpledb_index', $select, $params));
 265  
 266          // Ensure both users' data in context 2 is retained.
 267          $params = ['contextid' => $this->c2context->id, 'owneruserid' => $this->u1->id, 'userid' => $this->u1->id];
 268          $this->assertEquals(2, $DB->count_records_select('search_simpledb_index', $select, $params));
 269          $params = ['contextid' => $this->c2context->id, 'owneruserid' => $this->u2->id, 'userid' => $this->u2->id];
 270          $this->assertEquals(2, $DB->count_records_select('search_simpledb_index', $select, $params));
 271      }
 272  
 273      /**
 274       * Mssql with fulltext support requires manual updates.
 275       *
 276       * @return bool
 277       */
 278      private function requires_manual_index_update() {
 279          global $DB;
 280          return ($DB->get_dbfamily() === 'mssql' && $DB->is_fulltext_search_supported());
 281      }
 282  }