| 1 | efrain | 1 | <?php
 | 
        
           |  |  | 2 | // This file is part of Moodle - http://moodle.org/
 | 
        
           |  |  | 3 | //
 | 
        
           |  |  | 4 | // Moodle is free software: you can redistribute it and/or modify
 | 
        
           |  |  | 5 | // it under the terms of the GNU General Public License as published by
 | 
        
           |  |  | 6 | // the Free Software Foundation, either version 3 of the License, or
 | 
        
           |  |  | 7 | // (at your option) any later version.
 | 
        
           |  |  | 8 | //
 | 
        
           |  |  | 9 | // Moodle is distributed in the hope that it will be useful,
 | 
        
           |  |  | 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
        
           |  |  | 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
        
           |  |  | 12 | // GNU General Public License for more details.
 | 
        
           |  |  | 13 | //
 | 
        
           |  |  | 14 | // You should have received a copy of the GNU General Public License
 | 
        
           |  |  | 15 | // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
 | 
        
           |  |  | 16 |   | 
        
           |  |  | 17 | namespace qbank_bulkmove;
 | 
        
           |  |  | 18 |   | 
        
           |  |  | 19 | use core_question\local\bank\question_edit_contexts;
 | 
        
           |  |  | 20 |   | 
        
           |  |  | 21 | defined('MOODLE_INTERNAL') || die();
 | 
        
           |  |  | 22 |   | 
        
           |  |  | 23 | global $CFG;
 | 
        
           |  |  | 24 | require_once($CFG->dirroot . '/question/editlib.php');
 | 
        
           |  |  | 25 |   | 
        
           |  |  | 26 | /**
 | 
        
           |  |  | 27 |  * Bulk move helper tests.
 | 
        
           |  |  | 28 |  *
 | 
        
           |  |  | 29 |  * @package    qbank_bulkmove
 | 
        
           |  |  | 30 |  * @copyright  2021 Catalyst IT Australia Pty Ltd
 | 
        
           |  |  | 31 |  * @author     Safat Shahin <safatshahin@catalyst-au.net>
 | 
        
           |  |  | 32 |  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 | 
        
           |  |  | 33 |  * @coversDefaultClass \qbank_bulkmove\helper
 | 
        
           |  |  | 34 |  */
 | 
        
           |  |  | 35 | class helper_test extends \advanced_testcase {
 | 
        
           |  |  | 36 |   | 
        
           |  |  | 37 |     /**
 | 
        
           |  |  | 38 |      * @var false|object|\stdClass|null $cat
 | 
        
           |  |  | 39 |      */
 | 
        
           |  |  | 40 |     protected $cat;
 | 
        
           |  |  | 41 |   | 
        
           |  |  | 42 |     /**
 | 
        
           |  |  | 43 |      * @var \stdClass $questiondata1
 | 
        
           |  |  | 44 |      */
 | 
        
           |  |  | 45 |     protected $questiondata1;
 | 
        
           |  |  | 46 |   | 
        
           |  |  | 47 |     /**
 | 
        
           |  |  | 48 |      * @var \stdClass $questiondata2
 | 
        
           |  |  | 49 |      */
 | 
        
           |  |  | 50 |     protected $questiondata2;
 | 
        
           |  |  | 51 |   | 
        
           |  |  | 52 |     /**
 | 
        
           |  |  | 53 |      * @var bool|\context|\context_course $context
 | 
        
           |  |  | 54 |      */
 | 
        
           |  |  | 55 |     protected $context;
 | 
        
           |  |  | 56 |   | 
        
           |  |  | 57 |     /**
 | 
        
           |  |  | 58 |      * @var \core_question\local\bank\question_edit_contexts $contexts
 | 
        
           |  |  | 59 |      */
 | 
        
           |  |  | 60 |     protected $contexts;
 | 
        
           |  |  | 61 |   | 
        
           |  |  | 62 |     /**
 | 
        
           |  |  | 63 |      * @var \stdClass $course
 | 
        
           |  |  | 64 |      */
 | 
        
           |  |  | 65 |     protected $course;
 | 
        
           |  |  | 66 |   | 
        
           |  |  | 67 |     /**
 | 
        
           |  |  | 68 |      * @var array $rawdata
 | 
        
           |  |  | 69 |      */
 | 
        
           |  |  | 70 |     protected $rawdata;
 | 
        
           |  |  | 71 |   | 
        
           |  |  | 72 |     /**
 | 
        
           |  |  | 73 |      * @var object $secondcategory
 | 
        
           |  |  | 74 |      */
 | 
        
           |  |  | 75 |     protected $secondcategory;
 | 
        
           |  |  | 76 |   | 
        
           |  |  | 77 |     /**
 | 
        
           |  |  | 78 |      * Setup the test.
 | 
        
           |  |  | 79 |      */
 | 
        
           |  |  | 80 |     protected function helper_setup(): void {
 | 
        
           |  |  | 81 |         $this->resetAfterTest();
 | 
        
           |  |  | 82 |         $this->setAdminUser();
 | 
        
           |  |  | 83 |         $generator = $this->getDataGenerator();
 | 
        
           |  |  | 84 |         /** @var \core_question_generator $questiongenerator */
 | 
        
           |  |  | 85 |         $questiongenerator = $generator->get_plugin_generator('core_question');
 | 
        
           |  |  | 86 |   | 
        
           |  |  | 87 |         // Create a course.
 | 
        
           |  |  | 88 |         $this->course = $generator->create_course();
 | 
        
           |  |  | 89 |         $this->context = \context_course::instance($this->course->id);
 | 
        
           |  |  | 90 |   | 
        
           |  |  | 91 |         // Create a question in the default category.
 | 
        
           |  |  | 92 |         $this->contexts = new question_edit_contexts($this->context);
 | 
        
           |  |  | 93 |         $this->cat = question_make_default_categories($this->contexts->all());
 | 
        
           |  |  | 94 |         $this->questiondata1 = $questiongenerator->create_question('numerical', null,
 | 
        
           |  |  | 95 |             ['name' => 'Example question', 'category' => $this->cat->id]);
 | 
        
           |  |  | 96 |   | 
        
           |  |  | 97 |         // Create a second category to move questions.
 | 
        
           |  |  | 98 |         $this->secondcategory = $questiongenerator->create_question_category(['contextid' => $this->context->id,
 | 
        
           |  |  | 99 |             'parent' => $this->cat->id]);
 | 
        
           |  |  | 100 |   | 
        
           |  |  | 101 |         // Ensure the question is not in the cache.
 | 
        
           |  |  | 102 |         $cache = \cache::make('core', 'questiondata');
 | 
        
           |  |  | 103 |         $cache->delete($this->questiondata1->id);
 | 
        
           |  |  | 104 |   | 
        
           |  |  | 105 |         $this->questiondata2 = $questiongenerator->create_question('numerical', null,
 | 
        
           |  |  | 106 |             ['name' => 'Example question second', 'category' => $this->cat->id]);
 | 
        
           |  |  | 107 |   | 
        
           |  |  | 108 |         // Ensure the question is not in the cache.
 | 
        
           |  |  | 109 |         $cache = \cache::make('core', 'questiondata');
 | 
        
           |  |  | 110 |         $cache->delete($this->questiondata2->id);
 | 
        
           |  |  | 111 |   | 
        
           |  |  | 112 |         // Posted raw data.
 | 
        
           |  |  | 113 |         $this->rawdata = [
 | 
        
           |  |  | 114 |             'courseid' => $this->course->id,
 | 
        
           |  |  | 115 |             'cat' => "{$this->cat->id},{$this->context->id}",
 | 
        
           |  |  | 116 |             'qpage' => '0',
 | 
        
           |  |  | 117 |             "q{$this->questiondata1->id}" => '1',
 | 
        
           |  |  | 118 |             "q{$this->questiondata2->id}" => '1',
 | 
        
           |  |  | 119 |             'move' => 'Move to'
 | 
        
           |  |  | 120 |         ];
 | 
        
           |  |  | 121 |     }
 | 
        
           |  |  | 122 |   | 
        
           |  |  | 123 |     /**
 | 
        
           |  |  | 124 |      * Count how many questions in the list belong to the given category.
 | 
        
           |  |  | 125 |      *
 | 
        
           |  |  | 126 |      * @param string $categoryid a category id
 | 
        
           |  |  | 127 |      * @param array $questionids list of question ids
 | 
        
           |  |  | 128 |      * @return int
 | 
        
           |  |  | 129 |      */
 | 
        
           |  |  | 130 |     private function count_category_questions(string $categoryid, array $questionids): int {
 | 
        
           |  |  | 131 |         global $DB;
 | 
        
           |  |  | 132 |         $this->assertNotEmpty($questionids);
 | 
        
           |  |  | 133 |         list($insql, $inparams) = $DB->get_in_or_equal($questionids, SQL_PARAMS_NAMED);
 | 
        
           |  |  | 134 |         $sql = "SELECT COUNT(q.id)
 | 
        
           |  |  | 135 |                   FROM {question} q
 | 
        
           |  |  | 136 |                   JOIN {question_versions} qv ON qv.questionid = q.id
 | 
        
           |  |  | 137 |                   JOIN {question_bank_entries} qbe ON qbe.id = qv.questionbankentryid
 | 
        
           |  |  | 138 |                   JOIN {question_categories} qc ON qc.id = qbe.questioncategoryid
 | 
        
           |  |  | 139 |                  WHERE qc.id = :categoryid
 | 
        
           |  |  | 140 |                    AND q.id $insql";
 | 
        
           |  |  | 141 |   | 
        
           |  |  | 142 |         return $DB->count_records_sql($sql, array_merge(['categoryid' => $categoryid], $inparams));
 | 
        
           |  |  | 143 |     }
 | 
        
           |  |  | 144 |   | 
        
           |  |  | 145 |     /**
 | 
        
           |  |  | 146 |      * Assert that the given category contains following questions
 | 
        
           |  |  | 147 |      *
 | 
        
           |  |  | 148 |      * @param string $categoryid a category id
 | 
        
           |  |  | 149 |      * @param array $questionids list of question ids
 | 
        
           |  |  | 150 |      * @return void
 | 
        
           |  |  | 151 |      */
 | 
        
           |  |  | 152 |     protected function assert_category_contains_questions(string $categoryid, array $questionids) {
 | 
        
           |  |  | 153 |         // The category need to contain all the questions.
 | 
        
           |  |  | 154 |         $this->assertEquals(count($questionids), $this->count_category_questions($categoryid, $questionids));
 | 
        
           |  |  | 155 |     }
 | 
        
           |  |  | 156 |   | 
        
           |  |  | 157 |     /**
 | 
        
           |  |  | 158 |      * Assert that the given category does not contain following questions
 | 
        
           |  |  | 159 |      *
 | 
        
           |  |  | 160 |      * @param string $categoryid a category id
 | 
        
           |  |  | 161 |      * @param array $questionids list of question ids
 | 
        
           |  |  | 162 |      * @return void
 | 
        
           |  |  | 163 |      */
 | 
        
           |  |  | 164 |     protected function assert_category_does_not_contain_questions(string $categoryid, array $questionids) {
 | 
        
           |  |  | 165 |         // The category does not contain any question.
 | 
        
           |  |  | 166 |         $this->assertEquals(0, $this->count_category_questions($categoryid, $questionids));
 | 
        
           |  |  | 167 |     }
 | 
        
           |  |  | 168 |   | 
        
           |  |  | 169 |     /**
 | 
        
           |  |  | 170 |      * Test bulk move of questions.
 | 
        
           |  |  | 171 |      *
 | 
        
           |  |  | 172 |      * @covers ::bulk_move_questions
 | 
        
           |  |  | 173 |      */
 | 
        
           | 11 | efrain | 174 |     public function test_bulk_move_questions(): void {
 | 
        
           | 1 | efrain | 175 |         global $DB;
 | 
        
           |  |  | 176 |         $this->helper_setup();
 | 
        
           |  |  | 177 |   | 
        
           |  |  | 178 |         // Get the processed question ids.
 | 
        
           |  |  | 179 |         $questionlist = $this->process_question_ids_test();
 | 
        
           |  |  | 180 |         $questionids = array_map('intval', explode(',', $questionlist));
 | 
        
           |  |  | 181 |   | 
        
           |  |  | 182 |         // Verify that the questions are available in the current view.
 | 
        
           |  |  | 183 |         $this->assert_category_contains_questions($this->cat->id, $questionids);
 | 
        
           |  |  | 184 |         helper::bulk_move_questions($questionlist, $this->secondcategory);
 | 
        
           |  |  | 185 |   | 
        
           |  |  | 186 |         // Verify the questions are not in the current category.
 | 
        
           |  |  | 187 |         $this->assert_category_does_not_contain_questions($this->cat->id, $questionids);
 | 
        
           |  |  | 188 |   | 
        
           |  |  | 189 |         // Verify the questions are in the new category.
 | 
        
           |  |  | 190 |         $this->assert_category_contains_questions($this->secondcategory->id, $questionids);
 | 
        
           |  |  | 191 |     }
 | 
        
           |  |  | 192 |   | 
        
           |  |  | 193 |     /**
 | 
        
           |  |  | 194 |      * Test the question processing and return the question list.
 | 
        
           |  |  | 195 |      *
 | 
        
           |  |  | 196 |      * @return mixed
 | 
        
           |  |  | 197 |      * @covers ::process_question_ids
 | 
        
           |  |  | 198 |      */
 | 
        
           |  |  | 199 |     protected function process_question_ids_test() {
 | 
        
           |  |  | 200 |         // Test the raw data processing.
 | 
        
           |  |  | 201 |         list($questionids, $questionlist) = helper::process_question_ids($this->rawdata);
 | 
        
           |  |  | 202 |         $this->assertEquals([$this->questiondata1->id, $this->questiondata2->id], $questionids);
 | 
        
           |  |  | 203 |         $this->assertEquals("{$this->questiondata1->id},{$this->questiondata2->id}", $questionlist);
 | 
        
           |  |  | 204 |         return $questionlist;
 | 
        
           |  |  | 205 |     }
 | 
        
           |  |  | 206 |   | 
        
           |  |  | 207 |     /**
 | 
        
           |  |  | 208 |      * Test the question displaydata.
 | 
        
           |  |  | 209 |      *
 | 
        
           |  |  | 210 |      * @covers ::get_displaydata
 | 
        
           |  |  | 211 |      */
 | 
        
           | 11 | efrain | 212 |     public function test_get_displaydata(): void {
 | 
        
           | 1 | efrain | 213 |         $this->helper_setup();
 | 
        
           |  |  | 214 |         $coursecontext = \context_course::instance($this->course->id);
 | 
        
           |  |  | 215 |         $contexts = new question_edit_contexts($coursecontext);
 | 
        
           |  |  | 216 |         $addcontexts = $contexts->having_cap('moodle/question:add');
 | 
        
           |  |  | 217 |         $url = new \moodle_url('/question/bank/bulkmove/move.php');
 | 
        
           |  |  | 218 |         $displaydata = \qbank_bulkmove\helper::get_displaydata($addcontexts, $url, $url);
 | 
        
           |  |  | 219 |         $this->assertStringContainsString('Test question category 1', $displaydata['categorydropdown']);
 | 
        
           |  |  | 220 |         $this->assertStringContainsString('Default for Category 1', $displaydata['categorydropdown']);
 | 
        
           |  |  | 221 |         $this->assertEquals($url, $displaydata ['moveurl']);
 | 
        
           |  |  | 222 |         $this->assertEquals($url, $displaydata ['returnurl']);
 | 
        
           |  |  | 223 |     }
 | 
        
           |  |  | 224 | }
 |