Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 4.0.x will end 8 May 2023 (12 months).
  • Bug fixes for security issues in 4.0.x will end 13 November 2023 (18 months).
  • PHP version: minimum PHP 7.3.0 Note: the minimum PHP version has increased since Moodle 3.10. PHP 7.4.x is also supported.

Differences Between: [Versions 400 and 402] [Versions 400 and 403]

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