Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 4.2.x will end 22 April 2024 (12 months).
  • Bug fixes for security issues in 4.2.x will end 7 October 2024 (18 months).
  • PHP version: minimum PHP 8.0.0 Note: minimum PHP version has increased since Moodle 4.1. PHP 8.1.x is supported too.

Differences Between: [Versions 311 and 402] [Versions 400 and 402] [Versions 401 and 402]

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