MDL-51629 mod_survey: New WS mod_survey_submit_answers
[moodle.git] / mod / survey / tests / externallib_test.php
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/>.
17 /**
18  * Survey module external functions tests
19  *
20  * @package    mod_survey
21  * @category   external
22  * @copyright  2015 Juan Leyva <juan@moodle.com>
23  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
24  * @since      Moodle 3.0
25  */
27 defined('MOODLE_INTERNAL') || die();
29 global $CFG;
31 require_once($CFG->dirroot . '/webservice/tests/helpers.php');
32 require_once($CFG->dirroot . '/mod/survey/lib.php');
34 /**
35  * Survey module external functions tests
36  *
37  * @package    mod_survey
38  * @category   external
39  * @copyright  2015 Juan Leyva <juan@moodle.com>
40  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
41  * @since      Moodle 3.0
42  */
43 class mod_survey_external_testcase extends externallib_advanced_testcase {
45     /**
46      * Set up for every test
47      */
48     public function setUp() {
49         global $DB;
50         $this->resetAfterTest();
51         $this->setAdminUser();
53         // Setup test data.
54         $this->course = $this->getDataGenerator()->create_course();
55         $this->survey = $this->getDataGenerator()->create_module('survey', array('course' => $this->course->id));
56         $this->context = context_module::instance($this->survey->cmid);
57         $this->cm = get_coursemodule_from_instance('survey', $this->survey->id);
59         // Create users.
60         $this->student = self::getDataGenerator()->create_user();
61         $this->teacher = self::getDataGenerator()->create_user();
63         // Users enrolments.
64         $this->studentrole = $DB->get_record('role', array('shortname' => 'student'));
65         $this->teacherrole = $DB->get_record('role', array('shortname' => 'editingteacher'));
66         $this->getDataGenerator()->enrol_user($this->student->id, $this->course->id, $this->studentrole->id, 'manual');
67         $this->getDataGenerator()->enrol_user($this->teacher->id, $this->course->id, $this->teacherrole->id, 'manual');
68     }
71     /*
72      * Test get surveys by courses
73      */
74     public function test_mod_survey_get_surveys_by_courses() {
75         global $DB;
77         // Create additional course.
78         $course2 = self::getDataGenerator()->create_course();
80         // Second survey.
81         $record = new stdClass();
82         $record->course = $course2->id;
83         $survey2 = self::getDataGenerator()->create_module('survey', $record);
84         // Force empty intro.
85         $DB->set_field('survey', 'intro', '', array('id' => $survey2->id));
87         // Execute real Moodle enrolment as we'll call unenrol() method on the instance later.
88         $enrol = enrol_get_plugin('manual');
89         $enrolinstances = enrol_get_instances($course2->id, true);
90         foreach ($enrolinstances as $courseenrolinstance) {
91             if ($courseenrolinstance->enrol == "manual") {
92                 $instance2 = $courseenrolinstance;
93                 break;
94             }
95         }
96         $enrol->enrol_user($instance2, $this->student->id, $this->studentrole->id);
98         self::setUser($this->student);
100         $returndescription = mod_survey_external::get_surveys_by_courses_returns();
102         // Create what we expect to be returned when querying the two courses.
103         // First for the student user.
104         $expectedfields = array('id', 'coursemodule', 'course', 'name', 'intro', 'introformat', 'template', 'days', 'questions',
105                                     'surveydone');
107         // Add expected coursemodule and data.
108         $survey1 = $this->survey;
109         $survey1->coursemodule = $survey1->cmid;
110         $survey1->introformat = 1;
111         $survey1->surveydone = 0;
112         $survey1->section = 0;
113         $survey1->visible = true;
114         $survey1->groupmode = 0;
115         $survey1->groupingid = 0;
117         $survey2->coursemodule = $survey2->cmid;
118         $survey2->introformat = 1;
119         $survey2->surveydone = 0;
120         $survey2->section = 0;
121         $survey2->visible = true;
122         $survey2->groupmode = 0;
123         $survey2->groupingid = 0;
124         $tempo = $DB->get_field("survey", "intro", array("id" => $survey2->template));
125         $survey2->intro = nl2br(get_string($tempo, "survey"));
127         foreach ($expectedfields as $field) {
128             $expected1[$field] = $survey1->{$field};
129             $expected2[$field] = $survey2->{$field};
130         }
132         $expectedsurveys = array($expected2, $expected1);
134         // Call the external function passing course ids.
135         $result = mod_survey_external::get_surveys_by_courses(array($course2->id, $this->course->id));
136         $result = external_api::clean_returnvalue($returndescription, $result);
138         $this->assertEquals($expectedsurveys, $result['surveys']);
139         $this->assertCount(0, $result['warnings']);
141         // Call the external function without passing course id.
142         $result = mod_survey_external::get_surveys_by_courses();
143         $result = external_api::clean_returnvalue($returndescription, $result);
144         $this->assertEquals($expectedsurveys, $result['surveys']);
145         $this->assertCount(0, $result['warnings']);
147         // Unenrol user from second course and alter expected surveys.
148         $enrol->unenrol_user($instance2, $this->student->id);
149         array_shift($expectedsurveys);
151         // Call the external function without passing course id.
152         $result = mod_survey_external::get_surveys_by_courses();
153         $result = external_api::clean_returnvalue($returndescription, $result);
154         $this->assertEquals($expectedsurveys, $result['surveys']);
156         // Call for the second course we unenrolled the user from, expected warning.
157         $result = mod_survey_external::get_surveys_by_courses(array($course2->id));
158         $this->assertCount(1, $result['warnings']);
159         $this->assertEquals('1', $result['warnings'][0]['warningcode']);
160         $this->assertEquals($course2->id, $result['warnings'][0]['itemid']);
162         // Now, try as a teacher for getting all the additional fields.
163         self::setUser($this->teacher);
165         $additionalfields = array('timecreated', 'timemodified', 'section', 'visible', 'groupmode', 'groupingid');
167         foreach ($additionalfields as $field) {
168             $expectedsurveys[0][$field] = $survey1->{$field};
169         }
171         $result = mod_survey_external::get_surveys_by_courses();
172         $result = external_api::clean_returnvalue($returndescription, $result);
173         $this->assertEquals($expectedsurveys, $result['surveys']);
175         // Admin also should get all the information.
176         self::setAdminUser();
178         $result = mod_survey_external::get_surveys_by_courses(array($this->course->id));
179         $result = external_api::clean_returnvalue($returndescription, $result);
180         $this->assertEquals($expectedsurveys, $result['surveys']);
182         // Now, prohibit capabilities.
183         $this->setUser($this->student);
184         $contextcourse1 = context_course::instance($this->course->id);
185         // Prohibit capability = mod/survey:participate on Course1 for students.
186         assign_capability('mod/survey:participate', CAP_PROHIBIT, $this->studentrole->id, $contextcourse1->id);
187         accesslib_clear_all_caches_for_unit_testing();
189         $surveys = mod_survey_external::get_surveys_by_courses(array($this->course->id));
190         $surveys = external_api::clean_returnvalue(mod_survey_external::get_surveys_by_courses_returns(), $surveys);
191         $this->assertFalse(isset($surveys['surveys'][0]['intro']));
192     }
194     /**
195      * Test view_survey
196      */
197     public function test_view_survey() {
198         global $DB;
200         // Test invalid instance id.
201         try {
202             mod_survey_external::view_survey(0);
203             $this->fail('Exception expected due to invalid mod_survey instance id.');
204         } catch (moodle_exception $e) {
205             $this->assertEquals('invalidrecord', $e->errorcode);
206         }
208         // Test not-enrolled user.
209         $usernotenrolled = self::getDataGenerator()->create_user();
210         $this->setUser($usernotenrolled);
211         try {
212             mod_survey_external::view_survey($this->survey->id);
213             $this->fail('Exception expected due to not enrolled user.');
214         } catch (moodle_exception $e) {
215             $this->assertEquals('requireloginerror', $e->errorcode);
216         }
218         // Test user with full capabilities.
219         $this->setUser($this->student);
221         // Trigger and capture the event.
222         $sink = $this->redirectEvents();
224         $result = mod_survey_external::view_survey($this->survey->id);
225         $result = external_api::clean_returnvalue(mod_survey_external::view_survey_returns(), $result);
226         $this->assertTrue($result['status']);
228         $events = $sink->get_events();
229         $this->assertCount(1, $events);
230         $event = array_shift($events);
232         // Checking that the event contains the expected values.
233         $this->assertInstanceOf('\mod_survey\event\course_module_viewed', $event);
234         $this->assertEquals($this->context, $event->get_context());
235         $moodlesurvey = new \moodle_url('/mod/survey/view.php', array('id' => $this->cm->id));
236         $this->assertEquals($moodlesurvey, $event->get_url());
237         $this->assertEventContextNotUsed($event);
238         $this->assertNotEmpty($event->get_name());
240         // Test user with no capabilities.
241         // We need a explicit prohibit since this capability is only defined in authenticated user and guest roles.
242         assign_capability('mod/survey:participate', CAP_PROHIBIT, $this->studentrole->id, $this->context->id);
243         accesslib_clear_all_caches_for_unit_testing();
245         try {
246             mod_survey_external::view_survey($this->survey->id);
247             $this->fail('Exception expected due to missing capability.');
248         } catch (moodle_exception $e) {
249             $this->assertEquals('nopermissions', $e->errorcode);
250         }
252     }
254     /**
255      * Test get_questions
256      */
257     public function test_get_questions() {
258         global $DB;
260         // Test user with full capabilities.
261         $this->setUser($this->student);
263         // Build our expectation array.
264         $expectedquestions = array();
265         $questions = survey_get_questions($this->survey);
266         foreach ($questions as $q) {
267             if ($q->type >= 0) {
268                 $expectedquestions[$q->id] = $q;
269                 if ($q->multi) {
270                     $subquestions = survey_get_subquestions($q);
271                     foreach ($subquestions as $sq) {
272                         $expectedquestions[$sq->id] = $sq;
273                     }
274                 }
275             }
276         }
278         $result = mod_survey_external::get_questions($this->survey->id);
279         $result = external_api::clean_returnvalue(mod_survey_external::get_questions_returns(), $result);
281         // Check we receive the same questions.
282         $this->assertCount(0, $result['warnings']);
283         foreach ($result['questions'] as $q) {
284             $this->assertEquals(get_string($expectedquestions[$q['id']]->text, 'survey'), $q['text']);
285             $this->assertEquals(get_string($expectedquestions[$q['id']]->shorttext, 'survey'), $q['shorttext']);
286             $this->assertEquals($expectedquestions[$q['id']]->multi, $q['multi']);
287             $this->assertEquals($expectedquestions[$q['id']]->type, $q['type']);
288             // Parent questions must have parent eq to 0.
289             if ($q['multi']) {
290                 $this->assertEquals(0, $q['parent']);
291                 $this->assertEquals(get_string($expectedquestions[$q['id']]->options, 'survey'), $q['options']);
292             }
293         }
295         // Test user with no capabilities.
296         // We need a explicit prohibit since this capability is only defined in authenticated user and guest roles.
297         assign_capability('mod/survey:participate', CAP_PROHIBIT, $this->studentrole->id, $this->context->id);
298         accesslib_clear_all_caches_for_unit_testing();
300         try {
301             mod_survey_external::get_questions($this->survey->id);
302             $this->fail('Exception expected due to missing capability.');
303         } catch (moodle_exception $e) {
304             $this->assertEquals('nopermissions', $e->errorcode);
305         }
306     }
308     /**
309      * Test submit_answers
310      */
311     public function test_submit_answers() {
312         global $DB;
314         // Test user with full capabilities.
315         $this->setUser($this->student);
317         // Build our questions and responses array.
318         $realquestions = array();
319         $questions = survey_get_questions($this->survey);
320         $i = 5;
321         foreach ($questions as $q) {
322             if ($q->type >= 0) {
323                 if ($q->multi) {
324                     $subquestions = survey_get_subquestions($q);
325                     foreach ($subquestions as $sq) {
326                         $realquestions[] = array(
327                             'key' => 'q' . $sq->id,
328                             'value' => $i % 5 + 1   // Values between 1 and 5.
329                         );
330                         $i++;
331                     }
332                 } else {
333                     $realquestions[] = array(
334                         'key' => 'q' . $q->id,
335                         'value' => $i % 5 + 1
336                     );
337                     $i++;
338                 }
339             }
340         }
342         $result = mod_survey_external::submit_answers($this->survey->id, $realquestions);
343         $result = external_api::clean_returnvalue(mod_survey_external::submit_answers_returns(), $result);
345         $this->assertTrue($result['status']);
346         $this->assertCount(0, $result['warnings']);
348         $dbanswers = $DB->get_records_menu('survey_answers', array('survey' => $this->survey->id), '', 'question, answer1');
349         foreach ($realquestions as $q) {
350             $id = str_replace('q', '', $q['key']);
351             $this->assertEquals($q['value'], $dbanswers[$id]);
352         }
354         // Submit again, we expect an error here.
355         try {
356             mod_survey_external::submit_answers($this->survey->id, $realquestions);
357             $this->fail('Exception expected due to answers already submitted.');
358         } catch (moodle_exception $e) {
359             $this->assertEquals('alreadysubmitted', $e->errorcode);
360         }
362         // Test user with no capabilities.
363         // We need a explicit prohibit since this capability is only defined in authenticated user and guest roles.
364         assign_capability('mod/survey:participate', CAP_PROHIBIT, $this->studentrole->id, $this->context->id);
365         accesslib_clear_all_caches_for_unit_testing();
367         try {
368             mod_survey_external::submit_answers($this->survey->id, $realquestions);
369             $this->fail('Exception expected due to missing capability.');
370         } catch (moodle_exception $e) {
371             $this->assertEquals('nopermissions', $e->errorcode);
372         }
374         // Test not-enrolled user.
375         $usernotenrolled = self::getDataGenerator()->create_user();
376         $this->setUser($usernotenrolled);
377         try {
378             mod_survey_external::submit_answers($this->survey->id, $realquestions);
379             $this->fail('Exception expected due to not enrolled user.');
380         } catch (moodle_exception $e) {
381             $this->assertEquals('requireloginerror', $e->errorcode);
382         }
383     }